2003-11-05 12:06:20 +00:00
/**
2007-04-26 04:41:58 +00:00
* \ file output_docbook . cpp
2003-11-05 12:06:20 +00:00
* This file is part of LyX , the document processor .
* Licence details can be found in the file COPYING .
*
2008-11-14 15:58:50 +00:00
* \ author Lars Gullik Bjønnes
* \ author José Matos
2003-11-05 12:06:20 +00:00
*
* Full author contact details are available in file CREDITS .
*/
# include <config.h>
2020-10-24 21:47:13 +00:00
# include "output_docbook.h"
2007-04-26 04:41:58 +00:00
# include "Buffer.h"
2005-02-25 11:55:36 +00:00
# include "buffer_funcs.h"
2007-04-26 04:41:58 +00:00
# include "BufferParams.h"
2010-03-31 19:38:05 +00:00
# include "Font.h"
2020-06-08 21:27:49 +00:00
# include "InsetList.h"
2007-04-26 04:41:58 +00:00
# include "Paragraph.h"
2006-03-23 20:11:06 +00:00
# include "ParagraphList.h"
2003-11-05 12:06:20 +00:00
# include "ParagraphParameters.h"
2019-05-09 23:35:40 +00:00
# include "xml.h"
2009-08-09 17:30:41 +00:00
# include "Text.h"
2007-11-07 23:25:08 +00:00
# include "TextClass.h"
2003-11-05 12:06:20 +00:00
2020-06-08 21:27:49 +00:00
# include "insets/InsetBibtex.h"
2020-07-25 02:48:33 +00:00
# include "insets/InsetBibitem.h"
2020-06-08 21:27:49 +00:00
# include "insets/InsetLabel.h"
2020-10-18 06:15:54 +00:00
# include "mathed/InsetMath.h"
2020-06-08 21:27:49 +00:00
# include "insets/InsetNote.h"
2020-11-22 22:23:39 +00:00
# include "support/debug.h"
2020-06-08 21:27:49 +00:00
# include "support/lassert.h"
2020-10-16 15:02:06 +00:00
# include "support/textutils.h"
2007-10-02 18:27:20 +00:00
2020-06-08 21:27:49 +00:00
# include <stack>
2016-07-04 10:11:24 +00:00
# include <iostream>
2020-06-08 21:27:49 +00:00
# include <algorithm>
2020-07-30 22:55:00 +00:00
# include <sstream>
2016-07-04 10:11:24 +00:00
2007-12-12 10:16:00 +00:00
using namespace std ;
2007-12-12 18:57:56 +00:00
using namespace lyx : : support ;
2003-11-05 12:06:20 +00:00
2006-10-21 00:16:43 +00:00
namespace lyx {
2004-10-24 00:02:39 +00:00
namespace {
2003-11-05 12:06:20 +00:00
2020-08-02 18:43:39 +00:00
std : : string fontToDocBookTag ( xml : : FontTypes type )
2020-07-08 16:15:34 +00:00
{
switch ( type ) {
case xml : : FontTypes : : FT_EMPH :
case xml : : FontTypes : : FT_BOLD :
return " emphasis " ;
case xml : : FontTypes : : FT_NOUN :
2020-08-30 22:50:37 +00:00
return " personname " ;
2020-07-08 16:15:34 +00:00
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 " " ;
}
2019-05-09 23:35:40 +00:00
}
2020-08-02 18:43:39 +00:00
2020-07-08 16:15:34 +00:00
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 " ;
2020-08-02 18:43:39 +00:00
case xml : : FontTypes : : FT_NOUN : // Outputs a <person>
case xml : : FontTypes : : FT_TYPE : // Outputs a <code>
return " " ;
2020-07-19 19:00:25 +00:00
case xml : : FontTypes : : FT_UBAR :
return " underline " ;
2020-07-08 16:15:34 +00:00
2020-08-02 18:43:39 +00:00
// All other roles are non-standard for DocBook.
2020-07-08 16:15:34 +00:00
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 " " ;
}
2004-10-24 00:02:39 +00:00
}
2003-11-25 17:23:36 +00:00
2020-08-18 03:40:34 +00:00
2020-06-08 21:27:49 +00:00
string fontToAttribute ( xml : : FontTypes type ) {
2020-07-08 16:15:34 +00:00
// 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 ) ;
2020-11-18 00:51:05 +00:00
if ( ! role . empty ( ) )
2020-07-08 16:15:34 +00:00
return " role=' " + role + " ' " ;
2020-11-18 00:51:05 +00:00
else
2020-07-08 16:15:34 +00:00
return " " ;
2020-06-08 21:27:49 +00:00
}
2003-11-05 12:06:20 +00:00
2020-06-08 21:27:49 +00:00
2020-08-17 22:37:34 +00:00
// Higher-level convenience functions.
2020-11-26 05:22:27 +00:00
void openParTag ( XMLStream & xs , const Paragraph * par , const Paragraph * prevpar , const OutputParams & runparams )
2020-08-17 22:37:34 +00:00
{
2020-08-02 16:58:36 +00:00
if ( par = = prevpar )
prevpar = nullptr ;
2020-11-28 00:23:22 +00:00
// If the previous paragraph is empty, don't consider it when opening wrappers.
if ( prevpar & & prevpar - > empty ( ) & & ! prevpar - > allowEmpty ( ) )
prevpar = nullptr ;
2020-08-02 16:58:36 +00:00
// When should the wrapper be opened here? Only if the previous paragraph has the SAME wrapper tag
// (usually, they won't have the same layout) and the CURRENT one allows merging.
// The main use case is author information in several paragraphs: if the name of the author is the
// first paragraph of an author, then merging with the previous tag does not make sense. Say the
// next paragraph is the affiliation, then it should be output in the same <author> tag (different
// layout, same wrapper tag).
2020-11-02 02:44:51 +00:00
Layout const & lay = par - > layout ( ) ;
2020-11-26 05:22:27 +00:00
bool openWrapper = lay . docbookwrappertag ( ) ! = " NONE " & & ! runparams . docbook_ignore_wrapper ;
2020-11-26 05:40:39 +00:00
if ( prevpar ! = nullptr & & ! runparams . docbook_ignore_wrapper ) {
2020-08-02 16:58:36 +00:00
Layout const & prevlay = prevpar - > layout ( ) ;
if ( prevlay . docbookwrappertag ( ) ! = " NONE " ) {
2020-09-01 22:46:08 +00:00
if ( prevlay . docbookwrappertag ( ) = = lay . docbookwrappertag ( ) & &
prevlay . docbookwrapperattr ( ) = = lay . docbookwrapperattr ( ) )
2020-08-30 01:30:56 +00:00
openWrapper = ! lay . docbookwrappermergewithprevious ( ) ;
else
openWrapper = true ;
2020-08-02 16:58:36 +00:00
}
}
2020-08-02 15:35:16 +00:00
2020-08-02 16:58:36 +00:00
// Main logic.
2022-12-22 03:38:36 +00:00
if ( openWrapper ) {
2020-11-18 04:38:25 +00:00
xml : : openTag ( xs , lay . docbookwrappertag ( ) , lay . docbookwrapperattr ( ) , lay . docbookwrappertagtype ( ) ) ;
2020-06-08 21:27:49 +00:00
2022-12-22 03:38:36 +00:00
if ( lay . docbookgeneratetitle ( ) ) {
docstring const label = par - > params ( ) . labelString ( ) ;
xml : : openTag ( xs , " title " , " " , " paragraph " ) ;
xs < < ( ! label . empty ( ) ? label : from_ascii ( " No title " ) ) ;
xml : : closeTag ( xs , " title " , " paragraph " ) ;
}
}
2020-08-17 22:37:34 +00:00
const string & tag = lay . docbooktag ( ) ;
2020-08-15 22:59:43 +00:00
if ( tag ! = " NONE " ) {
auto xmltag = xml : : ParTag ( tag , lay . docbookattr ( ) ) ;
2020-10-07 04:05:22 +00:00
if ( ! xs . isTagOpen ( xmltag , 1 ) ) { // Don't nest a paragraph directly in a paragraph.
2020-08-17 22:37:34 +00:00
// TODO: required or not?
// TODO: avoid creating a ParTag object just for this query...
2020-11-18 04:38:25 +00:00
xml : : openTag ( xs , lay . docbooktag ( ) , lay . docbookattr ( ) , lay . docbooktagtype ( ) ) ;
xml : : openTag ( xs , lay . docbookinnertag ( ) , lay . docbookinnerattr ( ) , lay . docbookinnertagtype ( ) ) ;
2020-10-07 04:05:22 +00:00
}
2020-08-15 22:59:43 +00:00
}
2020-08-02 15:28:50 +00:00
2020-11-26 05:22:27 +00:00
xml : : openTag ( xs , lay . docbookitemwrappertag ( ) , lay . docbookitemwrapperattr ( ) , lay . docbookitemwrappertagtype ( ) ) ;
2020-11-18 04:38:25 +00:00
xml : : openTag ( xs , lay . docbookitemtag ( ) , lay . docbookitemattr ( ) , lay . docbookitemtagtype ( ) ) ;
xml : : openTag ( xs , lay . docbookiteminnertag ( ) , lay . docbookiteminnerattr ( ) , lay . docbookiteminnertagtype ( ) ) ;
2004-10-24 00:02:39 +00:00
}
2003-11-05 12:06:20 +00:00
2004-04-03 08:37:12 +00:00
2020-11-26 05:22:27 +00:00
void closeParTag ( XMLStream & xs , Paragraph const * par , Paragraph const * nextpar , const OutputParams & runparams )
2020-07-08 16:15:34 +00:00
{
2020-08-02 16:58:36 +00:00
if ( par = = nextpar )
nextpar = nullptr ;
2020-11-28 00:23:22 +00:00
// If the next paragraph is empty, don't consider it when closing wrappers.
if ( nextpar & & nextpar - > empty ( ) & & ! nextpar - > allowEmpty ( ) )
nextpar = nullptr ;
2020-08-02 16:58:36 +00:00
// See comment in openParTag.
2020-08-15 22:59:43 +00:00
Layout const & lay = par - > layout ( ) ;
2020-11-26 05:22:27 +00:00
bool closeWrapper = lay . docbookwrappertag ( ) ! = " NONE " & & ! runparams . docbook_ignore_wrapper ;
2020-11-26 05:40:39 +00:00
if ( nextpar ! = nullptr & & ! runparams . docbook_ignore_wrapper ) {
2020-08-02 16:58:36 +00:00
Layout const & nextlay = nextpar - > layout ( ) ;
if ( nextlay . docbookwrappertag ( ) ! = " NONE " ) {
2020-09-01 22:46:08 +00:00
if ( nextlay . docbookwrappertag ( ) = = lay . docbookwrappertag ( ) & &
nextlay . docbookwrapperattr ( ) = = lay . docbookwrapperattr ( ) )
2020-08-30 01:30:56 +00:00
closeWrapper = ! nextlay . docbookwrappermergewithprevious ( ) ;
else
closeWrapper = true ;
2020-08-02 16:58:36 +00:00
}
}
2020-08-02 15:35:16 +00:00
2020-08-02 16:58:36 +00:00
// Main logic.
2020-11-18 04:38:25 +00:00
xml : : closeTag ( xs , lay . docbookiteminnertag ( ) , lay . docbookiteminnertagtype ( ) ) ;
xml : : closeTag ( xs , lay . docbookitemtag ( ) , lay . docbookitemtagtype ( ) ) ;
2020-11-26 05:22:27 +00:00
xml : : closeTag ( xs , lay . docbookitemwrappertag ( ) , lay . docbookitemwrappertagtype ( ) ) ;
2020-11-18 04:38:25 +00:00
xml : : closeTag ( xs , lay . docbookinnertag ( ) , lay . docbookinnertagtype ( ) ) ;
xml : : closeTag ( xs , lay . docbooktag ( ) , lay . docbooktagtype ( ) ) ;
2020-08-17 22:37:34 +00:00
if ( closeWrapper )
2020-11-18 04:38:25 +00:00
xml : : closeTag ( xs , lay . docbookwrappertag ( ) , lay . docbookwrappertagtype ( ) ) ;
2020-06-08 21:27:49 +00:00
}
2020-08-29 01:46:30 +00:00
void makeBibliography (
Text const & text ,
2020-08-15 22:59:43 +00:00
Buffer const & buf ,
XMLStream & xs ,
OutputParams const & runparams ,
2020-08-18 03:46:40 +00:00
ParagraphList : : const_iterator const & par )
2020-07-25 02:48:33 +00:00
{
// If this is the first paragraph in a bibliography, open the bibliography tag.
2020-09-11 23:43:57 +00:00
auto const * pbegin_before = text . paragraphs ( ) . getParagraphBefore ( par ) ;
if ( pbegin_before = = nullptr | | ( pbegin_before & & pbegin_before - > layout ( ) . latextype ! = LATEX_BIB_ENVIRONMENT ) ) {
2020-07-25 02:48:33 +00:00
xs < < xml : : StartTag ( " bibliography " ) ;
xs < < xml : : CR ( ) ;
}
2020-08-18 03:46:40 +00:00
// 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 ) {
2020-08-25 00:11:08 +00:00
Inset const * ip = par - > getInset ( i ) ;
if ( ! ip )
continue ;
if ( const auto * bibitem = dynamic_cast < const InsetBibitem * > ( ip ) ) {
2020-10-26 23:49:26 +00:00
auto id = xml : : cleanID ( bibitem - > getParam ( " key " ) ) ;
attr = from_utf8 ( " xml:id=' " ) + id + from_utf8 ( " ' " ) ;
2020-08-18 03:46:40 +00:00
break ;
2020-07-25 02:48:33 +00:00
}
2020-08-18 03:46:40 +00:00
}
xs < < xml : : StartTag ( from_utf8 ( " bibliomixed " ) , attr ) ;
2020-08-26 19:19:38 +00:00
// Generate the entry. Concatenate the different parts of the paragraph if any.
2020-08-18 03:46:40 +00:00
auto const begin = text . paragraphs ( ) . begin ( ) ;
2021-02-19 16:38:22 +00:00
std : : vector < docstring > pars_prepend ;
std : : vector < docstring > pars ;
std : : vector < docstring > pars_append ;
tie ( pars_prepend , pars , pars_append ) = par - > simpleDocBookOnePar ( buf , runparams , text . outerFont ( std : : distance ( begin , par ) ) , 0 ) ;
for ( auto & parXML : pars_prepend )
xs < < XMLStream : : ESCAPE_NONE < < parXML ;
2020-08-26 19:19:38 +00:00
for ( auto & parXML : pars )
xs < < XMLStream : : ESCAPE_NONE < < parXML ;
2021-02-19 16:38:22 +00:00
for ( auto & parXML : pars_append )
xs < < XMLStream : : ESCAPE_NONE < < parXML ;
2020-07-25 02:48:33 +00:00
2020-08-18 03:46:40 +00:00
// End the precooked bibliography entry.
xs < < xml : : EndTag ( " bibliomixed " ) ;
xs < < xml : : CR ( ) ;
2020-07-25 02:48:33 +00:00
2020-08-18 03:46:40 +00:00
// If this is the last paragraph in a bibliography, close the bibliography tag.
auto const end = text . paragraphs ( ) . end ( ) ;
2020-08-30 00:30:04 +00:00
auto nextpar = par ;
+ + nextpar ;
bool endBibliography = nextpar = = end | | nextpar - > layout ( ) . latextype ! = LATEX_BIB_ENVIRONMENT ;
2020-07-25 02:48:33 +00:00
2020-08-18 03:46:40 +00:00
if ( endBibliography ) {
2020-07-25 02:48:33 +00:00
xs < < xml : : EndTag ( " bibliography " ) ;
xs < < xml : : CR ( ) ;
}
}
2020-08-15 22:59:43 +00:00
void makeParagraph (
2020-08-29 01:46:30 +00:00
Text const & text ,
2020-08-15 22:59:43 +00:00
Buffer const & buf ,
XMLStream & xs ,
OutputParams const & runparams ,
ParagraphList : : const_iterator const & par )
2020-07-08 16:15:34 +00:00
{
2020-11-14 21:05:59 +00:00
// Useful variables.
2020-08-02 16:58:36 +00:00
auto const begin = text . paragraphs ( ) . begin ( ) ;
auto const end = text . paragraphs ( ) . end ( ) ;
2020-08-15 22:59:43 +00:00
auto prevpar = text . paragraphs ( ) . getParagraphBefore ( 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 <div>...</div>.
bool special_case = false ;
Inset const * specinset = par - > size ( ) = = 1 ? par - > getInset ( 0 ) : nullptr ;
2021-09-20 01:19:20 +00:00
if ( specinset & & ! specinset - > getLayout ( ) . htmlisblock ( ) ) { // TODO: Convert htmlisblock to a DocBook parameter? docbooknotinpara should be enough in most cases.
2020-08-15 22:59:43 +00:00
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 ( ) ;
2020-07-08 16:15:34 +00:00
2020-08-15 22:59:43 +00:00
if ( first_font = = our_font )
2020-07-08 16:15:34 +00:00
special_case = true ;
2020-08-15 22:59:43 +00:00
}
2020-12-05 21:51:56 +00:00
size_t nInsets = std : : distance ( par - > insetList ( ) . begin ( ) , par - > insetList ( ) . end ( ) ) ;
2020-11-15 18:04:21 +00:00
auto parSize = ( size_t ) par - > size ( ) ;
2020-08-15 22:59:43 +00:00
// Plain layouts must be ignored.
2020-08-29 17:05:59 +00:00
special_case | = buf . params ( ) . documentClass ( ) . isPlainLayout ( par - > layout ( ) ) & & ! runparams . docbook_force_pars ;
2021-10-06 22:07:21 +00:00
2020-08-29 17:05:59 +00:00
// Equations do not deserve their own paragraph (DocBook allows them outside paragraphs).
2020-10-18 06:15:54 +00:00
// Exception: any case that generates an <inlineequation> must still get a paragraph to be valid.
2021-10-06 22:07:21 +00:00
auto isEquationSpecialCase = [ ] ( InsetList : : Element inset ) {
2020-10-18 06:15:54 +00:00
return inset . inset & & inset . inset - > asInsetMath ( ) & & inset . inset - > asInsetMath ( ) - > getType ( ) ! = hullSimple ;
2021-10-06 22:07:21 +00:00
} ;
special_case | = nInsets = = parSize & & std : : all_of ( par - > insetList ( ) . begin ( ) , par - > insetList ( ) . end ( ) , isEquationSpecialCase ) ;
2020-07-08 16:15:34 +00:00
2020-11-28 19:48:43 +00:00
// Things that should not get into their own paragraph. (Only valid for DocBook.)
static std : : set < InsetCode > lyxCodeSpecialCases = {
TABULAR_CODE ,
FLOAT_CODE ,
BIBTEX_CODE , // Bibliographies cannot be in paragraphs. Bibitems should still be handled as paragraphs,
// though (see makeParagraphBibliography).
ERT_CODE , // ERTs are in comments, not paragraphs.
LISTINGS_CODE ,
BOX_CODE ,
INCLUDE_CODE ,
NOMENCL_PRINT_CODE ,
TOC_CODE , // To be ignored in DocBook, the processor afterwards should deal with ToCs.
NOTE_CODE // Notes do not produce any output.
} ;
auto isLyxCodeSpecialCase = [ ] ( InsetList : : Element inset ) {
return lyxCodeSpecialCases . find ( inset . inset - > lyxCode ( ) ) ! = lyxCodeSpecialCases . end ( ) ;
} ;
special_case | = nInsets = = parSize & & std : : all_of ( par - > insetList ( ) . begin ( ) , par - > insetList ( ) . end ( ) , isLyxCodeSpecialCase ) ;
2020-11-28 21:43:00 +00:00
// 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 ;
2020-12-05 21:51:56 +00:00
2020-11-28 21:43:00 +00:00
// 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 ( ) ) {
2020-12-05 21:51:56 +00:00
size_t nInsets = std : : distance ( par . insetList ( ) . begin ( ) , par . insetList ( ) . end ( ) ) ;
2020-11-28 21:43:00 +00:00
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 ) ;
2021-10-06 22:07:21 +00:00
// If the insets should be rendered as images, enter the special case.
auto isRenderedAsImageSpecialCase = [ ] ( InsetList : : Element inset ) {
return inset . inset & & inset . inset - > getLayout ( ) . docbookrenderasimage ( ) ;
} ;
special_case | = nInsets = = parSize & & std : : all_of ( par - > insetList ( ) . begin ( ) , par - > insetList ( ) . end ( ) , isRenderedAsImageSpecialCase ) ;
2020-11-28 19:48:43 +00:00
// 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).
2020-08-15 22:59:43 +00:00
bool const open_par = runparams . docbook_make_pars
& & ! runparams . docbook_in_par
& & ! special_case ;
2020-07-08 16:15:34 +00:00
2020-08-15 22:59:43 +00:00
// 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.
2020-11-18 00:51:05 +00:00
bool const close_par = open_par & & ! runparams . docbook_in_par ;
2020-07-08 16:15:34 +00:00
2020-08-15 22:59:43 +00:00
// 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.
2020-08-25 00:41:07 +00:00
// Thus, remove all spaces (including new lines: \r, \n) before checking for emptiness.
2020-08-26 19:19:38 +00:00
// std::all_of allows doing this check without having to copy the string.
// Open and close tags around each contained paragraph.
2020-08-30 21:56:16 +00:00
auto nextpar = par ;
+ + nextpar ;
2021-02-19 16:38:22 +00:00
std : : vector < docstring > pars_prepend ;
std : : vector < docstring > pars ;
std : : vector < docstring > pars_append ;
tie ( pars_prepend , pars , pars_append ) = par - > simpleDocBookOnePar ( buf , runparams , text . outerFont ( distance ( begin , par ) ) , 0 , nextpar = = end , special_case ) ;
for ( docstring const & parXML : pars_prepend )
xs < < XMLStream : : ESCAPE_NONE < < parXML ;
2020-09-01 22:36:46 +00:00
for ( docstring const & parXML : pars ) {
2020-11-14 21:05:59 +00:00
if ( ! xml : : isNotOnlySpace ( parXML ) )
continue ;
2020-08-26 19:19:38 +00:00
2020-11-14 21:05:59 +00:00
if ( open_par )
2020-11-26 05:22:27 +00:00
openParTag ( xs , & * par , prevpar , runparams ) ;
2020-08-26 19:19:38 +00:00
2020-11-14 21:05:59 +00:00
xs < < XMLStream : : ESCAPE_NONE < < parXML ;
if ( close_par )
2020-11-26 05:22:27 +00:00
closeParTag ( xs , & * par , ( nextpar ! = end ) ? & * nextpar : nullptr , runparams ) ;
2020-07-08 16:15:34 +00:00
}
2021-02-19 16:38:22 +00:00
for ( docstring const & parXML : pars_append )
xs < < XMLStream : : ESCAPE_NONE < < parXML ;
2020-06-08 21:27:49 +00:00
}
2020-08-29 01:46:30 +00:00
void makeEnvironment ( Text const & text ,
Buffer const & buf ,
2020-08-29 00:55:47 +00:00
XMLStream & xs ,
OutputParams const & runparams ,
ParagraphList : : const_iterator const & par )
{
2020-11-14 21:05:59 +00:00
// Useful variables.
2020-08-29 00:55:47 +00:00
auto const end = text . paragraphs ( ) . end ( ) ;
2020-08-30 23:24:28 +00:00
auto nextpar = par ;
+ + nextpar ;
// Special cases for listing-like environments provided in layouts. This is quite ad-hoc, but provides a useful
// default. This should not be used by too many environments (only LyX-Code right now).
// This would be much simpler if LyX-Code was implemented as InsetListings...
bool mimicListing = false ;
bool ignoreFonts = false ;
if ( par - > layout ( ) . docbooktag ( ) = = " programlisting " ) {
mimicListing = true ;
ignoreFonts = true ;
}
2020-08-29 00:55:47 +00:00
// 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 ) ;
2020-11-26 05:22:27 +00:00
openParTag ( xs , & * par , prevpar , runparams ) ;
2020-08-29 00:55:47 +00:00
// 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 ) {
2020-08-30 23:24:28 +00:00
// Generate the paragraph, if need be.
2021-02-19 16:38:22 +00:00
std : : vector < docstring > pars_prepend ;
std : : vector < docstring > pars ;
std : : vector < docstring > pars_append ;
2022-12-22 03:38:36 +00:00
tie ( pars_prepend , pars , pars_append ) =
par - > simpleDocBookOnePar ( buf , runparams , text . outerFont ( std : : distance ( text . paragraphs ( ) . begin ( ) , par ) ) ,
0 , false , ignoreFonts ) ;
2020-08-29 00:55:47 +00:00
2021-02-19 16:38:22 +00:00
for ( docstring const & parXML : pars_prepend )
xs < < XMLStream : : ESCAPE_NONE < < parXML ;
2020-08-30 23:24:28 +00:00
if ( mimicListing ) {
2020-08-29 00:55:47 +00:00
auto p = pars . begin ( ) ;
2020-08-30 23:24:28 +00:00
while ( p ! = pars . end ( ) ) {
2020-11-18 04:38:25 +00:00
xml : : openTag ( xs , par - > layout ( ) . docbookiteminnertag ( ) , par - > layout ( ) . docbookiteminnerattr ( ) ,
par - > layout ( ) . docbookiteminnertagtype ( ) ) ;
2020-08-29 00:55:47 +00:00
xs < < XMLStream : : ESCAPE_NONE < < * p ;
2020-11-18 04:38:25 +00:00
xml : : closeTag ( xs , par - > layout ( ) . docbookiteminnertag ( ) , par - > layout ( ) . docbookiteminnertagtype ( ) ) ;
2020-08-29 00:55:47 +00:00
+ + p ;
2020-08-30 23:24:28 +00:00
2020-10-26 02:55:25 +00:00
// Insert a new line after each "paragraph" (i.e. line in the listing), except for the last one.
// Otherwise, there would one more new line in the output than in the LyX document.
2020-08-30 23:24:28 +00:00
if ( p ! = pars . end ( ) )
xs < < xml : : CR ( ) ;
}
} else {
for ( auto const & p : pars ) {
2020-11-18 04:38:25 +00:00
xml : : openTag ( xs , par - > layout ( ) . docbookiteminnertag ( ) , par - > layout ( ) . docbookiteminnerattr ( ) ,
par - > layout ( ) . docbookiteminnertagtype ( ) ) ;
2020-08-30 23:24:28 +00:00
xs < < XMLStream : : ESCAPE_NONE < < p ;
2020-11-18 04:38:25 +00:00
xml : : closeTag ( xs , par - > layout ( ) . docbookiteminnertag ( ) , par - > layout ( ) . docbookiteminnertagtype ( ) ) ;
2020-08-29 00:55:47 +00:00
}
}
2021-02-19 16:38:22 +00:00
for ( docstring const & parXML : pars_append )
xs < < XMLStream : : ESCAPE_NONE < < parXML ;
2020-08-29 00:55:47 +00:00
} else {
makeAny ( text , buf , xs , runparams , par ) ;
}
// Close the environment.
2020-11-26 05:22:27 +00:00
closeParTag ( xs , & * par , ( nextpar ! = end ) ? & * nextpar : nullptr , runparams ) ;
2020-08-29 00:55:47 +00:00
}
ParagraphList : : const_iterator findEndOfEnvironment (
ParagraphList : : const_iterator const & pstart ,
ParagraphList : : const_iterator const & pend )
{
// Copy-paste from XHTML. Should be factored out at some point...
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 ;
}
2020-08-29 01:46:30 +00:00
ParagraphList : : const_iterator makeListEnvironment ( Text const & text ,
Buffer const & buf ,
2020-08-29 00:55:47 +00:00
XMLStream & xs ,
OutputParams const & runparams ,
2020-08-26 22:52:06 +00:00
ParagraphList : : const_iterator const & begin )
2020-07-08 16:15:34 +00:00
{
2020-11-14 21:05:59 +00:00
// Useful variables.
2020-08-26 22:52:06 +00:00
auto par = begin ;
2020-08-02 16:58:36 +00:00
auto const end = text . paragraphs ( ) . end ( ) ;
2020-08-26 22:52:06 +00:00
auto const envend = findEndOfEnvironment ( par , end ) ;
2020-08-02 16:58:36 +00:00
2020-08-26 22:52:06 +00:00
// Output the opening tag for this environment.
Layout const & envstyle = par - > layout ( ) ;
2020-11-18 04:38:25 +00:00
xml : : openTag ( xs , envstyle . docbookwrappertag ( ) , envstyle . docbookwrapperattr ( ) , envstyle . docbookwrappertagtype ( ) ) ;
xml : : openTag ( xs , envstyle . docbooktag ( ) , envstyle . docbookattr ( ) , envstyle . docbooktagtype ( ) ) ;
2020-08-15 22:59:43 +00:00
2020-08-26 22:52:06 +00:00
// Handle the content of the list environment, item by item.
while ( par ! = envend ) {
2020-10-26 04:11:59 +00:00
// Skip this paragraph if it is both empty and the last one (otherwise, there may be deeper paragraphs after).
auto nextpar = par ;
+ + nextpar ;
if ( par - > empty ( ) & & nextpar = = envend )
break ;
2020-08-26 22:52:06 +00:00
// Open the item wrapper.
2020-10-26 04:11:59 +00:00
Layout const & style = par - > layout ( ) ;
2020-11-18 04:38:25 +00:00
xml : : openTag ( xs , style . docbookitemwrappertag ( ) , style . docbookitemwrapperattr ( ) ,
style . docbookitemwrappertagtype ( ) ) ;
2020-06-08 21:27:49 +00:00
2020-08-15 22:59:43 +00:00
// 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 " ) {
2020-08-26 22:52:06 +00:00
if ( style . labeltype = = LABEL_MANUAL ) {
// Only variablelist gets here (or similar items defined as an extension in the layout).
2020-11-18 04:38:25 +00:00
xml : : openTag ( xs , style . docbookitemlabeltag ( ) , style . docbookitemlabelattr ( ) ,
style . docbookitemlabeltagtype ( ) ) ;
2020-08-26 22:52:06 +00:00
sep = 1 + par - > firstWordDocBook ( xs , runparams ) ;
2020-11-18 04:38:25 +00:00
xml : : closeTag ( xs , style . docbookitemlabeltag ( ) , style . docbookitemlabeltagtype ( ) ) ;
2020-08-26 22:52:06 +00:00
} else {
2020-08-15 22:59:43 +00:00
// Usual cases: maybe there is something specified at the layout level. Highly unlikely, though.
docstring const lbl = par - > params ( ) . labelString ( ) ;
2020-08-26 22:52:06 +00:00
if ( ! lbl . empty ( ) ) {
2020-11-18 04:38:25 +00:00
xml : : openTag ( xs , style . docbookitemlabeltag ( ) , style . docbookitemlabelattr ( ) ,
style . docbookitemlabeltagtype ( ) ) ;
2020-08-15 22:59:43 +00:00
xs < < lbl ;
2020-11-18 04:38:25 +00:00
xml : : closeTag ( xs , style . docbookitemlabeltag ( ) , style . docbookitemlabeltagtype ( ) ) ;
2020-07-26 02:44:04 +00:00
}
2004-10-24 00:02:39 +00:00
}
}
2020-08-15 22:59:43 +00:00
2020-08-26 22:52:06 +00:00
// Open the item (after the wrapper and the label).
2020-11-18 04:38:25 +00:00
xml : : openTag ( xs , style . docbookitemtag ( ) , style . docbookitemattr ( ) , style . docbookitemtagtype ( ) ) ;
2020-08-26 22:52:06 +00:00
// Generate the content of the item.
if ( sep < par - > size ( ) ) {
2021-02-19 16:38:22 +00:00
std : : vector < docstring > pars_prepend ;
std : : vector < docstring > pars ;
std : : vector < docstring > pars_append ;
tie ( pars_prepend , pars , pars_append ) = par - > simpleDocBookOnePar ( buf , runparams ,
2020-08-26 22:52:06 +00:00
text . outerFont ( std : : distance ( text . paragraphs ( ) . begin ( ) , par ) ) , sep ) ;
2021-02-19 16:38:22 +00:00
for ( docstring const & parXML : pars_prepend )
xs < < XMLStream : : ESCAPE_NONE < < parXML ;
2020-08-26 22:52:06 +00:00
for ( auto & p : pars ) {
2020-11-18 04:38:25 +00:00
xml : : openTag ( xs , par - > layout ( ) . docbookiteminnertag ( ) , par - > layout ( ) . docbookiteminnerattr ( ) ,
par - > layout ( ) . docbookiteminnertagtype ( ) ) ;
2020-08-26 22:52:06 +00:00
xs < < XMLStream : : ESCAPE_NONE < < p ;
2020-11-18 04:38:25 +00:00
xml : : closeTag ( xs , par - > layout ( ) . docbookiteminnertag ( ) , par - > layout ( ) . docbookiteminnertagtype ( ) ) ;
2020-08-26 19:19:38 +00:00
}
2021-02-19 16:38:22 +00:00
for ( docstring const & parXML : pars_append )
xs < < XMLStream : : ESCAPE_NONE < < parXML ;
2020-08-26 22:52:06 +00:00
} else {
// DocBook doesn't like emptiness.
2020-11-18 04:38:25 +00:00
xml : : compTag ( xs , par - > layout ( ) . docbookiteminnertag ( ) , par - > layout ( ) . docbookiteminnerattr ( ) ,
par - > layout ( ) . docbookiteminnertagtype ( ) ) ;
2020-07-26 02:44:04 +00:00
}
2020-08-26 22:52:06 +00:00
// If the next item is deeper, it must go entirely within this item (do it recursively).
// By construction, with findEndOfEnvironment, depth can only stay constant or increase, never decrease.
depth_type currentDepth = par - > getDepth ( ) ;
+ + par ;
while ( par ! = envend & & par - > getDepth ( ) ! = currentDepth )
par = makeAny ( text , buf , xs , runparams , par ) ;
// Usually, this loop only makes one iteration, except in complex scenarios, like an item with a paragraph,
// a list, and another paragraph; or an item with two types of list (itemise then enumerate, for instance).
// Close the item.
2020-11-18 04:38:25 +00:00
xml : : closeTag ( xs , style . docbookitemtag ( ) , style . docbookitemtagtype ( ) ) ;
xml : : closeTag ( xs , style . docbookitemwrappertag ( ) , style . docbookitemwrappertagtype ( ) ) ;
2020-07-26 02:44:04 +00:00
}
2020-08-15 22:59:43 +00:00
2020-08-26 22:52:06 +00:00
// Close this environment in exactly the same way as it was opened.
2020-11-18 04:38:25 +00:00
xml : : closeTag ( xs , envstyle . docbooktag ( ) , envstyle . docbooktagtype ( ) ) ;
xml : : closeTag ( xs , envstyle . docbookwrappertag ( ) , envstyle . docbookwrappertagtype ( ) ) ;
2020-08-29 01:03:16 +00:00
2020-08-26 22:52:06 +00:00
return envend ;
2004-10-24 00:02:39 +00:00
}
2020-07-08 16:15:34 +00:00
void makeCommand (
2020-08-29 01:46:30 +00:00
Text const & text ,
2020-07-08 16:15:34 +00:00
Buffer const & buf ,
XMLStream & xs ,
OutputParams const & runparams ,
2020-08-15 22:59:43 +00:00
ParagraphList : : const_iterator const & par )
2020-07-08 16:15:34 +00:00
{
2020-11-14 21:05:59 +00:00
// Useful variables.
2020-08-15 22:59:43 +00:00
// Unlike XHTML, no need for labels, as they are handled by DocBook tags.
2020-08-02 16:58:36 +00:00
auto const begin = text . paragraphs ( ) . begin ( ) ;
auto const end = text . paragraphs ( ) . end ( ) ;
2020-08-15 22:59:43 +00:00
auto nextpar = par ;
2020-08-02 16:58:36 +00:00
+ + nextpar ;
// Generate this command.
2020-08-15 22:59:43 +00:00
auto prevpar = text . paragraphs ( ) . getParagraphBefore ( par ) ;
2003-11-05 12:06:20 +00:00
2021-02-19 16:38:22 +00:00
std : : vector < docstring > pars_prepend ;
std : : vector < docstring > pars ;
std : : vector < docstring > pars_append ;
tie ( pars_prepend , pars , pars_append ) = par - > simpleDocBookOnePar ( buf , runparams , text . outerFont ( distance ( begin , par ) ) ) ;
for ( docstring const & parXML : pars_prepend )
xs < < XMLStream : : ESCAPE_NONE < < parXML ;
openParTag ( xs , & * par , prevpar , runparams ) ;
2020-08-26 19:19:38 +00:00
for ( auto & parXML : pars )
// TODO: decide what to do with openParTag/closeParTag in new lines.
xs < < XMLStream : : ESCAPE_NONE < < parXML ;
2021-02-19 16:38:22 +00:00
closeParTag ( xs , & * par , ( nextpar ! = end ) ? & * nextpar : nullptr , runparams ) ;
2020-08-02 15:35:16 +00:00
2021-02-19 16:38:22 +00:00
for ( docstring const & parXML : pars_append )
xs < < XMLStream : : ESCAPE_NONE < < parXML ;
2020-06-08 21:27:49 +00:00
}
2020-08-15 22:59:43 +00:00
2020-08-29 22:35:47 +00:00
bool isLayoutSectioning ( Layout const & lay )
{
2020-10-22 03:24:55 +00:00
if ( lay . docbooksection ( ) ) // Special case: some DocBook styles must be handled as sections.
return true ;
2020-11-04 23:02:59 +00:00
else if ( lay . category ( ) = = from_utf8 ( " Sectioning " ) | | lay . docbooktag ( ) = = " section " ) // Generic case.
2020-10-22 03:24:55 +00:00
return lay . toclevel ! = Layout : : NOT_IN_TOC ;
return false ;
2020-08-29 22:35:47 +00:00
}
2020-11-04 23:02:59 +00:00
bool isLayoutSectioningOrSimilar ( Layout const & lay )
{
return isLayoutSectioning ( lay ) | | lay . docbooktag ( ) = = " bridgehead " ;
}
2020-06-08 21:27:49 +00:00
using DocBookDocumentSectioning = tuple < bool , pit_type > ;
2020-08-02 18:43:39 +00:00
struct DocBookInfoTag
{
const set < pit_type > shouldBeInInfo ;
2020-08-29 22:35:47 +00:00
const set < pit_type > mustBeInInfo ; // With the notable exception of the abstract!
2020-08-02 18:43:39 +00:00
const set < pit_type > abstract ;
2020-08-29 22:35:47 +00:00
const bool abstractLayout ;
2020-08-02 18:43:39 +00:00
pit_type bpit ;
pit_type epit ;
DocBookInfoTag ( const set < pit_type > & shouldBeInInfo , const set < pit_type > & mustBeInInfo ,
2020-08-29 22:35:47 +00:00
const set < pit_type > & abstract , bool abstractLayout , pit_type bpit , pit_type epit ) :
2020-08-02 18:43:39 +00:00
shouldBeInInfo ( shouldBeInInfo ) , mustBeInInfo ( mustBeInInfo ) , abstract ( abstract ) ,
2020-08-29 22:35:47 +00:00
abstractLayout ( abstractLayout ) , bpit ( bpit ) , epit ( epit ) { }
2020-08-02 18:43:39 +00:00
} ;
2007-05-28 22:27:45 +00:00
2004-10-24 00:02:39 +00:00
2020-06-08 21:27:49 +00:00
DocBookDocumentSectioning hasDocumentSectioning ( ParagraphList const & paragraphs , pit_type bpit , pit_type const epit ) {
bool documentHasSections = false ;
while ( bpit < epit ) {
2021-10-15 03:31:14 +00:00
LASSERT ( static_cast < size_t > ( bpit ) < paragraphs . size ( ) , return make_tuple ( documentHasSections , bpit ) ) ;
2021-10-07 00:34:07 +00:00
2020-06-08 21:27:49 +00:00
Layout const & style = paragraphs [ bpit ] . layout ( ) ;
2020-11-04 23:02:59 +00:00
documentHasSections | = isLayoutSectioningOrSimilar ( style ) ;
2020-06-08 21:27:49 +00:00
2020-08-15 22:59:43 +00:00
if ( documentHasSections )
2004-10-24 00:02:39 +00:00
break ;
2020-06-08 21:27:49 +00:00
bpit + = 1 ;
}
// Paragraphs before the first section: [ runparams.par_begin ; eppit )
return make_tuple ( documentHasSections , bpit ) ;
}
2020-08-02 14:52:33 +00:00
bool hasOnlyNotes ( Paragraph const & par )
{
2020-08-15 22:59:43 +00:00
// Precondition: the paragraph is not empty. Otherwise, the function will always return true...
2020-08-02 14:52:33 +00:00
for ( int i = 0 ; i < par . size ( ) ; + + i )
2020-08-15 22:59:43 +00:00
// If you find something that is not an inset (like actual text) or an inset that is not a note,
// return false.
2020-08-29 23:14:44 +00:00
if ( ! par . isInset ( i ) | | par . getInset ( i ) - > lyxCode ( ) ! = NOTE_CODE )
2020-08-02 14:52:33 +00:00
return false ;
2020-10-21 12:54:25 +00:00
// An empty paragraph may still require some output.
if ( par . layout ( ) . docbooksection ( ) )
return false ;
// There should be really no content here.
2020-08-02 14:52:33 +00:00
return true ;
}
2020-08-29 22:35:47 +00:00
DocBookInfoTag getParagraphsWithInfo ( ParagraphList const & paragraphs ,
pit_type bpit , pit_type const epit ,
2020-10-22 04:28:46 +00:00
// Typically, bpit is the beginning of the document and epit the end of the
// document *or* the first section.
bool documentHasSections ,
bool detectUnlayoutedAbstract
// Whether paragraphs with no specific layout should be detected as abstracts.
// For inner sections, an abstract should only be detected if it has a specific
// layout. For others, anything that might look like an abstract should be sought.
) {
2020-06-08 21:27:49 +00:00
set < pit_type > shouldBeInInfo ;
set < pit_type > mustBeInInfo ;
2020-08-29 22:35:47 +00:00
set < pit_type > abstractWithLayout ;
set < pit_type > abstractNoLayout ;
2020-06-08 21:27:49 +00:00
2021-10-13 01:01:11 +00:00
// Find the first nonempty paragraph by mutating bpit.
2020-08-02 14:52:33 +00:00
while ( bpit < epit ) {
Paragraph const & par = paragraphs [ bpit ] ;
if ( par . empty ( ) | | hasOnlyNotes ( par ) )
bpit + = 1 ;
else
break ;
}
2020-08-29 22:35:47 +00:00
// Traverse everything that might belong to <info>.
2020-08-02 18:43:39 +00:00
bool hasAbstractLayout = false ;
2020-11-26 22:32:44 +00:00
static depth_type INVALID_DEPTH = 100000 ;
depth_type abstractDepth = INVALID_DEPTH ;
2020-08-29 22:35:47 +00:00
pit_type cpit = bpit ;
for ( ; cpit < epit ; + + cpit ) {
// Skip paragraphs that don't generate anything in DocBook.
2020-08-02 18:43:39 +00:00
Paragraph const & par = paragraphs [ cpit ] ;
2020-10-22 04:28:46 +00:00
Layout const & style = par . layout ( ) ;
2020-10-26 02:55:25 +00:00
if ( hasOnlyNotes ( par ) )
2020-07-20 00:47:40 +00:00
continue ;
2020-08-29 22:35:47 +00:00
2020-10-26 02:55:25 +00:00
// There should never be any section here, except for the first paragraph (a title can be part of <info>).
// (Just a sanity check: if this fails, this function could end up processing the whole document.)
2020-11-04 23:02:59 +00:00
if ( cpit ! = bpit & & isLayoutSectioningOrSimilar ( par . layout ( ) ) ) {
2022-02-27 19:35:58 +00:00
LYXERR ( Debug : : OUTFILE , " Assertion failed: section found in potential <info> paragraphs. " ) ;
2020-08-29 22:35:47 +00:00
break ;
2020-07-20 00:47:40 +00:00
}
2020-08-29 22:35:47 +00:00
// If this is marked as an abstract by the layout, put it in the right set.
2020-10-22 04:28:46 +00:00
if ( style . docbookabstract ( ) ) {
2020-08-02 18:43:39 +00:00
hasAbstractLayout = true ;
2020-11-26 05:22:27 +00:00
abstractDepth = par . getDepth ( ) ;
2020-08-29 22:35:47 +00:00
abstractWithLayout . emplace ( cpit ) ;
continue ;
}
2020-08-02 18:43:39 +00:00
2020-11-26 05:22:27 +00:00
// Deeper paragraphs following the abstract must still be considered as part of the abstract.
// For instance, this includes lists. There should not be any other kind of paragraph in between.
2020-11-26 22:32:44 +00:00
if ( abstractDepth ! = INVALID_DEPTH & & style . docbookininfo ( ) = = " never " ) {
2020-11-26 05:22:27 +00:00
if ( par . getDepth ( ) > abstractDepth ) {
abstractWithLayout . emplace ( cpit ) ;
continue ;
}
if ( par . getDepth ( ) = = abstractDepth ) {
// This is not an abstract paragraph and it should not either be considered as part
// of it. It breaks the rule that abstract paragraphs must follow each other.
2020-11-26 22:32:44 +00:00
abstractDepth = INVALID_DEPTH ;
2020-11-26 05:22:27 +00:00
break ;
}
}
2020-08-29 22:35:47 +00:00
// Based on layout information, store this paragraph in one set: should be in <info>, must be,
2020-08-29 23:14:44 +00:00
// or abstract (either because of layout or of position).
2020-08-29 22:35:47 +00:00
if ( style . docbookininfo ( ) = = " always " )
2020-06-08 21:27:49 +00:00
mustBeInInfo . emplace ( cpit ) ;
2020-08-29 22:35:47 +00:00
else if ( style . docbookininfo ( ) = = " maybe " )
2020-06-08 21:27:49 +00:00
shouldBeInInfo . emplace ( cpit ) ;
2020-11-02 02:44:51 +00:00
else if ( documentHasSections & & ! hasAbstractLayout & & detectUnlayoutedAbstract & &
( style . docbooktag ( ) = = " NONE " | | style . docbooktag ( ) = = " para " ) & &
style . docbookwrappertag ( ) = = " NONE " )
// In this case, it is very likely that style.docbookininfo() == "never"! Be extra careful
2020-11-26 05:22:27 +00:00
// about anything that gets caught here. For instance, don't ake into account
2020-08-29 22:35:47 +00:00
abstractNoLayout . emplace ( cpit ) ;
else // This should definitely not be in <info>.
2004-10-24 00:02:39 +00:00
break ;
2020-06-08 21:27:49 +00:00
}
2020-08-29 22:35:47 +00:00
// Now, cpit points to the first paragraph that no more has things that could go in <info>.
2020-08-02 18:43:39 +00:00
// bpit is the beginning of the <info> part.
2020-08-29 22:35:47 +00:00
return DocBookInfoTag ( shouldBeInInfo , mustBeInInfo ,
hasAbstractLayout ? abstractWithLayout : abstractNoLayout ,
hasAbstractLayout , bpit , cpit ) ;
2020-06-08 21:27:49 +00:00
}
2020-08-25 00:41:07 +00:00
} // end anonymous namespace
2020-11-18 00:51:05 +00:00
std : : set < const Inset * > gatherInfo ( ParagraphList : : const_iterator par )
{
// This function has a structure highly similar to makeAny and its friends. It's only made to be called on what
// should become the document's <abstract>.
std : : set < const Inset * > values ;
// If this kind of layout should be ignored, already leave.
if ( par - > layout ( ) . docbooktag ( ) = = " IGNORE " )
return values ;
// If this should go in info, mark it as such. Dive deep into the abstract, as it may hide many things that
// DocBook doesn't want to be inside the abstract.
for ( pos_type i = 0 ; i < par - > size ( ) ; + + i ) {
if ( par - > getInset ( i ) & & par - > getInset ( i ) - > asInsetText ( ) ) {
InsetText const * inset = par - > getInset ( i ) - > asInsetText ( ) ;
if ( inset - > getLayout ( ) . docbookininfo ( ) ! = " never " ) {
values . insert ( inset ) ;
} else {
auto subpar = inset - > paragraphs ( ) . begin ( ) ;
while ( subpar ! = inset - > paragraphs ( ) . end ( ) ) {
2020-11-20 20:59:22 +00:00
auto subinfos = gatherInfo ( subpar ) ;
for ( auto & subinfo : subinfos )
values . insert ( subinfo ) ;
2020-11-18 00:51:05 +00:00
+ + subpar ;
}
}
}
}
return values ;
}
2020-09-11 23:52:58 +00:00
ParagraphList : : const_iterator makeAny ( Text const & text ,
Buffer const & buf ,
XMLStream & xs ,
OutputParams const & runparams ,
ParagraphList : : const_iterator par )
{
2020-11-18 00:51:05 +00:00
bool ignoreParagraph = false ;
// If this kind of layout should be ignored, already leave.
ignoreParagraph | = par - > layout ( ) . docbooktag ( ) = = " IGNORE " ;
// For things that should go into <info>, check the variable rp.docbook_generate_info. This does not apply to the
// abstract itself.
bool isAbstract = par - > layout ( ) . docbookabstract ( ) | | par - > layout ( ) . docbooktag ( ) = = " abstract " ;
ignoreParagraph | = ! isAbstract & & par - > layout ( ) . docbookininfo ( ) ! = " never " & & ! runparams . docbook_generate_info ;
// Switch on the type of paragraph to call the right handler.
if ( ! ignoreParagraph ) {
switch ( par - > layout ( ) . latextype ) {
case LATEX_COMMAND :
makeCommand ( text , buf , xs , runparams , par ) ;
break ;
case LATEX_ENVIRONMENT :
makeEnvironment ( text , buf , xs , runparams , par ) ;
break ;
case LATEX_LIST_ENVIRONMENT :
case LATEX_ITEM_ENVIRONMENT :
// Only case when makeAny() might consume more than one paragraph.
return makeListEnvironment ( text , buf , xs , runparams , par ) ;
case LATEX_PARAGRAPH :
makeParagraph ( text , buf , xs , runparams , par ) ;
break ;
case LATEX_BIB_ENVIRONMENT :
makeBibliography ( text , buf , xs , runparams , par ) ;
break ;
}
2020-09-11 23:52:58 +00:00
}
2020-11-18 00:51:05 +00:00
// For cases that are not lists, the next paragraph to handle is the next one.
2020-09-11 23:52:58 +00:00
+ + par ;
return par ;
}
2020-08-25 00:41:07 +00:00
xml : : FontTag docbookStartFontTag ( xml : : FontTypes 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 ) ;
}
2020-06-08 21:27:49 +00:00
2020-07-08 16:15:34 +00:00
void outputDocBookInfo (
Text const & text ,
Buffer const & buf ,
XMLStream & xs ,
OutputParams const & runparams ,
ParagraphList const & paragraphs ,
2020-08-02 18:43:39 +00:00
DocBookInfoTag const & info )
2020-07-08 16:15:34 +00:00
{
2020-07-30 23:20:08 +00:00
// 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.
2020-08-02 18:43:39 +00:00
bool hasAbstract = ! info . abstract . empty ( ) ;
2020-07-30 23:20:08 +00:00
docstring abstract ;
2020-11-18 00:51:05 +00:00
set < const Inset * > infoInsets ; // Paragraphs that should go into <info>, but are hidden in an <abstract>
// paragraph. (This happens for quite a few layouts, unfortunately.)
2020-07-30 23:20:08 +00:00
if ( hasAbstract ) {
2020-08-15 22:59:43 +00:00
// Generate the abstract XML into a string before further checks.
2020-10-26 03:19:11 +00:00
// Usually, makeAny only generates one paragraph at a time. However, for the specific case of lists, it might
// generate more than one paragraph, as indicated in the return value.
2020-07-30 23:20:08 +00:00
odocstringstream os2 ;
2020-08-29 22:35:47 +00:00
XMLStream xs2 ( os2 ) ;
2020-10-26 03:19:11 +00:00
2020-11-18 00:51:05 +00:00
auto rp = runparams ;
rp . docbook_generate_info = false ;
2020-11-26 05:22:27 +00:00
rp . docbook_ignore_wrapper = true ;
2020-11-18 00:51:05 +00:00
set < pit_type > doneParas ; // Paragraphs that have already been converted (mostly to deal with lists).
2020-10-26 03:19:11 +00:00
for ( auto const & p : info . abstract ) {
if ( doneParas . find ( p ) = = doneParas . end ( ) ) {
auto oldPar = paragraphs . iterator_at ( p ) ;
2020-11-18 00:51:05 +00:00
auto newPar = makeAny ( text , buf , xs2 , rp , oldPar ) ;
2020-11-26 05:22:27 +00:00
// Find insets that should go outside the abstract.
2020-11-20 20:59:22 +00:00
auto subinfos = gatherInfo ( oldPar ) ;
for ( auto & subinfo : subinfos )
infoInsets . insert ( subinfo ) ;
2020-10-26 03:19:11 +00:00
// Insert the indices of all the paragraphs that were just generated (typically, one).
// **Make the hypothesis that, when an abstract has a list, all its items are consecutive.**
2020-11-18 00:51:05 +00:00
// Otherwise, makeAny and makeListEnvironment would have to be adapted too.
2020-10-26 03:19:11 +00:00
pit_type id = p ;
while ( oldPar ! = newPar ) {
doneParas . emplace ( id ) ;
+ + oldPar ;
+ + id ;
}
}
}
2020-07-30 23:20:08 +00:00
2022-02-06 05:44:29 +00:00
// Actually output the abstract if there is something to do. Don't count line feeds, spaces, or comments
// in this -- even though line feeds and spaces must be properly output if there is some abstract.
2020-08-03 14:04:26 +00:00
abstract = os2 . str ( ) ;
2020-08-25 00:41:07 +00:00
docstring cleaned = abstract ;
2020-10-16 15:02:06 +00:00
cleaned . erase ( std : : remove_if ( cleaned . begin ( ) , cleaned . end ( ) , lyx : : isSpace ) , cleaned . end ( ) ) ;
2020-07-30 23:20:08 +00:00
2022-02-06 05:44:29 +00:00
size_t beginComment ;
size_t endComment ;
while ( ( beginComment = cleaned . find ( from_ascii ( " <!-- " ) ) ) ! = lyx : : docstring : : npos ) {
if ( ( endComment = cleaned . find ( from_ascii ( " --> " ) , beginComment ) ) ! = lyx : : docstring : : npos ) {
cleaned . erase ( cleaned . begin ( ) + beginComment , cleaned . begin ( ) + endComment + 3 ) ;
}
}
2020-07-30 23:20:08 +00:00
// Nothing? Then there is no abstract!
2020-08-25 00:41:07 +00:00
if ( cleaned . empty ( ) )
2020-07-30 23:20:08 +00:00
hasAbstract = false ;
}
2020-08-15 22:59:43 +00:00
// The abstract must go in <info>. Otherwise, decide whether to open <info> based on the layouts.
2020-08-02 18:43:39 +00:00
bool needInfo = ! info . mustBeInInfo . empty ( ) | | hasAbstract ;
2020-06-08 21:27:49 +00:00
// Start the <info> tag if required.
if ( needInfo ) {
xs . startDivision ( false ) ;
xs < < xml : : StartTag ( " info " ) ;
xs < < xml : : CR ( ) ;
}
2020-11-18 00:51:05 +00:00
// Output the elements that should go in <info>.
// - First, the title.
2020-08-29 22:35:47 +00:00
for ( auto pit : info . shouldBeInInfo ) // Typically, the title: these elements are so important and ubiquitous
2020-08-15 22:59:43 +00:00
// that mandating a wrapper like <info> would repel users. Thus, generate them first.
makeAny ( text , buf , xs , runparams , paragraphs . iterator_at ( pit ) ) ;
2020-10-12 17:26:37 +00:00
// If there is no title, generate one (required for the document to be valid).
// This code is called for the main document, for table cells, etc., so be precise in this condition.
2020-10-18 07:05:49 +00:00
if ( text . isMainText ( ) & & info . shouldBeInInfo . empty ( ) & & ! runparams . inInclude ) {
2020-10-12 17:26:37 +00:00
xs < < xml : : StartTag ( " title " ) ;
xs < < " Untitled Document " ;
xs < < xml : : EndTag ( " title " ) ;
xs < < xml : : CR ( ) ;
}
2020-11-18 00:51:05 +00:00
// - Then, other metadata.
for ( auto pit : info . mustBeInInfo )
makeAny ( text , buf , xs , runparams , paragraphs . iterator_at ( pit ) ) ;
for ( auto const * inset : infoInsets )
inset - > docbook ( xs , runparams ) ;
// - Finally, always output the abstract as the last item of the <info>, as it requires special treatment
// (especially if it contains several paragraphs that are empty).
2020-08-03 14:04:26 +00:00
if ( hasAbstract ) {
2020-11-26 05:22:27 +00:00
string tag = paragraphs [ * info . abstract . begin ( ) ] . layout ( ) . docbookforceabstracttag ( ) ;
if ( tag = = " NONE " )
tag = " abstract " ;
2020-08-29 22:35:47 +00:00
2020-11-26 05:22:27 +00:00
if ( ! xs . isLastTagCR ( ) )
2020-08-29 22:35:47 +00:00
xs < < xml : : CR ( ) ;
2020-11-26 05:22:27 +00:00
xs < < xml : : StartTag ( tag ) ;
xs < < xml : : CR ( ) ;
xs < < XMLStream : : ESCAPE_NONE < < abstract ;
xs < < xml : : EndTag ( tag ) ;
xs < < xml : : CR ( ) ;
2020-06-08 21:27:49 +00:00
}
// End the <info> tag if it was started.
if ( needInfo ) {
2020-08-30 19:51:40 +00:00
if ( ! xs . isLastTagCR ( ) )
xs < < xml : : CR ( ) ;
2020-06-08 21:27:49 +00:00
xs < < xml : : EndTag ( " info " ) ;
xs < < xml : : CR ( ) ;
xs . endDivision ( ) ;
}
}
2020-07-08 16:15:34 +00:00
void docbookSimpleAllParagraphs (
Text const & text ,
Buffer const & buf ,
XMLStream & xs ,
OutputParams const & runparams )
{
2020-08-03 14:04:26 +00:00
// Handle the given text, supposing it has no sections (i.e. a "simple" text). The input may vary in length
// between a single paragraph to a whole document.
2020-08-29 23:14:44 +00:00
pit_type const bpit = runparams . par_begin ;
pit_type const epit = runparams . par_end ;
ParagraphList const & paragraphs = text . paragraphs ( ) ;
2020-06-08 21:27:49 +00:00
// First, the <info> tag.
2020-10-22 04:28:46 +00:00
DocBookInfoTag info = getParagraphsWithInfo ( paragraphs , bpit , epit , false , true ) ;
2020-08-02 18:43:39 +00:00
outputDocBookInfo ( text , buf , xs , runparams , paragraphs , info ) ;
2020-06-08 21:27:49 +00:00
2020-08-03 14:04:26 +00:00
// Then, the content. It starts where the <info> ends.
2020-08-29 23:14:44 +00:00
auto par = paragraphs . iterator_at ( info . epit ) ;
2023-10-08 19:11:17 +00:00
auto par_epit = paragraphs . iterator_at ( epit ) ;
auto par_end = paragraphs . end ( ) ;
while ( par ! = par_epit & & par ! = par_end ) {
2020-08-03 14:04:26 +00:00
if ( ! hasOnlyNotes ( * par ) )
2020-08-29 00:55:47 +00:00
par = makeAny ( text , buf , xs , runparams , par ) ;
else
+ + par ;
2004-10-24 00:02:39 +00:00
}
2003-11-05 12:06:20 +00:00
}
2006-10-21 00:16:43 +00:00
2020-06-08 21:27:49 +00:00
void docbookParagraphs ( Text const & text ,
2020-07-08 16:15:34 +00:00
Buffer const & buf ,
XMLStream & xs ,
OutputParams const & runparams ) {
2020-06-08 21:27:49 +00:00
ParagraphList const & paragraphs = text . paragraphs ( ) ;
if ( runparams . par_begin = = runparams . par_end ) {
2020-07-08 16:15:34 +00:00
runparams . par_begin = 0 ;
runparams . par_end = paragraphs . size ( ) ;
2020-06-08 21:27:49 +00:00
}
pit_type bpit = runparams . par_begin ;
pit_type const epit = runparams . par_end ;
LASSERT ( bpit < epit ,
2020-07-08 16:15:34 +00:00
{
xs < < XMLStream : : ESCAPE_NONE < < " <!-- DocBook output error! --> \n " ;
return ;
} ) ;
2020-06-08 21:27:49 +00:00
2020-08-29 22:35:47 +00:00
// Detect whether the document contains sections. If there are no sections, treatment is largely simplified.
// In particular, there can't be an abstract, unless it is manually marked.
2020-06-08 21:27:49 +00:00
bool documentHasSections ;
pit_type eppit ;
tie ( documentHasSections , eppit ) = hasDocumentSectioning ( paragraphs , bpit , epit ) ;
2020-08-29 22:35:47 +00:00
// Deal with "simple" documents, i.e. those without sections.
2020-08-29 23:14:44 +00:00
if ( ! documentHasSections ) {
2020-06-08 21:27:49 +00:00
docbookSimpleAllParagraphs ( text , buf , xs , runparams ) ;
return ;
}
2020-08-29 22:35:47 +00:00
// Output the first <info> tag (or just the title).
2020-10-22 04:28:46 +00:00
DocBookInfoTag info = getParagraphsWithInfo ( paragraphs , bpit , eppit , true , true ) ;
2020-08-29 22:35:47 +00:00
outputDocBookInfo ( text , buf , xs , runparams , paragraphs , info ) ;
2020-08-29 23:14:44 +00:00
bpit = info . epit ;
2020-08-29 22:35:47 +00:00
2021-02-14 04:20:26 +00:00
// In the specific case of books, there must be parts or chapters. In some cases, star sections are used at the
// beginning for many things like acknowledgements or licenses. DocBook has tags for many of these cases, but not
// the LyX layouts... Gather everything in a <preface>, that's the closest in meaning.
// This is only useful if the things after the <info> tag are not already parts or chapters!
if ( buf . params ( ) . documentClass ( ) . docbookroot ( ) = = " book " ) {
// Check the condition on the first few elements.
bool hasPreface = false ;
pit_type pref_bpit = bpit ;
2021-02-14 04:36:37 +00:00
pit_type pref_epit = bpit ;
2021-02-14 04:20:26 +00:00
static const std : : set < std : : string > allowedElements = {
// List from https://tdg.docbook.org/tdg/5.2/book.html
" acknowledgements " , " appendix " , " article " , " bibliography " , " chapter " , " colophon " , " dedication " ,
" glossary " , " index " , " part " , " preface " , " reference " , " toc "
} ;
2021-02-14 04:36:37 +00:00
for ( ; pref_epit < epit ; + + pref_epit ) {
2021-02-14 04:20:26 +00:00
auto par = text . paragraphs ( ) . iterator_at ( pref_epit ) ;
if ( allowedElements . find ( par - > layout ( ) . docbooktag ( ) ) ! = allowedElements . end ( ) | |
allowedElements . find ( par - > layout ( ) . docbooksectiontag ( ) ) ! = allowedElements . end ( ) )
break ;
hasPreface = true ;
}
// Output a preface if required. A title is needed for the document to be valid...
if ( hasPreface ) {
xs < < xml : : StartTag ( " preface " ) ;
xs < < xml : : CR ( ) ;
xs < < xml : : StartTag ( " title " ) ;
xs < < " Preface " ;
xs < < xml : : EndTag ( " title " ) ;
xs < < xml : : CR ( ) ;
auto pref_par = text . paragraphs ( ) . iterator_at ( pref_bpit ) ;
auto pref_end = text . paragraphs ( ) . iterator_at ( pref_epit ) ;
while ( pref_par ! = pref_end ) {
// Skip paragraphs not producing any output.
if ( hasOnlyNotes ( * pref_par ) ) {
+ + pref_par ;
continue ;
}
// TODO: must sections be handled here? If so, it might be useful to extract the corresponding loop
// in the rest of this function to use the same here (and avoid copy-paste mistakes).
pref_par = makeAny ( text , buf , xs , runparams , pref_par ) ;
}
xs < < xml : : EndTag ( " preface " ) ;
xs < < xml : : CR ( ) ;
// Skip what has just been generated in the preface.
bpit = pref_epit ;
}
}
2020-11-27 23:51:27 +00:00
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.
2020-08-29 22:35:47 +00:00
// Then, iterate through the paragraphs of this document.
2020-08-29 00:55:47 +00:00
auto par = text . paragraphs ( ) . iterator_at ( bpit ) ;
auto end = text . paragraphs ( ) . iterator_at ( epit ) ;
while ( par ! = end ) {
2020-11-27 16:59:35 +00:00
// Skip paragraphs not producing any output.
2020-08-03 14:04:26 +00:00
if ( hasOnlyNotes ( * par ) ) {
2020-08-29 00:55:47 +00:00
+ + par ;
2020-06-08 21:27:49 +00:00
continue ;
}
2020-11-27 16:59:35 +00:00
OutputParams ourparams = runparams ;
2020-08-29 00:55:47 +00:00
Layout const & style = par - > layout ( ) ;
2020-06-08 21:27:49 +00:00
// Think about adding <section> and/or </section>s.
2020-11-27 01:50:03 +00:00
if ( isLayoutSectioning ( style ) | | par - > params ( ) . startOfAppendix ( ) ) {
2020-07-08 16:15:34 +00:00
int level = style . toclevel ;
2020-06-08 21:27:49 +00:00
2020-08-29 22:58:50 +00:00
// 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:
2020-07-08 16:15:34 +00:00
// - 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)
2020-11-27 00:54:00 +00:00
// Some layouts require that Layout::NOT_IN_TOC sections still cause closing of previous sections. This is
// mostly to ensure that the section is positioned at a DocBook-compatible level (acknowledgements: cannot
// be under a section!).
2020-07-08 16:15:34 +00:00
while ( ! headerLevels . empty ( ) & & level < = headerLevels . top ( ) . first ) {
2020-08-29 22:58:50 +00:00
// Output the tag only if it corresponds to a legit section.
2020-06-08 21:27:49 +00:00
int stackLevel = headerLevels . top ( ) . first ;
2020-08-29 22:58:50 +00:00
if ( stackLevel ! = Layout : : NOT_IN_TOC ) {
xs < < xml : : EndTag ( headerLevels . top ( ) . second ) ;
xs < < xml : : CR ( ) ;
}
2020-06-08 21:27:49 +00:00
headerLevels . pop ( ) ;
2020-07-08 16:15:34 +00:00
}
2020-06-08 21:27:49 +00:00
2020-07-08 16:15:34 +00:00
// Open the new section: first push it onto the stack, then output it in DocBook.
2020-11-27 01:50:03 +00:00
string sectionTag = ( par - > params ( ) . startOfAppendix ( ) ) ? " appendix " : style . docbooksectiontag ( ) ;
2020-07-08 16:15:34 +00:00
headerLevels . push ( std : : make_pair ( level , sectionTag ) ) ;
2020-06-08 21:27:49 +00:00
2020-07-08 16:15:34 +00:00
// Some sectioning-like elements should not be output (such as FrontMatter).
if ( level ! = Layout : : NOT_IN_TOC ) {
2020-06-08 21:27:49 +00:00
// 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 ) ;
2020-07-09 00:40:20 +00:00
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 ( ) ) + " \" " ;
2020-06-08 21:27:49 +00:00
2020-07-09 00:40:20 +00:00
// Don't output the ID as a DocBook <anchor>.
ourparams . docbook_anchors_to_ignore . emplace ( label - > screenLabel ( ) ) ;
2020-06-08 21:27:49 +00:00
2020-10-26 02:55:25 +00:00
// Cannot have multiple IDs per tag. If there is another ID inset in the document, it will
// be output as a DocBook anchor.
2020-07-09 00:40:20 +00:00
break ;
}
2020-06-08 21:27:49 +00:00
}
}
// Write the open tag for this section.
2020-08-29 22:58:50 +00:00
docstring attrs ;
2020-06-08 21:27:49 +00:00
if ( ! id . empty ( ) )
2020-08-29 22:58:50 +00:00
attrs = id ;
xs < < xml : : StartTag ( sectionTag , attrs ) ;
2020-06-08 21:27:49 +00:00
xs < < xml : : CR ( ) ;
2020-07-08 16:15:34 +00:00
}
2020-06-08 21:27:49 +00:00
}
// Close all sections before the bibliography.
2023-10-07 23:38:27 +00:00
// TODO: Only close all when the bibliography is at the end of the document? Or force to output the bibliography
// at the end of the document? Or don't care (as allowed by DocBook)?
2020-09-12 23:50:11 +00:00
if ( ! par - > insetList ( ) . empty ( ) ) {
2020-06-08 21:27:49 +00:00
Inset const * firstInset = par - > getInset ( 0 ) ;
2020-08-30 00:30:04 +00:00
if ( firstInset & & ( firstInset - > lyxCode ( ) = = BIBITEM_CODE | | firstInset - > lyxCode ( ) = = BIBTEX_CODE ) ) {
2020-06-08 21:27:49 +00:00
while ( ! headerLevels . empty ( ) ) {
2020-11-27 04:53:46 +00:00
// Don't close appendices before bibliographies.
if ( headerLevels . top ( ) . second = = " appendix " )
break ;
// Pop the section from the stack.
2020-06-08 21:27:49 +00:00
int level = headerLevels . top ( ) . first ;
docstring tag = from_utf8 ( " </ " + headerLevels . top ( ) . second + " > " ) ;
headerLevels . pop ( ) ;
2020-11-27 04:53:46 +00:00
// Output the tag only if it corresponds to a legit section, as the rest of the code.
2020-06-08 21:27:49 +00:00
if ( level ! = Layout : : NOT_IN_TOC ) {
xs < < XMLStream : : ESCAPE_NONE < < tag ;
xs < < xml : : CR ( ) ;
}
}
}
}
2020-10-26 02:55:25 +00:00
// Generate the <info> tag if a section was just opened.
2020-10-22 04:28:46 +00:00
// Some sections may require abstracts (mostly parts, in books: DocBookForceAbstractTag will not be NONE),
// others can still have an abstract (it must be detected so that it can be output at the right place).
2023-10-07 23:38:27 +00:00
// TODO: docbookforceabstracttag is a bit contrived here, but it does the job. Having another field just for
// this would be cleaner, but that's just for <part> and <partintro>, so it's probably not worth the effort.
2020-10-22 04:28:46 +00:00
if ( isLayoutSectioning ( style ) ) {
2020-08-29 23:14:44 +00:00
// This abstract may be found between the next paragraph and the next title.
pit_type cpit = std : : distance ( text . paragraphs ( ) . begin ( ) , par ) ;
2020-10-26 02:55:25 +00:00
pit_type ppit = std : : get < 1 > ( hasDocumentSectioning ( paragraphs , cpit + 1L , epit ) ) ;
2020-08-29 23:14:44 +00:00
// Generate this abstract (this code corresponds to parts of outputDocBookInfo).
2020-10-22 04:28:46 +00:00
DocBookInfoTag secInfo = getParagraphsWithInfo ( paragraphs , cpit , ppit , true ,
style . docbookforceabstracttag ( ) ! = " NONE " ) ;
if ( ! secInfo . mustBeInInfo . empty ( ) | | ! secInfo . shouldBeInInfo . empty ( ) | | ! secInfo . abstract . empty ( ) ) {
// Generate the <info>, if required. If DocBookForceAbstractTag != NONE, this abstract will not be in
// <info>, unlike other ("standard") abstracts.
bool hasStandardAbstract = ! secInfo . abstract . empty ( ) & & style . docbookforceabstracttag ( ) = = " NONE " ;
bool needInfo = ! secInfo . mustBeInInfo . empty ( ) | | hasStandardAbstract ;
if ( needInfo ) {
xs . startDivision ( false ) ;
xs < < xml : : StartTag ( " info " ) ;
xs < < xml : : CR ( ) ;
}
2020-08-29 23:14:44 +00:00
2020-10-22 04:28:46 +00:00
// Output the elements that should go in <info>, before and after the abstract.
for ( auto pit : secInfo . shouldBeInInfo ) // Typically, the title: these elements are so important and ubiquitous
// that mandating a wrapper like <info> would repel users. Thus, generate them first.
2020-10-26 02:55:25 +00:00
makeAny ( text , buf , xs , ourparams , paragraphs . iterator_at ( pit ) ) ;
2020-10-22 04:28:46 +00:00
for ( auto pit : secInfo . mustBeInInfo )
2020-10-26 02:55:25 +00:00
makeAny ( text , buf , xs , ourparams , paragraphs . iterator_at ( pit ) ) ;
2020-10-22 04:28:46 +00:00
// Deal with the abstract in <info> if it is standard (i.e. its tag is <abstract>).
if ( ! secInfo . abstract . empty ( ) & & hasStandardAbstract ) {
if ( ! secInfo . abstractLayout ) {
xs < < xml : : StartTag ( " abstract " ) ;
xs < < xml : : CR ( ) ;
}
for ( auto const & p : secInfo . abstract )
2020-10-26 02:55:25 +00:00
makeAny ( text , buf , xs , ourparams , paragraphs . iterator_at ( p ) ) ;
2020-10-22 04:28:46 +00:00
if ( ! secInfo . abstractLayout ) {
xs < < xml : : EndTag ( " abstract " ) ;
xs < < xml : : CR ( ) ;
}
}
2020-08-29 23:14:44 +00:00
2020-10-22 04:28:46 +00:00
// End the <info> tag if it was started.
if ( needInfo ) {
if ( ! xs . isLastTagCR ( ) )
xs < < xml : : CR ( ) ;
xs < < xml : : EndTag ( " info " ) ;
xs < < xml : : CR ( ) ;
xs . endDivision ( ) ;
}
// Deal with the abstract outside <info> if it is not standard (i.e. its tag is layout-defined).
if ( ! secInfo . abstract . empty ( ) & & ! hasStandardAbstract ) {
// Assert: style.docbookforceabstracttag() != NONE.
xs < < xml : : StartTag ( style . docbookforceabstracttag ( ) ) ;
xs < < xml : : CR ( ) ;
for ( auto const & p : secInfo . abstract )
2020-10-26 02:55:25 +00:00
makeAny ( text , buf , xs , ourparams , paragraphs . iterator_at ( p ) ) ;
2020-10-22 04:28:46 +00:00
xs < < xml : : EndTag ( style . docbookforceabstracttag ( ) ) ;
xs < < xml : : CR ( ) ;
}
2020-10-26 02:55:25 +00:00
// Skip all the text that has just been generated.
par = paragraphs . iterator_at ( secInfo . epit ) ;
} else {
// No <info> tag to generate, proceed as for normal paragraphs.
par = makeAny ( text , buf , xs , ourparams , par ) ;
2020-10-22 04:28:46 +00:00
}
2020-10-26 02:55:25 +00:00
} else {
// Generate this paragraph, as it has nothing special.
par = makeAny ( text , buf , xs , ourparams , par ) ;
2020-08-29 23:14:44 +00:00
}
2020-06-08 21:27:49 +00:00
}
// If need be, close <section>s, but only at the end of the document (otherwise, dealt with at the beginning
// of the loop).
while ( ! headerLevels . empty ( ) & & headerLevels . top ( ) . first > Layout : : NOT_IN_TOC ) {
docstring tag = from_utf8 ( " </ " + headerLevels . top ( ) . second + " > " ) ;
headerLevels . pop ( ) ;
xs < < XMLStream : : ESCAPE_NONE < < tag ;
xs < < xml : : CR ( ) ;
}
}
2020-08-03 14:04:26 +00:00
} // namespace lyx