diff options
Diffstat (limited to 'Source/WebCore/rendering/svg/SVGTextChunk.cpp')
-rw-r--r-- | Source/WebCore/rendering/svg/SVGTextChunk.cpp | 201 |
1 files changed, 163 insertions, 38 deletions
diff --git a/Source/WebCore/rendering/svg/SVGTextChunk.cpp b/Source/WebCore/rendering/svg/SVGTextChunk.cpp index b7072753b..17811864e 100644 --- a/Source/WebCore/rendering/svg/SVGTextChunk.cpp +++ b/Source/WebCore/rendering/svg/SVGTextChunk.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) Research In Motion Limited 2010. All rights reserved. + * Copyright (C) 2015 Apple Inc. 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 @@ -18,60 +19,102 @@ */ #include "config.h" - -#if ENABLE(SVG) #include "SVGTextChunk.h" #include "SVGInlineTextBox.h" namespace WebCore { -SVGTextChunk::SVGTextChunk(unsigned chunkStyle, float desiredTextLength) - : m_chunkStyle(chunkStyle) - , m_desiredTextLength(desiredTextLength) -{ -} - -void SVGTextChunk::calculateLength(float& length, unsigned& characters) const +SVGTextChunk::SVGTextChunk(const Vector<SVGInlineTextBox*>& lineLayoutBoxes, unsigned first, unsigned limit) { - SVGTextFragment* lastFragment = 0; + ASSERT(first < limit); + ASSERT(limit <= lineLayoutBoxes.size()); + + const SVGInlineTextBox* box = lineLayoutBoxes[first]; + const RenderStyle& style = box->renderer().style(); + const SVGRenderStyle& svgStyle = style.svgStyle(); + + if (!style.isLeftToRightDirection()) + m_chunkStyle |= SVGTextChunk::RightToLeftText; + + if (style.isVerticalWritingMode()) + m_chunkStyle |= SVGTextChunk::VerticalText; + + switch (svgStyle.textAnchor()) { + case TA_START: + break; + case TA_MIDDLE: + m_chunkStyle |= MiddleAnchor; + break; + case TA_END: + m_chunkStyle |= EndAnchor; + break; + } - unsigned boxCount = m_boxes.size(); - for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) { - SVGInlineTextBox* textBox = m_boxes.at(boxPosition); - Vector<SVGTextFragment>& fragments = textBox->textFragments(); + if (auto* textContentElement = SVGTextContentElement::elementFromRenderer(box->renderer().parent())) { + SVGLengthContext lengthContext(textContentElement); + m_desiredTextLength = textContentElement->specifiedTextLength().value(lengthContext); + + switch (textContentElement->lengthAdjust()) { + case SVGLengthAdjustUnknown: + break; + case SVGLengthAdjustSpacing: + m_chunkStyle |= LengthAdjustSpacing; + break; + case SVGLengthAdjustSpacingAndGlyphs: + m_chunkStyle |= LengthAdjustSpacingAndGlyphs; + break; + } + } - unsigned size = fragments.size(); - if (!size) - continue; + for (unsigned i = first; i < limit; ++i) + m_boxes.append(lineLayoutBoxes[i]); +} - for (unsigned i = 0; i < size; ++i) { - SVGTextFragment& fragment = fragments.at(i); +unsigned SVGTextChunk::totalCharacters() const +{ + unsigned characters = 0; + for (auto* box : m_boxes) { + for (auto& fragment : box->textFragments()) characters += fragment.length; + } + return characters; +} - if (m_chunkStyle & VerticalText) - length += fragment.height; - else - length += fragment.width; - - if (!lastFragment) { - lastFragment = &fragment; - continue; - } - - // Resepect gap between chunks. - if (m_chunkStyle & VerticalText) - length += fragment.y - (lastFragment->y + lastFragment->height); - else - length += fragment.x - (lastFragment->x + lastFragment->width); +float SVGTextChunk::totalLength() const +{ + const SVGTextFragment* firstFragment = nullptr; + const SVGTextFragment* lastFragment = nullptr; + + for (auto* box : m_boxes) { + auto& fragments = box->textFragments(); + if (fragments.size()) { + firstFragment = &(*fragments.begin()); + break; + } + } - lastFragment = &fragment; + for (auto it = m_boxes.rbegin(), end = m_boxes.rend(); it != end; ++it) { + auto& fragments = (*it)->textFragments(); + if (fragments.size()) { + lastFragment = &(*fragments.rbegin()); + break; } } + + ASSERT(!firstFragment == !lastFragment); + if (!firstFragment) + return 0; + + if (m_chunkStyle & VerticalText) + return (lastFragment->y + lastFragment->height) - firstFragment->y; + + return (lastFragment->x + lastFragment->width) - firstFragment->x; } -float SVGTextChunk::calculateTextAnchorShift(float length) const +float SVGTextChunk::totalAnchorShift() const { + float length = totalLength(); if (m_chunkStyle & MiddleAnchor) return -length / 2; if (m_chunkStyle & EndAnchor) @@ -79,6 +122,88 @@ float SVGTextChunk::calculateTextAnchorShift(float length) const return m_chunkStyle & RightToLeftText ? -length : 0; } -} // namespace WebCore +void SVGTextChunk::layout(HashMap<SVGInlineTextBox*, AffineTransform>& textBoxTransformations) const +{ + if (hasDesiredTextLength()) { + if (hasLengthAdjustSpacing()) + processTextLengthSpacingCorrection(); + else { + ASSERT(hasLengthAdjustSpacingAndGlyphs()); + buildBoxTransformations(textBoxTransformations); + } + } -#endif // ENABLE(SVG) + if (hasTextAnchor()) + processTextAnchorCorrection(); +} + +void SVGTextChunk::processTextLengthSpacingCorrection() const +{ + float textLengthShift = (desiredTextLength() - totalLength()) / totalCharacters(); + bool isVerticalText = m_chunkStyle & VerticalText; + unsigned atCharacter = 0; + + for (auto* box : m_boxes) { + for (auto& fragment : box->textFragments()) { + if (isVerticalText) + fragment.y += textLengthShift * atCharacter; + else + fragment.x += textLengthShift * atCharacter; + + atCharacter += fragment.length; + } + } +} + +void SVGTextChunk::buildBoxTransformations(HashMap<SVGInlineTextBox*, AffineTransform>& textBoxTransformations) const +{ + AffineTransform spacingAndGlyphsTransform; + bool foundFirstFragment = false; + + for (auto* box : m_boxes) { + if (!foundFirstFragment) { + if (!boxSpacingAndGlyphsTransform(box, spacingAndGlyphsTransform)) + continue; + foundFirstFragment = true; + } + + textBoxTransformations.set(box, spacingAndGlyphsTransform); + } +} + +bool SVGTextChunk::boxSpacingAndGlyphsTransform(const SVGInlineTextBox* box, AffineTransform& spacingAndGlyphsTransform) const +{ + auto& fragments = box->textFragments(); + if (fragments.isEmpty()) + return false; + + const SVGTextFragment& fragment = fragments.first(); + float scale = desiredTextLength() / totalLength(); + + spacingAndGlyphsTransform.translate(fragment.x, fragment.y); + + if (m_chunkStyle & VerticalText) + spacingAndGlyphsTransform.scaleNonUniform(1, scale); + else + spacingAndGlyphsTransform.scaleNonUniform(scale, 1); + + spacingAndGlyphsTransform.translate(-fragment.x, -fragment.y); + return true; +} + +void SVGTextChunk::processTextAnchorCorrection() const +{ + float textAnchorShift = totalAnchorShift(); + bool isVerticalText = m_chunkStyle & VerticalText; + + for (auto* box : m_boxes) { + for (auto& fragment : box->textFragments()) { + if (isVerticalText) + fragment.y += textAnchorShift; + else + fragment.x += textAnchorShift; + } + } +} + +} // namespace WebCore |