1 /**
2 * Yudit Unicode Editor Source File
3 *
4 * GNU Copyright (C) 1997-2006 Gaspar Sinai <gaspar@yudit.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License, version 2,
8 * dated June 1991. See file COPYYING for details.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include "stoolkit/syntax/SHunspellPattern.h"
21 #include "stoolkit/SIO.h"
22 #include "stoolkit/SCharClass.h"
23
24 #ifdef USE_WINAPI
25 #include <windows.h>
26 #else
27 #include <stdlib.h>
28 #include <dlfcn.h>
29 #include <string.h>
30 #endif
31
32 static void fixFileName (SString& str);
33 static void convertRovasiras (const SV_UCS4& in, SV_UCS4& out);
34
fixFileName(SString & str)35 static void fixFileName (SString& str)
36 {
37 #ifdef USE_WINAPI
38 str.replaceAll ("/", "\\");
39 if (str.size() > 0 && str[0] == '\\') str.remove (0);
40 #endif
41 }
42
43 static void* dynHandle = 0;
44
SHunspellPattern(const SString & _name,const SStringVector & path)45 SHunspellPattern::SHunspellPattern (const SString& _name,
46 const SStringVector& path)
47 {
48 name = _name;
49 SString ps_dic = name;
50 ps_dic.append (".dic");
51 SFile fdic (ps_dic, path);
52 if (fdic.size() < 0)
53 {
54 valid = false;
55 fprintf (stderr, "Hunspell invalid fdic.size.\n");
56 }
57 else
58 {
59 dicFile = fdic.getName ();
60 fixFileName (dicFile);
61 }
62
63 SString ps_aff = name;
64 ps_aff.append (".aff");
65 SFile faff (ps_aff, path);
66 if (faff.size() < 0)
67 {
68 valid = false;
69 fprintf (stderr, "Hunspell invalid faff.size.\n");
70 }
71 else
72 {
73 affFile = faff.getName ();
74 fixFileName (affFile);
75 }
76
77
78 dicFile.append ((char)0);
79 affFile.append ((char)0);
80 SString mising = loadLibrary (path);
81 hunspell = 0;
82 functions.create = 0;
83 functions.spell = 0;
84 functions.destroy = 0;
85 functions.get_dic_encoding = 0;
86 entryEncoder = 0;
87 if (dynHandle)
88 {
89 #ifdef USE_WINAPI
90 functions.create = (void* (*)(const char*, const char*)) GetProcAddress((HMODULE)dynHandle, "hunspell_initialize");
91 functions.spell = (int (*)(void*, const char*)) GetProcAddress((HMODULE)dynHandle, "hunspell_spell");
92 functions.destroy = (int (*)(void*)) GetProcAddress((HMODULE)dynHandle, "hunspell_uninitialize");
93 functions.get_dic_encoding = (char* (*)(void*)) GetProcAddress((HMODULE)dynHandle, "hunspell_get_dic_encoding");
94 #else
95 functions.create = (void* (*)(const char*, const char*)) dlsym(dynHandle, "Hunspell_create");
96 functions.spell = (int (*)(void*, const char*)) dlsym(dynHandle, "Hunspell_spell");
97 functions.destroy = (int (*)(void*)) dlsym(dynHandle, "Hunspell_destroy");
98 functions.get_dic_encoding = (char* (*)(void*)) dlsym(dynHandle, "Hunspell_get_dic_encoding");
99 #endif
100 if (functions.create && functions.spell && functions.destroy &&
101 functions.get_dic_encoding)
102 {
103 hunspell = (*functions.create)(affFile.array (), dicFile.array ());
104 valid = (hunspell != 0);
105 }
106 else
107 {
108 fprintf (stderr, "hunspell: can not get function handles. create=%d spell=%d destroy=%d get_dic_encoding=%d\n",
109 (int)(functions.create!=0),
110 (int)(functions.spell!=0),
111 (int)(functions.destroy!=0),
112 (int)(functions.get_dic_encoding!=0)
113 );
114 functions.create = 0;
115 functions.spell = 0;
116 functions.destroy = 0;
117 functions.get_dic_encoding = 0;
118 valid = false;
119 }
120 }
121 else
122 {
123 valid = false;
124 }
125 if (valid) {
126 char* enc = (*functions.get_dic_encoding)(hunspell);
127 // Get at least the ISO set that comes with Yudit.
128 if (enc == 0) {
129 entryEncoder = new SEncoder ("utf-8");
130 } else if (strcmp (enc, "UTF-8") == 0) {
131 entryEncoder = new SEncoder ("utf-8");
132 } else if (strcmp (enc, "ISO8859-1") == 0) {
133 entryEncoder = new SEncoder ("iso-8859-1");
134 } else if (strcmp (enc, "ISO8859-2") == 0) {
135 entryEncoder = new SEncoder ("iso-8859-2");
136 } else if (strcmp (enc, "ISO8859-3") == 0) {
137 entryEncoder = new SEncoder ("iso-8859-3");
138 } else if (strcmp (enc, "ISO8859-4") == 0) {
139 entryEncoder = new SEncoder ("iso-8859-4");
140 } else if (strcmp (enc, "ISO8859-5") == 0) {
141 entryEncoder = new SEncoder ("iso-8859-5");
142 } else if (strcmp (enc, "ISO8859-6") == 0) {
143 entryEncoder = new SEncoder ("iso-8859-6");
144 } else if (strcmp (enc, "ISO8859-7") == 0) {
145 entryEncoder = new SEncoder ("iso-8859-7");
146 } else if (strcmp (enc, "ISO8859-8") == 0) {
147 entryEncoder = new SEncoder ("iso-8859-8");
148 } else if (strcmp (enc, "ISO8859-9") == 0) {
149 entryEncoder = new SEncoder ("iso-8859-9");
150 } else if (strcmp (enc, "ISO8859-13") == 0) {
151 entryEncoder = new SEncoder ("iso-8859-13");
152 } else if (strcmp (enc, "ISO8859-15") == 0) {
153 entryEncoder = new SEncoder ("iso-8859-15");
154 } else if (strcmp (enc, "ISO8859-16") == 0) {
155 entryEncoder = new SEncoder ("iso-8859-16");
156 } else if (strcmp (enc, "KOI8-R") == 0) {
157 entryEncoder = new SEncoder ("koi8-r");
158 } else {
159 fprintf (stderr, "Need hunspell encoder for %s\n", enc);
160 entryEncoder = new SEncoder ("utf-8");
161 }
162 }
163 }
164
~SHunspellPattern()165 SHunspellPattern::~SHunspellPattern ()
166 {
167 if (functions.destroy && hunspell)
168 {
169 (*functions.destroy) (hunspell);
170 }
171 if (entryEncoder) delete entryEncoder;
172 }
173
174 SString
loadLibrary(const SStringVector & path)175 SHunspellPattern::loadLibrary (const SStringVector& path)
176 {
177 if (dynHandle == 0)
178 {
179 SString libFile;
180 #ifdef USE_WINAPI
181 libFile = "libhunspell.dll";
182 #else
183 #ifdef __APPLE__
184 libFile = "libhunspell.dylib";
185 #else
186 libFile = "libhunspell.so";
187 #endif
188 #endif
189 SString ret = libFile;
190 SFile flib (libFile, path);
191 if (flib.size() > 0)
192 {
193 libFile = flib.getName();
194 fixFileName (libFile);
195 }
196 libFile.append ((char)0);
197 #ifdef USE_WINAPI
198 SEncoder u8("utf-8");
199 SEncoder u16("utf-16-le");
200 SString res = u16.encode (u8.decode (libFile));
201 WCHAR* filenameW = (WCHAR *) res.array();
202 dynHandle = LoadLibraryW (filenameW);
203 if (!dynHandle)
204 {
205 dynHandle = LoadLibraryA (libFile.array());
206 if (!dynHandle) return SString(ret);
207 }
208 #else
209 dynHandle = dlopen (libFile.array(), RTLD_LAZY);
210 if (!dynHandle)
211 {
212 fprintf (stderr, "Can not open shared library %s\n", libFile.array());
213 return SString(ret);
214 }
215 #endif
216 return SString ();
217 }
218 return SString ();
219 }
220
221 SString
getFolderFor(const SString & name,const SStringVector & path)222 SHunspellPattern::getFolderFor (const SString& name,
223 const SStringVector& path)
224 {
225 for (unsigned int i=0; i<path.size(); i++)
226 {
227 SString f = path[i];
228 f.append ("/");
229 f.append (name);
230 f.append (".dic");
231 SFile file (f);
232 if (file.size() > 0) return SString (path[i]);
233 }
234 return SString ("");
235 }
236
237 // Return a non "" string in case there are some missing files
238 SString
getMissingFile(const SString & name,const SStringVector & path)239 SHunspellPattern::getMissingFile (const SString& name,
240 const SStringVector& path)
241 {
242 SString missLib = loadLibrary (path);
243 if (missLib != "") return SString (missLib);
244 if (name == "")
245 {
246 return SString ("*.dic *.aff");
247 }
248 for (unsigned int i=0; i<path.size(); i++)
249 {
250 SString f_dic = path[i];
251 f_dic.append ("/");
252 f_dic.append (name);
253 f_dic.append (".dic");
254 SFile file_dic (f_dic);
255 // aff file should be in the same directory
256 if (file_dic.size() > 0)
257 {
258 SString f_aff = path[i];
259 f_aff.append ("/");
260 f_aff.append (name);
261 f_aff.append (".aff");
262 SFile file_aff (f_aff);
263 if (file_aff.size() > 0) return (SString (""));
264 f_aff = name;
265 f_aff.append (".aff");
266 return (SString (f_aff));
267 }
268 }
269 SString ret = name;
270 ret.append (".dic");
271 return SString (ret);
272 }
273
274
275 // This method updates matchBegin and matchEnd (exclusive)
276 // variables in case of a match, in case of no match, this
277 // will not be touched. Action will contain that action string.
278 bool
checkMatch()279 SHunspellPattern::checkMatch ()
280 {
281 if (current.size() == 0) return false;
282 if (current.size() == 1) return false;
283 // get the controls out first
284 SS_UCS4 chr = current[0];
285 if (isNumber (chr))
286 {
287 action = ACT_NUMBER;
288 matchBegin = matchEnd;
289 matchEnd++;
290 current.remove (0);
291 return true;
292 }
293 if (isOther (chr))
294 {
295 action = ACT_OTHER;
296 matchBegin = matchEnd;
297 matchEnd++;
298 current.remove (0);
299 return true;
300 }
301 if (isSeparator (chr) || chr == (SS_UCS4) '-')
302 {
303 action = ACT_CONTROL;
304 matchBegin = matchEnd;
305 matchEnd++;
306 current.remove (0);
307 return true;
308 }
309 if (isJapanese (chr))
310 {
311 action = ACT_NONE;
312 matchBegin = matchEnd;
313 matchEnd++;
314 current.remove (0);
315 return true;
316 }
317 unsigned int i;
318 for (i=0; i<current.size(); i++)
319 {
320 if (isSeparator (current[i])) break;
321 }
322 if (i==current.size()) return false;
323 if (i==0) return false; // never
324 action = ACT_NONE;
325 SV_UCS4 v = current;
326 v.truncate (v.size() -1);
327
328 matchBegin = matchEnd;
329 matchEnd = matchBegin + v.size();
330 for (i=0; i<v.size(); i++)
331 {
332 current.remove (0);
333 }
334 SV_UCS4 v1;
335 convertRovasiras (v, v1);
336 SString str = (entryEncoder==0) ? SString ("E_R_R_O_R") : entryEncoder->encode (v1);
337 str.append ((char) 0);
338 /*
339 fprintf (stderr, "call %08lx %08lx %s\n",
340 (unsigned long) functions.spell, (unsigned long) hunspell, str.array());
341 */
342 if ((*functions.spell)(hunspell, str.array())==0)
343 {
344 action = ACT_ERROR;
345 }
346 return true;
347 }
348
349 // There is no spell checking for japanese/chinese
350 bool
isJapanese(SS_UCS4 chr) const351 SHunspellPattern::isJapanese (SS_UCS4 chr) const
352 {
353 if (chr==SD_CD_ZWJ) return false;
354 if (chr==SD_CD_ZWNJ) return false;
355 SD_CharClass cc = getCharClass (chr);
356 if (cc == SD_CC_Mn || cc == SD_CC_Me) return false;
357
358
359 // CJK Symbols and Punctuation
360 if (chr >= 0x3000 && chr <=0x303f) return true;
361 // Hiragana
362 if (chr >= 0x3040 && chr <=0x309f) return true;
363 // Katakana
364 if (chr >= 0x30A0 && chr <=0x30ff) return true;
365 //
366 if (chr >= 0x31c0 && chr <=0x9fff) return true;
367
368 // Japanes fullwidth alphabet
369 if (chr >= 0xff21 && chr <=0xff5a) return false;
370
371 // Halfwidth and Fullwidth Forms
372 if (chr >= 0xff00 && chr <=0xffef) return true;
373 // CJK Compatibility Ideographs
374 if (chr >= 0xf900 && chr <=0xfaff) return true;
375 // CJK Unified Ideographs Extension B
376 if (chr >= 0x20000 && chr <=0x2A6DF) return true;
377 // CJK Compatibility Ideographs Supplement
378 if (chr >= 0x2F800 && chr <=0x2FA1F) return true;
379 return false;
380 }
381
382 // This includes numbers
383 bool
isSeparator(SS_UCS4 chr) const384 SHunspellPattern::isSeparator (SS_UCS4 chr) const
385 {
386 if (chr == 0xEE2F) return true; // HUNGARIAN RUNIC SEPARATOR in PUA
387
388 if (chr==SD_CD_ZWJ) return false;
389 if (chr==SD_CD_ZWNJ) return false; // this can be used within the word
390 SD_CharClass cc = getCharClass (chr);
391 // combined
392 if (cc == SD_CC_Mn || cc == SD_CC_Me) return false;
393
394 return (chr != (SS_UCS4) '-'
395 && cc != SD_CC_Lu && cc != SD_CC_Ll && cc != SD_CC_Lt
396 && cc != SD_CC_Lm && cc != SD_CC_Lo);
397 }
398 bool
isNumber(SS_UCS4 chr) const399 SHunspellPattern::isNumber (SS_UCS4 chr) const
400 {
401 SD_CharClass cc = getCharClass (chr);
402 return (cc == SD_CC_Nd || cc == SD_CC_Nl || cc == SD_CC_No);
403 }
404 bool
isOther(SS_UCS4 chr) const405 SHunspellPattern::isOther (SS_UCS4 chr) const
406 {
407 SD_CharClass cc = getCharClass (chr);
408 return (cc == SD_CC_So);
409 }
410
411 // Convert rovasiras into standard hungarian, all caps
412 static void
convertRovasiras(const SV_UCS4 & in,SV_UCS4 & out)413 convertRovasiras (const SV_UCS4& in, SV_UCS4& out)
414 {
415 SS_UCS4 chr;
416 for (unsigned int i=0; i<in.size(); i++)
417 {
418 chr = in[i];
419 switch ((int)chr)
420 {
421 case 0xEE00: out.append ((SS_UCS4) 'A'); break;
422 case 0xEE01: out.append ((SS_UCS4) 0xC1); break; // AA
423 case 0xEE02: out.append ((SS_UCS4) 'B'); break;
424 case 0xEE03: out.append ((SS_UCS4) 'C'); break;
425 case 0xEE04: out.append ((SS_UCS4) 'C'); out.append ((SS_UCS4) 'S'); break;
426 case 0xEE05: out.append ((SS_UCS4) 'D'); break;
427 case 0xEE06: out.append ((SS_UCS4) 'E'); break; // AE
428 case 0xEE07: out.append ((SS_UCS4) 'E'); break;
429 case 0xEE08: out.append ((SS_UCS4) 0xC9); break; // EE
430 case 0xEE09: out.append ((SS_UCS4) 'F'); break;
431 case 0xEE0A: out.append ((SS_UCS4) 'G'); break;
432 case 0xEE0B: out.append ((SS_UCS4) 'G'); out.append ((SS_UCS4) 'Y'); break;
433 case 0xEE0C: out.append ((SS_UCS4) 'H'); break;
434 case 0xEE0D: out.append ((SS_UCS4) 'I'); break;
435 case 0xEE0E: out.append ((SS_UCS4) 0xCD); break; // II
436 case 0xEE0F: out.append ((SS_UCS4) 'J'); break;
437 case 0xEE10: out.append ((SS_UCS4) 'K'); break; // AK
438 case 0xEE11: out.append ((SS_UCS4) 'K'); break; // EK
439 case 0xEE12: out.append ((SS_UCS4) 'L'); break;
440 case 0xEE13: out.append ((SS_UCS4) 'L'); out.append ((SS_UCS4) 'Y'); break;
441 case 0xEE14: out.append ((SS_UCS4) 'M'); break;
442 case 0xEE15: out.append ((SS_UCS4) 'N'); break;
443 case 0xEE16: out.append ((SS_UCS4) 'N'); out.append ((SS_UCS4) 'Y'); break;
444 case 0xEE17: out.append ((SS_UCS4) 'O'); break;
445 case 0xEE18: out.append ((SS_UCS4) 0xD3); break; // OO
446 case 0xEE19: out.append ((SS_UCS4) 0xD6); break; // OE
447 case 0xEE1A: out.append ((SS_UCS4) 0x150); break; // OEE
448 case 0xEE1B: out.append ((SS_UCS4) 'P'); break;
449 case 0xEE1C: out.append ((SS_UCS4) 'R'); break;
450 case 0xEE1D: out.append ((SS_UCS4) 'S'); break; // AS
451 case 0xEE1E: out.append ((SS_UCS4) 'S'); break; // ES
452 case 0xEE1F: out.append ((SS_UCS4) 'S'); out.append ((SS_UCS4) 'Z'); break;
453 case 0xEE20: out.append ((SS_UCS4) 'T'); break;
454 case 0xEE21: out.append ((SS_UCS4) 'T'); out.append ((SS_UCS4) 'Y'); break; // ATY
455 case 0xEE22: out.append ((SS_UCS4) 'T'); out.append ((SS_UCS4) 'Y'); break; // ETY
456 case 0xEE23: out.append ((SS_UCS4) 'U'); break;
457 case 0xEE24: out.append ((SS_UCS4) 0xDA); break; // UU
458 case 0xEE25: out.append ((SS_UCS4) 0xDC); break; // UE
459 case 0xEE26: out.append ((SS_UCS4) 0x170); break; // UEE
460 case 0xEE27: out.append ((SS_UCS4) 'V'); break;
461 case 0xEE28: out.append ((SS_UCS4) 'Z'); break;
462 case 0xEE29: out.append ((SS_UCS4) 'Z'); out.append ((SS_UCS4) 'S'); break;
463 case 0xEE2F: out.append ((SS_UCS4) ' '); break;
464
465 // NUMBERS
466 case 0xEE31: out.append ((SS_UCS4) '1'); break;
467 case 0xEE35: out.append ((SS_UCS4) '5'); break;
468 case 0xEE3A: out.append ((SS_UCS4) '1'); out.append ((SS_UCS4) '0'); break;
469 case 0xEE3B: out.append ((SS_UCS4) '5'); out.append ((SS_UCS4) '0'); break;
470 case 0xEE3C: out.append ((SS_UCS4) '1'); out.append ((SS_UCS4) '0'); out.append ((SS_UCS4) '0');break;
471 case 0xEE3D: out.append ((SS_UCS4) '1'); out.append ((SS_UCS4) '0'); out.append ((SS_UCS4) '0'); out.append ((SS_UCS4) '0'); break;
472
473 // LIGATURES
474 case 0xEE40: out.append ((SS_UCS4) 'A'); out.append ((SS_UCS4) 'B'); break;
475 case 0xEE41: out.append ((SS_UCS4) 'A'); out.append ((SS_UCS4) 'D'); break;
476 case 0xEE42: out.append ((SS_UCS4) 'A'); out.append ((SS_UCS4) 'L'); break;
477 case 0xEE43: out.append ((SS_UCS4) 'A'); out.append ((SS_UCS4) 'M'); out.append ((SS_UCS4) 'B'); break;
478 case 0xEE44: out.append ((SS_UCS4) 'A'); out.append ((SS_UCS4) 'N'); out.append ((SS_UCS4) 'D'); break;
479 case 0xEE45: out.append ((SS_UCS4) 'A'); out.append ((SS_UCS4) 'N'); out.append ((SS_UCS4) 'T'); break;
480 case 0xEE46: out.append ((SS_UCS4) 'A'); out.append ((SS_UCS4) 'R'); break;
481 case 0xEE47: out.append ((SS_UCS4) 'A'); out.append ((SS_UCS4) 'T'); out.append ((SS_UCS4) 'T'); break;
482 // AAR
483 case 0xEE48: out.append ((SS_UCS4) 0xC1); out.append ((SS_UCS4) 'R'); break;
484 case 0xEE49: out.append ((SS_UCS4) 'B'); out.append ((SS_UCS4) 'A'); break;
485 case 0xEE4A: out.append ((SS_UCS4) 'B'); out.append ((SS_UCS4) 'E'); break;
486 case 0xEE4B: out.append ((SS_UCS4) 'B'); out.append ((SS_UCS4) 'E'); out.append ((SS_UCS4) 'T'); break;
487 case 0xEE4C: out.append ((SS_UCS4) 'B'); out.append ((SS_UCS4) 'I'); break;
488 case 0xEE4D: out.append ((SS_UCS4) 'B'); out.append ((SS_UCS4) 'O'); break;
489 case 0xEE4E: out.append ((SS_UCS4) 'C'); out.append ((SS_UCS4) 'A'); out.append ((SS_UCS4) 'K'); break;
490 case 0xEE4F: out.append ((SS_UCS4) 'C'); out.append ((SS_UCS4) 'K'); break;
491
492 case 0xEE50: out.append ((SS_UCS4) 'C'); out.append ((SS_UCS4) 'S'); out.append ((SS_UCS4) 'A'); break;
493 case 0xEE51: out.append ((SS_UCS4) 'C'); out.append ((SS_UCS4) 'S'); out.append ((SS_UCS4) 'I'); out.append ((SS_UCS4) 'N'); break;
494 case 0xEE52: out.append ((SS_UCS4) 'D'); out.append ((SS_UCS4) 'U'); break;
495 case 0xEE53: out.append ((SS_UCS4) 'E'); out.append ((SS_UCS4) 'M'); out.append ((SS_UCS4) 'P'); break;
496 case 0xEE54: out.append ((SS_UCS4) 'E'); out.append ((SS_UCS4) 'N'); out.append ((SS_UCS4) 'T'); break;
497 case 0xEE55: out.append ((SS_UCS4) 'G'); out.append ((SS_UCS4) 'A'); break;
498 case 0xEE56: out.append ((SS_UCS4) 'G'); out.append ((SS_UCS4) 'E'); break;
499 case 0xEE57: out.append ((SS_UCS4) 'G'); out.append ((SS_UCS4) 'I'); break;
500 case 0xEE58: out.append ((SS_UCS4) 'G'); out.append ((SS_UCS4) 'O'); break;
501 case 0xEE59: out.append ((SS_UCS4) 'H'); out.append ((SS_UCS4) 'A'); break;
502 case 0xEE5A: out.append ((SS_UCS4) 'H'); out.append ((SS_UCS4) 'E'); break;
503 case 0xEE5B: out.append ((SS_UCS4) 'H'); out.append ((SS_UCS4) 'I'); break;
504 case 0xEE5C: out.append ((SS_UCS4) 'H'); out.append ((SS_UCS4) 'O'); break;
505 case 0xEE5D: out.append ((SS_UCS4) 'I'); out.append ((SS_UCS4) 'T'); break;
506 case 0xEE5E: out.append ((SS_UCS4) 'I'); out.append ((SS_UCS4) 'R'); out.append ((SS_UCS4) 'T'); break;
507 case 0xEE5F: out.append ((SS_UCS4) 'L'); out.append ((SS_UCS4) 'A'); break;
508
509 // LAA
510 case 0xEE60: out.append ((SS_UCS4) 'L'); out.append ((SS_UCS4) 0xC1); break;
511 case 0xEE61: out.append ((SS_UCS4) 'L'); out.append ((SS_UCS4) 'E'); break;
512 case 0xEE62: out.append ((SS_UCS4) 'L'); out.append ((SS_UCS4) 'O'); break;
513 case 0xEE63: out.append ((SS_UCS4) 'L'); out.append ((SS_UCS4) 'T'); break;
514 case 0xEE64: out.append ((SS_UCS4) 'M'); out.append ((SS_UCS4) 'B'); break;
515 case 0xEE65: out.append ((SS_UCS4) 'N'); out.append ((SS_UCS4) 'A'); break;
516 case 0xEE66: out.append ((SS_UCS4) 'N'); out.append ((SS_UCS4) 'A'); out.append ((SS_UCS4) 'P'); break;
517 case 0xEE67: out.append ((SS_UCS4) 'N'); out.append ((SS_UCS4) 'B'); break;
518 case 0xEE68: out.append ((SS_UCS4) 'N'); out.append ((SS_UCS4) 'C'); break;
519 case 0xEE69: out.append ((SS_UCS4) 'N'); out.append ((SS_UCS4) 'D'); break;
520 case 0xEE6A: out.append ((SS_UCS4) 'N'); out.append ((SS_UCS4) 'G'); out.append ((SS_UCS4) 'Y'); break;
521 case 0xEE6B: out.append ((SS_UCS4) 'N'); out.append ((SS_UCS4) 'I'); break;
522 case 0xEE6C: out.append ((SS_UCS4) 'N'); out.append ((SS_UCS4) 'K'); break;
523 case 0xEE6D: out.append ((SS_UCS4) 'N'); out.append ((SS_UCS4) 'T'); break;
524 case 0xEE6E: out.append ((SS_UCS4) 'O'); out.append ((SS_UCS4) 'R'); break;
525 case 0xEE6F: out.append ((SS_UCS4) 'R'); out.append ((SS_UCS4) 'A'); break;
526
527
528 case 0xEE70: out.append ((SS_UCS4) 'R'); out.append ((SS_UCS4) 'E'); break;
529 case 0xEE71: out.append ((SS_UCS4) 'R'); out.append ((SS_UCS4) 'I'); break;
530 case 0xEE72: out.append ((SS_UCS4) 'R'); out.append ((SS_UCS4) 'O'); break;
531 case 0xEE73: out.append ((SS_UCS4) 'R'); out.append ((SS_UCS4) 'T'); break;
532 case 0xEE74: out.append ((SS_UCS4) 'R'); out.append ((SS_UCS4) 'U'); break;
533 case 0xEE75: out.append ((SS_UCS4) 'S'); out.append ((SS_UCS4) 'A'); break;
534 case 0xEE76: out.append ((SS_UCS4) 'S'); out.append ((SS_UCS4) 'E'); break;
535 case 0xEE77: out.append ((SS_UCS4) 'S'); out.append ((SS_UCS4) 'I'); break;
536 case 0xEE78: out.append ((SS_UCS4) 'S'); out.append ((SS_UCS4) 'K'); break;
537 case 0xEE79: out.append ((SS_UCS4) 'S'); out.append ((SS_UCS4) 'M'); break;
538 case 0xEE7A: out.append ((SS_UCS4) 'S'); out.append ((SS_UCS4) 'O'); break;
539 case 0xEE7B: out.append ((SS_UCS4) 'S'); out.append ((SS_UCS4) 'P'); break;
540 case 0xEE7C: out.append ((SS_UCS4) 'S'); out.append ((SS_UCS4) 'T'); break;
541 case 0xEE7D: out.append ((SS_UCS4) 'S'); out.append ((SS_UCS4) 'Z'); out.append ((SS_UCS4) 'T'); break;
542 case 0xEE7E: out.append ((SS_UCS4) 'T'); out.append ((SS_UCS4) 'I'); break;
543 case 0xEE7F: out.append ((SS_UCS4) 'T'); out.append ((SS_UCS4) 'P'); out.append ((SS_UCS4) 'R'); out.append ((SS_UCS4) 'U'); break;
544
545 case 0xEE80: out.append ((SS_UCS4) 'T'); out.append ((SS_UCS4) 'P'); out.append ((SS_UCS4) 'R'); out.append ((SS_UCS4) 'U'); out.append ((SS_UCS4) 'S'); break;
546 case 0xEE81: out.append ((SS_UCS4) 'T'); out.append ((SS_UCS4) 'Y'); out.append ((SS_UCS4) 'A'); break;
547 case 0xEE82: out.append ((SS_UCS4) 'U'); out.append ((SS_UCS4) 'L'); break;
548 case 0xEE83: out.append ((SS_UCS4) 'U'); out.append ((SS_UCS4) 'M'); break;
549 case 0xEE84: out.append ((SS_UCS4) 'U'); out.append ((SS_UCS4) 'N'); out.append ((SS_UCS4) 'K');break;
550 case 0xEE85: out.append ((SS_UCS4) 'U'); out.append ((SS_UCS4) 'R'); break;
551 case 0xEE86: out.append ((SS_UCS4) 'U'); out.append ((SS_UCS4) 'S'); break;
552 case 0xEE87: out.append ((SS_UCS4) 'V'); out.append ((SS_UCS4) 'A'); break;
553 // VAAR
554 case 0xEE88: out.append ((SS_UCS4) 'V'); out.append ((SS_UCS4) 0xc1); out.append ((SS_UCS4) 'R'); break;
555 case 0xEE89: out.append ((SS_UCS4) 'Z'); out.append ((SS_UCS4) 'A'); break;
556 case 0xEE8A: out.append ((SS_UCS4) 'Z'); out.append ((SS_UCS4) 'R'); break;
557 case 0xEE8B: out.append ((SS_UCS4) 'Z'); out.append ((SS_UCS4) 'T'); break;
558 default: out.append (chr);
559 break;
560 }
561 }
562 }
563