mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-29 12:38:58 -06:00
Layers panel: Reset to Initial Visibility
Layer options in the Layers panel header resets every layer to the state the file's default configuration (/OCProperties /D) gives it, as Acrobat does. Visibility is view state, so the reset doesn't mark the document changed and doesn't enter Undo. doc_info now reports each layer's initial state.
This commit is contained in:
+22
-22
@@ -7,7 +7,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Greek UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "6eee0b34db6529f26235bfc07fa1fdb9030b8e7b0b10ede271ca9242cb491919"
|
||||
sha256 = "85100503b2b0b30b72927783a8c2a4c601f88b69c9d83a51426aaa548ecc919e"
|
||||
|
||||
# ATTRIBUTION.toml: one entry per asset (AGENTS.md §1). The source of truth for provenance and licences.
|
||||
# ATTRIBUTION.md is generated from this file: `cargo xtask assets --write`. Checked by `cargo xtask assets` (part of `xtask ci`).
|
||||
@@ -30,7 +30,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Simplified Chinese UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "9870c0fb9f7198155be16adee7f45d36cb9a254af90b545a3ce5b04cbbbf24d4"
|
||||
sha256 = "d24435fe507f4a466a04a03e0ded2fa174cc66382d669f67cdfbdf1637b45e39"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/zh-hant.tsv"
|
||||
@@ -41,7 +41,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Traditional Chinese UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "fc31e40877ce115cfb7033c57dd6acabcae3725b249364387e3fe23e04d94e5e"
|
||||
sha256 = "dba5335f7185bc07036abec5630038bd050a89402145a1a9a1636f8bcb622192"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/cs.tsv"
|
||||
@@ -52,7 +52,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Czech UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "5de19834f293efaea68cb6b7f51a71d320391f53345f56cfebcd73dab828b665"
|
||||
sha256 = "db22bb0649ab8922d7a6d882548037f9cd134329aee9047172ef405063e0a42a"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/pt-br.tsv"
|
||||
@@ -63,7 +63,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Brazilian Portuguese UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "cef0d22341d0722f029c6543fc846fce164331b7ad8f693a6754b7567b9d9e95"
|
||||
sha256 = "86f72cce105dfad308eb67dee2a46f15319303a6ef45ddbe9f09a04c48b8577d"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/es.tsv"
|
||||
@@ -74,7 +74,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Spanish UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "c0ca7761cbabd8096bc9af54cdf6b88b3ae0cd8210003db8b3356e2706e44fc8"
|
||||
sha256 = "167cfb16e67622356a0563157904193d9af891dbea69a99094ff18f5ebfec7d4"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/fr.tsv"
|
||||
@@ -85,7 +85,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "French UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "a8afd51c3296f44b0446d6b06492b1348444b3e3c448a326ad213d18ce7af714"
|
||||
sha256 = "09acccff04e4c4fe8288787eeacffa58fd63b57f3c93f512a6d7f28a62228bb5"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/de.tsv"
|
||||
@@ -96,7 +96,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "German UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "7676a2dfaa7d66e2e9ef608d2d9de7f863557dcecbef6d94bafa981097405e6d"
|
||||
sha256 = "61c333edf6b96cbd428616c427f5921d7bde9f780e06b759adcf609ebe9e3584"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/te.tsv"
|
||||
@@ -107,7 +107,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Telugu UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "9a22996c0308ea5b20ec5e328504cd83cbb560147763faaf0e6987f974844c29"
|
||||
sha256 = "81147bf7c3a0e1651c862a74da86d91347ee27166a4e9817c1bf4484b2f6da33"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/it.tsv"
|
||||
@@ -118,7 +118,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Italian UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "eacc972d65a2d951e94e1a8cf944638e8328f73f6c05759de6059a425f159cd7"
|
||||
sha256 = "3a6400e7009e4d69ac014954a169055c26b95244d5b6acef68c7023bee55ed21"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/pl.tsv"
|
||||
@@ -129,7 +129,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Polish UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "dfa4aa540cc7d61fa22a27a949e6eec0f78dcc5b883d0e4e004d695e460b95aa"
|
||||
sha256 = "5c67bd8796d3de37097ee0e70baeaceaa9dbf5fba830c01237561893587c9cee"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/ru.tsv"
|
||||
@@ -140,7 +140,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Russian UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "27e02e7bb3d378bceca76988a9dceaa68d974f57cbee962954deca6184cf62aa"
|
||||
sha256 = "f7297ea2bc5bd8fc74900bda036141f71cfdebfd7d2ad60528bd3ba989a9e27e"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/hu.tsv"
|
||||
@@ -151,7 +151,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Hungarian UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "9675edd36c5ae54d9893fd10c4325aab664941bf714c08fbfeef9134fa42056c"
|
||||
sha256 = "21cbdc6ba7df33318e49c6223baa3103c8b9c64c76c58ba6221b904a4a6090bc"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/bg.tsv"
|
||||
@@ -162,7 +162,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Bulgarian UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "ce18fc96f8516aa917e4a8bcfd290edec13a9800b3d1ee102e4b2a71b8467d2b"
|
||||
sha256 = "85b7aa8f198de76c7045ffc4864e094717131f9256bc9fccbc4e3e44786eb254"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/kk.tsv"
|
||||
@@ -173,7 +173,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Kazakh UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "286a1e2e051748eb4bcee3906918dc78d0704f8a37bde22dc50d3376d5e75017"
|
||||
sha256 = "b403a9907434e35705c8062b1a0feb1eefc20c28cae3ca101f6d3877717e80a7"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/ja.tsv"
|
||||
@@ -184,7 +184,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Japanese UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "77593b7497c67f4a79cc31fa43fdd6eae11601dd87c079a8e666b25cdd9117ba"
|
||||
sha256 = "8cabeb2cf2ea5cd1d937739f33a785ab4221a3b3249e1a2b4dafe48c4ef425dd"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/uk.tsv"
|
||||
@@ -195,7 +195,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Ukrainian UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "d7d93a03f22ab380696f69b20b12104bf30e78c4440b683df63594af716894e2"
|
||||
sha256 = "8592c561daeb872215ef5f1615a864bfcd93a80952d84cc2a002fc6112aa8085"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/ar.tsv"
|
||||
@@ -206,7 +206,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Arabic UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "889a0d0a9ea53ca0a818c2ba672c053c232d2cca140f8d63127353e6f36505fe"
|
||||
sha256 = "d1ba0e543c058d5500c47fd90df6c503b43572937d1f8245e88b6334a688fbdf"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/tr.tsv"
|
||||
@@ -217,7 +217,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Turkish UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "bae8b21651ae2a69c71c91b69ba79727254dbfb078c1be312a13383332934927"
|
||||
sha256 = "cbfcbb27e0de3a3952698d7c49c5a449602e86ad7a2c5ceb25f4f26af41a0975"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/lt.tsv"
|
||||
@@ -228,7 +228,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Lithuanian UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "7fa70f68a9fa24d0b1cac05fe3aaabaeaca9750c57416124bd317a4eda806d5a"
|
||||
sha256 = "f4411c01d9d8df98944da540ef8bcf0d700201bd78eb3595d23862fdab210807"
|
||||
|
||||
|
||||
[[asset]]
|
||||
@@ -3352,7 +3352,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Vietnamese UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "95b5fc6d10f7bc8371688772741870448de716fa2dd9af62fdfe4e009d269b45"
|
||||
sha256 = "8f887d8c969cdd3e9e2dd95fa7de7b8bf683297f0edee3141fb1cdcd73243df0"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/sl.tsv"
|
||||
@@ -3363,7 +3363,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Slovene UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "3ac2b113045b7e399dd6d9ddb7773e5b0874c8b43c2ba4cca5854598a2a7ba92"
|
||||
sha256 = "cd7b0bd7075e4497d1e74283877d99f9ca4b9d608290ee2301aed9757ac19b83"
|
||||
|
||||
[[asset]]
|
||||
|
||||
|
||||
Generated
+1
@@ -4131,6 +4131,7 @@ dependencies = [
|
||||
"pdfcraft-redact",
|
||||
"pdfcraft-render",
|
||||
"pdfcraft-sign",
|
||||
"pdfcraft-testkit",
|
||||
"pdfcraft-xfa",
|
||||
"pdfcraft-xfdf",
|
||||
"png 0.18.1",
|
||||
|
||||
@@ -2466,7 +2466,7 @@ fn info(d: &Document) -> Value {
|
||||
pdfcraft_render::LinkTarget::Other(o) => json!({ "other": o }),
|
||||
},
|
||||
})).collect::<Vec<_>>(),
|
||||
"layers": i.layers.iter().map(|l| json!({ "name": l.name, "visible": l.visible })).collect::<Vec<_>>(),
|
||||
"layers": i.layers.iter().map(|l| json!({ "name": l.name, "visible": l.visible, "initial": l.initial })).collect::<Vec<_>>(),
|
||||
// Full-screen presentation settings a page carries (ISO 32000-2 §12.4.4): only pages with
|
||||
// a `/Trans` or `/Dur` appear; `style` is null for a display time alone.
|
||||
"transitions": i.transitions.iter().map(|t| json!({
|
||||
|
||||
@@ -5638,12 +5638,13 @@ fn doc_info_says_when_a_runaway_script_turned_the_forms_scripts_off() {
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
/// doc_info describes a link's set-layer-visibility action by layer name.
|
||||
/// doc_info describes a link's set-layer-visibility action by layer name, and each layer's
|
||||
/// visibility now and as the file opens.
|
||||
#[test]
|
||||
fn doc_info_describes_set_layer_links() {
|
||||
let dir = workdir("layer-links");
|
||||
let objs = [
|
||||
"<< /Type /Catalog /Pages 2 0 R /OCProperties << /OCGs [4 0 R 5 0 R] /D << >> >> >>",
|
||||
"<< /Type /Catalog /Pages 2 0 R /OCProperties << /OCGs [4 0 R 5 0 R] /D << /OFF [5 0 R] >> >> >>",
|
||||
"<< /Type /Pages /Kids [3 0 R] /Count 1 >>",
|
||||
"<< /Type /Page /Parent 2 0 R /MediaBox [0 0 200 200] /Annots [6 0 R] >>",
|
||||
"<< /Type /OCG /Name (Red) >>",
|
||||
@@ -5674,6 +5675,7 @@ fn doc_info_describes_set_layer_links() {
|
||||
"preserve_rb": false,
|
||||
})
|
||||
);
|
||||
assert_eq!(info["layers"], json!([{ "name": "Red", "visible": true, "initial": true }, { "name": "Green", "visible": false, "initial": false }]));
|
||||
}
|
||||
|
||||
mod close_argument_tests {
|
||||
|
||||
@@ -47,3 +47,6 @@ unicode-segmentation = "1.13"
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
# Already in the graph through lopdf; used only for the local UTC offset ("today").
|
||||
chrono = { version = "0.4", default-features = false, features = ["clock"] }
|
||||
|
||||
[dev-dependencies]
|
||||
pdfcraft-testkit = { workspace = true }
|
||||
|
||||
@@ -4785,6 +4785,22 @@ impl Session {
|
||||
true
|
||||
}
|
||||
|
||||
/// Show each layer as the file opens (Acrobat's Layers ▸ Reset to Initial Visibility). Returns
|
||||
/// `true` if any layer changed; callers must drop cached rasters and text for the document,
|
||||
/// as for [`Self::set_layer_visible`].
|
||||
pub fn reset_layers(&mut self, id: DocId) -> bool {
|
||||
let Some(doc) = self.docs.iter_mut().find(|d| d.id == id) else { return false };
|
||||
let mut changed = false;
|
||||
for l in &mut doc.info.layers {
|
||||
changed |= l.visible != l.initial;
|
||||
l.visible = l.initial;
|
||||
}
|
||||
if changed {
|
||||
use_layer_choices(doc);
|
||||
}
|
||||
changed
|
||||
}
|
||||
|
||||
/// Hide or show every comment on the page (Acrobat's Comments ▸ Hide all comments). Form
|
||||
/// fields and links still draw. Returns whether anything changed.
|
||||
pub fn set_hide_comments(&mut self, id: DocId, hide: bool) -> bool {
|
||||
|
||||
@@ -685,7 +685,7 @@ fn layer_choices_survive_edits() {
|
||||
#[test]
|
||||
fn set_layer_actions_toggle_in_order_and_keep_radio_groups() {
|
||||
use pdfcraft_render::LayerOp::{Off, On, Toggle};
|
||||
let layer = |n: u32, visible: bool| Layer { id: (n, 0), name: format!("L{n}"), visible };
|
||||
let layer = |n: u32, visible: bool| Layer { id: (n, 0), name: format!("L{n}"), visible, initial: visible };
|
||||
let run = |changes: &[(LayerOp, u32)], preserve_rb: bool| {
|
||||
// L1 and L2 are a radio-button group; L3 is on its own.
|
||||
let mut layers = vec![layer(1, true), layer(2, false), layer(3, true)];
|
||||
@@ -761,6 +761,59 @@ fn set_layer_actions_change_what_renders() {
|
||||
assert!(!s.set_layer_state(DocId(999), &[(LayerOp::On, (5, 0))], true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reset_layers_returns_to_the_files_visibility() {
|
||||
// Red (left half, on) and Green (right half, off) are a radio-button group.
|
||||
let content = "/OC /R BDC 1 0 0 rg 0 0 50 100 re f EMC /OC /G BDC 0 1 0 rg 50 0 50 100 re f EMC";
|
||||
let pdf = pdfcraft_testkit::Pdf::new()
|
||||
.obj("<< /Type /Catalog /Pages 2 0 R /OCProperties << /OCGs [5 0 R 6 0 R] /D << /OFF [6 0 R] /RBGroups [[5 0 R 6 0 R]] >> >> >>")
|
||||
.obj("<< /Type /Pages /Kids [3 0 R] /Count 1 >>")
|
||||
.obj("<< /Type /Page /Parent 2 0 R /MediaBox [0 0 100 100] /Contents 4 0 R /Resources << /Properties << /R 5 0 R /G 6 0 R >> >> >>")
|
||||
.stream("", content)
|
||||
.obj("<< /Type /OCG /Name (Red) >>")
|
||||
.obj("<< /Type /OCG /Name (Green) >>")
|
||||
.build();
|
||||
// A page drawing on layer Blue, which that file has off.
|
||||
let other = pdfcraft_testkit::Pdf::new()
|
||||
.obj("<< /Type /Catalog /Pages 2 0 R /OCProperties << /OCGs [5 0 R] /D << /OFF [5 0 R] >> >> >>")
|
||||
.obj("<< /Type /Pages /Kids [3 0 R] /Count 1 >>")
|
||||
.obj("<< /Type /Page /Parent 2 0 R /MediaBox [0 0 100 100] /Contents 4 0 R /Resources << /Properties << /B 5 0 R >> >> >>")
|
||||
.stream("", "/OC /B BDC 0 0 1 rg 0 0 10 10 re f EMC")
|
||||
.obj("<< /Type /OCG /Name (Blue) >>")
|
||||
.build();
|
||||
let mut s = Session::new();
|
||||
let id = s.open("layers.pdf", None, Arc::new(pdf), None).unwrap();
|
||||
let shown = |s: &Session| s.get(id).unwrap().info.layers.iter().map(|l| (l.name.clone(), l.visible)).collect::<Vec<_>>();
|
||||
let left = |s: &Session| {
|
||||
let doc = s.get(id).unwrap();
|
||||
let mut r = pdfcraft_render::PageRenderer::new(doc.bytes.clone(), doc.config.clone());
|
||||
let p = r.render(pdfcraft_render::RenderRequest { page: 0, kind: pdfcraft_render::RequestKind::Pixels, scale: 1.0, ..Default::default() });
|
||||
let i = (50 * p.width as usize + 25) * 4;
|
||||
p.rgba[i..i + 3].to_vec()
|
||||
};
|
||||
assert!(s.set_layer_state(id, &[(LayerOp::On, (6, 0))], true));
|
||||
let edit = Edit::InsertPagesFrom {
|
||||
name: "b.pdf".into(),
|
||||
bytes: Arc::new(other),
|
||||
pages: None,
|
||||
at: 1,
|
||||
field_conflicts: pdfcraft_organize::FieldConflicts::Rename,
|
||||
};
|
||||
s.apply(id, edit).unwrap();
|
||||
let blue = s.get(id).unwrap().info.layers.iter().position(|l| l.name == "Blue").unwrap();
|
||||
assert!(s.set_layer_visible(id, blue, true));
|
||||
assert_eq!(shown(&s), [("Red".into(), false), ("Green".into(), true), ("Blue".into(), true)]);
|
||||
assert_eq!(left(&s), [255, 255, 255]);
|
||||
// Every layer, the inserted one too, goes back to the file's default configuration.
|
||||
assert!(s.reset_layers(id));
|
||||
assert_eq!(shown(&s), [("Red".into(), true), ("Green".into(), false), ("Blue".into(), false)]);
|
||||
assert_eq!(left(&s), [255, 0, 0]);
|
||||
assert!(!s.reset_layers(id), "already as the file opens");
|
||||
// What the eye sees is not an edit: Undo still undoes the insert.
|
||||
assert_eq!(s.get(id).unwrap().can_undo(), Some("Insert pages from b.pdf"));
|
||||
assert!(!s.reset_layers(DocId(999)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn edits_to_unknown_documents_are_rejected() {
|
||||
let (mut s, _) = session_with(1);
|
||||
|
||||
@@ -425,6 +425,8 @@ pub struct Layer {
|
||||
pub id: (u32, u16),
|
||||
pub name: String,
|
||||
pub visible: bool,
|
||||
/// Shown as the file opens: the default configuration (`/OCProperties /D`).
|
||||
pub initial: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -1428,7 +1430,7 @@ impl<'a> Inspector<'a> {
|
||||
_ => !base_off,
|
||||
};
|
||||
let Some(id) = id else { continue };
|
||||
out.push(Layer { id, name: self.text(d, b"Name").unwrap_or_else(|| "Layer".into()), visible });
|
||||
out.push(Layer { id, name: self.text(d, b"Name").unwrap_or_else(|| "Layer".into()), visible, initial: visible });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2358,6 +2360,24 @@ trailer << /Root 1 0 R /Info 4 0 R >>
|
||||
assert_eq!(info.layer_groups, [vec![(5, 0), (6, 0)], vec![(6, 0), (7, 0)]]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn initial_layer_visibility_is_the_default_configuration() {
|
||||
// BaseState OFF: Red is listed ON; Green is listed both ON and OFF, and OFF wins; Blue
|
||||
// isn't listed, so it takes the base state.
|
||||
let pdf = b"%PDF-1.7
|
||||
1 0 obj << /Type /Catalog /Pages 2 0 R /OCProperties << /OCGs [5 0 R 6 0 R 7 0 R] /D << /BaseState /OFF /ON [5 0 R 6 0 R] /OFF [6 0 R] >> >> >> endobj
|
||||
2 0 obj << /Type /Pages /Kids [3 0 R] /Count 1 >> endobj
|
||||
3 0 obj << /Type /Page /Parent 2 0 R /MediaBox [0 0 200 200] >> endobj
|
||||
5 0 obj << /Type /OCG /Name (Red) >> endobj
|
||||
6 0 obj << /Type /OCG /Name (Green) >> endobj
|
||||
7 0 obj << /Type /OCG /Name (Blue) >> endobj
|
||||
trailer << /Root 1 0 R >>
|
||||
%%EOF";
|
||||
let info = inspect(Arc::new(pdf.to_vec()), None).expect("opens");
|
||||
let layers: Vec<_> = info.layers.iter().map(|l| (l.name.as_str(), l.initial)).collect();
|
||||
assert_eq!(layers, [("Red", true), ("Green", false), ("Blue", false)]);
|
||||
}
|
||||
|
||||
const ORPHAN_FIELDS: &[u8] = b"%PDF-1.7
|
||||
1 0 obj << /Type /Catalog /Pages 2 0 R /AcroForm 4 0 R >> endobj
|
||||
2 0 obj << /Type /Pages /Kids [3 0 R] /Count 1 >> endobj
|
||||
|
||||
@@ -1332,6 +1332,8 @@
|
||||
This document has no layers. هذا المستند لا يحتوي على طبقات.
|
||||
This document has no attachments. هذا المستند لا يحتوي على مرفقات.
|
||||
Hide layer إخفاء الطبقة
|
||||
Layer options خيارات الطبقات
|
||||
Reset to Initial Visibility إعادة تعيين إلى الرؤية الأولية
|
||||
Show layer إظهار الطبقة
|
||||
Set layer visibility ضبط ظهور الطبقة
|
||||
on page {label} في الصفحة {label}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -1274,6 +1274,8 @@
|
||||
This document has no layers. Този документ няма слоеве.
|
||||
This document has no attachments. Този документ няма прикачени файлове.
|
||||
Hide layer Скрий слоя
|
||||
Layer options Опции за слоеве
|
||||
Reset to Initial Visibility Възстанови първоначалната видимост
|
||||
Show layer Покажи слоя
|
||||
Set layer visibility Задай видимост на слоя
|
||||
on page {label} на страница {label}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 8 and column 16.
|
@@ -1400,6 +1400,8 @@ key Space Mezerník
|
||||
This document has no layers. Tento dokument nemá žádné vrstvy.
|
||||
This document has no attachments. Tento dokument nemá žádné přílohy.
|
||||
Hide layer Skrýt vrstvu
|
||||
Layer options Možnosti vrstev
|
||||
Reset to Initial Visibility Obnovit počáteční viditelnost
|
||||
Show layer Zobrazit vrstvu
|
||||
Set layer visibility Nastavit viditelnost vrstvy
|
||||
on page {label} na stránce {label}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -1318,6 +1318,8 @@
|
||||
This document has no layers. Dieses Dokument hat keine Ebenen.
|
||||
This document has no attachments. Dieses Dokument hat keine Anhänge.
|
||||
Hide layer Ebene ausblenden
|
||||
Layer options Ebenenoptionen
|
||||
Reset to Initial Visibility Ursprüngliche Sichtbarkeit wiederherstellen
|
||||
Show layer Ebene einblenden
|
||||
on page {label} auf Seite {label}
|
||||
Save attachment… Anhang speichern…
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 51.
|
@@ -363,6 +363,8 @@
|
||||
Distribute Κατανομή
|
||||
Show layer Εμφάνιση επιπέδου
|
||||
Hide layer Απόκρυψη επιπέδου
|
||||
Layer options Επιλογές επιπέδων
|
||||
Reset to Initial Visibility Επαναφορά αρχικής ορατότητας
|
||||
Collate Συρραφή αντιγράφων
|
||||
Fit to page Προσαρμογή στη σελίδα
|
||||
Reverse pages Αντιστροφή σειράς σελίδων
|
||||
|
||||
|
@@ -1327,6 +1327,8 @@
|
||||
This document has no layers. Este documento no tiene capas.
|
||||
This document has no attachments. Este documento no tiene archivos adjuntos.
|
||||
Hide layer Ocultar capa
|
||||
Layer options Opciones de capas
|
||||
Reset to Initial Visibility Restablecer visibilidad inicial
|
||||
Show layer Mostrar capa
|
||||
Set layer visibility Definir visibilidad de capas
|
||||
on page {label} en la página {label}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 60.
|
@@ -1313,6 +1313,8 @@
|
||||
This document has no layers. Ce document ne contient aucun calque.
|
||||
This document has no attachments. Ce document ne contient aucune pièce jointe.
|
||||
Hide layer Masquer le calque
|
||||
Layer options Options des calques
|
||||
Reset to Initial Visibility Rétablir la visibilité initiale
|
||||
Show layer Afficher le calque
|
||||
on page {label} à la page {label}
|
||||
Save attachment… Enregistrer la pièce jointe…
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 39.
|
@@ -1303,6 +1303,8 @@
|
||||
This document has no layers. Ebben a dokumentumban nincs réteg.
|
||||
This document has no attachments. Ebben a dokumentumban nincs melléklet.
|
||||
Hide layer Réteg elrejtése
|
||||
Layer options Réteg beállításai
|
||||
Reset to Initial Visibility Kezdeti láthatóság visszaállítása
|
||||
Show layer Réteg megjelenítése
|
||||
on page {label} {label}. oldal
|
||||
Save attachment… Melléklet mentése…
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 7 and column 44.
|
@@ -1328,6 +1328,8 @@
|
||||
This document has no layers. Questo documento non ha livelli.
|
||||
This document has no attachments. Questo documento non ha allegati.
|
||||
Hide layer Nascondi livello
|
||||
Layer options Opzioni livelli
|
||||
Reset to Initial Visibility Ripristina visibilità iniziale
|
||||
Show layer Mostra livello
|
||||
Set layer visibility Imposta visibilità livello
|
||||
on page {label} a pagina {label}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -1333,6 +1333,8 @@
|
||||
This document has no layers. この文書にレイヤーはありません。
|
||||
This document has no attachments. この文書に添付ファイルはありません。
|
||||
Hide layer レイヤーを非表示
|
||||
Layer options レイヤーのオプション
|
||||
Reset to Initial Visibility 初期表示状態にリセット
|
||||
Show layer レイヤーを表示
|
||||
Set layer visibility レイヤーの表示を設定
|
||||
on page {label} ページ {label}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -1275,6 +1275,8 @@
|
||||
This document has no layers. Бұл құжатта қабаттар жоқ.
|
||||
This document has no attachments. Бұл құжатта тіркемелер жоқ.
|
||||
Hide layer Қабатты жасыру
|
||||
Layer options Қабат параметрлері
|
||||
Reset to Initial Visibility Бастапқы көрінуді қалпына келтіру
|
||||
Show layer Қабатты көрсету
|
||||
Set layer visibility Қабаттың көрінісін орнату
|
||||
on page {label} {label} бетінде
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 826 and column 9.
|
@@ -629,6 +629,8 @@
|
||||
Height: Aukštis:
|
||||
Hide field highlights Slėpti laukų paryškinimą
|
||||
Hide layer Slėpti sluoksnį
|
||||
Layer options Sluoksnių parinktys
|
||||
Reset to Initial Visibility Atkurti pradinį matomumą
|
||||
Hide menu bar Slėpti meniu juostą
|
||||
Hide password Slėpti slaptažodį
|
||||
Hide toolbars Slėpti įrankių juostas
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 404 and column 9.
|
@@ -1318,6 +1318,8 @@
|
||||
This document has no layers. Ten dokument nie zawiera warstw.
|
||||
This document has no attachments. Ten dokument nie zawiera załączników.
|
||||
Hide layer Ukryj warstwę
|
||||
Layer options Opcje warstw
|
||||
Reset to Initial Visibility Przywróć początkową widoczność
|
||||
Show layer Pokaż warstwę
|
||||
Set layer visibility Ustaw widoczność warstwy
|
||||
on page {label} na stronie {label}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -1218,6 +1218,8 @@
|
||||
This document has no layers. Este documento não tem camadas.
|
||||
This document has no attachments. Este documento não tem anexos.
|
||||
Hide layer Ocultar camada
|
||||
Layer options Opções de camadas
|
||||
Reset to Initial Visibility Redefinir visibilidade inicial
|
||||
Show layer Mostrar camada
|
||||
on page {label} na página {label}
|
||||
Save attachment… Salvar anexo…
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 9 and column 16.
|
@@ -1333,6 +1333,8 @@
|
||||
This document has no layers. В этом документе нет слоёв.
|
||||
This document has no attachments. В этом документе нет вложений.
|
||||
Hide layer Скрыть слой
|
||||
Layer options Параметры слоёв
|
||||
Reset to Initial Visibility Восстановить исходную видимость
|
||||
Show layer Показать слой
|
||||
Set layer visibility Задать видимость слоя
|
||||
on page {label} на стр. {label}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 9 and column 16.
|
@@ -902,6 +902,8 @@
|
||||
Hide all comments Skrij vse komentarje
|
||||
Hide field highlights Skrij osvetlitev polj
|
||||
Hide layer Skrij plast
|
||||
Layer options Možnosti plasti
|
||||
Reset to Initial Visibility Ponastavi na začetno vidnost
|
||||
Hide menu bar Skrij menijsko vrstico
|
||||
Hide password Skrij geslo
|
||||
Hide toolbars Skrij orodne vrstice
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 9 and column 16.
|
@@ -1303,6 +1303,8 @@ signature pad Type టైప్ చేయి
|
||||
This document has no layers. ఈ పత్రంలో లేయర్లు లేవు.
|
||||
This document has no attachments. ఈ పత్రంలో జోడింపులు లేవు.
|
||||
Hide layer లేయర్ను దాచు
|
||||
Layer options లేయర్ ఎంపికలు
|
||||
Reset to Initial Visibility ప్రారంభ దృశ్యతకు రీసెట్ చేయి
|
||||
Show layer లేయర్ను చూపించు
|
||||
on page {label} పేజీ {label}లో
|
||||
Save attachment… జోడింపును సేవ్ చేయి…
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 13 and column 16.
|
@@ -1290,6 +1290,8 @@
|
||||
This document has no layers. Bu belgede katman yok.
|
||||
This document has no attachments. Bu belgede ek yok.
|
||||
Hide layer Katmanı gizle
|
||||
Layer options Katman seçenekleri
|
||||
Reset to Initial Visibility Başlangıç görünürlüğüne sıfırla
|
||||
Show layer Katmanı göster
|
||||
on page {label} sayfa {label} üzerinde
|
||||
Save attachment… Eki kaydet…
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 825 and column 9.
|
@@ -1330,6 +1330,8 @@
|
||||
This document has no layers. Цей документ не має шарів.
|
||||
This document has no attachments. Цей документ не має вкладень.
|
||||
Hide layer Приховати шар
|
||||
Layer options Параметри шарів
|
||||
Reset to Initial Visibility Відновити початкову видимість
|
||||
Show layer Показати шар
|
||||
Set layer visibility Задати видимість шару
|
||||
on page {label} на сторінці {label}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -625,6 +625,8 @@ theme Appearance Giao diện
|
||||
Height: Chiều cao:
|
||||
Hide field highlights Ẩn tô sáng trường
|
||||
Hide layer Ẩn lớp
|
||||
Layer options Tùy chọn lớp
|
||||
Reset to Initial Visibility Đặt lại khả năng hiển thị ban đầu
|
||||
Hide menu bar Ẩn thanh trình đơn
|
||||
Hide password Ẩn mật khẩu
|
||||
Hide toolbars Ẩn thanh công cụ
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 491 and column 12.
|
@@ -1281,6 +1281,8 @@
|
||||
This document has no layers. 本文档没有图层。
|
||||
This document has no attachments. 本文档没有附件。
|
||||
Hide layer 隐藏图层
|
||||
Layer options 图层选项
|
||||
Reset to Initial Visibility 重置为初始可见性
|
||||
Show layer 显示图层
|
||||
Set layer visibility 设置图层可见性
|
||||
on page {label} 第 {label} 页
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 9 and column 16.
|
@@ -1225,6 +1225,8 @@
|
||||
This document has no layers. 本文件沒有圖層。
|
||||
This document has no attachments. 本文件沒有附件。
|
||||
Hide layer 隱藏圖層
|
||||
Layer options 圖層選項
|
||||
Reset to Initial Visibility 重設為初始可見性
|
||||
Show layer 顯示圖層
|
||||
Set layer visibility 設定圖層可見性
|
||||
on page {label} 第 {label} 頁
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 9 and column 16.
|
@@ -507,6 +507,7 @@ pub fn right_panel(app: &mut PdfCraftApp, ui: &mut egui::Ui) {
|
||||
let mut nav: Option<Nav> = None;
|
||||
let mut close = false;
|
||||
let mut toggle_layer: Option<(usize, bool)> = None;
|
||||
let mut reset_layers = false;
|
||||
let mut attachment_action: Option<(usize, bool)> = None; // (index, open instead of save)
|
||||
let mut bm_action: Option<BmAction> = None;
|
||||
let mut bm_expand: Option<usize> = None;
|
||||
@@ -599,6 +600,16 @@ pub fn right_panel(app: &mut PdfCraftApp, ui: &mut egui::Ui) {
|
||||
}
|
||||
});
|
||||
}
|
||||
if panel == RightPanel::Layers && !info.layers.is_empty() {
|
||||
let more = icons::button(ui, "ellipsis", 26.0, false, tl!("Layer options"));
|
||||
egui::Popup::menu(&more).show(|ui| {
|
||||
ui.set_min_width(200.0);
|
||||
if ui.button(tl!("Reset to Initial Visibility")).clicked() {
|
||||
reset_layers = true;
|
||||
ui.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
let new_tip = crate::commands::command_tip(ui.ctx(), tl!("New bookmark ({key})"), "bookmark.add");
|
||||
if panel == RightPanel::Bookmarks && bm_editable && icons::button(ui, "bookmark-plus", 26.0, false, &new_tip).clicked() {
|
||||
bm_action = Some(BmAction::New);
|
||||
@@ -818,6 +829,9 @@ pub fn right_panel(app: &mut PdfCraftApp, ui: &mut egui::Ui) {
|
||||
{
|
||||
app.views[index].invalidate_content();
|
||||
}
|
||||
if reset_layers && app.session.reset_layers(id) {
|
||||
app.views[index].invalidate_content();
|
||||
}
|
||||
match nav {
|
||||
Some(Nav::Page(p)) => app.views[index].go_to_page(p),
|
||||
Some(Nav::Dest(p, dest)) => match app.session.get(id) {
|
||||
|
||||
@@ -104,3 +104,23 @@ fn push_buttons_set_layer_visibility() {
|
||||
let s = h.state();
|
||||
assert!(!s.session.get(s.views[0].id).unwrap().dirty, "showing and hiding layers doesn't change the document");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn layer_options_reset_to_initial_visibility() {
|
||||
// Red off and Green on: the opposite of how the file opens.
|
||||
let mut h = harness(|app| {
|
||||
app.set_option("layer", "Red=off").unwrap();
|
||||
app.set_option("layer", "Green=on").unwrap();
|
||||
app.set_option("panel", "layers").unwrap();
|
||||
});
|
||||
assert_eq!(shown(&h), layers(false, true));
|
||||
h.get_by_label("Layer options").click();
|
||||
h.run_steps(2);
|
||||
h.get_by_label("Reset to Initial Visibility").click();
|
||||
h.run_steps(3);
|
||||
assert_eq!(shown(&h), layers(true, false));
|
||||
let s = h.state();
|
||||
let doc = s.session.get(s.views[0].id).unwrap();
|
||||
assert!(!doc.dirty, "showing and hiding layers doesn't change the document");
|
||||
assert_eq!(doc.can_undo(), None, "nor is it an edit to undo");
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ are borrowed; PDF 2.0 additions and most subset standards are missing. **150–2
|
||||
| §14.12 | Document parts (DPart) | yes | none | `core.pdf20-dpart` planned |
|
||||
| §14.13 | Associated files (`/AF`) | yes | none | `core.pdf20-associated-files` planned |
|
||||
| — | Structural validation (Arlington PDF model) | yes (Preflight syntax checks) | none | `core.arlington-validation` planned |
|
||||
| — | Optional content (layers) | full | **own** toggle and default state; set-OCG-state actions; no layer editing | `view.layers-panel` |
|
||||
| — | Optional content (layers) | full | **own** toggle and default state, reset to the default state; set-OCG-state actions; no layer editing | `view.layers-panel`, `view.layers-reset` |
|
||||
| — | Redaction (`/Redact` annotations, applying) | full | **own**: removes glyphs, image pixels, vectors, XObject content, annotations, fields and tags; verifies no residue | `protect.redact-*`, `protect.redaction-verification` |
|
||||
|
||||
## Annotations (§12.5.6)
|
||||
|
||||
@@ -1331,11 +1331,11 @@ title = "Reset layers to initial visibility; list for all or current pages"
|
||||
area = "B"
|
||||
tier = "P2"
|
||||
milestone = "M12"
|
||||
status = "planned"
|
||||
status = "partial"
|
||||
commands = []
|
||||
tools = []
|
||||
evidence = []
|
||||
notes = ""
|
||||
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"]
|
||||
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]]
|
||||
id = "view.destinations-panel"
|
||||
|
||||
Reference in New Issue
Block a user