You are viewing a plain text version of this content. The canonical link for it is here.
Posted to fop-commits@xmlgraphics.apache.org by je...@apache.org on 2003/01/08 14:54:05 UTC
cvs commit: xml-fop/src/org/apache/fop/fonts/truetype FontFileReader.java TTFCmapEntry.java TTFDirTabEntry.java TTFFile.java TTFMtxEntry.java TTFSubSetFile.java
jeremias 2003/01/08 05:54:05
Modified: src/org/apache/fop/fonts/apps TTFReader.java
Added: src/org/apache/fop/fonts BFEntry.java CIDFont.java
CIDFontType.java CustomFont.java Font.java
FontDescriptor.java FontMetrics.java FontType.java
LazyFont.java MultiByteFont.java MutableFont.java
SingleByteFont.java package.html
src/org/apache/fop/fonts/truetype FontFileReader.java
TTFCmapEntry.java TTFDirTabEntry.java TTFFile.java
TTFMtxEntry.java TTFSubSetFile.java
Removed: src/org/apache/fop/fonts FontFileReader.java
TTFCmapEntry.java TTFDirTabEntry.java TTFFile.java
TTFMtxEntry.java TTFSubSetFile.java
Log:
First part of my refactoring of fonts.
TrueType font classes have moved to subpackage (like Type1 before)
Lots of Javadocs
Fixed Checkstyle errors
Revision Changes Path
1.1 xml-fop/src/org/apache/fop/fonts/BFEntry.java
Index: BFEntry.java
===================================================================
/*
* $Id: BFEntry.java,v 1.1 2003/01/08 13:54:03 jeremias Exp $
* 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;
}
}
1.1 xml-fop/src/org/apache/fop/fonts/CIDFont.java
Index: CIDFont.java
===================================================================
/*
* $Id: CIDFont.java,v 1.1 2003/01/08 13:54:03 jeremias Exp $
* 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 �
* 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 � 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;
}
}
1.1 xml-fop/src/org/apache/fop/fonts/CIDFontType.java
Index: CIDFontType.java
===================================================================
/*
* $Id: CIDFontType.java,v 1.1 2003/01/08 13:54:03 jeremias Exp $
* 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);
}
}
}
1.1 xml-fop/src/org/apache/fop/fonts/CustomFont.java
Index: CustomFont.java
===================================================================
/*
* $Id: CustomFont.java,v 1.1 2003/01/08 13:54:03 jeremias Exp $
* 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);
}
}
1.1 xml-fop/src/org/apache/fop/fonts/Font.java
Index: Font.java
===================================================================
/*
* $Id: Font.java,v 1.1 2003/01/08 13:54:03 jeremias Exp $
* 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;
}
}
1.1 xml-fop/src/org/apache/fop/fonts/FontDescriptor.java
Index: FontDescriptor.java
===================================================================
/*
* $Id: FontDescriptor.java,v 1.1 2003/01/08 13:54:03 jeremias Exp $
* 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();
}
1.1 xml-fop/src/org/apache/fop/fonts/FontMetrics.java
Index: FontMetrics.java
===================================================================
/*
* $Id: FontMetrics.java,v 1.1 2003/01/08 13:54:03 jeremias Exp $
* 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();
}
1.1 xml-fop/src/org/apache/fop/fonts/FontType.java
Index: FontType.java
===================================================================
/*
* $Id: FontType.java,v 1.1 2003/01/08 13:54:03 jeremias Exp $
* 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);
}
}
}
1.1 xml-fop/src/org/apache/fop/fonts/LazyFont.java
Index: LazyFont.java
===================================================================
/*
* $Id: LazyFont.java,v 1.1 2003/01/08 13:54:03 jeremias Exp $
* 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();
}
}
1.1 xml-fop/src/org/apache/fop/fonts/MultiByteFont.java
Index: MultiByteFont.java
===================================================================
/*
* $Id: MultiByteFont.java,v 1.1 2003/01/08 13:54:03 jeremias Exp $
* 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;
}
}
1.1 xml-fop/src/org/apache/fop/fonts/MutableFont.java
Index: MutableFont.java
===================================================================
/*
* $Id: MutableFont.java,v 1.1 2003/01/08 13:54:03 jeremias Exp $
* 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);
}
1.1 xml-fop/src/org/apache/fop/fonts/SingleByteFont.java
Index: SingleByteFont.java
===================================================================
/*
* $Id: SingleByteFont.java,v 1.1 2003/01/08 13:54:03 jeremias Exp $
* 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;
}
}
1.2 +6 -0 xml-fop/src/org/apache/fop/fonts/package.html
1.11 +4 -4 xml-fop/src/org/apache/fop/fonts/apps/TTFReader.java
Index: TTFReader.java
===================================================================
RCS file: /home/cvs/xml-fop/src/org/apache/fop/fonts/apps/TTFReader.java,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -r1.10 -r1.11
--- TTFReader.java 2 Dec 2002 14:27:58 -0000 1.10
+++ TTFReader.java 8 Jan 2003 13:54:04 -0000 1.11
@@ -25,9 +25,9 @@
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
1.1 xml-fop/src/org/apache/fop/fonts/truetype/FontFileReader.java
Index: FontFileReader.java
===================================================================
/*
* $Id: FontFileReader.java,v 1.1 2003/01/08 13:54:04 jeremias Exp $
* 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;
}
}
1.1 xml-fop/src/org/apache/fop/fonts/truetype/TTFCmapEntry.java
Index: TTFCmapEntry.java
===================================================================
/*
* $Id: TTFCmapEntry.java,v 1.1 2003/01/08 13:54:04 jeremias Exp $
* 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;
}
}
1.1 xml-fop/src/org/apache/fop/fonts/truetype/TTFDirTabEntry.java
Index: TTFDirTabEntry.java
===================================================================
/*
* $Id: TTFDirTabEntry.java,v 1.1 2003/01/08 13:54:04 jeremias Exp $
* 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;
}
}
1.1 xml-fop/src/org/apache/fop/fonts/truetype/TTFFile.java
Index: TTFFile.java
===================================================================
/*
* $Id: TTFFile.java,v 1.1 2003/01/08 13:54:04 jeremias Exp $
* 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;
}
}
1.1 xml-fop/src/org/apache/fop/fonts/truetype/TTFMtxEntry.java
Index: TTFMtxEntry.java
===================================================================
/*
* $Id: TTFMtxEntry.java,v 1.1 2003/01/08 13:54:04 jeremias Exp $
* 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;
}
}
1.1 xml-fop/src/org/apache/fop/fonts/truetype/TTFSubSetFile.java
Index: TTFSubSetFile.java
===================================================================
/*
* $Id: TTFSubSetFile.java,v 1.1 2003/01/08 13:54:04 jeremias Exp $
* 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);
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: fop-cvs-unsubscribe@xml.apache.org
For additional commands, e-mail: fop-cvs-help@xml.apache.org