void SVGTextChunkBuilder::processTextChunk(const SVGTextChunk& chunk) { bool processTextLength = chunk.hasDesiredTextLength(); bool processTextAnchor = chunk.hasTextAnchor(); if (!processTextAnchor && !processTextLength) return; const Vector<SVGInlineTextBox*>& boxes = chunk.boxes(); unsigned boxCount = boxes.size(); if (!boxCount) return; // Calculate absolute length of whole text chunk (starting from text box 'start', spanning 'length' text boxes). float chunkLength = 0; unsigned chunkCharacters = 0; chunk.calculateLength(chunkLength, chunkCharacters); bool isVerticalText = chunk.isVerticalText(); if (processTextLength) { if (chunk.hasLengthAdjustSpacing()) { float textLengthShift = (chunk.desiredTextLength() - chunkLength) / chunkCharacters; unsigned atCharacter = 0; for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) { Vector<SVGTextFragment>& fragments = boxes[boxPosition]->textFragments(); if (fragments.isEmpty()) continue; processTextLengthSpacingCorrection(isVerticalText, textLengthShift, fragments, atCharacter); } } else { ASSERT(chunk.hasLengthAdjustSpacingAndGlyphs()); float textLengthScale = chunk.desiredTextLength() / chunkLength; AffineTransform spacingAndGlyphsTransform; bool foundFirstFragment = false; for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) { SVGInlineTextBox* textBox = boxes[boxPosition]; Vector<SVGTextFragment>& fragments = textBox->textFragments(); if (fragments.isEmpty()) continue; if (!foundFirstFragment) { foundFirstFragment = true; buildSpacingAndGlyphsTransform(isVerticalText, textLengthScale, fragments.first(), spacingAndGlyphsTransform); } m_textBoxTransformations.set(textBox, spacingAndGlyphsTransform); } } } if (!processTextAnchor) return; // If we previously applied a lengthAdjust="spacing" correction, we have to recalculate the chunk length, to be able to apply the text-anchor shift. if (processTextLength && chunk.hasLengthAdjustSpacing()) { chunkLength = 0; chunkCharacters = 0; chunk.calculateLength(chunkLength, chunkCharacters); } float textAnchorShift = chunk.calculateTextAnchorShift(chunkLength); for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) { Vector<SVGTextFragment>& fragments = boxes[boxPosition]->textFragments(); if (fragments.isEmpty()) continue; processTextAnchorCorrection(isVerticalText, textAnchorShift, fragments); } }
void SVGTextChunkBuilder::handleTextChunk(BoxListConstIterator boxStart, BoxListConstIterator boxEnd) { ASSERT(*boxStart); const LineLayoutSVGInlineText textLineLayout = LineLayoutSVGInlineText((*boxStart)->getLineLayoutItem()); const ComputedStyle& style = textLineLayout.styleRef(); // Handle 'lengthAdjust' property. float desiredTextLength = 0; SVGLengthAdjustType lengthAdjust = SVGLengthAdjustUnknown; if (SVGTextContentElement* textContentElement = SVGTextContentElement::elementFromLineLayoutItem( textLineLayout.parent())) { lengthAdjust = textContentElement->lengthAdjust()->currentValue()->enumValue(); SVGLengthContext lengthContext(textContentElement); if (textContentElement->textLengthIsSpecifiedByUser()) desiredTextLength = textContentElement->textLength()->currentValue()->value( lengthContext); else desiredTextLength = 0; } bool processTextLength = desiredTextLength > 0; bool processTextAnchor = needsTextAnchorAdjustment(style); if (!processTextAnchor && !processTextLength) return; bool isVerticalText = !style.isHorizontalWritingMode(); // Calculate absolute length of whole text chunk (starting from text box // 'start', spanning 'length' text boxes). ChunkLengthAccumulator lengthAccumulator(isVerticalText); lengthAccumulator.processRange(boxStart, boxEnd); if (processTextLength) { float chunkLength = lengthAccumulator.length(); if (lengthAdjust == SVGLengthAdjustSpacing) { float textLengthShift = (desiredTextLength - chunkLength) / lengthAccumulator.numCharacters(); unsigned atCharacter = 0; for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) { Vector<SVGTextFragment>& fragments = (*boxIter)->textFragments(); if (fragments.isEmpty()) continue; processTextLengthSpacingCorrection(isVerticalText, textLengthShift, fragments, atCharacter); } // Fragments have been adjusted, we have to recalculate the chunk // length, to be able to apply the text-anchor shift. if (processTextAnchor) { lengthAccumulator.reset(); lengthAccumulator.processRange(boxStart, boxEnd); } } else { ASSERT(lengthAdjust == SVGLengthAdjustSpacingAndGlyphs); float textLengthScale = desiredTextLength / chunkLength; float textLengthBias = 0; bool foundFirstFragment = false; for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) { SVGInlineTextBox* textBox = *boxIter; Vector<SVGTextFragment>& fragments = textBox->textFragments(); if (fragments.isEmpty()) continue; if (!foundFirstFragment) { foundFirstFragment = true; textLengthBias = computeTextLengthBias(fragments.first(), textLengthScale); } applyTextLengthScaleAdjustment(textLengthScale, textLengthBias, fragments); } } } if (!processTextAnchor) return; float textAnchorShift = calculateTextAnchorShift(style, lengthAccumulator.length()); for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) { Vector<SVGTextFragment>& fragments = (*boxIter)->textFragments(); if (fragments.isEmpty()) continue; processTextAnchorCorrection(isVerticalText, textAnchorShift, fragments); } }