]> git.lyx.org Git - features.git/blob - src/Encoding.cpp
Execute lyx2lyx unit test when running tests
[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 "Lexer.h"
18
19 #include "support/debug.h"
20 #include "support/gettext.h"
21 #include "support/lstrings.h"
22 #include "support/textutils.h"
23 #include "support/unicode.h"
24
25 #include <boost/cstdint.hpp>
26
27 #include <sstream>
28
29 using namespace std;
30 using namespace lyx::support;
31
32 namespace lyx {
33
34 int const Encoding::any = -1;
35
36 Encodings encodings;
37
38 Encodings::MathCommandSet Encodings::mathcmd;
39 Encodings::TextCommandSet Encodings::textcmd;
40 Encodings::MathSymbolSet  Encodings::mathsym;
41
42 namespace {
43
44 char_type arabic_table[172][4] = {
45         {0xfe80, 0xfe80, 0xfe80, 0xfe80}, // 0x0621 = hamza
46         {0xfe81, 0xfe82, 0xfe81, 0xfe82}, // 0x0622 = ligature madda on alef
47         {0xfe83, 0xfe84, 0xfe83, 0xfe84}, // 0x0623 = ligature hamza on alef
48         {0xfe85, 0xfe86, 0xfe85, 0xfe86}, // 0x0624 = ligature hamza on waw
49         {0xfe87, 0xfe88, 0xfe87, 0xfe88}, // 0x0625 = ligature hamza under alef
50         {0xfe89, 0xfe8a, 0xfe8b, 0xfe8c}, // 0x0626 = ligature hamza on ya
51         {0xfe8d, 0xfe8e, 0xfe8d, 0xfe8e}, // 0x0627 = alef
52         {0xfe8f, 0xfe90, 0xfe91, 0xfe92}, // 0x0628 = baa
53         {0xfe93, 0xfe94, 0xfe93, 0xfe94}, // 0x0629 = taa marbuta
54         {0xfe95, 0xfe96, 0xfe97, 0xfe98}, // 0x062a = taa
55         {0xfe99, 0xfe9a, 0xfe9b, 0xfe9c}, // 0x062b = thaa
56         {0xfe9d, 0xfe9e, 0xfe9f, 0xfea0}, // 0x062c = jeem
57         {0xfea1, 0xfea2, 0xfea3, 0xfea4}, // 0x062d = haa
58         {0xfea5, 0xfea6, 0xfea7, 0xfea8}, // 0x062e = khaa
59         {0xfea9, 0xfeaa, 0xfea9, 0xfeaa}, // 0x062f = dal
60
61         {0xfeab, 0xfeac, 0xfeab, 0xfeac}, // 0x0630 = thal
62         {0xfead, 0xfeae, 0xfead, 0xfeae}, // 0x0631 = ra
63         {0xfeaf, 0xfeb0, 0xfeaf, 0xfeb0}, // 0x0632 = zain
64         {0xfeb1, 0xfeb2, 0xfeb3, 0xfeb4}, // 0x0633 = seen
65         {0xfeb5, 0xfeb6, 0xfeb7, 0xfeb8}, // 0x0634 = sheen
66         {0xfeb9, 0xfeba, 0xfebb, 0xfebc}, // 0x0635 = sad
67         {0xfebd, 0xfebe, 0xfebf, 0xfec0}, // 0x0636 = dad
68         {0xfec1, 0xfec2, 0xfec3, 0xfec4}, // 0x0637 = tah
69         {0xfec5, 0xfec6, 0xfec7, 0xfec8}, // 0x0638 = zah
70         {0xfec9, 0xfeca, 0xfecb, 0xfecc}, // 0x0639 = ain
71         {0xfecd, 0xfece, 0xfecf, 0xfed0}, // 0x063a = ghain
72         {0, 0, 0, 0}, // 0x063b
73         {0, 0, 0, 0}, // 0x063c
74         {0, 0, 0, 0}, // 0x063d
75         {0, 0, 0, 0}, // 0x063e
76         {0, 0, 0, 0}, // 0x063f
77
78         {0, 0, 0, 0}, // 0x0640
79         {0xfed1, 0xfed2, 0xfed3, 0xfed4}, // 0x0641 = fa
80         {0xfed5, 0xfed6, 0xfed7, 0xfed8}, // 0x0642 = qaf
81         {0xfed9, 0xfeda, 0xfedb, 0xfedc}, // 0x0643 = kaf
82         {0xfedd, 0xfede, 0xfedf, 0xfee0}, // 0x0644 = lam
83         {0xfee1, 0xfee2, 0xfee3, 0xfee4}, // 0x0645 = meem
84         {0xfee5, 0xfee6, 0xfee7, 0xfee8}, // 0x0646 = noon
85         {0xfee9, 0xfeea, 0xfeeb, 0xfeec}, // 0x0647 = ha
86         {0xfeed, 0xfeee, 0xfeed, 0xfeee}, // 0x0648 = waw
87         {0xfeef, 0xfef0, 0xfeef, 0xfef0}, // 0x0649 = alef maksura
88         {0xfef1, 0xfef2, 0xfef3, 0xfef4}, // 0x064a = ya
89         {0x065b, 0x065b, 0x065b, 0x065b}, // 0x064b = fathatan
90         {0x065c, 0x065c, 0x065c, 0x065c}, // 0x064c = dammatan
91         {0x064d, 0x064d, 0x064d, 0x064d}, // 0x064d = kasratan
92         {0x064e, 0x064e, 0x064e, 0x064e}, // 0x064e = fatha
93         {0x064f, 0x064f, 0x064f, 0x064f}, // 0x064f = damma
94
95         {0x0650, 0x0650, 0x0650, 0x0650}, // 0x0650 = kasra
96         {0x0651, 0x0651, 0x0651, 0x0651}, // 0x0651 = shadda
97         {0x0652, 0x0652, 0x0652, 0x0652}, // 0x0652 = sukun
98
99         {0, 0, 0, 0}, // 0x0653
100         {0, 0, 0, 0}, // 0x0654
101         {0, 0, 0, 0}, // 0x0655
102         {0, 0, 0, 0}, // 0x0656
103         {0, 0, 0, 0}, // 0x0657
104         {0, 0, 0, 0}, // 0x0658
105         {0, 0, 0, 0}, // 0x0659
106         {0, 0, 0, 0}, // 0x065a
107         {0, 0, 0, 0}, // 0x065b
108         {0, 0, 0, 0}, // 0x065c
109         {0, 0, 0, 0}, // 0x065d
110         {0, 0, 0, 0}, // 0x065e
111         {0, 0, 0, 0}, // 0x065f
112         {0, 0, 0, 0}, // 0x0660
113         {0, 0, 0, 0}, // 0x0661
114         {0, 0, 0, 0}, // 0x0662
115         {0, 0, 0, 0}, // 0x0663
116         {0, 0, 0, 0}, // 0x0664
117         {0, 0, 0, 0}, // 0x0665
118         {0, 0, 0, 0}, // 0x0666
119         {0, 0, 0, 0}, // 0x0667
120         {0, 0, 0, 0}, // 0x0668
121         {0, 0, 0, 0}, // 0x0669
122         {0, 0, 0, 0}, // 0x066a
123         {0, 0, 0, 0}, // 0x066b
124         {0, 0, 0, 0}, // 0x066c
125         {0, 0, 0, 0}, // 0x066d
126         {0, 0, 0, 0}, // 0x066e
127         {0, 0, 0, 0}, // 0x066f
128         {0, 0, 0, 0}, // 0x0670
129         {0, 0, 0, 0}, // 0x0671
130         {0, 0, 0, 0}, // 0x0672
131         {0, 0, 0, 0}, // 0x0673
132         {0, 0, 0, 0}, // 0x0674
133         {0, 0, 0, 0}, // 0x0675
134         {0, 0, 0, 0}, // 0x0676
135         {0, 0, 0, 0}, // 0x0677
136         {0, 0, 0, 0}, // 0x0678
137         {0, 0, 0, 0}, // 0x0679
138         {0, 0, 0, 0}, // 0x067a
139         {0, 0, 0, 0}, // 0x067b
140         {0, 0, 0, 0}, // 0x067c
141         {0, 0, 0, 0}, // 0x067d
142         {0xfb56, 0xfb57, 0xfb58, 0xfb59}, // 0x067e = peh
143         {0, 0, 0, 0}, // 0x067f
144         {0, 0, 0, 0}, // 0x0680
145         {0, 0, 0, 0}, // 0x0681
146         {0, 0, 0, 0}, // 0x0682
147         {0, 0, 0, 0}, // 0x0683
148         {0, 0, 0, 0}, // 0x0684
149         {0, 0, 0, 0}, // 0x0685
150         {0xfb7a, 0xfb7b, 0xfb7c, 0xfb7d}, // 0x0686 = tcheh
151         {0, 0, 0, 0}, // 0x0687
152         {0, 0, 0, 0}, // 0x0688
153         {0, 0, 0, 0}, // 0x0689
154         {0, 0, 0, 0}, // 0x068a
155         {0, 0, 0, 0}, // 0x068b
156         {0, 0, 0, 0}, // 0x068c
157         {0, 0, 0, 0}, // 0x068d
158         {0, 0, 0, 0}, // 0x068e
159         {0, 0, 0, 0}, // 0x068f
160         {0, 0, 0, 0}, // 0x0690
161         {0, 0, 0, 0}, // 0x0691
162         {0, 0, 0, 0}, // 0x0692
163         {0, 0, 0, 0}, // 0x0693
164         {0, 0, 0, 0}, // 0x0694
165         {0, 0, 0, 0}, // 0x0695
166         {0, 0, 0, 0}, // 0x0696
167         {0, 0, 0, 0}, // 0x0697
168         {0xfb8a, 0xfb8b, 0xfb8a, 0xfb8b}, // 0x0698 = jeh
169         {0, 0, 0, 0}, // 0x0699
170         {0, 0, 0, 0}, // 0x069a
171         {0, 0, 0, 0}, // 0x069b
172         {0, 0, 0, 0}, // 0x069c
173         {0, 0, 0, 0}, // 0x069d
174         {0, 0, 0, 0}, // 0x069e
175         {0, 0, 0, 0}, // 0x069f
176         {0, 0, 0, 0}, // 0x06a0
177         {0, 0, 0, 0}, // 0x06a1
178         {0, 0, 0, 0}, // 0x06a2
179         {0, 0, 0, 0}, // 0x06a3
180         {0, 0, 0, 0}, // 0x06a4
181         {0, 0, 0, 0}, // 0x06a5
182         {0, 0, 0, 0}, // 0x06a6
183         {0, 0, 0, 0}, // 0x06a7
184         {0, 0, 0, 0}, // 0x06a8
185         {0xfb8e, 0xfb8f, 0xfb90, 0xfb91}, // 0x06a9 = farsi kaf
186         {0, 0, 0, 0}, // 0x06aa
187         {0, 0, 0, 0}, // 0x06ab
188         {0, 0, 0, 0}, // 0x06ac
189         {0, 0, 0, 0}, // 0x06ad
190         {0, 0, 0, 0}, // 0x06ae
191         {0xfb92, 0xfb93, 0xfb94, 0xfb95}, // 0x06af = gaf
192         {0, 0, 0, 0}, // 0x06b0
193         {0, 0, 0, 0}, // 0x06b1
194         {0, 0, 0, 0}, // 0x06b2
195         {0, 0, 0, 0}, // 0x06b3
196         {0, 0, 0, 0}, // 0x06b4
197         {0, 0, 0, 0}, // 0x06b5
198         {0, 0, 0, 0}, // 0x06b6
199         {0, 0, 0, 0}, // 0x06b7
200         {0, 0, 0, 0}, // 0x06b8
201         {0, 0, 0, 0}, // 0x06b9
202         {0, 0, 0, 0}, // 0x06ba
203         {0, 0, 0, 0}, // 0x06bb
204         {0, 0, 0, 0}, // 0x06bc
205         {0, 0, 0, 0}, // 0x06bd
206         {0, 0, 0, 0}, // 0x06be
207         {0, 0, 0, 0}, // 0x06bf
208         {0, 0, 0, 0}, // 0x06c0
209         {0, 0, 0, 0}, // 0x06c1
210         {0, 0, 0, 0}, // 0x06c2
211         {0, 0, 0, 0}, // 0x06c3
212         {0, 0, 0, 0}, // 0x06c4
213         {0, 0, 0, 0}, // 0x06c5
214         {0, 0, 0, 0}, // 0x06c6
215         {0, 0, 0, 0}, // 0x06c7
216         {0, 0, 0, 0}, // 0x06c8
217         {0, 0, 0, 0}, // 0x06c9
218         {0, 0, 0, 0}, // 0x06ca
219         {0, 0, 0, 0}, // 0x06cb
220         {0xfbfc, 0xfbfd, 0xfbfe, 0xfbff} // 0x06cc = farsi yeh
221 };
222
223
224 char_type const arabic_start = 0x0621;
225 char_type const arabic_end = 0x06cc;
226
227
228 typedef map<char_type, CharInfo> CharInfoMap;
229 CharInfoMap unicodesymbols;
230
231 typedef set<char_type> CharSet;
232 typedef map<string, CharSet> CharSetMap;
233 CharSet forced;
234 CharSetMap forcedselected;
235
236 typedef set<char_type> MathAlphaSet;
237 MathAlphaSet mathalpha;
238
239
240 /// The highest code point in UCS4 encoding (1<<20 + 1<<16)
241 char_type const max_ucs4 = 0x110000;
242
243 } // namespace anon
244
245
246 EncodingException::EncodingException(char_type c)
247         : failed_char(c), par_id(0), pos(0)
248 {
249 }
250
251
252 const char * EncodingException::what() const throw()
253 {
254         return "Could not find LaTeX command for a character";
255 }
256
257
258 CharInfo::CharInfo(
259         docstring const textcommand, docstring const mathcommand,
260         std::string const textpreamble, std::string const mathpreamble,
261         std::string const tipashortcut, unsigned int flags)
262         : textcommand_(textcommand), mathcommand_(mathcommand),
263           textpreamble_(textpreamble), mathpreamble_(mathpreamble),
264           tipashortcut_(tipashortcut), flags_(flags)
265 {
266 }
267
268 Encoding::Encoding(string const & n, string const & l, string const & g,
269                    string const & i, bool f, bool u, Encoding::Package p)
270         : name_(n), latexName_(l), guiName_(g), iconvName_(i), fixedwidth_(f),
271           unsafe_(u), forced_(&forcedselected[n]), package_(p)
272 {
273         if (n == "ascii") {
274                 // ASCII can encode 128 code points and nothing else
275                 start_encodable_ = 128;
276                 complete_ = true;
277         } else if (i == "UTF-8") {
278                 // UTF8 can encode all UCS4 code points
279                 start_encodable_ = max_ucs4;
280                 complete_ = true;
281         } else {
282                 start_encodable_ = 0;
283                 complete_ = false;
284         }
285 }
286
287
288 void Encoding::init() const
289 {
290         if (complete_)
291                 return;
292
293         start_encodable_ = 0;
294         // temporarily switch off lyxerr, since we will generate iconv errors
295         lyxerr.disable();
296         if (fixedwidth_) {
297                 // We do not need to check all UCS4 code points, it is enough
298                 // if we check all 256 code points of this encoding.
299                 for (unsigned short j = 0; j < 256; ++j) {
300                         char const c = char(j);
301                         vector<char_type> const ucs4 = eightbit_to_ucs4(&c, 1, iconvName_);
302                         if (ucs4.size() != 1)
303                                 continue;
304                         char_type const uc = ucs4[0];
305                         CharInfoMap::const_iterator const it = unicodesymbols.find(uc);
306                         if (it == unicodesymbols.end())
307                                 encodable_.insert(uc);
308                         else if (!it->second.force()) {
309                                 if (forced_->empty() || forced_->find(uc) == forced_->end())
310                                         encodable_.insert(uc);
311                         }
312                 }
313         } else {
314                 // We do not know how many code points this encoding has, and
315                 // they do not have a direct representation as a single byte,
316                 // therefore we need to check all UCS4 code points.
317                 // This is expensive!
318                 for (char_type c = 0; c < max_ucs4; ++c) {
319                         vector<char> const eightbit = ucs4_to_eightbit(&c, 1, iconvName_);
320                         if (!eightbit.empty()) {
321                                 CharInfoMap::const_iterator const it = unicodesymbols.find(c);
322                                 if (it == unicodesymbols.end())
323                                         encodable_.insert(c);
324                                 else if (!it->second.force()) {
325                                         if (forced_->empty() || forced_->find(c) == forced_->end())
326                                                 encodable_.insert(c);
327                                 }
328                         }
329                 }
330         }
331         lyxerr.enable();
332         CharSet::iterator it = encodable_.find(start_encodable_);
333         while (it != encodable_.end()) {
334                 encodable_.erase(it);
335                 ++start_encodable_;
336                 it = encodable_.find(start_encodable_);
337         }
338         complete_ = true;
339 }
340
341
342 bool Encoding::isForced(char_type c) const
343 {
344         if (!forced.empty() && forced.find(c) != forced.end())
345                 return true;
346         return !forced_->empty() && forced_->find(c) != forced_->end();
347 }
348
349
350 bool Encoding::encodable(char_type c) const
351 {
352         // assure the used encoding is properly initialized
353         init();
354
355         if (iconvName_ == "UTF-8" && package_ == none)
356                 return true;
357         if (c < start_encodable_ && !isForced(c))
358                 return true;
359         if (encodable_.find(c) != encodable_.end())
360                 return true;
361         return false;
362 }
363
364
365 pair<docstring, bool> Encoding::latexChar(char_type c) const
366 {
367         if (encodable(c))
368                 return make_pair(docstring(1, c), false);
369
370         // c cannot (or should not) be encoded in this encoding
371         CharInfoMap::const_iterator const it = unicodesymbols.find(c);
372         if (it == unicodesymbols.end())
373                 throw EncodingException(c);
374         // at least one of mathcommand and textcommand is nonempty
375         if (it->second.textcommand().empty())
376                 return make_pair(
377                         "\\ensuremath{" + it->second.mathcommand() + '}', false);
378         return make_pair(it->second.textcommand(), !it->second.textnotermination());
379 }
380
381
382 pair<docstring, docstring> Encoding::latexString(docstring const input, bool dryrun) const
383 {
384         docstring result;
385         docstring uncodable;
386         bool terminate = false;
387         for (size_t n = 0; n < input.size(); ++n) {
388                 try {
389                         char_type const c = input[n];
390                         pair<docstring, bool> latex_char = latexChar(c);
391                         docstring const latex = latex_char.first;
392                         if (terminate && !prefixIs(latex, '\\')
393                             && !prefixIs(latex, '{')
394                             && !prefixIs(latex, '}')) {
395                                         // Prevent eating of a following
396                                         // space or command corruption by
397                                         // following characters
398                                         if (latex == " ")
399                                                 result += "{}";
400                                         else
401                                                 result += " ";
402                                 }
403                         result += latex;
404                         terminate = latex_char.second;
405                 } catch (EncodingException & /* e */) {
406                         LYXERR0("Uncodable character in latexString!");
407                         if (dryrun) {
408                                 result += "<" + _("LyX Warning: ")
409                                            + _("uncodable character") + " '";
410                                 result += docstring(1, input[n]);
411                                 result += "'>";
412                         } else
413                                 uncodable += input[n];
414                 }
415         }
416         return make_pair(result, uncodable);
417 }
418
419
420 vector<char_type> Encoding::symbolsList() const
421 {
422         // assure the used encoding is properly initialized
423         init();
424
425         // first all encodable characters
426         vector<char_type> symbols(encodable_.begin(), encodable_.end());
427         // add those below start_encodable_
428         for (char_type c = 0; c < start_encodable_; ++c)
429                 symbols.push_back(c);
430         // now the ones from the unicodesymbols file
431         CharInfoMap::const_iterator const end = unicodesymbols.end();
432         CharInfoMap::const_iterator it = unicodesymbols.begin();
433         for (; it != end; ++it)
434                 symbols.push_back(it->first);
435         return symbols;
436 }
437
438
439 bool Encodings::latexMathChar(char_type c, bool mathmode,
440                         Encoding const * encoding, docstring & command,
441                         bool & needsTermination)
442 {
443         command = empty_docstring();
444         if (encoding)
445                 if (encoding->encodable(c))
446                         command = docstring(1, c);
447         needsTermination = false;
448
449         CharInfoMap::const_iterator const it = unicodesymbols.find(c);
450         if (it == unicodesymbols.end()) {
451                 if (!encoding || command.empty())
452                         throw EncodingException(c);
453                 if (mathmode)
454                         addMathSym(c);
455                 return false;
456         }
457         // at least one of mathcommand and textcommand is nonempty
458         bool use_math = (mathmode && !it->second.mathcommand().empty()) ||
459                         (!mathmode && it->second.textcommand().empty());
460         if (use_math) {
461                 command = it->second.mathcommand();
462                 needsTermination = !it->second.mathnotermination();
463                 addMathCmd(c);
464         } else {
465                 if (!encoding || command.empty()) {
466                         command = it->second.textcommand();
467                         needsTermination = !it->second.textnotermination();
468                         addTextCmd(c);
469                 }
470                 if (mathmode)
471                         addMathSym(c);
472         }
473         return use_math;
474 }
475
476
477 char_type Encodings::fromLaTeXCommand(docstring const & cmd, int cmdtype,
478                 bool & combining, bool & needsTermination, set<string> * req)
479 {
480         CharInfoMap::const_iterator const end = unicodesymbols.end();
481         CharInfoMap::const_iterator it = unicodesymbols.begin();
482         for (combining = false; it != end; ++it) {
483                 docstring const math = it->second.mathcommand();
484                 docstring const text = it->second.textcommand();
485                 if ((cmdtype & MATH_CMD) && math == cmd) {
486                         combining = it->second.combining();
487                         needsTermination = !it->second.mathnotermination();
488                         if (req && it->second.mathfeature() &&
489                             !it->second.mathpreamble().empty())
490                                 req->insert(it->second.mathpreamble());
491                         return it->first;
492                 }
493                 if ((cmdtype & TEXT_CMD) && text == cmd) {
494                         combining = it->second.combining();
495                         needsTermination = !it->second.textnotermination();
496                         if (req && it->second.textfeature() &&
497                             !it->second.textpreamble().empty())
498                                 req->insert(it->second.textpreamble());
499                         return it->first;
500                 }
501         }
502         needsTermination = false;
503         return 0;
504 }
505
506
507 docstring Encodings::fromLaTeXCommand(docstring const & cmd, int cmdtype,
508                 bool & needsTermination, docstring & rem, set<string> * req)
509 {
510         needsTermination = false;
511         rem = empty_docstring();
512         bool const mathmode = cmdtype & MATH_CMD;
513         bool const textmode = cmdtype & TEXT_CMD;
514         docstring symbols;
515         size_t const cmdend = cmd.size();
516         size_t prefix = 0;
517         CharInfoMap::const_iterator const uniend = unicodesymbols.end();
518         for (size_t i = 0, j = 0; j < cmdend; ++j) {
519                 // Also get the char after a backslash
520                 if (j + 1 < cmdend && cmd[j] == '\\') {
521                         ++j;
522                         prefix = 1;
523                         // Detect things like \=*{e} as well
524                         if (j + 3 < cmdend && cmd[j+1] == '*' &&
525                             cmd[j+2] == '{') {
526                                 ++j;
527                                 prefix = 2;
528                         }
529                 }
530                 // position of the last character before a possible macro
531                 // argument
532                 size_t m = j;
533                 // If a macro argument follows, get it, too
534                 // Do it here only for single character commands. Other
535                 // combining commands need this too, but they are handled in
536                 // the loop below for performance reasons.
537                 if (j + 1 < cmdend && cmd[j + 1] == '{') {
538                         size_t k = j + 1;
539                         int count = 1;
540                         while (k < cmdend && count) {
541                                 k = cmd.find_first_of(from_ascii("{}"), k + 1);
542                                 // braces may not be balanced
543                                 if (k == docstring::npos)
544                                         break;
545                                 if (cmd[k] == '{')
546                                         ++count;
547                                 else
548                                         --count;
549                         }
550                         if (k != docstring::npos)
551                                 j = k;
552                 } else if (m + 1 < cmdend && isAlphaASCII(cmd[m])) {
553                         while (m + 2 < cmdend && isAlphaASCII(cmd[m+1]))
554                                 m++;
555                 }
556                 // Start with this substring and try augmenting it when it is
557                 // the prefix of some command in the unicodesymbols file
558                 docstring subcmd = cmd.substr(i, j - i + 1);
559
560                 CharInfoMap::const_iterator it = unicodesymbols.begin();
561                 // First part of subcmd which might be a combining character
562                 docstring combcmd = (m == j) ? docstring() : cmd.substr(i, m - i + 1);
563                 // The combining character of combcmd if it exists
564                 CharInfoMap::const_iterator combining = uniend;
565                 size_t unicmd_size = 0;
566                 char_type c = 0;
567                 for (; it != uniend; ++it) {
568                         docstring const math = mathmode ? it->second.mathcommand()
569                                                         : docstring();
570                         docstring const text = textmode ? it->second.textcommand()
571                                                         : docstring();
572                         if (!combcmd.empty() && it->second.combining() &&
573                             (math == combcmd || text == combcmd))
574                                 combining = it;
575                         size_t cur_size = max(math.size(), text.size());
576                         // The current math or text unicode command cannot
577                         // match, or we already matched a longer one
578                         if (cur_size < subcmd.size() || cur_size <= unicmd_size)
579                                 continue;
580
581                         docstring tmp = subcmd;
582                         size_t k = j;
583                         while (prefixIs(math, tmp) || prefixIs(text, tmp)) {
584                                 ++k;
585                                 if (k >= cmdend || cur_size <= tmp.size())
586                                         break;
587                                 tmp += cmd[k];
588                         }
589                         // No match
590                         if (k == j)
591                                 continue;
592
593                         // The last added char caused a mismatch, because
594                         // we didn't exhaust the chars in cmd and didn't
595                         // exceed the maximum size of the current unicmd
596                         if (k < cmdend && cur_size > tmp.size())
597                                 tmp.resize(tmp.size() - 1);
598
599                         // If this is an exact match, we found a (longer)
600                         // matching entry in the unicodesymbols file.
601                         if (math != tmp && text != tmp)
602                                 continue;
603                         // If we found a combining command, we need to append
604                         // the macro argument if this has not been done above.
605                         if (tmp == combcmd && combining != uniend &&
606                             k < cmdend && cmd[k] == '{') {
607                                 size_t l = k;
608                                 int count = 1;
609                                 while (l < cmdend && count) {
610                                         l = cmd.find_first_of(from_ascii("{}"), l + 1);
611                                         // braces may not be balanced
612                                         if (l == docstring::npos)
613                                                 break;
614                                         if (cmd[l] == '{')
615                                                 ++count;
616                                         else
617                                                 --count;
618                                 }
619                                 if (l != docstring::npos) {
620                                         j = l;
621                                         subcmd = cmd.substr(i, j - i + 1);
622                                 }
623                         }
624                         // If the entry doesn't start with '\', we take note
625                         // of the match and continue (this is not a ultimate
626                         // acceptance, as some other entry may match a longer
627                         // portion of the cmd string). However, if the entry
628                         // does start with '\', we accept the match only if
629                         // this is a valid macro, i.e., either it is a single
630                         // (nonletter) char macro, or nothing else follows,
631                         // or what follows is a nonletter char, or the last
632                         // character is a }.
633                         else if (tmp[0] != '\\'
634                                    || (tmp.size() == prefix + 1 &&
635                                        !isAlphaASCII(tmp[1]) &&
636                                        (prefix == 1 || !isAlphaASCII(tmp[2])))
637                                    || k == cmdend 
638                                    || !isAlphaASCII(cmd[k])
639                                    || tmp[tmp.size() - 1] == '}'
640                                  ) {
641                                 c = it->first;
642                                 j = k - 1;
643                                 i = j + 1;
644                                 unicmd_size = cur_size;
645                                 if (math == tmp)
646                                         needsTermination = !it->second.mathnotermination();
647                                 else
648                                         needsTermination = !it->second.textnotermination();
649                                 if (req) {
650                                         if (math == tmp && it->second.mathfeature() &&
651                                             !it->second.mathpreamble().empty())
652                                                 req->insert(it->second.mathpreamble());
653                                         if (text == tmp && it->second.textfeature() &&
654                                             !it->second.textpreamble().empty())
655                                                 req->insert(it->second.textpreamble());
656                                 }
657                         }
658                 }
659                 if (unicmd_size)
660                         symbols += c;
661                 else if (combining != uniend &&
662                          prefixIs(subcmd, combcmd + '{')) {
663                         // We know that subcmd starts with combcmd and
664                         // contains an argument in braces.
665                         docstring const arg = subcmd.substr(
666                                 combcmd.length() + 1,
667                                 subcmd.length() - combcmd.length() - 2);
668                         // If arg is a single character we can construct a
669                         // combining sequence.
670                         char_type a;
671                         bool argcomb = false;
672                         if (arg.size() == 1 && isAlnumASCII(arg[0]))
673                                 a = arg[0];
674                         else {
675                                 // Use the version of fromLaTeXCommand() that
676                                 // parses only one command, since we cannot
677                                 // use more than one character.
678                                 bool dummy = false;
679                                 set<string> r;
680                                 a = fromLaTeXCommand(arg, cmdtype, argcomb,
681                                                      dummy, &r);
682                                 if (a && req && !argcomb)
683                                         req->insert(r.begin(), r.end());
684                         }
685                         if (a && !argcomb) {
686                                 // In unicode the combining character comes
687                                 // after its base
688                                 symbols += a;
689                                 symbols += combining->first;
690                                 i = j + 1;
691                                 unicmd_size = 2;
692                         }
693                 }
694                 if (j + 1 == cmdend && !unicmd_size) {
695                         // No luck. Return what remains
696                         rem = cmd.substr(i);
697                         if (needsTermination && !rem.empty()) {
698                                 if (rem.substr(0, 2) == "{}") {
699                                         rem = rem.substr(2);
700                                         needsTermination = false;
701                                 } else if (rem[0] == ' ') {
702                                         needsTermination = false;
703                                         // LaTeX would swallow all spaces
704                                         rem = ltrim(rem);
705                                 }
706                         }
707                 }
708         }
709         return symbols;
710 }
711
712
713 bool Encodings::isHebrewComposeChar(char_type c)
714 {
715         return c <= 0x05c2 && c >= 0x05b0 && c != 0x05be && c != 0x05c0;
716 }
717
718
719 // Special Arabic letters are ones that do not get connected from left
720 // they are hamza, alef_madda, alef_hamza, waw_hamza, alef_hamza_under,
721 // alef, tah_marbota, dal, thal, rah, zai, wow, alef_maksoura
722
723 bool Encodings::isArabicSpecialChar(char_type c)
724 {
725         return (c >= 0x0621 && c <= 0x0625) || (c >= 0x0630 && c <= 0x0632)
726                 || c == 0x0627 || c == 0x0629 || c == 0x062f || c == 0x0648
727                 || c == 0x0649 || c == 0x0698;
728 }
729
730
731 bool Encodings::isArabicComposeChar(char_type c)
732 {
733         return c >= 0x064b && c <= 0x0652;
734 }
735
736
737 bool Encodings::isArabicChar(char_type c)
738 {
739         return c >= arabic_start && c <= arabic_end
740                 && arabic_table[c-arabic_start][0];
741 }
742
743
744 CharInfo const & Encodings::unicodeCharInfo(char_type c)
745 {
746         static CharInfo empty;
747         CharInfoMap::const_iterator const it = unicodesymbols.find(c);
748         return it != unicodesymbols.end() ? it->second : empty;
749 }
750
751
752 char_type Encodings::transformChar(char_type c, Encodings::LetterForm form)
753 {
754         return isArabicChar(c) ? arabic_table[c-arabic_start][form] : c;
755 }
756
757
758 bool Encodings::isCombiningChar(char_type c)
759 {
760         CharInfoMap::const_iterator const it = unicodesymbols.find(c);
761         if (it != unicodesymbols.end())
762                 return it->second.combining();
763         return false;
764 }
765
766
767 string const Encodings::TIPAShortcut(char_type c)
768 {
769         CharInfoMap::const_iterator const it = unicodesymbols.find(c);
770         if (it != unicodesymbols.end())
771                 return it->second.tipashortcut();
772         return string();
773 }
774
775
776 bool Encodings::isKnownScriptChar(char_type const c, string & preamble)
777 {
778         CharInfoMap::const_iterator const it = unicodesymbols.find(c);
779
780         if (it == unicodesymbols.end())
781                 return false;
782
783         if (it->second.textpreamble() != "textgreek" && it->second.textpreamble() != "textcyr")
784                 return false;
785
786         if (preamble.empty()) {
787                 preamble = it->second.textpreamble();
788                 return true;
789         }
790         return it->second.textpreamble() == preamble;
791 }
792
793
794 bool Encodings::isMathAlpha(char_type c)
795 {
796         return mathalpha.count(c);
797 }
798
799
800 Encoding const *
801 Encodings::fromLyXName(string const & name, bool allowUnsafe) const
802 {
803         EncodingList::const_iterator const it = encodinglist.find(name);
804         if (!allowUnsafe && it->second.unsafe())
805                 return 0;
806         return it != encodinglist.end() ? &it->second : 0;
807 }
808
809
810 Encoding const *
811 Encodings::fromLaTeXName(string const & n, int const & p, bool allowUnsafe) const
812 {
813         string name = n;
814         // FIXME: if we have to test for too many of these synonyms,
815         // we should instead extend the format of lib/encodings
816         if (n == "ansinew")
817                 name = "cp1252";
818
819         // We don't use find_if because it makes copies of the pairs in
820         // the map.
821         // This linear search is OK since we don't have many encodings.
822         // Users could even optimize it by putting the encodings they use
823         // most at the top of lib/encodings.
824         EncodingList::const_iterator const end = encodinglist.end();
825         for (EncodingList::const_iterator it = encodinglist.begin(); it != end; ++it)
826                 if ((it->second.latexName() == name) && (it->second.package() & p)
827                                 && (!it->second.unsafe() || allowUnsafe))
828                         return &it->second;
829         return 0;
830 }
831
832
833 Encoding const *
834 Encodings::fromIconvName(string const & n, int const & p, bool allowUnsafe) const
835 {
836         EncodingList::const_iterator const end = encodinglist.end();
837         for (EncodingList::const_iterator it = encodinglist.begin(); it != end; ++it)
838                 if ((it->second.iconvName() == n) && (it->second.package() & p)
839                                 && (!it->second.unsafe() || allowUnsafe))
840                         return &it->second;
841         return 0;
842 }
843
844
845 Encodings::Encodings()
846 {}
847
848
849 void Encodings::read(FileName const & encfile, FileName const & symbolsfile)
850 {
851         // We must read the symbolsfile first, because the Encoding
852         // constructor depends on it.
853         CharSetMap forcednotselected;
854         Lexer symbolslex;
855         symbolslex.setFile(symbolsfile);
856         bool getNextToken = true;
857         while (symbolslex.isOK()) {
858                 char_type symbol;
859
860                 if (getNextToken) {
861                         if (!symbolslex.next(true))
862                                 break;
863                 } else
864                         getNextToken = true;
865
866                 istringstream is(symbolslex.getString());
867                 // reading symbol directly does not work if
868                 // char_type == wchar_t.
869                 boost::uint32_t tmp;
870                 if(!(is >> hex >> tmp))
871                         break;
872                 symbol = tmp;
873
874                 if (!symbolslex.next(true))
875                         break;
876                 docstring textcommand = symbolslex.getDocString();
877                 if (!symbolslex.next(true))
878                         break;
879                 string textpreamble = symbolslex.getString();
880                 if (!symbolslex.next(true))
881                         break;
882                 string sflags = symbolslex.getString();
883                 
884                 string tipashortcut;
885                 int flags = 0;
886
887                 if (suffixIs(textcommand, '}'))
888                         flags |= CharInfoTextNoTermination;
889                 while (!sflags.empty()) {
890                         string flag;
891                         sflags = split(sflags, flag, ',');
892                         if (flag == "combining") {
893                                 flags |= CharInfoCombining;
894                         } else if (flag == "force") {
895                                 flags |= CharInfoForce;
896                                 forced.insert(symbol);
897                         } else if (prefixIs(flag, "force=")) {
898                                 vector<string> encodings =
899                                         getVectorFromString(flag.substr(6), ";");
900                                 for (size_t i = 0; i < encodings.size(); ++i)
901                                         forcedselected[encodings[i]].insert(symbol);
902                                 flags |= CharInfoForceSelected;
903                         } else if (prefixIs(flag, "force!=")) {
904                                 vector<string> encodings =
905                                         getVectorFromString(flag.substr(7), ";");
906                                 for (size_t i = 0; i < encodings.size(); ++i)
907                                         forcednotselected[encodings[i]].insert(symbol);
908                                 flags |= CharInfoForceSelected;
909                         } else if (flag == "mathalpha") {
910                                 mathalpha.insert(symbol);
911                         } else if (flag == "notermination=text") {
912                                 flags |= CharInfoTextNoTermination;
913                         } else if (flag == "notermination=math") {
914                                 flags |= CharInfoMathNoTermination;
915                         } else if (flag == "notermination=both") {
916                                 flags |= CharInfoTextNoTermination;
917                                 flags |= CharInfoMathNoTermination;
918                         } else if (flag == "notermination=none") {
919                                 flags &= ~CharInfoTextNoTermination;
920                                 flags &= ~CharInfoMathNoTermination;
921                         } else if (contains(flag, "tipashortcut=")) {
922                                 tipashortcut = split(flag, '=');
923                         } else {
924                                 lyxerr << "Ignoring unknown flag `" << flag
925                                        << "' for symbol `0x"
926                                        << hex << symbol << dec
927                                        << "'." << endl;
928                         }
929                 }
930                 // mathcommand and mathpreamble have been added for 1.6.0.
931                 // make them optional so that old files still work.
932                 int const lineno = symbolslex.lineNumber();
933                 bool breakout = false;
934                 docstring mathcommand;
935                 string mathpreamble;
936                 if (symbolslex.next(true)) {
937                         if (symbolslex.lineNumber() != lineno) {
938                                 // line in old format without mathcommand and mathpreamble
939                                 getNextToken = false;
940                         } else {
941                                 mathcommand = symbolslex.getDocString();
942                                 if (suffixIs(mathcommand, '}'))
943                                         flags |= CharInfoMathNoTermination;
944                                 if (symbolslex.next(true)) {
945                                         if (symbolslex.lineNumber() != lineno) {
946                                                 // line in new format with mathcommand only
947                                                 getNextToken = false;
948                                         } else {
949                                                 // line in new format with mathcommand and mathpreamble
950                                                 mathpreamble = symbolslex.getString();
951                                         }
952                                 } else
953                                         breakout = true;
954                         }
955                 } else {
956                         breakout = true;
957                 }
958
959                 // backward compatibility
960                 if (mathpreamble == "esintoramsmath")
961                         mathpreamble = "esint|amsmath";
962
963                 if (!textpreamble.empty())
964                         if (textpreamble[0] != '\\')
965                                 flags |= CharInfoTextFeature;
966                 if (!mathpreamble.empty())
967                         if (mathpreamble[0] != '\\')
968                                 flags |= CharInfoMathFeature;
969
970                 CharInfo info = CharInfo(
971                         textcommand, mathcommand,
972                         textpreamble, mathpreamble,
973                         tipashortcut, flags);
974                 LYXERR(Debug::INFO, "Read unicode symbol " << symbol << " '"
975                            << to_utf8(info.textcommand()) << "' '" << info.textpreamble()
976                            << " '" << info.textfeature() << ' ' << info.textnotermination()
977                            << ' ' << to_utf8(info.mathcommand()) << "' '" << info.mathpreamble()
978                            << "' " << info.mathfeature() << ' ' << info.mathnotermination()
979                            << ' ' << info.combining() << ' ' << info.force()
980                            << ' ' << info.forceselected());
981
982                 // we assume that at least one command is nonempty when using unicodesymbols
983                 if (info.isUnicodeSymbol()) {
984                         unicodesymbols[symbol] = info;
985                 }
986
987                 if (breakout)
988                         break;
989         }
990
991         // Now read the encodings
992         enum {
993                 et_encoding = 1,
994                 et_end
995         };
996
997         LexerKeyword encodingtags[] = {
998                 { "encoding", et_encoding },
999                 { "end", et_end }
1000         };
1001
1002         Lexer lex(encodingtags);
1003         lex.setFile(encfile);
1004         lex.setContext("Encodings::read");
1005         while (lex.isOK()) {
1006                 switch (lex.lex()) {
1007                 case et_encoding:
1008                 {
1009                         lex.next();
1010                         string const name = lex.getString();
1011                         lex.next();
1012                         string const latexname = lex.getString();
1013                         lex.next();
1014                         string const guiname = lex.getString();
1015                         lex.next();
1016                         string const iconvname = lex.getString();
1017                         lex.next();
1018                         string const width = lex.getString();
1019                         bool fixedwidth = false;
1020                         bool unsafe = false;
1021                         if (width == "fixed")
1022                                 fixedwidth = true;
1023                         else if (width == "variable")
1024                                 fixedwidth = false;
1025                         else if (width == "variableunsafe") {
1026                                 fixedwidth = false;
1027                                 unsafe = true;
1028                         }
1029                         else
1030                                 lex.printError("Unknown width");
1031
1032                         lex.next();
1033                         string const p = lex.getString();
1034                         Encoding::Package package = Encoding::none;
1035                         if (p == "none")
1036                                 package = Encoding::none;
1037                         else if (p == "inputenc")
1038                                 package = Encoding::inputenc;
1039                         else if (p == "CJK")
1040                                 package = Encoding::CJK;
1041                         else if (p == "japanese")
1042                                 package = Encoding::japanese;
1043                         else
1044                                 lex.printError("Unknown package");
1045
1046                         LYXERR(Debug::INFO, "Reading encoding " << name);
1047                         encodinglist[name] = Encoding(name, latexname,
1048                                 guiname, iconvname, fixedwidth, unsafe,
1049                                 package);
1050
1051                         if (lex.lex() != et_end)
1052                                 lex.printError("Missing end");
1053                         break;
1054                 }
1055                 case et_end:
1056                         lex.printError("Misplaced end");
1057                         break;
1058                 case Lexer::LEX_FEOF:
1059                         break;
1060                 default:
1061                         lex.printError("Unknown tag");
1062                         break;
1063                 }
1064         }
1065
1066         // Move all information from forcednotselected to forcedselected
1067         for (CharSetMap::const_iterator it1 = forcednotselected.begin(); it1 != forcednotselected.end(); ++it1) {
1068                 for (CharSetMap::iterator it2 = forcedselected.begin(); it2 != forcedselected.end(); ++it2) {
1069                         if (it2->first != it1->first)
1070                                 it2->second.insert(it1->second.begin(), it1->second.end());
1071                 }
1072         }
1073
1074 }
1075
1076
1077 } // namespace lyx