]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiFontLoader.cpp
Added "cancel" to the GUI handled list of LaTeX packages.
[lyx.git] / src / frontends / qt4 / GuiFontLoader.cpp
1 /**
2  * \file FontLoader.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Asger Alstrup
7  * \author John Levon
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "FontLoader.h"
15
16 #include "FontInfo.h"
17 #include "GuiFontMetrics.h"
18 #include "qt_helpers.h"
19
20 #include "LyXRC.h"
21
22 #include "support/convert.h"
23 #include "support/debug.h"
24 #include "support/filetools.h"
25 #include "support/lstrings.h"
26 #include "support/Systemcall.h"
27 #include "support/Package.h"
28 #include "support/os.h"
29
30 #include <QFontInfo>
31 #include <QFontDatabase>
32
33 #include "support/lassert.h"
34
35 using namespace std;
36 using namespace lyx::support;
37
38 QString const math_fonts[] = {"cmex10", "cmmi10", "cmr10", "cmsy10",
39         "esint10", "eufm10", "msam10", "msbm10", "rsfs10", "stmary10",
40         "wasy10"};
41 int const num_math_fonts = sizeof(math_fonts) / sizeof(*math_fonts);
42
43 namespace lyx {
44
45 extern docstring const stateText(FontInfo const & f);
46
47 namespace frontend {
48
49 /**
50  * Matches Fonts against
51  * actual QFont instances, and also caches metrics.
52  */
53 class GuiFontInfo
54 {
55 public:
56         GuiFontInfo(FontInfo const & f);
57
58         /// The font instance
59         QFont font;
60         /// Metrics on the font
61         GuiFontMetrics metrics;
62 };
63
64 namespace {
65
66 struct SymbolFont {
67         FontFamily lyx_family;
68         QString family;
69         QString xlfd;
70 };
71
72 SymbolFont symbol_fonts[] = {
73         { SYMBOL_FAMILY,"symbol", "-*-symbol-*-*-*-*-*-*-*-*-*-*-adobe-fontspecific"},
74         { CMR_FAMILY,   "cmr10",  "-*-cmr10-medium-*-*-*-*-*-*-*-*-*-*-*" },
75         { CMSY_FAMILY,  "cmsy10", "-*-cmsy10-*-*-*-*-*-*-*-*-*-*-*-*" },
76         { CMM_FAMILY,   "cmmi10", "-*-cmmi10-medium-*-*-*-*-*-*-*-*-*-*-*" },
77         { CMEX_FAMILY,  "cmex10", "-*-cmex10-*-*-*-*-*-*-*-*-*-*-*-*" },
78         { MSA_FAMILY,   "msam10", "-*-msam10-*-*-*-*-*-*-*-*-*-*-*-*" },
79         { MSB_FAMILY,   "msbm10", "-*-msbm10-*-*-*-*-*-*-*-*-*-*-*-*" },
80         { EUFRAK_FAMILY,"eufm10", "-*-eufm10-medium-*-*-*-*-*-*-*-*-*-*-*" },
81         { RSFS_FAMILY,  "rsfs10", "-*-rsfs10-medium-*-*-*-*-*-*-*-*-*-*-*" },
82         { STMARY_FAMILY,"stmary10","-*-stmary10-medium-*-*-*-*-*-*-*-*-*-*-*" },
83         { WASY_FAMILY,  "wasy10", "-*-wasy10-medium-*-*-*-*-*-*-*-*-*-*-*" },
84         { ESINT_FAMILY, "esint10","-*-esint10-medium-*-*-*-*-*-*-*-*-*-*-*" }
85 };
86
87 size_t const nr_symbol_fonts = sizeof(symbol_fonts) / sizeof(symbol_fonts[0]);
88
89 /// BUTT ugly !
90 static GuiFontInfo * fontinfo_[NUM_FAMILIES][NUM_SERIES][NUM_SHAPE][NUM_SIZE];
91
92
93 /// Get font info (font + metrics) for the given LyX font.
94 // if not cached, create it.
95 GuiFontInfo & fontinfo(FontInfo const & f)
96 {
97         LASSERT(f.family() < NUM_FAMILIES, /**/);
98         LASSERT(f.series() < NUM_SERIES, /**/);
99         LASSERT(f.realShape() < NUM_SHAPE, /**/);
100         LASSERT(f.size() < NUM_SIZE, /**/);
101         // fi is a reference to the pointer type (GuiFontInfo *) in the
102         // fontinfo_ table.
103         GuiFontInfo * & fi =
104                 fontinfo_[f.family()][f.series()][f.realShape()][f.size()];
105         if (!fi)
106                 fi = new GuiFontInfo(f);
107         return *fi;
108 }
109
110
111 QString rawName(QString const & family)
112 {
113         for (size_t i = 0; i < nr_symbol_fonts; ++i)
114                 if (family == symbol_fonts[i].family)
115                         return symbol_fonts[i].xlfd;
116
117         LYXERR(Debug::FONT, "BUG: family not found !");
118         return QString();
119 }
120
121
122 QString symbolFamily(FontFamily family)
123 {
124         for (size_t i = 0; i < nr_symbol_fonts; ++i) {
125                 if (family == symbol_fonts[i].lyx_family)
126                         return symbol_fonts[i].family;
127         }
128         return QString();
129 }
130
131
132 bool isSymbolFamily(FontFamily family)
133 {
134         return family >= SYMBOL_FAMILY && family <= ESINT_FAMILY;
135 }
136
137
138 static bool isChosenFont(QFont & font, QString const & family)
139 {
140         // QFontInfo won't find a font that has only a few glyphs at unusual
141         // positions, e.g. the original esint10 font.
142         // The workaround is to add dummy glyphs at least at all ASCII
143         // positions.
144         QFontInfo fi(font);
145
146         LYXERR(Debug::FONT, "got: " << fi.family());
147
148         if (fi.family().contains(family)) {
149                 LYXERR(Debug::FONT, " got it ");
150                 return true;
151         }
152
153         return false;
154 }
155
156
157 QFont symbolFont(QString const & family, bool * ok)
158 {
159         LYXERR(Debug::FONT, "Looking for font family " << family << " ... ");
160         QString upper = family;
161         upper[0] = family[0].toUpper();
162
163         QFont font;
164         font.setKerning(false);
165         font.setFamily(family);
166
167         if (isChosenFont(font, family)) {
168                 LYXERR(Debug::FONT, "normal!");
169                 *ok = true;
170                 return font;
171         }
172
173         LYXERR(Debug::FONT, "Trying " << upper << " ... ");
174         font.setFamily(upper);
175
176         if (isChosenFont(font, upper)) {
177                 LYXERR(Debug::FONT, "upper!");
178                 *ok = true;
179                 return font;
180         }
181
182         // A simple setFamily() fails on Qt 2
183
184         QString const raw = rawName(family);
185         LYXERR(Debug::FONT, "Trying " << raw << " ... ");
186         font.setRawName(raw);
187
188         if (isChosenFont(font, family)) {
189                 LYXERR(Debug::FONT, "raw version!");
190                 *ok = true;
191                 return font;
192         }
193
194         LYXERR(Debug::FONT, " FAILED :-(");
195         *ok = false;
196         return font;
197 }
198
199 } // namespace anon
200
201
202 FontLoader::FontLoader()
203 {
204         QString const fonts_dir =
205                 toqstr(addPath(package().system_support().absFileName(), "fonts"));
206
207         for (int i = 0 ; i < num_math_fonts; ++i) {
208                 QString const font_file = fonts_dir + '/' + math_fonts[i] + ".ttf";
209                 int fontID = QFontDatabase::addApplicationFont(font_file);
210
211                 LYXERR(Debug::FONT, "Adding font " << font_file
212                                     << (fontID < 0 ? " FAIL" : " OK"));
213         }
214
215         for (int i1 = 0; i1 < NUM_FAMILIES; ++i1)
216                 for (int i2 = 0; i2 < NUM_SERIES; ++i2)
217                         for (int i3 = 0; i3 < NUM_SHAPE; ++i3)
218                                 for (int i4 = 0; i4 < NUM_SIZE; ++i4)
219                                         fontinfo_[i1][i2][i3][i4] = 0;
220 }
221
222
223 void FontLoader::update()
224 {
225         for (int i1 = 0; i1 < NUM_FAMILIES; ++i1)
226                 for (int i2 = 0; i2 < NUM_SERIES; ++i2)
227                         for (int i3 = 0; i3 < NUM_SHAPE; ++i3)
228                                 for (int i4 = 0; i4 < NUM_SIZE; ++i4) {
229                                         delete fontinfo_[i1][i2][i3][i4];
230                                         fontinfo_[i1][i2][i3][i4] = 0;
231                                 }
232 }
233
234
235 FontLoader::~FontLoader()
236 {
237         update();
238 }
239
240 /////////////////////////////////////////////////
241
242
243 static QString makeFontName(QString const & family, QString const & foundry)
244 {
245         QString res = family;
246         if (!foundry.isEmpty())
247                 res += " [" + foundry + ']';
248         return res;
249 }
250
251
252 GuiFontInfo::GuiFontInfo(FontInfo const & f)
253         : metrics(QFont())
254 {
255         font.setKerning(false);
256         QString const pat = symbolFamily(f.family());
257         if (!pat.isEmpty()) {
258                 bool ok;
259                 font = symbolFont(pat, &ok);
260         } else {
261                 switch (f.family()) {
262                 case ROMAN_FAMILY: {
263                         QString family = makeFontName(toqstr(lyxrc.roman_font_name),
264                                 toqstr(lyxrc.roman_font_foundry)); 
265                         font.setFamily(family);
266 #ifdef Q_WS_MACX
267 #if QT_VERSION >= 0x040300 //&& QT_VERSION < 0x040800
268                         // Workaround for a Qt bug, see http://www.lyx.org/trac/ticket/3684
269                         // and http://bugreports.qt.nokia.com/browse/QTBUG-11145.
270                         // FIXME: Check whether this is really fixed in Qt 4.8
271                         if (family == "Times" && !font.exactMatch())
272                                 font.setFamily("Times New Roman");
273 #endif
274 #endif
275                         break;
276                 }
277                 case SANS_FAMILY:
278                         font.setFamily(makeFontName(toqstr(lyxrc.sans_font_name),
279                                                     toqstr(lyxrc.sans_font_foundry)));
280                         break;
281                 case TYPEWRITER_FAMILY:
282                         font.setFamily(makeFontName(toqstr(lyxrc.typewriter_font_name),
283                                                     toqstr(lyxrc.typewriter_font_foundry)));
284                         break;
285                 default:
286                         break;
287                 }
288         }
289
290         switch (f.series()) {
291                 case MEDIUM_SERIES:
292                         font.setWeight(QFont::Normal);
293                         break;
294                 case BOLD_SERIES:
295                         font.setWeight(QFont::Bold);
296                         break;
297                 default:
298                         break;
299         }
300
301         switch (f.realShape()) {
302                 case ITALIC_SHAPE:
303                 case SLANTED_SHAPE:
304                         font.setItalic(true);
305                         break;
306                 default:
307                         break;
308         }
309
310         LYXERR(Debug::FONT, "Font '" << stateText(f)
311                 << "' matched by\n" << font.family());
312
313         // Is this an exact match?
314         if (font.exactMatch())
315                 LYXERR(Debug::FONT, "This font is an exact match");
316         else
317                 LYXERR(Debug::FONT, "This font is NOT an exact match");
318
319         LYXERR(Debug::FONT, "XFLD: " << font.rawName());
320
321         font.setPointSizeF(convert<double>(lyxrc.font_sizes[f.size()])
322                                * lyxrc.zoom / 100.0);
323
324         LYXERR(Debug::FONT, "The font has size: " << font.pointSizeF());
325
326         if (f.realShape() != SMALLCAPS_SHAPE) {
327                 metrics = GuiFontMetrics(font);
328         } else {
329                 // handle small caps ourselves ...
330                 FontInfo smallfont = f;
331                 smallfont.decSize().decSize().setShape(UP_SHAPE);
332                 QFont font2(font);
333                 font2.setKerning(false);
334                 font2.setPointSizeF(convert<double>(lyxrc.font_sizes[smallfont.size()])
335                                * lyxrc.zoom / 100.0);
336
337                 metrics = GuiFontMetrics(font, font2);
338         }
339 }
340
341
342 bool FontLoader::available(FontInfo const & f)
343 {
344         static vector<int> cache_set(NUM_FAMILIES, false);
345         static vector<int> cache(NUM_FAMILIES, false);
346
347         FontFamily family = f.family();
348         if (cache_set[family])
349                 return cache[family];
350         cache_set[family] = true;
351
352         QString const pat = symbolFamily(family);
353         if (pat.isEmpty())
354                 // We don't care about non-symbol fonts
355                 return false;
356
357         bool ok;
358         symbolFont(pat, &ok);
359         if (!ok)
360                 return false;
361
362         cache[family] = true;
363         return true;
364 }
365
366
367 FontMetrics const & FontLoader::metrics(FontInfo const & f)
368 {
369         return fontinfo(f).metrics;
370 }
371
372
373 GuiFontMetrics const & getFontMetrics(FontInfo const & f)
374 {
375         return fontinfo(f).metrics;
376 }
377
378
379 QFont const & getFont(FontInfo const & f)
380 {
381         return fontinfo(f).font;
382 }
383
384 } // namespace frontend
385 } // namespace lyx