3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Jürgen Spitzmüller
8 * Full author contact details are available in file CREDITS.
13 #include "LaTeXFonts.h"
15 #include "LaTeXFeatures.h"
18 #include "frontends/alert.h"
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"
30 using namespace lyx::support;
35 LaTeXFonts latexfonts;
38 LaTeXFont LaTeXFont::altFont(docstring const & name) const
40 return theLaTeXFonts().getAltFont(name);
44 bool LaTeXFont::available(bool ot1, bool nomath) const
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())
53 else if (!required_.empty()
54 && LaTeXFeatures::isAvailable(to_ascii(required_)))
56 else if (required_.empty() && !package_.empty()
57 && LaTeXFeatures::isAvailable(to_ascii(package_)))
59 else if (!altfonts_.empty()) {
60 for (auto const & name : altfonts_) {
61 if (altFont(name).available(ot1, nomath))
69 bool LaTeXFont::providesNoMath(bool ot1, bool complete) const
71 docstring const usedfont = getUsedFont(ot1, complete, false, false);
75 else if (usedfont != name_)
76 return altFont(usedfont).providesNoMath(ot1, complete);
78 return (!nomathfont_.empty() && available(ot1, true));
82 bool LaTeXFont::providesOSF(bool ot1, bool complete, bool nomath) const
84 docstring const usedfont = getUsedFont(ot1, complete, nomath, 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))
95 return (!osfoption_.empty() || !osfscoption_.empty());
99 bool LaTeXFont::providesSC(bool ot1, bool complete, bool nomath) const
101 docstring const usedfont = getUsedFont(ot1, complete, nomath, false);
103 if (usedfont.empty())
105 else if (usedfont != name_)
106 return altFont(usedfont).providesSC(ot1, complete, nomath);
107 else if (!available(ot1, nomath))
110 return (!scoption_.empty() || !osfscoption_.empty());
114 bool LaTeXFont::hasMonolithicExpertSet(bool ot1, bool complete, bool nomath) const
116 docstring const usedfont = getUsedFont(ot1, complete, nomath, false);
118 if (usedfont.empty())
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());
127 bool LaTeXFont::providesScale(bool ot1, bool complete, bool nomath) const
129 docstring const usedfont = getUsedFont(ot1, complete, nomath, false);
131 if (usedfont.empty())
133 else if (usedfont != name_)
134 return altFont(usedfont).providesScale(ot1, complete, nomath);
135 else if (!available(ot1, nomath))
137 return (!scaleoption_.empty() || !scalecmd_.empty());
141 bool LaTeXFont::providesMoreOptions(bool ot1, bool complete, bool nomath) const
143 docstring const usedfont = getUsedFont(ot1, complete, nomath, false);
145 if (usedfont.empty())
147 else if (usedfont != name_)
148 return altFont(usedfont).providesMoreOptions(ot1, complete, nomath);
149 else if (!available(ot1, nomath))
155 bool LaTeXFont::provides(std::string const & name, bool ot1, bool complete, bool nomath) const
157 docstring const usedfont = getUsedFont(ot1, complete, nomath, false);
159 if (usedfont.empty())
161 else if (usedfont != name_)
162 return altFont(usedfont).provides(name, ot1, complete, nomath);
163 else if (provides_.empty())
166 for (auto const & provide : provides_) {
174 docstring const LaTeXFont::getUsedFont(bool ot1, bool complete, bool nomath, bool osf) const
176 if (osf && osfFontOnly())
178 else if (nomath && !nomathfont_.empty() && available(ot1, true))
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_))))
191 else if (!required_.empty()
192 && LaTeXFeatures::isAvailable(to_ascii(required_)))
194 else if (!package_.empty()
195 && LaTeXFeatures::isAvailable(to_ascii(package_)))
197 else if (!preamble_.empty() && package_.empty()
198 && required_.empty() && !switchdefault_
199 && altfonts_.empty()) {
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);
214 docstring const LaTeXFont::getUsedPackage(bool ot1, bool complete, bool nomath) const
216 docstring const usedfont = getUsedFont(ot1, complete, nomath, false);
217 if (usedfont.empty())
219 return theLaTeXFonts().getLaTeXFont(usedfont).package();
223 string const LaTeXFont::getAvailablePackage(bool dryrun) const
225 if (package_.empty())
228 string const package = to_ascii(package_);
229 if (!required_.empty() && LaTeXFeatures::isAvailable(to_ascii(required_)))
231 else if (LaTeXFeatures::isAvailable(package))
233 // Output unavailable packages in source preview
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);
247 string const LaTeXFont::getPackageOptions(bool ot1, bool complete, bool sc, bool osf,
248 int scale, string const & extraopts, bool nomath) const
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);
256 if (!packageoptions_.empty())
257 os << to_ascii(packageoptions_);
259 if (sc && needosfopt && has_osf && has_sc) {
260 if (!os.str().empty())
262 if (!osfscoption_.empty())
263 os << to_ascii(osfscoption_);
265 os << to_ascii(osfoption_)
266 << ',' << to_ascii(scoption_);
267 } else if (needosfopt && has_osf) {
268 if (!os.str().empty())
270 os << to_ascii(osfoption_);
271 } else if (sc && has_sc) {
272 if (!os.str().empty())
274 os << to_ascii(scoption_);
277 if (scale != 100 && !scaleoption_.empty()
278 && providesScale(ot1, complete, nomath)) {
279 if (!os.str().empty())
281 os << subst(to_ascii(scaleoption_), "$$val",
282 convert<std::string>(float(scale) / 100));
285 if (moreopts && !extraopts.empty()) {
286 if (!os.str().empty())
294 string const LaTeXFont::getLaTeXCode(bool dryrun, bool ot1, bool complete, bool sc,
295 bool osf, bool nomath, string const & extraopts, int scale) const
299 docstring const usedfont = getUsedFont(ot1, complete, nomath, osf);
300 if (usedfont.empty())
302 else if (usedfont != name_)
303 return altFont(usedfont).getLaTeXCode(dryrun, ot1, complete, sc,
304 osf, nomath, extraopts, scale);
306 if (switchdefault_) {
307 if (family_.empty()) {
308 LYXERR0("Error: Font `" << name_ << "' has no family defined!");
311 if (available(ot1, nomath) || dryrun)
312 os << "\\renewcommand{\\" << to_ascii(family_) << "default}{"
313 << to_ascii(name_) << "}\n";
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);
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";
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";
341 if (!preamble_.empty())
342 os << to_utf8(preamble_);
348 bool LaTeXFont::hasFontenc(string const & name) const
350 for (auto const & fe : fontenc_) {
358 bool LaTeXFont::readFont(Lexer & lex)
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 }
414 bool finished = false;
415 lex.pushTable(latexFontTags);
416 // parse style section
417 while (!finished && lex.isOK() && !error) {
419 // See comment in LyXRC.cpp.
421 case Lexer::LEX_FEOF:
424 case Lexer::LEX_UNDEF: // parse error
425 lex.printError("Unknown LaTeXFont tag `$$Token'");
432 switch (static_cast<LaTeXFontTags>(le)) {
433 case LF_END: // end of structure
438 docstring altp = lex.getDocString();
439 altfonts_ = getVectorFromString(altp);
442 case LF_COMPLETE_FONT:
443 lex >> completefont_;
453 string fe = lex.getString();
454 fontenc_ = getVectorFromString(fe);
481 case LF_PACKAGEOPTIONS:
482 lex >> packageoptions_;
485 preamble_ = lex.getLongString(from_ascii("EndPreamble"));
489 string features = lex.getString();
490 provides_ = getVectorFromString(features);
508 case LF_SWITCHDEFAULT:
509 lex >> switchdefault_;
514 lex.printError("No End tag found for LaTeXFont tag `$$Token'");
518 return finished && !error;
522 bool LaTeXFont::read(Lexer & lex)
524 switchdefault_ = false;
527 osffontonly_ = false;
530 lex.printError("No name given for LaTeX font: `$$Token'.");
534 name_ = lex.getDocString();
535 LYXERR(Debug::INFO, "Reading LaTeX font " << name_);
536 if (!readFont(lex)) {
537 LYXERR0("Error parsing LaTeX font `" << name_ << '\'');
541 if (fontenc_.empty())
542 fontenc_.push_back("T1");
548 void LaTeXFonts::readLaTeXFonts()
550 // Read latexfonts file
551 FileName filename = libFileSearch(string(), "latexfonts");
552 if (filename.empty()) {
553 LYXERR0("Error: latexfonts file not found!");
557 lex.setFile(filename);
558 lex.setContext("LaTeXFeatures::readLaTeXFonts");
562 case Lexer::LEX_FEOF:
568 string const type = lex.getString();
569 if (type != "Font" && type != "AltFont") {
570 lex.printError("Unknown LaTeXFont tag `$$Token'");
578 if (type == "AltFont")
579 texaltfontmap_[f.name()] = f;
581 texfontmap_[f.name()] = f;
586 LaTeXFonts::TexFontMap LaTeXFonts::getLaTeXFonts()
588 if (texfontmap_.empty())
594 LaTeXFont LaTeXFonts::getLaTeXFont(docstring const & name)
596 if (name == "default" || name == "auto")
598 if (texfontmap_.empty())
600 if (texfontmap_.find(name) == texfontmap_.end()) {
601 LYXERR0("LaTeXFonts::getLaTeXFont: font '" << name << "' not found!");
604 return texfontmap_[name];
608 LaTeXFont LaTeXFonts::getAltFont(docstring const & name)
610 if (name == "default" || name == "auto")
612 if (texaltfontmap_.empty())
614 if (texaltfontmap_.find(name) == texaltfontmap_.end()) {
615 LYXERR0("LaTeXFonts::getAltFont: alternative font '" << name << "' not found!");
618 return texaltfontmap_[name];