docs(parity): point evidence at the tests' new files

This commit is contained in:
aiongg
2026-10-26 20:51:19 +00:00
parent da51747654
commit b1a09ee08d
+67 -67
View File
@@ -46,7 +46,7 @@ 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", "crates/engine/src/tests.rs::a_repaired_file_is_rewritten_by_save_even_without_edits", "crates/automation/tests/it/automation.rs::saving_a_repaired_file_puts_the_repair_on_disk", "crates/engine/src/tests.rs::a_signed_repaired_file_keeps_its_bytes_when_saved_without_edits", "crates/cos/src/document.rs::a_table_numbered_from_1_that_begins_with_object_0_is_read_from_0"]
evidence = ["crates/cos/src/document/tests.rs::damaged_xref_is_reconstructed", "crates/cos/src/parser.rs::indirect_stream_with_wrong_length_recovers", "crates/cos/src/document/tests.rs::self_referential_length_does_not_overflow", "crates/engine/src/tests.rs::a_repaired_file_is_rewritten_by_save_even_without_edits", "crates/automation/tests/it/automation.rs::saving_a_repaired_file_puts_the_repair_on_disk", "crates/engine/src/tests.rs::a_signed_repaired_file_keeps_its_bytes_when_saved_without_edits", "crates/cos/src/document/tests.rs::a_table_numbered_from_1_that_begins_with_object_0_is_read_from_0"]
notes = "Save and doc_save write a reconstructed file in full even when nothing was edited, so the repair reaches the disk and the saved file opens without one (#1937); a signed one keeps its bytes, so its signatures stay valid. A repair does not mark the document as having unsaved changes, so closing it does not offer to save the repaired file. A classic table numbered 1 n that begins with object 0's free entry is read from 0 (#2676) and logged."
[[feature]]
@@ -58,7 +58,7 @@ milestone = "M1"
status = "shipped"
commands = []
tools = []
evidence = ["crates/ui-egui/tests/it/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"]
evidence = ["crates/ui-egui/tests/it/ui.rs::garbage_input_is_rejected_without_panicking", "crates/cos/src/parser.rs::garbage_never_panics", "crates/render/src/raster/tests.rs::missing_page_and_garbage_input_fail_gracefully"]
notes = "fuzzed nightly (M1.10)"
[[feature]]
@@ -94,7 +94,7 @@ milestone = "M1"
status = "shipped"
commands = []
tools = ["doc_open"]
evidence = ["crates/cos/src/document.rs::hybrid_reference_files_read_the_xref_stream_too"]
evidence = ["crates/cos/src/document/tests.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]]
@@ -106,7 +106,7 @@ 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"]
evidence = ["crates/cos/src/document/tests.rs::looping_revision_chains_stop", "crates/cos/src/document/tests.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]]
@@ -286,7 +286,7 @@ milestone = "M1"
status = "shipped"
commands = ["file.save"]
tools = ["doc_save"]
evidence = ["crates/platform/src/staging.rs::atomic_saves_keep_finder_tags_and_binary_extended_attributes", "crates/platform/src/staging.rs::a_growing_resource_fork_refuses_publication_and_keeps_original", "crates/automation/tests/it/automation.rs::saving_a_pdf_keeps_the_destinations_extended_attributes", "crates/automation/tests/it/automation.rs::a_metadata_preservation_error_keeps_the_pdf_and_unsaved_edit", "crates/ui-egui/tests/it/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", "crates/automation/src/lib.rs::staging_never_writes_through_a_file_planted_at_its_name", "crates/ui-egui/src/editing.rs::saving_never_writes_through_a_file_planted_at_the_staging_name"]
evidence = ["crates/platform/src/staging/tests.rs::atomic_saves_keep_finder_tags_and_binary_extended_attributes", "crates/platform/src/staging/tests.rs::a_growing_resource_fork_refuses_publication_and_keeps_original", "crates/automation/tests/it/automation.rs::saving_a_pdf_keeps_the_destinations_extended_attributes", "crates/automation/tests/it/automation.rs::a_metadata_preservation_error_keeps_the_pdf_and_unsaved_edit", "crates/ui-egui/tests/it/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", "crates/automation/src/tests.rs::staging_never_writes_through_a_file_planted_at_its_name", "crates/ui-egui/src/editing/tests.rs::saving_never_writes_through_a_file_planted_at_the_staging_name"]
notes = "The atomic write stages to a new, randomly named file created with create_new, so a file or link planted at the staging path is never written through (desktop app, automation and CLI). On macOS, accessible destination xattrs (including Finder tags) are copied before flush/rename; errors, metadata limits or a changed post-read size refuse the save without replacing the file. System-managed provenance is excluded. Permissions, ACLs, hard-link identity and creation dates are not preserved by this change (#2658)."
[[feature]]
@@ -334,7 +334,7 @@ milestone = "M1"
status = "shipped"
commands = []
tools = []
evidence = ["crates/cos/src/document.rs::reads_classic_revision_and_edits_are_copy_on_write"]
evidence = ["crates/cos/src/document/tests.rs::reads_classic_revision_and_edits_are_copy_on_write"]
notes = ""
[[feature]]
@@ -346,7 +346,7 @@ milestone = "M1"
status = "shipped"
commands = []
tools = []
evidence = ["crates/cos/tests/it/encryption.rs::qpdf_agrees", "crates/cos/tests/it/writer.rs::qpdf_accepts_object_stream_output", "crates/platform/src/staging.rs::an_atomic_write_replaces_the_target_only_when_complete", "apps/pdfcraft-cli/tests/it/edit.rs::editing_in_place_leaves_a_valid_file_and_no_staging_file", "crates/automation/tests/it/automation.rs::a_root_less_write_output_is_atomic_too"]
evidence = ["crates/cos/tests/it/encryption.rs::qpdf_agrees", "crates/cos/tests/it/writer.rs::qpdf_accepts_object_stream_output", "crates/platform/src/staging/tests.rs::an_atomic_write_replaces_the_target_only_when_complete", "apps/pdfcraft-cli/tests/it/edit.rs::editing_in_place_leaves_a_valid_file_and_no_staging_file", "crates/automation/tests/it/automation.rs::a_root_less_write_output_is_atomic_too"]
notes = "qpdf is installed on the Linux CI runner and PDFCRAFT_ORACLES_REQUIRED makes its absence a failure, so these run on every CI run rather than skipping; on a machine without qpdf they skip with a notice (crates/oracle). Every PDF leaves the engine checked (it reopens with its pages): saves, new files from Combine/Extract/Split/Create, signed, timestamped and LTV-embedded files, OCR batch output; and every writer goes through one staging-file writer (the desktop app, the tools with or without --root, the CLI), so a failed write never leaves a truncated file (#1698, #2166)"
[[feature]]
@@ -382,7 +382,7 @@ milestone = "M1"
status = "shipped"
commands = ["file.properties"]
tools = ["doc_revisions"]
evidence = ["crates/cos/src/document.rs::revision_ends_split_incremental_updates", "crates/automation/tests/it/automation.rs::digital_ids_signing_and_validation_through_tools"]
evidence = ["crates/cos/src/document/tests.rs::revision_ends_split_incremental_updates", "crates/automation/tests/it/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]]
@@ -734,7 +734,7 @@ milestone = "M2"
status = "partial"
commands = []
tools = ["page_render"]
evidence = ["crates/render/src/raster.rs::renders_a_blue_rectangle", "crates/automation/tests/it/automation.rs::open_inspect_render_and_find", "crates/render/src/raster.rs::user_unit_scales_the_page_and_everything_on_it"]
evidence = ["crates/render/src/raster/tests.rs::renders_a_blue_rectangle", "crates/automation/tests/it/automation.rs::open_inspect_render_and_find", "crates/render/src/raster/tests.rs::user_unit_scales_the_page_and_everything_on_it"]
notes = "hayro bootstrap renderer; own DisplayList devices in M2.6. Pages with /UserUnit are shown at their size in points, as Acrobat reads the value"
[[feature]]
@@ -746,7 +746,7 @@ milestone = "M2"
status = "partial"
commands = []
tools = []
evidence = ["crates/render/src/raster.rs::self_referencing_tiling_pattern_terminates", "crates/render/tests/it/tiling_cell_placement.rs::far_cells_of_a_fill_sit_at_whole_steps", "crates/render/tests/it/tiling_cell_placement.rs::far_cells_sit_at_whole_steps_under_a_turned_scaled_matrix", "crates/render/tests/it/tiling_cell_placement.rs::far_cells_of_a_stroke_sit_at_whole_steps"]
evidence = ["crates/render/src/raster/tests.rs::self_referencing_tiling_pattern_terminates", "crates/render/tests/it/tiling_cell_placement.rs::far_cells_of_a_fill_sit_at_whole_steps", "crates/render/tests/it/tiling_cell_placement.rs::far_cells_sit_at_whole_steps_under_a_turned_scaled_matrix", "crates/render/tests/it/tiling_cell_placement.rs::far_cells_of_a_stroke_sit_at_whole_steps"]
notes = "via hayro bootstrap. Tiling pattern cells sit at whole multiples of /XStep and /YStep at any zoom, filled or stroked, under any pattern matrix. Missing: a cell is cut at its step, so content reaching past the step into the next cell is lost; a cell pixmap is capped at 3000 pixels a side, so very large steps draw at reduced resolution"
[[feature]]
@@ -758,7 +758,7 @@ milestone = "M2"
status = "partial"
commands = []
tools = []
evidence = ["crates/render/src/raster.rs::soft_mask_without_group_colour_space_is_applied", "crates/render/src/raster.rs::a_multiply_highlight_inside_a_transparency_group_keeps_the_text", "crates/render/tests/it/luminosity_group_space.rs::black_ink_in_a_device_cmyk_group_masks_fully_and_no_ink_shows_fully"]
evidence = ["crates/render/src/raster/tests.rs::soft_mask_without_group_colour_space_is_applied", "crates/render/src/raster/tests.rs::a_multiply_highlight_inside_a_transparency_group_keeps_the_text", "crates/render/tests/it/luminosity_group_space.rs::black_ink_in_a_device_cmyk_group_masks_fully_and_no_ink_shows_fully"]
notes = "via hayro bootstrap. A luminosity soft mask whose group is in DeviceCMYK reads its flat CMYK colours and backdrop as their gray, 1 - min(1, 0.3 c + 0.59 m + 0.11 y + k) (ISO 32000-2 §11.5.3), so 100% K masks fully; CMYK images, shadings and tiling patterns inside such a mask still go through the display profile. A transparency group that is neither isolated nor knockout and is entered at full opacity, with no soft mask and Normal blending, is drawn straight onto the page, so a blend mode inside it (the Multiply of a highlight appearance written by other editors) sees the page content (#1968). Missing: a non-isolated group that needs a layer (partial opacity, a soft mask or a blend mode on the group itself) still composites its elements against a blank backdrop, and knockout groups are drawn as ordinary ones"
[[feature]]
@@ -794,7 +794,7 @@ milestone = "M2"
status = "partial"
commands = []
tools = []
evidence = ["crates/render/src/raster.rs::huge_cid_width_ranges_terminate", "crates/render/src/raster.rs::non_embedded_japanese_cid_fonts_draw_with_a_craft_fonts_face", "crates/render/src/raster.rs::mincho_substitute_covers_half_width_katakana", "crates/render/src/text.rs::identity_to_unicode_names_give_text", "crates/fonts/src/pdf.rs::identity_to_unicode_names_read_codes_as_unicode"]
evidence = ["crates/render/src/raster/tests.rs::huge_cid_width_ranges_terminate", "crates/render/src/raster/tests.rs::non_embedded_japanese_cid_fonts_draw_with_a_craft_fonts_face", "crates/render/src/raster/tests.rs::mincho_substitute_covers_half_width_katakana", "crates/render/src/text/tests.rs::identity_to_unicode_names_give_text", "crates/fonts/src/pdf/tests.rs::identity_to_unicode_names_read_codes_as_unicode"]
notes = "CID-to-Unicode tables are M2.4; a non-embedded Adobe-Japan1 font is drawn in a craft-fonts Japanese face (Mincho or Gothic by name, else by the descendant font descriptor's Serif flag; bold Gothic for a Bold name or a `/FontWeight` of 600 or more; BIZ UDMincho for Mincho, which covers half-width katakana) when built with craft-fonts; non-embedded GB1, CNS1 and Korea1 fonts still draw nothing (no allowed face); a `/ToUnicode` given as the name `/Identity-H` or `/Identity-V` (WSCAD) reads codes as Unicode for selection, search and editing, without control characters"
[[feature]]
@@ -818,7 +818,7 @@ milestone = "M2"
status = "shipped"
commands = []
tools = []
evidence = ["crates/render/src/raster.rs::widget_appearance_states", "crates/render/src/raster.rs::highlights_without_an_appearance_are_drawn", "crates/engine/src/tests.rs::comments_without_appearances_are_drawn_but_not_saved", "crates/engine/src/tests.rs::cli_display_adds_missing_appearances_without_changing_the_source", "crates/engine/src/tests.rs::fields_left_to_the_viewer_are_displayed_from_their_values", "crates/forms/src/tests.rs::need_appearances_draws_every_text_and_choice_field_in_the_display_copy", "crates/forms/src/tests.rs::the_field_appearance_cache_sees_every_input", "crates/engine/src/tests.rs::buttons_and_combs_left_to_the_viewer_are_displayed", "crates/forms/src/tests.rs::check_boxes_without_an_appearance_show_their_caption_symbol", "crates/forms/src/tests.rs::push_buttons_without_an_appearance_show_their_caption_and_frame", "crates/forms/src/tests.rs::need_appearances_draws_check_boxes_and_radio_buttons_from_their_captions", "crates/engine/src/tests.rs::need_appearances_buttons_are_displayed_from_their_captions", "crates/render/src/raster.rs::invisible_unknown_annotations_and_popups_paint_nothing", "crates/print/src/tests.rs::invisible_annotations_of_unknown_types_do_not_print", "crates/engine/src/tests.rs::stamps_without_appearances_draw_the_stamp_they_name", "crates/automation/tests/it/automation.rs::page_render_draws_stamps_without_an_appearance"]
evidence = ["crates/render/src/raster/tests.rs::widget_appearance_states", "crates/render/src/raster/tests.rs::highlights_without_an_appearance_are_drawn", "crates/engine/src/tests.rs::comments_without_appearances_are_drawn_but_not_saved", "crates/engine/src/tests.rs::cli_display_adds_missing_appearances_without_changing_the_source", "crates/engine/src/tests.rs::fields_left_to_the_viewer_are_displayed_from_their_values", "crates/forms/src/tests.rs::need_appearances_draws_every_text_and_choice_field_in_the_display_copy", "crates/forms/src/tests.rs::the_field_appearance_cache_sees_every_input", "crates/engine/src/tests.rs::buttons_and_combs_left_to_the_viewer_are_displayed", "crates/forms/src/tests.rs::check_boxes_without_an_appearance_show_their_caption_symbol", "crates/forms/src/tests.rs::push_buttons_without_an_appearance_show_their_caption_and_frame", "crates/forms/src/tests.rs::need_appearances_draws_check_boxes_and_radio_buttons_from_their_captions", "crates/engine/src/tests.rs::need_appearances_buttons_are_displayed_from_their_captions", "crates/render/src/raster/tests.rs::invisible_unknown_annotations_and_popups_paint_nothing", "crates/print/src/tests.rs::invisible_annotations_of_unknown_types_do_not_print", "crates/engine/src/tests.rs::stamps_without_appearances_draw_the_stamp_they_name", "crates/automation/tests/it/automation.rs::page_render_draws_stamps_without_an_appearance"]
notes = "comments without /AP (FreeText, Ink, notes, stamps, shapes, markup) are drawn from their dictionaries in the page view, page_render and page images, including the CLI render command, never saved; a stamp from another program draws the stamp its /Name names (the fourteen standard names of ISO 32000-2 Table 184, Draft when there is none) or, for any other name, a grey frame with both diagonals; text and choice fields are drawn from their values when the form sets /NeedAppearances (every one) or a widget has no /N (#3470), also display-only and cached per widget (a field edit on a 1000-field /NeedAppearances form takes ~13 ms against ~7 ms on a form that needs none) (a read-only document still gets the renderer's Highlight fallback only); check boxes and radio buttons with no appearance for their /AS are drawn display-only from /MK (background, border, and when on the /MK /CA ZapfDingbats symbol in the /DA colour, a check or a dot by default), and when the form sets /NeedAppearances so is every one that has an /MK, over the appearance the file carries (ISO 32000-2 Table 224; one without /MK keeps its own), and push buttons with none from /MK and /BS with their caption; Link borders without /AP and printing are not covered yet; an annotation of a non-standard type with the Invisible flag (ISO 32000-2 Table 167) is neither drawn nor printed, and a popup's own /AP is never painted on the page (its parent's text shows in the popup window, §12.5.6.14)"
[[feature]]
@@ -830,7 +830,7 @@ 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"]
evidence = ["crates/render/src/raster/tests.rs::missing_page_and_garbage_input_fail_gracefully", "crates/render/src/raster/tests.rs::scale_is_clamped_for_huge_pages", "crates/render/src/raster/tests.rs::watchdog_skips_a_stuck_page_and_keeps_rendering"]
notes = ""
[[feature]]
@@ -842,7 +842,7 @@ milestone = "M3"
status = "shipped"
commands = []
tools = []
evidence = ["crates/render/src/raster.rs::inline_pool_renders_in_priority_order"]
evidence = ["crates/render/src/raster/tests.rs::inline_pool_renders_in_priority_order"]
notes = ""
[[feature]]
@@ -854,7 +854,7 @@ 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", "crates/ui-egui/tests/it/view.rs::returning_to_a_visited_zoom_reuses_its_cached_tiles", "crates/ui-egui/tests/it/view.rs::zooming_a_tiled_page_shows_its_earlier_tiles_until_new_ones_arrive"]
evidence = ["crates/render/src/raster/tests.rs::tiles_match_full_render", "crates/render/src/raster/tests.rs::effective_scale_never_exceeds_the_caps", "crates/ui-egui/tests/it/view.rs::returning_to_a_visited_zoom_reuses_its_cached_tiles", "crates/ui-egui/tests/it/view.rs::zooming_a_tiled_page_shows_its_earlier_tiles_until_new_ones_arrive"]
notes = "capped at 8192 px per side (M3.3); tiles of a zoom already rendered stay cached while their page is on screen, so zooming away and back shows the area sharp instead of re-rendering it tile by tile (#2196)"
[[feature]]
@@ -866,7 +866,7 @@ milestone = "M3"
status = "shipped"
commands = ["view.continuous", "view.page_gaps"]
tools = []
evidence = ["crates/ui-egui/tests/it/view.rs::page_gaps_in_organizer_do_not_move_the_document_scroll_position", "crates/ui-egui/tests/it/view.rs::page_gaps_comparison_menu_preserves_the_active_panes_choice", "crates/ui-egui/tests/it/view.rs::page_gaps_comparison_control_preserves_both_states", "crates/ui-egui/tests/it/view.rs::page_gaps_stay_closed_at_fractional_device_scales", "crates/ui-egui/tests/it/view.rs::page_gaps_toggle_joins_edges_without_changing_the_document", "crates/ui-egui/tests/it/view.rs::page_gaps_default_survives_restart_and_applies_to_new_documents", "crates/ui-egui/tests/it/view.rs::page_gaps_menu_joins_pages_without_a_shadow_at_the_seam", "crates/ui-egui/tests/it/view.rs::page_gaps_work_for_mixed_sizes_and_two_page_rows", "crates/ui-egui/tests/it/view.rs::continuous_layout_stacks_pages_vertically", "crates/ui-egui/tests/it/view.rs::page_display_commands_switch_layouts", "crates/ui-egui/tests/it/view.rs::default_page_display_is_used_for_new_documents", "crates/ui-egui/src/canvas.rs::fit_page_and_height_hold_still_while_mixed_page_sizes_scroll_past", "crates/ui-egui/tests/it/view.rs::touchpad_swipe_glides_on_after_the_fingers_lift_and_stops", "crates/ui-egui/tests/it/view.rs::touching_the_touchpad_again_or_turning_animations_off_stops_the_glide", "crates/ui-egui/tests/it/view.rs::rapid_wheel_notches_accelerate_and_follow_touchpad_input", "crates/ui-egui/tests/it/view.rs::changing_continuous_layout_resets_wheel_acceleration"]
evidence = ["crates/ui-egui/tests/it/view.rs::page_gaps_in_organizer_do_not_move_the_document_scroll_position", "crates/ui-egui/tests/it/view.rs::page_gaps_comparison_menu_preserves_the_active_panes_choice", "crates/ui-egui/tests/it/view.rs::page_gaps_comparison_control_preserves_both_states", "crates/ui-egui/tests/it/view.rs::page_gaps_stay_closed_at_fractional_device_scales", "crates/ui-egui/tests/it/view.rs::page_gaps_toggle_joins_edges_without_changing_the_document", "crates/ui-egui/tests/it/view.rs::page_gaps_default_survives_restart_and_applies_to_new_documents", "crates/ui-egui/tests/it/view.rs::page_gaps_menu_joins_pages_without_a_shadow_at_the_seam", "crates/ui-egui/tests/it/view.rs::page_gaps_work_for_mixed_sizes_and_two_page_rows", "crates/ui-egui/tests/it/view.rs::continuous_layout_stacks_pages_vertically", "crates/ui-egui/tests/it/view.rs::page_display_commands_switch_layouts", "crates/ui-egui/tests/it/view.rs::default_page_display_is_used_for_new_documents", "crates/ui-egui/src/canvas/tests.rs::fit_page_and_height_hold_still_while_mixed_page_sizes_scroll_past", "crates/ui-egui/tests/it/view.rs::touchpad_swipe_glides_on_after_the_fingers_lift_and_stops", "crates/ui-egui/tests/it/view.rs::touching_the_touchpad_again_or_turning_animations_off_stops_the_glide", "crates/ui-egui/tests/it/view.rs::rapid_wheel_notches_accelerate_and_follow_touchpad_input", "crates/ui-egui/tests/it/view.rs::changing_continuous_layout_resets_wheel_acceleration"]
notes = "Page gaps, shadows and outlines can be hidden through the page-display menu, with a persisted default in Preferences and matching UI control options; page margins in the PDF are unchanged. Continuous scrolling remains the default for PDFs that don't ask for a layout (Preferences ▸ Default page display); pages stack with no snapping. Fit page and Fit height fit the largest page, so the zoom holds still across mixed page sizes. Rapid line-wheel notches gain a bounded, constant-space arrival-rate boost (#1072); isolated notches still use the chosen Scroll speed, and page units stay unboosted. A touchpad swipe glides on after the fingers lift on Wayland (#531; macOS glides by itself; Windows and X11 report no lift through winit), unless Animation effects is off; a click, key, zoom or new scroll catches it. Not yet: fingers resting on the touchpad don't catch it (needs Wayland hold gestures, which the vendored winit doesn't bind)"
[[feature]]
@@ -1190,7 +1190,7 @@ milestone = "M3"
status = "shipped"
commands = []
tools = ["link_open"]
evidence = ["crates/ui-egui/tests/it/view.rs::clicking_a_link_goes_to_its_destination", "crates/ui-egui/tests/it/view.rs::link_destinations_scroll_to_their_position", "crates/ui-egui/tests/it/view.rs::a_link_without_a_position_keeps_the_place_on_the_next_page", "crates/ui-egui/src/canvas.rs::a_null_xyz_coordinate_keeps_the_readers_place_on_the_new_page", "crates/render/src/inspect.rs::bookmarks_and_links_keep_their_destination_view", "crates/render/src/inspect.rs::links_read_bounded_local_remote_go_to_actions", "crates/automation/tests/it/automation.rs::local_gotor_link_open_applies_destinations_and_honors_root", "crates/ui-egui/tests/it/document_links.rs::a_local_gotor_link_opens_in_a_tab_and_applies_its_destination", "crates/ui-egui/tests/it/document_links.rs::an_absolute_gotor_waits_for_consent_and_keeps_its_destination_across_tabs", "crates/ui-egui/tests/it/document_links.rs::cancelling_an_absolute_gotor_by_button_escape_or_dismissal_changes_nothing", "crates/automation/tests/it/automation.rs::absolute_gotor_consent_does_not_bypass_validation_or_passwords", "crates/render/src/inspect.rs::launch_actions_that_name_a_pdf_open_it_like_a_remote_link", "crates/automation/tests/it/automation.rs::a_launch_link_to_a_pdf_opens_it_like_a_remote_link"]
evidence = ["crates/ui-egui/tests/it/view.rs::clicking_a_link_goes_to_its_destination", "crates/ui-egui/tests/it/view.rs::link_destinations_scroll_to_their_position", "crates/ui-egui/tests/it/view.rs::a_link_without_a_position_keeps_the_place_on_the_next_page", "crates/ui-egui/src/canvas/tests.rs::a_null_xyz_coordinate_keeps_the_readers_place_on_the_new_page", "crates/render/src/inspect/tests.rs::bookmarks_and_links_keep_their_destination_view", "crates/render/src/inspect/tests.rs::links_read_bounded_local_remote_go_to_actions", "crates/automation/tests/it/automation.rs::local_gotor_link_open_applies_destinations_and_honors_root", "crates/ui-egui/tests/it/document_links.rs::a_local_gotor_link_opens_in_a_tab_and_applies_its_destination", "crates/ui-egui/tests/it/document_links.rs::an_absolute_gotor_waits_for_consent_and_keeps_its_destination_across_tabs", "crates/ui-egui/tests/it/document_links.rs::cancelling_an_absolute_gotor_by_button_escape_or_dismissal_changes_nothing", "crates/automation/tests/it/automation.rs::absolute_gotor_consent_does_not_bypass_validation_or_passwords", "crates/render/src/inspect/tests.rs::launch_actions_that_name_a_pdf_open_it_like_a_remote_link", "crates/automation/tests/it/automation.rs::a_launch_link_to_a_pdf_opens_it_like_a_remote_link"]
notes = "GoTo and URI links plus local-PDF GoToR links: source-relative or local absolute /F paths, /UF preferred in file-spec dictionaries, explicit numeric page/view destinations, and internal tabs. Absolute local GoToR targets require desktop Allow/Cancel confirmation or an explicit allow_absolute opt-in in link_open; relative links open normally. link_open honors --root. Named remote destinations open at default view with a warning; /NewWindow is a tab hint. A Launch action whose /F names a .pdf file (and has no /Win launch parameters) opens that PDF the same way, backslash separators read as / (#4168); URL/network file systems, GoToE, other Launch actions and other actions are not executed. Same-document destinations retain their supported /XYZ, /Fit, /FitH, /FitV and /FitR views; /FitB* is read as whole-page fit. A null /XYZ left or top keeps the reader's place, as ISO 32000-2 §12.3.2.2 says and Acrobat Reader was observed to do (synthetic fixture): the user-space point at the window's top-left stays there on the target page at the same zoom, along each axis the page overflows; CAD exports (WSCAD) write every cross-reference this way. Null /FitH top and /FitV left still go to the page edge."
[[feature]]
@@ -1214,7 +1214,7 @@ milestone = "M3"
status = "shipped"
commands = []
tools = []
evidence = ["crates/ui-egui/tests/it/control.rs::page_size_shows_while_the_pointer_is_in_the_bottom_left_quadrant", "crates/ui-egui/src/canvas.rs::page_size_hint_zone_is_the_bottom_left_quadrant"]
evidence = ["crates/ui-egui/tests/it/control.rs::page_size_shows_while_the_pointer_is_in_the_bottom_left_quadrant", "crates/ui-egui/src/canvas/tests.rs::page_size_hint_zone_is_the_bottom_left_quadrant"]
notes = "While the pointer is in the bottom-left quadrant of the page view, a small label shows the current page's size (after /Rotate and /UserUnit) in the Preferences unit, formatted as in Document Properties (#1855). It never takes clicks and hides when the pointer leaves. Acrobat's trigger is the corner itself and may differ in exact area and wording (not compared with real Acrobat); the size of the current page only, not of the page under the pointer; no headless tool (page sizes are in document info)."
[[feature]]
@@ -1322,7 +1322,7 @@ milestone = "M3"
status = "shipped"
commands = []
tools = ["doc_info"]
evidence = ["crates/render/src/inspect.rs::attachments_of_both_kinds_are_listed_and_extracted", "crates/ui-egui/tests/it/attachment_row.rs::the_right_click_menu_opens_saves_and_deletes", "crates/ui-egui/tests/it/attachment_row.rs::double_clicking_a_pdf_attachment_opens_it_in_a_new_tab"]
evidence = ["crates/render/src/inspect/tests.rs::attachments_of_both_kinds_are_listed_and_extracted", "crates/ui-egui/tests/it/attachment_row.rs::the_right_click_menu_opens_saves_and_deletes", "crates/ui-egui/tests/it/attachment_row.rs::double_clicking_a_pdf_attachment_opens_it_in_a_new_tab"]
notes = "Each row has Save, Open in a new tab (PDFs) and Delete (the document's own attachments) buttons, the same items on a right-click, and a double-click that opens a PDF in a new tab or saves any other file (#3466). Not yet: Acrobat's Search attachments, and opening a non-PDF attachment in another program (it is saved instead)."
[[feature]]
@@ -1334,7 +1334,7 @@ milestone = "M3"
status = "shipped"
commands = []
tools = ["doc_info", "layer_set"]
evidence = ["crates/render/src/raster.rs::layer_override_hides_content", "crates/engine/src/tests.rs::layer_choices_survive_edits", "crates/automation/tests/it/automation.rs::layers_and_hidden_comments_change_what_page_render_draws"]
evidence = ["crates/render/src/raster/tests.rs::layer_override_hides_content", "crates/engine/src/tests.rs::layer_choices_survive_edits", "crates/automation/tests/it/automation.rs::layers_and_hidden_comments_change_what_page_render_draws"]
notes = "layer_set shows or hides a layer by name (every group with that name, as CAD exporters repeat names) or by position, and page_render draws with the document's layer visibility. Still missing: Reset to initial visibility (view.layers-reset), layer properties, and /OC on annotations (#2176)."
[[feature]]
@@ -1346,7 +1346,7 @@ milestone = "M12"
status = "partial"
commands = []
tools = ["doc_info"]
evidence = ["crates/engine/src/tests.rs::reset_layers_returns_to_the_files_visibility", "crates/render/src/inspect.rs::initial_layer_visibility_is_the_default_configuration"]
evidence = ["crates/engine/src/tests.rs::reset_layers_returns_to_the_files_visibility", "crates/render/src/inspect/tests.rs::initial_layer_visibility_is_the_default_configuration"]
notes = "Layers panel ▸ Layer options ▸ Reset to Initial Visibility shows each layer as the default configuration (/OCProperties /D) has it; doc_info reports it as `initial`. Not yet: listing the layers of all pages or the current page only."
[[feature]]
@@ -1394,7 +1394,7 @@ milestone = "M3"
status = "shipped"
commands = []
tools = ["doc_info"]
evidence = ["crates/render/src/inspect.rs::roman_and_alpha_labels", "crates/automation/tests/it/automation.rs::open_inspect_render_and_find"]
evidence = ["crates/render/src/inspect/tests.rs::roman_and_alpha_labels", "crates/automation/tests/it/automation.rs::open_inspect_render_and_find"]
notes = ""
[[feature]]
@@ -1406,7 +1406,7 @@ milestone = "M3"
status = "shipped"
commands = ["edit.find"]
tools = ["text_find"]
evidence = ["crates/ui-egui/tests/it/ui.rs::find_counts_matches_across_pages", "crates/ui-egui/tests/it/ui.rs::find_reports_no_matches", "crates/automation/tests/it/automation.rs::open_inspect_render_and_find", "crates/render/src/text.rs::identity_v_columns_are_searchable_in_reading_order", "crates/render/src/text.rs::toc_leaders_do_not_hide_word_gaps", "crates/render/src/text.rs::short_tracked_ellipses_remain_searchable"]
evidence = ["crates/ui-egui/tests/it/ui.rs::find_counts_matches_across_pages", "crates/ui-egui/tests/it/ui.rs::find_reports_no_matches", "crates/automation/tests/it/automation.rs::open_inspect_render_and_find", "crates/render/src/text/tests.rs::identity_v_columns_are_searchable_in_reading_order", "crates/render/src/text/tests.rs::toc_leaders_do_not_hide_word_gaps", "crates/render/src/text/tests.rs::short_tracked_ellipses_remain_searchable"]
notes = "Horizontal and WMode 1 vertical text are grouped independently, including a horizontal heading above right-to-left vertical columns. Dotted TOC leaders preserve word spaces for phrase search (#3086), while short tracked ellipses remain intact and searchable; sparse lines and condensed OCR gaps below the existing height-based threshold remain limitations. Complex interleaved flows still use a geometric reading-order heuristic."
[[feature]]
@@ -1538,7 +1538,7 @@ milestone = "M3"
status = "shipped"
commands = ["edit.column_select"]
tools = ["text_extract"]
evidence = ["crates/render/src/text.rs::a_column_selection_takes_one_table_column", "crates/render/src/text.rs::a_column_selection_across_columns_reads_row_by_row", "crates/render/src/text.rs::right_to_left_rows_read_right_to_left", "crates/render/src/text.rs::vertical_lines_keep_reading_order", "crates/ui-egui/tests/it/column_select.rs::alt_drag_selects_only_the_text_inside_the_rectangle", "crates/ui-egui/tests/it/column_select.rs::the_column_select_tool_needs_no_modifier", "crates/ui-egui/tests/it/column_select.rs::alt_drag_with_the_highlighter_marks_only_the_column", "crates/automation/tests/it/automation.rs::text_extract_with_a_rect_takes_one_column"]
evidence = ["crates/render/src/text/tests.rs::a_column_selection_takes_one_table_column", "crates/render/src/text/tests.rs::a_column_selection_across_columns_reads_row_by_row", "crates/render/src/text/tests.rs::right_to_left_rows_read_right_to_left", "crates/render/src/text/tests.rs::vertical_lines_keep_reading_order", "crates/ui-egui/tests/it/column_select.rs::alt_drag_selects_only_the_text_inside_the_rectangle", "crates/ui-egui/tests/it/column_select.rs::the_column_select_tool_needs_no_modifier", "crates/ui-egui/tests/it/column_select.rs::alt_drag_with_the_highlighter_marks_only_the_column", "crates/automation/tests/it/automation.rs::text_extract_with_a_rect_takes_one_column"]
notes = "Issue #740. Holding Alt (Option on macOS) when a drag starts selects only the text inside the rectangle drawn, with the Select tool, the text markup tools and Redact (with Highlight and Redact the drag must start on text, since off text they draw an area box). Edit ▸ Column select does the same with a plain drag. A glyph counts when its centre is inside. Copy gives one row per visual line, top to bottom; pieces side by side (table cells) are separated by a tab, right to left in right-to-left rows; vertical text keeps reading order. text_extract takes the same rectangle as rect. Platforms: on Linux desktops whose window manager moves windows with Alt-drag (Xfce, Cinnamon and MATE by default; KDE Plasma 5 and other setups with Alt as the window modifier; GNOME and Plasma 6 use Super) the drag never reaches PdfCraft, so use Edit ▸ Column select or change that setting. On Windows, releasing Alt does not open a system menu (the window has no native menu). The web build has the same Linux caveat in the browser. Checked by hand on Linux (Fedora) with the Column select tool; Alt/Option-drag, and macOS and Windows generally, rest on the kittest tests. Missing: selection across pages, ⇧-click extending a column, a default shortcut, and a check of Acrobat's own copied-text format (not observed)."
[[feature]]
@@ -1562,7 +1562,7 @@ milestone = "M3"
status = "shipped"
commands = []
tools = []
evidence = ["crates/ui-egui/tests/it/viewer.rs::view_history_select_all_and_find_options", "crates/ui-egui/src/canvas.rs::select_all_copies_the_whole_document_while_scrolling_continuously", "crates/ui-egui/src/canvas.rs::select_all_of_the_document_only_while_scrolling_through_several_pages"]
evidence = ["crates/ui-egui/tests/it/viewer.rs::view_history_select_all_and_find_options", "crates/ui-egui/src/canvas/tests.rs::select_all_copies_the_whole_document_while_scrolling_continuously", "crates/ui-egui/src/canvas/tests.rs::select_all_of_the_document_only_while_scrolling_through_several_pages"]
notes = "⌘A: the current page one page at a time; the whole document scrolling continuously (#2639), copied whole"
[[feature]]
@@ -1670,7 +1670,7 @@ milestone = "M3"
status = "shipped"
commands = ["view.theme", "view.theme.light", "view.theme.dark"]
tools = []
evidence = ["crates/ui-egui/tests/it/theme.rs::toolbar_and_view_menu_share_three_state_preferences", "crates/ui-egui/src/theme.rs::text_is_readable_on_every_surface"]
evidence = ["crates/ui-egui/tests/it/theme.rs::toolbar_and_view_menu_share_three_state_preferences", "crates/ui-egui/src/theme/tests.rs::text_is_readable_on_every_surface"]
notes = "Toolbar and Display theme share three choices and a preference setter; manual choices exit system mode. Every text colour meets WCAG AA (4.5:1) on every surface in both themes; glyphs are pixel-aligned for sharper text at 100–150 % scaling."
[[feature]]
@@ -1878,7 +1878,7 @@ milestone = "M7"
status = "partial"
commands = ["edit.edit_text"]
tools = ["text_lines", "text_paragraphs", "text_edit", "text_edit_layout", "text_font_files", "text_font_import", "text_fonts"]
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/it/editing.rs::editing_existing_text_in_place", "crates/automation/tests/it/automation.rs::editing_existing_text_through_tools", "crates/edit/src/tests.rs::a_line_whose_operator_starts_the_next_stream_can_be_edited", "crates/automation/tests/it/automation.rs::a_line_split_across_content_streams_is_listed_and_edited", "crates/ui-egui/tests/it/editing.rs::selected_text_formatting_keeps_the_rest_of_the_paragraph_and_reopens", "crates/edit/src/tests.rs::marked_paragraph_boundaries_and_cross_stream_transforms_survive_rewrite", "crates/automation/tests/it/automation.rs::explicit_font_and_disjoint_ranges_save_reopen_through_tools", "crates/edit/src/tests.rs::disjoint_style_ranges_preserve_unselected_runs_and_validate_utf8_atomically", "crates/engine/tests/it/document_fonts.rs::opentype_cff_document_font_preserves_program_codes_and_rendering_after_reopen", "crates/ui-egui/tests/it/editing.rs::cropped_images_do_not_intercept_text_and_stay_inside_each_page", "crates/automation/tests/it/automation.rs::clipped_image_bounds_and_text_edit_save_reopen_through_tools", "crates/edit/src/tests.rs::mixed_styles_and_matrix_sizes_form_visual_regions_without_merging_columns", "crates/edit/src/tests.rs::rich_region_edits_preserve_styles_positions_and_delete_all_runs_after_reopen", "crates/ui-egui/tests/it/editing.rs::one_editor_contains_mixed_styles_and_visual_rows", "crates/ui-egui/tests/it/editing.rs::inline_editor_preserves_source_font_and_previews_live_bounds", "crates/fonts/src/pdf.rs::mac_subset_cmap_uses_pdf_unicode_without_changing_source_program", "crates/automation/tests/it/automation.rs::mixed_visual_region_edit_preview_save_reopen_and_undo_through_tools", "crates/ui-egui/tests/it/ui.rs::the_edit_tab_turns_on_edit_text_and_images", "crates/ui-egui/tests/it/editing.rs::the_caret_on_a_small_row_is_as_tall_as_that_row", "crates/edit/src/tests.rs::uzbek_and_kazakh_cyrillic_use_the_bundled_latin_face", "crates/automation/tests/it/automation.rs::editing_uzbek_and_kazakh_cyrillic_through_tools", "crates/edit/src/tests.rs::styled_paragraph_batches_cyrillic_without_changing_source_fonts", "crates/edit/src/tests.rs::styled_paragraph_over_240_fallback_glyphs_splits_and_refuses_atomically", "crates/edit/src/tests.rs::styled_fallback_batches_keep_face_choices_and_range_styles", "crates/edit/src/tests.rs::styled_fallback_shards_keep_text_advances_and_alignment", "crates/edit/src/tests.rs::styled_fallback_budget_is_enforced_before_objects_are_written", "crates/edit/src/tests.rs::styled_fallback_same_base_font_and_character_keep_both_weights", "crates/automation/tests/it/automation.rs::styled_cyrillic_fallback_is_batched_through_tools_and_reopen"]
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/it/editing.rs::editing_existing_text_in_place", "crates/automation/tests/it/automation.rs::editing_existing_text_through_tools", "crates/edit/src/tests.rs::a_line_whose_operator_starts_the_next_stream_can_be_edited", "crates/automation/tests/it/automation.rs::a_line_split_across_content_streams_is_listed_and_edited", "crates/ui-egui/tests/it/editing.rs::selected_text_formatting_keeps_the_rest_of_the_paragraph_and_reopens", "crates/edit/src/tests.rs::marked_paragraph_boundaries_and_cross_stream_transforms_survive_rewrite", "crates/automation/tests/it/automation.rs::explicit_font_and_disjoint_ranges_save_reopen_through_tools", "crates/edit/src/tests.rs::disjoint_style_ranges_preserve_unselected_runs_and_validate_utf8_atomically", "crates/engine/tests/it/document_fonts.rs::opentype_cff_document_font_preserves_program_codes_and_rendering_after_reopen", "crates/ui-egui/tests/it/editing.rs::cropped_images_do_not_intercept_text_and_stay_inside_each_page", "crates/automation/tests/it/automation.rs::clipped_image_bounds_and_text_edit_save_reopen_through_tools", "crates/edit/src/tests.rs::mixed_styles_and_matrix_sizes_form_visual_regions_without_merging_columns", "crates/edit/src/tests.rs::rich_region_edits_preserve_styles_positions_and_delete_all_runs_after_reopen", "crates/ui-egui/tests/it/editing.rs::one_editor_contains_mixed_styles_and_visual_rows", "crates/ui-egui/tests/it/editing.rs::inline_editor_preserves_source_font_and_previews_live_bounds", "crates/fonts/src/pdf/tests.rs::mac_subset_cmap_uses_pdf_unicode_without_changing_source_program", "crates/automation/tests/it/automation.rs::mixed_visual_region_edit_preview_save_reopen_and_undo_through_tools", "crates/ui-egui/tests/it/ui.rs::the_edit_tab_turns_on_edit_text_and_images", "crates/ui-egui/tests/it/editing.rs::the_caret_on_a_small_row_is_as_tall_as_that_row", "crates/edit/src/tests.rs::uzbek_and_kazakh_cyrillic_use_the_bundled_latin_face", "crates/automation/tests/it/automation.rs::editing_uzbek_and_kazakh_cyrillic_through_tools", "crates/edit/src/tests.rs::styled_paragraph_batches_cyrillic_without_changing_source_fonts", "crates/edit/src/tests.rs::styled_paragraph_over_240_fallback_glyphs_splits_and_refuses_atomically", "crates/edit/src/tests.rs::styled_fallback_batches_keep_face_choices_and_range_styles", "crates/edit/src/tests.rs::styled_fallback_shards_keep_text_advances_and_alignment", "crates/edit/src/tests.rs::styled_fallback_budget_is_enforced_before_objects_are_written", "crates/edit/src/tests.rs::styled_fallback_same_base_font_and_character_keep_both_weights", "crates/automation/tests/it/automation.rs::styled_cyrillic_fallback_is_batched_through_tools_and_reopen"]
notes = "The Edit tab turns Edit text & images on at once, as in Acrobat (leaving the tab puts the tool down and applies an edit in progress); click a paragraph box and retype it in place (font reused or Helvetica substituted, colour and spacing kept, rewrapped); a page's /Contents pieces are parsed joined, so a line whose operands and operator sit in different pieces is listed and edited (lines and paragraphs still don't join across pieces; Edit image still parses each piece alone); drag a box to move it or its right-edge handle to rewrap it (edit.text-move-resize); per-character formatting changes are next Since #1116: mixed-style visual regions retain source rows, subset encodings, colour and baseline anchors in one textless-background editor. Explicit paragraph/heading scopes constrain grouping; compatible graphics planes permit cross-stream regions. Full matching document fonts complete absent subset glyphs; installed/file selection, TrueType/OpenType-CFF embedding and disjoint UTF-8 range styles have save/reopen coverage. Single-face OpenType shaping/bidi retains logical text through ToUnicode/ActualText. Replacement text the paragraph's font can't show takes the craft-fonts Japanese faces when they draw all of it (Uzbek қ, Kazakh ә are in none of them), else the bundled Inter face, so those letters write in every build (#2593). Legacy non-SFNT preview, mixed-font/style contextual runs, full structure-tree preservation and author intent in untagged PDFs remain limited; Styled-path outline fallback batches by actual face with 240 glyphs per font and preserves source styles; adjacent equal-font/style characters form runs. At most 15360 distinct fallback glyphs per styled paragraph; missing glyphs are refused before fallback objects are written. Glyph coverage, mixed contextual shaping, single-style fallback and stale-font cleanup are unchanged; see docs/editing-existing-text.md"
[[feature]]
@@ -1926,7 +1926,7 @@ 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/edit/src/tests.rs::a_bold_stand_in_font_leaves_an_earlier_regular_one_alone", "crates/fonts/src/pdf.rs::subset_fonts_only_encode_the_glyphs_they_have", "crates/fonts/src/pdf.rs::composite_fonts_use_to_unicode"]
evidence = ["crates/edit/src/tests.rs::missing_glyphs_substitute_helvetica_and_impossible_text_is_refused", "crates/edit/src/tests.rs::a_bold_stand_in_font_leaves_an_earlier_regular_one_alone", "crates/fonts/src/pdf/tests.rs::subset_fonts_only_encode_the_glyphs_they_have", "crates/fonts/src/pdf/tests.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 a standard face of the source font's family (Helvetica, Times or Courier, in its weight and slant; WinAnsi) is used and reported; text no font can show is refused. Each standard stand-in has a resource of its own face (PCEdTimesBold, ...), so a bold paragraph's stand-in no longer turns paragraphs set earlier in a regular one bold (#4280); pages already saved with the shared PCEdHelv name by older builds are not repaired"
[[feature]]
@@ -2351,7 +2351,7 @@ milestone = "M12"
status = "partial"
commands = []
tools = []
evidence = ["crates/ui-egui/tests/it/layer_actions.rs::links_set_layer_visibility", "crates/ui-egui/tests/it/layer_actions.rs::push_buttons_set_layer_visibility", "crates/engine/src/tests.rs::set_layer_actions_change_what_renders", "crates/engine/src/tests.rs::set_layer_actions_toggle_in_order_and_keep_radio_groups", "crates/render/src/inspect.rs::links_read_set_layer_actions", "crates/forms/src/tests.rs::push_buttons_read_set_layer_actions"]
evidence = ["crates/ui-egui/tests/it/layer_actions.rs::links_set_layer_visibility", "crates/ui-egui/tests/it/layer_actions.rs::push_buttons_set_layer_visibility", "crates/engine/src/tests.rs::set_layer_actions_change_what_renders", "crates/engine/src/tests.rs::set_layer_actions_toggle_in_order_and_keep_radio_groups", "crates/render/src/inspect/tests.rs::links_read_set_layer_actions", "crates/forms/src/tests.rs::push_buttons_read_set_layer_actions"]
notes = "Links and push buttons run it: ON, OFF and Toggle in order, by optional content group, and with /PreserveRB (the default) a layer turned on turns off the rest of its /RBGroups group. Like the Layers panel it changes the view, not the document (no undo step, not saved). Not yet: creating the action (Link Properties, a field's Actions), bookmarks and other triggers, /Next chains, JavaScript ocg.state, and the Layers panel keeping radio-button groups itself."
[[feature]]
@@ -2847,7 +2847,7 @@ 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/it/editing.rs::replace_pages_dialog_swaps_page_content", "crates/ui-egui/tests/it/editing.rs::replace_button_replaces_selected_pages_in_organize", "crates/automation/tests/it/automation.rs::replacing_pages_through_tools", "crates/ui-egui/src/canvas.rs::replacing_a_page_after_layout_never_uploads_an_oversized_texture", "crates/ui-egui/src/canvas.rs::oversized_raster_results_do_not_replace_cached_textures"]
evidence = ["crates/organize/src/tests.rs::replace_pages_swaps_content_but_keeps_annotations", "crates/ui-egui/tests/it/editing.rs::replace_pages_dialog_swaps_page_content", "crates/ui-egui/tests/it/editing.rs::replace_button_replaces_selected_pages_in_organize", "crates/automation/tests/it/automation.rs::replacing_pages_through_tools", "crates/ui-egui/src/canvas/tests.rs::replacing_a_page_after_layout_never_uploads_an_oversized_texture", "crates/ui-egui/src/canvas/tests.rs::oversized_raster_results_do_not_replace_cached_textures"]
notes = "Content, resources and boxes replaced; links, comments, fields and bookmarks kept. Page-list edits discard demand laid out against the previous geometry; the next frame plans bounded backdrops and tiles. Raster uploads reject empty images and dimensions beyond the current GPU texture limit before replacing cached textures (#3149). This covers the viewer's page, tile, thumbnail, organize and print-preview result paths; it does not change the renderer's output resolution or add a global GPU memory budget."
[[feature]]
@@ -3051,7 +3051,7 @@ milestone = "M4"
status = "shipped"
commands = ["page.combine"]
tools = ["doc_combine"]
evidence = ["crates/ui-egui/tests/it/combine_grid.rs::combine_shows_a_grid_by_default_and_the_view_is_kept_in_the_settings", "crates/ui-egui/tests/it/combine_grid.rs::cards_select_like_the_list_and_the_selection_survives_switching_views", "crates/ui-egui/tests/it/combine_grid.rs::dragging_a_card_moves_it_and_undo_puts_it_back", "crates/ui-egui/tests/it/combine_grid.rs::a_dragged_selection_moves_together_to_the_first_and_last_gaps", "crates/ui-egui/tests/it/combine_grid.rs::thumbnails_show_the_first_page_each_file_takes", "crates/ui-egui/tests/it/combine_grid.rs::many_files_render_only_what_is_in_view_a_few_at_a_time", "crates/ui-egui/tests/it/combine_grid.rs::long_thin_pages_get_thumbnails_within_the_size_limits", "crates/ui-egui/tests/it/combine_grid.rs::combine_follows_the_order_shown_in_the_grid", "crates/ui-egui/src/combine_grid.rs::render_threads_never_exceed_three_while_files_come_and_go", "crates/ui-egui/src/combine_grid.rs::textures_over_budget_go_longest_unseen_first_and_those_in_view_stay", "crates/ui-egui/src/combine_grid.rs::on_the_web_one_thumbnail_waits_at_a_time", "crates/ui-egui/tests/it/combine_grid.rs::a_file_whose_pages_cant_be_measured_says_there_is_no_preview", "crates/ui-egui/tests/it/combine_grid.rs::the_toolbar_sizes_the_cards_within_limits_and_the_size_is_kept_in_the_settings", "crates/ui-egui/src/combine_grid.rs::the_cards_zoom_far_enough_to_read_a_value", "crates/ui-egui/tests/it/combine_grid.rs::ctrl_keys_and_ctrl_wheel_size_the_cards_and_leave_the_window_alone", "crates/ui-egui/tests/it/combine_grid.rs::zooming_keeps_the_card_under_the_pointer_in_place_and_thumbnails_never_go_blank", "crates/ui-egui/tests/it/combine_grid.rs::zooming_quickly_never_runs_more_than_three_render_threads", "crates/ui-egui/tests/it/combine_grid.rs::hovering_a_card_shows_its_trash_and_magnifier_and_only_then", "crates/ui-egui/tests/it/combine_grid.rs::the_card_trash_removes_only_that_file_and_undo_brings_it_back", "crates/ui-egui/tests/it/combine_grid.rs::the_magnifier_shows_the_file_large_and_flips_through_the_pages_it_adds", "crates/ui-egui/tests/it/combine_grid.rs::the_preview_closes_when_its_file_leaves_and_keeps_to_three_render_threads", "crates/ui-egui/tests/it/combine_grid.rs::a_right_click_on_a_card_offers_preview_and_remove", "crates/ui-egui/tests/it/combine_grid.rs::space_shows_the_selected_file_large_in_the_grid_and_the_list", "crates/ui-egui/tests/it/combine_grid.rs::expanding_a_file_shows_the_pages_it_adds_in_order_with_their_numbers", "crates/ui-egui/tests/it/combine_grid.rs::expand_all_and_collapse_all_from_the_toolbar_and_a_right_click", "crates/ui-egui/tests/it/combine_grid.rs::expanding_is_a_view_kept_across_views_and_zoom_and_never_an_undo_step", "crates/ui-egui/tests/it/combine_grid.rs::dragging_a_page_moves_that_page_even_between_another_files_pages", "crates/ui-egui/tests/it/combine_grid.rs::combining_with_files_expanded_gives_the_same_document", "crates/ui-egui/tests/it/combine_grid.rs::a_page_click_selects_that_page_and_the_arrows_step_rows_of_cards", "crates/ui-egui/tests/it/combine_grid.rs::a_page_cards_magnifier_opens_the_preview_on_that_page", "crates/ui-egui/tests/it/combine_grid.rs::a_long_file_expanded_renders_only_its_pages_in_view_within_three_threads", "crates/ui-egui/tests/it/combine_grid.rs::collapsing_brings_the_files_card_into_view", "crates/ui-egui/tests/it/combine_grid.rs::a_file_too_long_to_spread_out_offers_no_expand", "crates/ui-egui/src/combine_grid.rs::the_next_page_of_a_file_reuses_its_open_document_until_it_closes", "crates/ui-egui/tests/it/combine_grid.rs::removing_pages_from_the_hover_bar_the_toolbar_and_delete_never_touches_the_file", "crates/ui-egui/tests/it/combine_grid.rs::a_split_file_counts_once_and_combines_with_one_bookmark", "crates/ui-egui/src/combine_ui.rs::a_page_moved_between_another_files_pages_splits_its_file_and_parts_rejoin", "crates/ui-egui/src/combine_ui.rs::removing_pages_drops_emptied_parts_and_joins_what_meets", "crates/ui-egui/src/combine_ui.rs::page_lists_become_the_shortest_range", "crates/ui-egui/tests/it/combine_grid.rs::pages_picked_out_of_sight_or_before_an_undo_are_let_go_of", "crates/ui-egui/tests/it/combine_grid.rs::a_files_parts_join_when_what_was_between_them_goes_and_one_file_cannot_be_combined", "crates/organize/src/tests.rs::a_page_shown_twice_gets_its_own_annotations", "crates/engine/src/tests.rs::a_page_with_a_form_field_shown_twice_keeps_one_field", "crates/ui-egui/tests/it/combine_grid.rs::files_picked_by_automation_let_go_of_the_pages_picked"]
evidence = ["crates/ui-egui/tests/it/combine_grid.rs::combine_shows_a_grid_by_default_and_the_view_is_kept_in_the_settings", "crates/ui-egui/tests/it/combine_grid.rs::cards_select_like_the_list_and_the_selection_survives_switching_views", "crates/ui-egui/tests/it/combine_grid.rs::dragging_a_card_moves_it_and_undo_puts_it_back", "crates/ui-egui/tests/it/combine_grid.rs::a_dragged_selection_moves_together_to_the_first_and_last_gaps", "crates/ui-egui/tests/it/combine_grid.rs::thumbnails_show_the_first_page_each_file_takes", "crates/ui-egui/tests/it/combine_grid.rs::many_files_render_only_what_is_in_view_a_few_at_a_time", "crates/ui-egui/tests/it/combine_grid.rs::long_thin_pages_get_thumbnails_within_the_size_limits", "crates/ui-egui/tests/it/combine_grid.rs::combine_follows_the_order_shown_in_the_grid", "crates/ui-egui/src/combine_grid/tests.rs::render_threads_never_exceed_three_while_files_come_and_go", "crates/ui-egui/src/combine_grid/tests.rs::textures_over_budget_go_longest_unseen_first_and_those_in_view_stay", "crates/ui-egui/src/combine_grid/tests.rs::on_the_web_one_thumbnail_waits_at_a_time", "crates/ui-egui/tests/it/combine_grid.rs::a_file_whose_pages_cant_be_measured_says_there_is_no_preview", "crates/ui-egui/tests/it/combine_grid.rs::the_toolbar_sizes_the_cards_within_limits_and_the_size_is_kept_in_the_settings", "crates/ui-egui/src/combine_grid/tests.rs::the_cards_zoom_far_enough_to_read_a_value", "crates/ui-egui/tests/it/combine_grid.rs::ctrl_keys_and_ctrl_wheel_size_the_cards_and_leave_the_window_alone", "crates/ui-egui/tests/it/combine_grid.rs::zooming_keeps_the_card_under_the_pointer_in_place_and_thumbnails_never_go_blank", "crates/ui-egui/tests/it/combine_grid.rs::zooming_quickly_never_runs_more_than_three_render_threads", "crates/ui-egui/tests/it/combine_grid.rs::hovering_a_card_shows_its_trash_and_magnifier_and_only_then", "crates/ui-egui/tests/it/combine_grid.rs::the_card_trash_removes_only_that_file_and_undo_brings_it_back", "crates/ui-egui/tests/it/combine_grid.rs::the_magnifier_shows_the_file_large_and_flips_through_the_pages_it_adds", "crates/ui-egui/tests/it/combine_grid.rs::the_preview_closes_when_its_file_leaves_and_keeps_to_three_render_threads", "crates/ui-egui/tests/it/combine_grid.rs::a_right_click_on_a_card_offers_preview_and_remove", "crates/ui-egui/tests/it/combine_grid.rs::space_shows_the_selected_file_large_in_the_grid_and_the_list", "crates/ui-egui/tests/it/combine_grid.rs::expanding_a_file_shows_the_pages_it_adds_in_order_with_their_numbers", "crates/ui-egui/tests/it/combine_grid.rs::expand_all_and_collapse_all_from_the_toolbar_and_a_right_click", "crates/ui-egui/tests/it/combine_grid.rs::expanding_is_a_view_kept_across_views_and_zoom_and_never_an_undo_step", "crates/ui-egui/tests/it/combine_grid.rs::dragging_a_page_moves_that_page_even_between_another_files_pages", "crates/ui-egui/tests/it/combine_grid.rs::combining_with_files_expanded_gives_the_same_document", "crates/ui-egui/tests/it/combine_grid.rs::a_page_click_selects_that_page_and_the_arrows_step_rows_of_cards", "crates/ui-egui/tests/it/combine_grid.rs::a_page_cards_magnifier_opens_the_preview_on_that_page", "crates/ui-egui/tests/it/combine_grid.rs::a_long_file_expanded_renders_only_its_pages_in_view_within_three_threads", "crates/ui-egui/tests/it/combine_grid.rs::collapsing_brings_the_files_card_into_view", "crates/ui-egui/tests/it/combine_grid.rs::a_file_too_long_to_spread_out_offers_no_expand", "crates/ui-egui/src/combine_grid/tests.rs::the_next_page_of_a_file_reuses_its_open_document_until_it_closes", "crates/ui-egui/tests/it/combine_grid.rs::removing_pages_from_the_hover_bar_the_toolbar_and_delete_never_touches_the_file", "crates/ui-egui/tests/it/combine_grid.rs::a_split_file_counts_once_and_combines_with_one_bookmark", "crates/ui-egui/src/combine_ui.rs::a_page_moved_between_another_files_pages_splits_its_file_and_parts_rejoin", "crates/ui-egui/src/combine_ui.rs::removing_pages_drops_emptied_parts_and_joins_what_meets", "crates/ui-egui/src/combine_ui.rs::page_lists_become_the_shortest_range", "crates/ui-egui/tests/it/combine_grid.rs::pages_picked_out_of_sight_or_before_an_undo_are_let_go_of", "crates/ui-egui/tests/it/combine_grid.rs::a_files_parts_join_when_what_was_between_them_goes_and_one_file_cannot_be_combined", "crates/organize/src/tests.rs::a_page_shown_twice_gets_its_own_annotations", "crates/engine/src/tests.rs::a_page_with_a_form_field_shown_twice_keeps_one_field", "crates/ui-egui/tests/it/combine_grid.rs::files_picked_by_automation_let_go_of_the_pages_picked"]
notes = "Combine files shows a card per file (the default; Grid/List toggle kept in the settings, --combine-view grid|list): the first page the file contributes (its page range's first page), stacked when it contributes more, and its name shortened in the middle. Cards are sized from 60% to 500% (toolbar slider, buttons and percentage reset, Ctrl/⌘ +/−/0, Ctrl/⌘ with the wheel or a pinch over the grid; the buttons step 60/80/100/125/150/175/200/250/300/400/500%, kept in the settings, --combine-zoom 60..500), keeping the card under the pointer in place. 200% used to be the ceiling, which was too small to read values off an invoice while reordering the pages of a statement (#2531). Hovering a card shows its own actions along the top of its page: remove that file only (whatever else is selected; undoable) and a magnifier that shows the file large, flipping through the pages it adds (buttons, Left/Right; Esc, close or a click outside closes it), also on the card's right-click menu, and Space shows the selected file large (the way there by keyboard). Cards select (click, Ctrl, Shift, arrows by card and by row), drag to reorder (one undo step; the grid scrolls at its edges) and share the list's toolbar. Thumbnails render off the UI thread, only for cards in view, sized to both sides of the page and at most 1536 px (room for a Letter page at 500% on a 1.5 pixels-per-point screen); at most three render threads at once, counted as threads until they exit (no watchdog replacements; a page stuck for good in one step that can't be interrupted keeps its thread, and so its place, for the rest of the run); textures out of view are dropped over a 32 MB budget (those in view are kept). Locked, unreadable and bad-range files show why instead of a page, and a file whose page size can't be read (e.g. opened with an RC4 owner password) says No preview; page ranges are edited in the list. On the web a thumbnail, and the page shown large, render on the UI thread (one per frame, and only one waits at a time), so a very slow or very large page stalls that frame. A file that adds more than one page (up to 2000) expands into its pages (hover bar, right-click, or Expand all / Collapse all in the toolbar): one card per page it adds, in its range's order, each saying Page N and the file's name, on a tinted band; a click on a page selects that page, Up/Down step rows of cards, dragging a file's card moves the whole file (a drop among a file's pages goes to its nearer end), and its magnifier opens the preview on that page; expanding is a view, not an undo step, and changes nothing in the combined file; a page's render reuses its file's open document for a moment rather than reading the file again. With a file expanded, single pages are selected (click, Ctrl, Shift), dragged anywhere (a page moved between another file's pages splits its file into parts, shown as rows in the list with their own page ranges; parts side by side join again) and removed (a page card's trash, the toolbar, Delete), each one undo step, never changing the files themselves; a split file still counts and combines as one file."
[[feature]]
@@ -3231,7 +3231,7 @@ milestone = "M4"
status = "partial"
commands = []
tools = ["doc_info", "page_transition_set"]
evidence = ["crates/render/src/inspect.rs::page_transitions_and_display_times_are_read", "crates/automation/tests/it/automation.rs::doc_info_lists_page_transitions_and_display_times", "crates/organize/src/tests.rs::page_transitions_are_set_removed_and_checked", "crates/automation/tests/it/automation.rs::page_transitions_are_set_and_removed_through_the_tools"]
evidence = ["crates/render/src/inspect/tests.rs::page_transitions_and_display_times_are_read", "crates/automation/tests/it/automation.rs::doc_info_lists_page_transitions_and_display_times", "crates/organize/src/tests.rs::page_transitions_are_set_removed_and_checked", "crates/automation/tests/it/automation.rs::page_transitions_are_set_and_removed_through_the_tools"]
notes = "Read and set, not played: each page's /Trans (style, duration, dimension, motion, direction, Fly scale and backdrop) and /Dur display time are read with the spec's defaults and listed by doc_info (DocInfo::transitions); page_transition_set (Edit::SetTransition, one undo step) writes or removes them on chosen pages with the values checked first. Not yet: playing them in full screen, auto-advancing on /Dur, and the Page Transitions dialog"
[[feature]]
@@ -3339,7 +3339,7 @@ milestone = "M4"
status = "shipped"
commands = []
tools = ["bookmark_list", "bookmark_set_style"]
evidence = ["crates/render/src/inspect.rs::outline_items_carry_their_colour_and_style", "crates/organize/src/tests.rs::bookmarks_report_their_colour_and_style", "crates/organize/src/tests.rs::bookmark_style_and_colour_are_set_as_the_table_says", "crates/ui-egui/src/panels.rs::bookmark_titles_take_their_style_and_colour", "crates/ui-egui/src/theme.rs::a_document_colour_is_kept_where_it_reads", "crates/ui-egui/tests/it/bookmarks.rs::style_and_colour_from_the_bookmark_menu", "crates/automation/tests/it/automation.rs::bookmark_list_reports_colour_and_style"]
evidence = ["crates/render/src/inspect/tests.rs::outline_items_carry_their_colour_and_style", "crates/organize/src/tests.rs::bookmarks_report_their_colour_and_style", "crates/organize/src/tests.rs::bookmark_style_and_colour_are_set_as_the_table_says", "crates/ui-egui/src/panels.rs::bookmark_titles_take_their_style_and_colour", "crates/ui-egui/src/theme/tests.rs::a_document_colour_is_kept_where_it_reads", "crates/ui-egui/tests/it/bookmarks.rs::style_and_colour_from_the_bookmark_menu", "crates/automation/tests/it/automation.rs::bookmark_list_reports_colour_and_style"]
notes = "#3193: the Bookmarks panel draws each title in its /C colour and /F style (ISO 32000-2 §12.3.3, Tables 151 and 152: bit 1 italic, bit 2 bold), lightening a colour too dark for the dark theme until it reads (3:1). A bookmark's menu has Style (Bold, Italic) and Colour (the comment swatches and Other colour…); bookmark_set_style does the same and bookmark_list reports them. Regular removes /F, black removes /C, and /F bits the table doesn't define are kept. Not yet: one change for several selected bookmarks, and a Properties dialog."
[[feature]]
@@ -3519,7 +3519,7 @@ milestone = "M4"
status = "partial"
commands = ["file.properties"]
tools = ["doc_info", "doc_set_info"]
evidence = ["crates/organize/src/tests.rs::custom_document_properties_are_listed_set_removed_and_their_names_checked", "crates/render/src/inspect.rs::custom_document_properties_are_listed_apart_from_the_standard_ones", "crates/automation/tests/it/automation.rs::custom_document_properties_are_set_listed_and_cleared_through_the_tools", "crates/ui-egui/tests/it/editing.rs::custom_properties_are_added_in_the_custom_tab_as_one_undoable_step", "crates/ui-egui/tests/it/editing.rs::custom_properties_are_changed_and_deleted_in_the_custom_tab", "crates/ui-egui/tests/it/editing.rs::the_custom_tab_refuses_names_a_custom_property_cannot_have", "crates/ui-egui/tests/it/editing.rs::a_custom_property_whose_name_the_tools_refuse_is_listed_and_left_alone", "crates/ui-egui/tests/it/editing.rs::a_custom_property_is_deleted_from_an_info_dictionary_in_the_trailer", "crates/ui-egui/tests/it/editing.rs::add_stops_at_the_256_custom_properties_the_tab_lists", "crates/organize/src/tests.rs::document_info_entries_are_cleared_from_an_info_dictionary_in_the_trailer"]
evidence = ["crates/organize/src/tests.rs::custom_document_properties_are_listed_set_removed_and_their_names_checked", "crates/render/src/inspect/tests.rs::custom_document_properties_are_listed_apart_from_the_standard_ones", "crates/automation/tests/it/automation.rs::custom_document_properties_are_set_listed_and_cleared_through_the_tools", "crates/ui-egui/tests/it/editing.rs::custom_properties_are_added_in_the_custom_tab_as_one_undoable_step", "crates/ui-egui/tests/it/editing.rs::custom_properties_are_changed_and_deleted_in_the_custom_tab", "crates/ui-egui/tests/it/editing.rs::the_custom_tab_refuses_names_a_custom_property_cannot_have", "crates/ui-egui/tests/it/editing.rs::a_custom_property_whose_name_the_tools_refuse_is_listed_and_left_alone", "crates/ui-egui/tests/it/editing.rs::a_custom_property_is_deleted_from_an_info_dictionary_in_the_trailer", "crates/ui-egui/tests/it/editing.rs::add_stops_at_the_256_custom_properties_the_tab_lists", "crates/organize/src/tests.rs::document_info_entries_are_cleared_from_an_info_dictionary_in_the_trailer"]
notes = "Document Properties ▸ Custom lists them, with Name and Value fields and Add, Update (the value of the listed property the Name field names) and Delete; OK writes the changes as one undo step with the other tabs' changes. A property already in the file under a name the tools refuse (a space, a delimiter) or a name that is not UTF-8 is listed and cannot be changed or deleted there, and Add is off for a name the file already uses for an entry that is not listed (not text, only padding, or past the 256 listed), and once the tab or the file lists 256 properties. Through the tools: doc_set_info sets or clears an /Info entry under any name the spec does not use (names checked as Acrobat does: no standard name, no empty name, no spaces or delimiters, at most 127 bytes), and doc_info lists them under custom, apart from the standard entries. Not yet: mirroring them into XMP (pdfx:) as Acrobat does"
[[feature]]
@@ -3607,7 +3607,7 @@ milestone = "M5"
status = "shipped"
commands = ["comment.note"]
tools = ["comment_add"]
evidence = ["crates/ui-egui/tests/it/comments.rs::sticky_note_composer_posts_and_returns_to_select", "crates/ui-egui/tests/it/comments.rs::the_comment_composer_shortcut_only_posts_the_focused_editor", "crates/annot/src/tests.rs::every_tool_creates_a_drawable_comment", "crates/annot/src/tests.rs::every_note_icon_draws_its_own_shape", "crates/annot/src/tests.rs::a_star_note_keeps_its_star_when_recoloured", "crates/engine/src/tests.rs::sticky_notes_stay_upright_where_they_are_placed_on_rotated_pages", "crates/render/src/raster.rs::no_rotate_notes_stay_upright_at_their_corner_on_rotated_pages", "crates/ui-egui/tests/it/comments.rs::a_sticky_note_on_a_turned_page_is_picked_where_it_is_drawn"]
evidence = ["crates/ui-egui/tests/it/comments.rs::sticky_note_composer_posts_and_returns_to_select", "crates/ui-egui/tests/it/comments.rs::the_comment_composer_shortcut_only_posts_the_focused_editor", "crates/annot/src/tests.rs::every_tool_creates_a_drawable_comment", "crates/annot/src/tests.rs::every_note_icon_draws_its_own_shape", "crates/annot/src/tests.rs::a_star_note_keeps_its_star_when_recoloured", "crates/engine/src/tests.rs::sticky_notes_stay_upright_where_they_are_placed_on_rotated_pages", "crates/render/src/raster/tests.rs::no_rotate_notes_stay_upright_at_their_corner_on_rotated_pages", "crates/ui-egui/tests/it/comments.rs::a_sticky_note_on_a_turned_page_is_picked_where_it_is_drawn"]
notes = "our own icon drawings for the seven standard /Name values of ISO 32000-2 Table 175 (Comment, Note, Help, Insert, Key, NewParagraph, Paragraph) and seven further names (Check, Circle, Cross, CrossHairs, Star, RightArrow, UpArrow), each its own shape, chosen in Properties or comment_add, and kept when the colour changes; another name draws as Note. With a pop-up; Ctrl+Enter (also Command+Enter) posts from the focused editor, Shift+Enter adds a newline. Notes are saved with the NoRotate flag, and it is honoured: on a page with /Rotate the icon stays upright with its top-left where it was placed, and is selected and hovered there (#2583). Not yet: opening the pop-ups a file saves open (/Open); NoZoom (the icon scales with the zoom); printing and flattening still place the icon in its /Rect, turned with the page."
[[feature]]
@@ -4451,7 +4451,7 @@ milestone = "M3"
status = "shipped"
commands = ["form.fields"]
tools = ["doc_info"]
evidence = ["crates/ui-egui/tests/it/view.rs::field_list_jumps_to_a_fields_page", "crates/render/src/inspect.rs::fields_listed_only_on_the_pages_are_listed", "crates/ui-egui/tests/it/bulk_fields.rs::fields_panel_selects_multiple_fields_and_opens_their_shared_properties", "crates/ui-egui/tests/it/bulk_fields.rs::double_clicking_a_field_in_the_panel_opens_its_individual_properties"]
evidence = ["crates/ui-egui/tests/it/view.rs::field_list_jumps_to_a_fields_page", "crates/render/src/inspect/tests.rs::fields_listed_only_on_the_pages_are_listed", "crates/ui-egui/tests/it/bulk_fields.rs::fields_panel_selects_multiple_fields_and_opens_their_shared_properties", "crates/ui-egui/tests/it/bulk_fields.rs::double_clicking_a_field_in_the_panel_opens_its_individual_properties"]
notes = "list with jump-to-field; in Prepare a form, Shift/Ctrl/Command-click selects several fields and Properties edits their shared properties; double-click opens field properties; fields a file lists only as page widgets (empty /Fields) are adopted too"
[[feature]]
@@ -4475,7 +4475,7 @@ milestone = "M2"
status = "shipped"
commands = []
tools = []
evidence = ["crates/render/src/raster.rs::widget_appearance_states", "crates/engine/src/tests.rs::validated_signatures_without_layers_keep_their_appearance", "crates/engine/src/tests.rs::real_layered_signatures_keep_their_original_appearance", "crates/engine/src/tests.rs::real_layered_signatures_round_trip_behind_a_user_password", "crates/engine/src/tests.rs::a_metadata_edit_keeps_the_original_signature_appearance", "crates/engine/src/tests.rs::saving_after_an_edit_keeps_the_baked_appearance", "crates/engine/src/tests.rs::chamfered_signature_borders_survive_trust_exports_printing_and_save", "crates/engine/src/tests.rs::mixed_signature_caption_details_survive_trust_exports_printing_and_save", "crates/engine/src/tests.rs::buttons_and_combs_left_to_the_viewer_are_displayed", "crates/forms/src/tests.rs::button_states_without_an_appearance"]
evidence = ["crates/render/src/raster/tests.rs::widget_appearance_states", "crates/engine/src/tests.rs::validated_signatures_without_layers_keep_their_appearance", "crates/engine/src/tests.rs::real_layered_signatures_keep_their_original_appearance", "crates/engine/src/tests.rs::real_layered_signatures_round_trip_behind_a_user_password", "crates/engine/src/tests.rs::a_metadata_edit_keeps_the_original_signature_appearance", "crates/engine/src/tests.rs::saving_after_an_edit_keeps_the_baked_appearance", "crates/engine/src/tests.rs::chamfered_signature_borders_survive_trust_exports_printing_and_save", "crates/engine/src/tests.rs::mixed_signature_caption_details_survive_trust_exports_printing_and_save", "crates/engine/src/tests.rs::buttons_and_combs_left_to_the_viewer_are_displayed", "crates/forms/src/tests.rs::button_states_without_an_appearance"]
notes = "widgets retain their original artwork. Signature validation is a separate viewer page overlay; it is never written into document streams or included in image/text exports, vector printing or Print as Image. Saves retain signed appearance bytes. Generic in-place appearance rewriting is deferred: no supported producer template is positively identified, and caption text plus drawing uniqueness does not establish an icon role. Baked Not Verified captions remain unchanged; issue #79 is not fully resolved. Check boxes and radio buttons with no appearance for their state are shown (display-only) with their /MK border and background and, when on, the /MK /CA ZapfDingbats symbol in the /DA colour (a check or a dot by default); push buttons with none show their /MK frame and caption; files that carry their own appearances are not redrawn."
[[feature]]
@@ -4571,7 +4571,7 @@ 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/it/automation.rs::form_formats_and_calculations_through_tools"]
evidence = ["crates/forms/src/af/tests.rs::numbers_format_like_acrobat", "crates/forms/src/tests.rs::formats_validation_and_calculations_run_like_acrobat", "crates/automation/tests/it/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]]
@@ -4583,7 +4583,7 @@ milestone = "M6"
status = "shipped"
commands = ["form.field.properties"]
tools = ["form_set_props", "form_fill"]
evidence = ["crates/forms/src/af.rs::numbers_format_like_acrobat"]
evidence = ["crates/forms/src/af/tests.rs::numbers_format_like_acrobat"]
notes = "AFPercent_Format/Keystroke"
[[feature]]
@@ -4595,7 +4595,7 @@ 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", "crates/forms/src/af.rs::digits_typed_without_separators_fill_adjacent_fields"]
evidence = ["crates/forms/src/af/tests.rs::dates_times_and_masks", "crates/forms/src/tests.rs::formats_validation_and_calculations_run_like_acrobat", "crates/forms/src/af.rs::digits_typed_without_separators_fill_adjacent_fields"]
notes = "AFDate_FormatEx/KeystrokeEx, AFDate_Format presets, AFTime_Format(Ex): lenient parsing, month and day names, impossible dates refused; digits typed without separators fill fields the format writes side by side by their fixed widths (20261018 for yyyymmdd, 1430 for HHMM). A run of digits is split only where the format itself has no separator: 20261018 typed into a yyyy-mm-dd field is still refused"
[[feature]]
@@ -4607,7 +4607,7 @@ milestone = "M6"
status = "shipped"
commands = ["form.field.properties"]
tools = ["form_set_props", "form_fill"]
evidence = ["crates/forms/src/af.rs::dates_times_and_masks"]
evidence = ["crates/forms/src/af/tests.rs::dates_times_and_masks"]
notes = "zip, zip+4, phone, SSN, arbitrary masks (9 A O X)"
[[feature]]
@@ -4619,7 +4619,7 @@ 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/it/automation.rs::form_formats_and_calculations_through_tools"]
evidence = ["crates/forms/src/af/tests.rs::validation_and_calculation", "crates/forms/src/tests.rs::formats_validation_and_calculations_run_like_acrobat", "crates/automation/tests/it/automation.rs::form_formats_and_calculations_through_tools"]
notes = "AFRange_Validate with Acrobat's messages"
[[feature]]
@@ -4631,7 +4631,7 @@ 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"]
evidence = ["crates/forms/src/af/tests.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]]
@@ -4643,7 +4643,7 @@ 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/it/automation.rs::form_formats_and_calculations_through_tools", "crates/forms/src/af.rs::notation_is_read_only_from_a_comment"]
evidence = ["crates/forms/src/af/tests.rs::validation_and_calculation", "crates/forms/src/tests.rs::formats_validation_and_calculations_run_like_acrobat", "crates/automation/tests/it/automation.rs::form_formats_and_calculations_through_tools", "crates/forms/src/af/tests.rs::notation_is_read_only_from_a_comment"]
notes = "BVCALC expressions with + - * / and parentheses, escaped names, read from the script's comment as Acrobat writes it; a string or code that spells the markers is not notation"
[[feature]]
@@ -5039,7 +5039,7 @@ milestone = "M6"
status = "shipped"
commands = ["form.field.properties"]
tools = ["form_set_props", "form_fill"]
evidence = ["crates/ui-egui/tests/it/prepare.rs::format_validate_and_calculate_tabs", "crates/automation/tests/it/automation.rs::form_formats_and_calculations_through_tools", "crates/forms/src/tests.rs::renaming_a_field_renames_it_in_the_calculations_that_read_it", "crates/forms/src/tests.rs::a_rename_reaches_every_stored_form_of_a_calculation", "crates/automation/tests/it/automation.rs::renaming_a_field_keeps_the_formulas_that_read_it", "crates/ui-egui/tests/it/prepare.rs::renaming_a_field_says_how_many_calculations_now_use_the_new_name", "crates/forms/src/af.rs::a_regex_after_a_condition_is_not_read_as_code", "crates/forms/src/af.rs::a_line_comment_ends_at_any_line_terminator", "crates/forms/src/af.rs::a_token_ends_at_a_line_separator"]
evidence = ["crates/ui-egui/tests/it/prepare.rs::format_validate_and_calculate_tabs", "crates/automation/tests/it/automation.rs::form_formats_and_calculations_through_tools", "crates/forms/src/tests.rs::renaming_a_field_renames_it_in_the_calculations_that_read_it", "crates/forms/src/tests.rs::a_rename_reaches_every_stored_form_of_a_calculation", "crates/automation/tests/it/automation.rs::renaming_a_field_keeps_the_formulas_that_read_it", "crates/ui-egui/tests/it/prepare.rs::renaming_a_field_says_how_many_calculations_now_use_the_new_name", "crates/forms/src/af/tests.rs::a_regex_after_a_condition_is_not_read_as_code", "crates/forms/src/af/tests.rs::a_line_comment_ends_at_any_line_terminator", "crates/forms/src/af/tests.rs::a_token_ends_at_a_line_separator"]
notes = "sum/product/average/min/max of picked fields, simplified field notation; custom script pending. Renaming a field renames it in the calculations that read it (field lists, notation, and getField calls naming it in a string literal), in the rename's undo step; Field Properties says how many changed and form_set_props lists them. Not rewritten: names built at run time, other events' scripts, document-level scripts. A regular expression that starts a statement right after a `}` is still read as code, so a quote inside one can hide the getField calls after it from a rename and from the calculation order. Whether Acrobat does this is not observed."
[[feature]]
@@ -5380,7 +5380,7 @@ milestone = "M5"
status = "shipped"
commands = ["sign.fill.signature", "sign.fill.initials", "sign.fill.signature.change", "sign.fill.initials.change", "sign.fill.signature.remove", "sign.fill.initials.remove"]
tools = ["fill_sign_add", "comment_edit", "comment_image_preview"]
evidence = ["crates/ui-egui/tests/it/fill_sign.rs::drawn_signatures_resize_from_their_corners", "crates/ui-egui/tests/it/fill_sign.rs::drawn_and_typed_signatures_stay_upright_on_rotated_pages", "crates/automation/tests/it/automation.rs::typed_signatures_stay_upright_on_rotated_pages", "crates/ui-egui/tests/it/fill_sign.rs::image_signature_corners_restore_original_aspect_after_edge_resize_and_reopen", "crates/ui-egui/src/comments.rs::proportional_corners_keep_the_opposite_anchor_and_edges_stretch_one_axis", "crates/ui-egui/tests/it/fill_sign.rs::image_signature_live_resize_and_move_preserve_background_and_hide_moving_handles", "crates/ui-egui/tests/it/fill_sign.rs::embedded_image_signature_live_gestures_follow_document_and_view_rotation", "crates/automation/tests/it/automation.rs::image_signature_preview_layers_are_read_only_and_survive_encrypted_save", "crates/engine/src/signature_image.rs::embedded_signature_preview_preserves_alpha_without_editing_the_document", "crates/engine/src/signature_image.rs::oversized_embedded_signature_preview_is_rejected_before_decoding", "crates/ui-egui/tests/it/fill_sign.rs::image_signature_preview_follows_pointer_at_page_size_with_zoom_and_rotation", "crates/ui-egui/tests/it/fill_sign.rs::image_signature_placement_selects_resize_handles_and_requires_reselecting_to_repeat", "crates/ui-egui/tests/it/fill_sign.rs::typed_signatures_and_initials", "crates/ui-egui/tests/it/fill_sign.rs::signing_draws_a_signature_once_and_places_it", "crates/ui-egui/tests/it/fill_sign.rs::changing_saved_signatures_and_initials_preserves_placed_marks", "crates/ui-egui/tests/it/fill_sign.rs::changing_drawn_signatures_can_be_cancelled_or_switched_to_type", "crates/ui-egui/tests/it/fill_sign.rs::saved_signature_cards_remove_and_add_without_changing_the_document", "crates/ui-egui/tests/it/fill_sign.rs::long_typed_names_fit_the_placed_signature_and_keep_every_outline", "crates/ui-egui/src/fill_sign.rs::long_signature_previews_have_clear_margins", "crates/ui-egui/tests/it/control.rs::saved_signature_can_be_changed_through_the_control_channel", "crates/fonts/src/script.rs::a_name_becomes_outlines", "crates/fonts/src/script.rs::long_names_keep_every_glyph_with_bounded_outline_work", "crates/automation/tests/it/automation.rs::fill_and_sign_through_tools", "crates/ui-egui/tests/it/fill_sign.rs::image_signatures_and_initials_can_be_imported_placed_and_remembered", "crates/ui-egui/tests/it/fill_sign.rs::image_import_cancel_clear_and_errors_preserve_saved_signatures", "crates/engine/src/signature_image.rs::image_signatures_preserve_alpha_and_fit_the_signature_box", "crates/engine/src/signature_image.rs::invalid_and_oversized_signature_images_are_rejected", "crates/engine/src/signature_image.rs::large_signature_images_are_scaled_down", "crates/engine/src/signature_image.rs::jpeg_signatures_are_normalized_to_png", "crates/engine/src/signature_image.rs::jpeg_signature_orientation_is_applied_before_preview_and_embedding", "crates/engine/src/signature_image.rs::very_wide_image_signatures_keep_the_requested_rectangle", "crates/automation/tests/it/automation.rs::image_signatures_through_tools_preserve_transparency_and_survive_save", "crates/ui-egui/tests/it/fill_sign.rs::fill_and_sign_tools_pick_up_placed_marks_to_move_and_resize_them", "crates/ui-egui/tests/it/fill_sign.rs::select_tool_picks_up_a_signature_over_a_form_field", "crates/ui-egui/tests/it/fill_sign.rs::scanned_signatures_are_framed_cleaned_adjusted_placed_and_remembered", "crates/ui-egui/tests/it/fill_sign.rs::scan_cancel_errors_and_stale_answers_preserve_saved_signatures", "crates/automation/tests/it/automation.rs::scanned_signatures_through_tools_drop_the_paper_and_report_bad_arguments", "crates/engine/src/signature_scan.rs::a_photo_is_scanned_into_a_trimmed_transparent_signature_in_its_ink_colour", "crates/ui-egui/tests/it/fill_sign.rs::undo_button_and_ctrl_z_take_back_one_drawn_stroke_at_a_time", "crates/ui-egui/src/fill_sign.rs::undo_removes_the_last_stroke_and_skips_empty_ones", "crates/ui-egui/src/comments.rs::shift_keeps_the_current_proportions_unless_the_box_has_its_own"]
evidence = ["crates/ui-egui/tests/it/fill_sign.rs::drawn_signatures_resize_from_their_corners", "crates/ui-egui/tests/it/fill_sign.rs::drawn_and_typed_signatures_stay_upright_on_rotated_pages", "crates/automation/tests/it/automation.rs::typed_signatures_stay_upright_on_rotated_pages", "crates/ui-egui/tests/it/fill_sign.rs::image_signature_corners_restore_original_aspect_after_edge_resize_and_reopen", "crates/ui-egui/src/comments/tests.rs::proportional_corners_keep_the_opposite_anchor_and_edges_stretch_one_axis", "crates/ui-egui/tests/it/fill_sign.rs::image_signature_live_resize_and_move_preserve_background_and_hide_moving_handles", "crates/ui-egui/tests/it/fill_sign.rs::embedded_image_signature_live_gestures_follow_document_and_view_rotation", "crates/automation/tests/it/automation.rs::image_signature_preview_layers_are_read_only_and_survive_encrypted_save", "crates/engine/src/signature_image.rs::embedded_signature_preview_preserves_alpha_without_editing_the_document", "crates/engine/src/signature_image.rs::oversized_embedded_signature_preview_is_rejected_before_decoding", "crates/ui-egui/tests/it/fill_sign.rs::image_signature_preview_follows_pointer_at_page_size_with_zoom_and_rotation", "crates/ui-egui/tests/it/fill_sign.rs::image_signature_placement_selects_resize_handles_and_requires_reselecting_to_repeat", "crates/ui-egui/tests/it/fill_sign.rs::typed_signatures_and_initials", "crates/ui-egui/tests/it/fill_sign.rs::signing_draws_a_signature_once_and_places_it", "crates/ui-egui/tests/it/fill_sign.rs::changing_saved_signatures_and_initials_preserves_placed_marks", "crates/ui-egui/tests/it/fill_sign.rs::changing_drawn_signatures_can_be_cancelled_or_switched_to_type", "crates/ui-egui/tests/it/fill_sign.rs::saved_signature_cards_remove_and_add_without_changing_the_document", "crates/ui-egui/tests/it/fill_sign.rs::long_typed_names_fit_the_placed_signature_and_keep_every_outline", "crates/ui-egui/src/fill_sign.rs::long_signature_previews_have_clear_margins", "crates/ui-egui/tests/it/control.rs::saved_signature_can_be_changed_through_the_control_channel", "crates/fonts/src/script.rs::a_name_becomes_outlines", "crates/fonts/src/script.rs::long_names_keep_every_glyph_with_bounded_outline_work", "crates/automation/tests/it/automation.rs::fill_and_sign_through_tools", "crates/ui-egui/tests/it/fill_sign.rs::image_signatures_and_initials_can_be_imported_placed_and_remembered", "crates/ui-egui/tests/it/fill_sign.rs::image_import_cancel_clear_and_errors_preserve_saved_signatures", "crates/engine/src/signature_image.rs::image_signatures_preserve_alpha_and_fit_the_signature_box", "crates/engine/src/signature_image.rs::invalid_and_oversized_signature_images_are_rejected", "crates/engine/src/signature_image.rs::large_signature_images_are_scaled_down", "crates/engine/src/signature_image.rs::jpeg_signatures_are_normalized_to_png", "crates/engine/src/signature_image.rs::jpeg_signature_orientation_is_applied_before_preview_and_embedding", "crates/engine/src/signature_image.rs::very_wide_image_signatures_keep_the_requested_rectangle", "crates/automation/tests/it/automation.rs::image_signatures_through_tools_preserve_transparency_and_survive_save", "crates/ui-egui/tests/it/fill_sign.rs::fill_and_sign_tools_pick_up_placed_marks_to_move_and_resize_them", "crates/ui-egui/tests/it/fill_sign.rs::select_tool_picks_up_a_signature_over_a_form_field", "crates/ui-egui/tests/it/fill_sign.rs::scanned_signatures_are_framed_cleaned_adjusted_placed_and_remembered", "crates/ui-egui/tests/it/fill_sign.rs::scan_cancel_errors_and_stale_answers_preserve_saved_signatures", "crates/automation/tests/it/automation.rs::scanned_signatures_through_tools_drop_the_paper_and_report_bad_arguments", "crates/engine/src/signature_scan/tests.rs::a_photo_is_scanned_into_a_trimmed_transparent_signature_in_its_ink_colour", "crates/ui-egui/tests/it/fill_sign.rs::undo_button_and_ctrl_z_take_back_one_drawn_stroke_at_a_time", "crates/ui-egui/src/fill_sign.rs::undo_removes_the_last_stroke_and_skips_empty_ones", "crates/ui-egui/src/comments/tests.rs::shift_keeps_the_current_proportions_unless_the_box_has_its_own"]
notes = "signature and initials, typed (Dancing Script outlines as filled paths, no font embedded), drawn (an Undo button and Ctrl/Cmd+Z in the pad take back the last stroke; Clear empties it) or imported from local PNG/JPEG images; all three are placed upright and anchored as displayed on pages with /Rotate (#299); saved in settings with preview cards and add/change/remove controls; changes leave placed annotations untouched; images preserve alpha, aspect ratio and EXIF orientation, fit within 150 pt by 32 pt (24 pt for initials), and are normalized without source paths or metadata; image previews follow the pointer at placement size with zoom and rotation; signatures/initials place once then select the new annotation; while a Fill & Sign tool is active, marks already placed are picked up as with Select (hover shows the move cursor, a click selects, a drag moves, the selected mark's handles resize) and the tool places only away from them; embedded image signatures resize and move live, hiding the frame during movement and restoring it on release; corner handles preserve the original aspect ratio of the embedded image with the opposite corner anchored, while edge midpoint handles stretch width or height independently; cached background layers preserve page content and alpha, including reopened/encrypted documents; Escape cancels a gesture without committing; stamps resize through selection handles or comment_edit with their original appearance; holding Shift on a corner handle keeps any resizable comment's current proportions (#2199); native and web asynchronous pickers; fill_sign_add accepts path instead of text; image imports capped at 4 MiB, 32 megapixels and 8192 pixels per side, larger than 4 megapixels or 4096 pixels per side scaled down to fit (#1075); Scan tab (fill_sign_add scan/sensitivity headless): a phone photo of a signature on paper (PNG/JPEG within the same image limits, EXIF-oriented, kept at most 4 megapixels) is framed automatically (all words; table, sheet edge and dust ignored; a sideways signature turned upright), the frame can be moved, resized or redrawn and the photo turned in quarter turns, and an ink-sensitivity slider (0-100) controls how faint ink is kept; the paper is estimated by a morphological closing, so shadows and uneven light are removed, and the result is trimmed ink in its own colour on transparency, saved like an imported image. Still missing: perspective correction of a skewed sheet, live camera capture, keyboard control of the frame, and processing off the UI thread (bounded by the 4-megapixel working copy; quick 900 px previews while dragging)"
[[feature]]
@@ -6300,7 +6300,7 @@ milestone = "M9"
status = "shipped"
commands = ["sign.panel"]
tools = []
evidence = ["crates/ui-egui/tests/it/signing.rs::drawing_a_signature_creating_an_id_signing_and_trusting", "crates/ui-egui/tests/it/signing.rs::certifying_without_a_visible_signature", "crates/ui-egui/src/canvas.rs::signature_badges_keep_signed_visible_widgets_on_the_page"]
evidence = ["crates/ui-egui/tests/it/signing.rs::drawing_a_signature_creating_an_id_signing_and_trusting", "crates/ui-egui/tests/it/signing.rs::certifying_without_a_visible_signature", "crates/ui-egui/src/canvas/tests.rs::signature_badges_keep_signed_visible_widgets_on_the_page"]
notes = "signed/valid, problems, invalid and certified bars; visible signed widgets also carry a separate viewer validation overlay. Trust and explicit validation invalidate view caches without editing document artwork; baked appearance captions remain unchanged."
[[feature]]
@@ -7052,7 +7052,7 @@ milestone = "M10"
status = "partial"
commands = ["export.docx"]
tools = ["doc_export_office"]
evidence = ["crates/export/src/lib.rs::word_table_cell_widths_follow_the_columns_they_span", "crates/export/src/lib.rs::table_spans_do_not_consume_text_they_do_not_emit", "crates/edit/src/tests.rs::reading_keeps_dotted_leader_entries_whole_and_in_order", "crates/engine/src/tests.rs::exporting_office_keeps_dotted_leader_entries_as_paragraphs", "crates/export/src/lib.rs::word_does_not_invent_borders_for_aligned_invoice_fields", "crates/export/src/lib.rs::word_overlap_does_not_consume_invoice_body_space", "crates/export/src/lib.rs::word_keeps_a_page_with_a_clear_image_and_big_title_in_flow", "crates/export/src/lib.rs::word_rotated_pages_keep_separate_page_sizes_and_unique_shapes", "crates/engine/src/tests.rs::office_export_positions_follow_the_displayed_crop_rotation_and_units", "crates/automation/tests/it/automation.rs::exporting_an_invoice_to_word_keeps_the_watermark_out_of_flow", "crates/export/src/lib.rs::docx_is_a_word_package", "crates/export/src/lib.rs::text_colour_survives_every_format", "crates/engine/src/tests.rs::exporting_office_files_keeps_text_colour", "crates/export/src/lib.rs::grid_text_becomes_a_real_table_in_every_format", "crates/export/src/lib.rs::a_link_inside_a_sentence_is_not_a_table_column", "crates/export/src/lib.rs::sentences_that_run_on_into_links_are_not_a_table", "crates/export/src/lib.rs::a_superscript_beside_a_number_keeps_its_table", "crates/export/src/lib.rs::word_opens_text_with_control_characters_and_long_pages", "crates/engine/src/tests.rs::exporting_office_files_keeps_images", "crates/automation/tests/it/automation.rs::exporting_to_word_html_and_rtf", "crates/ui-egui/tests/it/compare.rs::export_to_word_html_and_rtf", "crates/export/src/lib.rs::docx_keeps_a_shared_page_background", "crates/edit/src/tests.rs::a_page_painted_first_with_a_full_page_fill_has_that_background", "crates/engine/src/tests.rs::exporting_to_word_keeps_the_page_background", "crates/export/src/lib.rs::an_italic_word_stays_in_its_sentence", "crates/export/src/lib.rs::a_sentence_keeps_its_reading_order", "crates/export/src/lib.rs::runs_keep_the_spaces_the_source_had_between_them", "crates/export/src/lib.rs::a_paragraph_is_a_heading_only_when_its_text_is", "crates/automation/tests/it/automation.rs::exporting_to_word_keeps_an_italic_word_inside_its_sentence"]
evidence = ["crates/export/src/tests.rs::word_table_cell_widths_follow_the_columns_they_span", "crates/export/src/tests.rs::table_spans_do_not_consume_text_they_do_not_emit", "crates/edit/src/tests.rs::reading_keeps_dotted_leader_entries_whole_and_in_order", "crates/engine/src/tests.rs::exporting_office_keeps_dotted_leader_entries_as_paragraphs", "crates/export/src/tests.rs::word_does_not_invent_borders_for_aligned_invoice_fields", "crates/export/src/tests.rs::word_overlap_does_not_consume_invoice_body_space", "crates/export/src/tests.rs::word_keeps_a_page_with_a_clear_image_and_big_title_in_flow", "crates/export/src/tests.rs::word_rotated_pages_keep_separate_page_sizes_and_unique_shapes", "crates/engine/src/tests.rs::office_export_positions_follow_the_displayed_crop_rotation_and_units", "crates/automation/tests/it/automation.rs::exporting_an_invoice_to_word_keeps_the_watermark_out_of_flow", "crates/export/src/tests.rs::docx_is_a_word_package", "crates/export/src/tests.rs::text_colour_survives_every_format", "crates/engine/src/tests.rs::exporting_office_files_keeps_text_colour", "crates/export/src/tests.rs::grid_text_becomes_a_real_table_in_every_format", "crates/export/src/tests.rs::a_link_inside_a_sentence_is_not_a_table_column", "crates/export/src/tests.rs::sentences_that_run_on_into_links_are_not_a_table", "crates/export/src/tests.rs::a_superscript_beside_a_number_keeps_its_table", "crates/export/src/tests.rs::word_opens_text_with_control_characters_and_long_pages", "crates/engine/src/tests.rs::exporting_office_files_keeps_images", "crates/automation/tests/it/automation.rs::exporting_to_word_html_and_rtf", "crates/ui-egui/tests/it/compare.rs::export_to_word_html_and_rtf", "crates/export/src/tests.rs::docx_keeps_a_shared_page_background", "crates/edit/src/tests.rs::a_page_painted_first_with_a_full_page_fill_has_that_background", "crates/engine/src/tests.rs::exporting_to_word_keeps_the_page_background", "crates/export/src/tests.rs::an_italic_word_stays_in_its_sentence", "crates/export/src/tests.rs::a_sentence_keeps_its_reading_order", "crates/export/src/tests.rs::runs_keep_the_spaces_the_source_had_between_them", "crates/export/src/tests.rs::a_paragraph_is_a_heading_only_when_its_text_is", "crates/automation/tests/it/automation.rs::exporting_to_word_keeps_an_italic_word_inside_its_sentence"]
notes = "Text in slots covered by a preceding table-cell span remains in paragraph flow instead of being discarded. Flowing Word document: paragraphs, Heading 1/2 from larger text, bold/italic, text colour (fill as sRGB; gray/CMYK converted naively, colour spaces via cs/sc read as black), borderless detected tables with spans/empty cells (cell widths sum the grid columns they occupy, including cells after spans, verified in the XML; a row whose blocks run on into each other, such as a link or superscript inside a sentence, or overlap, as a paragraph's lines do, is text and not a table row, #2806), per-page sections/sizes, the page colour (a full-page opaque fill painted first, such as Edit a PDF ▸ Background) as the document background when every page shares one non-white colour (Word has no per-page background, so differing colours are dropped; #2992; checked in the XML only, not yet opened in Word), and positioned editable text/artwork only for pages with rotated text or text overlapped by an image or watermark-sized text, other pages flowing with inline images (#773); synthetic invoice regression; dotted-leader contents entries keep the title, dots and page number together in separate paragraphs (#1757); the emitted package validates with Open XML SDK 2.20 (Office 2010 schema). Word/LibreOffice rendering of the positioned output remains unverified. Pictures on positioned pages are DrawingML anchors behind the text (a VML rectangle with an image showed no picture, #1936; not yet opened in Word); a Word file embeds at most 1000 pictures and 512 MiB of them. The earlier flowing output was checked with macOS textutil and opened in Microsoft Word (Windows). Characters XML forbids are dropped; the page size stays within Word's 22 in. A block that continues another on its line (an italic case name, a link, a superscript) is a run in its own size, bold/italic and colour inside that paragraph, keeping the source's word space between them, rather than a paragraph of its own; a lead-in in bold no longer makes its whole line a heading (#4158). Not yet: a style change that wraps onto the next line still splits its paragraph there (the reading blocks keep style boundaries, #4158), exact font matching, vector rules, arbitrary paint-order/transparency, general exact layout fidelity."
[[feature]]
@@ -7064,7 +7064,7 @@ milestone = "M10"
status = "shipped"
commands = ["export.rtf"]
tools = ["doc_export_office"]
evidence = ["crates/export/src/lib.rs::table_spans_do_not_consume_text_they_do_not_emit", "crates/export/src/lib.rs::rtf_escapes_and_sizes", "crates/export/src/lib.rs::text_colour_survives_every_format", "crates/export/src/lib.rs::word_opens_text_with_control_characters_and_long_pages", "crates/automation/tests/it/automation.rs::exporting_to_word_html_and_rtf", "crates/ui-egui/tests/it/compare.rs::export_to_word_html_and_rtf"]
evidence = ["crates/export/src/tests.rs::table_spans_do_not_consume_text_they_do_not_emit", "crates/export/src/tests.rs::rtf_escapes_and_sizes", "crates/export/src/tests.rs::text_colour_survives_every_format", "crates/export/src/tests.rs::word_opens_text_with_control_characters_and_long_pages", "crates/automation/tests/it/automation.rs::exporting_to_word_html_and_rtf", "crates/ui-egui/tests/it/compare.rs::export_to_word_html_and_rtf"]
notes = "Text in slots covered by a preceding table-cell span remains in paragraph flow instead of being discarded. Paragraphs with sizes, bold, italic, text colour (colortbl), Unicode; a block that continues another on its line is a formatted group inside that paragraph (#4158), though a style change that wraps onto the next line still splits it; page breaks. Images are left out."
[[feature]]
@@ -7088,18 +7088,18 @@ milestone = "M10"
status = "partial"
commands = ["export.pptx"]
tools = ["doc_export_office"]
evidence = ["crates/export/src/lib.rs::table_spans_do_not_consume_text_they_do_not_emit",
"crates/export/src/pptx.rs::pptx_is_a_presentationml_package",
"crates/export/src/pptx.rs::pptx_slide_has_title_body_and_images_in_order",
"crates/export/src/pptx.rs::pptx_text_colour_and_formatting",
"crates/export/src/pptx.rs::pptx_empty_pages_produce_valid_slide",
"crates/export/src/pptx.rs::pptx_slide_dimensions_and_clamping",
"crates/export/src/pptx.rs::pptx_table_cell_with_largest_font_stays_in_table_and_not_in_title",
"crates/export/src/pptx.rs::pptx_opens_in_libreoffice",
"crates/export/src/pptx.rs::pptx_package_parts_are_complete_and_consistent",
"crates/export/src/pptx.rs::hostile_sizes_do_not_overflow_the_slide_position",
"crates/export/src/lib.rs::grid_text_becomes_a_real_table_in_every_format",
"crates/export/src/lib.rs::text_colour_survives_every_format",
evidence = ["crates/export/src/tests.rs::table_spans_do_not_consume_text_they_do_not_emit",
"crates/export/src/pptx/tests.rs::pptx_is_a_presentationml_package",
"crates/export/src/pptx/tests.rs::pptx_slide_has_title_body_and_images_in_order",
"crates/export/src/pptx/tests.rs::pptx_text_colour_and_formatting",
"crates/export/src/pptx/tests.rs::pptx_empty_pages_produce_valid_slide",
"crates/export/src/pptx/tests.rs::pptx_slide_dimensions_and_clamping",
"crates/export/src/pptx/tests.rs::pptx_table_cell_with_largest_font_stays_in_table_and_not_in_title",
"crates/export/src/pptx/tests.rs::pptx_opens_in_libreoffice",
"crates/export/src/pptx/tests.rs::pptx_package_parts_are_complete_and_consistent",
"crates/export/src/pptx/tests.rs::hostile_sizes_do_not_overflow_the_slide_position",
"crates/export/src/tests.rs::grid_text_becomes_a_real_table_in_every_format",
"crates/export/src/tests.rs::text_colour_survives_every_format",
"crates/engine/src/tests.rs::exporting_office_files_keeps_images",
"crates/engine/src/tests.rs::exporting_office_files_keeps_text_colour",
"crates/automation/tests/it/automation.rs::exporting_to_word_html_and_rtf",
@@ -7164,7 +7164,7 @@ milestone = "M10"
status = "partial"
commands = ["export.html"]
tools = ["doc_export_office"]
evidence = ["crates/export/src/lib.rs::table_spans_do_not_consume_text_they_do_not_emit", "crates/export/src/lib.rs::html_has_headings_paragraphs_and_images_in_order", "crates/export/src/lib.rs::text_colour_survives_every_format", "crates/export/src/lib.rs::grid_text_becomes_a_real_table_in_every_format", "crates/automation/tests/it/automation.rs::exporting_to_word_html_and_rtf", "crates/ui-egui/tests/it/compare.rs::export_to_word_html_and_rtf", "crates/automation/tests/it/automation.rs::exporting_office_files_says_which_pages_left_form_text_out", "crates/edit/src/tests.rs::reading_follows_forms_as_far_as_the_renderer_and_says_where_it_stops"]
evidence = ["crates/export/src/tests.rs::table_spans_do_not_consume_text_they_do_not_emit", "crates/export/src/tests.rs::html_has_headings_paragraphs_and_images_in_order", "crates/export/src/tests.rs::text_colour_survives_every_format", "crates/export/src/tests.rs::grid_text_becomes_a_real_table_in_every_format", "crates/automation/tests/it/automation.rs::exporting_to_word_html_and_rtf", "crates/ui-egui/tests/it/compare.rs::export_to_word_html_and_rtf", "crates/automation/tests/it/automation.rs::exporting_office_files_says_which_pages_left_form_text_out", "crates/edit/src/tests.rs::reading_follows_forms_as_far_as_the_renderer_and_says_where_it_stops"]
notes = "Text in slots covered by a preceding table-cell span remains in paragraph flow instead of being discarded. Single HTML file with headings, paragraphs (with text colour), inline images, and detected tables with spans/empty cells. Text and images in form XObjects are read 50 levels deep, as the renderer does, and up to 50 000 forms a page; the tool lists pages past that in truncated_pages, with a warning (#3120). Not yet: multi-page folder output, CSS from fonts."
[[feature]]
@@ -7272,7 +7272,7 @@ 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/it/automation.rs::parallel_text_extraction_keeps_page_order_and_follows_edits"]
evidence = ["crates/render/src/text/tests.rs::rtl_runs_come_out_in_logical_order_and_cjk_has_no_spaces", "crates/render/src/text/tests.rs::lines_words_and_search", "crates/automation/tests/it/automation.rs::parallel_text_extraction_keeps_page_order_and_follows_edits"]
notes = ""
[[feature]]
@@ -9180,7 +9180,7 @@ milestone = "M12"
status = "shipped"
commands = []
tools = ["view_reading_colors"]
evidence = ["crates/automation/tests/it/automation.rs::reading_colors_through_tools", "crates/render/src/raster.rs::reading_colors_map_solid_paints_and_leave_images", "crates/render/src/raster.rs::reading_colors_tiles_match_full_render", "crates/render/src/raster.rs::print_preview_requests_render_with_reading_colors_off", "crates/render/src/raster.rs::reading_colors_keep_highlight_annotations_readable", "crates/ui-egui/tests/it/reading_colors.rs::reading_colours_preference_round_trips", "crates/ui-egui/tests/it/reading_colors.rs::reading_colours_seed_on_open", "crates/ui-egui/tests/it/reading_colors.rs::pending_pages_use_document_reading_background"]
evidence = ["crates/automation/tests/it/automation.rs::reading_colors_through_tools", "crates/render/src/raster/tests.rs::reading_colors_map_solid_paints_and_leave_images", "crates/render/src/raster/tests.rs::reading_colors_tiles_match_full_render", "crates/render/src/raster/tests.rs::print_preview_requests_render_with_reading_colors_off", "crates/render/src/raster/tests.rs::reading_colors_keep_highlight_annotations_readable", "crates/ui-egui/tests/it/reading_colors.rs::reading_colours_preference_round_trips", "crates/ui-egui/tests/it/reading_colors.rs::reading_colours_seed_on_open", "crates/ui-egui/tests/it/reading_colors.rs::pending_pages_use_document_reading_background"]
notes = "View only, off by default: Preferences ▸ Reading colours holds the app-wide preference, seeded into every document as it opens (including created ones), and view_reading_colors reads and sets it per document (Acrobat's Replace Document Colors). Solid text paints and, by default, line-art paints (including stencil-mask image paints) are remapped by luminance — each lerps between the preset's text and background colour by how dark or light the original paint is — while images, shadings and tiling patterns pass through untouched. Annotations keep the colours they were given (both /AP appearances and the no-/AP Highlight fallback), so a Multiply highlight over a dark background shows the marked-up words in the highlight's colour instead of a flat bar that hides them (#1548). Presets: black on white, white on black, yellow on black, green on black, or a custom #RRGGBB pair; only_black limits the mapping to near-black paints (a 10% luminance threshold — our choice; Acrobat's was not observed). Display only: the document is not changed and print and export are unaffected; print previews and view snapshots render with the document's true colours (a preview matches the uncoloured printout, and a snapshot is an export surface), so reading colours apply to the reading view only. Pending canvas pages use the configured reading background before their raster arrives (#2059). Closes #897 (duplicate #488)."
[[feature]]
@@ -10012,7 +10012,7 @@ milestone = "M14"
status = "partial"
commands = ["app.preferences"]
tools = []
evidence = ["crates/ui-egui/src/commands.rs::localized_shortcut_labels_follow_language_and_platform", "crates/ui-egui/src/i18n/mod.rs::keyboard_key_names_have_a_separate_context_and_english_fallback", "crates/ui-egui/tests/it/commands.rs::localized_shortcuts_update_immediately_and_keep_command_ids_and_key_bindings", "crates/ui-egui/src/i18n/mod.rs::windows_display_language_resolves_chinese_scripts", "crates/ui-egui/src/i18n/mod.rs::neutral_locale_does_not_override_display_language", "crates/ui-egui/src/i18n/mod.rs::windows_display_language_query_returns_a_locale_tag", "crates/ui-egui/src/i18n/mod.rs::language_persists_and_invalid_input_keeps_current_language", "crates/ui-egui/src/i18n/mod.rs::bundled_catalogs_are_consistent", "crates/ui-egui/src/i18n/mod.rs::arabic_is_registered_and_persists", "crates/ui-egui/src/i18n/mod.rs::arabic_catalog_stays_logical", "crates/ui-egui/src/i18n/mod.rs::arabic_plurals_follow_the_six_cldr_forms", "crates/ui-egui/src/i18n/mod.rs::arabic_labels_have_glyphs", "crates/ui-egui/src/bidi.rs::arabic_ui_labels_use_an_explicit_paragraph_direction", "crates/ui-egui/src/theme.rs::arabic_chrome_is_right_to_left_after_the_language_changes", "crates/ui-egui/tests/it/control.rs::simplified_chinese_signature_prompts_and_errors_keep_document_state", "crates/ui-egui/tests/it/control.rs::language_switch_preserves_document_and_command_ids", "crates/ui-egui/tests/it/control.rs::japanese_controls_and_search_keep_command_ids", "crates/ui-egui/tests/it/control.rs::japanese_dialogs_errors_and_custom_action_names", "crates/ui-egui/src/i18n/mod.rs::french_is_registered", "crates/ui-egui/src/widgets.rs::search_placeholder_never_overlaps_the_shortcut", "crates/ui-egui/src/home.rs::translated_card_description_leaves_room_for_the_action", "crates/ui-egui/tests/it/control.rs::german_preferences_and_search_keep_command_ids", "crates/ui-egui/src/i18n/mod.rs::telugu_labels_have_glyphs", "crates/ui-egui/src/i18n/mod.rs::hungarian_is_registered", "crates/ui-egui/src/i18n/mod.rs::ukrainian_is_registered_and_persists", "crates/ui-egui/src/i18n/mod.rs::ukrainian_integer_plurals_cover_teens_and_large_counts", "crates/ui-egui/src/i18n/mod.rs::spanish_keeps_the_holes_in_what_the_engine_says", "crates/print/src/tests.rs::wordings_are_said", "crates/ui-egui/src/i18n/mod.rs::norwegian_bokmal_is_registered_and_persists"]
evidence = ["crates/ui-egui/src/commands.rs::localized_shortcut_labels_follow_language_and_platform", "crates/ui-egui/src/i18n/tests.rs::keyboard_key_names_have_a_separate_context_and_english_fallback", "crates/ui-egui/tests/it/commands.rs::localized_shortcuts_update_immediately_and_keep_command_ids_and_key_bindings", "crates/ui-egui/src/i18n/tests.rs::windows_display_language_resolves_chinese_scripts", "crates/ui-egui/src/i18n/tests.rs::neutral_locale_does_not_override_display_language", "crates/ui-egui/src/i18n/tests.rs::windows_display_language_query_returns_a_locale_tag", "crates/ui-egui/src/i18n/tests.rs::language_persists_and_invalid_input_keeps_current_language", "crates/ui-egui/src/i18n/tests.rs::bundled_catalogs_are_consistent", "crates/ui-egui/src/i18n/tests.rs::arabic_is_registered_and_persists", "crates/ui-egui/src/i18n/tests.rs::arabic_catalog_stays_logical", "crates/ui-egui/src/i18n/tests.rs::arabic_plurals_follow_the_six_cldr_forms", "crates/ui-egui/src/i18n/tests.rs::arabic_labels_have_glyphs", "crates/ui-egui/src/bidi/tests.rs::arabic_ui_labels_use_an_explicit_paragraph_direction", "crates/ui-egui/src/theme/tests.rs::arabic_chrome_is_right_to_left_after_the_language_changes", "crates/ui-egui/tests/it/control.rs::simplified_chinese_signature_prompts_and_errors_keep_document_state", "crates/ui-egui/tests/it/control.rs::language_switch_preserves_document_and_command_ids", "crates/ui-egui/tests/it/control.rs::japanese_controls_and_search_keep_command_ids", "crates/ui-egui/tests/it/control.rs::japanese_dialogs_errors_and_custom_action_names", "crates/ui-egui/src/i18n/tests.rs::french_is_registered", "crates/ui-egui/src/widgets.rs::search_placeholder_never_overlaps_the_shortcut", "crates/ui-egui/src/home.rs::translated_card_description_leaves_room_for_the_action", "crates/ui-egui/tests/it/control.rs::german_preferences_and_search_keep_command_ids", "crates/ui-egui/src/i18n/tests.rs::telugu_labels_have_glyphs", "crates/ui-egui/src/i18n/tests.rs::hungarian_is_registered", "crates/ui-egui/src/i18n/tests.rs::ukrainian_is_registered_and_persists", "crates/ui-egui/src/i18n/tests.rs::ukrainian_integer_plurals_cover_teens_and_large_counts", "crates/ui-egui/src/i18n/tests.rs::spanish_keeps_the_holes_in_what_the_engine_says", "crates/print/src/tests.rs::wordings_are_said", "crates/ui-egui/src/i18n/tests.rs::norwegian_bokmal_is_registered_and_persists"]
notes = "Shortcut modifiers and key names use a dedicated catalog context across toolbar, menus, palette, Help and tooltips, with English fallback and macOS symbols preserved. Preferences switches Auto (system language)/English/Japanese/Simplified Chinese/Traditional Chinese/Czech/Brazilian Portuguese/German/Spanish/French/Telugu/Hungarian/Ukrainian/Italian/Arabic/Norwegian Bokmål and persists; ui.set language auto|en|ja|zh-hans|zh-hant|cs|pt-br|de|es|fr|te|hu|uk|it|ar|nb and ui.state expose it. TSV catalogs per language (same system as PhotoCraft); every dialog, panel and notice goes through tl!. Japanese, Traditional Chinese, Simplified Chinese, German, Spanish, French and Hungarian cover commands, dialogs, panels, shortcuts and history labels; Brazilian Portuguese covers the current union of catalog keys, including signature and accessibility messages; Czech remains limited to menus. French locales (fr, fr_FR, fr_CA, fr_BE) select the French catalog, German locales (de, de_DE, de_AT, de_CH) the German one, Hungarian locales (hu, hu_HU) the Hungarian one, and Norwegian locales (nb, nb_NO, and no/nn until a Nynorsk catalog exists) the Bokmål one. German search hints reserve shortcut space, Home cards bound translated text, certificate Subject has its own context, and inserted tool names keep their capitalization. The sentences the layers below word for themselves (accessibility findings and the saved report, PDF/A problems, signature details, page-range mistakes, edit labels) are handed over apart from the document's own values and go through the catalog too, through said(&i18n::words); Spanish, Italian and Brazilian Portuguese translate them, Polish the signature details and the changes after signing; strings absent from other catalogs fall back to English. OS error details, and faults rather than mistakes, stay as they are. Auto detects the Windows display language after explicitly named supported locale overrides; neutral C/POSIX launcher locales are skipped on Windows, with Chinese script/region mapping and English fallback. Arabic (ar and every ar-* locale) covers commands, dialogs and panels with the six CLDR plural forms. Catalogs stay logical and egui 0.37's epaint applies UAX #9 reordering; Arabic and Kurdish labels take an explicit right-to-left paragraph direction and stock chrome is mirrored through Style::right_to_left. Missing: native and browser end-to-end RTL input, IME and complex bidirectional workflows are not yet qualified; Czech dialogs, Chinese and Telugu font delivery (native release builds embed LXGW WenKai Lite for Simplified Chinese, while the web build has no Chinese face beyond BIZ UDPGothic and the release-pinned craft-fonts has no Telugu face), the engine's sentences in the other catalogs, a full localization audit."
[[feature]]
@@ -10144,7 +10144,7 @@ milestone = "M14"
status = "partial"
commands = []
tools = []
evidence = ["crates/ui-egui/src/i18n/mod.rs::bundled_catalogs_are_consistent", "crates/ui-egui/src/i18n/mod.rs::date_languages_are_the_interface_languages", "crates/ui-egui/src/i18n/mod.rs::language_persists_and_invalid_input_keeps_current_language"]
evidence = ["crates/ui-egui/src/i18n/tests.rs::bundled_catalogs_are_consistent", "crates/ui-egui/src/i18n/tests.rs::date_languages_are_the_interface_languages", "crates/ui-egui/src/i18n/tests.rs::language_persists_and_invalid_input_keeps_current_language"]
notes = "The UI offers 22 non-English catalogs plus English fallback (entry counts vary by language), and the selected language persists. Partial because the languages are not all usable yet: Simplified Chinese has a native release LXGW WenKai Lite face but the web build has no Hans face (#826, #688), the release-pinned craft-fonts has no Telugu face, Arabic labels stay logical with explicit right-to-left direction and mirrored stock chrome while native/browser/IME RTL workflows are not yet qualified (misc.rtl-ui), and strings missing from a catalog fall back to English (localization-parity.md). The language can be set through the UI control channel (`set language`) but not through an automation tool, so pdfcraft-cli run and MCP cannot change it."
[[feature]]