/* * Copyright (C) Research In Motion Limited 2010-2012. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "config.h" #include "SVGTextMetricsBuilder.h" #include "RenderSVGInline.h" #include "RenderSVGInlineText.h" #include "RenderSVGText.h" #include "SVGTextRunRenderingContext.h" namespace WebCore { SVGTextMetricsBuilder::SVGTextMetricsBuilder() : m_text(0) , m_run(StringView()) , m_textPosition(0) , m_isComplexText(false) , m_totalWidth(0) { } inline bool SVGTextMetricsBuilder::currentCharacterStartsSurrogatePair() const { return U16_IS_LEAD(m_run[m_textPosition]) && (m_textPosition + 1) < m_run.charactersLength() && U16_IS_TRAIL(m_run[m_textPosition + 1]); } bool SVGTextMetricsBuilder::advance() { m_textPosition += m_currentMetrics.length(); if (m_textPosition >= m_run.charactersLength()) return false; if (m_isComplexText) advanceComplexText(); else advanceSimpleText(); return m_currentMetrics.length() > 0; } void SVGTextMetricsBuilder::advanceSimpleText() { GlyphBuffer glyphBuffer; unsigned metricsLength = m_simpleWidthIterator->advance(m_textPosition + 1, &glyphBuffer); if (!metricsLength) { m_currentMetrics = SVGTextMetrics(); return; } 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 } void SVGTextMetricsBuilder::advanceComplexText() { 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); // 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); m_totalWidth = m_complexStartToCurrentMetrics.width(); } void SVGTextMetricsBuilder::initializeMeasurementWithTextRenderer(RenderSVGInlineText& text) { m_text = &text; m_textPosition = 0; m_currentMetrics = SVGTextMetrics(); m_complexStartToCurrentMetrics = SVGTextMetrics(); m_totalWidth = 0; const FontCascade& scaledFont = text.scaledFont(); m_run = SVGTextMetrics::constructTextRun(text); m_isComplexText = scaledFont.codePath(m_run) == FontCascade::Complex; if (m_isComplexText) m_simpleWidthIterator = nullptr; else m_simpleWidthIterator = std::make_unique(&scaledFont, m_run); } struct MeasureTextData { MeasureTextData(SVGCharacterDataMap* characterDataMap) : allCharactersMap(characterDataMap) , lastCharacter(0) , processRenderer(false) , valueListPosition(0) , skippedCharacters(0) { } SVGCharacterDataMap* allCharactersMap; UChar lastCharacter; bool processRenderer; unsigned valueListPosition; unsigned skippedCharacters; }; void SVGTextMetricsBuilder::measureTextRenderer(RenderSVGInlineText& text, MeasureTextData* data) { SVGTextLayoutAttributes* attributes = text.layoutAttributes(); Vector* textMetricsValues = &attributes->textMetricsValues(); if (data->processRenderer) { if (data->allCharactersMap) attributes->clear(); else textMetricsValues->clear(); } initializeMeasurementWithTextRenderer(text); bool preserveWhiteSpace = text.style().whiteSpace() == PRE; int surrogatePairCharacters = 0; while (advance()) { UChar currentCharacter = m_run[m_textPosition]; if (currentCharacter == ' ' && !preserveWhiteSpace && (!data->lastCharacter || data->lastCharacter == ' ')) { if (data->processRenderer) textMetricsValues->append(SVGTextMetrics(SVGTextMetrics::SkippedSpaceMetrics)); if (data->allCharactersMap) data->skippedCharacters += m_currentMetrics.length(); continue; } if (data->processRenderer) { if (data->allCharactersMap) { const SVGCharacterDataMap::const_iterator it = data->allCharactersMap->find(data->valueListPosition + m_textPosition - data->skippedCharacters - surrogatePairCharacters + 1); if (it != data->allCharactersMap->end()) attributes->characterDataMap().set(m_textPosition + 1, it->value); } textMetricsValues->append(m_currentMetrics); } if (data->allCharactersMap && currentCharacterStartsSurrogatePair()) surrogatePairCharacters++; data->lastCharacter = currentCharacter; } if (!data->allCharactersMap) return; data->valueListPosition += m_textPosition - data->skippedCharacters; data->skippedCharacters = 0; } void SVGTextMetricsBuilder::walkTree(RenderElement& start, RenderSVGInlineText* stopAtLeaf, MeasureTextData* data) { for (auto* child = start.firstChild(); child; child = child->nextSibling()) { if (is(*child)) { RenderSVGInlineText& text = downcast(*child); if (stopAtLeaf && stopAtLeaf != &text) { data->processRenderer = false; measureTextRenderer(text, data); continue; } data->processRenderer = true; measureTextRenderer(text, data); if (stopAtLeaf) return; continue; } if (!is(*child)) continue; walkTree(downcast(*child), stopAtLeaf, data); } } void SVGTextMetricsBuilder::measureTextRenderer(RenderSVGInlineText& text) { auto* textRoot = RenderSVGText::locateRenderSVGTextAncestor(text); if (!textRoot) return; MeasureTextData data(nullptr); walkTree(*textRoot, &text, &data); } void SVGTextMetricsBuilder::buildMetricsAndLayoutAttributes(RenderSVGText& textRoot, RenderSVGInlineText* stopAtLeaf, SVGCharacterDataMap& allCharactersMap) { MeasureTextData data(&allCharactersMap); walkTree(textRoot, stopAtLeaf, &data); } }