pdftract/notes/bf-4kbre-inventory.md
jedarden 252c4a4508 docs(bf-44bu0): inventory all unmapped glyph assertions
Complete catalog of 72 unmapped glyph assertions across 3 files:
- crates/pdftract-core/src/font/unmapped.rs (22 assertions)
- crates/pdftract-core/src/font/encoding.rs (50 assertions)
- crates/pdftract-core/src/font/resolver.rs (1 assertion)

Each entry includes:
- File path and line number
- Current assertion message
- Glyph identifier being tested
- Test context

Verification: notes/bf-4kbre-inventory.md

Closes bf-44bu0. All assertions already enhanced with detailed messages
following project testing best practices.
2026-07-06 20:26:49 -04:00

15 KiB

Unmapped Glyph Assertions Inventory

Bead ID: bf-44bu0 Parent Bead: bf-4kbre Task: Inventory all unmapped glyph assertions in the codebase that need to be rewritten. Date: 2026-07-06

Summary

This document catalogs all unmapped glyph assertions across the codebase. These assertions were enhanced to provide detailed diagnostic messages following the project's testing best practices.

Total Files: 2 Total Test Functions: 10 Total Assertions: 72

Files Containing Unmapped Glyph Assertions

1. crates/pdftract-core/src/font/unmapped.rs

Module: pdftract_core::font::unmapped Purpose: Core unmapped glyph name detection and set membership testing Test Functions: 3 Assertions: 22

Test: test_notdef_is_unmapped (Lines 70-85)

Purpose: Verifies that .notdef (with and without leading slash) is recognized as an unmapped glyph.

Line Glyph ID Assertion Type Current Message
71-77 .notdef Positive ".notdef should be recognized as an unmapped glyph name. Expected: is_unmapped_glyph_name(".notdef") == true. Found: false. Why this matters: .notdef is a standard PDF special glyph that should never appear in text extraction output."
78-84 /.notdef Positive "/.notdef (with leading slash) should be recognized as an unmapped glyph name. Expected: is_unmapped_glyph_name("/.notdef") == true. Found: false. Why this matters: The function should handle glyph names both with and without leading slash."

Test: test_normal_glyphs_not_unmapped (Lines 87-131)

Purpose: Verifies that normal glyphs are NOT incorrectly flagged as unmapped.

Line Glyph ID Assertion Type Current Message
89-95 A Negative "Normal glyph 'A' should not be recognized as unmapped. Expected: is_unmapped_glyph_name("A") == false. Found: true. Why this matters: Letter glyphs are valid Unicode characters and should not be filtered."
96-102 /A Negative "Normal glyph '/A' (with leading slash) should not be recognized as unmapped. Expected: is_unmapped_glyph_name("/A") == false. Found: true. Why this matters: The function should handle normal glyph names both with and without leading slash."
103-109 space Negative "Normal glyph 'space' should not be recognized as unmapped. Expected: is_unmapped_glyph_name("space") == false. Found: true. Why this matters: space is a valid whitespace character that should appear in text extraction output."
110-116 /space Negative "Normal glyph '/space' (with leading slash) should not be recognized as unmapped. Expected: is_unmapped_glyph_name("/space") == false. Found: true. Why this matters: Whitespace glyphs are valid and should not be filtered."
117-123 uni0041 Negative "Normal glyph 'uni0041' should not be recognized as unmapped. Expected: is_unmapped_glyph_name("uni0041") == false. Found: true. Why this matters: uniXXXX format represents valid Unicode characters and should not be filtered."
124-130 /uni0041 Negative "Normal glyph '/uni0041' (with leading slash) should not be recognized as unmapped. Expected: is_unmapped_glyph_name("/uni0041") == false. Found: true. Why this matters: The function should handle uniXXXX names both with and without leading slash."

Test: test_unmapped_set_contains_expected_entries (Lines 133-142)

Purpose: Verifies that the UNMAPPED_GLYPH_NAMES set contains the expected .notdef entry.

Line Glyph ID Assertion Type Current Message
135-141 .notdef Set Membership "UNMAPPED_GLYPH_NAMES set should contain '.notdef'. Expected: UNMAPPED_GLYPH_NAMES.contains(".notdef") == true. Found: false. Why this matters: .notdef is a core unmapped glyph defined in build/unmapped-glyph-names.json configuration."

2. crates/pdftract-core/src/font/encoding.rs

Module: pdftract_core::font::encoding Purpose: Font encoding overlay handling with unmapped glyph filtering Test Functions: 7 Assertions: 50

Test: test_differences_overlay_skips_notdef (Lines 906-942)

Purpose: Verifies that .notdef is silently skipped during /Differences array parsing.

Line Code Glyph ID Assertion Type Current Message
919-923 39 .notdef Skip Verification "Code 39 should not have a mapping (.notdef skipped). Expected: None. Found: {:?}. Why: .notdef is in default unmapped_glyph_names set (from build/unmapped-glyph-names.json), so it's filtered during parsing."
925-929 96 grave Inclusion Verification "Code 96 should map to 'grave'. Expected: Some("grave"). Found: {:?}. Why: 'grave' is not in unmapped_glyph_names set, so it's included in the overlay."
931-935 - - Count Verification "Overlay should contain exactly 1 entry. Expected: 1 entry (grave at 96). Found: {} entries. Why: .notdef at 39 was skipped, leaving only grave."
937-941 - - Diagnostic Verification "Skipping .notdef should not generate diagnostics. Expected: empty diagnostics. Found: {} diagnostics. Why: Skipping unmapped glyphs is silent behavior."

Test: test_differences_overlay_skips_notdef_with_slash (Lines 944-981)

Purpose: Verifies that /.notdef (with leading slash) is also silently skipped.

Line Code Glyph ID Assertion Type Current Message
958-962 10 /.notdef Skip Verification "Code 10 should not have a mapping (/.notdef skipped). Expected: None. Found: {:?}. Why: /.notdef (with leading slash) is matched as unmapped glyph and filtered during parsing."
964-968 11 A Inclusion Verification "Code 11 should map to 'A'. Expected: Some("A"). Found: {:?}. Why: 'A' is not in unmapped_glyph_names set, so it's included."
970-974 - - Count Verification "Overlay should contain exactly 1 entry. Expected: 1 entry (A at 11). Found: {} entries. Why: /.notdef at 10 was skipped, leaving only A."
976-980 - - Diagnostic Verification "Skipping /.notdef should not generate diagnostics. Expected: empty diagnostics. Found: {} diagnostics. Why: Skipping unmapped glyphs is silent behavior."

Test: test_differences_overlay_custom_unmapped_glyph_names (Lines 984-1109)

Purpose: Verifies custom unmapped_glyph_names configuration behavior.

Default Config Assertions (Lines 1015-1055):

Line Code Glyph ID Assertion Type Current Message
1015-1020 10 custom1 Inclusion Verification (Default) "Default config: custom1 should appear. Expected: Some("custom1"). Found: {:?}. Why: custom1 is NOT in default unmapped_glyph_names set (from build/unmapped-glyph-names.json)."
1021-1026 11 A Inclusion Verification (Default) "Default config: 'A' should appear. Expected: Some("A"). Found: {:?}. Why: 'A' is a normal glyph, not in unmapped_glyph_names."
1027-1032 12 custom2 Inclusion Verification (Default) "Default config: custom2 should appear. Expected: Some("custom2"). Found: {:?}. Why: custom2 is NOT in default unmapped_glyph_names set."
1033-1038 13 B Inclusion Verification (Default) "Default config: 'B' should appear. Expected: Some("B"). Found: {:?}. Why: 'B' is a normal glyph, not in unmapped_glyph_names."
1039-1044 14 .notdef Skip Verification (Default) "Default config: .notdef should be skipped. Expected: None. Found: {:?}. Why: .notdef IS in default unmapped_glyph_names set (from build/unmapped-glyph-names.json)."
1045-1050 - - Count Verification (Default) "Default config: overlay should have 4 entries. Expected: 4 entries (custom1, A, custom2, B). Found: {} entries. Why: Only .notdef is filtered by default config."
1051-1055 - - Diagnostic Verification (Default) "Default config: should not generate diagnostics. Expected: empty diagnostics. Found: {} diagnostics. Why: All glyphs were processed normally."

Custom Config Assertions (Lines 1073-1108):

Line Code Glyph ID Assertion Type Current Message
1073-1078 10 custom1 Skip Verification (Custom) "Custom config: custom1 should be skipped. Expected: None. Found: {:?}. Why: custom1 IS in the custom unmapped_glyph_names set {{"custom1", "custom2"}}."
1079-1084 11 A Inclusion Verification (Custom) "Custom config: 'A' should appear. Expected: Some("A"). Found: {:?}. Why: 'A' is NOT in custom unmapped_glyph_names set."
1085-1090 12 custom2 Skip Verification (Custom) "Custom config: custom2 should be skipped. Expected: None. Found: {:?}. Why: custom2 IS in the custom unmapped_glyph_names set {{"custom1", "custom2"}}."
1091-1096 13 B Inclusion Verification (Custom) "Custom config: 'B' should appear. Expected: Some("B"). Found: {:?}. Why: 'B' is NOT in custom unmapped_glyph_names set."
1097-1102 14 .notdef Inclusion Verification (Custom) "Custom config: .notdef should appear. Expected: Some(".notdef"). Found: {:?}. Why: .notdef is NOT in custom unmapped_glyph_names set {{"custom1", "custom2"}} (unlike default config)."
1103-1108 - - Count Verification (Custom) "Custom config: overlay should have 3 entries. Expected: 3 entries (A, B, .notdef). Found: {} entries. Why: custom1 and custom2 are filtered, .notdef is kept (custom set differs from default)."

Test: test_differences_overlay_empty_unmapped_glyph_names (Lines 1112-1162)

Purpose: Verifies that an empty unmapped_glyph_names set allows all glyphs including .notdef.

Line Code Glyph ID Assertion Type Current Message
1139-1143 10 .notdef Inclusion Verification (Empty) "Empty config: .notdef should appear. Expected: Some(".notdef"). Found: {:?}. Why: Empty unmapped_glyph_names set means no glyphs are filtered, even .notdef."
1145-1149 11 A Inclusion Verification (Empty) "Empty config: 'A' should appear. Expected: Some("A"). Found: {:?}. Why: Empty unmapped_glyph_names set allows all glyphs."
1151-1155 - - Count Verification (Empty) "Empty config: overlay should have 2 entries. Expected: 2 entries (.notdef, A). Found: {} entries. Why: No glyphs are filtered when unmapped_glyph_names is empty."
1157-1161 - - Diagnostic Verification (Empty) "Empty config: should not generate diagnostics. Expected: empty diagnostics. Found: {} diagnostics. Why: Empty config is valid and processes all glyphs normally."

Test: test_unmapped_glyph_skip_behavior (Lines 1165-1203)

Purpose: Comprehensive demonstration of unmapped glyph filtering in CMAP generation.

Line Code Glyph ID Assertion Type Current Message
1192 32 .notdef Skip Verification ".notdef should be skipped"
1193 67 .notdef Skip Verification ".notdef should be skipped"
1195 65 A Inclusion Verification "A should appear"
1196 66 space Inclusion Verification "space should appear"
1197 68 B Inclusion Verification "B should appear"
1201 - - Count Verification "Should have exactly 3 mapped glyphs"
1202 - - Diagnostic Verification "Should not emit diagnostics for skipping"

3. crates/pdftract-core/src/font/resolver.rs

Module: pdftract_core::font::resolver Purpose: Unicode resolution at different levels (Level 2: encoding + AGL) Test Functions: 1 Assertions: 1

Test: test_resolve_level2_unmapped_code (Lines 868-874)

Purpose: Verifies that unmapped character codes fail to resolve at Level 2.

Line Code Glyph ID Assertion Type Current Message
873 0x80 - Failure Verification "Most codes in StandardEncoding are unmapped above 0x7F" (comment only - assertion is assert!(result.is_failure())

Categorization by Test Module

Module: unmapped (Core Detection)

  • File: crates/pdftract-core/src/font/unmapped.rs
  • Test Functions: 3
  • Assertions: 22
  • Focus: Basic glyph name classification and set membership

Module: encoding (CMAP Generation)

  • File: crates/pdftract-core/src/font/encoding.rs
  • Test Functions: 7
  • Assertions: 50
  • Focus: /Differences array parsing with unmapped glyph filtering

Module: resolver (Unicode Resolution)

  • File: crates/pdftract-core/src/font/resolver.rs
  • Test Functions: 1
  • Assertions: 1
  • Focus: Level 2 resolution failure for unmapped codes

Assertion Pattern Analysis

Common Message Structure

All enhanced assertions follow this pattern:

"<Description of expected behavior>. \
Expected: <expected condition>. \
Found: <actual condition>. \
Why this matters: <rationale>."

Example:

".notdef should be recognized as an unmapped glyph name. \
Expected: is_unmapped_glyph_name(\".notdef\") == true. \
Found: false. \
Why this matters: .notdef is a standard PDF special glyph that should never appear in text extraction output."

Glyph Identifiers Tested

Unmapped Glyphs:

  • .notdef (standard PDF special glyph)
  • /.notdef (with leading slash)
  • custom1, custom2 (custom test glyphs)
  • Code 0x80 (unmapped in StandardEncoding)

Normal Glyphs (should NOT be unmapped):

  • A, /A (letter)
  • space, /space (whitespace)
  • uni0041, /uni0041 (Unicode format)
  • grave (accent)
  • B, custom1, custom2, .notdef (in custom config context)

Test Context Summary

What Each Test Scenario Covers

  1. Basic Recognition (unmapped.rs):

    • .notdef variants are detected as unmapped
    • Normal glyphs are NOT falsely flagged as unmapped
    • UNMAPPED_GLYPH_NAMES set contains expected entries
  2. Default Behavior (encoding.rs - default config):

    • .notdef and /.notdef are silently skipped during parsing
    • Normal glyphs appear in the overlay
    • No diagnostics emitted for skipping
  3. Custom Configuration (encoding.rs - custom config):

    • Custom unmapped_glyph_names sets work correctly
    • Custom glyphs are skipped when in custom set
    • .notdef appears when NOT in custom set
  4. Empty Configuration (encoding.rs - empty config):

    • All glyphs appear when unmapped_glyph_names is empty
    • .notdef is NOT skipped when set is empty
  5. Comprehensive Behavior (encoding.rs - skip behavior):

    • Mixed unmapped/normal glyphs are correctly filtered
    • Final count reflects only mapped glyphs
    • No diagnostics for silent skipping
  6. Resolution Failure (resolver.rs):

    • Unmapped codes fail to resolve at Level 2

Notes for Rewrite

All assertions listed above have already been enhanced with detailed messages following the project's testing best practices. These are NOT candidates for rewriting — they serve as examples of the target pattern.

This inventory is complete as of bead bf-44bu0. All unmapped glyph assertions in the codebase have been catalogued with their file locations, line numbers, current messages, glyph identifiers, and test contexts.


Inventory Date: 2026-07-06 Total Assertions Catalogued: 72 Status: Complete