#include "Buffer.h"
#include "buffer_funcs.h"
#include "BufferParams.h"
-#include "Counters.h"
#include "Font.h"
#include "InsetList.h"
#include "Layout.h"
#include "TextClass.h"
#include "insets/InsetBibtex.h"
+#include "insets/InsetBibitem.h"
#include "insets/InsetLabel.h"
#include "insets/InsetNote.h"
#include "support/lstrings.h"
#include "support/textutils.h"
+#include "support/regex.h"
+
#include <stack>
#include <iostream>
#include <algorithm>
+#include <sstream>
using namespace std;
using namespace lyx::support;
namespace {
-std::string const fontToDocBookTag(xml::FontTypes type) {
- switch (type) {
- case xml::FontTypes::FT_EMPH:
- case xml::FontTypes::FT_BOLD:
- return "emphasis";
- case xml::FontTypes::FT_NOUN:
- return "person";
- case xml::FontTypes::FT_UBAR:
- case xml::FontTypes::FT_WAVE:
- case xml::FontTypes::FT_DBAR:
- case xml::FontTypes::FT_SOUT:
- case xml::FontTypes::FT_XOUT:
- case xml::FontTypes::FT_ITALIC:
- case xml::FontTypes::FT_UPRIGHT:
- case xml::FontTypes::FT_SLANTED:
- case xml::FontTypes::FT_SMALLCAPS:
- case xml::FontTypes::FT_ROMAN:
- case xml::FontTypes::FT_SANS:
- return "emphasis";
- case xml::FontTypes::FT_TYPE:
- return "code";
- case xml::FontTypes::FT_SIZE_TINY:
- case xml::FontTypes::FT_SIZE_SCRIPT:
- case xml::FontTypes::FT_SIZE_FOOTNOTE:
- case xml::FontTypes::FT_SIZE_SMALL:
- case xml::FontTypes::FT_SIZE_NORMAL:
- case xml::FontTypes::FT_SIZE_LARGE:
- case xml::FontTypes::FT_SIZE_LARGER:
- case xml::FontTypes::FT_SIZE_LARGEST:
- case xml::FontTypes::FT_SIZE_HUGE:
- case xml::FontTypes::FT_SIZE_HUGER:
- case xml::FontTypes::FT_SIZE_INCREASE:
- case xml::FontTypes::FT_SIZE_DECREASE:
- return "emphasis";
- default:
- return "";
- }
+std::string const fontToDocBookTag(xml::FontTypes type)
+{
+ switch (type) {
+ case xml::FontTypes::FT_EMPH:
+ case xml::FontTypes::FT_BOLD:
+ return "emphasis";
+ case xml::FontTypes::FT_NOUN:
+ return "person";
+ case xml::FontTypes::FT_UBAR:
+ case xml::FontTypes::FT_WAVE:
+ case xml::FontTypes::FT_DBAR:
+ case xml::FontTypes::FT_SOUT:
+ case xml::FontTypes::FT_XOUT:
+ case xml::FontTypes::FT_ITALIC:
+ case xml::FontTypes::FT_UPRIGHT:
+ case xml::FontTypes::FT_SLANTED:
+ case xml::FontTypes::FT_SMALLCAPS:
+ case xml::FontTypes::FT_ROMAN:
+ case xml::FontTypes::FT_SANS:
+ return "emphasis";
+ case xml::FontTypes::FT_TYPE:
+ return "code";
+ case xml::FontTypes::FT_SIZE_TINY:
+ case xml::FontTypes::FT_SIZE_SCRIPT:
+ case xml::FontTypes::FT_SIZE_FOOTNOTE:
+ case xml::FontTypes::FT_SIZE_SMALL:
+ case xml::FontTypes::FT_SIZE_NORMAL:
+ case xml::FontTypes::FT_SIZE_LARGE:
+ case xml::FontTypes::FT_SIZE_LARGER:
+ case xml::FontTypes::FT_SIZE_LARGEST:
+ case xml::FontTypes::FT_SIZE_HUGE:
+ case xml::FontTypes::FT_SIZE_HUGER:
+ case xml::FontTypes::FT_SIZE_INCREASE:
+ case xml::FontTypes::FT_SIZE_DECREASE:
+ return "emphasis";
+ default:
+ return "";
+ }
}
-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.
-
- 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 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>
+ case xml::FontTypes::FT_UBAR:
+ return "underline";
+
+ // All other roles are non-standard for DocBook.
+
+ 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 "";
- }
+ // 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 "";
+ }
}
} // end anonymous namespace
xml::FontTag docbookStartFontTag(xml::FontTypes type)
{
- return xml::FontTag(from_utf8(fontToDocBookTag(type)), from_utf8(fontToAttribute(type)), type);
+ return xml::FontTag(from_utf8(fontToDocBookTag(type)), from_utf8(fontToAttribute(type)), type);
}
xml::EndFontTag docbookEndFontTag(xml::FontTypes type)
{
- return xml::EndFontTag(from_utf8(fontToDocBookTag(type)), type);
+ return xml::EndFontTag(from_utf8(fontToDocBookTag(type)), type);
}
// convenience functions
-void openParTag(XMLStream &xs, Layout const &lay) {
- if (lay.docbookwrappertag() != "NONE") {
- xs << xml::StartTag(lay.docbookwrappertag(), lay.docbookwrapperattr());
- }
+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";
+ string tag = lay.docbooktag();
+ if (tag == "Plain Layout")
+ tag = "para";
- xs << xml::ParTag(tag, lay.docbookattr());
+ xs << xml::ParTag(tag, lay.docbookattr());
}
-void closeTag(XMLStream &xs, Layout const &lay) {
- string tag = lay.docbooktag();
- if (tag == "Plain Layout")
- tag = "para";
+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());
+ xs << xml::EndTag(tag);
+ if (lay.docbookwrappertag() != "NONE")
+ xs << xml::EndTag(lay.docbookwrappertag());
}
void openLabelTag(XMLStream & xs, Layout const & lay) // Mostly for definition lists.
{
- xs << xml::StartTag(lay.docbookitemlabeltag(), lay.docbookitemlabelattr());
+ xs << xml::StartTag(lay.docbookitemlabeltag(), lay.docbookitemlabelattr());
}
void closeLabelTag(XMLStream & xs, Layout const & lay)
{
- xs << xml::EndTag(lay.docbookitemlabeltag());
- xs << xml::CR();
+ xs << xml::EndTag(lay.docbookitemlabeltag());
+ xs << xml::CR();
}
-void openItemTag(XMLStream &xs, Layout const &lay) {
+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;
+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) {
+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();
+inline void closeItemTag(XMLStream &xs, Layout const &lay)
+{
+ xs << xml::EndTag(lay.docbookitemtag());
+ xs << 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);
+ ParagraphList::const_iterator p,
+ ParagraphList::const_iterator const & pend) {
+ for (++p; p != pend && p->layout().latextype == LATEX_PARAGRAPH; ++p);
+
+ return p;
+}
- return p;
+ParagraphList::const_iterator findLastBibliographyParagraph(
+ ParagraphList::const_iterator p,
+ ParagraphList::const_iterator const & pend) {
+ for (++p; p != pend && p->layout().latextype == LATEX_BIB_ENVIRONMENT; ++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 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 makeParagraphBibliography(
+ Buffer const &buf,
+ XMLStream &xs,
+ OutputParams const &runparams,
+ Text const &text,
+ ParagraphList::const_iterator const & pbegin,
+ ParagraphList::const_iterator const & pend)
+{
+ auto const begin = text.paragraphs().begin();
+ auto const end = text.paragraphs().end();
+
+ // Find the paragraph *before* pbegin.
+ ParagraphList::const_iterator pbegin_before = begin;
+ if (pbegin != begin) {
+ ParagraphList::const_iterator pbegin_before_next = begin;
+ ++pbegin_before_next;
+
+ while (pbegin_before_next != pbegin) {
+ ++pbegin_before;
+ ++pbegin_before_next;
+ }
+ }
+
+ ParagraphList::const_iterator par = pbegin;
+
+ // If this is the first paragraph in a bibliography, open the bibliography tag.
+ if (pbegin != begin && pbegin_before->layout().latextype != LATEX_BIB_ENVIRONMENT) {
+ xs << xml::StartTag("bibliography");
+ xs << xml::CR();
+ }
+
+ // Generate the required paragraphs, but only if they are .
+ for (; par != pend; ++par) {
+ // Start the precooked bibliography entry. This is very much like opening a paragraph tag.
+ // Don't forget the citation ID!
+ docstring attr;
+ for (auto i = 0; i < par->size(); ++i) {
+ Inset const *ip = par->getInset(0);
+ if (ip != nullptr && ip->lyxCode() == BIBITEM_CODE) {
+ const auto * bibitem = dynamic_cast<const InsetBibitem*>(par->getInset(i));
+ attr = from_utf8("xml:id='") + bibitem->getParam("key") + from_utf8("'");
+ break;
+ }
+ }
+ xs << xml::StartTag(from_utf8("bibliomixed"), attr);
+
+ // Generate the entry.
+ par->simpleDocBookOnePar(buf, xs, runparams, text.outerFont(distance(begin, par)), true, true, 0);
+
+ // End the precooked bibliography entry.
+ xs << xml::EndTag("bibliomixed");
+ xs << xml::CR();
+ }
+
+ // If this is the last paragraph in a bibliography, close the bibliography tag.
+ if (par == end || par->layout().latextype != LATEX_BIB_ENVIRONMENT) {
+ xs << xml::EndTag("bibliography");
+ xs << xml::CR();
+ }
+
+ 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;
+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();
+
+ // 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_utf8(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));
+
+ // Determine if this paragraph has some real content. Things like new pages are not caught
+ // by Paragraph::empty(), even though they do not generate anything useful in DocBook.
+ odocstringstream os2;
+ XMLStream xs2(os2);
+ par->simpleDocBookOnePar(buf, xs2, runparams, text.outerFont(distance(begin, par)), open_par, close_par, 0);
+
+ docstring cleaned = os2.str();
+ static const lyx::regex reg("[ \\r\\n]*");
+ cleaned = from_utf8(lyx::regex_replace(to_utf8(cleaned), reg, string("")));
+
+ if (!cleaned.empty()) {
+ if (open_par)
+ openParTag(xs, lay);
+
+ xs << XMLStream::ESCAPE_NONE << os2.str();
+
+ 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;
+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 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();
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;
// Actual content of this paragraph.
LATTEST(bstyle == style);
if (lastlay != nullptr) {
closeItemTag(xs, *lastlay);
+ if (lastlay->docbookitemwrappertag() != "NONE") {
+ xs << xml::EndTag(lastlay->docbookitemwrappertag());
+ xs << xml::CR();
+ }
lastlay = nullptr;
}
- // this will be positive, if we want to skip the
+ // this will be positive if we want to skip the
// initial word (if it's been taken for the label).
pos_type sep = 0;
openLabelTag(xs, style);
xs << lbl;
closeLabelTag(xs, style);
+ } else {
+ // No new line after closeLabelTag.
+ xs << xml::CR();
}
- xs << xml::CR();
}
} else { // some kind of list
if (style.labeltype == LABEL_MANUAL) {
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
+ // Start generating the item.
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);
+ // Maybe the item is completely empty, i.e. if the first word ends at the end of the current paragraph
+ // AND if the next paragraph doesn't have the same depth (if there is such a paragraph).
+ // Common case: there is only the first word on the line, but there is a nested list instead
+ // of more text.
+ bool emptyItem = false;
+ if (sep == par->size()) {
+ auto next_par = par;
+ ++next_par;
+ if (next_par == text.paragraphs().end()) // There is no next paragraph.
+ emptyItem = true;
+ else // There is a next paragraph: check depth.
+ emptyItem = par->params().depth() >= next_par->params().depth();
+ }
+
+ if (emptyItem) {
+ // Avoid having an empty item, this is not valid DocBook. A single character is enough to force
+ // generation of a full <para>.
+ xs << ' ';
+ } else {
+ // Generate the rest of the paragraph, if need be.
+ par->simpleDocBookOnePar(buf, xs, rp, text.outerFont(distance(begin, par)), true, true, sep);
+ }
+
++par;
if (getsIntoParagraph)
closeInnerItemTag(xs, style);
}
}
}
- // The other possibility is that the depth has increased, in which
- // case we need to recurse.
+ // The other possibility is that the depth has increased.
else {
send = findEndOfEnvironment(par, pend);
par = makeEnvironment(buf, xs, runparams, text, par, send);
par = makeParagraphs(buf, xs, runparams, text, par, send);
break;
case LATEX_BIB_ENVIRONMENT:
- // Handled in InsetBibtex.
+ send = findLastBibliographyParagraph(par, pend);
+ par = makeParagraphBibliography(buf, xs, runparams, text, par, send);
break;
case LATEX_COMMAND:
++par;
}
}
- if (lastlay != 0)
+ if (lastlay != nullptr) {
closeItemTag(xs, *lastlay);
+ if (lastlay->docbookitemwrappertag() != "NONE") {
+ xs << xml::EndTag(lastlay->docbookitemwrappertag());
+ xs << xml::CR();
+ }
+ }
closeTag(xs, bstyle);
xs << xml::CR();
return pend;
}
-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);
+void makeCommand(
+ Buffer const & buf,
+ XMLStream & xs,
+ OutputParams const & runparams,
+ Text const & text,
+ ParagraphList::const_iterator const & pbegin)
+{
+ Layout const &style = pbegin->layout();
- // No need for labels, as they are handled by DocBook tags.
+ // No need for labels, as they are handled by DocBook tags.
- openParTag(xs, style);
+ openParTag(xs, style);
- ParagraphList::const_iterator const begin = text.paragraphs().begin();
- pbegin->simpleDocBookOnePar(buf, xs, runparams,
- text.outerFont(distance(begin, pbegin)));
- closeTag(xs, style);
- xs << xml::CR();
+ 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();
+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: {
break;
}
case LATEX_BIB_ENVIRONMENT: {
- // Handled in InsetBibtex.
+ send = findLastBibliographyParagraph(par, pend);
+ par = makeParagraphBibliography(buf, xs, ourparams, text, par, send);
break;
}
case LATEX_PARAGRAPH: {
pit_type cpit = bpit;
while (cpit < epit) {
- Layout const &style = paragraphs[cpit].layout();
+ // Skip paragraphs only containing one note.
+ Paragraph const &par = paragraphs[cpit];
+ if (par.size() == 1 && dynamic_cast<InsetNote*>(paragraphs[cpit].insetList().get(0))) {
+ cpit += 1;
+ continue;
+ }
+
+ // Based on layout information, store this paragraph in one set: should be in <info>, must be.
+ Layout const &style = par.layout();
+
if (style.docbookininfo() == "always") {
mustBeInInfo.emplace(cpit);
} else if (style.docbookininfo() == "maybe") {
}
cpit += 1;
}
- // Now, bpit points to the last paragraph that has things that could go in <info>.
+ // Now, cpit points to the last paragraph that has things that could go in <info>.
+ // bpit is still the beginning of the <info> part.
return make_tuple(shouldBeInInfo, mustBeInInfo, bpit, cpit);
}
}
-pit_type generateDocBookParagraphWithoutSectioning(Text const &text,
- Buffer const &buf,
- XMLStream &xs,
- OutputParams const &runparams,
- ParagraphList const ¶graphs,
- pit_type bpit,
- pit_type epit) {
+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;
while (bpit < epit) {
tie(par, send) = makeAny(text, buf, xs, runparams, par, send, pend);
bpit += distance(lastStartedPar, par);
+ lastStartedPar = par;
}
return bpit;
}
-void outputDocBookInfo(Text const &text,
- Buffer const &buf,
- XMLStream &xs,
- OutputParams const &runparams,
- ParagraphList const ¶graphs,
- DocBookInfoTag const &info,
- pit_type bpitAbstract,
- pit_type const epitAbstract) {
+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.
pit_type epitInfo;
tie(shouldBeInInfo, mustBeInInfo, bpitInfo, epitInfo) = info;
- // The abstract must go in <info>.
+ // Perform an additional check on the abstract. Sometimes, there are many paragraphs that should go
+ // into the abstract, but none generates actual content. Thus, first generate to a temporary stream,
+ // then only create the <abstract> tag if these paragraphs generate some content.
+ // This check must be performed *before* a decision on whether or not to output <info> is made.
bool hasAbstract = hasAbstractBetween(paragraphs, bpitAbstract, epitAbstract);
+ docstring abstract;
+ if (hasAbstract) {
+ odocstringstream os2;
+ XMLStream xs2(os2);
+ generateDocBookParagraphWithoutSectioning(text, buf, xs2, runparams, paragraphs, bpitAbstract, epitAbstract);
+
+ // Actually output the abstract if there is something to do. Don't count line feeds or spaces in this,
+ // even though they must be properly output if there is some abstract.
+ docstring abstractContent = os2.str();
+ static const lyx::regex reg("[ \\r\\n]*");
+ abstractContent = from_utf8(lyx::regex_replace(to_utf8(abstractContent), reg, string("")));
+
+ // Nothing? Then there is no abstract!
+ if (abstractContent.empty())
+ hasAbstract = false;
+ }
+
+ // The abstract must go in <info>.
bool needInfo = !mustBeInInfo.empty() || hasAbstract;
// Start the <info> tag if required.
// Output the elements that should go in <info>.
generateDocBookParagraphWithoutSectioning(text, buf, xs, runparams, paragraphs, bpitInfo, epitInfo);
- if (hasAbstract) {
+ if (hasAbstract && !abstract.empty()) { // The second test is probably superfluous.
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 << XMLStream::ESCAPE_NONE << abstract;
xs << xml::EndTag(tag);
xs << xml::CR();
}
}
-void docbookFirstParagraphs(Text const &text,
- Buffer const &buf,
- XMLStream &xs,
- OutputParams const &runparams,
- pit_type epit) {
+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.
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));
+ dynamic_cast<InsetNote *>(insets.get(0));
return hasParagraphOnlyNote;
}
-void docbookSimpleAllParagraphs(Text const &text,
- Buffer const &buf,
- XMLStream &xs,
- OutputParams const &runparams) {
+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.
void docbookParagraphs(Text const &text,
- Buffer const &buf,
- XMLStream &xs,
- OutputParams const &runparams) {
+ Buffer const &buf,
+ XMLStream &xs,
+ OutputParams const &runparams) {
ParagraphList const ¶graphs = text.paragraphs();
if (runparams.par_begin == runparams.par_end) {
- runparams.par_begin = 0;
- runparams.par_end = paragraphs.size();
+ 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;
- });
+ {
+ xs << XMLStream::ESCAPE_NONE << "<!-- DocBook output error! -->\n";
+ return;
+ });
ParagraphList::const_iterator const pend =
- (epit == (int) paragraphs.size()) ?
- paragraphs.end() : paragraphs.iterator_at(epit);
+ (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.
// Think about adding <section> and/or </section>s.
const bool isLayoutSectioning = style.category() == from_utf8("Sectioning");
if (isLayoutSectioning) {
- int level = style.toclevel;
+ 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) {
+ // 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));
+ // 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) {
+ // 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()) + "\"";
+ if (inset) {
+ if (auto label = dynamic_cast<InsetLabel const *>(inset)) {
+ // Generate the attributes for the section if need be.
+ id += "xml:id=\"" + xml::cleanID(label->screenLabel()) + "\"";
- // Don't output the ID as a DocBook <anchor>.
- ourparams.docbook_anchors_to_ignore.emplace(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;
+ // Cannot have multiple IDs per tag.
+ break;
+ }
}
}
tag += from_utf8(">");
xs << XMLStream::ESCAPE_NONE << tag;
xs << xml::CR();
- }
+ }
}
// Close all sections before the bibliography.