]> git.lyx.org Git - lyx.git/commitdiff
New DocBook support
authorThibaut Cuvelier <cuvelier.thibaut@gmail.com>
Mon, 8 Jun 2020 21:27:49 +0000 (23:27 +0200)
committerPavel Sanda <sanda@lyx.org>
Wed, 8 Jul 2020 06:42:16 +0000 (08:42 +0200)
104 files changed:
src/BiblioInfo.cpp
src/BiblioInfo.h
src/Buffer.cpp
src/Buffer.h
src/Converter.cpp
src/Converter.h
src/Floating.cpp
src/Floating.h
src/Layout.cpp
src/Layout.h
src/OutputParams.cpp
src/OutputParams.h
src/Paragraph.cpp
src/Paragraph.h
src/TextClass.cpp
src/TextClass.h
src/insets/Inset.cpp
src/insets/Inset.h
src/insets/InsetArgument.h
src/insets/InsetBibtex.cpp
src/insets/InsetBibtex.h
src/insets/InsetBox.cpp
src/insets/InsetBox.h
src/insets/InsetBranch.cpp
src/insets/InsetBranch.h
src/insets/InsetCaption.cpp
src/insets/InsetCaption.h
src/insets/InsetCaptionable.cpp
src/insets/InsetCaptionable.h
src/insets/InsetCitation.cpp
src/insets/InsetCitation.h
src/insets/InsetCommand.cpp
src/insets/InsetCommand.h
src/insets/InsetCounter.cpp
src/insets/InsetCounter.h
src/insets/InsetERT.cpp
src/insets/InsetERT.h
src/insets/InsetExternal.cpp
src/insets/InsetExternal.h
src/insets/InsetFloat.cpp
src/insets/InsetFloat.h
src/insets/InsetFloatList.h
src/insets/InsetFoot.cpp
src/insets/InsetFoot.h
src/insets/InsetGraphics.cpp
src/insets/InsetGraphics.h
src/insets/InsetHyperlink.cpp
src/insets/InsetHyperlink.h
src/insets/InsetIPA.cpp
src/insets/InsetIPA.h
src/insets/InsetIPAMacro.cpp
src/insets/InsetIPAMacro.h
src/insets/InsetInclude.cpp
src/insets/InsetInclude.h
src/insets/InsetIndex.cpp
src/insets/InsetIndex.h
src/insets/InsetLabel.cpp
src/insets/InsetLabel.h
src/insets/InsetLine.cpp
src/insets/InsetLine.h
src/insets/InsetListings.cpp
src/insets/InsetListings.h
src/insets/InsetMarginal.cpp
src/insets/InsetMarginal.h
src/insets/InsetNewline.cpp
src/insets/InsetNewline.h
src/insets/InsetNewpage.cpp
src/insets/InsetNewpage.h
src/insets/InsetNomencl.cpp
src/insets/InsetNomencl.h
src/insets/InsetNote.cpp
src/insets/InsetNote.h
src/insets/InsetPhantom.cpp
src/insets/InsetPhantom.h
src/insets/InsetQuotes.cpp
src/insets/InsetQuotes.h
src/insets/InsetRef.cpp
src/insets/InsetRef.h
src/insets/InsetScript.cpp
src/insets/InsetScript.h
src/insets/InsetSeparator.cpp
src/insets/InsetSeparator.h
src/insets/InsetSpace.cpp
src/insets/InsetSpace.h
src/insets/InsetSpecialChar.cpp
src/insets/InsetSpecialChar.h
src/insets/InsetTOC.cpp
src/insets/InsetTOC.h
src/insets/InsetTabular.cpp
src/insets/InsetTabular.h
src/insets/InsetText.cpp
src/insets/InsetText.h
src/insets/InsetVSpace.cpp
src/insets/InsetVSpace.h
src/insets/InsetWrap.cpp
src/insets/InsetWrap.h
src/mathed/InsetMathHull.cpp
src/mathed/InsetMathHull.h
src/mathed/InsetMathRef.cpp
src/mathed/InsetMathRef.h
src/output_docbook.cpp
src/output_docbook.h
src/xml.cpp
src/xml.h

index f43d4149ab5fa907e6417c4d7451d96af90bdd6d..4c2f5497db0242cd74133b2e02e8693fd88448a7 100644 (file)
@@ -22,7 +22,7 @@
 #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"
@@ -1591,4 +1591,77 @@ string citationStyleToString(const CitationStyle & cs, bool const latex)
        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
index 3ef1ead5f39d92cc1379aca25a4d0a5d5fa07568..4509101fd379486c7b5ed200b3f23e0088b53a02 100644 (file)
@@ -35,6 +35,9 @@ CitationStyle citationStyleFromString(std::string const & latex_str,
 /// 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.
index eb2a4ef6d156ff7b35cc4dc3f0a9eecb03480819..bfb0dbb33dfcf580f10864e223175af585d7c3ee 100644 (file)
 #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"
@@ -2112,7 +2112,7 @@ Buffer::ExportStatus Buffer::makeDocBookFile(FileName const & fname,
        updateMacroInstances(OutputUpdate);
 
        ExportStatus const retval =
-               writeDocBookSource(ofs, fname.absFileName(), runparams, output);
+               writeDocBookSource(ofs, runparams, output);
        if (retval == ExportKilled)
                return ExportKilled;
 
@@ -2123,85 +2123,56 @@ Buffer::ExportStatus Buffer::makeDocBookFile(FileName const & fname,
 }
 
 
-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;
 }
@@ -4132,8 +4103,9 @@ unique_ptr<TexRow> Buffer::getSourceCode(odocstream & os, string const & format,
                        // 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,
@@ -4185,8 +4157,8 @@ unique_ptr<TexRow> Buffer::getSourceCode(odocstream & os, string const & format,
                                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);
@@ -4495,8 +4467,9 @@ Buffer::ExportStatus Buffer::doExport(string const & target, bool put_in_tempdir
                        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;
index 8eb6e06494d93bbc27740d97f0c22eb009eb5057..40d4e11a30ffd5c57650fae7f4491620a183e4a5 100644 (file)
@@ -335,7 +335,7 @@ public:
                             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;
        ///
index 5de38b6cca0a1a7d69a00003ddeae67befcc20c4..59604d312740f3f368ffdc8a7ddf686d4a2275fb 100644 (file)
@@ -105,7 +105,7 @@ private:
 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)
 {}
 
@@ -122,7 +122,7 @@ void Converter::readFlags()
                        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() ?
@@ -276,7 +276,7 @@ OutputParams::FLAVOR Converters::getFlavor(Graph::EdgePath const & path,
                        if (conv.latex_flavor() == "pdflatex")
                                return OutputParams::PDFLATEX;
                }
-               if (conv.xml())
+               if (conv.docbook())
                        return OutputParams::DOCBOOK5;
        }
        return buffer ? buffer->params().getOutputFlavor()
index 684895e984b8e57de914823b09b1a4615b6589db..fe95be6ed8948e099df16fac6990c4037e7df432 100644 (file)
@@ -74,7 +74,7 @@ public:
        ///
        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
@@ -108,8 +108,8 @@ private:
        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.
index 3dbc0a9f946dd2110c3359774ae62f9066f110b5..ebd1e41c09812f7f89ed6770451d1aa65326b953 100644 (file)
@@ -30,7 +30,8 @@ Floating::Floating(string const & type, string const & placement,
                   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),
@@ -38,7 +39,8 @@ Floating::Floating(string const & type, string const & placement,
          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)
 {}
 
 
@@ -80,4 +82,42 @@ string Floating::defaultCSSClass() const
 }
 
 
+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
index f013ed2270cf9129cac4965178ec6e8c0f854225..d10694f92be29b88400e7970cbc3fff7a0ffb3db 100644 (file)
@@ -37,9 +37,9 @@ public:
                 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_; }
        ///
@@ -79,6 +79,12 @@ public:
        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;
@@ -120,6 +126,12 @@ private:
        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_;
 };
 
 
index 877464f240b74c5856525e73d03983027cf864d6..04cd74792f560359797677ac2cf246816af0af2b 100644 (file)
@@ -31,7 +31,7 @@ using namespace lyx::support;
 
 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]
@@ -104,6 +104,21 @@ enum LayoutTags {
        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,
@@ -204,6 +219,21 @@ bool Layout::readIgnoreForcelocal(Lexer & lex, TextClass const & tclass)
                { "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 },
@@ -689,6 +719,66 @@ bool Layout::readIgnoreForcelocal(Lexer & lex, TextClass const & tclass)
                        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;
@@ -1499,8 +1589,38 @@ void Layout::write(ostream & os) const
                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";
 }
@@ -1648,6 +1768,116 @@ string Layout::defaultCSSClass() const
 }
 
 
+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)
index ffc976d8ff7df98c00f2836d14a896e1bc24af78..7e9409ad44c9216dfe9f3091657ad61dbb812f2c 100644 (file)
@@ -193,6 +193,36 @@ public:
        ///
        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; }
@@ -457,6 +487,39 @@ private:
        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
index 0409634be3b895a6e37c618c0b910d10ee8f7151..e2e3332069b89566165aab3e0a25e4b840d7d1b2 100644 (file)
@@ -33,7 +33,9 @@ OutputParams::OutputParams(Encoding const * enc)
          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
index f26aa52263c753d5f8bc501640632cd65b1fb290..9b4fdbf0006bcdd2bfc9fe9e0276995cd83edace 100644 (file)
@@ -16,6 +16,7 @@
 #include "Changes.h"
 
 #include <memory>
+#include <unordered_set>
 
 
 namespace lyx {
@@ -350,6 +351,24 @@ public:
        /// 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;
 
index 5d1a7fe82041de854a1433a66ddeb0aad066d9d0..2ab27da77eabc535b7287ec902e103e42653f0f7 100644 (file)
@@ -64,6 +64,7 @@
 #include "support/lassert.h"
 #include "support/lstrings.h"
 #include "support/textutils.h"
+#include "output_docbook.h"
 
 #include <atomic>
 #include <sstream>
@@ -2054,13 +2055,6 @@ docstring Paragraph::expandLabel(Layout const & layout,
 }
 
 
-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
 {
@@ -2952,18 +2946,17 @@ string Paragraph::getID(Buffer const &, OutputParams 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;
@@ -3005,57 +2998,312 @@ bool Paragraph::Private::onlyText(Buffer const & buf, Font const & outerfont, po
 }
 
 
-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();
 }
 
 
index 60b829e813ced09832c6edbf56dec0e5b005f77e..fb18352dbc24b483851a88a877084314478da4ca 100644 (file)
@@ -200,27 +200,29 @@ public:
        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;
@@ -307,8 +309,6 @@ public:
        ///
        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;
index 91f68f729124df5327a9a7691c5c21d233d53ce6..a39ab34f2aacc7b5ae39ac0311894485bb3eeb88 100644 (file)
@@ -62,7 +62,7 @@ namespace lyx {
 // 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
@@ -143,7 +143,7 @@ TextClass::TextClass()
          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)
 {
 }
 
@@ -216,7 +216,9 @@ enum TextClassTags {
        TC_FULLAUTHORLIST,
        TC_OUTLINERNAME,
        TC_TABLESTYLE,
-       TC_BIBINTOC
+       TC_BIBINTOC,
+       TC_DOCBOOKROOT,
+       TC_DOCBOOKFORCEABSTRACT
 };
 
 
@@ -239,6 +241,8 @@ LexerKeyword textClassTags[] = {
        { "defaultfont",       TC_DEFAULTFONT },
        { "defaultmodule",     TC_DEFAULTMODULE },
        { "defaultstyle",      TC_DEFAULTSTYLE },
+       { "docbookforceabstract", TC_DOCBOOKFORCEABSTRACT },
+       { "docbookroot",       TC_DOCBOOKROOT },
        { "excludesmodule",    TC_EXCLUDESMODULE },
        { "float",             TC_FLOAT },
        { "format",            TC_FORMAT },
@@ -868,10 +872,20 @@ TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
                        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
        }
 
@@ -994,7 +1008,6 @@ void TextClass::readClassOptions(Lexer & lexrc)
                CO_PAGESIZE_FORMAT,
                CO_PAGESTYLE,
                CO_OTHER,
-               CO_HEADER,
                CO_END
        };
 
@@ -1002,7 +1015,6 @@ void TextClass::readClassOptions(Lexer & lexrc)
                {"end",       CO_END },
                {"fontsize",  CO_FONTSIZE },
                {"fontsizeformat", CO_FONTSIZE_FORMAT },
-               {"header",    CO_HEADER },
                {"other",     CO_OTHER },
                {"pagesize",  CO_PAGESIZE },
                {"pagesizeformat", CO_PAGESIZE_FORMAT },
@@ -1048,10 +1060,6 @@ void TextClass::readClassOptions(Lexer & lexrc)
                        else
                                options_ += ',' + lexrc.getString();
                        break;
-               case CO_HEADER:
-                       lexrc.next();
-                       class_header_ = subst(lexrc.getString(), "&quot;", "\"");
-                       break;
                case CO_END:
                        getout = true;
                        break;
@@ -1373,6 +1381,8 @@ bool TextClass::readFloat(Lexer & lexrc)
                FT_HTMLSTYLE,
                FT_HTMLATTR,
                FT_HTMLTAG,
+               FT_DOCBOOKATTR,
+               FT_DOCBOOKTAG,
                FT_LISTCOMMAND,
                FT_REFPREFIX,
                FT_ALLOWED_PLACEMENT,
@@ -1386,6 +1396,8 @@ bool TextClass::readFloat(Lexer & lexrc)
                { "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 },
@@ -1410,6 +1422,8 @@ bool TextClass::readFloat(Lexer & lexrc)
        string htmlattr;
        docstring htmlstyle;
        string htmltag;
+       string docbookattr;
+       string docbooktag;
        string listname;
        string listcommand;
        string name;
@@ -1523,6 +1537,14 @@ bool TextClass::readFloat(Lexer & lexrc)
                        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;
@@ -1550,8 +1572,9 @@ bool TextClass::readFloat(Lexer & lexrc)
                }
                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),
index 207b868a4b5379a5e2d5dd48eea288dd5b74317f..bf061acd0ff64e3fae2e4a819563babfe371ab07 100644 (file)
@@ -303,8 +303,13 @@ protected:
        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.
@@ -484,6 +489,10 @@ public:
        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.
index 904ac81686c2708bf4f088a39291e86e0e0dcf71..b561533998ede6e1dbfe86249d35abc77ec78f12 100644 (file)
@@ -29,6 +29,7 @@
 #include "FuncStatus.h"
 #include "MetricsInfo.h"
 #include "output_xhtml.h"
+#include "xml.h"
 #include "Text.h"
 #include "TextClass.h"
 #include "TocBackend.h"
@@ -466,9 +467,9 @@ bool Inset::idxUpDown(Cursor &, bool) const
 }
 
 
-int Inset::docbook(odocstream &, OutputParams const &) const
+void Inset::docbook(XMLStream & xs, OutputParams const &) const
 {
-       return 0;
+       xs << "[[Inset: " << from_ascii(insetName(lyxCode())) << "]]";
 }
 
 
index 653123073a1cb10b27cb103ef38349bf555a97ea..4ef73db0acbcfb1aacc7a4d45813cf916b1e0a3e 100644 (file)
@@ -341,7 +341,7 @@ public:
        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
index 8cc3e087227560c337fb16cf0374b6a10667d3a4..933d66b32d5adc84cb0d35bdda301c5c48c0339c 100644 (file)
@@ -54,7 +54,7 @@ public:
        ///
        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(); }
index 2e41a4f3e487ad3df78f66712878f4efbba27326..3e59d654ceba136f59b4f070c414ef5e55630246 100644 (file)
@@ -27,7 +27,6 @@
 #include "FuncStatus.h"
 #include "LaTeXFeatures.h"
 #include "output_latex.h"
-#include "output_xhtml.h"
 #include "xml.h"
 #include "OutputParams.h"
 #include "PDFOptions.h"
@@ -44,6 +43,7 @@
 #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;
@@ -1075,6 +1079,342 @@ docstring InsetBibtex::xhtml(XMLStream & xs, OutputParams const &) const
 }
 
 
+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());
index 073b3760161065fc1791e4bcdd500df79ab5fd2c..932fb0f48a720e1b550ee34766f07ecfc293e3f3 100644 (file)
@@ -66,6 +66,8 @@ public:
        ///
        docstring xhtml(XMLStream &, OutputParams const &) const;
        ///
+       void docbook(XMLStream &, OutputParams const &) const;
+       ///
        std::string contextMenuName() const;
        //@}
 
index 3e8612d4fad899dbed917f5d688382e7146b4913..c61efe6d0165138be1b477ca344a4fea14100400 100644 (file)
@@ -715,9 +715,9 @@ int InsetBox::plaintext(odocstringstream & os,
 }
 
 
-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);
 }
 
 
index a1f44669580ab8d78bfc08ba7544a9b5747ee5cd..418a24277200f67a479ed11a383780a73b2f36be 100644 (file)
@@ -133,7 +133,7 @@ public:
        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;
        ///
index db987ba0d3dd3e45fbd2c0be5222db692da0ca2b..a70ff27463c6db5003efc637d6e33315d02aff80 100644 (file)
@@ -24,6 +24,7 @@
 #include "Lexer.h"
 #include "LyX.h"
 #include "OutputParams.h"
+#include "output_docbook.h"
 #include "output_xhtml.h"
 #include "TextClass.h"
 #include "TocBackend.h"
@@ -329,10 +330,14 @@ int InsetBranch::plaintext(odocstringstream & os,
 }
 
 
-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);
+       }
 }
 
 
index 1723fd1484276d88297195ed680051b7651c0e14..42b11d215d58944bbeb8e4360ba02e8f8620233e 100644 (file)
@@ -76,7 +76,7 @@ private:
        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;
        ///
index 42f951989ce0cf62af9fc437a2dc23bdfb798f14..029162563af3ea888f04c855cea090d303aa69ac 100644 (file)
@@ -27,6 +27,7 @@
 #include "Language.h"
 #include "LyXRC.h"
 #include "MetricsInfo.h"
+#include "xml.h"
 #include "output_latex.h"
 #include "output_xhtml.h"
 #include "OutputParams.h"
@@ -291,14 +292,9 @@ int InsetCaption::plaintext(odocstringstream & os,
 }
 
 
-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).
 }
 
 
@@ -364,6 +360,19 @@ int InsetCaption::getCaptionAsPlaintext(odocstream & os,
 }
 
 
+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
 {
index 2b0af0dd0829d96b3ca3a519e60b66603da9f84f..1fb1022faadc02e58c28f855165514a953cd2839 100644 (file)
@@ -30,6 +30,8 @@ public:
        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;
        ///
@@ -76,7 +78,7 @@ private:
        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;
        ///
index 61c9acc3b8ac492aa97ad84f2c3339d741fab7a3..a8a2108ce9bd8917c061065d6c6dc03c7a478827 100644 (file)
@@ -80,6 +80,19 @@ docstring InsetCaptionable::getCaptionText(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();
index 870489888cae4e0239e3562514fae73f8fc8c69f..3190c9a01c6441cfb69a90d6fb37608acdcbc4ce 100644 (file)
@@ -38,6 +38,8 @@ protected:
        ///
        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; }
index 744368d2e3d7e59c1b44e70fbd2935939ce408ce..337c4bb2dd6324f827039a936702adb6c84840c8 100644 (file)
@@ -24,6 +24,7 @@
 #include "FuncStatus.h"
 #include "LaTeXFeatures.h"
 #include "output_xhtml.h"
+#include <output_docbook.h>
 #include "ParIterator.h"
 #include "texstream.h"
 #include "TocBackend.h"
@@ -545,19 +546,33 @@ static docstring const cleanupWhitespace(docstring const & citelist)
 }
 
 
-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
index c7cd8dcb604b1d8469bbd20b5e8d2aa7328456e6..bc15e11a6f28609e8acec5db1fcb1f694b159c0c 100644 (file)
@@ -56,7 +56,7 @@ public:
        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;
        ///
index 47affcc543a41177d0f57077dd26c27c6630357d..6d15c6886abf65e5f7b2b7a6176e687ce0e58208 100644 (file)
@@ -162,9 +162,9 @@ int InsetCommand::plaintext(odocstringstream & os,
 }
 
 
-int InsetCommand::docbook(odocstream &, OutputParams const &) const
+void InsetCommand::docbook(XMLStream &, OutputParams const &) const
 {
-       return 0;
+       return;
 }
 
 
index 58d2345841b7c25b194957da4edaa084eff6547f..0c0cd1a954adbb76b726508d71de51d41ea1e2d9 100644 (file)
@@ -89,7 +89,7 @@ public:
        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;
        ///
index 85efd3add2d027f61c9ab359dae88bbd5ef7f2d2..015d458e62f417b6999760767a734500693c0c23 100644 (file)
@@ -184,13 +184,12 @@ void InsetCounter::trackCounters(string const & cmd) 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;
 }
 
 
index 65334a3836f62838c36885d7d9dbe9aaacd79257..3020fdb424f21b7662d7a04bfe6585027c873b13 100644 (file)
@@ -39,7 +39,7 @@ public:
        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;
        ///
index 403df75032da7d1c5263c65e785b42be7d68cbd1..eb4567175e5bff28c97fce6e0ef41374d8ac782a 100644 (file)
@@ -25,6 +25,7 @@
 #include "Lexer.h"
 #include "LyXAction.h"
 #include "OutputParams.h"
+#include "xml.h"
 #include "ParagraphParameters.h"
 #include "Paragraph.h"
 
@@ -90,25 +91,24 @@ int InsetERT::plaintext(odocstringstream & os,
 }
 
 
-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 << " -->";
 }
 
 
index 1ee17b3a15ff2fbcac80a40d21cb498cfd404c83..b24c93d628e16ae8a40c34a24e73364de049cf61 100644 (file)
@@ -52,7 +52,7 @@ private:
        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;
        ///
index 8531e56694ee023cdd821c9ad46a34a25fa69fe2..a2f7216eaa1b580328f6598030d629869f0e5851 100644 (file)
@@ -770,42 +770,35 @@ int InsetExternal::plaintext(odocstringstream & os,
 }
 
 
-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();
 }
 
index b03e416de822f58fcba7cf8a9dc1203f0b5e34c8..8fb95b33d8a7c5a446e8e791a49acf01c3d5b739 100644 (file)
@@ -146,7 +146,9 @@ public:
        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;
index c1dc9856a92abf2f16627f6974e31b6d76fd0b39..6644d7169302fa69d714363064ebaac21891c634 100644 (file)
  * 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"
@@ -480,15 +486,193 @@ int InsetFloat::plaintext(odocstringstream & os, OutputParams const & runparams,
 }
 
 
-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();
 }
 
 
index b43147e7252e3a87d01e3c3bfe40c68608370636..bdf68089a9a49a085ca7e4bc24052b98cad77190 100644 (file)
@@ -99,7 +99,7 @@ private:
        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;
        ///
index 20c8e22d62587374635397fc547a6b21b75e36e4..e0ae669de0ca8842b12f3ba8fef6b7d7bd97c0a1 100644 (file)
@@ -40,7 +40,7 @@ public:
        ///
        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;
index 5865c7d77ea4ab99e3ba107b16f60da5d72f3671..ad3fc3a641fb0e64c0d84de807f5871e864f062b 100644 (file)
@@ -10,6 +10,7 @@
  */
 
 #include <config.h>
+#include <output_docbook.h>
 
 #include "InsetFoot.h"
 #include "InsetBox.h"
@@ -122,13 +123,12 @@ int InsetFoot::plaintext(odocstringstream & os,
 }
 
 
-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);
 }
 
 
index 194661a9b2a22a2e336aea9071abcbaba41b3f92..e93892738544ad456dad317ef6fd666b8c58267f 100644 (file)
@@ -35,7 +35,7 @@ private:
        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
index 0daae4c9214b29d947f1d17f35af46e28f58e7d4..2b19d50665bce524e98538c0f017f5b95b9438d6 100644 (file)
@@ -95,7 +95,7 @@ TODO
 
 #include <algorithm>
 #include <sstream>
-#include <tuple>
+#include <output_docbook.h>
 
 using namespace std;
 using namespace lyx::support;
@@ -125,8 +125,9 @@ string findTargetFormat(string const & format, OutputParams const & runparams)
                // 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"))
@@ -522,7 +523,6 @@ docstring InsetGraphics::createDocBookAttributes() const
                }
        }
 
-
        if (!params().special.empty())
                options << from_ascii(params().special) << " ";
 
@@ -935,62 +935,24 @@ int InsetGraphics::plaintext(odocstringstream & os,
 }
 
 
-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();
 }
 
 
index 8c07c0f9698dd5b0de3981b4629c383a6805525b..c987ed06159adbcf4b191febb86b23f8577bfbf2 100644 (file)
@@ -78,7 +78,7 @@ public:
        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
index 6246ff840a53ac3752fb1fbf9b59b009b09844f5..1444fc19a28edc260aa48b987c9a620190f5b725 100644 (file)
@@ -10,6 +10,7 @@
  */
 
 #include <config.h>
+#include <output_docbook.h>
 
 #include "InsetHyperlink.h"
 
@@ -213,14 +214,11 @@ int InsetHyperlink::plaintext(odocstringstream & os,
 }
 
 
-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("&amp;"))
-          << "\">"
-          << xml::escapeString(getParam("name"))
-          << "</ulink>";
-       return 0;
+       xs << xml::StartTag("link", "xlink:href=\"" + subst(getParam("target"), from_ascii("&"), from_ascii("&amp;")) + "\"");
+       xs << xml::escapeString(getParam("name"));
+       xs << xml::EndTag("link");
 }
 
 
index 0e0e8d34465320821e9ddb43feea6ea367f23b36..90ae05b94f7413f721373c68ccdf102747937abe 100644 (file)
@@ -51,7 +51,7 @@ public:
        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;
        //@}
index e11f0cc1f5186924178c21fe43eeef95ec580bc4..02dad4d455040ebf1585f77cf435033ade6d519c 100644 (file)
@@ -233,6 +233,14 @@ void InsetIPA::latex(otexstream & os, OutputParams const & runparams_in) 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);
index 35f2ea4b5de4fdfda6be81259f8a4a5838c8e501..695179738f400581467e15d451341fb131e35200 100644 (file)
@@ -77,6 +77,8 @@ public:
        ///
        void latex(otexstream &, OutputParams const &) const;
        ///
+       void docbook(XMLStream &, OutputParams const &) const;
+       ///
        docstring xhtml(XMLStream & xs, OutputParams const &) const;
        ///
        void validate(LaTeXFeatures & features) const;
index 91c5c88176438ac6e473aac22634aaa4b99459bc..f96ee7a5e878fcc62944c51cf7f63c2b75bfaf30 100644 (file)
@@ -22,7 +22,7 @@
 #include "LaTeXFeatures.h"
 #include "Lexer.h"
 #include "MetricsInfo.h"
-#include "output_xhtml.h"
+#include "xml.h"
 #include "texstream.h"
 
 #include "frontends/FontMetrics.h"
@@ -305,10 +305,22 @@ int InsetIPADeco::plaintext(odocstringstream & os,
 }
 
 
-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 << "&#x0361;";
+       else if (params_.type == InsetIPADecoParams::Bottomtiebar)
+               xs << XMLStream::ESCAPE_NONE << "&#x035c;";
+       xs << XMLStream::ESCAPE_NONE << after;
 }
 
 
@@ -540,19 +552,31 @@ int InsetIPAChar::plaintext(odocstringstream & os, OutputParams const &, size_t)
 }
 
 
-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 << "&#x2e5;";
+        xs << XMLStream::ESCAPE_NONE << "&#x2e9;";
+        break;
+    case TONE_RISING:
+        xs << XMLStream::ESCAPE_NONE << "&#x2e9;";
+        xs << XMLStream::ESCAPE_NONE << "&#x2e5;";
+        break;
+    case TONE_HIGH_RISING:
+        xs << XMLStream::ESCAPE_NONE << "&#x2e7;";
+        xs << XMLStream::ESCAPE_NONE << "&#x2e5;";
+        break;
+    case TONE_LOW_RISING:
+        xs << XMLStream::ESCAPE_NONE << "&#x2e9;";
+        xs << XMLStream::ESCAPE_NONE << "&#x2e7;";
+        break;
+    case TONE_HIGH_RISING_FALLING:
+        xs << XMLStream::ESCAPE_NONE << "&#x2e8;";
+        xs << XMLStream::ESCAPE_NONE << "&#x2e5;";
+        xs << XMLStream::ESCAPE_NONE << "&#x2e8;";
+        break;
+    }
 }
 
 
index b985ef3b7b5707706db3453e00a9542693442d2e..273e572e258ddd7a1c5607180b92e6d9ce5d754d 100644 (file)
@@ -79,7 +79,7 @@ private:
        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;
        ///
@@ -150,7 +150,7 @@ public:
        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;
        ///
index 6eba05abde51d319d7ab81cc6cd49e3c4df05c76..235023ed0016b08f5e352e77913f065bb13bbd59 100644 (file)
@@ -987,12 +987,11 @@ docstring InsetInclude::xhtml(XMLStream & xs, OutputParams const & rp) 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();
 }
@@ -1037,56 +1036,68 @@ int InsetInclude::plaintext(odocstringstream & os,
 }
 
 
-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
+                  << " -->";
 }
 
 
index 08fe9434cc5f7830b51a00d3dd02c56ca95361e3..db7beca5d49e6ad268fd7d461c3497892b6ca84d 100644 (file)
@@ -94,7 +94,7 @@ public:
        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;
        ///
index 11a854528074e654b70946a543f10fb61bcabc62..ed77d1d966112b8c85439978cd6c000579ea5622 100644 (file)
 #include "frontends/alert.h"
 
 #include <algorithm>
+#include <set>
 #include <ostream>
 
+#include <QThreadStorage>
+
 using namespace std;
 using namespace lyx::support;
 
@@ -182,12 +185,181 @@ void InsetIndex::latex(otexstream & ios, OutputParams const & runparams_in) cons
 }
 
 
-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");
+       }
 }
 
 
index 01831d33bca5a8bb3b3c28e2fa4a281af07cb689..a999ee5bb3195b77793c26a64c4ad727733e1559 100644 (file)
@@ -57,7 +57,7 @@ private:
        ///
        void read(Lexer & lex);
        ///
-       int docbook(odocstream &, OutputParams const &) const;
+       void docbook(XMLStream &, OutputParams const &) const;
        ///
        docstring xhtml(XMLStream &, OutputParams const &) const;
        ///
index 44324933194d677760ae0abe47d1bfae9817173c..ec50d4feb601efdf349878f990b4bf6da7084de4 100644 (file)
@@ -25,7 +25,6 @@
 #include "InsetIterator.h"
 #include "Language.h"
 #include "LyX.h"
-#include "output_xhtml.h"
 #include "ParIterator.h"
 #include "xml.h"
 #include "texstream.h"
@@ -351,12 +350,13 @@ int InsetLabel::plaintext(odocstringstream & os,
 }
 
 
-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));
+       }
 }
 
 
index 8d063168b68223341f66a2f127e971a0983322c0..4747bb5d3e4e6a470f88152b21cf70718dba5b9f 100644 (file)
@@ -53,7 +53,7 @@ public:
        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;
        ///
index 1688af3ccf44aed376e485428b00918fc308244e..2479be13a03ced33b37e110527aba238803a8ac9 100644 (file)
@@ -37,6 +37,7 @@
 #include "support/lstrings.h"
 
 #include <cstdlib>
+#include <output_docbook.h>
 
 using namespace std;
 
@@ -183,10 +184,9 @@ int InsetLine::plaintext(odocstringstream & os,
 }
 
 
-int InsetLine::docbook(odocstream & os, OutputParams const &) const
+void InsetLine::docbook(XMLStream & xs, OutputParams const &) const
 {
-       os << '\n';
-       return 0;
+       xs << xml::CR();
 }
 
 
index 7be587028ffb9e9a55005ad68292530e9b72954e..4c918a872d0fe17022f44eb9522b3af7f48856ef 100644 (file)
@@ -41,8 +41,7 @@ private:
        /// 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;
index 2f1462a154a6419063ec5da984c76488be6c1897..05048d43513002c29d91e23a488c3982c69d5731 100644 (file)
@@ -27,6 +27,7 @@
 #include "LaTeXFeatures.h"
 #include "Lexer.h"
 #include "output_latex.h"
+#include "output_docbook.h"
 #include "output_xhtml.h"
 #include "OutputParams.h"
 #include "TextClass.h"
@@ -480,6 +481,45 @@ docstring InsetListings::xhtml(XMLStream & os, OutputParams const & rp) const
 }
 
 
+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";
index 06c310739f76e484ad2b6225f9be65a322c1cc9d..a74569c54464cefc26eeb03449bb26ce789cbcc8 100644 (file)
@@ -58,6 +58,8 @@ private:
        ///
        docstring xhtml(XMLStream &, OutputParams const &) const;
        ///
+       void docbook(XMLStream &, OutputParams const &) const;
+       ///
        void validate(LaTeXFeatures &) const;
        ///
        bool showInsetDialog(BufferView *) const;
index 3dfcf7d048ff8eb4f19888db620567ff7306fbd7..54922fced5acec533d95d8d254c0775b7474fb6a 100644 (file)
@@ -10,6 +10,7 @@
  */
 
 #include <config.h>
+#include <output_docbook.h>
 
 #include "InsetMarginal.h"
 
@@ -41,14 +42,15 @@ int InsetMarginal::plaintext(odocstringstream & os,
 }
 
 
-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");
 }
 
 
index db0f6db0239d15cdf9543f29088a916dc77a1563..749a0c4152eb4f03b0372356590cd40c21bee6b6 100644 (file)
@@ -34,7 +34,7 @@ public:
        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:
index 4930dd902a38930ea9b6f0c65856940937d2de1d..86f60f6b3a5441d6be3764daf25075d1f1637d5f 100644 (file)
@@ -10,6 +10,7 @@
  */
 
 #include <config.h>
+#include <output_docbook.h>
 
 #include "InsetNewline.h"
 
@@ -171,10 +172,13 @@ int InsetNewline::plaintext(odocstringstream & os,
 }
 
 
-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");
+       }
 }
 
 
index afa5fcdcc88ee07fe0b1528816d2866ea42d34b7..4209dfee380bcc1ed3e9ab5f43319134919c236f 100644 (file)
@@ -65,7 +65,7 @@ private:
        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;
        ///
index b108885f9c522a37c386688f4afac930342a2057..6e9f34211fb62803496086b01795403935ed37e3 100644 (file)
@@ -19,7 +19,7 @@
 #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"
@@ -248,10 +248,9 @@ int InsetNewpage::plaintext(odocstringstream & os,
 }
 
 
-int InsetNewpage::docbook(odocstream & os, OutputParams const &) const
+void InsetNewpage::docbook(XMLStream & os, OutputParams const &) const
 {
-       os << '\n';
-       return 0;
+       os << xml::CR();
 }
 
 
index 7fc4cc49b1f2e23d82456bf29a1c5e98f91c5536..1fb3436b62d373e728b85d35bd16d66f997b32d5 100644 (file)
@@ -66,7 +66,7 @@ private:
        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;
        ///
index 5e8813c39840ad1e658320c64d641763aa337fe9..e7fc8a7d98935ddfc3274e64b556faf83dec64c9 100644 (file)
@@ -29,7 +29,6 @@
 #include "Length.h"
 #include "LyX.h"
 #include "OutputParams.h"
-#include "output_xhtml.h"
 #include "xml.h"
 #include "texstream.h"
 #include "TocBackend.h"
@@ -105,12 +104,12 @@ int InsetNomencl::plaintext(odocstringstream & os,
 }
 
 
-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");
 }
 
 
@@ -120,20 +119,6 @@ docstring InsetNomencl::xhtml(XMLStream &, OutputParams const &) const
 }
 
 
-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");
@@ -316,29 +301,75 @@ bool InsetPrintNomencl::getStatus(Cursor & cur, FuncRequest const & cmd,
 }
 
 
-// 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();
 }
 
 
index d023237865c73485ece5f1c361969a34821c4579..b36527c7bb02036ea192898ef133058ae82d0c6d 100644 (file)
@@ -28,9 +28,6 @@ public:
        ///
        InsetNomencl(Buffer * buf, InsetCommandParams const &);
 
-       ///
-       int docbookGlossary(odocstream &) const;
-
        /// \name Public functions inherited from Inset class
        //@{
        ///
@@ -48,7 +45,7 @@ public:
        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;
        //@}
@@ -92,7 +89,7 @@ public:
        /// 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;
        ///
index 31df791f59a0e237df21bbc194a60b598da16804..85469f67ac06de438929ce478abd35a3b9fa4c06 100644 (file)
@@ -42,6 +42,7 @@
 
 #include <algorithm>
 #include <sstream>
+#include <output_docbook.h>
 
 using namespace std;
 
@@ -260,27 +261,28 @@ int InsetNote::plaintext(odocstringstream & os,
 }
 
 
-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();
+       }
 }
 
 
index 3c04a1012889c16119de884808806e8df7075594..f76ce21ed64e1fb0546ccf9bcb0d7b6f6d840e9c 100644 (file)
@@ -86,7 +86,7 @@ private:
        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;
        ///
index 4771cb83ee42899992846295e967d8b67141d664..37367add2ccafa0719b8c51ba57c10f983df3689 100644 (file)
@@ -348,22 +348,9 @@ int InsetPhantom::plaintext(odocstringstream & os,
 }
 
 
-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;
 }
 
 
index 44ab078c81a81e6e810d08456f3405add8026e39..89ccfb3d765ea6d2c9d6183d331770533d36368a 100644 (file)
@@ -79,7 +79,7 @@ private:
        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;
        ///
index 5818cd7a966f45e6457b8447323df165d20002c3..5358bbaf19d64deb9430f396ac4ad328cb23425c 100644 (file)
 #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"
@@ -1041,10 +1041,9 @@ docstring InsetQuotes::getQuoteEntity(bool isHTML) const {
 }
 
 
-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);
 }
 
 
index 939e6d61b4e43c39edcdd43306c9b905997b82e3..09eadec3c752436e35d923281bf2d53655742f44 100644 (file)
@@ -146,7 +146,7 @@ public:
        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;
 
index d4cdaac30cd8a725ef28573209d7d187cf8a50a7..e9b26953976a729bb4e2a2967859600f1d6fd865 100644 (file)
@@ -309,28 +309,59 @@ int InsetRef::plaintext(odocstringstream & os,
 }
 
 
-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;
 }
 
 
index 253718450bba82f2e0d5798d18063afe09c84e8f..192ca0d6967acedadccb490fb5ef94df90be2417 100644 (file)
@@ -61,7 +61,7 @@ public:
        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;
        ///
index 13b67e16e149371e0f0dddca22e03f9e50c6d93c..3dc9e1248a6f4bb3e822eadefe36deae14b8433f 100644 (file)
@@ -40,6 +40,7 @@
 #include "frontends/Painter.h"
 
 #include <algorithm>
+#include <output_docbook.h>
 
 using namespace std;
 
@@ -354,7 +355,7 @@ int InsetScript::plaintext(odocstringstream & os,
 }
 
 
-int InsetScript::docbook(odocstream & os, OutputParams const & runparams) const
+void InsetScript::docbook(XMLStream & xs, OutputParams const & runparams) const
 {
        docstring cmdname;
        switch (params_.type) {
@@ -365,11 +366,10 @@ int InsetScript::docbook(odocstream & os, OutputParams const & runparams) const
                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);
 }
 
 
index 7f9cd852786cbf6d836e6d02286ff4e2402f9619..45e766f943a9fc6388f941f2d3aa3d2231c830fc 100644 (file)
@@ -99,7 +99,7 @@ public:
        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);
index 42ff09e70ae0463ee74c5f1fd0d2977152bb95e3..b588b3943994abb17ba1d913e094d03af55ed849 100644 (file)
@@ -9,6 +9,7 @@
  */
 
 #include <config.h>
+#include <output_docbook.h>
 
 #include "InsetSeparator.h"
 
@@ -167,10 +168,9 @@ int InsetSeparator::plaintext(odocstringstream & os,
 }
 
 
-int InsetSeparator::docbook(odocstream & os, OutputParams const &) const
+void InsetSeparator::docbook(XMLStream & xs, OutputParams const &) const
 {
-       os << '\n';
-       return 0;
+       xs << xml::CR();
 }
 
 
index a57f45c4613c493469c130f2921cca18332e3b94..85d9347ed8000e867081e6768108bcb95155b556 100644 (file)
@@ -79,7 +79,7 @@ private:
        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;
        ///
index 84c6a2599c60ba163927bdc4b5f8e9a860a0788d..04b515cdccf5d088e188598d3385ce2c50d061c2 100644 (file)
@@ -26,8 +26,8 @@
 #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"
@@ -741,68 +741,57 @@ int InsetSpace::plaintext(odocstringstream & os,
 }
 
 
-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 << "&emsp;";
+               xs << XMLStream::ESCAPE_NONE << "&#x2003;"; // HTML: &emsp;
                break;
        case InsetSpaceParams::QQUAD:
-               os << "&emsp;&emsp;";
+               xs << XMLStream::ESCAPE_NONE << "&#x2003;&#x2003;"; // HTML: &emsp;&emsp;
                break;
        case InsetSpaceParams::ENSKIP:
-               os << "&ensp;";
+               xs << XMLStream::ESCAPE_NONE << "&#x2002;"; // HTML: &ensp;
                break;
        case InsetSpaceParams::PROTECTED:
-               os << "&nbsp;";
+               xs << XMLStream::ESCAPE_NONE << "&#xA0;"; // HTML: &nbsp;
                break;
        case InsetSpaceParams::VISIBLE:
-               os << "&#x2423;";
+               xs << XMLStream::ESCAPE_NONE << "&#x2423;";
                break;
-       case InsetSpaceParams::ENSPACE:
-               os << "&#x2060;&ensp;&#x2060;";
+       case InsetSpaceParams::ENSPACE: // HTML: &#x2060;&ensp;&#x2060; (word joiners)
+               xs << XMLStream::ESCAPE_NONE << "&#x2060;&#x2002;&#x2060;";
                break;
        case InsetSpaceParams::THIN:
-               os << "&thinsp;";
+               xs << XMLStream::ESCAPE_NONE << "&#x2009;"; // HTML: &thinspace;
                break;
        case InsetSpaceParams::MEDIUM:
-               os << "&emsp14;";
+               xs << XMLStream::ESCAPE_NONE << "&#x2005;"; // HTML: &emsp14;
                break;
        case InsetSpaceParams::THICK:
-               os << "&emsp13;";
+               xs << XMLStream::ESCAPE_NONE << "&#x2004;"; // HTML: &emsp13;
                break;
        case InsetSpaceParams::NEGTHIN:
        case InsetSpaceParams::NEGMEDIUM:
        case InsetSpaceParams::NEGTHICK:
-               // FIXME
-               os << "&nbsp;";
+               xs << XMLStream::ESCAPE_NONE << "&#xA0;"; // HTML: &nbsp;
                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;
 }
 
 
index 65b1dc6ac4ed0c9b473a69fdb130c3c8c3db8f24..1111aa1e828d7215d56104e429930ad511f1a08f 100644 (file)
@@ -129,7 +129,7 @@ public:
        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;
        ///
index 2e51e95d3f84c676284d91bbf3d0c7dd9722f185..33d3d0bc19d572b5920afe703069e7dfd684a78a 100644 (file)
@@ -22,6 +22,7 @@
 #include "Lexer.h"
 #include "MetricsInfo.h"
 #include "output_xhtml.h"
+#include "xml.h"
 #include "texstream.h"
 
 #include "frontends/FontMetrics.h"
@@ -529,48 +530,54 @@ int InsetSpecialChar::plaintext(odocstringstream & os,
 }
 
 
-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 << "&#xAD;";
+        break;
+    case ALLOWBREAK:
+       // Zero-width space
+        xs << XMLStream::ESCAPE_NONE << "&#x200B;";
+        break;
        case LIGATURE_BREAK:
+               // Zero width non-joiner
+               xs << XMLStream::ESCAPE_NONE << "&#x200C;";
                break;
        case END_OF_SENTENCE:
-               os << '.';
+               xs << '.';
                break;
        case LDOTS:
-               os << "&hellip;";
+               // &hellip;
+               xs << XMLStream::ESCAPE_NONE << "&#x2026;";
                break;
        case MENU_SEPARATOR:
-               os << "&lyxarrow;";
+               // &rArr;, right arrow.
+               xs << XMLStream::ESCAPE_NONE << "&#x21D2;";
                break;
        case SLASH:
-               os << '/';
+               // &frasl;, fractional slash.
+               xs << XMLStream::ESCAPE_NONE << "&#x2044;";
                break;
+               // Non-breaking hyphen.
        case NOBREAKDASH:
-               os << '-';
+               xs << XMLStream::ESCAPE_NONE << "&#x2011;";
                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 << "&#x03b5;";
                break;
        case PHRASE_LATEX:
-               os << "LaTeX";
+               xs << "LaTeX";
                break;
        }
-       return 0;
 }
 
 
index b7c6e794d044cc677d5bb346dcce0cfd59bf64bc..286e3b990738193e2d9f3011c3b6c9e6244e85e5 100644 (file)
@@ -78,7 +78,7 @@ public:
        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;
        ///
index c700e57640a97f26d1d51dbc43edbea2b0014824..4469b88939d72b9d259b86a600f110032d7e9e48 100644 (file)
@@ -128,11 +128,10 @@ int InsetTOC::plaintext(odocstringstream & os,
 }
 
 
-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;
 }
 
 
index 8ab1cc84ac5d29123eb5dfdd4c8a63a3654b3cd6..4b92e329a2ec95f86d91f146f05e0122b0e875e7 100644 (file)
@@ -44,7 +44,7 @@ public:
        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;
        ///
index fb9f64a7d2519d435ece908596e9135e8cc14720..e9d71f91ef0610b0f56c65db72a27bc8fc728ec8 100644 (file)
@@ -42,6 +42,7 @@
 #include "LyXRC.h"
 #include "MetricsInfo.h"
 #include "OutputParams.h"
+#include "xml.h"
 #include "output_xhtml.h"
 #include "Paragraph.h"
 #include "ParagraphParameters.h"
@@ -3498,154 +3499,136 @@ void Tabular::latex(otexstream & os, OutputParams const & runparams) const
 }
 
 
-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;
 }
 
 
@@ -3728,20 +3711,22 @@ docstring Tabular::xhtml(XMLStream & xs, OutputParams const & runparams) const
                        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);
@@ -3750,7 +3735,8 @@ docstring Tabular::xhtml(XMLStream & xs, OutputParams const & runparams) const
        // 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)) &&
@@ -3758,7 +3744,8 @@ docstring Tabular::xhtml(XMLStream & xs, OutputParams const & runparams) const
                                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);
@@ -3773,21 +3760,22 @@ docstring Tabular::xhtml(XMLStream & xs, OutputParams const & runparams) const
                                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;
 }
 
@@ -4175,6 +4163,12 @@ docstring InsetTableCell::xhtml(XMLStream & xs, OutputParams const & rp) const
 }
 
 
+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());
@@ -6010,30 +6004,9 @@ int InsetTabular::plaintext(odocstringstream & os,
 }
 
 
-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);
 }
 
 
index 0e76bdd9aeb0465128c851eb9ba67ff690a790b0..ce684ec9ead375cfe6f4c062483396805496d7ea 100644 (file)
@@ -74,6 +74,8 @@ public:
        ///
        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;
        ///
@@ -571,7 +573,7 @@ public:
        ///
        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;
        ///
@@ -906,7 +908,8 @@ public:
                                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;
@@ -979,7 +982,7 @@ public:
        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;
        ///
index aeb4c43d6ba9192285fa9b2bd2ac490da1781dcf..7f881f708acc39b94ba08107fb19d003339ed55d 100644 (file)
 #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"
@@ -589,20 +588,52 @@ int InsetText::plaintext(odocstringstream & os,
 }
 
 
-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());
 }
 
 
index 59f1202a24235bf324784c811ee9629f1ec64f29..d86bfb75d339456def755b311329633fed9f575a 100644 (file)
@@ -81,8 +81,6 @@ public:
        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 {
@@ -96,6 +94,10 @@ public:
        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
index 24ea9aa63659b8c2a7fbbbf43789dd96dab637e8..a6092cb8a337ba8f5098a4e143f5cece21a37521 100644 (file)
@@ -23,7 +23,7 @@
 #include "Lexer.h"
 #include "MetricsInfo.h"
 #include "OutputParams.h"
-#include "output_xhtml.h"
+#include "xml.h"
 #include "texstream.h"
 #include "Text.h"
 
@@ -224,10 +224,9 @@ int InsetVSpace::plaintext(odocstringstream & os,
 }
 
 
-int InsetVSpace::docbook(odocstream & os, OutputParams const &) const
+void InsetVSpace::docbook(XMLStream & xs, OutputParams const &) const
 {
-       os << '\n';
-       return 1;
+       xs << xml::CR();
 }
 
 
index 08e8ea882ec922051e7f95b1f2d357ff0caca58f..6e307561d52f20b7e0adf70a27e613779829a860 100644 (file)
@@ -50,7 +50,7 @@ private:
        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
index 4460f99c9b2d2ef441a3bfe96afc925324194ced..b05d5dba4790bf9634652b2992d10814259176a7 100644 (file)
@@ -26,7 +26,7 @@
 #include "FuncStatus.h"
 #include "LaTeXFeatures.h"
 #include "Lexer.h"
-#include "output_xhtml.h"
+#include "xml.h"
 #include "texstream.h"
 #include "TextClass.h"
 
@@ -207,13 +207,13 @@ int InsetWrap::plaintext(odocstringstream & os,
 }
 
 
-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());
 }
 
 
index 8df38e97f5de5f5e4040e042abde2ada0b235ea4..934e3bd101e3a1e03440b218ae1c7bee24f3752c 100644 (file)
@@ -72,7 +72,7 @@ private:
        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;
        ///
index d9f314b3c7a15acc2a212e29bb45cc0e261ab9dc..9800866d6b9c3961dffe6c4d31aaeeb4c19fd5ed 100644 (file)
@@ -38,7 +38,7 @@
 #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"
@@ -2404,13 +2404,12 @@ int InsetMathHull::plaintext(odocstringstream & os,
 }
 
 
-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;
@@ -2419,6 +2418,8 @@ int InsetMathHull::docbook(odocstream & os, OutputParams const & runparams) cons
        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()) {
@@ -2429,44 +2430,44 @@ int InsetMathHull::docbook(odocstream & os, OutputParams const & runparams) cons
 
        ++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()), "&", "&amp;"), "<", "&lt;"));
-               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()), "&", "&amp;"), "<", "&lt;"));
-               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()), "&", "&amp;"), "<", "&lt;"));
+       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();
 }
 
 
index 96dbc10be604fecc48df3a08932809ea322ec3d4..42144a3f4bfebbd07bbd210d60b766b5f092478a 100644 (file)
@@ -144,7 +144,7 @@ public:
        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;
        ///
index 3e1bc0290ae93e4885de32ff02b0cb73a135a32e..99058d776f2a2668691e4b2af1388736ee981b7d 100644 (file)
@@ -182,24 +182,18 @@ void InsetMathRef::validate(LaTeXFeatures & features) 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;
 }
 
 
index cdcbb1cc152c81f10f390c7135c57451abb53c2c..1f5e0b8bb8327d8aee6388d3470635918faf5cb7 100644 (file)
@@ -48,7 +48,7 @@ public:
        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;
 
index 45af41bece27ff56baef5ef6ca1d504c92e112ce..b80aaddf9b34903d697ea64d2112ed593bef40f5 100644 (file)
 
 #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;
@@ -80,335 +86,896 @@ std::string const fontToDocBookTag(xml::FontTypes type) {
     }
 }
 
-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 &paragraphs, 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 &paragraphs, 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 &paragraphs, 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 &paragraphs,
+                                                   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 &paragraphs,
+                       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 &paragraphs = 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 &paragraphs = 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 &paragraphs = 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
index 819c2035e4b04f6d800fc1ee33175b5c7144282f..4dcc853df28baa340f66e18841c0809fe5cf13ce 100644 (file)
@@ -6,6 +6,8 @@
  *
  * \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
 
index 2c30f1548dfd8fbc9802fe7e8f370781ba2cedfd..7733bf0260d7d944be9b012dd6cd266edc097b27 100644 (file)
@@ -24,6 +24,7 @@
 
 #include "support/convert.h"
 #include "support/docstream.h"
+#include "support/lassert.h"
 #include "support/lstrings.h"
 #include "support/textutils.h"
 
@@ -31,7 +32,6 @@
 #include <map>
 #include <functional>
 #include <QThreadStorage>
-#include <support/lassert.h>
 
 using namespace std;
 using namespace lyx::support;
@@ -351,37 +351,44 @@ XMLStream &XMLStream::operator<<(xml::FontTag const &tag) {
 
 
 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;
 }
 
index b4339ba7ffaa9782aec8ff7270f6029a8f995e91..c7f1e5076e2a5b3cde5601b45c828976df72d795 100644 (file)
--- a/src/xml.h
+++ b/src/xml.h
@@ -91,15 +91,15 @@ public:
     /// 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;
     ///
@@ -183,7 +183,7 @@ struct StartTag
     /// </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_; }
@@ -303,8 +303,12 @@ struct FontTag : public StartTag
     ///
     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;
@@ -319,6 +323,8 @@ struct EndFontTag : public EndTag
     ///
     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_;