diff --git a/src/ocrmypdf/_pipeline.py b/src/ocrmypdf/_pipeline.py index bb4426d8..dfc2f5df 100644 --- a/src/ocrmypdf/_pipeline.py +++ b/src/ocrmypdf/_pipeline.py @@ -170,6 +170,13 @@ def repair_and_parse_pdf( log.error(e) raise InputFileError() + if pdfinfo.needs_rendering: + log.error( + "This PDF contains dynamic XFA forms created by Adobe LiveCycle " + "Designer and can only be read by Adobe Acrobat or Adobe Reader." + ) + raise InputFileError() + if pdfinfo.has_userunit and options.output_type.startswith('pdfa'): log.error( "This input file uses a PDF feature that is not supported " diff --git a/src/ocrmypdf/pdfinfo.py b/src/ocrmypdf/pdfinfo.py index d877d568..eeb92907 100644 --- a/src/ocrmypdf/pdfinfo.py +++ b/src/ocrmypdf/pdfinfo.py @@ -611,7 +611,7 @@ def _pdf_get_pageinfo(pdf, pageno: int, infile): def _pdf_get_all_pageinfo(infile): pdf = pikepdf.open(infile) - return [PageInfo(pdf, n, infile) for n in range(len(pdf.pages))] + return [PageInfo(pdf, n, infile) for n in range(len(pdf.pages))], pdf class PageInfo: @@ -697,7 +697,8 @@ class PdfInfo: """ def __init__(self, infile): self._infile = infile - self._pages = _pdf_get_all_pageinfo(infile) + self._pages, pdf = _pdf_get_all_pageinfo(infile) + self._needs_rendering = pdf.root.get('/NeedsRendering', False) @property def pages(self): @@ -718,6 +719,10 @@ class PdfInfo: raise NotImplementedError("can't get filename from stream") return self._infile + @property + def needs_rendering(self): + return self._needs_rendering + def __getitem__(self, item): return self._pages[item] @@ -727,15 +732,6 @@ class PdfInfo: def __repr__(self): return "".format(len(self)) - # def __getstate__(self): - # state = {'_infile': self._infile} - # return state - # - # def __setstate__(self, state): - # self._infile = state['_infile'] - # self._pages = _pdf_get_all_pageinfo(self._infile) - - def main(): import argparse parser = argparse.ArgumentParser() diff --git a/tests/resources/README.rst b/tests/resources/README.rst index 63fdd705..dc64622d 100644 --- a/tests/resources/README.rst +++ b/tests/resources/README.rst @@ -109,6 +109,11 @@ licensed under the specified license. - @jbarlow83 - @jbarlow83 - CC-BY-SA 4.0 + * - livecycle.pdf + - a minimal PDF that claims to use dynamic XFA forms + - @jbarlow83 + - @jbarlow83 + - CC-BY-SA 4.0 * - masks.pdf - file containing explicit masks and a stencil mask drawn without a proper transformation matrix; printout of a German Wikipedia article (CC-BY-SA) - @supergrobi diff --git a/tests/resources/livecycle.pdf b/tests/resources/livecycle.pdf new file mode 100644 index 00000000..633057f9 --- /dev/null +++ b/tests/resources/livecycle.pdf @@ -0,0 +1,28 @@ +%PDF-1.7 +%¿÷¢þ +1 0 obj +<< /NeedsRendering true /Pages 2 0 R /Type /Catalog >> +endobj +2 0 obj +<< /Count 1 /Kids [ 3 0 R ] /Type /Pages >> +endobj +3 0 obj +<< /Contents 4 0 R /MediaBox [ 0 0 300 300 ] /Parent 2 0 R /Resources << >> /Type /Page >> +endobj +4 0 obj +<< /Length 0 >> +stream + +endstream +endobj +xref +0 5 +0000000000 65535 f +0000000015 00000 n +0000000085 00000 n +0000000144 00000 n +0000000250 00000 n +trailer << /Root 1 0 R /Size 5 /ID [<8088c23e0edc07ef4fbed3daa55f52ed><9d36eee510d20004f32255f5800b6fd7>] >> +startxref +299 +%%EOF diff --git a/tests/test_main.py b/tests/test_main.py index dea376cf..1e90f2f5 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -925,3 +925,12 @@ def test_output_is_symlink(spoof_tesseract_noop, resources, outdir): ) assert p.returncode == ExitCode.ok, err assert (outdir / 'out.pdf').stat().st_size > 0, 'target file not created' + + +def test_livecycle(resources, no_outpdf): + p, _, err = run_ocrmypdf( + resources / 'livecycle.pdf', + no_outpdf + ) + + assert p.returncode == ExitCode.input_file, err