]> git.lyx.org Git - lyx.git/blob - src/LaTeXFonts.cpp
Avoid full metrics computation with Update:FitCursor
[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
17 #include "frontends/alert.h"
18
19 #include "support/convert.h"
20 #include "support/debug.h"
21 #include "support/docstream.h"
22 #include "support/FileName.h"
23 #include "support/filetools.h"
24 #include "support/gettext.h"
25 #include "support/Lexer.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) const
39 {
40         return theLaTeXFonts().getAltFont(name);
41 }
42
43
44 bool LaTeXFont::available(bool ot1, bool nomath) const
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 (auto const & name : altfonts_) {
61                         if (altFont(name).available(ot1, nomath))
62                                 return true;
63                 }
64         }
65         return false;
66 }
67
68
69 bool LaTeXFont::providesNoMath(bool ot1, bool complete) const
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) const
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) const
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) const
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) const
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) const
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) const
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 (auto const & provide : provides_) {
167                 if (provide == name)
168                         return true;
169         }
170         return false;
171 }
172
173
174 docstring const LaTeXFont::getUsedFont(bool ot1, bool complete, bool nomath, bool osf) const
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 (auto const & name : altfonts_) {
204                         LaTeXFont altf = altFont(name);
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) const
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) const
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) const
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, int scale) const
296 {
297         ostringstream os;
298
299         docstring const usedfont = getUsedFont(ot1, complete, nomath, osf);
300         if (usedfont.empty())
301                 return string();
302         else if (usedfont != name_)
303                 return altFont(usedfont).getLaTeXCode(dryrun, ot1, complete, sc,
304                                                       osf, nomath, extraopts, scale);
305
306         if (switchdefault_) {
307                 if (family_.empty()) {
308                         LYXERR0("Error: Font `" << name_ << "' has no family defined!");
309                         return string();
310                 }
311                 if (available(ot1, nomath) || dryrun)
312                         os << "\\renewcommand{\\" << to_ascii(family_) << "default}{"
313                            << to_ascii(name_) << "}\n";
314                 else
315                         frontend::Alert::warning(_("Font not available"),
316                                         bformat(_("The LaTeX package `%1$s' needed for the font `%2$s'\n"
317                                                   "is not available on your system. LyX will fall back to the default font."),
318                                                 required_, guiname_), true);
319         } else {
320                 string const package =
321                         getAvailablePackage(dryrun);
322                 string const packageopts = getPackageOptions(ot1, complete, sc, osf, scale, extraopts, nomath);
323                 if (packageopts.empty() && !package.empty())
324                         os << "\\usepackage{" << package << "}\n";
325                 else if (!packageopts.empty() && !package.empty())
326                         os << "\\usepackage[" << packageopts << "]{" << package << "}\n";
327         }
328         if (osf && providesOSF(ot1, complete, nomath) && !osffont_.empty())
329                 os << altFont(osffont_).getLaTeXCode(dryrun, ot1, complete, sc, osf,
330                                                      nomath, extraopts, scale);
331         if (scale != 100 && !scalecmd_.empty()
332             && providesScale(ot1, complete, nomath)) {
333                 if (contains(scalecmd_, '@'))
334                         os << "\\makeatletter\n";
335                 os << subst(to_ascii(scalecmd_), "$$val",
336                             convert<std::string>(float(scale) / 100)) << '\n';
337                 if (contains(scalecmd_, '@'))
338                         os << "\\makeatother\n";
339         }
340
341         if (!preamble_.empty())
342                 os << to_utf8(preamble_);
343
344         return os.str();
345 }
346
347
348 bool LaTeXFont::hasFontenc(string const & name) const
349 {
350         for (auto const & fe : fontenc_) {
351                 if (fe == name)
352                         return true;
353         }
354         return false;
355 }
356
357
358 bool LaTeXFont::readFont(Lexer & lex)
359 {
360         enum LaTeXFontTags {
361                 LF_ALT_FONTS = 1,
362                 LF_COMPLETE_FONT,
363                 LF_END,
364                 LF_FAMILY,
365                 LF_FONTENC,
366                 LF_GUINAME,
367                 LF_NOMATHFONT,
368                 LF_OSFDEFAULT,
369                 LF_OSFFONT,
370                 LF_OSFFONTONLY,
371                 LF_OSFOPTION,
372                 LF_OSFSCOPTION,
373                 LF_OT1_FONT,
374                 LF_MOREOPTS,
375                 LF_PACKAGE,
376                 LF_PACKAGEOPTIONS,
377                 LF_PREAMBLE,
378                 LF_PROVIDES,
379                 LF_REQUIRES,
380                 LF_SCALECMD,
381                 LF_SCALEOPTION,
382                 LF_SCOPTION,
383                 LF_SWITCHDEFAULT
384         };
385
386         // Keep these sorted alphabetically!
387         LexerKeyword latexFontTags[] = {
388                 { "altfonts",             LF_ALT_FONTS },
389                 { "completefont",         LF_COMPLETE_FONT },
390                 { "endfont",              LF_END },
391                 { "family",               LF_FAMILY },
392                 { "fontencoding",         LF_FONTENC },
393                 { "guiname",              LF_GUINAME },
394                 { "moreoptions",          LF_MOREOPTS },
395                 { "nomathfont",           LF_NOMATHFONT },
396                 { "osfdefault",           LF_OSFDEFAULT },
397                 { "osffont",              LF_OSFFONT },
398                 { "osffontonly",          LF_OSFFONTONLY },
399                 { "osfoption",            LF_OSFOPTION },
400                 { "osfscoption",          LF_OSFSCOPTION },
401                 { "ot1font",              LF_OT1_FONT },
402                 { "package",              LF_PACKAGE },
403                 { "packageoptions",       LF_PACKAGEOPTIONS },
404                 { "preamble",             LF_PREAMBLE },
405                 { "provides",             LF_PROVIDES },
406                 { "requires",             LF_REQUIRES },
407                 { "scalecommand",         LF_SCALECMD },
408                 { "scaleoption",          LF_SCALEOPTION },
409                 { "scoption",             LF_SCOPTION },
410                 { "switchdefault",        LF_SWITCHDEFAULT }
411         };
412
413         bool error = false;
414         bool finished = false;
415         lex.pushTable(latexFontTags);
416         // parse style section
417         while (!finished && lex.isOK() && !error) {
418                 int le = lex.lex();
419                 // See comment in LyXRC.cpp.
420                 switch (le) {
421                 case Lexer::LEX_FEOF:
422                         continue;
423
424                 case Lexer::LEX_UNDEF: // parse error
425                         lex.printError("Unknown LaTeXFont tag `$$Token'");
426                         error = true;
427                         continue;
428
429                 default:
430                         break;
431                 }
432                 switch (static_cast<LaTeXFontTags>(le)) {
433                 case LF_END: // end of structure
434                         finished = true;
435                         break;
436                 case LF_ALT_FONTS: {
437                         lex.eatLine();
438                         docstring altp = lex.getDocString();
439                         altfonts_ = getVectorFromString(altp);
440                         break;
441                 }
442                 case LF_COMPLETE_FONT:
443                         lex >> completefont_;
444                         break;
445                 case LF_FAMILY:
446                         lex >> family_;
447                         break;
448                 case LF_GUINAME:
449                         lex >> guiname_;
450                         break;
451                 case LF_FONTENC: {
452                         lex.eatLine();
453                         string fe = lex.getString();
454                         fontenc_ = getVectorFromString(fe);
455                         break;
456                 }
457                 case LF_NOMATHFONT:
458                         lex >> nomathfont_;
459                         break;
460                 case LF_OSFOPTION:
461                         lex >> osfoption_;
462                         break;
463                 case LF_OSFFONT:
464                         lex >> osffont_;
465                         break;
466                 case LF_OSFDEFAULT:
467                         lex >> osfdefault_;
468                         break;
469                 case LF_OSFFONTONLY:
470                         lex >> osffontonly_;
471                         break;
472                 case LF_OSFSCOPTION:
473                         lex >> osfscoption_;
474                         break;
475                 case LF_OT1_FONT:
476                         lex >> ot1font_;
477                         break;
478                 case LF_PACKAGE:
479                         lex >> package_;
480                         break;
481                 case LF_PACKAGEOPTIONS:
482                         lex >> packageoptions_;
483                         break;
484                 case LF_PREAMBLE:
485                         preamble_ = lex.getLongString(from_ascii("EndPreamble"));
486                         break;
487                 case LF_PROVIDES: {
488                         lex.eatLine();
489                         string features = lex.getString();
490                         provides_ = getVectorFromString(features);
491                         break;
492                 }
493                 case LF_REQUIRES:
494                         lex >> required_;
495                         break;
496                 case LF_SCALECMD:
497                         lex >> scalecmd_;
498                         break;
499                 case LF_SCALEOPTION:
500                         lex >> scaleoption_;
501                         break;
502                 case LF_SCOPTION:
503                         lex >> scoption_;
504                         break;
505                 case LF_MOREOPTS:
506                         lex >> moreopts_;
507                         break;
508                 case LF_SWITCHDEFAULT:
509                         lex >> switchdefault_;
510                         break;
511                 }
512         }
513         if (!finished) {
514                 lex.printError("No End tag found for LaTeXFont tag `$$Token'");
515                 return false;
516         }
517         lex.popTable();
518         return finished && !error;
519 }
520
521
522 bool LaTeXFont::read(Lexer & lex)
523 {
524         switchdefault_ = false;
525         osfdefault_ = false;
526         moreopts_ = false;
527         osffontonly_ = false;
528
529         if (!lex.next()) {
530                 lex.printError("No name given for LaTeX font: `$$Token'.");
531                 return false;
532         }
533
534         name_ = lex.getDocString();
535         LYXERR(Debug::INFO, "Reading LaTeX font " << name_);
536         if (!readFont(lex)) {
537                 LYXERR0("Error parsing LaTeX font `" << name_ << '\'');
538                 return false;
539         }
540
541         if (fontenc_.empty())
542                 fontenc_.push_back("T1");
543
544         return true;
545 }
546
547
548 void LaTeXFonts::readLaTeXFonts()
549 {
550         // Read latexfonts file
551         FileName filename = libFileSearch(string(), "latexfonts");
552         if (filename.empty()) {
553                 LYXERR0("Error: latexfonts file not found!");
554                 return;
555         }
556         Lexer lex;
557         lex.setFile(filename);
558         lex.setContext("LaTeXFeatures::readLaTeXFonts");
559         while (lex.isOK()) {
560                 int le = lex.lex();
561                 switch (le) {
562                 case Lexer::LEX_FEOF:
563                         continue;
564
565                 default:
566                         break;
567                 }
568                 string const type = lex.getString();
569                 if (type != "Font" && type != "AltFont") {
570                         lex.printError("Unknown LaTeXFont tag `$$Token'");
571                         continue;
572                 }
573                 LaTeXFont f;
574                 f.read(lex);
575                 if (!lex)
576                         break;
577
578                 if (type == "AltFont")
579                         texaltfontmap_[f.name()] = f;
580                 else
581                         texfontmap_[f.name()] = f;
582         }
583 }
584
585
586 LaTeXFonts::TexFontMap LaTeXFonts::getLaTeXFonts()
587 {
588         if (texfontmap_.empty())
589                 readLaTeXFonts();
590         return texfontmap_;
591 }
592
593
594 LaTeXFont LaTeXFonts::getLaTeXFont(docstring const & name)
595 {
596         if (name == "default" || name == "auto")
597                 return LaTeXFont();
598         if (texfontmap_.empty())
599                 readLaTeXFonts();
600         if (texfontmap_.find(name) == texfontmap_.end()) {
601                 LYXERR0("LaTeXFonts::getLaTeXFont: font '" << name << "' not found!");
602                 return LaTeXFont();
603         }
604         return texfontmap_[name];
605 }
606
607
608 LaTeXFont LaTeXFonts::getAltFont(docstring const & name)
609 {
610         if (name == "default" || name == "auto")
611                 return LaTeXFont();
612         if (texaltfontmap_.empty())
613                 readLaTeXFonts();
614         if (texaltfontmap_.find(name) == texaltfontmap_.end()) {
615                 LYXERR0("LaTeXFonts::getAltFont: alternative font '" << name << "' not found!");
616                 return LaTeXFont();
617         }
618         return texaltfontmap_[name];
619 }
620
621
622 } // namespace lyx