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 |