]> git.lyx.org Git - features.git/blob - src/LaTeXFonts.cpp
824986052ac7b3e2259d5821725f25f13cf3bc3f
[features.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
141 bool LaTeXFont::providesMoreOptions(bool ot1, bool complete, bool nomath)
142 {
143         docstring const usedfont = getUsedFont(ot1, complete, nomath);
144
145         if (usedfont.empty())
146                 return false;
147         else if (usedfont != name_)
148                 return altFont(usedfont).providesMoreOptions(ot1, complete, nomath);
149         else if (!available(ot1, nomath))
150                 return false;
151
152         return (moreopts_);
153 }
154
155 bool LaTeXFont::provides(std::string const & name, bool ot1, bool complete, bool nomath)
156 {
157         docstring const usedfont = getUsedFont(ot1, complete, nomath);
158
159         if (usedfont.empty())
160                 return false;
161         else if (usedfont != name_)
162                 return altFont(usedfont).provides(name, ot1, complete, nomath);
163         else if (provides_.empty())
164                 return false;
165
166         for (size_t i = 0; i < provides_.size(); ++i) {
167                 if (provides_[i] == name)
168                         return true;
169         }
170         return false;
171 }
172
173
174 docstring const LaTeXFont::getUsedFont(bool ot1, bool complete, bool nomath)
175 {
176         if (nomath && !nomathfont_.empty() && available(ot1, true))
177                 return nomathfont_;
178         else if (ot1 && !ot1font_.empty())
179                 return (ot1font_ == "none") ? docstring() : ot1font_;
180         else if (family_ == "rm" && complete && !completefont_.empty()
181                  && altFont(completefont_).available(ot1, nomath))
182                         return completefont_;
183         else if (switchdefault_) {
184                 if (requires_.empty()
185                     || (!requires_.empty()
186                         && LaTeXFeatures::isAvailable(to_ascii(requires_))))
187                         return name_;
188         }
189         else if (!requires_.empty()
190                 && LaTeXFeatures::isAvailable(to_ascii(requires_)))
191                         return name_;
192         else if (!package_.empty()
193                 && LaTeXFeatures::isAvailable(to_ascii(package_)))
194                         return name_;
195         else if (!preamble_.empty() && package_.empty()
196                  && requires_.empty() && !switchdefault_
197                  && altfonts_.empty()) {
198                         return name_;
199         }
200         else if (!altfonts_.empty()) {
201                 for (size_t i = 0; i < altfonts_.size(); ++i) {
202                         LaTeXFont altf = altFont(altfonts_[i]);
203                         if (altf.available(ot1, nomath))
204                                 return altf.getUsedFont(ot1, complete, nomath);
205                 }
206         }
207
208         return docstring();
209 }
210
211
212 docstring const LaTeXFont::getUsedPackage(bool ot1, bool complete, bool nomath)
213 {
214         docstring const usedfont = getUsedFont(ot1, complete, nomath);
215         if (usedfont.empty())
216                 return docstring();
217         return theLaTeXFonts().getLaTeXFont(usedfont).package();
218 }
219
220
221 string const LaTeXFont::getAvailablePackage(bool dryrun)
222 {
223         if (package_.empty())
224                 return string();
225
226         string const package = to_ascii(package_);
227         if (!requires_.empty() && LaTeXFeatures::isAvailable(to_ascii(requires_)))
228                 return package;
229         else if (LaTeXFeatures::isAvailable(package))
230                 return package;
231         // Output unavailable packages in source preview
232         else if (dryrun)
233                 return package;
234
235         docstring const req = requires_.empty() ? package_ : requires_;
236         frontend::Alert::warning(_("Font not available"),
237                         bformat(_("The LaTeX package `%1$s' needed for the font `%2$s'\n"
238                                   "is not available on your system. LyX will fall back to the default font."),
239                                 req, guiname_), true);
240
241         return string();
242 }
243
244
245 string const LaTeXFont::getPackageOptions(bool ot1, bool complete, bool sc, bool osf,
246                                           int scale, string const extraopts, bool nomath)
247 {
248         ostringstream os;
249         bool const needosfopt = (osf != osfdefault_);
250         bool const has_osf = providesOSF(ot1, complete, nomath);
251         bool const has_sc = providesSC(ot1, complete, nomath);
252         bool const moreopts = providesMoreOptions(ot1, complete, nomath);
253
254         if (!packageoption_.empty())
255                 os << to_ascii(packageoption_);
256
257         if (sc && needosfopt && has_osf && has_sc) {
258                 if (!os.str().empty())
259                         os << ',';
260                 if (!osfscoption_.empty())
261                         os << to_ascii(osfscoption_);
262                 else
263                         os << to_ascii(osfoption_)
264                            << ',' << to_ascii(scoption_);
265         } else if (needosfopt && has_osf) {
266                 if (!os.str().empty())
267                         os << ',';
268                 os << to_ascii(osfoption_);
269         } else if (sc && has_sc) {
270                 if (!os.str().empty())
271                         os << ',';
272                 os << to_ascii(scoption_);
273         }
274
275         if (scale != 100 && !scaleoption_.empty()
276             && providesScale(ot1, complete, nomath)) {
277                 if (!os.str().empty())
278                         os << ',';
279                 os << subst(to_ascii(scaleoption_), "$$val",
280                             convert<std::string>(float(scale) / 100));
281         }
282
283         if (moreopts && !extraopts.empty()) {
284                 if (!os.str().empty())
285                         os << ',';
286                 os << extraopts;
287         }
288         return os.str();
289 }
290
291
292 string const LaTeXFont::getLaTeXCode(bool dryrun, bool ot1, bool complete, bool sc,
293                                      bool osf, bool nomath, string const extraopts,
294                                      int const & scale)
295 {
296         ostringstream os;
297
298         docstring const usedfont = getUsedFont(ot1, complete, nomath);
299         if (usedfont.empty())
300                 return string();
301         else if (usedfont != name_)
302                 return altFont(usedfont).getLaTeXCode(dryrun, ot1, complete, sc,
303                                                       osf, nomath, extraopts, scale);
304
305         if (switchdefault_) {
306                 if (family_.empty()) {
307                         LYXERR0("Error: Font `" << name_ << "' has no family defined!");
308                         return string();
309                 }
310                 if (available(ot1, nomath) || dryrun)
311                         os << "\\renewcommand{\\" << to_ascii(family_) << "default}{"
312                            << to_ascii(name_) << "}\n";
313                 else
314                         frontend::Alert::warning(_("Font not available"),
315                                         bformat(_("The LaTeX package `%1$s' needed for the font `%2$s'\n"
316                                                   "is not available on your system. LyX will fall back to the default font."),
317                                                 requires_, guiname_), true);
318         } else {
319                 string const package =
320                         getAvailablePackage(dryrun);
321                 string const packageopts = getPackageOptions(ot1, complete, sc, osf, scale, extraopts, nomath);
322                 if (packageopts.empty() && !package.empty())
323                         os << "\\usepackage{" << package << "}\n";
324                 else if (!packageopts.empty() && !package.empty())
325                         os << "\\usepackage[" << packageopts << "]{" << package << "}\n";
326         }
327         if (osf && providesOSF(ot1, complete, nomath) && !osffont_.empty())
328                 os << altFont(osffont_).getLaTeXCode(dryrun, ot1, complete, sc, osf,
329                                                      nomath, extraopts, scale);
330
331         if (!preamble_.empty())
332                 os << to_utf8(preamble_);
333
334         return os.str();
335 }
336
337
338 bool LaTeXFont::hasFontenc(string const & name) const
339 {
340         for (auto const & fe : fontenc_) {
341                 if (fe == name)
342                         return true;
343         }
344         return false;
345 }
346
347
348 bool LaTeXFont::readFont(Lexer & lex)
349 {
350         enum LaTeXFontTags {
351                 LF_ALT_FONTS = 1,
352                 LF_COMPLETE_FONT,
353                 LF_END,
354                 LF_FAMILY,
355                 LF_FONTENC,
356                 LF_GUINAME,
357                 LF_NOMATHFONT,
358                 LF_OSFDEFAULT,
359                 LF_OSFFONT,
360                 LF_OSFOPTION,
361                 LF_OSFSCOPTION,
362                 LF_OT1_FONT,
363                 LF_MOREOPTS,
364                 LF_PACKAGE,
365                 LF_PACKAGEOPTION,
366                 LF_PREAMBLE,
367                 LF_PROVIDES,
368                 LF_REQUIRES,
369                 LF_SCALEOPTION,
370                 LF_SCOPTION,
371                 LF_SWITCHDEFAULT
372         };
373
374         // Keep these sorted alphabetically!
375         LexerKeyword latexFontTags[] = {
376                 { "altfonts",             LF_ALT_FONTS },
377                 { "completefont",         LF_COMPLETE_FONT },
378                 { "endfont",              LF_END },
379                 { "family",               LF_FAMILY },
380                 { "fontencoding",         LF_FONTENC },
381                 { "guiname",              LF_GUINAME },
382                 { "moreoptions",          LF_MOREOPTS },
383                 { "nomathfont",           LF_NOMATHFONT },
384                 { "osfdefault",           LF_OSFDEFAULT },
385                 { "osffont",              LF_OSFFONT },
386                 { "osfoption",            LF_OSFOPTION },
387                 { "osfscoption",          LF_OSFSCOPTION },
388                 { "ot1font",              LF_OT1_FONT },
389                 { "package",              LF_PACKAGE },
390                 { "packageoption",        LF_PACKAGEOPTION },
391                 { "packageoptions",       LF_PACKAGEOPTION },
392                 { "preamble",             LF_PREAMBLE },
393                 { "provides",             LF_PROVIDES },
394                 { "requires",             LF_REQUIRES },
395                 { "scaleoption",          LF_SCALEOPTION },
396                 { "scoption",             LF_SCOPTION },
397                 { "switchdefault",        LF_SWITCHDEFAULT }
398         };
399
400         bool error = false;
401         bool finished = false;
402         lex.pushTable(latexFontTags);
403         // parse style section
404         while (!finished && lex.isOK() && !error) {
405                 int le = lex.lex();
406                 // See comment in LyXRC.cpp.
407                 switch (le) {
408                 case Lexer::LEX_FEOF:
409                         continue;
410
411                 case Lexer::LEX_UNDEF: // parse error
412                         lex.printError("Unknown LaTeXFont tag `$$Token'");
413                         error = true;
414                         continue;
415
416                 default:
417                         break;
418                 }
419                 switch (static_cast<LaTeXFontTags>(le)) {
420                 case LF_END: // end of structure
421                         finished = true;
422                         break;
423                 case LF_ALT_FONTS: {
424                         lex.eatLine();
425                         docstring altp = lex.getDocString();
426                         altfonts_ = getVectorFromString(altp);
427                         break;
428                 }
429                 case LF_COMPLETE_FONT:
430                         lex >> completefont_;
431                         break;
432                 case LF_FAMILY:
433                         lex >> family_;
434                         break;
435                 case LF_GUINAME:
436                         lex >> guiname_;
437                         break;
438                 case LF_FONTENC: {
439                         lex.eatLine();
440                         string fe = lex.getString();
441                         fontenc_ = getVectorFromString(fe);
442                         break;
443                 }
444                 case LF_NOMATHFONT:
445                         lex >> nomathfont_;
446                         break;
447                 case LF_OSFOPTION:
448                         lex >> osfoption_;
449                         break;
450                 case LF_OSFFONT:
451                         lex >> osffont_;
452                         break;
453                 case LF_OSFDEFAULT:
454                         lex >> osfdefault_;
455                         break;
456                 case LF_OSFSCOPTION:
457                         lex >> osfscoption_;
458                         break;
459                 case LF_OT1_FONT:
460                         lex >> ot1font_;
461                         break;
462                 case LF_PACKAGE:
463                         lex >> package_;
464                         break;
465                 case LF_PACKAGEOPTION:
466                         lex >> packageoption_;
467                         break;
468                 case LF_PREAMBLE:
469                         preamble_ = lex.getLongString(from_ascii("EndPreamble"));
470                         break;
471                 case LF_PROVIDES: {
472                         lex.eatLine();
473                         string features = lex.getString();
474                         provides_ = getVectorFromString(features);
475                         break;
476                 }
477                 case LF_REQUIRES:
478                         lex >> requires_;
479                         break;
480                 case LF_SCALEOPTION:
481                         lex >> scaleoption_;
482                         break;
483                 case LF_SCOPTION:
484                         lex >> scoption_;
485                         break;
486                 case LF_MOREOPTS:
487                         lex >> moreopts_;
488                         break;
489                 case LF_SWITCHDEFAULT:
490                         lex >> switchdefault_;
491                         break;
492                 }
493         }
494         if (!finished) {
495                 lex.printError("No End tag found for LaTeXFont tag `$$Token'");
496                 return false;
497         }
498         lex.popTable();
499         return finished && !error;
500 }
501
502
503 bool LaTeXFont::read(Lexer & lex)
504 {
505         switchdefault_ = 0;
506         osfdefault_ = 0;
507         moreopts_ = 0;
508
509         if (!lex.next()) {
510                 lex.printError("No name given for LaTeX font: `$$Token'.");
511                 return false;
512         }
513
514         name_ = lex.getDocString();
515         LYXERR(Debug::INFO, "Reading LaTeX font " << name_);
516         if (!readFont(lex)) {
517                 LYXERR0("Error parsing LaTeX font `" << name_ << '\'');
518                 return false;
519         }
520
521         if (fontenc_.empty())
522                 fontenc_.push_back("T1");
523
524         return true;
525 }
526
527
528 void LaTeXFonts::readLaTeXFonts()
529 {
530         // Read latexfonts file
531         FileName filename = libFileSearch(string(), "latexfonts");
532         if (filename.empty()) {
533                 LYXERR0("Error: latexfonts file not found!");
534                 return;
535         }
536         Lexer lex;
537         lex.setFile(filename);
538         lex.setContext("LaTeXFeatures::readLaTeXFonts");
539         while (lex.isOK()) {
540                 int le = lex.lex();
541                 switch (le) {
542                 case Lexer::LEX_FEOF:
543                         continue;
544
545                 default:
546                         break;
547                 }
548                 string const type = lex.getString();
549                 if (type != "Font" && type != "AltFont") {
550                         lex.printError("Unknown LaTeXFont tag `$$Token'");
551                         continue;
552                 }
553                 LaTeXFont f;
554                 f.read(lex);
555                 if (!lex)
556                         break;
557
558                 if (type == "AltFont")
559                         texaltfontmap_[f.name()] = f;
560                 else
561                         texfontmap_[f.name()] = f;
562         }
563 }
564
565
566 LaTeXFonts::TexFontMap LaTeXFonts::getLaTeXFonts()
567 {
568         if (texfontmap_.empty())
569                 readLaTeXFonts();
570         return texfontmap_;
571 }
572
573
574 LaTeXFont LaTeXFonts::getLaTeXFont(docstring const & name)
575 {
576         if (name == "default" || name == "auto")
577                 return LaTeXFont();
578         if (texfontmap_.empty())
579                 readLaTeXFonts();
580         if (texfontmap_.find(name) == texfontmap_.end()) {
581                 LYXERR0("LaTeXFonts::getLaTeXFont: font '" << name << "' not found!");
582                 return LaTeXFont();
583         }
584         return texfontmap_[name];
585 }
586
587
588 LaTeXFont LaTeXFonts::getAltFont(docstring const & name)
589 {
590         if (name == "default" || name == "auto")
591                 return LaTeXFont();
592         if (texaltfontmap_.empty())
593                 readLaTeXFonts();
594         if (texaltfontmap_.find(name) == texaltfontmap_.end()) {
595                 LYXERR0("LaTeXFonts::getAltFont: alternative font '" << name << "' not found!");
596                 return LaTeXFont();
597         }
598         return texaltfontmap_[name];
599 }
600
601
602 } // namespace lyx