diff --git a/Cargo.lock b/Cargo.lock index a9fe5e43..7451a1b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1214,6 +1214,30 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "bp256" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46f98c2d9e8e0796b24f84b7ae14159b91ae6ae4b85f74f7655a678a56ad5e1e" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primefield", + "primeorder", +] + +[[package]] +name = "bp384" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc61f3a96b44a33fbb971e9917b81f4207d6dae9a6b0715845c785a48e73d9c2" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primefield", + "primeorder", +] + [[package]] name = "brotli" version = "8.0.4" @@ -4249,12 +4273,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97b65636e5b9ef369943878ac74335ba1c55c1cb6adbf1e2c293c624248d693" -[[package]] -name = "fearless_simd" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3296eb97d869113eaf148a6bdf01884e90b3828ad7ae2340b0f93362fc158611" - [[package]] name = "ff" version = "0.14.0" @@ -6508,12 +6526,13 @@ dependencies = [ [[package]] name = "hayro" version = "0.7.1" -source = "git+https://github.com/ymote/pdfcraft.git?rev=615ff308173a3dd7c595fc598508b519a895c0e9#615ff308173a3dd7c595fc598508b519a895c0e9" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" dependencies = [ "bytemuck 1.25.2", "hayro-interpret", "image", "kurbo", + "log", "pic-scale", "vello_cpu 0.0.8", ] @@ -6524,12 +6543,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f4d0e94ddd48749f06bbe4e5389fb9799a0c45bcaf00495042076ef05e3241a" -[[package]] -name = "hayro-ccitt" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2866f0d976e76497480b853d5e258dab6e4b89ecfe991124e08f15dfa8fd03ad" - [[package]] name = "hayro-cmap" version = "0.1.0" @@ -6543,7 +6556,7 @@ dependencies = [ [[package]] name = "hayro-interpret" version = "0.7.0" -source = "git+https://github.com/ymote/pdfcraft.git?rev=615ff308173a3dd7c595fc598508b519a895c0e9#615ff308173a3dd7c595fc598508b519a895c0e9" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" dependencies = [ "bitflags 2.13.2", "hayro-cmap", @@ -6560,12 +6573,11 @@ dependencies = [ [[package]] name = "hayro-jbig2" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "000e025850f4592ddcaee85640db70704151f2bafd43b6f431aa5b6a2c6a35a9" +version = "0.3.0" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" dependencies = [ - "fearless_simd 1.1.0", - "hayro-ccitt 0.4.0", + "fearless_simd", + "hayro-ccitt", ] [[package]] @@ -6574,7 +6586,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c75ab947623ef4ccaa7acf0579edf7cbb5a73838e3839a7be73335e522f433a1" dependencies = [ - "fearless_simd 0.4.1", + "fearless_simd", ] [[package]] @@ -6586,10 +6598,10 @@ checksum = "885c5ef0654933139a9b9546fc2c69e18d37f38aa2520f079092b1be18f1fcaa" [[package]] name = "hayro-syntax" version = "0.7.2" -source = "git+https://github.com/ymote/pdfcraft.git?rev=615ff308173a3dd7c595fc598508b519a895c0e9#615ff308173a3dd7c595fc598508b519a895c0e9" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" dependencies = [ "flate2", - "hayro-ccitt 0.3.0", + "hayro-ccitt", "hayro-jbig2", "hayro-jpeg2000", "memchr 2.8.3", @@ -7862,6 +7874,16 @@ dependencies = [ "tracing", ] +[[package]] +name = "keccak" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8f198d1db720e4940b5a493201d199d9f24f568f8f746bd13706243a2f71598" +dependencies = [ + "cfg-if 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cpufeatures 0.3.1", +] + [[package]] name = "keyring" version = "3.6.3" @@ -8448,7 +8470,7 @@ dependencies = [ [[package]] name = "lopdf" version = "0.45.0" -source = "git+https://github.com/ymote/pdfcraft.git?rev=615ff308173a3dd7c595fc598508b519a895c0e9#615ff308173a3dd7c595fc598508b519a895c0e9" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" dependencies = [ "aes 0.9.3", "bitflags 2.13.2", @@ -12276,6 +12298,20 @@ dependencies = [ "sha2 0.11.0", ] +[[package]] +name = "p521" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ad64cc32c2dc466317c12ee5853e61f159f9eab1fe7efade0395dc2e7b43449" +dependencies = [ + "base16ct", + "ecdsa", + "elliptic-curve", + "primefield", + "primeorder", + "sha2 0.11.0", +] + [[package]] name = "parking" version = "2.2.1" @@ -12418,32 +12454,35 @@ dependencies = [ [[package]] name = "pdfcraft-a11y" -version = "0.4.0" -source = "git+https://github.com/ymote/pdfcraft.git?rev=615ff308173a3dd7c595fc598508b519a895c0e9#615ff308173a3dd7c595fc598508b519a895c0e9" +version = "0.6.0" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" dependencies = [ "pdfcraft-content", "pdfcraft-cos", + "pdfcraft-fonts", "pdfcraft-model", "thiserror 2.0.20", ] [[package]] name = "pdfcraft-annot" -version = "0.4.0" -source = "git+https://github.com/ymote/pdfcraft.git?rev=615ff308173a3dd7c595fc598508b519a895c0e9#615ff308173a3dd7c595fc598508b519a895c0e9" +version = "0.6.0" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" dependencies = [ "pdfcraft-cos", "pdfcraft-fonts", + "pdfcraft-model", "thiserror 2.0.20", ] [[package]] name = "pdfcraft-automation" -version = "0.4.0" -source = "git+https://github.com/ymote/pdfcraft.git?rev=615ff308173a3dd7c595fc598508b519a895c0e9#615ff308173a3dd7c595fc598508b519a895c0e9" +version = "0.6.0" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" dependencies = [ "pdfcraft-engine", "pdfcraft-organize", + "pdfcraft-platform", "pdfcraft-render", "png 0.18.1", "serde_json", @@ -12451,21 +12490,21 @@ dependencies = [ [[package]] name = "pdfcraft-compare" -version = "0.4.0" -source = "git+https://github.com/ymote/pdfcraft.git?rev=615ff308173a3dd7c595fc598508b519a895c0e9#615ff308173a3dd7c595fc598508b519a895c0e9" +version = "0.6.0" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" [[package]] name = "pdfcraft-content" -version = "0.4.0" -source = "git+https://github.com/ymote/pdfcraft.git?rev=615ff308173a3dd7c595fc598508b519a895c0e9#615ff308173a3dd7c595fc598508b519a895c0e9" +version = "0.6.0" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" dependencies = [ "pdfcraft-cos", ] [[package]] name = "pdfcraft-cos" -version = "0.4.0" -source = "git+https://github.com/ymote/pdfcraft.git?rev=615ff308173a3dd7c595fc598508b519a895c0e9#615ff308173a3dd7c595fc598508b519a895c0e9" +version = "0.6.0" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" dependencies = [ "pdfcraft-crypt", "pdfcraft-filters", @@ -12474,9 +12513,10 @@ dependencies = [ [[package]] name = "pdfcraft-create" -version = "0.4.0" -source = "git+https://github.com/ymote/pdfcraft.git?rev=615ff308173a3dd7c595fc598508b519a895c0e9#615ff308173a3dd7c595fc598508b519a895c0e9" +version = "0.6.0" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" dependencies = [ + "encoding_rs", "image", "pdfcraft-cos", "pdfcraft-fonts", @@ -12488,8 +12528,8 @@ dependencies = [ [[package]] name = "pdfcraft-crypt" -version = "0.4.0" -source = "git+https://github.com/ymote/pdfcraft.git?rev=615ff308173a3dd7c595fc598508b519a895c0e9#615ff308173a3dd7c595fc598508b519a895c0e9" +version = "0.6.0" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" dependencies = [ "aes 0.9.3", "md-5 0.11.0", @@ -12500,22 +12540,26 @@ dependencies = [ [[package]] name = "pdfcraft-edit" -version = "0.4.0" -source = "git+https://github.com/ymote/pdfcraft.git?rev=615ff308173a3dd7c595fc598508b519a895c0e9#615ff308173a3dd7c595fc598508b519a895c0e9" +version = "0.6.0" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" dependencies = [ + "pdfcraft-annot", "pdfcraft-content", "pdfcraft-cos", "pdfcraft-fonts", "pdfcraft-model", "thiserror 2.0.20", + "unicode-bidi 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "pdfcraft-engine" -version = "0.4.0" -source = "git+https://github.com/ymote/pdfcraft.git?rev=615ff308173a3dd7c595fc598508b519a895c0e9#615ff308173a3dd7c595fc598508b519a895c0e9" +version = "0.6.0" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" dependencies = [ "chrono", + "icu_properties", + "idna", "image", "pdfcraft-a11y", "pdfcraft-annot", @@ -12526,6 +12570,7 @@ dependencies = [ "pdfcraft-export", "pdfcraft-fonts", "pdfcraft-forms", + "pdfcraft-geom", "pdfcraft-js", "pdfcraft-measure", "pdfcraft-model", @@ -12541,20 +12586,21 @@ dependencies = [ "pdfcraft-xfdf", "png 0.18.1", "thiserror 2.0.20", + "url", ] [[package]] name = "pdfcraft-export" -version = "0.4.0" -source = "git+https://github.com/ymote/pdfcraft.git?rev=615ff308173a3dd7c595fc598508b519a895c0e9#615ff308173a3dd7c595fc598508b519a895c0e9" +version = "0.6.0" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" dependencies = [ "flate2", ] [[package]] name = "pdfcraft-filters" -version = "0.4.0" -source = "git+https://github.com/ymote/pdfcraft.git?rev=615ff308173a3dd7c595fc598508b519a895c0e9#615ff308173a3dd7c595fc598508b519a895c0e9" +version = "0.6.0" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" dependencies = [ "flate2", "thiserror 2.0.20", @@ -12562,17 +12608,18 @@ dependencies = [ [[package]] name = "pdfcraft-fonts" -version = "0.4.0" -source = "git+https://github.com/ymote/pdfcraft.git?rev=615ff308173a3dd7c595fc598508b519a895c0e9#615ff308173a3dd7c595fc598508b519a895c0e9" +version = "0.6.0" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" dependencies = [ + "harfrust 0.12.0", "pdfcraft-cos", "skrifa 0.44.0", ] [[package]] name = "pdfcraft-forms" -version = "0.4.0" -source = "git+https://github.com/ymote/pdfcraft.git?rev=615ff308173a3dd7c595fc598508b519a895c0e9#615ff308173a3dd7c595fc598508b519a895c0e9" +version = "0.6.0" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" dependencies = [ "pdfcraft-content", "pdfcraft-cos", @@ -12581,18 +12628,23 @@ dependencies = [ "thiserror 2.0.20", ] +[[package]] +name = "pdfcraft-geom" +version = "0.6.0" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" + [[package]] name = "pdfcraft-js" -version = "0.4.0" -source = "git+https://github.com/ymote/pdfcraft.git?rev=615ff308173a3dd7c595fc598508b519a895c0e9#615ff308173a3dd7c595fc598508b519a895c0e9" +version = "0.6.0" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" dependencies = [ "boa_engine", ] [[package]] name = "pdfcraft-measure" -version = "0.4.0" -source = "git+https://github.com/ymote/pdfcraft.git?rev=615ff308173a3dd7c595fc598508b519a895c0e9#615ff308173a3dd7c595fc598508b519a895c0e9" +version = "0.6.0" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" dependencies = [ "pdfcraft-annot", "pdfcraft-content", @@ -12604,16 +12656,16 @@ dependencies = [ [[package]] name = "pdfcraft-model" -version = "0.4.0" -source = "git+https://github.com/ymote/pdfcraft.git?rev=615ff308173a3dd7c595fc598508b519a895c0e9#615ff308173a3dd7c595fc598508b519a895c0e9" +version = "0.6.0" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" dependencies = [ "pdfcraft-cos", ] [[package]] name = "pdfcraft-ocr" -version = "0.4.0" -source = "git+https://github.com/ymote/pdfcraft.git?rev=615ff308173a3dd7c595fc598508b519a895c0e9#615ff308173a3dd7c595fc598508b519a895c0e9" +version = "0.6.0" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" dependencies = [ "ocrs", "pdfcraft-fonts", @@ -12623,8 +12675,8 @@ dependencies = [ [[package]] name = "pdfcraft-optimize" -version = "0.4.0" -source = "git+https://github.com/ymote/pdfcraft.git?rev=615ff308173a3dd7c595fc598508b519a895c0e9#615ff308173a3dd7c595fc598508b519a895c0e9" +version = "0.6.0" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" dependencies = [ "image", "pdfcraft-annot", @@ -12636,18 +12688,26 @@ dependencies = [ [[package]] name = "pdfcraft-organize" -version = "0.4.0" -source = "git+https://github.com/ymote/pdfcraft.git?rev=615ff308173a3dd7c595fc598508b519a895c0e9#615ff308173a3dd7c595fc598508b519a895c0e9" +version = "0.6.0" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" dependencies = [ "pdfcraft-content", "pdfcraft-cos", "thiserror 2.0.20", ] +[[package]] +name = "pdfcraft-platform" +version = "0.6.0" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" +dependencies = [ + "zbus 5.19.0", +] + [[package]] name = "pdfcraft-preflight" -version = "0.4.0" -source = "git+https://github.com/ymote/pdfcraft.git?rev=615ff308173a3dd7c595fc598508b519a895c0e9#615ff308173a3dd7c595fc598508b519a895c0e9" +version = "0.6.0" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" dependencies = [ "pdfcraft-cos", "pdfcraft-model", @@ -12655,8 +12715,8 @@ dependencies = [ [[package]] name = "pdfcraft-print" -version = "0.4.0" -source = "git+https://github.com/ymote/pdfcraft.git?rev=615ff308173a3dd7c595fc598508b519a895c0e9#615ff308173a3dd7c595fc598508b519a895c0e9" +version = "0.6.0" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" dependencies = [ "pdfcraft-content", "pdfcraft-cos", @@ -12666,8 +12726,8 @@ dependencies = [ [[package]] name = "pdfcraft-redact" -version = "0.4.0" -source = "git+https://github.com/ymote/pdfcraft.git?rev=615ff308173a3dd7c595fc598508b519a895c0e9#615ff308173a3dd7c595fc598508b519a895c0e9" +version = "0.6.0" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" dependencies = [ "pdfcraft-annot", "pdfcraft-content", @@ -12681,61 +12741,75 @@ dependencies = [ [[package]] name = "pdfcraft-render" -version = "0.4.0" -source = "git+https://github.com/ymote/pdfcraft.git?rev=615ff308173a3dd7c595fc598508b519a895c0e9#615ff308173a3dd7c595fc598508b519a895c0e9" +version = "0.6.0" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" dependencies = [ + "bytemuck 1.25.2", "getrandom 0.4.3", "hayro", "hayro-syntax", "kurbo", "log", "lopdf 0.45.0", + "pdfcraft-cos", + "pdfcraft-fonts", "thiserror 2.0.20", ] [[package]] name = "pdfcraft-sign" -version = "0.4.0" -source = "git+https://github.com/ymote/pdfcraft.git?rev=615ff308173a3dd7c595fc598508b519a895c0e9#615ff308173a3dd7c595fc598508b519a895c0e9" +version = "0.6.0" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" dependencies = [ "aes 0.9.3", "aws-lc-rs", "base64 0.22.1", + "bp256", + "bp384", "cbc 0.2.1", + "crypto-bigint", "des", + "ecdsa", + "ed25519-dalek", "getrandom 0.4.3", "hmac 0.13.0", "p256", "p384", + "p521", "pbkdf2 0.13.0", "pdfcraft-annot", "pdfcraft-cos", "pdfcraft-fonts", "rc2", + "ripemd", "rsa", "rustls-cng", "security-framework 3.7.0", "sha1 0.11.0", "sha2 0.11.0", + "sha3", "thiserror 2.0.20", ] [[package]] name = "pdfcraft-xfa" -version = "0.4.0" -source = "git+https://github.com/ymote/pdfcraft.git?rev=615ff308173a3dd7c595fc598508b519a895c0e9#615ff308173a3dd7c595fc598508b519a895c0e9" +version = "0.6.0" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" dependencies = [ "base64 0.22.1", + "image", "pdfcraft-cos", "pdfcraft-fonts", + "png 0.18.1", "roxmltree 0.20.0", + "sha2 0.11.0", "thiserror 2.0.20", ] [[package]] name = "pdfcraft-xfdf" -version = "0.4.0" -source = "git+https://github.com/ymote/pdfcraft.git?rev=615ff308173a3dd7c595fc598508b519a895c0e9#615ff308173a3dd7c595fc598508b519a895c0e9" +version = "0.6.0" +source = "git+https://github.com/ymote/pdfcraft.git?rev=51f09eea27c56e8e033f8234fa7f4072ba7846d3#51f09eea27c56e8e033f8234fa7f4072ba7846d3" dependencies = [ "pdfcraft-annot", "pdfcraft-cos", @@ -14537,6 +14611,15 @@ dependencies = [ "serde_json", ] +[[package]] +name = "ripemd" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dd4211456b4172d7e44261920c25acf07367c4f04bb5f5d54fc21b090d9b159" +dependencies = [ + "digest 0.11.3", +] + [[package]] name = "rmcp" version = "1.8.0" @@ -16035,6 +16118,17 @@ dependencies = [ "digest 0.11.3", ] +[[package]] +name = "sha3" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc9bad02c26382724b2d2692c6f179285e4b54eeecd7968f52a50059c3c11759" +dependencies = [ + "digest 0.11.3", + "keccak", + "sponge-cursor", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -16443,6 +16537,12 @@ dependencies = [ "der 0.8.2", ] +[[package]] +name = "sponge-cursor" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a0219bd7d979d58245a4f41f695e1ac9f8befdffadd7f61f1bae9e39abc6620" + [[package]] name = "sqlite-wasm-rs" version = "0.5.5" @@ -18197,7 +18297,7 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "vectorcraft-brush" version = "0.6.0" -source = "git+https://github.com/ymote/vectorcraft.git?rev=85dcb28e411db4271f3b726d6502b78e1375cbba#85dcb28e411db4271f3b726d6502b78e1375cbba" +source = "git+https://github.com/ymote/vectorcraft.git?rev=2cd3a1ca8fbcc5b2e40de47588194798a9f8e497#2cd3a1ca8fbcc5b2e40de47588194798a9f8e497" dependencies = [ "kurbo", "serde", @@ -18210,7 +18310,7 @@ dependencies = [ [[package]] name = "vectorcraft-cad" version = "0.6.0" -source = "git+https://github.com/ymote/vectorcraft.git?rev=85dcb28e411db4271f3b726d6502b78e1375cbba#85dcb28e411db4271f3b726d6502b78e1375cbba" +source = "git+https://github.com/ymote/vectorcraft.git?rev=2cd3a1ca8fbcc5b2e40de47588194798a9f8e497#2cd3a1ca8fbcc5b2e40de47588194798a9f8e497" dependencies = [ "image", "kurbo", @@ -18226,7 +18326,7 @@ dependencies = [ [[package]] name = "vectorcraft-color" version = "0.6.0" -source = "git+https://github.com/ymote/vectorcraft.git?rev=85dcb28e411db4271f3b726d6502b78e1375cbba#85dcb28e411db4271f3b726d6502b78e1375cbba" +source = "git+https://github.com/ymote/vectorcraft.git?rev=2cd3a1ca8fbcc5b2e40de47588194798a9f8e497#2cd3a1ca8fbcc5b2e40de47588194798a9f8e497" dependencies = [ "kurbo", "moxcms 0.8.1", @@ -18238,7 +18338,7 @@ dependencies = [ [[package]] name = "vectorcraft-doc" version = "0.6.0" -source = "git+https://github.com/ymote/vectorcraft.git?rev=85dcb28e411db4271f3b726d6502b78e1375cbba#85dcb28e411db4271f3b726d6502b78e1375cbba" +source = "git+https://github.com/ymote/vectorcraft.git?rev=2cd3a1ca8fbcc5b2e40de47588194798a9f8e497#2cd3a1ca8fbcc5b2e40de47588194798a9f8e497" dependencies = [ "image", "kurbo", @@ -18254,7 +18354,7 @@ dependencies = [ [[package]] name = "vectorcraft-effects" version = "0.6.0" -source = "git+https://github.com/ymote/vectorcraft.git?rev=85dcb28e411db4271f3b726d6502b78e1375cbba#85dcb28e411db4271f3b726d6502b78e1375cbba" +source = "git+https://github.com/ymote/vectorcraft.git?rev=2cd3a1ca8fbcc5b2e40de47588194798a9f8e497#2cd3a1ca8fbcc5b2e40de47588194798a9f8e497" dependencies = [ "kurbo", "serde_json", @@ -18268,7 +18368,7 @@ dependencies = [ [[package]] name = "vectorcraft-engine" version = "0.6.0" -source = "git+https://github.com/ymote/vectorcraft.git?rev=85dcb28e411db4271f3b726d6502b78e1375cbba#85dcb28e411db4271f3b726d6502b78e1375cbba" +source = "git+https://github.com/ymote/vectorcraft.git?rev=2cd3a1ca8fbcc5b2e40de47588194798a9f8e497#2cd3a1ca8fbcc5b2e40de47588194798a9f8e497" dependencies = [ "flate2", "image", @@ -18298,7 +18398,7 @@ dependencies = [ [[package]] name = "vectorcraft-eps" version = "0.6.0" -source = "git+https://github.com/ymote/vectorcraft.git?rev=85dcb28e411db4271f3b726d6502b78e1375cbba#85dcb28e411db4271f3b726d6502b78e1375cbba" +source = "git+https://github.com/ymote/vectorcraft.git?rev=2cd3a1ca8fbcc5b2e40de47588194798a9f8e497#2cd3a1ca8fbcc5b2e40de47588194798a9f8e497" dependencies = [ "flate2", "image", @@ -18316,7 +18416,7 @@ dependencies = [ [[package]] name = "vectorcraft-format" version = "0.6.0" -source = "git+https://github.com/ymote/vectorcraft.git?rev=85dcb28e411db4271f3b726d6502b78e1375cbba#85dcb28e411db4271f3b726d6502b78e1375cbba" +source = "git+https://github.com/ymote/vectorcraft.git?rev=2cd3a1ca8fbcc5b2e40de47588194798a9f8e497#2cd3a1ca8fbcc5b2e40de47588194798a9f8e497" dependencies = [ "flate2", "serde", @@ -18328,7 +18428,7 @@ dependencies = [ [[package]] name = "vectorcraft-geom" version = "0.6.0" -source = "git+https://github.com/ymote/vectorcraft.git?rev=85dcb28e411db4271f3b726d6502b78e1375cbba#85dcb28e411db4271f3b726d6502b78e1375cbba" +source = "git+https://github.com/ymote/vectorcraft.git?rev=2cd3a1ca8fbcc5b2e40de47588194798a9f8e497#2cd3a1ca8fbcc5b2e40de47588194798a9f8e497" dependencies = [ "kurbo", "serde", @@ -18338,7 +18438,7 @@ dependencies = [ [[package]] name = "vectorcraft-metafile" version = "0.6.0" -source = "git+https://github.com/ymote/vectorcraft.git?rev=85dcb28e411db4271f3b726d6502b78e1375cbba#85dcb28e411db4271f3b726d6502b78e1375cbba" +source = "git+https://github.com/ymote/vectorcraft.git?rev=2cd3a1ca8fbcc5b2e40de47588194798a9f8e497#2cd3a1ca8fbcc5b2e40de47588194798a9f8e497" dependencies = [ "image", "kurbo", @@ -18353,7 +18453,7 @@ dependencies = [ [[package]] name = "vectorcraft-pathops" version = "0.6.0" -source = "git+https://github.com/ymote/vectorcraft.git?rev=85dcb28e411db4271f3b726d6502b78e1375cbba#85dcb28e411db4271f3b726d6502b78e1375cbba" +source = "git+https://github.com/ymote/vectorcraft.git?rev=2cd3a1ca8fbcc5b2e40de47588194798a9f8e497#2cd3a1ca8fbcc5b2e40de47588194798a9f8e497" dependencies = [ "kurbo", "linesweeper", @@ -18364,7 +18464,7 @@ dependencies = [ [[package]] name = "vectorcraft-pdf" version = "0.6.0" -source = "git+https://github.com/ymote/vectorcraft.git?rev=85dcb28e411db4271f3b726d6502b78e1375cbba#85dcb28e411db4271f3b726d6502b78e1375cbba" +source = "git+https://github.com/ymote/vectorcraft.git?rev=2cd3a1ca8fbcc5b2e40de47588194798a9f8e497#2cd3a1ca8fbcc5b2e40de47588194798a9f8e497" dependencies = [ "aes 0.8.4", "flate2", @@ -18392,7 +18492,7 @@ dependencies = [ [[package]] name = "vectorcraft-plugins" version = "0.6.0" -source = "git+https://github.com/ymote/vectorcraft.git?rev=85dcb28e411db4271f3b726d6502b78e1375cbba#85dcb28e411db4271f3b726d6502b78e1375cbba" +source = "git+https://github.com/ymote/vectorcraft.git?rev=2cd3a1ca8fbcc5b2e40de47588194798a9f8e497#2cd3a1ca8fbcc5b2e40de47588194798a9f8e497" dependencies = [ "serde", "serde_json", @@ -18406,7 +18506,7 @@ dependencies = [ [[package]] name = "vectorcraft-render" version = "0.6.0" -source = "git+https://github.com/ymote/vectorcraft.git?rev=85dcb28e411db4271f3b726d6502b78e1375cbba#85dcb28e411db4271f3b726d6502b78e1375cbba" +source = "git+https://github.com/ymote/vectorcraft.git?rev=2cd3a1ca8fbcc5b2e40de47588194798a9f8e497#2cd3a1ca8fbcc5b2e40de47588194798a9f8e497" dependencies = [ "flate2", "gif", @@ -18427,7 +18527,7 @@ dependencies = [ [[package]] name = "vectorcraft-svg" version = "0.6.0" -source = "git+https://github.com/ymote/vectorcraft.git?rev=85dcb28e411db4271f3b726d6502b78e1375cbba#85dcb28e411db4271f3b726d6502b78e1375cbba" +source = "git+https://github.com/ymote/vectorcraft.git?rev=2cd3a1ca8fbcc5b2e40de47588194798a9f8e497#2cd3a1ca8fbcc5b2e40de47588194798a9f8e497" dependencies = [ "flate2", "image", @@ -18448,7 +18548,7 @@ dependencies = [ [[package]] name = "vectorcraft-text" version = "0.6.0" -source = "git+https://github.com/ymote/vectorcraft.git?rev=85dcb28e411db4271f3b726d6502b78e1375cbba#85dcb28e411db4271f3b726d6502b78e1375cbba" +source = "git+https://github.com/ymote/vectorcraft.git?rev=2cd3a1ca8fbcc5b2e40de47588194798a9f8e497#2cd3a1ca8fbcc5b2e40de47588194798a9f8e497" dependencies = [ "harfrust 0.13.3", "kurbo", @@ -18466,7 +18566,7 @@ dependencies = [ [[package]] name = "vectorcraft-tools" version = "0.6.0" -source = "git+https://github.com/ymote/vectorcraft.git?rev=85dcb28e411db4271f3b726d6502b78e1375cbba#85dcb28e411db4271f3b726d6502b78e1375cbba" +source = "git+https://github.com/ymote/vectorcraft.git?rev=2cd3a1ca8fbcc5b2e40de47588194798a9f8e497#2cd3a1ca8fbcc5b2e40de47588194798a9f8e497" dependencies = [ "kurbo", "serde", @@ -18481,7 +18581,7 @@ dependencies = [ [[package]] name = "vectorcraft-trace" version = "0.6.0" -source = "git+https://github.com/ymote/vectorcraft.git?rev=85dcb28e411db4271f3b726d6502b78e1375cbba#85dcb28e411db4271f3b726d6502b78e1375cbba" +source = "git+https://github.com/ymote/vectorcraft.git?rev=2cd3a1ca8fbcc5b2e40de47588194798a9f8e497#2cd3a1ca8fbcc5b2e40de47588194798a9f8e497" dependencies = [ "image", "kurbo", @@ -18498,7 +18598,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3361bff7f7d82c0c496b92048db83846691f0e844cc28dee92b1c824291b55ee" dependencies = [ "bytemuck 1.25.2", - "fearless_simd 0.4.1", + "fearless_simd", "guillotiere", "hashbrown 0.17.1", "log", @@ -18515,7 +18615,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19d672facaa2d697285a786cd9d44d614cd2ce54cdc022504bf339f8fff3b750" dependencies = [ "bytemuck 1.25.2", - "fearless_simd 0.4.1", + "fearless_simd", "guillotiere", "hashbrown 0.17.1", "log", @@ -18532,7 +18632,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb2141a2bca6e6d598e471fd4d1d7eed8e020aad6a28187edda07f091a325dd" dependencies = [ "bytemuck 1.25.2", - "fearless_simd 0.4.1", + "fearless_simd", "guillotiere", "log", "peniko", @@ -21019,8 +21119,3 @@ dependencies = [ "syn 3.0.5", "winnow 1.0.4", ] - -[[patch.unused]] -name = "hayro-jbig2" -version = "0.3.0" -source = "git+https://github.com/ymote/pdfcraft.git?rev=615ff308173a3dd7c595fc598508b519a895c0e9#615ff308173a3dd7c595fc598508b519a895c0e9" diff --git a/Cargo.toml b/Cargo.toml index fa5c96c5..a9ee1a71 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -274,26 +274,26 @@ effectcraft-media = { git = "https://github.com/ymote/effectcraft.git", rev = "d effectcraft-expr = { git = "https://github.com/ymote/effectcraft.git", rev = "d60cb71e297a4bb76851b27b48c0014b56ccab69" } effectcraft-model = { git = "https://github.com/ymote/effectcraft.git", rev = "d60cb71e297a4bb76851b27b48c0014b56ccab69" } # The vectorcraft vector engine (ADR 0013): the pinned ymote fork, one revision. -vectorcraft-engine = { git = "https://github.com/ymote/vectorcraft.git", rev = "85dcb28e411db4271f3b726d6502b78e1375cbba" } -vectorcraft-render = { git = "https://github.com/ymote/vectorcraft.git", rev = "85dcb28e411db4271f3b726d6502b78e1375cbba" } +vectorcraft-engine = { git = "https://github.com/ymote/vectorcraft.git", rev = "2cd3a1ca8fbcc5b2e40de47588194798a9f8e497" } +vectorcraft-render = { git = "https://github.com/ymote/vectorcraft.git", rev = "2cd3a1ca8fbcc5b2e40de47588194798a9f8e497" } # The vector service's embedded-reference fence (ADR 0013): the engine's own # SVG importer, run with a reader that records instead of reads, and its # native format, for the links a document carries. -vectorcraft-svg = { git = "https://github.com/ymote/vectorcraft.git", rev = "85dcb28e411db4271f3b726d6502b78e1375cbba" } -vectorcraft-format = { git = "https://github.com/ymote/vectorcraft.git", rev = "85dcb28e411db4271f3b726d6502b78e1375cbba" } +vectorcraft-svg = { git = "https://github.com/ymote/vectorcraft.git", rev = "2cd3a1ca8fbcc5b2e40de47588194798a9f8e497" } +vectorcraft-format = { git = "https://github.com/ymote/vectorcraft.git", rev = "2cd3a1ca8fbcc5b2e40de47588194798a9f8e497" } # The pdfcraft PDF engine (ADR 0013): the pinned ymote fork, one revision; # default features off to leave its MCP server out of the build. -pdfcraft-automation = { git = "https://github.com/ymote/pdfcraft.git", rev = "615ff308173a3dd7c595fc598508b519a895c0e9", default-features = false } +pdfcraft-automation = { git = "https://github.com/ymote/pdfcraft.git", rev = "51f09eea27c56e8e033f8234fa7f4072ba7846d3", default-features = false } # The pdf service's comment dates: the engine's own object reader and comment # reader, which keep `/M` with its zone (the engine's listing drops it). -pdfcraft-cos = { git = "https://github.com/ymote/pdfcraft.git", rev = "615ff308173a3dd7c595fc598508b519a895c0e9" } -pdfcraft-annot = { git = "https://github.com/ymote/pdfcraft.git", rev = "615ff308173a3dd7c595fc598508b519a895c0e9" } +pdfcraft-cos = { git = "https://github.com/ymote/pdfcraft.git", rev = "51f09eea27c56e8e033f8234fa7f4072ba7846d3" } +pdfcraft-annot = { git = "https://github.com/ymote/pdfcraft.git", rev = "51f09eea27c56e8e033f8234fa7f4072ba7846d3" } # The pdf service's edit check: the engine's own text editor, run on a copy of # the open document to see what an edit would do before PDF Tools makes it. -pdfcraft-edit = { git = "https://github.com/ymote/pdfcraft.git", rev = "615ff308173a3dd7c595fc598508b519a895c0e9" } +pdfcraft-edit = { git = "https://github.com/ymote/pdfcraft.git", rev = "51f09eea27c56e8e033f8234fa7f4072ba7846d3" } # The pdf service's tests only: the engine's own scripted XFA form, the # hostile fixture of the scripts-off fence (ADR 0013). -pdfcraft-xfa = { git = "https://github.com/ymote/pdfcraft.git", rev = "615ff308173a3dd7c595fc598508b519a895c0e9" } +pdfcraft-xfa = { git = "https://github.com/ymote/pdfcraft.git", rev = "51f09eea27c56e8e033f8234fa7f4072ba7846d3" } octosense-mail-service = { path = "apps/mail/host-service" } octosense-calendar-service = { path = "apps/calendar/host-service" } octosense-news-service = { path = "apps/news/host-service" } @@ -458,9 +458,12 @@ nix = { git = "https://github.com/octos-org/octos.git", rev = "18fcd3f16e527d2b6 # interpret APIs the engine uses, lopdf and jbig2 allocation fixes), and # [patch] sections do not reach a consumer workspace, so the same five # are patched here, pinned to the engine's own ymote/pdfcraft revision -# (ADR 0013; the rev moves with the pdfcraft-automation pin above). -hayro = { git = "https://github.com/ymote/pdfcraft.git", rev = "615ff308173a3dd7c595fc598508b519a895c0e9" } -hayro-interpret = { git = "https://github.com/ymote/pdfcraft.git", rev = "615ff308173a3dd7c595fc598508b519a895c0e9" } -hayro-jbig2 = { git = "https://github.com/ymote/pdfcraft.git", rev = "615ff308173a3dd7c595fc598508b519a895c0e9" } -hayro-syntax = { git = "https://github.com/ymote/pdfcraft.git", rev = "615ff308173a3dd7c595fc598508b519a895c0e9" } -lopdf = { git = "https://github.com/ymote/pdfcraft.git", rev = "615ff308173a3dd7c595fc598508b519a895c0e9" } +# (ADR 0013; the rev moves with the pdfcraft-automation pin above). A +# patch that loses to a newer crates.io release goes unused and the +# unhardened crate is built (hayro-jbig2's 0.3.0 had lost to 0.3.1): the +# pdf service's skill test refuses that. +hayro = { git = "https://github.com/ymote/pdfcraft.git", rev = "51f09eea27c56e8e033f8234fa7f4072ba7846d3" } +hayro-interpret = { git = "https://github.com/ymote/pdfcraft.git", rev = "51f09eea27c56e8e033f8234fa7f4072ba7846d3" } +hayro-jbig2 = { git = "https://github.com/ymote/pdfcraft.git", rev = "51f09eea27c56e8e033f8234fa7f4072ba7846d3" } +hayro-syntax = { git = "https://github.com/ymote/pdfcraft.git", rev = "51f09eea27c56e8e033f8234fa7f4072ba7846d3" } +lopdf = { git = "https://github.com/ymote/pdfcraft.git", rev = "51f09eea27c56e8e033f8234fa7f4072ba7846d3" } diff --git a/apps/pdf/host-service/skill/commands.md b/apps/pdf/host-service/skill/commands.md index 7a8d58de..68c51876 100644 --- a/apps/pdf/host-service/skill/commands.md +++ b/apps/pdf/host-service/skill/commands.md @@ -1,6 +1,6 @@ # pdf engine commands -The pdfcraft engine's command catalog at revision 615ff308173a: 132 commands, one per line as `id` label: params. +The pdfcraft engine's command catalog at revision 51f09eea27c5: 144 commands, one per line as `id` label: params. Generated from the engine by `OCTOSENSE_SKILL_REGEN=1 cargo test --locked -p octosense-pdf-service --test skill`; do not edit. A tag after the id marks a command that reaches past the open document (safety.json has every id's class): [file] reads or writes a path, [code] installs or runs code or other commands, [network] uses the network, [device] uses hardware, [host] changes windows, views, preferences, the clipboard or other app state. An untagged command works on the open document only. @@ -22,20 +22,24 @@ A tag after the id marks a command that reaches past the open document (safety.j - `bookmark_add` Add a bookmark. Add a bookmark that goes to a page, under a parent bookmark (or at the top level), at a position. Undoable.: {doc: integer, page: integer, parent?: array, position?: integer, title: string} - `bookmark_delete` Delete a bookmark. Delete a bookmark and the bookmarks under it. Undoable.: {doc: integer, path: array} -- `bookmark_list` List bookmarks. The bookmark tree with each bookmark's path, title, target page and open state.: {doc: integer} +- `bookmark_from_structure` New bookmarks from structure. Make bookmarks from the document's tagged headings (H, H1-H6), nested by level under a new first bookmark titled "Untitled". Fails if the document has no tagged headings. Returns the first page of the bookmark tree (see bookmark_list). Undoable.: {doc: integer} +- `bookmark_list` List bookmarks. The bookmarks in outline order, one page at a time: each with its path (1-based, as the other bookmark tools take it), title, target page and open state, and its children on the same page. next is the offset of the following page, or null at the end. Bookmarks nested deeper than 33 levels are not listed.: {doc: integer, limit?: integer, offset?: integer} - `bookmark_move` Move a bookmark. Move a bookmark (with its children) under another parent, or within its level. Undoable.: {doc: integer, parent?: array, path: array, position?: integer} - `bookmark_rename` Rename a bookmark. Change a bookmark's title. Undoable.: {doc: integer, path: array, title: string} - `bookmark_set_page` Set a bookmark's page. Point a bookmark at another page. Undoable.: {doc: integer, page: integer, path: array} ## command -- `command_list` List commands. Every registered PdfCraft command with its menu, shortcut, whether it is enabled now, and the tool that automates it.: {doc?: integer} +- `command_batch` [code] Run several commands. Run steps in order (at most 1000). Returns completed/failed counts and per-step ok/result/error; stop_on_error defaults to true.: {steps: array, stop_on_error?: boolean} +- `command_list` List commands. Every registered PdfCraft command with its menu, shortcut, whether it is enabled now, and the tool that automates it.: {doc?: integer, enabled_only?: boolean, filter?: string} +- `command_run` [code] Run a command. Run a registry command through its headless tool. Pass that tool's arguments in params (see command_list). Unknown params are ignored with warnings; path confinement still applies.: {id: string, params?: object} ## comment -- `comment_add` [file] Add a comment. Add a comment as Acrobat's commenting tools do. Geometry is in points with the origin at the top-left of the displayed page, y down (as in page_render images at 72 dpi and text_find rects). note: `at` [x, y] (icon top-left). stamp: `at` [x, y] (its centre) and `stamp`: approved, completed, confidential, draft, final, for comment, for public release, information only, not approved, not for public release, preliminary results, void, accepted, initial here, rejected, sign here, witness; dynamic: true for the dynamic approved/confidential/received/reviewed/revised stamps with a By … at … line. highlight/underline/strikeout/squiggly: `find` (text on the page to mark; every match with all: true) or `quads`. rectangle/oval/textbox: `rect` [x0, y0, x1, y1]. line/arrow: `from`, `to`. ink: `strokes` [[[x, y], …], …]. polygon/cloud/polyline (connected lines): `points` [[x, y], …]. callout: `rect` (its text box), `to` (the point the arrow touches), optional `knee`. caret (inserted text): `at`, the insertion point on the baseline. replace (Replace Text): `find` or `quads` like highlight, `contents` the replacement. attachment: `path` (the file), `at`, optional `icon` (PushPin, Paperclip, Graph, Tag). Undoable.: {all?: boolean, at?: array, author?: string, color?: string, contents?: string, doc: integer, dynamic?: boolean, fill?: string, find?: string, font_size?: number, from?: array, icon?: string, knee?: array, opacity?: number, page: integer, path?: string, points?: array, quads?: array, rect?: array, stamp?: string, strokes?: array, to?: array, type: string, width?: number} +- `comment_add` [file] Add a comment. Add a comment as Acrobat's commenting tools do. Geometry is in points with the origin at the top-left of the displayed page, y down (as in page_render images at 72 dpi and text_find rects). note: `at` [x, y] (icon top-left). stamp: `at` [x, y] (its centre) and `stamp`: approved, completed, confidential, draft, final, for comment, for public release, information only, not approved, not for public release, preliminary results, void, accepted, initial here, rejected, sign here, witness; dynamic: true for the dynamic approved/confidential/received/reviewed/revised stamps with a By … at … line. highlight/underline/strikeout/squiggly: `find` (text on the page to mark; every match with all: true) or `quads`. rectangle/oval/textbox: `rect` [x0, y0, x1, y1]. line/arrow: `from`, `to`. Optional `endings` (two names: start, end) for a line, arrow or polyline, or one name for a callout: None, Square, Circle, Diamond, OpenArrow, ClosedArrow, Butt, ROpenArrow, RClosedArrow, Slash. An arrow without endings is None then OpenArrow. ink: `strokes` [[[x, y], …], …]. polygon/cloud/polyline (connected lines): `points` [[x, y], …]. callout: `rect` (its text box), `to` (the point the arrow touches), optional `knee`. caret (inserted text): `at`, the insertion point on the baseline. replace (Replace Text): `find` or `quads` like highlight, `contents` the replacement. attachment: `path` (the file), `at`, optional `icon` (PushPin, Paperclip, Graph, Tag). Undoable.: {all?: boolean, at?: array, author?: string, color?: string, contents?: string, doc: integer, dynamic?: boolean, endings?: array, fill?: string, find?: string, font_size?: number, from?: array, icon?: string, knee?: array, opacity?: number, page: integer, path?: string, points?: array, quads?: array, rect?: array, stamp?: string, strokes?: array, to?: array, type: string, width?: number} - `comment_delete` Delete a comment. Delete a comment with its pop-up and replies. Undoable.: {doc: integer, id?: string, index?: integer, page?: integer} -- `comment_edit` Edit a comment. Change a comment's text, colour, opacity, line width, rectangle (rectangle/oval/text box) or position (`move` [dx, dy] in points). One undo step.: {color?: string, contents?: string, doc: integer, id?: string, index?: integer, move?: array, opacity?: number, page?: integer, rect?: array, width?: number} +- `comment_edit` Edit a comment. Change a comment's text, colour, opacity, line width, fill (rectangle/oval/polygon; "none" removes it), rectangle (rectangle/oval/text box/stamp) or position (`move` [dx, dy] in points). Stamps keep their original appearance when resized. One undo step.: {color?: string, contents?: string, doc: integer, fill?: string, id?: string, index?: integer, move?: array, opacity?: number, page?: integer, rect?: array, width?: number} +- `comment_image_preview` Preview image signature layers. Return a PNG of the page without the selected image signature/initials (layer=background, default), or its embedded image with alpha (layer=image). Includes the displayed rectangle, document rotation, image_rotation (how far clockwise the image is shown turned) and annotation opacity. Cache these layers for live placement/resizing; commit once with comment_edit. Does not change the document or undo history.: {doc: integer, dpi?: number, id?: string, index?: integer, layer?: string, page?: integer} - `comment_list` List comments. Every comment (annotation other than links, form widgets and pop-ups) with its page, index, id, type, author, text, date, rectangle, colour, review status and replies.: {doc: integer, page?: integer} - `comment_lock` Lock a comment. Lock or unlock a comment (Properties ▸ Locked). Locked comments can't be moved, resized, restyled or deleted; their text stays editable. Undoable.: {doc: integer, id?: string, index?: integer, locked?: boolean, page?: integer} - `comment_mark` Mark a comment with a checkmark. Mark or unmark a comment with a checkmark (Acrobat: Mark with checkmark), recorded as a private reply. Undoable.: {author?: string, doc: integer, id?: string, index?: integer, marked?: boolean, page?: integer} @@ -58,14 +62,15 @@ A tag after the id marks a command that reaches past the open document (safety.j - `doc_audit_space` Audit space usage. How many bytes each kind of content takes and its share of the file (PDF Optimizer ▸ Audit space usage): images, content streams, fonts, forms, comments, structure, bookmarks, … and document overhead.: {doc: integer} - `doc_background` [file] Add background. Fill page backgrounds with a colour, or a picture from `file` (an image, or page `file_page` of a PDF) fitted at `scale` (default 1), behind the content. Undoable.: {color?: string, doc: integer, file?: string, file_page?: integer, opacity?: number, pages?: array, replace?: boolean, scale?: number} - `doc_close` Close a document. Close a document. Fails if it has unsaved changes unless discard_changes is true.: {discard_changes?: boolean, doc: integer} -- `doc_combine` [file] Combine files. Combine PDFs, in order, into one (bookmarks are kept under one entry per file). pages optionally chooses each file's pages, in step with paths: a range such as "1-3, 6" or null for all pages.: {open?: boolean, out?: string, pages?: array, paths: array} +- `doc_combine` [file] Combine files. Combine files, in order, into one PDF (bookmarks are kept under one entry per file). Images (PNG, JPEG, TIFF, GIF, BMP, JPEG 2000) and .txt files are made into PDFs first. pages optionally chooses each file's pages, in step with paths: a range such as "1-3, 6" or null for all pages. passwords, also in step with paths, opens encrypted files (the open password, or the permissions password where a file's security doesn't allow copying pages; null for none). The result is not encrypted. Passwords are never echoed back. groups, also in step with paths, splits a file around others: entries with the same number (they must name the same path) are one file, copied once, so it keeps one bookmark at its first page, its links and its form fields; null for a file of its own. A group uses its first entry's password.: {groups?: array, open?: boolean, out?: string, pages?: array, passwords?: array, paths: array} - `doc_compare` Compare files. Compare the text of two open documents: other is the older version, doc the newer. Returns counts and each change (replaced, inserted, deleted) with the old and new text, pages (1-based) and rectangles (points, origin bottom-left).: {doc: integer, limit?: integer, other: integer, visual?: boolean} - `doc_compare_mark` Mark differences as comments. Add the differences from other (older) to doc (newer) as comments in doc: highlights over replaced (blue) and inserted (green) text, notes where text was deleted (red), authored Compare. Undoable.: {doc: integer, other: integer} - `doc_compare_report` [file] Compare report. Write the compare summary report (a PDF listing every change) to path.: {doc: integer, other: integer, path: string} -- `doc_create` [file] Create a PDF. Create a new, unsaved document and return it like doc_open: `blank` (pages, width, height in points; default 1 US Letter page), `images` (paths of PNG, JPEG, TIFF (every page), GIF or BMP files, one page each at the image's resolution) or `text` (a .txt path, or `text` directly). Save it with doc_save and a path.: {dpi?: number, from: string, height?: number, name?: string, pages?: integer, path?: string, paths?: array, text?: string, width?: number} +- `doc_create` [file] Create a PDF. Create a new, unsaved document and return it like doc_open: `blank` (pages, width, height in points; default 1 US Letter page), `images` (paths of PNG, JPEG, TIFF (every page), GIF or BMP files, one page each at the image's resolution) or `text` (a .txt path, or `text` directly; Western European (WinAnsi) characters only, others are refused). Save it with doc_save and a path.: {dpi?: number, from: string, height?: number, name?: string, pages?: integer, path?: string, paths?: array, text?: string, width?: number} +- `doc_create_multiple` [file] Create PDF from multiple files. Convert several files (PDFs, PNG/JPEG/JPEG 2000/TIFF/GIF/BMP images, .txt files) to PDF in one run. mode "combine" (default) joins them, in order, into one PDF with a bookmark per file; pages optionally chooses each file's pages, in step with paths (a range such as "1-3, 6" or null). mode "separate" writes one PDF per file into out_dir (files that are already PDFs are skipped; existing files are never overwritten) and reports each file's result.: {mode?: string, open?: boolean, out?: string, out_dir?: string, pages?: array, paths: array} - `doc_export_all_images` [file] Export all images. Write the images that pages use into a folder (`_Page__Image_.jpg|png`), each once: JPEG images unchanged, others as PNG with their soft mask as alpha. min_size skips images with fewer pixels on their shorter side. Images that can't be decoded yet (JPEG 2000, JBIG2, CCITT, separations) are listed under skipped. Includes unsaved edits.: {doc: integer, folder: string, min_size?: integer, pages?: array} - `doc_export_data` [file] Export comments or form data. Write comments and/or form data to path; the extension picks the format: .xfdf or .fdf (comments and/or fields), .xml, .csv or .txt (form data). what: all (default), comments, fields.: {doc: integer, path: string, what?: string} -- `doc_export_images` [file] Export pages as images. Write pages as PNG, JPEG or TIFF files (`_page_.png|jpg|tif`) into a folder, at a resolution (default 150 dpi). JPEG and TIFF are flattened onto white paper. Includes unsaved edits.: {doc: integer, dpi?: number, folder: string, format?: string, pages?: array, quality?: integer} +- `doc_export_images` [file] Export pages as images. Write pages as PNG, JPEG or TIFF files (`_page_.png|jpg|tif`) into a folder, at a resolution (default 150 dpi). JPEG and TIFF are flattened onto white paper. A page too large for the renderer at that resolution is drawn at the most it allows, listed in `lower_dpi` ({page, dpi}). Includes unsaved edits.: {doc: integer, dpi?: number, folder: string, format?: string, pages?: array, quality?: integer} - `doc_export_office` [file] Export to Word, HTML or RTF. Export a PDF ▸ Word (.docx), HTML (.html, one file with images inline) or RTF (.rtf), chosen by path's extension: paragraphs in reading order, headings from larger text, bold and italic, images where they fall, a page break between pages.: {doc: integer, path: string} - `doc_export_text` [file] Export text. Write the reading-order text of pages to a .txt file (pages separated by form feeds).: {doc: integer, pages?: array, path: string} - `doc_flatten` Flatten. Merge comment and/or form field appearances into the page content, so they print and display everywhere but can no longer be edited. Undoable until saved.: {comments?: boolean, doc: integer, fields?: boolean} @@ -74,21 +79,22 @@ A tag after the id marks a command that reaches past the open document (safety.j - `doc_import_data` [file] Import comments or form data. Import comments and/or field values from an XFDF, FDF, XML, CSV or tab-delimited text file (detected from its content). Comments with the same name are replaced; values go through the form's formats and validation. Undoable.: {doc: integer, path: string} - `doc_info` Inspect a document. Metadata, page sizes and labels, bookmarks, annotations, form fields, links, layers, attachments, fonts, security and repair notes.: {doc: integer} - `doc_initial_view` Initial view. Read or change how the document opens (Document Properties ▸ Initial View) and its reading options: navigation (page, bookmarks, pages, attachments, layers), layout (default, single, continuous, two_up, two_up_continuous, two_up_cover, two_up_continuous_cover), magnification (default, actual, fit_page, fit_width, fit_height, fit_visible, or a percentage), page, window options (fit_window, center_window, full_screen, display_title), interface options (hide_menubar, hide_toolbar, hide_window_ui), language and binding (left, right). Only the given ones change; returns the result. Undoable.: {binding?: string, center_window?: boolean, display_title?: boolean, doc: integer, fit_window?: boolean, full_screen?: boolean, hide_menubar?: boolean, hide_toolbar?: boolean, hide_window_ui?: boolean, language?: string, layout?: string, magnification?: any, navigation?: string, page?: integer} +- `doc_inspect` Inspect open documents. With doc, return doc_info; otherwise return information for every open document (empty documents array when none are open).: {doc?: integer} - `doc_list` List open documents. List the open documents with their ids, page counts and unsaved state.: {} - `doc_open` [code] Open a PDF. Open a PDF file and return its document id, page count and whether it can be edited.: {password?: string, path: string} - `doc_open_revision` [code] Open a revision. Open saved revision `revision` (1 = the oldest) of a document as a new, unsaved document, to see the file as it was then.: {doc: integer, revision: integer} -- `doc_optimize` [file] Optimize PDF. Write an optimized copy to `path` (Acrobat's PDF Optimizer). color / gray: { downsample, ppi, above_ppi, compression: jpeg|zip|retain, quality 1–100 } (defaults: downsample to 150 ppi above 225, JPEG 60). Images are measured where pages draw them and replaced only when smaller. discard_*: thumbnails (default true), alternate_images (true), tags, print_settings; flate_unencoded (true); remove_invalid_links and remove_unreferenced_dests (true). discard: Remove Hidden Information categories (metadata, attachments, comments, form-fields, hidden-text, hidden-layers, bookmarks, links-actions-scripts, private-data). Signed documents are refused. The open document is unchanged.: {color?: object, discard?: array, discard_alternate_images?: boolean, discard_print_settings?: boolean, discard_tags?: boolean, discard_thumbnails?: boolean, doc: integer, flate_unencoded?: boolean, gray?: object, path: string, remove_invalid_links?: boolean, remove_unreferenced_dests?: boolean} -- `doc_print` [device] Print. Print with Acrobat's Print dialog options, or save the print-ready PDF. pages: a range such as "1-3, 6, 9-" (page labels allowed; default all); subset odd/even; reverse. layout: fit (default), actual, shrink, custom (scale %), multiple (per_sheet 2/4/6/9/16, order, border, auto_rotate; cut-stack order arranges single-sided sheets for cutting into piles and stacking left to right, top to bottom, keeping sheet order within each pile; duplex must be off), booklet (booklet_subset both/front/back, binding left/right), poster (scale %, overlap pt, cut_marks). orientation auto/portrait/landscape; comments_forms document / document-and-markups (default) / document-and-stamps / form-fields-only; paper Letter/Legal/Tabloid/A3/A4/A5. Then path (save) or printer (a name or "default") with copies, collate, duplex off/long-edge/short-edge, grayscale.: {auto_rotate?: boolean, binding?: string, booklet_subset?: string, border?: boolean, collate?: boolean, comments_forms?: string, copies?: integer, cut_marks?: boolean, doc: integer, duplex?: string, grayscale?: boolean, layout?: string, order?: string, orientation?: string, overlap?: number, pages?: string, paper?: string, path?: string, per_sheet?: integer, printer?: string, reverse?: boolean, scale?: number, subset?: string} +- `doc_optimize` [file] Optimize PDF. Write an optimized copy to `path` (Acrobat's PDF Optimizer). color / gray: { downsample, ppi, above_ppi, compression: jpeg|zip|retain, quality 1–100 } (defaults: downsample to 150 ppi above 225, JPEG 60). Images are measured where pages draw them and replaced only when smaller. discard_*: thumbnails (default true), alternate_images (true), tags, print_settings; flate_unencoded (true); remove_invalid_links and remove_unreferenced_dests (true); images and forms the pages list but never draw are always dropped (unused_xobjects). discard: Remove Hidden Information categories (metadata, attachments, comments, form-fields, hidden-text, hidden-layers, bookmarks, links-actions-scripts, private-data). Signed documents are refused. The open document is unchanged.: {color?: object, discard?: array, discard_alternate_images?: boolean, discard_print_settings?: boolean, discard_tags?: boolean, discard_thumbnails?: boolean, doc: integer, flate_unencoded?: boolean, gray?: object, path: string, remove_invalid_links?: boolean, remove_unreferenced_dests?: boolean} +- `doc_print` [device] Print. Print with Acrobat's Print dialog options, or save the print-ready PDF. pages: a range such as "1-3, 6, 9-" (page labels allowed; default all); subset odd/even; reverse. layout: fit (default), actual, shrink, custom (scale %), multiple (per_sheet 2/4/6/9/16, order, border, auto_rotate; cut-stack order arranges single-sided sheets for cutting into piles and stacking left to right, top to bottom, keeping sheet order within each pile; duplex must be off), booklet (booklet_subset both/front/back, binding left/right), poster (scale %, overlap pt, cut_marks). orientation auto/portrait/landscape; comments_forms document / document-and-markups (default) / document-and-stamps / form-fields-only; paper Letter/Legal/Tabloid/A3/A4/A5. Then path (save) or printer (a name or "default") with copies, collate, duplex off/long-edge/short-edge, grayscale, and options (driver options from printer_options, CUPS).: {auto_rotate?: boolean, binding?: string, booklet_subset?: string, border?: boolean, collate?: boolean, comments_forms?: string, copies?: integer, cut_marks?: boolean, doc: integer, duplex?: string, grayscale?: boolean, layout?: string, options?: object, order?: string, orientation?: string, overlap?: number, pages?: string, paper?: string, path?: string, per_sheet?: integer, printer?: string, reverse?: boolean, scale?: number, subset?: string} - `doc_protect` Protect with passwords. Encrypt the document (applied by the next doc_save, a full rewrite). open_password is needed to open it. permissions_password is needed to change security and lifts the restrictions given by printing/changes/copy/accessibility; those restrictions (and their defaults) apply only when permissions_password is given. With open_password alone the document is encrypted and everything stays allowed, so passing a restriction without permissions_password is an error. Passwords are never echoed back. Undoable.: {accessibility?: boolean, changes?: string, compatibility?: string, copy?: boolean, doc: integer, encrypt_metadata?: boolean, open_password?: string, permissions_password?: string, printing?: string} -- `doc_reduce` [file] Reduce file size. Write a smaller copy of the document to `path` with Acrobat's Reduce File Size choices: images above 225 ppi downsampled to 150 ppi and JPEG-compressed (medium quality), thumbnails dropped, identical fonts and images merged, unused objects dropped, compressed object streams. The open document is unchanged.: {doc: integer, path: string} +- `doc_reduce` [file] Reduce file size. Write a smaller copy of the document to `path` with Acrobat's Reduce File Size choices: images above 225 ppi downsampled to 150 ppi and JPEG-compressed (medium quality), thumbnails dropped, identical fonts and images merged, unused objects dropped, compressed object streams. If the copy wouldn't be smaller, nothing is written and `written` is false. The open document is unchanged.: {doc: integer, path: string} - `doc_remove_hidden` Remove hidden information / Sanitize. Remove the listed categories (see doc_hidden_info), or every category when none are listed (Sanitize Document). Form fields are flattened so their values stay visible. The next save rewrites the whole file. Undoable until saved.: {categories?: array, doc: integer} - `doc_remove_marks` Remove header & footer, watermark or background. Remove every header and footer, watermark or background PdfCraft (or a compatible tool) added. Undoable.: {doc: integer, kind: string} - `doc_revisions` List revisions. List the document's saved revisions (oldest first): each incremental update is one. Returns revision number, where it ends in the file and its size, and which signatures sign exactly that revision.: {doc: integer} -- `doc_save` [file] Save a document. Save to its own file (an incremental update, which keeps signatures valid) or to a new path (a full rewrite). The write is atomic.: {doc: integer, full?: boolean, path?: string} +- `doc_save` [file] Save a document. Save to its own file (an incremental update, which keeps signatures valid) or to a new path (a full rewrite). The write is atomic. flatten_fill_sign bakes Fill & Sign text, marks and signatures into the page first and removes those annotations; other comments and form fields stay. Omit it, or pass false, to leave them editable.: {doc: integer, flatten_fill_sign?: boolean, full?: boolean, path?: string} - `doc_set_info` Set document metadata. Set a document information entry such as Title, Author, Subject or Keywords. Undoable.: {doc: integer, key: string, value: string} -- `doc_split` [file] Split a document. Split into several files written to out_dir: every N pages, before given pages, at top-level bookmarks (bookmarks: true; files named after them), or by file size (max_mb). Files are -partK.pdf.: {before?: array, bookmarks?: boolean, doc: integer, every?: integer, max_mb?: number, out_dir: string} +- `doc_split` [file] Split a document. Split into several files written to out_dir: every N pages, before given pages, at top-level bookmarks (bookmarks: true; files named after them, repeated titles get -2, -3), or by file size (max_mb). Files are -partK.pdf.: {before?: array, bookmarks?: boolean, doc: integer, every?: integer, max_mb?: number, out_dir: string} - `doc_unprotect` Remove security. Remove password security (the document must have been opened with its permissions password, or have none). Applied by the next doc_save. Undoable.: {doc: integer} -- `doc_watermark` [file] Add watermark. Add a watermark to pages: text, or a picture from `file` (an image, or page `file_page` of a PDF) at `scale` of the page (default 0.5); rotated (degrees counter-clockwise, default 45), semi-transparent (opacity 0–1, default 0.5), text fitted to the page unless font_size is given, on top unless behind: true. replace: true swaps out existing ones. Undoable.: {behind?: boolean, color?: string, doc: integer, file?: string, file_page?: integer, font_size?: number, opacity?: number, pages?: array, replace?: boolean, rotation?: number, scale?: number, text?: string} +- `doc_watermark` [file] Add watermark. Add a watermark to pages: text, or a picture from `file` (an image, or page `file_page` of a PDF) at `scale` of the page (default 0.5); rotated (degrees counter-clockwise, default 45), semi-transparent (opacity 0–1, default 0.5), text fitted to the page unless font_size is given, on top unless behind: true; moved from the page centre by `offset_x` (right) and `offset_y` (up) points (default 0). replace: true swaps out existing ones. Undoable.: {behind?: boolean, color?: string, doc: integer, file?: string, file_page?: integer, font_size?: number, offset_x?: number, offset_y?: number, opacity?: number, pages?: array, replace?: boolean, rotation?: number, scale?: number, text?: string} ## edit @@ -97,7 +103,8 @@ A tag after the id marks a command that reaches past the open document (safety.j ## fill -- `fill_sign_add` Fill & Sign: type text or place a mark. Fill in a form that has no fields, as Acrobat's Fill & Sign does: type text (`text`, 10 pt), place a check, cross, dot or line, today's date, or a typed signature or initials (`text` drawn in a script font as filled outlines; at is its left edge, centred vertically), at `at` [x, y] in points from the top-left of the page (the text's top-left; a mark's centre). Creates movable, undoable annotations.: {at: array, author?: string, doc: integer, page: integer, text?: string, type: string} +- `fill_sign_add` [file] Fill & Sign: type text or place a mark. Fill in a form that has no fields, as Acrobat's Fill & Sign does: type text (`text`, 10 pt), place a check, cross, dot or line, today's date (in Preferences ▸ Date format, see fill_sign_date_format, or `format`), or a signature or initials (either `text` drawn in a script font, or `path` to a local PNG/JPEG image up to 4 MiB and 4 megapixels, preserving transparency). `at` [x, y] is in points from the top-left of the page: text's top-left, mark's centre, signature's left edge centred vertically. Image signatures fit within 150 pt wide and 32 pt tall (24 pt for initials). Creates movable, undoable annotations.: {at: array, author?: string, doc: integer, format?: string, language?: string, page: integer, path?: string, text?: string, type: string} +- `fill_sign_date_format` Date format for Fill & Sign. Preferences ▸ Date format: the pattern Fill & Sign dates use (default m/d/yyyy). Set it with `format` (Acrobat date codes yyyy yy mmmm mmm mm m dddd ddd dd d, other runs of y, m or d are refused; H h M s t are time letters and need \ before them; \ shows the next character as is), or read it. `language` sets the language of month and weekday names (a code from `languages`, or `auto` to follow the app's interface language; English headless). Returns the format, the language, today in them, `unwritable` (characters of today's date that Fill & Sign can't write into a PDF yet, Western European only; fill_sign_add refuses such a date), the ready-made presets and the languages.: {format?: string, language?: string} ## form @@ -105,20 +112,20 @@ A tag after the id marks a command that reaches past the open document (safety.j - `form_add_field` Prepare form: add a field. Add a form field on a page. type: text, date, checkbox, radio, combo, list, button, image (a picture placeholder; fill it with form_set_image), signature. rect in points from the top-left of the displayed page [x0, y0, x1, y1]. name defaults to Acrobat's next free name (Text1, Check Box1, Group1, Dropdown1, List Box1, Button1, Image1, Signature1, Date1). Radio buttons join the radio group named by group (a new group otherwise) with the export value export. Returns the field's name. Undoable.: {caption?: string, doc: integer, editable?: boolean, export?: string, group?: string, multi_select?: boolean, multiline?: boolean, name?: string, options?: array, page: integer, rect: array, type: string} - `form_delete_field` Delete a field. Delete a form field and all its widgets. Undoable.: {doc: integer, field: string} - `form_detect_fields` Detect form fields. Prepare a form ▸ automatic field detection: find the blanks a printed form asks to be filled (underscore runs, lines, empty boxes, small squares for check boxes) and name each field from its label. With add (default true) the fields are created as one undoable step; otherwise they are only proposed. Returns page (1-based), kind, name and rect (points, origin bottom-left).: {add?: boolean, doc: integer, pages?: array} -- `form_fields` List form fields. Every interactive form field: name, type (text, checkbox, radio, combo, list, button, signature), value, options, page and rect (top-left-origin points), read-only and required flags.: {doc: integer} +- `form_fields` List form fields. Every interactive form field: name, type (text, checkbox, radio, combo, list, button, signature), value, options, page, rect (top-left-origin points) and rotation (0, 90, 180 or 270 degrees counterclockwise), read-only and required flags.: {doc: integer} - `form_fill` [code] Fill in form fields. Set several fields at once (one undo step). values maps field names (from form_fields) to: a string for text fields, combo boxes and radio groups (an option), true/false for check boxes, an array of strings for multi-select lists. Appearances are regenerated so every viewer shows the values.: {doc: integer, values: object} - `form_merge_data` [file] Merge data files into spreadsheet. Collect the field values of form data files (FDF, XFDF) or filled-in PDF forms into one CSV file at path: a column per field name, a row per file. Returns the row and column counts.: {path: string, paths: array} - `form_reset` [code] Clear form. Reset fields to their default values: all of them, or only those listed. Undoable.: {doc: integer, fields?: array} - `form_set_actions` [code] Set field actions. Field Properties ▸ Actions: replace the field's actions. Each item: trigger (mouse_up, mouse_down, mouse_enter, mouse_exit, on_focus, on_blur) and one of javascript (source), url, reset (field names; [] for all), menu (Print, NextPage, PrevPage, FirstPage, LastPage), page (1-based), show / hide (field names), submit (URL). Triggers not listed lose their action. Undoable.: {actions: array, doc: integer, field: string} - `form_set_image` [file] Set an image field's picture. Show an image file (PNG, JPEG, TIFF, GIF or BMP) in an image field or button, scaled to fit and centred (what clicking an image field does). Undoable.: {doc: integer, field: string, path: string} -- `form_set_props` [code] Field properties. Change a field's properties (General and Options tabs): name (renames it), tooltip, read_only, required, multiline, max_length (0 = no limit), options (combo/list items), font_size (0 = auto), rect (position), format, validate, calculate (Acrobat's Format/Validate/Calculate tabs, run natively: values are checked, formatted and recalculated as in Acrobat). Options tab: align (left, center, right), default (the value Reset form restores; for check boxes and radio buttons their on state, "" for off), flags { scroll, rich_text, password, file_select, spell_check, comb (needs max_length), sort, editable, multi_select, commit_immediately }, check_style (check boxes and radio buttons: check, circle, cross, diamond, square, star). Only the given ones change. Undoable.: {align?: string, appearance?: object, calculate?: any, check_style?: string, default?: string, doc: integer, field: string, flags?: object, font_size?: number, format?: any, locked?: boolean, max_length?: integer, multiline?: boolean, name?: string, options?: array, read_only?: boolean, rect?: array, required?: boolean, tooltip?: string, validate?: any} +- `form_set_props` [code] Field properties. Change field properties: give field for one field, or fields for a list of unique field names (1–1000). A bulk change is atomic and one Undo step; an invalid or locked field leaves every field unchanged. Only the given properties change, and partial appearance changes preserve each field's other colours and font. name (rename), rect (position) and rotation require a single field. General and Options: tooltip, read_only, required, multiline, max_length (0 = no limit), options (combo/list items), font_size (0 = auto), format, validate, calculate (Acrobat's native AF rules). Options: align (left, center, right), default (Reset form's value; check boxes/radio buttons use their on state, "" for off), flags { scroll, rich_text, password, file_select, spell_check, comb (needs max_length), sort, editable, multi_select, commit_immediately }, check_style (check boxes/radio buttons: check, circle, cross, diamond, square, star). rotation is 0, 90, 180 or 270 degrees counterclockwise and swaps width/height when the axis changes. Undoable.: {align?: string, appearance?: object, calculate?: any, check_style?: string, default?: string, doc: integer, field?: string, fields?: array, flags?: object, font_size?: number, format?: any, locked?: boolean, max_length?: integer, multiline?: boolean, name?: string, options?: array, read_only?: boolean, rect?: array, required?: boolean, rotation?: integer, tooltip?: string, validate?: any} - `form_set_script` [code] Set a field's JavaScript. Field Properties ▸ Run custom script: set field's JavaScript for event keystroke, format, validate or calculate (the field's actions; calculated fields join the calculation order) or mouse_up (a button's action). Omit script to remove it. The scripts use Acrobat's object model (event.value, event.rc, getField, util.printf, …) and run when the field changes. Undoable.: {doc: integer, event: string, field: string, script?: string} - `form_tab_order` Set the tab order. Set the tab order of pages (default all): row (top to bottom, left to right), column, structure, or annotations (unspecified). Or order tabs manually: `field` with `move` earlier|later moves that field one place on its page. Returns the resulting order of fields. Undoable.: {doc: integer, field?: string, move?: string, order?: string, pages?: array} ## image -- `image_edit` [file] Edit an image. Change one of a page's images (number from page_images): action move (rect: new box in top-left-origin points), rotate (quarters clockwise, default 1), flip_horizontal, flip_vertical, replace (path: an image file, drawn in the same place) or delete. Undoable.: {action: string, doc: integer, image: integer, page: integer, path?: string, quarters?: integer, rect?: array} -- `image_save` [file] Save image as. Write one of a page's images to path: JPEG images unchanged, others as PNG (the extension is added when missing).: {doc: integer, image: integer, page: integer, path: string} +- `image_edit` [file] Edit an image. Change one of a page's images (number from page_images): action move (rect: new box in top-left-origin points), rotate (quarters clockwise, default 1), flip_horizontal, flip_vertical, replace (path: an image file, drawn in the same place) or delete. Undoable. Form artwork supports move/resize/rotate/flip/delete as a group; replace and image_save only support raster images.: {action: string, doc: integer, image: integer, page: integer, path?: string, quarters?: integer, rect?: array} +- `image_save` [file] Save image as. Write one of a page's raster images (number from page_images; forms are refused) to path: JPEG images unchanged, others as PNG (the extension is added when missing).: {doc: integer, image: integer, page: integer, path: string} ## js @@ -150,20 +157,26 @@ A tag after the id marks a command that reaches past the open document (safety.j - `measure_scale` Set or read a measurement scale. Read the scale at a point, or add a rectangular viewport using units_per_point or two calibration points and their real-world distance. Existing measurements retain their original scales. Undoable.: {at?: array, distance?: number, doc: integer, name?: string, page: integer, points?: array, precision?: integer, rect?: array, unit?: string, units_per_point?: number} - `measure_snap` Snap a measurement vertex. Snap a point to vector paths, endpoints, midpoints or intersections. Coordinates and tolerance are in display points. Bounded extraction reports truncated geometry.: {at: array, doc: integer, endpoints?: boolean, intersections?: boolean, midpoints?: boolean, page: integer, paths?: boolean, tolerance?: number} +## object + +- `object_list` List editable objects. List existing paragraphs, Image/Form artwork and added content once, with source kind/index references, displayed rectangles and document generation. Indexes change when content changes; use generation in object_move to reject stale references.: {doc: integer, page: integer} +- `object_move` Move objects together. Atomically translate 1–1000 mixed references from object_list by [dx, dy] in displayed points (right/down). Preserves glyph codes, fonts, image data and relative spacing; success is one undo step. Clipping and vertical text are refused. A bad/stale reference refuses the entire move.: {doc: integer, generation?: integer, objects: array, offset: array, page: integer} + ## ocr - `ocr_recognize` Recognize text (OCR). Scan & OCR ▸ Recognize text: render pages, read the words in them and add them as invisible text over the page image, so scanned pages become searchable and selectable (a searchable image; the image is not changed). Pages that already have text are skipped unless skip_text_pages is false. Returns each page's recognised text, word count or why it was skipped. Needs the OCR models (ocr_status). Undoable as one step.: {doc: integer, dpi?: number, language?: string, pages?: array, skip_text_pages?: boolean} - `ocr_recognize_files` [code] Recognize text in multiple files. Scan & OCR ▸ Recognize text ▸ In multiple files: read every page of each PDF in paths and write the searchable result into folder under the same name (pages that already have text are left alone). Returns, per file, the output path, word count and skipped pages, or the error.: {dpi?: number, folder: string, language?: string, paths: array} -- `ocr_status` [host] OCR status. Whether text recognition is available (its models are installed: run `cargo xtask models` or set PDFCRAFT_MODELS), where it looks for them, and the languages it reads.: {} +- `ocr_status` [host] OCR status. Whether text recognition is available (its models are installed: release packages include them; a source build fetches them with `cargo xtask models`; PDFCRAFT_MODELS overrides), where it looks for them, and the languages it reads.: {} ## page - `page_add_image` [file] Add an image to a page. Add an image file (PNG, JPEG, TIFF, GIF, BMP) as page content: in rect (points from the top-left of the page), or centred at its natural size (shrunk to fit). Undoable; movable with content_update.: {doc: integer, page: integer, path: string, rect?: array} -- `page_add_text` Add text to a page. Add text as page content (not a comment). Place it with at [x, y] (top-left, points from the top-left of the displayed page) and width (wrap width, default 200), or rect. Newlines start new lines; long lines wrap. Style: font helvetica/times/courier, size, bold, italic, color (#RRGGBB or a name), align left/center/right. It stays editable with content_update. Undoable.: {align?: string, at?: array, bold?: boolean, color?: string, doc: integer, font?: string, italic?: boolean, page: integer, rect?: array, size?: number, text: string, width?: number} +- `page_add_text` Add text to a page. Add text as page content (not a comment). Place it with at [x, y] (top-left, points from the top-left of the displayed page) and width (wrap width, default 200), or rect. Newlines start new lines; long lines wrap. Style: font helvetica/times/courier, size, bold, italic, color (#RRGGBB or a name), align left/center/right. Text must be Western European (WinAnsi) characters; others are refused. It stays editable with content_update. Undoable.: {align?: string, at?: array, bold?: boolean, color?: string, doc: integer, font?: string, italic?: boolean, page: integer, rect?: array, size?: number, text: string, width?: number} - `page_delete` Delete pages. Delete pages. Undoable until saved.: {doc: integer, pages: array} - `page_duplicate` Duplicate pages. Insert copies of pages after the last of them (fonts and images are shared, not copied). Undoable.: {doc: integer, pages: array} +- `page_enhance_camera` Enhance camera image. Straighten a photographed document (Scan & OCR ▸ Enhance ▸ Camera image): the part of the page inside the four corners (x1, y1, … x4, y4 in top-left-origin points, any order, a convex quadrilateral; when omitted, the document's edges are found automatically, or an error says they could not be) is warped to an upright rectangle that replaces what the page shows, at the page picture's own resolution (at most 600 dpi). The page's comments, links and bookmarks stay. Undoable.: {corners?: array, doc: integer, page: integer} - `page_extract` [file] Extract pages. Copy pages into a new PDF (links, bookmarks, fields and layers that belong to them come along). separate: true writes each page as its own file into out_dir; delete: true removes the pages from this document afterwards (undoable).: {delete?: boolean, doc: integer, open?: boolean, out?: string, out_dir?: string, pages: array, separate?: boolean} -- `page_images` List page images. The images a page draws (Edit a PDF): number, box (top-left-origin points), pixel size and resource name.: {doc: integer, page: integer} +- `page_images` List page images. The raster images and grouped Form artwork a page draws (Edit a PDF): number, box (top-left-origin points), kind (image/form), pixel size ([0,0] for forms) and resource name. Forms include nested graphics and are edited as a whole. Numbers count forms and images together in drawing order, so a page with forms numbers its images differently than before forms were listed: always take numbers from a fresh page_images call.: {doc: integer, page: integer} - `page_insert_blank` Insert a blank page. Insert a blank page so it becomes page `at`. Size defaults to the neighbouring page. Undoable.: {at: integer, doc: integer, height?: number, width?: number} - `page_insert_file` [file] Insert pages from a file. Insert pages of another PDF so the first becomes page `at`. Undoable.: {at: integer, doc: integer, pages?: array, path: string} - `page_move` Move pages. Move pages so the first of them lands at position `to` (1-based, counted before the move). Undoable.: {doc: integer, pages: array, to: integer} @@ -178,6 +191,10 @@ A tag after the id marks a command that reaches past the open document (safety.j - `pdfa_convert` Save as PDF/A. Standards ▸ Save as PDF/A: fix what can be fixed for PDF/A-2b or 3b (XMP identification and metadata, an sRGB output intent, forbidden actions and JavaScript, annotation print flags, image interpolation, encryption). Returns what was fixed and what remains (e.g. fonts that aren't embedded). Undoable; save the document to keep it.: {doc: integer, level?: string} - `pdfa_verify` Verify PDF/A. Standards ▸ Verify PDF/A compliance: the PDF/A-2b or 3b rules the document breaks (ISO 19005 clause, message, page, whether Save as PDF/A can fix it), plus what it declares.: {doc: integer, level?: string} +## printer + +- `printer_options` [device] List printer options. A printer driver's own job options (CUPS: from its PPD), such as the paper tray, paper type or finishing: key, label, group, default and choices. Pass chosen values to doc_print as options. Empty on Windows, where the driver's preferences window holds them.: {printer: string} + ## printers - `printers` [device] List printers. The printers the system's print spooler knows (CUPS on macOS and Linux), with the default marked.: {} @@ -186,7 +203,11 @@ A tag after the id marks a command that reaches past the open document (safety.j - `redact_apply` Apply redactions. Apply the redaction marks (all, or those on pages): text, images, vectors, comments and form fields under them are removed for good and boxes are drawn in their place. A verification pass fails the operation if anything readable remains. Undoable until saved; the saved file no longer contains the content.: {doc: integer, pages?: array} - `redact_clear` Remove redaction marks. Remove every redaction mark without applying it. Undoable.: {doc: integer} -- `redact_mark` Mark for redaction. Mark content for redaction (nothing is removed until redact_apply). One of: rect [x0, y0, x1, y1] (points from the top-left of the displayed page) with page; find (text, every match); pattern (phone, email, credit-card, ssn, date: every match, Acrobat's Search & Redact patterns); whole_pages: true. find, pattern and whole_pages work on pages (default all). overlay: text shown on the box once applied; fill: box colour (default black). Undoable.: {author?: string, doc: integer, fill?: string, find?: string, overlay?: string, page?: integer, pages?: array, pattern?: string, rect?: array, whole_pages?: boolean} +- `redact_mark` Mark for redaction. Mark content for redaction (nothing is removed until redact_apply). One of: rect [x0, y0, x1, y1] (points from the top-left of the displayed page) with page; find (text, every match); words (a list of words or phrases, every match of each; the result's matched_words says how many marks each made); pattern (phone, email, credit-card, ssn, date: every match, Acrobat's Search & Redact patterns); whole_pages: true. find, words, pattern and whole_pages work on pages (default all). overlay: text shown on the box once applied, or code_set (foia: U.S. FOIA (b)(1)(A)…(b)(9); privacy-act: U.S. Privacy Act (d)(5), (j)(1)…(k)(7)) with codes, shown as "(b)(6), (b)(7)(C)"; fill: box colour (default black). Undoable.: {author?: string, code_set?: string, codes?: array, doc: integer, fill?: string, find?: string, overlay?: string, page?: integer, pages?: array, pattern?: string, rect?: array, whole_pages?: boolean, words?: array} + +## render + +- `render_preview` Render a preview. Render a PNG bounded by max_side (default 1024), without changing or saving the document. Page defaults to 1; doc may be omitted when exactly one document is open.: {doc?: integer, max_side?: integer, page?: integer} ## sign @@ -195,7 +216,7 @@ A tag after the id marks a command that reaches past the open document (safety.j - `sign_keychain_ids` [host] List Keychain digital IDs. macOS: the signing identities in the user's keychains (certificate details and the keychain: reference sign_document takes). The private keys stay in the Keychain, which may ask the user to allow their use.: {} - `sign_list` List and validate signatures. Every signature field with its validation: status (valid, unknown = intact but the signer isn't trusted, invalid, unsigned), signer and certificate, date, reason, location, certification level, page and rect, the revision it covers and changes made after signing (none, allowed, disallowed), with Acrobat-style explanations.: {doc: integer} - `sign_trust` [file] Trust certificates. Add certificates (.cer/.crt/.pem/.der, or the certificates in a .p12 with `password`) to the trusted certificates used to validate signatures, or clear the list (clear: true). Every open document is revalidated. Returns the trusted list.: {clear?: boolean, password?: string, paths?: array} -- `sign_windows_ids` [host] List Windows store digital IDs. Windows: signing identities in the Current User Personal certificate store (certificate details and the windows: reference sign_document takes). Private keys remain in CNG; Windows may ask permission to use them.: {} +- `sign_windows_ids` [host] List Windows store digital IDs. Windows: signing identities in the Current User Personal certificate store (certificate details and the windows: reference sign_document takes). Private keys remain in CNG; Windows may ask permission to use them. `unusable` lists the store's other certificates with why they can't sign (no private key, an unsupported key type, a key CNG can't open).: {} ## stamp @@ -203,8 +224,8 @@ A tag after the id marks a command that reaches past the open document (safety.j ## text -- `text_edit` Edit text. Replace the text of one paragraph (paragraph, from text_paragraphs: rewrapped to the paragraph's width with its line spacing) or one line (line, from text_lines) in place, keeping position, size and colour. For a paragraph, also change its formatting: font (helvetica, times, courier) with bold/italic, size (points), color (#rrggbb), align (left, center, right, justify), underline, line_spacing (× size), char_spacing (points) and scale (horizontal, percent), or move it (dx, dy in points; up is +dy) and rewrap it to a new width (points); text may then be omitted. Its own font is reused when it can show every character; otherwise the line is set in Helvetica (the result shows the font used). Text that no available font can show is refused. Undoable.: {align?: string, bold?: boolean, char_spacing?: number, color?: string, doc: integer, dx?: number, dy?: number, font?: string, italic?: boolean, line?: integer, line_spacing?: number, page: integer, paragraph?: integer, scale?: number, size?: number, text?: string, underline?: boolean, width?: number} -- `text_extract` Extract text. Extract the text of some or all pages, in reading order.: {doc: integer, pages?: array} +- `text_edit` Edit text. Replace the text of one paragraph (paragraph, from text_paragraphs: rewrapped to the paragraph's width with its line spacing) or one line (line, from text_lines) in place, keeping position, size and colour. For a paragraph, also change its formatting: font (helvetica, times, courier) with bold/italic, size (points), color (#rrggbb), align (left, center, right, justify), underline, line_spacing (× size), char_spacing (points) and scale (horizontal, percent), or move it (dx, dy in points; up is +dy) and rewrap it to a new width (points); text may then be omitted. Its own font is reused when it can show every character and no font/weight change is requested; otherwise it uses a standard font matching the source style or, for Japanese and other text no standard font can show (Cyrillic, Greek), real Mincho/Gothic fallback outlines matching the requested/source family and available weight, from a face that has every character (the result shows the font used). Missing Japanese weights use Regular, not synthetic bold; the small web build has only Gothic Regular. Paragraph bold may be set without font to keep the source family. Text that no available font can show is refused. Undoable.: {align?: string, bold?: boolean, char_spacing?: number, color?: string, doc: integer, dx?: number, dy?: number, font?: string, italic?: boolean, line?: integer, line_spacing?: number, page: integer, paragraph?: integer, scale?: number, size?: number, text?: string, underline?: boolean, width?: number} +- `text_extract` Extract text. Extract the text of some or all pages, in reading order. With rect, only the text inside that rectangle on each page, as Column select takes it: one row per visual line, side-by-side pieces (table cells) separated by a tab.: {doc: integer, pages?: array, rect?: array} - `text_find` Find text. Find a phrase (case-insensitive, whitespace-normalised) and return each match with its page and line rectangles in points (origin top-left).: {doc: integer, limit?: integer, query: string} - `text_lines` List text lines. The lines of existing text on a page (Edit a PDF ▸ Edit text): number, text, box (top-left-origin points), font and size. Use the number with text_edit.: {doc: integer, page: integer} - `text_paragraphs` List paragraphs. The paragraphs on a page (lines grouped by font, size, alignment and spacing): number, text, its line numbers, box (top-left-origin points), font and size. Use the number with text_edit's paragraph.: {doc: integer, page: integer} diff --git a/apps/pdf/host-service/skill/safety-rules.json b/apps/pdf/host-service/skill/safety-rules.json index 2e64ed27..9ed7a863 100644 --- a/apps/pdf/host-service/skill/safety-rules.json +++ b/apps/pdf/host-service/skill/safety-rules.json @@ -1,6 +1,6 @@ { "engine": "pdfcraft", - "method": "I read the tool table (crates/automation/src/tools.rs), Automation::call (lib.rs) and every handler module, following each into the engine (engine/src/lib.rs Session::open/apply/run_edit, js.rs, xfa.rs, actions.rs, ocr.rs), print::spool, sign (keychain, windows, pdf) and the js crate, and mapped every self.resolve, std::fs and write_atomic call to its tool; greps for std::net, HTTP/TLS clients and std::process found no network client (FormCalc get/post/put are refused) and only lpstat/lp in print::spool. PDF JavaScript/FormCalc execution counts as code although pdfcraft-js is a sandbox: it is on by default and runs on doc_open/doc_open_revision of laid-out XFA forms, on form_fill/form_reset/form_set_script edits, and in the fresh JavaScript-on sessions that action_run and ocr_recognize_files create; tools that install script text which the engine or other viewers run later are code too. Root confinement and the table's read_only/destructive flags were ignored (printers, sign_keychain_ids and ocr_status are flagged read_only). With JavaScript forced off and js_enabled denied, doc_open would drop to file and form_fill, form_reset and doc_open_revision to safe.", + "method": "I read the tool table (crates/automation/src/tools.rs), Automation::call (lib.rs) and every handler module, following each into the engine (engine/src/lib.rs Session::open/apply/run_edit, js.rs, xfa.rs, actions.rs, ocr.rs), print::spool, sign (keychain, windows, pdf) and the js crate, and mapped every self.resolve, std::fs and write_atomic call to its tool; greps for std::net, HTTP/TLS clients and std::process found no network client (FormCalc get/post/put are refused) and only lpstat/lp in print::spool. PDF JavaScript/FormCalc execution counts as code although pdfcraft-js is a sandbox: it is on by default and runs on doc_open/doc_open_revision of laid-out XFA forms, on form_fill/form_reset/form_set_script edits, and in the fresh JavaScript-on sessions that action_run and ocr_recognize_files create; tools that install script text which the engine or other viewers run later are code too. Root confinement and the table's read_only/destructive flags were ignored (printers, sign_keychain_ids and ocr_status are flagged read_only). With JavaScript forced off and js_enabled denied, doc_open would drop to file and form_fill, form_reset and doc_open_revision to safe. Updated for the upstream sync (ymote/pdfcraft merging storytold/pdfcraft main f22b5d4): the twelve new tools (bookmark_from_structure, command_batch, command_run, comment_image_preview, doc_create_multiple, doc_inspect, fill_sign_date_format, object_list, object_move, page_enhance_camera, printer_options, render_preview) were traced the same way, every changed tool schema was compared with the previous pin's, and the diff of the crates automation links was searched for new std::fs, std::process, std::net and environment reads. New reach past the document: fill_sign_add's image `path`, doc_create_multiple's files, and printer_options' PPD read and lpoptions call; doc_combine's files may now be images or text, write_atomic's staging names changed, and the rest (the Windows printer-preferences window, the desktop theme read) is reached only from the desktop UI. fill_sign_add moves from safe to file.", "rules": [ { "ids": [ @@ -97,6 +97,18 @@ "crates/engine/src/actions.rs: Step::RunJavaScript, apply_step (s.run_javascript), run_on (Session::new, s.open)" ] }, + { + "ids": [ + "command_run", + "command_batch" + ], + "class": "code", + "why": "They run other commands. command_run maps a registry command id to the tool that automates it (tools::tool_for_command: 79 tools carry a command id, among them doc_open, doc_save, sign_document, js_run, action_run and ocr_recognize_files) and calls that tool through Automation::call with the caller's `params`, dropping unknown keys with a warning; command_batch runs up to 1,000 such steps in order (MAX_BATCH_STEPS). The inner tool keeps its own argument checks and root confinement, but a deny-list keyed on these two ids cannot see it, as with action_run.", + "evidence": [ + "crates/automation/src/conventions.rs: command_run (tools::tool_for_command, self.call), command_batch (MAX_BATCH_STEPS, command_run per step)", + "crates/automation/src/tools.rs: tool_for_command, the tools' .cmd() registry ids" + ] + }, { "ids": [ "ocr_recognize_files" @@ -132,6 +144,17 @@ "crates/print/src/spool.rs: lpstat_command, printers" ] }, + { + "ids": [ + "printer_options" + ], + "class": "device", + "why": "spool::printer_options reads the queue's PPD from /etc/cups/ppd/.ppd (at most 16 MiB; a name that isn't a PPD keyword, such as one with `/`, reads nothing) and spawns `lpoptions -p -l` (CUPS, looked up on PATH) for the current choices; empty on Windows and where there is no spooler. Flagged read_only in the table, but it queries the print system, like printers.", + "evidence": [ + "crates/automation/src/printing.rs: printer_options", + "crates/print/src/spool.rs: printer_options (is_ppd_keyword, PPD_CAP, lpoptions), parse_ppd, parse_lpoptions" + ] + }, { "ids": [ "doc_print" @@ -194,9 +217,10 @@ "doc_save" ], "class": "file", - "why": "write_atomic writes `..pdfcraft-tmp` beside the target and renames it over `path`, or over the path the document was opened from when `path` is omitted (an incremental update by default).", + "why": "write_atomic creates a staging file `..<16 hex digits>.pdfcraft-tmp` beside the target (create_new, so a file or link planted at that name is never written through) and renames it over `path`, or over the path the document was opened from when `path` is omitted (an incremental update by default). flatten_fill_sign first applies Edit::FlattenFillSign to the open document.", "evidence": [ - "crates/automation/src/lib.rs: doc_save, write_atomic" + "crates/automation/src/lib.rs: doc_save, write_atomic, write_atomic_with", + "crates/platform/src/staging.rs: create_staging (StagingName::SuffixThenTag), staging_suffixes" ] }, { @@ -222,16 +246,28 @@ "crates/automation/src/forms.rs: doc_import_data; crates/engine/src/lib.rs: run_edit ImportData -> pdfcraft_xfdf::import" ] }, + { + "ids": [ + "fill_sign_add" + ], + "class": "file", + "why": "Since this pin a signature or initials can be an image: with `path`, fill_sign_add resolves the caller's path, std::fs opens it and SignatureImage::read takes a PNG or JPEG of at most 4 MiB and 4 megapixels into a new annotation in the open document. Without `path` it is a plain document edit, as before: typed text, a check, cross, dot or line, today's date (in the session's date format, or `format` and `language`), or a typed signature in the script font baked in at build time.", + "evidence": [ + "crates/automation/src/comments.rs: fill_sign_add (self.resolve, std::fs::File::open, SignatureImage::read)", + "crates/engine/src/signature_image.rs: SignatureImage::read (MAX_SIGNATURE_IMAGE_BYTES, MAX_PIXELS)" + ] + }, { "ids": [ "doc_combine", - "doc_create" + "doc_create", + "doc_create_multiple" ], "class": "file", - "why": "doc_combine std::fs::reads every PDF in `paths` (parsed with open_source, not Session::open) and writes the result to `out` or opens it as a new document; doc_create reads image files, or a text file when `path` is given, into a new unsaved document (from: blank touches no file).", + "why": "doc_combine std::fs::reads every file in `paths` (PDFs with their `passwords`, and images or .txt files made into PDFs first by Session::as_pdf; PDFs are parsed with open_source, not Session::open) and writes the result to `out` or opens it as a new document; doc_create reads image files, or a text file when `path` is given, into a new unsaved document (from: blank touches no file); doc_create_multiple reads up to 1,000 files the same way (Session::convert_to_pdf) and either combines them like doc_combine or, in mode separate, writes one PDF per image or text file into `out_dir`, under a name not already taken.", "evidence": [ - "crates/automation/src/lib.rs: doc_combine, deliver, doc_create", - "crates/engine/src/lib.rs: combine_ranges (open_source)" + "crates/automation/src/lib.rs: doc_combine, deliver, doc_create, doc_create_multiple, create_separate, unused", + "crates/engine/src/lib.rs: combine_unlocked, combine_grouped (open_source_with), as_pdf, convert_to_pdf (open_source, pdfcraft_create)" ] }, { @@ -300,12 +336,19 @@ "doc_hidden_info", "command_list", "edit_undo", - "edit_redo" + "edit_redo", + "doc_inspect", + "render_preview", + "comment_image_preview", + "object_list" ], "class": "safe", - "why": "They read the open documents or the static command registry, close a document in the session, or move along the session's undo stacks; page_render returns PNG bytes and the text tools return text, nothing is written (doc_revisions only reads although the table does not flag it read_only).", + "why": "They read the open documents or the static command registry, close a document in the session, or move along the session's undo stacks; page_render, render_preview (at most 4,096 pixels a side, never above 72 dpi) and comment_image_preview (an image signature's picture, or its page rendered without it at up to 600 dpi) return PNG bytes, doc_inspect returns doc_info for one or every open document, object_list lists a page's editable objects, and the text tools return text; nothing is written (doc_revisions only reads although the table does not flag it read_only).", "evidence": [ "crates/automation/src/lib.rs: Automation::call arms for doc_list, doc_info (info()), page_render, text_extract, text_find, text_lines, text_paragraphs, page_images, doc_audit_space, edit_undo, edit_redo; doc_close, doc_revisions, command_list", + "crates/automation/src/conventions.rs: doc_inspect, render_preview", + "crates/automation/src/comments.rs: comment_image_preview; crates/engine/src/signature_image.rs: image_signature_preview", + "crates/automation/src/content.rs: object_list (Document::editable_objects)", "crates/automation/src/redact.rs: doc_hidden_info", "crates/engine/src/commands.rs: is_enabled reads only session state" ] @@ -329,13 +372,16 @@ "text_edit", "page_add_text", "content_list", - "content_delete" + "content_delete", + "page_enhance_camera", + "object_move" ], "class": "safe", - "why": "Undoable edits of the open document through Session::apply (pages, page boxes, labels, metadata, the initial view and viewer preferences stored in the file, header/footer text, flattening, encryption settings applied by the next save, text edits in built-in fonts) or listings of added content. None reads or writes a path or runs scripts.", + "why": "Undoable edits of the open document through Session::apply (pages, page boxes, labels, metadata, the initial view and viewer preferences stored in the file, header/footer text, flattening, encryption settings applied by the next save, text edits in fonts built into the engine, moving listed objects) or listings of added content. page_enhance_camera renders the page (at most 600 dpi and 40 megapixels), finds the document's edges when no corners are given, warps the quadrilateral upright and replaces the page's picture with it: pixels only, no camera or other device. None reads or writes a path or runs scripts.", "evidence": [ - "crates/automation/src/lib.rs: arms for page_rotate, page_delete, page_move, insert_blank, page_duplicate, page_set_box, page_number, doc_set_info, doc_initial_view, marks() (doc_header_footer, doc_remove_marks), doc_flatten, doc_protect, doc_unprotect, text_edit", - "crates/automation/src/content.rs: content_list, page_add_text, content_delete" + "crates/automation/src/lib.rs: arms for page_rotate, page_enhance_camera, page_delete, page_move, insert_blank, page_duplicate, page_set_box, page_number, doc_set_info, doc_initial_view, marks() (doc_header_footer, doc_remove_marks), doc_flatten, doc_protect, doc_unprotect, text_edit", + "crates/automation/src/content.rs: content_list, page_add_text, content_delete, object_move (Edit::MoveObjects)", + "crates/engine/src/camera.rs: detect_camera_corners, enhance_camera_page (MAX_SCALE, MAX_PIXELS, warp)" ] }, { @@ -345,12 +391,13 @@ "bookmark_rename", "bookmark_delete", "bookmark_move", - "bookmark_set_page" + "bookmark_set_page", + "bookmark_from_structure" ], "class": "safe", - "why": "Bookmark (outline) tree listing and edits in the open document.", + "why": "Bookmark (outline) tree listing, a page of at most 1,000 at a time, and edits in the open document; bookmark_from_structure adds bookmarks made from the document's own tagged headings.", "evidence": [ - "crates/automation/src/lib.rs: bookmark_* arms (Edit::AddBookmark, RenameBookmark, DeleteBookmark, MoveBookmark, SetBookmarkPage)" + "crates/automation/src/lib.rs: bookmark_* arms (Edit::AddBookmark, RenameBookmark, DeleteBookmark, MoveBookmark, SetBookmarkPage, BookmarksFromStructure), bookmark_listing (MAX_BOOKMARK_PAGE)" ] }, { @@ -429,13 +476,13 @@ "comments_hide", "comment_edit", "comment_delete", - "fill_sign_add" + "fill_sign_date_format" ], "class": "safe", - "why": "Comment annotations and Fill & Sign marks in the open document (typed signatures use the script font baked in at build time); comments_hide only sets a per-document view flag in the session. comment_add (attachments from a path) and comments_summarize (writes a PDF) are in file rules.", + "why": "Comment annotations in the open document; comments_hide only sets a per-document view flag in the session, and fill_sign_date_format only sets or reads the session's Fill & Sign date format and name language (two fields in session memory, checked against fixed patterns and languages; nothing is saved) and returns today's date in them. comment_add (attachments from a path), comments_summarize (writes a PDF) and fill_sign_add (an image signature from a path) are in file rules.", "evidence": [ - "crates/automation/src/comments.rs: comment_list, comment_reply, comment_set_status, comment_mark, comment_lock, comments_hide (session.set_hide_comments), comment_edit, comment_delete, fill_sign_add", - "crates/fonts/build.rs: fonts are embedded at build time" + "crates/automation/src/comments.rs: comment_list, comment_reply, comment_set_status, comment_mark, comment_lock, comments_hide (session.set_hide_comments), comment_edit, comment_delete, fill_sign_date_format", + "crates/engine/src/dates.rs: Session::set_date_format, set_date_language, today_text (session fields date_format, date_language)" ] }, { diff --git a/apps/pdf/host-service/skill/safety.json b/apps/pdf/host-service/skill/safety.json index c3a7d1b9..20e39d97 100644 --- a/apps/pdf/host-service/skill/safety.json +++ b/apps/pdf/host-service/skill/safety.json @@ -1,14 +1,14 @@ { "engine": "pdfcraft", - "revision": "615ff308173a3dd7c595fc598508b519a895c0e9", + "revision": "51f09eea27c56e8e033f8234fa7f4072ba7846d3", "regenerate": "OCTOSENSE_SKILL_REGEN=1 cargo test --locked -p octosense-pdf-service --test skill", "rules": "safety-rules.json", "classes": { - "safe": {"count": 83, "meaning": "pure document or pixel operations inside the session"}, - "file": {"count": 31, "meaning": "reads or writes a path (a door may allow it only inside the caller's area)"}, - "code": {"count": 12, "meaning": "installs or runs code: plug-ins, scripts, expressions with I/O, or other commands (macros, actions, batches)"}, + "safe": {"count": 90, "meaning": "pure document or pixel operations inside the session"}, + "file": {"count": 33, "meaning": "reads or writes a path (a door may allow it only inside the caller's area)"}, + "code": {"count": 14, "meaning": "installs or runs code: plug-ins, scripts, expressions with I/O, or other commands (macros, actions, batches)"}, "network": {"count": 0, "meaning": "uses the network"}, - "device": {"count": 2, "meaning": "uses hardware: audio, MIDI, cameras, scanners, printers, tablets"}, + "device": {"count": 3, "meaning": "uses hardware: audio, MIDI, cameras, scanners, printers, tablets"}, "host": {"count": 4, "meaning": "host, app or UI state outside the document: windows, panels, views, preferences, shortcuts, the clipboard"} }, "commands": { @@ -21,14 +21,18 @@ "action_run": "code", "bookmark_add": "safe", "bookmark_delete": "safe", + "bookmark_from_structure": "safe", "bookmark_list": "safe", "bookmark_move": "safe", "bookmark_rename": "safe", "bookmark_set_page": "safe", + "command_batch": "code", "command_list": "safe", + "command_run": "code", "comment_add": "file", "comment_delete": "safe", "comment_edit": "safe", + "comment_image_preview": "safe", "comment_list": "safe", "comment_lock": "safe", "comment_mark": "safe", @@ -47,6 +51,7 @@ "doc_compare_mark": "safe", "doc_compare_report": "file", "doc_create": "file", + "doc_create_multiple": "file", "doc_export_all_images": "file", "doc_export_data": "file", "doc_export_images": "file", @@ -58,6 +63,7 @@ "doc_import_data": "file", "doc_info": "safe", "doc_initial_view": "safe", + "doc_inspect": "safe", "doc_list": "safe", "doc_open": "code", "doc_open_revision": "code", @@ -75,7 +81,8 @@ "doc_watermark": "file", "edit_redo": "safe", "edit_undo": "safe", - "fill_sign_add": "safe", + "fill_sign_add": "file", + "fill_sign_date_format": "safe", "form_actions": "safe", "form_add_field": "safe", "form_delete_field": "safe", @@ -109,6 +116,8 @@ "measure_perimeter": "safe", "measure_scale": "safe", "measure_snap": "safe", + "object_list": "safe", + "object_move": "safe", "ocr_recognize": "safe", "ocr_recognize_files": "code", "ocr_status": "host", @@ -116,6 +125,7 @@ "page_add_text": "safe", "page_delete": "safe", "page_duplicate": "safe", + "page_enhance_camera": "safe", "page_extract": "file", "page_images": "safe", "page_insert_blank": "safe", @@ -128,10 +138,12 @@ "page_set_box": "safe", "pdfa_convert": "safe", "pdfa_verify": "safe", + "printer_options": "device", "printers": "device", "redact_apply": "safe", "redact_clear": "safe", "redact_mark": "safe", + "render_preview": "safe", "sign_document": "host", "sign_id_create": "file", "sign_keychain_ids": "host", diff --git a/apps/pdf/host-service/src/doc_tests.rs b/apps/pdf/host-service/src/doc_tests.rs index 24157f06..220b3c40 100644 --- a/apps/pdf/host-service/src/doc_tests.rs +++ b/apps/pdf/host-service/src/doc_tests.rs @@ -623,6 +623,56 @@ fn fields_fill_and_fill_and_sign() { coded(&w.err("fill_sign", json!({"doc": lease, "page": 1, "kind": "check"})), "invalid"); } +/// A 4 × 4 PNG: an image signature the engine would read. +const SIGNATURE_PNG: &[u8] = &[ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x08, 0x06, 0x00, 0x00, 0x00, 0xa9, 0xf1, 0x9e, + 0x7e, 0x00, 0x00, 0x00, 0x12, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x10, 0x50, 0x68, 0xf8, + 0x8f, 0x8c, 0x19, 0x48, 0x17, 0x00, 0x00, 0x7a, 0xe9, 0x1a, 0xf1, 0x2c, 0xd2, 0xc2, 0x5d, 0x00, + 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, +]; + +/// Since the engine's upstream sync, `fill_sign_add` reads an image +/// signature or initials from the file its `path` names (`file` in +/// skill/safety.json). `pdf.fill_sign` never lets a caller name one: `path`, +/// and the command's other new keys `format` and `language`, are refused +/// before any engine work, whatever the kind; the service builds the +/// command's arguments itself. +#[test] +fn fill_sign_never_reads_a_file() { + let w = World::new().samples(); + w.put("sig.png", SIGNATURE_PNG); + // The engine itself reads the file `path` names into the document. + let mut engine = Automation::new().with_root(&w.root).unwrap(); + let (d, _) = open(&mut engine, "Apartment lease.pdf").unwrap(); + run(&mut engine, "fill_sign_add", &json!({"doc": d, "page": 1, "type": "signature", "at": [60, 300], "path": "sig.png"})).unwrap(); + let listed = run(&mut engine, "comment_list", &json!({"doc": d})).unwrap(); + assert_eq!(listed["comments"].as_array().map(Vec::len), Some(1), "the engine placed the image: {listed}"); + + // Through the service, none of those keys gets through. + let doc = w.open("Apartment lease.pdf"); + for (kind, extra) in [ + ("initials", json!({"path": "sig.png"})), + ("initials", json!({"text": "SR", "path": "sig.png"})), + ("text", json!({"text": "Sam Rivera", "path": "sig.png"})), + ("signature", json!({"path": "sig.png"})), + ("check", json!({"path": "sig.png"})), + ("date", json!({"format": "yyyy-mm-dd"})), + ("date", json!({"language": "de"})), + ] { + let mut args = json!({"doc": doc, "page": 1, "kind": kind, "at": [60, 300]}); + args.as_object_mut().unwrap().extend(extra.as_object().unwrap().clone()); + let e = w.err("fill_sign", args.clone()); + coded(&e, "invalid"); + assert!(e.contains("is not one of them"), "{args}: {e}"); + } + assert_eq!(w.ok("comments", json!({"doc": doc}))["comments"], json!([]), "nothing reached the document"); + assert_eq!(w.ok("state", json!({"doc": doc}))["edited"], false); + // Without them, initials are typed, as before. + assert_eq!(w.ok("fill_sign", json!({"doc": doc, "page": 1, "kind": "initials", "at": [60, 300], "text": "SR"})), json!({"added": true})); + assert_eq!(w.ok("comments", json!({"doc": doc}))["comments"].as_array().map(Vec::len), Some(1)); +} + /// `pdf.fields` marks a read-only field, and `pdf.fill` refuses one as /// `invalid:` before the engine sees the call: nothing of it is filled. #[test] diff --git a/apps/pdf/host-service/src/review.rs b/apps/pdf/host-service/src/review.rs index e59cc2e0..c8decaf2 100644 --- a/apps/pdf/host-service/src/review.rs +++ b/apps/pdf/host-service/src/review.rs @@ -22,8 +22,16 @@ //! format and keystroke scripts, document script and open action would //! each leave a mark, and leave none through the service //! (`doc_tests::a_forms_own_scripts_never_run`). +//! - `fill_sign_add` is `file` since the engine's upstream sync: a +//! signature or initials may be an image, read from the file its `path` +//! names. The service builds its arguments itself, from `page`, `kind` +//! (one of [`MARKS`], so never `signature`), `at` and, for text and +//! initials, `text`; `pdf.fill_sign` refuses any other key (`path`, +//! `format`, `language`, `author`…) before any engine work. No parameter +//! that names a file ever reaches the command, so it runs as a plain +//! document edit (`doc_tests::fill_sign_never_reads_a_file`). //! - `comment_list`, `comment_edit`, `comment_delete`, `comment_reply`, -//! `comment_set_status`, `form_fields` and `fill_sign_add` are `safe`. +//! `comment_set_status` and `form_fields` are `safe`. //! //! **Dates.** `comment_list` lists a comment's `/M` through pdfcraft-render's //! `pretty_date`, which keeps the file's wall clock and drops the zone and diff --git a/apps/pdf/host-service/tests/skill.rs b/apps/pdf/host-service/tests/skill.rs index e023cb3c..22f19265 100644 --- a/apps/pdf/host-service/tests/skill.rs +++ b/apps/pdf/host-service/tests/skill.rs @@ -2,10 +2,12 @@ //! `skill/safety.json` are what pdfcraft's live tool table, at the pinned //! revision, and `skill/safety-rules.json` generate. Regenerate with //! `OCTOSENSE_SKILL_REGEN=1 cargo test --locked -p octosense-pdf-service --test skill`. +//! The engine's hardened PDF stack, patched in at the same revision, must be +//! the one built. use std::path::Path; -use octosense_skill_gen::{check_commands, params_from_schema, Catalog, Entry}; +use octosense_skill_gen::{check_commands, locked_revision, params_from_schema, workspace_lock, Catalog, Entry}; /// pdfcraft's automation tools: the table `pdf.*` drives the engine /// through (`Automation::call`), each with its JSON Schema. @@ -21,3 +23,29 @@ fn catalog() -> Catalog { fn the_skill_references_match_the_pinned_engine() { check_commands(&catalog(), &Path::new(env!("CARGO_MANIFEST_DIR")).join("skill")); } + +/// The root Cargo.toml patches pdfcraft's bootstrap PDF stack (hayro, +/// hayro-interpret, hayro-jbig2, hayro-syntax, lopdf) with the engine's +/// hardened vendor/ copies, at the engine's own revision. A patch whose +/// version loses to a newer crates.io release goes unused, and the +/// crates.io crate is built instead, without the engine's limits: until the +/// upstream sync, hayro-jbig2's patch (0.3.0) lost to crates.io's 0.3.1, so +/// JBIG2 images were decoded without its bounds on bitmap and dictionary +/// pixels and on decoding steps that never advance. Pin such a release down +/// to the patch's version, e.g. `cargo update -p hayro-jbig2@0.3.1 --precise 0.3.0`. +#[test] +fn the_engines_hardened_pdf_stack_is_the_one_built() { + let lock = std::fs::read_to_string(workspace_lock()).expect("Cargo.lock"); + let mut sections = lock.split("[[patch.unused]]"); + let packages = sections.next().unwrap_or_default(); + let unused: Vec<&str> = + sections.filter_map(|entry| entry.lines().find_map(|l| l.strip_prefix("name = \"")?.strip_suffix('"'))).collect(); + let revision = locked_revision(&lock, "pdfcraft-automation").expect("the engine's revision"); + let source = format!("source = \"git+https://github.com/ymote/pdfcraft.git?rev={revision}#{revision}\""); + for name in ["hayro", "hayro-interpret", "hayro-jbig2", "hayro-syntax", "lopdf"] { + assert!(!unused.contains(&name), "Cargo.lock lists {name}'s [patch] as unused, so the crates.io crate is built instead"); + let name_line = format!("\nname = \"{name}\"\n"); + let built = packages.split("[[package]]").any(|block| block.contains(&name_line) && block.contains(&source)); + assert!(built, "{name} is not built from the engine's revision {revision}"); + } +} diff --git a/apps/vector/host-service/skill/commands.md b/apps/vector/host-service/skill/commands.md index 27f31df0..014fb529 100644 --- a/apps/vector/host-service/skill/commands.md +++ b/apps/vector/host-service/skill/commands.md @@ -1,6 +1,6 @@ # vector engine commands -The vectorcraft engine's command catalog at revision 85dcb28e411d: 679 commands, one per line as `id` label: params. +The vectorcraft engine's command catalog at revision 2cd3a1ca8fbc: 679 commands, one per line as `id` label: params. Generated from the engine by `OCTOSENSE_SKILL_REGEN=1 cargo test --locked -p octosense-vector-service --test skill`; do not edit. A tag after the id marks a command that reaches past the open document (safety.json has every id's class): [file] reads or writes a path, [code] installs or runs code or other commands, [network] uses the network, [device] uses hardware, [host] changes windows, views, preferences, the clipboard or other app state. An untagged command works on the open document only. diff --git a/apps/vector/host-service/skill/safety.json b/apps/vector/host-service/skill/safety.json index 9e077f05..0869b118 100644 --- a/apps/vector/host-service/skill/safety.json +++ b/apps/vector/host-service/skill/safety.json @@ -1,6 +1,6 @@ { "engine": "vectorcraft", - "revision": "85dcb28e411db4271f3b726d6502b78e1375cbba", + "revision": "2cd3a1ca8fbcc5b2e40de47588194798a9f8e497", "regenerate": "OCTOSENSE_SKILL_REGEN=1 cargo test --locked -p octosense-vector-service --test skill", "rules": "safety-rules.json", "classes": { diff --git a/docs/adr/0013-craft-engines-as-pinned-services.md b/docs/adr/0013-craft-engines-as-pinned-services.md index e415c316..ba77d279 100644 --- a/docs/adr/0013-craft-engines-as-pinned-services.md +++ b/docs/adr/0013-craft-engines-as-pinned-services.md @@ -159,7 +159,7 @@ reach: photocraft alone has 817 commands. Beside it are references generated from the engine at its pin: the command catalog `commands.md`, one line per id, for the ten engines that have a catalog (photo, word, deck, cad, light, design, film, effect, - vector, pdf; 4,557 ids in all), light's `controls.md` and sheet's + vector, pdf; 4,569 ids in all), light's `controls.md` and sheet's `functions.md`. Each service embeds its skill (`src/skill.rs`), so a build ships the skill that matches its engine. The shell registers the linked engines' skills with the services' own gates diff --git a/docs/adr/0013-craft-engines-as-pinned-services.zh-CN.md b/docs/adr/0013-craft-engines-as-pinned-services.zh-CN.md index c40d2607..76485a1d 100644 --- a/docs/adr/0013-craft-engines-as-pinned-services.zh-CN.md +++ b/docs/adr/0013-craft-engines-as-pinned-services.zh-CN.md @@ -132,7 +132,7 @@ vector、pdf)现已有代理工具,系统代理可以调用: `SKILL.md`:frontmatter 为 `name: -engine` 和一行不超过 200 字节的 `description`,正文写引擎能做什么、系统代理可用的工具、文件规则和示例。旁边是 按引擎固定版本生成的参考文件:有命令目录的十个引擎(photo、word、deck、cad、 - light、design、film、effect、vector、pdf,共 4,557 个 id)各有一份 + light、design、film、effect、vector、pdf,共 4,569 个 id)各有一份 `commands.md`,每个 id 一行;light 有 `controls.md`,sheet 有 `functions.md`。 每个服务都嵌入自己的技能(`src/skill.rs`),因此构建发布的技能总与其引擎一致。 Shell 用与服务相同的条件注册已链接引擎的技能