Remove all uses of PyPDF2 except PDF/A check

Leave PDF/A check alone for now, since pikepdf has no equivalent.
This commit is contained in:
James R. Barlow
2018-05-26 02:07:18 -07:00
parent 8ba4968c48
commit 9608b22d34
7 changed files with 18 additions and 33 deletions
+5 -7
View File
@@ -1,7 +1,7 @@
PDF security issues
===================
OCRmyPDF should only be used on PDFs you trust. It is not designed to protect you against malware.
OCRmyPDF should only be used on PDFs you trust. It is not designed to protect you against malware.
Recognizing that many users have an interest in handling PDFs and applying OCR to PDFs they did not generate themselves, this article discusses the security implications of PDFs and how users can protect themselves.
@@ -19,9 +19,7 @@ This `article <https://theinvisiblethings.blogspot.ca/2013/02/converting-untrust
How OCRmyPDF processes PDFs
---------------------------
OCRmyPDF must open and interpret your PDF in order to insert an OCR layer. First, it runs all PDFs through `qpdf <https://github.com/qpdf/qpdf>`_, a program that repairs PDFs with syntax errors. This is done because, in the author's experience, a significant number of PDFs in the wild especially those created by scanners are not well-formed files. qpdf makes it more likely that OCRmyPDF will succeed, but offers no security guarantees. qpdf is also used to split the PDF into single page PDFs.
After qpdf, OCRmyPDF examines each page using `PyPDF2 <https://github.com/mstamy2/PyPDF2>`_. This library also has no warranties or guarantees. OCRmyPDF works with qpdf 5.0 and up, but version 7.0 is recommended because of known security vulnerabilities in early versions.
OCRmyPDF must open and interpret your PDF in order to insert an OCR layer. First, it runs all PDFs through `pikepdf <https://github.com/pikepdf/pikepdf>`_, a library based on `qpdf <https://github.com/qpdf/qpdf>`_, a program that repairs PDFs with syntax errors. This is done because, in the author's experience, a significant number of PDFs in the wild especially those created by scanners are not well-formed files. qpdf makes it more likely that OCRmyPDF will succeed, but offers no security guarantees. qpdf is also used to split the PDF into single page PDFs.
Finally, OCRmyPDF rasterizes each page of the PDF using `Ghostscript <http://ghostscript.com/>`_ in ``-dSAFER`` mode.
@@ -60,13 +58,13 @@ Commercial alternatives
The author also provides professional services that include OCR and building databases around PDFs, and is happy to provide consultation.
Abbyy Cloud OCR is a viable commercial alternative with a web services API.
Abbyy Cloud OCR is a viable commercial alternative with a web services API.
Password protection, digital signatures and certification
---------------------------------------------------------
Password protected PDFs usually have two passwords, and owner and user password. When the user password is set to empty, PDF readers will open the file automatically and marked it as "(SECURED)". While not as reliable as a digital signature, this indicates that whoever set the password approved of the file at that time. When the user password is set, the document cannot be viewed without the password.
Password protected PDFs usually have two passwords, and owner and user password. When the user password is set to empty, PDF readers will open the file automatically and marked it as "(SECURED)". While not as reliable as a digital signature, this indicates that whoever set the password approved of the file at that time. When the user password is set, the document cannot be viewed without the password.
Either way, OCRmyPDF does not remove passwords from PDFs and exits with an error on encountering them.
@@ -76,4 +74,4 @@ After OCR is applied, password protection is not permitted on PDF/A documents bu
Many programs exist which are capable of inserting an image of someone's signature. On its own, this offers no security guarantees. It is trivial to remove the signature image and apply it to other files. This practice offers no real security.
Important documents can be digitally signed and certified to attest to their authorship. OCRmyPDF cannot do this. Open source tools such as pdfbox (Java) have this capability as does Adobe Acrobat.
Important documents can be digitally signed and certified to attest to their authorship. OCRmyPDF cannot do this. Open source tools such as pdfbox (Java) have this capability as does Adobe Acrobat.
+1 -7
View File
@@ -22,14 +22,12 @@ from pathlib import Path
import sys
import os
import re
import warnings
import multiprocessing
import atexit
import textwrap
import logging
import argparse
import PyPDF2 as pypdf
import PIL
import ruffus.ruffus_exceptions as ruffus_exceptions
@@ -50,8 +48,6 @@ from .exceptions import ExitCode, ExitCodeException, MissingDependencyError, \
from . import exceptions as ocrmypdf_exceptions
from ._unicodefun import verify_python3_env
warnings.simplefilter('ignore', pypdf.utils.PdfReadWarning)
# -------------
# External dependencies
@@ -616,9 +612,7 @@ def do_ruffus_exception(ruffus_five_tuple, options, log):
msg = "Error occurred while running this command:"
log.error(msg + '\n' + exc_value)
exit_code = ExitCode.child_process_error
elif (exc_name == 'PyPDF2.utils.PdfReadError' and \
'not been decrypted' in exc_value) or \
(exc_name == 'ocrmypdf.exceptions.EncryptedPdfError'):
elif exc_name == 'ocrmypdf.exceptions.EncryptedPdfError':
log.error(textwrap.dedent("""\
Input PDF is encrypted. The encryption must be removed to
perform OCR.
+2 -4
View File
@@ -26,8 +26,6 @@ from subprocess import PIPE, CalledProcessError, \
TimeoutExpired, check_output, STDOUT
from contextlib import suppress
import PyPDF2 as pypdf
from ..exceptions import MissingDependencyError, TesseractConfigError
from ..helpers import page_number
from . import get_version
@@ -67,8 +65,8 @@ def v4():
@lru_cache(maxsize=1)
def has_textonly_pdf():
"""Does Tesseract have textonly_pdf capability?
Available in 3.05.01, and v4.00.00alpha since January 2017. Best to
Available in 3.05.01, and v4.00.00alpha since January 2017. Best to
parse the parameter list
"""
args_tess = [
+8 -7
View File
@@ -23,6 +23,7 @@ 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
@@ -105,7 +106,7 @@ def encode_text_string(s: str) -> str:
def encode_pdf_date(d: datetime) -> str:
"""Encode Python datetime object as PDF date string
From Adobe pdfmark manual:
From Adobe pdfmark manual:
(D:YYYYMMDDHHmmSSOHH'mm')
D: is an optional prefix. YYYY is the year. All fields after the year are
optional. MM is the month (01-12), DD is the day (01-31), HH is the
@@ -140,13 +141,13 @@ def decode_pdf_date(s: str) -> datetime:
if s.startswith('D:'):
s = s[2:]
# Literal Z00'00', is incorrect but found in the wild,
# Literal Z00'00', is incorrect but found in the wild,
# probably made by OS X Quartz -- standardize
if s.endswith("Z00'00'"):
s = s.replace("Z00'00'", '+0000')
elif s.endswith('Z'):
s = s.replace('Z', '+0000')
s = s.replace("'", "") # Remove apos from PDF time strings
return datetime.strptime(s, r'%Y%m%d%H%M%S%z')
@@ -154,7 +155,7 @@ def decode_pdf_date(s: str) -> datetime:
def _get_pdfmark_dates(pdfmark):
"""Encode dates for pdfmark Postscript. The best way to deal with a
missing date entry is set it to null, because if the key is omitted
missing date entry is set it to null, because if the key is omitted
Ghostscript will set it to now - we do not want to erase the fact that
the value was unknown. Setting to an empty string breaks Ghostscript
9.22 as reported here:
@@ -172,7 +173,7 @@ def _get_pdfmark_dates(pdfmark):
date_str = date_str[2:]
try:
yield ' {} (D:{})'.format(
key,
key,
encode_pdf_date(decode_pdf_date(date_str)))
except ValueError:
yield ' {} null'.format(key)
@@ -180,7 +181,7 @@ def _get_pdfmark_dates(pdfmark):
def _get_pdfa_def(icc_profile, icc_identifier, pdfmark):
"""Create a Postscript file for Ghostscript. pdfmark contains the various
objects as strings; these must be encoded in ASCII, and dates have a
objects as strings; these must be encoded in ASCII, and dates have a
special format."""
# Ghostscript <= 9.21 has a bug where null entries in DOCINFO might produce
@@ -229,6 +230,7 @@ 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
@@ -263,4 +265,3 @@ def file_claims_pdfa(filename):
pdfa_dict['conformance'] = conformance
return pdfa_dict
+1 -5
View File
@@ -124,11 +124,7 @@ ContentsInfo = namedtuple('ContentsInfo',
def _normalize_stack(operations):
"""Fix runs of qQ's in the stack
For some reason PyPDF2 converts runs of qqq, QQ, QQQq, etc. into single
operations. Break this silliness up and issue each stack operation
individually so we don't lose count.
"""
"""Convert runs of qQ's in the stack into single operations"""
for operands, command in operations:
command = str(command)
if re.match(r'Q*q+$', command): # Zero or more Q, one or more q
-1
View File
@@ -26,7 +26,6 @@ import shutil
import re
import img2pdf
import PyPDF2 as pypdf
import pikepdf
from PIL import Image
+1 -2
View File
@@ -22,14 +22,13 @@
import sys
import img2pdf
import PyPDF2 as pypdf
from PIL import Image
"""Tesseract bad utf8 spoof
In 'hocr' mode or 'pdf' mode, return error code 1 and some non-Unicode
text because tesseract seems to do that in some cases related to
text because tesseract seems to do that in some cases related to
language pack version mismatches
"""