--- /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 <ojh16@student.canterbury.ac.nz>
+ * (C) 2006 Apple Computer Inc.
+ * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * 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<SVGTextPositioningElement*>(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<SVGInlineTextBox*>(curr)->buildLayoutInformation(info, lastGlyph);
+ else {
+ ASSERT(curr->isInlineFlowBox());
+ InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(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<SVGTextPositioningElement*>(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<SVGTextContentElement*>(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<SVGTextChunk> tempChunks;
+
+ SVGCharacterLayoutInfo tempCharInfo;
+ buildLayoutInformation(flowBox, tempCharInfo);
+
+ SVGTextChunkLayoutInfo tempChunkInfo;
+ tempChunkInfo.buildTextChunks(tempCharInfo.svgChars.begin(), tempCharInfo.svgChars.end(), flowBox);
+ tempChunks = tempChunkInfo.textChunks();
+
+ Vector<SVGTextChunk>::iterator it = tempChunks.begin();
+ Vector<SVGTextChunk>::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<SVGInlineTextBox*>(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<SVGInlineFlowBox*>(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<SVGInlineTextBox*> boxes;
+#endif
+
+ // Loop through all text chunks
+ const Vector<SVGTextChunk>::const_iterator end = m_svgTextChunks.end();
+ for (Vector<SVGTextChunk>::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<SVGInlineBoxCharacterRange>::const_iterator boxEnd = chunk.boxes.end();
+ for (Vector<SVGInlineBoxCharacterRange>::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<SVGInlineTextBox*>(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<SVGChar>::iterator itCharBegin = chunk.start + processedChunkCharacters;
+ Vector<SVGChar>::iterator itCharEnd = chunk.start + processedChunkCharacters + rangeLength;
+ ASSERT(itCharEnd <= chunk.end);
+
+ // Loop through all characters in range
+ int processedRangeCharacters = 0;
+ for (Vector<SVGChar>::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<SVGChar>::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<SVGInlineTextBox*>::const_iterator it = boxes.begin();
+ const ListHashSet<SVGInlineTextBox*>::const_iterator end = boxes.end();
+
+ for (; it != end; ++it) {
+ const SVGInlineTextBox* box = *it;
+ const Vector<SVGTextChunkPart>& parts = box->svgTextChunkParts();
+
+ fprintf(stderr, " Box %p contains %i text chunk parts:\n", box, static_cast<int>(parts.size()));
+ Vector<SVGTextChunkPart>::const_iterator partIt = parts.begin();
+ const Vector<SVGTextChunkPart>::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)