From 87dae26e4a8146c7e9b450dfff4d8711deeed933 Mon Sep 17 00:00:00 2001 From: Thibaut Cuvelier Date: Sun, 16 Aug 2020 00:59:43 +0200 Subject: [PATCH] Simplify code to generate only one paragraph at a time. --- autotests/export/docbook/basic.xml | 351 ++++++------ autotests/export/docbook/easy.lyx | 253 +++++++++ autotests/export/docbook/easy.xml | 68 +++ autotests/export/docbook/lists.lyx | 18 +- autotests/export/docbook/lists.xml | 48 +- lib/layouts/stdlayouts.inc | 10 +- lib/layouts/stdlists.inc | 4 +- lib/layouts/stdstarsections.inc | 4 +- lib/layouts/stdstruct.inc | 5 +- src/Paragraph.cpp | 9 +- src/ParagraphList.h | 11 + src/insets/InsetBibtex.cpp | 3 +- src/insets/InsetERT.cpp | 3 +- src/insets/InsetNewline.cpp | 21 +- src/mathed/InsetMathHull.cpp | 3 +- src/output_docbook.cpp | 855 +++++++++++------------------ src/xml.cpp | 45 +- 17 files changed, 928 insertions(+), 783 deletions(-) create mode 100644 autotests/export/docbook/easy.lyx create mode 100644 autotests/export/docbook/easy.xml diff --git a/autotests/export/docbook/basic.xml b/autotests/export/docbook/basic.xml index f208ae61da..6f4a828d4b 100644 --- a/autotests/export/docbook/basic.xml +++ b/autotests/export/docbook/basic.xml @@ -1,69 +1,62 @@ -
- -I am a title -I am an author +
+ +I am a title + +I am an author + I am a date - -I am an abstract + +I am an abstract I am also an abstract -I am a standard paragraph. -
+
I am the first section I am the first paragraph of the first section. - I am the second paragraph of the first section. - -
I am a quote - - - \,with\,a\,formula +
+I am a quote +\,with\,a\,formula + + - - with - a - formula + withaformula - - . + +.
- - - - Formula! + +Formula! + + Formula! - - - - - \text{I am a formula with a ref.}\label{eq:EQ.} + + +\text{I am a formula with a ref.}\label{eq:EQ.} + + I am a formula with a ref. - - - -See . - -Also, a formula with an user-defined macro that outputs well in LaTeX but cannot in MathML (hence replaced by picture): - - - \testmacro - - . - -
+ + +See . +Also, a formula with an user-defined macro that outputs well in LaTeX but cannot in MathML (hence replaced by picture): +\testmacro +MathML export failed. Please report this as a bug. +. + +
I am the first subsection I am the only paragraph of the first subsection.
@@ -72,174 +65,150 @@ I am the only paragraph of the second subsection.
-
+
I am the second section I am the only paragraph of the second section. Hyperlink. “Text between quotes.” - -See . +See .
I am the third section and I have fun with lists +First item. +
+Second line of the first item, after a line break.
+Second item. +Item has no order (1). +Item has no order (2). + +Word description + + + -First item. -Second line of the first item, after a line break. - - -Second item. - - - - -Item has no order (1). - - -Item has no order (2). - - - -Word - - - description - -Sentence - - - meaning - - +Sentence meaning + +
I am the fourth section and I deal with formatting. The following paragraph should be code. - I am some code. -I am a second line of code. I am no more code. - +I am a second line of code. +I am no more code. This line has inline code. This has typewriter fontI repeat that in a footnote. . - On the other hand, this footnote - has multiple - paragraphs. .
I am the fifth section and I deal with floats Now, three tables: - - - - - - +
I am a table caption below the table.
Table 1
+ + + + - + + - + + - - + +
I am a table caption below the table.
Table 1 Table 2 Table 3
Row 1
Row 1 Col 3, row 1
Row 2
Row 2 Col 3, row 2
- - - - - - - +
I am a table caption above the table.
Table 1
+ + + + - + + - + + - + +
I am a table caption above the table.
Table 1 Table 2 Table 3
Row 1
Row 1 Col 3, row 1
Row 2
Row 2 Col 3, row 2
- - - - -Table that has no caption 1 + + + +Table that has no caption 1 Table that has no caption 2 Table that has no caption 3 -Row 1 + +Row 1 Col 3, row 1 -Row 2 + +Row 2 Col 3, row 2 - + + - Then, one figure: - - -
Caption. - - - +
+Caption. + + + -
I am the sixth section and I really like bibliographies This text has references. First reference: . Second reference: . Both at the same time: , . A book: . - Many things, just testing for completeness: , , , , , .
I am the seventh section and I deal with indices First, a term to index: Term to index. Then a term to add to the second index: Term to add to the second index. - Then several terms for the first index: Termtoindex. - With a see: Termindex. With a see also: Termindex. - Several terms with a see: Termtoindexindex. Several terms with a see also: Termtoindexindex. - -A start of range: Term to index. The corresponding end of range: Term to index. - -Several terms with a start of range: Termtoindex. The corresponding end of range: Termtoindex. - -These terms already appeared before! Start of range: Termtoindex. The corresponding end of range: Termtoindex. +A start of range: Term to index. The corresponding end of range: Term to index. +Several terms with a start of range: Termtoindex. The corresponding end of range: Termtoindex. +These terms already appeared before! Start of range: Termtoindex. The corresponding end of range: Termtoindex.
I am the eight section and I deal with star sections -Star part +Star part Star section (sect1) Star subsection (sect2) Star subsubsection (sect3) Star paragraph (sect4) Star subparagraph (sect5)
- -References - -The title of the work4201-213article + +References + +The title of the work4201-213article 1993 - -The name of the journal + +The name of the journal - - -Peter + + +Peter Adams @@ -247,119 +216,119 @@ I am a second line of code. I am no more code. 2 An optional note - -The title of the work43book - -The name of the publisher + +The title of the work43book + +The name of the publisher
The address
1993 - - -Peter + + +Peter Babington An optional note
- -The title of the workbooklet + +The title of the workbooklet 1993 - - -Peter + + +Peter Caxton - -The title of the work213conference - -The publisher + +The title of the work213conference + +The publisher 1993 - - -Peter + + +Peter Draper - -The title of the work201-213inbook - -The name of the publisher + +The title of the work201-213inbook + +The name of the publisher 1993 - - -Peter + + +Peter Eston - -The title of the work43201-213incollection - -The name of the publisher + +The title of the work43201-213incollection + +The name of the publisher
The address of the publisher
1993 - -The title of the book + +The title of the book - - -Peter + + +Peter Farindon An optional note
- -A small paper-1article + +A small paper-1article 1997 - -The journal of small papers + +The journal of small papers - - -Freely + + +Freely I. P. - - -Ditto + + +Ditto Johannes to appear - -Éléments de géométrie algébriquebook + +Éléments de géométrie algébriquebook 1960 - - -Alexander + + +Alexander Grothendieck - -A big paperMCMXCVIIarticle + +A big paperMCMXCVIIarticle 7991 - -The journal of big papers + +The journal of big papers - - -Hugh + + +Hugh Jass diff --git a/autotests/export/docbook/easy.lyx b/autotests/export/docbook/easy.lyx new file mode 100644 index 0000000000..0ea08e7e65 --- /dev/null +++ b/autotests/export/docbook/easy.lyx @@ -0,0 +1,253 @@ +#LyX 2.4 created this file. For more info see https://www.lyx.org/ +\lyxformat 598 +\begin_document +\begin_header +\save_transient_properties true +\origin unavailable +\textclass article +\begin_preamble +\newcommand{\testmacro}{\ensuremath{\operatorname{testmacro}}} +\end_preamble +\use_default_options true +\maintain_unincluded_children no +\language english +\language_package default +\inputencoding auto-legacy +\fontencoding auto +\font_roman "default" "default" +\font_sans "default" "default" +\font_typewriter "default" "default" +\font_math "auto" "auto" +\font_default_family default +\use_non_tex_fonts false +\font_sc false +\font_roman_osf false +\font_sans_osf false +\font_typewriter_osf false +\font_sf_scale 100 100 +\font_tt_scale 100 100 +\use_microtype false +\use_dash_ligatures true +\graphics default +\default_output_format default +\output_sync 0 +\bibtex_command default +\index_command default +\paperfontsize default +\spacing single +\use_hyperref false +\papersize default +\use_geometry false +\use_package amsmath 1 +\use_package amssymb 1 +\use_package cancel 1 +\use_package esint 1 +\use_package mathdots 1 +\use_package mathtools 1 +\use_package mhchem 1 +\use_package stackrel 1 +\use_package stmaryrd 1 +\use_package undertilde 1 +\cite_engine basic +\cite_engine_type default +\biblio_style plain +\use_bibtopic false +\use_indices true +\paperorientation portrait +\suppress_date false +\justification true +\use_refstyle 1 +\use_minted 0 +\use_lineno 0 +\index Index +\shortcut idx +\color #008000 +\end_index +\index Other index +\shortcut oth +\color #cc0000 +\end_index +\secnumdepth 3 +\tocdepth 3 +\paragraph_separation indent +\paragraph_indentation default +\is_math_indent 0 +\math_numbering_side default +\quotes_style english +\dynamic_quotes 0 +\papercolumns 1 +\papersides 1 +\paperpagestyle default +\tablestyle default +\tracking_changes false +\output_changes false +\change_bars false +\postpone_fragile_content false +\html_math_output 0 +\html_css_as_file 0 +\html_be_strict false +\docbook_table_output 0 +\end_header + +\begin_body + +\begin_layout Title +I am a title +\end_layout + +\begin_layout Author +I am an author +\end_layout + +\begin_layout Date +I am a date +\end_layout + +\begin_layout Abstract +I am an abstract +\end_layout + +\begin_layout Abstract +I am also an abstract +\end_layout + +\begin_layout Section +I am the first section +\begin_inset CommandInset label +LatexCommand label +name "sec:Sec-1" + +\end_inset + + +\end_layout + +\begin_layout Standard +I am the first paragraph of the first section. + +\end_layout + +\begin_layout Standard +I am the second paragraph of the first section. + +\end_layout + +\begin_layout Quote +I am a quote +\begin_inset Formula $\,with\,a\,formula$ +\end_inset + +. + +\end_layout + +\begin_layout Standard +\begin_inset Formula +\[ +Formula! +\] + +\end_inset + + +\end_layout + +\begin_layout Subsection +I am the first subsection +\end_layout + +\begin_layout Standard +I am the only paragraph of the first subsection. + +\end_layout + +\begin_layout Subsection +I am the second subsection +\end_layout + +\begin_layout Standard +I am the only paragraph of the second subsection. + +\end_layout + +\begin_layout Section +I am the second section +\end_layout + +\begin_layout Standard +I am the only paragraph of the second section. + +\begin_inset CommandInset href +LatexCommand href +name "Hyperlink." +target "http://example.org" +literal "false" + +\end_inset + + +\begin_inset Quotes eld +\end_inset + +Text between quotes. +\begin_inset Quotes erd +\end_inset + + +\end_layout + +\begin_layout Standard +See +\begin_inset CommandInset ref +LatexCommand ref +reference "sec:Sec-1" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +. +\end_layout + +\begin_layout Section +I am the third section and I deal with star sections +\end_layout + +\begin_layout Part* +Star part +\end_layout + +\begin_layout Section* +Star section (sect1) +\end_layout + +\begin_layout Subsection* +Star subsection (sect2) +\end_layout + +\begin_layout Subsubsection* +Star subsubsection (sect3) +\end_layout + +\begin_layout Paragraph* +Star paragraph (sect4) +\end_layout + +\begin_layout Subparagraph* +Star subparagraph (sect5) +\end_layout + +\begin_layout Standard +\begin_inset CommandInset bibtex +LatexCommand bibtex +bibfiles "basic" +options "plain" + +\end_inset + + +\end_layout + +\end_body +\end_document diff --git a/autotests/export/docbook/easy.xml b/autotests/export/docbook/easy.xml new file mode 100644 index 0000000000..883c98226e --- /dev/null +++ b/autotests/export/docbook/easy.xml @@ -0,0 +1,68 @@ + + +
+ +I am a title + +I am an author + +I am a date + +I am an abstract +I am also an abstract + + +
+I am the first section +I am the first paragraph of the first section. +I am the second paragraph of the first section. +
+I am a quote +\,with\,a\,formula + + + + withaformula + + + +. +
+ +Formula! + + + + Formula! + + + +
+I am the first subsection +I am the only paragraph of the first subsection. +
+
+I am the second subsection +I am the only paragraph of the second subsection. +
+
+
+I am the second section +I am the only paragraph of the second section. Hyperlink. “Text between quotes.” +See . +
+
+I am the third section and I deal with star sections +Star part +Star section (sect1) +Star subsection (sect2) +Star subsubsection (sect3) +Star paragraph (sect4) +Star subparagraph (sect5) +
+ +References + + +
\ No newline at end of file diff --git a/autotests/export/docbook/lists.lyx b/autotests/export/docbook/lists.lyx index 4e46d709b2..f903372084 100644 --- a/autotests/export/docbook/lists.lyx +++ b/autotests/export/docbook/lists.lyx @@ -87,7 +87,23 @@ Test document \end_layout \begin_layout Standard -A list: +A simple list: +\end_layout + +\begin_layout Itemize +First item +\end_layout + +\begin_layout Itemize +Second item on two lines +\begin_inset Newline newline +\end_inset + +I'm the second line +\end_layout + +\begin_layout Standard +A complex list: \end_layout \begin_layout Itemize diff --git a/autotests/export/docbook/lists.xml b/autotests/export/docbook/lists.xml index a00ab4f633..4621e30937 100644 --- a/autotests/export/docbook/lists.xml +++ b/autotests/export/docbook/lists.xml @@ -2,33 +2,61 @@
-Test document -A list: + +Test document + + +A simple list: + First item + + +Second item on two lines + + +I'm the second line + + + + + + + + +A complex list: + +First item + + First first item First second item -Text after first item - + +Text after first item + + Second item - - + + + Second first item - + + Second second item -Text after second item - - + +Text after second item +
\ No newline at end of file diff --git a/lib/layouts/stdlayouts.inc b/lib/layouts/stdlayouts.inc index 3b10ff4143..16706956b7 100644 --- a/lib/layouts/stdlayouts.inc +++ b/lib/layouts/stdlayouts.inc @@ -28,8 +28,9 @@ Style Quotation AlignPossible Block, Left, Right, Center HTMLTag blockquote HTMLItem div - DocBookTag blockquote - DocBookItemTag para + DocBookWrapperTag blockquote + DocBookWrapperMergeWithPrevious true + DocBookTag para End @@ -50,8 +51,9 @@ Style Quote LabelType No_Label HTMLTag blockquote HTMLItem div - DocBookTag blockquote - DocBookItemTag para + DocBookWrapperTag blockquote + DocBookWrapperMergeWithPrevious true + DocBookTag para End diff --git a/lib/layouts/stdlists.inc b/lib/layouts/stdlists.inc index 0eef65c665..16a9e8188f 100644 --- a/lib/layouts/stdlists.inc +++ b/lib/layouts/stdlists.inc @@ -43,7 +43,9 @@ Style Itemize Color latex EndFont EndArgument - DocBookTag itemizedlist + DocBookWrapperTag itemizedlist + DocBookWrapperMergeWithPrevious true + DocBookTag NONE DocBookItemTag listitem DocBookItemInnerTag para End diff --git a/lib/layouts/stdstarsections.inc b/lib/layouts/stdstarsections.inc index 7d6865fe36..60354b1dd3 100644 --- a/lib/layouts/stdstarsections.inc +++ b/lib/layouts/stdstarsections.inc @@ -19,7 +19,7 @@ Style Part* LabelCounter "" ResetArgs 1 DocBookTag bridgehead - DocBookAttr renderas="part" + DocBookAttr "renderas='other' otherrenderas='part'" DocBookSectionTag NONE DocBookForceAbstractTag NONE End @@ -34,7 +34,7 @@ Style Chapter* LabelCounter "" ResetArgs 1 DocBookTag bridgehead - DocBookAttr renderas="chapter" + DocBookAttr "renderas='other' otherrenderas='chapter'" DocBookSectionTag NONE DocBookForceAbstractTag NONE End diff --git a/lib/layouts/stdstruct.inc b/lib/layouts/stdstruct.inc index b02499ff51..c8b7eb4cbd 100644 --- a/lib/layouts/stdstruct.inc +++ b/lib/layouts/stdstruct.inc @@ -51,8 +51,9 @@ Style Abstract EndHTMLStyle DocBookAbstract true DocBookInInfo always - DocBookTag abstract - DocBookItemTag para + DocBookWrapperTag abstract + DocBookWrapperMergeWithPrevious true + DocBookTag para End diff --git a/src/Paragraph.cpp b/src/Paragraph.cpp index 2465cc4101..24947f80dd 100644 --- a/src/Paragraph.cpp +++ b/src/Paragraph.cpp @@ -3383,12 +3383,9 @@ void Paragraph::simpleDocBookOnePar(Buffer const & buf, 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. - // This "special case" is defined in more details in output_docbook.cpp, makeParagraphs. The results - // of that brittle logic is passed to this function through open_par. - if (!inset->getLayout().htmlisblock() && size() != 1) // TODO: htmlisblock here too! - np.docbook_in_par = true; + + // TODO: special case will bite here. + np.docbook_in_par = true; inset->docbook(xs, np); } } else { diff --git a/src/ParagraphList.h b/src/ParagraphList.h index a57deb4eea..7e5cc1a869 100644 --- a/src/ParagraphList.h +++ b/src/ParagraphList.h @@ -29,6 +29,17 @@ public: ParagraphList(InputIterator first, InputIterator last) : RandomAccessList(first, last) {} + + const Paragraph * getParagraphBefore(const_iterator const & par) const + { + // No previous paragraph. + if (par == begin()) + return nullptr; + + auto prevpar = par; + --prevpar; + return &*prevpar; + } }; } // namespace lyx diff --git a/src/insets/InsetBibtex.cpp b/src/insets/InsetBibtex.cpp index 34b22c1583..c03a8e24d3 100644 --- a/src/insets/InsetBibtex.cpp +++ b/src/insets/InsetBibtex.cpp @@ -1157,10 +1157,11 @@ void InsetBibtex::docbook(XMLStream & xs, OutputParams const &) const if (vit == ven) { xs << XMLStream::ESCAPE_NONE << ""; + xs << xml::CR(); } for (; vit != ven; ++vit) { - BiblioInfo::const_iterator const biit = bibinfo.find(*vit); + auto const biit = bibinfo.find(*vit); if (biit == bibinfo.end()) continue; diff --git a/src/insets/InsetERT.cpp b/src/insets/InsetERT.cpp index a023f7a464..8e69e88260 100644 --- a/src/insets/InsetERT.cpp +++ b/src/insets/InsetERT.cpp @@ -101,7 +101,7 @@ void InsetERT::docbook(XMLStream & xs, OutputParams const & runparams) const odocstringstream os2; XMLStream xs2(os2); - // Recreate the logic of makeParagraphs in output_docbook.cpp, but much simplified: never open + // Recreate the logic of makeParagraph in output_docbook.cpp, but much simplified: never open // in an ERT, use simple line breaks. while (par != end) { par->simpleDocBookOnePar(buffer(), xs2, runparams, text().outerFont(distance(begin, par))); @@ -116,6 +116,7 @@ void InsetERT::docbook(XMLStream & xs, OutputParams const & runparams) const xs << XMLStream::ESCAPE_NONE << ""; + xs << xml::CR(); } diff --git a/src/insets/InsetNewline.cpp b/src/insets/InsetNewline.cpp index 45d52b23da..00b276ea47 100644 --- a/src/insets/InsetNewline.cpp +++ b/src/insets/InsetNewline.cpp @@ -176,17 +176,18 @@ void InsetNewline::docbook(XMLStream & xs, OutputParams const & runparams) const { if (runparams.docbook_in_par) { xs.closeFontTags(); - if (!xs.pending_tags_empty()) { - xs << xml::EndTag("para"); - xs << xml::StartTag("para"); - } - else { - xs << xml::CR() << xml::CompTag("br") << xml::CR(); - } - } - else { - xs << xml::CR() << xml::CompTag("br") << xml::CR(); + + // TODO: what if within a list item, and docbookiteminnertag is not para? This would require information + // about the paragraph's layout... Good for now, though, this should not happen in DocBook, only maybe + // extensions. + xs << XMLStream::ESCAPE_NONE << from_utf8(""); + xs << XMLStream::ESCAPE_NONE << from_utf8("\nMathML export failed. Please report this as a bug."; } // Output the complete formula to the DocBook stream. diff --git a/src/output_docbook.cpp b/src/output_docbook.cpp index 4430041434..b29da56c34 100644 --- a/src/output_docbook.cpp +++ b/src/output_docbook.cpp @@ -43,6 +43,8 @@ #include #include +// #define DOCBOOK_DEBUG_NEWLINES + using namespace std; using namespace lyx::support; @@ -191,6 +193,10 @@ namespace { void openParTag(XMLStream & xs, const Paragraph * par, const Paragraph * prevpar) { +#ifdef DOCBOOK_DEBUG_NEWLINES + xs << XMLStream::ESCAPE_NONE << ""; +#endif + Layout const & lay = par->layout(); if (par == prevpar) @@ -212,29 +218,43 @@ void openParTag(XMLStream & xs, const Paragraph * par, const Paragraph * prevpar } // Main logic. - if (openWrapper) + if (openWrapper) { xs << xml::StartTag(lay.docbookwrappertag(), lay.docbookwrapperattr()); + xs << xml::CR(); + } string tag = lay.docbooktag(); - if (tag == "Plain Layout") - tag = "para"; + if (tag != "NONE") { + auto xmltag = xml::ParTag(tag, lay.docbookattr()); + if (!xs.isTagOpen(xmltag, 1)) // Don't nest a paragraph directly in a paragraph. TODO: required or not? + xs << xmltag; + } - if (!xs.isTagOpen(xml::ParTag(tag, lay.docbookattr()), 1)) // Don't nest a paragraph directly in a paragraph. - xs << xml::ParTag(tag, lay.docbookattr()); - - if (lay.docbookitemtag() != "NONE") + if (lay.docbookitemtag() != "NONE") { xs << xml::StartTag(lay.docbookitemtag(), lay.docbookitemattr()); + xs << xml::CR(); + } + + if (lay.docbookiteminnertag() != "NONE") + xs << xml::StartTag(lay.docbookiteminnertag(), lay.docbookiteminnerattr()); + +#ifdef DOCBOOK_DEBUG_NEWLINES + xs << XMLStream::ESCAPE_NONE << ""; +#endif } -void closeTag(XMLStream & xs, Paragraph const * par, Paragraph const * nextpar) +void closeParTag(XMLStream & xs, Paragraph const * par, Paragraph const * nextpar) { - Layout const & lay = par->layout(); +#ifdef DOCBOOK_DEBUG_NEWLINES + xs << XMLStream::ESCAPE_NONE << ""; +#endif if (par == nextpar) nextpar = nullptr; // See comment in openParTag. + Layout const & lay = par->layout(); bool closeWrapper = lay.docbookwrappertag() != "NONE"; if (nextpar != nullptr) { Layout const & nextlay = nextpar->layout(); @@ -245,132 +265,133 @@ void closeTag(XMLStream & xs, Paragraph const * par, Paragraph const * nextpar) } // Main logic. - if (lay.docbookitemtag() != "NONE") + if (lay.docbookiteminnertag() != "NONE") { + xs << xml::EndTag(lay.docbookiteminnertag()); + xs << xml::CR(); + } + + if (lay.docbookitemtag() != "NONE") { xs << xml::EndTag(lay.docbookitemtag()); + xs << xml::CR(); + } - string tag = lay.docbooktag(); - if (tag == "Plain Layout") - tag = "para"; + if (lay.docbooktag() != "NONE") { + xs << xml::EndTag(lay.docbooktag()); + xs << xml::CR(); + } - xs << xml::EndTag(tag); - if (closeWrapper) + if (closeWrapper) { xs << xml::EndTag(lay.docbookwrappertag()); + xs << xml::CR(); + } + +#ifdef DOCBOOK_DEBUG_NEWLINES + xs << XMLStream::ESCAPE_NONE << ""; +#endif +} + + +void openBlockTag(XMLStream & xs, const Paragraph * par, const Paragraph * prevpar) +{ +#ifdef DOCBOOK_DEBUG_NEWLINES + xs << XMLStream::ESCAPE_NONE << ""; +#endif + + // Similar as openParTag, but with a line feed after. + openParTag(xs, par, prevpar); + xs << xml::CR(); + +#ifdef DOCBOOK_DEBUG_NEWLINES + xs << XMLStream::ESCAPE_NONE << ""; +#endif +} + + +void closeBlockTag(XMLStream & xs, const Paragraph * par, const Paragraph * prevpar) +{ +#ifdef DOCBOOK_DEBUG_NEWLINES + xs << XMLStream::ESCAPE_NONE << ""; +#endif + + // Similar as closeParTag, but with a line feed before. + xs << xml::CR(); + closeParTag(xs, par, prevpar); + +#ifdef DOCBOOK_DEBUG_NEWLINES + xs << XMLStream::ESCAPE_NONE << ""; +#endif } void openLabelTag(XMLStream & xs, Layout const & lay) // Mostly for definition lists. { +#ifdef DOCBOOK_DEBUG_NEWLINES + xs << XMLStream::ESCAPE_NONE << ""; +#endif + xs << xml::StartTag(lay.docbookitemlabeltag(), lay.docbookitemlabelattr()); + +#ifdef DOCBOOK_DEBUG_NEWLINES + xs << XMLStream::ESCAPE_NONE << ""; +#endif } void closeLabelTag(XMLStream & xs, Layout const & lay) { +#ifdef DOCBOOK_DEBUG_NEWLINES + xs << XMLStream::ESCAPE_NONE << ""; +#endif + xs << xml::EndTag(lay.docbookitemlabeltag()); xs << xml::CR(); + +#ifdef DOCBOOK_DEBUG_NEWLINES + xs << XMLStream::ESCAPE_NONE << ""; +#endif } void openItemTag(XMLStream & xs, Layout const & lay) { +#ifdef DOCBOOK_DEBUG_NEWLINES + xs << XMLStream::ESCAPE_NONE << ""; +#endif + xs << xml::StartTag(lay.docbookitemtag(), lay.docbookitemattr()); + +#ifdef DOCBOOK_DEBUG_NEWLINES + xs << XMLStream::ESCAPE_NONE << ""; +#endif } -// Return true when new elements are output in a paragraph, false otherwise. -bool openInnerItemTag(XMLStream & xs, Layout const & lay) +void closeItemTag(XMLStream & xs, Layout const & lay) { - if (lay.docbookiteminnertag() != "NONE") { - xs << xml::CR(); - xs << xml::ParTag(lay.docbookiteminnertag(), lay.docbookiteminnerattr()); +#ifdef DOCBOOK_DEBUG_NEWLINES + xs << XMLStream::ESCAPE_NONE << ""; +#endif - 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()); xs << xml::CR(); -} -// end of convenience functions - -ParagraphList::const_iterator findLast( - ParagraphList::const_iterator p, - ParagraphList::const_iterator const & pend, - LatexType type) { - for (++p; p != pend && p->layout().latextype == type; ++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; +#ifdef DOCBOOK_DEBUG_NEWLINES + xs << XMLStream::ESCAPE_NONE << ""; +#endif } -ParagraphList::const_iterator findEndOfEnvironment( - ParagraphList::const_iterator const & pstart, - ParagraphList::const_iterator const & pend) -{ - ParagraphList::const_iterator p = pstart; - 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 != p->layout()) - 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) +void makeParagraphBibliography( + Buffer const & buf, + XMLStream & xs, + OutputParams const & runparams, + Text const & text, + ParagraphList::const_iterator const & pbegin) { auto const begin = text.paragraphs().begin(); auto const end = text.paragraphs().end(); + auto pend = pbegin; + ++pend; // Find the paragraph *before* pbegin. ParagraphList::const_iterator pbegin_before = begin; @@ -420,322 +441,203 @@ ParagraphList::const_iterator makeParagraphBibliography( 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) +void makeParagraph( + Buffer const & buf, + XMLStream & xs, + OutputParams const & runparams, + Text const & text, + ParagraphList::const_iterator const & par) { auto const begin = text.paragraphs().begin(); auto const end = text.paragraphs().end(); - ParagraphList::const_iterator par = pbegin; - ParagraphList::const_iterator prevpar = pbegin; + auto prevpar = text.paragraphs().getParagraphBefore(par); - for (; par != pend; prevpar = par, ++par) { - // 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
...
. - bool special_case = false; - Inset const *specinset = par->size() == 1 ? par->getInset(0) : nullptr; - 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(); + // 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
...
. + bool special_case = false; + Inset const *specinset = par->size() == 1 ? par->getInset(0) : nullptr; + 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(std::distance(begin, par))).fontInfo(); - if (first_font == our_font) - special_case = true; - } - - // Plain layouts must be ignored. - if (!special_case && buf.params().documentClass().isPlainLayout(par->layout()) && !runparams.docbook_force_pars) + if (first_font == our_font) 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. - auto 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, &*par, &*prevpar); - - xs << XMLStream::ESCAPE_NONE << os2.str(); - - if (close_par) { - closeTag(xs, &*par, (nextpar != end) ? &*nextpar : nullptr); - xs << xml::CR(); - } - } } - return pend; + + // Plain layouts must be ignored. + if (!special_case && buf.params().documentClass().isPlainLayout(par->layout()) && !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 + && !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. + auto nextpar = par; + ++nextpar; + bool const close_par = open_par && (!runparams.docbook_in_par); + + // 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, &*par, prevpar); + + xs << XMLStream::ESCAPE_NONE << os2.str(); + + if (close_par) + closeParTag(xs, &*par, (nextpar != end) ? &*nextpar : nullptr); + } } -bool isNormalEnv(Layout const &lay) -{ - return lay.latextype == LATEX_ENVIRONMENT - || lay.latextype == LATEX_BIB_ENVIRONMENT; -} +void makeAny( + Text const &text, + Buffer const &buf, + XMLStream &xs, + OutputParams const &ourparams, + ParagraphList::const_iterator par); -ParagraphList::const_iterator makeEnvironment( +void 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 & par) { - auto const begin = text.paragraphs().begin(); auto const end = text.paragraphs().end(); - ParagraphList::const_iterator par = pbegin; - depth_type const origdepth = pbegin->params().depth(); - // Output the opening tag for this environment. - { - // Find the previous paragraph. - auto prevpar = begin; - if (prevpar != par) { - auto prevpar_next = prevpar; - ++prevpar_next; + // Output the opening tag for this environment, but only if it has not been previously opened (condition + // implemented in openParTag). + auto prevpar = text.paragraphs().getParagraphBefore(par); + openParTag(xs, &*par, prevpar); // TODO: switch in layout for par/block? - while (prevpar_next != par) { - ++prevpar_next; - ++prevpar; - } - } - - // Open tag for this environment. - openParTag(xs, &*par, &*prevpar); - xs << xml::CR(); - } - - // we will on occasion need to remember a layout from before. - Layout const *lastlay = nullptr; - auto prevpar = par; - - while (par != pend) { - Layout const & style = par->layout(); - ParagraphList::const_iterator send; - - auto parnext = par; - ++parnext; - - // Actual content of this paragraph. - prevpar = par; - switch (style.latextype) { - case LATEX_ENVIRONMENT: - case LATEX_LIST_ENVIRONMENT: - case LATEX_ITEM_ENVIRONMENT: { - // 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(par->layout() == 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 - // 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); - } else { - // No new line after closeLabelTag. - 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); - } else { - openLabelTag(xs, style); - xs << par->params().labelString(); - closeLabelTag(xs, style); - } - } - } // 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; - - // 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 . - 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); - - // 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. - else { - send = findEndOfEnvironment(par, pend); - par = makeEnvironment(buf, xs, runparams, text, par, send); - } - break; - } - case LATEX_PARAGRAPH: -// send = findLast(par, pend, LATEX_PARAGRAPH); - par = makeParagraphs(buf, xs, runparams, text, par, parnext); - break; - case LATEX_BIB_ENVIRONMENT: -// send = findLast(par, pend, LATEX_BIB_ENVIRONMENT); - makeParagraphBibliography(buf, xs, runparams, text, par, parnext); - break; - case LATEX_COMMAND: - par = parnext; - break; - } - } - - if (lastlay != nullptr) { - closeItemTag(xs, *lastlay); - if (lastlay->docbookitemwrappertag() != "NONE") { - xs << xml::EndTag(lastlay->docbookitemwrappertag()); + // Generate the contents of this environment. There is a special case if this is like some environment. + Layout const & style = par->layout(); + if (style.latextype == LATEX_COMMAND) { + // Nothing to do (otherwise, infinite loops). + } else if (style.latextype == LATEX_ENVIRONMENT || + style.latextype == LATEX_LIST_ENVIRONMENT || + style.latextype == LATEX_ITEM_ENVIRONMENT) { + // Open a wrapper tag if needed. + if (style.docbookitemwrappertag() != "NONE") { + xs << xml::StartTag(style.docbookitemwrappertag(), style.docbookitemwrapperattr()); xs << xml::CR(); } + + // Generate the label, if need be. If it is taken from the text, sep != 0 and corresponds to the first + // character after the label. + pos_type sep = 0; + if (style.labeltype != LABEL_NO_LABEL && style.docbookitemlabeltag() != "NONE") { + // At least one condition must be met: + // - this environment is not a list + // - if this is a list, the label must not be manual (i.e. it must be taken from the layout) + if (style.latextype != LATEX_LIST_ENVIRONMENT || style.labeltype != LABEL_MANUAL) { + // Usual cases: maybe there is something specified at the layout level. Highly unlikely, though. + docstring const lbl = par->params().labelString(); + + if (lbl.empty()) { + xs << xml::CR(); + } else { + openLabelTag(xs, style); + xs << lbl; + closeLabelTag(xs, style); + } + } else { + // Only variablelist gets here (or similar items defined as an extension in the layout). + openLabelTag(xs, style); + sep = par->firstWordDocBook(xs, runparams); + closeLabelTag(xs, style); + } + } + + // 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()) { // If the separator is already at the end of this paragraph... + 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 . + // TODO: this always worked only by magic... + xs << ' '; + } else { + // Generate the rest of the paragraph, if need be. + par->simpleDocBookOnePar(buf, xs, runparams, text.outerFont(std::distance(text.paragraphs().begin(), par)), + true, true, sep); + } + } else { + makeAny(text, buf, xs, runparams, par); } -// auto nextpar = par; -// ++nextpar; - closeTag(xs, &*prevpar, &*par); -// closeTag(xs, &*par, (nextpar != end) ? &*nextpar : nullptr); - xs << xml::CR(); - return pend; + + // Close the environment. + auto nextpar = par; + ++nextpar; + closeParTag(xs, &*par, (nextpar != end) ? &*nextpar : nullptr); // TODO: switch in layout for par/block? } @@ -744,117 +646,48 @@ void makeCommand( XMLStream & xs, OutputParams const & runparams, Text const & text, - ParagraphList::const_iterator const & pbegin) + ParagraphList::const_iterator const & par) { - // No need for labels, as they are handled by DocBook tags. + // Unlike XHTML, no need for labels, as they are handled by DocBook tags. auto const begin = text.paragraphs().begin(); auto const end = text.paragraphs().end(); - auto nextpar = pbegin; + auto nextpar = par; ++nextpar; - // Find the previous paragraph. - auto prevpar = begin; - if (prevpar != pbegin) { - auto prevpar_next = prevpar; - ++prevpar_next; - - while (prevpar_next != pbegin) { - ++prevpar_next; - ++prevpar; - } - } - // Generate this command. - openParTag(xs, &*pbegin, &*prevpar); + auto prevpar = text.paragraphs().getParagraphBefore(par); + openParTag(xs, &*par, prevpar); - pbegin->simpleDocBookOnePar(buf, xs, runparams, - text.outerFont(distance(begin, pbegin))); + par->simpleDocBookOnePar(buf, xs, runparams, + text.outerFont(distance(begin, par))); - closeTag(xs, &*pbegin, (nextpar != end) ? &*nextpar : nullptr); - xs << xml::CR(); + closeParTag(xs, &*par, (nextpar != end) ? &*nextpar : nullptr); } -pair makeAny( - Text const &text, - Buffer const &buf, - XMLStream &xs, - OutputParams const &ourparams, - ParagraphList::const_iterator par, - ParagraphList::const_iterator send, - ParagraphList::const_iterator pend) -{ - switch (par->layout().latextype) { - case LATEX_COMMAND: { - // 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: - // FIXME Same fix here. - send = findEndOfEnvironment(par, pend); - par = makeEnvironment(buf, xs, ourparams, text, par, send); - break; - case LATEX_PARAGRAPH: - send = findLast(par, pend, LATEX_PARAGRAPH); - par = makeParagraphs(buf, xs, ourparams, text, par, send); - break; - case LATEX_BIB_ENVIRONMENT: - send = findLast(par, pend, LATEX_BIB_ENVIRONMENT); - par = makeParagraphBibliography(buf, xs, ourparams, text, par, send); - break; - } - return make_pair(par, send); -} - -ParagraphList::const_iterator makeAnySimple( +void makeAny( Text const &text, Buffer const &buf, XMLStream &xs, OutputParams const &ourparams, ParagraphList::const_iterator par) { - auto parnext = par; - ++parnext; - switch (par->layout().latextype) { - case LATEX_COMMAND: { - // 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. + case LATEX_COMMAND: makeCommand(buf, xs, ourparams, text, par); - return parnext; - } + break; case LATEX_ENVIRONMENT: case LATEX_LIST_ENVIRONMENT: case LATEX_ITEM_ENVIRONMENT: - // FIXME Same fix here. - return makeEnvironment(buf, xs, ourparams, text, par, parnext); + makeEnvironment(buf, xs, ourparams, text, par); + break; case LATEX_PARAGRAPH: - return makeParagraphs(buf, xs, ourparams, text, par, parnext); + makeParagraph(buf, xs, ourparams, text, par); + break; case LATEX_BIB_ENVIRONMENT: - return makeParagraphBibliography(buf, xs, ourparams, text, par, parnext); + makeParagraphBibliography(buf, xs, ourparams, text, par); + break; } - - // This should never happen. Return the next paragraph to avoid an infinite loop. - return parnext; } } // end anonymous namespace @@ -885,9 +718,8 @@ DocBookDocumentSectioning hasDocumentSectioning(ParagraphList const ¶graphs, Layout const &style = paragraphs[bpit].layout(); documentHasSections |= style.category() == from_utf8("Sectioning"); - if (documentHasSections) { + if (documentHasSections) break; - } bpit += 1; } // Paragraphs before the first section: [ runparams.par_begin ; eppit ) @@ -898,7 +730,10 @@ DocBookDocumentSectioning hasDocumentSectioning(ParagraphList const ¶graphs, bool hasOnlyNotes(Paragraph const & par) { + // Precondition: the paragraph is not empty. Otherwise, the function will always return true... for (int i = 0; i < par.size(); ++i) + // If you find something that is not an inset (like actual text) or an inset that is not a note, + // return false. if (!par.isInset(i) || !dynamic_cast(par.insetList().get(i))) return false; return true; @@ -994,37 +829,6 @@ DocBookInfoTag getParagraphsWithInfo(ParagraphList const ¶graphs, pit_type b } -pit_type generateDocBookParagraphWithoutSectioning( - Text const & text, - Buffer const & buf, - XMLStream & xs, - OutputParams const & runparams, - ParagraphList const & paragraphs, - DocBookInfoTag const & info) -{ - auto bpit = info.bpit; - auto par = paragraphs.iterator_at(bpit); - auto lastStartedPar = par; - ParagraphList::const_iterator send; - auto const pend = - (info.epit == (int) paragraphs.size()) ? - paragraphs.end() : paragraphs.iterator_at(info.epit); - - while (bpit < info.epit) { - if (info.abstract.find(bpit) != info.abstract.end()) { - bpit += 1; - continue; - } - - 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, @@ -1040,6 +844,7 @@ void outputDocBookInfo( bool hasAbstract = !info.abstract.empty(); docstring abstract; if (hasAbstract) { + // Generate the abstract XML into a string before further checks. odocstringstream os2; { XMLStream xs2(os2); @@ -1048,8 +853,7 @@ void outputDocBookInfo( // info.abstract is inclusive, epit is exclusive, hence +1 for looping. while (bpit < epit) { - makeAnySimple(text, buf, xs2, runparams, paragraphs.iterator_at(bpit)); - xs2 << XMLStream::ESCAPE_NONE << from_ascii(""); + makeAny(text, buf, xs2, runparams, paragraphs.iterator_at(bpit)); bpit += 1; } } @@ -1065,7 +869,7 @@ void outputDocBookInfo( hasAbstract = false; } - // The abstract must go in . + // The abstract must go in . Otherwise, decide whether to open based on the layouts. bool needInfo = !info.mustBeInInfo.empty() || hasAbstract; // Start the tag if required. @@ -1076,16 +880,17 @@ void outputDocBookInfo( } // Output the elements that should go in , before and after the abstract. - xs << XMLStream::ESCAPE_NONE << ""; - for (auto pit : info.shouldBeInInfo) // Typically, the title: these elements are so important and ubiquitous - // that mandating a wrapper like would repel users. - makeAnySimple(text, buf, xs, runparams, paragraphs.iterator_at(pit)); - xs << XMLStream::ESCAPE_NONE << ""; - for (auto pit : info.mustBeInInfo) + for (auto pit : info.shouldBeInInfo) { // Typically, the title: these elements are so important and ubiquitous + // that mandating a wrapper like would repel users. Thus, generate them first. + makeAny(text, buf, xs, runparams, paragraphs.iterator_at(pit)); + } + for (auto pit : info.mustBeInInfo) { if (info.abstract.find(pit) == info.abstract.end()) // The abstract must be in info, but is dealt with after. - makeAnySimple(text, buf, xs, runparams, paragraphs.iterator_at(pit)); - xs << XMLStream::ESCAPE_NONE << ""; + makeAny(text, buf, xs, runparams, paragraphs.iterator_at(pit)); + } + // Always output the abstract as the last item of the , as it requires special treatment (especially if + // it contains several paragraphs that are empty). if (hasAbstract) { // string tag = paragraphs[*info.abstract.begin()].layout().docbookforceabstracttag(); // if (tag == "NONE") @@ -1093,9 +898,7 @@ void outputDocBookInfo( // // xs << xml::StartTag(tag); // xs << xml::CR(); - xs << XMLStream::ESCAPE_NONE << ""; xs << XMLStream::ESCAPE_NONE << abstract; - xs << XMLStream::ESCAPE_NONE << ""; // xs << xml::EndTag(tag); // xs << xml::CR(); } @@ -1147,7 +950,7 @@ void docbookSimpleAllParagraphs( while (bpit < epit) { auto par = paragraphs.iterator_at(bpit); if (!hasOnlyNotes(*par)) - makeAnySimple(text, buf, xs, runparams, par); + makeAny(text, buf, xs, runparams, par); bpit += 1; } } @@ -1170,9 +973,6 @@ void docbookParagraphs(Text const &text, return; }); - ParagraphList::const_iterator const pend = - (epit == (int) paragraphs.size()) ? - paragraphs.end() : paragraphs.iterator_at(epit); std::stack> headerLevels; // Used to determine when to open/close sections: store the depth // of the section and the tag that was used to open it. @@ -1203,8 +1003,7 @@ void docbookParagraphs(Text const &text, ParagraphList::const_iterator send; if (hasOnlyNotes(*par)) { - ++par; - bpit += distance(lastStartedPar, par); + bpit += 1; continue; } @@ -1283,8 +1082,8 @@ void docbookParagraphs(Text const &text, } // Generate this paragraph. - tie(par, send) = makeAny(text, buf, xs, ourparams, par, send, pend); - bpit += distance(lastStartedPar, par); + makeAny(text, buf, xs, ourparams, par); + bpit += 1; } // If need be, close
s, but only at the end of the document (otherwise, dealt with at the beginning diff --git a/src/xml.cpp b/src/xml.cpp index e16a7840b6..147eaddbb9 100644 --- a/src/xml.cpp +++ b/src/xml.cpp @@ -200,7 +200,7 @@ bool XMLStream::closeFontTags() tag_stack_.pop_back(); // this shouldn't happen, since then the font tags // weren't in any other tag. -// LASSERT(!tag_stack_.empty(), return true); + LASSERT(!tag_stack_.empty(), return true); if (tag_stack_.empty()) return true; curtag = &tag_stack_.back(); @@ -583,62 +583,57 @@ docstring xml::uniqueID(docstring const & label) docstring xml::cleanID(docstring const & orig) { - // The standard xml:id only allows letters, - // digits, '-' and '.' in a name. - // This routine replaces illegal characters by '-' or '.' - // and adds a number for uniqueness if need be. - docstring const allowed = from_ascii(".-_"); + // The standard xml:id only allows letters, digits, '-' and '.' in a name. + // This routine replaces illegal characters by '-' or '.' and adds a number for uniqueness if need be. // Use a cache of already mangled names: the alterations may merge several IDs as one. This ensures that the IDs // are not mixed up in the document. + // This code could be improved: it uses Qt outside the GUI part. Any TLS implementation could do the trick. typedef map MangledMap; static QThreadStorage tMangledNames; static QThreadStorage tMangleID; - MangledMap & mangledNames = tMangledNames.localData(); - // If the name is already known, just return it. - MangledMap::const_iterator const known = mangledNames.find(orig); + MangledMap & mangledNames = tMangledNames.localData(); + auto const known = mangledNames.find(orig); if (known != mangledNames.end()) return known->second; // Start creating the mangled name by iterating over the characters. docstring content; - docstring::const_iterator it = orig.begin(); - docstring::const_iterator end = orig.end(); + auto it = orig.cbegin(); + auto end = orig.cend(); // Make sure it starts with a letter. - if (!isAlphaASCII(*it) && allowed.find(*it) >= allowed.size()) + if (!isAlphaASCII(*it)) content += "x"; - // Do the mangling. + // Parse the ID character by character and change what needs to. bool mangle = false; // Indicates whether the ID had to be changed, i.e. if ID no more ensured to be unique. for (; it != end; ++it) { char_type c = *it; - if (isAlphaASCII(c) || isDigitASCII(c) || c == '-' || c == '.' - || allowed.find(c) < allowed.size()) + if (isAlphaASCII(c) || isDigitASCII(c) || c == '-' || c == '.' || c == '_') { content += c; - else if (c == '_' || c == ' ') { - mangle = true; - content += "-"; - } - else if (c == ':' || c == ',' || c == ';' || c == '!') { + } else if (c == ':' || c == ',' || c == ';' || c == '!') { mangle = true; content += "."; - } - else { + } else { // Other invalid characters, such as ' '. mangle = true; content += "-"; } } - if (mangle) { + // If there had to be a change, check if ID unicity is still guaranteed. + // This avoids having a clash if satisfying XML requirements for ID makes two IDs identical, like "a:b" and "a!b", + // as both of them would be transformed as "a.b". With this procedure, one will become "a.b" and the other "a.b-1". + if (mangle && mangledNames.find(content) != mangledNames.end()) { int & mangleID = tMangleID.localData(); - content += "-" + convert(mangleID++); + content += "-" + convert(mangleID); + mangleID += 1; } + // Save the new ID to avoid recomputing it afterwards and to ensure stability over the document. mangledNames[orig] = content; - return content; }