• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hazendaz / displaytag / 1753

12 Feb 2026 03:17AM UTC coverage: 77.321% (-0.01%) from 77.334%
1753

push

github

web-flow
Merge pull request #1102 from hazendaz/renovate/javax-support-logback-monorepo

Update dependency ch.qos.logback:logback-classic to v1.5.29 (javax-support)

1438 of 2003 branches covered (71.79%)

Branch coverage included in aggregate %.

4034 of 5074 relevant lines covered (79.5%)

0.8 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

56.18
/displaytag/src/main/java/org/displaytag/export/FopExportView.java
1
/*
2
 * SPDX-License-Identifier: MIT
3
 * See LICENSE file for details.
4
 *
5
 * Copyright 2002-2026 Fabrizio Giustina, the Displaytag team
6
 */
7
package org.displaytag.export;
8

9
import java.io.ByteArrayInputStream;
10
import java.io.File;
11
import java.io.IOException;
12
import java.io.InputStream;
13
import java.io.OutputStream;
14
import java.io.StringReader;
15
import java.io.StringWriter;
16
import java.nio.charset.StandardCharsets;
17
import java.nio.file.Files;
18
import java.nio.file.Path;
19

20
import javax.servlet.jsp.JspException;
21
import javax.xml.transform.Result;
22
import javax.xml.transform.Source;
23
import javax.xml.transform.Transformer;
24
import javax.xml.transform.TransformerConfigurationException;
25
import javax.xml.transform.TransformerException;
26
import javax.xml.transform.TransformerFactory;
27
import javax.xml.transform.sax.SAXResult;
28
import javax.xml.transform.stream.StreamResult;
29
import javax.xml.transform.stream.StreamSource;
30

31
import org.apache.commons.lang3.StringUtils;
32
import org.apache.fop.apps.FOPException;
33
import org.apache.fop.apps.Fop;
34
import org.apache.fop.apps.FopFactory;
35
import org.apache.fop.fo.ValidationException;
36
import org.displaytag.model.TableModel;
37
import org.slf4j.Logger;
38
import org.slf4j.LoggerFactory;
39

40
/**
41
 * Exports the data to a totaled xml format, and then transforms that data using XSL-FO to a pdf. The stylesheet can be
42
 * fed in as a string from the property export.pdf.fo.stylesheetbody, or you can use a default stylesheet named by the
43
 * property export.pdf.fo.stylesheet. When you are developing a stylesheet, this class will output the raw FO if you set
44
 * your log level to debug, which is very handy if you are getting errors or unexpected pdf output. See asFo_us.xsl for
45
 * a sample XSL-FO stylesheet. The basic structure of the intermediate XML is
46
 *
47
 * <pre>
48
 * &lt;table&gt;
49
 *   &lt;header&gt;
50
 *     &lt;header-cell&gt;AntColumn&lt;/header-cell&gt;
51
 *   &lt;/header&gt;
52
 *   &lt;data&gt;
53
 *     &lt;subgroup grouped-by="0"&gt;
54
 *       &lt;row&gt;
55
 *         &lt;cell grouped="true"&gt;Ant&lt;/cell&gt;
56
 *       &lt;/row&gt;
57
 *       &lt;subtotal&gt;
58
 *         &lt;subtotal-cell&gt; &lt;/subtotal-cell&gt;
59
 *       &lt;/subtotal&gt;
60
 *     &lt;/subgroup&gt;
61
 *   &lt;/data&gt;
62
 * &lt;/table&gt;
63
 * </pre>
64
 *
65
 * @see FopExportView#SPECIFIC_STYLESHEET the property that contains the text of a stylesheet
66
 * @see FopExportView#DEFAULT_STYLESHEET the default stylesheet location
67
 * @see XmlTotalsWriter
68
 */
69
public class FopExportView implements BinaryExportView {
1✔
70

71
    /** The log. */
72
    private static Logger log = LoggerFactory.getLogger(FopExportView.class);
1✔
73

74
    /**
75
     * Default stylesheet.
76
     */
77
    public static final String DEFAULT_STYLESHEET = "export.pdf.fo.stylesheet"; //$NON-NLS-1$
78

79
    /**
80
     * A stylesheet as a string on a property.
81
     */
82
    public static final String SPECIFIC_STYLESHEET = "export.pdf.fo.stylesheetbody"; //$NON-NLS-1$
83

84
    /**
85
     * TableModel to render.
86
     */
87
    protected TableModel model;
88

89
    /**
90
     * Sets the parameters.
91
     *
92
     * @param tableModel
93
     *            the table model
94
     * @param exportFullList
95
     *            the export full list
96
     * @param includeHeader
97
     *            the include header
98
     * @param decorateValues
99
     *            the decorate values
100
     *
101
     * @see org.displaytag.export.ExportView#setParameters(TableModel, boolean, boolean, boolean)
102
     */
103
    @Override
104
    public void setParameters(final TableModel tableModel, final boolean exportFullList, final boolean includeHeader,
105
            final boolean decorateValues) {
106
        this.model = tableModel;
1✔
107
    }
1✔
108

109
    /**
110
     * Gets the mime type.
111
     *
112
     * @return "application/pdf"
113
     *
114
     * @see org.displaytag.export.BaseExportView#getMimeType()
115
     */
116
    @Override
117
    public String getMimeType() {
118
        return "application/pdf"; //$NON-NLS-1$
1✔
119
    }
120

121
    /**
122
     * Load the stylesheet.
123
     *
124
     * @return the stylesheet
125
     *
126
     * @throws IOException
127
     *             if we cannot locate it
128
     */
129
    public InputStream getStyleSheet() throws IOException {
130

131
        InputStream styleSheetStream;
132
        final String styleSheetString = this.model.getProperties().getProperty(FopExportView.SPECIFIC_STYLESHEET);
1✔
133
        if (StringUtils.isNotEmpty(styleSheetString)) {
1✔
134
            styleSheetStream = new ByteArrayInputStream(styleSheetString.getBytes(StandardCharsets.UTF_8));
1✔
135
        } else {
136
            final String styleSheetPath = this.model.getProperties().getProperty(FopExportView.DEFAULT_STYLESHEET);
1✔
137
            styleSheetStream = this.getClass().getResourceAsStream(styleSheetPath);
1✔
138
            if (styleSheetStream == null) {
1!
139
                throw new IOException("Cannot locate stylesheet " + styleSheetPath); //$NON-NLS-1$
×
140
            }
141
        }
142
        return styleSheetStream;
1✔
143
    }
144

145
    /**
146
     * Don't forget to enable debug if you want to see the raw FO.
147
     *
148
     * @param out
149
     *            output writer
150
     *
151
     * @throws IOException
152
     *             Signals that an I/O exception has occurred.
153
     * @throws JspException
154
     *             the jsp exception
155
     */
156
    @Override
157
    public void doExport(final OutputStream out) throws IOException, JspException {
158
        final String xmlResults = this.getXml();
1✔
159

160
        final FopFactory fopFactory = FopFactory.newInstance(Path.of(".").toUri());
1✔
161
        final Source xslt = new StreamSource(this.getStyleSheet());
1✔
162
        final TransformerFactory factory = TransformerFactory.newInstance();
1✔
163
        Transformer transformer;
164
        try {
165
            transformer = factory.newTransformer(xslt);
1✔
166
        } catch (final TransformerConfigurationException e) {
×
167
            throw new JspException("Cannot configure pdf export " + e.getMessage(), e); //$NON-NLS-1$
×
168
        }
1✔
169

170
        final boolean outputForDebug = FopExportView.log.isDebugEnabled();
1✔
171
        if (outputForDebug) {
1!
172
            this.logXsl(xmlResults, transformer, null);
×
173
        }
174

175
        Fop fop;
176
        try {
177
            fop = fopFactory.newFop(org.apache.xmlgraphics.util.MimeConstants.MIME_PDF, out);
1✔
178
        } catch (final FOPException e) {
×
179
            throw new JspException("Cannot configure pdf export " + e.getMessage(), e); //$NON-NLS-1$
×
180
        }
1✔
181

182
        final StreamSource src = new StreamSource(new StringReader(xmlResults));
1✔
183
        Result res;
184
        try {
185
            res = new SAXResult(fop.getDefaultHandler());
1✔
186
        } catch (final FOPException e) {
×
187
            throw new JspException("error setting up transform ", e); //$NON-NLS-1$
×
188
        }
1✔
189
        try {
190
            transformer.transform(src, res);
1✔
191
        } catch (final TransformerException e) {
×
192
            if (!(e.getCause() instanceof ValidationException)) {
×
193
                throw new JspException("error creating pdf output", e); //$NON-NLS-1$
×
194
            }
195
            // recreate the errant fo
196
            final ValidationException ve = (ValidationException) e.getCause();
×
197
            this.logXsl(xmlResults, transformer, ve);
×
198

199
        }
1✔
200
    }
1✔
201

202
    /**
203
     * Gets the xml.
204
     *
205
     * @return the xml
206
     *
207
     * @throws JspException
208
     *             the jsp exception
209
     */
210
    protected String getXml() throws JspException {
211
        final XmlTotalsWriter totals = new XmlTotalsWriter(this.model);
1✔
212
        totals.writeTable(this.model, "-1");
1✔
213
        return totals.getXml();
1✔
214
    }
215

216
    /**
217
     * log it.
218
     *
219
     * @param xmlResults
220
     *            raw
221
     * @param transformer
222
     *            the transformer
223
     * @param e
224
     *            the optional exception
225
     *
226
     * @throws JspException
227
     *             wrapping an existing error
228
     */
229
    protected void logXsl(final String xmlResults, final Transformer transformer, final Exception e)
230
            throws JspException {
231
        final StreamResult debugRes = new StreamResult(new StringWriter());
×
232
        final StreamSource src = new StreamSource(new StringReader(xmlResults));
×
233
        try {
234
            transformer.transform(src, debugRes);
×
235
            if (e != null) {
×
236
                FopExportView.log.error("xslt-fo error {}", e.getMessage(), e); //$NON-NLS-1$
×
237
                FopExportView.log.error("xslt-fo result of {}", debugRes.getWriter()); //$NON-NLS-1$
×
238
                throw new JspException("Stylesheet produced invalid xsl-fo result", e); //$NON-NLS-1$
×
239
            }
240
            FopExportView.log.info("xslt-fo result of {}", debugRes.getWriter()); //$NON-NLS-1$
×
241
        } catch (final TransformerException ee) {
×
242
            throw new JspException("error creating pdf output " + ee.getMessage(), ee); //$NON-NLS-1$
×
243
        }
×
244
    }
×
245

246
    /**
247
     * If you are authoring a stylesheet locally, this is highly recommended as a way to test your stylesheet against
248
     * dummy data.
249
     *
250
     * @param xmlSrc
251
     *            xml as string
252
     * @param styleSheetPath
253
     *            the path to the stylesheet
254
     * @param f
255
     *            the f
256
     *
257
     * @throws Exception
258
     *             if trouble
259
     */
260
    public static void transform(final String xmlSrc, final String styleSheetPath, final File f) throws Exception {
261

262
        final FopFactory fopFactory = FopFactory.newInstance(Path.of(".").toUri());
1✔
263
        final InputStream styleSheetStream = FopExportView.class.getResourceAsStream(styleSheetPath);
1✔
264

265
        final Source xslt = new StreamSource(styleSheetStream);
1✔
266
        final TransformerFactory factory = TransformerFactory.newInstance();
1✔
267
        Transformer transformer;
268
        try {
269
            transformer = factory.newTransformer(xslt);
1✔
270
        } catch (final TransformerConfigurationException e) {
×
271
            throw new JspException("Cannot configure pdf export " + e.getMessage(), e); //$NON-NLS-1$
×
272
        }
1✔
273
        Fop fop;
274
        try {
275
            final OutputStream fw = Files.newOutputStream(f.toPath());
1✔
276
            fop = fopFactory.newFop(org.apache.xmlgraphics.util.MimeConstants.MIME_PDF, fw);
1✔
277
        } catch (final FOPException e) {
×
278
            throw new JspException("Cannot configure pdf export " + e.getMessage(), e); //$NON-NLS-1$
×
279
        }
1✔
280

281
        final Source src = new StreamSource(new StringReader(xmlSrc));
1✔
282
        Result res;
283
        try {
284
            res = new SAXResult(fop.getDefaultHandler());
1✔
285
        } catch (final FOPException e) {
×
286
            throw new JspException("error setting up transform ", e); //$NON-NLS-1$
×
287
        }
1✔
288
        try {
289
            transformer.transform(src, res);
1✔
290
        } catch (final TransformerException e) {
×
291
            throw new JspException("error creating pdf output " + e.getMessage(), e); //$NON-NLS-1$
×
292
        }
1✔
293
    }
1✔
294
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc