]> git.lyx.org Git - lyx.git/blob - src/Encoding.cpp
Revert "Objective-C compililation support with cmake and C++11"
[lyx.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 && k != docstring::npos) {
541                                 k = cmd.find_first_of(from_ascii("{}"), k + 1);
542                                 if (cmd[k] == '{')
543                                         ++count;
544                                 else
545                                         --count;
546                         }
547                         if (k != docstring::npos)
548                                 j = k;
549                 } else if (m + 1 < cmdend && isAlphaASCII(cmd[m])) {
550                         while (m + 2 < cmdend && isAlphaASCII(cmd[m+1]))
551                                 m++;
552                 }
553                 // Start with this substring and try augmenting it when it is
554                 // the prefix of some command in the unicodesymbols file
555                 docstring subcmd = cmd.substr(i, j - i + 1);
556
557                 CharInfoMap::const_iterator it = unicodesymbols.begin();
558                 // First part of subcmd which might be a combining character
559                 docstring combcmd = (m == j) ? docstring() : cmd.substr(i, m - i + 1);
560                 // The combining character of combcmd if it exists
561                 CharInfoMap::const_iterator combining = uniend;
562                 size_t unicmd_size = 0;
563                 char_type c = 0;
564                 for (; it != uniend; ++it) {
565                         docstring const math = mathmode ? it->second.mathcommand()
566                                                         : docstring();
567                         docstring const text = textmode ? it->second.textcommand()
568                                                         : docstring();
569                         if (!combcmd.empty() && it->second.combining() &&
570                             (math == combcmd || text == combcmd))
571                                 combining = it;
572                         size_t cur_size = max(math.size(), text.size());
573                         // The current math or text unicode command cannot
574                         // match, or we already matched a longer one
575                         if (cur_size < subcmd.size() || cur_size <= unicmd_size)
576                                 continue;
577
578                         docstring tmp = subcmd;
579                         size_t k = j;
580                         while (prefixIs(math, tmp) || prefixIs(text, tmp)) {
581                                 ++k;
582                                 if (k >= cmdend || cur_size <= tmp.size())
583                                         break;
584                                 tmp += cmd[k];
585                         }
586                         // No match
587                         if (k == j)
588                                 continue;
589
590                         // The last added char caused a mismatch, because
591                         // we didn't exhaust the chars in cmd and didn't
592                         // exceed the maximum size of the current unicmd
593                         if (k < cmdend && cur_size > tmp.size())
594                                 tmp.resize(tmp.size() - 1);
595
596                         // If this is an exact match, we found a (longer)
597                         // matching entry in the unicodesymbols file.
598                         if (math != tmp && text != tmp)
599                                 continue;
600                         // If we found a combining command, we need to append
601                         // the macro argument if this has not been done above.
602                         if (tmp == combcmd && combining != uniend &&
603                             k < cmdend && cmd[k] == '{') {
604                                 size_t l = k;
605                                 int count = 1;
606                                 while (l < cmdend && count && l != docstring::npos) {
607                                         l = cmd.find_first_of(from_ascii("{}"), l + 1);
608                                         if (cmd[l] == '{')
609                                                 ++count;
610                                         else
611                                                 --count;
612                                 }
613                                 if (l != docstring::npos) {
614                                         j = l;
615                                         subcmd = cmd.substr(i, j - i + 1);
616                                 }
617                         }
618                         // If the entry doesn't start with '\', we take note
619                         // of the match and continue (this is not a ultimate
620                         // acceptance, as some other entry may match a longer
621                         // portion of the cmd string). However, if the entry
622                         // does start with '\', we accept the match only if
623                         // this is a valid macro, i.e., either it is a single
624                         // (nonletter) char macro, or nothing else follows,
625                         // or what follows is a nonletter char, or the last
626                         // character is a }.
627                         else if (tmp[0] != '\\'
628                                    || (tmp.size() == prefix + 1 &&
629                                        !isAlphaASCII(tmp[1]) &&
630                                        (prefix == 1 || !isAlphaASCII(tmp[2])))
631                                    || k == cmdend 
632                                    || !isAlphaASCII(cmd[k])
633                                    || tmp[tmp.size() - 1] == '}'
634                                  ) {
635                                 c = it->first;
636                                 j = k - 1;
637                                 i = j + 1;
638                                 unicmd_size = cur_size;
639                                 if (math == tmp)
640                                         needsTermination = !it->second.mathnotermination();
641                                 else
642                                         needsTermination = !it->second.textnotermination();
643                                 if (req) {
644                                         if (math == tmp && it->second.mathfeature() &&
645                                             !it->second.mathpreamble().empty())
646                                                 req->insert(it->second.mathpreamble());
647                                         if (text == tmp && it->second.textfeature() &&
648                                             !it->second.textpreamble().empty())
649                                                 req->insert(it->second.textpreamble());
650                                 }
651                         }
652                 }
653                 if (unicmd_size)
654                         symbols += c;
655                 else if (combining != uniend &&
656                          prefixIs(subcmd, combcmd + '{')) {
657                         // We know that subcmd starts with combcmd and
658                         // contains an argument in braces.
659                         docstring const arg = subcmd.substr(
660                                 combcmd.length() + 1,
661                                 subcmd.length() - combcmd.length() - 2);
662                         // If arg is a single character we can construct a
663                         // combining sequence.
664                         char_type a;
665                         bool argcomb = false;
666                         if (arg.size() == 1 && isAlnumASCII(arg[0]))
667                                 a = arg[0];
668                         else {
669                                 // Use the version of fromLaTeXCommand() that
670                                 // parses only one command, since we cannot
671                                 // use more than one character.
672                                 bool dummy = false;
673                                 set<string> r;
674                                 a = fromLaTeXCommand(arg, cmdtype, argcomb,
675                                                      dummy, &r);
676                                 if (a && req && !argcomb)
677                                         req->insert(r.begin(), r.end());
678                         }
679                         if (a && !argcomb) {
680                                 // In unicode the combining character comes
681                                 // after its base
682                                 symbols += a;
683                                 symbols += combining->first;
684                                 i = j + 1;
685                                 unicmd_size = 2;
686                         }
687                 }
688                 if (j + 1 == cmdend && !unicmd_size) {
689                         // No luck. Return what remains
690                         rem = cmd.substr(i);
691                         if (needsTermination && !rem.empty()) {
692                                 if (rem.substr(0, 2) == "{}") {
693                                         rem = rem.substr(2);
694                                         needsTermination = false;
695                                 } else if (rem[0] == ' ') {
696                                         needsTermination = false;
697                                         // LaTeX would swallow all spaces
698                                         rem = ltrim(rem);
699                                 }
700                         }
701                 }
702         }
703         return symbols;
704 }
705
706
707 bool Encodings::isHebrewComposeChar(char_type c)
708 {
709         return c <= 0x05c2 && c >= 0x05b0 && c != 0x05be && c != 0x05c0;
710 }
711
712
713 // Special Arabic letters are ones that do not get connected from left
714 // they are hamza, alef_madda, alef_hamza, waw_hamza, alef_hamza_under,
715 // alef, tah_marbota, dal, thal, rah, zai, wow, alef_maksoura
716
717 bool Encodings::isArabicSpecialChar(char_type c)
718 {
719         return (c >= 0x0621 && c <= 0x0625) || (c >= 0x0630 && c <= 0x0632)
720                 || c == 0x0627 || c == 0x0629 || c == 0x062f || c == 0x0648
721                 || c == 0x0649 || c == 0x0698;
722 }
723
724
725 bool Encodings::isArabicComposeChar(char_type c)
726 {
727         return c >= 0x064b && c <= 0x0652;
728 }
729
730
731 bool Encodings::isArabicChar(char_type c)
732 {
733         return c >= arabic_start && c <= arabic_end
734                 && arabic_table[c-arabic_start][0];
735 }
736
737
738 CharInfo const & Encodings::unicodeCharInfo(char_type c)
739 {
740         static CharInfo empty;
741         CharInfoMap::const_iterator const it = unicodesymbols.find(c);
742         return it != unicodesymbols.end() ? it->second : empty;
743 }
744
745
746 char_type Encodings::transformChar(char_type c, Encodings::LetterForm form)
747 {
748         return isArabicChar(c) ? arabic_table[c-arabic_start][form] : c;
749 }
750
751
752 bool Encodings::isCombiningChar(char_type c)
753 {
754         CharInfoMap::const_iterator const it = unicodesymbols.find(c);
755         if (it != unicodesymbols.end())
756                 return it->second.combining();
757         return false;
758 }
759
760
761 string const Encodings::TIPAShortcut(char_type c)
762 {
763         CharInfoMap::const_iterator const it = unicodesymbols.find(c);
764         if (it != unicodesymbols.end())
765                 return it->second.tipashortcut();
766         return string();
767 }
768
769
770 bool Encodings::isKnownScriptChar(char_type const c, string & preamble)
771 {
772         CharInfoMap::const_iterator const it = unicodesymbols.find(c);
773
774         if (it == unicodesymbols.end())
775                 return false;
776
777         if (it->second.textpreamble() != "textgreek" && it->second.textpreamble() != "textcyr")
778                 return false;
779
780         if (preamble.empty()) {
781                 preamble = it->second.textpreamble();
782                 return true;
783         }
784         return it->second.textpreamble() == preamble;
785 }
786
787
788 bool Encodings::isMathAlpha(char_type c)
789 {
790         return mathalpha.count(c);
791 }
792
793
794 Encoding const *
795 Encodings::fromLyXName(string const & name, bool allowUnsafe) const
796 {
797         EncodingList::const_iterator const it = encodinglist.find(name);
798         if (!allowUnsafe && it->second.unsafe())
799                 return 0;
800         return it != encodinglist.end() ? &it->second : 0;
801 }
802
803
804 Encoding const *
805 Encodings::fromLaTeXName(string const & n, int const & p, bool allowUnsafe) const
806 {
807         string name = n;
808         // FIXME: if we have to test for too many of these synonyms,
809         // we should instead extend the format of lib/encodings
810         if (n == "ansinew")
811                 name = "cp1252";
812
813         // We don't use find_if because it makes copies of the pairs in
814         // the map.
815         // This linear search is OK since we don't have many encodings.
816         // Users could even optimize it by putting the encodings they use
817         // most at the top of lib/encodings.
818         EncodingList::const_iterator const end = encodinglist.end();
819         for (EncodingList::const_iterator it = encodinglist.begin(); it != end; ++it)
820                 if ((it->second.latexName() == name) && (it->second.package() & p)
821                                 && (!it->second.unsafe() || allowUnsafe))
822                         return &it->second;
823         return 0;
824 }
825
826
827 Encoding const *
828 Encodings::fromIconvName(string const & n, int const & p, bool allowUnsafe) const
829 {
830         EncodingList::const_iterator const end = encodinglist.end();
831         for (EncodingList::const_iterator it = encodinglist.begin(); it != end; ++it)
832                 if ((it->second.iconvName() == n) && (it->second.package() & p)
833                                 && (!it->second.unsafe() || allowUnsafe))
834                         return &it->second;
835         return 0;
836 }
837
838
839 Encodings::Encodings()
840 {}
841
842
843 void Encodings::read(FileName const & encfile, FileName const & symbolsfile)
844 {
845         // We must read the symbolsfile first, because the Encoding
846         // constructor depends on it.
847         CharSetMap forcednotselected;
848         Lexer symbolslex;
849         symbolslex.setFile(symbolsfile);
850         bool getNextToken = true;
851         while (symbolslex.isOK()) {
852                 char_type symbol;
853
854                 if (getNextToken) {
855                         if (!symbolslex.next(true))
856                                 break;
857                 } else
858                         getNextToken = true;
859
860                 istringstream is(symbolslex.getString());
861                 // reading symbol directly does not work if
862                 // char_type == wchar_t.
863                 boost::uint32_t tmp;
864                 if(!(is >> hex >> tmp))
865                         break;
866                 symbol = tmp;
867
868                 if (!symbolslex.next(true))
869                         break;
870                 docstring textcommand = symbolslex.getDocString();
871                 if (!symbolslex.next(true))
872                         break;
873                 string textpreamble = symbolslex.getString();
874                 if (!symbolslex.next(true))
875                         break;
876                 string sflags = symbolslex.getString();
877                 
878                 string tipashortcut;
879                 int flags = 0;
880
881                 if (suffixIs(textcommand, '}'))
882                         flags |= CharInfoTextNoTermination;
883                 while (!sflags.empty()) {
884                         string flag;
885                         sflags = split(sflags, flag, ',');
886                         if (flag == "combining") {
887                                 flags |= CharInfoCombining;
888                         } else if (flag == "force") {
889                                 flags |= CharInfoForce;
890                                 forced.insert(symbol);
891                         } else if (prefixIs(flag, "force=")) {
892                                 vector<string> encodings =
893                                         getVectorFromString(flag.substr(6), ";");
894                                 for (size_t i = 0; i < encodings.size(); ++i)
895                                         forcedselected[encodings[i]].insert(symbol);
896                                 flags |= CharInfoForceSelected;
897                         } else if (prefixIs(flag, "force!=")) {
898                                 vector<string> encodings =
899                                         getVectorFromString(flag.substr(7), ";");
900                                 for (size_t i = 0; i < encodings.size(); ++i)
901                                         forcednotselected[encodings[i]].insert(symbol);
902                                 flags |= CharInfoForceSelected;
903                         } else if (flag == "mathalpha") {
904                                 mathalpha.insert(symbol);
905                         } else if (flag == "notermination=text") {
906                                 flags |= CharInfoTextNoTermination;
907                         } else if (flag == "notermination=math") {
908                                 flags |= CharInfoMathNoTermination;
909                         } else if (flag == "notermination=both") {
910                                 flags |= CharInfoTextNoTermination;
911                                 flags |= CharInfoMathNoTermination;
912                         } else if (flag == "notermination=none") {
913                                 flags &= ~CharInfoTextNoTermination;
914                                 flags &= ~CharInfoMathNoTermination;
915                         } else if (contains(flag, "tipashortcut=")) {
916                                 tipashortcut = split(flag, '=');
917                         } else {
918                                 lyxerr << "Ignoring unknown flag `" << flag
919                                        << "' for symbol `0x"
920                                        << hex << symbol << dec
921                                        << "'." << endl;
922                         }
923                 }
924                 // mathcommand and mathpreamble have been added for 1.6.0.
925                 // make them optional so that old files still work.
926                 int const lineno = symbolslex.lineNumber();
927                 bool breakout = false;
928                 docstring mathcommand;
929                 string mathpreamble;
930                 if (symbolslex.next(true)) {
931                         if (symbolslex.lineNumber() != lineno) {
932                                 // line in old format without mathcommand and mathpreamble
933                                 getNextToken = false;
934                         } else {
935                                 mathcommand = symbolslex.getDocString();
936                                 if (suffixIs(mathcommand, '}'))
937                                         flags |= CharInfoMathNoTermination;
938                                 if (symbolslex.next(true)) {
939                                         if (symbolslex.lineNumber() != lineno) {
940                                                 // line in new format with mathcommand only
941                                                 getNextToken = false;
942                                         } else {
943                                                 // line in new format with mathcommand and mathpreamble
944                                                 mathpreamble = symbolslex.getString();
945                                         }
946                                 } else
947                                         breakout = true;
948                         }
949                 } else {
950                         breakout = true;
951                 }
952
953                 // backward compatibility
954                 if (mathpreamble == "esintoramsmath")
955                         mathpreamble = "esint|amsmath";
956
957                 if (!textpreamble.empty())
958                         if (textpreamble[0] != '\\')
959                                 flags |= CharInfoTextFeature;
960                 if (!mathpreamble.empty())
961                         if (mathpreamble[0] != '\\')
962                                 flags |= CharInfoMathFeature;
963
964                 CharInfo info = CharInfo(
965                         textcommand, mathcommand,
966                         textpreamble, mathpreamble,
967                         tipashortcut, flags);
968                 LYXERR(Debug::INFO, "Read unicode symbol " << symbol << " '"
969                            << to_utf8(info.textcommand()) << "' '" << info.textpreamble()
970                            << " '" << info.textfeature() << ' ' << info.textnotermination()
971                            << ' ' << to_utf8(info.mathcommand()) << "' '" << info.mathpreamble()
972                            << "' " << info.mathfeature() << ' ' << info.mathnotermination()
973                            << ' ' << info.combining() << ' ' << info.force()
974                            << ' ' << info.forceselected());
975
976                 // we assume that at least one command is nonempty when using unicodesymbols
977                 if (info.isUnicodeSymbol()) {
978                         unicodesymbols[symbol] = info;
979                 }
980
981                 if (breakout)
982                         break;
983         }
984
985         // Now read the encodings
986         enum {
987                 et_encoding = 1,
988                 et_end
989         };
990
991         LexerKeyword encodingtags[] = {
992                 { "encoding", et_encoding },
993                 { "end", et_end }
994         };
995
996         Lexer lex(encodingtags);
997         lex.setFile(encfile);
998         lex.setContext("Encodings::read");
999         while (lex.isOK()) {
1000                 switch (lex.lex()) {
1001                 case et_encoding:
1002                 {
1003                         lex.next();
1004                         string const name = lex.getString();
1005                         lex.next();
1006                         string const latexname = lex.getString();
1007                         lex.next();
1008                         string const guiname = lex.getString();
1009                         lex.next();
1010                         string const iconvname = lex.getString();
1011                         lex.next();
1012                         string const width = lex.getString();
1013                         bool fixedwidth = false;
1014                         bool unsafe = false;
1015                         if (width == "fixed")
1016                                 fixedwidth = true;
1017                         else if (width == "variable")
1018                                 fixedwidth = false;
1019                         else if (width == "variableunsafe") {
1020                                 fixedwidth = false;
1021                                 unsafe = true;
1022                         }
1023                         else
1024                                 lex.printError("Unknown width");
1025
1026                         lex.next();
1027                         string const p = lex.getString();
1028                         Encoding::Package package = Encoding::none;
1029                         if (p == "none")
1030                                 package = Encoding::none;
1031                         else if (p == "inputenc")
1032                                 package = Encoding::inputenc;
1033                         else if (p == "CJK")
1034                                 package = Encoding::CJK;
1035                         else if (p == "japanese")
1036                                 package = Encoding::japanese;
1037                         else
1038                                 lex.printError("Unknown package");
1039
1040                         LYXERR(Debug::INFO, "Reading encoding " << name);
1041                         encodinglist[name] = Encoding(name, latexname,
1042                                 guiname, iconvname, fixedwidth, unsafe,
1043                                 package);
1044
1045                         if (lex.lex() != et_end)
1046                                 lex.printError("Missing end");
1047                         break;
1048                 }
1049                 case et_end:
1050                         lex.printError("Misplaced end");
1051                         break;
1052                 case Lexer::LEX_FEOF:
1053                         break;
1054                 default:
1055                         lex.printError("Unknown tag");
1056                         break;
1057                 }
1058         }
1059
1060         // Move all information from forcednotselected to forcedselected
1061         for (CharSetMap::const_iterator it1 = forcednotselected.begin(); it1 != forcednotselected.end(); ++it1) {
1062                 for (CharSetMap::iterator it2 = forcedselected.begin(); it2 != forcedselected.end(); ++it2) {
1063                         if (it2->first != it1->first)
1064                                 it2->second.insert(it1->second.begin(), it1->second.end());
1065                 }
1066         }
1067
1068 }
1069
1070
1071 } // namespace lyx