From 2c1b5e100b79d7c620a21cdef75e9741e6b4159e Mon Sep 17 00:00:00 2001 From: "James R. Barlow" Date: Sun, 26 Jul 2015 18:18:41 -0700 Subject: [PATCH] Test cases for pageinfo; complain about inline images --- ocrmypdf/pageinfo.py | 82 +++++++++++++++++++++++----------- ocrmypdf/test/test_pageinfo.py | 30 +++---------- setup.py | 2 +- 3 files changed, 65 insertions(+), 49 deletions(-) diff --git a/ocrmypdf/pageinfo.py b/ocrmypdf/pageinfo.py index 815457d2..685a6c2e 100644 --- a/ocrmypdf/pageinfo.py +++ b/ocrmypdf/pageinfo.py @@ -2,8 +2,10 @@ # from subprocess import Popen, PIPE -import PyPDF2 as pypdf from decimal import Decimal, getcontext +import re +import sys +import PyPDF2 as pypdf FRIENDLY_COLORSPACE = { @@ -35,32 +37,32 @@ FRIENDLY_COMP = { } -def _pdf_get_pageinfo(infile, page: int): - pageinfo = {} - pageinfo['pageno'] = page - pageinfo['images'] = [] +def _page_has_inline_images(page): + # PDF always uses \r\n for separator regardless of platform + # Really basic heuristic that might trigger the odd false positive + # This is only finds the first image and is not quite spec compliant + contents = page.getContents() + data = contents.getData() + begin_image, image_data, end_image = False, False, False + for data in re.split(b'\s+', data): + print(data) + if data == b'BI': + begin_image = True + elif data == b'ID': + image_data = True + elif data == b'EI': + end_image = True + if all((begin_image, image_data, end_image)): + return True - p_pdftotext = Popen(['pdftotext', '-f', str(page), '-l', str(page), - '-raw', '-nopgbrk', infile, '-'], - close_fds=True, stdout=PIPE, stderr=PIPE, - universal_newlines=True) - text, _ = p_pdftotext.communicate() - if len(text.strip()) > 0: - pageinfo['has_text'] = True - else: - pageinfo['has_text'] = False - pdf = pypdf.PdfFileReader(infile) - page = pdf.pages[page - 1] - width_pt = page['/MediaBox'][2] - page['/MediaBox'][0] - height_pt = page['/MediaBox'][3] - page['/MediaBox'][1] - pageinfo['width_inches'] = width_pt / Decimal(72.0) - pageinfo['height_inches'] = height_pt / Decimal(72.0) - - if '/XObject' not in page['/Resources']: - # Missing /XObject means no images or possibly corrupt PDF - return pageinfo +def _find_page_images(page, pageinfo): + try: + page['/Resources']['/XObject'] + except KeyError: + return + # Look for XObject (out of line images) for xobj in page['/Resources']['/XObject']: # PyPDF2 returns the keys as an iterator pdfimage = page['/Resources']['/XObject'][xobj] @@ -92,7 +94,37 @@ def _pdf_get_pageinfo(infile, page: int): image['dpi_w'] = image['width'] / pageinfo['width_inches'] image['dpi_h'] = image['height'] / pageinfo['height_inches'] image['dpi'] = (image['dpi_w'] * image['dpi_h']) ** Decimal(0.5) - pageinfo['images'].append(image) + yield image + + +def _pdf_get_pageinfo(infile, page: int): + pageinfo = {} + pageinfo['pageno'] = page + pageinfo['images'] = [] + + p_pdftotext = Popen(['pdftotext', '-f', str(page), '-l', str(page), + '-raw', '-nopgbrk', infile, '-'], + close_fds=True, stdout=PIPE, stderr=PIPE, + universal_newlines=True) + text, _ = p_pdftotext.communicate() + if len(text.strip()) > 0: + pageinfo['has_text'] = True + else: + pageinfo['has_text'] = False + + pdf = pypdf.PdfFileReader(infile) + page = pdf.pages[page - 1] + width_pt = page['/MediaBox'][2] - page['/MediaBox'][0] + height_pt = page['/MediaBox'][3] - page['/MediaBox'][1] + pageinfo['width_inches'] = width_pt / Decimal(72.0) + pageinfo['height_inches'] = height_pt / Decimal(72.0) + + pageinfo['images'] = [im for im in _find_page_images(page, pageinfo)] + + # Look for inline images + if _page_has_inline_images(page): + raise NotImplementedError( + "Warning: input PDF contains inline images - not supported") if pageinfo['images']: xres = max(image['dpi_w'] for image in pageinfo['images']) diff --git a/ocrmypdf/test/test_pageinfo.py b/ocrmypdf/test/test_pageinfo.py index d4b8ab9b..a8087fd0 100644 --- a/ocrmypdf/test/test_pageinfo.py +++ b/ocrmypdf/test/test_pageinfo.py @@ -8,6 +8,7 @@ from contextlib import suppress import os import sys import shutil +from nose.tools import * TEST_OUTPUT = os.path.join(os.path.dirname(__file__), 'output') @@ -43,7 +44,7 @@ def test_single_page_text(): def test_single_page_image(): filename = os.path.join(TEST_OUTPUT, 'image-mono.pdf') - pdf = Canvas(filename, pagesize=(8*72, 6*72)) + pdf = Canvas(filename, pagesize=(72, 72)) with NamedTemporaryFile() as im_tmp: im = Image.new('1', (8, 8), 0) for n in range(8): @@ -64,7 +65,7 @@ def test_single_page_image(): pdfimage = page['images'][0] assert pdfimage['width'] == 8 - assert pdfimage['color'] == 'gray' + # assert pdfimage['color'] == 'gray' # While unexpected, this is correct # PDF spec says /FlateDecode image must have /BitsPerComponent 8 @@ -72,10 +73,11 @@ def test_single_page_image(): assert pdfimage['bpc'] == 8 # DPI in a 1"x1" is the image width - assert pdfimage['dpi_w'] == 8 - assert pdfimage['dpi_h'] == 8 + eq_(pdfimage['dpi_w'], 8) + eq_(pdfimage['dpi_h'], 8) +@raises(NotImplementedError) def test_single_page_inline_image(): filename = os.path.join(TEST_OUTPUT, 'image-mono-inline.pdf') pdf = Canvas(filename, pagesize=(8*72, 6*72)) @@ -89,23 +91,5 @@ def test_single_page_inline_image(): pdf.showPage() pdf.save() - pdfinfo = pageinfo.pdf_get_all_pageinfo(filename) + pageinfo.pdf_get_all_pageinfo(filename) - assert len(pdfinfo) == 1 - page = pdfinfo[0] - - assert not page['has_text'] - assert len(page['images']) == 1 - - pdfimage = page['images'][0] - assert pdfimage['width'] == 8 - assert pdfimage['color'] == 'gray' - - # While unexpected, this is correct - # PDF spec says /FlateDecode image must have /BitsPerComponent 8 - # So mono images get upgraded to 8-bit - assert pdfimage['bpc'] == 8 - - # DPI in a 1"x1" is the image width - assert pdfimage['dpi_w'] == 8 - assert pdfimage['dpi_h'] == 8 diff --git a/setup.py b/setup.py index ed69adc4..56dd059b 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,7 @@ setup( 'Pillow>=2.7.0', 'lxml>=3.4.2', 'reportlab>=3.1.44', - 'PyPDF2>=1.24' + 'PyPDF2>=1.25.1' ], entry_points={ 'console_scripts': [