From d90b3f897f980ec66ab479d69a23127fbe647345 Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Fri, 9 Oct 2026 18:42:23 -0400 Subject: [PATCH] Refs #103: ship the OCR models in the desktop release packages (#630) Release builds said "Text recognition isn't installed: its models are missing" because no packaging script or release job fetched the OCR models: only `cargo xtask models` does, into the source tree. - packaging/env.sh gains stage_models DEST, which runs `cargo xtask models DEST` (every ATTRIBUTION.toml kind = "model" file, SHA-256 verified, with its licence text). No model file is named, so the models PRs #433/#572 add ship without packaging changes. - `cargo xtask models` also writes ATTRIBUTION.txt crediting each model (CC-BY-SA-4.0 requires attribution when redistributed). - macOS: PdfCraft.app/Contents/Resources/models, before signing. - Windows: models\ beside pdfcraft.exe in the MSI (WiX v5 harvest) and the portable zip; test-msi.ps1 checks the folder. - Linux deb/rpm/tar.gz/AppImage, Flatpak bundle and FreeBSD tarball: share/pdfcraft/models beside bin/. Models::search_dirs now also looks in /../share/pdfcraft/models (Models::dirs_beside_exe); a test builds every package layout on disk and finds the models in each. release.yml and freebsd.yml check the packages contain them. - The missing-models messages no longer tell end users to run cargo, and the 11 catalogs that keyed an old PRINTCRAFT_MODELS wording (or the previous one) now key the current English text; ATTRIBUTION hashes updated. A test checks the translations match. The web (wasm) build, which the issue reports, still has no models; this is the desktop half only. Co-authored-by: Claude Opus 5.5 --- .github/workflows/freebsd.yml | 2 +- .github/workflows/release.yml | 3 + ATTRIBUTION.toml | 22 +++---- crates/automation/src/tools.rs | 2 +- crates/ocr/src/lib.rs | 58 +++++++++++++++++-- crates/ui-egui/src/i18n/bg.tsv | 5 +- crates/ui-egui/src/i18n/de.tsv | 5 +- crates/ui-egui/src/i18n/es.tsv | 5 +- crates/ui-egui/src/i18n/fr.tsv | 5 +- crates/ui-egui/src/i18n/hu.tsv | 5 +- crates/ui-egui/src/i18n/ja.tsv | 4 +- crates/ui-egui/src/i18n/mod.rs | 16 +++++ crates/ui-egui/src/i18n/ru.tsv | 5 +- crates/ui-egui/src/i18n/te.tsv | 5 +- crates/ui-egui/src/i18n/uk.tsv | 5 +- crates/ui-egui/src/i18n/zh-hans.tsv | 5 +- crates/ui-egui/src/i18n/zh-hant.tsv | 4 +- crates/ui-egui/src/ocr_ui.rs | 2 +- docs/releasing.md | 8 +++ packaging/env.sh | 19 ++++++ packaging/freebsd/package.sh | 4 +- packaging/linux/flatpak-bundle.sh | 2 +- .../ai.storyteller.pdfcraft.bundle.yml | 2 + packaging/linux/package.sh | 4 +- packaging/macos/package.sh | 3 + packaging/windows/package.ps1 | 19 +++++- packaging/windows/pdfcraft.wxs | 14 ++++- packaging/windows/test-msi.ps1 | 16 ++++- parity/acrobat-features.toml | 2 +- xtask/src/assets.rs | 29 +++++++++- 30 files changed, 218 insertions(+), 62 deletions(-) diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index a86fc140b..b852d863a 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -49,4 +49,4 @@ jobs: CARGO_PROFILE_DEV_DEBUG=0 cargo test --workspace --locked # The release packaging, end to end (a quicker release profile than the real release). CARGO_PROFILE_RELEASE_LTO=false CARGO_PROFILE_RELEASE_CODEGEN_UNITS=16 packaging/freebsd/package.sh - tar -tzf dist/release/pdfcraft-*-freebsd-*.tar.gz | grep -E '/bin/pdfcraft$|/bin/pdfcraft-cli$|applications/' + tar -tzf dist/release/pdfcraft-*-freebsd-*.tar.gz | grep -E '/bin/pdfcraft$|/bin/pdfcraft-cli$|applications/|/share/pdfcraft/models/.+\.rten$' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e5c62e1f5..2f6c3a4fd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -222,6 +222,9 @@ jobs: run: | dpkg-deb --info dist/release/*.deb dpkg-deb --contents dist/release/*.deb | grep -E 'usr/bin/pdfcraft$|metainfo|applications' + # The OCR models (#103), where the installed app looks: /../share/pdfcraft/models. + dpkg-deb --contents dist/release/*.deb | grep -E 'usr/share/pdfcraft/models/.+\.rten$' + tar -tzf dist/release/*.tar.gz | grep -E '/share/pdfcraft/models/.+\.rten$' ldd target/release/pdfcraft # The CLI-only archive: the binary and its licences, and it runs. cli="dist/release/pdfcraft-cli-$PDFCRAFT_VERSION-linux-${{ matrix.arch }}.tar.gz" diff --git a/ATTRIBUTION.toml b/ATTRIBUTION.toml index f8f3781ce..99cbbfb0b 100644 --- a/ATTRIBUTION.toml +++ b/ATTRIBUTION.toml @@ -19,7 +19,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 = "d1f4c7c71d2359e816878e8de837119649513e20b8cf1036276f4d18639398f5" +sha256 = "ed6e797e2ae7e4632aabc585af973c15f34d0366671a6c57acf222a3a98b087b" [[asset]] path = "crates/ui-egui/src/i18n/zh-hant.tsv" @@ -30,7 +30,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 = "1cac4789161502b2fc3c52ac0bc9a89fd714495a87c594ba7a4f04e49e5a6456" +sha256 = "0575e82da2596ca407ad8153cde04750fa6c1ab98ab7b850032ed361d51f9a4e" [[asset]] path = "crates/ui-egui/src/i18n/cs.tsv" @@ -63,7 +63,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 = "85a57e66f819e93c8c541548cd932ec98cd3dca888c25b62d5c973fb795eb78a" +sha256 = "460a8a8740f1a8a97f3c3e4bc8d27913d417539a5667a2686a4ec1047c309d39" [[asset]] path = "crates/ui-egui/src/i18n/fr.tsv" @@ -74,7 +74,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 = "f005a60c77110251be379572e3eb6bf9a7907946788f5ce0f8689800f5cc7b76" +sha256 = "e41c2db222bd35f291a8fa286ac49f4efc8886646269a084958aaa59e2c848c8" [[asset]] path = "crates/ui-egui/src/i18n/de.tsv" @@ -85,7 +85,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 = "a53fb2b7d91a6f9cb37c5d7fcc77e0884b9b23a0f406569ba203062cff731be1" +sha256 = "357f864eb011b8fdcbe0db609a85ce6f8c7fb421769361e5def005cc515d311d" [[asset]] path = "crates/ui-egui/src/i18n/te.tsv" @@ -96,7 +96,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 = "affd4f7f2e7285abfb987625bbb726cf3d761f0a67ccaf00c18d986be3fa5f9d" +sha256 = "2d42c2fbb62771bad33dab83926adc34c553b7d0b6cf666322076b9820f641de" [[asset]] path = "crates/ui-egui/src/i18n/ru.tsv" @@ -107,7 +107,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 = "88e402a8a16ba8ccd6f73e91326691ce4c4f86bf113cc484fb4c2af953ab3122" +sha256 = "bb5e94d419faf579e9cd0ef9e1f26da6aa342567004a8ff53cb0a2037a8981bb" [[asset]] path = "crates/ui-egui/src/i18n/hu.tsv" @@ -118,7 +118,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 = "54561c1c94ad81a6e452e42cb9d9d49ef5417d4d4b08a1b71cadba1327b85499" +sha256 = "31b4e703f7b60727e09cc7e68b287e748f4dbd0ae9e1298b96714bcf8da6e74e" [[asset]] path = "crates/ui-egui/src/i18n/bg.tsv" @@ -129,7 +129,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 = "af9bd7f0a2c9f64faaea928e219be50a9ce8a71d40ae4c13e84cfe1aed751f40" +sha256 = "d25162d477b3370238b23b0a61e827c4df73aac2cb5ccfc694f79e2adc9798c7" [[asset]] path = "crates/ui-egui/src/i18n/ja.tsv" @@ -140,7 +140,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 = "b93836c384d11e5027b902f6f5a9fa389a5a793213bb77f6754e4cf71fdc28c8" +sha256 = "0534c5852449a72af65da004b085c6d0566aa7d9cdf98d38978ba3e39493ef75" [[asset]] path = "crates/ui-egui/src/i18n/uk.tsv" @@ -151,7 +151,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 = "55fb645e8983bd4396c3d70a7a53040bd260476b198d3cf2cf5d223f909e7436" +sha256 = "306d998039e76769eeb6fa43871807193b8f2ebafc4924f15d17db710ddab184" [[asset]] path = "assets/icons/accessibility.svg" diff --git a/crates/automation/src/tools.rs b/crates/automation/src/tools.rs index 8eb2fa03f..9fa6a54a9 100644 --- a/crates/automation/src/tools.rs +++ b/crates/automation/src/tools.rs @@ -898,7 +898,7 @@ pub fn tools() -> Vec { }), &["paths", "folder"], )), - t("ocr_status", "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.") + t("ocr_status", "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.") .ro() .cmd("ocr.recognize") .with(schema(json!({}), &[])), diff --git a/crates/ocr/src/lib.rs b/crates/ocr/src/lib.rs index f026f5bde..ae829d965 100644 --- a/crates/ocr/src/lib.rs +++ b/crates/ocr/src/lib.rs @@ -23,7 +23,9 @@ pub const LANGUAGES: &[(&str, &str)] = &[("en", "English")]; #[derive(Debug, thiserror::Error)] pub enum OcrError { - #[error("the text recognition models are not installed (run `cargo xtask models`, or set PDFCRAFT_MODELS)")] + /// Shown to end users (release packages ship the models; a source build fetches them with + /// `cargo xtask models`). The UI catalogs key this exact text. + #[error("the text recognition models are not installed (reinstall PdfCraft, or set PDFCRAFT_MODELS to the folder that holds them)")] NoModels, #[error("loading {0}: {1}")] Load(String, String), @@ -47,8 +49,8 @@ impl Models { (m.detection.is_file() && m.recognition.is_file()).then_some(m) } - /// Look for the models: `$PDFCRAFT_MODELS`, then `models/` beside the executable (and - /// `Resources/models` in a macOS bundle), then the source tree's `assets/models/`. + /// Look for the models: `$PDFCRAFT_MODELS`, then where the release packages install them + /// beside the executable ([`Models::dirs_beside_exe`]), then the source tree's `assets/models/`. pub fn find() -> Option { Self::search_dirs().iter().find_map(|d| Self::in_dir(d)) } @@ -60,12 +62,21 @@ impl Models { dirs.push(PathBuf::from(d)); } if let Some(exe) = std::env::current_exe().ok().and_then(|e| e.parent().map(Path::to_path_buf)) { - dirs.push(exe.join("models")); - dirs.push(exe.join("../Resources/models")); + dirs.extend(Self::dirs_beside_exe(&exe)); } dirs.push(Path::new(env!("CARGO_MANIFEST_DIR")).join("../../assets/models")); dirs } + + /// Where the release packages put the models, relative to the directory holding the + /// executable (packaging/*): + /// - `models/` beside it: the Windows MSI and portable zip; + /// - `../Resources/models`: the macOS app bundle (`Contents/MacOS` → `Contents/Resources`); + /// - `../share/pdfcraft/models`: the FHS-style trees (`bin/` → `share/`) of the Linux + /// deb/rpm/tar.gz/AppImage/Flatpak and the FreeBSD tarball, wherever they are installed. + pub fn dirs_beside_exe(exe_dir: &Path) -> Vec { + vec![exe_dir.join("models"), exe_dir.join("../Resources/models"), exe_dir.join("../share/pdfcraft/models")] + } } /// A recognised word: its text and its box in image pixels `[left, top, right, bottom]`. @@ -236,6 +247,43 @@ mod tests { assert_eq!(w.up, [-10.0, 0.0]); } + /// #103: every release package's layout puts the models where the installed app looks. + /// Mirrors packaging/{windows/package.ps1,macos/package.sh,linux/package.sh,freebsd/package.sh}. + #[test] + fn finds_the_models_in_every_release_package_layout() { + let base = std::env::temp_dir().join(format!("pdfcraft-ocr-layouts-{}", std::process::id())); + let layouts = [ + ("windows", "PdfCraft", "PdfCraft/models"), + ("macos", "PdfCraft.app/Contents/MacOS", "PdfCraft.app/Contents/Resources/models"), + ("linux-deb-rpm", "usr/bin", "usr/share/pdfcraft/models"), + ("linux-tar", "pdfcraft-1.0.0-linux-x86_64/bin", "pdfcraft-1.0.0-linux-x86_64/share/pdfcraft/models"), + ("appimage", "PdfCraft.AppDir/usr/bin", "PdfCraft.AppDir/usr/share/pdfcraft/models"), + ("flatpak", "app/bin", "app/share/pdfcraft/models"), + ("freebsd", "usr/local/bin", "usr/local/share/pdfcraft/models"), + ]; + for (name, exe_dir, models_dir) in layouts { + let root = base.join(name); + let (exe_dir, models_dir) = (root.join(exe_dir), root.join(models_dir)); + std::fs::create_dir_all(&exe_dir).unwrap(); + std::fs::create_dir_all(&models_dir).unwrap(); + let found = || Models::dirs_beside_exe(&exe_dir).iter().find_map(|d| Models::in_dir(d)); + assert_eq!(found(), None, "{name}: no models yet"); + for file in [DETECTION_MODEL, RECOGNITION_MODEL] { + std::fs::write(models_dir.join(file), b"model").unwrap(); + } + let models = found().unwrap_or_else(|| panic!("{name}: models in {} not found", models_dir.display())); + assert_eq!(models.detection.canonicalize().unwrap(), models_dir.join(DETECTION_MODEL).canonicalize().unwrap(), "{name}"); + } + let _ = std::fs::remove_dir_all(&base); + } + + /// The message tells end users what to do; release builds have no cargo. + #[test] + fn missing_models_message_is_for_end_users() { + let message = OcrError::NoModels.to_string(); + assert!(!message.contains("cargo") && message.contains("PDFCRAFT_MODELS"), "{message}"); + } + /// End to end on rendered text, when the models are installed (`cargo xtask models`). #[test] fn reads_rendered_text() { diff --git a/crates/ui-egui/src/i18n/bg.tsv b/crates/ui-egui/src/i18n/bg.tsv index cc047f5a2..419ef3135 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -899,7 +899,7 @@ Searchable Image (Exact) Изображение с възможност за търсене (точно) Adds invisible text over each word; the page image is not changed Добавя невидим текст върху всяка дума; изображението на страницата не се променя Downsample to Намали разделителната способност до - Text recognition isn't installed: its models are missing (run `cargo xtask models`, or set PRINTCRAFT_MODELS). Разпознаването на текст не е инсталирано: моделите му липсват (изпълнете `cargo xtask models` или задайте PRINTCRAFT_MODELS). + Text recognition isn't installed: its model files are missing. Reinstall PdfCraft, or set PDFCRAFT_MODELS to the folder that holds them. Разпознаването на текст не е инсталирано: файловете на моделите му липсват. Инсталирайте PdfCraft отново или задайте в PDFCRAFT_MODELS папката, която ги съдържа. Settings Настройки Protect Защита Advanced options Разширени опции @@ -1968,7 +1968,7 @@ the image is empty изображението е празно The operation failed. Операцията е неуспешна. the password is incorrect паролата е грешна - the text recognition models are not installed (run `cargo xtask models`, or set PRINTCRAFT_MODELS) моделите за разпознаване на текст не са инсталирани (изпълнете `cargo xtask models` или задайте PRINTCRAFT_MODELS) + the text recognition models are not installed (reinstall PdfCraft, or set PDFCRAFT_MODELS to the folder that holds them) моделите за разпознаване на текст не са инсталирани (инсталирайте PdfCraft отново или задайте в PDFCRAFT_MODELS папката, която ги съдържа) The title in the metadata and the document information differ Заглавието в метаданните и в информацията за документа се различават the title must not be empty заглавието не може да е празно there are no pages to print няма страници за печат @@ -1997,7 +1997,6 @@ Scripts run in a sandbox without file or network access. A script that asks to open a web page needs your permission first. With JavaScript off, Acrobat's standard format, validate and calculate functions still work. Скриптовете се изпълняват в изолирана среда без достъп до файлове или мрежа. Скрипт, който иска да отвори уебстраница, първо се нуждае от вашето разрешение. При изключен JavaScript стандартните функции на Acrobat за форматиране, валидиране и изчисляване продължават да работят. Sign yourself Подпишете сами Switch to two-page view first to show the cover page Първо превключете към изглед с две страници, за да се покаже корицата - Text recognition isn't installed: its models are missing (run `cargo xtask models`, or set PDFCRAFT_MODELS). Разпознаването на текст не е инсталирано: моделите му липсват (изпълнете `cargo xtask models` или задайте PDFCRAFT_MODELS). Used when opening PDFs. An explicit launch or control mode takes precedence for the session. Използва се при отваряне на PDF файлове. Изрично зададен режим при стартиране или управление има предимство за сесията. Used when a PDF doesn't ask for a layout or zoom. Continuous scrolling never snaps between pages. Използва се, когато PDF файлът не задава оформление или мащаб. Непрекъснатото превъртане никога не спира на границите между страниците. Write this email? Да се напише ли този имейл? diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index 8aa85e06a..1c4aa3ebc 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -1174,7 +1174,7 @@ Searchable Image (Exact) Durchsuchbares Bild (exakt) Adds invisible text over each word; the page image is not changed Legt unsichtbaren Text über jedes Wort; das Seitenbild bleibt unverändert Downsample to Herunterrechnen auf - Text recognition isn't installed: its models are missing (run `cargo xtask models`, or set PRINTCRAFT_MODELS). Die Texterkennung ist nicht installiert, ihre Modelle fehlen (führe `cargo xtask models` aus oder setze PRINTCRAFT_MODELS). + Text recognition isn't installed: its model files are missing. Reinstall PdfCraft, or set PDFCRAFT_MODELS to the folder that holds them. Die Texterkennung ist nicht installiert: ihre Modelldateien fehlen. Installiere PdfCraft neu oder setze PDFCRAFT_MODELS auf den Ordner, der sie enthält. Settings Einstellungen Optional Optional Planned · {m} Geplant · {m} @@ -1693,7 +1693,7 @@ there are no pages to print keine Seiten zum Drucken vorhanden the document has no tags das Dokument hat keine Tags {0}: {1} {0}: {1} - the text recognition models are not installed (run `cargo xtask models`, or set PRINTCRAFT_MODELS) die Modelle für die Texterkennung sind nicht installiert (führe `cargo xtask models` aus oder setze PRINTCRAFT_MODELS) + the text recognition models are not installed (reinstall PdfCraft, or set PDFCRAFT_MODELS to the folder that holds them) die Modelle für die Texterkennung sind nicht installiert (installiere PdfCraft neu oder setze PDFCRAFT_MODELS auf den Ordner, der sie enthält) the image is empty das Bild ist leer this file isn't XFDF, FDF, XML or text form data diese Datei enthält keine Formulardaten als XFDF, FDF, XML oder Text nothing in the file matches this document nichts in der Datei passt zu diesem Dokument @@ -1888,7 +1888,6 @@ Open this web page? Diese Webseite öffnen? Scripts run in a sandbox without file or network access. A script that asks to open a web page needs your permission first. With JavaScript off, Acrobat's standard format, validate and calculate functions still work. Skripte laufen in einer Sandbox ohne Datei- oder Netzwerkzugriff. Ein Skript, das eine Webseite öffnen will, braucht zuerst deine Erlaubnis. Bei deaktiviertem JavaScript funktionieren die Standardfunktionen von Acrobat zum Formatieren, Prüfen und Berechnen weiterhin. Sign yourself Selbst unterschreiben - Text recognition isn't installed: its models are missing (run `cargo xtask models`, or set PDFCRAFT_MODELS). Die Texterkennung ist nicht installiert, ihre Modelle fehlen (führe `cargo xtask models` aus oder setze PDFCRAFT_MODELS). Used when opening PDFs. An explicit launch or control mode takes precedence for the session. Wird beim Öffnen von PDFs verwendet. Ein ausdrücklich gewählter Start- oder Steuermodus hat für die Sitzung Vorrang. Write this email? Diese E-Mail schreiben? {who} in this document wants to open this address in your browser: {who} in diesem Dokument möchte diese Adresse in deinem Browser öffnen: diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index 079faac35..4c75dcb94 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -1193,7 +1193,7 @@ Searchable Image (Exact) Imagen con búsqueda (exacta) Adds invisible text over each word; the page image is not changed Añade texto invisible sobre cada palabra; la imagen de la página no cambia Downsample to Reducir resolución a - Text recognition isn't installed: its models are missing (run `cargo xtask models`, or set PRINTCRAFT_MODELS). El reconocimiento de texto no está instalado: faltan sus modelos (ejecuta `cargo xtask models` o define PRINTCRAFT_MODELS). + Text recognition isn't installed: its model files are missing. Reinstall PdfCraft, or set PDFCRAFT_MODELS to the folder that holds them. El reconocimiento de texto no está instalado: faltan sus archivos de modelo. Reinstala PdfCraft o define PDFCRAFT_MODELS con la carpeta que los contiene. Settings Ajustes Optional Opcional Planned · {m} Previsto · {m} @@ -1769,7 +1769,7 @@ there are no pages to print no hay páginas que imprimir the document has no tags el documento no tiene etiquetas {0}: {1} {0}: {1} - the text recognition models are not installed (run `cargo xtask models`, or set PRINTCRAFT_MODELS) los modelos de reconocimiento de texto no están instalados (ejecuta `cargo xtask models` o define PRINTCRAFT_MODELS) + the text recognition models are not installed (reinstall PdfCraft, or set PDFCRAFT_MODELS to the folder that holds them) los modelos de reconocimiento de texto no están instalados (reinstala PdfCraft o define PDFCRAFT_MODELS con la carpeta que los contiene) the image is empty la imagen está vacía this file isn't XFDF, FDF, XML or text form data este archivo no contiene datos de formulario XFDF, FDF, XML ni de texto nothing in the file matches this document nada en el archivo coincide con este documento @@ -1964,7 +1964,6 @@ Open this web page? ¿Abrir esta página web? Scripts run in a sandbox without file or network access. A script that asks to open a web page needs your permission first. With JavaScript off, Acrobat's standard format, validate and calculate functions still work. Los scripts se ejecutan en un entorno aislado, sin acceso a archivos ni a la red. Un script que pida abrir una página web necesita antes tu permiso. Con JavaScript desactivado, las funciones estándar de formato, validación y cálculo de Acrobat siguen funcionando. Sign yourself Firmar tú mismo - Text recognition isn't installed: its models are missing (run `cargo xtask models`, or set PDFCRAFT_MODELS). El reconocimiento de texto no está instalado: faltan sus modelos (ejecuta `cargo xtask models` o define PDFCRAFT_MODELS). Used when opening PDFs. An explicit launch or control mode takes precedence for the session. Se usa al abrir archivos PDF. Un modo de inicio o de control explícito tiene prioridad durante la sesión. Write this email? ¿Escribir este correo? {who} in this document wants to open this address in your browser: {who} de este documento quiere abrir esta dirección en tu navegador: diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index e83f588cd..0b008dbe8 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -1173,7 +1173,7 @@ Searchable Image (Exact) Image recherchable (exacte) Adds invisible text over each word; the page image is not changed Ajoute du texte invisible sur chaque mot ; l'image de la page n'est pas modifiée Downsample to Sous-échantillonner à - Text recognition isn't installed: its models are missing (run `cargo xtask models`, or set PRINTCRAFT_MODELS). La reconnaissance de texte n'est pas installée : ses modèles sont absents (exécutez `cargo xtask models`, ou définissez PRINTCRAFT_MODELS). + Text recognition isn't installed: its model files are missing. Reinstall PdfCraft, or set PDFCRAFT_MODELS to the folder that holds them. La reconnaissance de texte n'est pas installée : ses fichiers de modèles sont absents. Réinstallez PdfCraft, ou définissez PDFCRAFT_MODELS sur le dossier qui les contient. Settings Réglages Optional Facultatif Planned · {m} Prévu · {m} @@ -1692,7 +1692,7 @@ there are no pages to print il n'y a aucune page à imprimer the document has no tags le document ne contient aucune balise {0}: {1} {0} : {1} - the text recognition models are not installed (run `cargo xtask models`, or set PRINTCRAFT_MODELS) les modèles de reconnaissance de texte ne sont pas installés (exécutez `cargo xtask models`, ou définissez PRINTCRAFT_MODELS) + the text recognition models are not installed (reinstall PdfCraft, or set PDFCRAFT_MODELS to the folder that holds them) les modèles de reconnaissance de texte ne sont pas installés (réinstallez PdfCraft, ou définissez PDFCRAFT_MODELS sur le dossier qui les contient) the image is empty l'image est vide this file isn't XFDF, FDF, XML or text form data ce fichier n'est pas des données de formulaire XFDF, FDF, XML ou texte nothing in the file matches this document rien dans le fichier ne correspond à ce document @@ -1887,7 +1887,6 @@ Open this web page? Ouvrir cette page web ? Scripts run in a sandbox without file or network access. A script that asks to open a web page needs your permission first. With JavaScript off, Acrobat's standard format, validate and calculate functions still work. Les scripts s'exécutent dans un bac à sable, sans accès aux fichiers ni au réseau. Un script qui demande d'ouvrir une page web a d'abord besoin de votre autorisation. Si JavaScript est désactivé, les fonctions standard de format, de validation et de calcul d'Acrobat continuent de fonctionner. Sign yourself Signer soi-même - Text recognition isn't installed: its models are missing (run `cargo xtask models`, or set PDFCRAFT_MODELS). La reconnaissance de texte n'est pas installée : ses modèles sont absents (exécutez `cargo xtask models`, ou définissez PDFCRAFT_MODELS). Used when opening PDFs. An explicit launch or control mode takes precedence for the session. Utilisé à l'ouverture des PDF. Un mode de lancement ou de contrôle explicite a priorité pour la session. Write this email? Écrire ce courriel ? {who} in this document wants to open this address in your browser: {who} dans ce document veut ouvrir cette adresse dans votre navigateur : diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index eae14c395..82d8c6c1a 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -1169,7 +1169,7 @@ Searchable Image (Exact) Kereshető kép (pontos) Adds invisible text over each word; the page image is not changed Láthatatlan szöveget helyez minden szóra; az oldalkép nem változik Downsample to Feloldás csökkentése erre - Text recognition isn't installed: its models are missing (run `cargo xtask models`, or set PRINTCRAFT_MODELS). A szövegfelismerés nincs telepítve: hiányoznak a modelljei (futtasd a `cargo xtask models` parancsot, vagy állítsd be a PRINTCRAFT_MODELS változót). + Text recognition isn't installed: its model files are missing. Reinstall PdfCraft, or set PDFCRAFT_MODELS to the folder that holds them. A szövegfelismerés nincs telepítve: hiányoznak a modellfájljai. Telepítsd újra a PdfCraftot, vagy állítsd a PDFCRAFT_MODELS változót az őket tartalmazó mappára. Settings Beállítások Optional Nem kötelező Planned · {m} Tervezett · {m} @@ -1688,7 +1688,7 @@ there are no pages to print nincs nyomtatható oldal the document has no tags a dokumentumnak nincs címkéje {0}: {1} {0}: {1} - the text recognition models are not installed (run `cargo xtask models`, or set PRINTCRAFT_MODELS) a szövegfelismerés modelljei nincsenek telepítve (futtasd a `cargo xtask models` parancsot, vagy állítsd be a PRINTCRAFT_MODELS változót) + the text recognition models are not installed (reinstall PdfCraft, or set PDFCRAFT_MODELS to the folder that holds them) a szövegfelismerés modelljei nincsenek telepítve (telepítsd újra a PdfCraftot, vagy állítsd a PDFCRAFT_MODELS változót az őket tartalmazó mappára) the image is empty a kép üres this file isn't XFDF, FDF, XML or text form data ez a fájl nem XFDF, FDF, XML vagy szöveges űrlapadat nothing in the file matches this document a fájlban semmi sem illeszkedik ehhez a dokumentumhoz @@ -1883,7 +1883,6 @@ Open this web page? Megnyitod ezt a weboldalt? Scripts run in a sandbox without file or network access. A script that asks to open a web page needs your permission first. With JavaScript off, Acrobat's standard format, validate and calculate functions still work. A szkriptek homokozóban futnak, fájl- és hálózat-hozzáférés nélkül. Ha egy szkript weboldalt akar nyitni, előbb az engedélyed kell. Kikapcsolt JavaScript mellett az Acrobat szabványos formázó, ellenőrző és számoló függvényei továbbra is működnek. Sign yourself Saját kezű aláírás - Text recognition isn't installed: its models are missing (run `cargo xtask models`, or set PDFCRAFT_MODELS). A szövegfelismerés nincs telepítve: hiányoznak a modelljei (futtasd a `cargo xtask models` parancsot, vagy állítsd be a PDFCRAFT_MODELS változót). Used when opening PDFs. An explicit launch or control mode takes precedence for the session. PDF-ek megnyitásakor érvényes. A kifejezetten választott indítási vagy vezérlőmód a munkamenetre felülírja. Write this email? Megírod ezt az e-mailt? {who} in this document wants to open this address in your browser: {who} ebben a dokumentumban meg akarja nyitni ezt a címet a böngésződben: diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index 8d9d5511e..713bf60d7 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -1199,7 +1199,7 @@ Searchable Image (Exact) 検索可能な画像(元の画像を保持) Adds invisible text over each word; the page image is not changed 各単語に非表示のテキストを重ねます。ページ画像は変更しません Downsample to ダウンサンプル: - Text recognition isn't installed: its models are missing (run `cargo xtask models`, or set PRINTCRAFT_MODELS). テキスト認識がインストールされていません。モデルがありません(`cargo xtask models` を実行するか PRINTCRAFT_MODELS を設定してください)。 + Text recognition isn't installed: its model files are missing. Reinstall PdfCraft, or set PDFCRAFT_MODELS to the folder that holds them. テキスト認識がインストールされていません。モデルファイルがありません。PdfCraft を再インストールするか、PDFCRAFT_MODELS にモデルファイルのあるフォルダーを設定してください。 Settings 設定 Optional 任意 Planned · {m} 予定 · {m} @@ -1775,7 +1775,7 @@ there are no pages to print 印刷するページはありません the document has no tags 文書にタグはありません {0}: {1} {0}: {1} - the text recognition models are not installed (run `cargo xtask models`, or set PRINTCRAFT_MODELS) テキスト認識モデルがインストールされていません(`cargo xtask models` を実行するか PRINTCRAFT_MODELS を設定してください) + the text recognition models are not installed (reinstall PdfCraft, or set PDFCRAFT_MODELS to the folder that holds them) テキスト認識モデルがインストールされていません(PdfCraft を再インストールするか、PDFCRAFT_MODELS にモデルのあるフォルダーを設定してください) the image is empty 画像が空です this file isn't XFDF, FDF, XML or text form data このファイルは XFDF、FDF、XML、テキストのフォームデータではありません nothing in the file matches this document この文書に一致する項目がファイルにありません diff --git a/crates/ui-egui/src/i18n/mod.rs b/crates/ui-egui/src/i18n/mod.rs index 93931b669..15a33b786 100644 --- a/crates/ui-egui/src/i18n/mod.rs +++ b/crates/ui-egui/src/i18n/mod.rs @@ -637,6 +637,22 @@ mod tests { assert_eq!(fmt("unchanged", &[]), "unchanged"); } + /// #103: the missing-OCR-models messages are translated under their current English text + /// (catalogs once keyed an older `PRINTCRAFT_MODELS` wording, which never matched). + #[test] + fn missing_ocr_models_messages_are_translated() { + let error = pdfcraft_engine::ocr::OcrError::NoModels.to_string(); + let notice = "Text recognition isn't installed: its model files are missing. Reinstall PdfCraft, or set PDFCRAFT_MODELS to the folder that holds them."; + assert!(include_str!("../ocr_ui.rs").contains(notice), "keep in step with ocr_ui.rs"); + for code in ["bg", "de", "es", "fr", "hu", "ja", "ru", "te", "uk", "zh-hans", "zh-hant"] { + let lang = Lang::from_code(code).expect("registered"); + assert!(has(lang, notice) && has(lang, &error), "{code}"); + } + for lang in &LANGUAGES { + assert!(!lang.source.contains("PRINTCRAFT"), "{}", lang.code); + } + } + /// Japanese translates every registered command and every All tools group, section and item. #[test] fn japanese_covers_commands_and_catalogue() { diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index 12c8ef8ce..fbe654bfc 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -1194,7 +1194,7 @@ Searchable Image (Exact) Доступное для поиска изображение (точное) Adds invisible text over each word; the page image is not changed Добавляет невидимый текст над каждым словом; изображение страницы не меняется Downsample to Уменьшить до - Text recognition isn't installed: its models are missing (run `cargo xtask models`, or set PRINTCRAFT_MODELS). Распознавание текста не установлено: отсутствуют модели (запустите `cargo xtask models` или задайте PRINTCRAFT_MODELS). + Text recognition isn't installed: its model files are missing. Reinstall PdfCraft, or set PDFCRAFT_MODELS to the folder that holds them. Распознавание текста не установлено: отсутствуют файлы моделей. Переустановите PdfCraft или задайте в PDFCRAFT_MODELS папку, в которой они лежат. Settings Настройки Optional Опционально Planned · {m} Запланировано · {m} @@ -1760,7 +1760,7 @@ there are no pages to print нет страниц для печати the document has no tags у документа нет тегов {0}: {1} {0}: {1} - the text recognition models are not installed (run `cargo xtask models`, or set PRINTCRAFT_MODELS) модели распознавания текста не установлены (запустите `cargo xtask models` или задайте PRINTCRAFT_MODELS) + the text recognition models are not installed (reinstall PdfCraft, or set PDFCRAFT_MODELS to the folder that holds them) модели распознавания текста не установлены (переустановите PdfCraft или задайте в PDFCRAFT_MODELS папку, в которой они лежат) the image is empty изображение пустое this file isn't XFDF, FDF, XML or text form data этот файл не является XFDF, FDF, XML или текстовыми данными формы nothing in the file matches this document ничего в файле не соответствует этому документу @@ -1955,7 +1955,6 @@ Open this web page? Открыть эту веб-страницу? Scripts run in a sandbox without file or network access. A script that asks to open a web page needs your permission first. With JavaScript off, Acrobat's standard format, validate and calculate functions still work. Скрипты выполняются в песочнице без доступа к файлам и сети. Скрипт, который просит открыть веб-страницу, сначала нуждается в вашем разрешении. При отключённом JavaScript стандартные функции Acrobat форматирования, проверки и вычислений всё равно работают. Sign yourself Подписать самим собой - Text recognition isn't installed: its models are missing (run `cargo xtask models`, or set PDFCRAFT_MODELS). Распознавание текста не установлено: отсутствуют модели (запустите `cargo xtask models` или задайте PDFCRAFT_MODELS). Used when opening PDFs. An explicit launch or control mode takes precedence for the session. Используется при открытии PDF. Явный запуск или режим управления имеет приоритет для сессии. Write this email? Написать это письмо? {who} in this document wants to open this address in your browser: {who} в этом документе хочет открыть этот адрес в вашем браузере: diff --git a/crates/ui-egui/src/i18n/te.tsv b/crates/ui-egui/src/i18n/te.tsv index ebef861c6..28c6e24cb 100644 --- a/crates/ui-egui/src/i18n/te.tsv +++ b/crates/ui-egui/src/i18n/te.tsv @@ -1173,7 +1173,7 @@ comment menu Edit సవరించు Searchable Image (Exact) వెతకగలిగే చిత్రం (యథాతథం) Adds invisible text over each word; the page image is not changed ప్రతి పదంపై కనిపించని టెక్స్ట్‌ను జోడిస్తుంది; పేజీ చిత్రం మారదు Downsample to ఈ రిజల్యూషన్‌కు తగ్గించు - Text recognition isn't installed: its models are missing (run `cargo xtask models`, or set PRINTCRAFT_MODELS). టెక్స్ట్ గుర్తింపు ఇన్‌స్టాల్ చేయబడలేదు: దానికి అవసరమైన మోడల్‌లు లేవు (`cargo xtask models`ను అమలు చేయండి, లేదా PRINTCRAFT_MODELSను సెట్ చేయండి). + Text recognition isn't installed: its model files are missing. Reinstall PdfCraft, or set PDFCRAFT_MODELS to the folder that holds them. టెక్స్ట్ గుర్తింపు ఇన్‌స్టాల్ కాలేదు: దాని మోడల్ ఫైల్‌లు లేవు. PdfCraftను మళ్లీ ఇన్‌స్టాల్ చేయండి, లేదా వాటిని కలిగి ఉన్న ఫోల్డర్‌కు PDFCRAFT_MODELSను సెట్ చేయండి. Settings సెట్టింగ్‌లు Optional ఐచ్ఛికం Planned · {m} ప్రణాళికలో ఉంది · {m} @@ -1693,7 +1693,7 @@ action wizard Start ప్రారంభించు there are no pages to print ముద్రించడానికి పేజీలు ఏవీ లేవు the document has no tags పత్రంలో ట్యాగ్‌లు లేవు {0}: {1} {0}: {1} - the text recognition models are not installed (run `cargo xtask models`, or set PRINTCRAFT_MODELS) టెక్స్ట్ గుర్తింపు మోడల్‌లు ఇన్‌స్టాల్ చేయబడలేదు (`cargo xtask models` అమలు చేయండి, లేదా PRINTCRAFT_MODELS సెట్ చేయండి) + the text recognition models are not installed (reinstall PdfCraft, or set PDFCRAFT_MODELS to the folder that holds them) టెక్స్ట్ గుర్తింపు మోడల్‌లు ఇన్‌స్టాల్ చేయబడలేదు (PdfCraftను మళ్లీ ఇన్‌స్టాల్ చేయండి, లేదా వాటిని కలిగి ఉన్న ఫోల్డర్‌కు PDFCRAFT_MODELSను సెట్ చేయండి) the image is empty చిత్రం ఖాళీగా ఉంది this file isn't XFDF, FDF, XML or text form data ఈ ఫైల్ XFDF, FDF, XML లేదా టెక్స్ట్ ఫారం డేటా కాదు nothing in the file matches this document ఫైల్‌లోని ఏదీ ఈ పత్రంతో సరిపోలడం లేదు @@ -1888,7 +1888,6 @@ action wizard Start ప్రారంభించు Open this web page? ఈ వెబ్ పేజీని తెరవాలా? Scripts run in a sandbox without file or network access. A script that asks to open a web page needs your permission first. With JavaScript off, Acrobat's standard format, validate and calculate functions still work. స్క్రిప్ట్‌లు ఫైల్ లేదా నెట్‌వర్క్ యాక్సెస్ లేని శాండ్‌బాక్స్‌లో అమలవుతాయి. వెబ్ పేజీని తెరవమని అడిగే స్క్రిప్ట్‌కు ముందుగా మీ అనుమతి అవసరం. JavaScript ఆఫ్‌లో ఉన్నా, Acrobat ప్రామాణిక ఫార్మాట్, ధృవీకరణ, గణన ఫంక్షన్‌లు పని చేస్తూనే ఉంటాయి. Sign yourself స్వయంగా సంతకం చేయడం - Text recognition isn't installed: its models are missing (run `cargo xtask models`, or set PDFCRAFT_MODELS). టెక్స్ట్ గుర్తింపు ఇన్‌స్టాల్ కాలేదు: దాని మోడల్‌లు లేవు (`cargo xtask models` అమలు చేయండి, లేదా PDFCRAFT_MODELS సెట్ చేయండి). Used when opening PDFs. An explicit launch or control mode takes precedence for the session. PDFలను తెరిచేటప్పుడు ఉపయోగించబడుతుంది. ప్రారంభించేటప్పుడు లేదా కంట్రోల్ ద్వారా స్పష్టంగా ఇచ్చిన మోడ్‌కు ఆ సెషన్‌లో ప్రాధాన్యత ఉంటుంది. Write this email? ఈ ఇమెయిల్ రాయాలా? {who} in this document wants to open this address in your browser: ఈ పత్రంలోని {who} ఈ చిరునామాను మీ బ్రౌజర్‌లో తెరవాలనుకుంటోంది: diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index 080f89979..51bf057b5 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -1197,7 +1197,7 @@ Searchable Image (Exact) Зображення з пошуком (точне) Adds invisible text over each word; the page image is not changed Додає невидимий текст поверх кожного слова; зображення сторінки не змінюється Downsample to Знизити роздільну здатність до - Text recognition isn't installed: its models are missing (run `cargo xtask models`, or set PRINTCRAFT_MODELS). Розпізнавання тексту не встановлено: бракує моделей (виконайте `cargo xtask models` або задайте PRINTCRAFT_MODELS). + Text recognition isn't installed: its model files are missing. Reinstall PdfCraft, or set PDFCRAFT_MODELS to the folder that holds them. Розпізнавання тексту не встановлено: бракує файлів моделей. Перевстановіть PdfCraft або задайте в PDFCRAFT_MODELS теку, у якій вони лежать. Settings Налаштування Optional Необов’язково Planned · {m} Заплановано · {m} @@ -1763,7 +1763,7 @@ there are no pages to print немає сторінок для друку the document has no tags документ не має тегів {0}: {1} {0}: {1} - the text recognition models are not installed (run `cargo xtask models`, or set PRINTCRAFT_MODELS) моделі розпізнавання тексту не встановлено (виконайте `cargo xtask models` або задайте PRINTCRAFT_MODELS) + the text recognition models are not installed (reinstall PdfCraft, or set PDFCRAFT_MODELS to the folder that holds them) моделі розпізнавання тексту не встановлено (перевстановіть PdfCraft або задайте в PDFCRAFT_MODELS теку, у якій вони лежать) the image is empty зображення порожнє this file isn't XFDF, FDF, XML or text form data цей файл не містить даних форми XFDF, FDF, XML чи текстових даних nothing in the file matches this document ніщо у файлі не відповідає цьому документу @@ -1958,7 +1958,6 @@ Open this web page? Відкрити цю вебсторінку? Scripts run in a sandbox without file or network access. A script that asks to open a web page needs your permission first. With JavaScript off, Acrobat's standard format, validate and calculate functions still work. Сценарії виконуються в ізольованому середовищі без доступу до файлів і мережі. Сценарій, що просить відкрити вебсторінку, спочатку потребує вашого дозволу. Коли JavaScript вимкнено, стандартні функції форматування, перевірки й обчислення Acrobat усе одно працюють. Sign yourself Підписати самостійно - Text recognition isn't installed: its models are missing (run `cargo xtask models`, or set PDFCRAFT_MODELS). Розпізнавання тексту не встановлено: бракує моделей (виконайте `cargo xtask models` або задайте PDFCRAFT_MODELS). Used when opening PDFs. An explicit launch or control mode takes precedence for the session. Використовується під час відкриття PDF. Явно заданий режим запуску або керування має пріоритет для сеансу. Write this email? Написати цього листа? {who} in this document wants to open this address in your browser: {who} у цьому документі хоче відкрити цю адресу у вашому браузері: diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index 3fdbc568e..f8b9f6e28 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -906,7 +906,7 @@ Searchable Image (Exact) 可搜索图像(精确) Adds invisible text over each word; the page image is not changed 在每个词上添加不可见文本;页面图像保持不变 Downsample to 降采样至 - Text recognition isn't installed: its models are missing (run `cargo xtask models`, or set PRINTCRAFT_MODELS). 未安装文本识别:缺少模型(运行 `cargo xtask models`,或设置 PRINTCRAFT_MODELS)。 + Text recognition isn't installed: its model files are missing. Reinstall PdfCraft, or set PDFCRAFT_MODELS to the folder that holds them. 未安装文本识别:缺少模型文件。请重新安装 PdfCraft,或将 PDFCRAFT_MODELS 设置为包含这些文件的文件夹。 Settings 设置 Protect 保护 Advanced options 高级选项 @@ -1978,7 +1978,7 @@ the image is empty 图像为空 The operation failed. 操作失败。 the password is incorrect 密码不正确 - the text recognition models are not installed (run `cargo xtask models`, or set PRINTCRAFT_MODELS) 尚未安装文本识别模型(请运行 `cargo xtask models` 或设置 PRINTCRAFT_MODELS) + the text recognition models are not installed (reinstall PdfCraft, or set PDFCRAFT_MODELS to the folder that holds them) 尚未安装文本识别模型(请重新安装 PdfCraft,或将 PDFCRAFT_MODELS 设置为包含这些模型的文件夹) The title in the metadata and the document information differ 元数据与文档信息中的标题不一致 the title must not be empty 标题不能为空 there are no pages to print 没有可打印的页面 @@ -2009,7 +2009,6 @@ Scripts run in a sandbox without file or network access. A script that asks to open a web page needs your permission first. With JavaScript off, Acrobat's standard format, validate and calculate functions still work. 脚本在沙盒中运行,无法访问文件或网络。脚本请求打开网页时,需要先获得您的许可。关闭 JavaScript 后,Acrobat 的标准格式、验证和计算函数仍可使用。 Sign yourself 自行签名 Switch to two-page view first to show the cover page 请先切换到双页视图,才能显示封面页 - Text recognition isn't installed: its models are missing (run `cargo xtask models`, or set PDFCRAFT_MODELS). 尚未安装文本识别功能:缺少模型(请运行 `cargo xtask models` 或设置 PDFCRAFT_MODELS)。 Used when opening PDFs. An explicit launch or control mode takes precedence for the session. 用于打开 PDF 时。如果启动参数或控制通道明确指定了模式,则本次会话优先使用该模式。 Used when a PDF doesn't ask for a layout or zoom. Continuous scrolling never snaps between pages. PDF 未指定布局或缩放时使用。连续滚动时页面不会逐页吸附。 Reopen the files that were open when PdfCraft last closed 重新打开上次关闭 PdfCraft 时打开的文件 diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index 18fd93015..fae92447e 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -861,7 +861,7 @@ Searchable Image (Exact) 可搜尋圖片(精確) Adds invisible text over each word; the page image is not changed 在每個詞上新增不可見文字;頁面圖片保持不變 Downsample to 降取樣至 - Text recognition isn't installed: its models are missing (run `cargo xtask models`, or set PRINTCRAFT_MODELS). 未安裝文字識別:缺少模型(執行 `cargo xtask models`,或設定 PRINTCRAFT_MODELS)。 + Text recognition isn't installed: its model files are missing. Reinstall PdfCraft, or set PDFCRAFT_MODELS to the folder that holds them. 未安裝文字辨識:缺少模型檔案。請重新安裝 PdfCraft,或將 PDFCRAFT_MODELS 設定為包含這些檔案的資料夾。 Settings 設定 Protect 保護 Advanced options 進階選項 @@ -1768,7 +1768,7 @@ there are no pages to print 沒有可列印的頁面 the document has no tags 此文件沒有標籤 {0}: {1} {0}:{1} - the text recognition models are not installed (run `cargo xtask models`, or set PRINTCRAFT_MODELS) 尚未安裝文字辨識模型,請執行 `cargo xtask models` 或設定 PRINTCRAFT_MODELS + the text recognition models are not installed (reinstall PdfCraft, or set PDFCRAFT_MODELS to the folder that holds them) 尚未安裝文字辨識模型(請重新安裝 PdfCraft,或將 PDFCRAFT_MODELS 設定為包含這些模型的資料夾) the image is empty 圖片是空的 this file isn't XFDF, FDF, XML or text form data 此檔案不是 XFDF、FDF、XML 或純文字表單資料 nothing in the file matches this document 檔案中沒有與此文件相符的資料 diff --git a/crates/ui-egui/src/ocr_ui.rs b/crates/ui-egui/src/ocr_ui.rs index c8a32bbe6..f980d7cc0 100644 --- a/crates/ui-egui/src/ocr_ui.rs +++ b/crates/ui-egui/src/ocr_ui.rs @@ -113,7 +113,7 @@ pub(crate) fn body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tokens) -> (boo }); if !available { ui.label( - egui::RichText::new(tl!("Text recognition isn't installed: its models are missing (run `cargo xtask models`, or set PDFCRAFT_MODELS).")) + egui::RichText::new(tl!("Text recognition isn't installed: its model files are missing. Reinstall PdfCraft, or set PDFCRAFT_MODELS to the folder that holds them.")) .small() .color(t.text_muted), ); diff --git a/docs/releasing.md b/docs/releasing.md index 5fb8d70cf..06b1c2ce6 100644 --- a/docs/releasing.md +++ b/docs/releasing.md @@ -68,6 +68,14 @@ pinned in `release.yml` and builds with `CRAFT_FONTS_DIR` and `CRAFT_FONTS_REQUI embed its Japanese fonts and fail rather than ship without them (`AGENTS.md` §1.4). Bump the pin deliberately. +**OCR models:** every desktop package ships the Scan & OCR models (#103). The packaging scripts call +`stage_models` (`packaging/env.sh`; `package.ps1` on Windows), which runs `cargo xtask models` to +fetch every `kind = "model"` file in `ATTRIBUTION.toml` (verified by SHA-256) with its licence text +and an `ATTRIBUTION.txt`, straight into the package, so packaging needs the network. They go where +`pdfcraft_ocr::Models::dirs_beside_exe` looks: `models\` beside `pdfcraft.exe` (MSI, portable zip), +`PdfCraft.app/Contents/Resources/models` (macOS), and `share/pdfcraft/models` beside `bin/` +(deb, rpm, tar.gz, AppImage, Flatpak, FreeBSD). The web build doesn't include them yet. + ### macOS `packaging/macos/package.sh` builds both architectures, joins them with `lipo`, and assembles diff --git a/packaging/env.sh b/packaging/env.sh index a903be9c4..f34f17bd7 100644 --- a/packaging/env.sh +++ b/packaging/env.sh @@ -65,6 +65,25 @@ copy_font_licences() { done } +# Fetch the OCR models into a package directory (#103): `cargo xtask models DEST` downloads every +# ATTRIBUTION.toml `kind = "model"` file, verified by SHA-256, with its licence text and an +# ATTRIBUTION.txt crediting them. DEST must be where pdfcraft_ocr::Models::dirs_beside_exe looks +# from the installed executable. Nothing here names a model file, so a model added to the manifest +# later ships without changing the packaging. +stage_models() { + local dest="$1" + mkdir -p "$dest" + dest="$(cd "$dest" && pwd)" + (cd "$ROOT" && cargo xtask models "$dest") + rm -f "$dest"/*.part + if ! ls "$dest"/*.LICENCE.txt >/dev/null 2>&1 || [ ! -s "$dest/ATTRIBUTION.txt" ]; then + echo "error: no OCR models in $dest after cargo xtask models" >&2 + exit 1 + fi + echo "OCR models in $dest:" + ls -l "$dest" +} + # Portable SHA-256 of a file (prints just the hash). sha256() { if command -v sha256sum >/dev/null; then sha256sum "$1" | cut -d' ' -f1; else shasum -a 256 "$1" | cut -d' ' -f1; fi diff --git a/packaging/freebsd/package.sh b/packaging/freebsd/package.sh index cb357b8ba..0828058fc 100755 --- a/packaging/freebsd/package.sh +++ b/packaging/freebsd/package.sh @@ -6,7 +6,7 @@ # MIME type, AppStream metadata and icons are the freedesktop ones Linux uses. # # Usage: packaging/freebsd/package.sh [--skip-build] -# Needs: bash, a Rust toolchain, and the GUI libraries in .github/workflows/freebsd.yml. +# Needs: bash, a Rust toolchain, curl and the network (the OCR models), and the GUI libraries in .github/workflows/freebsd.yml. set -euo pipefail # shellcheck source=../env.sh . "$(dirname "${BASH_SOURCE[0]}")/../env.sh" @@ -51,6 +51,8 @@ sed -e "s/@VERSION@/$VERSION/g" -e "s/@DATE@/$PDFCRAFT_BUILD_DATE/g" \ "$LINUX/$APP_ID.metainfo.xml.in" >"$STAGE/share/metainfo/$APP_ID.metainfo.xml" cp -R "$ROOT/assets/app-icon/hicolor" "$STAGE/share/icons/" copy_docs "$STAGE/share/doc/pdfcraft" +# OCR models: the app finds them at /../share/pdfcraft/models. +stage_models "$STAGE/share/pdfcraft/models" "$STAGE/bin/pdfcraft" --version mkdir -p "$DIST" diff --git a/packaging/linux/flatpak-bundle.sh b/packaging/linux/flatpak-bundle.sh index f983fa226..4f6d31040 100755 --- a/packaging/linux/flatpak-bundle.sh +++ b/packaging/linux/flatpak-bundle.sh @@ -66,7 +66,7 @@ if [ "$TEST" = 1 ]; then flatpak install --user -y --noninteractive --reinstall "$OUT" flatpak info --user "$APP_ID" flatpak run --command=pdfcraft-cli "$APP_ID" --version - flatpak run --command=sh "$APP_ID" -c 'ls /app/share/applications /app/share/metainfo /app/share/mime/packages /app/share/icons/hicolor/scalable/apps' + flatpak run --command=sh "$APP_ID" -c 'ls /app/share/applications /app/share/metainfo /app/share/mime/packages /app/share/icons/hicolor/scalable/apps /app/share/pdfcraft/models' # Informational: printing runs lp/lpstat, which the freedesktop runtime doesn't ship (see the # manifest's finish-args). Says so if a runtime update ever adds them. flatpak run --command=sh "$APP_ID" -c 'command -v lp lpstat' || echo "note: no lp/lpstat in the runtime, so printing is unavailable in the Flatpak" diff --git a/packaging/linux/flatpak/ai.storyteller.pdfcraft.bundle.yml b/packaging/linux/flatpak/ai.storyteller.pdfcraft.bundle.yml index 05e951670..10aedeeee 100644 --- a/packaging/linux/flatpak/ai.storyteller.pdfcraft.bundle.yml +++ b/packaging/linux/flatpak/ai.storyteller.pdfcraft.bundle.yml @@ -45,6 +45,8 @@ modules: - install -Dm644 share/mime/packages/ai.storyteller.pdfcraft.xml /app/share/mime/packages/ai.storyteller.pdfcraft.xml # Already has the release's version and date substituted by package.sh. - install -Dm644 share/metainfo/ai.storyteller.pdfcraft.metainfo.xml /app/share/metainfo/ai.storyteller.pdfcraft.metainfo.xml + # OCR models (with their licences), found at /app/bin/../share/pdfcraft/models. + - mkdir -p /app/share/pdfcraft && cp -R share/pdfcraft/models /app/share/pdfcraft/ # Licences, including the embedded craft-fonts' OFL texts. - mkdir -p /app/share/icons /app/share/doc && cp -R share/icons/hicolor /app/share/icons/ && cp -R share/doc/pdfcraft /app/share/doc/ sources: diff --git a/packaging/linux/package.sh b/packaging/linux/package.sh index 06b04a9e2..129aaea9e 100755 --- a/packaging/linux/package.sh +++ b/packaging/linux/package.sh @@ -10,7 +10,7 @@ # # Usage: packaging/linux/package.sh [--skip-build] [--formats "appimage deb rpm tar cli"] # -# Needs: cargo; nfpm for deb/rpm (https://nfpm.goreleaser.com); appimagetool for the AppImage +# Needs: cargo, curl and the network (the OCR models: cargo xtask models); nfpm for deb/rpm (https://nfpm.goreleaser.com); appimagetool for the AppImage # (downloaded into $CARGO_TARGET_DIR if missing). Build on an old distro (CI: Ubuntu 22.04, # glibc 2.35) so the binaries run on newer ones. Optional: desktop-file-validate, appstreamcli, # zsyncmake (the zsync package) for the AppImage's .zsync. @@ -63,6 +63,8 @@ mkdir -p "$STAGE/usr/share/icons" cp -R "$ROOT/assets/app-icon/hicolor" "$STAGE/usr/share/icons/" mkdir -p "$STAGE/usr/share/doc/pdfcraft" copy_docs "$STAGE/usr/share/doc/pdfcraft" +# OCR models: the app finds them at /../share/pdfcraft/models, so in every format below. +stage_models "$STAGE/usr/share/pdfcraft/models" if command -v desktop-file-validate >/dev/null; then desktop-file-validate "$STAGE/usr/share/applications/$APP_ID.desktop" diff --git a/packaging/macos/package.sh b/packaging/macos/package.sh index 507e911d8..c73581a24 100755 --- a/packaging/macos/package.sh +++ b/packaging/macos/package.sh @@ -104,6 +104,9 @@ mkdir -p "$APP/Contents/MacOS" "$APP/Contents/Resources" cp "$WORK/bin/pdfcraft" "$APP/Contents/MacOS/PdfCraft" cp "$ROOT/assets/app-icon/pdfcraft.icns" "$APP/Contents/Resources/PdfCraft.icns" copy_font_licences "$APP/Contents/Resources" +# OCR models: the app finds them at Contents/MacOS/../Resources/models. Before signing, so the +# bundle's seal covers them. +stage_models "$APP/Contents/Resources/models" sed -e "s/@VERSION@/$VERSION/g" -e "s/@SHORT_VERSION@/$SHORT_VERSION/g" \ -e "s/@BUILD_SHA@/${PDFCRAFT_BUILD_SHA:-unknown}/g" \ "$HERE/Info.plist.in" >"$APP/Contents/Info.plist" diff --git a/packaging/windows/package.ps1 b/packaging/windows/package.ps1 index 14588668d..c556652d9 100644 --- a/packaging/windows/package.ps1 +++ b/packaging/windows/package.ps1 @@ -5,13 +5,13 @@ .DESCRIPTION Produces, in $env:DIST (default: dist/release): pdfcraft--windows-.msi per-machine installer (WiX v5) - pdfcraft--windows--portable.zip pdfcraft.exe + pdfcraft-cli.exe + portable.txt + pdfcraft--windows--portable.zip pdfcraft.exe + pdfcraft-cli.exe + models\ + portable.txt The binaries link the C runtime statically (+crt-static), so neither the MSI nor the portable zip needs the Visual C++ redistributable. Signing is delegated to sign.ps1 (skipped with a warning when no signing secrets are set). - Needs: Rust (MSVC toolchain + the target), the Windows SDK (rc.exe, signtool.exe), + Needs: Rust (MSVC toolchain + the target), curl and the network (the OCR models), the Windows SDK (rc.exe, signtool.exe), and WiX v5: dotnet tool install --global wix --version 5.0.2 .EXAMPLE @@ -90,12 +90,24 @@ Copy-Item (Join-Path $Bin 'pdfcraft.exe'), (Join-Path $Bin 'pdfcraft-cli.exe') $ & (Join-Path $PSScriptRoot 'sign.ps1') (Join-Path $Stage 'pdfcraft.exe') (Join-Path $Stage 'pdfcraft-cli.exe') +# OCR models (#103): the app looks in models\ beside pdfcraft.exe. `cargo xtask models` fetches every +# ATTRIBUTION.toml `kind = "model"` file (verified by SHA-256) with its licence and ATTRIBUTION.txt; +# the MSI and the portable zip take the whole folder, so new models ship without naming them here. +$Models = Join-Path $Stage 'models' +New-Item -ItemType Directory -Force -Path $Models | Out-Null +Push-Location $Root +try { Invoke-Native 'cargo xtask models' { cargo xtask models $Models } } finally { Pop-Location } +Remove-Item -Force (Join-Path $Models '*.part') -ErrorAction SilentlyContinue +if (-not (Test-Path (Join-Path $Models 'ATTRIBUTION.txt')) -or -not (Get-ChildItem (Join-Path $Models '*.LICENCE.txt'))) { + throw "no OCR models in $Models after cargo xtask models" +} + # ---- MSI --------------------------------------------------------------------------------------- $Msi = Join-Path $Dist "pdfcraft-$Version-windows-$Arch.msi" Invoke-Native 'wix build' { wix build (Join-Path $PSScriptRoot 'pdfcraft.wxs') -arch $Arch ` (Join-Path $PSScriptRoot 'installer-ui.wxs') ` - -d "Version=$MsiVersion" -d "BinDir=$Stage" -d "IconPath=$(Join-Path $Root 'assets\app-icon\pdfcraft.ico')" ` + -d "Version=$MsiVersion" -d "BinDir=$Stage" -d "ModelsDir=$Models" -d "IconPath=$(Join-Path $Root 'assets\app-icon\pdfcraft.ico')" ` -o $Msi } # Inspect the built MSI, not just the XML, before signing/publishing it. In a child process, so @@ -110,6 +122,7 @@ $Portable = Join-Path $TargetDir "windows-package\pdfcraft-$Version-windows-$Arc Remove-Item -Recurse -Force $Portable -ErrorAction SilentlyContinue New-Item -ItemType Directory -Force -Path $Portable | Out-Null Copy-Item (Join-Path $Stage '*.exe') $Portable +Copy-Item -Recurse $Models (Join-Path $Portable 'models') foreach ($f in 'README.md', 'LICENSE', 'LICENSE-MIT', 'LICENSE-APACHE') { $p = Join-Path $Root $f if (Test-Path $p) { Copy-Item $p $Portable } diff --git a/packaging/windows/pdfcraft.wxs b/packaging/windows/pdfcraft.wxs index cd0480fac..200800af6 100644 --- a/packaging/windows/pdfcraft.wxs +++ b/packaging/windows/pdfcraft.wxs @@ -2,7 +2,7 @@ + + + diff --git a/packaging/windows/test-msi.ps1 b/packaging/windows/test-msi.ps1 index 29191c40d..9c0d50b57 100644 --- a/packaging/windows/test-msi.ps1 +++ b/packaging/windows/test-msi.ps1 @@ -1,6 +1,6 @@ <# .SYNOPSIS - Check the compiled MSI's install scope, publisher, shortcuts and native UI, without installing it. + Check the compiled MSI's install scope, publisher, shortcuts, native UI and OCR models, without installing it. .EXAMPLE pwsh packaging/windows/test-msi.ps1 dist/release/pdfcraft-0.2.1-windows-x64.msi #> @@ -131,8 +131,20 @@ foreach ($exit in @(@('InstallComplete', '-1'), @('InstallCancelled', '-2'), @(' } $title = Read-Row 'SELECT `Text` FROM `Control` WHERE `Dialog_` = ''InstallComplete'' AND `Control` = ''Title''' 1 if ($title[0] -notmatch 'completed successfully') { throw 'Success dialog does not confirm completion' } +# OCR models (#103): installed into models\ beside pdfcraft.exe, where the app looks for them. +$modelsDir = Read-Row 'SELECT `Directory_Parent`, `DefaultDir` FROM `Directory` WHERE `Directory` = ''ModelsFolder''' 2 +Assert-Equal $modelsDir[0] 'INSTALLFOLDER' 'OCR models folder parent' +# DefaultDir is `models`, or `SHORT|models` with a generated 8.3 name. +if ($modelsDir[1] -notmatch '(^|\|)models$') { throw "OCR models folder name: '$($modelsDir[1])'" } +$modelFiles = 0 +$view = $Database.OpenView('SELECT `File`.`FileName` FROM `File`, `Component` WHERE `File`.`Component_` = `Component`.`Component` AND `Component`.`Directory_` = ''ModelsFolder''') +try { + [void] $view.Execute() + while ($record = $view.Fetch()) { if ($record.StringData(1) -match '\.rten$') { $modelFiles++ } } +} finally { [void] $view.Close() } +if ($modelFiles -lt 2) { throw "expected the OCR models (*.rten) in models\, found $modelFiles" } $rm = Read-Row 'SELECT `Dialog` FROM `Dialog` WHERE `Dialog` = ''MsiRMFilesInUse''' 1 Assert-Equal $rm[0] 'MsiRMFilesInUse' 'Files-in-use dialog' [void] [Runtime.InteropServices.Marshal]::FinalReleaseComObject($Database) [void] [Runtime.InteropServices.Marshal]::FinalReleaseComObject($Installer) -Write-Output 'ok MSI: per-machine scope guard, publisher, persistent progress text, Start Menu shortcut, optional desktop shortcut (default on, checkbox), icon/key path, full-UI success/cancel/error and Finish controls, files-in-use dialog' +Write-Output 'ok MSI: per-machine scope guard, publisher, persistent progress text, Start Menu shortcut, optional desktop shortcut (default on, checkbox), icon/key path, full-UI success/cancel/error and Finish controls, files-in-use dialog, OCR models' diff --git a/parity/acrobat-features.toml b/parity/acrobat-features.toml index ba32039e8..42aebb07f 100644 --- a/parity/acrobat-features.toml +++ b/parity/acrobat-features.toml @@ -6367,7 +6367,7 @@ status = "shipped" commands = ["ocr.recognize"] tools = ["ocr_recognize", "ocr_status"] evidence = ["crates/engine/src/tests.rs::recognize_text_makes_a_scanned_page_searchable", "crates/automation/tests/automation.rs::ocr_tools_make_a_scan_searchable", "crates/ui-egui/tests/ocr.rs::recognize_text_dialog_adds_searchable_text"] -notes = "ocrs engine (MIT/Apache) with CC-BY-SA models fetched by `cargo xtask models`; runs on a worker thread with progress; one undoable step; pages with text are skipped." +notes = "ocrs engine (MIT/Apache) with CC-BY-SA models fetched by `cargo xtask models`; the desktop release packages ship them (#103; packaging/env.sh stage_models), the web build does not yet; runs on a worker thread with progress; one undoable step; pages with text are skipped." [[feature]] id = "ocr.recognize-range" diff --git a/xtask/src/assets.rs b/xtask/src/assets.rs index 43a0af7b2..870d9b953 100644 --- a/xtask/src/assets.rs +++ b/xtask/src/assets.rs @@ -336,7 +336,8 @@ fn fetch_verified(url: &str, path: &Path, sha256: &str) -> Result<()> { } /// `cargo xtask models [DIR]`: fetch the OCR models (`kind = "model"`) into `assets/models/` (git-ignored), -/// where the app and tests look for them. +/// where the app and tests look for them. Each comes with its licence (`.LICENCE.txt`), and +/// `ATTRIBUTION.txt` credits them all, so the release packages can ship the directory as it is. pub fn models(args: &[String]) -> Result<()> { let root = root(); let m = load(&root)?; @@ -344,9 +345,23 @@ pub fn models(args: &[String]) -> Result<()> { for p in fetch_all(&m, &dir, "model")? { println!("models: {}", p.display()); } + std::fs::write(dir.join("ATTRIBUTION.txt"), models_attribution(&m))?; Ok(()) } +/// The credits shipped beside the models (their licences require attribution). +fn models_attribution(m: &Manifest) -> String { + let mut s = String::from("PdfCraft's text recognition (Scan & OCR) uses these models, unmodified.\n"); + for f in m.fetched.iter().filter(|f| f.kind == "model") { + let _ = write!( + s, + "\n{}\n {}\n by {}\n from {}\n licence: {} ({}; full text in {}.LICENCE.txt)\n", + f.file, f.title, f.author, f.source, f.licence, f.licence_url, f.file + ); + } + s +} + /// ATTRIBUTION.md, generated from the manifest. pub fn render_markdown(m: &Manifest) -> String { let mut s = String::new(); @@ -417,6 +432,18 @@ pub fn render_markdown(m: &Manifest) -> String { mod tests { use super::*; + /// The release packages ship assets/models as fetched (#103): every model is credited there. + #[test] + fn models_attribution_credits_every_model() { + let m = load(&root()).unwrap(); + let text = models_attribution(&m); + let models: Vec<_> = m.fetched.iter().filter(|f| f.kind == "model").collect(); + assert!(!models.is_empty()); + for f in models { + assert!(text.contains(&f.file) && text.contains(&f.author) && text.contains(&f.licence), "{}", f.file); + } + } + fn asset(path: &str, author: &str, kind: &str, licence: &str) -> Asset { Asset { path: path.into(),