You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

rcube_spellcheck_pspell.php 5.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. <?php
  2. /**
  3. +-----------------------------------------------------------------------+
  4. | This file is part of the Roundcube Webmail client |
  5. | |
  6. | Copyright (C) 2008-2013, The Roundcube Dev Team |
  7. | |
  8. | Licensed under the GNU General Public License version 3 or |
  9. | any later version with exceptions for skins & plugins. |
  10. | See the README file for a full license statement. |
  11. | |
  12. | PURPOSE: |
  13. | Spellchecking backend implementation to work with Pspell |
  14. +-----------------------------------------------------------------------+
  15. | Author: Aleksander Machniak <machniak@kolabsys.com> |
  16. | Author: Thomas Bruederli <roundcube@gmail.com> |
  17. +-----------------------------------------------------------------------+
  18. */
  19. /**
  20. * Spellchecking backend implementation to work with Pspell
  21. *
  22. * @package Framework
  23. * @subpackage Utils
  24. */
  25. class rcube_spellcheck_pspell extends rcube_spellcheck_engine
  26. {
  27. private $plink;
  28. private $matches = array();
  29. /**
  30. * Return a list of languages supported by this backend
  31. *
  32. * @see rcube_spellcheck_engine::languages()
  33. */
  34. function languages()
  35. {
  36. $defaults = array('en');
  37. $langs = array();
  38. // get aspell dictionaries
  39. exec('aspell dump dicts', $dicts);
  40. if (!empty($dicts)) {
  41. $seen = array();
  42. foreach ($dicts as $lang) {
  43. $lang = preg_replace('/-.*$/', '', $lang);
  44. $langc = strlen($lang) == 2 ? $lang.'_'.strtoupper($lang) : $lang;
  45. if (!$seen[$langc]++)
  46. $langs[] = $lang;
  47. }
  48. $langs = array_unique($langs);
  49. }
  50. else {
  51. $langs = $defaults;
  52. }
  53. return $langs;
  54. }
  55. /**
  56. * Initializes PSpell dictionary
  57. */
  58. private function init()
  59. {
  60. if (!$this->plink) {
  61. if (!extension_loaded('pspell')) {
  62. $this->error = "Pspell extension not available";
  63. return;
  64. }
  65. $this->plink = pspell_new($this->lang, null, null, RCUBE_CHARSET, PSPELL_FAST);
  66. }
  67. if (!$this->plink) {
  68. $this->error = "Unable to load Pspell engine for selected language";
  69. }
  70. }
  71. /**
  72. * Set content and check spelling
  73. *
  74. * @see rcube_spellcheck_engine::check()
  75. */
  76. function check($text)
  77. {
  78. $this->init();
  79. if (!$this->plink) {
  80. return array();
  81. }
  82. // tokenize
  83. $text = preg_split($this->separator, $text, NULL, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_OFFSET_CAPTURE);
  84. $diff = 0;
  85. $matches = array();
  86. foreach ($text as $w) {
  87. $word = trim($w[0]);
  88. $pos = $w[1] - $diff;
  89. $len = mb_strlen($word);
  90. // skip exceptions
  91. if ($this->dictionary->is_exception($word)) {
  92. }
  93. else if (!pspell_check($this->plink, $word)) {
  94. $suggestions = pspell_suggest($this->plink, $word);
  95. if (count($suggestions) > self::MAX_SUGGESTIONS) {
  96. $suggestions = array_slice($suggestions, 0, self::MAX_SUGGESTIONS);
  97. }
  98. $matches[] = array($word, $pos, $len, null, $suggestions);
  99. }
  100. $diff += (strlen($word) - $len);
  101. }
  102. $this->matches = $matches;
  103. return $matches;
  104. }
  105. /**
  106. * Returns suggestions for the specified word
  107. *
  108. * @see rcube_spellcheck_engine::get_words()
  109. */
  110. function get_suggestions($word)
  111. {
  112. $this->init();
  113. if (!$this->plink) {
  114. return array();
  115. }
  116. $suggestions = pspell_suggest($this->plink, $word);
  117. if (count($suggestions) > self::MAX_SUGGESTIONS)
  118. $suggestions = array_slice($suggestions, 0, self::MAX_SUGGESTIONS);
  119. return is_array($suggestions) ? $suggestions : array();
  120. }
  121. /**
  122. * Returns misspelled words
  123. *
  124. * @see rcube_spellcheck_engine::get_suggestions()
  125. */
  126. function get_words($text = null)
  127. {
  128. $result = array();
  129. if ($text) {
  130. // init spellchecker
  131. $this->init();
  132. if (!$this->plink) {
  133. return array();
  134. }
  135. // With PSpell we don't need to get suggestions to return misspelled words
  136. $text = preg_split($this->separator, $text, NULL, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_OFFSET_CAPTURE);
  137. foreach ($text as $w) {
  138. $word = trim($w[0]);
  139. // skip exceptions
  140. if ($this->dictionary->is_exception($word)) {
  141. continue;
  142. }
  143. if (!pspell_check($this->plink, $word)) {
  144. $result[] = $word;
  145. }
  146. }
  147. return $result;
  148. }
  149. foreach ($this->matches as $m) {
  150. $result[] = $m[0];
  151. }
  152. return $result;
  153. }
  154. }