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