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