Source code for qf_lib.documents_utils.document_exporting.pdf_exporter

#     Copyright 2016-present CERN – European Organization for Nuclear Research
#
#     Licensed under the Apache License, Version 2.0 (the "License");
#     you may not use this file except in compliance with the License.
#     You may obtain a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#     Unless required by applicable law or agreed to in writing, software
#     distributed under the License is distributed on an "AS IS" BASIS,
#     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#     See the License for the specific language governing permissions and
#     limitations under the License.

import os
from os.path import join, abspath, dirname
from typing import List

from weasyprint import HTML, CSS

from qf_lib.common.utils.logging.qf_parent_logger import qf_logger
from qf_lib.documents_utils.document_exporting.document import Document
from qf_lib.documents_utils.document_exporting.document_exporter import DocumentExporter
from qf_lib.settings import Settings
from qf_lib.starting_dir import get_starting_dir_abs_path


[docs]class PDFExporter(DocumentExporter): """ Stores elements such as the ParagraphElement and ChartElement in order to build a PDF based on them once they have all been added. If there is a "document_css_directory" attribute set in the Settings, then CSS files from that directory will be applied for styling the output page. Otherwise the default styling will be applied. """ DEFAULT_CSS_DIR_NAME = 'default_css' def __init__(self, settings: Settings): super().__init__(settings) if hasattr(settings, 'document_css_directory'): self._document_css_dir = join(get_starting_dir_abs_path(), settings.document_css_directory) else: this_dir_abs_path = abspath(dirname(__file__)) self._document_css_dir = join(this_dir_abs_path, self.DEFAULT_CSS_DIR_NAME) self.logger = qf_logger.getChild(self.__class__.__name__)
[docs] def set_default_directory_level_up(self): """ Sets the document_css_dir one level above 'default css', to enable applying css classes in other folders. Using the generate function demands inputting css_file_names as paths from newly set level. e.g: 'default_css\main" """ self._document_css_dir = abspath(dirname(__file__))
[docs] def generate(self, documents: List[Document], export_dir: str, filename: str, include_table_of_contents=False, css_file_names: List[str] = None) -> str: """ Merged all documents into one and then exports the merged document to a PDF file in the given directory. Allows defining of multiple css files. The base css file will be applied first, followed sequentially by files defined in css_file_names. The CSS files must be placed in the Settings.document_css_directory directory. CSS files placed in Settings.document_css_directory/base will be applied for all exported PDF documents. Parameters ---------- documents list of documents for which files should be generated export_dir relative path to the directory (relative to the output root directory) in which the PDF should be saved filename filename under which the merged document should be saved include_table_of_contents if True then table of contents will be generated at the beginning of the file css_file_names names of css files which should be applied for generating the PDF Returns ------- the absolute path to the output PDF file that was saved """ css_file_paths = [] documents = [self._merge_documents(documents, filename)] # Find the output directory output_dir = self.get_output_dir(export_dir) output_filename = os.path.join(output_dir, filename) for document in documents: if include_table_of_contents: self._add_table_of_contents(document) # Generate the full document HTML self.logger.info("Generating HTML for PDF...") html = document.generate_html() # Automatically include all the css files in the `document_css/base` directory base_css = os.listdir(self._document_css_dir) for name in base_css: path = os.path.join(self._document_css_dir, name) if os.path.isfile(path): css_file_paths.append(CSS(path)) # If we've set custom css files, add them to the pdf if css_file_names is not None: for name in css_file_names: css_file_paths.append(CSS(os.path.join(self._document_css_dir, name + ".css"))) # Parse the HTML. html = HTML(string=html) # Write out the PDF. self.logger.info("Rendering PDF in {}...".format(output_filename)) html.write_pdf(output_filename, css_file_paths) return output_filename