Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

@font-face with Flying Saucer

I am hopping somebody can help me... it seems like what I am trying to do should be fairly simple but I have been fighting this thing for over a day now and am out of ideas. I have found lots of information on StackOverflow and the Internet at large but nothing has helped me resolve this issue.

I am trying to use itext-2.0.8 along with core-renderer-R8 to create a PDF with an embedded font. I am trying to generate the PDF from valid XHTML and am embedding the font using the @font-face style tag. I have confirmed that the @font-face tag is including the font by opening the file in a browser. And I am always careful to keep the TTF fiel relative to teh XHTML/CSS doc.

In order to try and work my way through this I have created a small 'Hello World' type program to try and embed a font. I have taken two separate approaches and both of them fail to produce the desired result. I have place a copy of this little Eclipse program at http://christopherluft.com/FlyingSaucer.zip

The program produces a PDF in both instances but neither has the PDF embedded as expected. The first method using a file with setDocument produces no errors but also no fonts. And the second method produces a PDF but shows a java.net.MalformedURLException in the debug output.

I have tried numerous permutations of the various paths and URLs; however, none fail to produce the desired result. My suspicion is that I am failing to understand something about ITextRenderer.setDocument; however, I have had a really hard time finding any proper documentation specific to my use case.

The first method I am trying is:

public static void main(String[] args) throws IOException, DocumentException {

    System.getProperties().setProperty("xr.util-logging.loggingEnabled",
            "true");
    XRLog.setLoggingEnabled(true);

    String inputFile = "sample.xhtml";
    String url = new File(inputFile).toURI().toURL().toString();
    String outputFile = "firstdoc.pdf";

    OutputStream os = new FileOutputStream(outputFile);

    ITextRenderer renderer = new ITextRenderer();
    renderer.setDocument(url);
    renderer.layout();
    renderer.createPDF(os);

    os.close();
}

And the second method I am using (which is closer to the actual way we use it in our app) is:

public static void main(String[] args)  throws IOException, DocumentException {

    System.getProperties().setProperty("xr.util-logging.loggingEnabled", "true");
    XRLog.setLoggingEnabled(true);

    String inputFile = "sample.xhtml";
    String url = new File(inputFile).toURI().toURL().toString();

    DocumentBuilder documentBuilder;
    org.w3c.dom.Document xhtmlContent;

    try 
    {
        documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        documentBuilder.setEntityResolver(new XhtmlEntityResolver(new SuppressingEntityResolver()));
        xhtmlContent = documentBuilder.parse(url);

        System.out.println(url);

        String outputFile = "firstdoc.pdf";
        OutputStream os = new FileOutputStream(outputFile);
        ITextRenderer renderer = new ITextRenderer();

        renderer.setDocument(xhtmlContent,".");
        renderer.layout();
        renderer.createPDF(os);

        System.out.println("Finishing up....");
        os.close();
    } 
    catch (SAXException e) 
    {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } 
    catch (ParserConfigurationException e) 
    {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }   
}

And the @font-face inclusion in the XHTML looks like this:

@font-face {

    font-family: 'MisoRegular';

    src: url("miso-regular-webfont.ttf");

    -fs-pdf-font-embed: embed;

    -fs-pdf-font-encoding: Identity-H;
}

Now I feel like this is a really common use case and I imagine that I am just failing to perform one simple step in order to get this to work... the problem is that I have been at this for a while and think I am unable to see the forest through the trees. Any help anybody could offer me would be greatly appreciated. Thank you for your time.

like image 796
tekgrunt Avatar asked Apr 25 '12 20:04

tekgrunt


3 Answers

I've experienced problems with Flying Saucer where it seemed the fonts were not embedded properly and it ended up being that the font I was trying to embed was corrupted or at least not complete. Attributes regarding the Font Family and description were missing. When I tried validating my font using the Validate File option in FontBook on OS X I was promptly presented with warnings saying:

  • 'name' table usability
  • 'name' table structure

However FlyingSaucer failed silently and just proceeded with embedding Times-Roman and Times-Bold as default fonts. When I tried using a font that passed the FontBook validation I found the font was embedded properly.

I spent hours trying to increase the verbosity in logging from FlyingSaucer to get more information and didn't discover this was the case until I stepped through the entire rendering process and just happened to note that FontFamily was set to "" in the debugger after loading the font and that it didn't include the name I expected it to be registered under.

Bottom line: If you're having an issue with FlyingSaucer and fonts and are banging your head against the wall make sure your font is valid.

like image 200
cclark Avatar answered Oct 01 '22 12:10

cclark


I struggled with the same problem like tekgrunt, but finally I got a PDF with a custom font (Computer Modern) embedded. My experiences:

  • OTF format for the font didn't work (crash in an iText function)
  • TTF format DID work, after I made the following change: In the CSS, use the same font-family name like the one that is encoded in the font. Set a breakpoint in
    org.xhtmlrenderer.pdf.ITextFontResolver.addFontFaceFont()
    and find out which value the variable "fontFamily" gets. If this isn't the same like the CSS font-family, I guess it won't work.

Of course these problems should be reported by the library to the user.

like image 42
m4t Avatar answered Oct 01 '22 13:10

m4t


I was facing a similar issue, font not getting applied to the final pdf, and m4t's suggestion about debugging really helped me in identifying the issue. So thanks for that!

In my case actually there were two issues:

  1. The src attribute specified as part of @font-face rule had 2 values, the url and the format. It looked something like
@font-face {
   src: url("../../static/fonts/OpenSans-Regular.ttf") format("ttf");
}

Because of the second value format, the CSSParser broke causing the src attribute to be completely skipped silently. As a result in further processing the render did not know where to pick the fonts from. The issue got sorted once I removed the format part from the value.

  1. Once the url was parsed successfully by the CSSParser, it could not be resolved correctly, as the url specified in the css was a relative one and there was no baseUrl specified explicitly (optional 2nd parameter) when calling the setDocumentAsString on the renderer. I fixed the issue by loading uri of static resources explicitly in my code using the classloader, and then setting that as the base url. After that change the path for the font file was resolved successfully and applied to my pdf as well.

Critical thing to note here was, that the base url supplied to setDocumentAsString must be a URI (file:/folder/structure/static/), and must end with a trailing slash ('/'). Otherwise the file and url doesn't get resolved correctly.

Hope this helps someone facing a similar problem.

like image 44
viveksd87 Avatar answered Oct 01 '22 13:10

viveksd87