Skip to content

Commit

Permalink
Merge pull request #35 from karnov/complex-list
Browse files Browse the repository at this point in the history
Handle Complex list
  • Loading branch information
anitsirc committed Nov 11, 2015
2 parents f29a243 + 3dc9999 commit ddafdd9
Show file tree
Hide file tree
Showing 9 changed files with 358 additions and 148 deletions.
3 changes: 2 additions & 1 deletion lib/htmltoword/document.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ def replace_file(html, file_name = Document.doc_xml_file, extras = false)
source = Nokogiri::HTML(html.gsub(/>\s+</, '><'))
transform_and_replace(source, Document.numbering_xslt, Document.numbering_xml_file)
transform_and_replace(source, Document.relations_xslt, Document.relations_xml_file)
transform_and_replace(source, Document.xslt_template(extras), file_name, extras)
cleaned_source = Nokogiri::XSLT(File.open(File.join(Htmltoword.config.default_xslt_path, 'inline_elements.xslt'))).transform(source)
transform_and_replace(cleaned_source, Document.xslt_template(extras), file_name, extras)
end

private
Expand Down
81 changes: 61 additions & 20 deletions lib/htmltoword/xslt/base.xslt
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,14 @@
</xsl:template>


<xsl:template match="br[not(ancestor::p) and not(ancestor::div) and not(name(..)='td') and not(name(..)='li') or
<xsl:template match="br[not(ancestor::p) and not(ancestor::div) and not(ancestor::td|ancestor::li) or
(preceding-sibling::div or following-sibling::div or preceding-sibling::p or following-sibling::p)]">
<w:p>
<w:r></w:r>
</w:p>
</xsl:template>

<xsl:template match="br[(name(..)='li' or name(..)='td') and
<xsl:template match="br[(ancestor::li or ancestor::td) and
(preceding-sibling::div or following-sibling::div or preceding-sibling::p or following-sibling::p)]">
<w:r>
<w:br />
Expand Down Expand Up @@ -110,31 +110,72 @@
</xsl:template>

<xsl:template match="ol|ul">
<xsl:param name="local_level" select="0"/>
<xsl:param name="global_level" select="count(preceding::ol[not(ancestor::ol or ancestor::ul)]) + count(preceding::ul[not(ancestor::ol or ancestor::ul)]) + 1"/>
<xsl:apply-templates>
<xsl:with-param name="local_level" select="$local_level + 1" />
<xsl:with-param name="global_level" select="$global_level" />
</xsl:apply-templates>
</xsl:template>

<xsl:template match="li">
<xsl:param name="local_level" />
<xsl:template name="listItem" match="li">
<xsl:param name="global_level" />
<w:p>
<w:pPr>
<w:pStyle w:val="ListParagraph"></w:pStyle>
<w:numPr>
<w:ilvl w:val="{$local_level - 1}"/>
<w:numId w:val="{$global_level}"/>
</w:numPr>
</w:pPr>
<xsl:apply-templates select="*[not(name()='ol' or name()='ul')]|text()"/>
</w:p>
<xsl:apply-templates select="./ol|./ul">
<xsl:with-param name="local_level" select="$local_level" />
<xsl:with-param name="global_level" select="$global_level" />
</xsl:apply-templates>
<xsl:param name="preceding-siblings" select="0"/>
<xsl:for-each select="node()">
<xsl:choose>
<xsl:when test="self::br">
<w:p>
<w:pPr>
<w:pStyle w:val="ListParagraph"></w:pStyle>
<w:numPr>
<w:ilvl w:val="0"/>
<w:numId w:val="0"/>
</w:numPr>
</w:pPr>
<w:r></w:r>
</w:p>
</xsl:when>
<xsl:when test="self::ol|self::ul">
<xsl:apply-templates>
<xsl:with-param name="global_level" select="$global_level" />
</xsl:apply-templates>
</xsl:when>
<xsl:when test="not(descendant::li)"> <!-- simpler div, p, headings, etc... -->
<xsl:variable name="ilvl" select="count(ancestor::ol) + count(ancestor::ul) - 1"></xsl:variable>
<xsl:choose>
<xsl:when test="$preceding-siblings + count(preceding-sibling::*) > 0">
<w:p>
<w:pPr>
<w:pStyle w:val="ListParagraph"></w:pStyle>
<w:numPr>
<w:ilvl w:val="0"/>
<w:numId w:val="0"/>
</w:numPr>
<w:ind w:left="{720 * ($ilvl + 1)}"/>
</w:pPr>
<xsl:apply-templates/>
</w:p>
</xsl:when>
<xsl:otherwise>
<w:p>
<w:pPr>
<w:pStyle w:val="ListParagraph"></w:pStyle>
<w:numPr>
<w:ilvl w:val="{$ilvl}"/>
<w:numId w:val="{$global_level}"/>
</w:numPr>
</w:pPr>
<xsl:apply-templates/>
</w:p>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise> <!-- mixed things div having list and stuff content... -->
<xsl:call-template name="listItem">
<xsl:with-param name="global_level" select="$global_level" />
<xsl:with-param name="preceding-siblings" select="$preceding-siblings + count(preceding-sibling::*)" />
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>

<xsl:template match="span[not(ancestor::td) and not(ancestor::li) and (preceding-sibling::h1 or preceding-sibling::h2 or preceding-sibling::h3 or preceding-sibling::h4 or preceding-sibling::h5 or preceding-sibling::h6 or preceding-sibling::table or preceding-sibling::p or preceding-sibling::ol or preceding-sibling::ul or preceding-sibling::div or following-sibling::h1 or following-sibling::h2 or following-sibling::h3 or following-sibling::h4 or following-sibling::h5 or following-sibling::h6 or following-sibling::table or following-sibling::p or following-sibling::ol or following-sibling::ul or following-sibling::div)]
Expand Down
39 changes: 39 additions & 0 deletions lib/htmltoword/xslt/inline_elements.xslt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="utf-8" omit-xml-declaration="yes" indent="yes" />

<xsl:strip-space elements="*"/>

<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="@*|node()[1]"/>
</xsl:copy>
<xsl:apply-templates select="following-sibling::node()[1]"/>
</xsl:template>

<!-- get first inline element of a sequence or text having block element siblings... -->
<xsl:template match="node()[self::a|self::b|self::em|self::i|self::small|self::span|self::strong|self::u|self::text()][parent::div|parent::li|parent::td]">
<div>
<xsl:attribute name="class"><xsl:value-of select="../@class"/></xsl:attribute>
<xsl:attribute name="style"><xsl:value-of select="../@style"/></xsl:attribute>
<xsl:call-template name="inlineElement"/>
</div>
<xsl:apply-templates select="following-sibling::node()[not((self::a|self::b|self::em|self::i|self::small|self::span|self::strong|self::u|self::text())[parent::div|parent::li|parent::td])][1]"/>
</xsl:template>

<!-- get following inline elements... -->
<xsl:template match="a[preceding-sibling::node()[1][self::a|self::b|self::em|self::i|self::small|self::span|self::strong|self::u|self::text()]]
|b[preceding-sibling::node()[1][self::a|self::b|self::em|self::i|self::small|self::span|self::strong|self::u|self::text()]]
|em[preceding-sibling::node()[1][self::a|self::b|self::em|self::i|self::small|self::span|self::strong|self::u|self::text()]]
|i[preceding-sibling::node()[1][self::a|self::b|self::em|self::i|self::small|self::span|self::strong|self::u|self::text()]]
|small[preceding-sibling::node()[1][self::a|self::b|self::em|self::i|self::small|self::span|self::strong|self::u|self::text()]]
|span[preceding-sibling::node()[1][self::a|self::b|self::em|self::i|self::small|self::span|self::strong|self::u|self::text()]]
|strong[preceding-sibling::node()[1][self::a|self::b|self::em|self::i|self::small|self::span|self::strong|self::u|self::text()]]
|u[preceding-sibling::node()[1][self::a|self::b|self::em|self::i|self::small|self::span|self::strong|self::u|self::text()]]
|text()[preceding-sibling::node()[1][self::a|self::b|self::em|self::i|self::small|self::span|self::strong|self::u|self::text()]]"
name="inlineElement">
<xsl:copy>
<xsl:apply-templates select="@*|node()[1]"/>
</xsl:copy>
<xsl:apply-templates select="following-sibling::node()[1][self::a|self::b|self::em|self::i|self::small|self::span|self::strong|self::u|self::text()]"/>
</xsl:template>
</xsl:stylesheet>
63 changes: 29 additions & 34 deletions lib/htmltoword/xslt/numbering.xslt
Original file line number Diff line number Diff line change
Expand Up @@ -72,58 +72,48 @@
</w:numbering>
</xsl:template>

<xsl:template match="ol|ul">
<xsl:param name="local_level" select="0"/>
<xsl:param name="global_level" select="count(preceding::ol[not(ancestor::ol or ancestor::ul)]) + count(preceding::ul[not(ancestor::ol or ancestor::ul)]) + 1"/>
<xsl:template name="container" match="ol|ul">
<xsl:variable name="global_level" select="count(preceding::ol[not(ancestor::ol or ancestor::ul)]) + count(preceding::ul[not(ancestor::ol or ancestor::ul)]) + 1"/>
<xsl:variable name="style" select="func:list-type(name(.), concat(' ', @style, ' '), concat(' ', @class, ' '))"/>
<xsl:choose>
<xsl:when test="$local_level = 0">
<xsl:when test="not(ancestor::ol or ancestor::ul)">
<w:abstractNum w:abstractNumId="{$global_level - 1}">
<w:nsid w:val="{concat('099A08C', $global_level)}"/>
<w:multiLevelType w:val="hybridMultilevel"/>
<xsl:call-template name="numbering_level">
<xsl:with-param name="ilvl" select="$local_level"/>
<xsl:with-param name="ilvl" select="0"/>
<xsl:with-param name="style" select="$style"/>
</xsl:call-template>
<xsl:apply-templates>
<xsl:with-param name="local_level" select="$local_level + 1" />
<xsl:with-param name="global_level" select="$global_level" />
</xsl:apply-templates>
<xsl:if test="count(.//ol) + count(.//ul) = 0">
<xsl:call-template name="item"/>
<xsl:if test="count(.//ol|.//ul) &lt; 6">
<xsl:call-template name="autocomplete">
<xsl:with-param name="ilvl" select="$local_level + 1"/>
<xsl:with-param name="ilvl" select="count(.//ol) + count(.//ul)"/>
<xsl:with-param name="style" select="$style"/>
</xsl:call-template>
</xsl:if>
</w:abstractNum>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="numbering_level">
<xsl:with-param name="ilvl" select="$local_level"/>
<xsl:with-param name="ilvl" select="count(ancestor::ol) + count(ancestor::ul)"/>
<xsl:with-param name="style" select="$style"/>
</xsl:call-template>
<xsl:apply-templates>
<xsl:with-param name="local_level" select="$local_level + 1" />
<xsl:with-param name="global_level" select="$global_level" />
</xsl:apply-templates>

<xsl:if test="count(.//ol) + count(.//ul) = 0">
<xsl:call-template name="autocomplete">
<xsl:with-param name="ilvl" select="$local_level + 1"/>
<xsl:with-param name="style" select="$style"/>
</xsl:call-template>
</xsl:if>
<xsl:call-template name="item"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

<xsl:template match="li">
<xsl:param name="local_level" />
<xsl:param name="global_level" />
<xsl:apply-templates select="./ol|./ul">
<xsl:with-param name="local_level" select="$local_level" />
<xsl:with-param name="global_level" select="$global_level" />
</xsl:apply-templates>
<xsl:template name="item">
<xsl:for-each select="node()">
<xsl:choose>
<xsl:when test="self::ol|self::ul">
<xsl:call-template name="container"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="item"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>

<xsl:template match="*">
Expand Down Expand Up @@ -165,13 +155,19 @@
<xsl:template name="autocomplete">
<xsl:param name="ilvl"/>
<xsl:param name="style" />
<xsl:if test="$ilvl &lt; 6">
<xsl:variable name="current_level">
<xsl:choose>
<xsl:when test="$ilvl &lt; 1">1</xsl:when>
<xsl:otherwise><xsl:value-of select="$ilvl"/></xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:if test="$current_level &lt; 6">
<xsl:call-template name="numbering_level">
<xsl:with-param name="ilvl" select="$ilvl"/>
<xsl:with-param name="ilvl" select="$current_level"/>
<xsl:with-param name="style" select="$style"/>
</xsl:call-template>
<xsl:call-template name="autocomplete">
<xsl:with-param name="ilvl" select="$ilvl + 1"/>
<xsl:with-param name="ilvl" select="$current_level + 1"/>
<xsl:with-param name="style" select="$style"/>
</xsl:call-template>
</xsl:if>
Expand All @@ -190,5 +186,4 @@
</xsl:call-template>
</xsl:if>
</xsl:template>

</xsl:stylesheet>
Loading

0 comments on commit ddafdd9

Please sign in to comment.