composer package updates
[openemr.git] / vendor / phenx / php-svg-lib / src / Svg / Document.php
blob4ecdc761af4bb332343b04416538ef16e6d798f3
1 <?php
2 /**
3 * @package php-svg-lib
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
7 */
9 namespace Svg;
11 use Svg\Surface\SurfaceInterface;
12 use Svg\Tag\AbstractTag;
13 use Svg\Tag\Anchor;
14 use Svg\Tag\Circle;
15 use Svg\Tag\Ellipse;
16 use Svg\Tag\Group;
17 use Svg\Tag\ClipPath;
18 use Svg\Tag\Image;
19 use Svg\Tag\Line;
20 use Svg\Tag\LinearGradient;
21 use Svg\Tag\Path;
22 use Svg\Tag\Polygon;
23 use Svg\Tag\Polyline;
24 use Svg\Tag\Rect;
25 use Svg\Tag\Stop;
26 use Svg\Tag\Text;
27 use Svg\Tag\StyleTag;
28 use Svg\Tag\UseTag;
30 class Document extends AbstractTag
32 protected $filename;
33 public $inDefs = false;
35 protected $x;
36 protected $y;
37 protected $width;
38 protected $height;
40 protected $subPathInit;
41 protected $pathBBox;
42 protected $viewBox;
44 /** @var resource */
45 protected $parser;
47 /** @var SurfaceInterface */
48 protected $surface;
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(
68 $parser,
69 array($this, "_tagStart"),
70 array($this, "_tagEnd")
72 xml_set_character_data_handler(
73 $parser,
74 array($this, "_charData")
77 return $this->parser = $parser;
80 public function __construct() {
84 /**
85 * @return SurfaceInterface
87 public function getSurface()
89 return $this->surface;
92 public function getStack()
94 return $this->stack;
97 public function getWidth()
99 return $this->width;
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(
113 $parser,
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) {
129 break;
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];
156 if (!$this->width) {
157 $this->width = $viewBox[2];
159 if (!$this->height) {
160 $this->height = $viewBox[3];
166 return array(
167 0 => $this->width,
168 1 => $this->height,
170 "width" => $this->width,
171 "height" => $this->height,
175 public function getDocument(){
176 return $this;
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)
246 $this->x = 0;
247 $this->y = 0;
249 $tag = null;
251 $attributes = array_change_key_case($attributes, CASE_LOWER);
253 switch (strtolower($name)) {
254 case 'defs':
255 $this->inDefs = true;
256 return;
258 case 'svg':
259 if (count($this->attributes)) {
260 $tag = new Group($this, $name);
262 else {
263 $tag = $this;
264 $this->svgOffset($attributes);
266 break;
268 case 'path':
269 $tag = new Path($this, $name);
270 break;
272 case 'rect':
273 $tag = new Rect($this, $name);
274 break;
276 case 'circle':
277 $tag = new Circle($this, $name);
278 break;
280 case 'ellipse':
281 $tag = new Ellipse($this, $name);
282 break;
284 case 'image':
285 $tag = new Image($this, $name);
286 break;
288 case 'line':
289 $tag = new Line($this, $name);
290 break;
292 case 'polyline':
293 $tag = new Polyline($this, $name);
294 break;
296 case 'polygon':
297 $tag = new Polygon($this, $name);
298 break;
300 case 'lineargradient':
301 $tag = new LinearGradient($this, $name);
302 break;
304 case 'radialgradient':
305 $tag = new LinearGradient($this, $name);
306 break;
308 case 'stop':
309 $tag = new Stop($this, $name);
310 break;
312 case 'style':
313 $tag = new StyleTag($this, $name);
314 break;
316 case 'a':
317 $tag = new Anchor($this, $name);
318 break;
320 case 'g':
321 case 'symbol':
322 $tag = new Group($this, $name);
323 break;
325 case 'clippath':
326 $tag = new ClipPath($this, $name);
327 break;
329 case 'use':
330 $tag = new UseTag($this, $name);
331 break;
333 case 'text':
334 $tag = new Text($this, $name);
335 break;
337 case 'desc':
338 return;
341 if ($tag) {
342 if (isset($attributes["id"])) {
343 $this->defs[$attributes["id"]] = $tag;
345 else {
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 */
371 $tag = null;
372 switch (strtolower($name)) {
373 case 'defs':
374 $this->inDefs = false;
375 return;
377 case 'svg':
378 case 'path':
379 case 'rect':
380 case 'circle':
381 case 'ellipse':
382 case 'image':
383 case 'line':
384 case 'polyline':
385 case 'polygon':
386 case 'radialgradient':
387 case 'lineargradient':
388 case 'stop':
389 case 'style':
390 case 'text':
391 case 'g':
392 case 'symbol':
393 case 'clippath':
394 case 'use':
395 case 'a':
396 $tag = array_pop($this->stack);
397 break;
400 if (!$this->inDefs && $tag) {
401 $tag->handleEnd();