Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(890)

Unified Diff: third_party/WebKit/WebCore/rendering/SVGRootInlineBox.cpp

Issue 21184: WebKit merge 40722:40785 (part 1) (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 11 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/WebCore/rendering/SVGRootInlineBox.cpp
===================================================================
--- third_party/WebKit/WebCore/rendering/SVGRootInlineBox.cpp (revision 9391)
+++ third_party/WebKit/WebCore/rendering/SVGRootInlineBox.cpp (working copy)
@@ -1,1719 +1,1719 @@
-/*
- * This file is part of the WebKit project.
- *
- * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz>
- * (C) 2006 Apple Computer Inc.
- * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
- *
- * 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"
-
-#if ENABLE(SVG)
-#include "SVGRootInlineBox.h"
-
-#include "Editor.h"
-#include "Frame.h"
-#include "GraphicsContext.h"
-#include "RenderBlock.h"
-#include "RenderSVGRoot.h"
-#include "SVGInlineFlowBox.h"
-#include "SVGInlineTextBox.h"
-#include "SVGFontElement.h"
-#include "SVGPaintServer.h"
-#include "SVGRenderStyleDefs.h"
-#include "SVGRenderSupport.h"
-#include "SVGResourceFilter.h"
-#include "SVGTextPositioningElement.h"
-#include "SVGURIReference.h"
-#include "Text.h"
-#include "UnicodeRange.h"
-
-#include <float.h>
-
-// Text chunk creation is complex and the whole process
-// can easily be traced by setting this variable > 0.
-#define DEBUG_CHUNK_BUILDING 0
-
-namespace WebCore {
-
-static inline bool isVerticalWritingMode(const SVGRenderStyle* style)
-{
- return style->writingMode() == WM_TBRL || style->writingMode() == WM_TB;
-}
-
-static inline EAlignmentBaseline dominantBaselineToShift(bool isVerticalText, const RenderObject* text, const Font& font)
-{
- ASSERT(text);
-
- const SVGRenderStyle* style = text->style() ? text->style()->svgStyle() : 0;
- ASSERT(style);
-
- const SVGRenderStyle* parentStyle = text->parent() && text->parent()->style() ? text->parent()->style()->svgStyle() : 0;
-
- EDominantBaseline baseline = style->dominantBaseline();
- if (baseline == DB_AUTO) {
- if (isVerticalText)
- baseline = DB_CENTRAL;
- else
- baseline = DB_ALPHABETIC;
- }
-
- switch (baseline) {
- case DB_USE_SCRIPT:
- // TODO: The dominant-baseline and the baseline-table components are set by
- // determining the predominant script of the character data content.
- return AB_ALPHABETIC;
- case DB_NO_CHANGE:
- {
- if (parentStyle)
- return dominantBaselineToShift(isVerticalText, text->parent(), font);
-
- ASSERT_NOT_REACHED();
- return AB_AUTO;
- }
- case DB_RESET_SIZE:
- {
- if (parentStyle)
- return dominantBaselineToShift(isVerticalText, text->parent(), font);
-
- ASSERT_NOT_REACHED();
- return AB_AUTO;
- }
- case DB_IDEOGRAPHIC:
- return AB_IDEOGRAPHIC;
- case DB_ALPHABETIC:
- return AB_ALPHABETIC;
- case DB_HANGING:
- return AB_HANGING;
- case DB_MATHEMATICAL:
- return AB_MATHEMATICAL;
- case DB_CENTRAL:
- return AB_CENTRAL;
- case DB_MIDDLE:
- return AB_MIDDLE;
- case DB_TEXT_AFTER_EDGE:
- return AB_TEXT_AFTER_EDGE;
- case DB_TEXT_BEFORE_EDGE:
- return AB_TEXT_BEFORE_EDGE;
- default:
- ASSERT_NOT_REACHED();
- return AB_AUTO;
- }
-}
-
-static inline float alignmentBaselineToShift(bool isVerticalText, const RenderObject* text, const Font& font)
-{
- ASSERT(text);
-
- const SVGRenderStyle* style = text->style() ? text->style()->svgStyle() : 0;
- ASSERT(style);
-
- const SVGRenderStyle* parentStyle = text->parent() && text->parent()->style() ? text->parent()->style()->svgStyle() : 0;
-
- EAlignmentBaseline baseline = style->alignmentBaseline();
- if (baseline == AB_AUTO) {
- if (parentStyle && style->dominantBaseline() == DB_AUTO)
- baseline = dominantBaselineToShift(isVerticalText, text->parent(), font);
- else
- baseline = dominantBaselineToShift(isVerticalText, text, font);
-
- ASSERT(baseline != AB_AUTO);
- }
-
- // Note: http://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling
- switch (baseline) {
- case AB_BASELINE:
- {
- if (parentStyle)
- return dominantBaselineToShift(isVerticalText, text->parent(), font);
-
- return 0.0f;
- }
- case AB_BEFORE_EDGE:
- case AB_TEXT_BEFORE_EDGE:
- return font.ascent();
- case AB_MIDDLE:
- return font.xHeight() / 2.0f;
- case AB_CENTRAL:
- // Not needed, we're taking this into account already for vertical text!
- // return (font.ascent() - font.descent()) / 2.0f;
- return 0.0f;
- case AB_AFTER_EDGE:
- case AB_TEXT_AFTER_EDGE:
- case AB_IDEOGRAPHIC:
- return font.descent();
- case AB_ALPHABETIC:
- return 0.0f;
- case AB_HANGING:
- return font.ascent() * 8.0f / 10.0f;
- case AB_MATHEMATICAL:
- return font.ascent() / 2.0f;
- default:
- ASSERT_NOT_REACHED();
- return 0.0f;
- }
-}
-
-static inline float glyphOrientationToAngle(const SVGRenderStyle* svgStyle, bool isVerticalText, const UChar& character)
-{
- switch (isVerticalText ? svgStyle->glyphOrientationVertical() : svgStyle->glyphOrientationHorizontal()) {
- case GO_AUTO:
- {
- // Spec: Fullwidth ideographic and fullwidth Latin text will be set with a glyph-orientation of 0-degrees.
- // Text which is not fullwidth will be set with a glyph-orientation of 90-degrees.
- unsigned int unicodeRange = findCharUnicodeRange(character);
- if (unicodeRange == cRangeSetLatin || unicodeRange == cRangeArabic)
- return 90.0f;
-
- return 0.0f;
- }
- case GO_90DEG:
- return 90.0f;
- case GO_180DEG:
- return 180.0f;
- case GO_270DEG:
- return 270.0f;
- case GO_0DEG:
- default:
- return 0.0f;
- }
-}
-
-static inline bool glyphOrientationIsMultiplyOf180Degrees(float orientationAngle)
-{
- return fabsf(fmodf(orientationAngle, 180.0f)) == 0.0f;
-}
-
-static inline float calculateGlyphAdvanceAndShiftRespectingOrientation(bool isVerticalText, float orientationAngle, float glyphWidth, float glyphHeight, const Font& font, SVGChar& svgChar, float& xOrientationShift, float& yOrientationShift)
-{
- bool orientationIsMultiplyOf180Degrees = glyphOrientationIsMultiplyOf180Degrees(orientationAngle);
-
- // The function is based on spec requirements:
- //
- // Spec: If the 'glyph-orientation-horizontal' results in an orientation angle that is not a multiple of
- // of 180 degrees, then the current text position is incremented according to the vertical metrics of the glyph.
- //
- // Spec: If if the 'glyph-orientation-vertical' results in an orientation angle that is not a multiple of
- // 180 degrees,then the current text position is incremented according to the horizontal metrics of the glyph.
-
- // vertical orientation handling
- if (isVerticalText) {
- if (orientationAngle == 0.0f) {
- xOrientationShift = -glyphWidth / 2.0f;
- yOrientationShift = font.ascent();
- } else if (orientationAngle == 90.0f) {
- xOrientationShift = -glyphHeight;
- yOrientationShift = font.descent();
- svgChar.orientationShiftY = -font.ascent();
- } else if (orientationAngle == 270.0f) {
- xOrientationShift = glyphHeight;
- yOrientationShift = font.descent();
- svgChar.orientationShiftX = -glyphWidth;
- svgChar.orientationShiftY = -font.ascent();
- } else if (orientationAngle == 180.0f) {
- yOrientationShift = font.ascent();
- svgChar.orientationShiftX = -glyphWidth / 2.0f;
- svgChar.orientationShiftY = font.ascent() - font.descent();
- }
-
- // vertical advance calculation
- if (orientationAngle != 0.0f && !orientationIsMultiplyOf180Degrees)
- return glyphWidth;
-
- return glyphHeight;
- }
-
- // horizontal orientation handling
- if (orientationAngle == 90.0f) {
- xOrientationShift = glyphWidth / 2.0f;
- yOrientationShift = -font.descent();
- svgChar.orientationShiftX = -glyphWidth / 2.0f - font.descent();
- svgChar.orientationShiftY = font.descent();
- } else if (orientationAngle == 270.0f) {
- xOrientationShift = -glyphWidth / 2.0f;
- yOrientationShift = -font.descent();
- svgChar.orientationShiftX = -glyphWidth / 2.0f + font.descent();
- svgChar.orientationShiftY = glyphHeight;
- } else if (orientationAngle == 180.0f) {
- xOrientationShift = glyphWidth / 2.0f;
- svgChar.orientationShiftX = -glyphWidth / 2.0f;
- svgChar.orientationShiftY = font.ascent() - font.descent();
- }
-
- // horizontal advance calculation
- if (orientationAngle != 0.0f && !orientationIsMultiplyOf180Degrees)
- return glyphHeight;
-
- return glyphWidth;
-}
-
-static inline void startTextChunk(SVGTextChunkLayoutInfo& info)
-{
- info.chunk.boxes.clear();
- info.chunk.boxes.append(SVGInlineBoxCharacterRange());
-
- info.chunk.start = info.it;
- info.assignChunkProperties = true;
-}
-
-static inline void closeTextChunk(SVGTextChunkLayoutInfo& info)
-{
- ASSERT(!info.chunk.boxes.last().isOpen());
- ASSERT(info.chunk.boxes.last().isClosed());
-
- info.chunk.end = info.it;
- ASSERT(info.chunk.end >= info.chunk.start);
-
- info.svgTextChunks.append(info.chunk);
-}
-
-RenderSVGRoot* findSVGRootObject(RenderObject* start)
-{
- // Find associated root inline box
- while (start && !start->isSVGRoot())
- start = start->parent();
-
- ASSERT(start);
- ASSERT(start->isSVGRoot());
-
- return static_cast<RenderSVGRoot*>(start);
-}
-
-static inline FloatPoint topLeftPositionOfCharacterRange(Vector<SVGChar>& chars)
-{
- return topLeftPositionOfCharacterRange(chars.begin(), chars.end());
-}
-
-FloatPoint topLeftPositionOfCharacterRange(Vector<SVGChar>::iterator it, Vector<SVGChar>::iterator end)
-{
- float lowX = FLT_MAX, lowY = FLT_MAX;
- for (; it != end; ++it) {
- if (it->isHidden())
- continue;
-
- float x = (*it).x;
- float y = (*it).y;
-
- if (x < lowX)
- lowX = x;
-
- if (y < lowY)
- lowY = y;
- }
-
- return FloatPoint(lowX, lowY);
-}
-
-// Helper function
-static float calculateKerning(RenderObject* item)
-{
- const Font& font = item->style()->font();
- const SVGRenderStyle* svgStyle = item->style()->svgStyle();
-
- float kerning = 0.0f;
- if (CSSPrimitiveValue* primitive = static_cast<CSSPrimitiveValue*>(svgStyle->kerning())) {
- kerning = primitive->getFloatValue();
-
- if (primitive->primitiveType() == CSSPrimitiveValue::CSS_PERCENTAGE && font.pixelSize() > 0)
- kerning = kerning / 100.0f * font.pixelSize();
- }
-
- return kerning;
-}
-
-// Helper class for paint()
-struct SVGRootInlineBoxPaintWalker {
- SVGRootInlineBoxPaintWalker(SVGRootInlineBox* rootBox, SVGResourceFilter* rootFilter, RenderObject::PaintInfo paintInfo, int tx, int ty)
- : m_rootBox(rootBox)
- , m_chunkStarted(false)
- , m_paintInfo(paintInfo)
- , m_savedInfo(paintInfo)
- , m_boundingBox(tx + rootBox->xPos(), ty + rootBox->yPos(), rootBox->width(), rootBox->height())
- , m_filter(0)
- , m_rootFilter(rootFilter)
- , m_fillPaintServer(0)
- , m_strokePaintServer(0)
- , m_fillPaintServerObject(0)
- , m_strokePaintServerObject(0)
- , m_tx(tx)
- , m_ty(ty)
- {
- }
-
- ~SVGRootInlineBoxPaintWalker()
- {
- ASSERT(!m_filter);
- ASSERT(!m_fillPaintServer);
- ASSERT(!m_fillPaintServerObject);
- ASSERT(!m_strokePaintServer);
- ASSERT(!m_strokePaintServerObject);
- ASSERT(!m_chunkStarted);
- }
-
- void teardownFillPaintServer()
- {
- if (!m_fillPaintServer)
- return;
-
- m_fillPaintServer->teardown(m_paintInfo.context, m_fillPaintServerObject, ApplyToFillTargetType, true);
-
- m_fillPaintServer = 0;
- m_fillPaintServerObject = 0;
- }
-
- void teardownStrokePaintServer()
- {
- if (!m_strokePaintServer)
- return;
-
- m_strokePaintServer->teardown(m_paintInfo.context, m_strokePaintServerObject, ApplyToStrokeTargetType, true);
-
- m_strokePaintServer = 0;
- m_strokePaintServerObject = 0;
- }
-
- void chunkStartCallback(InlineBox* box)
- {
- ASSERT(!m_chunkStarted);
- m_chunkStarted = true;
-
- InlineFlowBox* flowBox = box->parent();
-
- // Initialize text rendering
- RenderObject* object = flowBox->object();
- ASSERT(object);
-
- m_savedInfo = m_paintInfo;
- m_paintInfo.context->save();
-
- if (!flowBox->isRootInlineBox())
- m_paintInfo.context->concatCTM(m_rootBox->object()->localTransform());
-
- m_paintInfo.context->concatCTM(object->localTransform());
-
- if (!flowBox->isRootInlineBox()) {
- prepareToRenderSVGContent(object, m_paintInfo, m_boundingBox, m_filter, m_rootFilter);
- m_paintInfo.rect = object->localTransform().inverse().mapRect(m_paintInfo.rect);
- }
- }
-
- void chunkEndCallback(InlineBox* box)
- {
- ASSERT(m_chunkStarted);
- m_chunkStarted = false;
-
- InlineFlowBox* flowBox = box->parent();
-
- RenderObject* object = flowBox->object();
- ASSERT(object);
-
- // Clean up last used paint server
- teardownFillPaintServer();
- teardownStrokePaintServer();
-
- // Finalize text rendering
- if (!flowBox->isRootInlineBox()) {
- finishRenderSVGContent(object, m_paintInfo, m_boundingBox, m_filter, m_savedInfo.context);
- m_filter = 0;
- }
-
- // Restore context & repaint rect
- m_paintInfo.context->restore();
- m_paintInfo.rect = m_savedInfo.rect;
- }
-
- bool chunkSetupFillCallback(InlineBox* box)
- {
- InlineFlowBox* flowBox = box->parent();
-
- // Setup fill paint server
- RenderObject* object = flowBox->object();
- ASSERT(object);
-
- ASSERT(!m_strokePaintServer);
- teardownFillPaintServer();
-
- m_fillPaintServer = SVGPaintServer::fillPaintServer(object->style(), object);
- if (m_fillPaintServer) {
- m_fillPaintServer->setup(m_paintInfo.context, object, ApplyToFillTargetType, true);
- m_fillPaintServerObject = object;
- return true;
- }
-
- return false;
- }
-
- bool chunkSetupStrokeCallback(InlineBox* box)
- {
- InlineFlowBox* flowBox = box->parent();
-
- // Setup stroke paint server
- RenderObject* object = flowBox->object();
- ASSERT(object);
-
- // If we're both stroked & filled, teardown fill paint server before stroking.
- teardownFillPaintServer();
- teardownStrokePaintServer();
-
- m_strokePaintServer = SVGPaintServer::strokePaintServer(object->style(), object);
-
- if (m_strokePaintServer) {
- m_strokePaintServer->setup(m_paintInfo.context, object, ApplyToStrokeTargetType, true);
- m_strokePaintServerObject = object;
- return true;
- }
-
- return false;
- }
-
- void chunkPortionCallback(SVGInlineTextBox* textBox, int startOffset, const TransformationMatrix& chunkCtm,
- const Vector<SVGChar>::iterator& start, const Vector<SVGChar>::iterator& end)
- {
- RenderText* text = textBox->textObject();
- ASSERT(text);
-
- RenderStyle* styleToUse = text->style(textBox->isFirstLineStyle());
- ASSERT(styleToUse);
-
- startOffset += textBox->start();
-
- int textDecorations = styleToUse->textDecorationsInEffect();
-
- int textWidth = 0;
- IntPoint decorationOrigin;
- SVGTextDecorationInfo info;
-
- if (!chunkCtm.isIdentity())
- m_paintInfo.context->concatCTM(chunkCtm);
-
- for (Vector<SVGChar>::iterator it = start; it != end; ++it) {
- if (it->isHidden())
- continue;
-
- // Determine how many characters - starting from the current - can be drawn at once.
- Vector<SVGChar>::iterator itSearch = it + 1;
- while (itSearch != end) {
- if (itSearch->drawnSeperated || itSearch->isHidden())
- break;
-
- itSearch++;
- }
-
- const UChar* stringStart = text->characters() + startOffset + (it - start);
- unsigned int stringLength = itSearch - it;
-
- // Paint decorations, that have to be drawn before the text gets drawn
- if (textDecorations != TDNONE && m_paintInfo.phase != PaintPhaseSelection) {
- textWidth = styleToUse->font().width(svgTextRunForInlineTextBox(stringStart, stringLength, styleToUse, textBox, (*it).x));
- decorationOrigin = IntPoint((int) (*it).x, (int) (*it).y - styleToUse->font().ascent());
- info = m_rootBox->retrievePaintServersForTextDecoration(text);
- }
-
- if (textDecorations & UNDERLINE && textWidth != 0.0f)
- textBox->paintDecoration(UNDERLINE, m_paintInfo.context, decorationOrigin.x(), decorationOrigin.y(), textWidth, *it, info);
-
- if (textDecorations & OVERLINE && textWidth != 0.0f)
- textBox->paintDecoration(OVERLINE, m_paintInfo.context, decorationOrigin.x(), decorationOrigin.y(), textWidth, *it, info);
-
- // Paint text
- SVGPaintServer* activePaintServer = m_fillPaintServer;
- if (!activePaintServer)
- activePaintServer = m_strokePaintServer;
-
- ASSERT(activePaintServer);
- textBox->paintCharacters(m_paintInfo, m_tx, m_ty, *it, stringStart, stringLength, activePaintServer);
-
- // Paint decorations, that have to be drawn afterwards
- if (textDecorations & LINE_THROUGH && textWidth != 0.0f)
- textBox->paintDecoration(LINE_THROUGH, m_paintInfo.context, decorationOrigin.x(), decorationOrigin.y(), textWidth, *it, info);
-
- // Skip processed characters
- it = itSearch - 1;
- }
-
- if (!chunkCtm.isIdentity())
- m_paintInfo.context->concatCTM(chunkCtm.inverse());
- }
-
-private:
- SVGRootInlineBox* m_rootBox;
- bool m_chunkStarted : 1;
-
- RenderObject::PaintInfo m_paintInfo;
- RenderObject::PaintInfo m_savedInfo;
-
- FloatRect m_boundingBox;
- SVGResourceFilter* m_filter;
- SVGResourceFilter* m_rootFilter;
-
- SVGPaintServer* m_fillPaintServer;
- SVGPaintServer* m_strokePaintServer;
-
- RenderObject* m_fillPaintServerObject;
- RenderObject* m_strokePaintServerObject;
-
- int m_tx;
- int m_ty;
-};
-
-void SVGRootInlineBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty)
-{
- if (paintInfo.context->paintingDisabled() || paintInfo.phase != PaintPhaseForeground)
- return;
-
- RenderObject::PaintInfo savedInfo(paintInfo);
- paintInfo.context->save();
-
- SVGResourceFilter* filter = 0;
- FloatRect boundingBox(tx + xPos(), ty + yPos(), width(), height());
-
- // Initialize text rendering
- paintInfo.context->concatCTM(object()->localTransform());
- prepareToRenderSVGContent(object(), paintInfo, boundingBox, filter);
- paintInfo.context->concatCTM(object()->localTransform().inverse());
-
- // Render text, chunk-by-chunk
- SVGRootInlineBoxPaintWalker walkerCallback(this, filter, paintInfo, tx, ty);
- SVGTextChunkWalker<SVGRootInlineBoxPaintWalker> walker(&walkerCallback,
- &SVGRootInlineBoxPaintWalker::chunkPortionCallback,
- &SVGRootInlineBoxPaintWalker::chunkStartCallback,
- &SVGRootInlineBoxPaintWalker::chunkEndCallback,
- &SVGRootInlineBoxPaintWalker::chunkSetupFillCallback,
- &SVGRootInlineBoxPaintWalker::chunkSetupStrokeCallback);
-
- walkTextChunks(&walker);
-
- // Finalize text rendering
- finishRenderSVGContent(object(), paintInfo, boundingBox, filter, savedInfo.context);
- paintInfo.context->restore();
-}
-
-int SVGRootInlineBox::placeBoxesHorizontally(int, int& leftPosition, int& rightPosition, bool&)
-{
- // Remove any offsets caused by RTL text layout
- leftPosition = 0;
- rightPosition = 0;
- return 0;
-}
-
-int SVGRootInlineBox::verticallyAlignBoxes(int)
-{
- // height is set by layoutInlineBoxes.
- return height();
-}
-
-float cummulatedWidthOfInlineBoxCharacterRange(SVGInlineBoxCharacterRange& range)
-{
- ASSERT(!range.isOpen());
- ASSERT(range.isClosed());
- ASSERT(range.box->isInlineTextBox());
-
- InlineTextBox* textBox = static_cast<InlineTextBox*>(range.box);
- RenderText* text = textBox->textObject();
- RenderStyle* style = text->style();
-
- return style->font().floatWidth(svgTextRunForInlineTextBox(text->characters() + textBox->start() + range.startOffset, range.endOffset - range.startOffset, style, textBox, 0));
-}
-
-float cummulatedHeightOfInlineBoxCharacterRange(SVGInlineBoxCharacterRange& range)
-{
- ASSERT(!range.isOpen());
- ASSERT(range.isClosed());
- ASSERT(range.box->isInlineTextBox());
-
- InlineTextBox* textBox = static_cast<InlineTextBox*>(range.box);
- RenderText* text = textBox->textObject();
- const Font& font = text->style()->font();
-
- return (range.endOffset - range.startOffset) * (font.ascent() + font.descent());
-}
-
-TextRun svgTextRunForInlineTextBox(const UChar* c, int len, RenderStyle* style, const InlineTextBox* textBox, float xPos)
-{
- ASSERT(textBox);
- ASSERT(style);
-
- TextRun run(c, len, false, static_cast<int>(xPos), textBox->toAdd(), textBox->direction() == RTL, textBox->m_dirOverride || style->visuallyOrdered());
-
-#if ENABLE(SVG_FONTS)
- run.setReferencingRenderObject(textBox->textObject()->parent());
-#endif
-
- // We handle letter & word spacing ourselves
- run.disableSpacing();
- return run;
-}
-
-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->object()->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 offset = box->direction() == RTL ? box->end() - i - positionOffset + 1 : box->start() + i + positionOffset - 1;
-
- // FIXME: does this need to change to handle multichar glyphs?
- int charsConsumed = 1;
- String glyphName;
- if (calcWidthOnly) {
- float lastGlyphWidth = box->calculateGlyphWidth(style, offset, 0, charsConsumed, glyphName);
- length += currentCharacter.x - lastCharacter.x - lastGlyphWidth;
- } else {
- float lastGlyphHeight = box->calculateGlyphHeight(style, offset, 0);
- length += currentCharacter.y - lastCharacter.y - lastGlyphHeight;
- }
- }
-
- // Advance processed characters
- i += positionOffset - 1;
- charIt = itSearch;
- }
- }
-
- ASSERT(charIt == chunk.end);
- return length;
-}
-
-static float cummulatedWidthOfTextChunk(SVGTextChunk& chunk)
-{
- return cummulatedWidthOrHeightOfTextChunk(chunk, true);
-}
-
-static float cummulatedHeightOfTextChunk(SVGTextChunk& chunk)
-{
- return cummulatedWidthOrHeightOfTextChunk(chunk, false);
-}
-
-static float calculateTextAnchorShiftForTextChunk(SVGTextChunk& chunk, ETextAnchor anchor)
-{
- float shift = 0.0f;
-
- if (chunk.isVerticalText)
- shift = cummulatedHeightOfTextChunk(chunk);
- else
- shift = cummulatedWidthOfTextChunk(chunk);
-
- if (anchor == TA_MIDDLE)
- shift *= -0.5f;
- else
- shift *= -1.0f;
-
- return shift;
-}
-
-static void applyTextAnchorToTextChunk(SVGTextChunk& chunk)
-{
- // This method is not called for chunks containing chars aligned on a path.
- // -> all characters are visible, no need to check for "isHidden()" anywhere.
-
- if (chunk.anchor == TA_START)
- return;
-
- float shift = calculateTextAnchorShiftForTextChunk(chunk, chunk.anchor);
-
- // Apply correction to chunk
- Vector<SVGChar>::iterator chunkIt = chunk.start;
- for (; chunkIt != chunk.end; ++chunkIt) {
- SVGChar& curChar = *chunkIt;
-
- if (chunk.isVerticalText)
- curChar.y += shift;
- else
- curChar.x += shift;
- }
-
- // Move inline boxes
- Vector<SVGInlineBoxCharacterRange>::iterator boxIt = chunk.boxes.begin();
- Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = chunk.boxes.end();
-
- for (; boxIt != boxEnd; ++boxIt) {
- SVGInlineBoxCharacterRange& range = *boxIt;
-
- InlineBox* curBox = range.box;
- ASSERT(curBox->isInlineTextBox());
- ASSERT(curBox->parent() && (curBox->parent()->isRootInlineBox() || curBox->parent()->isInlineFlowBox()));
-
- // Move target box
- if (chunk.isVerticalText)
- curBox->setYPos(curBox->yPos() + static_cast<int>(shift));
- else
- curBox->setXPos(curBox->xPos() + static_cast<int>(shift));
- }
-}
-
-static float calculateTextLengthCorrectionForTextChunk(SVGTextChunk& chunk, ELengthAdjust lengthAdjust, float& computedLength)
-{
- if (chunk.textLength <= 0.0f)
- return 0.0f;
-
- float computedWidth = cummulatedWidthOfTextChunk(chunk);
- float computedHeight = cummulatedHeightOfTextChunk(chunk);
-
- if ((computedWidth <= 0.0f && !chunk.isVerticalText) ||
- (computedHeight <= 0.0f && chunk.isVerticalText))
- return 0.0f;
-
- if (chunk.isVerticalText)
- computedLength = computedHeight;
- else
- computedLength = computedWidth;
-
- if (lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) {
- if (chunk.isVerticalText)
- chunk.ctm.scale(1.0f, chunk.textLength / computedLength);
- else
- chunk.ctm.scale(chunk.textLength / computedLength, 1.0f);
-
- return 0.0f;
- }
-
- return (chunk.textLength - computedLength) / float(chunk.end - chunk.start);
-}
-
-static void applyTextLengthCorrectionToTextChunk(SVGTextChunk& chunk)
-{
- // This method is not called for chunks containing chars aligned on a path.
- // -> all characters are visible, no need to check for "isHidden()" anywhere.
-
- // lengthAdjust="spacingAndGlyphs" is handled by modifying chunk.ctm
- float computedLength = 0.0f;
- float spacingToApply = calculateTextLengthCorrectionForTextChunk(chunk, chunk.lengthAdjust, computedLength);
-
- if (!chunk.ctm.isIdentity() && chunk.lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) {
- SVGChar& firstChar = *(chunk.start);
-
- // Assure we apply the chunk scaling in the right origin
- TransformationMatrix newChunkCtm;
- newChunkCtm.translate(firstChar.x, firstChar.y);
- newChunkCtm = chunk.ctm * newChunkCtm;
- newChunkCtm.translate(-firstChar.x, -firstChar.y);
-
- chunk.ctm = newChunkCtm;
- }
-
- // Apply correction to chunk
- if (spacingToApply != 0.0f) {
- Vector<SVGChar>::iterator chunkIt = chunk.start;
- for (; chunkIt != chunk.end; ++chunkIt) {
- SVGChar& curChar = *chunkIt;
- curChar.drawnSeperated = true;
-
- if (chunk.isVerticalText)
- curChar.y += (chunkIt - chunk.start) * spacingToApply;
- else
- curChar.x += (chunkIt - chunk.start) * spacingToApply;
- }
- }
-}
-
-void SVGRootInlineBox::computePerCharacterLayoutInformation()
-{
- // Clean up any previous layout information
- m_svgChars.clear();
- m_svgTextChunks.clear();
-
- // Build layout information for all contained render objects
- SVGCharacterLayoutInfo info(m_svgChars);
- buildLayoutInformation(this, info);
-
- // 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.
- buildTextChunks(m_svgChars, m_svgTextChunks, this);
-
- // Layout all text chunks
- // text-anchor needs to be applied to individual chunks.
- layoutTextChunks();
-
- // Finally the top left position of our box is known.
- // Propogate this knownledge to our RenderSVGText parent.
- FloatPoint topLeft = topLeftPositionOfCharacterRange(m_svgChars);
- block()->setLocation((int) floorf(topLeft.x()), (int) floorf(topLeft.y()));
-
- // Layout all InlineText/Flow boxes
- // BEWARE: This requires the root top/left position to be set correctly before!
- layoutInlineBoxes();
-}
-
-void SVGRootInlineBox::buildLayoutInformation(InlineFlowBox* start, SVGCharacterLayoutInfo& info)
-{
- if (start->isRootInlineBox()) {
- ASSERT(start->object()->element()->hasTagName(SVGNames::textTag));
-
- SVGTextPositioningElement* positioningElement = static_cast<SVGTextPositioningElement*>(start->object()->element());
- ASSERT(positioningElement);
- ASSERT(positioningElement->parentNode());
-
- info.addLayoutInformation(positioningElement);
- }
-
- LastGlyphInfo lastGlyph;
-
- for (InlineBox* curr = start->firstChild(); curr; curr = curr->nextOnLine()) {
- if (curr->object()->isText())
- buildLayoutInformationForTextBox(info, static_cast<InlineTextBox*>(curr), lastGlyph);
- else {
- ASSERT(curr->isInlineFlowBox());
- InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(curr);
-
- if (!flowBox->object()->element())
- continue; // Skip generated content.
-
- bool isAnchor = flowBox->object()->element()->hasTagName(SVGNames::aTag);
- bool isTextPath = flowBox->object()->element()->hasTagName(SVGNames::textPathTag);
-
- if (!isTextPath && !isAnchor) {
- SVGTextPositioningElement* positioningElement = static_cast<SVGTextPositioningElement*>(flowBox->object()->element());
- 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->object()->element();
- if (node && node->isSVGElement())
- textContent = static_cast<SVGTextContentElement*>(node);
- ASSERT(textContent);
-
- ELengthAdjust lengthAdjust = (ELengthAdjust) textContent->lengthAdjust();
- ETextAnchor anchor = flowBox->object()->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<SVGChar> tempChars;
- Vector<SVGTextChunk> tempChunks;
-
- SVGCharacterLayoutInfo tempInfo(tempChars);
- buildLayoutInformation(flowBox, tempInfo);
-
- buildTextChunks(tempChars, tempChunks, flowBox);
-
- Vector<SVGTextChunk>::iterator it = tempChunks.begin();
- Vector<SVGTextChunk>::iterator end = tempChunks.end();
-
- TransformationMatrix ctm;
- 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::layoutInlineBoxes()
-{
- int lowX = INT_MAX;
- int lowY = INT_MAX;
- int highX = INT_MIN;
- int highY = INT_MIN;
-
- // Layout all child boxes
- Vector<SVGChar>::iterator it = m_svgChars.begin();
- layoutInlineBoxes(this, it, lowX, highX, lowY, highY);
- ASSERT(it == m_svgChars.end());
-}
-
-void SVGRootInlineBox::layoutInlineBoxes(InlineFlowBox* start, Vector<SVGChar>::iterator& it, int& lowX, int& highX, int& lowY, int& highY)
-{
- for (InlineBox* curr = start->firstChild(); curr; curr = curr->nextOnLine()) {
- RenderStyle* style = curr->object()->style();
- const Font& font = style->font();
-
- if (curr->object()->isText()) {
- SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(curr);
- unsigned length = textBox->len();
-
- SVGChar curChar = *it;
- ASSERT(it != m_svgChars.end());
-
- FloatRect stringRect;
- for (unsigned i = 0; i < length; ++i) {
- ASSERT(it != m_svgChars.end());
-
- if (it->isHidden()) {
- ++it;
- continue;
- }
-
- stringRect.unite(textBox->calculateGlyphBoundaries(style, textBox->start() + i, *it));
- ++it;
- }
-
- IntRect enclosedStringRect = enclosingIntRect(stringRect);
-
- int minX = enclosedStringRect.x();
- int maxX = minX + enclosedStringRect.width();
-
- int minY = enclosedStringRect.y();
- int maxY = minY + enclosedStringRect.height();
-
- curr->setXPos(minX - block()->x());
- curr->setWidth(enclosedStringRect.width());
-
- curr->setYPos(minY - block()->y());
- curr->setBaseline(font.ascent());
- curr->setHeight(enclosedStringRect.height());
-
- if (minX < lowX)
- lowX = minX;
-
- if (maxX > highX)
- highX = maxX;
-
- if (minY < lowY)
- lowY = minY;
-
- if (maxY > highY)
- highY = maxY;
- } else {
- ASSERT(curr->isInlineFlowBox());
-
- int minX = INT_MAX;
- int minY = INT_MAX;
- int maxX = INT_MIN;
- int maxY = INT_MIN;
-
- InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(curr);
-
- if (!flowBox->object()->element())
- continue; // Skip generated content.
-
- layoutInlineBoxes(flowBox, it, minX, maxX, minY, maxY);
-
- curr->setXPos(minX - block()->x());
- curr->setWidth(maxX - minX);
-
- curr->setYPos(minY - block()->y());
- curr->setBaseline(font.ascent());
- curr->setHeight(maxY - minY);
-
- if (minX < lowX)
- lowX = minX;
-
- if (maxX > highX)
- highX = maxX;
-
- if (minY < lowY)
- lowY = minY;
-
- if (maxY > highY)
- highY = maxY;
- }
- }
-
- if (start->isRootInlineBox()) {
- int top = lowY - block()->y();
- int bottom = highY - block()->y();
-
- start->setXPos(lowX - block()->x());
- start->setYPos(top);
-
- start->setWidth(highX - lowX);
- start->setHeight(highY - lowY);
-
- start->setVerticalOverflowPositions(top, bottom);
- start->setVerticalSelectionPositions(top, bottom);
- }
-}
-
-void SVGRootInlineBox::buildLayoutInformationForTextBox(SVGCharacterLayoutInfo& info, InlineTextBox* textBox, LastGlyphInfo& lastGlyph)
-{
- RenderText* text = textBox->textObject();
- ASSERT(text);
-
- RenderStyle* style = text->style(textBox->isFirstLineStyle());
- ASSERT(style);
-
- const Font& font = style->font();
- SVGInlineTextBox* svgTextBox = static_cast<SVGInlineTextBox*>(textBox);
-
- unsigned length = textBox->len();
-
- const SVGRenderStyle* svgStyle = style->svgStyle();
- bool isVerticalText = isVerticalWritingMode(svgStyle);
-
- int charsConsumed = 0;
- for (unsigned i = 0; i < length; i += charsConsumed) {
- SVGChar svgChar;
-
- if (info.inPathLayout())
- svgChar.pathData = SVGCharOnPath::create();
-
- float glyphWidth = 0.0f;
- float glyphHeight = 0.0f;
-
- int extraCharsAvailable = length - i - 1;
-
- String unicodeStr;
- String glyphName;
- if (textBox->direction() == RTL) {
- glyphWidth = svgTextBox->calculateGlyphWidth(style, textBox->end() - i, extraCharsAvailable, charsConsumed, glyphName);
- glyphHeight = svgTextBox->calculateGlyphHeight(style, textBox->end() - i, extraCharsAvailable);
- unicodeStr = String(textBox->textObject()->text()->characters() + textBox->end() - i, charsConsumed);
- } else {
- glyphWidth = svgTextBox->calculateGlyphWidth(style, textBox->start() + i, extraCharsAvailable, charsConsumed, glyphName);
- glyphHeight = svgTextBox->calculateGlyphHeight(style, textBox->start() + i, extraCharsAvailable);
- unicodeStr = String(textBox->textObject()->text()->characters() + textBox->start() + i, charsConsumed);
- }
-
- bool assignedX = false;
- bool assignedY = false;
-
- if (info.xValueAvailable() && (!info.inPathLayout() || (info.inPathLayout() && !isVerticalText))) {
- if (!isVerticalText)
- svgChar.newTextChunk = true;
-
- assignedX = true;
- svgChar.drawnSeperated = true;
- info.curx = info.xValueNext();
- }
-
- if (info.yValueAvailable() && (!info.inPathLayout() || (info.inPathLayout() && isVerticalText))) {
- if (isVerticalText)
- svgChar.newTextChunk = true;
-
- assignedY = true;
- svgChar.drawnSeperated = true;
- info.cury = info.yValueNext();
- }
-
- float dx = 0.0f;
- float dy = 0.0f;
-
- // Apply x-axis shift
- if (info.dxValueAvailable()) {
- svgChar.drawnSeperated = true;
-
- dx = info.dxValueNext();
- info.dx += dx;
-
- if (!info.inPathLayout())
- info.curx += dx;
- }
-
- // Apply y-axis shift
- if (info.dyValueAvailable()) {
- svgChar.drawnSeperated = true;
-
- dy = info.dyValueNext();
- info.dy += dy;
-
- if (!info.inPathLayout())
- info.cury += dy;
- }
-
- // Take letter & word spacing and kerning into account
- float spacing = font.letterSpacing() + calculateKerning(textBox->object()->element()->renderer());
-
- const UChar* currentCharacter = text->characters() + (textBox->direction() == RTL ? textBox->end() - i : textBox->start() + i);
- const UChar* lastCharacter = 0;
-
- if (textBox->direction() == RTL) {
- if (i < textBox->end())
- lastCharacter = text->characters() + textBox->end() - i + 1;
- } else {
- if (i > 0)
- lastCharacter = text->characters() + textBox->start() + i - 1;
- }
-
- if (info.nextDrawnSeperated || spacing != 0.0f) {
- info.nextDrawnSeperated = false;
- svgChar.drawnSeperated = true;
- }
-
- if (currentCharacter && Font::treatAsSpace(*currentCharacter) && lastCharacter && !Font::treatAsSpace(*lastCharacter)) {
- spacing += font.wordSpacing();
-
- if (spacing != 0.0f && !info.inPathLayout())
- info.nextDrawnSeperated = true;
- }
-
- float orientationAngle = glyphOrientationToAngle(svgStyle, isVerticalText, *currentCharacter);
-
- float xOrientationShift = 0.0f;
- float yOrientationShift = 0.0f;
- float glyphAdvance = calculateGlyphAdvanceAndShiftRespectingOrientation(isVerticalText, orientationAngle, glyphWidth, glyphHeight,
- font, svgChar, xOrientationShift, yOrientationShift);
-
- // Handle textPath layout mode
- if (info.inPathLayout()) {
- float extraAdvance = isVerticalText ? dy : dx;
- float newOffset = FLT_MIN;
-
- if (assignedX && !isVerticalText)
- newOffset = info.curx;
- else if (assignedY && isVerticalText)
- newOffset = info.cury;
-
- float correctedGlyphAdvance = glyphAdvance;
-
- // Handle lengthAdjust="spacingAndGlyphs" by specifying per-character scale operations
- if (info.pathTextLength > 0.0f && info.pathChunkLength > 0.0f) {
- if (isVerticalText) {
- svgChar.pathData->yScale = info.pathChunkLength / info.pathTextLength;
- spacing *= svgChar.pathData->yScale;
- correctedGlyphAdvance *= svgChar.pathData->yScale;
- } else {
- svgChar.pathData->xScale = info.pathChunkLength / info.pathTextLength;
- spacing *= svgChar.pathData->xScale;
- correctedGlyphAdvance *= svgChar.pathData->xScale;
- }
- }
-
- // Handle letter & word spacing on text path
- float pathExtraAdvance = info.pathExtraAdvance;
- info.pathExtraAdvance += spacing;
-
- svgChar.pathData->hidden = !info.nextPathLayoutPointAndAngle(correctedGlyphAdvance, extraAdvance, newOffset);
- svgChar.drawnSeperated = true;
-
- info.pathExtraAdvance = pathExtraAdvance;
- }
-
- // Apply rotation
- if (info.angleValueAvailable())
- info.angle = info.angleValueNext();
-
- // Apply baseline-shift
- if (info.baselineShiftValueAvailable()) {
- svgChar.drawnSeperated = true;
- float shift = info.baselineShiftValueNext();
-
- if (isVerticalText)
- info.shiftx += shift;
- else
- info.shifty -= shift;
- }
-
- // Take dominant-baseline / alignment-baseline into account
- yOrientationShift += alignmentBaselineToShift(isVerticalText, text, font);
-
- svgChar.x = info.curx;
- svgChar.y = info.cury;
- svgChar.angle = info.angle;
-
- // For text paths any shift (dx/dy/baseline-shift) has to be applied after the rotation
- if (!info.inPathLayout()) {
- svgChar.x += info.shiftx + xOrientationShift;
- svgChar.y += info.shifty + yOrientationShift;
-
- if (orientationAngle != 0.0f)
- svgChar.angle += orientationAngle;
-
- if (svgChar.angle != 0.0f)
- svgChar.drawnSeperated = true;
- } else {
- svgChar.pathData->orientationAngle = orientationAngle;
-
- if (isVerticalText)
- svgChar.angle -= 90.0f;
-
- svgChar.pathData->xShift = info.shiftx + xOrientationShift;
- svgChar.pathData->yShift = info.shifty + yOrientationShift;
-
- // Translate to glyph midpoint
- if (isVerticalText) {
- svgChar.pathData->xShift += info.dx;
- svgChar.pathData->yShift -= glyphAdvance / 2.0f;
- } else {
- svgChar.pathData->xShift -= glyphAdvance / 2.0f;
- svgChar.pathData->yShift += info.dy;
- }
- }
-
- double kerning = 0.0;
-#if ENABLE(SVG_FONTS)
- SVGFontElement* svgFont = 0;
- if (style->font().isSVGFont())
- svgFont = style->font().svgFont();
-
- if (lastGlyph.isValid && style->font().isSVGFont()) {
- SVGHorizontalKerningPair kerningPair;
- if (svgFont->getHorizontalKerningPairForStringsAndGlyphs(lastGlyph.unicode, lastGlyph.glyphName, unicodeStr, glyphName, kerningPair))
- kerning = kerningPair.kerning;
- }
-
- if (style->font().isSVGFont()) {
- lastGlyph.unicode = unicodeStr;
- lastGlyph.glyphName = glyphName;
- lastGlyph.isValid = true;
- } else
- lastGlyph.isValid = false;
-#endif
-
- svgChar.x -= (float)kerning;
-
- // Advance to new position
- if (isVerticalText) {
- svgChar.drawnSeperated = true;
- info.cury += glyphAdvance + spacing;
- } else
- info.curx += glyphAdvance + spacing - (float)kerning;
-
- // Advance to next character group
- for (int k = 0; k < charsConsumed; ++k) {
- info.svgChars.append(svgChar);
- info.processedSingleCharacter();
- svgChar.drawnSeperated = false;
- svgChar.newTextChunk = false;
- }
- }
-}
-
-void SVGRootInlineBox::buildTextChunks(Vector<SVGChar>& svgChars, Vector<SVGTextChunk>& svgTextChunks, InlineFlowBox* start)
-{
- SVGTextChunkLayoutInfo info(svgTextChunks);
- info.it = svgChars.begin();
- info.chunk.start = svgChars.begin();
- info.chunk.end = svgChars.begin();
-
- buildTextChunks(svgChars, start, info);
- ASSERT(info.it == svgChars.end());
-}
-
-void SVGRootInlineBox::buildTextChunks(Vector<SVGChar>& svgChars, InlineFlowBox* start, SVGTextChunkLayoutInfo& info)
-{
-#if DEBUG_CHUNK_BUILDING > 1
- fprintf(stderr, " -> buildTextChunks(start=%p)\n", start);
-#endif
-
- for (InlineBox* curr = start->firstChild(); curr; curr = curr->nextOnLine()) {
- if (curr->object()->isText()) {
- InlineTextBox* textBox = static_cast<InlineTextBox*>(curr);
-
- unsigned length = textBox->len();
- if (!length)
- continue;
-
-#if DEBUG_CHUNK_BUILDING > 1
- fprintf(stderr, " -> Handle inline text box (%p) with %i characters (start: %i, end: %i), handlingTextPath=%i\n",
- textBox, length, textBox->start(), textBox->end(), (int) info.handlingTextPath);
-#endif
-
- RenderText* text = textBox->textObject();
- ASSERT(text);
- ASSERT(text->element());
-
- SVGTextContentElement* textContent = 0;
- Node* node = text->element()->parent();
- while (node && node->isSVGElement() && !textContent) {
- if (static_cast<SVGElement*>(node)->isTextContent())
- textContent = static_cast<SVGTextContentElement*>(node);
- else
- node = node->parentNode();
- }
- ASSERT(textContent);
-
- // Start new character range for the first chunk
- bool isFirstCharacter = info.svgTextChunks.isEmpty() && info.chunk.start == info.it && info.chunk.start == info.chunk.end;
- if (isFirstCharacter) {
- ASSERT(info.chunk.boxes.isEmpty());
- info.chunk.boxes.append(SVGInlineBoxCharacterRange());
- } else
- ASSERT(!info.chunk.boxes.isEmpty());
-
- // Walk string to find out new chunk positions, if existant
- for (unsigned i = 0; i < length; ++i) {
- ASSERT(info.it != svgChars.end());
-
- SVGInlineBoxCharacterRange& range = info.chunk.boxes.last();
- if (range.isOpen()) {
- range.box = curr;
- range.startOffset = (i == 0 ? 0 : i - 1);
-
-#if DEBUG_CHUNK_BUILDING > 1
- fprintf(stderr, " | -> Range is open! box=%p, startOffset=%i\n", range.box, range.startOffset);
-#endif
- }
-
- // If a new (or the first) chunk has been started, record it's text-anchor and writing mode.
- if (info.assignChunkProperties) {
- info.assignChunkProperties = false;
-
- info.chunk.isVerticalText = isVerticalWritingMode(text->style()->svgStyle());
- info.chunk.isTextPath = info.handlingTextPath;
- info.chunk.anchor = text->style()->svgStyle()->textAnchor();
- info.chunk.textLength = textContent->textLength().value(textContent);
- info.chunk.lengthAdjust = (ELengthAdjust) textContent->lengthAdjust();
-
-#if DEBUG_CHUNK_BUILDING > 1
- fprintf(stderr, " | -> Assign chunk properties, isVerticalText=%i, anchor=%i\n", info.chunk.isVerticalText, info.chunk.anchor);
-#endif
- }
-
- if (i > 0 && !isFirstCharacter && (*info.it).newTextChunk) {
- // Close mid chunk & character range
- ASSERT(!range.isOpen());
- ASSERT(!range.isClosed());
-
- range.endOffset = i;
- closeTextChunk(info);
-
-#if DEBUG_CHUNK_BUILDING > 1
- fprintf(stderr, " | -> Close mid-text chunk, at endOffset: %i and starting new mid chunk!\n", range.endOffset);
-#endif
-
- // Prepare for next chunk, if we're not at the end
- startTextChunk(info);
- if (i + 1 == length) {
-#if DEBUG_CHUNK_BUILDING > 1
- fprintf(stderr, " | -> Record last chunk of inline text box!\n");
-#endif
-
- startTextChunk(info);
- SVGInlineBoxCharacterRange& range = info.chunk.boxes.last();
-
- info.assignChunkProperties = false;
- info.chunk.isVerticalText = isVerticalWritingMode(text->style()->svgStyle());
- info.chunk.isTextPath = info.handlingTextPath;
- info.chunk.anchor = text->style()->svgStyle()->textAnchor();
- info.chunk.textLength = textContent->textLength().value(textContent);
- info.chunk.lengthAdjust = (ELengthAdjust) textContent->lengthAdjust();
-
- range.box = curr;
- range.startOffset = i;
-
- ASSERT(!range.isOpen());
- ASSERT(!range.isClosed());
- }
- }
-
- // This should only hold true for the first character of the first chunk
- if (isFirstCharacter)
- isFirstCharacter = false;
-
- ++info.it;
- }
-
-#if DEBUG_CHUNK_BUILDING > 1
- fprintf(stderr, " -> Finished inline text box!\n");
-#endif
-
- SVGInlineBoxCharacterRange& range = info.chunk.boxes.last();
- if (!range.isOpen() && !range.isClosed()) {
-#if DEBUG_CHUNK_BUILDING > 1
- fprintf(stderr, " -> Last range not closed - closing with endOffset: %i\n", length);
-#endif
-
- // Current text chunk is not yet closed. Finish the current range, but don't start a new chunk.
- range.endOffset = length;
-
- if (info.it != svgChars.end()) {
-#if DEBUG_CHUNK_BUILDING > 1
- fprintf(stderr, " -> Not at last character yet!\n");
-#endif
-
- // If we're not at the end of the last box to be processed, and if the next
- // character starts a new chunk, then close the current chunk and start a new one.
- if ((*info.it).newTextChunk) {
-#if DEBUG_CHUNK_BUILDING > 1
- fprintf(stderr, " -> Next character starts new chunk! Closing current chunk, and starting a new one...\n");
-#endif
-
- closeTextChunk(info);
- startTextChunk(info);
- } else {
- // Just start a new character range
- info.chunk.boxes.append(SVGInlineBoxCharacterRange());
-
-#if DEBUG_CHUNK_BUILDING > 1
- fprintf(stderr, " -> Next character does NOT start a new chunk! Starting new character range...\n");
-#endif
- }
- } else {
-#if DEBUG_CHUNK_BUILDING > 1
- fprintf(stderr, " -> Closing final chunk! Finished processing!\n");
-#endif
-
- // Close final chunk, once we're at the end of the last box
- closeTextChunk(info);
- }
- }
- } else {
- ASSERT(curr->isInlineFlowBox());
- InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(curr);
-
- if (!flowBox->object()->element())
- continue; // Skip generated content.
-
- bool isTextPath = flowBox->object()->element()->hasTagName(SVGNames::textPathTag);
-
-#if DEBUG_CHUNK_BUILDING > 1
- fprintf(stderr, " -> Handle inline flow box (%p), isTextPath=%i\n", flowBox, (int) isTextPath);
-#endif
-
- if (isTextPath)
- info.handlingTextPath = true;
-
- buildTextChunks(svgChars, flowBox, info);
-
- if (isTextPath)
- info.handlingTextPath = false;
- }
- }
-
-#if DEBUG_CHUNK_BUILDING > 1
- fprintf(stderr, " <- buildTextChunks(start=%p)\n", start);
-#endif
-}
-
-const Vector<SVGTextChunk>& SVGRootInlineBox::svgTextChunks() const
-{
- return m_svgTextChunks;
-}
-
-void SVGRootInlineBox::layoutTextChunks()
-{
- Vector<SVGTextChunk>::iterator it = m_svgTextChunks.begin();
- Vector<SVGTextChunk>::iterator end = m_svgTextChunks.end();
-
- for (; it != end; ++it) {
- SVGTextChunk& chunk = *it;
-
-#if DEBUG_CHUNK_BUILDING > 0
- {
- fprintf(stderr, "Handle TEXT CHUNK! anchor=%i, textLength=%f, lengthAdjust=%i, isVerticalText=%i, isTextPath=%i start=%p, end=%p -> dist: %i\n",
- (int) chunk.anchor, chunk.textLength, (int) chunk.lengthAdjust, (int) chunk.isVerticalText,
- (int) chunk.isTextPath, chunk.start, chunk.end, (unsigned int) (chunk.end - chunk.start));
-
- Vector<SVGInlineBoxCharacterRange>::iterator boxIt = chunk.boxes.begin();
- Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = chunk.boxes.end();
-
- unsigned int i = 0;
- for (; boxIt != boxEnd; ++boxIt) {
- SVGInlineBoxCharacterRange& range = *boxIt; i++;
- fprintf(stderr, " -> RANGE %i STARTOFFSET: %i, ENDOFFSET: %i, BOX: %p\n", i, range.startOffset, range.endOffset, range.box);
- }
- }
-#endif
-
- if (chunk.isTextPath)
- continue;
-
- // text-path & textLength, with lengthAdjust="spacing" is already handled for textPath layouts.
- applyTextLengthCorrectionToTextChunk(chunk);
-
- // text-anchor is already handled for textPath layouts.
- applyTextAnchorToTextChunk(chunk);
- }
-}
-
-static inline void addPaintServerToTextDecorationInfo(ETextDecoration decoration, SVGTextDecorationInfo& info, RenderObject* object)
-{
- if (object->style()->svgStyle()->hasFill())
- info.fillServerMap.set(decoration, object);
-
- if (object->style()->svgStyle()->hasStroke())
- info.strokeServerMap.set(decoration, object);
-}
-
-SVGTextDecorationInfo SVGRootInlineBox::retrievePaintServersForTextDecoration(RenderObject* start)
-{
- ASSERT(start);
-
- Vector<RenderObject*> parentChain;
- while ((start = start->parent())) {
- parentChain.prepend(start);
-
- // Stop at our direct <text> parent.
- if (start->isSVGText())
- break;
- }
-
- Vector<RenderObject*>::iterator it = parentChain.begin();
- Vector<RenderObject*>::iterator end = parentChain.end();
-
- SVGTextDecorationInfo info;
-
- for (; it != end; ++it) {
- RenderObject* object = *it;
- ASSERT(object);
-
- RenderStyle* style = object->style();
- ASSERT(style);
-
- int decorations = style->textDecoration();
- if (decorations != NONE) {
- if (decorations & OVERLINE)
- addPaintServerToTextDecorationInfo(OVERLINE, info, object);
-
- if (decorations & UNDERLINE)
- addPaintServerToTextDecorationInfo(UNDERLINE, info, object);
-
- if (decorations & LINE_THROUGH)
- addPaintServerToTextDecorationInfo(LINE_THROUGH, info, object);
- }
- }
-
- return info;
-}
-
-void SVGRootInlineBox::walkTextChunks(SVGTextChunkWalkerBase* walker, const SVGInlineTextBox* textBox)
-{
- ASSERT(walker);
-
- Vector<SVGTextChunk>::iterator it = m_svgTextChunks.begin();
- Vector<SVGTextChunk>::iterator itEnd = m_svgTextChunks.end();
-
- for (; it != itEnd; ++it) {
- SVGTextChunk& curChunk = *it;
-
- Vector<SVGInlineBoxCharacterRange>::iterator boxIt = curChunk.boxes.begin();
- Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = curChunk.boxes.end();
-
- InlineBox* lastNotifiedBox = 0;
- InlineBox* prevBox = 0;
-
- unsigned int chunkOffset = 0;
- bool startedFirstChunk = false;
-
- for (; boxIt != boxEnd; ++boxIt) {
- SVGInlineBoxCharacterRange& range = *boxIt;
-
- ASSERT(range.box->isInlineTextBox());
- SVGInlineTextBox* rangeTextBox = static_cast<SVGInlineTextBox*>(range.box);
-
- if (textBox && rangeTextBox != textBox) {
- chunkOffset += range.endOffset - range.startOffset;
- continue;
- }
-
- // Eventually notify that we started a new chunk
- if (!textBox && !startedFirstChunk) {
- startedFirstChunk = true;
-
- lastNotifiedBox = range.box;
- walker->start(range.box);
- } else {
- // Eventually apply new style, as this chunk spans multiple boxes (with possible different styling)
- if (prevBox && prevBox != range.box) {
- lastNotifiedBox = range.box;
-
- walker->end(prevBox);
- walker->start(lastNotifiedBox);
- }
- }
-
- unsigned int length = range.endOffset - range.startOffset;
-
- Vector<SVGChar>::iterator itCharBegin = curChunk.start + chunkOffset;
- Vector<SVGChar>::iterator itCharEnd = curChunk.start + chunkOffset + length;
- ASSERT(itCharEnd <= curChunk.end);
-
- // Process this chunk portion
- if (textBox)
- (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd);
- else {
- if (walker->setupFill(range.box))
- (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd);
-
- if (walker->setupStroke(range.box))
- (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd);
- }
-
- chunkOffset += length;
-
- if (!textBox)
- prevBox = range.box;
- }
-
- if (!textBox && startedFirstChunk)
- walker->end(lastNotifiedBox);
- }
-}
-
-} // namespace WebCore
-
-#endif // ENABLE(SVG)
+/*
+ * This file is part of the WebKit project.
+ *
+ * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz>
+ * (C) 2006 Apple Computer Inc.
+ * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ *
+ * 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"
+
+#if ENABLE(SVG)
+#include "SVGRootInlineBox.h"
+
+#include "Editor.h"
+#include "Frame.h"
+#include "GraphicsContext.h"
+#include "RenderBlock.h"
+#include "RenderSVGRoot.h"
+#include "SVGInlineFlowBox.h"
+#include "SVGInlineTextBox.h"
+#include "SVGFontElement.h"
+#include "SVGPaintServer.h"
+#include "SVGRenderStyleDefs.h"
+#include "SVGRenderSupport.h"
+#include "SVGResourceFilter.h"
+#include "SVGTextPositioningElement.h"
+#include "SVGURIReference.h"
+#include "Text.h"
+#include "UnicodeRange.h"
+
+#include <float.h>
+
+// Text chunk creation is complex and the whole process
+// can easily be traced by setting this variable > 0.
+#define DEBUG_CHUNK_BUILDING 0
+
+namespace WebCore {
+
+static inline bool isVerticalWritingMode(const SVGRenderStyle* style)
+{
+ return style->writingMode() == WM_TBRL || style->writingMode() == WM_TB;
+}
+
+static inline EAlignmentBaseline dominantBaselineToShift(bool isVerticalText, const RenderObject* text, const Font& font)
+{
+ ASSERT(text);
+
+ const SVGRenderStyle* style = text->style() ? text->style()->svgStyle() : 0;
+ ASSERT(style);
+
+ const SVGRenderStyle* parentStyle = text->parent() && text->parent()->style() ? text->parent()->style()->svgStyle() : 0;
+
+ EDominantBaseline baseline = style->dominantBaseline();
+ if (baseline == DB_AUTO) {
+ if (isVerticalText)
+ baseline = DB_CENTRAL;
+ else
+ baseline = DB_ALPHABETIC;
+ }
+
+ switch (baseline) {
+ case DB_USE_SCRIPT:
+ // TODO: The dominant-baseline and the baseline-table components are set by
+ // determining the predominant script of the character data content.
+ return AB_ALPHABETIC;
+ case DB_NO_CHANGE:
+ {
+ if (parentStyle)
+ return dominantBaselineToShift(isVerticalText, text->parent(), font);
+
+ ASSERT_NOT_REACHED();
+ return AB_AUTO;
+ }
+ case DB_RESET_SIZE:
+ {
+ if (parentStyle)
+ return dominantBaselineToShift(isVerticalText, text->parent(), font);
+
+ ASSERT_NOT_REACHED();
+ return AB_AUTO;
+ }
+ case DB_IDEOGRAPHIC:
+ return AB_IDEOGRAPHIC;
+ case DB_ALPHABETIC:
+ return AB_ALPHABETIC;
+ case DB_HANGING:
+ return AB_HANGING;
+ case DB_MATHEMATICAL:
+ return AB_MATHEMATICAL;
+ case DB_CENTRAL:
+ return AB_CENTRAL;
+ case DB_MIDDLE:
+ return AB_MIDDLE;
+ case DB_TEXT_AFTER_EDGE:
+ return AB_TEXT_AFTER_EDGE;
+ case DB_TEXT_BEFORE_EDGE:
+ return AB_TEXT_BEFORE_EDGE;
+ default:
+ ASSERT_NOT_REACHED();
+ return AB_AUTO;
+ }
+}
+
+static inline float alignmentBaselineToShift(bool isVerticalText, const RenderObject* text, const Font& font)
+{
+ ASSERT(text);
+
+ const SVGRenderStyle* style = text->style() ? text->style()->svgStyle() : 0;
+ ASSERT(style);
+
+ const SVGRenderStyle* parentStyle = text->parent() && text->parent()->style() ? text->parent()->style()->svgStyle() : 0;
+
+ EAlignmentBaseline baseline = style->alignmentBaseline();
+ if (baseline == AB_AUTO) {
+ if (parentStyle && style->dominantBaseline() == DB_AUTO)
+ baseline = dominantBaselineToShift(isVerticalText, text->parent(), font);
+ else
+ baseline = dominantBaselineToShift(isVerticalText, text, font);
+
+ ASSERT(baseline != AB_AUTO);
+ }
+
+ // Note: http://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling
+ switch (baseline) {
+ case AB_BASELINE:
+ {
+ if (parentStyle)
+ return dominantBaselineToShift(isVerticalText, text->parent(), font);
+
+ return 0.0f;
+ }
+ case AB_BEFORE_EDGE:
+ case AB_TEXT_BEFORE_EDGE:
+ return font.ascent();
+ case AB_MIDDLE:
+ return font.xHeight() / 2.0f;
+ case AB_CENTRAL:
+ // Not needed, we're taking this into account already for vertical text!
+ // return (font.ascent() - font.descent()) / 2.0f;
+ return 0.0f;
+ case AB_AFTER_EDGE:
+ case AB_TEXT_AFTER_EDGE:
+ case AB_IDEOGRAPHIC:
+ return font.descent();
+ case AB_ALPHABETIC:
+ return 0.0f;
+ case AB_HANGING:
+ return font.ascent() * 8.0f / 10.0f;
+ case AB_MATHEMATICAL:
+ return font.ascent() / 2.0f;
+ default:
+ ASSERT_NOT_REACHED();
+ return 0.0f;
+ }
+}
+
+static inline float glyphOrientationToAngle(const SVGRenderStyle* svgStyle, bool isVerticalText, const UChar& character)
+{
+ switch (isVerticalText ? svgStyle->glyphOrientationVertical() : svgStyle->glyphOrientationHorizontal()) {
+ case GO_AUTO:
+ {
+ // Spec: Fullwidth ideographic and fullwidth Latin text will be set with a glyph-orientation of 0-degrees.
+ // Text which is not fullwidth will be set with a glyph-orientation of 90-degrees.
+ unsigned int unicodeRange = findCharUnicodeRange(character);
+ if (unicodeRange == cRangeSetLatin || unicodeRange == cRangeArabic)
+ return 90.0f;
+
+ return 0.0f;
+ }
+ case GO_90DEG:
+ return 90.0f;
+ case GO_180DEG:
+ return 180.0f;
+ case GO_270DEG:
+ return 270.0f;
+ case GO_0DEG:
+ default:
+ return 0.0f;
+ }
+}
+
+static inline bool glyphOrientationIsMultiplyOf180Degrees(float orientationAngle)
+{
+ return fabsf(fmodf(orientationAngle, 180.0f)) == 0.0f;
+}
+
+static inline float calculateGlyphAdvanceAndShiftRespectingOrientation(bool isVerticalText, float orientationAngle, float glyphWidth, float glyphHeight, const Font& font, SVGChar& svgChar, float& xOrientationShift, float& yOrientationShift)
+{
+ bool orientationIsMultiplyOf180Degrees = glyphOrientationIsMultiplyOf180Degrees(orientationAngle);
+
+ // The function is based on spec requirements:
+ //
+ // Spec: If the 'glyph-orientation-horizontal' results in an orientation angle that is not a multiple of
+ // of 180 degrees, then the current text position is incremented according to the vertical metrics of the glyph.
+ //
+ // Spec: If if the 'glyph-orientation-vertical' results in an orientation angle that is not a multiple of
+ // 180 degrees,then the current text position is incremented according to the horizontal metrics of the glyph.
+
+ // vertical orientation handling
+ if (isVerticalText) {
+ if (orientationAngle == 0.0f) {
+ xOrientationShift = -glyphWidth / 2.0f;
+ yOrientationShift = font.ascent();
+ } else if (orientationAngle == 90.0f) {
+ xOrientationShift = -glyphHeight;
+ yOrientationShift = font.descent();
+ svgChar.orientationShiftY = -font.ascent();
+ } else if (orientationAngle == 270.0f) {
+ xOrientationShift = glyphHeight;
+ yOrientationShift = font.descent();
+ svgChar.orientationShiftX = -glyphWidth;
+ svgChar.orientationShiftY = -font.ascent();
+ } else if (orientationAngle == 180.0f) {
+ yOrientationShift = font.ascent();
+ svgChar.orientationShiftX = -glyphWidth / 2.0f;
+ svgChar.orientationShiftY = font.ascent() - font.descent();
+ }
+
+ // vertical advance calculation
+ if (orientationAngle != 0.0f && !orientationIsMultiplyOf180Degrees)
+ return glyphWidth;
+
+ return glyphHeight;
+ }
+
+ // horizontal orientation handling
+ if (orientationAngle == 90.0f) {
+ xOrientationShift = glyphWidth / 2.0f;
+ yOrientationShift = -font.descent();
+ svgChar.orientationShiftX = -glyphWidth / 2.0f - font.descent();
+ svgChar.orientationShiftY = font.descent();
+ } else if (orientationAngle == 270.0f) {
+ xOrientationShift = -glyphWidth / 2.0f;
+ yOrientationShift = -font.descent();
+ svgChar.orientationShiftX = -glyphWidth / 2.0f + font.descent();
+ svgChar.orientationShiftY = glyphHeight;
+ } else if (orientationAngle == 180.0f) {
+ xOrientationShift = glyphWidth / 2.0f;
+ svgChar.orientationShiftX = -glyphWidth / 2.0f;
+ svgChar.orientationShiftY = font.ascent() - font.descent();
+ }
+
+ // horizontal advance calculation
+ if (orientationAngle != 0.0f && !orientationIsMultiplyOf180Degrees)
+ return glyphHeight;
+
+ return glyphWidth;
+}
+
+static inline void startTextChunk(SVGTextChunkLayoutInfo& info)
+{
+ info.chunk.boxes.clear();
+ info.chunk.boxes.append(SVGInlineBoxCharacterRange());
+
+ info.chunk.start = info.it;
+ info.assignChunkProperties = true;
+}
+
+static inline void closeTextChunk(SVGTextChunkLayoutInfo& info)
+{
+ ASSERT(!info.chunk.boxes.last().isOpen());
+ ASSERT(info.chunk.boxes.last().isClosed());
+
+ info.chunk.end = info.it;
+ ASSERT(info.chunk.end >= info.chunk.start);
+
+ info.svgTextChunks.append(info.chunk);
+}
+
+RenderSVGRoot* findSVGRootObject(RenderObject* start)
+{
+ // Find associated root inline box
+ while (start && !start->isSVGRoot())
+ start = start->parent();
+
+ ASSERT(start);
+ ASSERT(start->isSVGRoot());
+
+ return static_cast<RenderSVGRoot*>(start);
+}
+
+static inline FloatPoint topLeftPositionOfCharacterRange(Vector<SVGChar>& chars)
+{
+ return topLeftPositionOfCharacterRange(chars.begin(), chars.end());
+}
+
+FloatPoint topLeftPositionOfCharacterRange(Vector<SVGChar>::iterator it, Vector<SVGChar>::iterator end)
+{
+ float lowX = FLT_MAX, lowY = FLT_MAX;
+ for (; it != end; ++it) {
+ if (it->isHidden())
+ continue;
+
+ float x = (*it).x;
+ float y = (*it).y;
+
+ if (x < lowX)
+ lowX = x;
+
+ if (y < lowY)
+ lowY = y;
+ }
+
+ return FloatPoint(lowX, lowY);
+}
+
+// Helper function
+static float calculateKerning(RenderObject* item)
+{
+ const Font& font = item->style()->font();
+ const SVGRenderStyle* svgStyle = item->style()->svgStyle();
+
+ float kerning = 0.0f;
+ if (CSSPrimitiveValue* primitive = static_cast<CSSPrimitiveValue*>(svgStyle->kerning())) {
+ kerning = primitive->getFloatValue();
+
+ if (primitive->primitiveType() == CSSPrimitiveValue::CSS_PERCENTAGE && font.pixelSize() > 0)
+ kerning = kerning / 100.0f * font.pixelSize();
+ }
+
+ return kerning;
+}
+
+// Helper class for paint()
+struct SVGRootInlineBoxPaintWalker {
+ SVGRootInlineBoxPaintWalker(SVGRootInlineBox* rootBox, SVGResourceFilter* rootFilter, RenderObject::PaintInfo paintInfo, int tx, int ty)
+ : m_rootBox(rootBox)
+ , m_chunkStarted(false)
+ , m_paintInfo(paintInfo)
+ , m_savedInfo(paintInfo)
+ , m_boundingBox(tx + rootBox->xPos(), ty + rootBox->yPos(), rootBox->width(), rootBox->height())
+ , m_filter(0)
+ , m_rootFilter(rootFilter)
+ , m_fillPaintServer(0)
+ , m_strokePaintServer(0)
+ , m_fillPaintServerObject(0)
+ , m_strokePaintServerObject(0)
+ , m_tx(tx)
+ , m_ty(ty)
+ {
+ }
+
+ ~SVGRootInlineBoxPaintWalker()
+ {
+ ASSERT(!m_filter);
+ ASSERT(!m_fillPaintServer);
+ ASSERT(!m_fillPaintServerObject);
+ ASSERT(!m_strokePaintServer);
+ ASSERT(!m_strokePaintServerObject);
+ ASSERT(!m_chunkStarted);
+ }
+
+ void teardownFillPaintServer()
+ {
+ if (!m_fillPaintServer)
+ return;
+
+ m_fillPaintServer->teardown(m_paintInfo.context, m_fillPaintServerObject, ApplyToFillTargetType, true);
+
+ m_fillPaintServer = 0;
+ m_fillPaintServerObject = 0;
+ }
+
+ void teardownStrokePaintServer()
+ {
+ if (!m_strokePaintServer)
+ return;
+
+ m_strokePaintServer->teardown(m_paintInfo.context, m_strokePaintServerObject, ApplyToStrokeTargetType, true);
+
+ m_strokePaintServer = 0;
+ m_strokePaintServerObject = 0;
+ }
+
+ void chunkStartCallback(InlineBox* box)
+ {
+ ASSERT(!m_chunkStarted);
+ m_chunkStarted = true;
+
+ InlineFlowBox* flowBox = box->parent();
+
+ // Initialize text rendering
+ RenderObject* object = flowBox->object();
+ ASSERT(object);
+
+ m_savedInfo = m_paintInfo;
+ m_paintInfo.context->save();
+
+ if (!flowBox->isRootInlineBox())
+ m_paintInfo.context->concatCTM(m_rootBox->object()->localTransform());
+
+ m_paintInfo.context->concatCTM(object->localTransform());
+
+ if (!flowBox->isRootInlineBox()) {
+ prepareToRenderSVGContent(object, m_paintInfo, m_boundingBox, m_filter, m_rootFilter);
+ m_paintInfo.rect = object->localTransform().inverse().mapRect(m_paintInfo.rect);
+ }
+ }
+
+ void chunkEndCallback(InlineBox* box)
+ {
+ ASSERT(m_chunkStarted);
+ m_chunkStarted = false;
+
+ InlineFlowBox* flowBox = box->parent();
+
+ RenderObject* object = flowBox->object();
+ ASSERT(object);
+
+ // Clean up last used paint server
+ teardownFillPaintServer();
+ teardownStrokePaintServer();
+
+ // Finalize text rendering
+ if (!flowBox->isRootInlineBox()) {
+ finishRenderSVGContent(object, m_paintInfo, m_boundingBox, m_filter, m_savedInfo.context);
+ m_filter = 0;
+ }
+
+ // Restore context & repaint rect
+ m_paintInfo.context->restore();
+ m_paintInfo.rect = m_savedInfo.rect;
+ }
+
+ bool chunkSetupFillCallback(InlineBox* box)
+ {
+ InlineFlowBox* flowBox = box->parent();
+
+ // Setup fill paint server
+ RenderObject* object = flowBox->object();
+ ASSERT(object);
+
+ ASSERT(!m_strokePaintServer);
+ teardownFillPaintServer();
+
+ m_fillPaintServer = SVGPaintServer::fillPaintServer(object->style(), object);
+ if (m_fillPaintServer) {
+ m_fillPaintServer->setup(m_paintInfo.context, object, ApplyToFillTargetType, true);
+ m_fillPaintServerObject = object;
+ return true;
+ }
+
+ return false;
+ }
+
+ bool chunkSetupStrokeCallback(InlineBox* box)
+ {
+ InlineFlowBox* flowBox = box->parent();
+
+ // Setup stroke paint server
+ RenderObject* object = flowBox->object();
+ ASSERT(object);
+
+ // If we're both stroked & filled, teardown fill paint server before stroking.
+ teardownFillPaintServer();
+ teardownStrokePaintServer();
+
+ m_strokePaintServer = SVGPaintServer::strokePaintServer(object->style(), object);
+
+ if (m_strokePaintServer) {
+ m_strokePaintServer->setup(m_paintInfo.context, object, ApplyToStrokeTargetType, true);
+ m_strokePaintServerObject = object;
+ return true;
+ }
+
+ return false;
+ }
+
+ void chunkPortionCallback(SVGInlineTextBox* textBox, int startOffset, const TransformationMatrix& chunkCtm,
+ const Vector<SVGChar>::iterator& start, const Vector<SVGChar>::iterator& end)
+ {
+ RenderText* text = textBox->textObject();
+ ASSERT(text);
+
+ RenderStyle* styleToUse = text->style(textBox->isFirstLineStyle());
+ ASSERT(styleToUse);
+
+ startOffset += textBox->start();
+
+ int textDecorations = styleToUse->textDecorationsInEffect();
+
+ int textWidth = 0;
+ IntPoint decorationOrigin;
+ SVGTextDecorationInfo info;
+
+ if (!chunkCtm.isIdentity())
+ m_paintInfo.context->concatCTM(chunkCtm);
+
+ for (Vector<SVGChar>::iterator it = start; it != end; ++it) {
+ if (it->isHidden())
+ continue;
+
+ // Determine how many characters - starting from the current - can be drawn at once.
+ Vector<SVGChar>::iterator itSearch = it + 1;
+ while (itSearch != end) {
+ if (itSearch->drawnSeperated || itSearch->isHidden())
+ break;
+
+ itSearch++;
+ }
+
+ const UChar* stringStart = text->characters() + startOffset + (it - start);
+ unsigned int stringLength = itSearch - it;
+
+ // Paint decorations, that have to be drawn before the text gets drawn
+ if (textDecorations != TDNONE && m_paintInfo.phase != PaintPhaseSelection) {
+ textWidth = styleToUse->font().width(svgTextRunForInlineTextBox(stringStart, stringLength, styleToUse, textBox, (*it).x));
+ decorationOrigin = IntPoint((int) (*it).x, (int) (*it).y - styleToUse->font().ascent());
+ info = m_rootBox->retrievePaintServersForTextDecoration(text);
+ }
+
+ if (textDecorations & UNDERLINE && textWidth != 0.0f)
+ textBox->paintDecoration(UNDERLINE, m_paintInfo.context, decorationOrigin.x(), decorationOrigin.y(), textWidth, *it, info);
+
+ if (textDecorations & OVERLINE && textWidth != 0.0f)
+ textBox->paintDecoration(OVERLINE, m_paintInfo.context, decorationOrigin.x(), decorationOrigin.y(), textWidth, *it, info);
+
+ // Paint text
+ SVGPaintServer* activePaintServer = m_fillPaintServer;
+ if (!activePaintServer)
+ activePaintServer = m_strokePaintServer;
+
+ ASSERT(activePaintServer);
+ textBox->paintCharacters(m_paintInfo, m_tx, m_ty, *it, stringStart, stringLength, activePaintServer);
+
+ // Paint decorations, that have to be drawn afterwards
+ if (textDecorations & LINE_THROUGH && textWidth != 0.0f)
+ textBox->paintDecoration(LINE_THROUGH, m_paintInfo.context, decorationOrigin.x(), decorationOrigin.y(), textWidth, *it, info);
+
+ // Skip processed characters
+ it = itSearch - 1;
+ }
+
+ if (!chunkCtm.isIdentity())
+ m_paintInfo.context->concatCTM(chunkCtm.inverse());
+ }
+
+private:
+ SVGRootInlineBox* m_rootBox;
+ bool m_chunkStarted : 1;
+
+ RenderObject::PaintInfo m_paintInfo;
+ RenderObject::PaintInfo m_savedInfo;
+
+ FloatRect m_boundingBox;
+ SVGResourceFilter* m_filter;
+ SVGResourceFilter* m_rootFilter;
+
+ SVGPaintServer* m_fillPaintServer;
+ SVGPaintServer* m_strokePaintServer;
+
+ RenderObject* m_fillPaintServerObject;
+ RenderObject* m_strokePaintServerObject;
+
+ int m_tx;
+ int m_ty;
+};
+
+void SVGRootInlineBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty)
+{
+ if (paintInfo.context->paintingDisabled() || paintInfo.phase != PaintPhaseForeground)
+ return;
+
+ RenderObject::PaintInfo savedInfo(paintInfo);
+ paintInfo.context->save();
+
+ SVGResourceFilter* filter = 0;
+ FloatRect boundingBox(tx + xPos(), ty + yPos(), width(), height());
+
+ // Initialize text rendering
+ paintInfo.context->concatCTM(object()->localTransform());
+ prepareToRenderSVGContent(object(), paintInfo, boundingBox, filter);
+ paintInfo.context->concatCTM(object()->localTransform().inverse());
+
+ // Render text, chunk-by-chunk
+ SVGRootInlineBoxPaintWalker walkerCallback(this, filter, paintInfo, tx, ty);
+ SVGTextChunkWalker<SVGRootInlineBoxPaintWalker> walker(&walkerCallback,
+ &SVGRootInlineBoxPaintWalker::chunkPortionCallback,
+ &SVGRootInlineBoxPaintWalker::chunkStartCallback,
+ &SVGRootInlineBoxPaintWalker::chunkEndCallback,
+ &SVGRootInlineBoxPaintWalker::chunkSetupFillCallback,
+ &SVGRootInlineBoxPaintWalker::chunkSetupStrokeCallback);
+
+ walkTextChunks(&walker);
+
+ // Finalize text rendering
+ finishRenderSVGContent(object(), paintInfo, boundingBox, filter, savedInfo.context);
+ paintInfo.context->restore();
+}
+
+int SVGRootInlineBox::placeBoxesHorizontally(int, int& leftPosition, int& rightPosition, bool&)
+{
+ // Remove any offsets caused by RTL text layout
+ leftPosition = 0;
+ rightPosition = 0;
+ return 0;
+}
+
+int SVGRootInlineBox::verticallyAlignBoxes(int)
+{
+ // height is set by layoutInlineBoxes.
+ return height();
+}
+
+float cummulatedWidthOfInlineBoxCharacterRange(SVGInlineBoxCharacterRange& range)
+{
+ ASSERT(!range.isOpen());
+ ASSERT(range.isClosed());
+ ASSERT(range.box->isInlineTextBox());
+
+ InlineTextBox* textBox = static_cast<InlineTextBox*>(range.box);
+ RenderText* text = textBox->textObject();
+ RenderStyle* style = text->style();
+
+ return style->font().floatWidth(svgTextRunForInlineTextBox(text->characters() + textBox->start() + range.startOffset, range.endOffset - range.startOffset, style, textBox, 0));
+}
+
+float cummulatedHeightOfInlineBoxCharacterRange(SVGInlineBoxCharacterRange& range)
+{
+ ASSERT(!range.isOpen());
+ ASSERT(range.isClosed());
+ ASSERT(range.box->isInlineTextBox());
+
+ InlineTextBox* textBox = static_cast<InlineTextBox*>(range.box);
+ RenderText* text = textBox->textObject();
+ const Font& font = text->style()->font();
+
+ return (range.endOffset - range.startOffset) * (font.ascent() + font.descent());
+}
+
+TextRun svgTextRunForInlineTextBox(const UChar* c, int len, RenderStyle* style, const InlineTextBox* textBox, float xPos)
+{
+ ASSERT(textBox);
+ ASSERT(style);
+
+ TextRun run(c, len, false, static_cast<int>(xPos), textBox->toAdd(), textBox->direction() == RTL, textBox->m_dirOverride || style->visuallyOrdered());
+
+#if ENABLE(SVG_FONTS)
+ run.setReferencingRenderObject(textBox->textObject()->parent());
+#endif
+
+ // We handle letter & word spacing ourselves
+ run.disableSpacing();
+ return run;
+}
+
+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->object()->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 offset = box->direction() == RTL ? box->end() - i - positionOffset + 1 : box->start() + i + positionOffset - 1;
+
+ // FIXME: does this need to change to handle multichar glyphs?
+ int charsConsumed = 1;
+ String glyphName;
+ if (calcWidthOnly) {
+ float lastGlyphWidth = box->calculateGlyphWidth(style, offset, 0, charsConsumed, glyphName);
+ length += currentCharacter.x - lastCharacter.x - lastGlyphWidth;
+ } else {
+ float lastGlyphHeight = box->calculateGlyphHeight(style, offset, 0);
+ length += currentCharacter.y - lastCharacter.y - lastGlyphHeight;
+ }
+ }
+
+ // Advance processed characters
+ i += positionOffset - 1;
+ charIt = itSearch;
+ }
+ }
+
+ ASSERT(charIt == chunk.end);
+ return length;
+}
+
+static float cummulatedWidthOfTextChunk(SVGTextChunk& chunk)
+{
+ return cummulatedWidthOrHeightOfTextChunk(chunk, true);
+}
+
+static float cummulatedHeightOfTextChunk(SVGTextChunk& chunk)
+{
+ return cummulatedWidthOrHeightOfTextChunk(chunk, false);
+}
+
+static float calculateTextAnchorShiftForTextChunk(SVGTextChunk& chunk, ETextAnchor anchor)
+{
+ float shift = 0.0f;
+
+ if (chunk.isVerticalText)
+ shift = cummulatedHeightOfTextChunk(chunk);
+ else
+ shift = cummulatedWidthOfTextChunk(chunk);
+
+ if (anchor == TA_MIDDLE)
+ shift *= -0.5f;
+ else
+ shift *= -1.0f;
+
+ return shift;
+}
+
+static void applyTextAnchorToTextChunk(SVGTextChunk& chunk)
+{
+ // This method is not called for chunks containing chars aligned on a path.
+ // -> all characters are visible, no need to check for "isHidden()" anywhere.
+
+ if (chunk.anchor == TA_START)
+ return;
+
+ float shift = calculateTextAnchorShiftForTextChunk(chunk, chunk.anchor);
+
+ // Apply correction to chunk
+ Vector<SVGChar>::iterator chunkIt = chunk.start;
+ for (; chunkIt != chunk.end; ++chunkIt) {
+ SVGChar& curChar = *chunkIt;
+
+ if (chunk.isVerticalText)
+ curChar.y += shift;
+ else
+ curChar.x += shift;
+ }
+
+ // Move inline boxes
+ Vector<SVGInlineBoxCharacterRange>::iterator boxIt = chunk.boxes.begin();
+ Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = chunk.boxes.end();
+
+ for (; boxIt != boxEnd; ++boxIt) {
+ SVGInlineBoxCharacterRange& range = *boxIt;
+
+ InlineBox* curBox = range.box;
+ ASSERT(curBox->isInlineTextBox());
+ ASSERT(curBox->parent() && (curBox->parent()->isRootInlineBox() || curBox->parent()->isInlineFlowBox()));
+
+ // Move target box
+ if (chunk.isVerticalText)
+ curBox->setYPos(curBox->yPos() + static_cast<int>(shift));
+ else
+ curBox->setXPos(curBox->xPos() + static_cast<int>(shift));
+ }
+}
+
+static float calculateTextLengthCorrectionForTextChunk(SVGTextChunk& chunk, ELengthAdjust lengthAdjust, float& computedLength)
+{
+ if (chunk.textLength <= 0.0f)
+ return 0.0f;
+
+ float computedWidth = cummulatedWidthOfTextChunk(chunk);
+ float computedHeight = cummulatedHeightOfTextChunk(chunk);
+
+ if ((computedWidth <= 0.0f && !chunk.isVerticalText) ||
+ (computedHeight <= 0.0f && chunk.isVerticalText))
+ return 0.0f;
+
+ if (chunk.isVerticalText)
+ computedLength = computedHeight;
+ else
+ computedLength = computedWidth;
+
+ if (lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) {
+ if (chunk.isVerticalText)
+ chunk.ctm.scaleNonUniform(1.0f, chunk.textLength / computedLength);
+ else
+ chunk.ctm.scaleNonUniform(chunk.textLength / computedLength, 1.0f);
+
+ return 0.0f;
+ }
+
+ return (chunk.textLength - computedLength) / float(chunk.end - chunk.start);
+}
+
+static void applyTextLengthCorrectionToTextChunk(SVGTextChunk& chunk)
+{
+ // This method is not called for chunks containing chars aligned on a path.
+ // -> all characters are visible, no need to check for "isHidden()" anywhere.
+
+ // lengthAdjust="spacingAndGlyphs" is handled by modifying chunk.ctm
+ float computedLength = 0.0f;
+ float spacingToApply = calculateTextLengthCorrectionForTextChunk(chunk, chunk.lengthAdjust, computedLength);
+
+ if (!chunk.ctm.isIdentity() && chunk.lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) {
+ SVGChar& firstChar = *(chunk.start);
+
+ // Assure we apply the chunk scaling in the right origin
+ TransformationMatrix newChunkCtm;
+ newChunkCtm.translate(firstChar.x, firstChar.y);
+ newChunkCtm = chunk.ctm * newChunkCtm;
+ newChunkCtm.translate(-firstChar.x, -firstChar.y);
+
+ chunk.ctm = newChunkCtm;
+ }
+
+ // Apply correction to chunk
+ if (spacingToApply != 0.0f) {
+ Vector<SVGChar>::iterator chunkIt = chunk.start;
+ for (; chunkIt != chunk.end; ++chunkIt) {
+ SVGChar& curChar = *chunkIt;
+ curChar.drawnSeperated = true;
+
+ if (chunk.isVerticalText)
+ curChar.y += (chunkIt - chunk.start) * spacingToApply;
+ else
+ curChar.x += (chunkIt - chunk.start) * spacingToApply;
+ }
+ }
+}
+
+void SVGRootInlineBox::computePerCharacterLayoutInformation()
+{
+ // Clean up any previous layout information
+ m_svgChars.clear();
+ m_svgTextChunks.clear();
+
+ // Build layout information for all contained render objects
+ SVGCharacterLayoutInfo info(m_svgChars);
+ buildLayoutInformation(this, info);
+
+ // 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.
+ buildTextChunks(m_svgChars, m_svgTextChunks, this);
+
+ // Layout all text chunks
+ // text-anchor needs to be applied to individual chunks.
+ layoutTextChunks();
+
+ // Finally the top left position of our box is known.
+ // Propogate this knownledge to our RenderSVGText parent.
+ FloatPoint topLeft = topLeftPositionOfCharacterRange(m_svgChars);
+ block()->setLocation((int) floorf(topLeft.x()), (int) floorf(topLeft.y()));
+
+ // Layout all InlineText/Flow boxes
+ // BEWARE: This requires the root top/left position to be set correctly before!
+ layoutInlineBoxes();
+}
+
+void SVGRootInlineBox::buildLayoutInformation(InlineFlowBox* start, SVGCharacterLayoutInfo& info)
+{
+ if (start->isRootInlineBox()) {
+ ASSERT(start->object()->element()->hasTagName(SVGNames::textTag));
+
+ SVGTextPositioningElement* positioningElement = static_cast<SVGTextPositioningElement*>(start->object()->element());
+ ASSERT(positioningElement);
+ ASSERT(positioningElement->parentNode());
+
+ info.addLayoutInformation(positioningElement);
+ }
+
+ LastGlyphInfo lastGlyph;
+
+ for (InlineBox* curr = start->firstChild(); curr; curr = curr->nextOnLine()) {
+ if (curr->object()->isText())
+ buildLayoutInformationForTextBox(info, static_cast<InlineTextBox*>(curr), lastGlyph);
+ else {
+ ASSERT(curr->isInlineFlowBox());
+ InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(curr);
+
+ if (!flowBox->object()->element())
+ continue; // Skip generated content.
+
+ bool isAnchor = flowBox->object()->element()->hasTagName(SVGNames::aTag);
+ bool isTextPath = flowBox->object()->element()->hasTagName(SVGNames::textPathTag);
+
+ if (!isTextPath && !isAnchor) {
+ SVGTextPositioningElement* positioningElement = static_cast<SVGTextPositioningElement*>(flowBox->object()->element());
+ 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->object()->element();
+ if (node && node->isSVGElement())
+ textContent = static_cast<SVGTextContentElement*>(node);
+ ASSERT(textContent);
+
+ ELengthAdjust lengthAdjust = (ELengthAdjust) textContent->lengthAdjust();
+ ETextAnchor anchor = flowBox->object()->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<SVGChar> tempChars;
+ Vector<SVGTextChunk> tempChunks;
+
+ SVGCharacterLayoutInfo tempInfo(tempChars);
+ buildLayoutInformation(flowBox, tempInfo);
+
+ buildTextChunks(tempChars, tempChunks, flowBox);
+
+ Vector<SVGTextChunk>::iterator it = tempChunks.begin();
+ Vector<SVGTextChunk>::iterator end = tempChunks.end();
+
+ TransformationMatrix ctm;
+ 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::layoutInlineBoxes()
+{
+ int lowX = INT_MAX;
+ int lowY = INT_MAX;
+ int highX = INT_MIN;
+ int highY = INT_MIN;
+
+ // Layout all child boxes
+ Vector<SVGChar>::iterator it = m_svgChars.begin();
+ layoutInlineBoxes(this, it, lowX, highX, lowY, highY);
+ ASSERT(it == m_svgChars.end());
+}
+
+void SVGRootInlineBox::layoutInlineBoxes(InlineFlowBox* start, Vector<SVGChar>::iterator& it, int& lowX, int& highX, int& lowY, int& highY)
+{
+ for (InlineBox* curr = start->firstChild(); curr; curr = curr->nextOnLine()) {
+ RenderStyle* style = curr->object()->style();
+ const Font& font = style->font();
+
+ if (curr->object()->isText()) {
+ SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(curr);
+ unsigned length = textBox->len();
+
+ SVGChar curChar = *it;
+ ASSERT(it != m_svgChars.end());
+
+ FloatRect stringRect;
+ for (unsigned i = 0; i < length; ++i) {
+ ASSERT(it != m_svgChars.end());
+
+ if (it->isHidden()) {
+ ++it;
+ continue;
+ }
+
+ stringRect.unite(textBox->calculateGlyphBoundaries(style, textBox->start() + i, *it));
+ ++it;
+ }
+
+ IntRect enclosedStringRect = enclosingIntRect(stringRect);
+
+ int minX = enclosedStringRect.x();
+ int maxX = minX + enclosedStringRect.width();
+
+ int minY = enclosedStringRect.y();
+ int maxY = minY + enclosedStringRect.height();
+
+ curr->setXPos(minX - block()->x());
+ curr->setWidth(enclosedStringRect.width());
+
+ curr->setYPos(minY - block()->y());
+ curr->setBaseline(font.ascent());
+ curr->setHeight(enclosedStringRect.height());
+
+ if (minX < lowX)
+ lowX = minX;
+
+ if (maxX > highX)
+ highX = maxX;
+
+ if (minY < lowY)
+ lowY = minY;
+
+ if (maxY > highY)
+ highY = maxY;
+ } else {
+ ASSERT(curr->isInlineFlowBox());
+
+ int minX = INT_MAX;
+ int minY = INT_MAX;
+ int maxX = INT_MIN;
+ int maxY = INT_MIN;
+
+ InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(curr);
+
+ if (!flowBox->object()->element())
+ continue; // Skip generated content.
+
+ layoutInlineBoxes(flowBox, it, minX, maxX, minY, maxY);
+
+ curr->setXPos(minX - block()->x());
+ curr->setWidth(maxX - minX);
+
+ curr->setYPos(minY - block()->y());
+ curr->setBaseline(font.ascent());
+ curr->setHeight(maxY - minY);
+
+ if (minX < lowX)
+ lowX = minX;
+
+ if (maxX > highX)
+ highX = maxX;
+
+ if (minY < lowY)
+ lowY = minY;
+
+ if (maxY > highY)
+ highY = maxY;
+ }
+ }
+
+ if (start->isRootInlineBox()) {
+ int top = lowY - block()->y();
+ int bottom = highY - block()->y();
+
+ start->setXPos(lowX - block()->x());
+ start->setYPos(top);
+
+ start->setWidth(highX - lowX);
+ start->setHeight(highY - lowY);
+
+ start->setVerticalOverflowPositions(top, bottom);
+ start->setVerticalSelectionPositions(top, bottom);
+ }
+}
+
+void SVGRootInlineBox::buildLayoutInformationForTextBox(SVGCharacterLayoutInfo& info, InlineTextBox* textBox, LastGlyphInfo& lastGlyph)
+{
+ RenderText* text = textBox->textObject();
+ ASSERT(text);
+
+ RenderStyle* style = text->style(textBox->isFirstLineStyle());
+ ASSERT(style);
+
+ const Font& font = style->font();
+ SVGInlineTextBox* svgTextBox = static_cast<SVGInlineTextBox*>(textBox);
+
+ unsigned length = textBox->len();
+
+ const SVGRenderStyle* svgStyle = style->svgStyle();
+ bool isVerticalText = isVerticalWritingMode(svgStyle);
+
+ int charsConsumed = 0;
+ for (unsigned i = 0; i < length; i += charsConsumed) {
+ SVGChar svgChar;
+
+ if (info.inPathLayout())
+ svgChar.pathData = SVGCharOnPath::create();
+
+ float glyphWidth = 0.0f;
+ float glyphHeight = 0.0f;
+
+ int extraCharsAvailable = length - i - 1;
+
+ String unicodeStr;
+ String glyphName;
+ if (textBox->direction() == RTL) {
+ glyphWidth = svgTextBox->calculateGlyphWidth(style, textBox->end() - i, extraCharsAvailable, charsConsumed, glyphName);
+ glyphHeight = svgTextBox->calculateGlyphHeight(style, textBox->end() - i, extraCharsAvailable);
+ unicodeStr = String(textBox->textObject()->text()->characters() + textBox->end() - i, charsConsumed);
+ } else {
+ glyphWidth = svgTextBox->calculateGlyphWidth(style, textBox->start() + i, extraCharsAvailable, charsConsumed, glyphName);
+ glyphHeight = svgTextBox->calculateGlyphHeight(style, textBox->start() + i, extraCharsAvailable);
+ unicodeStr = String(textBox->textObject()->text()->characters() + textBox->start() + i, charsConsumed);
+ }
+
+ bool assignedX = false;
+ bool assignedY = false;
+
+ if (info.xValueAvailable() && (!info.inPathLayout() || (info.inPathLayout() && !isVerticalText))) {
+ if (!isVerticalText)
+ svgChar.newTextChunk = true;
+
+ assignedX = true;
+ svgChar.drawnSeperated = true;
+ info.curx = info.xValueNext();
+ }
+
+ if (info.yValueAvailable() && (!info.inPathLayout() || (info.inPathLayout() && isVerticalText))) {
+ if (isVerticalText)
+ svgChar.newTextChunk = true;
+
+ assignedY = true;
+ svgChar.drawnSeperated = true;
+ info.cury = info.yValueNext();
+ }
+
+ float dx = 0.0f;
+ float dy = 0.0f;
+
+ // Apply x-axis shift
+ if (info.dxValueAvailable()) {
+ svgChar.drawnSeperated = true;
+
+ dx = info.dxValueNext();
+ info.dx += dx;
+
+ if (!info.inPathLayout())
+ info.curx += dx;
+ }
+
+ // Apply y-axis shift
+ if (info.dyValueAvailable()) {
+ svgChar.drawnSeperated = true;
+
+ dy = info.dyValueNext();
+ info.dy += dy;
+
+ if (!info.inPathLayout())
+ info.cury += dy;
+ }
+
+ // Take letter & word spacing and kerning into account
+ float spacing = font.letterSpacing() + calculateKerning(textBox->object()->element()->renderer());
+
+ const UChar* currentCharacter = text->characters() + (textBox->direction() == RTL ? textBox->end() - i : textBox->start() + i);
+ const UChar* lastCharacter = 0;
+
+ if (textBox->direction() == RTL) {
+ if (i < textBox->end())
+ lastCharacter = text->characters() + textBox->end() - i + 1;
+ } else {
+ if (i > 0)
+ lastCharacter = text->characters() + textBox->start() + i - 1;
+ }
+
+ if (info.nextDrawnSeperated || spacing != 0.0f) {
+ info.nextDrawnSeperated = false;
+ svgChar.drawnSeperated = true;
+ }
+
+ if (currentCharacter && Font::treatAsSpace(*currentCharacter) && lastCharacter && !Font::treatAsSpace(*lastCharacter)) {
+ spacing += font.wordSpacing();
+
+ if (spacing != 0.0f && !info.inPathLayout())
+ info.nextDrawnSeperated = true;
+ }
+
+ float orientationAngle = glyphOrientationToAngle(svgStyle, isVerticalText, *currentCharacter);
+
+ float xOrientationShift = 0.0f;
+ float yOrientationShift = 0.0f;
+ float glyphAdvance = calculateGlyphAdvanceAndShiftRespectingOrientation(isVerticalText, orientationAngle, glyphWidth, glyphHeight,
+ font, svgChar, xOrientationShift, yOrientationShift);
+
+ // Handle textPath layout mode
+ if (info.inPathLayout()) {
+ float extraAdvance = isVerticalText ? dy : dx;
+ float newOffset = FLT_MIN;
+
+ if (assignedX && !isVerticalText)
+ newOffset = info.curx;
+ else if (assignedY && isVerticalText)
+ newOffset = info.cury;
+
+ float correctedGlyphAdvance = glyphAdvance;
+
+ // Handle lengthAdjust="spacingAndGlyphs" by specifying per-character scale operations
+ if (info.pathTextLength > 0.0f && info.pathChunkLength > 0.0f) {
+ if (isVerticalText) {
+ svgChar.pathData->yScale = info.pathChunkLength / info.pathTextLength;
+ spacing *= svgChar.pathData->yScale;
+ correctedGlyphAdvance *= svgChar.pathData->yScale;
+ } else {
+ svgChar.pathData->xScale = info.pathChunkLength / info.pathTextLength;
+ spacing *= svgChar.pathData->xScale;
+ correctedGlyphAdvance *= svgChar.pathData->xScale;
+ }
+ }
+
+ // Handle letter & word spacing on text path
+ float pathExtraAdvance = info.pathExtraAdvance;
+ info.pathExtraAdvance += spacing;
+
+ svgChar.pathData->hidden = !info.nextPathLayoutPointAndAngle(correctedGlyphAdvance, extraAdvance, newOffset);
+ svgChar.drawnSeperated = true;
+
+ info.pathExtraAdvance = pathExtraAdvance;
+ }
+
+ // Apply rotation
+ if (info.angleValueAvailable())
+ info.angle = info.angleValueNext();
+
+ // Apply baseline-shift
+ if (info.baselineShiftValueAvailable()) {
+ svgChar.drawnSeperated = true;
+ float shift = info.baselineShiftValueNext();
+
+ if (isVerticalText)
+ info.shiftx += shift;
+ else
+ info.shifty -= shift;
+ }
+
+ // Take dominant-baseline / alignment-baseline into account
+ yOrientationShift += alignmentBaselineToShift(isVerticalText, text, font);
+
+ svgChar.x = info.curx;
+ svgChar.y = info.cury;
+ svgChar.angle = info.angle;
+
+ // For text paths any shift (dx/dy/baseline-shift) has to be applied after the rotation
+ if (!info.inPathLayout()) {
+ svgChar.x += info.shiftx + xOrientationShift;
+ svgChar.y += info.shifty + yOrientationShift;
+
+ if (orientationAngle != 0.0f)
+ svgChar.angle += orientationAngle;
+
+ if (svgChar.angle != 0.0f)
+ svgChar.drawnSeperated = true;
+ } else {
+ svgChar.pathData->orientationAngle = orientationAngle;
+
+ if (isVerticalText)
+ svgChar.angle -= 90.0f;
+
+ svgChar.pathData->xShift = info.shiftx + xOrientationShift;
+ svgChar.pathData->yShift = info.shifty + yOrientationShift;
+
+ // Translate to glyph midpoint
+ if (isVerticalText) {
+ svgChar.pathData->xShift += info.dx;
+ svgChar.pathData->yShift -= glyphAdvance / 2.0f;
+ } else {
+ svgChar.pathData->xShift -= glyphAdvance / 2.0f;
+ svgChar.pathData->yShift += info.dy;
+ }
+ }
+
+ double kerning = 0.0;
+#if ENABLE(SVG_FONTS)
+ SVGFontElement* svgFont = 0;
+ if (style->font().isSVGFont())
+ svgFont = style->font().svgFont();
+
+ if (lastGlyph.isValid && style->font().isSVGFont()) {
+ SVGHorizontalKerningPair kerningPair;
+ if (svgFont->getHorizontalKerningPairForStringsAndGlyphs(lastGlyph.unicode, lastGlyph.glyphName, unicodeStr, glyphName, kerningPair))
+ kerning = kerningPair.kerning;
+ }
+
+ if (style->font().isSVGFont()) {
+ lastGlyph.unicode = unicodeStr;
+ lastGlyph.glyphName = glyphName;
+ lastGlyph.isValid = true;
+ } else
+ lastGlyph.isValid = false;
+#endif
+
+ svgChar.x -= (float)kerning;
+
+ // Advance to new position
+ if (isVerticalText) {
+ svgChar.drawnSeperated = true;
+ info.cury += glyphAdvance + spacing;
+ } else
+ info.curx += glyphAdvance + spacing - (float)kerning;
+
+ // Advance to next character group
+ for (int k = 0; k < charsConsumed; ++k) {
+ info.svgChars.append(svgChar);
+ info.processedSingleCharacter();
+ svgChar.drawnSeperated = false;
+ svgChar.newTextChunk = false;
+ }
+ }
+}
+
+void SVGRootInlineBox::buildTextChunks(Vector<SVGChar>& svgChars, Vector<SVGTextChunk>& svgTextChunks, InlineFlowBox* start)
+{
+ SVGTextChunkLayoutInfo info(svgTextChunks);
+ info.it = svgChars.begin();
+ info.chunk.start = svgChars.begin();
+ info.chunk.end = svgChars.begin();
+
+ buildTextChunks(svgChars, start, info);
+ ASSERT(info.it == svgChars.end());
+}
+
+void SVGRootInlineBox::buildTextChunks(Vector<SVGChar>& svgChars, InlineFlowBox* start, SVGTextChunkLayoutInfo& info)
+{
+#if DEBUG_CHUNK_BUILDING > 1
+ fprintf(stderr, " -> buildTextChunks(start=%p)\n", start);
+#endif
+
+ for (InlineBox* curr = start->firstChild(); curr; curr = curr->nextOnLine()) {
+ if (curr->object()->isText()) {
+ InlineTextBox* textBox = static_cast<InlineTextBox*>(curr);
+
+ unsigned length = textBox->len();
+ if (!length)
+ continue;
+
+#if DEBUG_CHUNK_BUILDING > 1
+ fprintf(stderr, " -> Handle inline text box (%p) with %i characters (start: %i, end: %i), handlingTextPath=%i\n",
+ textBox, length, textBox->start(), textBox->end(), (int) info.handlingTextPath);
+#endif
+
+ RenderText* text = textBox->textObject();
+ ASSERT(text);
+ ASSERT(text->element());
+
+ SVGTextContentElement* textContent = 0;
+ Node* node = text->element()->parent();
+ while (node && node->isSVGElement() && !textContent) {
+ if (static_cast<SVGElement*>(node)->isTextContent())
+ textContent = static_cast<SVGTextContentElement*>(node);
+ else
+ node = node->parentNode();
+ }
+ ASSERT(textContent);
+
+ // Start new character range for the first chunk
+ bool isFirstCharacter = info.svgTextChunks.isEmpty() && info.chunk.start == info.it && info.chunk.start == info.chunk.end;
+ if (isFirstCharacter) {
+ ASSERT(info.chunk.boxes.isEmpty());
+ info.chunk.boxes.append(SVGInlineBoxCharacterRange());
+ } else
+ ASSERT(!info.chunk.boxes.isEmpty());
+
+ // Walk string to find out new chunk positions, if existant
+ for (unsigned i = 0; i < length; ++i) {
+ ASSERT(info.it != svgChars.end());
+
+ SVGInlineBoxCharacterRange& range = info.chunk.boxes.last();
+ if (range.isOpen()) {
+ range.box = curr;
+ range.startOffset = (i == 0 ? 0 : i - 1);
+
+#if DEBUG_CHUNK_BUILDING > 1
+ fprintf(stderr, " | -> Range is open! box=%p, startOffset=%i\n", range.box, range.startOffset);
+#endif
+ }
+
+ // If a new (or the first) chunk has been started, record it's text-anchor and writing mode.
+ if (info.assignChunkProperties) {
+ info.assignChunkProperties = false;
+
+ info.chunk.isVerticalText = isVerticalWritingMode(text->style()->svgStyle());
+ info.chunk.isTextPath = info.handlingTextPath;
+ info.chunk.anchor = text->style()->svgStyle()->textAnchor();
+ info.chunk.textLength = textContent->textLength().value(textContent);
+ info.chunk.lengthAdjust = (ELengthAdjust) textContent->lengthAdjust();
+
+#if DEBUG_CHUNK_BUILDING > 1
+ fprintf(stderr, " | -> Assign chunk properties, isVerticalText=%i, anchor=%i\n", info.chunk.isVerticalText, info.chunk.anchor);
+#endif
+ }
+
+ if (i > 0 && !isFirstCharacter && (*info.it).newTextChunk) {
+ // Close mid chunk & character range
+ ASSERT(!range.isOpen());
+ ASSERT(!range.isClosed());
+
+ range.endOffset = i;
+ closeTextChunk(info);
+
+#if DEBUG_CHUNK_BUILDING > 1
+ fprintf(stderr, " | -> Close mid-text chunk, at endOffset: %i and starting new mid chunk!\n", range.endOffset);
+#endif
+
+ // Prepare for next chunk, if we're not at the end
+ startTextChunk(info);
+ if (i + 1 == length) {
+#if DEBUG_CHUNK_BUILDING > 1
+ fprintf(stderr, " | -> Record last chunk of inline text box!\n");
+#endif
+
+ startTextChunk(info);
+ SVGInlineBoxCharacterRange& range = info.chunk.boxes.last();
+
+ info.assignChunkProperties = false;
+ info.chunk.isVerticalText = isVerticalWritingMode(text->style()->svgStyle());
+ info.chunk.isTextPath = info.handlingTextPath;
+ info.chunk.anchor = text->style()->svgStyle()->textAnchor();
+ info.chunk.textLength = textContent->textLength().value(textContent);
+ info.chunk.lengthAdjust = (ELengthAdjust) textContent->lengthAdjust();
+
+ range.box = curr;
+ range.startOffset = i;
+
+ ASSERT(!range.isOpen());
+ ASSERT(!range.isClosed());
+ }
+ }
+
+ // This should only hold true for the first character of the first chunk
+ if (isFirstCharacter)
+ isFirstCharacter = false;
+
+ ++info.it;
+ }
+
+#if DEBUG_CHUNK_BUILDING > 1
+ fprintf(stderr, " -> Finished inline text box!\n");
+#endif
+
+ SVGInlineBoxCharacterRange& range = info.chunk.boxes.last();
+ if (!range.isOpen() && !range.isClosed()) {
+#if DEBUG_CHUNK_BUILDING > 1
+ fprintf(stderr, " -> Last range not closed - closing with endOffset: %i\n", length);
+#endif
+
+ // Current text chunk is not yet closed. Finish the current range, but don't start a new chunk.
+ range.endOffset = length;
+
+ if (info.it != svgChars.end()) {
+#if DEBUG_CHUNK_BUILDING > 1
+ fprintf(stderr, " -> Not at last character yet!\n");
+#endif
+
+ // If we're not at the end of the last box to be processed, and if the next
+ // character starts a new chunk, then close the current chunk and start a new one.
+ if ((*info.it).newTextChunk) {
+#if DEBUG_CHUNK_BUILDING > 1
+ fprintf(stderr, " -> Next character starts new chunk! Closing current chunk, and starting a new one...\n");
+#endif
+
+ closeTextChunk(info);
+ startTextChunk(info);
+ } else {
+ // Just start a new character range
+ info.chunk.boxes.append(SVGInlineBoxCharacterRange());
+
+#if DEBUG_CHUNK_BUILDING > 1
+ fprintf(stderr, " -> Next character does NOT start a new chunk! Starting new character range...\n");
+#endif
+ }
+ } else {
+#if DEBUG_CHUNK_BUILDING > 1
+ fprintf(stderr, " -> Closing final chunk! Finished processing!\n");
+#endif
+
+ // Close final chunk, once we're at the end of the last box
+ closeTextChunk(info);
+ }
+ }
+ } else {
+ ASSERT(curr->isInlineFlowBox());
+ InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(curr);
+
+ if (!flowBox->object()->element())
+ continue; // Skip generated content.
+
+ bool isTextPath = flowBox->object()->element()->hasTagName(SVGNames::textPathTag);
+
+#if DEBUG_CHUNK_BUILDING > 1
+ fprintf(stderr, " -> Handle inline flow box (%p), isTextPath=%i\n", flowBox, (int) isTextPath);
+#endif
+
+ if (isTextPath)
+ info.handlingTextPath = true;
+
+ buildTextChunks(svgChars, flowBox, info);
+
+ if (isTextPath)
+ info.handlingTextPath = false;
+ }
+ }
+
+#if DEBUG_CHUNK_BUILDING > 1
+ fprintf(stderr, " <- buildTextChunks(start=%p)\n", start);
+#endif
+}
+
+const Vector<SVGTextChunk>& SVGRootInlineBox::svgTextChunks() const
+{
+ return m_svgTextChunks;
+}
+
+void SVGRootInlineBox::layoutTextChunks()
+{
+ Vector<SVGTextChunk>::iterator it = m_svgTextChunks.begin();
+ Vector<SVGTextChunk>::iterator end = m_svgTextChunks.end();
+
+ for (; it != end; ++it) {
+ SVGTextChunk& chunk = *it;
+
+#if DEBUG_CHUNK_BUILDING > 0
+ {
+ fprintf(stderr, "Handle TEXT CHUNK! anchor=%i, textLength=%f, lengthAdjust=%i, isVerticalText=%i, isTextPath=%i start=%p, end=%p -> dist: %i\n",
+ (int) chunk.anchor, chunk.textLength, (int) chunk.lengthAdjust, (int) chunk.isVerticalText,
+ (int) chunk.isTextPath, chunk.start, chunk.end, (unsigned int) (chunk.end - chunk.start));
+
+ Vector<SVGInlineBoxCharacterRange>::iterator boxIt = chunk.boxes.begin();
+ Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = chunk.boxes.end();
+
+ unsigned int i = 0;
+ for (; boxIt != boxEnd; ++boxIt) {
+ SVGInlineBoxCharacterRange& range = *boxIt; i++;
+ fprintf(stderr, " -> RANGE %i STARTOFFSET: %i, ENDOFFSET: %i, BOX: %p\n", i, range.startOffset, range.endOffset, range.box);
+ }
+ }
+#endif
+
+ if (chunk.isTextPath)
+ continue;
+
+ // text-path & textLength, with lengthAdjust="spacing" is already handled for textPath layouts.
+ applyTextLengthCorrectionToTextChunk(chunk);
+
+ // text-anchor is already handled for textPath layouts.
+ applyTextAnchorToTextChunk(chunk);
+ }
+}
+
+static inline void addPaintServerToTextDecorationInfo(ETextDecoration decoration, SVGTextDecorationInfo& info, RenderObject* object)
+{
+ if (object->style()->svgStyle()->hasFill())
+ info.fillServerMap.set(decoration, object);
+
+ if (object->style()->svgStyle()->hasStroke())
+ info.strokeServerMap.set(decoration, object);
+}
+
+SVGTextDecorationInfo SVGRootInlineBox::retrievePaintServersForTextDecoration(RenderObject* start)
+{
+ ASSERT(start);
+
+ Vector<RenderObject*> parentChain;
+ while ((start = start->parent())) {
+ parentChain.prepend(start);
+
+ // Stop at our direct <text> parent.
+ if (start->isSVGText())
+ break;
+ }
+
+ Vector<RenderObject*>::iterator it = parentChain.begin();
+ Vector<RenderObject*>::iterator end = parentChain.end();
+
+ SVGTextDecorationInfo info;
+
+ for (; it != end; ++it) {
+ RenderObject* object = *it;
+ ASSERT(object);
+
+ RenderStyle* style = object->style();
+ ASSERT(style);
+
+ int decorations = style->textDecoration();
+ if (decorations != NONE) {
+ if (decorations & OVERLINE)
+ addPaintServerToTextDecorationInfo(OVERLINE, info, object);
+
+ if (decorations & UNDERLINE)
+ addPaintServerToTextDecorationInfo(UNDERLINE, info, object);
+
+ if (decorations & LINE_THROUGH)
+ addPaintServerToTextDecorationInfo(LINE_THROUGH, info, object);
+ }
+ }
+
+ return info;
+}
+
+void SVGRootInlineBox::walkTextChunks(SVGTextChunkWalkerBase* walker, const SVGInlineTextBox* textBox)
+{
+ ASSERT(walker);
+
+ Vector<SVGTextChunk>::iterator it = m_svgTextChunks.begin();
+ Vector<SVGTextChunk>::iterator itEnd = m_svgTextChunks.end();
+
+ for (; it != itEnd; ++it) {
+ SVGTextChunk& curChunk = *it;
+
+ Vector<SVGInlineBoxCharacterRange>::iterator boxIt = curChunk.boxes.begin();
+ Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = curChunk.boxes.end();
+
+ InlineBox* lastNotifiedBox = 0;
+ InlineBox* prevBox = 0;
+
+ unsigned int chunkOffset = 0;
+ bool startedFirstChunk = false;
+
+ for (; boxIt != boxEnd; ++boxIt) {
+ SVGInlineBoxCharacterRange& range = *boxIt;
+
+ ASSERT(range.box->isInlineTextBox());
+ SVGInlineTextBox* rangeTextBox = static_cast<SVGInlineTextBox*>(range.box);
+
+ if (textBox && rangeTextBox != textBox) {
+ chunkOffset += range.endOffset - range.startOffset;
+ continue;
+ }
+
+ // Eventually notify that we started a new chunk
+ if (!textBox && !startedFirstChunk) {
+ startedFirstChunk = true;
+
+ lastNotifiedBox = range.box;
+ walker->start(range.box);
+ } else {
+ // Eventually apply new style, as this chunk spans multiple boxes (with possible different styling)
+ if (prevBox && prevBox != range.box) {
+ lastNotifiedBox = range.box;
+
+ walker->end(prevBox);
+ walker->start(lastNotifiedBox);
+ }
+ }
+
+ unsigned int length = range.endOffset - range.startOffset;
+
+ Vector<SVGChar>::iterator itCharBegin = curChunk.start + chunkOffset;
+ Vector<SVGChar>::iterator itCharEnd = curChunk.start + chunkOffset + length;
+ ASSERT(itCharEnd <= curChunk.end);
+
+ // Process this chunk portion
+ if (textBox)
+ (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd);
+ else {
+ if (walker->setupFill(range.box))
+ (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd);
+
+ if (walker->setupStroke(range.box))
+ (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd);
+ }
+
+ chunkOffset += length;
+
+ if (!textBox)
+ prevBox = range.box;
+ }
+
+ if (!textBox && startedFirstChunk)
+ walker->end(lastNotifiedBox);
+ }
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(SVG)
« no previous file with comments | « third_party/WebKit/WebCore/rendering/SVGCharacterLayoutInfo.cpp ('k') | third_party/WebKit/WebCore/rendering/bidi.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698