On Nov 12, 2006, at 13:25, Jeremias Maerki wrote:

Ok, so you don't just have a simple list of name/value pairs that happen
to be separated by a tab element. You rather have arbitrary
type-writer-style text. That makes it a lot more complicated, at least
for XSL-FO.

Well, I've already been playing a bit with it yesterday, and found out that the only really 'complicated' parts are
a) determining/guessing the number of characters per tab.
FTM, suppose this is a constant, and we put it in a global variable: chars-per-tab. (but this can easily be replaced by a template parameter which is passed down) b) depending on the answer to the question whether there can be more than two 'columns' per par, determining the number-columns-spanned can also become increasingly difficult for all but the first text node

WRT b), what I'm referring to is the possibility of par nodes like this:

<par>
  blabla:
  <tab />
  more text more text
  <tab />
  <tab />
  some more text
</par>

This type of par does not occur in Andrejus' example. If there is always at most one 'label' and one 'item' separated by an arbitrary number of tabs, then it is not too difficult to solve in XSLT. ('label' = first text node in a par; 'item' = a sequence of text() and break nodes)

In that case, the trick seems to be to apply templates only to the text() nodes, and using the preceding-sibling tabs to determine the right column-number to set.

Take control by starting the transform by applying templates only to the first text node in each par ( a par node will correspond to a row in the output ). Take care to add an xsl:strip-space, to avoid processing white-space-only text nodes.

This would only be reliable, of course, when using non-proportional fonts, and supposing default linefeed- and white-space-treatment.

After processing this first text node, check for
following-sibling::text()[1][not(preceding-sibling::*[1][name(.) ='break'])]

If true, also check for tab nodes in between the current text node and that following-sibling. From that and the number of columns spanned by the current cell, you can then calculate the next column-number and pass that as a parameter into the template applied to the following-sibling::text()[1].

After processing each text node, check for following-sibling::node() [1][name(.)='break']. If this evaluates to true, apply templates to that break (which will do no more than create a new block for its immediately following text node in the current cell)

The matching template for the break nodes will also perform a similar check for following-sibling breaks, and create a new block for them...

Complete stylesheet code below. Put the source supplied by Andrejus in one <text> node, and it can be transformed with this code into a table structure.

No guarantees that it will cover all possibilities, but it may already offer Andrejus some idea on how to achieve this.

Cheers,

Andreas

---
<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/ Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format";>

<xsl:strip-space elements="*" />
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />

<xsl:template match="/text">
<xsl:param name="tab-width" select="'0.5in'" />
<xsl:param name="max-tabs" select="9" />

  <fo:table width="100%" table-layout="fixed">
<fo:table-column column-width="{$tab-width}" number-columns- repeated="{$max-tabs}" />
    <fo:table-body>
      <xsl:apply-templates select="par">
        <xsl:with-param name="chars-per-tab" select="5" />
        <xsl:with-param name="max-tabs" select="$max-tabs" />
      </xsl:apply-templates>
    </fo:table-body>
  </fo:table>
</xsl:template>

<xsl:template match="par">
<xsl:param name="chars-per-tab" select="1" />
<xsl:param name="max-tabs" select="40" />

  <fo:table-row>
    <xsl:apply-templates select="text()[1]">
      <xsl:with-param name="chars-per-tab" select="$chars-per-tab" />
      <xsl:with-param name="max-tabs" select="$max-tabs" />
    </xsl:apply-templates>
  </fo:table-row>
</xsl:template>

<xsl:template match="par/text()">
<xsl:param name="chars-per-tab" select="1" />
<xsl:param name="next-colnr" select="0" />
<xsl:param name="max-tabs" select="40" />

  <xsl:choose>
    <xsl:when test="not(preceding-sibling::text())">
<xsl:variable name="colnr" select="1 + count(preceding- sibling::tab)" /> <xsl:variable name="colspan" select="1 + floor(string-length (normalize-space(.)) div $chars-per-tab)" />

<fo:table-cell column-number="{$colnr}" number-columns- spanned="{$colspan}">
        <fo:block>
          <xsl:value-of select="normalize-space(.)" />
        </fo:block>
        <xsl:if test="following-sibling::*[1][name(.)='break']">
          <xsl:apply-templates select="following-sibling::break[1]" />
        </xsl:if>
      </fo:table-cell>
<xsl:if test="following-sibling::text()[1][not(preceding- sibling::*[1][name(.)='break'])]">
        <xsl:apply-templates select="following-sibling::text()[1]">
<xsl:with-param name="next-colnr" select="$colnr + $colspan - 1" />
          <xsl:with-param name="max-tabs" select="$max-tabs" />
        </xsl:apply-templates>
      </xsl:if>
    </xsl:when>
    <xsl:otherwise>
<fo:table-cell column-number="{$next-colnr}" number-columns- spanned="{$max-tabs - $next-colnr + 1}">
        <fo:block>
          <xsl:value-of select="normalize-space(.)" />
        </fo:block>
        <xsl:if test="following-sibling::*[1][name(.)='break']">
          <xsl:apply-templates select="following-sibling::break[1]" />
        </xsl:if>
      </fo:table-cell>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="break">
  <fo:block>
<xsl:value-of select="normalize-space(following-sibling::text() [1])" />
  </fo:block>
  <xsl:if test="following-sibling::*[1][name(.)='break']">
    <xsl:apply-templates select="following-sibling::break[1]" />
  </xsl:if>
</xsl:template>

</xsl:stylesheet>
---

Reply via email to