diff --git a/VERSION b/VERSION index 502482d1..8c57b9ad 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.02.17.09.49.04 +Version 2.4.1-alpha.2+timestamp.2013.02.17.11.27.22 diff --git a/gluon/contrib/fpdf/__init__.py b/gluon/contrib/fpdf/__init__.py index e0ffc017..3ba17860 100644 --- a/gluon/contrib/fpdf/__init__.py +++ b/gluon/contrib/fpdf/__init__.py @@ -14,5 +14,3 @@ except ImportError: warnings.warn("web2py gluon package not installed, required for html2pdf") from template import Template - - diff --git a/gluon/contrib/fpdf/fonts.py b/gluon/contrib/fpdf/fonts.py index f584a1e8..b30ca3d3 100644 --- a/gluon/contrib/fpdf/fonts.py +++ b/gluon/contrib/fpdf/fonts.py @@ -1,157 +1,156 @@ -#!/usr/bin/env python -# -*- coding: latin-1 -*- - -# Fonts: - -fpdf_charwidths = {} - -fpdf_charwidths['courier']={} - -for i in xrange(0,256): - fpdf_charwidths['courier'][chr(i)]=600 - fpdf_charwidths['courierB']=fpdf_charwidths['courier'] - fpdf_charwidths['courierI']=fpdf_charwidths['courier'] - fpdf_charwidths['courierBI']=fpdf_charwidths['courier'] - -fpdf_charwidths['helvetica']={ - '\x00':278,'\x01':278,'\x02':278,'\x03':278,'\x04':278,'\x05':278,'\x06':278,'\x07':278,'\x08':278,'\t':278,'\n':278,'\x0b':278,'\x0c':278,'\r':278,'\x0e':278,'\x0f':278,'\x10':278,'\x11':278,'\x12':278,'\x13':278,'\x14':278,'\x15':278, - '\x16':278,'\x17':278,'\x18':278,'\x19':278,'\x1a':278,'\x1b':278,'\x1c':278,'\x1d':278,'\x1e':278,'\x1f':278,' ':278,'!':278,'"':355,'#':556,'$':556,'%':889,'&':667,'\'':191,'(':333,')':333,'*':389,'+':584, - ',':278,'-':333,'.':278,'/':278,'0':556,'1':556,'2':556,'3':556,'4':556,'5':556,'6':556,'7':556,'8':556,'9':556,':':278,';':278,'<':584,'=':584,'>':584,'?':556,'@':1015,'A':667, - 'B':667,'C':722,'D':722,'E':667,'F':611,'G':778,'H':722,'I':278,'J':500,'K':667,'L':556,'M':833,'N':722,'O':778,'P':667,'Q':778,'R':722,'S':667,'T':611,'U':722,'V':667,'W':944, - 'X':667,'Y':667,'Z':611,'[':278,'\\':278,']':278,'^':469,'_':556,'`':333,'a':556,'b':556,'c':500,'d':556,'e':556,'f':278,'g':556,'h':556,'i':222,'j':222,'k':500,'l':222,'m':833, - 'n':556,'o':556,'p':556,'q':556,'r':333,'s':500,'t':278,'u':556,'v':500,'w':722,'x':500,'y':500,'z':500,'{':334,'|':260,'}':334,'~':584,'\x7f':350,'\x80':556,'\x81':350,'\x82':222,'\x83':556, - '\x84':333,'\x85':1000,'\x86':556,'\x87':556,'\x88':333,'\x89':1000,'\x8a':667,'\x8b':333,'\x8c':1000,'\x8d':350,'\x8e':611,'\x8f':350,'\x90':350,'\x91':222,'\x92':222,'\x93':333,'\x94':333,'\x95':350,'\x96':556,'\x97':1000,'\x98':333,'\x99':1000, - '\x9a':500,'\x9b':333,'\x9c':944,'\x9d':350,'\x9e':500,'\x9f':667,'\xa0':278,'\xa1':333,'\xa2':556,'\xa3':556,'\xa4':556,'\xa5':556,'\xa6':260,'\xa7':556,'\xa8':333,'\xa9':737,'\xaa':370,'\xab':556,'\xac':584,'\xad':333,'\xae':737,'\xaf':333, - '\xb0':400,'\xb1':584,'\xb2':333,'\xb3':333,'\xb4':333,'\xb5':556,'\xb6':537,'\xb7':278,'\xb8':333,'\xb9':333,'\xba':365,'\xbb':556,'\xbc':834,'\xbd':834,'\xbe':834,'\xbf':611,'\xc0':667,'\xc1':667,'\xc2':667,'\xc3':667,'\xc4':667,'\xc5':667, - '\xc6':1000,'\xc7':722,'\xc8':667,'\xc9':667,'\xca':667,'\xcb':667,'\xcc':278,'\xcd':278,'\xce':278,'\xcf':278,'\xd0':722,'\xd1':722,'\xd2':778,'\xd3':778,'\xd4':778,'\xd5':778,'\xd6':778,'\xd7':584,'\xd8':778,'\xd9':722,'\xda':722,'\xdb':722, - '\xdc':722,'\xdd':667,'\xde':667,'\xdf':611,'\xe0':556,'\xe1':556,'\xe2':556,'\xe3':556,'\xe4':556,'\xe5':556,'\xe6':889,'\xe7':500,'\xe8':556,'\xe9':556,'\xea':556,'\xeb':556,'\xec':278,'\xed':278,'\xee':278,'\xef':278,'\xf0':556,'\xf1':556, - '\xf2':556,'\xf3':556,'\xf4':556,'\xf5':556,'\xf6':556,'\xf7':584,'\xf8':611,'\xf9':556,'\xfa':556,'\xfb':556,'\xfc':556,'\xfd':500,'\xfe':556,'\xff':500} - -fpdf_charwidths['helveticaB']={ - '\x00':278,'\x01':278,'\x02':278,'\x03':278,'\x04':278,'\x05':278,'\x06':278,'\x07':278,'\x08':278,'\t':278,'\n':278,'\x0b':278,'\x0c':278,'\r':278,'\x0e':278,'\x0f':278,'\x10':278,'\x11':278,'\x12':278,'\x13':278,'\x14':278,'\x15':278, - '\x16':278,'\x17':278,'\x18':278,'\x19':278,'\x1a':278,'\x1b':278,'\x1c':278,'\x1d':278,'\x1e':278,'\x1f':278,' ':278,'!':333,'"':474,'#':556,'$':556,'%':889,'&':722,'\'':238,'(':333,')':333,'*':389,'+':584, - ',':278,'-':333,'.':278,'/':278,'0':556,'1':556,'2':556,'3':556,'4':556,'5':556,'6':556,'7':556,'8':556,'9':556,':':333,';':333,'<':584,'=':584,'>':584,'?':611,'@':975,'A':722, - 'B':722,'C':722,'D':722,'E':667,'F':611,'G':778,'H':722,'I':278,'J':556,'K':722,'L':611,'M':833,'N':722,'O':778,'P':667,'Q':778,'R':722,'S':667,'T':611,'U':722,'V':667,'W':944, - 'X':667,'Y':667,'Z':611,'[':333,'\\':278,']':333,'^':584,'_':556,'`':333,'a':556,'b':611,'c':556,'d':611,'e':556,'f':333,'g':611,'h':611,'i':278,'j':278,'k':556,'l':278,'m':889, - 'n':611,'o':611,'p':611,'q':611,'r':389,'s':556,'t':333,'u':611,'v':556,'w':778,'x':556,'y':556,'z':500,'{':389,'|':280,'}':389,'~':584,'\x7f':350,'\x80':556,'\x81':350,'\x82':278,'\x83':556, - '\x84':500,'\x85':1000,'\x86':556,'\x87':556,'\x88':333,'\x89':1000,'\x8a':667,'\x8b':333,'\x8c':1000,'\x8d':350,'\x8e':611,'\x8f':350,'\x90':350,'\x91':278,'\x92':278,'\x93':500,'\x94':500,'\x95':350,'\x96':556,'\x97':1000,'\x98':333,'\x99':1000, - '\x9a':556,'\x9b':333,'\x9c':944,'\x9d':350,'\x9e':500,'\x9f':667,'\xa0':278,'\xa1':333,'\xa2':556,'\xa3':556,'\xa4':556,'\xa5':556,'\xa6':280,'\xa7':556,'\xa8':333,'\xa9':737,'\xaa':370,'\xab':556,'\xac':584,'\xad':333,'\xae':737,'\xaf':333, - '\xb0':400,'\xb1':584,'\xb2':333,'\xb3':333,'\xb4':333,'\xb5':611,'\xb6':556,'\xb7':278,'\xb8':333,'\xb9':333,'\xba':365,'\xbb':556,'\xbc':834,'\xbd':834,'\xbe':834,'\xbf':611,'\xc0':722,'\xc1':722,'\xc2':722,'\xc3':722,'\xc4':722,'\xc5':722, - '\xc6':1000,'\xc7':722,'\xc8':667,'\xc9':667,'\xca':667,'\xcb':667,'\xcc':278,'\xcd':278,'\xce':278,'\xcf':278,'\xd0':722,'\xd1':722,'\xd2':778,'\xd3':778,'\xd4':778,'\xd5':778,'\xd6':778,'\xd7':584,'\xd8':778,'\xd9':722,'\xda':722,'\xdb':722, - '\xdc':722,'\xdd':667,'\xde':667,'\xdf':611,'\xe0':556,'\xe1':556,'\xe2':556,'\xe3':556,'\xe4':556,'\xe5':556,'\xe6':889,'\xe7':556,'\xe8':556,'\xe9':556,'\xea':556,'\xeb':556,'\xec':278,'\xed':278,'\xee':278,'\xef':278,'\xf0':611,'\xf1':611, - '\xf2':611,'\xf3':611,'\xf4':611,'\xf5':611,'\xf6':611,'\xf7':584,'\xf8':611,'\xf9':611,'\xfa':611,'\xfb':611,'\xfc':611,'\xfd':556,'\xfe':611,'\xff':556 -} - -fpdf_charwidths['helveticaBI']={ - '\x00':278,'\x01':278,'\x02':278,'\x03':278,'\x04':278,'\x05':278,'\x06':278,'\x07':278,'\x08':278,'\t':278,'\n':278,'\x0b':278,'\x0c':278,'\r':278,'\x0e':278,'\x0f':278,'\x10':278,'\x11':278,'\x12':278,'\x13':278,'\x14':278,'\x15':278, - '\x16':278,'\x17':278,'\x18':278,'\x19':278,'\x1a':278,'\x1b':278,'\x1c':278,'\x1d':278,'\x1e':278,'\x1f':278,' ':278,'!':333,'"':474,'#':556,'$':556,'%':889,'&':722,'\'':238,'(':333,')':333,'*':389,'+':584, - ',':278,'-':333,'.':278,'/':278,'0':556,'1':556,'2':556,'3':556,'4':556,'5':556,'6':556,'7':556,'8':556,'9':556,':':333,';':333,'<':584,'=':584,'>':584,'?':611,'@':975,'A':722, - 'B':722,'C':722,'D':722,'E':667,'F':611,'G':778,'H':722,'I':278,'J':556,'K':722,'L':611,'M':833,'N':722,'O':778,'P':667,'Q':778,'R':722,'S':667,'T':611,'U':722,'V':667,'W':944, - 'X':667,'Y':667,'Z':611,'[':333,'\\':278,']':333,'^':584,'_':556,'`':333,'a':556,'b':611,'c':556,'d':611,'e':556,'f':333,'g':611,'h':611,'i':278,'j':278,'k':556,'l':278,'m':889, - 'n':611,'o':611,'p':611,'q':611,'r':389,'s':556,'t':333,'u':611,'v':556,'w':778,'x':556,'y':556,'z':500,'{':389,'|':280,'}':389,'~':584,'\x7f':350,'\x80':556,'\x81':350,'\x82':278,'\x83':556, - '\x84':500,'\x85':1000,'\x86':556,'\x87':556,'\x88':333,'\x89':1000,'\x8a':667,'\x8b':333,'\x8c':1000,'\x8d':350,'\x8e':611,'\x8f':350,'\x90':350,'\x91':278,'\x92':278,'\x93':500,'\x94':500,'\x95':350,'\x96':556,'\x97':1000,'\x98':333,'\x99':1000, - '\x9a':556,'\x9b':333,'\x9c':944,'\x9d':350,'\x9e':500,'\x9f':667,'\xa0':278,'\xa1':333,'\xa2':556,'\xa3':556,'\xa4':556,'\xa5':556,'\xa6':280,'\xa7':556,'\xa8':333,'\xa9':737,'\xaa':370,'\xab':556,'\xac':584,'\xad':333,'\xae':737,'\xaf':333, - '\xb0':400,'\xb1':584,'\xb2':333,'\xb3':333,'\xb4':333,'\xb5':611,'\xb6':556,'\xb7':278,'\xb8':333,'\xb9':333,'\xba':365,'\xbb':556,'\xbc':834,'\xbd':834,'\xbe':834,'\xbf':611,'\xc0':722,'\xc1':722,'\xc2':722,'\xc3':722,'\xc4':722,'\xc5':722, - '\xc6':1000,'\xc7':722,'\xc8':667,'\xc9':667,'\xca':667,'\xcb':667,'\xcc':278,'\xcd':278,'\xce':278,'\xcf':278,'\xd0':722,'\xd1':722,'\xd2':778,'\xd3':778,'\xd4':778,'\xd5':778,'\xd6':778,'\xd7':584,'\xd8':778,'\xd9':722,'\xda':722,'\xdb':722, - '\xdc':722,'\xdd':667,'\xde':667,'\xdf':611,'\xe0':556,'\xe1':556,'\xe2':556,'\xe3':556,'\xe4':556,'\xe5':556,'\xe6':889,'\xe7':556,'\xe8':556,'\xe9':556,'\xea':556,'\xeb':556,'\xec':278,'\xed':278,'\xee':278,'\xef':278,'\xf0':611,'\xf1':611, - '\xf2':611,'\xf3':611,'\xf4':611,'\xf5':611,'\xf6':611,'\xf7':584,'\xf8':611,'\xf9':611,'\xfa':611,'\xfb':611,'\xfc':611,'\xfd':556,'\xfe':611,'\xff':556} - -fpdf_charwidths['helveticaI']={ - '\x00':278,'\x01':278,'\x02':278,'\x03':278,'\x04':278,'\x05':278,'\x06':278,'\x07':278,'\x08':278,'\t':278,'\n':278,'\x0b':278,'\x0c':278,'\r':278,'\x0e':278,'\x0f':278,'\x10':278,'\x11':278,'\x12':278,'\x13':278,'\x14':278,'\x15':278, - '\x16':278,'\x17':278,'\x18':278,'\x19':278,'\x1a':278,'\x1b':278,'\x1c':278,'\x1d':278,'\x1e':278,'\x1f':278,' ':278,'!':278,'"':355,'#':556,'$':556,'%':889,'&':667,'\'':191,'(':333,')':333,'*':389,'+':584, - ',':278,'-':333,'.':278,'/':278,'0':556,'1':556,'2':556,'3':556,'4':556,'5':556,'6':556,'7':556,'8':556,'9':556,':':278,';':278,'<':584,'=':584,'>':584,'?':556,'@':1015,'A':667, - 'B':667,'C':722,'D':722,'E':667,'F':611,'G':778,'H':722,'I':278,'J':500,'K':667,'L':556,'M':833,'N':722,'O':778,'P':667,'Q':778,'R':722,'S':667,'T':611,'U':722,'V':667,'W':944, - 'X':667,'Y':667,'Z':611,'[':278,'\\':278,']':278,'^':469,'_':556,'`':333,'a':556,'b':556,'c':500,'d':556,'e':556,'f':278,'g':556,'h':556,'i':222,'j':222,'k':500,'l':222,'m':833, - 'n':556,'o':556,'p':556,'q':556,'r':333,'s':500,'t':278,'u':556,'v':500,'w':722,'x':500,'y':500,'z':500,'{':334,'|':260,'}':334,'~':584,'\x7f':350,'\x80':556,'\x81':350,'\x82':222,'\x83':556, - '\x84':333,'\x85':1000,'\x86':556,'\x87':556,'\x88':333,'\x89':1000,'\x8a':667,'\x8b':333,'\x8c':1000,'\x8d':350,'\x8e':611,'\x8f':350,'\x90':350,'\x91':222,'\x92':222,'\x93':333,'\x94':333,'\x95':350,'\x96':556,'\x97':1000,'\x98':333,'\x99':1000, - '\x9a':500,'\x9b':333,'\x9c':944,'\x9d':350,'\x9e':500,'\x9f':667,'\xa0':278,'\xa1':333,'\xa2':556,'\xa3':556,'\xa4':556,'\xa5':556,'\xa6':260,'\xa7':556,'\xa8':333,'\xa9':737,'\xaa':370,'\xab':556,'\xac':584,'\xad':333,'\xae':737,'\xaf':333, - '\xb0':400,'\xb1':584,'\xb2':333,'\xb3':333,'\xb4':333,'\xb5':556,'\xb6':537,'\xb7':278,'\xb8':333,'\xb9':333,'\xba':365,'\xbb':556,'\xbc':834,'\xbd':834,'\xbe':834,'\xbf':611,'\xc0':667,'\xc1':667,'\xc2':667,'\xc3':667,'\xc4':667,'\xc5':667, - '\xc6':1000,'\xc7':722,'\xc8':667,'\xc9':667,'\xca':667,'\xcb':667,'\xcc':278,'\xcd':278,'\xce':278,'\xcf':278,'\xd0':722,'\xd1':722,'\xd2':778,'\xd3':778,'\xd4':778,'\xd5':778,'\xd6':778,'\xd7':584,'\xd8':778,'\xd9':722,'\xda':722,'\xdb':722, - '\xdc':722,'\xdd':667,'\xde':667,'\xdf':611,'\xe0':556,'\xe1':556,'\xe2':556,'\xe3':556,'\xe4':556,'\xe5':556,'\xe6':889,'\xe7':500,'\xe8':556,'\xe9':556,'\xea':556,'\xeb':556,'\xec':278,'\xed':278,'\xee':278,'\xef':278,'\xf0':556,'\xf1':556, - '\xf2':556,'\xf3':556,'\xf4':556,'\xf5':556,'\xf6':556,'\xf7':584,'\xf8':611,'\xf9':556,'\xfa':556,'\xfb':556,'\xfc':556,'\xfd':500,'\xfe':556,'\xff':500} - -fpdf_charwidths['symbol']={ - '\x00':250,'\x01':250,'\x02':250,'\x03':250,'\x04':250,'\x05':250,'\x06':250,'\x07':250,'\x08':250,'\t':250,'\n':250,'\x0b':250,'\x0c':250,'\r':250,'\x0e':250,'\x0f':250,'\x10':250,'\x11':250,'\x12':250,'\x13':250,'\x14':250,'\x15':250, - '\x16':250,'\x17':250,'\x18':250,'\x19':250,'\x1a':250,'\x1b':250,'\x1c':250,'\x1d':250,'\x1e':250,'\x1f':250,' ':250,'!':333,'"':713,'#':500,'$':549,'%':833,'&':778,'\'':439,'(':333,')':333,'*':500,'+':549, - ',':250,'-':549,'.':250,'/':278,'0':500,'1':500,'2':500,'3':500,'4':500,'5':500,'6':500,'7':500,'8':500,'9':500,':':278,';':278,'<':549,'=':549,'>':549,'?':444,'@':549,'A':722, - 'B':667,'C':722,'D':612,'E':611,'F':763,'G':603,'H':722,'I':333,'J':631,'K':722,'L':686,'M':889,'N':722,'O':722,'P':768,'Q':741,'R':556,'S':592,'T':611,'U':690,'V':439,'W':768, - 'X':645,'Y':795,'Z':611,'[':333,'\\':863,']':333,'^':658,'_':500,'`':500,'a':631,'b':549,'c':549,'d':494,'e':439,'f':521,'g':411,'h':603,'i':329,'j':603,'k':549,'l':549,'m':576, - 'n':521,'o':549,'p':549,'q':521,'r':549,'s':603,'t':439,'u':576,'v':713,'w':686,'x':493,'y':686,'z':494,'{':480,'|':200,'}':480,'~':549,'\x7f':0,'\x80':0,'\x81':0,'\x82':0,'\x83':0, - '\x84':0,'\x85':0,'\x86':0,'\x87':0,'\x88':0,'\x89':0,'\x8a':0,'\x8b':0,'\x8c':0,'\x8d':0,'\x8e':0,'\x8f':0,'\x90':0,'\x91':0,'\x92':0,'\x93':0,'\x94':0,'\x95':0,'\x96':0,'\x97':0,'\x98':0,'\x99':0, - '\x9a':0,'\x9b':0,'\x9c':0,'\x9d':0,'\x9e':0,'\x9f':0,'\xa0':750,'\xa1':620,'\xa2':247,'\xa3':549,'\xa4':167,'\xa5':713,'\xa6':500,'\xa7':753,'\xa8':753,'\xa9':753,'\xaa':753,'\xab':1042,'\xac':987,'\xad':603,'\xae':987,'\xaf':603, - '\xb0':400,'\xb1':549,'\xb2':411,'\xb3':549,'\xb4':549,'\xb5':713,'\xb6':494,'\xb7':460,'\xb8':549,'\xb9':549,'\xba':549,'\xbb':549,'\xbc':1000,'\xbd':603,'\xbe':1000,'\xbf':658,'\xc0':823,'\xc1':686,'\xc2':795,'\xc3':987,'\xc4':768,'\xc5':768, - '\xc6':823,'\xc7':768,'\xc8':768,'\xc9':713,'\xca':713,'\xcb':713,'\xcc':713,'\xcd':713,'\xce':713,'\xcf':713,'\xd0':768,'\xd1':713,'\xd2':790,'\xd3':790,'\xd4':890,'\xd5':823,'\xd6':549,'\xd7':250,'\xd8':713,'\xd9':603,'\xda':603,'\xdb':1042, - '\xdc':987,'\xdd':603,'\xde':987,'\xdf':603,'\xe0':494,'\xe1':329,'\xe2':790,'\xe3':790,'\xe4':786,'\xe5':713,'\xe6':384,'\xe7':384,'\xe8':384,'\xe9':384,'\xea':384,'\xeb':384,'\xec':494,'\xed':494,'\xee':494,'\xef':494,'\xf0':0,'\xf1':329, - '\xf2':274,'\xf3':686,'\xf4':686,'\xf5':686,'\xf6':384,'\xf7':384,'\xf8':384,'\xf9':384,'\xfa':384,'\xfb':384,'\xfc':494,'\xfd':494,'\xfe':494,'\xff':0} - -fpdf_charwidths['times']={ - '\x00':250,'\x01':250,'\x02':250,'\x03':250,'\x04':250,'\x05':250,'\x06':250,'\x07':250,'\x08':250,'\t':250,'\n':250,'\x0b':250,'\x0c':250,'\r':250,'\x0e':250,'\x0f':250,'\x10':250,'\x11':250,'\x12':250,'\x13':250,'\x14':250,'\x15':250, - '\x16':250,'\x17':250,'\x18':250,'\x19':250,'\x1a':250,'\x1b':250,'\x1c':250,'\x1d':250,'\x1e':250,'\x1f':250,' ':250,'!':333,'"':408,'#':500,'$':500,'%':833,'&':778,'\'':180,'(':333,')':333,'*':500,'+':564, - ',':250,'-':333,'.':250,'/':278,'0':500,'1':500,'2':500,'3':500,'4':500,'5':500,'6':500,'7':500,'8':500,'9':500,':':278,';':278,'<':564,'=':564,'>':564,'?':444,'@':921,'A':722, - 'B':667,'C':667,'D':722,'E':611,'F':556,'G':722,'H':722,'I':333,'J':389,'K':722,'L':611,'M':889,'N':722,'O':722,'P':556,'Q':722,'R':667,'S':556,'T':611,'U':722,'V':722,'W':944, - 'X':722,'Y':722,'Z':611,'[':333,'\\':278,']':333,'^':469,'_':500,'`':333,'a':444,'b':500,'c':444,'d':500,'e':444,'f':333,'g':500,'h':500,'i':278,'j':278,'k':500,'l':278,'m':778, - 'n':500,'o':500,'p':500,'q':500,'r':333,'s':389,'t':278,'u':500,'v':500,'w':722,'x':500,'y':500,'z':444,'{':480,'|':200,'}':480,'~':541,'\x7f':350,'\x80':500,'\x81':350,'\x82':333,'\x83':500, - '\x84':444,'\x85':1000,'\x86':500,'\x87':500,'\x88':333,'\x89':1000,'\x8a':556,'\x8b':333,'\x8c':889,'\x8d':350,'\x8e':611,'\x8f':350,'\x90':350,'\x91':333,'\x92':333,'\x93':444,'\x94':444,'\x95':350,'\x96':500,'\x97':1000,'\x98':333,'\x99':980, - '\x9a':389,'\x9b':333,'\x9c':722,'\x9d':350,'\x9e':444,'\x9f':722,'\xa0':250,'\xa1':333,'\xa2':500,'\xa3':500,'\xa4':500,'\xa5':500,'\xa6':200,'\xa7':500,'\xa8':333,'\xa9':760,'\xaa':276,'\xab':500,'\xac':564,'\xad':333,'\xae':760,'\xaf':333, - '\xb0':400,'\xb1':564,'\xb2':300,'\xb3':300,'\xb4':333,'\xb5':500,'\xb6':453,'\xb7':250,'\xb8':333,'\xb9':300,'\xba':310,'\xbb':500,'\xbc':750,'\xbd':750,'\xbe':750,'\xbf':444,'\xc0':722,'\xc1':722,'\xc2':722,'\xc3':722,'\xc4':722,'\xc5':722, - '\xc6':889,'\xc7':667,'\xc8':611,'\xc9':611,'\xca':611,'\xcb':611,'\xcc':333,'\xcd':333,'\xce':333,'\xcf':333,'\xd0':722,'\xd1':722,'\xd2':722,'\xd3':722,'\xd4':722,'\xd5':722,'\xd6':722,'\xd7':564,'\xd8':722,'\xd9':722,'\xda':722,'\xdb':722, - '\xdc':722,'\xdd':722,'\xde':556,'\xdf':500,'\xe0':444,'\xe1':444,'\xe2':444,'\xe3':444,'\xe4':444,'\xe5':444,'\xe6':667,'\xe7':444,'\xe8':444,'\xe9':444,'\xea':444,'\xeb':444,'\xec':278,'\xed':278,'\xee':278,'\xef':278,'\xf0':500,'\xf1':500, - '\xf2':500,'\xf3':500,'\xf4':500,'\xf5':500,'\xf6':500,'\xf7':564,'\xf8':500,'\xf9':500,'\xfa':500,'\xfb':500,'\xfc':500,'\xfd':500,'\xfe':500,'\xff':500} - -fpdf_charwidths['timesB']={ - '\x00':250,'\x01':250,'\x02':250,'\x03':250,'\x04':250,'\x05':250,'\x06':250,'\x07':250,'\x08':250,'\t':250,'\n':250,'\x0b':250,'\x0c':250,'\r':250,'\x0e':250,'\x0f':250,'\x10':250,'\x11':250,'\x12':250,'\x13':250,'\x14':250,'\x15':250, - '\x16':250,'\x17':250,'\x18':250,'\x19':250,'\x1a':250,'\x1b':250,'\x1c':250,'\x1d':250,'\x1e':250,'\x1f':250,' ':250,'!':333,'"':555,'#':500,'$':500,'%':1000,'&':833,'\'':278,'(':333,')':333,'*':500,'+':570, - ',':250,'-':333,'.':250,'/':278,'0':500,'1':500,'2':500,'3':500,'4':500,'5':500,'6':500,'7':500,'8':500,'9':500,':':333,';':333,'<':570,'=':570,'>':570,'?':500,'@':930,'A':722, - 'B':667,'C':722,'D':722,'E':667,'F':611,'G':778,'H':778,'I':389,'J':500,'K':778,'L':667,'M':944,'N':722,'O':778,'P':611,'Q':778,'R':722,'S':556,'T':667,'U':722,'V':722,'W':1000, - 'X':722,'Y':722,'Z':667,'[':333,'\\':278,']':333,'^':581,'_':500,'`':333,'a':500,'b':556,'c':444,'d':556,'e':444,'f':333,'g':500,'h':556,'i':278,'j':333,'k':556,'l':278,'m':833, - 'n':556,'o':500,'p':556,'q':556,'r':444,'s':389,'t':333,'u':556,'v':500,'w':722,'x':500,'y':500,'z':444,'{':394,'|':220,'}':394,'~':520,'\x7f':350,'\x80':500,'\x81':350,'\x82':333,'\x83':500, - '\x84':500,'\x85':1000,'\x86':500,'\x87':500,'\x88':333,'\x89':1000,'\x8a':556,'\x8b':333,'\x8c':1000,'\x8d':350,'\x8e':667,'\x8f':350,'\x90':350,'\x91':333,'\x92':333,'\x93':500,'\x94':500,'\x95':350,'\x96':500,'\x97':1000,'\x98':333,'\x99':1000, - '\x9a':389,'\x9b':333,'\x9c':722,'\x9d':350,'\x9e':444,'\x9f':722,'\xa0':250,'\xa1':333,'\xa2':500,'\xa3':500,'\xa4':500,'\xa5':500,'\xa6':220,'\xa7':500,'\xa8':333,'\xa9':747,'\xaa':300,'\xab':500,'\xac':570,'\xad':333,'\xae':747,'\xaf':333, - '\xb0':400,'\xb1':570,'\xb2':300,'\xb3':300,'\xb4':333,'\xb5':556,'\xb6':540,'\xb7':250,'\xb8':333,'\xb9':300,'\xba':330,'\xbb':500,'\xbc':750,'\xbd':750,'\xbe':750,'\xbf':500,'\xc0':722,'\xc1':722,'\xc2':722,'\xc3':722,'\xc4':722,'\xc5':722, - '\xc6':1000,'\xc7':722,'\xc8':667,'\xc9':667,'\xca':667,'\xcb':667,'\xcc':389,'\xcd':389,'\xce':389,'\xcf':389,'\xd0':722,'\xd1':722,'\xd2':778,'\xd3':778,'\xd4':778,'\xd5':778,'\xd6':778,'\xd7':570,'\xd8':778,'\xd9':722,'\xda':722,'\xdb':722, - '\xdc':722,'\xdd':722,'\xde':611,'\xdf':556,'\xe0':500,'\xe1':500,'\xe2':500,'\xe3':500,'\xe4':500,'\xe5':500,'\xe6':722,'\xe7':444,'\xe8':444,'\xe9':444,'\xea':444,'\xeb':444,'\xec':278,'\xed':278,'\xee':278,'\xef':278,'\xf0':500,'\xf1':556, - '\xf2':500,'\xf3':500,'\xf4':500,'\xf5':500,'\xf6':500,'\xf7':570,'\xf8':500,'\xf9':556,'\xfa':556,'\xfb':556,'\xfc':556,'\xfd':500,'\xfe':556,'\xff':500} - -fpdf_charwidths['timesBI']={ - '\x00':250,'\x01':250,'\x02':250,'\x03':250,'\x04':250,'\x05':250,'\x06':250,'\x07':250,'\x08':250,'\t':250,'\n':250,'\x0b':250,'\x0c':250,'\r':250,'\x0e':250,'\x0f':250,'\x10':250,'\x11':250,'\x12':250,'\x13':250,'\x14':250,'\x15':250, - '\x16':250,'\x17':250,'\x18':250,'\x19':250,'\x1a':250,'\x1b':250,'\x1c':250,'\x1d':250,'\x1e':250,'\x1f':250,' ':250,'!':389,'"':555,'#':500,'$':500,'%':833,'&':778,'\'':278,'(':333,')':333,'*':500,'+':570, - ',':250,'-':333,'.':250,'/':278,'0':500,'1':500,'2':500,'3':500,'4':500,'5':500,'6':500,'7':500,'8':500,'9':500,':':333,';':333,'<':570,'=':570,'>':570,'?':500,'@':832,'A':667, - 'B':667,'C':667,'D':722,'E':667,'F':667,'G':722,'H':778,'I':389,'J':500,'K':667,'L':611,'M':889,'N':722,'O':722,'P':611,'Q':722,'R':667,'S':556,'T':611,'U':722,'V':667,'W':889, - 'X':667,'Y':611,'Z':611,'[':333,'\\':278,']':333,'^':570,'_':500,'`':333,'a':500,'b':500,'c':444,'d':500,'e':444,'f':333,'g':500,'h':556,'i':278,'j':278,'k':500,'l':278,'m':778, - 'n':556,'o':500,'p':500,'q':500,'r':389,'s':389,'t':278,'u':556,'v':444,'w':667,'x':500,'y':444,'z':389,'{':348,'|':220,'}':348,'~':570,'\x7f':350,'\x80':500,'\x81':350,'\x82':333,'\x83':500, - '\x84':500,'\x85':1000,'\x86':500,'\x87':500,'\x88':333,'\x89':1000,'\x8a':556,'\x8b':333,'\x8c':944,'\x8d':350,'\x8e':611,'\x8f':350,'\x90':350,'\x91':333,'\x92':333,'\x93':500,'\x94':500,'\x95':350,'\x96':500,'\x97':1000,'\x98':333,'\x99':1000, - '\x9a':389,'\x9b':333,'\x9c':722,'\x9d':350,'\x9e':389,'\x9f':611,'\xa0':250,'\xa1':389,'\xa2':500,'\xa3':500,'\xa4':500,'\xa5':500,'\xa6':220,'\xa7':500,'\xa8':333,'\xa9':747,'\xaa':266,'\xab':500,'\xac':606,'\xad':333,'\xae':747,'\xaf':333, - '\xb0':400,'\xb1':570,'\xb2':300,'\xb3':300,'\xb4':333,'\xb5':576,'\xb6':500,'\xb7':250,'\xb8':333,'\xb9':300,'\xba':300,'\xbb':500,'\xbc':750,'\xbd':750,'\xbe':750,'\xbf':500,'\xc0':667,'\xc1':667,'\xc2':667,'\xc3':667,'\xc4':667,'\xc5':667, - '\xc6':944,'\xc7':667,'\xc8':667,'\xc9':667,'\xca':667,'\xcb':667,'\xcc':389,'\xcd':389,'\xce':389,'\xcf':389,'\xd0':722,'\xd1':722,'\xd2':722,'\xd3':722,'\xd4':722,'\xd5':722,'\xd6':722,'\xd7':570,'\xd8':722,'\xd9':722,'\xda':722,'\xdb':722, - '\xdc':722,'\xdd':611,'\xde':611,'\xdf':500,'\xe0':500,'\xe1':500,'\xe2':500,'\xe3':500,'\xe4':500,'\xe5':500,'\xe6':722,'\xe7':444,'\xe8':444,'\xe9':444,'\xea':444,'\xeb':444,'\xec':278,'\xed':278,'\xee':278,'\xef':278,'\xf0':500,'\xf1':556, - '\xf2':500,'\xf3':500,'\xf4':500,'\xf5':500,'\xf6':500,'\xf7':570,'\xf8':500,'\xf9':556,'\xfa':556,'\xfb':556,'\xfc':556,'\xfd':444,'\xfe':500,'\xff':444} - -fpdf_charwidths['timesI']={ - '\x00':250,'\x01':250,'\x02':250,'\x03':250,'\x04':250,'\x05':250,'\x06':250,'\x07':250,'\x08':250,'\t':250,'\n':250,'\x0b':250,'\x0c':250,'\r':250,'\x0e':250,'\x0f':250,'\x10':250,'\x11':250,'\x12':250,'\x13':250,'\x14':250,'\x15':250, - '\x16':250,'\x17':250,'\x18':250,'\x19':250,'\x1a':250,'\x1b':250,'\x1c':250,'\x1d':250,'\x1e':250,'\x1f':250,' ':250,'!':333,'"':420,'#':500,'$':500,'%':833,'&':778,'\'':214,'(':333,')':333,'*':500,'+':675, - ',':250,'-':333,'.':250,'/':278,'0':500,'1':500,'2':500,'3':500,'4':500,'5':500,'6':500,'7':500,'8':500,'9':500,':':333,';':333,'<':675,'=':675,'>':675,'?':500,'@':920,'A':611, - 'B':611,'C':667,'D':722,'E':611,'F':611,'G':722,'H':722,'I':333,'J':444,'K':667,'L':556,'M':833,'N':667,'O':722,'P':611,'Q':722,'R':611,'S':500,'T':556,'U':722,'V':611,'W':833, - 'X':611,'Y':556,'Z':556,'[':389,'\\':278,']':389,'^':422,'_':500,'`':333,'a':500,'b':500,'c':444,'d':500,'e':444,'f':278,'g':500,'h':500,'i':278,'j':278,'k':444,'l':278,'m':722, - 'n':500,'o':500,'p':500,'q':500,'r':389,'s':389,'t':278,'u':500,'v':444,'w':667,'x':444,'y':444,'z':389,'{':400,'|':275,'}':400,'~':541,'\x7f':350,'\x80':500,'\x81':350,'\x82':333,'\x83':500, - '\x84':556,'\x85':889,'\x86':500,'\x87':500,'\x88':333,'\x89':1000,'\x8a':500,'\x8b':333,'\x8c':944,'\x8d':350,'\x8e':556,'\x8f':350,'\x90':350,'\x91':333,'\x92':333,'\x93':556,'\x94':556,'\x95':350,'\x96':500,'\x97':889,'\x98':333,'\x99':980, - '\x9a':389,'\x9b':333,'\x9c':667,'\x9d':350,'\x9e':389,'\x9f':556,'\xa0':250,'\xa1':389,'\xa2':500,'\xa3':500,'\xa4':500,'\xa5':500,'\xa6':275,'\xa7':500,'\xa8':333,'\xa9':760,'\xaa':276,'\xab':500,'\xac':675,'\xad':333,'\xae':760,'\xaf':333, - '\xb0':400,'\xb1':675,'\xb2':300,'\xb3':300,'\xb4':333,'\xb5':500,'\xb6':523,'\xb7':250,'\xb8':333,'\xb9':300,'\xba':310,'\xbb':500,'\xbc':750,'\xbd':750,'\xbe':750,'\xbf':500,'\xc0':611,'\xc1':611,'\xc2':611,'\xc3':611,'\xc4':611,'\xc5':611, - '\xc6':889,'\xc7':667,'\xc8':611,'\xc9':611,'\xca':611,'\xcb':611,'\xcc':333,'\xcd':333,'\xce':333,'\xcf':333,'\xd0':722,'\xd1':667,'\xd2':722,'\xd3':722,'\xd4':722,'\xd5':722,'\xd6':722,'\xd7':675,'\xd8':722,'\xd9':722,'\xda':722,'\xdb':722, - '\xdc':722,'\xdd':556,'\xde':611,'\xdf':500,'\xe0':500,'\xe1':500,'\xe2':500,'\xe3':500,'\xe4':500,'\xe5':500,'\xe6':667,'\xe7':444,'\xe8':444,'\xe9':444,'\xea':444,'\xeb':444,'\xec':278,'\xed':278,'\xee':278,'\xef':278,'\xf0':500,'\xf1':500, - '\xf2':500,'\xf3':500,'\xf4':500,'\xf5':500,'\xf6':500,'\xf7':675,'\xf8':500,'\xf9':500,'\xfa':500,'\xfb':500,'\xfc':500,'\xfd':444,'\xfe':500,'\xff':444} - -fpdf_charwidths['zapfdingbats']={ - '\x00':0,'\x01':0,'\x02':0,'\x03':0,'\x04':0,'\x05':0,'\x06':0,'\x07':0,'\x08':0,'\t':0,'\n':0,'\x0b':0,'\x0c':0,'\r':0,'\x0e':0,'\x0f':0,'\x10':0,'\x11':0,'\x12':0,'\x13':0,'\x14':0,'\x15':0, - '\x16':0,'\x17':0,'\x18':0,'\x19':0,'\x1a':0,'\x1b':0,'\x1c':0,'\x1d':0,'\x1e':0,'\x1f':0,' ':278,'!':974,'"':961,'#':974,'$':980,'%':719,'&':789,'\'':790,'(':791,')':690,'*':960,'+':939, - ',':549,'-':855,'.':911,'/':933,'0':911,'1':945,'2':974,'3':755,'4':846,'5':762,'6':761,'7':571,'8':677,'9':763,':':760,';':759,'<':754,'=':494,'>':552,'?':537,'@':577,'A':692, - 'B':786,'C':788,'D':788,'E':790,'F':793,'G':794,'H':816,'I':823,'J':789,'K':841,'L':823,'M':833,'N':816,'O':831,'P':923,'Q':744,'R':723,'S':749,'T':790,'U':792,'V':695,'W':776, - 'X':768,'Y':792,'Z':759,'[':707,'\\':708,']':682,'^':701,'_':826,'`':815,'a':789,'b':789,'c':707,'d':687,'e':696,'f':689,'g':786,'h':787,'i':713,'j':791,'k':785,'l':791,'m':873, - 'n':761,'o':762,'p':762,'q':759,'r':759,'s':892,'t':892,'u':788,'v':784,'w':438,'x':138,'y':277,'z':415,'{':392,'|':392,'}':668,'~':668,'\x7f':0,'\x80':390,'\x81':390,'\x82':317,'\x83':317, - '\x84':276,'\x85':276,'\x86':509,'\x87':509,'\x88':410,'\x89':410,'\x8a':234,'\x8b':234,'\x8c':334,'\x8d':334,'\x8e':0,'\x8f':0,'\x90':0,'\x91':0,'\x92':0,'\x93':0,'\x94':0,'\x95':0,'\x96':0,'\x97':0,'\x98':0,'\x99':0, - '\x9a':0,'\x9b':0,'\x9c':0,'\x9d':0,'\x9e':0,'\x9f':0,'\xa0':0,'\xa1':732,'\xa2':544,'\xa3':544,'\xa4':910,'\xa5':667,'\xa6':760,'\xa7':760,'\xa8':776,'\xa9':595,'\xaa':694,'\xab':626,'\xac':788,'\xad':788,'\xae':788,'\xaf':788, - '\xb0':788,'\xb1':788,'\xb2':788,'\xb3':788,'\xb4':788,'\xb5':788,'\xb6':788,'\xb7':788,'\xb8':788,'\xb9':788,'\xba':788,'\xbb':788,'\xbc':788,'\xbd':788,'\xbe':788,'\xbf':788,'\xc0':788,'\xc1':788,'\xc2':788,'\xc3':788,'\xc4':788,'\xc5':788, - '\xc6':788,'\xc7':788,'\xc8':788,'\xc9':788,'\xca':788,'\xcb':788,'\xcc':788,'\xcd':788,'\xce':788,'\xcf':788,'\xd0':788,'\xd1':788,'\xd2':788,'\xd3':788,'\xd4':894,'\xd5':838,'\xd6':1016,'\xd7':458,'\xd8':748,'\xd9':924,'\xda':748,'\xdb':918, - '\xdc':927,'\xdd':928,'\xde':928,'\xdf':834,'\xe0':873,'\xe1':828,'\xe2':924,'\xe3':924,'\xe4':917,'\xe5':930,'\xe6':931,'\xe7':463,'\xe8':883,'\xe9':836,'\xea':836,'\xeb':867,'\xec':867,'\xed':696,'\xee':696,'\xef':874,'\xf0':0,'\xf1':874, - '\xf2':760,'\xf3':946,'\xf4':771,'\xf5':865,'\xf6':771,'\xf7':888,'\xf8':967,'\xf9':888,'\xfa':831,'\xfb':873,'\xfc':927,'\xfd':970,'\xfe':918,'\xff':0} - - +#!/usr/bin/env python +# -*- coding: latin-1 -*- + +# Fonts: + +fpdf_charwidths = {} + +fpdf_charwidths['courier']={} + +for i in xrange(0,256): + fpdf_charwidths['courier'][chr(i)]=600 + fpdf_charwidths['courierB']=fpdf_charwidths['courier'] + fpdf_charwidths['courierI']=fpdf_charwidths['courier'] + fpdf_charwidths['courierBI']=fpdf_charwidths['courier'] + +fpdf_charwidths['helvetica']={ + '\x00':278,'\x01':278,'\x02':278,'\x03':278,'\x04':278,'\x05':278,'\x06':278,'\x07':278,'\x08':278,'\t':278,'\n':278,'\x0b':278,'\x0c':278,'\r':278,'\x0e':278,'\x0f':278,'\x10':278,'\x11':278,'\x12':278,'\x13':278,'\x14':278,'\x15':278, + '\x16':278,'\x17':278,'\x18':278,'\x19':278,'\x1a':278,'\x1b':278,'\x1c':278,'\x1d':278,'\x1e':278,'\x1f':278,' ':278,'!':278,'"':355,'#':556,'$':556,'%':889,'&':667,'\'':191,'(':333,')':333,'*':389,'+':584, + ',':278,'-':333,'.':278,'/':278,'0':556,'1':556,'2':556,'3':556,'4':556,'5':556,'6':556,'7':556,'8':556,'9':556,':':278,';':278,'<':584,'=':584,'>':584,'?':556,'@':1015,'A':667, + 'B':667,'C':722,'D':722,'E':667,'F':611,'G':778,'H':722,'I':278,'J':500,'K':667,'L':556,'M':833,'N':722,'O':778,'P':667,'Q':778,'R':722,'S':667,'T':611,'U':722,'V':667,'W':944, + 'X':667,'Y':667,'Z':611,'[':278,'\\':278,']':278,'^':469,'_':556,'`':333,'a':556,'b':556,'c':500,'d':556,'e':556,'f':278,'g':556,'h':556,'i':222,'j':222,'k':500,'l':222,'m':833, + 'n':556,'o':556,'p':556,'q':556,'r':333,'s':500,'t':278,'u':556,'v':500,'w':722,'x':500,'y':500,'z':500,'{':334,'|':260,'}':334,'~':584,'\x7f':350,'\x80':556,'\x81':350,'\x82':222,'\x83':556, + '\x84':333,'\x85':1000,'\x86':556,'\x87':556,'\x88':333,'\x89':1000,'\x8a':667,'\x8b':333,'\x8c':1000,'\x8d':350,'\x8e':611,'\x8f':350,'\x90':350,'\x91':222,'\x92':222,'\x93':333,'\x94':333,'\x95':350,'\x96':556,'\x97':1000,'\x98':333,'\x99':1000, + '\x9a':500,'\x9b':333,'\x9c':944,'\x9d':350,'\x9e':500,'\x9f':667,'\xa0':278,'\xa1':333,'\xa2':556,'\xa3':556,'\xa4':556,'\xa5':556,'\xa6':260,'\xa7':556,'\xa8':333,'\xa9':737,'\xaa':370,'\xab':556,'\xac':584,'\xad':333,'\xae':737,'\xaf':333, + '\xb0':400,'\xb1':584,'\xb2':333,'\xb3':333,'\xb4':333,'\xb5':556,'\xb6':537,'\xb7':278,'\xb8':333,'\xb9':333,'\xba':365,'\xbb':556,'\xbc':834,'\xbd':834,'\xbe':834,'\xbf':611,'\xc0':667,'\xc1':667,'\xc2':667,'\xc3':667,'\xc4':667,'\xc5':667, + '\xc6':1000,'\xc7':722,'\xc8':667,'\xc9':667,'\xca':667,'\xcb':667,'\xcc':278,'\xcd':278,'\xce':278,'\xcf':278,'\xd0':722,'\xd1':722,'\xd2':778,'\xd3':778,'\xd4':778,'\xd5':778,'\xd6':778,'\xd7':584,'\xd8':778,'\xd9':722,'\xda':722,'\xdb':722, + '\xdc':722,'\xdd':667,'\xde':667,'\xdf':611,'\xe0':556,'\xe1':556,'\xe2':556,'\xe3':556,'\xe4':556,'\xe5':556,'\xe6':889,'\xe7':500,'\xe8':556,'\xe9':556,'\xea':556,'\xeb':556,'\xec':278,'\xed':278,'\xee':278,'\xef':278,'\xf0':556,'\xf1':556, + '\xf2':556,'\xf3':556,'\xf4':556,'\xf5':556,'\xf6':556,'\xf7':584,'\xf8':611,'\xf9':556,'\xfa':556,'\xfb':556,'\xfc':556,'\xfd':500,'\xfe':556,'\xff':500} + +fpdf_charwidths['helveticaB']={ + '\x00':278,'\x01':278,'\x02':278,'\x03':278,'\x04':278,'\x05':278,'\x06':278,'\x07':278,'\x08':278,'\t':278,'\n':278,'\x0b':278,'\x0c':278,'\r':278,'\x0e':278,'\x0f':278,'\x10':278,'\x11':278,'\x12':278,'\x13':278,'\x14':278,'\x15':278, + '\x16':278,'\x17':278,'\x18':278,'\x19':278,'\x1a':278,'\x1b':278,'\x1c':278,'\x1d':278,'\x1e':278,'\x1f':278,' ':278,'!':333,'"':474,'#':556,'$':556,'%':889,'&':722,'\'':238,'(':333,')':333,'*':389,'+':584, + ',':278,'-':333,'.':278,'/':278,'0':556,'1':556,'2':556,'3':556,'4':556,'5':556,'6':556,'7':556,'8':556,'9':556,':':333,';':333,'<':584,'=':584,'>':584,'?':611,'@':975,'A':722, + 'B':722,'C':722,'D':722,'E':667,'F':611,'G':778,'H':722,'I':278,'J':556,'K':722,'L':611,'M':833,'N':722,'O':778,'P':667,'Q':778,'R':722,'S':667,'T':611,'U':722,'V':667,'W':944, + 'X':667,'Y':667,'Z':611,'[':333,'\\':278,']':333,'^':584,'_':556,'`':333,'a':556,'b':611,'c':556,'d':611,'e':556,'f':333,'g':611,'h':611,'i':278,'j':278,'k':556,'l':278,'m':889, + 'n':611,'o':611,'p':611,'q':611,'r':389,'s':556,'t':333,'u':611,'v':556,'w':778,'x':556,'y':556,'z':500,'{':389,'|':280,'}':389,'~':584,'\x7f':350,'\x80':556,'\x81':350,'\x82':278,'\x83':556, + '\x84':500,'\x85':1000,'\x86':556,'\x87':556,'\x88':333,'\x89':1000,'\x8a':667,'\x8b':333,'\x8c':1000,'\x8d':350,'\x8e':611,'\x8f':350,'\x90':350,'\x91':278,'\x92':278,'\x93':500,'\x94':500,'\x95':350,'\x96':556,'\x97':1000,'\x98':333,'\x99':1000, + '\x9a':556,'\x9b':333,'\x9c':944,'\x9d':350,'\x9e':500,'\x9f':667,'\xa0':278,'\xa1':333,'\xa2':556,'\xa3':556,'\xa4':556,'\xa5':556,'\xa6':280,'\xa7':556,'\xa8':333,'\xa9':737,'\xaa':370,'\xab':556,'\xac':584,'\xad':333,'\xae':737,'\xaf':333, + '\xb0':400,'\xb1':584,'\xb2':333,'\xb3':333,'\xb4':333,'\xb5':611,'\xb6':556,'\xb7':278,'\xb8':333,'\xb9':333,'\xba':365,'\xbb':556,'\xbc':834,'\xbd':834,'\xbe':834,'\xbf':611,'\xc0':722,'\xc1':722,'\xc2':722,'\xc3':722,'\xc4':722,'\xc5':722, + '\xc6':1000,'\xc7':722,'\xc8':667,'\xc9':667,'\xca':667,'\xcb':667,'\xcc':278,'\xcd':278,'\xce':278,'\xcf':278,'\xd0':722,'\xd1':722,'\xd2':778,'\xd3':778,'\xd4':778,'\xd5':778,'\xd6':778,'\xd7':584,'\xd8':778,'\xd9':722,'\xda':722,'\xdb':722, + '\xdc':722,'\xdd':667,'\xde':667,'\xdf':611,'\xe0':556,'\xe1':556,'\xe2':556,'\xe3':556,'\xe4':556,'\xe5':556,'\xe6':889,'\xe7':556,'\xe8':556,'\xe9':556,'\xea':556,'\xeb':556,'\xec':278,'\xed':278,'\xee':278,'\xef':278,'\xf0':611,'\xf1':611, + '\xf2':611,'\xf3':611,'\xf4':611,'\xf5':611,'\xf6':611,'\xf7':584,'\xf8':611,'\xf9':611,'\xfa':611,'\xfb':611,'\xfc':611,'\xfd':556,'\xfe':611,'\xff':556 +} + +fpdf_charwidths['helveticaBI']={ + '\x00':278,'\x01':278,'\x02':278,'\x03':278,'\x04':278,'\x05':278,'\x06':278,'\x07':278,'\x08':278,'\t':278,'\n':278,'\x0b':278,'\x0c':278,'\r':278,'\x0e':278,'\x0f':278,'\x10':278,'\x11':278,'\x12':278,'\x13':278,'\x14':278,'\x15':278, + '\x16':278,'\x17':278,'\x18':278,'\x19':278,'\x1a':278,'\x1b':278,'\x1c':278,'\x1d':278,'\x1e':278,'\x1f':278,' ':278,'!':333,'"':474,'#':556,'$':556,'%':889,'&':722,'\'':238,'(':333,')':333,'*':389,'+':584, + ',':278,'-':333,'.':278,'/':278,'0':556,'1':556,'2':556,'3':556,'4':556,'5':556,'6':556,'7':556,'8':556,'9':556,':':333,';':333,'<':584,'=':584,'>':584,'?':611,'@':975,'A':722, + 'B':722,'C':722,'D':722,'E':667,'F':611,'G':778,'H':722,'I':278,'J':556,'K':722,'L':611,'M':833,'N':722,'O':778,'P':667,'Q':778,'R':722,'S':667,'T':611,'U':722,'V':667,'W':944, + 'X':667,'Y':667,'Z':611,'[':333,'\\':278,']':333,'^':584,'_':556,'`':333,'a':556,'b':611,'c':556,'d':611,'e':556,'f':333,'g':611,'h':611,'i':278,'j':278,'k':556,'l':278,'m':889, + 'n':611,'o':611,'p':611,'q':611,'r':389,'s':556,'t':333,'u':611,'v':556,'w':778,'x':556,'y':556,'z':500,'{':389,'|':280,'}':389,'~':584,'\x7f':350,'\x80':556,'\x81':350,'\x82':278,'\x83':556, + '\x84':500,'\x85':1000,'\x86':556,'\x87':556,'\x88':333,'\x89':1000,'\x8a':667,'\x8b':333,'\x8c':1000,'\x8d':350,'\x8e':611,'\x8f':350,'\x90':350,'\x91':278,'\x92':278,'\x93':500,'\x94':500,'\x95':350,'\x96':556,'\x97':1000,'\x98':333,'\x99':1000, + '\x9a':556,'\x9b':333,'\x9c':944,'\x9d':350,'\x9e':500,'\x9f':667,'\xa0':278,'\xa1':333,'\xa2':556,'\xa3':556,'\xa4':556,'\xa5':556,'\xa6':280,'\xa7':556,'\xa8':333,'\xa9':737,'\xaa':370,'\xab':556,'\xac':584,'\xad':333,'\xae':737,'\xaf':333, + '\xb0':400,'\xb1':584,'\xb2':333,'\xb3':333,'\xb4':333,'\xb5':611,'\xb6':556,'\xb7':278,'\xb8':333,'\xb9':333,'\xba':365,'\xbb':556,'\xbc':834,'\xbd':834,'\xbe':834,'\xbf':611,'\xc0':722,'\xc1':722,'\xc2':722,'\xc3':722,'\xc4':722,'\xc5':722, + '\xc6':1000,'\xc7':722,'\xc8':667,'\xc9':667,'\xca':667,'\xcb':667,'\xcc':278,'\xcd':278,'\xce':278,'\xcf':278,'\xd0':722,'\xd1':722,'\xd2':778,'\xd3':778,'\xd4':778,'\xd5':778,'\xd6':778,'\xd7':584,'\xd8':778,'\xd9':722,'\xda':722,'\xdb':722, + '\xdc':722,'\xdd':667,'\xde':667,'\xdf':611,'\xe0':556,'\xe1':556,'\xe2':556,'\xe3':556,'\xe4':556,'\xe5':556,'\xe6':889,'\xe7':556,'\xe8':556,'\xe9':556,'\xea':556,'\xeb':556,'\xec':278,'\xed':278,'\xee':278,'\xef':278,'\xf0':611,'\xf1':611, + '\xf2':611,'\xf3':611,'\xf4':611,'\xf5':611,'\xf6':611,'\xf7':584,'\xf8':611,'\xf9':611,'\xfa':611,'\xfb':611,'\xfc':611,'\xfd':556,'\xfe':611,'\xff':556} + +fpdf_charwidths['helveticaI']={ + '\x00':278,'\x01':278,'\x02':278,'\x03':278,'\x04':278,'\x05':278,'\x06':278,'\x07':278,'\x08':278,'\t':278,'\n':278,'\x0b':278,'\x0c':278,'\r':278,'\x0e':278,'\x0f':278,'\x10':278,'\x11':278,'\x12':278,'\x13':278,'\x14':278,'\x15':278, + '\x16':278,'\x17':278,'\x18':278,'\x19':278,'\x1a':278,'\x1b':278,'\x1c':278,'\x1d':278,'\x1e':278,'\x1f':278,' ':278,'!':278,'"':355,'#':556,'$':556,'%':889,'&':667,'\'':191,'(':333,')':333,'*':389,'+':584, + ',':278,'-':333,'.':278,'/':278,'0':556,'1':556,'2':556,'3':556,'4':556,'5':556,'6':556,'7':556,'8':556,'9':556,':':278,';':278,'<':584,'=':584,'>':584,'?':556,'@':1015,'A':667, + 'B':667,'C':722,'D':722,'E':667,'F':611,'G':778,'H':722,'I':278,'J':500,'K':667,'L':556,'M':833,'N':722,'O':778,'P':667,'Q':778,'R':722,'S':667,'T':611,'U':722,'V':667,'W':944, + 'X':667,'Y':667,'Z':611,'[':278,'\\':278,']':278,'^':469,'_':556,'`':333,'a':556,'b':556,'c':500,'d':556,'e':556,'f':278,'g':556,'h':556,'i':222,'j':222,'k':500,'l':222,'m':833, + 'n':556,'o':556,'p':556,'q':556,'r':333,'s':500,'t':278,'u':556,'v':500,'w':722,'x':500,'y':500,'z':500,'{':334,'|':260,'}':334,'~':584,'\x7f':350,'\x80':556,'\x81':350,'\x82':222,'\x83':556, + '\x84':333,'\x85':1000,'\x86':556,'\x87':556,'\x88':333,'\x89':1000,'\x8a':667,'\x8b':333,'\x8c':1000,'\x8d':350,'\x8e':611,'\x8f':350,'\x90':350,'\x91':222,'\x92':222,'\x93':333,'\x94':333,'\x95':350,'\x96':556,'\x97':1000,'\x98':333,'\x99':1000, + '\x9a':500,'\x9b':333,'\x9c':944,'\x9d':350,'\x9e':500,'\x9f':667,'\xa0':278,'\xa1':333,'\xa2':556,'\xa3':556,'\xa4':556,'\xa5':556,'\xa6':260,'\xa7':556,'\xa8':333,'\xa9':737,'\xaa':370,'\xab':556,'\xac':584,'\xad':333,'\xae':737,'\xaf':333, + '\xb0':400,'\xb1':584,'\xb2':333,'\xb3':333,'\xb4':333,'\xb5':556,'\xb6':537,'\xb7':278,'\xb8':333,'\xb9':333,'\xba':365,'\xbb':556,'\xbc':834,'\xbd':834,'\xbe':834,'\xbf':611,'\xc0':667,'\xc1':667,'\xc2':667,'\xc3':667,'\xc4':667,'\xc5':667, + '\xc6':1000,'\xc7':722,'\xc8':667,'\xc9':667,'\xca':667,'\xcb':667,'\xcc':278,'\xcd':278,'\xce':278,'\xcf':278,'\xd0':722,'\xd1':722,'\xd2':778,'\xd3':778,'\xd4':778,'\xd5':778,'\xd6':778,'\xd7':584,'\xd8':778,'\xd9':722,'\xda':722,'\xdb':722, + '\xdc':722,'\xdd':667,'\xde':667,'\xdf':611,'\xe0':556,'\xe1':556,'\xe2':556,'\xe3':556,'\xe4':556,'\xe5':556,'\xe6':889,'\xe7':500,'\xe8':556,'\xe9':556,'\xea':556,'\xeb':556,'\xec':278,'\xed':278,'\xee':278,'\xef':278,'\xf0':556,'\xf1':556, + '\xf2':556,'\xf3':556,'\xf4':556,'\xf5':556,'\xf6':556,'\xf7':584,'\xf8':611,'\xf9':556,'\xfa':556,'\xfb':556,'\xfc':556,'\xfd':500,'\xfe':556,'\xff':500} + +fpdf_charwidths['symbol']={ + '\x00':250,'\x01':250,'\x02':250,'\x03':250,'\x04':250,'\x05':250,'\x06':250,'\x07':250,'\x08':250,'\t':250,'\n':250,'\x0b':250,'\x0c':250,'\r':250,'\x0e':250,'\x0f':250,'\x10':250,'\x11':250,'\x12':250,'\x13':250,'\x14':250,'\x15':250, + '\x16':250,'\x17':250,'\x18':250,'\x19':250,'\x1a':250,'\x1b':250,'\x1c':250,'\x1d':250,'\x1e':250,'\x1f':250,' ':250,'!':333,'"':713,'#':500,'$':549,'%':833,'&':778,'\'':439,'(':333,')':333,'*':500,'+':549, + ',':250,'-':549,'.':250,'/':278,'0':500,'1':500,'2':500,'3':500,'4':500,'5':500,'6':500,'7':500,'8':500,'9':500,':':278,';':278,'<':549,'=':549,'>':549,'?':444,'@':549,'A':722, + 'B':667,'C':722,'D':612,'E':611,'F':763,'G':603,'H':722,'I':333,'J':631,'K':722,'L':686,'M':889,'N':722,'O':722,'P':768,'Q':741,'R':556,'S':592,'T':611,'U':690,'V':439,'W':768, + 'X':645,'Y':795,'Z':611,'[':333,'\\':863,']':333,'^':658,'_':500,'`':500,'a':631,'b':549,'c':549,'d':494,'e':439,'f':521,'g':411,'h':603,'i':329,'j':603,'k':549,'l':549,'m':576, + 'n':521,'o':549,'p':549,'q':521,'r':549,'s':603,'t':439,'u':576,'v':713,'w':686,'x':493,'y':686,'z':494,'{':480,'|':200,'}':480,'~':549,'\x7f':0,'\x80':0,'\x81':0,'\x82':0,'\x83':0, + '\x84':0,'\x85':0,'\x86':0,'\x87':0,'\x88':0,'\x89':0,'\x8a':0,'\x8b':0,'\x8c':0,'\x8d':0,'\x8e':0,'\x8f':0,'\x90':0,'\x91':0,'\x92':0,'\x93':0,'\x94':0,'\x95':0,'\x96':0,'\x97':0,'\x98':0,'\x99':0, + '\x9a':0,'\x9b':0,'\x9c':0,'\x9d':0,'\x9e':0,'\x9f':0,'\xa0':750,'\xa1':620,'\xa2':247,'\xa3':549,'\xa4':167,'\xa5':713,'\xa6':500,'\xa7':753,'\xa8':753,'\xa9':753,'\xaa':753,'\xab':1042,'\xac':987,'\xad':603,'\xae':987,'\xaf':603, + '\xb0':400,'\xb1':549,'\xb2':411,'\xb3':549,'\xb4':549,'\xb5':713,'\xb6':494,'\xb7':460,'\xb8':549,'\xb9':549,'\xba':549,'\xbb':549,'\xbc':1000,'\xbd':603,'\xbe':1000,'\xbf':658,'\xc0':823,'\xc1':686,'\xc2':795,'\xc3':987,'\xc4':768,'\xc5':768, + '\xc6':823,'\xc7':768,'\xc8':768,'\xc9':713,'\xca':713,'\xcb':713,'\xcc':713,'\xcd':713,'\xce':713,'\xcf':713,'\xd0':768,'\xd1':713,'\xd2':790,'\xd3':790,'\xd4':890,'\xd5':823,'\xd6':549,'\xd7':250,'\xd8':713,'\xd9':603,'\xda':603,'\xdb':1042, + '\xdc':987,'\xdd':603,'\xde':987,'\xdf':603,'\xe0':494,'\xe1':329,'\xe2':790,'\xe3':790,'\xe4':786,'\xe5':713,'\xe6':384,'\xe7':384,'\xe8':384,'\xe9':384,'\xea':384,'\xeb':384,'\xec':494,'\xed':494,'\xee':494,'\xef':494,'\xf0':0,'\xf1':329, + '\xf2':274,'\xf3':686,'\xf4':686,'\xf5':686,'\xf6':384,'\xf7':384,'\xf8':384,'\xf9':384,'\xfa':384,'\xfb':384,'\xfc':494,'\xfd':494,'\xfe':494,'\xff':0} + +fpdf_charwidths['times']={ + '\x00':250,'\x01':250,'\x02':250,'\x03':250,'\x04':250,'\x05':250,'\x06':250,'\x07':250,'\x08':250,'\t':250,'\n':250,'\x0b':250,'\x0c':250,'\r':250,'\x0e':250,'\x0f':250,'\x10':250,'\x11':250,'\x12':250,'\x13':250,'\x14':250,'\x15':250, + '\x16':250,'\x17':250,'\x18':250,'\x19':250,'\x1a':250,'\x1b':250,'\x1c':250,'\x1d':250,'\x1e':250,'\x1f':250,' ':250,'!':333,'"':408,'#':500,'$':500,'%':833,'&':778,'\'':180,'(':333,')':333,'*':500,'+':564, + ',':250,'-':333,'.':250,'/':278,'0':500,'1':500,'2':500,'3':500,'4':500,'5':500,'6':500,'7':500,'8':500,'9':500,':':278,';':278,'<':564,'=':564,'>':564,'?':444,'@':921,'A':722, + 'B':667,'C':667,'D':722,'E':611,'F':556,'G':722,'H':722,'I':333,'J':389,'K':722,'L':611,'M':889,'N':722,'O':722,'P':556,'Q':722,'R':667,'S':556,'T':611,'U':722,'V':722,'W':944, + 'X':722,'Y':722,'Z':611,'[':333,'\\':278,']':333,'^':469,'_':500,'`':333,'a':444,'b':500,'c':444,'d':500,'e':444,'f':333,'g':500,'h':500,'i':278,'j':278,'k':500,'l':278,'m':778, + 'n':500,'o':500,'p':500,'q':500,'r':333,'s':389,'t':278,'u':500,'v':500,'w':722,'x':500,'y':500,'z':444,'{':480,'|':200,'}':480,'~':541,'\x7f':350,'\x80':500,'\x81':350,'\x82':333,'\x83':500, + '\x84':444,'\x85':1000,'\x86':500,'\x87':500,'\x88':333,'\x89':1000,'\x8a':556,'\x8b':333,'\x8c':889,'\x8d':350,'\x8e':611,'\x8f':350,'\x90':350,'\x91':333,'\x92':333,'\x93':444,'\x94':444,'\x95':350,'\x96':500,'\x97':1000,'\x98':333,'\x99':980, + '\x9a':389,'\x9b':333,'\x9c':722,'\x9d':350,'\x9e':444,'\x9f':722,'\xa0':250,'\xa1':333,'\xa2':500,'\xa3':500,'\xa4':500,'\xa5':500,'\xa6':200,'\xa7':500,'\xa8':333,'\xa9':760,'\xaa':276,'\xab':500,'\xac':564,'\xad':333,'\xae':760,'\xaf':333, + '\xb0':400,'\xb1':564,'\xb2':300,'\xb3':300,'\xb4':333,'\xb5':500,'\xb6':453,'\xb7':250,'\xb8':333,'\xb9':300,'\xba':310,'\xbb':500,'\xbc':750,'\xbd':750,'\xbe':750,'\xbf':444,'\xc0':722,'\xc1':722,'\xc2':722,'\xc3':722,'\xc4':722,'\xc5':722, + '\xc6':889,'\xc7':667,'\xc8':611,'\xc9':611,'\xca':611,'\xcb':611,'\xcc':333,'\xcd':333,'\xce':333,'\xcf':333,'\xd0':722,'\xd1':722,'\xd2':722,'\xd3':722,'\xd4':722,'\xd5':722,'\xd6':722,'\xd7':564,'\xd8':722,'\xd9':722,'\xda':722,'\xdb':722, + '\xdc':722,'\xdd':722,'\xde':556,'\xdf':500,'\xe0':444,'\xe1':444,'\xe2':444,'\xe3':444,'\xe4':444,'\xe5':444,'\xe6':667,'\xe7':444,'\xe8':444,'\xe9':444,'\xea':444,'\xeb':444,'\xec':278,'\xed':278,'\xee':278,'\xef':278,'\xf0':500,'\xf1':500, + '\xf2':500,'\xf3':500,'\xf4':500,'\xf5':500,'\xf6':500,'\xf7':564,'\xf8':500,'\xf9':500,'\xfa':500,'\xfb':500,'\xfc':500,'\xfd':500,'\xfe':500,'\xff':500} + +fpdf_charwidths['timesB']={ + '\x00':250,'\x01':250,'\x02':250,'\x03':250,'\x04':250,'\x05':250,'\x06':250,'\x07':250,'\x08':250,'\t':250,'\n':250,'\x0b':250,'\x0c':250,'\r':250,'\x0e':250,'\x0f':250,'\x10':250,'\x11':250,'\x12':250,'\x13':250,'\x14':250,'\x15':250, + '\x16':250,'\x17':250,'\x18':250,'\x19':250,'\x1a':250,'\x1b':250,'\x1c':250,'\x1d':250,'\x1e':250,'\x1f':250,' ':250,'!':333,'"':555,'#':500,'$':500,'%':1000,'&':833,'\'':278,'(':333,')':333,'*':500,'+':570, + ',':250,'-':333,'.':250,'/':278,'0':500,'1':500,'2':500,'3':500,'4':500,'5':500,'6':500,'7':500,'8':500,'9':500,':':333,';':333,'<':570,'=':570,'>':570,'?':500,'@':930,'A':722, + 'B':667,'C':722,'D':722,'E':667,'F':611,'G':778,'H':778,'I':389,'J':500,'K':778,'L':667,'M':944,'N':722,'O':778,'P':611,'Q':778,'R':722,'S':556,'T':667,'U':722,'V':722,'W':1000, + 'X':722,'Y':722,'Z':667,'[':333,'\\':278,']':333,'^':581,'_':500,'`':333,'a':500,'b':556,'c':444,'d':556,'e':444,'f':333,'g':500,'h':556,'i':278,'j':333,'k':556,'l':278,'m':833, + 'n':556,'o':500,'p':556,'q':556,'r':444,'s':389,'t':333,'u':556,'v':500,'w':722,'x':500,'y':500,'z':444,'{':394,'|':220,'}':394,'~':520,'\x7f':350,'\x80':500,'\x81':350,'\x82':333,'\x83':500, + '\x84':500,'\x85':1000,'\x86':500,'\x87':500,'\x88':333,'\x89':1000,'\x8a':556,'\x8b':333,'\x8c':1000,'\x8d':350,'\x8e':667,'\x8f':350,'\x90':350,'\x91':333,'\x92':333,'\x93':500,'\x94':500,'\x95':350,'\x96':500,'\x97':1000,'\x98':333,'\x99':1000, + '\x9a':389,'\x9b':333,'\x9c':722,'\x9d':350,'\x9e':444,'\x9f':722,'\xa0':250,'\xa1':333,'\xa2':500,'\xa3':500,'\xa4':500,'\xa5':500,'\xa6':220,'\xa7':500,'\xa8':333,'\xa9':747,'\xaa':300,'\xab':500,'\xac':570,'\xad':333,'\xae':747,'\xaf':333, + '\xb0':400,'\xb1':570,'\xb2':300,'\xb3':300,'\xb4':333,'\xb5':556,'\xb6':540,'\xb7':250,'\xb8':333,'\xb9':300,'\xba':330,'\xbb':500,'\xbc':750,'\xbd':750,'\xbe':750,'\xbf':500,'\xc0':722,'\xc1':722,'\xc2':722,'\xc3':722,'\xc4':722,'\xc5':722, + '\xc6':1000,'\xc7':722,'\xc8':667,'\xc9':667,'\xca':667,'\xcb':667,'\xcc':389,'\xcd':389,'\xce':389,'\xcf':389,'\xd0':722,'\xd1':722,'\xd2':778,'\xd3':778,'\xd4':778,'\xd5':778,'\xd6':778,'\xd7':570,'\xd8':778,'\xd9':722,'\xda':722,'\xdb':722, + '\xdc':722,'\xdd':722,'\xde':611,'\xdf':556,'\xe0':500,'\xe1':500,'\xe2':500,'\xe3':500,'\xe4':500,'\xe5':500,'\xe6':722,'\xe7':444,'\xe8':444,'\xe9':444,'\xea':444,'\xeb':444,'\xec':278,'\xed':278,'\xee':278,'\xef':278,'\xf0':500,'\xf1':556, + '\xf2':500,'\xf3':500,'\xf4':500,'\xf5':500,'\xf6':500,'\xf7':570,'\xf8':500,'\xf9':556,'\xfa':556,'\xfb':556,'\xfc':556,'\xfd':500,'\xfe':556,'\xff':500} + +fpdf_charwidths['timesBI']={ + '\x00':250,'\x01':250,'\x02':250,'\x03':250,'\x04':250,'\x05':250,'\x06':250,'\x07':250,'\x08':250,'\t':250,'\n':250,'\x0b':250,'\x0c':250,'\r':250,'\x0e':250,'\x0f':250,'\x10':250,'\x11':250,'\x12':250,'\x13':250,'\x14':250,'\x15':250, + '\x16':250,'\x17':250,'\x18':250,'\x19':250,'\x1a':250,'\x1b':250,'\x1c':250,'\x1d':250,'\x1e':250,'\x1f':250,' ':250,'!':389,'"':555,'#':500,'$':500,'%':833,'&':778,'\'':278,'(':333,')':333,'*':500,'+':570, + ',':250,'-':333,'.':250,'/':278,'0':500,'1':500,'2':500,'3':500,'4':500,'5':500,'6':500,'7':500,'8':500,'9':500,':':333,';':333,'<':570,'=':570,'>':570,'?':500,'@':832,'A':667, + 'B':667,'C':667,'D':722,'E':667,'F':667,'G':722,'H':778,'I':389,'J':500,'K':667,'L':611,'M':889,'N':722,'O':722,'P':611,'Q':722,'R':667,'S':556,'T':611,'U':722,'V':667,'W':889, + 'X':667,'Y':611,'Z':611,'[':333,'\\':278,']':333,'^':570,'_':500,'`':333,'a':500,'b':500,'c':444,'d':500,'e':444,'f':333,'g':500,'h':556,'i':278,'j':278,'k':500,'l':278,'m':778, + 'n':556,'o':500,'p':500,'q':500,'r':389,'s':389,'t':278,'u':556,'v':444,'w':667,'x':500,'y':444,'z':389,'{':348,'|':220,'}':348,'~':570,'\x7f':350,'\x80':500,'\x81':350,'\x82':333,'\x83':500, + '\x84':500,'\x85':1000,'\x86':500,'\x87':500,'\x88':333,'\x89':1000,'\x8a':556,'\x8b':333,'\x8c':944,'\x8d':350,'\x8e':611,'\x8f':350,'\x90':350,'\x91':333,'\x92':333,'\x93':500,'\x94':500,'\x95':350,'\x96':500,'\x97':1000,'\x98':333,'\x99':1000, + '\x9a':389,'\x9b':333,'\x9c':722,'\x9d':350,'\x9e':389,'\x9f':611,'\xa0':250,'\xa1':389,'\xa2':500,'\xa3':500,'\xa4':500,'\xa5':500,'\xa6':220,'\xa7':500,'\xa8':333,'\xa9':747,'\xaa':266,'\xab':500,'\xac':606,'\xad':333,'\xae':747,'\xaf':333, + '\xb0':400,'\xb1':570,'\xb2':300,'\xb3':300,'\xb4':333,'\xb5':576,'\xb6':500,'\xb7':250,'\xb8':333,'\xb9':300,'\xba':300,'\xbb':500,'\xbc':750,'\xbd':750,'\xbe':750,'\xbf':500,'\xc0':667,'\xc1':667,'\xc2':667,'\xc3':667,'\xc4':667,'\xc5':667, + '\xc6':944,'\xc7':667,'\xc8':667,'\xc9':667,'\xca':667,'\xcb':667,'\xcc':389,'\xcd':389,'\xce':389,'\xcf':389,'\xd0':722,'\xd1':722,'\xd2':722,'\xd3':722,'\xd4':722,'\xd5':722,'\xd6':722,'\xd7':570,'\xd8':722,'\xd9':722,'\xda':722,'\xdb':722, + '\xdc':722,'\xdd':611,'\xde':611,'\xdf':500,'\xe0':500,'\xe1':500,'\xe2':500,'\xe3':500,'\xe4':500,'\xe5':500,'\xe6':722,'\xe7':444,'\xe8':444,'\xe9':444,'\xea':444,'\xeb':444,'\xec':278,'\xed':278,'\xee':278,'\xef':278,'\xf0':500,'\xf1':556, + '\xf2':500,'\xf3':500,'\xf4':500,'\xf5':500,'\xf6':500,'\xf7':570,'\xf8':500,'\xf9':556,'\xfa':556,'\xfb':556,'\xfc':556,'\xfd':444,'\xfe':500,'\xff':444} + +fpdf_charwidths['timesI']={ + '\x00':250,'\x01':250,'\x02':250,'\x03':250,'\x04':250,'\x05':250,'\x06':250,'\x07':250,'\x08':250,'\t':250,'\n':250,'\x0b':250,'\x0c':250,'\r':250,'\x0e':250,'\x0f':250,'\x10':250,'\x11':250,'\x12':250,'\x13':250,'\x14':250,'\x15':250, + '\x16':250,'\x17':250,'\x18':250,'\x19':250,'\x1a':250,'\x1b':250,'\x1c':250,'\x1d':250,'\x1e':250,'\x1f':250,' ':250,'!':333,'"':420,'#':500,'$':500,'%':833,'&':778,'\'':214,'(':333,')':333,'*':500,'+':675, + ',':250,'-':333,'.':250,'/':278,'0':500,'1':500,'2':500,'3':500,'4':500,'5':500,'6':500,'7':500,'8':500,'9':500,':':333,';':333,'<':675,'=':675,'>':675,'?':500,'@':920,'A':611, + 'B':611,'C':667,'D':722,'E':611,'F':611,'G':722,'H':722,'I':333,'J':444,'K':667,'L':556,'M':833,'N':667,'O':722,'P':611,'Q':722,'R':611,'S':500,'T':556,'U':722,'V':611,'W':833, + 'X':611,'Y':556,'Z':556,'[':389,'\\':278,']':389,'^':422,'_':500,'`':333,'a':500,'b':500,'c':444,'d':500,'e':444,'f':278,'g':500,'h':500,'i':278,'j':278,'k':444,'l':278,'m':722, + 'n':500,'o':500,'p':500,'q':500,'r':389,'s':389,'t':278,'u':500,'v':444,'w':667,'x':444,'y':444,'z':389,'{':400,'|':275,'}':400,'~':541,'\x7f':350,'\x80':500,'\x81':350,'\x82':333,'\x83':500, + '\x84':556,'\x85':889,'\x86':500,'\x87':500,'\x88':333,'\x89':1000,'\x8a':500,'\x8b':333,'\x8c':944,'\x8d':350,'\x8e':556,'\x8f':350,'\x90':350,'\x91':333,'\x92':333,'\x93':556,'\x94':556,'\x95':350,'\x96':500,'\x97':889,'\x98':333,'\x99':980, + '\x9a':389,'\x9b':333,'\x9c':667,'\x9d':350,'\x9e':389,'\x9f':556,'\xa0':250,'\xa1':389,'\xa2':500,'\xa3':500,'\xa4':500,'\xa5':500,'\xa6':275,'\xa7':500,'\xa8':333,'\xa9':760,'\xaa':276,'\xab':500,'\xac':675,'\xad':333,'\xae':760,'\xaf':333, + '\xb0':400,'\xb1':675,'\xb2':300,'\xb3':300,'\xb4':333,'\xb5':500,'\xb6':523,'\xb7':250,'\xb8':333,'\xb9':300,'\xba':310,'\xbb':500,'\xbc':750,'\xbd':750,'\xbe':750,'\xbf':500,'\xc0':611,'\xc1':611,'\xc2':611,'\xc3':611,'\xc4':611,'\xc5':611, + '\xc6':889,'\xc7':667,'\xc8':611,'\xc9':611,'\xca':611,'\xcb':611,'\xcc':333,'\xcd':333,'\xce':333,'\xcf':333,'\xd0':722,'\xd1':667,'\xd2':722,'\xd3':722,'\xd4':722,'\xd5':722,'\xd6':722,'\xd7':675,'\xd8':722,'\xd9':722,'\xda':722,'\xdb':722, + '\xdc':722,'\xdd':556,'\xde':611,'\xdf':500,'\xe0':500,'\xe1':500,'\xe2':500,'\xe3':500,'\xe4':500,'\xe5':500,'\xe6':667,'\xe7':444,'\xe8':444,'\xe9':444,'\xea':444,'\xeb':444,'\xec':278,'\xed':278,'\xee':278,'\xef':278,'\xf0':500,'\xf1':500, + '\xf2':500,'\xf3':500,'\xf4':500,'\xf5':500,'\xf6':500,'\xf7':675,'\xf8':500,'\xf9':500,'\xfa':500,'\xfb':500,'\xfc':500,'\xfd':444,'\xfe':500,'\xff':444} + +fpdf_charwidths['zapfdingbats']={ + '\x00':0,'\x01':0,'\x02':0,'\x03':0,'\x04':0,'\x05':0,'\x06':0,'\x07':0,'\x08':0,'\t':0,'\n':0,'\x0b':0,'\x0c':0,'\r':0,'\x0e':0,'\x0f':0,'\x10':0,'\x11':0,'\x12':0,'\x13':0,'\x14':0,'\x15':0, + '\x16':0,'\x17':0,'\x18':0,'\x19':0,'\x1a':0,'\x1b':0,'\x1c':0,'\x1d':0,'\x1e':0,'\x1f':0,' ':278,'!':974,'"':961,'#':974,'$':980,'%':719,'&':789,'\'':790,'(':791,')':690,'*':960,'+':939, + ',':549,'-':855,'.':911,'/':933,'0':911,'1':945,'2':974,'3':755,'4':846,'5':762,'6':761,'7':571,'8':677,'9':763,':':760,';':759,'<':754,'=':494,'>':552,'?':537,'@':577,'A':692, + 'B':786,'C':788,'D':788,'E':790,'F':793,'G':794,'H':816,'I':823,'J':789,'K':841,'L':823,'M':833,'N':816,'O':831,'P':923,'Q':744,'R':723,'S':749,'T':790,'U':792,'V':695,'W':776, + 'X':768,'Y':792,'Z':759,'[':707,'\\':708,']':682,'^':701,'_':826,'`':815,'a':789,'b':789,'c':707,'d':687,'e':696,'f':689,'g':786,'h':787,'i':713,'j':791,'k':785,'l':791,'m':873, + 'n':761,'o':762,'p':762,'q':759,'r':759,'s':892,'t':892,'u':788,'v':784,'w':438,'x':138,'y':277,'z':415,'{':392,'|':392,'}':668,'~':668,'\x7f':0,'\x80':390,'\x81':390,'\x82':317,'\x83':317, + '\x84':276,'\x85':276,'\x86':509,'\x87':509,'\x88':410,'\x89':410,'\x8a':234,'\x8b':234,'\x8c':334,'\x8d':334,'\x8e':0,'\x8f':0,'\x90':0,'\x91':0,'\x92':0,'\x93':0,'\x94':0,'\x95':0,'\x96':0,'\x97':0,'\x98':0,'\x99':0, + '\x9a':0,'\x9b':0,'\x9c':0,'\x9d':0,'\x9e':0,'\x9f':0,'\xa0':0,'\xa1':732,'\xa2':544,'\xa3':544,'\xa4':910,'\xa5':667,'\xa6':760,'\xa7':760,'\xa8':776,'\xa9':595,'\xaa':694,'\xab':626,'\xac':788,'\xad':788,'\xae':788,'\xaf':788, + '\xb0':788,'\xb1':788,'\xb2':788,'\xb3':788,'\xb4':788,'\xb5':788,'\xb6':788,'\xb7':788,'\xb8':788,'\xb9':788,'\xba':788,'\xbb':788,'\xbc':788,'\xbd':788,'\xbe':788,'\xbf':788,'\xc0':788,'\xc1':788,'\xc2':788,'\xc3':788,'\xc4':788,'\xc5':788, + '\xc6':788,'\xc7':788,'\xc8':788,'\xc9':788,'\xca':788,'\xcb':788,'\xcc':788,'\xcd':788,'\xce':788,'\xcf':788,'\xd0':788,'\xd1':788,'\xd2':788,'\xd3':788,'\xd4':894,'\xd5':838,'\xd6':1016,'\xd7':458,'\xd8':748,'\xd9':924,'\xda':748,'\xdb':918, + '\xdc':927,'\xdd':928,'\xde':928,'\xdf':834,'\xe0':873,'\xe1':828,'\xe2':924,'\xe3':924,'\xe4':917,'\xe5':930,'\xe6':931,'\xe7':463,'\xe8':883,'\xe9':836,'\xea':836,'\xeb':867,'\xec':867,'\xed':696,'\xee':696,'\xef':874,'\xf0':0,'\xf1':874, + '\xf2':760,'\xf3':946,'\xf4':771,'\xf5':865,'\xf6':771,'\xf7':888,'\xf8':967,'\xf9':888,'\xfa':831,'\xfb':873,'\xfc':927,'\xfd':970,'\xfe':918,'\xff':0} + diff --git a/gluon/contrib/fpdf/fpdf.py b/gluon/contrib/fpdf/fpdf.py index 4f9a3eb5..62503e10 100644 --- a/gluon/contrib/fpdf/fpdf.py +++ b/gluon/contrib/fpdf/fpdf.py @@ -23,9 +23,13 @@ try: except ImportError: import pickle +# Check if PIL is available (tries importing both pypi version and corrected or manually installed versions). +# Necessary for JPEG and GIF support. try: - # Check if PIL is available, necessary for JPEG support. - import Image + try: + import Image + except: + from PIL import Image except ImportError: Image = None @@ -48,7 +52,7 @@ def set_global(var, val): class FPDF(object): "PDF Generation class" - + def __init__(self, orientation='P',unit='mm',format='A4'): # Some checks self._dochecks() @@ -352,7 +356,7 @@ class FPDF(object): elif (self.current_font['desc']['MissingWidth']) : w += self.current_font['desc']['MissingWidth'] #elif (isset($this->CurrentFont['MissingWidth'])) { $w += $this->CurrentFont['MissingWidth']; } - else: + else: w += 500 else: for i in xrange(0, l): @@ -397,10 +401,10 @@ class FPDF(object): global SYSTEM_TTFONTS if os.path.exists(fname): ttffilename = fname - elif (FPDF_FONT_DIR and + elif (FPDF_FONT_DIR and os.path.exists(os.path.join(FPDF_FONT_DIR, fname))): ttffilename = os.path.join(FPDF_FONT_DIR, fname) - elif (SYSTEM_TTFONTS and + elif (SYSTEM_TTFONTS and os.path.exists(os.path.join(SYSTEM_TTFONTS, fname))): ttffilename = os.path.join(SYSTEM_TTFONTS, fname) else: @@ -455,11 +459,11 @@ class FPDF(object): else: sbarr = range(0,32) self.fonts[fontkey] = { - 'i': len(self.fonts)+1, 'type': font_dict['type'], - 'name': font_dict['name'], 'desc': font_dict['desc'], - 'up': font_dict['up'], 'ut': font_dict['ut'], - 'cw': font_dict['cw'], - 'ttffile': font_dict['ttffile'], 'fontkey': fontkey, + 'i': len(self.fonts)+1, 'type': font_dict['type'], + 'name': font_dict['name'], 'desc': font_dict['desc'], + 'up': font_dict['up'], 'ut': font_dict['ut'], + 'cw': font_dict['cw'], + 'ttffile': font_dict['ttffile'], 'fontkey': fontkey, 'subset': sbarr, 'unifilename': unifilename, } self.font_files[fontkey] = {'length1': font_dict['originalsize'], @@ -490,7 +494,7 @@ class FPDF(object): if (type == 'TrueType'): self.font_files[filename]={'length1': originalsize} else: - self.font_files[filename]={'length1': size1, + self.font_files[filename]={'length1': size1, 'length2': size2} def set_font(self, family,style='',size=0): @@ -656,7 +660,7 @@ class FPDF(object): dx=self.c_margin if(self.color_flag): s+='q '+self.text_color+' ' - + # If multibyte, Tw has no effect - do word spacing using an adjustment before each space if (self.ws and self.unifontsubset): for uni in UTF8StringToArray(txt): @@ -680,9 +684,9 @@ class FPDF(object): for uni in UTF8StringToArray(txt): self.current_font['subset'].append(uni) else: - txt2 = txt.replace('\\','\\\\').replace(')','\\)').replace('(','\\(') + txt2 = self._escape(txt) s += sprintf('BT %.2f %.2f Td (%s) Tj ET',(self.x+dx)*k,(self.h-(self.y+.5*h+.3*self.font_size))*k,txt2) - + if(self.underline): s+=' '+self._dounderline(self.x+dx,self.y+.5*h+.3*self.font_size,txt) if(self.color_flag): @@ -760,7 +764,7 @@ class FPDF(object): sep=i ls=l ns+=1 - if self.unifontsubset: + if self.unifontsubset: l += self.get_string_width(c) / self.font_size*1000.0 else: l += cw.get(c,0) @@ -844,7 +848,7 @@ class FPDF(object): continue if(c==' '): sep=i - if self.unifontsubset: + if self.unifontsubset: l += self.get_string_width(c) / self.font_size*1000.0 else: l += cw.get(c,0) @@ -1198,7 +1202,7 @@ class FPDF(object): self._out('<>') @@ -1260,7 +1264,7 @@ class FPDF(object): for kd in ('Ascent', 'Descent', 'CapHeight', 'Flags', 'FontBBox', 'ItalicAngle', 'StemV', 'MissingWidth'): v = font['desc'][kd] if (kd == 'Flags'): - v = v | 4; + v = v | 4; v = v & ~32; # SYMBOLIC font flag self._out(' /%s %s' % (kd, v)) self._out('/FontFile2 ' + str(self.n + 2) + ' 0 R') @@ -1282,7 +1286,7 @@ class FPDF(object): self._putstream(cidtogidmap) self._out('endobj') - #Font file + #Font file self._newobj() self._out('< 255 and (cid not in font['subset']) or not cid): # + if (cid > 255 and (cid not in font['subset']) or not cid): # continue if ('dw' not in font or (font['dw'] and width != font['dw'])): if (cid == (prevcid + 1)): @@ -1396,7 +1400,7 @@ class FPDF(object): if (len(set(ws)) == 1): w.append(' %s %s %s' % (k, k + len(ws) - 1, ws[0])) else: - w.append(' %s [ %s ]\n' % (k, ' '.join([str(int(h)) for h in ws]))) ## + w.append(' %s [ %s ]\n' % (k, ' '.join([str(int(h)) for h in ws]))) ## self._out('/W [%s]' % ''.join(w)) def _putimages(self): @@ -1408,7 +1412,7 @@ class FPDF(object): del info['data'] if 'smask' in info: del info['smask'] - + def _putimage(self, info): if 'data' in info: self._newobj() @@ -1637,9 +1641,11 @@ class FPDF(object): # Use temporary file f = tempfile.NamedTemporaryFile(delete=False, suffix=".png") tmp = f.name - transparency = im.info['transparency'] f.close() - im.save(tmp, transparency=transparency) + if "transparency" in im.info: + im.save(tmp, transparency = im.info['transparency']) + else: + im.save(tmp) info = self._parsepng(tmp) os.unlink(tmp) return info @@ -1759,7 +1765,7 @@ class FPDF(object): def _freadint(self, f): #Read a 4-byte integer from file try: - return struct.unpack('>HH',f.read(4))[1] + return struct.unpack('>I', f.read(4))[0] except: return None @@ -1769,7 +1775,7 @@ class FPDF(object): def _escape(self, s): #Add \ before \, ( and ) - return s.replace('\\','\\\\').replace(')','\\)').replace('(','\\(') + return s.replace('\\','\\\\').replace(')','\\)').replace('(','\\(').replace('\r','\\r') def _putstream(self, s): self._out('stream') @@ -1785,20 +1791,20 @@ class FPDF(object): def interleaved2of5(self, txt, x, y, w=1.0, h=10.0): "Barcode I2of5 (numeric), adds a 0 if odd lenght" - narrow = w / 3.0 + narrow = w / 3.0 wide = w - + # wide/narrow codes for the digits bar_char={'0': 'nnwwn', '1': 'wnnnw', '2': 'nwnnw', '3': 'wwnnn', '4': 'nnwnw', '5': 'wnwnn', '6': 'nwwnn', '7': 'nnnww', '8': 'wnnwn', '9': 'nwnwn', 'A': 'nn', 'Z': 'wn'} - + self.set_fill_color(0) code = txt # add leading zero if code-length is odd if len(code) % 2 != 0: code = '0' + code - + # add start and stop codes code = 'AA' + code.lower() + 'ZA' @@ -1837,7 +1843,7 @@ class FPDF(object): narrow = w / 3.0 gap = narrow - bar_char={'0': 'nnnwwnwnn', '1': 'wnnwnnnnw', '2': 'nnwwnnnnw', + bar_char={'0': 'nnnwwnwnn', '1': 'wnnwnnnnw', '2': 'nnwwnnnnw', '3': 'wnwwnnnnn', '4': 'nnnwwnnnw', '5': 'wnnwwnnnn', '6': 'nnwwwnnnn', '7': 'nnnwnnwnw', '8': 'wnnwnnwnn', '9': 'nnwwnnwnn', 'A': 'wnnnnwnnw', 'B': 'nnwnnwnnw', @@ -1854,8 +1860,8 @@ class FPDF(object): '+': 'nwnnnwnwn', '%': 'nnnwnwnwn'} self.set_fill_color(0) - code = txt - + code = txt + code = code.upper() for i in xrange (0, len(code), 2): char_bar = code[i] @@ -1865,7 +1871,7 @@ class FPDF(object): seq= '' for s in xrange(0, len(bar_char[char_bar])): - seq += bar_char[char_bar][s] + seq += bar_char[char_bar][s] for bar in xrange(0, len(seq)): if seq[bar] == 'n': @@ -1879,4 +1885,3 @@ class FPDF(object): x += gap - diff --git a/gluon/contrib/fpdf/html.py b/gluon/contrib/fpdf/html.py index f379fcd5..a1269bc9 100644 --- a/gluon/contrib/fpdf/html.py +++ b/gluon/contrib/fpdf/html.py @@ -26,9 +26,8 @@ def hex2dec(color = "#000000"): class HTML2FPDF(HTMLParser): "Render basic HTML to FPDF" - def __init__(self, pdf, image_map = None): + def __init__(self, pdf, image_map=None): HTMLParser.__init__(self) - self.image_map = image_map or (lambda src: src) self.style = {} self.pre = False self.href = '' @@ -36,8 +35,9 @@ class HTML2FPDF(HTMLParser): self.page_links = {} self.font_list = ("times","courier", "helvetica") self.font = None - self.font_stack = [] + self.font_stack = [] self.pdf = pdf + self.image_map = image_map or (lambda src: src) self.r = self.g = self.b = 0 self.indent = 0 self.bullet = [] @@ -55,7 +55,7 @@ class HTML2FPDF(HTMLParser): self.thead = None self.tfoot = None self.theader_out = self.tfooter_out = False - + def width2mm(self, length): if length[-1]=='%': total = self.pdf.w - self.pdf.r_margin - self.pdf.l_margin @@ -102,13 +102,13 @@ class HTML2FPDF(HTMLParser): self.pdf.add_page() self.theader_out = self.tfooter_out = False if self.tfoot is None and self.thead is None: - if not self.theader_out: + if not self.theader_out: self.output_table_header() self.box_shadow(w, h, bgcolor) if DEBUG: print "td cell", self.pdf.x, w, txt, "*" self.pdf.cell(w,h,txt,border,0,align) elif self.table is not None: - # ignore anything else than td inside a table + # ignore anything else than td inside a table pass elif self.align: if DEBUG: print "cell", txt, "*" @@ -143,7 +143,7 @@ class HTML2FPDF(HTMLParser): self.pdf.set_x(self.table_offset) #self.pdf.set_x(x) self.theader_out = True - + def output_table_footer(self): if self.tfooter: x = self.pdf.x @@ -157,7 +157,7 @@ class HTML2FPDF(HTMLParser): if int(self.table.get('border', 0)): self.output_table_sep() self.tfooter_out = True - + def output_table_sep(self): self.pdf.set_x(self.table_offset) x1 = self.pdf.x @@ -337,7 +337,7 @@ class HTML2FPDF(HTMLParser): if face: self.pdf.set_text_color(0,0,0) self.color = None - self.set_font(face, size) + self.set_font(face, size) self.font = None if tag=='center': self.align = None @@ -354,7 +354,7 @@ class HTML2FPDF(HTMLParser): self.set_style('u', False) self.set_style('b', False) self.set_style('i', False) - self.set_text_color() + self.set_text_color() def set_style(self, tag=None, enable=None): #Modify style and select corresponding font @@ -376,7 +376,7 @@ class HTML2FPDF(HTMLParser): self.r = r self.g = g self.b = b - + def put_link(self, url, txt): #Put a hyperlink self.set_text_color(0,0,255) @@ -396,4 +396,3 @@ class HTMLMixin(object): h2p = HTML2FPDF(self, image_map) h2p.feed(text) - diff --git a/gluon/contrib/fpdf/php.py b/gluon/contrib/fpdf/php.py index 569bf9e0..00cbe4f0 100644 --- a/gluon/contrib/fpdf/php.py +++ b/gluon/contrib/fpdf/php.py @@ -1,49 +1,49 @@ -#!/usr/bin/env python -# -*- coding: latin-1 -*- - -# fpdf php helpers: - -def substr(s, start, length=-1): - if length < 0: - length=len(s)-start - return s[start:start+length] - -def sprintf(fmt, *args): return fmt % args - -def print_r(array): - if not isinstance(array, dict): - array = dict([(k, k) for k in array]) - for k, v in array.items(): - print "[%s] => %s" % (k, v), - -def UTF8ToUTF16BE(instr, setbom=True): - "Converts UTF-8 strings to UTF16-BE." - outstr = "" - if (setbom): - outstr += "\xFE\xFF"; - if not isinstance(instr, unicode): - instr = instr.decode('UTF-8') - outstr += instr.encode('UTF-16BE') - return outstr - -def UTF8StringToArray(instr): - "Converts UTF-8 strings to codepoints array" - return [ord(c) for c in instr] - -# ttfints php helpers: - -def die(msg): - raise RuntimeError(msg) - -def str_repeat(s, count): - return s * count - -def str_pad(s, pad_length=0, pad_char= " ", pad_type= +1 ): - if pad_type<0: # pad left - return s.rjust(pad_length, pad_char) - elif pad_type>0: # pad right - return s.ljust(pad_length, pad_char) - else: # pad both - return s.center(pad_length, pad_char) - -strlen = count = lambda s: len(s) +#!/usr/bin/env python +# -*- coding: latin-1 -*- + +# fpdf php helpers: + +def substr(s, start, length=-1): + if length < 0: + length=len(s)-start + return s[start:start+length] + +def sprintf(fmt, *args): return fmt % args + +def print_r(array): + if not isinstance(array, dict): + array = dict([(k, k) for k in array]) + for k, v in array.items(): + print "[%s] => %s" % (k, v), + +def UTF8ToUTF16BE(instr, setbom=True): + "Converts UTF-8 strings to UTF16-BE." + outstr = "" + if (setbom): + outstr += "\xFE\xFF"; + if not isinstance(instr, unicode): + instr = instr.decode('UTF-8') + outstr += instr.encode('UTF-16BE') + return outstr + +def UTF8StringToArray(instr): + "Converts UTF-8 strings to codepoints array" + return [ord(c) for c in instr] + +# ttfints php helpers: + +def die(msg): + raise RuntimeError(msg) + +def str_repeat(s, count): + return s * count + +def str_pad(s, pad_length=0, pad_char= " ", pad_type= +1 ): + if pad_type<0: # pad left + return s.rjust(pad_length, pad_char) + elif pad_type>0: # pad right + return s.ljust(pad_length, pad_char) + else: # pad both + return s.center(pad_length, pad_char) + +strlen = count = lambda s: len(s) \ No newline at end of file diff --git a/gluon/contrib/fpdf/template.py b/gluon/contrib/fpdf/template.py index 651a1b83..2d38c9f9 100644 --- a/gluon/contrib/fpdf/template.py +++ b/gluon/contrib/fpdf/template.py @@ -18,7 +18,7 @@ class Template: if elements: self.elements = elements self.keys = [v['name'].lower() for v in self.elements] - self.handlers = {'T': self.text, 'L': self.line, 'I': self.image, + self.handlers = {'T': self.text, 'L': self.line, 'I': self.image, 'B': self.rect, 'BC': self.barcode, } self.pg_no = 0 self.texts = {} @@ -38,7 +38,7 @@ class Template: for row in csv.reader(open(infile, 'rb'), delimiter=delimiter): kargs = {} for i,v in enumerate(row): - if not v.startswith("'") and decimal_sep!=".": + if not v.startswith("'") and decimal_sep!=".": v = v.replace(decimal_sep,".") else: v = v @@ -53,7 +53,7 @@ class Template: def add_page(self): self.pg_no += 1 self.texts[self.pg_no] = {} - + def __setitem__(self, name, value): if self.has_key(name): if isinstance(value,unicode): @@ -69,7 +69,7 @@ class Template: def has_key(self, name): return name.lower() in self.keys - + def __getitem__(self, name): if self.has_key(name): key = name.lower() @@ -101,7 +101,7 @@ class Template: return pdf.multi_cell(w=element['x2']-element['x1'], h=element['y2']-element['y1'], txt=text,align=align,split_only=True) - + def render(self, outfile, dest="F"): pdf = self.pdf for pg in range(1, self.pg_no+1): @@ -118,11 +118,11 @@ class Template: self.handlers[element['type'].upper()](pdf, **element) if 'rotate' in element: pdf.rotate(0) - + return pdf.output(outfile, dest) - - def text(self, pdf, x1=0, y1=0, x2=0, y2=0, text='', font="arial", size=10, - bold=False, italic=False, underline=False, align="", + + def text(self, pdf, x1=0, y1=0, x2=0, y2=0, text='', font="arial", size=10, + bold=False, italic=False, underline=False, align="", foreground=0, backgroud=65535, multiline=None, *args, **kwargs): if text: @@ -201,7 +201,7 @@ if __name__ == "__main__": title="Sample Invoice", author="Sample Company", subject="Sample Customer", keywords="Electronic TAX Invoice") f.parse_csv(infile="invoice.csv", delimiter=";", decimal_sep=",") - + detail = "Lorem ipsum dolor sit amet, consectetur. " * 30 items = [] for i in range(1, 30): @@ -210,7 +210,7 @@ if __name__ == "__main__": price = round(random.random()*100,3) code = "%s%s%02d" % (chr(random.randint(65,90)), chr(random.randint(65,90)),i) items.append(dict(code=code, unit='u', - qty=qty, price=price, + qty=qty, price=price, amount=qty*price, ds="%s: %s" % (i,ds))) @@ -253,7 +253,7 @@ if __name__ == "__main__": f["company_name"] = "Sample Company" f["company_logo"] = "tutorial/logo.png" f["company_header1"] = "Some Address - somewhere -" - f["company_header2"] = "http://www.example.com" + f["company_header2"] = "http://www.example.com" f["company_footer1"] = "Tax Code ..." f["company_footer2"] = "Tax/VAT ID ..." f['number'] = '0001-00001234' @@ -261,9 +261,9 @@ if __name__ == "__main__": f['due_date'] = '2099-09-10' f['customer_name'] = "Sample Client" f['customer_address'] = "Siempreviva 1234" - + # print line item... - li = 0 + li = 0 k = 0 total = Decimal("0.00") for it in li_items: @@ -293,10 +293,9 @@ if __name__ == "__main__": else: f['total_label'] = 'SubTotal:' f['total'] = "%0.2f" % total - + f.render("./invoice.pdf") if sys.platform.startswith("linux"): os.system("evince ./invoice.pdf") else: os.system("./invoice.pdf") - diff --git a/gluon/contrib/fpdf/ttfonts.py b/gluon/contrib/fpdf/ttfonts.py index ddab0798..d9bc5784 100644 --- a/gluon/contrib/fpdf/ttfonts.py +++ b/gluon/contrib/fpdf/ttfonts.py @@ -1,1033 +1,1083 @@ -#****************************************************************************** -# TTFontFile class -# -# This class is based on The ReportLab Open Source PDF library -# written in Python - http://www.reportlab.com/software/opensource/ -# together with ideas from the OpenOffice source code and others. -# -# Version: 1.04 -# Date: 2011-09-18 -# Author: Ian Back -# License: LGPL -# Copyright (c) Ian Back, 2010 -# Ported to Python 2.7 by Mariano Reingart (reingart@gmail.com) on 2012 -# This header must be retained in any redistribution or -# modification of the file. -# -#****************************************************************************** - -from struct import pack, unpack, unpack_from -import re -import warnings -from php import die, substr, str_repeat, str_pad, strlen, count - - -# Define the value used in the "head" table of a created TTF file -# 0x74727565 "true" for Mac -# 0x00010000 for Windows -# Either seems to work for a font embedded in a PDF file -# when read by Adobe Reader on a Windows PC(!) -_TTF_MAC_HEADER = False - - -# TrueType Font Glyph operators -GF_WORDS = (1 << 0) -GF_SCALE = (1 << 3) -GF_MORE = (1 << 5) -GF_XYSCALE = (1 << 6) -GF_TWOBYTWO = (1 << 7) - - -def sub32(x, y): - xlo = x[1] - xhi = x[0] - ylo = y[1] - yhi = y[0] - if (ylo > xlo): - xlo += 1 << 16 - yhi += 1 - reslo = xlo-ylo - if (yhi > xhi): - xhi += 1 << 16 - reshi = xhi-yhi - reshi = reshi & 0xFFFF - return (reshi, reslo) - -def calcChecksum(data): - if (strlen(data) % 4): - data += str_repeat("\0", (4-(len(data) % 4))) - hi=0x0000 - lo=0x0000 - for i in range(0, len(data), 4): - hi += (ord(data[i])<<8) + ord(data[i+1]) - lo += (ord(data[i+2])<<8) + ord(data[i+3]) - hi += lo >> 16 - lo = lo & 0xFFFF - hi = hi & 0xFFFF - return (hi, lo) - - -class TTFontFile: - - def __init__(self): - self.maxStrLenRead = 200000 # Maximum size of glyf table to read in as string (otherwise reads each glyph from file) - - def getMetrics(self, file): - self.filename = file - self.fh = open(file,'rb') - self._pos = 0 - self.charWidths = [] - self.glyphPos = {} - self.charToGlyph = {} - self.tables = {} - self.otables = {} - self.ascent = 0 - self.descent = 0 - self.TTCFonts = {} - self.version = version = self.read_ulong() - if (version==0x4F54544F): - die("Postscript outlines are not supported") - if (version==0x74746366): - die("ERROR - TrueType Fonts Collections not supported") - if (version not in (0x00010000,0x74727565)): - die("Not a TrueType font: version=" + version) - self.readTableDirectory() - self.extractInfo() - self.fh.close() - - def readTableDirectory(self, ): - self.numTables = self.read_ushort() - self.searchRange = self.read_ushort() - self.entrySelector = self.read_ushort() - self.rangeShift = self.read_ushort() - self.tables = {} - for i in range(self.numTables): - record = {} - record['tag'] = self.read_tag() - record['checksum'] = (self.read_ushort(),self.read_ushort()) - record['offset'] = self.read_ulong() - record['length'] = self.read_ulong() - self.tables[record['tag']] = record - - def get_table_pos(self, tag): - offset = self.tables[tag]['offset'] - length = self.tables[tag]['length'] - return (offset, length) - - def seek(self, pos): - self._pos = pos - self.fh.seek(self._pos) - - def skip(self, delta): - self._pos = self._pos + delta - self.fh.seek(self._pos) - - def seek_table(self, tag, offset_in_table = 0): - tpos = self.get_table_pos(tag) - self._pos = tpos[0] + offset_in_table - self.fh.seek(self._pos) - return self._pos - - def read_tag(self): - self._pos += 4 - return self.fh.read(4) - - def read_short(self): - self._pos += 2 - s = self.fh.read(2) - a = (ord(s[0])<<8) + ord(s[1]) - if (a & (1 << 15) ): - a = (a - (1 << 16)) - return a - - def unpack_short(self, s): - a = (ord(s[0])<<8) + ord(s[1]) - if (a & (1 << 15) ): - a = (a - (1 << 16)) - return a - - def read_ushort(self): - self._pos += 2 - s = self.fh.read(2) - return (ord(s[0])<<8) + ord(s[1]) - - def read_ulong(self): - self._pos += 4 - s = self.fh.read(4) - # if large uInt32 as an integer, PHP converts it to -ve - return (ord(s[0])*16777216) + (ord(s[1])<<16) + (ord(s[2])<<8) + ord(s[3]) # 16777216 = 1<<24 - - def get_ushort(self, pos): - self.fh.seek(pos) - s = self.fh.read(2) - return (ord(s[0])<<8) + ord(s[1]) - - def get_ulong(self, pos): - self.fh.seek(pos) - s = self.fh.read(4) - # iF large uInt32 as an integer, PHP converts it to -ve - return (ord(s[0])*16777216) + (ord(s[1])<<16) + (ord(s[2])<<8) + ord(s[3]) # 16777216 = 1<<24 - - def pack_short(self, val): - if (val<0): - val = abs(val) - val = ~val - val += 1 - return pack(">H",val) - - def splice(self, stream, offset, value): - return substr(stream,0,offset) + value + substr(stream,offset+strlen(value)) - - def _set_ushort(self, stream, offset, value): - up = pack(">H", value) - return self.splice(stream, offset, up) - - def _set_short(self, stream, offset, val): - if (val<0): - val = abs(val) - val = ~val - val += 1 - up = pack(">H",val) - return self.splice(stream, offset, up) - - def get_chunk(self, pos, length): - self.fh.seek(pos) - if (length <1): return '' - return (self.fh.read(length)) - - def get_table(self, tag): - (pos, length) = self.get_table_pos(tag) - if (length == 0): - die('Truetype font (' + self.filename + '): error reading table: ' + tag) - self.fh.seek(pos) - return (self.fh.read(length)) - - def add(self, tag, data): - if (tag == 'head') : - data = self.splice(data, 8, "\0\0\0\0") - self.otables[tag] = data - -############################################/ -############################################/ - -############################################/ - - def extractInfo(self): - #################/ - # name - Naming table - #################/ - self.sFamilyClass = 0 - self.sFamilySubClass = 0 - - name_offset = self.seek_table("name") - format = self.read_ushort() - if (format != 0): - die("Unknown name table format " + format) - numRecords = self.read_ushort() - string_data_offset = name_offset + self.read_ushort() - names = {1:'',2:'',3:'',4:'',6:''} - K = names.keys() - nameCount = len(names) - for i in range(numRecords): - platformId = self.read_ushort() - encodingId = self.read_ushort() - languageId = self.read_ushort() - nameId = self.read_ushort() - length = self.read_ushort() - offset = self.read_ushort() - if (nameId not in K): continue - N = '' - if (platformId == 3 and encodingId == 1 and languageId == 0x409): # Microsoft, Unicode, US English, PS Name - opos = self._pos - self.seek(string_data_offset + offset) - if (length % 2 != 0): - die("PostScript name is UTF-16BE string of odd length") - length /= 2 - N = '' - while (length > 0): - char = self.read_ushort() - N += (chr(char)) - length -= 1 - self._pos = opos - self.seek(opos) - - elif (platformId == 1 and encodingId == 0 and languageId == 0): # Macintosh, Roman, English, PS Name - opos = self._pos - N = self.get_chunk(string_data_offset + offset, length) - self._pos = opos - self.seek(opos) - - if (N and names[nameId]==''): - names[nameId] = N - nameCount -= 1 - if (nameCount==0): break - - - if (names[6]): - psName = names[6] - elif (names[4]): - psName = re.sub(' ','-',names[4]) - elif (names[1]): - psName = re.sub(' ','-',names[1]) - else: - psName = '' - if (not psName): - die("Could not find PostScript font name") - self.name = psName - if (names[1]): - self.familyName = names[1] - else: - self.familyName = psName - if (names[2]): - self.styleName = names[2] - else: - self.styleName = 'Regular' - if (names[4]): - self.fullName = names[4] - else: - self.fullName = psName - if (names[3]): - self.uniqueFontID = names[3] - else: - self.uniqueFontID = psName - if (names[6]): - self.fullName = names[6] - - #################/ - # head - Font header table - #################/ - self.seek_table("head") - self.skip(18) - self.unitsPerEm = unitsPerEm = self.read_ushort() - scale = 1000 / float(unitsPerEm) - self.skip(16) - xMin = self.read_short() - yMin = self.read_short() - xMax = self.read_short() - yMax = self.read_short() - self.bbox = [(xMin*scale), (yMin*scale), (xMax*scale), (yMax*scale)] - self.skip(3*2) - indexToLocFormat = self.read_ushort() - glyphDataFormat = self.read_ushort() - if (glyphDataFormat != 0): - die('Unknown glyph data format ' + glyphDataFormat) - - #################/ - # hhea metrics table - #################/ - # ttf2t1 seems to use this value rather than the one in OS/2 - so put in for compatibility - if ("hhea" in self.tables): - self.seek_table("hhea") - self.skip(4) - hheaAscender = self.read_short() - hheaDescender = self.read_short() - self.ascent = (hheaAscender *scale) - self.descent = (hheaDescender *scale) - - - #################/ - # OS/2 - OS/2 and Windows metrics table - #################/ - if ("OS/2" in self.tables): - self.seek_table("OS/2") - version = self.read_ushort() - self.skip(2) - usWeightClass = self.read_ushort() - self.skip(2) - fsType = self.read_ushort() - if (fsType == 0x0002 or (fsType & 0x0300) != 0): - die('ERROR - Font file ' + self.filename + ' cannot be embedded due to copyright restrictions.') - self.restrictedUse = True - - self.skip(20) - sF = self.read_short() - self.sFamilyClass = (sF >> 8) - self.sFamilySubClass = (sF & 0xFF) - self._pos += 10 #PANOSE = 10 byte length - panose = self.fh.read(10) - self.skip(26) - sTypoAscender = self.read_short() - sTypoDescender = self.read_short() - if (not self.ascent): - self.ascent = (sTypoAscender*scale) - if (not self.descent): - self.descent = (sTypoDescender*scale) - if (version > 1): - self.skip(16) - sCapHeight = self.read_short() - self.capHeight = (sCapHeight*scale) - else: - self.capHeight = self.ascent - - else: - usWeightClass = 500 - if (not self.ascent): self.ascent = (yMax*scale) - if (not self.descent): self.descent = (yMin*scale) - self.capHeight = self.ascent - - self.stemV = 50 + int(pow((usWeightClass / 65.0),2)) - - #################/ - # post - PostScript table - #################/ - self.seek_table("post") - self.skip(4) - self.italicAngle = self.read_short() + self.read_ushort() / 65536.0 - self.underlinePosition = self.read_short() * scale - self.underlineThickness = self.read_short() * scale - isFixedPitch = self.read_ulong() - - self.flags = 4 - - if (self.italicAngle!= 0): - self.flags = self.flags | 64 - if (usWeightClass >= 600): - self.flags = self.flags | 262144 - if (isFixedPitch): - self.flags = self.flags | 1 - - #################/ - # hhea - Horizontal header table - #################/ - self.seek_table("hhea") - self.skip(32) - metricDataFormat = self.read_ushort() - if (metricDataFormat != 0): - die('Unknown horizontal metric data format '.metricDataFormat) - numberOfHMetrics = self.read_ushort() - if (numberOfHMetrics == 0): - die('Number of horizontal metrics is 0') - - #################/ - # maxp - Maximum profile table - #################/ - self.seek_table("maxp") - self.skip(4) - numGlyphs = self.read_ushort() - - #################/ - # cmap - Character to glyph index mapping table - #################/ - cmap_offset = self.seek_table("cmap") - self.skip(2) - cmapTableCount = self.read_ushort() - unicode_cmap_offset = 0 - for i in range(cmapTableCount): - platformID = self.read_ushort() - encodingID = self.read_ushort() - offset = self.read_ulong() - save_pos = self._pos - if ((platformID == 3 and encodingID == 1) or platformID == 0): # Microsoft, Unicode - format = self.get_ushort(cmap_offset + offset) - if (format == 4): - if (not unicode_cmap_offset): - unicode_cmap_offset = cmap_offset + offset - break - self.seek(save_pos ) - - if (not unicode_cmap_offset): - die('Font (' + self.filename + ') does not have cmap for Unicode (platform 3, encoding 1, format 4, or platform 0, any encoding, format 4)') - - glyphToChar = {} - charToGlyph = {} - self.getCMAP4(unicode_cmap_offset, glyphToChar, charToGlyph ) - - #################/ - # hmtx - Horizontal metrics table - #################/ - self.getHMTX(numberOfHMetrics, numGlyphs, glyphToChar, scale) - - -############################################/ -############################################/ - - def makeSubset(self, file, subset): - self.filename = file - self.fh = open(file ,'rb') - self._pos = 0 - self.charWidths = [] - self.glyphPos = {} - self.charToGlyph = {} - self.tables = {} - self.otables = {} - self.ascent = 0 - self.descent = 0 - self.skip(4) - self.maxUni = 0 - self.readTableDirectory() - - #################/ - # head - Font header table - #################/ - self.seek_table("head") - self.skip(50) - indexToLocFormat = self.read_ushort() - glyphDataFormat = self.read_ushort() - - #################/ - # hhea - Horizontal header table - #################/ - self.seek_table("hhea") - self.skip(32) - metricDataFormat = self.read_ushort() - orignHmetrics = numberOfHMetrics = self.read_ushort() - - #################/ - # maxp - Maximum profile table - #################/ - self.seek_table("maxp") - self.skip(4) - numGlyphs = self.read_ushort() - - #################/ - # cmap - Character to glyph index mapping table - #################/ - cmap_offset = self.seek_table("cmap") - self.skip(2) - cmapTableCount = self.read_ushort() - unicode_cmap_offset = 0 - for i in range(cmapTableCount): - platformID = self.read_ushort() - encodingID = self.read_ushort() - offset = self.read_ulong() - save_pos = self._pos - if ((platformID == 3 and encodingID == 1) or platformID == 0): # Microsoft, Unicode - format = self.get_ushort(cmap_offset + offset) - if (format == 4): - unicode_cmap_offset = cmap_offset + offset - break - - self.seek(save_pos ) - - if (not unicode_cmap_offset): - die('Font (' + self.filename + ') does not have cmap for Unicode (platform 3, encoding 1, format 4, or platform 0, any encoding, format 4)') - - glyphToChar = {} - charToGlyph = {} - self.getCMAP4(unicode_cmap_offset, glyphToChar, charToGlyph ) - - self.charToGlyph = charToGlyph - - #################/ - # hmtx - Horizontal metrics table - #################/ - scale = 1 # not used - self.getHMTX(numberOfHMetrics, numGlyphs, glyphToChar, scale) - - #################/ - # loca - Index to location - #################/ - self.getLOCA(indexToLocFormat, numGlyphs) - - subsetglyphs = [(0, 0)] # special "sorted dict"! - subsetCharToGlyph = {} - for code in subset: - if (code in self.charToGlyph): - if (self.charToGlyph[code], code) not in subsetglyphs: - subsetglyphs.append((self.charToGlyph[code], code)) # Old Glyph ID => Unicode - subsetCharToGlyph[code] = self.charToGlyph[code] # Unicode to old GlyphID - self.maxUni = max(self.maxUni, code) - (start,dummy) = self.get_table_pos('glyf') - - subsetglyphs.sort() - glyphSet = {} - n = 0 - fsLastCharIndex = 0 # maximum Unicode index (character code) in this font, according to the cmap subtable for platform ID 3 and platform- specific encoding ID 0 or 1. - for originalGlyphIdx, uni in subsetglyphs: - fsLastCharIndex = max(fsLastCharIndex , uni) - glyphSet[originalGlyphIdx] = n # old glyphID to new glyphID - n += 1 - - codeToGlyph = {} - for uni, originalGlyphIdx in sorted(subsetCharToGlyph.items()): - codeToGlyph[uni] = glyphSet[originalGlyphIdx] - - self.codeToGlyph = codeToGlyph - - for originalGlyphIdx, uni in subsetglyphs: - nonlocals = {'start': start, 'glyphSet': glyphSet, - 'subsetglyphs': subsetglyphs} - self.getGlyphs(originalGlyphIdx, nonlocals) - - numGlyphs = numberOfHMetrics = len(subsetglyphs) - - #tables copied from the original - tags = ['name'] - for tag in tags: - self.add(tag, self.get_table(tag)) - tags = ['cvt ', 'fpgm', 'prep', 'gasp'] - for tag in tags: - if (tag in self.tables): - self.add(tag, self.get_table(tag)) - - # post - PostScript - opost = self.get_table('post') - post = "\x00\x03\x00\x00" + substr(opost,4,12) + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - self.add('post', post) - - # Sort CID2GID map into segments of contiguous codes - if 0 in codeToGlyph: - del codeToGlyph[0] - #unset(codeToGlyph[65535]) - rangeid = 0 - range_ = {} - prevcid = -2 - prevglidx = -1 - # for each character - for cid, glidx in sorted(codeToGlyph.items()): - if (cid == (prevcid + 1) and glidx == (prevglidx + 1)): - range_[rangeid].append(glidx) - else: - # new range - rangeid = cid - range_[rangeid] = [] - range_[rangeid].append(glidx) - prevcid = cid - prevglidx = glidx - - # cmap - Character to glyph mapping - Format 4 (MS / ) - segCount = len(range_) + 1 # + 1 Last segment has missing character 0xFFFF - searchRange = 1 - entrySelector = 0 - while (searchRange * 2 <= segCount ): - searchRange = searchRange * 2 - entrySelector = entrySelector + 1 - - searchRange = searchRange * 2 - rangeShift = segCount * 2 - searchRange - length = 16 + (8*segCount ) + (numGlyphs+1) - cmap = [0, 1, # Index : version, number of encoding subtables - 3, 1, # Encoding Subtable : platform (MS=3), encoding (Unicode) - 0, 12, # Encoding Subtable : offset (hi,lo) - 4, length, 0, # Format 4 Mapping subtable: format, length, language - segCount*2, - searchRange, - entrySelector, - rangeShift] - - range_ = sorted(range_.items()) - - # endCode(s) - for start, subrange in range_: - endCode = start + (len(subrange)-1) - cmap.append(endCode) # endCode(s) - - cmap.append(0xFFFF) # endCode of last Segment - cmap.append(0) # reservedPad - - # startCode(s) - for start, subrange in range_: - cmap.append(start) # startCode(s) - - cmap.append(0xFFFF) # startCode of last Segment - # idDelta(s) - for start, subrange in range_: - idDelta = -(start-subrange[0]) - n += count(subrange) - cmap.append(idDelta) # idDelta(s) - - cmap.append(1) # idDelta of last Segment - # idRangeOffset(s) - for subrange in range_: - cmap.append(0) # idRangeOffset[segCount] Offset in bytes to glyph indexArray, or 0 - - cmap.append(0) # idRangeOffset of last Segment - for subrange, glidx in range_: - cmap.extend(glidx) - - cmap.append(0) # Mapping for last character - cmapstr = '' - for cm in cmap: - if cm >= 0: - cmapstr += pack(">H", cm) - else: - try: - cmapstr += pack(">h", cm) - except: - warnings.warn("cmap value too big/small: %s" % cm) - cmapstr += pack(">H", -cm) - self.add('cmap', cmapstr) - - # glyf - Glyph data - (glyfOffset,glyfLength) = self.get_table_pos('glyf') - if (glyfLength < self.maxStrLenRead): - glyphData = self.get_table('glyf') - - offsets = [] - glyf = '' - pos = 0 - - hmtxstr = '' - xMinT = 0 - yMinT = 0 - xMaxT = 0 - yMaxT = 0 - advanceWidthMax = 0 - minLeftSideBearing = 0 - minRightSideBearing = 0 - xMaxExtent = 0 - maxPoints = 0 # points in non-compound glyph - maxContours = 0 # contours in non-compound glyph - maxComponentPoints = 0 # points in compound glyph - maxComponentContours = 0 # contours in compound glyph - maxComponentElements = 0 # number of glyphs referenced at top level - maxComponentDepth = 0 # levels of recursion, set to 0 if font has only simple glyphs - self.glyphdata = {} - - for originalGlyphIdx, uni in subsetglyphs: - # hmtx - Horizontal Metrics - hm = self.getHMetric(orignHmetrics, originalGlyphIdx) - hmtxstr += hm - - offsets.append(pos) - try: - glyphPos = self.glyphPos[originalGlyphIdx] - glyphLen = self.glyphPos[originalGlyphIdx + 1] - glyphPos - except IndexError: - warnings.warn("missing glyph %s" % (originalGlyphIdx)) - glyphLen = 0 - - if (glyfLength < self.maxStrLenRead): - data = substr(glyphData,glyphPos,glyphLen) - else: - if (glyphLen > 0): - data = self.get_chunk(glyfOffset+glyphPos,glyphLen) - else: - data = '' - - if (glyphLen > 0): - up = unpack(">H", substr(data,0,2))[0] - if (glyphLen > 2 and (up & (1 << 15)) ): # If number of contours <= -1 i.e. composiste glyph - pos_in_glyph = 10 - flags = GF_MORE - nComponentElements = 0 - while (flags & GF_MORE): - nComponentElements += 1 # number of glyphs referenced at top level - up = unpack(">H", substr(data,pos_in_glyph,2)) - flags = up[0] - up = unpack(">H", substr(data,pos_in_glyph+2,2)) - glyphIdx = up[0] - self.glyphdata.setdefault(originalGlyphIdx, {}).setdefault('compGlyphs', []).append(glyphIdx) - try: - data = self._set_ushort(data, pos_in_glyph + 2, glyphSet[glyphIdx]) - except KeyError: - data = 0 - warnings.warn("missing glyph data %s" % glyphIdx) - pos_in_glyph += 4 - if (flags & GF_WORDS): - pos_in_glyph += 4 - else: - pos_in_glyph += 2 - if (flags & GF_SCALE): - pos_in_glyph += 2 - elif (flags & GF_XYSCALE): - pos_in_glyph += 4 - elif (flags & GF_TWOBYTWO): - pos_in_glyph += 8 - - maxComponentElements = max(maxComponentElements, nComponentElements) - - glyf += data - pos += glyphLen - if (pos % 4 != 0): - padding = 4 - (pos % 4) - glyf += str_repeat("\0",padding) - pos += padding - - offsets.append(pos) - self.add('glyf', glyf) - - # hmtx - Horizontal Metrics - self.add('hmtx', hmtxstr) - - # loca - Index to location - locastr = '' - if (((pos + 1) >> 1) > 0xFFFF): - indexToLocFormat = 1 # long format - for offset in offsets: - locastr += pack(">L",offset) - else: - indexToLocFormat = 0 # short format - for offset in offsets: - locastr += pack(">H",(offset/2)) - - self.add('loca', locastr) - - # head - Font header - head = self.get_table('head') - head = self._set_ushort(head, 50, indexToLocFormat) - self.add('head', head) - - # hhea - Horizontal Header - hhea = self.get_table('hhea') - hhea = self._set_ushort(hhea, 34, numberOfHMetrics) - self.add('hhea', hhea) - - # maxp - Maximum Profile - maxp = self.get_table('maxp') - maxp = self._set_ushort(maxp, 4, numGlyphs) - self.add('maxp', maxp) - - # OS/2 - OS/2 - os2 = self.get_table('OS/2') - self.add('OS/2', os2 ) - - self.fh.close() - - # Put the TTF file together - stm = self.endTTFile('') - return stm - - - ######################################### - # Recursively get composite glyph data - def getGlyphData(self, originalGlyphIdx, nonlocals): - # &maxdepth, &depth, &points, &contours - nonlocals['depth'] += 1 - nonlocals['maxdepth'] = max(nonlocals['maxdepth'], nonlocals['depth']) - if (len(self.glyphdata[originalGlyphIdx]['compGlyphs'])): - for glyphIdx in self.glyphdata[originalGlyphIdx]['compGlyphs']: - self.getGlyphData(glyphIdx, nonlocals) - - elif ((self.glyphdata[originalGlyphIdx]['nContours'] > 0) and nonlocals['depth'] > 0): # simple - contours += self.glyphdata[originalGlyphIdx]['nContours'] - points += self.glyphdata[originalGlyphIdx]['nPoints'] - - nonlocals['depth'] -= 1 - - - ######################################### - # Recursively get composite glyphs - def getGlyphs(self, originalGlyphIdx, nonlocals): - # &start, &glyphSet, &subsetglyphs) - - try: - glyphPos = self.glyphPos[originalGlyphIdx] - glyphLen = self.glyphPos[originalGlyphIdx + 1] - glyphPos - except IndexError: - warnings.warn("missing glyph %s" % (originalGlyphIdx)) - return - - if (not glyphLen): - return - - self.seek(nonlocals['start'] + glyphPos) - numberOfContours = self.read_short() - if (numberOfContours < 0): - self.skip(8) - flags = GF_MORE - while (flags & GF_MORE): - flags = self.read_ushort() - glyphIdx = self.read_ushort() - if (glyphIdx not in nonlocals['glyphSet']): - nonlocals['glyphSet'][glyphIdx] = len(nonlocals['subsetglyphs']) # old glyphID to new glyphID - nonlocals['subsetglyphs'].append((glyphIdx, 1)) - - savepos = self.fh.tell() - self.getGlyphs(glyphIdx, nonlocals) - self.seek(savepos) - if (flags & GF_WORDS): - self.skip(4) - else: - self.skip(2) - if (flags & GF_SCALE): - self.skip(2) - elif (flags & GF_XYSCALE): - self.skip(4) - elif (flags & GF_TWOBYTWO): - self.skip(8) - - ######################################### - - def getHMTX(self, numberOfHMetrics, numGlyphs, glyphToChar, scale): - start = self.seek_table("hmtx") - aw = 0 - self.charWidths = [0] * 256*256*2 - nCharWidths = 0 - if ((numberOfHMetrics*4) < self.maxStrLenRead): - data = self.get_chunk(start,(numberOfHMetrics*4)) - arr = unpack(">" + "H" * (len(data)/2), data) - else: - self.seek(start) - for glyph in range(numberOfHMetrics): - if ((numberOfHMetrics*4) < self.maxStrLenRead): - aw = arr[(glyph*2)] # PHP starts arrays from index 0!? +1 - else: - aw = self.read_ushort() - lsb = self.read_ushort() - - if (glyph in glyphToChar or glyph == 0): - if (aw >= (1 << 15) ): - aw = 0 # 1.03 Some (arabic) fonts have -ve values for width - # although should be unsigned value - comes out as e.g. 65108 (intended -50) - if (glyph == 0): - self.defaultWidth = scale*aw - continue - - for char in glyphToChar[glyph]: - if (char != 0 and char != 65535): - w = int(round(scale*aw)) - if (w == 0): w = 65535 - if (char < 196608): - self.charWidths[char] = w - nCharWidths += 1 - - - data = self.get_chunk((start+numberOfHMetrics*4),(numGlyphs*2)) - arr = unpack(">" + "H" * (len(data)/2), data) - diff = numGlyphs-numberOfHMetrics - for pos in range(diff): - glyph = pos + numberOfHMetrics - if (glyph in glyphToChar): - for char in glyphToChar[glyph]: - if (char != 0 and char != 65535): - w = int(round(scale*aw)) - if (w == 0): w = 65535 - if (char < 196608): - self.charWidths[char] = w - nCharWidths += 1 - - - # NB 65535 is a set width of 0 - # First bytes define number of chars in font - self.charWidths[0] = nCharWidths - - - def getHMetric(self, numberOfHMetrics, gid): - start = self.seek_table("hmtx") - if (gid < numberOfHMetrics): - self.seek(start+(gid*4)) - hm = self.fh.read(4) - else: - self.seek(start+((numberOfHMetrics-1)*4)) - hm = self.fh.read(2) - self.seek(start+(numberOfHMetrics*2)+(gid*2)) - hm += self.fh.read(2) - return hm - - - def getLOCA(self, indexToLocFormat, numGlyphs): - start = self.seek_table('loca') - self.glyphPos = [] - if (indexToLocFormat == 0): - data = self.get_chunk(start,(numGlyphs*2)+2) - arr = unpack(">" + "H" * (len(data)/2), data) - for n in range(numGlyphs): - self.glyphPos.append((arr[n] * 2)) # n+1 !? - elif (indexToLocFormat == 1): - data = self.get_chunk(start,(numGlyphs*4)+4) - arr = unpack(">" + "L" * (len(data)/4), data) - for n in range(numGlyphs): - self.glyphPos.append((arr[n])) # n+1 !? - else: - die('Unknown location table format ' + indexToLocFormat) - - # CMAP Format 4 - def getCMAP4(self, unicode_cmap_offset, glyphToChar, charToGlyph ): - self.maxUniChar = 0 - self.seek(unicode_cmap_offset + 2) - length = self.read_ushort() - limit = unicode_cmap_offset + length - self.skip(2) - - segCount = self.read_ushort() / 2 - self.skip(6) - endCount = [] - for i in range(segCount): - endCount.append(self.read_ushort()) - self.skip(2) - startCount = [] - for i in range(segCount): - startCount.append(self.read_ushort()) - idDelta = [] - for i in range(segCount): - idDelta.append(self.read_short()) # ???? was unsigned short - idRangeOffset_start = self._pos - idRangeOffset = [] - for i in range(segCount): - idRangeOffset.append(self.read_ushort()) - - for n in range(segCount): - endpoint = (endCount[n] + 1) - for unichar in range(startCount[n], endpoint, 1): - if (idRangeOffset[n] == 0): - glyph = (unichar + idDelta[n]) & 0xFFFF - else: - offset = (unichar - startCount[n]) * 2 + idRangeOffset[n] - offset = idRangeOffset_start + 2 * n + offset - if (offset >= limit): - glyph = 0 - else: - glyph = self.get_ushort(offset) - if (glyph != 0): - glyph = (glyph + idDelta[n]) & 0xFFFF - - charToGlyph[unichar] = glyph - if (unichar < 196608): - self.maxUniChar = max(unichar,self.maxUniChar) - glyphToChar.setdefault(glyph, []).append(unichar) - - - # Put the TTF file together - def endTTFile(self, stm): - stm = '' - numTables = count(self.otables) - searchRange = 1 - entrySelector = 0 - while (searchRange * 2 <= numTables): - searchRange = searchRange * 2 - entrySelector = entrySelector + 1 - - searchRange = searchRange * 16 - rangeShift = numTables * 16 - searchRange - - # Header - if (_TTF_MAC_HEADER): - stm += (pack(">LHHHH", 0x74727565, numTables, searchRange, entrySelector, rangeShift)) # Mac - else: - stm += (pack(">LHHHH", 0x00010000 , numTables, searchRange, entrySelector, rangeShift)) # Windows - - - # Table directory - tables = self.otables - - offset = 12 + numTables * 16 - sorted_tables = sorted(tables.items()) - for tag, data in sorted_tables: - if (tag == 'head'): - head_start = offset - stm += tag - checksum = calcChecksum(data) - stm += pack(">HH", checksum[0],checksum[1]) - stm += pack(">LL", offset, strlen(data)) - paddedLength = (strlen(data)+3)&~3 - offset = offset + paddedLength - - # Table data - for tag, data in sorted_tables: - data += "\0\0\0" - stm += substr(data,0,(strlen(data)&~3)) - - checksum = calcChecksum(stm) - checksum = sub32((0xB1B0,0xAFBA), checksum) - chk = pack(">HH", checksum[0],checksum[1]) - stm = self.splice(stm,(head_start + 8),chk) - return stm - -if __name__ == '__main__': - ttf = TTFontFile() - ttffile = 'DejaVuSansCondensed.ttf'; - ttf.getMetrics(ttffile) - # test basic metrics: - assert round(ttf.descent, 0) == -236 - assert round(ttf.capHeight, 0) == 928 - assert ttf.flags == 4 - assert [round(i, 0) for i in ttf.bbox] == [-918, -415, 1513, 1167] - assert ttf.italicAngle == 0 - assert ttf.stemV == 87 - assert round(ttf.defaultWidth, 0) == 540 - assert round(ttf.underlinePosition, 0) == -63 - assert round(ttf.underlineThickness, 0) == 44 - # test char widths 8(against binary file generated by tfpdf.php): - assert ''.join(ttf.charWidths) == open("dejavusanscondensed.cw.dat").read() - +#****************************************************************************** +# TTFontFile class +# +# This class is based on The ReportLab Open Source PDF library +# written in Python - http://www.reportlab.com/software/opensource/ +# together with ideas from the OpenOffice source code and others. +# +# Version: 1.04 +# Date: 2011-09-18 +# Author: Ian Back +# License: LGPL +# Copyright (c) Ian Back, 2010 +# Ported to Python 2.7 by Mariano Reingart (reingart@gmail.com) on 2012 +# This header must be retained in any redistribution or +# modification of the file. +# +#****************************************************************************** + +from struct import pack, unpack, unpack_from +import re +import warnings +from php import die, substr, str_repeat, str_pad, strlen, count + + +# Define the value used in the "head" table of a created TTF file +# 0x74727565 "true" for Mac +# 0x00010000 for Windows +# Either seems to work for a font embedded in a PDF file +# when read by Adobe Reader on a Windows PC(!) +_TTF_MAC_HEADER = False + + +# TrueType Font Glyph operators +GF_WORDS = (1 << 0) +GF_SCALE = (1 << 3) +GF_MORE = (1 << 5) +GF_XYSCALE = (1 << 6) +GF_TWOBYTWO = (1 << 7) + + +def sub32(x, y): + xlo = x[1] + xhi = x[0] + ylo = y[1] + yhi = y[0] + if (ylo > xlo): + xlo += 1 << 16 + yhi += 1 + reslo = xlo-ylo + if (yhi > xhi): + xhi += 1 << 16 + reshi = xhi-yhi + reshi = reshi & 0xFFFF + return (reshi, reslo) + +def calcChecksum(data): + if (strlen(data) % 4): + data += str_repeat("\0", (4-(len(data) % 4))) + hi=0x0000 + lo=0x0000 + for i in range(0, len(data), 4): + hi += (ord(data[i])<<8) + ord(data[i+1]) + lo += (ord(data[i+2])<<8) + ord(data[i+3]) + hi += lo >> 16 + lo = lo & 0xFFFF + hi = hi & 0xFFFF + return (hi, lo) + + +class TTFontFile: + + def __init__(self): + self.maxStrLenRead = 200000 # Maximum size of glyf table to read in as string (otherwise reads each glyph from file) + + def getMetrics(self, file): + self.filename = file + self.fh = open(file,'rb') + self._pos = 0 + self.charWidths = [] + self.glyphPos = {} + self.charToGlyph = {} + self.tables = {} + self.otables = {} + self.ascent = 0 + self.descent = 0 + self.TTCFonts = {} + self.version = version = self.read_ulong() + if (version==0x4F54544F): + die("Postscript outlines are not supported") + if (version==0x74746366): + die("ERROR - TrueType Fonts Collections not supported") + if (version not in (0x00010000,0x74727565)): + die("Not a TrueType font: version=" + version) + self.readTableDirectory() + self.extractInfo() + self.fh.close() + + def readTableDirectory(self, ): + self.numTables = self.read_ushort() + self.searchRange = self.read_ushort() + self.entrySelector = self.read_ushort() + self.rangeShift = self.read_ushort() + self.tables = {} + for i in range(self.numTables): + record = {} + record['tag'] = self.read_tag() + record['checksum'] = (self.read_ushort(),self.read_ushort()) + record['offset'] = self.read_ulong() + record['length'] = self.read_ulong() + self.tables[record['tag']] = record + + def get_table_pos(self, tag): + offset = self.tables[tag]['offset'] + length = self.tables[tag]['length'] + return (offset, length) + + def seek(self, pos): + self._pos = pos + self.fh.seek(self._pos) + + def skip(self, delta): + self._pos = self._pos + delta + self.fh.seek(self._pos) + + def seek_table(self, tag, offset_in_table = 0): + tpos = self.get_table_pos(tag) + self._pos = tpos[0] + offset_in_table + self.fh.seek(self._pos) + return self._pos + + def read_tag(self): + self._pos += 4 + return self.fh.read(4) + + def read_short(self): + self._pos += 2 + s = self.fh.read(2) + a = (ord(s[0])<<8) + ord(s[1]) + if (a & (1 << 15) ): + a = (a - (1 << 16)) + return a + + def unpack_short(self, s): + a = (ord(s[0])<<8) + ord(s[1]) + if (a & (1 << 15) ): + a = (a - (1 << 16)) + return a + + def read_ushort(self): + self._pos += 2 + s = self.fh.read(2) + return (ord(s[0])<<8) + ord(s[1]) + + def read_ulong(self): + self._pos += 4 + s = self.fh.read(4) + # if large uInt32 as an integer, PHP converts it to -ve + return (ord(s[0])*16777216) + (ord(s[1])<<16) + (ord(s[2])<<8) + ord(s[3]) # 16777216 = 1<<24 + + def get_ushort(self, pos): + self.fh.seek(pos) + s = self.fh.read(2) + return (ord(s[0])<<8) + ord(s[1]) + + def get_ulong(self, pos): + self.fh.seek(pos) + s = self.fh.read(4) + # iF large uInt32 as an integer, PHP converts it to -ve + return (ord(s[0])*16777216) + (ord(s[1])<<16) + (ord(s[2])<<8) + ord(s[3]) # 16777216 = 1<<24 + + def pack_short(self, val): + if (val<0): + val = abs(val) + val = ~val + val += 1 + return pack(">H",val) + + def splice(self, stream, offset, value): + return substr(stream,0,offset) + value + substr(stream,offset+strlen(value)) + + def _set_ushort(self, stream, offset, value): + up = pack(">H", value) + return self.splice(stream, offset, up) + + def _set_short(self, stream, offset, val): + if (val<0): + val = abs(val) + val = ~val + val += 1 + up = pack(">H",val) + return self.splice(stream, offset, up) + + def get_chunk(self, pos, length): + self.fh.seek(pos) + if (length <1): return '' + return (self.fh.read(length)) + + def get_table(self, tag): + (pos, length) = self.get_table_pos(tag) + if (length == 0): + die('Truetype font (' + self.filename + '): error reading table: ' + tag) + self.fh.seek(pos) + return (self.fh.read(length)) + + def add(self, tag, data): + if (tag == 'head') : + data = self.splice(data, 8, "\0\0\0\0") + self.otables[tag] = data + +############################################/ +############################################/ + +############################################/ + + def extractInfo(self): + #################/ + # name - Naming table + #################/ + self.sFamilyClass = 0 + self.sFamilySubClass = 0 + + name_offset = self.seek_table("name") + format = self.read_ushort() + if (format != 0): + die("Unknown name table format " + format) + numRecords = self.read_ushort() + string_data_offset = name_offset + self.read_ushort() + names = {1:'',2:'',3:'',4:'',6:''} + K = names.keys() + nameCount = len(names) + for i in range(numRecords): + platformId = self.read_ushort() + encodingId = self.read_ushort() + languageId = self.read_ushort() + nameId = self.read_ushort() + length = self.read_ushort() + offset = self.read_ushort() + if (nameId not in K): continue + N = '' + if (platformId == 3 and encodingId == 1 and languageId == 0x409): # Microsoft, Unicode, US English, PS Name + opos = self._pos + self.seek(string_data_offset + offset) + if (length % 2 != 0): + die("PostScript name is UTF-16BE string of odd length") + length /= 2 + N = '' + while (length > 0): + char = self.read_ushort() + N += (chr(char)) + length -= 1 + self._pos = opos + self.seek(opos) + + elif (platformId == 1 and encodingId == 0 and languageId == 0): # Macintosh, Roman, English, PS Name + opos = self._pos + N = self.get_chunk(string_data_offset + offset, length) + self._pos = opos + self.seek(opos) + + if (N and names[nameId]==''): + names[nameId] = N + nameCount -= 1 + if (nameCount==0): break + + + if (names[6]): + psName = names[6] + elif (names[4]): + psName = re.sub(' ','-',names[4]) + elif (names[1]): + psName = re.sub(' ','-',names[1]) + else: + psName = '' + if (not psName): + die("Could not find PostScript font name") + self.name = psName + if (names[1]): + self.familyName = names[1] + else: + self.familyName = psName + if (names[2]): + self.styleName = names[2] + else: + self.styleName = 'Regular' + if (names[4]): + self.fullName = names[4] + else: + self.fullName = psName + if (names[3]): + self.uniqueFontID = names[3] + else: + self.uniqueFontID = psName + if (names[6]): + self.fullName = names[6] + + #################/ + # head - Font header table + #################/ + self.seek_table("head") + self.skip(18) + self.unitsPerEm = unitsPerEm = self.read_ushort() + scale = 1000 / float(unitsPerEm) + self.skip(16) + xMin = self.read_short() + yMin = self.read_short() + xMax = self.read_short() + yMax = self.read_short() + self.bbox = [(xMin*scale), (yMin*scale), (xMax*scale), (yMax*scale)] + self.skip(3*2) + indexToLocFormat = self.read_ushort() + glyphDataFormat = self.read_ushort() + if (glyphDataFormat != 0): + die('Unknown glyph data format ' + glyphDataFormat) + + #################/ + # hhea metrics table + #################/ + # ttf2t1 seems to use this value rather than the one in OS/2 - so put in for compatibility + if ("hhea" in self.tables): + self.seek_table("hhea") + self.skip(4) + hheaAscender = self.read_short() + hheaDescender = self.read_short() + self.ascent = (hheaAscender *scale) + self.descent = (hheaDescender *scale) + + + #################/ + # OS/2 - OS/2 and Windows metrics table + #################/ + if ("OS/2" in self.tables): + self.seek_table("OS/2") + version = self.read_ushort() + self.skip(2) + usWeightClass = self.read_ushort() + self.skip(2) + fsType = self.read_ushort() + if (fsType == 0x0002 or (fsType & 0x0300) != 0): + die('ERROR - Font file ' + self.filename + ' cannot be embedded due to copyright restrictions.') + self.restrictedUse = True + + self.skip(20) + sF = self.read_short() + self.sFamilyClass = (sF >> 8) + self.sFamilySubClass = (sF & 0xFF) + self._pos += 10 #PANOSE = 10 byte length + panose = self.fh.read(10) + self.skip(26) + sTypoAscender = self.read_short() + sTypoDescender = self.read_short() + if (not self.ascent): + self.ascent = (sTypoAscender*scale) + if (not self.descent): + self.descent = (sTypoDescender*scale) + if (version > 1): + self.skip(16) + sCapHeight = self.read_short() + self.capHeight = (sCapHeight*scale) + else: + self.capHeight = self.ascent + + else: + usWeightClass = 500 + if (not self.ascent): self.ascent = (yMax*scale) + if (not self.descent): self.descent = (yMin*scale) + self.capHeight = self.ascent + + self.stemV = 50 + int(pow((usWeightClass / 65.0),2)) + + #################/ + # post - PostScript table + #################/ + self.seek_table("post") + self.skip(4) + self.italicAngle = self.read_short() + self.read_ushort() / 65536.0 + self.underlinePosition = self.read_short() * scale + self.underlineThickness = self.read_short() * scale + isFixedPitch = self.read_ulong() + + self.flags = 4 + + if (self.italicAngle!= 0): + self.flags = self.flags | 64 + if (usWeightClass >= 600): + self.flags = self.flags | 262144 + if (isFixedPitch): + self.flags = self.flags | 1 + + #################/ + # hhea - Horizontal header table + #################/ + self.seek_table("hhea") + self.skip(32) + metricDataFormat = self.read_ushort() + if (metricDataFormat != 0): + die('Unknown horizontal metric data format '.metricDataFormat) + numberOfHMetrics = self.read_ushort() + if (numberOfHMetrics == 0): + die('Number of horizontal metrics is 0') + + #################/ + # maxp - Maximum profile table + #################/ + self.seek_table("maxp") + self.skip(4) + numGlyphs = self.read_ushort() + + #################/ + # cmap - Character to glyph index mapping table + #################/ + cmap_offset = self.seek_table("cmap") + self.skip(2) + cmapTableCount = self.read_ushort() + unicode_cmap_offset = 0 + unicode_cmap_offset12 = 0 + + for i in range(cmapTableCount): + platformID = self.read_ushort() + encodingID = self.read_ushort() + offset = self.read_ulong() + save_pos = self._pos + if platformID == 3 and encodingID == 10: # Microsoft, UCS-4 + format = self.get_ushort(cmap_offset + offset) + if (format == 12): + if not unicode_cmap_offset12: + unicode_cmap_offset12 = cmap_offset + offset + break + if ((platformID == 3 and encodingID == 1) or platformID == 0): # Microsoft, Unicode + format = self.get_ushort(cmap_offset + offset) + if (format == 4): + if (not unicode_cmap_offset): + unicode_cmap_offset = cmap_offset + offset + break + + self.seek(save_pos) + + if not unicode_cmap_offset and not unicode_cmap_offset12: + die('Font (' + self.filename + ') does not have cmap for Unicode (platform 3, encoding 1, format 4, or platform 3, encoding 10, format 12, or platform 0, any encoding, format 4)') + + glyphToChar = {} + charToGlyph = {} + if unicode_cmap_offset12: + self.getCMAP12(unicode_cmap_offset12, glyphToChar, charToGlyph) + else: + self.getCMAP4(unicode_cmap_offset, glyphToChar, charToGlyph) + + #################/ + # hmtx - Horizontal metrics table + #################/ + self.getHMTX(numberOfHMetrics, numGlyphs, glyphToChar, scale) + + +############################################/ +############################################/ + + def makeSubset(self, file, subset): + self.filename = file + self.fh = open(file ,'rb') + self._pos = 0 + self.charWidths = [] + self.glyphPos = {} + self.charToGlyph = {} + self.tables = {} + self.otables = {} + self.ascent = 0 + self.descent = 0 + self.skip(4) + self.maxUni = 0 + self.readTableDirectory() + + #################/ + # head - Font header table + #################/ + self.seek_table("head") + self.skip(50) + indexToLocFormat = self.read_ushort() + glyphDataFormat = self.read_ushort() + + #################/ + # hhea - Horizontal header table + #################/ + self.seek_table("hhea") + self.skip(32) + metricDataFormat = self.read_ushort() + orignHmetrics = numberOfHMetrics = self.read_ushort() + + #################/ + # maxp - Maximum profile table + #################/ + self.seek_table("maxp") + self.skip(4) + numGlyphs = self.read_ushort() + + #################/ + # cmap - Character to glyph index mapping table + #################/ + cmap_offset = self.seek_table("cmap") + self.skip(2) + cmapTableCount = self.read_ushort() + unicode_cmap_offset = 0 + unicode_cmap_offset12 = 0 + for i in range(cmapTableCount): + platformID = self.read_ushort() + encodingID = self.read_ushort() + offset = self.read_ulong() + save_pos = self._pos + if platformID == 3 and encodingID == 10: # Microsoft, UCS-4 + format = self.get_ushort(cmap_offset + offset) + if (format == 12): + if not unicode_cmap_offset12: + unicode_cmap_offset12 = cmap_offset + offset + break + if ((platformID == 3 and encodingID == 1) or platformID == 0): # Microsoft, Unicode + format = self.get_ushort(cmap_offset + offset) + if (format == 4): + unicode_cmap_offset = cmap_offset + offset + break + + self.seek(save_pos ) + + if not unicode_cmap_offset and not unicode_cmap_offset12: + die('Font (' + self.filename + ') does not have cmap for Unicode (platform 3, encoding 1, format 4, or platform 3, encoding 10, format 12, or platform 0, any encoding, format 4)') + + glyphToChar = {} + charToGlyph = {} + if unicode_cmap_offset12: + self.getCMAP12(unicode_cmap_offset12, glyphToChar, charToGlyph) + else: + self.getCMAP4(unicode_cmap_offset, glyphToChar, charToGlyph) + + self.charToGlyph = charToGlyph + + #################/ + # hmtx - Horizontal metrics table + #################/ + scale = 1 # not used + self.getHMTX(numberOfHMetrics, numGlyphs, glyphToChar, scale) + + #################/ + # loca - Index to location + #################/ + self.getLOCA(indexToLocFormat, numGlyphs) + + subsetglyphs = [(0, 0)] # special "sorted dict"! + subsetCharToGlyph = {} + for code in subset: + if (code in self.charToGlyph): + if (self.charToGlyph[code], code) not in subsetglyphs: + subsetglyphs.append((self.charToGlyph[code], code)) # Old Glyph ID => Unicode + subsetCharToGlyph[code] = self.charToGlyph[code] # Unicode to old GlyphID + self.maxUni = max(self.maxUni, code) + (start,dummy) = self.get_table_pos('glyf') + + subsetglyphs.sort() + glyphSet = {} + n = 0 + fsLastCharIndex = 0 # maximum Unicode index (character code) in this font, according to the cmap subtable for platform ID 3 and platform- specific encoding ID 0 or 1. + for originalGlyphIdx, uni in subsetglyphs: + fsLastCharIndex = max(fsLastCharIndex , uni) + glyphSet[originalGlyphIdx] = n # old glyphID to new glyphID + n += 1 + + codeToGlyph = {} + for uni, originalGlyphIdx in sorted(subsetCharToGlyph.items()): + codeToGlyph[uni] = glyphSet[originalGlyphIdx] + + self.codeToGlyph = codeToGlyph + + for originalGlyphIdx, uni in subsetglyphs: + nonlocals = {'start': start, 'glyphSet': glyphSet, + 'subsetglyphs': subsetglyphs} + self.getGlyphs(originalGlyphIdx, nonlocals) + + numGlyphs = numberOfHMetrics = len(subsetglyphs) + + #tables copied from the original + tags = ['name'] + for tag in tags: + self.add(tag, self.get_table(tag)) + tags = ['cvt ', 'fpgm', 'prep', 'gasp'] + for tag in tags: + if (tag in self.tables): + self.add(tag, self.get_table(tag)) + + # post - PostScript + opost = self.get_table('post') + post = "\x00\x03\x00\x00" + substr(opost,4,12) + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + self.add('post', post) + + # Sort CID2GID map into segments of contiguous codes + if 0 in codeToGlyph: + del codeToGlyph[0] + #unset(codeToGlyph[65535]) + rangeid = 0 + range_ = {} + prevcid = -2 + prevglidx = -1 + # for each character + for cid, glidx in sorted(codeToGlyph.items()): + if (cid == (prevcid + 1) and glidx == (prevglidx + 1)): + range_[rangeid].append(glidx) + else: + # new range + rangeid = cid + range_[rangeid] = [] + range_[rangeid].append(glidx) + prevcid = cid + prevglidx = glidx + + # cmap - Character to glyph mapping - Format 4 (MS / ) + segCount = len(range_) + 1 # + 1 Last segment has missing character 0xFFFF + searchRange = 1 + entrySelector = 0 + while (searchRange * 2 <= segCount ): + searchRange = searchRange * 2 + entrySelector = entrySelector + 1 + + searchRange = searchRange * 2 + rangeShift = segCount * 2 - searchRange + length = 16 + (8*segCount ) + (numGlyphs+1) + cmap = [0, 1, # Index : version, number of encoding subtables + 3, 1, # Encoding Subtable : platform (MS=3), encoding (Unicode) + 0, 12, # Encoding Subtable : offset (hi,lo) + 4, length, 0, # Format 4 Mapping subtable: format, length, language + segCount*2, + searchRange, + entrySelector, + rangeShift] + + range_ = sorted(range_.items()) + + # endCode(s) + for start, subrange in range_: + endCode = start + (len(subrange)-1) + cmap.append(endCode) # endCode(s) + + cmap.append(0xFFFF) # endCode of last Segment + cmap.append(0) # reservedPad + + # startCode(s) + for start, subrange in range_: + cmap.append(start) # startCode(s) + + cmap.append(0xFFFF) # startCode of last Segment + # idDelta(s) + for start, subrange in range_: + idDelta = -(start-subrange[0]) + n += count(subrange) + cmap.append(idDelta) # idDelta(s) + + cmap.append(1) # idDelta of last Segment + # idRangeOffset(s) + for subrange in range_: + cmap.append(0) # idRangeOffset[segCount] Offset in bytes to glyph indexArray, or 0 + + cmap.append(0) # idRangeOffset of last Segment + for subrange, glidx in range_: + cmap.extend(glidx) + + cmap.append(0) # Mapping for last character + cmapstr = '' + for cm in cmap: + if cm >= 0: + cmapstr += pack(">H", cm) + else: + try: + cmapstr += pack(">h", cm) + except: + warnings.warn("cmap value too big/small: %s" % cm) + cmapstr += pack(">H", -cm) + self.add('cmap', cmapstr) + + # glyf - Glyph data + (glyfOffset,glyfLength) = self.get_table_pos('glyf') + if (glyfLength < self.maxStrLenRead): + glyphData = self.get_table('glyf') + + offsets = [] + glyf = '' + pos = 0 + + hmtxstr = '' + xMinT = 0 + yMinT = 0 + xMaxT = 0 + yMaxT = 0 + advanceWidthMax = 0 + minLeftSideBearing = 0 + minRightSideBearing = 0 + xMaxExtent = 0 + maxPoints = 0 # points in non-compound glyph + maxContours = 0 # contours in non-compound glyph + maxComponentPoints = 0 # points in compound glyph + maxComponentContours = 0 # contours in compound glyph + maxComponentElements = 0 # number of glyphs referenced at top level + maxComponentDepth = 0 # levels of recursion, set to 0 if font has only simple glyphs + self.glyphdata = {} + + for originalGlyphIdx, uni in subsetglyphs: + # hmtx - Horizontal Metrics + hm = self.getHMetric(orignHmetrics, originalGlyphIdx) + hmtxstr += hm + + offsets.append(pos) + try: + glyphPos = self.glyphPos[originalGlyphIdx] + glyphLen = self.glyphPos[originalGlyphIdx + 1] - glyphPos + except IndexError: + warnings.warn("missing glyph %s" % (originalGlyphIdx)) + glyphLen = 0 + + if (glyfLength < self.maxStrLenRead): + data = substr(glyphData,glyphPos,glyphLen) + else: + if (glyphLen > 0): + data = self.get_chunk(glyfOffset+glyphPos,glyphLen) + else: + data = '' + + if (glyphLen > 0): + up = unpack(">H", substr(data,0,2))[0] + if (glyphLen > 2 and (up & (1 << 15)) ): # If number of contours <= -1 i.e. composiste glyph + pos_in_glyph = 10 + flags = GF_MORE + nComponentElements = 0 + while (flags & GF_MORE): + nComponentElements += 1 # number of glyphs referenced at top level + up = unpack(">H", substr(data,pos_in_glyph,2)) + flags = up[0] + up = unpack(">H", substr(data,pos_in_glyph+2,2)) + glyphIdx = up[0] + self.glyphdata.setdefault(originalGlyphIdx, {}).setdefault('compGlyphs', []).append(glyphIdx) + try: + data = self._set_ushort(data, pos_in_glyph + 2, glyphSet[glyphIdx]) + except KeyError: + data = 0 + warnings.warn("missing glyph data %s" % glyphIdx) + pos_in_glyph += 4 + if (flags & GF_WORDS): + pos_in_glyph += 4 + else: + pos_in_glyph += 2 + if (flags & GF_SCALE): + pos_in_glyph += 2 + elif (flags & GF_XYSCALE): + pos_in_glyph += 4 + elif (flags & GF_TWOBYTWO): + pos_in_glyph += 8 + + maxComponentElements = max(maxComponentElements, nComponentElements) + + glyf += data + pos += glyphLen + if (pos % 4 != 0): + padding = 4 - (pos % 4) + glyf += str_repeat("\0",padding) + pos += padding + + offsets.append(pos) + self.add('glyf', glyf) + + # hmtx - Horizontal Metrics + self.add('hmtx', hmtxstr) + + # loca - Index to location + locastr = '' + if (((pos + 1) >> 1) > 0xFFFF): + indexToLocFormat = 1 # long format + for offset in offsets: + locastr += pack(">L",offset) + else: + indexToLocFormat = 0 # short format + for offset in offsets: + locastr += pack(">H",(offset/2)) + + self.add('loca', locastr) + + # head - Font header + head = self.get_table('head') + head = self._set_ushort(head, 50, indexToLocFormat) + self.add('head', head) + + # hhea - Horizontal Header + hhea = self.get_table('hhea') + hhea = self._set_ushort(hhea, 34, numberOfHMetrics) + self.add('hhea', hhea) + + # maxp - Maximum Profile + maxp = self.get_table('maxp') + maxp = self._set_ushort(maxp, 4, numGlyphs) + self.add('maxp', maxp) + + # OS/2 - OS/2 + os2 = self.get_table('OS/2') + self.add('OS/2', os2 ) + + self.fh.close() + + # Put the TTF file together + stm = self.endTTFile('') + return stm + + + ######################################### + # Recursively get composite glyph data + def getGlyphData(self, originalGlyphIdx, nonlocals): + # &maxdepth, &depth, &points, &contours + nonlocals['depth'] += 1 + nonlocals['maxdepth'] = max(nonlocals['maxdepth'], nonlocals['depth']) + if (len(self.glyphdata[originalGlyphIdx]['compGlyphs'])): + for glyphIdx in self.glyphdata[originalGlyphIdx]['compGlyphs']: + self.getGlyphData(glyphIdx, nonlocals) + + elif ((self.glyphdata[originalGlyphIdx]['nContours'] > 0) and nonlocals['depth'] > 0): # simple + contours += self.glyphdata[originalGlyphIdx]['nContours'] + points += self.glyphdata[originalGlyphIdx]['nPoints'] + + nonlocals['depth'] -= 1 + + + ######################################### + # Recursively get composite glyphs + def getGlyphs(self, originalGlyphIdx, nonlocals): + # &start, &glyphSet, &subsetglyphs) + + try: + glyphPos = self.glyphPos[originalGlyphIdx] + glyphLen = self.glyphPos[originalGlyphIdx + 1] - glyphPos + except IndexError: + warnings.warn("missing glyph %s" % (originalGlyphIdx)) + return + + if (not glyphLen): + return + + self.seek(nonlocals['start'] + glyphPos) + numberOfContours = self.read_short() + if (numberOfContours < 0): + self.skip(8) + flags = GF_MORE + while (flags & GF_MORE): + flags = self.read_ushort() + glyphIdx = self.read_ushort() + if (glyphIdx not in nonlocals['glyphSet']): + nonlocals['glyphSet'][glyphIdx] = len(nonlocals['subsetglyphs']) # old glyphID to new glyphID + nonlocals['subsetglyphs'].append((glyphIdx, 1)) + + savepos = self.fh.tell() + self.getGlyphs(glyphIdx, nonlocals) + self.seek(savepos) + if (flags & GF_WORDS): + self.skip(4) + else: + self.skip(2) + if (flags & GF_SCALE): + self.skip(2) + elif (flags & GF_XYSCALE): + self.skip(4) + elif (flags & GF_TWOBYTWO): + self.skip(8) + + ######################################### + + def getHMTX(self, numberOfHMetrics, numGlyphs, glyphToChar, scale): + start = self.seek_table("hmtx") + aw = 0 + self.charWidths = [0] * 256*256*2 + nCharWidths = 0 + if ((numberOfHMetrics*4) < self.maxStrLenRead): + data = self.get_chunk(start,(numberOfHMetrics*4)) + arr = unpack(">" + "H" * (len(data)/2), data) + else: + self.seek(start) + for glyph in range(numberOfHMetrics): + if ((numberOfHMetrics*4) < self.maxStrLenRead): + aw = arr[(glyph*2)] # PHP starts arrays from index 0!? +1 + else: + aw = self.read_ushort() + lsb = self.read_ushort() + + if (glyph in glyphToChar or glyph == 0): + if (aw >= (1 << 15) ): + aw = 0 # 1.03 Some (arabic) fonts have -ve values for width + # although should be unsigned value - comes out as e.g. 65108 (intended -50) + if (glyph == 0): + self.defaultWidth = scale*aw + continue + + for char in glyphToChar[glyph]: + if (char != 0 and char != 65535): + w = int(round(scale*aw)) + if (w == 0): w = 65535 + if (char < 196608): + self.charWidths[char] = w + nCharWidths += 1 + + + data = self.get_chunk((start+numberOfHMetrics*4),(numGlyphs*2)) + arr = unpack(">" + "H" * (len(data)/2), data) + diff = numGlyphs-numberOfHMetrics + for pos in range(diff): + glyph = pos + numberOfHMetrics + if (glyph in glyphToChar): + for char in glyphToChar[glyph]: + if (char != 0 and char != 65535): + w = int(round(scale*aw)) + if (w == 0): w = 65535 + if (char < 196608): + self.charWidths[char] = w + nCharWidths += 1 + + + # NB 65535 is a set width of 0 + # First bytes define number of chars in font + self.charWidths[0] = nCharWidths + + + def getHMetric(self, numberOfHMetrics, gid): + start = self.seek_table("hmtx") + if (gid < numberOfHMetrics): + self.seek(start+(gid*4)) + hm = self.fh.read(4) + else: + self.seek(start+((numberOfHMetrics-1)*4)) + hm = self.fh.read(2) + self.seek(start+(numberOfHMetrics*2)+(gid*2)) + hm += self.fh.read(2) + return hm + + + def getLOCA(self, indexToLocFormat, numGlyphs): + start = self.seek_table('loca') + self.glyphPos = [] + if (indexToLocFormat == 0): + data = self.get_chunk(start,(numGlyphs*2)+2) + arr = unpack(">" + "H" * (len(data)/2), data) + for n in range(numGlyphs): + self.glyphPos.append((arr[n] * 2)) # n+1 !? + elif (indexToLocFormat == 1): + data = self.get_chunk(start,(numGlyphs*4)+4) + arr = unpack(">" + "L" * (len(data)/4), data) + for n in range(numGlyphs): + self.glyphPos.append((arr[n])) # n+1 !? + else: + die('Unknown location table format ' + indexToLocFormat) + + # CMAP Format 4 + def getCMAP4(self, unicode_cmap_offset, glyphToChar, charToGlyph): + self.maxUniChar = 0 + self.seek(unicode_cmap_offset + 2) + length = self.read_ushort() + limit = unicode_cmap_offset + length + self.skip(2) + + segCount = self.read_ushort() / 2 + self.skip(6) + endCount = [] + for i in range(segCount): + endCount.append(self.read_ushort()) + self.skip(2) + startCount = [] + for i in range(segCount): + startCount.append(self.read_ushort()) + idDelta = [] + for i in range(segCount): + idDelta.append(self.read_short()) # ???? was unsigned short + idRangeOffset_start = self._pos + idRangeOffset = [] + for i in range(segCount): + idRangeOffset.append(self.read_ushort()) + + for n in range(segCount): + endpoint = (endCount[n] + 1) + for unichar in range(startCount[n], endpoint, 1): + if (idRangeOffset[n] == 0): + glyph = (unichar + idDelta[n]) & 0xFFFF + else: + offset = (unichar - startCount[n]) * 2 + idRangeOffset[n] + offset = idRangeOffset_start + 2 * n + offset + if (offset >= limit): + glyph = 0 + else: + glyph = self.get_ushort(offset) + if (glyph != 0): + glyph = (glyph + idDelta[n]) & 0xFFFF + + charToGlyph[unichar] = glyph + if (unichar < 196608): + self.maxUniChar = max(unichar,self.maxUniChar) + glyphToChar.setdefault(glyph, []).append(unichar) + + # CMAP Format 12 + def getCMAP12(self, unicode_cmap_offset, glyphToChar, charToGlyph): + self.maxUniChar = 0 + # table (skip format version, should be 12) + self.seek(unicode_cmap_offset + 2) + # reserved + self.skip(2) + # table length + length = self.read_ulong() + # language (should be 0) + self.skip(4) + # groups count + grpCount = self.read_ulong() + + if 2 + 2 + 4 + 4 + 4 + grpCount * 3 * 4 > length: + die("TTF format 12 cmap table too small") + for n in range(grpCount): + startCharCode = self.read_ulong() + endCharCode = self.read_ulong() + glyph = self.read_ulong() + for unichar in range(startCharCode, endCharCode + 1): + charToGlyph[unichar] = glyph + if (unichar < 196608): + self.maxUniChar = max(unichar, self.maxUniChar) + glyphToChar.setdefault(glyph, []).append(unichar) + glyph += 1 + + + + # Put the TTF file together + def endTTFile(self, stm): + stm = '' + numTables = count(self.otables) + searchRange = 1 + entrySelector = 0 + while (searchRange * 2 <= numTables): + searchRange = searchRange * 2 + entrySelector = entrySelector + 1 + + searchRange = searchRange * 16 + rangeShift = numTables * 16 - searchRange + + # Header + if (_TTF_MAC_HEADER): + stm += (pack(">LHHHH", 0x74727565, numTables, searchRange, entrySelector, rangeShift)) # Mac + else: + stm += (pack(">LHHHH", 0x00010000 , numTables, searchRange, entrySelector, rangeShift)) # Windows + + + # Table directory + tables = self.otables + + offset = 12 + numTables * 16 + sorted_tables = sorted(tables.items()) + for tag, data in sorted_tables: + if (tag == 'head'): + head_start = offset + stm += tag + checksum = calcChecksum(data) + stm += pack(">HH", checksum[0],checksum[1]) + stm += pack(">LL", offset, strlen(data)) + paddedLength = (strlen(data)+3)&~3 + offset = offset + paddedLength + + # Table data + for tag, data in sorted_tables: + data += "\0\0\0" + stm += substr(data,0,(strlen(data)&~3)) + + checksum = calcChecksum(stm) + checksum = sub32((0xB1B0,0xAFBA), checksum) + chk = pack(">HH", checksum[0],checksum[1]) + stm = self.splice(stm,(head_start + 8),chk) + return stm + +if __name__ == '__main__': + ttf = TTFontFile() + ttffile = 'DejaVuSansCondensed.ttf'; + ttf.getMetrics(ttffile) + # test basic metrics: + assert round(ttf.descent, 0) == -236 + assert round(ttf.capHeight, 0) == 928 + assert ttf.flags == 4 + assert [round(i, 0) for i in ttf.bbox] == [-918, -415, 1513, 1167] + assert ttf.italicAngle == 0 + assert ttf.stemV == 87 + assert round(ttf.defaultWidth, 0) == 540 + assert round(ttf.underlinePosition, 0) == -63 + assert round(ttf.underlineThickness, 0) == 44 + # test char widths 8(against binary file generated by tfpdf.php): + assert ''.join(ttf.charWidths) == open("dejavusanscondensed.cw.dat").read() +