From 93623b2226db2943940d9dff5d3ca30eb77a1a46 Mon Sep 17 00:00:00 2001 From: "James R. Barlow" Date: Mon, 29 Oct 2018 14:46:40 -0700 Subject: [PATCH] Refactor TextboxInfo --- src/ocrmypdf/pdfinfo/__init__.py | 31 ++++++++++++++++++------------- src/ocrmypdf/pdfinfo/layout.py | 29 ++++------------------------- 2 files changed, 22 insertions(+), 38 deletions(-) diff --git a/src/ocrmypdf/pdfinfo/__init__.py b/src/ocrmypdf/pdfinfo/__init__.py index 2f866fa3..da14389f 100644 --- a/src/ocrmypdf/pdfinfo/__init__.py +++ b/src/ocrmypdf/pdfinfo/__init__.py @@ -28,7 +28,7 @@ import xml.etree.ElementTree as ET from pikepdf import PdfMatrix import pikepdf -from .layout import get_textblocks, filter_textboxes, textbox_predicate +from .layout import get_page_analysis, get_text_boxes from ..exec import ghostscript from ..helpers import fspath @@ -108,6 +108,9 @@ InlineSettings = namedtuple('InlineSettings', ContentsInfo = namedtuple('ContentsInfo', ['xobject_settings', 'inline_images', 'found_text', 'found_vector']) +TextBoxInfo = namedtuple('TextBoxInfo', + ['bbox', 'is_visible', 'is_corrupt']) + class VectorInfo: def __init__(self): @@ -590,15 +593,17 @@ def _page_has_text(text_blocks, page_width, page_height): def simplify_textboxes(miner): - for box in filter_textboxes(miner, lambda x: True): - result = {} + """Extract only limited content from text boxes + + We do this to save memory and ensure that our objects are pickleable. + """ + for box in get_text_boxes(miner): first_line = box._objs[0] first_char = first_line._objs[0] - result['is_visible'] = (first_char.rendermode != 3) - result['is_corrupt'] = (first_char.get_text() == '\ufffd') - result['bbox'] = box.bbox - yield result + visible = (first_char.rendermode != 3) + corrupt = (first_char.get_text() == '\ufffd') + yield TextBoxInfo(box.bbox, visible, corrupt) def _pdf_get_pageinfo(pdf, pageno: int, infile, xmltext): @@ -612,14 +617,14 @@ def _pdf_get_pageinfo(pdf, pageno: int, infile, xmltext): # fspath(infile), pageno, xmltext=xmltext) with Path(infile).open('rb') as f: - miner = get_textblocks(f, pageno) - pageinfo['textobjs'] = list(simplify_textboxes(miner)) + miner = get_page_analysis(f, pageno) + pageinfo['textboxes'] = list(simplify_textboxes(miner)) mediabox = [Decimal(d) for d in page.MediaBox.as_list()] width_pt = mediabox[2] - mediabox[0] height_pt = mediabox[3] - mediabox[1] - bboxes = (obj['bbox'] for obj in pageinfo['textobjs']) + bboxes = (box.bbox for box in pageinfo['textboxes']) pageinfo['has_text'] = _page_has_text( bboxes, width_pt, height_pt ) @@ -748,14 +753,14 @@ class PageInfo: def predicate(obj, want_visible, want_corrupt): result = True if want_visible is not None: - if obj['is_visible'] != want_visible: + if obj.is_visible != want_visible: result = False if want_corrupt is not None: - if obj['is_corrupt'] != want_corrupt: + if obj.is_corrupt != want_corrupt: result = False return result - return (obj['bbox'] for obj in self._pageinfo['textobjs'] + return (obj.bbox for obj in self._pageinfo['textboxes'] if predicate(obj, visible, corrupt)) @property diff --git a/src/ocrmypdf/pdfinfo/layout.py b/src/ocrmypdf/pdfinfo/layout.py index 4f2d002b..0991ceb4 100644 --- a/src/ocrmypdf/pdfinfo/layout.py +++ b/src/ocrmypdf/pdfinfo/layout.py @@ -133,7 +133,7 @@ class TextPositionTracker(PDFLayoutAnalyzer): return self.result -def get_textblocks(infile, pageno): +def get_page_analysis(infile, pageno): rman = pdfminer.pdfinterp.PDFResourceManager(caching=True) dev = TextPositionTracker(rman, laparams=LAParams()) interp = pdfminer.pdfinterp.PDFPageInterpreter(rman, dev) @@ -148,33 +148,12 @@ def get_textblocks(infile, pageno): return dev.get_result() -def textbox_predicate(*, visible, corrupt): - def real_predicate(textbox, want_visible=visible, want_corrupt=corrupt): - textline = textbox._objs[0] - first_char = textline._objs[0] - - result = True - - is_visible = (first_char.rendermode != 3) - if want_visible is not None: - if is_visible != want_visible: - result = False - is_corrupt = (first_char.get_text() == '\ufffd') - if want_corrupt is not None: - if is_corrupt != want_corrupt: - result = False - - return result - return real_predicate - - -def filter_textboxes(obj, predicate): +def get_text_boxes(obj): for child in obj: if isinstance(child, (LTTextBox)): - if predicate(child): - yield child + yield child else: try: - yield from filter_textboxes(child, predicate) + yield from get_text_boxes(child) except TypeError: continue