]> git.lyx.org Git - lyx.git/blob - src/LaTeXFonts.cpp
e58b55f5048138c33c496cc68f91153aa1fdb99c
[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());
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
333         if (!preamble_.empty())
334                 os << to_utf8(preamble_);
335
336         return os.str();
337 }
338
339
340 bool LaTeXFont::hasFontenc(string const & name) const
341 {
342         for (auto const & fe : fontenc_) {
343                 if (fe == name)
344                         return true;
345         }
346         return false;
347 }
348
349
350 bool LaTeXFont::readFont(Lexer & lex)
351 {
352         enum LaTeXFontTags {
353                 LF_ALT_FONTS = 1,
354                 LF_COMPLETE_FONT,
355                 LF_END,
356                 LF_FAMILY,
357                 LF_FONTENC,
358                 LF_GUINAME,
359                 LF_NOMATHFONT,
360                 LF_OSFDEFAULT,
361                 LF_OSFFONT,
362                 LF_OSFFONTONLY,
363                 LF_OSFOPTION,
364                 LF_OSFSCOPTION,
365                 LF_OT1_FONT,
366                 LF_MOREOPTS,
367                 LF_PACKAGE,
368                 LF_PACKAGEOPTIONS,
369                 LF_PREAMBLE,
370                 LF_PROVIDES,
371                 LF_REQUIRES,
372                 LF_SCALEOPTION,
373                 LF_SCOPTION,
374                 LF_SWITCHDEFAULT
375         };
376
377         // Keep these sorted alphabetically!
378         LexerKeyword latexFontTags[] = {
379                 { "altfonts",             LF_ALT_FONTS },
380                 { "completefont",         LF_COMPLETE_FONT },
381                 { "endfont",              LF_END },
382                 { "family",               LF_FAMILY },
383                 { "fontencoding",         LF_FONTENC },
384                 { "guiname",              LF_GUINAME },
385                 { "moreoptions",          LF_MOREOPTS },
386                 { "nomathfont",           LF_NOMATHFONT },
387                 { "osfdefault",           LF_OSFDEFAULT },
388                 { "osffont",              LF_OSFFONT },
389                 { "osffontonly",          LF_OSFFONTONLY },
390                 { "osfoption",            LF_OSFOPTION },
391                 { "osfscoption",          LF_OSFSCOPTION },
392                 { "ot1font",              LF_OT1_FONT },
393                 { "package",              LF_PACKAGE },
394                 { "packageoptions",       LF_PACKAGEOPTIONS },
395                 { "preamble",             LF_PREAMBLE },
396                 { "provides",             LF_PROVIDES },
397                 { "requires",             LF_REQUIRES },
398                 { "scaleoption",          LF_SCALEOPTION },
399                 { "scoption",             LF_SCOPTION },
400                 { "switchdefault",        LF_SWITCHDEFAULT }
401         };
402
403         bool error = false;
404         bool finished = false;
405         lex.pushTable(latexFontTags);
406         // parse style section
407         while (!finished && lex.isOK() && !error) {
408                 int le = lex.lex();
409                 // See comment in LyXRC.cpp.
410                 switch (le) {
411                 case Lexer::LEX_FEOF:
412                         continue;
413
414                 case Lexer::LEX_UNDEF: // parse error
415                         lex.printError("Unknown LaTeXFont tag `$$Token'");
416                         error = true;
417                         continue;
418
419                 default:
420                         break;
421                 }
422                 switch (static_cast<LaTeXFontTags>(le)) {
423                 case LF_END: // end of structure
424                         finished = true;
425                         break;
426                 case LF_ALT_FONTS: {
427                         lex.eatLine();
428                         docstring altp = lex.getDocString();
429                         altfonts_ = getVectorFromString(altp);
430                         break;
431                 }
432                 case LF_COMPLETE_FONT:
433                         lex >> completefont_;
434                         break;
435                 case LF_FAMILY:
436                         lex >> family_;
437                         break;
438                 case LF_GUINAME:
439                         lex >> guiname_;
440                         break;
441                 case LF_FONTENC: {
442                         lex.eatLine();
443                         string fe = lex.getString();
444                         fontenc_ = getVectorFromString(fe);
445                         break;
446                 }
447                 case LF_NOMATHFONT:
448                         lex >> nomathfont_;
449                         break;
450                 case LF_OSFOPTION:
451                         lex >> osfoption_;
452                         break;
453                 case LF_OSFFONT:
454                         lex >> osffont_;
455                         break;
456                 case LF_OSFDEFAULT:
457                         lex >> osfdefault_;
458                         break;
459                 case LF_OSFFONTONLY:
460                         lex >> osffontonly_;
461                         break;
462                 case LF_OSFSCOPTION:
463                         lex >> osfscoption_;
464                         break;
465                 case LF_OT1_FONT:
466                         lex >> ot1font_;
467                         break;
468                 case LF_PACKAGE:
469                         lex >> package_;
470                         break;
471                 case LF_PACKAGEOPTIONS:
472                         lex >> packageoptions_;
473                         break;
474                 case LF_PREAMBLE:
475                         preamble_ = lex.getLongString(from_ascii("EndPreamble"));
476                         break;
477                 case LF_PROVIDES: {
478                         lex.eatLine();
479                         string features = lex.getString();
480                         provides_ = getVectorFromString(features);
481                         break;
482                 }
483                 case LF_REQUIRES:
484                         lex >> required_;
485                         break;
486                 case LF_SCALEOPTION:
487                         lex >> scaleoption_;
488                         break;
489                 case LF_SCOPTION:
490                         lex >> scoption_;
491                         break;
492                 case LF_MOREOPTS:
493                         lex >> moreopts_;
494                         break;
495                 case LF_SWITCHDEFAULT:
496                         lex >> switchdefault_;
497                         break;
498                 }
499         }
500         if (!finished) {
501                 lex.printError("No End tag found for LaTeXFont tag `$$Token'");
502                 return false;
503         }
504         lex.popTable();
505         return finished && !error;
506 }
507
508
509 bool LaTeXFont::read(Lexer & lex)
510 {
511         switchdefault_ = 0;
512         osfdefault_ = 0;
513         moreopts_ = 0;
514         osffontonly_ = 0;
515
516         if (!lex.next()) {
517                 lex.printError("No name given for LaTeX font: `$$Token'.");
518                 return false;
519         }
520
521         name_ = lex.getDocString();
522         LYXERR(Debug::INFO, "Reading LaTeX font " << name_);
523         if (!readFont(lex)) {
524                 LYXERR0("Error parsing LaTeX font `" << name_ << '\'');
525                 return false;
526         }
527
528         if (fontenc_.empty())
529                 fontenc_.push_back("T1");
530
531         return true;
532 }
533
534
535 void LaTeXFonts::readLaTeXFonts()
536 {
537         // Read latexfonts file
538         FileName filename = libFileSearch(string(), "latexfonts");
539         if (filename.empty()) {
540                 LYXERR0("Error: latexfonts file not found!");
541                 return;
542         }
543         Lexer lex;
544         lex.setFile(filename);
545         lex.setContext("LaTeXFeatures::readLaTeXFonts");
546         while (lex.isOK()) {
547                 int le = lex.lex();
548                 switch (le) {
549                 case Lexer::LEX_FEOF:
550                         continue;
551
552                 default:
553                         break;
554                 }
555                 string const type = lex.getString();
556                 if (type != "Font" && type != "AltFont") {
557                         lex.printError("Unknown LaTeXFont tag `$$Token'");
558                         continue;
559                 }
560                 LaTeXFont f;
561                 f.read(lex);
562                 if (!lex)
563                         break;
564
565                 if (type == "AltFont")
566                         texaltfontmap_[f.name()] = f;
567                 else
568                         texfontmap_[f.name()] = f;
569         }
570 }
571
572
573 LaTeXFonts::TexFontMap LaTeXFonts::getLaTeXFonts()
574 {
575         if (texfontmap_.empty())
576                 readLaTeXFonts();
577         return texfontmap_;
578 }
579
580
581 LaTeXFont LaTeXFonts::getLaTeXFont(docstring const & name)
582 {
583         if (name == "default" || name == "auto")
584                 return LaTeXFont();
585         if (texfontmap_.empty())
586                 readLaTeXFonts();
587         if (texfontmap_.find(name) == texfontmap_.end()) {
588                 LYXERR0("LaTeXFonts::getLaTeXFont: font '" << name << "' not found!");
589                 return LaTeXFont();
590         }
591         return texfontmap_[name];
592 }
593
594
595 LaTeXFont LaTeXFonts::getAltFont(docstring const & name)
596 {
597         if (name == "default" || name == "auto")
598                 return LaTeXFont();
599         if (texaltfontmap_.empty())
600                 readLaTeXFonts();
601         if (texaltfontmap_.find(name) == texaltfontmap_.end()) {
602                 LYXERR0("LaTeXFonts::getAltFont: alternative font '" << name << "' not found!");
603                 return LaTeXFont();
604         }
605         return texaltfontmap_[name];
606 }
607
608
609 } // namespace lyx