diff options
author | Upstream <upstream-import@none> | 1970-01-12 13:46:40 +0000 |
---|---|---|
committer | Upstream <upstream-import@none> | 1970-01-12 13:46:40 +0000 |
commit | d8543bb6618c17b12da906afa77d216f58cf4058 (patch) | |
tree | c58dc05ed86825bd0ef8d305d58c8205106b540f /WebCore/rendering/SVGCharacterLayoutInfo.cpp | |
download | external_webkit-d8543bb6618c17b12da906afa77d216f58cf4058.zip external_webkit-d8543bb6618c17b12da906afa77d216f58cf4058.tar.gz external_webkit-d8543bb6618c17b12da906afa77d216f58cf4058.tar.bz2 |
external/webkit r30707
Diffstat (limited to 'WebCore/rendering/SVGCharacterLayoutInfo.cpp')
-rw-r--r-- | WebCore/rendering/SVGCharacterLayoutInfo.cpp | 535 |
1 files changed, 535 insertions, 0 deletions
diff --git a/WebCore/rendering/SVGCharacterLayoutInfo.cpp b/WebCore/rendering/SVGCharacterLayoutInfo.cpp new file mode 100644 index 0000000..5cad6d9 --- /dev/null +++ b/WebCore/rendering/SVGCharacterLayoutInfo.cpp @@ -0,0 +1,535 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (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 "SVGCharacterLayoutInfo.h" + +#include "InlineFlowBox.h" +#include "InlineTextBox.h" +#include "SVGLengthList.h" +#include "SVGNumberList.h" +#include "SVGTextPositioningElement.h" +#include "RenderSVGTextPath.h" + +#include <float.h> + +namespace WebCore { + +// Helper function +static float calculateBaselineShift(RenderObject* item) +{ + const Font& font = item->style()->font(); + const SVGRenderStyle* svgStyle = item->style()->svgStyle(); + + float baselineShift = 0.0f; + if (svgStyle->baselineShift() == BS_LENGTH) { + CSSPrimitiveValue* primitive = static_cast<CSSPrimitiveValue*>(svgStyle->baselineShiftValue()); + baselineShift = primitive->getFloatValue(); + + if (primitive->primitiveType() == CSSPrimitiveValue::CSS_PERCENTAGE) + baselineShift = baselineShift / 100.0f * font.pixelSize(); + } else { + float baselineAscent = font.ascent() + font.descent(); + + switch (svgStyle->baselineShift()) { + case BS_BASELINE: + break; + case BS_SUB: + baselineShift = -baselineAscent / 2.0f; + break; + case BS_SUPER: + baselineShift = baselineAscent / 2.0f; + break; + default: + ASSERT_NOT_REACHED(); + } + } + + return baselineShift; +} + +SVGCharacterLayoutInfo::SVGCharacterLayoutInfo(Vector<SVGChar>& chars) + : curx(0.0f) + , cury(0.0f) + , angle(0.0f) + , dx(0.0f) + , dy(0.0f) + , shiftx(0.0f) + , shifty(0.0f) + , pathExtraAdvance(0.0f) + , pathTextLength(0.0f) + , pathChunkLength(0.0f) + , svgChars(chars) + , nextDrawnSeperated(false) + , xStackChanged(false) + , yStackChanged(false) + , dxStackChanged(false) + , dyStackChanged(false) + , angleStackChanged(false) + , baselineShiftStackChanged(false) + , pathLayout(false) + , currentOffset(0.0f) + , startOffset(0.0f) + , layoutPathLength(0.0f) +{ +} + +bool SVGCharacterLayoutInfo::xValueAvailable() const +{ + return xStack.isEmpty() ? false : xStack.last().position() < xStack.last().size(); +} + +bool SVGCharacterLayoutInfo::yValueAvailable() const +{ + return yStack.isEmpty() ? false : yStack.last().position() < yStack.last().size(); +} + +bool SVGCharacterLayoutInfo::dxValueAvailable() const +{ + return dxStack.isEmpty() ? false : dxStack.last().position() < dxStack.last().size(); +} + +bool SVGCharacterLayoutInfo::dyValueAvailable() const +{ + return dyStack.isEmpty() ? false : dyStack.last().position() < dyStack.last().size(); +} + +bool SVGCharacterLayoutInfo::angleValueAvailable() const +{ + return angleStack.isEmpty() ? false : angleStack.last().position() < angleStack.last().size(); +} + +bool SVGCharacterLayoutInfo::baselineShiftValueAvailable() const +{ + return !baselineShiftStack.isEmpty(); +} + +float SVGCharacterLayoutInfo::xValueNext() const +{ + ASSERT(!xStack.isEmpty()); + return xStack.last().valueAtCurrentPosition(); +} + +float SVGCharacterLayoutInfo::yValueNext() const +{ + ASSERT(!yStack.isEmpty()); + return yStack.last().valueAtCurrentPosition(); +} + +float SVGCharacterLayoutInfo::dxValueNext() const +{ + ASSERT(!dxStack.isEmpty()); + return dxStack.last().valueAtCurrentPosition(); +} + +float SVGCharacterLayoutInfo::dyValueNext() const +{ + ASSERT(!dyStack.isEmpty()); + return dyStack.last().valueAtCurrentPosition(); +} + +float SVGCharacterLayoutInfo::angleValueNext() const +{ + ASSERT(!angleStack.isEmpty()); + return angleStack.last().valueAtCurrentPosition(); +} + +float SVGCharacterLayoutInfo::baselineShiftValueNext() const +{ + ASSERT(!baselineShiftStack.isEmpty()); + return baselineShiftStack.last(); +} + +void SVGCharacterLayoutInfo::processedSingleCharacter() +{ + xStackWalk(); + yStackWalk(); + dxStackWalk(); + dyStackWalk(); + angleStackWalk(); + baselineShiftStackWalk(); +} + +void SVGCharacterLayoutInfo::processedChunk(float savedShiftX, float savedShiftY) +{ + // baseline-shift doesn't span across ancestors, unlike dx/dy. + curx += savedShiftX - shiftx; + cury += savedShiftY - shifty; + + if (inPathLayout()) { + shiftx = savedShiftX; + shifty = savedShiftY; + } + + // rotation also doesn't span + angle = 0.0f; + + if (xStackChanged) { + ASSERT(!xStack.isEmpty()); + xStack.removeLast(); + xStackChanged = false; + } + + if (yStackChanged) { + ASSERT(!yStack.isEmpty()); + yStack.removeLast(); + yStackChanged = false; + } + + if (dxStackChanged) { + ASSERT(!dxStack.isEmpty()); + dxStack.removeLast(); + dxStackChanged = false; + } + + if (dyStackChanged) { + ASSERT(!dyStack.isEmpty()); + dyStack.removeLast(); + dyStackChanged = false; + } + + if (angleStackChanged) { + ASSERT(!angleStack.isEmpty()); + angleStack.removeLast(); + angleStackChanged = false; + } + + if (baselineShiftStackChanged) { + ASSERT(!baselineShiftStack.isEmpty()); + baselineShiftStack.removeLast(); + baselineShiftStackChanged = false; + } +} + +bool SVGCharacterLayoutInfo::nextPathLayoutPointAndAngle(float glyphAdvance, float extraAdvance, float newOffset) +{ + if (layoutPathLength <= 0.0f) + return false; + + if (newOffset != FLT_MIN) + currentOffset = startOffset + newOffset; + + // Respect translation along path (extraAdvance is orthogonal to the path) + currentOffset += extraAdvance; + + float offset = currentOffset + glyphAdvance / 2.0f; + currentOffset += glyphAdvance + pathExtraAdvance; + + if (offset < 0.0f || offset > layoutPathLength) + return false; + + bool ok = false; + FloatPoint point = layoutPath.pointAtLength(offset, ok); + ASSERT(ok); + + curx = point.x(); + cury = point.y(); + + angle = layoutPath.normalAngleAtLength(offset, ok); + ASSERT(ok); + + // fprintf(stderr, "t: %f, x: %f, y: %f, angle: %f, glyphAdvance: %f\n", currentOffset, x, y, angle, glyphAdvance); + return true; +} + +bool SVGCharacterLayoutInfo::inPathLayout() const +{ + return pathLayout; +} + +void SVGCharacterLayoutInfo::setInPathLayout(bool value) +{ + pathLayout = value; + + pathExtraAdvance = 0.0f; + pathTextLength = 0.0f; + pathChunkLength = 0.0f; +} + +void SVGCharacterLayoutInfo::addLayoutInformation(InlineFlowBox* flowBox, float textAnchorStartOffset) +{ + bool isInitialLayout = xStack.isEmpty() && yStack.isEmpty() && + dxStack.isEmpty() && dyStack.isEmpty() && + angleStack.isEmpty() && baselineShiftStack.isEmpty() && + curx == 0.0f && cury == 0.0f; + + RenderSVGTextPath* textPath = static_cast<RenderSVGTextPath*>(flowBox->object()); + Path path = textPath->layoutPath(); + + float baselineShift = calculateBaselineShift(textPath); + + layoutPath = path; + layoutPathLength = path.length(); + + if (layoutPathLength <= 0.0f) + return; + + startOffset = textPath->startOffset(); + + if (textPath->startOffset() >= 0.0f && textPath->startOffset() <= 1.0f) + startOffset *= layoutPathLength; + + startOffset += textAnchorStartOffset; + currentOffset = startOffset; + + // Only baseline-shift is handled through the normal layout system + addStackContent(BaselineShiftStack, baselineShift); + + if (isInitialLayout) { + xStackChanged = false; + yStackChanged = false; + dxStackChanged = false; + dyStackChanged = false; + angleStackChanged = false; + baselineShiftStackChanged = false; + } +} + +void SVGCharacterLayoutInfo::addLayoutInformation(SVGTextPositioningElement* element) +{ + bool isInitialLayout = xStack.isEmpty() && yStack.isEmpty() && + dxStack.isEmpty() && dyStack.isEmpty() && + angleStack.isEmpty() && baselineShiftStack.isEmpty() && + curx == 0.0f && cury == 0.0f; + + float baselineShift = calculateBaselineShift(element->renderer()); + + addStackContent(XStack, element->x()); + addStackContent(YStack, element->y()); + addStackContent(DxStack, element->dx()); + addStackContent(DyStack, element->dy()); + addStackContent(AngleStack, element->rotate()); + addStackContent(BaselineShiftStack, baselineShift); + + if (isInitialLayout) { + xStackChanged = false; + yStackChanged = false; + dxStackChanged = false; + dyStackChanged = false; + angleStackChanged = false; + baselineShiftStackChanged = false; + } +} + +void SVGCharacterLayoutInfo::addStackContent(StackType type, SVGNumberList* list) +{ + unsigned length = list->numberOfItems(); + if (!length) + return; + + PositionedFloatVector newLayoutInfo; + + // TODO: Convert more efficiently! + ExceptionCode ec = 0; + for (unsigned i = 0; i < length; ++i) { + float value = list->getItem(i, ec); + ASSERT(ec == 0); + + newLayoutInfo.append(value); + } + + addStackContent(type, newLayoutInfo); +} + +void SVGCharacterLayoutInfo::addStackContent(StackType type, SVGLengthList* list) +{ + unsigned length = list->numberOfItems(); + if (!length) + return; + + PositionedFloatVector newLayoutInfo; + + ExceptionCode ec = 0; + for (unsigned i = 0; i < length; ++i) { + float value = list->getItem(i, ec).value(); + ASSERT(ec == 0); + + newLayoutInfo.append(value); + } + + addStackContent(type, newLayoutInfo); +} + +void SVGCharacterLayoutInfo::addStackContent(StackType type, const PositionedFloatVector& list) +{ + switch (type) { + case XStack: + xStackChanged = true; + xStack.append(list); + break; + case YStack: + yStackChanged = true; + yStack.append(list); + break; + case DxStack: + dxStackChanged = true; + dxStack.append(list); + break; + case DyStack: + dyStackChanged = true; + dyStack.append(list); + break; + case AngleStack: + angleStackChanged = true; + angleStack.append(list); + break; + default: + ASSERT_NOT_REACHED(); + } +} + +void SVGCharacterLayoutInfo::addStackContent(StackType type, float value) +{ + if (value == 0.0f) + return; + + switch (type) { + case BaselineShiftStack: + baselineShiftStackChanged = true; + baselineShiftStack.append(value); + break; + default: + ASSERT_NOT_REACHED(); + } +} + +void SVGCharacterLayoutInfo::xStackWalk() +{ + unsigned i = 1; + + while (!xStack.isEmpty()) { + PositionedFloatVector& cur = xStack.last(); + if (i + cur.position() < cur.size()) { + cur.advance(i); + break; + } + + i += cur.position(); + xStack.removeLast(); + xStackChanged = false; + } +} + +void SVGCharacterLayoutInfo::yStackWalk() +{ + unsigned i = 1; + + while (!yStack.isEmpty()) { + PositionedFloatVector& cur = yStack.last(); + if (i + cur.position() < cur.size()) { + cur.advance(i); + break; + } + + i += cur.position(); + yStack.removeLast(); + yStackChanged = false; + } +} + +void SVGCharacterLayoutInfo::dxStackWalk() +{ + unsigned i = 1; + + while (!dxStack.isEmpty()) { + PositionedFloatVector& cur = dxStack.last(); + if (i + cur.position() < cur.size()) { + cur.advance(i); + break; + } + + i += cur.position(); + dxStack.removeLast(); + dxStackChanged = false; + } +} + +void SVGCharacterLayoutInfo::dyStackWalk() +{ + unsigned i = 1; + + while (!dyStack.isEmpty()) { + PositionedFloatVector& cur = dyStack.last(); + if (i + cur.position() < cur.size()) { + cur.advance(i); + break; + } + + i += cur.position(); + dyStack.removeLast(); + dyStackChanged = false; + } +} + +void SVGCharacterLayoutInfo::angleStackWalk() +{ + unsigned i = 1; + + while (!angleStack.isEmpty()) { + PositionedFloatVector& cur = angleStack.last(); + if (i + cur.position() < cur.size()) { + cur.advance(i); + break; + } + + i += cur.position(); + angleStack.removeLast(); + angleStackChanged = false; + } +} + +void SVGCharacterLayoutInfo::baselineShiftStackWalk() +{ + if (!baselineShiftStack.isEmpty()) { + baselineShiftStack.removeLast(); + baselineShiftStackChanged = false; + } +} + +bool SVGChar::isHidden() const +{ + return pathData && pathData->hidden; +} + +AffineTransform SVGChar::characterTransform() const +{ + AffineTransform ctm; + + // Rotate character around angle, and possibly scale. + ctm.translate(x, y); + ctm.rotate(angle); + + if (pathData) { + ctm.scale(pathData->xScale, pathData->yScale); + ctm.translate(pathData->xShift, pathData->yShift); + ctm.rotate(pathData->orientationAngle); + } + + ctm.translate(orientationShiftX - x, orientationShiftY - y); + return ctm; +} + +} // namespace WebCore + +#endif // ENABLE(SVG) |