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.
19 #include "BufferParams.h" // stateText
23 #include "LaTeXFeatures.h"
26 #include "output_latex.h"
27 #include "OutputParams.h"
29 #include "support/lassert.h"
30 #include "support/convert.h"
31 #include "support/debug.h"
32 #include "support/gettext.h"
33 #include "support/lstrings.h"
38 using namespace lyx::support
;
48 char const * GUIFamilyNames
[NUM_FAMILIES
+ 2 /* default & error */] =
49 { N_("Roman"), N_("Sans Serif"), N_("Typewriter"), N_("Symbol"),
50 "cmr", "cmsy", "cmm", "cmex", "msa", "msb", "eufrak", "wasy", "esint",
51 N_("Inherit"), N_("Ignore") };
53 char const * GUISeriesNames
[4] =
54 { N_("Medium"), N_("Bold"), N_("Inherit"), N_("Ignore") };
56 char const * GUIShapeNames
[6] =
57 { N_("Upright"), N_("Italic"), N_("Slanted"), N_("Smallcaps"), N_("Inherit"),
60 char const * GUISizeNames
[14] =
61 { N_("Tiny"), N_("Smallest"), N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"),
62 N_("Larger"), N_("Largest"), N_("Huge"), N_("Huger"), N_("Increase"), N_("Decrease"),
63 N_("Inherit"), N_("Ignore") };
65 char const * GUIMiscNames
[5] =
66 { N_("Off"), N_("On"), N_("Toggle"), N_("Inherit"), N_("Ignore") };
70 // Strings used to read and write .lyx format files
72 char const * LyXFamilyNames
[NUM_FAMILIES
+ 2 /* default & error */] =
73 { "roman", "sans", "typewriter", "symbol",
74 "cmr", "cmsy", "cmm", "cmex", "msa", "msb", "eufrak", "wasy", "esint",
77 char const * LyXSeriesNames
[4] =
78 { "medium", "bold", "default", "error" };
80 char const * LyXShapeNames
[6] =
81 { "up", "italic", "slanted", "smallcaps", "default", "error" };
83 char const * LyXSizeNames
[14] =
84 { "tiny", "scriptsize", "footnotesize", "small", "normal", "large",
85 "larger", "largest", "huge", "giant",
86 "increase", "decrease", "default", "error" };
88 char const * LyXMiscNames
[5] =
89 { "off", "on", "toggle", "default", "error" };
92 // Strings used to write LaTeX files
94 char const * LaTeXFamilyNames
[6] =
95 { "textrm", "textsf", "texttt", "error1", "error2", "error3" };
97 char const * LaTeXSeriesNames
[4] =
98 { "textmd", "textbf", "error4", "error5" };
100 char const * LaTeXShapeNames
[6] =
101 { "textup", "textit", "textsl", "textsc", "error6", "error7" };
103 char const * LaTeXSizeNames
[14] =
104 { "tiny", "scriptsize", "footnotesize", "small", "normalsize", "large",
105 "Large", "LARGE", "huge", "Huge", "error8", "error9", "error10", "error11" };
110 Font::Font(FontInfo bits
, Language
const * l
)
111 : bits_(bits
), lang_(l
), open_encoding_(false)
114 lang_
= default_language
;
118 bool Font::isRightToLeft() const
120 return lang_
->rightToLeft();
124 bool Font::isVisibleRightToLeft() const
126 return (lang_
->rightToLeft() &&
127 bits_
.number() != FONT_ON
);
131 void Font::setLanguage(Language
const * l
)
137 /// Updates font settings according to request
138 void Font::update(Font
const & newfont
,
139 Language
const * document_language
,
142 bits_
.update(newfont
.fontInfo(), toggleall
);
144 if (newfont
.language() == language() && toggleall
)
145 if (language() == document_language
)
146 setLanguage(default_language
);
148 setLanguage(document_language
);
149 else if (newfont
.language() == reset_language
)
150 setLanguage(document_language
);
151 else if (newfont
.language() != ignore_language
)
152 setLanguage(newfont
.language());
156 docstring
const stateText(FontInfo
const & f
)
159 if (f
.family() != INHERIT_FAMILY
)
160 os
<< _(GUIFamilyNames
[f
.family()]) << ", ";
161 if (f
.series() != INHERIT_SERIES
)
162 os
<< _(GUISeriesNames
[f
.series()]) << ", ";
163 if (f
.shape() != INHERIT_SHAPE
)
164 os
<< _(GUIShapeNames
[f
.shape()]) << ", ";
165 if (f
.size() != FONT_SIZE_INHERIT
)
166 os
<< _(GUISizeNames
[f
.size()]) << ", ";
167 if (f
.color() != Color_inherit
)
168 os
<< lcolor
.getGUIName(f
.color()) << ", ";
169 // FIXME: uncomment this when we support background.
170 //if (f.background() != Color_inherit)
171 // os << lcolor.getGUIName(f.background()) << ", ";
172 if (f
.emph() != FONT_INHERIT
)
173 os
<< bformat(_("Emphasis %1$s, "),
174 _(GUIMiscNames
[f
.emph()]));
175 if (f
.underbar() != FONT_INHERIT
)
176 os
<< bformat(_("Underline %1$s, "),
177 _(GUIMiscNames
[f
.underbar()]));
178 if (f
.noun() != FONT_INHERIT
)
179 os
<< bformat(_("Noun %1$s, "),
180 _(GUIMiscNames
[f
.noun()]));
181 if (f
== inherit_font
)
182 os
<< _("Default") << ", ";
188 docstring
const Font::stateText(BufferParams
* params
) const
191 os
<< lyx::stateText(bits_
);
192 if (!params
|| (language() != params
->language
))
193 os
<< bformat(_("Language: %1$s, "),
194 _(language()->display()));
195 if (bits_
.number() != FONT_OFF
)
196 os
<< bformat(_(" Number %1$s"),
197 _(GUIMiscNames
[bits_
.number()]));
198 return rtrim(os
.str(), ", ");
202 // Set family according to lyx format string
203 void setLyXFamily(string
const & fam
, FontInfo
& f
)
205 string
const s
= ascii_lowercase(fam
);
208 while (LyXFamilyNames
[i
] != s
&&
209 LyXFamilyNames
[i
] != string("error"))
211 if (s
== LyXFamilyNames
[i
])
212 f
.setFamily(FontFamily(i
));
214 lyxerr
<< "setLyXFamily: Unknown family `"
215 << s
<< '\'' << endl
;
219 // Set series according to lyx format string
220 void setLyXSeries(string
const & ser
, FontInfo
& f
)
222 string
const s
= ascii_lowercase(ser
);
225 while (LyXSeriesNames
[i
] != s
&&
226 LyXSeriesNames
[i
] != string("error")) ++i
;
227 if (s
== LyXSeriesNames
[i
]) {
228 f
.setSeries(FontSeries(i
));
230 lyxerr
<< "setLyXSeries: Unknown series `"
231 << s
<< '\'' << endl
;
235 // Set shape according to lyx format string
236 void setLyXShape(string
const & sha
, FontInfo
& f
)
238 string
const s
= ascii_lowercase(sha
);
241 while (LyXShapeNames
[i
] != s
&& LyXShapeNames
[i
] != string("error"))
243 if (s
== LyXShapeNames
[i
])
244 f
.setShape(FontShape(i
));
246 lyxerr
<< "Font::setLyXShape: Unknown shape `"
247 << s
<< '\'' << endl
;
251 // Set size according to lyx format string
252 void setLyXSize(string
const & siz
, FontInfo
& f
)
254 string
const s
= ascii_lowercase(siz
);
256 while (LyXSizeNames
[i
] != s
&& LyXSizeNames
[i
] != string("error"))
258 if (s
== LyXSizeNames
[i
]) {
259 f
.setSize(FontSize(i
));
261 lyxerr
<< "Font::setLyXSize: Unknown size `"
262 << s
<< '\'' << endl
;
266 // Set size according to lyx format string
267 FontState
Font::setLyXMisc(string
const & siz
)
269 string
const s
= ascii_lowercase(siz
);
271 while (LyXMiscNames
[i
] != s
&&
272 LyXMiscNames
[i
] != string("error")) ++i
;
273 if (s
== LyXMiscNames
[i
])
275 lyxerr
<< "Font::setLyXMisc: Unknown misc flag `"
276 << s
<< '\'' << endl
;
281 /// Sets color after LyX text format
282 void setLyXColor(string
const & col
, FontInfo
& f
)
284 f
.setColor(lcolor
.getFromLyXName(col
));
288 // Returns size in latex format
289 string
const Font::latexSize() const
291 return LaTeXSizeNames
[bits_
.size()];
295 // Read a font definition from given file in lyx format
297 FontInfo
lyxRead(Lexer
& lex
, FontInfo
const & fi
)
301 bool finished
= false;
302 while (!finished
&& lex
.isOK() && !error
) {
304 string
const tok
= ascii_lowercase(lex
.getString());
308 } else if (tok
== "endfont") {
310 } else if (tok
== "family") {
312 string
const ttok
= lex
.getString();
313 setLyXFamily(ttok
, f
);
314 } else if (tok
== "series") {
316 string
const ttok
= lex
.getString();
317 setLyXSeries(ttok
, f
);
318 } else if (tok
== "shape") {
320 string
const ttok
= lex
.getString();
321 setLyXShape(ttok
, f
);
322 } else if (tok
== "size") {
324 string
const ttok
= lex
.getString();
326 } else if (tok
== "misc") {
328 string
const ttok
= ascii_lowercase(lex
.getString());
330 if (ttok
== "no_bar") {
331 f
.setUnderbar(FONT_OFF
);
332 } else if (ttok
== "no_emph") {
334 } else if (ttok
== "no_noun") {
336 } else if (ttok
== "emph") {
338 } else if (ttok
== "underbar") {
339 f
.setUnderbar(FONT_ON
);
340 } else if (ttok
== "noun") {
343 lex
.printError("Illegal misc type");
345 } else if (tok
== "color") {
347 string
const ttok
= lex
.getString();
348 setLyXColor(ttok
, f
);
350 lex
.printError("Unknown tag");
358 /// Writes the changes from this font to orgfont in .lyx format in file
359 void Font::lyxWriteChanges(Font
const & orgfont
,
363 if (orgfont
.fontInfo().family() != bits_
.family())
364 os
<< "\\family " << LyXFamilyNames
[bits_
.family()] << "\n";
365 if (orgfont
.fontInfo().series() != bits_
.series())
366 os
<< "\\series " << LyXSeriesNames
[bits_
.series()] << "\n";
367 if (orgfont
.fontInfo().shape() != bits_
.shape())
368 os
<< "\\shape " << LyXShapeNames
[bits_
.shape()] << "\n";
369 if (orgfont
.fontInfo().size() != bits_
.size())
370 os
<< "\\size " << LyXSizeNames
[bits_
.size()] << "\n";
371 if (orgfont
.fontInfo().emph() != bits_
.emph())
372 os
<< "\\emph " << LyXMiscNames
[bits_
.emph()] << "\n";
373 if (orgfont
.fontInfo().number() != bits_
.number())
374 os
<< "\\numeric " << LyXMiscNames
[bits_
.number()] << "\n";
375 if (orgfont
.fontInfo().underbar() != bits_
.underbar()) {
376 // This is only for backwards compatibility
377 switch (bits_
.underbar()) {
378 case FONT_OFF
: os
<< "\\bar no\n"; break;
379 case FONT_ON
: os
<< "\\bar under\n"; break;
380 case FONT_TOGGLE
: lyxerr
<< "Font::lyxWriteFontChanges: "
381 "FONT_TOGGLE should not appear here!"
384 case FONT_INHERIT
: os
<< "\\bar default\n"; break;
385 case FONT_IGNORE
: lyxerr
<< "Font::lyxWriteFontChanges: "
386 "IGNORE should not appear here!"
391 if (orgfont
.fontInfo().noun() != bits_
.noun()) {
392 os
<< "\\noun " << LyXMiscNames
[bits_
.noun()] << "\n";
394 if (orgfont
.fontInfo().color() != bits_
.color())
395 os
<< "\\color " << lcolor
.getLyXName(bits_
.color()) << '\n';
396 // FIXME: uncomment this when we support background.
397 //if (orgfont.fontInfo().background() != bits_.background())
398 // os << "\\color " << lcolor.getLyXName(bits_.background()) << '\n';
399 if (orgfont
.language() != language() &&
400 language() != latex_language
) {
402 os
<< "\\lang " << language()->lang() << "\n";
404 os
<< "\\lang unknown\n";
409 /// Writes the head of the LaTeX needed to impose this font
410 // Returns number of chars written.
411 int Font::latexWriteStartChanges(odocstream
& os
, BufferParams
const & bparams
,
412 OutputParams
const & runparams
,
414 Font
const & prev
) const
419 if (language()->babel() != base
.language()->babel() &&
420 language() != prev
.language()) {
421 if (language()->lang() == "farsi") {
424 } else if (!isRightToLeft() &&
425 base
.language()->lang() == "farsi") {
428 } else if (language()->lang() == "arabic_arabi") {
431 } else if (!isRightToLeft() &&
432 base
.language()->lang() == "arabic_arabi") {
435 // currently the remaining RTL languages are arabic_arabtex and hebrew
436 } else if (isRightToLeft() != prev
.isRightToLeft()) {
437 if (isRightToLeft()) {
444 } else if (!language()->babel().empty()) {
446 subst(lyxrc
.language_command_local
,
447 "$$lang", language()->babel());
448 os
<< from_ascii(tmp
);
449 count
+= tmp
.length();
456 if (language()->encoding()->package() == Encoding::CJK
) {
457 pair
<bool, int> const c
= switchEncoding(os
, bparams
,
458 runparams
, *(language()->encoding()));
460 open_encoding_
= true;
462 runparams
.encoding
= language()->encoding();
466 // When the current language is Hebrew, Arabic, or Farsi
467 // the numbers are written Left-to-Right. ArabTeX package
468 // reorders the number automatically but the packages used
469 // for Hebrew and Farsi (Arabi) do not.
470 if (bits_
.number() == FONT_ON
&& prev
.fontInfo().number() != FONT_ON
471 && (language()->lang() == "hebrew"
472 || language()->lang() == "farsi"
473 || language()->lang() == "arabic_arabi")) {
479 f
.reduce(base
.bits_
);
481 if (f
.family() != INHERIT_FAMILY
) {
483 << LaTeXFamilyNames
[f
.family()]
485 count
+= strlen(LaTeXFamilyNames
[f
.family()]) + 2;
486 env
= true; //We have opened a new environment
488 if (f
.series() != INHERIT_SERIES
) {
490 << LaTeXSeriesNames
[f
.series()]
492 count
+= strlen(LaTeXSeriesNames
[f
.series()]) + 2;
493 env
= true; //We have opened a new environment
495 if (f
.shape() != INHERIT_SHAPE
) {
497 << LaTeXShapeNames
[f
.shape()]
499 count
+= strlen(LaTeXShapeNames
[f
.shape()]) + 2;
500 env
= true; //We have opened a new environment
502 if (f
.color() != Color_inherit
&& f
.color() != Color_ignore
) {
504 << from_ascii(lcolor
.getLaTeXName(f
.color()))
506 count
+= lcolor
.getLaTeXName(f
.color()).length() + 13;
507 env
= true; //We have opened a new environment
509 // FIXME: uncomment this when we support background.
511 if (f.background() != Color_inherit && f.background() != Color_ignore) {
513 << from_ascii(lcolor.getLaTeXName(f.background()))
515 count += lcolor.getLaTeXName(f.background()).length() + 13;
516 env = true; //We have opened a new environment
519 if (f
.emph() == FONT_ON
) {
522 env
= true; //We have opened a new environment
524 if (f
.underbar() == FONT_ON
) {
527 env
= true; //We have opened a new environment
529 // \noun{} is a LyX special macro
530 if (f
.noun() == FONT_ON
) {
533 env
= true; //We have opened a new environment
535 if (f
.size() != FONT_SIZE_INHERIT
) {
536 // If we didn't open an environment above, we open one here
542 << LaTeXSizeNames
[f
.size()]
544 count
+= strlen(LaTeXSizeNames
[f
.size()]) + 2;
550 /// Writes ending block of LaTeX needed to close use of this font
551 // Returns number of chars written
552 // This one corresponds to latexWriteStartChanges(). (Asger)
553 int Font::latexWriteEndChanges(odocstream
& os
, BufferParams
const & bparams
,
554 OutputParams
const & runparams
,
557 bool const & closeLanguage
) const
562 // reduce the current font to changes against the base
563 // font (of the layout). We use a temporary for this to
564 // avoid changing this font instance, as that would break
566 f
.reduce(base
.bits_
);
568 if (f
.family() != INHERIT_FAMILY
) {
571 env
= true; // Size change need not bother about closing env.
573 if (f
.series() != INHERIT_SERIES
) {
576 env
= true; // Size change need not bother about closing env.
578 if (f
.shape() != INHERIT_SHAPE
) {
581 env
= true; // Size change need not bother about closing env.
583 if (f
.color() != Color_inherit
&& f
.color() != Color_ignore
) {
586 env
= true; // Size change need not bother about closing env.
588 if (f
.emph() == FONT_ON
) {
591 env
= true; // Size change need not bother about closing env.
593 if (f
.underbar() == FONT_ON
) {
596 env
= true; // Size change need not bother about closing env.
598 if (f
.noun() == FONT_ON
) {
601 env
= true; // Size change need not bother about closing env.
603 if (f
.size() != FONT_SIZE_INHERIT
) {
604 // We only have to close if only size changed
611 // When the current language is Hebrew, Arabic, or Farsi
612 // the numbers are written Left-to-Right. ArabTeX package
613 // reorders the number automatically but the packages used
614 // for Hebrew and Farsi (Arabi) do not.
615 if (bits_
.number() == FONT_ON
&& next
.fontInfo().number() != FONT_ON
616 && (language()->lang() == "hebrew"
617 || language()->lang() == "farsi"
618 || language()->lang() == "arabic_arabi")) {
623 if (open_encoding_
) {
624 // We need to close the encoding even if it does not change
625 // to do correct environment nesting
626 Encoding
const * const ascii
= encodings
.fromLyXName("ascii");
627 pair
<bool, int> const c
= switchEncoding(os
, bparams
,
629 LASSERT(c
.first
, /**/);
631 runparams
.encoding
= ascii
;
632 open_encoding_
= false;
636 language() != base
.language() && language() != next
.language()) {
645 string
Font::toString(bool const toggle
) const
647 string
const lang
= (language() == reset_language
)
648 ? "reset" : language()->lang();
651 os
<< "family " << bits_
.family() << '\n'
652 << "series " << bits_
.series() << '\n'
653 << "shape " << bits_
.shape() << '\n'
654 << "size " << bits_
.size() << '\n'
655 << "emph " << bits_
.emph() << '\n'
656 << "underbar " << bits_
.underbar() << '\n'
657 << "noun " << bits_
.noun() << '\n'
658 << "number " << bits_
.number() << '\n'
659 << "color " << bits_
.color() << '\n'
660 << "language " << lang
<< '\n'
661 << "toggleall " << convert
<string
>(toggle
);
666 bool Font::fromString(string
const & data
, bool & toggle
)
668 istringstream
is(data
);
676 token
= lex
.getString();
678 if (token
.empty() || !lex
.next())
681 if (token
== "family") {
682 int const next
= lex
.getInteger();
683 bits_
.setFamily(FontFamily(next
));
685 } else if (token
== "series") {
686 int const next
= lex
.getInteger();
687 bits_
.setSeries(FontSeries(next
));
689 } else if (token
== "shape") {
690 int const next
= lex
.getInteger();
691 bits_
.setShape(FontShape(next
));
693 } else if (token
== "size") {
694 int const next
= lex
.getInteger();
695 bits_
.setSize(FontSize(next
));
697 } else if (token
== "emph" || token
== "underbar" ||
698 token
== "noun" || token
== "number") {
700 int const next
= lex
.getInteger();
701 FontState
const misc
= FontState(next
);
705 else if (token
== "underbar")
706 bits_
.setUnderbar(misc
);
707 else if (token
== "noun")
709 else if (token
== "number")
710 bits_
.setNumber(misc
);
712 } else if (token
== "color") {
713 int const next
= lex
.getInteger();
714 bits_
.setColor(ColorCode(next
));
717 } else if (token == "background") {
718 int const next = lex.getInteger();
719 bits_.setBackground(ColorCode(next));
722 } else if (token
== "language") {
723 string
const next
= lex
.getString();
724 setLanguage(languages
.getLanguage(next
));
726 } else if (token
== "toggleall") {
727 toggle
= lex
.getBool();
730 // Unrecognised token
740 void Font::validate(LaTeXFeatures
& features
) const
742 BufferParams
const & bparams
= features
.bufferParams();
743 Language
const * doc_language
= bparams
.language
;
745 if (bits_
.noun() == FONT_ON
) {
746 LYXERR(Debug::LATEX
, "font.noun: " << bits_
.noun());
747 features
.require("noun");
748 LYXERR(Debug::LATEX
, "Noun enabled. Font: " << to_utf8(stateText(0)));
750 switch (bits_
.color()) {
754 // probably we should put here all interface colors used for
755 // font displaying! For now I just add this ones I know of (Jug)
757 case Color_notelabel
:
760 features
.require("color");
761 LYXERR(Debug::LATEX
, "Color enabled. Font: " << to_utf8(stateText(0)));
764 // FIXME: Do something for background and soul package?
766 if (lang_
->babel() != doc_language
->babel() &&
767 lang_
!= ignore_language
&&
768 lang_
!= latex_language
)
770 features
.useLanguage(lang_
);
771 LYXERR(Debug::LATEX
, "Found language " << lang_
->lang());
776 ostream
& operator<<(ostream
& os
, FontState fms
)
778 return os
<< int(fms
);
782 ostream
& operator<<(ostream
& os
, FontInfo
const & f
)
785 << " family " << f
.family()
786 << " series " << f
.series()
787 << " shape " << f
.shape()
788 << " size " << f
.size()
789 << " color " << f
.color()
790 // FIXME: uncomment this when we support background.
791 //<< " background " << f.background()
792 << " emph " << f
.emph()
793 << " underbar " << f
.underbar()
794 << " noun " << f
.noun()
795 << " number " << f
.number();
799 ostream
& operator<<(ostream
& os
, Font
const & font
)
801 return os
<< font
.bits_
802 << " lang: " << (font
.lang_
? font
.lang_
->lang() : 0);