diff --git a/md_pdf/build/pdf.py b/md_pdf/build/pdf.py index 1802710..7ad086b 100644 --- a/md_pdf/build/pdf.py +++ b/md_pdf/build/pdf.py @@ -13,8 +13,8 @@ DEFAULT_MARGIN_VERTICAL = '1.5cm' DEFAULT_MARGIN_HORIZONTAL = '2.5cm' STYLE_FILE = os.path.join(STATIC_DIR, 'style.css') -HEADER_FILE = os.path.join(TEMPLATES_DIR, 'header.html') -FOOTER_FILE = os.path.join(TEMPLATES_DIR, 'footer.html') +HEADER_FILE = FILE_NAME_FORMAT.format('header') +FOOTER_FILE = FILE_NAME_FORMAT.format('footer') TOC_OPTIONS = { 'xsl-style-sheet': os.path.join(TEMPLATES_DIR, 'toc.xsl') @@ -37,23 +37,25 @@ def export_pdf(content, config): if logger.getEffectiveLevel() > logging.DEBUG: PDF_OPTIONS['quiet'] = "" PDF_OPTIONS['title'] = config.get('title', 'Output') - PDF_OPTIONS['replace'] = [(key, str(value)) for key, value in config['context'].items()] + context = config.get('context', {}) - PDF_OPTIONS['margin-top'] = config['context'].get('margin_vertical', DEFAULT_MARGIN_VERTICAL) - PDF_OPTIONS['margin-bottom'] = config['context'].get('margin_vertical', DEFAULT_MARGIN_VERTICAL) - PDF_OPTIONS['margin-left'] = config['context'].get('margin_horizontal', DEFAULT_MARGIN_HORIZONTAL) - PDF_OPTIONS['margin-right'] = config['context'].get('margin_horizontal', DEFAULT_MARGIN_HORIZONTAL) + PDF_OPTIONS['replace'] = [(key, str(value)) for key, value in context.items()] + PDF_OPTIONS['margin-top'] = context.get('margin_vertical', DEFAULT_MARGIN_VERTICAL) + PDF_OPTIONS['margin-bottom'] = context.get('margin_vertical', DEFAULT_MARGIN_VERTICAL) + PDF_OPTIONS['margin-left'] = context.get('margin_horizontal', DEFAULT_MARGIN_HORIZONTAL) + PDF_OPTIONS['margin-right'] = context.get('margin_horizontal', DEFAULT_MARGIN_HORIZONTAL) logger.info("Rendering PDF...") - render_ok = pdfkit.from_string( - content, - os.path.join(os.path.abspath(config['output_dir']), 'output.pdf'), - options=PDF_OPTIONS, - cover=FILE_NAME_FORMAT.format('cover'), - toc=TOC_OPTIONS if config.get('toc') else {}, - cover_first=True - ) - if not render_ok: - raise PDFRenderException('Failed to render PDF. ' + render_ok) + try: + pdfkit.from_string( + content, + os.path.join(os.path.abspath(config['output_dir']), 'output.pdf'), + options=PDF_OPTIONS, + cover=FILE_NAME_FORMAT.format('cover'), + toc=TOC_OPTIONS if config.get('toc') else {}, + cover_first=True + ) + except OSError as e: + raise PDFRenderException('Failed to render PDF. ' + str(e)) return PDF_OPTIONS # mostly for testing diff --git a/tests/__init__.py b/tests/__init__.py index 14e6b78..fe1f9b2 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,6 +1,7 @@ import unittest import os from md_pdf.consts import TEMPLATES_DIR, STATIC_DIR +from md_pdf.build.templates import FILE_NAME_FORMAT from bs4 import BeautifulSoup @@ -26,6 +27,17 @@ class BaseTestCase(unittest.TestCase): except OSError: pass + def touch_file(self, file): + open(file, 'w').close() + + def create_fake_templates(self): + for template in [ + 'header', + 'footer', + 'cover' + ]: + self.touch_file(FILE_NAME_FORMAT.format(template)) + def extend_config(self, *args): base_config = self.BASE_VALID_CONFIG.copy() for arg in args: @@ -45,3 +57,8 @@ class BaseTestCase(unittest.TestCase): self.delete_templates() self.remove_file(os.path.join(STATIC_DIR, 'style.css')) + def call_to_args(self, call): + args = tuple(call.call_args)[0] + kwargs = tuple(call.call_args)[1] + return args, kwargs + diff --git a/tests/test_pdf.py b/tests/test_pdf.py new file mode 100644 index 0000000..43199e5 --- /dev/null +++ b/tests/test_pdf.py @@ -0,0 +1,90 @@ +from tests import BaseTestCase +import os +from md_pdf.build.pdf import export_pdf, TOC_OPTIONS, DEFAULT_MARGIN_VERTICAL, DEFAULT_MARGIN_HORIZONTAL +from unittest.mock import patch +from md_pdf.build.templates import FILE_NAME_FORMAT +from md_pdf.exceptions import PDFRenderException +import pdfkit + + +class PDFRendererTestCase(BaseTestCase): + def setUp(self): + super().setUp() + self.content = 'test content' + self.output_file_path = os.path.join(self.BASE_VALID_CONFIG['output_dir'], 'output.pdf') + self.assertFalse(os.path.isfile(self.output_file_path)) + self.create_fake_templates() + + def tearDown(self): + super().tearDown() + self.remove_file(self.output_file_path) + + def test_renders(self): + export_pdf(self.content, self.BASE_VALID_CONFIG) + + def test_title(self): + context = export_pdf(self.content, self.BASE_VALID_CONFIG) + self.assertEqual(context['title'], self.BASE_VALID_CONFIG['title']) + + def test_replace_context(self): + self.BASE_VALID_CONFIG['context'] = { + '1': 2, + '2': '1' + } + context = export_pdf(self.content, self.BASE_VALID_CONFIG) + self.assertEqual(context['replace'], [ + ('1', '2'), + ('2', '1'), + ]) + + def test_default_margins(self): + context = export_pdf(self.content, self.BASE_VALID_CONFIG) + self.assertEqual(context['margin-top'], DEFAULT_MARGIN_VERTICAL) + self.assertEqual(context['margin-bottom'], DEFAULT_MARGIN_VERTICAL) + self.assertEqual(context['margin-left'], DEFAULT_MARGIN_HORIZONTAL) + self.assertEqual(context['margin-right'], DEFAULT_MARGIN_HORIZONTAL) + + def test_override_margin(self): + self.BASE_VALID_CONFIG['context'] = { + 'margin_vertical': '1cm', + 'margin_horizontal': '2cm' + } + context = export_pdf(self.content, self.BASE_VALID_CONFIG) + self.assertEqual(context['margin-top'], '1cm') + self.assertEqual(context['margin-bottom'], '1cm') + self.assertEqual(context['margin-left'], '2cm') + self.assertEqual(context['margin-right'], '2cm') + + @patch.object(pdfkit, 'from_string') + def test_kit_call(self, pdf_render): + context = export_pdf(self.content, self.BASE_VALID_CONFIG) + self.assertTrue(pdf_render.called) + args, kwargs = self.call_to_args(pdf_render) + self.assertEqual(args[0], self.content) + self.assertIn(self.output_file_path, args[1]) + self.assertEqual(kwargs['options'], context) + self.assertTrue(kwargs['cover_first']) + self.assertEqual(kwargs['cover'], FILE_NAME_FORMAT.format('cover')) + self.assertEqual(kwargs['toc'], {}) + + @patch.object(pdfkit, 'from_string') + def test_toc(self, pdf_render): + self.BASE_VALID_CONFIG['toc'] = True + export_pdf(self.content, self.BASE_VALID_CONFIG) + args, kwargs = self.call_to_args(pdf_render) + self.assertEqual(kwargs['toc'], TOC_OPTIONS) + + def test_fails_if_missing_templates(self): + self.remove_file(FILE_NAME_FORMAT.format('cover')) + with self.assertRaises(PDFRenderException): + export_pdf(self.content, self.BASE_VALID_CONFIG) + + def test_files_exist(self): + context = export_pdf(self.content, self.BASE_VALID_CONFIG) + self.assertTrue(os.path.isfile(context['header-html'])) + self.assertTrue(os.path.isfile(context['footer-html'])) + self.assertEqual(context['header-html'], FILE_NAME_FORMAT.format('header')) + self.assertEqual(context['footer-html'], FILE_NAME_FORMAT.format('footer')) + + +