%PDF- %PDF-
Direktori : /usr/lib/calibre/calibre/ebooks/pdf/render/ |
Current File : //usr/lib/calibre/calibre/ebooks/pdf/render/common.py |
#!/usr/bin/env python3 __license__ = 'GPL v3' __copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>' __docformat__ = 'restructuredtext en' import codecs, zlib, numbers from io import BytesIO from datetime import datetime from calibre.utils.logging import default_log from polyglot.builtins import iteritems, codepoint_to_chr from polyglot.binary import as_hex_bytes from calibre_extensions.speedup import pdf_float EOL = b'\n' # Sizes {{{ inch = 72.0 cm = inch / 2.54 mm = cm * 0.1 pica = 12.0 didot = 0.375 * mm cicero = 12 * didot _W, _H = (21*cm, 29.7*cm) A6 = (_W*.5, _H*.5) A5 = (_H*.5, _W) A4 = (_W, _H) A3 = (_H, _W*2) A2 = (_W*2, _H*2) A1 = (_H*2, _W*4) A0 = (_W*4, _H*4) LETTER = (8.5*inch, 11*inch) LEGAL = (8.5*inch, 14*inch) ELEVENSEVENTEEN = (11*inch, 17*inch) _BW, _BH = (25*cm, 35.3*cm) B6 = (_BW*.5, _BH*.5) B5 = (_BH*.5, _BW) B4 = (_BW, _BH) B3 = (_BH*2, _BW) B2 = (_BW*2, _BH*2) B1 = (_BH*4, _BW*2) B0 = (_BW*4, _BH*4) PAPER_SIZES = {k:globals()[k.upper()] for k in ('a0 a1 a2 a3 a4 a5 a6 b0 b1 b2' ' b3 b4 b5 b6 letter legal').split()} # }}} def fmtnum(o): if isinstance(o, float): return pdf_float(o) return str(o) def serialize(o, stream): if isinstance(o, float): stream.write_raw(pdf_float(o).encode('ascii')) elif isinstance(o, bool): # Must check bool before int as bools are subclasses of int stream.write_raw(b'true' if o else b'false') elif isinstance(o, numbers.Integral): stream.write_raw(str(o).encode('ascii')) elif hasattr(o, 'pdf_serialize'): o.pdf_serialize(stream) elif o is None: stream.write_raw(b'null') elif isinstance(o, datetime): val = o.strftime("D:%Y%m%d%H%M%%02d%z")%min(59, o.second) if datetime.tzinfo is not None: val = "(%s'%s')"%(val[:-2], val[-2:]) stream.write(val.encode('ascii')) else: raise ValueError('Unknown object: %r'%o) class Name(str): def pdf_serialize(self, stream): raw = self.encode('ascii') if len(raw) > 126: raise ValueError('Name too long: %r'%self) raw = bytearray(raw) sharp = ord(b'#') buf = ( codepoint_to_chr(x).encode('ascii') if 33 < x < 126 and x != sharp else f'#{x:x}'.encode('ascii') for x in raw) stream.write(b'/'+b''.join(buf)) def escape_pdf_string(bytestring): indices = [] bad = [] ba = bytearray(bytestring) bad_map = {10:ord('n'), 13:ord('r'), 12:ord('f'), 8:ord('b'), 9:ord('\t'), 92:ord('\\')} for i, num in enumerate(ba): if num == 40: # ( indices.append((i, 40)) elif num == 41: # ) if indices: indices.pop() else: bad.append((i, 41)) elif num in bad_map: # '\n\r\f\b\t\\' see Table 3.2 in PDF 1.7 spec bad.append((i, bad_map[num])) bad = sorted(indices + bad, reverse=True) if not bad: return bytestring for i, repl in bad: ba[i:i+1] = (92, repl) # 92 = ord('\') return bytes(ba) class String(str): def pdf_serialize(self, stream): try: raw = self.encode('latin1') if raw.startswith(codecs.BOM_UTF16_BE): raw = codecs.BOM_UTF16_BE + self.encode('utf-16-be') except UnicodeEncodeError: raw = codecs.BOM_UTF16_BE + self.encode('utf-16-be') stream.write(b'('+escape_pdf_string(raw)+b')') class UTF16String(str): def pdf_serialize(self, stream): raw = codecs.BOM_UTF16_BE + self.encode('utf-16-be') if False: # Disabled as the parentheses based strings give easier to debug # PDF files stream.write(b'<' + as_hex_bytes(raw) + b'>') else: stream.write(b'('+escape_pdf_string(raw)+b')') class Dictionary(dict): def pdf_serialize(self, stream): stream.write(b'<<' + EOL) sorted_keys = sorted(self, key=lambda x:({'Type':'1', 'Subtype':'2'}.get( x, x)+x)) for k in sorted_keys: serialize(Name(k), stream) stream.write(b' ') serialize(self[k], stream) stream.write(EOL) stream.write(b'>>' + EOL) class InlineDictionary(Dictionary): def pdf_serialize(self, stream): stream.write(b'<< ') for k, v in iteritems(self): serialize(Name(k), stream) stream.write(b' ') serialize(v, stream) stream.write(b' ') stream.write(b'>>') class Array(list): def pdf_serialize(self, stream): stream.write(b'[') for i, o in enumerate(self): if i != 0: stream.write(b' ') serialize(o, stream) stream.write(b']') class Stream(BytesIO): def __init__(self, compress=False): BytesIO.__init__(self) self.compress = compress self.filters = Array() def add_extra_keys(self, d): pass def pdf_serialize(self, stream): raw = self.getvalue() dl = len(raw) filters = self.filters if self.compress: filters.append(Name('FlateDecode')) raw = zlib.compress(raw) d = InlineDictionary({'Length':len(raw), 'DL':dl}) self.add_extra_keys(d) if filters: d['Filter'] = filters serialize(d, stream) stream.write(EOL+b'stream'+EOL) stream.write(raw) stream.write(EOL+b'endstream'+EOL) def write_line(self, raw=b''): self.write(raw if isinstance(raw, bytes) else raw.encode('ascii')) self.write(EOL) def write(self, raw): super().write(raw if isinstance(raw, bytes) else raw.encode('ascii')) def write_raw(self, raw): BytesIO.write(self, raw) class Reference: def __init__(self, num, obj): self.num, self.obj = num, obj def pdf_serialize(self, stream): raw = '%d 0 R'%self.num stream.write(raw.encode('ascii')) def __repr__(self): return '%d 0 R'%self.num def __str__(self): return repr(self) # }}} def current_log(newlog=None): if newlog: current_log.ans = newlog return current_log.ans or default_log current_log.ans = None