1<?php
2/**
3 * DokuWiki Plugin a2s (Syntax Component)
4 *
5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
6 * @author  Schplurtz le Déboulonné <Schplurtz@laposte.net>
7 */
8
9// must be run within Dokuwiki
10if (!defined('DOKU_INC')) die();
11
12class syntax_plugin_a2s extends DokuWiki_Syntax_Plugin {
13    protected static $cssAlign=array(
14        '' => 'media', 'left' => 'medialeft',
15        'right' => 'mediaright', 'center' => 'mediacenter'
16    );
17    protected static $opening=<<<SVG
18<?xml version="1.0" encoding="UTF-8" standalone="no"?>
19<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
20<!-- Created with ASCIIToSVG (https://github.com/dhobsd/asciitosvg/) -->
21<svg
22SVG;
23    protected static $renderer=null;
24    protected static $align='';
25    /**
26     * return some info.
27     * Why did I copy this function here ? old DW compat ???
28     *
29     * @return array hash of plugin technical informations.
30     */
31    function getInfo(){
32        return confToHash(dirname(__FILE__).'/plugin.info.txt');
33    }
34
35    /**
36     * @return string Syntax mode type
37     */
38    public function getType() {
39        return 'protected';
40    }
41    /**
42     * @return string Paragraph type
43     */
44    public function getPType() {
45        return 'block';
46    }
47    /**
48     * @return int Sort order - Low numbers go before high numbers
49     */
50    public function getSort() {
51        return 610;
52    }
53
54    /**
55     * Connect lookup pattern to lexer.
56     *
57     * @param string $mode Parser mode
58     */
59    public function connectTo($mode) {
60        $this->Lexer->addEntryPattern('< *a2s *>(?=.*?</a2s>)',$mode,'plugin_a2s');
61    }
62
63    public function postConnect() {
64        $this->Lexer->addExitPattern('</a2s>','plugin_a2s');
65    }
66
67    /**
68     * Handle matches of the a2s syntax
69     *
70     * @param string          $match   The match of the syntax
71     * @param int             $state   The state of the handler
72     * @param int             $pos     The position in the document
73     * @param Doku_Handler    $handler The handler
74     * @return array Data for the renderer
75     */
76    public function handle($match, $state, $pos, Doku_Handler $handler){
77        switch ($state) {
78        case DOKU_LEXER_ENTER :
79            $spaces=array();
80            preg_match( '/<( *)a2s( *)>/', $match, $spaces );
81            $left=strlen($spaces[1]);
82            $right=strlen($spaces[2]);
83            $align='';
84            if( ($right + $left) > 0 ) {
85                if( $right > $left )
86                    $align='left';
87                elseif( $left > $right)
88                    $align='right';
89                else
90                    $align='center';
91            }
92            self::$align=$align; // needed to pass $align to ODT LEXER_MATCHED render
93            return array($state, $align, null); // odt renderer expects 3 values
94        case DOKU_LEXER_UNMATCHED :
95            $o = new dokuwiki\plugin\a2s\ASCIIToSVG($this->_prepare($match));
96            $o->setDimensionScale(9, 16);
97            $o->parseGrid();
98            // save alignment for later use by ODT renderer
99            return array($state, $o->render(), self::$align);
100        case DOKU_LEXER_EXIT :
101            return array($state, null, null); // odt renderer expects 3 values
102        }
103        return array();
104    }
105
106    /**
107     * Render output, generic method. Call specialized renderer depending
108     * on the mode.
109     *
110     * @param string         $mode      Renderer mode (supported modes: xhtml, odt)
111     * @param Doku_Renderer  $renderer  The renderer
112     * @param array          $data      The data from the handler() function
113     * @return bool If rendering was successful.
114     */
115    public function render($mode, Doku_Renderer $renderer, $data) {
116        list($state, $txtdata, $align) = $data;
117        if( $state == DOKU_LEXER_UNMATCHED ) {
118            $this->renderer = $renderer;
119            $txtdata=preg_replace_callback(
120                         '/
121                         xlink:href="
122                         <INTERWIKI>
123                         <([^>]*)>
124                         <([^>]*)>
125                         "
126                         /x',
127                         function( $match ) {
128                             return 'xlink:href="' .
129                                    $this->renderer->_resolveInterWiki($match[1],$match[2]) .
130                                    '"';
131                         }
132                         , $txtdata
133            );
134        }
135
136        switch($mode) {
137        case 'xhtml':
138            return $this->_render_xhtml($renderer, $state, $txtdata );
139        break;
140        case 'odt': case 'odt_pdf':
141            return $this->_render_odt($renderer, $state, $txtdata, $align );
142        break;
143        }
144        return false;
145    }
146
147    /**
148     * Render xhtml output. add data to the renderer doc.
149     *
150     * @param Doku_Renderer  $renderer  The renderer
151     * @param int            $state     The state
152     * @param string         $txtdata   Textual data that handle() associated with this state
153     * @return bool If rendering was successful.
154     */
155    protected function _render_xhtml(Doku_Renderer $renderer, $state, $txtdata) {
156
157        switch ($state) {
158        case DOKU_LEXER_ENTER :
159            $align=self::$cssAlign[$txtdata];
160            $renderer->doc .= "<svg class=\"a2s {$align}\" ";
161        break;
162        case DOKU_LEXER_UNMATCHED :
163            $renderer->doc .= $txtdata;
164        break;
165        }
166        return true;
167    }
168
169    /**
170     * Render odt output.
171     *
172     * @param Doku_Renderer  $renderer  The renderer
173     * @param int            $state     The state
174     * @param string         $txtdata   Textual data that handle() associated with this state
175     * @param string         $align     img align
176     * @return bool If rendering was successful.
177     */
178    protected function _render_odt(Doku_Renderer $renderer, $state, $txtdata, $align) {
179        if($state === DOKU_LEXER_UNMATCHED) {
180            $dim=$this->_extract_XY_4svg( $txtdata );
181            $renderer->_addStringAsSVGImage(self::$opening.$txtdata, $dim[0], $dim[1], $align);
182        }
183        return true;
184    }
185
186    /**
187     * Find the SVG X and Y dimensions in the svg string of the image.
188     * it searches for 'width="nnnpx" height="mmmpx"' in the first
189     * given string and returns the dimension in inch.
190     *
191     * @param String  $svgtxt  The svg string to inspect
192     * @return array the X and Y dimensions suitable as SVG dimensions
193     */
194    protected function _extract_XY_4svg( $svgtxt ) {
195        $sizes=array();
196        preg_match( '/width="(.*?)px" height="(.*?)px"/', $svgtxt, $sizes );
197        array_shift($sizes);
198        // assume a 96 dpi screen
199        return array_map( function($v) { return ($v/96.0)."in"; }, $sizes );
200    }
201    /**
202     * Prepare matched text for beeing parsed. Removes unnecessary blank lines
203     * expand wikilinks to http absolute links. (absolute links because of
204     * ODT export)
205     *
206     * @param String  $text  The matched a2s input string
207     * @return String        the prepared string
208     */
209    protected function _prepare( $text ) {
210        return preg_replace_callback(
211                     '/"a2s:link":"
212                     \\[\\[
213                         ([^]|]*)    # The page_id
214                         (\\|[^]]*)? # |description optional
215                     ]]"
216                     /x',
217                     function( $match ) {
218                         return '"a2s:link":"' . wl( cleanID($match[1]), '', true ) . '"';
219                     },
220                     preg_replace_callback(
221                                  '/"a2s:link":"
222                                  \\[\\[
223                                      ([a-z0-9][-_.a-z0-9]*[a-z0-9])>([^]]*)
224                                  ]]"
225                                  /x',
226                                  function( $match ) {
227                                      return '"a2s:link":"<INTERWIKI><' . $match[1] . '><' . $match[2] . '>"';
228                                  },
229                                  trim($text, "\r\n")
230                     )
231               );
232    }
233}
234
235// vim:ts=4:sw=4:et:
236