void SVGTextChunkBuilder::addTextChunk(Vector<SVGInlineTextBox*>& lineLayoutBoxes, unsigned boxStart, unsigned boxCount) { SVGInlineTextBox* textBox = lineLayoutBoxes[boxStart]; ASSERT(textBox); const RenderStyle* style = textBox->renderer().style(); ASSERT(style); const SVGRenderStyle* svgStyle = style->svgStyle(); ASSERT(svgStyle); // Build chunk style flags. unsigned chunkStyle = SVGTextChunk::DefaultStyle; // Handle 'direction' property. if (!style->isLeftToRightDirection()) chunkStyle |= SVGTextChunk::RightToLeftText; // Handle 'writing-mode' property. if (svgStyle->isVerticalWritingMode()) chunkStyle |= SVGTextChunk::VerticalText; // Handle 'text-anchor' property. switch (svgStyle->textAnchor()) { case TA_START: break; case TA_MIDDLE: chunkStyle |= SVGTextChunk::MiddleAnchor; break; case TA_END: chunkStyle |= SVGTextChunk::EndAnchor; break; }; // Handle 'lengthAdjust' property. float desiredTextLength = 0; if (SVGTextContentElement* textContentElement = SVGTextContentElement::elementFromRenderer(textBox->renderer().parent())) { SVGLengthContext lengthContext(textContentElement); desiredTextLength = textContentElement->specifiedTextLength().value(lengthContext); switch (textContentElement->lengthAdjust()) { case SVGLengthAdjustUnknown: break; case SVGLengthAdjustSpacing: chunkStyle |= SVGTextChunk::LengthAdjustSpacing; break; case SVGLengthAdjustSpacingAndGlyphs: chunkStyle |= SVGTextChunk::LengthAdjustSpacingAndGlyphs; break; }; } SVGTextChunk chunk(chunkStyle, desiredTextLength); Vector<SVGInlineTextBox*>& boxes = chunk.boxes(); for (unsigned i = boxStart; i < boxStart + boxCount; ++i) boxes.append(lineLayoutBoxes[i]); m_textChunks.append(chunk); }
static inline void reverseInlineBoxRangeAndValueListsIfNeeded(void* userData, Vector<InlineBox*>::iterator first, Vector<InlineBox*>::iterator last) { ASSERT(userData); Vector<SVGTextLayoutAttributes*>& attributes = *reinterpret_cast<Vector<SVGTextLayoutAttributes*>*>(userData); // This is a copy of std::reverse(first, last). It additionally assures that the metrics map within the renderers belonging to the InlineBoxes are reordered as well. while (true) { if (first == last || first == --last) return; if (!(*last)->isSVGInlineTextBox() || !(*first)->isSVGInlineTextBox()) { InlineBox* temp = *first; *first = *last; *last = temp; ++first; continue; } SVGInlineTextBox* firstTextBox = toSVGInlineTextBox(*first); SVGInlineTextBox* lastTextBox = toSVGInlineTextBox(*last); // Reordering is only necessary for BiDi text that is _absolutely_ positioned. if (firstTextBox->len() == 1 && firstTextBox->len() == lastTextBox->len()) { RenderSVGInlineText& firstContext = firstTextBox->renderer(); RenderSVGInlineText& lastContext = lastTextBox->renderer(); SVGTextLayoutAttributes* firstAttributes = 0; SVGTextLayoutAttributes* lastAttributes = 0; findFirstAndLastAttributesInVector(attributes, &firstContext, &lastContext, firstAttributes, lastAttributes); swapItemsInLayoutAttributes(firstAttributes, lastAttributes, firstTextBox->start(), lastTextBox->start()); } InlineBox* temp = *first; *first = *last; *last = temp; ++first; } }
static inline void dumpTextBoxes(Vector<SVGInlineTextBox*>& boxes) { unsigned boxCount = boxes.size(); fprintf(stderr, "Dumping all text fragments in text sub tree, %i boxes\n", boxCount); for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) { SVGInlineTextBox* textBox = boxes.at(boxPosition); Vector<SVGTextFragment>& fragments = textBox->textFragments(); fprintf(stderr, "-> Box %i: Dumping text fragments for SVGInlineTextBox, textBox=%p, textRenderer=%p\n", boxPosition, textBox, textBox->renderer()); fprintf(stderr, " textBox properties, start=%i, len=%i, box direction=%i\n", textBox->start(), textBox->len(), textBox->direction()); fprintf(stderr, " textRenderer properties, textLength=%i\n", textBox->renderer()->textLength()); const UChar* characters = textBox->renderer()->characters(); unsigned fragmentCount = fragments.size(); for (unsigned i = 0; i < fragmentCount; ++i) { SVGTextFragment& fragment = fragments.at(i); String fragmentString(characters + fragment.characterOffset, fragment.length); fprintf(stderr, " -> Fragment %i, x=%lf, y=%lf, width=%lf, height=%lf, characterOffset=%i, length=%i, characters='%s'\n" , i, fragment.x, fragment.y, fragment.width, fragment.height, fragment.characterOffset, fragment.length, fragmentString.utf8().data()); } } }
static float cummulatedWidthOrHeightOfTextChunk(SVGTextChunk& chunk, bool calcWidthOnly) { float length = 0.0f; Vector<SVGChar>::iterator charIt = chunk.start; Vector<SVGInlineBoxCharacterRange>::iterator it = chunk.boxes.begin(); Vector<SVGInlineBoxCharacterRange>::iterator end = chunk.boxes.end(); for (; it != end; ++it) { SVGInlineBoxCharacterRange& range = *it; SVGInlineTextBox* box = static_cast<SVGInlineTextBox*>(range.box); RenderStyle* style = box->renderer()->style(); for (int i = range.startOffset; i < range.endOffset; ++i) { ASSERT(charIt <= chunk.end); // Determine how many characters - starting from the current - can be measured at once. // Important for non-absolute positioned non-latin1 text (ie. Arabic) where ie. the width // of a string is not the sum of the boundaries of all contained glyphs. Vector<SVGChar>::iterator itSearch = charIt + 1; Vector<SVGChar>::iterator endSearch = charIt + range.endOffset - i; while (itSearch != endSearch) { // No need to check for 'isHidden()' here as this function is not called for text paths. if (itSearch->drawnSeperated) break; itSearch++; } unsigned int positionOffset = itSearch - charIt; // Calculate width/height of subrange SVGInlineBoxCharacterRange subRange; subRange.box = range.box; subRange.startOffset = i; subRange.endOffset = i + positionOffset; if (calcWidthOnly) length += cummulatedWidthOfInlineBoxCharacterRange(subRange); else length += cummulatedHeightOfInlineBoxCharacterRange(subRange); // Calculate gap between the previous & current range // <text x="10 50 70">ABCD</text> - we need to take the gaps between A & B into account // so add "40" as width, and analogous for B & C, add "20" as width. if (itSearch > chunk.start && itSearch < chunk.end) { SVGChar& lastCharacter = *(itSearch - 1); SVGChar& currentCharacter = *itSearch; int charsConsumed = 0; float glyphWidth = 0.0f; float glyphHeight = 0.0f; String glyphName; String unicodeString; box->measureCharacter(style, i + positionOffset - 1, charsConsumed, glyphName, unicodeString, glyphWidth, glyphHeight); if (calcWidthOnly) length += currentCharacter.x - lastCharacter.x - glyphWidth; else length += currentCharacter.y - lastCharacter.y - glyphHeight; } // Advance processed characters i += positionOffset - 1; charIt = itSearch; } } ASSERT(charIt == chunk.end); return length; }