Verified that the skip logic is fully implemented in encoding.rs: - is_unmapped_glyph_name() method checks glyph names against unmapped set - Skip logic at CMAP entry creation (line 227) prevents unmapped glyphs - All tests pass (26/26 encoding tests) - Clean skip with no errors or partial entries Closes bf-2ts7z
4 KiB
bf-2ts7z: Implement skip logic for unmapped glyphs in CMAP entry creation
Implementation Status
COMPLETE - Skip logic was already implemented in commit 7660cfc8.
Verification
1. Code Implementation ✓
Location: /home/coding/pdftract/crates/pdftract-core/src/font/encoding.rs
Key Components:
- Skip Logic at CMAP Entry Creation Point (lines 221-230):
// MARKER: CMAP entry creation point - Type1 font encoding differences.
// See notes/bf-e4uvb-child-1.md for documentation.
if cursor <= 255 {
// Skip unmapped glyph names (e.g., .notdef) to prevent them from
// appearing in text extraction output. These glyphs have no valid
// Unicode mapping and should emit GLYPH_UNMAPPED diagnostics instead.
if !overlay.is_unmapped_glyph_name(&name) {
overlay.entries.push((cursor as u8, Arc::clone(name)));
}
}
cursor = cursor.saturating_add(1);
- Glyph Name Check Method (lines 274-282):
fn is_unmapped_glyph_name(&self, name: &str) -> bool {
// Strip leading slash if present
let clean_name = if name.starts_with('/') {
&name[1..]
} else {
name
};
self.unmapped_glyph_names.contains(clean_name)
}
- Struct Integration (lines 133-168):
DifferencesOverlaystruct hasunmapped_glyph_names: std::collections::HashSet<String>field- Default constructor uses global
UNMAPPED_GLYPH_NAMESset - Custom constructor accepts custom unmapped sets
- Getter/setter methods for configuration
2. Acceptance Criteria ✓
-
Generator checks glyph name against unmapped_glyph_names before CMAP entry creation: ✓
- Implemented at line 227:
if !overlay.is_unmapped_glyph_name(&name)
- Implemented at line 227:
-
Unmapped glyphs are skipped during CMAP generation: ✓
- When
is_unmapped_glyph_name()returnstrue, the entry is NOT added tooverlay.entries
- When
-
Skip happens cleanly (no errors, no partial entries): ✓
- No error messages are emitted
- No partial entries are written (skip is atomic)
- Cursor continues normally with
cursor = cursor.saturating_add(1)
-
Code compiles without errors: ✓
cargo check --workspacesucceeds with only unrelated warningscargo test --lib -p pdftract-core encoding::tests- all 26 tests pass
3. Test Coverage ✓
All relevant tests pass:
test_differences_overlay_skips_notdef: Verifies.notdefis skippedtest_differences_overlay_skips_notdef_with_slash: Verifies/.notdef(with slash) is skippedtest_differences_overlay_custom_unmapped_glyph_names: Verifies custom unmapped sets work correctlytest_differences_overlay_empty_unmapped_glyph_names: Verifies empty sets allow all glyphs
4. Related Implementation
This is the child 2 component in the unmapped_glyph_names feature:
- Child 1 (bf-4zgpr): Added
unmapped_glyph_namesconfig field to CMAP generator struct - Child 2 (bf-2ts7z): THIS BEAD - Implements the skip logic at CMAP entry creation
- Child 3 (bf-2nn5x): Verified wiring of config to CMAP entry creation point
Implementation Quality
Excellent - The implementation is:
- Clean and well-documented
- Handles both
.notdefand/.notdef(with/without leading slash) - Supports both default and custom unmapped glyph sets
- Has comprehensive test coverage
- No errors or partial entries during skip
- Atomic operation - either entry is added or skipped cleanly
Git Commits
7660cfc8- feat(bf-4zgpr): add unmapped_glyph_names config field to CMAP generator84043bd9- docs(bf-3s7gd): document CMAP skip check verification (already implemented)f757d8b6- docs(bf-2nn5x): verify unmapped_glyph_names config wiring to CMAP entry creation
Verification Date
2026-07-06
Conclusion
The skip logic for unmapped glyphs in CMAP entry creation is fully implemented and tested. The implementation prevents unmapped glyphs (like .notdef) from appearing in the CMAP, making them unmappable from text content and ensuring proper GLYPH_UNMAPPED diagnostics instead.