Skip to content

fix: tolerate real-world producer quirks found in invoice OFDs - #9

Open
add-uos wants to merge 1 commit into
linuxdeepin:mainfrom
add-uos:fix/real-world-ofd-compat
Open

add-uos wants to merge 1 commit into
linuxdeepin:mainfrom
add-uos:fix/real-world-ofd-compat

Conversation

@add-uos

@add-uos add-uos commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

概要

真实世界的 OFD 文件(发票生成器尤为典型)会触发三处解析/渲染死路;由于 deepin-reader 链接同一套库,以下失败在阅读器中同样复现:

  1. 重复 MultiMedias 块:部分发票生成器在 DocumentRes.xml 输出多个 <MultiMedias> 目录,serde 报 duplicate field 直接失败。改为与 Fonts 块相同的处理:lenient 模式合并(Option<T> → Vec<T>),strict 模式仍然拒绝
  2. 空 <Clips/> 元素:lenient 模式视为无裁剪继续渲染,strict 模式保持报错
  3. 图片声明格式与实际不符:发票把 JPEG 数据声明成 PNG。改为按字节签名嗅探解码(未支持签名仍然拒绝),目录声明仅作元数据

修复前 6 张真实报销发票仅 2 张可渲染,修复后 6/6 全部正常。

测试

  • 新增 3 组回归测试(多 MultiMedias 合并/strict 拒绝、空 Clips lenient/strict、格式嗅探覆盖旧断言并改名)
  • cargo test -p rofd-core -p rofd-render 全量通过;cargo clippy --all-targets -- -D warnings 干净
  • 关联:feat: add OFD thumbnail and space preview support dde-file-manager#4575(文管 OFD 缩略图/空格预览,依赖本修复才能渲染真实发票)

Summary by Sourcery

Make OFD parsing and rendering resilient to common producer inconsistencies while retaining strict validation options.

Bug Fixes:

  • Improve compatibility with real-world invoice OFD files by tolerating repeated multimedia resource sections and empty clipping elements in lenient mode.
  • Decode images according to their detected byte signatures when producer-declared formats are incorrect, while continuing to reject unsupported formats.

Enhancements:

  • Preserve strict-mode validation for duplicate MultiMedias sections and empty Clips elements.

Tests:

  • Add regression coverage for duplicate multimedia handling, empty clipping elements, and image format detection overriding incorrect declarations.

Real-world OFD files (invoice generators in particular) hit three
parsing/rendering dead ends; every failure below also reproduces in
deepin-reader since it links the same library:

- rofd-core: accept several <MultiMedias> blocks in one resource
  catalog (mirrors the existing Fonts handling) instead of failing
  with a serde duplicate-field error; strict mode still rejects them
- rofd-core: treat an empty <Clips/> element as absent in lenient
  mode instead of rejecting the whole page; strict mode still errors
- rofd-render: decode images by their detected byte signature rather
  than the catalog-declared format (invoices declare PNG for JPEG
  data); unsupported signatures are still rejected

With these, 6/6 real invoice OFDs render (2/6 before). Regression
tests added for all three; existing suites updated where the old
strictness was asserted.
@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: add-uos

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@sourcery-ai

sourcery-ai Bot commented Sep 29, 2026

Copy link
Copy Markdown

Reviewer's Guide

The PR hardens OFD parsing and rendering against common invoice-producer deviations by merging repeated resource blocks and tolerating empty clipping containers in lenient mode, while retaining strict-mode validation. Image decoding now trusts supported byte signatures over potentially incorrect declared formats, with regression tests covering each compatibility behavior.

Sequence diagram for image format sniffing during rendering

sequenceDiagram
    participant Renderer
    participant Preflight
    participant ImageResource
    participant Decoder
    Renderer->>Preflight: preflight(resource, limits)
    Preflight->>ImageResource: asset_path()
    Preflight->>Preflight: detect_format(resource)
    Preflight->>Decoder: Decode using detected byte signature
    Decoder-->>Preflight: Decoded image
    Preflight-->>Renderer: Rendering source
Loading

Flow diagram for lenient and strict OFD compatibility handling

flowchart TD
    A[Parse OFD resources and page content] --> B{Duplicate MultiMedias blocks?}
    B -->|No| C[Build ResourceCatalog]
    B -->|Lenient| D[Merge MultiMedias entries]
    B -->|Strict| E[Reject duplicate MultiMedias]
    D --> C
    A --> F{Empty Clips element?}
    F -->|No| G[Process clipping paths]
    F -->|Lenient| H[Treat Clips as absent]
    F -->|Strict| I[Reject invalid Clips]
    H --> J[Render page]
    G --> J
    C --> J
    E --> K[Parse failure]
    I --> K
Loading

File-Level Changes

Change Details Files
Make resource parsing tolerant of repeated MultiMedias blocks while preserving strict validation.
  • Parse all MultiMedias blocks instead of failing on duplicate XML fields.
  • Merge entries from every block into the resource catalog in lenient mode.
  • Reject multiple blocks explicitly in strict mode.
  • Add lenient and strict regression coverage.
crates/rofd-core/src/raw.rs
crates/rofd-core/src/resources.rs
crates/rofd-core/tests/resources.rs
Treat empty Clips elements according to document strictness.
  • Ignore empty elements in lenient mode.
  • Continue reporting invalid structure in strict mode.
  • Add separate regression tests for both behaviors.
crates/rofd-core/src/content.rs
crates/rofd-core/tests/clipping.rs
Decode images from detected byte signatures rather than unreliable catalog format metadata.
  • Remove the declared-versus-detected format mismatch rejection.
  • Use the detected format as the decoding authority while retaining rejection of unsupported signatures.
  • Update image tests to verify decoding succeeds when declarations disagree and unsupported signatures still fail.
crates/rofd-render/src/images.rs
crates/rofd-render/tests/images.rs

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot 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.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

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