Repository navigation
fix(extractor): skip comment stripping inside inline image data - #626
deepanshupal wants to merge 4 commits into
Conversation
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Confidence score: 4/5
src/extractor/content_stream.rsrejectsIDwhen it is followed directly by a delimiter, despite documenting that boundary; inline-image streams in that form may not tokenize correctly. Update the follower check to accept the documented delimiter.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/extractor/content_stream.rs">
<violation number="1" location="src/extractor/content_stream.rs:113">
P3: The new `ends_token` doc claims inline images may run one token into the next (`BI/W`, `ID<`), but the `ID` branch's follower check only accepts whitespace or end-of-stream. So when `ID` is followed directly by a delimiter byte (e.g. image data starting with `<` or `(`), `in_image_data` is never set, `bi_open` stays true, and `%` bytes later in the data region are still stripped as comments — the corruption this PR exists to prevent. A missed `ID` also leaves `bi_open` set, so a later bare `ID` (the `cm ID`-style marker the PR deliberately tolerates) then wrongly opens verbatim mode and shields real comments. Widen the follower to `ends_token` (a strict superset: whitespace/EOF stay accepted) to match the documented and feedback-grounded token rule.</violation>
</file>
Shadow auto-approve: would auto-approve with 1 open P3 issue. Fixes strip_pdf_comments to treat bytes between ID and EI as raw inline image data, preventing % bytes from being stripped as comments; bounded to the content-stream pre-pass and pinned by unit and integration tests.
View guided diff | Re-trigger cubic
| && b == b'I' | ||
| && data.get(i + 1) == Some(&b'D') | ||
| && after_token_break(data, i) | ||
| && data.get(i + 2).is_none_or(|&n| is_pdf_whitespace(n)) |
There was a problem hiding this comment.
P3: The new ends_token doc claims inline images may run one token into the next (BI/W, ID<), but the ID branch's follower check only accepts whitespace or end-of-stream. So when ID is followed directly by a delimiter byte (e.g. image data starting with < or (), in_image_data is never set, bi_open stays true, and % bytes later in the data region are still stripped as comments — the corruption this PR exists to prevent. A missed ID also leaves bi_open set, so a later bare ID (the cm ID-style marker the PR deliberately tolerates) then wrongly opens verbatim mode and shields real comments. Widen the follower to ends_token (a strict superset: whitespace/EOF stay accepted) to match the documented and feedback-grounded token rule.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At src/extractor/content_stream.rs, line 113:
<comment>The new `ends_token` doc claims inline images may run one token into the next (`BI/W`, `ID<`), but the `ID` branch's follower check only accepts whitespace or end-of-stream. So when `ID` is followed directly by a delimiter byte (e.g. image data starting with `<` or `(`), `in_image_data` is never set, `bi_open` stays true, and `%` bytes later in the data region are still stripped as comments — the corruption this PR exists to prevent. A missed `ID` also leaves `bi_open` set, so a later bare `ID` (the `cm ID`-style marker the PR deliberately tolerates) then wrongly opens verbatim mode and shields real comments. Widen the follower to `ends_token` (a strict superset: whitespace/EOF stay accepted) to match the documented and feedback-grounded token rule.</comment>
<file context>
@@ -76,14 +93,29 @@ fn strip_pdf_comments(data: &[u8]) -> Vec<u8> {
- && data.get(i + 2).is_some_and(|&n| is_pdf_whitespace(n))
- && (i == 0 || is_pdf_whitespace(data[i - 1]))
+ && after_token_break(data, i)
+ && data.get(i + 2).is_none_or(|&n| is_pdf_whitespace(n))
{
result.push(b'I');
</file context>
| && data.get(i + 2).is_none_or(|&n| is_pdf_whitespace(n)) | |
| && data.get(i + 2).is_none_or(|&n| ends_token(n)) |
|
Replies to the three cubic findings (native per-thread replies aren't offered on the resolved threads, so answering here with the thread refs): r4236543730 (bare r4236543732 ( r4236568238 ( Full |
There was a problem hiding this comment.
0 issues found across 1 file (changes from recent commits).
Confidence score: 5/5
- Automated review surfaced no issues in the provided summaries.
- No files require special attention.
Shadow auto-approve: would auto-approve with 1 open P3 issue. Fixes strip_pdf_comments to preserve raw bytes between ID and EI so %/(/< in inline image data no longer breaks extraction; the change is scoped to the content-stream pre-pass and pinned by unit and integration tests.
View guided diff | Re-trigger cubic
What
Fixes the in-repo half of #617:
strip_pdf_commentsno longer strips inside inline image data. Bytes between theIDoperator's whitespace separator and the terminatingEIare raw image content, not content-stream tokens - a%there is a pixel value, not a comment, and a(or<must not move the string state.Why
On 1.25.2, an unfiltered inline image whose data contains a
%byte loses everything from that byte to the next end-of-line in the pre-pass; the image data then comes up short, lopdf rejects the content stream, and whole-document calls (extractPagesMarkdownand friends) fail for every page, including the readable ones. A(or<byte in the data similarly corrupts the string state for the rest of the stream.How
The pre-pass tracks inline-image state: it enters on the
IDoperator token (whitespace-delimited, outside string and hex-string context) and copies bytes verbatim untilEIdelimited by whitespace - the same delimiter rule lopdf uses to locateEI, so the pre-pass stays aligned with the parser it feeds.The reporter offered to pick this up 5 days ago and no PR has landed, so here's a scoped fix for component 3 only; happy to close this if they'd rather take it.
Scope, per the issue's three components:
strip_pdf_comments). With it, the issue's "image data contains a%byte" repro row extracts all 3 pages onpages="all".ID) needs the upstream fix in fix: read one white-space character after ID in inline images J-F-Liu/lopdf#582 and a lopdf bump; the "image data is three spaces" row still depends on that.needs_ocrinstead of failing the call) is a behavior/API decision and is left alone here.Tests
test_strip_pdf_comments_skips_inline_image_data(unit):%in image data preserved,(in data does not shield a real comment afterEI,IDinside a string literal stays text, multiple images in one stream stay verbatim.test_inline_image_percent_byte_does_not_fail_document(integration): rebuilds the issue's repro PDF in Rust; fails onmainexactly as the issue's matrix shows (whole-document call throws,pages=[0,2]subset still extracts), passes with the fix.test_inline_image_gray_control_still_extracts(integration): the issue's control row, guarding against over-skipping.cargo test: 1954 passed, 0 failed.cargo clippy -- -D warningsandcargo fmt --checkclean (AGENTS.md gates).Summary by cubic
Fixes
strip_pdf_commentsso%,(, and<bytes inside unfiltered inline image data are preserved instead of being stripped as comments or shifting string state, which was truncating image data and failing whole-document extraction on PDFs that contain such images.IDoperator and the whitespace-delimitedEI, using the same delimiter rule as lopdf; inline-image mode only opens after aBIoperator, so a bareIDor the name/IDstays regular text, andIDadjacent to a closing delimiter (e.g.[/Fl]ID) is still recognized.needs_ocrdegradation behavior (component 1) is unchanged.Written for commit 207b42b. Summary will update on new commits.