]> git.lyx.org Git - lyx.git/blob - src/Encoding.cpp
59536062686ddafe77a5b169ba6f43c1870922b8
[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 "debug.h"
18 #include "LaTeXFeatures.h"
19 #include "Lexer.h"
20 #include "LyXRC.h"
21
22 #include "support/FileName.h"
23 #include "support/lstrings.h"
24 #include "support/unicode.h"
25
26 #include <sstream>
27
28
29 namespace lyx {
30
31 using support::FileName;
32
33 #ifndef CXX_GLOBAL_CSTD
34 using std::strtol;
35 #endif
36
37 using std::endl;
38 using std::string;
39
40
41 Encodings encodings;
42
43 namespace {
44
45 char_type arabic_table[50][4] = {
46         {0xfe80, 0xfe80, 0xfe80, 0xfe80}, // 0x0621 = hamza
47         {0xfe81, 0xfe82, 0xfe81, 0xfe82}, // 0x0622 = ligature madda on alef
48         {0xfe83, 0xfe84, 0xfe83, 0xfe84}, // 0x0623 = ligature hamza on alef
49         {0xfe85, 0xfe86, 0xfe85, 0xfe86}, // 0x0624 = ligature hamza on waw
50         {0xfe87, 0xfe88, 0xfe87, 0xfe88}, // 0x0625 = ligature hamza under alef
51         {0xfe89, 0xfe8a, 0xfe8b, 0xfe8c}, // 0x0626 = ligature hamza on ya
52         {0xfe8d, 0xfe8e, 0xfe8d, 0xfe8e}, // 0x0627 = alef
53         {0xfe8f, 0xfe90, 0xfe91, 0xfe92}, // 0x0628 = baa
54         {0xfe93, 0xfe94, 0xfe93, 0xfe94}, // 0x0629 = taa marbuta
55         {0xfe95, 0xfe96, 0xfe97, 0xfe98}, // 0x062a = taa
56         {0xfe99, 0xfe9a, 0xfe9b, 0xfe9c}, // 0x062b = thaa
57         {0xfe9d, 0xfe9e, 0xfe9f, 0xfea0}, // 0x062c = jeem
58         {0xfea1, 0xfea2, 0xfea3, 0xfea4}, // 0x062d = haa
59         {0xfea5, 0xfea6, 0xfea7, 0xfea8}, // 0x062e = khaa
60         {0xfea9, 0xfeaa, 0xfea9, 0xfeaa}, // 0x062f = dal
61
62         {0xfeab, 0xfeac, 0xfeab, 0xfeac}, // 0x0630 = thal
63         {0xfead, 0xfeae, 0xfead, 0xfeae}, // 0x0631 = ra
64         {0xfeaf, 0xfeb0, 0xfeaf, 0xfeb0}, // 0x0632 = zain
65         {0xfeb1, 0xfeb2, 0xfeb3, 0xfeb4}, // 0x0633 = seen
66         {0xfeb5, 0xfeb6, 0xfeb7, 0xfeb8}, // 0x0634 = sheen
67         {0xfeb9, 0xfeba, 0xfebb, 0xfebc}, // 0x0635 = sad
68         {0xfebd, 0xfebe, 0xfebf, 0xfec0}, // 0x0636 = dad
69         {0xfec1, 0xfec2, 0xfec3, 0xfec4}, // 0x0637 = tah
70         {0xfec5, 0xfec6, 0xfec7, 0xfec8}, // 0x0638 = zah
71         {0xfec9, 0xfeca, 0xfecb, 0xfecc}, // 0x0639 = ain
72         {0xfecd, 0xfece, 0xfecf, 0xfed0}, // 0x063a = ghain
73         {0, 0, 0, 0}, // 0x063b
74         {0, 0, 0, 0}, // 0x063c
75         {0, 0, 0, 0}, // 0x063d
76         {0, 0, 0, 0}, // 0x063e
77         {0, 0, 0, 0}, // 0x063f
78
79         {0, 0, 0, 0}, // 0x0640
80         {0xfed1, 0xfed2, 0xfed3, 0xfed4}, // 0x0641 = fa
81         {0xfed5, 0xfed6, 0xfed7, 0xfed8}, // 0x0642 = qaf
82         {0xfed9, 0xfeda, 0xfedb, 0xfedc}, // 0x0643 = kaf
83         {0xfedd, 0xfede, 0xfedf, 0xfee0}, // 0x0644 = lam
84         {0xfee1, 0xfee2, 0xfee3, 0xfee4}, // 0x0645 = meem
85         {0xfee5, 0xfee6, 0xfee7, 0xfee8}, // 0x0646 = noon
86         {0xfee9, 0xfeea, 0xfeeb, 0xfeec}, // 0x0647 = ha
87         {0xfeed, 0xfeee, 0xfeed, 0xfeee}, // 0x0648 = waw
88         {0xfeef, 0xfef0, 0xfeef, 0xfef0}, // 0x0649 = alef maksura
89         {0xfef1, 0xfef2, 0xfef3, 0xfef4}, // 0x064a = ya
90         {0x065b, 0x065b, 0x065b, 0x065b}, // 0x064b = fathatan
91         {0x065c, 0x065c, 0x065c, 0x065c}, // 0x064c = dammatan
92         {0x064d, 0x064d, 0x064d, 0x064d}, // 0x064d = kasratan
93         {0x064e, 0x064e, 0x064e, 0x064e}, // 0x064e = fatha
94         {0x064f, 0x064f, 0x064f, 0x064f}, // 0x064f = damma
95
96         {0x0650, 0x0650, 0x0650, 0x0650}, // 0x0650 = kasra
97         {0x0651, 0x0651, 0x0651, 0x0651}, // 0x0651 = shadda
98         {0x0652, 0x0652, 0x0652, 0x0652}, // 0x0652 = sukun
99 };
100
101
102 char_type const arabic_start = 0x0621;
103 char_type const arabic_end = 0x0652;
104
105
106 /// Information about a single UCS4 character
107 struct CharInfo {
108         /// LaTeX command for this character
109         docstring command;
110         /// Needed LaTeX preamble (or feature)
111         string preamble;
112         /// Is this a combining character?
113         bool combining;
114         /// Is \c preamble a feature known by LaTeXFeatures, or a raw LaTeX
115         /// command?
116         bool feature;
117         /// Always force the LaTeX command, even if the encoding contains
118         /// this character?
119         bool force;
120 };
121
122
123 typedef std::map<char_type, CharInfo> CharInfoMap;
124 CharInfoMap unicodesymbols;
125
126
127 /// The highest code point in UCS4 encoding (1<<20 + 1<<16)
128 char_type const max_ucs4 = 0x110000;
129
130 } // namespace anon
131
132
133 Encoding::Encoding(string const & n, string const & l, string const & i,
134                    bool f, Encoding::Package p)
135         : Name_(n), LatexName_(l), iconvName_(i), fixedwidth_(f), package_(p)
136 {
137         if (n == "ascii") {
138                 // ASCII can encode 128 code points and nothing else
139                 start_encodable_ = 128;
140                 complete_ = true;
141         } else if (i == "UTF-8") {
142                 // UTF8 can encode all UCS4 code points
143                 start_encodable_ = max_ucs4;
144                 complete_ = true;
145         } else {
146                 complete_ = false;
147         }
148 }
149
150
151 void Encoding::init() const
152 {
153         start_encodable_ = 0;
154         // temporarily switch off lyxerr, since we will generate iconv errors
155         lyxerr.disable();
156         if (fixedwidth_) {
157                 // We do not need to check all UCS4 code points, it is enough
158                 // if we check all 256 code points of this encoding.
159                 for (unsigned short j = 0; j < 256; ++j) {
160                         char const c = j;
161                         std::vector<char_type> const ucs4 = eightbit_to_ucs4(&c, 1, iconvName_);
162                         if (ucs4.size() == 1) {
163                                 char_type const c = ucs4[0];
164                                 CharInfoMap::const_iterator const it = unicodesymbols.find(c);
165                                 if (it == unicodesymbols.end() || !it->second.force)
166                                         encodable_.insert(c);
167                         }
168                 }
169                 } else {
170                 // We do not know how many code points this encoding has, and
171                 // they do not have a direct representation as a single byte,
172                 // therefore we need to check all UCS4 code points.
173                 // This is expensive!
174                 for (char_type c = 0; c < max_ucs4; ++c) {
175                         std::vector<char> const eightbit = ucs4_to_eightbit(&c, 1, iconvName_);
176                         if (!eightbit.empty()) {
177                                 CharInfoMap::const_iterator const it = unicodesymbols.find(c);
178                                 if (it == unicodesymbols.end() || !it->second.force)
179                                         encodable_.insert(c);
180                         }
181                 }
182         }
183         lyxerr.enable();
184         CharSet::iterator it = encodable_.find(start_encodable_);
185         while (it != encodable_.end()) {
186                 encodable_.erase(it);
187                 ++start_encodable_;
188                 it = encodable_.find(start_encodable_);
189         }
190         complete_ = true;
191 }
192
193
194 docstring const Encoding::latexChar(char_type c) const
195 {
196         // assure the used encoding is properly initialized
197         if (!complete_)
198                 init();
199         BOOST_ASSERT(complete_);
200
201         if (c < start_encodable_)
202                 return docstring(1, c);
203         if (encodable_.find(c) == encodable_.end()) {
204                 // c cannot be encoded in this encoding
205                 CharInfoMap::const_iterator const it = unicodesymbols.find(c);
206                 if (it == unicodesymbols.end())
207                         lyxerr << "Could not find LaTeX command for character 0x"
208                                << std::hex << c << std::dec
209                                << ".\nLaTeX export will fail."
210                                << endl;
211                 else
212                         return it->second.command;
213         }
214         return docstring(1, c);
215 }
216
217
218 void Encodings::validate(char_type c, LaTeXFeatures & features)
219 {
220         CharInfoMap::const_iterator const it = unicodesymbols.find(c);
221         if (it != unicodesymbols.end() && !it->second.preamble.empty()) {
222                 if (it->second.feature)
223                         features.require(it->second.preamble);
224                 else
225                         features.addPreambleSnippet(it->second.preamble);
226         }
227 }
228
229
230 bool Encodings::isComposeChar_hebrew(char_type c)
231 {
232         return c <= 0x05c2 && c >= 0x05b0 &&
233                c != 0x05be && c != 0x05c0;
234 }
235
236
237 // Special Arabic letters are ones that do not get connected from left
238 // they are hamza, alef_madda, alef_hamza, waw_hamza, alef_hamza_under,
239 // alef, tah_marbota, dal, thal, rah, zai, wow, alef_maksoura
240
241 bool Encodings::is_arabic_special(char_type c)
242 {
243         return (c >= 0x0621 && c <= 0x0625) ||
244                 c == 0x0627 || c == 0x0629  ||
245                 c == 0x062f || c == 0x0648  ||
246                (c >= 0x0630 && c <= 0x0632) ||
247                 c == 0x0649;
248 }
249
250
251 bool Encodings::isComposeChar_arabic(char_type c)
252 {
253         return c >= 0x064b && c <= 0x0652;
254 }
255
256
257 bool Encodings::is_arabic(char_type c)
258 {
259         return c >= arabic_start && c <= arabic_end &&
260                arabic_table[c-arabic_start][0];
261 }
262
263
264 char_type Encodings::transformChar(char_type c,
265                                       Encodings::Letter_Form form)
266 {
267         if (!is_arabic(c))
268                 return c;
269
270         return arabic_table[c-arabic_start][form];
271 }
272
273
274 bool Encodings::isCombiningChar(char_type c)
275 {
276         CharInfoMap::const_iterator const it = unicodesymbols.find(c);
277         if (it != unicodesymbols.end())
278                 return it->second.combining;
279         return false;
280 }
281
282
283 Encoding const * Encodings::getFromLyXName(string const & name) const
284 {
285         EncodingList::const_iterator it = encodinglist.find(name);
286         if (it != encodinglist.end())
287                 return &it->second;
288         else
289                 return 0;
290 }
291
292
293 Encoding const * Encodings::getFromLaTeXName(string const & name) const
294 {
295         // We don't use std::find_if because it makes copies of the pairs in
296         // the map.
297         // This linear search is OK since we don't have many encodings.
298         // Users could even optimize it by putting the encodings they use
299         // most at the top of lib/encodings.
300         EncodingList::const_iterator const end = encodinglist.end();
301         for (EncodingList::const_iterator it = encodinglist.begin(); it != end; ++it)
302                 if (it->second.latexName() == name)
303                         return &it->second;
304         return 0;
305 }
306
307
308 Encodings::Encodings()
309 {
310 }
311
312
313 void Encodings::read(FileName const & encfile, FileName const & symbolsfile)
314 {
315         // We must read the symbolsfile first, because the Encoding
316         // constructor depends on it.
317         Lexer symbolslex(0, 0);
318         symbolslex.setFile(symbolsfile);
319         while (symbolslex.isOK()) {
320                 char_type symbol;
321                 CharInfo info;
322                 string flags;
323
324                 if (symbolslex.next(true)) {
325                         std::istringstream is(symbolslex.getString());
326                         // reading symbol directly does not work if
327                         // char_type == std::wchar_t.
328                         boost::uint32_t tmp;
329                         if(!(is >> std::hex >> tmp))
330                                 break;
331                         symbol = tmp;
332                 } else
333                         break;
334                 if (symbolslex.next(true))
335                         info.command = symbolslex.getDocString();
336                 else
337                         break;
338                 if (symbolslex.next(true))
339                         info.preamble = symbolslex.getString();
340                 else
341                         break;
342                 if (symbolslex.next(true))
343                         flags = symbolslex.getString();
344                 else
345                         break;
346
347                 info.combining = false;
348                 info.feature = false;
349                 info.force = false;
350                 while (!flags.empty()) {
351                         string flag;
352                         flags = support::split(flags, flag, ',');
353                         if (flag == "combining")
354                                 info.combining = true;
355                         else if (flag == "force")
356                                 info.force = true;
357                         else
358                                 lyxerr << "Ignoring unknown flag `" << flag
359                                        << "' for symbol `0x" 
360                                        << std::hex << symbol << std::dec 
361                                        << "'." << endl;
362                 }
363
364                 if (!info.preamble.empty())
365                         info.feature = info.preamble[0] != '\\';
366
367                 LYXERR(Debug::INFO)
368                         << "Read unicode symbol " << symbol << " '"
369                         << to_utf8(info.command) << "' '" << info.preamble
370                         << "' " << info.combining << ' ' << info.feature
371                         << endl;
372                 unicodesymbols[symbol] = info;
373         }
374
375         // Now read the encodings
376         enum Encodingtags {
377                 et_encoding = 1,
378                 et_end,
379                 et_last
380         };
381
382         struct keyword_item encodingtags[et_last - 1] = {
383                 { "encoding", et_encoding },
384                 { "end", et_end }
385         };
386
387         Lexer lex(encodingtags, et_last - 1);
388         lex.setFile(encfile);
389         while (lex.isOK()) {
390                 switch (lex.lex()) {
391                 case et_encoding:
392                 {
393                         lex.next();
394                         string const name = lex.getString();
395                         lex.next();
396                         string const latexname = lex.getString();
397                         lex.next();
398                         string const iconvname = lex.getString();
399                         lex.next();
400                         string const width = lex.getString();
401                         bool fixedwidth;
402                         if (width == "fixed")
403                                 fixedwidth = true;
404                         else if (width == "variable")
405                                 fixedwidth = false;
406                         else
407                                 lex.printError("Encodings::read: "
408                                                "Unknown width: `$$Token'");
409                         lex.next();
410                         string const p = lex.getString();
411                         Encoding::Package package;
412                         if (p == "none")
413                                 package = Encoding::none;
414                         else if (p == "inputenc")
415                                 package = Encoding::inputenc;
416                         else if (p == "CJK")
417                                 package = Encoding::CJK;
418                         else
419                                 lex.printError("Encodings::read: "
420                                                "Unknown package: `$$Token'");
421                         LYXERR(Debug::INFO) << "Reading encoding " << name << endl;
422                         encodinglist[name] = Encoding(name, latexname,
423                                                       iconvname, fixedwidth,
424                                                       package);
425                         if (lex.lex() != et_end)
426                                 lex.printError("Encodings::read: "
427                                                "missing end");
428                         break;
429                 }
430                 case et_end:
431                         lex.printError("Encodings::read: Misplaced end");
432                         break;
433                 case Lexer::LEX_FEOF:
434                         break;
435                 default:
436                         lex.printError("Encodings::read: "
437                                        "Unknown tag: `$$Token'");
438                         break;
439                 }
440         }
441 }
442
443
444 } // namespace lyx