From 074508d0642887cdbd9d3c3e2835fb18b5c176ed Mon Sep 17 00:00:00 2001 From: Juergen Spitzmueller Date: Thu, 11 Jul 2019 13:21:32 +0200 Subject: [PATCH] Add support for \babelfont This is a higher-level (non-TeX) font interface of babel that draws on, but is supposed to be used rather than, fontspec with babel and XeTeX/ LuaTeX. File format change. Addresses: #11614 --- development/FORMAT | 3 + lib/lyx2lyx/lyx_2_4.py | 125 +++++++++++++++++++++++++++++++++++++-- src/BufferParams.cpp | 54 +++++++++++++---- src/tex2lyx/Preamble.cpp | 51 ++++++++++++++++ src/version.h | 4 +- 5 files changed, 217 insertions(+), 20 deletions(-) diff --git a/development/FORMAT b/development/FORMAT index cd66ae7235..2f4097c3bf 100644 --- a/development/FORMAT +++ b/development/FORMAT @@ -7,6 +7,9 @@ changes happened in particular if possible. A good example would be ----------------------- +2019-07-11 Jürgen Spitzmüller + * Format incremented to 579: Add support for \babelfont. + 2019-06-23 Jürgen Spitzmüller * Format incremented to 578: Add support for Discourse Representation Structures in the Linguistics module (using drs package). diff --git a/lib/lyx2lyx/lyx_2_4.py b/lib/lyx2lyx/lyx_2_4.py index 5dd9a1bbee..0849057655 100644 --- a/lib/lyx2lyx/lyx_2_4.py +++ b/lib/lyx2lyx/lyx_2_4.py @@ -37,11 +37,11 @@ from parser_tools import (count_pars_in_inset, del_token, find_end_of_inset, # is_in_inset, set_bool_value # find_tokens, check_token -from lyx2lyx_tools import (put_cmd_in_ert, add_to_preamble, lyx2latex, - revert_language, revert_flex_inset) -# revert_font_attrs, insert_to_preamble, latex_length +from lyx2lyx_tools import (put_cmd_in_ert, add_to_preamble, insert_to_preamble, lyx2latex, + revert_language, revert_flex_inset, str2bool) +# revert_font_attrs, latex_length # get_ert, lyx2verbatim, length_in_bp, convert_info_insets -# revert_flex_inset, hex2ratio, str2bool +# revert_flex_inset, hex2ratio #################################################################### # Private helper functions @@ -2172,6 +2172,117 @@ def revert_drs(document): i = beginPlain + +def revert_babelfont(document): + " Reverts the use of \\babelfont to user preamble " + + i = find_token(document.header, '\\use_non_tex_fonts', 0) + if i == -1: + document.warning("Malformed LyX document: Missing \\use_non_tex_fonts.") + return + if not str2bool(get_value(document.header, "\\use_non_tex_fonts", i)): + return + i = find_token(document.header, '\\language_package', 0) + if i == -1: + document.warning("Malformed LyX document: Missing \\language_package.") + return + if get_value(document.header, "\\language_package", 0) != "babel": + return + + # check font settings + # defaults + roman = sans = typew = "default" + osf = False + sf_scale = tt_scale = 100.0 + + j = find_token(document.header, "\\font_roman", 0) + if j == -1: + document.warning("Malformed LyX document: Missing \\font_roman.") + else: + # We need to use this regex since split() does not handle quote protection + romanfont = re.findall(r'[^"\s]\S*|".+?"', document.header[j]) + roman = romanfont[2].strip('"') + romanfont[2] = '"default"' + document.header[j] = " ".join(romanfont) + + j = find_token(document.header, "\\font_sans", 0) + if j == -1: + document.warning("Malformed LyX document: Missing \\font_sans.") + else: + # We need to use this regex since split() does not handle quote protection + sansfont = re.findall(r'[^"\s]\S*|".+?"', document.header[j]) + sans = sansfont[2].strip('"') + sansfont[2] = '"default"' + document.header[j] = " ".join(sansfont) + + j = find_token(document.header, "\\font_typewriter", 0) + if j == -1: + document.warning("Malformed LyX document: Missing \\font_typewriter.") + else: + # We need to use this regex since split() does not handle quote protection + ttfont = re.findall(r'[^"\s]\S*|".+?"', document.header[j]) + typew = ttfont[2].strip('"') + ttfont[2] = '"default"' + document.header[j] = " ".join(ttfont) + + i = find_token(document.header, "\\font_osf", 0) + if i == -1: + document.warning("Malformed LyX document: Missing \\font_osf.") + else: + osf = str2bool(get_value(document.header, "\\font_osf", i)) + + j = find_token(document.header, "\\font_sf_scale", 0) + if j == -1: + document.warning("Malformed LyX document: Missing \\font_sf_scale.") + else: + sfscale = document.header[j].split() + val = sfscale[2] + sfscale[2] = "100" + document.header[j] = " ".join(sfscale) + try: + # float() can throw + sf_scale = float(val) + except: + document.warning("Invalid font_sf_scale value: " + val) + + j = find_token(document.header, "\\font_tt_scale", 0) + if j == -1: + document.warning("Malformed LyX document: Missing \\font_tt_scale.") + else: + ttscale = document.header[j].split() + val = ttscale[2] + ttscale[2] = "100" + document.header[j] = " ".join(ttscale) + try: + # float() can throw + tt_scale = float(val) + except: + document.warning("Invalid font_tt_scale value: " + val) + + # set preamble stuff + pretext = ['%% This document must be processed with xelatex or lualatex!'] + pretext.append('\\AtBeginDocument{%') + if roman != "default": + pretext.append('\\babelfont{rm}[Mapping=tex-text]{' + roman + '}') + if sans != "default": + sf = '\\babelfont{sf}[' + if sf_scale != 100.0: + sf += 'Scale=' + str(sf_scale / 100.0) + ',' + sf += 'Mapping=tex-text]{' + sans + '}' + pretext.append(sf) + if typew != "default": + tw = '\\babelfont{tt}' + if tt_scale != 100.0: + tw += '[Scale=' + str(tt_scale / 100.0) + ']' + tw += '{' + typew + '}' + pretext.append(tw) + if osf: + pretext.append('\\defaultfontfeatures{Numbers=OldStyle}') + pretext.append('}') + insert_to_preamble(document, pretext) + + + ## # Conversion hub # @@ -2211,10 +2322,12 @@ convert = [ [575, [convert_lineno]], [576, []], [577, [convert_linggloss]], - [578, []] + [578, []], + [579, []] ] -revert = [[577, [revert_drs]], +revert = [[578, [revert_babelfont]], + [577, [revert_drs]], [576, [revert_linggloss, revert_subexarg]], [575, [revert_new_languages]], [574, [revert_lineno]], diff --git a/src/BufferParams.cpp b/src/BufferParams.cpp index d98f768b36..d4e2ca4e85 100644 --- a/src/BufferParams.cpp +++ b/src/BufferParams.cpp @@ -1763,7 +1763,8 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features, os << from_ascii(ams); if (useNonTeXFonts) { - if (!features.isProvided("fontspec")) + // Babel loads fontspec itself + if (!features.isProvided("fontspec") && !features.useBabel()) os << "\\usepackage{fontspec}\n"; if (features.mustProvide("unicode-math") && features.isAvailable("unicode-math")) @@ -1781,8 +1782,9 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features, } // font selection must be done before loading fontenc.sty + // but after babel with non-TeX fonts string const fonts = loadFonts(features); - if (!fonts.empty()) + if (!fonts.empty() && (!features.useBabel() || !useNonTeXFonts)) os << from_utf8(fonts); if (fonts_default_family != "default") @@ -2313,6 +2315,10 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features, if (contains(features.getBabelPostsettings(), from_ascii("thai.ldf"))) writeEncodingPreamble(os, features); + // font selection must be done after babel with non-TeX fonts + if (!fonts.empty() && features.useBabel() && useNonTeXFonts) + os << from_utf8(fonts); + if (features.isRequired("bicaption")) os << "\\usepackage{bicaption}\n"; if (!listings_params.empty() @@ -3391,36 +3397,60 @@ string const BufferParams::loadFonts(LaTeXFeatures & features) const // variants are understood by both engines. However, // we want to provide support for at least TeXLive 2009 // (for XeTeX; LuaTeX is only supported as of v.2) + // Babel has its own higher-level interface on top of + // fontspec that is to be used. + bool const babel = features.useBabel(); string const texmapping = (features.runparams().flavor == OutputParams::XETEX) ? "Mapping=tex-text" : "Ligatures=TeX"; if (fontsRoman() != "default") { - os << "\\setmainfont[" << texmapping; + if (babel) + os << "\\babelfont{rm}["; + else + os << "\\setmainfont["; + os << texmapping; if (fonts_old_figures) os << ",Numbers=OldStyle"; os << "]{" << parseFontName(fontsRoman()) << "}\n"; } if (fontsSans() != "default") { string const sans = parseFontName(fontsSans()); - if (fontsSansScale() != 100) - os << "\\setsansfont[Scale=" + if (fontsSansScale() != 100) { + if (babel) + os << "\\babelfont{sf}"; + else + os << "\\setsansfont"; + os << "[Scale=" << float(fontsSansScale()) / 100 << "," << texmapping << "]{" << sans << "}\n"; - else - os << "\\setsansfont[" << texmapping << "]{" + } else { + if (babel) + os << "\\babelfont{sf}["; + else + os << "\\setsansfont["; + os << texmapping << "]{" << sans << "}\n"; + } } if (fontsTypewriter() != "default") { string const mono = parseFontName(fontsTypewriter()); - if (fontsTypewriterScale() != 100) - os << "\\setmonofont[Scale=" + if (fontsTypewriterScale() != 100) { + if (babel) + os << "\\babelfont{tt}"; + else + os << "\\setmonofont"; + os << "[Scale=" << float(fontsTypewriterScale()) / 100 << "]{" << mono << "}\n"; - else - os << "\\setmonofont{" - << mono << "}\n"; + } else { + if (babel) + os << "\\babelfont{tt}{"; + else + os << "\\setmonofont{"; + os << mono << "}\n"; + } } return os.str(); } diff --git a/src/tex2lyx/Preamble.cpp b/src/tex2lyx/Preamble.cpp index 5fc5ca06a2..739af0f86c 100644 --- a/src/tex2lyx/Preamble.cpp +++ b/src/tex2lyx/Preamble.cpp @@ -1716,6 +1716,57 @@ void Preamble::parse(Parser & p, string const & forceclass, continue; } + if (t.cs() == "babelfont") { + xetex = true; + h_use_non_tex_fonts = true; + h_language_package = "babel"; + if (h_inputencoding == "auto-legacy") + p.setEncoding("UTF-8"); + // we don't care about the lang option + string const lang = p.hasOpt() ? p.getArg('[', ']') : string(); + string const family = p.getArg('{', '}'); + string const fontopts = p.hasOpt() ? p.getArg('[', ']') : string(); + string const fontname = p.getArg('{', '}'); + if (lang.empty() && family == "rm") { + h_font_roman[1] = fontname; + continue; + } else if (lang.empty() && (family == "sf" || family == "tt")) { + // LyX currently only supports the scale option + string scale; + if (!fontopts.empty()) { + // check if the option contains a scaling, if yes, extract it + string::size_type pos = fontopts.find("Scale"); + if (pos != string::npos) { + string::size_type i = fontopts.find(',', pos); + if (i == string::npos) + scale_as_percentage(fontopts.substr(pos + 1), scale); + else + scale_as_percentage(fontopts.substr(pos, i - pos), scale); + } + } + if (family == "sf") { + if (!scale.empty()) + h_font_sf_scale[1] = scale; + h_font_sans[1] = fontname; + } else { + if (!scale.empty()) + h_font_tt_scale[1] = scale; + h_font_typewriter[1] = fontname; + } + continue; + } else { + // not rm, sf or tt or lang specific + h_preamble << '\\' << t.cs(); + if (!lang.empty()) + h_preamble << '[' << lang << ']'; + h_preamble << '{' << family << '}'; + if (!fontopts.empty()) + h_preamble << '[' << fontopts << ']'; + h_preamble << '{' << fontname << '}' << '\n'; + continue; + } + } + if (t.cs() == "date") { string argument = p.getArg('{', '}'); if (argument.empty()) diff --git a/src/version.h b/src/version.h index ce41bf00a9..386d72efa8 100644 --- a/src/version.h +++ b/src/version.h @@ -32,8 +32,8 @@ extern char const * const lyx_version_info; // Do not remove the comment below, so we get merge conflict in // independent branches. Instead add your own. -#define LYX_FORMAT_LYX 578 // spitz: drs -#define LYX_FORMAT_TEX2LYX 578 +#define LYX_FORMAT_LYX 579 // spitz: babelfont +#define LYX_FORMAT_TEX2LYX 579 #if LYX_FORMAT_TEX2LYX != LYX_FORMAT_LYX #ifndef _MSC_VER -- 2.39.5