4 * @link http://github.com/PhenX/php-svg-lib
5 * @author Fabien Ménager <fabien.menager@gmail.com>
6 * @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html
11 use Svg\Surface\SurfaceInterface
;
12 use Svg\Tag\AbstractTag
;
20 use Svg\Tag\LinearGradient
;
30 class Document
extends AbstractTag
33 public $inDefs = false;
40 protected $subPathInit;
47 /** @var SurfaceInterface */
50 /** @var AbstractTag[] */
51 protected $stack = array();
53 /** @var AbstractTag[] */
54 protected $defs = array();
56 /** @var \Sabberworm\CSS\CSSList\Document[] */
57 protected $styleSheets = array();
59 public function loadFile($filename)
61 $this->filename
= $filename;
64 protected function initParser() {
65 $parser = xml_parser_create("utf-8");
66 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING
, false);
67 xml_set_element_handler(
69 array($this, "_tagStart"),
70 array($this, "_tagEnd")
72 xml_set_character_data_handler(
74 array($this, "_charData")
77 return $this->parser
= $parser;
80 public function __construct() {
85 * @return SurfaceInterface
87 public function getSurface()
89 return $this->surface
;
92 public function getStack()
97 public function getWidth()
102 public function getHeight()
104 return $this->height
;
107 public function getDimensions() {
108 $rootAttributes = null;
110 $parser = xml_parser_create("utf-8");
111 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING
, false);
112 xml_set_element_handler(
114 function ($parser, $name, $attributes) use (&$rootAttributes) {
115 if ($name === "svg" && $rootAttributes === null) {
116 $attributes = array_change_key_case($attributes, CASE_LOWER
);
118 $rootAttributes = $attributes;
121 function ($parser, $name) {}
124 $fp = fopen($this->filename
, "r");
125 while ($line = fread($fp, 8192)) {
126 xml_parse($parser, $line, false);
128 if ($rootAttributes !== null) {
133 xml_parser_free($parser);
135 return $this->handleSizeAttributes($rootAttributes);
138 public function handleSizeAttributes($attributes){
139 if ($this->width
=== null) {
140 if (isset($attributes["width"])) {
141 $width = Style
::convertSize($attributes["width"], 400);
142 $this->width
= $width;
145 if (isset($attributes["height"])) {
146 $height = Style
::convertSize($attributes["height"], 300);
147 $this->height
= $height;
150 if (isset($attributes['viewbox'])) {
151 $viewBox = preg_split('/[\s,]+/is', trim($attributes['viewbox']));
152 if (count($viewBox) == 4) {
153 $this->x
= $viewBox[0];
154 $this->y
= $viewBox[1];
157 $this->width
= $viewBox[2];
159 if (!$this->height
) {
160 $this->height
= $viewBox[3];
170 "width" => $this->width
,
171 "height" => $this->height
,
175 public function getDocument(){
180 * Append a style sheet
182 * @param \Sabberworm\CSS\CSSList\Document $stylesheet
184 public function appendStyleSheet($stylesheet) {
185 $this->styleSheets
[] = $stylesheet;
189 * Get the document style sheets
191 * @return \Sabberworm\CSS\CSSList\Document[]
193 public function getStyleSheets() {
194 return $this->styleSheets
;
197 protected function before($attributes)
199 $surface = $this->getSurface();
201 $style = new DefaultStyle();
202 $style->inherit($this);
203 $style->fromAttributes($attributes);
205 $this->setStyle($style);
207 $surface->setStyle($style);
210 public function render(SurfaceInterface
$surface)
212 $this->inDefs
= false;
213 $this->surface
= $surface;
215 $parser = $this->initParser();
217 if ($this->x ||
$this->y
) {
218 $surface->translate(-$this->x
, -$this->y
);
221 $fp = fopen($this->filename
, "r");
222 while ($line = fread($fp, 8192)) {
223 xml_parse($parser, $line, false);
226 xml_parse($parser, "", true);
228 xml_parser_free($parser);
231 protected function svgOffset($attributes)
233 $this->attributes
= $attributes;
235 $this->handleSizeAttributes($attributes);
238 public function getDef($id) {
239 $id = ltrim($id, "#");
241 return isset($this->defs
[$id]) ?
$this->defs
[$id] : null;
244 private function _tagStart($parser, $name, $attributes)
251 $attributes = array_change_key_case($attributes, CASE_LOWER
);
253 switch (strtolower($name)) {
255 $this->inDefs
= true;
259 if (count($this->attributes
)) {
260 $tag = new Group($this, $name);
264 $this->svgOffset($attributes);
269 $tag = new Path($this, $name);
273 $tag = new Rect($this, $name);
277 $tag = new Circle($this, $name);
281 $tag = new Ellipse($this, $name);
285 $tag = new Image($this, $name);
289 $tag = new Line($this, $name);
293 $tag = new Polyline($this, $name);
297 $tag = new Polygon($this, $name);
300 case 'lineargradient':
301 $tag = new LinearGradient($this, $name);
304 case 'radialgradient':
305 $tag = new LinearGradient($this, $name);
309 $tag = new Stop($this, $name);
313 $tag = new StyleTag($this, $name);
317 $tag = new Anchor($this, $name);
322 $tag = new Group($this, $name);
326 $tag = new ClipPath($this, $name);
330 $tag = new UseTag($this, $name);
334 $tag = new Text($this, $name);
342 if (isset($attributes["id"])) {
343 $this->defs
[$attributes["id"]] = $tag;
346 /** @var AbstractTag $top */
347 $top = end($this->stack
);
348 if ($top && $top != $tag) {
349 $top->children
[] = $tag;
353 $this->stack
[] = $tag;
355 $tag->handle($attributes);
359 function _charData($parser, $data)
361 $stack_top = end($this->stack
);
363 if ($stack_top instanceof Text ||
$stack_top instanceof StyleTag
) {
364 $stack_top->appendText($data);
368 function _tagEnd($parser, $name)
370 /** @var AbstractTag $tag */
372 switch (strtolower($name)) {
374 $this->inDefs
= false;
386 case 'radialgradient':
387 case 'lineargradient':
396 $tag = array_pop($this->stack
);
400 if (!$this->inDefs
&& $tag) {