diff options
| author | smerten <smerten@929543f6-e4f2-0310-98a6-ba3bd3dd1d04> | 2010-03-14 17:53:59 +0000 |
|---|---|---|
| committer | smerten <smerten@929543f6-e4f2-0310-98a6-ba3bd3dd1d04> | 2010-03-14 17:53:59 +0000 |
| commit | 307e008db5097a976da06f683c18cdfbd57948e1 (patch) | |
| tree | 9afae3c408f22a3fcb2bed4da11072513aa10a07 /sandbox/xml2rst | |
| parent | 16147a0d69c361cb6d025a85fc8da8c65d668245 (diff) | |
| download | docutils-307e008db5097a976da06f683c18cdfbd57948e1.tar.gz | |
Update to version V0.5.0.
git-svn-id: http://svn.code.sf.net/p/docutils/code/trunk@6263 929543f6-e4f2-0310-98a6-ba3bd3dd1d04
Diffstat (limited to 'sandbox/xml2rst')
| -rw-r--r-- | sandbox/xml2rst/README.rst | 136 | ||||
| -rw-r--r-- | sandbox/xml2rst/xml2rst-noexslt.xsl | 4136 | ||||
| -rwxr-xr-x | sandbox/xml2rst/xml2rst.py | 498 | ||||
| -rw-r--r-- | sandbox/xml2rst/xml2rst.xsl | 524 |
4 files changed, 4745 insertions, 549 deletions
diff --git a/sandbox/xml2rst/README.rst b/sandbox/xml2rst/README.rst index 94224de20..4e47b00ad 100644 --- a/sandbox/xml2rst/README.rst +++ b/sandbox/xml2rst/README.rst @@ -4,127 +4,89 @@ xml2rst .. contents:: ----------------- What is xml2rst? ----------------- +================ `xml2rst` is a tool to generate reStructuredText_ syntax back from `Docutils XML`_ input. This way one can create XML files using `Docutils XML`_ from some other format (such as ODF_) and then transform them to reStructuredText_. -It is currently implemented as an XSLT_ stylesheet. +.. _flavor: ------------- -Availability ------------- - -`xml2rst` is available through the `Docutils Subversion repository`_ -as part of the Docutils sandbox at -http://svn.berlios.de/viewcvs/docutils/trunk/sandbox/xml2rst - -Moreover you can fetch it directly from the current maintainer at -http://www.merten-home.de/FreeSoftware/xml2rst/ - ------------- -Installation ------------- - -`xml2rst` needs no special installation. Just use it with your -favorite XSLT processor such as Xalan_. Check Synopsis_ for -instructions how to do this. - ---------------------- -Copyright and license ---------------------- - -Copyright (C) 2005, 2006, 2009 by Stefan Merten and David Priest - -License is GPL_ v2 or later. - -------------- -Documentation -------------- +It is currently implemented as an XSLT_ stylesheet coming in three +flavors: -Synopsis -======== +1. ``xml2rst.xsl`` -``Xalan`` `docutils.xml` ``xml2rst.xsl`` + This version uses EXSLT_ and should be functionally equivalent to + the old version. Because using EXSLT_ this version needs EXSLT_ + capable XSLT_ processors such as xsltproc_ [#deb-xsltproc]_. -``testXSLT`` ``-xsl`` ``xml2rst.xsl`` ``-in`` `docutils.xml` + This version is currently maintained. -Options -======= + .. [#deb-xsltproc] Under Debian based operating systems try + ``apt-get install xsltproc`` -The following options are supported. They are XSLT parameters for the -whole script and must be given to the XSLT processor by the respective -option (Xalan: ``-p``, testXSLT: ``-param``). +2. ``xml2rst.py`` --param ``adornment`` `adornment_configuration` - Configures title markup to use so different styles can be requested - easily. + This script uses ``xml2rst.xsl`` but through the XSLT_ engine + available through the lxml_ package [#deb-lxml]_. - The value of the parameter must be a string made up of a sequence of - character pairs. The first character of a pair is "o" (overline) or - "u" (underline) and the second character is the character to use for - the markup. + .. [#deb-lxml] Under Debian based operating systems try ``apt-get + install python-lxml`` - The first and the second character pair is used for document title - and subtitle, the following pairs are used for section titles where - the third pair is used for the top level section title. +3. ``xml2rst-noexslt.xsl`` - Defaults to ``o=o-u=u-u~u:u.u``\ `````. + This version can be processed with every XSLT_ processor like + Xalan_ [#deb-xalan]_. --param ``fold`` `folding_length` - Configures whether long text lines in paragraphs should be folded - and to which length. This option is for input not coming from reST - which may have no internal line feeds in plain text strings. + This version is no longer maintained, though. - If folding is enabled text strings which are not in a linefeed - preserving context are first white-space normalized and then broken - according to the folding rules. Folding rules put out the first word - and continue to do so with the following words unless the next word - would cross the folding boundary. Words are delimited by - white-space. + .. [#deb-xalan] Under Debian based operating systems try ``apt-get + install xalan`` - Defaults to ``0``, i.e. no folding. +Availability +============ -Unsupported features -==================== +`xml2rst` is available through the `Docutils Subversion repository`_ +as part of the Docutils sandbox at +http://svn.berlios.de/viewcvs/docutils/trunk/sandbox/xml2rst -It is generally not possible to create an exact reproduction of an -original reStructuredText source from an intermediate XML file. The -reason is that Docutils transports pretty much but not all information -of the original source into the XML. Also the sequence of things is -changed sometimes. +Moreover you can fetch it directly from the current maintainer at +http://www.merten-home.de/FreeSoftware/xml2rst/ -However, the coverage of Docutils features of ``xml2rst`` is pretty -good. A few minor features are not supported: +Installation +============ -* Fully minimized style for literal blocks +Depending on the flavor_ you choose you need to install certain +packages to run `xml2rst`. If using an XSLT_ processor try ``perldoc +xml2rst.xsl`` for instructions how to run it. If using the script try +``perldoc xml2rst.py``. -* Substitution references for ``replace::`` substitutions +Copyright and license +===================== -* Counting roman numbers in enumerated lists +Copyright (C) 2005, 2006 by Stefan Merten and David Priest +Copyright (C) 2009, 2010 by Stefan Merten -* Special table types like ``list-table::`` and ``csv-table::`` +License is GPL_ v2 or later. Example ======= For a roundtrip try:: - rst2xml your_file.rst | xalan -xsl xml2rst.xsl | diff - your_file.rst + rst2xml your_file.rst your_file.xml ; xml2rst.py your_file.xml | diff - your_file.rst ------------ Development ------------ +=========== ToDos -===== +----- -The ``xml2rst.xsl`` contains a couple of comments marked with ``TODO`` -which contain things which should be done. +``xml2rst.xsl`` and ``xml2rst.py`` contain a couple of comments marked +with ``TODO`` which contain things which should be done. .. ############################################################################ @@ -136,8 +98,14 @@ which contain things which should be done. .. _Docutils Subversion repository: http://docutils.sourceforge.net/docs/dev/repository.html -.. _Xalan: http://xalan.apache.org/ +.. _xalan: http://xalan.apache.org/ .. _GPL: http://www.gnu.org/copyleft/gpl.html .. _ODF: http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=office + +.. _EXSLT: http://www.exslt.org/ + +.. _xsltproc: http://xmlsoft.org/XSLT/xsltproc2.html + +.. _lxml: http://codespeak.net/lxml/ diff --git a/sandbox/xml2rst/xml2rst-noexslt.xsl b/sandbox/xml2rst/xml2rst-noexslt.xsl new file mode 100644 index 000000000..a10a6acdf --- /dev/null +++ b/sandbox/xml2rst/xml2rst-noexslt.xsl @@ -0,0 +1,4136 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE xsl:stylesheet [ +<!ENTITY CR "
"> +<!-- "xml:space='preserve'" is needed for use with libxslt --> +<!-- "xmlns:xsl='http://www.w3.org/1999/XSL/Transform'" is needed for + use with xsltproc --> +<!-- Used to create a blank line --> +<!ENTITY tCR "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform' xml:space='preserve'>&CR;</xsl:text>"> +<!-- Used when the line before must be ended --> +<!ENTITY tEOL "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform' xml:space='preserve'>&CR;</xsl:text>"> +<!ENTITY tSP "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform' xml:space='preserve'> </xsl:text>"> +]> + +<!-- + Copyright (C) 2005, 2006, 2009 Stefan Merten, David Priest + + xml2rst.xsl is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +--> + +<!-- ********************************************************************** --> +<!-- ********************************************************************** --> + +<!-- These elements in the DTD need support: + + - ``colspec`` has attribute "stub %yesorno; #IMPLIED" + + - ``document`` has attribute "title CDATA #IMPLIED" + + Probably rendered by the `.. title::` directive + + --> + +<!-- +Set namespace extensions. These are used as [shortname]:[tag] throughout the +XSL-FO files. +xsl: eXtensible Stylesheet Language +u: user extensions (indicates utility 'call-template' routines defined in +these XSL files) +data: Data elements used by the stylesheet +--> +<xsl:stylesheet + version="1.0" + xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + xmlns:u="u" + xmlns:data="a" + exclude-result-prefixes="data"> + + <xsl:output + method="text" + omit-xml-declaration="yes" + indent="no"/> + + <!-- ******************************************************************** --> + <!-- ******************************************************************** --> + + <!-- Parameter to configure title markup; see manual page for description --> + <xsl:param + name="adornment" + select="'o=o-u=u-u~u`u,u.'"/> + + <!-- Parameter for folding long lines; see manual page for description --> + <xsl:param + name="fold" + select="0"/> + + <!-- ******************************************************************** --> + <!-- ******************************************************************** --> + + <xsl:variable + name="apos" + select='"'"'/> + + <xsl:variable + name="structural_elements" + select="'*document*section*topic*sidebar*'"/> + + <xsl:variable + name="structural_subelements" + select="'*title*subtitle*docinfo*decoration*transition*'"/> + + <xsl:variable + name="bibliographic_elements" + select="'*address*author*authors*contact*copyright*date*field*organization*revision*status*version*'"/> + + <xsl:variable + name="decorative_elements" + select="'*footer*header*'"/> + + <xsl:variable + name="simple_body_elements_no_substitution" + select="'*comment*doctest_block*image*literal_block*paragraph*pending*raw*rubric*target*'"/> + + <xsl:variable + name="folding_elements" + select="concat('*comment*paragraph*rubric*attribution*caption*line*', substring-after($bibliographic_elements, '*'))"/> + + <xsl:variable + name="simple_body_elements" + select="concat($simple_body_elements_no_substitution, 'substitution_definition*')"/> + + <xsl:variable + name="compound_body_elements" + select="'*admonition*attention*block_quote*bullet_list*caution*citation*compound*danger*definition_list*enumerated_list*error*field_list*figure*footnote*hint*important*line_block*note*option_list*system_message*table*tip*warning*container*'"/> + + <xsl:variable + name="body_elements" + select="concat($simple_body_elements, substring-after($compound_body_elements, '*'))"/> + + <xsl:variable + name="admonitions" + select="'*admonition*attention*caution*danger*error*hint*important*note*tip*warning*'"/> + + <xsl:variable + name="simple_body_subelements" + select="'*attribution*caption*classifier*colspec*field_name*label*line*option_argument*option_string*term*'"/> + + <xsl:variable + name="compound_body_subelements" + select="'*definition*definition_list_item*description*entry*field*field_body*legend*list_item*option*option_group*option_list_item*row*tbody*tgroup*thead*'"/> + + <xsl:variable + name="inline_elements" + select="'*abbreviation*acronym*citation_reference*emphasis*footnote_reference*generated*image*inline*literal*problematic*reference*strong*subscript*substitution_reference*superscript*target*title_reference*raw*'"/> + + <xsl:variable + name="inline_containers" + select="concat($simple_body_elements_no_substitution, substring-after($inline_elements, '*'))"/> + + <xsl:variable + name="directives" + select="'*admonition*attention*caution*comment*danger*error*footnote*hint*important*note*tip*warning*image*figure*topic*sidebar*rubric*meta*raw*citation*compound*substitution_definition*container*'"/> + + <xsl:variable + name="titled_elements" + select="'*sidebar*topic*admonition*'"/> + + <xsl:variable + name="blank_after" + select="concat($structural_elements, substring-after($structural_subelements, '*'), substring-after($body_elements, '*'))"/> + + <xsl:variable + name="adornment_characters" + select="concat($apos, '!"#$%&()*+,-./:;<=>?@[\]^_`{|}~')"/> + + <!-- ******************************************************************** --> + <!-- ******************************************************************** --> + + <!-- + Content Model: ((title, subtitle?)?, docinfo?, decoration?, + %structure.model;) + + Attributes: The document element contains only the common attributes: ids, + names, dupnames, source, and classes. + + Depending on the source of the data and the stage of processing, the + "document" may not initially contain a "title". A document title is not + directly representable in reStructuredText. Instead, a lone top-level section + may have its title promoted to become the document title, and similarly for a + lone second-level (sub)section's title to become the document subtitle. The + "docinfo" may be transformed from an initial field_list, and "decoration" is + usually constructed programmatically. + --> + <!-- == structural_element --> + <xsl:template + match="document"> + <xsl:if + test="//generated[@classes = 'sectnum']"> + <xsl:text>.. section-numbering::</xsl:text> + &tEOL; + &tCR; + </xsl:if> + <xsl:call-template + name="u:outputClass"> + <xsl:with-param + name="blankAfter" + select="true()"/> + </xsl:call-template> + <xsl:apply-templates/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: (title, %structure.model;) + Attributes: The section element contains only the common attributes: ids, + names, dupnames, source, and classes. + --> + <!-- == structural_element --> + <xsl:template + match="section"> + <xsl:call-template + name="u:outputClass"/> + <xsl:call-template + name="u:blank"/> + <xsl:apply-templates/> + </xsl:template> + + <!-- == structural_element --> + <xsl:template + match="section[@classes = 'system-messages']"/> + <!-- Ignore system messages completely --> + <!-- This should be really in `generated' --> + + <!-- ******************************************************************** --> + + <!-- + Content Model: (title, subtitle?, (%body.elements;)+) + Attributes: The sidebar element contains only the common attributes: ids, + names, dupnames, source, and classes. + --> + <!-- == structural_element == directive --> + <xsl:template + match="sidebar"> + <xsl:call-template + name="u:BandI"/> + <xsl:text>.. sidebar:: </xsl:text> + <xsl:value-of + select="title"/> + &tEOL; + <xsl:if + test="subtitle"> + <xsl:call-template + name="u:param"> + <xsl:with-param + name="name" + select="'subtitle'"/> + <xsl:with-param + name="value" + select="subtitle"/> + <xsl:with-param + name="ancestors" + select="ancestor-or-self::*"/> + </xsl:call-template> + </xsl:if> + <xsl:call-template + name="u:params"/> + <!-- Always blank line after parameter block --> + &tCR; + <xsl:apply-templates + select="*[not(self::title) and not(self::subtitle)]"/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: (title?, (%body.elements;)+) + Attributes: The topic element contains only the common attributes: ids, + names, dupnames, source, and classes. + --> + <!-- == structural_element == directive --> + <xsl:template + match="topic"> + <xsl:call-template + name="u:BandI"/> + <xsl:text>.. topic:: </xsl:text> + <xsl:value-of + select="title"/> + &tEOL; + <xsl:call-template + name="u:params"/> + <xsl:apply-templates + select="*[not(self::title)]"/> + </xsl:template> + + <!-- == structural_element == directive --> + <xsl:template + match="topic[starts-with(@classes, 'contents')]"> + <xsl:call-template + name="u:BandI"/> + <xsl:text>.. contents:: </xsl:text> + <xsl:apply-templates + select="title"/> + &tEOL; + <xsl:call-template + name="u:params"> + <xsl:with-param + name="params" + select="@*[name() != 'ids' and name() != 'names' and name() != 'classes']"/> + </xsl:call-template> + <xsl:variable + name="isLocal" + select="substring-before(@classes, ' local')"/> + <xsl:variable + name="realClassesLocal" + select="normalize-space(substring-after(@classes, 'contents'))"/> + <xsl:variable + name="realClassesNode"> + <xsl:choose> + <xsl:when + test="$isLocal"> + <xsl:value-of + select="normalize-space(substring-before($realClassesLocal, 'local'))"/> + </xsl:when> + <xsl:otherwise> + <xsl:value-of + select="$realClassesLocal"/> + </xsl:otherwise> + </xsl:choose> + </xsl:variable> + <xsl:variable + name="realClasses" + select="string($realClassesNode)"/> + <xsl:if + test="$isLocal"> + <xsl:call-template + name="u:param"> + <xsl:with-param + name="name" + select="'local'"/> + <xsl:with-param + name="value" + select="''"/> + <xsl:with-param + name="ancestors" + select="ancestor-or-self::*"/> + </xsl:call-template> + </xsl:if> + <xsl:if + test="$realClasses"> + <xsl:call-template + name="u:param"> + <xsl:with-param + name="name" + select="'class'"/> + <xsl:with-param + name="value" + select="$realClasses"/> + <xsl:with-param + name="ancestors" + select="ancestor-or-self::*"/> + </xsl:call-template> + </xsl:if> + <!-- Autogenerated content is discarded --> + &tCR; + </xsl:template> + + <!-- == structural_element == directive --> + <xsl:template + match="topic[@classes='dedication' or @classes='abstract']"> + <xsl:call-template + name="u:BandI"/> + <xsl:text>:</xsl:text> + <xsl:apply-templates + select="title"/> + <xsl:text>: </xsl:text> + &tEOL; + <xsl:apply-templates + select="*[not(self::title)]"/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: (title, (%body.elements;)+) + Attributes: The admonition element contains only the common attributes: + ids, names, dupnames, source, and classes. + --> + <!-- == compound_body_element == directive --> + <xsl:template + match="admonition"> + <xsl:call-template + name="u:BandI"/> + <xsl:text>.. admonition:: </xsl:text> + <xsl:apply-templates + select="title"/> + &tEOL; + <xsl:call-template + name="u:params"> + <xsl:with-param + name="params" + select="@*[name() != 'classes' or not(starts-with(., 'admonition-'))]"/> + </xsl:call-template> + <xsl:call-template + name="u:indent"/> + <xsl:apply-templates + select="*[not(self::title)]"/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: (%body.elements;)+ + Attributes: The note element contains only the common attributes: ids, + names, dupnames, source, and classes. + --> + <!-- == compound_body_element == directive --> + <xsl:template + match="attention | caution | danger | error | hint | important | note | tip | warning"> + <xsl:call-template + name="u:outputClass"/> + <xsl:call-template + name="u:BandI"/> + <xsl:text>.. </xsl:text> + <xsl:value-of + select="name()"/> + <xsl:text>:: </xsl:text> + <xsl:call-template + name="u:params"> + <xsl:with-param + name="params" + select="@*[name() != 'classes']"/> + </xsl:call-template> + <xsl:apply-templates/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: (header?, footer?) + Attributes: The decoration element contains only the common attributes: + ids, names, dupnames, source, and classes. + + Although the content model doesn't specifically require contents, no empty + decoration elements are ever created. + --> + <!-- == structural_subelement --> + <xsl:template + match="//document/decoration"> + <xsl:apply-templates/> + </xsl:template> + + <!-- TODO To be rendered as `.. header::` directive --> + <!-- == decorative_element --> + <xsl:template + match="//document/decoration/header"> + <xsl:apply-templates/> + </xsl:template> + + <!-- TODO To be rendered as `.. footer::` directive --> + <!-- == decorative_element --> + <xsl:template + match="//document/decoration/footer"> + <xsl:apply-templates/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: (%bibliographic.elements;)+ + Attributes: The docinfo element contains only the common attributes: ids, + names, dupnames, source, and classes. + --> + <!-- == structural_subelement --> + <xsl:template + match="docinfo"> + <xsl:call-template + name="u:outputClass"/> + <xsl:call-template + name="u:blank"/> + <xsl:apply-templates/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: ((author, organization?, address?, contact?)+) + Attributes: The authors element contains only the common attributes: ids, + names, dupnames, source, and classes. + + In reStructuredText, multiple author's names are separated with semicolons + (";") or commas (","); semicolons take precedence. There is currently no way + to represent the author's organization, address, or contact in a + reStructuredText "Authors" field. + --> + <!-- == bibliographic_element == folding_element --> + <xsl:template + match="docinfo/authors"> + <xsl:call-template + name="u:outputFolding"> + <xsl:with-param + name="prefix"> + <xsl:text>:</xsl:text> + <xsl:value-of + select="name()"/> + <xsl:text>: </xsl:text> + </xsl:with-param> + </xsl:call-template> + </xsl:template> + + <!-- + Content Model: %text.model; + Attributes: All docinfo elements contains the common attributes (ids, + names, dupnames, source, and classes) + Some docinfo elements also have xml:space. + --> + <xsl:template + match="docinfo/authors/*"> + <xsl:apply-templates/> + <!-- no semicolon after final author --> + <xsl:if + test="generate-id(current()) != generate-id(../*[last()])"> + <xsl:text>; </xsl:text> + </xsl:if> + </xsl:template> + + <!-- + Content Model: (field_name, field_body) + Attributes: The field element contains only the common attributes: ids, + names, dupnames, source, and classes. + --> + <!-- == bibliographic_element --> + <xsl:template + match="docinfo/field"> + <!-- contents handled by ordinary field lists --> + <xsl:apply-templates/> + <!-- Supply an EOL because following elements do not recognize this --> + &tEOL; + </xsl:template> + + <!-- + Content Model: %text.model; + Attributes: All docinfo elements contains the common attributes (ids, + names, dupnames, source, and classes) + Some docinfo elements also have xml:space. + --> + <!-- == bibliographic_element == folding_element --> + <xsl:template + match="docinfo/*[name()!='authors' and name()!='field']"> + <xsl:call-template + name="u:outputFolding"> + <xsl:with-param + name="prefix"> + <xsl:text>:</xsl:text> + <xsl:value-of + select="name()"/> + <xsl:text>: </xsl:text> + </xsl:with-param> + </xsl:call-template> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: EMPTY + Attributes: The transition element contains only the common attributes: + ids, names, dupnames, source, and classes. + --> + <!-- == structural_subelement --> + <xsl:template + match="transition"> + <xsl:call-template + name="u:outputClass"/> + &tCR; <!-- req: blank line before --> + <xsl:text>-----</xsl:text> + &tEOL; + <!-- Add a required blank line after unless class follows immediately --> + <xsl:if + test="not(following-sibling::*[1]/@classes)"> + &tCR; + </xsl:if> + </xsl:template> + + <!-- ******************************************************************** --> + <!-- ******************************************************************** --> + + <!-- + IFF there is a /document/title element, it is the publication's title. All + other titles will appear within sections. + + Content Model: %text.model; + Attributes: The title element contains the common attributes (ids, names, + dupnames, source, and classes), plus refid and auto. + refid is used as a backlink to a table of contents entry. + auto is used to indicate (with value "1") that the title has been + numbered automatically. + --> + <!-- == structural_subelement --> + <xsl:template + match="//document/title"> + <xsl:call-template + name="u:outputClass"/> + <xsl:variable + name="textWS"> + <!-- Catch the title text as it is rendered so its length can be + determined --> + <xsl:apply-templates/> + </xsl:variable> + <xsl:variable + name="text" + select="normalize-space($textWS)"/> + <xsl:variable + name="length" + select="string-length($text)"/> + <xsl:call-template + name="u:overline"> + <xsl:with-param + name="length" + select="$length"/> + <xsl:with-param + name="depth" + select="1"/> + </xsl:call-template> + <xsl:value-of + select="$text"/> + &tEOL; + <xsl:call-template + name="u:underline"> + <xsl:with-param + name="length" + select="$length"/> + <xsl:with-param + name="depth" + select="1"/> + </xsl:call-template> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Title Underlines are defined by their position within the tree. + + Content Model: %text.model; + Attributes: The title element contains the common attributes (ids, names, + dupnames, source, and classes), plus refid and auto. + refid is used as a backlink to a table of contents entry. + auto is used to indicate (with value "1") that the title has been + numbered automatically. + --> + <!-- == structural_subelement --> + <xsl:template + match="section/title"> + <xsl:call-template + name="u:outputClass"/> + <xsl:variable + name="textWS"> + <!-- catch the title text as it is rendered --> + <xsl:apply-templates/> + </xsl:variable> + <xsl:variable + name="text" + select="normalize-space($textWS)"/> + <xsl:variable + name="length" + select="string-length($text)"/> + <xsl:variable + name="depth" + select="count(ancestor::section)"/> + <xsl:call-template + name="u:overline"> + <xsl:with-param + name="length" + select="$length"/> + <xsl:with-param + name="depth" + select="$depth + 2"/> + </xsl:call-template> + <xsl:value-of + select="$text"/> + &tEOL; + <xsl:call-template + name="u:underline"> + <xsl:with-param + name="length" + select="$length"/> + <xsl:with-param + name="depth" + select="$depth + 2"/> + </xsl:call-template> + <!-- Add a blank line after unless structure follows immediately --> + <xsl:if + test="not(contains(concat($structural_elements, $compound_body_elements), concat('*', name(following-sibling::*[1]), '*')) or following-sibling::*[1]/@classes)"> + &tCR; + </xsl:if> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: %text.model; + Attributes: The title element contains the common attributes (ids, names, + dupnames, source, and classes), plus refid and auto. + refid is used as a backlink to a table of contents entry. + auto is used to indicate (with value "1") that the title has been + numbered automatically. + --> + <!-- == structural_subelement --> + <xsl:template + match="title"> + <xsl:call-template + name="u:outputClass"> + <xsl:with-param + name="alreadyBlanked" + select="true()"/> + </xsl:call-template> + <!-- blank line provided by parent --> + <xsl:apply-templates/> + <!-- no EOL: provided by parent --> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + IFF there is a /document/title element, it is the publication's title. All + other titles will appear within sections. + + Content Model: %text.model; + Attributes: The subtitle element contains only the common attributes: + ids, names, dupnames, source, and classes. + --> + <!-- == structural_subelement --> + <xsl:template + match="//document/subtitle"> + <xsl:call-template + name="u:outputClass"/> + <xsl:variable + name="textWS"> + <!-- Catch the title text as it is rendered --> + <xsl:apply-templates/> + </xsl:variable> + <xsl:variable + name="text" + select="normalize-space($textWS)"/> + <xsl:variable + name="length" + select="string-length($text)"/> + + <!-- always a blank line above --> + &tCR; + <xsl:call-template + name="u:overline"> + <xsl:with-param + name="length" + select="$length"/> + <xsl:with-param + name="depth" + select="2"/> + </xsl:call-template> + <xsl:value-of + select="$text"/> + &tEOL; + <xsl:call-template + name="u:underline"> + <xsl:with-param + name="length" + select="$length"/> + <xsl:with-param + name="depth" + select="2"/> + </xsl:call-template> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: %text.model; + Attributes: The subtitle element contains only the common attributes: ids, + names, dupnames, source, and classes. + --> + <!-- == structural_subelement --> + <xsl:template + match="sidebar/subtitle"> + <xsl:call-template + name="u:outputClass"/> + <xsl:call-template + name="u:indent"/> + <xsl:apply-templates/> + &tEOL; + &tCR; + </xsl:template> + + <!-- ******************************************************************** --> + <!-- ******************************************************************** --> + + <!-- + Content Model: %text.model; + Attributes: The comment element contains only the common attributes: ids, + names, dupnames, source, and classes. + --> + <!-- == simple_body_element == folding_element == directive --> + <xsl:template + match="comment"> + <xsl:call-template + name="u:BandI"/> + <xsl:call-template + name="u:outputFolding"> + <xsl:with-param + name="prefix"> + <xsl:text>.. </xsl:text> + </xsl:with-param> + </xsl:call-template> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: %text.model; + Attributes: The doctest_block element contains the common attributes (ids, + names, dupnames, source, and classes), plus xml:space. + --> + <!-- == simple_body_element --> + <xsl:template + match="doctest_block"> + <xsl:call-template + name="u:outputClass"/> + <xsl:call-template + name="u:BandI"/> + <xsl:apply-templates/> + &tEOL; + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- An image element can have various roles; they are all covered here --> + <!-- == simple_body_element == inline_element == directive --> + <xsl:template + match="image"> + <xsl:choose> + <xsl:when + test="contains($inline_containers, concat('*', name(..), '*')) and @alt"> + <!-- An inline image with an `@alt' - must be a substitution + reference --> + <xsl:text>|</xsl:text> + <!-- Original text is lost - use what we have --> + <xsl:value-of + select="@alt"/> + <xsl:text>|</xsl:text> + </xsl:when> + <xsl:otherwise> + <!-- A directive --> + <xsl:if + test="not(parent::figure)"> + <xsl:if + test="not(parent::substitution_definition)"> + <xsl:call-template + name="u:BandI"/> + <xsl:text>.. </xsl:text> + </xsl:if> + <xsl:text>image:: </xsl:text> + </xsl:if> + <xsl:value-of + select="@uri"/> + &tEOL; + <xsl:choose> + <xsl:when + test="parent::figure"> + <!-- `@classes' is special because it is in the parent --> + <xsl:if + test="../@classes"> + <xsl:call-template + name="u:param"> + <xsl:with-param + name="name" + select="'figclass'"/> + <xsl:with-param + name="value" + select="../@classes"/> + <xsl:with-param + name="ancestors" + select="ancestor::*"/> + </xsl:call-template> + </xsl:if> + <!-- `@align' is special because it is in the parent --> + <xsl:if + test="../@align"> + <xsl:call-template + name="u:param"> + <xsl:with-param + name="name" + select="'align'"/> + <xsl:with-param + name="value" + select="../@align"/> + <xsl:with-param + name="ancestors" + select="ancestor::*"/> + </xsl:call-template> + </xsl:if> + <xsl:call-template + name="u:params"> + <!-- `figure' would add one level of indentation --> + <xsl:with-param + name="ancestors" + select="ancestor::*"/> + </xsl:call-template> + </xsl:when> + <xsl:when + test="parent::substitution_definition"> + <xsl:call-template + name="u:params"> + <!-- `@alt' only for the real images --> + <xsl:with-param + name="params" + select="@*[name() != 'alt']"/> + </xsl:call-template> + </xsl:when> + <xsl:otherwise> + <xsl:call-template + name="u:params"> + <xsl:with-param + name="params" + select="@*[name() != 'ids' and name() != 'names']"/> + </xsl:call-template> + </xsl:otherwise> + </xsl:choose> + <xsl:if + test="parent::reference"> + <!-- A clickable image --> + <xsl:call-template + name="u:param"> + <xsl:with-param + name="name" + select="'target'"/> + <xsl:with-param + name="value"> + <xsl:choose> + <xsl:when + test="../@name"> + <!-- An internal link --> + <xsl:call-template + name="u:inlineReference"> + <xsl:with-param + name="text" + select="../@refid"/> + </xsl:call-template> + </xsl:when> + <xsl:otherwise> + <!-- An external link --> + <xsl:value-of + select="../@refuri"/> + </xsl:otherwise> + </xsl:choose> + </xsl:with-param> + <xsl:with-param + name="ancestors" + select="ancestor-or-self::*"/> + </xsl:call-template> + </xsl:if> + <!-- Always blank line after parameter block --> + &tCR; + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: (line_block | line)+ + Attributes: The line_block element contains the common attributes (ids, + names, dupnames, source, and classes), plus xml:space. + --> + <!-- == compound_body_element --> + <xsl:template + match="line_block"> + <xsl:call-template + name="u:outputClass"/> + <xsl:variable + name="isEmbedded" + select="name(..) = 'line_block'"/> + <xsl:if + test="not($isEmbedded)"> + <xsl:call-template + name="u:blank"/> + </xsl:if> + <xsl:apply-templates/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: %text.model; + Attributes: The line element contains the common attributes (ids, + names, dupnames, source, and classes). + --> + <!-- == simple_body_subelement == folding_element --> + <xsl:template + match="line"> + <xsl:call-template + name="u:outputClass"/> + <xsl:variable + name="indent"> + <xsl:call-template + name="u:indent"/> + </xsl:variable> + <xsl:value-of + select="$indent"/> + <xsl:choose> + <xsl:when + test="node()"> + <!-- Only for non-empty lines --> + <xsl:variable + name="lineBlockIndent"> + <!-- Very special indendation for nested `line_block's --> + <xsl:for-each + select="ancestor::line_block[position() > 1]"> + <xsl:call-template + name="u:repeat"> + <xsl:with-param + name="length" + select="4"/> + </xsl:call-template> + </xsl:for-each> + </xsl:variable> + <xsl:call-template + name="u:outputFolding"> + <xsl:with-param + name="prefix"> + <xsl:text>| </xsl:text> + <xsl:value-of + select="$lineBlockIndent"/> + </xsl:with-param> + <xsl:with-param + name="indent" + select="concat($indent, ' ', $lineBlockIndent)"/> + </xsl:call-template> + </xsl:when> + <xsl:otherwise> + <xsl:text>|</xsl:text> + &tEOL; + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: %text.model; + Attributes: The literal_block element contains the common attributes (ids, + names, dupnames, source, and classes), plus xml:space. + --> + <!-- == simple_body_element == directive --> + <xsl:template + match="literal_block"> + <xsl:choose> + <xsl:when + test=".//*[contains($inline_elements, concat('*', name(), '*'))]"> + <!-- If it contains inline elements this is a parsed-literal --> + <xsl:call-template + name="u:BandI"/> + <xsl:text>.. parsed-literal::</xsl:text> + &tEOL; + <xsl:call-template + name="u:params"/> + &tCR; + </xsl:when> + <xsl:otherwise> + <xsl:call-template + name="u:outputClass"/> + <!-- TODO Support for the (fully) minimized style would be nice but + is difficult to accomplish because there is a linefeed + already when we arrive here :-( --> + <xsl:call-template + name="u:BandI"/> + <xsl:text>::</xsl:text> + &tEOL; + &tCR; + </xsl:otherwise> + </xsl:choose> + <xsl:variable + name="isQuotedLiteral"> + <xsl:call-template + name="u:isQuotedLiteral"/> + </xsl:variable> + <!-- Indent correctly depending on quoted literal or not --> + <xsl:choose> + <xsl:when + test="string-length($isQuotedLiteral)"> + <xsl:call-template + name="u:indent"> + <xsl:with-param + name="ancestors" + select="ancestor::*"/> + </xsl:call-template> + <xsl:apply-templates + mode="quotedLiteral"/> + </xsl:when> + <xsl:otherwise> + <xsl:call-template + name="u:indent"> + <xsl:with-param + name="ancestors" + select="ancestor-or-self::*"/> + </xsl:call-template> + <xsl:apply-templates/> + </xsl:otherwise> + </xsl:choose> + &tEOL; + </xsl:template> + + <!-- Indent a text of a quoted literal containing line feeds correctly --> + <xsl:template + match="text()" + mode="quotedLiteral"> + <xsl:call-template + name="u:indentLF"> + <xsl:with-param + name="indent"> + <xsl:call-template + name="u:indent"> + <xsl:with-param + name="ancestors" + select="ancestor::*[position() > 1]"/> + </xsl:call-template> + </xsl:with-param> + </xsl:call-template> + </xsl:template> + + <!-- Determine whether `$text' is a quoted literal and return the quote + character if so --> + <xsl:template + name="u:isQuotedLiteral"> + <xsl:param + name="text" + select="text()"/> + <xsl:param + name="quote" + select="substring($text, 1, 1)"/> + <xsl:if + test="contains($adornment_characters, $quote) and substring($text, 1, 1) = $quote"> + <!-- Given quote is an adornment character and first character is this + quote --> + <xsl:choose> + <xsl:when + test="contains($text, '
')"> + <!-- Test the remaining lines --> + <xsl:call-template + name="u:isQuotedLiteral"> + <xsl:with-param + name="text" + select="substring-after($text, '
')"/> + <xsl:with-param + name="quote" + select="$quote"/> + </xsl:call-template> + </xsl:when> + <xsl:otherwise> + <!-- No more lines to test so this is a quoted literal --> + <xsl:value-of + select="$quote"/> + </xsl:otherwise> + </xsl:choose> + </xsl:if> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: %text.model; + Attributes: The paragraph element contains only the common attributes: + ids, names, dupnames, source, and classes. + --> + <!-- == simple_body_element == folding_element --> + <xsl:template + match="paragraph"> + <xsl:variable + name="previous" + select="preceding-sibling::*[1]"/> + <!-- Do indent except first element in some compound elements --> + <xsl:variable + name="needsIndent" + select="($previous or not(parent::list_item or parent::field_body or contains($admonitions, concat('*', name(..), '*')))) and name($previous) != 'label'"/> + <!-- Blank line in front if following a body element, except first + elements in some compound elements --> + <xsl:variable + name="needsBlank" + select="($previous or not(parent::list_item or ../parent::option_list_item or parent::field_body or parent::document or contains($admonitions, concat('*', name(..), '*')))) and (not($previous) or contains($body_elements, concat('*', name($previous), '*')) or name($previous) = 'title' and contains($titled_elements, concat('*', name(..), '*')) or name($previous) = 'docinfo')"/> + <xsl:if + test="$needsBlank"> + &tCR; + </xsl:if> + <xsl:if + test="$needsIndent"> + <xsl:call-template + name="u:indent"/> + </xsl:if> + <xsl:if + test="@classes"> + <!-- This paragraph has a classes attribute - always needs newline and + indent --> + <xsl:call-template + name="u:outputClass"> + <xsl:with-param + name="alreadyBlanked" + select="$needsBlank"/> + <xsl:with-param + name="alreadyIndented" + select="$needsIndent"/> + </xsl:call-template> + <xsl:call-template + name="u:BandI"/> + </xsl:if> + <xsl:call-template + name="u:outputFolding"/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- == simple_body_element --> + <xsl:template + match="pending"> + <xsl:call-template + name="u:notSupported"/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- == simple_body_element == inline_element == directive --> + <xsl:template + match="raw"> + <xsl:choose> + <xsl:when + test="contains($inline_containers, concat('*', name(..), '*'))"> + <!-- Used as a custom role --> + <!-- TODO `role' directives must be generated for user-defined raw + roles. --> + <!-- The name of the custom role is not contained in the input --> + <xsl:text>:RAW-ROLE:`</xsl:text> + <xsl:apply-templates/> + <xsl:text>`</xsl:text> + </xsl:when> + <xsl:otherwise> + <!-- A directive --> + <xsl:call-template + name="u:outputClass"/> + <xsl:call-template + name="u:BandI"/> + <xsl:text>.. raw:: </xsl:text> + <xsl:value-of + select="@format"/> + &tEOL; + <xsl:call-template + name="u:params"> + <xsl:with-param + name="params" + select="@*[name() != 'format' and name() != 'classes']"/> + </xsl:call-template> + &tCR; + <xsl:call-template + name="u:indent"> + <xsl:with-param + name="ancestors" + select="ancestor-or-self::*"/> + </xsl:call-template> + <xsl:apply-templates/> + &tEOL; + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- == simple_body_element == folding_element == directive --> + <xsl:template + match="rubric"> + <xsl:call-template + name="u:BandI"/> + <xsl:call-template + name="u:outputFolding"> + <xsl:with-param + name="prefix"> + <xsl:text>.. rubric:: </xsl:text> + </xsl:with-param> + </xsl:call-template> + <xsl:call-template + name="u:params"/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- == compound_body_element == directive --> + <xsl:template + match="compound"> + <xsl:call-template + name="u:BandI"/> + <xsl:text>.. compound::</xsl:text> + &tEOL; + <xsl:call-template + name="u:params"/> + <xsl:apply-templates/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- == compound_body_element == directive --> + <xsl:template + match="container"> + <xsl:call-template + name="u:BandI"/> + <xsl:text>.. container::</xsl:text> + <xsl:if + test="@classes"> + &tSP; + <xsl:value-of + select="@classes"/> + </xsl:if> + &tEOL; + <xsl:apply-templates/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- == simple_body_element == directive --> + <xsl:template + match="substitution_definition"> + <!-- More than one child or not a directive is a replacement text --> + <xsl:variable + name="isReplace" + select="not(* and count(node()) = 1 and contains($directives, concat('*', name(*[1]), '*')))"/> + <xsl:call-template + name="u:BandI"/> + <xsl:variable + name="prefix"> + <xsl:text>.. |</xsl:text> + <xsl:call-template + name="u:outputNames"/> + <xsl:text>| </xsl:text> + </xsl:variable> + <xsl:choose> + <xsl:when + test="$isReplace"> + <!-- TODO Substitution references for replace can not be found because + they are not marked as such; embedding them in `generated' + would be nice --> + <xsl:call-template + name="u:outputFolding"> + <xsl:with-param + name="prefix"> + <xsl:value-of + select="$prefix"/> + <xsl:text>replace:: </xsl:text> + </xsl:with-param> + </xsl:call-template> + </xsl:when> + <xsl:otherwise> + <xsl:value-of + select="$prefix"/> + <xsl:apply-templates/> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: ((%body.elements;)+, attribution?) + Attributes: The block_quote element contains only the common attributes: + ids, names, dupnames, source, and classes. + --> + <!-- == compound_body_element --> + <xsl:template + match="block_quote"> + <xsl:if + test="@classes = 'epigraph' or @classes = 'highlights' or @classes = 'pull-quote'"> + <xsl:call-template + name="u:BandI"/> + <xsl:text>.. </xsl:text> + <xsl:value-of + select="@classes"/> + <xsl:text>::</xsl:text> + &tEOL; + <xsl:call-template + name="u:params"/> + </xsl:if> + <xsl:apply-templates/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- == simple_body_subelement == folding_element --> + <xsl:template + match="attribution"> + <xsl:call-template + name="u:outputClass"/> + <!-- blank line between quote and attribution --> + &tCR; + <xsl:call-template + name="u:indent"/> + <xsl:call-template + name="u:outputFolding"> + <xsl:with-param + name="prefix"> + <xsl:text>-- </xsl:text> + </xsl:with-param> + </xsl:call-template> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- == compound_body_element == directive --> + <xsl:template + match="citation"> + <xsl:call-template + name="u:outputClass"/> + <xsl:call-template + name="u:BandI"/> + <xsl:text>.. [</xsl:text> + <xsl:value-of + select="label"/> + <xsl:text>] </xsl:text> + <xsl:apply-templates + select="*[not(self::label)]"/> + </xsl:template> + + <!-- == simple_body_subelement --> + <xsl:template + match="citation/label"> + <xsl:apply-templates/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- == compound_body_element == directive --> + <xsl:template + match="figure"> + <xsl:call-template + name="u:BandI"/> + <xsl:text>.. figure:: </xsl:text> + <xsl:apply-templates/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- == simple_body_subelement == folding_element --> + <xsl:template + match="caption"> + <xsl:call-template + name="u:indent"/> + <xsl:call-template + name="u:outputFolding"/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- == compound_body_subelement --> + <xsl:template + match="legend"> + <xsl:apply-templates/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- TODO Footnotes should continue on line of definition --> + + <!-- user-numbered footnotes lack @auto --> + <!-- == compound_body_element == directive --> + <xsl:template + match="footnote[not(@auto)]"> + <xsl:call-template + name="u:outputClass"/> + <xsl:call-template + name="u:BandI"/> + <xsl:text>.. [</xsl:text> + <xsl:apply-templates + select="label"/> + <xsl:text>] </xsl:text> + <xsl:apply-templates + select="*[not(self::label)]"/> + </xsl:template> + + <!-- autonumbered footnotes have @auto --> + <!-- if the target footnote_reference@names matches its label, it was not a + numbered-name footnote --> + <!-- == compound_body_element == directive --> + <xsl:template + match="footnote[@auto='1']"> + <xsl:call-template + name="u:outputClass"/> + <xsl:call-template + name="u:BandI"/> + <xsl:text>.. [#</xsl:text> + <xsl:if + test="@names = @ids"> + <xsl:call-template + name="u:outputNames"/> + </xsl:if> + <xsl:text>] </xsl:text> + <xsl:apply-templates + select="*[not(self::label)]"/> + </xsl:template> + + <!-- autosymboled footnotes have @auto --> + <!-- == compound_body_element == directive --> + <xsl:template + match="footnote[@auto='*']"> + <xsl:call-template + name="u:outputClass"/> + <xsl:call-template + name="u:BandI"/> + <xsl:text>.. [*] </xsl:text> + <xsl:apply-templates + select="*[not(self::label)]"/> + </xsl:template> + + <!-- == compound_body_element == directive --> + <xsl:template + match="footnote[starts-with(@names, 'TARGET_NOTE:\ ')]"> + <!-- This is not a footnote but a hint for a directive --> + <xsl:if + test="generate-id(//footnote[starts-with(@names, 'TARGET_NOTE:\ ')][1]) = generate-id(.)"> + <!-- Only for the first one --> + <xsl:call-template + name="u:BandI"/> + <!-- TODO May have a `classes` attribute --> + <xsl:text>.. target-notes::</xsl:text> + &tEOL; + </xsl:if> + </xsl:template> + + <!-- == simple_body_subelement --> + <xsl:template + match="footnote/label"> + <xsl:apply-templates/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: (list_item +) + Attributes: The bullet_list element contains the common attributes (ids, + names, dupnames, source, and classes), plus bullet. + bullet is used to record the style of bullet from the input data. + In documents processed from reStructuredText, it contains one of "-", + "+", or "*". It may be ignored in processing. + --> + <!-- == compound_body_element --> + <xsl:template + match="bullet_list"> + <xsl:call-template + name="u:outputClass"/> + <xsl:apply-templates + mode="bullet_list"/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: (definition_list_item +) + Attributes: The definition_list element contains only the common + attributes: ids, names, dupnames, source, and classes. + --> + <!-- == compound_body_element --> + <xsl:template + match="definition_list"> + <xsl:call-template + name="u:outputClass"/> + <xsl:apply-templates/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: (term, classifier?, definition) + Attributes: The definition_list_item element contains only the common + attributes: ids, names, dupnames, source, and classes. + --> + <!-- == compound_body_subelement --> + <xsl:template + match="definition_list_item"> + <xsl:call-template + name="u:outputClass"/> + <xsl:call-template + name="u:BandI"/> + <xsl:apply-templates/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: %text.model; + Attributes: The term element contains only the common attributes: ids, + names, dupnames, source, and classes. + --> + <!-- == simple_body_subelement --> + <xsl:template + match="term"> + <xsl:apply-templates/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: %text.model; + Attributes: The classifier element contains only the common attributes: + ids, names, dupnames, source, and classes. + --> + <!-- == simple_body_subelement --> + <xsl:template + match="classifier"> + <xsl:text> : </xsl:text> + <xsl:apply-templates/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: (%body.elements;)+ + Attributes: The definition element contains only the common attributes: + ids, names, dupnames, source, and classes. + --> + <!-- == compound_body_subelement --> + <xsl:template + match="definition"> + <xsl:call-template + name="u:outputClass"/> + <xsl:apply-templates/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: (list_item +) + Attributes: The enumerated_list element contains the common attributes + (ids, names, dupnames, source, and classes), plus enumtype, prefix, suffix, and + start. + enumtype is used to record the intended enumeration sequence, one + of "arabic" (1, 2, 3, ...), "loweralpha" (a, b, c, ..., z), "upperalpha" (A, + B, C, ..., Z), "lowerroman" (i, ii, iii, iv, ..., mmmmcmxcix [4999]), or + "upperroman" (I, II, III, IV, ..., MMMMCMXCIX [4999]). + prefix stores the formatting characters used before the enumerator. In + documents originating from reStructuredText data, it will contain either "" + (empty string) or "(" (left parenthesis). It may or may not affect + processing. + suffix stores the formatting characters used after the enumerator. In + documents originating from reStructuredText data, it will contain either "." + (period) or ")" (right parenthesis). Depending on the capabilities of the + output format, this attribute may or may not affect processing. + start contains the ordinal value of the first item in the list, in + decimal. For lists beginning at value 1 ("1", "a", "A", "i", or "I"), this + attribute may be omitted. + --> + <!-- == compound_body_element --> + <xsl:template + match="enumerated_list"> + <xsl:call-template + name="u:outputClass"/> + <xsl:apply-templates + mode="enumerated_list"/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: (field +) + Attributes: The field_list element contains only the common attributes: + ids, names, dupnames, source, and classes. + --> + <!-- == compound_body_element --> + <xsl:template + match="field_list"> + <xsl:call-template + name="u:outputClass"/> + <xsl:apply-templates/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: (field_name, field_body) + Attributes: The field element contains only the common attributes: ids, + names, dupnames, source, and classes. + --> + <!-- == compound_body_subelement --> + <xsl:template + match="field"> + <xsl:call-template + name="u:outputClass"/> + <xsl:call-template + name="u:BandI"/> + <xsl:apply-templates/> + </xsl:template> + + <!-- ********************************************************************** --> + + <!-- + Content Model: %text.model; + Attributes: The field_name element contains only the common attributes: + ids, names, dupnames, source, and classes. + --> + <!-- == simple_body_subelement --> + <xsl:template + match="field_name"> + <xsl:text>:</xsl:text> + <xsl:apply-templates/> + <xsl:text>: </xsl:text> + <!-- no EOL: field_body starts on same line --> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: (%body.elements;)* + Attributes: The field_body element contains only the common attributes: + ids, names, dupnames, source, and classes. + --> + <!-- == compound_body_subelement --> + <xsl:template + match="field_body"> + <xsl:call-template + name="u:outputClass"/> + <xsl:apply-templates/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: (option_list_item +) + Attributes: The option_list element contains only the common attributes: + ids, names, dupnames, source, and classes. + --> + <!-- == compound_body_element --> + <xsl:template + match="option_list"> + <xsl:call-template + name="u:outputClass"/> + <xsl:call-template + name="u:blank"/> + <xsl:apply-templates/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: (option_group, description) + Attributes: The option_list_item element contains only the common + attributes: ids, names, dupnames, source, and classes. + --> + <!-- == compound_body_subelement --> + <xsl:template + match="option_list_item"> + <xsl:call-template + name="u:outputClass"/> + <xsl:call-template + name="u:indent"/> + <xsl:apply-templates/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: (option_group, description) + Attributes: The option_group element contains only the common attributes: + ids, names, dupnames, source, and classes. + --> + <!-- == compound_body_subelement --> + <xsl:template + match="option_group"> + <xsl:apply-templates/> + &tEOL; + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: (option_string, option_argument *) + Attributes: The option element contains only the common attributes: ids, + names, dupnames, source, and classes. + --> + <!-- == compound_body_subelement --> + <xsl:template + match="option"> + <xsl:call-template + name="u:outputClass"/> + <xsl:apply-templates/> + <xsl:if + test="generate-id(current()) != generate-id(../*[last()])"> + <!-- No comma after final option --> + <xsl:text>, </xsl:text> + </xsl:if> + <!-- no EOL: description on same line --> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: (#PCDATA) + Attributes: The option_string element contains only the common attributes: + ids, names, dupnames, source, and classes. + --> + <!-- == simple_body_subelement --> + <xsl:template + match="option_string"> + <xsl:apply-templates/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: (#PCDATA) + Attributes: The option_argument element contains the common attributes + (ids, names, dupnames, source, and classes), plus delimiter. + delimiter contains the text preceding the option_argument: + either the text separating it from the option_string (typically + either "=" or " ") + or the text between option arguments (typically either "," or " "). + --> + <!-- == simple_body_subelement --> + <xsl:template + match="option_argument"> + <xsl:value-of + select="@delimiter"/> + <xsl:apply-templates/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: (%body.elements;)+ + Attributes: The description element contains only the common attributes: + ids, names, dupnames, source, and classes. + --> + <!-- == compound_body_subelement --> + <xsl:template + match="description"> + <xsl:call-template + name="u:outputClass"/> + <xsl:apply-templates/> + &tEOL; + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: (%body.elements;)+ + Attributes: The list_item element contains only the common attributes: + ids, names, dupnames, source, and classes + + BULLET LIST + bullet is used to record the style of bullet from the input data. + In documents processed from reStructuredText, it contains one of "-", + "+", or "*". It may be ignored in processing. + --> + <!-- == compound_body_subelement --> + <xsl:template + match="list_item" + mode="bullet_list"> + <xsl:call-template + name="u:outputClass"/> + <xsl:call-template + name="u:BandI"/> + <xsl:value-of + select="../@bullet"/> + &tSP; <!-- space after bullet --> + <xsl:apply-templates/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + ENUMERATED LIST + enumtype is used to record the intended enumeration sequence, one of + "arabic" (1, 2, 3, ...), "loweralpha" (a, b, c, ..., z), "upperalpha" (A, B, + C, ..., Z), "lowerroman" (i, ii, iii, iv, ..., mmmmcmxcix [4999]), or + "upperroman" (I, II, III, IV, ..., MMMMCMXCIX [4999]). + prefix stores the formatting characters used before the enumerator. In + documents originating from reStructuredText data, it will contain either "" + (empty string) or "(" (left parenthesis). It may or may not affect + processing. + suffix stores the formatting characters used after the enumerator. In + documents originating from reStructuredText data, it will contain either "." + (period) or ")" (right parenthesis). Depending on the capabilities of the + output format, this attribute may or may not affect processing. + start contains the ordinal value of the first item in the list, in + decimal. For lists beginning at value 1 ("1", "a", "A", "i", or "I"), this + attribute may be omitted. + + --> + <!-- == compound_body_subelement --> + <xsl:template + match="list_item" + mode="enumerated_list"> + <xsl:call-template + name="u:outputClass"/> + <xsl:call-template + name="u:BandI"/> + <xsl:call-template + name="u:outputEnumerator"/> + <xsl:apply-templates/> + </xsl:template> + + <!-- Outputs a complete enumerator when called in an + enumerated_list/list_item --> + <xsl:template + name="u:outputEnumerator"> + <!-- Use parent's numeration attribute --> + <xsl:variable + name="enumType" + select="../@enumtype"/> + <!-- Determine starting point --> + <xsl:variable + name="start"> + <xsl:choose> + <xsl:when + test="../@start"> + <xsl:value-of + select="../@start"/> + </xsl:when> + <xsl:otherwise> + <xsl:value-of + select="1"/> + </xsl:otherwise> + </xsl:choose> + </xsl:variable> + <!-- Determine position of this item in its real context --> + <xsl:variable + name="position"> + <xsl:variable + name="wanted" + select="generate-id()"/> + <!-- Generate the right current node list --> + <xsl:for-each + select="../list_item"> + <xsl:if + test="generate-id() = $wanted"> + <xsl:value-of + select="position()"/> + </xsl:if> + </xsl:for-each> + </xsl:variable> + <!-- Determine encoding of the number for the given numeration --> + <xsl:variable + name="cur"> + <xsl:call-template + name="u:position2Enumerator"> + <xsl:with-param + name="enumType" + select="$enumType"/> + <xsl:with-param + name="position" + select="$position"/> + <xsl:with-param + name="start" + select="$start"/> + </xsl:call-template> + </xsl:variable> + <!-- Determine encoding of the maximum number --> + <xsl:variable + name="max"> + <xsl:call-template + name="u:position2Enumerator"> + <xsl:with-param + name="enumType" + select="$enumType"/> + <xsl:with-param + name="position" + select="count(../list_item)"/> + <xsl:with-param + name="start" + select="$start"/> + </xsl:call-template> + </xsl:variable> + <!-- Output complete enumerator --> + <xsl:value-of + select="../@prefix"/> + <xsl:value-of + select="$cur"/> + <xsl:value-of + select="../@suffix"/> + <!-- Output at least one trailing space --> + &tSP; + <!-- Output more whitespace to align with the maximum enumerator --> + <xsl:if + test="$enumType != 'lowerroman' and $enumType != 'upperroman'"> + <xsl:call-template + name="u:repeat"> + <!-- Assumes that the maximum number has maximum string length --> + <xsl:with-param + name="length" + select="string-length($max) - string-length($cur)"/> + </xsl:call-template> + </xsl:if> + </xsl:template> + + <!-- Determine the right ordinal enumerator based on the parameters --> + <xsl:template + name="u:position2Enumerator"> + <xsl:param + name="enumType"/> + <xsl:param + name="start"/> + <xsl:param + name="position"/> + <!-- Determine logical number --> + <xsl:variable + name="ordinal" + select="$start - 1 + $position"/> + <xsl:choose> + <xsl:when + test="$enumType = 'arabic'"> + <xsl:value-of + select="$ordinal"/> + </xsl:when> + <xsl:when + test="$enumType = 'loweralpha'"> + <xsl:value-of + select="substring('abcdefghijklmnopqrstzuvwxyz', $ordinal, 1)"/> + </xsl:when> + <xsl:when + test="$enumType = 'upperalpha'"> + <xsl:value-of + select="substring('ABCDEFGHIJKLMNOPQRSTZUVWXYZ', $ordinal, 1)"/> + </xsl:when> + <!-- TODO Support for counting roman numbers --> + <xsl:when + test="$enumType = 'lowerroman'"> + <xsl:text>i</xsl:text> + </xsl:when> + <xsl:when + test="$enumType = 'upperroman'"> + <xsl:text>I</xsl:text> + </xsl:when> + </xsl:choose> + </xsl:template> + + <!-- ******************************************************************** --> + <!-- ******************************************************************** --> + + <!-- + Content Model: (title?, tgroup+) + Attributes: The table element contains the common attributes and: + frame, colsep, rowsep, pgwide + --> + <!-- == compound_body_element --> + <xsl:template + match="table"> + <xsl:call-template + name="u:outputClass"/> + <xsl:call-template + name="u:blank"/> + <xsl:apply-templates + select="tgroup"/> + <xsl:if + test="title"> + <!-- TODO A table title must be rendered by using the `.. table::' + directive --> + <xsl:call-template + name="u:BandI"/> + <xsl:apply-templates + select="title"/> + &tEOL; + </xsl:if> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: (colspec*, thead?, tbody) + Attributes: The tgroup element contains the common attributes and: + cols, colsep, rowsep, align + --> + <!-- == compound_body_subelement --> + <xsl:template + match="tgroup"> + <xsl:apply-templates/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: EMPTY + Attributes: The colspec element contains the common attributes and: + colnum, colname, colwidth, colsep, rowsep, align, char, charoff + + The colwidth attribute gives the width of the respective column in characters + including padding whitespace but no separator markup. + --> + <!-- == simple_body_subelement --> + <!-- @colwidth needed by children but element has no own output --> + <xsl:template + match="colspec"/> + + <!-- ******************************************************************** --> + + <!-- + Content Model: (row+) + Attributes: The thead element contains the common attributes and: + valign + --> + <!-- == compound_body_subelement --> + <xsl:template + match="thead"> + <xsl:apply-templates/> + </xsl:template> + + <!-- + Content Model: (row+) + Attributes: The tbody element contains the common attributes and: + valign + --> + <!-- == compound_body_subelement --> + <xsl:template + match="tbody"> + <xsl:apply-templates/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: (entry+) + Attributes: The row element contains the common attributes and: + rowsep, valign + --> + <!-- == compound_body_subelement --> + <xsl:template + match="row"> + <!-- Separator line above unless first row of a tbody with no previous + thead (in this case the separator line is output already as the + closing separator line of thead) --> + <xsl:if + test="position() > 1 or parent::thead or parent::tbody and not(../../thead)"> + <xsl:call-template + name="u:rowSeparatorLine"/> + </xsl:if> + <!-- Determine heights in physical lines of all entries --> + <xsl:variable + name="heights"> + <xsl:for-each + select="entry"> + <xsl:variable + name="text"> + <!-- Catch the text of all entries --> + <xsl:apply-templates/> + </xsl:variable> + <!-- Compute height of this entry; leading and trailing EOL must be + subtracted --> + <xsl:value-of + select="string-length($text) - string-length(translate($text, '
', '')) - 1"/> + &tSP; <!-- A space as a list separator --> + </xsl:for-each> + </xsl:variable> + <!-- Determine maximum height so every entry must be this high --> + <xsl:variable + name="maxHeight"> + <xsl:call-template + name="u:maxNumber"> + <xsl:with-param + name="numbers" + select="$heights"/> + </xsl:call-template> + </xsl:variable> + <!-- Output all the physical lines of this row --> + <xsl:call-template + name="u:rowLines"> + <xsl:with-param + name="currentLine" + select="1"/> + <xsl:with-param + name="maxLine" + select="$maxHeight"/> + </xsl:call-template> + <!-- Output final separator line if this is the last row --> + <xsl:if + test="position() = last()"> + <xsl:call-template + name="u:rowSeparatorLine"> + <xsl:with-param + name="char"> + <!-- Determine correct character for the separator line --> + <xsl:choose> + <xsl:when + test="parent::thead"> + <xsl:value-of + select="'='"/> + </xsl:when> + <xsl:otherwise> + <xsl:value-of + select="'-'"/> + </xsl:otherwise> + </xsl:choose> + </xsl:with-param> + </xsl:call-template> + </xsl:if> + </xsl:template> + + <!-- Output physical line $currentLine of a row and continue until + line $maxLine is output --> + <xsl:template + name="u:rowLines"> + <xsl:param + name="currentLine"/> + <xsl:param + name="maxLine"/> + <xsl:if + test="$currentLine <= $maxLine"> + <!-- If there are still physical lines to output do it --> + <xsl:call-template + name="u:indent"/> + <!-- Leading bar --> + <xsl:text>|</xsl:text> + <xsl:apply-templates> + <xsl:with-param + name="currentLine" + select="$currentLine"/> + </xsl:apply-templates> + <!-- End of this physical line --> + &tEOL; + <!-- Continue with the next physical line --> + <xsl:call-template + name="u:rowLines"> + <xsl:with-param + name="currentLine" + select="$currentLine + 1"/> + <xsl:with-param + name="maxLine" + select="$maxLine"/> + </xsl:call-template> + </xsl:if> + </xsl:template> + + <!-- Output a separator line with all the right characters --> + <xsl:template + name="u:rowSeparatorLine"> + <xsl:param + name="char" + select="'-'"/> + <xsl:call-template + name="u:indent"/> + <xsl:text>+</xsl:text> + <xsl:for-each + select="../../colspec"> + <xsl:call-template + name="u:repeat"> + <xsl:with-param + name="length" + select="@colwidth"/> + <xsl:with-param + name="chars" + select="$char"/> + </xsl:call-template> + <xsl:text>+</xsl:text> + </xsl:for-each> + &tEOL; + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: (%body.elements;)* + Attributes: The entry element contains the common attributes and: + colname, namest, morerows, colsep, rowsep, align, char, charoff, valign and + morecols + --> + <!-- == compound_body_subelement --> + <xsl:template + match="entry"> + <!-- TODO `classes` attribute needs support --> + <!-- This is called in two ways; if $currentLine = 0 all physical lines + of this entry must be output; if $currentLine > 0 the physical line + with exactly this number shall be output --> + <xsl:param + name="currentLine" + select="0"/> + <xsl:variable + name="column" + select="position() + sum(preceding-sibling::entry/@morecols)"/> + <!-- Determine width in characters needed for this entry --> + <xsl:variable + name="width"> + <xsl:call-template + name="u:computeEntryWidth"> + <xsl:with-param + name="colspecs" + select="../../../colspec"/> + <xsl:with-param + name="column" + select="$column"/> + <xsl:with-param + name="span" + select="@morecols"/> + </xsl:call-template> + </xsl:variable> + <!-- Output the entry completely or a certain physical line --> + <xsl:call-template + name="u:outputEntry"> + <xsl:with-param + name="string"> + <!-- Capture physical lines of the entry in a variable --> + <xsl:apply-templates/> + </xsl:with-param> + <xsl:with-param + name="width" + select="$width"/> + <xsl:with-param + name="expectedIndent"> + <!-- Capture indent for the entry generated by the normal template + rules to remove it later --> + <xsl:call-template + name="u:indent"/> + </xsl:with-param> + <xsl:with-param + name="outputLine" + select="$currentLine"/> + </xsl:call-template> + <!-- Final bar after the entry --> + <xsl:text>|</xsl:text> + </xsl:template> + + <!-- Compute width of the entry --> + <xsl:template + name="u:computeEntryWidth"> + <!-- The colspec elements of all columns --> + <xsl:param + name="colspecs"/> + <!-- Column of this entry --> + <xsl:param + name="column"/> + <!-- Number of columns this entry spans --> + <xsl:param + name="span" + select="0"/> + <xsl:param + name="sum" + select="0"/> + <xsl:choose> + <xsl:when + test="$span"> + <!-- If entry spans multiple columns compute their width --> + <xsl:call-template + name="u:computeEntryWidth"> + <xsl:with-param + name="colspecs" + select="$colspecs"/> + <xsl:with-param + name="column" + select="$column + 1"/> + <xsl:with-param + name="span" + select="$span - 1"/> + <!-- Add the separator character and the following column width --> + <xsl:with-param + name="sum" + select="$sum + 1 + $colspecs[$column]/@colwidth"/> + </xsl:call-template> + </xsl:when> + <xsl:otherwise> + <!-- Does not span more columns so return sum and width of this + column --> + <xsl:value-of + select="$sum + $colspecs[$column]/@colwidth"/> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <!-- Outputs one or all lines of a table entry as a string trimmed left and + padded --> + <xsl:template + name="u:outputEntry"> + <!-- Width of the entry; there is no provision for actual physical lines + longer than this width --> + <xsl:param + name="width"/> + <!-- The string containing the remaining physical lines; may be an empty + string --> + <xsl:param + name="string" + select="''"/> + <!-- The indendation which is expected to be prefixed before every + physical line --> + <xsl:param + name="expectedIndent" + select="''"/> + <!-- Is this the first call to this template --> + <xsl:param + name="isFirst" + select="true()"/> + <!-- Number of physical line to output or 0 to output all lines --> + <xsl:param + name="outputLine" + select="0"/> + <!-- Output is wanted if all or the first physical line are to be + output --> + <xsl:variable + name="doOutput" + select="$outputLine = 0 or $outputLine = 1"/> + <xsl:variable + name="stringLFHalfTrimmed"> + <xsl:choose> + <xsl:when + test="$isFirst and substring($string, 1, 1) = '
'"> + <!-- Remove leading linefeed if this is the first time --> + <xsl:value-of + select="substring($string, 2)"/> + </xsl:when> + <xsl:otherwise> + <xsl:value-of + select="$string"/> + </xsl:otherwise> + </xsl:choose> + </xsl:variable> + <xsl:variable + name="stringLFTrimmed"> + <xsl:choose> + <xsl:when + test="$isFirst and substring($stringLFHalfTrimmed, string-length($stringLFHalfTrimmed), 1) = '
'"> + <!-- Remove trailing linefeed if this is the first time --> + <xsl:value-of + select="substring($stringLFHalfTrimmed, 1, string-length($stringLFHalfTrimmed) - 1)"/> + </xsl:when> + <xsl:otherwise> + <xsl:value-of + select="$stringLFHalfTrimmed"/> + </xsl:otherwise> + </xsl:choose> + </xsl:variable> + <!-- Determine remaining lines after the first one --> + <xsl:variable + name="remainingLines"> + <xsl:if + test="contains($stringLFTrimmed, '
')"> + <xsl:value-of + select="substring-after($stringLFTrimmed, '
')"/> + </xsl:if> + </xsl:variable> + <xsl:if + test="$doOutput"> + <!-- If this physical line must be output determine the first physical + line --> + <xsl:variable + name="firstLine"> + <xsl:choose> + <xsl:when + test="string-length($remainingLines)"> + <xsl:value-of + select="substring-before($stringLFTrimmed, '
')"/> + </xsl:when> + <xsl:otherwise> + <xsl:value-of + select="$stringLFTrimmed"/> + </xsl:otherwise> + </xsl:choose> + </xsl:variable> + <!-- Remove the leading indentation from the physical line which is + brought there by the normal templates --> + <xsl:variable + name="trimmed"> + <xsl:if + test="string-length($firstLine)"> + <!-- Trim only non-empty lines --> + <xsl:value-of + select="substring-after($firstLine, $expectedIndent)"/> + </xsl:if> + </xsl:variable> + <!-- Pad the line with a leading and a trailing space --> + <xsl:variable + name="padded" + select="concat(' ', $trimmed, ' ')"/> + <!-- Output the padded value --> + <xsl:value-of + select="$padded"/> + <!-- Fill up the width of the entry with spaces --> + <xsl:if + test="$width - string-length($padded) < 0"> + <xsl:message> + <xsl:text>WARNING: Table column too narrow (minimum: </xsl:text> + <xsl:value-of + select="string-length($padded)"/> + <xsl:text>)</xsl:text> + &tEOL; + </xsl:message> + </xsl:if> + <xsl:call-template + name="u:repeat"> + <xsl:with-param + name="length" + select="$width - string-length($padded)"/> + </xsl:call-template> + </xsl:if> + <xsl:if + test="$outputLine > 1 or $outputLine = 0 and string-length($remainingLines)"> + <!-- If a following physical line must be output or if all physical + lines shall be output and there are remaining physical lines --> + <xsl:if + test="$outputLine = 0"> + <!-- Output linefeed only if we output all the lines --> + &tEOL; + </xsl:if> + <!-- Output the remaining lines --> + <xsl:call-template + name="u:outputEntry"> + <xsl:with-param + name="width" + select="$width"/> + <xsl:with-param + name="string" + select="$remainingLines"/> + <xsl:with-param + name="expectedIndent" + select="$expectedIndent"/> + <xsl:with-param + name="isFirst" + select="false()"/> + <xsl:with-param + name="outputLine"> + <xsl:choose> + <xsl:when + test="$outputLine = 0"> + <xsl:value-of + select="0"/> + </xsl:when> + <xsl:otherwise> + <xsl:value-of + select="$outputLine - 1"/> + </xsl:otherwise> + </xsl:choose> + </xsl:with-param> + </xsl:call-template> + </xsl:if> + </xsl:template> + + <!-- ******************************************************************** --> + <!-- ******************************************************************** --> + + <!-- == inline_element --> + <xsl:template + match="citation_reference"> + <xsl:call-template + name="u:bkslshEscPre"/> + <xsl:text>[</xsl:text> + <xsl:apply-templates/> + <xsl:text>]_</xsl:text> + <xsl:call-template + name="u:bkslshEscSuf"/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- == inline_element --> + <xsl:template + match="emphasis"> + <xsl:call-template + name="u:bkslshEscPre"/> + <xsl:text>*</xsl:text> + <xsl:apply-templates/> + <xsl:text>*</xsl:text> + <xsl:call-template + name="u:bkslshEscSuf"/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- user-numbered footnotes lack @auto --> + <!-- == inline_element --> + <xsl:template + match="footnote_reference[not(@auto)]"> + <xsl:call-template + name="u:bkslshEscPre"/> + <xsl:text>[</xsl:text> + <xsl:value-of + select="text()"/> + <xsl:text>]_</xsl:text> + <xsl:call-template + name="u:bkslshEscSuf"/> + <!-- child paragraph provides blank line --> + </xsl:template> + + <!-- automatically numbered footnotes have @auto --> + <!-- if @names is different from label content, it is a named footnote --> + <!-- == inline_element --> + <xsl:template + match="footnote_reference[@auto='1']"> + <xsl:variable + name="ref" + select="@refid"/> + <xsl:if + test="not(starts-with(//footnote[@ids=$ref]/@names, 'TARGET_NOTE:\ '))"> + <!-- Not a generated footnote reference for a `.. target-notes::'; + such footnote reference and the preceding space should be + embedded in `generated'! --> + <xsl:call-template + name="u:bkslshEscPre"/> + <xsl:text>[#</xsl:text> + <xsl:if + test="//footnote[@ids=$ref]/@names != //footnote[@ids=$ref]/label"> + <xsl:call-template + name="u:outputNames"> + <xsl:with-param + name="names" + select="//footnote[@ids=$ref]/@names"/> + </xsl:call-template> + </xsl:if> + <xsl:text>]_</xsl:text> + <xsl:call-template + name="u:bkslshEscSuf"/> + </xsl:if> + </xsl:template> + + <!-- automatically symboled footnotes have @auto=* --> + <!-- == inline_element --> + <xsl:template + match="footnote_reference[@auto='*']"> + <xsl:call-template + name="u:bkslshEscPre"/> + <xsl:text>[*]_</xsl:text> + <xsl:call-template + name="u:bkslshEscSuf"/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: %text.model; + --> + <!-- == inline_element --> + <xsl:template + match="literal"> + <xsl:call-template + name="u:bkslshEscPre"/> + <xsl:text>``</xsl:text> + <xsl:apply-templates/> + <xsl:text>``</xsl:text> + <xsl:call-template + name="u:bkslshEscSuf"/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- Attribute combinations found in `standard' text and other examples: + @refuri = standalone hyperlink + @ids @refid = TOC, probably all in <generated> + @name @refuri with matching <target> in document = named external hyperlink _ + @name @refuri immediately followed by matching <target> = named embedded URI _ + @name @refuri with no matching <target> in document = anonymous embedded URI __ + @anonymous @name @refuri with no matching <target> in document = anonymous explicit URI __ + @name @refid = internal cross-reference _ + @anonymous @name @refid = anonymous implicit internal reference __ + @name @refid image = clickable image to internal reference _ + @refuri image = clickable image to standalone hyperlink + + A target matches if target/@names contains the lower cased, whitespace + quoted reference/@name + --> + + <!-- Standalone hyperlink --> + <!-- == inline_element --> + <xsl:template + match="reference[not(@name or @anonymous)]"> + <xsl:call-template + name="u:bkslshEscPre"/> + <xsl:choose> + <xsl:when + test="starts-with(., 'PEP ')"> + <xsl:text>:PEP:`</xsl:text> + <xsl:value-of + select="substring-after(., 'PEP ')"/> + <xsl:text>`</xsl:text> + </xsl:when> + <xsl:when + test="starts-with(., 'RFC ')"> + <xsl:text>:RFC:`</xsl:text> + <xsl:value-of + select="substring-after(., 'RFC ')"/> + <xsl:text>`</xsl:text> + </xsl:when> + <xsl:otherwise> + <xsl:apply-templates/> + </xsl:otherwise> + </xsl:choose> + <xsl:call-template + name="u:bkslshEscSuf"/> + </xsl:template> + + <!-- External references --> + <!-- == inline_element --> + <xsl:template + match="reference[@name and @refuri]"> + <!-- Determine normalized name by downcasing it --> + <xsl:variable + name="normalized" + select="translate(normalize-space(@name), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')"/> + <xsl:variable + name="quoted"> + <xsl:call-template + name="u:quoteWhite"> + <xsl:with-param + name="string" + select="$normalized"/> + </xsl:call-template> + </xsl:variable> + <xsl:variable + name="matching" + select="//target[contains(@names, $quoted)]"/> + <xsl:call-template + name="u:inlineReference"> + <xsl:with-param + name="anonymous" + select="not($matching) or @anonymous"/> + <xsl:with-param + name="embedded" + select="not(@anonymous) and (not($matching) or generate-id(following-sibling::node()[1]) = generate-id($matching))"/> + </xsl:call-template> + </xsl:template> + + <!-- Internal references --> + <!-- == inline_element --> + <xsl:template + match="reference[@name and @refid]"> + <xsl:call-template + name="u:inlineReference"> + <xsl:with-param + name="anonymous" + select="@anonymous"/> + </xsl:call-template> + </xsl:template> + + <!-- Image references --> + <!-- == inline_element --> + <xsl:template + match="reference[image]"> + <!-- All done by the `image' tag --> + <xsl:apply-templates/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: %text.model; + --> + <!-- == inline_element --> + <xsl:template + match="strong"> + <xsl:call-template + name="u:bkslshEscPre"/> + <xsl:text>**</xsl:text> + <xsl:apply-templates/> + <xsl:text>**</xsl:text> + <xsl:call-template + name="u:bkslshEscSuf"/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- == inline_element --> + <xsl:template + match="subscript"> + <xsl:call-template + name="u:bkslshEscPre"/> + <xsl:text>:sub:`</xsl:text> + <xsl:apply-templates/> + <xsl:text>`</xsl:text> + <xsl:call-template + name="u:bkslshEscSuf"/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- == inline_element --> + <xsl:template + match="superscript"> + <xsl:call-template + name="u:bkslshEscPre"/> + <xsl:text>:sup:`</xsl:text> + <xsl:apply-templates/> + <xsl:text>`</xsl:text> + <xsl:call-template + name="u:bkslshEscSuf"/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- The target element has various roles depending on context; they are + all handled here --> + <!-- == simple_body_element == inline_element == directive --> + <xsl:template + match="target"> + <xsl:choose> + <xsl:when + test="name(preceding-sibling::*[1]) = 'reference'"> + <!-- An embedded inline target - handled by the reference itself --> + </xsl:when> + <xsl:when + test="contains($inline_containers, concat('*', name(..), '*'))"> + <!-- An inline target of some sort --> + <xsl:call-template + name="u:inlineReference"> + <xsl:with-param + name="isTarget" + select="true()"/> + </xsl:call-template> + </xsl:when> + <xsl:when + test="@anonymous"> + <!-- An anonymous target directive --> + <xsl:call-template + name="u:outputClass"/> + <xsl:call-template + name="u:BandI"/> + <xsl:text>__ </xsl:text> + <xsl:choose> + <xsl:when + test="@refid"> + <xsl:call-template + name="u:outputNamesRefid"/> + <xsl:text>_</xsl:text> + </xsl:when> + <xsl:when + test="@refuri"> + <xsl:value-of + select="@refuri"/> + </xsl:when> + </xsl:choose> + &tEOL; + </xsl:when> + <xsl:when + test="@names | @refid"> + <!-- A target directive --> + <xsl:call-template + name="u:outputClass"/> + <xsl:call-template + name="u:BandI"/> + <xsl:text>.. _</xsl:text> + <xsl:choose> + <xsl:when + test="@refid"> + <xsl:call-template + name="u:outputNamesRefid"/> + </xsl:when> + <xsl:otherwise> + <xsl:call-template + name="u:outputNames"/> + </xsl:otherwise> + </xsl:choose> + <xsl:text>:</xsl:text> + <xsl:if + test="@refuri"> + <xsl:text> </xsl:text> + <xsl:value-of + select="@refuri"/> + </xsl:if> + &tEOL; + </xsl:when> + <xsl:otherwise> + <!-- Should not happen --> + <xsl:call-template + name="u:notSupported"/> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: %text.model; + --> + <!-- == inline_element --> + <xsl:template + match="title_reference"> + <xsl:call-template + name="u:bkslshEscPre"/> + <xsl:text>`</xsl:text> + <xsl:apply-templates/> + <xsl:text>`</xsl:text> + <xsl:call-template + name="u:bkslshEscSuf"/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Content Model: %text.model; + --> + <!-- == inline_element --> + <xsl:template + match="inline"> + <!-- TODO `role' directives must be generated for plain and derived + user-defined roles. --> + <xsl:call-template + name="u:bkslshEscPre"/> + <xsl:text>:</xsl:text> + <xsl:value-of + select="@classes"/> + <xsl:text>:`</xsl:text> + <xsl:apply-templates/> + <xsl:text>`</xsl:text> + <xsl:call-template + name="u:bkslshEscSuf"/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- TODO `meta` directive must be implemented. --> + + <!-- ******************************************************************** --> + + <!-- + Docutils wraps generated elements around text that is inserted (generated) by + Docutils; i.e., text that was not in the document, like section numbers + inserted by the "sectnum" directive. + --> + <!-- == inline_element --> + <xsl:template + match="generated"/> + + <!-- == inline_element --> + <xsl:template + match="problematic"> + <!-- Simply output the contained text because this is probably the + original text--> + <xsl:value-of + select="text()"/> + </xsl:template> + + <!-- == compound_body_element --> + <xsl:template + match="system_message"/> + + <!-- ******************************************************************** --> + <!-- ******************************************************************** --> + + <!-- + When a block of text contains linefeeds, it was indented relative to a marker + on the first line + --> + <xsl:template + match="text()"> + <xsl:call-template + name="u:indentLF"/> + </xsl:template> + + <!-- ******************************************************************** --> + <!-- ******************************************************************** --> + + <!-- Add a blank line if necessary and indent --> + <xsl:template + name="u:BandI"> + <xsl:call-template + name="u:blank"/> + <xsl:call-template + name="u:indent"/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- Add a blank line in certain contexts --> + <xsl:template + name="u:blank"> + <xsl:apply-templates + mode="blankSkipInline" + select="preceding::*[1]"/> + </xsl:template> + + <!-- Find the preceding element we are really interested in and act + according to this element --> + <xsl:template + mode="blankSkipInline" + match="*"> + <xsl:choose> + <!-- Skip all inline elements and body subelements and check their + parents --> + <xsl:when + test="contains(concat($inline_elements, $simple_body_subelements, $compound_body_subelements), concat('*', name(.), '*'))"> + <xsl:apply-templates + mode="blankSkipInline" + select=".."/> + </xsl:when> + <xsl:otherwise> + <!-- Reached the type of element we decide on --> + <xsl:if + test="contains($blank_after, concat('*', name(.), '*'))"> + &tCR; + </xsl:if> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Indent a block if it's a child of... + --> + <data:lookup> + <node + name="address" + indent="10"/> + <node + name="author" + indent="9"/> + <node + name="authors" + indent="10"/> + <node + name="contact" + indent="10"/> + <node + name="copyright" + indent="12"/> + <node + name="date" + indent="7"/> + <node + name="organization" + indent="15"/> + <node + name="revision" + indent="11"/> + <node + name="status" + indent="9"/> + <node + name="version" + indent="10"/> + <!-- This is only for `bullet_list/list_item'; + `enumerated_list/list_item' is handled special --> + <node + name="list_item" + indent="2"/> + <node + name="definition_list_item" + indent="4"/> + <node + name="field_body" + indent="4"/> + <node + name="option_list_item" + indent="4"/> + <!-- This is also the indentation if block_quote comes as one of the + special directives --> + <node + name="block_quote" + indent="4"/> + <node + name="literal_block" + indent="4"/> + <node + name="attribution" + indent="3"/> + <node + name="line" + indent="2"/> + </data:lookup> + + <!-- Do indent according to ancestor --> + <xsl:template + name="u:indent"> + <!-- In some cases the ancestors to indent for need to be determined + by the calling template --> + <xsl:param + name="ancestors" + select="ancestor::*"/> + <xsl:for-each + select="$ancestors"> + <xsl:variable + name="this" + select="name()"/> + <xsl:choose> + <xsl:when + test="contains($directives, concat('*', $this, '*'))"> + <xsl:call-template + name="u:repeat"> + <!-- TODO Indentation of lines after some directives must be + indented to align with the directive instead of a + fixed indentation; however, this is rather complicated + since identation for parameters should be fixed --> + <xsl:with-param + name="length" + select="3"/> + </xsl:call-template> + </xsl:when> + <xsl:when + test="$this = 'list_item' and parent::enumerated_list"> + <!-- Enumerated list items base their indentation on the + numeration --> + <xsl:variable + name="enumerator"> + <xsl:call-template + name="u:outputEnumerator"/> + </xsl:variable> + <xsl:call-template + name="u:repeat"> + <xsl:with-param + name="length" + select="string-length($enumerator)"/> + </xsl:call-template> + </xsl:when> + <xsl:otherwise> + <xsl:call-template + name="u:repeat"> + <xsl:with-param + name="length" + select="document('')//data:lookup/node[@name=$this]/@indent"/> + </xsl:call-template> + </xsl:otherwise> + </xsl:choose> + </xsl:for-each> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- + Create a repeated character string + Shamelessly borrowed from Jeni Tennison's work on EXSLT + --> + <xsl:template + name="u:repeat"> + <xsl:param + name="length" + select="0"/> + <xsl:param + name="chars" + select="' '"/> + <xsl:choose> + <xsl:when + test="not($length) or $length <= 0 or not($chars)"/> + <xsl:otherwise> + <xsl:variable + name="string" + select="concat($chars, $chars, $chars, $chars, $chars, $chars, $chars, $chars, $chars, $chars)"/> + <xsl:choose> + <xsl:when + test="string-length($string) >= $length"> + <xsl:value-of + select="substring($string, 1, $length)"/> + </xsl:when> + <xsl:otherwise> + <xsl:call-template + name="u:repeat"> + <xsl:with-param + name="length" + select="$length"/> + <xsl:with-param + name="chars" + select="$string"/> + </xsl:call-template> + </xsl:otherwise> + </xsl:choose> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- Indent a text containing line feeds correctly --> + <xsl:template + name="u:indentLF"> + <xsl:param + name="string" + select="."/> + <!-- A fixed indentation may be given by caller --> + <xsl:param + name="indent"> + <!-- If not given compute it --> + <xsl:call-template + name="u:indent"/> + </xsl:param> + <xsl:choose> + <xsl:when + test="contains($string,'
')"> + <!-- Output first physical line --> + <xsl:value-of + select="substring-before($string, '
')"/> + &tEOL; + <!-- Indent before the next line --> + <xsl:value-of + select="$indent"/> + <!-- Output remaining physical lines --> + <xsl:call-template + name="u:indentLF"> + <xsl:with-param + name="string" + select="substring-after($string, '
')"/> + <xsl:with-param + name="indent" + select="$indent"/> + </xsl:call-template> + </xsl:when> + <!-- String does not contain newline, so just output it --> + <xsl:otherwise> + <xsl:value-of + select="$string"/> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- Do output for those elements which do fold their inline content --> + <xsl:template + name="u:outputFolding"> + <!-- The prefix text to be output before the body --> + <xsl:param + name="prefix" + select="''"/> + <!-- The indentation for this body --> + <xsl:param + name="indent"> + <xsl:call-template + name="u:indent"> + <xsl:with-param + name="ancestors" + select="ancestor-or-self::*"/> + </xsl:call-template> + </xsl:param> + <xsl:variable + name="string"> + <!-- TODO Whitespace count of inline literals must be preserved --> + <xsl:apply-templates/> + </xsl:variable> + <!-- Always output prefix with all trailing and leading spaces --> + <xsl:value-of + select="$prefix"/> + <xsl:choose> + <xsl:when + test="$fold > 0"> + <xsl:variable + name="normalized" + select="normalize-space($string)"/> + <xsl:choose> + <xsl:when + test="$string = ''"> + <!-- Empty strings need no output --> + </xsl:when> + <xsl:when + test="$normalized = ''"> + <!-- Only white-space in string; output a single space here --> + &tSP; + </xsl:when> + <xsl:otherwise> + <!-- Output leading white-space here --> + <xsl:if + test="normalize-space(substring($string, 1, 1)) = ''"> + &tSP; + </xsl:if> + <xsl:call-template + name="u:indentFold"> + <xsl:with-param + name="string" + select="$normalized"/> + <xsl:with-param + name="indent" + select="$indent"/> + <xsl:with-param + name="cursorColumn" + select="string-length($indent) + string-length($prefix)"/> + </xsl:call-template> + <!-- Output trailing white-space here --> + <xsl:if + test="normalize-space(substring($string, string-length($string), 1)) = ''"> + &tSP; + </xsl:if> + </xsl:otherwise> + </xsl:choose> + </xsl:when> + <xsl:otherwise> + <xsl:value-of + select="$string"/> + </xsl:otherwise> + </xsl:choose> + &tEOL; + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- Indent a string with folding --> + <xsl:template + name="u:indentFold"> + <!-- Normalized string to output --> + <xsl:param + name="string"/> + <!-- Indentation to use for a new line --> + <xsl:param + name="indent"/> + <!-- Current output column --> + <!-- TODO This is not a correct assumption for field definitions where + the field name effectively determines the column of the first + line --> + <xsl:param + name="cursorColumn" + select="string-length($indent)"/> + <!-- Do we start on a new (indented) line? --> + <xsl:param + name="isNewLine" + select="true()"/> + <xsl:variable + name="firstWord"> + <xsl:choose> + <!-- TODO Quoted spaces must not end a word --> + <xsl:when + test="contains($string, ' ')"> + <xsl:value-of + select="substring-before($string, ' ')"/> + </xsl:when> + <xsl:otherwise> + <xsl:value-of + select="$string"/> + </xsl:otherwise> + </xsl:choose> + </xsl:variable> + <xsl:variable + name="rest" + select="substring-after($string, ' ')"/> + <xsl:choose> + <xsl:when + test="$string = ''"/> + <xsl:when + test="$isNewLine"> + <!-- Output at least first word --> + <xsl:value-of + select="$firstWord"/> + <xsl:call-template + name="u:indentFold"> + <xsl:with-param + name="string" + select="$rest"/> + <xsl:with-param + name="indent" + select="$indent"/> + <xsl:with-param + name="cursorColumn" + select="$cursorColumn + string-length($firstWord)"/> + <xsl:with-param + name="isNewLine" + select="false()"/> + </xsl:call-template> + </xsl:when> + <xsl:when + test="$cursorColumn + 1 + string-length($firstWord) > $fold"> + <!-- Line would get too long; start new line, indent and continue --> + &tEOL; + <xsl:value-of + select="$indent"/> + <xsl:call-template + name="u:indentFold"> + <xsl:with-param + name="string" + select="$string"/> + <xsl:with-param + name="indent" + select="$indent"/> + <xsl:with-param + name="cursorColumn" + select="string-length($indent)"/> + <xsl:with-param + name="isNewLine" + select="true()"/> + </xsl:call-template> + </xsl:when> + <xsl:otherwise> + <!-- In a line and first word fits; separate and add word --> + &tSP; + <xsl:value-of + select="$firstWord"/> + <xsl:call-template + name="u:indentFold"> + <xsl:with-param + name="string" + select="$rest"/> + <xsl:with-param + name="indent" + select="$indent"/> + <xsl:with-param + name="cursorColumn" + select="$cursorColumn + 1 + string-length($firstWord)"/> + <xsl:with-param + name="isNewLine" + select="false()"/> + </xsl:call-template> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- Output attributes of the current element as a field list --> + <xsl:template + name="u:params"> + <xsl:param + name="params" + select="@*"/> + <!-- Ancestors are needed for determining indentation; caller may give + them --> + <xsl:param + name="ancestors" + select="ancestor-or-self::*"/> + <xsl:for-each + select="$params"> + <!-- Skip URIs based on parent --> + <xsl:if + test="name() != 'uri' and name() != 'xml:space'"> + <xsl:call-template + name="u:param"> + <xsl:with-param + name="ancestors" + select="$ancestors"/> + </xsl:call-template> + </xsl:if> + </xsl:for-each> + </xsl:template> + + <!-- Output one attribute of the current element as a field list --> + <xsl:template + name="u:param"> + <xsl:param + name="name" + select="name()"/> + <xsl:param + name="value" + select="."/> + <!-- Ancestors are needed for determining indentation; caller may give + them --> + <xsl:param + name="ancestors" + select="ancestor::*"/> + <xsl:call-template + name="u:indent"> + <xsl:with-param + name="ancestors" + select="$ancestors"/> + </xsl:call-template> + <xsl:text>:</xsl:text> + <xsl:choose> + <xsl:when + test="$name = 'classes'"> + <xsl:text>class</xsl:text> + </xsl:when> + <xsl:otherwise> + <xsl:value-of + select="$name"/> + </xsl:otherwise> + </xsl:choose> + <xsl:text>:</xsl:text> + <xsl:if + test="$value"> + <xsl:text> </xsl:text> + <xsl:value-of + select="$value"/> + </xsl:if> + &tEOL; + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- Output `\' or `\ ' before some inline element if necessary --> + <xsl:template + name="u:bkslshEscPre"> + <!-- Get the sibling node directly before the current element --> + <xsl:variable + name="before" + select="preceding-sibling::node()[1]"/> + <xsl:choose> + <!-- No sibling before this node --> + <xsl:when + test="not($before)"/> + <!-- Element directly before this - must be another inline element --> + <xsl:when + test="name($before)"> + <!-- So separate it by a quoted space --> + <xsl:text>\ </xsl:text> + </xsl:when> + <!-- Node directly before this is text - check it --> + <xsl:when + test="not(contains(concat($apos, ' 
	
"([{<-/:'), substring($before, string-length($before), 1)))"> + <!-- Does not end in one of the allowed characters so separate it --> + <xsl:text>\ </xsl:text> + </xsl:when> + </xsl:choose> + </xsl:template> + + <!-- Output `\' or `\ ' after some inline element if necessary --> + <xsl:template + name="u:bkslshEscSuf"> + <!-- Get the sibling node directly after the current element --> + <xsl:variable + name="after" + select="following-sibling::node()[1]"/> + <xsl:choose> + <!-- No sibling after this node --> + <xsl:when + test="not($after)"/> + <!-- Element directly after this - must be another inline element + handling itself --> + <xsl:when + test="name($after)"/> + <!-- Node directly after this is text - check it --> + <xsl:when + test="not(contains(concat($apos, ' 
	
")]}>-/:.,;!?\'), substring($after, 1, 1)))"> + <!-- Does not start with one of the allowed characters so separate + it --> + <xsl:text>\</xsl:text> + </xsl:when> + </xsl:choose> + </xsl:template> + + <!-- ******************************************************************** --> + + <xsl:template + name="u:notSupported"> + <xsl:call-template + name="u:BandI"/> + <xsl:text>######## NOT SUPPORTED: `</xsl:text> + <xsl:value-of + select="name(.)"/> + <xsl:text>' ########</xsl:text> + &tEOL; + </xsl:template> + + <!-- ******************************************************************** --> + + <xsl:template + name="u:overline"> + <!-- Length of the rendered(!) text --> + <xsl:param + name="length"/> + <!-- Depth 1 and 2 are document title and subtitle while depths + greater than 2 are normal section titles --> + <xsl:param + name="depth"/> + <!-- Test whether a overline is wanted at all --> + <xsl:if + test="substring($adornment, 2 * ($depth - 1) + 1, 1) = 'o'"> + <xsl:call-template + name="u:repeat"> + <xsl:with-param + name="length" + select="$length"/> + <xsl:with-param + name="chars" + select="substring($adornment, 2 * ($depth - 1) + 2, 1)"/> + </xsl:call-template> + &tEOL; + </xsl:if> + </xsl:template> + + <xsl:template + name="u:underline"> + <!-- Length of the rendered(!) text --> + <xsl:param + name="length"/> + <!-- Depth 1 and 2 are document title and subtitle while depths + greater than 2 are normal section titles --> + <xsl:param + name="depth"/> + <xsl:call-template + name="u:repeat"> + <xsl:with-param + name="length" + select="$length"/> + <xsl:with-param + name="chars" + select="substring($adornment, 2 * ($depth - 1) + 2, 1)"/> + </xsl:call-template> + &tEOL; + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- Output a non-standalone reference or target --> + <xsl:template + name="u:inlineReference"> + <xsl:param + name="anonymous" + select="false()"/> + <xsl:param + name="embedded" + select="false()"/> + <!-- Is this a target instead of a reference? --> + <xsl:param + name="isTarget" + select="false()"/> + <xsl:param + name="text" + select="node()"/> + <xsl:call-template + name="u:bkslshEscPre"/> + <!-- Delimiter only if link contains other than alphanumerics --> + <xsl:variable + name="delimiter"> + <xsl:if + test="* or translate($text, '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', '') or $embedded"> + <xsl:text>`</xsl:text> + </xsl:if> + </xsl:variable> + <xsl:if + test="$isTarget"> + <xsl:text>_</xsl:text> + </xsl:if> + <xsl:value-of + select="$delimiter"/> + <xsl:apply-templates + select="$text"/> + <xsl:if + test="$embedded"> + <xsl:text> <</xsl:text> + <xsl:value-of + select="@refuri"/> + <xsl:text>></xsl:text> + </xsl:if> + <xsl:value-of + select="$delimiter"/> + <xsl:if + test="not($isTarget)"> + <xsl:text>_</xsl:text> + <xsl:if + test="$anonymous"> + <xsl:text>_</xsl:text> + </xsl:if> + </xsl:if> + <xsl:call-template + name="u:bkslshEscSuf"/> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- Determine the maximum number from a whitespace separated number list --> + <xsl:template + name="u:maxNumber"> + <xsl:param + name="numbers"/> + <!-- If this is not given by the caller a list of negative numbers will + not work --> + <xsl:param + name="currentMax" + select="0"/> + <xsl:variable + name="cleanNumbers" + select="normalize-space($numbers)"/> + <xsl:choose> + <xsl:when + test="contains($cleanNumbers, ' ')"> + <xsl:variable + name="head" + select="substring-before($cleanNumbers, ' ')"/> + <xsl:choose> + <xsl:when + test="$head > $currentMax"> + <xsl:call-template + name="u:maxNumber"> + <xsl:with-param + name="numbers" + select="substring-after($cleanNumbers, ' ')"/> + <xsl:with-param + name="currentMax" + select="$head"/> + </xsl:call-template> + </xsl:when> + <xsl:otherwise> + <xsl:call-template + name="u:maxNumber"> + <xsl:with-param + name="numbers" + select="substring-after($cleanNumbers, ' ')"/> + <xsl:with-param + name="currentMax" + select="$currentMax"/> + </xsl:call-template> + </xsl:otherwise> + </xsl:choose> + </xsl:when> + <xsl:otherwise> + <xsl:choose> + <xsl:when + test="$cleanNumbers > $currentMax"> + <xsl:value-of + select="$cleanNumbers"/> + </xsl:when> + <xsl:otherwise> + <xsl:value-of + select="$currentMax"/> + </xsl:otherwise> + </xsl:choose> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- Output a class directive for the directly following element. --> + <!-- TODO A class directive can also be used as a container putting the + respective attribute to its content; however, this is not + reflected in XML - you'd need to check a sequence of elements + whether they all have the same attribute; furthermore class + settings for block quotes needs to be treated special --> + <xsl:template + name="u:outputClass"> + <!-- Blank line already output? --> + <xsl:param + name="alreadyBlanked" + select="false()"/> + <!-- Indentation already output? --> + <xsl:param + name="alreadyIndented" + select="false()"/> + <!-- Add a blank line after class directive? --> + <xsl:param + name="blankAfter" + select="false()"/> + <xsl:if + test="@classes"> + <xsl:if + test="not($alreadyBlanked)"> + <xsl:call-template + name="u:blank"/> + </xsl:if> + <xsl:if + test="not($alreadyIndented)"> + <xsl:call-template + name="u:indent"/> + </xsl:if> + <xsl:text>.. class:: </xsl:text> + <xsl:value-of + select="@classes"/> + &tEOL; + <xsl:if + test="$blankAfter"> + &tCR; + </xsl:if> + </xsl:if> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- Output a string with backslashed stripped --> + <xsl:template + name="u:outputUnbackslashed"> + <xsl:param + name="string"/> + <xsl:choose> + <xsl:when + test="not(contains($string, '\'))"> + <xsl:value-of + select="$string"/> + </xsl:when> + <xsl:otherwise> + <xsl:value-of + select="substring-before($string, '\')"/> + <xsl:call-template + name="u:outputUnbackslashed"> + <xsl:with-param + name="string" + select="substring-after($string, '\')"/> + </xsl:call-template> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <!-- Returns a name at an index --> + <xsl:template + name="u:name4Index"> + <xsl:param + name="names"/> + <xsl:param + name="index"/> + <xsl:param + name="name0" + select="''"/> + <xsl:choose> + <xsl:when + test="not(contains($names, ' '))"> + <xsl:choose> + <xsl:when + test="not($index)"> + <xsl:value-of + select="concat($name0, $names)"/> + </xsl:when> + <xsl:otherwise> + <!-- No string with this index --> + <xsl:value-of + select="''"/> + </xsl:otherwise> + </xsl:choose> + </xsl:when> + <xsl:otherwise> + <xsl:variable + name="before" + select="substring-before($names, ' ')"/> + <xsl:choose> + <xsl:when + test="substring($before, string-length($before)) = '\'"> + <!-- Quoted space found --> + <xsl:call-template + name="u:name4Index"> + <xsl:with-param + name="names" + select="substring-after($names, ' ')"/> + <xsl:with-param + name="index" + select="$index"/> + <xsl:with-param + name="name0" + select="concat($name0, $before, ' ')"/> + </xsl:call-template> + </xsl:when> + <xsl:otherwise> + <!-- Separating space found --> + <xsl:choose> + <xsl:when + test="not($index)"> + <xsl:value-of + select="concat($name0, $before)"/> + </xsl:when> + <xsl:otherwise> + <xsl:call-template + name="u:name4Index"> + <xsl:with-param + name="names" + select="substring-after($names, ' ')"/> + <xsl:with-param + name="index" + select="$index - 1"/> + </xsl:call-template> + </xsl:otherwise> + </xsl:choose> + </xsl:otherwise> + </xsl:choose> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <!-- Output a names attribute at index considering quoted spaces. --> + <xsl:template + name="u:outputNames"> + <!-- Blank line already output? --> + <xsl:param + name="names" + select="@names"/> + <xsl:param + name="index" + select="0"/> + <xsl:variable + name="name"> + <xsl:call-template + name="u:name4Index"> + <xsl:with-param + name="names" + select="$names"/> + <xsl:with-param + name="index" + select="$index"/> + </xsl:call-template> + </xsl:variable> + <xsl:call-template + name="u:outputUnbackslashed"> + <xsl:with-param + name="string" + select="$name"/> + </xsl:call-template> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- Finds an id in a list and returns its index or empty string if not + contained exactly. --> + <xsl:template + name="u:findId"> + <xsl:param + name="list"/> + <xsl:param + name="id"/> + <xsl:param + name="index" + select="0"/> + <xsl:choose> + <xsl:when + test="$list = ''"> + <xsl:value-of + select="''"/> + </xsl:when> + <xsl:when + test="$list = $id or starts-with($list, concat($id, ' '))"> + <xsl:value-of + select="$index"/> + </xsl:when> + <xsl:otherwise> + <xsl:call-template + name="u:findId"> + <xsl:with-param + name="list" + select="substring-after($list, ' ')"/> + <xsl:with-param + name="id" + select="$id"/> + <xsl:with-param + name="index" + select="$index + 1"/> + </xsl:call-template> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <!-- In a number of elements find the one containing exactly id and return + the index of the element as well as the index of the id --> + <xsl:template + name="u:findRefElem"> + <xsl:param + name="possibleElems"/> + <xsl:param + name="id"/> + <!-- XSLT is 1-based --> + <xsl:param + name="elemIndex" + select="1"/> + <xsl:variable + name="elem" + select="$possibleElems[1]"/> + <xsl:choose> + <!-- No more elements - return empty string --> + <xsl:when + test="not($possibleElems)"> + <xsl:value-of + select="''"/> + </xsl:when> + <xsl:otherwise> + <!-- Check whether the first element contains the id --> + <xsl:variable + name="fnd"> + <xsl:call-template + name="u:findId"> + <xsl:with-param + name="list" + select="$elem/@ids"/> + <xsl:with-param + name="id" + select="$id"/> + </xsl:call-template> + </xsl:variable> + <xsl:choose> + <xsl:when + test="$fnd != ''"> + <xsl:value-of + select="concat($elemIndex, ' ', $fnd)"/> + </xsl:when> + <xsl:otherwise> + <xsl:call-template + name="u:findRefElem"> + <xsl:with-param + name="possibleElems" + select="$possibleElems[position() != 1]"/> + <xsl:with-param + name="id" + select="$id"/> + <xsl:with-param + name="elemIndex" + select="$elemIndex + 1"/> + </xsl:call-template> + </xsl:otherwise> + </xsl:choose> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <!-- Output a names attribute for a refid. --> + <xsl:template + name="u:outputNamesRefid"> + <xsl:param + name="refid" + select="@refid"/> + <!-- Determine all possible elements which might be referred --> + <xsl:variable + name="possibleElems" + select="//*[contains(@ids, $refid)]"/> + <xsl:variable + name="refElem_index"> + <xsl:call-template + name="u:findRefElem"> + <xsl:with-param + name="possibleElems" + select="$possibleElems"/> + <xsl:with-param + name="id" + select="$refid"/> + </xsl:call-template> + </xsl:variable> + <xsl:call-template + name="u:outputNames"> + <xsl:with-param + name="names" + select="$possibleElems[position() = substring-before($refElem_index, ' ')]/@names"/> + <xsl:with-param + name="index" + select="number(substring-after($refElem_index, ' '))"/> + </xsl:call-template> + </xsl:template> + + <!-- ******************************************************************** --> + + <!-- Output a string with each space character quoted --> + <xsl:template + name="u:quoteWhite"> + <xsl:param + name="string"/> + <xsl:variable + name="head" + select="substring-before($string, ' ')"/> + <xsl:choose> + <xsl:when + test="$head = ''"> + <xsl:value-of + select="$string"/> + </xsl:when> + <xsl:otherwise> + <xsl:value-of + select="concat($head, '\ ')"/> + <xsl:call-template + name="u:quoteWhite"> + <xsl:with-param + name="string" + select="substring-after($string, ' ')"/> + </xsl:call-template> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <!-- ******************************************************************** --> + <!-- ******************************************************************** --> + + <!-- Report unknown tags --> + <xsl:template + match="*"> + <xsl:message> + <xsl:text>`</xsl:text> + <xsl:value-of + select="name(.)"/> + <xsl:text>' encountered</xsl:text> + <xsl:if + test="parent::*"> + <xsl:text> in `</xsl:text> + <xsl:value-of + select="name(parent::*)"/> + <xsl:text>'</xsl:text> + </xsl:if> + <xsl:text>, but no template matches.</xsl:text> + </xsl:message> + </xsl:template> + +</xsl:stylesheet> + +<!-- ********************************************************************** --> +<!-- ********************************************************************** --> +<!-- ********************************************************************** --> +<!-- POD manual page + +=head1 NAME + +xml2rst.xsl - An XSLT script to convert Docutils XML to reStructuredText + +=head1 SYNOPSIS + + Xalan docutils.xml xml2rst.xsl + +=head1 DESCRIPTION + +B<xml2rst.xsl> is an XSLT script to convert Docutils XML to +reStructuredText. You can use your favorite XSLT processor (e.g. Xalan +from the Apache project) to generate reStructuredText from the +Docutils intermediate XML representation. Its main use is to generate +reStructuredText from some other format where a converter to Docutils +XML already exists. + +=head2 Options + +The following options are supported. They are XSLT parameters for the +whole script and must be given to the XSLT processor by the respective +option (Xalan: B<-p>). + +=over 4 + +=item adornment + +Configures title markup to use so different styles can be requested +easily. + +The value of the parameter must be a string made up of a sequence of +character pairs. The first character of a pair is C<o> (overline) or +C<u> (underline) and the second character is the character to use for +the markup. + +The first and the second character pair is used for document title and +subtitle, the following pairs are used for section titles where the +third pair is used for the top level section title. + +Defaults to C<o=o-u=u-u~u:u.u`>. + +=item fold + +Configures whether long text lines in paragraphs should be folded and +to which length. This option is for input not coming from reST which +may have no internal line feeds in plain text strings. + +If folding is enabled text strings not in a line feed preserving +context are first white-space normalized and then broken according to +the folding rules. Folding rules put out the first word and continue +to do so with the following words unless the next word would cross +the folding boundary. Words are delimited by white-space. + +Defaults to C<0>, i.e. no folding. + +=back + +=head2 Unsupported features + +It is generally not possible to create an exact reproduction of an +original reStructuredText source from an intermediate XML file. The +reason is that Docutils transports pretty much but not all information +of the original source into the XML. Also the sequence is changed +sometimes. + +However, the coverage of Docutils features of B<xml2rst.xsl> is pretty +good. A few minor features are not supported: + +=over 4 + +=item * Fully minimized style for literal blocks + +=item * Substitution references for C<replace::> substitutions + +=item * Counting roman numbers in enumerated lists + +=item * Special table types like C<list-table::> and C<csv-table::> + +=item * Custom role definitions + +=back + +=head1 INSTALLATION + +Installation is not necessary. Just use the file as downloaded with +your favorite XSLT processor. + +=head1 AUTHOR + +Stefan Merten <smerten@oekonux.de> based on works by David Priest. + +=head1 LICENSE + +This program is licensed under the terms of the GPL. See + + http://www.gnu.org/licenses/gpl.txt + +=head1 AVAILABILTY + +See + + http://www.merten-home.de/FreeSoftware/xml2rst/ + +=cut + +--> diff --git a/sandbox/xml2rst/xml2rst.py b/sandbox/xml2rst/xml2rst.py new file mode 100755 index 000000000..956edba85 --- /dev/null +++ b/sandbox/xml2rst/xml2rst.py @@ -0,0 +1,498 @@ +#! /usr/bin/env python +# -*- coding: iso-8859-1 -*- + +# Based on sample.py,v 4.1.2.6 2006/04/14 13:59:26 cvs Exp + +# Copyright (C) 2009 Stefan Merten + +# xml2rst.py is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published +# by the Free Software Foundation; either version 2 of the License, +# or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +""" +Convert a docutils XML file to reStructuredText syntax. + +Do + + perldoc xml2rst.py + +for a man page. +""" + +""" +=head1 NAME + +xml2rst.py -- convert a docutils XML file to reStructuredText syntax + +=head1 SYNOPSIS + +B<xml2rst.py> [B<-v>] I<xml> [I<reST>] + +B<xml2rst.py> B<--help> + +=head1 DESCRIPTION + +Converts a docutils XML input file to reStructuredText source. + +This can be used to transform another format to reStructuredText given you have +a transformation to docutils XML. + +=cut +""" + +############################################################################### +############################################################################### +# Import + +import sys +import os.path +import re + +from optparse import OptionParser, OptionGroup, OptionValueError, Option +from copy import copy + +try: + from lxml import etree +except ImportError: + errorExit(2, ( "Python package 'lxml' is not available", + "You may try to use 'xml2rst.xsl' with a standalone XSLT processor like 'xalan' or 'xsltproc'", )) + +############################################################################### +############################################################################### +# Constants + +""" +@var MainXsltNm: Name of the main XSLT source file +@type MainXsltNm: str +""" +MainXsltNm = "xml2rst.xsl" + +""" +@var ScriptNm: Name of the script +@type ScriptNm: str +""" +ScriptNm = sys.argv[0] + +############################################################################### +############################################################################### +# Variables + +""" +@var options: Options given on the command line +@type options: optparse.Values +""" +global options + +############################################################################### +############################################################################### +# General functions + +def pod2Head(pod): + """ + @param pod: Snippet in POD format to be analyzed. + @type pod: str + + @return: String of first `=headX' entry in POD snippet or empty string if + none found. + @rtype: str + """ + for line in pod.split("\n"): + if line.startswith("=head"): + return line[len("=headX"):].strip() + return "" + +############################################################################### + +def pod2Description(pod): + """ + @param pod: Snippet in POD format to be analyzed. + @type pod: str + + @return: Stripped text from all lines not being a POD line command. + @rtype: str + """ + result = "" + for line in pod.split("\n"): + if not line.startswith("="): + result = result.strip() + " " + line.strip() + return result.strip() + +############################################################################### + +def pod2OptionList(pod): + """ + Return option names found in POD snippet. Option names are recognized in + `=item B<option>' constructs. + + @param pod: Snippet in POD format to be analyzed. + @type pod: str + + @return: All option names contained in POD snippet as a list. + @rtype: [ str, ..., ] + """ + result = [ ] + for line in pod.split("\n"): + found = re.search("^=item\s*B<(-[^>]+)>", line) + if found: + result.append(found.group(1)) + return result + +############################################################################### + +def pod2OptionKeywords(pod): + """ + Return a dict mapping `OptionParser.add_option' keywords to values found in + POD snippet. + + @param pod: Snippet in POD format to be analyzed. + @type pod: str + + @return: Mapping for all values found. Currently `help' and `dest' are + filled. + @rtype: { keyword: value, ..., } + """ + result = { 'help': "", } + for line in pod.split("\n"): + if line.startswith("=cut"): + break + found = re.search("^=item\s*B<--?([^>]+)>(?:=|\s*)", line) + if found: + result['help'] = "" + optionName = found.group(1) + found = re.search("I<([^>]+)>", line) + if found: + result['dest'] = found.group(1) + elif len(optionName) > 1: + result['dest'] = optionName + else: + result['help'] += line + "\n" + result['help'] = result['help'].strip() + if result.has_key('dest'): + result['dest'] = result['dest'].replace("-", "_") + else: + errorExit(1, ( "Internal error: Missing `dest' in documentation string:", + pod, )) + return result + +############################################################################### + +def pod2Argument(pod): + """ + Return a list of two strings for `OptionGroup.__init__' describing the + argument found in POD snippet. + + @param pod: Snippet in POD format to be analyzed. + @type pod: str + + @return: Name of the argument and its description. + @rtype: [ argument, description, ] + """ + argument = "" + description = "" + for line in pod.split("\n"): + if line.startswith("=cut"): + break + found = re.search("^=item\s*I<([^>]+)>", line) + if found: + description = "" + argument = found.group(1) + else: + description += line + "\n" + description = description.strip() + return [ argument, description, ] + +############################################################################### + +def parseOptions(): + """ + Sets options and returns arguments. + + @return: Name of input file and optionally of output file. + @rtype: ( str, [str,] ) + """ + global options + pod = """ + +=head1 OPTIONS + +=cut + """ + optionParser = OptionParser("usage: %prog [option]... <xml> [<rst>]") + + pod = """ + +=head2 General options + +=over 4 + +=cut + """ + generalGroup = OptionGroup(optionParser, pod2Head(pod), + pod2Description(pod)) + + pod = """ + +=item B<-a> I<adornment> + +=item B<--adornment>=I<adornment> + +Configures title markup to use so different styles can be requested +easily. + +The value of the parameter must be a string made up of a sequence of +character pairs. The first character of a pair is C<o> (overline) or +C<u> (underline) and the second character is the character to use for +the markup. + +The first and the second character pair is used for document title and +subtitle, the following pairs are used for section titles where the +third pair is used for the top level section title. + +Defaults to C<o=o-u=u-u~u:u.u`>. + +=cut + """ + generalGroup.add_option(default=None, *pod2OptionList(pod), + **pod2OptionKeywords(pod)) + + pod = """ + +=item B<-f> I<fold> + +=item B<--fold>=I<fold> + +Configures whether long text lines in paragraphs should be folded and +to which length. This option is for input not coming from reST which +may have no internal line feeds in plain text strings. + +If folding is enabled text strings not in a line feed preserving +context are first white-space normalized and then broken according to +the folding rules. Folding rules put out the first word and continue +to do so with the following words unless the next word would cross +the folding boundary. Words are delimited by white-space. + +Defaults to C<0>, i.e. no folding. + +=cut + """ + generalGroup.add_option(type="int", default=None, + *pod2OptionList(pod), **pod2OptionKeywords(pod)) + + pod = """ + +=item B<-v> + +=item B<--verbose> + +Operate verbose. + +=cut + """ + generalGroup.add_option(action="store_true", + *pod2OptionList(pod), **pod2OptionKeywords(pod)) + optionParser.add_option_group(generalGroup) + + pod = """ + +=back + +=head2 Arguments + +=over 4 + +=cut + """ + argumentGroup = OptionGroup(optionParser, pod2Head(pod), + pod2Description(pod)) + optionParser.add_option_group(argumentGroup) + + pod = """ + +=item I<xml> + +The XML input file containing docutils XML. + +=cut + """ + + argument1Group = OptionGroup(optionParser, *pod2Argument(pod)) + optionParser.add_option_group(argument1Group) + + pod = """ + +=item I<rst> + +The optional output file containing reStructuredText. + +If not given output is put to C<STDOUT>. + +=cut + """ + argument2Group = OptionGroup(optionParser, *pod2Argument(pod)) + optionParser.add_option_group(argument2Group) + + pod = """ + +=back + +=cut + """ + ( options, args, ) = optionParser.parse_args() + + if len(args) < 1: + optionParser.error("An input file is required") + if len(args) > 2: + optionParser.error("At most two arguments are allowed") + if (options.adornment is not None + and re.search('^([ou][]!"#$%&\'()*+,\-./:;<=>?@[\\^_`{|}~])+$', + options.adornment) is None): + optionParser.error("Invalid adornment string given") + + return args + +############################################################################### + +def errorOut(lines): + """ + Outputs messages as error. + + @param lines: Messages to be output as single lines. + @type lines: ( str, ..., ) + + @return: 0 + @rtype: int + """ + scriptName = os.path.basename(sys.argv[0]) + for line in lines: + print >>sys.stderr, ("%s: %s" % ( scriptName, line, )) + return 0 + +############################################################################### + +def verboseOut(lines): + """ + Outputs messages as a verbose message. + + @param lines: Messages to be output as single lines. + @type lines: ( str, ..., ) + + @return: 0 + @rtype: int + """ + if options.verbose: + errorOut([ "## " + line + for line in lines ]) + return 0 + +############################################################################### + +def errorExit(code, lines): + """ + Exit program with an error message. + + @param code: Exit Code to use. + @type code: int + + @param lines: Strings to output as error message. + @type lines: ( str, ..., ) + + @return: Does not return. + """ + errorOut(lines) + sys.exit(code) + +############################################################################### +############################################################################### +# Specialized functions + +def convert(inNm, outNm): + """ + Do the conversion. + + @param inNm: Filename of input file. + @type inNm: str + + @param outNm: Filename of output file or None. + @type outNm: str | None + """ + try: + inF = open(inNm) + except IOError: + errorExit(1, ( "Can't open input file %r" % ( inNm, ), )) + + scriptP = os.path.dirname(os.path.realpath(ScriptNm)) + mainXsltNm = os.path.join(scriptP, MainXsltNm) + try: + mainXsltF = open(mainXsltNm) + except IOError: + errorExit(1, ( "Can't open main XSLT file %r" % ( mainXsltNm, ), )) + + xsltParser = etree.XMLParser() + mainXsltDoc = etree.parse(mainXsltF, xsltParser) + mainXsltF.close() + mainXslt = etree.XSLT(mainXsltDoc) + + inParser = etree.XMLParser() + try: + inDoc = etree.parse(inF, inParser) + except Exception, e: + errorExit(1, ( "Error parsing input file %r: %s" % ( inNm, e, ), )) + inF.close() + + xsltParams = { } + if options.fold is not None: + xsltParams['fold'] = str(options.fold) + if options.adornment is not None: + xsltParams['adornment'] = "'" + options.adornment + "'" + try: + result = mainXslt(inDoc, **xsltParams) + except Exception, e: + errorExit(1, ( "Error transforming input file %r: %s" % ( inNm, e, ), )) + # Chop off trailing linefeed - added somehow + outS = str(result)[:-1] + if outNm: + try: + outF = open(outNm, "w") + except IOError: + errorExit(1, ( "Can't open output file %r" % ( outNm, ), )) + outF.write(outS) + outF.close() + else: + print(outS) + +############################################################################### +############################################################################### +# Classes + +######################################################################## +############################################################################## +# Now work + +if __name__ == '__main__': + arguments = parseOptions() + inF = arguments[0] + if len(arguments) > 1: + outF = arguments[1] + else: + outF = None + convert(inF, outF) + +############################################################################## +############################################################################## + +# TODO Accept additional XSLT sheets to create a transformation pipeline + +# TODO Move from XSLT to Python implementation step by step by replacing +# XSLT-code by Python code through extensions and other means + diff --git a/sandbox/xml2rst/xml2rst.xsl b/sandbox/xml2rst/xml2rst.xsl index 915c798d4..c27a4200f 100644 --- a/sandbox/xml2rst/xml2rst.xsl +++ b/sandbox/xml2rst/xml2rst.xsl @@ -1,15 +1,19 @@ <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE xsl:stylesheet [ <!ENTITY CR "
"> +<!-- "xml:space='preserve'" is needed for use with libxslt --> +<!-- "xmlns:xsl='http://www.w3.org/1999/XSL/Transform'" is needed for + use with xsltproc --> <!-- Used to create a blank line --> -<!ENTITY tCR "<xsl:text>&CR;</xsl:text>"> +<!ENTITY tCR "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform' xml:space='preserve'>&CR;</xsl:text>"> <!-- Used when the line before must be ended --> -<!ENTITY tEOL "<xsl:text>&CR;</xsl:text>"> -<!ENTITY tSP "<xsl:text> </xsl:text>"> +<!ENTITY tEOL "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform' xml:space='preserve'>&CR;</xsl:text>"> +<!ENTITY tSP "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform' xml:space='preserve'> </xsl:text>"> ]> <!-- - Copyright (C) 2005, 2006, 2009 Stefan Merten, David Priest + Copyright (C) 2005, 2006 Stefan Merten, David Priest + Copyright (C) 2009, 2010 Stefan Merten xml2rst.xsl is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -53,7 +57,13 @@ data: Data elements used by the stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:u="u" xmlns:data="a" - exclude-result-prefixes="data"> + exclude-result-prefixes="data" + xmlns:str="http://exslt.org/strings" + xmlns:dyn="http://exslt.org/dynamic" + xmlns:math="http://exslt.org/math" + extension-element-prefixes="str dyn math"> + + <!-- xmlns:regexp="http://exslt.org/regular-expressions" not supported :-( --> <xsl:output method="text" @@ -992,12 +1002,8 @@ data: Data elements used by the stylesheet <!-- Very special indendation for nested `line_block's --> <xsl:for-each select="ancestor::line_block[position() > 1]"> - <xsl:call-template - name="u:repeat"> - <xsl:with-param - name="length" - select="4"/> - </xsl:call-template> + <xsl:value-of + select="str:padding(4)"/> </xsl:for-each> </xsl:variable> <xsl:call-template @@ -1959,13 +1965,9 @@ data: Data elements used by the stylesheet <!-- Output more whitespace to align with the maximum enumerator --> <xsl:if test="$enumType != 'lowerroman' and $enumType != 'upperroman'"> - <xsl:call-template - name="u:repeat"> - <!-- Assumes that the maximum number has maximum string length --> - <xsl:with-param - name="length" - select="string-length($max) - string-length($cur)"/> - </xsl:call-template> + <!-- Assumes that the maximum number has maximum string length --> + <xsl:value-of + select="str:padding(string-length($max) - string-length($cur))"/> </xsl:if> </xsl:template> @@ -2128,14 +2130,8 @@ data: Data elements used by the stylesheet </xsl:variable> <!-- Determine maximum height so every entry must be this high --> <xsl:variable - name="maxHeight"> - <xsl:call-template - name="u:maxNumber"> - <xsl:with-param - name="numbers" - select="$heights"/> - </xsl:call-template> - </xsl:variable> + name="maxHeight" + select="math:max(str:tokenize(normalize-space($heights)))"/> <!-- Output all the physical lines of this row --> <xsl:call-template name="u:rowLines"> @@ -2216,15 +2212,8 @@ data: Data elements used by the stylesheet <xsl:text>+</xsl:text> <xsl:for-each select="../../colspec"> - <xsl:call-template - name="u:repeat"> - <xsl:with-param - name="length" - select="@colwidth"/> - <xsl:with-param - name="chars" - select="$char"/> - </xsl:call-template> + <xsl:value-of + select="str:padding(@colwidth, $char)"/> <xsl:text>+</xsl:text> </xsl:for-each> &tEOL; @@ -2456,12 +2445,8 @@ data: Data elements used by the stylesheet &tEOL; </xsl:message> </xsl:if> - <xsl:call-template - name="u:repeat"> - <xsl:with-param - name="length" - select="$width - string-length($padded)"/> - </xsl:call-template> + <xsl:value-of + select="str:padding($width - string-length($padded))"/> </xsl:if> <xsl:if test="$outputLine > 1 or $outputLine = 0 and string-length($remainingLines)"> @@ -2666,14 +2651,8 @@ data: Data elements used by the stylesheet name="normalized" select="translate(normalize-space(@name), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')"/> <xsl:variable - name="quoted"> - <xsl:call-template - name="u:quoteWhite"> - <xsl:with-param - name="string" - select="$normalized"/> - </xsl:call-template> - </xsl:variable> + name="quoted" + select="str:replace($normalized, ' ', '\ ')"/> <xsl:variable name="matching" select="//target[contains(@names, $quoted)]"/> @@ -3042,16 +3021,12 @@ data: Data elements used by the stylesheet <xsl:choose> <xsl:when test="contains($directives, concat('*', $this, '*'))"> - <xsl:call-template - name="u:repeat"> - <!-- TODO Indentation of lines after some directives must be - indented to align with the directive instead of a - fixed indentation; however, this is rather complicated - since identation for parameters should be fixed --> - <xsl:with-param - name="length" - select="3"/> - </xsl:call-template> + <!-- TODO Indentation of lines after some directives must be + indented to align with the directive instead of a + fixed indentation; however, this is rather complicated + since identation for parameters should be fixed --> + <xsl:value-of + select="str:padding(3)"/> </xsl:when> <xsl:when test="$this = 'list_item' and parent::enumerated_list"> @@ -3062,20 +3037,12 @@ data: Data elements used by the stylesheet <xsl:call-template name="u:outputEnumerator"/> </xsl:variable> - <xsl:call-template - name="u:repeat"> - <xsl:with-param - name="length" - select="string-length($enumerator)"/> - </xsl:call-template> + <xsl:value-of + select="str:padding(string-length($enumerator))"/> </xsl:when> <xsl:otherwise> - <xsl:call-template - name="u:repeat"> - <xsl:with-param - name="length" - select="document('')//data:lookup/node[@name=$this]/@indent"/> - </xsl:call-template> + <xsl:value-of + select="str:padding(document('')//data:lookup/node[@name=$this]/@indent)"/> </xsl:otherwise> </xsl:choose> </xsl:for-each> @@ -3083,49 +3050,6 @@ data: Data elements used by the stylesheet <!-- ******************************************************************** --> - <!-- - Create a repeated character string - Shamelessly borrowed from Jeni Tennison's work on EXSLT - --> - <xsl:template - name="u:repeat"> - <xsl:param - name="length" - select="0"/> - <xsl:param - name="chars" - select="' '"/> - <xsl:choose> - <xsl:when - test="not($length) or $length <= 0 or not($chars)"/> - <xsl:otherwise> - <xsl:variable - name="string" - select="concat($chars, $chars, $chars, $chars, $chars, $chars, $chars, $chars, $chars, $chars)"/> - <xsl:choose> - <xsl:when - test="string-length($string) >= $length"> - <xsl:value-of - select="substring($string, 1, $length)"/> - </xsl:when> - <xsl:otherwise> - <xsl:call-template - name="u:repeat"> - <xsl:with-param - name="length" - select="$length"/> - <xsl:with-param - name="chars" - select="$string"/> - </xsl:call-template> - </xsl:otherwise> - </xsl:choose> - </xsl:otherwise> - </xsl:choose> - </xsl:template> - - <!-- ******************************************************************** --> - <!-- Indent a text containing line feeds correctly --> <xsl:template name="u:indentLF"> @@ -3452,7 +3376,7 @@ data: Data elements used by the stylesheet </xsl:choose> </xsl:template> - <!-- Output `\' or `\ ' after some inline element if necessary --> + <!-- Output `\' after some inline element if necessary --> <xsl:template name="u:bkslshEscSuf"> <!-- Get the sibling node directly after the current element --> @@ -3504,15 +3428,8 @@ data: Data elements used by the stylesheet <!-- Test whether a overline is wanted at all --> <xsl:if test="substring($adornment, 2 * ($depth - 1) + 1, 1) = 'o'"> - <xsl:call-template - name="u:repeat"> - <xsl:with-param - name="length" - select="$length"/> - <xsl:with-param - name="chars" - select="substring($adornment, 2 * ($depth - 1) + 2, 1)"/> - </xsl:call-template> + <xsl:value-of + select="str:padding($length, substring($adornment, 2 * ($depth - 1) + 2, 1))"/> &tEOL; </xsl:if> </xsl:template> @@ -3526,15 +3443,8 @@ data: Data elements used by the stylesheet greater than 2 are normal section titles --> <xsl:param name="depth"/> - <xsl:call-template - name="u:repeat"> - <xsl:with-param - name="length" - select="$length"/> - <xsl:with-param - name="chars" - select="substring($adornment, 2 * ($depth - 1) + 2, 1)"/> - </xsl:call-template> + <xsl:value-of + select="str:padding($length, substring($adornment, 2 * ($depth - 1) + 2, 1))"/> &tEOL; </xsl:template> @@ -3597,69 +3507,6 @@ data: Data elements used by the stylesheet <!-- ******************************************************************** --> - <!-- Determine the maximum number from a whitespace separated number list --> - <xsl:template - name="u:maxNumber"> - <xsl:param - name="numbers"/> - <!-- If this is not given by the caller a list of negative numbers will - not work --> - <xsl:param - name="currentMax" - select="0"/> - <xsl:variable - name="cleanNumbers" - select="normalize-space($numbers)"/> - <xsl:choose> - <xsl:when - test="contains($cleanNumbers, ' ')"> - <xsl:variable - name="head" - select="substring-before($cleanNumbers, ' ')"/> - <xsl:choose> - <xsl:when - test="$head > $currentMax"> - <xsl:call-template - name="u:maxNumber"> - <xsl:with-param - name="numbers" - select="substring-after($cleanNumbers, ' ')"/> - <xsl:with-param - name="currentMax" - select="$head"/> - </xsl:call-template> - </xsl:when> - <xsl:otherwise> - <xsl:call-template - name="u:maxNumber"> - <xsl:with-param - name="numbers" - select="substring-after($cleanNumbers, ' ')"/> - <xsl:with-param - name="currentMax" - select="$currentMax"/> - </xsl:call-template> - </xsl:otherwise> - </xsl:choose> - </xsl:when> - <xsl:otherwise> - <xsl:choose> - <xsl:when - test="$cleanNumbers > $currentMax"> - <xsl:value-of - select="$cleanNumbers"/> - </xsl:when> - <xsl:otherwise> - <xsl:value-of - select="$currentMax"/> - </xsl:otherwise> - </xsl:choose> - </xsl:otherwise> - </xsl:choose> - </xsl:template> - - <!-- ******************************************************************** --> - <!-- Output a class directive for the directly following element. --> <!-- TODO A class directive can also be used as a container putting the respective attribute to its content; however, this is not @@ -3705,103 +3552,6 @@ data: Data elements used by the stylesheet <!-- ******************************************************************** --> - <!-- Output a string with backslashed stripped --> - <xsl:template - name="u:outputUnbackslashed"> - <xsl:param - name="string"/> - <xsl:choose> - <xsl:when - test="not(contains($string, '\'))"> - <xsl:value-of - select="$string"/> - </xsl:when> - <xsl:otherwise> - <xsl:value-of - select="substring-before($string, '\')"/> - <xsl:call-template - name="u:outputUnbackslashed"> - <xsl:with-param - name="string" - select="substring-after($string, '\')"/> - </xsl:call-template> - </xsl:otherwise> - </xsl:choose> - </xsl:template> - - <!-- Returns a name at an index --> - <xsl:template - name="u:name4Index"> - <xsl:param - name="names"/> - <xsl:param - name="index"/> - <xsl:param - name="name0" - select="''"/> - <xsl:choose> - <xsl:when - test="not(contains($names, ' '))"> - <xsl:choose> - <xsl:when - test="not($index)"> - <xsl:value-of - select="concat($name0, $names)"/> - </xsl:when> - <xsl:otherwise> - <!-- No string with this index --> - <xsl:value-of - select="''"/> - </xsl:otherwise> - </xsl:choose> - </xsl:when> - <xsl:otherwise> - <xsl:variable - name="before" - select="substring-before($names, ' ')"/> - <xsl:choose> - <xsl:when - test="substring($before, string-length($before)) = '\'"> - <!-- Quoted space found --> - <xsl:call-template - name="u:name4Index"> - <xsl:with-param - name="names" - select="substring-after($names, ' ')"/> - <xsl:with-param - name="index" - select="$index"/> - <xsl:with-param - name="name0" - select="concat($name0, $before, ' ')"/> - </xsl:call-template> - </xsl:when> - <xsl:otherwise> - <!-- Separating space found --> - <xsl:choose> - <xsl:when - test="not($index)"> - <xsl:value-of - select="concat($name0, $before)"/> - </xsl:when> - <xsl:otherwise> - <xsl:call-template - name="u:name4Index"> - <xsl:with-param - name="names" - select="substring-after($names, ' ')"/> - <xsl:with-param - name="index" - select="$index - 1"/> - </xsl:call-template> - </xsl:otherwise> - </xsl:choose> - </xsl:otherwise> - </xsl:choose> - </xsl:otherwise> - </xsl:choose> - </xsl:template> - <!-- Output a names attribute at index considering quoted spaces. --> <xsl:template name="u:outputNames"> @@ -3812,191 +3562,34 @@ data: Data elements used by the stylesheet <xsl:param name="index" select="0"/> - <xsl:variable - name="name"> - <xsl:call-template - name="u:name4Index"> - <xsl:with-param - name="names" - select="$names"/> - <xsl:with-param - name="index" - select="$index"/> - </xsl:call-template> - </xsl:variable> - <xsl:call-template - name="u:outputUnbackslashed"> - <xsl:with-param - name="string" - select="$name"/> - </xsl:call-template> + <xsl:value-of + select="str:replace(str:tokenize(normalize-space(str:replace($names, '\ ', '|')))[position() = $index + 1], '|', ' ')"/> </xsl:template> <!-- ******************************************************************** --> - <!-- Finds an id in a list and returns its index or empty string if not - contained exactly. --> - <xsl:template - name="u:findId"> - <xsl:param - name="list"/> - <xsl:param - name="id"/> - <xsl:param - name="index" - select="0"/> - <xsl:choose> - <xsl:when - test="$list = ''"> - <xsl:value-of - select="''"/> - </xsl:when> - <xsl:when - test="$list = $id or starts-with($list, concat($id, ' '))"> - <xsl:value-of - select="$index"/> - </xsl:when> - <xsl:otherwise> - <xsl:call-template - name="u:findId"> - <xsl:with-param - name="list" - select="substring-after($list, ' ')"/> - <xsl:with-param - name="id" - select="$id"/> - <xsl:with-param - name="index" - select="$index + 1"/> - </xsl:call-template> - </xsl:otherwise> - </xsl:choose> - </xsl:template> - - <!-- In a number of elements find the one containing exactly id and return - the index of the element as well as the index of the id --> - <xsl:template - name="u:findRefElem"> - <xsl:param - name="possibleElems"/> - <xsl:param - name="id"/> - <!-- XSLT is 1-based --> - <xsl:param - name="elemIndex" - select="1"/> - <xsl:variable - name="elem" - select="$possibleElems[1]"/> - <xsl:choose> - <!-- No more elements - return empty string --> - <xsl:when - test="not($possibleElems)"> - <xsl:value-of - select="''"/> - </xsl:when> - <xsl:otherwise> - <!-- Check whether the first element contains the id --> - <xsl:variable - name="fnd"> - <xsl:call-template - name="u:findId"> - <xsl:with-param - name="list" - select="$elem/@ids"/> - <xsl:with-param - name="id" - select="$id"/> - </xsl:call-template> - </xsl:variable> - <xsl:choose> - <xsl:when - test="$fnd != ''"> - <xsl:value-of - select="concat($elemIndex, ' ', $fnd)"/> - </xsl:when> - <xsl:otherwise> - <xsl:call-template - name="u:findRefElem"> - <xsl:with-param - name="possibleElems" - select="$possibleElems[position() != 1]"/> - <xsl:with-param - name="id" - select="$id"/> - <xsl:with-param - name="elemIndex" - select="$elemIndex + 1"/> - </xsl:call-template> - </xsl:otherwise> - </xsl:choose> - </xsl:otherwise> - </xsl:choose> - </xsl:template> - <!-- Output a names attribute for a refid. --> <xsl:template name="u:outputNamesRefid"> <xsl:param name="refid" select="@refid"/> - <!-- Determine all possible elements which might be referred --> - <xsl:variable - name="possibleElems" - select="//*[contains(@ids, $refid)]"/> + <!-- Determine the elements which is referred --> <xsl:variable - name="refElem_index"> - <xsl:call-template - name="u:findRefElem"> - <xsl:with-param - name="possibleElems" - select="$possibleElems"/> - <xsl:with-param - name="id" - select="$refid"/> - </xsl:call-template> - </xsl:variable> + name="refElem" + select="//*[@ids and math:max(dyn:map(str:tokenize(normalize-space(@ids)), 'number($refid = .)')) > 0]"/> <xsl:call-template name="u:outputNames"> <xsl:with-param name="names" - select="$possibleElems[position() = substring-before($refElem_index, ' ')]/@names"/> + select="$refElem/@names"/> <xsl:with-param name="index" - select="number(substring-after($refElem_index, ' '))"/> + select="math:max(dyn:map(str:tokenize(normalize-space($refElem/@ids)), 'number($refid = .) * position() - 1'))"/> </xsl:call-template> </xsl:template> <!-- ******************************************************************** --> - - <!-- Output a string with each space character quoted --> - <xsl:template - name="u:quoteWhite"> - <xsl:param - name="string"/> - <xsl:variable - name="head" - select="substring-before($string, ' ')"/> - <xsl:choose> - <xsl:when - test="$head = ''"> - <xsl:value-of - select="$string"/> - </xsl:when> - <xsl:otherwise> - <xsl:value-of - select="concat($head, '\ ')"/> - <xsl:call-template - name="u:quoteWhite"> - <xsl:with-param - name="string" - select="substring-after($string, ' ')"/> - </xsl:call-template> - </xsl:otherwise> - </xsl:choose> - </xsl:template> - - <!-- ******************************************************************** --> <!-- ******************************************************************** --> <!-- Report unknown tags --> @@ -4031,22 +3624,22 @@ xml2rst.xsl - An XSLT script to convert Docutils XML to reStructuredText =head1 SYNOPSIS - Xalan docutils.xml xml2rst.xsl + xsltproc docutils.xml xml2rst.xsl =head1 DESCRIPTION B<xml2rst.xsl> is an XSLT script to convert Docutils XML to -reStructuredText. You can use your favorite XSLT processor (e.g. Xalan -from the Apache project) to generate reStructuredText from the -Docutils intermediate XML representation. Its main use is to generate -reStructuredText from some other format where a converter to Docutils -XML already exists. +reStructuredText. You can use your favorite XSLT processor supporting +EXSLT (e.g. xsltproc from the Gnome project) to generate +reStructuredText from the Docutils intermediate XML representation. +Its main use is to generate reStructuredText from some other format +where a converter to Docutils XML already exists. =head2 Options The following options are supported. They are XSLT parameters for the whole script and must be given to the XSLT processor by the respective -option (Xalan: B<-p>). +option (xsltproc: B<--param> or B<--stringparam>). =over 4 @@ -4110,7 +3703,8 @@ good. A few minor features are not supported: =head1 INSTALLATION Installation is not necessary. Just use the file as downloaded with -your favorite XSLT processor. +your favorite XSLT processor supporting EXSLT. For instance you can +use B<xsltproc> from the Gnome project. =head1 AUTHOR |
