diff --git a/VERSION b/VERSION index 234bace8..03475297 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-08-23 08:35:38) dev +Version 2.00.0 (2012-08-23 08:36:47) dev diff --git a/gluon/contrib/fpdf/__init__.py b/gluon/contrib/fpdf/__init__.py index 3ba17860..e0ffc017 100644 --- a/gluon/contrib/fpdf/__init__.py +++ b/gluon/contrib/fpdf/__init__.py @@ -14,3 +14,5 @@ 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 433fbf61..f584a1e8 100644 --- a/gluon/contrib/fpdf/fonts.py +++ b/gluon/contrib/fpdf/fonts.py @@ -83,7 +83,7 @@ fpdf_charwidths['symbol']={ '\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, @@ -111,7 +111,7 @@ fpdf_charwidths['timesB']={ '\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, @@ -154,3 +154,4 @@ fpdf_charwidths['zapfdingbats']={ '\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 dd8d2a7f..4f9a3eb5 100644 --- a/gluon/contrib/fpdf/fpdf.py +++ b/gluon/contrib/fpdf/fpdf.py @@ -48,7 +48,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 +352,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 +397,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 +455,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 +490,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 +656,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 +680,9 @@ class FPDF(object): for uni in UTF8StringToArray(txt): self.current_font['subset'].append(uni) else: - txt2 = txt.replace('\\','\\\\').replace(')','\\)').replace('(','\\(') + txt2 = txt.replace('\\','\\\\').replace(')','\\)').replace('(','\\(') 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 +760,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 +844,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 +1198,7 @@ class FPDF(object): self._out('<>') @@ -1260,7 +1260,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 +1282,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 +1396,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 +1408,7 @@ class FPDF(object): del info['data'] if 'smask' in info: del info['smask'] - + def _putimage(self, info): if 'data' in info: self._newobj() @@ -1785,20 +1785,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 +1837,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 +1854,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 +1865,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,3 +1879,4 @@ class FPDF(object): x += gap + diff --git a/gluon/contrib/fpdf/html.py b/gluon/contrib/fpdf/html.py index bc1c53b7..95b7e8b3 100644 --- a/gluon/contrib/fpdf/html.py +++ b/gluon/contrib/fpdf/html.py @@ -35,7 +35,7 @@ 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.r = self.g = self.b = 0 self.indent = 0 @@ -54,7 +54,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 @@ -101,13 +101,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, "*" @@ -142,7 +142,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 @@ -156,7 +156,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 @@ -335,7 +335,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 @@ -352,7 +352,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 @@ -374,7 +374,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) @@ -394,3 +394,4 @@ class HTMLMixin(object): h2p = HTML2FPDF(self) h2p.feed(text) + diff --git a/gluon/contrib/fpdf/php.py b/gluon/contrib/fpdf/php.py index 80a4622d..569bf9e0 100644 --- a/gluon/contrib/fpdf/php.py +++ b/gluon/contrib/fpdf/php.py @@ -15,12 +15,12 @@ def print_r(array): 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"; + outstr += "\xFE\xFF"; if not isinstance(instr, unicode): instr = instr.decode('UTF-8') outstr += instr.encode('UTF-16BE') @@ -30,14 +30,14 @@ def UTF8StringToArray(instr): "Converts UTF-8 strings to codepoints array" return [ord(c) for c in instr] -# ttfints php helpers: +# 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) @@ -46,4 +46,4 @@ def str_pad(s, pad_length=0, pad_char= " ", pad_type= +1 ): else: # pad both return s.center(pad_length, pad_char) -strlen = count = lambda s: len(s) \ No newline at end of file +strlen = count = lambda s: len(s) diff --git a/gluon/contrib/fpdf/template.py b/gluon/contrib/fpdf/template.py index 2d38c9f9..651a1b83 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,9 +293,10 @@ 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 cdb280a3..ddab0798 100644 --- a/gluon/contrib/fpdf/ttfonts.py +++ b/gluon/contrib/fpdf/ttfonts.py @@ -1,19 +1,19 @@ #****************************************************************************** -# 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. -# +# 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 @@ -43,22 +43,22 @@ def sub32(x, y): xhi = x[0] ylo = y[1] yhi = y[0] - if (ylo > xlo): - xlo += 1 << 16 - yhi += 1 + if (ylo > xlo): + xlo += 1 << 16 + yhi += 1 reslo = xlo-ylo - if (yhi > xhi): - xhi += 1 << 16 + if (yhi > xhi): + xhi += 1 << 16 reshi = xhi-yhi reshi = reshi & 0xFFFF return (reshi, reslo) -def calcChecksum(data): +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): + 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 @@ -94,34 +94,34 @@ class TTFontFile: 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 = {} + 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 + 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): + + def seek(self, pos): self._pos = pos self.fh.seek(self._pos) - - def skip(self, delta): + + 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 @@ -132,32 +132,32 @@ class TTFontFile: self._pos += 4 return self.fh.read(4) - def read_short(self): + 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)) + 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)) + 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): + 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): + def get_ushort(self, pos): self.fh.seek(pos) s = self.fh.read(2) return (ord(s[0])<<8) + ord(s[1]) @@ -166,45 +166,45 @@ class TTFontFile: 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 + 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) - + 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) + 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) + up = pack(">H",val) return self.splice(stream, offset, up) - def get_chunk(self, pos, length): + def get_chunk(self, pos, length): self.fh.seek(pos) - if (length <1): return '' + 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) + 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") + data = self.splice(data, 8, "\0\0\0\0") self.otables[tag] = data ############################################/ @@ -212,7 +212,7 @@ class TTFontFile: ############################################/ - def extractInfo(self): + def extractInfo(self): #################/ # name - Naming table #################/ @@ -228,7 +228,7 @@ class TTFontFile: names = {1:'',2:'',3:'',4:'',6:''} K = names.keys() nameCount = len(names) - for i in range(numRecords): + for i in range(numRecords): platformId = self.read_ushort() encodingId = self.read_ushort() languageId = self.read_ushort() @@ -250,19 +250,19 @@ class TTFontFile: 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]): @@ -275,29 +275,29 @@ class TTFontFile: die("Could not find PostScript font name") self.name = psName if (names[1]): - self.familyName = names[1] - else: - self.familyName = psName + self.familyName = names[1] + else: + self.familyName = psName if (names[2]): self.styleName = names[2] else: - self.styleName = 'Regular' + self.styleName = 'Regular' if (names[4]): self.fullName = names[4] else: - self.fullName = psName + self.fullName = psName if (names[3]): self.uniqueFontID = names[3] else: - self.uniqueFontID = psName + self.uniqueFontID = psName if (names[6]): - self.fullName = names[6] + self.fullName = names[6] #################/ # head - Font header table #################/ self.seek_table("head") - self.skip(18) + self.skip(18) self.unitsPerEm = unitsPerEm = self.read_ushort() scale = 1000 / float(unitsPerEm) self.skip(16) @@ -323,22 +323,22 @@ class TTFontFile: 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): + 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): + 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) @@ -348,30 +348,30 @@ class TTFontFile: self.skip(26) sTypoAscender = self.read_short() sTypoDescender = self.read_short() - if (not self.ascent): + if (not self.ascent): self.ascent = (sTypoAscender*scale) - if (not self.descent): + 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 - + 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.skip(4) self.italicAngle = self.read_short() + self.read_ushort() / 65536.0 self.underlinePosition = self.read_short() * scale self.underlineThickness = self.read_short() * scale @@ -390,7 +390,7 @@ class TTFontFile: # hhea - Horizontal header table #################/ self.seek_table("hhea") - self.skip(32) + self.skip(32) metricDataFormat = self.read_ushort() if (metricDataFormat != 0): die('Unknown horizontal metric data format '.metricDataFormat) @@ -402,7 +402,7 @@ class TTFontFile: # maxp - Maximum profile table #################/ self.seek_table("maxp") - self.skip(4) + self.skip(4) numGlyphs = self.read_ushort() #################/ @@ -424,7 +424,7 @@ class TTFontFile: 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)') @@ -460,7 +460,7 @@ class TTFontFile: # head - Font header table #################/ self.seek_table("head") - self.skip(50) + self.skip(50) indexToLocFormat = self.read_ushort() glyphDataFormat = self.read_ushort() @@ -468,7 +468,7 @@ class TTFontFile: # hhea - Horizontal header table #################/ self.seek_table("hhea") - self.skip(32) + self.skip(32) metricDataFormat = self.read_ushort() orignHmetrics = numberOfHMetrics = self.read_ushort() @@ -496,9 +496,9 @@ class TTFontFile: 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)') @@ -521,7 +521,7 @@ class TTFontFile: subsetglyphs = [(0, 0)] # special "sorted dict"! subsetCharToGlyph = {} - for code in subset: + 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 @@ -540,12 +540,12 @@ class TTFontFile: codeToGlyph = {} for uni, originalGlyphIdx in sorted(subsetCharToGlyph.items()): - codeToGlyph[uni] = glyphSet[originalGlyphIdx] - + codeToGlyph[uni] = glyphSet[originalGlyphIdx] + self.codeToGlyph = codeToGlyph - - for originalGlyphIdx, uni in subsetglyphs: - nonlocals = {'start': start, 'glyphSet': glyphSet, + + for originalGlyphIdx, uni in subsetglyphs: + nonlocals = {'start': start, 'glyphSet': glyphSet, 'subsetglyphs': subsetglyphs} self.getGlyphs(originalGlyphIdx, nonlocals) @@ -553,12 +553,12 @@ class TTFontFile: #tables copied from the original tags = ['name'] - for tag in tags: - self.add(tag, self.get_table(tag)) + 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)) + if (tag in self.tables): + self.add(tag, self.get_table(tag)) # post - PostScript opost = self.get_table('post') @@ -592,7 +592,7 @@ class TTFontFile: while (searchRange * 2 <= segCount ): searchRange = searchRange * 2 entrySelector = entrySelector + 1 - + searchRange = searchRange * 2 rangeShift = segCount * 2 - searchRange length = 16 + (8*segCount ) + (numGlyphs+1) @@ -606,46 +606,46 @@ class TTFontFile: 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_: + 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(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_: + # 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_: + 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) + cmapstr += pack(">H", cm) else: try: - cmapstr += pack(">h", cm) + cmapstr += pack(">h", cm) except: warnings.warn("cmap value too big/small: %s" % cm) - cmapstr += pack(">H", -cm) + cmapstr += pack(">H", -cm) self.add('cmap', cmapstr) # glyf - Glyph data @@ -674,9 +674,9 @@ class TTFontFile: maxComponentDepth = 0 # levels of recursion, set to 0 if font has only simple glyphs self.glyphdata = {} - for originalGlyphIdx, uni in subsetglyphs: + for originalGlyphIdx, uni in subsetglyphs: # hmtx - Horizontal Metrics - hm = self.getHMetric(orignHmetrics, originalGlyphIdx) + hm = self.getHMetric(orignHmetrics, originalGlyphIdx) hmtxstr += hm offsets.append(pos) @@ -694,7 +694,7 @@ class TTFontFile: 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 @@ -714,22 +714,22 @@ class TTFontFile: 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_WORDS): + pos_in_glyph += 4 + else: + pos_in_glyph += 2 if (flags & GF_SCALE): - pos_in_glyph += 2 + pos_in_glyph += 2 elif (flags & GF_XYSCALE): - pos_in_glyph += 4 + pos_in_glyph += 4 elif (flags & GF_TWOBYTWO): - pos_in_glyph += 8 - + pos_in_glyph += 8 + maxComponentElements = max(maxComponentElements, nComponentElements) - + glyf += data pos += glyphLen - if (pos % 4 != 0): + if (pos % 4 != 0): padding = 4 - (pos % 4) glyf += str_repeat("\0",padding) pos += padding @@ -742,15 +742,15 @@ class TTFontFile: # loca - Index to location locastr = '' - if (((pos + 1) >> 1) > 0xFFFF): + if (((pos + 1) >> 1) > 0xFFFF): indexToLocFormat = 1 # long format for offset in offsets: - locastr += pack(">L",offset) + locastr += pack(">L",offset) else: indexToLocFormat = 0 # short format - for offset in offsets: - locastr += pack(">H",(offset/2)) - + for offset in offsets: + locastr += pack(">H",(offset/2)) + self.add('loca', locastr) # head - Font header @@ -776,8 +776,8 @@ class TTFontFile: # Put the TTF file together stm = self.endTTFile('') - return stm - + return stm + ######################################### # Recursively get composite glyph data @@ -786,21 +786,21 @@ class TTFontFile: 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) - + 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) - + # &start, &glyphSet, &subsetglyphs) + try: glyphPos = self.glyphPos[originalGlyphIdx] glyphLen = self.glyphPos[originalGlyphIdx + 1] - glyphPos @@ -808,21 +808,21 @@ class TTFontFile: warnings.warn("missing glyph %s" % (originalGlyphIdx)) return - if (not glyphLen): + 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): + 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) @@ -844,56 +844,56 @@ class TTFontFile: aw = 0 self.charWidths = [0] * 256*256*2 nCharWidths = 0 - if ((numberOfHMetrics*4) < self.maxStrLenRead): + 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): + 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): + if (glyph == 0): self.defaultWidth = scale*aw continue - - for char in glyphToChar[glyph]: - if (char != 0 and char != 65535): + + 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 + 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): + for pos in range(diff): glyph = pos + numberOfHMetrics - if (glyph in glyphToChar): - for char in glyphToChar[glyph]: - if (char != 0 and char != 65535): + 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 (w == 0): w = 65535 if (char < 196608): self.charWidths[char] = w - nCharWidths += 1 - - + nCharWidths += 1 + + # NB 65535 is a set width of 0 # First bytes define number of chars in font - self.charWidths[0] = nCharWidths - + self.charWidths[0] = nCharWidths - def getHMetric(self, numberOfHMetrics, gid): + + def getHMetric(self, numberOfHMetrics, gid): start = self.seek_table("hmtx") if (gid < numberOfHMetrics): self.seek(start+(gid*4)) @@ -904,15 +904,15 @@ class TTFontFile: self.seek(start+(numberOfHMetrics*2)+(gid*2)) hm += self.fh.read(2) return hm - - def getLOCA(self, indexToLocFormat, numGlyphs): + + 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): + for n in range(numGlyphs): self.glyphPos.append((arr[n] * 2)) # n+1 !? elif (indexToLocFormat == 1): data = self.get_chunk(start,(numGlyphs*4)+4) @@ -938,18 +938,18 @@ class TTFontFile: self.skip(2) startCount = [] for i in range(segCount): - startCount.append(self.read_ushort()) + 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()) + idRangeOffset.append(self.read_ushort()) - for n in range(segCount): + for n in range(segCount): endpoint = (endCount[n] + 1) - for unichar in range(startCount[n], endpoint, 1): + for unichar in range(startCount[n], endpoint, 1): if (idRangeOffset[n] == 0): glyph = (unichar + idDelta[n]) & 0xFFFF else: @@ -961,33 +961,33 @@ class TTFontFile: 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) + self.maxUniChar = max(unichar,self.maxUniChar) glyphToChar.setdefault(glyph, []).append(unichar) # Put the TTF file together - def endTTFile(self, stm): + def endTTFile(self, stm): stm = '' numTables = count(self.otables) searchRange = 1 entrySelector = 0 - while (searchRange * 2 <= numTables): + while (searchRange * 2 <= numTables): searchRange = searchRange * 2 entrySelector = entrySelector + 1 - + searchRange = searchRange * 16 rangeShift = numTables * 16 - searchRange # Header - if (_TTF_MAC_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 @@ -995,7 +995,7 @@ class TTFontFile: sorted_tables = sorted(tables.items()) for tag, data in sorted_tables: if (tag == 'head'): - head_start = offset + head_start = offset stm += tag checksum = calcChecksum(data) stm += pack(">HH", checksum[0],checksum[1]) @@ -1004,7 +1004,7 @@ class TTFontFile: offset = offset + paddedLength # Table data - for tag, data in sorted_tables: + for tag, data in sorted_tables: data += "\0\0\0" stm += substr(data,0,(strlen(data)&~3)) @@ -1012,8 +1012,8 @@ class TTFontFile: checksum = sub32((0xB1B0,0xAFBA), checksum) chk = pack(">HH", checksum[0],checksum[1]) stm = self.splice(stm,(head_start + 8),chk) - return stm - + return stm + if __name__ == '__main__': ttf = TTFontFile() ttffile = 'DejaVuSansCondensed.ttf'; @@ -1030,4 +1030,4 @@ if __name__ == '__main__': 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() - \ No newline at end of file +