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

hazendaz / displaytag / 1779

14 Feb 2026 04:52AM UTC coverage: 76.997%. Remained the same
1779

push

github

web-flow
Merge pull request #1107 from hazendaz/copilot/update-opentext-pdf-to-9

Migrate itextpdf 5.5.13.5 to openpdf 2.4.0

1380 of 1939 branches covered (71.17%)

Branch coverage included in aggregate %.

229 of 277 new or added lines in 5 files covered. (82.67%)

3902 of 4921 relevant lines covered (79.29%)

0.79 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 jakarta.servlet.jsp.JspException;
10

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

200
        }
1✔
201
    }
1✔
202

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

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

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

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

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

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

© 2026 Coveralls, Inc