1<?php
2/**
3 * Plugin svgpureInsert: Inserts a non png or other modified svg file, just its pure version
4 *
5 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author     Andreas Gohr <gohr@cosmocode.de>
7 */
8
9if(!defined('DOKU_INC')) exit;
10if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN', DOKU_INC . 'lib/plugins/');
11
12class helper_plugin_svgpureinsert extends DokuWiki_Plugin {
13
14    /**
15     * Gets a local scalable copy of the SVG with its dimensions
16     *
17     * @param $id
18     * @param int $cachetime
19     * @return bool|array either array($file, $width, $height) or false if no cache available
20     */
21    public function getAdjustedSVG($id, $cachetime = -1) {
22        $info = $this->getInfo();
23        $cachefile = getCacheName($id . $info['date'], '.svg');
24        $cachedate = @filemtime($cachefile);
25        if($cachedate && $cachetime < (time() - $cachedate)) {
26            list($width, $height) = $this->readSVGsize($cachefile);
27            return array($cachefile, $width, $height);
28        }
29
30        // still here, create a new cache file
31        if(preg_match('/^https?:\/\//i', $id)) {
32            io_download($id, $cachefile); #FIXME make max size configurable
33        } else {
34            @copy(mediaFN($id), $cachefile);
35        }
36        clearstatcache(false, $cachefile);
37
38        // adjust the size in the cache file
39        if(file_exists($cachefile)){
40            list($width, $height) = $this->readSVGsize($cachefile, true);
41            return array($cachefile, $width, $height);
42        }
43
44        return false;
45    }
46
47
48    /**
49     * Parse te given XML attributes into an array
50     *
51     * @author troelskn
52     * @link http://stackoverflow.com/a/1083821/172068
53     * @param $input
54     * @return array
55     */
56    public function parseAttributes($input) {
57        $dom = new DomDocument();
58        $dom->loadHtml("<html $input />");
59        $attributes = array();
60        foreach($dom->documentElement->attributes as $name => $attr) {
61            $attributes[$name] = $attr->value;
62        }
63        return $attributes;
64    }
65
66    /**
67     * Calculates pixel size for any given SVG size
68     *
69     * @param $value
70     * @return int
71     */
72    public function convertToPixel($value) {
73        if(!preg_match('/^(\d+?(\.\d*)?)(in|em|ex|px|pt|pc|cm|mm)?$/', $value, $m)) return 0;
74
75        $digit = (double) $m[1];
76        $unit  = (string) $m[3];
77
78        $dpi         = 72;
79        $conversions = array(
80            'in' => $dpi,
81            'em' => 16,
82            'ex' => 12,
83            'px' => 1,
84            'pt' => $dpi / 72, # 1/27 of an inch
85            'pc' => $dpi / 6, # 1/6 of an inch
86            'cm' => $dpi / 2.54, # inch to cm
87            'mm' => $dpi / (2.54 * 10), # inch to cm,
88        );
89
90        if(isset($conversions[$unit])) {
91            $digit = $digit * (float) $conversions[$unit];
92        }
93
94        return ceil($digit);
95    }
96
97    /**
98     * Read the Size of an SVG from its contents
99     *
100     * @param string $file local SVG file (or part of it)
101     * @param bool $fix should the file's size attributes be fixed as well?
102     * @return array
103     */
104    public function readSVGsize($file, $fix = false) {
105        $default  = array(100, 100);
106
107        $data = io_readFile($file, false);
108        if(!$data) return $default;
109        if(!preg_match('/<svg([^>]*)>/s', $data, $m)) return $default;
110        $attributes = $this->parseAttributes($m[1]);
111
112        $width  = $attributes['width'];
113        $height = $attributes['height'];
114
115        if(substr($width, -1, 1) == '%' || substr($height, -1, 1) == '%') {
116            // dimensions are in percent, try viewBox instead
117            list(, , $width, $height) = explode(' ', $attributes['viewbox']);
118        }
119
120        // fix units
121        $width  = $this->convertToPixel($width);
122        $height = $this->convertToPixel($height);
123
124        // if calculation failed use default
125        if(!$width) $width = $default[0];
126        if(!$height) $height = $default[0];
127
128        // fix the SVG to be autoscaling
129        if($fix) {
130            if(isset($attributes['viewbox'])) unset($attributes['viewbox']);
131            if(isset($attributes['preserveaspectratio'])) unset($attributes['preserveaspectratio']);
132
133            $attributes['width']   = '100%';
134            $attributes['height']  = '100%';
135            $attributes['viewBox'] = "0 0 $width $height";
136            $attributes['preserveAspectRatio'] = 'xMidYMid slice';
137
138            $svg  = '<svg ' . buildAttributes($attributes) . '>';
139            $data = preg_replace('/<svg([^>]*)>/s', $svg, $data, 1);
140            io_saveFile($file, $data);
141        }
142
143        return array(
144            $width,
145            $height
146        );
147    }
148
149}