systori/bericht

View on GitHub
bericht/pdf/page.py

Summary

Maintainability
A
35 mins
Test Coverage
from bericht.html.box import stringWidth
from bericht.html.style import default as default_style
from .text import PDFText

__all__ = ('PDFPage',)


class PDFPage:

    tag = '@page'

    def __init__(self, document, page_number, css, size='letter', layout='portrait'):
        self.parent = None
        self.position = page_number
        self.classes = []
        self.style = default_style.set(**{
            'margin_'+side: value for side, value in DEFAULT_MARGINS.items()
        })
        css.apply(self)
        self.margins = {
            side: getattr(self.style, 'margin_'+side) for side in DEFAULT_MARGINS
        }

        self.document = document
        self.size = size
        self.layout = layout

        dimensions = SIZES[self.size.upper()]
        self.width = dimensions[0 if self.layout is 'portrait' else 1]
        self.height = dimensions[1 if self.layout is 'portrait' else 0]

        self.x = self.margins['left']
        self.y = self.height - self.margins['top']
        self.available_width = self.width - self.x - self.margins['right']

        self.content = document.ref()

        # Initialize the Font, XObject, and ExtGState dictionaries
        self.resources = document.ref({
            'ProcSet': ['PDF', 'Text', 'ImageB', 'ImageC', 'ImageI'],
        })

        if self.document.letterhead and self.style.letterhead_page:
            letterhead_page = self.document.letterhead[int(self.style.letterhead_page)-1]
            self.resources.meta.update({
                'XObject': {letterhead_page.name: letterhead_page}
            })
            self.write('/{} Do\n'.format(letterhead_page.name))

        # The page dictionary
        self.dictionary = document.ref({
            'Type': 'Page',
            'Parent': document.root.meta['Pages'],
            'MediaBox': [0, 0, self.width, self.height],
            'Contents': self.content,
            'Resources': self.resources,
        })

        if self.style.page_bottom_right_content:
            style = self.style.set(font_size=9)
            page_text = self.style.page_bottom_right_content(self)
            text_width = stringWidth(page_text, style.font_name, style.font_size)
            text = self.begin_text(
                (self.margins['left'] + self.available_width) - text_width,
                self.margins['bottom'] - style.leading*2
            )
            text.set_font(style.font_name, style.leading, style.font_size)
            text.draw(page_text)
            text.close()

    @property
    def page_number(self):
        return self.position

    @property
    def has_content(self):
        return self.content.has_content

    @property
    def available_height(self):
        return self.y - self.margins['bottom']

    @property
    def font(self):
        if 'Font' not in self.resources.meta:
            self.resources.meta['Font'] = {}
        return self.resources.meta['Font']

    def write(self, chunk):
        self.content.write(chunk.encode())

    def read(self):
        for ref in (self.dictionary, self.resources, self.content):
            yield from self.document.finalize_reference(ref)

    def save_state(self):
        self.write("q\n")

    def restore_state(self):
        self.write("Q\n")

    def translate(self, x, y):
        self.write("1 0 0 1 {} {} cm\n".format(x, y))

    def begin_text(self, x, y):
        return PDFText(self, x, y)

    def line_width(self, width):
        self.write("{} w\n".format(width))

    def stroke_color(self, r, g, b, a):
        assert a == 1, "TODO: implement alpha"
        self.write("{} {} {} RG\n".format(r, g, b))

    def line(self, x1, y1, x2, y2):
        self.write("n {} {} m {} {} l S\n".format(x1, y1, x2, y2))

    def fill_color(self, r, g, b, a):
        assert a == 1, "TODO: implement alpha"
        self.write("{} {} {} rg\n".format(r, g, b))

    def rectangle(self, x, y, width, height):
        self.write("n {} {} {} {} re f*\n".format(x, y, width, height))


DEFAULT_MARGINS = {
    'top': 72,
    'left': 72,
    'bottom': 72,
    'right': 72
}

SIZES = {
    '4A0': [4767.87, 6740.79],
    '2A0': [3370.39, 4767.87],
    'A0': [2383.94, 3370.39],
    'A1': [1683.78, 2383.94],
    'A2': [1190.55, 1683.78],
    'A3': [841.89, 1190.55],
    'A4': [595.28, 841.89],
    'A5': [419.53, 595.28],
    'A6': [297.64, 419.53],
    'A7': [209.76, 297.64],
    'A8': [147.40, 209.76],
    'A9': [104.88, 147.40],
    'A10': [73.70, 104.88],
    'B0': [2834.65, 4008.19],
    'B1': [2004.09, 2834.65],
    'B2': [1417.32, 2004.09],
    'B3': [1000.63, 1417.32],
    'B4': [708.66, 1000.63],
    'B5': [498.90, 708.66],
    'B6': [354.33, 498.90],
    'B7': [249.45, 354.33],
    'B8': [175.75, 249.45],
    'B9': [124.72, 175.75],
    'B10': [87.87, 124.72],
    'C0': [2599.37, 3676.54],
    'C1': [1836.85, 2599.37],
    'C2': [1298.27, 1836.85],
    'C3': [918.43, 1298.27],
    'C4': [649.13, 918.43],
    'C5': [459.21, 649.13],
    'C6': [323.15, 459.21],
    'C7': [229.61, 323.15],
    'C8': [161.57, 229.61],
    'C9': [113.39, 161.57],
    'C10': [79.37, 113.39],
    'RA0': [2437.80, 3458.27],
    'RA1': [1729.13, 2437.80],
    'RA2': [1218.90, 1729.13],
    'RA3': [864.57, 1218.90],
    'RA4': [609.45, 864.57],
    'SRA0': [2551.18, 3628.35],
    'SRA1': [1814.17, 2551.18],
    'SRA2': [1275.59, 1814.17],
    'SRA3': [907.09, 1275.59],
    'SRA4': [637.80, 907.09],
    'EXECUTIVE': [521.86, 756.00],
    'FOLIO': [612.00, 936.00],
    'LEGAL': [612.00, 1008.00],
    'LETTER': [612.00, 792.00],
    'TABLOID': [792.00, 1224.00]
}