-
-
Notifications
You must be signed in to change notification settings - Fork 88
/
SVGNode.php
275 lines (242 loc) · 7.77 KB
/
SVGNode.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
<?php
namespace SVG\Nodes;
use SVG\Rasterization\SVGRasterizer;
use SVG\Reading\SVGAttrParser;
/**
* Represents a single element inside an SVG image (in other words, an XML tag).
* It stores hierarchy info, as well as attributes and styles.
*/
abstract class SVGNode
{
/** @var SVGNodeContainer $parent The parent node. */
protected $parent;
/** @var string[] $styles This node's set of explicit style declarations. */
protected $styles;
/** @var string[] $attributes This node's set of attributes. */
protected $attributes;
public function __construct()
{
$this->styles = array();
$this->attributes = array();
}
/**
* Factory function for this class, which accepts an associative array of
* strings instead of parameters in the correct order (like `__construct`).
*
* By default, simply invokes the constructor with no arguments. Subclasses
* may choose to override this if they require special behavior.
*
* @param string[] $attrs The attribute array (or array-like object; e.g. \SimpleXMLElement).
*
* @return static A new instance of the class this was called on.
*
* @SuppressWarnings("unused")
*/
public static function constructFromAttributes($attrs)
{
return new static();
}
/**
* @return string This node's tag name (e.g. 'rect' or 'g').
*/
public function getName()
{
return static::TAG_NAME;
}
/**
* @return SVGNodeContainer|null This node's parent node, if not root.
*/
public function getParent()
{
return $this->parent;
}
/**
* Obtains the style with the given name as specified on this node.
*
* @param string $name The name of the style to get.
*
* @return string|null The style value if specified on this node, else null.
*/
public function getStyle($name)
{
return isset($this->styles[$name]) ? $this->styles[$name] : null;
}
/**
* Defines a style on this node. A value of null or the empty string will
* unset the property.
*
* @param string $name The name of the style to set.
* @param string|null $value The new style value.
*
* @return $this This node instance, for call chaining.
*/
public function setStyle($name, $value)
{
$value = (string) $value;
if (strlen($value) === 0) {
unset($this->styles[$name]);
return $this;
}
$this->styles[$name] = $value;
return $this;
}
/**
* Removes a style from this node's set of styles.
*
* @param string $name The name of the style to remove.
*
* @return $this This node instance, for call chaining.
*/
public function removeStyle($name)
{
unset($this->styles[$name]);
return $this;
}
/**
* Obtains the computed style with the given name. The 'computed style' is
* the one in effect; taking inheritance and default styles into account.
*
* @param string $name The name of the style to compute.
*
* @return string|null The style value if specified anywhere, else null.
*/
public function getComputedStyle($name)
{
$style = $this->getStyle($name);
// If no immediate style then get style from container/global style rules
if ($style === null && isset($this->parent)) {
$containerStyles = $this->parent->getContainerStyleForNode($this);
$style = isset($containerStyles[$name]) ? $containerStyles[$name] : null;
}
// If still no style then get parent's style
if (($style === null || $style === 'inherit') && isset($this->parent)) {
return $this->parent->getComputedStyle($name);
}
// 'inherit' is not what we want. Either get the real style, or
// nothing at all.
return $style !== 'inherit' ? $style : null;
}
/**
* Obtains the attribute with the given name as specified on this node.
* For style attributes, use `getStyle($name)` instead.
*
* @param string $name The name of the attribute to get.
*
* @return string|null The attribute's value, or null.
*/
public function getAttribute($name)
{
return isset($this->attributes[$name]) ? $this->attributes[$name] : null;
}
/**
* Defines an attribute on this node. A value of null will unset the
* attribute. Note that the empty string is perfectly valid.
*
* @param string $name The name of the attribute to set.
* @param string|null $value The new attribute value.
*
* @return $this This node instance, for call chaining.
*/
public function setAttribute($name, $value)
{
if (!isset($value)) {
unset($this->attributes[$name]);
return $this;
}
$this->attributes[$name] = (string) $value;
return $this;
}
/**
* Defines an attribute, if and only if a non-null value is given; otherwise
* behaves like `setAttribute(...)`.
*
* This is useful for initializing attributes in constructors.
*
* @param string $name The name of the attribute to set.
* @param string|null $value The new attribute value.
*
* @return $this This node instance, for call chaining.
*/
protected function setAttributeOptional($name, $value = null)
{
if (!isset($value)) {
return;
}
return $this->setAttribute($name, $value);
}
/**
* Removes an attribute from this node's set of attributes.
*
* @param string $name The name of the attribute to remove.
*
* @return $this This node instance, for call chaining.
*/
public function removeAttribute($name)
{
unset($this->attributes[$name]);
return $this;
}
/**
* Constructs a set of attributes that shall be included in generated XML.
*
* Subclasses MUST override this and include their own properties, if they
* don't already use SVGNode's attribute set for storing them.
*
* @return string[] The set of attributes to include in generated XML.
*/
public function getSerializableAttributes()
{
return $this->attributes;
}
/**
* Constructs a set of styles that shall be included in generated XML.
*
* Subclasses MAY override this to augment or limit the styles returned
* (in the case of SVG default values, for example).
*
* @return string[] The set of styles to include in generated XML.
*/
public function getSerializableStyles()
{
return $this->styles;
}
/**
* Constructs a regex pattern to use as the key to retrieve styles for this
* node from its container.
*
* @return string|null The generated pattern.
*/
public function getIdAndClassPattern()
{
$id = $this->getAttribute('id');
$class = $this->getAttribute('class');
$pattern = '';
if (!empty($id)) {
$pattern = '#'.$id.'|#'.$id;
}
if (!empty($class)) {
if (!empty($pattern)) {
$pattern .= '.'.$class.'|';
}
$pattern .= '.'.$class;
}
return empty($pattern) ? null : '/('.$pattern.')/';
}
/**
* Returns the viewBox array (x, y, width, height) for the current node.
*
* @return float[]|null The viewbox array.
*/
public function getViewBox()
{
return SVGAttrParser::parseViewBox($this->getAttribute('viewBox'));
}
/**
* Draws this node to the given rasterizer.
*
* @param SVGRasterizer $rasterizer The rasterizer to draw to.
*
* @return void
*/
abstract public function rasterize(SVGRasterizer $rasterizer);
}