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.
23 #include "support/convert.h"
24 #include "support/debug.h"
25 #include "support/docstring.h"
26 #include "support/gettext.h"
27 #include "support/lstrings.h"
34 using namespace lyx::support;
42 char const * GUIFamilyNames[NUM_FAMILIES + 2 /* default & error */] =
43 { N_("Roman"), N_("Sans Serif"), N_("Typewriter"), N_("Symbol"),
44 "cmr", "cmsy", "cmm", "cmex", "msa", "msb", "ds", "eufrak", "rsfs", "stmry",
45 "wasy", "esint", N_("Inherit"), N_("Ignore") };
47 char const * GUISeriesNames[NUM_SERIES + 2 /* default & error */] =
48 { N_("Medium"), N_("Bold"), N_("Inherit"), N_("Ignore") };
50 char const * GUIShapeNames[NUM_SHAPE + 2 /* default & error */] =
51 { N_("Upright"), N_("Italic"), N_("Slanted"), N_("Smallcaps"), N_("Inherit"),
54 char const * GUISizeNames[NUM_SIZE + 4 /* increase, decrease, default & error */] =
55 { N_("Tiny"), N_("Smallest"), N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"),
56 N_("Larger"), N_("Largest"), N_("Huge"), N_("Huger"), N_("Increase"), N_("Decrease"),
57 N_("Inherit"), N_("Ignore") };
59 char const * GUIMiscNames[5] =
60 { N_("Off"), N_("On"), N_("Toggle"), N_("Inherit"), N_("Ignore") };
64 // Strings used to read and write .lyx format files
66 char const * LyXFamilyNames[NUM_FAMILIES + 2 /* default & error */] =
67 { "roman", "sans", "typewriter", "symbol",
68 "cmr", "cmsy", "cmm", "cmex", "msa", "msb", "ds", "eufrak", "rsfs", "stmry",
69 "wasy", "esint", "default", "error" };
71 char const * LyXSeriesNames[NUM_SERIES + 2 /* default & error */] =
72 { "medium", "bold", "default", "error" };
74 char const * LyXShapeNames[NUM_SHAPE + 2 /* default & error */] =
75 { "up", "italic", "slanted", "smallcaps", "default", "error" };
77 char const * LyXSizeNames[NUM_SIZE + 4 /* increase, decrease, default & error */] =
78 { "tiny", "scriptsize", "footnotesize", "small", "normal", "large",
79 "larger", "largest", "huge", "giant",
80 "increase", "decrease", "default", "error" };
82 char const * LyXMiscNames[5] =
83 { "off", "on", "toggle", "default", "error" };
86 FontInfo const sane_font(
104 FontInfo const inherit_font(
122 FontInfo const ignore_font(
147 /// Decreases font size_ by one
148 FontInfo & FontInfo::decSize()
151 case HUGER_SIZE: size_ = HUGE_SIZE; break;
152 case HUGE_SIZE: size_ = LARGEST_SIZE; break;
153 case LARGEST_SIZE: size_ = LARGER_SIZE; break;
154 case LARGER_SIZE: size_ = LARGE_SIZE; break;
155 case LARGE_SIZE: size_ = NORMAL_SIZE; break;
156 case NORMAL_SIZE: size_ = SMALL_SIZE; break;
157 case SMALL_SIZE: size_ = FOOTNOTE_SIZE; break;
158 case FOOTNOTE_SIZE: size_ = SCRIPT_SIZE; break;
159 case SCRIPT_SIZE: size_ = TINY_SIZE; break;
160 case TINY_SIZE: break;
162 LYXERR0("Can't FontInfo::decSize on INCREASE_SIZE");
165 LYXERR0("Can't FontInfo::decSize on DECREASE_SIZE");
168 LYXERR0("Can't FontInfo::decSize on INHERIT_SIZE");
171 LYXERR0("Can't FontInfo::decSize on IGNORE_SIZE");
178 /// Increases font size_ by one
179 FontInfo & FontInfo::incSize()
182 case HUGER_SIZE: break;
183 case HUGE_SIZE: size_ = HUGER_SIZE; break;
184 case LARGEST_SIZE: size_ = HUGE_SIZE; break;
185 case LARGER_SIZE: size_ = LARGEST_SIZE; break;
186 case LARGE_SIZE: size_ = LARGER_SIZE; break;
187 case NORMAL_SIZE: size_ = LARGE_SIZE; break;
188 case SMALL_SIZE: size_ = NORMAL_SIZE; break;
189 case FOOTNOTE_SIZE: size_ = SMALL_SIZE; break;
190 case SCRIPT_SIZE: size_ = FOOTNOTE_SIZE; break;
191 case TINY_SIZE: size_ = SCRIPT_SIZE; break;
193 LYXERR0("Can't FontInfo::incSize on INCREASE_SIZE");
196 LYXERR0("Can't FontInfo::incSize on DECREASE_SIZE");
199 LYXERR0("Can't FontInfo::incSize on INHERIT_SIZE");
202 LYXERR0("Can't FontInfo::incSize on IGNORE_SIZE");
209 double FontInfo::realSize() const
211 double d = convert<double>(lyxrc.font_sizes[size()]);
212 // The following is according to the average of the values in the
213 // definitions of \defaultscriptratio and \defaultscriptscriptratio in LaTeX
214 // font packages. No attempt is made to implement the actual values from
225 case SCRIPTSCRIPT_STYLE:
229 // Never go below the smallest size
230 return max(d, convert<double>(lyxrc.font_sizes[TINY_SIZE]));
234 /// Reduce font to fall back to template where possible
235 void FontInfo::reduce(FontInfo const & tmplt)
237 if (family_ == tmplt.family_)
238 family_ = INHERIT_FAMILY;
239 if (series_ == tmplt.series_)
240 series_ = INHERIT_SERIES;
241 if (shape_ == tmplt.shape_)
242 shape_ = INHERIT_SHAPE;
243 if (size_ == tmplt.size_)
244 size_ = INHERIT_SIZE;
245 if (style_ == tmplt.style_)
246 style_ = INHERIT_STYLE;
247 if (emph_ == tmplt.emph_)
248 emph_ = FONT_INHERIT;
249 if (underbar_ == tmplt.underbar_)
250 underbar_ = FONT_INHERIT;
251 if (strikeout_ == tmplt.strikeout_)
252 strikeout_ = FONT_INHERIT;
253 if (xout_ == tmplt.xout_)
254 xout_ = FONT_INHERIT;
255 if (uuline_ == tmplt.uuline_)
256 uuline_ = FONT_INHERIT;
257 if (uwave_ == tmplt.uwave_)
258 uwave_ = FONT_INHERIT;
259 if (noun_ == tmplt.noun_)
260 noun_ = FONT_INHERIT;
261 if (color_ == tmplt.color_)
262 color_ = Color_inherit;
263 if (background_ == tmplt.background_)
264 background_ = Color_inherit;
265 if (nospellcheck_ == tmplt.nospellcheck_)
266 nospellcheck_ = FONT_INHERIT;
270 /// Realize font from a template
271 FontInfo & FontInfo::realize(FontInfo const & tmplt)
273 if ((*this) == inherit_font) {
278 if (family_ == INHERIT_FAMILY)
279 family_ = tmplt.family_;
281 if (series_ == INHERIT_SERIES)
282 series_ = tmplt.series_;
284 if (shape_ == INHERIT_SHAPE)
285 shape_ = tmplt.shape_;
287 if (size_ == INHERIT_SIZE)
290 if (style_ == INHERIT_STYLE)
291 style_ = tmplt.style_;
293 if (emph_ == FONT_INHERIT)
296 if (underbar_ == FONT_INHERIT)
297 underbar_ = tmplt.underbar_;
299 if (strikeout_ == FONT_INHERIT)
300 strikeout_ = tmplt.strikeout_;
302 if (xout_ == FONT_INHERIT)
305 if (uuline_ == FONT_INHERIT)
306 uuline_ = tmplt.uuline_;
308 if (uwave_ == FONT_INHERIT)
309 uwave_ = tmplt.uwave_;
311 if (noun_ == FONT_INHERIT)
314 if (color_ == Color_inherit)
315 color_ = tmplt.color_;
317 if (background_ == Color_inherit)
318 background_ = tmplt.background_;
320 if (nospellcheck_ == FONT_INHERIT)
321 nospellcheck_ = tmplt.nospellcheck_;
327 Changer FontInfo::changeColor(ColorCode const color)
329 return changeVar(color_, color);
333 Changer FontInfo::changeShape(FontShape const shape)
335 return changeVar(shape_, shape);
339 Changer FontInfo::changeStyle(MathStyle const new_style)
341 return changeVar(style_, new_style);
345 Changer FontInfo::change(FontInfo font, bool realize)
349 return changeVar(*this, font);
353 /// Updates a misc setting according to request
354 static FontState setMisc(FontState newfont,
357 if (newfont == FONT_TOGGLE) {
360 else if (org == FONT_OFF)
363 LYXERR0("Font::setMisc: Need state"
364 " FONT_ON or FONT_OFF to toggle. Setting to FONT_ON");
367 } else if (newfont == FONT_IGNORE)
373 /// Updates font settings according to request
374 void FontInfo::update(FontInfo const & newfont, bool toggleall)
376 if (newfont.family_ == family_ && toggleall)
377 setFamily(INHERIT_FAMILY); // toggle 'back'
378 else if (newfont.family_ != IGNORE_FAMILY)
379 setFamily(newfont.family_);
380 // else it's IGNORE_SHAPE
382 // "Old" behaviour: "Setting" bold will toggle bold on/off.
383 switch (newfont.series_) {
386 if (series_ == BOLD_SERIES && toggleall)
387 setSeries(MEDIUM_SERIES);
389 setSeries(BOLD_SERIES);
393 setSeries(newfont.series_);
399 if (newfont.shape_ == shape_ && toggleall)
400 shape_ = INHERIT_SHAPE; // toggle 'back'
401 else if (newfont.shape_ != IGNORE_SHAPE)
402 shape_ = newfont.shape_;
403 // else it's IGNORE_SHAPE
405 if (newfont.size_ != IGNORE_SIZE) {
406 if (newfont.size_ == INCREASE_SIZE)
408 else if (newfont.size_ == DECREASE_SIZE)
411 size_ = newfont.size_;
414 if (newfont.style_ != IGNORE_STYLE) {
415 style_ = newfont.style_;
418 setEmph(setMisc(newfont.emph_, emph_));
419 setUnderbar(setMisc(newfont.underbar_, underbar_));
420 setStrikeout(setMisc(newfont.strikeout_, strikeout_));
421 setXout(setMisc(newfont.xout_, xout_));
422 setUuline(setMisc(newfont.uuline_, uuline_));
423 setUwave(setMisc(newfont.uwave_, uwave_));
424 setNoun(setMisc(newfont.noun_, noun_));
425 setNumber(setMisc(newfont.number_, number_));
426 setNoSpellcheck(setMisc(newfont.nospellcheck_, nospellcheck_));
428 if (newfont.color_ == color_ && toggleall)
429 setColor(Color_inherit); // toggle 'back'
430 else if (newfont.color_ != Color_ignore)
431 setColor(newfont.color_);
433 if (newfont.background_ == background_ && toggleall)
434 setBackground(Color_inherit); // toggle 'back'
435 else if (newfont.background_ != Color_ignore)
436 setBackground(newfont.background_);
439 /// Is font resolved?
440 bool FontInfo::resolved() const
442 return (family_ != INHERIT_FAMILY && series_ != INHERIT_SERIES
443 && shape_ != INHERIT_SHAPE && size_ != INHERIT_SIZE
444 && style_ != INHERIT_STYLE
445 && emph_ != FONT_INHERIT && underbar_ != FONT_INHERIT
446 && uuline_ != FONT_INHERIT && uwave_ != FONT_INHERIT
447 && strikeout_ != FONT_INHERIT && xout_ != FONT_INHERIT
448 && noun_ != FONT_INHERIT && color_ != Color_inherit
449 && background_ != Color_inherit && nospellcheck_ != FONT_INHERIT);
453 Color FontInfo::realColor() const
455 if (paint_color_ != Color_none)
457 if (color_ == Color_none)
458 return Color_foreground;
465 void appendSep(string & s1, string const & s2)
469 s1 += s1.empty() ? "" : "\n";
474 string makeCSSTag(string const & key, string const & val)
476 return key + ": " + val + ";";
480 string getFamilyCSS(FontFamily const & f)
487 case TYPEWRITER_FAMILY:
510 string getSeriesCSS(FontSeries const & s)
525 string getShapeCSS(FontShape const & s)
527 string fs = "normal";
528 string fv = "normal";
530 case UP_SHAPE: break;
531 case ITALIC_SHAPE: fs = "italic"; break;
532 case SLANTED_SHAPE: fs = "oblique"; break;
533 case SMALLCAPS_SHAPE: fv = "small-caps"; break;
536 fs = ""; fv = ""; break;
540 appendSep(retval, makeCSSTag("font-style", fs));
542 appendSep(retval, makeCSSTag("font-variant", fv));
547 string getSizeCSS(FontSize const & s)
581 // FIXME This does not yet handle color
582 docstring FontInfo::asCSS() const
585 string tmp = getFamilyCSS(family_);
587 appendSep(retval, makeCSSTag("font-family", tmp));
588 tmp = getSeriesCSS(series_);
590 appendSep(retval, makeCSSTag("font-weight", tmp));
591 appendSep(retval, getShapeCSS(shape_));
592 tmp = getSizeCSS(size_);
594 appendSep(retval, makeCSSTag("font-size", tmp));
595 return from_ascii(retval);
599 docstring const FontInfo::stateText(bool const terse) const
602 if (family() != INHERIT_FAMILY && (!terse || family() != IGNORE_FAMILY))
603 os << _(GUIFamilyNames[family()]) << ", ";
604 if (series() != INHERIT_SERIES && (!terse || series() != IGNORE_SERIES))
605 os << _(GUISeriesNames[series()]) << ", ";
606 if (shape() != INHERIT_SHAPE && (!terse || shape() != IGNORE_SHAPE))
607 os << _(GUIShapeNames[shape()]) << ", ";
608 if (size() != INHERIT_SIZE && (!terse || size() != IGNORE_SIZE))
609 os << _(GUISizeNames[size()]) << ", ";
610 // FIXME: shall style be handled there? Probably not.
611 if (color() != Color_inherit && (!terse || color() != Color_ignore))
612 os << lcolor.getGUIName(color()) << ", ";
613 // FIXME: uncomment this when we support background.
614 //if (background() != Color_inherit)
615 // os << lcolor.getGUIName(background()) << ", ";
616 if (emph() != FONT_INHERIT && (!terse || emph() != FONT_IGNORE))
617 os << bformat(_("Emphasis %1$s, "),
618 _(GUIMiscNames[emph()]));
619 if (underbar() != FONT_INHERIT && (!terse || underbar() == FONT_ON))
620 os << bformat(_("Underline %1$s, "),
621 _(GUIMiscNames[underbar()]));
622 if (uuline() != FONT_INHERIT && (!terse || uuline() == FONT_ON))
623 os << bformat(_("Double underline %1$s, "),
624 _(GUIMiscNames[uuline()]));
625 if (uwave() != FONT_INHERIT && (!terse || uwave() == FONT_ON))
626 os << bformat(_("Wavy underline %1$s, "),
627 _(GUIMiscNames[uwave()]));
628 if (strikeout() != FONT_INHERIT && (!terse || strikeout() == FONT_ON))
629 os << bformat(_("Strike out %1$s, "),
630 _(GUIMiscNames[strikeout()]));
631 if (xout() != FONT_INHERIT && (!terse || xout() == FONT_ON))
632 os << bformat(_("Cross out %1$s, "),
633 _(GUIMiscNames[xout()]));
634 if (noun() != FONT_INHERIT && (!terse || noun() != FONT_IGNORE))
635 os << bformat(_("Noun %1$s, "),
636 _(GUIMiscNames[noun()]));
637 if (*this == inherit_font)
638 os << _("Default") << ", ";
644 // Set family according to lyx format string
645 void setLyXFamily(string const & fam, FontInfo & f)
647 string const s = ascii_lowercase(fam);
650 while (LyXFamilyNames[i] != s &&
651 LyXFamilyNames[i] != string("error"))
653 if (s == LyXFamilyNames[i])
654 f.setFamily(FontFamily(i));
656 LYXERR0("Unknown family `" << s << '\'');
660 // Set series according to lyx format string
661 void setLyXSeries(string const & ser, FontInfo & f)
663 string const s = ascii_lowercase(ser);
666 while (LyXSeriesNames[i] != s &&
667 LyXSeriesNames[i] != string("error")) ++i;
668 if (s == LyXSeriesNames[i]) {
669 f.setSeries(FontSeries(i));
671 LYXERR0("Unknown series `" << s << '\'');
675 // Set shape according to lyx format string
676 void setLyXShape(string const & sha, FontInfo & f)
678 string const s = ascii_lowercase(sha);
681 while (LyXShapeNames[i] != s && LyXShapeNames[i] != string("error"))
683 if (s == LyXShapeNames[i])
684 f.setShape(FontShape(i));
686 LYXERR0("Unknown shape `" << s << '\'');
690 // Set size according to lyx format string
691 void setLyXSize(string const & siz, FontInfo & f)
693 string const s = ascii_lowercase(siz);
695 while (LyXSizeNames[i] != s && LyXSizeNames[i] != string("error"))
697 if (s == LyXSizeNames[i]) {
698 f.setSize(FontSize(i));
700 LYXERR0("Unknown size `" << s << '\'');
704 // Set size according to lyx format string
705 FontState setLyXMisc(string const & siz)
707 string const s = ascii_lowercase(siz);
709 while (LyXMiscNames[i] != s &&
710 LyXMiscNames[i] != string("error")) ++i;
711 if (s == LyXMiscNames[i])
713 LYXERR0("Unknown misc flag `" << s << '\'');
718 /// Sets color after LyX text format
719 void setLyXColor(string const & col, FontInfo & f)
721 f.setColor(lcolor.getFromLyXName(col));
725 // Read a font definition from given file in lyx format
727 FontInfo lyxRead(Lexer & lex, FontInfo const & fi)
731 bool finished = false;
732 while (!finished && lex.isOK() && !error) {
734 string const tok = ascii_lowercase(lex.getString());
738 } else if (tok == "endfont") {
740 } else if (tok == "family") {
742 string const ttok = lex.getString();
743 setLyXFamily(ttok, f);
744 } else if (tok == "series") {
746 string const ttok = lex.getString();
747 setLyXSeries(ttok, f);
748 } else if (tok == "shape") {
750 string const ttok = lex.getString();
751 setLyXShape(ttok, f);
752 } else if (tok == "size") {
754 string const ttok = lex.getString();
756 } else if (tok == "misc") {
758 string const ttok = ascii_lowercase(lex.getString());
760 if (ttok == "no_bar") {
761 f.setUnderbar(FONT_OFF);
762 } else if (ttok == "no_strikeout") {
763 f.setStrikeout(FONT_OFF);
764 } else if (ttok == "no_xout") {
766 } else if (ttok == "no_uuline") {
767 f.setUuline(FONT_OFF);
768 } else if (ttok == "no_uwave") {
769 f.setUwave(FONT_OFF);
770 } else if (ttok == "no_emph") {
772 } else if (ttok == "no_noun") {
774 } else if (ttok == "emph") {
776 } else if (ttok == "underbar") {
777 f.setUnderbar(FONT_ON);
778 } else if (ttok == "strikeout") {
779 f.setStrikeout(FONT_ON);
780 } else if (ttok == "xout") {
782 } else if (ttok == "uuline") {
783 f.setUuline(FONT_ON);
784 } else if (ttok == "uwave") {
786 } else if (ttok == "noun") {
788 } else if (ttok == "nospellcheck") {
789 f.setNoSpellcheck(FONT_ON);
790 } else if (ttok == "no_nospellcheck") {
791 f.setNoSpellcheck(FONT_OFF);
793 lex.printError("Illegal misc type");
795 } else if (tok == "color") {
797 string const ttok = lex.getString();
798 setLyXColor(ttok, f);
800 lex.printError("Unknown tag");
808 void lyxWrite(ostream & os, FontInfo const & f, string const & start, int level)
811 for (int i = 0; i < level; ++i)
814 if (f.family() != INHERIT_FAMILY)
815 oss << indent << "\tFamily " << LyXFamilyNames[f.family()]
817 if (f.series() != INHERIT_SERIES)
818 oss << indent << "\tSeries " << LyXSeriesNames[f.series()]
820 if (f.shape() != INHERIT_SHAPE)
821 oss << indent << "\tShape " << LyXShapeNames[f.shape()]
823 if (f.size() != INHERIT_SIZE)
824 oss << indent << "\tSize " << LyXSizeNames[f.size()]
826 //FIXME: shall style be handled here? Probably not.
827 if (f.underbar() == FONT_ON)
828 oss << indent << "\tMisc Underbar\n";
829 else if (f.underbar() == FONT_OFF)
830 oss << indent << "\tMisc No_Bar\n";
831 if (f.strikeout() == FONT_ON)
832 oss << indent << "\tMisc Strikeout\n";
833 else if (f.strikeout() == FONT_OFF)
834 oss << indent << "\tMisc No_Strikeout\n";
835 if (f.xout() == FONT_ON)
836 oss << indent << "\tMisc Xout\n";
837 else if (f.xout() == FONT_OFF)
838 oss << indent << "\tMisc No_Xout\n";
839 if (f.uuline() == FONT_ON)
840 oss << indent << "\tMisc Uuline\n";
841 else if (f.uuline() == FONT_OFF)
842 oss << indent << "\tMisc No_Uuline\n";
843 if (f.uwave() == FONT_ON)
844 oss << indent << "\tMisc Uwave\n";
845 else if (f.uwave() == FONT_OFF)
846 oss << indent << "\tMisc No_Uwave\n";
847 if (f.emph() == FONT_ON)
848 oss << indent << "\tMisc Emph\n";
849 else if (f.emph() == FONT_OFF)
850 oss << indent << "\tMisc No_Emph\n";
851 if (f.noun() == FONT_ON)
852 oss << indent << "\tMisc Noun\n";
853 else if (f.noun() == FONT_OFF)
854 oss << indent << "\tMisc No_Noun\n";
855 if (f.nospellcheck() == FONT_ON)
856 oss << indent << "\tMisc NoSpellcheck\n";
857 else if (f.nospellcheck() == FONT_OFF)
858 oss << indent << "\tMisc No_NoSpellcheck\n";
859 if (f.color() != Color_inherit && f.color() != Color_none)
860 oss << indent << "\tColor " << lcolor.getLyXName(f.color())
862 if (!oss.str().empty()) {
863 os << indent << start << '\n'
865 << indent << "EndFont\n";