Commit Graph
9773 Commits
Author SHA1 Message Date
Triwarr f7dfe7a017 PDF: test last-object cancellation and raster fidelity
- A complete import makes a fixed number of cancellation checks; cancelling on the last per-object check and on the public boundary check must both return Cancelled and never a document.
- Six page variants (plain, CropBox offset, rotations 90/180/270, oversized CropBox) compare each stored page raster with a direct PDFium render at 72, 150 and 300 DPI: 18/18 byte-identical, and the sizes prove the crop intersection (190x80) and oversized CropBox (200x100) cases.
- Both tests skip without PDFium and fail under PHOTOCRAFT_REQUIRE_PDFIUM, like the existing end-to-end test.
2026-11-07 02:34:55 +01:00
Triwarr d5157dd799 Merge remote-tracking branch 'origin/main' into pdf 2026-11-07 00:27:36 +01:00
David Mann 8df2b7a2e2 Merge pull request #11738 from davidmann4/fix/az-contextual-task-bar
i18n: translate Contextual Task Bar in the Azerbaijani catalog (#11737)
2026-11-07 00:20:21 +01:00
Triwarr b57603708e PDF: drop the unused image dependency from photocraft-io
The PDF export codec goes through photocraft-codecs; nothing in the crate used the image crate, so the dependency and its lockfile edge are removed. pdfium-render stays pinned exactly.
2026-11-06 23:47:23 +01:00
Triwarr 7e1de84bce PDF: document the added test functions
Every function this PR adds now has a doc comment, including the test functions in the importer, PDF export and layers_to_files suites. No behaviour change.
2026-11-06 23:44:59 +01:00
Triwarr 2f95eac75b PDF: document the importer's known limits
Module docs now state that pdfium-render 0.9.5 exposes path fill/stroke presence but not paint, width or dash (black paints, 1 pt stroke); that text colour is sampled and fonts are matched by family/name; and that only package.ps1 provisions PDFium, not the shared release workflow. The text and path extraction comments say the same where the code relies on it.
2026-11-06 23:41:53 +01:00
Triwarr eda8759d11 PDF: document the import test helpers
Raise docstring coverage on the functions this PR adds: the fixture/assertion helpers in the end-to-end test and the importer's test frame builders now have doc comments. No behaviour change.
2026-11-06 23:35:28 +01:00
David MannandClaude Opus 5.5 ee8a4caa03 i18n: translate Contextual Task Bar in the Azerbaijani catalog (#11737)
The Window menu item from #11669 and the Azerbaijani catalog landed
together, so azerbaijani_covers_every_menu_label_and_path fails on main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 23:32:50 +01:00
David Mann 9922871a4e Merge pull request #11148 from vangel76/perf/paint-procedural-texture-cache
perf(paint): cache procedural brush textures between strokes
2026-11-06 23:20:44 +01:00
David Mann ad55badcb8 Merge pull request #11146 from vangel76/perf/paint-retouch-smudge-selection
perf(paint): Smudge start-up and selection sampling without per-pixel work
2026-11-06 23:20:17 +01:00
Triwarr 00a1b1cd3e PDF: use PDFium's page bounding box; bound editable objects
- Map editable objects through the page's effective bounding box (media box clipped to the crop box) rather than the raw CropBox: an oversized CropBox no longer offsets text or paths from the rendered page. Covered by a fourth fixture page with CropBox (-50, -50, 250, 150) around a 200 x 100 MediaBox.
- Add a document-wide editable-object budget (20,000 layers, 1,000,000 path knots). A page that exceeds it keeps its raster, warns that its editable objects were omitted, and the unit test covers the accounting.
2026-11-06 23:12:14 +01:00
David Mann 6b0f7762e1 Merge pull request #11671 from davidmann4/integration-batch-20
Integration batch 20: 7 i18n PRs (Thai, Hebrew, Urdu, Tamil, Azerbaijani, Romanian, Korean)
2026-11-06 22:49:42 +01:00
David Mann fd890eaca4 Merge pull request #11669 from davidmann4/feat/crop-task-bar-10890
Crop: Contextual Task Bar under the crop frame, as a reusable task-bar widget (#10890)
2026-11-06 22:49:21 +01:00
David Mann 7ecfccbd64 Merge pull request #11643 from davidmann4/integration-batch-19
Integration batch 19: epic PRs (WIP)
2026-11-06 22:48:55 +01:00
Triwarr 4f585f704b PDF: rotate/offset-aware objects, cancellation, licences, CI PDFium
- Map editable text and paths through the page's crop box and /Rotate, matching the rendered raster: box offsets, 90/180/270 rotation, rotated text transforms and rotation-aware colour sampling.
- Check cancellation while traversing page objects and before returning the import; cap text colour samples at 4096.
- The cached PDFium failure now says to restart the application after fixing the library path.
- package.ps1 stages PDFium's LICENSE and bundled licence files flat for the MSI and the portable ZIP, tags the cache with the pinned release, and verifies freshly extracted DLLs.
- ci.yml provisions the pinned PDFium for the Windows integration leg and requires it there; local runs still skip explicitly.
2026-11-06 22:48:30 +01:00
Triwarr 95908e2b92 Pin pdfium-render exactly to the verified wrapper
Same reasoning as the wintab-sys pin in #11331: PDFium is FFI-loaded at run time, so a lockfile refresh must not move the wrapper under the pinned DLL release. Bump deliberately and re-check both the wrapper and the DLL release pin in packaging/windows/package.ps1.
2026-11-06 22:16:32 +01:00
Triwarr fabfbb5d5a PDF: fix segment closing, admission, glyph colour and PDFium reuse
- Process a closing segment's own geometry before marking the subpath closed, so a closing Bezier endpoint cannot leak control points into the next curve.
- Estimate retained memory from tile-aligned page bounds (including the page's X offset) instead of raw pixels: a 1 x 10,000 px page admits its 40 tiles, not 10,000 pixels.
- Sample text colour from the glyph ink (the pixel farthest from the box's dominant colour) instead of the bounds' top-left corner, which was usually background or antialiasing.
- Cache the PDFium bind: pdfium-render binds once per process, so a second PDF import in one session failed with PdfiumLibraryBindingsAlreadyInitialized.
- Add a PDFium-backed two-page end-to-end test with a transformed path on page 2 and non-origin red/green text at 72 and 300 dpi; it prints an explicit SKIPPED line when PDFium is unavailable.
- package.ps1: pin pdfium-binaries chromium/8138, verify the archive and any cached DLL against SHA-256, drop a stale bin/pdfium.dll before extraction and check tar's exit status.
2026-11-06 22:15:50 +01:00
David Mann bd84401dcf Merge remote-tracking branch 'origin/main' into integration-batch-20 2026-11-06 21:46:47 +01:00
David MannandClaude Opus 5.5 e339ecead2 Batch fix-up: keep one path.toShape entry after merging #11005's 0b66e8f00c
#11005's new head adds path.toShape to the vector-path list next to
path.stroke; the batch's 95c7a5d17e had added it next to path.set. Both
merged cleanly into a duplicate or-pattern; keep the contributor's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 21:44:32 +01:00
David Mann 47f4f1ce23 Merge PR #11005's new head 0b66e8f00c (path.toShape vector-path fix) 2026-11-06 21:44:23 +01:00
Triwarr 192b075bfa PDF: map editable objects through one page-to-document transform
- Paths on page 2+ were placed without the page's document X offset; text and paths now share PageFrame::to_doc, which applies the DPI scale, the Y flip and the page offset in one place.
- Flip Y in pixels and round once, so text placement, text colour sampling and path points all stay in the same coordinate system.
- Add unit tests for 72/300 dpi, the page-2 offset and the sampled colour; they need no PDFium.
2026-11-06 21:29:06 +01:00
David MannandClaude Opus 5.5 46d6307cba Crop task bar: Done fits the cropped document
The bar draws inside the canvas pass, whose write-back of its own camera
dropped the fit that commit_crop asks for, so Done kept the old zoom. The
stored view now wins when the bar changed it, as a view menu row's does.
done_fits_the_cropped_document starts at 50 % and fails without the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 21:17:51 +01:00
David Mann ce88165362 Merge remote-tracking branch 'origin/main' into fix/crop-task-bar-fit-11669 2026-11-06 21:00:22 +01:00
tasosf b49c6c97c5 fix(export): preserve high-quality JPEG colour detail in Export As (#7985)
* fix(export): preserve full JPEG chroma in Export As

* style(ui): simplify blend preview condition for Clippy

* test(export): verify full JPEG chroma at every quality

Exercise real JPEG export and decode at five qualities across U8, U16 and F32. Read all sampling factors from the main frame while skipping metadata segments and embedded thumbnails. Keep the colour-error limit at quality 100 and verify that the layered source is unchanged.

* Keep JPEG regression compatible with current Clippy

Restore the upstream blend-preview and IME predicates, removing unrelated lint-only changes. Use array chunks in the JPEG colour-error assertion for current stable Clippy.

* Use quality-dependent JPEG sampling in Export As

Keep 4:2:0 through quality 50 and full 4:4:4 above it, using the existing percentage-scale cutoff from Save for Web. Photoshop 27.11 modern Export As measurements show 4:2:0 at levels 1-4 and 4:4:4 at 5-7; no exact numerical mapping of the two scales is claimed.

Exercise both sampling regimes through 15 real JPEG exports across U8/U16/F32, check all percentage qualities in settings, and retain the Save As default. Boundary mutations fail at 50 and 51 as expected.
2026-11-06 20:59:25 +01:00
DingusMcRinkyDinkyandClaude Opus 5.5 0b66e8f00c path.toShape: its path is a vector path, not a host file
The automation registry check takes a `path` parameter for a host file
unless the command is listed as taking a document vector path, as the
other path commands are. path.toShape's `path` is the inline path being
drawn, so it belongs in that list; integration batch #11643 failed on it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZbgszHyuEuWAzxaamteuU
2026-11-06 20:55:12 +01:00
David MannandClaude Opus 5.5 0b3b3e470b direction tests: ignore the env override, check glyphs only with craft-fonts
Unit tests no longer read PHOTOCRAFT_UI_DIRECTION (cached per process, so a
developer's shell could flip the Hebrew/Urdu and shell assertions). The
Hebrew/Urdu test checks glyphs only when craft-fonts carries a face for the
script (the eager provider draws it in the first frame, as in CI with
CRAFT_FONTS_REQUIRED); without it the harness allows tofu and only the
mirroring is checked, instead of panicking on a host without the fonts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 20:52:58 +01:00
Triwarr 36e7c50178 PDF: transform imported paths by their object matrix
- Apply PdfPageObject::matrix() to path segments: PDFium returns raw object-space points, so scaled or rotated paths landed in the wrong place.
- Flip path y with the page height in pixels, not points; paths were off-canvas at any DPI other than 72.
- Take fill and stroke presence from the path's draw mode: stroke-only paths no longer get a black fill, and paths that are neither filled nor stroked are skipped.
- Estimate import pixel memory as retained tiles (4 B/px over all pages) plus the converting page's peak (24 B/px), defaulting to a 2 GiB budget when the caller sets none. Reuse the RGBA copy and reserve the f32 buffer fallibly.
2026-11-06 20:34:01 +01:00
Triwarr adbe906eaf PDF: fix CI packaging and CodeRabbit findings
- Stage pdfium.dll for the MSI fixtures (install-policy payload) and for the real package
  stage/portable zip; the package script downloads it before cargo.
- Regenerate docs/scorecard.md for the new Preferences fields.
- Text y positions convert the page height to pixels; invisible PDF text stays hidden.
- Page ranges that start past the PDF are refused; zero first/last bounds are independent.
2026-11-06 20:11:03 +01:00
David MannandClaude Opus 5.5 e078c560ec Address CodeRabbit review: task bar tile, inspect and ui.set merge
- Draw the Crop task bar only on the active document's tile: a click into
  another tile activates its document mid-pass, and the tile drawn after it
  put the new frame's bar on itself for that frame.
- ui.inspect reports the bar hidden (and no rect) as soon as it can't show,
  not only after the next draw.
- ui.set merges panels.task_bar field by field: {"at": [x, y]} keeps a pin,
  {"pinned": true} keeps a dragged place; unknown nested fields are refused.
- Tests for each (each fails with its fix reverted), plus the task bar's
  preset dropdown picking 1 : 1 in sync with the options bar.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 19:41:21 +01:00
David MannandClaude Opus 5.5 5d5519dcac Regenerate docs/scorecard.md after merging main
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 19:41:20 +01:00
95c7a5d17e Batch fix-up: path.toShape (#11005) takes a document vector path, not a file
The automation registry check reads a `path` param as a host filesystem
path unless the command is listed as taking a document vector path, as
path.set is. #11005's new path.toShape takes an inline vector path, so it
joins that list; CI failed registry_filesystem_path_params_are_classified.

Co-Authored-By: DingusMcRinkyDinky <DingusMcRinkyDinky@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 19:39:16 +01:00
Triwarr 84644de102 PDF: expose the page range as two Preferences numbers 2026-11-06 19:36:49 +01:00
Triwarr fb66c11df6 PDF: CodeRabbit SVG signature fix, sampled text colour, Preferences page fields
- A PDF named .svg now keeps the PDF importer; other signatures beat the .svg suffix fallback (regression test).
- Text layers sample their fill colour from the rendered page and use the PDF font family/name.
- File Handling preferences expose pdf_dpi, pdf_first_page and pdf_last_page; the page range maps to ImportOptions.pdf_pages.
2026-11-06 19:35:02 +01:00
David MannandClaude Opus 5.5 d7e66606ad Address CodeRabbit review: Turkic I text, type-ahead language change, RTL coverage
- zoom field: a menu's I key drops İ/ı text whatever the UI language (the keyboard layout is
  not the interface language).
- dropdown type-ahead: one entry per list tagged with its language, cleared on any frame in
  another language, so a prefix never carries over a quick switch and back.
- Hebrew and Urdu: live shell test selects each catalog, checks mirroring and glyphs.
- Azerbaijani restart test resets the drawing language before the restarted shell loads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 19:27:11 +01:00
David MannandClaude Opus 5.5 da7690e937 Camera Raw localization test: allow tofu only for Tamil until craft-fonts ships it
The pinned central font manifest has no Tamil face (storytold/craft-fonts#63 adds one) and the
Linux runner has none installed, so the strict missing-glyph policy panicked on the Tamil
labels. Tamil keeps every label and state check; a guard fails once the manifest ships Taml.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 19:27:10 +01:00
David Mann d1d913e7e6 Merge remote-tracking branch 'origin/main' into feat/crop-task-bar-10890 2026-11-06 19:24:17 +01:00
Triwarr 244a6287ab PDF: import options (page range, DPI, editable text/vector) and DLL packaging
- ImportOptions gains pdf_pages (1-based inclusive), pdf_text and pdf_vectors;
  the importer maps PDF text objects to editable text layers and vector paths to
  shape layers through the effective DPI.
- photocraft-cli convert gains --pdf-dpi and --pdf-pages.
- Preferences gain pdf_dpi/pdf_pages, wired through jobs::import_options.
- apps/photocraft/build.rs copies PDFIUM_DLL beside the exe; the MSI installs it.
2026-11-06 19:15:09 +01:00
Triwarr 06a6442076 PDF: SVG precedence, hidden artboards, export budget, writer cancellation 2026-11-06 19:15:08 +01:00
Triwarr ef7699ae9a PDF: render each artboard rect instead of a whole-document crop 2026-11-06 19:15:08 +01:00
Triwarr b1870f1950 PDF: second CodeRabbit round (render size, buffer estimate, label tree) 2026-11-06 19:15:08 +01:00
Triwarr 94a4998208 PDF: address CodeRabbit review (cancellation, budgets, labels, export edges)
- Cancellation: the importer and the PDF writer take the Interrupt and check between pages.
- Physical size: the effective DPI keeps each page print size when budgets cap the raster.
- Oversized pages: final dimensions are checked before rendering.
- Memory: pages go straight into layer surfaces; the aggregate pixel estimate is admitted.
- Labels: /PageLabels (UTF-16BE) carry artboard names; no label is drawn outside the media box.
- Metadata: the artboard export warns when free-form text metadata is dropped.
- Export: hidden groups report their children; non-string modes are rejected; case-insensitive
  filename collisions are suffixed.
- Dispatch: a recognised raster renamed .pdf goes to the flat importer; .ai keeps its path.
- Platform: the PDFium recovery message names the actual library.
2026-11-06 19:15:08 +01:00
Triwarr 3a1b41f0c6 PDF: bound the import canvas
The importer budgets the per-page raster (100 M px) and the artboard strip (200 M px): an
oversized page is scaled down, and pages that would exceed the strip budget are skipped with
a warning, instead of allocating a multi-gigabyte union canvas. The page-count cap stays 16.

Also applies cargo fmt to the new PDF code and the export-recursion test.
2026-11-06 19:15:08 +01:00
Triwarr c1662d4911 Export: split nested groups into separate layers/files
file.export.layersToFiles gains groups: layers (walk every visible leaf at any depth, not only top-level layers) and naming: layer (bare <layer>.png names; duplicates get -2). This is the recursive folder export from the local Program Files build, brought in as the second fix on this branch.

Tested: layers_to_files_exports_group_children_as_loose_files (nested subgroup + sibling).
2026-11-06 19:15:08 +01:00
Triwarr 399cd6ea4d PDF: import every page as an artboard; export a page per artboard
The PDF importer renders each page with PDFium at the import DPI (default 300), lays the
pages out side by side and wraps each in an artboard, so a 2-page PDF opens as 2 artboards
and saves back as a 2-page PDF (one page per artboard, page-sized, labelled). The import
caps at 16 pages for now.

The PDF writer now takes a list of pages: shared font/ICC/selection objects, one image per
page, and the existing one-page export is the single-page case. Artboards with vector/text
layers are rasterised in this path.

Tested: artboards_export_as_one_page_each; convert sample.pdf app-multi.pdf (2 pages),
rendered back with the probe.
2026-11-06 19:15:08 +01:00
Triwarr 95c9d3c9eb PDF import: open the first page with PDFium
File > Open, drag-and-drop and photocraft-cli now accept PDF files. The first page is
rendered with PDFium at 300 dpi (ImportOptions.pdf_dpi), built through the flat
importer, and the document resolution keeps the page's print size. The page count is
reported; multi-page import is the next step (#2541 / FILE-215-10).

- crates/io: pdf_import module, native-only pdfium-render + image deps, import and
  preflight dispatch (.ai keeps its existing path).
- PDFium loads from the executable's folder or PDFIUM_DIR.
2026-11-06 19:15:07 +01:00
David Mann 670fa8b5f1 Merge remote-tracking branch 'origin/main' into integration-batch-20
# Conflicts:
#	crates/ui-egui/src/prefs/prefs_ui/fields.rs
2026-11-06 19:01:38 +01:00
David MannandClaude Opus 5.5 af0ae88bad Batch: drop #9876 (New Guide unit dropdown) from the batch
The reference application's New Guide dialog has Orientation, Position
(a text field that takes typed units such as "2 cm") and Color, but no
unit dropdown; measured on 2026-11-06. Typed units already work on main,
so the PR only adds something the reference lacks.

This reverts merge 22e03d0bcb.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 18:54:34 +01:00
2ff8f419cb ci: run the GPU tests on lavapipe on Linux and fail if they skip (#7975)
The Linux runner has no GPU, so the GPU tests ran on Mesa llvmpipe over
GL, which can't render Rgba32Float: 57 GPU checks (50 GPU parity, 3
canvas, 2 adjustment preview parity, 2 effect-map recovery) returned
early and passed without comparing anything. A new step after Test
installs mesa-vulkan-drivers and runs the nextest `gpu` group on
lavapipe (Vulkan), reusing Test's build, and fails unless the parity
tests report the Vulkan backend and no test printed a skip.

No code change: photocraft's tests take whatever adapter wgpu returns,
software adapters included. Follows the CI half of lightcraft#1148.

Co-authored-by: FredApps <FredApps@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 18:44:13 +01:00
f23d173933 Add an opt-in WinTab backend for pen pressure on Windows (#11331)
* Add an opt-in WinTab backend for pen pressure on Windows

Windows Ink stays the default. Preferences > Tools > Pen Tablet has a Tablet
input dropdown (Windows Ink (default) / WinTab). WinTab opens a WinTab 1.4
system context on PhotoCraft's window and polls pressure and the eraser end
(tilt on devices that report orientation axes; rotation only with a pen and
driver that report twist) into the stylus feed. The row says to turn Windows
Ink off in the tablet's software utility; the choice applies at next launch.

- crates/tablet: Windows-only wintab/wintab-sys backend. Devices with
  orientation axes use the orientation packet; pressure-only devices open a
  packet without it and report neutral tilt. Leaving is the driver's hardware
  proximity message (WT_PROXIMITY) and the packet's out-of-context status, with
  a long fallback for drivers that never report either. Overlap priority
  follows window focus, and the message hook requests a frame for each WinTab
  message so pressure-only strokes update without cursor movement.
- apps/photocraft: install the backend when the preference is on; poll it at
  the start of each frame; the installed wake repaints the UI.
- ui-egui: TabletPollFn service; the Windows-only Pen Tablet row with the
  Tablet input dropdown and the note.
- engine: tools.tabletInput (windowsInk/winTab), default windowsInk.
- docs: hardware-parity WinTab gap entry, qualifying tilt to orientation axes
  and rotation to twist-reporting pens.

Tested locally on a Wacom tablet with Windows Ink off: pressure, tilt and the
eraser end reach the brush (rotation where the pen reports twist); the default
path is unchanged.

* WinTab: a message that finds the state borrowed is skipped, not a panic in the hook

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* WinTab: rustfmt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Scorecard: count the Tablet input setting

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 18:39:40 +01:00
FredApps d2ce418bb3 Merge pull request #11641 from wanikhawar/claude/agitated-jones-8e1a8e
A little free memory no longer refuses every PSD; the refusal says what to do
2026-11-06 18:36:17 +01:00