Changeset 336

Show
Ignore:
Timestamp:
03/07/08 08:33:04 (4 years ago)
Author:
nperriault
Message:

Clever Svg:

  • refs #40: more elements tests
  • Removed depth management, as svg handle it natively

Warning: breaks BC from 0.5.

Location:
cleversvg/trunk
Files:
14 added
15 modified

Legend:

Unmodified
Added
Removed
  • cleversvg/trunk/CHANGELOG

    r327 r336  
    11== Roadmap == 
     2 
    23=== version 0.6 === 
    34 * Write complete PHPUnit test suite 
     
    78 
    89== Changelog == 
     10 
     11=== 2008-03-XX - 0.6 === 
     12 * Complete PHPUnit test suite 
     13 * Refactorized containers classes (BC) 
     14 
    915=== 2008-02-26 - 0.5 === 
    1016 * Initial public release. Features: 
  • cleversvg/trunk/INSTALL

    r315 r336  
    33 
    44==== Installation ==== 
    5 The CleverSvg library is only available through svn export: 
     5The CleverSvg library is only available through svn export at this time: 
    66{{{ 
    7 $ svn export http://www.clever-age.org/svn/cleversvg/trunk/ cleversvg  
     7$ svn export http://www.clever-age.org/svn/cleversvg/trunk/ cleversvg 
    88}}} 
    99 
  • cleversvg/trunk/base/csBaseElement.class.php

    r335 r336  
    7676              } 
    7777            } 
    78             $element_node->setAttribute('style', implode('; ', $styles)); 
     78            if (count($styles) > 0) 
     79            { 
     80              $element_node->setAttribute('style', implode('; ', $styles)); 
     81            } 
    7982          break; 
    8083          default: 
     
    260263    if (!is_null($value)) 
    261264    { 
    262       $this->attributes[$name] = $value; 
     265      $this->attributes[strtolower($name)] = $value; 
    263266    } 
    264267  } 
     
    271274  public function setDepth($depth) 
    272275  { 
    273     if (!is_null($depth) && is_int($depth)) 
     276    if (is_null($depth)) 
     277    { 
     278      unset($this->attributes['style']['z-index']); 
     279    } 
     280    elseif (is_int($depth)) 
    274281    { 
    275282      $this->attributes['style']['z-index'] = (string) $depth; 
  • cleversvg/trunk/cleversvg.php

    r318 r336  
    1 <?php 
    2 /** 
    3  * CleverSvg library 
    4  * Include this file to be able to use the library, eg. : 
    5  * <pre> 
    6  * require_once('cleversvg.php'); 
    7  * $doc = new csDocument(550, 400, 'Untitled Document'); 
    8  * $rect = new csRect(10, 10, 100, 100); 
    9  * $rect->setFill('red'); 
    10  * $doc->addElement($rect); 
    11  * header("content-type: image/svg+xml; charset=utf-8"); 
    12  * echo $doc->toXML(); 
    13  * </pre> 
    14  * 
    15  * @author  Nicolas Perriault <nperriault@gmail.com> 
    16  * @package cleversvg 
    17  */ 
    18  
     1<?php // CleverSvg framework class autoloading 
    192function __autoload($class) 
    203{ 
    21   $dirs = array('exceptions', 'base', 'gradients', 'elements', 'document'); 
     4  $dirs = array('exceptions', 
     5                'base', 
     6                'container', 
     7                'gradients', 
     8                'elements', 
     9                'importer', 
     10                'document', 
     11                'interfaces'); 
    2212  $pwd = dirname(__FILE__); 
    2313  foreach ($dirs as $dir) 
    2414  { 
    25     $class_file = sprintf('%s%s%s%s%s.class.php', 
    26                           $pwd, DIRECTORY_SEPARATOR, 
    27                           $dir, DIRECTORY_SEPARATOR, $class); 
    28     if (file_exists($class_file)) 
     15    foreach (array('class', 'interface') as $type) 
    2916    { 
    30       class_exists($class) or require($class_file); 
     17      $class_file = sprintf('%s%s%s%s%s.%s.php', 
     18                            $pwd, DIRECTORY_SEPARATOR, 
     19                            $dir, DIRECTORY_SEPARATOR, $class, 
     20                            $type); 
     21      if (file_exists($class_file)) 
     22      { 
     23        if ($type == 'interface') 
     24        { 
     25          interface_exists($class) or require($class_file); 
     26        } 
     27        else 
     28        { 
     29          class_exists($class) or require($class_file); 
     30        } 
     31      } 
    3132    } 
    3233  } 
    3334} 
     35 
     36/** 
     37 * CleverSvg default PHP error handler 
     38 * 
     39 * TODO: switch from error code to provide specific errors 
     40 * 
     41 * @param  int    $code 
     42 * @param  string $message 
     43 * @param  string $file 
     44 * @param  int    $line 
     45 * @throws csException 
     46 */ 
     47function cs_error_handler($code, $message, $file, $line) 
     48{ 
     49  switch ($code) 
     50  { 
     51    case E_USER_ERROR: 
     52      $type = 'Error'; 
     53      $exception = 'csException'; 
     54      break; 
     55    case E_USER_WARNING: 
     56      $type = 'Warning'; 
     57      $exception = 'csException'; 
     58      break; 
     59    case E_USER_NOTICE: 
     60      $type = 'Notice'; 
     61      $exception = 'csException'; 
     62      break; 
     63    default: 
     64      $type = 'Unknown error'; 
     65      $exception = 'csException'; 
     66      break; 
     67  } 
     68  $message = sprintf('%s "%s" in %s line %d', 
     69                     $type, $message, $file, $line); 
     70  throw new $exception($message, $code); 
     71} 
     72set_error_handler('cs_error_handler'); 
  • cleversvg/trunk/document/csDocument.class.php

    r335 r336  
    1717 
    1818  /** 
    19    * Definitions container 
    20    * @var array 
    21    */ 
    22   protected $definitions = array(); 
     19   * Definitions and elements container 
     20   * @var csElementsContainer 
     21   */ 
     22  protected $container; 
    2323 
    2424  /** 
     
    4141 
    4242  /** 
    43    * Used definitions references container 
    44    * @var array 
    45    */ 
    46   protected $used_definitions = array(); 
    47  
    48   /** 
    49    * Document elements container 
    50    * @var array 
    51    */ 
    52   protected $elements = array(); 
    53  
    54   /** 
    55    * DOM document unique id attributes container 
    56    * @var array 
    57    */ 
    58   protected $dom_ids = array(); 
    59  
    60   /** 
    6143   * Document description 
    6244   * @var string 
     
    8668   * @var boolean 
    8769   */ 
    88   protected $embedded = false; 
     70  public static $embedded = false; 
    8971 
    9072  /** 
     
    10789    $this->setHeight($height); 
    10890    $this->setTitle($title); 
     91    $this->container = new csElementsContainer(); 
    10992    $this->attributes = array_merge($attrs, $this->attributes); 
    11093    $this->initDefaults(); 
     
    11598   * 
    11699   * @param  mixed    $element  SVG element to add 
    117    * @param  mixed    $depth    Depth (default: auto when set to NULL) 
    118    * @param  boolean  $replace_at_depth Replace element at depth if exist ? 
    119100   * @throws csException 
    120101   */ 
    121   public function addElement($element, $depth = null, $replace_at_depth = false) 
    122   { 
    123     // Add DOM id to document list, if any 
    124     $id = $element->getId(); 
    125     if (!is_null($id)) 
    126     { 
    127       if (in_array($id, $this->dom_ids)) 
    128       { 
    129         throw new csException( 
    130           sprintf('Document has already an element with id "%s"', $id)); 
    131       } 
    132       $this->dom_ids[] = $id; 
    133     } 
    134  
    135     if (is_null($element->getDepth())) 
    136     { 
    137       if (!$replace_at_depth && array_key_exists($depth, $this->elements)) 
    138       { 
    139         throw new csException( 
    140           sprintf('Unable to add element at depth "%d", a "%s" element already'. 
    141                   ' exist at this index', 
    142                   $depth, $this->elements[$depth]->getElementName(false))); 
    143       } 
    144       elseif (!is_int($depth)) 
    145       { 
    146         $depth = ++$this->maxdepth; 
    147       } 
    148     } 
    149     else 
    150     { 
    151       $depth = $element->getDepth(); 
    152     } 
    153     $this->elements[$depth] = $element; 
    154   } 
    155  
    156   /** 
     102  public function addElement($element) 
     103  { 
     104    return $this->container->addElement($element); 
     105  } 
     106 
     107/** 
    157108   * Create an svg element and returns its instance 
    158109   * 
     
    162113  public function createElement($name) 
    163114  { 
    164     $class_name = sprintf('cs%s', ucfirst($name)); 
    165     if (!class_exists($class_name)) 
    166     { 
    167       throw new csException(sprintf('Element class "%s" does not exist', 
    168                                    $class_name)); 
    169     } 
    170     return new $class_name; 
     115    return $this->container->createElement($name); 
    171116  } 
    172117 
     
    179124  public function dropElementById($id) 
    180125  { 
    181     foreach ($this->getElements() as $index => $element) 
    182     { 
    183       if ($element->getId() == $id) 
    184       { 
    185         foreach ($this->dom_ids as $dom_index => $dom_id) 
    186         { 
    187           if ($dom_id == $id) 
    188           { 
    189             unset($this->dom_ids[$dom_index]); 
    190           } 
    191         } 
    192         unset($this->elements[$index]); 
    193         return; 
    194       } 
    195     } 
     126    return $this->container->dropElementById($id); 
    196127  } 
    197128 
     
    205136  public function getElementById($id) 
    206137  { 
    207     foreach ($this->elements as $element) 
    208     { 
    209       if ($element->getId() == $id) 
    210       { 
    211         return $element; 
    212       } 
    213     } 
    214     throw new csException(sprintf('No element with id "%s"', $id)); 
     138    return $this->container->getElementById($id); 
    215139  } 
    216140 
     
    222146  protected function getElements() 
    223147  { 
    224     return $this->elements; 
     148    return $this->container->getElements(); 
     149  } 
     150 
     151  /** 
     152   * Checks if SVG output should be in embed mode 
     153   * 
     154   * @return boolean 
     155   */ 
     156  public static function getIsEmbedded() 
     157  { 
     158    return self::$embedded; 
    225159  } 
    226160 
     
    230164   * @return string 
    231165   */ 
    232   protected function getNodeNS() 
     166  public static function getNodeNS() 
    233167  { 
    234168    $prefix = ''; 
    235     if ($this->embedded === true) 
     169    if (self::$embedded === true) 
    236170    { 
    237171      $prefix = 'svg:'; 
     
    293227  public function addAsDefinition($definition, $id = null) 
    294228  { 
    295     $is_element = is_callable(array($definition, 'setId')); 
    296     if (is_string($id) && trim($id) != '' && $is_element) 
    297     { 
    298       $definition->setId($id); 
    299     } 
    300     $id = $definition->getId(); 
    301     if (is_null($id)) 
    302     { 
    303       throw new csException( 
    304                   sprintf('A definition must have an id specified')); 
    305     } 
    306     elseif ($this->hasId($id)) 
    307     { 
    308       throw new csException( 
    309                   sprintf('The DOM id "%s" has been already defined', $id)); 
    310     } 
    311     else 
    312     { 
    313       $this->definitions[$id] = $definition; 
    314       $this->dom_ids[] = $id; 
    315     } 
     229    return $this->container->addAsDefinition($definition, $id); 
    316230  } 
    317231 
     
    323237  protected function getDefinitions() 
    324238  { 
    325     return $this->definitions; 
     239    return $this->container->getDefinitions(); 
    326240  } 
    327241 
     
    333247  protected function getUsedDefinitions() 
    334248  { 
    335     return $this->used_definitions; 
     249    return $this->container->getUsedDefinitions(); 
    336250  } 
    337251 
     
    339253   * Returns a defined element alias (a &lt;use/&gt; tag) 
    340254   * 
     255   * @param  string  $id 
    341256   * @return array 
     257   * @throws csException 
    342258   */ 
    343259  protected function getDefinition($id) 
    344260  { 
    345     if (!isset($this->definitions[$id])) 
    346     { 
    347       throw new csException( 
    348                   sprintf('Unable to find id "%s" in current document.')); 
    349     } 
    350     else 
    351     { 
    352       return $this->definitions[$id]; 
    353     } 
     261    return $this->container->getDefinition($id); 
    354262  } 
    355263 
     
    384292        break; 
    385293      } 
    386       if (true === $this->embedded) 
     294      if (true === self::$embedded) 
    387295      { 
    388296        $dom = new DOMDocument('1.0', 'UTF-8'); 
     
    408316  public function useDefinition($id, $attrs = array()) 
    409317  { 
    410     $this->used_definitions[] = array($this->getDefinition($id), $attrs); 
     318    return $this->container->useDefinition($id, $attrs); 
    411319  } 
    412320 
     
    421329  public function swapDepths($id1, $id2) 
    422330  { 
    423     if ($id1 == $id2) 
    424     { 
    425       return; 
    426     } 
    427     $e1 = $this->getElementById($id1); 
    428     $e2 = $this->getElementById($id2); 
    429     $e1depth = $e1->getDepth(); 
    430     $e2depth = $e2->getDepth(); 
    431     $e2->setDepth($e1depth); 
    432     $e1->setDepth($e2depth); 
    433     $this->dropElementById($id1); 
    434     $this->dropElementById($id2); 
    435     $this->addElement($e1, $e1depth, true); 
    436     $this->addElement($e2, $e2depth, true); 
     331    return $this->container->swapDepths($id1, $id2); 
    437332  } 
    438333 
     
    440335   * Checks if a DOM id already exists in current SVG Document 
    441336   * 
    442    * @param  string  $dom_id 
     337   * @param  string  $id 
    443338   * @return boolean 
    444339   */ 
    445   public function hasId($dom_id) 
    446   { 
    447     return in_array($dom_id, $this->dom_ids); 
     340  public function hasId($id) 
     341  { 
     342    return $this->container->hasId($id); 
    448343  } 
    449344 
     
    486381  public function setEmbedded($flag) 
    487382  { 
    488     $this->embedded = (boolean) $flag; 
     383    self::$embedded = (boolean) $flag; 
    489384  } 
    490385 
     
    627522    $dom->normalizeDocument(); 
    628523 
    629     // Validation 
    630     $this->validate($dom); 
     524    // Validation if strict mode 
     525    if (true === $this->strict_mode) 
     526    { 
     527      $this->validate(); 
     528    } 
    631529 
    632530    // Output 
    633     if ($this->embedded === true) 
     531    if (self::$embedded === true) 
    634532    { 
    635533      return $dom->saveXml($dom->documentElement, LIBXML_NOXMLDECL); 
     
    642540 
    643541  /** 
    644    * Process definitionsn if any 
     542   * Process definitions if any 
    645543   * 
    646544   */ 
    647545  protected function processDefinitions() 
    648546  { 
    649     $dom = $this->getDomDocument(); 
    650     if (count($this->getDefinitions()) > 0) 
    651     { 
    652       // Definitions declaration 
    653       $defs_node = $dom->createElement($this->getNodeNS().'defs'); 
    654       foreach ($this->getDefinitions() as $definition) 
    655       { 
    656         // Define elements 
    657         $definition_node = $definition->compile($this->embedded); 
    658         $def_node = $dom->importNode($definition_node, true); 
    659         $defs_node->appendChild($def_node); 
    660       } 
    661       $dom->documentElement->appendChild($defs_node); 
    662     } 
     547    return $this->container->processDefinitions($this->getDomDocument()); 
    663548  } 
    664549 
     
    669554  protected function processElements() 
    670555  { 
    671     $dom = $this->getDomDocument(); 
    672     foreach ($this->getElements() as $element) 
    673     { 
    674       $element_node = $element->compile($this->embedded); 
    675       $node_element = $dom->importNode($element_node, true); 
    676       $dom->documentElement->appendChild($node_element); 
    677     } 
     556    return $this->container->processElements($this->getDomDocument()); 
    678557  } 
    679558 
     
    690569      foreach ($this->embed_styles as $style) 
    691570      { 
    692         $style_node = $dom->createElement($this->getNodeNS().'style'); 
     571        $style_node = $dom->createElement(self::getNodeNS().'style'); 
    693572        $style_node->setAttribute('type',  $style['type']); 
    694573        $style_node->setAttribute('media', $style['media']); 
     
    733612    if (!is_null($this->title)) 
    734613    { 
    735       $title_node = $dom->createElement($this->getNodeNS().'title', 
     614      $title_node = $dom->createElement(self::getNodeNS().'title', 
    736615                                        strip_tags($this->title)); 
    737616      $dom->documentElement->appendChild($title_node); 
     
    742621    if (!is_null($this->description)) 
    743622    { 
    744       $desc_node = $dom->createElement($this->getNodeNS().'desc', 
     623      $desc_node = $dom->createElement(self::getNodeNS().'desc', 
    745624                                       strip_tags($this->description)); 
    746625      $dom->documentElement->appendChild($desc_node); 
     
    755634  { 
    756635    $dom = $this->getDomDocument(); 
    757     $root = $this->dom_document->createElement($this->getNodeNS().'svg'); 
     636    $root = $this->dom_document->createElement(self::getNodeNS().'svg'); 
    758637    $dom->appendChild($root); 
    759638    foreach ($this->attributes as $attr_name => $attr_value) 
     
    767646 
    768647  /** 
     648   * Process used definitions, if any 
     649   * 
     650   */ 
     651  protected function processUsedDefinitions() 
     652  { 
     653    return $this->container->processUsedDefinitions($this->getDomDocument()); 
     654  } 
     655 
     656  /** 
    769657   * Process scripts, if any 
    770658   * 
     
    778666      foreach ($this->scripts as $script) 
    779667      { 
    780         $script_node = $dom->createElement($this->getNodeNS().'script'); 
     668        $script_node = $dom->createElement(self::getNodeNS().'script'); 
    781669        $script_node->setAttribute('type', $script[1]); 
    782670        $script_content = $dom->createCDATASection($script[0]); 
     
    788676 
    789677  /** 
    790    * Process used definitions, if any 
    791    * 
    792    */ 
    793   protected function processUsedDefinitions() 
    794   { 
    795     $dom = $this->getDomDocument(); 
    796     if (count($this->getUsedDefinitions()) > 0) 
    797     { 
    798       foreach ($this->getUsedDefinitions() as $used_definition) 
     678   * Validates document 
     679   * 
     680   * @throws csException 
     681   */ 
     682  public function validate() 
     683  { 
     684    try 
     685    { 
     686      $this->getDomDocument()->validate(); 
     687    } 
     688    catch (csException $e) 
     689    { 
     690      if (preg_match('/php_network_getaddresses/', $e->getMessage())) 
    799691      { 
    800         $element = $used_definition[0]; 
    801         $attributes = $used_definition[1]; 
    802         $use_node = $dom->createElement($this->getNodeNS().'use'); 
    803         $use_node->setAttribute('xlink:href', '#'.$element->getId()); 
    804         foreach ($attributes as $attr_name => $attr_value) 
    805         { 
    806           $use_node->setAttribute($attr_name, $attr_value); 
    807         } 
    808         $dom->documentElement->appendChild($use_node); 
     692        throw new csException( 
     693          'An internet connection is required to validate document'); 
    809694      } 
    810     } 
    811   } 
    812  
    813   /** 
    814    * Validates document 
    815    * 
    816    * @throws csException 
    817    */ 
    818   protected function validate(DOMDocument $dom) 
    819   { 
    820     if (true == $this->strict_mode && !$dom->validate()) 
    821     { 
    822       throw new csException('This SVG Document does not validate'); 
     695      throw new csException( 
     696        'SVG document validation failed: '.$e->getMessage()); 
    823697    } 
    824698  } 
  • cleversvg/trunk/elements/csEllipse.class.php

    r318 r336  
    22/** 
    33 * SVG Ellipse class 
    4  *  
     4 * 
    55 * @author     Nicolas Perriault <nperriault@gmail.com> 
    66 * @package    cleversvg 
     
    1414  /** 
    1515   * Class constructor 
    16    *  
     16   * 
    1717   */ 
    1818  public function __construct($cx=null, $cy=null, $rx=null, $ry=null, $attrs=array()) 
     
    2424    $this->attributes = array_merge($attrs, $this->attributes); 
    2525  } 
    26    
     26 
    2727  /** 
    2828   * The x-axis coordinate of the center of the circle. 
    29    *  
    30    * @param  int  $cx  
     29   * 
     30   * @param  int  $cx 
    3131   */ 
    3232  public function setCx($cx) 
     
    3434    $this->setAttribute('cx', $cx); 
    3535  } 
    36    
     36 
    3737  /** 
    3838   * The y-axis coordinate of the center of the circle. 
    39    *  
    40    * @param  int  $cy  
     39   * 
     40   * @param  int  $cy 
    4141   */ 
    4242  public function setCy($cy) 
     
    4444    $this->setAttribute('cy', $cy); 
    4545  } 
    46    
     46 
    4747  /** 
    4848   * Sets the x-radius of the circle 
    49    *  
     49   * 
    5050   * @param  int  $xradius 
    5151   */ 
     
    5454    $this->setAttribute('rx', $xradius); 
    5555  } 
    56    
     56 
    5757  /** 
    5858   * Sets the y-radius of the circle 
    59    *  
     59   * 
    6060   * @param  int  $yradius 
    6161   */ 
     
    6464    $this->setAttribute('ry', $yradius); 
    6565  } 
    66    
     66 
    6767} 
  • cleversvg/trunk/elements/csGroup.class.php

    r322 r336  
    22/** 
    33 * SVG Group class 
    4  *  
     4 * 
    55 * @author     Nicolas Perriault <nperriault@gmail.com> 
    66 * @package    cleversvg 
    77 * @subpackage elements 
    88 */ 
    9 class csGroup extends csBaseElement 
     9class csGroup extends csElementsContainer 
    1010{ 
    1111 
    12   protected  
    13     $elements         = array(), 
    14     $used_definitions = array(), 
    15     $xml_node_name    = 'g'; 
     12  /** 
     13   * Definitions and elements container 
     14   * @var csElementsContainer 
     15   */ 
     16  protected $container = null; 
     17 
     18  /** 
     19   * SVG XML node name 
     20   * @var string 
     21   */ 
     22  protected $xml_node_name = 'g'; 
    1623 
    1724  /** 
    1825   * Class constructor 
    19    *  
     26   * 
    2027   * @param  mixed  $id     The DOM id of the element 
    2128   * @param  array  $attrs  More attributes to affect 
    2229   */ 
    23   public function __construct($id=null, $attrs=array()) 
     30  public function __construct($id = null, $attrs = array()) 
    2431  { 
    2532    $this->setId($id); 
    2633    $this->attributes = array_merge($attrs, $this->attributes); 
     34    $this->container = new csElementsContainer(); 
    2735  } 
    28    
     36 
    2937  /** 
    3038   * Computes DOMXML Node 
    31    *  
     39   * 
    3240   * @param  boolean  $embedded  Is SVG element embedded ? 
    3341   * @return DOMElement 
    3442   */ 
    35   public function compile($embedded=false) 
     43  public function compile($embedded = false) 
    3644  { 
    3745    $group_node = parent::compile($embedded); 
    3846    $dom = $this->getDomDocument(); 
    39      
     47 
    4048    # Prefix 
    4149    $prefix = $embedded === true ? 'svg:' : ''; 
    42      
     50 
    4351    // Add elements to current group node 
    4452    foreach ($this->getElements() as $element) 
     
    4755      $group_node->appendChild($node_element); 
    4856    } 
    49      
     57 
    5058    // Add used definitions 
    5159    foreach ($this->used_definitions as $used_definition) 
     
    6573      $group_node->appendChild($use_node); 
    6674    } 
    67      
     75 
    6876    return $group_node; 
    6977  } 
    70    
     78 
    7179  /** 
    72    * Adds a SVG element to current group 
    73    *  
    74    * @param  mixed  $element  SVG element to add 
     80   * Adds a SVG shape or group to current SVG Document at a certain depth 
     81   * 
     82   * @param  mixed    $element  SVG element to add 
     83   * @throws csException 
    7584   */ 
    7685  public function addElement($element) 
    7786  { 
    78     $this->elements[] = $element; 
     87    return $this->container->addElement($element); 
    7988  } 
    80    
     89 
     90  /** 
     91   * Retrieve an contained element instance by its DOM id 
     92   * 
     93   * @param  string  $id 
     94   * @return csBaseElement 
     95   * @throw  csException 
     96   */ 
     97  public function getElementById($id) 
     98  { 
     99    return $this->container->getElementById($id); 
     100  } 
     101 
    81102  /** 
    82103   * Returns an array containing current group elements 
    83    *  
    84    * @return array  
     104   * 
     105   * @return array 
    85106   */ 
    86   protected function getElements() 
     107  public function getElements() 
    87108  { 
    88     return $this->elements; 
     109    return $this->container->getElements(); 
    89110  } 
    90    
     111 
     112  /** 
     113   * Checks if a DOM id already exists in current SVG Document 
     114   * 
     115   * @param  string  $id 
     116   * @return boolean 
     117   */ 
     118  public function hasId($id) 
     119  { 
     120    return $this->container->hasId($id); 
     121  } 
     122 
    91123  /** 
    92124   * Uses a definition in current group 
    93    *  
     125   * 
    94126   * @param  mixed  $element 
    95127   * @param  array  $attrs    More atributes to add to &lt;use/&gt; tag 
    96128   */ 
    97   public function useDefinition($definition, $attrs=array()) 
     129  public function useDefinition($definition, $attrs = array()) 
    98130  { 
    99     $this->used_definitions[] = array($definition, $attrs); 
     131    return $this->container->useDefinition($definition, $attrs); 
    100132  } 
    101    
     133 
    102134} 
  • cleversvg/trunk/elements/csLink.class.php

    r322 r336  
    22/** 
    33 * SVG Link class 
    4  *  
     4 * 
    55 * @author     Nicolas Perriault <nperriault@gmail.com> 
    66 * @package    cleversvg 
    77 * @subpackage elements 
    88 */ 
    9 class csLink extends csBaseElement 
     9class csLink extends csElementsContainer 
    1010{ 
    1111 
    12   protected  
    13     $elements      = array(), 
    14     $xml_node_name = 'a'; 
     12  /** 
     13   * Elements container 
     14   * @var csElementsContainer 
     15   */ 
     16  protected $container = null; 
     17 
     18  /** 
     19   * Document elements 
     20   * @var array 
     21   */ 
     22  protected $elements = array(); 
     23 
     24  /** 
     25   * XML node name 
     26   * @var string 
     27   */ 
     28  protected $xml_node_name = 'a'; 
    1529 
    1630  /** 
    1731   * Class constructor 
    18    *  
     32   * 
    1933   * @param  mixed  $href    Link 
    2034   * @param  mixed  $target  Target 
     
    2640    $this->setTarget($target); 
    2741    $this->attributes = array_merge($attrs, $this->attributes); 
     42    $this->container = new csElementsContainer(); 
    2843  } 
    29    
     44 
    3045  /** 
    3146   * Computes DOMXML Node 
    32    *  
     47   * 
    3348   * @param  boolean  $embedded  Is SVG element embedded ? 
    3449   * @return DOMElement 
     
    3651  public function compile($embedded=false) 
    3752  { 
    38     $group_node = parent::compile($embedded); 
     53    $link_node = parent::compile($embedded); 
    3954    $dom = $this->getDomDocument(); 
    40      
     55 
    4156    // Add elements to current group node 
    4257    foreach ($this->getElements() as $element) 
    4358    { 
    4459      $node_element = $dom->importNode($element->compile($embedded), true); 
    45       $group_node->appendChild($node_element); 
     60      $link_node->appendChild($node_element); 
    4661    } 
    47      
    48     return $group_node; 
     62 
     63    return $link_node; 
    4964  } 
    50    
     65 
    5166  /** 
    52    * Adds a SVG element to current group 
    53    *  
    54    * @param  mixed  $element  SVG element to add 
     67   * Adds a SVG shape or group to current SVG Document at a certain depth 
     68   * 
     69   * @param  mixed    $element  SVG element to add 
     70   * @param  mixed    $depth    Depth (default: auto when set to NULL) 
     71   * @param  boolean  $replace_at_depth Replace element at depth if exist? 
     72   * @throws csException 
    5573   */ 
    56   public function addElement($element) 
     74  public function addElement($element, $depth = null, $replace_at_depth = false) 
    5775  { 
    58     $this->elements[] = $element; 
     76    return $this->container->addElement($element, $depth, $replace_at_depth); 
    5977  } 
    60    
     78 
     79  /** 
     80   * Retrieve an contained element instance by its DOM id 
     81   * 
     82   * @param  string  $id 
     83   * @return csBaseElement 
     84   * @throw  csException 
     85   */ 
     86  public function getElementById($id) 
     87  { 
     88    return $this->container->getElementById($id); 
     89  } 
     90 
    6191  /** 
    6292   * Returns an array containing current group elements 
    63    *  
    64    * @return array  
     93   * 
     94   * @return array 
    6595   */ 
    66   protected function getElements() 
     96  public function getElements() 
    6797  { 
    68     return $this->elements; 
     98    return $this->container->getElements(); 
    6999  } 
    70    
     100 
     101  /** 
     102   * Checks if a DOM id already exists in current SVG Document 
     103   * 
     104   * @param  string  $id 
     105   * @return boolean 
     106   */ 
     107  public function hasId($id) 
     108  { 
     109    return $this->container->hasId($id); 
     110  } 
     111 
    71112  /** 
    72113   * Sets the href attribute of the link 
    73    *  
     114   * 
    74115   * @param  string  $href 
    75116   */ 
     
    78119    $this->setAttribute('xlink:href', $href); 
    79120  } 
    80    
     121 
    81122  /** 
    82123   * Sets the target attribute of the link 
    83    *  
     124   * 
    84125   * @param  string  $target 
    85126   */ 
     
    88129    $this->setAttribute('target', $target); 
    89130  } 
    90    
     131 
    91132} 
  • cleversvg/trunk/elements/csPath.class.php

    r318 r336  
    22/** 
    33 * SVG Path class 
    4  *  
     4 * 
    55 * @author     Nicolas Perriault <nperriault@gmail.com> 
    66 * @package    cleversvg 
     
    1010{ 
    1111 
    12   protected  
     12  protected 
    1313    $xml_node_name = 'path', 
    1414    $path_array    = array(); 
     
    1616  /** 
    1717   * Class constructor 
    18    *  
     18   * 
    1919   */ 
    20   public function __construct($path_data=array(), $attrs=array()) 
     20  public function __construct($path_data = array(), $attrs = array()) 
    2121  { 
    2222    $this->setPathDataArray($path_data); 
     
    2424    $this->attributes = array_merge($attrs, $this->attributes); 
    2525  } 
    26    
     26 
    2727  /** 
    2828   * Compute current path data array 
    29    *  
     29   * 
    3030   */ 
    3131  protected function computePath() 
     
    3636      if (isset($move[1])) 
    3737      { 
    38         $path_string .= sprintf('%s %s',  
     38        $path_string .= sprintf('%s %s', 
    3939                                $move[0], 
    4040                                implode(' ', $move[1])); 
     
    4747    $this->setPathDataString(trim($path_string)); 
    4848  } 
    49    
     49 
    5050  /** 
    5151   * Sets the path data from an array 
    52    *  
     52   * 
     53   * @param  array 
    5354   */ 
    5455  public function setPathDataArray($path_data_array) 
     
    5758    $this->computePath(); 
    5859  } 
    59    
     60 
    6061  /** 
    6162   * Sets the path string 
    62    *  
     63   * 
     64   * @param  string 
    6365   */ 
    6466  public function setPathDataString($path_data_string) 
     
    7173    $this->setAttribute('d', $path_data_string); 
    7274  } 
    73    
     75 
    7476  /** 
    7577   * Init path data array and ready to receive path building directions 
    76    *  
     78   * 
    7779   */ 
    7880  public function begin() 
     
    8082    $this->path_data_array = array(); 
    8183  } 
    82    
     84 
    8385  public function end() 
    8486  { 
     
    8688    $this->computePath(); 
    8789  } 
    88    
     90 
    8991  /** 
    9092   * Move to a point 
    91    *  
     93   * 
     94   * @param  int  $x 
     95   * @param  int  $y 
    9296   */ 
    9397  public function moveTo($x, $y) 
     
    9599    $this->path_data_array[] = array('M', array($x, $y)); 
    96100  } 
    97    
     101 
    98102  /** 
    99103   * Draws a line moving to a point 
    100    *  
     104   * 
     105   * @param  int  $x 
     106   * @param  int  $y 
    101107   */ 
    102108  public function lineTo($x, $y) 
     
    104110    $this->path_data_array[] = array('L', array($x, $y)); 
    105111  } 
    106    
     112 
    107113  /** 
    108114   * Draws a horizontal line moving to a point 
    109    *  
     115   * 
     116   * @param  int  $x 
    110117   */ 
    111118  public function hLineTo($x) 
     
    113120    $this->path_data_array[] = array('H', array($x)); 
    114121  } 
    115    
     122 
    116123  /** 
    117124   * Draws a vertical line moving to a point 
    118    *  
     125   * 
     126   * @param  int  $y 
    119127   */ 
    120128  public function vLineTo($y) 
     
    122130    $this->path_data_array[] = array('V', array($y)); 
    123131  } 
    124    
     132 
    125133  /** 
    126134   * Draws a curve moving to a point 
    127    *  
     135   * 
    128136   */ 
    129137  public function curveTo($x, $y, $x1, $y1, $x2, $y2) 
     
    131139    $this->path_data_array[] = array('C', array($x, $y, $x1, $y1, $x2, $y2)); 
    132140  } 
    133    
     141 
    134142  /** 
    135143   * Draws a smooth curve moving to a point 
    136    *  
     144   * 
    137145   */ 
    138146  public function smoothCurveTo($x, $y, $x2, $y2) 
     
    140148    $this->path_data_array[] = array('S', array($x, $y, $x2, $y2)); 
    141149  } 
    142    
     150 
    143151  /** 
    144152   * The x-axis coordinate of one corner of the rectangular region into which an 
    145153   * embedded 'svg' element is placed. 
    146    *  
     154   * 
     155   * @param  int 
    147156   */ 
    148157  public function setX($x) 
     
    150159    $this->setAttribute('x', $x); 
    151160  } 
    152    
     161 
    153162  /** 
    154163   * The y-axis coordinate of one corner of the rectangular region into which an 
    155164   * embedded 'svg' element is placed. 
    156    *  
     165   * 
     166   * @param  int 
    157167   */ 
    158168  public function setY($y) 
     
    160170    $this->setAttribute('y', $y); 
    161171  } 
    162    
    163    
     172 
     173 
    164174} 
  • cleversvg/trunk/exceptions/csException.class.php

    r318 r336  
    22/** 
    33 * SVG Generation exception 
    4  *  
     4 * 
    55 * @author     Nicolas Perriault <nperriault@gmail.com> 
    66 * @package    cleversvg 
    7  * @subpackage esxceptions 
     7 * @subpackage exceptions 
    88 */ 
    99class csException extends Exception 
  • cleversvg/trunk/quick_examples.php

    r323 r336  
    11<?php 
    2 //First, require the library call script 
    32require_once('cleversvg.php'); 
    4  
    5 //Instanciate a SVG document, specifying its width and height 
    6 $doc = new csDocument(1000, 700, 'Un document SVG de test'); 
     3$doc = new csDocument(550, 400, 'SVG test document'); 
    74 
    85$gradient = new csLinearGradient(0, 0, '100%', '100%'); 
    96$gradient->addStop('0%', 'yellow'); 
    107$gradient->addStop('100%', 'red'); 
    11 $doc->addAsDefinition($gradient, 'myLinearGradient'); 
     8$doc->addAsDefinition($gradient, 'gradient'); 
    129 
    13 $polygon = new csPolygon(); 
    14 $polygon->setPointsArray(array(array(350,75), 
    15                             array(379,161), 
    16                             array(469,161), 
    17                             array(397,215), 
    18                             array(423,301), 
    19                             array(350,250), 
    20                             array(277,301), 
    21                             array(303,215), 
    22                             array(231,161), 
    23                             array(321,161))); 
    24 $polygon->setStroke('blue'); 
    25 $polygon->setFill('url(#myLinearGradient)'); 
    26 $polygon->setStrokeWidth(3); 
    27 $doc->addElement($polygon); 
     10$star = new csPolygon(); 
     11$star->setPointsArray(array(array(350,75),  array(379,161), 
     12                            array(469,161), array(397,215), 
     13                            array(423,301), array(350,250), 
     14                            array(277,301), array(303,215), 
     15                            array(231,161), array(321,161))); 
     16$star->setStroke('blue'); 
     17$star->setFill('url(#gradient)'); 
     18$star->setStrokeWidth(4); 
     19$linkedstar = new csLink(); 
     20$linkedstar->setHref('http://prendreuncafe.com/blog/'); 
     21$linkedstar->addElement($star); 
     22$doc->addElement($linkedstar); 
    2823 
    29  
    30  
    31 $gradient = new csRadialGradient('50%', '50%', '30%', '30%', '50%'); 
    32 $gradient->addStop('15%', 'lightblue', 0.5); 
    33 $gradient->addStop('100%', 'darkblue'); 
    34 $doc->addAsDefinition($gradient, 'myRadialGradient'); 
    35  
    36 $path = new csPath(); 
    37 $path->begin(); 
    38 $path->moveTo(600, 25); 
    39 $path->lineTo(120, 25); 
    40 $path->lineTo(350, 500); 
    41 $path->end(); 
    42 $path->setStroke('orange'); 
    43 $path->setStrokeWidth(3); 
    44 $path->setFill('url(#myRadialGradient)'); 
    45 $doc->addElement($path); 
    46  
    47  
    48 //Never forget to send image/svg+xml headers before outputing 
    4924header("Content-type: image/svg+xml"); 
    50  
    51 //When you're done, render the document as a standalone one 
    52 echo $doc->toXML(true); 
    53  
    54 //Or an embedded one : 
    55 //echo $doc->toXML(true); 
    56 ?> 
     25echo $doc->toXML(); 
  • cleversvg/trunk/tests/csBaseTestCase.class.php

    r335 r336  
    2727 
    2828  /** 
     29   * Checks if an attribute exists for given node 
     30   * 
     31   * @param  csDocument $doc 
     32   * @param  string     $xpath_query 
     33   * @param  string     $attr_name 
     34   * @return boolean 
     35   */ 
     36  public function assertNodeAttrExists($doc, $xpath_query, $attr_name) 
     37  { 
     38    $result = $this->documentXpath($doc, $xpath_query); 
     39    $this->assertNodeExists($doc, $xpath_query); 
     40    if (!$result->item(0)->hasAttribute($attr_name)) 
     41    { 
     42      throw new PHPUnit_Framework_ExpectationFailedException( 
     43        sprintf('No attribute "%s" found in node retrieved by "%s". Source: %s', 
     44                $attr_name, $xpath_query, "\n".$doc->toXML())); 
     45    } 
     46  } 
     47 
     48  /** 
     49   * Checks that an attribute does not exist for given node 
     50   * 
     51   * @param  csDocument $doc 
     52   * @param  string     $xpath_query 
     53   * @param  string     $attr_name 
     54   * @return boolean 
     55   */ 
     56  public function assertNodeAttrNotExists($doc, $xpath_query, $attr_name) 
     57  { 
     58    $result = $this->documentXpath($doc, $xpath_query); 
     59    $this->assertNodeExists($doc, $xpath_query); 
     60    if ($result->item(0)->hasAttribute($attr_name)) 
     61    { 
     62      throw new PHPUnit_Framework_ExpectationFailedException( 
     63        sprintf('Attribute %s="%s" retrieved by node "%s". Source: %s', 
     64                $attr_name, 
     65                $result->item(0)->getAttribute($attr_name), 
     66                $xpath_query, "\n".$doc->toXML())); 
     67    } 
     68  } 
     69 
     70  /** 
    2971   * Checks if an attribute xpath query matches given value for given doc. 
    3072   * 
     
    5193                $xpath_query, $value, $attr_value, "\n".$doc->toXML())); 
    5294    } 
    53     return true; 
    5495  } 
    5596 
     
    5798  { 
    5899    $result = $this->documentXpath($doc, $xpath_query); 
    59     return !is_null($result->item(0)); 
     100    if (is_null($result->item(0))) 
     101    { 
     102      throw new PHPUnit_Framework_ExpectationFailedException( 
     103        sprintf('XPath query "%s" does not return a result. Source: %s', 
     104                $xpath_query, "\n".$doc->toXML())); 
     105    } 
    60106  } 
    61107 
    62108  protected function assertNodeDoesNotExist($doc, $xpath_query) 
    63109  { 
    64     return !$this->assertNodeExists($doc, $xpath_query); 
     110    $result = $this->documentXpath($doc, $xpath_query); 
     111    if (!is_null($result->item(0))) 
     112    { 
     113      throw new PHPUnit_Framework_ExpectationFailedException( 
     114        sprintf('XPath query "%s" return a result. Source: %s', 
     115                $xpath_query, "\n".$doc->toXML())); 
     116    } 
    65117  } 
    66118 
     
    74126                $xpath_query, $value, "\n".$doc->toXML())); 
    75127    } 
    76     return true; 
    77128  } 
    78129 
     
    88139  private function documentXpath(csDocument $doc, $xpath_query) 
    89140  { 
    90     $dom = DOMDocument::loadXML($doc->toXML()); // yeah, I *must* reload the xml 
     141    $dom = new DOMDocument(); 
     142    $dom->loadXML($doc->toXML()); // yeah, I *must* reload the xml 
    91143    $xpath = new DOMXPath($dom); 
    92144    $xpath->registerNamespace("svg", "http://www.w3.org/2000/svg"); // uh oh 
     145    $xpath->registerNamespace("xlink", "http://www.w3.org/1999/xlink"); 
    93146    return $xpath->query($xpath_query, $dom); 
    94147  } 
  • cleversvg/trunk/tests/csCircleTest.php

    r333 r336  
    4646    $doc->addElement($circle); 
    4747    $xml = $doc->toXML(); 
    48     $this->assertEquals(preg_match('#z-index: 4#s', $xml), 1, $xml); 
     48    $this->assertAttrValueEquals($doc, '/svg:svg/svg:circle/@style', 'z-index: 4'); 
     49    $circle->setDepth(null); 
     50    $this->assertNodeAttrNotExists($doc, '/svg:svg/svg:circle', 'style'); 
    4951  } 
    5052 
  • cleversvg/trunk/tests/csDocumentTest.php

    r335 r336  
    1111{ 
    1212 
     13  public function testAddAsDefinition() 
     14  { 
     15    $doc = new csDocument(320, 240, 'Blah'); 
     16    $circle = new csCircle(); 
     17    $circle->setStroke('yellow'); 
     18    $circle->setStrokeWidth(2); 
     19    $circle->setFill('orange'); 
     20    $circle->setId('circle1'); 
     21    $doc->addAsDefinition($circle, 'c1'); 
     22    $this->assertNodeExists($doc, '/svg:svg/svg:defs/svg:circle[@id = "c1"]'); 
     23    $this->assertType('csCircle', $doc->getElementById('c1')); 
     24  } 
     25 
     26  /** 
     27   * @expectedException csException 
     28   * 
     29   */ 
     30  public function testAddElement() 
     31  { 
     32    $doc = new csDocument(320, 240, 'Blah'); 
     33    $c = $doc->createElement('circle'); 
     34    $c->setId('myId'); 
     35    $doc->addElement($c); 
     36    $this->assertType('csCircle', $doc->getElementById('myId')); 
     37    $rect = new csRect(100, 50, 40, 20); 
     38    $rect->setId('myId'); 
     39    $doc->addElement($rect); 
     40  } 
     41 
    1342  public function testConstructor() 
    1443  { 
     
    4170  } 
    4271 
     72  public function testGetDomDocument() 
     73  { 
     74    $doc = new csDocument(320, 240, 'Blah'); 
     75    $this->assertType('DOMDocument', $doc->getDomDocument()); 
     76  } 
     77 
     78  public function testGetElementById() 
     79  { 
     80    $doc = new csDocument(320, 240, 'Blah'); 
     81 
     82    // Testing flat level search 
     83    $c = new csCircle(); 
     84    $c->setRadius(10); 
     85    $c->setStroke('red'); 
     86    $c->setId('c1'); 
     87    $doc->addElement($c); 
     88    $this->assertType('csBaseElement', $doc->getElementById('c1')); 
     89 
     90    // Testing deep level search 
     91    $c2 = clone($c); 
     92    $c2->setId('c2'); 
     93    $g1 = new csGroup('g1'); 
     94    $g1->addElement($c2); 
     95    $doc->addElement($g1); 
     96    $this->assertType('csBaseElement', $doc->getElementById('c2')); 
     97 
     98    $c3 = clone($c); 
     99    $c3->setId('c3'); 
     100    $g2 = new csGroup('g2'); 
     101    $g2->addElement($c3); 
     102    $g3 = new csGroup('g3'); 
     103    $g3->addElement($g2); 
     104    $doc->addElement($g3); 
     105    $this->assertType('csBaseElement', $doc->getElementById('c3')); 
     106  } 
     107 
    43108  public function testHasId() 
    44109  { 
     
    55120    $this->assertTrue($doc->hasId('circle2')); 
    56121    $this->assertFalse($doc->hasId('jfhkjsdfh')); 
     122    $doc->dropElementById('circle1'); 
     123    $this->assertFalse($doc->hasId('circle1')); 
     124    $this->assertTrue($doc->hasId('circle2')); 
     125    $doc->dropElementById('circle2'); 
     126    $this->assertFalse($doc->hasId('circle2')); 
    57127  } 
    58128 
     
    106176  } 
    107177 
     178  public function testUseDefinition() 
     179  { 
     180    $doc = new csDocument(320, 240, 'Blah'); 
     181    $circle = new csCircle(); 
     182    $doc->addAsDefinition($circle, 'c1'); 
     183    $doc->useDefinition('c1', array('x' => 200, 'y' => 100)); 
     184    $this->assertAttrValueEquals($doc, '/svg:svg/svg:use/@xlink:href', '#c1'); 
     185  } 
     186 
     187  public function testValidation() 
     188  { 
     189    $doc = new csDocument(320, 240, 'Test depths'); 
     190    $c1 = new csCircle(160, 120, 40); 
     191    $c1->setDepth(20); 
     192    $c1->setId('c1'); 
     193    $c2 = new csCircle(140, 100, 40); 
     194    $c2->setDepth(40); 
     195    $c2->setId('c2'); 
     196    $doc->addElement($c1); 
     197    $doc->addElement($c2); 
     198    $doc->setStrictMode(true); 
     199    $doc->toXML(); 
     200  } 
     201 
    108202  public function testViewBox() 
    109203  { 
  • cleversvg/trunk/tests/csTestSuite.php

    r326 r336  
    55require_once 'csCircleTest.php'; 
    66require_once 'csDocumentTest.php'; 
     7require_once 'csEllipseTest.php'; 
     8require_once 'csGroupTest.php'; 
     9require_once 'csImageTest.php'; 
     10require_once 'csLineTest.php'; 
     11require_once 'csLinkTest.php'; 
     12require_once 'csPathTest.php'; 
     13require_once 'csPolylineTest.php'; 
     14require_once 'csRectTest.php'; 
     15require_once 'csTextTest.php'; 
    716 
    817if (!defined('PHPUnit_MAIN_METHOD')) 
     
    2130  public static function suite() 
    2231  { 
    23     $suite = new PHPUnit_Framework_TestSuite('CleverSvg'); 
     32    $suite = new PHPUnit_Framework_TestSuite('CleverSvg::csDocument'); 
     33    $suite->addTestSuite('csCircleTest'); 
    2434    $suite->addTestSuite('csDocumentTest'); 
    25     $suite->addTestSuite('csCircleTest'); 
     35    $suite->addTestSuite('csEllipseTest'); 
     36    $suite->addTestSuite('csGroupTest'); 
     37    $suite->addTestSuite('csImageTest'); 
     38    $suite->addTestSuite('csLineTest'); 
     39    $suite->addTestSuite('csLinkTest'); 
     40    $suite->addTestSuite('csPathTest'); 
     41    $suite->addTestSuite('csPolylineTest'); 
     42    $suite->addTestSuite('csRectTest'); 
     43    $suite->addTestSuite('csTextTest'); 
    2644    return $suite; 
    2745  }