Published on Jan 4 2012 in Java

Java hosting clients happen to require custom font for their dynamically generated images or reports. Basically, there are 2 methods of making new TTF fonts available to your servlets or JSP - installing in JDK directory tree or outside of it. The methods and their variants will be described below in more details with examples.

Installation outside of JDK tree

This is preferred method as you can embed the font files inside your application (.war or .jar) and do not depend on what fonts are provided by the JVM your application is running in or underlying OS. You can also place fonts in a subdirectory of your home directory. The fonts can be loaded on the fly.

Optionally - when you request the install on our side - we can choose to install the font system-wide so that any JDK can see it. Example of a font already installed this way on our shared Java hosting servers is Bitstream Vera that can be used (in a slanted variant in this example) simply by calling its name:

Font font = new Font("Bitstream Vera Sans Mono Oblique",Font.PLAIN,32);
Bitstream Vera Sans Bold from OS (seen by JVM) Verdana Bold from WEB-INF/lib of a web application
bitstream verdana

Installation in JDK tree

Open support ticket and ask for installaton of your font for given JDK. If approved, it will be installed in /opt/YOURJDKVERSION/jre/lib/fonts directory e.g. /opt/jdk1.6.0_25/jre/lib/fonts. You should be aware that if you change JDK version your font will no longer be accessible unless it is also installed in the current JDK directory tree. Note: there is no need for fonts.dir and fonts.scale update with mkfontscale/mkfontdir commands by support.

As a last resort you can install your own JDK in your home directory, overwrite JAVA_HOME in ~/.bashrc and copy the fonts to $JAVA_HOME/jre/lib/fonts directory. This approach is not recommended in our environment as .bashrc can be overwritten by JCP control panel though.

By default, JVM will see fonts provided by host OS e.g. from /usr/share/fonts and the ones placed in $JAVA_HOME/jre/lib/fonts. To start with, you can check fonts availability with the following JSP code:

<%@page pageEncoding="UTF-8" language="java" import="java.awt.*"%>
<%@page contentType="text/html;charset=UTF-8"%>
<html><head><META http-equiv="Content-Type" content="text/html;charset=UTF-8"></head>
<body>
<% GraphicsEnvironment e = GraphicsEnvironment.getLocalGraphicsEnvironment();
    Font[] fonts = e.getAllFonts(); // Get the fonts
    for (Font f : fonts) {
         out.println(f.getFontName());
         out.println("<br/>");
    }
%>
</body></html>

In the next code snippet we generate an image with text written using our TTF font placed in WEB-INF/lib.

<%@page import="java.awt.*,java.io.*,java.awt.image.*,java.awt.font.*,java.awt.geom.*,javax.imageio.*"%>
<% String text = "Custom font test";
    String font_file = "verdana.ttf";
    font_file = request.getRealPath("WEB-INF/lib/" + font_file);
    Font font = Font.createFont(Font.TRUETYPE_FONT, new FileInputStream(font_file));
    font = font.deriveFont(25.0f);
    
    // create temporary 1x1 image to get FontRenderingContext needed to calculate image size
    BufferedImage buffer = new BufferedImage(1,1,BufferedImage.TYPE_INT_RGB);
    Graphics2D g2 = buffer.createGraphics();
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    FontRenderContext fc = g2.getFontRenderContext();
    Rectangle2D bounds = font.getStringBounds(text,fc);
    
    // calculate the size of the text
    int width = (int) bounds.getWidth();
    int height = (int) bounds.getHeight();
    
    // prepare final image with proper dimensions
    buffer = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    g2 = buffer.createGraphics();
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    g2.setFont(font);
    
    // actually do the drawing
    g2.setColor(Color.white);
    g2.fillRect(0,0,width,height);
    g2.setColor(Color.blue);
    g2.drawString(text,0,(int)-bounds.getY());
    
    // set the content type, get the output stream and print image as PNG
    response.setContentType("image/png");
    OutputStream os = response.getOutputStream();
    ImageIO.write(buffer, "png", os);
    os.close();
%>

As the above image generation script outputs data directly to browser, it can be included in another page with:

<img src="/pathto/font.jsp">

In the same easy way we can use custom fonts in our dynamically generated PDF documents. We need to download a PDF library for example iText and place the included jar (e.g. itextpdf-5.1.3.jar) in WEB-INF/lib directory of our web application.

<%@page import="java.io.*,java.awt.Color,java.io.ByteArrayOutputStream,"%>
<%@page import="com.itextpdf.text.*,com.itextpdf.text.pdf.*"%>
<% FontFactory.register(request.getRealPath("verdana.ttf"), "my_font_alias");
    Document document = new Document();
    PdfWriter.getInstance(document, new FileOutputStream(request.getRealPath("hello.pdf")));
    document.open();
    Paragraph p = new Paragraph("This is a PDF paragraph written with custom font", FontFactory.getFont("my_font_alias", 18.0f, Font.BOLDITALIC, new BaseColor(0, 0, 255)));
    document.add(p);
    document.close();
%>

And if we decide to send the PDF directly to the browser it may look like below.

<%@page import="java.io.*,java.awt.Color,java.io.ByteArrayOutputStream"%>
<%@page import="com.itextpdf.text.*,com.itextpdf.text.pdf.*"%>
<% FontFactory.register(request.getRealPath("verdana.ttf"), "my_font_alias");
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    Document document = new Document();
    PdfWriter.getInstance(document, baos);
    document.open();
    Paragraph p = new Paragraph("This is a PDF paragraph written with custom font", FontFactory.getFont("my_font_alias", 18.0f, Font.BOLDITALIC, new BaseColor(0, 0, 255)));
    document.add(p);
    document.close();
    response.setHeader("Expires", "0");
    response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
    response.setHeader("Pragma", "public");
    // setting the content type
    response.setContentType("application/pdf");
    // the contentlength is needed for MSIE!!!
    response.setContentLength(baos.size());
    // write ByteArrayOutputStream to the ServletOutputStream
    ServletOutputStream os = response.getOutputStream();
    baos.writeTo(os);
    os.flush();
%>

To sum up, JDK offers quite simple custom TTF font support and leaves the decision on where to keep the font to the developer.