blob: b5a53226456ba2c10975db20dc10bcb996019844 [file] [log] [blame]
/*
* Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <zimmermann@kde.org>
* Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
* Copyright (C) 2007 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "core/svg/SVGLength.h"
#include "core/css/CSSPrimitiveValue.h"
#include "core/css/CSSValue.h"
#include "core/css/parser/CSSParser.h"
#include "core/svg/SVGAnimationElement.h"
#include "core/svg_names.h"
#include "platform/wtf/MathExtras.h"
#include "platform/wtf/text/WTFString.h"
namespace blink {
SVGLength::SVGLength(SVGLengthMode mode)
: value_(
CSSPrimitiveValue::Create(0,
CSSPrimitiveValue::UnitType::kUserUnits)),
unit_mode_(static_cast<unsigned>(mode)) {
DCHECK_EQ(UnitMode(), mode);
}
SVGLength::SVGLength(const SVGLength& o)
: value_(o.value_), unit_mode_(o.unit_mode_) {}
void SVGLength::Trace(blink::Visitor* visitor) {
visitor->Trace(value_);
SVGPropertyBase::Trace(visitor);
}
SVGLength* SVGLength::Clone() const {
return new SVGLength(*this);
}
SVGPropertyBase* SVGLength::CloneForAnimation(const String& value) const {
SVGLength* length = Create();
length->unit_mode_ = unit_mode_;
if (length->SetValueAsString(value) != SVGParseStatus::kNoError)
length->value_ =
CSSPrimitiveValue::Create(0, CSSPrimitiveValue::UnitType::kUserUnits);
return length;
}
bool SVGLength::operator==(const SVGLength& other) const {
return unit_mode_ == other.unit_mode_ && value_ == other.value_;
}
float SVGLength::Value(const SVGLengthContext& context) const {
if (IsCalculated())
return context.ResolveValue(AsCSSPrimitiveValue(), UnitMode());
return context.ConvertValueToUserUnits(value_->GetFloatValue(), UnitMode(),
value_->TypeWithCalcResolved());
}
void SVGLength::SetValueAsNumber(float value) {
value_ =
CSSPrimitiveValue::Create(value, CSSPrimitiveValue::UnitType::kUserUnits);
}
void SVGLength::SetValue(float value, const SVGLengthContext& context) {
value_ = CSSPrimitiveValue::Create(
context.ConvertValueFromUserUnits(value, UnitMode(),
value_->TypeWithCalcResolved()),
value_->TypeWithCalcResolved());
}
static bool IsCalcCSSUnitType(CSSPrimitiveValue::UnitType type) {
return type >= CSSPrimitiveValue::UnitType::kCalc &&
type <=
CSSPrimitiveValue::UnitType::kCalcPercentageWithLengthAndNumber;
}
static bool IsSupportedCSSUnitType(CSSPrimitiveValue::UnitType type) {
return (CSSPrimitiveValue::IsLength(type) ||
type == CSSPrimitiveValue::UnitType::kNumber ||
type == CSSPrimitiveValue::UnitType::kPercentage ||
IsCalcCSSUnitType(type)) &&
type != CSSPrimitiveValue::UnitType::kQuirkyEms;
}
void SVGLength::SetUnitType(CSSPrimitiveValue::UnitType type) {
DCHECK(IsSupportedCSSUnitType(type));
value_ = CSSPrimitiveValue::Create(value_->GetFloatValue(), type);
}
float SVGLength::ValueAsPercentage() const {
// LengthTypePercentage is represented with 100% = 100.0. Good for accuracy
// but could eventually be changed.
if (value_->IsPercentage()) {
// Note: This division is a source of floating point inaccuracy.
return value_->GetFloatValue() / 100;
}
return value_->GetFloatValue();
}
float SVGLength::ValueAsPercentage100() const {
// LengthTypePercentage is represented with 100% = 100.0. Good for accuracy
// but could eventually be changed.
if (value_->IsPercentage())
return value_->GetFloatValue();
return value_->GetFloatValue() * 100;
}
float SVGLength::ScaleByPercentage(float input) const {
float result = input * value_->GetFloatValue();
if (value_->IsPercentage()) {
// Delaying division by 100 as long as possible since it introduces floating
// point errors.
result = result / 100;
}
return result;
}
SVGParsingError SVGLength::SetValueAsString(const String& string) {
// TODO(fs): Preferably we wouldn't need to special-case the null
// string (which we'll get for example for removeAttribute.)
// Hopefully work on crbug.com/225807 can help here.
if (string.IsNull()) {
value_ =
CSSPrimitiveValue::Create(0, CSSPrimitiveValue::UnitType::kUserUnits);
return SVGParseStatus::kNoError;
}
// NOTE(ikilpatrick): We will always parse svg lengths in the insecure
// context mode. If a function/unit/etc will require a secure context check
// in the future, plumbing will need to be added.
CSSParserContext* svg_parser_context = CSSParserContext::Create(
kSVGAttributeMode, SecureContextMode::kInsecureContext);
const CSSValue* parsed =
CSSParser::ParseSingleValue(CSSPropertyX, string, svg_parser_context);
if (!parsed || !parsed->IsPrimitiveValue())
return SVGParseStatus::kExpectedLength;
const CSSPrimitiveValue* new_value = ToCSSPrimitiveValue(parsed);
if (!IsSupportedCSSUnitType(new_value->TypeWithCalcResolved()))
return SVGParseStatus::kExpectedLength;
value_ = new_value;
return SVGParseStatus::kNoError;
}
String SVGLength::ValueAsString() const {
return value_->CustomCSSText();
}
void SVGLength::NewValueSpecifiedUnits(CSSPrimitiveValue::UnitType type,
float value) {
value_ = CSSPrimitiveValue::Create(value, type);
}
void SVGLength::ConvertToSpecifiedUnits(CSSPrimitiveValue::UnitType type,
const SVGLengthContext& context) {
DCHECK(IsSupportedCSSUnitType(type));
float value_in_user_units = Value(context);
value_ = CSSPrimitiveValue::Create(
context.ConvertValueFromUserUnits(value_in_user_units, UnitMode(), type),
type);
}
SVGLengthMode SVGLength::LengthModeForAnimatedLengthAttribute(
const QualifiedName& attr_name) {
typedef HashMap<QualifiedName, SVGLengthMode> LengthModeForLengthAttributeMap;
DEFINE_STATIC_LOCAL(LengthModeForLengthAttributeMap, length_mode_map, ());
if (length_mode_map.IsEmpty()) {
length_mode_map.Set(SVGNames::xAttr, SVGLengthMode::kWidth);
length_mode_map.Set(SVGNames::yAttr, SVGLengthMode::kHeight);
length_mode_map.Set(SVGNames::cxAttr, SVGLengthMode::kWidth);
length_mode_map.Set(SVGNames::cyAttr, SVGLengthMode::kHeight);
length_mode_map.Set(SVGNames::dxAttr, SVGLengthMode::kWidth);
length_mode_map.Set(SVGNames::dyAttr, SVGLengthMode::kHeight);
length_mode_map.Set(SVGNames::frAttr, SVGLengthMode::kOther);
length_mode_map.Set(SVGNames::fxAttr, SVGLengthMode::kWidth);
length_mode_map.Set(SVGNames::fyAttr, SVGLengthMode::kHeight);
length_mode_map.Set(SVGNames::rAttr, SVGLengthMode::kOther);
length_mode_map.Set(SVGNames::rxAttr, SVGLengthMode::kWidth);
length_mode_map.Set(SVGNames::ryAttr, SVGLengthMode::kHeight);
length_mode_map.Set(SVGNames::widthAttr, SVGLengthMode::kWidth);
length_mode_map.Set(SVGNames::heightAttr, SVGLengthMode::kHeight);
length_mode_map.Set(SVGNames::x1Attr, SVGLengthMode::kWidth);
length_mode_map.Set(SVGNames::x2Attr, SVGLengthMode::kWidth);
length_mode_map.Set(SVGNames::y1Attr, SVGLengthMode::kHeight);
length_mode_map.Set(SVGNames::y2Attr, SVGLengthMode::kHeight);
length_mode_map.Set(SVGNames::refXAttr, SVGLengthMode::kWidth);
length_mode_map.Set(SVGNames::refYAttr, SVGLengthMode::kHeight);
length_mode_map.Set(SVGNames::markerWidthAttr, SVGLengthMode::kWidth);
length_mode_map.Set(SVGNames::markerHeightAttr, SVGLengthMode::kHeight);
length_mode_map.Set(SVGNames::textLengthAttr, SVGLengthMode::kWidth);
length_mode_map.Set(SVGNames::startOffsetAttr, SVGLengthMode::kWidth);
}
if (length_mode_map.Contains(attr_name))
return length_mode_map.at(attr_name);
return SVGLengthMode::kOther;
}
bool SVGLength::NegativeValuesForbiddenForAnimatedLengthAttribute(
const QualifiedName& attr_name) {
DEFINE_STATIC_LOCAL(
HashSet<QualifiedName>, no_negative_values_set,
({
SVGNames::frAttr, SVGNames::rAttr, SVGNames::rxAttr, SVGNames::ryAttr,
SVGNames::widthAttr, SVGNames::heightAttr, SVGNames::markerWidthAttr,
SVGNames::markerHeightAttr, SVGNames::textLengthAttr,
}));
return no_negative_values_set.Contains(attr_name);
}
void SVGLength::Add(SVGPropertyBase* other, SVGElement* context_element) {
SVGLengthContext length_context(context_element);
SetValue(Value(length_context) + ToSVGLength(other)->Value(length_context),
length_context);
}
void SVGLength::CalculateAnimatedValue(
SVGAnimationElement* animation_element,
float percentage,
unsigned repeat_count,
SVGPropertyBase* from_value,
SVGPropertyBase* to_value,
SVGPropertyBase* to_at_end_of_duration_value,
SVGElement* context_element) {
SVGLength* from_length = ToSVGLength(from_value);
SVGLength* to_length = ToSVGLength(to_value);
SVGLength* to_at_end_of_duration_length =
ToSVGLength(to_at_end_of_duration_value);
SVGLengthContext length_context(context_element);
float animated_number = Value(length_context);
animation_element->AnimateAdditiveNumber(
percentage, repeat_count, from_length->Value(length_context),
to_length->Value(length_context),
to_at_end_of_duration_length->Value(length_context), animated_number);
DCHECK_EQ(UnitMode(), LengthModeForAnimatedLengthAttribute(
animation_element->AttributeName()));
// TODO(shanmuga.m): Construct a calc() expression if the units fall in
// different categories.
CSSPrimitiveValue::UnitType new_unit =
CSSPrimitiveValue::UnitType::kUserUnits;
if (percentage < 0.5) {
if (!from_length->IsCalculated())
new_unit = from_length->TypeWithCalcResolved();
} else {
if (!to_length->IsCalculated())
new_unit = to_length->TypeWithCalcResolved();
}
animated_number = length_context.ConvertValueFromUserUnits(
animated_number, UnitMode(), new_unit);
value_ = CSSPrimitiveValue::Create(animated_number, new_unit);
}
float SVGLength::CalculateDistance(SVGPropertyBase* to_value,
SVGElement* context_element) {
SVGLengthContext length_context(context_element);
SVGLength* to_length = ToSVGLength(to_value);
return fabsf(to_length->Value(length_context) - Value(length_context));
}
} // namespace blink