// Copyright 2001-2009 freehep
package org.xmind.org.freehep.graphicsio.font;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphMetrics;
import java.awt.geom.Rectangle2D;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import org.xmind.org.freehep.graphics2d.font.CharTable;
import org.xmind.org.freehep.util.io.ASCIIHexOutputStream;
import org.xmind.org.freehep.util.io.CountedByteOutputStream;
import org.xmind.org.freehep.util.io.EEXECEncryption;
/**
* Font embedder for type 1 fonts. The output can be directly fed into a ps file
* or as a FontFile to a pdf file. <h3>Todo</h3>
* <ul>
* <li>use subroutines for accents
* <li>add more hints
* </ul>
*
* @author Simon Fischer
* @author Jason Wong
*/
@SuppressWarnings("nls")
public class FontEmbedderType1 extends FontEmbedder {
/** Defines whether or not eexec encryption is used. */
private static final boolean ENCRYPT = true;
/**
* Defines whether or not encrypted part should be hex encoded (otherwise it
* is binary).
*/
private static final boolean HEX_ENC = true;
/**
* Defines whether or not encrypted charstrings should be hex encoded
* Ghostview crashes when set to true. The freehep ps interpreter handles it
* correctly.
*/
private static boolean HEX_ENC_CHARSTRINGS = false;
private PrintStream fontFile, encrypted;
private CountedByteOutputStream byteCounter;
private int asciiEnd, encEnd; // remember the lengths of the three
// portions
private boolean addZeros;
public FontEmbedderType1(FontRenderContext context, OutputStream out,
boolean addZeros) {
super(context);
this.byteCounter = new CountedByteOutputStream(out);
this.fontFile = new PrintStream(byteCounter);
this.addZeros = addZeros;
asciiEnd = encEnd = -1;
}
protected void writeWidths(double[] w) throws IOException {
}
protected void writeEncoding(CharTable t) throws IOException {
fontFile.println("/Encoding 256 array");
fontFile.println("0 1 255 {1 index exch /.notdef put} for"); // set
// undefined
// to
// .notdef
// ??
for (int i = 0; i < 256; i++) {
String charName = t.toName(i);
if (charName != null)
fontFile.println("dup " + i + " /" + charName + " put");
}
fontFile.println("readonly def");
}
protected void openIncludeFont() throws IOException {
// begin clear text ascii portion
fontFile.println("%!FontType1-1.0: " + getFont().getName()); // unknown
// version
// number
// fontFile.println("%%CreationDate: " +
// DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).
// format(new Date()));
fontFile.println("% Generated by: " + getClass().getName());
fontFile.println("11 dict begin");
fontFile.println("/FontInfo 8 dict dup begin");
fontFile.println("/FullName (" + getFont().getPSName()
+ ") readonly def");
fontFile.println("/FamilyName (" + getFont().getFamily()
+ ") readonly def");
fontFile.println("end readonly def");
fontFile.println("/FontName /" + getFontName() + " def");
fontFile.println("/PaintType 0 def");
fontFile.println("/FontType 1 def");
fontFile.println("/FontMatrix [" + 1 / FONT_SIZE + " 0.0 0.0 " + 1
/ FONT_SIZE + " 0.0 0.0] readonly def");
}
protected void closeIncludeFont() {
Rectangle2D boundingBox = getFontBBox();
int llx = (int) Math.round(boundingBox.getX());
int lly = (int) Math.round(boundingBox.getY());
int urx = (int) Math.round(boundingBox.getX() + boundingBox.getWidth());
int ury = (int) Math
.round(boundingBox.getY() + boundingBox.getHeight());
fontFile.println("/FontBBox {" + llx + " " + lly + " " + urx + " "
+ ury + "} readonly def");
fontFile.println("currentdict end");
// begin encrypted portion
if (ENCRYPT) {
fontFile.print("currentfile eexec ");
asciiEnd = byteCounter.getCount();
}
fontFile.flush();
}
protected void openGlyphs() throws IOException {
// begin encryption
if (ENCRYPT) {
if (HEX_ENC) {
encrypted = new PrintStream(new EEXECEncryption(
new ASCIIHexOutputStream(fontFile),
EEXECEncryption.EEXEC_R));
} else {
encrypted = new PrintStream(new EEXECEncryption(fontFile,
EEXECEncryption.EEXEC_R));
}
} else {
encrypted = fontFile;
}
// begin the Private dictionary
encrypted.println("dup /Private 8 dict dup begin");
encrypted
.println("/RD {string currentfile exch readstring pop} executeonly def");
encrypted.println("/ND {noaccess def} executeonly def");
encrypted.println("/NP {noaccess put} executeonly def");
encrypted.println("/BlueValues [] def"); // ???
encrypted.println("/MinFeature {16 16} def");
encrypted.println("/password 5839 def");
encrypted.print("2 index ");
encrypted.println("/CharStrings " + (getNODefinedChars() + 1)
+ " dict dup begin");
}
protected void closeGlyphs() throws IOException {
encrypted.println("end"); // end Private
encrypted.println("end"); // end CharStrings
}
protected void closeEmbedFont() throws IOException {
encrypted.println("readonly put");
encrypted.println("noaccess put");
encrypted.println("dup /FontName get exch definefont pop");
encrypted.print("mark");
if (ENCRYPT) {
encrypted.print(" currentfile closefile ");
}
encrypted.flush();
encEnd = byteCounter.getCount();
if (!ENCRYPT)
asciiEnd = encEnd;
if (addZeros) {
fontFile.println();
for (int i = 0; i < 16; i++)
fontFile.println("00000000000000000000000000000000");
fontFile.println("cleartomark");
}
}
protected void writeGlyph(String characterName, Shape glyph,
GlyphMetrics glyphMetrics) throws IOException {
// FIXME: find out why Acrobat Reader displays some characters displaced
// when
// using the correct sidebearing. A value of 0 looks good
double sidebearing = glyphMetrics != null ? glyphMetrics.getLSB() : 0;
// double sidebearing = 0;
// write the binary charstring to a buffer
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
CharstringEncoder charString = (HEX_ENC_CHARSTRINGS ? new CharstringEncoder(
new EEXECEncryption(new ASCIIHexOutputStream(bytes),
EEXECEncryption.CHARSTRING_R)) : new CharstringEncoder(
new EEXECEncryption(bytes, EEXECEncryption.CHARSTRING_R)));
charString.startChar(sidebearing,
(glyphMetrics != null ? glyphMetrics.getAdvance()
: getUndefinedWidth())); // bounds.getWidth());
charString.drawPath(glyph);
charString.endchar();
// write the buffer to the encrypted fontFile
byte[] binaryString = bytes.toByteArray();
encrypted.print("/" + characterName + " " + binaryString.length
+ " RD ");
for (int i = 0; i < binaryString.length; i++) {
encrypted.write(binaryString[i] & 0x00ff);
}
encrypted.println("ND");
encrypted.flush();
}
/** Returns the length of the ascii portion of the output. */
public int getAsciiLength() {
return asciiEnd;
}
/** Returns the length of the encrypted portion of the output. */
public int getEncryptedLength() {
return encEnd - asciiEnd;
}
}