Conflicts resolved as unions of both sides: codecs re-exports (PngRows + TiffBands), smart_cmds source imports
(main's source_surface_from_buffer and cache API with #8571's svg_source_image_in/convert_svg_source; main's
duplicate rendering import dropped), io flat.rs test modules (#8536's banded TIFF tests and main's PNG ones),
menus/invoke.rs (#8571's smart SVG intercept and main's layer.selectUnderCursor), state.rs (#8311's
type_box_overflow and main's type_insertion_style), the canvas_geom test keeps the is_finite guard, docs
status lines keep both change lists, i18n catalogs keep both sides' rows (hi.tsv: main's wording where both
added the same string).
Conflicts: svg.rs (main's bufread inflate and shared render helper, kept
with the PR's bounded raster path), psd_import.rs (the PR's vector
preview moved into main's smart_content), menus/invoke.rs (the SVG
intercept moved into main's split invoke_unguarded), rendering.rs and
the ui-egui import lists (main's explicit imports), Cargo.lock, docs
status lines (unions).
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Ported hanging punctuation into current line placement and kerning modules, preserving forced breaks, bounded edge-tip row merging, justified-last-line behavior, area exclusions and independent Roman orientation shaping; kept PSD/engine/UI regression coverage.
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Ported upright Roman classification, independent unkerned/unligated em advances and tab reflow into current Para/VerticalPiece shaping modules; preserved area and paragraph flow; retained vertical geometry and PSD BaselineDirection regressions.
Main now carries #9031 (Halftone) and #8923; this branch keeps only what it adds on top: the guard for a setting beside a Gallery stack's effect list, its test, and the PSD open warning for patterns that paint nothing.
Vertical type laid Latin letters and digits on their side (the word
turned 90°), and called that Photoshop's default. Photoshop's Vertical
Type tool stacks them upright, one character per em down the column:
the run's BaselineDirection is 1 (withStream); 2 (rotated) turns them.
- doc: CharStyle::rotate_roman (false = upright, the default).
- text/psd: BaselineDirection is read (a run without one takes the
style sheet's, which Photoshop writes as 2) and written back.
- text/layout: in vertical type, roman characters take the upright
path unless their run rotates them. Each is given one em down the
column (a second shaping pass sets its letter spacing from the shaped
advance), with pair kerning off between the stacked letters.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A pattern past the per-call budget is converted under one lock and shared while in use, with a one-time warning, and the desktop budget keeps every pattern a call uses (1 GiB). The web build caps a pattern's working copy at 128 MiB and a call at 256 MiB, and the pattern readers reserve their memory fallibly on every target. The legacy text restyle writes and compares BaselineShift, FontCaps, FontBaseline, UnderlinePosition, StrikethroughPosition and the ligatures per run, keeps an automatic leading's own value, and import reads the legacy AutoKern; a corpus test edits the size in psd-tools' text.psd and checks the rest of the legacy object is unchanged.
Align measures shape extents at the document's own sample type, a render keeps two of the largest patterns converted per call instead of rebuilding the second for every tile, a pattern too large to paint is named by every command that would use it, by pattern.list and by a PSD open warning, and a style-only edit restyles the older Photoshop's legacy text objects too, checked against the real psd-tools text.psd.
Return the pattern code to the state before the earlier 8000 x 8000 attempts. A pattern that fits the compositor's 256 MiB cap is expanded as before; a larger 8-bit or 16-bit one is held as straight RGBA8 or RGBA16, exactly its values at 4 or 8 bytes a pixel, so Define Pattern's 8000 x 8000 paints at 8 bits. Define Pattern refuses an area the compositor could not hold with its limit, patterns whose RGBA32F texture passes 256 MiB paint on the CPU, shape bounds for Align keep full alpha precision, Define Pattern judges opacity on the stored samples, and a Halftone Gallery stack with an unknown setting beside its effect list stays verbatim.
The GPU path keeps its RGBA32F pattern textures and shader, and declines a pattern whose texture would pass 256 MiB so the CPU compositor, which holds an 8-bit pattern at 4 bytes a pixel, paints it. Add tests for the fallback, the pattern id staying the same when read in bands, and the paintable limits.
Define Pattern accepts 8-bit patterns up to 8000 x 8000: an 8-bit pattern is held at 4 bytes a pixel by the compositor and the GPU, read in bands, and what is paintable follows the working-copy bytes. Shape bounds for Align keep full alpha precision, Define Pattern judges opacity on the stored samples, and a Halftone Gallery stack with an unknown setting beside its effect list stays verbatim.
Rect::tile_count counts a rect's tiles in u64; try_take_tiles uses it (no usize overflow on 32-bit). Surface::translated reuses one rebuilt tile for equal source-tile windows. auto_slices merges vertically adjacent runs through a hash index instead of searching the output.
- crates/ui-egui/src/layer_style.rs: values_of keeps the PR's method/smoothness keys and source-stop ends, and main's (#7072) unconditional From/To and geometry fields.
- crates/compose/src/cmyk_tests.rs (no textual conflict): add the PR's new `interpolation: None` field to main's CMYK test GradientOverlay literal.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- crates/engine/src/tests.rs: kept both tests (main's restore_layer_selection test and the PR's history_states_share_large_document_payloads).
- crates/io/src/psd_import.rs: kept main's parsed Duotone inks (duotone_map) with the PR's Arc<[u8]> psd_raw; also main's unknown-adjustment-key import (#6575) now builds Arc<[u8]> raw.
- crates/io/tests/adjust_warnings.rs: moved to tests/it/adjust_warnings.rs on main; PR's .into() edits applied there, plus #6322's Color Lookup test (raw.as_slice().into()).
- docs/architecture.md: union; status line names both changes, both revision-history rows kept.
- Adapted main-new sites to the PR's types: psd_export.rs (Duotone write-back via as_deref/to_vec, Unsupported block compare by slice), inspect.rs and ui-egui unsupported_marker_tests.rs (raw .into()), io tests/it/psd_unknown_adjustment.rs (slice compare).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Main merged #5940, #6472, #7838 and #7922 directly. Kept from the batch:
the snapshot/History-follow identity at the history limit (#5940,
c7ef3676a; main still had the 3-tuple HistorySpot) and the strokeless
shape at a 0 px width step (#6472, 962539607; main still added the
stroke). Main already carries the other two fix-ups (34c9d0ea4, dff8bfd0e).
Overlaps with main resolved:
- #5842 vs main's #5568: one path, apply_color, tool-gated as #5842
has it, with #5568's float fill, kept fill opacity and lock skip;
shape_fill_ui's duplicate foreground_changed/gesture dropped; #5568's
tests run with a shape tool.
- #6230 vs main's #5180: one Type tool path click (path_target), no
second type.attachToPath; vertical type does not start type on a path,
as main has it; main's path_at/path_cursor and canvas arm dropped.
- #5452 moved the shape swatch to shape_paint_ui.rs: main's hex field
(#6201) ported there.
- type_extra_cmds::with_text takes main's dpi form, pub(crate) for
type_area_cmds.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
crates/engine/src/file_cmds.rs: batch keeps the save-point closure (takes the output path) and also copies the session's authorize hook into each scratch session, as main now does.
crates/raw/src/sensor.rs: both sides cap the memory of retained tiles; kept the existing retained_segment_bytes check (checked multiplication, same limit) and dropped main's duplicate inline loop.
auto_slices tested every grid cell against the full slice-rectangle list
(covered(x, y) = taken.iter().any(...)). With slice edges forming the
grid, that is cubic in the slice count, so a crafted document's slice
arrangement could stall ordinary canvas drawing on the UI thread.
For each row band, mark covered x-cells in a single pass over the rects
that span the band, then scan the precomputed cells. This drops the inner
work from O(rects) per cell to O(1), while producing the same tiling.
Finding: 9fbea43b-3096-4cdc-b005-86d44a8fe57e (CWE-400)
Conflicts, both sides kept: main's .act swatches next to the batch's .asl
styles (Open filters, preset extensions), main's Render smart filters next
to the batch's Distort ones (53 mapped), parity doc status lines.
docs/scorecard.md regenerated.
Follow-ups the merge needed: the Distort smart filter readers pass their
enum type to main's stricter enum_name (the type their writers use), and
the CMYK effect tests set main's new StrokeFx overprint and Bevel
gloss_anti_alias.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Integration: resolve with main for area type (#6230)
- Image Size with a new resolution (#7124, canvas_geom::set_resolution) now scales an area type's
path with the text space like a box or a type path, so the same words wrap onto the same lines;
test a_resolution_change_keeps_area_type_wrapping_where_it_was fails without it.
- The Paragraph panel's justify buttons (#7713) stay available on area type, which is paragraph
text in Photoshop; test justify_buttons_work_on_area_type.
- Test files: main's tests plus #6230's; layout.rs imports joined; docs and scorecard regenerated.
A layer keeps its own XMP packet (Layer::xmp), where scripts and embedding
apps store data about the layer under their own namespaces.
- PSD/PSB/layered TIFF: read from and written to shmd > cust > layerXMP
(UTF-16, NUL-terminated, after layerTime), laid out as Photoshop 2026
writes it; an unchanged Photoshop block round-trips byte for byte.
- .pcraft: optional `xmp` on each layer.
- layer.xmpMetadata {layer?, rawData?}: read, set (one undo step) or
remove with null; refuses malformed XML, DTDs, NUL and > 4 MiB.
- Duplicate Layer copies the packet; document.inspect reports hasXmp.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Photoshop's Paragraph panel checkbox, per paragraph style:
- doc: ParagraphStyle.hanging (serde default off; .pcraft keeps it).
- layout: a trailing run of closing marks (. , : ; ! ? and fullwidth
twins, ellipsis) trims out of the aligned width on center/right lines,
so a centered line ending in an ellipsis stays centered; a wrapped row
of nothing but marks steps back onto the line above and hangs past the
box edge (contiguous ranges, merged-row baseline, manual kerning; no
RTL, vertical or tabs).
- PSD: /Hanging bool in the paragraph sheet, right after /LeadingType as
the corpus files show; read defaults off.
- engine: type.setStyle takes "hanging":bool, params doc updated.
- UI: checkbox in the Paragraph section, a Paragraph Styles editor row,
Character dock group grows one row; translations for the new label.
- tests: layout hang runs (trim, merge-back, plain no-op), PSD write and
paragraph-sheet read round trips, engine setStyle, panel harness.