10003 lines
265 KiB
TOML
10003 lines
265 KiB
TOML
# PrintCraft parity checklist vs Acrobat Pro (offline feature set). Generated from plan docs; maintained by hand.
|
||
# Validated and reported by `cargo xtask parity`. Schema: see the [schema] table.
|
||
# Status rules: `shipped` = usable today with at least one cited test; `partial` = part exists (notes say what is
|
||
# missing); `planned` = not started; `na` = Adobe-cloud-only (notes name the local or pluggable alternative).
|
||
# `commands` lists registry ids from crates/engine/src/commands.rs only; `tools` lists crates/automation/src/tools.rs names.
|
||
[schema]
|
||
version = 1
|
||
statuses = ["planned", "partial", "shipped", "na"]
|
||
tiers = ["P0", "P1", "P2", "P3", "N/A"]
|
||
areas = { A = "core", B = "view", C = "edit", D = "organize", E = "comment", F = "form", G = "protect", H = "sign", I = "ocr", J = "create", K = "optimize", L = "print", M = "a11y", N = "misc" }
|
||
|
||
# ==================================================================================================
|
||
# A. Core document model and fidelity
|
||
# ==================================================================================================
|
||
|
||
[[feature]]
|
||
id = "core.open-pdf"
|
||
title = "Open PDF files"
|
||
area = "A"
|
||
tier = "P0"
|
||
milestone = "M1"
|
||
status = "shipped"
|
||
commands = ["file.open"]
|
||
tools = ["doc_open"]
|
||
evidence = ["crates/automation/tests/automation.rs::open_inspect_render_and_find", "crates/ui-egui/tests/ui.rs::opening_a_pdf_shows_comments_and_bookmarks", "crates/ui-egui/tests/ui.rs::files_and_quit_from_the_operating_system"]
|
||
notes = "File ▸ Open, the command line (Linux and Windows file associations), and on macOS Finder, Open With and the Dock (Apple events; the bundle declares PDF as a document type)"
|
||
|
||
[[feature]]
|
||
id = "core.repair-broken-files"
|
||
title = "Open damaged files by reconstructing the cross-reference table"
|
||
area = "A"
|
||
tier = "P0"
|
||
milestone = "M1"
|
||
status = "shipped"
|
||
commands = ["file.open"]
|
||
tools = ["doc_open"]
|
||
evidence = ["crates/cos/src/document.rs::damaged_xref_is_reconstructed", "crates/cos/src/parser.rs::indirect_stream_with_wrong_length_recovers", "crates/cos/src/document.rs::self_referential_length_does_not_overflow"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.malformed-input-safety"
|
||
title = "Reject garbage or hostile input without crashing"
|
||
area = "A"
|
||
tier = "P0"
|
||
milestone = "M1"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/ui.rs::garbage_input_is_rejected_without_panicking", "crates/cos/src/parser.rs::garbage_never_panics", "crates/render/src/raster.rs::missing_page_and_garbage_input_fail_gracefully"]
|
||
notes = "fuzzed nightly (M1.10)"
|
||
|
||
[[feature]]
|
||
id = "core.repair-log"
|
||
title = "Record repairs and show them to the user"
|
||
area = "A"
|
||
tier = "P0"
|
||
milestone = "M1"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = ["doc_info"]
|
||
evidence = ["crates/ui-egui/tests/viewer.rs::damaged_files_say_they_were_repaired"]
|
||
notes = "a notice says the file was repaired; Document Properties ▸ Advanced lists the repairs; doc_info reports them"
|
||
|
||
[[feature]]
|
||
id = "core.xref-streams"
|
||
title = "Read cross-reference streams and compressed object streams"
|
||
area = "A"
|
||
tier = "P0"
|
||
milestone = "M1"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/cos/tests/writer.rs::incremental_saves_stack_on_an_xref_stream_file", "crates/cos/tests/writer.rs::page_tree_loops_in_object_streams_do_not_crash_readers"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.hybrid-xref"
|
||
title = "Hybrid-reference files (/XRefStm) and quirky xref subsections"
|
||
area = "A"
|
||
tier = "P0"
|
||
milestone = "M1"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = ["doc_open"]
|
||
evidence = ["crates/cos/src/document.rs::hybrid_reference_files_read_the_xref_stream_too"]
|
||
notes = "/XRefStm entries supplement the classic table and replace its free entries for objects in object streams; each section is merged as a unit so older revisions only fill gaps"
|
||
|
||
[[feature]]
|
||
id = "core.revision-chain"
|
||
title = "Follow /Prev revision chains with loop protection"
|
||
area = "A"
|
||
tier = "P0"
|
||
milestone = "M1"
|
||
status = "shipped"
|
||
commands = ["file.properties"]
|
||
tools = ["doc_revisions"]
|
||
evidence = ["crates/cos/src/document.rs::looping_revision_chains_stop", "crates/cos/src/document.rs::revision_ends_split_incremental_updates"]
|
||
notes = "/Prev chains followed newest first with a visited set (loops stop and are logged); revisions listed and opened (doc_revisions, doc_open_revision)"
|
||
|
||
[[feature]]
|
||
id = "core.stream-filters"
|
||
title = "Standard stream filters (Flate, LZW, ASCII85, ASCIIHex, RunLength, predictors)"
|
||
area = "A"
|
||
tier = "P0"
|
||
milestone = "M1"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/filters/src/lib.rs::chain_three_filters_with_predictor", "crates/filters/src/lzw.rs::spec_example", "crates/filters/src/predictor.rs::png_hand_vectors"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.image-filters"
|
||
title = "Image codecs in the filters crate (DCT, JPX, JBIG2, CCITT)"
|
||
area = "A"
|
||
tier = "P0"
|
||
milestone = "M1"
|
||
status = "partial"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "decoded through the hayro bootstrap for display; own codecs are M1.1/M1.2"
|
||
|
||
[[feature]]
|
||
id = "core.decompression-limits"
|
||
title = "Bounded decompression (filter bomb protection)"
|
||
area = "A"
|
||
tier = "P0"
|
||
milestone = "M1"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/filters/src/flate.rs::bomb_is_bounded", "crates/filters/src/lib.rs::limit_applies_to_intermediate_buffers"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.open-rc4"
|
||
title = "Open RC4-encrypted documents (40/128-bit)"
|
||
area = "A"
|
||
tier = "P0"
|
||
milestone = "M1"
|
||
status = "shipped"
|
||
commands = ["file.open"]
|
||
tools = ["doc_open"]
|
||
evidence = ["crates/crypt/src/tests.rs::rc4_reference_vectors", "crates/cos/tests/encryption.rs::every_algorithm_round_trips_and_hides_the_content"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.open-aes"
|
||
title = "Open AES-128 and AES-256 encrypted documents (R4-R6)"
|
||
area = "A"
|
||
tier = "P0"
|
||
milestone = "M1"
|
||
status = "shipped"
|
||
commands = ["file.open"]
|
||
tools = ["doc_open"]
|
||
evidence = ["crates/crypt/src/tests.rs::aes_cbc_nist_sp800_38a_vectors", "crates/cos/tests/encryption.rs::every_algorithm_round_trips_and_hides_the_content"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.password-prompt"
|
||
title = "Ask for the user or owner password, with retry"
|
||
area = "A"
|
||
tier = "P0"
|
||
milestone = "M1"
|
||
status = "shipped"
|
||
commands = ["file.open"]
|
||
tools = ["doc_open"]
|
||
evidence = ["crates/ui-egui/tests/editing.rs::password_prompt_opens_and_security_tab_reports_the_details", "crates/crypt/src/tests.rs::empty_user_password_opens_without_asking"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.unicode-passwords"
|
||
title = "Unicode passwords normalised with SASLprep"
|
||
area = "A"
|
||
tier = "P0"
|
||
milestone = "M1"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/crypt/src/tests.rs::r6_passwords_are_saslprepped"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.owner-password-unlock"
|
||
title = "Owner password lifts document restrictions"
|
||
area = "A"
|
||
tier = "P0"
|
||
milestone = "M1"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = ["doc_open"]
|
||
evidence = ["crates/engine/src/tests.rs::restricted_documents_refuse_changes_unless_opened_by_the_owner", "crates/engine/src/tests.rs::owner_password_of_older_revisions_opens_the_viewer_too", "crates/crypt/src/tests.rs::owner_authentication_recovers_the_user_password"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.permissions-enforced"
|
||
title = "Honour document permissions (print, modify, assemble, copy)"
|
||
area = "A"
|
||
tier = "P0"
|
||
milestone = "M1"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/editing.rs::restricted_documents_show_a_notice_and_block_page_changes", "crates/crypt/src/tests.rs::permission_bits_follow_table_22", "crates/engine/src/tests.rs::combining_protected_files_is_refused_clearly"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.crypt-filters"
|
||
title = "Crypt filters (Identity, /StmF, /StrF)"
|
||
area = "A"
|
||
tier = "P0"
|
||
milestone = "M1"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/crypt/src/tests.rs::identity_crypt_filters_leave_data_alone"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.attachment-only-encryption"
|
||
title = "Attachment-only encryption (/EFF) opens without a password"
|
||
area = "A"
|
||
tier = "P1"
|
||
milestone = "M1"
|
||
status = "partial"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "two corpus files still ask for a password (M1.6)"
|
||
|
||
[[feature]]
|
||
id = "core.unencrypted-metadata"
|
||
title = "Leave XMP metadata readable when /EncryptMetadata is false"
|
||
area = "A"
|
||
tier = "P1"
|
||
milestone = "M1"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/cos/tests/encryption.rs::unencrypted_metadata_is_left_readable"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.unsupported-handlers"
|
||
title = "Clear message for unsupported security handlers"
|
||
area = "A"
|
||
tier = "P1"
|
||
milestone = "M1"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/crypt/src/tests.rs::unsupported_handlers_are_reported"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.open-public-key"
|
||
title = "Open certificate-encrypted (public-key handler) documents"
|
||
area = "A"
|
||
tier = "P1"
|
||
milestone = "M8"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.incremental-save"
|
||
title = "Save as an incremental update (original bytes kept, atomic write)"
|
||
area = "A"
|
||
tier = "P0"
|
||
milestone = "M1"
|
||
status = "shipped"
|
||
commands = ["file.save"]
|
||
tools = ["doc_save"]
|
||
evidence = ["crates/ui-egui/tests/editing.rs::save_writes_an_incremental_update_and_clears_dirty", "crates/engine/src/tests.rs::save_is_incremental_stamps_mod_date_and_rebases", "crates/organize/src/tests.rs::successive_incremental_saves_stack_revisions"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.save-as-full-rewrite"
|
||
title = "Save as a new file with a full rewrite and garbage collection"
|
||
area = "A"
|
||
tier = "P0"
|
||
milestone = "M1"
|
||
status = "shipped"
|
||
commands = ["file.save_as"]
|
||
tools = ["doc_save"]
|
||
evidence = ["crates/organize/src/tests.rs::full_save_drops_unreachable_objects", "crates/engine/src/tests.rs::full_save_is_readable_and_smaller_after_deletes"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.object-stream-compression"
|
||
title = "Compress objects into object streams behind an xref stream on full save"
|
||
area = "A"
|
||
tier = "P0"
|
||
milestone = "M1"
|
||
status = "shipped"
|
||
commands = ["file.save_as"]
|
||
tools = ["doc_save"]
|
||
evidence = ["crates/cos/tests/writer.rs::full_save_packs_objects_into_object_streams", "crates/cos/tests/writer.rs::qpdf_accepts_object_stream_output", "crates/cos/tests/writer.rs::objects_referenced_by_the_encrypt_dictionary_stay_outside_object_streams"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.encrypted-save"
|
||
title = "Save encrypted documents re-encrypted under the same keys"
|
||
area = "A"
|
||
tier = "P0"
|
||
milestone = "M1"
|
||
status = "shipped"
|
||
commands = ["file.save"]
|
||
tools = ["doc_save"]
|
||
evidence = ["crates/cos/tests/encryption.rs::incremental_edits_of_encrypted_documents_stay_encrypted", "crates/engine/src/tests.rs::encrypted_documents_open_edit_and_save_encrypted"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.copy-on-write-fidelity"
|
||
title = "Preserve untouched objects byte-for-byte (copy-on-write edits)"
|
||
area = "A"
|
||
tier = "P0"
|
||
milestone = "M1"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/cos/src/document.rs::reads_classic_revision_and_edits_are_copy_on_write"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.write-validity"
|
||
title = "Written files pass qpdf --check"
|
||
area = "A"
|
||
tier = "P0"
|
||
milestone = "M1"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/cos/tests/encryption.rs::qpdf_agrees", "crates/cos/tests/writer.rs::qpdf_accepts_object_stream_output"]
|
||
notes = "skipped when qpdf is absent"
|
||
|
||
[[feature]]
|
||
id = "core.deterministic-save"
|
||
title = "Deterministic full-save mode"
|
||
area = "A"
|
||
tier = "P2"
|
||
milestone = "M1"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "M1.9 option"
|
||
|
||
[[feature]]
|
||
id = "core.linearization"
|
||
title = "Linearized save (Fast Web View)"
|
||
area = "A"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.revision-list"
|
||
title = "List document revisions from the xref chain"
|
||
area = "A"
|
||
tier = "P1"
|
||
milestone = "M1"
|
||
status = "shipped"
|
||
commands = ["file.properties"]
|
||
tools = ["doc_revisions"]
|
||
evidence = ["crates/cos/src/document.rs::revision_ends_split_incremental_updates", "crates/automation/tests/automation.rs::digital_ids_signing_and_validation_through_tools"]
|
||
notes = "revision ends from the xref chain (linearized first-page sections fold into their revision); listed in Document Properties ▸ Advanced and by doc_revisions with the signatures covering each"
|
||
|
||
[[feature]]
|
||
id = "core.view-revision"
|
||
title = "View an earlier revision as a read-only snapshot"
|
||
area = "A"
|
||
tier = "P1"
|
||
milestone = "M9"
|
||
status = "shipped"
|
||
commands = ["file.properties"]
|
||
tools = ["doc_open_revision"]
|
||
evidence = ["crates/ui-egui/tests/viewer.rs::an_earlier_revision_opens_from_document_properties", "crates/automation/tests/automation.rs::digital_ids_signing_and_validation_through_tools"]
|
||
notes = "opens the revision's bytes as a new unsaved document named '<name> (revision n)'"
|
||
|
||
[[feature]]
|
||
id = "core.lazy-loading"
|
||
title = "Lazy loading of very large (GB) files within a memory budget"
|
||
area = "A"
|
||
tier = "P0"
|
||
milestone = "M1"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "ByteSource (M1.5); files are buffered whole today"
|
||
|
||
[[feature]]
|
||
id = "core.pdf20-utf8-strings"
|
||
title = "PDF 2.0 UTF-8 text strings"
|
||
area = "A"
|
||
tier = "P1"
|
||
milestone = "M2"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.pdf20-associated-files"
|
||
title = "PDF 2.0 associated files (/AF)"
|
||
area = "A"
|
||
tier = "P1"
|
||
milestone = "M2"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.pdf20-dpart"
|
||
title = "PDF 2.0 document parts (DPart)"
|
||
area = "A"
|
||
tier = "P2"
|
||
milestone = "M2"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.pdf20-namespaces"
|
||
title = "PDF 2.0 structure namespaces"
|
||
area = "A"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.arlington-validation"
|
||
title = "Structural validation against the Arlington PDF model"
|
||
area = "A"
|
||
tier = "P1"
|
||
milestone = "M2"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.undo-redo"
|
||
title = "Undo and redo every document edit"
|
||
area = "A"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["edit.undo", "edit.redo"]
|
||
tools = ["edit_undo", "edit_redo"]
|
||
evidence = ["crates/engine/src/tests.rs::undo_and_redo_restore_exact_states", "crates/ui-egui/tests/editing.rs::organize_delete_then_undo_and_redo_with_keys"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.undo-labels"
|
||
title = "Edit menu names the step to undo or redo"
|
||
area = "A"
|
||
tier = "P1"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["edit.undo", "edit.redo"]
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/editing.rs::edit_menu_names_the_step_to_undo", "crates/engine/src/commands.rs::enablement_follows_the_document_state"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.undo-after-save"
|
||
title = "Undo still works after saving"
|
||
area = "A"
|
||
tier = "P1"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["edit.undo"]
|
||
tools = ["edit_undo"]
|
||
evidence = ["crates/engine/src/tests.rs::undo_still_works_after_save"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.atomic-batch-edits"
|
||
title = "Multi-step edits are one undo step and all-or-nothing"
|
||
area = "A"
|
||
tier = "P1"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/engine/src/tests.rs::batch_is_one_undo_step_and_all_or_nothing", "crates/engine/src/tests.rs::failed_edit_changes_nothing"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.history-panel"
|
||
title = "History panel with named snapshots"
|
||
area = "A"
|
||
tier = "P2"
|
||
milestone = "M4"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.autosave"
|
||
title = "Autosave changed documents periodically"
|
||
area = "A"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/engine/src/tests.rs::autosave_snapshots_only_changed_documents", "crates/ui-egui/tests/recovery.rs::saving_or_closing_clears_the_entry_and_nothing_is_written_for_clean_documents"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.crash-recovery"
|
||
title = "Offer to recover unsaved work after a crash"
|
||
area = "A"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/recovery.rs::a_crashed_session_is_offered_and_recovered_unsaved", "crates/ui-egui/tests/recovery.rs::discarding_removes_the_autosaves", "crates/engine/src/tests.rs::recovered_documents_reopen_unsaved_at_their_original_path"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.encrypted-autosave"
|
||
title = "Autosaves of encrypted documents stay encrypted"
|
||
area = "A"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/recovery.rs::encrypted_documents_are_autosaved_encrypted_and_recovered_with_the_password"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.web-recovery"
|
||
title = "Crash recovery in the web app"
|
||
area = "A"
|
||
tier = "P1"
|
||
milestone = "M4"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.unsaved-changes-prompt"
|
||
title = "Ask before closing or quitting with unsaved changes"
|
||
area = "A"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["file.close"]
|
||
tools = ["doc_close"]
|
||
evidence = ["crates/ui-egui/tests/editing.rs::closing_a_dirty_tab_asks_and_cancel_keeps_it", "crates/ui-egui/tests/editing.rs::closing_a_dirty_tab_can_save_first", "crates/ui-egui/tests/editing.rs::quitting_with_unsaved_changes_asks_for_each_document"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.dirty-indicator"
|
||
title = "Unsaved-changes marker on document tabs"
|
||
area = "A"
|
||
tier = "P1"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/editing.rs::organize_delete_then_undo_and_redo_with_keys", "crates/engine/src/tests.rs::edits_update_view_data_and_mark_dirty"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.close-document"
|
||
title = "Close a document"
|
||
area = "A"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = ["file.close"]
|
||
tools = ["doc_close"]
|
||
evidence = ["crates/ui-egui/tests/editing.rs::clean_tabs_close_without_asking"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "core.close-all"
|
||
title = "Close all documents"
|
||
area = "A"
|
||
tier = "P1"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = ["file.close_all"]
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/viewer.rs::close_all_asks_only_for_changed_documents"]
|
||
notes = "clean documents close; each changed one asks"
|
||
|
||
[[feature]]
|
||
id = "core.revert"
|
||
title = "Revert to the last saved version"
|
||
area = "A"
|
||
tier = "P1"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["file.revert"]
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/viewer.rs::revert_after_confirming", "crates/engine/src/tests.rs::revert_goes_back_to_the_saved_version"]
|
||
notes = "with confirmation; clears undo, as Acrobat"
|
||
|
||
[[feature]]
|
||
id = "core.signed-save-policy"
|
||
title = "Force incremental saves for signed documents"
|
||
area = "A"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["file.save"]
|
||
tools = ["doc_save"]
|
||
evidence = ["crates/engine/src/tests.rs::signing_saving_trusting_and_commenting_afterwards"]
|
||
notes = "saves of signed documents are always incremental: full rewrites are refused, and edits that would need one (applying redactions, changing security, sanitizing) are refused before they change anything"
|
||
|
||
[[feature]]
|
||
id = "core.drag-drop-open"
|
||
title = "Open files by drag and drop"
|
||
area = "A"
|
||
tier = "P1"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/ui.rs::dropping_a_pdf_on_the_window_opens_it"]
|
||
notes = "desktop: the dropped file opens in a tab (by path, or its bytes); web: read asynchronously"
|
||
|
||
[[feature]]
|
||
id = "core.recent-files"
|
||
title = "Local recent-files list"
|
||
area = "A"
|
||
tier = "P1"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/ui.rs::persistence_round_trips_and_tolerates_garbage"]
|
||
notes = "never reads other apps' lists"
|
||
|
||
[[feature]]
|
||
id = "core.usage-rights-preserve"
|
||
title = "Preserve usage-rights (UR3) signatures from Reader-extended files"
|
||
area = "A"
|
||
tier = "P3"
|
||
milestone = "M9"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "preserve only"
|
||
|
||
# ==================================================================================================
|
||
# B. Viewing and navigation
|
||
# ==================================================================================================
|
||
|
||
[[feature]]
|
||
id = "view.render-pages"
|
||
title = "Render pages (text, vectors, images)"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M2"
|
||
status = "partial"
|
||
commands = []
|
||
tools = ["page_render"]
|
||
evidence = ["crates/render/src/raster.rs::renders_a_blue_rectangle", "crates/automation/tests/automation.rs::open_inspect_render_and_find"]
|
||
notes = "hayro bootstrap renderer; own DisplayList devices in M2.6"
|
||
|
||
[[feature]]
|
||
id = "view.render-shadings-patterns"
|
||
title = "Shadings, gradients and tiling patterns"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M2"
|
||
status = "partial"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/render/src/raster.rs::self_referencing_tiling_pattern_terminates"]
|
||
notes = "via hayro bootstrap"
|
||
|
||
[[feature]]
|
||
id = "view.render-transparency"
|
||
title = "Blend modes, soft masks and transparency groups"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M2"
|
||
status = "partial"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/render/src/raster.rs::soft_mask_without_group_colour_space_is_applied"]
|
||
notes = "via hayro bootstrap"
|
||
|
||
[[feature]]
|
||
id = "view.render-knockout"
|
||
title = "Knockout transparency groups"
|
||
area = "B"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = ["doc_save"]
|
||
evidence = ["crates/engine/src/tests.rs::signing_saving_trusting_and_commenting_afterwards", "crates/automation/tests/automation.rs::digital_ids_signing_and_validation_through_tools"]
|
||
notes = "full rewrites of signed documents are refused; saves stay incremental"
|
||
|
||
[[feature]]
|
||
id = "view.render-type3"
|
||
title = "Type 3 fonts"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M2"
|
||
status = "partial"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "hayro bootstrap with vendored patches"
|
||
|
||
[[feature]]
|
||
id = "view.render-cid-fonts"
|
||
title = "CID fonts and CMaps incl. CJK without ToUnicode"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M2"
|
||
status = "partial"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/render/src/raster.rs::huge_cid_width_ranges_terminate"]
|
||
notes = "CID-to-Unicode tables are M2.4"
|
||
|
||
[[feature]]
|
||
id = "view.render-image-codecs"
|
||
title = "JBIG2, JPEG 2000, CCITT and JPEG images"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M2"
|
||
status = "partial"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "hayro bootstrap"
|
||
|
||
[[feature]]
|
||
id = "view.render-annotation-appearances"
|
||
title = "Annotation and widget appearances incl. appearance states"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M2"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/render/src/raster.rs::widget_appearance_states"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.render-error-isolation"
|
||
title = "Per-page error isolation (a bad page never takes down the app)"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/render/src/raster.rs::missing_page_and_garbage_input_fail_gracefully", "crates/render/src/raster.rs::scale_is_clamped_for_huge_pages", "crates/render/src/raster.rs::watchdog_skips_a_stuck_page_and_keeps_rendering"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.render-priority"
|
||
title = "Render visible pages first, then neighbours and thumbnails"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/render/src/raster.rs::inline_pool_renders_in_priority_order"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.deep-zoom-tiles"
|
||
title = "Tiled rendering keeps deep zoom sharp"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "partial"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/render/src/raster.rs::tiles_match_full_render", "crates/render/src/raster.rs::effective_scale_never_exceeds_the_caps"]
|
||
notes = "capped at 8192 px per side (M3.3)"
|
||
|
||
[[feature]]
|
||
id = "view.layout-continuous"
|
||
title = "Single page continuous scrolling"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/view.rs::continuous_layout_stacks_pages_vertically"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.layout-single-page"
|
||
title = "Single page view"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/view.rs::single_page_layout_shows_one_page_at_a_time"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.layout-two-up"
|
||
title = "Two-page view"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/view.rs::two_up_layout_places_pages_side_by_side"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.layout-two-up-scrolling"
|
||
title = "Two-page scrolling"
|
||
area = "B"
|
||
tier = "P1"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/viewer.rs::layouts_fit_height_labels_and_system_theme"]
|
||
notes = "the two-page view scrolls continuously"
|
||
|
||
[[feature]]
|
||
id = "view.cover-page"
|
||
title = "Show cover page in two-page view"
|
||
area = "B"
|
||
tier = "P1"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/viewer.rs::layouts_fit_height_labels_and_system_theme"]
|
||
notes = "View ▸ Show cover page in two-page view"
|
||
|
||
[[feature]]
|
||
id = "view.page-gaps"
|
||
title = "Show or hide gaps between pages"
|
||
area = "B"
|
||
tier = "P2"
|
||
milestone = "M3"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.auto-scroll"
|
||
title = "Automatic scrolling"
|
||
area = "B"
|
||
tier = "P2"
|
||
milestone = "M3"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.zoom-in-out"
|
||
title = "Zoom in and out around the cursor (keys, wheel, pinch)"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/ui.rs::zoom_keeps_the_point_under_the_cursor_still"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.zoom-to-percent"
|
||
title = "Zoom to a chosen percentage"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/ui.rs::selection_works_in_every_view_rotation"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.zoom-actual-size"
|
||
title = "Actual size"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/view.rs::actual_size_fit_width_and_fit_page"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.zoom-fit-page"
|
||
title = "Fit page"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/view.rs::actual_size_fit_width_and_fit_page"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.zoom-fit-width"
|
||
title = "Fit width"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/view.rs::actual_size_fit_width_and_fit_page"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.zoom-fit-height"
|
||
title = "Fit height"
|
||
area = "B"
|
||
tier = "P1"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/viewer.rs::layouts_fit_height_labels_and_system_theme"]
|
||
notes = "View ▸ Fit to height"
|
||
|
||
[[feature]]
|
||
id = "view.zoom-fit-visible"
|
||
title = "Fit visible content"
|
||
area = "B"
|
||
tier = "P1"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = ["view.fit_visible"]
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/viewer.rs::fit_visible_zooms_to_the_content_width", "crates/ui-egui/src/zoom_snap.rs::ink_bounds_finds_the_marked_area"]
|
||
notes = "View ▸ Zoom ▸ Fit visible (⌘3): the current page's inked area (found by a quick low-resolution render) spans the window width; respects view rotation; drivable via the control channel's command"
|
||
|
||
[[feature]]
|
||
id = "view.marquee-zoom"
|
||
title = "Marquee zoom tool"
|
||
area = "B"
|
||
tier = "P1"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = ["view.marquee_zoom"]
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/viewer.rs::marquee_zoom_and_snapshot"]
|
||
notes = "drag to fill the window with an area; click to zoom in"
|
||
|
||
[[feature]]
|
||
id = "view.dynamic-zoom"
|
||
title = "Dynamic zoom (drag to zoom)"
|
||
area = "B"
|
||
tier = "P2"
|
||
milestone = "M3"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.loupe"
|
||
title = "Loupe magnifier tool"
|
||
area = "B"
|
||
tier = "P2"
|
||
milestone = "M3"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.pan-and-zoom"
|
||
title = "Pan and zoom overview window"
|
||
area = "B"
|
||
tier = "P2"
|
||
milestone = "M3"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.hand-tool"
|
||
title = "Hand tool for panning"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/view.rs::the_hand_tool_pans_by_dragging"]
|
||
notes = "drag to pan; Grab/Grabbing cursors"
|
||
|
||
[[feature]]
|
||
id = "view.rotate-view"
|
||
title = "Rotate the view without changing the file"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/ui.rs::selection_works_in_every_view_rotation"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.read-mode"
|
||
title = "Read mode (hide tool chrome)"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = ["view.read_mode"]
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/control.rs::inspect_and_click_by_label_and_id", "crates/ui-egui/tests/commands.rs::every_registered_command_is_implemented"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.full-screen"
|
||
title = "Full screen mode"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = ["view.full_screen"]
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/commands.rs::full_screen_toggles_by_shortcut_and_command_and_escape_leaves"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.full-screen-presentation"
|
||
title = "Full-screen presentation options (auto-advance, loop, click to advance)"
|
||
area = "B"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.reflow"
|
||
title = "Reflow text to the window width"
|
||
area = "B"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.page-navigation"
|
||
title = "Page navigation (next, previous, first, last, go to page)"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/control.rs::state_and_view_options", "crates/ui-egui/tests/ui.rs::theme_and_view_options_apply"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.goto-page-label"
|
||
title = "Go to a page by its label (logical page numbers)"
|
||
area = "B"
|
||
tier = "P1"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/viewer.rs::layouts_fit_height_labels_and_system_theme"]
|
||
notes = "the page box takes labels (iv, A-1) before numbers"
|
||
|
||
[[feature]]
|
||
id = "view.view-history"
|
||
title = "Previous view / next view"
|
||
area = "B"
|
||
tier = "P1"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/viewer.rs::view_history_select_all_and_find_options"]
|
||
notes = "⌘[ / ⌘] and View ▸ Page navigation"
|
||
|
||
[[feature]]
|
||
id = "view.follow-links"
|
||
title = "Follow links and destinations"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/view.rs::clicking_a_link_goes_to_its_destination"]
|
||
notes = "GoTo and URI links; other action types (JavaScript, Launch, named actions) arrive with M6"
|
||
|
||
[[feature]]
|
||
id = "view.annotation-hover"
|
||
title = "Show comment text on hover"
|
||
area = "B"
|
||
tier = "P1"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/comments.rs::hovering_a_comment_shows_its_author_and_text"]
|
||
notes = "a popup with the author and the comment text, as Acrobat shows"
|
||
|
||
[[feature]]
|
||
id = "view.document-tabs"
|
||
title = "Tabbed documents"
|
||
area = "B"
|
||
tier = "P1"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = ["doc_list"]
|
||
evidence = ["crates/ui-egui/tests/editing.rs::combining_files_opens_a_new_unsaved_tab", "crates/ui-egui/tests/control.rs::state_and_view_options"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.multiple-windows"
|
||
title = "Multiple windows; drag a tab out into a window"
|
||
area = "B"
|
||
tier = "P1"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.split-window"
|
||
title = "Split window (two panes on one document)"
|
||
area = "B"
|
||
tier = "P2"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.spreadsheet-split"
|
||
title = "Spreadsheet split (four synchronised panes)"
|
||
area = "B"
|
||
tier = "P2"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.thumbnails-panel"
|
||
title = "Page thumbnails panel"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/ui.rs::theme_and_view_options_apply"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.thumbnail-context-ops"
|
||
title = "Page operations from the thumbnails panel (drag reorder, context menu)"
|
||
area = "B"
|
||
tier = "P1"
|
||
milestone = "M4"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.bookmarks-panel"
|
||
title = "Bookmarks panel with navigation"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = ["doc_info"]
|
||
evidence = ["crates/ui-egui/tests/ui.rs::opening_a_pdf_shows_comments_and_bookmarks", "crates/ui-egui/tests/control.rs::state_and_view_options"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.highlight-current-bookmark"
|
||
title = "Highlight the current bookmark"
|
||
area = "B"
|
||
tier = "P2"
|
||
milestone = "M4"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.attachments-panel"
|
||
title = "Attachments panel: list, open and save embedded files"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = ["doc_info"]
|
||
evidence = ["crates/render/src/inspect.rs::attachments_of_both_kinds_are_listed_and_extracted"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.layers-panel"
|
||
title = "Layers panel: toggle optional content visibility"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = ["doc_info"]
|
||
evidence = ["crates/render/src/raster.rs::layer_override_hides_content", "crates/engine/src/tests.rs::layer_choices_survive_edits"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.layers-reset"
|
||
title = "Reset layers to initial visibility; list for all or current pages"
|
||
area = "B"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.destinations-panel"
|
||
title = "Named destinations panel"
|
||
area = "B"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.articles-panel"
|
||
title = "Articles panel and article reading"
|
||
area = "B"
|
||
tier = "P3"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.model-tree"
|
||
title = "3D model tree panel"
|
||
area = "B"
|
||
tier = "P3"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.page-labels-display"
|
||
title = "Show page labels (i, ii, 1, 2...)"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = ["doc_info"]
|
||
evidence = ["crates/render/src/inspect.rs::roman_and_alpha_labels", "crates/automation/tests/automation.rs::open_inspect_render_and_find"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.find"
|
||
title = "Find text across the document"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = ["edit.find"]
|
||
tools = ["text_find"]
|
||
evidence = ["crates/ui-egui/tests/ui.rs::find_counts_matches_across_pages", "crates/ui-egui/tests/ui.rs::find_reports_no_matches", "crates/automation/tests/automation.rs::open_inspect_render_and_find"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.find-next-previous"
|
||
title = "Find next / previous match"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = ["edit.find"]
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/ui.rs::find_counts_matches_across_pages"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.find-options"
|
||
title = "Find options: whole words, case-sensitive"
|
||
area = "B"
|
||
tier = "P1"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = ["edit.find"]
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/viewer.rs::view_history_select_all_and_find_options"]
|
||
notes = "whole words only, case-sensitive"
|
||
|
||
[[feature]]
|
||
id = "view.find-in-comments-bookmarks"
|
||
title = "Find in comments and bookmarks"
|
||
area = "B"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.find-results-panel"
|
||
title = "Find results list"
|
||
area = "B"
|
||
tier = "P1"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = ["edit.advanced_search"]
|
||
tools = ["text_find"]
|
||
evidence = ["crates/ui-egui/tests/viewer.rs::advanced_search_lists_results_with_context"]
|
||
notes = "Search panel (⇧⌘F): every match grouped by page with its context, whole words / case options, click to go; shares the find bar's search (closing the panel moves it to the bar)"
|
||
|
||
[[feature]]
|
||
id = "view.advanced-search"
|
||
title = "Advanced search window"
|
||
area = "B"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "partial"
|
||
commands = ["edit.advanced_search"]
|
||
tools = ["text_find"]
|
||
evidence = ["crates/ui-egui/tests/viewer.rs::advanced_search_lists_results_with_context"]
|
||
notes = "in the current document, as a side panel; searching other open documents, folders and indexes is still to come"
|
||
|
||
[[feature]]
|
||
id = "view.search-folder"
|
||
title = "Search all PDFs in a folder"
|
||
area = "B"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.search-boolean"
|
||
title = "Boolean, proximity and stemming search"
|
||
area = "B"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.search-index"
|
||
title = "Build and search a full-text index across files"
|
||
area = "B"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.embedded-index"
|
||
title = "Embedded search index"
|
||
area = "B"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.text-select"
|
||
title = "Select text in reading order"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/ui.rs::drag_selects_text_and_copy_returns_it", "crates/ui-egui/tests/ui.rs::selection_works_in_every_view_rotation"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.copy-text"
|
||
title = "Copy selected text"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/ui.rs::drag_selects_text_and_copy_returns_it"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.select-all"
|
||
title = "Select all text on a page"
|
||
area = "B"
|
||
tier = "P1"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/viewer.rs::view_history_select_all_and_find_options"]
|
||
notes = "⌘A on the current page"
|
||
|
||
[[feature]]
|
||
id = "view.copy-with-layout"
|
||
title = "Copy text keeping layout"
|
||
area = "B"
|
||
tier = "P2"
|
||
milestone = "M3"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.snapshot"
|
||
title = "Snapshot tool (copy a region as an image)"
|
||
area = "B"
|
||
tier = "P1"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = ["edit.snapshot"]
|
||
tools = ["page_render"]
|
||
evidence = ["crates/ui-egui/tests/viewer.rs::marquee_zoom_and_snapshot"]
|
||
notes = "copies the area at the current zoom to the clipboard as an image"
|
||
|
||
[[feature]]
|
||
id = "view.rulers"
|
||
title = "Rulers"
|
||
area = "B"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.grid"
|
||
title = "Grid and snap to grid"
|
||
area = "B"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.guides"
|
||
title = "Guides"
|
||
area = "B"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.line-weights"
|
||
title = "Show line weights"
|
||
area = "B"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.cursor-coordinates"
|
||
title = "Cursor coordinates"
|
||
area = "B"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.theme-light-dark"
|
||
title = "Light and dark themes"
|
||
area = "B"
|
||
tier = "P1"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = ["view.theme"]
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/ui.rs::theme_and_view_options_apply"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.theme-system"
|
||
title = "Follow the system theme"
|
||
area = "B"
|
||
tier = "P1"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = ["view.theme"]
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/viewer.rs::layouts_fit_height_labels_and_system_theme"]
|
||
notes = "Display theme ▸ Use system setting (and --theme system)"
|
||
|
||
[[feature]]
|
||
id = "view.home-view"
|
||
title = "Home view with recommended tools and recent files"
|
||
area = "B"
|
||
tier = "P1"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/ui.rs::home_shows_welcome_and_tools"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.all-tools"
|
||
title = "All tools catalogue with milestone badges"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/ui.rs::every_catalog_tool_is_listed_after_view_more", "crates/engine/src/commands.rs::every_ready_catalogue_item_is_a_registered_command"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.command-palette"
|
||
title = "Command palette (⌘K) over every command"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = ["view.palette"]
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/commands.rs::the_palette_lists_commands_with_shortcuts_and_runs_them"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.keyboard-shortcuts"
|
||
title = "Platform keyboard shortcuts for registered commands"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/commands.rs::shortcuts_run_registered_commands", "crates/engine/src/commands.rs::shortcut_labels_follow_platform_conventions"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.shortcuts-dialog"
|
||
title = "Keyboard shortcuts reference dialog"
|
||
area = "B"
|
||
tier = "P1"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["help.shortcuts"]
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/commands.rs::the_shortcuts_dialog_lists_the_real_bindings"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.menus-from-registry"
|
||
title = "Menus generated from the command registry"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/commands.rs::the_pages_menu_comes_from_the_registry"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.about-dialog"
|
||
title = "About dialog"
|
||
area = "B"
|
||
tier = "P2"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = ["help.about"]
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/commands.rs::the_about_dialog_shows_the_running_version"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.check-for-updates"
|
||
title = "Help ▸ Check for updates"
|
||
area = "B"
|
||
tier = "P2"
|
||
milestone = "M3"
|
||
status = "partial"
|
||
commands = ["help.check_updates"]
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/updates.rs::a_newer_release_is_offered_for_download", "crates/ui-egui/tests/updates.rs::nothing_is_asked_until_the_user_checks", "apps/printcraft/src/updates.rs::answers_are_read_and_only_our_release_pages_are_offered"]
|
||
notes = "asks GitHub Releases on request and offers the release page. By the owner's decision there is no check at start and no in-place install (Acrobat has both)"
|
||
|
||
[[feature]]
|
||
id = "view.single-key-accelerators"
|
||
title = "Single-key tool accelerators"
|
||
area = "B"
|
||
tier = "P2"
|
||
milestone = "M3"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.customize-quick-tools"
|
||
title = "Customise the quick-action toolbar and pinned tools"
|
||
area = "B"
|
||
tier = "P2"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.restriction-notice"
|
||
title = "Message bar for restricted documents"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/editing.rs::restricted_documents_show_a_notice_and_block_page_changes"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.pdfa-view-mode"
|
||
title = "Read-only view for PDF/A files with an Enable editing bar"
|
||
area = "B"
|
||
tier = "P2"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.show-page-boxes"
|
||
title = "Show art, trim and bleed boxes"
|
||
area = "B"
|
||
tier = "P2"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "view.web-app"
|
||
title = "Run in the browser (WebAssembly)"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "partial"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "web build works; IndexedDB/OPFS recent list and download flow are M3.10"
|
||
|
||
[[feature]]
|
||
id = "view.large-document-performance"
|
||
title = "Smooth scrolling on 500-page documents"
|
||
area = "B"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/perf.rs::scrolling_a_500_page_document_stays_within_the_frame_budget", "crates/ui-egui/tests/perf.rs::panels_with_hundreds_of_items_stay_within_the_frame_budget"]
|
||
notes = "UI work per frame ~6 ms scrolling 500 pages (comments panel with 500 cards ~6 ms, thumbnails ~2 ms); rendering is off-thread and tiled"
|
||
|
||
# ==================================================================================================
|
||
# C. Content editing
|
||
# ==================================================================================================
|
||
|
||
[[feature]]
|
||
id = "edit.text-edit"
|
||
title = "Edit existing text in place"
|
||
area = "C"
|
||
tier = "P0"
|
||
milestone = "M7"
|
||
status = "partial"
|
||
commands = ["edit.edit_text"]
|
||
tools = ["text_lines", "text_paragraphs", "text_edit"]
|
||
evidence = ["crates/edit/src/tests.rs::paragraphs_are_found_and_rewrapped", "crates/edit/src/tests.rs::text_lines_are_found_and_replaced_in_place", "crates/ui-egui/tests/editing.rs::editing_existing_text_in_place", "crates/automation/tests/automation.rs::editing_existing_text_through_tools"]
|
||
notes = "click a paragraph box and retype it in place (font reused or Helvetica substituted, colour and spacing kept, rewrapped); drag a box to move it or its right-edge handle to rewrap it (edit.text-move-resize); per-character formatting changes are next"
|
||
|
||
[[feature]]
|
||
id = "edit.text-reflow"
|
||
title = "Reflow edited text inside its paragraph box"
|
||
area = "C"
|
||
tier = "P0"
|
||
milestone = "M7"
|
||
status = "shipped"
|
||
commands = ["edit.edit_text"]
|
||
tools = ["text_edit"]
|
||
evidence = ["crates/edit/src/tests.rs::paragraphs_are_found_and_rewrapped", "crates/edit/src/tests.rs::text_lines_are_found_and_replaced_in_place", "crates/ui-egui/tests/editing.rs::editing_existing_text_in_place", "crates/automation/tests/automation.rs::editing_existing_text_through_tools"]
|
||
notes = "edited paragraphs are rewrapped to the box width with the font's own advance widths and the original line spacing, starting at the original text matrix; text state and fill colour restored after; a single line grows to the right margin"
|
||
|
||
[[feature]]
|
||
id = "edit.text-move-resize"
|
||
title = "Move and resize text boxes"
|
||
area = "C"
|
||
tier = "P0"
|
||
milestone = "M7"
|
||
status = "shipped"
|
||
commands = ["edit.edit_text"]
|
||
tools = ["text_edit"]
|
||
evidence = ["crates/edit/src/tests.rs::paragraphs_move_and_rewrap_to_a_new_width", "crates/ui-egui/tests/editing.rs::dragging_a_paragraph_moves_it_and_its_edge_rewraps_it", "crates/automation/tests/automation.rs::editing_existing_text_through_tools"]
|
||
notes = "existing paragraphs: drag the box to move it, drag the handle on its right edge to rewrap it to a new width (upright pages); the move is applied in page space through the paragraph's CTM, in its own q … Q; a click still opens it for typing (#40)"
|
||
|
||
[[feature]]
|
||
id = "edit.paragraph-detection"
|
||
title = "Detect paragraphs and text blocks for editing"
|
||
area = "C"
|
||
tier = "P0"
|
||
milestone = "M7"
|
||
status = "shipped"
|
||
commands = ["edit.edit_text"]
|
||
tools = ["text_paragraphs"]
|
||
evidence = ["crates/edit/src/tests.rs::paragraphs_are_found_and_rewrapped", "crates/edit/src/tests.rs::text_lines_are_found_and_replaced_in_place", "crates/ui-egui/tests/editing.rs::editing_existing_text_in_place", "crates/automation/tests/automation.rs::editing_existing_text_through_tools"]
|
||
notes = "lines grouped into paragraphs by stream, font, size, left alignment and regular baseline spacing; hyphenated line ends are joined"
|
||
|
||
[[feature]]
|
||
id = "edit.font-reuse-substitution"
|
||
title = "Reuse embedded fonts or substitute with a warning"
|
||
area = "C"
|
||
tier = "P0"
|
||
milestone = "M7"
|
||
status = "shipped"
|
||
commands = ["edit.edit_text"]
|
||
tools = ["text_edit"]
|
||
evidence = ["crates/edit/src/tests.rs::missing_glyphs_substitute_helvetica_and_impossible_text_is_refused", "crates/fonts/src/pdf.rs::subset_fonts_only_encode_the_glyphs_they_have", "crates/fonts/src/pdf.rs::composite_fonts_use_to_unicode"]
|
||
notes = "the line's font is reused when every character has a code and a glyph (ToUnicode, encodings, Differences; subset glyph checks); otherwise Helvetica (WinAnsi) is used and reported; text no font can show is refused"
|
||
|
||
[[feature]]
|
||
id = "edit.text-format-font"
|
||
title = "Change font, size and colour"
|
||
area = "C"
|
||
tier = "P0"
|
||
milestone = "M7"
|
||
status = "shipped"
|
||
commands = ["edit.edit_text", "edit.text"]
|
||
tools = ["text_edit", "content_update"]
|
||
evidence = ["crates/edit/src/tests.rs::paragraphs_take_new_formatting", "crates/ui-egui/tests/editing.rs::the_format_panel_restyles_the_paragraph_being_edited", "crates/automation/tests/automation.rs::editing_existing_text_through_tools"]
|
||
notes = "existing paragraphs and added text: font family (Helvetica, Times, Courier) with bold and italic, size, colour; existing paragraphs are rewrapped"
|
||
|
||
[[feature]]
|
||
id = "edit.text-format-style"
|
||
title = "Bold, italic, underline"
|
||
area = "C"
|
||
tier = "P1"
|
||
milestone = "M7"
|
||
status = "shipped"
|
||
commands = ["edit.edit_text"]
|
||
tools = ["text_edit"]
|
||
evidence = ["crates/edit/src/tests.rs::justify_underline_and_spacing", "crates/edit/src/tests.rs::paragraphs_take_new_formatting", "crates/ui-egui/tests/editing.rs::the_format_panel_restyles_the_paragraph_being_edited"]
|
||
notes = "bold and italic through the standard families; underline drawn under each line in the text colour"
|
||
|
||
[[feature]]
|
||
id = "edit.text-super-subscript"
|
||
title = "Superscript and subscript"
|
||
area = "C"
|
||
tier = "P2"
|
||
milestone = "M7"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "edit.paragraph-alignment"
|
||
title = "Paragraph alignment (left, centre, right, justify)"
|
||
area = "C"
|
||
tier = "P0"
|
||
milestone = "M7"
|
||
status = "shipped"
|
||
commands = ["edit.edit_text", "edit.text"]
|
||
tools = ["text_edit", "content_update"]
|
||
evidence = ["crates/edit/src/tests.rs::justify_underline_and_spacing", "crates/edit/src/tests.rs::paragraphs_take_new_formatting", "crates/ui-egui/tests/editing.rs::the_format_panel_restyles_the_paragraph_being_edited"]
|
||
notes = "left, centre, right and justify (word spacing per line, last line left) for existing paragraphs and added text"
|
||
|
||
[[feature]]
|
||
id = "edit.line-paragraph-spacing"
|
||
title = "Line and paragraph spacing"
|
||
area = "C"
|
||
tier = "P1"
|
||
milestone = "M7"
|
||
status = "partial"
|
||
commands = ["edit.edit_text"]
|
||
tools = ["text_edit"]
|
||
evidence = ["crates/edit/src/tests.rs::justify_underline_and_spacing", "crates/edit/src/tests.rs::paragraphs_take_new_formatting", "crates/ui-egui/tests/editing.rs::the_format_panel_restyles_the_paragraph_being_edited"]
|
||
notes = "line spacing (1, 1.15, 1.5, 2 × size or any multiple); space between paragraphs is not adjustable yet"
|
||
|
||
[[feature]]
|
||
id = "edit.char-spacing-scale"
|
||
title = "Character spacing and horizontal scaling"
|
||
area = "C"
|
||
tier = "P1"
|
||
milestone = "M7"
|
||
status = "shipped"
|
||
commands = ["edit.edit_text"]
|
||
tools = ["text_edit"]
|
||
evidence = ["crates/edit/src/tests.rs::justify_underline_and_spacing", "crates/edit/src/tests.rs::paragraphs_take_new_formatting", "crates/ui-egui/tests/editing.rs::the_format_panel_restyles_the_paragraph_being_edited"]
|
||
notes = "character spacing in points and horizontal scaling in percent for a paragraph (Tc, Tz), rewrapped"
|
||
|
||
[[feature]]
|
||
id = "edit.lists"
|
||
title = "Bulleted and numbered lists"
|
||
area = "C"
|
||
tier = "P1"
|
||
milestone = "M7"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "edit.add-text"
|
||
title = "Add a new text box as page content"
|
||
area = "C"
|
||
tier = "P0"
|
||
milestone = "M7"
|
||
status = "shipped"
|
||
commands = ["edit.text"]
|
||
tools = ["page_add_text", "content_update"]
|
||
evidence = ["crates/ui-egui/tests/content.rs::typing_moving_styling_and_deleting_added_content", "crates/automation/tests/automation.rs::adding_content_through_tools", "crates/edit/src/tests.rs::added_text_and_images_are_page_content_that_stays_editable", "crates/engine/src/tests.rs::added_text_and_images_render_and_stay_editable"]
|
||
notes = "page content, editable later (retype, move, restyle); standard fonts, Windows-1252 text"
|
||
|
||
[[feature]]
|
||
id = "edit.find-replace"
|
||
title = "Find and replace in edit mode"
|
||
area = "C"
|
||
tier = "P2"
|
||
milestone = "M7"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "edit.rtl-cjk-editing"
|
||
title = "Edit right-to-left and CJK text"
|
||
area = "C"
|
||
tier = "P1"
|
||
milestone = "M7"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "edit.rotated-text-editing"
|
||
title = "Edit rotated text"
|
||
area = "C"
|
||
tier = "P2"
|
||
milestone = "M7"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "edit.keep-tags-on-edit"
|
||
title = "Keep structure tags when editing"
|
||
area = "C"
|
||
tier = "P1"
|
||
milestone = "M7"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "edit.image-add"
|
||
title = "Add an image"
|
||
area = "C"
|
||
tier = "P0"
|
||
milestone = "M7"
|
||
status = "shipped"
|
||
commands = ["edit.image"]
|
||
tools = ["page_add_image"]
|
||
evidence = ["crates/ui-egui/tests/content.rs::typing_moving_styling_and_deleting_added_content", "crates/automation/tests/automation.rs::adding_content_through_tools", "crates/edit/src/tests.rs::added_text_and_images_are_page_content_that_stays_editable", "crates/engine/src/tests.rs::added_text_and_images_render_and_stay_editable"]
|
||
notes = "PNG, JPEG, TIFF, GIF, BMP; centred at natural size or in a rect"
|
||
|
||
[[feature]]
|
||
id = "edit.image-replace"
|
||
title = "Replace an image (keep position and size)"
|
||
area = "C"
|
||
tier = "P0"
|
||
milestone = "M7"
|
||
status = "shipped"
|
||
commands = ["edit.edit_text", "edit.image"]
|
||
tools = ["image_edit"]
|
||
evidence = ["crates/edit/src/tests.rs::page_images_move_turn_replace_and_delete", "crates/ui-egui/tests/editing.rs::editing_existing_images_on_the_page", "crates/automation/tests/automation.rs::editing_page_images_through_tools"]
|
||
notes = "existing page images (right-click ▸ Replace Image) and added images: the new picture is drawn in the same place"
|
||
|
||
[[feature]]
|
||
id = "edit.image-crop"
|
||
title = "Crop an image"
|
||
area = "C"
|
||
tier = "P0"
|
||
milestone = "M7"
|
||
status = "partial"
|
||
commands = ["edit.image"]
|
||
tools = ["content_update"]
|
||
evidence = ["crates/engine/src/tests.rs::added_images_rotate_flip_and_crop_as_drawn", "crates/automation/tests/automation.rs::adding_content_through_tools"]
|
||
notes = "for added images: trim each side, clipped"
|
||
|
||
[[feature]]
|
||
id = "edit.image-rotate-flip"
|
||
title = "Rotate and flip images"
|
||
area = "C"
|
||
tier = "P0"
|
||
milestone = "M7"
|
||
status = "shipped"
|
||
commands = ["edit.edit_text"]
|
||
tools = ["image_edit"]
|
||
evidence = ["crates/edit/src/tests.rs::page_images_move_turn_replace_and_delete", "crates/ui-egui/tests/editing.rs::editing_existing_images_on_the_page", "crates/automation/tests/automation.rs::editing_page_images_through_tools"]
|
||
notes = "existing page images: rotate either way about the centre, flip horizontally or vertically (a q/cm/Q wrapper); added images too"
|
||
|
||
[[feature]]
|
||
id = "edit.image-move-resize"
|
||
title = "Move and resize images"
|
||
area = "C"
|
||
tier = "P0"
|
||
milestone = "M7"
|
||
status = "shipped"
|
||
commands = ["edit.edit_text"]
|
||
tools = ["image_edit"]
|
||
evidence = ["crates/edit/src/tests.rs::page_images_move_turn_replace_and_delete", "crates/ui-egui/tests/editing.rs::editing_existing_images_on_the_page", "crates/automation/tests/automation.rs::editing_page_images_through_tools"]
|
||
notes = "existing page images: select, drag to move, drag a corner to resize keeping proportions; Delete removes; added images too"
|
||
|
||
[[feature]]
|
||
id = "edit.image-extract"
|
||
title = "Save an image to a file"
|
||
area = "C"
|
||
tier = "P1"
|
||
milestone = "M7"
|
||
status = "shipped"
|
||
commands = ["edit.edit_text"]
|
||
tools = ["image_save"]
|
||
evidence = ["crates/edit/src/tests.rs::page_images_move_turn_replace_and_delete", "crates/ui-egui/tests/editing.rs::editing_existing_images_on_the_page", "crates/automation/tests/automation.rs::editing_page_images_through_tools", "crates/create/src/tests.rs::images_export_as_jpeg_unchanged_and_others_as_png"]
|
||
notes = "right-click an image ▸ Save Image As: JPEG unchanged, others PNG"
|
||
|
||
[[feature]]
|
||
id = "edit.image-external-editor"
|
||
title = "Edit an image in an external application"
|
||
area = "C"
|
||
tier = "P2"
|
||
milestone = "M7"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "edit.arrange-objects"
|
||
title = "Arrange objects (bring forward, send backward)"
|
||
area = "C"
|
||
tier = "P1"
|
||
milestone = "M7"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "edit.align-distribute-objects"
|
||
title = "Align and distribute objects"
|
||
area = "C"
|
||
tier = "P1"
|
||
milestone = "M7"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "edit.vector-edit"
|
||
title = "Select, move, scale, recolour and delete vector paths"
|
||
area = "C"
|
||
tier = "P1"
|
||
milestone = "M7"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "edit.edit-object-tool"
|
||
title = "Edit object tool for any page object"
|
||
area = "C"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "edit.object-properties"
|
||
title = "Object properties (colour space, font, convert colour)"
|
||
area = "C"
|
||
tier = "P2"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "edit.link-create"
|
||
title = "Create web and page links"
|
||
area = "C"
|
||
tier = "P1"
|
||
milestone = "M7"
|
||
status = "shipped"
|
||
commands = ["edit.link"]
|
||
tools = ["link_add"]
|
||
evidence = ["crates/ui-egui/tests/pdf_links.rs::drawing_editing_and_deleting_links", "crates/automation/tests/automation.rs::links_through_tools", "crates/annot/src/tests.rs::links_are_added_edited_listed_and_removed"]
|
||
notes = "drag a rectangle; go to a page or open a web page"
|
||
|
||
[[feature]]
|
||
id = "edit.link-properties"
|
||
title = "Link appearance and properties"
|
||
area = "C"
|
||
tier = "P1"
|
||
milestone = "M7"
|
||
status = "shipped"
|
||
commands = ["edit.link"]
|
||
tools = ["link_edit"]
|
||
evidence = ["crates/ui-egui/tests/pdf_links.rs::drawing_editing_and_deleting_links", "crates/automation/tests/automation.rs::links_through_tools", "crates/annot/src/tests.rs::links_are_added_edited_listed_and_removed"]
|
||
notes = "visible/invisible rectangle, highlight, thickness, solid/dashed/underline, colour; action"
|
||
|
||
[[feature]]
|
||
id = "edit.links-from-urls"
|
||
title = "Create links from URLs in the text"
|
||
area = "C"
|
||
tier = "P1"
|
||
milestone = "M7"
|
||
status = "shipped"
|
||
commands = ["edit.links_from_urls"]
|
||
tools = ["links_from_urls"]
|
||
evidence = ["crates/engine/src/tests.rs::links_from_urls_and_link_edits", "crates/automation/tests/automation.rs::links_through_tools", "crates/annot/src/tests.rs::urls_are_found_in_text"]
|
||
notes = "http(s):// and www. addresses; already-linked text skipped"
|
||
|
||
[[feature]]
|
||
id = "edit.remove-all-links"
|
||
title = "Remove all links"
|
||
area = "C"
|
||
tier = "P2"
|
||
milestone = "M7"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "edit.action-goto"
|
||
title = "Go-to-page-view action (with named destinations)"
|
||
area = "C"
|
||
tier = "P1"
|
||
milestone = "M7"
|
||
status = "partial"
|
||
commands = ["edit.link"]
|
||
tools = ["link_add"]
|
||
evidence = ["crates/annot/src/tests.rs::links_are_added_edited_listed_and_removed", "crates/ui-egui/tests/pdf_links.rs::drawing_editing_and_deleting_links"]
|
||
notes = "go to a page (Fit view); named destinations and zoom pending"
|
||
|
||
[[feature]]
|
||
id = "edit.action-uri"
|
||
title = "Open-a-web-link action"
|
||
area = "C"
|
||
tier = "P1"
|
||
milestone = "M7"
|
||
status = "shipped"
|
||
commands = ["edit.link"]
|
||
tools = ["link_add"]
|
||
evidence = ["crates/annot/src/tests.rs::links_are_added_edited_listed_and_removed", "crates/automation/tests/automation.rs::links_through_tools"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "edit.action-launch"
|
||
title = "Open-a-file action"
|
||
area = "C"
|
||
tier = "P2"
|
||
milestone = "M7"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "edit.action-named"
|
||
title = "Execute-a-menu-item action (safe list)"
|
||
area = "C"
|
||
tier = "P2"
|
||
milestone = "M7"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "edit.action-set-ocg-state"
|
||
title = "Set-layer-visibility action"
|
||
area = "C"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "edit.action-show-hide-field"
|
||
title = "Show/hide-a-field action"
|
||
area = "C"
|
||
tier = "P1"
|
||
milestone = "M6"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "edit.action-javascript"
|
||
title = "Run-a-JavaScript action"
|
||
area = "C"
|
||
tier = "P1"
|
||
milestone = "M6"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "edit.action-thread"
|
||
title = "Read-an-article action"
|
||
area = "C"
|
||
tier = "P3"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "edit.action-goto-embedded"
|
||
title = "Go to a target in an embedded file (GoToE)"
|
||
area = "C"
|
||
tier = "P3"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "edit.action-media"
|
||
title = "Play sound / media / rich-media actions"
|
||
area = "C"
|
||
tier = "P3"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "edit.header-footer-add"
|
||
title = "Add header and footer"
|
||
area = "C"
|
||
tier = "P0"
|
||
milestone = "M7"
|
||
status = "shipped"
|
||
commands = ["edit.header_footer"]
|
||
tools = ["doc_header_footer"]
|
||
evidence = ["crates/ui-egui/tests/marks.rs::header_and_footer_dialog_inserts_tokens_and_applies_to_a_subset", "crates/edit/src/tests.rs::header_and_footer_are_drawn_in_display_space_and_wrap_the_original_content", "crates/engine/src/tests.rs::headers_footers_watermarks_and_backgrounds_show_update_and_remove"]
|
||
notes = "Helvetica only (no font choice yet); drawn in display space on rotated pages; artifact-tagged"
|
||
|
||
[[feature]]
|
||
id = "edit.header-footer-update-remove"
|
||
title = "Update or remove header and footer"
|
||
area = "C"
|
||
tier = "P0"
|
||
milestone = "M7"
|
||
status = "shipped"
|
||
commands = ["edit.header_footer.update", "edit.header_footer.remove"]
|
||
tools = ["doc_header_footer", "doc_remove_marks"]
|
||
evidence = ["crates/edit/src/tests.rs::replace_and_remove_restore_the_original_content", "crates/ui-egui/tests/marks.rs::header_and_footer_dialog_inserts_tokens_and_applies_to_a_subset"]
|
||
notes = "recognises PrintCraft marks; Acrobat-made headers are not detected yet"
|
||
|
||
[[feature]]
|
||
id = "edit.header-footer-tokens"
|
||
title = "Page number and date tokens in header and footer"
|
||
area = "C"
|
||
tier = "P0"
|
||
milestone = "M7"
|
||
status = "shipped"
|
||
commands = ["edit.header_footer"]
|
||
tools = ["doc_header_footer"]
|
||
evidence = ["crates/edit/src/tests.rs::tokens_expand", "crates/engine/src/tests.rs::headers_footers_watermarks_and_backgrounds_show_update_and_remove"]
|
||
notes = "<<1>>, <<n>>, <<1 of n>>, <<Page 1 of n>>, dates, Bates"
|
||
|
||
[[feature]]
|
||
id = "edit.header-footer-ranges"
|
||
title = "Header and footer page ranges (all, range, odd, even)"
|
||
area = "C"
|
||
tier = "P1"
|
||
milestone = "M7"
|
||
status = "shipped"
|
||
commands = ["edit.header_footer"]
|
||
tools = ["doc_header_footer"]
|
||
evidence = ["crates/ui-egui/src/marks_ui.rs::page_ranges_with_subsets", "crates/ui-egui/tests/marks.rs::header_and_footer_dialog_inserts_tokens_and_applies_to_a_subset"]
|
||
notes = "all / from–to, all / even / odd"
|
||
|
||
[[feature]]
|
||
id = "edit.header-footer-shrink"
|
||
title = "Shrink document to avoid overwriting content"
|
||
area = "C"
|
||
tier = "P2"
|
||
milestone = "M7"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "edit.watermark-add-text"
|
||
title = "Add a text watermark"
|
||
area = "C"
|
||
tier = "P0"
|
||
milestone = "M7"
|
||
status = "shipped"
|
||
commands = ["edit.watermark"]
|
||
tools = ["doc_watermark"]
|
||
evidence = ["crates/edit/src/tests.rs::watermarks_rotate_fade_and_can_go_behind", "crates/ui-egui/tests/marks.rs::watermark_and_background_dialogs", "crates/automation/tests/automation.rs::headers_watermarks_and_backgrounds_through_tools"]
|
||
notes = "Helvetica; fit-to-page or a size"
|
||
|
||
[[feature]]
|
||
id = "edit.watermark-add-file"
|
||
title = "Add a watermark from an image or PDF page"
|
||
area = "C"
|
||
tier = "P1"
|
||
milestone = "M7"
|
||
status = "shipped"
|
||
commands = ["edit.watermark"]
|
||
tools = ["doc_watermark"]
|
||
evidence = ["crates/engine/src/tests.rs::backgrounds_and_watermarks_from_files"]
|
||
notes = "an image or a page of a PDF, scaled relative to the page, rotated, with opacity, behind or on top"
|
||
|
||
[[feature]]
|
||
id = "edit.watermark-update-remove"
|
||
title = "Update or remove watermarks"
|
||
area = "C"
|
||
tier = "P0"
|
||
milestone = "M7"
|
||
status = "shipped"
|
||
commands = ["edit.watermark.update", "edit.watermark.remove"]
|
||
tools = ["doc_watermark", "doc_remove_marks"]
|
||
evidence = ["crates/edit/src/tests.rs::replace_and_remove_restore_the_original_content", "crates/engine/src/tests.rs::headers_footers_watermarks_and_backgrounds_show_update_and_remove"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "edit.watermark-appearance"
|
||
title = "Watermark rotation, opacity, scale, position, print/screen visibility"
|
||
area = "C"
|
||
tier = "P1"
|
||
milestone = "M7"
|
||
status = "partial"
|
||
commands = ["edit.watermark"]
|
||
tools = ["doc_watermark"]
|
||
evidence = ["crates/edit/src/tests.rs::watermarks_rotate_fade_and_can_go_behind"]
|
||
notes = "rotation, opacity, behind/on top; no position offsets, scale or print/screen-only variants in the UI yet"
|
||
|
||
[[feature]]
|
||
id = "edit.background-add"
|
||
title = "Add a page background (colour or file)"
|
||
area = "C"
|
||
tier = "P0"
|
||
milestone = "M7"
|
||
status = "shipped"
|
||
commands = ["edit.background"]
|
||
tools = ["doc_background"]
|
||
evidence = ["crates/ui-egui/tests/marks.rs::watermark_and_background_dialogs", "crates/engine/src/tests.rs::headers_footers_watermarks_and_backgrounds_show_update_and_remove", "crates/engine/src/tests.rs::backgrounds_and_watermarks_from_files", "crates/ui-egui/tests/marks.rs::backgrounds_and_watermarks_from_a_file", "crates/organize/src/tests.rs::a_page_becomes_a_form_xobject_upright"]
|
||
notes = "colour, or a picture from a file (an image or a page of a PDF) scaled relative to the page"
|
||
|
||
[[feature]]
|
||
id = "edit.background-update-remove"
|
||
title = "Update or remove backgrounds"
|
||
area = "C"
|
||
tier = "P0"
|
||
milestone = "M7"
|
||
status = "shipped"
|
||
commands = ["edit.background.update", "edit.background.remove"]
|
||
tools = ["doc_background", "doc_remove_marks"]
|
||
evidence = ["crates/edit/src/tests.rs::replace_and_remove_restore_the_original_content", "crates/engine/src/tests.rs::headers_footers_watermarks_and_backgrounds_show_update_and_remove"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "edit.decoration-profiles"
|
||
title = "Saved header/footer and watermark settings"
|
||
area = "C"
|
||
tier = "P2"
|
||
milestone = "M7"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "edit.bates-numbering"
|
||
title = "Bates numbering"
|
||
area = "C"
|
||
tier = "P1"
|
||
milestone = "M7"
|
||
status = "partial"
|
||
commands = ["edit.bates", "edit.header_footer"]
|
||
tools = ["doc_header_footer"]
|
||
evidence = ["crates/edit/src/tests.rs::tokens_expand"]
|
||
notes = "Bates ▸ Add opens Header & Footer with a Bates number; no multi-file batch yet"
|
||
|
||
[[feature]]
|
||
id = "edit.bates-multi-file"
|
||
title = "Bates numbering across multiple files"
|
||
area = "C"
|
||
tier = "P1"
|
||
milestone = "M7"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "edit.bates-remove"
|
||
title = "Remove Bates numbering"
|
||
area = "C"
|
||
tier = "P1"
|
||
milestone = "M7"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "edit.spell-check"
|
||
title = "Spell check in comments, form fields and edited text"
|
||
area = "C"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "edit.spell-dictionaries"
|
||
title = "Spelling dictionaries per language and a user dictionary"
|
||
area = "C"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "edit.format-panel"
|
||
title = "Format panel for edited text"
|
||
area = "C"
|
||
tier = "P0"
|
||
milestone = "M7"
|
||
status = "shipped"
|
||
commands = ["edit.edit_text"]
|
||
tools = ["text_edit"]
|
||
evidence = ["crates/edit/src/tests.rs::paragraphs_take_new_formatting", "crates/ui-egui/tests/editing.rs::the_format_panel_restyles_the_paragraph_being_edited", "crates/automation/tests/automation.rs::editing_existing_text_through_tools"]
|
||
notes = "Edit a PDF ▸ Format text shows the paragraph being edited (family, bold/italic guessed from its font, size) and applies changes at once as one undo step each, keeping the paragraph open"
|
||
|
||
[[feature]]
|
||
id = "edit.adobe-express-edits"
|
||
title = "Adobe Express image edits (remove background, generative fill)"
|
||
area = "C"
|
||
tier = "N/A"
|
||
milestone = "none"
|
||
status = "na"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "cloud-only; images open in an external editor instead"
|
||
|
||
# ==================================================================================================
|
||
# D. Organize pages and documents
|
||
# ==================================================================================================
|
||
|
||
[[feature]]
|
||
id = "organize.organize-grid"
|
||
title = "Organize pages thumbnail grid"
|
||
area = "D"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["page.organize"]
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/editing.rs::shift_click_selects_a_range_and_moves_it"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.multi-select-pages"
|
||
title = "Select pages with click, ⌘-click and ⇧-click"
|
||
area = "D"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["page.organize"]
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/editing.rs::command_click_toggles_and_rotation_applies_to_selection", "crates/ui-egui/tests/editing.rs::shift_click_selects_a_range_and_moves_it"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.rotate-pages"
|
||
title = "Rotate pages"
|
||
area = "D"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["page.rotate", "page.rotate_ccw"]
|
||
tools = ["page_rotate"]
|
||
evidence = ["crates/organize/src/tests.rs::rotate_uses_inherited_rotation_and_round_trips", "crates/ui-egui/tests/editing.rs::command_click_toggles_and_rotation_applies_to_selection"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.rotate-filters"
|
||
title = "Rotate by range with even/odd and orientation filters"
|
||
area = "D"
|
||
tier = "P1"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["page.rotate_dialog"]
|
||
tools = ["page_rotate"]
|
||
evidence = ["crates/ui-egui/tests/editing.rs::extract_options_and_rotate_pages_dialog", "crates/automation/tests/automation.rs::organizing_with_filters_bookmark_splits_and_extract_options", "crates/engine/src/tests.rs::split_by_size_and_bookmarks_and_page_filters"]
|
||
notes = "Rotate Pages: all/selection/range; even/odd; landscape/portrait"
|
||
|
||
[[feature]]
|
||
id = "organize.delete-pages"
|
||
title = "Delete pages"
|
||
area = "D"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["page.delete"]
|
||
tools = ["page_delete"]
|
||
evidence = ["crates/organize/src/tests.rs::delete_keeps_inherited_attributes_on_survivors", "crates/organize/src/tests.rs::cannot_delete_every_page_or_missing_pages", "crates/ui-egui/tests/editing.rs::delete_is_refused_when_it_would_remove_every_page"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.move-pages"
|
||
title = "Move pages earlier or later"
|
||
area = "D"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = ["page_move"]
|
||
evidence = ["crates/organize/src/tests.rs::move_pages_reorders", "crates/ui-egui/tests/editing.rs::shift_click_selects_a_range_and_moves_it"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.drag-reorder"
|
||
title = "Reorder pages by drag and drop"
|
||
area = "D"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/editing.rs::dragging_thumbnails_reorders_pages"]
|
||
notes = "drag thumbnails (or the selection) in the page grid; an insertion bar shows where they go; move earlier/later buttons too"
|
||
|
||
[[feature]]
|
||
id = "organize.insert-blank"
|
||
title = "Insert blank pages"
|
||
area = "D"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["page.insert_blank"]
|
||
tools = ["page_insert_blank"]
|
||
evidence = ["crates/organize/src/tests.rs::inserts_blank_page", "crates/ui-egui/tests/editing.rs::insert_blank_page_after_selection", "crates/engine/src/tests.rs::insert_blank_between_pages_keeps_both_neighbours"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.insert-from-file"
|
||
title = "Insert pages from another file"
|
||
area = "D"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["page.insert"]
|
||
tools = ["page_insert_file"]
|
||
evidence = ["crates/organize/src/tests.rs::inserting_pages_from_another_file_is_an_incremental_edit", "crates/ui-egui/tests/editing.rs::inserting_a_file_goes_after_the_selection_and_undoes"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.insert-from-clipboard"
|
||
title = "Insert pages from the clipboard"
|
||
area = "D"
|
||
tier = "P1"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.insert-from-scanner"
|
||
title = "Insert pages from a scanner"
|
||
area = "D"
|
||
tier = "P2"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.insert-from-web"
|
||
title = "Insert pages from a web page"
|
||
area = "D"
|
||
tier = "P2"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.insert-by-drop"
|
||
title = "Insert by dropping files onto the page grid"
|
||
area = "D"
|
||
tier = "P1"
|
||
milestone = "M4"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.extract-pages"
|
||
title = "Extract pages to a new document"
|
||
area = "D"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["page.extract"]
|
||
tools = ["page_extract"]
|
||
evidence = ["crates/ui-egui/tests/editing.rs::extract_button_copies_selected_pages_to_a_new_tab", "crates/automation/tests/automation.rs::combine_extract_and_split"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.extract-separate-files"
|
||
title = "Extract pages as separate files"
|
||
area = "D"
|
||
tier = "P1"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["page.extract"]
|
||
tools = ["page_extract"]
|
||
evidence = ["crates/ui-egui/tests/editing.rs::extract_options_and_rotate_pages_dialog", "crates/automation/tests/automation.rs::organizing_with_filters_bookmark_splits_and_extract_options"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.extract-and-delete"
|
||
title = "Delete pages after extracting"
|
||
area = "D"
|
||
tier = "P1"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["page.extract"]
|
||
tools = ["page_extract"]
|
||
evidence = ["crates/ui-egui/tests/editing.rs::extract_options_and_rotate_pages_dialog", "crates/automation/tests/automation.rs::organizing_with_filters_bookmark_splits_and_extract_options"]
|
||
notes = "undoable"
|
||
|
||
[[feature]]
|
||
id = "organize.replace-pages"
|
||
title = "Replace pages with pages from another file"
|
||
area = "D"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["page.replace"]
|
||
tools = ["page_replace"]
|
||
evidence = ["crates/organize/src/tests.rs::replace_pages_swaps_content_but_keeps_annotations", "crates/ui-egui/tests/editing.rs::replace_pages_dialog_swaps_page_content", "crates/automation/tests/automation.rs::replacing_pages_through_tools"]
|
||
notes = "content, resources and boxes replaced; links, comments, fields and bookmarks kept"
|
||
|
||
[[feature]]
|
||
id = "organize.duplicate-pages"
|
||
title = "Duplicate pages"
|
||
area = "D"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["page.duplicate"]
|
||
tools = ["page_duplicate"]
|
||
evidence = ["crates/ui-egui/tests/pageboxes.rs::duplicate_pages_copies_the_current_page", "crates/organize/src/tests.rs::duplicate_pages_inserts_copies_after_the_last"]
|
||
notes = "copies share fonts and images with the originals"
|
||
|
||
[[feature]]
|
||
id = "organize.reverse-pages"
|
||
title = "Reverse page order"
|
||
area = "D"
|
||
tier = "P2"
|
||
milestone = "M4"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.cut-copy-paste-pages"
|
||
title = "Cut, copy and paste pages"
|
||
area = "D"
|
||
tier = "P1"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["page.copy", "page.cut", "page.paste"]
|
||
tools = ["page_insert_file"]
|
||
evidence = ["crates/ui-egui/tests/editing.rs::copying_cutting_and_pasting_pages"]
|
||
notes = "⌘C/⌘X/⌘V and the thumbnail menu in Organize Pages; paste works across documents"
|
||
|
||
[[feature]]
|
||
id = "organize.drag-pages-between-documents"
|
||
title = "Drag pages between documents"
|
||
area = "D"
|
||
tier = "P2"
|
||
milestone = "M4"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.page-range-selection"
|
||
title = "Select by range, odd/even, landscape/portrait"
|
||
area = "D"
|
||
tier = "P1"
|
||
milestone = "M4"
|
||
status = "partial"
|
||
commands = ["page.organize"]
|
||
tools = ["page_rotate"]
|
||
evidence = ["crates/engine/src/tests.rs::split_by_size_and_bookmarks_and_page_filters"]
|
||
notes = "Organize ▸ Select: all, odd, even, landscape, portrait; ranges via the page tools"
|
||
|
||
[[feature]]
|
||
id = "organize.thumbnail-size"
|
||
title = "Thumbnail size slider"
|
||
area = "D"
|
||
tier = "P2"
|
||
milestone = "M4"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.split-every-n"
|
||
title = "Split every N pages"
|
||
area = "D"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["page.split"]
|
||
tools = ["doc_split"]
|
||
evidence = ["crates/ui-egui/tests/editing.rs::split_dialog_writes_one_file_per_part", "crates/organize/src/tests.rs::split_ranges_cover_every_page_once", "crates/organize/src/tests.rs::split_produces_standalone_documents_with_metadata"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.split-before-pages"
|
||
title = "Split before chosen pages"
|
||
area = "D"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["page.split"]
|
||
tools = ["doc_split"]
|
||
evidence = ["crates/ui-egui/tests/editing.rs::split_before_selected_pages"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.split-by-size"
|
||
title = "Split by file size"
|
||
area = "D"
|
||
tier = "P1"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["page.split"]
|
||
tools = ["doc_split"]
|
||
evidence = ["crates/automation/tests/automation.rs::organizing_with_filters_bookmark_splits_and_extract_options", "crates/engine/src/tests.rs::split_by_size_and_bookmarks_and_page_filters"]
|
||
notes = "greedy by single-page size (an over-estimate: parts stay under the limit)"
|
||
|
||
[[feature]]
|
||
id = "organize.split-by-bookmarks"
|
||
title = "Split by top-level bookmarks"
|
||
area = "D"
|
||
tier = "P1"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["page.split"]
|
||
tools = ["doc_split"]
|
||
evidence = ["crates/automation/tests/automation.rs::organizing_with_filters_bookmark_splits_and_extract_options", "crates/engine/src/tests.rs::split_by_size_and_bookmarks_and_page_filters"]
|
||
notes = "files named after the bookmarks"
|
||
|
||
[[feature]]
|
||
id = "organize.split-naming"
|
||
title = "Name split parts after bookmarks; never overwrite"
|
||
area = "D"
|
||
tier = "P2"
|
||
milestone = "M4"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.split-multiple-files"
|
||
title = "Split multiple files in one run"
|
||
area = "D"
|
||
tier = "P2"
|
||
milestone = "M13"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.combine-files"
|
||
title = "Combine files into one PDF"
|
||
area = "D"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["page.combine"]
|
||
tools = ["doc_combine"]
|
||
evidence = ["crates/organize/src/tests.rs::combine_concatenates_with_a_bookmark_per_file", "crates/ui-egui/tests/editing.rs::combining_files_opens_a_new_unsaved_tab", "crates/automation/tests/automation.rs::combine_extract_and_split"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.combine-bookmarks"
|
||
title = "Combine adds a bookmark per file with its own bookmarks nested"
|
||
area = "D"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["page.combine"]
|
||
tools = ["doc_combine"]
|
||
evidence = ["crates/organize/src/tests.rs::combine_nests_source_bookmarks_and_keeps_attachments"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.combine-choose-pages"
|
||
title = "Choose which pages of each file to combine"
|
||
area = "D"
|
||
tier = "P1"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["page.combine"]
|
||
tools = ["doc_combine"]
|
||
evidence = ["crates/ui-egui/tests/editing.rs::combine_files_takes_chosen_pages_in_the_order_listed", "crates/automation/tests/automation.rs::combine_extract_and_split"]
|
||
notes = "Combine files list: order (move up/down), remove, add files, a page range per file (such as 1-3, 6; in the order given); bookmarks to left-out pages lose their destination"
|
||
|
||
[[feature]]
|
||
id = "organize.combine-sort"
|
||
title = "Sort and reorder files before combining"
|
||
area = "D"
|
||
tier = "P2"
|
||
milestone = "M4"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.combine-options"
|
||
title = "Combine options (file size, convert to PDF/A, include open files)"
|
||
area = "D"
|
||
tier = "P2"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.resource-dedupe"
|
||
title = "Store identical fonts and images once when combining or inserting"
|
||
area = "D"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["page.combine", "page.insert"]
|
||
tools = ["doc_combine"]
|
||
evidence = ["crates/organize/src/tests.rs::combine_and_insert_share_identical_resources", "crates/organize/src/dedupe.rs::merges_bottom_up_and_keeps_identity_objects"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.preserve-links-dests"
|
||
title = "Rewire links and named destinations to copied pages"
|
||
area = "D"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/organize/src/tests.rs::links_are_rewired_to_copied_pages_or_dropped", "crates/organize/src/tests.rs::named_destinations_are_resolved_to_the_copies"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.preserve-fields"
|
||
title = "Keep form fields interactive when copying pages"
|
||
area = "D"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/organize/src/tests.rs::form_fields_come_along_and_are_registered"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.preserve-layers"
|
||
title = "Keep layers and their default state when copying pages"
|
||
area = "D"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/organize/src/tests.rs::layers_are_registered_with_their_default_state"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.preserve-inherited"
|
||
title = "Copy inherited page attributes with pages"
|
||
area = "D"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/organize/src/tests.rs::inherited_attributes_travel_with_copied_pages"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.preserve-attachments"
|
||
title = "Keep attachments when combining"
|
||
area = "D"
|
||
tier = "P1"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/organize/src/tests.rs::combine_nests_source_bookmarks_and_keeps_attachments"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.field-name-conflicts"
|
||
title = "Rename or merge colliding field names when combining"
|
||
area = "D"
|
||
tier = "P1"
|
||
milestone = "M4"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.struct-tree-merge"
|
||
title = "Merge structure trees when combining"
|
||
area = "D"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.page-labels-edit"
|
||
title = "Edit page labels (style, prefix, start)"
|
||
area = "D"
|
||
tier = "P1"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["page.number"]
|
||
tools = ["page_number"]
|
||
evidence = ["crates/organize/src/tests.rs::number_pages_like_acrobat", "crates/engine/src/tests.rs::number_pages_shows_in_the_viewer_and_undoes", "crates/ui-egui/tests/bookmarks.rs::number_pages_dialog_labels_the_selected_pages", "crates/automation/tests/automation.rs::numbering_pages_through_tools"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.page-boxes"
|
||
title = "Set page boxes (crop, trim, bleed, art)"
|
||
area = "D"
|
||
tier = "P1"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["page.boxes"]
|
||
tools = ["page_set_box"]
|
||
evidence = ["crates/ui-egui/tests/pageboxes.rs::set_page_boxes_dialog_applies_margins_to_the_chosen_pages", "crates/organize/src/tests.rs::page_boxes_default_inherit_and_set"]
|
||
notes = "crop/trim/bleed/art/media, margins in pt/in/mm, page ranges, preview; no Remove white margins yet"
|
||
|
||
[[feature]]
|
||
id = "organize.crop-tool"
|
||
title = "Crop pages by dragging a rectangle"
|
||
area = "D"
|
||
tier = "P1"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["page.crop"]
|
||
tools = ["page_set_box"]
|
||
evidence = ["crates/ui-egui/tests/pageboxes.rs::the_crop_tool_crops_to_the_dragged_rectangle_then_returns_to_select"]
|
||
notes = "drag to crop; double-click opens Set Page Boxes"
|
||
|
||
[[feature]]
|
||
id = "organize.remove-white-margins"
|
||
title = "Remove white margins"
|
||
area = "D"
|
||
tier = "P2"
|
||
milestone = "M4"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.change-page-size"
|
||
title = "Change page size"
|
||
area = "D"
|
||
tier = "P2"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.page-transitions"
|
||
title = "Page transitions"
|
||
area = "D"
|
||
tier = "P2"
|
||
milestone = "M4"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.page-tab-order"
|
||
title = "Page tab order property"
|
||
area = "D"
|
||
tier = "P1"
|
||
milestone = "M6"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.page-actions"
|
||
title = "Page open/close actions"
|
||
area = "D"
|
||
tier = "P2"
|
||
milestone = "M6"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.page-templates"
|
||
title = "Page templates"
|
||
area = "D"
|
||
tier = "P3"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.bookmark-add"
|
||
title = "Add a bookmark (⌘B)"
|
||
area = "D"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["bookmark.add"]
|
||
tools = ["bookmark_add"]
|
||
evidence = ["crates/ui-egui/tests/bookmarks.rs::new_rename_reorder_indent_delete_and_undo", "crates/organize/src/tests.rs::bookmarks_add_rename_move_delete", "crates/engine/src/tests.rs::bookmark_edits_show_in_the_viewer_undo_and_save", "crates/automation/tests/automation.rs::bookmarks_through_tools"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.bookmark-rename"
|
||
title = "Rename bookmarks"
|
||
area = "D"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = ["bookmark_rename"]
|
||
evidence = ["crates/ui-egui/tests/bookmarks.rs::new_rename_reorder_indent_delete_and_undo", "crates/ui-egui/tests/bookmarks.rs::escape_cancels_a_rename", "crates/organize/src/tests.rs::bookmarks_add_rename_move_delete", "crates/organize/src/tests.rs::bookmark_edits_keep_unknown_keys_and_touch_few_objects"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.bookmark-delete"
|
||
title = "Delete bookmarks"
|
||
area = "D"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = ["bookmark_delete"]
|
||
evidence = ["crates/ui-egui/tests/bookmarks.rs::new_rename_reorder_indent_delete_and_undo", "crates/organize/src/tests.rs::bookmarks_add_rename_move_delete", "crates/engine/src/tests.rs::bookmark_edits_show_in_the_viewer_undo_and_save"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.bookmark-move"
|
||
title = "Reorder and nest bookmarks"
|
||
area = "D"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = ["bookmark_move"]
|
||
evidence = ["crates/ui-egui/tests/bookmarks.rs::new_rename_reorder_indent_delete_and_undo", "crates/organize/src/tests.rs::bookmarks_add_rename_move_delete", "crates/engine/src/tests.rs::bookmark_edits_show_in_the_viewer_undo_and_save", "crates/automation/tests/automation.rs::bookmarks_through_tools"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.bookmark-set-destination"
|
||
title = "Set a bookmark's destination"
|
||
area = "D"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = ["bookmark_set_page"]
|
||
evidence = ["crates/organize/src/tests.rs::bookmarks_add_rename_move_delete", "crates/engine/src/tests.rs::bookmark_edits_show_in_the_viewer_undo_and_save", "crates/automation/tests/automation.rs::bookmarks_through_tools"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.bookmark-appearance"
|
||
title = "Bookmark style and colour"
|
||
area = "D"
|
||
tier = "P2"
|
||
milestone = "M4"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.bookmark-actions"
|
||
title = "Bookmark actions"
|
||
area = "D"
|
||
tier = "P1"
|
||
milestone = "M4"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.bookmark-expand-collapse"
|
||
title = "Expand and collapse all bookmarks"
|
||
area = "D"
|
||
tier = "P1"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["file.properties"]
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/bookmarks.rs::expanding_and_collapsing_all_bookmarks"]
|
||
notes = "Bookmarks panel options: expand all, expand top-level, collapse all"
|
||
|
||
[[feature]]
|
||
id = "organize.bookmarks-from-structure"
|
||
title = "Generate bookmarks from headings or tags"
|
||
area = "D"
|
||
tier = "P1"
|
||
milestone = "M4"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.attachments-add"
|
||
title = "Add file attachments"
|
||
area = "D"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.attachments-delete"
|
||
title = "Delete attachments"
|
||
area = "D"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.attachments-description"
|
||
title = "Edit attachment descriptions"
|
||
area = "D"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.associated-files"
|
||
title = "Associated files with relationships (PDF/A-3, PDF 2.0)"
|
||
area = "D"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.portfolio-view"
|
||
title = "View PDF Portfolios as a file list"
|
||
area = "D"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.portfolio-extract"
|
||
title = "Extract files from a portfolio"
|
||
area = "D"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.portfolio-create"
|
||
title = "Create PDF Portfolios"
|
||
area = "D"
|
||
tier = "P3"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.document-properties"
|
||
title = "Document properties dialog (description, fonts, advanced)"
|
||
area = "D"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = ["file.properties"]
|
||
tools = ["doc_info"]
|
||
evidence = ["crates/ui-egui/tests/editing.rs::document_properties_edit_is_one_undoable_step", "crates/ui-egui/tests/editing.rs::cancelling_properties_discards_the_draft"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.edit-metadata"
|
||
title = "Edit title, author, subject and keywords"
|
||
area = "D"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["file.properties"]
|
||
tools = ["doc_set_info"]
|
||
evidence = ["crates/organize/src/tests.rs::document_info_edits_round_trip_with_unicode", "crates/ui-egui/tests/editing.rs::document_properties_edit_is_one_undoable_step"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.fonts-list"
|
||
title = "List fonts with type and embedding status"
|
||
area = "D"
|
||
tier = "P1"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = ["file.properties"]
|
||
tools = ["doc_info"]
|
||
evidence = ["crates/ui-egui/tests/view.rs::fonts_tab_lists_fonts_and_embedding"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.initial-view"
|
||
title = "Edit initial view (layout, magnification, panels, window options)"
|
||
area = "D"
|
||
tier = "P1"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["file.properties"]
|
||
tools = ["doc_initial_view"]
|
||
evidence = ["crates/organize/src/tests.rs::initial_view_round_trips_and_keeps_scripts", "crates/ui-egui/tests/viewer.rs::initial_view_is_edited_and_honoured_on_open", "crates/automation/tests/automation.rs::initial_view_through_tools"]
|
||
notes = "navigation panel, layout, magnification, page, window and interface options; honoured on open; script open actions kept"
|
||
|
||
[[feature]]
|
||
id = "organize.custom-properties"
|
||
title = "Custom document properties"
|
||
area = "D"
|
||
tier = "P2"
|
||
milestone = "M4"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.xmp-editor"
|
||
title = "Additional metadata (XMP) editor"
|
||
area = "D"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.document-language"
|
||
title = "Set document language and binding"
|
||
area = "D"
|
||
tier = "P1"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = ["file.properties"]
|
||
tools = ["doc_initial_view"]
|
||
evidence = ["crates/organize/src/tests.rs::initial_view_round_trips_and_keeps_scripts", "crates/automation/tests/automation.rs::initial_view_through_tools"]
|
||
notes = "Advanced ▸ Reading Options: binding and language"
|
||
|
||
[[feature]]
|
||
id = "organize.print-presets"
|
||
title = "Print dialog presets in document properties"
|
||
area = "D"
|
||
tier = "P2"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "organize.base-url"
|
||
title = "Base URL for relative links"
|
||
area = "D"
|
||
tier = "P3"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
# ==================================================================================================
|
||
# E. Comments and review
|
||
# ==================================================================================================
|
||
|
||
[[feature]]
|
||
id = "comment.list-view"
|
||
title = "Comments panel with threaded replies (read-only)"
|
||
area = "E"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = ["comment.list"]
|
||
tools = ["comment_list"]
|
||
evidence = ["crates/ui-egui/tests/ui.rs::opening_a_pdf_shows_comments_and_bookmarks", "crates/ui-egui/tests/comments.rs::the_panel_posts_comments_and_replies"]
|
||
notes = "cards grouped by page with replies, status badges, selection, reply box and an Add a comment box"
|
||
|
||
[[feature]]
|
||
id = "comment.appearance-generation"
|
||
title = "Generate correct appearances for every markup type"
|
||
area = "E"
|
||
tier = "P0"
|
||
milestone = "M5"
|
||
status = "partial"
|
||
commands = []
|
||
tools = ["comment_add"]
|
||
evidence = ["crates/annot/src/tests.rs::every_tool_creates_a_drawable_comment", "crates/engine/src/tests.rs::highlight_multiplies_over_text", "crates/annot/src/tests.rs::polygons_clouds_connected_lines_callouts_and_carets_are_drawn"]
|
||
notes = "appearances for every type PrintCraft creates (highlight multiplies; clouds, callouts, stamps, polygons, carets); rich text not yet"
|
||
|
||
[[feature]]
|
||
id = "comment.sticky-note"
|
||
title = "Sticky note comments"
|
||
area = "E"
|
||
tier = "P0"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.note"]
|
||
tools = ["comment_add"]
|
||
evidence = ["crates/ui-egui/tests/comments.rs::sticky_note_composer_posts_and_returns_to_select", "crates/annot/src/tests.rs::every_tool_creates_a_drawable_comment"]
|
||
notes = "our own Comment/Note icon drawings, with a pop-up"
|
||
|
||
[[feature]]
|
||
id = "comment.highlight"
|
||
title = "Highlight text"
|
||
area = "E"
|
||
tier = "P0"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.highlight"]
|
||
tools = ["comment_add"]
|
||
evidence = ["crates/ui-egui/tests/comments.rs::dragging_over_text_with_the_highlighter_highlights_it", "crates/engine/src/tests.rs::highlight_multiplies_over_text"]
|
||
notes = "drag over text, or pick the tool with text selected; agents can highlight by text search"
|
||
|
||
[[feature]]
|
||
id = "comment.underline"
|
||
title = "Underline text"
|
||
area = "E"
|
||
tier = "P0"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.underline"]
|
||
tools = ["comment_add"]
|
||
evidence = ["crates/annot/src/tests.rs::every_tool_creates_a_drawable_comment", "crates/ui-egui/tests/comments.rs::a_selection_made_first_is_marked_when_a_tool_is_picked"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "comment.strikeout"
|
||
title = "Strikethrough text"
|
||
area = "E"
|
||
tier = "P0"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.strikeout"]
|
||
tools = ["comment_add"]
|
||
evidence = ["crates/ui-egui/tests/comments.rs::a_selection_made_first_is_marked_when_a_tool_is_picked", "crates/annot/src/tests.rs::every_tool_creates_a_drawable_comment"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "comment.squiggly"
|
||
title = "Squiggly underline"
|
||
area = "E"
|
||
tier = "P2"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.squiggly"]
|
||
tools = ["comment_add"]
|
||
evidence = ["crates/annot/src/tests.rs::every_tool_creates_a_drawable_comment", "crates/ui-egui/tests/comments.rs::the_squiggly_tool_marks_selected_text"]
|
||
notes = "Highlight ▸ Squiggly underline (with Underline and Strikethrough), the agent tool and the engine"
|
||
|
||
[[feature]]
|
||
id = "comment.replace-text"
|
||
title = "Replace-text proposal (strikeout + caret)"
|
||
area = "E"
|
||
tier = "P1"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.replace"]
|
||
tools = ["comment_add"]
|
||
evidence = ["crates/annot/src/tests.rs::replace_text_groups_a_strikeout_and_a_caret", "crates/ui-egui/tests/comments.rs::replacing_text_strikes_it_and_adds_a_caret", "crates/automation/tests/automation.rs::drawing_comments_through_tools"]
|
||
notes = "strikeout (/IT StrikeOutTextEdit) and a grouped caret (/RT /Group) with the replacement"
|
||
|
||
[[feature]]
|
||
id = "comment.insert-text"
|
||
title = "Insert-text proposal (caret)"
|
||
area = "E"
|
||
tier = "P1"
|
||
milestone = "M5"
|
||
status = "partial"
|
||
commands = ["comment.caret"]
|
||
tools = ["comment_add"]
|
||
evidence = ["crates/annot/src/tests.rs::polygons_clouds_connected_lines_callouts_and_carets_are_drawn", "crates/ui-egui/tests/comments.rs::polygons_clouds_connected_lines_callouts_and_inserted_text", "crates/automation/tests/automation.rs::drawing_comments_through_tools"]
|
||
notes = "caret at a click; not yet snapped to the text insertion point"
|
||
|
||
[[feature]]
|
||
id = "comment.typewriter"
|
||
title = "Add text comment (typewriter)"
|
||
area = "E"
|
||
tier = "P0"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["sign.fill.text"]
|
||
tools = ["fill_sign_add"]
|
||
evidence = ["crates/annot/src/tests.rs::fill_and_sign_items_are_drawn", "crates/ui-egui/tests/fill_sign.rs::text_marks_and_date"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "comment.text-box"
|
||
title = "Text box comment with rich text"
|
||
area = "E"
|
||
tier = "P0"
|
||
milestone = "M5"
|
||
status = "partial"
|
||
commands = ["comment.freetext"]
|
||
tools = ["comment_add"]
|
||
evidence = ["crates/ui-egui/tests/comments.rs::sticky_note_composer_posts_and_returns_to_select", "crates/annot/src/tests.rs::text_box_text_and_colour_changes_are_drawn"]
|
||
notes = "Helvetica text with colour, size and alignment; /RC (XHTML) and /DS are written and kept in step with edits; per-run formatting (bold/italic spans) is not yet"
|
||
|
||
[[feature]]
|
||
id = "comment.callout"
|
||
title = "Callout comment"
|
||
area = "E"
|
||
tier = "P1"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.callout"]
|
||
tools = ["comment_add"]
|
||
evidence = ["crates/annot/src/tests.rs::polygons_clouds_connected_lines_callouts_and_carets_are_drawn", "crates/annot/src/tests.rs::editing_a_callout_refits_the_box_and_keeps_the_leader", "crates/ui-egui/tests/comments.rs::polygons_clouds_connected_lines_callouts_and_inserted_text", "crates/automation/tests/automation.rs::drawing_comments_through_tools"]
|
||
notes = "drag from the target to the box; resizing re-attaches the leader, and so does editing the text"
|
||
|
||
[[feature]]
|
||
id = "comment.pencil"
|
||
title = "Freehand drawing (ink)"
|
||
area = "E"
|
||
tier = "P0"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.ink"]
|
||
tools = ["comment_add"]
|
||
evidence = ["crates/ui-egui/tests/comments.rs::shapes_and_ink_are_drawn_by_dragging"]
|
||
notes = "one ink annotation per stroke"
|
||
|
||
[[feature]]
|
||
id = "comment.eraser"
|
||
title = "Eraser for ink strokes"
|
||
area = "E"
|
||
tier = "P1"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.eraser"]
|
||
tools = []
|
||
evidence = ["crates/annot/src/tests.rs::the_eraser_cuts_strokes_and_removes_empty_drawings", "crates/ui-egui/tests/comments.rs::erasing_part_of_a_drawing"]
|
||
notes = "cuts strokes where the eraser passes; an emptied drawing is deleted; one undo step"
|
||
|
||
[[feature]]
|
||
id = "comment.line"
|
||
title = "Line"
|
||
area = "E"
|
||
tier = "P0"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.line"]
|
||
tools = ["comment_add"]
|
||
evidence = ["crates/annot/src/tests.rs::every_tool_creates_a_drawable_comment"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "comment.arrow"
|
||
title = "Arrow"
|
||
area = "E"
|
||
tier = "P0"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.arrow"]
|
||
tools = ["comment_add"]
|
||
evidence = ["crates/ui-egui/tests/comments.rs::shapes_and_ink_are_drawn_by_dragging"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "comment.rectangle"
|
||
title = "Rectangle"
|
||
area = "E"
|
||
tier = "P0"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.square"]
|
||
tools = ["comment_add", "comment_edit"]
|
||
evidence = ["crates/ui-egui/tests/comments.rs::shapes_and_ink_are_drawn_by_dragging", "crates/annot/src/tests.rs::restyle_and_resize_redraw_the_appearance"]
|
||
notes = "resize with the 8 handles"
|
||
|
||
[[feature]]
|
||
id = "comment.oval"
|
||
title = "Oval"
|
||
area = "E"
|
||
tier = "P0"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.circle"]
|
||
tools = ["comment_add"]
|
||
evidence = ["crates/annot/src/tests.rs::every_tool_creates_a_drawable_comment"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "comment.polygon"
|
||
title = "Polygon"
|
||
area = "E"
|
||
tier = "P1"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.polygon"]
|
||
tools = ["comment_add"]
|
||
evidence = ["crates/annot/src/tests.rs::polygons_clouds_connected_lines_callouts_and_carets_are_drawn", "crates/ui-egui/tests/comments.rs::polygons_clouds_connected_lines_callouts_and_inserted_text", "crates/automation/tests/automation.rs::drawing_comments_through_tools"]
|
||
notes = "click the corners; Enter, double-click or the first point finishes"
|
||
|
||
[[feature]]
|
||
id = "comment.polyline"
|
||
title = "Connected lines (polyline)"
|
||
area = "E"
|
||
tier = "P1"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.polyline"]
|
||
tools = ["comment_add"]
|
||
evidence = ["crates/annot/src/tests.rs::polygons_clouds_connected_lines_callouts_and_carets_are_drawn", "crates/ui-egui/tests/comments.rs::polygons_clouds_connected_lines_callouts_and_inserted_text", "crates/automation/tests/automation.rs::drawing_comments_through_tools"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "comment.cloud"
|
||
title = "Cloud shape"
|
||
area = "E"
|
||
tier = "P1"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.cloud"]
|
||
tools = ["comment_add"]
|
||
evidence = ["crates/annot/src/tests.rs::polygons_clouds_connected_lines_callouts_and_carets_are_drawn", "crates/annot/src/tests.rs::cloud_paths_bulge_outwards_either_way_round", "crates/ui-egui/tests/comments.rs::polygons_clouds_connected_lines_callouts_and_inserted_text", "crates/automation/tests/automation.rs::drawing_comments_through_tools"]
|
||
notes = "polygon with a cloudy border (/BE /S /C, /IT /PolygonCloud)"
|
||
|
||
[[feature]]
|
||
id = "comment.area-highlight"
|
||
title = "Area highlight"
|
||
area = "E"
|
||
tier = "P1"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.highlight"]
|
||
tools = ["comment_add"]
|
||
evidence = ["crates/ui-egui/tests/comments.rs::highlighting_an_area_off_the_text"]
|
||
notes = "drag the highlighter off text to highlight a box"
|
||
|
||
[[feature]]
|
||
id = "comment.attach-file-comment"
|
||
title = "Attach a file as a comment"
|
||
area = "E"
|
||
tier = "P1"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.attach"]
|
||
tools = ["comment_add"]
|
||
evidence = ["crates/annot/src/tests.rs::files_attach_as_comments", "crates/ui-egui/tests/comments.rs::attaching_a_file_as_a_comment", "crates/automation/tests/automation.rs::drawing_comments_through_tools"]
|
||
notes = "push pin, paperclip, graph or tag icon; the file is embedded and listed in the Attachments panel"
|
||
|
||
[[feature]]
|
||
id = "comment.record-audio"
|
||
title = "Sound comments"
|
||
area = "E"
|
||
tier = "P3"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "comment.image-comment"
|
||
title = "Add an image as a comment"
|
||
area = "E"
|
||
tier = "P2"
|
||
milestone = "M5"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "comment.line-endings"
|
||
title = "All ten line-ending styles and dashed/cloudy borders"
|
||
area = "E"
|
||
tier = "P1"
|
||
milestone = "M5"
|
||
status = "partial"
|
||
commands = []
|
||
tools = ["comment_add"]
|
||
evidence = ["crates/annot/src/tests.rs::every_tool_creates_a_drawable_comment"]
|
||
notes = "None, OpenArrow and ClosedArrow are drawn; other endings are kept but not redrawn"
|
||
|
||
[[feature]]
|
||
id = "comment.properties-dialog"
|
||
title = "Comment properties (appearance, author, subject)"
|
||
area = "E"
|
||
tier = "P0"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.list"]
|
||
tools = ["comment_edit"]
|
||
evidence = ["crates/ui-egui/tests/comments.rs::comment_properties_change_appearance_and_author", "crates/annot/src/tests.rs::author_subject_and_note_icon_change"]
|
||
notes = "Appearance (colour, opacity, thickness, note icon), General (author, subject), Review History"
|
||
|
||
[[feature]]
|
||
id = "comment.properties-bar"
|
||
title = "Comment properties bar"
|
||
area = "E"
|
||
tier = "P1"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.note"]
|
||
tools = ["comment_edit"]
|
||
evidence = ["crates/annot/src/tests.rs::restyle_and_resize_redraw_the_appearance", "crates/ui-egui/tests/comments.rs::the_opacity_set_for_a_tool_goes_into_its_new_comments"]
|
||
notes = "quick-bar colour swatches, opacity (10–100 %) and line thickness per tool; colour from the context menu"
|
||
|
||
[[feature]]
|
||
id = "comment.lock-comment"
|
||
title = "Lock comments"
|
||
area = "E"
|
||
tier = "P1"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = ["comment_lock"]
|
||
evidence = ["crates/engine/src/tests.rs::comments_lock_take_checkmarks_hide_and_summarize", "crates/ui-egui/tests/comments.rs::comments_take_checkmarks_lock_hide_and_summarize", "crates/automation/tests/automation.rs::comment_checkmarks_locks_hiding_and_summaries_through_tools"]
|
||
notes = "Comment Properties ▸ Locked; locked comments refuse move, resize, restyle and delete; text stays editable"
|
||
|
||
[[feature]]
|
||
id = "comment.default-properties"
|
||
title = "Make current properties the tool default"
|
||
area = "E"
|
||
tier = "P1"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.list"]
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/comments.rs::make_current_properties_default"]
|
||
notes = "colour, opacity and line width become the tool's defaults (canvas and Comments panel menus)"
|
||
|
||
[[feature]]
|
||
id = "comment.author-identity"
|
||
title = "Author name from identity preferences"
|
||
area = "E"
|
||
tier = "P1"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = ["comment_add"]
|
||
evidence = ["crates/ui-egui/tests/comments.rs::dragging_over_text_with_the_highlighter_highlights_it", "crates/ui-egui/tests/identity.rs::the_identity_name_signs_new_comments_and_is_remembered"]
|
||
notes = "Preferences ▸ Identity: the name on new comments (default the login name, as Acrobat), remembered across restarts"
|
||
|
||
[[feature]]
|
||
id = "comment.reply"
|
||
title = "Reply to comments"
|
||
area = "E"
|
||
tier = "P0"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.list"]
|
||
tools = ["comment_reply"]
|
||
evidence = ["crates/ui-egui/tests/comments.rs::the_panel_posts_comments_and_replies", "crates/annot/src/tests.rs::replies_and_status_thread_onto_the_parent"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "comment.set-status"
|
||
title = "Set review status (accepted, rejected, cancelled, completed)"
|
||
area = "E"
|
||
tier = "P0"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.list"]
|
||
tools = ["comment_set_status"]
|
||
evidence = ["crates/annot/src/tests.rs::replies_and_status_thread_onto_the_parent", "crates/engine/src/tests.rs::comments_are_added_drawn_threaded_and_undone"]
|
||
notes = "status replies (/State, /StateModel Review), shown as a badge"
|
||
|
||
[[feature]]
|
||
id = "comment.checkmark"
|
||
title = "Mark comments with a checkmark"
|
||
area = "E"
|
||
tier = "P1"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = ["comment_mark"]
|
||
evidence = ["crates/engine/src/tests.rs::comments_lock_take_checkmarks_hide_and_summarize", "crates/ui-egui/tests/comments.rs::comments_take_checkmarks_lock_hide_and_summarize", "crates/automation/tests/automation.rs::comment_checkmarks_locks_hiding_and_summaries_through_tools"]
|
||
notes = "hidden /StateModel /Marked reply; card toggle and context menu"
|
||
|
||
[[feature]]
|
||
id = "comment.delete-comment"
|
||
title = "Delete comments"
|
||
area = "E"
|
||
tier = "P0"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.list"]
|
||
tools = ["comment_delete"]
|
||
evidence = ["crates/ui-egui/tests/comments.rs::comments_are_selected_moved_and_deleted_with_the_select_tool", "crates/annot/src/tests.rs::delete_takes_popup_and_reply_chain"]
|
||
notes = "deletes the pop-up and the reply thread"
|
||
|
||
[[feature]]
|
||
id = "comment.edit-comment-text"
|
||
title = "Edit comment text"
|
||
area = "E"
|
||
tier = "P0"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.list"]
|
||
tools = ["comment_edit"]
|
||
evidence = ["crates/annot/src/tests.rs::text_box_text_and_colour_changes_are_drawn", "crates/annot/src/tests.rs::editing_a_text_box_refits_its_rectangle", "crates/automation/tests/automation.rs::comments_through_tools"]
|
||
notes = "double-click a comment or card, or Edit in the menus; a text box's rectangle follows the new text (wrap width and top edge stay, the height fits the wrapped lines)"
|
||
|
||
[[feature]]
|
||
id = "comment.copy-comment-text"
|
||
title = "Copy comment text"
|
||
area = "E"
|
||
tier = "P1"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/comments.rs::comments_take_checkmarks_lock_hide_and_summarize"]
|
||
notes = "card and canvas context menus"
|
||
|
||
[[feature]]
|
||
id = "comment.filter-comments"
|
||
title = "Filter comments (type, author, status, colour, checkmark)"
|
||
area = "E"
|
||
tier = "P0"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.list"]
|
||
tools = ["comment_list"]
|
||
evidence = ["crates/ui-egui/tests/comments.rs::the_panel_filters_and_sorts", "crates/ui-egui/tests/comments.rs::the_panel_filters_by_colour_and_checkmark"]
|
||
notes = "type, author, status, colour and checkmark"
|
||
|
||
[[feature]]
|
||
id = "comment.sort-comments"
|
||
title = "Sort comments (page, author, date, type, colour)"
|
||
area = "E"
|
||
tier = "P0"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.list"]
|
||
tools = ["comment_list"]
|
||
evidence = ["crates/ui-egui/tests/comments.rs::the_panel_filters_and_sorts"]
|
||
notes = "page, author, date, type, colour, with group headers"
|
||
|
||
[[feature]]
|
||
id = "comment.search-comments"
|
||
title = "Search comments"
|
||
area = "E"
|
||
tier = "P1"
|
||
milestone = "M5"
|
||
status = "partial"
|
||
commands = ["comment.list"]
|
||
tools = ["comment_list"]
|
||
evidence = ["crates/ui-egui/tests/comments.rs::the_panel_posts_comments_and_replies"]
|
||
notes = "search by author, text and type in the panel; not yet in the Find bar"
|
||
|
||
[[feature]]
|
||
id = "comment.show-hide-comments"
|
||
title = "Show or hide all comments and pop-ups"
|
||
area = "E"
|
||
tier = "P1"
|
||
milestone = "M5"
|
||
status = "partial"
|
||
commands = ["comment.hide_all"]
|
||
tools = ["comments_hide"]
|
||
evidence = ["crates/engine/src/tests.rs::comments_lock_take_checkmarks_hide_and_summarize", "crates/ui-egui/tests/comments.rs::comments_take_checkmarks_lock_hide_and_summarize", "crates/automation/tests/automation.rs::comment_checkmarks_locks_hiding_and_summaries_through_tools"]
|
||
notes = "hide/show all comments (fields and links still draw); per-type hiding not yet"
|
||
|
||
[[feature]]
|
||
id = "comment.popups"
|
||
title = "Open and reposition comment pop-ups"
|
||
area = "E"
|
||
tier = "P1"
|
||
milestone = "M5"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "comment.connector-lines"
|
||
title = "Connector lines between markups and pop-ups"
|
||
area = "E"
|
||
tier = "P2"
|
||
milestone = "M5"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "comment.review-history"
|
||
title = "Review history of state changes"
|
||
area = "E"
|
||
tier = "P2"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.list"]
|
||
tools = ["comment_list"]
|
||
evidence = ["crates/ui-egui/tests/comments.rs::comment_properties_change_appearance_and_author"]
|
||
notes = "status changes listed in Comment Properties"
|
||
|
||
[[feature]]
|
||
id = "comment.stamps-standard"
|
||
title = "Standard business stamps"
|
||
area = "E"
|
||
tier = "P1"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.stamp"]
|
||
tools = ["comment_add"]
|
||
evidence = ["crates/annot/src/tests.rs::stamps_draw_their_label_and_by_line", "crates/ui-egui/tests/stamps.rs::choosing_and_placing_stamps", "crates/automation/tests/automation.rs::stamps_through_tools"]
|
||
notes = "12 standard business stamps in PrintCraft's own design; ISO 32000 stamp names where defined"
|
||
|
||
[[feature]]
|
||
id = "comment.stamps-sign-here"
|
||
title = "Sign-here stamps"
|
||
area = "E"
|
||
tier = "P1"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.stamp"]
|
||
tools = ["comment_add"]
|
||
evidence = ["crates/annot/src/tests.rs::stamps_draw_their_label_and_by_line", "crates/ui-egui/tests/stamps.rs::choosing_and_placing_stamps", "crates/automation/tests/automation.rs::stamps_through_tools"]
|
||
notes = "Accepted, Initial Here, Rejected, Sign Here, Witness as pointed tags"
|
||
|
||
[[feature]]
|
||
id = "comment.stamps-dynamic"
|
||
title = "Dynamic stamps (name and date)"
|
||
area = "E"
|
||
tier = "P1"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["comment.stamp"]
|
||
tools = ["comment_add"]
|
||
evidence = ["crates/annot/src/tests.rs::stamps_draw_their_label_and_by_line", "crates/ui-egui/tests/stamps.rs::choosing_and_placing_stamps", "crates/automation/tests/automation.rs::stamps_through_tools"]
|
||
notes = "Approved, Confidential, Received, Reviewed, Revised with a By author at time, date line (local time)"
|
||
|
||
[[feature]]
|
||
id = "comment.stamps-custom"
|
||
title = "Custom stamps from images or PDF pages"
|
||
area = "E"
|
||
tier = "P1"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.stamp"]
|
||
tools = ["stamp_custom"]
|
||
evidence = ["crates/ui-egui/tests/stamps.rs::creating_placing_and_keeping_custom_stamps", "crates/automation/tests/automation.rs::stamps_through_tools"]
|
||
notes = "Create custom stamp from a PDF page or an image (category and name), kept in the app's settings; placed at natural size (at most 200 pt) keeping its shape; resizable (appearance redrawn from /PCPicture); delete from the palette"
|
||
|
||
[[feature]]
|
||
id = "comment.stamps-manage"
|
||
title = "Manage stamp categories and imports"
|
||
area = "E"
|
||
tier = "P2"
|
||
milestone = "M5"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "comment.stamps-rotate"
|
||
title = "Rotate stamps with a handle"
|
||
area = "E"
|
||
tier = "P2"
|
||
milestone = "M5"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "comment.import-fdf-xfdf"
|
||
title = "Import comments from FDF/XFDF"
|
||
area = "E"
|
||
tier = "P1"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.import"]
|
||
tools = ["doc_import_data"]
|
||
evidence = ["crates/xfdf/src/tests.rs::xfdf_round_trips_comments_and_values", "crates/xfdf/src/tests.rs::fdf_round_trips_values_and_comments", "crates/automation/tests/automation.rs::comments_and_form_data_travel_as_xfdf_fdf_and_text"]
|
||
notes = "replies, pop-ups, ink, quads, line ends; same /NM replaces"
|
||
|
||
[[feature]]
|
||
id = "comment.export-fdf-xfdf"
|
||
title = "Export all comments to FDF/XFDF"
|
||
area = "E"
|
||
tier = "P1"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.export"]
|
||
tools = ["doc_export_data"]
|
||
evidence = ["crates/xfdf/src/tests.rs::xfdf_round_trips_comments_and_values", "crates/xfdf/src/tests.rs::fdf_round_trips_values_and_comments", "crates/automation/tests/automation.rs::comments_and_form_data_travel_as_xfdf_fdf_and_text"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "comment.export-selected"
|
||
title = "Export selected comments"
|
||
area = "E"
|
||
tier = "P2"
|
||
milestone = "M5"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "comment.import-from-pdf"
|
||
title = "Import comments from another PDF"
|
||
area = "E"
|
||
tier = "P2"
|
||
milestone = "M5"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "comment.summarize"
|
||
title = "Summarize comments into a new PDF"
|
||
area = "E"
|
||
tier = "P1"
|
||
milestone = "M5"
|
||
status = "partial"
|
||
commands = ["comment.summarize"]
|
||
tools = ["comments_summarize"]
|
||
evidence = ["crates/engine/src/tests.rs::comments_lock_take_checkmarks_hide_and_summarize", "crates/ui-egui/tests/comments.rs::comments_take_checkmarks_lock_hide_and_summarize", "crates/automation/tests/automation.rs::comment_checkmarks_locks_hiding_and_summaries_through_tools"]
|
||
notes = "Comments only layout, sorted by page, author, date or type; the layouts with the document and connector lines not yet"
|
||
|
||
[[feature]]
|
||
id = "comment.flatten"
|
||
title = "Flatten comments into page content"
|
||
area = "E"
|
||
tier = "P1"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["comment.flatten"]
|
||
tools = ["doc_flatten"]
|
||
evidence = ["crates/edit/src/tests.rs::flattening_draws_appearances_into_the_page_and_removes_the_comments", "crates/engine/src/tests.rs::flattening_keeps_the_look_and_drops_the_objects", "crates/automation/tests/automation.rs::flattening_through_tools"]
|
||
notes = "appearances become page content; pop-ups and replies removed"
|
||
|
||
[[feature]]
|
||
id = "comment.migrate"
|
||
title = "Migrate comments to a revised document"
|
||
area = "E"
|
||
tier = "P3"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "comment.measure-distance"
|
||
title = "Distance measuring tool"
|
||
area = "E"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "comment.measure-perimeter"
|
||
title = "Perimeter measuring tool"
|
||
area = "E"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "comment.measure-area"
|
||
title = "Area measuring tool"
|
||
area = "E"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "comment.measure-scale"
|
||
title = "Scale ratio and calibration per viewport"
|
||
area = "E"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "comment.measure-snap"
|
||
title = "Snap to paths, endpoints, midpoints and intersections"
|
||
area = "E"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "comment.measure-info-panel"
|
||
title = "Measurement info panel"
|
||
area = "E"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "comment.measure-export"
|
||
title = "Export measurements as CSV"
|
||
area = "E"
|
||
tier = "P3"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "comment.geospatial"
|
||
title = "Geospatial location and measuring"
|
||
area = "E"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "comment.shared-review"
|
||
title = "Send for comments / shared review"
|
||
area = "E"
|
||
tier = "N/A"
|
||
milestone = "none"
|
||
status = "na"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "Adobe cloud; exchange XFDF files or a pluggable review server instead"
|
||
|
||
[[feature]]
|
||
id = "comment.mentions-live-review"
|
||
title = "@mentions, presence and live co-review"
|
||
area = "E"
|
||
tier = "N/A"
|
||
milestone = "none"
|
||
status = "na"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "Adobe cloud; no local equivalent"
|
||
|
||
[[feature]]
|
||
id = "comment.review-tracker"
|
||
title = "Review tracker"
|
||
area = "E"
|
||
tier = "N/A"
|
||
milestone = "none"
|
||
status = "na"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "Adobe cloud / legacy"
|
||
|
||
[[feature]]
|
||
id = "comment.enable-reader-commenting"
|
||
title = "Enable commenting and measuring in Reader (usage rights)"
|
||
area = "E"
|
||
tier = "N/A"
|
||
milestone = "none"
|
||
status = "na"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "needs Adobe's private key; modern readers allow commenting anyway"
|
||
|
||
# ==================================================================================================
|
||
# F. Forms and JavaScript
|
||
# ==================================================================================================
|
||
|
||
[[feature]]
|
||
id = "form.field-list"
|
||
title = "Form fields panel listing every field and value"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = ["form.fields"]
|
||
tools = ["doc_info"]
|
||
evidence = ["crates/ui-egui/tests/view.rs::field_list_jumps_to_a_fields_page"]
|
||
notes = "read-only list with jump-to-field; filling and editing are separate features (M6)"
|
||
|
||
[[feature]]
|
||
id = "form.highlight-fields"
|
||
title = "Highlight existing fields"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/view.rs::highlight_fields_tints_the_field_area"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "form.field-appearance-display"
|
||
title = "Display checkbox, radio, list and signature widgets as designed"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M2"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/render/src/raster.rs::widget_appearance_states"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "form.fill-text"
|
||
title = "Fill text fields"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.fields"]
|
||
tools = ["form_fill", "form_fields"]
|
||
evidence = ["crates/ui-egui/tests/forms.rs::typing_into_a_text_field_and_tabbing_on", "crates/forms/src/tests.rs::text_fields_get_new_appearances", "crates/engine/src/tests.rs::filling_a_form_shows_saves_and_undoes"]
|
||
notes = "in-place editor; Enter or clicking away commits, Escape cancels; no format/keystroke JavaScript yet (M6.4)"
|
||
|
||
[[feature]]
|
||
id = "form.fill-checkbox-radio"
|
||
title = "Toggle checkboxes and radio buttons (radios in unison)"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.fields"]
|
||
tools = ["form_fill"]
|
||
evidence = ["crates/ui-egui/tests/forms.rs::check_boxes_radios_and_choices", "crates/forms/src/tests.rs::check_boxes_and_radios_switch_states"]
|
||
notes = "NoToggleToOff honoured; widgets without appearances get our own"
|
||
|
||
[[feature]]
|
||
id = "form.fill-choice"
|
||
title = "Choose from combo boxes and list boxes"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.fields"]
|
||
tools = ["form_fill"]
|
||
evidence = ["crates/ui-egui/tests/forms.rs::check_boxes_radios_and_choices", "crates/forms/src/tests.rs::choices_accept_exports_or_display_text"]
|
||
notes = "combo and list boxes, multi-select lists; editable combo boxes accept free text"
|
||
|
||
[[feature]]
|
||
id = "form.push-buttons"
|
||
title = "Push buttons run their actions"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "partial"
|
||
commands = ["form.fields"]
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/buttons.rs::buttons_reset_navigate_and_print", "crates/forms/src/af.rs::common_button_scripts_are_recognised", "crates/ui-egui/tests/javascript.rs::button_scripts_run_and_alert"]
|
||
notes = "Reset form, Named (print, page navigation), URI, Go to, and any JavaScript (run in the sandboxed engine); submit explained, not sent"
|
||
|
||
[[feature]]
|
||
id = "form.field-navigation"
|
||
title = "Tab between fields"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.fields"]
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/forms.rs::tabbing_into_a_filled_field_selects_it_so_typing_replaces"]
|
||
notes = "Tab / Shift+Tab move between text and choice fields in field order; entering with Tab selects the value"
|
||
|
||
[[feature]]
|
||
id = "form.appearance-regeneration"
|
||
title = "Regenerate field appearances (auto-size, comb, multiline, password)"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "partial"
|
||
commands = ["form.fields"]
|
||
tools = ["form_fill"]
|
||
evidence = ["crates/forms/src/tests.rs::text_fields_get_new_appearances", "crates/forms/src/tests.rs::choices_accept_exports_or_display_text"]
|
||
notes = "auto-size, comb, multiline, password, quadding, /MK colours; approximate Helvetica metrics, no /MK rotation yet"
|
||
|
||
[[feature]]
|
||
id = "form.rich-text-fields"
|
||
title = "Rich-text field values"
|
||
area = "F"
|
||
tier = "P2"
|
||
milestone = "M6"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "form.af-number"
|
||
title = "Number and currency formatting (AFNumber)"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.field.properties"]
|
||
tools = ["form_set_props", "form_fill"]
|
||
evidence = ["crates/forms/src/af.rs::numbers_format_like_acrobat", "crates/forms/src/tests.rs::formats_validation_and_calculations_run_like_acrobat", "crates/automation/tests/automation.rs::form_formats_and_calculations_through_tools"]
|
||
notes = "AFNumber_Format/Keystroke run natively: decimals, 5 separator styles, currency before/after, negative styles (red shown as minus/parentheses)"
|
||
|
||
[[feature]]
|
||
id = "form.af-percent"
|
||
title = "Percentage formatting"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.field.properties"]
|
||
tools = ["form_set_props", "form_fill"]
|
||
evidence = ["crates/forms/src/af.rs::numbers_format_like_acrobat"]
|
||
notes = "AFPercent_Format/Keystroke"
|
||
|
||
[[feature]]
|
||
id = "form.af-date-time"
|
||
title = "Date and time formatting and parsing"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.field.properties"]
|
||
tools = ["form_set_props", "form_fill"]
|
||
evidence = ["crates/forms/src/af.rs::dates_times_and_masks", "crates/forms/src/tests.rs::formats_validation_and_calculations_run_like_acrobat"]
|
||
notes = "AFDate_FormatEx/KeystrokeEx, AFDate_Format presets, AFTime_Format(Ex): lenient parsing, month and day names, impossible dates refused"
|
||
|
||
[[feature]]
|
||
id = "form.af-special"
|
||
title = "Special formats (zip, phone, SSN, arbitrary mask)"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.field.properties"]
|
||
tools = ["form_set_props", "form_fill"]
|
||
evidence = ["crates/forms/src/af.rs::dates_times_and_masks"]
|
||
notes = "zip, zip+4, phone, SSN, arbitrary masks (9 A O X)"
|
||
|
||
[[feature]]
|
||
id = "form.af-range-validate"
|
||
title = "Range validation"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.field.properties"]
|
||
tools = ["form_set_props", "form_fill"]
|
||
evidence = ["crates/forms/src/af.rs::validation_and_calculation", "crates/forms/src/tests.rs::formats_validation_and_calculations_run_like_acrobat", "crates/automation/tests/automation.rs::form_formats_and_calculations_through_tools"]
|
||
notes = "AFRange_Validate with Acrobat's messages"
|
||
|
||
[[feature]]
|
||
id = "form.af-simple-calculate"
|
||
title = "Sum, product, average, min, max calculations"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.field.properties"]
|
||
tools = ["form_set_props", "form_fill"]
|
||
evidence = ["crates/forms/src/af.rs::validation_and_calculation", "crates/forms/src/tests.rs::formats_validation_and_calculations_run_like_acrobat"]
|
||
notes = "SUM, PRD, AVG, MIN, MAX over fields and groups"
|
||
|
||
[[feature]]
|
||
id = "form.simplified-notation"
|
||
title = "Simplified field notation calculations"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.field.properties"]
|
||
tools = ["form_set_props", "form_fill"]
|
||
evidence = ["crates/forms/src/af.rs::validation_and_calculation", "crates/forms/src/tests.rs::formats_validation_and_calculations_run_like_acrobat", "crates/automation/tests/automation.rs::form_formats_and_calculations_through_tools"]
|
||
notes = "BVCALC expressions with + - * / and parentheses, escaped names"
|
||
|
||
[[feature]]
|
||
id = "form.custom-scripts"
|
||
title = "Custom format, keystroke, validate and calculate scripts"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.prepare"]
|
||
tools = ["form_set_script"]
|
||
evidence = ["crates/engine/src/tests.rs::form_javascript_validates_calculates_and_formats", "crates/automation/tests/automation.rs::javascript_through_tools"]
|
||
notes = "Custom keystroke (commit), validate, calculate and format scripts run in Acrobat's event order; AF calls still run natively."
|
||
|
||
[[feature]]
|
||
id = "form.calculation-order"
|
||
title = "Calculation order"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.field.properties"]
|
||
tools = ["form_set_props", "form_fill"]
|
||
evidence = ["crates/forms/src/tests.rs::formats_validation_and_calculations_run_like_acrobat"]
|
||
notes = "/CO order, then other calculated fields; recalculated after every fill and Clear form"
|
||
|
||
[[feature]]
|
||
id = "form.event-order"
|
||
title = "Keystroke, validate, calculate, format event order"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.prepare"]
|
||
tools = ["form_fill"]
|
||
evidence = ["crates/engine/src/tests.rs::form_javascript_validates_calculates_and_formats"]
|
||
notes = "Keystroke (commit) \u2192 Validate \u2192 value \u2192 Calculate (in /CO order) \u2192 Format, for AF calls and custom scripts."
|
||
|
||
[[feature]]
|
||
id = "form.date-picker"
|
||
title = "Date picker for date fields"
|
||
area = "F"
|
||
tier = "P1"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = ["form_fill"]
|
||
evidence = ["crates/ui-egui/tests/forms.rs::date_fields_offer_a_calendar"]
|
||
notes = "a month calendar under date fields; the pick is written in the field's own date format"
|
||
|
||
[[feature]]
|
||
id = "form.auto-complete"
|
||
title = "Auto-complete from previous entries"
|
||
area = "F"
|
||
tier = "P2"
|
||
milestone = "M6"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "form.required-fields"
|
||
title = "Required-field highlighting"
|
||
area = "F"
|
||
tier = "P1"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = ["form_set_props"]
|
||
evidence = ["crates/ui-egui/tests/view.rs::required_fields_get_a_red_border_when_highlighting"]
|
||
notes = "required fields get a red border when fields are highlighted"
|
||
|
||
[[feature]]
|
||
id = "form.reset-form"
|
||
title = "Clear or reset a form"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.clear"]
|
||
tools = ["form_reset"]
|
||
evidence = ["crates/forms/src/tests.rs::reset_restores_defaults", "crates/ui-egui/tests/forms.rs::check_boxes_radios_and_choices"]
|
||
notes = "back to /DV; undoable"
|
||
|
||
[[feature]]
|
||
id = "form.submit-form"
|
||
title = "Submit form to a URL or email (with a prompt)"
|
||
area = "F"
|
||
tier = "P1"
|
||
milestone = "M6"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "form.import-data"
|
||
title = "Import form data (FDF, XFDF)"
|
||
area = "F"
|
||
tier = "P1"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.import_data"]
|
||
tools = ["doc_import_data"]
|
||
evidence = ["crates/xfdf/src/tests.rs::xfdf_round_trips_comments_and_values", "crates/xfdf/src/tests.rs::fdf_round_trips_values_and_comments", "crates/automation/tests/automation.rs::comments_and_form_data_travel_as_xfdf_fdf_and_text"]
|
||
notes = "XFDF, FDF; values go through formats and validation; read-only fields take data"
|
||
|
||
[[feature]]
|
||
id = "form.export-data"
|
||
title = "Export form data (FDF, XFDF)"
|
||
area = "F"
|
||
tier = "P1"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.export_data"]
|
||
tools = ["doc_export_data"]
|
||
evidence = ["crates/xfdf/src/tests.rs::xfdf_round_trips_comments_and_values", "crates/automation/tests/automation.rs::comments_and_form_data_travel_as_xfdf_fdf_and_text"]
|
||
notes = "XFDF, FDF"
|
||
|
||
[[feature]]
|
||
id = "form.data-xml-csv-txt"
|
||
title = "Form data as XML, CSV or tab-delimited text"
|
||
area = "F"
|
||
tier = "P1"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.export_data", "form.import_data"]
|
||
tools = ["doc_export_data", "doc_import_data"]
|
||
evidence = ["crates/xfdf/src/tests.rs::form_data_as_xml_csv_and_text", "crates/automation/tests/automation.rs::comments_and_form_data_travel_as_xfdf_fdf_and_text"]
|
||
notes = "Acrobat's XML (xfdf:original), CSV, tab-delimited"
|
||
|
||
[[feature]]
|
||
id = "form.merge-to-spreadsheet"
|
||
title = "Merge form data files into a spreadsheet"
|
||
area = "F"
|
||
tier = "P1"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.merge_data"]
|
||
tools = ["form_merge_data"]
|
||
evidence = ["crates/xfdf/src/tests.rs::data_files_merge_into_a_spreadsheet", "crates/automation/tests/automation.rs::merging_form_data_into_a_spreadsheet", "crates/ui-egui/tests/javascript.rs::merge_data_files_into_a_spreadsheet"]
|
||
notes = "FDF, XFDF or filled-in PDF forms \u2192 CSV, a column per field and a row per file."
|
||
|
||
[[feature]]
|
||
id = "form.flatten-fields"
|
||
title = "Flatten form fields"
|
||
area = "F"
|
||
tier = "P1"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.flatten"]
|
||
tools = ["doc_flatten"]
|
||
evidence = ["crates/edit/src/tests.rs::flattening_draws_appearances_into_the_page_and_removes_the_comments", "crates/engine/src/tests.rs::flattening_keeps_the_look_and_drops_the_objects", "crates/automation/tests/automation.rs::flattening_through_tools"]
|
||
notes = "widgets drawn into content and pruned from the field tree"
|
||
|
||
[[feature]]
|
||
id = "form.prepare-form-mode"
|
||
title = "Prepare a form mode"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.prepare"]
|
||
tools = ["form_add_field", "form_set_props", "form_delete_field"]
|
||
evidence = ["crates/ui-egui/tests/prepare.rs::placing_moving_editing_and_deleting_a_field", "crates/engine/src/tests.rs::preparing_a_form_adds_renames_and_deletes_fields"]
|
||
notes = "fields shown with blue fill and name tags; select, move, resize by corners, Delete; field tools return to Select after one field"
|
||
|
||
[[feature]]
|
||
id = "form.add-text-field"
|
||
title = "Add text fields"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.add.text"]
|
||
tools = ["form_add_field"]
|
||
evidence = ["crates/forms/src/tests.rs::every_field_type_can_be_added_named_and_drawn", "crates/ui-egui/tests/prepare.rs::placing_moving_editing_and_deleting_a_field", "crates/automation/tests/automation.rs::preparing_a_form_through_tools"]
|
||
notes = "click for the default size or drag; named Text1, Text2…"
|
||
|
||
[[feature]]
|
||
id = "form.add-checkbox"
|
||
title = "Add checkboxes"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.add.checkbox"]
|
||
tools = ["form_add_field"]
|
||
evidence = ["crates/forms/src/tests.rs::every_field_type_can_be_added_named_and_drawn", "crates/ui-egui/tests/prepare.rs::placing_moving_editing_and_deleting_a_field"]
|
||
notes = "Check Box1…, Yes/Off appearance states"
|
||
|
||
[[feature]]
|
||
id = "form.add-radio"
|
||
title = "Add radio buttons"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.add.radio"]
|
||
tools = ["form_add_field"]
|
||
evidence = ["crates/forms/src/tests.rs::every_field_type_can_be_added_named_and_drawn", "crates/automation/tests/automation.rs::preparing_a_form_through_tools"]
|
||
notes = "new group Group1… or join the selected group with the next ChoiceN"
|
||
|
||
[[feature]]
|
||
id = "form.add-dropdown"
|
||
title = "Add drop-down lists"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.add.combo"]
|
||
tools = ["form_add_field"]
|
||
evidence = ["crates/forms/src/tests.rs::every_field_type_can_be_added_named_and_drawn", "crates/automation/tests/automation.rs::preparing_a_form_through_tools"]
|
||
notes = "Dropdown1…; items in Field Properties ▸ Options"
|
||
|
||
[[feature]]
|
||
id = "form.add-listbox"
|
||
title = "Add list boxes"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.add.list"]
|
||
tools = ["form_add_field"]
|
||
evidence = ["crates/forms/src/tests.rs::every_field_type_can_be_added_named_and_drawn"]
|
||
notes = "List Box1…"
|
||
|
||
[[feature]]
|
||
id = "form.add-button"
|
||
title = "Add push buttons"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.add.button"]
|
||
tools = ["form_add_field"]
|
||
evidence = ["crates/forms/src/tests.rs::every_field_type_can_be_added_named_and_drawn"]
|
||
notes = "Button1…; actions arrive with the JavaScript engine"
|
||
|
||
[[feature]]
|
||
id = "form.add-image-field"
|
||
title = "Add image fields"
|
||
area = "F"
|
||
tier = "P1"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.add.image"]
|
||
tools = ["form_add_field", "form_set_image"]
|
||
evidence = ["crates/forms/src/tests.rs::image_fields_ask_for_a_picture_and_show_it", "crates/ui-egui/tests/prepare.rs::image_fields_take_a_picture_when_clicked", "crates/automation/tests/automation.rs::preparing_a_form_through_tools"]
|
||
notes = "icon-only push button with event.target.buttonImportIcon(), recognised natively; clicking asks for a PNG/JPEG/TIFF/GIF/BMP that becomes /MK /I, scaled proportionally and centred"
|
||
|
||
[[feature]]
|
||
id = "form.add-date-field"
|
||
title = "Add date fields"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.add.date"]
|
||
tools = ["form_add_field"]
|
||
evidence = ["crates/forms/src/tests.rs::every_field_type_can_be_added_named_and_drawn"]
|
||
notes = "text field with AFDate_FormatEx(\"mm/dd/yyyy\") format/keystroke actions"
|
||
|
||
[[feature]]
|
||
id = "form.add-signature-field"
|
||
title = "Add digital signature fields"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.add.signature"]
|
||
tools = ["form_add_field"]
|
||
evidence = ["crates/forms/src/tests.rs::every_field_type_can_be_added_named_and_drawn"]
|
||
notes = "unsigned /Sig field; signing is M9"
|
||
|
||
[[feature]]
|
||
id = "form.add-barcode-field"
|
||
title = "Barcode fields (PDF417, QR, Data Matrix)"
|
||
area = "F"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "form.props-general"
|
||
title = "Field properties: general (name, tooltip, visibility, read-only, required)"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.field.properties"]
|
||
tools = ["form_set_props"]
|
||
evidence = ["crates/forms/src/tests.rs::properties_rename_and_delete", "crates/ui-egui/tests/prepare.rs::placing_moving_editing_and_deleting_a_field", "crates/automation/tests/automation.rs::preparing_a_form_through_tools"]
|
||
notes = "name, tooltip, read only, required (visibility/orientation pending)"
|
||
|
||
[[feature]]
|
||
id = "form.props-appearance"
|
||
title = "Field properties: appearance (border, fill, font)"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.field.properties"]
|
||
tools = ["form_set_props"]
|
||
evidence = ["crates/forms/src/tests.rs::appearance_properties_restyle_every_widget", "crates/ui-egui/tests/prepare.rs::appearance_tab_restyles_the_field", "crates/automation/tests/automation.rs::tab_order_and_field_appearance_through_tools"]
|
||
notes = "border/fill colour, thin/medium/thick, solid/dashed/beveled/inset/underline, text colour, Helvetica/Times/Courier, font size"
|
||
|
||
[[feature]]
|
||
id = "form.props-position"
|
||
title = "Field properties: position and size"
|
||
area = "F"
|
||
tier = "P1"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.field.properties"]
|
||
tools = ["form_set_props"]
|
||
evidence = ["crates/forms/src/tests.rs::properties_rename_and_delete", "crates/ui-egui/tests/prepare.rs::placing_moving_editing_and_deleting_a_field"]
|
||
notes = "left/bottom/width/height in points; drag to move, corners to resize"
|
||
|
||
[[feature]]
|
||
id = "form.props-options"
|
||
title = "Field properties: options per field type"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.field.properties"]
|
||
tools = ["form_set_props"]
|
||
evidence = ["crates/forms/src/tests.rs::properties_rename_and_delete", "crates/automation/tests/automation.rs::preparing_a_form_through_tools", "crates/forms/src/tests.rs::options_tab_flags_alignment_and_defaults", "crates/ui-egui/tests/prepare.rs::options_tab_sets_flags_alignment_and_defaults"]
|
||
notes = "text: alignment, default, multi-line, scroll, rich text flag, limit, password, file selection, spell check, comb; choices: items, default, sort, custom text, multiple selection, commit immediately; check boxes and radios: checked by default, radios in unison"
|
||
|
||
[[feature]]
|
||
id = "form.props-actions"
|
||
title = "Field properties: actions per trigger"
|
||
area = "F"
|
||
tier = "P1"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.prepare"]
|
||
tools = ["form_actions", "form_set_actions"]
|
||
evidence = ["crates/forms/src/tests.rs::field_actions_round_trip_on_every_trigger", "crates/automation/tests/automation.rs::field_actions_through_tools", "crates/ui-egui/tests/prepare.rs::actions_tab_adds_and_removes_actions"]
|
||
notes = "Six triggers; Run a JavaScript, Open a web link, Reset a form, Execute a menu item, Go to a page view, Show/hide a field, Submit a form. Other action types are kept."
|
||
|
||
[[feature]]
|
||
id = "form.props-format"
|
||
title = "Field properties: format"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.field.properties"]
|
||
tools = ["form_set_props", "form_fill"]
|
||
evidence = ["crates/ui-egui/tests/prepare.rs::format_validate_and_calculate_tabs", "crates/automation/tests/automation.rs::form_formats_and_calculations_through_tools"]
|
||
notes = "None/Number/Percentage/Date/Time/Special with a live example; writes Acrobat's AF scripts"
|
||
|
||
[[feature]]
|
||
id = "form.props-validate"
|
||
title = "Field properties: validate"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.field.properties"]
|
||
tools = ["form_set_props", "form_fill"]
|
||
evidence = ["crates/ui-egui/tests/prepare.rs::format_validate_and_calculate_tabs", "crates/automation/tests/automation.rs::form_formats_and_calculations_through_tools"]
|
||
notes = "range from/to"
|
||
|
||
[[feature]]
|
||
id = "form.props-calculate"
|
||
title = "Field properties: calculate"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.field.properties"]
|
||
tools = ["form_set_props", "form_fill"]
|
||
evidence = ["crates/ui-egui/tests/prepare.rs::format_validate_and_calculate_tabs", "crates/automation/tests/automation.rs::form_formats_and_calculations_through_tools"]
|
||
notes = "sum/product/average/min/max of picked fields, simplified field notation; custom script pending"
|
||
|
||
[[feature]]
|
||
id = "form.props-selection-change"
|
||
title = "Field properties: selection change"
|
||
area = "F"
|
||
tier = "P2"
|
||
milestone = "M6"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "form.props-signed"
|
||
title = "Field properties: signed (lock fields when signed)"
|
||
area = "F"
|
||
tier = "P1"
|
||
milestone = "M9"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "form.align-distribute-fields"
|
||
title = "Align, centre, distribute and match field sizes"
|
||
area = "F"
|
||
tier = "P1"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.prepare"]
|
||
tools = ["form_set_props"]
|
||
evidence = ["crates/ui-egui/tests/prepare.rs::aligning_distributing_and_sizing_several_fields"]
|
||
notes = "Shift/⌘-click to select several; right-click: Align (left, right, top, bottom, centres), Distribute, Set Fields to Same Size; the right-clicked field is the anchor"
|
||
|
||
[[feature]]
|
||
id = "form.duplicate-fields"
|
||
title = "Duplicate fields across pages"
|
||
area = "F"
|
||
tier = "P1"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.prepare"]
|
||
tools = []
|
||
evidence = ["crates/forms/src/tests.rs::duplicating_a_field_across_pages_shares_its_value", "crates/ui-egui/tests/prepare.rs::duplicating_a_field_onto_every_page"]
|
||
notes = "right-click a field ▸ Duplicate: all pages or a range; one field, shared value"
|
||
|
||
[[feature]]
|
||
id = "form.multiple-copies"
|
||
title = "Create multiple copies of a field in a grid"
|
||
area = "F"
|
||
tier = "P2"
|
||
milestone = "M6"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "form.rename-lock-fields"
|
||
title = "Rename and lock fields"
|
||
area = "F"
|
||
tier = "P1"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.prepare"]
|
||
tools = ["form_set_props"]
|
||
evidence = ["crates/forms/src/tests.rs::locked_fields_only_take_unlocking", "crates/ui-egui/tests/prepare.rs::preview_fills_the_form_and_locked_fields_keep_their_properties"]
|
||
notes = "rename in Field Properties ▸ General; Locked sets the widgets' annotation Locked flag (/F bit 8) and greys out the properties until unlocked; values can still be filled in"
|
||
|
||
[[feature]]
|
||
id = "form.tab-order"
|
||
title = "Tab order (row, column, structure, manual)"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.tab_order.row", "form.tab_order.column", "form.tab_order.structure"]
|
||
tools = ["form_tab_order"]
|
||
evidence = ["crates/forms/src/tests.rs::tab_order_follows_the_page_setting", "crates/automation/tests/automation.rs::tab_order_and_field_appearance_through_tools", "crates/forms/src/tests.rs::ordering_tabs_manually", "crates/ui-egui/tests/prepare.rs::the_fields_panel_orders_tabs_manually"]
|
||
notes = "rows, columns, structure/annotation order (page /Tabs), and manual order from the Fields panel while preparing a form; honoured by Tab"
|
||
|
||
[[feature]]
|
||
id = "form.show-tab-numbers"
|
||
title = "Show tab numbers"
|
||
area = "F"
|
||
tier = "P2"
|
||
milestone = "M6"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "form.preview-form"
|
||
title = "Preview mode for testing a form"
|
||
area = "F"
|
||
tier = "P1"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.prepare"]
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/prepare.rs::preview_fills_the_form_and_locked_fields_keep_their_properties"]
|
||
notes = "Preview / Edit fields toggle in the Prepare a form panel"
|
||
|
||
[[feature]]
|
||
id = "form.auto-detect-fields"
|
||
title = "Automatic form field detection"
|
||
area = "F"
|
||
tier = "P1"
|
||
milestone = "M6"
|
||
status = "partial"
|
||
commands = ["form.detect", "form.prepare"]
|
||
tools = ["form_detect_fields"]
|
||
evidence = ["crates/forms/src/tests.rs::detection_finds_blanks_rules_boxes_and_names_them", "crates/engine/src/tests.rs::detecting_fields_on_a_printed_form", "crates/automation/tests/automation.rs::detecting_form_fields_through_tools", "crates/ui-egui/tests/javascript.rs::prepare_a_form_detects_fields_on_a_paper_form"]
|
||
notes = "Underscore runs, rules, empty boxes and small squares (check boxes); runs when Prepare a form starts on a document without fields. Not yet: comb boxes, tables of cells, scanned forms (needs OCR first), radio groups."
|
||
|
||
[[feature]]
|
||
id = "form.auto-name-fields"
|
||
title = "Name new fields from nearby labels"
|
||
area = "F"
|
||
tier = "P1"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["form.detect"]
|
||
tools = ["form_detect_fields"]
|
||
evidence = ["crates/forms/src/tests.rs::detection_finds_blanks_rules_boxes_and_names_them", "crates/engine/src/tests.rs::detecting_fields_on_a_printed_form", "crates/automation/tests/automation.rs::detecting_form_fields_through_tools"]
|
||
notes = "Names come from the label to the left (or above; right of check boxes), unique like Acrobat's (Name2, Text1)."
|
||
|
||
[[feature]]
|
||
id = "form.document-javascripts"
|
||
title = "Document-level JavaScripts"
|
||
area = "F"
|
||
tier = "P1"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["tools.document_js"]
|
||
tools = ["js_document_scripts", "js_set_document_script"]
|
||
evidence = ["crates/ui-egui/tests/javascript.rs::document_scripts_and_preferences", "crates/automation/tests/automation.rs::javascript_through_tools", "crates/engine/src/tests.rs::form_javascript_validates_calculates_and_formats"]
|
||
notes = "Names /JavaScript tree: list, add, edit, delete; functions are available to field scripts."
|
||
|
||
[[feature]]
|
||
id = "form.document-actions"
|
||
title = "Document actions (will close, will save, did save, will print, did print)"
|
||
area = "F"
|
||
tier = "P1"
|
||
milestone = "M6"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "form.js-host"
|
||
title = "JavaScript engine with the Acrobat object model subset"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "partial"
|
||
commands = ["tools.js_console"]
|
||
tools = ["js_run"]
|
||
evidence = ["crates/js/src/tests.rs::calculate_scripts_read_fields_and_set_the_value", "crates/engine/src/tests.rs::form_javascript_validates_calculates_and_formats", "crates/automation/tests/automation.rs::javascript_through_tools"]
|
||
notes = "boa (pure Rust) with the forms object model: event, getField/Field (value, readonly, display, colours, items, check boxes), app, util.printf/printd/printx, console, display, color; no annotations, layers or dialogs (app.response returns null) yet."
|
||
|
||
[[feature]]
|
||
id = "form.js-console"
|
||
title = "JavaScript console"
|
||
area = "F"
|
||
tier = "P1"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["tools.js_console"]
|
||
tools = ["js_run"]
|
||
evidence = ["crates/ui-egui/tests/javascript.rs::console_runs_scripts_against_the_document", "crates/automation/tests/automation.rs::javascript_through_tools"]
|
||
notes = "\u2318J: run JavaScript against the document, with console output, results and errors."
|
||
|
||
[[feature]]
|
||
id = "form.js-debugger"
|
||
title = "JavaScript debugger"
|
||
area = "F"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "form.edit-all-javascripts"
|
||
title = "Edit all JavaScripts in one editor"
|
||
area = "F"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "form.js-security"
|
||
title = "JavaScript enable switch and sandboxing"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M6"
|
||
status = "shipped"
|
||
commands = ["app.preferences"]
|
||
tools = ["js_enabled"]
|
||
evidence = ["crates/js/src/tests.rs::the_sandbox_has_no_host_access", "crates/js/src/tests.rs::errors_and_runaway_scripts_are_contained", "crates/ui-egui/tests/javascript.rs::document_scripts_and_preferences"]
|
||
notes = "Preferences \u25b8 JavaScript \u25b8 Enable Acrobat JavaScript (remembered); no host access; loop and recursion limits."
|
||
|
||
[[feature]]
|
||
id = "form.folder-level-js"
|
||
title = "Folder-level JavaScript"
|
||
area = "F"
|
||
tier = "P3"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "form.xfa-static"
|
||
title = "View and fill static XFA forms"
|
||
area = "F"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "not read yet; detected on open (DocInfo.xfa, doc_info \"xfa\") and a notice says the fields can be filled but Acrobat may show the XFA values instead (#60)"
|
||
|
||
[[feature]]
|
||
id = "form.xfa-dynamic"
|
||
title = "View and fill dynamic XFA forms"
|
||
area = "F"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "not rendered yet; detected on open and a notice says the page shown is the file's placeholder (#60)"
|
||
|
||
[[feature]]
|
||
id = "form.xfa-formcalc"
|
||
title = "FormCalc scripting"
|
||
area = "F"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "form.xfa-data"
|
||
title = "XFA XML data import and export"
|
||
area = "F"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "form.xfa-flatten"
|
||
title = "Flatten XFA forms"
|
||
area = "F"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "form.fill-sign-text"
|
||
title = "Fill & Sign: add text on flat forms"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["sign.fill.text"]
|
||
tools = ["fill_sign_add"]
|
||
evidence = ["crates/ui-egui/tests/fill_sign.rs::text_marks_and_date", "crates/annot/src/tests.rs::fill_and_sign_items_are_drawn", "crates/automation/tests/automation.rs::fill_and_sign_through_tools"]
|
||
notes = "typewriter FreeText, 10 pt Helvetica"
|
||
|
||
[[feature]]
|
||
id = "form.fill-sign-marks"
|
||
title = "Fill & Sign: check, cross, dot, line marks"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["sign.fill.check", "sign.fill.cross", "sign.fill.dot", "sign.fill.line"]
|
||
tools = ["fill_sign_add"]
|
||
evidence = ["crates/ui-egui/tests/fill_sign.rs::text_marks_and_date", "crates/annot/src/tests.rs::fill_and_sign_items_are_drawn"]
|
||
notes = "PrintCraft-drawn stamps"
|
||
|
||
[[feature]]
|
||
id = "form.fill-sign-date"
|
||
title = "Fill & Sign: today's date"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M5"
|
||
status = "shipped"
|
||
commands = ["sign.fill.date"]
|
||
tools = ["fill_sign_add"]
|
||
evidence = ["crates/ui-egui/tests/fill_sign.rs::text_marks_and_date", "crates/automation/tests/automation.rs::fill_and_sign_through_tools"]
|
||
notes = "local date, m/d/yyyy"
|
||
|
||
[[feature]]
|
||
id = "form.fill-sign-signature"
|
||
title = "Fill & Sign: typed, drawn or image signature and initials"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M5"
|
||
status = "partial"
|
||
commands = ["sign.fill.signature", "sign.fill.initials"]
|
||
tools = ["fill_sign_add"]
|
||
evidence = ["crates/ui-egui/tests/fill_sign.rs::typed_signatures_and_initials", "crates/ui-egui/tests/fill_sign.rs::signing_draws_a_signature_once_and_places_it", "crates/fonts/src/script.rs::a_name_becomes_outlines", "crates/automation/tests/automation.rs::fill_and_sign_through_tools"]
|
||
notes = "signature and initials, typed (Dancing Script outlines as filled paths, no font embedded) or drawn, saved in settings; image signatures not yet"
|
||
|
||
[[feature]]
|
||
id = "form.fill-sign-flatten"
|
||
title = "Fill & Sign: flatten on save"
|
||
area = "F"
|
||
tier = "P0"
|
||
milestone = "M5"
|
||
status = "partial"
|
||
commands = ["comment.flatten"]
|
||
tools = ["doc_flatten"]
|
||
evidence = ["crates/edit/src/tests.rs::flattening_draws_appearances_into_the_page_and_removes_the_comments", "crates/engine/src/tests.rs::flattening_keeps_the_look_and_drops_the_objects", "crates/automation/tests/automation.rs::flattening_through_tools"]
|
||
notes = "Flatten comments flattens Fill & Sign items; no flatten-on-save option yet"
|
||
|
||
[[feature]]
|
||
id = "form.autofill-profile"
|
||
title = "Autofill collection synced to an Adobe account"
|
||
area = "F"
|
||
tier = "N/A"
|
||
milestone = "none"
|
||
status = "na"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "Adobe cloud; a local profile may come later"
|
||
|
||
[[feature]]
|
||
id = "form.distribute-track"
|
||
title = "Distribute and track forms, collect responses"
|
||
area = "F"
|
||
tier = "N/A"
|
||
milestone = "none"
|
||
status = "na"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "Adobe cloud; only submit-to-URL/email actions are supported"
|
||
|
||
[[feature]]
|
||
id = "form.web-forms"
|
||
title = "Acrobat Sign web forms"
|
||
area = "F"
|
||
tier = "N/A"
|
||
milestone = "none"
|
||
status = "na"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "Adobe cloud"
|
||
|
||
# ==================================================================================================
|
||
# G. Protect, redact, sanitize
|
||
# ==================================================================================================
|
||
|
||
[[feature]]
|
||
id = "protect.security-properties"
|
||
title = "Security properties (method, which password opened it, each permission)"
|
||
area = "G"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = ["protect.properties"]
|
||
tools = ["doc_info"]
|
||
evidence = ["crates/ui-egui/tests/editing.rs::password_prompt_opens_and_security_tab_reports_the_details"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "protect.password-to-open"
|
||
title = "Protect with a password to open"
|
||
area = "G"
|
||
tier = "P0"
|
||
milestone = "M8"
|
||
status = "shipped"
|
||
commands = ["protect.password"]
|
||
tools = ["doc_protect"]
|
||
evidence = ["crates/ui-egui/tests/protect.rs::protecting_for_viewing_requires_matching_passwords_and_saves_encrypted", "crates/engine/src/tests.rs::protect_with_an_open_password_then_save_reopen_and_undo", "crates/automation/tests/automation.rs::protecting_through_tools"]
|
||
notes = "Protect Using Password (Viewing); applied by the next save"
|
||
|
||
[[feature]]
|
||
id = "protect.permissions-password"
|
||
title = "Restrict editing and printing with a permissions password"
|
||
area = "G"
|
||
tier = "P0"
|
||
milestone = "M8"
|
||
status = "shipped"
|
||
commands = ["protect.password"]
|
||
tools = ["doc_protect"]
|
||
evidence = ["crates/ui-egui/tests/protect.rs::editing_restrictions_via_advanced_options", "crates/engine/src/tests.rs::permissions_password_restricts_others_but_not_this_session", "crates/automation/tests/automation.rs::protecting_through_tools"]
|
||
notes = "Protect Using Password (Editing); the author keeps full rights after saving"
|
||
|
||
[[feature]]
|
||
id = "protect.permissions-matrix"
|
||
title = "Choose printing, changes, copying and accessibility permissions"
|
||
area = "G"
|
||
tier = "P0"
|
||
milestone = "M8"
|
||
status = "shipped"
|
||
commands = ["protect.password"]
|
||
tools = ["doc_protect"]
|
||
evidence = ["crates/ui-egui/tests/protect.rs::editing_restrictions_via_advanced_options", "crates/engine/src/tests.rs::permissions_password_restricts_others_but_not_this_session"]
|
||
notes = "Advanced Options: printing (none/low/high), changes (5 levels), copying, screen readers"
|
||
|
||
[[feature]]
|
||
id = "protect.encryption-level"
|
||
title = "Choose encryption compatibility (RC4 40/128, AES-128, AES-256)"
|
||
area = "G"
|
||
tier = "P1"
|
||
milestone = "M8"
|
||
status = "shipped"
|
||
commands = ["protect.password"]
|
||
tools = ["doc_protect"]
|
||
evidence = ["crates/engine/src/tests.rs::protection_is_validated_undoable_and_never_logged"]
|
||
notes = "AES-256 (default), AES-128, RC4-128, RC4-40"
|
||
|
||
[[feature]]
|
||
id = "protect.encrypt-except-metadata"
|
||
title = "Encrypt everything except metadata"
|
||
area = "G"
|
||
tier = "P1"
|
||
milestone = "M8"
|
||
status = "shipped"
|
||
commands = ["protect.password"]
|
||
tools = ["doc_protect"]
|
||
evidence = ["crates/engine/src/tests.rs::protection_is_validated_undoable_and_never_logged"]
|
||
notes = "Advanced Options; disabled for RC4 levels as in Acrobat"
|
||
|
||
[[feature]]
|
||
id = "protect.encrypt-attachments-only"
|
||
title = "Encrypt only file attachments"
|
||
area = "G"
|
||
tier = "P2"
|
||
milestone = "M8"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "protect.password-strength"
|
||
title = "Password strength meter"
|
||
area = "G"
|
||
tier = "P2"
|
||
milestone = "M8"
|
||
status = "shipped"
|
||
commands = ["protect.password"]
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/protect.rs::protecting_for_viewing_requires_matching_passwords_and_saves_encrypted"]
|
||
notes = "Weak / Medium / Strong under the password field"
|
||
|
||
[[feature]]
|
||
id = "protect.remove-security"
|
||
title = "Remove security"
|
||
area = "G"
|
||
tier = "P0"
|
||
milestone = "M8"
|
||
status = "shipped"
|
||
commands = ["protect.remove"]
|
||
tools = ["doc_unprotect"]
|
||
evidence = ["crates/engine/src/tests.rs::protect_with_an_open_password_then_save_reopen_and_undo", "crates/automation/tests/automation.rs::protecting_through_tools"]
|
||
notes = "needs the permissions password (or none set)"
|
||
|
||
[[feature]]
|
||
id = "protect.certificate-encryption"
|
||
title = "Encrypt with certificates for recipients"
|
||
area = "G"
|
||
tier = "P1"
|
||
milestone = "M8"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "protect.recipient-permissions"
|
||
title = "Per-recipient permissions for certificate security"
|
||
area = "G"
|
||
tier = "P1"
|
||
milestone = "M8"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "protect.recipient-ldap"
|
||
title = "Find recipients in a directory (LDAP)"
|
||
area = "G"
|
||
tier = "P3"
|
||
milestone = "M8"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "protect.security-policies"
|
||
title = "Local security policies (saved presets)"
|
||
area = "G"
|
||
tier = "P2"
|
||
milestone = "M8"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "protect.rights-management"
|
||
title = "Server-based rights management (AEM/LiveCycle)"
|
||
area = "G"
|
||
tier = "N/A"
|
||
milestone = "none"
|
||
status = "na"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "server product; local policy presets only"
|
||
|
||
[[feature]]
|
||
id = "protect.mip-labels"
|
||
title = "Open Microsoft Purview-protected PDFs and apply labels"
|
||
area = "G"
|
||
tier = "P3"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "optional Windows plug-in"
|
||
|
||
[[feature]]
|
||
id = "protect.redact-text"
|
||
title = "Mark text for redaction"
|
||
area = "G"
|
||
tier = "P0"
|
||
milestone = "M8"
|
||
status = "shipped"
|
||
commands = ["redact.mark"]
|
||
tools = ["redact_mark"]
|
||
evidence = ["crates/ui-egui/tests/redact.rs::marking_applying_and_clearing", "crates/automation/tests/automation.rs::redacting_through_tools", "crates/redact/src/tests.rs::glyphs_under_a_mark_go_and_the_rest_stays_put"]
|
||
notes = "drag across text (or select first) with the Redact tool"
|
||
|
||
[[feature]]
|
||
id = "protect.redact-area"
|
||
title = "Mark areas and images for redaction"
|
||
area = "G"
|
||
tier = "P0"
|
||
milestone = "M8"
|
||
status = "shipped"
|
||
commands = ["redact.mark"]
|
||
tools = ["redact_mark"]
|
||
evidence = ["crates/ui-egui/tests/redact.rs::marking_applying_and_clearing", "crates/redact/src/tests.rs::images_are_removed_or_have_their_pixels_cleared"]
|
||
notes = "drag a box anywhere else"
|
||
|
||
[[feature]]
|
||
id = "protect.redact-pages"
|
||
title = "Mark whole pages for redaction"
|
||
area = "G"
|
||
tier = "P0"
|
||
milestone = "M8"
|
||
status = "shipped"
|
||
commands = ["redact.pages"]
|
||
tools = ["redact_mark"]
|
||
evidence = ["crates/ui-egui/tests/redact.rs::marking_applying_and_clearing"]
|
||
notes = "Mark Page Range: current page or a range"
|
||
|
||
[[feature]]
|
||
id = "protect.search-and-redact"
|
||
title = "Find text and redact"
|
||
area = "G"
|
||
tier = "P0"
|
||
milestone = "M8"
|
||
status = "shipped"
|
||
commands = ["redact.search"]
|
||
tools = ["redact_mark"]
|
||
evidence = ["crates/automation/tests/automation.rs::redacting_through_tools"]
|
||
notes = "single word or phrase, every match on every page"
|
||
|
||
[[feature]]
|
||
id = "protect.redact-word-lists"
|
||
title = "Redact lists of words or phrases"
|
||
area = "G"
|
||
tier = "P1"
|
||
milestone = "M8"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "protect.redact-patterns"
|
||
title = "Redact patterns (phone, email, card, SSN, dates)"
|
||
area = "G"
|
||
tier = "P0"
|
||
milestone = "M8"
|
||
status = "shipped"
|
||
commands = ["redact.search"]
|
||
tools = ["redact_mark"]
|
||
evidence = ["crates/redact/src/patterns.rs::phone_numbers", "crates/redact/src/patterns.rs::credit_cards_need_a_valid_checksum", "crates/automation/tests/automation.rs::redacting_through_tools"]
|
||
notes = "phone, e-mail, credit card (Luhn), SSN, dates (English US forms)"
|
||
|
||
[[feature]]
|
||
id = "protect.redact-pattern-locales"
|
||
title = "Pattern sets per locale"
|
||
area = "G"
|
||
tier = "P1"
|
||
milestone = "M8"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "protect.redact-partial-words"
|
||
title = "Redact part of a match (e.g. all but the last four digits)"
|
||
area = "G"
|
||
tier = "P2"
|
||
milestone = "M8"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "protect.redact-folder"
|
||
title = "Search and redact across a folder"
|
||
area = "G"
|
||
tier = "P2"
|
||
milestone = "M13"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "protect.redaction-properties"
|
||
title = "Redaction fill colour and overlay text"
|
||
area = "G"
|
||
tier = "P0"
|
||
milestone = "M8"
|
||
status = "partial"
|
||
commands = ["redact.properties"]
|
||
tools = ["redact_mark"]
|
||
evidence = ["crates/ui-egui/tests/redact.rs::marking_applying_and_clearing", "crates/redact/src/tests.rs::overlay_text_takes_its_font_size_colour_alignment_and_repeats"]
|
||
notes = "fill colour or none; overlay text with font, size or auto-size, colour, alignment and repeat; redaction codes pending"
|
||
|
||
[[feature]]
|
||
id = "protect.redaction-codes"
|
||
title = "Redaction code sets (e.g. FOIA exemptions)"
|
||
area = "G"
|
||
tier = "P1"
|
||
milestone = "M8"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "protect.apply-redactions"
|
||
title = "Apply redactions (remove underlying content)"
|
||
area = "G"
|
||
tier = "P0"
|
||
milestone = "M8"
|
||
status = "shipped"
|
||
commands = ["redact.apply"]
|
||
tools = ["redact_apply"]
|
||
evidence = ["crates/ui-egui/tests/redact.rs::marking_applying_and_clearing", "crates/engine/src/tests.rs::redaction_marks_apply_for_good_and_undo", "crates/automation/tests/automation.rs::redacting_through_tools"]
|
||
notes = "confirmation; one undo step; next save is a full rewrite"
|
||
|
||
[[feature]]
|
||
id = "protect.redact-glyph-removal"
|
||
title = "Remove redacted text glyphs from content streams"
|
||
area = "G"
|
||
tier = "P0"
|
||
milestone = "M8"
|
||
status = "shipped"
|
||
commands = ["redact.apply"]
|
||
tools = ["redact_apply"]
|
||
evidence = ["crates/redact/src/tests.rs::glyphs_under_a_mark_go_and_the_rest_stays_put", "crates/redact/src/tests.rs::kerning_spacing_scaling_and_line_operators_are_honoured", "crates/redact/src/tests.rs::rotated_text_and_composite_fonts", "crates/redact/src/tests.rs::shared_form_xobjects_are_copied_not_changed"]
|
||
notes = "TJ displacement keeps the rest in place; simple, composite (CMaps), Type 3 fonts"
|
||
|
||
[[feature]]
|
||
id = "protect.redact-image-pixels"
|
||
title = "Remove redacted image pixels"
|
||
area = "G"
|
||
tier = "P0"
|
||
milestone = "M8"
|
||
status = "shipped"
|
||
commands = ["redact.apply"]
|
||
tools = ["redact_apply"]
|
||
evidence = ["crates/redact/src/tests.rs::images_are_removed_or_have_their_pixels_cleared"]
|
||
notes = "covered pixels cleared in a copy; DCT/JPX/JBIG2/CCITT images removed whole"
|
||
|
||
[[feature]]
|
||
id = "protect.redact-vectors"
|
||
title = "Remove or cut redacted vector paths"
|
||
area = "G"
|
||
tier = "P0"
|
||
milestone = "M8"
|
||
status = "shipped"
|
||
commands = ["redact.apply"]
|
||
tools = ["redact_apply"]
|
||
evidence = ["crates/redact/src/tests.rs::vectors_are_removed_or_clipped_and_inline_images_go"]
|
||
notes = "covered paths removed, others clipped; shadings clipped"
|
||
|
||
[[feature]]
|
||
id = "protect.redact-annots-fields"
|
||
title = "Remove annotations and fields under redactions"
|
||
area = "G"
|
||
tier = "P0"
|
||
milestone = "M8"
|
||
status = "shipped"
|
||
commands = ["redact.apply"]
|
||
tools = ["redact_apply"]
|
||
evidence = ["crates/redact/src/tests.rs::comments_links_and_fields_under_a_mark_go", "crates/ui-egui/tests/redact.rs::marking_applying_and_clearing"]
|
||
notes = "with pop-ups; fields lose all widgets"
|
||
|
||
[[feature]]
|
||
id = "protect.redact-struct-tree"
|
||
title = "Remove redacted content from tags and alternate text"
|
||
area = "G"
|
||
tier = "P0"
|
||
milestone = "M8"
|
||
status = "shipped"
|
||
commands = ["redact.apply"]
|
||
tools = ["redact_apply"]
|
||
evidence = ["crates/redact/src/tests.rs::tags_lose_what_redaction_removed"]
|
||
notes = "marked content changed by redaction is found by comparing each MCID before and after; its elements lose Alt, ActualText and E, and references to now-empty marked content are dropped; untouched tags stay"
|
||
|
||
[[feature]]
|
||
id = "protect.redaction-verification"
|
||
title = "Verify no residue remains after redaction"
|
||
area = "G"
|
||
tier = "P0"
|
||
milestone = "M8"
|
||
status = "shipped"
|
||
commands = ["redact.apply"]
|
||
tools = ["redact_apply"]
|
||
evidence = ["crates/redact/src/tests.rs::unreadable_content_fails_closed", "crates/engine/src/tests.rs::redaction_marks_apply_for_good_and_undo"]
|
||
notes = "re-reads redacted pages; residue or unreadable content fails the whole operation"
|
||
|
||
[[feature]]
|
||
id = "protect.redact-filename-suffix"
|
||
title = "Add a suffix to redacted file names"
|
||
area = "G"
|
||
tier = "P3"
|
||
milestone = "M8"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "protect.redact-ai-suggestions"
|
||
title = "Suggest sensitive content to redact via an AI provider"
|
||
area = "G"
|
||
tier = "P2"
|
||
milestone = "M13"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "local or user-provided model; off by default"
|
||
|
||
[[feature]]
|
||
id = "protect.remove-hidden-information"
|
||
title = "Remove hidden information (selectable categories)"
|
||
area = "G"
|
||
tier = "P1"
|
||
milestone = "M8"
|
||
status = "shipped"
|
||
commands = ["protect.remove_hidden"]
|
||
tools = ["doc_hidden_info", "doc_remove_hidden"]
|
||
evidence = ["crates/redact/src/tests.rs::hidden_information_is_counted_and_removed", "crates/ui-egui/tests/redact.rs::removing_hidden_information_and_sanitizing", "crates/automation/tests/automation.rs::removing_hidden_information_through_tools"]
|
||
notes = "metadata, attachments, comments, form fields (flattened), hidden text, hidden layers, bookmarks, links/actions/scripts, private data; overlapping objects and cropped content pending"
|
||
|
||
[[feature]]
|
||
id = "protect.sanitize"
|
||
title = "Sanitize document (remove all hidden information)"
|
||
area = "G"
|
||
tier = "P1"
|
||
milestone = "M8"
|
||
status = "shipped"
|
||
commands = ["redact.sanitize"]
|
||
tools = ["doc_remove_hidden"]
|
||
evidence = ["crates/redact/src/tests.rs::hidden_information_is_counted_and_removed", "crates/ui-egui/tests/redact.rs::removing_hidden_information_and_sanitizing", "crates/automation/tests/automation.rs::removing_hidden_information_through_tools"]
|
||
notes = "every category, then a full rewrite on save"
|
||
|
||
[[feature]]
|
||
id = "protect.remove-hidden-on-close"
|
||
title = "Remove hidden information when closing or sending"
|
||
area = "G"
|
||
tier = "P3"
|
||
milestone = "M8"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "protect.attachment-trust"
|
||
title = "Block opening risky attachment types"
|
||
area = "G"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "protect.url-access-control"
|
||
title = "Control internet access from PDFs"
|
||
area = "G"
|
||
tier = "P1"
|
||
milestone = "M6"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "protect.protected-view"
|
||
title = "Open untrusted files in a restricted view"
|
||
area = "G"
|
||
tier = "P2"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "protect.security-envelope"
|
||
title = "Security envelopes"
|
||
area = "G"
|
||
tier = "P3"
|
||
milestone = "M8"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "legacy"
|
||
|
||
# ==================================================================================================
|
||
# H. Digital signatures
|
||
# ==================================================================================================
|
||
|
||
[[feature]]
|
||
id = "sign.pkcs12"
|
||
title = "Sign with a PKCS#12 (.p12/.pfx) digital ID"
|
||
area = "H"
|
||
tier = "P0"
|
||
milestone = "M9"
|
||
status = "shipped"
|
||
commands = ["sign.digital"]
|
||
tools = ["sign_document"]
|
||
evidence = ["crates/sign/tests/crypto.rs::opens_every_openssl_flavour_of_pkcs12", "crates/sign/tests/pdf.rs::signing_then_validating_with_and_without_trust", "crates/ui-egui/tests/signing.rs::certifying_without_a_visible_signature", "crates/automation/tests/automation.rs::digital_ids_signing_and_validation_through_tools"]
|
||
notes = "PBES2 (AES, 3DES) and legacy SHA-1 3DES/RC2 files; RSA, P-256, P-384"
|
||
|
||
[[feature]]
|
||
id = "sign.macos-keychain"
|
||
title = "Sign with macOS Keychain identities"
|
||
area = "H"
|
||
tier = "P0"
|
||
milestone = "M9"
|
||
status = "shipped"
|
||
commands = ["sign.digital"]
|
||
tools = ["sign_keychain_ids", "sign_document"]
|
||
evidence = ["crates/sign/tests/pdf.rs::signing_with_keychain_identities", "crates/automation/tests/automation.rs::digital_ids_signing_and_validation_through_tools"]
|
||
notes = "Security framework identities (RSA PKCS #1 v1.5, ECDSA P-256/P-384) listed with the digital IDs in Sign with a Digital ID; the key never leaves the Keychain (ExternalKey); keychain:<fingerprint|name> for agents. The test uses a throwaway keychain file (run with --ignored)"
|
||
|
||
[[feature]]
|
||
id = "sign.windows-cert-store"
|
||
title = "Sign with the Windows certificate store"
|
||
area = "H"
|
||
tier = "P0"
|
||
milestone = "M9"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "sign.pkcs11"
|
||
title = "Sign with PKCS#11 tokens and smart cards"
|
||
area = "H"
|
||
tier = "P1"
|
||
milestone = "M9"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "sign.self-signed-id"
|
||
title = "Create a self-signed digital ID"
|
||
area = "H"
|
||
tier = "P1"
|
||
milestone = "M9"
|
||
status = "shipped"
|
||
commands = ["sign.digital"]
|
||
tools = ["sign_id_create"]
|
||
evidence = ["crates/sign/tests/crypto.rs::new_digital_ids_are_self_signed_and_survive_a_p12_round_trip", "crates/ui-egui/tests/signing.rs::drawing_a_signature_creating_an_id_signing_and_trusting", "crates/automation/tests/automation.rs::digital_ids_signing_and_validation_through_tools"]
|
||
notes = "RSA 2048/3072/4096 or P-256, saved as a password-protected .p12"
|
||
|
||
[[feature]]
|
||
id = "sign.export-certificate"
|
||
title = "Export a public certificate"
|
||
area = "H"
|
||
tier = "P2"
|
||
milestone = "M9"
|
||
status = "shipped"
|
||
commands = ["sign.panel"]
|
||
tools = []
|
||
evidence = ["crates/sign/tests/crypto.rs::certificates_load_from_pem_and_der_and_export_as_pem"]
|
||
notes = "Signatures panel ▸ Export certificate (.cer, PEM)"
|
||
|
||
[[feature]]
|
||
id = "sign.visible-signature"
|
||
title = "Visible signature by drawing a rectangle"
|
||
area = "H"
|
||
tier = "P0"
|
||
milestone = "M9"
|
||
status = "shipped"
|
||
commands = ["sign.digital"]
|
||
tools = ["sign_document"]
|
||
evidence = ["crates/ui-egui/tests/signing.rs::drawing_a_signature_creating_an_id_signing_and_trusting", "crates/automation/tests/automation.rs::digital_ids_signing_and_validation_through_tools"]
|
||
notes = "drag a rectangle; Acrobat's name-and-details appearance"
|
||
|
||
[[feature]]
|
||
id = "sign.sign-existing-field"
|
||
title = "Sign an existing signature field"
|
||
area = "H"
|
||
tier = "P0"
|
||
milestone = "M9"
|
||
status = "shipped"
|
||
commands = ["sign.digital"]
|
||
tools = ["sign_document"]
|
||
evidence = ["crates/sign/tests/pdf.rs::signing_an_existing_field_and_counter_signing", "crates/ui-egui/tests/signing.rs::clicking_an_empty_signature_field_signs_it"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "sign.invisible-signature"
|
||
title = "Invisible signature"
|
||
area = "H"
|
||
tier = "P1"
|
||
milestone = "M9"
|
||
status = "shipped"
|
||
commands = ["sign.certify_invisible"]
|
||
tools = ["sign_document"]
|
||
evidence = ["crates/sign/tests/pdf.rs::invisible_signatures_and_refusals", "crates/ui-egui/tests/signing.rs::certifying_without_a_visible_signature"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "sign.signature-appearance"
|
||
title = "Configure signature appearance (text, graphic, name)"
|
||
area = "H"
|
||
tier = "P1"
|
||
milestone = "M9"
|
||
status = "partial"
|
||
commands = ["sign.digital"]
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/signing.rs::drawing_a_signature_creating_an_id_signing_and_trusting"]
|
||
notes = "text items (name, date, reason, location, DN, labels); no graphics or saved appearances yet"
|
||
|
||
[[feature]]
|
||
id = "sign.reason-location"
|
||
title = "Reason, location and contact info"
|
||
area = "H"
|
||
tier = "P1"
|
||
milestone = "M9"
|
||
status = "shipped"
|
||
commands = ["sign.digital"]
|
||
tools = ["sign_document"]
|
||
evidence = ["crates/sign/tests/pdf.rs::signing_then_validating_with_and_without_trust", "crates/automation/tests/automation.rs::digital_ids_signing_and_validation_through_tools"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "sign.lock-after-signing"
|
||
title = "Lock document or fields after signing"
|
||
area = "H"
|
||
tier = "P1"
|
||
milestone = "M9"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "sign.pkcs7-detached"
|
||
title = "PKCS#7 detached signatures"
|
||
area = "H"
|
||
tier = "P0"
|
||
milestone = "M9"
|
||
status = "shipped"
|
||
commands = ["sign.validate"]
|
||
tools = ["sign_list"]
|
||
evidence = ["crates/sign/tests/pdf.rs::validates_a_signature_made_by_openssl"]
|
||
notes = "validated; new signatures use CAdES"
|
||
|
||
[[feature]]
|
||
id = "sign.cades-pades-bb"
|
||
title = "CAdES / PAdES B-B signatures"
|
||
area = "H"
|
||
tier = "P0"
|
||
milestone = "M9"
|
||
status = "shipped"
|
||
commands = ["sign.digital"]
|
||
tools = ["sign_document"]
|
||
evidence = ["crates/sign/tests/crypto.rs::signatures_round_trip_for_every_key_type", "crates/sign/tests/pdf.rs::signing_then_validating_with_and_without_trust"]
|
||
notes = "ETSI.CAdES.detached with signing-certificate-v2; pdfsig reports them valid"
|
||
|
||
[[feature]]
|
||
id = "sign.hash-algorithms"
|
||
title = "SHA-256/384/512 hashes; no SHA-1 for new signatures"
|
||
area = "H"
|
||
tier = "P0"
|
||
milestone = "M9"
|
||
status = "shipped"
|
||
commands = ["sign.digital"]
|
||
tools = ["sign_document"]
|
||
evidence = ["crates/sign/tests/crypto.rs::signatures_round_trip_for_every_key_type"]
|
||
notes = "SHA-256, SHA-384 for P-384; SHA-1 refused for new signatures, validated in old ones"
|
||
|
||
[[feature]]
|
||
id = "sign.certify"
|
||
title = "Certify a document (DocMDP)"
|
||
area = "H"
|
||
tier = "P1"
|
||
milestone = "M9"
|
||
status = "shipped"
|
||
commands = ["sign.certify", "sign.certify_invisible"]
|
||
tools = ["sign_document"]
|
||
evidence = ["crates/sign/tests/pdf.rs::later_changes_are_classified_under_the_signature_permissions", "crates/ui-egui/tests/signing.rs::certifying_without_a_visible_signature"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "sign.certify-permissions"
|
||
title = "Allowed changes after certifying"
|
||
area = "H"
|
||
tier = "P1"
|
||
milestone = "M9"
|
||
status = "shipped"
|
||
commands = ["sign.certify"]
|
||
tools = ["sign_document"]
|
||
evidence = ["crates/sign/tests/pdf.rs::later_changes_are_classified_under_the_signature_permissions"]
|
||
notes = "DocMDP P=1/2/3 enforced when validating"
|
||
|
||
[[feature]]
|
||
id = "sign.field-mdp"
|
||
title = "Field locks (FieldMDP)"
|
||
area = "H"
|
||
tier = "P1"
|
||
milestone = "M9"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "sign.document-timestamp"
|
||
title = "Add a document timestamp (RFC 3161)"
|
||
area = "H"
|
||
tier = "P1"
|
||
milestone = "M9"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "sign.timestamp-servers"
|
||
title = "Configure timestamp servers"
|
||
area = "H"
|
||
tier = "P1"
|
||
milestone = "M9"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "sign.pades-bt"
|
||
title = "PAdES B-T (signature timestamp)"
|
||
area = "H"
|
||
tier = "P1"
|
||
milestone = "M9"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "sign.ltv"
|
||
title = "Long-term validation (DSS/VRI)"
|
||
area = "H"
|
||
tier = "P1"
|
||
milestone = "M9"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "sign.pades-blta"
|
||
title = "PAdES B-LT and B-LTA"
|
||
area = "H"
|
||
tier = "P1"
|
||
milestone = "M9"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "sign.ocsp-crl"
|
||
title = "Revocation checking (OCSP and CRL)"
|
||
area = "H"
|
||
tier = "P1"
|
||
milestone = "M9"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "sign.validate-all"
|
||
title = "Validate all signatures"
|
||
area = "H"
|
||
tier = "P0"
|
||
milestone = "M9"
|
||
status = "shipped"
|
||
commands = ["sign.validate"]
|
||
tools = ["sign_list"]
|
||
evidence = ["crates/sign/tests/pdf.rs::signing_then_validating_with_and_without_trust", "crates/automation/tests/automation.rs::digital_ids_signing_and_validation_through_tools"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "sign.signatures-panel"
|
||
title = "Signatures panel with per-revision tree"
|
||
area = "H"
|
||
tier = "P0"
|
||
milestone = "M9"
|
||
status = "partial"
|
||
commands = ["sign.panel"]
|
||
tools = ["sign_list"]
|
||
evidence = ["crates/ui-egui/tests/signing.rs::drawing_a_signature_creating_an_id_signing_and_trusting"]
|
||
notes = "per-signature tree with details and actions; no per-revision page outline yet"
|
||
|
||
[[feature]]
|
||
id = "sign.status-icons"
|
||
title = "Signature status (valid, unknown, invalid)"
|
||
area = "H"
|
||
tier = "P0"
|
||
milestone = "M9"
|
||
status = "shipped"
|
||
commands = ["sign.panel"]
|
||
tools = ["sign_list"]
|
||
evidence = ["crates/ui-egui/tests/signing.rs::drawing_a_signature_creating_an_id_signing_and_trusting"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "sign.validation-banner"
|
||
title = "Signed/certified document message bar"
|
||
area = "H"
|
||
tier = "P0"
|
||
milestone = "M9"
|
||
status = "shipped"
|
||
commands = ["sign.panel"]
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/signing.rs::drawing_a_signature_creating_an_id_signing_and_trusting", "crates/ui-egui/tests/signing.rs::certifying_without_a_visible_signature"]
|
||
notes = "signed/valid, problems, invalid and certified bars"
|
||
|
||
[[feature]]
|
||
id = "sign.signature-properties"
|
||
title = "Signature properties (validity summary, signer, time)"
|
||
area = "H"
|
||
tier = "P0"
|
||
milestone = "M9"
|
||
status = "partial"
|
||
commands = ["sign.panel"]
|
||
tools = ["sign_list"]
|
||
evidence = ["crates/ui-egui/tests/signing.rs::drawing_a_signature_creating_an_id_signing_and_trusting"]
|
||
notes = "validity summary, signer, issuer, time, algorithm in the panel; no separate properties dialog"
|
||
|
||
[[feature]]
|
||
id = "sign.certificate-viewer"
|
||
title = "Certificate viewer"
|
||
area = "H"
|
||
tier = "P1"
|
||
milestone = "M9"
|
||
status = "shipped"
|
||
commands = ["sign.panel"]
|
||
tools = ["sign_list"]
|
||
evidence = ["crates/ui-egui/tests/signing.rs::drawing_a_signature_creating_an_id_signing_and_trusting"]
|
||
notes = "Signatures panel ▸ Show certificate: the chain (root on top) and Summary, Details (serial, key, constraints, usage, SHA-1/SHA-256 digests) and Trust tabs; add to trusted certificates; export"
|
||
|
||
[[feature]]
|
||
id = "sign.trust-store"
|
||
title = "Trusted certificates store"
|
||
area = "H"
|
||
tier = "P0"
|
||
milestone = "M9"
|
||
status = "shipped"
|
||
commands = ["sign.panel"]
|
||
tools = ["sign_trust"]
|
||
evidence = ["crates/sign/tests/pdf.rs::signing_then_validating_with_and_without_trust", "crates/ui-egui/tests/signing.rs::drawing_a_signature_creating_an_id_signing_and_trusting", "crates/automation/tests/automation.rs::digital_ids_signing_and_validation_through_tools"]
|
||
notes = "trusted certificates persist with the app's settings"
|
||
|
||
[[feature]]
|
||
id = "sign.trust-import"
|
||
title = "Import trusted certificates and set trust"
|
||
area = "H"
|
||
tier = "P1"
|
||
milestone = "M9"
|
||
status = "partial"
|
||
commands = []
|
||
tools = ["sign_trust"]
|
||
evidence = ["crates/automation/tests/automation.rs::digital_ids_signing_and_validation_through_tools"]
|
||
notes = "import .cer/.pem/.der/.p12 through the tool; per-certificate trust settings not yet"
|
||
|
||
[[feature]]
|
||
id = "sign.eutl"
|
||
title = "EU Trusted Lists"
|
||
area = "H"
|
||
tier = "P2"
|
||
milestone = "M9"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "sign.os-trust"
|
||
title = "Trust the operating system's root certificates"
|
||
area = "H"
|
||
tier = "P1"
|
||
milestone = "M9"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "sign.verification-time"
|
||
title = "Verification time (signing time, timestamp, now)"
|
||
area = "H"
|
||
tier = "P1"
|
||
milestone = "M9"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "sign.changes-after-signing"
|
||
title = "List changes made after signing (MDP-aware diff)"
|
||
area = "H"
|
||
tier = "P0"
|
||
milestone = "M9"
|
||
status = "shipped"
|
||
commands = ["sign.panel"]
|
||
tools = ["sign_list"]
|
||
evidence = ["crates/sign/tests/pdf.rs::later_changes_are_classified_under_the_signature_permissions", "crates/sign/tests/pdf.rs::signing_an_existing_field_and_counter_signing", "crates/engine/src/tests.rs::signing_saving_trusting_and_commenting_afterwards"]
|
||
notes = "revision diff classified as signing, form fill, comments, metadata, page content or structure"
|
||
|
||
[[feature]]
|
||
id = "sign.view-signed-version"
|
||
title = "View the signed version"
|
||
area = "H"
|
||
tier = "P1"
|
||
milestone = "M9"
|
||
status = "shipped"
|
||
commands = ["sign.panel"]
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/signing.rs::drawing_a_signature_creating_an_id_signing_and_trusting"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "sign.compare-signed-version"
|
||
title = "Compare signed version with the current one"
|
||
area = "H"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "sign.verify-on-open"
|
||
title = "Verify signatures when a document opens"
|
||
area = "H"
|
||
tier = "P1"
|
||
milestone = "M9"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = ["sign_list"]
|
||
evidence = ["crates/engine/src/tests.rs::signing_saving_trusting_and_commenting_afterwards"]
|
||
notes = "validated when opened and after every edit"
|
||
|
||
[[feature]]
|
||
id = "sign.clear-signature"
|
||
title = "Clear an unsaved signature; clear all signature fields"
|
||
area = "H"
|
||
tier = "P2"
|
||
milestone = "M9"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "sign.seed-values"
|
||
title = "Signature seed values"
|
||
area = "H"
|
||
tier = "P2"
|
||
milestone = "M9"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "sign.preview-mode"
|
||
title = "Preview document mode before signing"
|
||
area = "H"
|
||
tier = "P2"
|
||
milestone = "M9"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "sign.qualified-labels"
|
||
title = "Show qualified signature/seal labels"
|
||
area = "H"
|
||
tier = "P2"
|
||
milestone = "M9"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "sign.web-signing"
|
||
title = "Sign in the browser with a PKCS#12 file"
|
||
area = "H"
|
||
tier = "P1"
|
||
milestone = "M9"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "sign.aatl"
|
||
title = "Adobe Approved Trust List"
|
||
area = "H"
|
||
tier = "N/A"
|
||
milestone = "none"
|
||
status = "na"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "Adobe-hosted list; use OS trust stores and EU Trusted Lists"
|
||
|
||
[[feature]]
|
||
id = "sign.request-esignatures"
|
||
title = "Request e-signatures (Acrobat Sign)"
|
||
area = "H"
|
||
tier = "N/A"
|
||
milestone = "none"
|
||
status = "na"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "Adobe cloud; pluggable provider hook only"
|
||
|
||
[[feature]]
|
||
id = "sign.cloud-signatures"
|
||
title = "Cloud signature (CSC) digital IDs via Adobe"
|
||
area = "H"
|
||
tier = "N/A"
|
||
milestone = "none"
|
||
status = "na"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "Adobe cloud; a pluggable remote-signing KeyProvider may follow"
|
||
|
||
# ==================================================================================================
|
||
# I. Scan and OCR
|
||
# ==================================================================================================
|
||
|
||
[[feature]]
|
||
id = "ocr.recognize-text"
|
||
title = "Recognize text (OCR) in this file"
|
||
area = "I"
|
||
tier = "P0"
|
||
milestone = "M10"
|
||
status = "shipped"
|
||
commands = ["ocr.recognize"]
|
||
tools = ["ocr_recognize", "ocr_status"]
|
||
evidence = ["crates/engine/src/tests.rs::recognize_text_makes_a_scanned_page_searchable", "crates/automation/tests/automation.rs::ocr_tools_make_a_scan_searchable", "crates/ui-egui/tests/ocr.rs::recognize_text_dialog_adds_searchable_text"]
|
||
notes = "ocrs engine (MIT/Apache) with CC-BY-SA models fetched by `cargo xtask models`; runs on a worker thread with progress; one undoable step; pages with text are skipped."
|
||
|
||
[[feature]]
|
||
id = "ocr.recognize-range"
|
||
title = "Recognize text in the current page or a range"
|
||
area = "I"
|
||
tier = "P0"
|
||
milestone = "M10"
|
||
status = "shipped"
|
||
commands = ["ocr.recognize"]
|
||
tools = ["ocr_recognize"]
|
||
evidence = ["crates/automation/tests/automation.rs::ocr_tools_make_a_scan_searchable", "crates/ui-egui/tests/ocr.rs::recognize_text_dialog_adds_searchable_text"]
|
||
notes = "All pages, current page or a range."
|
||
|
||
[[feature]]
|
||
id = "ocr.recognize-multiple"
|
||
title = "Recognize text in multiple files"
|
||
area = "I"
|
||
tier = "P1"
|
||
milestone = "M10"
|
||
status = "shipped"
|
||
commands = ["ocr.recognize_batch"]
|
||
tools = ["ocr_recognize_files"]
|
||
evidence = ["crates/automation/tests/automation.rs::ocr_recognize_files_writes_searchable_copies", "crates/ui-egui/tests/ocr.rs::recognize_text_in_multiple_files_writes_searchable_copies"]
|
||
notes = "Pick PDFs, then an output folder; copies are saved incrementally under the same names, in the background with progress."
|
||
|
||
[[feature]]
|
||
id = "ocr.ocr-languages"
|
||
title = "OCR document language selection"
|
||
area = "I"
|
||
tier = "P0"
|
||
milestone = "M10"
|
||
status = "partial"
|
||
commands = ["ocr.recognize"]
|
||
tools = ["ocr_recognize"]
|
||
evidence = ["crates/ui-egui/tests/ocr.rs::recognize_text_dialog_adds_searchable_text"]
|
||
notes = "Language choice exists, but the models read only the Latin alphabet without accents (English)."
|
||
|
||
[[feature]]
|
||
id = "ocr.searchable-image"
|
||
title = "Searchable image output (invisible text layer)"
|
||
area = "I"
|
||
tier = "P0"
|
||
milestone = "M10"
|
||
status = "partial"
|
||
commands = ["ocr.recognize"]
|
||
tools = ["ocr_recognize"]
|
||
evidence = ["crates/engine/src/tests.rs::recognize_text_makes_a_scanned_page_searchable"]
|
||
notes = "Invisible text (Tr 3) fitted to each word box; the image is not deskewed or downsampled (exact mode only)."
|
||
|
||
[[feature]]
|
||
id = "ocr.searchable-image-exact"
|
||
title = "Searchable image (exact): leave the image untouched"
|
||
area = "I"
|
||
tier = "P1"
|
||
milestone = "M10"
|
||
status = "shipped"
|
||
commands = ["ocr.recognize"]
|
||
tools = ["ocr_recognize"]
|
||
evidence = ["crates/engine/src/tests.rs::recognize_text_makes_a_scanned_page_searchable"]
|
||
notes = "The image is left untouched."
|
||
|
||
[[feature]]
|
||
id = "ocr.editable-text-output"
|
||
title = "Editable text and images output"
|
||
area = "I"
|
||
tier = "P1"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "ocr.ocr-downsample"
|
||
title = "Downsample images during OCR"
|
||
area = "I"
|
||
tier = "P2"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "ocr.skip-renderable-text"
|
||
title = "Skip pages that already have text"
|
||
area = "I"
|
||
tier = "P1"
|
||
milestone = "M10"
|
||
status = "shipped"
|
||
commands = ["ocr.recognize"]
|
||
tools = ["ocr_recognize"]
|
||
evidence = ["crates/engine/src/tests.rs::recognize_text_makes_a_scanned_page_searchable", "crates/automation/tests/automation.rs::ocr_tools_make_a_scan_searchable"]
|
||
notes = "Pages that already have text are skipped and reported (skip_text_pages false reads them anyway)."
|
||
|
||
[[feature]]
|
||
id = "ocr.rotation-detection"
|
||
title = "Detect and fix page rotation"
|
||
area = "I"
|
||
tier = "P1"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "ocr.deskew"
|
||
title = "Deskew"
|
||
area = "I"
|
||
tier = "P1"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "ocr.despeckle"
|
||
title = "Despeckle"
|
||
area = "I"
|
||
tier = "P1"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "ocr.background-removal"
|
||
title = "Background removal"
|
||
area = "I"
|
||
tier = "P1"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "ocr.descreen"
|
||
title = "Descreen"
|
||
area = "I"
|
||
tier = "P2"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "ocr.remove-halo"
|
||
title = "Remove halos"
|
||
area = "I"
|
||
tier = "P3"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "ocr.edge-shadow-removal"
|
||
title = "Edge shadow removal"
|
||
area = "I"
|
||
tier = "P2"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "ocr.camera-perspective"
|
||
title = "Enhance camera images (edges, perspective, contrast)"
|
||
area = "I"
|
||
tier = "P1"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "ocr.camera-document-type"
|
||
title = "Camera image document types (whiteboard, card, form)"
|
||
area = "I"
|
||
tier = "P2"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "ocr.correct-recognized-text"
|
||
title = "Correct recognized text (low-confidence suspects)"
|
||
area = "I"
|
||
tier = "P2"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "ocr.adaptive-compression"
|
||
title = "Adaptive (MRC) compression of scans"
|
||
area = "I"
|
||
tier = "P2"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "ocr.scan-pdfa"
|
||
title = "Make scans PDF/A compliant"
|
||
area = "I"
|
||
tier = "P2"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "ocr.ocr-engines"
|
||
title = "Pluggable OCR engines (ocrs default; tesseract optional)"
|
||
area = "I"
|
||
tier = "P1"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "ocr.ocr-web"
|
||
title = "OCR in the browser"
|
||
area = "I"
|
||
tier = "P1"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "ocr.scanner-acquire"
|
||
title = "Scan from TWAIN/WIA/ICA/SANE scanners"
|
||
area = "I"
|
||
tier = "P2"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "ocr.scan-presets"
|
||
title = "Scan presets"
|
||
area = "I"
|
||
tier = "P2"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "ocr.scan-append"
|
||
title = "Append scans to an existing PDF or split into files"
|
||
area = "I"
|
||
tier = "P2"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "ocr.adobe-scan-sync"
|
||
title = "Adobe Scan mobile capture sync"
|
||
area = "I"
|
||
tier = "N/A"
|
||
milestone = "none"
|
||
status = "na"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "Adobe cloud; import images from any source instead"
|
||
|
||
# ==================================================================================================
|
||
# J. Create and export
|
||
# ==================================================================================================
|
||
|
||
[[feature]]
|
||
id = "create.from-images"
|
||
title = "Create PDF from images (JPEG, PNG, TIFF, GIF, BMP, JPEG 2000)"
|
||
area = "J"
|
||
tier = "P0"
|
||
milestone = "M10"
|
||
status = "shipped"
|
||
commands = ["create.images"]
|
||
tools = ["doc_create"]
|
||
evidence = ["crates/create/src/tests.rs::jpeg_2000_images_are_embedded_as_is", "crates/create/src/tests.rs::images_export_as_jpeg_unchanged_and_others_as_png"]
|
||
notes = "PNG, JPEG, TIFF (multi-page), GIF (first frame), BMP, and JPEG 2000 (JP2 or codestream, embedded as JPXDecode with its capture resolution)"
|
||
|
||
[[feature]]
|
||
id = "create.from-multipage-tiff"
|
||
title = "Multi-page TIFF to multi-page PDF"
|
||
area = "J"
|
||
tier = "P0"
|
||
milestone = "M10"
|
||
status = "shipped"
|
||
commands = ["create.images", "create.file"]
|
||
tools = ["doc_create"]
|
||
evidence = ["crates/create/src/tests.rs::bmp_gif_and_multi_page_tiff_images"]
|
||
notes = "every page; 1-bit scans stay 1-bit; resolution from the TIFF"
|
||
|
||
[[feature]]
|
||
id = "create.from-text"
|
||
title = "Create PDF from plain text"
|
||
area = "J"
|
||
tier = "P0"
|
||
milestone = "M10"
|
||
status = "shipped"
|
||
commands = ["create.file"]
|
||
tools = ["doc_create"]
|
||
evidence = ["crates/create/src/tests.rs::text_is_wrapped_and_paginated", "crates/automation/tests/automation.rs::creating_and_reducing_through_tools"]
|
||
notes = "Helvetica 11 pt on US Letter; form feeds start pages"
|
||
|
||
[[feature]]
|
||
id = "create.from-markdown"
|
||
title = "Create PDF from Markdown"
|
||
area = "J"
|
||
tier = "P2"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "create.from-clipboard"
|
||
title = "Create PDF from the clipboard"
|
||
area = "J"
|
||
tier = "P0"
|
||
milestone = "M10"
|
||
status = "shipped"
|
||
commands = ["create.clipboard"]
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/create.rs::clipboard_images_and_text_become_new_pdfs"]
|
||
notes = "an image (one page at its size) or text; headless tools read files instead"
|
||
|
||
[[feature]]
|
||
id = "create.from-screenshot"
|
||
title = "Create PDF from a screen capture"
|
||
area = "J"
|
||
tier = "P1"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "create.blank"
|
||
title = "Create a blank PDF"
|
||
area = "J"
|
||
tier = "P0"
|
||
milestone = "M10"
|
||
status = "shipped"
|
||
commands = ["create.blank"]
|
||
tools = ["doc_create"]
|
||
evidence = ["crates/ui-egui/tests/create.rs::opening_images_and_text_converts_them_to_new_pdfs", "crates/create/src/tests.rs::blank_documents", "crates/automation/tests/automation.rs::creating_and_reducing_through_tools"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "create.from-html"
|
||
title = "Create PDF from an HTML file"
|
||
area = "J"
|
||
tier = "P2"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "create.from-web-page"
|
||
title = "Create PDF from a web page URL"
|
||
area = "J"
|
||
tier = "P2"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "create.from-office"
|
||
title = "Create PDF from Office documents (LibreOffice sidecar)"
|
||
area = "J"
|
||
tier = "P2"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "create.create-multiple"
|
||
title = "Convert many files to PDF in one run"
|
||
area = "J"
|
||
tier = "P1"
|
||
milestone = "M13"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "create.image-conversion-options"
|
||
title = "Image compression options when converting"
|
||
area = "J"
|
||
tier = "P2"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "create.pdf-printer"
|
||
title = "Print to PDF from other applications"
|
||
area = "J"
|
||
tier = "P3"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "create.distiller"
|
||
title = "PostScript/EPS to PDF (Distiller)"
|
||
area = "J"
|
||
tier = "P3"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "deferred: a PostScript interpreter is a large project"
|
||
|
||
[[feature]]
|
||
id = "create.job-options"
|
||
title = "PDF settings presets (job options)"
|
||
area = "J"
|
||
tier = "P3"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "create.export-docx"
|
||
title = "Export to Word (.docx)"
|
||
area = "J"
|
||
tier = "P1"
|
||
milestone = "M10"
|
||
status = "partial"
|
||
commands = ["export.docx"]
|
||
tools = ["doc_export_office"]
|
||
evidence = ["crates/export/src/lib.rs::docx_is_a_word_package", "crates/export/src/lib.rs::grid_text_becomes_a_real_table_in_every_format", "crates/engine/src/tests.rs::exporting_office_files_keeps_images", "crates/automation/tests/automation.rs::exporting_to_word_html_and_rtf", "crates/ui-egui/tests/compare.rs::export_to_word_html_and_rtf"]
|
||
notes = "Flowing Word document: paragraphs, Heading 1/2 from larger text, bold/italic, images inline, page breaks, and detected tables with spans/empty cells; checked with macOS textutil. Not yet: columns, fonts and colours, exact layout mode."
|
||
|
||
[[feature]]
|
||
id = "create.export-rtf"
|
||
title = "Export to RTF"
|
||
area = "J"
|
||
tier = "P1"
|
||
milestone = "M10"
|
||
status = "shipped"
|
||
commands = ["export.rtf"]
|
||
tools = ["doc_export_office"]
|
||
evidence = ["crates/export/src/lib.rs::rtf_escapes_and_sizes", "crates/automation/tests/automation.rs::exporting_to_word_html_and_rtf", "crates/ui-egui/tests/compare.rs::export_to_word_html_and_rtf"]
|
||
notes = "Paragraphs with sizes, bold, italic, Unicode; page breaks. Images are left out."
|
||
|
||
[[feature]]
|
||
id = "create.export-xlsx"
|
||
title = "Export to Excel (.xlsx) with table detection"
|
||
area = "J"
|
||
tier = "P1"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "create.export-pptx"
|
||
title = "Export to PowerPoint (.pptx)"
|
||
area = "J"
|
||
tier = "P1"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "create.export-png"
|
||
title = "Export pages as PNG"
|
||
area = "J"
|
||
tier = "P0"
|
||
milestone = "M10"
|
||
status = "shipped"
|
||
commands = ["export.image"]
|
||
tools = ["doc_export_images", "page_render"]
|
||
evidence = ["crates/ui-egui/tests/export.rs::export_dialogs_write_images_and_text", "crates/engine/src/tests.rs::export_images_and_text_follow_the_working_file", "crates/automation/tests/automation.rs::exporting_images_and_text_through_tools"]
|
||
notes = "PNG at 72–600 dpi, page ranges; includes unsaved edits; background thread with progress"
|
||
|
||
[[feature]]
|
||
id = "create.export-jpeg-tiff"
|
||
title = "Export pages as JPEG or TIFF"
|
||
area = "J"
|
||
tier = "P0"
|
||
milestone = "M10"
|
||
status = "shipped"
|
||
commands = ["export.image"]
|
||
tools = ["doc_export_images"]
|
||
evidence = ["crates/engine/src/tests.rs::pages_export_as_png_jpeg_and_tiff", "crates/automation/tests/automation.rs::exporting_images_and_text_through_tools"]
|
||
notes = "JPEG with quality, TIFF; flattened onto white"
|
||
|
||
[[feature]]
|
||
id = "create.export-jpeg2000"
|
||
title = "Export pages as JPEG 2000"
|
||
area = "J"
|
||
tier = "P2"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "create.export-all-images"
|
||
title = "Export all embedded images"
|
||
area = "J"
|
||
tier = "P1"
|
||
milestone = "M10"
|
||
status = "shipped"
|
||
commands = ["export.all_images"]
|
||
tools = ["doc_export_all_images"]
|
||
evidence = ["crates/create/src/tests.rs::images_export_as_jpeg_unchanged_and_others_as_png", "crates/create/src/tests.rs::indexed_and_one_bit_images_decode", "crates/automation/tests/automation.rs::exporting_all_images_through_tools", "crates/ui-egui/tests/export.rs::export_dialogs_write_images_and_text"]
|
||
notes = "each image once (page resources and nested forms); JPEG unchanged, others PNG (gray/RGB/CMYK/ICC/Indexed, 1–16 bpc, stencil masks, soft mask as alpha); JPX/JBIG2/CCITT/separations reported as skipped; minimum size filter"
|
||
|
||
[[feature]]
|
||
id = "create.export-html"
|
||
title = "Export to HTML"
|
||
area = "J"
|
||
tier = "P1"
|
||
milestone = "M10"
|
||
status = "partial"
|
||
commands = ["export.html"]
|
||
tools = ["doc_export_office"]
|
||
evidence = ["crates/export/src/lib.rs::html_has_headings_paragraphs_and_images_in_order", "crates/export/src/lib.rs::grid_text_becomes_a_real_table_in_every_format", "crates/automation/tests/automation.rs::exporting_to_word_html_and_rtf", "crates/ui-egui/tests/compare.rs::export_to_word_html_and_rtf"]
|
||
notes = "Single HTML file with headings, paragraphs, inline images, and detected tables with spans/empty cells. Not yet: multi-page folder output, CSS from fonts."
|
||
|
||
[[feature]]
|
||
id = "create.export-text"
|
||
title = "Export to plain text"
|
||
area = "J"
|
||
tier = "P0"
|
||
milestone = "M10"
|
||
status = "shipped"
|
||
commands = ["export.text"]
|
||
tools = ["doc_export_text", "text_extract"]
|
||
evidence = ["crates/ui-egui/tests/export.rs::export_dialogs_write_images_and_text", "crates/automation/tests/automation.rs::exporting_images_and_text_through_tools"]
|
||
notes = "plain reading-order text; accessible text (tags) comes with M12"
|
||
|
||
[[feature]]
|
||
id = "create.export-accessible-text"
|
||
title = "Export accessible text (from tags)"
|
||
area = "J"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "create.export-xml"
|
||
title = "Export to XML"
|
||
area = "J"
|
||
tier = "P2"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "create.export-svg"
|
||
title = "Export to SVG"
|
||
area = "J"
|
||
tier = "P1"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "create.export-ps"
|
||
title = "Export to PostScript"
|
||
area = "J"
|
||
tier = "P2"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "create.export-eps"
|
||
title = "Export to EPS"
|
||
area = "J"
|
||
tier = "P2"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "create.export-ocr"
|
||
title = "Run OCR automatically when exporting scans"
|
||
area = "J"
|
||
tier = "P1"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "create.export-settings"
|
||
title = "Per-format export settings"
|
||
area = "J"
|
||
tier = "P2"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "create.text-extraction"
|
||
title = "Extract text in reading order (columns, RTL, CJK)"
|
||
area = "J"
|
||
tier = "P0"
|
||
milestone = "M2"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = ["text_extract"]
|
||
evidence = ["crates/render/src/text.rs::rtl_runs_come_out_in_logical_order_and_cjk_has_no_spaces", "crates/render/src/text.rs::lines_words_and_search", "crates/automation/tests/automation.rs::parallel_text_extraction_keeps_page_order_and_follows_edits"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "create.translate"
|
||
title = "Translate documents"
|
||
area = "J"
|
||
tier = "P3"
|
||
milestone = "M13"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "through an optional AiProvider; never Adobe's cloud"
|
||
|
||
[[feature]]
|
||
id = "create.cloud-office-conversion"
|
||
title = "Cloud conversion from the macOS Office add-in"
|
||
area = "J"
|
||
tier = "N/A"
|
||
milestone = "none"
|
||
status = "na"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "Adobe cloud; LibreOffice sidecar instead"
|
||
|
||
# ==================================================================================================
|
||
# K. Optimize and standards
|
||
# ==================================================================================================
|
||
|
||
[[feature]]
|
||
id = "optimize.reduce-file-size"
|
||
title = "Reduce file size (one click)"
|
||
area = "K"
|
||
tier = "P0"
|
||
milestone = "M11"
|
||
status = "shipped"
|
||
commands = ["optimize.reduce"]
|
||
tools = ["doc_reduce"]
|
||
evidence = ["crates/ui-egui/tests/create.rs::reduce_file_size_writes_a_compact_copy", "crates/engine/src/tests.rs::create_and_reduce", "crates/automation/tests/automation.rs::creating_and_reducing_through_tools", "crates/automation/tests/automation.rs::optimizing_through_tools"]
|
||
notes = "images above 225 ppi to 150 ppi JPEG medium, thumbnails dropped, resources merged, object streams"
|
||
|
||
[[feature]]
|
||
id = "optimize.compatibility-level"
|
||
title = "Make compatible with a chosen PDF version"
|
||
area = "K"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.optimizer"
|
||
title = "Advanced optimization (PDF Optimizer)"
|
||
area = "K"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "partial"
|
||
commands = ["optimize.advanced"]
|
||
tools = ["doc_optimize"]
|
||
evidence = ["crates/ui-egui/tests/create.rs::the_pdf_optimizer_dialog_saves_an_optimized_copy", "crates/automation/tests/automation.rs::optimizing_through_tools"]
|
||
notes = "Images, Discard Objects, Discard User Data, Clean Up; no Fonts or Transparency panels, no space audit"
|
||
|
||
[[feature]]
|
||
id = "optimize.downsample-images"
|
||
title = "Downsample colour, grey and mono images"
|
||
area = "K"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "partial"
|
||
commands = ["optimize.advanced"]
|
||
tools = ["doc_optimize"]
|
||
evidence = ["crates/optimize/tests/optimize.rs::images_are_measured_where_drawn_and_downsampled", "crates/optimize/tests/optimize.rs::settings_choose_what_happens", "crates/automation/tests/automation.rs::optimizing_through_tools"]
|
||
notes = "colour and grayscale, bicubic, effective resolution measured where drawn; monochrome not yet"
|
||
|
||
[[feature]]
|
||
id = "optimize.recompress-images"
|
||
title = "Recompress images (JPEG, JPEG 2000, Flate)"
|
||
area = "K"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "partial"
|
||
commands = ["optimize.advanced"]
|
||
tools = ["doc_optimize"]
|
||
evidence = ["crates/optimize/tests/optimize.rs::images_are_measured_where_drawn_and_downsampled", "crates/optimize/tests/optimize.rs::settings_choose_what_happens"]
|
||
notes = "JPEG (quality steps) and ZIP; no JPEG 2000"
|
||
|
||
[[feature]]
|
||
id = "optimize.mono-compression"
|
||
title = "Monochrome compression (JBIG2, CCITT G4)"
|
||
area = "K"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.only-if-smaller"
|
||
title = "Optimize images only if smaller"
|
||
area = "K"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "shipped"
|
||
commands = ["optimize.advanced"]
|
||
tools = ["doc_optimize"]
|
||
evidence = ["crates/optimize/tests/optimize.rs::images_are_measured_where_drawn_and_downsampled"]
|
||
notes = "always on"
|
||
|
||
[[feature]]
|
||
id = "optimize.unembed-fonts"
|
||
title = "Unembed fonts"
|
||
area = "K"
|
||
tier = "P2"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.flatten-transparency"
|
||
title = "Flatten transparency"
|
||
area = "K"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.discard-javascript"
|
||
title = "Discard JavaScript actions"
|
||
area = "K"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "shipped"
|
||
commands = ["optimize.advanced"]
|
||
tools = ["doc_optimize"]
|
||
evidence = ["crates/redact/src/tests.rs::hidden_information_is_counted_and_removed", "crates/automation/tests/automation.rs::optimizing_through_tools"]
|
||
notes = "with links and actions (Remove Hidden Information)"
|
||
|
||
[[feature]]
|
||
id = "optimize.discard-thumbnails"
|
||
title = "Discard embedded thumbnails"
|
||
area = "K"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "shipped"
|
||
commands = ["optimize.advanced"]
|
||
tools = ["doc_optimize"]
|
||
evidence = ["crates/optimize/tests/optimize.rs::images_are_measured_where_drawn_and_downsampled"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.discard-tags"
|
||
title = "Discard document tags"
|
||
area = "K"
|
||
tier = "P2"
|
||
milestone = "M11"
|
||
status = "shipped"
|
||
commands = ["optimize.advanced"]
|
||
tools = ["doc_optimize"]
|
||
evidence = ["crates/optimize/tests/optimize.rs::discards_and_clean_up", "crates/ui-egui/tests/create.rs::the_pdf_optimizer_dialog_saves_an_optimized_copy"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.discard-bookmarks"
|
||
title = "Discard bookmarks"
|
||
area = "K"
|
||
tier = "P2"
|
||
milestone = "M11"
|
||
status = "shipped"
|
||
commands = ["optimize.advanced"]
|
||
tools = ["doc_optimize"]
|
||
evidence = ["crates/redact/src/tests.rs::hidden_information_is_counted_and_removed", "crates/automation/tests/automation.rs::optimizing_through_tools"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.discard-search-index"
|
||
title = "Discard embedded search index"
|
||
area = "K"
|
||
tier = "P2"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.discard-print-settings"
|
||
title = "Discard embedded print settings"
|
||
area = "K"
|
||
tier = "P2"
|
||
milestone = "M11"
|
||
status = "shipped"
|
||
commands = ["optimize.advanced"]
|
||
tools = ["doc_optimize"]
|
||
evidence = ["crates/optimize/tests/optimize.rs::discards_and_clean_up"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.discard-alternate-images"
|
||
title = "Discard alternate images"
|
||
area = "K"
|
||
tier = "P2"
|
||
milestone = "M11"
|
||
status = "shipped"
|
||
commands = ["optimize.advanced"]
|
||
tools = ["doc_optimize"]
|
||
evidence = ["crates/optimize/tests/optimize.rs::images_are_measured_where_drawn_and_downsampled"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.discard-form-actions"
|
||
title = "Discard form submit, import and reset actions"
|
||
area = "K"
|
||
tier = "P2"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.flatten-form-fields"
|
||
title = "Flatten form fields while optimizing"
|
||
area = "K"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "shipped"
|
||
commands = ["optimize.advanced"]
|
||
tools = ["doc_optimize"]
|
||
evidence = ["crates/redact/src/tests.rs::hidden_information_is_counted_and_removed", "crates/automation/tests/automation.rs::optimizing_through_tools"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.discard-comments"
|
||
title = "Discard comments, forms and multimedia"
|
||
area = "K"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "shipped"
|
||
commands = ["optimize.advanced"]
|
||
tools = ["doc_optimize"]
|
||
evidence = ["crates/redact/src/tests.rs::hidden_information_is_counted_and_removed", "crates/automation/tests/automation.rs::optimizing_through_tools"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.discard-metadata"
|
||
title = "Discard document information and metadata"
|
||
area = "K"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "shipped"
|
||
commands = ["optimize.advanced"]
|
||
tools = ["doc_optimize"]
|
||
evidence = ["crates/automation/tests/automation.rs::optimizing_through_tools", "crates/ui-egui/tests/create.rs::the_pdf_optimizer_dialog_saves_an_optimized_copy"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.discard-attachments"
|
||
title = "Discard file attachments"
|
||
area = "K"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "shipped"
|
||
commands = ["optimize.advanced"]
|
||
tools = ["doc_optimize"]
|
||
evidence = ["crates/redact/src/tests.rs::hidden_information_is_counted_and_removed", "crates/automation/tests/automation.rs::optimizing_through_tools"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.discard-private-data"
|
||
title = "Discard private application data (PieceInfo)"
|
||
area = "K"
|
||
tier = "P2"
|
||
milestone = "M11"
|
||
status = "shipped"
|
||
commands = ["optimize.advanced"]
|
||
tools = ["doc_optimize"]
|
||
evidence = ["crates/redact/src/tests.rs::hidden_information_is_counted_and_removed", "crates/automation/tests/automation.rs::optimizing_through_tools"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.discard-external-refs"
|
||
title = "Discard external cross-references"
|
||
area = "K"
|
||
tier = "P2"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.discard-hidden-layers"
|
||
title = "Discard hidden layers and flatten visible ones"
|
||
area = "K"
|
||
tier = "P2"
|
||
milestone = "M11"
|
||
status = "partial"
|
||
commands = ["optimize.advanced"]
|
||
tools = ["doc_optimize"]
|
||
evidence = ["crates/redact/src/tests.rs::hidden_information_is_counted_and_removed", "crates/automation/tests/automation.rs::optimizing_through_tools"]
|
||
notes = "hidden layer content removed; visible layers not flattened"
|
||
|
||
[[feature]]
|
||
id = "optimize.merge-image-fragments"
|
||
title = "Detect and merge image fragments"
|
||
area = "K"
|
||
tier = "P3"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.lines-to-curves"
|
||
title = "Convert smooth lines to curves"
|
||
area = "K"
|
||
tier = "P3"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.compress-structure"
|
||
title = "Compress document structure (object streams)"
|
||
area = "K"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "partial"
|
||
commands = ["file.save_as"]
|
||
tools = ["doc_save"]
|
||
evidence = ["crates/cos/tests/writer.rs::full_save_packs_objects_into_object_streams"]
|
||
notes = "on by default for full saves; no Optimizer option yet"
|
||
|
||
[[feature]]
|
||
id = "optimize.flate-unencoded"
|
||
title = "Flate-encode unencoded streams; replace LZW with Flate"
|
||
area = "K"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "partial"
|
||
commands = ["optimize.advanced"]
|
||
tools = ["doc_optimize"]
|
||
evidence = ["crates/optimize/tests/optimize.rs::discards_and_clean_up"]
|
||
notes = "unencoded streams; LZW not yet re-encoded"
|
||
|
||
[[feature]]
|
||
id = "optimize.remove-invalid-links"
|
||
title = "Remove invalid links, bookmarks and unreferenced destinations"
|
||
area = "K"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "shipped"
|
||
commands = ["optimize.advanced"]
|
||
tools = ["doc_optimize"]
|
||
evidence = ["crates/optimize/tests/optimize.rs::invalid_links_and_unreferenced_destinations_go"]
|
||
notes = "Clean Up: links to missing pages or names are removed; bookmarks keep their place (and children) but lose a broken destination; named destinations nothing refers to (links, bookmarks, actions anywhere) are removed from the name tree and the old /Dests; counts reported"
|
||
|
||
[[feature]]
|
||
id = "optimize.optimize-content"
|
||
title = "Optimize page content streams"
|
||
area = "K"
|
||
tier = "P2"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.audit-space"
|
||
title = "Audit space usage"
|
||
area = "K"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "shipped"
|
||
commands = ["optimize.advanced"]
|
||
tools = ["doc_audit_space"]
|
||
evidence = ["crates/optimize/tests/optimize.rs::space_audit_shares_the_file_out_by_kind", "crates/automation/tests/automation.rs::auditing_space_through_tools", "crates/ui-egui/tests/viewer.rs::optimizer_audits_space_usage"]
|
||
notes = "PDF Optimizer ▸ Audit space usage: 16 categories (images, content streams, fonts, X Object forms, structure, bookmarks, thumbnails, comments, forms, named destinations, colour spaces, shadings, patterns, graphics states, embedded files, overhead) with bytes and percentages"
|
||
|
||
[[feature]]
|
||
id = "optimize.optimizer-presets"
|
||
title = "Optimizer presets"
|
||
area = "K"
|
||
tier = "P2"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.batch-optimize"
|
||
title = "Optimize multiple files"
|
||
area = "K"
|
||
tier = "P2"
|
||
milestone = "M13"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.preflight"
|
||
title = "Preflight analysis"
|
||
area = "K"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.preflight-fix"
|
||
title = "Preflight analyze and fix"
|
||
area = "K"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.preflight-profiles"
|
||
title = "Built-in preflight profiles"
|
||
area = "K"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.preflight-single-checks"
|
||
title = "Single checks"
|
||
area = "K"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.preflight-single-fixups"
|
||
title = "Single fixups"
|
||
area = "K"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.preflight-custom-profiles"
|
||
title = "Create and edit custom profiles"
|
||
area = "K"
|
||
tier = "P2"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.preflight-profile-exchange"
|
||
title = "Import, export and lock profiles"
|
||
area = "K"
|
||
tier = "P2"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.preflight-results"
|
||
title = "Preflight results tree with object details"
|
||
area = "K"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.preflight-snap-view"
|
||
title = "Snap view of a flagged object"
|
||
area = "K"
|
||
tier = "P2"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.preflight-reports"
|
||
title = "Preflight reports (PDF, XML, text)"
|
||
area = "K"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.preflight-droplets"
|
||
title = "Preflight droplets and batch preflight"
|
||
area = "K"
|
||
tier = "P2"
|
||
milestone = "M13"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.preflight-audit-trail"
|
||
title = "Embed preflight audit trail"
|
||
area = "K"
|
||
tier = "P3"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.pdfa-validate"
|
||
title = "Verify PDF/A compliance (1-4)"
|
||
area = "K"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "partial"
|
||
commands = ["standards.pdfa"]
|
||
tools = ["pdfa_verify"]
|
||
evidence = ["crates/preflight/src/tests.rs::verify_reports_what_breaks_pdfa", "crates/preflight/src/tests.rs::convert_fixes_all_but_fonts", "crates/automation/tests/automation.rs::pdfa_through_tools", "crates/ui-egui/tests/compare.rs::pdfa_dialog_verifies_and_converts"]
|
||
notes = "PDF/A-2b/3b structural rules: identification, encryption, output intent, fonts embedded, annotations, actions, images, embedded files. Not yet: content-stream operator checks, transparency details, PDF/A-1 and -4, a veraPDF comparison."
|
||
|
||
[[feature]]
|
||
id = "optimize.pdfa-convert"
|
||
title = "Save as PDF/A (1b, 2b, 3b, 4)"
|
||
area = "K"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "partial"
|
||
commands = ["standards.pdfa"]
|
||
tools = ["pdfa_convert"]
|
||
evidence = ["crates/preflight/src/tests.rs::convert_fixes_all_but_fonts", "crates/automation/tests/automation.rs::pdfa_through_tools", "crates/ui-egui/tests/compare.rs::pdfa_dialog_verifies_and_converts"]
|
||
notes = "Fixes XMP, output intent (generated sRGB ICC profile), forbidden actions, annotation flags, interpolation, encryption, AFRelationship. Not yet: embedding fonts, converting device CMYK, flattening transparency for PDF/A-1."
|
||
|
||
[[feature]]
|
||
id = "optimize.pdfa-accessible-levels"
|
||
title = "PDF/A conformance levels a and u"
|
||
area = "K"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.pdfx-validate"
|
||
title = "Verify PDF/X compliance"
|
||
area = "K"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.pdfx-convert"
|
||
title = "Save as PDF/X (1a, 3, 4)"
|
||
area = "K"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.output-intents"
|
||
title = "Choose output intents and conditions"
|
||
area = "K"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "partial"
|
||
commands = ["standards.pdfa"]
|
||
tools = ["pdfa_convert", "pdfa_verify"]
|
||
evidence = ["crates/preflight/src/tests.rs::convert_fixes_all_but_fonts", "crates/automation/tests/automation.rs::pdfa_through_tools", "crates/ui-egui/tests/compare.rs::pdfa_dialog_verifies_and_converts"]
|
||
notes = "sRGB output intent added and listed; no choice of other conditions yet."
|
||
|
||
[[feature]]
|
||
id = "optimize.pdfe"
|
||
title = "PDF/E"
|
||
area = "K"
|
||
tier = "P2"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.pdfvt"
|
||
title = "PDF/VT"
|
||
area = "K"
|
||
tier = "P2"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.pdfua-validate"
|
||
title = "Verify PDF/UA-1 and PDF/UA-2"
|
||
area = "K"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.standards-panel"
|
||
title = "Standards panel showing declared conformance"
|
||
area = "K"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "partial"
|
||
commands = ["standards.pdfa"]
|
||
tools = ["pdfa_verify"]
|
||
evidence = ["crates/automation/tests/automation.rs::pdfa_through_tools", "crates/ui-egui/tests/compare.rs::pdfa_dialog_verifies_and_converts"]
|
||
notes = "Declared PDF/A, PDF/UA and output intents shown in the PDF/A dialog and returned by pdfa_verify."
|
||
|
||
[[feature]]
|
||
id = "optimize.remove-standards-id"
|
||
title = "Remove PDF/A or PDF/X identification"
|
||
area = "K"
|
||
tier = "P2"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.pdfx5-reference-xobjects"
|
||
title = "Show PDF/X-5 reference XObjects"
|
||
area = "K"
|
||
tier = "P3"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "optimize.einvoice-pdfa3"
|
||
title = "Embed e-invoice XML in PDF/A-3"
|
||
area = "K"
|
||
tier = "P3"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
# ==================================================================================================
|
||
# L. Print and print production
|
||
# ==================================================================================================
|
||
|
||
[[feature]]
|
||
id = "print.print-dialog"
|
||
title = "Print dialog"
|
||
area = "L"
|
||
tier = "P0"
|
||
milestone = "M10"
|
||
status = "shipped"
|
||
commands = ["print.dialog"]
|
||
tools = ["doc_print", "printers"]
|
||
evidence = ["crates/ui-egui/tests/print.rs::print_dialog_lays_out_sheets_and_saves_a_pdf", "crates/engine/src/tests.rs::printing_lays_out_sheets_that_render"]
|
||
notes = "⌘P; settings and live sheet preview"
|
||
|
||
[[feature]]
|
||
id = "print.printer-selection"
|
||
title = "Choose printer and properties"
|
||
area = "L"
|
||
tier = "P0"
|
||
milestone = "M10"
|
||
status = "shipped"
|
||
commands = ["print.dialog"]
|
||
tools = ["printers", "doc_print"]
|
||
evidence = ["crates/print/src/tests.rs::spooler_arguments_and_printer_list"]
|
||
notes = "CUPS printers via lpstat; Save as PDF; Windows spooler pending"
|
||
|
||
[[feature]]
|
||
id = "print.copies-collate"
|
||
title = "Copies and collation"
|
||
area = "L"
|
||
tier = "P0"
|
||
milestone = "M10"
|
||
status = "shipped"
|
||
commands = ["print.dialog"]
|
||
tools = ["doc_print"]
|
||
evidence = ["crates/print/src/tests.rs::spooler_arguments_and_printer_list"]
|
||
notes = "lp -n, collate option"
|
||
|
||
[[feature]]
|
||
id = "print.page-range"
|
||
title = "Print all, current page or a range (labels allowed)"
|
||
area = "L"
|
||
tier = "P0"
|
||
milestone = "M10"
|
||
status = "shipped"
|
||
commands = ["print.dialog"]
|
||
tools = ["doc_print"]
|
||
evidence = ["crates/print/src/tests.rs::page_selection", "crates/automation/tests/automation.rs::printing_through_tools"]
|
||
notes = "all, current, ranges with labels and open ends"
|
||
|
||
[[feature]]
|
||
id = "print.odd-even-reverse"
|
||
title = "Odd/even pages and reverse order"
|
||
area = "L"
|
||
tier = "P1"
|
||
milestone = "M10"
|
||
status = "shipped"
|
||
commands = ["print.dialog"]
|
||
tools = ["doc_print"]
|
||
evidence = ["crates/print/src/tests.rs::page_selection"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "print.scaling"
|
||
title = "Fit, actual size, shrink oversized, custom scale"
|
||
area = "L"
|
||
tier = "P0"
|
||
milestone = "M10"
|
||
status = "shipped"
|
||
commands = ["print.dialog"]
|
||
tools = ["doc_print"]
|
||
evidence = ["crates/print/src/tests.rs::size_modes"]
|
||
notes = "fit, actual size, shrink oversized, custom %"
|
||
|
||
[[feature]]
|
||
id = "print.paper-source-by-size"
|
||
title = "Choose paper source by PDF page size"
|
||
area = "L"
|
||
tier = "P2"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "print.duplex"
|
||
title = "Print on both sides"
|
||
area = "L"
|
||
tier = "P1"
|
||
milestone = "M10"
|
||
status = "shipped"
|
||
commands = ["print.dialog"]
|
||
tools = ["doc_print"]
|
||
evidence = ["crates/print/src/tests.rs::spooler_arguments_and_printer_list"]
|
||
notes = "long/short edge via the spooler"
|
||
|
||
[[feature]]
|
||
id = "print.n-up"
|
||
title = "Multiple pages per sheet"
|
||
area = "L"
|
||
tier = "P0"
|
||
milestone = "M10"
|
||
status = "shipped"
|
||
commands = ["print.dialog"]
|
||
tools = ["doc_print"]
|
||
evidence = ["crates/print/src/tests.rs::multiple_pages_per_sheet", "crates/automation/tests/automation.rs::printing_through_tools"]
|
||
notes = "2/4/6/9/16, four orders, borders, auto-rotate"
|
||
|
||
[[feature]]
|
||
id = "print.booklet"
|
||
title = "Booklet printing"
|
||
area = "L"
|
||
tier = "P0"
|
||
milestone = "M10"
|
||
status = "shipped"
|
||
commands = ["print.dialog"]
|
||
tools = ["doc_print"]
|
||
evidence = ["crates/print/src/tests.rs::booklets_pair_pages_for_folding"]
|
||
notes = "both/front/back, left/right binding"
|
||
|
||
[[feature]]
|
||
id = "print.poster"
|
||
title = "Poster / tiled printing with overlap and cut marks"
|
||
area = "L"
|
||
tier = "P1"
|
||
milestone = "M10"
|
||
status = "shipped"
|
||
commands = ["print.dialog"]
|
||
tools = ["doc_print"]
|
||
evidence = ["crates/print/src/tests.rs::posters_tile_with_overlap", "crates/automation/tests/automation.rs::printing_through_tools"]
|
||
notes = "tile scale, overlap, cut marks (labels pending)"
|
||
|
||
[[feature]]
|
||
id = "print.orientation"
|
||
title = "Auto portrait/landscape"
|
||
area = "L"
|
||
tier = "P0"
|
||
milestone = "M10"
|
||
status = "shipped"
|
||
commands = ["print.dialog"]
|
||
tools = ["doc_print"]
|
||
evidence = ["crates/print/src/tests.rs::size_modes"]
|
||
notes = "auto portrait/landscape"
|
||
|
||
[[feature]]
|
||
id = "print.comments-and-forms"
|
||
title = "Print document, markups, stamps or form fields only"
|
||
area = "L"
|
||
tier = "P0"
|
||
milestone = "M10"
|
||
status = "shipped"
|
||
commands = ["print.dialog"]
|
||
tools = ["doc_print"]
|
||
evidence = ["crates/print/src/tests.rs::imposed_pdf_has_the_sheets_and_honours_comments_and_forms"]
|
||
notes = "Print flag honoured"
|
||
|
||
[[feature]]
|
||
id = "print.print-with-comment-summary"
|
||
title = "Print with a comments summary"
|
||
area = "L"
|
||
tier = "P2"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "print.print-preview"
|
||
title = "Print preview"
|
||
area = "L"
|
||
tier = "P0"
|
||
milestone = "M10"
|
||
status = "shipped"
|
||
commands = ["print.dialog"]
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/print.rs::print_dialog_lays_out_sheets_and_saves_a_pdf"]
|
||
notes = "live sheet preview with sheet navigation"
|
||
|
||
[[feature]]
|
||
id = "print.grayscale"
|
||
title = "Print in grayscale"
|
||
area = "L"
|
||
tier = "P1"
|
||
milestone = "M10"
|
||
status = "partial"
|
||
commands = ["print.dialog"]
|
||
tools = ["doc_print"]
|
||
evidence = ["crates/print/src/tests.rs::spooler_arguments_and_printer_list"]
|
||
notes = "asks the printer for monochrome; the PDF itself stays in colour"
|
||
|
||
[[feature]]
|
||
id = "print.print-as-image"
|
||
title = "Print as image"
|
||
area = "L"
|
||
tier = "P1"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "print.page-setup"
|
||
title = "Page setup"
|
||
area = "L"
|
||
tier = "P1"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "print.print-ready-pdf"
|
||
title = "Produce a print-ready imposed PDF"
|
||
area = "L"
|
||
tier = "P1"
|
||
milestone = "M10"
|
||
status = "shipped"
|
||
commands = ["print.dialog"]
|
||
tools = ["doc_print"]
|
||
evidence = ["crates/ui-egui/tests/print.rs::print_dialog_lays_out_sheets_and_saves_a_pdf", "crates/automation/tests/automation.rs::printing_through_tools", "crates/engine/src/tests.rs::printing_lays_out_sheets_that_render"]
|
||
notes = "Save as PDF from the dialog"
|
||
|
||
[[feature]]
|
||
id = "print.print-pages-from-thumbnails"
|
||
title = "Print selected pages from thumbnails"
|
||
area = "L"
|
||
tier = "P2"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "print.postscript-output"
|
||
title = "PostScript Level 3 output"
|
||
area = "L"
|
||
tier = "P2"
|
||
milestone = "M10"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "print.output-preview"
|
||
title = "Output preview"
|
||
area = "L"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "print.separations"
|
||
title = "Separations preview with area coverage"
|
||
area = "L"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "print.total-area-coverage"
|
||
title = "Total area coverage warning"
|
||
area = "L"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "print.color-warnings"
|
||
title = "Overprint and rich-black warnings"
|
||
area = "L"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "print.object-inspector"
|
||
title = "Object inspector"
|
||
area = "L"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "print.simulate-paper-ink"
|
||
title = "Simulate paper colour and black ink"
|
||
area = "L"
|
||
tier = "P2"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "print.overprint-preview"
|
||
title = "Overprint preview"
|
||
area = "L"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "print.convert-colors"
|
||
title = "Convert colours"
|
||
area = "L"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "print.ink-manager"
|
||
title = "Ink manager"
|
||
area = "L"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "print.ink-aliases"
|
||
title = "Ink aliases and spot-to-process"
|
||
area = "L"
|
||
tier = "P2"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "print.printer-marks"
|
||
title = "Add printer marks (crop, bleed, registration, colour bars)"
|
||
area = "L"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "print.fix-hairlines"
|
||
title = "Fix hairlines"
|
||
area = "L"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "print.flattener-preview"
|
||
title = "Flattener preview"
|
||
area = "L"
|
||
tier = "P2"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "print.color-management"
|
||
title = "Colour management settings (working spaces, intents)"
|
||
area = "L"
|
||
tier = "P1"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "print.advanced-print-separations"
|
||
title = "Print separations and in-RIP separations"
|
||
area = "L"
|
||
tier = "P2"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "print.print-marks-bleeds"
|
||
title = "Marks and bleeds when printing"
|
||
area = "L"
|
||
tier = "P2"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "print.trap-presets"
|
||
title = "Trap presets (stored as data)"
|
||
area = "L"
|
||
tier = "P3"
|
||
milestone = "M11"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "print.jdf"
|
||
title = "JDF job definitions (read-only)"
|
||
area = "L"
|
||
tier = "P3"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
# ==================================================================================================
|
||
# M. Accessibility
|
||
# ==================================================================================================
|
||
|
||
[[feature]]
|
||
id = "a11y.app-screen-reader"
|
||
title = "Screen-reader support for the app (AccessKit)"
|
||
area = "M"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "partial"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/control.rs::inspect_and_click_by_label_and_id"]
|
||
notes = "AccessKit labels on all custom widgets; full audit in M14.3"
|
||
|
||
[[feature]]
|
||
id = "a11y.keyboard-only"
|
||
title = "Full keyboard operation of the app"
|
||
area = "M"
|
||
tier = "P0"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "a11y.checker"
|
||
title = "Accessibility checker (full check)"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/a11y/src/tests.rs::an_untagged_document_fails_the_document_rules", "crates/a11y/src/tests.rs::a_well_tagged_document_passes", "crates/ui-egui/tests/accessibility.rs::checking_fixing_skipping_and_reporting", "crates/automation/tests/automation.rs::accessibility_check_report_and_fixes_through_tools"]
|
||
notes = "full check: options dialog (report, page range, 32 rules by category, 31 on by default), results panel by category with Passed / Failed / Needs manual check / Skipped and findings that go to their page"
|
||
|
||
[[feature]]
|
||
id = "a11y.checker-report"
|
||
title = "Accessibility report"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.report"]
|
||
tools = ["accessibility_report"]
|
||
evidence = ["crates/a11y/src/tests.rs::rules_have_ids_and_the_report_lists_them", "crates/ui-egui/tests/accessibility.rs::checking_fixing_skipping_and_reporting", "crates/automation/tests/automation.rs::accessibility_check_report_and_fixes_through_tools"]
|
||
notes = "HTML report: summary counts and a table per category with descriptions and findings"
|
||
|
||
[[feature]]
|
||
id = "a11y.checker-fix"
|
||
title = "Fix, skip, explain and re-check rules"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_fix"]
|
||
evidence = ["crates/ui-egui/tests/accessibility.rs::checking_fixing_skipping_and_reporting", "crates/automation/tests/automation.rs::accessibility_check_report_and_fixes_through_tools"]
|
||
notes = "right-click a rule: Fix (language via Document Properties, title, tab order), Skip Rule (kept on Check Again), Explain, Check Again, Show Report, Options"
|
||
|
||
[[feature]]
|
||
id = "a11y.rule-permission-flag"
|
||
title = "Rule: accessibility permission flag"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/a11y/src/tests.rs::an_untagged_document_fails_the_document_rules"]
|
||
notes = "rule permission-flag: the file's /P bit 10 whichever password opened it"
|
||
|
||
[[feature]]
|
||
id = "a11y.rule-image-only"
|
||
title = "Rule: image-only PDF"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/a11y/src/tests.rs::scans_identity_fonts_and_long_documents"]
|
||
notes = "rule image-only: images and no text on the checked pages"
|
||
|
||
[[feature]]
|
||
id = "a11y.rule-tagged-pdf"
|
||
title = "Rule: tagged PDF"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/a11y/src/tests.rs::an_untagged_document_fails_the_document_rules"]
|
||
notes = "rule tagged-pdf: StructTreeRoot and MarkInfo Marked true"
|
||
|
||
[[feature]]
|
||
id = "a11y.rule-logical-order"
|
||
title = "Rule: logical reading order (manual)"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/a11y/src/tests.rs::a_well_tagged_document_passes"]
|
||
notes = "rule logical-reading-order: needs manual check"
|
||
|
||
[[feature]]
|
||
id = "a11y.rule-primary-language"
|
||
title = "Rule: primary language"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/automation/tests/automation.rs::accessibility_check_report_and_fixes_through_tools"]
|
||
notes = "rule primary-language: catalog /Lang; fix via Document Properties or accessibility_fix"
|
||
|
||
[[feature]]
|
||
id = "a11y.rule-title"
|
||
title = "Rule: title shown in the window"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/automation/tests/automation.rs::accessibility_check_report_and_fixes_through_tools"]
|
||
notes = "rule title: Info Title and DisplayDocTitle; Fix sets both"
|
||
|
||
[[feature]]
|
||
id = "a11y.rule-bookmarks"
|
||
title = "Rule: bookmarks in long documents"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/a11y/src/tests.rs::scans_identity_fonts_and_long_documents"]
|
||
notes = "rule bookmarks: more than 20 pages need bookmarks"
|
||
|
||
[[feature]]
|
||
id = "a11y.rule-color-contrast"
|
||
title = "Rule: colour contrast (manual)"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/automation/tests/automation.rs::accessibility_check_report_and_fixes_through_tools"]
|
||
notes = "rule color-contrast: needs manual check; off by default as in Acrobat"
|
||
|
||
[[feature]]
|
||
id = "a11y.rule-tagged-content"
|
||
title = "Rule: all content tagged or artifact"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/a11y/src/tests.rs::page_content_and_annotation_problems_are_found"]
|
||
notes = "rule tagged-content: painting outside MCID or Artifact marked content, through form XObjects; counted per page"
|
||
|
||
[[feature]]
|
||
id = "a11y.rule-tagged-annotations"
|
||
title = "Rule: tagged annotations"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/a11y/src/tests.rs::page_content_and_annotation_problems_are_found"]
|
||
notes = "rule tagged-annotations: annotations other than popups, widgets and multimedia need /StructParent"
|
||
|
||
[[feature]]
|
||
id = "a11y.rule-tab-order"
|
||
title = "Rule: tab order follows structure"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/a11y/src/tests.rs::page_content_and_annotation_problems_are_found"]
|
||
notes = "rule tab-order: pages with annotations need /Tabs /S; Fix sets it on every page"
|
||
|
||
[[feature]]
|
||
id = "a11y.rule-character-encoding"
|
||
title = "Rule: reliable character encoding"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/a11y/src/tests.rs::scans_identity_fonts_and_long_documents"]
|
||
notes = "rule character-encoding: ToUnicode, a standard encoding, or a predefined CJK collection; each font once"
|
||
|
||
[[feature]]
|
||
id = "a11y.rule-tagged-multimedia"
|
||
title = "Rule: tagged multimedia"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/a11y/src/tests.rs::a_well_tagged_document_passes"]
|
||
notes = "rule tagged-multimedia: Screen, Movie, Sound, RichMedia and 3D annotations need /StructParent"
|
||
|
||
[[feature]]
|
||
id = "a11y.rule-screen-flicker"
|
||
title = "Rule: screen flicker (manual)"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/a11y/src/tests.rs::a_well_tagged_document_passes"]
|
||
notes = "rule screen-flicker: needs manual check"
|
||
|
||
[[feature]]
|
||
id = "a11y.rule-scripts"
|
||
title = "Rule: scripts (manual)"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/automation/tests/automation.rs::accessibility_check_report_and_fixes_through_tools"]
|
||
notes = "rule scripts: needs manual check"
|
||
|
||
[[feature]]
|
||
id = "a11y.rule-timed-responses"
|
||
title = "Rule: timed responses (manual)"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/a11y/src/tests.rs::a_well_tagged_document_passes"]
|
||
notes = "rule timed-responses: needs manual check"
|
||
|
||
[[feature]]
|
||
id = "a11y.rule-navigation-links"
|
||
title = "Rule: navigation links (manual)"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/a11y/src/tests.rs::a_well_tagged_document_passes"]
|
||
notes = "rule navigation-links: needs manual check"
|
||
|
||
[[feature]]
|
||
id = "a11y.rule-tagged-fields"
|
||
title = "Rule: tagged form fields"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/a11y/src/tests.rs::a_well_tagged_document_passes"]
|
||
notes = "rule tagged-form-fields: widgets need /StructParent"
|
||
|
||
[[feature]]
|
||
id = "a11y.rule-field-descriptions"
|
||
title = "Rule: field descriptions"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/a11y/src/tests.rs::page_content_and_annotation_problems_are_found"]
|
||
notes = "rule field-descriptions: fields need a tooltip (/TU)"
|
||
|
||
[[feature]]
|
||
id = "a11y.rule-figure-alt"
|
||
title = "Rule: figures have alternate text"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/a11y/src/tests.rs::structure_problems_are_found"]
|
||
notes = "rule figures-alt-text: Figure elements need Alt or ActualText (or an ancestor's)"
|
||
|
||
[[feature]]
|
||
id = "a11y.rule-nested-alt"
|
||
title = "Rule: no nested alternate text"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/a11y/src/tests.rs::structure_problems_are_found"]
|
||
notes = "rule nested-alt-text: Alt inside an element with Alt"
|
||
|
||
[[feature]]
|
||
id = "a11y.rule-alt-associated"
|
||
title = "Rule: alternate text associated with content"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/a11y/src/tests.rs::structure_problems_are_found"]
|
||
notes = "rule alt-text-associated: Alt on an element with no content beneath it"
|
||
|
||
[[feature]]
|
||
id = "a11y.rule-alt-hides-annot"
|
||
title = "Rule: alternate text does not hide annotations"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/a11y/src/tests.rs::structure_problems_are_found"]
|
||
notes = "rule alt-text-hides-annotation: Alt on an element containing an OBJR"
|
||
|
||
[[feature]]
|
||
id = "a11y.rule-other-alt"
|
||
title = "Rule: other elements have alternate text"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/a11y/src/tests.rs::structure_problems_are_found"]
|
||
notes = "rule other-elements-alt-text: Formula elements need Alt"
|
||
|
||
[[feature]]
|
||
id = "a11y.rule-table-rows"
|
||
title = "Rule: table rows"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/a11y/src/tests.rs::structure_problems_are_found"]
|
||
notes = "rule table-rows: TR parent is Table, THead, TBody or TFoot"
|
||
|
||
[[feature]]
|
||
id = "a11y.rule-table-cells"
|
||
title = "Rule: TH and TD in rows"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/a11y/src/tests.rs::structure_problems_are_found"]
|
||
notes = "rule table-cells: TH/TD parent is TR"
|
||
|
||
[[feature]]
|
||
id = "a11y.rule-table-headers"
|
||
title = "Rule: tables have headers"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/a11y/src/tests.rs::structure_problems_are_found"]
|
||
notes = "rule table-headers: at least one TH"
|
||
|
||
[[feature]]
|
||
id = "a11y.rule-table-regularity"
|
||
title = "Rule: table regularity"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/a11y/src/tests.rs::row_and_column_spans_keep_a_table_regular"]
|
||
notes = "rule table-regularity: equal row widths counting ColSpan and RowSpan"
|
||
|
||
[[feature]]
|
||
id = "a11y.rule-table-summary"
|
||
title = "Rule: table summary"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/a11y/src/tests.rs::structure_problems_are_found"]
|
||
notes = "rule table-summary: Summary table attribute"
|
||
|
||
[[feature]]
|
||
id = "a11y.rule-list-items"
|
||
title = "Rule: list items"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/a11y/src/tests.rs::structure_problems_are_found"]
|
||
notes = "rule list-items: LI parent is L"
|
||
|
||
[[feature]]
|
||
id = "a11y.rule-lbl-lbody"
|
||
title = "Rule: Lbl and LBody"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/a11y/src/tests.rs::structure_problems_are_found"]
|
||
notes = "rule lbl-lbody: Lbl/LBody parent is LI (TOCI and notes allowed)"
|
||
|
||
[[feature]]
|
||
id = "a11y.rule-heading-nesting"
|
||
title = "Rule: heading nesting"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.check"]
|
||
tools = ["accessibility_check"]
|
||
evidence = ["crates/a11y/src/tests.rs::structure_problems_are_found"]
|
||
notes = "rule heading-nesting: H1–H6 never skip a level going down; the first is H1"
|
||
|
||
[[feature]]
|
||
id = "a11y.autotag"
|
||
title = "Automatically tag a document"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "a11y.autotag-fields"
|
||
title = "Autotag form fields"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "a11y.reading-order-tool"
|
||
title = "Reading order tool"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "a11y.tags-panel"
|
||
title = "Tags panel (new, delete, find, change to artifact)"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "a11y.tag-properties"
|
||
title = "Tag properties (type, title, actual text, alternate text, language)"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "a11y.role-map"
|
||
title = "Role map editor"
|
||
area = "M"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "a11y.class-map"
|
||
title = "Class map editor"
|
||
area = "M"
|
||
tier = "P3"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "a11y.order-panel"
|
||
title = "Order panel"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "a11y.content-panel"
|
||
title = "Content panel"
|
||
area = "M"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "a11y.create-tag-from-selection"
|
||
title = "Create a tag from a selection"
|
||
area = "M"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "a11y.artifact-marking"
|
||
title = "Mark content as artifact"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "a11y.find-untagged"
|
||
title = "Find unmarked content, comments and links"
|
||
area = "M"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "a11y.table-editor"
|
||
title = "Table editor (headers, scope, spans)"
|
||
area = "M"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "a11y.alt-text-workflow"
|
||
title = "Set alternate text figure by figure (with decorative option)"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["a11y.alt_text"]
|
||
tools = ["accessibility_figures", "accessibility_set_alt"]
|
||
evidence = ["crates/a11y/src/tests.rs::figures_are_listed_with_their_alt_text_and_place", "crates/a11y/src/tests.rs::setting_alt_text_and_marking_decorative", "crates/ui-egui/tests/accessibility.rs::setting_alternate_text_figure_by_figure", "crates/automation/tests/automation.rs::accessibility_check_report_and_fixes_through_tools"]
|
||
notes = "Set Alternate Text dialog: each tagged figure (role map honoured) with a preview of where it is drawn, its alternate text, Decorative figure (content becomes /Artifact, element leaves the tags and parent tree), Previous/Next, Save & Close as one undo step"
|
||
|
||
[[feature]]
|
||
id = "a11y.alt-text-suggestions"
|
||
title = "Suggest alternate text via an AI provider"
|
||
area = "M"
|
||
tier = "P2"
|
||
milestone = "M13"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "optional provider; off by default"
|
||
|
||
[[feature]]
|
||
id = "a11y.set-language"
|
||
title = "Set document language"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["file.properties"]
|
||
tools = ["doc_initial_view"]
|
||
evidence = ["crates/organize/src/tests.rs::initial_view_round_trips_and_keeps_scripts", "crates/automation/tests/automation.rs::initial_view_through_tools", "crates/ui-egui/tests/viewer.rs::initial_view_is_edited_and_honoured_on_open"]
|
||
notes = "Document Properties ▸ Initial View ▸ Language (catalog /Lang)"
|
||
|
||
[[feature]]
|
||
id = "a11y.set-title-display"
|
||
title = "Show the document title in the window"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["file.properties"]
|
||
tools = ["doc_initial_view"]
|
||
evidence = ["crates/automation/tests/automation.rs::initial_view_through_tools", "crates/ui-egui/tests/viewer.rs::tab_and_window_show_the_document_title_when_asked"]
|
||
notes = "Initial View ▸ Show: Document Title (/DisplayDocTitle); the tab and window then show the title"
|
||
|
||
[[feature]]
|
||
id = "a11y.make-accessible-action"
|
||
title = "Make Accessible guided action"
|
||
area = "M"
|
||
tier = "P1"
|
||
milestone = "M13"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "a11y.setup-assistant"
|
||
title = "Accessibility setup assistant"
|
||
area = "M"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "a11y.read-aloud"
|
||
title = "Read out loud (platform TTS)"
|
||
area = "M"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "a11y.read-aloud-options"
|
||
title = "Read-aloud voice, pitch and speed"
|
||
area = "M"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "a11y.replace-page-colors"
|
||
title = "Replace page colours (high contrast)"
|
||
area = "M"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "a11y.reflow-tagged"
|
||
title = "Reflow tagged content"
|
||
area = "M"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "a11y.keyboard-selection-cursor"
|
||
title = "Always show the keyboard selection cursor"
|
||
area = "M"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
# ==================================================================================================
|
||
# N. Compare, automation, AI, preferences, misc
|
||
# ==================================================================================================
|
||
|
||
[[feature]]
|
||
id = "misc.compare-text"
|
||
title = "Compare files: text differences"
|
||
area = "N"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["doc.compare"]
|
||
tools = ["doc_compare", "doc_compare_mark"]
|
||
evidence = ["crates/compare/src/lib.rs::insertions_deletions_and_replacements", "crates/engine/src/tests.rs::comparing_two_versions_of_a_document", "crates/automation/tests/automation.rs::comparing_documents_through_tools", "crates/ui-egui/tests/compare.rs::compare_two_versions"]
|
||
notes = "Word-level Myers diff with patience anchors for long files; replaced/inserted/deleted shaded on the new file, listed in the Compare panel, or added as comments."
|
||
|
||
[[feature]]
|
||
id = "misc.compare-visual"
|
||
title = "Compare files: visual differences"
|
||
area = "N"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["doc.compare"]
|
||
tools = ["doc_compare"]
|
||
evidence = ["crates/compare/src/lib.rs::finds_changed_regions", "crates/engine/src/tests.rs::comparing_two_versions_of_a_document", "crates/automation/tests/automation.rs::comparing_documents_through_tools"]
|
||
notes = "Pages rendered at 72 dpi and compared page by page; differing regions shaded orange where no text change explains them."
|
||
|
||
[[feature]]
|
||
id = "misc.compare-side-by-side"
|
||
title = "Side-by-side compare view with synchronised scrolling"
|
||
area = "N"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.compare-report"
|
||
title = "Compare summary report as PDF"
|
||
area = "N"
|
||
tier = "P1"
|
||
milestone = "M12"
|
||
status = "shipped"
|
||
commands = ["doc.compare"]
|
||
tools = ["doc_compare_report"]
|
||
evidence = ["crates/engine/src/tests.rs::comparing_two_versions_of_a_document", "crates/automation/tests/automation.rs::comparing_documents_through_tools", "crates/ui-egui/tests/compare.rs::compare_two_versions"]
|
||
notes = "Summary report as a new PDF (counts and every change with pages)."
|
||
|
||
[[feature]]
|
||
id = "misc.compare-filters"
|
||
title = "Filter changes by type (text, images, formatting)"
|
||
area = "N"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.compare-page-ranges"
|
||
title = "Compare page ranges"
|
||
area = "N"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.compare-scanned"
|
||
title = "Compare scanned documents via OCR"
|
||
area = "N"
|
||
tier = "P2"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.action-wizard-run"
|
||
title = "Run guided actions on files"
|
||
area = "N"
|
||
tier = "P1"
|
||
milestone = "M13"
|
||
status = "shipped"
|
||
commands = ["actions.wizard"]
|
||
tools = ["action_list", "action_run"]
|
||
evidence = ["crates/engine/src/tests.rs::actions_run_their_steps_on_files", "crates/automation/tests/automation.rs::actions_through_tools", "crates/ui-egui/tests/actions.rs::run_and_create_actions"]
|
||
notes = "Pick an action and files; results go to a chosen folder, in the background with progress."
|
||
|
||
[[feature]]
|
||
id = "misc.action-wizard-create"
|
||
title = "Create and edit actions"
|
||
area = "N"
|
||
tier = "P1"
|
||
milestone = "M13"
|
||
status = "shipped"
|
||
commands = ["actions.wizard"]
|
||
tools = ["action_run"]
|
||
evidence = ["crates/automation/tests/automation.rs::actions_through_tools", "crates/ui-egui/tests/actions.rs::run_and_create_actions"]
|
||
notes = "New/Edit/Delete actions with 12 step kinds (OCR, reduce size, remove hidden information, sanitize, flatten, watermark, header, footer, title, detect fields, JavaScript); remembered between runs."
|
||
|
||
[[feature]]
|
||
id = "misc.action-wizard-manage"
|
||
title = "Import, export, copy and delete actions"
|
||
area = "N"
|
||
tier = "P2"
|
||
milestone = "M13"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.action-prepare-distribution"
|
||
title = "Built-in action: prepare for distribution"
|
||
area = "N"
|
||
tier = "P1"
|
||
milestone = "M13"
|
||
status = "shipped"
|
||
commands = ["actions.distribution"]
|
||
tools = ["action_run"]
|
||
evidence = ["crates/engine/src/tests.rs::actions_run_their_steps_on_files", "crates/automation/tests/automation.rs::actions_through_tools", "crates/ui-egui/tests/actions.rs::run_and_create_actions"]
|
||
notes = "Remove hidden information, flatten comments and fields."
|
||
|
||
[[feature]]
|
||
id = "misc.action-optimize-scans"
|
||
title = "Built-in action: optimize scanned documents"
|
||
area = "N"
|
||
tier = "P1"
|
||
milestone = "M13"
|
||
status = "shipped"
|
||
commands = ["actions.optimize_scans"]
|
||
tools = ["action_run"]
|
||
evidence = ["crates/engine/src/tests.rs::actions_run_their_steps_on_files", "crates/automation/tests/automation.rs::actions_through_tools"]
|
||
notes = "Recognize text, then reduce file size."
|
||
|
||
[[feature]]
|
||
id = "misc.action-archive"
|
||
title = "Built-in action: archive documents"
|
||
area = "N"
|
||
tier = "P1"
|
||
milestone = "M13"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.action-publish-web"
|
||
title = "Built-in action: prepare for web publishing"
|
||
area = "N"
|
||
tier = "P2"
|
||
milestone = "M13"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.batch-processing"
|
||
title = "Batch processing over files and folders"
|
||
area = "N"
|
||
tier = "P1"
|
||
milestone = "M13"
|
||
status = "partial"
|
||
commands = ["actions.wizard", "ocr.recognize_batch"]
|
||
tools = ["action_run", "ocr_recognize_files"]
|
||
evidence = ["crates/automation/tests/automation.rs::actions_through_tools", "crates/ui-egui/tests/actions.rs::run_and_create_actions"]
|
||
notes = "Actions and OCR run over chosen files; whole folders (with subfolders) not yet."
|
||
|
||
[[feature]]
|
||
id = "misc.action-prompt-steps"
|
||
title = "Prompt-user and instruction steps"
|
||
area = "N"
|
||
tier = "P2"
|
||
milestone = "M13"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.action-javascript-step"
|
||
title = "Execute JavaScript step"
|
||
area = "N"
|
||
tier = "P2"
|
||
milestone = "M13"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.custom-commands"
|
||
title = "Custom commands"
|
||
area = "N"
|
||
tier = "P1"
|
||
milestone = "M13"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.custom-tools"
|
||
title = "Custom tool sets"
|
||
area = "N"
|
||
tier = "P2"
|
||
milestone = "M13"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.command-registry"
|
||
title = "One command registry for menus, shortcuts, palette and automation"
|
||
area = "N"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = ["command_list"]
|
||
evidence = ["crates/engine/src/commands.rs::ids_and_shortcuts_are_unique", "crates/ui-egui/tests/commands.rs::every_registered_command_is_implemented"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.disabled-command-reasons"
|
||
title = "Disabled commands explain why"
|
||
area = "N"
|
||
tier = "P1"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/commands.rs::disabled_commands_explain_themselves"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.cli-inspect"
|
||
title = "CLI: inspect, render and extract text"
|
||
area = "N"
|
||
tier = "P0"
|
||
milestone = "M2"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = ["doc_info", "page_render", "text_extract"]
|
||
evidence = ["crates/automation/tests/automation.rs::open_inspect_render_and_find"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.cli-edit"
|
||
title = "CLI: edit, combine, extract, split"
|
||
area = "N"
|
||
tier = "P0"
|
||
milestone = "M4"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = ["page_rotate", "page_delete", "doc_combine", "page_extract", "doc_split"]
|
||
evidence = ["crates/automation/tests/automation.rs::combine_extract_and_split", "crates/automation/tests/automation.rs::edit_undo_redo_and_save_round_trip"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.cli-run"
|
||
title = "CLI run: call any automation tool or a JSON script"
|
||
area = "N"
|
||
tier = "P0"
|
||
milestone = "M13"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/automation/tests/automation.rs::tool_table_is_well_formed", "crates/automation/tests/automation.rs::edit_undo_redo_and_save_round_trip"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.cli-completeness"
|
||
title = "CLI covers every shipped command"
|
||
area = "N"
|
||
tier = "P0"
|
||
milestone = "M13"
|
||
status = "partial"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/automation/tests/automation.rs::command_list_reports_enablement_and_tools"]
|
||
notes = "tools grow as features land"
|
||
|
||
[[feature]]
|
||
id = "misc.automation-tools"
|
||
title = "Headless JSON-Schema tool table"
|
||
area = "N"
|
||
tier = "P0"
|
||
milestone = "M13"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = ["command_list"]
|
||
evidence = ["crates/automation/tests/automation.rs::tool_table_is_well_formed", "crates/automation/tests/automation.rs::errors_are_specific_and_safe"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.command-list-tool"
|
||
title = "List commands with enablement and their tools"
|
||
area = "N"
|
||
tier = "P1"
|
||
milestone = "M13"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = ["command_list"]
|
||
evidence = ["crates/automation/tests/automation.rs::command_list_reports_enablement_and_tools"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.mcp-server"
|
||
title = "Opt-in MCP server over stdio"
|
||
area = "N"
|
||
tier = "P0"
|
||
milestone = "M13"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/automation/tests/automation.rs::mcp_session_over_stdio", "crates/automation/tests/automation.rs::mcp_errors"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.automation-root-confinement"
|
||
title = "Confine automation file access to a root directory"
|
||
area = "N"
|
||
tier = "P0"
|
||
milestone = "M13"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/automation/tests/automation.rs::errors_are_specific_and_safe"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.mcp-resources"
|
||
title = "MCP resources for page images and text"
|
||
area = "N"
|
||
tier = "P1"
|
||
milestone = "M13"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = ["doc_info", "text_extract", "page_render"]
|
||
evidence = ["crates/automation/tests/automation.rs::mcp_resources_expose_open_documents", "crates/automation/src/mcp.rs::resource_uris_parse"]
|
||
notes = "resources/list, resources/templates/list and resources/read: printcraft://doc/{doc}/info (JSON), /text, /page/{page}/text, /page/{page}/image?dpi= (PNG)"
|
||
|
||
[[feature]]
|
||
id = "misc.ui-control-channel"
|
||
title = "UI control channel (inspect, click, type, keys, commands)"
|
||
area = "N"
|
||
tier = "P0"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/control.rs::inspect_and_click_by_label_and_id", "crates/ui-egui/tests/control.rs::commands_keys_and_typing", "crates/ui-egui/tests/control.rs::loopback_transport_requires_the_token"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.ui-screenshots"
|
||
title = "Screenshots of the window or a region via the control channel"
|
||
area = "N"
|
||
tier = "P1"
|
||
milestone = "M3"
|
||
status = "shipped"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/tests/control.rs::screenshots_of_window_and_region"]
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.ai-provider"
|
||
title = "AI provider interface (local model or own endpoint), off by default"
|
||
area = "N"
|
||
tier = "P2"
|
||
milestone = "M13"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.ai-summary"
|
||
title = "Summarize a document"
|
||
area = "N"
|
||
tier = "P2"
|
||
milestone = "M13"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.ai-ask"
|
||
title = "Ask questions with citations to pages"
|
||
area = "N"
|
||
tier = "P2"
|
||
milestone = "M13"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.ai-conversational-edit"
|
||
title = "Natural-language editing through registry commands"
|
||
area = "N"
|
||
tier = "P3"
|
||
milestone = "M13"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.ai-presentation"
|
||
title = "Generate a presentation from documents"
|
||
area = "N"
|
||
tier = "P3"
|
||
milestone = "M13"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.ai-podcast"
|
||
title = "Generate an audio overview"
|
||
area = "N"
|
||
tier = "P3"
|
||
milestone = "M13"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.ai-charts"
|
||
title = "Charts from document data"
|
||
area = "N"
|
||
tier = "P3"
|
||
milestone = "M13"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.contract-analysis"
|
||
title = "Contract key terms and multi-contract compare"
|
||
area = "N"
|
||
tier = "P3"
|
||
milestone = "M13"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.adobe-ai-assistant"
|
||
title = "Adobe AI Assistant"
|
||
area = "N"
|
||
tier = "N/A"
|
||
milestone = "none"
|
||
status = "na"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "Adobe cloud; replaced by the optional local/BYO AiProvider"
|
||
|
||
[[feature]]
|
||
id = "misc.pdf-spaces"
|
||
title = "PDF Spaces"
|
||
area = "N"
|
||
tier = "N/A"
|
||
milestone = "none"
|
||
status = "na"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "Adobe cloud; no local equivalent planned"
|
||
|
||
[[feature]]
|
||
id = "misc.adobe-express"
|
||
title = "Adobe Express integration and generated images"
|
||
area = "N"
|
||
tier = "N/A"
|
||
milestone = "none"
|
||
status = "na"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "Adobe cloud"
|
||
|
||
[[feature]]
|
||
id = "misc.document-cloud-storage"
|
||
title = "Adobe Document Cloud storage"
|
||
area = "N"
|
||
tier = "N/A"
|
||
milestone = "none"
|
||
status = "na"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "Adobe cloud; local files and native dialogs (pluggable storage later)"
|
||
|
||
[[feature]]
|
||
id = "misc.share-links"
|
||
title = "Share links and invitations"
|
||
area = "N"
|
||
tier = "N/A"
|
||
milestone = "none"
|
||
status = "na"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "Adobe cloud; pluggable share provider"
|
||
|
||
[[feature]]
|
||
id = "misc.connected-storage"
|
||
title = "Connected third-party storage accounts in-app"
|
||
area = "N"
|
||
tier = "N/A"
|
||
milestone = "none"
|
||
status = "na"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "Adobe cloud; use OS file dialogs and synced folders"
|
||
|
||
[[feature]]
|
||
id = "misc.cross-device-sync"
|
||
title = "Sync signatures, stamps and preferences across devices"
|
||
area = "N"
|
||
tier = "N/A"
|
||
milestone = "none"
|
||
status = "na"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "Adobe account service"
|
||
|
||
[[feature]]
|
||
id = "misc.adobe-account"
|
||
title = "Adobe account sign-in"
|
||
area = "N"
|
||
tier = "N/A"
|
||
milestone = "none"
|
||
status = "na"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "not applicable; no account needed"
|
||
|
||
[[feature]]
|
||
id = "misc.rich-media-preserve"
|
||
title = "Preserve 3D, video and sound content"
|
||
area = "N"
|
||
tier = "P3"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.rich-media-poster"
|
||
title = "Show posters for rich media"
|
||
area = "N"
|
||
tier = "P3"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.rich-media-playback"
|
||
title = "Play video and sound via the platform"
|
||
area = "N"
|
||
tier = "P3"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.3d-view"
|
||
title = "View 3D (U3D/PRC) content"
|
||
area = "N"
|
||
tier = "P3"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.legacy-multimedia"
|
||
title = "Legacy multimedia (screen annotations, renditions)"
|
||
area = "N"
|
||
tier = "P3"
|
||
milestone = "M12"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.flash-swf"
|
||
title = "Flash/SWF content"
|
||
area = "N"
|
||
tier = "N/A"
|
||
milestone = "none"
|
||
status = "na"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = "Flash is end-of-life; content is preserved but not played"
|
||
|
||
[[feature]]
|
||
id = "misc.preferences-dialog"
|
||
title = "Preferences dialog generated from a schema"
|
||
area = "N"
|
||
tier = "P1"
|
||
milestone = "M14"
|
||
status = "partial"
|
||
commands = ["app.preferences"]
|
||
tools = ["js_enabled"]
|
||
evidence = ["crates/ui-egui/tests/javascript.rs::document_scripts_and_preferences"]
|
||
notes = "Preferences dialog with Interface language, Identity and JavaScript; other categories still live in their tools, and it isn't generated from a schema yet."
|
||
|
||
[[feature]]
|
||
id = "misc.prefs-general"
|
||
title = "Preferences: general"
|
||
area = "N"
|
||
tier = "P1"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.prefs-page-display"
|
||
title = "Preferences: page display"
|
||
area = "N"
|
||
tier = "P1"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.prefs-documents"
|
||
title = "Preferences: documents (autosave interval, recent count)"
|
||
area = "N"
|
||
tier = "P1"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.prefs-commenting"
|
||
title = "Preferences: commenting"
|
||
area = "N"
|
||
tier = "P1"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.prefs-forms"
|
||
title = "Preferences: forms"
|
||
area = "N"
|
||
tier = "P1"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.prefs-identity"
|
||
title = "Preferences: identity"
|
||
area = "N"
|
||
tier = "P1"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.prefs-full-screen"
|
||
title = "Preferences: full screen"
|
||
area = "N"
|
||
tier = "P2"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.prefs-search"
|
||
title = "Preferences: search"
|
||
area = "N"
|
||
tier = "P2"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.prefs-signatures"
|
||
title = "Preferences: signatures"
|
||
area = "N"
|
||
tier = "P1"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.prefs-security"
|
||
title = "Preferences: security and trust"
|
||
area = "N"
|
||
tier = "P1"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.prefs-spelling"
|
||
title = "Preferences: spelling"
|
||
area = "N"
|
||
tier = "P2"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.prefs-language"
|
||
title = "Preferences: language"
|
||
area = "N"
|
||
tier = "P1"
|
||
milestone = "M14"
|
||
status = "partial"
|
||
commands = []
|
||
tools = []
|
||
evidence = ["crates/ui-egui/src/i18n.rs::language_persists_and_invalid_input_keeps_current_language"]
|
||
notes = "Preferences switches English/Japanese and persists; ui.set language en|ja and ui.state expose it. Core menu labels translated; broader dialog/panel translations and additional languages remain open."
|
||
|
||
[[feature]]
|
||
id = "misc.prefs-reading"
|
||
title = "Preferences: reading"
|
||
area = "N"
|
||
tier = "P2"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.prefs-accessibility"
|
||
title = "Preferences: accessibility"
|
||
area = "N"
|
||
tier = "P1"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.prefs-color-management"
|
||
title = "Preferences: colour management"
|
||
area = "N"
|
||
tier = "P1"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.prefs-javascript"
|
||
title = "Preferences: JavaScript"
|
||
area = "N"
|
||
tier = "P1"
|
||
milestone = "M14"
|
||
status = "shipped"
|
||
commands = ["app.preferences"]
|
||
tools = ["js_enabled"]
|
||
evidence = ["crates/ui-egui/tests/javascript.rs::document_scripts_and_preferences"]
|
||
notes = "Enable Acrobat JavaScript, remembered between runs."
|
||
|
||
[[feature]]
|
||
id = "misc.prefs-measuring"
|
||
title = "Preferences: measuring"
|
||
area = "N"
|
||
tier = "P2"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.prefs-units-guides"
|
||
title = "Preferences: units and guides"
|
||
area = "N"
|
||
tier = "P2"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.prefs-content-editing"
|
||
title = "Preferences: content editing"
|
||
area = "N"
|
||
tier = "P2"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.prefs-convert"
|
||
title = "Preferences: convert to and from PDF"
|
||
area = "N"
|
||
tier = "P2"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.prefs-catalog"
|
||
title = "Preferences: catalog / index"
|
||
area = "N"
|
||
tier = "P3"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.admin-preferences"
|
||
title = "Admin-deployable preference defaults"
|
||
area = "N"
|
||
tier = "P2"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.localization"
|
||
title = "Localization (Fluent catalogs)"
|
||
area = "N"
|
||
tier = "P1"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.rtl-ui"
|
||
title = "Right-to-left user interface"
|
||
area = "N"
|
||
tier = "P2"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.installers"
|
||
title = "Signed installers for macOS, Windows and Linux"
|
||
area = "N"
|
||
tier = "P1"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.web-deploy"
|
||
title = "Hosted web app deployment"
|
||
area = "N"
|
||
tier = "P1"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.performance-budgets"
|
||
title = "Meet performance budgets on Tier-1 platforms"
|
||
area = "N"
|
||
tier = "P0"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.send-email"
|
||
title = "Send the file by email (system mail client)"
|
||
area = "N"
|
||
tier = "P3"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|
||
|
||
[[feature]]
|
||
id = "misc.user-docs"
|
||
title = "User documentation and keyboard reference"
|
||
area = "N"
|
||
tier = "P1"
|
||
milestone = "M14"
|
||
status = "planned"
|
||
commands = []
|
||
tools = []
|
||
evidence = []
|
||
notes = ""
|