Sunday, December 7, 2014

how to use Flying saucer API for PDF generation using HTML with iText, in English Spanish Chinese with fonts with Italic and Bold.

Requirement:
1. In Web application write a code to generate a PDF file
2. PDF file should have look and feel just like the HTML
3. PDF file contents could be in English, Spanish or Chinese

Solution:
Get the maven dependencies :

1. <dependency>
<groupId>com.lowagie</groupId>
<artifactId>itext</artifactId>
<version>2.0.8</version>
</dependency>

2.  This one is required to be R8, the higher version would not work.
    <dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>core-renderer</artifactId>
<version>R8</version>
</dependency>

3.  <dependency>
       <groupId>apache-xerces</groupId>
          <artifactId>xercesImpl</artifactId>
       <version>2.9.1</version>
  </dependency>

Store the HTML on the server outside the WAR or bundle it inside the WAR.

Use below code to read the HTML and pass it to Flying Saucer api.

ByteArrayOutputStream os = new ByteArrayOutputStream();
String html = readFile(myfile);
//see below for readFile method code.

ITextRenderer renderer = new ITextRenderer();

//add font file, this will embed the font file in the pdf, so pdf file will be large in size.

renderer.getFontResolver().
addFont("images/ARIALUNI.TTF", 
BaseFont.IDENTITY_H, BaseFont.EMBEDDED); 

renderer.getFontResolver().
addFont("images/VERDANA.TTF", 
BaseFont.IDENTITY_H, BaseFont.EMBEDDED);

renderer.getFontResolver().
addFont("images/verdanab.ttf", 
BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
            
renderer.getFontResolver().
addFont("images/verdanai.ttf", 
BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
            
renderer.getFontResolver().
addFont("images/verdanaz.ttf", 
BaseFont.IDENTITY_H, BaseFont.EMBEDDED);

ResourceLoaderUserAgent callback = 
new ResourceLoaderUserAgent(renderer.getOutputDevice());
            callback.setSharedContext(renderer.getSharedContext());
            renderer.getSharedContext().setUserAgentCallback(callback);

renderer.setDocumentFromString(finalHTML); 

renderer.layout();

renderer.createPDF(os);
//this will create the pdf in the byte array output stream

OutputStream file = new FileOutputStream(
new File("/filelocation/test.pdf")); 
//open output stream to physical file

file.write(os.toByteArray()); 
//get the bytes from byte array output stream and write to //outputstream

file.close(); //close the output stream

os.close(); //close the byte array output stream
      
//Override this class so that you can manipulate the font or //image files' location that you put in the CSS or in //<img> tag //in html      

private class ResourceLoaderUserAgent extends ITextUserAgent
    {
        public ResourceLoaderUserAgent(
                   ITextOutputDevice outputDevice) {
                     super(outputDevice);
        }

        protected InputStream resolveAndOpenStream(String uri) {
            log.debug("uri : "+uri);
            InputStream is = super.resolveAndOpenStream(uri);
            String fileName = "";
            
            try {
                String[] split = uri.split("/");
                fileName = split[split.length - 1];
                log.debug("fileName : "+fileName);
            } catch (Exception e) {
                return null;
            }
            
            if (is == null) {
                // Resource is on the classpath
                try{
                    is = ResourceLoaderUserAgent.
                           class.getClassLoader().
                            getResourceAsStream("images/" 
                             + fileName);
                } catch (Exception e) {
                 log.error(" resouce on class path :",e);
                }
            }

            if (is == null) {
                // Resource is in the file system
                try {
                    is = new FileInputStream(
                        new File("images/" + fileName));
                } catch (Exception e) {
                 log.error(" resouce on file path :",e);
                }
            }

            return is;
        }
    }

//private method to read a file
private String readFile(String myfile){

InputStream inputStream = new FileInputStream(file);
Reader      reader      = new InputStreamReader(
                  inputStream,"UTF-8");
BufferedReader readerf = new BufferedReader(reader);
String         line = null;
StringBuilder  stringBuilder = new StringBuilder();
String         ls = System.getProperty("line.separator");

    while((line = readerf.readLine() ) != null ) {
     if(!StringUtils.isEmpty(line)){
     stringBuilder.append( line);
          stringBuilder.append( ls );
     }
    }
    return stringBuilder.toString();

}
Below is the HTML 
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>
<html>
 <head> 
<style type="text/css">
div.ErrorPage {
margin: 15px auto 25px;
width: 514px;
}
div.ErrorPage h1 {
padding: 0 0 3px 55px;
color: #e66600;
background: url(images/icons_all.gif) no-repeat 20px -1679px;
font-size: 2em;
line-height: 1.25em;
}
@font-face {
    font-family: 'Verdana';
    src: url("VERDANA.TTF");
    -fs-pdf-font-embed: embed;
    -fs-pdf-font-encoding: Identity-H;
}
@font-face {
    font-family: 'Verdana';
    src: url("verdanai.ttf");
    -fs-pdf-font-embed: embed;
    -fs-pdf-font-encoding: Identity-H;
}
@font-face {
    font-family: 'Verdana';
    src: url("verdanab.ttf");
    -fs-pdf-font-embed: embed;
    -fs-pdf-font-encoding: Identity-H;
}
@font-face {
    font-family: 'ARIALUNI';
    src: url("ARIALUNI.TTF");
    -fs-pdf-font-embed: embed;
    -fs-pdf-font-encoding: Identity-H;
}
body {
font-family: "Arial Unicode MS";
font: 11pt;
background: #fff;
color: #252525;
font-weight: bold;
}

</style>
</head>
<body>
<img src='https://productionsite/where/image/is_stored.png'></img>
<div>Chinese text here or spanish text here or english text here</div>
</body>
</html>
That's all folks.
This is how to use Flying saucer API for PDF generation using HTML with iText, in English Spanish Chinese with fonts with Italic and Bold.

If you need any help please leave comment below and let me know and I will help you in this.

2 comments:

Unknown said...

This post has helped me alot. Just wanted to tell you that!

I am writting spring boot application and usually resource loader just locates my external files within resources/static , but i couldnt get pdf to render correctly so i have realized for some reason path is not correct, and the patch which was working for me is src/main/resources/static/css/style.css , but only on my localserver, and when i save pdf files to my harddrive.

that way i could load external css files and everything was working great. Now when i have deployed app to amazon aws suddenly, i just serve pdf for download from my controller, css is not loading again ! any idea how to solve this problem ? I have tried all possible solutions /css/style.css css/style.css and so on with no luck.
When i try to access css directly from browser i can access it without problems http://52.23.223.45:8080/css/style.css

Flyingsaucer has proven to be very complicated sometimes.

Thanks

Anonymous said...

Heya, am facing a bit problem in image source, if I try secure image source "https://www...." it does not load the image. If using http it works fine.
This only happens on my server and runs fine on my local system, can you help me in this regard.