Commit Graph
630 Commits
Author SHA1 Message Date
aiongg 91aaa294d0 docking: anchor an outer edge drop on the area it shows
The edge band and window.panel.dock to an outer edge anchor on the canvas only where the edge area narrows to the canvas column, else on a panel that is not protected, so tree order no longer decides the frame. The renderer tries the drop and offers the band only when it wraps the panels the band shows. A top (bottom) edge also narrows a split of rows to its first (last) row when only that row holds the canvas. to_actions takes the permissions, and FloatSpace records side_edge_zones.
2026-10-25 06:18:34 +00:00
aiongg 4dc543cb44 panes: the dock preview counts a pane its drop reveals, and a side pane stays inside the host
With SidePolicy::OneSlot a drop can reveal a pane the frame hides, so the landing slot is
worked out from every open pane. A side pane is no wider than the room the panes outside it
leave, as the preview assumes; egui placed a wider one partly beyond the host's edge.
2026-10-25 05:50:40 +00:00
aiongg 950b9644e8 docking: a drop beside a frame too narrow to halve takes the room from beyond its divider 2026-10-25 05:34:35 +00:00
aiongg 63b3e4e2e8 docking: a drop that changes only sizes is a no-op; a lone floating group's header drag looks through its window 2026-10-25 05:34:35 +00:00
aiongg f268d21055 panes: preview the slot a dragged pane docks in, also while floating 2026-10-25 05:20:53 +00:00
aiongg 876d2539f9 docking: drop_edges defaults to all sides; the docking profile's outer band takes only its top and bottom 2026-10-25 05:20:27 +00:00
aiongg 889f0367f2 docking: narrow the outer edge band to the canvas column and add DockStyle::drop_edges 2026-10-25 05:18:09 +00:00
aiongg 02d7d92a3e docking: fade a dragged native window only when the drag can dock 2026-10-25 05:18:09 +00:00
aiongg 8a503cfe79 docking: drop targets follow the pointer over the whole group (#310)
* docking: drop targets follow the pointer over the whole group

Outside side_edge_zones, a group body's outer third on each side docks
beside it and the middle joins as a tab; at a corner the relatively
nearer side wins, and a group that takes no tabs maps its whole body to
its edges. Dock-column groups get the same zones: top and bottom stack,
left and right make a column. Border bands (seam, column edge, new
dock, root edge) are 28 pt and reach only 4 pt into a tab strip that
takes tabs.

Previews are a drop_line_width line along the edge a drop docks to, or
the whole group's outline with a light fill for a tab join, for tab and
group drags alike. side_edge_zones keeps its 40 pt picture-frame zones,
overlay and previews (SIDE_EDGE_ZONE_DEPTH).

DockStyle: side_zone_fraction replaces zone_depth; new column_edge_band
and drop_line_width; edge_band, seam_band and new_dock_band default to
28.

* docking: document the drop zones, bands and previews

* docking: dock groups and the outer edge band keep the split-tree sizes with side edge zones

* docking: drop zones for hosts that resolve drops, lines kept in view, stripless dock groups

resolve_drop_by and DockStyle::zone_rule give hosts the dock area's zones and previews.
A stack line on an edge scrolled out of a dock column shows at the view's edge, and a dock
group without a tab strip takes its zones from the top of its body. A style needs a drop
line wider than zero and a side zone fraction of at most one half.
2026-10-24 23:51:43 -04:00
aiongg 865c9af7fd print: collated copies that can't be one job print as a job per copy (#308) 2026-10-24 22:25:28 -04:00
aiongg 2869b8cc10 print: collated copies on a Windows driver that can't collate repeat the pages in one job (#305) 2026-10-24 22:11:16 -04:00
aiongg e95bc0d2a2 docking: blocking input hands the keyboard to the main window (#306)
* docking: blocking input hands the keyboard from a native window to the main window

* docking: the keyboard goes to the main window only if a native window still has it after a short wait
2026-10-24 22:10:52 -04:00
aiongg 869928d2a0 docking: app drags in panel bodies reach other native windows (#302)
* docking: route app drags in panel bodies across native windows

While an app's drag that began in a panel body lasts, the owner window's
input hook sends pointer moves over another of the app's windows to that
window in its coordinates (PointerMoved, PointerGone, and the primary
release), and the owner sees the pointer gone there. Drop sites in the
target window see hover and release with no app changes.

* docking: wrap the body-drag docs

* docking: a stopped body-drag route takes back the events it queued

* docking: route only body drags that carry a payload, and hold the owner's release

A body drag reaches other windows only with an egui drag-and-drop
payload or when the app calls docking::route_app_drag that frame or the
last; sliders, scrollbars and other plain drags keep the owner's
pointer. When the release goes to another window, the owner's own
release waits for its next input, so a target drawn before the owner
reads the release while egui's payload still exists. A stop never
withdraws that held release.

* docking: routed and held events come before a window's own input

* docking: a held release waits for the main window's next pass, with the pointer events after it

* docking: the owner sees the pointer go in the pass its release is held
2026-10-24 20:36:23 -04:00
aiongg 5c232fd83d Merge pull request #303 from aiongg/print-verify-capabilities
craft-print: refuse a Windows job option only when the driver offers it and dropped it
2026-10-24 20:23:56 -04:00
aiongg 13bb99663e print: DocumentDuplex counts as a driver's duplex feature 2026-10-24 23:37:08 +00:00
aiongg 00f0656e36 print: refuse a Windows job option only when the driver offers it and dropped it
The driver's PrintCapabilities tell a driver that dropped a requested value
from one that never had the feature. Grayscale on a driver with no colour
option and uncollated copies on one that can't collate are accepted, like
one-sided on a queue without duplex. Collated copies on a driver that can't
collate are refused with their own message. The capabilities are read only
when the strict check fails.
2026-10-24 23:23:12 +00:00
aiongg 941316b7ce Merge storytold/craft-libs#141 (Windows ticket names a colour only for grayscale) 2026-10-24 23:17:33 +00:00
aiongg 286212626e Merge remote-tracking branch 'upstream/main' into print-verify-capabilities 2026-10-24 23:16:52 +00:00
aiongg 6e7f28518d Merge pull request #299 from aiongg/craft-jpeg
Add craft-jpeg: 12-bit and arithmetic-coded JPEG decoder (from PhotoCraft)
2026-10-24 18:27:43 -04:00
aiongg 38c4ecab17 docking: native windows ignore input while the app shows a modal (#298)
* docking: native windows ignore input while the app shows a modal

DockArea::input_blocked disables each native floating window's widgets, drops its pointer, key, text and scroll input, covers it with DockStyle::blocked_dim over a layer that takes the pointer, offers no drop targets, forwards no events and cancels its OS close request. Turning it on cancels a drag in progress.

* docking: a blocked native window also drops hovered and dropped files

* docking: blocked palettes drop to the normal window level

* docking: a blocked native window still forwards key releases

* docking: while input is blocked, activation raises the main window last

* docking: blocked palettes stay shown, and the main window comes in front on the app's return
2026-10-24 18:27:19 -04:00
aiongg acb7290804 docking: normal floating windows come forward when the app is activated (#301)
* docking: normal floating windows come forward when the app is activated

* docking: drop windows that became palettes from the raise queue

* docking: the main window comes forward with the normal windows, in focus order
2026-10-24 18:12:06 -04:00
aiongg f9181cc7b9 docking: replacing the workspace cancels a drag in progress (#294)
* docking: replacing the workspace cancels a drag in progress

A dock area keeps the layout it drew while a drag is in progress, with
the actions it returned. When the next layout is neither that one nor
that one with the actions applied, the app replaced or changed it, and
the area cancels its tab, group, column, window bar, native bar, divider
and cross-window drags itself. cancel_drag also ends a native window's
bar drag.

* docking: replacing the layout also ends a divider drag in a native window
2026-10-24 18:11:42 -04:00
aiongg 955831a3a2 jpeg: Limits is non_exhaustive; examples set fields on Limits::default() 2026-10-24 22:05:06 +00:00
aiongg b4e764e44c jpeg: conformance fixtures for jpegtran's progressive and 12-bit arithmetic transcodes 2026-10-24 22:04:21 +00:00
aiongg 2628b8ac9c jpeg: reject decoder buffers above isize::MAX bytes whatever max_alloc allows 2026-10-24 22:03:57 +00:00
aiongg c91d190a7e jpeg: DC-only blocks skip the IDCT; README advises a smaller max_alloc for UI and preview decodes 2026-10-24 22:03:34 +00:00
aiongg e96bf9552b jpeg: arithmetic scans take zeros at a marker without limit
libjpeg's encoder drops trailing zero bytes, so a flat tail of a few thousand blocks decodes from zeros supplied at EOI. Damage is data that runs to the end of the file or a lost restart marker. whitetail-ari.jpg (cjpeg -arithmetic) and its Huffman transcode cover it.
2026-10-24 22:02:59 +00:00
aiongg d12a6db750 docking: an input hook and dropped files for every native window (#293)
* docking: an input hook and dropped files for every native window

eframe runs raw_input_hook only for the root viewport, and each native window's
dropped files arrive only in that window's input. DockArea::native_input_hook runs
an app hook on each native window's RawInput through an egui plugin, before egui
reads it, and DockOutput::window_files reports files hovered or dropped on native
windows with the window and the panel under the pointer. The test host takes
hovered and dropped files per window.

* docking: a discarded pass reports no hovered files; share hook state through a mutex

* docking: one file report per native window when its pass is discarded
2026-10-24 17:53:29 -04:00
aiongg 75da958619 docking: panels closed one by one reopen into one group (#290)
* docking: panels closed one by one reopen into one group

Showing a hidden panel whose anchor is hidden too follows the chain of
hidden anchors to its far end, so a floating location no longer opens a
second window. The hidden panels the restore passed then anchor back on
the panel before them in the chain, on the recorded side, so reopening
them joins the restored panel instead of rebuilding their group or
window. A restored tab whose next tab is not in the anchor's group goes
last.

* docking: a panel beside an edge frame keeps its own location on reopen

* docking: a panel reopened through a hidden neighbour keeps its own group state; the chain stops reversing at an edge frame
2026-10-24 17:52:34 -04:00
aiongg 35bd2f3891 jpeg: CodeRabbit fixes: the max_alloc and damaged docs describe the combined check and block budget, SOF2's message names its coding 2026-10-24 21:30:42 +00:00
aiongg c75bf618f0 docking: viewer panels float as normal windows (#296)
* docking: viewer panels float as normal windows

DockContent::window_kind marks panels as palettes (the default) or normal
windows. A native floating window holding any Normal panel gets the normal
window level and stays shown while the app is in the background; drags
treat it as stacked with the main window by focus.

* docs: a normal window behind the main window takes drags outside it
2026-10-24 17:13:37 -04:00
aiongg c98ee15a7f docking: tabs shrink before they overflow (#295)
* docking: tabs shrink before they overflow

Tabs in a narrow strip shrink, widest first, down to DockStyle::min_tab_width (54 pt at the
default font), cutting their labels with "…" and naming them in a tooltip. Only tabs that do
not fit at that width go to the overflow menu.

* docking: the drag bar narrows once the strip is narrower than the header plus the bar
2026-10-24 17:13:12 -04:00
aiongg 36db71c5c3 docking: floating windows stay in the app window on Wayland (#292)
* docking: floating windows stay in the app window on Wayland

The compositor reports no window positions, so native windows could not
dock again by drag or reopen where they were. The dock area detects
Wayland (WAYLAND_DISPLAY or WAYLAND_SOCKET set and a main window that never
reported its outer rect) and draws floats in-window, keeping their native
rects. DockArea::native_windows(bool) overrides the choice.

* docking: a missing DISPLAY also counts as a possible Wayland backend

* Revert "docking: a missing DISPLAY also counts as a possible Wayland backend"

This reverts commit a804afb1d3.

* docking: the Wayland session is a pure function of the environment; without a Wayland variable only a real window counts
2026-10-24 17:13:00 -04:00
aiongg 059d1519a6 jpeg: test DAC conditioning and interleaved subsampled arithmetic scans 2026-10-24 21:06:47 +00:00
aiongg 3ab100f46e jpeg: test that truncated arithmetic data is reported damaged 2026-10-24 21:06:47 +00:00
aiongg cb28b0c194 jpeg: use the quantization table in force at a component's first scan
The test encoder takes several components, scans of any of them, segments
between scans and per-table conditioning, and drops trailing zero bytes as
libjpeg does.
2026-10-24 21:06:47 +00:00
aiongg b636dad56e jpeg: read the reference PPM with as_chunks 2026-10-24 21:03:28 +00:00
aiongg 3acc132ff0 jpeg: mutation test, fuzz target and CI workflow
tests/mutation.rs runs a seeded mutation pass over the test images and
small SOF9, SOF10 and 12-bit seeds, checking that decode never panics
and stays within small limits. fuzz/ is a cargo-fuzz crate (own
workspace) with a decode target and those seeds. jpeg.yml runs fmt,
clippy, tests and a wasm32 check on Linux. The README gains a testing
section.
2026-10-24 21:00:51 +00:00
aiongg 3be6c3d09d jpeg: bound decoder memory and work
Check coefficients, planes and output against max_alloc together. Stop an
arithmetic scan once it has consumed more than 8 zero bytes past its data or
misses a restart marker, and cap the decoded blocks at 64 passes over the
frame instead of capping the scan count.
2026-10-24 20:57:23 +00:00
aiongg 6fdd2efbba docking: showing a panel with no known place opens it in the first dock group or at an edge (#286)
* docking: open an unanchored panel in the first dock group or at an edge

* docking: an unanchored open prefers the end-side dock; an edge frame is a quarter wide

* docking: drop the redundant edge resize; an edge frame is already a quarter wide

* docking: an unanchored open passes over minimized groups and iconic columns
2026-10-24 16:52:51 -04:00
aiongg f28de79037 jpeg: conformance tests against the libjpeg-turbo test images
Commit testorig.jpg, testimgari.jpg and testorig12.jpg with a jpegtran
Huffman transcode of testimgari.jpg and libjpeg-turbo's 12-bit decode
of testorig12.jpg. The arithmetic-coded image must match its transcode
exactly; the 8-bit and 12-bit decodes are compared against zune-jpeg
and libjpeg-turbo within a small tolerance. Replaces the ignored
conformance_samples test.
2026-10-24 20:49:41 +00:00
aiongg 6354ef2728 jpeg: README and crate list entry 2026-10-24 20:43:51 +00:00
aiongg 5b609acb6f jpeg: make the decoder a standalone crate
Own Error and Limits types, a Decoded image with typed samples, a
public frame() probe for routing, and the tests adapted to them.
2026-10-24 20:43:14 +00:00
Higgs32584andClaude Opus 5.5 537ac17c7c JPEG: cap the scans decoded from one stream at 500
Every scan walks its whole MCU grid, and an arithmetic-coded scan does so
even with no entropy-coded data (the decoder is fed zeros), so a small file
repeating empty scan headers multiplied the work by the frame size. Scans
past the cap are dropped and the image is reported damaged, keeping what
the earlier scans decoded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 20:37:15 +00:00
Higgs32584andClaude Opus 5.5 425bd319c0 JPEG: 12-bit and arithmetic-coded files keep their Photoshop resources
The jpeg_ext path set exif, xmp and dpi but not photoshop_resources, so APP13 8BIM blocks
(paths, Photoshop settings) were lost on import and re-export. Both decoder paths now fill
the same Metadata. Test: a 12-bit JPEG with an APP13 block keeps it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 20:37:15 +00:00
Higgs32584andClaude Opus 5.5 5e2d33cd82 JPEG: a Huffman scan that runs out of data is damaged, not clean
Bits now tracks how many buffered bits are zero padding supplied at a
marker or the end of the data; a read that consumes padding marks the
scan exhausted and the block returns an error, so the scan is reported
as damaged (or fails if no MCU decoded) even when EOI is present.
Arithmetic termination is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 20:37:15 +00:00
Higgs32584andClaude Opus 5.5 8566c598e5 JPEG: 12-bit and arithmetic-coded files open (#4934, #5003)
zune-jpeg decodes neither 12-bit extended sequential (SOF1, P=12) nor
arithmetic-coded (SOF9/SOF10, DAC) JPEGs. Route those files to a new
clean-room T.81 decoder (codecs/jpeg_ext.rs): Huffman sequential 12-bit,
Annex D arithmetic decoding with DAC conditioning (sequential F.2.4 and
progressive G.1.3, 8 or 12-bit), restart intervals, 1/3/4 components and
any sampling factors. Float IDCT, centred linear upsampling, the existing
YCbCr / Adobe CMYK / YCCK conventions; 12-bit output is U16 (scaled).
Allocations are checked against Limits first; every stream read is
bounds-checked and damaged entropy data stops the scan with a Truncated
warning instead of panicking.

Tests: a test-only arithmetic encoder round-trips coefficients through
sequential and progressive (incl. successive approximation) scans with
restarts; a hand-built 12-bit SOF1 block; baseline files relabelled SOF1
match zune; truncation/byte-flip sweeps never panic. Locally the
conformance samples testorig12.jpg and testimgari.jpg decode with a mean
abs diff < 1 level vs testorig.jpg (samples not committed).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 20:37:15 +00:00
aiongg 17744430be docking: a divider drag keeps the split's fixed or ratio size (#291) 2026-10-24 16:18:39 -04:00
aiongg 24a472717a prepress: transparency flattener (flatten feature) (#194)
* hayro-interpret: ink colors, overprint and font bytes for the flattener

Local patches for craft-prepress's PDF-in adapter, labeled Craft patch
(flatten) and listed in vendor/README.craft-reader.md:

- Device::set_overprint and Device::set_isolated, called where the
  blend mode is set and after an isolated group is pushed.
- Color::ink_paint, ColorSpace::ink_space and ink_values: colors and
  image samples as the inks they print, without converting to RGB.
  RasterImage::with_inks gives native samples with Decode and Matte.
- OutlineFontData::embedded and index, and the CFF bytes of Type1C
  fonts, so text can be embedded again.
- The sh operator ignores /Background (ISO 32000-2 8.7.4.5.1).

* plate-test: flattened-page checks, image alpha and scenes F01, F02

- report::run_flattened separates each scene's page after a caller's
  flattener as well as the original, and structure::transparency lists
  what transparency a page still has. plate-test doesn't depend on the
  flattener.
- Scene images take an alpha channel, written as /SMask.
- F01: a partly transparent image over a flat fill; where its alpha is
  0 the fill keeps its exact inks (vectorcraft#2160).
- F02: art hidden under opaque fills leaves no trace after flattening
  (vectorcraft#2161).

* prepress: transparency flattener (flatten feature)

craft_prepress::flatten turns PDF pages with transparency into pages
without it, keeping inks, overprint and live text, for PDF/X-1a and
other output that can't carry transparency.

- pdf: reads a page into a display list in inks through hayro-interpret:
  paths, outlined strokes, clips, groups, soft masks, overprint, spot
  and DeviceN colors, images in their own color space, gradients,
  rasterized mesh shadings, tiling patterns and glyph runs.
- font: prepares the source fonts for embedding again (TrueType,
  OpenType, CFF wrapped as OpenType); others fall back to outlines with
  ActualText.
- geom: planar regions with the shapes that cover each one (linesweeper).
- compose: composites each region per ink (separable and non-separable
  blend modes, isolated and knockout groups, overprint per plate as ISO
  32000 defines it); regions under images, gradients and soft masks are
  marked complex.
- raster: complex regions as DeviceN images through plate mode, grid
  snapped with a one-pixel outset; flatten_page.
- write: writes the result with the vendored krilla; flatten_document
  writes every page into one PDF with the output intent, page boxes and
  fonts shared across pages when their bytes match.

The regions and the compositor are ported from VectorCraft (commit
c2b15982, crates/pathops and crates/engine/src/cmd/flatten.rs), credited
in the crate README. scripts/plate-check.sh --flatten separates every
scene's flattened page with Ghostscript (local only). CI runs
craft-prepress with all features.

* krilla: DeviceN /Colorants from PDF 1.3, NChannel attributes from 1.6 (patch 11)

* prepress: flatten_document output PDF version and PDF/X version

* prepress: format the PDF/X-1a flatten test

* krilla, craft-prepress: write GTS_PDFXConformance next to GTS_PDFXVersion

PDF/X-1a:2003 files carry GTS_PDFXVersion (PDF/X-1:2003) and
GTS_PDFXConformance (PDF/X-1a:2003). Krilla patch 12 adds
SerializeSettings::pdfx_conformance, and FlattenDocumentOptions passes it
through.

* krilla, craft-pdf, craft-prepress: carry the modification date over

Krilla patch 13 adds Metadata::modification_date, written as /ModDate and
xmp:ModifyDate in place of the creation date. craft-pdf's Metadata gains
modified, and flatten_document takes it from the source's ModDate.

* craft-prepress: count the print annotations a read leaves out

ReadReport::dropped_annotations counts the annotations a printer would
paint (Print flag, not Hidden, with a normal appearance) when
ReadOptions::annotations is false; FlattenTotals sums it.

* craft-prepress: degenerate groups rasterize without a soft mask

A group the region sweep fails on is clipped to its members' boxes, which
no other group's items reach, so the paper is its only backdrop. Its
image is written opaque, as the plates of the items over paper, and the
page keeps no transparency for PDF/X-1a.

* craft-prepress: write untouched JPEG images as they are

A DCT-encoded DeviceGray or DeviceCMYK image that krilla's JPEG
passthrough writes with the same meaning (8 bits, no decode parameters,
the default gray Decode or the inverted Adobe CMYK one) keeps its data
in ImageItem::jpeg, and the writer embeds it unchanged when the image
has no soft mask. Other images are still 8-bit Flate samples.

* craft-prepress: keep group members with no transparency above them

Groups are transitive by bounding box, so a background frame put all the
body text on it under a transparent object far away, and the text came
back as outlined fills. A member is now drawn as it is when it is opaque
and, in every region its shapes are in, only members kept this way are
above it. The region fills under it are the composite of the other
members, and it is drawn over them in paint order.

* prepress: keep the backdrop under elements of a knockout group

* prepress: rasterize complex regions thinner than a point

* prepress: mark flatten errors, reasons and reports non_exhaustive

* prepress: bound the items a tiling pattern fill expands into

Each of up to MAX_TILING_CELLS cells copied the whole cell content, and a
cell can hold many items or an expanded pattern of its own. A fill whose
cells would hold more than MAX_TILING_ITEMS items in all, counting groups,
masks and nested patterns, is read as an image.

* prepress: stroke solid past the dash budget

A dash pattern that cuts a stroke into more than 250,000 pieces is outlined
solid, with the estimate hayro's renderer uses.

* prepress: bound the sampling and memory of rasterized tiling patterns

Each pixel sampled every cell over it, which for a BBox far larger than
its step is millions of cells per pixel; past MAX_TILING_CELLS cells per
pixel the fill paints nothing, and below it the image resolution drops to
keep the samples within MAX_TILING_SAMPLES. The canvases also hold a value
per page ink, so their pixel limits shrink with the ink count to keep the
memory of CMYK canvases, and the canvas size uses checked arithmetic.

* prepress: mark the page reader's error and report non_exhaustive

* prepress: format the tiling tests

* prepress: CodeRabbit fixes: no nonzero overprint mode for images and shadings, count dropped tilings, widen the hhea ascender

* prepress: refuse DeviceN spaces past the output's colorant limit and regions past the separated spot inks
2026-10-24 16:15:41 -04:00
aiongg 96f8ae3d5a docking: a double-click on a minimized group's active tab leaves it expanded (#289)
* docking: keep a minimized group expanded when its active tab is double-clicked

* docking: no extra SetMinimized for a click on the active tab
2026-10-24 16:15:20 -04:00