The OCG implementation was already complete in ocg.rs. All 20 tests pass: - BaseState parsing (ON/OFF/Unchanged) - /ON and /OFF array override handling - OCMD policy preservation (AllOn, AnyOn, AllOff, AnyOff) - INV-8 compliance verified via proptests Phase 3 will consume OcProperties via is_visible() to suppress glyphs in /OC /OCGRef BDC blocks when the referenced OCG is OFF. Co-Authored-By: Claude Code <noreply@anthropic.com>
4.5 KiB
4.5 KiB
pdftract-2a6rk: OCG /OCProperties parsing verification
Summary
The OCG (Optional Content Groups) implementation is complete in /home/coding/pdftract/crates/pdftract-core/src/parser/ocg.rs. The implementation was already present in the codebase when this task was picked up.
Implementation details
Public API
parse_oc_properties(resolver: &XrefResolver, oc_props_ref: Option<ObjRef>) -> OcProperties- Located in
crates/pdftract-core/src/parser/ocg.rs:273-422
Data structures
-
OcProperties (lines 192-271):
present: bool— true if /OCProperties was presentgroups: HashMap<ObjRef, OcGroup>— all OCGs in the documentdefault_visibility: HashMap<ObjRef, bool>— default visibility per OCGbase_state: BaseState— overall default (ON/OFF/Unchanged)ocmds: HashMap<ObjRef, Ocmd>— optional content membership dictsdiagnostics: Vec<Diagnostic>— parsing diagnostics
-
OcGroup (lines 122-186):
name: Option<String>— /Name entryintent: Vec<String>— /Intent (e.g., "View", "Design")usage: Option<PdfDict>— /Usage dictionary
-
BaseState enum (lines 15-49):
On,Off,UnchangedUnchangedtreated asOnfor default config
-
Ocmd and OcmdPolicy (lines 51-120):
- OCMD support for boolean combinations (AllOn, AnyOn, AllOff, AnyOff)
- Policy evaluation deferred to Phase 3
Catalog integration
The catalog.rs already integrates OCG parsing:
- Lines 486-491: Extract /OCProperties from catalog
- Stores result in
Catalog.oc_properties: Option<OcProperties>
Acceptance criteria verification
| Criteria | Status | Test |
|---|---|---|
| EC-16: OCG with default OFF state | ✅ PASS | test_parse_oc_properties_base_state_off |
| /BaseState OFF + /ON [ocg1 ocg2] | ✅ PASS | test_parse_oc_properties_with_on_array |
| No /OCProperties: present = false | ✅ PASS | test_oc_properties_not_present |
| /OCMD with /AllOn policy preserved | ✅ PASS | test_ocmd_parse, test_ocmd_evaluation_all_on |
| proptest: never panics on random input | ✅ PASS | fuzz_parse_oc_properties_no_panics, fuzz_ocg_group_parse_no_panics, fuzz_ocmd_parse_no_panics |
| INV-8: no panics on arbitrary input | ✅ PASS | All proptests verify INV-8 compliance |
Test results
running 20 tests
test parser::ocg::tests::test_base_state_from_name ... ok
test parser::ocg::tests::test_ocmd_evaluation_any_on ... ok
test parser::ocg::tests::test_ocmd_policy_from_name ... ok
test parser::ocg::tests::test_ocg_group_parse ... ok
test parser::ocg::tests::test_ocg_name_none ... ok
test parser::ocg::tests::test_ocmd_parse ... ok
test parser::ocg::tests::test_ocmd_parse_single_ref ... ok
test parser::ocg::tests::test_parse_oc_properties_base_state_off ... ok
test parser::ocg::tests::test_parse_oc_properties_off_overrides_on ... ok
test parser::ocg::tests::test_parse_oc_properties_simple ... ok
test parser::ocg::tests::test_ocg_name_retrieval ... ok
test parser::ocg::tests::test_ocmd_evaluation_all_on ... ok
test parser::ocg::tests::test_parse_oc_properties_with_off_array ... ok
test parser::ocg::tests::test_parse_oc_properties_with_on_array ... ok
test parser::ocg::tests::test_unknown_ocg_treated_as_visible ... ok
test parser::ocg::tests::test_oc_properties_not_present ... ok
test parser::ocg::tests::test_base_state_as_bool ... ok
test parser::ocg::tests::proptests::fuzz_ocmd_parse_no_panics ... ok
test parser::ocg::tests::proptests::fuzz_parse_oc_properties_no_panics ... ok
test parser::ocg::tests::proptests::fuzz_ocg_group_parse_no_panics ... ok
test result: ok. 20 passed; 0 failed; 0 ignored
Phase 3 integration
The OcProperties struct provides:
is_visible(ocg_ref: ObjRef) -> bool— check OCG default visibilityis_ocmd_visible(ocmd_ref: ObjRef) -> bool— evaluate OCMD policyocg_name(ocg_ref: ObjRef) -> Option<&str>— get OCG name
Phase 3 content stream processing will use these methods to suppress glyphs inside /OC /OCGRef BDC blocks when the referenced OCG is OFF.
Files
- Implementation:
crates/pdftract-core/src/parser/ocg.rs(909 lines) - Catalog integration:
crates/pdftract-core/src/parser/catalog.rs(lines 10, 326, 486-491) - Tests: inline in
ocg.rs(lines 424-908)
Retrospective
- What worked: The implementation was already complete and well-tested
- What didn't: N/A (implementation was already present)
- Surprise: The bead description matched an existing implementation exactly
- Reusable pattern: Use
OcProperties::not_present()for documents without /OCProperties