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