blob: 5132bbed75d11ee0ad59fc50e7e8b1deec73cf7e [file] [log] [blame]
/*
* Copyright (C) 2004, 2005, 2008 Nikolas Zimmermann <zimmermann@kde.org>
* Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
* Copyright (C) 2007 Eric Seidel <eric@webkit.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"
#include "SVGTransformable.h"
#include "AffineTransform.h"
#include "FloatConversion.h"
#include "SVGElement.h"
#include "SVGNames.h"
#include "SVGParserUtilities.h"
#include <wtf/text/StringParsingBuffer.h>
#include <wtf/text/StringView.h>
namespace WebCore {
SVGTransformable::~SVGTransformable() = default;
template<typename CharacterType> static int parseTransformParamList(StringParsingBuffer<CharacterType>& buffer, float* values, int required, int optional)
{
int optionalParams = 0, requiredParams = 0;
if (!skipOptionalSVGSpaces(buffer) || *buffer != '(')
return -1;
++buffer;
skipOptionalSVGSpaces(buffer);
while (requiredParams < required) {
if (buffer.atEnd())
return -1;
auto parsedNumber = parseNumber(buffer, SuffixSkippingPolicy::DontSkip);
if (!parsedNumber)
return -1;
values[requiredParams] = *parsedNumber;
requiredParams++;
if (requiredParams < required)
skipOptionalSVGSpacesOrDelimiter(buffer);
}
if (!skipOptionalSVGSpaces(buffer))
return -1;
bool delimParsed = skipOptionalSVGSpacesOrDelimiter(buffer);
if (buffer.atEnd())
return -1;
if (*buffer == ')') {
// skip optionals
++buffer;
if (delimParsed)
return -1;
} else {
while (optionalParams < optional) {
if (buffer.atEnd())
return -1;
auto parsedNumber = parseNumber(buffer, SuffixSkippingPolicy::DontSkip);
if (!parsedNumber)
return -1;
values[requiredParams + optionalParams] = *parsedNumber;
optionalParams++;
if (optionalParams < optional)
skipOptionalSVGSpacesOrDelimiter(buffer);
}
if (!skipOptionalSVGSpaces(buffer))
return -1;
delimParsed = skipOptionalSVGSpacesOrDelimiter(buffer);
if (buffer.atEnd() || *buffer != ')' || delimParsed)
return -1;
++buffer;
}
return requiredParams + optionalParams;
}
// These should be kept in sync with enum SVGTransformType
static constexpr int requiredValuesForType[] = { 0, 6, 1, 1, 1, 1, 1 };
static constexpr int optionalValuesForType[] = { 0, 0, 1, 1, 2, 0, 0 };
template<typename CharacterType> static std::optional<SVGTransformValue> parseTransformValueGeneric(SVGTransformValue::SVGTransformType type, StringParsingBuffer<CharacterType>& buffer)
{
if (type == SVGTransformValue::SVG_TRANSFORM_UNKNOWN)
return std::nullopt;
int valueCount = 0;
float values[] = {0, 0, 0, 0, 0, 0};
if ((valueCount = parseTransformParamList(buffer, values, requiredValuesForType[type], optionalValuesForType[type])) < 0)
return std::nullopt;
SVGTransformValue transform;
switch (type) {
case SVGTransformValue::SVG_TRANSFORM_UNKNOWN:
ASSERT_NOT_REACHED();
break;
case SVGTransformValue::SVG_TRANSFORM_SKEWX:
transform.setSkewX(values[0]);
break;
case SVGTransformValue::SVG_TRANSFORM_SKEWY:
transform.setSkewY(values[0]);
break;
case SVGTransformValue::SVG_TRANSFORM_SCALE:
if (valueCount == 1) // Spec: if only one param given, assume uniform scaling
transform.setScale(values[0], values[0]);
else
transform.setScale(values[0], values[1]);
break;
case SVGTransformValue::SVG_TRANSFORM_TRANSLATE:
if (valueCount == 1) // Spec: if only one param given, assume 2nd param to be 0
transform.setTranslate(values[0], 0);
else
transform.setTranslate(values[0], values[1]);
break;
case SVGTransformValue::SVG_TRANSFORM_ROTATE:
if (valueCount == 1)
transform.setRotate(values[0], 0, 0);
else
transform.setRotate(values[0], values[1], values[2]);
break;
case SVGTransformValue::SVG_TRANSFORM_MATRIX:
transform.setMatrix(AffineTransform(values[0], values[1], values[2], values[3], values[4], values[5]));
break;
}
return transform;
}
std::optional<SVGTransformValue> SVGTransformable::parseTransformValue(SVGTransformValue::SVGTransformType type, StringParsingBuffer<LChar>& buffer)
{
return parseTransformValueGeneric(type, buffer);
}
std::optional<SVGTransformValue> SVGTransformable::parseTransformValue(SVGTransformValue::SVGTransformType type, StringParsingBuffer<UChar>& buffer)
{
return parseTransformValueGeneric(type, buffer);
}
template<typename CharacterType> static constexpr CharacterType skewXDesc[] = {'s', 'k', 'e', 'w', 'X'};
template<typename CharacterType> static constexpr CharacterType skewYDesc[] = {'s', 'k', 'e', 'w', 'Y'};
template<typename CharacterType> static constexpr CharacterType scaleDesc[] = {'s', 'c', 'a', 'l', 'e'};
template<typename CharacterType> static constexpr CharacterType translateDesc[] = {'t', 'r', 'a', 'n', 's', 'l', 'a', 't', 'e'};
template<typename CharacterType> static constexpr CharacterType rotateDesc[] = {'r', 'o', 't', 'a', 't', 'e'};
template<typename CharacterType> static constexpr CharacterType matrixDesc[] = {'m', 'a', 't', 'r', 'i', 'x'};
template<typename CharacterType> static std::optional<SVGTransformValue::SVGTransformType> parseTransformTypeGeneric(StringParsingBuffer<CharacterType>& buffer)
{
if (buffer.atEnd())
return std::nullopt;
if (*buffer == 's') {
if (skipCharactersExactly(buffer, skewXDesc<CharacterType>))
return SVGTransformValue::SVG_TRANSFORM_SKEWX;
if (skipCharactersExactly(buffer, skewYDesc<CharacterType>))
return SVGTransformValue::SVG_TRANSFORM_SKEWY;
if (skipCharactersExactly(buffer, scaleDesc<CharacterType>))
return SVGTransformValue::SVG_TRANSFORM_SCALE;
return std::nullopt;
}
if (skipCharactersExactly(buffer, translateDesc<CharacterType>))
return SVGTransformValue::SVG_TRANSFORM_TRANSLATE;
if (skipCharactersExactly(buffer, rotateDesc<CharacterType>))
return SVGTransformValue::SVG_TRANSFORM_ROTATE;
if (skipCharactersExactly(buffer, matrixDesc<CharacterType>))
return SVGTransformValue::SVG_TRANSFORM_MATRIX;
return std::nullopt;
}
std::optional<SVGTransformValue::SVGTransformType> SVGTransformable::parseTransformType(StringView string)
{
return readCharactersForParsing(string, [](auto buffer) {
return parseTransformType(buffer);
});
}
std::optional<SVGTransformValue::SVGTransformType> SVGTransformable::parseTransformType(StringParsingBuffer<LChar>& buffer)
{
return parseTransformTypeGeneric(buffer);
}
std::optional<SVGTransformValue::SVGTransformType> SVGTransformable::parseTransformType(StringParsingBuffer<UChar>& buffer)
{
return parseTransformTypeGeneric(buffer);
}
}