# -*- coding: utf-8 -*- # (C) 2011 by Bernd Wurst # This file is part of Bib2011. # # Bib2011 is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Bib2011 is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Bib2011. If not, see . import os.path import re from reportlab.lib.pagesizes import A4 # reportlab imports from reportlab.lib.units import cm, inch from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.ttfonts import TTFont, TTFError from reportlab.pdfgen import canvas from .InvoiceObjects import InvoiceTable, InvoiceText, InvoiceImage, GUTSCHRIFT def _formatPrice(price, symbol='€'): '''_formatPrice(price, symbol='€'): Gets a floating point value and returns a formatted price, suffixed by 'symbol'. ''' s = ("%.2f" % price).replace('.', ',') pat = re.compile(r'([0-9])([0-9]{3}[.,])') while pat.search(s): s = pat.sub(r'\1.\2', s) return s + ' ' + symbol def find_font_file(filename): for n in range(4): candidate = os.path.abspath(os.path.join(os.path.dirname(__file__), '../' * n, 'ressource/fonts', filename)) if os.path.exists(candidate): return candidate def _registerFonts(): fonts = [ ("DejaVu", "DejaVuSans.ttf"), ("DejaVu-Bold", "DejaVuSans-Bold.ttf"), ("DejaVu-Italic", "DejaVuSans-Oblique.ttf"), ("DejaVu-BoldItalic", "DejaVuSans-BoldOblique.ttf") ] for fontname, fontfile in fonts: found = False try: pdfmetrics.registerFont(TTFont(fontname, fontfile)) found = True except TTFError: pass if not found: f = find_font_file(fontfile) if f: pdfmetrics.registerFont(TTFont(fontname, f)) class PDF(object): # Set default font size default_font_size = 8 font = 'DejaVu' # set margins topmargin = 2 * cm bottommargin = 2.2 * cm leftmargin = 2 * cm rightmargin = 2 * cm rightcolumn = 13 * cm canvas = None num_pages = 1 font_height = 0.3 * cm line_padding = 0.1 * cm line_height = font_height + 0.1 * cm invoice = None def __init__(self, invoice): _registerFonts() from io import BytesIO self.fd = BytesIO() self.canvas = canvas.Canvas(self.fd, pagesize=A4) self.invoice = invoice self.topcontent = -self.topmargin self.leftcontent = self.leftmargin self.rightcontent = A4[0] - self.rightmargin self.bottomcontent = -(A4[1] - self.bottommargin) self.font_size = 8 self.x = 2.0 * cm self.y = -4.8 * cm - self.font_size - 1 self.canvas.setFont(self.font, self.font_size) def _splitToWidth(self, text, width, font, size): '''_splitToWidth(canvas, text, width, font, size) Split a string to several lines of a given width.''' lines = [] paras = text.split('\n') for para in paras: words = para.split(' ') while len(words) > 0: mywords = [words[0], ] del words[0] while len(words) > 0 and self.canvas.stringWidth(' '.join(mywords) + ' ' + words[0], font, size) <= width: mywords.append(words[0]) del words[0] lines.append(' '.join(mywords)) return lines def _PageMarkers(self): """Setzt Falzmarken""" self.canvas.setStrokeColor((0, 0, 0)) self.canvas.setLineWidth(0.01 * cm) self.canvas.lines([(0.3 * cm, -10.5 * cm, 0.65 * cm, -10.5 * cm), (0.3 * cm, -21.0 * cm, 0.65 * cm, -21.0 * cm), (0.3 * cm, -14.85 * cm, 0.7 * cm, -14.85 * cm)]) def _lineHeight(self, fontsize=None, font=None): if not fontsize: fontsize = self.default_font_size if not font: font = self.font face = pdfmetrics.getFont(font).face string_height = (face.ascent - face.descent) / 1000 * fontsize return string_height + 0.1 * cm def _partHeight(self, part): height = 0 if isinstance(part, InvoiceText): left, right = self.leftcontent, self.rightcontent if part.urgent: left += 1.5 * cm right -= 1.5 * cm height += len(part.paragraphs) * 3 * self.line_padding # Rechne eine Zeile mehr für den Rahmen height += self.line_height if part.headline: height += (len(self._splitToWidth(part.headline, right - left, self.font + '-Bold', self.default_font_size + 1)) * self.line_height) + self.line_padding for para in part.paragraphs: height += (len(self._splitToWidth(para, right - left, self.font, self.default_font_size)) * self.line_height) + self.line_padding elif isinstance(part, InvoiceTable): # Eine Zeile plus 2 mal line_padding für Tabellenkopf height = self.line_height + 2 * self.line_padding # Wenn nur ein Element (plus Summen) hin passt, reicht uns das el = part.entries[0] # Die Abstände oben und unten height += 2 * self.line_padding # Die Breite ist konservativ if el['type'] == 'title': height += self.line_height + 0.2 * cm else: height += self.line_height * len(self._splitToWidth(el['subject'], 9.3 * cm, self.font, self.font_size)) if 'desc' in el and el['desc'] != '': height += self.line_height * len(self._splitToWidth(el['desc'], 11 * cm, self.font, self.font_size)) if part.vatType == 'net': # Eine Zeile mehr height += self.line_height + self.line_padding # Für die MwSt-Summen height += (self.line_height + self.line_padding) * len(part.vat) # Für den Rechnungsbetrag height += self.line_height + self.line_padding return height def _tableHead(self, part): self.canvas.setFont(self.font, self.font_size) self.canvas.drawString(self.leftcontent + (0.1 * cm), self.y - self.line_height + self.line_padding, 'Anz.') self.canvas.drawString(self.leftcontent + (2.1 * cm), self.y - self.line_height + self.line_padding, 'Beschreibung') if len(part.vat) == 1: self.canvas.drawRightString(self.leftcontent + (14.3 * cm), self.y - self.line_height + self.line_padding, 'Einzelpreis') else: self.canvas.drawRightString(self.leftcontent + (13.3 * cm), self.y - self.line_height + self.line_padding, 'Einzelpreis') self.canvas.drawRightString(self.leftcontent + (16.8 * cm), self.y - self.line_height + self.line_padding, 'Gesamtpreis') self.canvas.setLineWidth(0.01 * cm) self.canvas.line(self.leftcontent, self.y - self.line_height, self.rightcontent, self.y - self.line_height) self.y -= self.line_height + 0.02 * cm def _PageWrap(self): '''Seitenumbruch''' self.num_pages += 1 self.canvas.setFont(self.font, self.default_font_size - 2) self.canvas.drawRightString(self.rightcontent, self.bottomcontent + self.line_padding, 'Fortsetzung auf Seite %i' % self.num_pages) self.canvas.showPage() self.basicPage() self.y = self.topcontent - self.font_size self.canvas.setFillColor((0, 0, 0)) self.canvas.setFont(self.font, self.font_size - 2) self.canvas.drawCentredString(self.leftcontent + (self.rightcontent - self.leftcontent) / 2, self.y, '- Seite %i -' % self.num_pages) def _Footer(self): self.canvas.setStrokeColor((0, 0, 0)) self.canvas.setFillColor((0, 0, 0)) self.canvas.line(self.leftcontent, self.bottomcontent, self.rightcontent, self.bottomcontent) self.canvas.setFont(self.font, 7) lines = list(filter(None, [ self.invoice.seller['trade_name'], self.invoice.seller['name'] if self.invoice.seller['trade_name'] else None, self.invoice.seller['website'], self.invoice.seller['email'], ])) c = 0 for line in lines: c += 10 self.canvas.drawString(self.leftcontent, self.bottomcontent - c, line) if self.invoice.seller_vat_id: lines = list(filter(None, [ "USt-ID: " + self.invoice.seller_vat_id ])) c = 0 for line in lines: c += 10 self.canvas.drawString(self.leftcontent + ((self.rightcontent - self.leftcontent) // 3), self.bottomcontent - c, line) if not self.invoice.debit: iban = self.invoice.seller_bank_data['iban'] iban = ' '.join([iban[i:i + 4] for i in range(0, len(iban), 4)]) lines = [ f"IBAN: {iban}", self.invoice.seller_bank_data['bankname'], f"BIC: {self.invoice.seller_bank_data['bic']}" if self.invoice.seller_bank_data['bic'] else None, ] c = 0 for line in lines: c += 10 self.canvas.drawString(self.leftcontent + ((self.rightcontent - self.leftcontent) // 3) * 2, self.bottomcontent - c, line) def basicPage(self): # Set marker to top. self.canvas.translate(0, A4[1]) self._PageMarkers() self._Footer() def addressBox(self): lines = [ self.invoice.seller['trade_name'], self.invoice.seller['address']['line1'], self.invoice.seller['address']['line2'], self.invoice.seller['address']['line3'], self.invoice.seller['address']['postcode'] + ' ' + self.invoice.seller['address']['city_name'], ] address = ' · '.join(filter(None, lines)) self.canvas.drawString(self.x, self.y + 0.1 * cm, f' {address}') self.canvas.line(self.x, self.y, self.x + (8.5 * cm), self.y) self.y = self.y - self.font_size - 3 font_size = 11 x = self.x + 0.5 * cm self.y -= 0.5 * cm self.canvas.setFont(self.font, font_size) addresslines = filter(None, [ self.invoice.customer['name'].strip(), self.invoice.customer['address']['line1'] or '', self.invoice.customer['address']['line2'] or '', self.invoice.customer['address']['line3'] or '', ((self.invoice.customer['address']['postcode'] or '') + ' ' + ( self.invoice.customer['address']['city_name'] or '')).strip(), ]) for line in addresslines: self.canvas.drawString(x, self.y, line) self.y -= font_size * 0.03527 * cm * 1.2 def firstPage(self): self.basicPage() self.addressBox() self.y = self.topcontent self.canvas.drawImage(self.invoice.logo_image_file, self.rightcolumn, self.topcontent - (2 * cm), height=2 * cm, preserveAspectRatio=True, anchor='nw') self.y -= (2.5 * cm) self.canvas.setFont(self.font + "-Bold", self.font_size) self.canvas.drawString(self.rightcolumn, self.y, self.invoice.seller['trade_name'] or self.invoice.seller['name']) self.y -= (self.font_size + 5) self.canvas.setFont(self.font, self.font_size) lines = [ self.invoice.seller['name'] if self.invoice.seller['trade_name'] else None, self.invoice.seller['address']['line1'], self.invoice.seller['address']['line2'], self.invoice.seller['address']['line3'], self.invoice.seller['address']['postcode'] + ' ' + self.invoice.seller['address']['city_name'], self.invoice.seller['website'], ] address = filter(None, lines) for line in address: self.canvas.drawString(self.rightcolumn, self.y, line) self.y -= (self.font_size + 5) self.y -= 5 if self.invoice.seller['phone']: self.canvas.drawString(self.rightcolumn, self.y, f"Tel: {self.invoice.seller['phone']}") self.y -= (self.font_size + 5) if self.invoice.seller['email']: self.canvas.drawString(self.rightcolumn, self.y, f"E-Mail: {self.invoice.seller['email']}") self.y -= (self.font_size + 10) self.y = -9.5 * cm def title(self, title): self.canvas.setTitle(title) self.canvas.drawString(self.leftcontent, self.y, title) def renderRechnung(self): self.firstPage() self.canvas.setFont(self.font + '-Bold', self.font_size + 3) self.title(self.invoice.title) if self.invoice.tender: self.canvas.setFont(self.font, self.font_size) self.canvas.drawString(self.rightcolumn, self.y, "Erstellungsdatum:") self.canvas.drawRightString(self.rightcontent, self.y, "%s" % self.invoice.date.strftime('%d. %m. %Y')) self.y -= (self.font_size + 0.1 * cm) else: self.canvas.setFont(self.font + '-Bold', self.font_size) self.canvas.drawString(self.rightcolumn, self.y, "Bei Fragen bitte immer angeben:") self.y -= (self.font_size + 0.2 * cm) self.canvas.setFont(self.font, self.font_size) self.canvas.drawString(self.rightcolumn, self.y, "Rechnungsdatum:") self.canvas.drawRightString(self.rightcontent, self.y, "%s" % self.invoice.date.strftime('%d. %m. %Y')) self.y -= (self.font_size + 0.1 * cm) self.canvas.drawString(self.rightcolumn, self.y, "Rechnungsnummer:") self.canvas.drawRightString(self.rightcontent, self.y, "%s" % self.invoice.id) self.y -= (self.font_size + 0.1 * cm) if self.invoice.customerno: self.canvas.drawString(self.rightcolumn, self.y, "Kundennummer:") self.canvas.drawRightString(self.rightcontent, self.y, "%s" % self.invoice.customerno) self.y -= (self.font_size + 0.5 * cm) if self.invoice.leitweg_id: self.canvas.drawString(self.rightcolumn, self.y, "Leitweg-ID:") self.canvas.drawRightString(self.rightcontent, self.y, "%s" % self.invoice.leitweg_id) self.y -= (self.font_size + 0.1 * cm) if self.invoice.buyer_reference: self.canvas.drawString(self.rightcolumn, self.y, "Kunden-Referenz:") self.canvas.drawRightString(self.rightcontent, self.y, "%s" % self.invoice.buyer_reference) self.y -= (self.font_size + 0.1 * cm) if self.invoice.contract_number: self.canvas.drawString(self.rightcolumn, self.y, "Vertragsnummer:") self.canvas.drawRightString(self.rightcontent, self.y, "%s" % self.invoice.contract_number) self.y -= (self.font_size + 0.1 * cm) if self.invoice.order_number: self.canvas.drawString(self.rightcolumn, self.y, "Ihre Bestellnummer:") self.canvas.drawRightString(self.rightcontent, self.y, "%s" % self.invoice.order_number) self.y -= (self.font_size + 0.1 * cm) self.canvas.setFont(self.font, self.font_size) if self.invoice.salutation: self.canvas.drawString(self.leftcontent, self.y, self.invoice.salutation) self.y -= self.font_size + 0.2 * cm introText = 'hiermit stellen wir Ihnen die nachfolgend genannten Leistungen in Rechnung.' if self.invoice.tender: introText = 'hiermit unterbreiten wir Ihnen folgendes Angebot.' if self.invoice.type == GUTSCHRIFT: introText = 'nach unserer Berechnung entsteht für Sie folgende Gutschrift.' intro = self._splitToWidth(introText, self.rightcontent - self.leftcontent, self.font, self.font_size) for line in intro: self.canvas.drawString(self.leftcontent, self.y, line) self.y -= self.font_size + 0.1 * cm self.y -= self.font_size + 0.1 * cm font_size = self.default_font_size for part in self.invoice.parts: if self.y - self._partHeight(part) < (self.bottomcontent + (0.5 * cm)): self._PageWrap() self.y = self.topcontent - self.font_size - self.line_padding * 3 if isinstance(part, InvoiceTable): left = self.leftcontent right = self.rightcontent self._tableHead(part) temp_sum = 0.0 odd = True for el in part.entries: if el['type'] == 'title': self.y -= self.line_padding + 0.2 * cm self.canvas.setFillColorRGB(0, 0, 0) self.canvas.setFont(self.font + '-Italic', font_size) self.canvas.drawString(left, self.y - self.font_height, el['title']) self.canvas.setFont(self.font, font_size) self.y -= self.line_height + self.line_padding else: if len(part.vat) == 1: subject = self._splitToWidth(el['subject'], 9.8 * cm, self.font, font_size) else: subject = self._splitToWidth(el['subject'], 8.8 * cm, self.font, font_size) desc = [] if 'desc' in el and el['desc'] != '': desc = self._splitToWidth(el['desc'], 14.0 * cm, self.font, font_size) if 'period_start' in el and el['period_start']: if 'period_end' in el and el['period_end']: desc.extend(self._splitToWidth('Leistungszeitraum: %s - %s' % (el['period_start'].strftime('%d.%m.%Y'), el['period_end'].strftime('%d.%m.%Y')), 14.0 * cm, self.font, font_size)) else: desc.extend(self._splitToWidth('Leistungsdatum: %s' % (el['period_start'].strftime('%d.%m.%Y'),), 14.0 * cm, self.font, font_size)) need_lines = len(subject) + len(desc) # need page wrap? if self.y - (need_lines + 1 * (self.line_height + self.line_padding)) < ( self.bottomcontent + 1 * cm): self.canvas.setFont(self.font + '-Italic', font_size) # Zwischensumme self.canvas.drawRightString(left + 14.5 * cm, self.y - self.font_height, 'Zwischensumme:') self.canvas.drawRightString(left + 16.8 * cm, self.y - self.font_height, _formatPrice(temp_sum)) # page wrap self._PageWrap() self.y = self.topcontent - font_size - self.line_padding * 3 # header self._tableHead(part) self.y -= self.line_padding * 3 odd = True # übertrag self.canvas.setFont(self.font + '-Italic', font_size) self.canvas.drawRightString(left + 14.5 * cm, self.y - self.font_height, 'Übertrag:') self.canvas.drawRightString(left + 16.8 * cm, self.y - self.font_height, _formatPrice(temp_sum)) self.y -= self.font_height + self.line_padding * 3 self.canvas.setFont(self.font, self.default_font_size) # Zwischensumme (inkl. aktueller Posten) temp_sum += el['total'] # draw the background if not odd: self.canvas.setFillColorRGB(0.9, 0.9, 0.9) else: self.canvas.setFillColorRGB(1, 1, 1) self.canvas.rect(left, self.y - (need_lines * self.line_height) - (2 * self.line_padding), height=(need_lines * self.line_height) + (2 * self.line_padding), width=right - left, fill=1, stroke=0) self.canvas.setFillColorRGB(0, 0, 0) self.y -= self.line_padding self.canvas.drawRightString(left + 1.1 * cm, self.y - self.font_height, '%.0f' % el['count']) if el['unit']: self.canvas.drawString(left + 1.2 * cm, self.y - self.font_height, el['unit']) self.canvas.drawString(left + 2.2 * cm, self.y - self.font_height, subject[0]) if len(part.vat) == 1: self.canvas.drawRightString(left + 14.3 * cm, self.y - self.font_height, _formatPrice(el['price'])) else: self.canvas.drawRightString(left + 13.3 * cm, self.y - self.font_height, _formatPrice(el['price'])) self.canvas.drawString(left + 13.7 * cm, self.y - self.font_height, str(part.vat[el['vat']][1])) if el['tender']: self.canvas.drawRightString(left + 16.8 * cm, self.y - self.font_height, 'eventual') else: self.canvas.drawRightString(left + 16.8 * cm, self.y - self.font_height, _formatPrice(el['total'])) subject = subject[1:] x = 1 for line in subject: self.canvas.drawString(left + 2.2 * cm, self.y - (x * self.line_height) - self.font_height, line) x += 1 for line in desc: self.canvas.drawString(left + 2.2 * cm, self.y - (x * self.line_height) - self.font_height, line) x += 1 odd = not odd self.y -= (need_lines * self.line_height) + self.line_padding if part.summary: need_lines = 5 if self.y - (need_lines + 1 * (self.line_height + self.line_padding)) < ( self.bottomcontent + 1 * cm): self.canvas.setFont(self.font + '-Italic', font_size) # Zwischensumme self.canvas.drawRightString(left + 14.5 * cm, self.y - self.font_height, 'Zwischensumme:') self.canvas.drawRightString(left + 16.8 * cm, self.y - self.font_height, _formatPrice(temp_sum)) # page wrap self._PageWrap() self.y = self.topcontent - font_size - self.line_padding * 3 # header self._tableHead(part) odd = True # übertrag self.canvas.setFont(self.font + '-Italic', font_size) self.canvas.drawRightString(left + 14.5 * cm, self.y - self.font_height, 'Übertrag:') self.canvas.drawRightString(left + 16.8 * cm, self.y - self.font_height, _formatPrice(temp_sum)) self.y -= self.font_height + self.line_padding self.y -= (0.3 * cm) if part.vatType == 'gross': self.canvas.setFont(self.font + '-Bold', font_size) if self.invoice.tender or not self.invoice.official: self.canvas.drawRightString(left + 14.5 * cm, self.y - self.font_height, 'Gesamtbetrag:') else: self.canvas.drawRightString(left + 14.5 * cm, self.y - self.font_height, 'Rechnungsbetrag:') self.canvas.drawRightString(left + 16.8 * cm, self.y - self.font_height, _formatPrice(part.sum)) if self.invoice.official: self.canvas.setFont(self.font, font_size) self.y -= self.line_height + self.line_padding summaries = [] vat = 0.0 if len(part.vat) == 1 and list(part.vat.keys())[0] == 0.0: self.canvas.drawString(left, self.y - self.font_height, 'Dieser Beleg wurde ohne Ausweis von MwSt erstellt.') self.y -= self.line_height else: if len(part.vat) == 1: vat = list(part.vat.keys())[0] if self.invoice.tender: summaries.append(('Im Gesamtbetrag sind %.1f%% MwSt enthalten:' % (vat * 100), _formatPrice((part.sum / (vat + 1)) * vat))) else: summaries.append(( 'Im Rechnungsbetrag sind %.1f%% MwSt enthalten:' % (vat * 100), _formatPrice((part.sum / (vat + 1)) * vat))) else: for vat, vatdata in part.vat.items(): if vat > 0: summaries.append(('%s: Im Teilbetrag von %s sind %.1f%% MwSt enthalten:' % ( vatdata[1], _formatPrice(vatdata[0]), vat * 100), _formatPrice((vatdata[0] / (vat + 1)) * vat))) else: summaries.append(('%s: Durchlaufende Posten ohne Berechnung von MwSt.' % ( vatdata[1]), 0.0)) summaries.append(('Nettobetrag:', _formatPrice(part.sum - (part.sum / (vat + 1)) * vat))) summaries.sort() for line in summaries: self.canvas.drawRightString(left + 14.5 * cm, self.y - self.font_height, line[0]) if line[1]: self.canvas.drawRightString(left + 16.8 * cm, self.y - self.font_height, line[1]) self.y -= self.line_height elif len(part.vat) == 1 and part.vatType == 'net': self.canvas.drawRightString(left + 14.5 * cm, self.y - self.font_height, 'Nettobetrag:') self.canvas.drawRightString(left + 16.8 * cm, self.y - self.font_height, _formatPrice(part.sum)) self.y -= self.line_height summaries = [] if list(part.vat.keys())[0] == 0.0: self.canvas.drawString(left, self.y - self.font_height, 'Dieser Beleg wurde ohne Ausweis von MwSt erstellt.') self.y -= self.line_height else: if len(part.vat) == 1: vat = list(part.vat.keys())[0] summaries.append(('zzgl. %.1f%% MwSt:' % (vat * 100), _formatPrice(vat * part.sum))) else: for vat, vatdata in part.vat.items(): summaries.append(('zzgl. %.1f%% MwSt (%s):' % (vat * 100, vatdata[1]), _formatPrice(vat * vatdata[0]))) summaries.sort() for line in summaries: self.canvas.drawRightString(left + 14.5 * cm, self.y - self.font_height, line[0]) self.canvas.drawRightString(left + 16.8 * cm, self.y - self.font_height, line[1]) self.y -= self.line_height sum = 0 for vat, vatdata in part.vat.items(): sum += (vat + 1) * vatdata[0] self.canvas.setFont(self.font + '-Bold', font_size) if self.invoice.tender: self.canvas.drawRightString(left + 14.5 * cm, self.y - self.font_height, 'Gesamtbetrag:') else: self.canvas.drawRightString(left + 14.5 * cm, self.y - self.font_height, 'Rechnungsbetrag:') self.canvas.drawRightString(left + 16.8 * cm, self.y - self.font_height, _formatPrice(sum)) self.canvas.setFont(self.font, font_size) self.y -= self.line_height + self.line_padding paysum = 0.0 for pay in part.payments: paysum += pay['amount'] descr = 'Zahlung' if pay['type'] == 'cash': descr = 'gegeben' elif pay['type'] == 'return': descr = 'zurück' elif pay['type'] == 'ec': descr = 'Kartenzahlung (EC)' elif pay['type'] == 'gutschein': descr = 'Einlösung Gutschein' if pay['date'] != self.invoice.date: descr += ' am %s' % (pay['date'].strftime('%d. %m. %Y')) self.canvas.drawRightString(left + 14.5 * cm, self.y - self.font_height, descr + ':') self.canvas.drawRightString(left + 16.8 * cm, self.y - self.font_height, _formatPrice(pay['amount'])) self.y -= self.line_height sum = part.sum if part.vatType == 'net': sum = 0 for vat, vatdata in part.vat.items(): sum += (vat + 1) * vatdata[0] rest = sum - paysum if part.payments and rest > 0: self.canvas.setFont(self.font + '-Bold', font_size) self.canvas.drawRightString(left + 14.5 * cm, self.y - self.font_height, 'Offener Rechnungsbetrag:') self.canvas.drawRightString(left + 16.8 * cm, self.y - self.font_height, _formatPrice(rest)) self.canvas.setFont(self.font, font_size) self.y -= self.line_height + self.line_padding self.y -= self.line_padding elif isinstance(part, InvoiceText): my_font_size = font_size + part.fontsize # Relative Schriftgröße beachten self.canvas.setFont(self.font, my_font_size) left, right = self.leftcontent, self.rightcontent firsttime = True headlines = [] if part.urgent: left += 1.5 * cm right -= 1.5 * cm if part.headline: headlines = self._splitToWidth(part.headline, right - left, self.font, my_font_size) for para in part.paragraphs: lines = self._splitToWidth(para, right - left, self.font, my_font_size) if part.urgent: need_height = len(lines) * self._lineHeight(my_font_size) if len(headlines) > 0: need_height += len(headlines) * (self._lineHeight(my_font_size) + 1) + self.line_padding self.canvas.setFillColorRGB(0.95, 0.95, 0.95) self.canvas.rect(left - 0.5 * cm, self.y - (need_height + (6 * self.line_padding)), height=need_height + (6 * self.line_padding), width=right - left + 1 * cm, fill=1, stroke=1) self.canvas.setFillColorRGB(0, 0, 0) self.y -= self.line_padding * 3 if part.headline and firsttime: firsttime = False self.canvas.setFont(self.font + '-Bold', my_font_size + 1) for line in headlines: self.canvas.drawString(left, self.y - (self.font_height + 1), line) self.y -= self._lineHeight(my_font_size) + 1 self.y -= self.line_padding self.canvas.setFont(self.font, my_font_size) for line in lines: self.canvas.drawString(left, self.y - self.font_height, line) self.y -= self._lineHeight(my_font_size) self.y -= self.line_padding * 3 elif isinstance(part, InvoiceImage): width = (part.imagedata.width / part.dpi) * inch height = width * (part.imagedata.height / part.imagedata.width) x = self.leftcontent if part.alignment == "center": x = self.leftcontent + (self.rightcontent - self.leftcontent) / 2 - width / 2 elif part.alignment == "right": x = self.rightcontent - width self.canvas.drawInlineImage(part.imagedata, x, self.y - height, width=width, height=height) self.y -= self.line_padding + height if part.caption: self.canvas.drawString(x, self.y - self.font_height, part.caption) self.y -= self._lineHeight() else: raise NotImplementedError("Cannot handle part of type %s" % type(part)) self.y -= (0.5 * cm) self.canvas.showPage() self.canvas.save() pdfdata = self.fd.getvalue() return pdfdata def InvoiceToPDF(iv): pdf = PDF(iv) return pdf.renderRechnung()