SVGTextLayoutAttributesBuilder.cpp [plain text]
#include "config.h"
#if ENABLE(SVG)
#include "SVGTextLayoutAttributesBuilder.h"
#include "RenderSVGInlineText.h"
#include "RenderSVGText.h"
#include "SVGTextPositioningElement.h"
#define DUMP_TEXT_LAYOUT_ATTRIBUTES 0
namespace WebCore {
SVGTextLayoutAttributesBuilder::SVGTextLayoutAttributesBuilder()
{
}
void SVGTextLayoutAttributesBuilder::buildLayoutAttributesForTextSubtree(RenderSVGText* textRoot)
{
ASSERT(textRoot);
Vector<SVGTextLayoutAttributes>& allAttributes = textRoot->layoutAttributes();
allAttributes.clear();
unsigned atCharacter = 0;
UChar lastCharacter = '\0';
collectTextPositioningElements(textRoot, atCharacter, lastCharacter);
if (!atCharacter)
return;
buildLayoutAttributesForAllCharacters(textRoot, atCharacter);
atCharacter = 0;
lastCharacter = '\0';
propagateLayoutAttributes(textRoot, allAttributes, atCharacter, lastCharacter);
}
static inline void extractFloatValuesFromSVGLengthList(SVGElement* lengthContext, const SVGLengthList& list, Vector<float>& floatValues, unsigned textContentLength)
{
ASSERT(lengthContext);
unsigned length = list.size();
if (length > textContentLength)
length = textContentLength;
floatValues.reserveCapacity(length);
for (unsigned i = 0; i < length; ++i) {
const SVGLength& length = list.at(i);
floatValues.append(length.value(lengthContext));
}
}
static inline void extractFloatValuesFromSVGNumberList(const SVGNumberList& list, Vector<float>& floatValues, unsigned textContentLength)
{
unsigned length = list.size();
if (length > textContentLength)
length = textContentLength;
floatValues.reserveCapacity(length);
for (unsigned i = 0; i < length; ++i)
floatValues.append(list.at(i));
}
static inline bool characterIsSpace(const UChar& character)
{
return character == ' ';
}
static inline bool characterIsSpaceOrNull(const UChar& character)
{
return character == ' ' || character == '\0';
}
static inline bool shouldPreserveAllWhiteSpace(RenderStyle* style)
{
ASSERT(style);
return style->whiteSpace() == PRE;
}
static inline void processRenderSVGInlineText(RenderSVGInlineText* text, unsigned& atCharacter, UChar& lastCharacter)
{
if (shouldPreserveAllWhiteSpace(text->style())) {
atCharacter += text->textLength();
return;
}
const UChar* characters = text->characters();
unsigned textLength = text->textLength();
for (unsigned textPosition = 0; textPosition < textLength; ++textPosition) {
const UChar& currentCharacter = characters[textPosition];
if (characterIsSpace(currentCharacter) && characterIsSpaceOrNull(lastCharacter))
continue;
lastCharacter = currentCharacter;
++atCharacter;
}
}
void SVGTextLayoutAttributesBuilder::collectTextPositioningElements(RenderObject* start, unsigned& atCharacter, UChar& lastCharacter)
{
ASSERT(!start->isSVGText() || m_textPositions.isEmpty());
for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) {
if (child->isSVGInlineText()) {
processRenderSVGInlineText(toRenderSVGInlineText(child), atCharacter, lastCharacter);
continue;
}
if (!child->isSVGInline())
continue;
SVGTextPositioningElement* element = SVGTextPositioningElement::elementFromRenderer(child);
unsigned atPosition = m_textPositions.size();
if (element)
m_textPositions.append(TextPosition(element, atCharacter));
collectTextPositioningElements(child, atCharacter, lastCharacter);
if (!element)
continue;
TextPosition& position = m_textPositions[atPosition];
ASSERT(!position.length);
position.length = atCharacter - position.start;
}
}
void SVGTextLayoutAttributesBuilder::buildLayoutAttributesForAllCharacters(RenderSVGText* textRoot, unsigned textLength)
{
ASSERT(textLength);
SVGTextPositioningElement* outermostTextElement = SVGTextPositioningElement::elementFromRenderer(textRoot);
ASSERT(outermostTextElement);
m_positioningLists.fillWithEmptyValues(textLength);
TextPosition wholeTextPosition(outermostTextElement, 0, textLength);
fillAttributesAtPosition(wholeTextPosition);
float& xFirst = m_positioningLists.xValues.first();
if (xFirst == SVGTextLayoutAttributes::emptyValue())
xFirst = 0;
float& yFirst = m_positioningLists.yValues.first();
if (yFirst == SVGTextLayoutAttributes::emptyValue())
yFirst = 0;
unsigned size = m_textPositions.size();
for (unsigned i = 0; i < size; ++i)
fillAttributesAtPosition(m_textPositions[i]);
}
void SVGTextLayoutAttributesBuilder::propagateLayoutAttributes(RenderObject* start, Vector<SVGTextLayoutAttributes>& allAttributes, unsigned& atCharacter, UChar& lastCharacter) const
{
for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) {
if (child->isSVGInlineText()) {
RenderSVGInlineText* text = toRenderSVGInlineText(child);
const UChar* characters = text->characters();
unsigned textLength = text->textLength();
bool preserveWhiteSpace = shouldPreserveAllWhiteSpace(text->style());
SVGTextLayoutAttributes attributes(text);
attributes.reserveCapacity(textLength);
unsigned valueListPosition = atCharacter;
unsigned metricsLength = 1;
SVGTextMetrics lastMetrics = SVGTextMetrics::emptyMetrics();
for (unsigned textPosition = 0; textPosition < textLength; textPosition += metricsLength) {
const UChar& currentCharacter = characters[textPosition];
SVGTextMetrics startToCurrentMetrics = SVGTextMetrics::measureCharacterRange(text, 0, textPosition + 1);
SVGTextMetrics currentMetrics = SVGTextMetrics::measureCharacterRange(text, textPosition, 1);
float runWidthAdvance = startToCurrentMetrics.width() - lastMetrics.width();
if (runWidthAdvance != currentMetrics.width())
currentMetrics.setWidth(runWidthAdvance);
lastMetrics = startToCurrentMetrics;
metricsLength = currentMetrics.length();
if (!preserveWhiteSpace && characterIsSpace(currentCharacter) && characterIsSpaceOrNull(lastCharacter)) {
attributes.positioningLists().appendEmptyValues();
attributes.textMetricsValues().append(SVGTextMetrics::emptyMetrics());
continue;
}
SVGTextLayoutAttributes::PositioningLists& positioningLists = attributes.positioningLists();
positioningLists.appendValuesFromPosition(m_positioningLists, valueListPosition);
attributes.textMetricsValues().append(currentMetrics);
if (metricsLength > 1) {
for (unsigned i = 0; i < metricsLength - 1; ++i)
positioningLists.appendEmptyValues();
}
lastCharacter = currentCharacter;
valueListPosition += metricsLength;
}
#if DUMP_TEXT_LAYOUT_ATTRIBUTES > 0
fprintf(stderr, "\nDumping layout attributes for RenderSVGInlineText, renderer=%p, node=%p (atCharacter: %i)\n", text, text->node(), atCharacter);
fprintf(stderr, "BiDi properties: unicode-bidi=%i, block direction=%i\n", text->style()->unicodeBidi(), text->style()->direction());
attributes.dump();
#endif
text->storeLayoutAttributes(attributes);
allAttributes.append(attributes);
atCharacter = valueListPosition;
continue;
}
if (!child->isSVGInline())
continue;
propagateLayoutAttributes(child, allAttributes, atCharacter, lastCharacter);
}
}
static inline void fillListAtPosition(Vector<float>& allValues, Vector<float>& values, unsigned start)
{
unsigned valuesSize = values.size();
for (unsigned i = 0; i < valuesSize; ++i)
allValues[start + i] = values[i];
}
void SVGTextLayoutAttributesBuilder::fillAttributesAtPosition(const TextPosition& position)
{
Vector<float> values;
extractFloatValuesFromSVGLengthList(position.element, position.element->x(), values, position.length);
fillListAtPosition(m_positioningLists.xValues, values, position.start);
values.clear();
extractFloatValuesFromSVGLengthList(position.element, position.element->y(), values, position.length);
fillListAtPosition(m_positioningLists.yValues, values, position.start);
values.clear();
extractFloatValuesFromSVGLengthList(position.element, position.element->dx(), values, position.length);
fillListAtPosition(m_positioningLists.dxValues, values, position.start);
values.clear();
extractFloatValuesFromSVGLengthList(position.element, position.element->dy(), values, position.length);
fillListAtPosition(m_positioningLists.dyValues, values, position.start);
values.clear();
extractFloatValuesFromSVGNumberList(position.element->rotate(), values, position.length);
fillListAtPosition(m_positioningLists.rotateValues, values, position.start);
if (values.isEmpty())
return;
float lastValue = values.last();
for (unsigned i = values.size(); i < position.length; ++i)
m_positioningLists.rotateValues[position.start + i] = lastValue;
}
}
#endif // ENABLE(SVG)