1/*
2 * Copyright (C) 2004, 2005, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005, 2006, 2007, 2010 Rob Buis <buis@kde.org>
4 * Copyright (C) 2018 Apple Inc. All rights reserved.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22#include "config.h"
23#include "SVGFitToViewBox.h"
24
25#include "AffineTransform.h"
26#include "Document.h"
27#include "FloatRect.h"
28#include "SVGDocumentExtensions.h"
29#include "SVGElement.h"
30#include "SVGNames.h"
31#include "SVGParserUtilities.h"
32#include "SVGPreserveAspectRatioValue.h"
33#include <wtf/text/StringView.h>
34
35namespace WebCore {
36
37SVGFitToViewBox::SVGFitToViewBox(SVGElement* contextElement, SVGPropertyAccess access)
38 : m_viewBox(SVGAnimatedRect::create(contextElement, access))
39 , m_preserveAspectRatio(SVGAnimatedPreserveAspectRatio::create(contextElement, access))
40{
41 static std::once_flag onceFlag;
42 std::call_once(onceFlag, [] {
43 PropertyRegistry::registerProperty<SVGNames::viewBoxAttr, &SVGFitToViewBox::m_viewBox>();
44 PropertyRegistry::registerProperty<SVGNames::preserveAspectRatioAttr, &SVGFitToViewBox::m_preserveAspectRatio>();
45 });
46}
47
48void SVGFitToViewBox::setViewBox(const FloatRect& viewBox)
49{
50 m_viewBox->setBaseValInternal(viewBox);
51 m_isViewBoxValid = true;
52}
53
54void SVGFitToViewBox::resetViewBox()
55{
56 m_viewBox->setBaseValInternal({ });
57 m_isViewBoxValid = false;
58}
59
60void SVGFitToViewBox::reset()
61{
62 resetViewBox();
63 resetPreserveAspectRatio();
64}
65
66bool SVGFitToViewBox::parseAttribute(const QualifiedName& name, const AtomicString& value)
67{
68 if (name == SVGNames::viewBoxAttr) {
69 FloatRect viewBox;
70 if (!value.isNull() && parseViewBox(value, viewBox))
71 setViewBox(viewBox);
72 else
73 resetViewBox();
74 return true;
75 }
76
77 if (name == SVGNames::preserveAspectRatioAttr) {
78 SVGPreserveAspectRatioValue preserveAspectRatio;
79 preserveAspectRatio.parse(value);
80 setPreserveAspectRatio(preserveAspectRatio);
81 return true;
82 }
83
84 return false;
85}
86
87bool SVGFitToViewBox::parseViewBox(const AtomicString& value, FloatRect& viewBox)
88{
89 auto upconvertedCharacters = StringView(value).upconvertedCharacters();
90 const UChar* characters = upconvertedCharacters;
91 return parseViewBox(characters, characters + value.length(), viewBox);
92}
93
94bool SVGFitToViewBox::parseViewBox(const UChar*& c, const UChar* end, FloatRect& viewBox, bool validate)
95{
96 String str(c, end - c);
97
98 skipOptionalSVGSpaces(c, end);
99
100 float x = 0.0f;
101 float y = 0.0f;
102 float width = 0.0f;
103 float height = 0.0f;
104 bool valid = parseNumber(c, end, x) && parseNumber(c, end, y) && parseNumber(c, end, width) && parseNumber(c, end, height, false);
105
106 if (validate) {
107 Document& document = m_viewBox->contextElement()->document();
108
109 if (!valid) {
110 document.accessSVGExtensions().reportWarning("Problem parsing viewBox=\"" + str + "\"");
111 return false;
112 }
113
114 // Check that width is positive.
115 if (width < 0.0) {
116 document.accessSVGExtensions().reportError("A negative value for ViewBox width is not allowed");
117 return false;
118 }
119
120 // Check that height is positive.
121 if (height < 0.0) {
122 document.accessSVGExtensions().reportError("A negative value for ViewBox height is not allowed");
123 return false;
124 }
125
126 // Nothing should come after the last, fourth number.
127 skipOptionalSVGSpaces(c, end);
128 if (c < end) {
129 document.accessSVGExtensions().reportWarning("Problem parsing viewBox=\"" + str + "\"");
130 return false;
131 }
132 }
133
134 viewBox = { x, y, width, height };
135 return true;
136}
137
138AffineTransform SVGFitToViewBox::viewBoxToViewTransform(const FloatRect& viewBoxRect, const SVGPreserveAspectRatioValue& preserveAspectRatio, float viewWidth, float viewHeight)
139{
140 if (!viewBoxRect.width() || !viewBoxRect.height() || !viewWidth || !viewHeight)
141 return AffineTransform();
142
143 return preserveAspectRatio.getCTM(viewBoxRect.x(), viewBoxRect.y(), viewBoxRect.width(), viewBoxRect.height(), viewWidth, viewHeight);
144}
145
146}
147