Skip to content

Fix pdf text extraction multipage - #5260

Open
codewithfourtix wants to merge 3 commits into
aboutcode-org:developfrom
codewithfourtix:fix-pdf-text-extraction-multipage
Open

Fix pdf text extraction multipage#5260
codewithfourtix wants to merge 3 commits into
aboutcode-org:developfrom
codewithfourtix:fix-pdf-text-extraction-multipage

Conversation

@codewithfourtix

Copy link
Copy Markdown
Contributor

What

Fix a regression in textcode/pdf.py where PDF text extraction silently stops after the first page, and can return None instead of a list of lines.

The bug

The refactoring in #4606 (commit fa63f1a7d, "Improve package scan performance") removed one level of with contextlib.closing(...) nesting in get_text_lines() and dedented the function body — except the final two lines, which kept their old absolute indentation and therefore silently moved inside the page loop:

for page_num, page in enumerate(pages, 1):
    interpreter.process_page(page)
    if max_pages and page_num == max_pages:
        break
    extracted_text.seek(0)
    return extracted_text.readlines()   # returns after processing page 1

Consequences:

  • With the default max_pages=5, the function returns right after processing page 1, so text on pages 2–5 is never extracted. Copyright, license, email and URL detection silently miss anything past the first page of every PDF.
  • When max_pages is reached (via break), or the PDF yields no pages, the function returns None. The caller textcode.analysis.unicode_text_lines_from_pdf then crashes with TypeError: 'NoneType' object is not iterable.

Before #4606, both lines executed after the loop. This PR restores that placement.

Why existing tests did not catch it

The PDF test files in tests/textcode/data/pdf/ only assert content from the first page, so the suite passes both with and without the regression.

Tests added

A 7-page test PDF (multi_page.pdf) with distinct text on each page, plus three regression tests asserting that:

  • text is extracted from all pages up to the default max_pages=5 (and not beyond);
  • reaching max_pages returns the extracted lines and not None;
  • max_pages=0 extracts all pages.

Verified against the pinned pdfminer.six==20260107: all existing PDF tests still pass, and the new tests fail on develop and pass with this fix.

Related issues

I checked open issues and PRs — none cover this regression. (Open issue #3794 is a different, older TypeError coming from inside pdfminer on a malformed PDF, predating #4606.)

Restore the extracted-text read to its position after the page loop in
get_text_lines. The refactoring in aboutcode-org#4606 removed one level of with-block
nesting and dedented the whole function body except the final two lines,
which silently moved them inside the for loop:

- with the default max_pages=5, the function returned right after
  processing page 1, so text on pages 2-5 was never extracted and
  copyright/license/email/url detection silently missed it
- when max_pages was reached via break, or the PDF had no pages, the
  function returned None, crashing the caller
  textcode.analysis.unicode_text_lines_from_pdf with a TypeError when
  it iterates the result

Signed-off-by: Ali Zulfiqar <codewithfourtix@gmail.com>
Add a 7-page test PDF with distinct text on each page and tests
asserting that:

- text is extracted from all pages up to the default max_pages=5
- reaching max_pages returns the extracted lines and not None
- max_pages=0 extracts all pages

The existing PDF test files only assert content from the first page,
which is why the regression was not caught.

Signed-off-by: Ali Zulfiqar <codewithfourtix@gmail.com>
Copilot AI review requested due to automatic review settings August 3, 2026 12:37

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a regression in PDF text extraction where textcode/pdf.py:get_text_lines() returned after processing only the first page (and could return None in some paths), causing downstream text analysis to miss content beyond page 1 and potentially crash.

Changes:

  • Move extracted_text.seek(0) and return extracted_text.readlines() outside the per-page loop so extraction continues through all pages up to max_pages.
  • Add regression tests covering multi-page extraction, max_pages=1 (no None), and max_pages=0 (unlimited pages).

Reviewed changes

Copilot reviewed 1 out of 3 changed files in this pull request and generated 1 comment.

File Description
src/textcode/pdf.py Fixes control flow so text extraction does not stop after the first processed page.
tests/textcode/test_pdf.py Adds multi-page regression tests validating correct max_pages behavior and non-None returns.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/textcode/pdf.py
Comment on lines 43 to +47
interpreter.process_page(page)
if max_pages and page_num == max_pages:
break
extracted_text.seek(0)
return extracted_text.readlines()
extracted_text.seek(0)
return extracted_text.readlines()
Signed-off-by: Ali Zulfiqar <codewithfourtix@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants