]> git.lyx.org Git - lyx.git/blob - src/LaTeXFonts.cpp
Fix text direction issue for InsetInfo in RTL context
[lyx.git] / src / LaTeXFonts.cpp
1 /**
2  * \file LaTeXFonts.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Jürgen Spitzmüller
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "LaTeXFonts.h"
14
15 #include "LaTeXFeatures.h"
16 #include "Lexer.h"
17
18 #include "frontends/alert.h"
19
20 #include "support/convert.h"
21 #include "support/debug.h"
22 #include "support/docstream.h"
23 #include "support/FileName.h"
24 #include "support/filetools.h"
25 #include "support/gettext.h"
26 #include "support/lstrings.h"
27
28
29 using namespace std;
30 using namespace lyx::support;
31
32
33 namespace lyx {
34
35 LaTeXFonts latexfonts;
36
37
38 LaTeXFont LaTeXFont::altFont(docstring const & name)
39 {
40         return theLaTeXFonts().getAltFont(name);
41 }
42
43
44 bool LaTeXFont::available(bool ot1, bool nomath)
45 {
46         if (nomath && !nomathfont_.empty())
47                 return altFont(nomathfont_).available(ot1, nomath);
48         else if (ot1 && !ot1font_.empty())
49                 return (ot1font_ == "none") ?
50                         true : altFont(ot1font_).available(ot1, nomath);
51         else if (requires_.empty() && package_.empty())
52                 return true;
53         else if (!requires_.empty()
54                 && LaTeXFeatures::isAvailable(to_ascii(requires_)))
55                 return true;
56         else if (requires_.empty() && !package_.empty()
57                 && LaTeXFeatures::isAvailable(to_ascii(package_)))
58                 return true;
59         else if (!altfonts_.empty()) {
60                 for (size_t i = 0; i < altfonts_.size(); ++i) {
61                         if (altFont(altfonts_[i]).available(ot1, nomath))
62                                 return true;
63                 }
64         }
65         return false;
66 }
67
68
69 bool LaTeXFont::providesNoMath(bool ot1, bool complete)
70 {
71         docstring const usedfont = getUsedFont(ot1, complete, false);
72
73         if (usedfont.empty())
74                 return false;
75         else if (usedfont != name_)
76                 return altFont(usedfont).providesNoMath(ot1, complete);
77
78         return (!nomathfont_.empty() && available(ot1, true));
79 }
80
81
82 bool LaTeXFont::providesOSF(bool ot1, bool complete, bool nomath)
83 {
84         docstring const usedfont = getUsedFont(ot1, complete, nomath);
85
86         if (usedfont.empty())
87                 return false;
88         else if (usedfont != name_)
89                 return altFont(usedfont).providesOSF(ot1, complete, nomath);
90         else if (!osffont_.empty())
91                 return altFont(osffont_).available(ot1, nomath);
92         else if (!available(ot1, nomath))
93                 return false;
94
95         return (!osfoption_.empty() || !osfscoption_.empty());
96 }
97
98
99 bool LaTeXFont::providesSC(bool ot1, bool complete, bool nomath)
100 {
101         docstring const usedfont = getUsedFont(ot1, complete, nomath);
102
103         if (usedfont.empty())
104                 return false;
105         else if (usedfont != name_)
106                 return altFont(usedfont).providesSC(ot1, complete, nomath);
107         else if (!available(ot1, nomath))
108                 return false;
109
110         return (!scoption_.empty() || !osfscoption_.empty());
111 }
112
113
114 bool LaTeXFont::hasMonolithicExpertSet(bool ot1, bool complete, bool nomath)
115 {
116         docstring const usedfont = getUsedFont(ot1, complete, nomath);
117
118         if (usedfont.empty())
119                 return false;
120         else if (usedfont != name_)
121                 return altFont(usedfont).hasMonolithicExpertSet(ot1, complete, nomath);
122         return (!osfoption_.empty() && !scoption_.empty() && osfoption_ == scoption_)
123                 || (osfoption_.empty() && scoption_.empty() && !osfscoption_.empty());
124 }
125
126
127 bool LaTeXFont::providesScale(bool ot1, bool complete, bool nomath)
128 {
129         docstring const usedfont = getUsedFont(ot1, complete, nomath);
130
131         if (usedfont.empty())
132                 return false;
133         else if (usedfont != name_)
134                 return altFont(usedfont).providesScale(ot1, complete, nomath);
135         else if (!available(ot1, nomath))
136                 return false;
137         return (!scaleoption_.empty());
138 }
139
140 bool LaTeXFont::provides(std::string const & name, bool ot1, bool complete, bool nomath)
141 {
142         docstring const usedfont = getUsedFont(ot1, complete, nomath);
143
144         if (usedfont.empty())
145                 return false;
146         else if (usedfont != name_)
147                 return altFont(usedfont).provides(name, ot1, complete, nomath);
148         else if (provides_.empty())
149                 return false;
150
151         for (size_t i = 0; i < provides_.size(); ++i) {
152                 if (provides_[i] == name)
153                         return true;
154         }
155         return false;
156 }
157
158
159 docstring const LaTeXFont::getUsedFont(bool ot1, bool complete, bool nomath)
160 {
161         if (nomath && !nomathfont_.empty() && available(ot1, true))
162                 return nomathfont_;
163         else if (ot1 && !ot1font_.empty())
164                 return (ot1font_ == "none") ? docstring() : ot1font_;
165         else if (family_ == "rm" && complete && !completefont_.empty()
166                  && altFont(completefont_).available(ot1, nomath))
167                         return completefont_;
168         else if (switchdefault_) {
169                 if (requires_.empty()
170                     || (!requires_.empty()
171                         && LaTeXFeatures::isAvailable(to_ascii(requires_))))
172                         return name_;
173         }
174         else if (!requires_.empty()
175                 && LaTeXFeatures::isAvailable(to_ascii(requires_)))
176                         return name_;
177         else if (!package_.empty()
178                 && LaTeXFeatures::isAvailable(to_ascii(package_)))
179                         return name_;
180         else if (!preamble_.empty() && package_.empty()
181                  && requires_.empty() && !switchdefault_
182                  && altfonts_.empty()) {
183                         return name_;
184         }
185         else if (!altfonts_.empty()) {
186                 for (size_t i = 0; i < altfonts_.size(); ++i) {
187                         LaTeXFont altf = altFont(altfonts_[i]);
188                         if (altf.available(ot1, nomath))
189                                 return altf.getUsedFont(ot1, complete, nomath);
190                 }
191         }
192
193         return docstring();
194 }
195
196
197 docstring const LaTeXFont::getUsedPackage(bool ot1, bool complete, bool nomath)
198 {
199         docstring const usedfont = getUsedFont(ot1, complete, nomath);
200         if (usedfont.empty())
201                 return docstring();
202         return theLaTeXFonts().getLaTeXFont(usedfont).package();
203 }
204
205
206 string const LaTeXFont::getAvailablePackage(bool dryrun)
207 {
208         if (package_.empty())
209                 return string();
210
211         string const package = to_ascii(package_);
212         if (!requires_.empty() && LaTeXFeatures::isAvailable(to_ascii(requires_)))
213                 return package;
214         else if (LaTeXFeatures::isAvailable(package))
215                 return package;
216         // Output unavailable packages in source preview
217         else if (dryrun)
218                 return package;
219
220         docstring const req = requires_.empty() ? package_ : requires_;
221         frontend::Alert::warning(_("Font not available"),
222                         bformat(_("The LaTeX package `%1$s' needed for the font `%2$s'\n"
223                                   "is not available on your system. LyX will fall back to the default font."),
224                                 req, guiname_), true);
225
226         return string();
227 }
228
229
230 string const LaTeXFont::getPackageOptions(bool ot1, bool complete, bool sc, bool osf,
231                                           int scale, bool nomath)
232 {
233         ostringstream os;
234         bool const needosfopt = (osf != osfdefault_);
235         bool const has_osf = providesOSF(ot1, complete, nomath);
236         bool const has_sc = providesSC(ot1, complete, nomath);
237
238         if (!packageoption_.empty())
239                 os << to_ascii(packageoption_);
240
241         if (sc && needosfopt && has_osf && has_sc) {
242                 if (!os.str().empty())
243                         os << ',';
244                 if (!osfscoption_.empty())
245                         os << to_ascii(osfscoption_);
246                 else
247                         os << to_ascii(osfoption_)
248                            << ',' << to_ascii(scoption_);
249         } else if (needosfopt && has_osf) {
250                 if (!os.str().empty())
251                         os << ',';
252                 os << to_ascii(osfoption_);
253         } else if (sc && has_sc) {
254                 if (!os.str().empty())
255                         os << ',';
256                 os << to_ascii(scoption_);
257         }
258
259         if (scale != 100 && !scaleoption_.empty()
260             && providesScale(ot1, complete, nomath)) {
261                 if (!os.str().empty())
262                         os << ',';
263                 os << subst(to_ascii(scaleoption_), "$$val",
264                             convert<std::string>(float(scale) / 100));
265         }
266         return os.str();
267 }
268
269
270 string const LaTeXFont::getLaTeXCode(bool dryrun, bool ot1, bool complete, bool sc,
271                                      bool osf, bool nomath, int const & scale)
272 {
273         ostringstream os;
274
275         docstring const usedfont = getUsedFont(ot1, complete, nomath);
276         if (usedfont.empty())
277                 return string();
278         else if (usedfont != name_)
279                 return altFont(usedfont).getLaTeXCode(dryrun, ot1, complete, sc, osf, nomath, scale);
280
281         if (switchdefault_) {
282                 if (family_.empty()) {
283                         LYXERR0("Error: Font `" << name_ << "' has no family defined!");
284                         return string();
285                 }
286                 if (available(ot1, nomath) || dryrun)
287                         os << "\\renewcommand{\\" << to_ascii(family_) << "default}{"
288                            << to_ascii(name_) << "}\n";
289                 else
290                         frontend::Alert::warning(_("Font not available"),
291                                         bformat(_("The LaTeX package `%1$s' needed for the font `%2$s'\n"
292                                                   "is not available on your system. LyX will fall back to the default font."),
293                                                 requires_, guiname_), true);
294         } else {
295                 string const package =
296                         getAvailablePackage(dryrun);
297                 string const packageopts = getPackageOptions(ot1, complete, sc, osf, scale, nomath);
298                 if (packageopts.empty() && !package.empty())
299                         os << "\\usepackage{" << package << "}\n";
300                 else if (!packageopts.empty() && !package.empty())
301                         os << "\\usepackage[" << packageopts << "]{" << package << "}\n";
302         }
303         if (osf && providesOSF(ot1, complete, nomath) && !osffont_.empty())
304                 os << altFont(osffont_).getLaTeXCode(dryrun, ot1, complete, sc, osf, nomath, scale);
305
306         if (!preamble_.empty())
307                 os << to_utf8(preamble_);
308
309         return os.str();
310 }
311
312
313 bool LaTeXFont::hasFontenc(string const & name) const
314 {
315         for (auto const & fe : fontenc_) {
316                 if (fe == name)
317                         return true;
318         }
319         return false;
320 }
321
322
323 bool LaTeXFont::readFont(Lexer & lex)
324 {
325         enum LaTeXFontTags {
326                 LF_ALT_FONTS = 1,
327                 LF_COMPLETE_FONT,
328                 LF_END,
329                 LF_FAMILY,
330                 LF_FONTENC,
331                 LF_GUINAME,
332                 LF_NOMATHFONT,
333                 LF_OSFDEFAULT,
334                 LF_OSFFONT,
335                 LF_OSFOPTION,
336                 LF_OSFSCOPTION,
337                 LF_OT1_FONT,
338                 LF_PACKAGE,
339                 LF_PACKAGEOPTION,
340                 LF_PREAMBLE,
341                 LF_PROVIDES,
342                 LF_REQUIRES,
343                 LF_SCALEOPTION,
344                 LF_SCOPTION,
345                 LF_SWITCHDEFAULT
346         };
347
348         // Keep these sorted alphabetically!
349         LexerKeyword latexFontTags[] = {
350                 { "altfonts",             LF_ALT_FONTS },
351                 { "completefont",         LF_COMPLETE_FONT },
352                 { "endfont",              LF_END },
353                 { "family",               LF_FAMILY },
354                 { "fontencoding",         LF_FONTENC },
355                 { "guiname",              LF_GUINAME },
356                 { "nomathfont",           LF_NOMATHFONT },
357                 { "osfdefault",           LF_OSFDEFAULT },
358                 { "osffont",              LF_OSFFONT },
359                 { "osfoption",            LF_OSFOPTION },
360                 { "osfscoption",          LF_OSFSCOPTION },
361                 { "ot1font",              LF_OT1_FONT },
362                 { "package",              LF_PACKAGE },
363                 { "packageoption",        LF_PACKAGEOPTION },
364                 { "preamble",             LF_PREAMBLE },
365                 { "provides",             LF_PROVIDES },
366                 { "requires",             LF_REQUIRES },
367                 { "scaleoption",          LF_SCALEOPTION },
368                 { "scoption",             LF_SCOPTION },
369                 { "switchdefault",        LF_SWITCHDEFAULT }
370         };
371
372         bool error = false;
373         bool finished = false;
374         lex.pushTable(latexFontTags);
375         // parse style section
376         while (!finished && lex.isOK() && !error) {
377                 int le = lex.lex();
378                 // See comment in LyXRC.cpp.
379                 switch (le) {
380                 case Lexer::LEX_FEOF:
381                         continue;
382
383                 case Lexer::LEX_UNDEF: // parse error
384                         lex.printError("Unknown LaTeXFont tag `$$Token'");
385                         error = true;
386                         continue;
387
388                 default:
389                         break;
390                 }
391                 switch (static_cast<LaTeXFontTags>(le)) {
392                 case LF_END: // end of structure
393                         finished = true;
394                         break;
395                 case LF_ALT_FONTS: {
396                         lex.eatLine();
397                         docstring altp = lex.getDocString();
398                         altfonts_ = getVectorFromString(altp);
399                         break;
400                 }
401                 case LF_COMPLETE_FONT:
402                         lex >> completefont_;
403                         break;
404                 case LF_FAMILY:
405                         lex >> family_;
406                         break;
407                 case LF_GUINAME:
408                         lex >> guiname_;
409                         break;
410                 case LF_FONTENC: {
411                         lex.eatLine();
412                         string fe = lex.getString();
413                         fontenc_ = getVectorFromString(fe);
414                         break;
415                 }
416                 case LF_NOMATHFONT:
417                         lex >> nomathfont_;
418                         break;
419                 case LF_OSFOPTION:
420                         lex >> osfoption_;
421                         break;
422                 case LF_OSFFONT:
423                         lex >> osffont_;
424                         break;
425                 case LF_OSFDEFAULT:
426                         lex >> osfdefault_;
427                         break;
428                 case LF_OSFSCOPTION:
429                         lex >> osfscoption_;
430                         break;
431                 case LF_OT1_FONT:
432                         lex >> ot1font_;
433                         break;
434                 case LF_PACKAGE:
435                         lex >> package_;
436                         break;
437                 case LF_PACKAGEOPTION:
438                         lex >> packageoption_;
439                         break;
440                 case LF_PREAMBLE:
441                         preamble_ = lex.getLongString(from_ascii("EndPreamble"));
442                         break;
443                 case LF_PROVIDES: {
444                         lex.eatLine();
445                         string features = lex.getString();
446                         provides_ = getVectorFromString(features);
447                         break;
448                 }
449                 case LF_REQUIRES:
450                         lex >> requires_;
451                         break;
452                 case LF_SCALEOPTION:
453                         lex >> scaleoption_;
454                         break;
455                 case LF_SCOPTION:
456                         lex >> scoption_;
457                         break;
458                 case LF_SWITCHDEFAULT:
459                         lex >> switchdefault_;
460                         break;
461                 }
462         }
463         if (!finished) {
464                 lex.printError("No End tag found for LaTeXFont tag `$$Token'");
465                 return false;
466         }
467         lex.popTable();
468         return finished && !error;
469 }
470
471
472 bool LaTeXFont::read(Lexer & lex)
473 {
474         switchdefault_ = 0;
475         osfdefault_ = 0;
476
477         if (!lex.next()) {
478                 lex.printError("No name given for LaTeX font: `$$Token'.");
479                 return false;
480         }
481
482         name_ = lex.getDocString();
483         LYXERR(Debug::INFO, "Reading LaTeX font " << name_);
484         if (!readFont(lex)) {
485                 LYXERR0("Error parsing LaTeX font `" << name_ << '\'');
486                 return false;
487         }
488
489         if (fontenc_.empty())
490                 fontenc_.push_back("T1");
491
492         return true;
493 }
494
495
496 void LaTeXFonts::readLaTeXFonts()
497 {
498         // Read latexfonts file
499         FileName filename = libFileSearch(string(), "latexfonts");
500         if (filename.empty()) {
501                 LYXERR0("Error: latexfonts file not found!");
502                 return;
503         }
504         Lexer lex;
505         lex.setFile(filename);
506         lex.setContext("LaTeXFeatures::readLaTeXFonts");
507         while (lex.isOK()) {
508                 int le = lex.lex();
509                 switch (le) {
510                 case Lexer::LEX_FEOF:
511                         continue;
512
513                 default:
514                         break;
515                 }
516                 string const type = lex.getString();
517                 if (type != "Font" && type != "AltFont") {
518                         lex.printError("Unknown LaTeXFont tag `$$Token'");
519                         continue;
520                 }
521                 LaTeXFont f;
522                 f.read(lex);
523                 if (!lex)
524                         break;
525
526                 if (type == "AltFont")
527                         texaltfontmap_[f.name()] = f;
528                 else
529                         texfontmap_[f.name()] = f;
530         }
531 }
532
533
534 LaTeXFonts::TexFontMap LaTeXFonts::getLaTeXFonts()
535 {
536         if (texfontmap_.empty())
537                 readLaTeXFonts();
538         return texfontmap_;
539 }
540
541
542 LaTeXFont LaTeXFonts::getLaTeXFont(docstring const & name)
543 {
544         if (name == "default" || name == "auto")
545                 return LaTeXFont();
546         if (texfontmap_.empty())
547                 readLaTeXFonts();
548         if (texfontmap_.find(name) == texfontmap_.end()) {
549                 LYXERR0("LaTeXFonts::getLaTeXFont: font '" << name << "' not found!");
550                 return LaTeXFont();
551         }
552         return texfontmap_[name];
553 }
554
555
556 LaTeXFont LaTeXFonts::getAltFont(docstring const & name)
557 {
558         if (name == "default" || name == "auto")
559                 return LaTeXFont();
560         if (texaltfontmap_.empty())
561                 readLaTeXFonts();
562         if (texaltfontmap_.find(name) == texaltfontmap_.end()) {
563                 LYXERR0("LaTeXFonts::getAltFont: alternative font '" << name << "' not found!");
564                 return LaTeXFont();
565         }
566         return texaltfontmap_[name];
567 }
568
569
570 } // namespace lyx