import struct
from types import StringType
class _BUILDER:
	'''Virtual base helper class for structured file scanning'''
	def _get_struct_fmt(self,info):
		fmt = '<'
		for f, _, _ in info:
			fmt += f
		return fmt

	def _scan_from_file(self,f,info):
		fmt = self._get_struct_fmt(info)
		size = struct.calcsize(fmt)
		T = struct.unpack(fmt,f.read(size))
		i = 0
		for _, n, _ in info:
			setattr(self,n,T[i])
			i = i + 1

	def _dump(self,A):
		for a in A:
			print a, getattr(self,a)

	def _attr_names(self,*I):
		A = []
		for i in I:
			if type(i) is StringType:
				A.append(i)
			else:
				A.extend(map(lambda x: x[1],i))
		return A

	def _scanZTStr(self,f,loc):
		'''scan a zero terminated string from the file'''
		f.seek(loc)
		s = ''
		while 1:
			c = f.read(1)
			if c=='\000': break
			s = s+c
		return s

	def _scanN(self,N,fmt,f,loc):
		if not loc: return None
		fmt = len(fmt)==1 and ("<%d%c" % (N,fmt)) or ("<"+N*fmt)
		f.seek(loc)
		size = struct.calcsize(fmt)
		return struct.unpack(fmt,f.read(size))

	def _scanNT(self,T,N,fmt,f,loc):
		if not loc: return None
		n = len(fmt)
		X = []
		i = 0
		S = []
		for x in self._scanN(N,fmt,f,loc):
			S.append(x)
			i = i + 1
			if i==n:
				X.append(S)
				i = 0
				S = []
		return map(lambda x,T=T: apply(T,x),X)

class KernPair:
	'''hold info about a possible kerning pair'''
	def __init__(self,first,second,amount):
		self.first = first
		self.scond = second
		self.amount = amount

class KernTrack:
	def __init__(self,degree,minSize,minAmount,maxSize,maxAmount):
		'''
		degree	amount to change the character spacing. Negative values mean closer together,p
				ositive values mean farther apart. 
		minSize minimum font height (in device units) for which to use linear track kerning. 
		minAmount track kerning amount to use for font heights less or equal ktMinSize. 
		maxSize maximum font height (in device units) for which to use linear track kerning.f
				For font heights between ktMinSize and ktMaxSize the track kerning amount has 
				to increase linearily from ktMinAmount to ktMaxAmount. 
		maxAmount track kerning amount to use for font heights greater or equal ktMaxSize. 
		'''
		self.degree = degree
		self.minSize = minSize
		self.minAmount = minAmount
		self.maxSize = maxSize
		self.maxAmount = maxAmount

class PFM(_BUILDER):
	def __init__(self,fn=None):
		if fn:
			if type(fn) is StringType:
				f = open(fn,'rb')
			else:
				f = fn
			self.scan_from_file(f)
			if f is not fn: f.close()

	'''Class to hold information scanned from a type-1 .pfm file'''
	def scan_from_file(self,f):
		self._scan_from_file(f,self._header_struct_info)
		if self.dfType!=0x81: raise ValueError, "Not a Type-1 Font description"
		else: self.WidthTable = None
		self._scan_from_file(f,self._extension_struct_info)
		if not self.dfExtentTable: raise ValueError, 'dfExtentTable is zero'
		if not self.dfExtMetricsOffset: raise ValueError, 'dfExtMetricsOffset is zero'
		if self.dfDevice: self.DeviceName = self._scanZTStr(f,self.dfDevice)
		else: self.DeviceName = None
		if self.dfFace: self.FaceName = self._scanZTStr(f,self.dfFace)
		else: self.FaceName = None
		f.seek(self.dfExtMetricsOffset)
		self._scan_from_file(f, self._extTextMetrics_struct_info)
		N = self.dfLastChar - self.dfFirstChar + 1
		self.ExtentTable = self._scanN(N,'H',f,self.dfExtentTable)
		if self.dfDriverInfo: self.DriverInfo = self._scanZTStr(f,self.dfDriverInfo)
		else: self.DriverInfo = None
		if self.dfPairKernTable: self.KerningPairs = self._scanNT(KernPair,self.dfPairKernTable,'BBh',f,self.etmKernPairs)
		else: self.KerningPairs = []
		if self.dfTrackKernTable: self.KerningTracks = self._scanNT(KernTrack,self.dfTrackKernTable,'hhhhh',f,self.etmKernTracks)
		else: self.KerningTracks = []

	def dump(self):
		self._dump(
			self._attr_names(
			self._header_struct_info,'WidthTable',
			self._extension_struct_info,
			'DeviceName',
			'FaceName',
			self._extTextMetrics_struct_info,
			'DriverInfo',
			))

	_header_struct_info = (('H','dfVersion',
'''This field contains the version of the PFM file.
For PFM files that conform to this description
(namely PFM files for Type-1 fonts) the
value of this field is always 0x0100.'''),

('i','dfSize',
'''This field contains the total size of the PFM file in bytes.
Some drivers check this field and compare its value with the size of the PFM
file, and if these two values don't match the font is ignored
(I know this happens e.g. with Adobe PostScript printer drivers). '''),

('60s','dfCopyright',
'''This field contains a null-terminated copyright
string, often from the application that created the
PFM file (this normally isn't the
copyright string for the font file itself).
The unused bytes in this field should be set to zero. '''),

('H','dfType',
'''This field contains the font type. The low-order
byte is a combination of the following values
(only the values being of interest in PFM
files are given): 

0x00 (PF_RASTER_TYPE): font is a raster font 
0x01 (PF_VECTOR_TYPE): font is a vector font 
0x80 (PF_DEVICE_REALIZED): font realized by the device driver 

The high-order byte is never used in PFM files, it is always zero. 

In PFM files for Type-1 fonts the value in this field is always 0x0081. '''),

('H','dfPoints',
'''This field contains the point size at which this font
looks best. Since this is not relevant for scalable fonts
the field is ignored. The value
of this field should be set to 0x000a (10 pt). '''),

('H','dfVertRes',
'''This field contains the vertical resolution at which the
font was digitized (the value is in dots per inch).
The value of this field should be
set to 0x012C (300 dpi). '''),

('H','dfHorizRes',
'''This field contains the horizontal resolution at which
the font was digitized (the value is in dots per inch).
The value of this field should
be set to 0x012C (300 dpi). '''),

('H','dfAscent',
'''This field contains the distance from the top of a
character definition cell to the baseline of the
typographical font. It is useful for aligning the
baseline of fonts of different heights. '''),

('H','dfInternalLeading',
'''This field contains the amount of leading inside
the bounds set by the dfPixHeight field in the PFMHEADER
structure. Accent marks may occur in this area. '''),

('H','dfExternalLeading',
'''This field contains the amount of extra leading that the
designer requests the application to add between rows. Since this area is
outside the character definition cells, it contains no marks and will not be altered by text outputs. '''),

('B','dfItalic',
'''This field specifies whether this font is an italic
(or oblique) font. The low-order bit is 1 if the flag
is set, all other bits are zero. '''),

('B','dfUnderline',
'''This field specifies whether this font is an underlined
font. The low-order bit is 1 if the flag is set, all other
bits are zero. '''),

('B','dfStrikeOut',
'''This field specifies whether this font is a striked-out font.
The low-order bit is 1 if the flag is set, all other bits are zero. '''),

('H','dfWeight',
'''This field contains the weight of the characters in this font.
The value is on a scale from 0 through 1000, increments are in
steps of 100 each. The values roughly give the number of black
pixel from every 1000 pixels. Typical values are: 

0 (FW_DONTCARE): unknown or no information 
300 (FW_LIGHT): light font 
400 (FW_NORMAL): normal font 
700 (FW_BOLD): bold font '''),

('B','dfCharSet',
'''This field specifies the character set used in this font.
It can be one of the following values (probably other values
may be used here as well): 

0x00 (ANSI_CHARSET): the font uses the ANSI character set;
this means that the font implements all characters needed for the
current Windows code page (e.g. 1252). In case of a Type-1 font
this font has been created with the encoding StandardEncoding
Note that the code page number itself is not stored in the PFM file. 

0x02 (SYMBOL_CHARSET): the font uses a font-specific encoding
which will be used unchanged in displaying an printing text
using this font. In case of a Type-1 font this font has been
created with a font-specific encoding vector. Typical examples are
the Symbol and the ZapfDingbats fonts. 

0xFF (OEM_CHARSET): the font uses the OEM character set; this