How can I load a font from /WEB-INF/resources/fonts/foobar.ttf?

Category: 
Tags: fontsFontFactoryWEB-INFresourcesiText 5

The question pretty much says it all. I get following exception:

ExceptionConverter: java.io.IOException: /fonts/CALIBRI.TTF not found as file or resource.

Caused By: java.io.IOException: /fonts/CALIBRI.TTF not found as file or resource.

The physical location of the font file is /WEB-INF/resources/fonts/CALIBRI.TTF. As far as I can see, it should be not found in resources by iText's FontFactory. class.

Posted on StackOverflow on Jun 8, 2015 by m3t4lukas

Answer by BalusC

Based on the source code of iText's RandomAccessSourceFactory#createBestSource(), the FontFactory#register() method can only take a path representing a valid (relative) disk file system path, or a resource URL (e.g. HTTP URL or classpath URL), not a web resource path. You actually supplied a web resource path which you can usually pass to ServletContext#getResource(), getResourceAsStream(), getRealPath() and getRequestDispatcher() only.

There are several ways to get it to work:

1. Convert the web resource path to an absolute disk file system path with help of ServletContext#getRealPath():

String path = getServletContext().getRealPath("/WEB-INF/resources/fonts/foobar.ttf");
FontFactory.register(path);
 
caveat is that this method is highly unportable and environment-dependent. Among others, this will fail when the server is configured to expand the WAR straight into memory instead of on local disk file system, or uses a virtual file system. E.g., by default, it may work on Tomcat, but it may not work on WebLogic. Your stack trace confirms that you're using WebLogic. The chance is big that the `getRealPath()` will by default always return `null`.

2. Move the font file outside /WEB-INF folder so that it's publicly accessible by a HTTP URL. Provided that you can move whole /resources folder one level up, outside /WEB-INF, and that the font is accessible by http://localhost:8080/context/resources/fonts/foobar.ttf (first test in a normal web browser to be sure), then just supply exactly that path.

String path = "http://localhost:8080/context/resources/fonts/foobar.ttf";
FontFactory.register(path);

You can if necessary dynamically construct http://localhost:8080/context (the base URL) as below:

String url = request.getRequestURL().toString();
String baseURL = url.substring(0, url.length() - request.getRequestURI().length()) + request.getContextPath();
String path = baseURL + "/resources/fonts/foobar.ttf";
FontFactory.register(path);

3. Move the font file to the classpath. I.e. make sure it ultimately ends up in /WEB-INF/classes among all those Java class files and resource files. Let's assume that the package name is just fonts. In case you're using Maven, just put it in /main/resources/fonts/foobar.ttf. In case you're using a "plain vanilla" web project with only a src folder where you put all your Java packages and class source files, then create a package fonts the usual way and put the foobar.ttf file right there. Then you can supply a path of /fonts/foobar.ttf.

String path = "/fonts/foobar.ttf";
FontFactory.register(path);

4. In case none of above options apply, your last resort is creating a temporary file with help of File#createTempFile() in container managed temporary folder, grabbing the web resource contents by ServletContext#getResourceAsStream(), writing it to the temp file and finally provide the absolute temp file path.

String tempDir = (String) getServletContext().getAttribute(ServletContext.TEMPDIR);
File tempFile = File.createTempFile("foobar-", ".ttf", new File(tempDir));
InputStream input = getServletContext().getResourceAsStream("/WEB-INF/resources/fonts/foobar.ttf");
Files.copy(input, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
String path = tempFile.getAbsolutePath();
FontFactory.register(path);

Note: the above answer assumes that the code is placed in a "plain" Java EE servlet class. In case you're using JSF — as confirmed by the stack trace — you can obtain the ServletContext by ExternalContext#getContext(), the HttpServletRequest by ExternalContext#getRequest(), the ServletContext attributes by ExternalContext#getApplicationMap(), the real path by ExternalContext#getRealPath(), and the resource stream by ExternalContext#getResourceAsStream().