How to print source XML into a PDF using XSL-FO?

2020-06-25 04:08发布

问题:

Good day! I need to write a xsl-fo template but I don't have access to the source XML. Is there a way how top print the source XML into a PDF so that I can copy it from the PDF then and paste into a file? It should have the same structure as the source xml including attributes. Please how to do it? Thank you in advance! Vojtech

Edited: I have a web interface where I can paste my template and a PDF is generated. But I don't exactly know what is the structure of the XML used as data source. So I need to write another template which will read input XML (elements, attributes, structure) and write it into a PDF. I'd like to copy content of the PDF then and save it into a file.xml so that I can study it.

回答1:

Here's another option that is greatly simplified; just print a copy of the entire XML.

Example:

XML Input

<doc attr="test">
  <a>Lorem ipsum dolor sit amet...</a>
  <b>
    <c>Lorem ipsum dolor sit amet...</c>
    <d>
      <e attr="another test"/>
      <f>Lorem ipsum dolor sit amet...</f>
    </d>
  </b>
</doc>

XSLT 1.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="/">
    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
      <fo:layout-master-set>
        <fo:simple-page-master master-name="my-page" page-width="8.5in" page-height="11in">
          <fo:region-body margin="1in" margin-top="1.5in" margin-bottom="1.5in"/>
        </fo:simple-page-master>
      </fo:layout-master-set>
      <fo:page-sequence master-reference="my-page">
        <fo:flow flow-name="xsl-region-body" font-family="monospace">
          <fo:block white-space-collapse="false" white-space-treatment="preserve" linefeed-treatment="preserve">
            <xsl:text disable-output-escaping="yes">
              &lt;![CDATA[
            </xsl:text>
            <xsl:copy-of select="/*"/>
            <xsl:text disable-output-escaping="yes">
              ]]&gt;
            </xsl:text>
          </fo:block>
        </fo:flow>
      </fo:page-sequence>
    </fo:root>
  </xsl:template>

</xsl:stylesheet>

XSL-FO Output

<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
   <fo:layout-master-set>
      <fo:simple-page-master master-name="my-page" page-width="8.5in" page-height="11in">
         <fo:region-body margin="1in" margin-top="1.5in" margin-bottom="1.5in"/>
      </fo:simple-page-master>
   </fo:layout-master-set>
   <fo:page-sequence master-reference="my-page">
      <fo:flow flow-name="xsl-region-body" font-family="monospace">
         <fo:block white-space-collapse="false" white-space-treatment="preserve" linefeed-treatment="preserve">
              <![CDATA[
            <doc attr="test">
               <a>Lorem ipsum dolor sit amet...</a>
               <b>
                  <c>Lorem ipsum dolor sit amet...</c>
                  <d>
                     <e attr="another test"/>
                     <f>Lorem ipsum dolor sit amet...</f>
                  </d>
               </b>
            </doc>
              ]]>
            </fo:block>
      </fo:flow>
   </fo:page-sequence>
</fo:root>

PDF Output (Apache FOP)



回答2:

Here you will find an excellent article about copying source XML.
I've just wrapped it into a simple XSL-FO stub, so full credit to the original author. Here's the full snippet:

<?xml version="1.0" encoding="iso-8859-1"?>

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

    <xsl:template match="/">
        <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
          <fo:layout-master-set>
            <fo:simple-page-master master-name="main">
              <fo:region-body margin="1in"/>
            </fo:simple-page-master>
          </fo:layout-master-set>

          <fo:page-sequence master-reference="main">
            <fo:flow flow-name="xsl-region-body">
              <fo:block text-align="left">
                <xsl:apply-templates mode="escape"/>
              </fo:block>
            </fo:flow>
          </fo:page-sequence>
        </fo:root>
    </xsl:template>

    <xsl:template match="*" mode="escape">
        <!-- Begin opening tag -->
        <xsl:text>&lt;</xsl:text>
        <xsl:value-of select="name()"/>

        <!-- Namespaces -->
        <xsl:for-each select="namespace::*">
            <xsl:text> xmlns</xsl:text>
            <xsl:if test="name() != ''">
                <xsl:text>:</xsl:text>
                <xsl:value-of select="name()"/>
            </xsl:if>
            <xsl:text>='</xsl:text>
            <xsl:call-template name="escape-xml">
                <xsl:with-param name="text" select="."/>
            </xsl:call-template>
            <xsl:text>'</xsl:text>
        </xsl:for-each>

        <!-- Attributes -->
        <xsl:for-each select="@*">
            <xsl:text> </xsl:text>
            <xsl:value-of select="name()"/>
            <xsl:text>='</xsl:text>
            <xsl:call-template name="escape-xml">
                <xsl:with-param name="text" select="."/>
            </xsl:call-template>
            <xsl:text>'</xsl:text>
        </xsl:for-each>

        <!-- End opening tag -->
        <xsl:text>&gt;</xsl:text>

        <!-- Content (child elements, text nodes, and PIs) -->
        <xsl:apply-templates select="node()" mode="escape" />

        <!-- Closing tag -->
        <xsl:text>&lt;/</xsl:text>
        <xsl:value-of select="name()"/>
        <xsl:text>&gt;</xsl:text>
    </xsl:template>

    <xsl:template match="text()" mode="escape">
        <xsl:call-template name="escape-xml">
            <xsl:with-param name="text" select="."/>
        </xsl:call-template>
    </xsl:template>

    <xsl:template match="processing-instruction()" mode="escape">
        <xsl:text>&lt;?</xsl:text>
        <xsl:value-of select="name()"/>
        <xsl:text> </xsl:text>
        <xsl:call-template name="escape-xml">
            <xsl:with-param name="text" select="."/>
        </xsl:call-template>
        <xsl:text>?&gt;</xsl:text>
    </xsl:template>

    <xsl:template name="escape-xml">
        <xsl:param name="text"/>
        <xsl:if test="$text != ''">
            <xsl:variable name="head" select="substring($text, 1, 1)"/>
            <xsl:variable name="tail" select="substring($text, 2)"/>
            <xsl:choose>
                <xsl:when test="$head = '&amp;'">&amp;amp;</xsl:when>
                <xsl:when test="$head = '&lt;'">&amp;lt;</xsl:when>
                <xsl:when test="$head = '&gt;'">&amp;gt;</xsl:when>
                <xsl:when test="$head = '&quot;'">&amp;quot;</xsl:when>
                <xsl:when test="$head = &quot;&apos;&quot;">&amp;apos;</xsl:when>
                <xsl:otherwise><xsl:value-of select="$head"/></xsl:otherwise>
            </xsl:choose>
            <xsl:call-template name="escape-xml">
                <xsl:with-param name="text" select="$tail"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>

</xsl:stylesheet>