| OLD | NEW |
| 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 Loading... |
| 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 Loading... |
| 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 |
| OLD | NEW |