Merge pull request #3211 from Icinga/feature/php-svg-lib-v0-3

Upgrade php-svg-lib to v0.3
This commit is contained in:
lippserd 2017-12-15 09:21:00 +01:00 committed by GitHub
commit a71037cd5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 475 additions and 176 deletions

View File

@ -10,7 +10,7 @@ mkdir -p lib/php-font-lib
tar xzf php-font-lib-0.4.tar.gz --strip-components 1 -C lib/php-font-lib php-font-lib-0.4/{src,LICENSE} tar xzf php-font-lib-0.4.tar.gz --strip-components 1 -C lib/php-font-lib php-font-lib-0.4/{src,LICENSE}
rm php-font-lib-0.4.tar.gz rm php-font-lib-0.4.tar.gz
curl https://codeload.github.com/PhenX/php-svg-lib/tar.gz/v0.1 -o php-svg-lib-0.1.tar.gz curl https://codeload.github.com/PhenX/php-svg-lib/tar.gz/v0.3 -o php-svg-lib-0.3.tar.gz
mkdir -p lib/php-svg-lib mkdir -p lib/php-svg-lib
tar xzf php-svg-lib-0.1.tar.gz --strip-components 1 -C lib/php-svg-lib php-svg-lib-0.1/src tar xzf php-svg-lib-0.3.tar.gz --strip-components 1 -C lib/php-svg-lib php-svg-lib-0.3/src
rm php-svg-lib-0.1.tar.gz rm php-svg-lib-0.3.tar.gz

View File

@ -12,6 +12,7 @@ class DefaultStyle extends Style
{ {
public $color = ''; public $color = '';
public $opacity = 1.0; public $opacity = 1.0;
public $display = 'inline';
public $fill = 'black'; public $fill = 'black';
public $fillOpacity = 1.0; public $fillOpacity = 1.0;

View File

@ -14,6 +14,7 @@ use Svg\Tag\Anchor;
use Svg\Tag\Circle; use Svg\Tag\Circle;
use Svg\Tag\Ellipse; use Svg\Tag\Ellipse;
use Svg\Tag\Group; use Svg\Tag\Group;
use Svg\Tag\ClipPath;
use Svg\Tag\Image; use Svg\Tag\Image;
use Svg\Tag\Line; use Svg\Tag\Line;
use Svg\Tag\LinearGradient; use Svg\Tag\LinearGradient;
@ -23,11 +24,13 @@ use Svg\Tag\Polyline;
use Svg\Tag\Rect; use Svg\Tag\Rect;
use Svg\Tag\Stop; use Svg\Tag\Stop;
use Svg\Tag\Text; use Svg\Tag\Text;
use Svg\Tag\StyleTag;
use Svg\Tag\UseTag;
class Document extends AbstractTag class Document extends AbstractTag
{ {
protected $filename; protected $filename;
protected $inDefs = false; public $inDefs = false;
protected $x; protected $x;
protected $y; protected $y;
@ -50,6 +53,9 @@ class Document extends AbstractTag
/** @var AbstractTag[] */ /** @var AbstractTag[] */
protected $defs = array(); protected $defs = array();
/** @var \Sabberworm\CSS\CSSList\Document[] */
protected $styleSheets = array();
public function loadFile($filename) public function loadFile($filename)
{ {
$this->filename = $filename; $this->filename = $filename;
@ -132,12 +138,12 @@ class Document extends AbstractTag
public function handleSizeAttributes($attributes){ public function handleSizeAttributes($attributes){
if ($this->width === null) { if ($this->width === null) {
if (isset($attributes["width"])) { if (isset($attributes["width"])) {
$width = (int)$attributes["width"]; $width = Style::convertSize($attributes["width"], 400);
$this->width = $width; $this->width = $width;
} }
if (isset($attributes["height"])) { if (isset($attributes["height"])) {
$height = (int)$attributes["height"]; $height = Style::convertSize($attributes["height"], 300);
$this->height = $height; $this->height = $height;
} }
@ -166,17 +172,35 @@ class Document extends AbstractTag
); );
} }
protected function getDocument(){ public function getDocument(){
return $this; return $this;
} }
protected function before($attribs) /**
* Append a style sheet
*
* @param \Sabberworm\CSS\CSSList\Document $stylesheet
*/
public function appendStyleSheet($stylesheet) {
$this->styleSheets[] = $stylesheet;
}
/**
* Get the document style sheets
*
* @return \Sabberworm\CSS\CSSList\Document[]
*/
public function getStyleSheets() {
return $this->styleSheets;
}
protected function before($attributes)
{ {
$surface = $this->getSurface(); $surface = $this->getSurface();
$style = new DefaultStyle(); $style = new DefaultStyle();
$style->inherit($this); $style->inherit($this);
$style->fromAttributes($attribs); $style->fromAttributes($attributes);
$this->setStyle($style); $this->setStyle($style);
@ -211,6 +235,12 @@ class Document extends AbstractTag
$this->handleSizeAttributes($attributes); $this->handleSizeAttributes($attributes);
} }
public function getDef($id) {
$id = ltrim($id, "#");
return isset($this->defs[$id]) ? $this->defs[$id] : null;
}
private function _tagStart($parser, $name, $attributes) private function _tagStart($parser, $name, $attributes)
{ {
$this->x = 0; $this->x = 0;
@ -227,7 +257,7 @@ class Document extends AbstractTag
case 'svg': case 'svg':
if (count($this->attributes)) { if (count($this->attributes)) {
$tag = new Group($this); $tag = new Group($this, $name);
} }
else { else {
$tag = $this; $tag = $this;
@ -236,72 +266,90 @@ class Document extends AbstractTag
break; break;
case 'path': case 'path':
$tag = new Path($this); $tag = new Path($this, $name);
break; break;
case 'rect': case 'rect':
$tag = new Rect($this); $tag = new Rect($this, $name);
break; break;
case 'circle': case 'circle':
$tag = new Circle($this); $tag = new Circle($this, $name);
break; break;
case 'ellipse': case 'ellipse':
$tag = new Ellipse($this); $tag = new Ellipse($this, $name);
break; break;
case 'image': case 'image':
$tag = new Image($this); $tag = new Image($this, $name);
break; break;
case 'line': case 'line':
$tag = new Line($this); $tag = new Line($this, $name);
break; break;
case 'polyline': case 'polyline':
$tag = new Polyline($this); $tag = new Polyline($this, $name);
break; break;
case 'polygon': case 'polygon':
$tag = new Polygon($this); $tag = new Polygon($this, $name);
break; break;
case 'lineargradient': case 'lineargradient':
$tag = new LinearGradient($this); $tag = new LinearGradient($this, $name);
break; break;
case 'radialgradient': case 'radialgradient':
$tag = new LinearGradient($this); $tag = new LinearGradient($this, $name);
break; break;
case 'stop': case 'stop':
$tag = new Stop($this); $tag = new Stop($this, $name);
break;
case 'style':
$tag = new StyleTag($this, $name);
break; break;
case 'a': case 'a':
$tag = new Anchor($this); $tag = new Anchor($this, $name);
break; break;
case 'g': case 'g':
$tag = new Group($this); case 'symbol':
$tag = new Group($this, $name);
break;
case 'clippath':
$tag = new ClipPath($this, $name);
break;
case 'use':
$tag = new UseTag($this, $name);
break; break;
case 'text': case 'text':
$tag = new Text($this); $tag = new Text($this, $name);
break; break;
} }
if ($tag) { if ($tag) {
if (!$this->inDefs) { if (isset($attributes["id"])) {
$this->stack[] = $tag; $this->defs[$attributes["id"]] = $tag;
$tag->handle($attributes);
} }
else { else {
if (isset($attributes["id"])) { /** @var AbstractTag $top */
$this->defs[$attributes["id"]] = $tag; $top = end($this->stack);
if ($top && $top != $tag) {
$top->children[] = $tag;
} }
} }
$this->stack[] = $tag;
$tag->handle($attributes);
} else { } else {
echo "Unknown: '$name'\n"; echo "Unknown: '$name'\n";
} }
@ -311,7 +359,7 @@ class Document extends AbstractTag
{ {
$stack_top = end($this->stack); $stack_top = end($this->stack);
if ($stack_top instanceof Text) { if ($stack_top instanceof Text || $stack_top instanceof StyleTag) {
$stack_top->appendText($data); $stack_top->appendText($data);
} }
} }
@ -337,16 +385,18 @@ class Document extends AbstractTag
case 'radialgradient': case 'radialgradient':
case 'lineargradient': case 'lineargradient':
case 'stop': case 'stop':
case 'style':
case 'text': case 'text':
case 'g': case 'g':
case 'symbol':
case 'clippath':
case 'use':
case 'a': case 'a':
if (!$this->inDefs) { $tag = array_pop($this->stack);
$tag = array_pop($this->stack);
}
break; break;
} }
if ($tag) { if (!$this->inDefs && $tag) {
$tag->handleEnd(); $tag->handleEnd();
} }
} }

View File

@ -2,7 +2,7 @@
/** /**
* @package php-svg-lib * @package php-svg-lib
* @link http://github.com/PhenX/php-svg-lib * @link http://github.com/PhenX/php-svg-lib
* @author Fabien Ménager <fabien.menager@gmail.com> * @author Fabien M<EFBFBD>nager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/ */
@ -20,6 +20,7 @@ class Style
public $color; public $color;
public $opacity; public $opacity;
public $display;
public $fill; public $fill;
public $fillOpacity; public $fillOpacity;
@ -45,6 +46,7 @@ class Style
return array( return array(
'color' => array('color', self::TYPE_COLOR), 'color' => array('color', self::TYPE_COLOR),
'opacity' => array('opacity', self::TYPE_NUMBER), 'opacity' => array('opacity', self::TYPE_NUMBER),
'display' => array('display', self::TYPE_NAME),
'fill' => array('fill', self::TYPE_COLOR), 'fill' => array('fill', self::TYPE_COLOR),
'fill-opacity' => array('fillOpacity', self::TYPE_NUMBER), 'fill-opacity' => array('fillOpacity', self::TYPE_NUMBER),
@ -61,7 +63,7 @@ class Style
'font-family' => array('fontFamily', self::TYPE_NAME), 'font-family' => array('fontFamily', self::TYPE_NAME),
'font-size' => array('fontSize', self::TYPE_NUMBER), 'font-size' => array('fontSize', self::TYPE_NUMBER),
'font-weight' => array('fontWeight', self::TYPE_NUMBER), 'font-weight' => array('fontWeight', self::TYPE_NAME),
'font-style' => array('fontStyle', self::TYPE_NAME), 'font-style' => array('fontStyle', self::TYPE_NAME),
'text-anchor' => array('textAnchor', self::TYPE_NAME), 'text-anchor' => array('textAnchor', self::TYPE_NAME),
); );
@ -95,6 +97,52 @@ class Style
} }
} }
public function fromStyleSheets(AbstractTag $tag, $attributes) {
$class = isset($attributes["class"]) ? preg_split('/\s+/', trim($attributes["class"])) : null;
$stylesheets = $tag->getDocument()->getStyleSheets();
$styles = array();
foreach ($stylesheets as $_sc) {
/** @var \Sabberworm\CSS\RuleSet\DeclarationBlock $_decl */
foreach ($_sc->getAllDeclarationBlocks() as $_decl) {
/** @var \Sabberworm\CSS\Property\Selector $_selector */
foreach ($_decl->getSelectors() as $_selector) {
$_selector = $_selector->getSelector();
// Match class name
if ($class !== null) {
foreach ($class as $_class) {
if ($_selector === ".$_class") {
/** @var \Sabberworm\CSS\Rule\Rule $_rule */
foreach ($_decl->getRules() as $_rule) {
$styles[$_rule->getRule()] = $_rule->getValue() . "";
}
break 2;
}
}
}
// Match tag name
if ($_selector === $tag->tagName) {
/** @var \Sabberworm\CSS\Rule\Rule $_rule */
foreach ($_decl->getRules() as $_rule) {
$styles[$_rule->getRule()] = $_rule->getValue() . "";
}
break;
}
}
}
}
$this->fillStyles($styles);
}
protected function fillStyles($styles) protected function fillStyles($styles)
{ {
foreach ($this->getStyleMap() as $from => $spec) { foreach ($this->getStyleMap() as $from => $spec) {
@ -125,7 +173,7 @@ class Style
{ {
$color = strtolower(trim($color)); $color = strtolower(trim($color));
$parts = preg_split('/\s+/', $color, 2); $parts = preg_split('/[^,]\s+/', $color, 2);
if (count($parts) == 2) { if (count($parts) == 2) {
$color = $parts[1]; $color = $parts[1];
@ -218,6 +266,19 @@ class Style
); );
} }
// Gradient
if (strpos($color, "url(#") !== false) {
$i = strpos($color, "(");
$j = strpos($color, ")");
// Bad url format
if ($i === false || $j === false) {
return null;
}
return trim(substr($color, $i + 1, $j - $i - 1));
}
return null; return null;
} }
@ -298,13 +359,13 @@ class Style
/** /**
* Convert a size to a float * Convert a size to a float
* *
* @param string $size SVG size * @param string $size SVG size
* @param float $dpi DPI * @param float $dpi DPI
* @param float $fontsize Reference font size * @param float $referenceSize Reference size
* *
* @return float|null * @return float|null
*/ */
static function convertSize($size, $dpi = 72.0, $fontsize = 11.0) { static function convertSize($size, $referenceSize = 11.0, $dpi = 96.0) {
$size = trim(strtolower($size)); $size = trim(strtolower($size));
if (is_numeric($size)) { if (is_numeric($size)) {
@ -319,12 +380,16 @@ class Style
return floatval(substr($size, 0, $pos)); return floatval(substr($size, 0, $pos));
} }
if ($pos = strpos($size, "cm")) {
return floatval(substr($size, 0, $pos)) * $dpi;
}
if ($pos = strpos($size, "%")) { if ($pos = strpos($size, "%")) {
return $fontsize * substr($size, 0, $pos) / 100; return $referenceSize * substr($size, 0, $pos) / 100;
} }
if ($pos = strpos($size, "em")) { if ($pos = strpos($size, "em")) {
return $fontsize * substr($size, 0, $pos); return $referenceSize * substr($size, 0, $pos);
} }
// TODO cm, mm, pc, in, etc // TODO cm, mm, pc, in, etc

View File

@ -2,7 +2,7 @@
/** /**
* @package php-svg-lib * @package php-svg-lib
* @link http://github.com/PhenX/php-svg-lib * @link http://github.com/PhenX/php-svg-lib
* @author Fabien Ménager <fabien.menager@gmail.com> * @author Fabien M<EFBFBD>nager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/ */
@ -15,7 +15,7 @@ class SurfaceCpdf implements SurfaceInterface
{ {
const DEBUG = false; const DEBUG = false;
/** @var Cpdf */ /** @var \CPdf\CPdf */
private $canvas; private $canvas;
private $width; private $width;
@ -33,7 +33,9 @@ class SurfaceCpdf implements SurfaceInterface
$h = $dimensions["height"]; $h = $dimensions["height"];
if (!$canvas) { if (!$canvas) {
$canvas = new CPdf(array(0, 0, $w, $h)); $canvas = new \CPdf\CPdf(array(0, 0, $w, $h));
$refl = new \ReflectionClass($canvas);
$canvas->fontcache = realpath(dirname($refl->getFileName()) . "/../../fonts/")."/";
} }
// Flip PDF coordinate system so that the origin is in // Flip PDF coordinate system so that the origin is in
@ -141,7 +143,7 @@ class SurfaceCpdf implements SurfaceInterface
public function strokeText($text, $x, $y, $maxWidth = null) public function strokeText($text, $x, $y, $maxWidth = null)
{ {
if (self::DEBUG) echo __FUNCTION__ . "\n"; if (self::DEBUG) echo __FUNCTION__ . "\n";
// TODO: Implement drawImage() method. $this->canvas->addText($x, $y, $this->style->fontSize, $text);
} }
public function drawImage($image, $sx, $sy, $sw = null, $sh = null, $dx = null, $dy = null, $dw = null, $dh = null) public function drawImage($image, $sx, $sy, $sw = null, $sh = null, $dx = null, $dy = null, $dw = null, $dh = null)
@ -149,9 +151,22 @@ class SurfaceCpdf implements SurfaceInterface
if (self::DEBUG) echo __FUNCTION__ . "\n"; if (self::DEBUG) echo __FUNCTION__ . "\n";
if (strpos($image, "data:") === 0) { if (strpos($image, "data:") === 0) {
$data = substr($image, strpos($image, ";") + 1); $parts = explode(',', $image, 2);
if (strpos($data, "base64") === 0) {
$data = base64_decode(substr($data, 7)); $data = $parts[1];
$base64 = false;
$token = strtok($parts[0], ';');
while ($token !== false) {
if ($token == 'base64') {
$base64 = true;
}
$token = strtok(';');
}
if ($base64) {
$data = base64_decode($data);
} }
} }
else { else {
@ -281,6 +296,9 @@ class SurfaceCpdf implements SurfaceInterface
return; return;
} }
$rx = min($rx, $w / 2);
$rx = min($rx, $h / 2);
/* Define a path for a rectangle with corners rounded by a given radius. /* Define a path for a rectangle with corners rounded by a given radius.
* Start from the lower left corner and proceed counterclockwise. * Start from the lower left corner and proceed counterclockwise.
*/ */
@ -340,7 +358,7 @@ class SurfaceCpdf implements SurfaceInterface
{ {
if (self::DEBUG) echo __FUNCTION__ . "\n"; if (self::DEBUG) echo __FUNCTION__ . "\n";
$style = $this->getStyle(); $style = $this->getStyle();
$this->getFont($style->fontFamily, $style->fontStyle); $this->setFont($style->fontFamily, $style->fontStyle, $style->fontWeight);
return $this->canvas->getTextWidth($this->getStyle()->fontSize, $text); return $this->canvas->getTextWidth($this->getStyle()->fontSize, $text);
} }
@ -358,12 +376,12 @@ class SurfaceCpdf implements SurfaceInterface
$this->style = $style; $this->style = $style;
$canvas = $this->canvas; $canvas = $this->canvas;
if ($stroke = $style->stroke) { if (is_array($style->stroke) && $stroke = $style->stroke) {
$canvas->setStrokeColor(array($stroke[0]/255, $stroke[1]/255, $stroke[2]/255), true); $canvas->setStrokeColor(array((float)$stroke[0]/255, (float)$stroke[1]/255, (float)$stroke[2]/255), true);
} }
if ($fill = $style->fill) { if (is_array($style->fill) && $fill = $style->fill) {
$canvas->setColor(array($fill[0]/255, $fill[1]/255, $fill[2]/255), true); $canvas->setColor(array((float)$fill[0]/255, (float)$fill[1]/255, (float)$fill[2]/255), true);
} }
if ($fillRule = strtolower($style->fillRule)) { if ($fillRule = strtolower($style->fillRule)) {
@ -392,17 +410,22 @@ class SurfaceCpdf implements SurfaceInterface
} }
} }
$dashArray = null;
if ($style->strokeDasharray) {
$dashArray = preg_split('/\s*,\s*/', $style->strokeDasharray);
}
$canvas->setLineStyle( $canvas->setLineStyle(
$style->strokeWidth, $style->strokeWidth,
$style->strokeLinecap, $style->strokeLinecap,
$style->strokeLinejoin $style->strokeLinejoin,
$dashArray
); );
//$font = $this->getFont($style->fontFamily, $style->fontStyle); $this->setFont($style->fontFamily, $style->fontStyle, $style->fontWeight);
//$canvas->setfont($font, $style->fontSize);
} }
private function getFont($family, $style) public function setFont($family, $style, $weight)
{ {
$map = array( $map = array(
"serif" => "Times", "serif" => "Times",
@ -415,11 +438,49 @@ class SurfaceCpdf implements SurfaceInterface
"verdana" => "Helvetica", "verdana" => "Helvetica",
); );
$styleMap = array(
'Helvetica' => array(
'b' => 'Helvetica-Bold',
'i' => 'Helvetica-Oblique',
'bi' => 'Helvetica-BoldOblique',
),
'Courier' => array(
'b' => 'Courier-Bold',
'i' => 'Courier-Oblique',
'bi' => 'Courier-BoldOblique',
),
'Times' => array(
'' => 'Times-Roman',
'b' => 'Times-Bold',
'i' => 'Times-Italic',
'bi' => 'Times-BoldItalic',
),
);
$family = strtolower($family); $family = strtolower($family);
$style = strtolower($style);
$weight = strtolower($weight);
if (isset($map[$family])) { if (isset($map[$family])) {
$family = $map[$family]; $family = $map[$family];
} }
//return $this->canvas->load_font($family, "unicode", "fontstyle=$style"); if (isset($styleMap[$family])) {
$key = "";
if ($weight === "bold" || $weight === "bolder" || (is_numeric($weight) && $weight >= 600)) {
$key .= "b";
}
if ($style === "italic" || $style === "oblique") {
$key .= "i";
}
if (isset($styleMap[$family][$key])) {
$family = $styleMap[$family][$key];
}
}
$this->canvas->selectFont("$family.afm");
} }
} }

View File

@ -2,7 +2,7 @@
/** /**
* @package php-svg-lib * @package php-svg-lib
* @link http://github.com/PhenX/php-svg-lib * @link http://github.com/PhenX/php-svg-lib
* @author Fabien Ménager <fabien.menager@gmail.com> * @author Fabien M<EFBFBD>nager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/ */
@ -202,7 +202,7 @@ class SurfaceGmagick implements SurfaceInterface
$this->fill(); $this->fill();
} }
public function rect($x, $y, $w, $h) public function rect($x, $y, $w, $h, $rx = 0, $ry = 0)
{ {
if (self::DEBUG) echo __FUNCTION__ . "\n"; if (self::DEBUG) echo __FUNCTION__ . "\n";
$this->canvas->rect($x, $y, $w, $h); $this->canvas->rect($x, $y, $w, $h);
@ -256,11 +256,11 @@ class SurfaceGmagick implements SurfaceInterface
$this->style = $style; $this->style = $style;
$canvas = $this->canvas; $canvas = $this->canvas;
if ($stroke = $style->stroke) { if (is_array($style->stroke) && $stroke = $style->stroke) {
$canvas->setcolor("stroke", "rgb", $stroke[0] / 255, $stroke[1] / 255, $stroke[2] / 255, null); $canvas->setcolor("stroke", "rgb", $stroke[0] / 255, $stroke[1] / 255, $stroke[2] / 255, null);
} }
if ($fill = $style->fill) { if (is_array($style->fill) && $fill = $style->fill) {
// $canvas->setcolor("fill", "rgb", $fill[0] / 255, $fill[1] / 255, $fill[2] / 255, null); // $canvas->setcolor("fill", "rgb", $fill[0] / 255, $fill[1] / 255, $fill[2] / 255, null);
} }
@ -300,4 +300,9 @@ class SurfaceGmagick implements SurfaceInterface
return $this->canvas->load_font($family, "unicode", "fontstyle=$style"); return $this->canvas->load_font($family, "unicode", "fontstyle=$style");
} }
public function setFont($family, $style, $weight)
{
// TODO: Implement setFont() method.
}
} }

View File

@ -2,7 +2,7 @@
/** /**
* @package php-svg-lib * @package php-svg-lib
* @link http://github.com/PhenX/php-svg-lib * @link http://github.com/PhenX/php-svg-lib
* @author Fabien Ménager <fabien.menager@gmail.com> * @author Fabien M<EFBFBD>nager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/ */
@ -85,4 +85,6 @@ interface SurfaceInterface
* @return Style * @return Style
*/ */
public function getStyle(); public function getStyle();
public function setFont($family, $style, $weight);
} }

View File

@ -2,7 +2,7 @@
/** /**
* @package php-svg-lib * @package php-svg-lib
* @link http://github.com/PhenX/php-svg-lib * @link http://github.com/PhenX/php-svg-lib
* @author Fabien Ménager <fabien.menager@gmail.com> * @author Fabien M<EFBFBD>nager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/ */
@ -315,7 +315,7 @@ class SurfacePDFLib implements SurfaceInterface
$this->style = $style; $this->style = $style;
$canvas = $this->canvas; $canvas = $this->canvas;
if (($stroke = $style->stroke) && $stroke !== "none") { if ($stroke = $style->stroke && is_array($style->stroke)) {
$canvas->setcolor( $canvas->setcolor(
"stroke", "stroke",
"rgb", "rgb",
@ -326,7 +326,7 @@ class SurfacePDFLib implements SurfaceInterface
); );
} }
if (($fill = $style->fill) && $fill !== "none") { if ($fill = $style->fill && is_array($style->fill)) {
$canvas->setcolor( $canvas->setcolor(
"fill", "fill",
"rgb", "rgb",
@ -414,4 +414,9 @@ class SurfacePDFLib implements SurfaceInterface
return $this->canvas->load_font($family, "unicode", "fontstyle=$style"); return $this->canvas->load_font($family, "unicode", "fontstyle=$style");
} }
public function setFont($family, $style, $weight)
{
// TODO: Implement setFont() method.
}
} }

View File

@ -16,6 +16,8 @@ abstract class AbstractTag
/** @var Document */ /** @var Document */
protected $document; protected $document;
public $tagName;
/** @var Style */ /** @var Style */
protected $style; protected $style;
@ -23,12 +25,16 @@ abstract class AbstractTag
protected $hasShape = true; protected $hasShape = true;
public function __construct(Document $document) /** @var self[] */
protected $children = array();
public function __construct(Document $document, $tagName)
{ {
$this->document = $document; $this->document = $document;
$this->tagName = $tagName;
} }
protected function getDocument(){ public function getDocument(){
return $this->document; return $this->document;
} }
@ -48,25 +54,29 @@ abstract class AbstractTag
return null; return null;
} }
public final function handle($attributes) public function handle($attributes)
{ {
$this->attributes = $attributes; $this->attributes = $attributes;
$this->before($attributes); if (!$this->getDocument()->inDefs) {
$this->start($attributes); $this->before($attributes);
$this->start($attributes);
}
} }
public final function handleEnd() public function handleEnd()
{ {
$this->end(); if (!$this->getDocument()->inDefs) {
$this->after(); $this->end();
$this->after();
}
} }
protected function before($attribs) protected function before($attributes)
{ {
} }
protected function start($attribs) protected function start($attributes)
{ {
} }
@ -86,23 +96,43 @@ abstract class AbstractTag
protected function setStyle(Style $style) protected function setStyle(Style $style)
{ {
$this->style = $style; $this->style = $style;
if ($style->display === "none") {
$this->hasShape = false;
}
} }
/** /**
* @return \Svg\Style * @return Style
*/ */
public function getStyle() public function getStyle()
{ {
return $this->style; return $this->style;
} }
protected function applyTransform($attribs) /**
* Make a style object from the tag and its attributes
*
* @param array $attributes
*
* @return Style
*/
protected function makeStyle($attributes) {
$style = new Style();
$style->inherit($this);
$style->fromStyleSheets($this, $attributes);
$style->fromAttributes($attributes);
return $style;
}
protected function applyTransform($attributes)
{ {
if (isset($attribs["transform"])) { if (isset($attributes["transform"])) {
$surface = $this->document->getSurface(); $surface = $this->document->getSurface();
$transform = $attribs["transform"]; $transform = $attributes["transform"];
$match = array(); $match = array();
preg_match_all( preg_match_all(

View File

@ -14,16 +14,16 @@ class Circle extends Shape
protected $cy = 0; protected $cy = 0;
protected $r; protected $r;
public function start($attribs) public function start($attributes)
{ {
if (isset($attribs['cx'])) { if (isset($attributes['cx'])) {
$this->cx = $attribs['cx']; $this->cx = $attributes['cx'];
} }
if (isset($attribs['cy'])) { if (isset($attributes['cy'])) {
$this->cy = $attribs['cy']; $this->cy = $attributes['cy'];
} }
if (isset($attribs['r'])) { if (isset($attributes['r'])) {
$this->r = $attribs['r']; $this->r = $attributes['r'];
} }
$this->document->getSurface()->circle($this->cx, $this->cy, $this->r); $this->document->getSurface()->circle($this->cx, $this->cy, $this->r);

View File

@ -15,21 +15,21 @@ class Ellipse extends Shape
protected $rx = 0; protected $rx = 0;
protected $ry = 0; protected $ry = 0;
public function start($attribs) public function start($attributes)
{ {
parent::start($attribs); parent::start($attributes);
if (isset($attribs['cx'])) { if (isset($attributes['cx'])) {
$this->cx = $attribs['cx']; $this->cx = $attributes['cx'];
} }
if (isset($attribs['cy'])) { if (isset($attributes['cy'])) {
$this->cy = $attribs['cy']; $this->cy = $attributes['cy'];
} }
if (isset($attribs['rx'])) { if (isset($attributes['rx'])) {
$this->rx = $attribs['rx']; $this->rx = $attributes['rx'];
} }
if (isset($attribs['ry'])) { if (isset($attributes['ry'])) {
$this->ry = $attribs['ry']; $this->ry = $attributes['ry'];
} }
$this->document->getSurface()->ellipse($this->cx, $this->cy, $this->rx, $this->ry, 0, 0, 360, false); $this->document->getSurface()->ellipse($this->cx, $this->cy, $this->rx, $this->ry, 0, 0, 360, false);

View File

@ -12,21 +12,18 @@ use Svg\Style;
class Group extends AbstractTag class Group extends AbstractTag
{ {
protected function before($attribs) protected function before($attributes)
{ {
$surface = $this->document->getSurface(); $surface = $this->document->getSurface();
$surface->save(); $surface->save();
$style = new Style(); $style = $this->makeStyle($attributes);
$style->inherit($this);
$style->fromAttributes($attribs);
$this->setStyle($style); $this->setStyle($style);
$surface->setStyle($style); $surface->setStyle($style);
$this->applyTransform($attribs); $this->applyTransform($attributes);
} }
protected function after() protected function after()

View File

@ -16,38 +16,38 @@ class Image extends AbstractTag
protected $height = 0; protected $height = 0;
protected $href = null; protected $href = null;
protected function before($attribs) protected function before($attributes)
{ {
parent::before($attribs); parent::before($attributes);
$surface = $this->document->getSurface(); $surface = $this->document->getSurface();
$surface->save(); $surface->save();
$this->applyTransform($attribs); $this->applyTransform($attributes);
} }
public function start($attribs) public function start($attributes)
{ {
$document = $this->document; $document = $this->document;
$height = $this->document->getHeight(); $height = $this->document->getHeight();
$this->y = $height; $this->y = $height;
if (isset($attribs['x'])) { if (isset($attributes['x'])) {
$this->x = $attribs['x']; $this->x = $attributes['x'];
} }
if (isset($attribs['y'])) { if (isset($attributes['y'])) {
$this->y = $height - $attribs['y']; $this->y = $height - $attributes['y'];
} }
if (isset($attribs['width'])) { if (isset($attributes['width'])) {
$this->width = $attribs['width']; $this->width = $attributes['width'];
} }
if (isset($attribs['height'])) { if (isset($attributes['height'])) {
$this->height = $attribs['height']; $this->height = $attributes['height'];
} }
if (isset($attribs['xlink:href'])) { if (isset($attributes['xlink:href'])) {
$this->href = $attribs['xlink:href']; $this->href = $attributes['xlink:href'];
} }
$document->getSurface()->transform(1, 0, 0, -1, 0, $height); $document->getSurface()->transform(1, 0, 0, -1, 0, $height);

View File

@ -16,19 +16,19 @@ class Line extends Shape
protected $x2 = 0; protected $x2 = 0;
protected $y2 = 0; protected $y2 = 0;
public function start($attribs) public function start($attributes)
{ {
if (isset($attribs['x1'])) { if (isset($attributes['x1'])) {
$this->x1 = $attribs['x1']; $this->x1 = $attributes['x1'];
} }
if (isset($attribs['y1'])) { if (isset($attributes['y1'])) {
$this->y1 = $attribs['y1']; $this->y1 = $attributes['y1'];
} }
if (isset($attribs['x2'])) { if (isset($attributes['x2'])) {
$this->x2 = $attribs['x2']; $this->x2 = $attributes['x2'];
} }
if (isset($attribs['y2'])) { if (isset($attributes['y2'])) {
$this->y2 = $attribs['y2']; $this->y2 = $attributes['y2'];
} }
$surface = $this->document->getSurface(); $surface = $this->document->getSurface();

View File

@ -9,10 +9,75 @@
namespace Svg\Tag; namespace Svg\Tag;
use Svg\Gradient\Stop;
use Svg\Style;
class LinearGradient extends AbstractTag class LinearGradient extends AbstractTag
{ {
public function start($attribs) protected $x1;
{ protected $y1;
protected $x2;
protected $y2;
/** @var Stop[] */
protected $stops = array();
public function start($attributes)
{
parent::start($attributes);
if (isset($attributes['x1'])) {
$this->x1 = $attributes['x1'];
}
if (isset($attributes['y1'])) {
$this->y1 = $attributes['y1'];
}
if (isset($attributes['x2'])) {
$this->x2 = $attributes['x2'];
}
if (isset($attributes['y2'])) {
$this->y2 = $attributes['y2'];
}
}
public function getStops() {
if (empty($this->stops)) {
foreach ($this->children as $_child) {
if ($_child->tagName != "stop") {
continue;
}
$_stop = new Stop();
$_attributes = $_child->attributes;
// Style
if (isset($_attributes["style"])) {
$_style = Style::parseCssStyle($_attributes["style"]);
if (isset($_style["stop-color"])) {
$_stop->color = Style::parseColor($_style["stop-color"]);
}
if (isset($_style["stop-opacity"])) {
$_stop->opacity = max(0, min(1.0, $_style["stop-opacity"]));
}
}
// Attributes
if (isset($_attributes["offset"])) {
$_stop->offset = $_attributes["offset"];
}
if (isset($_attributes["stop-color"])) {
$_stop->color = Style::parseColor($_attributes["stop-color"]);
}
if (isset($_attributes["stop-opacity"])) {
$_stop->opacity = max(0, min(1.0, $_attributes["stop-opacity"]));
}
$this->stops[] = $_stop;
}
}
return $this->stops;
} }
} }

View File

@ -29,23 +29,22 @@ class Path extends Shape
'M' => 'L', 'M' => 'L',
); );
public function start($attribs) public function start($attributes)
{ {
if (!isset($attribs['d'])) { if (!isset($attributes['d'])) {
$this->hasShape = false; $this->hasShape = false;
return; return;
} }
$commands = array(); $commands = array();
preg_match_all('/([MZLHVCSQTAmzlhvcsqta])([eE ,\-.\d]+)*/', $attribs['d'], $commands, PREG_SET_ORDER); preg_match_all('/([MZLHVCSQTAmzlhvcsqta])([eE ,\-.\d]+)*/', $attributes['d'], $commands, PREG_SET_ORDER);
$path = array(); $path = array();
foreach ($commands as $c) { foreach ($commands as $c) {
if (count($c) == 3) { if (count($c) == 3) {
$arguments = array(); $arguments = array();
preg_match_all('/[\-^]?[\d.]+(e[\-]?[\d]+){0,1}/i', $c[2], $arguments, PREG_PATTERN_ORDER); preg_match_all('/([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:e[-+]?\d+)?)/i', $c[2], $arguments, PREG_PATTERN_ORDER);
$item = $arguments[0]; $item = $arguments[0];
$commandLower = strtolower($c[1]); $commandLower = strtolower($c[1]);

View File

@ -10,10 +10,10 @@ namespace Svg\Tag;
class Polygon extends Shape class Polygon extends Shape
{ {
public function start($attribs) public function start($attributes)
{ {
$tmp = array(); $tmp = array();
preg_match_all('/([\-]*[0-9\.]+)/', $attribs['points'], $tmp); preg_match_all('/([\-]*[0-9\.]+)/', $attributes['points'], $tmp);
$points = $tmp[0]; $points = $tmp[0];
$count = count($points); $count = count($points);

View File

@ -10,10 +10,10 @@ namespace Svg\Tag;
class Polyline extends Shape class Polyline extends Shape
{ {
public function start($attribs) public function start($attributes)
{ {
$tmp = array(); $tmp = array();
preg_match_all('/([\-]*[0-9\.]+)/', $attribs['points'], $tmp); preg_match_all('/([\-]*[0-9\.]+)/', $attributes['points'], $tmp);
$points = $tmp[0]; $points = $tmp[0];
$count = count($points); $count = count($points);

View File

@ -10,7 +10,7 @@ namespace Svg\Tag;
class RadialGradient extends AbstractTag class RadialGradient extends AbstractTag
{ {
public function start($attribs) public function start($attributes)
{ {
} }

View File

@ -17,27 +17,27 @@ class Rect extends Shape
protected $rx = 0; protected $rx = 0;
protected $ry = 0; protected $ry = 0;
public function start($attribs) public function start($attributes)
{ {
if (isset($attribs['x'])) { if (isset($attributes['x'])) {
$this->x = $attribs['x']; $this->x = $attributes['x'];
} }
if (isset($attribs['y'])) { if (isset($attributes['y'])) {
$this->y = $attribs['y']; $this->y = $attributes['y'];
} }
if (isset($attribs['width'])) { if (isset($attributes['width'])) {
$this->width = $attribs['width']; $this->width = $attributes['width'];
} }
if (isset($attribs['height'])) { if (isset($attributes['height'])) {
$this->height = $attribs['height']; $this->height = $attributes['height'];
} }
if (isset($attribs['rx'])) { if (isset($attributes['rx'])) {
$this->rx = $attribs['rx']; $this->rx = $attributes['rx'];
} }
if (isset($attribs['ry'])) { if (isset($attributes['ry'])) {
$this->ry = $attribs['ry']; $this->ry = $attributes['ry'];
} }
$this->document->getSurface()->rect($this->x, $this->y, $this->width, $this->height, $this->rx, $this->ry); $this->document->getSurface()->rect($this->x, $this->y, $this->width, $this->height, $this->rx, $this->ry);

View File

@ -2,7 +2,7 @@
/** /**
* @package php-svg-lib * @package php-svg-lib
* @link http://github.com/PhenX/php-svg-lib * @link http://github.com/PhenX/php-svg-lib
* @author Fabien Ménager <fabien.menager@gmail.com> * @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/ */
@ -12,21 +12,18 @@ use Svg\Style;
class Shape extends AbstractTag class Shape extends AbstractTag
{ {
protected function before($attribs) protected function before($attributes)
{ {
$surface = $this->document->getSurface(); $surface = $this->document->getSurface();
$surface->save(); $surface->save();
$style = new Style(); $style = $this->makeStyle($attributes);
$style->inherit($this);
$style->fromAttributes($attribs);
$this->setStyle($style); $this->setStyle($style);
$surface->setStyle($style); $surface->setStyle($style);
$this->applyTransform($attribs); $this->applyTransform($attributes);
} }
protected function after() protected function after()
@ -36,16 +33,24 @@ class Shape extends AbstractTag
if ($this->hasShape) { if ($this->hasShape) {
$style = $surface->getStyle(); $style = $surface->getStyle();
$fill = $style->fill && $style->fill !== "none"; $fill = $style->fill && is_array($style->fill);
$stroke = $style->stroke && $style->stroke !== "none"; $stroke = $style->stroke && is_array($style->stroke);
if ($fill) { if ($fill) {
if ($stroke) { if ($stroke) {
$surface->fillStroke(); $surface->fillStroke();
} else { } else {
// if (is_string($style->fill)) {
// /** @var LinearGradient|RadialGradient $gradient */
// $gradient = $this->getDocument()->getDef($style->fill);
//
// var_dump($gradient->getStops());
// }
$surface->fill(); $surface->fill();
} }
} elseif ($stroke) { }
elseif ($stroke) {
$surface->stroke(); $surface->stroke();
} }
else { else {

View File

@ -10,7 +10,7 @@ namespace Svg\Tag;
class Stop extends AbstractTag class Stop extends AbstractTag
{ {
public function start($attribs) public function start($attributes)
{ {
} }

View File

@ -2,7 +2,7 @@
/** /**
* @package php-svg-lib * @package php-svg-lib
* @link http://github.com/PhenX/php-svg-lib * @link http://github.com/PhenX/php-svg-lib
* @author Fabien Ménager <fabien.menager@gmail.com> * @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/ */
@ -14,17 +14,17 @@ class Text extends Shape
protected $y = 0; protected $y = 0;
protected $text = ""; protected $text = "";
public function start($attribs) public function start($attributes)
{ {
$document = $this->document; $document = $this->document;
$height = $this->document->getHeight(); $height = $this->document->getHeight();
$this->y = $height; $this->y = $height;
if (isset($attribs['x'])) { if (isset($attributes['x'])) {
$this->x = $attribs['x']; $this->x = $attributes['x'];
} }
if (isset($attribs['y'])) { if (isset($attributes['y'])) {
$this->y = $height - $attribs['y']; $this->y = $height - $attributes['y'];
} }
$document->getSurface()->transform(1, 0, 0, -1, 0, $height); $document->getSurface()->transform(1, 0, 0, -1, 0, $height);
@ -35,13 +35,22 @@ class Text extends Shape
$surface = $this->document->getSurface(); $surface = $this->document->getSurface();
$x = $this->x; $x = $this->x;
$y = $this->y; $y = $this->y;
$style = $surface->getStyle();
$surface->setFont($style->fontFamily, $style->fontStyle, $style->fontWeight);
if ($surface->getStyle()->textAnchor == "middle") { switch ($style->textAnchor) {
$width = $surface->measureText($this->text); case "middle":
$x -= $width / 2; $width = $surface->measureText($this->text);
$x -= $width / 2;
break;
case "end":
$width = $surface->measureText($this->text);
$x -= $width;
break;
} }
$surface->fillText($this->text, $x, $y); $surface->fillText($this->getText(), $x, $y);
} }
protected function after() protected function after()
@ -53,4 +62,9 @@ class Text extends Shape
{ {
$this->text .= $text; $this->text .= $text;
} }
}
public function getText()
{
return trim($this->text);
}
}