Source code for PDFReport.report

import os
import platform
import subprocess
import json

from .globals import VAlign
from .pageformat import PageFormat
from .rect import Rect
from .globals import get_rect_with_size_and_align
from .renderer import Renderer
from .serialframe import SerialFrame
from .boxframe import BoxFrame
from .tableframe import TableFrame
from .tablecolumn import TableColumn
from .tablerow import TableRow
from .imageframe import ImageFrame
from .breakframe import BreakFrame
from .pageframe import PageFrame
from .positionframe import PositionFrame
from .barcodeframe import BarcodeFrame
from .lineframe import LineFrame
from .textframe import TextFrame
from .textstyle import TextStyle
from .textstyles import TextStyles
from .reportdata import ReportData


[docs] class Report: """ This is the main class for reports in the PDFReport It creates some basic frames and prints them to a PDF document """
[docs] def __init__(self, page_format: PageFormat = None, font_family: str = "Helvetica", font_size: float = 9.0, text_color: str = "#000000"): """ Creates a new Report object. If no page format is passed a default page format will be used :param page_format: PageFormat to be used for the report or None """ if page_format is None: page_format = PageFormat() self._page_format = page_format self._body = SerialFrame() self._body.frame_id = "b" self._header = SerialFrame() self._header.v_align = VAlign.TOP self._header.frame_id = "h" self._footer = SerialFrame() self._footer.v_align = VAlign.BOTTOM self._footer.frame_id = "f" self._header_max_height = 0.0 self._footer_max_height = 0.0 self._count_pages = False # Init text styles TextStyles.set_default(font_family, font_size, text_color)
@property def page_format(self) -> PageFormat: """ The page format to be used in the report :getter: Returns the page format :setter: Sets the page format """ return self._page_format @page_format.setter def page_format(self, page_format: PageFormat): self._page_format = page_format @property def header(self) -> SerialFrame: """ The header frame :getter: Returns the header frame (vertical serial frame) """ return self._header @property def footer(self) -> SerialFrame: """ The footer frame :getter: Returns the footer frame (vertical serial frame) """ return self._footer @property def body(self) -> SerialFrame: """ The body frame :getter: Returns the body frame (vertical serial frame) """ return self._body @property def count_pages(self) -> bool: """ Flag if the pages should be counted before the printing That is used if the variable for the number of pages is used :getter: Returns True if the pages will be counted :setter: Sets the count pages flag """ return self._count_pages @count_pages.setter def count_pages(self, count_pages: bool): self._count_pages = count_pages
[docs] def output(self, rep_file_name: str, show: bool = False, report_data: ReportData = None): """ Creates the pdf file from the report structure :param rep_file_name: The full filename for the report without extension :param show: If True the created pdf will be shown (if possible) :param report_data: Data provider to handle callbacks to get dynamic data """ if self._is_endless(): raise OverflowError("Endless recursion loop in the report structure.") renderer = Renderer(self._page_format, report_data) self._print_report(renderer, rep_file_name + ".pdf") if show: self.show(rep_file_name + ".pdf")
[docs] def save(self, rep_file_name: str, show: bool = False): """ Save the report in a json file. :param rep_file_name: The full filename for the report without extension :param show: If True the created json file will be shown (if possible) """ if self._is_endless(): raise OverflowError("Endless recursion loop in the report structure.") data = {} report = {} report["class"] = "Report" report["count_pages"] = self._count_pages report["page_format"] = self._page_format.to_dict() data["r"] = report frame = {} self._header.to_dict(data, frame) frame = {} self._footer.to_dict(data, frame) frame = {} self._body.to_dict(data, frame) with open(rep_file_name + ".json", "w") as fp: json.dump(data, fp, sort_keys=False, indent=4) fp.close() if show: self.show(rep_file_name + ".json")
[docs] def load(self, rep_file_name: str): """ Loads a report from a json file. :param rep_file_name: The full filename for the report without extension """ with open(rep_file_name + ".json", "r") as fp: data = json.load(fp) fp.close() frames = {} for frame_id, frame in data.items(): parent = None if "parent_id" in frame: parent = frames[frame["parent_id"]] if frame["class"] == "SerialFrame": if frame_id == "h": frames[frame_id] = self._header elif frame_id == "f": frames[frame_id] = self._footer elif frame_id == "b": frames[frame_id] = self._body else: frames[frame_id] = SerialFrame(parent, frame_id=frame_id) frames[frame_id].from_dict(frame) elif frame["class"] == "BarcodeFrame": frames[frame_id] = BarcodeFrame(parent, frame_id=frame_id) frames[frame_id].from_dict(frame) elif frame["class"] == "LineFrame": frames[frame_id] = LineFrame(parent, frame_id=frame_id) frames[frame_id].from_dict(frame) elif frame["class"] == "TextFrame": frames[frame_id] = TextFrame(parent, frame_id=frame_id) frames[frame_id].from_dict(frame) elif frame["class"] == "BoxFrame": frames[frame_id] = BoxFrame(parent, frame_id=frame_id) frames[frame_id].from_dict(frame) elif frame["class"] == "ImageFrame": frames[frame_id] = ImageFrame(parent, frame_id=frame_id) frames[frame_id].from_dict(frame) elif frame["class"] == "BreakFrame": frames[frame_id] = BreakFrame(parent, frame_id=frame_id) frames[frame_id].from_dict(frame) elif frame["class"] == "PageFrame": frames[frame_id] = PageFrame(parent, frame_id=frame_id) frames[frame_id].from_dict(frame) elif frame["class"] == "PositionFrame": frames[frame_id] = PositionFrame(parent, frame_id=frame_id) frames[frame_id].from_dict(frame) elif frame["class"] == "TableFrame": frames[frame_id] = TableFrame(parent, frame_id=frame_id) frames[frame_id].from_dict(frame) elif frame["class"] == "TableColumn": tc = TableColumn(parent) tc.from_dict(frame) elif frame["class"] == "TableRow": tr = TableRow(parent) tr.from_dict(frame) elif frame["class"] == "Report": self._count_pages = frame["count_pages"] self._page_format = PageFormat() self._page_format.from_dict(frame["page_format"])
def _is_endless(self) -> bool: frames = [] if self._header.is_endless(frames): return True if self._footer.is_endless(frames): return True return self._body.is_endless(frames) def _print_report(self, r: Renderer, rep_file_name: str) -> int: self._on_begin_print(r) while self._on_print_page(r): pass self._on_end_print(r, rep_file_name) return r.current_page def _on_begin_print(self, r: Renderer): r.create_new_pdf() r.pages_counted = False self._reset() def _reset(self): self._body.reset() self._header.reset() self._footer.reset() def _print_a_page(self, r: Renderer) -> bool: page_bounds = r.get_page_bounds() if self._header.get_frame_count() > 0: header_bounds = Rect(other=page_bounds) if self._header_max_height > 0: header_bounds.bottom = header_bounds.top + self._header_max_height self._header.print(r, header_bounds) self._header.reset() page_bounds.top += self._header.get_size().height if self._footer.get_frame_count() > 0: footer_bounds = Rect(other=page_bounds) if self._footer_max_height > 0: footer_bounds.top = footer_bounds.bottom + self._footer_max_height self._footer.calc_size(r, footer_bounds) footer_bounds = get_rect_with_size_and_align(footer_bounds, self._footer.get_size(), self._footer.h_align, self._footer.v_align) self._footer.print(r, footer_bounds) self._footer.reset() page_bounds.bottom -= self._footer.get_size().height if self._body.get_frame_count() > 0: self._body.print(r, page_bounds) has_more_pages = self._body.continued else: has_more_pages = False return has_more_pages def _do_count_pages(self, r: Renderer) -> int: if not self._count_pages: return 0 if not r.pages_counted: while self._print_a_page(r): r.add_page() r.pages_counted = True r.create_new_pdf() self._reset() return r.total_pages def _on_print_page(self, r: Renderer) -> bool: if self._count_pages and not r.pages_counted: self._do_count_pages(r) has_more_pages = self._print_a_page(r) if has_more_pages: r.add_page() return has_more_pages def _on_end_print(self, r: Renderer, rep_file_name: str): if len(rep_file_name) > 0: r.output(rep_file_name) self._reset() @staticmethod def show(rep_file_name: str): if platform.system() == 'Darwin': subprocess.call(('open', rep_file_name)) elif platform.system() == 'Windows': os.startfile(rep_file_name) else: subprocess.call(('xdg-open', rep_file_name))