mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-26 19:19:10 -06:00
M11: PDF Optimizer ▸ Clean Up stores LZW streams with Flate (optimize.flate-unencoded)
This commit is contained in:
+33
-33
@@ -7,7 +7,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Greek UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "1902f1c31698e6adfdaf65b35b1fe1628f5cd2bd0182dd3b99bcf5e21720f394"
|
||||
sha256 = "f6f451f25716c7b542d61f772fc52b0ac742e528e1725f88e6438f50de83685b"
|
||||
|
||||
# ATTRIBUTION.toml: one entry per asset (AGENTS.md §1). The source of truth for provenance and licences.
|
||||
# ATTRIBUTION.md is generated from this file: `cargo xtask assets --write`. Checked by `cargo xtask assets` (part of `xtask ci`).
|
||||
@@ -30,7 +30,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Simplified Chinese UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "2e23c41fcd5b9118cfde03b3a45aa2f988f7eeba650677b6f1f6664e543c7a59"
|
||||
sha256 = "5f85e70f547ad70e9c3a53c3eddc6a56125868bd40271da8a93dbb3da58d8898"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/zh-hant.tsv"
|
||||
@@ -41,7 +41,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Traditional Chinese UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "105703f2fbc07c0663308ebd6852199de12a5851a37507d7e51e477eb5e57218"
|
||||
sha256 = "a764edaaf6fa2123c348c7e07b7fcaa00c0c80fa0a4196e74962db3c43d27668"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/cs.tsv"
|
||||
@@ -52,7 +52,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Czech UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "b08f8282923d2faaf1f658d2aff8f71fe5f2d3a33e792eeb50fb0e2b396ef24f"
|
||||
sha256 = "5ac28a6ebde1d64a0c66dac327b86556c2e52a4b02fa3402fa743c6c534611d7"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/pt-br.tsv"
|
||||
@@ -63,7 +63,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Brazilian Portuguese UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "df17dada3df736e75bd9b03e5ecceec164cce63767e33a1efdb98c56a121c890"
|
||||
sha256 = "1e516d10b2d8f4a13b9f22a5be892aa40280d29757823472e5c2d1e0f3d90833"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/es.tsv"
|
||||
@@ -74,7 +74,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Spanish UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "8e8c0cfda0886c584e5286370ec00814206c3af3699c76665e636fa6fdb6310b"
|
||||
sha256 = "36460fa583d1bbe469bcb1d332d6e9dd4cce5803db4497c1e8b185e9ccdc1a98"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/fr.tsv"
|
||||
@@ -85,7 +85,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "French UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "3f31e5c9abdb0208d48d8c68aa81437d9d1e10aeb615dedf811fefa539d93279"
|
||||
sha256 = "2ed7411670f084424427306ef1a9d1a498f0c38a3a6f2917058a6394108f3c95"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/de.tsv"
|
||||
@@ -96,7 +96,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "German UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "815a3a672421cc3590fb1f267755316659cdb2046e30bbb3ad8b31e96f08b20a"
|
||||
sha256 = "5a668835a7d4852bf4e81290290777b9ddc440ff6f73d7898d2eda2057a75a42"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/lo.tsv"
|
||||
@@ -107,7 +107,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Lao UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "6bd67286795ed487eedf2ce549249c8a83286cb4273490c1349996050b540fed"
|
||||
sha256 = "08d1862a7185a27d180c3af52c02e05a052ab6b5a9fa91d8904ed92fbe6e0c99"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/te.tsv"
|
||||
@@ -118,7 +118,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Telugu UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "9cd63425705fb9a3aa029ff3a5110d0877a34f9038f1d64a2102ccd6c3cb35a6"
|
||||
sha256 = "995221cf9dbfd85ce565205d500f3f6160cafa0e4dd9562ad8ba04994252d1fe"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/it.tsv"
|
||||
@@ -129,7 +129,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Italian UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "76f138293fc12ea3584568a81f6f90a1cedfff6256e78b4f60cb21b1b66bbba4"
|
||||
sha256 = "df08daffe1d39ab7f957e6f42e3ff5cea6fb463c27bf697495a934071d6c07a6"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/pl.tsv"
|
||||
@@ -140,7 +140,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Polish UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "0bd75a93f8d18deb832223bd0642cc4f4abde04a322b684a143a9d4b4687a344"
|
||||
sha256 = "704de81fc1892b3b7d3ec1f67869939e8034d7ace700b98aaf550dd228157898"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/ru.tsv"
|
||||
@@ -151,7 +151,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Russian UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "3d539a28afcf8c2e36b7087ac23334b50bac7971e03da20a62b6e7e400763a87"
|
||||
sha256 = "a63f2fa2f18790a4d1feb5a79be00cff342b7d93d4df176990cce73b34a576fa"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/hu.tsv"
|
||||
@@ -162,7 +162,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Hungarian UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "71b7e8180fc759aebad4660953ab8b55383def0572c1ace2288cd8bfeb1b82dc"
|
||||
sha256 = "44601c04ba0029f3af1767b1f045467823fca9aa09e81826f2bc551d240237b2"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/bg.tsv"
|
||||
@@ -173,7 +173,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Bulgarian UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "e689fb24a8acfa7e85f0f60644f7e2da1225595e6cd6a94109fbe86082926244"
|
||||
sha256 = "2525dfa1bc461cfe7531a1e5dd5bbef41d89690e33457dbd4cde64ae1ae34954"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/kk.tsv"
|
||||
@@ -184,7 +184,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Kazakh UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "c601ea117ec2a3cdba606fdc9a62220fcffbd0c20c1133bacb653a979cf046a5"
|
||||
sha256 = "d694ce0975ce09537f565abe94f204b618cc79c4e000af96693115c8bbb508d3"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/ja.tsv"
|
||||
@@ -195,7 +195,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Japanese UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "006867b891ae2f995c7a711e55b6401db29d20ab493889c6284dc3671f9faa2d"
|
||||
sha256 = "83f80bb06b9dd4b6c77e8f4f62aea71c3fcc67d1df2fdef95a2c43077d2771ad"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/uk.tsv"
|
||||
@@ -206,7 +206,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Ukrainian UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "9e5b18a09c209525cf08ef53a242ed457cfa346295eef7facde482a75cf72f52"
|
||||
sha256 = "384b20f09727e50e191e270171c29a08e8cc4eadc77417234d75407ed4d17d04"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/ar.tsv"
|
||||
@@ -217,7 +217,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Arabic UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "2306dbbe6a73671fd20df116adb159a7f66c85fbbd1bae5ecef5f6e882d6b7f1"
|
||||
sha256 = "dea69e04b75e61c33ee30cb13739a1279cb44ded4163524ba2124dce5095987f"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/ckb.tsv"
|
||||
@@ -228,7 +228,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Central Kurdish UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "864db8aa0a5e443c92e7e388f89349eb0f0e7beda35a0b81695d864dcec51b72"
|
||||
sha256 = "5710c3739425f7228274e9c30cf9f675b664db154971def67850e60d0468f461"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/tr.tsv"
|
||||
@@ -239,7 +239,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Turkish UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "463a2d28bd5c5ac8df31a5b5c68400d7a61ce51237a58d9337078c973e6f9e3d"
|
||||
sha256 = "12cf178181235a54ded7cad2e0699a20ff4db5c589d239fae73ae3ec7d65d793"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/lt.tsv"
|
||||
@@ -250,7 +250,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Lithuanian UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "bbef7f4e16f83e9e0116278e7a0a175bad79757f96a18dc639516572533f6d5b"
|
||||
sha256 = "81dc2d3c750ac9df73f4a8a5ab7e3723905479ec51d92d63994cf14ff9890a0d"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/hi.tsv"
|
||||
@@ -261,7 +261,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Hindi UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "09d2840c02b40a6049b86c2cb68151d863913e8c91c22226f25d07496c6366aa"
|
||||
sha256 = "f3ad03739d6bc50263eb819159c7ac6823c5bf3326f4677f081742ffb8fe32ab"
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/id.tsv"
|
||||
title = "Indonesian interface translation catalog"
|
||||
@@ -271,7 +271,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Indonesian UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "0dc1abdb262e4568f19ffb2233bfeb3686ae42df8a8d8bec2951351a50e6a886"
|
||||
sha256 = "a6357827206369cd49b1f9b374bef43e5dc10afa3329015509033101f1d812bf"
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/ko.tsv"
|
||||
title = "Korean interface translation catalog"
|
||||
@@ -281,7 +281,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Korean UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "a3e67cee4f3fff776e816523f810b2bb6353235dbdf85181c3f12896604a3339"
|
||||
sha256 = "595a8c32d74c169ac22c0e4f73bd8e00551708a7501647e37b78de0ebc2e5103"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/ro.tsv"
|
||||
@@ -292,7 +292,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Romanian UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "00a2153c20a191fbe04f1ce61f140fd9628cfe2f8f75dad5bce8a7406e1bd091"
|
||||
sha256 = "4b34cd917fa4863e46ca18ed296cec715a1a40c56fca0b26ad29a638c835f84f"
|
||||
|
||||
|
||||
[[asset]]
|
||||
@@ -304,7 +304,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Finnish UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "609efa92054aa76de4bb6062c0fcc0a8b450519f4b253b4c0f395b3cbb34d2ba"
|
||||
sha256 = "7447236441d1768a10a0dd8c9e156222a5c4e2435e62e8dcc7b1918d6bba9973"
|
||||
|
||||
[[asset]]
|
||||
path = "assets/icons/accessibility.svg"
|
||||
@@ -3426,7 +3426,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Vietnamese UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "57f0dd007ad16518fbc87d8aeac840ef6d86412bb460aff9fb17b176408fb7a8"
|
||||
sha256 = "0464b388d829e23d644d10bd54427c96189c2ffb3c9cc39a0299a6d85c267e35"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/sl.tsv"
|
||||
@@ -3437,7 +3437,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Slovene UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "a3b8351f0da80c2d394221dbe5d62e794bb019c74c2adfc5d933d999f7d3236f"
|
||||
sha256 = "dff23a9b66b5e6f49ff7387bc04a26769548c798427f4cf99046cadbc00bc063"
|
||||
|
||||
[[asset]]
|
||||
|
||||
@@ -3460,7 +3460,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Norwegian Bokmål UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "03477c4ab2d0d52543cb91b3aec6f27b2689e6b9bb7c95bbb8463664c75a9dc0"
|
||||
sha256 = "d69fec16fe7d422b1e1f6f1d7a3481ca49d8d0ab68098989b2f6b54adecdf2ef"
|
||||
|
||||
[[asset]]
|
||||
path = "assets/icons/superscript.svg"
|
||||
@@ -3515,7 +3515,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Albanian UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "ebd177262d8fe20f960f06df084f8a03c9504bcd9990dded3a006e35bd8ef7c9"
|
||||
sha256 = "6a190b4b04393d926adb3b259ba74f05ed4f87ce8f839b98624e8bad58307658"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/th.tsv"
|
||||
@@ -3526,7 +3526,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Thai UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "1dbb66c05af0aec06d5a6fa154e89f1684203dde32c8e8aedd9aeec6c2d5ae02"
|
||||
sha256 = "7de17b6bddd33ea916f32e035bdd96d2531d208737ddd04f2dcfe56980d78aec"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/ca.tsv"
|
||||
@@ -3537,4 +3537,4 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Catalan UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "5879ee3540ffb630fc83d0f2cb293cc6cbf2e081ea4577161a770794e8cbd22c"
|
||||
sha256 = "9afc2be3720e39bbcdc8ebf5ea475d21e2580b844d97621d5b30d025ac3756f8"
|
||||
|
||||
Generated
+1
@@ -4235,6 +4235,7 @@ dependencies = [
|
||||
"pdfcraft-annot",
|
||||
"pdfcraft-content",
|
||||
"pdfcraft-cos",
|
||||
"pdfcraft-filters",
|
||||
"pdfcraft-model",
|
||||
"pdfcraft-render",
|
||||
"thiserror 2.0.21",
|
||||
|
||||
@@ -1226,6 +1226,7 @@ impl Automation {
|
||||
("discard_tags", &mut settings.discard_tags),
|
||||
("discard_print_settings", &mut settings.discard_print_settings),
|
||||
("flate_unencoded", &mut settings.flate_unencoded),
|
||||
("lzw_to_flate", &mut settings.lzw_to_flate),
|
||||
("remove_invalid_links", &mut settings.remove_invalid_links),
|
||||
("remove_unreferenced_dests", &mut settings.remove_unreferenced_dests),
|
||||
] {
|
||||
@@ -1264,6 +1265,7 @@ impl Automation {
|
||||
"alternate_images": o.alternate_images,
|
||||
"tags_removed": o.tags_removed,
|
||||
"streams_compressed": o.streams_compressed,
|
||||
"lzw_streams": o.lzw_streams,
|
||||
"invalid_links": o.invalid_links,
|
||||
"invalid_bookmarks": o.invalid_bookmarks,
|
||||
"unreferenced_dests": o.unreferenced_dests,
|
||||
|
||||
@@ -1359,7 +1359,7 @@ pub fn tools() -> Vec<ToolDef> {
|
||||
.with(schema(json!({ "doc": doc() }), &["doc"])),
|
||||
t("doc_open_revision", "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.")
|
||||
.with(schema(json!({ "doc": doc(), "revision": { "type": "integer", "minimum": 1 } }), &["doc", "revision"])),
|
||||
t("doc_optimize", "Optimize PDF", "Write an optimized copy to `path` (Acrobat's PDF Optimizer). preset: standard (default; Reduce File Size's choices) or mobile (images to 96 ppi above 144, JPEG high quality); the options below change it. merge_fragments (default true) first puts a page picture drawn as many stacked image strips (as some PDF printers write it) back together into one image, so it can be resampled (merged_images, fragments_merged). 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.")
|
||||
t("doc_optimize", "Optimize PDF", "Write an optimized copy to `path` (Acrobat's PDF Optimizer). preset: standard (default; Reduce File Size's choices) or mobile (images to 96 ppi above 144, JPEG high quality); the options below change it. merge_fragments (default true) first puts a page picture drawn as many stacked image strips (as some PDF printers write it) back together into one image, so it can be resampled (merged_images, fragments_merged). 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); lzw_to_flate (true) decodes LZW streams and stores them with Flate instead, leaving any it cannot decode (lzw_streams); 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.")
|
||||
.cmd("optimize.advanced")
|
||||
.with(schema(
|
||||
json!({
|
||||
@@ -1374,6 +1374,7 @@ pub fn tools() -> Vec<ToolDef> {
|
||||
"discard_tags": { "type": "boolean" },
|
||||
"discard_print_settings": { "type": "boolean" },
|
||||
"flate_unencoded": { "type": "boolean" },
|
||||
"lzw_to_flate": { "type": "boolean" },
|
||||
"remove_invalid_links": { "type": "boolean" },
|
||||
"remove_unreferenced_dests": { "type": "boolean" },
|
||||
"discard": { "type": "array", "items": { "type": "string" } },
|
||||
|
||||
@@ -5580,6 +5580,56 @@ fn optimizing_through_tools() {
|
||||
assert!(matches!(a.call("doc_optimize", &json!({ "doc": doc, "path": "x.pdf", "discard": ["everything"] })), Err(ToolError::InvalidArgs(_))));
|
||||
}
|
||||
|
||||
/// `data` as LZW with one 9-bit literal code per byte (clear-table first, EOD last): valid for
|
||||
/// fewer than 250 bytes, before the code width grows.
|
||||
fn lzw_literals(data: &[u8]) -> Vec<u8> {
|
||||
let codes: Vec<u16> = std::iter::once(256).chain(data.iter().map(|&b| u16::from(b))).chain([257]).collect();
|
||||
let bits: Vec<bool> = codes.iter().flat_map(|c| (0..9).rev().map(move |i| c >> i & 1 == 1)).collect();
|
||||
bits.chunks(8).map(|byte| byte.iter().enumerate().fold(0u8, |acc, (i, &b)| acc | (u8::from(b) << (7 - i)))).collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lzw_streams_are_stored_with_flate_through_tools() {
|
||||
use pdfcraft_cos::{Dict, Document, Object, SaveOptions, Stream, write_full};
|
||||
let dir = workdir("optimize-lzw");
|
||||
let ops = b"0.2 0.4 0.8 rg 72 72 200 100 re f 0 0 1 RG 72 300 m 400 500 l S".to_vec();
|
||||
let mut doc = Document::new_empty();
|
||||
let pages = doc.get(doc.root().unwrap()).as_dict().unwrap().reference(b"Pages").unwrap();
|
||||
let lzw = Stream::from_raw(Dict::from_iter([(b"Filter".to_vec(), Object::name("LZWDecode"))]), lzw_literals(&ops));
|
||||
assert_eq!(lzw.decoded_strict().unwrap(), ops);
|
||||
let content = doc.add(Object::Stream(lzw));
|
||||
let page = doc.add(Object::Dict(Dict::from_iter([
|
||||
(b"Type".to_vec(), Object::name("Page")),
|
||||
(b"Parent".to_vec(), Object::Ref(pages)),
|
||||
(b"MediaBox".to_vec(), Object::Array(vec![0.into(), 0.into(), 612.into(), 792.into()])),
|
||||
(b"Contents".to_vec(), Object::Ref(content)),
|
||||
])));
|
||||
doc.update_dict(pages, |d| {
|
||||
d.set(b"Kids".to_vec(), Object::Array(vec![Object::Ref(page)]));
|
||||
d.set(b"Count".to_vec(), Object::Int(1));
|
||||
})
|
||||
.unwrap();
|
||||
std::fs::write(dir.join("lzw.pdf"), write_full(&doc, &SaveOptions::default()).unwrap()).unwrap();
|
||||
let mut a = auto(&dir);
|
||||
let doc = ok(&mut a, "doc_open", json!({ "path": "lzw.pdf" }))["doc"].as_u64().unwrap();
|
||||
let filters = |name: &str| {
|
||||
let saved = pdfcraft_cos::Document::open(std::sync::Arc::new(std::fs::read(dir.join(name)).unwrap())).unwrap();
|
||||
let catalog = saved.get(saved.root().unwrap()).as_dict().cloned().unwrap();
|
||||
let pages = saved.get(catalog.reference(b"Pages").unwrap()).as_dict().cloned().unwrap();
|
||||
let page = saved.get(pages.get(b"Kids").and_then(|k| k.as_array()?.first()?.as_ref()).unwrap()).as_dict().cloned().unwrap();
|
||||
match &*saved.get(page.reference(b"Contents").unwrap()) {
|
||||
Object::Stream(s) => (s.dict.get(b"Filter").cloned(), s.decoded_strict().unwrap()),
|
||||
_ => panic!("the page content is not a stream"),
|
||||
}
|
||||
};
|
||||
let r = ok(&mut a, "doc_optimize", json!({ "doc": doc, "path": "flate.pdf" }));
|
||||
assert_eq!(r["lzw_streams"].as_u64(), Some(1), "{r}");
|
||||
assert_eq!(filters("flate.pdf"), (Some(Object::name("FlateDecode")), ops.clone()));
|
||||
let off = ok(&mut a, "doc_optimize", json!({ "doc": doc, "path": "kept.pdf", "lzw_to_flate": false }));
|
||||
assert_eq!(off["lzw_streams"].as_u64(), Some(0), "{off}");
|
||||
assert_eq!(filters("kept.pdf"), (Some(Object::name("LZWDecode")), ops));
|
||||
}
|
||||
|
||||
/// A one-page PDF whose page picture is drawn as `rows` grey images one pixel row high, 600 px
|
||||
/// wide on a 72 pt square (600 ppi), as some PDF printers write it.
|
||||
fn printed_in_strips(rows: usize) -> Vec<u8> {
|
||||
|
||||
@@ -12,6 +12,7 @@ workspace = true
|
||||
|
||||
[dependencies]
|
||||
pdfcraft-cos = { workspace = true }
|
||||
pdfcraft-filters = { workspace = true }
|
||||
pdfcraft-content = { workspace = true }
|
||||
pdfcraft-model = { workspace = true }
|
||||
pdfcraft-annot = { workspace = true }
|
||||
|
||||
@@ -30,7 +30,11 @@ let report = optimize_with_progress(&mut doc, &settings, &mut |stage| keep_going
|
||||
- **Discard objects:** thumbnails, alternate images, document tags, print settings.
|
||||
- **Clean up:** Flate for streams with no filter; for streams with an outer ASCII85 or ASCIIHex
|
||||
layer, that layer is removed when the result is smaller (the inner filters keep their encoded
|
||||
bytes). Streams with `/DecodeParms`, images and object streams are left alone.
|
||||
bytes). Streams with `/DecodeParms`, images and object streams are left alone. LZW-compressed
|
||||
streams, content and images alike, are decoded and stored with Flate instead (Acrobat's "In
|
||||
streams that use LZW encoding, use Flate instead"): a predictor carries over to Flate without
|
||||
`EarlyChange`, filters applied before the LZW one go with it and those after it keep their
|
||||
bytes. A stream whose LZW data doesn't decode cleanly is left as it is. Not yet: inline images.
|
||||
|
||||
The engine runs Remove Hidden Information (`pdfcraft-redact`) for the user-data categories,
|
||||
then this, then merges identical objects and writes a full, compressed save
|
||||
|
||||
+126
-2
@@ -14,7 +14,7 @@
|
||||
//! print settings.
|
||||
//! - **Clean up:** drop images and forms that pages list but never draw (such as a deleted
|
||||
//! image); Flate-compress streams that have no filter; remove a redundant outer ASCII85 or
|
||||
//! ASCIIHex layer from streams that have one.
|
||||
//! ASCIIHex layer from streams that have one; store LZW-compressed streams with Flate instead.
|
||||
//!
|
||||
//! Metadata, attachments, comments, scripts, private data, hidden layers, bookmarks and form
|
||||
//! fields are discarded by `pdfcraft-redact`'s Remove Hidden Information, which the engine
|
||||
@@ -30,7 +30,8 @@ mod unused;
|
||||
|
||||
pub use audit::{SpaceCategory, SpaceUse, audit_space};
|
||||
|
||||
use pdfcraft_cos::{Document, ObjRef, Object, Stream, serialize};
|
||||
use pdfcraft_cos::{Dict, Document, MAX_PAGE_DECODED, ObjRef, Object, Stream, serialize};
|
||||
use pdfcraft_filters::{Filter, Params};
|
||||
|
||||
pub use images::effective_resolutions;
|
||||
|
||||
@@ -100,6 +101,8 @@ pub struct Settings {
|
||||
/// Flate-compress streams stored without a filter, and strip a redundant outer ASCII85 or
|
||||
/// ASCIIHex layer from streams that have one.
|
||||
pub flate_unencoded: bool,
|
||||
/// Clean Up: decode LZW-compressed streams and store them with Flate instead.
|
||||
pub lzw_to_flate: bool,
|
||||
/// Clean Up: remove links (and bookmark destinations) that point nowhere.
|
||||
pub remove_invalid_links: bool,
|
||||
/// Clean Up: remove named destinations nothing refers to.
|
||||
@@ -118,6 +121,7 @@ impl Default for Settings {
|
||||
discard_tags: false,
|
||||
discard_print_settings: false,
|
||||
flate_unencoded: true,
|
||||
lzw_to_flate: true,
|
||||
remove_invalid_links: true,
|
||||
remove_unreferenced_dests: true,
|
||||
}
|
||||
@@ -178,6 +182,8 @@ pub struct Report {
|
||||
pub tags_removed: bool,
|
||||
pub print_settings: usize,
|
||||
pub streams_compressed: usize,
|
||||
/// Streams whose LZW compression was replaced with Flate (Clean Up).
|
||||
pub lzw_streams: usize,
|
||||
/// Links removed and bookmarks whose destination was cleared (Clean Up).
|
||||
pub invalid_links: usize,
|
||||
pub invalid_bookmarks: usize,
|
||||
@@ -279,6 +285,19 @@ pub fn optimize_with_progress(doc: &mut Document, settings: &Settings, progress:
|
||||
if settings.remove_unreferenced_dests {
|
||||
report.unreferenced_dests = links::remove_unreferenced_dests(doc)?;
|
||||
}
|
||||
// Before the unencoded-stream pass, which would otherwise take the ASCII layer off an
|
||||
// `[/ASCII85Decode /LZWDecode]` stream first and count it twice.
|
||||
if settings.lzw_to_flate {
|
||||
for num in doc.object_numbers() {
|
||||
let r = ObjRef { num, generation: doc.generation(num) };
|
||||
let obj = doc.get(r);
|
||||
let Object::Stream(s) = &*obj else { continue };
|
||||
if let Some(replacement) = lzw_to_flate(s, MAX_LZW_DECODED) {
|
||||
doc.set(r, Object::Stream(replacement));
|
||||
report.lzw_streams += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if settings.flate_unencoded {
|
||||
for num in doc.object_numbers() {
|
||||
let r = ObjRef { num, generation: doc.generation(num) };
|
||||
@@ -305,6 +324,94 @@ pub fn optimize_with_progress(doc: &mut Document, settings: &Settings, progress:
|
||||
Ok(report)
|
||||
}
|
||||
|
||||
/// Largest decoded LZW stream converted, as for a page's content: the data and its check are
|
||||
/// held together, so a few hundred kilobytes of hostile LZW can't ask for gigabytes.
|
||||
const MAX_LZW_DECODED: usize = MAX_PAGE_DECODED;
|
||||
|
||||
/// `s` with its LZW compression replaced by Flate (Acrobat's "In streams that use LZW encoding,
|
||||
/// use Flate instead"). The filters up to the last LZW one are decoded, strictly and with its
|
||||
/// predictor; the result is Flate-encoded under the same predictor, without `EarlyChange`, and
|
||||
/// the filters after it keep their encoded bytes. A stream that doesn't decode cleanly, or
|
||||
/// whose filters or parameters can't be read for certain, or that decodes past `max` bytes, is
|
||||
/// left as it is.
|
||||
fn lzw_to_flate(s: &Stream, max: usize) -> Option<Stream> {
|
||||
// An external file's data isn't in `raw`; cross-reference and object streams are rewritten
|
||||
// on save, so converting them would change nothing in the file.
|
||||
if s.dict.contains(b"F") || matches!(s.dict.name(b"Type"), Some(b"XRef" | b"ObjStm")) {
|
||||
return None;
|
||||
}
|
||||
let names: Vec<&[u8]> = match s.dict.get(b"Filter")? {
|
||||
Object::Name(n) => vec![n.as_slice()],
|
||||
Object::Array(a) => a.iter().map(Object::as_name).collect::<Option<_>>()?,
|
||||
_ => return None,
|
||||
};
|
||||
// `/Crypt` was applied when the object was loaded, and dropping it would change how the
|
||||
// stream is encrypted.
|
||||
if names.contains(&&b"Crypt"[..]) {
|
||||
return None;
|
||||
}
|
||||
let lzw = names.iter().rposition(|n| matches!(*n, b"LZWDecode" | b"LZW"))?;
|
||||
// One entry per filter, each a dictionary or null; anything else (an indirect reference, an
|
||||
// array of the wrong length) would be read as defaults, so the data might not decode as
|
||||
// written.
|
||||
let parms: Vec<Option<&Dict>> = match s.dict.get(b"DecodeParms") {
|
||||
None | Some(Object::Null) => vec![None; names.len()],
|
||||
Some(Object::Dict(d)) if names.len() == 1 => vec![Some(d)],
|
||||
Some(Object::Array(a)) if a.len() == names.len() => a
|
||||
.iter()
|
||||
.map(|p| match p {
|
||||
Object::Dict(d) => Some(Some(d)),
|
||||
Object::Null => Some(None),
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Option<_>>()?,
|
||||
_ => return None,
|
||||
};
|
||||
let chain = s.filters();
|
||||
let stages = chain.get(..=lzw)?;
|
||||
let (_, params) = stages.last()?;
|
||||
let data = pdfcraft_filters::decode(stages, &s.raw, max).ok()?;
|
||||
let flate = Params { early_change: 1, ..params.clone() };
|
||||
let raw = pdfcraft_filters::encode(&Filter::Flate, &flate, &data).ok()?;
|
||||
// The predictor's encoder must give back exactly what its decoder read (a short last row,
|
||||
// unusual parameters), or the stream would change. The limit counts a PNG predictor's tag
|
||||
// bytes too, at most one per row of at least one byte.
|
||||
if pdfcraft_filters::decode(&[(Filter::Flate, flate)], &raw, data.len().saturating_mul(2).saturating_add(1)).ok()? != data {
|
||||
return None;
|
||||
}
|
||||
// Without a predictor the other parameters say nothing to Flate.
|
||||
let first = parms.get(lzw).copied().flatten().filter(|_| params.predictor > 1).map(|d| {
|
||||
let mut d = d.clone();
|
||||
d.remove(b"EarlyChange");
|
||||
Object::Dict(d)
|
||||
});
|
||||
let rest_names = names.get(lzw + 1..)?;
|
||||
let rest_parms = parms.get(lzw + 1..)?;
|
||||
let mut dict = s.dict.clone();
|
||||
dict.remove(b"Length");
|
||||
if rest_names.is_empty() {
|
||||
dict.set(b"Filter".to_vec(), Object::name("FlateDecode"));
|
||||
match first {
|
||||
Some(p) => dict.set(b"DecodeParms".to_vec(), p),
|
||||
None => {
|
||||
dict.remove(b"DecodeParms");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let filters = std::iter::once(Object::name("FlateDecode")).chain(rest_names.iter().map(|n| Object::Name(n.to_vec())));
|
||||
dict.set(b"Filter".to_vec(), Object::Array(filters.collect()));
|
||||
let parms: Vec<Object> = std::iter::once(first.unwrap_or(Object::Null))
|
||||
.chain(rest_parms.iter().map(|p| p.map_or(Object::Null, |d| Object::Dict(d.clone()))))
|
||||
.collect();
|
||||
if parms.iter().all(|p| matches!(p, Object::Null)) {
|
||||
dict.remove(b"DecodeParms");
|
||||
} else {
|
||||
dict.set(b"DecodeParms".to_vec(), Object::Array(parms));
|
||||
}
|
||||
}
|
||||
Some(Stream::from_raw(dict, raw))
|
||||
}
|
||||
|
||||
/// `s` without its outer ASCII85 or ASCIIHex layer (`[/ASCII85Decode /FlateDecode]` becomes
|
||||
/// `[/FlateDecode]`), when that is smaller. Only the outer layer is decoded: strictly, and within
|
||||
/// the stored size, which ASCII text never falls below, so the inner encoded bytes stay exact.
|
||||
@@ -335,3 +442,20 @@ fn stored_len(s: &Stream) -> Option<usize> {
|
||||
serialize(&Object::Dict(dict), &mut out);
|
||||
out.len().checked_add(s.raw.len())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn an_lzw_stream_that_decodes_past_the_limit_is_left_as_it_is() {
|
||||
let data = b"0 0 m 612 792 l S\n".repeat(64);
|
||||
let raw = pdfcraft_filters::encode(&Filter::Lzw, &Params::default(), &data).unwrap();
|
||||
let mut dict = Dict::new();
|
||||
dict.set(b"Filter".to_vec(), Object::name("LZWDecode"));
|
||||
let s = Stream::from_raw(dict, raw);
|
||||
assert!(lzw_to_flate(&s, data.len() - 1).is_none());
|
||||
let converted = lzw_to_flate(&s, data.len()).unwrap();
|
||||
assert_eq!(converted.decoded_strict().unwrap(), data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use pdfcraft_cos::{Dict, Document, ObjRef, Object, SaveOptions, Stream, write_full};
|
||||
use pdfcraft_filters::{Filter, Params};
|
||||
use pdfcraft_optimize::{Compression, ImageSettings, Settings, SpaceCategory, audit_space, effective_resolutions, optimize};
|
||||
|
||||
fn image(doc: &mut Document, w: u32, h: u32, n: usize, jpeg: bool, smask: Option<ObjRef>) -> ObjRef {
|
||||
@@ -1097,3 +1098,200 @@ fn a_hostile_soft_mask_leaves_its_image_unchanged() {
|
||||
assert_eq!(stream(&doc, photo), before);
|
||||
assert_eq!(stream(&doc, mask), hostile);
|
||||
}
|
||||
|
||||
/// Page content long enough for LZW codes to grow past 9 bits, where `EarlyChange` matters.
|
||||
fn lzw_ops() -> Vec<u8> {
|
||||
(0..300).flat_map(|i| format!("{} {} m {} {} l S\n", i % 97, i * 7 % 389, i * 13 % 612, i * 31 % 792).into_bytes()).collect()
|
||||
}
|
||||
|
||||
fn lzw(data: &[u8], params: &Params) -> Vec<u8> {
|
||||
pdfcraft_filters::encode(&Filter::Lzw, params, data).unwrap()
|
||||
}
|
||||
|
||||
/// `params` as a `/DecodeParms` dictionary, only the entries that differ from the defaults.
|
||||
fn decode_parms(p: &Params) -> Dict {
|
||||
let mut d = Dict::new();
|
||||
for (k, v, default) in [
|
||||
(&b"Predictor"[..], p.predictor, 1),
|
||||
(b"Colors", p.colors, 1),
|
||||
(b"BitsPerComponent", p.bits_per_component, 8),
|
||||
(b"Columns", p.columns, 1),
|
||||
(b"EarlyChange", p.early_change, 1),
|
||||
] {
|
||||
if v != default {
|
||||
d.set(k.to_vec(), Object::Int(v));
|
||||
}
|
||||
}
|
||||
d
|
||||
}
|
||||
|
||||
/// A 64 × 48 RGB image, LZW-encoded with `params`.
|
||||
fn lzw_image(doc: &mut Document, params: &Params) -> (ObjRef, Vec<u8>) {
|
||||
let (w, h) = (64u32, 48u32);
|
||||
let px: Vec<u8> = (0..w * h).flat_map(|i| [(i % w * 4) as u8, (i / w * 5) as u8, ((i % w + i / w) * 2) as u8]).collect();
|
||||
let mut d = Dict::new();
|
||||
d.set(b"Type".to_vec(), Object::name("XObject"));
|
||||
d.set(b"Subtype".to_vec(), Object::name("Image"));
|
||||
d.set(b"Width".to_vec(), Object::Int(w.into()));
|
||||
d.set(b"Height".to_vec(), Object::Int(h.into()));
|
||||
d.set(b"BitsPerComponent".to_vec(), Object::Int(8));
|
||||
d.set(b"ColorSpace".to_vec(), Object::name("DeviceRGB"));
|
||||
d.set(b"Filter".to_vec(), Object::name("LZWDecode"));
|
||||
d.set(b"DecodeParms".to_vec(), Object::Dict(decode_parms(params)));
|
||||
(doc.add(Object::Stream(Stream::from_raw(d, lzw(&px, params)))), px)
|
||||
}
|
||||
|
||||
/// Settings that leave images to the clean-up: no resampling, no recompression.
|
||||
fn clean_up_only() -> Settings {
|
||||
let keep = ImageSettings { downsample: false, compression: Compression::Retain, ..ImageSettings::REDUCE };
|
||||
Settings { color: keep, gray: keep, ..Settings::default() }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lzw_streams_become_flate_and_decode_to_the_same_bytes() {
|
||||
let ops = lzw_ops();
|
||||
let early = Params::default();
|
||||
let late = Params { early_change: 0, ..Params::default() };
|
||||
let png = Params { predictor: 15, colors: 3, columns: 64, ..Params::default() };
|
||||
let png_late = Params { early_change: 0, ..png.clone() };
|
||||
let mut doc = Document::new_empty();
|
||||
let content = |doc: &mut Document, p: &Params| {
|
||||
let mut d = Dict::new();
|
||||
d.set(b"Filter".to_vec(), Object::name("LZWDecode"));
|
||||
if *p != Params::default() {
|
||||
d.set(b"DecodeParms".to_vec(), Object::Dict(decode_parms(p)));
|
||||
}
|
||||
doc.add(Object::Stream(Stream::from_raw(d, lzw(&ops, p))))
|
||||
};
|
||||
let c_early = content(&mut doc, &early);
|
||||
let c_late = content(&mut doc, &late);
|
||||
let (img, px) = lzw_image(&mut doc, &png);
|
||||
let (img_late, _) = lzw_image(&mut doc, &png_late);
|
||||
page(&mut doc, &[("Im", img), ("Im2", img_late)], "q 64 0 0 48 72 600 cm /Im Do Q q 64 0 0 48 172 600 cm /Im2 Do Q");
|
||||
let pg = pdfcraft_annot::page_refs(&doc).unwrap()[0];
|
||||
doc.update_dict(pg, |d| {
|
||||
d.set(b"Contents".to_vec(), Object::Array(vec![Object::Ref(c_early), Object::Ref(c_late), d.get(b"Contents").unwrap().clone()]))
|
||||
})
|
||||
.unwrap();
|
||||
// Each LZW stream reads as its own data before, so a decoder ignoring EarlyChange fails here.
|
||||
assert_eq!((stream(&doc, c_early).decoded_strict().unwrap(), stream(&doc, c_late).decoded_strict().unwrap()), (ops.clone(), ops.clone()));
|
||||
assert!(lzw(&ops, &early) != lzw(&ops, &late), "the two code widths differ on this data");
|
||||
|
||||
let r = optimize(&mut doc, &clean_up_only()).unwrap();
|
||||
assert_eq!(r.lzw_streams, 4, "{r:?}");
|
||||
for (label, at, data) in
|
||||
[("content", c_early, &ops), ("content, EarlyChange 0", c_late, &ops), ("image", img, &px), ("image, EarlyChange 0", img_late, &px)]
|
||||
{
|
||||
let after = stream(&doc, at);
|
||||
assert_eq!(after.dict.get(b"Filter"), Some(&Object::name("FlateDecode")), "{label}");
|
||||
assert_eq!(&after.decoded_strict().unwrap(), data, "{label}");
|
||||
let parms = after.dict.get(b"DecodeParms").and_then(Object::as_dict);
|
||||
assert!(parms.is_none_or(|p| !p.contains(b"EarlyChange")), "{label}: EarlyChange is LZW's alone");
|
||||
}
|
||||
// The image keeps its PNG predictor, now applied under Flate.
|
||||
let parms = stream(&doc, img).dict.get(b"DecodeParms").and_then(Object::as_dict).cloned().unwrap();
|
||||
assert_eq!((parms.int(b"Predictor"), parms.int(b"Colors"), parms.int(b"Columns")), (Some(15), Some(3), Some(64)));
|
||||
assert!(!stream(&doc, c_late).dict.contains(b"DecodeParms"), "nothing left to say for the content stream");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_page_drawn_from_lzw_streams_renders_the_same_after_conversion() {
|
||||
let png = Params { predictor: 12, colors: 3, columns: 64, early_change: 0, ..Params::default() };
|
||||
let mut doc = Document::new_empty();
|
||||
let (img, _) = lzw_image(&mut doc, &png);
|
||||
let pg = page(&mut doc, &[("Im", img)], "");
|
||||
let ops = b"0.2 0.5 0.8 rg 36 36 300 200 re f q 256 0 0 192 72 400 cm /Im Do Q\n".repeat(4);
|
||||
let mut d = Dict::new();
|
||||
d.set(b"Filter".to_vec(), Object::name("LZWDecode"));
|
||||
d.set(b"DecodeParms".to_vec(), Object::Dict(decode_parms(&Params { early_change: 0, ..Params::default() })));
|
||||
let c = doc.add(Object::Stream(Stream::from_raw(d, lzw(&ops, &Params { early_change: 0, ..Params::default() }))));
|
||||
doc.update_dict(pg, |p| p.set(b"Contents".to_vec(), Object::Ref(c))).unwrap();
|
||||
let render = |doc: &Document| {
|
||||
let bytes = write_full(doc, &SaveOptions::default()).unwrap();
|
||||
let mut r = pdfcraft_render::PageRenderer::new(Arc::new(bytes), pdfcraft_render::RenderConfig::default());
|
||||
let out = r.render(pdfcraft_render::RenderRequest { page: 0, scale: 1.0, ..Default::default() });
|
||||
assert!(out.error.is_none(), "{:?}", out.error);
|
||||
out.rgba
|
||||
};
|
||||
let before = render(&doc);
|
||||
// The picture and the rectangle are drawn: the page is not blank.
|
||||
assert!(before.chunks(4).any(|p| p[..3] != [255, 255, 255]));
|
||||
let r = optimize(&mut doc, &clean_up_only()).unwrap();
|
||||
assert_eq!(r.lzw_streams, 2, "{r:?}");
|
||||
assert_eq!(stream(&doc, c).dict.name(b"Filter"), Some(&b"FlateDecode"[..]));
|
||||
assert!(render(&doc) == before, "the page renders pixel for pixel as before");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lzw_inside_a_filter_chain_is_replaced_and_the_other_stages_kept() {
|
||||
let ops = lzw_ops();
|
||||
let inner = lzw(&ops, &Params::default());
|
||||
// An ASCII85 layer outside the LZW goes with it; a JPEG inside it stays encoded as it was.
|
||||
let (mut doc, c) = with_content(filtered(&["ASCII85Decode", "LZWDecode"], ascii85(&inner)));
|
||||
let photo = image(&mut doc, 40, 30, 3, true, None);
|
||||
let jpeg = stream(&doc, photo).raw.to_vec();
|
||||
let mut picture = filtered(&["LZWDecode", "DCTDecode"], lzw(&jpeg, &Params::default()));
|
||||
picture.dict.set(b"DecodeParms".to_vec(), Object::Array(vec![Object::Null, Object::Null]));
|
||||
let pic = doc.add(Object::Stream(picture));
|
||||
// An ASCII85 layer over LZW with a PNG predictor: the predictor's parameters move to the one
|
||||
// Flate filter left, without EarlyChange.
|
||||
let rows: Vec<u8> = (0..40 * 30).map(|i| (i % 40 * 3 + i / 40) as u8).collect();
|
||||
let png = Params { predictor: 12, columns: 40, early_change: 0, ..Params::default() };
|
||||
let mut predicted = filtered(&["ASCII85Decode", "LZWDecode"], ascii85(&lzw(&rows, &png)));
|
||||
predicted.dict.set(b"DecodeParms".to_vec(), Object::Array(vec![Object::Null, Object::Dict(decode_parms(&png))]));
|
||||
let pred = doc.add(Object::Stream(predicted));
|
||||
let r = optimize(&mut doc, &clean_up_only()).unwrap();
|
||||
assert_eq!(r.lzw_streams, 3, "{r:?}");
|
||||
let after = stream(&doc, pred);
|
||||
assert_eq!(after.dict.get(b"Filter"), Some(&Object::name("FlateDecode")));
|
||||
let parms = after.dict.get(b"DecodeParms").and_then(Object::as_dict).cloned().unwrap();
|
||||
assert_eq!((parms.int(b"Predictor"), parms.int(b"Columns"), parms.contains(b"EarlyChange")), (Some(12), Some(40), false));
|
||||
assert_eq!(after.decoded_strict().unwrap(), rows);
|
||||
let after = stream(&doc, c);
|
||||
assert_eq!(after.dict.get(b"Filter"), Some(&Object::name("FlateDecode")));
|
||||
assert_eq!(after.decoded_strict().unwrap(), ops);
|
||||
let after = stream(&doc, pic);
|
||||
assert_eq!(after.dict.get(b"Filter"), Some(&Object::Array(vec![Object::name("FlateDecode"), Object::name("DCTDecode")])));
|
||||
assert!(!after.dict.contains(b"DecodeParms"), "all null: dropped");
|
||||
let inside = pdfcraft_filters::decode(&[(Filter::Flate, Params::default())], &after.raw, 1 << 20).unwrap();
|
||||
assert_eq!(inside, jpeg, "the JPEG bytes are kept exactly");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lzw_streams_that_cannot_be_read_or_are_not_asked_for_stay_unchanged() {
|
||||
let ops = lzw_ops();
|
||||
let good = lzw(&ops, &Params::default());
|
||||
// 9-bit codes 511 after a literal: past the table, corrupt.
|
||||
let corrupt = vec![0x80, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF];
|
||||
assert!(filtered(&["LZWDecode"], corrupt.clone()).decoded_strict().is_err());
|
||||
let mut bad_predictor = filtered(&["LZWDecode"], good.clone());
|
||||
let mut parms = Dict::new();
|
||||
parms.set(b"Predictor".to_vec(), Object::Int(12));
|
||||
parms.set(b"Columns".to_vec(), Object::Int(7));
|
||||
bad_predictor.dict.set(b"DecodeParms".to_vec(), Object::Dict(parms));
|
||||
let mut indirect_parms = filtered(&["LZWDecode"], good.clone());
|
||||
indirect_parms.dict.set(b"DecodeParms".to_vec(), Object::Ref(ObjRef { num: 999, generation: 0 }));
|
||||
let mut objstm = filtered(&["LZWDecode"], good.clone());
|
||||
objstm.dict.set(b"Type".to_vec(), Object::name("ObjStm"));
|
||||
let mut external = filtered(&["LZWDecode"], good.clone());
|
||||
external.dict.set(b"F".to_vec(), Object::String(pdfcraft_cos::PdfString::literal(b"data.bin".to_vec())));
|
||||
let cases = [
|
||||
("corrupt LZW", filtered(&["LZWDecode"], corrupt), Settings::default()),
|
||||
("corrupt under a PNG predictor", bad_predictor, Settings::default()),
|
||||
("indirect DecodeParms", indirect_parms, Settings::default()),
|
||||
("external file", external, Settings::default()),
|
||||
("object stream, rewritten on save", objstm, Settings::default()),
|
||||
("crypt filter", filtered(&["Crypt", "LZWDecode"], good.clone()), Settings::default()),
|
||||
("option off", filtered(&["LZWDecode"], good.clone()), Settings { lzw_to_flate: false, ..Settings::default() }),
|
||||
];
|
||||
for (label, s, settings) in cases {
|
||||
let (mut doc, c) = with_content(s.clone());
|
||||
let r = optimize(&mut doc, &settings).unwrap();
|
||||
assert_eq!(r.lzw_streams, 0, "{label}");
|
||||
assert_eq!(stream(&doc, c), s, "{label}");
|
||||
}
|
||||
// The same data, readable and asked for, is converted: the cases above fail for their reason.
|
||||
let (mut doc, c) = with_content(filtered(&["LZWDecode"], good));
|
||||
assert_eq!(optimize(&mut doc, &Settings::default()).unwrap().lzw_streams, 1);
|
||||
assert_eq!(stream(&doc, c).decoded_strict().unwrap(), ops);
|
||||
}
|
||||
|
||||
@@ -611,6 +611,7 @@
|
||||
Discard private data of other applications تجاهل البيانات الخاصة بالتطبيقات الأخرى
|
||||
Discard hidden text تجاهل النص المخفي
|
||||
Use Flate to encode streams that are not encoded استخدام Flate لترميز التدفقات غير المرمَّزة
|
||||
In streams that use LZW encoding, use Flate instead في التدفقات التي تستخدم ترميز LZW، استخدام Flate بدلاً منه
|
||||
Remove invalid links and bookmarks إزالة الروابط والإشارات المرجعية غير الصالحة
|
||||
Remove unreferenced named destinations إزالة الوجهات المسماة غير المشار إليها
|
||||
Compress document structure (object streams) ضغط بنية المستند (تدفقات الكائنات)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -282,6 +282,7 @@
|
||||
Discard private data of other applications Премахни частните данни на други приложения
|
||||
Discard hidden text Премахни скрития текст
|
||||
Use Flate to encode streams that are not encoded Кодирай с Flate некодираните потоци
|
||||
In streams that use LZW encoding, use Flate instead Кодирай с Flate потоците, кодирани с LZW
|
||||
Remove invalid links and bookmarks Премахни невалидните връзки и отметки
|
||||
Remove unreferenced named destinations Премахни неизползваните именувани местоназначения
|
||||
Compress document structure (object streams) Компресирай структурата на документа (потоци от обекти)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 8 and column 16.
|
@@ -574,6 +574,7 @@
|
||||
Discard private data of other applications Descarta les dades privades d'altres aplicacions
|
||||
Discard hidden text Descarta el text ocult
|
||||
Use Flate to encode streams that are not encoded Utilitza Flate per codificar els fluxos que no estan codificats
|
||||
In streams that use LZW encoding, use Flate instead Als fluxos que utilitzen codificació LZW, utilitza Flate
|
||||
Remove invalid links and bookmarks Elimina els enllaços i marcadors no vàlids
|
||||
Remove unreferenced named destinations Elimina les destinacions amb nom sense referència
|
||||
Compress document structure (object streams) Comprimeix l'estructura del document (fluxos d'objectes)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 7 and column 3.
|
@@ -581,6 +581,7 @@
|
||||
Discard private data of other applications فڕێدانی داتا تایبەتەکانی بەرنامەکانی تر
|
||||
Discard hidden text فڕێدانی دەقی شاراوە
|
||||
Use Flate to encode streams that are not encoded بەکارهێنانی Flate بۆ کۆدکردنی ئەو لێشاوانەی کۆد نەکراون
|
||||
In streams that use LZW encoding, use Flate instead بەکارهێنانی Flate لە جیاتی LZW بۆ ئەو لێشاوانەی بە LZW کۆد کراون
|
||||
Remove invalid links and bookmarks لابردنی بەستەر و دڵخوازە نادروستەکان
|
||||
Remove unreferenced named destinations لابردنی مەبەستە ناونراوەکانی ئاماژەپێنەکراو
|
||||
Compress document structure (object streams) پەستاندنی پێکهاتەی بەڵگەنامە (لێشاوەکانی تەن)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -692,6 +692,7 @@ key Space Mezerník
|
||||
Discard private data of other applications Zahodit soukromá data jiných aplikací
|
||||
Discard hidden text Zahodit skrytý text
|
||||
Use Flate to encode streams that are not encoded Kódovat nekódované proudy metodou Flate
|
||||
In streams that use LZW encoding, use Flate instead Proudy kódované LZW kódovat metodou Flate
|
||||
Remove invalid links and bookmarks Odebrat neplatné odkazy a záložky
|
||||
Remove unreferenced named destinations Odebrat nepoužívané pojmenované cíle
|
||||
Compress document structure (object streams) Komprimovat strukturu dokumentu (proudy objektů)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -584,6 +584,7 @@
|
||||
Discard private data of other applications Private Daten anderer Anwendungen verwerfen
|
||||
Discard hidden text Ausgeblendeten Text verwerfen
|
||||
Use Flate to encode streams that are not encoded Nicht codierte Streams mit Flate codieren
|
||||
In streams that use LZW encoding, use Flate instead In LZW-codierten Streams stattdessen Flate verwenden
|
||||
Remove invalid links and bookmarks Ungültige Links und Lesezeichen entfernen
|
||||
Remove unreferenced named destinations Nicht referenzierte benannte Ziele entfernen
|
||||
Compress document structure (object streams) Dokumentstruktur komprimieren (Objekt-Streams)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 51.
|
@@ -1923,6 +1923,7 @@
|
||||
Discard private data of other applications Αφαίρεση ιδιωτικών δεδομένων άλλων εφαρμογών
|
||||
Discard hidden text Αφαίρεση κρυφού κειμένου
|
||||
Use Flate to encode streams that are not encoded Χρήση συμπίεσης Flate για ροές που δεν είναι συμπιεσμένες
|
||||
In streams that use LZW encoding, use Flate instead Χρήση Flate αντί για LZW στις ροές με κωδικοποίηση LZW
|
||||
Remove invalid links and bookmarks Αφαίρεση μη έγκυρων συνδέσμων και σελιδοδεικτών
|
||||
Remove unreferenced named destinations Αφαίρεση ονομασμένων προορισμών χωρίς αναφορές
|
||||
Compress document structure (object streams) Συμπίεση δομής εγγράφου (ροές αντικειμένων)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 482 and column 9.
|
@@ -607,6 +607,7 @@
|
||||
Discard private data of other applications Descartar datos privados de otras aplicaciones
|
||||
Discard hidden text Descartar texto oculto
|
||||
Use Flate to encode streams that are not encoded Usar Flate para codificar las secuencias sin codificar
|
||||
In streams that use LZW encoding, use Flate instead En las secuencias con codificación LZW, usar Flate en su lugar
|
||||
Remove invalid links and bookmarks Quitar enlaces y marcadores no válidos
|
||||
Remove unreferenced named destinations Quitar destinos con nombre sin referencias
|
||||
Compress document structure (object streams) Comprimir la estructura del documento (secuencias de objetos)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 60.
|
@@ -574,6 +574,7 @@
|
||||
Discard private data of other applications Hylkää muiden sovellusten yksityiset tiedot
|
||||
Discard hidden text Hylkää piilotettu teksti
|
||||
Use Flate to encode streams that are not encoded Koodaa koodaamattomat virrat Flate-menetelmällä
|
||||
In streams that use LZW encoding, use Flate instead Koodaa LZW-koodatut virrat Flate-menetelmällä
|
||||
Remove invalid links and bookmarks Poista virheelliset linkit ja kirjanmerkit
|
||||
Remove unreferenced named destinations Poista viittaamattomat nimetyt kohteet
|
||||
Compress document structure (object streams) Pakkaa asiakirjan rakenne (objektivirrat)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 5 and column 47.
|
@@ -584,6 +584,7 @@
|
||||
Discard private data of other applications Ignorer les données privées d'autres applications
|
||||
Discard hidden text Ignorer le texte masqué
|
||||
Use Flate to encode streams that are not encoded Utiliser Flate pour coder les flux qui ne sont pas codés
|
||||
In streams that use LZW encoding, use Flate instead Dans les flux qui utilisent le codage LZW, utiliser Flate à la place
|
||||
Remove invalid links and bookmarks Retirer les liens et signets non valides
|
||||
Remove unreferenced named destinations Retirer les destinations nommées non référencées
|
||||
Compress document structure (object streams) Compresser la structure du document (flux d'objets)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 39.
|
@@ -558,6 +558,7 @@
|
||||
Discard private data of other applications अन्य ऐप्लिकेशन का निजी डेटा हटाएँ
|
||||
Discard hidden text छिपा हुआ टेक्स्ट हटाएँ
|
||||
Use Flate to encode streams that are not encoded जो स्ट्रीम एन्कोडेड नहीं हैं उन्हें Flate से एन्कोड करें
|
||||
In streams that use LZW encoding, use Flate instead LZW एन्कोडिंग वाली स्ट्रीम में इसके बजाय Flate का उपयोग करें
|
||||
Remove invalid links and bookmarks अमान्य लिंक और बुकमार्क हटाएँ
|
||||
Remove unreferenced named destinations बिना संदर्भ वाले नामित गंतव्य हटाएँ
|
||||
Compress document structure (object streams) दस्तावेज़ संरचना संपीड़ित करें (ऑब्जेक्ट स्ट्रीम)
|
||||
|
||||
|
@@ -582,6 +582,7 @@
|
||||
Discard private data of other applications Más alkalmazások privát adatainak elvetése
|
||||
Discard hidden text Rejtett szöveg elvetése
|
||||
Use Flate to encode streams that are not encoded A kódolatlan adatfolyamok Flate kódolása
|
||||
In streams that use LZW encoding, use Flate instead Az LZW kódolású adatfolyamok Flate kódolása
|
||||
Remove invalid links and bookmarks Érvénytelen hivatkozások és könyvjelzők eltávolítása
|
||||
Remove unreferenced named destinations Hivatkozás nélküli elnevezett célok eltávolítása
|
||||
Compress document structure (object streams) Dokumentumszerkezet tömörítése (objektumfolyamok)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 7 and column 44.
|
@@ -1219,6 +1219,7 @@
|
||||
Update Perbarui
|
||||
Use 72 DPI (one point per pixel) Gunakan 72 DPI (satu titik per piksel)
|
||||
Use Flate to encode streams that are not encoded Gunakan Flate untuk mengodekan stream yang belum dikodekan
|
||||
In streams that use LZW encoding, use Flate instead Pada stream yang memakai pengodean LZW, gunakan Flate sebagai gantinya
|
||||
Use a Digital ID from a file Gunakan ID Digital dari file
|
||||
Use custom DPI Gunakan DPI kustom
|
||||
Use each image's embedded DPI; use 72 DPI when it is absent. Gunakan DPI tertanam pada setiap gambar; gunakan 72 DPI jika tidak ada.
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 483 and column 12.
|
@@ -608,6 +608,7 @@
|
||||
Discard private data of other applications Elimina dati privati di altre applicazioni
|
||||
Discard hidden text Elimina testo nascosto
|
||||
Use Flate to encode streams that are not encoded Usa Flate per codificare i flussi non codificati
|
||||
In streams that use LZW encoding, use Flate instead Nei flussi con codifica LZW, usa Flate
|
||||
Remove invalid links and bookmarks Rimuovi collegamenti e segnalibri non validi
|
||||
Remove unreferenced named destinations Rimuovi destinazioni con nome non referenziate
|
||||
Compress document structure (object streams) Comprimi struttura del documento (flussi di oggetti)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -611,6 +611,7 @@
|
||||
Discard private data of other applications 他のアプリケーションのプライベートデータを破棄
|
||||
Discard hidden text 非表示テキストを破棄
|
||||
Use Flate to encode streams that are not encoded 未エンコードのストリームを Flate でエンコード
|
||||
In streams that use LZW encoding, use Flate instead LZW エンコードのストリームを Flate でエンコード
|
||||
Remove invalid links and bookmarks 無効なリンクとしおりを削除
|
||||
Remove unreferenced named destinations 参照されていない名前付き移動先を削除
|
||||
Compress document structure (object streams) 文書構造を圧縮(オブジェクトストリーム)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -571,6 +571,7 @@
|
||||
Discard private data of other applications Басқа қосымшалардың жеке деректерін алып тастау
|
||||
Discard hidden text Жасырын мәтінді алып тастау
|
||||
Use Flate to encode streams that are not encoded Кодталмаған ағындарды Flate көмегімен кодтау
|
||||
In streams that use LZW encoding, use Flate instead LZW кодталған ағындарда оның орнына Flate қолдану
|
||||
Remove invalid links and bookmarks Жарамсыз сілтемелерді және бетбелгілерді алып тастау
|
||||
Remove unreferenced named destinations Сілтеме берілмеген атаулы межелерді алып тастау
|
||||
Compress document structure (object streams) Құжат құрылымын сығу (нысан ағындары)
|
||||
|
||||
|
@@ -558,6 +558,7 @@
|
||||
Discard private data of other applications 다른 응용 프로그램의 개인 데이터 삭제
|
||||
Discard hidden text 숨겨진 텍스트 삭제
|
||||
Use Flate to encode streams that are not encoded 인코딩되지 않은 스트림을 Flate로 인코딩
|
||||
In streams that use LZW encoding, use Flate instead LZW 인코딩 스트림을 Flate로 인코딩
|
||||
Remove invalid links and bookmarks 잘못된 링크 및 책갈피 제거
|
||||
Remove unreferenced named destinations 참조되지 않은 이름이 지정된 대상 제거
|
||||
Compress document structure (object streams) 문서 구조 압축(개체 스트림)
|
||||
|
||||
|
@@ -579,6 +579,7 @@
|
||||
Discard private data of other applications ຕັດຂໍ້ມູນສ່ວນຕົວຂອງແອັບພລິເຄຊັນອື່ນອອກ
|
||||
Discard hidden text ຕັດຂໍ້ຄວາມທີ່ເຊື່ອງໄວ້ອອກ
|
||||
Use Flate to encode streams that are not encoded ໃຊ້ Flate ເພື່ອເຂົ້າລະຫັດສະຕຣີມທີ່ຍັງບໍ່ໄດ້ເຂົ້າລະຫັດ
|
||||
In streams that use LZW encoding, use Flate instead ໃຊ້ Flate ແທນ LZW ໃນສະຕຣີມທີ່ເຂົ້າລະຫັດດ້ວຍ LZW
|
||||
Remove invalid links and bookmarks ລຶບລິ້ງຄ໌ ແລະ ບຸກມາກທີ່ໃຊ້ການບໍ່ໄດ້
|
||||
Remove unreferenced named destinations ລຶບຈຸດໝາຍປາຍທາງທີ່ມີຊື່ເຊິ່ງບໍ່ມີການອ້າງອີງ
|
||||
Compress document structure (object streams) ບີບອັດໂຄງສ້າງເອກະສານ (ສະຕຣີມວັດຖຸ)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 32.
|
@@ -1278,6 +1278,7 @@
|
||||
Update Atnaujinti
|
||||
Use 72 DPI (one point per pixel) Naudoti 72 DPI (vienas taškas vienam pikseliui)
|
||||
Use Flate to encode streams that are not encoded Nekoduotiems srautams koduoti naudoti „Flate“
|
||||
In streams that use LZW encoding, use Flate instead LZW koduotiems srautams naudoti „Flate“
|
||||
Use a Digital ID from a file Naudoti faile esantį skaitmeninį ID
|
||||
Use custom DPI Naudoti pasirinktinį DPI
|
||||
Use each image's embedded DPI; use 72 DPI when it is absent. Naudoti paveikslėlyje įrašytą DPI; jei jo nėra, naudoti 72 DPI.
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 406 and column 9.
|
@@ -1228,6 +1228,7 @@
|
||||
Update Oppdater
|
||||
Use 72 DPI (one point per pixel) Bruk 72 DPI (ett punkt per piksel)
|
||||
Use Flate to encode streams that are not encoded Bruk Flate til å kode strømmer som ikke er kodet
|
||||
In streams that use LZW encoding, use Flate instead Bruk Flate i stedet for LZW i strømmer som er LZW-kodet
|
||||
Use a Digital ID from a file Bruk en digital ID fra en fil
|
||||
Use custom DPI Bruk egendefinert DPI
|
||||
Use each image's embedded DPI; use 72 DPI when it is absent. Bruk hvert bildes innebygde DPI; bruk 72 DPI når den mangler.
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 8 and column 16.
|
@@ -606,6 +606,7 @@
|
||||
Discard private data of other applications Usuń prywatne dane innych aplikacji
|
||||
Discard hidden text Usuń ukryty tekst
|
||||
Use Flate to encode streams that are not encoded Zakoduj nieskompresowane strumienie metodą Flate
|
||||
In streams that use LZW encoding, use Flate instead Zakoduj strumienie LZW metodą Flate
|
||||
Remove invalid links and bookmarks Usuń nieprawidłowe łącza i zakładki
|
||||
Remove unreferenced named destinations Usuń nieużywane nazwane miejsca docelowe
|
||||
Compress document structure (object streams) Kompresuj strukturę dokumentu (strumienie obiektów)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -238,6 +238,7 @@
|
||||
Discard private data of other applications Descartar dados privados de outros aplicativos
|
||||
Discard hidden text Descartar texto oculto
|
||||
Use Flate to encode streams that are not encoded Usar Flate para codificar fluxos não codificados
|
||||
In streams that use LZW encoding, use Flate instead Em fluxos que usam codificação LZW, usar Flate
|
||||
Remove invalid links and bookmarks Remover links e marcadores inválidos
|
||||
Remove unreferenced named destinations Remover destinos nomeados não referenciados
|
||||
Compress document structure (object streams) Compactar estrutura do documento (fluxos de objetos)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 9 and column 16.
|
@@ -1228,6 +1228,7 @@
|
||||
Update Actualizare
|
||||
Use 72 DPI (one point per pixel) Folosire 72 DPI (un punct pe pixel)
|
||||
Use Flate to encode streams that are not encoded Folosire Flate pentru codarea fluxurilor necodate
|
||||
In streams that use LZW encoding, use Flate instead Folosire Flate în locul LZW pentru fluxurile codate LZW
|
||||
Use a Digital ID from a file Folosire ID digital dintr-un fișier
|
||||
Use custom DPI Folosire DPI personalizat
|
||||
Use each image's embedded DPI; use 72 DPI when it is absent. Folosește DPI-ul încorporat al fiecărei imagini; 72 DPI când lipsește.
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 3 and column 110.
|
@@ -604,6 +604,7 @@
|
||||
Discard private data of other applications Удалить личные данные других приложений
|
||||
Discard hidden text Удалить скрытый текст
|
||||
Use Flate to encode streams that are not encoded Использовать Flate для кодирования некодированных потоков
|
||||
In streams that use LZW encoding, use Flate instead Использовать Flate вместо LZW в потоках с кодированием LZW
|
||||
Remove invalid links and bookmarks Удалить недействительные ссылки и закладки
|
||||
Remove unreferenced named destinations Удалить неиспользуемые именованные места назначения
|
||||
Compress document structure (object streams) Сжать структуру документа (потоки объектов)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 9 and column 16.
|
@@ -1964,6 +1964,7 @@
|
||||
Update watermark… Posodobi vodni žig…
|
||||
Use 72 DPI (one point per pixel) Uporabi 72 DPI (ena točka na slikovno piko)
|
||||
Use Flate to encode streams that are not encoded Za kodiranje nekodiranih tokov uporabi Flate
|
||||
In streams that use LZW encoding, use Flate instead V tokovih s kodiranjem LZW uporabi Flate
|
||||
Use JavaScript Uporabi JavaScript
|
||||
Use a Digital ID from a file Uporabi digitalno identiteto iz datoteke
|
||||
Use a certificate Uporabi potrdilo
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 9 and column 16.
|
@@ -1910,6 +1910,7 @@
|
||||
Update watermark… Përditëso filigranin…
|
||||
Use 72 DPI (one point per pixel) Përdor 72 DPI (një pikë për piksel)
|
||||
Use Flate to encode streams that are not encoded Përdor Flate për të koduar rrjedhat që nuk janë të koduara
|
||||
In streams that use LZW encoding, use Flate instead Në rrjedhat që përdorin kodimin LZW, përdor Flate në vend të tij
|
||||
Use JavaScript Përdor JavaScript
|
||||
Use a Digital ID from a file Përdor një ID dixhitale nga një skedar
|
||||
Use a certificate Përdor një certifikatë
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 9 and column 16.
|
@@ -588,6 +588,7 @@ comment menu Edit సవరించు
|
||||
Discard private data of other applications ఇతర అప్లికేషన్ల ప్రైవేట్ డేటాను విస్మరించు
|
||||
Discard hidden text దాచిన టెక్స్ట్ను విస్మరించు
|
||||
Use Flate to encode streams that are not encoded ఎన్కోడ్ చేయని స్ట్రీమ్లను Flateతో ఎన్కోడ్ చేయి
|
||||
In streams that use LZW encoding, use Flate instead LZW ఎన్కోడింగ్ ఉన్న స్ట్రీమ్లలో బదులుగా Flate ఉపయోగించు
|
||||
Remove invalid links and bookmarks చెల్లని లింక్లు, బుక్మార్క్లను తీసివేయి
|
||||
Remove unreferenced named destinations ఎక్కడా సూచించని పేరున్న గమ్యాలను తీసివేయి
|
||||
Compress document structure (object streams) పత్రం నిర్మాణాన్ని కుదించు (ఆబ్జెక్ట్ స్ట్రీమ్లు)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 13 and column 16.
|
@@ -568,6 +568,7 @@
|
||||
Discard private data of other applications ละทิ้งข้อมูลเฉพาะของแอปพลิเคชันอื่น
|
||||
Discard hidden text ละทิ้งข้อความที่ซ่อนอยู่
|
||||
Use Flate to encode streams that are not encoded ใช้ Flate เข้ารหัสสตรีมที่ยังไม่ได้เข้ารหัส
|
||||
In streams that use LZW encoding, use Flate instead ใช้ Flate แทนในสตรีมที่เข้ารหัสแบบ LZW
|
||||
Remove invalid links and bookmarks เอาลิงก์และบุ๊กมาร์กที่ใช้ไม่ได้ออก
|
||||
Remove unreferenced named destinations เอาปลายทางที่มีชื่อซึ่งไม่ถูกอ้างถึงออก
|
||||
Compress document structure (object streams) บีบอัดโครงสร้างเอกสาร (object streams)
|
||||
|
||||
|
@@ -569,6 +569,7 @@
|
||||
Discard private data of other applications Diğer uygulamaların özel verilerini at
|
||||
Discard hidden text Gizli metni at
|
||||
Use Flate to encode streams that are not encoded Kodlanmamış akışları kodlamak için Flate kullan
|
||||
In streams that use LZW encoding, use Flate instead LZW kodlamalı akışlarda bunun yerine Flate kullan
|
||||
Remove invalid links and bookmarks Geçersiz bağlantıları ve yer işaretlerini kaldır
|
||||
Remove unreferenced named destinations Referans verilmeyen adlandırılmış hedefleri kaldır
|
||||
Compress document structure (object streams) Belge yapısını sıkıştır (nesne akışları)
|
||||
|
||||
|
@@ -609,6 +609,7 @@
|
||||
Discard private data of other applications Видалити приватні дані інших застосунків
|
||||
Discard hidden text Видалити прихований текст
|
||||
Use Flate to encode streams that are not encoded Кодувати незакодовані потоки за допомогою Flate
|
||||
In streams that use LZW encoding, use Flate instead Використовувати Flate замість LZW у потоках із кодуванням LZW
|
||||
Remove invalid links and bookmarks Видалити недійсні посилання й закладки
|
||||
Remove unreferenced named destinations Видалити іменовані призначення без посилань
|
||||
Compress document structure (object streams) Стиснути структуру документа (потоки об’єктів)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -1268,6 +1268,7 @@ theme Appearance Giao diện
|
||||
Update Cập nhật
|
||||
Use 72 DPI (one point per pixel) Dùng 72 DPI (một điểm cho mỗi điểm ảnh)
|
||||
Use Flate to encode streams that are not encoded Dùng Flate để mã hóa các luồng chưa được mã hóa
|
||||
In streams that use LZW encoding, use Flate instead Dùng Flate thay cho LZW trong các luồng mã hóa LZW
|
||||
Use a Digital ID from a file Dùng Digital ID từ một tệp
|
||||
Use custom DPI Dùng DPI tùy chỉnh
|
||||
Use each image's embedded DPI; use 72 DPI when it is absent. Dùng DPI nhúng của từng hình ảnh; dùng 72 DPI khi không có.
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 493 and column 12.
|
@@ -288,6 +288,7 @@
|
||||
Discard private data of other applications 丢弃其他应用程序的私有数据
|
||||
Discard hidden text 丢弃隐藏文本
|
||||
Use Flate to encode streams that are not encoded 使用 Flate 编码未编码的流
|
||||
In streams that use LZW encoding, use Flate instead 在使用 LZW 编码的流中改用 Flate
|
||||
Remove invalid links and bookmarks 删除无效链接和书签
|
||||
Remove unreferenced named destinations 删除未引用的命名目标
|
||||
Compress document structure (object streams) 压缩文档结构(对象流)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 9 and column 16.
|
@@ -244,6 +244,7 @@
|
||||
Discard private data of other applications 捨棄其他應用程式的私有資料
|
||||
Discard hidden text 捨棄隱藏文字
|
||||
Use Flate to encode streams that are not encoded 使用 Flate 編碼未編碼的流
|
||||
In streams that use LZW encoding, use Flate instead 在使用 LZW 編碼的流中改用 Flate
|
||||
Remove invalid links and bookmarks 刪除無效連結和書籤
|
||||
Remove unreferenced named destinations 刪除未引用的命名目標
|
||||
Compress document structure (object streams) 壓縮文件結構(物件流)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 9 and column 16.
|
@@ -219,6 +219,7 @@ pub(crate) fn body(ui: &mut egui::Ui, d: &mut OptimizeDraft, t: &Tokens) -> (boo
|
||||
}
|
||||
OptimizeTab::CleanUp => {
|
||||
ui.add(widgets::checkbox(&mut s.flate_unencoded, tl!("Use Flate to encode streams that are not encoded")));
|
||||
ui.add(widgets::checkbox(&mut s.lzw_to_flate, tl!("In streams that use LZW encoding, use Flate instead")));
|
||||
ui.add(widgets::checkbox(&mut s.remove_invalid_links, tl!("Remove invalid links and bookmarks")));
|
||||
ui.add(widgets::checkbox(&mut s.remove_unreferenced_dests, tl!("Remove unreferenced named destinations")));
|
||||
ui.add_enabled(false, widgets::checkbox(&mut true, tl!("Compress document structure (object streams)")));
|
||||
|
||||
@@ -7589,8 +7589,8 @@ milestone = "M11"
|
||||
status = "partial"
|
||||
commands = ["optimize.advanced"]
|
||||
tools = ["doc_optimize"]
|
||||
evidence = ["crates/optimize/tests/optimize.rs::discards_and_clean_up"]
|
||||
notes = "unencoded streams; LZW not yet re-encoded"
|
||||
evidence = ["crates/optimize/tests/optimize.rs::discards_and_clean_up", "crates/optimize/tests/optimize.rs::lzw_streams_become_flate_and_decode_to_the_same_bytes", "crates/optimize/tests/optimize.rs::a_page_drawn_from_lzw_streams_renders_the_same_after_conversion"]
|
||||
notes = "Two Clean Up options, as in Acrobat: unencoded streams are Flate-compressed, and LZW streams (content and image XObjects; EarlyChange 0 and 1, with or without a predictor, LZW anywhere in a filter chain) are decoded and stored with Flate instead, keeping the predictor and the filters after the LZW one; LZW data that doesn't decode cleanly is left as it is. Not yet: LZW inline images inside content streams."
|
||||
|
||||
[[feature]]
|
||||
id = "optimize.remove-invalid-links"
|
||||
|
||||
Reference in New Issue
Block a user