XML/SvgToPdf
authorAlan Knowles <alan@roojs.com>
Thu, 20 Oct 2016 03:11:53 +0000 (11:11 +0800)
committerAlan Knowles <alan@roojs.com>
Thu, 20 Oct 2016 03:11:53 +0000 (11:11 +0800)
Attribute changed XML/SvgToPdf

XML/SvgToPdf/Base.php [new file with mode: 0644]
XML/SvgToPdf/G.php [new file with mode: 0644]
XML/SvgToPdf/Image.php [new file with mode: 0644]
XML/SvgToPdf/Path.php [new file with mode: 0644]
XML/SvgToPdf/Rect.php [new file with mode: 0644]
XML/SvgToPdf/Text.php [new file with mode: 0644]
XML/SvgToPdf/Tspan.php [new file with mode: 0644]

diff --git a/XML/SvgToPdf/Base.php b/XML/SvgToPdf/Base.php
new file mode 100644 (file)
index 0000000..334b26f
--- /dev/null
@@ -0,0 +1,353 @@
+<?php
+
+/*
+The base node includes:
+    fromNode - convert the XML_Tree node into object with vars
+        (and parse styles)
+    
+    // iternate tree with writePDF
+    writePDF($pdf,$data) 
+    childrenWritePDF($pdf,$data)  
+
+    // shift coordinates of group dynamic elements 
+    // so the x is relative to the block corner.
+    shiftChildren($x,$y) {
+    shift($x,$y) 
+    
+    // find a dynamic block and calculate how much data it can hold.
+    // so you know how many pages are needed.
+    calcPerPage()
+*/
+
+class XML_SvgToPDF_Base { 
+
+    var $x = 0;
+    var $y = 0;
+    var $width = 0; // used in svg..
+    var $height= 0; 
+    
+    var $style = array();
+    var $children = array();
+    
+    var $transform = '';
+    
+    function fromXmlNode($node)
+    {
+        // extract attributes
+        foreach($node->attributes as $k=>$v) {
+            if (in_array($k, array('style'))) {
+                continue;
+            }
+            
+            $this->$k = $v->value;
+            if (preg_match('/[0-9]+mm$/',$v->value)) {
+                $vv = str_replace('mm','',$v->value);
+                $vv = $vv * 3.543307;
+                
+                $this->$k = $vv;
+                continue;
+            }
+        }
+        // deal with style..
+        if ($node->hasAttribute('style') && strlen($node->getAttribute('style'))) {
+            
+            $s = explode(';',$node->getAttribute('style'));
+            foreach($s as $ss) {
+                if (!strlen(trim($ss))) {
+                    continue;
+                }
+                if (strpos($ss,':') === false) {
+                    $style[$ss] = true;
+                    continue;
+                }
+                $sss = explode(':',$ss);
+                if (preg_match('/[0-9]+pt$/',$sss[1])) {
+                    $sss[1] =  str_replace('pt','',$sss[1]);
+                }
+                $style[$sss[0]] = $sss[1];
+            }
+            $this->style = $style;
+        }
+      
+        $this->transform();
+        // if node is a tspan  
+       
+         
+         
+    }
+    
+    
+    function fromNode($node) {
+        
+        if ($node->attributes) {
+            foreach($node->attributes as $k=>$v) {
+                
+                echo $node->name . ":" . $k . "=>". $v. "<BR>";
+                
+                if (strpos($k,':')) {
+                    $kk = explode(':',$k);
+                    $k = $kk[1];
+                }
+                $this->$k = $v;
+                if (preg_match('/[0-9]+mm$/',$v)) {
+                    $v = str_replace('mm','',$v);
+                    $v = $v * 3.543307;
+                    
+                    $this->$k = $v;
+                    continue;
+                }
+                
+            }
+        }
+        
+        if (isset($this->style)) {
+            $s = explode(';',$this->style);
+            foreach($s as $ss) {
+                if (!strlen(trim($ss))) {
+                    continue;
+                }
+                if (strpos($ss,':') === false) {
+                    $style[$ss] = true;
+                    continue;
+                }
+                $sss = explode(':',$ss);
+                if (preg_match('/[0-9]+pt$/',$sss[1])) {
+                    $sss[1] =  str_replace('pt','',$sss[1]);
+                }
+                $style[$sss[0]] = $sss[1];
+            }
+            $this->style = $style;
+        }
+                
+        
+        if ($node->content) {
+            $this->content = trim($node->content);
+               echo $node->name . ":CONTENT=>". $node->content. "<BR>";
+        }
+        if ($node->children) {
+            $this->children = $node->children;
+        }
+        echo "<PRE>BEFORE:";print_r($this->toArray());
+        $this->transform();
+        echo "<PRE>AFTER:";print_r($this->toArray());
+    }
+
+
+    function transform() {
+        if (empty($this->transform)) {
+            return;
+        }
+        // do not transform tspans -- done by overwriting this...
+        //if ($this->x === false) {
+        //    return;
+       // }
+        
+        // deal with transformation..
+        $tr = $this->transform;
+        if (preg_match('/scale\(([0-9e.-]+),([0-9e.-]+)\)/',$tr,$args)) {
+            $xscale = $args[1];
+            $yscale = $args[2];
+            $method = 'scale';
+            } else if (preg_match('/matrix\(([0-9e.-]+),([0-9e.-]+),([0-9e.-]+),([0-9e.-]+),([0-9e.-]+),([0-9e.-]+)\)/',$tr,$args)) {
+            array_shift($args);
+            require_once 'Math/Matrix.php';
+            $matrix =  new Math_Matrix( array(
+                    array($args[0],$args[2],$args[4]),
+                    array($args[1],$args[3],$args[5]),
+                    array(       0,       0,   1))
+            );
+            $method = 'matrix';
+        } else if (preg_match('/translate\(([0-9e.-]+),([0-9e.-]+)\)/',$tr,$args)) {
+            $x = $args[1];
+            $y = $args[2];
+            $method = 'translate';
+        } else {
+            echo "<PRE>no match?";print_r($this); exit;
+            return;
+        }
+        //
+        switch ($method) {
+            case 'scale':
+                $this->x *=  $xscale;
+                $this->y *=  $yscale;
+                if (empty($this->children)) {
+                    return;
+                }
+                foreach(array_keys($this->children) as $i) {
+                    if ($this->children[$i]->x === false) {
+                        continue;
+                        // echo "<PRE>";print_r($this);exit;
+                    }
+                    $this->children[$i]->x *=  $xscale;
+                    $this->children[$i]->y *=  $yscale;
+                }
+                break;
+            case 'matrix':
+                $v = new Math_Vector(array($this->x,$this->y,0));
+                
+                $r = $matrix->vectorMultiply($v);
+                $r = $r->getData();
+                $this->x = $r[0];
+                $this->y = $r[1];
+                //echo "<PRE>";var_dump(       $r);exit;
+                if (empty($this->children)) {
+                    return;
+                }
+                foreach(array_keys($this->children) as $i) {
+                    if ($this->children[$i]->x === false) {
+                        continue;
+                        // echo "<PRE>";print_r($this);exit;
+                    }
+                    $v = new Math_Vector(array($this->children[$i]->x,$this->children[$i]->y,0));
+                    $r =  $matrix->vectorMultiply($v);
+                    $r = $r->getData();
+                    $this->children[$i]->x = $r[0];
+                    $this->children[$i]->y = $r[1];
+     
+                }
+                break; 
+            case 'translate':
+                if ($this->x !== false &&  $this->y !== false) {
+                         
+                   $this->x +=  $x;
+                   $this->y +=  $y;
+                }
+                if (empty($this->children)) {
+                    return;
+                }
+                foreach(array_keys($this->children) as $i) {
+                    if ($this->children[$i]->x === false || $this->children[$i]->y === false) {
+                        continue;
+                        // echo "<PRE>";print_r($this);exit;
+                    }
+                    $this->children[$i]->x +=  $x;
+                    $this->children[$i]->y +=  $y;
+                }
+                break; 
+                 
+          }
+     }
+
+
+
+
+    
+    function writePDF($pdf,&$data) {
+        $this->childrenWritePDF($pdf,$data);
+    }
+    
+    function childrenWritePDF(&$pdf,&$data) {
+        if (!@$this->children) {
+            return;
+        }
+        foreach(array_keys($this->children) as $k) {
+            if (empty($this->children[$k])) {
+                continue;
+            }
+            if (!method_exists($this->children[$k],'writePDF')) {
+                echo "OOPS unknown object? <PRE>" ; print_r($this->children[$k]); exit;
+            }
+            $this->children[$k]->writePDF($pdf,$data);
+        }
+    }
+    
+    
+    // add the values to the children..
+    function shiftChildren($x,$y) {
+        if (empty($this->children)) {
+            return;
+        }
+        foreach(array_keys($this->children) as $k) {
+            if (!$this->children[$k]) {
+                continue;
+            }
+            $this->children[$k]->shift($x,$y);
+        }
+    }
+    
+    function shift($x,$y) {
+        //XML_SvgToPDF::debug('shift');
+        //XML_SvgToPDF::debug(array($x,$y));
+        //XML_SvgToPDF::debug($this);
+        
+        if ($x === false) {
+            return;
+        }
+        
+        //var_dump(array("SHIFT", $x, $y, "TO: ", $this->x , $this->y));
+        if ($this->x !== false) {
+            $this->x += $x;
+        }
+        if ($this->y !== false) {
+            $this->y += $y;
+        }
+        //XML_SvgToPDF::debug($this);
+        $this->shiftChildren($x,$y);
+    }
+    function calcPerPage() {
+        $ret = array();
+        foreach($this->children as $n) {
+            if (!$n) {
+                continue;
+            }
+            if (!is_a($n, 'XML_SvgToPDF_G')) {
+                continue;
+            }
+            if (!isset($n->settings) || !isset($n->settings['dynamic']))  {
+                continue;
+            }
+            
+            
+            $rows  = isset($n->settings['rows']) ? $n->settings['rows'] : 1;
+            $cols  = isset($n->settings['cols']) ? $n->settings['cols'] : 1;
+           // return array($n->settings['dynamic'], $rows * $cols);
+
+             
+            $ret[$n->settings['dynamic']] =  $rows * $cols;
+            
+            
+            
+            
+        }
+        //return array('',0);
+
+        return $ret;
+         
+    
+    
+    
+    
+    }
+    
+    function toColor($color) {
+        if (!$color || ($color == 'none')) {
+            return false;
+        }
+        
+        if ($color == 'black') {
+            $color = '#000000';
+        }
+        
+        return array(
+            hexdec(substr($color,1,2)),
+            hexdec(substr($color,3,2)),
+            hexdec(substr($color,5,2)));
+        
+    }
+    function toArray()
+    {
+        $ret = array();
+        $ar = (array) $this;
+        $ret['__CLASS__'] = get_class($this);
+        foreach($ar as $k=>$v) {
+            if (is_array($v) || is_object($v)) {
+                $ret[$k] = "**ARRAY|OBJECT**";
+                continue;
+            }
+            $ret[$k] = $v;
+        }
+        return $ret;    
+    }
+    
+    
+}
diff --git a/XML/SvgToPdf/G.php b/XML/SvgToPdf/G.php
new file mode 100644 (file)
index 0000000..b854f41
--- /dev/null
@@ -0,0 +1,217 @@
+<?php
+
+/* code that deals with svg groups
+it does alot of smart stuff to handle 'dynamic' blocks
+
+*/
+
+
+class XML_SvgToPDF_G     extends XML_SvgToPDF_Base
+{ 
+    var $boundingbox = false; // for repeats...
+    var $settings = array();  // cols/rows..
+
+    function fromXmlNode($node)
+    {
+       // print_r("G:fromXmlNode");
+        parent::fromXmlNode($node);
+        
+        if (empty($this->dynamic)) {
+            return;
+        }
+        $settings = array(
+            'rows' => $this->rows,
+            'cols' => $this->cols,
+            'dynamic' => $this->dynamic
+        );
+        
+        
+         //look for the bounding box..
+        $boundingbox = false;
+//echo "<PRE>";print_r($this->children);exit;
+        foreach(array_keys($this->children) as $k) {
+            if (!is_a($this->children[$k], 'XML_SvgToPDF_Rect')) {
+               continue;
+            }
+            if (empty($this->children[$k]->nonprintable) || ($this->children[$k]->nonprintable != 'true')) {
+                continue;
+            }
+ //           echo "SETTING BOUNDING BOX"; exit; 
+            $boundingbox = clone($this->children[$k]);
+            // box will be rendered..
+            $this->children[$k]->style['fill'] = 'none';
+               // unset($this->children[$k]);
+          
+        }
+        if (!$boundingbox) {
+            return;
+        }
+        //echo "<PRE>";print_r($boundingbox ); exit;
+        
+        $this->boundingbox =  $boundingbox ;
+        $this->settings = $settings;
+        
+        // change the X/Y values of all the child elements..
+         
+        
+        $this->shiftChildren(-1* $this->boundingbox->x,-1 * $this->boundingbox->y);
+        //$this->shiftChildren($this->boundingbox->x,$this->boundingbox->y);
+      
+    }
+
+    // not sure why this is done twice?
+
+    function fromNode($node) {
+        parent::fromNode($node);
+        
+    
+//----------- applyDynamic...        
+        
+          // look for 
+        if (empty($this->children)) {
+            return;
+        }
+        
+        if (empty($this->dynamic)) {
+            return;
+        }
+        $settings = array(
+            'rows' => $this->rows,
+            'cols' => $this->cols,
+            'dynamic' => $this->dynamic
+        );
+         
+        
+        
+         //look for the bounding box..
+        $boundingbox = false;
+        foreach(array_keys($this->children) as $k) {
+            if (!is_a($this->children[$k], 'XML_SvgToPDF_Rect')) {
+               continue;
+            }
+            if (@$this->children[$k]->nonprintable == 'true') {
+                $boundingbox = clone($this->children[$k]);
+                $this->children[$k]->style['fill'] = 'none';
+               // unset($this->children[$k]);
+            }
+        }
+        if (!$boundingbox) {
+            return;
+        }
+        //echo "<PRE>";print_r($boundingbox );
+        
+        $this->boundingbox =  $boundingbox ;
+        $this->settings = $settings;
+        $this->shiftChildren($this->boundingbox->x,$this->boundingbox->y);
+    }
+    
+    function shift($x,$y) {
+        
+        if ($this->boundingbox) {
+            return;
+        }
+        
+        $this->shiftChildren($x,$y);
+    
+    }
+
+
+
+    function writePDF($pdf,&$data) {
+        // g group = does it have a 
+        // look for 
+        if (empty($this->children)) {
+            return;
+        }
+         
+        // not dynamic.. -> just dump..
+        if (empty($this->settings)) {
+            return $this->childrenWritePDF($pdf,$data);
+        }
+        
+        $use = false;
+        if (substr($this->settings['dynamic'],-2,2) == '()') {
+        
+            $use = $data->{substr($this->settings['dynamic'],0,-2)}();
+            
+        } else {
+            $use = empty($data[$this->settings['dynamic']]) ? ''  : $data[$this->settings['dynamic']];
+        }
+        
+            
+        if (empty($use)) {
+            return;
+        }
+        // if use is a value  - make it an array with a single element, so that the bounding box
+        // additions apply..
+        if (!is_array($use)) {
+            $use = array($use);
+        }
+        
+        // echo "<PRE>";print_r($boundingbox );
+        
+        
+        $this->x = $x = $this->boundingbox->x;
+        $this->y =$y = $this->boundingbox->y;
+        $w = $this->boundingbox->width;
+        $h = $this->boundingbox->height; 
+        
+        
+        //echo '<PRE>';print_r($this);exit;
+        // shift... ** this does not handle groups!!!
+      
+        //print_R($use);
+        $keys = array_keys($use);
+        $kpos = 0;
+        $kmax = count($keys);
+        //XML_SvgToPDF::debug(array($x,$y,$w,$h));
+        //XML_SvgToPDF::debug($keys);
+        XML_SvgToPDF::debug($this->settings);
+        
+        $yy = $y;
+        
+        for($r=0;$r<$this->settings['rows'];$r++) {
+            $record = $use[$keys[$kpos]];
+            
+            
+            for($c=0;$c<$this->settings['cols'];$c++) {
+                
+                $record = $use[$keys[$kpos]];
+                
+                $xx = $x + ($c*$w);
+                XML_SvgToPDF::debug(array($xx,$yy));
+                foreach(array_keys($this->children) as $k) {
+                    if (!$this->children[$k]) {
+                        continue;
+                    }
+                    
+                   //  if (is_object($use[$keys[$kpos]]) && method_exists($use[$keys[$kpos]], 'loadSvg')) {
+                   //     // set the defaults, as we cant do it in the thing now..
+                   //     $use[$keys[$kpos]]->loadSvg();
+                   // }
+                    
+                    
+                    $this->children[$k]->xx = $xx;
+                    $this->children[$k]->yy = $yy;
+                    $this->children[$k]->maxWidth = $w - 20; 
+                    $this->children[$k]->writePDF($pdf,$use[$keys[$kpos]]);
+                }
+                $kpos++;
+                if ($kpos >= $kmax) {
+                    break 2;
+                }
+            }
+            $yy += !empty($record->userows) ? ($record->userows) * $h : $h;
+
+            
+        }
+        
+        
+        
+    }
+
+
+
+
+}
diff --git a/XML/SvgToPdf/Image.php b/XML/SvgToPdf/Image.php
new file mode 100644 (file)
index 0000000..ecc3dbc
--- /dev/null
@@ -0,0 +1,20 @@
+<?php
+
+/* output a rectangle
+
+*/
+
+class XML_SvgToPDF_Image  extends XML_SvgToPDF_Base {
+    function writePDF($pdf,$data) {
+    
+    
+        $dir = dirname($GLOBALS['_XML_SVGTOPDF']['options']['file']);
+        $pdf->Image($dir .'/'.basename($this->href), $this->x/ 3.543307, $this->y/ 3.543307, $this->width/ 3.543307,$this->height/ 3.543307);
+     
+    
+    }
+
+
+
+}
diff --git a/XML/SvgToPdf/Path.php b/XML/SvgToPdf/Path.php
new file mode 100644 (file)
index 0000000..408b909
--- /dev/null
@@ -0,0 +1,148 @@
+<?php
+
+/* output a line
+
+*/
+
+class XML_SvgToPDF_Path  extends XML_SvgToPDF_Base {
+    var $d; // stores the details of the path..
+    
+    
+    function fromXmlNode($node) {
+        parent::fromXmlNode($node);
+        $d = explode(' ',trim($this->d));
+        $i=0;
+        $data = array();
+        
+        while ($i < count($d)) {
+            $action = $d[$i];
+            switch(strtolower($action)) {
+                
+                
+                
+                case 'c': // ????
+                   
+                    $data[] = array('L',$d[$i+3],$d[$i+4]);
+                    $i+=7;
+                    break;
+                case 'm': // move
+                case 'l': // line
+                    $data[] = array($action,$d[$i+1],$d[$i+2]);
+                    $i+=3;
+                    break;
+                case 'h': // move horizontal
+                case 'v': // move horizontal
+                    $data[] = array($action,$d[$i+1]);
+                    $i+=2;
+                    break;
+                
+                
+                case 'z': // close path..
+                    $data[] = array($action);
+                    $i++;
+                    break;
+                default:
+                    echo "oops found something odd in path? '$action'";
+                    echo $this->d;
+                    exit;
+                    break;
+            }
+        }
+        $this->d = $data;
+    }
+            
+            
+        // TODO!! - shift!!!
+            
+        
+        
+        
+        
+     function shift($x,$y) {
+        //XML_SvgToPDF::debug('shift');
+        //XML_SvgToPDF::debug(array($x,$y));
+        //XML_SvgToPDF::debug($this);
+        foreach($this->d as $i=>$a) {
+            if (count($a) < 2) {
+                continue;
+            }
+            if ($a[0] == 'v') {
+                $this->d[$i][1] -= $y;
+            } else {
+                $this->d[$i][1] -= $x;
+                if (isset($this->d[$i][2])) {
+                    $this->d[$i][2] -= $y;
+                }
+            }
+        }
+        
+    }   
+        
+    
+    
+    function writePDF($pdf,$data) {
+        
+        $l = $this->toColor(@$this->style['stroke']);
+        if ($l) {
+            $pdf->setDrawColor($l[0],$l[1],$l[2]);
+        }
+        $pdf->setLineWidth($this->style['stroke-width']/ 3.543307);
+     
+        $c = array();
+        if (count($this->d) > 2) {
+            $cc = array();
+            foreach($this->d as $a) { 
+                 if (count($a) < 2) {
+                        continue;       
+                 }
+                 $x = $a[1] + @$this->xx;
+                 $y = $a[2] + @$this->yy;
+                 $cc[] = $x/ 3.543307;
+                 $cc[] = $y/ 3.543307;
+            }
+            $pdf->line($cc,0,0,0);
+            return;
+        }   
+
+        foreach($this->d as $a) {
+            switch($a[0]) {
+                case 'M':
+                    $x = $a[1] + @$this->xx;
+                    $y = $a[2] + @$this->yy;
+                    $c = array($x,$y);
+                    break;
+                
+                case 'L':
+                    $x = $a[1] + @$this->xx;
+                    $y = $a[2] + @$this->yy;
+                    $pdf->line($c[0]/ 3.543307,$c[1]/ 3.543307,$x/ 3.543307,$y/ 3.543307);
+                    $c = array($x,$y);
+                    break;
+                default:
+                    break;
+            }
+        }
+                
+         
+    
+    
+    }
+     
+        
+    
+
+
+    function toColor($color) {
+        if (!$color || ($color == 'none')) {
+            return false;
+        }
+        return array(
+            hexdec(substr($color,1,2)),
+            hexdec(substr($color,3,2)),
+            hexdec(substr($color,5,2)));
+        
+    }
+
+
+
+}
diff --git a/XML/SvgToPdf/Rect.php b/XML/SvgToPdf/Rect.php
new file mode 100644 (file)
index 0000000..e7ce229
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+
+/* output a rectangle
+
+*/
+
+class XML_SvgToPDF_Rect  extends XML_SvgToPDF_Base {
+    
+    var $xx = 0;
+    var $yy = 0;
+    var $nonprintable = false;
+    
+    function writePDF($pdf,$data) {
+        
+        //print_r(array("rect:", $this->x , $this->y , ':', $this->xx, $this->yy));
+        
+        $x =  $this->x   + @$this->xx;
+        $y =  $this->y  + @$this->yy;
+        
+        
+        
+        $pdf->setLineWidth($this->style['stroke-width']); 
+        $f = $this->toColor($this->style['fill']);
+        if ($f) {
+            $pdf->setFillColor($f[0],$f[1],$f[2]);
+        }
+        
+        $l = $this->toColor(@$this->style['stroke']);
+        if ($l) {
+            $pdf->setDrawColor($l[0],$l[1],$l[2]);
+        }
+        // no fill, no line = dont draw...
+        if (!$l && !$f) {
+            return;
+        }
+        XML_SvgToPDF::debug("RECT:" .($x/ 3.543307).',' .($y/ 3.543307). ','
+             .($this->width/ 3.543307).',' . ($this->height/ 3.543307));
+        $pdf->rect($x/ 3.543307,$y/ 3.543307,
+            $this->width/ 3.543307,$this->height/ 3.543307,($l ? 'D' : ''). ($f ? 'F' : ''));
+    
+    
+    
+    }
+
+
+    function toColor($color) {
+        if (!$color || ($color == 'none')) {
+            return false;
+        }
+        return array(
+            hexdec(substr($color,1,2)),
+            hexdec(substr($color,3,2)),
+            hexdec(substr($color,5,2)));
+        
+    }
+
+
+}
\ No newline at end of file
diff --git a/XML/SvgToPdf/Text.php b/XML/SvgToPdf/Text.php
new file mode 100644 (file)
index 0000000..dbbb4c0
--- /dev/null
@@ -0,0 +1,341 @@
+<?php
+
+
+class XML_SvgToPDF_Text  extends XML_SvgToPDF_Base { 
+
+    function fromXmlNode($node) {
+        
+        parent::fromXmlNode($node);
+        
+        // any text ???
+        if (empty($this->children) || empty($this->children[0]->content)) {
+            return;
+        }
+        
+        // modify the alignment of the if this block content of the first child is "=="
+        
+        if (substr($this->children[0]->content,0,2) == '==') {
+            $this->style['text-anchor'] = 'justify';
+            $this->children[0]->content = substr($this->children[0]->content,2);
+        }
+    }
+      
+    function transform() {
+        
+        parent::transform();
+        if (empty($this->transform)) {
+            return; 
+        }
+        if (preg_match('/scale\(([0-9e.-]+),([0-9e.-]+)\)/',$this->transform,$args)) {
+            $xscale = $args[1]; // do we use this??? = what do do about 'e'?
+            $yscale = $args[2];
+            $this->style['font-size'] *= $args[1];
+        }
+    }
+    
+        
+    
+    function writePDF($pdf,$data) {
+        // set up font.. 
+         
+        $font = strtolower($this->style['font-family']);
+        $font = trim($font, "'");
+        
+        static $map = array(
+            'times new roman' => 'times',
+            'courier new' => 'courier',
+            'arial' => 'arial',
+        );
+        if (isset($map[$font])) {
+            $font = $map[$font];
+        } else {
+           $font = 'times';
+        }
+        $ffont = $font;
+        if (preg_match('/big5/i',$this->style['font-family'])) {
+            $ffont = 'ARIALUNI';
+            $font = 'arial'; // default if not big4
+        }
+            
+        
+        
+        $weight =  '';
+        if ($this->style['font-weight'] == 'bold') {
+            $weight .=  'B';
+        }
+        if (@$this->style['font-style'] == 'italic') {
+            $weight .=  'I';
+        }
+        
+        if (@$this->style['text-decoration'] == 'underline') {
+            $weight .=  'U';
+        }
+        // size..
+        $size = $this->style['font-size']  * 0.85;
+        
+        $pdf->setFont($font,$weight,$size);
+     
+        switch(@$this->style['text-anchor']) {
+            case 'end':
+                $align = 'E';
+                break;
+            case 'middle':
+                $align = 'M';
+                break;
+            case 'justify':
+                $align = 'J';
+                $max = 0;
+                foreach ($this->children as $child) {
+                    if (!@$child->content) {
+                        continue;
+                    }
+                    $l = $pdf->getStringWidth($child->content);
+                    $max = ($l > $max) ? $l : $max;
+                }
+                // fudge factor!
+                $this->justifyLen = $max;
+                break;
+            default:
+                $align = 'L';
+            
+        }
+        
+        $f = $this->toColor(@$this->style['fill']);
+        if ($f) {
+            $pdf->setTextColor($f[0],$f[1],$f[2]);
+        }
+     
+     
+     
+        $yoffset = 0;
+        $x =  $this->x   + @$this->xx;
+        $y =  $this->y  + @$this->yy;
+        if (empty($this->children)) {
+            return;
+        }
+        $lineno = 0;
+        foreach($this->children as $i=>$c) {
+        
+            $xx = $c->x !== false ? $c->x + @$this->xx : $x;
+            $yy = $c->y !== false ? $c->y + @$this->yy : $y + ($lineno * $size * 1.3);
+            $lineno++;              
+            $val = $c->content;
+            if ($ffont == 'ARIALUNI') { //) && preg_match('/[\x7f-\xff]+/',$val)) {
+                $pdf->setFont('ARIALUNI' ,
+                            $weight,
+                            $size);
+            }
+            if (isset($c->args)) {
+                
+                $args = array();
+                foreach($c->args as $v) {
+                    if ($v == 'page') {
+                        $args[] = $pdf->PageNo();
+                        continue;
+                    }
+                    if ($v == 'nb') {
+                        $args[] = '{nb}';
+                        continue;
+                    }
+                    
+                    // echo "GET: $v : '$val'<BR>";
+                    $args[] = trim($this->getValue($data,trim($v))); // removes trailing CRLF...
+                }
+                
+                
+                $has_template = preg_match('/%s/', $val);
+                     
+                $val = trim(vsprintf($val,$args));
+                
+                if ($has_template && ($ffont == 'ARIALUNI') && preg_match('/[\x7f-\xff]+/',$val)) {
+                    //require_once  'Text/ZhDetect.php';
+                    //$detect = new Text_zhDetect;
+                    //$type = $detect->guess($val);
+                    //if ($v == 'S') {
+                       
+                    //    $val = @iconv('utf8', 'GB2312//IGNORE', $val);
+                    //    $pdf->setFont('GB' ,
+                    //        $weight,
+                    //        $size);
+                    //} else {
+//                        $val = @iconv('utf8', 'BIG5//IGNORE', $val);
+                        $pdf->setFont('ARIALUNI' ,
+                            $weight,
+                            $size);
+                   //}
+                }  else {
+                    $val = @iconv('utf8','ascii//ignore',$val);
+                }
+                
+                
+                
+                
+            }
+            $talign = $align;
+            if ((!@$this->children[$i+1] ||  !strlen(trim(@$this->children[$i+1]->content))) && ($align == 'J')) {
+                $talign = 'L';
+            }
+            
+            $yoffset += $this->multiLine($pdf, str_replace("\r", "", explode("\n",$val)),
+                    $xx/ 3.543307,
+                    ($yy / 3.543307) + $yoffset,
+                    ($size / 3.543307) + 1,
+                    $talign
+                );
+            
+             
+        }
+        
+        // now daraw
+    
+    }
+    
+    /**
+    * get a value from the data
+    *
+    * eg. $data = array(
+    *     'fred' => 'somevalue'
+    *
+    *  getValue ($data,'fred') returns 'somevalue' 
+    * 
+    * value can also be a method = eg. getFred()
+    *
+    * @param   object|array $data 
+    * @param   string $v key to get.
+    * 
+    *
+    * @return   string
+    * @access   public
+    */
+  
+    function getValue($data,$v) {
+        
+       // print_R(array("GET VALUE: ", $v));
+        // not a method:
+        
+        if ($v[strlen($v)-1]  != ')') {
+            
+            $data = (array) $data;
+            //echo "<PRE>";print_r(array_keys($data));
+            if (false === strpos($v,'.')) {
+                if (!isset($data[$v])) {
+                  //  echo "missing $v\n";
+                    return '';
+                }
+                if (is_array($data[$v]) || is_object($data[$v])) {
+                  //  echo "array/object $v\n";
+                    return '';
+                }
+                //echo "returning $v\n";
+                return $data[$v];
+            }
+            $dd = (array)$data;
+            foreach(explode('.', $v) as $vv) {
+                if (!is_array($dd)) {
+                    return '';   
+                }
+                if (!isset($dd[$vv])) {
+                    return '';
+                }
+                $dd = is_object($dd[$vv]) ? ((array) $dd[$vv]) : $dd[$vv];
+            }
+            //echo "ATTEMPT: $v: got $dd\n";
+            //exit;
+            if (is_array($dd) || is_object($dd)) {
+                return '';
+            }
+            return $dd;
+        }
+        // method !!!
+        if (!is_object($data)) {
+            return '';
+        }
+        $method = substr($v,0,-2);
+       
+        if (is_object($data) && method_exists($data,$method)) {
+           // echo "call $method<BR>";
+            $ret = $data->$method();
+            // echo "done $method $ret<BR>";
+            if (is_array($ret) || is_object($ret)) {
+                return '';
+            }
+            return '' . $ret;
+        }
+        
+        //echo 
+        //print_r($data);
+        
+        //exit;
+        return "no method $method in ".get_class($data);
+    
+    
+    }
+    
+    
+    function breakLines($pdf,$str,$x,$y,$h,$align) {
+        // do the estimation...
+        $len = strlen($str);
+        $total = $pdf->getStringWidth($str . '      ');
+         
+        $charsize = $total/$len;
+        
+        $max_chars = floor(($this->maxWidth / 3.543307) / $charsize);
+        //echo "LEN: $len, $total, $charsize, $max_chars";
+        $lines = explode("\n",wordwrap($str,$max_chars));
+         
+        return $this->multiLine($pdf,$lines,$x,$y,$h,$align);
+    }
+    var $maxWidth = false;
+    
+    function multiLine($pdf,$lines,$x,$y,$h,$align) {
+        // now dealing with mm
+        ///XML_SvgToPDF::debug("MULTILINE " .implode("\n",$lines) . " $x, $y, $h");
+        $yoffset  = 0;
+        $line = -1;
+        foreach ($lines as $l=>$v) {
+            $line++;
+            if ($this->maxWidth !== false && ($pdf->getStringWidth($v) > ($this->maxWidth / 3.543307))) {
+                $yoffset += $this->breakLines($pdf,$v,$x,$y + ($l * $h) + $yoffset, $h,$align);
+                continue;
+            }
+            XML_SvgToPDF::debug("TEXT: $x,$y, $l * $h + $yoffset,$v");
+            $xoffset = 0;
+            if ($align == 'M') { // center
+                $xoffset = -1 * ($pdf->getStringWidth($v) / 2);
+            }
+            if ($align == 'E') { // right/end
+                $xoffset = -1 * $pdf->getStringWidth($v);
+            }
+           
+            if ($align == 'J' ) { // justified (eg. started with ==
+                $this->justify($pdf, $x , $y + ($l * $h) + $yoffset , $v, $this->justifyLen);
+                continue;
+            }
+            $pdf->text(
+                $xoffset + $x ,
+                $y + ($l * $h) + $yoffset ,
+                $v);
+                
+        }
+        return   ($l * $h) + $yoffset;
+        
+    }
+        
+        
+    function justify($pdf,$x,$y,$text,$len) {
+        if (!strlen(trim($text))) {
+            return;
+        }
+        $bits = explode(' ', $text);
+        $textlen = $pdf->getStringWidth(implode('',$bits));
+        $spacesize = ($len - $textlen) / (count($bits) -1);
+        foreach($bits as $word) {
+            $pdf->text($x , $y ,$word );
+            $x += $spacesize + $pdf->getStringWidth($word);
+        }
+    }
+        
+     
+
+}
diff --git a/XML/SvgToPdf/Tspan.php b/XML/SvgToPdf/Tspan.php
new file mode 100644 (file)
index 0000000..2ac9b7b
--- /dev/null
@@ -0,0 +1,79 @@
+<?php
+
+/* the actual text container..
+
+does a quick bit of parsing to see if it a {template}var ..
+*/ 
+
+class XML_SvgToPDF_Tspan extends XML_SvgToPDF_Base { 
+
+    var $content = ''; // applies to tspan only..
+    var $x = false;
+    var $y = false;
+    var $args = array(); // arguments..
+    function fromXmlNode($node) {
+        parent::fromXmlNode($node);
+        $this->x = false;
+        $this->y = false;
+        $this->content = $node->textContent;
+        /*
+        if (isset($this->x)) {
+               unset($this->x); 
+        }
+        if (isset($this->y)) {
+               unset($this->y); 
+        }
+        */
+        static $trans = false;
+        if (!$trans) {
+            $trans = array_flip(get_html_translation_table(HTML_ENTITIES));
+        }
+        
+        if (strlen($this->content)) {
+            // convert &amp; etc. 
+            if (strpos($this->content,'&') !== false) {
+                $this->content = strtr($this->content, $trans);
+                $this->content = str_replace('&apos;',"'",$this->content);
+
+                $this->content =  preg_replace_callback('/&#(\d+);/m', array($this, 'content_replace'),
+                                    $this->content);
+            }
+            if (!empty($node->language)) {
+                // todo - other conversions....
+                $this->content = mb_convert_encoding($this->content,'BIG-5','UTF-8');
+
+            }
+            // dynamic parts..
+            if (false === strpos($this->content,'{')) {
+                return;
+            }
+            preg_match_all('/\{([a-z0-9_.]+(\(\))?)\}/i',$this->content,$matches);
+            $this->args = $matches[1];
+            foreach($this->args as $v) {
+                $this->content  = str_replace('{'.$v.'}', '%s',$this->content);
+            }
+            //$this->content = preg_replace('/\{('.implode('|',$matches[1]).')\}/','%s',$this->content);
+        }
+        
+    
+    }
+    
+    function content_replace($matches) { // php5.2 needs this to be a function... 
+            return chr($matches[1]);
+    }
+    
+    
+    function shift() // disable shifting on text
+    {
+        return;
+    }
+    function transform() 
+    {
+        
+    }
+   
+
+
+
+}