Release 0.7.1
[grauphel.git] / lib / converter / restructuredtext.php
1 <?php
2 /**
3  * Part of grauphel
4  *
5  * PHP version 5
6  *
7  * @category  Tools
8  * @package   Grauphel
9  * @author    Christian Weiske <cweiske@cweiske.de>
10  * @copyright 2014 Christian Weiske
11  * @license   http://www.gnu.org/licenses/agpl.html GNU AGPL v3
12  * @link      http://cweiske.de/grauphel.htm
13  */
14 namespace OCA\Grauphel\Converter;
15 use \XMLReader;
16
17 /**
18  * Convert Tomboy note XML to reStructuredText.
19  * Mainly used to paste the content of a note into an e-mail
20  *
21  * @category  Tools
22  * @package   Grauphel
23  * @author    Christian Weiske <cweiske@cweiske.de>
24  * @copyright 2014 Christian Weiske
25  * @license   http://www.gnu.org/licenses/agpl.html GNU AGPL v3
26  * @version   Release: @package_version@
27  * @link      http://cweiske.de/grauphel.htm
28  */
29 class ReStructuredText extends Base
30 {
31     protected static $simpleMap = array(
32         'bold'      => '**',
33         'italic'    => '*',
34         'monospace' => '``',
35         'strikethrough' => '-',
36         'highlight'  => '**',
37     );
38
39     public $internalLinkHandler;
40
41
42
43     public function __construct()
44     {
45         $this->internalLinkHandler = array($this, 'internalLinkHandler');
46     }
47
48     /**
49      * Converts the tomboy note XML into reStructuredText
50      *
51      * @param string $xmlContent Tomboy note content
52      *
53      * @return string Plain text
54      */
55     public function convert($xmlContent)
56     {
57         if (strpos($xmlContent, '</link:internal><link:internal>') !== false) {
58             $xmlContent = $this->fixNastyLinks($xmlContent);
59         }
60
61         $rst = '';
62         $reader = new XMLReader();
63         $reader->xml(
64             '<?xml version="1.0" encoding="utf-8"?>' . "\n"
65             . '<content xmlns:size="size" xmlns:link="link">'
66             . $xmlContent
67             . '</content>'
68         );
69
70         $withinLink = false;
71         $store = &$rst;
72         $listLevel  = -1;
73         $listPrefix = '';
74         $listItemCount = 0;
75         $heading = false;
76         $headingLength = 0;
77         while ($reader->read()) {
78             switch ($reader->nodeType) {
79             case XMLReader::ELEMENT:
80                 //echo $reader->name . "\n";
81                 if (isset(static::$simpleMap[$reader->name])) {
82                     $store .= static::$simpleMap[$reader->name];
83                 } else if ($reader->name == 'list') {
84                     ++$listLevel;
85                     $listItemCount = 0;
86                     $listPrefix = str_repeat('  ', $listLevel);
87                 } else if ($reader->name == 'list-item') {
88                     ++$listItemCount;
89                     if ($listItemCount == 1) {
90                         $store .= "\n";
91                     }
92                     $store .= $listPrefix . '- ';
93                 } else if ($reader->name == 'size:large'
94                     || $reader->name == 'size:huge'
95                 ) {
96                     $store .= "\n";
97                     $heading = true;
98                 } else if (substr($reader->name, 0, 5) == 'link:') {
99                     $withinLink = true;
100                     $linkText    = '';
101                     $store       = &$linkText;
102                 }
103                 break;
104             case XMLReader::END_ELEMENT:
105                 if (isset(static::$simpleMap[$reader->name])) {
106                     $store .= static::$simpleMap[$reader->name];
107                 } else if ($reader->name == 'list') {
108                     --$listLevel;
109                     $listPrefix = str_repeat(
110                         '  ', $listLevel < 0 ? 0 : $listLevel
111                     );
112                     if ($listLevel == -1) {
113                         $store .= "\n";
114                     }
115                 } else if ($reader->name == 'size:large') {
116                     $store .= "\n" . str_repeat('-', $headingLength);
117                     $heading = false;
118                 } else if ($reader->name == 'size:huge') {
119                     $store .= "\n" . str_repeat('=', $headingLength);
120                     $heading = false;
121                 } else if (substr($reader->name, 0, 5) == 'link:') {
122                     $withinLink = false;
123                     $store      = &$rst;
124                     $linkUrl = htmlspecialchars_decode(strip_tags($linkText));
125                     if ($reader->name == 'link:internal') {
126                         $linkUrl = call_user_func($this->internalLinkHandler, $linkUrl);
127                     } else {
128                         $linkUrl = $this->fixLinkUrl($linkUrl);
129                     }
130                     $store .= $linkUrl;
131                 }
132                 break;
133             case XMLReader::TEXT:
134             case XMLReader::SIGNIFICANT_WHITESPACE:
135                 if ($heading) {
136                     $headingLength = strlen(trim($reader->value));
137                     $store .= trim($reader->value);
138                 } else {
139                     $text = wordwrap($reader->value, 72 - 2 * $listLevel, "\n");
140                     $parts = explode("\n", $text);
141                     foreach ($parts as $k => $v) {
142                         if ($k == 0) {
143                             continue;
144                         }
145                         if ($v != '') {
146                             $parts[$k] = str_repeat(' ', $listLevel * 2 + 2) . $v;
147                         }
148                     }
149                     $store .= implode("\n", $parts);
150                 }
151                 break;
152             default:
153                 throw new Exception(
154                     'Unsupported XML node type: ' . $reader->nodeType
155                 );
156             }
157         }
158
159         return $rst;
160     }
161
162     /**
163      * Fixes external URLs without a protocol
164      *
165      * @param string $linkUrl URL to fix
166      *
167      * @return string Fixed URL
168      */
169     protected function fixLinkUrl($linkUrl)
170     {
171         if ($linkUrl{0} == '/') {
172             //Unix file path
173             $linkUrl = 'file://' . $linkUrl;
174         }
175         return $linkUrl;
176     }
177
178     /**
179      * Dummy internal link handler that simply adds ".htm" to the note title
180      *
181      * @param string $linkUrl Title of page that is linked
182      *
183      * @return string URL to link to
184      */
185     public function internalLinkHandler($linkUrl)
186     {
187         return $linkUrl . '.htm';
188     }
189 }
190 ?>