mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-25 10:50:14 +00:00
sonar-fix/enforce-https
1189 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
786fa481f4 |
Issue #189: each document tab keeps its own scroll position (#259)
The page view's ScrollArea had no id of its own, so egui derived it from the parent Ui, which is the same for every document. All tabs therefore shared one scroll offset: switching to another document showed it at wherever the previous one had been scrolled to (clamped to its length), and switching back showed the first document at the other one's offset, usually the top. Salt the ScrollArea with the document's id so egui keeps one offset per tab. Test: viewer.rs::each_document_keeps_its_own_scroll_position scrolls a.pdf to page 4, switches to b.pdf and scrolls it to page 2, then switches back and forth: before the fix b.pdf opened on page 2 instead of its own top. Cited as evidence on view.document-tabs. Co-authored-by: wstock <1394687+wstock@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
505c4a2fa3 |
Issue #248: pdfcraft-cli render writes the format its --out name says (.png, .jpg, .tif or .pam) (#255)
`render --out p1.png` wrote a netpbm PAM stream (P7) whatever the name, so a `.png` could not be opened by any PNG reader. The CLI already links the engine, whose export module encodes PNG, JPEG and TIFF, so the output format now follows the extension: `.png`, `.jpg`/`.jpeg` (quality 90), `.tif`/`.tiff`, and `.pam` for the raw stream as before. Any other name is refused up front, before the page is rendered, with the accepted list. `export::encode_image` wraps the three encoders behind `ImageFormat` so the CLI and `Exporter::image` share one match. Tests (apps/pdfcraft-cli/tests/render.rs): a `.png` gets the PNG signature and an IHDR with the rendered size; `.JPG` and `.tif` get JPEG/TIFF magic; `.pam` still gets the P7 header and the right number of bytes; `.bmp` fails with the message and writes nothing. Co-authored-by: wstock <1394687+wstock@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
776c8f0742 |
Issue #253: comment_add anchors note and attachment icons at the displayed top-left on rotated pages (#254)
`comment_add` converted a note's or attachment's `at` (the icon's top-left as displayed) as a single point through `PageInfo::view_to_user`, and the engine's `rect_for` extends the icon square from `at` toward +x/-y in user space. On an unrotated page the displayed and user-space top-left are the same corner; under `/Rotate` they are different corners of the square, so the icon landed one icon width (20 pt) away from the requested point. `icon_anchor` converts the whole displayed square to user space and takes its user-space top-left, so the `/Rect` covers the requested square under any rotation. `PageInfo::view_rect_to_user` is the render-side counterpart of `view_rect_to_quad`, and `NOTE_SIZE` is re-exported from the engine. Test: `note_icons_anchor_at_the_requested_corner_on_rotated_pages` adds a note and an attachment on pages rotated 0/90/180/270 and asserts the `comment_list` rectangles; before the fix the 90° page reports [52, 72, 72, 92] for a note asked for at [72, 72]. Co-authored-by: wstock <1394687+wstock@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
4db49900c6 |
Add Spanish (es) interface catalog (#251)
Registers Español in the language list, with a full catalog covering commands, dialogs, panels and notices, plus tests and docs. Claude-Session: https://claude.ai/code/session_01A3kqJC1D3PAgT3dHxnqYNy Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
bfd60787eb | Pin mixed-size Fit page behavior with regression tests (#245) | ||
|
|
ca9c653e3a |
Automation: doc_protect says restrictions need a permissions password, and refuses them without one (#134) (#243)
The schema documented printing/changes/copy/accessibility and their defaults unconditionally, but the engine (like Acrobat) only enforces /P behind an owner password: open_password alone encrypts the document and leaves everything allowed, even when a caller passed copy: false or changes: "none" explicitly, and the call reported success. State the condition in the tool and property descriptions, and turn an explicit restriction without permissions_password into InvalidArgs that names the argument and what is needed, instead of writing an unrestricted file. open_password alone is unchanged. Co-authored-by: wstock <1394687+wstock@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
abf4f80a62 |
Automation: doc_info reports annotation and link rects in displayed-page coordinates (#129) (#242)
doc_info (and the MCP document-info resource built on it) serialised annotation and link rectangles straight from the PDF /Rect, in user space, while every other tool reports and accepts rectangles with the origin at the top-left of the displayed page. Feeding a doc_info rect to link_edit therefore moved the link, and on rotated pages the two conventions disagreed in more than a y flip. Convert both through the page's user_to_view, the same conversion comment_list and link_list use, so the three agree. Pages that an annotation cannot be matched to keep the raw rect. Co-authored-by: wstock <1394687+wstock@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
8407c87ef9 |
#214: wheel paging, Page display menu, default layout and zoom (#241)
- The wheel turns pages in single-page view: one per notch, one per swipe. - The rail's Page display menu offers Acrobat's view choices. - Preferences: default page display and default zoom. - Fit page and Fit height no longer jump across mixed page sizes. |
||
|
|
11ffedcc98 |
XFA: run the forms' JavaScript: calculations, validation, rows, visibility (#60) (#230)
* M12: run XFA forms' JavaScript: calculations, validation, rows, visibility (#60) Laid-out XFA forms had their fields but none of their behaviour: totals didn't compute, "Add row" buttons did nothing, and subforms a check box should reveal stayed hidden. The js crate gains the XFA scripting object model as native boa objects over a snapshot of the form tree: xfa.form navigation by name, SOM resolution (absolute, relative with the containing-subform search, descendants, indexes and wildcards), rawValue/presence/access, instance managers (_name, addInstance, removeInstance, setInstances, count), xfa.host (messageBox, resetData, print, setFocus, gotoURL), xfa.layout page numbers, xfa.event, app, console and util, plus unqualified sibling names as XFA resolves them. Scripts run with `this` bound to the node and yield their completion value; everything they change or ask for comes back as effects. The xfa crate builds the live form tree (instances from the data, values, presence and access with overrides) and the event table from the template, keeps presence and access overrides on the AcroForm (/PCXfaOverrides) so undo and reopening preserve them, adds and removes row instances in the datasets packet through the existing merge pipeline, and lays the form out again on demand. The engine runs initialize and calculate scripts on open (an appended revision, nothing dirty), exit, validate and calculate after a field changes (a failing validate shows its message and keeps the value, as Acrobat does), and click scripts for buttons through run_javascript, so the app's buttons and the js_run tool work unchanged. Values a script sets go to the fields and the data at once; rows and visibility changes re-lay the form out. All of it is one undo step. JavaScript off turns it off. FormCalc scripts are recognised and reported, not run. Bounds: script length and nesting as before, loop and recursion limits, at most 1000 instances added per run, 2000 scripts and 50 re-layouts per event; a calculate script that adds rows, or a click script looping on addInstance/setInstances, ends quickly with a bounded form (tested). Checked on IRCC's IMM 5669: opens with its page-number scripts, the Print button's xfa.host.print runs through js_run, fill and save keep the original bytes. * M12: Save and SaveAs named actions run the file commands (#60) XFA forms' Save buttons are app.execMenuItem("SaveAs"), which the layout turns into a SaveAs named action; the shell answered it with "the SaveAs action isn't supported yet". Named Save and SaveAs now run file.save and file.save_as, like Named Print opens the print dialog. The buttons test covers Save against a file on disk (SaveAs opens the native dialog, which a headless test must not). * M12: bound XFA form scripts against hostile forms (#60) Review fixes for the XFA scripting PR (#230): - Effects: values, presence and access set again on the same object merge (last wins, behind order-dependent effects); 10k effects, 100 message boxes and 1000 console lines per script and per event, reported when hit. An event's data writes are kept in memory and written as one rewrite of the datasets packet; later writes in the same edit replace that stream in place. - form_tree stops at 200k objects and says so; forms without scripts build no live form and run nothing. - Only click scripts may print, save, open links or move the focus; others are noted in the console. Open-time messages and errors show right after open. - Lower loop limits for initialize/calculate/validate, a time budget per event after which remaining scripts are skipped and reported, and a per-script time limit after which a runaway script is abandoned and the document's scripts turned off. - What open-time scripts change is noted in the XFA warnings. - A document's Named /Save no longer writes the file on click (SaveAs opens the Save As dialog, as a script's execMenuItem does). - XFA click scripts are no longer written as /S /JavaScript actions: the button carries /PCXfaClick and runs by SOM path. Private keys documented. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
dfb4e26e1a |
Split: keep only the resources each part draws (#204) (#228)
* Split: keep only the resources each part draws (#204) In a book whose pages share one resource dictionary, every split part carried every XObject the source listed: a part saved out at the source's size although its pages drew a fraction of the images. extract_pages now walks each part's content (form XObjects included) and drops the /XObject entries no page names; the garbage-collecting rewrite on save then drops the objects themselves. Tests cover a one-page part that must lose the other page's image and a part that needs both. * Split/Extract pruning never drops an XObject something might still draw A resources dictionary is left untouched when one of its pages has content that can't be decoded, when a form nests past the depth limit, or when it lists a Type 3 font without its own /Resources (its glyphs draw through the page's). Regression tests fail without this. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Mohammed Munzir <Mohammed.Munzir@iga.gov.bh> Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
932967a30d |
Restore #196, #202, #203 and the macOS release-runner change, reverted by the i18n chain (#229)
* Release CI: macOS job on macos-15-xlarge Custom macOS larger runners in a runner group do not pick up jobs; the built-in M2 Pro label does. Only release.yml uses it, and the actions_macos_xl budget caps the monthly spend. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit |
||
|
|
827c61bba9 |
Picker tests wait for the worker instead of polling with a deadline (#221)
a_panicking_worker_clears_showing_and_queues_nothing failed on main (Linux CI and Windows ARM64): panic unwinding on a loaded runner can take longer than the 1 s polling window. Tests now join the worker thread, so they can't time out. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
42078a8365 |
Fix theme controls with shared System, Light and Dark preferences (#212)
* M3: unify toolbar and menu theme preferences Co-authored-by: Codex <noreply@openai.com> * M3: use the current egui style API in theme regression Co-authored-by: Codex <noreply@openai.com> --------- Co-authored-by: Codex <noreply@openai.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
ec980d0743 |
fix(i18n): follow Windows display language for Chinese UI (#216)
* fix(i18n): follow Windows display language for Chinese UI M14: resolve Auto through the Windows UI culture after locale environment variables. Reuse Chinese region/script mapping and cover the query and fallback behavior. Refs #215 Co-authored-by: Codex <noreply@openai.com> * Windows Auto language: ask Windows directly instead of starting PowerShell sys-locale (1Password, MIT OR Apache-2.0) reads GetUserPreferredUILanguages, so Auto no longer launches a hidden powershell.exe at startup (slow, and the kind of behaviour endpoint security tools flag). All preferred display languages are tried in order. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Codex <noreply@openai.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
6974c6550d |
Measure: add distance, perimeter and area tools (#208)
* M12: add calibrated 2D measurement tools * M12: harden measurement tools for landing (#208) Bounded snap targets (endpoints only for accepted segments, page-wide cap), lenient extraction (deep q nesting and undecodable streams are skipped and reported), per-page snap cache, measure::list reports unsupported entries instead of failing, closed polygons with a repeated vertex, printable unit labels (m²), cached panel listing, WinAnsi captions, indirect /VP kept, measure.export skipped in the command test like other pickers, missing translations, honest parity (scale and distance partial) and ROADMAP. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
77893e8ab3 |
M14: complete Simplified Chinese translation catalog (#209)
* M14: complete Simplified Chinese translation catalog * M14: translate new XFA notices and counts --------- Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
42ddb2d546 |
XFA: lay dynamic forms out from their template and keep their data in step (#60) (#121)
* M12: lay dynamic XFA forms out from their template (#60) A dynamic XFA form is a PDF shell around an XML template: one placeholder page ("requires Adobe Reader") and /NeedsRendering true. Every viewer without an XFA engine shows the placeholder, and so did PrintCraft. New crate printcraft-xfa (L3, already reserved in the layering table). It reads the XDP packets, parses the template (XFA 3.3: subforms, areas, fields, draws, exclusion groups, page sets, fonts, paragraphs, borders, captions, rich text, inline JPEGs, occurrences, breaks, overflow leaders, column widths), lays it out (positioned, tb, lr-tb, table/row; containers without a width size to their content; flow across content areas and page masters; header rows repeat after a break; hidden takes no space; page number and count fields resolve) and writes real pages and AcroForm widgets into the document: text, date (AFDate format and keystroke actions from the picture clause), check boxes, radio groups, push buttons with reset/print/save/URI actions. Standard-14 fonts stand in for Arial, Courier New and friends; nothing is embedded. The engine lays a dynamic form out when it opens it. The original bytes stay: the layout is one appended revision, the widgets get appearances from the forms crate, and the XFA packets remain so Adobe's viewers keep rendering the form their way. A marker on the AcroForm (/PCXfaLayout) stops a saved form from being laid out twice; each field keeps its SOM path (/PCSom) for the data round trip to come. doc_info, the CLI's info and the notice bar report the layout. Checked on IRCC's IMM 5669 (Schedule A, Designer 6.2, 464 KB template, AES-encrypted, usage rights): four pages and 308 fields in 27 ms, the same page structure as the form, filled through form_fill, saved in place with the original bytes intact and reopened with the values. Not done, and said so in the parity notes: the datasets packet is neither read nor written, so Adobe's viewers don't show values filled in here; no FormCalc or XFA JavaScript (rows don't grow, nothing calculates); choice lists are text fields; signature, image and barcode fields are blank; metrics are approximate and fidelity is eyeballed, not measured. Tests: crates/xfa (parsing, sizing, pagination with repeated headers, PDF writing, hostile input), the engine (open, fill, incremental save, reopen without a second layout), the automation tools (open, info, render, fill, save, reopen) and the UI notice (kittest). * M12: XFA forms read and write their datasets packet; static forms too (#60) With the layout alone, a form filled in PrintCraft opened blank in Adobe Reader, and one filled in Acrobat opened blank here: Adobe's viewers keep the values in the XFA datasets packet, not in the AcroForm fields. The xfa crate's new data module reads the datasets packet (normal binding: a field's node is named after it under its named ancestor subforms, repeated instances as repeated siblings) and writes it back from the fields. Laying a form out now takes each field's value from its data node (dates in ISO form shown in the field's pattern, check boxes and radio groups by their on values) and gives a repeating subform or table row as many instances as the data has, so rows another viewer added appear. Every laid-out field carries its SOM path and its on/off items, so the packet can be rebuilt from the AcroForm at any time. The engine writes the datasets after every form edit (one revision with the field change), and on open applies the datasets to the fields of a static XFA form, or of a dynamic one laid out earlier, when another viewer changed them since. The static-form notice now says the two are kept in step instead of warning that Acrobat may disagree. Checked on IRCC's IMM 5669: fill, save in place (original bytes intact), and the file holds <IMM_5669><page1><familyName>SINGH</familyName>…, the structure Acrobat writes for this form; reopening shows the values. Not done: explicit bind refs, global binding and data descriptions are not followed; no import or export of standalone XML or XDP data files. * M12: XFA layout memoizes container sizes, bounding measurement work (#60) A container without a width measured its content for its width and again for its height at every level, so nested width-less subforms took 2^depth measurements, all synchronously in Session::open. Container sizes are now cached per (node, width, depth), the root and page-area items keep stable addresses instead of being cloned per measurement, and a measurement budget turns anything left into TooLarge. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * M12: rustfmt the merged XFA notice (#60) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * M12: XFA datasets are merged into, not replaced; capped; UTF-16 kept; rewrites recorded (#60) - write_datasets merged nothing: it replaced <xfa:data> with the AcroForm-bound values alone, dropping unbound, namespaced and deep data. It now edits the existing packet text in place: a bound node's text is replaced only when it differs, missing nodes are added below the deepest existing one, and everything else (unbound data, other namespaces, attributes, comments, layout) stays byte for byte. A packet of an XFA array that relies on the envelope's prefixes is read inside a wrapper declaring them. - What can't be written (a node holding structured content, too-deep paths or absurd SOM indices, conflicting fields, an unreadable or non-UTF packet) comes back as DatasetsWrite.warnings; the engine keeps them in Document.xfa_warnings and info.warnings (surviving re-reads), and doc_info reports xfa_warnings. - Building data counted same-name siblings for every one it added (quadratic in an untrusted index) with no total cap. Children are now indexed by name, indices above 10,000 and paths deeper than 64 are refused, and one write creates at most 20,000 nodes. - Packets are decoded exactly (UTF-8, UTF-16 LE/BE with or without BOM) and written back in the encoding they came in; anything else is reported and left untouched instead of being rewritten from a lossy UTF-8 reading. - A static XFA form whose fields are rewritten from the datasets on open now says so in info.warnings, naming the fields. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
0bbba8cafa |
#161: keep the Save-changes prompt inside the screen (#207)
An unwrapped title carried a long file name past the viewport: egui's Modal sizes to its content, so the centered panel grew to the name's full width, clipped its own message and pushed Save/Cancel off-screen (reported on Windows, reproduced everywhere). Wrap the title to the dialog width in the save prompt and the password prompt, and pin the title and all three buttons inside a 1365x719 screen in a regression test. Co-authored-by: Mawfyy <mawfy12@gmail.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
607d3d1c0e |
#190: choose DPI when creating PDFs from images (#206)
Co-authored-by: Codex <noreply@openai.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
4d0078aeff |
Signatures: RFC 3161 timestamps, document timestamps, DSS/VRI and revocation verification (non-network half) (#69)
* Signatures: RFC 3161 timestamp tokens (PAdES B-T) behind a transport-free TSA interface * Signatures: document timestamps, DSS/VRI evidence embedding, trusted verification time * Signatures: revocation evidence verification (CRL and OCSP) wired into validation * Signatures: real-world certificate compatibility (CRLDP, tolerant extensions) and XFA-aware DocMDP classification * Signatures: RFC 6960 encodings, delegated-responder verification, TSA trust anchoring, try_oid overflow * Signatures: harden PR #69 validation (XFA, metadata, revocation, TSA trust, token digest) - Remove the XFA "form fill" reclassification: it read the new revision, so an update could add /XFA + /NeedsRendering and replace the page tree while approval and P=2/3 certification signatures stayed Valid. Classification (including catalog /Extensions) is back to main's. - A changed stream tagged /Type /Metadata counts as metadata only when it is the signed catalog's /Metadata stream or was already metadata when signed; a relabelled page Form XObject is "other changes". - A verified revocation stays Invalid when the certificate was also outside its validity at signing time (was softened to Unknown). - An embedded timestamp whose TSA does not chain to the trust store is reported as an unverified timestamp and no longer sets the validity or revocation time; the signer's claimed time is used, as on main. - parse_token checks the TSTInfo messageDigest with the SignerInfo digest algorithm, not the imprint's. Each fix has a regression test in crates/sign/tests/pdf.rs that fails on the PR head. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
355de9038e |
i18n: Simplified Chinese UI language option (#112)
* i18n: Czech interface language
Adds Czech (Čeština, code `cs`) next to English and Japanese:
Preferences ▸ Interface language, `ui.set language cs`, `ui.state`
and persisted preferences all accept it.
- i18n: `Language::Cs`; `tr()` picks the language's table with a match
(English has none), panic-free. The CZECH table (83 entries) covers
every label passed to `.tr("…")` (main menu, View zoom/theme/side-panel
entries, Preferences dialog), every registered File/Edit/Pages/View/Help
command shown by `registry_menu`, and every key of the Japanese table.
Written clean-room from the English meaning, not from any Acrobat
localisation.
- Tests: Czech covers every registered menu title and label, every
`.tr("…")` literal found by scanning the crate's sources, and the
Japanese keys; nothing is left identical except an explicit KEEP_AS_IS
list ("OK"); ellipses match the source; keys are unique per language;
`cs` round-trips through set_option, ui.set/ui.state and persistence.
- theme: the font setup is split into `font_definitions()`, and a test
checks with skrifa (dev-dependency, already in the tree via egui) that
Inter Regular/Medium/SemiBold and JetBrains Mono draw all Czech letters
and every Czech translation, so nothing falls back to Shippori Mincho.
- The `language` error lists `cs`; docs/localization.md, the parity note
for misc.prefs-language and the ROADMAP log mention Czech.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* M14: translation catalogs, same system as PhotoCraft
Move the interface translations to PhotoCraft's i18n system: one TSV
catalog per language (crates/ui-egui/src/i18n/ja.tsv), the tl! macro,
entries keyed by command id, context or plural forms, and catalog tests
(format, duplicates, placeholders, command ids).
The 49 existing Japanese strings move over unchanged, plus "Auto". The
language preference now defaults to "auto" and follows the system
language (English when it has no catalog); "en" and "ja" still persist
and unknown values are rejected.
Update docs/localization.md, ROADMAP.md and the misc.prefs-language
parity entry.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* M14: add Traditional Chinese (Taiwan) UI localization
Co-Authored-By: Codex <noreply@openai.com>
* M14: translate shortcuts and expose language preferences in Edit menu
Add command-comma and verify language switching through the actual selector.
Co-authored-by: Codex <noreply@openai.com>
* M14: expand Traditional Chinese dialogs, panels and diagnostics
Preserve opaque values in translated templates and keep application diagnostics at the UI boundary. Extend native control and template regression coverage.
Co-authored-by: Codex <noreply@openai.com>
* M14: finish Traditional Chinese signature prompts and history labels
Translate generated action labels without changing filenames or field names. Complete checkbox styles, resolution labels and native picker filters.
Co-authored-by: Codex <noreply@openai.com>
* M14: complete Japanese dialogs, panels, shortcuts and diagnostics
Expand Japanese from 49 to 1830 interface entries, expose language preferences in the Edit menu, preserve opaque template and history values, and localize known application diagnostics only at the UI boundary. Add control, persistence, template and desktop/web glyph regressions.
Co-authored-by: Codex <noreply@openai.com>
* i18n: Simplified Chinese UI language option
Adds Language::Zh alongside en/ja (--language zh, preferences switch,
persisted): full Chinese translation table covering every user-visible
label, generic Hans font fallback plumbing, and a build.rs skip-guard
plus test so Noto CJK is never embedded (AGENTS.md 1.1). Pairs with
storytold/craft-fonts#5 (ChillRoundF Regular, OFL-1.1 Adobe-free
chain), which supplies the Hans glyphs. Also fixes a startup deadlock
from translating inside lazy widget_info closures.
* i18n: add Brazilian Portuguese (pt-br) UI translation
Adds Language::PtBr (code pt-br, endonym Português (Brasil)) with serde rename so it persists as "pt-br", registers it in Language::ALL (Preferences ▸ Interface language and ui.set/ui.state) and translates the same 49 menu/command keys as the Japanese table. Extends the i18n tests (table parity, codes, persistence) and updates docs/localization.md.
* M14: strict catalog validation, one-pass fmt, translation assets
Fold in the infrastructure parts of #175:
- `fmt` fills placeholders in one pass, so inserted values (a file name
containing `{n}`) are never substituted again.
- Catalog validation rejects unknown escapes, unknown `@` contexts,
placeholder, ellipsis and plural-form mismatches and duplicates. The
bundled catalogs must have no errors (tests); at run time a bad line is
still only skipped and logged, so the rest of the catalog loads.
- `.tsv` files count as assets in `cargo xtask assets`, with a
`translation` kind that may never come from Adobe; ja.tsv gets its
ATTRIBUTION.toml entry.
- Control-channel test: switching the language keeps documents, their
dirty state and command ids.
Also make the drawing language thread-local, as in PhotoCraft, so
parallel kittest harnesses can't change each other's language.
Co-Authored-By: 才倾梦华V <185041488+CQMHV@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* M14: Brazilian Portuguese as a TSV catalog
Move #165's Portuguese table into crates/ui-egui/src/i18n/pt-br.tsv
(same 49 strings, plus Auto) and register it in LANGUAGES with
PhotoCraft's Portuguese plural rule (0 and 1 singular). Any Portuguese
locale (pt_BR, pt_PT, pt) follows the Brazilian catalog, as in
PhotoCraft. The catalog is an attributed asset (ATTRIBUTION.toml), and
the docs and parity notes list the new code.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* M14: Czech as a TSV catalog
Move #68's Czech table into crates/ui-egui/src/i18n/cs.tsv (PrintCraft
labels renamed PdfCraft, plus Auto) and register it with PhotoCraft's
Czech plural rule (one / few / other). #68's tests become catalog
tests: Czech covers every registered menu title and menu command label,
keeps ellipses, uses no three-dot ellipsis or stray whitespace, and only
leaves "OK" untranslated; the code persists and `cz` is rejected. The
source-scanning `.tr("…")` test is dropped: strings now go through tl!
and the catalog tests check consistency.
The font-coverage test moves to tests/fonts.rs and covers both Latin
catalogs (cs, pt-br) against Inter and JetBrains Mono. It keeps skrifa
as a dev-dependency (the version pdfcraft-fonts already uses): egui's
has_glyphs can't answer it, since a family with only the primary face
also uses that face as its replacement face.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* M14: route every UI string through the catalog; Japanese as ja.tsv
Port #141's wiring to the catalog API from land/106:
- `crate::i18n::tr(ui, s)` becomes `tl!(s)`, `tr_fmt`/`tr_template`
become `i18n::fmt(tl!(..), ..)`, and `Language::get`/`store` (the
egui temp-data copy of the language) are gone: the drawing language
is the per-thread `i18n::current()`, set from the preference at the
start of each frame. Worker threads (export, OCR, batch actions) take
the UI's language with them for their summaries.
- ja.rs (1,830 pairs) becomes ja.tsv, merged with the existing entries
and renamed PrintCraft -> PdfCraft. The 29 diagnostic templates of
errors.rs are dropped.
- `Language::diagnostic` and errors.rs are dropped: matching engine and
OS messages against English templates breaks silently whenever the
wording changes. Error details are shown as they are, inside the
translated frame ("{label} failed: {e}").
- History labels (`command_label`/`action_label`) move into i18n and keep
captured file and field names; registry menus prefer `@id` entries.
- Preferences: Auto plus every registered language, relabelled at once.
- Tests: Japanese covers every command and All tools entry; Japanese
labels have glyphs with the desktop faces and with the web subset
(with craft-fonts); history labels; #141's control tests, with the
error-detail expectation updated.
Co-Authored-By: shuiyue-cmyk <291597411+shuiyue-cmyk@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* M14: Preferences in the Edit menu, Cmd/Ctrl-comma
Split out of #141: register app.preferences in the Edit menu with the
platform's usual shortcut (Command-comma on macOS, Ctrl-comma
elsewhere), so the interface language can be found and changed with no
document open. Shortcut keys may now be punctuation (egui's key name
","). #141's control test drives the menu item, the language selector
and the shortcut in Japanese and English.
Co-Authored-By: sakamoto824 <sakamoto824@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* M14: Traditional Chinese as zh-hant.tsv
Reduce #140 to its translations: zh_tw.rs becomes
crates/ui-egui/src/i18n/zh-hant.tsv (PrintCraft renamed PdfCraft; the
29 errors.rs diagnostic templates dropped, as for Japanese) and one
registry row, code `zh-hant` (Taiwan vocabulary). Locales zh_TW, zh_HK,
zh_MO and zh-Hant-* resolve to it. #140's own copy of the UI wiring and
its errors.rs diagnostics are not taken: land/141 carries the wiring.
Tests: registration, locale mapping, every command label translated.
The catalog is an attributed asset; docs, ROADMAP and parity list it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* M14: Simplified Chinese as zh-hans.tsv
Reduce #112's translations to crates/ui-egui/src/i18n/zh-hans.tsv
(PrintCraft renamed PdfCraft) and one registry row, code `zh-hans`
(#112 used `zh`; `zh`, `zh_CN`, `zh_SG` and `zh-Hans` locales still
resolve to it through the system-language match). #112's own copy of
the UI wiring is superseded by land/141. Tests: registration, locale
mapping, menu coverage. The catalog is an attributed asset; docs,
ROADMAP and parity list it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* M14: CJK font order follows the UI language
From #112: `ui_cjk_fonts(prefer_hans)` orders the craft-fonts CJK faces
for the interface, and `theme::font_definitions_for` installs them. In
Simplified Chinese the `Hans` faces come first; otherwise the Japanese
ones (the previous behaviour). egui draws each character with the first
face that has it, so with Japanese first a Simplified-only character
(欢) would land in another face and sit on a different baseline. The
fonts are reinstalled when the language crosses that line (same family
names, so named fonts stay valid while the new set loads).
Changes from #112: within each group BIZ UDPGothic still leads, so a
serif listed first in the manifest (Shippori Mincho) never becomes the
Chinese-mode UI fallback; the unused `document_chinese_font` is left
out; and the crates/fonts/build.rs special case that skipped
"Noto Sans CJK SC" (and its test assertion and README line) is not
taken. Without CRAFT_FONTS_DIR, or without a `Hans` face, the order
logic yields only what exists: Chinese characters then show replacement
boxes and nothing panics (tests/fonts.rs, the fonts crate test, and the
control test switching ja -> zh-hans -> en).
Also document `--language` on the app's command line.
Co-Authored-By: shuiyue-cmyk <291597411+shuiyue-cmyk@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Petr Leo Compel <petrleocompel@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Simon Leblanc <contact@leblanc-simon.eu>
Co-authored-by: sakamoto824 <sakamoto824@gmail.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: shuiyue-cmyk <291597411+shuiyue-cmyk@users.noreply.github.com>
Co-authored-by: RicSchonfelder <ricschonfelder@icloud.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: 才倾梦华V <185041488+CQMHV@users.noreply.github.com>
|
||
|
|
ab4c87926a |
M14: expand Traditional Chinese UI, dialogs and language preferences (#140)
* i18n: Czech interface language
Adds Czech (Čeština, code `cs`) next to English and Japanese:
Preferences ▸ Interface language, `ui.set language cs`, `ui.state`
and persisted preferences all accept it.
- i18n: `Language::Cs`; `tr()` picks the language's table with a match
(English has none), panic-free. The CZECH table (83 entries) covers
every label passed to `.tr("…")` (main menu, View zoom/theme/side-panel
entries, Preferences dialog), every registered File/Edit/Pages/View/Help
command shown by `registry_menu`, and every key of the Japanese table.
Written clean-room from the English meaning, not from any Acrobat
localisation.
- Tests: Czech covers every registered menu title and label, every
`.tr("…")` literal found by scanning the crate's sources, and the
Japanese keys; nothing is left identical except an explicit KEEP_AS_IS
list ("OK"); ellipses match the source; keys are unique per language;
`cs` round-trips through set_option, ui.set/ui.state and persistence.
- theme: the font setup is split into `font_definitions()`, and a test
checks with skrifa (dev-dependency, already in the tree via egui) that
Inter Regular/Medium/SemiBold and JetBrains Mono draw all Czech letters
and every Czech translation, so nothing falls back to Shippori Mincho.
- The `language` error lists `cs`; docs/localization.md, the parity note
for misc.prefs-language and the ROADMAP log mention Czech.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* M14: translation catalogs, same system as PhotoCraft
Move the interface translations to PhotoCraft's i18n system: one TSV
catalog per language (crates/ui-egui/src/i18n/ja.tsv), the tl! macro,
entries keyed by command id, context or plural forms, and catalog tests
(format, duplicates, placeholders, command ids).
The 49 existing Japanese strings move over unchanged, plus "Auto". The
language preference now defaults to "auto" and follows the system
language (English when it has no catalog); "en" and "ja" still persist
and unknown values are rejected.
Update docs/localization.md, ROADMAP.md and the misc.prefs-language
parity entry.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* M14: add Traditional Chinese (Taiwan) UI localization
Co-Authored-By: Codex <noreply@openai.com>
* M14: translate shortcuts and expose language preferences in Edit menu
Add command-comma and verify language switching through the actual selector.
Co-authored-by: Codex <noreply@openai.com>
* M14: expand Traditional Chinese dialogs, panels and diagnostics
Preserve opaque values in translated templates and keep application diagnostics at the UI boundary. Extend native control and template regression coverage.
Co-authored-by: Codex <noreply@openai.com>
* M14: finish Traditional Chinese signature prompts and history labels
Translate generated action labels without changing filenames or field names. Complete checkbox styles, resolution labels and native picker filters.
Co-authored-by: Codex <noreply@openai.com>
* M14: complete Japanese dialogs, panels, shortcuts and diagnostics
Expand Japanese from 49 to 1830 interface entries, expose language preferences in the Edit menu, preserve opaque template and history values, and localize known application diagnostics only at the UI boundary. Add control, persistence, template and desktop/web glyph regressions.
Co-authored-by: Codex <noreply@openai.com>
* i18n: add Brazilian Portuguese (pt-br) UI translation
Adds Language::PtBr (code pt-br, endonym Português (Brasil)) with serde rename so it persists as "pt-br", registers it in Language::ALL (Preferences ▸ Interface language and ui.set/ui.state) and translates the same 49 menu/command keys as the Japanese table. Extends the i18n tests (table parity, codes, persistence) and updates docs/localization.md.
* M14: strict catalog validation, one-pass fmt, translation assets
Fold in the infrastructure parts of #175:
- `fmt` fills placeholders in one pass, so inserted values (a file name
containing `{n}`) are never substituted again.
- Catalog validation rejects unknown escapes, unknown `@` contexts,
placeholder, ellipsis and plural-form mismatches and duplicates. The
bundled catalogs must have no errors (tests); at run time a bad line is
still only skipped and logged, so the rest of the catalog loads.
- `.tsv` files count as assets in `cargo xtask assets`, with a
`translation` kind that may never come from Adobe; ja.tsv gets its
ATTRIBUTION.toml entry.
- Control-channel test: switching the language keeps documents, their
dirty state and command ids.
Also make the drawing language thread-local, as in PhotoCraft, so
parallel kittest harnesses can't change each other's language.
Co-Authored-By: 才倾梦华V <185041488+CQMHV@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* M14: Brazilian Portuguese as a TSV catalog
Move #165's Portuguese table into crates/ui-egui/src/i18n/pt-br.tsv
(same 49 strings, plus Auto) and register it in LANGUAGES with
PhotoCraft's Portuguese plural rule (0 and 1 singular). Any Portuguese
locale (pt_BR, pt_PT, pt) follows the Brazilian catalog, as in
PhotoCraft. The catalog is an attributed asset (ATTRIBUTION.toml), and
the docs and parity notes list the new code.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* M14: Czech as a TSV catalog
Move #68's Czech table into crates/ui-egui/src/i18n/cs.tsv (PrintCraft
labels renamed PdfCraft, plus Auto) and register it with PhotoCraft's
Czech plural rule (one / few / other). #68's tests become catalog
tests: Czech covers every registered menu title and menu command label,
keeps ellipses, uses no three-dot ellipsis or stray whitespace, and only
leaves "OK" untranslated; the code persists and `cz` is rejected. The
source-scanning `.tr("…")` test is dropped: strings now go through tl!
and the catalog tests check consistency.
The font-coverage test moves to tests/fonts.rs and covers both Latin
catalogs (cs, pt-br) against Inter and JetBrains Mono. It keeps skrifa
as a dev-dependency (the version pdfcraft-fonts already uses): egui's
has_glyphs can't answer it, since a family with only the primary face
also uses that face as its replacement face.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* M14: route every UI string through the catalog; Japanese as ja.tsv
Port #141's wiring to the catalog API from land/106:
- `crate::i18n::tr(ui, s)` becomes `tl!(s)`, `tr_fmt`/`tr_template`
become `i18n::fmt(tl!(..), ..)`, and `Language::get`/`store` (the
egui temp-data copy of the language) are gone: the drawing language
is the per-thread `i18n::current()`, set from the preference at the
start of each frame. Worker threads (export, OCR, batch actions) take
the UI's language with them for their summaries.
- ja.rs (1,830 pairs) becomes ja.tsv, merged with the existing entries
and renamed PrintCraft -> PdfCraft. The 29 diagnostic templates of
errors.rs are dropped.
- `Language::diagnostic` and errors.rs are dropped: matching engine and
OS messages against English templates breaks silently whenever the
wording changes. Error details are shown as they are, inside the
translated frame ("{label} failed: {e}").
- History labels (`command_label`/`action_label`) move into i18n and keep
captured file and field names; registry menus prefer `@id` entries.
- Preferences: Auto plus every registered language, relabelled at once.
- Tests: Japanese covers every command and All tools entry; Japanese
labels have glyphs with the desktop faces and with the web subset
(with craft-fonts); history labels; #141's control tests, with the
error-detail expectation updated.
Co-Authored-By: shuiyue-cmyk <291597411+shuiyue-cmyk@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* M14: Preferences in the Edit menu, Cmd/Ctrl-comma
Split out of #141: register app.preferences in the Edit menu with the
platform's usual shortcut (Command-comma on macOS, Ctrl-comma
elsewhere), so the interface language can be found and changed with no
document open. Shortcut keys may now be punctuation (egui's key name
","). #141's control test drives the menu item, the language selector
and the shortcut in Japanese and English.
Co-Authored-By: sakamoto824 <sakamoto824@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* M14: Traditional Chinese as zh-hant.tsv
Reduce #140 to its translations: zh_tw.rs becomes
crates/ui-egui/src/i18n/zh-hant.tsv (PrintCraft renamed PdfCraft; the
29 errors.rs diagnostic templates dropped, as for Japanese) and one
registry row, code `zh-hant` (Taiwan vocabulary). Locales zh_TW, zh_HK,
zh_MO and zh-Hant-* resolve to it. #140's own copy of the UI wiring and
its errors.rs diagnostics are not taken: land/141 carries the wiring.
Tests: registration, locale mapping, every command label translated.
The catalog is an attributed asset; docs, ROADMAP and parity list it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Petr Leo Compel <petrleocompel@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Simon Leblanc <contact@leblanc-simon.eu>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: RicSchonfelder <ricschonfelder@icloud.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: 才倾梦华V <185041488+CQMHV@users.noreply.github.com>
Co-authored-by: shuiyue-cmyk <291597411+shuiyue-cmyk@users.noreply.github.com>
|
||
|
|
a4340d34c8 |
M14: complete Japanese dialogs, panels, shortcuts and diagnostics (#141)
* i18n: Czech interface language
Adds Czech (Čeština, code `cs`) next to English and Japanese:
Preferences ▸ Interface language, `ui.set language cs`, `ui.state`
and persisted preferences all accept it.
- i18n: `Language::Cs`; `tr()` picks the language's table with a match
(English has none), panic-free. The CZECH table (83 entries) covers
every label passed to `.tr("…")` (main menu, View zoom/theme/side-panel
entries, Preferences dialog), every registered File/Edit/Pages/View/Help
command shown by `registry_menu`, and every key of the Japanese table.
Written clean-room from the English meaning, not from any Acrobat
localisation.
- Tests: Czech covers every registered menu title and label, every
`.tr("…")` literal found by scanning the crate's sources, and the
Japanese keys; nothing is left identical except an explicit KEEP_AS_IS
list ("OK"); ellipses match the source; keys are unique per language;
`cs` round-trips through set_option, ui.set/ui.state and persistence.
- theme: the font setup is split into `font_definitions()`, and a test
checks with skrifa (dev-dependency, already in the tree via egui) that
Inter Regular/Medium/SemiBold and JetBrains Mono draw all Czech letters
and every Czech translation, so nothing falls back to Shippori Mincho.
- The `language` error lists `cs`; docs/localization.md, the parity note
for misc.prefs-language and the ROADMAP log mention Czech.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* M14: translation catalogs, same system as PhotoCraft
Move the interface translations to PhotoCraft's i18n system: one TSV
catalog per language (crates/ui-egui/src/i18n/ja.tsv), the tl! macro,
entries keyed by command id, context or plural forms, and catalog tests
(format, duplicates, placeholders, command ids).
The 49 existing Japanese strings move over unchanged, plus "Auto". The
language preference now defaults to "auto" and follows the system
language (English when it has no catalog); "en" and "ja" still persist
and unknown values are rejected.
Update docs/localization.md, ROADMAP.md and the misc.prefs-language
parity entry.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* M14: complete Japanese dialogs, panels, shortcuts and diagnostics
Expand Japanese from 49 to 1830 interface entries, expose language preferences in the Edit menu, preserve opaque template and history values, and localize known application diagnostics only at the UI boundary. Add control, persistence, template and desktop/web glyph regressions.
Co-authored-by: Codex <noreply@openai.com>
* i18n: add Brazilian Portuguese (pt-br) UI translation
Adds Language::PtBr (code pt-br, endonym Português (Brasil)) with serde rename so it persists as "pt-br", registers it in Language::ALL (Preferences ▸ Interface language and ui.set/ui.state) and translates the same 49 menu/command keys as the Japanese table. Extends the i18n tests (table parity, codes, persistence) and updates docs/localization.md.
* M14: strict catalog validation, one-pass fmt, translation assets
Fold in the infrastructure parts of #175:
- `fmt` fills placeholders in one pass, so inserted values (a file name
containing `{n}`) are never substituted again.
- Catalog validation rejects unknown escapes, unknown `@` contexts,
placeholder, ellipsis and plural-form mismatches and duplicates. The
bundled catalogs must have no errors (tests); at run time a bad line is
still only skipped and logged, so the rest of the catalog loads.
- `.tsv` files count as assets in `cargo xtask assets`, with a
`translation` kind that may never come from Adobe; ja.tsv gets its
ATTRIBUTION.toml entry.
- Control-channel test: switching the language keeps documents, their
dirty state and command ids.
Also make the drawing language thread-local, as in PhotoCraft, so
parallel kittest harnesses can't change each other's language.
Co-Authored-By: 才倾梦华V <185041488+CQMHV@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* M14: Brazilian Portuguese as a TSV catalog
Move #165's Portuguese table into crates/ui-egui/src/i18n/pt-br.tsv
(same 49 strings, plus Auto) and register it in LANGUAGES with
PhotoCraft's Portuguese plural rule (0 and 1 singular). Any Portuguese
locale (pt_BR, pt_PT, pt) follows the Brazilian catalog, as in
PhotoCraft. The catalog is an attributed asset (ATTRIBUTION.toml), and
the docs and parity notes list the new code.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* M14: Czech as a TSV catalog
Move #68's Czech table into crates/ui-egui/src/i18n/cs.tsv (PrintCraft
labels renamed PdfCraft, plus Auto) and register it with PhotoCraft's
Czech plural rule (one / few / other). #68's tests become catalog
tests: Czech covers every registered menu title and menu command label,
keeps ellipses, uses no three-dot ellipsis or stray whitespace, and only
leaves "OK" untranslated; the code persists and `cz` is rejected. The
source-scanning `.tr("…")` test is dropped: strings now go through tl!
and the catalog tests check consistency.
The font-coverage test moves to tests/fonts.rs and covers both Latin
catalogs (cs, pt-br) against Inter and JetBrains Mono. It keeps skrifa
as a dev-dependency (the version pdfcraft-fonts already uses): egui's
has_glyphs can't answer it, since a family with only the primary face
also uses that face as its replacement face.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* M14: route every UI string through the catalog; Japanese as ja.tsv
Port #141's wiring to the catalog API from land/106:
- `crate::i18n::tr(ui, s)` becomes `tl!(s)`, `tr_fmt`/`tr_template`
become `i18n::fmt(tl!(..), ..)`, and `Language::get`/`store` (the
egui temp-data copy of the language) are gone: the drawing language
is the per-thread `i18n::current()`, set from the preference at the
start of each frame. Worker threads (export, OCR, batch actions) take
the UI's language with them for their summaries.
- ja.rs (1,830 pairs) becomes ja.tsv, merged with the existing entries
and renamed PrintCraft -> PdfCraft. The 29 diagnostic templates of
errors.rs are dropped.
- `Language::diagnostic` and errors.rs are dropped: matching engine and
OS messages against English templates breaks silently whenever the
wording changes. Error details are shown as they are, inside the
translated frame ("{label} failed: {e}").
- History labels (`command_label`/`action_label`) move into i18n and keep
captured file and field names; registry menus prefer `@id` entries.
- Preferences: Auto plus every registered language, relabelled at once.
- Tests: Japanese covers every command and All tools entry; Japanese
labels have glyphs with the desktop faces and with the web subset
(with craft-fonts); history labels; #141's control tests, with the
error-detail expectation updated.
Co-Authored-By: shuiyue-cmyk <291597411+shuiyue-cmyk@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* M14: Preferences in the Edit menu, Cmd/Ctrl-comma
Split out of #141: register app.preferences in the Edit menu with the
platform's usual shortcut (Command-comma on macOS, Ctrl-comma
elsewhere), so the interface language can be found and changed with no
document open. Shortcut keys may now be punctuation (egui's key name
","). #141's control test drives the menu item, the language selector
and the shortcut in Japanese and English.
Co-Authored-By: sakamoto824 <sakamoto824@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Petr Leo Compel <petrleocompel@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Simon Leblanc <contact@leblanc-simon.eu>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: RicSchonfelder <ricschonfelder@icloud.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: 才倾梦华V <185041488+CQMHV@users.noreply.github.com>
Co-authored-by: shuiyue-cmyk <291597411+shuiyue-cmyk@users.noreply.github.com>
|
||
|
|
f0f923b82e |
i18n: Czech interface language (#68)
* i18n: Czech interface language
Adds Czech (Čeština, code `cs`) next to English and Japanese:
Preferences ▸ Interface language, `ui.set language cs`, `ui.state`
and persisted preferences all accept it.
- i18n: `Language::Cs`; `tr()` picks the language's table with a match
(English has none), panic-free. The CZECH table (83 entries) covers
every label passed to `.tr("…")` (main menu, View zoom/theme/side-panel
entries, Preferences dialog), every registered File/Edit/Pages/View/Help
command shown by `registry_menu`, and every key of the Japanese table.
Written clean-room from the English meaning, not from any Acrobat
localisation.
- Tests: Czech covers every registered menu title and label, every
`.tr("…")` literal found by scanning the crate's sources, and the
Japanese keys; nothing is left identical except an explicit KEEP_AS_IS
list ("OK"); ellipses match the source; keys are unique per language;
`cs` round-trips through set_option, ui.set/ui.state and persistence.
- theme: the font setup is split into `font_definitions()`, and a test
checks with skrifa (dev-dependency, already in the tree via egui) that
Inter Regular/Medium/SemiBold and JetBrains Mono draw all Czech letters
and every Czech translation, so nothing falls back to Shippori Mincho.
- The `language` error lists `cs`; docs/localization.md, the parity note
for misc.prefs-language and the ROADMAP log mention Czech.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* M14: translation catalogs, same system as PhotoCraft
Move the interface translations to PhotoCraft's i18n system: one TSV
catalog per language (crates/ui-egui/src/i18n/ja.tsv), the tl! macro,
entries keyed by command id, context or plural forms, and catalog tests
(format, duplicates, placeholders, command ids).
The 49 existing Japanese strings move over unchanged, plus "Auto". The
language preference now defaults to "auto" and follows the system
language (English when it has no catalog); "en" and "ja" still persist
and unknown values are rejected.
Update docs/localization.md, ROADMAP.md and the misc.prefs-language
parity entry.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* i18n: add Brazilian Portuguese (pt-br) UI translation
Adds Language::PtBr (code pt-br, endonym Português (Brasil)) with serde rename so it persists as "pt-br", registers it in Language::ALL (Preferences ▸ Interface language and ui.set/ui.state) and translates the same 49 menu/command keys as the Japanese table. Extends the i18n tests (table parity, codes, persistence) and updates docs/localization.md.
* M14: strict catalog validation, one-pass fmt, translation assets
Fold in the infrastructure parts of #175:
- `fmt` fills placeholders in one pass, so inserted values (a file name
containing `{n}`) are never substituted again.
- Catalog validation rejects unknown escapes, unknown `@` contexts,
placeholder, ellipsis and plural-form mismatches and duplicates. The
bundled catalogs must have no errors (tests); at run time a bad line is
still only skipped and logged, so the rest of the catalog loads.
- `.tsv` files count as assets in `cargo xtask assets`, with a
`translation` kind that may never come from Adobe; ja.tsv gets its
ATTRIBUTION.toml entry.
- Control-channel test: switching the language keeps documents, their
dirty state and command ids.
Also make the drawing language thread-local, as in PhotoCraft, so
parallel kittest harnesses can't change each other's language.
Co-Authored-By: 才倾梦华V <185041488+CQMHV@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* M14: Brazilian Portuguese as a TSV catalog
Move #165's Portuguese table into crates/ui-egui/src/i18n/pt-br.tsv
(same 49 strings, plus Auto) and register it in LANGUAGES with
PhotoCraft's Portuguese plural rule (0 and 1 singular). Any Portuguese
locale (pt_BR, pt_PT, pt) follows the Brazilian catalog, as in
PhotoCraft. The catalog is an attributed asset (ATTRIBUTION.toml), and
the docs and parity notes list the new code.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* M14: Czech as a TSV catalog
Move #68's Czech table into crates/ui-egui/src/i18n/cs.tsv (PrintCraft
labels renamed PdfCraft, plus Auto) and register it with PhotoCraft's
Czech plural rule (one / few / other). #68's tests become catalog
tests: Czech covers every registered menu title and menu command label,
keeps ellipses, uses no three-dot ellipsis or stray whitespace, and only
leaves "OK" untranslated; the code persists and `cz` is rejected. The
source-scanning `.tr("…")` test is dropped: strings now go through tl!
and the catalog tests check consistency.
The font-coverage test moves to tests/fonts.rs and covers both Latin
catalogs (cs, pt-br) against Inter and JetBrains Mono. It keeps skrifa
as a dev-dependency (the version pdfcraft-fonts already uses): egui's
has_glyphs can't answer it, since a family with only the primary face
also uses that face as its replacement face.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Simon Leblanc <contact@leblanc-simon.eu>
Co-authored-by: RicSchonfelder <ricschonfelder@icloud.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: 才倾梦华V <185041488+CQMHV@users.noreply.github.com>
|
||
|
|
41734376be |
i18n: add Brazilian Portuguese (pt-br) UI translation (#165)
* M14: translation catalogs, same system as PhotoCraft Move the interface translations to PhotoCraft's i18n system: one TSV catalog per language (crates/ui-egui/src/i18n/ja.tsv), the tl! macro, entries keyed by command id, context or plural forms, and catalog tests (format, duplicates, placeholders, command ids). The 49 existing Japanese strings move over unchanged, plus "Auto". The language preference now defaults to "auto" and follows the system language (English when it has no catalog); "en" and "ja" still persist and unknown values are rejected. Update docs/localization.md, ROADMAP.md and the misc.prefs-language parity entry. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * i18n: add Brazilian Portuguese (pt-br) UI translation Adds Language::PtBr (code pt-br, endonym Português (Brasil)) with serde rename so it persists as "pt-br", registers it in Language::ALL (Preferences ▸ Interface language and ui.set/ui.state) and translates the same 49 menu/command keys as the Japanese table. Extends the i18n tests (table parity, codes, persistence) and updates docs/localization.md. * M14: strict catalog validation, one-pass fmt, translation assets Fold in the infrastructure parts of #175: - `fmt` fills placeholders in one pass, so inserted values (a file name containing `{n}`) are never substituted again. - Catalog validation rejects unknown escapes, unknown `@` contexts, placeholder, ellipsis and plural-form mismatches and duplicates. The bundled catalogs must have no errors (tests); at run time a bad line is still only skipped and logged, so the rest of the catalog loads. - `.tsv` files count as assets in `cargo xtask assets`, with a `translation` kind that may never come from Adobe; ja.tsv gets its ATTRIBUTION.toml entry. - Control-channel test: switching the language keeps documents, their dirty state and command ids. Also make the drawing language thread-local, as in PhotoCraft, so parallel kittest harnesses can't change each other's language. Co-Authored-By: 才倾梦华V <185041488+CQMHV@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * M14: Brazilian Portuguese as a TSV catalog Move #165's Portuguese table into crates/ui-egui/src/i18n/pt-br.tsv (same 49 strings, plus Auto) and register it in LANGUAGES with PhotoCraft's Portuguese plural rule (0 and 1 singular). Any Portuguese locale (pt_BR, pt_PT, pt) follows the Brazilian catalog, as in PhotoCraft. The catalog is an attributed asset (ATTRIBUTION.toml), and the docs and parity notes list the new code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Simon Leblanc <contact@leblanc-simon.eu> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: 才倾梦华V <185041488+CQMHV@users.noreply.github.com> |
||
|
|
a07243c8ff |
M14: translation catalogs, same system as PhotoCraft (#106)
* M14: translation catalogs, same system as PhotoCraft Move the interface translations to PhotoCraft's i18n system: one TSV catalog per language (crates/ui-egui/src/i18n/ja.tsv), the tl! macro, entries keyed by command id, context or plural forms, and catalog tests (format, duplicates, placeholders, command ids). The 49 existing Japanese strings move over unchanged, plus "Auto". The language preference now defaults to "auto" and follows the system language (English when it has no catalog); "en" and "ja" still persist and unknown values are rejected. Update docs/localization.md, ROADMAP.md and the misc.prefs-language parity entry. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * M14: strict catalog validation, one-pass fmt, translation assets Fold in the infrastructure parts of #175: - `fmt` fills placeholders in one pass, so inserted values (a file name containing `{n}`) are never substituted again. - Catalog validation rejects unknown escapes, unknown `@` contexts, placeholder, ellipsis and plural-form mismatches and duplicates. The bundled catalogs must have no errors (tests); at run time a bad line is still only skipped and logged, so the rest of the catalog loads. - `.tsv` files count as assets in `cargo xtask assets`, with a `translation` kind that may never come from Adobe; ja.tsv gets its ATTRIBUTION.toml entry. - Control-channel test: switching the language keeps documents, their dirty state and command ids. Also make the drawing language thread-local, as in PhotoCraft, so parallel kittest harnesses can't change each other's language. Co-Authored-By: 才倾梦华V <185041488+CQMHV@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: 才倾梦华V <185041488+CQMHV@users.noreply.github.com> |
||
|
|
41e214c318 |
About: contributor and model credits compiled in (grab bag + table, self-submitted names) (#196)
* About: contributors compiled in from contributors.json (grab bag + table, sortable, name toggle) Adds About ▸ Contributors and About ▸ Models tabs per the craftrules standard (standards/contributors.md): contributors/contributors.json is generated by craftrules/scripts/contributors.py and baked into the binary by crates/ui-egui/build.rs as static tables; nothing is read at run time. Each contributor line: @username, opt-in display/real name, PRs, commits, lines +/−/Δ, binary assets +/−, first/last commit. Sortable by every column; alphabetical ignores case and the leading @. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Contributors: self-submitted names in contributors/people.toml; AGENTS.md credit-yourself section build.rs reads contributors/people.toml next to the generated contributors.json and shows a name only for entries the generator verified as written by that person (craftrules standards/contributors.md). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Contributors: regenerate (verified people.toml entries) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
2f872f7e7d |
M10.5: add cut-and-stack page order for single-sided printing (#197)
Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
d7b93b56b5 |
Forms: run show/hide-a-field button actions (#192)
* Forms: push buttons run their show/hide-a-field action A push button whose Mouse Up action is a Hide action (ISO 32000-2 §12.6.4.11) did nothing when clicked: the action wasn't read, so the button counted as having no action at all. Buttons now read it: /T as a field name, a reference to a field or one of its widgets (named by its fully qualified name, following /Parent with a cycle guard), or an array of these; /H false shows instead of hiding. Clicking hides or shows the named fields and their kids through the same edit that a script setting field.display uses, so it is one undo step. A hidden widget (Hidden or NoView flag) no longer takes clicks, tab stops, hover frames or field highlights. That also covers fields that a script hides. Tests: forms push_buttons_read_hide_actions (names, references, arrays, /H, a /Parent cycle) and ui-egui hide_actions_hide_and_show_fields. parity: edit.action-show-hide-field is shipped. * Forms: push buttons with an action show the pointing hand Hovering a push button showed the "not allowed" cursor, although clicking it runs its action. Buttons that aren't read-only and have an action now show the pointing hand; read-only buttons and buttons with no action keep the "not allowed" cursor. Test: hide_actions_hide_and_show_fields checks the cursor (it fails without this change). * Clippy: needless borrow in the hide-action test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
89896af68b |
Fix saved signature replacement and long-name clipping (#163) (#178)
* #163: allow replacing saved signatures and render long names fully Add saved signature and initials previews with change/remove/add controls in Fill & Sign. Preserve placed annotations and saved values on cancellation. Use bounded line outlines for long names and fit typed placements to 150 pt. Co-authored-by: Codex <noreply@openai.com> * cargo fmt after the rename Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Codex <noreply@openai.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
d0991a8cd5 |
macOS: File ▸ Open and file pickers no longer crash the app (#145)
* macOS: File ▸ Open and file pickers no longer crash the app
A blocking rfd::FileDialog runs -[NSOpenPanel runModal], a nested run loop
inside winit's event handler. A mouse or gesture event that arrives while it
spins re-enters the handler, winit panics ("tried to handle event while
another event is currently being handled"), and because the panic can't
unwind out of the AppKit block callback the app aborts.
Open, Combine, Insert pages, Replace pages and OCR ▸ multiple files now use
rfd::AsyncFileDialog, which starts the panel as a sheet and returns at once.
A worker thread waits for the choice and the app uses it on a later frame.
Insert and Replace remember their document and drop a pick that arrives after
the user switched tabs. pick_override lets tests and automation drive these
commands.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Cargo.lock: pollster for pdfcraft-ui-egui
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
|
||
|
|
54df6b933e |
Persist default workspace mode for newly opened PDFs (#172)
* M14: persist the default PDF workspace mode Keep the saved workspace preference separate from current mode and explicit session overrides. Preserve CLI ordering and PDF Initial View behavior, and cover persistence, Preferences, and control-channel use. Co-authored-by: Codex <noreply@openai.com> * Opening a PDF keeps a closed left panel and an unchanged workspace's tool panel The default workspace is applied only when it differs from the current mode, and never reopens a left panel the user or `--left closed` closed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: James B. Gower <gowerblake2012@gmail.com> Co-authored-by: Codex <noreply@openai.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
5387c346a5 |
M3: Add middle-click autoscroll on Linux (#111)
* M3: add middle-click autoscroll Co-Authored-By: Codex <noreply@openai.com> * M3: keep middle-click autoscroll toggled after release Keep scrolling enabled after releasing the wheel, including when the pointer moves before release. Anchor speed to the initial press event, so movement later in the same frame cannot shift the starting point. Cover toggle behavior and increasing speed with distance in both views. Co-Authored-By: Codex <noreply@openai.com> * M3: increase middle-click autoscroll speed Double scrolling speed at the same pointer displacement and raise the speed cap proportionally, preserving the dead zone and click toggle. Keep the sustained-scroll fixture visible at the faster response. Co-Authored-By: Codex <noreply@openai.com> * M3: match Chromium middle-click scrolling Use a 15-point dead zone and Chromium's nonlinear distance-to-speed curve in the document viewer and Organize Pages. Request continuous redraws so scrolling follows elapsed frame time and preserves fractional movement. Keep click toggling, cancellation, and the existing marker unchanged. Cover reference speeds, pathological inputs, pause/resume, and consistent travel at 30/60/120/144 fps with different display scales and document zoom. Co-Authored-By: Codex <noreply@openai.com> * M3: leave README unchanged in autoscroll PR Remove the feature bullet so README matches upstream main. Keep the feature explanation in the pull request description. Co-Authored-By: Codex <noreply@openai.com> * M3: limit custom middle-click scrolling to Linux Return before handling input on other targets so their existing middle-button behavior, widgets, cursor, and Escape shortcuts stay intact. Restrict the autoscroll UI regressions to Linux and add unsupported-platform input coverage plus a non-Linux UI regression for the native CI matrix. Co-Authored-By: Codex <noreply@openai.com> * ROADMAP: drop the log line about merging the rename Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Codex <noreply@openai.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
9a1bf44e07 |
Render: round page rasters up so the last partial row and column are drawn (#102) (#114)
An A4 page (595.28×841.89 pt) rendered 595×841 at 72 dpi, where pdftoppm gives 596×842: the whole-page render passed hayro no size, and hayro floors the scaled page size. Content touching the right or bottom edge lost its last partial pixel at every resolution. - render: new device_pixels(pt, scale) rounds up (ignoring float noise up to 1/100 px, so Letter at 150 dpi stays 1275 px). The untiled render passes it to hayro as the width and height, still within MAX_SIDE. The partial edge pixels are anti-aliased against the white page, as the clip to the crop box already does. - ui-egui: the canvas tile grid uses the same helper instead of round(), so tiled pages get tiles for the partial row and column too. - No change to vendored hayro. Regression test: a fractional MediaBox renders 596×842 with partly covered edge pixels, 2481 px wide at 300 dpi, and a corner tile matches the whole-page render. It failed (595×841) before the fix. Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
4454af3742 |
Select all pages with Ctrl+A in Organize Pages (#113)
* M4: select all pages in Organize with Ctrl+A Route Select all to the page grid in Organize while preserving viewer text selection and keyboard ownership for text inputs, dialogs and the palette. Keep the current page as the selection anchor for subsequent range selection. Co-Authored-By: Codex <noreply@openai.com> * Use the pdfcraft crate names after the rename Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Codex <noreply@openai.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
e15e5ebce0 |
Checkboxes with an indirect /AP /N state dictionary are drawn (#180) (#181)
hayro-syntax recovered a stream with a wrong /Length by scanning ahead for the bytes `stream`. An indirect appearance-state dictionary (`<< /Yes … /Off … >>`, as Quartz writes a checkbox's `/AP /N`) was read as whatever stream came later in the file. That dictionary has no `/BBox`, so the appearance was dropped and the widget was skipped. `stream` now has to be the next token. A wrong `/Length` still recovers, and the renderer already selects the `/AS` state once `/N` is a dictionary. Fixes #180 Co-authored-by: Eri Bastos <eb@fluxmail.xyz> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
38c62bda3e |
Automation: file-writing tools no longer advertise read-only (#130) (#138)
doc_export_data, accessibility_report and image_save each write to their required path and replace an existing file there, but the tool table marked all three read-only, so MCP clients were told the calls change no file. They now carry the destructive flag the table defines for a tool that overwrites files, and a test pins the three names. Co-authored-by: Mohammed Munzir <Mohammed.Munzir@iga.gov.bh> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
ff1a8b85dc |
Edit text: keep the tokens a content stream shares with its neighbours (#154)
A page's /Contents array is one content stream in pieces, and a writer may split it between any two tokens (ISO 32000-2 §7.8.2): a TJ array can end one piece with its operator starting the next, and a marked-content dictionary can close at the start of the next piece. Replacing a line or rewriting a paragraph re-serialized the parsed operators of the line's piece, and the parser keeps neither operands left at the end of a piece nor a dictionary's closing >> at its start. The edit dropped them: the text after the edited line vanished, and the page was left with an operator without operands (TJ, BDC), which other readers reject. The piece is now rewritten by splicing: only the replaced, inserted and removed operators change, and every other byte is copied as it was. Co-authored-by: EthanSuquet <292885762+EthanSuquet@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
d63bc7ad98 |
Edit text: the graphics state carries over between a page's content streams (#150)
A page's /Contents array is one content stream in pieces (ISO 32000-2 §7.8.2), so a cm, a q that is still open, the font and the colour set in one piece still apply in the next. Edit text read every piece with a fresh state. AutoCAD scales the whole page in its first stream (0.12 0 0 0.12 0 0 cm) and draws in the following ones, so the text boxes of its plans landed far off the page and no label could be edited. Co-authored-by: Oliver Schiemann <228913879+Checkov23@users.noreply.github.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
6c89837541 |
fix: print: read lpstat untranslated so printers show on non-English macOS (#101)
* Print: read lpstat untranslated so printers show on non-English macOS On a Mac whose interface language isn't English, the Print dialog listed no printers. `lpstat -p -d` follows the UI language (here Polish: "drukarka … jest bezczynna", "domyślny cel systemowy: …"), and parse_lpstat only knows the English "printer " and "system default destination:" prefixes, so it matched nothing. - lpstat now runs with LC_ALL=C and LANG=C, which is enough on Linux. macOS CUPS ignores both and follows AppleLanguages unless SOFTWARE is set, in which case it uses LANG, so SOFTWARE is set too. - New test: localized output parses to nothing, and the command sets all three variables. Checked on macOS with a Polish UI: only SOFTWARE=1 LANG=C gives English output, and the dialog now lists every printer with the right default. lp's error messages stay localized; they are shown, never parsed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Use the PdfCraft name in lpstat's SOFTWARE variable Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
a2d9a9480c |
M1: validate object-number ranges before reading cross references (#92)
Reject invalid xref ranges before arithmetic or narrowing casts, allowing the existing reconstruction path to preserve intact objects and record the repair reason. Cover both tables and streams through Document::open. Co-authored-by: storybored379 <storybored379@users.noreply.github.com> Co-authored-by: OpenAI Codex <noreply@openai.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
cc7694be99 |
Security: ask before opening links from documents (#90, #91) (#104)
A document could open any address with no confirmation: a link click, a push button's URI action, a one-line button script mapped to a URI action, or app.launchURL from JavaScript (on by default). That allowed sending form data to an outside site, or handing file:, smb: and app-handler addresses to the operating system. All four now go through PdfCraftApp::request_document_url: - engine::links::document_url accepts only http, https and mailto, up to 2048 characters, and refuses control, zero-width, bidi-override and non-space whitespace characters, malformed schemes and web addresses without a host. - Accepted addresses wait in an "Open this web page?" dialog that shows the real host (engine::links::host drops user:password@, and treats \ like / as browsers do, so https://a.example\@b.example shows a.example) and the full address. Cancel is the highlighted button; Escape and clicking outside cancel. While one request waits, further ones are dropped, so a script can't stack dialogs. - Refused addresses are reported in a notice and never opened. PdfCraft's own links (Help, About, updates) still use open_url. Preferences > JavaScript now says scripts need permission to open a web page. Tests: engine unit tests for the policy and host parsing, and crates/ui-egui/tests/document_links.rs driving the link, button, button script, app.launchURL and Escape paths (all fail against the old code). Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
70da0179a6 |
Security: --root confinement leaks and escapes (#136, #137) (#164)
* Automation: --root refuses every outside path the same way (#136) With a root set, `resolve` canonicalized a path before checking it against the root, so an existing outside path got "is outside the allowed directory" while a missing one got the OS error ("No such file or directory", "Not a directory", "invalid path"): a confined MCP client or script could probe which files and folders exist anywhere on the disk. Containment is now decided before anything about an outside path is reported: - `..` is resolved by name first (as Windows does), so a path can't pass through an outside folder on its way back in; - on Windows another network share or device namespace (`\\host\share`, `\\?\UNC\...`, `\\.\...`) is refused by name, without contacting it; - the deepest existing ancestor, links followed, must be inside the root before anything is said about the rest; - a broken link is refused the same way, so whether its target exists stays hidden too. Every outside path now gets `<path> is outside the allowed directory <root>`, existing or not, file or folder, read or write. Filesystem errors such as "not found" are still reported for paths inside the root. Behaviour change: on Unix, `..` now means the parent by name, as on Windows (`link/../x` is `x` beside `link`), and a write such as `new/../x.pdf` that stays in the root now succeeds instead of failing with "'..' is not allowed here". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * CLI: script images stay inside --root (#137) `pdfcraft-cli run --script steps.json --root DIR` wrote each step's `"out"` PNG with `std::fs::write`, never through the root check the tools use: an absolute `"out"` created or replaced any file the process could write, and a relative one landed in the working directory instead of the root. The image is now saved through the new `Automation::write_output`, which uses the same resolver and atomic write as a tool's own output: outside paths get the usual "is outside the allowed directory" refusal (the step fails, nothing is written), relative paths resolve inside the root, and without --root the path is written as given, in place as before (so `/dev/stdout` still works). `run <tool> --out` goes the same way. Found while testing this, the same "." problem in two tools: - a write path naming a folder, such as "." (the root itself), staged its temporary file beside that folder, i.e. outside the root for ".", overwriting and then deleting any file there called `.root.pdfcraft-tmp`. `write_atomic` now refuses a folder before writing anything; - `image_save` adds an extension to a path without one, so "." became `root.png` beside the root. It now refuses a folder first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Automation: files named after a document stay in the output folder Found while reviewing the --root fixes for #136 and #137. Folder outputs name their files after the document and joined that name straight onto the resolved folder, without checking the result against the root: - `doc_export_images` and `doc_export_all_images` used the whole document name, so `doc_create` with `name: "../../escape"` followed by `doc_export_images` wrote `escape_page_1.png` outside the root (an absolute name went anywhere); - `page_extract` (separate files), `doc_split`, `ocr_recognize_files` and `action_run` took the last component, but on Windows a name such as `x/C:evil.pdf` gives `C:evil`, and `folder.join("C:evil-page1.pdf")` replaces the folder with a path relative to the current directory on C:. Every such name now goes through `child()`, which turns separators, colons and control characters into `_` (and prefixes a name of only dots), so the file is always a direct child of the folder. `with_root` also refuses a root that is not a folder. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
7920f99f7a |
Redact: operators split across a page's content streams are redacted whole (#153)
* Redact: operators split across a page's content streams are redacted whole A page's /Contents array is one content stream in pieces, and a writer may split it between any two tokens (ISO 32000-2 §7.8.2). Some office and layout exporters do: a TJ array ends one piece and its TJ operator starts the next, or a marked-content dictionary closes in the next piece. Redaction parsed each piece on its own, so such an operator was cut off from its operands. Glyphs shown by a split TJ were never checked against the marks: Apply drew the black box but left the text in the file, and Verify reported nothing under the box. When the piece holding the operands was rewritten for another mark, they were dropped, so the text after it vanished and the next piece began with an operator without operands, which other readers reject. process() now parses the pieces joined, gives each operator back to the piece its keyword is in, and rewrites every piece a split operator spans. Pages whose pieces split only between operators are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Use the pdfcraft crate names after the rename Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: EthanSuquet <292885762+EthanSuquet@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
d3db99818f |
Rename PrintCraft to PdfCraft (#174)
The app, crates, binaries, bundle id, packaging, icons, screenshots and docs are now PdfCraft / pdfcraft (repo storytold/pdfcraft). On first launch the desktop app moves the PrintCraft settings and crash-recovery folders to the new name, and both apps read settings saved under the old storage key, so upgrades keep recent files, preferences and unsaved work. The signing test fixtures keep their PrintCraft subject names (they are binary). Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
64fc90498b |
Merge pull request #120 from storytold/docs/97-98-100
Docs: automation README, internal craftrules links, docs/releasing.md (#97, #98, #100) |
||
|
|
26a3e63bb9 |
Merge pull request #118 from storytold/fix/94-check-box-styles
Forms: check box and radio button styles (#94) |
||
|
|
bddc800c87 |
Docs: say the craftrules standards are internal, and that the rules here are the binding ones (#98)
AGENTS.md, CLAUDE.md and crates/fonts/README.md linked to storytold/craftrules, which outside contributors can't open. The links stay for team members, now labelled internal, and AGENTS.md says up front that every rule a contributor here must follow is stated in AGENTS.md and CLAUDE.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
5e74f95017 |
Docs: the automation README describes the tools and MCP resources that ship (#97)
Its Status line said 38 tools and listed form authoring, redaction tools and MCP page-image resources as planned; 123 tools and all three ship. It now names the areas the tools cover and points to printcraft-cli tools for the current list instead of a count that goes stale, and the MCP section lists the resources/* methods and resource templates. ROADMAP's M13 row said 91 tools. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
cdb3f3d106 |
Forms: check box and radio button styles (#94)
Field Properties > Options > Check Box Style (Button Style for radio buttons): Check, Circle, Cross, Diamond, Square or Star, as Acrobat offers. The choice is stored in each widget's /MK /CA with Acrobat's ZapfDingbats codes (4 l 8 u n H), so other viewers draw the same mark, and PrintCraft draws it as paths, needing no symbol font. Styles set by other apps are read back too; the defaults stay a check for check boxes and a circle for radio buttons. - forms: CheckStyle, check_style(), FieldProps::check_style; set_props writes /MK /CA (resolving an indirect /MK so its colours survive) and redraws; text and choice fields refuse a style. - engine: Document::field_check_style. - automation: form_set_props takes check_style, form_fields reports it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |