strip-frames.xsl 6.02 KB
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
  xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0"
  xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0"
  xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"
  xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"
  xmlns:xlink="http://www.w3.org/1999/xlink"
  xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0"
  xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0"
  version="1.0">
  <xsl:output
    method="xml"
    indent="yes"
    encoding="UTF-8"
    standalone="yes"
    />

<!-- styles -->
<xsl:template match="office:automatic-styles">
  <xsl:copy>
    <xsl:apply-templates select="@* | node()"/>
    <!-- TODO: add styles for new sections based on the frames? -->
    <!-- create a derived style for each paragraph we strip from around a frame,
         which should be identical but for having a 0 line height -->
    <xsl:for-each select="//text:p[draw:frame]
                         |//text:h[draw:frame]">
      <xsl:element name="style:style">
        <xsl:variable name="style-name" select="@text:style-name"/>

        <xsl:attribute name="style:family">paragraph</xsl:attribute>
        <xsl:attribute name="style:name">
          <xsl:value-of select="concat('Placeholder', $style-name)"/>
        </xsl:attribute>
        <xsl:attribute name="style:parent-style-name">
          <xsl:value-of select="$style-name"/>
        </xsl:attribute>
        <!-- copy the original style:master-page-name attribute -->
        <xsl:copy-of select="//office:automatic-styles/style:style[@style:name=$style-name]/@style:master-page-name"/>

        <xsl:element name="style:paragraph-properties">
          <xsl:attribute name="fo:line-height">0</xsl:attribute>
        </xsl:element>
      </xsl:element>
    </xsl:for-each>
  </xsl:copy>
</xsl:template>

<!-- strip empty paragraphs that are likely here only to account for some
     frame sizes and will increase the page contents size for no good reason
     once the frames are stripped -->
<xsl:template match="text:p[count(*) = 0][count(preceding-sibling::*) > 0]">
  <xsl:comment>Stripped empty &lt;text:p text:style-name="<xsl:value-of select="@text:style-name"/>"&gt;</xsl:comment>
</xsl:template>

<!-- moves children of a draw:p containing a draw:frame outside of itself,
     because we want to replace the frame with sections, which do not belong
     inside a text:p -->
<xsl:template match="text:p[draw:frame] |
                     text:h[draw:frame]">
  <xsl:comment>Removed outer <xsl:value-of select="name()"/></xsl:comment>
  <xsl:copy>
    <!-- FIXME: there's probably a better way to do that -->
    <xsl:for-each select="@*">
      <xsl:choose>
        <xsl:when test="../@text:style-name = .">
          <xsl:attribute name="text:style-name">
            <xsl:value-of select="concat('Placeholder', .)"/>
          </xsl:attribute>
        </xsl:when>
        <xsl:otherwise>
          <xsl:apply-templates select="."/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:for-each>
  </xsl:copy>
  <xsl:apply-templates/>
</xsl:template>

<!-- Detect frames we can't handle -->
<xsl:template match="draw:frame[count(draw:text-box) > 0 and count(draw:text-box) != count(*)]">
  <xsl:message>Unsupported input with a frame combining children of different types</xsl:message>
  <xsl:comment>Unsupported input with a frame combining children of different types</xsl:comment>
  <xsl:call-template name="copy-and-recurse"/>
</xsl:template>

<!-- this is kind of a hack, as we stripped the text:p, we add it back -->
<xsl:template match="text:p/draw:frame[not(draw:text-box)]
                    |text:h/draw:frame[not(draw:text-box)]
                    |text:p[draw:frame]/*[not(self::draw:frame)][not(self::text:p)]
                    |text:h[draw:frame]/*[not(self::draw:frame)][not(self::text:h)]">
  <xsl:comment>Re-added <xsl:value-of select="name(..)" /> wrapping element</xsl:comment>
  <!-- FIXME: there's probably a better way to do that.
       Here in order to change the context (and because XSLT 1.0 doesn't have
       the "select" attribute on xsl:copy), we use a xsl:for-each that matches
       a single element in order to switch context -->
  <xsl:variable name="self" select="."/>
  <xsl:for-each select="..">
    <xsl:copy>
      <!-- TODO: add the stripped p's attributes here (?) -->
      <xsl:for-each select="$self">
        <xsl:call-template name="copy-and-recurse"/>
      </xsl:for-each>
    </xsl:copy>
  </xsl:for-each>
</xsl:template>

<!-- Replaces draw:frame/draw:text-box with a section -->
<xsl:template match="text:p/draw:frame[count(draw:text-box) = count(*)]
                    |text:h/draw:frame[count(draw:text-box) = count(*)]">
  <xsl:comment>draw:frame</xsl:comment>
  <xsl:comment>draw:text-box</xsl:comment>
  <xsl:element name="text:section">
    <xsl:attribute name="text:name">
      <xsl:value-of select="concat('PlaceholderSection', count(preceding::*))" />
    </xsl:attribute>
    <xsl:apply-templates select="./draw:text-box/*"/>
  </xsl:element>
</xsl:template>
<!-- companion for the above, catches frames not handled because outside of
     supported elements.  Should never happen, but helps debugging of unexpected
     cases. -->
<xsl:template match="*[not(self::text:p) and not(self::text:h)][draw:frame[count(draw:text-box) = count(*)]]">
  <xsl:message>Unsupported input with a draw:frame not inside a text:p or text:h (but a <xsl:value-of select="name()"/>) but with draw:text-box children</xsl:message>
  <xsl:comment>Unsupported input with a draw:frame not inside a text:p or text:h (but a <xsl:value-of select="name()"/>) but with draw:text-box children</xsl:comment>
  <xsl:call-template name="copy-and-recurse"/>
</xsl:template>

<!-- base template to match anything and call other templates -->
<xsl:template match="@* | node()" name="copy-and-recurse">
  <xsl:copy>
    <xsl:apply-templates select="@* | node()"/>
  </xsl:copy>
</xsl:template>

</xsl:stylesheet>