PageRenderTime 51ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/program/lib/Roundcube/rcube_text2html.php

https://github.com/trimbakgopalghare/roundcubemail
PHP | 307 lines | 171 code | 44 blank | 92 comment | 44 complexity | d3107a89df98cd8777e5b7832a0241b4 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.1
  1. <?php
  2. /**
  3. +-----------------------------------------------------------------------+
  4. | This file is part of the Roundcube Webmail client |
  5. | Copyright (C) 2008-2014, The Roundcube Dev Team |
  6. | |
  7. | Licensed under the GNU General Public License version 3 or |
  8. | any later version with exceptions for skins & plugins. |
  9. | See the README file for a full license statement. |
  10. | |
  11. | PURPOSE: |
  12. | Converts plain text to HTML |
  13. +-----------------------------------------------------------------------+
  14. | Author: Aleksander Machniak <alec@alec.pl> |
  15. +-----------------------------------------------------------------------+
  16. */
  17. /**
  18. * Converts plain text to HTML
  19. *
  20. * @package Framework
  21. * @subpackage Utils
  22. */
  23. class rcube_text2html
  24. {
  25. /**
  26. * Contains the HTML content after conversion.
  27. *
  28. * @var string $html
  29. */
  30. protected $html;
  31. /**
  32. * Contains the plain text.
  33. *
  34. * @var string $text
  35. */
  36. protected $text;
  37. /**
  38. * Configuration
  39. *
  40. * @var array $config
  41. */
  42. protected $config = array(
  43. // non-breaking space
  44. 'space' => "\xC2\xA0",
  45. // enables format=flowed parser
  46. 'flowed' => false,
  47. // enables wrapping for non-flowed text
  48. 'wrap' => true,
  49. // line-break tag
  50. 'break' => "<br>\n",
  51. // prefix and suffix (wrapper element)
  52. 'begin' => '<div class="pre">',
  53. 'end' => '</div>',
  54. // enables links replacement
  55. 'links' => true,
  56. );
  57. /**
  58. * Constructor.
  59. *
  60. * If the plain text source string (or file) is supplied, the class
  61. * will instantiate with that source propagated, all that has
  62. * to be done it to call get_html().
  63. *
  64. * @param string $source Plain text
  65. * @param boolean $from_file Indicates $source is a file to pull content from
  66. * @param array $config Class configuration
  67. */
  68. function __construct($source = '', $from_file = false, $config = array())
  69. {
  70. if (!empty($source)) {
  71. $this->set_text($source, $from_file);
  72. }
  73. if (!empty($config) && is_array($config)) {
  74. $this->config = array_merge($this->config, $config);
  75. }
  76. }
  77. /**
  78. * Loads source text into memory, either from $source string or a file.
  79. *
  80. * @param string $source Plain text
  81. * @param boolean $from_file Indicates $source is a file to pull content from
  82. */
  83. function set_text($source, $from_file = false)
  84. {
  85. if ($from_file && file_exists($source)) {
  86. $this->text = file_get_contents($source);
  87. }
  88. else {
  89. $this->text = $source;
  90. }
  91. $this->_converted = false;
  92. }
  93. /**
  94. * Returns the HTML content.
  95. *
  96. * @return string HTML content
  97. */
  98. function get_html()
  99. {
  100. if (!$this->_converted) {
  101. $this->_convert();
  102. }
  103. return $this->html;
  104. }
  105. /**
  106. * Prints the HTML.
  107. */
  108. function print_html()
  109. {
  110. print $this->get_html();
  111. }
  112. /**
  113. * Workhorse function that does actual conversion (calls _converter() method).
  114. */
  115. protected function _convert()
  116. {
  117. // Convert TXT to HTML
  118. $this->html = $this->_converter($this->text);
  119. $this->_converted = true;
  120. }
  121. /**
  122. * Workhorse function that does actual conversion.
  123. *
  124. * @param string Plain text
  125. */
  126. protected function _converter($text)
  127. {
  128. // make links and email-addresses clickable
  129. $attribs = array('link_attribs' => array('rel' => 'noreferrer', 'target' => '_blank'));
  130. $replacer = new rcmail_string_replacer($attribs);
  131. if ($this->config['flowed']) {
  132. $flowed_char = 0x01;
  133. $text = rcube_mime::unfold_flowed($text, chr($flowed_char));
  134. }
  135. // search for patterns like links and e-mail addresses and replace with tokens
  136. if ($this->config['links']) {
  137. $text = $replacer->replace($text);
  138. }
  139. // split body into single lines
  140. $text = preg_split('/\r?\n/', $text);
  141. $quote_level = 0;
  142. $last = null;
  143. // wrap quoted lines with <blockquote>
  144. for ($n = 0, $cnt = count($text); $n < $cnt; $n++) {
  145. $flowed = false;
  146. if ($this->config['flowed'] && ord($text[$n][0]) == $flowed_char) {
  147. $flowed = true;
  148. $text[$n] = substr($text[$n], 1);
  149. }
  150. if ($text[$n][0] == '>' && preg_match('/^(>+ {0,1})+/', $text[$n], $regs)) {
  151. $q = substr_count($regs[0], '>');
  152. $text[$n] = substr($text[$n], strlen($regs[0]));
  153. $text[$n] = $this->_convert_line($text[$n], $flowed || $this->config['wrap']);
  154. $_length = strlen(str_replace(' ', '', $text[$n]));
  155. if ($q > $quote_level) {
  156. if ($last !== null) {
  157. $text[$last] .= (!$length ? "\n" : '')
  158. . $replacer->get_replacement($replacer->add(
  159. str_repeat('<blockquote>', $q - $quote_level)))
  160. . $text[$n];
  161. unset($text[$n]);
  162. }
  163. else {
  164. $text[$n] = $replacer->get_replacement($replacer->add(
  165. str_repeat('<blockquote>', $q - $quote_level))) . $text[$n];
  166. $last = $n;
  167. }
  168. }
  169. else if ($q < $quote_level) {
  170. $text[$last] .= (!$length ? "\n" : '')
  171. . $replacer->get_replacement($replacer->add(
  172. str_repeat('</blockquote>', $quote_level - $q)))
  173. . $text[$n];
  174. unset($text[$n]);
  175. }
  176. else {
  177. $last = $n;
  178. }
  179. }
  180. else {
  181. $text[$n] = $this->_convert_line($text[$n], $flowed || $this->config['wrap']);
  182. $q = 0;
  183. $_length = strlen(str_replace(' ', '', $text[$n]));
  184. if ($quote_level > 0) {
  185. $text[$last] .= (!$length ? "\n" : '')
  186. . $replacer->get_replacement($replacer->add(
  187. str_repeat('</blockquote>', $quote_level)))
  188. . $text[$n];
  189. unset($text[$n]);
  190. }
  191. else {
  192. $last = $n;
  193. }
  194. }
  195. $quote_level = $q;
  196. $length = $_length;
  197. }
  198. if ($quote_level > 0) {
  199. $text[$last] .= $replacer->get_replacement($replacer->add(
  200. str_repeat('</blockquote>', $quote_level)));
  201. }
  202. $text = join("\n", $text);
  203. // colorize signature (up to <sig_max_lines> lines)
  204. $len = strlen($text);
  205. $sig_sep = "--" . $this->config['space'] . "\n";
  206. $sig_max_lines = rcube::get_instance()->config->get('sig_max_lines', 15);
  207. while (($sp = strrpos($text, $sig_sep, $sp ? -$len+$sp-1 : 0)) !== false) {
  208. if ($sp == 0 || $text[$sp-1] == "\n") {
  209. // do not touch blocks with more that X lines
  210. if (substr_count($text, "\n", $sp) < $sig_max_lines) {
  211. $text = substr($text, 0, max(0, $sp))
  212. .'<span class="sig">'.substr($text, $sp).'</span>';
  213. }
  214. break;
  215. }
  216. }
  217. // insert url/mailto links and citation tags
  218. $text = $replacer->resolve($text);
  219. // replace line breaks
  220. $text = str_replace("\n", $this->config['break'], $text);
  221. return $this->config['begin'] . $text . $this->config['end'];
  222. }
  223. /**
  224. * Converts spaces in line of text
  225. */
  226. protected function _convert_line($text, $is_flowed)
  227. {
  228. static $table;
  229. if (empty($table)) {
  230. $table = get_html_translation_table(HTML_SPECIALCHARS);
  231. unset($table['?']);
  232. }
  233. // skip signature separator
  234. if ($text == '-- ') {
  235. return '--' . $this->config['space'];
  236. }
  237. // replace HTML special characters
  238. $text = strtr($text, $table);
  239. $nbsp = $this->config['space'];
  240. // replace some whitespace characters
  241. $text = str_replace(array("\r", "\t"), array('', ' '), $text);
  242. // replace spaces with non-breaking spaces
  243. if ($is_flowed) {
  244. $pos = 0;
  245. $diff = 0;
  246. $len = strlen($nbsp);
  247. $copy = $text;
  248. while (($pos = strpos($text, ' ', $pos)) !== false) {
  249. if ($pos == 0 || $text[$pos-1] == ' ') {
  250. $copy = substr_replace($copy, $nbsp, $pos + $diff, 1);
  251. $diff += $len - 1;
  252. }
  253. $pos++;
  254. }
  255. $text = $copy;
  256. }
  257. else {
  258. // make the whole line non-breakable
  259. $text = str_replace(array(' ', '-', '/'), array($nbsp, '-&#8288;', '/&#8288;'), $text);
  260. }
  261. return $text;
  262. }
  263. }