mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-13 23:17:40 +00:00
* Issue #432: global Units preference (mm, cm, in, pt), defaulting to the system region Preferences ▸ Documents and view ▸ Units sets the unit for page sizes (Document Properties, Print), Header & Footer margins, the start of Set Page Boxes and the Measure tool on pages without a viewport scale. On first start it follows the system region: AppleMeasurementUnits on macOS, Region ▸ Measurement system (iMeasure) on Windows, then LC_ALL / LC_MEASUREMENT / LANG; inches for US, LR and MM, millimetres elsewhere. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Issue #432: macOS falls back to AppleLocale when the measurement system was never set AppleMeasurementUnits only exists in the global domain once someone changed Measurement system in System Settings, and apps started from the Finder get no LANG, so a German Mac with untouched settings landed on inches. The region of AppleLocale (always present) decides next; AppleMeasurementUnits still wins. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01REz7wTfQgnopyvN9eQJJVu * Issue #432: Units labels in the Arabic and Italian catalogs new on main Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01REz7wTfQgnopyvN9eQJJVu * Issue #432: the Print dialog's PDF page size follows the Units preference Main's new "PDF page: {w} × {h} in" label was always in inches. It now uses the preferred unit like the sheet size next to it; each catalog keeps its wording with the size in place of the inch values. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Tristan von Kries <tristan@MacBook-Pro-von-Tristan.local> Co-authored-by: Brandon Thomas <echelon@gmail.com>
This commit is contained in:
28 files changed
+291
-74
No files matched your search
+15
-15
@@ -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 = "e6a4d0363f5c146aa030e11d039da5ce62bcf92bbbb6c19d36a14a166d899daa"
|
||||
sha256 = "b12eb7c9c8e540b2c1294966f4cb8c885d32763204f8fdb4d0963d275c282a10"
|
||||
|
||||
[[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 = "2f807f925c462447b2c31edac51cd02e0fa3f4a65211db6e9545d9259d8401e1"
|
||||
sha256 = "6c8604d5cd71412c6a673b75001c7ef629727839eca1449147c7d6ec4bf0be99"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/cs.tsv"
|
||||
@@ -41,7 +41,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 = "bda5289ace1c4c959f0b4fc2482cb8ae3b4198733994b05042a584ece827dd3a"
|
||||
sha256 = "18f725656c3b560b3b84b908d017a8ccc1b01670cfeea4655d21fd3b41bc59a4"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/pt-br.tsv"
|
||||
@@ -52,7 +52,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 = "66f180b1b297afa69030d5bac14d645f5e1faa2ef22f6d19dc0d3e8f78c47162"
|
||||
sha256 = "c3bc62f928891ee565f2b53a29e4efe6d7f4f83f44cd62b74227a17171b8d82c"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/es.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 = "892d64bb142fd3b0cba1212ff38669388c5fd4631f7ade07fede78bf9bf38abd"
|
||||
sha256 = "09dec62d351c0bdfd33358b15f7d03a55ad6940571741def05e8ed899c4097d6"
|
||||
|
||||
[[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 = "a061b5ae287696bee00be6897a79e01c2a00e244ada2cc7471b8bbf839ee8da1"
|
||||
sha256 = "8956e77078a2dd55ee7fb9fe74d178753154bd4ea2ef98fecba8ed7553411c73"
|
||||
|
||||
[[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 = "d82f65eb81ff866ee637a113dba6ff2929e5ee89abfcac233e44983aa231dd62"
|
||||
sha256 = "4aa8784d99113ff58876e34469d6d2edadb6f226c748b153a11c97fac0eb5742"
|
||||
|
||||
[[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 = "c93373a64b31c7df333d45dd6d734f0a77f6a2daf64a2827c225034eecff997d"
|
||||
sha256 = "656940b456a6c2401f8bd3107a71cedcae5e52cd446e86e806285b6322d8ed8e"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/it.tsv"
|
||||
@@ -107,7 +107,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 = "996dbfee4fe322eef73f1e7e419d5bb57289508d05bd77d0a9d0316c9659985e"
|
||||
sha256 = "5c6da9eb74460a7ba67fa53866760644f258054a05cef7df4aedd60cd51928c5"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/pl.tsv"
|
||||
@@ -129,7 +129,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Russian UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "0492e7f97f8ca9d73bfe66e6f115a8323cb0c8548680b2d30a36025d820682f1"
|
||||
sha256 = "25fd7dc03149033b02c4e655da84380dd0d90c41df556183c98285f056db4c82"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/hu.tsv"
|
||||
@@ -140,7 +140,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 = "1c8219612b4acd797ea6beffdb04486a0120adcaa88ab327de1a4a165bf4386a"
|
||||
sha256 = "4045450381525f0acb0a3895879cd369f0c4de7a502790e8a4609c3333408931"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/bg.tsv"
|
||||
@@ -151,7 +151,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 = "53acc28351fb74296d9d01b863ff2e20ef93d990ba5f5ee3c00ba8370b9c7a13"
|
||||
sha256 = "1c747fb3a4cd1e66dd2868349d8df02385c1b825928bfca01b5059c7de9bdaba"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/ja.tsv"
|
||||
@@ -162,7 +162,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 = "253e22babcf4c3d03fa39b520c99be0aba14c785893472994f91aac0fa457533"
|
||||
sha256 = "40919a7582812d7231cd2c0cc712b57f066b8e1af6fc43281ec8ce6df4f60cca"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/uk.tsv"
|
||||
@@ -173,7 +173,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 = "08a9d93236c4ad47abb8df65042eeb2aa39935ddc40a53d32d00ab58fe5122b0"
|
||||
sha256 = "f61852f58a28ae3d8a3a59219b91ef79842c7bc7b01ea451aca6110f54939755"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/ar.tsv"
|
||||
@@ -184,7 +184,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 = "984e965465bc3e197b130d530abe3ec2e42395fbe67ea4a88f5647ae83d67635"
|
||||
sha256 = "0c0a492b757d3a5c488b2ef4e48386bf7528083c85805bcfd0e7e06fe4e9092f"
|
||||
|
||||
[[asset]]
|
||||
path = "assets/icons/accessibility.svg"
|
||||
|
||||
@@ -301,6 +301,8 @@ fn app_creator<'a>(
|
||||
Box::new(move |cc| {
|
||||
started.set(true);
|
||||
let mut app = PdfCraftApp::new();
|
||||
// First start (or settings from before Units existed): the region's unit.
|
||||
app.units = pdfcraft_ui_egui::units::Unit::system();
|
||||
#[cfg(target_os = "linux")]
|
||||
app.enable_desktop_theme();
|
||||
// The system's reopen-windows choice (macOS) starts the preference; a saved choice replaces it.
|
||||
|
||||
@@ -337,6 +337,12 @@ impl Document {
|
||||
let e = self.editor.as_ref().ok_or("the document can't be read")?;
|
||||
measure::scale_at(&e.cos, page, at).map_err(|e| e.to_string())
|
||||
}
|
||||
/// [`Self::measurement_scale`], measuring true size in `unit` (`per_point` of them in a
|
||||
/// point) where the page sets no scale.
|
||||
pub fn measurement_scale_or(&self, page: usize, at: measure::Point, per_point: f64, unit: &str) -> Result<measure::Scale, String> {
|
||||
let e = self.editor.as_ref().ok_or("the document can't be read")?;
|
||||
measure::scale_at_or(&e.cos, page, at, per_point, unit).map_err(|e| e.to_string())
|
||||
}
|
||||
pub fn measurement_to_user(&self, page: usize, point: measure::Point) -> Result<measure::Point, String> {
|
||||
let e = self.editor.as_ref().ok_or("the document can't be read")?;
|
||||
measure::view_to_user(&e.cos, page, point).map_err(|e| e.to_string())
|
||||
|
||||
@@ -447,7 +447,13 @@ pub fn list(doc: &Document) -> Listing {
|
||||
out
|
||||
}
|
||||
/// Last containing viewport wins, using the first point of the measurement (ISO 32000-2 §12.9).
|
||||
/// Outside every viewport the page measures true size in inches.
|
||||
pub fn scale_at(doc: &Document, page_index: usize, at: Point) -> Result<Scale> {
|
||||
scale_at_or(doc, page_index, at, 1.0 / 72.0, "in")
|
||||
}
|
||||
/// [`scale_at`], but outside every viewport the page measures true size in `unit`, of which there
|
||||
/// are `per_point` in a PDF point (25.4 / 72 for millimetres).
|
||||
pub fn scale_at_or(doc: &Document, page_index: usize, at: Point, per_point: f64, unit: &str) -> Result<Scale> {
|
||||
check_points(&[at])?;
|
||||
let p = page(doc, page_index)?;
|
||||
if let Some(o) = p.dict.get(b"VP") {
|
||||
@@ -470,7 +476,7 @@ pub fn scale_at(doc: &Document, page_index: usize, at: Point) -> Result<Scale> {
|
||||
}
|
||||
}
|
||||
}
|
||||
Scale::new(p.user_unit(doc) / 72.0, "in", 2)
|
||||
Scale::new(p.user_unit(doc) * per_point, unit, 2)
|
||||
}
|
||||
/// Add a named rectangular viewport. Existing viewports are preserved in drawing order.
|
||||
pub fn set_scale(doc: &mut Document, page_index: usize, bbox: [f64; 4], name: &str, scale: &Scale) -> Result<()> {
|
||||
|
||||
@@ -2166,6 +2166,7 @@ pub fn document_area(app: &mut PdfCraftApp, index: usize, ui: &mut egui::Ui) {
|
||||
| QuickTool::Snapshot => false,
|
||||
};
|
||||
let prefs = &app.comment_prefs;
|
||||
let units = app.units;
|
||||
let allowed = doc.allows_annotation();
|
||||
let comments_hidden = doc.comments_hidden();
|
||||
let form = doc.form.clone();
|
||||
@@ -2391,7 +2392,7 @@ pub fn document_area(app: &mut PdfCraftApp, index: usize, ui: &mut egui::Ui) {
|
||||
// Comments: tools, selection, moving and resizing come before text selection.
|
||||
let pcx = comments::PageCx { page: i, xf: &xf, info, tool, prefs, allowed, hidden: comments_hidden };
|
||||
if let QuickTool::Measure(measure_tool) = tool {
|
||||
crate::measure_ui::page_input(ui, &resp, doc, view, &pcx, measure_tool);
|
||||
crate::measure_ui::page_input(ui, &resp, doc, view, &pcx, measure_tool, units);
|
||||
}
|
||||
|
||||
// Form fields take clicks first with the Select tool (as Acrobat fills fields in
|
||||
|
||||
@@ -108,6 +108,7 @@ fn dialogs(app: &mut PdfCraftApp, ctx: &egui::Context) {
|
||||
}
|
||||
});
|
||||
ui.separator();
|
||||
let units = app.units;
|
||||
let Some((_, id)) = app.active_ids() else { return };
|
||||
let Some(doc) = app.session.get(id) else { return };
|
||||
let i = &doc.info;
|
||||
@@ -336,7 +337,7 @@ fn dialogs(app: &mut PdfCraftApp, ctx: &egui::Context) {
|
||||
row(ui, "Location", crate::bidi::visual(doc.path.as_deref().unwrap_or_default()).into_owned());
|
||||
row(ui, "File size", format!("{} ({} bytes)", human_size(i.file_size), i.file_size));
|
||||
let p = &i.pages[0];
|
||||
row(ui, "Page size", format!("{:.2} × {:.2} in", p.width / 72.0, p.height / 72.0));
|
||||
row(ui, "Page size", units.size(f64::from(p.width), f64::from(p.height)));
|
||||
row(ui, "Number of pages", i.pages.len().to_string());
|
||||
row(ui, "Tagged PDF", yes(i.tagged));
|
||||
row(ui, "Form fields", i.fields.len().to_string());
|
||||
|
||||
@@ -2193,6 +2193,8 @@ certificate Subject صاحب الشهادة
|
||||
A selected field has no widget on any page, so the fields can't be edited together. أحد الحقول المحددة ليس له عنصر واجهة في أي صفحة، لذا لا يمكن تحرير الحقول معًا.
|
||||
Text recognition isn't installed: its models are missing (run `cargo xtask models`, or set PDFCRAFT_MODELS). التعرف على النص غير مثبت: نماذجه مفقودة (شغّل `cargo xtask models`، أو اضبط PDFCRAFT_MODELS).
|
||||
({n} at {dpi} dpi, the largest size they can be drawn at) ({n} بدقة {dpi} dpi، وهي أكبر دقة يمكن رسمها بها)
|
||||
Margins الهوامش
|
||||
For page sizes and margins. لأحجام الصفحات والهوامش.
|
||||
|
||||
# Multi-object selection and movement (#844)
|
||||
@plural {n} object selected|{n} objects selected تم تحديد {n} عنصر|تم تحديد {n} عنصر|تم تحديد {n} عنصرين|تم تحديد {n} عناصر|تم تحديد {n} عنصرًا|تم تحديد {n} عنصر
|
||||
@@ -2239,7 +2241,7 @@ key Space مسافة
|
||||
Resize panel window تغيير حجم نافذة اللوحة
|
||||
|
||||
# Architectural print controls (follow-up to #599).
|
||||
PDF page: {w} × {h} in صفحة PDF: {w} × {h} بوصة
|
||||
PDF page: {size} صفحة PDF: {size}
|
||||
Match page at 100% مطابقة الورق للصفحة بنسبة 100%
|
||||
PDF scale: {scale}% مقياس PDF: {scale}%
|
||||
Printer paper and scaling may differ. Check driver settings. قد يختلف ورق الطابعة ومقياس الطباعة. تحقق من إعدادات برنامج التشغيل.
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -2110,6 +2110,8 @@ action wizard Start Старт
|
||||
Printer properties Свойства на принтера
|
||||
Reset to printer defaults Възстанови настройките по подразбиране на принтера
|
||||
This printer offers no settings of its own. Този принтер няма собствени настройки.
|
||||
Margins Полета
|
||||
For page sizes and margins. За размери на страници и полета.
|
||||
Scroll tabs left Превърти разделите наляво
|
||||
Scroll tabs right Превърти разделите надясно
|
||||
|
||||
@@ -2181,7 +2183,7 @@ key Space Интервал
|
||||
Inspector Инспектор
|
||||
|
||||
# Architectural print controls (follow-up to #599).
|
||||
PDF page: {w} × {h} in PDF страница: {w} × {h} инча
|
||||
PDF page: {size} PDF страница: {size}
|
||||
Match page at 100% Хартия по страницата, мащаб 100%
|
||||
PDF scale: {scale}% Мащаб на PDF: {scale}%
|
||||
Printer paper and scaling may differ. Check driver settings. Хартията и мащабът на принтера може да се различават. Проверете настройките на драйвера.
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 8 and column 16.
|
@@ -130,6 +130,8 @@
|
||||
Portable mode is off: {folder} can't be written ({error}). Settings are kept in your user folder instead. Přenosný režim je vypnutý: do složky {folder} nelze zapisovat ({error}). Nastavení se místo toho ukládá do vaší uživatelské složky.
|
||||
PdfCraft is drawing without a graphics processor, so it may be slow. Updating the graphics driver may help. PdfCraft vykresluje bez grafického procesoru, takže může být pomalý. Pomoci může aktualizace grafického ovladače.
|
||||
Go to page… Přejít na stránku…
|
||||
Margins Okraje
|
||||
For page sizes and margins. Pro velikosti stránek a okraje.
|
||||
Open arrow Otevřená šipka
|
||||
Closed arrow Zavřená šipka
|
||||
Butt Tupý konec
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 7 and column 81.
|
@@ -2152,6 +2152,8 @@ action wizard Start Starten
|
||||
Printer properties Druckereigenschaften
|
||||
Reset to printer defaults Auf Druckerstandard zurücksetzen
|
||||
This printer offers no settings of its own. Dieser Drucker bietet keine eigenen Einstellungen.
|
||||
Margins Ränder
|
||||
For page sizes and margins. Für Seitengrößen und Ränder.
|
||||
Scroll tabs left Tabs nach links scrollen
|
||||
Scroll tabs right Tabs nach rechts scrollen
|
||||
|
||||
@@ -2223,7 +2225,7 @@ key Space Leertaste
|
||||
Inspector Inspektor
|
||||
|
||||
# Architectural print controls (follow-up to #599).
|
||||
PDF page: {w} × {h} in PDF-Seite: {w} × {h} Zoll
|
||||
PDF page: {size} PDF-Seite: {size}
|
||||
Match page at 100% Papier an Seite anpassen, 100%
|
||||
PDF scale: {scale}% PDF-Maßstab: {scale}%
|
||||
Printer paper and scaling may differ. Check driver settings. Druckerpapier und Skalierung können abweichen. Treibereinstellungen prüfen.
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 51.
|
@@ -770,7 +770,7 @@
|
||||
{verb} Header and Footer {verb} encabezado y pie de página
|
||||
{verb} Watermark {verb} marca de agua
|
||||
{verb} Background {verb} fondo
|
||||
Margins (in) Márgenes (pulgadas)
|
||||
Margins Márgenes
|
||||
Left Header Text Texto del encabezado izquierdo
|
||||
Center Header Text Texto del encabezado central
|
||||
Right Header Text Texto del encabezado derecho
|
||||
@@ -2044,6 +2044,7 @@
|
||||
Choose a PNG or JPEG image (up to 4 MiB). Transparency is preserved. Elija una imagen PNG o JPEG (hasta 4 MiB). Se conserva la transparencia.
|
||||
Couldn't import the signature image: {e} No se pudo importar la imagen de la firma: {e}
|
||||
Go to page… Ir a la página…
|
||||
For page sizes and margins. Para tamaños de página y márgenes.
|
||||
The standard fonts can't draw “{c}” ({code}). Only Western European characters can be added as text for now. Las fuentes estándar no pueden mostrar «{c}» ({code}). Por ahora solo se pueden añadir como texto caracteres de Europa occidental.
|
||||
Open arrow Flecha abierta
|
||||
Closed arrow Flecha cerrada
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 60.
|
@@ -749,7 +749,7 @@
|
||||
{verb} Header and Footer {verb} l'en-tête et le pied de page
|
||||
{verb} Watermark {verb} le filigrane
|
||||
{verb} Background {verb} l'arrière-plan
|
||||
Margins (in) Marges (po)
|
||||
Margins Marges
|
||||
Left Header Text Texte de l'en-tête gauche
|
||||
Center Header Text Texte de l'en-tête central
|
||||
Right Header Text Texte de l'en-tête droit
|
||||
@@ -2088,6 +2088,7 @@
|
||||
Pinned {folder} to Home {folder} épinglé à l'Accueil
|
||||
No recent files Aucun fichier récent
|
||||
Go to page… Aller à la page…
|
||||
For page sizes and margins. Pour les tailles de page et les marges.
|
||||
The standard fonts can't draw “{c}” ({code}). Only Western European characters can be added as text for now. Les polices standard ne peuvent pas afficher « {c} » ({code}). Pour l'instant, seuls les caractères d'Europe occidentale peuvent être ajoutés comme texte.
|
||||
Larger pages Pages plus grandes
|
||||
Smaller pages Pages plus petites
|
||||
@@ -2223,7 +2224,7 @@ key Space Espace
|
||||
Inspector Inspecteur
|
||||
|
||||
# Architectural print controls (follow-up to #599).
|
||||
PDF page: {w} × {h} in Page PDF : {w} × {h} po
|
||||
PDF page: {size} Page PDF : {size}
|
||||
Match page at 100% Adapter le papier à la page à 100 %
|
||||
PDF scale: {scale}% Échelle PDF : {scale}%
|
||||
Printer paper and scaling may differ. Check driver settings. Le papier et l’échelle de l’imprimante peuvent différer. Vérifiez les réglages du pilote.
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 39.
|
@@ -2146,6 +2146,8 @@ action wizard Start Indítás
|
||||
Printer properties Nyomtató tulajdonságai
|
||||
Reset to printer defaults A nyomtató alapértékeinek visszaállítása
|
||||
This printer offers no settings of its own. Ennek a nyomtatónak nincsenek saját beállításai.
|
||||
Margins Margók
|
||||
For page sizes and margins. Oldalméretekhez és margókhoz.
|
||||
Scroll tabs left Lapok görgetése balra
|
||||
Scroll tabs right Lapok görgetése jobbra
|
||||
|
||||
@@ -2217,7 +2219,7 @@ key Space Szóköz
|
||||
Inspector Tulajdonságvizsgáló
|
||||
|
||||
# Architectural print controls (follow-up to #599).
|
||||
PDF page: {w} × {h} in PDF-oldal: {w} × {h} hüvelyk
|
||||
PDF page: {size} PDF-oldal: {size}
|
||||
Match page at 100% Papír illesztése az oldalhoz, 100%
|
||||
PDF scale: {scale}% PDF-méretarány: {scale}%
|
||||
Printer paper and scaling may differ. Check driver settings. A nyomtató papírja és méretezése eltérhet. Ellenőrizze az illesztőprogram beállításait.
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 7 and column 44.
|
@@ -2180,6 +2180,8 @@ signature pad Type Digita
|
||||
The field {name} no longer exists. Il campo {name} non esiste più.
|
||||
A selected field has no widget on any page, so the fields can't be edited together. Un campo selezionato non ha widget in nessuna pagina, quindi i campi non possono essere modificati insieme.
|
||||
({n} at {dpi} dpi, the largest size they can be drawn at) ({n} a {dpi} dpi, la dimensione massima a cui si possono disegnare)
|
||||
Margins Margini
|
||||
For page sizes and margins. Per formati pagina e margini.
|
||||
|
||||
# Multi-object selection and movement (#844)
|
||||
@plural {n} object selected|{n} objects selected {n} oggetto selezionato|{n} oggetti selezionati
|
||||
@@ -2226,7 +2228,7 @@ key Space Spazio
|
||||
Resize panel window Ridimensiona finestra pannello
|
||||
|
||||
# Architectural print controls (follow-up to #599).
|
||||
PDF page: {w} × {h} in Pagina PDF: {w} × {h} pollici
|
||||
PDF page: {size} Pagina PDF: {size}
|
||||
Match page at 100% Adatta il foglio alla pagina al 100%
|
||||
PDF scale: {scale}% Scala PDF: {scale}%
|
||||
Printer paper and scaling may differ. Check driver settings. Carta e scala della stampante possono differire. Controlla le impostazioni del driver.
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -776,7 +776,7 @@
|
||||
{verb} Header and Footer ヘッダーとフッターを{verb}
|
||||
{verb} Watermark 透かしを{verb}
|
||||
{verb} Background 背景を{verb}
|
||||
Margins (in) 余白(インチ)
|
||||
Margins 余白
|
||||
Left Header Text 左ヘッダーのテキスト
|
||||
Center Header Text 中央ヘッダーのテキスト
|
||||
Right Header Text 右ヘッダーのテキスト
|
||||
@@ -2017,6 +2017,7 @@
|
||||
Choose a PNG or JPEG image (up to 4 MiB). Transparency is preserved. PNG または JPEG 画像を選択してください(最大 4 MiB)。透明度は保持されます。
|
||||
Couldn't import the signature image: {e} 署名画像を読み込めませんでした: {e}
|
||||
Go to page… ページへ移動…
|
||||
For page sizes and margins. ページサイズと余白に使います。
|
||||
Reopen the files that were open when PdfCraft last closed 前回 PdfCraft を閉じたときに開いていたファイルを再度開く
|
||||
The standard fonts can't draw “{c}” ({code}). Only Western European characters can be added as text for now. 標準フォントでは「{c}」({code})を表示できません。現在、テキストとして追加できるのは西ヨーロッパの文字のみです。
|
||||
Open arrow 開いた矢印
|
||||
@@ -2219,7 +2220,7 @@ key Space スペース
|
||||
Inspector インスペクター
|
||||
|
||||
# Architectural print controls (follow-up to #599).
|
||||
PDF page: {w} × {h} in PDFページ: {w} × {h} インチ
|
||||
PDF page: {size} PDFページ: {size}
|
||||
Match page at 100% ページに合わせて100%に設定
|
||||
PDF scale: {scale}% PDFの倍率: {scale}%
|
||||
Printer paper and scaling may differ. Check driver settings. プリンターの用紙や倍率は異なる場合があります。ドライバーの設定を確認してください。
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -77,6 +77,8 @@
|
||||
PdfCraft is drawing without a graphics processor, so it may be slow. Updating the graphics driver may help. O PdfCraft está desenhando sem processador gráfico, por isso pode ficar lento. Atualizar o driver de vídeo pode ajudar.
|
||||
Go to page… Ir para a página…
|
||||
Reopen the files that were open when PdfCraft last closed Reabrir os arquivos que estavam abertos quando o PdfCraft foi fechado pela última vez
|
||||
Margins Margens
|
||||
For page sizes and margins. Para tamanhos de página e margens.
|
||||
Open Abrir
|
||||
Open file Abrir arquivo
|
||||
Home Início
|
||||
@@ -2220,7 +2222,7 @@ key Space Espaço
|
||||
Inspector Inspetor
|
||||
|
||||
# Architectural print controls (follow-up to #599).
|
||||
PDF page: {w} × {h} in Página PDF: {w} × {h} pol.
|
||||
PDF page: {size} Página PDF: {size}
|
||||
Match page at 100% Ajustar papel à página a 100%
|
||||
PDF scale: {scale}% Escala do PDF: {scale}%
|
||||
Printer paper and scaling may differ. Check driver settings. O papel e a escala da impressora podem variar. Confira as configurações do driver.
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 9 and column 16.
|
@@ -770,7 +770,7 @@
|
||||
{verb} Header and Footer {verb} колонтитулы
|
||||
{verb} Watermark {verb} водяной знак
|
||||
{verb} Background {verb} фон
|
||||
Margins (in) Поля (дюймы)
|
||||
Margins Поля
|
||||
Left Header Text Текст левого колонтитула
|
||||
Center Header Text Текст центрального колонтитула
|
||||
Right Header Text Текст правого колонтитула
|
||||
@@ -2048,6 +2048,7 @@
|
||||
Created a PDF from {n} files; left out: {names} PDF создан из файлов: {n}; пропущены: {names}
|
||||
Created a PDF from the first {n} files PDF создан из первых файлов: {n}
|
||||
Go to page… Перейти на страницу…
|
||||
For page sizes and margins. Для размеров страниц и полей.
|
||||
The standard fonts can't draw “{c}” ({code}). Only Western European characters can be added as text for now. Стандартные шрифты не могут отобразить «{c}» ({code}). Пока в виде текста можно добавлять только западноевропейские символы.
|
||||
Larger pages Крупнее страницы
|
||||
Smaller pages Мельче страницы
|
||||
@@ -2136,7 +2137,7 @@ key Space Пробел
|
||||
Inspector Инспектор
|
||||
|
||||
# Architectural print controls (follow-up to #599).
|
||||
PDF page: {w} × {h} in Страница PDF: {w} × {h} дюйм.
|
||||
PDF page: {size} Страница PDF: {size}
|
||||
Match page at 100% Подобрать бумагу, масштаб 100%
|
||||
PDF scale: {scale}% Масштаб PDF: {scale}%
|
||||
Printer paper and scaling may differ. Check driver settings. Бумага и масштаб принтера могут отличаться. Проверьте настройки драйвера.
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 9 and column 16.
|
@@ -753,7 +753,7 @@ comment menu Edit సవరించు
|
||||
{verb} Header and Footer హెడర్ మరియు ఫుటర్ను {verb}
|
||||
{verb} Watermark వాటర్మార్క్ను {verb}
|
||||
{verb} Background నేపథ్యాన్ని {verb}
|
||||
Margins (in) మార్జిన్లు (in)
|
||||
Margins మార్జిన్లు
|
||||
Left Header Text ఎడమ హెడర్ టెక్స్ట్
|
||||
Center Header Text మధ్య హెడర్ టెక్స్ట్
|
||||
Right Header Text కుడి హెడర్ టెక్స్ట్
|
||||
@@ -2063,6 +2063,7 @@ action wizard Start ప్రారంభించు
|
||||
Pin folder to Home… ఫోల్డర్ను హోమ్కు పిన్ చేయి…
|
||||
No recent files ఇటీవలి ఫైళ్లు లేవు
|
||||
Go to page… పేజీకి వెళ్లు…
|
||||
For page sizes and margins. పేజీ పరిమాణాలు మరియు మార్జిన్ల కోసం.
|
||||
Open arrow తెరిచిన బాణం
|
||||
Closed arrow మూసిన బాణం
|
||||
Butt మొండి చివర
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 13 and column 16.
|
@@ -2170,6 +2170,8 @@ calendar date December грудня
|
||||
Printer properties Властивості принтера
|
||||
Reset to printer defaults Скинути до налаштувань принтера за замовчуванням
|
||||
This printer offers no settings of its own. Цей принтер не має власних налаштувань.
|
||||
Margins Поля
|
||||
For page sizes and margins. Для розмірів сторінок і полів.
|
||||
certificate Subject Власник сертифіката
|
||||
Scroll tabs left Прокрутити вкладки ліворуч
|
||||
Scroll tabs right Прокрутити вкладки праворуч
|
||||
@@ -2240,7 +2242,7 @@ key Space Пробіл
|
||||
Resize panel window Змінити розмір вікна панелі
|
||||
|
||||
# Architectural print controls (follow-up to #599).
|
||||
PDF page: {w} × {h} in Сторінка PDF: {w} × {h} дюйм.
|
||||
PDF page: {size} Сторінка PDF: {size}
|
||||
Match page at 100% Підібрати папір, масштаб 100%
|
||||
PDF scale: {scale}% Масштаб PDF: {scale}%
|
||||
Printer paper and scaling may differ. Check driver settings. Папір і масштаб принтера можуть відрізнятися. Перевірте налаштування драйвера.
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -462,7 +462,7 @@
|
||||
{verb} Header and Footer {verb}页眉和页脚
|
||||
{verb} Watermark {verb}水印
|
||||
{verb} Background {verb}背景
|
||||
Margins (in) 边距(英寸)
|
||||
Margins 边距
|
||||
Left Header Text 左页眉文本
|
||||
Center Header Text 居中页眉文本
|
||||
Right Header Text 右页眉文本
|
||||
@@ -2099,6 +2099,7 @@
|
||||
Choose a PNG or JPEG image (up to 4 MiB). Transparency is preserved. 选择 PNG 或 JPEG 图像(最大 4 MiB)。透明度会保留。
|
||||
Couldn't import the signature image: {e} 无法导入签名图像:{e}
|
||||
Go to page… 转到页面…
|
||||
For page sizes and margins. 用于页面尺寸和边距。
|
||||
The standard fonts can't draw “{c}” ({code}). Only Western European characters can be added as text for now. 标准字体无法显示“{c}”({code})。目前只能添加西欧字符作为文本。
|
||||
Larger pages 放大页面
|
||||
Smaller pages 缩小页面
|
||||
@@ -2187,7 +2188,7 @@ key Space 空格
|
||||
Inspector 检查器
|
||||
|
||||
# Architectural print controls (follow-up to #599).
|
||||
PDF page: {w} × {h} in PDF 页面:{w} × {h} 英寸
|
||||
PDF page: {size} PDF 页面:{size}
|
||||
Match page at 100% 匹配页面并设为 100%
|
||||
PDF scale: {scale}% PDF 缩放:{scale}%
|
||||
Printer paper and scaling may differ. Check driver settings. 打印机纸张和缩放可能不同。请检查驱动程序设置。
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 9 and column 16.
|
@@ -418,7 +418,7 @@
|
||||
{verb} Header and Footer {verb}頁首和頁尾
|
||||
{verb} Watermark {verb}水印
|
||||
{verb} Background {verb}背景
|
||||
Margins (in) 邊距(英寸)
|
||||
Margins 邊距
|
||||
Left Header Text 左頁首文字
|
||||
Center Header Text 居中頁首文字
|
||||
Right Header Text 右頁首文字
|
||||
@@ -2008,6 +2008,7 @@
|
||||
Choose a PNG or JPEG image (up to 4 MiB). Transparency is preserved. 選擇 PNG 或 JPEG 圖像(最大 4 MiB)。透明度會保留。
|
||||
Couldn't import the signature image: {e} 無法匯入簽名圖像:{e}
|
||||
Go to page… 轉到頁面…
|
||||
For page sizes and margins. 用於頁面尺寸和邊距。
|
||||
Reopen the files that were open when PdfCraft last closed 重新開啟上次關閉 PdfCraft 時開啟的檔案
|
||||
The standard fonts can't draw “{c}” ({code}). Only Western European characters can be added as text for now. 標準字型無法顯示「{c}」({code})。目前只能新增西歐字元作為文字。
|
||||
Open arrow 開口箭頭
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 9 and column 16.
|
@@ -324,6 +324,13 @@ pub(crate) fn preferences_body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tok
|
||||
.small()
|
||||
.color(t.text_muted),
|
||||
);
|
||||
ui.horizontal(|ui| {
|
||||
ui.label(tl!("Units"));
|
||||
for u in crate::units::Unit::ALL {
|
||||
ui.radio_value(&mut app.units, u, u.label());
|
||||
}
|
||||
});
|
||||
ui.label(egui::RichText::new(tl!("For page sizes and margins.")).small().color(t.text_muted));
|
||||
// The web build has no file paths to reopen.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
ui.checkbox(&mut app.reopen_last_session, tl!("Reopen the files that were open when PdfCraft last closed"));
|
||||
|
||||
@@ -90,6 +90,7 @@ mod pickers;
|
||||
pub mod prepare;
|
||||
mod print_ui;
|
||||
mod signature_drag;
|
||||
pub mod units;
|
||||
pub use print_ui::{Handling as PrintHandling, PrintDraft, PrinterSource, Which as PrintWhich, system_printers};
|
||||
mod redact_ui;
|
||||
pub use redact_ui::{HiddenDraft, PagesDraft as RedactPagesDraft, RedactPrefs, SearchDraft as RedactSearchDraft, SearchMode as RedactSearchMode};
|
||||
@@ -432,6 +433,8 @@ pub struct PdfCraftApp {
|
||||
/// Documents and view). Continuous scrolling at fit width by default, which never snaps
|
||||
/// between pages.
|
||||
pub view_defaults: canvas::ViewDefaults,
|
||||
/// The unit page sizes and margins are shown in (Preferences ▸ Documents and view).
|
||||
pub units: units::Unit,
|
||||
/// The Preferences ▸ Date format box while it's being edited, or while it holds an invalid
|
||||
/// pattern (the session keeps the last valid one).
|
||||
pub date_format_draft: Option<String>,
|
||||
@@ -763,6 +766,7 @@ impl PdfCraftApp {
|
||||
mode: Mode::AllTools,
|
||||
default_mode: Mode::AllTools,
|
||||
view_defaults: Default::default(),
|
||||
units: Default::default(),
|
||||
date_format_draft: None,
|
||||
mode_override: None,
|
||||
left: LeftPanel::AllTools,
|
||||
@@ -1621,6 +1625,7 @@ impl PdfCraftApp {
|
||||
"default_layout": canvas::display_token(self.view_defaults.layout, self.view_defaults.continuous),
|
||||
"default_zoom": self.view_defaults.zoom_name(),
|
||||
"highlight_fields": self.view_defaults.highlight_fields,
|
||||
"units": self.units,
|
||||
"language": self.language,
|
||||
// Null follows the system's text scaling.
|
||||
"ui_scale": self.ui_scale,
|
||||
@@ -1707,6 +1712,9 @@ impl PdfCraftApp {
|
||||
if let Some((layout, continuous)) = v["default_layout"].as_str().and_then(canvas::parse_display_token) {
|
||||
(self.view_defaults.layout, self.view_defaults.continuous) = (layout, continuous);
|
||||
}
|
||||
if let Ok(units) = serde_json::from_value::<units::Unit>(v["units"].clone()) {
|
||||
self.units = units;
|
||||
}
|
||||
if let Some(on) = v["highlight_fields"].as_bool() {
|
||||
self.view_defaults.highlight_fields = on;
|
||||
}
|
||||
|
||||
@@ -147,6 +147,7 @@ pub(crate) fn body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tokens, kind: M
|
||||
let Some(doc) = app.session.get(id) else { return (false, true) };
|
||||
let count = doc.info.pages.len();
|
||||
let page = doc.info.pages.get(current).map(|p| (p.width as f64, p.height as f64)).unwrap_or((612.0, 792.0));
|
||||
let units = app.units;
|
||||
let d = &mut app.marks_draft;
|
||||
let mut browse = false;
|
||||
let verb = if d.replace { tl!("Update") } else { tl!("Add") };
|
||||
@@ -166,12 +167,15 @@ pub(crate) fn body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tokens, kind: M
|
||||
ui.checkbox(&mut d.hf.underline, tl!("Underline"));
|
||||
color_button(ui, &mut d.hf.color);
|
||||
ui.add_space(16.0);
|
||||
ui.label(tl!("Margins (in)"));
|
||||
ui.label(format!("{} ({})", tl!("Margins"), units.label()));
|
||||
let per = units.per_point();
|
||||
for (label, k) in [(tl!("Top"), 0), (tl!("Bottom"), 1), (tl!("Left"), 2), (tl!("Right"), 3)] {
|
||||
ui.label(label);
|
||||
let mut v = d.hf.margins[k] / 72.0;
|
||||
if ui.add(egui::DragValue::new(&mut v).range(0.0..=10.0).speed(0.05).max_decimals(2)).changed() {
|
||||
d.hf.margins[k] = v * 72.0;
|
||||
let mut v = d.hf.margins[k] * per;
|
||||
// Up to 10 inches, whatever the unit.
|
||||
let max = 720.0 * per;
|
||||
if ui.add(egui::DragValue::new(&mut v).range(0.0..=max).speed(units.speed()).max_decimals(units.decimals())).changed() {
|
||||
d.hf.margins[k] = v / per;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -98,7 +98,15 @@ impl MeasureView {
|
||||
self.scale = None;
|
||||
}
|
||||
}
|
||||
pub(crate) fn page_input(ui: &egui::Ui, resp: &egui::Response, doc: &Document, view: &mut DocView, cx: &crate::comments::PageCx<'_>, tool: Tool) {
|
||||
pub(crate) fn page_input(
|
||||
ui: &egui::Ui,
|
||||
resp: &egui::Response,
|
||||
doc: &Document,
|
||||
view: &mut DocView,
|
||||
cx: &crate::comments::PageCx<'_>,
|
||||
tool: Tool,
|
||||
units: crate::units::Unit,
|
||||
) {
|
||||
let page = cx.page;
|
||||
let xf = cx.xf;
|
||||
let author = &cx.prefs.author;
|
||||
@@ -167,7 +175,7 @@ pub(crate) fn page_input(ui: &egui::Ui, resp: &egui::Response, doc: &Document, v
|
||||
state.page = Some(page);
|
||||
}
|
||||
if state.points.is_empty() {
|
||||
match doc.measurement_scale(page, point) {
|
||||
match doc.measurement_scale_or(page, point, units.per_point(), units.label()) {
|
||||
Ok(scale) => {
|
||||
state.scale = Some(scale);
|
||||
state.error = None;
|
||||
@@ -291,6 +299,7 @@ fn panel_body(app: &mut PdfCraftApp, ui: &mut egui::Ui, _t: &Tokens) {
|
||||
}
|
||||
});
|
||||
let Some(doc) = app.session.get(id) else { return };
|
||||
let units = app.units;
|
||||
let page = app.views[index].current;
|
||||
let info = doc.info.pages.get(page).cloned();
|
||||
let state = &mut app.views[index].measure;
|
||||
@@ -300,7 +309,7 @@ fn panel_body(app: &mut PdfCraftApp, ui: &mut egui::Ui, _t: &Tokens) {
|
||||
if state.seeded_page != Some(page) {
|
||||
state.seeded_page = Some(page);
|
||||
if let (Ok(a), Ok(b)) = (doc.measurement_to_user(page, [0.0, 0.0]), doc.measurement_to_user(page, [72.0, 0.0]))
|
||||
&& let Ok(scale) = doc.measurement_scale(page, a)
|
||||
&& let Ok(scale) = doc.measurement_scale_or(page, a, units.per_point(), units.label())
|
||||
&& let Ok(reading) = measure::reading(Kind::Distance, &[a, b], &scale)
|
||||
{
|
||||
state.drawing_points = 72.0;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Set Page Boxes (Acrobat: Organize pages ▸ Set page boxes / Crop pages ▸ double-click), M4.4.
|
||||
//!
|
||||
//! Choose a box (crop, trim, bleed, art, media), margins from the media box in points, inches or
|
||||
//! millimetres, and the pages. A preview shows the current page's media box and the new box.
|
||||
//! Choose a box (crop, trim, bleed, art, media), margins from the media box in points, inches,
|
||||
//! millimetres or centimetres, and the pages. A preview shows the current page's media box and the new box.
|
||||
|
||||
use egui::{Align, Color32, CornerRadius, Layout, Rect, Stroke, pos2, vec2};
|
||||
use pdfcraft_engine::{BoxSpec, Edit, PageBox};
|
||||
@@ -9,30 +9,7 @@ use pdfcraft_engine::{BoxSpec, Edit, PageBox};
|
||||
use crate::theme::{self, Tokens};
|
||||
use crate::{PdfCraftApp, widgets};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum Unit {
|
||||
Points,
|
||||
Inches,
|
||||
Millimetres,
|
||||
}
|
||||
|
||||
impl Unit {
|
||||
fn per_point(self) -> f64 {
|
||||
match self {
|
||||
Unit::Points => 1.0,
|
||||
Unit::Inches => 1.0 / 72.0,
|
||||
Unit::Millimetres => 25.4 / 72.0,
|
||||
}
|
||||
}
|
||||
|
||||
fn label(self) -> &'static str {
|
||||
match self {
|
||||
Unit::Points => "pt",
|
||||
Unit::Inches => "in",
|
||||
Unit::Millimetres => "mm",
|
||||
}
|
||||
}
|
||||
}
|
||||
pub use crate::units::Unit;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum Range {
|
||||
@@ -93,7 +70,12 @@ pub(crate) fn body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tokens) -> (boo
|
||||
let Some(doc) = app.session.get(id) else { return (false, true) };
|
||||
let boxes = doc.page_boxes();
|
||||
let count = boxes.len().max(1);
|
||||
let units = app.units;
|
||||
let d = &mut app.boxes_draft;
|
||||
// Opening the dialog starts in the unit chosen in Preferences.
|
||||
if d.seeded.is_none() {
|
||||
d.unit = units;
|
||||
}
|
||||
// Seed the margins from the current page's box when the dialog opens or the box changes.
|
||||
if d.seeded != Some((current, d.which))
|
||||
&& let Some(b) = boxes.get(current)
|
||||
@@ -128,7 +110,7 @@ pub(crate) fn body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tokens) -> (boo
|
||||
ui.end_row();
|
||||
ui.label(tl!("Units"));
|
||||
egui::ComboBox::from_id_salt("boxes-unit").selected_text(d.unit.label()).show_ui(ui, |ui| {
|
||||
for u in [Unit::Inches, Unit::Millimetres, Unit::Points] {
|
||||
for u in Unit::ALL {
|
||||
ui.selectable_value(&mut d.unit, u, u.label());
|
||||
}
|
||||
});
|
||||
@@ -137,7 +119,7 @@ pub(crate) fn body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tokens) -> (boo
|
||||
for (label, idx) in [(tl!("Top"), 3), (tl!("Bottom"), 1), (tl!("Left"), 0), (tl!("Right"), 2)] {
|
||||
let l = ui.label(label);
|
||||
let mut v = d.margins[idx] * k;
|
||||
let speed = if d.unit == Unit::Inches { 0.01 } else { 0.5 };
|
||||
let speed = d.unit.speed();
|
||||
let r = ui
|
||||
.add(egui::DragValue::new(&mut v).speed(speed).range(0.0..=10_000.0).max_decimals(3).suffix(format!(" {}", d.unit.label())))
|
||||
.labelled_by(l.id);
|
||||
|
||||
@@ -35,6 +35,8 @@ pub enum Handling {
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PrintDraft {
|
||||
pub printers: Vec<spool::Printer>,
|
||||
/// The unit the sheet size is shown in (Preferences).
|
||||
pub units: crate::units::Unit,
|
||||
/// The printers are still being listed in the background (see [`PrinterSource`]).
|
||||
pub printers_loading: bool,
|
||||
/// The user chose the printer, so a list arriving late doesn't move it to the default.
|
||||
@@ -82,6 +84,7 @@ impl Default for PrintDraft {
|
||||
fn default() -> Self {
|
||||
PrintDraft {
|
||||
printers: Vec::new(),
|
||||
units: Default::default(),
|
||||
printers_loading: false,
|
||||
printer_picked: false,
|
||||
printer: None,
|
||||
@@ -223,6 +226,7 @@ impl PdfCraftApp {
|
||||
printers,
|
||||
printers_loading,
|
||||
printer_picked: false,
|
||||
units: self.units,
|
||||
printer: default,
|
||||
current_page: current,
|
||||
sheet: 0,
|
||||
@@ -619,7 +623,7 @@ pub(crate) fn body(
|
||||
}
|
||||
if let Some(&source) = sizes.get(d.current_page) {
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
ui.label(crate::i18n::fmt(tl!("PDF page: {w} × {h} in"), &[("w", &format!("{:.2}", source.0 / 72.0)), ("h", &format!("{:.2}", source.1 / 72.0))]));
|
||||
ui.label(crate::i18n::fmt(tl!("PDF page: {size}"), &[("size", &d.units.size(source.0, source.1))]));
|
||||
let matched = print::matching_paper(source);
|
||||
if ui.add_enabled(matched.is_some(), egui::Button::new(tl!("Match page at 100%"))).clicked()
|
||||
&& let Some(paper) = matched
|
||||
@@ -858,8 +862,7 @@ pub(crate) fn body(
|
||||
}
|
||||
});
|
||||
if let Some(s) = sheets.get(d.sheet) {
|
||||
let (w, h) = (s.size.0 / 72.0, s.size.1 / 72.0);
|
||||
ui.label(egui::RichText::new(format!("{w:.2} × {h:.2} in")).small().color(t.text_muted));
|
||||
ui.label(egui::RichText::new(d.units.size(s.size.0, s.size.1)).small().color(t.text_muted));
|
||||
if d.handling == Handling::Size
|
||||
&& let Some(p) = s.placed.first()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
//! The unit lengths are shown in: page sizes (Document Properties, Print) and the margins typed
|
||||
//! in Header & Footer and Set Page Boxes. Preferences ▸ Documents and view ▸ Units.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum Unit {
|
||||
#[default]
|
||||
Inches,
|
||||
Millimetres,
|
||||
Centimetres,
|
||||
Points,
|
||||
}
|
||||
|
||||
impl Unit {
|
||||
pub const ALL: [Unit; 4] = [Unit::Millimetres, Unit::Centimetres, Unit::Inches, Unit::Points];
|
||||
|
||||
/// Units per PDF point.
|
||||
pub fn per_point(self) -> f64 {
|
||||
match self {
|
||||
Unit::Points => 1.0,
|
||||
Unit::Inches => 1.0 / 72.0,
|
||||
Unit::Millimetres => 25.4 / 72.0,
|
||||
Unit::Centimetres => 2.54 / 72.0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn label(self) -> &'static str {
|
||||
match self {
|
||||
Unit::Points => "pt",
|
||||
Unit::Inches => "in",
|
||||
Unit::Millimetres => "mm",
|
||||
Unit::Centimetres => "cm",
|
||||
}
|
||||
}
|
||||
|
||||
/// Decimals worth showing: a tenth of a millimetre or point, a hundredth of an inch or centimetre.
|
||||
pub fn decimals(self) -> usize {
|
||||
match self {
|
||||
Unit::Millimetres | Unit::Points => 1,
|
||||
Unit::Inches | Unit::Centimetres => 2,
|
||||
}
|
||||
}
|
||||
|
||||
/// Drag speed for a value field in this unit.
|
||||
pub fn speed(self) -> f64 {
|
||||
match self {
|
||||
Unit::Inches | Unit::Centimetres => 0.01,
|
||||
Unit::Millimetres | Unit::Points => 0.5,
|
||||
}
|
||||
}
|
||||
|
||||
/// The unit the system's region measures in: inches in the United States, Liberia and
|
||||
/// Myanmar, millimetres everywhere else; inches when the region can't be told. Used on first
|
||||
/// start, before the user picks one in Preferences.
|
||||
pub fn system() -> Unit {
|
||||
static SYSTEM: std::sync::OnceLock<Unit> = std::sync::OnceLock::new();
|
||||
*SYSTEM.get_or_init(|| detect().unwrap_or_default())
|
||||
}
|
||||
|
||||
/// A page size given in points, e.g. "210.0 × 297.0 mm".
|
||||
pub fn size(self, width_pt: f64, height_pt: f64) -> String {
|
||||
let (k, d) = (self.per_point(), self.decimals());
|
||||
format!("{:.d$} × {:.d$} {}", width_pt * k, height_pt * k, self.label())
|
||||
}
|
||||
}
|
||||
|
||||
/// The unit for a locale tag's region: `en_US.UTF-8`, `en-US`, `bg-BG`, `zh-Hans-CN`, `en_GB@euro`.
|
||||
/// `None` without a region (`C`, `POSIX`, `en`).
|
||||
pub(crate) fn unit_for_tag(tag: &str) -> Option<Unit> {
|
||||
let tag = tag.split(['.', '@']).next().unwrap_or_default();
|
||||
let region = tag.split(['-', '_']).skip(1).filter(|p| p.len() == 2 && p.chars().all(|c| c.is_ascii_alphabetic())).last()?;
|
||||
Some(match region.to_ascii_uppercase().as_str() {
|
||||
"US" | "LR" | "MM" => Unit::Inches,
|
||||
_ => Unit::Millimetres,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn detect() -> Option<Unit> {
|
||||
// The system's own measurement setting first: a terminal's LANG is often en_US whatever the region.
|
||||
#[cfg(target_os = "macos")]
|
||||
if let Ok(out) = std::process::Command::new("/usr/bin/defaults").args(["read", "-g", "AppleMeasurementUnits"]).output()
|
||||
&& out.status.success()
|
||||
{
|
||||
match String::from_utf8_lossy(&out.stdout).trim() {
|
||||
"Inches" => return Some(Unit::Inches),
|
||||
"Centimeters" => return Some(Unit::Millimetres),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
// AppleMeasurementUnits only exists once someone changed Measurement system by hand, and apps
|
||||
// started from the Finder get no LANG, so the region of the system locale decides next.
|
||||
#[cfg(target_os = "macos")]
|
||||
if let Ok(out) = std::process::Command::new("/usr/bin/defaults").args(["read", "-g", "AppleLocale"]).output()
|
||||
&& out.status.success()
|
||||
&& let Some(unit) = unit_for_tag(String::from_utf8_lossy(&out.stdout).trim())
|
||||
{
|
||||
return Some(unit);
|
||||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
use std::os::windows::process::CommandExt;
|
||||
// Region ▸ Measurement system: 0 metric, 1 U.S. The absolute path keeps a `reg` earlier on
|
||||
// PATH from running; CREATE_NO_WINDOW keeps a console from flashing up.
|
||||
let windir = std::env::var_os("SystemRoot").unwrap_or_else(|| "C:\\Windows".into());
|
||||
let reg = std::path::Path::new(&windir).join("System32").join("reg.exe");
|
||||
if let Ok(out) = std::process::Command::new(reg)
|
||||
.args(["query", "HKCU\\Control Panel\\International", "/v", "iMeasure"])
|
||||
.creation_flags(0x0800_0000)
|
||||
.output()
|
||||
&& out.status.success()
|
||||
{
|
||||
match String::from_utf8_lossy(&out.stdout).split_whitespace().last() {
|
||||
Some("1") => return Some(Unit::Inches),
|
||||
Some("0") => return Some(Unit::Millimetres),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
["LC_ALL", "LC_MEASUREMENT", "LANG"].iter().find_map(|var| std::env::var(var).ok().filter(|v| !v.is_empty()).and_then(|v| unit_for_tag(&v)))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
fn detect() -> Option<Unit> {
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Unit, unit_for_tag};
|
||||
|
||||
#[test]
|
||||
fn region_decides_the_unit() {
|
||||
assert_eq!(unit_for_tag("en_US.UTF-8"), Some(Unit::Inches));
|
||||
assert_eq!(unit_for_tag("en-US"), Some(Unit::Inches));
|
||||
assert_eq!(unit_for_tag("en_GB@euro"), Some(Unit::Millimetres));
|
||||
assert_eq!(unit_for_tag("bg-BG"), Some(Unit::Millimetres));
|
||||
assert_eq!(unit_for_tag("de_DE.UTF-8"), Some(Unit::Millimetres));
|
||||
assert_eq!(unit_for_tag("zh-Hans-CN"), Some(Unit::Millimetres));
|
||||
assert_eq!(unit_for_tag("my_MM"), Some(Unit::Inches));
|
||||
assert_eq!(unit_for_tag("C"), None);
|
||||
assert_eq!(unit_for_tag("POSIX"), None);
|
||||
assert_eq!(unit_for_tag("en"), None);
|
||||
assert_eq!(unit_for_tag(""), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a4_in_every_unit() {
|
||||
assert_eq!(Unit::Millimetres.size(595.276, 841.89), "210.0 × 297.0 mm");
|
||||
assert_eq!(Unit::Centimetres.size(595.276, 841.89), "21.00 × 29.70 cm");
|
||||
assert_eq!(Unit::Inches.size(612.0, 792.0), "8.50 × 11.00 in");
|
||||
assert_eq!(Unit::Points.size(612.0, 792.0), "612.0 × 792.0 pt");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saved_names_round_trip() {
|
||||
for u in Unit::ALL {
|
||||
let v = serde_json::to_value(u).unwrap();
|
||||
assert_eq!(serde_json::from_value::<Unit>(v).unwrap(), u);
|
||||
}
|
||||
assert_eq!(serde_json::to_value(Unit::Millimetres).unwrap(), "millimetres");
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user