--- /dev/null
+/*
+ * $Id$
+ * Copyright (C) 2001-2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts;
+
+/**
+ * This is just a holder class for bfentries.
+ */
+public class BFEntry {
+
+ private int unicodeStart;
+ private int unicodeEnd;
+ private int glyphStartIndex;
+
+ /**
+ * Main constructor.
+ * @param unicodeStart Unicode start index
+ * @param unicodeEnd Unicode end index
+ * @param glyphStartIndex glyph start index
+ */
+ public BFEntry(int unicodeStart, int unicodeEnd, int glyphStartIndex) {
+ this.unicodeStart = unicodeStart;
+ this.unicodeEnd = unicodeEnd;
+ this.glyphStartIndex = glyphStartIndex;
+ }
+
+ /**
+ * Returns the unicodeStart.
+ * @return the Unicode start index
+ */
+ public int getUnicodeStart() {
+ return unicodeStart;
+ }
+
+ /**
+ * Returns the unicodeEnd.
+ * @return the Unicode end index
+ */
+ public int getUnicodeEnd() {
+ return unicodeEnd;
+ }
+
+ /**
+ * Returns the glyphStartIndex.
+ * @return the glyph start index
+ */
+ public int getGlyphStartIndex() {
+ return glyphStartIndex;
+ }
+
+}
--- /dev/null
+/*
+ * $Id$
+ * Copyright (C) 2001-2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts;
+
+/**
+ * Abstract base class for CID fonts.
+ */
+public abstract class CIDFont extends CustomFont {
+
+ // ---- Required ----
+ /**
+ * Returns the name of the base font.
+ * @return the name of the base font
+ */
+ public abstract String getCidBaseFont();
+
+ /**
+ * Returns the type of the CID font.
+ * @return the type of the CID font
+ */
+ public abstract CIDFontType getCIDType();
+
+ /**
+ * Returns the name of the issuer of the font.
+ * @return a String identifying an issuer of character collections \97
+ * for example, Adobe
+ */
+ public abstract String getRegistry();
+
+ /**
+ * Returns a font name for use within a registry.
+ * @return a String that uniquely names a character collection issued by
+ * a specific registry \97 for example, Japan1.
+ */
+ public abstract String getOrdering();
+
+ /**
+ * Returns the supplement number of the character collection.
+ * @return the supplement number
+ */
+ public abstract int getSupplement();
+
+
+ // ---- Optional ----
+ /**
+ * Returns the default width for this font.
+ * @return the default width
+ */
+ public int getDefaultWidth() {
+ return 0;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.Font#isMultiByte()
+ */
+ public boolean isMultiByte() {
+ return true;
+ }
+}
--- /dev/null
+/*
+ * $Id$
+ * Copyright (C) 2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts;
+
+import org.apache.avalon.framework.ValuedEnum;
+
+/**
+ * This class enumerates all supported CID font types.
+ */
+public class CIDFontType extends ValuedEnum {
+
+ /**
+ * CID Font Type 0
+ */
+ public static final CIDFontType CIDTYPE0 = new CIDFontType("CIDFontType0", 0);
+
+ /**
+ * CID Font Type 2
+ */
+ public static final CIDFontType CIDTYPE2 = new CIDFontType("CIDFontType2", 1);
+
+
+ /**
+ * @see org.apache.avalon.framework.Enum#Enum(String)
+ */
+ protected CIDFontType(String name, int value) {
+ super(name, value);
+ }
+
+
+ /**
+ * Returns the CIDFontType by name.
+ * @param name Name of the CID font type to look up
+ * @return FontType the CID font type
+ */
+ public static CIDFontType byName(String name) {
+ if (name.equalsIgnoreCase(CIDFontType.CIDTYPE0.getName())) {
+ return CIDFontType.CIDTYPE0;
+ } else if (name.equalsIgnoreCase(CIDFontType.CIDTYPE2.getName())) {
+ return CIDFontType.CIDTYPE2;
+ } else {
+ throw new IllegalArgumentException("Invalid CID font type: " + name);
+ }
+ }
+
+
+ /**
+ * Returns the CID FontType by value.
+ * @param value Value of the CID font type to look up
+ * @return FontType the CID font type
+ */
+ public static CIDFontType byValue(int value) {
+ if (value == CIDFontType.CIDTYPE0.getValue()) {
+ return CIDFontType.CIDTYPE0;
+ } else if (value == CIDFontType.CIDTYPE2.getValue()) {
+ return CIDFontType.CIDTYPE2;
+ } else {
+ throw new IllegalArgumentException("Invalid CID font type: " + value);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * $Id$
+ * Copyright (C) 2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts;
+
+import java.util.Map;
+
+
+/**
+ * Abstract base class for custom fonts loaded from files, for example.
+ */
+public abstract class CustomFont extends Font
+ implements FontDescriptor, MutableFont {
+
+ private String fontName = null;
+ private String embedFileName = null;
+ private String embedResourceName = null;
+
+ private int capHeight = 0;
+ private int xHeight = 0;
+ private int ascender = 0;
+ private int descender = 0;
+ private int[] fontBBox = {0, 0, 0, 0};
+ private int flags = 4;
+ private int stemV = 0;
+ private int italicAngle = 0;
+ private int missingWidth = 0;
+ private FontType fontType = FontType.TYPE1;
+ private int firstChar = 0;
+ private int lastChar = 255;
+
+ private Map kerning = new java.util.HashMap();
+
+
+ private boolean useKerning = true;
+
+
+ /**
+ * @see org.apache.fop.fonts.FontMetrics#getFontName()
+ */
+ public String getFontName() {
+ return fontName;
+ }
+
+ /**
+ * Returns an URI representing an embeddable font file. The URI will often
+ * be a filename or an URL.
+ * @return URI to an embeddable font file or null if not available.
+ */
+ public String getEmbedFileName() {
+ return embedFileName;
+ }
+
+ /**
+ * Returns the lookup name to an embeddable font file available as a
+ * resource.
+ * @todo Remove this method, this should be done using a resource: URI.
+ * @return the lookup name
+ */
+ public String getEmbedResourceName() {
+ return embedResourceName;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontDescriptor#getAscender()
+ */
+ public int getAscender() {
+ return ascender;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontDescriptor#getDescender()
+ */
+ public int getDescender() {
+ return descender;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontDescriptor#getCapHeight()
+ */
+ public int getCapHeight() {
+ return capHeight;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontMetrics#getAscender(int)
+ */
+ public int getAscender(int size) {
+ return size * ascender;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontMetrics#getDescender(int)
+ */
+ public int getDescender(int size) {
+ return size * descender;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontMetrics#getCapHeight(int)
+ */
+ public int getCapHeight(int size) {
+ return size * capHeight;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontMetrics#getXHeight(int)
+ */
+ public int getXHeight(int size) {
+ return size * xHeight;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontDescriptor#getFontBBox()
+ */
+ public int[] getFontBBox() {
+ return fontBBox;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontDescriptor#getFlags()
+ */
+ public int getFlags() {
+ return flags;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontDescriptor#getStemV()
+ */
+ public int getStemV() {
+ return stemV;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontDescriptor#getItalicAngle()
+ */
+ public int getItalicAngle() {
+ return italicAngle;
+ }
+
+ /**
+ * Returns the width to be used when no width is available.
+ * @return a character width
+ */
+ public int getMissingWidth() {
+ return missingWidth;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontDescriptor#getFontType()
+ */
+ public FontType getFontType() {
+ return fontType;
+ }
+
+ /**
+ * Returns the index of the first character defined in this font.
+ * @return the index of the first character
+ */
+ public int getFirstChar() {
+ return 0;
+ // return firstChar;
+ /**@todo Why is this hardcoded??? This code was in SingleByteFont.java */
+ }
+
+ /**
+ * Returns the index of the last character defined in this font.
+ * @return the index of the last character
+ */
+ public int getLastChar() {
+ return lastChar;
+ }
+
+ /**
+ * Used to determine if kerning is enabled.
+ * @return True if kerning is enabled.
+ */
+ public boolean isKerningEnabled() {
+ return useKerning;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontMetrics#hasKerningInfo()
+ */
+ public final boolean hasKerningInfo() {
+ return (isKerningEnabled() & kerning.isEmpty());
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontMetrics#getKerningInfo()
+ */
+ public final Map getKerningInfo() {
+ if (isKerningEnabled()) {
+ return kerning;
+ } else {
+ return java.util.Collections.EMPTY_MAP;
+ }
+ }
+
+
+ /* ---- MutableFont interface ---- */
+
+ /**
+ * @see org.apache.fop.fonts.MutableFont#setFontName(String)
+ */
+ public void setFontName(String name) {
+ this.fontName = name;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.MutableFont#setEmbedFileName(String)
+ */
+ public void setEmbedFileName(String path) {
+ this.embedFileName = path;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.MutableFont#setEmbedResourceName(String)
+ */
+ public void setEmbedResourceName(String name) {
+ this.embedResourceName = name;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.MutableFont#setCapHeight(int)
+ */
+ public void setCapHeight(int capHeight) {
+ this.capHeight = capHeight;
+ }
+
+ /**
+ * Returns the XHeight value of the font.
+ * @param xHeight the XHeight value
+ */
+ public void setXHeight(int xHeight) {
+ this.xHeight = xHeight;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.MutableFont#setAscender(int)
+ */
+ public void setAscender(int ascender) {
+ this.ascender = ascender;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.MutableFont#setDescender(int)
+ */
+ public void setDescender(int descender) {
+ this.descender = descender;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.MutableFont#setFontBBox(int[])
+ */
+ public void setFontBBox(int[] bbox) {
+ this.fontBBox = bbox;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.MutableFont#setFlags(int)
+ */
+ public void setFlags(int flags) {
+ this.flags = flags;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.MutableFont#setStemV(int)
+ */
+ public void setStemV(int stemV) {
+ this.stemV = stemV;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.MutableFont#setItalicAngle(int)
+ */
+ public void setItalicAngle(int italicAngle) {
+ this.italicAngle = italicAngle;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.MutableFont#setMissingWidth(int)
+ */
+ public void setMissingWidth(int width) {
+ this.missingWidth = width;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.MutableFont#setFontType(FontType)
+ */
+ public void setFontType(FontType fontType) {
+ this.fontType = fontType;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.MutableFont#setFirstChar(int)
+ */
+ public void setFirstChar(int index) {
+ this.firstChar = index;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.MutableFont#setLastChar(int)
+ */
+ public void setLastChar(int index) {
+ this.lastChar = index;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.MutableFont#setKerningEnabled(boolean)
+ */
+ public void setKerningEnabled(boolean enabled) {
+ this.useKerning = enabled;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.MutableFont#putKerningEntry(Integer, Map)
+ */
+ public void putKerningEntry(Integer key, Map value) {
+ this.kerning.put(key, value);
+ }
+
+}
--- /dev/null
+/*
+ * $Id$
+ * Copyright (C) 2001-2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts;
+
+// FOP
+
+
+/**
+ * Base class for PDF font classes
+ */
+public abstract class Font implements FontMetrics {
+
+ /**
+ * Get the encoding of the font.
+ * @return the encoding
+ */
+ public abstract String getEncoding();
+
+ /**
+ * Map a Unicode character to a code point in the font.
+ * @param c character to map
+ * @return the mapped character
+ */
+ public abstract char mapChar(char c);
+
+ /**
+ * Determines whether the font is a multibyte font.
+ * @return True if it is multibyte
+ */
+ public boolean isMultiByte() {
+ return false;
+ }
+
+}
+
--- /dev/null
+/*
+ * $Id$
+ * Copyright (C) 2001-2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts;
+
+/**
+ * This interface enhances the font metrics interface with access methods to
+ * value needed to register fonts in various target formats like PDF or
+ * PostScript.
+ */
+public interface FontDescriptor extends FontMetrics {
+
+ /**
+ * Returns the ascender value of the font. (Ascent in pdf spec)
+ * @return the ascender
+ */
+ int getAscender();
+
+
+ /**
+ * Returns the capital height of the font.
+ * @return the capiptal height
+ */
+ int getCapHeight();
+
+
+ /**
+ * Returns the descender value of the font. (Descent in pdf spec)
+ * @return the descender value
+ */
+ int getDescender();
+
+
+ /**
+ * Returns the flags for the font. (See pdf spec)
+ * @return the flags
+ */
+ int getFlags();
+
+
+ /**
+ * Returns the font's bounding box.
+ * @return the bounding box
+ */
+ int[] getFontBBox();
+
+
+ /**
+ * Returns the italic angle for the font.
+ * @return the italic angle
+ */
+ int getItalicAngle();
+
+
+ /**
+ * Returns the vertical stem width for the font.
+ * @return the vertical stem width
+ */
+ int getStemV();
+
+
+ /**
+ * Indicates if this font may be embedded.
+ * @return True, if embedding is possible/permitted
+ */
+ boolean isEmbeddable();
+
+
+}
+++ /dev/null
-/*
- * $Id$
- * Copyright (C) 2001-2002 The Apache Software Foundation. All rights reserved.
- * For details on use and redistribution please refer to the
- * LICENSE file included with these sources.
- */
-
-package org.apache.fop.fonts;
-
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.File;
-import java.io.IOException;
-
-/**
- * Reads a file into an array and
- * provides file like functions for array access.
- */
-public class FontFileReader {
-
- private int fsize; // file size
- private int current; // current position in file
- private byte[] file;
-
- /**
- * Initializes class and reads stream. Init does not close stream.
- *
- * @param in InputStream to read from new array with size + inc
- * @throws IOException In case of an I/O problem
- */
- private void init(InputStream in) throws java.io.IOException {
- java.io.ByteArrayOutputStream bout = new java.io.ByteArrayOutputStream();
- try {
- copyStream(in, bout);
- this.file = bout.toByteArray();
- this.fsize = this.file.length;
- this.current = 0;
- } finally {
- bout.close();
- }
- }
-
- /**@todo Use method from Avalon Excalibur IO or Jakarta Commons IO*/
- private void copyStream(InputStream in, OutputStream out) throws IOException {
- final int bufferSize = 2048;
- final byte[] buf = new byte[bufferSize];
- int bytesRead;
- while ((bytesRead = in.read(buf)) != -1) {
- out.write(buf, 0, bytesRead);
- }
- }
-
- /**
- * Constructor
- *
- * @param fileName filename to read
- * @throws IOException In case of an I/O problem
- */
- public FontFileReader(String fileName) throws IOException {
- final File f = new File(fileName);
- InputStream in = new java.io.FileInputStream(f);
- try {
- init(in);
- } finally {
- in.close();
- }
- }
-
-
- /**
- * Constructor
- *
- * @param in InputStream to read from
- * @throws IOException In case of an I/O problem
- */
- public FontFileReader(InputStream in) throws IOException {
- init(in);
- }
-
-
- /**
- * Set current file position to offset
- *
- * @param offset The new offset to set
- * @throws IOException In case of an I/O problem
- */
- public void seekSet(long offset) throws IOException {
- if (offset > fsize || offset < 0) {
- throw new java.io.EOFException("Reached EOF, file size=" + fsize
- + " offset=" + offset);
- }
- current = (int)offset;
- }
-
- /**
- * Set current file position to offset
- *
- * @param add The number of bytes to advance
- * @throws IOException In case of an I/O problem
- */
- public void seekAdd(long add) throws IOException {
- seekSet(current + add);
- }
-
- /**
- * Skip a given number of bytes.
- *
- * @param add The number of bytes to advance
- * @throws IOException In case of an I/O problem
- */
- public void skip(long add) throws IOException {
- seekAdd(add);
- }
-
- /**
- * Returns current file position.
- *
- * @return int The current position.
- */
- public int getCurrentPos() {
- return current;
- }
-
- /**
- * Returns the size of the file.
- *
- * @return int The filesize
- */
- public int getFileSize() {
- return fsize;
- }
-
- /**
- * Read 1 byte.
- *
- * @return One byte
- * @throws IOException If EOF is reached
- */
- public byte read() throws IOException {
- if (current > fsize) {
- throw new java.io.EOFException("Reached EOF, file size=" + fsize);
- }
-
- final byte ret = file[current++];
- return ret;
- }
-
- /**
- * Read 1 signed byte.
- *
- * @return One byte
- * @throws IOException If EOF is reached
- */
- public final byte readTTFByte() throws IOException {
- return read();
- }
-
- /**
- * Read 1 unsigned byte.
- *
- * @return One unsigned byte
- * @throws IOException If EOF is reached
- */
- public final int readTTFUByte() throws IOException {
- final byte buf = read();
-
- if (buf < 0) {
- return (int)(256 + buf);
- } else {
- return (int)buf;
- }
- }
-
- /**
- * Read 2 bytes signed.
- *
- * @return One signed short
- * @throws IOException If EOF is reached
- */
- public final short readTTFShort() throws IOException {
- final int ret = (readTTFUByte() << 8) + readTTFUByte();
- final short sret = (short)ret;
- return sret;
- }
-
- /**
- * Read 2 bytes unsigned.
- *
- * @return One unsigned short
- * @throws IOException If EOF is reached
- */
- public final int readTTFUShort() throws IOException {
- final int ret = (readTTFUByte() << 8) + readTTFUByte();
- return (int)ret;
- }
-
- /**
- * Write a USHort at a given position.
- *
- * @param pos The absolute position to write to
- * @param val The value to write
- * @throws IOException If EOF is reached
- */
- public final void writeTTFUShort(int pos, int val) throws IOException {
- if ((pos + 2) > fsize) {
- throw new java.io.EOFException("Reached EOF");
- }
- final byte b1 = (byte)((val >> 8) & 0xff);
- final byte b2 = (byte)(val & 0xff);
- file[pos] = b1;
- file[pos + 1] = b2;
- }
-
- /**
- * Read 2 bytes signed at position pos without changing current position.
- *
- * @param pos The absolute position to read from
- * @return One signed short
- * @throws IOException If EOF is reached
- */
- public final short readTTFShort(long pos) throws IOException {
- final long cp = getCurrentPos();
- seekSet(pos);
- final short ret = readTTFShort();
- seekSet(cp);
- return ret;
- }
-
- /**
- * Read 2 bytes unsigned at position pos without changing current position.
- *
- * @param pos The absolute position to read from
- * @return One unsigned short
- * @throws IOException If EOF is reached
- */
- public final int readTTFUShort(long pos) throws IOException {
- long cp = getCurrentPos();
- seekSet(pos);
- int ret = readTTFUShort();
- seekSet(cp);
- return ret;
- }
-
- /**
- * Read 4 bytes.
- *
- * @return One signed integer
- * @throws IOException If EOF is reached
- */
- public final int readTTFLong() throws IOException {
- long ret = readTTFUByte(); // << 8;
- ret = (ret << 8) + readTTFUByte();
- ret = (ret << 8) + readTTFUByte();
- ret = (ret << 8) + readTTFUByte();
-
- return (int)ret;
- }
-
- /**
- * Read 4 bytes.
- *
- * @return One unsigned integer
- * @throws IOException If EOF is reached
- */
- public final long readTTFULong() throws IOException {
- long ret = readTTFUByte();
- ret = (ret << 8) + readTTFUByte();
- ret = (ret << 8) + readTTFUByte();
- ret = (ret << 8) + readTTFUByte();
-
- return ret;
- }
-
- /**
- * Read a NUL terminated ISO-8859-1 string.
- *
- * @return A String
- * @throws IOException If EOF is reached
- */
- public final String readTTFString() throws IOException {
- int i = current;
- while (file[i++] != 0) {
- if (i > fsize) {
- throw new java.io.EOFException("Reached EOF, file size="
- + fsize);
- }
- }
-
- byte[] tmp = new byte[i - current];
- System.arraycopy(file, current, tmp, 0, i - current);
- return new String(tmp, "ISO-8859-1");
- }
-
-
- /**
- * Read an ISO-8859-1 string of len bytes.
- *
- * @param len The length of the string to read
- * @return A String
- * @throws IOException If EOF is reached
- */
- public final String readTTFString(int len) throws IOException {
- if ((len + current) > fsize) {
- throw new java.io.EOFException("Reached EOF, file size=" + fsize);
- }
-
- byte[] tmp = new byte[len];
- System.arraycopy(file, current, tmp, 0, len);
- current += len;
- return new String(tmp, "ISO-8859-1");
- }
-
- /**
- * Return a copy of the internal array
- *
- * @param offset The absolute offset to start reading from
- * @param length The number of bytes to read
- * @return An array of bytes
- * @throws IOException if out of bounds
- */
- public byte[] getBytes(int offset,
- int length) throws IOException {
- if ((offset + length) > fsize) {
- throw new java.io.IOException("Reached EOF");
- }
-
- byte[] ret = new byte[length];
- System.arraycopy(file, offset, ret, 0, length);
- return ret;
- }
-
-
-}
\ No newline at end of file
--- /dev/null
+/*
+ * $Id$
+ * Copyright (C) 2001-2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts;
+
+import java.util.Map;
+
+
+/**
+ * Main interface for access to font metrics.
+ */
+public interface FontMetrics {
+
+ /**
+ * Returns the font name.
+ * @return the font name
+ */
+ String getFontName();
+
+
+ /**
+ * Returns the type of the font.
+ * @return the font type
+ */
+ FontType getFontType();
+
+
+ /**
+ * Returns the ascent of the font described by this
+ * FontMetrics object.
+ * @param size font size
+ * @return ascent in milliponts
+ */
+ int getAscender(int size);
+
+ /**
+ * Returns the size of a capital letter measured from the font's baseline.
+ * @param size font size
+ * @return height of capital characters
+ */
+ int getCapHeight(int size);
+
+
+ /**
+ * Returns the descent of the font described by this
+ * FontMetrics object.
+ * @param size font size
+ * @return descent in milliponts
+ */
+ int getDescender(int size);
+
+
+ /**
+ * Determines the typical font height of this
+ * FontMetrics object
+ * @param size font size
+ * @return font height in millipoints
+ */
+ int getXHeight(int size);
+
+ /**
+ * Return the width (in 1/1000ths of point size) of the character at
+ * code point i.
+ * @param i code point index
+ * @param size font size
+ * @return the width of the character
+ */
+ int getWidth(int i, int size);
+
+ /**
+ * Return the array of widths.
+ * <p>
+ * This is used to get an array for inserting in an output format.
+ * It should not be used for lookup.
+ * @return an array of widths
+ */
+ int[] getWidths();
+
+ /**
+ * Indicates if the font has kering information.
+ * @return True, if kerning is available.
+ */
+ boolean hasKerningInfo();
+
+ /**
+ * Returns the kerning map for the font.
+ * @return the kerning map
+ */
+ Map getKerningInfo();
+
+}
--- /dev/null
+/*
+ * $Id$
+ * Copyright (C) 2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts;
+
+import org.apache.avalon.framework.ValuedEnum;
+
+/**
+ * This class enumerates all supported font types.
+ */
+public class FontType extends ValuedEnum {
+
+ /**
+ * Collective identifier for "other" font types
+ */
+ public static final FontType OTHER = new FontType("Other", 0);
+ /**
+ * Adobe Type 0 fonts
+ */
+ public static final FontType TYPE0 = new FontType("Type0", 1);
+ /**
+ * Adobe Type 1 fonts
+ */
+ public static final FontType TYPE1 = new FontType("Type1", 2);
+ /**
+ * Adobe Multiple Master Type 1 fonts
+ */
+ public static final FontType MMTYPE1 = new FontType("MMType1", 3);
+ /**
+ * Adobe Type 3 fonts ("user-defined" fonts)
+ */
+ public static final FontType TYPE3 = new FontType("Type3", 4);
+ /**
+ * TrueType fonts
+ */
+ public static final FontType TRUETYPE = new FontType("TrueType", 5);
+
+
+ /**
+ * @see org.apache.avalon.framework.Enum#Enum(String)
+ */
+ protected FontType(String name, int value) {
+ super(name, value);
+ }
+
+
+ /**
+ * Returns the FontType by name.
+ * @param name Name of the font type to look up
+ * @return the font type
+ */
+ public static FontType byName(String name) {
+ if (name.equalsIgnoreCase(FontType.OTHER.getName())) {
+ return FontType.OTHER;
+ } else if (name.equalsIgnoreCase(FontType.TYPE0.getName())) {
+ return FontType.TYPE0;
+ } else if (name.equalsIgnoreCase(FontType.TYPE1.getName())) {
+ return FontType.TYPE1;
+ } else if (name.equalsIgnoreCase(FontType.MMTYPE1.getName())) {
+ return FontType.MMTYPE1;
+ } else if (name.equalsIgnoreCase(FontType.TYPE3.getName())) {
+ return FontType.TYPE3;
+ } else if (name.equalsIgnoreCase(FontType.TRUETYPE.getName())) {
+ return FontType.TRUETYPE;
+ } else {
+ throw new IllegalArgumentException("Invalid font type: " + name);
+ }
+ }
+
+
+ /**
+ * Returns the FontType by value.
+ * @param value Value of the font type to look up
+ * @return the font type
+ */
+ public static FontType byValue(int value) {
+ if (value == FontType.OTHER.getValue()) {
+ return FontType.OTHER;
+ } else if (value == FontType.TYPE0.getValue()) {
+ return FontType.TYPE0;
+ } else if (value == FontType.TYPE1.getValue()) {
+ return FontType.TYPE1;
+ } else if (value == FontType.MMTYPE1.getValue()) {
+ return FontType.MMTYPE1;
+ } else if (value == FontType.TYPE3.getValue()) {
+ return FontType.TYPE3;
+ } else if (value == FontType.TRUETYPE.getValue()) {
+ return FontType.TRUETYPE;
+ } else {
+ throw new IllegalArgumentException("Invalid font type: " + value);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * $Id$
+ * Copyright (C) 2001-2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts;
+
+//Java
+import java.util.Map;
+
+//FOP
+import org.apache.fop.render.pdf.FontReader;
+
+/**
+ * This class is used to defer the loading of a font until it is really used.
+ */
+public class LazyFont extends Font implements FontDescriptor {
+
+ private String metricsFileName = null;
+ private String fontEmbedPath = null;
+ private boolean useKerning = false;
+
+ private boolean isMetricsLoaded = false;
+ private Font realFont = null;
+ private FontDescriptor realFontDescriptor = null;
+
+ /**
+ * Main constructor
+ * @param fontEmbedPath path to embeddable file (may be null)
+ * @param metricsFileName path to the metrics XML file
+ * @param useKerning True, if kerning should be enabled
+ */
+ public LazyFont(String fontEmbedPath, String metricsFileName, boolean useKerning) {
+ this.metricsFileName = metricsFileName;
+ this.fontEmbedPath = fontEmbedPath;
+ this.useKerning = useKerning;
+ }
+
+ private void load() {
+ if (!isMetricsLoaded) {
+ isMetricsLoaded = true;
+ try {
+ /**@todo Possible thread problem here */
+
+ FontReader reader = new FontReader(metricsFileName);
+ reader.setKerningEnabled(useKerning);
+ reader.setFontEmbedPath(fontEmbedPath);
+ realFont = reader.getFont();
+ if (realFont instanceof FontDescriptor) {
+ realFontDescriptor = (FontDescriptor) realFont;
+ }
+ // System.out.println("Metrics " + metricsFileName + " loaded.");
+ } catch (Exception ex) {
+ /**@todo Log this exception */
+ //log.error("Failed to read font metrics file "
+ // + metricsFileName
+ // + " : " + ex.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Gets the real font.
+ * @return the real font
+ */
+ public Font getRealFont() {
+ load();
+ return realFont;
+ }
+
+ // ---- Font ----
+ /**
+ * @see org.apache.fop.fonts.Font#getEncoding()
+ */
+ public String getEncoding() {
+ load();
+ return realFont.getEncoding();
+ }
+
+ /**
+ * @see org.apache.fonts.pdf.Font#mapChar(char)
+ */
+ public char mapChar(char c) {
+ load();
+ return realFont.mapChar(c);
+ }
+
+ /**
+ * @see org.apache.fop.fonts.Font#isMultiByte()
+ */
+ public boolean isMultiByte() {
+ return realFont.isMultiByte();
+ }
+
+ // ---- FontMetrics interface ----
+ /**
+ * @see org.apache.fop.fonts.FontMetrics#getFontName()
+ */
+ public String getFontName() {
+ load();
+ return realFont.getFontName();
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontMetrics#getAscender(int)
+ */
+ public int getAscender(int size) {
+ load();
+ return realFont.getAscender(size);
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontMetrics#getCapHeight(int)
+ */
+ public int getCapHeight(int size) {
+ load();
+ return realFont.getCapHeight(size);
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontMetrics#getDescender(int)
+ */
+ public int getDescender(int size) {
+ load();
+ return realFont.getDescender(size);
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontMetrics#getXHeight(int)
+ */
+ public int getXHeight(int size) {
+ load();
+ return realFont.getXHeight(size);
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontMetrics#getWidth(int, int)
+ */
+ public int getWidth(int i, int size) {
+ load();
+ return realFont.getWidth(i, size);
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontMetrics#getWidths()
+ */
+ public int[] getWidths() {
+ load();
+ return realFont.getWidths();
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontMetrics#hasKerningInfo()
+ */
+ public boolean hasKerningInfo() {
+ load();
+ return realFont.hasKerningInfo();
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontMetrics#getKerningInfo()
+ */
+ public Map getKerningInfo() {
+ load();
+ return realFont.getKerningInfo();
+ }
+
+ // ---- FontDescriptor interface ----
+ /**
+ * @see org.apache.fop.fonts.FontDescriptor#getCapHeight()
+ */
+ public int getCapHeight() {
+ load();
+ return realFontDescriptor.getCapHeight();
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontDescriptor#getDescender()
+ */
+ public int getDescender() {
+ load();
+ return realFontDescriptor.getDescender();
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontDescriptor#getAscender()
+ */
+ public int getAscender() {
+ load();
+ return realFontDescriptor.getAscender();
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontDescriptor#getFlags()
+ */
+ public int getFlags() {
+ load();
+ return realFontDescriptor.getFlags();
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontDescriptor#getFontBBox()
+ */
+ public int[] getFontBBox() {
+ load();
+ return realFontDescriptor.getFontBBox();
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontDescriptor#getItalicAngle()
+ */
+ public int getItalicAngle() {
+ load();
+ return realFontDescriptor.getItalicAngle();
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontDescriptor#getStemV()
+ */
+ public int getStemV() {
+ load();
+ return realFontDescriptor.getStemV();
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontDescriptor#getFontType()
+ */
+ public FontType getFontType() {
+ load();
+ return realFontDescriptor.getFontType();
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontDescriptor#isEmbeddable()
+ */
+ public boolean isEmbeddable() {
+ load();
+ return realFontDescriptor.isEmbeddable();
+ }
+
+}
+
--- /dev/null
+/*
+ * $Id$
+ * Copyright (C) 2001-2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts;
+
+//Java
+import java.util.Map;
+
+//FOP
+import org.apache.fop.pdf.PDFWArray;
+
+/**
+ * Generic MultiByte (CID) font
+ */
+public class MultiByteFont extends CIDFont {
+
+ private static int uniqueCounter = 1;
+
+
+ private String ttcName = null;
+ private String encoding = "Identity-H";
+
+ private String embedResourceName = null;
+
+ private int defaultWidth = 0;
+ private CIDFontType cidType = CIDFontType.CIDTYPE2;
+
+ private String namePrefix = null; // Quasi unique prefix
+ private PDFWArray warray = new PDFWArray();
+ private int width[] = null;
+
+ private BFEntry[] bfentries = null;
+
+ /**
+ * usedGlyphs contains orginal, new glyph index
+ */
+ private Map usedGlyphs = new java.util.HashMap();
+
+ /**
+ * usedGlyphsIndex contains new glyph, original index
+ */
+ private Map usedGlyphsIndex = new java.util.HashMap();
+ private int usedGlyphsCount = 0;
+
+
+ /**
+ * Default constructor
+ */
+ public MultiByteFont() {
+ // Make sure that the 3 first glyphs are included
+ usedGlyphs.put(new Integer(0), new Integer(0));
+ usedGlyphsIndex.put(new Integer(0), new Integer(0));
+ usedGlyphsCount++;
+ usedGlyphs.put(new Integer(1), new Integer(1));
+ usedGlyphsIndex.put(new Integer(1), new Integer(1));
+ usedGlyphsCount++;
+ usedGlyphs.put(new Integer(2), new Integer(2));
+ usedGlyphsIndex.put(new Integer(2), new Integer(2));
+ usedGlyphsCount++;
+
+ // Create a quasiunique prefix for fontname
+ int cnt = 0;
+ synchronized (this.getClass()) {
+ cnt = uniqueCounter++;
+ }
+ int ctm = (int)(System.currentTimeMillis() & 0xffff);
+ namePrefix = new String(cnt + "E" + Integer.toHexString(ctm));
+
+ setFontType(FontType.TYPE0);
+ }
+
+ /**
+ * @see org.apache.fop.fonts.CIDFont#getDefaultWidth()
+ */
+ public int getDefaultWidth() {
+ return defaultWidth;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.CIDFont#getRegistry()
+ */
+ public String getRegistry() {
+ return "Adobe";
+ }
+
+ /**
+ * @see org.apache.fop.fonts.CIDFont#getOrdering()
+ */
+ public String getOrdering() {
+ return "UCS";
+ }
+
+ /**
+ * @see org.apache.fop.fonts.CIDFont#getSupplement()
+ */
+ public int getSupplement() {
+ return 0;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.CIDFont#getCIDType()
+ */
+ public CIDFontType getCIDType() {
+ return cidType;
+ }
+
+ /**
+ * Sets the CIDType.
+ * @param cidType The cidType to set
+ */
+ public void setCIDType(CIDFontType cidType) {
+ this.cidType = cidType;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.CIDFont#getCidBaseFont()
+ */
+ public String getCidBaseFont() {
+ if (isEmbeddable()) {
+ return namePrefix + super.getFontName();
+ } else {
+ return super.getFontName();
+ }
+ }
+
+/* unused
+ public PDFWArray getWidthsAsPDFWArray() {
+ if (isEmbeddable()) {
+ // Create widths for reencoded chars
+ warray = new PDFWArray();
+ int[] tmpWidth = new int[usedGlyphsCount];
+
+ for (int i = 0; i < usedGlyphsCount; i++) {
+ Integer nw = (Integer)usedGlyphsIndex.get(new Integer(i));
+ int nwx = (nw == null) ? 0 : nw.intValue();
+ tmpWidth[i] = width[nwx];
+ }
+ warray.addEntry(0, tmpWidth);
+ }
+ return warray;
+ }*/
+
+ /**
+ * @see org.apache.fop.fonts.FontDescriptor#isEmbeddable()
+ */
+ public boolean isEmbeddable() {
+ if (getEmbedFileName() == null
+ && embedResourceName == null) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * @see org.apache.fop.fonts.Font#getEncoding()
+ */
+ public String getEncoding() {
+ return encoding;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontMetrics#getFontName()
+ */
+ public String getFontName() {
+ if (isEmbeddable()) {
+ return namePrefix + super.getFontName();
+ } else {
+ return super.getFontName();
+ }
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontMetrics#getWidth(int, int)
+ */
+ public int getWidth(int i, int size) {
+ if (isEmbeddable()) {
+ Integer idx = (Integer)usedGlyphsIndex.get(new Integer(i));
+ return size * width[idx.intValue()];
+ } else {
+ return size * width[i];
+ }
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontMetrics#getWidths()
+ */
+ public int[] getWidths() {
+ int[] arr = new int[width.length];
+ System.arraycopy(width, 0, arr, 0, width.length - 1);
+ /*
+ for (int i = 0; i < arr.length; i++)
+ arr[i] *= size;
+ */
+ return arr;
+ }
+
+ /**
+ * Remaps a codepoint based.
+ * @param i codepoint to remap
+ * @return new codepoint
+ */
+/* unused
+ public Integer reMap(Integer i) {
+ if (isEmbeddable()) {
+ Integer ret = (Integer)usedGlyphsIndex.get(i);
+ if (ret == null) {
+ ret = i;
+ }
+ return ret;
+ } else {
+ return i;
+ }
+
+ }
+*/
+
+ /**
+ * @see org.apache.fop.fonts.Font#mapChar(char)
+ */
+ public char mapChar(char c) {
+ int idx = (int)c;
+ int retIdx = 0;
+
+ for (int i = 0; (i < bfentries.length) && retIdx == 0; i++) {
+ if (bfentries[i].getUnicodeStart() <= idx
+ && bfentries[i].getUnicodeEnd() >= idx) {
+ retIdx = bfentries[i].getGlyphStartIndex() + idx
+ - bfentries[i].getUnicodeStart();
+ }
+ }
+
+ if (isEmbeddable()) {
+ // Reencode to a new subset font or get
+ // the reencoded value
+ Integer newIdx = (Integer)usedGlyphs.get(new Integer(retIdx));
+ if (newIdx == null) {
+ usedGlyphs.put(new Integer(retIdx),
+ new Integer(usedGlyphsCount));
+ usedGlyphsIndex.put(new Integer(usedGlyphsCount),
+ new Integer(retIdx));
+ retIdx = usedGlyphsCount;
+ // System.out.println(c+"("+(int)c+") = "+retIdx);
+ usedGlyphsCount++;
+ } else {
+ retIdx = newIdx.intValue();
+ }
+ }
+
+ return (char)retIdx;
+ }
+
+ /**
+ * Sets the bfentries.
+ * @param bfentries The bfentries to set
+ */
+ public void setBFEntries(BFEntry[] bfentries) {
+ this.bfentries = bfentries;
+ }
+
+ /**
+ * Sets the defaultWidth.
+ * @param defaultWidth The defaultWidth to set
+ */
+ public void setDefaultWidth(int defaultWidth) {
+ this.defaultWidth = defaultWidth;
+ }
+
+ /**
+ * Returns the TrueType Collection Name.
+ * @return the TrueType Collection Name
+ */
+ public String getTTCName() {
+ return ttcName;
+ }
+
+ /**
+ * Sets the the TrueType Collection Name.
+ * @param ttcName the TrueType Collection Name
+ */
+ public void setTTCName(String ttcName) {
+ this.ttcName = ttcName;
+ }
+
+ /**
+ * Adds a new CID width entry to the font.
+ * @param cidWidthIndex index
+ * @param wds array of widths
+ */
+ public void addCIDWidthEntry(int cidWidthIndex, int[] wds) {
+ this.warray.addEntry(cidWidthIndex, wds);
+ }
+
+
+ /**
+ * Sets the width array.
+ * @param wds array of widths.
+ */
+ public void setWidthArray(int[] wds) {
+ this.width = wds;
+ }
+
+ /**
+ * Returns a Map of used Glyphs.
+ * @return Map Map of used Glyphs
+ */
+ public Map getUsedGlyphs() {
+ return usedGlyphs;
+ }
+
+}
+
--- /dev/null
+/*
+ * $Id$
+ * Copyright (C) 2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts;
+
+import java.util.Map;
+
+
+/**
+ * This interface is used to set the values of a font during configuration time.
+ */
+public interface MutableFont {
+
+ /**
+ * Sets the font name.
+ * @param name font name
+ */
+ void setFontName(String name);
+
+ /**
+ * Sets the path to the embeddable font file.
+ * @param path URI to the file
+ */
+ void setEmbedFileName(String path);
+
+ /**
+ * Sets the resource name of the embeddable font file.
+ * @param name resource name
+ */
+ void setEmbedResourceName(String name);
+
+ /**
+ * Sets the capital height value.
+ * @param capHeight capital height
+ */
+ void setCapHeight(int capHeight);
+
+ /**
+ * Sets the ascent value.
+ * @param ascender ascent height
+ */
+ void setAscender(int ascender);
+
+ /**
+ * Sets the descent value.
+ * @param descender descent value
+ */
+ void setDescender(int descender);
+
+ /**
+ * Sets the font's bounding box
+ * @param bbox bounding box
+ */
+ void setFontBBox(int[] bbox);
+
+ /**
+ * Sets the font's flags
+ * @param flags flags
+ */
+ void setFlags(int flags);
+
+ /**
+ * Sets the font's StemV value.
+ * @param stemV StemV
+ */
+ void setStemV(int stemV);
+
+ /**
+ * Sets the font's italic angle.
+ * @param italicAngle italic angle
+ */
+ void setItalicAngle(int italicAngle);
+
+ /**
+ * Sets the font's default width
+ * @param width default width
+ */
+ void setMissingWidth(int width);
+
+ /**
+ * Sets the font type.
+ * @param fontType font type
+ */
+ void setFontType(FontType fontType);
+
+ /**
+ * Sets the index of the first character in the character table.
+ * @param index index of first character
+ */
+ void setFirstChar(int index);
+
+ /**
+ * Sets the index of the last character in the character table.
+ * @param index index of the last character
+ */
+ void setLastChar(int index);
+
+ /**
+ * Enables/disabled kerning.
+ * @param enabled True if kerning should be enabled if available
+ */
+ void setKerningEnabled(boolean enabled);
+
+ /**
+ * Adds an entry to the kerning table.
+ * @param key Kerning key
+ * @param value Kerning value
+ */
+ void putKerningEntry(Integer key, Map value);
+
+}
--- /dev/null
+/*
+ * $Id$
+ * Copyright (C) 2001-2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts;
+
+/**
+ * Generic SingleByte font
+ */
+public class SingleByteFont extends CustomFont {
+
+ private final CodePointMapping mapping
+ = CodePointMapping.getMapping("WinAnsiEncoding");
+
+ private String encoding = "WinAnsiEncoding";
+
+ private int width[] = null;
+
+
+ /**
+ * @see org.apache.fop.fonts.FontDescriptor#isEmbeddable()
+ */
+ public boolean isEmbeddable() {
+ return (getEmbedFileName() == null && getEmbedResourceName() == null) ? false
+ : true;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.Font#getEncoding()
+ */
+ public String getEncoding() {
+ return encoding;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontMetrics#getWidth(int, int)
+ */
+ public int getWidth(int i, int size) {
+ return size * width[i];
+ }
+
+ /**
+ * @see org.apache.fop.fonts.FontMetrics#getWidths()
+ */
+ public int[] getWidths() {
+ int[] arr = new int[width.length];
+ System.arraycopy(width, 0, arr, 0, width.length - 1);
+ /*
+ for (int i = 0; i < arr.length; i++)
+ arr[i] *= size;
+ */
+ return arr;
+ }
+
+ /**
+ * @see org.apache.fop.fonts.Font#mapChar(char)
+ */
+ public char mapChar(char c) {
+ char d = mapping.mapChar(c);
+ if (d != 0) {
+ return d;
+ } else {
+ return '#';
+ }
+ }
+
+ /* ---- single byte font specific setters --- */
+
+ /**
+ * Sets a width for a character.
+ * @param index index of the character
+ * @param width the width of the character
+ */
+ public void setWidth(int index, int width) {
+ if (this.width == null) {
+ this.width = new int[256];
+ }
+ this.width[index] = width;
+ }
+
+}
+
+++ /dev/null
-/*
- * $Id$
- * Copyright (C) 2001-2002 The Apache Software Foundation. All rights reserved.
- * For details on use and redistribution please refer to the
- * LICENSE file included with these sources.
- */
-
-package org.apache.fop.fonts;
-
-/**
- * The CMap entry contains information of a Unicode range and the
- * the glyph indexes related to the range
- */
-public class TTFCmapEntry {
-
- private int unicodeStart;
- private int unicodeEnd;
- private int glyphStartIndex;
-
- TTFCmapEntry() {
- unicodeStart = 0;
- unicodeEnd = 0;
- glyphStartIndex = 0;
- }
-
- TTFCmapEntry(int unicodeStart, int unicodeEnd, int glyphStartIndex) {
- this.unicodeStart = unicodeStart;
- this.unicodeEnd = unicodeEnd;
- this.glyphStartIndex = glyphStartIndex;
- }
-
- /**
- * @see java.lang.Object#equals(Object)
- */
- public boolean equals(Object o) {
- if (o instanceof TTFCmapEntry) {
- TTFCmapEntry ce = (TTFCmapEntry)o;
- if (ce.unicodeStart == this.unicodeStart
- && ce.unicodeEnd == this.unicodeEnd
- && ce.glyphStartIndex == this.glyphStartIndex) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Returns the glyphStartIndex.
- * @return int
- */
- public int getGlyphStartIndex() {
- return glyphStartIndex;
- }
-
- /**
- * Returns the unicodeEnd.
- * @return int
- */
- public int getUnicodeEnd() {
- return unicodeEnd;
- }
-
- /**
- * Returns the unicodeStart.
- * @return int
- */
- public int getUnicodeStart() {
- return unicodeStart;
- }
-
- /**
- * Sets the glyphStartIndex.
- * @param glyphStartIndex The glyphStartIndex to set
- */
- public void setGlyphStartIndex(int glyphStartIndex) {
- this.glyphStartIndex = glyphStartIndex;
- }
-
- /**
- * Sets the unicodeEnd.
- * @param unicodeEnd The unicodeEnd to set
- */
- public void setUnicodeEnd(int unicodeEnd) {
- this.unicodeEnd = unicodeEnd;
- }
-
- /**
- * Sets the unicodeStart.
- * @param unicodeStart The unicodeStart to set
- */
- public void setUnicodeStart(int unicodeStart) {
- this.unicodeStart = unicodeStart;
- }
-
-}
+++ /dev/null
-/*
- * $Id$
- * Copyright (C) 2001-2002 The Apache Software Foundation. All rights reserved.
- * For details on use and redistribution please refer to the
- * LICENSE file included with these sources.
- */
-
-package org.apache.fop.fonts;
-
-import java.io.IOException;
-
-class TTFDirTabEntry {
-
- private byte[] tag = new byte[4];
- private int checksum;
- private long offset;
- private long length;
-
- /**
- * Read Dir Tab, return tag name
- */
- public String read(FontFileReader in) throws IOException {
- tag[0] = in.readTTFByte();
- tag[1] = in.readTTFByte();
- tag[2] = in.readTTFByte();
- tag[3] = in.readTTFByte();
-
- in.skip(4); // Skip checksum
-
- offset = in.readTTFULong();
- length = in.readTTFULong();
-
- //System.out.println(this.toString());
- return new String(tag, "ISO-8859-1");
- }
-
-
- public String toString() {
- return "Read dir tab ["
- + tag[0] + " " + tag[1] + " " + tag[2] + " " + tag[3] + "]"
- + " offset: " + offset
- + " length: " + length
- + " name: " + tag;
- }
-
- /**
- * Returns the checksum.
- * @return int
- */
- public int getChecksum() {
- return checksum;
- }
-
- /**
- * Returns the length.
- * @return long
- */
- public long getLength() {
- return length;
- }
-
- /**
- * Returns the offset.
- * @return long
- */
- public long getOffset() {
- return offset;
- }
-
- /**
- * Returns the tag.
- * @return byte[]
- */
- public byte[] getTag() {
- return tag;
- }
-
-}
+++ /dev/null
-/*
- * $Id$
- * Copyright (C) 2001-2002 The Apache Software Foundation. All rights reserved.
- * For details on use and redistribution please refer to the
- * LICENSE file included with these sources.
- */
-
-package org.apache.fop.fonts;
-
-import java.io.IOException;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.List;
-
-import org.apache.avalon.framework.logger.AbstractLogEnabled;
-import org.apache.avalon.framework.logger.ConsoleLogger;
-import org.apache.avalon.framework.logger.Logger;
-
-/**
- * Reads a TrueType file or a TrueType Collection.
- * The TrueType spec can be found at the Microsoft.
- * Typography site: http://www.microsoft.com/truetype/
- */
-public class TTFFile extends AbstractLogEnabled {
-
- static final byte NTABS = 24;
- static final int NMACGLYPHS = 258;
- static final int MAX_CHAR_CODE = 255;
- static final int ENC_BUF_SIZE = 1024;
-
- private String encoding = "WinAnsiEncoding"; // Default encoding
-
- private short firstChar = 0;
- private boolean isEmbeddable = true;
- private boolean hasSerifs = true;
- /**
- * Table directory
- */
- protected Map dirTabs;
- private Map kerningTab; // for CIDs
- private Map ansiKerningTab; // For winAnsiEncoding
- private List cmaps;
- private List unicodeMapping;
-
- private int upem; // unitsPerEm from "head" table
- private int nhmtx; // Number of horizontal metrics
- private int postFormat;
- private int locaFormat;
- /**
- * Offset to last loca
- */
- protected long lastLoca = 0;
- private int numberOfGlyphs; // Number of glyphs in font (read from "maxp" table)
- private int nmGlyphs; // Used in fixWidths - remove?
-
- /**
- * Contains glyph data
- */
- protected TTFMtxEntry mtxTab[]; // Contains glyph data
- private int[] mtxEncoded = null;
-
- private String fontName = "";
- private String fullName = "";
- private String notice = "";
- private String familyName = "";
- private String subFamilyName = "";
-
- private long italicAngle = 0;
- private long isFixedPitch = 0;
- private int fontBBox1 = 0;
- private int fontBBox2 = 0;
- private int fontBBox3 = 0;
- private int fontBBox4 = 0;
- private int capHeight = 0;
- private int underlinePosition = 0;
- private int underlineThickness = 0;
- private int xHeight = 0;
- private int ascender = 0;
- private int descender = 0;
-
- private short lastChar = 0;
-
- private int ansiWidth[];
- private Map ansiIndex;
-
- /**
- * Position inputstream to position indicated
- * in the dirtab offset + offset
- */
- void seekTab(FontFileReader in, String name,
- long offset) throws IOException {
- TTFDirTabEntry dt = (TTFDirTabEntry)dirTabs.get(name);
- if (dt == null) {
- getLogger().error("Dirtab " + name + " not found.");
- return;
- }
-
- in.seekSet(dt.getOffset() + offset);
- }
-
- /**
- * Convert from truetype unit to pdf unit based on the
- * unitsPerEm field in the "head" table
- * @param n truetype unit
- * @return pdf unit
- */
- public int convertTTFUnit2PDFUnit(int n) {
- int ret;
- if (n < 0) {
- long rest1 = n % upem;
- long storrest = 1000 * rest1;
- long ledd2 = rest1 / storrest;
- ret = -((-1000 * n) / upem - (int)ledd2);
- } else {
- ret = (n / upem) * 1000 + ((n % upem) * 1000) / upem;
- }
-
- return ret;
- }
-
- /**
- * Read the cmap table,
- * return false if the table is not present or only unsupported
- * tables are present. Currently only unicode cmaps are supported.
- * Set the unicodeIndex in the TTFMtxEntries and fills in the
- * cmaps vector.
- */
- private boolean readCMAP(FontFileReader in) throws IOException {
-
- unicodeMapping = new java.util.ArrayList();
-
- //Read CMAP table and correct mtxTab.index
- int mtxPtr = 0;
-
- seekTab(in, "cmap", 2);
- int numCMap = in.readTTFUShort(); // Number of cmap subtables
- long cmapUniOffset = 0;
-
- getLogger().info(numCMap + " cmap tables");
-
- //Read offset for all tables. We are only interested in the unicode table
- for (int i = 0; i < numCMap; i++) {
- int cmapPID = in.readTTFUShort();
- int cmapEID = in.readTTFUShort();
- long cmapOffset = in.readTTFULong();
-
- getLogger().debug("Platform ID: " + cmapPID
- + " Encoding: " + cmapEID);
-
- if (cmapPID == 3 && cmapEID == 1) {
- cmapUniOffset = cmapOffset;
- }
- }
-
- if (cmapUniOffset <= 0) {
- getLogger().fatalError("Unicode cmap table not present");
- getLogger().fatalError("Unsupported format: Aborting");
- return false;
- }
-
- // Read unicode cmap
- seekTab(in, "cmap", cmapUniOffset);
- int cmapFormat = in.readTTFUShort();
- /*int cmap_length =*/ in.readTTFUShort(); //skip cmap length
-
- getLogger().info("CMAP format: " + cmapFormat);
- if (cmapFormat == 4) {
- in.skip(2); // Skip version number
- int cmapSegCountX2 = in.readTTFUShort();
- int cmapSearchRange = in.readTTFUShort();
- int cmapEntrySelector = in.readTTFUShort();
- int cmapRangeShift = in.readTTFUShort();
-
- getLogger().debug("segCountX2 : " + cmapSegCountX2);
- getLogger().debug("searchRange : " + cmapSearchRange);
- getLogger().debug("entrySelector: " + cmapEntrySelector);
- getLogger().debug("rangeShift : " + cmapRangeShift);
-
-
- int cmapEndCounts[] = new int[cmapSegCountX2 / 2];
- int cmapStartCounts[] = new int[cmapSegCountX2 / 2];
- int cmapDeltas[] = new int[cmapSegCountX2 / 2];
- int cmapRangeOffsets[] = new int[cmapSegCountX2 / 2];
-
- for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
- cmapEndCounts[i] = in.readTTFUShort();
- }
-
- in.skip(2); // Skip reservedPad
-
- for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
- cmapStartCounts[i] = in.readTTFUShort();
- }
-
- for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
- cmapDeltas[i] = in.readTTFShort();
- }
-
- //int startRangeOffset = in.getCurrentPos();
-
- for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
- cmapRangeOffsets[i] = in.readTTFUShort();
- }
-
- int glyphIdArrayOffset = in.getCurrentPos();
-
- // Insert the unicode id for the glyphs in mtxTab
- // and fill in the cmaps ArrayList
-
- for (int i = 0; i < cmapStartCounts.length; i++) {
-
- getLogger().debug(i + ": " + cmapStartCounts[i]
- + " - " + cmapEndCounts[i]);
-
- for (int j = cmapStartCounts[i]; j <= cmapEndCounts[i]; j++) {
-
- // Update lastChar
- if (j < 256 && j > lastChar) {
- lastChar = (short)j;
- }
-
- if (mtxPtr < mtxTab.length) {
- int glyphIdx;
- // the last character 65535 = .notdef
- // may have a range offset
- if (cmapRangeOffsets[i] != 0 && j != 65535) {
- int glyphOffset = glyphIdArrayOffset
- + ((cmapRangeOffsets[i] / 2)
- + (j - cmapStartCounts[i])
- + (i)
- - cmapSegCountX2 / 2) * 2;
- in.seekSet(glyphOffset);
- glyphIdx = (in.readTTFUShort() + cmapDeltas[i])
- & 0xffff;
-
- unicodeMapping.add(new UnicodeMapping(glyphIdx, j));
- mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(j));
-
-
- // Also add winAnsiWidth
- List v = (List)ansiIndex.get(new Integer(j));
- if (v != null) {
- Iterator e = v.listIterator();
- while (e.hasNext()) {
- Integer aIdx = (Integer)e.next();
- ansiWidth[aIdx.intValue()] =
- mtxTab[glyphIdx].getWx();
-
- getLogger().debug("Added width "
- + mtxTab[glyphIdx].getWx()
- + " uni: " + j
- + " ansi: " + aIdx.intValue());
- }
- }
-
- getLogger().debug("Idx: "
- + glyphIdx
- + " Delta: " + cmapDeltas[i]
- + " Unicode: " + j
- + " name: " + mtxTab[glyphIdx].getName());
- } else {
- glyphIdx = (j + cmapDeltas[i]) & 0xffff;
-
- if (glyphIdx < mtxTab.length) {
- mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(j));
- } else {
- getLogger().debug("Glyph " + glyphIdx
- + " out of range: "
- + mtxTab.length);
- }
-
- unicodeMapping.add(new UnicodeMapping(glyphIdx, j));
- if (glyphIdx < mtxTab.length) {
- mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(j));
- } else {
- getLogger().debug("Glyph " + glyphIdx
- + " out of range: "
- + mtxTab.length);
- }
-
- // Also add winAnsiWidth
- List v = (List)ansiIndex.get(new Integer(j));
- if (v != null) {
- Iterator e = v.listIterator();
- while (e.hasNext()) {
- Integer aIdx = (Integer)e.next();
- ansiWidth[aIdx.intValue()] = mtxTab[glyphIdx].getWx();
- }
- }
-
- //getLogger().debug("IIdx: " +
- // mtxPtr +
- // " Delta: " + cmap_deltas[i] +
- // " Unicode: " + j +
- // " name: " +
- // mtxTab[(j+cmap_deltas[i]) & 0xffff].name);
-
- }
- if (glyphIdx < mtxTab.length) {
- if (mtxTab[glyphIdx].getUnicodeIndex().size() < 2) {
- mtxPtr++;
- }
- }
- }
- }
- }
- }
- return true;
- }
-
- /**
- * Print first char/last char
- */
- private void printMaxMin() {
- int min = 255;
- int max = 0;
- for (int i = 0; i < mtxTab.length; i++) {
- if (mtxTab[i].getIndex() < min) {
- min = mtxTab[i].getIndex();
- }
- if (mtxTab[i].getIndex() > max) {
- max = mtxTab[i].getIndex();
- }
- }
- getLogger().info("Min: " + min);
- getLogger().info("Max: " + max);
- }
-
-
- /**
- * Reads the font using a FontFileReader.
- *
- * @param in The FontFileReader to use
- * @throws IOException In case of an I/O problem
- */
- public void readFont(FontFileReader in) throws IOException {
- readFont(in, (String)null);
- }
-
- /**
- * initialize the ansiWidths array (for winAnsiEncoding)
- * and fill with the missingwidth
- */
- private void initAnsiWidths() {
- ansiWidth = new int[256];
- for (int i = 0; i < 256; i++) {
- ansiWidth[i] = mtxTab[0].getWx();
- }
-
- // Create an index hash to the ansiWidth
- // Can't just index the winAnsiEncoding when inserting widths
- // same char (eg bullet) is repeated more than one place
- ansiIndex = new java.util.HashMap();
- for (int i = 32; i < Glyphs.WINANSI_ENCODING.length; i++) {
- Integer ansi = new Integer(i);
- Integer uni = new Integer((int)Glyphs.WINANSI_ENCODING[i]);
-
- List v = (List)ansiIndex.get(uni);
- if (v == null) {
- v = new java.util.ArrayList();
- ansiIndex.put(uni, v);
- }
- v.add(ansi);
- }
- }
-
- /**
- * Read the font data.
- * If the fontfile is a TrueType Collection (.ttc file)
- * the name of the font to read data for must be supplied,
- * else the name is ignored.
- *
- * @param in The FontFileReader to use
- * @param name The name of the font
- * @return boolean Returns true if the font is valid
- * @throws IOException In case of an I/O problem
- */
- public boolean readFont(FontFileReader in, String name) throws IOException {
-
- /*
- * Check if TrueType collection, and that the name
- * exists in the collection
- */
- if (!checkTTC(in, name)) {
- throw new IOException("Failed to read font");
- }
-
- readDirTabs(in);
- readFontHeader(in);
- getNumGlyphs(in);
- getLogger().info("Number of glyphs in font: " + numberOfGlyphs);
- readHorizontalHeader(in);
- readHorizontalMetrics(in);
- initAnsiWidths();
- readPostscript(in);
- readOS2(in);
- readIndexToLocation(in);
- readGlyf(in);
- readName(in);
- readPCLT(in);
- // Read cmap table and fill in ansiwidths
- boolean valid = readCMAP(in);
- if (!valid) {
- return false;
- }
- // Create cmaps for bfentries
- createCMaps();
- // print_max_min();
-
- readKerning(in);
- return true;
- }
-
- private void createCMaps() {
- cmaps = new java.util.ArrayList();
- TTFCmapEntry tce = new TTFCmapEntry();
-
- Iterator e = unicodeMapping.listIterator();
- UnicodeMapping um = (UnicodeMapping)e.next();
- UnicodeMapping lastMapping = um;
-
- tce.setUnicodeStart(um.getUIdx());
- tce.setGlyphStartIndex(um.getGIdx());
-
- while (e.hasNext()) {
- um = (UnicodeMapping)e.next();
- if (((lastMapping.getUIdx() + 1) != um.getUIdx())
- || ((lastMapping.getGIdx() + 1) != um.getGIdx())) {
- tce.setUnicodeEnd(lastMapping.getUIdx());
- cmaps.add(tce);
-
- tce = new TTFCmapEntry();
- tce.setUnicodeStart(um.getUIdx());
- tce.setGlyphStartIndex(um.getGIdx());
- }
- lastMapping = um;
- }
-
- tce.setUnicodeEnd(um.getUIdx());
- cmaps.add(tce);
- }
-
- /**
- * Returns the Windows name of the font.
- * @return String The Windows name
- */
- public String getWindowsName() {
- return familyName + "," + subFamilyName;
- }
-
- /**
- * Returns the PostScript name of the font.
- * @return String The PostScript name
- */
- public String getPostscriptName() {
- if ("Regular".equals(subFamilyName) || "Roman".equals(subFamilyName)) {
- return familyName;
- } else {
- return familyName + "," + subFamilyName;
- }
- }
-
- /**
- * Returns the font family name of the font.
- * @return String The family name
- */
- public String getFamilyName() {
- return familyName;
- }
-
- /**
- * Returns the name of the character set used.
- * @return String The caracter set
- */
- public String getCharSetName() {
- return encoding;
- }
-
- /**
- * Returns the CapHeight attribute of the font.
- * @return int The CapHeight
- */
- public int getCapHeight() {
- return (int)convertTTFUnit2PDFUnit(capHeight);
- }
-
- /**
- * Returns the XHeight attribute of the font.
- * @return int The XHeight
- */
- public int getXHeight() {
- return (int)convertTTFUnit2PDFUnit(xHeight);
- }
-
- /**
- * Returns the Flags attribute of the font.
- * @return int The Flags
- */
- public int getFlags() {
- int flags = 32; // Use Adobe Standard charset
- if (italicAngle != 0) {
- flags = flags | 64;
- }
- if (isFixedPitch != 0) {
- flags = flags | 2;
- }
- if (hasSerifs) {
- flags = flags | 1;
- }
- return flags;
- }
-
-
- /**
- * Returns the StemV attribute of the font.
- * @return String The StemV
- */
- public String getStemV() {
- return "0";
- }
-
- /**
- * Returns the ItalicAngle attribute of the font.
- * @return String The ItalicAngle
- */
- public String getItalicAngle() {
- String ia = Short.toString((short)(italicAngle / 0x10000));
-
- // This is the correct italic angle, however only int italic
- // angles are supported at the moment so this is commented out.
- /*
- * if ((italicAngle % 0x10000) > 0 )
- * ia=ia+(comma+Short.toString((short)((short)((italicAngle % 0x10000)*1000)/0x10000)));
- */
- return ia;
- }
-
- /**
- * Returns the font bounding box.
- * @return int[] The font bbox
- */
- public int[] getFontBBox() {
- final int[] fbb = new int[4];
- fbb[0] = (int)convertTTFUnit2PDFUnit(fontBBox1);
- fbb[1] = (int)convertTTFUnit2PDFUnit(fontBBox2);
- fbb[2] = (int)convertTTFUnit2PDFUnit(fontBBox3);
- fbb[3] = (int)convertTTFUnit2PDFUnit(fontBBox4);
-
- return fbb;
- }
-
- /**
- * Returns the LowerCaseAscent attribute of the font.
- * @return int The LowerCaseAscent
- */
- public int getLowerCaseAscent() {
- return (int)convertTTFUnit2PDFUnit(ascender);
- }
-
- /**
- * Returns the LowerCaseDescent attribute of the font.
- * @return int The LowerCaseDescent
- */
- public int getLowerCaseDescent() {
- return (int)convertTTFUnit2PDFUnit(descender);
- }
-
- /**
- * Returns the index of the last character, but this is for WinAnsiEncoding
- * only, so the last char is < 256.
- * @return short Index of the last character (<256)
- */
- public short getLastChar() {
- return lastChar;
- }
-
- /**
- * Returns the index of the first character.
- * @return short Index of the first character
- */
- public short getFirstChar() {
- return firstChar;
- }
-
- /**
- * Returns an array of character widths.
- * @return int[] The character widths
- */
- public int[] getWidths() {
- int[] wx = new int[mtxTab.length];
- for (int i = 0; i < wx.length; i++) {
- wx[i] = (int)convertTTFUnit2PDFUnit(mtxTab[i].getWx());
- }
-
- return wx;
- }
-
- /**
- * Returns the width of a given character.
- * @param idx Index of the character
- * @return int Standard width
- */
- public int getCharWidth(int idx) {
- return (int)convertTTFUnit2PDFUnit(ansiWidth[idx]);
- }
-
- /**
- * Returns the kerning table.
- * @return Map The kerning table
- */
- public Map getKerning() {
- return kerningTab;
- }
-
- /**
- * Returns the ANSI kerning table.
- * @return Map The ANSI kerning table
- */
- public Map getAnsiKerning() {
- return ansiKerningTab;
- }
-
- /**
- * Indicates if the font may be embedded.
- * @return boolean True if it may be embedded
- */
- public boolean isEmbeddable() {
- return isEmbeddable;
- }
-
-
- /**
- * Read Table Directory from the current position in the
- * FontFileReader and fill the global HashMap dirTabs
- * with the table name (String) as key and a TTFDirTabEntry
- * as value.
- * @param in FontFileReader to read the table directory from
- * @throws IOException in case of an I/O problem
- */
- protected void readDirTabs(FontFileReader in) throws IOException {
- in.skip(4); // TTF_FIXED_SIZE
- int ntabs = in.readTTFUShort();
- in.skip(6); // 3xTTF_USHORT_SIZE
-
- dirTabs = new java.util.HashMap();
- TTFDirTabEntry[] pd = new TTFDirTabEntry[ntabs];
- getLogger().debug("Reading " + ntabs + " dir tables");
- for (int i = 0; i < ntabs; i++) {
- pd[i] = new TTFDirTabEntry();
- dirTabs.put(pd[i].read(in), pd[i]);
- }
- }
-
- /**
- * Read the "head" table, this reads the bounding box and
- * sets the upem (unitsPerEM) variable
- * @param in FontFileReader to read the header from
- * @throws IOException in case of an I/O problem
- */
- protected void readFontHeader(FontFileReader in) throws IOException {
- seekTab(in, "head", 2 * 4 + 2 * 4 + 2);
- upem = in.readTTFUShort();
-
- in.skip(16);
-
- fontBBox1 = in.readTTFShort();
- fontBBox2 = in.readTTFShort();
- fontBBox3 = in.readTTFShort();
- fontBBox4 = in.readTTFShort();
-
- in.skip(2 + 2 + 2);
-
- locaFormat = in.readTTFShort();
- }
-
- /**
- * Read the number of glyphs from the "maxp" table
- * @param in FontFileReader to read the number of glyphs from
- * @throws IOException in case of an I/O problem
- */
- protected void getNumGlyphs(FontFileReader in) throws IOException {
- seekTab(in, "maxp", 4);
- numberOfGlyphs = in.readTTFUShort();
- }
-
-
- /**
- * Read the "hhea" table to find the ascender and descender and
- * size of "hmtx" table, i.e. a fixed size font might have only
- * one width
- * @param in FontFileReader to read the hhea table from
- * @throws IOException in case of an I/O problem
- */
- protected void readHorizontalHeader(FontFileReader in)
- throws IOException {
- seekTab(in, "hhea", 4);
- ascender = in.readTTFShort(); // Use sTypoAscender in "OS/2" table?
- descender = in.readTTFShort(); // Use sTypoDescender in "OS/2" table?
-
- in.skip(2 + 2 + 3 * 2 + 8 * 2);
- nhmtx = in.readTTFUShort();
- getLogger().debug("Number of horizontal metrics: " + nhmtx);
- }
-
- /**
- * Read "hmtx" table and put the horizontal metrics
- * in the mtxTab array. If the number of metrics is less
- * than the number of glyphs (eg fixed size fonts), extend
- * the mtxTab array and fill in the missing widths
- * @param in FontFileReader to read the hmtx table from
- * @throws IOException in case of an I/O problem
- */
- protected void readHorizontalMetrics(FontFileReader in)
- throws IOException {
- seekTab(in, "hmtx", 0);
-
- int mtxSize = (numberOfGlyphs > nhmtx) ? numberOfGlyphs : nhmtx;
- mtxTab = new TTFMtxEntry[mtxSize];
-
- getLogger().debug("*** Widths array: \n");
- for (int i = 0; i < mtxSize; i++) {
- mtxTab[i] = new TTFMtxEntry();
- }
- for (int i = 0; i < nhmtx; i++) {
- mtxTab[i].setWx(in.readTTFUShort());
- mtxTab[i].setLsb(in.readTTFUShort());
-
- getLogger().debug(" width[" + i + "] = "
- + convertTTFUnit2PDFUnit(mtxTab[i].getWx()) + ";");
- }
-
- if (nhmtx < mtxSize) {
- // Fill in the missing widths
- int lastWidth = mtxTab[nhmtx - 1].getWx();
- for (int i = nhmtx; i < mtxSize; i++) {
- mtxTab[i].setWx(lastWidth);
- mtxTab[i].setLsb(in.readTTFUShort());
- }
- }
- }
-
-
- /**
- * Read the "post" table
- * containing the postscript names of the glyphs.
- */
- private final void readPostscript(FontFileReader in) throws IOException {
- String[] psGlyphsBuffer;
- int i, k, l;
-
- seekTab(in, "post", 0);
- postFormat = in.readTTFLong();
- italicAngle = in.readTTFULong();
- underlinePosition = in.readTTFShort();
- underlineThickness = in.readTTFShort();
- isFixedPitch = in.readTTFULong();
-
- in.skip(4 * 4);
-
- getLogger().debug("Post format: " + postFormat);
- switch (postFormat) {
- case 0x00010000:
- getLogger().debug("Postscript format 1");
- for (i = 0; i < Glyphs.MAC_GLYPH_NAMES.length; i++) {
- mtxTab[i].setName(Glyphs.MAC_GLYPH_NAMES[i]);
- }
- break;
- case 0x00020000:
- getLogger().debug("Postscript format 2");
- int numGlyphStrings = 0;
- l = in.readTTFUShort(); // Num Glyphs
- // short minIndex=256;
- for (i = 0; i < l; i++) { // Read indexes
- mtxTab[i].setIndex(in.readTTFUShort());
- // if (minIndex > mtxTab[i].index)
- // minIndex=(short)mtxTab[i].index;
-
- if (mtxTab[i].getIndex() > 257) {
- numGlyphStrings++;
- }
-
- getLogger().debug("Post index: " + mtxTab[i].getIndex());
- }
- // firstChar=minIndex;
- psGlyphsBuffer = new String[numGlyphStrings];
- getLogger().debug("Reading " + numGlyphStrings
- + " glyphnames" + ", was n num glyphs=" + l);
- for (i = 0; i < psGlyphsBuffer.length; i++) {
- psGlyphsBuffer[i] = in.readTTFString(in.readTTFUByte());
- }
-
- for (i = 0; i < l; i++) {
- if (mtxTab[i].getIndex() < NMACGLYPHS) {
- mtxTab[i].setName(Glyphs.MAC_GLYPH_NAMES[mtxTab[i].getIndex()]);
- } else {
- k = mtxTab[i].getIndex() - NMACGLYPHS;
-
- getLogger().debug(k + " i=" + i + " mtx=" + mtxTab.length
- + " ps=" + psGlyphsBuffer.length);
-
- mtxTab[i].setName(psGlyphsBuffer[k]);
- }
- }
-
- break;
- case 0x00030000:
- // Postscript format 3 contains no glyph names
- getLogger().debug("Postscript format 3");
- break;
- default:
- getLogger().error("Unknown Postscript format: " + postFormat);
- }
- }
-
-
- /**
- * Read the "OS/2" table
- */
- private final void readOS2(FontFileReader in) throws IOException {
- // Check if font is embeddable
- if (dirTabs.get("OS/2") != null) {
- seekTab(in, "OS/2", 2 * 4);
- int fsType = in.readTTFUShort();
- if (fsType == 2) {
- isEmbeddable = false;
- } else {
- isEmbeddable = true;
- }
- } else {
- isEmbeddable = true;
- }
- }
-
- /**
- * Read the "loca" table.
- * @param in FontFileReader to read from
- * @throws IOException In case of a I/O problem
- */
- protected final void readIndexToLocation(FontFileReader in)
- throws IOException {
- seekTab(in, "loca", 0);
- for (int i = 0; i < numberOfGlyphs; i++) {
- mtxTab[i].setOffset(locaFormat == 1 ? in.readTTFULong()
- : (in.readTTFUShort() << 1));
- }
- lastLoca = (locaFormat == 1 ? in.readTTFULong()
- : (in.readTTFUShort() << 1));
- }
-
- /**
- * Read the "glyf" table to find the bounding boxes.
- * @param in FontFileReader to read from
- * @throws IOException In case of a I/O problem
- */
- private final void readGlyf(FontFileReader in) throws IOException {
- TTFDirTabEntry dirTab = (TTFDirTabEntry)dirTabs.get("glyf");
- for (int i = 0; i < (numberOfGlyphs - 1); i++) {
- if (mtxTab[i].getOffset() != mtxTab[i + 1].getOffset()) {
- in.seekSet(dirTab.getOffset() + mtxTab[i].getOffset());
- in.skip(2);
- final int[] bbox = {
- in.readTTFShort(),
- in.readTTFShort(),
- in.readTTFShort(),
- in.readTTFShort()};
- mtxTab[i].setBoundingBox(bbox);
- } else {
- mtxTab[i].setBoundingBox(mtxTab[0].getBoundingBox());
- }
- }
-
-
- long n = ((TTFDirTabEntry)dirTabs.get("glyf")).getOffset();
- for (int i = 0; i < numberOfGlyphs; i++) {
- if ((i + 1) >= mtxTab.length
- || mtxTab[i].getOffset() != mtxTab[i + 1].getOffset()) {
- in.seekSet(n + mtxTab[i].getOffset());
- in.skip(2);
- final int[] bbox = {
- in.readTTFShort(),
- in.readTTFShort(),
- in.readTTFShort(),
- in.readTTFShort()};
- mtxTab[i].setBoundingBox(bbox);
- } else {
- /**@todo Verify that this is correct, looks like a copy/paste bug (jm)*/
- final int bbox0 = mtxTab[0].getBoundingBox()[0];
- final int[] bbox = {bbox0, bbox0, bbox0, bbox0};
- mtxTab[i].setBoundingBox(bbox);
- /* Original code
- mtxTab[i].bbox[0] = mtxTab[0].bbox[0];
- mtxTab[i].bbox[1] = mtxTab[0].bbox[0];
- mtxTab[i].bbox[2] = mtxTab[0].bbox[0];
- mtxTab[i].bbox[3] = mtxTab[0].bbox[0]; */
- }
- getLogger().debug(mtxTab[i].toString(this));
- }
- }
-
- /**
- * Read the "name" table.
- * @param in FontFileReader to read from
- * @throws IOException In case of a I/O problem
- */
- private final void readName(FontFileReader in) throws IOException {
- seekTab(in, "name", 2);
- int i = in.getCurrentPos();
- int n = in.readTTFUShort();
- int j = in.readTTFUShort() + i - 2;
- i += 2 * 2;
-
- while (n-- > 0) {
- // getLogger().debug("Iteration: " + n);
- in.seekSet(i);
- final int platformID = in.readTTFUShort();
- final int encodingID = in.readTTFUShort();
- /*final int language_id =*/ in.readTTFUShort(); //Skip language id
-
- int k = in.readTTFUShort();
- int l = in.readTTFUShort();
-
- if (((platformID == 1 || platformID == 3) && (encodingID == 0 || encodingID == 1))
- && (k == 1 || k == 2 || k == 0 || k == 4 || k == 6)) {
- // if (k==1 || k==2 || k==0 || k==4 || k==6) {
- in.seekSet(j + in.readTTFUShort());
- String txt = in.readTTFString(l);
- // getLogger().debug(platform_id + " " + encoding_id
- // + " " + k + " " + txt);
- switch (k) {
- case 0:
- notice = txt;
- break;
- case 1:
- familyName = txt;
- break;
- case 2:
- subFamilyName = txt;
- break;
- case 4:
- fullName = txt;
- break;
- case 6:
- fontName = txt;
- break;
- }
- if (!notice.equals("")
- && !fullName.equals("")
- && !fontName.equals("")
- && !familyName.equals("")
- && !subFamilyName.equals("")) {
- break;
- }
- }
- i += 6 * 2;
- }
- }
-
- /**
- * Read the "PCLT" table to find xHeight and capHeight.
- * @param in FontFileReader to read from
- * @throws IOException In case of a I/O problem
- */
- private final void readPCLT(FontFileReader in) throws IOException {
- TTFDirTabEntry dirTab = (TTFDirTabEntry)dirTabs.get("PCLT");
- if (dirTab != null) {
- in.seekSet(dirTab.getOffset() + 4 + 4 + 2);
- xHeight = in.readTTFUShort();
- in.skip(2 * 2);
- capHeight = in.readTTFUShort();
- in.skip(2 + 16 + 8 + 6 + 1 + 1);
-
- int serifStyle = in.readTTFUByte();
- serifStyle = serifStyle >> 6;
- serifStyle = serifStyle & 3;
- if (serifStyle == 1) {
- hasSerifs = false;
- } else {
- hasSerifs = true;
- }
- } else {
- // Approximate capHeight from height of "H"
- // It's most unlikly that a font misses the PCLT table
- // This also assumes that psocriptnames exists ("H")
- // Should look it up int the cmap (that wouldn't help
- // for charsets without H anyway...)
- for (int i = 0; i < mtxTab.length; i++) {
- if ("H".equals(mtxTab[i].getName())) {
- capHeight = mtxTab[i].getBoundingBox()[3] - mtxTab[i].getBoundingBox()[1];
- }
- }
- }
- }
-
- /**
- * Read the kerning table, create a table for both CIDs and
- * winAnsiEncoding.
- * @param in FontFileReader to read from
- * @throws IOException In case of a I/O problem
- */
- private final void readKerning(FontFileReader in) throws IOException {
- // Read kerning
- kerningTab = new java.util.HashMap();
- ansiKerningTab = new java.util.HashMap();
- TTFDirTabEntry dirTab = (TTFDirTabEntry)dirTabs.get("kern");
- if (dirTab != null) {
- seekTab(in, "kern", 2);
- for (int n = in.readTTFUShort(); n > 0; n--) {
- in.skip(2 * 2);
- int k = in.readTTFUShort();
- if (!((k & 1) != 0) || (k & 2) != 0 || (k & 4) != 0) {
- return;
- }
- if ((k >> 8) != 0) {
- continue;
- }
-
- k = in.readTTFUShort();
- in.skip(3 * 2);
- while (k-- > 0) {
- int i = in.readTTFUShort();
- int j = in.readTTFUShort();
- int kpx = in.readTTFShort();
- if (kpx != 0) {
- // CID table
- Integer iObj = new Integer(i);
- Map adjTab = (Map)kerningTab.get(iObj);
- if (adjTab == null) {
- adjTab = new java.util.HashMap();
- }
- adjTab.put(new Integer(j),
- new Integer((int)convertTTFUnit2PDFUnit(kpx)));
- kerningTab.put(iObj, adjTab);
- }
- }
- }
- // getLogger().debug(kerningTab.toString());
-
- // Create winAnsiEncoded kerning table
- Iterator ae = kerningTab.keySet().iterator();
- while (ae.hasNext()) {
- Integer cidKey = (Integer)ae.next();
- Map akpx = new java.util.HashMap();
- Map ckpx = (Map)kerningTab.get(cidKey);
-
- Iterator aee = ckpx.keySet().iterator();
- while (aee.hasNext()) {
- Integer cidKey2 = (Integer)aee.next();
- Integer kern = (Integer)ckpx.get(cidKey2);
-
- Iterator uniMap = mtxTab[cidKey2.intValue()].getUnicodeIndex().listIterator();
- while (uniMap.hasNext()) {
- Integer unicodeKey = (Integer)uniMap.next();
- Integer[] ansiKeys = unicodeToWinAnsi(unicodeKey.intValue());
- for (int u = 0; u < ansiKeys.length; u++) {
- akpx.put(ansiKeys[u], kern);
- }
- }
- }
-
- if (akpx.size() > 0) {
- Iterator uniMap = mtxTab[cidKey.intValue()].getUnicodeIndex().listIterator();
- while (uniMap.hasNext()) {
- Integer unicodeKey = (Integer)uniMap.next();
- Integer[] ansiKeys = unicodeToWinAnsi(unicodeKey.intValue());
- for (int u = 0; u < ansiKeys.length; u++) {
- ansiKerningTab.put(ansiKeys[u], akpx);
- }
- }
- }
- }
- }
- }
-
- /**
- * Return a List with TTFCmapEntry.
- * @return A list of TTFCmapEntry objects
- */
- public List getCMaps() {
- return cmaps;
- }
-
- /**
- * Check if this is a TrueType collection and that the given
- * name exists in the collection.
- * If it does, set offset in fontfile to the beginning of
- * the Table Directory for that font.
- * @param in FontFileReader to read from
- * @param name The name to check
- * @return True if not collection or font name present, false otherwise
- * @throws IOException In case of an I/O problem
- */
- protected final boolean checkTTC(FontFileReader in, String name) throws IOException {
- String tag = in.readTTFString(4);
-
- if ("ttcf".equals(tag)) {
- // This is a TrueType Collection
- in.skip(4);
-
- // Read directory offsets
- int numDirectories = (int)in.readTTFULong();
- // int numDirectories=in.readTTFUShort();
- long[] dirOffsets = new long[numDirectories];
- for (int i = 0; i < numDirectories; i++) {
- dirOffsets[i] = in.readTTFULong();
- }
-
- getLogger().debug("This is a TrueType collection file with"
- + numDirectories + " fonts");
- getLogger().debug("Containing the following fonts: ");
- // Read all the directories and name tables to check
- // If the font exists - this is a bit ugly, but...
- boolean found = false;
-
- // Iterate through all name tables even if font
- // Is found, just to show all the names
- long dirTabOffset = 0;
- for (int i = 0; (i < numDirectories); i++) {
- in.seekSet(dirOffsets[i]);
- readDirTabs(in);
-
- readName(in);
-
- if (fullName.equals(name)) {
- found = true;
- dirTabOffset = dirOffsets[i];
- getLogger().debug("* " + fullName);
- } else {
- getLogger().debug(fullName);
- }
-
- // Reset names
- notice = "";
- fullName = "";
- familyName = "";
- fontName = "";
- subFamilyName = "";
- }
-
- in.seekSet(dirTabOffset);
- return found;
- } else {
- in.seekSet(0);
- return true;
- }
- }
-
- /*
- * Helper classes, they are not very efficient, but that really
- * doesn't matter...
- */
- private Integer[] unicodeToWinAnsi(int unicode) {
- List ret = new java.util.ArrayList();
- for (int i = 32; i < Glyphs.WINANSI_ENCODING.length; i++) {
- if (unicode == Glyphs.WINANSI_ENCODING[i]) {
- ret.add(new Integer(i));
- }
- }
- return (Integer[])ret.toArray(new Integer[0]);
- }
-
- /**
- * Dumps a few informational values to System.out.
- */
- public void printStuff() {
- System.out.println("Font name: " + fontName);
- System.out.println("Full name: " + fullName);
- System.out.println("Family name: " + familyName);
- System.out.println("Subfamily name: " + subFamilyName);
- System.out.println("Notice: " + notice);
- System.out.println("xHeight: " + (int)convertTTFUnit2PDFUnit(xHeight));
- System.out.println("capheight: " + (int)convertTTFUnit2PDFUnit(capHeight));
-
- int italic = (int)(italicAngle >> 16);
- System.out.println("Italic: " + italic);
- System.out.print("ItalicAngle: " + (short)(italicAngle / 0x10000));
- if ((italicAngle % 0x10000) > 0) {
- System.out.print("."
- + (short)((italicAngle % 0x10000) * 1000)
- / 0x10000);
- }
- System.out.println();
- System.out.println("Ascender: " + convertTTFUnit2PDFUnit(ascender));
- System.out.println("Descender: " + convertTTFUnit2PDFUnit(descender));
- System.out.println("FontBBox: [" + (int)convertTTFUnit2PDFUnit(fontBBox1)
- + " " + (int)convertTTFUnit2PDFUnit(fontBBox2) + " "
- + (int)convertTTFUnit2PDFUnit(fontBBox3) + " "
- + (int)convertTTFUnit2PDFUnit(fontBBox4) + "]");
- }
-
- /**
- * Static main method to get info about a TrueType font.
- * @param args The command line arguments
- */
- public static void main(String[] args) {
- int level = ConsoleLogger.LEVEL_WARN;
- Logger log = new ConsoleLogger(level);
- try {
- TTFFile ttfFile = new TTFFile();
- ttfFile.enableLogging(log);
-
- FontFileReader reader = new FontFileReader(args[0]);
-
- String name = null;
- if (args.length >= 2) {
- name = args[1];
- }
-
- ttfFile.readFont(reader, name);
- ttfFile.printStuff();
-
- } catch (IOException ioe) {
- log.error("Problem reading font: " + ioe.toString(), ioe);
- }
- }
-
-}
-
-
-/**
- * Key-value helper class
- */
-class UnicodeMapping {
-
- private int uIdx;
- private int gIdx;
-
- UnicodeMapping(int gIdx, int uIdx) {
- this.uIdx = uIdx;
- this.gIdx = gIdx;
- }
-
- /**
- * Returns the gIdx.
- * @return int
- */
- public int getGIdx() {
- return gIdx;
- }
-
- /**
- * Returns the uIdx.
- * @return int
- */
- public int getUIdx() {
- return uIdx;
- }
-
-}
+++ /dev/null
-/*
- * $Id$
- * Copyright (C) 2001-2002 The Apache Software Foundation. All rights reserved.
- * For details on use and redistribution please refer to the
- * LICENSE file included with these sources.
- */
-
-package org.apache.fop.fonts;
-
-import java.util.List;
-
-class TTFMtxEntry {
-
- private int wx;
- private int lsb;
- private String name = "";
- private int index;
- private List unicodeIndex = new java.util.ArrayList();
- private int[] boundingBox = new int[4];
- private long offset;
- private byte found = 0;
-
- public String toString(TTFFile t) {
- return "Glyph " + name + " index: " + index + " bbox [ "
- + t.convertTTFUnit2PDFUnit(boundingBox[0]) + " "
- + t.convertTTFUnit2PDFUnit(boundingBox[1]) + " "
- + t.convertTTFUnit2PDFUnit(boundingBox[2]) + " "
- + t.convertTTFUnit2PDFUnit(boundingBox[3]) + "] wx: "
- + t.convertTTFUnit2PDFUnit(wx);
- }
-
- /**
- * Returns the boundingBox.
- * @return int[]
- */
- public int[] getBoundingBox() {
- return boundingBox;
- }
-
- /**
- * Sets the boundingBox.
- * @param boundingBox The boundingBox to set
- */
- public void setBoundingBox(int[] boundingBox) {
- this.boundingBox = boundingBox;
- }
-
- /**
- * Returns the found.
- * @return byte
- */
- public byte getFound() {
- return found;
- }
-
- /**
- * Returns the index.
- * @return int
- */
- public int getIndex() {
- return index;
- }
-
- /**
- * Returns the lsb.
- * @return int
- */
- public int getLsb() {
- return lsb;
- }
-
- /**
- * Returns the name.
- * @return String
- */
- public String getName() {
- return name;
- }
-
- /**
- * Returns the offset.
- * @return long
- */
- public long getOffset() {
- return offset;
- }
-
- /**
- * Returns the unicodeIndex.
- * @return List
- */
- public List getUnicodeIndex() {
- return unicodeIndex;
- }
-
- /**
- * Returns the wx.
- * @return int
- */
- public int getWx() {
- return wx;
- }
-
- /**
- * Sets the found.
- * @param found The found to set
- */
- public void setFound(byte found) {
- this.found = found;
- }
-
- /**
- * Sets the index.
- * @param index The index to set
- */
- public void setIndex(int index) {
- this.index = index;
- }
-
- /**
- * Sets the lsb.
- * @param lsb The lsb to set
- */
- public void setLsb(int lsb) {
- this.lsb = lsb;
- }
-
- /**
- * Sets the name.
- * @param name The name to set
- */
- public void setName(String name) {
- this.name = name;
- }
-
- /**
- * Sets the offset.
- * @param offset The offset to set
- */
- public void setOffset(long offset) {
- this.offset = offset;
- }
-
- /**
- * Sets the wx.
- * @param wx The wx to set
- */
- public void setWx(int wx) {
- this.wx = wx;
- }
-
-
-}
+++ /dev/null
-/*
- * $Id$
- * Copyright (C) 2001-2002 The Apache Software Foundation. All rights reserved.
- * For details on use and redistribution please refer to the
- * LICENSE file included with these sources.
- */
-
-package org.apache.fop.fonts;
-
-import java.io.IOException;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.List;
-
-/**
- * Reads a TrueType file and generates a subset
- * that can be used to embed a TrueType CID font
- * TrueType tables needed for embedded CID fonts are:
- * "head", "hhea", "loca", "maxp", "cvt ", "prep", "glyf", "hmtx" and "fpgm".
- * The TrueType spec can be found at the Microsoft
- * Typography site: http://www.microsoft.com/truetype/
- */
-public class TTFSubSetFile extends TTFFile {
-
- private byte[] output = null;
- private int realSize = 0;
- private int currentPos = 0;
-
- /*
- * Offsets in name table to be filled out by table.
- * The offsets are to the checkSum field
- */
- private int cvtDirOffset = 0;
- private int fpgmDirOffset = 0;
- private int glyfDirOffset = 0;
- private int headDirOffset = 0;
- private int hheaDirOffset = 0;
- private int hmtxDirOffset = 0;
- private int locaDirOffset = 0;
- private int maxpDirOffset = 0;
- private int prepDirOffset = 0;
-
- private int checkSumAdjustmentOffset = 0;
- private int locaOffset = 0;
-
- /**
- * Initalize the output array
- */
- private void init(int size) {
- output = new byte[size];
- realSize = 0;
- currentPos = 0;
-
- // createDirectory()
- }
-
- /**
- * Create the directory table
- */
- private void createDirectory() {
- int numTables = 9;
- // Create the TrueType header
- writeByte((byte)0);
- writeByte((byte)1);
- writeByte((byte)0);
- writeByte((byte)0);
- realSize += 4;
-
- writeUShort(numTables);
- realSize += 2;
-
- // Create searchRange, entrySelector and rangeShift
- int maxPow = maxPow2(numTables);
- int searchRange = maxPow * 16;
- writeUShort(searchRange);
- realSize += 2;
-
- writeUShort(maxPow);
- realSize += 2;
-
- writeUShort((numTables * 16) - searchRange);
- realSize += 2;
-
- // Create space for the table entries
- writeString("cvt ");
- cvtDirOffset = currentPos;
- currentPos += 12;
- realSize += 16;
-
- writeString("fpgm");
- fpgmDirOffset = currentPos;
- currentPos += 12;
- realSize += 16;
-
- writeString("glyf");
- glyfDirOffset = currentPos;
- currentPos += 12;
- realSize += 16;
-
- writeString("head");
- headDirOffset = currentPos;
- currentPos += 12;
- realSize += 16;
-
- writeString("hhea");
- hheaDirOffset = currentPos;
- currentPos += 12;
- realSize += 16;
-
- writeString("hmtx");
- hmtxDirOffset = currentPos;
- currentPos += 12;
- realSize += 16;
-
- writeString("loca");
- locaDirOffset = currentPos;
- currentPos += 12;
- realSize += 16;
-
- writeString("maxp");
- maxpDirOffset = currentPos;
- currentPos += 12;
- realSize += 16;
-
- writeString("prep");
- prepDirOffset = currentPos;
- currentPos += 12;
- realSize += 16;
- }
-
-
- /**
- * Copy the cvt table as is from original font to subset font
- */
- private void createCvt(FontFileReader in) throws IOException {
- TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("cvt ");
- if (entry != null) {
- pad4();
- seekTab(in, "cvt ", 0);
- System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
- 0, output, currentPos, (int)entry.getLength());
-
- int checksum = getCheckSum(currentPos, (int)entry.getLength());
- writeULong(cvtDirOffset, checksum);
- writeULong(cvtDirOffset + 4, currentPos);
- writeULong(cvtDirOffset + 8, (int)entry.getLength());
- currentPos += (int)entry.getLength();
- realSize += (int)entry.getLength();
- } else {
- throw new IOException("Can't find cvt table");
- }
- }
-
-
-
- /**
- * Copy the fpgm table as is from original font to subset font
- */
- private void createFpgm(FontFileReader in) throws IOException {
- TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("fpgm");
- if (entry != null) {
- pad4();
- seekTab(in, "fpgm", 0);
- System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
- 0, output, currentPos, (int)entry.getLength());
- int checksum = getCheckSum(currentPos, (int)entry.getLength());
- writeULong(fpgmDirOffset, checksum);
- writeULong(fpgmDirOffset + 4, currentPos);
- writeULong(fpgmDirOffset + 8, (int)entry.getLength());
- currentPos += (int)entry.getLength();
- realSize += (int)entry.getLength();
- } else {
- throw new IOException("Can't find fpgm table");
- }
- }
-
-
-
- /**
- * Create an empty loca table without updating checksum
- */
- private void createLoca(int size) throws IOException {
- pad4();
- locaOffset = currentPos;
- writeULong(locaDirOffset + 4, currentPos);
- writeULong(locaDirOffset + 8, size * 4 + 4);
- currentPos += size * 4 + 4;
- realSize += size * 4 + 4;
- }
-
-
- /**
- * Copy the maxp table as is from original font to subset font
- * and set num glyphs to size
- */
- private void createMaxp(FontFileReader in, int size) throws IOException {
- TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("maxp");
- if (entry != null) {
- pad4();
- seekTab(in, "maxp", 0);
- System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
- 0, output, currentPos, (int)entry.getLength());
- writeUShort(currentPos + 4, size);
-
- int checksum = getCheckSum(currentPos, (int)entry.getLength());
- writeULong(maxpDirOffset, checksum);
- writeULong(maxpDirOffset + 4, currentPos);
- writeULong(maxpDirOffset + 8, (int)entry.getLength());
- currentPos += (int)entry.getLength();
- realSize += (int)entry.getLength();
- } else {
- throw new IOException("Can't find maxp table");
- }
- }
-
-
- /**
- * Copy the prep table as is from original font to subset font
- */
- private void createPrep(FontFileReader in) throws IOException {
- TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("prep");
- if (entry != null) {
- pad4();
- seekTab(in, "prep", 0);
- System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
- 0, output, currentPos, (int)entry.getLength());
-
- int checksum = getCheckSum(currentPos, (int)entry.getLength());
- writeULong(prepDirOffset, checksum);
- writeULong(prepDirOffset + 4, currentPos);
- writeULong(prepDirOffset + 8, (int)entry.getLength());
- currentPos += (int)entry.getLength();
- realSize += (int)entry.getLength();
- } else {
- throw new IOException("Can't find prep table");
- }
- }
-
-
- /**
- * Copy the hhea table as is from original font to subset font
- * and fill in size of hmtx table
- */
- private void createHhea(FontFileReader in, int size) throws IOException {
- TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("hhea");
- if (entry != null) {
- pad4();
- seekTab(in, "hhea", 0);
- System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
- 0, output, currentPos, (int)entry.getLength());
- writeUShort((int)entry.getLength() + currentPos - 2, size);
-
- int checksum = getCheckSum(currentPos, (int)entry.getLength());
- writeULong(hheaDirOffset, checksum);
- writeULong(hheaDirOffset + 4, currentPos);
- writeULong(hheaDirOffset + 8, (int)entry.getLength());
- currentPos += (int)entry.getLength();
- realSize += (int)entry.getLength();
- } else {
- throw new IOException("Can't find hhea table");
- }
- }
-
-
- /**
- * Copy the head table as is from original font to subset font
- * and set indexToLocaFormat to long and set
- * checkSumAdjustment to 0, store offset to checkSumAdjustment
- * in checkSumAdjustmentOffset
- */
- private void createHead(FontFileReader in) throws IOException {
- TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("head");
- if (entry != null) {
- pad4();
- seekTab(in, "head", 0);
- System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
- 0, output, currentPos, (int)entry.getLength());
-
- checkSumAdjustmentOffset = currentPos + 8;
- output[currentPos + 8] = 0; // Set checkSumAdjustment to 0
- output[currentPos + 9] = 0;
- output[currentPos + 10] = 0;
- output[currentPos + 11] = 0;
- output[currentPos + 50] = 0; // long locaformat
- output[currentPos + 51] = 1; // long locaformat
-
- int checksum = getCheckSum(currentPos, (int)entry.getLength());
- writeULong(headDirOffset, checksum);
- writeULong(headDirOffset + 4, currentPos);
- writeULong(headDirOffset + 8, (int)entry.getLength());
-
- currentPos += (int)entry.getLength();
- realSize += (int)entry.getLength();
- } else {
- throw new IOException("Can't find head table");
- }
- }
-
-
- /**
- * Create the glyf table and fill in loca table
- */
- private void createGlyf(FontFileReader in,
- Map glyphs) throws IOException {
- TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("glyf");
- int size = 0;
- int start = 0;
- int endOffset = 0; // Store this as the last loca
- if (entry != null) {
- pad4();
- start = currentPos;
-
- /* Loca table must be in order by glyph index, so build
- * an array first and then write the glyph info and
- * location offset.
- */
- int[] origIndexes = new int[glyphs.size()];
-
- Iterator e = glyphs.keySet().iterator();
- while (e.hasNext()) {
- Integer origIndex = (Integer)e.next();
- Integer subsetIndex = (Integer)glyphs.get(origIndex);
- origIndexes[subsetIndex.intValue()] = origIndex.intValue();
- }
-
- for (int i = 0; i < origIndexes.length; i++) {
- int glyphLength = 0;
- int nextOffset = 0;
- int origGlyphIndex = origIndexes[i];
- if (origGlyphIndex >= (mtxTab.length - 1)) {
- nextOffset = (int)lastLoca;
- } else {
- nextOffset = (int)mtxTab[origGlyphIndex + 1].getOffset();
- }
- glyphLength = nextOffset - (int)mtxTab[origGlyphIndex].getOffset();
-
- // Copy glyph
- System.arraycopy(
- in.getBytes((int)entry.getOffset() + (int)mtxTab[origGlyphIndex].getOffset(),
- glyphLength), 0,
- output, currentPos,
- glyphLength);
-
-
- // Update loca table
- writeULong(locaOffset + i * 4, currentPos - start);
- if ((currentPos - start + glyphLength) > endOffset) {
- endOffset = (currentPos - start + glyphLength);
- }
-
- currentPos += glyphLength;
- realSize += glyphLength;
-
- }
-
- size = currentPos - start;
-
- int checksum = getCheckSum(start, size);
- writeULong(glyfDirOffset, checksum);
- writeULong(glyfDirOffset + 4, start);
- writeULong(glyfDirOffset + 8, size);
- currentPos += 12;
- realSize += 12;
-
- // Update loca checksum and last loca index
- writeULong(locaOffset + glyphs.size() * 4, endOffset);
-
- checksum = getCheckSum(locaOffset, glyphs.size() * 4 + 4);
- writeULong(locaDirOffset, checksum);
- } else {
- throw new IOException("Can't find glyf table");
- }
- }
-
-
- /**
- * Create the hmtx table by copying metrics from original
- * font to subset font. The glyphs Map contains an
- * Integer key and Integer value that maps the original
- * metric (key) to the subset metric (value)
- */
- private void createHmtx(FontFileReader in,
- Map glyphs) throws IOException {
- TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("hmtx");
-
- int longHorMetricSize = glyphs.size() * 2;
- int leftSideBearingSize = glyphs.size() * 2;
- int hmtxSize = longHorMetricSize + leftSideBearingSize;
-
- if (entry != null) {
- pad4();
- //int offset = (int)entry.offset;
- Iterator e = glyphs.keySet().iterator();
- while (e.hasNext()) {
- Integer origIndex = (Integer)e.next();
- Integer subsetIndex = (Integer)glyphs.get(origIndex);
-
- writeUShort(currentPos + subsetIndex.intValue() * 4,
- mtxTab[origIndex.intValue()].getWx());
- writeUShort(currentPos + subsetIndex.intValue() * 4 + 2,
- mtxTab[origIndex.intValue()].getLsb());
- }
-
- int checksum = getCheckSum(currentPos, hmtxSize);
- writeULong(hmtxDirOffset, checksum);
- writeULong(hmtxDirOffset + 4, currentPos);
- writeULong(hmtxDirOffset + 8, hmtxSize);
- currentPos += hmtxSize;
- realSize += hmtxSize;
- } else {
- throw new IOException("Can't find hmtx table");
- }
- }
-
- /**
- * Returns a List containing the glyph itself plus all glyphs
- * that this composite glyph uses
- */
- private List getIncludedGlyphs(FontFileReader in, int glyphOffset,
- Integer glyphIdx) throws IOException {
- List ret = new java.util.ArrayList();
- ret.add(glyphIdx);
- int offset = glyphOffset + (int)mtxTab[glyphIdx.intValue()].getOffset() + 10;
- Integer compositeIdx = null;
- int flags = 0;
- boolean moreComposites = true;
- while (moreComposites) {
- flags = in.readTTFUShort(offset);
- compositeIdx = new Integer(in.readTTFUShort(offset + 2));
- ret.add(compositeIdx);
-
- offset += 4;
- if ((flags & 1) > 0) {
- // ARG_1_AND_ARG_2_ARE_WORDS
- offset += 4;
- } else {
- offset += 2;
- }
-
- if ((flags & 8) > 0) {
- offset += 2; // WE_HAVE_A_SCALE
- } else if ((flags & 64) > 0) {
- offset += 4; // WE_HAVE_AN_X_AND_Y_SCALE
- } else if ((flags & 128) > 0) {
- offset += 8; // WE_HAVE_A_TWO_BY_TWO
- }
-
- if ((flags & 32) > 0) {
- moreComposites = true;
- } else {
- moreComposites = false;
- }
- }
-
- return ret;
- }
-
-
- /**
- * Rewrite all compositepointers in glyphindex glyphIdx
- *
- */
- private void remapComposite(FontFileReader in, Map glyphs,
- int glyphOffset,
- Integer glyphIdx) throws IOException {
- int offset = glyphOffset + (int)mtxTab[glyphIdx.intValue()].getOffset()
- + 10;
-
- Integer compositeIdx = null;
- int flags = 0;
- boolean moreComposites = true;
-
- while (moreComposites) {
- flags = in.readTTFUShort(offset);
- compositeIdx = new Integer(in.readTTFUShort(offset + 2));
- Integer newIdx = (Integer)glyphs.get(compositeIdx);
- if (newIdx == null) {
- // This errormessage would look much better
- // if the fontname was printed to
- //log.error("An embedded font "
- // + "contains bad glyph data. "
- // + "Characters might not display "
- // + "correctly.");
- moreComposites = false;
- continue;
- }
-
- in.writeTTFUShort(offset + 2, newIdx.intValue());
-
- offset += 4;
-
- if ((flags & 1) > 0) {
- // ARG_1_AND_ARG_2_ARE_WORDS
- offset += 4;
- } else {
- offset += 2;
- }
-
- if ((flags & 8) > 0) {
- offset += 2; // WE_HAVE_A_SCALE
- } else if ((flags & 64) > 0) {
- offset += 4; // WE_HAVE_AN_X_AND_Y_SCALE
- } else if ((flags & 128) > 0) {
- offset += 8; // WE_HAVE_A_TWO_BY_TWO
- }
-
- if ((flags & 32) > 0) {
- moreComposites = true;
- } else {
- moreComposites = false;
- }
- }
- }
-
-
- /**
- * Scan all the original glyphs for composite glyphs and add those glyphs
- * to the glyphmapping also rewrite the composite glyph pointers to the new
- * mapping
- */
- private void scanGlyphs(FontFileReader in,
- Map glyphs) throws IOException {
- TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("glyf");
- Map newComposites = null;
- Map allComposites = new java.util.HashMap();
-
- int newIndex = glyphs.size();
-
- if (entry != null) {
- while (newComposites == null || newComposites.size() > 0) {
- // Inefficient to iterate through all glyphs
- newComposites = new java.util.HashMap();
-
- Iterator e = glyphs.keySet().iterator();
- while (e.hasNext()) {
- Integer origIndex = (Integer)e.next();
-
- if (in.readTTFShort(entry.getOffset()
- + mtxTab[origIndex.intValue()].getOffset()) < 0) {
- // origIndex is a composite glyph
- allComposites.put(origIndex, glyphs.get(origIndex));
- List composites =
- getIncludedGlyphs(in, (int)entry.getOffset(),
- origIndex);
-
- // Iterate through all composites pointed to
- // by this composite and check if they exists
- // in the glyphs map, add them if not.
- Iterator cps = composites.iterator();
- while (cps.hasNext()) {
- Integer cIdx = (Integer)cps.next();
- if (glyphs.get(cIdx) == null
- && newComposites.get(cIdx) == null) {
- newComposites.put(cIdx,
- new Integer(newIndex));
- newIndex++;
- }
- }
- }
- }
-
- // Add composites to glyphs
- Iterator m = newComposites.keySet().iterator();
- while (m.hasNext()) {
- Integer im = (Integer)m.next();
- glyphs.put(im, newComposites.get(im));
- }
- }
-
- // Iterate through all composites to remap their composite index
- Iterator ce = allComposites.keySet().iterator();
- while (ce.hasNext()) {
- remapComposite(in, glyphs, (int)entry.getOffset(),
- (Integer)ce.next());
- }
-
- } else {
- throw new IOException("Can't find glyf table");
- }
- }
-
-
-
- /**
- * Returns a subset of the original font.
- *
- * @param in FontFileReader to read from
- * @param name Name to be checked for in the font file
- * @param glyphs Map of glyphs (glyphs has old index as (Integer) key and
- * new index as (Integer) value)
- * @return A subset of the original font
- * @throws IOException in case of an I/O problem
- */
- public byte[] readFont(FontFileReader in, String name,
- Map glyphs) throws IOException {
-
- //Check if TrueType collection, and that the name exists in the collection
- if (!checkTTC(in, name)) {
- throw new IOException("Failed to read font");
- }
-
- output = new byte[in.getFileSize()];
-
- readDirTabs(in);
- readFontHeader(in);
- getNumGlyphs(in);
- readHorizontalHeader(in);
- readHorizontalMetrics(in);
- readIndexToLocation(in);
-
- scanGlyphs(in, glyphs);
-
- createDirectory(); // Create the TrueType header and directory
-
- createHead(in);
- createHhea(in, glyphs.size()); // Create the hhea table
- createHmtx(in, glyphs); // Create hmtx table
- createMaxp(in, glyphs.size()); // copy the maxp table
-
- try {
- createCvt(in); // copy the cvt table
- } catch (IOException ex) {
- // Cvt is optional (only required for OpenType (MS) fonts)
- //log.error("TrueType warning: " + ex.getMessage());
- }
-
- try {
- createFpgm(in); // copy fpgm table
- } catch (IOException ex) {
- // Fpgm is optional (only required for OpenType (MS) fonts)
- //log.error("TrueType warning: " + ex.getMessage());
- }
-
- try {
- createPrep(in); // copy prep table
- } catch (IOException ex) {
- // Prep is optional (only required for OpenType (MS) fonts)
- //log.error("TrueType warning: " + ex.getMessage());
- }
-
- try {
- createLoca(glyphs.size()); // create empty loca table
- } catch (IOException ex) {
- // Loca is optional (only required for OpenType (MS) fonts)
- //log.error("TrueType warning: " + ex.getMessage());
- }
-
- try {
- createGlyf(in, glyphs);
- } catch (IOException ex) {
- // Glyf is optional (only required for OpenType (MS) fonts)
- //log.error("TrueType warning: " + ex.getMessage());
- }
-
- pad4();
- createCheckSumAdjustment();
-
- byte[] ret = new byte[realSize];
- System.arraycopy(output, 0, ret, 0, realSize);
-
- return ret;
- }
-
- /**
- * writes a ISO-8859-1 string at the currentPosition
- * updates currentPosition but not realSize
- * @return number of bytes written
- */
- private int writeString(String str) {
- int length = 0;
- try {
- byte[] buf = str.getBytes("ISO-8859-1");
- System.arraycopy(buf, 0, output, currentPos, buf.length);
- length = buf.length;
- currentPos += length;
- } catch (java.io.UnsupportedEncodingException e) {
- // This should never happen!
- }
-
- return length;
- }
-
- /**
- * Appends a byte to the output array,
- * updates currentPost but not realSize
- */
- private void writeByte(byte b) {
- output[currentPos++] = b;
- }
-
- /**
- * Appends a USHORT to the output array,
- * updates currentPost but not realSize
- */
- private void writeUShort(int s) {
- byte b1 = (byte)((s >> 8) & 0xff);
- byte b2 = (byte)(s & 0xff);
- writeByte(b1);
- writeByte(b2);
- }
-
- /**
- * Appends a USHORT to the output array,
- * at the given position without changing currentPos
- */
- private void writeUShort(int pos, int s) {
- byte b1 = (byte)((s >> 8) & 0xff);
- byte b2 = (byte)(s & 0xff);
- output[pos] = b1;
- output[pos + 1] = b2;
- }
-
- /**
- * Appends a ULONG to the output array,
- * updates currentPos but not realSize
- */
- private void writeULong(int s) {
- byte b1 = (byte)((s >> 24) & 0xff);
- byte b2 = (byte)((s >> 16) & 0xff);
- byte b3 = (byte)((s >> 8) & 0xff);
- byte b4 = (byte)(s & 0xff);
- writeByte(b1);
- writeByte(b2);
- writeByte(b3);
- writeByte(b4);
- }
-
- /**
- * Appends a ULONG to the output array,
- * at the given position without changing currentPos
- */
- private void writeULong(int pos, int s) {
- byte b1 = (byte)((s >> 24) & 0xff);
- byte b2 = (byte)((s >> 16) & 0xff);
- byte b3 = (byte)((s >> 8) & 0xff);
- byte b4 = (byte)(s & 0xff);
- output[pos] = b1;
- output[pos + 1] = b2;
- output[pos + 2] = b3;
- output[pos + 3] = b4;
- }
-
- /**
- * Read a signed short value at given position
- */
- private short readShort(int pos) {
- int ret = readUShort(pos);
- return (short)ret;
- }
-
- /**
- * Read a unsigned short value at given position
- */
- private int readUShort(int pos) {
- int ret = (int)output[pos];
- if (ret < 0) {
- ret += 256;
- }
- ret = ret << 8;
- if ((int)output[pos + 1] < 0) {
- ret |= (int)output[pos + 1] + 256;
- } else {
- ret |= (int)output[pos + 1];
- }
-
- return ret;
- }
-
- /**
- * Create a padding in the fontfile to align
- * on a 4-byte boundary
- */
- private void pad4() {
- int padSize = currentPos % 4;
- for (int i = 0; i < padSize; i++) {
- output[currentPos++] = 0;
- realSize++;
- }
- }
-
- /**
- * Returns the maximum power of 2 <= max
- */
- private int maxPow2(int max) {
- int i = 0;
- while (Math.pow(2, (double)i) < max) {
- i++;
- }
-
- return (i - 1);
- }
-
- private int log2(int num) {
- return (int)(Math.log((double)num) / Math.log(2));
- }
-
-
- private int getCheckSum(int start, int size) {
- return (int)getLongCheckSum(start, size);
- }
-
- private long getLongCheckSum(int start, int size) {
- // All the tables here are aligned on four byte boundaries
- // Add remainder to size if it's not a multiple of 4
- int remainder = size % 4;
- if (remainder != 0) {
- size += remainder;
- }
-
- long sum = 0;
-
- for (int i = 0; i < size; i += 4) {
- int l = (int)(output[start + i] << 24);
- l += (int)(output[start + i + 1] << 16);
- l += (int)(output[start + i + 2] << 16);
- l += (int)(output[start + i + 3] << 16);
- sum += l;
- if (sum > 0xffffffff) {
- sum = sum - 0xffffffff;
- }
- }
-
- return sum;
- }
-
- private void createCheckSumAdjustment() {
- long sum = getLongCheckSum(0, realSize);
- int checksum = (int)(0xb1b0afba - sum);
- writeULong(checkSumAdjustmentOffset, checksum);
- }
-
-}
-
-
-
import org.apache.avalon.framework.logger.Logger;
//FOP
-import org.apache.fop.fonts.FontFileReader;
-import org.apache.fop.fonts.TTFCmapEntry;
-import org.apache.fop.fonts.TTFFile;
+import org.apache.fop.fonts.truetype.FontFileReader;
+import org.apache.fop.fonts.truetype.TTFCmapEntry;
+import org.apache.fop.fonts.truetype.TTFFile;
/**
* A tool which reads TTF files and generates
--- /dev/null
+<HTML>
+<TITLE>org.apache.fop.fonts Package</TITLE>
+<BODY>
+<P>Classes for font handling. Subpackages contain command line applications for font metrics generation, font parsing classes etc.</P>
+</BODY>
+</HTML>
\ No newline at end of file
--- /dev/null
+/*
+ * $Id$
+ * Copyright (C) 2001-2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts.truetype;
+
+import java.io.InputStream;
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.fop.util.StreamUtilities;
+
+/**
+ * Reads a TrueType font file into a byte array and
+ * provides file like functions for array access.
+ */
+public class FontFileReader {
+
+ private int fsize; // file size
+ private int current; // current position in file
+ private byte[] file;
+
+ /**
+ * Initializes class and reads stream. Init does not close stream.
+ *
+ * @param in InputStream to read from new array with size + inc
+ * @throws IOException In case of an I/O problem
+ */
+ private void init(InputStream in) throws java.io.IOException {
+ java.io.ByteArrayOutputStream bout = new java.io.ByteArrayOutputStream();
+ try {
+ StreamUtilities.streamCopy(in, bout);
+ this.file = bout.toByteArray();
+ this.fsize = this.file.length;
+ this.current = 0;
+ } finally {
+ bout.close();
+ }
+ }
+
+ /**
+ * Constructor
+ *
+ * @param fileName filename to read
+ * @throws IOException In case of an I/O problem
+ */
+ public FontFileReader(String fileName) throws IOException {
+ final File f = new File(fileName);
+ InputStream in = new java.io.FileInputStream(f);
+ try {
+ init(in);
+ } finally {
+ in.close();
+ }
+ }
+
+
+ /**
+ * Constructor
+ *
+ * @param in InputStream to read from
+ * @throws IOException In case of an I/O problem
+ */
+ public FontFileReader(InputStream in) throws IOException {
+ init(in);
+ }
+
+
+ /**
+ * Set current file position to offset
+ *
+ * @param offset The new offset to set
+ * @throws IOException In case of an I/O problem
+ */
+ public void seekSet(long offset) throws IOException {
+ if (offset > fsize || offset < 0) {
+ throw new java.io.EOFException("Reached EOF, file size=" + fsize
+ + " offset=" + offset);
+ }
+ current = (int)offset;
+ }
+
+ /**
+ * Set current file position to offset
+ *
+ * @param add The number of bytes to advance
+ * @throws IOException In case of an I/O problem
+ */
+ public void seekAdd(long add) throws IOException {
+ seekSet(current + add);
+ }
+
+ /**
+ * Skip a given number of bytes.
+ *
+ * @param add The number of bytes to advance
+ * @throws IOException In case of an I/O problem
+ */
+ public void skip(long add) throws IOException {
+ seekAdd(add);
+ }
+
+ /**
+ * Returns current file position.
+ *
+ * @return int The current position.
+ */
+ public int getCurrentPos() {
+ return current;
+ }
+
+ /**
+ * Returns the size of the file.
+ *
+ * @return int The filesize
+ */
+ public int getFileSize() {
+ return fsize;
+ }
+
+ /**
+ * Read 1 byte.
+ *
+ * @return One byte
+ * @throws IOException If EOF is reached
+ */
+ public byte read() throws IOException {
+ if (current > fsize) {
+ throw new java.io.EOFException("Reached EOF, file size=" + fsize);
+ }
+
+ final byte ret = file[current++];
+ return ret;
+ }
+
+ /**
+ * Read 1 signed byte.
+ *
+ * @return One byte
+ * @throws IOException If EOF is reached
+ */
+ public final byte readTTFByte() throws IOException {
+ return read();
+ }
+
+ /**
+ * Read 1 unsigned byte.
+ *
+ * @return One unsigned byte
+ * @throws IOException If EOF is reached
+ */
+ public final int readTTFUByte() throws IOException {
+ final byte buf = read();
+
+ if (buf < 0) {
+ return (int)(256 + buf);
+ } else {
+ return (int)buf;
+ }
+ }
+
+ /**
+ * Read 2 bytes signed.
+ *
+ * @return One signed short
+ * @throws IOException If EOF is reached
+ */
+ public final short readTTFShort() throws IOException {
+ final int ret = (readTTFUByte() << 8) + readTTFUByte();
+ final short sret = (short)ret;
+ return sret;
+ }
+
+ /**
+ * Read 2 bytes unsigned.
+ *
+ * @return One unsigned short
+ * @throws IOException If EOF is reached
+ */
+ public final int readTTFUShort() throws IOException {
+ final int ret = (readTTFUByte() << 8) + readTTFUByte();
+ return (int)ret;
+ }
+
+ /**
+ * Write a USHort at a given position.
+ *
+ * @param pos The absolute position to write to
+ * @param val The value to write
+ * @throws IOException If EOF is reached
+ */
+ public final void writeTTFUShort(int pos, int val) throws IOException {
+ if ((pos + 2) > fsize) {
+ throw new java.io.EOFException("Reached EOF");
+ }
+ final byte b1 = (byte)((val >> 8) & 0xff);
+ final byte b2 = (byte)(val & 0xff);
+ file[pos] = b1;
+ file[pos + 1] = b2;
+ }
+
+ /**
+ * Read 2 bytes signed at position pos without changing current position.
+ *
+ * @param pos The absolute position to read from
+ * @return One signed short
+ * @throws IOException If EOF is reached
+ */
+ public final short readTTFShort(long pos) throws IOException {
+ final long cp = getCurrentPos();
+ seekSet(pos);
+ final short ret = readTTFShort();
+ seekSet(cp);
+ return ret;
+ }
+
+ /**
+ * Read 2 bytes unsigned at position pos without changing current position.
+ *
+ * @param pos The absolute position to read from
+ * @return One unsigned short
+ * @throws IOException If EOF is reached
+ */
+ public final int readTTFUShort(long pos) throws IOException {
+ long cp = getCurrentPos();
+ seekSet(pos);
+ int ret = readTTFUShort();
+ seekSet(cp);
+ return ret;
+ }
+
+ /**
+ * Read 4 bytes.
+ *
+ * @return One signed integer
+ * @throws IOException If EOF is reached
+ */
+ public final int readTTFLong() throws IOException {
+ long ret = readTTFUByte(); // << 8;
+ ret = (ret << 8) + readTTFUByte();
+ ret = (ret << 8) + readTTFUByte();
+ ret = (ret << 8) + readTTFUByte();
+
+ return (int)ret;
+ }
+
+ /**
+ * Read 4 bytes.
+ *
+ * @return One unsigned integer
+ * @throws IOException If EOF is reached
+ */
+ public final long readTTFULong() throws IOException {
+ long ret = readTTFUByte();
+ ret = (ret << 8) + readTTFUByte();
+ ret = (ret << 8) + readTTFUByte();
+ ret = (ret << 8) + readTTFUByte();
+
+ return ret;
+ }
+
+ /**
+ * Read a NUL terminated ISO-8859-1 string.
+ *
+ * @return A String
+ * @throws IOException If EOF is reached
+ */
+ public final String readTTFString() throws IOException {
+ int i = current;
+ while (file[i++] != 0) {
+ if (i > fsize) {
+ throw new java.io.EOFException("Reached EOF, file size="
+ + fsize);
+ }
+ }
+
+ byte[] tmp = new byte[i - current];
+ System.arraycopy(file, current, tmp, 0, i - current);
+ return new String(tmp, "ISO-8859-1");
+ }
+
+
+ /**
+ * Read an ISO-8859-1 string of len bytes.
+ *
+ * @param len The length of the string to read
+ * @return A String
+ * @throws IOException If EOF is reached
+ */
+ public final String readTTFString(int len) throws IOException {
+ if ((len + current) > fsize) {
+ throw new java.io.EOFException("Reached EOF, file size=" + fsize);
+ }
+
+ byte[] tmp = new byte[len];
+ System.arraycopy(file, current, tmp, 0, len);
+ current += len;
+ return new String(tmp, "ISO-8859-1");
+ }
+
+ /**
+ * Return a copy of the internal array
+ *
+ * @param offset The absolute offset to start reading from
+ * @param length The number of bytes to read
+ * @return An array of bytes
+ * @throws IOException if out of bounds
+ */
+ public byte[] getBytes(int offset,
+ int length) throws IOException {
+ if ((offset + length) > fsize) {
+ throw new java.io.IOException("Reached EOF");
+ }
+
+ byte[] ret = new byte[length];
+ System.arraycopy(file, offset, ret, 0, length);
+ return ret;
+ }
+
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * $Id$
+ * Copyright (C) 2001-2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts.truetype;
+
+/**
+ * The CMap entry contains information of a Unicode range and the
+ * the glyph indexes related to the range
+ */
+public class TTFCmapEntry {
+
+ private int unicodeStart;
+ private int unicodeEnd;
+ private int glyphStartIndex;
+
+ TTFCmapEntry() {
+ unicodeStart = 0;
+ unicodeEnd = 0;
+ glyphStartIndex = 0;
+ }
+
+ TTFCmapEntry(int unicodeStart, int unicodeEnd, int glyphStartIndex) {
+ this.unicodeStart = unicodeStart;
+ this.unicodeEnd = unicodeEnd;
+ this.glyphStartIndex = glyphStartIndex;
+ }
+
+ /**
+ * @see java.lang.Object#equals(Object)
+ */
+ public boolean equals(Object o) {
+ if (o instanceof TTFCmapEntry) {
+ TTFCmapEntry ce = (TTFCmapEntry)o;
+ if (ce.unicodeStart == this.unicodeStart
+ && ce.unicodeEnd == this.unicodeEnd
+ && ce.glyphStartIndex == this.glyphStartIndex) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the glyphStartIndex.
+ * @return int
+ */
+ public int getGlyphStartIndex() {
+ return glyphStartIndex;
+ }
+
+ /**
+ * Returns the unicodeEnd.
+ * @return int
+ */
+ public int getUnicodeEnd() {
+ return unicodeEnd;
+ }
+
+ /**
+ * Returns the unicodeStart.
+ * @return int
+ */
+ public int getUnicodeStart() {
+ return unicodeStart;
+ }
+
+ /**
+ * Sets the glyphStartIndex.
+ * @param glyphStartIndex The glyphStartIndex to set
+ */
+ public void setGlyphStartIndex(int glyphStartIndex) {
+ this.glyphStartIndex = glyphStartIndex;
+ }
+
+ /**
+ * Sets the unicodeEnd.
+ * @param unicodeEnd The unicodeEnd to set
+ */
+ public void setUnicodeEnd(int unicodeEnd) {
+ this.unicodeEnd = unicodeEnd;
+ }
+
+ /**
+ * Sets the unicodeStart.
+ * @param unicodeStart The unicodeStart to set
+ */
+ public void setUnicodeStart(int unicodeStart) {
+ this.unicodeStart = unicodeStart;
+ }
+
+}
--- /dev/null
+/*
+ * $Id$
+ * Copyright (C) 2001-2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts.truetype;
+
+import java.io.IOException;
+
+
+/**
+ * This class represents an entry to a TrueType font's Dir Tab.
+ */
+class TTFDirTabEntry {
+
+ private byte[] tag = new byte[4];
+ private int checksum;
+ private long offset;
+ private long length;
+
+ /**
+ * Read Dir Tab, return tag name
+ */
+ public String read(FontFileReader in) throws IOException {
+ tag[0] = in.readTTFByte();
+ tag[1] = in.readTTFByte();
+ tag[2] = in.readTTFByte();
+ tag[3] = in.readTTFByte();
+
+ in.skip(4); // Skip checksum
+
+ offset = in.readTTFULong();
+ length = in.readTTFULong();
+
+ //System.out.println(this.toString());
+ return new String(tag, "ISO-8859-1");
+ }
+
+
+ public String toString() {
+ return "Read dir tab ["
+ + tag[0] + " " + tag[1] + " " + tag[2] + " " + tag[3] + "]"
+ + " offset: " + offset
+ + " length: " + length
+ + " name: " + tag;
+ }
+
+ /**
+ * Returns the checksum.
+ * @return int
+ */
+ public int getChecksum() {
+ return checksum;
+ }
+
+ /**
+ * Returns the length.
+ * @return long
+ */
+ public long getLength() {
+ return length;
+ }
+
+ /**
+ * Returns the offset.
+ * @return long
+ */
+ public long getOffset() {
+ return offset;
+ }
+
+ /**
+ * Returns the tag.
+ * @return byte[]
+ */
+ public byte[] getTag() {
+ return tag;
+ }
+
+}
--- /dev/null
+/*
+ * $Id$
+ * Copyright (C) 2001-2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts.truetype;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.List;
+
+import org.apache.avalon.framework.logger.AbstractLogEnabled;
+import org.apache.avalon.framework.logger.ConsoleLogger;
+import org.apache.avalon.framework.logger.Logger;
+import org.apache.fop.fonts.Glyphs;
+
+/**
+ * Reads a TrueType file or a TrueType Collection.
+ * The TrueType spec can be found at the Microsoft.
+ * Typography site: http://www.microsoft.com/truetype/
+ */
+public class TTFFile extends AbstractLogEnabled {
+
+ static final byte NTABS = 24;
+ static final int NMACGLYPHS = 258;
+ static final int MAX_CHAR_CODE = 255;
+ static final int ENC_BUF_SIZE = 1024;
+
+ private String encoding = "WinAnsiEncoding"; // Default encoding
+
+ private short firstChar = 0;
+ private boolean isEmbeddable = true;
+ private boolean hasSerifs = true;
+ /**
+ * Table directory
+ */
+ protected Map dirTabs;
+ private Map kerningTab; // for CIDs
+ private Map ansiKerningTab; // For winAnsiEncoding
+ private List cmaps;
+ private List unicodeMapping;
+
+ private int upem; // unitsPerEm from "head" table
+ private int nhmtx; // Number of horizontal metrics
+ private int postFormat;
+ private int locaFormat;
+ /**
+ * Offset to last loca
+ */
+ protected long lastLoca = 0;
+ private int numberOfGlyphs; // Number of glyphs in font (read from "maxp" table)
+ private int nmGlyphs; // Used in fixWidths - remove?
+
+ /**
+ * Contains glyph data
+ */
+ protected TTFMtxEntry mtxTab[]; // Contains glyph data
+ private int[] mtxEncoded = null;
+
+ private String fontName = "";
+ private String fullName = "";
+ private String notice = "";
+ private String familyName = "";
+ private String subFamilyName = "";
+
+ private long italicAngle = 0;
+ private long isFixedPitch = 0;
+ private int fontBBox1 = 0;
+ private int fontBBox2 = 0;
+ private int fontBBox3 = 0;
+ private int fontBBox4 = 0;
+ private int capHeight = 0;
+ private int underlinePosition = 0;
+ private int underlineThickness = 0;
+ private int xHeight = 0;
+ private int ascender = 0;
+ private int descender = 0;
+
+ private short lastChar = 0;
+
+ private int ansiWidth[];
+ private Map ansiIndex;
+
+ /**
+ * Position inputstream to position indicated
+ * in the dirtab offset + offset
+ */
+ void seekTab(FontFileReader in, String name,
+ long offset) throws IOException {
+ TTFDirTabEntry dt = (TTFDirTabEntry)dirTabs.get(name);
+ if (dt == null) {
+ getLogger().error("Dirtab " + name + " not found.");
+ return;
+ }
+
+ in.seekSet(dt.getOffset() + offset);
+ }
+
+ /**
+ * Convert from truetype unit to pdf unit based on the
+ * unitsPerEm field in the "head" table
+ * @param n truetype unit
+ * @return pdf unit
+ */
+ public int convertTTFUnit2PDFUnit(int n) {
+ int ret;
+ if (n < 0) {
+ long rest1 = n % upem;
+ long storrest = 1000 * rest1;
+ long ledd2 = rest1 / storrest;
+ ret = -((-1000 * n) / upem - (int)ledd2);
+ } else {
+ ret = (n / upem) * 1000 + ((n % upem) * 1000) / upem;
+ }
+
+ return ret;
+ }
+
+ /**
+ * Read the cmap table,
+ * return false if the table is not present or only unsupported
+ * tables are present. Currently only unicode cmaps are supported.
+ * Set the unicodeIndex in the TTFMtxEntries and fills in the
+ * cmaps vector.
+ */
+ private boolean readCMAP(FontFileReader in) throws IOException {
+
+ unicodeMapping = new java.util.ArrayList();
+
+ //Read CMAP table and correct mtxTab.index
+ int mtxPtr = 0;
+
+ seekTab(in, "cmap", 2);
+ int numCMap = in.readTTFUShort(); // Number of cmap subtables
+ long cmapUniOffset = 0;
+
+ getLogger().info(numCMap + " cmap tables");
+
+ //Read offset for all tables. We are only interested in the unicode table
+ for (int i = 0; i < numCMap; i++) {
+ int cmapPID = in.readTTFUShort();
+ int cmapEID = in.readTTFUShort();
+ long cmapOffset = in.readTTFULong();
+
+ getLogger().debug("Platform ID: " + cmapPID
+ + " Encoding: " + cmapEID);
+
+ if (cmapPID == 3 && cmapEID == 1) {
+ cmapUniOffset = cmapOffset;
+ }
+ }
+
+ if (cmapUniOffset <= 0) {
+ getLogger().fatalError("Unicode cmap table not present");
+ getLogger().fatalError("Unsupported format: Aborting");
+ return false;
+ }
+
+ // Read unicode cmap
+ seekTab(in, "cmap", cmapUniOffset);
+ int cmapFormat = in.readTTFUShort();
+ /*int cmap_length =*/ in.readTTFUShort(); //skip cmap length
+
+ getLogger().info("CMAP format: " + cmapFormat);
+ if (cmapFormat == 4) {
+ in.skip(2); // Skip version number
+ int cmapSegCountX2 = in.readTTFUShort();
+ int cmapSearchRange = in.readTTFUShort();
+ int cmapEntrySelector = in.readTTFUShort();
+ int cmapRangeShift = in.readTTFUShort();
+
+ getLogger().debug("segCountX2 : " + cmapSegCountX2);
+ getLogger().debug("searchRange : " + cmapSearchRange);
+ getLogger().debug("entrySelector: " + cmapEntrySelector);
+ getLogger().debug("rangeShift : " + cmapRangeShift);
+
+
+ int cmapEndCounts[] = new int[cmapSegCountX2 / 2];
+ int cmapStartCounts[] = new int[cmapSegCountX2 / 2];
+ int cmapDeltas[] = new int[cmapSegCountX2 / 2];
+ int cmapRangeOffsets[] = new int[cmapSegCountX2 / 2];
+
+ for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
+ cmapEndCounts[i] = in.readTTFUShort();
+ }
+
+ in.skip(2); // Skip reservedPad
+
+ for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
+ cmapStartCounts[i] = in.readTTFUShort();
+ }
+
+ for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
+ cmapDeltas[i] = in.readTTFShort();
+ }
+
+ //int startRangeOffset = in.getCurrentPos();
+
+ for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
+ cmapRangeOffsets[i] = in.readTTFUShort();
+ }
+
+ int glyphIdArrayOffset = in.getCurrentPos();
+
+ // Insert the unicode id for the glyphs in mtxTab
+ // and fill in the cmaps ArrayList
+
+ for (int i = 0; i < cmapStartCounts.length; i++) {
+
+ getLogger().debug(i + ": " + cmapStartCounts[i]
+ + " - " + cmapEndCounts[i]);
+
+ for (int j = cmapStartCounts[i]; j <= cmapEndCounts[i]; j++) {
+
+ // Update lastChar
+ if (j < 256 && j > lastChar) {
+ lastChar = (short)j;
+ }
+
+ if (mtxPtr < mtxTab.length) {
+ int glyphIdx;
+ // the last character 65535 = .notdef
+ // may have a range offset
+ if (cmapRangeOffsets[i] != 0 && j != 65535) {
+ int glyphOffset = glyphIdArrayOffset
+ + ((cmapRangeOffsets[i] / 2)
+ + (j - cmapStartCounts[i])
+ + (i)
+ - cmapSegCountX2 / 2) * 2;
+ in.seekSet(glyphOffset);
+ glyphIdx = (in.readTTFUShort() + cmapDeltas[i])
+ & 0xffff;
+
+ unicodeMapping.add(new UnicodeMapping(glyphIdx, j));
+ mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(j));
+
+
+ // Also add winAnsiWidth
+ List v = (List)ansiIndex.get(new Integer(j));
+ if (v != null) {
+ Iterator e = v.listIterator();
+ while (e.hasNext()) {
+ Integer aIdx = (Integer)e.next();
+ ansiWidth[aIdx.intValue()] =
+ mtxTab[glyphIdx].getWx();
+
+ getLogger().debug("Added width "
+ + mtxTab[glyphIdx].getWx()
+ + " uni: " + j
+ + " ansi: " + aIdx.intValue());
+ }
+ }
+
+ getLogger().debug("Idx: "
+ + glyphIdx
+ + " Delta: " + cmapDeltas[i]
+ + " Unicode: " + j
+ + " name: " + mtxTab[glyphIdx].getName());
+ } else {
+ glyphIdx = (j + cmapDeltas[i]) & 0xffff;
+
+ if (glyphIdx < mtxTab.length) {
+ mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(j));
+ } else {
+ getLogger().debug("Glyph " + glyphIdx
+ + " out of range: "
+ + mtxTab.length);
+ }
+
+ unicodeMapping.add(new UnicodeMapping(glyphIdx, j));
+ if (glyphIdx < mtxTab.length) {
+ mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(j));
+ } else {
+ getLogger().debug("Glyph " + glyphIdx
+ + " out of range: "
+ + mtxTab.length);
+ }
+
+ // Also add winAnsiWidth
+ List v = (List)ansiIndex.get(new Integer(j));
+ if (v != null) {
+ Iterator e = v.listIterator();
+ while (e.hasNext()) {
+ Integer aIdx = (Integer)e.next();
+ ansiWidth[aIdx.intValue()] = mtxTab[glyphIdx].getWx();
+ }
+ }
+
+ //getLogger().debug("IIdx: " +
+ // mtxPtr +
+ // " Delta: " + cmap_deltas[i] +
+ // " Unicode: " + j +
+ // " name: " +
+ // mtxTab[(j+cmap_deltas[i]) & 0xffff].name);
+
+ }
+ if (glyphIdx < mtxTab.length) {
+ if (mtxTab[glyphIdx].getUnicodeIndex().size() < 2) {
+ mtxPtr++;
+ }
+ }
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Print first char/last char
+ */
+ private void printMaxMin() {
+ int min = 255;
+ int max = 0;
+ for (int i = 0; i < mtxTab.length; i++) {
+ if (mtxTab[i].getIndex() < min) {
+ min = mtxTab[i].getIndex();
+ }
+ if (mtxTab[i].getIndex() > max) {
+ max = mtxTab[i].getIndex();
+ }
+ }
+ getLogger().info("Min: " + min);
+ getLogger().info("Max: " + max);
+ }
+
+
+ /**
+ * Reads the font using a FontFileReader.
+ *
+ * @param in The FontFileReader to use
+ * @throws IOException In case of an I/O problem
+ */
+ public void readFont(FontFileReader in) throws IOException {
+ readFont(in, (String)null);
+ }
+
+ /**
+ * initialize the ansiWidths array (for winAnsiEncoding)
+ * and fill with the missingwidth
+ */
+ private void initAnsiWidths() {
+ ansiWidth = new int[256];
+ for (int i = 0; i < 256; i++) {
+ ansiWidth[i] = mtxTab[0].getWx();
+ }
+
+ // Create an index hash to the ansiWidth
+ // Can't just index the winAnsiEncoding when inserting widths
+ // same char (eg bullet) is repeated more than one place
+ ansiIndex = new java.util.HashMap();
+ for (int i = 32; i < Glyphs.WINANSI_ENCODING.length; i++) {
+ Integer ansi = new Integer(i);
+ Integer uni = new Integer((int)Glyphs.WINANSI_ENCODING[i]);
+
+ List v = (List)ansiIndex.get(uni);
+ if (v == null) {
+ v = new java.util.ArrayList();
+ ansiIndex.put(uni, v);
+ }
+ v.add(ansi);
+ }
+ }
+
+ /**
+ * Read the font data.
+ * If the fontfile is a TrueType Collection (.ttc file)
+ * the name of the font to read data for must be supplied,
+ * else the name is ignored.
+ *
+ * @param in The FontFileReader to use
+ * @param name The name of the font
+ * @return boolean Returns true if the font is valid
+ * @throws IOException In case of an I/O problem
+ */
+ public boolean readFont(FontFileReader in, String name) throws IOException {
+
+ /*
+ * Check if TrueType collection, and that the name
+ * exists in the collection
+ */
+ if (!checkTTC(in, name)) {
+ throw new IOException("Failed to read font");
+ }
+
+ readDirTabs(in);
+ readFontHeader(in);
+ getNumGlyphs(in);
+ getLogger().info("Number of glyphs in font: " + numberOfGlyphs);
+ readHorizontalHeader(in);
+ readHorizontalMetrics(in);
+ initAnsiWidths();
+ readPostscript(in);
+ readOS2(in);
+ readIndexToLocation(in);
+ readGlyf(in);
+ readName(in);
+ readPCLT(in);
+ // Read cmap table and fill in ansiwidths
+ boolean valid = readCMAP(in);
+ if (!valid) {
+ return false;
+ }
+ // Create cmaps for bfentries
+ createCMaps();
+ // print_max_min();
+
+ readKerning(in);
+ return true;
+ }
+
+ private void createCMaps() {
+ cmaps = new java.util.ArrayList();
+ TTFCmapEntry tce = new TTFCmapEntry();
+
+ Iterator e = unicodeMapping.listIterator();
+ UnicodeMapping um = (UnicodeMapping)e.next();
+ UnicodeMapping lastMapping = um;
+
+ tce.setUnicodeStart(um.getUIdx());
+ tce.setGlyphStartIndex(um.getGIdx());
+
+ while (e.hasNext()) {
+ um = (UnicodeMapping)e.next();
+ if (((lastMapping.getUIdx() + 1) != um.getUIdx())
+ || ((lastMapping.getGIdx() + 1) != um.getGIdx())) {
+ tce.setUnicodeEnd(lastMapping.getUIdx());
+ cmaps.add(tce);
+
+ tce = new TTFCmapEntry();
+ tce.setUnicodeStart(um.getUIdx());
+ tce.setGlyphStartIndex(um.getGIdx());
+ }
+ lastMapping = um;
+ }
+
+ tce.setUnicodeEnd(um.getUIdx());
+ cmaps.add(tce);
+ }
+
+ /**
+ * Returns the Windows name of the font.
+ * @return String The Windows name
+ */
+ public String getWindowsName() {
+ return familyName + "," + subFamilyName;
+ }
+
+ /**
+ * Returns the PostScript name of the font.
+ * @return String The PostScript name
+ */
+ public String getPostscriptName() {
+ if ("Regular".equals(subFamilyName) || "Roman".equals(subFamilyName)) {
+ return familyName;
+ } else {
+ return familyName + "," + subFamilyName;
+ }
+ }
+
+ /**
+ * Returns the font family name of the font.
+ * @return String The family name
+ */
+ public String getFamilyName() {
+ return familyName;
+ }
+
+ /**
+ * Returns the name of the character set used.
+ * @return String The caracter set
+ */
+ public String getCharSetName() {
+ return encoding;
+ }
+
+ /**
+ * Returns the CapHeight attribute of the font.
+ * @return int The CapHeight
+ */
+ public int getCapHeight() {
+ return (int)convertTTFUnit2PDFUnit(capHeight);
+ }
+
+ /**
+ * Returns the XHeight attribute of the font.
+ * @return int The XHeight
+ */
+ public int getXHeight() {
+ return (int)convertTTFUnit2PDFUnit(xHeight);
+ }
+
+ /**
+ * Returns the Flags attribute of the font.
+ * @return int The Flags
+ */
+ public int getFlags() {
+ int flags = 32; // Use Adobe Standard charset
+ if (italicAngle != 0) {
+ flags = flags | 64;
+ }
+ if (isFixedPitch != 0) {
+ flags = flags | 2;
+ }
+ if (hasSerifs) {
+ flags = flags | 1;
+ }
+ return flags;
+ }
+
+
+ /**
+ * Returns the StemV attribute of the font.
+ * @return String The StemV
+ */
+ public String getStemV() {
+ return "0";
+ }
+
+ /**
+ * Returns the ItalicAngle attribute of the font.
+ * @return String The ItalicAngle
+ */
+ public String getItalicAngle() {
+ String ia = Short.toString((short)(italicAngle / 0x10000));
+
+ // This is the correct italic angle, however only int italic
+ // angles are supported at the moment so this is commented out.
+ /*
+ * if ((italicAngle % 0x10000) > 0 )
+ * ia=ia+(comma+Short.toString((short)((short)((italicAngle % 0x10000)*1000)/0x10000)));
+ */
+ return ia;
+ }
+
+ /**
+ * Returns the font bounding box.
+ * @return int[] The font bbox
+ */
+ public int[] getFontBBox() {
+ final int[] fbb = new int[4];
+ fbb[0] = (int)convertTTFUnit2PDFUnit(fontBBox1);
+ fbb[1] = (int)convertTTFUnit2PDFUnit(fontBBox2);
+ fbb[2] = (int)convertTTFUnit2PDFUnit(fontBBox3);
+ fbb[3] = (int)convertTTFUnit2PDFUnit(fontBBox4);
+
+ return fbb;
+ }
+
+ /**
+ * Returns the LowerCaseAscent attribute of the font.
+ * @return int The LowerCaseAscent
+ */
+ public int getLowerCaseAscent() {
+ return (int)convertTTFUnit2PDFUnit(ascender);
+ }
+
+ /**
+ * Returns the LowerCaseDescent attribute of the font.
+ * @return int The LowerCaseDescent
+ */
+ public int getLowerCaseDescent() {
+ return (int)convertTTFUnit2PDFUnit(descender);
+ }
+
+ /**
+ * Returns the index of the last character, but this is for WinAnsiEncoding
+ * only, so the last char is < 256.
+ * @return short Index of the last character (<256)
+ */
+ public short getLastChar() {
+ return lastChar;
+ }
+
+ /**
+ * Returns the index of the first character.
+ * @return short Index of the first character
+ */
+ public short getFirstChar() {
+ return firstChar;
+ }
+
+ /**
+ * Returns an array of character widths.
+ * @return int[] The character widths
+ */
+ public int[] getWidths() {
+ int[] wx = new int[mtxTab.length];
+ for (int i = 0; i < wx.length; i++) {
+ wx[i] = (int)convertTTFUnit2PDFUnit(mtxTab[i].getWx());
+ }
+
+ return wx;
+ }
+
+ /**
+ * Returns the width of a given character.
+ * @param idx Index of the character
+ * @return int Standard width
+ */
+ public int getCharWidth(int idx) {
+ return (int)convertTTFUnit2PDFUnit(ansiWidth[idx]);
+ }
+
+ /**
+ * Returns the kerning table.
+ * @return Map The kerning table
+ */
+ public Map getKerning() {
+ return kerningTab;
+ }
+
+ /**
+ * Returns the ANSI kerning table.
+ * @return Map The ANSI kerning table
+ */
+ public Map getAnsiKerning() {
+ return ansiKerningTab;
+ }
+
+ /**
+ * Indicates if the font may be embedded.
+ * @return boolean True if it may be embedded
+ */
+ public boolean isEmbeddable() {
+ return isEmbeddable;
+ }
+
+
+ /**
+ * Read Table Directory from the current position in the
+ * FontFileReader and fill the global HashMap dirTabs
+ * with the table name (String) as key and a TTFDirTabEntry
+ * as value.
+ * @param in FontFileReader to read the table directory from
+ * @throws IOException in case of an I/O problem
+ */
+ protected void readDirTabs(FontFileReader in) throws IOException {
+ in.skip(4); // TTF_FIXED_SIZE
+ int ntabs = in.readTTFUShort();
+ in.skip(6); // 3xTTF_USHORT_SIZE
+
+ dirTabs = new java.util.HashMap();
+ TTFDirTabEntry[] pd = new TTFDirTabEntry[ntabs];
+ getLogger().debug("Reading " + ntabs + " dir tables");
+ for (int i = 0; i < ntabs; i++) {
+ pd[i] = new TTFDirTabEntry();
+ dirTabs.put(pd[i].read(in), pd[i]);
+ }
+ }
+
+ /**
+ * Read the "head" table, this reads the bounding box and
+ * sets the upem (unitsPerEM) variable
+ * @param in FontFileReader to read the header from
+ * @throws IOException in case of an I/O problem
+ */
+ protected void readFontHeader(FontFileReader in) throws IOException {
+ seekTab(in, "head", 2 * 4 + 2 * 4 + 2);
+ upem = in.readTTFUShort();
+
+ in.skip(16);
+
+ fontBBox1 = in.readTTFShort();
+ fontBBox2 = in.readTTFShort();
+ fontBBox3 = in.readTTFShort();
+ fontBBox4 = in.readTTFShort();
+
+ in.skip(2 + 2 + 2);
+
+ locaFormat = in.readTTFShort();
+ }
+
+ /**
+ * Read the number of glyphs from the "maxp" table
+ * @param in FontFileReader to read the number of glyphs from
+ * @throws IOException in case of an I/O problem
+ */
+ protected void getNumGlyphs(FontFileReader in) throws IOException {
+ seekTab(in, "maxp", 4);
+ numberOfGlyphs = in.readTTFUShort();
+ }
+
+
+ /**
+ * Read the "hhea" table to find the ascender and descender and
+ * size of "hmtx" table, i.e. a fixed size font might have only
+ * one width
+ * @param in FontFileReader to read the hhea table from
+ * @throws IOException in case of an I/O problem
+ */
+ protected void readHorizontalHeader(FontFileReader in)
+ throws IOException {
+ seekTab(in, "hhea", 4);
+ ascender = in.readTTFShort(); // Use sTypoAscender in "OS/2" table?
+ descender = in.readTTFShort(); // Use sTypoDescender in "OS/2" table?
+
+ in.skip(2 + 2 + 3 * 2 + 8 * 2);
+ nhmtx = in.readTTFUShort();
+ getLogger().debug("Number of horizontal metrics: " + nhmtx);
+ }
+
+ /**
+ * Read "hmtx" table and put the horizontal metrics
+ * in the mtxTab array. If the number of metrics is less
+ * than the number of glyphs (eg fixed size fonts), extend
+ * the mtxTab array and fill in the missing widths
+ * @param in FontFileReader to read the hmtx table from
+ * @throws IOException in case of an I/O problem
+ */
+ protected void readHorizontalMetrics(FontFileReader in)
+ throws IOException {
+ seekTab(in, "hmtx", 0);
+
+ int mtxSize = (numberOfGlyphs > nhmtx) ? numberOfGlyphs : nhmtx;
+ mtxTab = new TTFMtxEntry[mtxSize];
+
+ getLogger().debug("*** Widths array: \n");
+ for (int i = 0; i < mtxSize; i++) {
+ mtxTab[i] = new TTFMtxEntry();
+ }
+ for (int i = 0; i < nhmtx; i++) {
+ mtxTab[i].setWx(in.readTTFUShort());
+ mtxTab[i].setLsb(in.readTTFUShort());
+
+ getLogger().debug(" width[" + i + "] = "
+ + convertTTFUnit2PDFUnit(mtxTab[i].getWx()) + ";");
+ }
+
+ if (nhmtx < mtxSize) {
+ // Fill in the missing widths
+ int lastWidth = mtxTab[nhmtx - 1].getWx();
+ for (int i = nhmtx; i < mtxSize; i++) {
+ mtxTab[i].setWx(lastWidth);
+ mtxTab[i].setLsb(in.readTTFUShort());
+ }
+ }
+ }
+
+
+ /**
+ * Read the "post" table
+ * containing the postscript names of the glyphs.
+ */
+ private final void readPostscript(FontFileReader in) throws IOException {
+ String[] psGlyphsBuffer;
+ int i, k, l;
+
+ seekTab(in, "post", 0);
+ postFormat = in.readTTFLong();
+ italicAngle = in.readTTFULong();
+ underlinePosition = in.readTTFShort();
+ underlineThickness = in.readTTFShort();
+ isFixedPitch = in.readTTFULong();
+
+ in.skip(4 * 4);
+
+ getLogger().debug("Post format: " + postFormat);
+ switch (postFormat) {
+ case 0x00010000:
+ getLogger().debug("Postscript format 1");
+ for (i = 0; i < Glyphs.MAC_GLYPH_NAMES.length; i++) {
+ mtxTab[i].setName(Glyphs.MAC_GLYPH_NAMES[i]);
+ }
+ break;
+ case 0x00020000:
+ getLogger().debug("Postscript format 2");
+ int numGlyphStrings = 0;
+ l = in.readTTFUShort(); // Num Glyphs
+ // short minIndex=256;
+ for (i = 0; i < l; i++) { // Read indexes
+ mtxTab[i].setIndex(in.readTTFUShort());
+ // if (minIndex > mtxTab[i].index)
+ // minIndex=(short)mtxTab[i].index;
+
+ if (mtxTab[i].getIndex() > 257) {
+ numGlyphStrings++;
+ }
+
+ getLogger().debug("Post index: " + mtxTab[i].getIndex());
+ }
+ // firstChar=minIndex;
+ psGlyphsBuffer = new String[numGlyphStrings];
+ getLogger().debug("Reading " + numGlyphStrings
+ + " glyphnames" + ", was n num glyphs=" + l);
+ for (i = 0; i < psGlyphsBuffer.length; i++) {
+ psGlyphsBuffer[i] = in.readTTFString(in.readTTFUByte());
+ }
+
+ for (i = 0; i < l; i++) {
+ if (mtxTab[i].getIndex() < NMACGLYPHS) {
+ mtxTab[i].setName(Glyphs.MAC_GLYPH_NAMES[mtxTab[i].getIndex()]);
+ } else {
+ k = mtxTab[i].getIndex() - NMACGLYPHS;
+
+ getLogger().debug(k + " i=" + i + " mtx=" + mtxTab.length
+ + " ps=" + psGlyphsBuffer.length);
+
+ mtxTab[i].setName(psGlyphsBuffer[k]);
+ }
+ }
+
+ break;
+ case 0x00030000:
+ // Postscript format 3 contains no glyph names
+ getLogger().debug("Postscript format 3");
+ break;
+ default:
+ getLogger().error("Unknown Postscript format: " + postFormat);
+ }
+ }
+
+
+ /**
+ * Read the "OS/2" table
+ */
+ private final void readOS2(FontFileReader in) throws IOException {
+ // Check if font is embeddable
+ if (dirTabs.get("OS/2") != null) {
+ seekTab(in, "OS/2", 2 * 4);
+ int fsType = in.readTTFUShort();
+ if (fsType == 2) {
+ isEmbeddable = false;
+ } else {
+ isEmbeddable = true;
+ }
+ } else {
+ isEmbeddable = true;
+ }
+ }
+
+ /**
+ * Read the "loca" table.
+ * @param in FontFileReader to read from
+ * @throws IOException In case of a I/O problem
+ */
+ protected final void readIndexToLocation(FontFileReader in)
+ throws IOException {
+ seekTab(in, "loca", 0);
+ for (int i = 0; i < numberOfGlyphs; i++) {
+ mtxTab[i].setOffset(locaFormat == 1 ? in.readTTFULong()
+ : (in.readTTFUShort() << 1));
+ }
+ lastLoca = (locaFormat == 1 ? in.readTTFULong()
+ : (in.readTTFUShort() << 1));
+ }
+
+ /**
+ * Read the "glyf" table to find the bounding boxes.
+ * @param in FontFileReader to read from
+ * @throws IOException In case of a I/O problem
+ */
+ private final void readGlyf(FontFileReader in) throws IOException {
+ TTFDirTabEntry dirTab = (TTFDirTabEntry)dirTabs.get("glyf");
+ for (int i = 0; i < (numberOfGlyphs - 1); i++) {
+ if (mtxTab[i].getOffset() != mtxTab[i + 1].getOffset()) {
+ in.seekSet(dirTab.getOffset() + mtxTab[i].getOffset());
+ in.skip(2);
+ final int[] bbox = {
+ in.readTTFShort(),
+ in.readTTFShort(),
+ in.readTTFShort(),
+ in.readTTFShort()};
+ mtxTab[i].setBoundingBox(bbox);
+ } else {
+ mtxTab[i].setBoundingBox(mtxTab[0].getBoundingBox());
+ }
+ }
+
+
+ long n = ((TTFDirTabEntry)dirTabs.get("glyf")).getOffset();
+ for (int i = 0; i < numberOfGlyphs; i++) {
+ if ((i + 1) >= mtxTab.length
+ || mtxTab[i].getOffset() != mtxTab[i + 1].getOffset()) {
+ in.seekSet(n + mtxTab[i].getOffset());
+ in.skip(2);
+ final int[] bbox = {
+ in.readTTFShort(),
+ in.readTTFShort(),
+ in.readTTFShort(),
+ in.readTTFShort()};
+ mtxTab[i].setBoundingBox(bbox);
+ } else {
+ /**@todo Verify that this is correct, looks like a copy/paste bug (jm)*/
+ final int bbox0 = mtxTab[0].getBoundingBox()[0];
+ final int[] bbox = {bbox0, bbox0, bbox0, bbox0};
+ mtxTab[i].setBoundingBox(bbox);
+ /* Original code
+ mtxTab[i].bbox[0] = mtxTab[0].bbox[0];
+ mtxTab[i].bbox[1] = mtxTab[0].bbox[0];
+ mtxTab[i].bbox[2] = mtxTab[0].bbox[0];
+ mtxTab[i].bbox[3] = mtxTab[0].bbox[0]; */
+ }
+ getLogger().debug(mtxTab[i].toString(this));
+ }
+ }
+
+ /**
+ * Read the "name" table.
+ * @param in FontFileReader to read from
+ * @throws IOException In case of a I/O problem
+ */
+ private final void readName(FontFileReader in) throws IOException {
+ seekTab(in, "name", 2);
+ int i = in.getCurrentPos();
+ int n = in.readTTFUShort();
+ int j = in.readTTFUShort() + i - 2;
+ i += 2 * 2;
+
+ while (n-- > 0) {
+ // getLogger().debug("Iteration: " + n);
+ in.seekSet(i);
+ final int platformID = in.readTTFUShort();
+ final int encodingID = in.readTTFUShort();
+ /*final int language_id =*/ in.readTTFUShort(); //Skip language id
+
+ int k = in.readTTFUShort();
+ int l = in.readTTFUShort();
+
+ if (((platformID == 1 || platformID == 3) && (encodingID == 0 || encodingID == 1))
+ && (k == 1 || k == 2 || k == 0 || k == 4 || k == 6)) {
+ // if (k==1 || k==2 || k==0 || k==4 || k==6) {
+ in.seekSet(j + in.readTTFUShort());
+ String txt = in.readTTFString(l);
+ // getLogger().debug(platform_id + " " + encoding_id
+ // + " " + k + " " + txt);
+ switch (k) {
+ case 0:
+ notice = txt;
+ break;
+ case 1:
+ familyName = txt;
+ break;
+ case 2:
+ subFamilyName = txt;
+ break;
+ case 4:
+ fullName = txt;
+ break;
+ case 6:
+ fontName = txt;
+ break;
+ }
+ if (!notice.equals("")
+ && !fullName.equals("")
+ && !fontName.equals("")
+ && !familyName.equals("")
+ && !subFamilyName.equals("")) {
+ break;
+ }
+ }
+ i += 6 * 2;
+ }
+ }
+
+ /**
+ * Read the "PCLT" table to find xHeight and capHeight.
+ * @param in FontFileReader to read from
+ * @throws IOException In case of a I/O problem
+ */
+ private final void readPCLT(FontFileReader in) throws IOException {
+ TTFDirTabEntry dirTab = (TTFDirTabEntry)dirTabs.get("PCLT");
+ if (dirTab != null) {
+ in.seekSet(dirTab.getOffset() + 4 + 4 + 2);
+ xHeight = in.readTTFUShort();
+ in.skip(2 * 2);
+ capHeight = in.readTTFUShort();
+ in.skip(2 + 16 + 8 + 6 + 1 + 1);
+
+ int serifStyle = in.readTTFUByte();
+ serifStyle = serifStyle >> 6;
+ serifStyle = serifStyle & 3;
+ if (serifStyle == 1) {
+ hasSerifs = false;
+ } else {
+ hasSerifs = true;
+ }
+ } else {
+ // Approximate capHeight from height of "H"
+ // It's most unlikly that a font misses the PCLT table
+ // This also assumes that psocriptnames exists ("H")
+ // Should look it up int the cmap (that wouldn't help
+ // for charsets without H anyway...)
+ for (int i = 0; i < mtxTab.length; i++) {
+ if ("H".equals(mtxTab[i].getName())) {
+ capHeight = mtxTab[i].getBoundingBox()[3] - mtxTab[i].getBoundingBox()[1];
+ }
+ }
+ }
+ }
+
+ /**
+ * Read the kerning table, create a table for both CIDs and
+ * winAnsiEncoding.
+ * @param in FontFileReader to read from
+ * @throws IOException In case of a I/O problem
+ */
+ private final void readKerning(FontFileReader in) throws IOException {
+ // Read kerning
+ kerningTab = new java.util.HashMap();
+ ansiKerningTab = new java.util.HashMap();
+ TTFDirTabEntry dirTab = (TTFDirTabEntry)dirTabs.get("kern");
+ if (dirTab != null) {
+ seekTab(in, "kern", 2);
+ for (int n = in.readTTFUShort(); n > 0; n--) {
+ in.skip(2 * 2);
+ int k = in.readTTFUShort();
+ if (!((k & 1) != 0) || (k & 2) != 0 || (k & 4) != 0) {
+ return;
+ }
+ if ((k >> 8) != 0) {
+ continue;
+ }
+
+ k = in.readTTFUShort();
+ in.skip(3 * 2);
+ while (k-- > 0) {
+ int i = in.readTTFUShort();
+ int j = in.readTTFUShort();
+ int kpx = in.readTTFShort();
+ if (kpx != 0) {
+ // CID table
+ Integer iObj = new Integer(i);
+ Map adjTab = (Map)kerningTab.get(iObj);
+ if (adjTab == null) {
+ adjTab = new java.util.HashMap();
+ }
+ adjTab.put(new Integer(j),
+ new Integer((int)convertTTFUnit2PDFUnit(kpx)));
+ kerningTab.put(iObj, adjTab);
+ }
+ }
+ }
+ // getLogger().debug(kerningTab.toString());
+
+ // Create winAnsiEncoded kerning table
+ Iterator ae = kerningTab.keySet().iterator();
+ while (ae.hasNext()) {
+ Integer cidKey = (Integer)ae.next();
+ Map akpx = new java.util.HashMap();
+ Map ckpx = (Map)kerningTab.get(cidKey);
+
+ Iterator aee = ckpx.keySet().iterator();
+ while (aee.hasNext()) {
+ Integer cidKey2 = (Integer)aee.next();
+ Integer kern = (Integer)ckpx.get(cidKey2);
+
+ Iterator uniMap = mtxTab[cidKey2.intValue()].getUnicodeIndex().listIterator();
+ while (uniMap.hasNext()) {
+ Integer unicodeKey = (Integer)uniMap.next();
+ Integer[] ansiKeys = unicodeToWinAnsi(unicodeKey.intValue());
+ for (int u = 0; u < ansiKeys.length; u++) {
+ akpx.put(ansiKeys[u], kern);
+ }
+ }
+ }
+
+ if (akpx.size() > 0) {
+ Iterator uniMap = mtxTab[cidKey.intValue()].getUnicodeIndex().listIterator();
+ while (uniMap.hasNext()) {
+ Integer unicodeKey = (Integer)uniMap.next();
+ Integer[] ansiKeys = unicodeToWinAnsi(unicodeKey.intValue());
+ for (int u = 0; u < ansiKeys.length; u++) {
+ ansiKerningTab.put(ansiKeys[u], akpx);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Return a List with TTFCmapEntry.
+ * @return A list of TTFCmapEntry objects
+ */
+ public List getCMaps() {
+ return cmaps;
+ }
+
+ /**
+ * Check if this is a TrueType collection and that the given
+ * name exists in the collection.
+ * If it does, set offset in fontfile to the beginning of
+ * the Table Directory for that font.
+ * @param in FontFileReader to read from
+ * @param name The name to check
+ * @return True if not collection or font name present, false otherwise
+ * @throws IOException In case of an I/O problem
+ */
+ protected final boolean checkTTC(FontFileReader in, String name) throws IOException {
+ String tag = in.readTTFString(4);
+
+ if ("ttcf".equals(tag)) {
+ // This is a TrueType Collection
+ in.skip(4);
+
+ // Read directory offsets
+ int numDirectories = (int)in.readTTFULong();
+ // int numDirectories=in.readTTFUShort();
+ long[] dirOffsets = new long[numDirectories];
+ for (int i = 0; i < numDirectories; i++) {
+ dirOffsets[i] = in.readTTFULong();
+ }
+
+ getLogger().debug("This is a TrueType collection file with"
+ + numDirectories + " fonts");
+ getLogger().debug("Containing the following fonts: ");
+ // Read all the directories and name tables to check
+ // If the font exists - this is a bit ugly, but...
+ boolean found = false;
+
+ // Iterate through all name tables even if font
+ // Is found, just to show all the names
+ long dirTabOffset = 0;
+ for (int i = 0; (i < numDirectories); i++) {
+ in.seekSet(dirOffsets[i]);
+ readDirTabs(in);
+
+ readName(in);
+
+ if (fullName.equals(name)) {
+ found = true;
+ dirTabOffset = dirOffsets[i];
+ getLogger().debug("* " + fullName);
+ } else {
+ getLogger().debug(fullName);
+ }
+
+ // Reset names
+ notice = "";
+ fullName = "";
+ familyName = "";
+ fontName = "";
+ subFamilyName = "";
+ }
+
+ in.seekSet(dirTabOffset);
+ return found;
+ } else {
+ in.seekSet(0);
+ return true;
+ }
+ }
+
+ /*
+ * Helper classes, they are not very efficient, but that really
+ * doesn't matter...
+ */
+ private Integer[] unicodeToWinAnsi(int unicode) {
+ List ret = new java.util.ArrayList();
+ for (int i = 32; i < Glyphs.WINANSI_ENCODING.length; i++) {
+ if (unicode == Glyphs.WINANSI_ENCODING[i]) {
+ ret.add(new Integer(i));
+ }
+ }
+ return (Integer[])ret.toArray(new Integer[0]);
+ }
+
+ /**
+ * Dumps a few informational values to System.out.
+ */
+ public void printStuff() {
+ System.out.println("Font name: " + fontName);
+ System.out.println("Full name: " + fullName);
+ System.out.println("Family name: " + familyName);
+ System.out.println("Subfamily name: " + subFamilyName);
+ System.out.println("Notice: " + notice);
+ System.out.println("xHeight: " + (int)convertTTFUnit2PDFUnit(xHeight));
+ System.out.println("capheight: " + (int)convertTTFUnit2PDFUnit(capHeight));
+
+ int italic = (int)(italicAngle >> 16);
+ System.out.println("Italic: " + italic);
+ System.out.print("ItalicAngle: " + (short)(italicAngle / 0x10000));
+ if ((italicAngle % 0x10000) > 0) {
+ System.out.print("."
+ + (short)((italicAngle % 0x10000) * 1000)
+ / 0x10000);
+ }
+ System.out.println();
+ System.out.println("Ascender: " + convertTTFUnit2PDFUnit(ascender));
+ System.out.println("Descender: " + convertTTFUnit2PDFUnit(descender));
+ System.out.println("FontBBox: [" + (int)convertTTFUnit2PDFUnit(fontBBox1)
+ + " " + (int)convertTTFUnit2PDFUnit(fontBBox2) + " "
+ + (int)convertTTFUnit2PDFUnit(fontBBox3) + " "
+ + (int)convertTTFUnit2PDFUnit(fontBBox4) + "]");
+ }
+
+ /**
+ * Static main method to get info about a TrueType font.
+ * @param args The command line arguments
+ */
+ public static void main(String[] args) {
+ int level = ConsoleLogger.LEVEL_WARN;
+ Logger log = new ConsoleLogger(level);
+ try {
+ TTFFile ttfFile = new TTFFile();
+ ttfFile.enableLogging(log);
+
+ FontFileReader reader = new FontFileReader(args[0]);
+
+ String name = null;
+ if (args.length >= 2) {
+ name = args[1];
+ }
+
+ ttfFile.readFont(reader, name);
+ ttfFile.printStuff();
+
+ } catch (IOException ioe) {
+ log.error("Problem reading font: " + ioe.toString(), ioe);
+ }
+ }
+
+}
+
+
+/**
+ * Key-value helper class
+ */
+class UnicodeMapping {
+
+ private int uIdx;
+ private int gIdx;
+
+ UnicodeMapping(int gIdx, int uIdx) {
+ this.uIdx = uIdx;
+ this.gIdx = gIdx;
+ }
+
+ /**
+ * Returns the gIdx.
+ * @return int
+ */
+ public int getGIdx() {
+ return gIdx;
+ }
+
+ /**
+ * Returns the uIdx.
+ * @return int
+ */
+ public int getUIdx() {
+ return uIdx;
+ }
+
+}
--- /dev/null
+/*
+ * $Id$
+ * Copyright (C) 2001-2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts.truetype;
+
+import java.util.List;
+
+/**
+ * This class represents a TrueType Mtx Entry.
+ */
+class TTFMtxEntry {
+
+ private int wx;
+ private int lsb;
+ private String name = "";
+ private int index;
+ private List unicodeIndex = new java.util.ArrayList();
+ private int[] boundingBox = new int[4];
+ private long offset;
+ private byte found = 0;
+
+ /**
+ * Returns a String representation of this object.
+ *
+ * @param t TTFFile to use for unit conversion
+ * @return String String representation
+ */
+ public String toString(TTFFile t) {
+ return "Glyph " + name + " index: " + index + " bbox [ "
+ + t.convertTTFUnit2PDFUnit(boundingBox[0]) + " "
+ + t.convertTTFUnit2PDFUnit(boundingBox[1]) + " "
+ + t.convertTTFUnit2PDFUnit(boundingBox[2]) + " "
+ + t.convertTTFUnit2PDFUnit(boundingBox[3]) + "] wx: "
+ + t.convertTTFUnit2PDFUnit(wx);
+ }
+
+ /**
+ * Returns the boundingBox.
+ * @return int[]
+ */
+ public int[] getBoundingBox() {
+ return boundingBox;
+ }
+
+ /**
+ * Sets the boundingBox.
+ * @param boundingBox The boundingBox to set
+ */
+ public void setBoundingBox(int[] boundingBox) {
+ this.boundingBox = boundingBox;
+ }
+
+ /**
+ * Returns the found.
+ * @return byte
+ */
+ public byte getFound() {
+ return found;
+ }
+
+ /**
+ * Returns the index.
+ * @return int
+ */
+ public int getIndex() {
+ return index;
+ }
+
+ /**
+ * Returns the lsb.
+ * @return int
+ */
+ public int getLsb() {
+ return lsb;
+ }
+
+ /**
+ * Returns the name.
+ * @return String
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the offset.
+ * @return long
+ */
+ public long getOffset() {
+ return offset;
+ }
+
+ /**
+ * Returns the unicodeIndex.
+ * @return List
+ */
+ public List getUnicodeIndex() {
+ return unicodeIndex;
+ }
+
+ /**
+ * Returns the wx.
+ * @return int
+ */
+ public int getWx() {
+ return wx;
+ }
+
+ /**
+ * Sets the found.
+ * @param found The found to set
+ */
+ public void setFound(byte found) {
+ this.found = found;
+ }
+
+ /**
+ * Sets the index.
+ * @param index The index to set
+ */
+ public void setIndex(int index) {
+ this.index = index;
+ }
+
+ /**
+ * Sets the lsb.
+ * @param lsb The lsb to set
+ */
+ public void setLsb(int lsb) {
+ this.lsb = lsb;
+ }
+
+ /**
+ * Sets the name.
+ * @param name The name to set
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Sets the offset.
+ * @param offset The offset to set
+ */
+ public void setOffset(long offset) {
+ this.offset = offset;
+ }
+
+ /**
+ * Sets the wx.
+ * @param wx The wx to set
+ */
+ public void setWx(int wx) {
+ this.wx = wx;
+ }
+
+
+}
--- /dev/null
+/*
+ * $Id$
+ * Copyright (C) 2001-2003 The Apache Software Foundation. All rights reserved.
+ * For details on use and redistribution please refer to the
+ * LICENSE file included with these sources.
+ */
+
+package org.apache.fop.fonts.truetype;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.List;
+
+
+/**
+ * Reads a TrueType file and generates a subset
+ * that can be used to embed a TrueType CID font.
+ * TrueType tables needed for embedded CID fonts are:
+ * "head", "hhea", "loca", "maxp", "cvt ", "prep", "glyf", "hmtx" and "fpgm".
+ * The TrueType spec can be found at the Microsoft
+ * Typography site: http://www.microsoft.com/truetype/
+ */
+public class TTFSubSetFile extends TTFFile {
+
+ private byte[] output = null;
+ private int realSize = 0;
+ private int currentPos = 0;
+
+ /*
+ * Offsets in name table to be filled out by table.
+ * The offsets are to the checkSum field
+ */
+ private int cvtDirOffset = 0;
+ private int fpgmDirOffset = 0;
+ private int glyfDirOffset = 0;
+ private int headDirOffset = 0;
+ private int hheaDirOffset = 0;
+ private int hmtxDirOffset = 0;
+ private int locaDirOffset = 0;
+ private int maxpDirOffset = 0;
+ private int prepDirOffset = 0;
+
+ private int checkSumAdjustmentOffset = 0;
+ private int locaOffset = 0;
+
+ /**
+ * Initalize the output array
+ */
+ private void init(int size) {
+ output = new byte[size];
+ realSize = 0;
+ currentPos = 0;
+
+ // createDirectory()
+ }
+
+ /**
+ * Create the directory table
+ */
+ private void createDirectory() {
+ int numTables = 9;
+ // Create the TrueType header
+ writeByte((byte)0);
+ writeByte((byte)1);
+ writeByte((byte)0);
+ writeByte((byte)0);
+ realSize += 4;
+
+ writeUShort(numTables);
+ realSize += 2;
+
+ // Create searchRange, entrySelector and rangeShift
+ int maxPow = maxPow2(numTables);
+ int searchRange = maxPow * 16;
+ writeUShort(searchRange);
+ realSize += 2;
+
+ writeUShort(maxPow);
+ realSize += 2;
+
+ writeUShort((numTables * 16) - searchRange);
+ realSize += 2;
+
+ // Create space for the table entries
+ writeString("cvt ");
+ cvtDirOffset = currentPos;
+ currentPos += 12;
+ realSize += 16;
+
+ writeString("fpgm");
+ fpgmDirOffset = currentPos;
+ currentPos += 12;
+ realSize += 16;
+
+ writeString("glyf");
+ glyfDirOffset = currentPos;
+ currentPos += 12;
+ realSize += 16;
+
+ writeString("head");
+ headDirOffset = currentPos;
+ currentPos += 12;
+ realSize += 16;
+
+ writeString("hhea");
+ hheaDirOffset = currentPos;
+ currentPos += 12;
+ realSize += 16;
+
+ writeString("hmtx");
+ hmtxDirOffset = currentPos;
+ currentPos += 12;
+ realSize += 16;
+
+ writeString("loca");
+ locaDirOffset = currentPos;
+ currentPos += 12;
+ realSize += 16;
+
+ writeString("maxp");
+ maxpDirOffset = currentPos;
+ currentPos += 12;
+ realSize += 16;
+
+ writeString("prep");
+ prepDirOffset = currentPos;
+ currentPos += 12;
+ realSize += 16;
+ }
+
+
+ /**
+ * Copy the cvt table as is from original font to subset font
+ */
+ private void createCvt(FontFileReader in) throws IOException {
+ TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("cvt ");
+ if (entry != null) {
+ pad4();
+ seekTab(in, "cvt ", 0);
+ System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
+ 0, output, currentPos, (int)entry.getLength());
+
+ int checksum = getCheckSum(currentPos, (int)entry.getLength());
+ writeULong(cvtDirOffset, checksum);
+ writeULong(cvtDirOffset + 4, currentPos);
+ writeULong(cvtDirOffset + 8, (int)entry.getLength());
+ currentPos += (int)entry.getLength();
+ realSize += (int)entry.getLength();
+ } else {
+ throw new IOException("Can't find cvt table");
+ }
+ }
+
+
+
+ /**
+ * Copy the fpgm table as is from original font to subset font
+ */
+ private void createFpgm(FontFileReader in) throws IOException {
+ TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("fpgm");
+ if (entry != null) {
+ pad4();
+ seekTab(in, "fpgm", 0);
+ System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
+ 0, output, currentPos, (int)entry.getLength());
+ int checksum = getCheckSum(currentPos, (int)entry.getLength());
+ writeULong(fpgmDirOffset, checksum);
+ writeULong(fpgmDirOffset + 4, currentPos);
+ writeULong(fpgmDirOffset + 8, (int)entry.getLength());
+ currentPos += (int)entry.getLength();
+ realSize += (int)entry.getLength();
+ } else {
+ throw new IOException("Can't find fpgm table");
+ }
+ }
+
+
+
+ /**
+ * Create an empty loca table without updating checksum
+ */
+ private void createLoca(int size) throws IOException {
+ pad4();
+ locaOffset = currentPos;
+ writeULong(locaDirOffset + 4, currentPos);
+ writeULong(locaDirOffset + 8, size * 4 + 4);
+ currentPos += size * 4 + 4;
+ realSize += size * 4 + 4;
+ }
+
+
+ /**
+ * Copy the maxp table as is from original font to subset font
+ * and set num glyphs to size
+ */
+ private void createMaxp(FontFileReader in, int size) throws IOException {
+ TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("maxp");
+ if (entry != null) {
+ pad4();
+ seekTab(in, "maxp", 0);
+ System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
+ 0, output, currentPos, (int)entry.getLength());
+ writeUShort(currentPos + 4, size);
+
+ int checksum = getCheckSum(currentPos, (int)entry.getLength());
+ writeULong(maxpDirOffset, checksum);
+ writeULong(maxpDirOffset + 4, currentPos);
+ writeULong(maxpDirOffset + 8, (int)entry.getLength());
+ currentPos += (int)entry.getLength();
+ realSize += (int)entry.getLength();
+ } else {
+ throw new IOException("Can't find maxp table");
+ }
+ }
+
+
+ /**
+ * Copy the prep table as is from original font to subset font
+ */
+ private void createPrep(FontFileReader in) throws IOException {
+ TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("prep");
+ if (entry != null) {
+ pad4();
+ seekTab(in, "prep", 0);
+ System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
+ 0, output, currentPos, (int)entry.getLength());
+
+ int checksum = getCheckSum(currentPos, (int)entry.getLength());
+ writeULong(prepDirOffset, checksum);
+ writeULong(prepDirOffset + 4, currentPos);
+ writeULong(prepDirOffset + 8, (int)entry.getLength());
+ currentPos += (int)entry.getLength();
+ realSize += (int)entry.getLength();
+ } else {
+ throw new IOException("Can't find prep table");
+ }
+ }
+
+
+ /**
+ * Copy the hhea table as is from original font to subset font
+ * and fill in size of hmtx table
+ */
+ private void createHhea(FontFileReader in, int size) throws IOException {
+ TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("hhea");
+ if (entry != null) {
+ pad4();
+ seekTab(in, "hhea", 0);
+ System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
+ 0, output, currentPos, (int)entry.getLength());
+ writeUShort((int)entry.getLength() + currentPos - 2, size);
+
+ int checksum = getCheckSum(currentPos, (int)entry.getLength());
+ writeULong(hheaDirOffset, checksum);
+ writeULong(hheaDirOffset + 4, currentPos);
+ writeULong(hheaDirOffset + 8, (int)entry.getLength());
+ currentPos += (int)entry.getLength();
+ realSize += (int)entry.getLength();
+ } else {
+ throw new IOException("Can't find hhea table");
+ }
+ }
+
+
+ /**
+ * Copy the head table as is from original font to subset font
+ * and set indexToLocaFormat to long and set
+ * checkSumAdjustment to 0, store offset to checkSumAdjustment
+ * in checkSumAdjustmentOffset
+ */
+ private void createHead(FontFileReader in) throws IOException {
+ TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("head");
+ if (entry != null) {
+ pad4();
+ seekTab(in, "head", 0);
+ System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
+ 0, output, currentPos, (int)entry.getLength());
+
+ checkSumAdjustmentOffset = currentPos + 8;
+ output[currentPos + 8] = 0; // Set checkSumAdjustment to 0
+ output[currentPos + 9] = 0;
+ output[currentPos + 10] = 0;
+ output[currentPos + 11] = 0;
+ output[currentPos + 50] = 0; // long locaformat
+ output[currentPos + 51] = 1; // long locaformat
+
+ int checksum = getCheckSum(currentPos, (int)entry.getLength());
+ writeULong(headDirOffset, checksum);
+ writeULong(headDirOffset + 4, currentPos);
+ writeULong(headDirOffset + 8, (int)entry.getLength());
+
+ currentPos += (int)entry.getLength();
+ realSize += (int)entry.getLength();
+ } else {
+ throw new IOException("Can't find head table");
+ }
+ }
+
+
+ /**
+ * Create the glyf table and fill in loca table
+ */
+ private void createGlyf(FontFileReader in,
+ Map glyphs) throws IOException {
+ TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("glyf");
+ int size = 0;
+ int start = 0;
+ int endOffset = 0; // Store this as the last loca
+ if (entry != null) {
+ pad4();
+ start = currentPos;
+
+ /* Loca table must be in order by glyph index, so build
+ * an array first and then write the glyph info and
+ * location offset.
+ */
+ int[] origIndexes = new int[glyphs.size()];
+
+ Iterator e = glyphs.keySet().iterator();
+ while (e.hasNext()) {
+ Integer origIndex = (Integer)e.next();
+ Integer subsetIndex = (Integer)glyphs.get(origIndex);
+ origIndexes[subsetIndex.intValue()] = origIndex.intValue();
+ }
+
+ for (int i = 0; i < origIndexes.length; i++) {
+ int glyphLength = 0;
+ int nextOffset = 0;
+ int origGlyphIndex = origIndexes[i];
+ if (origGlyphIndex >= (mtxTab.length - 1)) {
+ nextOffset = (int)lastLoca;
+ } else {
+ nextOffset = (int)mtxTab[origGlyphIndex + 1].getOffset();
+ }
+ glyphLength = nextOffset - (int)mtxTab[origGlyphIndex].getOffset();
+
+ // Copy glyph
+ System.arraycopy(
+ in.getBytes((int)entry.getOffset() + (int)mtxTab[origGlyphIndex].getOffset(),
+ glyphLength), 0,
+ output, currentPos,
+ glyphLength);
+
+
+ // Update loca table
+ writeULong(locaOffset + i * 4, currentPos - start);
+ if ((currentPos - start + glyphLength) > endOffset) {
+ endOffset = (currentPos - start + glyphLength);
+ }
+
+ currentPos += glyphLength;
+ realSize += glyphLength;
+
+ }
+
+ size = currentPos - start;
+
+ int checksum = getCheckSum(start, size);
+ writeULong(glyfDirOffset, checksum);
+ writeULong(glyfDirOffset + 4, start);
+ writeULong(glyfDirOffset + 8, size);
+ currentPos += 12;
+ realSize += 12;
+
+ // Update loca checksum and last loca index
+ writeULong(locaOffset + glyphs.size() * 4, endOffset);
+
+ checksum = getCheckSum(locaOffset, glyphs.size() * 4 + 4);
+ writeULong(locaDirOffset, checksum);
+ } else {
+ throw new IOException("Can't find glyf table");
+ }
+ }
+
+
+ /**
+ * Create the hmtx table by copying metrics from original
+ * font to subset font. The glyphs Map contains an
+ * Integer key and Integer value that maps the original
+ * metric (key) to the subset metric (value)
+ */
+ private void createHmtx(FontFileReader in,
+ Map glyphs) throws IOException {
+ TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("hmtx");
+
+ int longHorMetricSize = glyphs.size() * 2;
+ int leftSideBearingSize = glyphs.size() * 2;
+ int hmtxSize = longHorMetricSize + leftSideBearingSize;
+
+ if (entry != null) {
+ pad4();
+ //int offset = (int)entry.offset;
+ Iterator e = glyphs.keySet().iterator();
+ while (e.hasNext()) {
+ Integer origIndex = (Integer)e.next();
+ Integer subsetIndex = (Integer)glyphs.get(origIndex);
+
+ writeUShort(currentPos + subsetIndex.intValue() * 4,
+ mtxTab[origIndex.intValue()].getWx());
+ writeUShort(currentPos + subsetIndex.intValue() * 4 + 2,
+ mtxTab[origIndex.intValue()].getLsb());
+ }
+
+ int checksum = getCheckSum(currentPos, hmtxSize);
+ writeULong(hmtxDirOffset, checksum);
+ writeULong(hmtxDirOffset + 4, currentPos);
+ writeULong(hmtxDirOffset + 8, hmtxSize);
+ currentPos += hmtxSize;
+ realSize += hmtxSize;
+ } else {
+ throw new IOException("Can't find hmtx table");
+ }
+ }
+
+ /**
+ * Returns a List containing the glyph itself plus all glyphs
+ * that this composite glyph uses
+ */
+ private List getIncludedGlyphs(FontFileReader in, int glyphOffset,
+ Integer glyphIdx) throws IOException {
+ List ret = new java.util.ArrayList();
+ ret.add(glyphIdx);
+ int offset = glyphOffset + (int)mtxTab[glyphIdx.intValue()].getOffset() + 10;
+ Integer compositeIdx = null;
+ int flags = 0;
+ boolean moreComposites = true;
+ while (moreComposites) {
+ flags = in.readTTFUShort(offset);
+ compositeIdx = new Integer(in.readTTFUShort(offset + 2));
+ ret.add(compositeIdx);
+
+ offset += 4;
+ if ((flags & 1) > 0) {
+ // ARG_1_AND_ARG_2_ARE_WORDS
+ offset += 4;
+ } else {
+ offset += 2;
+ }
+
+ if ((flags & 8) > 0) {
+ offset += 2; // WE_HAVE_A_SCALE
+ } else if ((flags & 64) > 0) {
+ offset += 4; // WE_HAVE_AN_X_AND_Y_SCALE
+ } else if ((flags & 128) > 0) {
+ offset += 8; // WE_HAVE_A_TWO_BY_TWO
+ }
+
+ if ((flags & 32) > 0) {
+ moreComposites = true;
+ } else {
+ moreComposites = false;
+ }
+ }
+
+ return ret;
+ }
+
+
+ /**
+ * Rewrite all compositepointers in glyphindex glyphIdx
+ *
+ */
+ private void remapComposite(FontFileReader in, Map glyphs,
+ int glyphOffset,
+ Integer glyphIdx) throws IOException {
+ int offset = glyphOffset + (int)mtxTab[glyphIdx.intValue()].getOffset()
+ + 10;
+
+ Integer compositeIdx = null;
+ int flags = 0;
+ boolean moreComposites = true;
+
+ while (moreComposites) {
+ flags = in.readTTFUShort(offset);
+ compositeIdx = new Integer(in.readTTFUShort(offset + 2));
+ Integer newIdx = (Integer)glyphs.get(compositeIdx);
+ if (newIdx == null) {
+ // This errormessage would look much better
+ // if the fontname was printed to
+ //log.error("An embedded font "
+ // + "contains bad glyph data. "
+ // + "Characters might not display "
+ // + "correctly.");
+ moreComposites = false;
+ continue;
+ }
+
+ in.writeTTFUShort(offset + 2, newIdx.intValue());
+
+ offset += 4;
+
+ if ((flags & 1) > 0) {
+ // ARG_1_AND_ARG_2_ARE_WORDS
+ offset += 4;
+ } else {
+ offset += 2;
+ }
+
+ if ((flags & 8) > 0) {
+ offset += 2; // WE_HAVE_A_SCALE
+ } else if ((flags & 64) > 0) {
+ offset += 4; // WE_HAVE_AN_X_AND_Y_SCALE
+ } else if ((flags & 128) > 0) {
+ offset += 8; // WE_HAVE_A_TWO_BY_TWO
+ }
+
+ if ((flags & 32) > 0) {
+ moreComposites = true;
+ } else {
+ moreComposites = false;
+ }
+ }
+ }
+
+
+ /**
+ * Scan all the original glyphs for composite glyphs and add those glyphs
+ * to the glyphmapping also rewrite the composite glyph pointers to the new
+ * mapping
+ */
+ private void scanGlyphs(FontFileReader in,
+ Map glyphs) throws IOException {
+ TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("glyf");
+ Map newComposites = null;
+ Map allComposites = new java.util.HashMap();
+
+ int newIndex = glyphs.size();
+
+ if (entry != null) {
+ while (newComposites == null || newComposites.size() > 0) {
+ // Inefficient to iterate through all glyphs
+ newComposites = new java.util.HashMap();
+
+ Iterator e = glyphs.keySet().iterator();
+ while (e.hasNext()) {
+ Integer origIndex = (Integer)e.next();
+
+ if (in.readTTFShort(entry.getOffset()
+ + mtxTab[origIndex.intValue()].getOffset()) < 0) {
+ // origIndex is a composite glyph
+ allComposites.put(origIndex, glyphs.get(origIndex));
+ List composites =
+ getIncludedGlyphs(in, (int)entry.getOffset(),
+ origIndex);
+
+ // Iterate through all composites pointed to
+ // by this composite and check if they exists
+ // in the glyphs map, add them if not.
+ Iterator cps = composites.iterator();
+ while (cps.hasNext()) {
+ Integer cIdx = (Integer)cps.next();
+ if (glyphs.get(cIdx) == null
+ && newComposites.get(cIdx) == null) {
+ newComposites.put(cIdx,
+ new Integer(newIndex));
+ newIndex++;
+ }
+ }
+ }
+ }
+
+ // Add composites to glyphs
+ Iterator m = newComposites.keySet().iterator();
+ while (m.hasNext()) {
+ Integer im = (Integer)m.next();
+ glyphs.put(im, newComposites.get(im));
+ }
+ }
+
+ // Iterate through all composites to remap their composite index
+ Iterator ce = allComposites.keySet().iterator();
+ while (ce.hasNext()) {
+ remapComposite(in, glyphs, (int)entry.getOffset(),
+ (Integer)ce.next());
+ }
+
+ } else {
+ throw new IOException("Can't find glyf table");
+ }
+ }
+
+
+
+ /**
+ * Returns a subset of the original font.
+ *
+ * @param in FontFileReader to read from
+ * @param name Name to be checked for in the font file
+ * @param glyphs Map of glyphs (glyphs has old index as (Integer) key and
+ * new index as (Integer) value)
+ * @return A subset of the original font
+ * @throws IOException in case of an I/O problem
+ */
+ public byte[] readFont(FontFileReader in, String name,
+ Map glyphs) throws IOException {
+
+ //Check if TrueType collection, and that the name exists in the collection
+ if (!checkTTC(in, name)) {
+ throw new IOException("Failed to read font");
+ }
+
+ output = new byte[in.getFileSize()];
+
+ readDirTabs(in);
+ readFontHeader(in);
+ getNumGlyphs(in);
+ readHorizontalHeader(in);
+ readHorizontalMetrics(in);
+ readIndexToLocation(in);
+
+ scanGlyphs(in, glyphs);
+
+ createDirectory(); // Create the TrueType header and directory
+
+ createHead(in);
+ createHhea(in, glyphs.size()); // Create the hhea table
+ createHmtx(in, glyphs); // Create hmtx table
+ createMaxp(in, glyphs.size()); // copy the maxp table
+
+ try {
+ createCvt(in); // copy the cvt table
+ } catch (IOException ex) {
+ // Cvt is optional (only required for OpenType (MS) fonts)
+ //log.error("TrueType warning: " + ex.getMessage());
+ }
+
+ try {
+ createFpgm(in); // copy fpgm table
+ } catch (IOException ex) {
+ // Fpgm is optional (only required for OpenType (MS) fonts)
+ //log.error("TrueType warning: " + ex.getMessage());
+ }
+
+ try {
+ createPrep(in); // copy prep table
+ } catch (IOException ex) {
+ // Prep is optional (only required for OpenType (MS) fonts)
+ //log.error("TrueType warning: " + ex.getMessage());
+ }
+
+ try {
+ createLoca(glyphs.size()); // create empty loca table
+ } catch (IOException ex) {
+ // Loca is optional (only required for OpenType (MS) fonts)
+ //log.error("TrueType warning: " + ex.getMessage());
+ }
+
+ try {
+ createGlyf(in, glyphs);
+ } catch (IOException ex) {
+ // Glyf is optional (only required for OpenType (MS) fonts)
+ //log.error("TrueType warning: " + ex.getMessage());
+ }
+
+ pad4();
+ createCheckSumAdjustment();
+
+ byte[] ret = new byte[realSize];
+ System.arraycopy(output, 0, ret, 0, realSize);
+
+ return ret;
+ }
+
+ /**
+ * writes a ISO-8859-1 string at the currentPosition
+ * updates currentPosition but not realSize
+ * @return number of bytes written
+ */
+ private int writeString(String str) {
+ int length = 0;
+ try {
+ byte[] buf = str.getBytes("ISO-8859-1");
+ System.arraycopy(buf, 0, output, currentPos, buf.length);
+ length = buf.length;
+ currentPos += length;
+ } catch (java.io.UnsupportedEncodingException e) {
+ // This should never happen!
+ }
+
+ return length;
+ }
+
+ /**
+ * Appends a byte to the output array,
+ * updates currentPost but not realSize
+ */
+ private void writeByte(byte b) {
+ output[currentPos++] = b;
+ }
+
+ /**
+ * Appends a USHORT to the output array,
+ * updates currentPost but not realSize
+ */
+ private void writeUShort(int s) {
+ byte b1 = (byte)((s >> 8) & 0xff);
+ byte b2 = (byte)(s & 0xff);
+ writeByte(b1);
+ writeByte(b2);
+ }
+
+ /**
+ * Appends a USHORT to the output array,
+ * at the given position without changing currentPos
+ */
+ private void writeUShort(int pos, int s) {
+ byte b1 = (byte)((s >> 8) & 0xff);
+ byte b2 = (byte)(s & 0xff);
+ output[pos] = b1;
+ output[pos + 1] = b2;
+ }
+
+ /**
+ * Appends a ULONG to the output array,
+ * updates currentPos but not realSize
+ */
+ private void writeULong(int s) {
+ byte b1 = (byte)((s >> 24) & 0xff);
+ byte b2 = (byte)((s >> 16) & 0xff);
+ byte b3 = (byte)((s >> 8) & 0xff);
+ byte b4 = (byte)(s & 0xff);
+ writeByte(b1);
+ writeByte(b2);
+ writeByte(b3);
+ writeByte(b4);
+ }
+
+ /**
+ * Appends a ULONG to the output array,
+ * at the given position without changing currentPos
+ */
+ private void writeULong(int pos, int s) {
+ byte b1 = (byte)((s >> 24) & 0xff);
+ byte b2 = (byte)((s >> 16) & 0xff);
+ byte b3 = (byte)((s >> 8) & 0xff);
+ byte b4 = (byte)(s & 0xff);
+ output[pos] = b1;
+ output[pos + 1] = b2;
+ output[pos + 2] = b3;
+ output[pos + 3] = b4;
+ }
+
+ /**
+ * Read a signed short value at given position
+ */
+ private short readShort(int pos) {
+ int ret = readUShort(pos);
+ return (short)ret;
+ }
+
+ /**
+ * Read a unsigned short value at given position
+ */
+ private int readUShort(int pos) {
+ int ret = (int)output[pos];
+ if (ret < 0) {
+ ret += 256;
+ }
+ ret = ret << 8;
+ if ((int)output[pos + 1] < 0) {
+ ret |= (int)output[pos + 1] + 256;
+ } else {
+ ret |= (int)output[pos + 1];
+ }
+
+ return ret;
+ }
+
+ /**
+ * Create a padding in the fontfile to align
+ * on a 4-byte boundary
+ */
+ private void pad4() {
+ int padSize = currentPos % 4;
+ for (int i = 0; i < padSize; i++) {
+ output[currentPos++] = 0;
+ realSize++;
+ }
+ }
+
+ /**
+ * Returns the maximum power of 2 <= max
+ */
+ private int maxPow2(int max) {
+ int i = 0;
+ while (Math.pow(2, (double)i) < max) {
+ i++;
+ }
+
+ return (i - 1);
+ }
+
+ private int log2(int num) {
+ return (int)(Math.log((double)num) / Math.log(2));
+ }
+
+
+ private int getCheckSum(int start, int size) {
+ return (int)getLongCheckSum(start, size);
+ }
+
+ private long getLongCheckSum(int start, int size) {
+ // All the tables here are aligned on four byte boundaries
+ // Add remainder to size if it's not a multiple of 4
+ int remainder = size % 4;
+ if (remainder != 0) {
+ size += remainder;
+ }
+
+ long sum = 0;
+
+ for (int i = 0; i < size; i += 4) {
+ int l = (int)(output[start + i] << 24);
+ l += (int)(output[start + i + 1] << 16);
+ l += (int)(output[start + i + 2] << 16);
+ l += (int)(output[start + i + 3] << 16);
+ sum += l;
+ if (sum > 0xffffffff) {
+ sum = sum - 0xffffffff;
+ }
+ }
+
+ return sum;
+ }
+
+ private void createCheckSumAdjustment() {
+ long sum = getLongCheckSum(0, realSize);
+ int checksum = (int)(0xb1b0afba - sum);
+ writeULong(checkSumAdjustmentOffset, checksum);
+ }
+
+}
+
+
+