Search code examples
xmlpdfsvgxsl-foapache-fop

What's the best way to exclude a node in an svg file when using xsl-fo to generate a pdf?


I have some SVG files that I'd like to create a PDF with. For simplicity lets assume each one represents one page in the PDF. There is a node in the SVG containing some text that I would like to exclude. How do I go about doing so? Here's a sample SVG file and fo to include it as a page in the PDF.

Lets say there exists a file named /home/dave/images/some_image.svg containing this:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="470" height="363" xml:space="preserve">
  <g clip-path="url(#some-path)">
    <rect id="some-rect" fill="#FFFFFF" stroke="#666" height="363" width="470" opacity="1" x="0" y="0"/>
    <defs id="some-defs">
      <clipPath id="some-path" x="0" y="0" width="100%" height="100%">
        <rect height="363" x="0" y="0" width="470" fill="#fff"/>
      </clipPath>
    </defs>
    <g id="some-group">
      <path id="a-path"/>
      <g id="a-container">
        <text id="some-text" x="235" y="181">This text needs to go</text>
        <image x="-2000" y="-1500" width="4000" height="3000" xlink:href="http://www.somewebsite.com/image.jpg" id="some-img"/>
      </g>
    </g>
  </g>
</svg>

and now for a sample fo that uses it

<?xml version="1.0" encoding="utf-8"?>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">

  <fo:layout-master-set>
    <fo:simple-page-master master-name="page" margin="0pt"
                           page-height="363pt" page-width="470pt">
      <fo:region-body />
    </fo:simple-page-master>
  </fo:layout-master-set>

  <fo:page-sequence master-reference="page">
    <fo:flow flow-name="xsl-region-body">
      <fo:block>
        <fo:external-graphic 
            src='/home/dave/images/some_image.svg'/>
      </fo:block>
    </fo:flow>
  </fo:page-sequence>

</fo:root>

So this works fine for including the whole svg "as is". How can I exclude the text node?

<text id="some-text" x="235" y="181">This text needs to go</text>

As for the real files I'm using they reference a font that I won't need to include in the PDF and the text will be behind the image anyway thus not viewable.


Solution

  • Keeping <fo:external-graphic /> : XSLT pre-processing

    If you want or need to keep the <fo:external-graphic /> approach, then you should certainly pre-process your SVG using XSLT, as suggested by Wivani

    A copy-all template and a remove template should be enough, like:

    <xsl:template match="node()|@*">
      <xsl:copy>
         <xsl:apply-templates select="node()|@*"/>
      </xsl:copy>
    </xsl:template>
    
    <xsl:template match="text[@id='some-text']"/>
    

    You can keep your FO as-is, maybe changing the SVG path to the new, pre-processed SVG:

    <fo:external-graphic src='/home/dave/images/some_image_stripped.svg'/>
    

    For more specific info, see other SO questions like:

    Alternative using <fo:instream-foreign-object />

    If, on the other hand, you want to keep a one-step process, you can investigate at inlining you SVG in your XML-FO, using <fo:instream-foreign-object />.

    But this would likely require you to change your processing logic, creating the XML-FO by means of an XSL transformation from your SVG file. This could conflict with other requirements of yours.

    You can have a look at Displaying SVG using XSLFO for more info on this technique.