2 * \file src/FontInfo.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
7 * \author Jean-Marc Lasgouttes
8 * \author Angus Leeming
12 * Full author contact details are available in file CREDITS.
22 #include "support/convert.h"
23 #include "support/debug.h"
24 #include "support/docstring.h"
25 #include "support/lstrings.h"
26 #include "support/RefChanger.h"
33 using namespace lyx::support;
38 // Strings used to read and write .lyx format files
40 char const * LyXFamilyNames[NUM_FAMILIES + 2 /* default & error */] =
41 { "roman", "sans", "typewriter", "symbol",
42 "cmr", "cmsy", "cmm", "cmex", "msa", "msb", "eufrak", "rsfs", "stmry",
43 "wasy", "esint", "default", "error" };
45 char const * LyXSeriesNames[NUM_SERIES + 2 /* default & error */] =
46 { "medium", "bold", "default", "error" };
48 char const * LyXShapeNames[NUM_SHAPE + 2 /* default & error */] =
49 { "up", "italic", "slanted", "smallcaps", "default", "error" };
51 char const * LyXSizeNames[NUM_SIZE + 4 /* increase, decrease, default & error */] =
52 { "tiny", "scriptsize", "footnotesize", "small", "normal", "large",
53 "larger", "largest", "huge", "giant",
54 "increase", "decrease", "default", "error" };
56 char const * LyXMiscNames[5] =
57 { "off", "on", "toggle", "default", "error" };
60 FontInfo const sane_font(
78 FontInfo const inherit_font(
96 FontInfo const ignore_font(
121 /// Decreases font size_ by one
122 FontInfo & FontInfo::decSize()
125 case FONT_SIZE_HUGER: size_ = FONT_SIZE_HUGE; break;
126 case FONT_SIZE_HUGE: size_ = FONT_SIZE_LARGEST; break;
127 case FONT_SIZE_LARGEST: size_ = FONT_SIZE_LARGER; break;
128 case FONT_SIZE_LARGER: size_ = FONT_SIZE_LARGE; break;
129 case FONT_SIZE_LARGE: size_ = FONT_SIZE_NORMAL; break;
130 case FONT_SIZE_NORMAL: size_ = FONT_SIZE_SMALL; break;
131 case FONT_SIZE_SMALL: size_ = FONT_SIZE_FOOTNOTE; break;
132 case FONT_SIZE_FOOTNOTE: size_ = FONT_SIZE_SCRIPT; break;
133 case FONT_SIZE_SCRIPT: size_ = FONT_SIZE_TINY; break;
134 case FONT_SIZE_TINY: break;
135 case FONT_SIZE_INCREASE:
136 LYXERR0("Can't FontInfo::decSize on FONT_SIZE_INCREASE");
138 case FONT_SIZE_DECREASE:
139 LYXERR0("Can't FontInfo::decSize on FONT_SIZE_DECREASE");
141 case FONT_SIZE_INHERIT:
142 LYXERR0("Can't FontInfo::decSize on FONT_SIZE_INHERIT");
144 case FONT_SIZE_IGNORE:
145 LYXERR0("Can't FontInfo::decSize on FONT_SIZE_IGNORE");
152 /// Increases font size_ by one
153 FontInfo & FontInfo::incSize()
156 case FONT_SIZE_HUGER: break;
157 case FONT_SIZE_HUGE: size_ = FONT_SIZE_HUGER; break;
158 case FONT_SIZE_LARGEST: size_ = FONT_SIZE_HUGE; break;
159 case FONT_SIZE_LARGER: size_ = FONT_SIZE_LARGEST; break;
160 case FONT_SIZE_LARGE: size_ = FONT_SIZE_LARGER; break;
161 case FONT_SIZE_NORMAL: size_ = FONT_SIZE_LARGE; break;
162 case FONT_SIZE_SMALL: size_ = FONT_SIZE_NORMAL; break;
163 case FONT_SIZE_FOOTNOTE: size_ = FONT_SIZE_SMALL; break;
164 case FONT_SIZE_SCRIPT: size_ = FONT_SIZE_FOOTNOTE; break;
165 case FONT_SIZE_TINY: size_ = FONT_SIZE_SCRIPT; break;
166 case FONT_SIZE_INCREASE:
167 LYXERR0("Can't FontInfo::incSize on FONT_SIZE_INCREASE");
169 case FONT_SIZE_DECREASE:
170 LYXERR0("Can't FontInfo::incSize on FONT_SIZE_DECREASE");
172 case FONT_SIZE_INHERIT:
173 LYXERR0("Can't FontInfo::incSize on FONT_SIZE_INHERIT");
175 case FONT_SIZE_IGNORE:
176 LYXERR0("Can't FontInfo::incSize on FONT_SIZE_IGNORE");
183 double FontInfo::realSize() const
185 double d = convert<double>(lyxrc.font_sizes[size()]);
186 // The following is according to the average of the values in the
187 // definitions of \defaultscriptratio and \defaultscriptscriptratio in LaTeX
188 // font packages. No attempt is made to implement the actual values from
199 case LM_ST_SCRIPTSCRIPT:
203 // Never go below the smallest size
204 return max(d, convert<double>(lyxrc.font_sizes[FONT_SIZE_TINY]));
208 /// Reduce font to fall back to template where possible
209 void FontInfo::reduce(FontInfo const & tmplt)
211 if (family_ == tmplt.family_)
212 family_ = INHERIT_FAMILY;
213 if (series_ == tmplt.series_)
214 series_ = INHERIT_SERIES;
215 if (shape_ == tmplt.shape_)
216 shape_ = INHERIT_SHAPE;
217 if (size_ == tmplt.size_)
218 size_ = FONT_SIZE_INHERIT;
219 if (style_ == tmplt.style_)
220 style_ = LM_ST_INHERIT;
221 if (emph_ == tmplt.emph_)
222 emph_ = FONT_INHERIT;
223 if (underbar_ == tmplt.underbar_)
224 underbar_ = FONT_INHERIT;
225 if (strikeout_ == tmplt.strikeout_)
226 strikeout_ = FONT_INHERIT;
227 if (xout_ == tmplt.xout_)
228 xout_ = FONT_INHERIT;
229 if (uuline_ == tmplt.uuline_)
230 uuline_ = FONT_INHERIT;
231 if (uwave_ == tmplt.uwave_)
232 uwave_ = FONT_INHERIT;
233 if (noun_ == tmplt.noun_)
234 noun_ = FONT_INHERIT;
235 if (color_ == tmplt.color_)
236 color_ = Color_inherit;
237 if (background_ == tmplt.background_)
238 background_ = Color_inherit;
239 if (nospellcheck_ == tmplt.nospellcheck_)
240 nospellcheck_ = FONT_INHERIT;
244 /// Realize font from a template
245 FontInfo & FontInfo::realize(FontInfo const & tmplt)
247 if ((*this) == inherit_font) {
252 if (family_ == INHERIT_FAMILY)
253 family_ = tmplt.family_;
255 if (series_ == INHERIT_SERIES)
256 series_ = tmplt.series_;
258 if (shape_ == INHERIT_SHAPE)
259 shape_ = tmplt.shape_;
261 if (size_ == FONT_SIZE_INHERIT)
264 if (style_ == LM_ST_INHERIT)
265 style_ = tmplt.style_;
267 if (emph_ == FONT_INHERIT)
270 if (underbar_ == FONT_INHERIT)
271 underbar_ = tmplt.underbar_;
273 if (strikeout_ == FONT_INHERIT)
274 strikeout_ = tmplt.strikeout_;
276 if (xout_ == FONT_INHERIT)
279 if (uuline_ == FONT_INHERIT)
280 uuline_ = tmplt.uuline_;
282 if (uwave_ == FONT_INHERIT)
283 uwave_ = tmplt.uwave_;
285 if (noun_ == FONT_INHERIT)
288 if (color_ == Color_inherit)
289 color_ = tmplt.color_;
291 if (background_ == Color_inherit)
292 background_ = tmplt.background_;
294 if (nospellcheck_ == FONT_INHERIT)
295 nospellcheck_ = tmplt.nospellcheck_;
301 Changer FontInfo::changeColor(ColorCode const color)
303 return make_change(color_, color);
307 Changer FontInfo::changeShape(FontShape const shape)
309 return make_change(shape_, shape);
313 Changer FontInfo::changeStyle(MathStyle const new_style)
315 return make_change(style_, new_style);
319 Changer FontInfo::change(FontInfo font, bool realiz)
323 return make_change(*this, font);
327 /// Updates a misc setting according to request
328 static FontState setMisc(FontState newfont,
331 if (newfont == FONT_TOGGLE) {
334 else if (org == FONT_OFF)
337 LYXERR0("Font::setMisc: Need state"
338 " FONT_ON or FONT_OFF to toggle. Setting to FONT_ON");
341 } else if (newfont == FONT_IGNORE)
347 /// Updates font settings according to request
348 void FontInfo::update(FontInfo const & newfont, bool toggleall)
350 if (newfont.family_ == family_ && toggleall)
351 setFamily(INHERIT_FAMILY); // toggle 'back'
352 else if (newfont.family_ != IGNORE_FAMILY)
353 setFamily(newfont.family_);
354 // else it's IGNORE_SHAPE
356 // "Old" behaviour: "Setting" bold will toggle bold on/off.
357 switch (newfont.series_) {
360 if (series_ == BOLD_SERIES && toggleall)
361 setSeries(MEDIUM_SERIES);
363 setSeries(BOLD_SERIES);
367 setSeries(newfont.series_);
373 if (newfont.shape_ == shape_ && toggleall)
374 shape_ = INHERIT_SHAPE; // toggle 'back'
375 else if (newfont.shape_ != IGNORE_SHAPE)
376 shape_ = newfont.shape_;
377 // else it's IGNORE_SHAPE
379 if (newfont.size_ != FONT_SIZE_IGNORE) {
380 if (newfont.size_ == FONT_SIZE_INCREASE)
382 else if (newfont.size_ == FONT_SIZE_DECREASE)
385 size_ = newfont.size_;
388 if (newfont.style_ != LM_ST_IGNORE) {
389 style_ = newfont.style_;
392 setEmph(setMisc(newfont.emph_, emph_));
393 setUnderbar(setMisc(newfont.underbar_, underbar_));
394 setStrikeout(setMisc(newfont.strikeout_, strikeout_));
395 setXout(setMisc(newfont.xout_, xout_));
396 setUuline(setMisc(newfont.uuline_, uuline_));
397 setUwave(setMisc(newfont.uwave_, uwave_));
398 setNoun(setMisc(newfont.noun_, noun_));
399 setNumber(setMisc(newfont.number_, number_));
400 setNoSpellcheck(setMisc(newfont.nospellcheck_, nospellcheck_));
402 if (newfont.color_ == color_ && toggleall)
403 setColor(Color_inherit); // toggle 'back'
404 else if (newfont.color_ != Color_ignore)
405 setColor(newfont.color_);
407 if (newfont.background_ == background_ && toggleall)
408 setBackground(Color_inherit); // toggle 'back'
409 else if (newfont.background_ != Color_ignore)
410 setBackground(newfont.background_);
413 /// Is font resolved?
414 bool FontInfo::resolved() const
416 return (family_ != INHERIT_FAMILY && series_ != INHERIT_SERIES
417 && shape_ != INHERIT_SHAPE && size_ != FONT_SIZE_INHERIT
418 && style_ != LM_ST_INHERIT
419 && emph_ != FONT_INHERIT && underbar_ != FONT_INHERIT
420 && uuline_ != FONT_INHERIT && uwave_ != FONT_INHERIT
421 && strikeout_ != FONT_INHERIT && xout_ != FONT_INHERIT
422 && noun_ != FONT_INHERIT && color_ != Color_inherit
423 && background_ != Color_inherit && nospellcheck_ != FONT_INHERIT);
427 Color FontInfo::realColor() const
429 if (paint_color_ != Color_none)
431 if (color_ == Color_none)
432 return Color_foreground;
439 void appendSep(string & s1, string const & s2)
443 s1 += s1.empty() ? "" : "\n";
448 string makeCSSTag(string const & key, string const & val)
450 return key + ": " + val + ";";
454 string getFamilyCSS(FontFamily const & f)
461 case TYPEWRITER_FAMILY:
483 string getSeriesCSS(FontSeries const & s)
498 string getShapeCSS(FontShape const & s)
500 string fs = "normal";
501 string fv = "normal";
503 case UP_SHAPE: break;
504 case ITALIC_SHAPE: fs = "italic"; break;
505 case SLANTED_SHAPE: fs = "oblique"; break;
506 case SMALLCAPS_SHAPE: fv = "small-caps"; break;
509 fs = ""; fv = ""; break;
513 appendSep(retval, makeCSSTag("font-style", fs));
515 appendSep(retval, makeCSSTag("font-variant", fv));
520 string getSizeCSS(FontSize const & s)
525 case FONT_SIZE_SCRIPT:
527 case FONT_SIZE_FOOTNOTE:
528 case FONT_SIZE_SMALL:
530 case FONT_SIZE_NORMAL:
532 case FONT_SIZE_LARGE:
534 case FONT_SIZE_LARGER:
535 case FONT_SIZE_LARGEST:
538 case FONT_SIZE_HUGER:
540 case FONT_SIZE_INCREASE:
542 case FONT_SIZE_DECREASE:
544 case FONT_SIZE_IGNORE:
545 case FONT_SIZE_INHERIT:
554 // FIXME This does not yet handle color
555 docstring FontInfo::asCSS() const
558 string tmp = getFamilyCSS(family_);
560 appendSep(retval, makeCSSTag("font-family", tmp));
561 tmp = getSeriesCSS(series_);
563 appendSep(retval, makeCSSTag("font-weight", tmp));
564 appendSep(retval, getShapeCSS(shape_));
565 tmp = getSizeCSS(size_);
567 appendSep(retval, makeCSSTag("font-size", tmp));
568 return from_ascii(retval);
572 // Set family according to lyx format string
573 void setLyXFamily(string const & fam, FontInfo & f)
575 string const s = ascii_lowercase(fam);
578 while (LyXFamilyNames[i] != s &&
579 LyXFamilyNames[i] != string("error"))
581 if (s == LyXFamilyNames[i])
582 f.setFamily(FontFamily(i));
584 LYXERR0("Unknown family `" << s << '\'');
588 // Set series according to lyx format string
589 void setLyXSeries(string const & ser, FontInfo & f)
591 string const s = ascii_lowercase(ser);
594 while (LyXSeriesNames[i] != s &&
595 LyXSeriesNames[i] != string("error")) ++i;
596 if (s == LyXSeriesNames[i]) {
597 f.setSeries(FontSeries(i));
599 LYXERR0("Unknown series `" << s << '\'');
603 // Set shape according to lyx format string
604 void setLyXShape(string const & sha, FontInfo & f)
606 string const s = ascii_lowercase(sha);
609 while (LyXShapeNames[i] != s && LyXShapeNames[i] != string("error"))
611 if (s == LyXShapeNames[i])
612 f.setShape(FontShape(i));
614 LYXERR0("Unknown shape `" << s << '\'');
618 // Set size according to lyx format string
619 void setLyXSize(string const & siz, FontInfo & f)
621 string const s = ascii_lowercase(siz);
623 while (LyXSizeNames[i] != s && LyXSizeNames[i] != string("error"))
625 if (s == LyXSizeNames[i]) {
626 f.setSize(FontSize(i));
628 LYXERR0("Unknown size `" << s << '\'');
632 // Set size according to lyx format string
633 FontState setLyXMisc(string const & siz)
635 string const s = ascii_lowercase(siz);
637 while (LyXMiscNames[i] != s &&
638 LyXMiscNames[i] != string("error")) ++i;
639 if (s == LyXMiscNames[i])
641 LYXERR0("Unknown misc flag `" << s << '\'');
646 /// Sets color after LyX text format
647 void setLyXColor(string const & col, FontInfo & f)
649 f.setColor(lcolor.getFromLyXName(col));
653 // Read a font definition from given file in lyx format
655 FontInfo lyxRead(Lexer & lex, FontInfo const & fi)
659 bool finished = false;
660 while (!finished && lex.isOK() && !error) {
662 string const tok = ascii_lowercase(lex.getString());
666 } else if (tok == "endfont") {
668 } else if (tok == "family") {
670 string const ttok = lex.getString();
671 setLyXFamily(ttok, f);
672 } else if (tok == "series") {
674 string const ttok = lex.getString();
675 setLyXSeries(ttok, f);
676 } else if (tok == "shape") {
678 string const ttok = lex.getString();
679 setLyXShape(ttok, f);
680 } else if (tok == "size") {
682 string const ttok = lex.getString();
684 } else if (tok == "misc") {
686 string const ttok = ascii_lowercase(lex.getString());
688 if (ttok == "no_bar") {
689 f.setUnderbar(FONT_OFF);
690 } else if (ttok == "no_strikeout") {
691 f.setStrikeout(FONT_OFF);
692 } else if (ttok == "no_xout") {
694 } else if (ttok == "no_uuline") {
695 f.setUuline(FONT_OFF);
696 } else if (ttok == "no_uwave") {
697 f.setUwave(FONT_OFF);
698 } else if (ttok == "no_emph") {
700 } else if (ttok == "no_noun") {
702 } else if (ttok == "emph") {
704 } else if (ttok == "underbar") {
705 f.setUnderbar(FONT_ON);
706 } else if (ttok == "strikeout") {
707 f.setStrikeout(FONT_ON);
708 } else if (ttok == "xout") {
710 } else if (ttok == "uuline") {
711 f.setUuline(FONT_ON);
712 } else if (ttok == "uwave") {
714 } else if (ttok == "noun") {
716 } else if (ttok == "nospellcheck") {
717 f.setNoSpellcheck(FONT_ON);
718 } else if (ttok == "no_nospellcheck") {
719 f.setNoSpellcheck(FONT_OFF);
721 lex.printError("Illegal misc type");
723 } else if (tok == "color") {
725 string const ttok = lex.getString();
726 setLyXColor(ttok, f);
728 lex.printError("Unknown tag");
736 void lyxWrite(ostream & os, FontInfo const & f, string const & start, int level)
739 for (int i = 0; i < level; ++i)
742 if (f.family() != INHERIT_FAMILY)
743 oss << indent << "\tFamily " << LyXFamilyNames[f.family()]
745 if (f.series() != INHERIT_SERIES)
746 oss << indent << "\tSeries " << LyXSeriesNames[f.series()]
748 if (f.shape() != INHERIT_SHAPE)
749 oss << indent << "\tShape " << LyXShapeNames[f.shape()]
751 if (f.size() != FONT_SIZE_INHERIT)
752 oss << indent << "\tSize " << LyXSizeNames[f.size()]
754 //FIXME: shall style be handled here? Probably not.
755 if (f.underbar() == FONT_ON)
756 oss << indent << "\tMisc Underbar\n";
757 else if (f.underbar() == FONT_OFF)
758 oss << indent << "\tMisc No_Bar\n";
759 if (f.strikeout() == FONT_ON)
760 oss << indent << "\tMisc Strikeout\n";
761 else if (f.strikeout() == FONT_OFF)
762 oss << indent << "\tMisc No_Strikeout\n";
763 if (f.xout() == FONT_ON)
764 oss << indent << "\tMisc Xout\n";
765 else if (f.xout() == FONT_OFF)
766 oss << indent << "\tMisc No_Xout\n";
767 if (f.uuline() == FONT_ON)
768 oss << indent << "\tMisc Uuline\n";
769 else if (f.uuline() == FONT_OFF)
770 oss << indent << "\tMisc No_Uuline\n";
771 if (f.uwave() == FONT_ON)
772 oss << indent << "\tMisc Uwave\n";
773 else if (f.uwave() == FONT_OFF)
774 oss << indent << "\tMisc No_Uwave\n";
775 if (f.emph() == FONT_ON)
776 oss << indent << "\tMisc Emph\n";
777 else if (f.emph() == FONT_OFF)
778 oss << indent << "\tMisc No_Emph\n";
779 if (f.noun() == FONT_ON)
780 oss << indent << "\tMisc Noun\n";
781 else if (f.noun() == FONT_OFF)
782 oss << indent << "\tMisc No_Noun\n";
783 if (f.nospellcheck() == FONT_ON)
784 oss << indent << "\tMisc NoSpellcheck\n";
785 else if (f.nospellcheck() == FONT_OFF)
786 oss << indent << "\tMisc No_NoSpellcheck\n";
787 if (f.color() != Color_inherit && f.color() != Color_none)
788 oss << indent << "\tColor " << lcolor.getLyXName(f.color())
790 if (!oss.str().empty()) {
791 os << indent << start << '\n'
793 << indent << "EndFont\n";