mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-14 11:17:10 -04:00
main
17
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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 <[email protected]> Co-authored-by: Claude Opus 5.5 <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
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 <[email protected]>
|
||
|
|
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 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
|
||
|
|
75b113ac5b | Raise workspace rust-version to 1.95 to match the UI crate (#1169) (#1188) | ||
|
|
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 <[email protected]> --------- Co-authored-by: Brandon Thomas <[email protected]> Co-authored-by: Claude Opus 5.5 <[email protected]> |
||
|
|
575f99f9be |
Add an opt-in low-disk Cargo profile (#951)
* PR-02: add opt-in low-disk Cargo profile * PR-02: document low-disk Cargo profile --------- Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
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) <[email protected]> * 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) <[email protected]> * Leave the symlink-test fix to #949, which covers the same ground --------- Co-authored-by: Codex (GPT-6) <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
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. |
||
|
|
8ff27dc86e | UI.9: preserve PDFCraft panels and document lifecycle | ||
|
|
4339f5b9ed | #570: log desktop renderer details (#726) | ||
|
|
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 <[email protected]> Claude-Session: https://claude.ai/code/session_01XBMGuLLMhs1h8orSxcJXER * 631: the winsafe feature comment says user (and kernel) --------- Co-authored-by: Claude Fable 5.1 <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
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 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
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 <[email protected]> * 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 <[email protected]> * vendor/egui-wgpu: drop the stray .cargo-ok marker --------- Co-authored-by: Claude Opus 5.5 <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
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 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * Never crash: decompression cut at its limit is logged, not silent; vendor README renumbered after #575 --------- Co-authored-by: Claude Opus 5.5 <[email protected]> Co-authored-by: philkwek <[email protected]> Co-authored-by: Streetblock <[email protected]> Co-authored-by: OpenAI Codex <[email protected]> Co-authored-by: James Locke <[email protected]> Co-authored-by: msaulc <[email protected]> Co-authored-by: Codex <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
2f42c743b9 |
Issue #157: the Windows portable zip keeps settings, logs and recovery beside pdfcraft.exe (#325)
* Issue #157: the Windows portable zip keeps settings, logs and recovery beside pdfcraft.exe The portable zip had no portable.txt, and the app had no portable mode: it always wrote %APPDATA%\PdfCraft and %LOCALAPPDATA%\PdfCraft. Now portable.txt (or PdfCraft.portable) beside the executable puts the settings, log files, crash recovery and new digital IDs in PdfCraftData next to it, as PhotoCraft does, and the zip ships portable.txt. An unwritable PdfCraftData falls back to the per-user folders and says so. * Issue #157: update ATTRIBUTION.toml SHA-256 for the translation catalogs this PR changes --------- Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
d30bb111db |
M14: Arabic and Hebrew file names render in the tab strip, recent files and Properties (#285)
A tab named in Arabic showed replacement boxes: no interface font had the glyphs, and egui joins the letters of a right-to-left word but places the runs in logical order. - Fonts: the interface takes `Arab` faces from the craft-fonts build input (also on wasm), and on desktop adds one font already installed on the machine as the last fallback of every family. It is read at runtime, never embedded; PDFCRAFT_SYSTEM_FONTS=0 turns it off and `cargo xtask screenshots` sets that. AGENTS.md §1.4 records the rule. - Order: bidi::visual puts single-line text in display order with a left-to-right base, used for the painted tab label and hover text, the recent-files name and path, and the Properties File and Location rows. Accessibility names keep the logical text. misc.rtl-ui stays planned: multi-line text, text input and a mirrored layout are not done. Co-authored-by: Claude Opus 5.5 <[email protected]> |
||
|
|
1d31c230c9 |
Desktop: write log records to standard error and a rotating log file (#239)
The desktop app installed no logger, so every log::warn!/log::error! in the workspace was discarded: a failed autosave, a page that would not render, a render worker that could not start. A launch from a desktop menu has no terminal either, so there was nothing to attach to a bug report. apps/pdfcraft/src/logging.rs (no new dependency besides log): - standard error plus <settings dir>/logs/pdfcraft.log, next to app.ron (Linux $XDG_DATA_HOME/pdfcraft); each launch rotates the previous log to pdfcraft.1.log and .2.log, so the log of a run that crashed survives the next start; the file is capped at 16 MiB - records logged before the settings directory is known are buffered (bounded) and written once the file is attached - default levels: info for pdfcraft*, warn for everything else; RUST_LOG replaces them with env_logger-style directives - never panics: an unusable directory or a failed write leaves standard error as the only sink The logger is installed first in main, so the panic hook's report is recorded too; the start-up messages in main go through it. The file is opened after argument parsing (--version leaves no file) and after the PrintCraft folder migration, which a fresh settings folder would block. Documented in docs/development.md (Logs, environment variables). |