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 12 matching lines...) Expand all Loading... |
23 | 23 |
24 #include "core/layout/svg/LayoutSVGInlineText.h" | 24 #include "core/layout/svg/LayoutSVGInlineText.h" |
25 | 25 |
26 #include "core/css/CSSFontSelector.h" | 26 #include "core/css/CSSFontSelector.h" |
27 #include "core/css/FontSize.h" | 27 #include "core/css/FontSize.h" |
28 #include "core/dom/StyleEngine.h" | 28 #include "core/dom/StyleEngine.h" |
29 #include "core/editing/TextAffinity.h" | 29 #include "core/editing/TextAffinity.h" |
30 #include "core/editing/VisiblePosition.h" | 30 #include "core/editing/VisiblePosition.h" |
31 #include "core/layout/svg/LayoutSVGText.h" | 31 #include "core/layout/svg/LayoutSVGText.h" |
32 #include "core/layout/svg/SVGLayoutSupport.h" | 32 #include "core/layout/svg/SVGLayoutSupport.h" |
33 #include "core/layout/svg/SVGTextMetricsBuilder.h" | |
34 #include "core/layout/svg/line/SVGInlineTextBox.h" | 33 #include "core/layout/svg/line/SVGInlineTextBox.h" |
| 34 #include "platform/fonts/CharacterRange.h" |
| 35 #include "platform/text/BidiCharacterRun.h" |
| 36 #include "platform/text/BidiResolver.h" |
| 37 #include "platform/text/TextDirection.h" |
| 38 #include "platform/text/TextRun.h" |
| 39 #include "platform/text/TextRunIterator.h" |
35 | 40 |
36 namespace blink { | 41 namespace blink { |
37 | 42 |
38 static PassRefPtr<StringImpl> applySVGWhitespaceRules(PassRefPtr<StringImpl> str
ing, bool preserveWhiteSpace) | 43 static PassRefPtr<StringImpl> applySVGWhitespaceRules(PassRefPtr<StringImpl> str
ing, bool preserveWhiteSpace) |
39 { | 44 { |
40 if (preserveWhiteSpace) { | 45 if (preserveWhiteSpace) { |
41 // Spec: When xml:space="preserve", the SVG user agent will do the follo
wing using a | 46 // Spec: When xml:space="preserve", the SVG user agent will do the follo
wing using a |
42 // copy of the original character data content. It will convert all newl
ine and tab | 47 // copy of the original character data content. It will convert all newl
ine and tab |
43 // characters into space characters. Then, it will draw all space charac
ters, including | 48 // characters into space characters. Then, it will draw all space charac
ters, including |
44 // leading, trailing and multiple contiguous space characters. | 49 // leading, trailing and multiple contiguous space characters. |
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
193 } | 198 } |
194 } | 199 } |
195 | 200 |
196 if (!closestDistanceFragment) | 201 if (!closestDistanceFragment) |
197 return createPositionWithAffinity(0); | 202 return createPositionWithAffinity(0); |
198 | 203 |
199 int offset = closestDistanceBox->offsetForPositionInFragment(*closestDistanc
eFragment, LayoutUnit(absolutePoint.x() - closestDistancePosition), true); | 204 int offset = closestDistanceBox->offsetForPositionInFragment(*closestDistanc
eFragment, LayoutUnit(absolutePoint.x() - closestDistancePosition), true); |
200 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); |
201 } | 206 } |
202 | 207 |
| 208 namespace { |
| 209 |
| 210 inline bool characterStartsSurrogatePair(const TextRun& run, unsigned index) |
| 211 { |
| 212 if (!U16_IS_LEAD(run[index])) |
| 213 return false; |
| 214 if (index + 1 >= static_cast<unsigned>(run.charactersLength())) |
| 215 return false; |
| 216 return U16_IS_TRAIL(run[index + 1]); |
| 217 } |
| 218 |
| 219 class SVGTextMetricsCalculator { |
| 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 { |
| 271 const ComputedStyle& style = text.styleRef(); |
| 272 |
| 273 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. |
| 275 , 0 // xPos, only relevant with allowTabs=true |
| 276 , 0 // padding, only relevant for justified text, not relevant for SVG |
| 277 , TextRun::AllowTrailingExpansion |
| 278 , textDirection |
| 279 , isOverride(style.unicodeBidi()) /* directionalOverride */); |
| 280 |
| 281 if (length) { |
| 282 if (text.is8Bit()) |
| 283 run.setText(text.characters8() + position, length); |
| 284 else |
| 285 run.setText(text.characters16() + position, length); |
| 286 } |
| 287 |
| 288 // We handle letter & word spacing ourselves. |
| 289 run.disableSpacing(); |
| 290 |
| 291 // 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); |
| 293 ASSERT(run.charactersLength() >= run.length()); |
| 294 return run; |
| 295 } |
| 296 |
| 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 |
| 342 // 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 |
| 344 // share a single position. This will incorrectly split combining diacritics. |
| 345 // See: https://crbug.com/473476. |
| 346 static void synthesizeGraphemeWidths(const TextRun& run, Vector<CharacterRange>&
ranges) |
| 347 { |
| 348 unsigned distributeCount = 0; |
| 349 for (int rangeIndex = static_cast<int>(ranges.size()) - 1; rangeIndex >= 0;
--rangeIndex) { |
| 350 CharacterRange& currentRange = ranges[rangeIndex]; |
| 351 if (currentRange.width() == 0) { |
| 352 distributeCount++; |
| 353 } else if (distributeCount != 0) { |
| 354 // Only count surrogate pairs as a single character. |
| 355 bool surrogatePair = characterStartsSurrogatePair(run, rangeIndex); |
| 356 if (!surrogatePair) |
| 357 distributeCount++; |
| 358 |
| 359 float newWidth = currentRange.width() / distributeCount; |
| 360 currentRange.end = currentRange.start + newWidth; |
| 361 float lastEndPosition = currentRange.end; |
| 362 for (unsigned distribute = 1; distribute < distributeCount; distribu
te++) { |
| 363 // This surrogate pair check will skip processing of the second |
| 364 // character forming the surrogate pair. |
| 365 unsigned distributeIndex = rangeIndex + distribute + (surrogateP
air ? 1 : 0); |
| 366 ranges[distributeIndex].start = lastEndPosition; |
| 367 ranges[distributeIndex].end = lastEndPosition + newWidth; |
| 368 lastEndPosition = ranges[distributeIndex].end; |
| 369 } |
| 370 |
| 371 distributeCount = 0; |
| 372 } |
| 373 } |
| 374 } |
| 375 |
| 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 |
| 408 |
| 409 void LayoutSVGInlineText::updateMetricsList(bool& lastCharacterWasWhiteSpace) |
| 410 { |
| 411 m_metrics.clear(); |
| 412 |
| 413 if (!textLength()) |
| 414 return; |
| 415 |
| 416 // TODO(pdr): This loop is too tightly coupled to SVGTextMetricsCalculator. |
| 417 // We should refactor SVGTextMetricsCalculator to be a simple bidi run |
| 418 // iterator and move all subrun logic to a single function. |
| 419 SVGTextMetricsCalculator calculator(*this); |
| 420 bool preserveWhiteSpace = styleRef().whiteSpace() == PRE; |
| 421 do { |
| 422 bool currentCharacterIsWhiteSpace = calculator.currentCharacterIsWhiteSp
ace(); |
| 423 if (!preserveWhiteSpace && lastCharacterWasWhiteSpace && currentCharacte
rIsWhiteSpace) { |
| 424 m_metrics.append(SVGTextMetrics(SVGTextMetrics::SkippedSpaceMetrics)
); |
| 425 ASSERT(calculator.currentCharacterMetrics().length() == 1); |
| 426 continue; |
| 427 } |
| 428 |
| 429 m_metrics.append(calculator.currentCharacterMetrics()); |
| 430 |
| 431 lastCharacterWasWhiteSpace = currentCharacterIsWhiteSpace; |
| 432 } while (calculator.advancePosition()); |
| 433 } |
| 434 |
203 void LayoutSVGInlineText::updateScaledFont() | 435 void LayoutSVGInlineText::updateScaledFont() |
204 { | 436 { |
205 computeNewScaledFontForStyle(this, m_scalingFactor, m_scaledFont); | 437 computeNewScaledFontForStyle(this, m_scalingFactor, m_scaledFont); |
206 } | 438 } |
207 | 439 |
208 void LayoutSVGInlineText::updateMetricsList(bool& lastCharacterWasWhiteSpace) | |
209 { | |
210 SVGTextMetricsBuilder::updateTextMetrics(*this, lastCharacterWasWhiteSpace); | |
211 } | |
212 | |
213 void LayoutSVGInlineText::computeNewScaledFontForStyle(LayoutObject* layoutObjec
t, float& scalingFactor, Font& scaledFont) | 440 void LayoutSVGInlineText::computeNewScaledFontForStyle(LayoutObject* layoutObjec
t, float& scalingFactor, Font& scaledFont) |
214 { | 441 { |
215 const ComputedStyle* style = layoutObject->style(); | 442 const ComputedStyle* style = layoutObject->style(); |
216 ASSERT(style); | 443 ASSERT(style); |
217 ASSERT(layoutObject); | 444 ASSERT(layoutObject); |
218 | 445 |
219 // Alter font-size to the right on-screen value to avoid scaling the glyphs
themselves, except when GeometricPrecision is specified. | 446 // Alter font-size to the right on-screen value to avoid scaling the glyphs
themselves, except when GeometricPrecision is specified. |
220 scalingFactor = SVGLayoutSupport::calculateScreenFontSizeScalingFactor(layou
tObject); | 447 scalingFactor = SVGLayoutSupport::calculateScreenFontSizeScalingFactor(layou
tObject); |
221 if (style->effectiveZoom() == 1 && (scalingFactor == 1 || !scalingFactor)) { | 448 if (style->effectiveZoom() == 1 && (scalingFactor == 1 || !scalingFactor)) { |
222 scalingFactor = 1; | 449 scalingFactor = 1; |
(...skipping 26 matching lines...) Expand all Loading... |
249 | 476 |
250 PassRefPtr<StringImpl> LayoutSVGInlineText::originalText() const | 477 PassRefPtr<StringImpl> LayoutSVGInlineText::originalText() const |
251 { | 478 { |
252 RefPtr<StringImpl> result = LayoutText::originalText(); | 479 RefPtr<StringImpl> result = LayoutText::originalText(); |
253 if (!result) | 480 if (!result) |
254 return nullptr; | 481 return nullptr; |
255 return applySVGWhitespaceRules(result, style() && style()->whiteSpace() == P
RE); | 482 return applySVGWhitespaceRules(result, style() && style()->whiteSpace() == P
RE); |
256 } | 483 } |
257 | 484 |
258 } // namespace blink | 485 } // namespace blink |
OLD | NEW |