/* * Copyright (C) 2006 Oliver Hunt * Copyright (C) 2006 Apple Computer Inc. * Copyright (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 "RenderSVGInlineText.h" #include "RenderSVGText.h" #include "SVGInlineFlowBox.h" #include "SVGInlineTextBox.h" #include "SVGNames.h" #include "SVGRenderSupport.h" #include "SVGTextPositioningElement.h" namespace WebCore { void SVGRootInlineBox::paint(PaintInfo& paintInfo, int, int, int, int) { ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection); ASSERT(!paintInfo.context->paintingDisabled()); RenderObject* boxRenderer = renderer(); ASSERT(boxRenderer); bool isPrinting = renderer()->document()->printing(); bool hasSelection = !isPrinting && selectionState() != RenderObject::SelectionNone; PaintInfo childPaintInfo(paintInfo); if (hasSelection) { for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) { if (child->isSVGInlineTextBox()) static_cast(child)->paintSelectionBackground(childPaintInfo); else if (child->isSVGInlineFlowBox()) static_cast(child)->paintSelectionBackground(childPaintInfo); } } childPaintInfo.context->save(); if (SVGRenderSupport::prepareToRenderSVGContent(boxRenderer, childPaintInfo)) { for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) { if (child->isSVGInlineTextBox()) SVGInlineFlowBox::computeTextMatchMarkerRectForRenderer(toRenderSVGInlineText(static_cast(child)->textRenderer())); child->paint(childPaintInfo, 0, 0, 0, 0); } } SVGRenderSupport::finishRenderSVGContent(boxRenderer, childPaintInfo, paintInfo.context); childPaintInfo.context->restore(); } void SVGRootInlineBox::computePerCharacterLayoutInformation() { RenderSVGText* parentBlock = toRenderSVGText(block()); ASSERT(parentBlock); Vector& attributes = parentBlock->layoutAttributes(); if (parentBlock->needsReordering()) reorderValueLists(attributes); // Perform SVG text layout phase two (see SVGTextLayoutEngine for details). SVGTextLayoutEngine characterLayout(attributes); layoutCharactersInTextBoxes(this, characterLayout); // Perform SVG text layout phase three (see SVGTextChunkBuilder for details). characterLayout.finishLayout(); // Perform SVG text layout phase four // Position & resize all SVGInlineText/FlowBoxes in the inline box tree, resize the root box as well as the RenderSVGText parent block. layoutChildBoxes(this); layoutRootBox(); } void SVGRootInlineBox::layoutCharactersInTextBoxes(InlineFlowBox* start, SVGTextLayoutEngine& characterLayout) { for (InlineBox* child = start->firstChild(); child; child = child->nextOnLine()) { if (child->isSVGInlineTextBox()) { ASSERT(child->renderer()); ASSERT(child->renderer()->isSVGInlineText()); SVGInlineTextBox* textBox = static_cast(child); characterLayout.layoutInlineTextBox(textBox); } else { // Skip generated content. Node* node = child->renderer()->node(); if (!node) continue; ASSERT(child->isInlineFlowBox()); SVGInlineFlowBox* flowBox = static_cast(child); bool isTextPath = node->hasTagName(SVGNames::textPathTag); if (isTextPath) { // Build text chunks for all children, using the line layout algorithm. // This is needeed as text-anchor is just an additional startOffset for text paths. RenderSVGText* parentBlock = toRenderSVGText(block()); ASSERT(parentBlock); SVGTextLayoutEngine lineLayout(parentBlock->layoutAttributes()); layoutCharactersInTextBoxes(flowBox, lineLayout); characterLayout.beginTextPathLayout(child->renderer(), lineLayout); } layoutCharactersInTextBoxes(flowBox, characterLayout); if (isTextPath) characterLayout.endTextPathLayout(); } } } void SVGRootInlineBox::layoutChildBoxes(InlineFlowBox* start) { for (InlineBox* child = start->firstChild(); child; child = child->nextOnLine()) { if (child->isSVGInlineTextBox()) { ASSERT(child->renderer()); ASSERT(child->renderer()->isSVGInlineText()); SVGInlineTextBox* textBox = static_cast(child); IntRect boxRect = textBox->calculateBoundaries(); textBox->setX(boxRect.x()); textBox->setY(boxRect.y()); textBox->setLogicalWidth(boxRect.width()); textBox->setLogicalHeight(boxRect.height()); } else { // Skip generated content. if (!child->renderer()->node()) continue; ASSERT(child->isInlineFlowBox()); SVGInlineFlowBox* flowBox = static_cast(child); layoutChildBoxes(flowBox); IntRect boxRect = flowBox->calculateBoundaries(); flowBox->setX(boxRect.x()); flowBox->setY(boxRect.y()); flowBox->setLogicalWidth(boxRect.width()); flowBox->setLogicalHeight(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); setLogicalWidth(widthBlock); setLogicalHeight(heightBlock); setBlockLogicalHeight(heightBlock); setLineTopBottomPositions(0, heightBlock); } InlineBox* SVGRootInlineBox::closestLeafChildForPosition(const IntPoint& point) { InlineBox* firstLeaf = firstLeafChild(); InlineBox* lastLeaf = lastLeafChild(); if (firstLeaf == lastLeaf) return firstLeaf; // FIXME: Check for vertical text! InlineBox* closestLeaf = 0; for (InlineBox* leaf = firstLeaf; leaf; leaf = leaf->nextLeafChild()) { if (!leaf->isSVGInlineTextBox()) continue; if (point.y() < leaf->m_y) continue; if (point.y() > leaf->m_y + leaf->virtualLogicalHeight()) continue; closestLeaf = leaf; if (point.x() < leaf->m_x + leaf->m_logicalWidth) return leaf; } return closestLeaf ? closestLeaf : lastLeaf; } static inline void swapItemsInVector(Vector& firstVector, Vector& lastVector, unsigned first, unsigned last) { float temp = firstVector.at(first); firstVector.at(first) = lastVector.at(last); lastVector.at(last) = temp; } static inline void swapItemsInLayoutAttributes(SVGTextLayoutAttributes& firstAttributes, SVGTextLayoutAttributes& lastAttributes, unsigned firstPosition, unsigned lastPosition) { swapItemsInVector(firstAttributes.xValues(), lastAttributes.xValues(), firstPosition, lastPosition); swapItemsInVector(firstAttributes.yValues(), lastAttributes.yValues(), firstPosition, lastPosition); swapItemsInVector(firstAttributes.dxValues(), lastAttributes.dxValues(), firstPosition, lastPosition); swapItemsInVector(firstAttributes.dyValues(), lastAttributes.dyValues(), firstPosition, lastPosition); swapItemsInVector(firstAttributes.rotateValues(), lastAttributes.rotateValues(), firstPosition, lastPosition); } static inline void findFirstAndLastAttributesInVector(Vector& attributes, RenderSVGInlineText* firstContext, RenderSVGInlineText* lastContext, SVGTextLayoutAttributes*& first, SVGTextLayoutAttributes*& last) { first = 0; last = 0; unsigned attributesSize = attributes.size(); for (unsigned i = 0; i < attributesSize; ++i) { SVGTextLayoutAttributes& current = attributes.at(i); if (!first && firstContext == current.context()) first = ¤t; if (!last && lastContext == current.context()) last = ¤t; if (first && last) break; } ASSERT(first); ASSERT(last); } static inline void reverseInlineBoxRangeAndValueListsIfNeeded(void* userData, Vector::iterator first, Vector::iterator last) { ASSERT(userData); Vector& attributes = *reinterpret_cast*>(userData); // This is a copy of std::reverse(first, last). It additionally assure that the value lists within 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 = static_cast(*first); SVGInlineTextBox* lastTextBox = static_cast(*last); // Reordering is only necessary for BiDi text that is _absolutely_ positioned. if (firstTextBox->len() == 1 && firstTextBox->len() == lastTextBox->len()) { RenderSVGInlineText* firstContext = toRenderSVGInlineText(firstTextBox->textRenderer()); RenderSVGInlineText* lastContext = toRenderSVGInlineText(lastTextBox->textRenderer()); SVGTextLayoutAttributes* firstAttributes = 0; SVGTextLayoutAttributes* lastAttributes = 0; findFirstAndLastAttributesInVector(attributes, firstContext, lastContext, firstAttributes, lastAttributes); unsigned firstBoxPosition = firstTextBox->start(); unsigned firstBoxEnd = firstTextBox->end(); unsigned lastBoxPosition = lastTextBox->start(); unsigned lastBoxEnd = lastTextBox->end(); for (; firstBoxPosition <= firstBoxEnd && lastBoxPosition <= lastBoxEnd; ++lastBoxPosition, ++firstBoxPosition) swapItemsInLayoutAttributes(*firstAttributes, *lastAttributes, firstBoxPosition, lastBoxPosition); } InlineBox* temp = *first; *first = *last; *last = temp; ++first; } } void SVGRootInlineBox::reorderValueLists(Vector& attributes) { Vector leafBoxesInLogicalOrder; collectLeafBoxesInLogicalOrder(leafBoxesInLogicalOrder, reverseInlineBoxRangeAndValueListsIfNeeded, &attributes); } } // namespace WebCore #endif // ENABLE(SVG)