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