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