add(... $properties); $this->add($properties); $this->name = $name; $this->close = $close; } public function append(XML_Node $child) { $this->children[] = $child; } //public function add(... $properties) { public function add($properties) { foreach($properties as $arr) $this->properties[] = $arr; } /* convert properties to a string, allow "late-binding", or when one wants * to set a property after initially setting it to another value, a good * example of this is a programmer "building" a wall and not knowing * beforehand what the final size of the wall is, part of the program * might calculate how tall something is based on how many bricks the * programmer used. This is sub-optimal, but at the moment I am not sure * how many bricks are needed, so unless someone can say this glut is going * to stay. * * -- better code would just build this string once rather than every * time the Class is coerced */ private function propertyStr() { $str = array(); foreach($this->properties as $arr) { // convert properties with values into HTML if(is_array($arr)) { // preserve native PHP datatype quotes, this way "1.0" is a string if("string" === gettype($arr[1]) || is_object($arr[1])) { $arr[1] = '"' . htmlentities($arr[1]) . '"'; } else if(is_object($arr[1])) {} $arr = "{$arr[0]}={$arr[1]}"; } else { /* just leave property alone */ } $str[] = $arr; } return (0name}{$this->propertyStr()}>"); foreach($this->children as $v) $arr[] = "\t" . strval($v); // either close comes at the end or is just a single node with terminating '/>' if($this->close) $arr[] = "name}>"; else $arr[0] = substr($arr[0], 0, -1) . " />"; return implode(PHP_EOL, $arr); } static public function stanza($version = "1.0") { $s = new XML_Node("?xml", false, array( array("version", $version), array("standalone", "no") )); // XML requires '?' instead of '/' in '/>' tag terminator return substr($s, 0, -2) . '?>'; } } Class Printable { public $value; public function __construct($v = 0) { $this->value = $v; } public function __tostring() { return strval($this->value); } } Class Coordinate { public $x; public $y; public function __construct($x = 0, $y = 0) { $this->x = $x; $this->y = $y; } } Class Coord extends Coordinate { /* alias */ } Class Paint { public $xml = array(); private $svg; private $height; private $width; public function __construct() { $this->xml[] = XML_Node::stanza(); // make references of the height and width properties for the SVG node $this->height = new Printable(); $this->width = new Printable(); $this->svg = new XML_Node("svg", true, array( // height and width as references array("width", /* needs to be ref */ $this->width), array("height", /* needs to be ref */ $this->height), array("version", "1.1"), array("xmlns", "http://www.w3.org/2000/svg"), )); // store a reference to the SVG, reference updates are tracked $this->xml[] = $this->svg; } private function expandCanvas($h = 0, $w = 0) { $this->height->value = max($this->height->value, $h); $this->width->value = max($this->width->value, $w); } public function addCircle(Coord $origin = NULL, Pixel $fill = NULL, $radius = 20) { if(NULL === $origin) $origin = new Coord(); if(NULL === $fill) $fill = new Pixel(/*red*/ 255,0,0, 0.5); $y = $x = $radius; $circle = new XML_Node("circle", false, array( array("cx", strval($x + $origin->x)), array("cy", strval($y + $origin->y)), array("r", strval($radius)), array("fill", "#" . strval($fill)) )); $this->svg->append($circle); // height needs to be radius * 2, assuming radius includes stroke $this->expandCanvas( /*height*/ $origin->y + ($y * 2), /*width*/ $origin->x + ($x * 2) ); } public function __tostring() { $s = ""; foreach($this->xml as $n) $s .= strval($n) . PHP_EOL; return $s; } } /* header("Content-Type: application: xhtml+xml"); $test = new Paint(); $test->addCircle(new Coord(0,0)); $test->addCircle(new Coord(40,0), new Pixel(255,255,0)); echo $test; */