Using iText, how can I apply css to my PDF documen

2019-04-06 04:57发布

问题:

Thanks for your help...

Problem

While converting an html table tag/snippet (which I have converted to string) into a PDF document...

I am able to successfully apply css styles to the PDF document using this technique...

CSSResolver cssResolver = XMLWorkerHelper.getInstance().getDefaultCssResolver(false);  
cssResolver.addCss("td {border-right: white .1px solid;}", true);

But, I am unsuccessful applying css to the PDF document using existing a css file (i.e., using CssFile object), like this...

CSSResolver cssResolver = new StyleAttrCSSResolver();
InputStream csspathtest = Thread.currentThread().getContextClassLoader().getResourceAsStream("styles/itextweb.css");            
CssFile cssfiletest = XMLWorkerHelper.getCSS(csspathtest);
cssResolver.addCss(cssfiletest);             

...I have not been able to get this working, and dont know what the issue is...I'm getting...

Java.io.IOException The document has no pages

Question:

How do I properly use CssFile with CssResolver to apply css styles -- i.e., from existing ".css" files -- to my PDF document? (What is wrong with the way I am using iText to accomplish this?)

(Again, thank you for any help/guidance on this)

================= BELOW IS MORE DETAILED INFOMATION ===================

Java 6, JSF (Mojarra) 2.1.11, Primefaces v3.4.2, itextpdf v5.3.4, xmlworker v1.2.1

This is the "printPDF" function in question...

public void createPDF() throws DocumentException, CssResolverException
{
    FacesContext context = FacesContext.getCurrentInstance();
    ExternalContext econtext = context.getExternalContext();

    try
    {
        String htmlstring   = context.getExternalContext().getRequestParameterMap().get("testForm:htmlstring");

        InputStream is = new ByteArrayInputStream(htmlstring.getBytes());             
        ByteArrayOutputStream baos = new ByteArrayOutputStream(); 

        // step 1
        Document document = new Document();

        // step 2
        PdfWriter writer = PdfWriter.getInstance(document, baos);

        writer.setInitialLeading(12.5f);

        // step 3
        document.open();

        HtmlPipelineContext htmlContext = new HtmlPipelineContext(null);

        htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());

        // CSS
        CSSResolver cssResolver = new StyleAttrCSSResolver();
        InputStream csspathtest = Thread.currentThread().getContextClassLoader().getResourceAsStream("styles/itextweb.css");            
        CssFile cssfiletest = XMLWorkerHelper.getCSS(csspathtest);
        cssResolver.addCss(cssfiletest);             

        Pipeline<?> pipeline =  new CssResolverPipeline(cssResolver, new HtmlPipeline(htmlContext, new PdfWriterPipeline(document, writer)));

        XMLWorker worker = new XMLWorker(pipeline, true);
        XMLParser p = new XMLParser(worker);
        p.parse(is); //new FileInputStream("results/demo2/walden.html"));

        // step     
        document.close();

        //post back...
        HttpServletResponse response = (HttpServletResponse) context.getExternalContext().getResponse();
        response.setContentType("application/pdf");
        response.setHeader("Expires", "0");
        response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");           
        response.setHeader("Content-Type", "application/pdf");
        response.setHeader("Content-disposition","attachment;filename=file.pdf");
        response.setContentLength(baos.size());
        OutputStream os = response.getOutputStream();
        baos.writeTo(os);
        os.flush();
        os.close();
        context.responseComplete();
    }
    catch (FileNotFoundException e)
    {
        e.printStackTrace();
    }
    catch (IOException e)
    {
        e.printStackTrace();
    }
    catch (DocumentException e)
    {
        e.printStackTrace();
    }
}

This is the page containing the html table snippet to be parsed to pdf (i.e., id="table1")...

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:c="http://java.sun.com/jsp/jstl/core"
      xmlns:p="http://primefaces.org/ui">
    <f:view contentType="text/html">
        <h:head>
            <title>test html-to-pdf with itext...</title>
            <meta charset="utf-8" />
        </h:head>
        <h:body>
            <h:form id="testForm">

                <p:panel id="queryPanel"  header="...test itext html-to-pdf conversion..." style="width:100%;">

                    <table id='table1'>
                        <thead class="dt-thd">
                            <tr id="table1-h-hdr-row">
                                <th style="width: 120px" class="dt-hhdr-c " >Last name</th>
                                <th style="width: 120px" class="dt-hhdr-c " >First Name</th>
                                <th style="width: 120px" class="dt-hhdr-c " >Middle Name</th>
                                <th style="width: 180px" class="dt-hhdr-c " >Date Of Birth</th>

                            </tr>
                        </thead>
                        <tbody>
                            <tr class="dt-r-even" onclick="uiOnRowClick('table1', 14, this)">
                                <td style="width: 120px" class="dt-c  row-selected-left" ><a class="column-link " onfocus="uiOnRowClick('table1', 14, this.parentNode.parentNode)" onclick="storeFilters()" href="#">lastnameAAA</a></td>
                                <td style="width: 120px" class="dt-c  row-selected" >firstnameAAA</td>
                                <td style="width: 120px" class="dt-c  row-selected" >A</td>
                                <td style="width: 180px" class="dt-c  row-selected" >11/27/1971</td>
                            </tr>
                            <tr class="dt-r-odd" onclick="uiOnRowClick('table1', 14, this)">
                                <td style="width: 120px" class="dt-c " ><a class="column-link " onfocus="uiOnRowClick('table1', 14, this.parentNode.parentNode)" onclick="storeFilters()" href="#">lastnameBBB</a></td>
                                <td style="width: 120px" class="dt-c " >firstnameBBB</td>
                                <td style="width: 120px" class="dt-c " >B</td>
                                <td style="width: 180px" class="dt-c " >01/15/1951</td>
                            </tr>
                            <tr class="dt-r-even" onclick="uiOnRowClick('table1', 14, this)">
                                <td style="width: 120px" class="dt-c " ><a class="column-link " onfocus="uiOnRowClick('table1', 14, this.parentNode.parentNode)" onclick="storeFilters()" href="#">lastnameCCC</a></td>
                                <td style="width: 120px" class="dt-c " >firstnameCCC</td>
                                <td style="width: 120px" class="dt-c " >C</td>
                                <td style="width: 180px" class="dt-c " >02/16/1962</td>
                            </tr>
                            <tr class="dt-r-odd" onclick="uiOnRowClick('table1', 14, this)">
                                <td style="width: 120px" class="dt-c " ><a class="column-link " onfocus="uiOnRowClick('table1', 14, this.parentNode.parentNode)" onclick="storeFilters()" href="#">lastnameDDD</a></td>
                                <td style="width: 120px" class="dt-c " >firstnameDDD</td>
                                <td style="width: 120px" class="dt-c " >D</td>
                                <td style="width: 180px" class="dt-c " >03/17/1973</td>
                            </tr>
                            <tr class="dt-r-even" onclick="uiOnRowClick('table1', 14, this)">
                                <td style="width: 120px" class="dt-c " ><a class="column-link " onfocus="uiOnRowClick('table1', 14, this.parentNode.parentNode)" onclick="storeFilters()" href="#">lastnameEEE</a></td>
                                <td style="width: 120px" class="dt-c " >firstnameEEE</td>
                                <td style="width: 120px" class="dt-c " >E</td>
                                <td style="width: 180px" class="dt-c " >04/18/1984</td>
                            </tr>
                            <tr class="dt-r-odd" onclick="uiOnRowClick('table1', 14, this)">
                                <td style="width: 120px" class="dt-c " ><a class="column-link " onfocus="uiOnRowClick('table1', 14, this.parentNode.parentNode)" onclick="storeFilters()" href="#">lastnameFFF</a></td>
                                <td style="width: 120px" class="dt-c " >firstnameFFF</td>
                                <td style="width: 120px" class="dt-c " >F</td>
                                <td style="width: 180px" class="dt-c " >05/19/1995</td>
                            </tr>
                        </tbody>
                    </table>

                    <p:commandButton
                        id="printPdf"
                        value="Print"
                        action="#{testBean.createPDF2}"
                        ajax="false"
                        onclick="printPreview(this);this.form.target='_blank'"/>

                    <h:inputHidden id="htmlstring" value="no value"/>

                </p:panel>

            </h:form>

            <h:outputStylesheet  library="styles"    name="itextweb.css"      />
            <h:outputScript      library="primefaces" name="/jquery/jquery.js" />
            <h:outputScript      library="primefaces" name="/jquery/plugins/ui/jquery-ui.custom.js" />
            <h:outputScript      library="primefaces" name="/jquery/plugins/inputmask/maskedinput.js" />
            <h:outputScript      library="js"         name="itextweb.js" />
        </h:body>
    </f:view>
</html>

Here is the javascript used...

function uiOnRowClick(a, b, c)
{
    alert("uiOnRowClick(a,b,c) function called...blah...");
}

function storeFilters()
{
    alert("storeFilters() function called...bleah...");
}

function printPreview(e)
{
    var t = document.getElementById("table1");
    var htmlstring = "<table id='table1-hdr' class='dt' style='width:2416px;position:absolute'>" + t.innerHTML + "</table>";
    document.getElementById('testForm:htmlstring').value = htmlstring;
}

Here is the CSS stylesheet for this example...

.text1
{
    background-color: transparent !important;
    font-weight: bold;
    font-size: 2em;
    color: blue;
    text-align:center;
}

.ui-inputfield {
    background: white !important;
    height: 10px !important;
    vertical-align: middle;
    display:inline-block;
    white-space: nowrap;
}


.ui-button
{
    margin-top: .5px !important;
    vertical-align: middle !important;
    display:inline-block !important;
    white-space: nowrap !important;
    text-align: center !important;
}

.ui-message-error
{
    background: transparent !important;
    border: none !important;
    font-size: .9em !important;
    font-weight: normal !important;
    font-family: Arial, sans-serif !important;
}

.ui-message-error-icon {
    display: none;
}

.ui-messages-error
{
    background: transparent !important;
    border: none !important;
    font-size: .9em !important;
    font-weight: normal !important;
    font-family: Arial, sans-serif !important;
}

.ui-messages-error-icon {
    display: none;
}

.ui-inputfield.ui-state-error
{
    background: pink !important;
}

form *
{
box-sizing: content-box !important;
-moz-box-sizing: content-box !important;
-ms-box-sizing: content-box !important;
}


.ui-widget, .ui-widget .ui-widget
{
font-size: 90% !important;
}


.dt-thd
{

}

.table1-h-hdr-row
{

}

.dt-hhdr-c
{
    color: blue;
    background-color:  lightgray;
}

.dt-r-odd
{
    background-color: aliceblue;
}

.dt-r-even
{
    background-color:  lightskyblue;
}

.dt-c
{
    font-size: 8px;
    font-weight: normal;
}

Below is the pom.xml used with this example (which illustrates dependenies/versions/etc)...

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>aaa.bbb.ccc</groupId>
    <artifactId>itextweb-war</artifactId>
    <packaging>war</packaging>
    <version>1</version>
    <name>itextweb-war</name>
    <url>http://maven.apache.org</url>

    <dependencies>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

        <dependency>
            <groupId>com.sun.faces</groupId>
            <artifactId>jsf-api</artifactId>
            <version>2.1.11</version>
        </dependency>

        <dependency>
            <groupId>com.sun.faces</groupId>
            <artifactId>jsf-impl</artifactId>
            <version>2.1.11</version>
        </dependency>

        <dependency>
            <groupId>javax.el</groupId>
            <artifactId>el-api</artifactId>
            <version>2.2</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.web</groupId>
            <artifactId>el-impl</artifactId>
            <version>2.2</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>3.1.1.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>3.1.1.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>

        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.4</version>
        </dependency>

        <dependency>
            <groupId>org.primefaces</groupId>
            <artifactId>primefaces</artifactId>
            <version>3.4.2</version>
        </dependency>

        <dependency>
            <groupId>org.primefaces.themes</groupId>
            <artifactId>aristo</artifactId>
            <version>1.0.1</version>
        </dependency>

        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.3.4</version>
        </dependency>

        <dependency>
            <groupId>com.itextpdf.tool</groupId>
            <artifactId>xmlworker</artifactId>
            <version>1.2.1</version>
        </dependency>

    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.3</version>
            </plugin>

        </plugins>
        <finalName>${project.name}-${project.version}</finalName>
    </build>
</project>

回答1:

Created a project starting from your code, tested and it works very well to create a PDF file from a html file with external CSS.

Here is the public repository: https://github.com/valentin-nasta/itext-html-css-pdf-jsf-template .



回答2:

I think a better and more powerful (and higher level of abstraction) tool for this purpose (generating PDF files from XHTML) would be Flying Saucer, aka. xhmlrenderer.

It gives complete CSS2 support and a few CSS3 properties for print media, and you can extend it to render any PDF's (see the ReplacementElementFactory API).

I've used it to render dynamic PDF reports (with freemarker), with custom tags, like . Very simple tool and easy to understand.

See: http://code.google.com/p/flying-saucer/

The user guide can be found here: http://flyingsaucerproject.github.com/flyingsaucer/r8/guide/users-guide-R8.html



回答3:

nope flying saucer is out of lifetime not supported anymore. we having lots of trouble with it. for private use its ok, but not in a business case.



回答4:

For cssResolver you need to specify the absoulte path for the .css as shown below.

HtmlPipelineContext htmlContext = new HtmlPipelineContext(null);

htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());

CSSResolver cssResolver = XMLWorkerHelper.getInstance().getDefaultCssResolver(false);

cssResolver.addCssFile("src\\template\\style.css",true);
Pipeline<?> pipeline = new CssResolverPipeline(cssResolver, new HtmlPipeline(htmlContext, new PdfWriterPipeline(document, writer)));

XMLWorker worker = new XMLWorker(pipeline, true);
XMLParser xmlParser = new XMLParser(worker);


标签: java itext