public byte[] writeFont () {
        // allocate a buffer to hold the font
        ByteBuffer buf = ByteBuffer.allocate (getLength ());

        // write the font header
        buf.putInt (getType ());
        buf.putShort (getNumTables ());
        buf.putShort (getSearchRange ());
        buf.putShort (getEntrySelector ());
        buf.putShort (getRangeShift ());

        // first offset is the end of the table directory entries
        int curOffset = 12 + (getNumTables () * 16);

        // write the tables
        for (Iterator i = tables.keySet ().iterator (); i.hasNext ();) {
            String tagString = (String) i.next ();
            int tag = TrueTypeTable.stringToTag (tagString);

            ByteBuffer data = null;

            Object tableObj = tables.get (tagString);
            if (tableObj instanceof TrueTypeTable) {
                data = ((TrueTypeTable) tableObj).getData ();
            } else {
                data = (ByteBuffer) tableObj;
            }

            int dataLen = data.remaining ();

            // write the table directory entry
            buf.putInt (tag);
            buf.putInt (calculateChecksum (tagString, data));
            buf.putInt (curOffset);
            buf.putInt (dataLen);

            // save the current position
            buf.mark ();

            // move to the current offset and write the data
            buf.position (curOffset);
            buf.put (data);

            // reset the data start pointer
            data.flip ();

            // return to the table directory entry
            buf.reset ();

            // udate the offset
            curOffset += dataLen;

            // don't forget the padding
            while ((curOffset % 4) > 0) {
                curOffset++;
            }
        }

        buf.position (curOffset);
        buf.flip ();

        // adjust the checksum
        updateChecksumAdj (buf);

        return buf.array ();
    }