Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(260)

Side by Side Diff: third_party/WebKit/Source/core/layout/svg/LayoutSVGInlineText.cpp

Issue 1880453002: Reorder metrics iteration in LayoutSVGInlineText::updateMetricsList (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@svg-metrics-cleanup-16
Patch Set: Use the correct run length. Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « third_party/WebKit/Source/core/layout/svg/LayoutSVGInlineText.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz> 2 * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz>
3 * Copyright (C) 2006 Apple Computer Inc. 3 * Copyright (C) 2006 Apple Computer Inc.
4 * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> 4 * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
5 * Copyright (C) 2008 Rob Buis <buis@kde.org> 5 * Copyright (C) 2008 Rob Buis <buis@kde.org>
6 * Copyright (C) Research In Motion Limited 2010. All rights reserved. 6 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
7 * 7 *
8 * This library is free software; you can redistribute it and/or 8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public 9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either 10 * License as published by the Free Software Foundation; either
(...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after
200 200
201 if (!closestDistanceFragment) 201 if (!closestDistanceFragment)
202 return createPositionWithAffinity(0); 202 return createPositionWithAffinity(0);
203 203
204 int offset = closestDistanceBox->offsetForPositionInFragment(*closestDistanc eFragment, LayoutUnit(absolutePoint.x() - closestDistancePosition), true); 204 int offset = closestDistanceBox->offsetForPositionInFragment(*closestDistanc eFragment, LayoutUnit(absolutePoint.x() - closestDistancePosition), true);
205 return createPositionWithAffinity(offset + closestDistanceBox->start(), offs et > 0 ? VP_UPSTREAM_IF_POSSIBLE : TextAffinity::Downstream); 205 return createPositionWithAffinity(offset + closestDistanceBox->start(), offs et > 0 ? VP_UPSTREAM_IF_POSSIBLE : TextAffinity::Downstream);
206 } 206 }
207 207
208 namespace { 208 namespace {
209 209
210 inline bool characterStartsSurrogatePair(const TextRun& run, unsigned index) 210 inline bool isValidSurrogatePair(const TextRun& run, unsigned index)
211 { 211 {
212 if (!U16_IS_LEAD(run[index])) 212 if (!U16_IS_LEAD(run[index]))
213 return false; 213 return false;
214 if (index + 1 >= static_cast<unsigned>(run.charactersLength())) 214 if (index + 1 >= static_cast<unsigned>(run.charactersLength()))
215 return false; 215 return false;
216 return U16_IS_TRAIL(run[index + 1]); 216 return U16_IS_TRAIL(run[index + 1]);
217 } 217 }
218 218
219 class SVGTextMetricsCalculator { 219 TextRun constructTextRun(LayoutSVGInlineText& text, unsigned position, unsigned length, TextDirection textDirection)
220 public:
221 SVGTextMetricsCalculator(LayoutSVGInlineText&);
222 ~SVGTextMetricsCalculator();
223
224 bool advancePosition();
225 unsigned currentPosition() const { return m_currentPosition; }
226
227 SVGTextMetrics currentCharacterMetrics();
228
229 // TODO(pdr): Character-based iteration is ambiguous and error-prone. It
230 // should be unified under a single concept. See: https://crbug.com/593570
231 bool currentCharacterStartsSurrogatePair() const
232 {
233 return characterStartsSurrogatePair(m_run, m_currentPosition);
234 }
235 bool currentCharacterIsWhiteSpace() const
236 {
237 return m_run[m_currentPosition] == ' ';
238 }
239 unsigned characterCount() const
240 {
241 return static_cast<unsigned>(m_run.charactersLength());
242 }
243
244 private:
245 void setupBidiRuns();
246
247 static TextRun constructTextRun(LayoutSVGInlineText&, unsigned position, uns igned length, TextDirection);
248
249 // Ensure |m_subrunRanges| is updated for the current bidi run, or the
250 // complete m_run if no bidi runs are present. Returns the current position
251 // in the subrun which can be used to index into |m_subrunRanges|.
252 unsigned updateSubrunRangesForCurrentPosition();
253
254 // Current character position in m_text.
255 unsigned m_currentPosition;
256
257 LayoutSVGInlineText& m_text;
258 float m_fontScalingFactor;
259 float m_cachedFontHeight;
260 TextRun m_run;
261
262 BidiCharacterRun* m_bidiRun;
263 BidiResolver<TextRunIterator, BidiCharacterRun> m_bidiResolver;
264
265 // Ranges for the current bidi run if present, or the entire run otherwise.
266 Vector<CharacterRange> m_subrunRanges;
267 };
268
269 TextRun SVGTextMetricsCalculator::constructTextRun(LayoutSVGInlineText& text, un signed position, unsigned length, TextDirection textDirection)
270 { 220 {
271 const ComputedStyle& style = text.styleRef(); 221 const ComputedStyle& style = text.styleRef();
272 222
273 TextRun run(static_cast<const LChar*>(nullptr) // characters, will be set be low if non-zero. 223 TextRun run(static_cast<const LChar*>(nullptr) // characters, will be set be low if non-zero.
274 , 0 // length, will be set below if non-zero. 224 , 0 // length, will be set below if non-zero.
275 , 0 // xPos, only relevant with allowTabs=true 225 , 0 // xPos, only relevant with allowTabs=true
276 , 0 // padding, only relevant for justified text, not relevant for SVG 226 , 0 // padding, only relevant for justified text, not relevant for SVG
277 , TextRun::AllowTrailingExpansion 227 , TextRun::AllowTrailingExpansion
278 , textDirection 228 , textDirection
279 , isOverride(style.unicodeBidi()) /* directionalOverride */); 229 , isOverride(style.unicodeBidi()) /* directionalOverride */);
280 230
281 if (length) { 231 if (length) {
282 if (text.is8Bit()) 232 if (text.is8Bit())
283 run.setText(text.characters8() + position, length); 233 run.setText(text.characters8() + position, length);
284 else 234 else
285 run.setText(text.characters16() + position, length); 235 run.setText(text.characters16() + position, length);
286 } 236 }
287 237
288 // We handle letter & word spacing ourselves. 238 // We handle letter & word spacing ourselves.
289 run.disableSpacing(); 239 run.disableSpacing();
290 240
291 // Propagate the maximum length of the characters buffer to the TextRun, eve n when we're only processing a substring. 241 // Propagate the maximum length of the characters buffer to the TextRun, eve n when we're only processing a substring.
292 run.setCharactersLength(text.textLength() - position); 242 run.setCharactersLength(text.textLength() - position);
293 ASSERT(run.charactersLength() >= run.length()); 243 ASSERT(run.charactersLength() >= run.length());
294 return run; 244 return run;
295 } 245 }
296 246
297 SVGTextMetricsCalculator::SVGTextMetricsCalculator(LayoutSVGInlineText& text)
298 : m_currentPosition(0)
299 , m_text(text)
300 , m_fontScalingFactor(m_text.scalingFactor())
301 , m_cachedFontHeight(m_text.scaledFont().getFontMetrics().floatHeight() / m_ fontScalingFactor)
302 , m_run(constructTextRun(m_text, 0, m_text.textLength(), m_text.styleRef().d irection()))
303 , m_bidiRun(nullptr)
304 {
305 setupBidiRuns();
306 }
307
308 SVGTextMetricsCalculator::~SVGTextMetricsCalculator()
309 {
310 m_bidiResolver.runs().deleteRuns();
311 }
312
313 void SVGTextMetricsCalculator::setupBidiRuns()
314 {
315 BidiRunList<BidiCharacterRun>& bidiRuns = m_bidiResolver.runs();
316 bool bidiOverride = isOverride(m_text.styleRef().unicodeBidi());
317 BidiStatus status(LTR, bidiOverride);
318 if (m_run.is8Bit() || bidiOverride) {
319 WTF::Unicode::CharDirection direction = WTF::Unicode::LeftToRight;
320 // If BiDi override is in effect, use the specified direction.
321 if (bidiOverride && !m_text.styleRef().isLeftToRightDirection())
322 direction = WTF::Unicode::RightToLeft;
323 bidiRuns.addRun(new BidiCharacterRun(0, m_run.charactersLength(), status .context.get(), direction));
324 } else {
325 status.last = status.lastStrong = WTF::Unicode::OtherNeutral;
326 m_bidiResolver.setStatus(status);
327 m_bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&m_run, 0));
328 const bool hardLineBreak = false;
329 const bool reorderRuns = false;
330 m_bidiResolver.createBidiRunsForLine(TextRunIterator(&m_run, m_run.lengt h()), NoVisualOverride, hardLineBreak, reorderRuns);
331 }
332 m_bidiRun = bidiRuns.firstRun();
333 }
334
335 bool SVGTextMetricsCalculator::advancePosition()
336 {
337 m_currentPosition += currentCharacterStartsSurrogatePair() ? 2 : 1;
338 return m_currentPosition < characterCount();
339 }
340
341 // TODO(pdr): We only have per-glyph data so we need to synthesize per-grapheme 247 // TODO(pdr): We only have per-glyph data so we need to synthesize per-grapheme
342 // data. E.g., if 'fi' is shaped into a single glyph, we do not know the 'i' 248 // data. E.g., if 'fi' is shaped into a single glyph, we do not know the 'i'
343 // position. The code below synthesizes an average glyph width when characters 249 // position. The code below synthesizes an average glyph width when characters
344 // share a single position. This will incorrectly split combining diacritics. 250 // share a single position. This will incorrectly split combining diacritics.
345 // See: https://crbug.com/473476. 251 // See: https://crbug.com/473476.
346 static void synthesizeGraphemeWidths(const TextRun& run, Vector<CharacterRange>& ranges) 252 void synthesizeGraphemeWidths(const TextRun& run, Vector<CharacterRange>& ranges )
347 { 253 {
348 unsigned distributeCount = 0; 254 unsigned distributeCount = 0;
349 for (int rangeIndex = static_cast<int>(ranges.size()) - 1; rangeIndex >= 0; --rangeIndex) { 255 for (int rangeIndex = static_cast<int>(ranges.size()) - 1; rangeIndex >= 0; --rangeIndex) {
350 CharacterRange& currentRange = ranges[rangeIndex]; 256 CharacterRange& currentRange = ranges[rangeIndex];
351 if (currentRange.width() == 0) { 257 if (currentRange.width() == 0) {
352 distributeCount++; 258 distributeCount++;
353 } else if (distributeCount != 0) { 259 } else if (distributeCount != 0) {
354 // Only count surrogate pairs as a single character. 260 // Only count surrogate pairs as a single character.
355 bool surrogatePair = characterStartsSurrogatePair(run, rangeIndex); 261 bool surrogatePair = isValidSurrogatePair(run, rangeIndex);
356 if (!surrogatePair) 262 if (!surrogatePair)
357 distributeCount++; 263 distributeCount++;
358 264
359 float newWidth = currentRange.width() / distributeCount; 265 float newWidth = currentRange.width() / distributeCount;
360 currentRange.end = currentRange.start + newWidth; 266 currentRange.end = currentRange.start + newWidth;
361 float lastEndPosition = currentRange.end; 267 float lastEndPosition = currentRange.end;
362 for (unsigned distribute = 1; distribute < distributeCount; distribu te++) { 268 for (unsigned distribute = 1; distribute < distributeCount; distribu te++) {
363 // This surrogate pair check will skip processing of the second 269 // This surrogate pair check will skip processing of the second
364 // character forming the surrogate pair. 270 // character forming the surrogate pair.
365 unsigned distributeIndex = rangeIndex + distribute + (surrogateP air ? 1 : 0); 271 unsigned distributeIndex = rangeIndex + distribute + (surrogateP air ? 1 : 0);
366 ranges[distributeIndex].start = lastEndPosition; 272 ranges[distributeIndex].start = lastEndPosition;
367 ranges[distributeIndex].end = lastEndPosition + newWidth; 273 ranges[distributeIndex].end = lastEndPosition + newWidth;
368 lastEndPosition = ranges[distributeIndex].end; 274 lastEndPosition = ranges[distributeIndex].end;
369 } 275 }
370 276
371 distributeCount = 0; 277 distributeCount = 0;
372 } 278 }
373 } 279 }
374 } 280 }
375 281
376 unsigned SVGTextMetricsCalculator::updateSubrunRangesForCurrentPosition()
377 {
378 ASSERT(m_bidiRun);
379 if (m_currentPosition >= static_cast<unsigned>(m_bidiRun->stop())) {
380 m_bidiRun = m_bidiRun->next();
381 // Ensure new subrange ranges are computed below.
382 m_subrunRanges.clear();
383 }
384 ASSERT(m_bidiRun);
385 ASSERT(static_cast<int>(m_currentPosition) < m_bidiRun->stop());
386
387 unsigned positionInRun = m_currentPosition - m_bidiRun->start();
388 if (positionInRun >= m_subrunRanges.size()) {
389 TextRun subRun = constructTextRun(m_text, m_bidiRun->start(),
390 m_bidiRun->stop() - m_bidiRun->start(), m_bidiRun->direction());
391 m_subrunRanges = m_text.scaledFont().individualCharacterRanges(subRun);
392 synthesizeGraphemeWidths(subRun, m_subrunRanges);
393 }
394
395 ASSERT(m_subrunRanges.size() && positionInRun < m_subrunRanges.size());
396 return positionInRun;
397 }
398
399 SVGTextMetrics SVGTextMetricsCalculator::currentCharacterMetrics()
400 {
401 unsigned currentSubrunPosition = updateSubrunRangesForCurrentPosition();
402 unsigned length = currentCharacterStartsSurrogatePair() ? 2 : 1;
403 float width = m_subrunRanges[currentSubrunPosition].width();
404 return SVGTextMetrics(length, width / m_fontScalingFactor, m_cachedFontHeigh t);
405 }
406
407 } // namespace 282 } // namespace
408 283
284 void LayoutSVGInlineText::addMetricsFromRun(
285 const TextRun& run, bool& lastCharacterWasWhiteSpace)
286 {
287 Vector<CharacterRange> charRanges = scaledFont().individualCharacterRanges(r un);
288 synthesizeGraphemeWidths(run, charRanges);
289
290 const float cachedFontHeight = scaledFont().getFontMetrics().floatHeight() / m_scalingFactor;
291 const bool preserveWhiteSpace = styleRef().whiteSpace() == PRE;
292 const unsigned runLength = static_cast<unsigned>(run.length());
293
294 // TODO(pdr): Character-based iteration is ambiguous and error-prone. It
295 // should be unified under a single concept. See: https://crbug.com/593570
296 unsigned characterIndex = 0;
297 while (characterIndex < runLength) {
298 bool currentCharacterIsWhiteSpace = run[characterIndex] == ' ';
299 if (!preserveWhiteSpace && lastCharacterWasWhiteSpace && currentCharacte rIsWhiteSpace) {
300 m_metrics.append(SVGTextMetrics(SVGTextMetrics::SkippedSpaceMetrics) );
301 characterIndex++;
302 continue;
303 }
304
305 unsigned length = isValidSurrogatePair(run, characterIndex) ? 2 : 1;
306 float width = charRanges[characterIndex].width() / m_scalingFactor;
307
308 m_metrics.append(SVGTextMetrics(length, width, cachedFontHeight));
309
310 lastCharacterWasWhiteSpace = currentCharacterIsWhiteSpace;
311 characterIndex += length;
312 }
313 }
314
409 void LayoutSVGInlineText::updateMetricsList(bool& lastCharacterWasWhiteSpace) 315 void LayoutSVGInlineText::updateMetricsList(bool& lastCharacterWasWhiteSpace)
410 { 316 {
411 m_metrics.clear(); 317 m_metrics.clear();
412 318
413 if (!textLength()) 319 if (!textLength())
414 return; 320 return;
415 321
416 // TODO(pdr): This loop is too tightly coupled to SVGTextMetricsCalculator. 322 TextRun run = constructTextRun(*this, 0, textLength(), styleRef().direction( ));
417 // We should refactor SVGTextMetricsCalculator to be a simple bidi run 323 BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver;
418 // iterator and move all subrun logic to a single function. 324 BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs();
419 SVGTextMetricsCalculator calculator(*this); 325 bool bidiOverride = isOverride(styleRef().unicodeBidi());
420 bool preserveWhiteSpace = styleRef().whiteSpace() == PRE; 326 BidiStatus status(LTR, bidiOverride);
421 do { 327 if (run.is8Bit() || bidiOverride) {
422 bool currentCharacterIsWhiteSpace = calculator.currentCharacterIsWhiteSp ace(); 328 WTF::Unicode::CharDirection direction = WTF::Unicode::LeftToRight;
423 if (!preserveWhiteSpace && lastCharacterWasWhiteSpace && currentCharacte rIsWhiteSpace) { 329 // If BiDi override is in effect, use the specified direction.
424 m_metrics.append(SVGTextMetrics(SVGTextMetrics::SkippedSpaceMetrics) ); 330 if (bidiOverride && !styleRef().isLeftToRightDirection())
425 ASSERT(calculator.currentCharacterMetrics().length() == 1); 331 direction = WTF::Unicode::RightToLeft;
426 continue; 332 bidiRuns.addRun(new BidiCharacterRun(0, run.charactersLength(), status.c ontext.get(), direction));
427 } 333 } else {
334 status.last = status.lastStrong = WTF::Unicode::OtherNeutral;
335 bidiResolver.setStatus(status);
336 bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0)) ;
337 const bool hardLineBreak = false;
338 const bool reorderRuns = false;
339 bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length()), NoVisualOverride, hardLineBreak, reorderRuns);
340 }
428 341
429 m_metrics.append(calculator.currentCharacterMetrics()); 342 for (const BidiCharacterRun* bidiRun = bidiRuns.firstRun(); bidiRun; bidiRun = bidiRun->next()) {
343 TextRun subRun = constructTextRun(*this, bidiRun->start(), bidiRun->stop () - bidiRun->start(),
344 bidiRun->direction());
345 addMetricsFromRun(subRun, lastCharacterWasWhiteSpace);
346 }
430 347
431 lastCharacterWasWhiteSpace = currentCharacterIsWhiteSpace; 348 bidiResolver.runs().deleteRuns();
432 } while (calculator.advancePosition());
433 } 349 }
434 350
435 void LayoutSVGInlineText::updateScaledFont() 351 void LayoutSVGInlineText::updateScaledFont()
436 { 352 {
437 computeNewScaledFontForStyle(this, m_scalingFactor, m_scaledFont); 353 computeNewScaledFontForStyle(this, m_scalingFactor, m_scaledFont);
438 } 354 }
439 355
440 void LayoutSVGInlineText::computeNewScaledFontForStyle(LayoutObject* layoutObjec t, float& scalingFactor, Font& scaledFont) 356 void LayoutSVGInlineText::computeNewScaledFontForStyle(LayoutObject* layoutObjec t, float& scalingFactor, Font& scaledFont)
441 { 357 {
442 const ComputedStyle* style = layoutObject->style(); 358 const ComputedStyle* style = layoutObject->style();
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
476 392
477 PassRefPtr<StringImpl> LayoutSVGInlineText::originalText() const 393 PassRefPtr<StringImpl> LayoutSVGInlineText::originalText() const
478 { 394 {
479 RefPtr<StringImpl> result = LayoutText::originalText(); 395 RefPtr<StringImpl> result = LayoutText::originalText();
480 if (!result) 396 if (!result)
481 return nullptr; 397 return nullptr;
482 return applySVGWhitespaceRules(result, style() && style()->whiteSpace() == P RE); 398 return applySVGWhitespaceRules(result, style() && style()->whiteSpace() == P RE);
483 } 399 }
484 400
485 } // namespace blink 401 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/core/layout/svg/LayoutSVGInlineText.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698