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/gettext.h"
26 #include "support/lstrings.h"
27 #include "support/RefChanger.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", "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", "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 make_change(color_, color);
333 Changer FontInfo::changeShape(FontShape const shape)
335 return make_change(shape_, shape);
339 Changer FontInfo::changeStyle(MathStyle const new_style)
341 return make_change(style_, new_style);
345 Changer FontInfo::change(FontInfo font, bool realiz)
349 return make_change(*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:
509 string getSeriesCSS(FontSeries const & s)
524 string getShapeCSS(FontShape const & s)
526 string fs = "normal";
527 string fv = "normal";
529 case UP_SHAPE: break;
530 case ITALIC_SHAPE: fs = "italic"; break;
531 case SLANTED_SHAPE: fs = "oblique"; break;
532 case SMALLCAPS_SHAPE: fv = "small-caps"; break;
535 fs = ""; fv = ""; break;
539 appendSep(retval, makeCSSTag("font-style", fs));
541 appendSep(retval, makeCSSTag("font-variant", fv));
546 string getSizeCSS(FontSize const & s)
580 // FIXME This does not yet handle color
581 docstring FontInfo::asCSS() const
584 string tmp = getFamilyCSS(family_);
586 appendSep(retval, makeCSSTag("font-family", tmp));
587 tmp = getSeriesCSS(series_);
589 appendSep(retval, makeCSSTag("font-weight", tmp));
590 appendSep(retval, getShapeCSS(shape_));
591 tmp = getSizeCSS(size_);
593 appendSep(retval, makeCSSTag("font-size", tmp));
594 return from_ascii(retval);
598 docstring const FontInfo::stateText(bool const terse) const
601 if (family() != INHERIT_FAMILY && (!terse || family() != IGNORE_FAMILY))
602 os << _(GUIFamilyNames[family()]) << ", ";
603 if (series() != INHERIT_SERIES && (!terse || series() != IGNORE_SERIES))
604 os << _(GUISeriesNames[series()]) << ", ";
605 if (shape() != INHERIT_SHAPE && (!terse || shape() != IGNORE_SHAPE))
606 os << _(GUIShapeNames[shape()]) << ", ";
607 if (size() != INHERIT_SIZE && (!terse || size() != IGNORE_SIZE))
608 os << _(GUISizeNames[size()]) << ", ";
609 // FIXME: shall style be handled there? Probably not.
610 if (color() != Color_inherit && (!terse || color() != Color_ignore))
611 os << lcolor.getGUIName(color()) << ", ";
612 // FIXME: uncomment this when we support background.
613 //if (background() != Color_inherit)
614 // os << lcolor.getGUIName(background()) << ", ";
615 if (emph() != FONT_INHERIT && (!terse || emph() != FONT_IGNORE))
616 os << bformat(_("Emphasis %1$s, "),
617 _(GUIMiscNames[emph()]));
618 if (underbar() != FONT_INHERIT && (!terse || underbar() == FONT_ON))
619 os << bformat(_("Underline %1$s, "),
620 _(GUIMiscNames[underbar()]));
621 if (uuline() != FONT_INHERIT && (!terse || uuline() == FONT_ON))
622 os << bformat(_("Double underline %1$s, "),
623 _(GUIMiscNames[uuline()]));
624 if (uwave() != FONT_INHERIT && (!terse || uwave() == FONT_ON))
625 os << bformat(_("Wavy underline %1$s, "),
626 _(GUIMiscNames[uwave()]));
627 if (strikeout() != FONT_INHERIT && (!terse || strikeout() == FONT_ON))
628 os << bformat(_("Strike out %1$s, "),
629 _(GUIMiscNames[strikeout()]));
630 if (xout() != FONT_INHERIT && (!terse || xout() == FONT_ON))
631 os << bformat(_("Cross out %1$s, "),
632 _(GUIMiscNames[xout()]));
633 if (noun() != FONT_INHERIT && (!terse || noun() != FONT_IGNORE))
634 os << bformat(_("Noun %1$s, "),
635 _(GUIMiscNames[noun()]));
636 if (*this == inherit_font)
637 os << _("Default") << ", ";
643 // Set family according to lyx format string
644 void setLyXFamily(string const & fam, FontInfo & f)
646 string const s = ascii_lowercase(fam);
649 while (LyXFamilyNames[i] != s &&
650 LyXFamilyNames[i] != string("error"))
652 if (s == LyXFamilyNames[i])
653 f.setFamily(FontFamily(i));
655 LYXERR0("Unknown family `" << s << '\'');
659 // Set series according to lyx format string
660 void setLyXSeries(string const & ser, FontInfo & f)
662 string const s = ascii_lowercase(ser);
665 while (LyXSeriesNames[i] != s &&
666 LyXSeriesNames[i] != string("error")) ++i;
667 if (s == LyXSeriesNames[i]) {
668 f.setSeries(FontSeries(i));
670 LYXERR0("Unknown series `" << s << '\'');
674 // Set shape according to lyx format string
675 void setLyXShape(string const & sha, FontInfo & f)
677 string const s = ascii_lowercase(sha);
680 while (LyXShapeNames[i] != s && LyXShapeNames[i] != string("error"))
682 if (s == LyXShapeNames[i])
683 f.setShape(FontShape(i));
685 LYXERR0("Unknown shape `" << s << '\'');
689 // Set size according to lyx format string
690 void setLyXSize(string const & siz, FontInfo & f)
692 string const s = ascii_lowercase(siz);
694 while (LyXSizeNames[i] != s && LyXSizeNames[i] != string("error"))
696 if (s == LyXSizeNames[i]) {
697 f.setSize(FontSize(i));
699 LYXERR0("Unknown size `" << s << '\'');
703 // Set size according to lyx format string
704 FontState setLyXMisc(string const & siz)
706 string const s = ascii_lowercase(siz);
708 while (LyXMiscNames[i] != s &&
709 LyXMiscNames[i] != string("error")) ++i;
710 if (s == LyXMiscNames[i])
712 LYXERR0("Unknown misc flag `" << s << '\'');
717 /// Sets color after LyX text format
718 void setLyXColor(string const & col, FontInfo & f)
720 f.setColor(lcolor.getFromLyXName(col));
724 // Read a font definition from given file in lyx format
726 FontInfo lyxRead(Lexer & lex, FontInfo const & fi)
730 bool finished = false;
731 while (!finished && lex.isOK() && !error) {
733 string const tok = ascii_lowercase(lex.getString());
737 } else if (tok == "endfont") {
739 } else if (tok == "family") {
741 string const ttok = lex.getString();
742 setLyXFamily(ttok, f);
743 } else if (tok == "series") {
745 string const ttok = lex.getString();
746 setLyXSeries(ttok, f);
747 } else if (tok == "shape") {
749 string const ttok = lex.getString();
750 setLyXShape(ttok, f);
751 } else if (tok == "size") {
753 string const ttok = lex.getString();
755 } else if (tok == "misc") {
757 string const ttok = ascii_lowercase(lex.getString());
759 if (ttok == "no_bar") {
760 f.setUnderbar(FONT_OFF);
761 } else if (ttok == "no_strikeout") {
762 f.setStrikeout(FONT_OFF);
763 } else if (ttok == "no_xout") {
765 } else if (ttok == "no_uuline") {
766 f.setUuline(FONT_OFF);
767 } else if (ttok == "no_uwave") {
768 f.setUwave(FONT_OFF);
769 } else if (ttok == "no_emph") {
771 } else if (ttok == "no_noun") {
773 } else if (ttok == "emph") {
775 } else if (ttok == "underbar") {
776 f.setUnderbar(FONT_ON);
777 } else if (ttok == "strikeout") {
778 f.setStrikeout(FONT_ON);
779 } else if (ttok == "xout") {
781 } else if (ttok == "uuline") {
782 f.setUuline(FONT_ON);
783 } else if (ttok == "uwave") {
785 } else if (ttok == "noun") {
787 } else if (ttok == "nospellcheck") {
788 f.setNoSpellcheck(FONT_ON);
789 } else if (ttok == "no_nospellcheck") {
790 f.setNoSpellcheck(FONT_OFF);
792 lex.printError("Illegal misc type");
794 } else if (tok == "color") {
796 string const ttok = lex.getString();
797 setLyXColor(ttok, f);
799 lex.printError("Unknown tag");
807 void lyxWrite(ostream & os, FontInfo const & f, string const & start, int level)
810 for (int i = 0; i < level; ++i)
813 if (f.family() != INHERIT_FAMILY)
814 oss << indent << "\tFamily " << LyXFamilyNames[f.family()]
816 if (f.series() != INHERIT_SERIES)
817 oss << indent << "\tSeries " << LyXSeriesNames[f.series()]
819 if (f.shape() != INHERIT_SHAPE)
820 oss << indent << "\tShape " << LyXShapeNames[f.shape()]
822 if (f.size() != INHERIT_SIZE)
823 oss << indent << "\tSize " << LyXSizeNames[f.size()]
825 //FIXME: shall style be handled here? Probably not.
826 if (f.underbar() == FONT_ON)
827 oss << indent << "\tMisc Underbar\n";
828 else if (f.underbar() == FONT_OFF)
829 oss << indent << "\tMisc No_Bar\n";
830 if (f.strikeout() == FONT_ON)
831 oss << indent << "\tMisc Strikeout\n";
832 else if (f.strikeout() == FONT_OFF)
833 oss << indent << "\tMisc No_Strikeout\n";
834 if (f.xout() == FONT_ON)
835 oss << indent << "\tMisc Xout\n";
836 else if (f.xout() == FONT_OFF)
837 oss << indent << "\tMisc No_Xout\n";
838 if (f.uuline() == FONT_ON)
839 oss << indent << "\tMisc Uuline\n";
840 else if (f.uuline() == FONT_OFF)
841 oss << indent << "\tMisc No_Uuline\n";
842 if (f.uwave() == FONT_ON)
843 oss << indent << "\tMisc Uwave\n";
844 else if (f.uwave() == FONT_OFF)
845 oss << indent << "\tMisc No_Uwave\n";
846 if (f.emph() == FONT_ON)
847 oss << indent << "\tMisc Emph\n";
848 else if (f.emph() == FONT_OFF)
849 oss << indent << "\tMisc No_Emph\n";
850 if (f.noun() == FONT_ON)
851 oss << indent << "\tMisc Noun\n";
852 else if (f.noun() == FONT_OFF)
853 oss << indent << "\tMisc No_Noun\n";
854 if (f.nospellcheck() == FONT_ON)
855 oss << indent << "\tMisc NoSpellcheck\n";
856 else if (f.nospellcheck() == FONT_OFF)
857 oss << indent << "\tMisc No_NoSpellcheck\n";
858 if (f.color() != Color_inherit && f.color() != Color_none)
859 oss << indent << "\tColor " << lcolor.getLyXName(f.color())
861 if (!oss.str().empty()) {
862 os << indent << start << '\n'
864 << indent << "EndFont\n";