tex2lyx: support biblatex

(qualified citation lists and multibib not yet supported)
This commit is contained in:
Juergen Spitzmueller 2018-03-04 16:46:31 +01:00
parent 476401a76f
commit f22213a04f
6 changed files with 292 additions and 30 deletions

View File

@ -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<string>::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<string> 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();

View File

@ -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<std::string> 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;

View File

@ -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=<val>...]... \biblatex_bibstyle <val>
\biblatex_citestyle <val>
...[...bibstyle=<val>...]... \biblatex_bibstyle <val>
...[...citestyle=<val>...]... \biblatex_citestyle <val>
...[...<any other opt>...]... \biblio_options <any other opt>
\printbibliography[<opts>] \begin_inset CommandInset bibtex
biblatexopts "<opts>"
\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

View File

@ -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);

View File

@ -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).

View File

@ -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<string> opts = getVectorFromString(bbloptions);
vector<string>::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
}
// }])