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

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

Issue 1879453003: Move SVGTextMetricsCalculator to LayoutSVGInlineText.cpp (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: 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
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 12 matching lines...) Expand all
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698