Portable Compiled Format (PCF) font: Java parsing library

Portable Compiled Format (PCF) font is a bitmap font format originating from X11 Window System. It matches BDF format (which is text-based) closely, but instead being binary and platform-independent (as opposed to previously used SNF binary format) due to introduced features to handle different endianness and bit order.

The overall composition of the format is straightforward: it's more or less classic directory of type-offset-size pointers, pointing to what PCF format calls "tables". Each table carries a certain piece of information related to the font (metadata properties, metrics, bitmaps, mapping of glyphs to characters, etc).

File extension

pcf

KS implementation details

License: CC0-1.0
Minimal Kaitai Struct required: 0.9

References

This page hosts a formal specification of Portable Compiled Format (PCF) font using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Usage

Runtime library

All parsing code for Java generated by Kaitai Struct depends on the Java runtime library. You have to install it before you can parse data.

The Java runtime library is published in the Maven Central Repository. Refer to the artifact page for instructions how to add it into your project with the build tool that you use.

Code

Parse a local file and get structure in memory:

PcfFontdata=PcfFont.fromFile("path/to/local/file.pcf");

Or parse structure from a byte array:

byte[]someArray=newbyte[]{...};
PcfFontdata=newPcfFont(newByteBufferKaitaiStream(someArray));

After that, one can get various attributes from the structure by invoking getter methods like:

data.magic()// => get magic

Java source code to parse Portable Compiled Format (PCF) font

PcfFont.java

Download

// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
importio.kaitai.struct.ByteBufferKaitaiStream;
importio.kaitai.struct.KaitaiStruct;
importio.kaitai.struct.KaitaiStream;
importjava.io.IOException;
importjava.util.Map;
importjava.util.HashMap;
importjava.util.Arrays;
importjava.util.ArrayList;
importjava.util.List;
importjava.nio.charset.StandardCharsets;
/**
 * Portable Compiled Format (PCF) font is a bitmap font format
 * originating from X11 Window System. It matches BDF format (which is
 * text-based) closely, but instead being binary and
 * platform-independent (as opposed to previously used SNF binary
 * format) due to introduced features to handle different endianness
 * and bit order.
 * 
 * The overall composition of the format is straightforward: it's more
 * or less classic directory of type-offset-size pointers, pointing to
 * what PCF format calls "tables". Each table carries a certain
 * piece of information related to the font (metadata properties,
 * metrics, bitmaps, mapping of glyphs to characters, etc).
 * @see <a href="https://fontforge.org/docs/techref/pcf-format.html">Source</a>
 */
publicclass PcfFontextendsKaitaiStruct{
publicstaticPcfFontfromFile(StringfileName)throwsIOException{
returnnewPcfFont(newByteBufferKaitaiStream(fileName));
}
publicenumTypes{
PROPERTIES(1),
ACCELERATORS(2),
METRICS(4),
BITMAPS(8),
INK_METRICS(16),
BDF_ENCODINGS(32),
SWIDTHS(64),
GLYPH_NAMES(128),
BDF_ACCELERATORS(256);
privatefinallongid;
Types(longid){this.id=id;}
publiclongid(){returnid;}
privatestaticfinalMap<Long,Types>byId=newHashMap<Long,Types>(9);
static{
for(Typese:Types.values())
byId.put(e.id(),e);
}
publicstaticTypesbyId(longid){returnbyId.get(id);}
}
publicPcfFont(KaitaiStream_io){
this(_io,null,null);
}
publicPcfFont(KaitaiStream_io,KaitaiStruct_parent){
this(_io,_parent,null);
}
publicPcfFont(KaitaiStream_io,KaitaiStruct_parent,PcfFont_root){
super(_io);
this._parent=_parent;
this._root=_root==null?this:_root;
_read();
}
privatevoid_read(){
this.magic=this._io.readBytes(4);
if(!(Arrays.equals(this.magic,newbyte[]{1,102,99,112}))){
thrownewKaitaiStream.ValidationNotEqualError(newbyte[]{1,102,99,112},this.magic,this._io,"/seq/0");
}
this.numTables=this._io.readU4le();
this.tables=newArrayList<Table>();
for(inti=0;i<numTables();i++){
this.tables.add(newTable(this._io,this,_root));
}
}
publicvoid_fetchInstances(){
for(inti=0;i<this.tables.size();i++){
this.tables.get(((Number)(i)).intValue())._fetchInstances();
}
}
/**
 * Table format specifier, always 4 bytes. Original implementation treats
 * it as always little-endian and makes liberal use of bitmasking to parse
 * various parts of it.
 * 
 * TODO: this format specification recognizes endianness and bit
 * order format bits, but it does not really takes any parsing
 * decisions based on them.
 * @see <a href="https://fontforge.org/docs/techref/pcf-format.html#file-header">Source</a>
 */
publicstaticclass FormatextendsKaitaiStruct{
publicstaticFormatfromFile(StringfileName)throwsIOException{
returnnewFormat(newByteBufferKaitaiStream(fileName));
}
publicFormat(KaitaiStream_io){
this(_io,null,null);
}
publicFormat(KaitaiStream_io,KaitaiStruct_parent){
this(_io,_parent,null);
}
publicFormat(KaitaiStream_io,KaitaiStruct_parent,PcfFont_root){
super(_io);
this._parent=_parent;
this._root=_root;
_read();
}
privatevoid_read(){
this.padding1=this._io.readBitsIntBe(2);
this.scanUnitMask=this._io.readBitsIntBe(2);
this.isMostSignificantBitFirst=this._io.readBitsIntBe(1)!=0;
this.isBigEndian=this._io.readBitsIntBe(1)!=0;
this.glyphPadMask=this._io.readBitsIntBe(2);
this.format=this._io.readU1();
this.padding=this._io.readU2le();
}
publicvoid_fetchInstances(){
}
privatelongpadding1;
privatelongscanUnitMask;
privatebooleanisMostSignificantBitFirst;
privatebooleanisBigEndian;
privatelongglyphPadMask;
privateintformat;
privateintpadding;
privatePcfFont_root;
privateKaitaiStruct_parent;
publiclongpadding1(){returnpadding1;}
publiclongscanUnitMask(){returnscanUnitMask;}
publicbooleanisMostSignificantBitFirst(){returnisMostSignificantBitFirst;}
/**
 * If set, then all integers in the table are treated as big-endian
 */
publicbooleanisBigEndian(){returnisBigEndian;}
publiclongglyphPadMask(){returnglyphPadMask;}
publicintformat(){returnformat;}
publicintpadding(){returnpadding;}
publicPcfFont_root(){return_root;}
publicKaitaiStruct_parent(){return_parent;}
}
/**
 * Table offers a offset + length pointer to a particular
 * table. "Type" of table references certain enum. Applications can
 * ignore enum values which they don't support.
 */
publicstaticclass TableextendsKaitaiStruct{
publicstaticTablefromFile(StringfileName)throwsIOException{
returnnewTable(newByteBufferKaitaiStream(fileName));
}
publicTable(KaitaiStream_io){
this(_io,null,null);
}
publicTable(KaitaiStream_io,PcfFont_parent){
this(_io,_parent,null);
}
publicTable(KaitaiStream_io,PcfFont_parent,PcfFont_root){
super(_io);
this._parent=_parent;
this._root=_root;
_read();
}
privatevoid_read(){
this.type=PcfFont.Types.byId(this._io.readU4le());
this.format=newFormat(this._io,this,_root);
this.lenBody=this._io.readU4le();
this.ofsBody=this._io.readU4le();
}
publicvoid_fetchInstances(){
this.format._fetchInstances();
body();
if(this.body!=null){
{
Typeson=type();
if(on!=null){
switch(type()){
caseBDF_ENCODINGS:{
((BdfEncodings)(this.body))._fetchInstances();
break;
}
caseBITMAPS:{
((Bitmaps)(this.body))._fetchInstances();
break;
}
caseGLYPH_NAMES:{
((GlyphNames)(this.body))._fetchInstances();
break;
}
casePROPERTIES:{
((Properties)(this.body))._fetchInstances();
break;
}
caseSWIDTHS:{
((Swidths)(this.body))._fetchInstances();
break;
}
default:{
break;
}
}
}else{
}
}
}
}
/**
 * Table that allows mapping of character codes to glyphs present in the
 * font. Supports 1-byte and 2-byte character codes.
 * 
 * Note that this mapping is agnostic to character encoding itself - it
 * can represent ASCII, Unicode (ISO/IEC 10646), various single-byte
 * national encodings, etc. If application cares about it, normally
 * encoding will be specified in `properties` table, in the properties named
 * `CHARSET_REGISTRY` / `CHARSET_ENCODING`.
 * @see <a href="https://fontforge.org/docs/techref/pcf-format.html#the-encoding-table">Source</a>
 */
publicstaticclass BdfEncodingsextendsKaitaiStruct{
publicstaticBdfEncodingsfromFile(StringfileName)throwsIOException{
returnnewBdfEncodings(newByteBufferKaitaiStream(fileName));
}
publicBdfEncodings(KaitaiStream_io){
this(_io,null,null);
}
publicBdfEncodings(KaitaiStream_io,PcfFont.Table_parent){
this(_io,_parent,null);
}
publicBdfEncodings(KaitaiStream_io,PcfFont.Table_parent,PcfFont_root){
super(_io);
this._parent=_parent;
this._root=_root;
_read();
}
privatevoid_read(){
this.format=newFormat(this._io,this,_root);
this.minCharOrByte2=this._io.readU2le();
this.maxCharOrByte2=this._io.readU2le();
this.minByte1=this._io.readU2le();
this.maxByte1=this._io.readU2le();
this.defaultChar=this._io.readU2le();
this.glyphIndexes=newArrayList<Integer>();
for(inti=0;i<((maxCharOrByte2()-minCharOrByte2())+1)*((maxByte1()-minByte1())+1);i++){
this.glyphIndexes.add(this._io.readU2le());
}
}
publicvoid_fetchInstances(){
this.format._fetchInstances();
for(inti=0;i<this.glyphIndexes.size();i++){
}
}
privateFormatformat;
privateintminCharOrByte2;
privateintmaxCharOrByte2;
privateintminByte1;
privateintmaxByte1;
privateintdefaultChar;
privateList<Integer>glyphIndexes;
privatePcfFont_root;
privatePcfFont.Table_parent;
publicFormatformat(){returnformat;}
publicintminCharOrByte2(){returnminCharOrByte2;}
publicintmaxCharOrByte2(){returnmaxCharOrByte2;}
publicintminByte1(){returnminByte1;}
publicintmaxByte1(){returnmaxByte1;}
publicintdefaultChar(){returndefaultChar;}
publicList<Integer>glyphIndexes(){returnglyphIndexes;}
publicPcfFont_root(){return_root;}
publicPcfFont.Table_parent(){return_parent;}
}
/**
 * Table containing uncompressed glyph bitmaps.
 * @see <a href="https://fontforge.org/docs/techref/pcf-format.html#the-bitmap-table">Source</a>
 */
publicstaticclass BitmapsextendsKaitaiStruct{
publicstaticBitmapsfromFile(StringfileName)throwsIOException{
returnnewBitmaps(newByteBufferKaitaiStream(fileName));
}
publicBitmaps(KaitaiStream_io){
this(_io,null,null);
}
publicBitmaps(KaitaiStream_io,PcfFont.Table_parent){
this(_io,_parent,null);
}
publicBitmaps(KaitaiStream_io,PcfFont.Table_parent,PcfFont_root){
super(_io);
this._parent=_parent;
this._root=_root;
_read();
}
privatevoid_read(){
this.format=newFormat(this._io,this,_root);
this.numGlyphs=this._io.readU4le();
this.offsets=newArrayList<Long>();
for(inti=0;i<numGlyphs();i++){
this.offsets.add(this._io.readU4le());
}
this.bitmapSizes=newArrayList<Long>();
for(inti=0;i<4;i++){
this.bitmapSizes.add(this._io.readU4le());
}
}
publicvoid_fetchInstances(){
this.format._fetchInstances();
for(inti=0;i<this.offsets.size();i++){
}
for(inti=0;i<this.bitmapSizes.size();i++){
}
}
privateFormatformat;
privatelongnumGlyphs;
privateList<Long>offsets;
privateList<Long>bitmapSizes;
privatePcfFont_root;
privatePcfFont.Table_parent;
publicFormatformat(){returnformat;}
publiclongnumGlyphs(){returnnumGlyphs;}
publicList<Long>offsets(){returnoffsets;}
publicList<Long>bitmapSizes(){returnbitmapSizes;}
publicPcfFont_root(){return_root;}
publicPcfFont.Table_parent(){return_parent;}
}
/**
 * Table containing character names for every glyph.
 * @see <a href="https://fontforge.org/docs/techref/pcf-format.html#the-glyph-names-table">Source</a>
 */
publicstaticclass GlyphNamesextendsKaitaiStruct{
publicstaticGlyphNamesfromFile(StringfileName)throwsIOException{
returnnewGlyphNames(newByteBufferKaitaiStream(fileName));
}
publicGlyphNames(KaitaiStream_io){
this(_io,null,null);
}
publicGlyphNames(KaitaiStream_io,PcfFont.Table_parent){
this(_io,_parent,null);
}
publicGlyphNames(KaitaiStream_io,PcfFont.Table_parent,PcfFont_root){
super(_io);
this._parent=_parent;
this._root=_root;
_read();
}
privatevoid_read(){
this.format=newFormat(this._io,this,_root);
this.numGlyphs=this._io.readU4le();
this.names=newArrayList<StringRef>();
for(inti=0;i<numGlyphs();i++){
this.names.add(newStringRef(this._io,this,_root));
}
this.lenStrings=this._io.readU4le();
KaitaiStream_io_strings=this._io.substream(lenStrings());
this.strings=newBytesWithIo(_io_strings);
}
publicvoid_fetchInstances(){
this.format._fetchInstances();
for(inti=0;i<this.names.size();i++){
this.names.get(((Number)(i)).intValue())._fetchInstances();
}
this.strings._fetchInstances();
}
publicstaticclass StringRefextendsKaitaiStruct{
publicstaticStringReffromFile(StringfileName)throwsIOException{
returnnewStringRef(newByteBufferKaitaiStream(fileName));
}
publicStringRef(KaitaiStream_io){
this(_io,null,null);
}
publicStringRef(KaitaiStream_io,PcfFont.Table.GlyphNames_parent){
this(_io,_parent,null);
}
publicStringRef(KaitaiStream_io,PcfFont.Table.GlyphNames_parent,PcfFont_root){
super(_io);
this._parent=_parent;
this._root=_root;
_read();
}
privatevoid_read(){
this.ofsString=this._io.readU4le();
}
publicvoid_fetchInstances(){
value();
if(this.value!=null){
}
}
privateStringvalue;
publicStringvalue(){
if(this.value!=null)
returnthis.value;
KaitaiStreamio=_parent().strings()._io();
long_pos=io.pos();
io.seek(ofsString());
this.value=newString(io.readBytesTerm((byte)0,false,true,true),StandardCharsets.UTF_8);
io.seek(_pos);
returnthis.value;
}
privatelongofsString;
privatePcfFont_root;
privatePcfFont.Table.GlyphNames_parent;
publiclongofsString(){returnofsString;}
publicPcfFont_root(){return_root;}
publicPcfFont.Table.GlyphNames_parent(){return_parent;}
}
privateFormatformat;
privatelongnumGlyphs;
privateList<StringRef>names;
privatelonglenStrings;
privateBytesWithIostrings;
privatePcfFont_root;
privatePcfFont.Table_parent;
publicFormatformat(){returnformat;}
publiclongnumGlyphs(){returnnumGlyphs;}
/**
 * Glyph names are represented as string references in strings buffer.
 */
publicList<StringRef>names(){returnnames;}
publiclonglenStrings(){returnlenStrings;}
/**
 * Strings buffer which contains all glyph names.
 */
publicBytesWithIostrings(){returnstrings;}
publicPcfFont_root(){return_root;}
publicPcfFont.Table_parent(){return_parent;}
}
/**
 * Array of properties (key-value pairs), used to convey different X11
 * settings of a font. Key is always an X font atom.
 * @see <a href="https://fontforge.org/docs/techref/pcf-format.html#properties-table">Source</a>
 */
publicstaticclass PropertiesextendsKaitaiStruct{
publicstaticPropertiesfromFile(StringfileName)throwsIOException{
returnnewProperties(newByteBufferKaitaiStream(fileName));
}
publicProperties(KaitaiStream_io){
this(_io,null,null);
}
publicProperties(KaitaiStream_io,PcfFont.Table_parent){
this(_io,_parent,null);
}
publicProperties(KaitaiStream_io,PcfFont.Table_parent,PcfFont_root){
super(_io);
this._parent=_parent;
this._root=_root;
_read();
}
privatevoid_read(){
this.format=newFormat(this._io,this,_root);
this.numProps=this._io.readU4le();
this.props=newArrayList<Prop>();
for(inti=0;i<numProps();i++){
this.props.add(newProp(this._io,this,_root));
}
this.padding=this._io.readBytes(((numProps()&3)==0?0:4-(numProps()&3)));
this.lenStrings=this._io.readU4le();
KaitaiStream_io_strings=this._io.substream(lenStrings());
this.strings=newBytesWithIo(_io_strings);
}
publicvoid_fetchInstances(){
this.format._fetchInstances();
for(inti=0;i<this.props.size();i++){
this.props.get(((Number)(i)).intValue())._fetchInstances();
}
this.strings._fetchInstances();
}
/**
 * Property is a key-value pair, "key" being always a
 * string and "value" being either a string or a 32-bit
 * integer based on an additinal flag (`is_string`).
 * 
 * Simple offset-based mechanism is employed to keep this
 * type's sequence fixed-sized and thus have simple access
 * to property key/value by index.
 */
publicstaticclass PropextendsKaitaiStruct{
publicstaticPropfromFile(StringfileName)throwsIOException{
returnnewProp(newByteBufferKaitaiStream(fileName));
}
publicProp(KaitaiStream_io){
this(_io,null,null);
}
publicProp(KaitaiStream_io,PcfFont.Table.Properties_parent){
this(_io,_parent,null);
}
publicProp(KaitaiStream_io,PcfFont.Table.Properties_parent,PcfFont_root){
super(_io);
this._parent=_parent;
this._root=_root;
_read();
}
privatevoid_read(){
this.ofsName=this._io.readU4le();
this.isString=this._io.readU1();
this.valueOrOfsValue=this._io.readU4le();
}
publicvoid_fetchInstances(){
name();
if(this.name!=null){
}
strValue();
if(this.strValue!=null){
}
}
privateLongintValue;
/**
 * Value of the property, if this is an integer value.
 */
publicLongintValue(){
if(this.intValue!=null)
returnthis.intValue;
if(isString()==0){
this.intValue=((Number)(valueOrOfsValue())).longValue();
}
returnthis.intValue;
}
privateStringname;
/**
 * Name of the property addressed in the strings buffer.
 */
publicStringname(){
if(this.name!=null)
returnthis.name;
KaitaiStreamio=_parent().strings()._io();
long_pos=io.pos();
io.seek(ofsName());
this.name=newString(io.readBytesTerm((byte)0,false,true,true),StandardCharsets.UTF_8);
io.seek(_pos);
returnthis.name;
}
privateStringstrValue;
/**
 * Value of the property addressed in the strings
 * buffer, if this is a string value.
 */
publicStringstrValue(){
if(this.strValue!=null)
returnthis.strValue;
if(isString()!=0){
KaitaiStreamio=_parent().strings()._io();
long_pos=io.pos();
io.seek(valueOrOfsValue());
this.strValue=newString(io.readBytesTerm((byte)0,false,true,true),StandardCharsets.UTF_8);
io.seek(_pos);
}
returnthis.strValue;
}
privatelongofsName;
privateintisString;
privatelongvalueOrOfsValue;
privatePcfFont_root;
privatePcfFont.Table.Properties_parent;
/**
 * Offset to name in the strings buffer.
 */
publiclongofsName(){returnofsName;}
/**
 * Designates if value is an integer (zero) or a string (non-zero).
 */
publicintisString(){returnisString;}
/**
 * If the value is an integer (`is_string` is false),
 * then it's stored here. If the value is a string
 * (`is_string` is true), then it stores offset to the
 * value in the strings buffer.
 */
publiclongvalueOrOfsValue(){returnvalueOrOfsValue;}
publicPcfFont_root(){return_root;}
publicPcfFont.Table.Properties_parent(){return_parent;}
}
privateFormatformat;
privatelongnumProps;
privateList<Prop>props;
privatebyte[]padding;
privatelonglenStrings;
privateBytesWithIostrings;
privatePcfFont_root;
privatePcfFont.Table_parent;
publicFormatformat(){returnformat;}
publiclongnumProps(){returnnumProps;}
publicList<Prop>props(){returnprops;}
publicbyte[]padding(){returnpadding;}
publiclonglenStrings(){returnlenStrings;}
/**
 * Strings buffer. Never used directly, but instead is
 * addressed by offsets from the properties.
 */
publicBytesWithIostrings(){returnstrings;}
publicPcfFont_root(){return_root;}
publicPcfFont.Table_parent(){return_parent;}
}
/**
 * Table containing scalable widths of characters.
 * @see <a href="https://fontforge.org/docs/techref/pcf-format.html#the-scalable-widths-table">Source</a>
 */
publicstaticclass SwidthsextendsKaitaiStruct{
publicstaticSwidthsfromFile(StringfileName)throwsIOException{
returnnewSwidths(newByteBufferKaitaiStream(fileName));
}
publicSwidths(KaitaiStream_io){
this(_io,null,null);
}
publicSwidths(KaitaiStream_io,PcfFont.Table_parent){
this(_io,_parent,null);
}
publicSwidths(KaitaiStream_io,PcfFont.Table_parent,PcfFont_root){
super(_io);
this._parent=_parent;
this._root=_root;
_read();
}
privatevoid_read(){
this.format=newFormat(this._io,this,_root);
this.numGlyphs=this._io.readU4le();
this.swidths=newArrayList<Long>();
for(inti=0;i<numGlyphs();i++){
this.swidths.add(this._io.readU4le());
}
}
publicvoid_fetchInstances(){
this.format._fetchInstances();
for(inti=0;i<this.swidths.size();i++){
}
}
privateFormatformat;
privatelongnumGlyphs;
privateList<Long>swidths;
privatePcfFont_root;
privatePcfFont.Table_parent;
publicFormatformat(){returnformat;}
publiclongnumGlyphs(){returnnumGlyphs;}
/**
 * The scalable width of a character is the width of the corresponding
 * PostScript character in em-units (1/1000ths of an em).
 */
publicList<Long>swidths(){returnswidths;}
publicPcfFont_root(){return_root;}
publicPcfFont.Table_parent(){return_parent;}
}
privateObjectbody;
publicObjectbody(){
if(this.body!=null)
returnthis.body;
long_pos=this._io.pos();
this._io.seek(ofsBody());
{
Typeson=type();
if(on!=null){
switch(type()){
caseBDF_ENCODINGS:{
KaitaiStream_io_body=this._io.substream(lenBody());
this.body=newBdfEncodings(_io_body,this,_root);
break;
}
caseBITMAPS:{
KaitaiStream_io_body=this._io.substream(lenBody());
this.body=newBitmaps(_io_body,this,_root);
break;
}
caseGLYPH_NAMES:{
KaitaiStream_io_body=this._io.substream(lenBody());
this.body=newGlyphNames(_io_body,this,_root);
break;
}
casePROPERTIES:{
KaitaiStream_io_body=this._io.substream(lenBody());
this.body=newProperties(_io_body,this,_root);
break;
}
caseSWIDTHS:{
KaitaiStream_io_body=this._io.substream(lenBody());
this.body=newSwidths(_io_body,this,_root);
break;
}
default:{
this.body=this._io.readBytes(lenBody());
break;
}
}
}else{
this.body=this._io.readBytes(lenBody());
}
}
this._io.seek(_pos);
returnthis.body;
}
privateTypestype;
privateFormatformat;
privatelonglenBody;
privatelongofsBody;
privatePcfFont_root;
privatePcfFont_parent;
publicTypestype(){returntype;}
publicFormatformat(){returnformat;}
publiclonglenBody(){returnlenBody;}
publiclongofsBody(){returnofsBody;}
publicPcfFont_root(){return_root;}
publicPcfFont_parent(){return_parent;}
}
privatebyte[]magic;
privatelongnumTables;
privateList<Table>tables;
privatePcfFont_root;
privateKaitaiStruct_parent;
publicbyte[]magic(){returnmagic;}
publiclongnumTables(){returnnumTables;}
publicList<Table>tables(){returntables;}
publicPcfFont_root(){return_root;}
publicKaitaiStruct_parent(){return_parent;}
}

AltStyle によって変換されたページ (->オリジナル) /