diff -r 000000000000 -r 4f2f89ce4247 WebCore/rendering/SVGRootInlineBox.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/WebCore/rendering/SVGRootInlineBox.cpp Fri Sep 17 09:02:29 2010 +0300 @@ -0,0 +1,364 @@ +/* + * Copyright (C) 2006 Oliver Hunt + * (C) 2006 Apple Computer Inc. + * (C) 2007 Nikolas Zimmermann + * Copyright (C) Research In Motion Limited 2010. 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 "SVGRootInlineBox.h" + +#if ENABLE(SVG) +#include "GraphicsContext.h" +#include "RenderBlock.h" +#include "SVGInlineFlowBox.h" +#include "SVGInlineTextBox.h" +#include "SVGRenderSupport.h" +#include "SVGTextLayoutUtilities.h" +#include "SVGTextPositioningElement.h" + +// Text chunk part propagation can be traced by setting this variable > 0. +#define DEBUG_CHUNK_PART_PROPAGATION 0 + +namespace WebCore { + +void SVGRootInlineBox::paint(PaintInfo& paintInfo, int, int) +{ + ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection); + ASSERT(!paintInfo.context->paintingDisabled()); + + RenderObject* boxRenderer = renderer(); + ASSERT(boxRenderer); + + PaintInfo childPaintInfo(paintInfo); + childPaintInfo.context->save(); + + if (SVGRenderSupport::prepareToRenderSVGContent(boxRenderer, childPaintInfo)) { + for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) + child->paint(childPaintInfo, 0, 0); + } + + SVGRenderSupport::finishRenderSVGContent(boxRenderer, childPaintInfo, paintInfo.context); + childPaintInfo.context->restore(); +} + +void SVGRootInlineBox::computePerCharacterLayoutInformation() +{ + // Clean up any previous layout information + m_svgChars.clear(); + m_svgTextChunks.clear(); + + // Build layout information for all contained render objects + SVGCharacterLayoutInfo charInfo; + buildLayoutInformation(this, charInfo); + m_svgChars = charInfo.svgChars; + + // Now all layout information are available for every character + // contained in any of our child inline/flow boxes. Build list + // of text chunks now, to be able to apply text-anchor shifts. + SVGTextChunkLayoutInfo chunkInfo; + chunkInfo.buildTextChunks(m_svgChars.begin(), m_svgChars.end(), this); + + // Layout all text chunks + // text-anchor needs to be applied to individual chunks. + chunkInfo.layoutTextChunks(); + m_svgTextChunks = chunkInfo.textChunks(); + + // Propagate text chunk part information to all SVGInlineTextBoxes, see SVGTextChunkLayoutInfo.h for details + propagateTextChunkPartInformation(); + + // Layout all child boxes. + layoutChildBoxes(this); + + // Resize our root box and our RenderSVGText parent block + layoutRootBox(); +} + +void SVGRootInlineBox::buildLayoutInformation(InlineFlowBox* start, SVGCharacterLayoutInfo& info) +{ + if (start->isRootInlineBox()) { + ASSERT(start->renderer()->node()->hasTagName(SVGNames::textTag)); + + SVGTextPositioningElement* positioningElement = static_cast(start->renderer()->node()); + ASSERT(positioningElement); + ASSERT(positioningElement->parentNode()); + + info.addLayoutInformation(positioningElement); + } + + SVGLastGlyphInfo lastGlyph; + + for (InlineBox* curr = start->firstChild(); curr; curr = curr->nextOnLine()) { + if (curr->renderer()->isText()) + static_cast(curr)->buildLayoutInformation(info, lastGlyph); + else { + ASSERT(curr->isInlineFlowBox()); + InlineFlowBox* flowBox = static_cast(curr); + + // Skip generated content. + if (!flowBox->renderer()->node()) + continue; + + bool isAnchor = flowBox->renderer()->node()->hasTagName(SVGNames::aTag); + bool isTextPath = flowBox->renderer()->node()->hasTagName(SVGNames::textPathTag); + + if (!isTextPath && !isAnchor) { + SVGTextPositioningElement* positioningElement = static_cast(flowBox->renderer()->node()); + ASSERT(positioningElement); + ASSERT(positioningElement->parentNode()); + + info.addLayoutInformation(positioningElement); + } else if (!isAnchor) { + info.setInPathLayout(true); + + // Handle text-anchor/textLength on path, which is special. + SVGTextContentElement* textContent = 0; + Node* node = flowBox->renderer()->node(); + if (node && node->isSVGElement()) + textContent = static_cast(node); + ASSERT(textContent); + + ELengthAdjust lengthAdjust = (ELengthAdjust) textContent->lengthAdjust(); + ETextAnchor anchor = flowBox->renderer()->style()->svgStyle()->textAnchor(); + float textAnchorStartOffset = 0.0f; + + // Initialize sub-layout. We need to create text chunks from the textPath + // children using our standard layout code, to be able to measure the + // text length using our normal methods and not textPath specific hacks. + Vector tempChunks; + + SVGCharacterLayoutInfo tempCharInfo; + buildLayoutInformation(flowBox, tempCharInfo); + + SVGTextChunkLayoutInfo tempChunkInfo; + tempChunkInfo.buildTextChunks(tempCharInfo.svgChars.begin(), tempCharInfo.svgChars.end(), flowBox); + tempChunks = tempChunkInfo.textChunks(); + + Vector::iterator it = tempChunks.begin(); + Vector::iterator end = tempChunks.end(); + + float computedLength = 0.0f; + + for (; it != end; ++it) { + SVGTextChunk& chunk = *it; + + // Apply text-length calculation + info.pathExtraAdvance += calculateTextLengthCorrectionForTextChunk(chunk, lengthAdjust, computedLength); + + if (lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) { + info.pathTextLength += computedLength; + info.pathChunkLength += chunk.textLength; + } + + // Calculate text-anchor start offset + if (anchor == TA_START) + continue; + + textAnchorStartOffset += calculateTextAnchorShiftForTextChunk(chunk, anchor); + } + + info.addLayoutInformation(flowBox, textAnchorStartOffset); + } + + float shiftxSaved = info.shiftx; + float shiftySaved = info.shifty; + + buildLayoutInformation(flowBox, info); + info.processedChunk(shiftxSaved, shiftySaved); + + if (isTextPath) + info.setInPathLayout(false); + } + } +} + +void SVGRootInlineBox::layoutChildBoxes(InlineFlowBox* start) +{ + for (InlineBox* child = start->firstChild(); child; child = child->nextOnLine()) { + if (child->renderer()->isText()) { + SVGInlineTextBox* textBox = static_cast(child); + IntRect boxRect = textBox->calculateBoundaries(); + textBox->setX(boxRect.x()); + textBox->setY(boxRect.y()); + textBox->setWidth(boxRect.width()); + textBox->setHeight(boxRect.height()); + } else { + ASSERT(child->isInlineFlowBox()); + + // Skip generated content. + if (!child->renderer()->node()) + continue; + + SVGInlineFlowBox* flowBox = static_cast(child); + layoutChildBoxes(flowBox); + + IntRect boxRect = flowBox->calculateBoundaries(); + flowBox->setX(boxRect.x()); + flowBox->setY(boxRect.y()); + flowBox->setWidth(boxRect.width()); + flowBox->setHeight(boxRect.height()); + } + } +} + +void SVGRootInlineBox::layoutRootBox() +{ + RenderBlock* parentBlock = block(); + ASSERT(parentBlock); + + IntRect childRect; + for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) { + // Skip generated content. + if (!child->renderer()->node()) + continue; + childRect.unite(child->calculateBoundaries()); + } + + int xBlock = childRect.x(); + int yBlock = childRect.y(); + int widthBlock = childRect.width(); + int heightBlock = childRect.height(); + + // Finally, assign the root block position, now that all content is laid out. + parentBlock->setLocation(xBlock, yBlock); + parentBlock->setWidth(widthBlock); + parentBlock->setHeight(heightBlock); + + // Position all children relative to the parent block. + for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) { + // Skip generated content. + if (!child->renderer()->node()) + continue; + child->adjustPosition(-xBlock, -yBlock); + } + + // Position ourselves. + setX(0); + setY(0); + setWidth(widthBlock); + setHeight(heightBlock); + setBlockHeight(heightBlock); + setLineTopBottomPositions(0, heightBlock); +} + +void SVGRootInlineBox::propagateTextChunkPartInformation() +{ +#if DEBUG_CHUNK_PART_PROPAGATION > 0 + ListHashSet boxes; +#endif + + // Loop through all text chunks + const Vector::const_iterator end = m_svgTextChunks.end(); + for (Vector::const_iterator it = m_svgTextChunks.begin(); it != end; ++it) { + const SVGTextChunk& chunk = *it; + int processedChunkCharacters = 0; + + // Loop through all ranges contained in this chunk + const Vector::const_iterator boxEnd = chunk.boxes.end(); + for (Vector::const_iterator boxIt = chunk.boxes.begin(); boxIt != boxEnd; ++boxIt) { + const SVGInlineBoxCharacterRange& range = *boxIt; + ASSERT(range.box->isSVGInlineTextBox()); + + // Access style & font information of this text box + SVGInlineTextBox* rangeTextBox = static_cast(range.box); + rangeTextBox->setChunkTransformation(chunk.ctm); + + RenderText* text = rangeTextBox->textRenderer(); + ASSERT(text); + + RenderStyle* style = text->style(); + ASSERT(style); + + const Font& font = style->font(); + + // Figure out first and last character of this range in this chunk + int rangeLength = range.endOffset - range.startOffset; + Vector::iterator itCharBegin = chunk.start + processedChunkCharacters; + Vector::iterator itCharEnd = chunk.start + processedChunkCharacters + rangeLength; + ASSERT(itCharEnd <= chunk.end); + + // Loop through all characters in range + int processedRangeCharacters = 0; + for (Vector::iterator itChar = itCharBegin; itChar != itCharEnd; ++itChar) { + if (itChar->isHidden()) { + ++processedRangeCharacters; + continue; + } + + // Determine how many characters - starting from the current - can be drawn at once. + Vector::iterator itSearch = itChar + 1; + while (itSearch != itCharEnd) { + if (itSearch->drawnSeperated || itSearch->isHidden()) + break; + + ++itSearch; + } + + // Calculate text chunk part information for this chunk sub-range + const UChar* partStart = text->characters() + rangeTextBox->start() + range.startOffset + processedRangeCharacters; + + SVGTextChunkPart part; + part.firstCharacter = itChar; + part.length = itSearch - itChar; + part.width = font.floatWidth(svgTextRunForInlineTextBox(partStart, part.length, style, rangeTextBox)); + part.height = font.height(); + part.offset = range.startOffset + processedRangeCharacters; + rangeTextBox->addChunkPartInformation(part); + processedRangeCharacters += part.length; + + // Skip processed characters + itChar = itSearch - 1; + } + + ASSERT(processedRangeCharacters == rangeLength); + processedChunkCharacters += rangeLength; + +#if DEBUG_CHUNK_PART_PROPAGATION > 0 + boxes.add(rangeTextBox); +#endif + } + } + +#if DEBUG_CHUNK_PART_PROPAGATION > 0 + { + fprintf(stderr, "Propagated text chunk part information:\n"); + + ListHashSet::const_iterator it = boxes.begin(); + const ListHashSet::const_iterator end = boxes.end(); + + for (; it != end; ++it) { + const SVGInlineTextBox* box = *it; + const Vector& parts = box->svgTextChunkParts(); + + fprintf(stderr, " Box %p contains %i text chunk parts:\n", box, static_cast(parts.size())); + Vector::const_iterator partIt = parts.begin(); + const Vector::const_iterator partEnd = parts.end(); + for (; partIt != partEnd; ++partIt) { + const SVGTextChunkPart& part = *partIt; + fprintf(stderr, " -> firstCharacter x=%lf, y=%lf, offset=%i, length=%i, width=%lf, height=%lf, textRenderer=%p\n" + , part.firstCharacter->x, part.firstCharacter->y, part.offset, part.length, part.width, part.height, box->textRenderer()); + } + } + } +#endif +} + +} // namespace WebCore + +#endif // ENABLE(SVG)