Embedding an image in RTF with Java

Posted by {"name"=>"Palash Ray", "email"=>"paawak@gmail.com", "url"=>"https://www.linkedin.com/in/palash-ray/"} on October 25, 2006 · 3 mins read

Ever wondered how to embed images in RTF docs? I was trying to do that for a couple of days...
The blasted MS Office application has obfuscated the RTF format so badly, that it is difficult to read it by opening it in another simple editor like NotePad or TextPad. A beginner like me is lost simply in the sheer volume of redundant control words and back-slashes.
So, if you want to learn the RTF format frtom the pros, a good idea to install OpenOffice (you can dowload it for free) in your PC and save the documents in RTF format and opening them in NotePad or TextPad.
Some basics facts about RTF format:

  • All RTF control words start with a back-slash
  • All RTF documents should start with {rtf1ansi and end with a }

If you want to embed a picture, you have to write the entire binary data of the picture in the RTF file. This is done as follows:
{*shppict{pictpicw800pich600jpegblip
ffd8ffe000104a46494600010201006000600000ffed182e50686f746f73686f
7020332e30003842494d03ed0a5265736f6c7574696f6e000000001000600000
0001000100600000000100013842494d040d18465820476c6f62616c204c6967}}
picw800 dictates that the picture is 800 pixels wide.
Similarly, pich600 means that the height of the picture is 600 pixels.
jpegblip is used when the given image is in JPEG format. For PNG images, use pngblip.
After these control words, the actual binary data of the image starts.
The following rules are followed:

  1. The raw data is from the image file is read in bytes and formatted as hexadecimal values.
  2. If a given byte is single digit, it is prepended by 0. For example, if a byte value is f, it is made 0f.
  3. When all the bytes are written, immediately indicate the end of the data by closing the braces.

The second point is most important.
Given below is the java source-code. In order to make it look nice, I have gone to a new line after every 64 bytes of raw data. But this is not necessary.

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.swing.ImageIcon;
public class PictureReader {
 public static void main(String[] args) {
  String ImageName = "/images/WaterLilies.jpg";
  InputStream imageIp = PictureReader.class.getResourceAsStream(ImageName);
  ImageIcon icon = new ImageIcon(PictureReader.class.getResource(ImageName));
  StringBuffer sb = new StringBuffer("{rtf1ansiparpardplain");
  sb.append("n{*shppict{pict")
  .append("picw").append(icon.getIconWidth())
  .append("pich").append(icon.getIconHeight())
  .append("jpegblipn"); //   for PNG images, use pngblip
  int count = 0;
  while(true) {
   try {
    int i = imageIp.read();
    if (i == -1) {
     break;
    }
    String hexStr = Integer.toHexString(i);
    if (hexStr.length() == 1) {
     hexStr = "0" + hexStr;
    }
    count += 2;
    sb.append(hexStr);
    if (count == 64) {
     count = 0;
     sb.append("n");
    }
   } catch (IOException e) {
    e.printStackTrace();
   }
  }
  sb.append("}}n}n");
  try {
   FileOutputStream fos = new FileOutputStream("Pic.rtf");
   fos.write(sb.toString().getBytes());
  } catch (IOException e) {
   e.printStackTrace();
  }
 }
}