diff --git a/lib/scripts/layout2layout.py b/lib/scripts/layout2layout.py index 29159b0e50..b2393ea276 100644 --- a/lib/scripts/layout2layout.py +++ b/lib/scripts/layout2layout.py @@ -11,7 +11,7 @@ # This script will update a .layout file to current format # The latest layout format is also defined in src/TextClass.cpp -currentFormat = 87 +currentFormat = 88 # Incremented to format 4, 6 April 2007, lasgouttes @@ -296,7 +296,10 @@ currentFormat = 87 # Incremented to format 86, 20 October 2020 by tcuvelier # New tag DocBookSection. -# Incremeted to format 87, 2 November 2020 by rkh +# Incremented to format 87, 2 November 2020 by rkh + +# Incremented to format 88, 28 November 2020 by tcuvelier +# New tag DocBookNotInPara. # Do not forget to document format change in Customization # Manual (section "Declaring a new text class"). @@ -358,6 +361,7 @@ def concatenate_label(old, new): else: return b'"' + old + new + b'"' + # appends a string to a list unless it's already there def addstring(s, l): if l.count(s) > 0: @@ -547,6 +551,11 @@ def convert(lines, end_format): i += 1 continue + if 87 <= format <= 88: + # nothing to do. + i += 1 + continue + if format == 86: if lines[i].lstrip().lower().startswith(b"stepmastercounter"): pattern = re.compile(b"stepmastercounter", re.IGNORECASE) diff --git a/src/Layout.h b/src/Layout.h index cb8f537cbb..dfb5afebda 100644 --- a/src/Layout.h +++ b/src/Layout.h @@ -115,6 +115,7 @@ public: docstring docbooktag; docstring docbooktagtype; docstring docbookattr; + bool docbookargumentbeforemaintag = false; }; /// typedef std::map LaTeXArgMap; diff --git a/src/OutputParams.h b/src/OutputParams.h index 7e0a4b2373..1cd16d2ee3 100644 --- a/src/OutputParams.h +++ b/src/OutputParams.h @@ -26,6 +26,7 @@ class Encoding; class ExportData; class Font; class Language; +class InsetArgument; class OutputParams { public: @@ -382,6 +383,9 @@ public: /// Should wrappers be ignored? Mostly useful to avoid generation of . bool docbook_ignore_wrapper = false; + /// Some parameters are output before the rest of the paragraph, they should not be generated a second time. + std::set docbook_prepended_arguments = {}; + /// Are we generating this material for inclusion in a TOC-like entity? bool for_toc = false; diff --git a/src/Paragraph.cpp b/src/Paragraph.cpp index aa6bd30304..1e92259158 100644 --- a/src/Paragraph.cpp +++ b/src/Paragraph.cpp @@ -865,6 +865,7 @@ int Paragraph::eraseChars(pos_type start, pos_type end, bool trackChanges) return end - i; } + // Handle combining characters int Paragraph::Private::latexSurrogatePair(BufferParams const & bparams, otexstream & os, char_type c, char_type next, diff --git a/src/TextClass.cpp b/src/TextClass.cpp index cd87cca2e4..a58ae6dacd 100644 --- a/src/TextClass.cpp +++ b/src/TextClass.cpp @@ -59,7 +59,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 = 87; // rkh: master --> parent for counters +int const LAYOUT_FORMAT = 88; // tcuvelier: add DocBookNotInPara // Layout format for the current lyx file format. Controls which format is diff --git a/src/insets/InsetArgument.cpp b/src/insets/InsetArgument.cpp index 8f1d762fa3..2ae80ca9cf 100644 --- a/src/insets/InsetArgument.cpp +++ b/src/insets/InsetArgument.cpp @@ -126,6 +126,7 @@ void InsetArgument::updateBuffer(ParIterator const & it, UpdateType utype, bool docbooktag_ = (*lait).second.docbooktag; docbooktagtype_ = (*lait).second.docbooktagtype; docbookattr_ = (*lait).second.docbookattr; + docbookargumentbeforemaintag_ = (*lait).second.docbookargumentbeforemaintag; pass_thru_local_ = false; if (lait->second.is_toc_caption) { is_toc_caption_ = true; @@ -312,6 +313,10 @@ InsetLayout::InsetDecoration InsetArgument::decoration() const void InsetArgument::docbook(XMLStream & xs, OutputParams const & rp) const { + // Ignore arguments that have already been output. + if (rp.docbook_prepended_arguments.find(this) != rp.docbook_prepended_arguments.end()) + return; + if (docbooktag_ != from_ascii("NONE") && docbooktag_ != from_ascii("IGNORE")) { // TODO: implement docbooktagtype_. xs << xml::StartTag(docbooktag_, docbookattr_); diff --git a/src/insets/InsetArgument.h b/src/insets/InsetArgument.h index d5d54a6a5b..1c63e5ccb3 100644 --- a/src/insets/InsetArgument.h +++ b/src/insets/InsetArgument.h @@ -39,6 +39,8 @@ public: std::string name() const { return name_; } + bool docbookargumentbeforemaintag() const { return docbookargumentbeforemaintag_; } + /// \name Public functions inherited from Inset class //@{ /// @@ -131,6 +133,8 @@ private: docstring docbooktagtype_; /// DocBook attributes. docstring docbookattr_; + /// + bool docbookargumentbeforemaintag_ = false; protected: /// \name Protected functions inherited from Inset class diff --git a/src/insets/InsetLayout.cpp b/src/insets/InsetLayout.cpp index 62030d063f..5b5308ddcf 100644 --- a/src/insets/InsetLayout.cpp +++ b/src/insets/InsetLayout.cpp @@ -95,6 +95,8 @@ bool InsetLayout::read(Lexer & lex, TextClass const & tclass, IL_DOCBOOKTAGTYPE, IL_DOCBOOKSECTION, IL_DOCBOOKININFO, + IL_DOCBOOKARGUMENTBEFOREMAINTAG, + IL_DOCBOOKNOTINPARA, IL_DOCBOOKWRAPPERTAG, IL_DOCBOOKWRAPPERTAGTYPE, IL_DOCBOOKWRAPPERATTR, @@ -148,6 +150,7 @@ bool InsetLayout::read(Lexer & lex, TextClass const & tclass, { "custompars", IL_CUSTOMPARS }, { "decoration", IL_DECORATION }, { "display", IL_DISPLAY }, + { "docbookargumentbeforemaintag", IL_DOCBOOKARGUMENTBEFOREMAINTAG }, { "docbookattr", IL_DOCBOOKATTR }, { "docbookininfo", IL_DOCBOOKININFO }, { "docbookitemattr", IL_DOCBOOKITEMATTR }, @@ -156,6 +159,7 @@ bool InsetLayout::read(Lexer & lex, TextClass const & tclass, { "docbookitemwrapperattr", IL_DOCBOOKITEMWRAPPERATTR }, { "docbookitemwrappertag", IL_DOCBOOKITEMWRAPPERTAG }, { "docbookitemwrappertagtype", IL_DOCBOOKITEMWRAPPERTAGTYPE }, + { "docbooknotinpara", IL_DOCBOOKNOTINPARA }, { "docbooksection", IL_DOCBOOKSECTION }, { "docbooktag", IL_DOCBOOKTAG }, { "docbooktagtype", IL_DOCBOOKTAGTYPE }, @@ -514,6 +518,12 @@ bool InsetLayout::read(Lexer & lex, TextClass const & tclass, case IL_DOCBOOKININFO: lex >> docbookininfo_; break; + case IL_DOCBOOKARGUMENTBEFOREMAINTAG: + lex >> docbookargumentbeforemaintag_; + break; + case IL_DOCBOOKNOTINPARA: + lex >> docbooknotinpara_; + break; case IL_DOCBOOKSECTION: lex >> docbooksection_; break; @@ -819,6 +829,9 @@ void InsetLayout::readArgument(Lexer & lex) } else if (tok == "docbooktagtype") { lex.next(); arg.docbooktagtype = lex.getDocString(); + } else if (tok == "docbookargumentbeforemaintag") { + lex.next(); + arg.docbookargumentbeforemaintag = lex.getBool(); } else { lex.printError("Unknown tag"); error = true; diff --git a/src/insets/InsetLayout.h b/src/insets/InsetLayout.h index 09abe71e13..88143d5faa 100644 --- a/src/insets/InsetLayout.h +++ b/src/insets/InsetLayout.h @@ -158,6 +158,10 @@ public: /// bool docbooksection() const { return docbooksection_; } /// + bool docbooknotinpara() const { return docbooknotinpara_; } + /// + bool docbookargumentbeforemaintag() const { return docbookargumentbeforemaintag_; } + /// std::string docbookwrappertag() const { return docbookwrappertag_; } /// std::string docbookwrappertagtype() const; @@ -311,6 +315,10 @@ private: /// mutable std::string docbookininfo_; /// + bool docbooknotinpara_ = false; + /// + bool docbookargumentbeforemaintag_ = false; + /// bool docbooksection_ = false; /// std::string docbookwrappertag_; diff --git a/src/insets/InsetText.cpp b/src/insets/InsetText.cpp index 2c757a9ca8..bf1fe62ea9 100644 --- a/src/insets/InsetText.cpp +++ b/src/insets/InsetText.cpp @@ -621,8 +621,35 @@ void InsetText::docbook(XMLStream & xs, OutputParams const & rp, XHTMLOptions op if (!rp.docbook_generate_info && il.docbookininfo() != "never") return; + // In some cases, the input parameters must be overridden for outer tags. + bool writeOuterTag = opts & WriteOuterTag; + if (writeOuterTag) { + // For each paragraph, if there are only Bibitems and the corresponding text, don't write the outer tags. + bool allBibitems = std::all_of(text().paragraphs().begin(), text().paragraphs().end(), [](Paragraph const & par) { + auto nInsets = std::distance(par.insetList().begin(), par.insetList().end()); + auto parSize = (size_t) par.size(); + return nInsets == 1 && parSize > 1 && par.insetList().begin()->inset->lyxCode() == BIBITEM_CODE; + }); + writeOuterTag = !allBibitems; + } + + // Detect arguments that should be output before the paragraph. + // Don't reuse runparams.docbook_prepended_arguments, as the same object is used in InsetArgument to determine + // whether the inset should be output or not, whatever the context (i.e. position with respect to the wrapper). + std::set prependedArguments; + for (auto const & par : paragraphs()) { + for (pos_type i = 0; i < par.size(); ++i) { + if (par.getInset(i) && par.getInset(i)->lyxCode() == ARG_CODE) { + InsetArgument const *arg = par.getInset(i)->asInsetArgument(); + if (arg->docbookargumentbeforemaintag()) + prependedArguments.insert(par.getInset(i)->asInsetArgument()); + } + } + } + // Start outputting this inset. - if (opts & WriteOuterTag) { + // - First, wrapper around the inset and its main tag. + if (writeOuterTag) { if (!il.docbookwrappertag().empty() && il.docbookwrappertag() != "NONE" && il.docbookwrappertag() != "IGNORE") xml::openTag(xs, il.docbookwrappertag(), il.docbookwrapperattr(), il.docbookwrappertagtype()); @@ -634,7 +661,19 @@ void InsetText::docbook(XMLStream & xs, OutputParams const & rp, XHTMLOptions op attrs += from_ascii(" xlink:href=\"") + text_.asString() + from_ascii("\""); xml::openTag(xs, il.docbooktag(), attrs, il.docbooktagtype()); } + } + // - Think about the arguments. + OutputParams np = runparams; + np.docbook_in_par = true; + for (auto const & arg : prependedArguments) + arg->docbook(xs, np); + + // - Mark the newly generated arguments are not-to-be-generated-again. + runparams.docbook_prepended_arguments = std::move(prependedArguments); + + // - Deal with the first item. + if (writeOuterTag) { if (!il.docbookitemwrappertag().empty() && il.docbookitemwrappertag() != "NONE" && il.docbookitemwrappertag() != "IGNORE") xml::openTag(xs, il.docbookitemwrappertag(), il.docbookitemwrapperattr(), il.docbookitemwrappertagtype()); @@ -650,11 +689,13 @@ void InsetText::docbook(XMLStream & xs, OutputParams const & rp, XHTMLOptions op if (il.isPassThru()) runparams.pass_thru = true; + // - Write the main content of the inset. xs.startDivision(false); docbookParagraphs(text_, buffer(), xs, runparams); xs.endDivision(); - if (opts & WriteOuterTag) { + // - Close the required tags. + if (writeOuterTag) { if (!il.docbookitemtag().empty() && il.docbookitemtag() != "NONE" && il.docbookitemtag() != "IGNORE") xml::closeTag(xs, il.docbookitemtag(), il.docbookitemtagtype()); diff --git a/src/output_docbook.cpp b/src/output_docbook.cpp index d815abc815..aaa3860a77 100644 --- a/src/output_docbook.cpp +++ b/src/output_docbook.cpp @@ -347,7 +347,7 @@ void makeParagraph( special_case = true; } - size_t nInsets = std::distance(par->insetList().begin(), par->insetList().end()); + auto nInsets = std::distance(par->insetList().begin(), par->insetList().end()); auto parSize = (size_t) par->size(); // Plain layouts must be ignored. @@ -377,6 +377,40 @@ void makeParagraph( }; special_case |= nInsets == parSize && std::all_of(par->insetList().begin(), par->insetList().end(), isLyxCodeSpecialCase); + // Flex elements (InsetLayout) have their own parameter to control the special case. + auto isFlexSpecialCase = [](InsetList::Element inset) { + if (inset.inset->lyxCode() != FLEX_CODE) + return false; + // Standard condition: check the parameter. + if (inset.inset->getLayout().docbooknotinpara()) + return true; + + // If the parameter is not set, maybe the flex inset only contains things that should match the standard + // condition. In this case, isLyxCodeSpecialCase must also check for bibitems... + auto isLyxCodeSpecialCase = [](InsetList::Element inset) { + return lyxCodeSpecialCases.find(inset.inset->lyxCode()) != lyxCodeSpecialCases.end() || + inset.inset->lyxCode() == BIBITEM_CODE; + }; + if (InsetText * text = inset.inset->asInsetText()) { + for (auto const & par : text->paragraphs()) { + auto nInsets = std::distance(par.insetList().begin(), par.insetList().end()); + auto parSize = (size_t) par.size(); + + if (nInsets == 1 && par.insetList().begin()->inset->lyxCode() == BIBITEM_CODE) + return true; + if (nInsets != parSize) + return false; + if (!std::all_of(par.insetList().begin(), par.insetList().end(), isLyxCodeSpecialCase)) + return false; + } + return true; + } + + // No case matched: give up. + return false; + }; + special_case |= nInsets == parSize && std::all_of(par->insetList().begin(), par->insetList().end(), isFlexSpecialCase); + // Open a paragraph if it is allowed, we are not already within a paragraph, and the insets in the paragraph do // not forbid paragraphs (aka special cases). bool const open_par = runparams.docbook_make_pars