createDocumentType( 'svg', '-//W3C//DTD SVG 1.1//EN', 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd' ); $this->document = $implementation->createDocument(null, '', $docType); $this->svg = $this->createOuterBox(); $this->document->appendChild($this->svg); } /** * Create the outer SVG box containing the root svg element and namespace and return it * * @return DOMElement The SVG root node */ private function createOuterBox() { $ctx = $this->createRenderContext(); $svg = $this->document->createElement('svg'); $svg->setAttribute('xmlns', 'http://www.w3.org/2000/svg'); $svg->setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink'); $svg->setAttribute('role', $this->ariaRole); $svg->setAttribute('width', '100%'); $svg->setAttribute('height', '100%'); $svg->setAttribute( 'viewBox', sprintf( '0 0 %s %s', $ctx->getNrOfUnitsX(), $ctx->getNrOfUnitsY() ) ); if ($this->preserveAspectRatio) { $svg->setAttribute( 'preserveAspectRatio', sprintf( '%s%s %s', $this->xAspectRatio, $this->yAspectRatio, $this->xFillMode ) ); } return $svg; } /** * Add aria title and description * * Adds an aria title and desc element to the given SVG node, which are used to describe this SVG by accessibility * tools such as screen readers. * * @param DOMNode $svg The SVG DOMNode to which the aria attributes should be attached * @param $title The title text * @param $description The description text */ private function addAriaDescription(DOMNode $svg, $titleText, $descriptionText) { $doc = $svg->ownerDocument; $titleId = $descId = ''; if (isset($this->ariaTitle)) { $titleId = 'aria-title-' . $this->stripNonAlphanumeric($titleText); $title = $doc->createElement('title'); $title->setAttribute('id', $titleId); $title->appendChild($doc->createTextNode($titleText)); $svg->appendChild($title); } if (isset($this->ariaDescription)) { $descId = 'aria-desc-' . $this->stripNonAlphanumeric($descriptionText); $desc = $doc->createElement('desc'); $desc->setAttribute('id', $descId); $desc->appendChild($doc->createTextNode($descriptionText)); $svg->appendChild($desc); } $svg->setAttribute('aria-labelledby', join(' ', array($titleId, $descId))); } /** * Initialises the XML-document, SVG-element and this figure's root canvas * * @param int $width The width ratio * @param int $height The height ratio */ public function __construct($width, $height) { $this->width = $width; $this->height = $height; $this->rootCanvas = new Canvas('root', new LayoutBox(0, 0)); } /** * Render the SVG-document * * @return string The resulting XML structure */ public function render() { $this->createRootDocument(); $ctx = $this->createRenderContext(); $this->addAriaDescription($this->svg, $this->ariaTitle, $this->ariaDescription); $this->svg->appendChild($this->rootCanvas->toSvg($ctx)); $this->document->formatOutput = true; $this->document->encoding = 'UTF-8'; return $this->document->saveXML(); } /** * Create a render context that will be used for rendering elements * * @return RenderContext The created RenderContext instance */ public function createRenderContext() { return new RenderContext($this->document, $this->width, $this->height); } /** * Return the root canvas of this rendered * * @return Canvas The canvas that will be the uppermost element in this figure */ public function getCanvas() { return $this->rootCanvas; } /** * Preserve the aspect ratio of the rendered object * * Do not deform the content of the SVG when the aspect ratio of the viewBox * differs from the aspect ratio of the SVG element, but add padding or cutoff * instead * * @param bool $preserve Whether the aspect ratio should be preserved */ public function preserveAspectRatio($preserve = true) { $this->preserveAspectRatio = $preserve; } /** * Change the horizontal alignment of the SVG element * * Change the horizontal alignment of the svg, when preserveAspectRatio is used and * padding is present. Defaults to */ public function setXAspectRatioAlignment($alignment) { $this->xAspectRatio = $alignment; } /** * Change the vertical alignment of the SVG element * * Change the vertical alignment of the svg, when preserveAspectRatio is used and * padding is present. */ public function setYAspectRatioAlignment($alignment) { $this->yAspectRatio = $alignment; } /** * Set the aria description, that is used as a title for this SVG in screen readers * * @param $text */ public function setAriaTitle($text) { $this->ariaTitle = $text; } /** * Set the aria description, that is used to describe this SVG in screen readers * * @param $text */ public function setAriaDescription($text) { $this->ariaDescription = $text; } /** * Set the aria role, that is used to describe the purpose of this SVG in screen readers * * @param $text */ public function setAriaRole($text) { $this->ariaRole = $text; } private function stripNonAlphanumeric($str) { return preg_replace('/[^A-Za-z]+/', '', $str); } }