]> git.lyx.org Git - features.git/blob - src/Encoding.cpp
0620a149cc56816f81a4778638fd63356b22d78f
[features.git] / src / Encoding.cpp
1 /**
2  * \file Encoding.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author Jean-Marc Lasgouttes
8  * \author Dekel Tsur
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 #include <config.h>
14
15 #include "Encoding.h"
16
17 #include "LaTeXFeatures.h"
18 #include "Lexer.h"
19 #include "LyXRC.h"
20
21 #include "support/debug.h"
22 #include "support/FileName.h"
23 #include "support/lstrings.h"
24 #include "support/unicode.h"
25
26 #include <boost/cstdint.hpp>
27
28 #include <sstream>
29
30 using namespace std;
31 using namespace lyx::support;
32
33 namespace lyx {
34
35 Encodings encodings;
36
37 Encodings::MathCommandSet Encodings::mathcmd;
38 Encodings::TextCommandSet Encodings::textcmd;
39 Encodings::MathSymbolSet  Encodings::mathsym;
40
41 namespace {
42
43 char_type arabic_table[172][4] = {
44         {0xfe80, 0xfe80, 0xfe80, 0xfe80}, // 0x0621 = hamza
45         {0xfe81, 0xfe82, 0xfe81, 0xfe82}, // 0x0622 = ligature madda on alef
46         {0xfe83, 0xfe84, 0xfe83, 0xfe84}, // 0x0623 = ligature hamza on alef
47         {0xfe85, 0xfe86, 0xfe85, 0xfe86}, // 0x0624 = ligature hamza on waw
48         {0xfe87, 0xfe88, 0xfe87, 0xfe88}, // 0x0625 = ligature hamza under alef
49         {0xfe89, 0xfe8a, 0xfe8b, 0xfe8c}, // 0x0626 = ligature hamza on ya
50         {0xfe8d, 0xfe8e, 0xfe8d, 0xfe8e}, // 0x0627 = alef
51         {0xfe8f, 0xfe90, 0xfe91, 0xfe92}, // 0x0628 = baa
52         {0xfe93, 0xfe94, 0xfe93, 0xfe94}, // 0x0629 = taa marbuta
53         {0xfe95, 0xfe96, 0xfe97, 0xfe98}, // 0x062a = taa
54         {0xfe99, 0xfe9a, 0xfe9b, 0xfe9c}, // 0x062b = thaa
55         {0xfe9d, 0xfe9e, 0xfe9f, 0xfea0}, // 0x062c = jeem
56         {0xfea1, 0xfea2, 0xfea3, 0xfea4}, // 0x062d = haa
57         {0xfea5, 0xfea6, 0xfea7, 0xfea8}, // 0x062e = khaa
58         {0xfea9, 0xfeaa, 0xfea9, 0xfeaa}, // 0x062f = dal
59
60         {0xfeab, 0xfeac, 0xfeab, 0xfeac}, // 0x0630 = thal
61         {0xfead, 0xfeae, 0xfead, 0xfeae}, // 0x0631 = ra
62         {0xfeaf, 0xfeb0, 0xfeaf, 0xfeb0}, // 0x0632 = zain
63         {0xfeb1, 0xfeb2, 0xfeb3, 0xfeb4}, // 0x0633 = seen
64         {0xfeb5, 0xfeb6, 0xfeb7, 0xfeb8}, // 0x0634 = sheen
65         {0xfeb9, 0xfeba, 0xfebb, 0xfebc}, // 0x0635 = sad
66         {0xfebd, 0xfebe, 0xfebf, 0xfec0}, // 0x0636 = dad
67         {0xfec1, 0xfec2, 0xfec3, 0xfec4}, // 0x0637 = tah
68         {0xfec5, 0xfec6, 0xfec7, 0xfec8}, // 0x0638 = zah
69         {0xfec9, 0xfeca, 0xfecb, 0xfecc}, // 0x0639 = ain
70         {0xfecd, 0xfece, 0xfecf, 0xfed0}, // 0x063a = ghain
71         {0, 0, 0, 0}, // 0x063b
72         {0, 0, 0, 0}, // 0x063c
73         {0, 0, 0, 0}, // 0x063d
74         {0, 0, 0, 0}, // 0x063e
75         {0, 0, 0, 0}, // 0x063f
76
77         {0, 0, 0, 0}, // 0x0640
78         {0xfed1, 0xfed2, 0xfed3, 0xfed4}, // 0x0641 = fa
79         {0xfed5, 0xfed6, 0xfed7, 0xfed8}, // 0x0642 = qaf
80         {0xfed9, 0xfeda, 0xfedb, 0xfedc}, // 0x0643 = kaf
81         {0xfedd, 0xfede, 0xfedf, 0xfee0}, // 0x0644 = lam
82         {0xfee1, 0xfee2, 0xfee3, 0xfee4}, // 0x0645 = meem
83         {0xfee5, 0xfee6, 0xfee7, 0xfee8}, // 0x0646 = noon
84         {0xfee9, 0xfeea, 0xfeeb, 0xfeec}, // 0x0647 = ha
85         {0xfeed, 0xfeee, 0xfeed, 0xfeee}, // 0x0648 = waw
86         {0xfeef, 0xfef0, 0xfeef, 0xfef0}, // 0x0649 = alef maksura
87         {0xfef1, 0xfef2, 0xfef3, 0xfef4}, // 0x064a = ya
88         {0x065b, 0x065b, 0x065b, 0x065b}, // 0x064b = fathatan
89         {0x065c, 0x065c, 0x065c, 0x065c}, // 0x064c = dammatan
90         {0x064d, 0x064d, 0x064d, 0x064d}, // 0x064d = kasratan
91         {0x064e, 0x064e, 0x064e, 0x064e}, // 0x064e = fatha
92         {0x064f, 0x064f, 0x064f, 0x064f}, // 0x064f = damma
93
94         {0x0650, 0x0650, 0x0650, 0x0650}, // 0x0650 = kasra
95         {0x0651, 0x0651, 0x0651, 0x0651}, // 0x0651 = shadda
96         {0x0652, 0x0652, 0x0652, 0x0652}, // 0x0652 = sukun
97
98         {0, 0, 0, 0}, // 0x0653
99         {0, 0, 0, 0}, // 0x0654
100         {0, 0, 0, 0}, // 0x0655
101         {0, 0, 0, 0}, // 0x0656
102         {0, 0, 0, 0}, // 0x0657
103         {0, 0, 0, 0}, // 0x0658
104         {0, 0, 0, 0}, // 0x0659
105         {0, 0, 0, 0}, // 0x065a
106         {0, 0, 0, 0}, // 0x065b
107         {0, 0, 0, 0}, // 0x065c
108         {0, 0, 0, 0}, // 0x065d
109         {0, 0, 0, 0}, // 0x065e
110         {0, 0, 0, 0}, // 0x065f
111         {0, 0, 0, 0}, // 0x0660
112         {0, 0, 0, 0}, // 0x0661
113         {0, 0, 0, 0}, // 0x0662
114         {0, 0, 0, 0}, // 0x0663
115         {0, 0, 0, 0}, // 0x0664
116         {0, 0, 0, 0}, // 0x0665
117         {0, 0, 0, 0}, // 0x0666
118         {0, 0, 0, 0}, // 0x0667
119         {0, 0, 0, 0}, // 0x0668
120         {0, 0, 0, 0}, // 0x0669
121         {0, 0, 0, 0}, // 0x066a
122         {0, 0, 0, 0}, // 0x066b
123         {0, 0, 0, 0}, // 0x066c
124         {0, 0, 0, 0}, // 0x066d
125         {0, 0, 0, 0}, // 0x066e
126         {0, 0, 0, 0}, // 0x066f
127         {0, 0, 0, 0}, // 0x0670
128         {0, 0, 0, 0}, // 0x0671
129         {0, 0, 0, 0}, // 0x0672
130         {0, 0, 0, 0}, // 0x0673
131         {0, 0, 0, 0}, // 0x0674
132         {0, 0, 0, 0}, // 0x0675
133         {0, 0, 0, 0}, // 0x0676
134         {0, 0, 0, 0}, // 0x0677
135         {0, 0, 0, 0}, // 0x0678
136         {0, 0, 0, 0}, // 0x0679
137         {0, 0, 0, 0}, // 0x067a
138         {0, 0, 0, 0}, // 0x067b
139         {0, 0, 0, 0}, // 0x067c
140         {0, 0, 0, 0}, // 0x067d
141         {0xfb56, 0xfb57, 0xfb58, 0xfb59}, // 0x067e = peh 
142         {0, 0, 0, 0}, // 0x067f
143         {0, 0, 0, 0}, // 0x0680
144         {0, 0, 0, 0}, // 0x0681
145         {0, 0, 0, 0}, // 0x0682
146         {0, 0, 0, 0}, // 0x0683
147         {0, 0, 0, 0}, // 0x0684
148         {0, 0, 0, 0}, // 0x0685
149         {0xfb7a, 0xfb7b, 0xfb7c, 0xfb7d}, // 0x0686 = tcheh 
150         {0, 0, 0, 0}, // 0x0687
151         {0, 0, 0, 0}, // 0x0688
152         {0, 0, 0, 0}, // 0x0689
153         {0, 0, 0, 0}, // 0x068a
154         {0, 0, 0, 0}, // 0x068b
155         {0, 0, 0, 0}, // 0x068c
156         {0, 0, 0, 0}, // 0x068d
157         {0, 0, 0, 0}, // 0x068e
158         {0, 0, 0, 0}, // 0x068f
159         {0, 0, 0, 0}, // 0x0690
160         {0, 0, 0, 0}, // 0x0691
161         {0, 0, 0, 0}, // 0x0692
162         {0, 0, 0, 0}, // 0x0693
163         {0, 0, 0, 0}, // 0x0694
164         {0, 0, 0, 0}, // 0x0695
165         {0, 0, 0, 0}, // 0x0696
166         {0, 0, 0, 0}, // 0x0697
167         {0xfb8a, 0xfb8b, 0xfb8a, 0xfb8b}, // 0x0698 = jeh
168         {0, 0, 0, 0}, // 0x0699
169         {0, 0, 0, 0}, // 0x069a
170         {0, 0, 0, 0}, // 0x069b
171         {0, 0, 0, 0}, // 0x069c
172         {0, 0, 0, 0}, // 0x069d
173         {0, 0, 0, 0}, // 0x069e
174         {0, 0, 0, 0}, // 0x069f
175         {0, 0, 0, 0}, // 0x06a0
176         {0, 0, 0, 0}, // 0x06a1
177         {0, 0, 0, 0}, // 0x06a2
178         {0, 0, 0, 0}, // 0x06a3
179         {0, 0, 0, 0}, // 0x06a4
180         {0, 0, 0, 0}, // 0x06a5
181         {0, 0, 0, 0}, // 0x06a6
182         {0, 0, 0, 0}, // 0x06a7
183         {0, 0, 0, 0}, // 0x06a8
184         {0xfb8e, 0xfb8f, 0xfb90, 0xfb91}, // 0x06a9 = farsi kaf 
185         {0, 0, 0, 0}, // 0x06aa
186         {0, 0, 0, 0}, // 0x06ab
187         {0, 0, 0, 0}, // 0x06ac
188         {0, 0, 0, 0}, // 0x06ad
189         {0, 0, 0, 0}, // 0x06ae
190         {0xfb92, 0xfb93, 0xfb94, 0xfb95}, // 0x06af = gaf 
191         {0, 0, 0, 0}, // 0x06b0
192         {0, 0, 0, 0}, // 0x06b1
193         {0, 0, 0, 0}, // 0x06b2
194         {0, 0, 0, 0}, // 0x06b3
195         {0, 0, 0, 0}, // 0x06b4
196         {0, 0, 0, 0}, // 0x06b5
197         {0, 0, 0, 0}, // 0x06b6
198         {0, 0, 0, 0}, // 0x06b7
199         {0, 0, 0, 0}, // 0x06b8
200         {0, 0, 0, 0}, // 0x06b9
201         {0, 0, 0, 0}, // 0x06ba
202         {0, 0, 0, 0}, // 0x06bb
203         {0, 0, 0, 0}, // 0x06bc
204         {0, 0, 0, 0}, // 0x06bd
205         {0, 0, 0, 0}, // 0x06be
206         {0, 0, 0, 0}, // 0x06bf
207         {0, 0, 0, 0}, // 0x06c0
208         {0, 0, 0, 0}, // 0x06c1
209         {0, 0, 0, 0}, // 0x06c2
210         {0, 0, 0, 0}, // 0x06c3
211         {0, 0, 0, 0}, // 0x06c4
212         {0, 0, 0, 0}, // 0x06c5
213         {0, 0, 0, 0}, // 0x06c6
214         {0, 0, 0, 0}, // 0x06c7
215         {0, 0, 0, 0}, // 0x06c8
216         {0, 0, 0, 0}, // 0x06c9
217         {0, 0, 0, 0}, // 0x06ca
218         {0, 0, 0, 0}, // 0x06cb
219         {0xfbfc, 0xfbfd, 0xfbfe, 0xfbff} // 0x06cc = farsi yeh  
220 };
221
222
223 char_type const arabic_start = 0x0621;
224 char_type const arabic_end = 0x06cc;
225
226
227 /// Information about a single UCS4 character
228 struct CharInfo {
229         /// LaTeX command (text mode) for this character
230         docstring textcommand;
231         /// LaTeX command (math mode) for this character
232         docstring mathcommand;
233         /// Needed LaTeX preamble (or feature) for text mode
234         string textpreamble;
235         /// Needed LaTeX preamble (or feature) for math mode
236         string mathpreamble;
237         /// Is this a combining character?
238         bool combining;
239         /// Is \c textpreamble a feature known by LaTeXFeatures, or a raw LaTeX
240         /// command?
241         bool textfeature;
242         /// Is \c mathpreamble a feature known by LaTeXFeatures, or a raw LaTeX
243         /// command?
244         bool mathfeature;
245         /// Always force the LaTeX command, even if the encoding contains
246         /// this character?
247         bool force;
248 };
249
250
251 typedef map<char_type, CharInfo> CharInfoMap;
252 CharInfoMap unicodesymbols;
253
254 typedef std::set<char_type> CharSet;
255 CharSet forced;
256
257
258 /// The highest code point in UCS4 encoding (1<<20 + 1<<16)
259 char_type const max_ucs4 = 0x110000;
260
261 } // namespace anon
262
263
264 EncodingException::EncodingException(char_type c)
265         : failed_char(c), par_id(0), pos(0)
266 {
267 }
268
269
270 const char * EncodingException::what() const throw()
271 {
272         return "Could not find LaTeX command for a character";
273 }
274
275
276 Encoding::Encoding(string const & n, string const & l, string const & g,
277                    string const & i, bool f, Encoding::Package p)
278         : name_(n), latexName_(l), guiName_(g), iconvName_(i), fixedwidth_(f), package_(p)
279 {
280         if (n == "ascii") {
281                 // ASCII can encode 128 code points and nothing else
282                 start_encodable_ = 128;
283                 complete_ = true;
284         } else if (i == "UTF-8") {
285                 // UTF8 can encode all UCS4 code points
286                 start_encodable_ = max_ucs4;
287                 complete_ = true;
288         } else {
289                 complete_ = false;
290         }
291 }
292
293
294 void Encoding::init() const
295 {
296         if (complete_)
297                 return;
298
299         start_encodable_ = 0;
300         // temporarily switch off lyxerr, since we will generate iconv errors
301         lyxerr.disable();
302         if (fixedwidth_) {
303                 // We do not need to check all UCS4 code points, it is enough
304                 // if we check all 256 code points of this encoding.
305                 for (unsigned short j = 0; j < 256; ++j) {
306                         char const c = char(j);
307                         vector<char_type> const ucs4 = eightbit_to_ucs4(&c, 1, iconvName_);
308                         if (ucs4.size() != 1)
309                                 continue;
310                         char_type const uc = ucs4[0];
311                         CharInfoMap::const_iterator const it = unicodesymbols.find(uc);
312                         if (it == unicodesymbols.end() || !it->second.force)
313                                 encodable_.insert(uc);
314                 }
315         } else {
316                 // We do not know how many code points this encoding has, and
317                 // they do not have a direct representation as a single byte,
318                 // therefore we need to check all UCS4 code points.
319                 // This is expensive!
320                 for (char_type c = 0; c < max_ucs4; ++c) {
321                         vector<char> const eightbit = ucs4_to_eightbit(&c, 1, iconvName_);
322                         if (!eightbit.empty()) {
323                                 CharInfoMap::const_iterator const it = unicodesymbols.find(c);
324                                 if (it == unicodesymbols.end() || !it->second.force)
325                                         encodable_.insert(c);
326                         }
327                 }
328         }
329         lyxerr.enable();
330         CharSet::iterator it = encodable_.find(start_encodable_);
331         while (it != encodable_.end()) {
332                 encodable_.erase(it);
333                 ++start_encodable_;
334                 it = encodable_.find(start_encodable_);
335         }
336         complete_ = true;
337 }
338
339
340 docstring Encoding::latexChar(char_type c, bool for_mathed) const
341 {
342         // assure the used encoding is properly initialized
343         init();
344
345         if (iconvName_ == "UTF-8" && package_ == none)
346                 return docstring(1, c);
347         if (c < start_encodable_ && !encodings.isForced(c))
348                 return docstring(1, c);
349         if (encodable_.find(c) != encodable_.end())
350                 return docstring(1, c);
351         if (for_mathed)
352                 return docstring();
353
354         // c cannot (or should not) be encoded in this encoding
355         CharInfoMap::const_iterator const it = unicodesymbols.find(c);
356         if (it == unicodesymbols.end())
357                 throw EncodingException(c);
358         // at least one of mathcommand and textcommand is nonempty
359         if (it->second.textcommand.empty())
360                 return "\\ensuremath{" + it->second.mathcommand + '}';
361         return it->second.textcommand;
362 }
363
364
365 vector<char_type> Encoding::symbolsList() const
366 {
367         // assure the used encoding is properly initialized
368         init();
369
370         // first all encodable characters
371         vector<char_type> symbols(encodable_.begin(), encodable_.end());
372         // add those below start_encodable_
373         for (char_type c = 0; c < start_encodable_; ++c)
374                 symbols.push_back(c);
375         // now the ones from the unicodesymbols file
376         CharInfoMap::const_iterator const end = unicodesymbols.end();
377         CharInfoMap::const_iterator it = unicodesymbols.begin();
378         for (; it != end; ++it)
379                 symbols.push_back(it->first);
380         return symbols;
381 }
382
383
384 bool Encodings::latexMathChar(char_type c, bool mathmode,
385                         Encoding const * encoding, docstring & command)
386 {
387         if (encoding)
388                 command = encoding->latexChar(c, true);
389
390         CharInfoMap::const_iterator const it = unicodesymbols.find(c);
391         if (it == unicodesymbols.end()) {
392                 if (!encoding || command.empty())
393                         throw EncodingException(c);
394                 if (mathmode)
395                         addMathSym(c);
396                 return false;
397         }
398         // at least one of mathcommand and textcommand is nonempty
399         bool use_math = (mathmode && !it->second.mathcommand.empty()) ||
400                         (!mathmode && it->second.textcommand.empty());
401         if (use_math) {
402                 command = it->second.mathcommand;
403                 addMathCmd(c);
404         } else {
405                 if (!encoding || command.empty()) {
406                         command = it->second.textcommand;
407                         addTextCmd(c);
408                 } else if (mathmode)
409                         addMathSym(c);
410         }
411         return use_math;
412 }
413
414
415 char_type Encodings::fromLaTeXCommand(docstring const & cmd, bool & combining)
416 {
417         CharInfoMap::const_iterator const end = unicodesymbols.end();
418         CharInfoMap::const_iterator it = unicodesymbols.begin();
419         for (; it != end; ++it) {
420                 docstring const math = it->second.mathcommand;
421                 docstring const text = it->second.textcommand;
422                 if (math == cmd || text == cmd) {
423                         combining = it->second.combining;
424                         return it->first;
425                 }
426         }
427         return 0;
428 }
429
430
431 docstring Encodings::fromLaTeXCommand(docstring const & cmd, docstring & rem)
432 {
433         docstring symbols;
434         size_t i = 0;
435         size_t const cmdend = cmd.size();
436         CharInfoMap::const_iterator const uniend = unicodesymbols.end();
437         for (size_t j = 0; j < cmdend; ++j) {
438                 // Also get the char after a backslash
439                 if (j + 1 < cmdend && cmd[j] == '\\')
440                         ++j;
441                 // If a macro argument follows, get it, too
442                 if (j + 1 < cmdend && cmd[j + 1] == '{') {
443                         size_t k = j + 1;
444                         int count = 1;
445                         while (k < cmdend && count && k != docstring::npos) {
446                                 k = cmd.find_first_of(from_ascii("{}"), k + 1);
447                                 if (cmd[k] == '{')
448                                         ++count;
449                                 else
450                                         --count;
451                         }
452                         if (k != docstring::npos)
453                                 j = k;
454                 }
455                 // Start with this substring and try augmenting it when it is
456                 // the prefix of some command in the unicodesymbols file
457                 docstring const subcmd = cmd.substr(i, j - i + 1);
458
459                 CharInfoMap::const_iterator it = unicodesymbols.begin();
460                 size_t unicmd_size = 0;
461                 char_type c = 0;
462                 for (; it != uniend; ++it) {
463                         docstring const math = it->second.mathcommand;
464                         docstring const text = it->second.textcommand;
465                         size_t cur_size = max(math.size(), text.size());
466                         // The current math or text unicode command cannot
467                         // match, or we already matched a longer one
468                         if (cur_size < subcmd.size() || cur_size <= unicmd_size)
469                                 continue;
470
471                         docstring tmp = subcmd;
472                         size_t k = j;
473                         while (prefixIs(math, tmp) || prefixIs(text, tmp)) {
474                                 ++k;
475                                 if (k >= cmdend || cur_size <= tmp.size())
476                                         break;
477                                 tmp += cmd[k];
478                         }
479                         // No match
480                         if (k == j)
481                                 continue;
482
483                         // The last added char caused a mismatch, because
484                         // we didn't exhaust the chars in cmd and didn't
485                         // exceed the maximum size of the current unicmd
486                         if (k < cmdend && cur_size > tmp.size())
487                                 tmp.resize(tmp.size() - 1);
488
489                         // If this is an exact match, we found a (longer)
490                         // matching command in the unicodesymbols file
491                         if (math == tmp || text == tmp) {
492                                 c = it->first;
493                                 j = k - 1;
494                                 i = j + 1;
495                                 unicmd_size = cur_size;
496                         }
497                 }
498                 if (unicmd_size)
499                         symbols += c;
500                 else if (j + 1 == cmdend)
501                         // No luck. Return what remains
502                         rem = cmd.substr(i);
503         }
504         return symbols;
505 }
506
507
508 void Encodings::validate(char_type c, LaTeXFeatures & features, bool for_mathed)
509 {
510         CharInfoMap::const_iterator const it = unicodesymbols.find(c);
511         if (it != unicodesymbols.end()) {
512                 // In mathed, c could be used both in textmode and mathmode
513                 bool const use_math = (for_mathed && isMathCmd(c)) ||
514                                       (!for_mathed && it->second.textcommand.empty());
515                 bool const use_text = (for_mathed && isTextCmd(c)) ||
516                                       (!for_mathed && !it->second.textcommand.empty());
517                 if (use_math) {
518                         if (!it->second.mathpreamble.empty()) {
519                                 if (it->second.mathfeature)
520                                         features.require(it->second.mathpreamble);
521                                 else
522                                         features.addPreambleSnippet(it->second.mathpreamble);
523                         }
524                 }
525                 if (use_text) {
526                         if (!it->second.textpreamble.empty()) {
527                                 if (it->second.textfeature)
528                                         features.require(it->second.textpreamble);
529                                 else
530                                         features.addPreambleSnippet(it->second.textpreamble);
531                         }
532                 }
533         }
534         if (for_mathed && isMathSym(c)) {
535                 features.require("relsize");
536                 features.require("lyxmathsym");
537         }
538 }
539
540
541 bool Encodings::isHebrewComposeChar(char_type c)
542 {
543         return c <= 0x05c2 && c >= 0x05b0 && c != 0x05be && c != 0x05c0;
544 }
545
546
547 // Special Arabic letters are ones that do not get connected from left
548 // they are hamza, alef_madda, alef_hamza, waw_hamza, alef_hamza_under,
549 // alef, tah_marbota, dal, thal, rah, zai, wow, alef_maksoura
550
551 bool Encodings::isArabicSpecialChar(char_type c)
552 {
553         return (c >= 0x0621 && c <= 0x0625) || (c >= 0x0630 && c <= 0x0632)
554                 || c == 0x0627 || c == 0x0629 || c == 0x062f || c == 0x0648
555                 || c == 0x0649 || c == 0x0698;
556 }
557
558
559 bool Encodings::isArabicComposeChar(char_type c)
560 {
561         return c >= 0x064b && c <= 0x0652;
562 }
563
564
565 bool Encodings::isArabicChar(char_type c)
566 {
567         return c >= arabic_start && c <= arabic_end
568                 && arabic_table[c-arabic_start][0];
569 }
570
571
572 char_type Encodings::transformChar(char_type c, Encodings::LetterForm form)
573 {
574         return isArabicChar(c) ? arabic_table[c-arabic_start][form] : c;
575 }
576
577
578 bool Encodings::isCombiningChar(char_type c)
579 {
580         CharInfoMap::const_iterator const it = unicodesymbols.find(c);
581         if (it != unicodesymbols.end())
582                 return it->second.combining;
583         return false;
584 }
585
586
587 bool Encodings::isKnownScriptChar(char_type const c, string & preamble)
588 {
589         CharInfoMap::const_iterator const it = unicodesymbols.find(c);
590
591         if (it == unicodesymbols.end())
592                 return false;
593
594         if (it->second.textpreamble != "textgreek" && it->second.textpreamble != "textcyr")
595                 return false;
596
597         if (preamble.empty()) {
598                 preamble = it->second.textpreamble;
599                 return true;
600         }
601         return it->second.textpreamble == preamble;
602 }
603
604
605 bool Encodings::isForced(char_type c)
606 {
607         return (!forced.empty() && forced.find(c) != forced.end());
608 }
609
610
611 Encoding const * Encodings::fromLyXName(string const & name) const
612 {
613         EncodingList::const_iterator const it = encodinglist.find(name);
614         return it != encodinglist.end() ? &it->second : 0;
615 }
616
617
618 Encoding const * Encodings::fromLaTeXName(string const & name) const
619 {
620         // We don't use find_if because it makes copies of the pairs in
621         // the map.
622         // This linear search is OK since we don't have many encodings.
623         // Users could even optimize it by putting the encodings they use
624         // most at the top of lib/encodings.
625         EncodingList::const_iterator const end = encodinglist.end();
626         for (EncodingList::const_iterator it = encodinglist.begin(); it != end; ++it)
627                 if (it->second.latexName() == name)
628                         return &it->second;
629         return 0;
630 }
631
632
633 Encodings::Encodings()
634 {
635 }
636
637
638 void Encodings::read(FileName const & encfile, FileName const & symbolsfile)
639 {
640         // We must read the symbolsfile first, because the Encoding
641         // constructor depends on it.
642         Lexer symbolslex;
643         symbolslex.setFile(symbolsfile);
644         bool getNextToken = true;
645         while (symbolslex.isOK()) {
646                 char_type symbol;
647                 CharInfo info;
648                 string flags;
649
650                 if (getNextToken) {
651                         if (!symbolslex.next(true))
652                                 break;
653                 } else
654                         getNextToken = true;
655
656                 istringstream is(symbolslex.getString());
657                 // reading symbol directly does not work if
658                 // char_type == wchar_t.
659                 boost::uint32_t tmp;
660                 if(!(is >> hex >> tmp))
661                         break;
662                 symbol = tmp;
663
664                 if (!symbolslex.next(true))
665                         break;
666                 info.textcommand = symbolslex.getDocString();
667                 if (!symbolslex.next(true))
668                         break;
669                 info.textpreamble = symbolslex.getString();
670                 if (!symbolslex.next(true))
671                         break;
672                 flags = symbolslex.getString();
673
674                 info.combining = false;
675                 info.textfeature = false;
676                 info.force = false;
677                 while (!flags.empty()) {
678                         string flag;
679                         flags = split(flags, flag, ',');
680                         if (flag == "combining")
681                                 info.combining = true;
682                         else if (flag == "force") {
683                                 info.force = true;
684                                 forced.insert(symbol);
685                         } else
686                                 lyxerr << "Ignoring unknown flag `" << flag
687                                        << "' for symbol `0x"
688                                        << hex << symbol << dec
689                                        << "'." << endl;
690                 }
691                 // mathcommand and mathpreamble have been added for 1.6.0.
692                 // make them optional so that old files still work.
693                 int const lineno = symbolslex.lineNumber();
694                 bool breakout = false;
695                 if (symbolslex.next(true)) {
696                         if (symbolslex.lineNumber() != lineno) {
697                                 // line in old format without mathcommand and mathpreamble
698                                 getNextToken = false;
699                         } else {
700                                 info.mathcommand = symbolslex.getDocString();
701                                 if (symbolslex.next(true)) {
702                                         if (symbolslex.lineNumber() != lineno) {
703                                                 // line in new format with mathcommand only
704                                                 getNextToken = false;
705                                         } else {
706                                                 // line in new format with mathcommand and mathpreamble
707                                                 info.mathpreamble = symbolslex.getString();
708                                         }
709                                 } else
710                                         breakout = true;
711                         }
712                 } else {
713                         breakout = true;
714                 }
715
716                 if (!info.textpreamble.empty())
717                         info.textfeature = info.textpreamble[0] != '\\';
718                 if (!info.mathpreamble.empty())
719                         info.mathfeature = info.mathpreamble[0] != '\\';
720
721                 LYXERR(Debug::INFO, "Read unicode symbol " << symbol << " '"
722                         << to_utf8(info.textcommand) << "' '" << info.textpreamble
723                         << "' " << info.combining << ' ' << info.textfeature
724                         << " '" << to_utf8(info.mathcommand) << "' '"
725                         << info.mathpreamble << "' " << info.mathfeature);
726
727                 // we assume that at least one command is nonempty when using unicodesymbols
728                 if (!info.textcommand.empty() || !info.mathcommand.empty())
729                         unicodesymbols[symbol] = info;
730
731                 if (breakout)
732                         break;
733         }
734
735         // Now read the encodings
736         enum {
737                 et_encoding = 1,
738                 et_end,
739         };
740
741         LexerKeyword encodingtags[] = {
742                 { "encoding", et_encoding },
743                 { "end", et_end }
744         };
745
746         Lexer lex(encodingtags);
747         lex.setFile(encfile);
748         lex.setContext("Encodings::read");
749         while (lex.isOK()) {
750                 switch (lex.lex()) {
751                 case et_encoding:
752                 {
753                         lex.next();
754                         string const name = lex.getString();
755                         lex.next();
756                         string const latexname = lex.getString();
757                         lex.next();
758                         string const guiname = lex.getString();
759                         lex.next();
760                         string const iconvname = lex.getString();
761                         lex.next();
762                         string const width = lex.getString();
763                         bool fixedwidth = false;
764                         if (width == "fixed")
765                                 fixedwidth = true;
766                         else if (width == "variable")
767                                 fixedwidth = false;
768                         else
769                                 lex.printError("Unknown width");
770
771                         lex.next();
772                         string const p = lex.getString();
773                         Encoding::Package package = Encoding::none;
774                         if (p == "none")
775                                 package = Encoding::none;
776                         else if (p == "inputenc")
777                                 package = Encoding::inputenc;
778                         else if (p == "CJK")
779                                 package = Encoding::CJK;
780                         else if (p == "japanese")
781                                 package = Encoding::japanese;
782                         else
783                                 lex.printError("Unknown package");
784
785                         LYXERR(Debug::INFO, "Reading encoding " << name);
786                         encodinglist[name] = Encoding(name, latexname,
787                                 guiname, iconvname, fixedwidth, package);
788
789                         if (lex.lex() != et_end)
790                                 lex.printError("Missing end");
791                         break;
792                 }
793                 case et_end:
794                         lex.printError("Misplaced end");
795                         break;
796                 case Lexer::LEX_FEOF:
797                         break;
798                 default:
799                         lex.printError("Unknown tag");
800                         break;
801                 }
802         }
803 }
804
805
806 } // namespace lyx