- From: Tim Mooney <mooney@dogbert.cc.ndsu.NoDak.edu>
- Date: Wed, 6 Oct 2004 16:00:45 -0500 (CDT)
- To: www-xsl-fo@w3.org
[I haven't yet subscribed to this list, please Cc: me on any replies]
I have some XML data like:
<?xml version="1.0"?>
<request>
<common>
  some common stuff, not important
</common>
<account>
  <id>foo1</id>
  <name>John. Q. Public</name>
</account>
<account>
  <id>foo2</id>
  <name>George Washington</name>
</account>
<!-- and so on, possibly hundreds of account elements -->
</request>
that I need to transform into PDF to look something like:
-------------------------------------------------------------------------
| Login ID:  foo1                    | Login ID: foo2                   |
| Customer Name:  John Q. Public     | Customer Name: George Washington |
-------------------------------------------------------------------------
| Login ID:  foo3                    | Login ID: foo4                   |
| Customer Name:  John Adams         | Customer Name: Thomas Jefferson  |
-------------------------------------------------------------------------
| Login ID:  foo5                    | Login ID: foo6                   |
| Customer Name:  Abraham Lincoln    | Customer Name: Theodore Roosevelt|
-------------------------------------------------------------------------
| Login ID:  foo7                    | Login ID: foo8                   |
| Customer Name:  Alexander Hamilton | Customer Name: Woodrow Wilson    |
-------------------------------------------------------------------------
| Login ID:  foo9                    | Login ID: foo10                  |
| Customer Name:  Herbert Hoover     | Customer Name: John F. Kennedy   |
-------------------------------------------------------------------------
The rub is that I can't have any of the cells broken by a page break --
the page break must happen between rows in the table, not within a cell.
I'm using Apache fop 0.20.5, which doesn't have support for most of the FO
that one would use to control that -- widows, orphans, keep-togethers, etc.
If I just mark up static content with FO, I can just do a
 	<fo:table-row break-before="page">
before every N rows, let's say every 20 rows.  I have "perfect" output
happening, with static content marked up and transformed to PDF via fop.
The problem is that now I want to transform my XML data, and I'm having a
miserable time trying to conditionally output the right fo:table-row.
I want to do something like:
  <xsl:for-each select="/request/account">
   <xsl:sort select="./id" />
   <!-- output a table row if position() mod 2 = 1 -->
   <xsl:choose>
    <xsl:when test="position() mod 2 = 1">
     <!-- yes, should it be a normal fo:table-row or one with -->
     <!-- a page break?                                       -->
     <xsl:choose>
       <xsl:when test="position() = 1">
         <!-- this will be the first cell, don't force a page break here -->
 	    <!-- just a normal page table row  -->
         <fo:table-row keep-together.within-page="always">
       </xsl:when>
       <xsl:otherwise>
         <!-- have we output enough so we should force a break?  -->
         <xsl:choose>
           <xsl:when test="position() mod 20 = 0">
              <!-- filled a page, output table-row and page-break -->
                 <fo:table-row break-before="page"
                      keep-together.within-page="always">
            </xsl:when>
            <xsl:otherwise>
              <!-- just a normal page table row  -->
              <fo:table-row keep-together.within-page="always">
            </xsl:otherwise>
          </xsl:choose>
        </xsl:otherwise>
     </xsl:choose>
    </xsl:when>
   </xsl:choose>
   <!-- now output a table cell -->
   <xsl:apply-templates select="." mode="cutsheet" />
   <!-- if postion() mode 2 = 0, we should output a row close, we've -->
   <!-- finished a row of 2 cells, and should output a /fo:table-row -->
   <xsl:if test="position() mod 2 = 0">
    </fo:table-row>
   </xsl:if>
</xsl:for-each>
The problem is that the xslt processor will choke on that, because it
sees e.g.
 	<xsl:if test="position() mod 2 = 0">
 		</fo:table-row>
 	</xsl:if>
which is a mismatched tag.
Next I tried:
<xsl:for-each select="/request/account">
<xsl:sort select="./iid" />
<!-- output a table row if position() mod 2 = 1 -->
<xsl:choose>
  <xsl:when test="position() mod 2 = 1">
    <!-- yes, should it be a normal fo:table-row or one with -->
    <!-- a page break?                                       -->
    <xsl:choose>
      <xsl:when test="position() = 1">
        <!-- just a normal page table row  -->
        <fo:table-row keep-together.within-page="always">
          <xsl:apply-templates select="." mode="cutsheet" />
          <xsl:apply-templates select="following-sibling::."
 				mode="cutsheet" />
        </fo:table-row>
      </xsl:when>
      <xsl:otherwise>
       <!-- have we output enough so we should force a break?  -->
       <xsl:choose>
        <xsl:when test="position() mod 28 = 0">
          <!-- filled a page, output table-row and page-break -->
             <fo:table-row break-before="page"
                 keep-together.within-page="always">
                 <xsl:apply-templates select="." mode="cutsheet" />
                 <xsl:apply-templates select="following-sibling::."
 					mode="cutsheet" />
             </fo:table-row>
        </xsl:when>
        <xsl:otherwise>
          <!-- just a normal page table row  -->
          <fo:table-row keep-together.within-page="always">
             <xsl:apply-templates select="." mode="cutsheet" />
             <xsl:apply-templates select="following-sibling::."
 					mode="cutsheet" />
          </fo:table-row>
        </xsl:otherwise>
       </xsl:choose>
      </xsl:otherwise>
     </xsl:choose>
  </xsl:when>
</xsl:choose>
</xsl:for-each>
In other words, don't just output a fo:table-row, output both the opening
and closing tags for the row, and try get two cells in between them.
As I expected, that doesn't work correctly either, I end up with:
-------------------------------------------------------------------------
| Login ID:  foo1                    |                                  |
| Customer Name:  John Q. Public     |                                  |
-------------------------------------|                                  |
| Login ID:  foo3                    |                                  |
| Customer Name:  John Adams         |                                  |
-------------------------------------|                                  |
| Login ID:  foo5                    |                                  |
| Customer Name:  Abraham Lincoln    |                                  |
-------------------------------------------------------------------------
and so on.
Can anyone suggest a way to accomplish what I'm trying to do?  I'll gladly
provide more info if I've left anything pertinent out.
Tim
-- 
Tim Mooney                              mooney@dogbert.cc.ndsu.NoDak.edu
Information Technology Services         (701) 231-1076 (Voice)
Room 242-J6, IACC Building              (701) 231-8541 (Fax)
North Dakota State University, Fargo, ND 58105-5164
Received on Wednesday, 6 October 2004 21:05:07 UTC