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