mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-27 11:37:59 -06:00
8f47b6ffe82eff09bdc05f8aebe21572400a769e
65
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
606c560d24 |
Use chrono instead of hand-rolled UTC conversion (#3824)
* Use chrono for UTC/calendar conversion instead of hand-rolled copies Replaces the hand-rolled civil_from_days/days_from_civil copies and the leap-year/month math around them (log timestamp, PDF /Info dates, ASN.1 UTCTime/GeneralizedTime, FormCalc Date/Num2Date, the Combine modified-time stamp, the form date picker, and the demo-PDF timestamps) with chrono as a workspace dependency. The engine's existing chrono (clock) is lifted to the workspace. Every output format is unchanged and pinned by tests. CLAUDE.md now says time goes through chrono. Follow-up to storytold/cadcraft#1785 * Guard chrono date paths: no panic on overflow, no '+' on 5-digit years Systemic chrono follow-ups to the UTC/calendar PR: - logging::timestamp and combine_ui::utc_stamp dropped chrono's From<SystemTime> (which unwraps internally and panics on a clock past year 262143) for the fallible DateTime::from_timestamp, reading an out-of-range clock as the epoch, as the old clamping code did. - timestamp, utc_stamp, pdf_date and the xtask Stamp now write the year with {:04} instead of %Y, which prefixes a '+' before years past 9999; the old hand-rolled code wrote the plain zero-padded year. - forms_ui calendar navigation and length, and formcalc days_in, use checked_add for the December year successor so y + 1 can't overflow at i32::MAX / i64::MAX (reachable from a parsed or persisted date). Docstrings added to the chrono-touched civil_from_days and Time::from_unix for the docstring-coverage gate. Tests cover the out-of-range-epoch and 10000-01-01 cases for each. * Don't build an unrepresentable SystemTime in the far-future test UNIX_EPOCH + Duration::from_secs(u64::MAX / 2) overflows Windows' SystemTime and panicked before timestamp() ran, failing the Windows test job. Build it with checked_add and only assert where it exists. * Keep FormCalc dates exact past chrono's range MAX_DAYS accepts day numbers up to 1e8 (years past 270000), but NaiveDate stops near ±262143, so Num2Date(99000000) and Format fell back to 1900-01-01. days_from_civil, civil_from_days and days_in now have chrono convert within the date's 400-year Gregorian cycle and add whole cycles back (via i128), which gives the pre-chrono results for every year. Tests cover accepted inputs past chrono's range on both sides of the epoch, through Num2Date and Format, with values from the old formula. * Bump rustls-webpki and webpki-root-certs for RUSTSEC-2026-0382/0383 rustls-webpki 0.103.15 -> 0.103.16 and webpki-root-certs 1.0.9 -> 1.0.10 (both semver-compatible, lockfile only) drop the distrusted emSign intermediates and root. Only these two entries change; cargo deny check advisories passes. Upstream main fails the same check. * logging/cos/sign/combine/demo: keep extended-year dates, no epoch fallback For consistency with the stricter no-epoch-fallback rule now agreed across the sibling chrono PRs (wordcraft#3427, filmcraft#2662): `DateTime::from_timestamp(..).unwrap_or_default()` changes a timestamp the old code converted past chrono's year 262143 range — it collapses to 1970-01-01 instead of the real proleptic-Gregorian date. The FormCalc Num2Date path was already fixed (FredApps approved); this applies the same fix to every remaining conversion. Each site uses the 400-year Gregorian cycle (146_097 days) with chrono as the only calendar logic: chrono converts the day within one cycle, whole cycles carry in the year (i128, saturated into i64). PDF/ASN.1 format-specific year handling is kept exactly as the old code had it. Verified the cycle result equals the old formula across a wide range and at the extremes. - apps/pdfcraft/src/logging.rs `timestamp`: cycle `civil_from_days(u64)`; pre-1970 epoch clamp and ms/time-of-day format kept. Pins: +10_000_000_000_000 s -> 318857-05-20T17:46:40.000Z (was 1970); checked_add u64::MAX/2 s -> 292277026596-12-04T15:30:07.000Z (was epoch). - crates/cos/src/writer.rs `pdf_date` (i64): cycle converter, no year clamp (old code wrote `y` as-is, so a negative year keeps its sign). Pins: pdf_date(10_000_000_000_000)="D:3188570520174640Z", pdf_date(-10_000_000_000_000)="D:-3149180813061320Z". - crates/sign/src/der.rs `Time::from_unix` (i64): cycle converter; the `year` field is a u32, so the year is cast `as u32` (wrapping) exactly as the old code did — ASN.1 year width preserved byte-for-byte. Pins: from_unix(10e12).year=318857, from_unix(-10e12).year=4_294_652_378, from_unix(i64::MAX).year=219_250_468, from_unix(i64::MIN).year=4_075_720_767. - crates/ui-egui/src/combine_ui.rs `utc_stamp` (u64): cycle converter; pre-1970 epoch clamp kept. Pins: utc_stamp(10e12)="318857-05-20 17:46 UTC", u64::MAX/2 s="292277026596-12-04 15:30 UTC". - xtask/src/demo_pdf/pdf.rs `Stamp::from_unix` (i64): cycle converter, no year clamp. Pins: from_unix(10e12).iso="318857-05-20T17:46:40Z"/.pdf="D:3188570520174640Z", from_unix(-10e12).iso="-314918-08-13T06:13:20Z". cos/sign/xtask built, tested (pdf_dates_are_utc_and_byte_identical, der::tests::times, stamps_round_trip), fmt and clippy clean. pdfcraft-ui-egui and the pdfcraft app can't link here (unrelated LLVM intrinsic bug in the rten-gemm dependency, 1.98.1), so their logging and combine_ui functions + tests were copied verbatim into a scratch crate with chrono 0.4.45 and pass there; fmt --check clean on all five packages. CI builds these on all platforms. * Don't build a year-318857 SystemTime unchecked in the logging test Windows' SystemTime only reaches about year 30828, so UNIX_EPOCH + Duration::from_secs(10_000_000_000_000) panicked before timestamp() ran and failed the Windows test job. Build it with checked_add and assert where the platform can hold it. The year-10000 case fits everywhere and stays unconditional. * Name the 400-year range extension as the one exception to the time rule The rule said never to hand-roll calendar math, but the range fix (shifting by whole 400-year Gregorian cycles so dates past chrono's year range keep their old output) is cycle arithmetic, so the rule and the code disagreed. The rule now names that exception: the library still computes month, day and time, and only whole cycles are carried in the year. * Say the library computes month and day; time-of-day is plain arithmetic Narrows the range-extension wording: the date library computes the calendar fields, and splitting seconds into hours, minutes and seconds is ordinary arithmetic, which is what the code does. |
||
|
|
2f21f920a0 |
fix(windows): name the single-instance socket by the session id, so a launch without SESSIONNAME finds its session's window (#3338) (#3340)
* fix(app): a launch without SESSIONNAME hands its files to the console session's window (#3338) Task Scheduler, services and some remote shells start PdfCraft without SESSIONNAME, so the launch looked for instance.sock while the window opened from Explorer listens on instance-Console.sock, and a second window opened. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Gv2WwdgjmBz33GWkwgNn3Q * fix(windows): name the single-instance socket by the session id, not SESSIONNAME (#3338) Review on #3340 (earlvanze): falling back to the console's socket when SESSIONNAME is unset could hand a file from a Remote Desktop session to a console window of the same user in another session. The socket is now named by the session id in the process token (winsafe, no unsafe): a launch from Task Scheduler runs in the user's session and finds its window, and different sessions never share a socket. SESSIONNAME names it only if the id can't be read. Tested with a real scheduled task: on main the task's launch (no SESSIONNAME, session 1) opened a second window; with this change the file went to the running window in session 1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Gv2WwdgjmBz33GWkwgNn3Q --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
b6b02a1b27 |
Merge upstream main 1952a8880 into next
ROADMAP.md, docs/roadmap.md: union of both sides' lines, each once. parity/acrobat-features.toml: the XFA entry has both sides' evidence and notes (#2604 page areas, #2813 CLI layout). |
||
|
|
6ce3bc198b |
fix(gpu): request adapter-supported device limits (#2383) (#2433)
Clamp the surface device descriptor to the adapter's capabilities while preserving its maximum 2D texture extent. Cover V3D shader variables, lower GL capabilities, alignment ordering and capable-device defaults. Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
7f718ecc62 |
Merge upstream main 50f261d70 into next
ROADMAP.md, docs/roadmap.md: union of both sides' log lines and milestone rows, each line once. parity/acrobat-features.toml, crates/xfdf/README.md: next's text, a superset of main's (#1917, #2686 plus #2688). crates/edit/src/tests.rs: both sides' new tests (#2558 and #3084). crates/ui-egui/src/editing.rs: the picked picture is cleared on every edit, and a new drawing commits its raster (#3149). |
||
|
|
265403813b |
fix(links): resolve remote document paths with the shared path check
GoToR file specifications go through craft-docpath: network, device and object-manager paths (\??\, /??/) are refused without touching the filesystem, relative targets resolve against the source document's folder, and a result that is not absolute is refused rather than read against the working directory. Files named on the command line open under their absolute path, so their relative links keep resolving. |
||
|
|
30c174be9e |
M3: closing PdfCraft while minimized no longer saves a 0 x 0 window
eframe stores the window's size and place just before App::save. A minimized window has none: Windows reports 0 x 0 at (-32000, -32000), and the next start opened at egui's 64-point floor in the screen's corner (seen on a 6K display at 225%: a 144 x 206 px window). While the window is minimized the app now puts back the last settings it was shown with (or none), and a window that opens at that floor from settings saved before this fix is given a usable size. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RUNQSPVXx3dDBabRZHAnjz |
||
|
|
afa8e1843c |
M3: Wayland: stop retaining Ctrl zoom for pixel-only wheel input (#1569)
* M3: release Ctrl zoom for pixel-only Wayland wheel events Use the reported scroll source to avoid unterminated finger gestures for wheel, wheel-tilt and continuous input. Exercise the production policy through egui, including modifier release and preserved finger momentum. Assisted-by: OpenAI Codex * M3: keep Cargo lockfile dependency ordering canonical Record the dependency ordering produced by the completed workspace and wasm gates. Assisted-by: OpenAI Codex --------- Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
3805be7669 |
M14: store XDG preferences in configuration directory (#1395) (#1536)
Generated with Codex for @DWAA1660. Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
41809cf765 |
fix(macos): Cmd+Q asks about unsaved changes (#1136) (#1533)
* fix(macos): Cmd+Q asks about unsaved changes (#1136) winit's default app menu sends Quit (Cmd+Q) straight to -[NSApplication terminate:], so PdfCraft quit without asking and unsaved edits were lost; the close button and Dock > Quit already ask. Install craft-quit's app menu (storytold/craft-libs#21) from the app creator. Its Quit sends ViewportCommand::Close, which guard_quit handles like the close button. If the menu can't be built, winit's menu stays and a warning is logged. craft-quit is pinned to the head of craft-libs#21 until #5 and #21 are merged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(macos): closing the window quits through terminate: (#1136) On Touch Bar Macs the close button crashed PdfCraft (exit 133, -[_NSTouchBarFinderObservation invalidate]): eframe drops the window inside AppKit's run loop. craft_quit::on_close_request, right after guard_quit in `logic`, cancels an allowed close and quits through -[NSApplication terminate:] instead. A close that guard_quit holds for the unsaved-changes prompt is left alone. Elsewhere it does nothing. craft-quit moves to the workspace dependencies, pinned to craft-libs main (f58bc1a, with #5 and #21 merged) instead of #21's head. The on_close_request change and its Touch Bar test are from jmacek. 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> |
||
|
|
d1a4fb18c5 |
fix(ui): on macOS, dragging a tab moves the tab, not the window (#2347)
On macOS the tab strip is the title bar: a transparent title bar over a full-size content view. AppKit asks the view under the pointer whether a press there can move the window (`mouseDownCanMoveWindow`), and NSView answers YES for a view that isn't opaque. winit's view didn't override it, so AppKit moved the whole window for any drag that began in the 28 pt band before egui saw it, and the tab reordering from #1551 never got the drag. vendor/winit now answers with the window's `movable_by_window_background` attribute (NO unless asked). The empty strip still moves the window through `ViewportCommand::StartDrag`, and double-clicking it still maximizes. Patch (12) in vendor/README.md, kept in the build by `tests/winit_macos_title_bar_drag.rs`. Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
32a397f27a |
fix(linux): open files from later launches in the running PdfCraft (#1726) (#2037)
The single-instance handoff (#282, #715) ran only on Windows; every other platform returned Claim::Alone, so on Linux each xdg-open or file-manager open started a second process and window. Linux and the BSDs now use the same Unix-socket exchange through std's UnixListener/UnixStream, with the socket in $XDG_RUNTIME_DIR/pdfcraft (never /tmp) and named after WAYLAND_DISPLAY or DISPLAY, so a launch on another display opens its own window. No XDG_RUNTIME_DIR: as before. macOS is unchanged (Apple events). winit ignores Focus on Wayland, so on a native Wayland window the handoff also requests user attention (xdg-activation), which desktops show in the taskbar. The hand-off, together and stale-socket tests now run on Linux/BSD too; a new test covers the display socket names. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
7437bde378 |
M3: draw a GNOME-style title bar on Wayland (#553) (#1530)
GNOME's Wayland compositor draws no window frames (it offers no zxdg_decoration_manager_v1), so winit drew its own fallback title bar, which ignores GNOME's theme, dark mode and button layout. PdfCraft now asks the compositor at launch (apps/pdfcraft/src/decorations.rs) and, where it draws no frames, turns winit's frame off and makes the tab strip the header bar, as in GNOME's own apps (crates/ui-egui/src/window_controls.rs): - Minimize, maximize/restore and close in Adwaita's style (round buttons, symbolic glyphs drawn in code, no new assets), in the order and on the side named by org.gnome.desktop.wm.preferences button-layout, read through gsettings; Fedora's default `appmenu:close` shows only close. Hidden in full screen. - Resize zones along the window's edges and L-shaped corners, above the rest of the UI so a press there resizes instead of reaching a scrollbar; a hairline outlines the window while it isn't maximized. - Preferences > Use system title bar (shown only where the app draws the frame) gives the window back winit's frame at once, without a restart (part of #1001). Named as in VectorCraft and PhotoCraft, but GNOME draws no title bars for apps, so on GNOME this is winit's basic frame, not GNOME's own; the tooltip says so. Saved as `system_title_bar`; `system-title-bar` on the control channel. - PDFCRAFT_TITLEBAR=app|system overrides the detection. KDE, Sway, Hyprland, COSMIC and X11 offer server-side decorations and keep the frame their desktop draws; macOS and Windows are unchanged. Not done: right-clicking the header bar doesn't open GNOME's window menu (egui has no command for it), the window has no shadow or rounded corners, the resize zones lie just inside the window rather than outside it, and Windows still uses its system frame (the rest of #1001). The new labels (Minimize, Maximize, Restore, Use system title bar and its tooltip) were added to all 21 translation catalogs. These translations have not been checked by anyone fluent in those languages and should be reviewed. The catalogs' hashes in ATTRIBUTION.toml are updated. Made with Claude Code (Claude Opus 5.5, medium effort). It passes the workspace quality gates (fmt, clippy, cargo test --workspace, xtask assets) and was checked by hand on Fedora 44 GNOME (light and dark, maximized and not, the preference toggled live), but it has not been exhaustively regression tested. Co-authored-by: bmh129 <11478682+bmh129@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
929ae1155b |
Windows: open a path a launcher passed unquoted and split at its spaces (#1456)
Some programs start the PDF reader with the path unquoted
(`pdfcraft.exe P:\Archive\00042A26 Contract.PDF`), so Windows splits it
into `P:\Archive\00042A26` and `Contract.PDF` and neither opens
("Couldn't read 00042A26: os error 2"). Acrobat accepts this. Rejoin
consecutive arguments when one names no file but it and the ones after it,
joined with a space, do (longest run first); everything else is kept as
given. Done before the single-instance hand-off, so files sent to a running
PdfCraft are rejoined too.
Co-authored-by: magdale76 <16279778+magdale76@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
e072bb8317 |
macOS: start on wgpu on Intel Metal by skipping indirect-call validation (#1627)
wgpu validates indirect draw and dispatch arguments with a compute shader it
compiles when the device is created. Metal on an Intel Iris Pro (MacBookPro11,3,
macOS 12.7.6) fails to compile it ("Compiler encountered an internal error" in
MTLCompilerService), the device is lost, and the window falls back to OpenGL.
PdfCraft draws and dispatches nothing indirectly, so leave the check out of the
instance flags. WGPU_VALIDATION_INDIRECT_CALL=1 turns it back on. VectorCraft
does the same (storytold/vectorcraft#651).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
13fbb2f6fb |
M0: label local builds in --version, About and the package release (#1529)
A build compiled with PDFCRAFT_BUILD_LABEL=<label> shows "0.7.1 (<label>)"
in `pdfcraft --version`, `pdfcraft-cli --version`, Help > About and the
first log line (pdfcraft_engine::build_info). The version itself is
unchanged, so the update check, the MCP server info and the User-Agent
still carry the release version.
packaging/linux/package.sh puts the label in the file names
(pdfcraft-0.7.1-<label>-linux-x86_64.rpm) and makes the .rpm/.deb release
`1.<label>` (nfpm `release: ${RELEASE}`, 1 when unlabelled). rpm sorts
0.7.1-1.<label> after 0.7.1-1 and before 0.7.2-1, so a locally built
package upgrades over the installed release and the next release replaces
it. packaging/env.sh accepts only letters, digits and inner dots, which
are valid in both rpm and deb release fields.
Unlabelled builds are unchanged.
Made with Claude Code (Claude Opus 5.5, medium effort). It passes the
workspace quality gates (fmt, clippy, cargo test --workspace, xtask assets)
and was checked by hand on Fedora 44, but it has not been exhaustively
regression tested.
Co-authored-by: bmh129 <11478682+bmh129@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
8cacb44728 |
M3: follow Windows' Animation effects setting; add an Animation effects preference (#1235)
* M3: follow Windows' Animation effects setting; Smooth scrolling preference With Settings > Accessibility > Visual effects > Animation effects turned off, the wheel still eased: egui spreads a coarse wheel step over about a quarter of a second, for scrolling and for Ctrl-wheel zooming, and has no switch for it. - crates/ui-egui/src/motion.rs reads each frame's wheel input the way egui does, minus the easing, and hands it to the scroll areas and the views (zoom_delta). A test sums what egui eases and compares it with what is read at once, and fails if egui stops easing. - With the system asking for no animations, egui's own animations take no time and the progress card's bar steps without its sweep. - The desktop app asks WinRT's UISettings.AnimationsEnabled at launch and every two seconds of use, so a change applies without a restart. - Preferences has Smooth scrolling: Use system setting (default), On, Off. On or Off decides the wheel's scrolling and zooming, and scrolling to a place, whatever the system says. Saved, settable as smooth_scrolling on the control channel, translated in all 20 catalogs. How far a notch scrolls or zooms is unchanged. macOS, Linux and the browser aren't asked for their setting yet; Off works everywhere. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Credit Jeremy Yao by name in About (contributors/people.toml) My own entry: real name and display name "Jeremy Yao". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * M3: rename Smooth scrolling to Animation effects; it decides every animation The preference also governs Ctrl-wheel zooming, so "Smooth scrolling" described only part of it. As "Animation effects" (Use system setting, On, Off) it now decides every animation in the window, the way the Windows setting of the same name does: - the wheel's scrolling and zooming, and scrolling to a place (as before); - egui's own animations, the progress card's sweep, and the chrome's hover and press washes (new on main, with durations of their own), which used to follow the system alone or not at all. Saved and set on the control channel as animation_effects (system, on, off); the unreleased smooth_scrolling name is gone. The three labels are retranslated in all 20 catalogs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Tests: the app's two real-GPU tests take turns (FreeBSD SIGSEGV) FreeBSD CI died with SIGSEGV in the pdfcraft binary's tests on this branch ( |
||
|
|
f5452cf126 |
Linux: prefer server DPI over panel physical DPI in vendored winit (#1150)
* fix(x11): prefer server DPI over panel physical DPI in winit Without Xft DPI, use the X server screen dimensions before the output physical density. Xorg can run a dense panel at 96 DPI; EDID sizes alone make only winit apps appear zoomed. Supersedes #908: preserves Xft.dpi priority, configured HiDPI screens, and WINIT_X11_SCALE_FACTOR override (including randr). Falls back to RandR output density when server dimensions are missing. Adds default_dpi_factor() with 4 unit tests plus vendored-copy guard test in apps/pdfcraft/tests/winit_x11_dpi.rs. * vendor/README: record the winit X11 DPI patch as (11) --------- Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
315a0cc458 |
Issue #432: global Units preference (mm, cm, in, pt), defaulting to the system region (#463)
* 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> |
||
|
|
49217aec6c |
fix(theme): complete Linux System appearance tracking (#1017)
* fix(theme): complete Linux System appearance tracking Follow up on #881 for #446 with the standard numeric appearance portal setting and event-driven updates that wake idle windows. Keep manual modes independent, cache their desktop updates without repainting, and cancel the worker on close. Retain inherited native window appearance and populate both egui style branches with the chosen palette. Preserve saved System, the Light default and unknown source fallback, and avoid desktop session access in headless UI contexts. Assisted-by: Codex (gpt-6.1-sol) * Desktop theme: keep the GNOME colour-scheme fallback XFCE needs The portal tracker read only org.freedesktop.appearance, which XFCE's portal doesn't answer (it proxies the GSettings schemas). It now also reads and subscribes to org.gnome.desktop.interface color-scheme and uses it when the freedesktop setting states no preference, as main's reader did. KDE answers only the freedesktop one, which wins when it states a preference. 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> |
||
|
|
1651069e7a |
Viewer: Continuous scrolling is an independent checkbox over the base layout (#752) (#896)
* Viewer: touchpad scrolling tracks the finger 1:1 (#759) A precision touchpad sends pixel-precise `MouseWheel` events. On Windows they arrive with no `TouchPhase::Start`, so egui never takes its no-smoothing "in touch" path and eases any delta of 8 px or more over several frames, leaving the page a frame behind the finger. The page view now applies precise (Point-unit) wheel input itself, 1:1, through the same un-eased scroll path as keyboard and middle-button auto-scroll, and suppresses egui's eased copy for a short window so its internal easing tail neither doubles nor trails the motion. A coarse mouse wheel (Line/Page units) keeps egui's smoothing, which is pleasant for chunky notches, and single-page paging still owns the vertical axis. Regression test: one 200 px precise swipe scrolls the full 200 px at once (an eased path would ramp there over ~0.1 s) and leaves no drift. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Viewer: Continuous scrolling is an independent checkbox over the base layout (#752) Page display matches Acrobat: the base layout (Single page or Two-page view) is a radio choice, and Continuous scrolling is a separate checkbox, so every combination is reachable. This also adds the previously missing "Two-page view, one spread at a time" mode. `PageLayout` now has just `Single` and `TwoUp`; a separate `continuous` flag on the view (and on the default view preferences) decides whether it scrolls. A single `display_token` ("single" | "continuous" | "two-up" | "two-page") serialises both for settings and the control channel, keeping the earlier `continuous`/`single`/`two-up` names working and adding `two-page` for the new mode. The PDF's own page-layout request now maps to the right (layout, continuous) pair, including plain TwoUp. - View ▸ Page display and the rail menu show two radios plus a Continuous scrolling checkbox; Preferences gains a default Continuous checkbox. - `view.continuous` toggles the checkbox; `view.layout.single` / `view.layout.two_up` switch the base layout without touching it. The former `view.layout.continuous` command is gone (its state is Single + continuous); the `layout`/`default-layout` options and a new `continuous` option cover scripting. - Not scrolling, the wheel and the arrow/Page keys turn whole spreads in Two-page view, and only the current page or spread is laid out. Tests: new `notches_turn_two_page_spreads`, `two_page_view_shows_one_spread_at_a_time_until_continuous`, and updated layout/command/persistence tests; the `view()` helper takes `continuous`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Parity: the continuous-scrolling feature points at view.continuous --------- Co-authored-by: Justin John <babyjjbabyjj549@gmail.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
24cb6018b6 |
macOS: the reopen-last-session preference starts from NSQuitAlwaysKeepsWindows (#871) (#1053)
System Settings > Desktop & Dock > "Close windows when quitting an application" is the system's own version of Preferences > "Reopen the files that were open when PdfCraft last closed". Read it once at launch (defaults read -g, 500 ms guard, as text_scale.rs does for gsettings) and use it as the preference's starting value, before the saved preferences are restored. A choice made in Preferences is persisted and still wins; other systems keep starting off. Co-authored-by: Tristan von Kries <tristan@MacBook-Pro-von-Tristan.local> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
f1e3a04587 |
Return renderer initialization errors to the OpenGL startup fallback (#1042)
* M13: report unavailable symbolic-link fixtures in hostile-argument tests Only include the planted link escape when symlink creation succeeds. On non-elevated Windows the fixture can fail with OS error 1314; exercising its nonexistent path otherwise produces false confinement leaks. Keep all remaining traversal and absolute-path cases and report the skip. Co-authored-by: Codex (GPT-6) <noreply@openai.com> * M3: return renderer initialization errors to the OpenGL startup fallback Guard renderer creation with the existing scoped GPU-error handler and return CreateRenderer instead of invoking wgpu's default panic handler. This lets Auto startup reach its existing OpenGL retry before app creation. Add a real-device invalid/valid pipeline regression and Windows startup reproduction notes. The host-specific DX12 shader compiler error itself is not fixed; explicit renderer choices and the non-winit path are unchanged. Co-authored-by: Codex (GPT-6) <noreply@openai.com> * Leave the symlink-test fix to #949, which covers the same ground --------- Co-authored-by: Codex (GPT-6) <noreply@openai.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
ea1df6e1cb |
Linux: StartupWMClass is the window's app id (#1087)
* Linux: StartupWMClass is the window's app id The desktop entry said StartupWMClass=pdfcraft, but the window carries the app id ai.storyteller.pdfcraft (APP_ID) on Wayland and under X11 alike, so nothing that matches a window to its launcher by that key ever matched it: on X11 the launch's startup notification can't end when the window maps. VectorCraft, GridCraft and WordCraft already use the app id. A test reads the entry and compares it with APP_ID. * Contributors: add atoslins (Atos Lins) --------- Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
86e74ec121 |
Linux: draw on the GPU the Wayland compositor draws with (#1088)
On a hybrid laptop whose Wayland compositor runs on the discrete GPU (Hyprland with AQ_DRM_DEVICES naming the NVIDIA card first), the built-in panel still hangs off the integrated GPU, so the panel rule (#8) picked it, and the window stayed empty: the compositor took the frames and showed nothing, with no error anywhere. Which GPU drives a display stands in for which GPU the compositor draws with. Under Wayland the compositor says so itself: the main_device of its linux-dmabuf feedback (version 4), the device it imports client buffers on, which Mesa's Vulkan device-select layer reads for the same purpose. compositor_gpu.rs asks for it on a short-lived connection, maps the dev_t to PCI ids through /sys/dev/char, and pick_adapter ranks that adapter first. No answer (X11, an older compositor, a GPU no adapter matches) leaves the existing rules in charge, and WGPU_POWER_PREF still overrides. wayland-client and wayland-protocols are already in the tree through winit; they become direct Linux-only dependencies. |
||
|
|
bbbe60015c |
M10: Windows printing prints at actual size, off the UI thread (follow-up to #847) (#1007)
* M10: Windows printing prints at actual size, off the UI thread (follow-up to #847) - Sheets print at 100% on the driver's paper closest to their size, landscape when wide, instead of being shrunk into .NET's default 1-inch margins (about 76% on Letter) and never rotated. Each sheet is capped at ~64 MP so a poster-sized sheet can't exhaust memory. - PowerShell runs with CREATE_NO_WINDOW: the release app has no console, so Windows opened one over the app for the whole job. - The Print dialog lists printers in the background (the desktop app's printer_source) and spools jobs on a worker thread, so Ctrl+P and long jobs no longer freeze the UI. A list arriving late preselects the default only if the user hasn't picked a printer. - Parity evidence named a test that #847 renamed; xtask parity passes again. The raster-only limit is now stated in the parity notes and README. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * M10: refresh catalog hashes in ATTRIBUTION.toml for the new print strings Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
892f0b78f0 |
M14: give Windows PDF files a dedicated document icon (#890)
* M14: give Windows PDF files a dedicated document icon Embed the document icon as resource 2 through the existing Windows resource build and select it in both PDF association registrations. Keep the asset reproducible and carry its required licence notices in Windows packages. Formatting, strict Clippy, 1,842 existing workspace tests, all 30 WASM crate checks, assets, layering and parity pass. The Windows x64 release package checks pass, and the contributor confirmed the icon in Explorer. Co-authored-by: Codex <noreply@openai.com> * Refresh catalog hashes after merging main --------- Co-authored-by: Codex <noreply@openai.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
8196d6f45a |
M14: interface size preference; Auto follows GNOME text scaling (#902)
* M14: interface size follows the system text scale Preferences ▸ Interface size scales the whole interface (75–250 %, setting `ui_scale`, control option `ui-scale`). Auto, the default, follows the desktop's text scaling: on Linux and the BSDs the desktop app reads GNOME's `text-scaling-factor` once at launch (through `gsettings`, with a 500 ms limit; anything unusable counts as 100 %). With Large Text (1.25) on a 1× display, the interface no longer draws 13 px labels that show their pixels next to the system's text. The size is egui's zoom factor, so `pixels_per_point` grows with it and page rasters stay one texel per screen pixel (`canvas::render_scale`). It is applied only when it changes, so a zoom set elsewhere (the test harness's `pixels_per_point`) isn't undone every frame. egui's own ⌘+/⌘−/⌘0 interface zoom is turned off so those keys stay page zoom. Tests and headless screenshots keep 100 %: only the desktop app sets `system_text_scale`. New labels are translated in every catalog, and the catalogs' hashes in ATTRIBUTION.toml are updated. * M14: offer a 50 % interface size On a display the system reports at 200 %, the smallest size the menu offered gave a 150 % interface (#456). 50 % brings it back to 100 %; settings and the control channel already accepted it. --------- Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
5268047a33 |
Combine files: a thumbnail grid, card sizes and card actions (refs #611) (#777)
Combine files shows each file as a card (the default; Grid/List kept in the settings, --combine-view): the first page it adds, stacked when it adds more, its name shortened in the middle. Cards select (click, Ctrl, Shift, arrows by card and by row), drag to reorder (one undo step; the grid scrolls at its edges) and share the list's toolbar, selection and history. Locked, unreadable, bad-range and unmeasurable files say why. Thumbnails render off the UI thread, only for cards in view: at most three render threads at once, counted until they exit (no watchdog replacements), a 32 MB texture budget, keys that never show another page, password or file, and one heavy step per frame on the web. Cards are sized from 60% to 200% (toolbar slider and buttons, Ctrl +/-/0, Ctrl+wheel or pinch over the grid, kept in the settings, --combine-zoom), keeping the card under the pointer in place. Hovering a card shows its actions over the top of its page: remove that file, and a magnifier that shows it large, flipping through the pages it adds; the same on a right click, and Space by keyboard. The toolbar wraps between groups at narrow widths. RenderPool gains retire(), without_replacements() and threads() so a caller can bound the render threads that exist at once. Co-authored-by: Andrew Taylor <andrew.taylor@tayeng.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
b21045de6e |
Single instance: enable tab handoff in Windows portable mode (#889) (#891)
* Single instance: enable tab handoff in Windows portable mode (#889) * Single instance: a portable copy's socket stays in %LOCALAPPDATA%, under its own name (cherry picked from commit c7db1e80a374e80a11504efefbaac680dfa5e872) --------- Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
e742e29771 |
Windows: single instance, launches started together share one window (#282, #317) (#715)
* Windows: open files in the running PdfCraft instead of a new window each time (#282, #317) Outlook attachments, Explorer double-clicks and Open With start pdfcraft.exe with the file, so every file got its own process and window. Now the first PdfCraft listens on a Unix domain socket in %LOCALAPPDATA%\PdfCraft (or a portable copy's data folder), one per Windows session. A later launch that only names files sends their absolute paths, waits for "ok" and exits; the running app opens them as tabs through the existing OsEvent::Open path (the one macOS Apple events use) and comes to the front. Anything else keeps today's behaviour: --new-window, any other option, a launch without files, or any failure to hand over within 5 seconds opens a window of its own. Other platforms are unchanged. The socket lives in the user's profile, so other accounts can't create, replace or reach it. uds_windows (already in the lockfile through zbus) provides it: std has no Unix sockets on Windows and the workspace forbids unsafe. A socket left by a crash is replaced on the next start. * Windows: launches started together share one window (#282, #317) Several files selected in Explorer and opened at once start one process per file at the same moment. A launch could then find the socket bound by another launch that was not listening yet: connecting failed, clearing the socket failed with "Access is denied", and the launch opened a window of its own. Launches now take turns through a lock on a file beside the socket, held only while a launch finds out whether it is the first. A socket that cannot be cleared because another launch holds it is tried again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016es5tszobbb1yZ7JeozJXL * Windows: a portable copy doesn't hand files to another instance Its data folder may be writable by other accounts, so a socket there could leak or inject file paths. Builds on art804's single-instance work in #602. Co-authored-by: art804 <7828385+art804@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: art804 <7828385+art804@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
4125e4138b | #570: declare PMv2 DPI awareness in manifest (#725) | ||
|
|
198ef4dc92 |
Issue #727: tell the user when GitHub rate-limits update checks (#731)
Help ▸ Check for updates showed a bare "couldn't reach GitHub
(http status: 403)" when the anonymous hourly quota ran out. Read
error statuses in latest_release instead: a 403 now reports the
shared-quota limit with the retry time from X-RateLimit-Reset
("try again in about N minutes"), falling back to "try again later"
when the header is missing or spent. Other statuses keep the old
message. Adds rate_limit_messages_name_the_wait.
Co-authored-by: ShauryaSwarup <shaurya.swarup@gmail.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
|
||
|
|
4339f5b9ed | #570: log desktop renderer details (#726) | ||
|
|
68e91d4815 |
macOS: the first Apple Korean key in a fresh PdfCraft composes (winit#4744) (#680)
* macOS: IME text committed after the marked text is cleared isn't lost (winit#4650)
winit 0.30.13 reports insertText as Ime::Commit only while marked text is
left, and after any commit it swallows the next key. Real paths where that
loses text:
- input methods that end a composition with unmarkText, then insertText
(rust-windowing/winit#3925): the committed text was dropped;
- Apple Korean 2-Set, for the key typed after a syllable: on 한 then 5 it
calls setMarkedText("한"), insertText("한"), setMarkedText(""),
insertText("5"), so the 5 (or ;, or any key that ends the syllable) was
dropped;
- a commit made outside a key press (a candidate clicked with the mouse,
dictation): no key follows to reset the state, so the next key (for
example Backspace) was swallowed.
vendor/winit now commits when a composition was started (pending_commit,
set in setMarkedText, cleared by the commit and when IME is disallowed);
other insertText calls still reach the app as KeyboardInput. A commit
outside keyDown clears the marked text and returns to the ground state.
validAttributesForMarkedText lists the underline and marked-clause
attributes, which some input methods need before they compose.
This is rust-windowing/winit#4650, the 0.30 backport of #4651 (merged on
winit master), plus the part of #4748 that fixes #4651's out-of-key-press
case (#4748's other parts are not included). One correction: upstream
spells the underline attribute "NSUnderlineStyle", but AppKit's
NSUnderlineStyleAttributeName is "NSUnderline" (printed on macOS 14), so
the AppKit value is used.
Checked with the real input methods on macOS 14.7.1 (a scratch winit app fed
key events through AppKit): with the patch 한+5 and 한+; commit the 5 and ;,
unmarkText-then-insertText commits, and Backspace after a commit outside a
key press is delivered; ABC typing, held keys, Japanese (日本, あ), Cangjie
(日) and Korean composition give the same events as before. A test fails if
a re-vendored winit loses the patch.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* macOS: the first Apple Korean key in a fresh PdfCraft composes (winit#4744)
On the first key after PdfCraft starts, Apple Korean 2-Set can answer
interpretKeyEvents with insertText and the raw compatibility jamo instead of
setMarkedText. winit sends that key to the app as an ordinary key, and the
next keys compose without it: g k s Space typed ㅎㅏㄴ instead of 한 (Search,
form fields, comments; rust-windowing/winit#3095).
vendor/winit now ports rust-windowing/winit#4744: keyDown interprets that
one key a second time, synchronously, before any KeyboardInput is sent, and
only when every condition holds: Apple Korean, IME allowed and not yet
composing, no pending commit or marked text, no Command/Control/Option, not
a repeat, the key window's first responder, the only callback is one
insertText of that same single jamo, and focus and input source unchanged
afterwards. The decision is ImeStartup (ime_startup.rs, copied with its
seven tests); no timer, no key queue, no text rewriting. It sits on top of
the previous macOS IME change (pending_commit, in_key_down); the retry runs
inside in_key_down. Command maps to ModifiersState::SUPER in winit 0.30
(META on winit master).
Checked with the real input method on macOS 14.7.1 (scratch winit app, fresh
process each time): without the patch g k s Space gave ㅎ as a key, then
ㅏ and ㄴ committed separately, 3 runs out of 3; with it, ㅎ → 하 → 한 → 한
committed, 3 out of 3. All other traces (Japanese, Cangjie, ABC, held
keys, later Korean keys) are unchanged. ImeStartup's tests pass with
rustc --test. A test fails if a re-vendored winit loses the patch.
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>
|
||
|
|
cf9f58fe87 |
macOS: Space in Apple Korean with no syllable in progress types one space (winit#4478) (#679)
* macOS: IME text committed after the marked text is cleared isn't lost (winit#4650)
winit 0.30.13 reports insertText as Ime::Commit only while marked text is
left, and after any commit it swallows the next key. Real paths where that
loses text:
- input methods that end a composition with unmarkText, then insertText
(rust-windowing/winit#3925): the committed text was dropped;
- Apple Korean 2-Set, for the key typed after a syllable: on 한 then 5 it
calls setMarkedText("한"), insertText("한"), setMarkedText(""),
insertText("5"), so the 5 (or ;, or any key that ends the syllable) was
dropped;
- a commit made outside a key press (a candidate clicked with the mouse,
dictation): no key follows to reset the state, so the next key (for
example Backspace) was swallowed.
vendor/winit now commits when a composition was started (pending_commit,
set in setMarkedText, cleared by the commit and when IME is disallowed);
other insertText calls still reach the app as KeyboardInput. A commit
outside keyDown clears the marked text and returns to the ground state.
validAttributesForMarkedText lists the underline and marked-clause
attributes, which some input methods need before they compose.
This is rust-windowing/winit#4650, the 0.30 backport of #4651 (merged on
winit master), plus the part of #4748 that fixes #4651's out-of-key-press
case (#4748's other parts are not included). One correction: upstream
spells the underline attribute "NSUnderlineStyle", but AppKit's
NSUnderlineStyleAttributeName is "NSUnderline" (printed on macOS 14), so
the AppKit value is used.
Checked with the real input methods on macOS 14.7.1 (a scratch winit app fed
key events through AppKit): with the patch 한+5 and 한+; commit the 5 and ;,
unmarkText-then-insertText commits, and Backspace after a commit outside a
key press is delivered; ABC typing, held keys, Japanese (日本, あ), Cangjie
(日) and Korean composition give the same events as before. A test fails if
a re-vendored winit loses the patch.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* macOS: Space in Apple Korean with no syllable in progress types one space (winit#4478)
With Apple Korean 2-Set on macOS 14, Space with no syllable in progress
(a second space, or a space after a digit or punctuation) makes AppKit
call setMarkedText(" "), insertText(" "), then setMarkedText(""). winit
reports the insertText as Ime::Commit(" "), but the trailing empty
setMarkedText returns its state to the ground state, so keyDown also sends
the key as KeyboardInput with text " ". egui inserts both, and PdfCraft's
text fields got two spaces (the double space of alacritty/alacritty#8079).
vendor/winit now records that the key being handled committed IME text and
then doesn't also send it as KeyboardInput, unless AppKit forwards it as a
command through doCommandBySelector (Enter after a syllable still commits
the syllable and sends Enter). keyDown consumes the record on every key,
so keys typed after IME is disallowed are unaffected.
rust-windowing/winit#4478 aims at the same double space, but its changes
(clearing the marked text on commit, forwarding a later insertText, and
dropping doCommandBySelector's guard) don't reach this call order, and the
key loss it also fixes is already fixed by the previous change
(pending_commit). This is the narrow form for the call order observed
here; it should go upstream with #4478.
Checked with the real input methods on macOS 14.7.1 (scratch winit app, on top
of the previous change): the duplicate KeyboardInput Space is gone and
nothing else changes (Korean syllables, 한+Enter, 한+5, Japanese, Cangjie,
ABC, held keys, keys after IME is disallowed). A test fails if a
re-vendored winit loses the patch.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Keep #678's emoji-picker branch: view.rs is main's plus this PR's Space fix (
|
||
|
|
3aa03d3c91 |
macOS: emoji from the emoji picker reach text fields (winit#4749) (#678)
* macOS: IME text committed after the marked text is cleared isn't lost (winit#4650)
winit 0.30.13 reports insertText as Ime::Commit only while marked text is
left, and after any commit it swallows the next key. Real paths where that
loses text:
- input methods that end a composition with unmarkText, then insertText
(rust-windowing/winit#3925): the committed text was dropped;
- Apple Korean 2-Set, for the key typed after a syllable: on 한 then 5 it
calls setMarkedText("한"), insertText("한"), setMarkedText(""),
insertText("5"), so the 5 (or ;, or any key that ends the syllable) was
dropped;
- a commit made outside a key press (a candidate clicked with the mouse,
dictation): no key follows to reset the state, so the next key (for
example Backspace) was swallowed.
vendor/winit now commits when a composition was started (pending_commit,
set in setMarkedText, cleared by the commit and when IME is disallowed);
other insertText calls still reach the app as KeyboardInput. A commit
outside keyDown clears the marked text and returns to the ground state.
validAttributesForMarkedText lists the underline and marked-clause
attributes, which some input methods need before they compose.
This is rust-windowing/winit#4650, the 0.30 backport of #4651 (merged on
winit master), plus the part of #4748 that fixes #4651's out-of-key-press
case (#4748's other parts are not included). One correction: upstream
spells the underline attribute "NSUnderlineStyle", but AppKit's
NSUnderlineStyleAttributeName is "NSUnderline" (printed on macOS 14), so
the AppKit value is used.
Checked with the real input methods on macOS 14.7.1 (a scratch winit app fed
key events through AppKit): with the patch 한+5 and 한+; commit the 5 and ;,
unmarkText-then-insertText commits, and Backspace after a commit outside a
key press is delivered; ABC typing, held keys, Japanese (日本, あ), Cangjie
(日) and Korean composition give the same events as before. A test fails if
a re-vendored winit loses the patch.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* macOS: emoji from the emoji picker reach text fields (winit#4749)
The macOS emoji picker (Fn/Globe-E, Control-Command-Space) inserts its text
with insertText:replacementRange: outside any key press and without a
composition. winit 0.30.13 reports insertText only as part of a key press or
a composition, so an emoji picked into Search, a form field or a comment
was dropped.
vendor/winit now reports text inserted outside a key press, with IME
allowed and no composition, as Ime::Commit, as rust-windowing/winit#4749
(the 0.30 backport of #4748) does. Builds on the pending_commit and
in_key_down state of the previous macOS IME change.
#4748's selectedRange change, which lets dictation start, is left out:
checked on macOS 14.7.1, a valid selection switches on press-and-hold for
every held key in a winit window, so holding e kept sending e key events
under an accent menu, and the accent picked with 1 arrived as a separate
commit after "eeee…1". Without it, held keys repeat as before.
Checked with a scratch winit app on macOS 14.7.1: an insertText outside a key
press now arrives as Ime::Commit; held keys, ABC, Japanese, Cangjie and
Korean typing give the same events as before. A test fails if a re-vendored
winit loses the patch or gains the selectedRange change.
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>
|
||
|
|
3a86ce073d |
macOS: an unfinished composition doesn't come back in the next text field (winit#4745) (#677)
egui disallows IME when a text field loses focus, for example when the user clicks the page while composing. winit 0.30.13 then clears its own marked text but never tells the input method, which keeps the composition and continues it once IME is allowed again. Checked with the real input methods on macOS 14.7.1: Korean 하, IME off and on, then r k gave 학, then 하 and 가 (하 committed a second time if the app already kept it); Japanese か, IME off and on, then s a gave 傘. vendor/winit now calls NSTextInputContext::discardMarkedText when IME is disallowed with marked text present, before its existing reset, as rust-windowing/winit#4745 does on winit master. With the patch the same sequences give 가 and さ. AppKit may call unmarkText from discardMarkedText while IME is still enabled, so its Preedit("") comes before Disabled; the RefCell borrow in the check ends before the call. Everything else in the scratch app's traces (ABC, Japanese, Cangjie, Korean) is unchanged. A test fails if a re-vendored winit loses the patch. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
ec6c904cf3 |
macOS: IME text committed after the marked text is cleared isn't lost (winit#4650) (#676)
winit 0.30.13 reports insertText as Ime::Commit only while marked text is
left, and after any commit it swallows the next key. Real paths where that
loses text:
- input methods that end a composition with unmarkText, then insertText
(rust-windowing/winit#3925): the committed text was dropped;
- Apple Korean 2-Set, for the key typed after a syllable: on 한 then 5 it
calls setMarkedText("한"), insertText("한"), setMarkedText(""),
insertText("5"), so the 5 (or ;, or any key that ends the syllable) was
dropped;
- a commit made outside a key press (a candidate clicked with the mouse,
dictation): no key follows to reset the state, so the next key (for
example Backspace) was swallowed.
vendor/winit now commits when a composition was started (pending_commit,
set in setMarkedText, cleared by the commit and when IME is disallowed);
other insertText calls still reach the app as KeyboardInput. A commit
outside keyDown clears the marked text and returns to the ground state.
validAttributesForMarkedText lists the underline and marked-clause
attributes, which some input methods need before they compose.
This is rust-windowing/winit#4650, the 0.30 backport of #4651 (merged on
winit master), plus the part of #4748 that fixes #4651's out-of-key-press
case (#4748's other parts are not included). One correction: upstream
spells the underline attribute "NSUnderlineStyle", but AppKit's
NSUnderlineStyleAttributeName is "NSUnderline" (printed on macOS 14), so
the AppKit value is used.
Checked with the real input methods on macOS 14.7.1 (a scratch winit app fed
key events through AppKit): with the patch 한+5 and 한+; commit the 5 and ;,
unmarkText-then-insertText commits, and Backspace after a commit outside a
key press is delivered; ABC typing, held keys, Japanese (日本, あ), Cangjie
(日) and Korean composition give the same events as before. A test fails if
a re-vendored winit loses the patch.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
|
||
|
|
84b241831b |
X11: the input method follows the text field that has focus (winit#4727) (#675)
egui calls Window::set_ime_allowed(true) when a text field gains focus and set_ime_allowed(false) when it loses it. On X11, winit 0.30.13 handles each change by destroying the window's X input context and creating a new one, and leaves focusing the new context to the next FocusIn. PdfCraft's window keeps the focus while the user moves between Search, form fields and the canvas, so no FocusIn comes and the new context stays unfocused: the input method (IBus, fcitx5) doesn't follow the field until the window loses and regains focus. vendor/winit's Ime::set_ime_allowed now returns create_context's result, and the event processor, after draining the queued IME requests, focuses the last context replaced for the active window, once, as rust-windowing/winit#4727 does on winit master. No public API changes. A test fails if a re-vendored winit loses the patch. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
9250e7542e |
Windows: a window closed maximized reopens without a white flash (winit#4587) (#674)
eframe creates PdfCraft's window hidden, restores the saved state (app.ron, persist_window) and shows the window after the first frame is drawn. For a window closed maximized, winit 0.30.13 applies the maximized state with ShowWindow(SW_MAXIMIZE), which shows a hidden window: PdfCraft appeared unpainted (white), was hidden again, then shown with SW_SHOW and SW_MAXIMIZE once more. rust-windowing/winit#4586 also shows the window's focus state going wrong on this path. vendor/winit now skips the maximize while the window is hidden and shows a hidden window that is maximized with one SW_MAXIMIZE, as rust-windowing/winit#4587 (approved upstream) does on winit master. That is the later, focus-safe form of #4575; #4575's other change (re-sending SW_MINIMIZE on every flag change while minimized) is not taken. Transitions between two visible states make the same ShowWindow calls as before. A test fails if a re-vendored winit loses the patch. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
5437390e7e |
Windows: the Korean IME composition caret sits after the syllable (winit#4746) (#673)
When a composition has no target clause, winit 0.30.13 reads the cursor with
ImmGetCompositionStringW(GCS_CURSORPOS) without checking that the
WM_IME_COMPOSITION lParam has GCS_CURSORPOS set. The Microsoft Korean IME
never sets it (lParam 0x6018), so the read gives 0 and winit reports
Ime::Preedit("한", Some((0, 0))). egui 0.36.2 recognises that exact report on
Windows and drops the range, so PdfCraft draws no caret while Korean is being
composed.
vendor/winit now reads GCS_CURSORPOS only when the flag says the IME set it
and otherwise puts the cursor at the end of the composition, as
rust-windowing/winit#4746 does on winit master (Firefox and SDL do the same).
IMEs that report a cursor or a target clause (Japanese, Chinese) are
unchanged. A test fails if a re-vendored winit loses the patch.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
|
||
|
|
60a0518349 |
Wayland: IME keeps working after a seat loses a capability (winit#4747) (#672)
winit 0.30.13 destroys a seat's zwp_text_input_v3 in SeatHandler::remove_capability, whichever capability went away (a touch screen or tablet unplugged, a remote-desktop session ending), and only new_capability recreates it. A seat that loses a capability without gaining one leaves PdfCraft without a text input: IME (fcitx5, IBus) stops working until PdfCraft is restarted. The text input belongs to the seat, so vendor/winit now destroys it in remove_seat only, as rust-windowing/winit#4747 does on winit master. A test fails if a re-vendored winit loses the patch. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
dc4b3eee84 |
Fix #378: draw on the GPU that drives the primary display on Windows (#631)
* Fix #378: draw on the GPU that drives the primary display on Windows The low-power preference picked the AMD Ryzen integrated GPU on a desktop whose monitors all hang off an NVIDIA card. Presenting from the headless iGPU to a window on another GPU took the AMD display driver down, blacking out every monitor for two minutes before PdfCraft exited. Windows now gets the display-aware adapter choice Linux already had: EnumDisplayDevices (through the safe `winsafe` crate, MIT) says which adapter drives each desktop display and which is primary; `pick_adapter` matches that to the wgpu adapters by PCI ids. The GPU driving the primary display wins, as the built-in panel does on Linux (#8). The log names the GPU chosen, the first question when a window stays black. Synthetic tests cover the Windows device-id parser and the primary-display rule; a live test checks the enumeration on the running machine. Verified on a Ryzen 7000 + RTX 3090 desktop: the app now starts on the 3090. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XBMGuLLMhs1h8orSxcJXER * 631: the winsafe feature comment says user (and kernel) --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
74e30e5803 |
CLI: pdfcraft-cli ui refuses a control file another user could plant (#597)
* Security: pdfcraft-cli ui refuses a control file another user could plant `pdfcraft-cli ui --control FILE` sent the token and every command, typed text included, to whatever port FILE named, without checking who owned it. The README suggested `/tmp/pc.json`; in a shared /tmp another local user can create that file first, so the CLI talked to their listener. The CLI now refuses a symbolic link and anything but a regular file, a file over 64 KiB, and on Unix a file owned by another user or with any group or other permission bits (the app writes 600). The owner and mode are read from the opened file, and its device and inode must match the path's, so a swap after the check is refused too. std has no geteuid and unsafe is forbidden, so the effective uid is the owner of a file the CLI creates in the temp folder and removes. The README now uses a per-user path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Security: stop the app when --control can't be published A control file that couldn't be written was only logged, and the app ran on with a listener nobody could reach. The failure can mean another user already created a file at a shared path to receive the commands, and a script started with --control would otherwise drive nothing without being told. The app now exits with the error, before any document opens, so no work is lost. The README and the app's usage comment say so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * CLI: pdfcraft-cli ui refuses a control-file port above 65535 `ui` truncated the control file's port with `as u16`, so 65536 + N reached port N, and the token and command went to whatever listened there. A port that doesn't fit is now refused, before connecting. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * CLI: learn the effective uid from rustix, not a temp-file probe Review on #597: the probe made `pdfcraft-cli ui` depend on a writable temp folder to judge a control file somewhere else, created and removed a file on every call, and could leave one behind. rustix's `geteuid` is safe and can't fail, and rustix is already in Cargo.lock; it is a Unix-only dependency of the CLI, so Windows builds don't compile it. A control file of your own now works with TMPDIR pointing at a folder that doesn't exist; the probe refused it. 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> |
||
|
|
36468f0ad4 |
Windows: a GPU that can't set up the window falls back to OpenGL (#519) (#617)
* Windows: vendor egui-wgpu 0.36.2 as published (#577) The crate as released on crates.io, plus egui's LICENSE-APACHE and LICENSE-MIT from the 0.36.2 tag (the published crate leaves them out). Not used yet: the next commit patches it and adds it to [patch.crates-io]. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Windows: a window larger than the GPU's texture limit can't close PdfCraft (#577) egui-wgpu 0.36.2 configures a window's surface at the window's own size. Beyond the device's max_texture_dimension_2d, Surface::configure fails validation and wgpu's default error handler panics. eframe configures the surface before the app is created, so no app code can intercept it (emilk/egui#8361). #578 raised the device's limit to the adapter's; this removes the crash for windows larger than even that. vendor/egui-wgpu: `winit::surface_fit` shrinks both sides by one factor so neither exceeds the limit (the longer side lands exactly on it), and egui draws at that factor into the surface. On DX12, PdfCraft's Windows default, the swapchain is stretched over the window (DXGI_SCALING_STRETCH), so the window stays complete and lined up with the pointer, only softer; wgpu's GL backend copies it unscaled into a corner instead. It applies wherever a surface is sized: install, resize (start-up restore, maximise, DPI change, fullscreen) and a recreated surface, which starts again from the window's own size. Depth and MSAA textures follow. A surface logs a warning when it becomes fitted. Why only an Outlook launch hit it (read from the code, not observed): winit creates the window at CW_USEDEFAULT, so Windows places it, and a launcher that hands ShellExecute its own window gets new windows on its monitor. winit then converts the restored logical size at that monitor's scale: 3440 points became 8600 pixels on a 250% panel. eframe's clamp compares points against the largest monitor, so it let 3440 through. Tests: surface_fit with the issue's 8600 x 3423 and edge sizes (zero, u32::MAX, a zero limit, the longer side exactly on the limit); egui-wgpu resolves to vendor/egui-wgpu and the fit stays wired into the paths that size a surface. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Windows: a GPU that can't set up the window falls back to OpenGL (#519) On a 2015 Intel GPU with no D3D12 driver, wgpu picked its GL adapter over WARP and Surface::configure failed (GpuWaitTimeout). wgpu's default error handler panics, and eframe configures the root surface before the app is created, so the OpenGL retry from #530 never ran. Vendored egui-wgpu (PdfCraft patch #519): winit::catch_errors runs a closure inside wgpu error scopes (validation, out of memory, internal) and returns the error. Painter::add_surface configures each new window's surface inside it and returns a failure as WgpuError::ConfigureSurface, so eframe returns Error::Wgpu and main retries with glow. Later reconfigures are unchanged. Tests: a real device returns a wgpu validation error from catch_errors instead of panicking (red with the scopes removed), and add_surface keeps the wiring (red with it removed). Docs: vendor/README.md patch 2, docs/development.md "Renderer fallback" and PDFCRAFT_RENDERER, ROADMAP. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * vendor/egui-wgpu: drop the stray .cargo-ok marker * Tests: the GPU error-scope test skips on a machine without a GPU adapter --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
f7d5ca4a07 |
Windows: a window larger than the GPU's texture limit can't close PdfCraft (#577) (#609)
* Windows: vendor egui-wgpu 0.36.2 as published (#577) The crate as released on crates.io, plus egui's LICENSE-APACHE and LICENSE-MIT from the 0.36.2 tag (the published crate leaves them out). Not used yet: the next commit patches it and adds it to [patch.crates-io]. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Windows: a window larger than the GPU's texture limit can't close PdfCraft (#577) egui-wgpu 0.36.2 configures a window's surface at the window's own size. Beyond the device's max_texture_dimension_2d, Surface::configure fails validation and wgpu's default error handler panics. eframe configures the surface before the app is created, so no app code can intercept it (emilk/egui#8361). #578 raised the device's limit to the adapter's; this removes the crash for windows larger than even that. vendor/egui-wgpu: `winit::surface_fit` shrinks both sides by one factor so neither exceeds the limit (the longer side lands exactly on it), and egui draws at that factor into the surface. On DX12, PdfCraft's Windows default, the swapchain is stretched over the window (DXGI_SCALING_STRETCH), so the window stays complete and lined up with the pointer, only softer; wgpu's GL backend copies it unscaled into a corner instead. It applies wherever a surface is sized: install, resize (start-up restore, maximise, DPI change, fullscreen) and a recreated surface, which starts again from the window's own size. Depth and MSAA textures follow. A surface logs a warning when it becomes fitted. Why only an Outlook launch hit it (read from the code, not observed): winit creates the window at CW_USEDEFAULT, so Windows places it, and a launcher that hands ShellExecute its own window gets new windows on its monitor. winit then converts the restored logical size at that monitor's scale: 3440 points became 8600 pixels on a 250% panel. eframe's clamp compares points against the largest monitor, so it let 3440 through. Tests: surface_fit with the issue's 8600 x 3423 and edge sizes (zero, u32::MAX, a zero limit, the longer side exactly on the limit); egui-wgpu resolves to vendor/egui-wgpu and the fit stays wired into the paths that size a surface. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * vendor/egui-wgpu: drop the stray .cargo-ok marker --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
22cbf65628 |
Tell the user when PdfCraft draws without a GPU (#519) (#627)
* Refs #519: tell the user when PdfCraft draws without a GPU When wgpu's adapter is a software one (WARP on Windows, llvmpipe on Linux), show a notice at launch that PdfCraft may be slow. egui-wgpu only logged it, so the #519 reporter had to find it in pdfcraft.log. The OpenGL fallback isn't covered: glow reports its renderer only through unsafe calls. The string is translated in the ten catalogs that carry the portable-mode notice, with their ATTRIBUTION.toml hashes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * zh-hant: the drawing-without-a-GPU notice --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
a70c2b4938 |
View: keep earlier tiles on screen while a zoom's new tiles render (#415)
* View: keep earlier tiles on screen while a zoom's new tiles render Zooming a tiled page (larger than 4096 device pixels) dropped every tile of the previous scale at once, so each zoom step fell back to the 2048 px backdrop until the new tiles arrived: the page went blurry on every frame of a pinch. Tiles are now keyed by scale; those of earlier zooms stay (nearest scale on top, at most 64) and are drawn stretched under the current ones until those cover the page. Entering the tiled range also keeps a whole-page raster sharper than the backdrop instead of replacing it with the backdrop. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * hayro-syntax: fix float mantissa overflow on high-precision coordinates (#199) jsPDF outputs double-precision floating-point coordinates with 16+ decimal places in /MediaBox. In hayro-syntax, read_inner accumulated float mantissa digits into u64 using wrapping arithmetic. Integer mantissas exceeding u64::MAX wrapped modulo 2^64, severely corrupting page dimensions (e.g., 3874.96 pt became 185.61 pt, rendering as a warped ribbon). Accumulation now uses checked arithmetic: trailing fractional digits exceeding u64 capacity are safely ignored without incrementing decimal_shift. This preserves full IEEE-754 f64 precision while preventing overflow for numbers with arbitrary numbers of decimal places. Includes: - Unit test in hayro-syntax with reported values and a 50-digit fraction. - Synthetic PDF regression test in pdfcraft-render verifying inspect(). - Vendored patch (6) documentation in vendor/README.md. Fixes #199 * M2: bound page-label work and output before applying labels Share bounded PDF Roman and repeated-letter formatters between inspection and editing. Bound tree traversal, raw and decoded prefixes, individual labels and aggregate label output, with checked numbering arithmetic. Stage inspection labels before applying them and retain physical page numbers with a warning when optional labels cannot be processed. Reject unsupported label edits before changing the document tree. Add synthetic limit, cycle, Unicode, numbering and atomic-edit regressions. Co-Authored-By: OpenAI Codex <codex@openai.com> * Never crash: mesh shadings can't exhaust memory or run for minutes `cargo xtask fuzz` (with the dash, JBIG2 and decompression fixes in place) found a page whose Coons patch shading (type 6), a 147-byte stream with 1-bit coordinates decoded onto +-40000, took the renderer past 3 GB. hayro-interpret samples mesh shadings (types 4-7) pixel by pixel over each triangle's device-space bounding box into a map, with nothing but the u16 cast bounding it: up to 65536 x 65536 entries for one triangle. A synthetic single patch passed 6 GB in six seconds; ten thousand triangles that each cover half a 1000 x 1000 page ran for minutes. Two rounds of review of earlier versions found more: every patch was cut into 722 triangles (about 150 KB) before anything limited them; a lattice mesh with `/VerticesPerRow 0` pushed empty rows forever, and one with 4294967295 read the whole stream into one row; a mesh painted a thousand times was triangulated a thousand times; and the first caps cut content without a word. Vendored hayro-interpret patch (18): - `ShadingPattern::encode_within(area)`: the vendored hayro renderer passes the area it will actually draw (the path's bounding box on the canvas, which it already computes) plus a pixel for texels at a fractional edge, and triangles are sampled only inside it, so the map holds at most that area's pixels. - `TriangleSampler`: at most 8 visits per pixel of that area plus 4 per triangle in it. A triangle's bounding box is about twice its area and a sub-pixel triangle's is at most 2 x 2 pixels, so tiling meshes and small dense ones (a patch in a few pixels is 722 triangles) still shade completely; overlapping ones stop. - Coons and tensor-product patches are triangulated one at a time. - Meshes hold at most `MAX_MESH_PATCHES` (2^16; a 200 x 200 gradient mesh is 40 thousand) patches and `MAX_MESH_TRIANGLES` (2^20) triangles, and a lattice needs two (ISO 32000-2 8.7.4.5.6) to 2^19 vertices per row. - A page samples at most `MAX_PAGE_MESH_TRIANGLES` (2^25; the 200 x 200 mesh is 29 million) mesh triangles in all, reset per page like the existing paint budget. - Each of these cuts logs a warning. `encode()` keeps its signature; with no area it samples within a fixed 2^24 visits. Synthetic regression tests in render: - `mesh_shadings_far_off_the_page_render`: a Coons patch and a triangle mesh decoded onto +-40000 render and the rest of the page draws; a patch on the page, the part of the huge one that covers it, and a patch filling a 4 x 4 pt rectangle as a pattern are all shaded. - `many_overlapping_mesh_triangles_finish`: 10,000 triangles each covering half of a 1000 x 1000 page finish, and are drawn. - `mesh_shadings_with_many_patches_or_no_rows_finish`: 40,000 patches beside the page, and a lattice with `/VerticesPerRow 0`. Red on main (out of memory, killed at 3.9 GB). Each part is pinned by a mutation: no visit budget stalls past 60 s; no clip leaves the covering patch unshaded; no per-triangle allowance leaves the small patch unshaded; triangulating all patches first runs out of memory (killed at 5.8 GB); no rows check runs out of memory. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Never crash: decompression bombs can't exhaust the renderer's memory The renderer's parser (vendored hayro-syntax) decoded Flate, LZW and RunLength streams with no output limit. A 2 MB page whose content stream inflates to 2 GB, or whose 8 x 8 image does, took `pdfcraft-cli render` and `text` past 3 GB in under a second, and a few more MB of file scale it further. On the desktop that is the first-page render of an emailed PDF, and the allocation-failure abort takes every open document with it. Our own layers already bound single streams (lopdf's load-time decoding at 256 MiB, cos at 1 GiB), so `info` stays at a few MB; the renderer path didn't. Random mutation almost never makes a high-ratio stream, so `cargo xtask fuzz` doesn't find this; found by hand while triaging the fuzz run. Vendored hayro-syntax patch (6): - `decode_limit` gives each filter an output limit, and Flate (both flate2 and the fallback inflater), LZW and RunLength never write past it (the buffer can still grow to under twice it): `MAX_DECODED_STREAM`, 256 MiB, lopdf's figure; or, for the last filter of an image whose size, bits per component and components are known, twice its pixel bytes (a PNG predictor tag byte per byte at most), capped at 1 GiB. Earlier filters in a chain produce compressed data and get the general limit. - `Stream::decoded_within(max)` for callers that budget. - A page's `/Contents` array shares one `MAX_DECODED_STREAM` (naming one bomb twenty times decoded 5 GB), reserving exactly what it appends. Vendored hayro-interpret patch (18): - The content a page holds at once (its contents, and the forms, tiling patterns and Type 3 glyph procedures being painted) stays within `MAX_PAGE_CONTENT`, 512 MiB, reset per page like the existing paint budget. Forms nest fifty deep and each level kept its content while the next ran: a form painting itself around a 256 MiB stream would have held over 12 GB. Each level gives its bytes back when it is dropped, so content painted again and again keeps drawing, and a stream without filters (borrowed from the file) costs nothing. - Type 0 sampled functions read every sample into a 4-byte value and then a table entry: 256 MiB of 1-bit samples asked for over 8 GB. They now take at most 2^21 table points and 2^23 values (the largest real tables, DeviceN tint transforms such as 33^4 points x 3 outputs, are 1.2 million and 3.6 million), counted with checked arithmetic, and read no further than `/Size` asks. `/Size 0` no longer underflows building the default `/Encode`. Three rounds of independent review found the aggregate cases, buffer doubling past the limit, a chain/predictor shape the first image limit would have truncated, the Type 0 amplifier, and a first, cumulative budget that would have dropped legitimately repeated content, and a first Type 0 cap low enough to refuse real tint transforms; all fixed here. Measured in a release build (render; OS peak commit): - content stream inflating to 2 GB: 389 MB, 0.3 s - 8 x 8 image inflating to 2 GB: 4 MB, 0.2 s - /Contents naming it twenty times: 516 MB, 0.3 s - a form painting itself around it: 644 MB, 0.4 s Synthetic regression tests in render (flate2 is a new dev-dependency there, already in the workspace): - `decompression_bombs_stop_at_the_stream_limit`: 288 MiB bombs through flate2, the fallback inflater (a stored block with a wrong length check, which flate2 refuses and the fallback accepts), LZW and RunLength each decode exactly 256 MiB, and at most 128 bytes as an 8 x 8 image. - `page_contents_and_nested_forms_share_one_budget`: three /Contents references decode one stream's worth; a self-painting form finishes. - `content_painted_again_and_again_keeps_drawing`: a 2 MiB form painted 300 times draws the 300th time. - `sampled_functions_with_billions_of_samples_are_refused`. Ten mutations each turn them red: no limit at all; no limit in flate2, the fallback, LZW, RunLength or for images; an unshared /Contents limit; no page budget; no refund on drop; no Type 0 sample cap. (Taking a form out of the budget no longer compiles: its content is the budget guard.) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * M2: normalize JPEG2000 samples without overflowing Use bounded integer sample scaling for 1-32-bit JPX images and reject empty dimensions before chunking. Cover public decoding and continued page rendering with synthetic regressions. Co-Authored-By: OpenAI Codex <codex@openai.com> * M14: use adapter texture limits for HiDPI surfaces (#577) Preserve the existing egui-wgpu device descriptor while enabling the selected adapter's supported 2D texture extent. Cover the observed 8600-pixel Outlook startup surface and downlevel limit preservation. Co-authored-by: Codex <noreply@openai.com> * Never crash: decompression cut at its limit is logged, not silent; vendor README renumbered after #575 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: philkwek <philkwek03@gmail.com> Co-authored-by: Streetblock <github@streetblock.de> Co-authored-by: OpenAI Codex <codex@openai.com> Co-authored-by: James Locke <336218001+jcl-bit@users.noreply.github.com> Co-authored-by: msaulc <103976208+msaulc@users.noreply.github.com> Co-authored-by: Codex <noreply@openai.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
24ba8385f8 |
Issue #324: on Windows 11, a window dragged to another monitor takes its scale (#544)
* Issue #324: vendor winit 0.30.13 as published The crate as released on crates.io, without its asset files (docs/res/*.svg and examples/data/*.png, AGENTS.md §1). Not used yet: the next commit patches it and adds it to [patch.crates-io]. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Issue #324: on Windows 11, a window dragged to another monitor takes its scale winit 0.30.13 handles WM_DPICHANGED by checking that the new window rect is on the monitor `MonitorFromWindow` names. During a drag that is still the monitor being left, so winit nudged the window back onto it, which sent another WM_DPICHANGED: the window grew and stayed on the wrong monitor at the wrong scale. From Windows 11 (build 22000) on, apply the rect Windows suggests, as winit master does since 488c036a (rust-windowing/winit#4341); no 0.30 release has it. Windows 10 keeps the old adjustment, as on winit master. The vendored copy goes in [patch.crates-io]; a test fails if winit resolves from crates.io again or the vendored copy loses the patch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |