1
1
mirror of https://github.com/Kozea/WeasyPrint.git synced 2024-10-05 00:21:15 +03:00

Write bookmarks and links with the new PDF module.

This commit is contained in:
Simon Sapin 2012-05-20 15:55:57 +02:00
parent 8b994c2ec6
commit b1b5d85b41
2 changed files with 73 additions and 10 deletions

View File

@ -181,12 +181,11 @@ class PDFDocument(Document):
surface.finish()
# XXX
# links = [self._get_link_rectangles(page) for page in self.pages]
# destinations = dict(self._get_link_destinations())
# bookmarks = self._get_bookmarks()
links = [self._get_link_rectangles(page) for page in self.pages]
destinations = dict(self._get_link_destinations())
bookmarks = self._get_bookmarks()
pdf.add_pdf_metadata(fileobj)
pdf.add_pdf_metadata(fileobj, links, destinations, bookmarks)
fileobj.seek(0)
if hasattr(target, 'write'):

View File

@ -38,6 +38,7 @@ import itertools
from . import VERSION_STRING
from .compat import xrange, iteritems
from .utils import iri_to_uri
class PDFFormatter(string.Formatter):
@ -210,6 +211,21 @@ class PDFFile(object):
self.overwritten_objects_offsets[object_number] = (
self._write_object(object_number, byte_string))
def extend_dict(self, dictionary, new_content):
"""Overwrite a dictionary object after adding content inside
the << >> delimiters.
"""
assert dictionary.byte_string.endswith(b'>>\n')
self.overwrite_object(
dictionary.object_number,
dictionary.byte_string[:-3] + new_content + b'\n>>\n')
def next_object_number(self):
"""Return the object number that would be used by write_new_object().
"""
return len(self.objects_offsets) + len(self.new_objects_offsets)
def write_new_object(self, byte_string):
"""Write a new object at the end of the file.
@ -219,9 +235,8 @@ class PDFFile(object):
The new object number.
"""
new_objects_offsets = self.new_objects_offsets
object_number = len(self.objects_offsets) + len(new_objects_offsets)
new_objects_offsets.append(
object_number = self.next_object_number()
self.new_objects_offsets.append(
self._write_object(object_number, byte_string))
return object_number
@ -254,7 +269,7 @@ class PDFFile(object):
'trailer\n<< '
'/Size {size} /Root {root} 0 R /Info {info} 0 R /Prev {prev}'
' >>\nstartxref\n{startxref}\n%%EOF\n',
size=len(self.objects_offsets) + len(self.new_objects_offsets),
size=self.next_object_number(),
root=self.catalog.object_number,
info=self.info.object_number,
prev=self.startxref,
@ -274,11 +289,60 @@ class PDFFile(object):
return fileobj.tell(), fileobj.write
def add_pdf_metadata(fileobj):
def add_pdf_metadata(fileobj, links, destinations, bookmarks):
pdf = PDFFile(fileobj)
pdf.overwrite_object(pdf.info.object_number, pdf_format(
'<< /Producer {producer!P} >>',
producer=VERSION_STRING))
root, bookmarks = bookmarks
if bookmarks:
bookmark_root = pdf.next_object_number()
pdf.write_new_object(pdf_format(
'<< /Type /Outlines /Count {0} /First {1} 0 R /Last {2} 0 R\n>>',
root['Count'],
root['First'] + bookmark_root,
root['Last'] + bookmark_root))
pdf.extend_dict(pdf.catalog, pdf_format(
'/Outlines {0} 0 R', bookmark_root))
for bookmark in bookmarks:
content = [pdf_format('<< /Title {0!P}\n', bookmark['label'])]
if bookmark['Count']:
content.append(pdf_format('/Count {0}\n', bookmark['Count']))
for key in ['Parent', 'Prev', 'Next', 'First', 'Last']:
if bookmark[key]:
content.append(pdf_format(
'/{0} {1} 0 R\n', key, bookmark[key] + bookmark_root))
content.append(pdf_format(
'/A << /Type /Action /S /GoTo '
'/D [{0} /XYZ {1:f} {2:f} 0] >>\n>>',
*bookmark['destination']))
pdf.write_new_object(b''.join(content))
for page, page_links in zip(pdf.pages, links):
annotations = []
for uri, x1, y1, x2, y2 in page_links:
content = [pdf_format(
'<< /Type /Annot /Subtype /Link '
'/Rect [{0:f} {1:f} {2:f} {3:f}] /Border [0 0 0]\n',
x1, y1, x2, y2)]
if uri and uri.startswith('#') and uri[1:] in destinations:
content.append(pdf_format(
'/A << /Type /Action /S /GoTo '
'/D [{0} /XYZ {1:f} {2:f} 0] >>\n',
*destinations[uri[1:]]))
elif uri:
content.append(pdf_format(
'/A << /Type /Action /S /URI /URI ({0}) >>\n',
iri_to_uri(uri)))
content.append(b'>>')
annotations.append(pdf.write_new_object(b''.join(content)))
if annotations:
pdf.extend_dict(page, pdf_format(
'/Annots [{0}]', ' '.join(
'{0} 0 R'.format(n) for n in annotations)))
pdf.finish()