diff --git a/appveyor.yml b/appveyor.yml index d54bd9ff6..c33b15d24 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -54,12 +54,6 @@ after_test: # This is where we create a package using PyInstaller # Install PyInstaller python -m pip install --no-warn-script-location pyinstaller - # Patch pyinstaller to fix a webengine bundle issue - Invoke-WebRequest -Uri "https://raw.githubusercontent.com/pyinstaller/pyinstaller/d08a42c612a91cc320c54f9e812fe967b9c8594a/PyInstaller/hooks/hook-PyQt5.QtWebEngineWidgets.py" -OutFile "hook-PyQt5.QtWebEngineWidgets.py" - Invoke-WebRequest -Uri "https://raw.githubusercontent.com/pyinstaller/pyinstaller/91481570517707fc70aa70dca9eb986c61eac35d/PyInstaller/hooks/hook-pkg_resources.py" -OutFile hook-pkg_resources.py - $sitePkgFolder = python -c "import sys; site_pkg_folder = next(p for p in sys.path if 'site-packages' in p); print(site_pkg_folder.replace('\\', '/'))" - cp hook-PyQt5.QtWebEngineWidgets.py "${sitePkgFolder}/PyInstaller/hooks/hook-PyQt5.QtWebEngineWidgets.py" - cp hook-pkg_resources.py "${sitePkgFolder}/PyInstaller/hooks/hook-pkg_resources.py" # Some windows only stuff... If ($isWindows) { # Disabled portable installers - can't figure out how to make them silent diff --git a/openlp/core/common/settings.py b/openlp/core/common/settings.py index ce84416cf..bca5166d5 100644 --- a/openlp/core/common/settings.py +++ b/openlp/core/common/settings.py @@ -280,8 +280,6 @@ class Settings(QtCore.QSettings): 'planningcenter/secret': '', 'presentations/status': PluginStatus.Inactive, 'presentations/override app': QtCore.Qt.Unchecked, - 'presentations/enable_pdf_program': QtCore.Qt.Unchecked, - 'presentations/pdf_program': None, 'presentations/maclo': QtCore.Qt.Checked, 'presentations/Impress': QtCore.Qt.Checked, 'presentations/Powerpoint': QtCore.Qt.Checked, @@ -406,7 +404,8 @@ class Settings(QtCore.QSettings): ('projector/last directory import', 'projector/last directory import', [(str_to_path, None)]), ('projector/last directory export', 'projector/last directory export', [(str_to_path, None)]), ('bibles/last directory import', 'bibles/last directory import', [(str_to_path, None)]), - ('presentations/pdf_program', 'presentations/pdf_program', [(str_to_path, None)]), + ('presentations/enable_pdf_program', '', []), + ('presentations/pdf_program', '', []), ('songs/last directory import', 'songs/last directory import', [(str_to_path, None)]), ('songs/last directory export', 'songs/last directory export', [(str_to_path, None)]), ('songusage/last directory export', 'songusage/last directory export', [(str_to_path, None)]), diff --git a/openlp/plugins/presentations/lib/messagelistener.py b/openlp/plugins/presentations/lib/messagelistener.py index a90d5e702..3f24d6d2c 100644 --- a/openlp/plugins/presentations/lib/messagelistener.py +++ b/openlp/plugins/presentations/lib/messagelistener.py @@ -340,7 +340,6 @@ class MessageListener(object): item.footer = item_cpy.footer item.from_service = item_cpy.from_service item.iconic_representation = item_cpy.icon - item.image_border = item_cpy.image_border item.main = item_cpy.main item.theme = item_cpy.theme # When presenting PDF/XPS/OXPS, we are using the image presentation code, diff --git a/openlp/plugins/presentations/lib/pdfcontroller.py b/openlp/plugins/presentations/lib/pdfcontroller.py index 0a54cb68c..87086e16f 100644 --- a/openlp/plugins/presentations/lib/pdfcontroller.py +++ b/openlp/plugins/presentations/lib/pdfcontroller.py @@ -19,13 +19,8 @@ # along with this program. If not, see . # ########################################################################## import logging -import re -from shutil import which -from subprocess import CalledProcessError, check_output -from openlp.core.common import check_binary_exists, is_win -from openlp.core.common.registry import Registry -from openlp.core.common.applocation import AppLocation +from openlp.core.common import is_win from openlp.core.display.screens import ScreenList from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument @@ -64,38 +59,6 @@ class PdfController(PresentationController): # Determine whether mudraw or ghostscript is used self.check_installed() - @staticmethod - def process_check_binary(program_path): - """ - Function that checks whether a binary is either ghostscript or mudraw or neither. - Is also used from presentationtab.py - - :param pathlib.Path program_path: The full path to the binary to check. - :return: Type of the binary, 'gs' if ghostscript, 'mudraw' if mudraw, None if invalid. - :rtype: str | None - """ - program_type = None - runlog = check_binary_exists(program_path) - # Analyse the output to see it the program is mudraw, ghostscript or neither - for line in runlog.splitlines(): - decoded_line = line.decode() - found_mudraw = re.search('usage: mudraw.*', decoded_line, re.IGNORECASE) - if found_mudraw: - program_type = 'mudraw' - break - found_mutool = re.search('usage: mutool.*', decoded_line, re.IGNORECASE) - if found_mutool: - # Test that mutool contains mudraw - if re.search(r'draw\s+--\s+convert document.*', runlog.decode(), re.IGNORECASE | re.MULTILINE): - program_type = 'mutool' - break - found_gs = re.search('GPL Ghostscript.*', decoded_line, re.IGNORECASE) - if found_gs: - program_type = 'gs' - break - log.debug('in check_binary, found: {text}'.format(text=program_type)) - return program_type - def check_available(self): """ PdfController is able to run on this machine. @@ -112,53 +75,10 @@ class PdfController(PresentationController): :return: True if program to open PDF-files was found, otherwise False. """ log.debug('check_installed Pdf') - self.mudrawbin = None - self.mutoolbin = None - self.gsbin = None self.also_supports = [] - # Use the user defined program if given - if Registry().get('settings').value('presentations/enable_pdf_program'): - program_path = Registry().get('settings').value('presentations/pdf_program') - program_type = self.process_check_binary(program_path) - if program_type == 'gs': - self.gsbin = program_path - elif program_type == 'mudraw': - self.mudrawbin = program_path - elif program_type == 'mutool': - self.mutoolbin = program_path - elif PYMUPDF_AVAILABLE: + if PYMUPDF_AVAILABLE: self.also_supports = ['xps', 'oxps', 'epub', 'cbz', 'fb2'] return True - else: - # Fallback to autodetection - application_path = AppLocation.get_directory(AppLocation.AppDir) - if is_win(): - # for windows we only accept mudraw.exe or mutool.exe in the base folder - if (application_path / 'mudraw.exe').is_file(): - self.mudrawbin = application_path / 'mudraw.exe' - elif (application_path / 'mutool.exe').is_file(): - self.mutoolbin = application_path / 'mutool.exe' - else: - # First try to find mudraw - self.mudrawbin = which('mudraw') - # if mudraw isn't installed, try mutool - if not self.mudrawbin: - self.mutoolbin = which('mutool') - # Check we got a working mutool - if not self.mutoolbin or self.process_check_binary(self.mutoolbin) != 'mutool': - self.gsbin = which('gs') - # Last option: check if mudraw or mutool is placed in OpenLP base folder - if not self.mudrawbin and not self.mutoolbin and not self.gsbin: - application_path = AppLocation.get_directory(AppLocation.AppDir) - if (application_path / 'mudraw').is_file(): - self.mudrawbin = application_path / 'mudraw' - elif (application_path / 'mutool').is_file(): - self.mutoolbin = application_path / 'mutool' - if self.mudrawbin or self.mutoolbin: - self.also_supports = ['xps', 'oxps', 'epub', 'cbz', 'fb2'] - return True - elif self.gsbin: - return True return False def kill(self): @@ -198,49 +118,6 @@ class PdfDocument(PresentationDocument): else: self.startupinfo = None - def gs_get_resolution(self, size): - """ - Only used when using ghostscript - Ghostscript can't scale automatically while keeping aspect like mupdf, so we need - to get the ratio between the screen size and the PDF to scale - - :param size: Size struct containing the screen size. - :return: The resolution dpi to be used. - """ - # Use a postscript script to get size of the pdf. It is assumed that all pages have same size - gs_resolution_script = AppLocation.get_directory( - AppLocation.PluginsDir) / 'presentations' / 'lib' / 'ghostscript_get_resolution.ps' - # Run the script on the pdf to get the size - runlog = [] - try: - runlog = check_output([str(self.controller.gsbin), '-dNOPAUSE', '-dNODISPLAY', '-dBATCH', - '-sFile={file_path}'.format(file_path=self.file_path), str(gs_resolution_script)], - startupinfo=self.startupinfo) - except CalledProcessError as e: - log.debug(' '.join(e.cmd)) - log.debug(e.output) - # Extract the pdf resolution from output, the format is " Size: x: , y: " - width = 0.0 - height = 0.0 - for line in runlog.splitlines(): - try: - width = float(re.search(r'.*Size: x: (\d+\.?\d*), y: \d+.*', line.decode()).group(1)) - height = float(re.search(r'.*Size: x: \d+\.?\d*, y: (\d+\.?\d*).*', line.decode()).group(1)) - break - except AttributeError: - continue - # Calculate the ratio from pdf to screen - if width > 0 and height > 0: - width_ratio = size.width() / width - height_ratio = size.height() / height - # return the resolution that should be used. 72 is default. - if width_ratio > height_ratio: - return int(height_ratio * 72) - else: - return int(width_ratio * 72) - else: - return 72 - def load_presentation(self): """ Called when a presentation is added to the SlideController. It generates images from the PDF. @@ -260,39 +137,18 @@ class PdfDocument(PresentationDocument): size = ScreenList().current.display_geometry # Generate images from PDF that will fit the frame. runlog = '' + if not temp_dir_path.is_dir(): + temp_dir_path.mkdir(parents=True) try: - if not temp_dir_path.is_dir(): - temp_dir_path.mkdir(parents=True) - # The %03d in the file name is handled by each binary - if self.controller.mudrawbin: - log.debug('loading presentation using mudraw') - runlog = check_output([str(self.controller.mudrawbin), '-w', str(size.width()), - '-h', str(size.height()), - '-o', str(temp_dir_path / 'mainslide%03d.png'), str(self.file_path)], - startupinfo=self.startupinfo) - elif self.controller.mutoolbin: - log.debug('loading presentation using mutool') - runlog = check_output([str(self.controller.mutoolbin), 'draw', '-w', str(size.width()), - '-h', str(size.height()), '-o', str(temp_dir_path / 'mainslide%03d.png'), - str(self.file_path)], - startupinfo=self.startupinfo) - elif self.controller.gsbin: - log.debug('loading presentation using gs') - resolution = self.gs_get_resolution(size) - runlog = check_output([str(self.controller.gsbin), '-dSAFER', '-dNOPAUSE', '-dBATCH', '-sDEVICE=png16m', - '-r{res}'.format(res=resolution), '-dTextAlphaBits=4', '-dGraphicsAlphaBits=4', - '-sOutputFile={output}'.format(output=temp_dir_path / 'mainslide%03d.png'), - str(self.file_path)], startupinfo=self.startupinfo) - elif PYMUPDF_AVAILABLE: - log.debug('loading presentation using PyMuPDF') - pdf = fitz.open(str(self.file_path)) - for i, page in enumerate(pdf, start=1): - src_size = page.bound().round() - # keep aspect ratio - scale = min(size.width() / src_size.width, size.height() / src_size.height) - m = fitz.Matrix(scale, scale) - page.getPixmap(m, alpha=False).writeImage(str(temp_dir_path / 'mainslide{:03d}.png'.format(i))) - pdf.close() + log.debug('loading presentation using PyMuPDF') + pdf = fitz.open(str(self.file_path)) + for i, page in enumerate(pdf, start=1): + src_size = page.bound().round() + # keep aspect ratio + scale = min(size.width() / src_size.width, size.height() / src_size.height) + m = fitz.Matrix(scale, scale) + page.getPixmap(m, alpha=False).writeImage(str(temp_dir_path / 'mainslide{:03d}.png'.format(i))) + pdf.close() created_files = sorted(temp_dir_path.glob('*')) for image_path in created_files: if image_path.is_file(): diff --git a/openlp/plugins/presentations/lib/presentationtab.py b/openlp/plugins/presentations/lib/presentationtab.py index 9c2e9ad62..833aa3d66 100644 --- a/openlp/plugins/presentations/lib/presentationtab.py +++ b/openlp/plugins/presentations/lib/presentationtab.py @@ -23,9 +23,6 @@ from PyQt5 import QtWidgets from openlp.core.common.i18n import UiStrings, translate from openlp.core.lib.settingstab import SettingsTab -from openlp.core.lib.ui import critical_error_message_box -from openlp.core.widgets.edits import PathEdit -from openlp.plugins.presentations.lib.pdfcontroller import PdfController class PresentationTab(SettingsTab): @@ -79,23 +76,10 @@ class PresentationTab(SettingsTab): self.ppt_window_check_box.setObjectName('ppt_window_check_box') self.powerpoint_layout.addWidget(self.ppt_window_check_box) self.left_layout.addWidget(self.powerpoint_group_box) - # Pdf options - self.pdf_group_box = QtWidgets.QGroupBox(self.left_column) - self.pdf_group_box.setObjectName('pdf_group_box') - self.pdf_layout = QtWidgets.QFormLayout(self.pdf_group_box) - self.pdf_layout.setObjectName('pdf_layout') - self.pdf_program_check_box = QtWidgets.QCheckBox(self.pdf_group_box) - self.pdf_program_check_box.setObjectName('pdf_program_check_box') - self.pdf_layout.addRow(self.pdf_program_check_box) - self.program_path_edit = PathEdit(self.pdf_group_box) - self.pdf_layout.addRow(self.program_path_edit) - self.left_layout.addWidget(self.pdf_group_box) + # setup layout self.left_layout.addStretch() self.right_column.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) self.right_layout.addStretch() - # Signals and slots - self.program_path_edit.pathChanged.connect(self.on_program_path_edit_path_changed) - self.pdf_program_check_box.clicked.connect(self.program_path_edit.setEnabled) def retranslate_ui(self): """ @@ -107,7 +91,6 @@ class PresentationTab(SettingsTab): checkbox = self.presenter_check_boxes[controller.name] self.set_controller_text(checkbox, controller) self.advanced_group_box.setTitle(UiStrings().Advanced) - self.pdf_group_box.setTitle(translate('PresentationPlugin.PresentationTab', 'PDF options')) self.powerpoint_group_box.setTitle(translate('PresentationPlugin.PresentationTab', 'PowerPoint options')) self.override_app_check_box.setText( translate('PresentationPlugin.PresentationTab', 'Allow presentation application to be overridden')) @@ -118,10 +101,6 @@ class PresentationTab(SettingsTab): translate('PresentationPlugin.PresentationTab', 'Let PowerPoint control the size and monitor of the presentations\n' '(This may fix PowerPoint scaling issues in Windows 8 and 10)')) - self.pdf_program_check_box.setText( - translate('PresentationPlugin.PresentationTab', 'Use given full path for mudraw or ghostscript binary:')) - self.program_path_edit.dialog_caption = translate('PresentationPlugin.PresentationTab', - 'Select mudraw or ghostscript binary') def set_controller_text(self, checkbox, controller): if checkbox.isEnabled(): @@ -147,11 +126,6 @@ class PresentationTab(SettingsTab): self.ppt_slide_click_check_box.setEnabled(powerpoint_available) self.ppt_window_check_box.setChecked(self.settings.value('presentations/powerpoint control window')) self.ppt_window_check_box.setEnabled(powerpoint_available) - # load pdf-program settings - enable_pdf_program = self.settings.value('presentations/enable_pdf_program') - self.pdf_program_check_box.setChecked(enable_pdf_program) - self.program_path_edit.setEnabled(enable_pdf_program) - self.program_path_edit.path = self.settings.value('presentations/pdf_program') def save(self): """ @@ -186,18 +160,6 @@ class PresentationTab(SettingsTab): if self.settings.value(setting_key) != self.ppt_window_check_box.checkState(): self.settings.setValue(setting_key, self.ppt_window_check_box.checkState()) changed = True - # Save pdf-settings - pdf_program_path = self.program_path_edit.path - enable_pdf_program = self.pdf_program_check_box.checkState() - # If the given program is blank disable using the program - if pdf_program_path is None: - enable_pdf_program = 0 - if pdf_program_path != self.settings.value('presentations/pdf_program'): - self.settings.setValue('presentations/pdf_program', pdf_program_path) - changed = True - if enable_pdf_program != self.settings.value('presentations/enable_pdf_program'): - self.settings.setValue('presentations/enable_pdf_program', enable_pdf_program) - changed = True if changed: self.settings_form.register_post_process('mediaitem_suffix_reset') self.settings_form.register_post_process('mediaitem_presentation_rebuild') @@ -213,16 +175,3 @@ class PresentationTab(SettingsTab): checkbox = self.presenter_check_boxes[controller.name] checkbox.setEnabled(controller.is_available()) self.set_controller_text(checkbox, controller) - - def on_program_path_edit_path_changed(self, new_path): - """ - Handle the `pathEditChanged` signal from program_path_edit - - :param pathlib.Path new_path: File path to the new program - :rtype: None - """ - if new_path: - if not PdfController.process_check_binary(new_path): - critical_error_message_box(UiStrings().Error, - translate('PresentationPlugin.PresentationTab', - 'The program is not ghostscript or mudraw which is required.')) diff --git a/tests/functional/openlp_plugins/presentations/test_pdfcontroller.py b/tests/functional/openlp_plugins/presentations/test_pdfcontroller.py index 9cf68316d..bf84b5751 100644 --- a/tests/functional/openlp_plugins/presentations/test_pdfcontroller.py +++ b/tests/functional/openlp_plugins/presentations/test_pdfcontroller.py @@ -24,10 +24,10 @@ This module contains tests for the PdfController import os import pytest from pathlib import Path -from shutil import rmtree, which +from shutil import rmtree from tempfile import mkdtemp from unittest import skipIf -from unittest.mock import MagicMock, patch +from unittest.mock import MagicMock from PyQt5 import QtCore, QtGui @@ -96,7 +96,7 @@ def test_constructor(settings, mock_plugin): assert 'Pdf' == controller.name, 'The name of the presentation controller should be correct' -def load_pdf(exe_path, pdf_env): +def load_pdf(pdf_env): """ Test loading a Pdf using the PdfController """ @@ -104,11 +104,9 @@ def load_pdf(exe_path, pdf_env): test_file_path = RESOURCE_PATH / 'presentations' / 'pdf_test1.pdf' # WHEN: The Pdf is loaded - settings = pdf_env[0] mock_plugin = pdf_env[1] temp_folder_path = pdf_env[2] thumbnail_folder_path = pdf_env[3] - settings.setValue('presentations/pdf_program', exe_path) controller = PdfController(plugin=mock_plugin) controller.temp_folder = temp_folder_path controller.thumbnail_folder = thumbnail_folder_path @@ -121,7 +119,7 @@ def load_pdf(exe_path, pdf_env): @skipIf(IS_QT_QPA_PLATFORM_OFFSCREEN, 'This test fails when QT_QPA_PLATFORM is set to "offscreen".') -def load_pdf_pictures(exe_path, pdf_env): +def load_pdf_pictures(pdf_env): """ Test loading a Pdf and check the generated pictures' size """ @@ -129,11 +127,9 @@ def load_pdf_pictures(exe_path, pdf_env): test_file_path = RESOURCE_PATH / 'presentations' / 'pdf_test1.pdf' # WHEN: The Pdf is loaded - settings = pdf_env[0] mock_plugin = pdf_env[1] temp_folder_path = pdf_env[2] thumbnail_folder_path = pdf_env[3] - settings.setValue('presentations/pdf_program', exe_path) controller = PdfController(plugin=mock_plugin) controller.temp_folder = temp_folder_path controller.thumbnail_folder = thumbnail_folder_path @@ -144,26 +140,11 @@ def load_pdf_pictures(exe_path, pdf_env): assert loaded is True, 'The loading of the PDF should succeed.' image = QtGui.QImage(os.path.join(str(temp_folder_path), 'pdf_test1.pdf', 'mainslide001.png')) # Based on the converter used the resolution will differ a bit - if controller.gsbin: - assert 1076 == image.height(), 'The height should be 1076' - assert 760 == image.width(), 'The width should be 760' - else: - width, height = get_screen_resolution() - # Calculate the width of the PDF based on the aspect ratio of the PDF - width = int(round(height * 0.70703125, 0)) - assert image.height() == height, 'The height should be {height}'.format(height=height) - assert image.width() == width, 'The width should be {width}'.format(width=width) - - -def test_load_pdf(pdf_env): - """ - Test loading a Pdf with each of the installed backends - """ - for exe_name in ['gs', 'mutool', 'mudraw']: - exe_path = which(exe_name) - if exe_path: - load_pdf(exe_path, pdf_env) - load_pdf_pictures(exe_path, pdf_env) + width, height = get_screen_resolution() + # Calculate the width of the PDF based on the aspect ratio of the PDF + width = int(round(height * 0.70703125, 0)) + assert image.height() == height, 'The height should be {height}'.format(height=height) + assert image.width() == width, 'The width should be {width}'.format(width=width) def test_loading_pdf_using_pymupdf(pdf_env): @@ -172,80 +153,5 @@ def test_loading_pdf_using_pymupdf(pdf_env): except ImportError: pytest.skip('PyMuPDF is not installed') - load_pdf(None, pdf_env) - load_pdf_pictures(None, pdf_env) - - -@patch('openlp.plugins.presentations.lib.pdfcontroller.check_binary_exists') -def test_process_check_binary_mudraw(mocked_check_binary_exists): - """ - Test that the correct output from mudraw is detected - """ - # GIVEN: A mocked check_binary_exists that returns mudraw output - mudraw_output = (b'usage: mudraw [options] input [pages]\n\t-o -\toutput filename (%d for page number)n\t\tsupp' - b'orted formats: pgm, ppm, pam, png, pbmn\t-p -\tpasswordn\t-r -\tresolution in dpi (default: ' - b'72)n\t-w -\twidth (in pixels) (maximum width if -r is specified)n\t-h -\theight (in pixels) ' - b'(maximum height if -r is specified)') - mocked_check_binary_exists.return_value = mudraw_output - - # WHEN: Calling process_check_binary - ret = PdfController.process_check_binary('test') - - # THEN: mudraw should be detected - assert 'mudraw' == ret, 'mudraw should have been detected' - - -@patch('openlp.plugins.presentations.lib.pdfcontroller.check_binary_exists') -def test_process_check_binary_new_motool(mocked_check_binary_exists): - """ - Test that the correct output from the new mutool is detected - """ - # GIVEN: A mocked check_binary_exists that returns new mutool output - new_mutool_output = (b'usage: mutool [options]\n\tdraw\t-- convert document\n\trun\t-- run javascript' - b'\n\tclean\t-- rewrite pdf file\n\textract\t-- extract font and image resources\n\tinfo\t' - b'-- show information about pdf resources\n\tpages\t-- show information about pdf pages\n' - b'\tposter\t-- split large page into many tiles\n\tshow\t-- show internal pdf objects\n\t' - b'create\t-- create pdf document\n\tmerge\t-- merge pages from multiple pdf sources into a' - b'new pdf\n') - mocked_check_binary_exists.return_value = new_mutool_output - - # WHEN: Calling process_check_binary - ret = PdfController.process_check_binary('test') - - # THEN: mutool should be detected - assert 'mutool' == ret, 'mutool should have been detected' - - -@patch('openlp.plugins.presentations.lib.pdfcontroller.check_binary_exists') -def test_process_check_binary_old_motool(mocked_check_binary_exists): - """ - Test that the output from the old mutool is not accepted - """ - # GIVEN: A mocked check_binary_exists that returns old mutool output - old_mutool_output = (b'usage: mutool [options]\n\tclean\t-- rewrite pdf file\n\textract\t-- extract ' - b'font and image resources\n\tinfo\t-- show information about pdf resources\n\tposter\t-- ' - b'split large page into many tiles\n\tshow\t-- show internal pdf objects') - mocked_check_binary_exists.return_value = old_mutool_output - - # WHEN: Calling process_check_binary - ret = PdfController.process_check_binary('test') - - # THEN: mutool should be detected - assert ret is None, 'old mutool should not be accepted!' - - -@patch('openlp.plugins.presentations.lib.pdfcontroller.check_binary_exists') -def test_process_check_binary_gs(mocked_check_binary_exists): - """ - Test that the correct output from gs is detected - """ - # GIVEN: A mocked check_binary_exists that returns gs output - gs_output = (b'GPL Ghostscript 9.19 (2016-03-23)\nCopyright (C) 2016 Artifex Software, Inc. All rights reserv' - b'ed.\nUsage: gs [switches] [file1.ps file2.ps ...]') - mocked_check_binary_exists.return_value = gs_output - - # WHEN: Calling process_check_binary - ret = PdfController.process_check_binary('test') - - # THEN: mutool should be detected - assert 'gs' == ret, 'mutool should have been detected' + load_pdf(pdf_env) + load_pdf_pictures(pdf_env)