summaryrefslogtreecommitdiffstats
path: root/chromium/third_party/WebKit/Source/core/rendering/svg/SVGTextMetricsBuilder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/WebKit/Source/core/rendering/svg/SVGTextMetricsBuilder.cpp')
-rw-r--r--chromium/third_party/WebKit/Source/core/rendering/svg/SVGTextMetricsBuilder.cpp238
1 files changed, 145 insertions, 93 deletions
diff --git a/chromium/third_party/WebKit/Source/core/rendering/svg/SVGTextMetricsBuilder.cpp b/chromium/third_party/WebKit/Source/core/rendering/svg/SVGTextMetricsBuilder.cpp
index 46a27979e9d..eaaa9361d19 100644
--- a/chromium/third_party/WebKit/Source/core/rendering/svg/SVGTextMetricsBuilder.cpp
+++ b/chromium/third_party/WebKit/Source/core/rendering/svg/SVGTextMetricsBuilder.cpp
@@ -21,186 +21,238 @@
#include "core/rendering/svg/SVGTextMetricsBuilder.h"
+#include "core/rendering/svg/RenderSVGInline.h"
#include "core/rendering/svg/RenderSVGInlineText.h"
#include "core/rendering/svg/RenderSVGText.h"
+#include "core/rendering/svg/SVGTextMetrics.h"
+#include "platform/fonts/GlyphBuffer.h"
+#include "platform/fonts/WidthIterator.h"
+#include "platform/text/BidiCharacterRun.h"
+#include "platform/text/BidiResolver.h"
+#include "platform/text/TextDirection.h"
+#include "platform/text/TextPath.h"
+#include "platform/text/TextRun.h"
+#include "platform/text/TextRunIterator.h"
+#include "wtf/Vector.h"
namespace WebCore {
-SVGTextMetricsBuilder::SVGTextMetricsBuilder()
- : m_text(0)
- , m_run(static_cast<const UChar*>(0), 0)
- , m_textPosition(0)
+class SVGTextMetricsCalculator {
+public:
+ SVGTextMetricsCalculator(RenderSVGInlineText*);
+ ~SVGTextMetricsCalculator();
+
+ SVGTextMetrics computeMetricsForCharacter(unsigned textPosition);
+ unsigned textLength() const { return static_cast<unsigned>(m_run.charactersLength()); }
+
+ bool characterStartsSurrogatePair(unsigned textPosition) const
+ {
+ return U16_IS_LEAD(m_run[textPosition]) && textPosition + 1 < textLength() && U16_IS_TRAIL(m_run[textPosition + 1]);
+ }
+ bool characterIsWhiteSpace(unsigned textPosition) const
+ {
+ return m_run[textPosition] == ' ';
+ }
+
+private:
+ void setupBidiRuns();
+ SVGTextMetrics computeMetricsForCharacterSimple(unsigned textPosition);
+ SVGTextMetrics computeMetricsForCharacterComplex(unsigned textPosition);
+
+ RenderSVGInlineText* m_text;
+ BidiCharacterRun* m_bidiRun;
+ TextRun m_run;
+ BidiResolver<TextRunIterator, BidiCharacterRun> m_bidiResolver;
+ bool m_isComplexText;
+ float m_totalWidth;
+ TextDirection m_textDirection;
+
+ // Simple text only.
+ OwnPtr<WidthIterator> m_simpleWidthIterator;
+};
+
+SVGTextMetricsCalculator::SVGTextMetricsCalculator(RenderSVGInlineText* text)
+ : m_text(text)
+ , m_bidiRun(0)
+ , m_run(SVGTextMetrics::constructTextRun(text, 0, text->textLength()))
, m_isComplexText(false)
, m_totalWidth(0)
{
+ const Font& scaledFont = text->scaledFont();
+ CodePath codePath = scaledFont.codePath(m_run);
+ m_isComplexText = codePath == ComplexPath;
+ m_run.setCharacterScanForCodePath(!m_isComplexText);
+
+ if (!m_isComplexText)
+ m_simpleWidthIterator = adoptPtr(new WidthIterator(&scaledFont, m_run));
+ else
+ setupBidiRuns();
}
-inline bool SVGTextMetricsBuilder::currentCharacterStartsSurrogatePair() const
+SVGTextMetricsCalculator::~SVGTextMetricsCalculator()
{
- return U16_IS_LEAD(m_run[m_textPosition]) && int(m_textPosition + 1) < m_run.charactersLength() && U16_IS_TRAIL(m_run[m_textPosition + 1]);
+ if (m_bidiRun)
+ m_bidiResolver.runs().deleteRuns();
}
-bool SVGTextMetricsBuilder::advance()
+void SVGTextMetricsCalculator::setupBidiRuns()
{
- m_textPosition += m_currentMetrics.length();
- if (int(m_textPosition) >= m_run.charactersLength())
- return false;
-
- if (m_isComplexText)
- advanceComplexText();
- else
- advanceSimpleText();
+ RenderStyle* style = m_text->style();
+ m_textDirection = style->direction();
+ if (isOverride(style->unicodeBidi()))
+ return;
- return m_currentMetrics.length() > 0;
+ BidiStatus status(LTR, false);
+ status.last = status.lastStrong = WTF::Unicode::OtherNeutral;
+ m_bidiResolver.setStatus(status);
+ m_bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&m_run, 0));
+ const bool hardLineBreak = false;
+ const bool reorderRuns = false;
+ m_bidiResolver.createBidiRunsForLine(TextRunIterator(&m_run, m_run.length()), NoVisualOverride, hardLineBreak, reorderRuns);
+ BidiRunList<BidiCharacterRun>& bidiRuns = m_bidiResolver.runs();
+ m_bidiRun = bidiRuns.firstRun();
}
-void SVGTextMetricsBuilder::advanceSimpleText()
+SVGTextMetrics SVGTextMetricsCalculator::computeMetricsForCharacterSimple(unsigned textPosition)
{
GlyphBuffer glyphBuffer;
- unsigned metricsLength = m_simpleWidthIterator->advance(m_textPosition + 1, &glyphBuffer);
- if (!metricsLength) {
- m_currentMetrics = SVGTextMetrics();
- return;
- }
+ unsigned metricsLength = m_simpleWidthIterator->advance(textPosition + 1, &glyphBuffer);
+ if (!metricsLength)
+ return SVGTextMetrics();
float currentWidth = m_simpleWidthIterator->runWidthSoFar() - m_totalWidth;
m_totalWidth = m_simpleWidthIterator->runWidthSoFar();
-#if ENABLE(SVG_FONTS)
- m_currentMetrics = SVGTextMetrics(m_text, m_textPosition, metricsLength, currentWidth, m_simpleWidthIterator->lastGlyphName());
-#else
- m_currentMetrics = SVGTextMetrics(m_text, m_textPosition, metricsLength, currentWidth, emptyString());
-#endif
+ Glyph glyphId = glyphBuffer.glyphAt(0);
+ return SVGTextMetrics(m_text, textPosition, metricsLength, currentWidth, glyphId);
}
-void SVGTextMetricsBuilder::advanceComplexText()
+SVGTextMetrics SVGTextMetricsCalculator::computeMetricsForCharacterComplex(unsigned textPosition)
{
- unsigned metricsLength = currentCharacterStartsSurrogatePair() ? 2 : 1;
- m_currentMetrics = SVGTextMetrics::measureCharacterRange(m_text, m_textPosition, metricsLength);
- m_complexStartToCurrentMetrics = SVGTextMetrics::measureCharacterRange(m_text, 0, m_textPosition + metricsLength);
- ASSERT(m_currentMetrics.length() == metricsLength);
+ unsigned metricsLength = characterStartsSurrogatePair(textPosition) ? 2 : 1;
+ SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(m_text, textPosition, metricsLength, m_textDirection);
+ ASSERT(metrics.length() == metricsLength);
+ unsigned startPosition = m_bidiRun ? m_bidiRun->start() : 0;
+ ASSERT(startPosition <= textPosition);
+ SVGTextMetrics complexStartToCurrentMetrics = SVGTextMetrics::measureCharacterRange(m_text, startPosition, textPosition - startPosition + metricsLength, m_textDirection);
// Frequent case for Arabic text: when measuring a single character the arabic isolated form is taken
// when rendering the glyph "in context" (with it's surrounding characters) it changes due to shaping.
// So whenever currentWidth != currentMetrics.width(), we are processing a text run whose length is
// not equal to the sum of the individual lengths of the glyphs, when measuring them isolated.
- float currentWidth = m_complexStartToCurrentMetrics.width() - m_totalWidth;
- if (currentWidth != m_currentMetrics.width())
- m_currentMetrics.setWidth(currentWidth);
+ float currentWidth = complexStartToCurrentMetrics.width() - m_totalWidth;
+ if (currentWidth != metrics.width())
+ metrics.setWidth(currentWidth);
- m_totalWidth = m_complexStartToCurrentMetrics.width();
+ m_totalWidth = complexStartToCurrentMetrics.width();
+ return metrics;
}
-void SVGTextMetricsBuilder::initializeMeasurementWithTextRenderer(RenderSVGInlineText* text)
+SVGTextMetrics SVGTextMetricsCalculator::computeMetricsForCharacter(unsigned textPosition)
{
- m_text = text;
- m_textPosition = 0;
- m_currentMetrics = SVGTextMetrics();
- m_complexStartToCurrentMetrics = SVGTextMetrics();
- m_totalWidth = 0;
-
- const Font& scaledFont = text->scaledFont();
- m_run = SVGTextMetrics::constructTextRun(text, 0, text->textLength());
- m_isComplexText = scaledFont.codePath(m_run) == Font::Complex;
+ if (m_bidiRun) {
+ if (textPosition >= static_cast<unsigned>(m_bidiRun->stop())) {
+ m_bidiRun = m_bidiRun->next();
+ // New BiDi run means new reference position for measurements, so reset |m_totalWidth|.
+ m_totalWidth = 0;
+ }
+ ASSERT(m_bidiRun);
+ ASSERT(static_cast<int>(textPosition) < m_bidiRun->stop());
+ m_textDirection = m_bidiRun->direction();
+ }
if (m_isComplexText)
- m_simpleWidthIterator.clear();
- else
- m_simpleWidthIterator = adoptPtr(new WidthIterator(&scaledFont, m_run));
+ return computeMetricsForCharacterComplex(textPosition);
+
+ return computeMetricsForCharacterSimple(textPosition);
}
struct MeasureTextData {
MeasureTextData(SVGCharacterDataMap* characterDataMap)
: allCharactersMap(characterDataMap)
- , hasLastCharacter(false)
- , lastCharacter(0)
- , processRenderer(false)
+ , lastCharacterWasWhiteSpace(true)
, valueListPosition(0)
- , skippedCharacters(0)
{
}
SVGCharacterDataMap* allCharactersMap;
- bool hasLastCharacter;
- UChar lastCharacter;
- bool processRenderer;
+ bool lastCharacterWasWhiteSpace;
unsigned valueListPosition;
- unsigned skippedCharacters;
};
-void SVGTextMetricsBuilder::measureTextRenderer(RenderSVGInlineText* text, MeasureTextData* data)
+static void measureTextRenderer(RenderSVGInlineText* text, MeasureTextData* data, bool processRenderer)
{
ASSERT(text);
SVGTextLayoutAttributes* attributes = text->layoutAttributes();
Vector<SVGTextMetrics>* textMetricsValues = &attributes->textMetricsValues();
- if (data->processRenderer) {
+ if (processRenderer) {
if (data->allCharactersMap)
attributes->clear();
else
textMetricsValues->clear();
}
- initializeMeasurementWithTextRenderer(text);
+ SVGTextMetricsCalculator calculator(text);
bool preserveWhiteSpace = text->style()->whiteSpace() == PRE;
- int surrogatePairCharacters = 0;
-
- while (advance()) {
- UChar currentCharacter = m_run[m_textPosition];
- if (currentCharacter == ' ' && !preserveWhiteSpace && (!data->hasLastCharacter || data->lastCharacter == ' ')) {
- if (data->processRenderer)
+ unsigned surrogatePairCharacters = 0;
+ unsigned skippedCharacters = 0;
+ unsigned textPosition = 0;
+ unsigned textLength = calculator.textLength();
+
+ SVGTextMetrics currentMetrics;
+ for (; textPosition < textLength; textPosition += currentMetrics.length()) {
+ currentMetrics = calculator.computeMetricsForCharacter(textPosition);
+ if (!currentMetrics.length())
+ break;
+
+ bool characterIsWhiteSpace = calculator.characterIsWhiteSpace(textPosition);
+ if (characterIsWhiteSpace && !preserveWhiteSpace && data->lastCharacterWasWhiteSpace) {
+ if (processRenderer)
textMetricsValues->append(SVGTextMetrics(SVGTextMetrics::SkippedSpaceMetrics));
if (data->allCharactersMap)
- data->skippedCharacters += m_currentMetrics.length();
+ skippedCharacters += currentMetrics.length();
continue;
}
- if (data->processRenderer) {
+ if (processRenderer) {
if (data->allCharactersMap) {
- const SVGCharacterDataMap::const_iterator it = data->allCharactersMap->find(data->valueListPosition + m_textPosition - data->skippedCharacters - surrogatePairCharacters + 1);
+ const SVGCharacterDataMap::const_iterator it = data->allCharactersMap->find(data->valueListPosition + textPosition - skippedCharacters - surrogatePairCharacters + 1);
if (it != data->allCharactersMap->end())
- attributes->characterDataMap().set(m_textPosition + 1, it->value);
+ attributes->characterDataMap().set(textPosition + 1, it->value);
}
- textMetricsValues->append(m_currentMetrics);
+ textMetricsValues->append(currentMetrics);
}
- if (data->allCharactersMap && currentCharacterStartsSurrogatePair())
+ if (data->allCharactersMap && calculator.characterStartsSurrogatePair(textPosition))
surrogatePairCharacters++;
- data->hasLastCharacter = true;
- data->lastCharacter = currentCharacter;
+ data->lastCharacterWasWhiteSpace = characterIsWhiteSpace;
}
if (!data->allCharactersMap)
return;
- data->valueListPosition += m_textPosition - data->skippedCharacters;
- data->skippedCharacters = 0;
+ data->valueListPosition += textPosition - skippedCharacters;
}
-void SVGTextMetricsBuilder::walkTree(RenderObject* start, RenderSVGInlineText* stopAtLeaf, MeasureTextData* data)
+static void walkTree(RenderSVGText* start, RenderSVGInlineText* stopAtLeaf, MeasureTextData* data)
{
- for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) {
+ RenderObject* child = start->firstChild();
+ while (child) {
if (child->isSVGInlineText()) {
RenderSVGInlineText* text = toRenderSVGInlineText(child);
- if (stopAtLeaf && stopAtLeaf != text) {
- data->processRenderer = false;
- measureTextRenderer(text, data);
+ measureTextRenderer(text, data, !stopAtLeaf || stopAtLeaf == text);
+ if (stopAtLeaf && stopAtLeaf == text)
+ return;
+ } else if (child->isSVGInline()) {
+ // Visit children of text content elements.
+ if (RenderObject* inlineChild = toRenderSVGInline(child)->firstChild()) {
+ child = inlineChild;
continue;
}
-
- data->processRenderer = true;
- measureTextRenderer(text, data);
- if (stopAtLeaf)
- return;
-
- continue;
}
-
- if (!child->isSVGInline())
- continue;
-
- walkTree(child, stopAtLeaf, data);
+ child = child->nextInPreOrderAfterChildren(start);
}
}