diff --git a/.travis.yml b/.travis.yml index 66313556..5a9ad79a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,7 @@ addons: - libavcodec56 - libavformat56 - libavutil54 + - libexempi3 - libffi-dev - pngquant - poppler-utils diff --git a/.travis/Brewfile b/.travis/Brewfile index b9699264..d12ae523 100644 --- a/.travis/Brewfile +++ b/.travis/Brewfile @@ -4,6 +4,7 @@ brew 'ghostscript' brew 'jbig2dec' brew 'jbig2enc' brew 'leptonica' +brew 'exempi' brew 'libffi' brew 'libtiff' brew 'libxml2' diff --git a/docs/release_notes.rst b/docs/release_notes.rst index 1cf89d91..8cd748fa 100644 --- a/docs/release_notes.rst +++ b/docs/release_notes.rst @@ -15,7 +15,7 @@ v7 - The core algorithm for combining OCR layers with existing PDF pages has been rewritten and improved considerably. The new algorithm uses less temporary disk space and eliminates object duplication and deduplication that was required when processing certain PDFs. -- New dependency: `pikepdf `_. pikepdf is a powerful new Python PDF library driving the latest OCRmyPDF features, built on the proven and mature libqpdf C++ library. +- New dependency: `pikepdf `_. pikepdf is a powerful new Python PDF library driving the latest OCRmyPDF features, built on QPDF's proven and mature libqpdf C++ library. - New feature: PDF optimization with ``-O`` or ``--optimize``. After OCR, OCRmyPDF will perform image optimizations relevant to OCR PDFs. @@ -37,6 +37,12 @@ v7 + ``-g``, the option to generate debug text pages, was removed because it was a maintenance burden and only worked in isolated cases. HOCR pages can still be previewed by running the hocrtransform.py with appropriate settings. +- Removed dependencies + + + ``PyPDF2`` is no longer used except in the test suite. + + + ``defusedxml`` is no longer used anywhere. + - The ``sandwich`` PDF renderer can be used with all supported versions of Tesseract, including that those priority to v3.05 which don't support ``-c textonly`` - ``--pdf-renderer auto`` option and the diagnostics used to select a PDF renderer now work better with old versions, but may make different decisions than past versions. diff --git a/setup.py b/setup.py index 28cfe652..cd89237d 100644 --- a/setup.py +++ b/setup.py @@ -247,13 +247,12 @@ setup( ], install_requires=[ 'cffi >= 1.9.1', # must be a setup and install requirement - 'defusedxml >= 0.5.0', # pure Python, so track HEAD closely 'img2pdf >= 0.2.4', # pure Python, so track HEAD closely 'pikepdf', 'Pillow >= 4.0.0, != 5.1.0 ; sys_platform == "darwin"', # Pillow < 4 has BytesIO/TIFF bug w/img2pdf 0.2.3 # block 5.1.0, broken wheels - 'PyPDF2 >= 1.26', # pure Python, so track HEAD closely + 'python-xmp-toolkit >= 2, < 3', 'reportlab >= 3.3.0', # oldest released version with sane image handling 'ruffus == 2.6.3', # pinned - ocrmypdf implements a 2.6.3 workaround ], diff --git a/src/ocrmypdf/pdfa.py b/src/ocrmypdf/pdfa.py index 50f489e6..38f58c3a 100644 --- a/src/ocrmypdf/pdfa.py +++ b/src/ocrmypdf/pdfa.py @@ -20,12 +20,9 @@ from string import Template from binascii import hexlify from datetime import datetime -from xml.parsers.expat import ExpatError import pkg_resources -import PyPDF2 as pypdf -import warnings -from defusedxml.minidom import parseString as defused_parseString -from unittest.mock import patch +from libxmp.utils import file_to_dict +from libxmp import consts ICC_PROFILE_RELPATH = 'data/sRGB.icc' @@ -230,35 +227,32 @@ def file_claims_pdfa(filename): This checks if the XMP metadata contains a PDF/A marker. """ - warnings.simplefilter('ignore', pypdf.utils.PdfReadWarning) - pdf = pypdf.PdfFileReader(filename) - try: - # Monkeypatch PyPDF2 to use defusedxml as its XML parser, for safety - with patch('xml.dom.minidom.parseString', new=defused_parseString): - xmp = pdf.getXmpMetadata() - except ExpatError: - return {'pass': False, 'output': 'pdf', - 'conformance': 'Invalid XML metadata'} - try: - pdfa_nodes = xmp.getNodesInNamespace( - aboutUri='', - namespace='http://www.aiim.org/pdfa/ns/id/') - except AttributeError: + xmp = file_to_dict(filename) + if not xmp: return {'pass': False, 'output': 'pdf', 'conformance': 'No XMP metadata'} - pdfa_dict = {attr.localName: attr.value for attr in pdfa_nodes} - if not pdfa_dict: + if not consts.XMP_NS_PDFA_ID in xmp: return {'pass': False, 'output': 'pdf', - 'conformance': 'No XMP metadata'} + 'conformance': 'No PDF/A metadata in XMP'} - part_conformance = pdfa_dict['part'] + pdfa_dict['conformance'] + pdfa_node = xmp[consts.XMP_NS_PDFA_ID] + def read_node(node, key): + return next( + (v for k, v, meta in pdfa_node if k == key), '' + ) + + part = read_node(pdfa_node, 'pdfaid:part') + conformance = read_node(pdfa_node, 'pdfaid:conformance') + + part_conformance = part + conformance valid_part_conforms = {'1A', '1B', '2A', '2B', '2U', '3A', '3B', '3U'} conformance = 'PDF/A-{}'.format( part_conformance) + pdfa_dict = {} if part_conformance in valid_part_conforms: pdfa_dict['pass'] = True pdfa_dict['output'] = 'pdfa' diff --git a/test_requirements.txt b/test_requirements.txt index 64be369e..e3c24956 100644 --- a/test_requirements.txt +++ b/test_requirements.txt @@ -4,4 +4,5 @@ pytest-xdist pytest-cov pytest-timeout python-xmp-toolkit # requires apt-get install libexempi3 - # or brew install exempi \ No newline at end of file + # or brew install exempi +PyPDF2 >= 1.26.0