#include "Encoding.h"
#include "InsetIterator.h"
#include "Language.h"
-#include "output_xhtml.h"
+#include "xml.h"
#include "Paragraph.h"
#include "TextClass.h"
#include "TocBackend.h"
return cmd;
}
+
+docstring authorsToDocBookAuthorGroup(docstring const & authorsString, XMLStream & xs, Buffer const & buf)
+{
+ // This function closely mimics getAuthorList, but produces DocBook instead of text.
+ // It has been greatly simplified, as the complete list of authors is always produced. No separators are required,
+ // as the output has a database-like shape.
+ // constructName has also been merged within, as it becomes really simple and leads to no copy-paste.
+
+ if (authorsString.empty()) {
+ return docstring();
+ }
+
+ // Split the input list of authors into individual authors.
+ vector<docstring> const authors = getAuthors(authorsString);
+
+ // Retrieve the "et al." variation.
+ string const etal = buf.params().documentClass().getCiteMacro(buf.params().citeEngineType(), "_etal");
+
+ // Output the list of authors.
+ xs << xml::StartTag("authorgroup");
+ auto it = authors.cbegin();
+ auto en = authors.cend();
+ for (size_t i = 0; it != en; ++it, ++i) {
+ xs << xml::StartTag("author");
+ xs << xml::CR();
+ xs << xml::StartTag("personname");
+ xs << xml::CR();
+ docstring name = *it;
+
+ // All authors go in a <personname>. If more structure is known, use it; otherwise (just "et al."), print it as such.
+ if (name == "others") {
+ xs << buf.B_(etal);
+ } else {
+ name_parts parts = nameParts(name);
+ if (! parts.prefix.empty()) {
+ xs << xml::StartTag("honorific");
+ xs << parts.prefix;
+ xs << xml::EndTag("honorific");
+ xs << xml::CR();
+ }
+ if (! parts.prename.empty()) {
+ xs << xml::StartTag("firstname");
+ xs << parts.prename;
+ xs << xml::EndTag("firstname");
+ xs << xml::CR();
+ }
+ if (! parts.surname.empty()) {
+ xs << xml::StartTag("surname");
+ xs << parts.surname;
+ xs << xml::EndTag("surname");
+ xs << xml::CR();
+ }
+ if (! parts.suffix.empty()) {
+ xs << xml::StartTag("othername", "role=\"suffix\"");
+ xs << parts.suffix;
+ xs << xml::EndTag("othername");
+ xs << xml::CR();
+ }
+ }
+
+ xs << xml::EndTag("personname");
+ xs << xml::CR();
+ xs << xml::EndTag("author");
+ xs << xml::CR();
+
+ // Could add an affiliation after <personname>, but not stored in BibTeX.
+ }
+ xs << xml::EndTag("authorgroup");
+ xs << xml::CR();
+
+ return docstring();
+}
+
} // namespace lyx
/// the other way round
std::string citationStyleToString(CitationStyle const &, bool const latex = false);
+/// Transforms the information about authors into a <authorgroup> (directly written to a XMLStream).
+docstring authorsToDocBookAuthorGroup(docstring const & authorsString, XMLStream & xs, Buffer const & buf);
+
/// Class to represent information about a BibTeX or
/// bibliography entry.
#include "LyX.h"
#include "LyXRC.h"
#include "LyXVC.h"
-#include "output_docbook.h"
#include "output.h"
#include "output_latex.h"
-#include "output_xhtml.h"
+#include "output_docbook.h"
#include "output_plaintext.h"
+#include "output_xhtml.h"
#include "Paragraph.h"
#include "ParagraphParameters.h"
#include "ParIterator.h"
updateMacroInstances(OutputUpdate);
ExportStatus const retval =
- writeDocBookSource(ofs, fname.absFileName(), runparams, output);
+ writeDocBookSource(ofs, runparams, output);
if (retval == ExportKilled)
return ExportKilled;
}
-Buffer::ExportStatus Buffer::writeDocBookSource(odocstream & os, string const & fname,
+Buffer::ExportStatus Buffer::writeDocBookSource(odocstream & os,
OutputParams const & runparams,
OutputWhat output) const
{
LaTeXFeatures features(*this, params(), runparams);
validate(features);
+ d->bibinfo_.makeCitationLabels(*this);
d->texrow.reset();
DocumentClass const & tclass = params().documentClass();
- string const & top_element = tclass.latexname();
bool const output_preamble =
output == FullSource || output == OnlyPreamble;
bool const output_body =
output == FullSource || output == OnlyBody;
- if (output_preamble) {
- if (runparams.flavor == OutputParams::DOCBOOK5)
- os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
-
- // FIXME UNICODE
- os << "<!DOCTYPE " << from_ascii(top_element) << ' ';
-
- // FIXME UNICODE
- if (! tclass.class_header().empty())
- os << from_ascii(tclass.class_header());
- else if (runparams.flavor == OutputParams::DOCBOOK5)
- os << "PUBLIC \"-//OASIS//DTD DocBook XML V4.2//EN\" "
- << "\"https://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd\"";
- else
- os << " PUBLIC \"-//OASIS//DTD DocBook V4.2//EN\"";
-
- docstring preamble = params().preamble;
- if (runparams.flavor != OutputParams::DOCBOOK5 ) {
- preamble += "<!ENTITY % output.print.png \"IGNORE\">\n";
- preamble += "<!ENTITY % output.print.pdf \"IGNORE\">\n";
- preamble += "<!ENTITY % output.print.eps \"IGNORE\">\n";
- preamble += "<!ENTITY % output.print.bmp \"IGNORE\">\n";
- }
-
- string const name = runparams.nice
- ? changeExtension(absFileName(), ".sgml") : fname;
- preamble += features.getIncludedFiles(name);
- preamble += features.getLyXSGMLEntities();
+ XMLStream xs(os);
- if (!preamble.empty()) {
- os << "\n [ " << preamble << " ]";
- }
- os << ">\n\n";
+ if (output_preamble) {
+ // XML preamble, no doctype needed.
+ // Not using XMLStream for this, as the root tag would be in the tag stack and make troubles with the error
+ // detection mechanisms (these are called before the end tag is output, and thus interact with the canary
+ // parsep in output_docbook.cpp).
+ os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ << "<!-- This DocBook file was created by LyX " << lyx_version
+ << "\n See http://www.lyx.org/ for more information -->\n";
+
+ // Directly output the root tag, based on the current type of document.
+ string languageCode = params().language->code();
+ string params = "xml:lang=\"" + languageCode + '"'
+ + " xmlns=\"http://docbook.org/ns/docbook\""
+ + " xmlns:xlink=\"http://www.w3.org/1999/xlink\""
+ + " xmlns:m=\"http://www.w3.org/1998/Math/MathML\""
+ + " xmlns:xi=\"http://www.w3.org/2001/XInclude\""
+ + " version=\"5.2\"";
+
+ os << "<" << from_ascii(tclass.docbookroot()) << " " << from_ascii(params) << ">\n";
}
if (output_body) {
- string top = top_element;
- top += " lang=\"";
- if (runparams.flavor == OutputParams::DOCBOOK5)
- top += params().language->code();
- else
- top += params().language->code().substr(0, 2);
- top += '"';
-
- if (!params().options.empty()) {
- top += ' ';
- top += params().options;
- }
-
- os << "<!-- " << ((runparams.flavor == OutputParams::DOCBOOK5)? "XML" : "SGML")
- << " file was created by LyX " << lyx_version
- << "\n See https://www.lyx.org/ for more information -->\n";
-
params().documentClass().counters().reset();
- xml::openTag(os, top);
- os << '\n';
- try {
- docbookParagraphs(text(), *this, os, runparams);
- }
- catch (ConversionException const &) { return ExportKilled; }
- xml::closeTag(os, top_element);
+ // Start to output the document.
+ docbookParagraphs(text(), *this, xs, runparams);
+ }
+
+ if (output_preamble) {
+ // Close the root element.
+ os << "\n</" << from_ascii(tclass.docbookroot()) << ">";
}
return ExportSuccess;
}
// Probably should have some routine with a signature like them.
writePlaintextParagraph(*this,
text().paragraphs()[par_begin], os, runparams, dummy);
- } else if (params().isDocBook()) {
- docbookParagraphs(text(), *this, os, runparams);
+ } else if (runparams.flavor == OutputParams::DOCBOOK5) {
+ XMLStream xs{os};
+ docbookParagraphs(text(), *this, xs, runparams);
} else {
// If we are previewing a paragraph, even if this is the
// child of some other buffer, let's cut the link here,
os << "% "<< _("Plain text does not have a preamble.");
} else
writePlaintextFile(*this, os, runparams);
- } else if (params().isDocBook()) {
- writeDocBookSource(os, absFileName(), runparams, output);
+ } else if (runparams.flavor == OutputParams::DOCBOOK5) {
+ writeDocBookSource(os, runparams, output);
} else {
// latex or literate
otexstream ots(os);
return ExportKilled;
} else if (backend_format == "lyx")
writeFile(FileName(filename));
- // Docbook backend
- else if (params().isDocBook()) {
+ // DocBook backend
+ else if (backend_format == "docbook5") {
+ runparams.flavor = OutputParams::DOCBOOK5;
runparams.nice = !put_in_tempdir;
if (makeDocBookFile(FileName(filename), runparams) == ExportKilled)
return ExportKilled;
OutputParams const & runparams_in,
OutputWhat output = FullSource) const;
///
- ExportStatus writeDocBookSource(odocstream & os, std::string const & filename,
+ ExportStatus writeDocBookSource(odocstream & os,
OutputParams const & runparams_in,
OutputWhat output = FullSource) const;
///
Converter::Converter(string const & f, string const & t,
string const & c, string const & l)
: from_(f), to_(t), command_(c), flags_(l),
- From_(nullptr), To_(nullptr), latex_(false), xml_(false),
+ From_(nullptr), To_(nullptr), latex_(false), docbook_(false),
need_aux_(false), nice_(false), need_auth_(false)
{}
latex_flavor_ = flag_value.empty() ?
"latex" : flag_value;
} else if (flag_name == "xml")
- xml_ = true;
+ docbook_ = true;
else if (flag_name == "needaux") {
need_aux_ = true;
latex_flavor_ = flag_value.empty() ?
if (conv.latex_flavor() == "pdflatex")
return OutputParams::PDFLATEX;
}
- if (conv.xml())
+ if (conv.docbook())
return OutputParams::DOCBOOK5;
}
return buffer ? buffer->params().getOutputFlavor()
///
std::string const latex_flavor() const { return latex_flavor_; }
///
- bool xml() const { return xml_; }
+ bool docbook() const { return docbook_; }
///
bool need_aux() const { return need_aux_; }
/// Return whether or not the needauth option is set for this converter
bool latex_;
/// The latex derivate
trivstring latex_flavor_;
- /// The converter is xml
- bool xml_;
+ /// The converter is DocBook
+ bool docbook_;
/// This converter needs the .aux files
bool need_aux_;
/// we need a "nice" file from the backend, c.f. OutputParams.nice.
string const & listName, std::string const & listCmd,
string const & refPrefix, std::string const & allowedplacement,
string const & htmlTag, string const & htmlAttrib,
- docstring const & htmlStyle, string const & required,
+ docstring const & htmlStyle, string const & docbookTag,
+ string const & docbookAttr, string const & required,
bool usesfloat, bool ispredefined,
bool allowswide, bool allowssideways)
: floattype_(type), placement_(placement), ext_(ext), within_(within),
refprefix_(refPrefix), allowedplacement_(allowedplacement), required_(required),
usesfloatpkg_(usesfloat), ispredefined_(ispredefined),
allowswide_(allowswide), allowssideways_(allowssideways),
- html_tag_(htmlTag), html_attrib_(htmlAttrib), html_style_(htmlStyle)
+ html_tag_(htmlTag), html_attrib_(htmlAttrib), html_style_(htmlStyle),
+ docbook_tag_(docbookTag), docbook_attr_(docbookAttr)
{}
}
+string const & Floating::docbookAttr() const
+{
+ return docbook_attr_;
+}
+
+
+string const & Floating::docbookTag(bool hasTitle) const
+{
+ docbook_tag_ = "";
+ if (floattype_ == "figure") {
+ docbook_tag_ = hasTitle ? "figure" : "informalfigure";
+ } else if (floattype_ == "table") {
+ docbook_tag_ = hasTitle ? "table" : "informaltable";
+ } else if (floattype_ == "algorithm") {
+ // TODO: no good translation for now! Figures are the closest match, as they can contain text.
+ // Solvable as soon as https://github.com/docbook/docbook/issues/157 has a definitive answer.
+ docbook_tag_ = "figure";
+ }
+ return docbook_tag_;
+}
+
+
+string const & Floating::docbookCaption() const
+{
+ docbook_caption_ = "";
+ if (floattype_ == "figure") {
+ docbook_caption_ = "title";
+ } else if (floattype_ == "table") {
+ docbook_caption_ = "caption";
+ } else if (floattype_ == "algorithm") {
+ // TODO: no good translation for now! Figures are the closest match, as they can contain text.
+ // Solvable as soon as https://github.com/docbook/docbook/issues/157 has a definitive answer.
+ docbook_caption_ = "title";
+ }
+ return docbook_caption_;
+}
+
+
} // namespace lyx
std::string const & listName, std::string const & listCmd,
std::string const & refPrefix, std::string const & allowedplacement,
std::string const & htmlType, std::string const & htmlClass,
- docstring const & htmlStyle, std::string const & required,
- bool usesfloat, bool isprefined,
- bool allowswide, bool allowssideways);
+ docstring const & htmlStyle, std::string const & docbookTag,
+ std::string const & docbookAttr, std::string const & required,
+ bool usesfloat, bool isprefined, bool allowswide, bool allowssideways);
///
std::string const & floattype() const { return floattype_; }
///
std::string const & htmlAttrib() const;
/// tag type, defaults to "div"
std::string const & htmlTag() const;
+ ///
+ std::string const & docbookTag(bool hasTitle = false) const;
+ ///
+ std::string const & docbookAttr() const;
+ ///
+ std::string const & docbookCaption() const;
private:
///
std::string defaultCSSClass() const;
mutable std::string defaultcssclass_;
///
docstring html_style_;
+ /// DocBook tag
+ mutable std::string docbook_tag_;
+ /// attribute (mostly, role)
+ mutable std::string docbook_caption_;
+ /// caption tag (mostly, either caption or title)
+ std::string docbook_attr_;
};
namespace lyx {
-/// Special value of toclevel for layouts that to not belong in a TOC
+/// Special value of toclevel for layouts that do not belong to a TOC
const int Layout::NOT_IN_TOC = -1000;
// The order of the LayoutTags enum is no more important. [asierra300396]
LT_HTMLPREAMBLE,
LT_HTMLSTYLE,
LT_HTMLFORCECSS,
+ LT_DOCBOOKTAG,
+ LT_DOCBOOKATTR,
+ LT_DOCBOOKININFO,
+ LT_DOCBOOKWRAPPERTAG,
+ LT_DOCBOOKWRAPPERATTR,
+ LT_DOCBOOKSECTIONTAG,
+ LT_DOCBOOKITEMWRAPPERTAG,
+ LT_DOCBOOKITEMWRAPPERATTR,
+ LT_DOCBOOKITEMTAG,
+ LT_DOCBOOKITEMATTR,
+ LT_DOCBOOKITEMLABELTAG,
+ LT_DOCBOOKITEMLABELATTR,
+ LT_DOCBOOKITEMINNERTAG,
+ LT_DOCBOOKITEMINNERATTR,
+ LT_DOCBOOKFORCEABSTRACTTAG,
LT_INPREAMBLE,
LT_HTMLTITLE,
LT_SPELLCHECK,
{ "commanddepth", LT_COMMANDDEPTH },
{ "copystyle", LT_COPYSTYLE },
{ "dependson", LT_DEPENDSON },
+ { "docbookattr", LT_DOCBOOKATTR },
+ { "docbookforceabstracttag", LT_DOCBOOKFORCEABSTRACTTAG },
+ { "docbookininfo", LT_DOCBOOKININFO },
+ { "docbookitemattr", LT_DOCBOOKITEMATTR },
+ { "docbookiteminnerattr", LT_DOCBOOKITEMINNERATTR },
+ { "docbookiteminnertag", LT_DOCBOOKITEMINNERTAG },
+ { "docbookitemlabelattr", LT_DOCBOOKITEMLABELATTR },
+ { "docbookitemlabeltag", LT_DOCBOOKITEMLABELTAG },
+ { "docbookitemtag", LT_DOCBOOKITEMTAG },
+ { "docbookitemwrapperattr", LT_DOCBOOKITEMWRAPPERATTR },
+ { "docbookitemwrappertag", LT_DOCBOOKITEMWRAPPERTAG },
+ { "docbooksectiontag", LT_DOCBOOKSECTIONTAG },
+ { "docbooktag", LT_DOCBOOKTAG },
+ { "docbookwrapperattr", LT_DOCBOOKWRAPPERATTR },
+ { "docbookwrappertag", LT_DOCBOOKWRAPPERTAG },
{ "end", LT_END },
{ "endlabelstring", LT_ENDLABELSTRING },
{ "endlabeltype", LT_ENDLABELTYPE },
lex >> htmltitle_;
break;
+ case LT_DOCBOOKTAG:
+ lex >> docbooktag_;
+ break;
+
+ case LT_DOCBOOKATTR:
+ lex >> docbookattr_;
+ break;
+
+ case LT_DOCBOOKFORCEABSTRACTTAG:
+ lex >> docbookforceabstracttag_;
+ break;
+
+ case LT_DOCBOOKININFO:
+ lex >> docbookininfo_;
+ break;
+
+ case LT_DOCBOOKWRAPPERTAG:
+ lex >> docbookwrappertag_;
+ break;
+
+ case LT_DOCBOOKWRAPPERATTR:
+ lex >> docbookwrapperattr_;
+ break;
+
+ case LT_DOCBOOKSECTIONTAG:
+ lex >> docbooksectiontag_;
+ break;
+
+ case LT_DOCBOOKITEMWRAPPERTAG:
+ lex >> docbookitemwrappertag_;
+ break;
+
+ case LT_DOCBOOKITEMWRAPPERATTR:
+ lex >> docbookitemwrapperattr_;
+ break;
+
+ case LT_DOCBOOKITEMTAG:
+ lex >> docbookitemtag_;
+ break;
+
+ case LT_DOCBOOKITEMATTR:
+ lex >> docbookitemattr_;
+ break;
+
+ case LT_DOCBOOKITEMLABELTAG:
+ lex >> docbookitemlabeltag_;
+ break;
+
+ case LT_DOCBOOKITEMLABELATTR:
+ lex >> docbookitemlabelattr_;
+ break;
+
+ case LT_DOCBOOKITEMINNERTAG:
+ lex >> docbookiteminnertag_;
+ break;
+
+ case LT_DOCBOOKITEMINNERATTR:
+ lex >> docbookiteminnerattr_;
+ break;
+
case LT_SPELLCHECK:
lex >> spellcheck;
break;
os << "\tHTMLPreamble\n"
<< to_utf8(rtrim(htmlpreamble_, "\n"))
<< "\n\tEndPreamble\n";
- os << "\tHTMLTitle " << htmltitle_ << "\n"
- "\tSpellcheck " << spellcheck << "\n"
+ os << "\tHTMLTitle " << htmltitle_ << "\n";
+ if(!docbooktag_.empty())
+ os << "\tDocBookTag " << docbooktag_ << '\n';
+ if(!docbookattr_.empty())
+ os << "\tDocBookAttr " << docbookattr_ << '\n';
+ if(!docbookininfo_.empty())
+ os << "\tDocBookInInfo " << docbookininfo_ << '\n';
+ if(!docbookwrappertag_.empty())
+ os << "\tDocBookWrapperTag " << docbookwrappertag_ << '\n';
+ if(!docbookwrapperattr_.empty())
+ os << "\tDocBookWrapperAttr " << docbookwrapperattr_ << '\n';
+ if(!docbooksectiontag_.empty())
+ os << "\tDocBookSectionTag " << docbooksectiontag_ << '\n';
+ if(!docbookitemtag_.empty())
+ os << "\tDocBookItemTag " << docbookitemtag_ << '\n';
+ if(!docbookitemattr_.empty())
+ os << "\tDocBookItemAttr " << docbookitemattr_ << '\n';
+ if(!docbookitemwrappertag_.empty())
+ os << "\tDocBookItemTag " << docbookitemwrappertag_ << '\n';
+ if(!docbookitemwrapperattr_.empty())
+ os << "\tDocBookItemWrapperAttr " << docbookitemwrapperattr_ << '\n';
+ if(!docbookitemlabeltag_.empty())
+ os << "\tDocBookItemLabelTag " << docbookitemlabeltag_ << '\n';
+ if(!docbookitemlabelattr_.empty())
+ os << "\tDocBookItemLabelAttr " << docbookitemlabelattr_ << '\n';
+ if(!docbookiteminnertag_.empty())
+ os << "\tDocBookItemInnerTag " << docbookiteminnertag_ << '\n';
+ if(!docbookiteminnerattr_.empty())
+ os << "\tDocBookItemInnerAttr " << docbookiteminnerattr_ << '\n';
+ if(!docbookforceabstracttag_.empty())
+ os << "\tDocBookForceAbstractTag " << docbookforceabstracttag_ << '\n';
+ os << "\tSpellcheck " << spellcheck << "\n"
"\tForceLocal " << forcelocal << "\n"
"End\n";
}
}
+string const & Layout::docbooktag() const
+{
+ // No sensible default value, unhappily...
+ if (docbooktag_.empty())
+ docbooktag_ = to_utf8(name_);
+ return docbooktag_;
+}
+
+
+string const & Layout::docbookattr() const
+{
+ // Perfectly OK to return no attributes, so docbookattr_ does not need to be filled.
+ return docbookattr_;
+}
+
+
+string const & Layout::docbookininfo() const
+{
+ // Indeed, a trilean. Only titles should be "maybe": otherwise, metadata is "always", content is "never".
+ if (docbookininfo_.empty() || (docbookininfo_ != "never" && docbookininfo_ != "always" && docbookininfo_ != "maybe"))
+ docbookininfo_ = "never";
+ return docbookininfo_;
+}
+
+
+string const & Layout::docbookwrappertag() const
+{
+ if (docbookwrappertag_.empty())
+ docbookwrappertag_ = "NONE";
+ return docbookwrappertag_;
+}
+
+
+string const & Layout::docbookwrapperattr() const
+{
+ return docbookwrapperattr_;
+}
+
+
+string const & Layout::docbooksectiontag() const
+{
+ if (docbooksectiontag_.empty())
+ docbooksectiontag_ = "section";
+ return docbooksectiontag_;
+}
+
+
+string const & Layout::docbookitemwrappertag() const
+{
+ if (docbookitemwrappertag_.empty())
+ docbookitemwrappertag_ = "NONE";
+ return docbookitemwrappertag_;
+}
+
+
+string const & Layout::docbookitemwrapperattr() const
+{
+ return docbookitemwrapperattr_;
+}
+
+
+string const & Layout::docbookitemtag() const
+{
+ return docbookitemtag_;
+}
+
+
+string const & Layout::docbookitemattr() const
+{
+ return docbookitemattr_;
+}
+
+
+string const & Layout::docbookitemlabeltag() const
+{
+ if (docbookitemlabeltag_.empty())
+ docbookitemlabeltag_ = "NONE";
+ return docbookitemlabeltag_;
+}
+
+
+string const & Layout::docbookitemlabelattr() const
+{
+ return docbookitemlabelattr_;
+}
+
+
+string const & Layout::docbookiteminnertag() const
+{
+ if (docbookiteminnertag_.empty())
+ docbookiteminnertag_ = "NONE";
+ return docbookiteminnertag_;
+}
+
+
+string const & Layout::docbookiteminnerattr() const
+{
+ return docbookiteminnerattr_;
+}
+
+
+std::string const & Layout::docbookforceabstracttag() const
+{
+ if (docbookforceabstracttag_.empty())
+ docbookforceabstracttag_ = "NONE";
+ return docbookforceabstracttag_;
+}
+
+
+
namespace {
string makeMarginValue(char const * side, double d)
///
bool htmltitle() const { return htmltitle_; }
///
+ std::string const & docbooktag() const;
+ ///
+ std::string const & docbookattr() const;
+ ///
+ std::string const & docbookininfo() const;
+ ///
+ std::string const & docbookwrappertag() const;
+ ///
+ std::string const & docbookwrapperattr() const;
+ ///
+ std::string const & docbooksectiontag() const;
+ ///
+ std::string const & docbookitemwrappertag() const;
+ ///
+ std::string const & docbookitemwrapperattr() const;
+ ///
+ std::string const & docbookitemlabeltag() const;
+ ///
+ std::string const & docbookitemlabelattr() const;
+ ///
+ std::string const & docbookiteminnertag() const;
+ ///
+ std::string const & docbookiteminnerattr() const;
+ ///
+ std::string const & docbookitemtag() const;
+ ///
+ std::string const & docbookitemattr() const;
+ ///
+ std::string const & docbookforceabstracttag() const;
+ ///
bool isParagraph() const { return latextype == LATEX_PARAGRAPH; }
///
bool isCommand() const { return latextype == LATEX_COMMAND; }
bool htmllabelfirst_;
/// CSS information needed by this layout.
docstring htmlstyle_;
+ /// DocBook tag corresponding to this layout.
+ mutable std::string docbooktag_;
+ /// Roles to add to docbooktag_, if any (default: none).
+ mutable std::string docbookattr_;
+ /// DocBook tag corresponding to this item (mainly for lists).
+ mutable std::string docbookitemtag_;
+ /// Roles to add to docbookitemtag_, if any (default: none).
+ mutable std::string docbookitemattr_;
+ /// DocBook tag corresponding to the wrapper around an item (mainly for lists).
+ mutable std::string docbookitemwrappertag_;
+ /// Roles to add to docbookitemwrappertag_, if any (default: none).
+ mutable std::string docbookitemwrapperattr_;
+ /// DocBook tag corresponding to this label (only for description lists;
+ /// labels in the common sense do not exist with DocBook).
+ mutable std::string docbookitemlabeltag_;
+ /// Roles to add to docbooklabeltag_, if any (default: none).
+ mutable std::string docbookitemlabelattr_;
+ /// DocBook tag to add within the item, around its direct content (mainly for lists).
+ mutable std::string docbookiteminnertag_;
+ /// Roles to add to docbookiteminnertag_, if any (default: none).
+ mutable std::string docbookiteminnerattr_;
+ /// DocBook tag corresponding to this wrapper around the main tag.
+ mutable std::string docbookwrappertag_;
+ /// Roles to add to docbookwrappertag_, if any (default: none).
+ mutable std::string docbookwrapperattr_;
+ /// Outer tag for this section, only if this layout represent a sectionning item, including chapters (default: section).
+ mutable std::string docbooksectiontag_;
+ /// Whether this tag must/can/can't go into an <info> tag (default: never, as it only makes sense for metadata).
+ mutable std::string docbookininfo_;
+ /// whether this element (root or not) does not accept text without a section(i.e. the first text that is met
+ /// in LyX must be considered as the abstract if this is true); this text must be output with the specific tag
+ /// held by this attribute
+ mutable std::string docbookforceabstracttag_;
/// Should we generate the default CSS for this layout, even if HTMLStyle
/// has been given? Default is false.
/// Note that the default CSS is output first, then the user CSS, so it is
par_begin(0), par_end(0), lastid(-1), lastpos(0), isLastPar(false),
dryrun(false), silent(false), pass_thru(false),
html_disable_captions(false), html_in_par(false),
- html_make_pars(true), for_toc(false), for_tooltip(false),
+ html_make_pars(true), docbook_in_par(false), docbook_make_pars(true),
+ docbook_force_pars(false), docbook_in_float(false), docbook_anchors_to_ignore(std::unordered_set<docstring>()),
+ docbook_in_listing(false), for_toc(false), for_tooltip(false),
for_search(false), for_preview(false), includeall(false)
{
// Note: in PreviewLoader::Impl::dumpPreamble
#include "Changes.h"
#include <memory>
+#include <unordered_set>
namespace lyx {
/// Does the present context even permit paragraphs?
bool html_make_pars;
+ /// Are we already in a paragraph?
+ bool docbook_in_par;
+
+ /// Does the present context even permit paragraphs?
+ bool docbook_make_pars;
+
+ /// Are paragraphs mandatory in this context?
+ bool docbook_force_pars;
+
+ /// Anchors that should not be output (LyX-side identifier, not DocBook-side).
+ std::unordered_set<docstring> docbook_anchors_to_ignore;
+
+ /// Is the current context a float (such as a table or a figure)?
+ bool docbook_in_float;
+
+ /// Is the current context a listing?
+ bool docbook_in_listing;
+
/// Are we generating this material for inclusion in a TOC-like entity?
bool for_toc;
#include "support/lassert.h"
#include "support/lstrings.h"
#include "support/textutils.h"
+#include "output_docbook.h"
#include <atomic>
#include <sstream>
}
-docstring Paragraph::expandDocBookLabel(Layout const & layout,
- BufferParams const & bparams) const
-{
- return expandParagraphLabel(layout, bparams, false);
-}
-
-
docstring Paragraph::expandParagraphLabel(Layout const & layout,
BufferParams const & bparams, bool process_appendix) const
{
}
-pos_type Paragraph::firstWordDocBook(odocstream & os, OutputParams const & runparams)
- const
+pos_type Paragraph::firstWordDocBook(XMLStream & xs, OutputParams const & runparams) const
{
pos_type i;
for (i = 0; i < size(); ++i) {
if (Inset const * inset = getInset(i)) {
- inset->docbook(os, runparams);
+ inset->docbook(xs, runparams);
} else {
char_type c = d->text_[i];
if (c == ' ')
break;
- os << xml::escapeChar(c, XMLStream::ESCAPE_ALL);
+ xs << c;
}
}
return i;
}
-void Paragraph::simpleDocBookOnePar(Buffer const & buf,
- odocstream & os,
- OutputParams const & runparams,
- Font const & outerfont,
- pos_type initial) const
+namespace {
+
+void doFontSwitchDocBook(vector<xml::FontTag> & tagsToOpen,
+ vector<xml::EndFontTag> & tagsToClose,
+ bool & flag, FontState curstate, xml::FontTypes type)
{
- bool emph_flag = false;
+ if (curstate == FONT_ON) {
+ tagsToOpen.push_back(docbookStartFontTag(type));
+ flag = true;
+ } else if (flag) {
+ tagsToClose.push_back(docbookEndFontTag(type));
+ flag = false;
+ }
+}
- Layout const & style = *d->layout_;
- FontInfo font_old =
- style.labeltype == LABEL_MANUAL ? style.labelfont : style.font;
+class OptionalFontType {
+public:
+ bool has_value;
+ xml::FontTypes ft;
- if (style.pass_thru && !d->onlyText(buf, outerfont, initial))
- os << "]]>";
+ OptionalFontType(): has_value(false), ft(xml::FT_EMPH) {} // A possible value at random for ft.
+ OptionalFontType(xml::FontTypes ft): has_value(true), ft(ft) {}
+};
- // parsing main loop
- for (pos_type i = initial; i < size(); ++i) {
- Font font = getFont(buf.params(), i, outerfont);
-
- // handle <emphasis> tag
- if (font_old.emph() != font.fontInfo().emph()) {
- if (font.fontInfo().emph() == FONT_ON) {
- os << "<emphasis>";
- emph_flag = true;
- } else if (i != initial) {
- os << "</emphasis>";
- emph_flag = false;
- }
- }
+OptionalFontType fontShapeToXml(FontShape fs)
+{
+ switch (fs) {
+ case ITALIC_SHAPE:
+ return {xml::FT_ITALIC};
+ case SLANTED_SHAPE:
+ return {xml::FT_SLANTED};
+ case SMALLCAPS_SHAPE:
+ return {xml::FT_SMALLCAPS};
+ case UP_SHAPE:
+ case INHERIT_SHAPE:
+ return {};
+ default:
+ // the other tags are for internal use
+ LATTEST(false);
+ return {};
+ }
+}
+
+OptionalFontType fontFamilyToXml(FontFamily fm)
+{
+ switch (fm) {
+ case ROMAN_FAMILY:
+ return {xml::FT_ROMAN};
+ break;
+ case SANS_FAMILY:
+ return {xml::FT_SANS};
+ break;
+ case TYPEWRITER_FAMILY:
+ return {xml::FT_TYPE};
+ break;
+ case INHERIT_FAMILY:
+ return {};
+ default:
+ // the other tags are for internal use
+ LATTEST(false);
+ return {};
+ }
+}
+
+OptionalFontType fontSizeToXml(FontSize fs)
+{
+ switch (fs) {
+ case TINY_SIZE:
+ return {xml::FT_SIZE_TINY};
+ case SCRIPT_SIZE:
+ return {xml::FT_SIZE_SCRIPT};
+ case FOOTNOTE_SIZE:
+ return {xml::FT_SIZE_FOOTNOTE};
+ case SMALL_SIZE:
+ return {xml::FT_SIZE_SMALL};
+ case LARGE_SIZE:
+ return {xml::FT_SIZE_LARGE};
+ case LARGER_SIZE:
+ return {xml::FT_SIZE_LARGER};
+ case LARGEST_SIZE:
+ return {xml::FT_SIZE_LARGEST};
+ case HUGE_SIZE:
+ return {xml::FT_SIZE_HUGE};
+ case HUGER_SIZE:
+ return {xml::FT_SIZE_HUGER};
+ case INCREASE_SIZE:
+ return {xml::FT_SIZE_INCREASE};
+ case DECREASE_SIZE:
+ return {xml::FT_SIZE_DECREASE};
+ case INHERIT_SIZE:
+ case NORMAL_SIZE:
+ return {};
+ default:
+ // the other tags are for internal use
+ LATTEST(false);
+ return {};
+ }
- if (Inset const * inset = getInset(i)) {
- inset->docbook(os, runparams);
- } else {
- char_type c = d->text_[i];
+}
- if (style.pass_thru)
- os.put(c);
- else
- os << xml::escapeChar(c, XMLStream::EscapeSettings::ESCAPE_ALL);
- }
- font_old = font.fontInfo();
- }
+}// anonymous namespace
- if (emph_flag) {
- os << "</emphasis>";
- }
- if (style.free_spacing)
- os << '\n';
- if (style.pass_thru && !d->onlyText(buf, outerfont, initial))
- os << "<![CDATA[";
+void Paragraph::simpleDocBookOnePar(Buffer const & buf,
+ XMLStream & xs,
+ OutputParams const & runparams,
+ Font const & outerfont,
+ bool start_paragraph, bool close_paragraph,
+ pos_type initial) const
+{
+ // track whether we have opened these tags
+ bool emph_flag = false;
+ bool bold_flag = false;
+ bool noun_flag = false;
+ bool ubar_flag = false;
+ bool dbar_flag = false;
+ bool sout_flag = false;
+ bool wave_flag = false;
+ // shape tags
+ bool shap_flag = false;
+ // family tags
+ bool faml_flag = false;
+ // size tags
+ bool size_flag = false;
+
+ Layout const & style = *d->layout_;
+
+ if (start_paragraph)
+ xs.startDivision(allowEmpty());
+
+ FontInfo font_old =
+ style.labeltype == LABEL_MANUAL ? style.labelfont : style.font;
+
+ FontShape curr_fs = INHERIT_SHAPE;
+ FontFamily curr_fam = INHERIT_FAMILY;
+ FontSize curr_size = INHERIT_SIZE;
+
+ string const default_family =
+ buf.masterBuffer()->params().fonts_default_family;
+
+ vector<xml::FontTag> tagsToOpen;
+ vector<xml::EndFontTag> tagsToClose;
+
+ // parsing main loop
+ for (pos_type i = initial; i < size(); ++i) {
+ // let's not show deleted material in the output
+ if (isDeleted(i))
+ continue;
+
+ Font const font = getFont(buf.masterBuffer()->params(), i, outerfont);
+
+ // emphasis
+ FontState curstate = font.fontInfo().emph();
+ if (font_old.emph() != curstate)
+ doFontSwitchDocBook(tagsToOpen, tagsToClose, emph_flag, curstate, xml::FT_EMPH);
+
+ // noun
+ curstate = font.fontInfo().noun();
+ if (font_old.noun() != curstate)
+ doFontSwitchDocBook(tagsToOpen, tagsToClose, noun_flag, curstate, xml::FT_NOUN);
+
+ // underbar
+ curstate = font.fontInfo().underbar();
+ if (font_old.underbar() != curstate)
+ doFontSwitchDocBook(tagsToOpen, tagsToClose, ubar_flag, curstate, xml::FT_UBAR);
+
+ // strikeout
+ curstate = font.fontInfo().strikeout();
+ if (font_old.strikeout() != curstate)
+ doFontSwitchDocBook(tagsToOpen, tagsToClose, sout_flag, curstate, xml::FT_SOUT);
+
+ // double underbar
+ curstate = font.fontInfo().uuline();
+ if (font_old.uuline() != curstate)
+ doFontSwitchDocBook(tagsToOpen, tagsToClose, dbar_flag, curstate, xml::FT_DBAR);
+
+ // wavy line
+ curstate = font.fontInfo().uwave();
+ if (font_old.uwave() != curstate)
+ doFontSwitchDocBook(tagsToOpen, tagsToClose, wave_flag, curstate, xml::FT_WAVE);
+
+ // bold
+ // a little hackish, but allows us to reuse what we have.
+ curstate = (font.fontInfo().series() == BOLD_SERIES ? FONT_ON : FONT_OFF);
+ if (font_old.series() != font.fontInfo().series())
+ doFontSwitchDocBook(tagsToOpen, tagsToClose, bold_flag, curstate, xml::FT_BOLD);
+
+ // Font shape
+ curr_fs = font.fontInfo().shape();
+ FontShape old_fs = font_old.shape();
+ if (old_fs != curr_fs) {
+ if (shap_flag) {
+ OptionalFontType tag = fontShapeToXml(old_fs);
+ if (tag.has_value) {
+ tagsToClose.push_back(docbookEndFontTag(tag.ft));
+ }
+ shap_flag = false;
+ }
+
+ OptionalFontType tag = fontShapeToXml(curr_fs);
+ if (tag.has_value) {
+ tagsToOpen.push_back(docbookStartFontTag(tag.ft));
+ }
+ }
+
+ // Font family
+ curr_fam = font.fontInfo().family();
+ FontFamily old_fam = font_old.family();
+ if (old_fam != curr_fam) {
+ if (faml_flag) {
+ OptionalFontType tag = fontFamilyToXml(old_fam);
+ if (tag.has_value) {
+ tagsToClose.push_back(docbookEndFontTag(tag.ft));
+ }
+ faml_flag = false;
+ }
+ switch (curr_fam) {
+ case ROMAN_FAMILY:
+ // we will treat a "default" font family as roman, since we have
+ // no other idea what to do.
+ if (default_family != "rmdefault" && default_family != "default") {
+ tagsToOpen.push_back(docbookStartFontTag(xml::FT_ROMAN));
+ faml_flag = true;
+ }
+ break;
+ case SANS_FAMILY:
+ if (default_family != "sfdefault") {
+ tagsToOpen.push_back(docbookStartFontTag(xml::FT_SANS));
+ faml_flag = true;
+ }
+ break;
+ case TYPEWRITER_FAMILY:
+ if (default_family != "ttdefault") {
+ tagsToOpen.push_back(docbookStartFontTag(xml::FT_TYPE));
+ faml_flag = true;
+ }
+ break;
+ case INHERIT_FAMILY:
+ break;
+ default:
+ // the other tags are for internal use
+ LATTEST(false);
+ break;
+ }
+ }
+
+ // Font size
+ curr_size = font.fontInfo().size();
+ FontSize old_size = font_old.size();
+ if (old_size != curr_size) {
+ if (size_flag) {
+ OptionalFontType tag = fontSizeToXml(old_size);
+ if (tag.has_value) {
+ tagsToClose.push_back(docbookEndFontTag(tag.ft));
+ }
+ size_flag = false;
+ }
+
+ OptionalFontType tag = fontSizeToXml(curr_size);
+ if (tag.has_value) {
+ tagsToOpen.push_back(docbookStartFontTag(tag.ft));
+ size_flag = true;
+ }
+ }
+
+ // FIXME XHTML
+ // Other such tags? What about the other text ranges?
+
+ vector<xml::EndFontTag>::const_iterator cit = tagsToClose.begin();
+ vector<xml::EndFontTag>::const_iterator cen = tagsToClose.end();
+ for (; cit != cen; ++cit)
+ xs << *cit;
+
+ vector<xml::FontTag>::const_iterator sit = tagsToOpen.begin();
+ vector<xml::FontTag>::const_iterator sen = tagsToOpen.end();
+ for (; sit != sen; ++sit)
+ xs << *sit;
+
+ tagsToClose.clear();
+ tagsToOpen.clear();
+
+ if (Inset const * inset = getInset(i)) {
+ if (!runparams.for_toc || inset->isInToc()) {
+ OutputParams np = runparams;
+ np.local_font = &font;
+ // If the paragraph has size 1, then we are in the "special
+ // case" where we do not output the containing paragraph info.
+ if (!inset->getLayout().htmlisblock() && size() != 1) // TODO: htmlisblock here too!
+ np.docbook_in_par = true;
+ inset->docbook(xs, np);
+ }
+ } else {
+ char_type c = getUChar(buf.masterBuffer()->params(), runparams, i);
+ xs << c;
+ }
+ font_old = font.fontInfo();
+ }
+
+ // FIXME XHTML
+ // I'm worried about what happens if a branch, say, is itself
+ // wrapped in some font stuff. I think that will not work.
+ xs.closeFontTags();
+ if (runparams.docbook_in_listing)
+ xs << xml::CR();
+ if (close_paragraph)
+ xs.endDivision();
}
std::string getID(Buffer const & buf, OutputParams const & runparams) const;
/// Output the first word of a paragraph, return the position where it left.
- pos_type firstWordDocBook(odocstream & os, OutputParams const & runparams) const;
+ pos_type firstWordDocBook(XMLStream & xs, OutputParams const & runparams) const;
/// Output the first word of a paragraph, return the position where it left.
pos_type firstWordLyXHTML(XMLStream & xs, OutputParams const & runparams) const;
- /// Writes to stream the docbook representation
+ /// Writes to stream the DocBook representation
void simpleDocBookOnePar(Buffer const & buf,
- odocstream &,
- OutputParams const & runparams,
- Font const & outerfont,
- pos_type initial = 0) const;
+ XMLStream &,
+ OutputParams const & runparams,
+ Font const & outerfont,
+ bool start_paragraph = true,
+ bool close_paragraph = true,
+ pos_type initial = 0) const;
/// \return any material that has had to be deferred until after the
/// paragraph has closed.
docstring simpleLyXHTMLOnePar(Buffer const & buf,
- XMLStream & xs,
- OutputParams const & runparams,
- Font const & outerfont,
- bool start_paragraph = true,
- bool close_paragraph = true,
- pos_type initial = 0) const;
+ XMLStream & xs,
+ OutputParams const & runparams,
+ Font const & outerfont,
+ bool start_paragraph = true,
+ bool close_paragraph = true,
+ pos_type initial = 0) const;
///
bool hasSameLayout(Paragraph const & par) const;
///
docstring expandLabel(Layout const &, BufferParams const &) const;
///
- docstring expandDocBookLabel(Layout const &, BufferParams const &) const;
- ///
docstring const & labelString() const;
/// the next two functions are for the manual labels
docstring const getLabelWidthString() const;
// You should also run the development/tools/updatelayouts.py script,
// to update the format of all of our layout files.
//
-int const LAYOUT_FORMAT = 81; // rikiheck: GuiName for counters
+int const LAYOUT_FORMAT = 82; // dourouc05: DocBook additions.
// Layout format for the current lyx file format. Controls which format is
outputFormat_("latex"), has_output_format_(false), defaultfont_(sane_font),
titletype_(TITLE_COMMAND_AFTER), titlename_("maketitle"),
min_toclevel_(0), max_toclevel_(0), maxcitenames_(2),
- cite_full_author_list_(true), bibintoc_(false)
+ cite_full_author_list_(true), bibintoc_(false), docbookroot_("article"), docbookforceabstract_(false)
{
}
TC_FULLAUTHORLIST,
TC_OUTLINERNAME,
TC_TABLESTYLE,
- TC_BIBINTOC
+ TC_BIBINTOC,
+ TC_DOCBOOKROOT,
+ TC_DOCBOOKFORCEABSTRACT
};
{ "defaultfont", TC_DEFAULTFONT },
{ "defaultmodule", TC_DEFAULTMODULE },
{ "defaultstyle", TC_DEFAULTSTYLE },
+ { "docbookforceabstract", TC_DOCBOOKFORCEABSTRACT },
+ { "docbookroot", TC_DOCBOOKROOT },
{ "excludesmodule", TC_EXCLUDESMODULE },
{ "float", TC_FLOAT },
{ "format", TC_FORMAT },
error = !readOutlinerName(lexrc);
break;
- case TC_TABLESTYLE:
+ case TC_TABLESTYLE:
lexrc.next();
tablestyle_ = rtrim(lexrc.getString());
break;
+
+ case TC_DOCBOOKROOT:
+ if (lexrc.next())
+ docbookroot_ = lexrc.getString();
+ break;
+
+ case TC_DOCBOOKFORCEABSTRACT:
+ if (lexrc.next())
+ docbookforceabstract_ = lexrc.getBool();
+ break;
} // end of switch
}
CO_PAGESIZE_FORMAT,
CO_PAGESTYLE,
CO_OTHER,
- CO_HEADER,
CO_END
};
{"end", CO_END },
{"fontsize", CO_FONTSIZE },
{"fontsizeformat", CO_FONTSIZE_FORMAT },
- {"header", CO_HEADER },
{"other", CO_OTHER },
{"pagesize", CO_PAGESIZE },
{"pagesizeformat", CO_PAGESIZE_FORMAT },
else
options_ += ',' + lexrc.getString();
break;
- case CO_HEADER:
- lexrc.next();
- class_header_ = subst(lexrc.getString(), """, "\"");
- break;
case CO_END:
getout = true;
break;
FT_HTMLSTYLE,
FT_HTMLATTR,
FT_HTMLTAG,
+ FT_DOCBOOKATTR,
+ FT_DOCBOOKTAG,
FT_LISTCOMMAND,
FT_REFPREFIX,
FT_ALLOWED_PLACEMENT,
{ "allowedplacement", FT_ALLOWED_PLACEMENT },
{ "allowssideways", FT_ALLOWS_SIDEWAYS },
{ "allowswide", FT_ALLOWS_WIDE },
+ { "docbookattr", FT_DOCBOOKATTR },
+ { "docbooktag", FT_DOCBOOKTAG },
{ "end", FT_END },
{ "extension", FT_EXT },
{ "guiname", FT_NAME },
string htmlattr;
docstring htmlstyle;
string htmltag;
+ string docbookattr;
+ string docbooktag;
string listname;
string listcommand;
string name;
lexrc.next();
htmltag = lexrc.getString();
break;
+ case FT_DOCBOOKATTR:
+ lexrc.next();
+ docbookattr = lexrc.getString();
+ break;
+ case FT_DOCBOOKTAG:
+ lexrc.next();
+ docbooktag = lexrc.getString();
+ break;
case FT_END:
getout = true;
break;
}
Floating fl(type, placement, ext, within, style, name,
listname, listcommand, refprefix, allowed_placement,
- htmltag, htmlattr, htmlstyle, required, usesfloat,
- ispredefined, allowswide, allowssideways);
+ htmltag, htmlattr, htmlstyle, docbooktag, docbookattr,
+ required, usesfloat, ispredefined, allowswide,
+ allowssideways);
floatlist_.newFloat(fl);
// each float has its own counter
counters_.newCounter(from_ascii(type), from_ascii(within),
docstring htmlpreamble_;
/// same, but specifically for CSS information
docstring htmlstyles_;
- /// the paragraph style to use for TOCs, Bibliography, etc
+ /// the paragraph style to use for TOCs, bibliography, etc.
mutable docstring html_toc_section_;
+ /// root element when exporting as DocBook
+ std::string docbookroot_;
+ /// whether this root element does not accept text without a section (i.e. the first text that is met in LyX must
+ /// be considered as the abstract if this is true); this text must be output within <info> and <abstract>
+ bool docbookforceabstract_;
/// latex packages loaded by document class.
std::set<std::string> provides_;
/// latex packages requested by document class.
docstring const & htmlpreamble() const { return htmlpreamble_; }
///
docstring const & htmlstyles() const { return htmlstyles_; }
+ ///
+ bool const & docbookforceabstract() const { return docbookforceabstract_; }
+ ///
+ std::string const & docbookroot() const { return docbookroot_; }
/// Looks for the layout of "highest level", other than Part (or other
/// layouts with a negative toc number), for use in constructing TOCs and
/// similar information.
#include "FuncStatus.h"
#include "MetricsInfo.h"
#include "output_xhtml.h"
+#include "xml.h"
#include "Text.h"
#include "TextClass.h"
#include "TocBackend.h"
}
-int Inset::docbook(odocstream &, OutputParams const &) const
+void Inset::docbook(XMLStream & xs, OutputParams const &) const
{
- return 0;
+ xs << "[[Inset: " << from_ascii(insetName(lyxCode())) << "]]";
}
virtual int plaintext(odocstringstream &, OutputParams const &,
size_t max_length = INT_MAX) const = 0;
/// docbook output
- virtual int docbook(odocstream & os, OutputParams const &) const;
+ virtual void docbook(XMLStream &, OutputParams const &) const;
/// XHTML output
/// the inset is expected to write XHTML to the XMLStream
/// \return any "deferred" material that should be written outside the
///
int plaintext(odocstringstream &, OutputParams const &, size_t) const { return 0; }
///
- int docbook(odocstream &, OutputParams const &) const { return 0; }
+ void docbook(XMLStream &, OutputParams const &) const { return; }
///
docstring xhtml(XMLStream &, OutputParams const &) const
{ return docstring(); }
#include "FuncStatus.h"
#include "LaTeXFeatures.h"
#include "output_latex.h"
-#include "output_xhtml.h"
#include "xml.h"
#include "OutputParams.h"
#include "PDFOptions.h"
#include "support/ExceptionMessage.h"
#include "support/FileNameList.h"
#include "support/filetools.h"
+#include "support/regex.h"
#include "support/gettext.h"
#include "support/lstrings.h"
#include "support/os.h"
#include "support/textutils.h"
#include <limits>
+#include <map>
+#include <utility>
+
+#include <iostream>
using namespace std;
using namespace lyx::support;
}
+void InsetBibtex::docbook(XMLStream & xs, OutputParams const &) const
+{
+ BiblioInfo const & bibinfo = buffer().masterBibInfo();
+ bool const all_entries = getParam("btprint") == "btPrintAll";
+ vector<docstring> const & cites =
+ all_entries ? bibinfo.getKeys() : bibinfo.citedEntries();
+
+ docstring const reflabel = buffer().B_("References");
+
+ // Tell BiblioInfo our purpose (i.e. generate HTML rich text).
+ CiteItem ci;
+ ci.context = CiteItem::Export;
+ ci.richtext = true;
+ ci.max_key_size = UINT_MAX;
+
+ // Header for bibliography (title required).
+ xs << xml::StartTag("bibliography");
+ xs << xml::CR();
+ xs << xml::StartTag("title");
+ xs << reflabel;
+ xs << xml::EndTag("title") << xml::CR();
+
+ // Translation between keys in each entry and DocBook tags.
+ // IDs for publications; list: http://tdg.docbook.org/tdg/5.2/biblioid.html.
+ vector<pair<string, string>> biblioId = { // <bibtex, docbook>
+ make_pair("doi", "doi"),
+ make_pair("isbn", "isbn"),
+ make_pair("issn", "issn"),
+ make_pair("isrn", "isrn"),
+ make_pair("istc", "istc"),
+ make_pair("lccn", "libraryofcongress"),
+ make_pair("number", "pubsnumber"),
+ make_pair("url", "uri")
+ };
+ // Relations between documents.
+ vector<pair<string, string>> relations = { // <bibtex, docbook biblioset relation>
+ make_pair("journal", "journal"),
+ make_pair("booktitle", "book"),
+ make_pair("series", "series")
+ };
+ // Various things that do not fit DocBook.
+ vector<string> misc = { "language", "school", "note" };
+
+ // Store the mapping between BibTeX and DocBook.
+ map<string, string> toDocBookTag;
+ toDocBookTag["fullnames:author"] = "SPECIFIC"; // No direct translation to DocBook: <authorgroup>.
+ toDocBookTag["publisher"] = "SPECIFIC"; // No direct translation to DocBook: <publisher>.
+ toDocBookTag["address"] = "SPECIFIC"; // No direct translation to DocBook: <publisher>.
+ toDocBookTag["editor"] = "editor";
+ toDocBookTag["institution"] = "SPECIFIC"; // No direct translation to DocBook: <org>.
+
+ toDocBookTag["title"] = "title";
+ toDocBookTag["volume"] = "volumenum";
+ toDocBookTag["edition"] = "edition";
+ toDocBookTag["pages"] = "artpagenums";
+
+ toDocBookTag["abstract"] = "SPECIFIC"; // No direct translation to DocBook: <abstract>.
+ toDocBookTag["keywords"] = "SPECIFIC"; // No direct translation to DocBook: <keywordset>.
+ toDocBookTag["year"] = "SPECIFIC"; // No direct translation to DocBook: <pubdate>.
+ toDocBookTag["month"] = "SPECIFIC"; // No direct translation to DocBook: <pubdate>.
+
+ toDocBookTag["journal"] = "SPECIFIC"; // No direct translation to DocBook: <biblioset>.
+ toDocBookTag["booktitle"] = "SPECIFIC"; // No direct translation to DocBook: <biblioset>.
+ toDocBookTag["series"] = "SPECIFIC"; // No direct translation to DocBook: <biblioset>.
+
+ for (auto const & id: biblioId)
+ toDocBookTag[id.first] = "SPECIFIC"; // No direct translation to DocBook: <biblioid>.
+ for (auto const & id: relations)
+ toDocBookTag[id.first] = "SPECIFIC"; // No direct translation to DocBook: <biblioset>.
+ for (auto const & id: misc)
+ toDocBookTag[id] = "SPECIFIC"; // No direct translation to DocBook: <bibliomisc>.
+
+ // Loop over the entries. If there are no entries, add a comment to say so.
+ auto vit = cites.begin();
+ auto ven = cites.end();
+
+ if (vit == ven) {
+ xs << XMLStream::ESCAPE_NONE << "<!-- No entry in the bibliography. -->";
+ }
+
+ for (; vit != ven; ++vit) {
+ BiblioInfo::const_iterator const biit = bibinfo.find(*vit);
+ if (biit == bibinfo.end())
+ continue;
+
+ BibTeXInfo const & entry = biit->second;
+ string const attr = "xml:id=\"" + to_utf8(xml::cleanID(entry.key())) + "\"";
+ xs << xml::StartTag("biblioentry", attr);
+ xs << xml::CR();
+
+ // FIXME Right now, we are calling BibInfo::getInfo on the key,
+ // which will give us all the cross-referenced info. But for every
+ // entry, so there's a lot of repetition. This should be fixed.
+
+ // Parse the results of getInfo and emit the corresponding DocBook tags. Interesting pieces have the form
+ // "<span class="bib-STH">STH</span>", the rest of the text may be discarded.
+ // Could have written a DocBook version of expandFormat (that parses a citation into HTML), but it implements
+ // some kind of recursion. Still, a (static) conversion step between the citation format and DocBook would have
+ // been required. All in all, both codes approaches would have been similar, but this parsing allows relying
+ // on existing building blocks.
+
+ string html = to_utf8(bibinfo.getInfo(entry.key(), buffer(), ci));
+ regex tagRegex("<span class=\"bib-([^\"]*)\">([^<]*)</span>");
+ smatch match;
+ auto tagIt = std::sregex_iterator(html.cbegin(), html.cend(), tagRegex, regex_constants::match_default);
+ auto tagEnd = std::sregex_iterator();
+ map<string, string> delayedTags;
+
+ // Read all tags from HTML and convert those that have a 1:1 matching.
+ while (tagIt != tagEnd) {
+ string tag = tagIt->str(); // regex_match cannot work with temporary strings.
+ ++tagIt;
+ std::regex_match(tag, match, tagRegex);
+
+ if (toDocBookTag.find(match[1]) == toDocBookTag.end()) {
+ LYXERR0("The BibTeX field " << match[1].str() << " is unknown.");
+ xs << XMLStream::ESCAPE_NONE << from_utf8("<!-- Output Error: The BibTeX field " + match[1].str() + " is unknown -->\n");
+ continue;
+ }
+
+ if (toDocBookTag[match[1]] == "SPECIFIC") {
+ delayedTags[match[1]] = match[2];
+ } else {
+ xs << xml::StartTag(toDocBookTag[match[1]]);
+ xs << from_utf8(match[2].str());
+ xs << xml::EndTag(toDocBookTag[match[1]]);
+ }
+ }
+
+ // Type of document (book, journal paper, etc.).
+ xs << xml::StartTag("bibliomisc", "role=\"type\"");
+ xs << entry.entryType();
+ xs << xml::EndTag("bibliomisc");
+ xs << xml::CR();
+
+ // Handle tags that have complex transformations.
+ if (! delayedTags.empty()) {
+ unsigned long remainingTags = delayedTags.size(); // Used as a workaround. With GCC 7, when erasing all
+ // elements one by one, some elements may still pop in later on (even though they were deleted previously).
+ auto hasTag = [&delayedTags](string key) { return delayedTags.find(key) != delayedTags.end(); };
+ auto getTag = [&delayedTags](string key) { return from_utf8(delayedTags[key]); };
+ auto eraseTag = [&delayedTags, &remainingTags](string key) {
+ remainingTags -= 1;
+ delayedTags.erase(key);
+ };
+
+ // Notes on order of checks.
+ // - address goes with publisher if there is one, so check this first. Otherwise, the address goes with
+ // the entry without other details.
+
+ // <publisher>
+ if (hasTag("publisher")) {
+ xs << xml::StartTag("publisher");
+ xs << xml::CR();
+ xs << xml::StartTag("publishername");
+ xs << getTag("publisher");
+ xs << xml::EndTag("publishername");
+ xs << xml::CR();
+
+ if (hasTag("address")) {
+ xs << xml::StartTag("address");
+ xs << getTag("address");
+ xs << xml::EndTag("address");
+ eraseTag("address");
+ }
+
+ xs << xml::EndTag("publisher");
+ xs << xml::CR();
+ eraseTag("publisher");
+ }
+
+ if (hasTag("address")) {
+ xs << xml::StartTag("address");
+ xs << getTag("address");
+ xs << xml::EndTag("address");
+ eraseTag("address");
+ }
+
+ // <keywordset>
+ if (hasTag("keywords")) {
+ // Split the keywords on comma.
+ docstring keywordSet = getTag("keywords");
+ vector<docstring> keywords;
+ if (keywordSet.find(from_utf8(",")) == string::npos) {
+ keywords = { keywordSet };
+ } else {
+ size_t pos = 0;
+ while ((pos = keywordSet.find(from_utf8(","))) != string::npos) {
+ keywords.push_back(keywordSet.substr(0, pos));
+ keywordSet.erase(0, pos + 1);
+ }
+ keywords.push_back(keywordSet);
+ }
+
+ xs << xml::StartTag("keywordset") << xml::CR();
+ for (auto & kw: keywords) {
+ kw.erase(kw.begin(), std::find_if(kw.begin(), kw.end(),
+ [](int c) {return !std::isspace(c);}));
+ xs << xml::StartTag("keyword");
+ xs << kw;
+ xs << xml::EndTag("keyword");
+ xs << xml::CR();
+ }
+ xs << xml::EndTag("keywordset") << xml::CR();
+ eraseTag("keywords");
+ }
+
+ // <copyright>
+ // Example: http://tdg.docbook.org/tdg/5.1/biblioset.html
+ if (hasTag("year")) {
+ docstring value = getTag("year");
+ eraseTag("year");
+
+ // Follow xsd:gYearMonth format (http://books.xmlschemata.org/relaxng/ch19-77135.html).
+ if (hasTag("month")) {
+ value += "-" + getTag("month");
+ eraseTag("month");
+ }
+
+ xs << xml::StartTag("pubdate");
+ xs << value;
+ xs << xml::EndTag("pubdate");
+ xs << xml::CR();
+ }
+
+ // <institution>
+ if (hasTag("institution")) {
+ xs << xml::StartTag("org");
+ xs << xml::CR();
+ xs << xml::StartTag("orgname");
+ xs << getTag("institution");
+ xs << xml::EndTag("orgname");
+ xs << xml::CR();
+ xs << xml::EndTag("org");
+ xs << xml::CR();
+ eraseTag("institution");
+ }
+
+ // <biblioset>
+ // Example: http://tdg.docbook.org/tdg/5.1/biblioset.html
+ for (auto const & id: relations) {
+ if (hasTag(id.first)) {
+ xs << xml::StartTag("biblioset", "relation=\"" + id.second + "\"");
+ xs << xml::CR();
+ xs << xml::StartTag("title");
+ xs << getTag(id.first);
+ xs << xml::EndTag("title");
+ xs << xml::CR();
+ xs << xml::EndTag("biblioset");
+ xs << xml::CR();
+ eraseTag(id.first);
+ }
+ }
+
+ // <authorgroup>
+ // Example: http://tdg.docbook.org/tdg/5.1/authorgroup.html
+ if (hasTag("fullnames:author")) {
+ // Perform full parsing of the BibTeX string, dealing with the many corner cases that might
+ // be encountered.
+ authorsToDocBookAuthorGroup(getTag("fullnames:author"), xs, buffer());
+ eraseTag("fullnames:author");
+ }
+
+ // <abstract>
+ if (hasTag("abstract")) {
+ // Split the paragraphs on new line.
+ docstring abstract = getTag("abstract");
+ vector<docstring> paragraphs;
+ if (abstract.find(from_utf8("\n")) == string::npos) {
+ paragraphs = { abstract };
+ } else {
+ size_t pos = 0;
+ while ((pos = abstract.find(from_utf8(","))) != string::npos) {
+ paragraphs.push_back(abstract.substr(0, pos));
+ abstract.erase(0, pos + 1);
+ }
+ paragraphs.push_back(abstract);
+ }
+
+ xs << xml::StartTag("abstract");
+ xs << xml::CR();
+ for (auto const & para: paragraphs) {
+ if (para.empty())
+ continue;
+ xs << xml::StartTag("para");
+ xs << para;
+ xs << xml::EndTag("para");
+ }
+ xs << xml::CR();
+ xs << xml::EndTag("abstract");
+ xs << xml::CR();
+ eraseTag("abstract");
+ }
+
+ // <biblioid>
+ for (auto const & id: biblioId) {
+ if (hasTag(id.first)) {
+ xs << xml::StartTag("biblioid", "class=\"" + id.second + "\"");
+ xs << getTag(id.first);
+ xs << xml::EndTag("biblioid");
+ xs << xml::CR();
+ eraseTag(id.first);
+ }
+ }
+
+ // <bibliomisc>
+ for (auto const & id: misc) {
+ if (hasTag(id)) {
+ xs << xml::StartTag("bibliomisc", "role=\"" + id + "\"");
+ xs << getTag(id);
+ xs << xml::EndTag("bibliomisc");
+ xs << xml::CR();
+ eraseTag(id);
+ }
+ }
+
+ // After all tags are processed, check for errors.
+ if (remainingTags > 0) {
+ LYXERR0("Still delayed tags not yet handled.");
+ xs << XMLStream::ESCAPE_NONE << from_utf8("<!-- Output Error: still delayed tags not yet handled.\n");
+ for (auto const & item: delayedTags) {
+ xs << from_utf8(" " + item.first + ": " + item.second + "\n");
+ }
+ xs << XMLStream::ESCAPE_NONE << from_utf8(" -->\n");
+ }
+ }
+
+ xs << xml::EndTag("biblioentry");
+ xs << xml::CR();
+ }
+
+ // Footer for bibliography.
+ xs << xml::EndTag("bibliography");
+}
+
+
void InsetBibtex::write(ostream & os) const
{
params().Write(os, &buffer());
///
docstring xhtml(XMLStream &, OutputParams const &) const;
///
+ void docbook(XMLStream &, OutputParams const &) const;
+ ///
std::string contextMenuName() const;
//@}
}
-int InsetBox::docbook(odocstream & os, OutputParams const & runparams) const
+void InsetBox::docbook(XMLStream & xs, OutputParams const & runparams) const
{
- return InsetText::docbook(os, runparams);
+ InsetText::docbook(xs, runparams);
}
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const &) const;
+ void docbook(XMLStream &, OutputParams const &) const;
///
docstring xhtml(XMLStream &, OutputParams const &) const;
///
#include "Lexer.h"
#include "LyX.h"
#include "OutputParams.h"
+#include "output_docbook.h"
#include "output_xhtml.h"
#include "TextClass.h"
#include "TocBackend.h"
}
-int InsetBranch::docbook(odocstream & os,
- OutputParams const & runparams) const
+void InsetBranch::docbook(XMLStream & xs, OutputParams const & runparams) const
{
- return producesOutput() ? InsetText::docbook(os, runparams) : 0;
+ if (producesOutput()) {
+ OutputParams rp = runparams;
+ rp.par_begin = 0;
+ rp.par_end = text().paragraphs().size();
+ docbookParagraphs(text(), buffer(), xs, rp);
+ }
}
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const &) const;
+ void docbook(XMLStream &, OutputParams const &) const;
///
docstring xhtml(XMLStream &, OutputParams const &) const;
///
#include "Language.h"
#include "LyXRC.h"
#include "MetricsInfo.h"
+#include "xml.h"
#include "output_latex.h"
#include "output_xhtml.h"
#include "OutputParams.h"
}
-int InsetCaption::docbook(odocstream & os,
- OutputParams const & runparams) const
+void InsetCaption::docbook(XMLStream &, OutputParams const &) const
{
- int ret;
- os << "<title>";
- ret = InsetText::docbook(os, runparams);
- os << "</title>\n";
- return ret;
+ // This function should never be called (rather InsetFloat::docbook, the titles should be skipped in floats).
}
}
+void InsetCaption::getCaptionAsDocBook(XMLStream & xs,
+ OutputParams const & runparams) const
+{
+ if (runparams.docbook_in_float)
+ return;
+
+ // Ignore full_label_, as the DocBook processor will deal with the numbering.
+ InsetText::XHTMLOptions const opts =
+ InsetText::WriteLabel | InsetText::WriteInnerTag;
+ InsetText::docbook(xs, runparams, opts);
+}
+
+
docstring InsetCaption::getCaptionAsHTML(XMLStream & xs,
OutputParams const & runparams) const
{
void getArgument(otexstream & os, OutputParams const &) const;
/// return the caption text
int getCaptionAsPlaintext(odocstream & os, OutputParams const &) const;
+ /// write the caption text as DocBook in os
+ void getCaptionAsDocBook(XMLStream & os, OutputParams const &) const;
/// return the caption text as HTML
docstring getCaptionAsHTML(XMLStream & os, OutputParams const &) const;
///
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream & os, OutputParams const & runparams) const;
+ void docbook(XMLStream &, OutputParams const &) const;
///
docstring xhtml(XMLStream & os, OutputParams const & runparams) const;
///
}
+docstring InsetCaptionable::getCaptionDocBook(OutputParams const & runparams) const
+{
+ InsetCaption const * ins = getCaptionInset();
+ if (ins == nullptr)
+ return docstring();
+
+ odocstringstream ods;
+ XMLStream xs(ods);
+ ins->getCaptionAsDocBook(xs, runparams);
+ return ods.str();
+}
+
+
docstring InsetCaptionable::getCaptionHTML(OutputParams const & runparams) const
{
InsetCaption const * ins = getCaptionInset();
///
docstring getCaptionHTML(OutputParams const &) const;
///
+ docstring getCaptionDocBook(OutputParams const &) const;
+ ///
virtual void setCaptionType(std::string const & type);
/// are our captions subcaptions?
virtual bool hasSubCaptions(ParIterator const &) const { return false; }
#include "FuncStatus.h"
#include "LaTeXFeatures.h"
#include "output_xhtml.h"
+#include <output_docbook.h>
#include "ParIterator.h"
#include "texstream.h"
#include "TocBackend.h"
}
-int InsetCitation::docbook(odocstream & os, OutputParams const &) const
+void InsetCitation::docbook(XMLStream & xs, OutputParams const &) const
{
- os << from_ascii("<citation>")
- << cleanupWhitespace(getParam("key"))
- << from_ascii("</citation>");
- return 0;
+ if (getCmdName() == "nocite")
+ return;
+
+ // Split the different citations (on ","), so that one tag can be output for each of them.
+ string citations = to_utf8(getParam("key")); // Citation strings are not supposed to be too fancy.
+ if (citations.find(',') == string::npos) {
+ xs << xml::CompTag("biblioref", "endterm=\"" + citations + "\"");
+ } else {
+ size_t pos = 0;
+ while (pos != string::npos) {
+ pos = citations.find(',');
+ xs << xml::CompTag("biblioref", "endterm=\"" + citations.substr(0, pos) + "\"");
+ citations.erase(0, pos + 1);
+
+ if (pos != string::npos) {
+ xs << ", ";
+ }
+ }
+ }
}
docstring InsetCitation::xhtml(XMLStream & xs, OutputParams const &) const
{
- string const & cmd = getCmdName();
- if (cmd == "nocite")
+ if (getCmdName() == "nocite")
return docstring();
// have to output this raw, because generateLabel() will include tags
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const &) const;
+ void docbook(XMLStream &, OutputParams const &) const;
///
docstring xhtml(XMLStream &, OutputParams const &) const;
///
}
-int InsetCommand::docbook(odocstream &, OutputParams const &) const
+void InsetCommand::docbook(XMLStream &, OutputParams const &) const
{
- return 0;
+ return;
}
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const & runparams) const;
+ void docbook(XMLStream &, OutputParams const &) const;
///
void validate(LaTeXFeatures & features) const;
///
}
}
-int InsetCounter::docbook(odocstream &, OutputParams const &) const
+void InsetCounter::docbook(odocstream &, OutputParams const &) const
{
// Here, we need to track counter values ourselves,
// since unlike in the LaTeX case, there is no external
// mechanism for doing that.
trackCounters(getCmdName());
- return 0;
}
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const &) const;
+ void docbook(odocstream &, OutputParams const &) const;
///
docstring xhtml(XMLStream &, OutputParams const &) const;
///
#include "Lexer.h"
#include "LyXAction.h"
#include "OutputParams.h"
+#include "xml.h"
#include "ParagraphParameters.h"
#include "Paragraph.h"
}
-int InsetERT::docbook(odocstream & os, OutputParams const &) const
+void InsetERT::docbook(XMLStream & xs, OutputParams const &) const
{
// FIXME can we do the same thing here as for LaTeX?
ParagraphList::const_iterator par = paragraphs().begin();
ParagraphList::const_iterator end = paragraphs().end();
- int lines = 0;
+ xs << XMLStream::ESCAPE_NONE << "<!-- ";
while (par != end) {
pos_type siz = par->size();
- for (pos_type i = 0; i < siz; ++i)
- os.put(par->getChar(i));
+ for (pos_type i = 0; i < siz; ++i) {
+ xs << par->getChar(i);
+ }
++par;
if (par != end) {
- os << "\n";
- ++lines;
+ xs << "\n";
}
}
-
- return lines;
+ xs << XMLStream::ESCAPE_NONE << " -->";
}
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const &) const;
+ void docbook(XMLStream &, OutputParams const &) const;
///
docstring xhtml(XMLStream &, OutputParams const &) const;
///
}
-int InsetExternal::docbook(odocstream & os,
- OutputParams const & runparams) const
+void InsetExternal::generateXML(XMLStream & xs, OutputParams const & runparams, std::string const & format) const
{
bool const external_in_tmpdir = !runparams.nice;
bool const dryrun = runparams.dryrun || runparams.inComment;
odocstringstream ods;
otexstream ots(ods);
external::RetVal retval =
- external::writeExternal(params_, "DocBook", buffer(), ots,
- *(runparams.exportdata), external_in_tmpdir, dryrun);
+ external::writeExternal(params_, format, buffer(), ots,
+ *(runparams.exportdata), external_in_tmpdir, dryrun);
if (retval == external::KILLED) {
LYXERR0("External template preparation killed.");
if (buffer().isClone() && buffer().isExporting())
throw ConversionException();
}
- os << ods.str();
- return int(count(ods.str().begin(), ods.str().end(), '\n'));
+ xs << XMLStream::ESCAPE_NONE << ods.str();
+}
+
+
+void InsetExternal::docbook(XMLStream & xs,
+ OutputParams const & runparams) const
+{
+ generateXML(xs, runparams, "DocBook");
}
docstring InsetExternal::xhtml(XMLStream & xs,
OutputParams const & runparams) const
{
- bool const external_in_tmpdir = !runparams.nice;
- bool const dryrun = runparams.dryrun || runparams.inComment;
- odocstringstream ods;
- otexstream ots(ods);
- external::RetVal retval =
- external::writeExternal(params_, "XHTML", buffer(), ots,
- *(runparams.exportdata), external_in_tmpdir, dryrun);
- if (retval == external::KILLED) {
- LYXERR0("External template preparation killed.");
- if (buffer().isClone() && buffer().isExporting())
- throw ConversionException();
- }
- xs << XMLStream::ESCAPE_NONE << ods.str();
+ generateXML(xs, runparams, "XHTML");
return docstring();
}
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const &) const;
+ void generateXML(XMLStream &, OutputParams const &, std::string const &) const;
+ ///
+ void docbook(XMLStream &, OutputParams const &) const;
/// For now, this does nothing. Someone who knows about this
/// should see what needs doing for XHTML output.
docstring xhtml(XMLStream &, OutputParams const &) const;
* Full author contact details are available in file CREDITS.
*/
+#include <typeinfo>
+
#include <config.h>
+#include <output_docbook.h>
#include <xml.h>
-#include "InsetFloat.h"
+#include "InsetBox.h"
#include "InsetCaption.h"
+#include "InsetFloat.h"
+#include "InsetGraphics.h"
+#include "InsetLabel.h"
#include "Buffer.h"
#include "BufferParams.h"
}
-int InsetFloat::docbook(odocstream & os, OutputParams const & runparams) const
+std::vector<const InsetBox *> findSubfiguresInParagraph(const Paragraph &par)
+{
+
+ // Don't make the hypothesis that all subfigures are in the same paragraph.
+ // Similarly, there may be several subfigures in the same paragraph (most likely case, based on the documentation).
+ // Any box is considered as a subfigure, even though the most likely case is \minipage.
+ std::vector<const InsetBox *> subfigures;
+ for (pos_type pos = 0; pos < par.size(); ++pos) {
+ const Inset *inset = par.getInset(pos);
+ if (!inset)
+ continue;
+ if (const auto box = dynamic_cast<const InsetBox *>(inset))
+ subfigures.push_back(box);
+ }
+ return subfigures;
+}
+
+
+const InsetLabel* findLabelInParagraph(const Paragraph &par)
{
- // FIXME Implement subfloat!
- // FIXME UNICODE
- os << '<' << from_ascii(params_.type) << '>';
- int const i = InsetText::docbook(os, runparams);
- os << "</" << from_ascii(params_.type) << '>';
+ for (pos_type pos = 0; pos < par.size(); ++pos) {
+ // If this inset is a subfigure, skip it.
+ const Inset *inset = par.getInset(pos);
+ if (dynamic_cast<const InsetBox *>(inset)) {
+ continue;
+ }
- return i;
+ // Maybe an inset is directly a label, in which case no more work is needed.
+ if (inset && dynamic_cast<const InsetLabel *>(inset))
+ return dynamic_cast<const InsetLabel *>(inset);
+
+ // More likely, the label is hidden in an inset of a paragraph (only if a subtype of InsetText).
+ if (!dynamic_cast<const InsetText *>(inset))
+ continue;
+
+ auto insetAsText = dynamic_cast<const InsetText *>(inset);
+ auto itIn = insetAsText->paragraphs().begin();
+ auto endIn = insetAsText->paragraphs().end();
+ for (; itIn != endIn; ++itIn) {
+ for (pos_type posIn = 0; posIn < itIn->size(); ++posIn) {
+ const Inset *insetIn = itIn->getInset(posIn);
+ if (insetIn && dynamic_cast<const InsetLabel *>(insetIn)) {
+ return dynamic_cast<const InsetLabel *>(insetIn);
+ }
+ }
+ }
+
+ // Obviously, this solution does not scale with more levels of paragraphs-insets, but this should be enough.
+ }
+
+ return nullptr;
+}
+
+
+const InsetCaption* findCaptionInParagraph(const Paragraph &par)
+{
+ // Don't dive too deep, otherwise, this could be a subfigure caption.
+ for (pos_type pos = 0; pos < par.size(); ++pos) {
+ // If this inset is a subfigure, skip it.
+ const Inset *inset = par.getInset(pos);
+ if (dynamic_cast<const InsetBox *>(inset))
+ continue;
+
+ if (inset && dynamic_cast<const InsetCaption *>(inset))
+ return dynamic_cast<const InsetCaption *>(inset);
+ }
+
+ return nullptr;
+}
+
+
+void InsetFloat::docbook(XMLStream & xs, OutputParams const & runparams) const
+{
+ // Determine whether the float has a title or not. For this, iterate through the paragraphs and look
+ // for an InsetCaption. Do the same for labels and subfigures.
+ // The caption and the label for each subfigure is handled by recursive calls.
+ const InsetCaption* caption = nullptr;
+ const InsetLabel* label = nullptr;
+ std::vector<const InsetBox *> subfigures;
+
+ auto end = paragraphs().end();
+ for (auto it = paragraphs().begin(); it != end; ++it) {
+ std::vector<const InsetBox *> foundSubfigures = findSubfiguresInParagraph(*it);
+ if (!foundSubfigures.empty()) {
+ subfigures.reserve(subfigures.size() + foundSubfigures.size());
+ subfigures.insert(subfigures.end(), foundSubfigures.begin(), foundSubfigures.end());
+ }
+
+ if (!caption)
+ caption = findCaptionInParagraph(*it);
+ if (!label)
+ label = findLabelInParagraph(*it);
+ }
+
+ // Gather a few things from global environment that are shared between all following cases.
+ FloatList const &floats = buffer().params().documentClass().floats();
+ Floating const &ftype = floats.getType(params_.type);
+ string const &titleTag = ftype.docbookCaption();
+
+ // Ensure there is no label output, it is supposed to be handled as xml:id.
+ OutputParams rpNoLabel = runparams;
+ if (label)
+ rpNoLabel.docbook_anchors_to_ignore.emplace(label->screenLabel());
+
+ // Ensure the float does not output its caption, as it is handled here (DocBook mandates a specific place for
+ // captions, they cannot appear at the end of the float, albeit LyX is happy with that).
+ OutputParams rpNoTitle = runparams;
+ rpNoTitle.docbook_in_float = true;
+
+ // Deal with subfigures.
+ if (!subfigures.empty()) {
+ // First, open the formal group.
+ docstring attr = docstring();
+ if (label)
+ attr += "xml:id=\"" + xml::cleanID(label->screenLabel()) + "\"";
+
+ xs.startDivision(false);
+ xs << xml::StartTag("formalgroup", attr);
+ xs << xml::CR();
+
+ xs << xml::StartTag("title", attr);
+ if (caption) {
+ caption->getCaptionAsDocBook(xs, rpNoLabel);
+ } else {
+ xs << "No caption";
+ // No caption has been detected, but this tag is required for the document to be valid DocBook.
+ }
+ xs << xml::EndTag("title");
+ xs << xml::CR();
+
+ // Deal with each subfigure individually. This should also deal with their caption and their label.
+ // This should be a recursive call to InsetFloat.
+ for (const InsetBox *subfigure: subfigures) {
+ // If there is no InsetFloat in the paragraphs, output a warning.
+ bool foundInsetFloat = false;
+ for (auto it = subfigure->paragraphs().begin(); it != subfigure->paragraphs().end(); ++it) {
+ for (pos_type posIn = 0; posIn < it->size(); ++posIn) {
+ const Inset *inset = it->getInset(posIn);
+ if (inset && dynamic_cast<const InsetFloat*>(inset)) {
+ foundInsetFloat = true;
+ break;
+ }
+ }
+
+ if (foundInsetFloat)
+ break;
+ }
+
+ if (!foundInsetFloat)
+ xs << XMLStream::ESCAPE_NONE << "Error: no float found in the box. "
+ "To use subfigures in DocBook, elements must be wrapped in a float "
+ "inset and have a title/caption.";
+ // TODO: could also output a table, that would ensure that the document is correct and *displays* correctly (but without the right semantics), instead of just an error.
+
+ // Finally, recurse.
+ subfigure->docbook(xs, runparams);
+ }
+
+ // Every subfigure is done: close the formal group.
+ xs << xml::EndTag("formalgroup");
+ xs << xml::CR();
+ xs.endDivision();
+ }
+
+ // Here, ensured not to have subfigures.
+
+ // Organisation: <float> <title if any/> <contents without title/> </float>
+ docstring attr = docstring();
+ if (label)
+ attr += "xml:id=\"" + xml::cleanID(label->screenLabel()) + "\"";
+ if (!ftype.docbookAttr().empty()) {
+ if (!attr.empty())
+ attr += " ";
+ attr += from_utf8(ftype.docbookAttr());
+ }
+
+ xs << xml::StartTag(ftype.docbookTag(caption != nullptr), attr);
+ xs << xml::CR();
+ if (caption != nullptr) {
+ xs << xml::StartTag(titleTag);
+ caption->getCaptionAsDocBook(xs, rpNoLabel);
+ xs << xml::EndTag(titleTag);
+ xs << xml::CR();
+ }
+ InsetText::docbook(xs, rpNoTitle);
+ xs << xml::EndTag(ftype.docbookTag(caption != nullptr));
+ xs << xml::CR();
}
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const &) const;
+ void docbook(XMLStream &, OutputParams const &) const;
///
docstring xhtml(XMLStream &, OutputParams const &) const;
///
///
void latex(otexstream &, OutputParams const &) const;
///
- int docbook(odocstream &, OutputParams const &) const { return 0; }
+ void docbook(XMLStream &, OutputParams const &) const { return; }
///
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
*/
#include <config.h>
+#include <output_docbook.h>
#include "InsetFoot.h"
#include "InsetBox.h"
}
-int InsetFoot::docbook(odocstream & os, OutputParams const & runparams) const
+void InsetFoot::docbook(XMLStream & xs, OutputParams const & runparams) const
{
- os << "<footnote>";
- int const i = InsetText::docbook(os, runparams);
- os << "</footnote>";
-
- return i;
+ OutputParams rp = runparams;
+ rp.docbook_force_pars = true;
+ rp.docbook_in_par = false;
+ InsetText::docbook(xs, rp);
}
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const &) const;
+ void docbook(XMLStream &, OutputParams const &) const;
///
void validate(LaTeXFeatures & features) const;
/// Update the counters of this inset and of its contents
#include <algorithm>
#include <sstream>
-#include <tuple>
+#include <output_docbook.h>
using namespace std;
using namespace lyx::support;
// Convert everything else to png
return "png";
}
- // for HTML, we leave the known formats and otherwise convert to png
- if (runparams.flavor == OutputParams::HTML) {
+
+ // for HTML and DocBook, we leave the known formats and otherwise convert to png
+ if (runparams.flavor == OutputParams::HTML || runparams.flavor == OutputParams::DOCBOOK5) {
Format const * const f = theFormats().getFormat(format);
// Convert vector graphics to svg
if (f && f->vectorFormat() && theConverters().isReachable(format, "svg"))
}
}
-
if (!params().special.empty())
options << from_ascii(params().special) << " ";
}
-static int writeImageObject(char const * format, odocstream & os,
- OutputParams const & runparams, docstring const & graphic_label,
- docstring const & attributes)
-{
- if (runparams.flavor != OutputParams::DOCBOOK5)
- os << "<![ %output.print." << format
- << "; [" << endl;
-
- os <<"<imageobject><imagedata fileref=\"&"
- << graphic_label
- << ";."
- << format
- << "\" "
- << attributes;
-
- if (runparams.flavor == OutputParams::DOCBOOK5)
- os << " role=\"" << format << "\"/>" ;
- else
- os << " format=\"" << format << "\">" ;
-
- os << "</imageobject>";
-
- if (runparams.flavor != OutputParams::DOCBOOK5)
- os << endl << "]]>" ;
-
- return runparams.flavor == OutputParams::DOCBOOK5 ? 0 : 2;
-}
-
-
// For explanation on inserting graphics into DocBook checkout:
// http://en.tldp.org/LDP/LDP-Author-Guide/html/inserting-pictures.html
// See also the docbook guide at http://www.docbook.org/
-int InsetGraphics::docbook(odocstream & os,
- OutputParams const & runparams) const
+void InsetGraphics::docbook(XMLStream & xs, OutputParams const & runparams) const
{
- // In DocBook v5.0, the graphic tag will be eliminated from DocBook, will
- // need to switch to MediaObject. However, for now this is sufficient and
- // easier to use.
- if (runparams.flavor == OutputParams::DOCBOOK5)
- runparams.exportdata->addExternalFile("docbook-xml",
- params().filename);
- else
- runparams.exportdata->addExternalFile("docbook",
- params().filename);
-
- os << "<inlinemediaobject>";
-
- int r = 0;
- docstring attributes = createDocBookAttributes();
- r += writeImageObject("png", os, runparams, graphic_label, attributes);
- r += writeImageObject("pdf", os, runparams, graphic_label, attributes);
- r += writeImageObject("eps", os, runparams, graphic_label, attributes);
- r += writeImageObject("bmp", os, runparams, graphic_label, attributes);
-
- os << "</inlinemediaobject>";
- return r;
+ string fn = params().filename.relFileName(runparams.export_folder);
+ string tag = runparams.docbook_in_float ? "mediaobject" : "inlinemediaobject";
+
+ xs << xml::StartTag(tag);
+ xs << xml::CR();
+ xs << xml::StartTag("imageobject");
+ xs << xml::CR();
+ xs << xml::CompTag("imagedata", "fileref=\"" + fn + "\" " + to_utf8(createDocBookAttributes()));
+ xs << xml::CR();
+ xs << xml::EndTag("imageobject");
+ xs << xml::CR();
+ xs << xml::EndTag(tag);
+ xs << xml::CR();
}
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const &) const;
+ void docbook(XMLStream &, OutputParams const &) const;
///
docstring xhtml(XMLStream & os, OutputParams const &) const;
/** Tell LyX what the latex features you need i.e. what latex packages
*/
#include <config.h>
+#include <output_docbook.h>
#include "InsetHyperlink.h"
}
-int InsetHyperlink::docbook(odocstream & os, OutputParams const &) const
+void InsetHyperlink::docbook(XMLStream & xs, OutputParams const &) const
{
- os << "<ulink url=\""
- << subst(getParam("target"), from_ascii("&"), from_ascii("&"))
- << "\">"
- << xml::escapeString(getParam("name"))
- << "</ulink>";
- return 0;
+ xs << xml::StartTag("link", "xlink:href=\"" + subst(getParam("target"), from_ascii("&"), from_ascii("&")) + "\"");
+ xs << xml::escapeString(getParam("name"));
+ xs << xml::EndTag("link");
}
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const &) const;
+ void docbook(XMLStream &, OutputParams const &) const;
///
docstring xhtml(XMLStream &, OutputParams const &) const;
//@}
}
+void InsetIPA::docbook(XMLStream & xs, OutputParams const & runparams) const
+{
+ OutputParams rp(runparams);
+ rp.inIPA = true;
+ InsetText::docbook(xs, rp);
+}
+
+
docstring InsetIPA::xhtml(XMLStream & xs, OutputParams const & runparams_in) const
{
OutputParams runparams(runparams_in);
///
void latex(otexstream &, OutputParams const &) const;
///
+ void docbook(XMLStream &, OutputParams const &) const;
+ ///
docstring xhtml(XMLStream & xs, OutputParams const &) const;
///
void validate(LaTeXFeatures & features) const;
#include "LaTeXFeatures.h"
#include "Lexer.h"
#include "MetricsInfo.h"
-#include "output_xhtml.h"
+#include "xml.h"
#include "texstream.h"
#include "frontends/FontMetrics.h"
}
-int InsetIPADeco::docbook(odocstream & os, OutputParams const & runparams) const
+void InsetIPADeco::docbook(XMLStream & xs, OutputParams const & runparams) const
{
- // FIXME: Any docbook option here?
- return InsetCollapsible::docbook(os, runparams);
+ // The special combining character must be put in the middle, between the two other characters.
+ // It will not work if there is anything else than two pure characters, so going back to plaintext.
+ odocstringstream ods;
+ int h = (int)(InsetText::plaintext(ods, runparams) / 2);
+ docstring result = ods.str();
+ docstring const before = result.substr(0, h);
+ docstring const after = result.substr(h, result.size());
+
+ xs << XMLStream::ESCAPE_NONE << before;
+ if (params_.type == InsetIPADecoParams::Toptiebar)
+ xs << XMLStream::ESCAPE_NONE << "͡";
+ else if (params_.type == InsetIPADecoParams::Bottomtiebar)
+ xs << XMLStream::ESCAPE_NONE << "͜";
+ xs << XMLStream::ESCAPE_NONE << after;
}
}
-int InsetIPAChar::docbook(odocstream & /*os*/, OutputParams const &) const
-{
- switch (kind_) {
- case TONE_FALLING:
- case TONE_RISING:
- case TONE_HIGH_RISING:
- case TONE_LOW_RISING:
- case TONE_HIGH_RISING_FALLING:
- // FIXME
- LYXERR0("IPA tone macros not yet implemented with DocBook!");
- break;
- }
- return 0;
+void InsetIPAChar::docbook(XMLStream & xs, OutputParams const &) const
+{
+ switch (kind_) {
+ case TONE_FALLING:
+ xs << XMLStream::ESCAPE_NONE << "˥";
+ xs << XMLStream::ESCAPE_NONE << "˩";
+ break;
+ case TONE_RISING:
+ xs << XMLStream::ESCAPE_NONE << "˩";
+ xs << XMLStream::ESCAPE_NONE << "˥";
+ break;
+ case TONE_HIGH_RISING:
+ xs << XMLStream::ESCAPE_NONE << "˧";
+ xs << XMLStream::ESCAPE_NONE << "˥";
+ break;
+ case TONE_LOW_RISING:
+ xs << XMLStream::ESCAPE_NONE << "˩";
+ xs << XMLStream::ESCAPE_NONE << "˧";
+ break;
+ case TONE_HIGH_RISING_FALLING:
+ xs << XMLStream::ESCAPE_NONE << "˨";
+ xs << XMLStream::ESCAPE_NONE << "˥";
+ xs << XMLStream::ESCAPE_NONE << "˨";
+ break;
+ }
}
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const &) const;
+ void docbook(XMLStream &, OutputParams const &) const;
///
docstring xhtml(XMLStream &, OutputParams const &) const;
///
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const &) const;
+ void docbook(XMLStream &, OutputParams const &) const;
///
docstring xhtml(XMLStream &, OutputParams const &) const;
///
op.par_begin = 0;
op.par_end = 0;
ibuf->writeLyXHTMLSource(xs.os(), op, Buffer::IncludedFile);
- } else
- xs << XMLStream::ESCAPE_NONE
- << "<!-- Included file: "
- << from_utf8(included_file.absFileName())
- << XMLStream::ESCAPE_NONE
- << " -->";
+ } else {
+ xs << XMLStream::ESCAPE_NONE << "<!-- Included file: ";
+ xs << from_utf8(included_file.absFileName());
+ xs << XMLStream::ESCAPE_NONE << " -->";
+ }
return docstring();
}
}
-int InsetInclude::docbook(odocstream & os, OutputParams const & runparams) const
+void InsetInclude::docbook(XMLStream & xs, OutputParams const & rp) const
{
- string incfile = ltrim(to_utf8(params()["filename"]));
+ if (rp.inComment)
+ return;
- // Do nothing if no file name has been specified
- if (incfile.empty())
- return 0;
+ // For verbatim and listings, we just include the contents of the file as-is.
+ bool const verbatim = isVerbatim(params());
+ bool const listing = isListings(params());
+ if (listing || verbatim) {
+ if (listing)
+ xs << xml::StartTag("programlisting");
+ else if (verbatim)
+ xs << xml::StartTag("literallayout");
- string const included_file = includedFileName(buffer(), params()).absFileName();
- string exppath = incfile;
- if (!runparams.export_folder.empty()) {
- exppath = makeAbsPath(exppath, runparams.export_folder).realPath();
- FileName(exppath).onlyPath().createPath();
- }
+ // FIXME: We don't know the encoding of the file, default to UTF-8.
+ xs << includedFileName(buffer(), params()).fileContents("UTF-8");
- // write it to a file (so far the complete file)
- string const exportfile = changeExtension(exppath, ".sgml");
- DocFileName writefile(changeExtension(included_file, ".sgml"));
+ if (listing)
+ xs << xml::EndTag("programlisting");
+ else if (verbatim)
+ xs << xml::EndTag("literallayout");
- Buffer * tmp = loadIfNeeded();
- if (tmp) {
- if (recursion_error_)
- return 0;
+ return;
+ }
- string const mangled = writefile.mangledFileName();
- writefile = makeAbsPath(mangled,
- buffer().masterBuffer()->temppath());
- if (!runparams.nice)
- incfile = mangled;
+ // We don't (yet) know how to Input or Include non-LyX files.
+ // (If we wanted to get really arcane, we could run some tex2html
+ // converter on the included file. But that's just masochistic.)
+ FileName const included_file = includedFileName(buffer(), params());
+ if (!isLyXFileName(included_file.absFileName())) {
+ if (!rp.silent)
+ frontend::Alert::warning(_("Unsupported Inclusion"),
+ bformat(_("LyX does not know how to include non-LyX files when "
+ "generating DocBook output. Offending file:\n%1$s"),
+ ltrim(params()["filename"])));
+ return;
+ }
- LYXERR(Debug::LATEX, "incfile:" << incfile);
- LYXERR(Debug::LATEX, "exportfile:" << exportfile);
- LYXERR(Debug::LATEX, "writefile:" << writefile);
+ // In the other cases, we will generate the HTML and include it.
+ Buffer const * const ibuf = loadIfNeeded();
+ if (!ibuf)
+ return;
- tmp->makeDocBookFile(writefile, runparams, Buffer::OnlyBody);
- }
+ if (recursion_error_)
+ return;
- runparams.exportdata->addExternalFile("docbook", writefile,
- exportfile);
- runparams.exportdata->addExternalFile("docbook-xml", writefile,
- exportfile);
+ // are we generating only some paragraphs, or all of them?
+ bool const all_pars = !rp.dryrun ||
+ (rp.par_begin == 0 &&
+ rp.par_end == (int) buffer().text().paragraphs().size());
- if (isVerbatim(params()) || isListings(params())) {
- os << "<inlinegraphic fileref=\""
- << '&' << include_label << ';'
- << "\" format=\"linespecific\">";
+ OutputParams op = rp;
+ if (all_pars) {
+ op.par_begin = 0;
+ op.par_end = 0;
+ ibuf->writeDocBookSource(xs.os(), op, Buffer::IncludedFile);
} else
- os << '&' << include_label << ';';
-
- return 0;
+ xs << XMLStream::ESCAPE_NONE
+ << "<!-- Included file: "
+ << from_utf8(included_file.absFileName())
+ << XMLStream::ESCAPE_NONE
+ << " -->";
}
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const &) const;
+ void docbook(XMLStream &, OutputParams const &) const;
///
docstring xhtml(XMLStream &, OutputParams const &) const;
///
#include "frontends/alert.h"
#include <algorithm>
+#include <set>
#include <ostream>
+#include <QThreadStorage>
+
using namespace std;
using namespace lyx::support;
}
-int InsetIndex::docbook(odocstream & os, OutputParams const & runparams) const
+void InsetIndex::docbook(XMLStream & xs, OutputParams const & runparams) const
{
- os << "<indexterm><primary>";
- int const i = InsetText::docbook(os, runparams);
- os << "</primary></indexterm>";
- return i;
+ // Get the content of the inset as LaTeX, as some things may be encoded as ERT (like {}).
+ odocstringstream odss;
+ otexstream ots(odss);
+ InsetText::latex(ots, runparams);
+ docstring latexString = trim(odss.str());
+
+ // Check whether there are unsupported things.
+ if (latexString.find(from_utf8("@")) != latexString.npos) {
+ docstring error = from_utf8("Unsupported feature: an index entry contains an @. "
+ "Complete entry: \"") + latexString + from_utf8("\"");
+ LYXERR0(error);
+ xs << XMLStream::ESCAPE_NONE << (from_utf8("<!-- Output Error: ") + error + from_utf8(" -->\n"));
+ }
+
+ // Handle several indices.
+ docstring indexType = from_utf8("");
+ if (buffer().masterBuffer()->params().use_indices) {
+ indexType += " type=\"" + params_.index + "\"";
+ }
+
+ // Split the string into its main constituents: terms, and command (see, see also, range).
+ size_t positionVerticalBar = latexString.find(from_ascii("|")); // What comes before | is (sub)(sub)entries.
+ docstring indexTerms = latexString.substr(0, positionVerticalBar);
+ docstring command = latexString.substr(positionVerticalBar + 1);
+
+ // Handle primary, secondary, and tertiary terms (entries, subentries, and subsubentries, for LaTeX).
+ vector<docstring> terms = getVectorFromString(indexTerms, from_ascii("!"), false);
+
+ // Handle ranges. Happily, (| and |) can only be at the end of the string! However, | may be trapped by the
+ bool hasStartRange = latexString.find(from_ascii("|(")) != latexString.npos;
+ bool hasEndRange = latexString.find(from_ascii("|)")) != latexString.npos;
+ if (hasStartRange || hasEndRange) {
+ // Remove the ranges from the command if they do not appear at the beginning.
+ size_t index = 0;
+ while ((index = command.find(from_utf8("|("), index)) != std::string::npos)
+ command.erase(index, 1);
+ index = 0;
+ while ((index = command.find(from_utf8("|)"), index)) != std::string::npos)
+ command.erase(index, 1);
+
+ // Remove the ranges when they are the only vertical bar in the complete string.
+ if (command[0] == '(' || command[0] == ')')
+ command.erase(0, 1);
+ }
+
+ // Handle see and seealso. As "see" is a prefix of "seealso", the order of the comparisons is important.
+ // Both commands are mutually exclusive!
+ docstring see = from_utf8("");
+ vector<docstring> seeAlsoes;
+ if (command.substr(0, 3) == "see") {
+ // Unescape brackets.
+ size_t index = 0;
+ while ((index = command.find(from_utf8("\\{"), index)) != std::string::npos)
+ command.erase(index, 1);
+ index = 0;
+ while ((index = command.find(from_utf8("\\}"), index)) != std::string::npos)
+ command.erase(index, 1);
+
+ // Retrieve the part between brackets, and remove the complete seealso.
+ size_t positionOpeningBracket = command.find(from_ascii("{"));
+ size_t positionClosingBracket = command.find(from_ascii("}"));
+ docstring list = command.substr(positionOpeningBracket + 1, positionClosingBracket - positionOpeningBracket - 1);
+
+ // Parse the list of referenced entries (or a single one for see).
+ if (command.substr(0, 7) == "seealso") {
+ seeAlsoes = getVectorFromString(list, from_ascii(","), false);
+ } else {
+ see = list;
+
+ if (see.find(from_ascii(",")) != see.npos) {
+ docstring error = from_utf8("Several index terms found as \"see\"! Only one is acceptable. "
+ "Complete entry: \"") + latexString + from_utf8("\"");
+ LYXERR0(error);
+ xs << XMLStream::ESCAPE_NONE << (from_utf8("<!-- Output Error: ") + error + from_utf8(" -->\n"));
+ }
+ }
+
+ // Remove the complete see/seealso from the commands, in case there is something else to parse.
+ command = command.substr(positionClosingBracket + 1);
+ }
+
+ // Some parts of the strings are not parsed, as they do not have anything matching in DocBook: things like
+ // formatting the entry or the page number, other strings for sorting. https://wiki.lyx.org/Tips/Indexing
+ // If there are such things in the index entry, then this code may miserably fail. For example, for "Peter|(textbf",
+ // no range will be detected.
+ // TODO: Could handle formatting as significance="preferred"?
+
+ // Write all of this down.
+ if (terms.empty() && !hasEndRange) {
+ docstring error = from_utf8("No index term found! Complete entry: \"") + latexString + from_utf8("\"");
+ LYXERR0(error);
+ xs << XMLStream::ESCAPE_NONE << (from_utf8("<!-- Output Error: ") + error + from_utf8(" -->\n"));
+ } else {
+ // Generate the attributes for ranges. It is based on the terms that are indexed, but the ID must be unique
+ // to this indexing area (xml::cleanID does not guarantee this: for each call with the same arguments,
+ // the same legal ID is produced; here, as the input would be the same, the output must be, by design).
+ // Hence the thread-local storage, as the numbers must strictly be unique, and thus cannot be shared across
+ // a paragraph (making the solution used for HTML worthless). This solution is very similar to the one used in
+ // xml::cleanID.
+ docstring attrs = indexType;
+ if (hasStartRange || hasEndRange) {
+ // Append an ID if uniqueness is not guaranteed across the document.
+ static QThreadStorage<set<docstring>> tKnownTermLists;
+ static QThreadStorage<int> tID;
+
+ set<docstring> & knownTermLists = tKnownTermLists.localData();
+ int & ID = tID.localData();
+
+ if (!tID.hasLocalData()) {
+ tID.localData() = 0;
+ }
+
+ // Modify the index terms to add the unique ID if needed.
+ docstring newIndexTerms = indexTerms;
+ if (knownTermLists.find(indexTerms) != knownTermLists.end()) {
+ newIndexTerms += from_ascii(string("-") + to_string(ID));
+
+ // Only increment for the end of range, so that the same number is used for the start of range.
+ if (hasEndRange) {
+ ID++;
+ }
+ }
+
+ // Term list not yet known: add it to the set AFTER the end of range. After
+ if (knownTermLists.find(indexTerms) == knownTermLists.end() && hasEndRange) {
+ knownTermLists.insert(indexTerms);
+ }
+
+ // Generate the attributes.
+ docstring id = xml::cleanID(newIndexTerms);
+ if (hasStartRange) {
+ attrs += " class=\"startofrange\" xml:id=\"" + id + "\"";
+ } else {
+ attrs += " class=\"endofrange\" startref=\"" + id + "\"";
+ }
+ }
+
+ // Handle the index terms (including the specific index for this entry).
+ xs << xml::StartTag("indexterm", attrs);
+ if (terms.size() > 0) { // hasEndRange has no content.
+ xs << xml::StartTag("primary");
+ xs << terms[0];
+ xs << xml::EndTag("primary");
+ }
+ if (terms.size() > 1) {
+ xs << xml::StartTag("secondary");
+ xs << terms[1];
+ xs << xml::EndTag("secondary");
+ }
+ if (terms.size() > 2) {
+ xs << xml::StartTag("tertiary");
+ xs << terms[2];
+ xs << xml::EndTag("tertiary");
+ }
+
+ // Handle see and see also.
+ if (!see.empty()) {
+ xs << xml::StartTag("see");
+ xs << see;
+ xs << xml::EndTag("see");
+ }
+
+ if (!seeAlsoes.empty()) {
+ for (auto & entry : seeAlsoes) {
+ xs << xml::StartTag("seealso");
+ xs << entry;
+ xs << xml::EndTag("seealso");
+ }
+ }
+
+ // Close the entry.
+ xs << xml::EndTag("indexterm");
+ }
}
///
void read(Lexer & lex);
///
- int docbook(odocstream &, OutputParams const &) const;
+ void docbook(XMLStream &, OutputParams const &) const;
///
docstring xhtml(XMLStream &, OutputParams const &) const;
///
#include "InsetIterator.h"
#include "Language.h"
#include "LyX.h"
-#include "output_xhtml.h"
#include "ParIterator.h"
#include "xml.h"
#include "texstream.h"
}
-int InsetLabel::docbook(odocstream & os, OutputParams const &) const
+void InsetLabel::docbook(XMLStream & xs, OutputParams const & runparams) const
{
- os << "<!-- anchor id=\""
- << xml::cleanID(getParam("name"))
- << "\" -->";
- return 0;
+ // Output an anchor only if it has not been processed before.
+ if (runparams.docbook_anchors_to_ignore.find(getParam("name")) == runparams.docbook_anchors_to_ignore.end()) {
+ docstring attr = from_utf8("xml:id=\"") + xml::cleanID(getParam("name")) + from_utf8("\"");
+ xs << xml::CompTag("anchor", to_utf8(attr));
+ }
}
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const &) const;
+ void docbook(XMLStream &, OutputParams const &) const;
///
docstring xhtml(XMLStream &, OutputParams const &) const;
///
#include "support/lstrings.h"
#include <cstdlib>
+#include <output_docbook.h>
using namespace std;
}
-int InsetLine::docbook(odocstream & os, OutputParams const &) const
+void InsetLine::docbook(XMLStream & xs, OutputParams const &) const
{
- os << '\n';
- return 0;
+ xs << xml::CR();
}
/// Inset inherited methods.
//@{
InsetCode lyxCode() const { return LINE_CODE; }
- int docbook(odocstream &, OutputParams const &) const;
- /// Does nothing at the moment.
+ void docbook(XMLStream &, OutputParams const &) const;
docstring xhtml(XMLStream &, OutputParams const &) const;
bool hasSettings() const { return true; }
void metrics(MetricsInfo &, Dimension &) const;
#include "LaTeXFeatures.h"
#include "Lexer.h"
#include "output_latex.h"
+#include "output_docbook.h"
#include "output_xhtml.h"
#include "OutputParams.h"
#include "TextClass.h"
}
+void InsetListings::docbook(XMLStream & xs, OutputParams const & rp) const
+{
+ InsetLayout const & il = getLayout();
+
+ // Forge the attributes.
+ string attrs;
+ if (!il.docbookattr().empty())
+ attrs += " role=\"" + il.docbookattr() + "\"";
+ string const lang = params().getParamValue("language");
+ if (!lang.empty())
+ attrs += " language=\"" + lang + "\"";
+ xs << xml::StartTag(il.docbooktag(), attrs);
+ xs.startDivision(false);
+
+ // Deal with the caption.
+ docstring caption = getCaptionDocBook(rp);
+ if (!caption.empty()) {
+ xs << xml::StartTag("bridgehead");
+ xs << XMLStream::ESCAPE_NONE;
+ xs << caption;
+ xs << xml::EndTag("bridgehead");
+ }
+
+ // Deal with the content of the listing.
+ OutputParams newrp = rp;
+ newrp.pass_thru = true;
+ newrp.docbook_make_pars = false;
+ newrp.par_begin = 0;
+ newrp.par_end = text().paragraphs().size();
+ newrp.docbook_in_listing = true;
+
+ docbookParagraphs(text(), buffer(), xs, newrp);
+
+ // Done with the listing.
+ xs.endDivision();
+ xs << xml::EndTag(il.docbooktag());
+}
+
+
string InsetListings::contextMenuName() const
{
return "context-listings";
///
docstring xhtml(XMLStream &, OutputParams const &) const;
///
+ void docbook(XMLStream &, OutputParams const &) const;
+ ///
void validate(LaTeXFeatures &) const;
///
bool showInsetDialog(BufferView *) const;
*/
#include <config.h>
+#include <output_docbook.h>
#include "InsetMarginal.h"
}
-int InsetMarginal::docbook(odocstream & os,
- OutputParams const & runparams) const
+void InsetMarginal::docbook(XMLStream & xs, OutputParams const & runparams) const
{
- os << "<note role=\"margin\">";
- int const i = InsetText::docbook(os, runparams);
- os << "</note>";
-
- return i;
+ // Implementation as per http://www.sagehill.net/docbookxsl/SideFloats.html
+ // Unfortunately, only for XSL-FO output with the default style sheets, hence the role.
+ xs << xml::StartTag("sidebar", "role=\"margin\"");
+ xs << xml::CR();
+ xs << "<?dbfo float-type=\"margin.note\"?>";
+ InsetText::docbook(xs, runparams);
+ xs << xml::EndTag("sidebar");
}
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const & runparams) const;
+ void docbook(XMLStream &, OutputParams const & runparams) const;
/// Is the content of this inset part of the immediate (visible) text sequence?
bool isPartOfTextSequence() const { return false; }
private:
*/
#include <config.h>
+#include <output_docbook.h>
#include "InsetNewline.h"
}
-int InsetNewline::docbook(odocstream & os, OutputParams const &) const
+void InsetNewline::docbook(XMLStream & xs, OutputParams const & runparams) const
{
- os << '\n';
- return 0;
+ if (runparams.docbook_in_par) {
+ xs.closeFontTags();
+ xs << xml::EndTag("para");
+ xs << xml::StartTag("para");
+ }
}
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const &) const;
+ void docbook(XMLStream &, OutputParams const &) const;
///
docstring xhtml(XMLStream &, OutputParams const &) const;
///
#include "Lexer.h"
#include "MetricsInfo.h"
#include "OutputParams.h"
-#include "output_xhtml.h"
+#include "xml.h"
#include "texstream.h"
#include "Text.h"
#include "TextMetrics.h"
}
-int InsetNewpage::docbook(odocstream & os, OutputParams const &) const
+void InsetNewpage::docbook(XMLStream & os, OutputParams const &) const
{
- os << '\n';
- return 0;
+ os << xml::CR();
}
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const &) const;
+ void docbook(XMLStream &, OutputParams const &) const;
///
docstring xhtml(XMLStream &, OutputParams const &) const;
///
#include "Length.h"
#include "LyX.h"
#include "OutputParams.h"
-#include "output_xhtml.h"
#include "xml.h"
#include "texstream.h"
#include "TocBackend.h"
}
-int InsetNomencl::docbook(odocstream & os, OutputParams const &) const
+void InsetNomencl::docbook(XMLStream & xs, OutputParams const &) const
{
- os << "<glossterm linkend=\"" << nomenclature_entry_id << "\">"
- << xml::escapeString(getParam("symbol"))
- << "</glossterm>";
- return 0;
+ docstring attr = "linkend=\"" + nomenclature_entry_id + "\"";
+ xs << xml::StartTag("glossterm", attr);
+ xs << xml::escapeString(getParam("symbol"));
+ xs << xml::EndTag("glossterm");
}
}
-int InsetNomencl::docbookGlossary(odocstream & os) const
-{
- os << "<glossentry id=\"" << nomenclature_entry_id << "\">\n"
- << "<glossterm>"
- << xml::escapeString(getParam("symbol"))
- << "</glossterm>\n"
- << "<glossdef><para>"
- << xml::escapeString(getParam("description"))
- << "</para></glossdef>\n"
- <<"</glossentry>\n";
- return 4;
-}
-
-
void InsetNomencl::validate(LaTeXFeatures & features) const
{
features.require("nomencl");
}
-// FIXME This should be changed to use the TOC. Perhaps
-// that could be done when XHTML output is added.
-int InsetPrintNomencl::docbook(odocstream & os, OutputParams const &) const
+void InsetPrintNomencl::docbook(XMLStream & xs, OutputParams const & runparams) const
{
- os << "<glossary>\n";
- int newlines = 2;
- InsetIterator it = inset_iterator_begin(buffer().inset());
- while (it) {
- if (it->lyxCode() == NOMENCL_CODE) {
- newlines += static_cast<InsetNomencl const &>(*it).docbookGlossary(os);
- ++it;
- } else if (!it->producesOutput()) {
- // Ignore contents of insets that are not in output
- size_t const depth = it.depth();
- ++it;
- while (it.depth() > depth)
- ++it;
- } else {
- ++it;
- }
+ shared_ptr<Toc const> toc = buffer().tocBackend().toc("nomencl");
+
+ EntryMap entries;
+ Toc::const_iterator it = toc->begin();
+ Toc::const_iterator const en = toc->end();
+ for (; it != en; ++it) {
+ DocIterator dit = it->dit();
+ Paragraph const & par = dit.innerParagraph();
+ Inset const * inset = par.getInset(dit.top().pos());
+ if (!inset)
+ return;
+ InsetCommand const * ic = inset->asInsetCommand();
+ if (!ic)
+ return;
+
+ // FIXME We need a link to the paragraph here, so we
+ // need some kind of struct.
+ docstring const symbol = ic->getParam("symbol");
+ docstring const desc = ic->getParam("description");
+ docstring const prefix = ic->getParam("prefix");
+ docstring const sortas = prefix.empty() ? symbol : prefix;
+
+ entries[sortas] = NomenclEntry(symbol, desc, &par);
}
- os << "</glossary>\n";
- return newlines;
+
+ if (entries.empty())
+ return;
+
+ // As opposed to XHTML, no need to defer everything until the end of time, so write directly to xs.
+ // TODO: At least, that's what was done before...
+
+ docstring toclabel = translateIfPossible(from_ascii("Nomenclature"),
+ runparams.local_font->language()->lang());
+
+ xs << xml::StartTag("glossary");
+ xs << xml::CR();
+ xs << xml::StartTag("title");
+ xs << toclabel;
+ xs << xml::EndTag("title");
+ xs << xml::CR();
+
+ EntryMap::const_iterator eit = entries.begin();
+ EntryMap::const_iterator const een = entries.end();
+ for (; eit != een; ++eit) {
+ NomenclEntry const & ne = eit->second;
+ string const parid = ne.par->magicLabel();
+
+ xs << xml::StartTag("glossentry", "xml:id=\"" + parid + "\"");
+ xs << xml::CR();
+ xs << xml::StartTag("glossterm");
+ xs << ne.symbol;
+ xs << xml::EndTag("glossterm");
+ xs << xml::CR();
+ xs << xml::StartTag("glossdef");
+ xs << xml::CR();
+ xs << xml::StartTag("para");
+ xs << ne.desc;
+ xs << xml::EndTag("para");
+ xs << xml::CR();
+ xs << xml::EndTag("glossdef");
+ xs << xml::CR();
+ xs << xml::EndTag("glossentry");
+ xs << xml::CR();
+ }
+
+ xs << xml::EndTag("glossary");
+ xs << xml::CR();
}
///
InsetNomencl(Buffer * buf, InsetCommandParams const &);
- ///
- int docbookGlossary(odocstream &) const;
-
/// \name Public functions inherited from Inset class
//@{
///
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const &) const;
+ void docbook(XMLStream &, OutputParams const &) const;
/// Does nothing at the moment.
docstring xhtml(XMLStream &, OutputParams const &) const;
//@}
/// Updates needed features for this inset.
void validate(LaTeXFeatures & features) const;
///
- int docbook(odocstream &, OutputParams const &) const;
+ void docbook(XMLStream &, OutputParams const &) const;
///
docstring xhtml(XMLStream &, OutputParams const &) const;
///
#include <algorithm>
#include <sstream>
+#include <output_docbook.h>
using namespace std;
}
-int InsetNote::docbook(odocstream & os, OutputParams const & runparams_in) const
+void InsetNote::docbook(XMLStream & xs, OutputParams const & runparams_in) const
{
if (params_.type == InsetNoteParams::Note)
- return 0;
+ return;
OutputParams runparams(runparams_in);
if (params_.type == InsetNoteParams::Comment) {
- os << "<remark>\n";
+ xs << xml::StartTag("remark");
+ xs << xml::CR();
runparams.inComment = true;
// Ignore files that are exported inside a comment
runparams.exportdata.reset(new ExportData);
}
+ // Greyed out text is output as such (no way to mark text as greyed out with DocBook).
- int const n = InsetText::docbook(os, runparams);
-
- if (params_.type == InsetNoteParams::Comment)
- os << "\n</remark>\n";
+ InsetText::docbook(xs, runparams);
- // Return how many newlines we issued.
- //return int(count(str.begin(), str.end(), '\n'));
- return n + 1 + 2;
+ if (params_.type == InsetNoteParams::Comment) {
+ xs << xml::CR();
+ xs << xml::EndTag("remark");
+ xs << xml::CR();
+ }
}
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const &) const;
+ void docbook(XMLStream &, OutputParams const &) const;
///
docstring xhtml(XMLStream &, OutputParams const &) const;
///
}
-int InsetPhantom::docbook(odocstream & os, OutputParams const & runparams) const
+void InsetPhantom::docbook(XMLStream &, OutputParams const &) const
{
- docstring cmdname;
- switch (params_.type) {
- case InsetPhantomParams::Phantom:
- case InsetPhantomParams::HPhantom:
- case InsetPhantomParams::VPhantom:
- default:
- cmdname = from_ascii("phantom");
- break;
- }
- os << "<" + cmdname + ">";
- int const i = InsetCollapsible::docbook(os, runparams);
- os << "</" + cmdname + ">";
-
- return i;
+ return;
}
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const &) const;
+ void docbook(XMLStream &, OutputParams const &) const;
/// Makes no sense for XHTML.
docstring xhtml(XMLStream &, OutputParams const &) const;
///
#include "LyXRC.h"
#include "MetricsInfo.h"
#include "OutputParams.h"
-#include "output_xhtml.h"
#include "Paragraph.h"
#include "ParIterator.h"
#include "texstream.h"
+#include "xml.h"
#include "frontends/FontMetrics.h"
#include "frontends/Painter.h"
}
-int InsetQuotes::docbook(odocstream & os, OutputParams const &) const
+void InsetQuotes::docbook(XMLStream & xs, OutputParams const &) const
{
- os << getQuoteEntity(false);
- return 0;
+ xs << XMLStream::ESCAPE_NONE << getQuoteEntity(false);
}
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const &) const;
+ void docbook(XMLStream &, OutputParams const &) const;
///
docstring xhtml(XMLStream &, OutputParams const &) const;
}
-int InsetRef::docbook(odocstream & os, OutputParams const & runparams) const
+void InsetRef::docbook(XMLStream & xs, OutputParams const &) const
{
+ docstring const & ref = getParam("reference");
+ InsetLabel const * il = buffer().insetLabel(ref, true);
+ string const & cmd = params().getCmdName();
+ docstring linkend = xml::cleanID(ref);
+
+ // A name is provided, LyX will provide it. This is supposed to be a very rare case.
+ // Link with linkend, as is it within the document (not outside, in which case xlink:href is better suited).
docstring const & name = getParam("name");
- if (name.empty()) {
- if (runparams.flavor == OutputParams::DOCBOOK5) {
- os << "<xref linkend=\""
- << xml::cleanID(getParam("reference"))
- << "\" />";
- } else {
- os << "<xref linkend=\""
- << xml::cleanID(getParam("reference"))
- << "\">";
+ if (!name.empty()) {
+ docstring attr = from_utf8("linkend=\"") + linkend + from_utf8("\"");
+
+ xs << xml::StartTag("link", to_utf8(attr));
+ xs << name;
+ xs << xml::EndTag("link");
+ return;
+ }
+
+ // The DocBook processor will generate the name when required.
+ docstring display_before;
+ docstring display_after;
+ docstring role;
+
+ if (il && !il->counterValue().empty()) {
+ // Try to construct a label from the InsetLabel we reference.
+ if (cmd == "vref" || cmd == "pageref" || cmd == "vpageref" || cmd == "nameref" || cmd == "formatted") {
+ // "ref on page #", "on page #", etc. The DocBook processor deals with generating the right text,
+ // including in the right language.
+ role = from_ascii(cmd);
+
+ if (cmd == "formatted") {
+ // A formatted reference may have many parameters. Generate all of them as roles, the only
+ // way arbitrary parameters can be passed into DocBook.
+ if (buffer().params().use_refstyle && getParam("caps") == "true")
+ role += " refstyle-caps";
+ if (buffer().params().use_refstyle && getParam("plural") == "true")
+ role += " refstyle-plural";
+ }
+ } else if (cmd == "eqref") {
+ display_before = from_ascii("(");
+ display_after = from_ascii(")");
}
- } else {
- os << "<link linkend=\""
- << xml::cleanID(getParam("reference"))
- << "\">"
- << getParam("name")
- << "</link>";
+ // TODO: what about labelonly? I don't get how this is supposed to work...
}
- return 0;
+ // No name, ask DocBook to generate one.
+ docstring attr = from_utf8("linkend=\"") + ref + from_utf8("\"");
+ if (!role.empty())
+ attr += " role=\"" + role + "\"";
+ xs << display_before;
+ xs << xml::CompTag("xref", to_utf8(attr));
+ xs << display_after;
}
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const &) const;
+ void docbook(XMLStream &, OutputParams const &) const;
///
docstring xhtml(XMLStream &, OutputParams const &) const;
///
#include "frontends/Painter.h"
#include <algorithm>
+#include <output_docbook.h>
using namespace std;
}
-int InsetScript::docbook(odocstream & os, OutputParams const & runparams) const
+void InsetScript::docbook(XMLStream & xs, OutputParams const & runparams) const
{
docstring cmdname;
switch (params_.type) {
cmdname = from_ascii("superscript");
break;
}
- os << '<' + cmdname + '>';
- int const i = InsetText::docbook(os, runparams);
- os << "</" + cmdname + '>';
- return i;
+ xs << xml::StartTag(cmdname);
+ InsetText::docbook(xs, runparams);
+ xs << xml::EndTag(cmdname);
}
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const &) const;
+ void docbook(XMLStream &, OutputParams const &) const;
///
void edit(Cursor & cur, bool front,
EntryDirection entry_from = ENTRY_DIRECTION_IGNORE);
*/
#include <config.h>
+#include <output_docbook.h>
#include "InsetSeparator.h"
}
-int InsetSeparator::docbook(odocstream & os, OutputParams const &) const
+void InsetSeparator::docbook(XMLStream & xs, OutputParams const &) const
{
- os << '\n';
- return 0;
+ xs << xml::CR();
}
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const &) const;
+ void docbook(XMLStream &, OutputParams const &) const;
///
docstring xhtml(XMLStream &, OutputParams const &) const;
///
#include "Lexer.h"
#include "MetricsInfo.h"
#include "OutputParams.h"
-#include "output_xhtml.h"
#include "texstream.h"
+#include "xml.h"
#include "support/debug.h"
#include "support/docstream.h"
}
-int InsetSpace::docbook(odocstream & os, OutputParams const &) const
+void InsetSpace::docbook(XMLStream & xs, OutputParams const &) const
{
switch (params_.kind) {
case InsetSpaceParams::NORMAL:
- os << " ";
+ xs << XMLStream::ESCAPE_NONE << " ";
break;
case InsetSpaceParams::QUAD:
- os << " ";
+ xs << XMLStream::ESCAPE_NONE << " "; // HTML:  
break;
case InsetSpaceParams::QQUAD:
- os << "  ";
+ xs << XMLStream::ESCAPE_NONE << "  "; // HTML:   
break;
case InsetSpaceParams::ENSKIP:
- os << " ";
+ xs << XMLStream::ESCAPE_NONE << " "; // HTML:  
break;
case InsetSpaceParams::PROTECTED:
- os << " ";
+ xs << XMLStream::ESCAPE_NONE << " "; // HTML:
break;
case InsetSpaceParams::VISIBLE:
- os << "␣";
+ xs << XMLStream::ESCAPE_NONE << "␣";
break;
- case InsetSpaceParams::ENSPACE:
- os << "⁠ ⁠";
+ case InsetSpaceParams::ENSPACE: // HTML: ⁠ ⁠ (word joiners)
+ xs << XMLStream::ESCAPE_NONE << "⁠ ⁠";
break;
case InsetSpaceParams::THIN:
- os << " ";
+ xs << XMLStream::ESCAPE_NONE << " "; // HTML: &thinspace;
break;
case InsetSpaceParams::MEDIUM:
- os << " ";
+ xs << XMLStream::ESCAPE_NONE << " "; // HTML:  
break;
case InsetSpaceParams::THICK:
- os << " ";
+ xs << XMLStream::ESCAPE_NONE << " "; // HTML:  
break;
case InsetSpaceParams::NEGTHIN:
case InsetSpaceParams::NEGMEDIUM:
case InsetSpaceParams::NEGTHICK:
- // FIXME
- os << " ";
+ xs << XMLStream::ESCAPE_NONE << " "; // HTML:
break;
case InsetSpaceParams::HFILL:
case InsetSpaceParams::HFILL_PROTECTED:
- os << '\n';
- break;
case InsetSpaceParams::DOTFILL:
- // FIXME
- os << '\n';
- break;
case InsetSpaceParams::HRULEFILL:
- // FIXME
- os << '\n';
- break;
case InsetSpaceParams::LEFTARROWFILL:
case InsetSpaceParams::RIGHTARROWFILL:
case InsetSpaceParams::UPBRACEFILL:
case InsetSpaceParams::DOWNBRACEFILL:
case InsetSpaceParams::CUSTOM:
case InsetSpaceParams::CUSTOM_PROTECTED:
- // FIXME
- os << '\n';
+ xs << '\n';
break;
}
- return 0;
}
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const &) const;
+ void docbook(XMLStream &, OutputParams const &) const;
///
docstring xhtml(XMLStream &, OutputParams const &) const;
///
#include "Lexer.h"
#include "MetricsInfo.h"
#include "output_xhtml.h"
+#include "xml.h"
#include "texstream.h"
#include "frontends/FontMetrics.h"
}
-int InsetSpecialChar::docbook(odocstream & os, OutputParams const &) const
+void InsetSpecialChar::docbook(XMLStream & xs, OutputParams const &) const
{
switch (kind_) {
- case HYPHENATION:
- break;
- case ALLOWBREAK:
- // U+200B ZERO WIDTH SPACE (ZWSP)
- os.put(0x200b);
- break;
+ case HYPHENATION:
+ // Soft hyphen.
+ xs << XMLStream::ESCAPE_NONE << "­";
+ break;
+ case ALLOWBREAK:
+ // Zero-width space
+ xs << XMLStream::ESCAPE_NONE << "​";
+ break;
case LIGATURE_BREAK:
+ // Zero width non-joiner
+ xs << XMLStream::ESCAPE_NONE << "‌";
break;
case END_OF_SENTENCE:
- os << '.';
+ xs << '.';
break;
case LDOTS:
- os << "…";
+ // …
+ xs << XMLStream::ESCAPE_NONE << "…";
break;
case MENU_SEPARATOR:
- os << "&lyxarrow;";
+ // ⇒, right arrow.
+ xs << XMLStream::ESCAPE_NONE << "⇒";
break;
case SLASH:
- os << '/';
+ // ⁄, fractional slash.
+ xs << XMLStream::ESCAPE_NONE << "⁄";
break;
+ // Non-breaking hyphen.
case NOBREAKDASH:
- os << '-';
+ xs << XMLStream::ESCAPE_NONE << "‑";
break;
case PHRASE_LYX:
- os << "LyX";
+ xs << "LyX";
break;
case PHRASE_TEX:
- os << "TeX";
+ xs << "TeX";
break;
case PHRASE_LATEX2E:
- os << "LaTeX2";
- // ε U+03B5 GREEK SMALL LETTER EPSILON
- os.put(0x03b5);
+ // Lower-case epsilon.
+ xs << "LaTeX2" << XMLStream::ESCAPE_NONE << "ε";
break;
case PHRASE_LATEX:
- os << "LaTeX";
+ xs << "LaTeX";
break;
}
- return 0;
}
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const &) const;
+ void docbook(XMLStream &, OutputParams const &) const;
///
docstring xhtml(XMLStream &, OutputParams const &) const;
///
}
-int InsetTOC::docbook(odocstream & os, OutputParams const &) const
+void InsetTOC::docbook(XMLStream &, OutputParams const &) const
{
- if (getCmdName() == "tableofcontents")
- os << "<toc></toc>";
- return 0;
+ // TOC are generated automatically by the DocBook processor.
+ return;
}
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const &) const;
+ void docbook(XMLStream &, OutputParams const &) const;
///
docstring xhtml(XMLStream & xs, OutputParams const &) const;
///
#include "LyXRC.h"
#include "MetricsInfo.h"
#include "OutputParams.h"
+#include "xml.h"
#include "output_xhtml.h"
#include "Paragraph.h"
#include "ParagraphParameters.h"
}
-int Tabular::docbookRow(odocstream & os, row_type row,
- OutputParams const & runparams) const
+void Tabular::docbookRow(XMLStream & xs, row_type row,
+ OutputParams const & runparams, bool header) const
{
- int ret = 0;
+ string const celltag = header ? "th" : "td";
idx_type cell = getFirstCellInRow(row);
- os << "<row>\n";
+ xs << xml::StartTag("tr");
+ xs << xml::CR();
for (col_type c = 0; c < ncols(); ++c) {
- if (isPartOfMultiColumn(row, c))
+ if (isPartOfMultiColumn(row, c) || isPartOfMultiRow(row, c))
continue;
- os << "<entry align=\"";
+ stringstream attr;
+
+ Length const cwidth = column_info[c].p_width;
+ if (!cwidth.zero()) {
+ string const hwidth = cwidth.asHTMLString();
+ attr << "style =\"width: " << hwidth << ";\" ";
+ }
+
+ attr << "align='";
switch (getAlignment(cell)) {
case LYX_ALIGN_LEFT:
- os << "left";
+ attr << "left";
break;
case LYX_ALIGN_RIGHT:
- os << "right";
+ attr << "right";
break;
default:
- os << "center";
+ attr << "center";
break;
}
-
- os << "\" valign=\"";
+ attr << "'";
+ attr << " valign='";
switch (getVAlignment(cell)) {
case LYX_VALIGN_TOP:
- os << "top";
+ attr << "top";
break;
case LYX_VALIGN_BOTTOM:
- os << "bottom";
+ attr << "bottom";
break;
case LYX_VALIGN_MIDDLE:
- os << "middle";
+ attr << "middle";
}
- os << '"';
+ attr << "'";
- if (isMultiColumn(cell)) {
- os << " namest=\"col" << c << "\" ";
- os << "nameend=\"col" << c + columnSpan(cell) - 1 << '"';
- }
+ if (isMultiColumn(cell))
+ attr << " colspan='" << columnSpan(cell) << "'";
+ else if (isMultiRow(cell))
+ attr << " rowspan='" << rowSpan(cell) << "'";
- os << '>';
- ret += cellInset(cell)->docbook(os, runparams);
- os << "</entry>\n";
+ xs << xml::StartTag(celltag, attr.str(), true);
+ cellInset(cell)->docbook(xs, runparams);
+ xs << xml::EndTag(celltag);
+ xs << xml::CR();
++cell;
}
- os << "</row>\n";
- return ret;
+ xs << xml::EndTag("tr");
+ xs<< xml::CR();
}
-int Tabular::docbook(odocstream & os, OutputParams const & runparams) const
+void Tabular::docbook(XMLStream & xs, OutputParams const & runparams) const
{
- int ret = 0;
-
- //+---------------------------------------------------------------------
- //+ first the opening preamble +
- //+---------------------------------------------------------------------
-
- os << "<tgroup cols=\"" << ncols()
- << "\" colsep=\"1\" rowsep=\"1\">\n";
+ docstring ret;
- for (col_type c = 0; c < ncols(); ++c) {
- os << "<colspec colname=\"col" << c << "\" align=\"";
- switch (column_info[c].alignment) {
- case LYX_ALIGN_LEFT:
- os << "left";
- break;
- case LYX_ALIGN_RIGHT:
- os << "right";
- break;
- default:
- os << "center";
- break;
- }
- os << '"';
- if (runparams.flavor == OutputParams::DOCBOOK5)
- os << '/';
- os << ">\n";
- ++ret;
+ // Some tables are inline. Likely limitation: cannot output a table within a table; is that really a limitation?
+ bool hasTableStarted = xs.isTagOpen(xml::StartTag("informaltable")) || xs.isTagOpen(xml::StartTag("table"));
+ if (!hasTableStarted) {
+ xs << xml::StartTag("informaltable");
+ xs << xml::CR();
}
- //+---------------------------------------------------------------------
- //+ Long Tabular case +
- //+---------------------------------------------------------------------
-
- // output caption info
- // The caption flag wins over head/foot
+ // "Formal" tables have a caption and use the tag <table>; the distinction with <informaltable> is done outside.
if (haveLTCaption()) {
- os << "<caption>\n";
- ++ret;
- for (row_type r = 0; r < nrows(); ++r) {
- if (row_info[r].caption) {
- ret += docbookRow(os, r, runparams);
- }
- }
- os << "</caption>\n";
- ++ret;
+ xs << xml::StartTag("caption");
+ for (row_type r = 0; r < nrows(); ++r)
+ if (row_info[r].caption)
+ docbookRow(xs, r, runparams);
+ xs << xml::EndTag("caption");
+ xs << xml::CR();
}
+
// output header info
- if (haveLTHead(false) || haveLTFirstHead(false)) {
- os << "<thead>\n";
- ++ret;
+ bool const havefirsthead = haveLTFirstHead(false);
+ // if we have a first head, then we are going to ignore the
+ // headers for the additional pages, since there aren't any
+ // in XHTML. this test accomplishes that.
+ bool const havehead = !havefirsthead && haveLTHead(false);
+ if (havehead || havefirsthead) {
+ xs << xml::StartTag("thead") << xml::CR();
for (row_type r = 0; r < nrows(); ++r) {
- if ((row_info[r].endhead || row_info[r].endfirsthead) &&
- !row_info[r].caption) {
- ret += docbookRow(os, r, runparams);
+ if (((havefirsthead && row_info[r].endfirsthead) ||
+ (havehead && row_info[r].endhead)) &&
+ !row_info[r].caption) {
+ docbookRow(xs, r, runparams, true);
}
}
- os << "</thead>\n";
- ++ret;
+ xs << xml::EndTag("thead");
+ xs << xml::CR();
}
// output footer info
- if (haveLTFoot(false) || haveLTLastFoot(false)) {
- os << "<tfoot>\n";
- ++ret;
+ bool const havelastfoot = haveLTLastFoot(false);
+ // as before.
+ bool const havefoot = !havelastfoot && haveLTFoot(false);
+ if (havefoot || havelastfoot) {
+ xs << xml::StartTag("tfoot") << xml::CR();
for (row_type r = 0; r < nrows(); ++r) {
- if ((row_info[r].endfoot || row_info[r].endlastfoot) &&
- !row_info[r].caption) {
- ret += docbookRow(os, r, runparams);
+ if (((havelastfoot && row_info[r].endlastfoot) ||
+ (havefoot && row_info[r].endfoot)) &&
+ !row_info[r].caption) {
+ docbookRow(xs, r, runparams);
}
}
- os << "</tfoot>\n";
- ++ret;
+ xs << xml::EndTag("tfoot");
+ xs << xml::CR();
}
- //+---------------------------------------------------------------------
- //+ the single row and columns (cells) +
- //+---------------------------------------------------------------------
+ xs << xml::StartTag("tbody");
+ xs << xml::CR();
+ for (row_type r = 0; r < nrows(); ++r)
+ if (isValidRow(r))
+ docbookRow(xs, r, runparams);
+ xs << xml::EndTag("tbody");
+ xs << xml::CR();
- os << "<tbody>\n";
- ++ret;
- for (row_type r = 0; r < nrows(); ++r) {
- if (isValidRow(r)) {
- ret += docbookRow(os, r, runparams);
- }
+ if (!hasTableStarted) {
+ xs << xml::EndTag("informaltable");
+ xs << xml::CR();
}
- os << "</tbody>\n";
- ++ret;
- //+---------------------------------------------------------------------
- //+ the closing of the tabular +
- //+---------------------------------------------------------------------
-
- os << "</tgroup>";
- ++ret;
-
- return ret;
}
align = "right";
break;
}
- xs << xml::StartTag("div", "class='longtable' style='text-align: " + align + ";'")
- << xml::CR();
+ xs << xml::StartTag("div", "class='longtable' style='text-align: " + align + ";'");
+ xs << xml::CR();
// The caption flag wins over head/foot
if (haveLTCaption()) {
- xs << xml::StartTag("div", "class='longtable-caption' style='text-align: " + align + ";'")
- << xml::CR();
+ xs << xml::StartTag("div", "class='longtable-caption' style='text-align: " + align + ";'");
+ xs << xml::CR();
for (row_type r = 0; r < nrows(); ++r)
if (row_info[r].caption)
ret += xhtmlRow(xs, r, runparams);
- xs << xml::EndTag("div") << xml::CR();
+ xs << xml::EndTag("div");
+ xs << xml::CR();
}
}
- xs << xml::StartTag("table") << xml::CR();
+ xs << xml::StartTag("table");
+ xs << xml::CR();
// output header info
bool const havefirsthead = haveLTFirstHead(false);
// in XHTML. this test accomplishes that.
bool const havehead = !havefirsthead && haveLTHead(false);
if (havehead || havefirsthead) {
- xs << xml::StartTag("thead") << xml::CR();
+ xs << xml::StartTag("thead");
+ xs << xml::CR();
for (row_type r = 0; r < nrows(); ++r) {
if (((havefirsthead && row_info[r].endfirsthead) ||
(havehead && row_info[r].endhead)) &&
ret += xhtmlRow(xs, r, runparams, true);
}
}
- xs << xml::EndTag("thead") << xml::CR();
+ xs << xml::EndTag("thead");
+ xs << xml::CR();
}
// output footer info
bool const havelastfoot = haveLTLastFoot(false);
ret += xhtmlRow(xs, r, runparams);
}
}
- xs << xml::EndTag("tfoot") << xml::CR();
+ xs << xml::EndTag("tfoot");
+ xs << xml::CR();
}
xs << xml::StartTag("tbody") << xml::CR();
- for (row_type r = 0; r < nrows(); ++r) {
- if (isValidRow(r)) {
+ for (row_type r = 0; r < nrows(); ++r)
+ if (isValidRow(r))
ret += xhtmlRow(xs, r, runparams);
- }
+ xs << xml::EndTag("tbody");
+ xs << xml::CR();
+ xs << xml::EndTag("table");
+ xs << xml::CR();
+ if (is_long_tabular) {
+ xs << xml::EndTag("div");
+ xs << xml::CR();
}
- xs << xml::EndTag("tbody")
- << xml::CR()
- << xml::EndTag("table")
- << xml::CR();
- if (is_long_tabular)
- xs << xml::EndTag("div") << xml::CR();
return ret;
}
}
+void InsetTableCell::docbook(XMLStream & xs, OutputParams const & runparams) const
+{
+ InsetText::docbook(xs, runparams);
+}
+
+
void InsetTableCell::metrics(MetricsInfo & mi, Dimension & dim) const
{
TextMetrics & tm = mi.base.bv->textMetrics(&text());
}
-int InsetTabular::docbook(odocstream & os, OutputParams const & runparams) const
+void InsetTabular::docbook(XMLStream & xs, OutputParams const & runparams) const
{
- int ret = 0;
- Inset * master = 0;
-
- // FIXME: Why not pass a proper DocIterator here?
-#if 0
- // if the table is inside a float it doesn't need the informaltable
- // wrapper. Search for it.
- for (master = owner(); master; master = master->owner())
- if (master->lyxCode() == FLOAT_CODE)
- break;
-#endif
-
- if (!master) {
- os << "<informaltable>";
- ++ret;
- }
- ret += tabular.docbook(os, runparams);
- if (!master) {
- os << "</informaltable>";
- ++ret;
- }
- return ret;
+ tabular.docbook(xs, runparams);
}
///
docstring xhtml(XMLStream &, OutputParams const &) const;
///
+ void docbook(XMLStream &, OutputParams const &) const;
+ ///
void addToToc(DocIterator const & di, bool output_active,
UpdateType utype, TocBackend & backend) const;
///
///
void latex(otexstream &, OutputParams const &) const;
///
- int docbook(odocstream & os, OutputParams const &) const;
+ void docbook(XMLStream &, OutputParams const &) const;
///
docstring xhtml(XMLStream &, OutputParams const &) const;
///
std::vector<unsigned int> const &,
bool onlydata, size_t max_length) const;
/// auxiliary function for docbook
- int docbookRow(odocstream & os, row_type, OutputParams const &) const;
+ void docbookRow(XMLStream &, row_type, OutputParams const &,
+ bool header = false) const;
///
docstring xhtmlRow(XMLStream & xs, row_type, OutputParams const &,
bool header = false) const;
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const &) const;
+ void docbook(XMLStream &, OutputParams const &) const;
///
docstring xhtml(XMLStream &, OutputParams const &) const;
///
#include "MetricsInfo.h"
#include "output_docbook.h"
#include "output_latex.h"
+#include "output_plaintext.h"
#include "output_xhtml.h"
#include "OutputParams.h"
-#include "output_plaintext.h"
#include "Paragraph.h"
#include "ParagraphParameters.h"
#include "ParIterator.h"
#include "Row.h"
-#include "xml.h"
#include "TexRow.h"
#include "texstream.h"
#include "TextClass.h"
}
-int InsetText::docbook(odocstream & os, OutputParams const & runparams) const
+
+void InsetText::docbook(XMLStream & xs, OutputParams const & rp) const
{
- ParagraphList::const_iterator const beg = paragraphs().begin();
+ docbook(xs, rp, WriteEverything);
+}
- if (!undefined())
- xml::openTag(os, getLayout().latexname(),
- beg->getID(buffer(), runparams) + getLayout().latexparam());
- docbookParagraphs(text_, buffer(), os, runparams);
+void InsetText::docbook(XMLStream & xs, OutputParams const & rp, XHTMLOptions opts) const
+{
+ // we will always want to output all our paragraphs when we are
+ // called this way.
+ OutputParams runparams = rp;
+ runparams.par_begin = 0;
+ runparams.par_end = text().paragraphs().size();
- if (!undefined())
- xml::closeTag(os, getLayout().latexname());
+ if (undefined()) {
+ xs.startDivision(false);
+ docbookParagraphs(text_, buffer(), xs, runparams);
+ xs.endDivision();
+ return;
+ }
- return 0;
+ InsetLayout const & il = getLayout();
+ if (opts & WriteOuterTag && !il.docbooktag().empty() && il.docbooktag() != "NONE") {
+ docstring attrs = docstring();
+ if (!il.docbookattr().empty())
+ attrs += from_ascii(il.docbookattr());
+ if (il.docbooktag() == "link")
+ attrs += from_ascii(" xlink:href=\"") + text_.asString() + from_ascii("\"");
+ xs << xml::StartTag(il.docbooktag(), attrs);
+ }
+
+ // No need for labels that are generated from counters.
+
+ // With respect to XHTML, paragraphs are still allowed here.
+ if (!allowMultiPar())
+ runparams.docbook_make_pars = false;
+ if (il.isPassThru())
+ runparams.pass_thru = true;
+
+ xs.startDivision(false);
+ docbookParagraphs(text_, buffer(), xs, runparams);
+ xs.endDivision();
+
+ if (opts & WriteOuterTag)
+ xs << xml::EndTag(il.docbooktag());
}
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const &) const;
- ///
docstring xhtml(XMLStream &, OutputParams const &) const;
///
enum XHTMLOptions {
docstring insetAsXHTML(XMLStream &, OutputParams const &,
XHTMLOptions) const;
///
+ void docbook(XMLStream &, OutputParams const &, XHTMLOptions opts) const;
+ ///
+ void docbook(XMLStream &, OutputParams const &) const;
+ ///
void validate(LaTeXFeatures & features) const;
/// return the argument(s) only
#include "Lexer.h"
#include "MetricsInfo.h"
#include "OutputParams.h"
-#include "output_xhtml.h"
+#include "xml.h"
#include "texstream.h"
#include "Text.h"
}
-int InsetVSpace::docbook(odocstream & os, OutputParams const &) const
+void InsetVSpace::docbook(XMLStream & xs, OutputParams const &) const
{
- os << '\n';
- return 1;
+ xs << xml::CR();
}
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const &) const;
+ void docbook(XMLStream &, OutputParams const &) const;
/// Note that this returns the inset rather than writing it,
/// so it will actually be written after the present paragraph.
/// The normal case is that this inset will be on a line by
#include "FuncStatus.h"
#include "LaTeXFeatures.h"
#include "Lexer.h"
-#include "output_xhtml.h"
+#include "xml.h"
#include "texstream.h"
#include "TextClass.h"
}
-int InsetWrap::docbook(odocstream & os, OutputParams const & runparams) const
+void InsetWrap::docbook(XMLStream & xs, OutputParams const & runparams) const
{
- // FIXME UNICODE
- os << '<' << from_ascii(params_.type) << '>';
- int const i = InsetText::docbook(os, runparams);
- os << "</" << from_ascii(params_.type) << '>';
- return i;
+ InsetLayout const & il = getLayout();
+
+ xs << xml::StartTag(il.docbooktag(), il.docbookattr()); // TODO: Detect when there is a title.
+ InsetText::docbook(xs, runparams);
+ xs << xml::EndTag(il.docbooktag());
}
int plaintext(odocstringstream & ods, OutputParams const & op,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const &) const;
+ void docbook(XMLStream &, OutputParams const &) const;
///
docstring xhtml(XMLStream &, OutputParams const &) const;
///
#include "InsetMathMacro.h"
#include "InsetMathMacroTemplate.h"
#include "MetricsInfo.h"
-#include "output_xhtml.h"
+#include "xml.h"
#include "Paragraph.h"
#include "ParIterator.h"
#include "xml.h"
}
-int InsetMathHull::docbook(odocstream & os, OutputParams const & runparams) const
+void InsetMathHull::docbook(XMLStream & xs, OutputParams const & runparams) const
{
// With DocBook 5, MathML must be within its own namespace; defined in Buffer.cpp::writeDocBookSource as "m".
// Output everything in a separate stream so that this does not interfere with the standard flow of DocBook tags.
odocstringstream osmath;
MathStream ms(osmath, "m", true);
- int res = 0;
// Choose the tag around the MathML equation.
docstring name;
else
name = from_ascii("informalequation");
+ // DocBook also has <equation>, but it comes with a title.
+
docstring bname = name;
for (row_type i = 0; i < nrows(); ++i) {
if (!label(i).empty()) {
++ms.tab(); ms.cr(); ms.os() << '<' << bname << '>';
+ // Output the MathML subtree.
odocstringstream ls;
otexstream ols(ls);
- if (runparams.flavor == OutputParams::DOCBOOK5) {
- ms << MTag("alt role='tex' ");
- // Workaround for db2latex: db2latex always includes equations with
- // \ensuremath{} or \begin{display}\end{display}
- // so we strip LyX' math environment
- WriteStream wi(ols, false, false, WriteStream::wsDefault, runparams.encoding);
- InsetMathGrid::write(wi);
- ms << from_utf8(subst(subst(to_utf8(ls.str()), "&", "&"), "<", "<"));
- ms << ETag("alt");
- ms << MTag("math");
- ms << ETag("alt");
- ms << MTag("math");
- InsetMathGrid::mathmlize(ms);
- ms << ETag("math");
- } else {
- ms << MTag("alt role='tex'");
- latex(ols, runparams);
- res = ols.texrow().rows();
- ms << from_utf8(subst(subst(to_utf8(ls.str()), "&", "&"), "<", "<"));
- ms << ETag("alt");
- }
-
- ms << from_ascii("<graphic fileref=\"eqn/");
- if (!label(0).empty())
- ms << xml::cleanID(label(0));
- else
- ms << xml::uniqueID(from_ascii("anon"));
-
- if (runparams.flavor == OutputParams::DOCBOOK5)
- ms << from_ascii("\"/>");
- else
- ms << from_ascii("\">");
+ // TeX transcription. Avoid MTag/ETag so that there are no extraneous spaces.
+ ms << "<" << from_ascii("alt") << " role='tex'" << ">";
+ // Workaround for db2latex: db2latex always includes equations with
+ // \ensuremath{} or \begin{display}\end{display}
+ // so we strip LyX' math environment
+ WriteStream wi(ols, false, false, WriteStream::wsDefault, runparams.encoding);
+ InsetMathGrid::write(wi);
+ ms << from_utf8(subst(subst(to_utf8(ls.str()), "&", "&"), "<", "<"));
+ ms << "</" << from_ascii("alt") << ">";
+
+ // Actual transformation of the formula into MathML. This translation may fail (for example, due to custom macros).
+ // The new output stream is required to deal with the errors: first write completely the formula into this
+ // temporary stream; then, if it is possible without error, then copy it back to the "real" stream. Otherwise,
+ // some incomplete tags might be put into the real stream.
+ try {
+ // First, generate the MathML expression.
+ odocstringstream ostmp;
+ MathStream mstmp(ostmp, ms.xmlns(), ms.xmlMode());
+ InsetMathGrid::mathmlize(mstmp);
+
+ // Then, output it (but only if the generation can be done without errors!).
+ ms << MTag("math");
+ ms.cr();
+ osmath << ostmp.str(); // osmath is not a XMLStream, so no need for XMLStream::ESCAPE_NONE.
+ ms << ETag("math");
+ } catch (MathExportException const &) {
+ osmath << "MathML export failed. Please report this as a bug.";
+ }
+
+ // Close the DocBook tag enclosing the formula.
ms.cr(); --ms.tab(); ms.os() << "</" << name << '>';
- return ms.line() + res;
+ // Output the complete tag to the DocBook stream.
+ xs << XMLStream::ESCAPE_NONE << osmath.str();
}
int plaintext(odocstringstream &, OutputParams const &,
size_t max_length = INT_MAX) const;
///
- int docbook(odocstream &, OutputParams const &) const;
+ void docbook(XMLStream &, OutputParams const &) const;
///
docstring xhtml(XMLStream &, OutputParams const &) const;
///
}
-int InsetMathRef::docbook(odocstream & os, OutputParams const & runparams) const
+void InsetMathRef::docbook(XMLStream & xs, OutputParams const &) const
{
if (cell(1).empty()) {
- os << "<xref linkend=\""
- << xml::cleanID(asString(cell(0)));
- if (runparams.flavor == OutputParams::DOCBOOK5)
- os << "\"/>";
- else
- os << "\">";
+ docstring attr = from_utf8("linkend=\"") + xml::cleanID(asString(cell(0))) + from_utf8("\"");
+ xs << xml::CompTag("xref", to_utf8(attr));
} else {
- os << "<link linkend=\""
- << xml::cleanID(asString(cell(0)))
- << "\">"
+ // Link with linkend, as is it within the document (not outside, in which case xlink:href is better suited).
+ docstring attr = from_utf8("linkend=\"") + xml::cleanID(asString(cell(0))) + from_utf8("\"");
+ xs << xml::StartTag("link", to_utf8(attr))
<< asString(cell(1))
- << "</link>";
+ << xml::EndTag("link");
}
-
- return 0;
}
virtual InsetMathRef * asRefInset() { return this; }
/// docbook output
- int docbook(odocstream & os, OutputParams const &) const;
+ void docbook(XMLStream &, OutputParams const &) const;
/// generate something that will be understood by the Dialogs.
std::string const createDialogStr() const;
#include <config.h>
-#include "output_docbook.h"
-
#include "Buffer.h"
#include "buffer_funcs.h"
#include "BufferParams.h"
#include "Counters.h"
#include "Font.h"
+#include "InsetList.h"
#include "Layout.h"
#include "OutputParams.h"
#include "Paragraph.h"
#include "Text.h"
#include "TextClass.h"
-#include "support/lassert.h"
+#include "insets/InsetBibtex.h"
+#include "insets/InsetLabel.h"
+#include "insets/InsetNote.h"
+
+#include "support/convert.h"
#include "support/debug.h"
+#include "support/lassert.h"
#include "support/lstrings.h"
-#include "support/lyxalgo.h"
+#include "support/textutils.h"
+#include <stack>
#include <iostream>
+#include <algorithm>
using namespace std;
using namespace lyx::support;
}
}
-ParagraphList::const_iterator searchParagraph(
- ParagraphList::const_iterator p,
- ParagraphList::const_iterator const & pend)
-{
- for (++p; p != pend && p->layout().latextype == LATEX_PARAGRAPH; ++p)
- ;
+string fontToRole(xml::FontTypes type) {
+ // Specific fonts are achieved with roles. The only common ones are "" for basic emphasis,
+ // and "bold"/"strong" for bold. With some specific options, other roles are copied into
+ // HTML output (via the DocBook XSLT sheets); otherwise, if not recognised, they are just ignored.
+ // Hence, it is not a problem to have many roles by default here.
+ // See https://www.sourceware.org/ml/docbook/2003-05/msg00269.html
+ switch (type) {
+ case xml::FontTypes::FT_ITALIC:
+ case xml::FontTypes::FT_EMPH:
+ return "";
+ case xml::FontTypes::FT_BOLD:
+ return "bold";
+ case xml::FontTypes::FT_NOUN:
+ return ""; // Outputs a <person>
+ case xml::FontTypes::FT_TYPE:
+ return ""; // Outputs a <code>
+
+ // All other roles are non-standard for DocBook.
- return p;
+ case xml::FontTypes::FT_UBAR:
+ return "ubar";
+ case xml::FontTypes::FT_WAVE:
+ return "wave";
+ case xml::FontTypes::FT_DBAR:
+ return "dbar";
+ case xml::FontTypes::FT_SOUT:
+ return "sout";
+ case xml::FontTypes::FT_XOUT:
+ return "xout";
+ case xml::FontTypes::FT_UPRIGHT:
+ return "upright";
+ case xml::FontTypes::FT_SLANTED:
+ return "slanted";
+ case xml::FontTypes::FT_SMALLCAPS:
+ return "smallcaps";
+ case xml::FontTypes::FT_ROMAN:
+ return "roman";
+ case xml::FontTypes::FT_SANS:
+ return "sans";
+ case xml::FontTypes::FT_SIZE_TINY:
+ return "tiny";
+ case xml::FontTypes::FT_SIZE_SCRIPT:
+ return "size_script";
+ case xml::FontTypes::FT_SIZE_FOOTNOTE:
+ return "size_footnote";
+ case xml::FontTypes::FT_SIZE_SMALL:
+ return "size_small";
+ case xml::FontTypes::FT_SIZE_NORMAL:
+ return "size_normal";
+ case xml::FontTypes::FT_SIZE_LARGE:
+ return "size_large";
+ case xml::FontTypes::FT_SIZE_LARGER:
+ return "size_larger";
+ case xml::FontTypes::FT_SIZE_LARGEST:
+ return "size_largest";
+ case xml::FontTypes::FT_SIZE_HUGE:
+ return "size_huge";
+ case xml::FontTypes::FT_SIZE_HUGER:
+ return "size_huger";
+ case xml::FontTypes::FT_SIZE_INCREASE:
+ return "size_increase";
+ case xml::FontTypes::FT_SIZE_DECREASE:
+ return "size_decrease";
+ default:
+ return "";
+ }
}
+string fontToAttribute(xml::FontTypes type) {
+ // If there is a role (i.e. nonstandard use of a tag), output the attribute. Otherwise, the sheer tag is sufficient
+ // for the font.
+ string role = fontToRole(type);
+ if (!role.empty()) {
+ return "role='" + role + "'";
+ } else {
+ return "";
+ }
+}
-ParagraphList::const_iterator searchCommand(
- ParagraphList::const_iterator p,
- ParagraphList::const_iterator const & pend)
-{
- Layout const & bstyle = p->layout();
+} // end anonymous namespace
- for (++p; p != pend; ++p) {
- Layout const & style = p->layout();
- if (style.latextype == LATEX_COMMAND
- && style.commanddepth <= bstyle.commanddepth)
- return p;
- }
- return pend;
+
+xml::FontTag docbookStartFontTag(xml::FontTypes type)
+{
+ return xml::FontTag(from_utf8(fontToDocBookTag(type)), from_utf8(fontToAttribute(type)), type);
}
-ParagraphList::const_iterator searchEnvironment(
- ParagraphList::const_iterator p,
- ParagraphList::const_iterator const & pend)
+xml::EndFontTag docbookEndFontTag(xml::FontTypes type)
{
- Layout const & bstyle = p->layout();
- size_t const depth = p->params().depth();
- for (++p; p != pend; ++p) {
- Layout const & style = p->layout();
- if (style.latextype == LATEX_COMMAND)
- return p;
-
- if (style.latextype == LATEX_PARAGRAPH) {
- if (p->params().depth() > depth)
- continue;
- return p;
- }
+ return xml::EndFontTag(from_utf8(fontToDocBookTag(type)), type);
+}
- if (p->params().depth() < depth)
- return p;
- if (style.latexname() != bstyle.latexname()
- && p->params().depth() == depth)
- return p;
- }
- return pend;
+namespace {
+
+// convenience functions
+
+void openParTag(XMLStream &xs, Layout const &lay) {
+ if (lay.docbookwrappertag() != "NONE") {
+ xs << xml::StartTag(lay.docbookwrappertag(), lay.docbookwrapperattr());
+ }
+
+ string tag = lay.docbooktag();
+ if (tag == "Plain Layout")
+ tag = "para";
+
+ xs << xml::ParTag(tag, lay.docbookattr());
}
-ParagraphList::const_iterator makeParagraph(
- Buffer const & buf,
- odocstream & os,
- OutputParams const & runparams,
- Text const & text,
- ParagraphList::const_iterator const & pbegin,
- ParagraphList::const_iterator const & pend)
+void closeTag(XMLStream &xs, Layout const &lay) {
+ string tag = lay.docbooktag();
+ if (tag == "Plain Layout")
+ tag = "para";
+
+ xs << xml::EndTag(tag);
+ if (lay.docbookwrappertag() != "NONE")
+ xs << xml::EndTag(lay.docbookwrappertag());
+}
+
+
+void openLabelTag(XMLStream & xs, Layout const & lay) // Mostly for definition lists.
{
- ParagraphList const & paragraphs = text.paragraphs();
- for (ParagraphList::const_iterator par = pbegin; par != pend; ++par) {
- if (par != pbegin)
- os << '\n';
- bool const default_or_plain =
- (buf.params().documentClass().isDefaultLayout(par->layout())
- || buf.params().documentClass().isPlainLayout(par->layout()));
- if (default_or_plain && par->emptyTag()) {
- par->simpleDocBookOnePar(buf, os, runparams,
- text.outerFont(distance(paragraphs.begin(), par)));
- } else {
- xml::openTag(buf, os, runparams, *par);
- par->simpleDocBookOnePar(buf, os, runparams,
- text.outerFont(distance(paragraphs.begin(), par)));
- xml::closeTag(os, *par);
- }
- }
- return pend;
+ xs << xml::StartTag(lay.docbookitemlabeltag(), lay.docbookitemlabelattr());
}
-ParagraphList::const_iterator makeEnvironment(
- Buffer const & buf,
- odocstream & os,
- OutputParams const & runparams,
- Text const & text,
- ParagraphList::const_iterator const & pbegin,
- ParagraphList::const_iterator const & pend)
+void closeLabelTag(XMLStream & xs, Layout const & lay)
{
- ParagraphList const & paragraphs = text.paragraphs();
+ xs << xml::EndTag(lay.docbookitemlabeltag());
+ xs << xml::CR();
+}
+
+
+void openItemTag(XMLStream &xs, Layout const &lay) {
+ xs << xml::StartTag(lay.docbookitemtag(), lay.docbookitemattr());
+}
+
+
+// Return true when new elements are output in a paragraph, false otherwise.
+bool openInnerItemTag(XMLStream &xs, Layout const &lay) {
+ if (lay.docbookiteminnertag() != "NONE") {
+ xs << xml::CR();
+ xs << xml::ParTag(lay.docbookiteminnertag(), lay.docbookiteminnerattr());
+
+ if (lay.docbookiteminnertag() == "para") {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+void closeInnerItemTag(XMLStream &xs, Layout const &lay) {
+ if (lay.docbookiteminnertag()!= "NONE") {
+ xs << xml::EndTag(lay.docbookiteminnertag());
+ xs << xml::CR();
+ }
+}
+
+
+inline void closeItemTag(XMLStream &xs, Layout const &lay) {
+ xs << xml::EndTag(lay.docbookitemtag()) << xml::CR();
+}
+
+// end of convenience functions
+
+ParagraphList::const_iterator findLastParagraph(
+ ParagraphList::const_iterator p,
+ ParagraphList::const_iterator const &pend) {
+ for (++p; p != pend && p->layout().latextype == LATEX_PARAGRAPH; ++p);
+
+ return p;
+}
+
+
+ParagraphList::const_iterator findEndOfEnvironment(
+ ParagraphList::const_iterator const &pstart,
+ ParagraphList::const_iterator const &pend) {
+ ParagraphList::const_iterator p = pstart;
+ Layout const &bstyle = p->layout();
+ size_t const depth = p->params().depth();
+ for (++p; p != pend; ++p) {
+ Layout const &style = p->layout();
+ // It shouldn't happen that e.g. a section command occurs inside
+ // a quotation environment, at a higher depth, but as of 6/2009,
+ // it can happen. We pretend that it's just at lowest depth.
+ if (style.latextype == LATEX_COMMAND)
+ return p;
+
+ // If depth is down, we're done
+ if (p->params().depth() < depth)
+ return p;
+
+ // If depth is up, we're not done
+ if (p->params().depth() > depth)
+ continue;
+
+ // FIXME I am not sure about the first check.
+ // Surely we *could* have different layouts that count as
+ // LATEX_PARAGRAPH, right?
+ if (style.latextype == LATEX_PARAGRAPH || style != bstyle)
+ return p;
+ }
+ return pend;
+}
+
+
+ParagraphList::const_iterator makeParagraphs(Buffer const &buf,
+ XMLStream &xs,
+ OutputParams const &runparams,
+ Text const &text,
+ ParagraphList::const_iterator const &pbegin,
+ ParagraphList::const_iterator const &pend) {
+ ParagraphList::const_iterator const begin = text.paragraphs().begin();
+ ParagraphList::const_iterator par = pbegin;
+ for (; par != pend; ++par) {
+ Layout const &lay = par->layout();
+ if (!lay.counter.empty())
+ buf.masterBuffer()->params().
+ documentClass().counters().step(lay.counter, OutputUpdate);
+
+ // We want to open the paragraph tag if:
+ // (i) the current layout permits multiple paragraphs
+ // (ii) we are either not already inside a paragraph (HTMLIsBlock) OR
+ // we are, but this is not the first paragraph
+ //
+ // But there is also a special case, and we first see whether we are in it.
+ // We do not want to open the paragraph tag if this paragraph contains
+ // only one item, and that item is "inline", i.e., not HTMLIsBlock (such
+ // as a branch). On the other hand, if that single item has a font change
+ // applied to it, then we still do need to open the paragraph.
+ //
+ // Obviously, this is very fragile. The main reason we need to do this is
+ // because of branches, e.g., a branch that contains an entire new section.
+ // We do not really want to wrap that whole thing in a <div>...</div>.
+ bool special_case = false;
+ Inset const *specinset = par->size() == 1 ? par->getInset(0) : 0;
+ if (specinset && !specinset->getLayout().htmlisblock()) { // TODO: Convert htmlisblock to a DocBook parameter?
+ Layout const &style = par->layout();
+ FontInfo const first_font = style.labeltype == LABEL_MANUAL ?
+ style.labelfont : style.font;
+ FontInfo const our_font =
+ par->getFont(buf.masterBuffer()->params(), 0,
+ text.outerFont(distance(begin, par))).fontInfo();
+
+ if (first_font == our_font)
+ special_case = true;
+ }
+
+ // Plain layouts must be ignored.
+ if (!special_case && buf.params().documentClass().isPlainLayout(lay) && !runparams.docbook_force_pars)
+ special_case = true;
+ // TODO: Could get rid of this with a DocBook equivalent to htmlisblock?
+ if (!special_case && par->size() == 1 && par->getInset(0)) {
+ Inset const * firstInset = par->getInset(0);
+
+ // Floats cannot be in paragraphs.
+ special_case = to_ascii(firstInset->layoutName()).substr(0, 6) == "Float:";
+
+ // Bibliographies cannot be in paragraphs.
+ if (!special_case && firstInset->asInsetCommand())
+ special_case = firstInset->asInsetCommand()->params().getCmdName() == "bibtex";
+
+ // Equations do not deserve their own paragraph (DocBook allows them outside paragraphs).
+ if (!special_case && firstInset->asInsetMath())
+ special_case = true;
+
+ // ERTs are in comments, not paragraphs.
+ if (!special_case && firstInset->lyxCode() == lyx::ERT_CODE)
+ special_case = true;
+
+ // Listings should not get into their own paragraph.
+ if (!special_case && firstInset->lyxCode() == lyx::LISTINGS_CODE)
+ special_case = true;
+ }
+
+ bool const open_par = runparams.docbook_make_pars
+ && (!runparams.docbook_in_par || par != pbegin)
+ && !special_case;
+
+ // We want to issue the closing tag if either:
+ // (i) We opened it, and either docbook_in_par is false,
+ // or we're not in the last paragraph, anyway.
+ // (ii) We didn't open it and docbook_in_par is true,
+ // but we are in the first par, and there is a next par.
+ ParagraphList::const_iterator nextpar = par;
+ ++nextpar;
+ bool const close_par =
+ ((open_par && (!runparams.docbook_in_par || nextpar != pend))
+ || (!open_par && runparams.docbook_in_par && par == pbegin && nextpar != pend));
+
+ if (open_par) {
+ openParTag(xs, lay);
+ }
+
+ par->simpleDocBookOnePar(buf, xs, runparams, text.outerFont(distance(begin, par)), open_par, close_par, 0);
+
+ if (close_par) {
+ closeTag(xs, lay);
+ xs << xml::CR();
+ }
+ }
+ return pend;
+}
+
+
+bool isNormalEnv(Layout const &lay) {
+ return lay.latextype == LATEX_ENVIRONMENT
+ || lay.latextype == LATEX_BIB_ENVIRONMENT;
+}
+
+
+ParagraphList::const_iterator makeEnvironment(Buffer const &buf,
+ XMLStream &xs,
+ OutputParams const &runparams,
+ Text const &text,
+ ParagraphList::const_iterator const &pbegin,
+ ParagraphList::const_iterator const &pend) {
+ ParagraphList::const_iterator const begin = text.paragraphs().begin();
ParagraphList::const_iterator par = pbegin;
+ Layout const &bstyle = par->layout();
+ depth_type const origdepth = pbegin->params().depth();
- Layout const & defaultstyle = buf.params().documentClass().defaultLayout();
- Layout const & bstyle = par->layout();
+ // open tag for this environment
+ openParTag(xs, bstyle);
+ xs << xml::CR();
- // Opening outter tag
- xml::openTag(buf, os, runparams, *pbegin);
- os << '\n';
- if (bstyle.latextype == LATEX_ENVIRONMENT && bstyle.pass_thru)
- os << "<![CDATA[";
+ // we will on occasion need to remember a layout from before.
+ Layout const *lastlay = nullptr;
while (par != pend) {
Layout const & style = par->layout();
+ // the counter only gets stepped if we're in some kind of list,
+ // or if it's the first time through.
+ // note that enum, etc, are handled automatically.
+ // FIXME There may be a bug here about user defined enumeration
+ // types. If so, then we'll need to take the counter and add "i",
+ // "ii", etc, as with enum.
+ Counters & cnts = buf.masterBuffer()->params().documentClass().counters();
+ docstring const & cntr = style.counter;
+ if (!style.counter.empty()
+ && (par == pbegin || !isNormalEnv(style))
+ && cnts.hasCounter(cntr)
+ )
+ cnts.step(cntr, OutputUpdate);
ParagraphList::const_iterator send;
- string id = par->getID(buf, runparams);
- string wrapper = "";
- pos_type sep = 0;
-
- // Opening inner tag
- switch (bstyle.latextype) {
- case LATEX_ENVIRONMENT:
- if (!bstyle.innertag().empty()) {
- xml::openTag(os, bstyle.innertag(), id);
- }
- break;
-
- case LATEX_ITEM_ENVIRONMENT:
- if (!bstyle.labeltag().empty()) {
- xml::openTag(os, bstyle.innertag(), id);
- xml::openTag(os, bstyle.labeltag());
- sep = par->firstWordDocBook(os, runparams) + 1;
- xml::closeTag(os, bstyle.labeltag());
- }
- wrapper = defaultstyle.latexname();
- // If a sub list (embedded list) appears next with a
- // different depth, then there is no need to open
- // another tag at the current depth.
- if(par->params().depth() == pbegin->params().depth()) {
- xml::openTag(os, bstyle.itemtag());
- }
- break;
- default:
- break;
- }
+ // Actual content of this paragraph.
switch (style.latextype) {
case LATEX_ENVIRONMENT:
+ case LATEX_LIST_ENVIRONMENT:
case LATEX_ITEM_ENVIRONMENT: {
- if (par->params().depth() == pbegin->params().depth()) {
- xml::openTag(os, wrapper);
- par->simpleDocBookOnePar(buf, os, runparams,
- text.outerFont(distance(paragraphs.begin(), par)), sep);
- xml::closeTag(os, wrapper);
+ // There are two possibilities in this case.
+ // One is that we are still in the environment in which we
+ // started---which we will be if the depth is the same.
+ if (par->params().depth() == origdepth) {
+ LATTEST(bstyle == style);
+ if (lastlay != nullptr) {
+ closeItemTag(xs, *lastlay);
+ lastlay = nullptr;
+ }
+
+ // this will be positive, if we want to skip the
+ // initial word (if it's been taken for the label).
+ pos_type sep = 0;
+
+ // Open a wrapper tag if needed.
+ if (style.docbookitemwrappertag() != "NONE") {
+ xs << xml::StartTag(style.docbookitemwrappertag(), style.docbookitemwrapperattr());
+ xs << xml::CR();
+ }
+
+ // label output
+ if (style.labeltype != LABEL_NO_LABEL &&
+ style.docbookitemlabeltag() != "NONE") {
+
+ if (isNormalEnv(style)) {
+ // in this case, we print the label only for the first
+ // paragraph (as in a theorem or an abstract).
+ if (par == pbegin) {
+ docstring const lbl = pbegin->params().labelString();
+ if (!lbl.empty()) {
+ openLabelTag(xs, style);
+ xs << lbl;
+ closeLabelTag(xs, style);
+ }
+ xs << xml::CR();
+ }
+ } else { // some kind of list
+ if (style.labeltype == LABEL_MANUAL) {
+ // Only variablelist gets here.
+
+ openLabelTag(xs, style);
+ sep = par->firstWordDocBook(xs, runparams);
+ closeLabelTag(xs, style);
+ xs << xml::CR();
+ } else {
+ openLabelTag(xs, style);
+ xs << par->params().labelString();
+ closeLabelTag(xs, style);
+ xs << xml::CR();
+ }
+ }
+ } // end label output
+
+ bool wasInParagraph = runparams.docbook_in_par;
+ openItemTag(xs, style);
+ bool getsIntoParagraph = openInnerItemTag(xs, style);
+ OutputParams rp = runparams;
+ rp.docbook_in_par = wasInParagraph | getsIntoParagraph;
+
+ par->simpleDocBookOnePar(buf, xs, rp, text.outerFont(distance(begin, par)), true, true, sep);
++par;
+ if (getsIntoParagraph)
+ closeInnerItemTag(xs, style);
+
+ // We may not want to close the tag yet, in particular:
+ // If we're not at the end of the item...
+ if (par != pend
+ // and are doing items...
+ && !isNormalEnv(style)
+ // and if the depth has changed...
+ && par->params().depth() != origdepth) {
+ // then we'll save this layout for later, and close it when
+ // we get another item.
+ lastlay = &style;
+ } else {
+ closeItemTag(xs, style);
+
+ // Eventually, close the item wrapper.
+ if (style.docbookitemwrappertag() != "NONE") {
+ xs << xml::EndTag(style.docbookitemwrappertag());
+ xs << xml::CR();
+ }
+ }
}
+ // The other possibility is that the depth has increased, in which
+ // case we need to recurse.
else {
- send = searchEnvironment(par, pend);
- par = makeEnvironment(buf, os, runparams, text, par,send);
+ send = findEndOfEnvironment(par, pend);
+ par = makeEnvironment(buf, xs, runparams, text, par, send);
}
break;
}
case LATEX_PARAGRAPH:
- send = searchParagraph(par, pend);
- par = makeParagraph(buf, os, runparams, text, par,send);
+ send = findLastParagraph(par, pend);
+ par = makeParagraphs(buf, xs, runparams, text, par, send);
break;
- case LATEX_LIST_ENVIRONMENT:
case LATEX_BIB_ENVIRONMENT:
+ // Handled in InsetBibtex.
+ break;
case LATEX_COMMAND:
- // FIXME This means that we are just skipping any paragraph that
- // isn't implemented above, and this includes lists.
++par;
break;
}
-
- // Closing inner tag
- switch (bstyle.latextype) {
- case LATEX_ENVIRONMENT:
- if (!bstyle.innertag().empty()) {
- xml::closeTag(os, bstyle.innertag());
- os << '\n';
- }
- break;
- case LATEX_ITEM_ENVIRONMENT:
- // If a sub list (embedded list) appears next, then
- // there is no need to close the current tag.
- // par should have already been incremented to the next
- // element. So we can compare the depth of the next
- // element with pbegin.
- // We need to be careful, that we don't dereference par
- // when par == pend but at the same time that the
- // current tag is closed.
- if((par != pend && par->params().depth() == pbegin->params().depth()) || par == pend) {
- xml::closeTag(os, bstyle.itemtag());
- }
- if (!bstyle.labeltag().empty())
- xml::closeTag(os, bstyle.innertag());
- break;
- default:
- break;
- }
}
- if (bstyle.latextype == LATEX_ENVIRONMENT && bstyle.pass_thru)
- os << "]]>";
-
- // Closing outer tag
- xml::closeTag(os, *pbegin);
-
+ if (lastlay != 0)
+ closeItemTag(xs, *lastlay);
+ closeTag(xs, bstyle);
+ xs << xml::CR();
return pend;
}
-ParagraphList::const_iterator makeCommand(
- Buffer const & buf,
- odocstream & os,
- OutputParams const & runparams,
- Text const & text,
- ParagraphList::const_iterator const & pbegin,
- ParagraphList::const_iterator const & pend)
-{
- ParagraphList const & paragraphs = text.paragraphs();
- ParagraphList::const_iterator par = pbegin;
- Layout const & bstyle = par->layout();
-
- //Open outter tag
- xml::openTag(buf, os, runparams, *pbegin);
- os << '\n';
-
- // Label around sectioning number:
- if (!bstyle.labeltag().empty()) {
- xml::openTag(os, bstyle.labeltag());
- // We don't care about appendix in DOCBOOK.
- os << par->expandDocBookLabel(bstyle, buf.params());
- xml::closeTag(os, bstyle.labeltag());
- }
+void makeCommand(Buffer const &buf,
+ XMLStream &xs,
+ OutputParams const &runparams,
+ Text const &text,
+ ParagraphList::const_iterator const &pbegin) {
+ Layout const &style = pbegin->layout();
+ if (!style.counter.empty())
+ buf.masterBuffer()->params().
+ documentClass().counters().step(style.counter, OutputUpdate);
- // Opend inner tag and close inner tags
- xml::openTag(os, bstyle.innertag());
- par->simpleDocBookOnePar(buf, os, runparams,
- text.outerFont(distance(paragraphs.begin(), par)));
- xml::closeTag(os, bstyle.innertag());
- os << '\n';
+ // No need for labels, as they are handled by DocBook tags.
- ++par;
- while (par != pend) {
- Layout const & style = par->layout();
- ParagraphList::const_iterator send;
+ openParTag(xs, style);
- switch (style.latextype) {
+ ParagraphList::const_iterator const begin = text.paragraphs().begin();
+ pbegin->simpleDocBookOnePar(buf, xs, runparams,
+ text.outerFont(distance(begin, pbegin)));
+ closeTag(xs, style);
+ xs << xml::CR();
+}
+
+pair<ParagraphList::const_iterator, ParagraphList::const_iterator> makeAny(Text const &text,
+ Buffer const &buf,
+ XMLStream &xs,
+ OutputParams const &ourparams,
+ ParagraphList::const_iterator par,
+ ParagraphList::const_iterator send,
+ ParagraphList::const_iterator pend) {
+ Layout const &style = par->layout();
+
+ switch (style.latextype) {
case LATEX_COMMAND: {
- send = searchCommand(par, pend);
- par = makeCommand(buf, os, runparams, text, par,send);
+ // The files with which we are working never have more than
+ // one paragraph in a command structure.
+ // FIXME
+ // if (ourparams.docbook_in_par)
+ // fix it so we don't get sections inside standard, e.g.
+ // note that we may then need to make runparams not const, so we
+ // can communicate that back.
+ // FIXME Maybe this fix should be in the routines themselves, in case
+ // they are called from elsewhere.
+ makeCommand(buf, xs, ourparams, text, par);
+ ++par;
break;
}
case LATEX_ENVIRONMENT:
+ case LATEX_LIST_ENVIRONMENT:
case LATEX_ITEM_ENVIRONMENT: {
- send = searchEnvironment(par, pend);
- par = makeEnvironment(buf, os, runparams, text, par,send);
+ // FIXME Same fix here.
+ send = findEndOfEnvironment(par, pend);
+ par = makeEnvironment(buf, xs, ourparams, text, par, send);
break;
}
- case LATEX_PARAGRAPH:
- send = searchParagraph(par, pend);
- par = makeParagraph(buf, os, runparams, text, par,send);
+ case LATEX_BIB_ENVIRONMENT: {
+ // Handled in InsetBibtex.
break;
- case LATEX_BIB_ENVIRONMENT:
- case LATEX_LIST_ENVIRONMENT:
- // FIXME This means that we are just skipping any paragraph that
- // isn't implemented above.
- ++par;
+ }
+ case LATEX_PARAGRAPH: {
+ send = findLastParagraph(par, pend);
+ par = makeParagraphs(buf, xs, ourparams, text, par, send);
break;
}
}
- // Close outter tag
- xml::closeTag(os, *pbegin);
- return pend;
+ return make_pair(par, send);
}
-} // namespace
+} // end anonymous namespace
-void docbookParagraphs(Text const & text,
- Buffer const & buf,
- odocstream & os,
- OutputParams const & runparams)
-{
- LASSERT(runparams.par_begin <= runparams.par_end,
- { os << "<!-- Docbook Output Error -->\n"; return; });
-
- ParagraphList const & paragraphs = text.paragraphs();
- ParagraphList::const_iterator par = paragraphs.begin();
- ParagraphList::const_iterator pend = paragraphs.end();
-
- // if only part of the paragraphs will be outputed
- if (runparams.par_begin != runparams.par_end) {
- par = paragraphs.iterator_at(runparams.par_begin);
- pend = paragraphs.iterator_at(runparams.par_end);
- // runparams will be passed to nested paragraphs, so
- // we have to reset the range parameters.
- const_cast<OutputParams&>(runparams).par_begin = 0;
- const_cast<OutputParams&>(runparams).par_end = 0;
- }
+using DocBookDocumentSectioning = tuple<bool, pit_type>;
+using DocBookInfoTag = tuple<set<pit_type>, set<pit_type>, pit_type, pit_type>;
- while (par != pend) {
- Layout const & style = par->layout();
- ParagraphList::const_iterator lastpar = par;
- ParagraphList::const_iterator send;
- switch (style.latextype) {
- case LATEX_COMMAND: {
- send = searchCommand(par, pend);
- par = makeCommand(buf, os, runparams, text, par, send);
+DocBookDocumentSectioning hasDocumentSectioning(ParagraphList const ¶graphs, pit_type bpit, pit_type const epit) {
+ bool documentHasSections = false;
+
+ while (bpit < epit) {
+ Layout const &style = paragraphs[bpit].layout();
+ documentHasSections |= style.category() == from_utf8("Sectioning");
+
+ if (documentHasSections) {
break;
}
- case LATEX_ENVIRONMENT:
- case LATEX_ITEM_ENVIRONMENT: {
- send = searchEnvironment(par, pend);
- par = makeEnvironment(buf, os, runparams, text, par, send);
+ bpit += 1;
+ }
+ // Paragraphs before the first section: [ runparams.par_begin ; eppit )
+
+ return make_tuple(documentHasSections, bpit);
+}
+
+
+DocBookInfoTag getParagraphsWithInfo(ParagraphList const ¶graphs, pit_type const bpit, pit_type const epit) {
+ set<pit_type> shouldBeInInfo;
+ set<pit_type> mustBeInInfo;
+
+ pit_type cpit = bpit;
+ while (cpit < epit) {
+ Layout const &style = paragraphs[cpit].layout();
+ if (style.docbookininfo() == "always") {
+ mustBeInInfo.emplace(cpit);
+ } else if (style.docbookininfo() == "maybe") {
+ shouldBeInInfo.emplace(cpit);
+ } else {
+ // Hypothesis: the <info> parts should be grouped together near the beginning bpit.
break;
}
- case LATEX_PARAGRAPH:
- send = searchParagraph(par, pend);
- par = makeParagraph(buf, os, runparams, text, par, send);
- break;
- case LATEX_BIB_ENVIRONMENT:
- case LATEX_LIST_ENVIRONMENT:
- // FIXME This means that we are just skipping any paragraph that
- // isn't implemented above.
+ cpit += 1;
+ }
+ // Now, bpit points to the last paragraph that has things that could go in <info>.
+
+ return make_tuple(shouldBeInInfo, mustBeInInfo, bpit, cpit);
+}
+
+
+bool hasAbstractBetween(ParagraphList const ¶graphs, pit_type const bpitAbstract, pit_type const epitAbstract)
+{
+ // Hypothesis: the paragraphs between bpitAbstract and epitAbstract can be considered an abstract because they
+ // are just after a document or part title.
+ if (epitAbstract - bpitAbstract <= 0)
+ return false;
+
+ // If there is something between these paragraphs, check if it's compatible with an abstract (i.e. some text).
+ pit_type bpit = bpitAbstract;
+ while (bpit < epitAbstract) {
+ const Paragraph &p = paragraphs.at(bpit);
+
+ if (p.layout().name() == from_ascii("Abstract"))
+ return true;
+
+ if (!p.insetList().empty()) {
+ for (const auto &i : p.insetList()) {
+ if (i.inset->getText(0) != nullptr) {
+ return true;
+ }
+ }
+ }
+ bpit++;
+ }
+ return false;
+}
+
+
+pit_type generateDocBookParagraphWithoutSectioning(Text const &text,
+ Buffer const &buf,
+ XMLStream &xs,
+ OutputParams const &runparams,
+ ParagraphList const ¶graphs,
+ pit_type bpit,
+ pit_type epit) {
+ auto par = paragraphs.iterator_at(bpit);
+ auto lastStartedPar = par;
+ ParagraphList::const_iterator send;
+ auto const pend =
+ (epit == (int) paragraphs.size()) ?
+ paragraphs.end() : paragraphs.iterator_at(epit);
+
+ while (bpit < epit) {
+ tie(par, send) = makeAny(text, buf, xs, runparams, par, send, pend);
+ bpit += distance(lastStartedPar, par);
+ }
+
+ return bpit;
+}
+
+
+void outputDocBookInfo(Text const &text,
+ Buffer const &buf,
+ XMLStream &xs,
+ OutputParams const &runparams,
+ ParagraphList const ¶graphs,
+ DocBookInfoTag const &info,
+ pit_type bpitAbstract,
+ pit_type const epitAbstract) {
+ // Consider everything between bpitAbstract and epitAbstract (excluded) as paragraphs for the abstract.
+ // Use bpitAbstract >= epitAbstract to indicate there is no abstract.
+
+ set<pit_type> shouldBeInInfo;
+ set<pit_type> mustBeInInfo;
+ pit_type bpitInfo;
+ pit_type epitInfo;
+ tie(shouldBeInInfo, mustBeInInfo, bpitInfo, epitInfo) = info;
+
+ // The abstract must go in <info>.
+ bool hasAbstract = hasAbstractBetween(paragraphs, bpitAbstract, epitAbstract);
+ bool needInfo = !mustBeInInfo.empty() || hasAbstract;
+
+ // Start the <info> tag if required.
+ if (needInfo) {
+ xs.startDivision(false);
+ xs << xml::StartTag("info");
+ xs << xml::CR();
+ }
+
+ // Output the elements that should go in <info>.
+ generateDocBookParagraphWithoutSectioning(text, buf, xs, runparams, paragraphs, bpitInfo, epitInfo);
+
+ if (hasAbstract) {
+ string tag = paragraphs[bpitAbstract].layout().docbookforceabstracttag();
+ if (tag == "NONE")
+ tag = "abstract";
+
+ xs << xml::StartTag(tag);
+ xs << xml::CR();
+ xs.startDivision(false);
+ generateDocBookParagraphWithoutSectioning(text, buf, xs, runparams, paragraphs, bpitAbstract, epitAbstract);
+ xs.endDivision();
+ xs << xml::EndTag(tag);
+ xs << xml::CR();
+ }
+
+ // End the <info> tag if it was started.
+ if (needInfo) {
+ xs << xml::EndTag("info");
+ xs << xml::CR();
+ xs.endDivision();
+ }
+}
+
+
+void docbookFirstParagraphs(Text const &text,
+ Buffer const &buf,
+ XMLStream &xs,
+ OutputParams const &runparams,
+ pit_type epit) {
+ // Handle the beginning of the document, supposing it has sections.
+ // Major role: output the first <info> tag.
+
+ ParagraphList const ¶graphs = text.paragraphs();
+ pit_type bpit = runparams.par_begin;
+ DocBookInfoTag info = getParagraphsWithInfo(paragraphs, bpit, epit);
+ outputDocBookInfo(text, buf, xs, runparams, paragraphs, info, get<3>(info), epit);
+}
+
+
+bool isParagraphEmpty(const Paragraph &par)
+{
+ InsetList const &insets = par.insetList();
+ size_t insetsLength = distance(insets.begin(), insets.end());
+ bool hasParagraphOnlyNote = insetsLength == 1 && insets.get(0) && insets.get(0)->asInsetCollapsible() &&
+ dynamic_cast<InsetNote *>(insets.get(0));
+ return hasParagraphOnlyNote;
+}
+
+
+void docbookSimpleAllParagraphs(Text const &text,
+ Buffer const &buf,
+ XMLStream &xs,
+ OutputParams const &runparams) {
+ // Handle the document, supposing it has no sections (i.e. a "simple" document).
+
+ // First, the <info> tag.
+ ParagraphList const ¶graphs = text.paragraphs();
+ pit_type bpit = runparams.par_begin;
+ pit_type const epit = runparams.par_end;
+ DocBookInfoTag info = getParagraphsWithInfo(paragraphs, bpit, epit);
+ outputDocBookInfo(text, buf, xs, runparams, paragraphs, info, 0, 0);
+ bpit = get<3>(info); // Generate the content starting from the end of the <info> part.
+
+ // Then, the content.
+ ParagraphList::const_iterator const pend =
+ (epit == (int) paragraphs.size()) ?
+ paragraphs.end() : paragraphs.iterator_at(epit);
+
+ while (bpit < epit) {
+ auto par = paragraphs.iterator_at(bpit);
+ ParagraphList::const_iterator const lastStartedPar = par;
+ ParagraphList::const_iterator send;
+
+ if (isParagraphEmpty(*par)) {
++par;
- break;
+ bpit += distance(lastStartedPar, par);
+ continue;
}
- // makeEnvironment may process more than one paragraphs and bypass pend
- if (distance(lastpar, par) >= distance(lastpar, pend))
- break;
+
+ // Generate this paragraph.
+ tie(par, send) = makeAny(text, buf, xs, runparams, par, send, pend);
+ bpit += distance(lastStartedPar, par);
}
}
+void docbookParagraphs(Text const &text,
+ Buffer const &buf,
+ XMLStream &xs,
+ OutputParams const &runparams) {
+ ParagraphList const ¶graphs = text.paragraphs();
+ if (runparams.par_begin == runparams.par_end) {
+ runparams.par_begin = 0;
+ runparams.par_end = paragraphs.size();
+ }
+ pit_type bpit = runparams.par_begin;
+ pit_type const epit = runparams.par_end;
+ LASSERT(bpit < epit,
+ {
+ xs << XMLStream::ESCAPE_NONE << "<!-- DocBook output error! -->\n";
+ return;
+ });
+
+ ParagraphList::const_iterator const pend =
+ (epit == (int) paragraphs.size()) ?
+ paragraphs.end() : paragraphs.iterator_at(epit);
+ std::stack<std::pair<int, string>> headerLevels; // Used to determine when to open/close sections: store the depth
+ // of the section and the tag that was used to open it.
+
+ // Detect whether the document contains sections. If there are no sections, there can be no automatically
+ // discovered abstract.
+ bool documentHasSections;
+ pit_type eppit;
+ tie(documentHasSections, eppit) = hasDocumentSectioning(paragraphs, bpit, epit);
+
+ if (documentHasSections) {
+ docbookFirstParagraphs(text, buf, xs, runparams, eppit);
+ bpit = eppit;
+ } else {
+ docbookSimpleAllParagraphs(text, buf, xs, runparams);
+ return;
+ }
+
+ bool currentlyInAppendix = false;
+
+ while (bpit < epit) {
+ OutputParams ourparams = runparams;
+
+ auto par = paragraphs.iterator_at(bpit);
+ if (par->params().startOfAppendix())
+ currentlyInAppendix = true;
+ Layout const &style = par->layout();
+ ParagraphList::const_iterator const lastStartedPar = par;
+ ParagraphList::const_iterator send;
+
+ if (isParagraphEmpty(*par)) {
+ ++par;
+ bpit += distance(lastStartedPar, par);
+ continue;
+ }
+
+ // Think about adding <section> and/or </section>s.
+ const bool isLayoutSectioning = style.category() == from_utf8("Sectioning");
+ if (isLayoutSectioning) {
+ int level = style.toclevel;
+
+ // Need to close a previous section if it has the same level or a higher one (close <section> if opening a <h2>
+ // after a <h2>, <h3>, <h4>, <h5> or <h6>). More examples:
+ // - current: h2; back: h1; do not close any <section>
+ // - current: h1; back: h2; close two <section> (first the <h2>, then the <h1>, so a new <h1> can come)
+ while (!headerLevels.empty() && level <= headerLevels.top().first) {
+ int stackLevel = headerLevels.top().first;
+ docstring stackTag = from_utf8("</" + headerLevels.top().second + ">");
+ headerLevels.pop();
+
+ // Output the tag only if it corresponds to a legit section.
+ if (stackLevel != Layout::NOT_IN_TOC)
+ xs << XMLStream::ESCAPE_NONE << stackTag << xml::CR();
+ }
+
+ // Open the new section: first push it onto the stack, then output it in DocBook.
+ string sectionTag = (currentlyInAppendix && style.docbooksectiontag() == "chapter") ?
+ "appendix" : style.docbooksectiontag();
+ headerLevels.push(std::make_pair(level, sectionTag));
+
+ // Some sectioning-like elements should not be output (such as FrontMatter).
+ if (level != Layout::NOT_IN_TOC) {
+ // Look for a label in the title, i.e. a InsetLabel as a child.
+ docstring id = docstring();
+ for (pos_type i = 0; i < par->size(); ++i) {
+ Inset const *inset = par->getInset(i);
+ if (inset && dynamic_cast<InsetLabel const *>(inset)) {
+ // Generate the attributes for the section if need be.
+ auto label = dynamic_cast<InsetLabel const *>(inset);
+ id += "xml:id=\"" + xml::cleanID(label->screenLabel()) + "\"";
+
+ // Don't output the ID as a DocBook <anchor>.
+ ourparams.docbook_anchors_to_ignore.emplace(label->screenLabel());
+
+ // Cannot have multiple IDs per tag.
+ break;
+ }
+ }
+
+ // Write the open tag for this section.
+ docstring tag = from_utf8("<" + sectionTag);
+ if (!id.empty())
+ tag += from_utf8(" ") + id;
+ tag += from_utf8(">");
+ xs << XMLStream::ESCAPE_NONE << tag;
+ xs << xml::CR();
+ }
+ }
+
+ // Close all sections before the bibliography.
+ // TODO: Only close all when the bibliography is at the end of the document? Or force to output the bibliography at the end of the document? Or don't care (as allowed by DocBook)?
+ auto insetsLength = distance(par->insetList().begin(), par->insetList().end());
+ if (insetsLength > 0) {
+ Inset const *firstInset = par->getInset(0);
+ if (firstInset && dynamic_cast<InsetBibtex const *>(firstInset)) {
+ while (!headerLevels.empty()) {
+ int level = headerLevels.top().first;
+ docstring tag = from_utf8("</" + headerLevels.top().second + ">");
+ headerLevels.pop();
+
+ // Output the tag only if it corresponds to a legit section.
+ if (level != Layout::NOT_IN_TOC) {
+ xs << XMLStream::ESCAPE_NONE << tag;
+ xs << xml::CR();
+ }
+ }
+ }
+ }
+
+ // Generate this paragraph.
+ tie(par, send) = makeAny(text, buf, xs, ourparams, par, send, pend);
+ bpit += distance(lastStartedPar, par);
+ }
+
+ // If need be, close <section>s, but only at the end of the document (otherwise, dealt with at the beginning
+ // of the loop).
+ while (!headerLevels.empty() && headerLevels.top().first > Layout::NOT_IN_TOC) {
+ docstring tag = from_utf8("</" + headerLevels.top().second + ">");
+ headerLevels.pop();
+ xs << XMLStream::ESCAPE_NONE << tag;
+ xs << xml::CR();
+ }
+}
+
} // namespace lyx
*
* \author Lars Gullik Bjønnes
* \author José Matos
+ * \author Thibaut Cuvelier
+ * \author Richard Heck
*
* Full author contact details are available in file CREDITS.
*/
#ifndef OUTPUT_DOCBOOK_H
#define OUTPUT_DOCBOOK_H
+#include "LayoutEnums.h"
+
+#include "support/docstream.h"
#include "support/strfwd.h"
#include "xml.h"
namespace lyx {
-std::string const fontToDocBookTag(xml::FontTypes type);
-
class Buffer;
class OutputParams;
class Text;
+///
+std::string const fontToDocBookTag(xml::FontTypes type);
+///
+xml::FontTag docbookStartFontTag(xml::FontTypes type);
+///
+xml::EndFontTag docbookEndFontTag(xml::FontTypes type);
+
///
void docbookParagraphs(Text const & text,
- Buffer const & buf,
- odocstream & os,
- OutputParams const & runparams);
+ Buffer const & buf,
+ XMLStream & os,
+ OutputParams const & runparams);
} // namespace lyx
#include "support/convert.h"
#include "support/docstream.h"
+#include "support/lassert.h"
#include "support/lstrings.h"
#include "support/textutils.h"
#include <map>
#include <functional>
#include <QThreadStorage>
-#include <support/lassert.h>
using namespace std;
using namespace lyx::support;
XMLStream &XMLStream::operator<<(xml::CR const &) {
+ clearTagDeque();
os_ << from_ascii("\n");
return *this;
}
-bool XMLStream::isTagOpen(xml::StartTag const &stag) const {
+bool XMLStream::isTagOpen(xml::StartTag const &stag, int maxdepth) const {
auto sit = tag_stack_.begin();
auto sen = tag_stack_.cend();
- for (; sit != sen; ++sit)
+ for (; sit != sen && maxdepth != 0; ++sit) {
if (**sit == stag)
return true;
+ maxdepth -= 1;
+ }
return false;
}
-bool XMLStream::isTagOpen(xml::EndTag const &etag) const {
+bool XMLStream::isTagOpen(xml::EndTag const &etag, int maxdepth) const {
auto sit = tag_stack_.begin();
auto sen = tag_stack_.cend();
- for (; sit != sen; ++sit)
+ for (; sit != sen && maxdepth != 0; ++sit) {
if (etag == **sit)
return true;
+ maxdepth -= 1;
+ }
return false;
}
-bool XMLStream::isTagPending(xml::StartTag const &stag) const {
+bool XMLStream::isTagPending(xml::StartTag const &stag, int maxdepth) const {
auto sit = pending_tags_.begin();
auto sen = pending_tags_.cend();
- for (; sit != sen; ++sit)
+ for (; sit != sen && maxdepth != 0; ++sit) {
if (**sit == stag)
return true;
+ maxdepth -= 1;
+ }
return false;
}
/// for it is disabled by default, however, so you will need
/// to enable it if you want to use it.
void dumpTagStack(std::string const & msg);
-private:
///
- void clearTagDeque();
+ bool isTagOpen(xml::StartTag const &, int maxdepth = -1) const;
///
- bool isTagOpen(xml::StartTag const &) const;
+ bool isTagOpen(xml::EndTag const &, int maxdepth = -1) const;
///
- bool isTagOpen(xml::EndTag const &) const;
+ bool isTagPending(xml::StartTag const &, int maxdepth = -1) const;
+private:
///
- bool isTagPending(xml::StartTag const &) const;
+ void clearTagDeque();
///
void writeError(std::string const &) const;
///
/// </tag_>
virtual docstring writeEndTag() const;
///
- virtual FontTag const * asFontTag() const { return 0; }
+ virtual FontTag const * asFontTag() const { return nullptr; }
///
virtual bool operator==(StartTag const & rhs) const
{ return tag_ == rhs.tag_; }
///
FontTag(docstring const & tag, FontTypes type): StartTag(tag), font_type_(type) {}
///
+ FontTag(std::string const & tag, FontTypes type): StartTag(from_utf8(tag)), font_type_(type) {}
+ ///
FontTag(docstring const & tag, docstring const & attr, FontTypes type): StartTag(tag, attr), font_type_(type) {}
///
+ FontTag(std::string const & tag, std::string const & attr, FontTypes type): StartTag(from_utf8(tag), from_utf8(attr)), font_type_(type) {}
+ ///
FontTag const * asFontTag() const override { return this; }
///
bool operator==(StartTag const &) const override;
///
EndFontTag(docstring const & tag, FontTypes type): EndTag(tag), font_type_(type) {}
///
+ EndFontTag(std::string const & tag, FontTypes type): EndTag(from_utf8(tag)), font_type_(type) {}
+ ///
EndFontTag const * asFontTag() const override { return this; }
///
FontTypes font_type_;