From f22213a04fe53dd128f11db9b228623b3fc3dda7 Mon Sep 17 00:00:00 2001 From: Juergen Spitzmueller Date: Sun, 4 Mar 2018 16:46:31 +0100 Subject: [PATCH] tex2lyx: support biblatex (qualified citation lists and multibib not yet supported) --- src/tex2lyx/Preamble.cpp | 53 ++++++++- src/tex2lyx/Preamble.h | 5 + src/tex2lyx/TODO.txt | 28 ----- src/tex2lyx/tex2lyx.cpp | 2 + src/tex2lyx/tex2lyx.h | 1 + src/tex2lyx/text.cpp | 233 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 292 insertions(+), 30 deletions(-) diff --git a/src/tex2lyx/Preamble.cpp b/src/tex2lyx/Preamble.cpp index 39796323ed..48c0033a62 100644 --- a/src/tex2lyx/Preamble.cpp +++ b/src/tex2lyx/Preamble.cpp @@ -1046,10 +1046,45 @@ void Preamble::handle_package(Parser &p, string const & name, h_biblio_options = join(options, ","); } + else if (name == "biblatex") { + h_biblio_style = "plainnat"; + h_cite_engine = "biblatex"; + h_cite_engine_type = "authoryear"; + string opt; + vector::iterator it = + find(options.begin(), options.end(), "natbib"); + if (it != options.end()) { + options.erase(it); + h_cite_engine = "biblatex-natbib"; + } else { + opt = process_keyval_opt(options, "natbib"); + if (opt == "true") + h_cite_engine = "biblatex-natbib"; + } + opt = process_keyval_opt(options, "style"); + if (!opt.empty()) { + h_biblatex_citestyle = opt; + h_biblatex_bibstyle = opt; + } else { + opt = process_keyval_opt(options, "citestyle"); + if (!opt.empty()) + h_biblatex_citestyle = opt; + opt = process_keyval_opt(options, "bibstyle"); + if (!opt.empty()) + h_biblatex_bibstyle = opt; + } + if (!options.empty()) { + h_biblio_options = join(options, ","); + options.clear(); + } + } + else if (name == "jurabib") { h_biblio_style = "jurabib"; h_cite_engine = "jurabib"; h_cite_engine_type = "authoryear"; + if (!options.empty()) + h_biblio_options = join(options, ","); } else if (name == "bibtopic") @@ -1270,8 +1305,14 @@ bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiled os << "\\cite_engine " << h_cite_engine << '\n' << "\\cite_engine_type " << h_cite_engine_type << '\n' << "\\biblio_style " << h_biblio_style << "\n" - << "\\use_bibtopic " << h_use_bibtopic << "\n" - << "\\use_indices " << h_use_indices << "\n" + << "\\use_bibtopic " << h_use_bibtopic << "\n"; + if (!h_biblio_options.empty()) + os << "\\biblio_options " << h_biblio_options << "\n"; + if (!h_biblatex_bibstyle.empty()) + os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n"; + if (!h_biblatex_citestyle.empty()) + os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n"; + os << "\\use_indices " << h_use_indices << "\n" << "\\paperorientation " << h_paperorientation << '\n' << "\\suppress_date " << h_suppress_date << '\n' << "\\justification " << h_justification << '\n' @@ -1566,6 +1607,14 @@ void Preamble::parse(Parser & p, string const & forceclass, p.skip_spaces(); } + else if (t.cs() == "addbibresource") + biblatex_bibliographies.push_back(removeExtension(p.getArg('{', '}'))); + + else if (t.cs() == "bibliography") { + vector bibs = getVectorFromString(p.getArg('{', '}')); + biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end()); + } + else if (t.cs() == "RS@ifundefined") { string const name = p.verbatim_item(); string const body1 = p.verbatim_item(); diff --git a/src/tex2lyx/Preamble.h b/src/tex2lyx/Preamble.h index 288dd87bf6..389bf5e48a 100644 --- a/src/tex2lyx/Preamble.h +++ b/src/tex2lyx/Preamble.h @@ -104,6 +104,8 @@ public: static const char * const polyglossia_languages[]; /// the same as polyglossia_languages with .lyx names static const char * const coded_polyglossia_languages[]; + /// + std::vector biblatex_bibliographies; private: /// @@ -132,6 +134,9 @@ private: std::ostringstream h_preamble; std::string h_backgroundcolor; std::string h_biblio_style; + std::string h_biblio_options; + std::string h_biblatex_bibstyle; + std::string h_biblatex_citestyle; std::string h_bibtex_command; std::string h_boxbgcolor; std::string h_cite_engine; diff --git a/src/tex2lyx/TODO.txt b/src/tex2lyx/TODO.txt index c448a45569..f5744e7f93 100644 --- a/src/tex2lyx/TODO.txt +++ b/src/tex2lyx/TODO.txt @@ -102,34 +102,6 @@ Format LaTeX feature LyX feature - cjkangle (angle brackets) \begin_inset Quotes k.. 526 Plural and capitalized refstyles InsetRef -528 Biblatex - \usepackage{biblatex} \cite_engine biblatex - ...[...natbib=true...]... \cite_engine biblatex-natbib - ...[...style=...]... \biblatex_bibstyle - \biblatex_citestyle - ...[...bibstyle=...]... \biblatex_bibstyle - ...[...citestyle=...]... \biblatex_citestyle - ...[......]... \biblio_options - \printbibliography[] \begin_inset CommandInset bibtex - biblatexopts "" - \addbibresource{file.bib} \begin_inset CommandInset bibtex - [multiple possible!] bibfiles "...,file,..." [NB: strip ext!] - \bibliography{file1,file2,...} \begin_inset CommandInset bibtex - bibfiles "...,file1,file2,..." - \begin_inset CommandInset citation - \Cite LatexCmd Cite - \cite* LatexCmd citeyear - \citeyear LatexCmd citebyear - \{T,t}extcite LatexCmd {C,c}itet - \{P,p}arencite LatexCmd {C,c}itep - \parencite* LatexCmd citeyearpar - \{S,s}martcite LatexCmd {F,f}ootcite - \{F,f}ootcite LatexCmd {F,f}ootcite - \{A,a}utocite LatexCmd {A,a}utocite - \citecite[*] LatexCmd citecite[*] - \fullcite LatexCmd fullcite - \footfullcite LatexCmd footfullcite - \supercite LatexCmd supercite 531 Biblatex "qualified citation lists" \cites(pre)(post)[pre1][post1]{key1}[pre2][post2]{key2}... \begin_inset CommandInset citation diff --git a/src/tex2lyx/tex2lyx.cpp b/src/tex2lyx/tex2lyx.cpp index 1db7d0ddd8..26f1f666d3 100644 --- a/src/tex2lyx/tex2lyx.cpp +++ b/src/tex2lyx/tex2lyx.cpp @@ -874,6 +874,8 @@ bool tex2lyx(idocstream & is, ostream & os, string const & encoding, context.font.language = preamble.defaultLanguage(); // parse the main text parse_text(p, ss, FLAG_END, true, context); + // check if we need a commented bibtex inset (biblatex) + check_comment_bib(ss, context); if (Context::empty) // Empty document body. LyX needs at least one paragraph. context.check_layout(ss); diff --git a/src/tex2lyx/tex2lyx.h b/src/tex2lyx/tex2lyx.h index a1bd9b42b8..4c12099bde 100644 --- a/src/tex2lyx/tex2lyx.h +++ b/src/tex2lyx/tex2lyx.h @@ -49,6 +49,7 @@ std::string translate_len(std::string const &); void parse_text(Parser & p, std::ostream & os, unsigned flags, bool outer, Context & context); +void check_comment_bib(std::ostream & os, Context & context); /*! * Parses a subdocument, usually useful in insets (whence the name). diff --git a/src/tex2lyx/text.cpp b/src/tex2lyx/text.cpp index 71eb6a52f0..8b8dc6180f 100644 --- a/src/tex2lyx/text.cpp +++ b/src/tex2lyx/text.cpp @@ -184,6 +184,18 @@ char const * const known_jurabib_commands[] = { "cite", "citet", "citep", // "footciteauthor", "footciteyear", "footciteyearpar", "citefield", "citetitle", 0 }; +/*! + * biblatex commands. + * Known starred forms: \cite*, \citeauthor*, \Citeauthor*, \parencite*, \citetitle*. + */ +char const * const known_biblatex_commands[] = { "cite", "Cite", "textcite", "Textcite", +"parencite", "Parencite", "citeauthor", "Citeauthor", "citeyear", "smartcite", "Smartcite", + "footcite", "Footcite", "autocite", "Autocite", "citetitle", "fullcite", "footfullcite", +"supercite", 0 }; + +// Whether we need to insert a bibtex inset in a comment +bool need_commentbib = false; + /// LaTeX names for quotes char const * const known_quotes[] = { "dq", "guillemotleft", "flqq", "og", "guillemotright", "frqq", "fg", "glq", "glqq", "textquoteleft", "grq", "grqq", @@ -2482,6 +2494,11 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, string bibliographystyle; bool const use_natbib = isProvided("natbib"); bool const use_jurabib = isProvided("jurabib"); + bool const use_biblatex = isProvided("biblatex") + && preamble.citeEngine() != "biblatex-natbib"; + bool const use_biblatex_natbib = isProvided("biblatex-natbib") + || (isProvided("biblatex") && preamble.citeEngine() == "biblatex-natbib"); + need_commentbib = use_biblatex || use_biblatex_natbib; string last_env; // it is impossible to determine the correct encoding for non-CJK Japanese. @@ -3819,6 +3836,157 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, preamble.citeEngine("natbib"); } + else if (use_biblatex + && is_known(t.cs(), known_biblatex_commands) + && ((t.cs() == "cite" + || t.cs() == "citeauthor" + || t.cs() == "Citeauthor" + || t.cs() == "parencite" + || t.cs() == "citetitle") + || p.next_token().asInput() != "*")) { + context.check_layout(os); + string command = t.cs(); + if (p.next_token().asInput() == "*") { + command += '*'; + p.get_token(); + } + + // text before the citation + string before; + // text after the citation + string after; + get_cite_arguments(p, true, before, after); + + // These use natbib cmd names in LyX + // for inter-citeengine compativility + if (command == "citeyear") + command = "citebyear"; + else if (command == "cite*") + command = "citeyear"; + else if (command == "textcite") + command = "citet"; + else if (command == "Textcite") + command = "Citet"; + else if (command == "parencite") + command = "citep"; + else if (command == "Parencite") + command = "Citep"; + else if (command == "parencite*") + command = "citeyearpar"; + else if (command == "smartcite") + command = "footcite"; + else if (command == "Smartcite") + command = "Footcite"; + + if (before.empty() && after == "[]") + // avoid \cite[]{a} + after.erase(); + else if (before == "[]" && after == "[]") { + // avoid \cite[][]{a} + before.erase(); + after.erase(); + } + // remove the brackets around after and before + if (!after.empty()) { + after.erase(0, 1); + after.erase(after.length() - 1, 1); + after = convert_command_inset_arg(after); + } + if (!before.empty()) { + before.erase(0, 1); + before.erase(before.length() - 1, 1); + before = convert_command_inset_arg(before); + } + begin_command_inset(os, "citation", command); + os << "after " << '"' << after << '"' << "\n"; + os << "before " << '"' << before << '"' << "\n"; + os << "key \"" + << convert_command_inset_arg(p.verbatim_item()) + << "\"\n" + << "literal \"true\"\n"; + end_inset(os); + // Need to set the cite engine if biblatex is loaded by + // the document class directly + if (preamble.citeEngine() == "basic") + preamble.citeEngine("biblatex"); + } + + else if (use_biblatex_natbib + && (is_known(t.cs(), known_biblatex_commands) + || is_known(t.cs(), known_natbib_commands)) + && ((t.cs() == "cite" || t.cs() == "citet" || t.cs() == "Citet" + || t.cs() == "citep" || t.cs() == "Citep" || t.cs() == "citealt" + || t.cs() == "Citealt" || t.cs() == "citealp" || t.cs() == "Citealp" + || t.cs() == "citeauthor" || t.cs() == "Citeauthor" + || t.cs() == "parencite" || t.cs() == "citetitle") + || p.next_token().asInput() != "*")) { + context.check_layout(os); + string command = t.cs(); + if (p.next_token().asInput() == "*") { + command += '*'; + p.get_token(); + } + + // text before the citation + string before; + // text after the citation + string after; + get_cite_arguments(p, true, before, after); + + // These use natbib cmd names in LyX + // for inter-citeengine compativility + if (command == "citeyear") + command = "citebyear"; + else if (command == "cite*") + command = "citeyear"; + else if (command == "textcite") + command = "citet"; + else if (command == "Textcite") + command = "Citet"; + else if (command == "parencite") + command = "citep"; + else if (command == "Parencite") + command = "Citep"; + else if (command == "parencite*") + command = "citeyearpar"; + else if (command == "smartcite") + command = "footcite"; + else if (command == "Smartcite") + command = "Footcite"; + + if (before.empty() && after == "[]") + // avoid \cite[]{a} + after.erase(); + else if (before == "[]" && after == "[]") { + // avoid \cite[][]{a} + before.erase(); + after.erase(); + } + // remove the brackets around after and before + if (!after.empty()) { + after.erase(0, 1); + after.erase(after.length() - 1, 1); + after = convert_command_inset_arg(after); + } + if (!before.empty()) { + before.erase(0, 1); + before.erase(before.length() - 1, 1); + before = convert_command_inset_arg(before); + } + begin_command_inset(os, "citation", command); + os << "after " << '"' << after << '"' << "\n"; + os << "before " << '"' << before << '"' << "\n"; + os << "key \"" + << convert_command_inset_arg(p.verbatim_item()) + << "\"\n" + << "literal \"true\"\n"; + end_inset(os); + // Need to set the cite engine if biblatex is loaded by + // the document class directly + if (preamble.citeEngine() == "basic") + preamble.citeEngine("biblatex-natbib"); + } + else if (use_jurabib && is_known(t.cs(), known_jurabib_commands) && (t.cs() == "cite" || p.next_token().asInput() != "*")) { @@ -4463,6 +4631,46 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, end_inset(os); } + else if (t.cs() == "printbibliography") { + context.check_layout(os); + string BibOpts; + string bbloptions = p.hasOpt() ? p.getArg('[', ']') : string(); + vector opts = getVectorFromString(bbloptions); + vector::iterator it = + find(opts.begin(), opts.end(), "heading=bibintoc"); + if (it != opts.end()) { + opts.erase(it); + BibOpts = "bibtotoc"; + } + bbloptions = getStringFromVector(opts); + begin_command_inset(os, "bibtex", "bibtex"); + if (!btprint.empty()) { + os << "btprint " << '"' << "btPrintAll" << '"' << "\n"; + // clear the string because the next BibTeX inset can be without the + // \nocite{*} option + btprint.clear(); + } + string bibfiles; + for (auto const & bf : preamble.biblatex_bibliographies) { + if (!bibfiles.empty()) + bibfiles += ","; + bibfiles += normalize_filename(bf); + } + if (!bibfiles.empty()) + os << "bibfiles " << '"' << bibfiles << '"' << "\n"; + // Do we have addcontentsline? + if (contentslineContent == "\\refname") { + BibOpts = "bibtotoc"; + // clear string because next BibTeX inset can be without addcontentsline + contentslineContent.clear(); + } + os << "options " << '"' << BibOpts << '"' << "\n"; + if (!bbloptions.empty()) + os << "biblatexopts " << '"' << bbloptions << '"' << "\n"; + end_inset(os); + need_commentbib = false; + } + else if (t.cs() == "parbox") { // Test whether this is an outer box of a shaded box p.pushPosition(); @@ -5200,6 +5408,31 @@ string guessLanguage(Parser & p, string const & lang) return use->first; } + +void check_comment_bib(ostream & os, Context & context) +{ + if (!need_commentbib) + return; + // We have a bibliography database, but no bibliography with biblatex + // which is completely valid. Insert a bibtex inset in a note. + context.check_layout(os); + begin_inset(os, "Note Note\n"); + os << "status open\n"; + os << "\\begin_layout Plain Layout\n"; + begin_command_inset(os, "bibtex", "bibtex"); + string bibfiles; + for (auto const & bf : preamble.biblatex_bibliographies) { + if (!bibfiles.empty()) + bibfiles += ","; + bibfiles += normalize_filename(bf); + } + if (!bibfiles.empty()) + os << "bibfiles " << '"' << bibfiles << '"' << "\n"; + end_inset(os);// Bibtex + os << "\\end_layout\n"; + end_inset(os);// Note +} + // }]) -- 2.39.2