def __init__(self, stream, page_size, compress=False, mark_links=False, debug=print): self.stream = HashingStream(stream) self.compress = compress self.write_line(PDFVER) self.write_line(u'%íì¦"'.encode('utf-8')) creator = ('%s %s [https://calibre-ebook.com]' % (__appname__, __version__)) self.write_line('%% Created by %s' % creator) self.objects = IndirectObjects() self.objects.add(PageTree(page_size)) self.objects.add(Catalog(self.page_tree)) self.current_page = Page(self.page_tree, compress=self.compress) self.info = Dictionary({ 'Creator': String(creator), 'Producer': String(creator), 'CreationDate': utcnow(), }) self.stroke_opacities, self.fill_opacities = {}, {} self.font_manager = FontManager(self.objects, self.compress) self.image_cache = {} self.pattern_cache, self.shader_cache = {}, {} self.debug = debug self.links = Links(self, mark_links, page_size) i = QImage(1, 1, QImage.Format_ARGB32) i.fill(qRgba(0, 0, 0, 255)) self.alpha_bit = i.constBits().asstring(4).find(b'\xff')
def set_metadata(self, title=None, author=None, tags=None): if title: self.info['Title'] = String(title) if author: self.info['Author'] = String(author) if tags: self.info['Keywords'] = String(tags)
def set_metadata(self, title=None, author=None, tags=None, mi=None): if title: self.info['Title'] = String(title) if author: self.info['Author'] = String(author) if tags: self.info['Keywords'] = String(tags) if mi is not None: self.metadata = self.objects.add(Metadata(mi)) self.catalog.obj['Metadata'] = self.metadata
def __init__(self, metrics, num, objects, compress): self.metrics, self.compress = metrics, compress self.is_otf = self.metrics.is_otf self.subset_tag = str( re.sub('.', lambda m: codepoint_to_chr(int(m.group())+ord('A')), oct(num).replace('o', '') )).rjust(6, 'A') self.font_stream = FontStream(metrics.is_otf, compress=compress) try: psname = metrics.postscript_name except Exception: psname = uuid4() self.font_descriptor = Dictionary({ 'Type': Name('FontDescriptor'), 'FontName': Name('%s+%s'%(self.subset_tag, psname)), 'Flags': 0b100, # Symbolic font 'FontBBox': Array(metrics.pdf_bbox), 'ItalicAngle': metrics.post.italic_angle, 'Ascent': metrics.pdf_ascent, 'Descent': metrics.pdf_descent, 'CapHeight': metrics.pdf_capheight, 'AvgWidth': metrics.pdf_avg_width, 'StemV': metrics.pdf_stemv, }) self.descendant_font = Dictionary({ 'Type':Name('Font'), 'Subtype':Name('CIDFontType' + ('0' if metrics.is_otf else '2')), 'BaseFont': self.font_descriptor['FontName'], 'FontDescriptor':objects.add(self.font_descriptor), 'CIDSystemInfo':Dictionary({ 'Registry':String('Adobe'), 'Ordering':String('Identity'), 'Supplement':0, }), }) if not self.is_otf: self.descendant_font['CIDToGIDMap'] = Name('Identity') self.font_dict = Dictionary({ 'Type':Name('Font'), 'Subtype':Name('Type0'), 'Encoding':Name('Identity-H'), 'BaseFont':self.descendant_font['BaseFont'], 'DescendantFonts':Array([objects.add(self.descendant_font)]), }) self.used_glyphs = set()
def add_links(self): for link in self.links: path, href, frag = link[0] page, rect = link[1:] combined_path = os.path.normcase( os.path.abspath( os.path.join(os.path.dirname(path), *unquote(href).split('/')))) is_local = not href or combined_path in self.anchors annot = Dictionary({ 'Type': Name('Annot'), 'Subtype': Name('Link'), 'Rect': rect, 'Border': Array([0, 0, 0]), }) if self.mark_links: annot.update({ 'Border': Array([16, 16, 1]), 'C': Array([1.0, 0, 0]) }) if is_local: path = combined_path if href else path try: annot['Dest'] = self.anchors[path][frag] except KeyError: try: annot['Dest'] = self.anchors[path][None] except KeyError: pass else: url = href + (('#' + frag) if frag else '') try: purl = urlparse(url) except Exception: self.pdf.debug('Ignoring unparseable URL: %r' % url) continue if purl.scheme and purl.scheme != 'file': action = Dictionary({ 'Type': Name('Action'), 'S': Name('URI'), }) # Do not try to normalize/quote/unquote this URL as if it # has a query part, it will get corrupted action['URI'] = String(url) annot['A'] = action if 'A' in annot or 'Dest' in annot: if 'Annots' not in page: page['Annots'] = Array() page['Annots'].append(self.pdf.objects.add(annot)) else: self.pdf.debug( 'Could not find destination for link: %s in file %s' % (href, path))
def process_toc_item(self, toc, parentref): path = toc.abspath or None frag = toc.fragment or None if path is None: return path = os.path.normcase(os.path.abspath(path)) if path not in self.anchors: return None a = self.anchors[path] dest = a.get(frag, a[None]) item = Dictionary({'Parent':parentref, 'Dest':dest, 'Title':String(toc.text or _('Unknown'))}) return self.pdf.objects.add(item)
def add_links(self): for link in self.links: path, href, frag = link[0] page, rect = link[1:] combined_path = os.path.abspath( os.path.join(os.path.dirname(path), *unquote(href).split('/'))) is_local = not href or combined_path in self.anchors annot = Dictionary({ 'Type': Name('Annot'), 'Subtype': Name('Link'), 'Rect': rect, 'Border': Array([0, 0, 0]), }) if self.mark_links: annot.update({ 'Border': Array([16, 16, 1]), 'C': Array([1.0, 0, 0]) }) if is_local: path = combined_path if href else path try: annot['Dest'] = self.anchors[path][frag] except KeyError: try: annot['Dest'] = self.anchors[path][None] except KeyError: pass else: url = href + (('#' + frag) if frag else '') purl = urlparse(url) if purl.scheme and purl.scheme != 'file': action = Dictionary({ 'Type': Name('Action'), 'S': Name('URI'), }) parts = (x.encode('utf-8') if isinstance(x, type(u'')) else x for x in purl) url = urlunparse(map(quote, map(unquote, parts))).decode('ascii') action['URI'] = String(url) annot['A'] = action if 'A' in annot or 'Dest' in annot: if 'Annots' not in page: page['Annots'] = Array() page['Annots'].append(self.pdf.objects.add(annot)) else: self.pdf.debug( 'Could not find destination for link: %s in file %s' % (href, path))
def end(self): if self.current_page.getvalue(): self.end_page() self.font_manager.embed_fonts(self.debug) inforef = self.objects.add(self.info) self.links.add_links() self.objects.pdf_serialize(self.stream) self.write_line() startxref = self.objects.write_xref(self.stream) file_id = String(as_unicode(self.stream.hashobj.hexdigest())) self.write_line('trailer') trailer = Dictionary({'Root':self.catalog, 'Size':len(self.objects)+1, 'ID':Array([file_id, file_id]), 'Info':inforef}) serialize(trailer, self.stream) self.write_line('startxref') self.write_line('%d'%startxref) self.stream.write('%%EOF')