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).
Main's noop_step now takes the step's params (an Arrange that cannot move the layer is a no-op too); it is public for the CLI's run and batch, which pass them.
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).
The reference application marks a paragraph box that can't hold its text
with a black "+" inside an enlarged lower-right handle; nothing is drawn
below the box. The PR drew a red disc 0-9 px below the box at a fixed
screen offset, so on a rotated layer it came loose from the box, a click
on it either grabbed the corner handle or ended the edit, and its hover
card popped up while dragging that same corner to enlarge the box.
Now the lower-right handle grows to 11 px and carries the black "+",
centred on the transformed corner, and the whole enlarged handle grabs
that corner, also in a rotated view. The disc, the hover card (state, its
tooltip row in every catalog), the GPU pixel test and its pollster
dev-dependency are gone; detection (`TextLayout::overflows`) and the
`typeBoxOverflow` inspect flag stay. docs/ui-parity.md: the overflow row
no longer claims box threading, the Guides row the PR duplicated is
removed, and the history has one row in date order.
Tests: `an_overflowing_box_shows_a_plus_in_its_enlarged_lower_right_handle`
(upright and rotated box; fails on the PR head with a 7 px handle) and
`the_whole_enlarged_overflow_handle_grabs_in_a_rotated_view`.
Co-authored-by: VladimirPozdnyakov <49595925+VladimirPozdnyakov@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Reconcile captured Pen and Freeform targets with current resumed-subpath, Type Path, Path Operation and draft recovery behavior. Valid source-owned tab switches commit to captured original target; invalid snapshots cancel with a diagnostic; refused commits retain draft and suppress automatic retries. Preserve current Saved(index) API and hidden-work-path protections, register all new ownership/gesture tests and add original-target tab-switch/deleted-target cases. Includes verified compile/API corrections from prior checkpoints.
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Add bounded deterministic suggestions via current error/session/control modules while preserving authorization/modal checks. Register hints and all original tests; fix scrollbar choice import and updated PSD image fixture field found by 100-head check.
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Use shared zoom-corrected brush resize and HUD while preserving immediate press ownership and anchored outline when hover is absent. Keep original resize regressions. Fix duplicate identical Dissolve test registration and missing test-only UnicodeString import reported by 94-head check.
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Apply stricter finite 1e9 geometry bound to current parse/import safety paths; prevent corrected legacy origin bypass and retain current full PSD unsafe-import warning/raster fallback regression. Keep split import/build modules and port new overflow tests into import module.
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Extended current crop menu with Reset and shield toggles, preserving newer preset management, overlays, ownership checks and no-menu-during-gesture logic. Ported reset-frame no-op/undo semantics into current CropState/release and crop commit; retained original regression cases in split modules.
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.
Served script fallbacks were appended to every UI font family in the
order they arrived. A Japanese and a Simplified Chinese face both cover
Han, and egui draws a character with the first face in the stack that has
it, so a Chinese interface drew Han with the Japanese face when that one
had arrived first (fetched for a Japanese text layer, say). ui_faces now
puts the interface language's own served face first, so the stack a
language change rebuilds follows the language; and when that face arrives
after another served face, add_ui_face rebuilds the stack the same way
instead of appending. served::fallback_for_language gives the face.
This orders the served faces only; with an interface language that has
no served face (English), they stay in arrival order.
Review finding on #10006.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
- Character section: a style picked under a family's ">" with type layers
selected set the layers but not the style for new type (the options bar
did both). Pick "Inter > Thin" in Properties > Character: new type stayed
Regular.
- Faux bold: a pointed tip moved r/sin(half its angle) along its bisector,
uncapped (3.9 r at 30 degrees, an A apex grew a spike), and tips sharper
than about 20 degrees did not move at all. Every tip now moves at most
2r, continuously; inner corners (the crotch of a V) keep their exact
miter.
- Faux bold: each point searched past runs of coincident points for its
neighbours, O(n^2); a contour of 50 000 copies of one point took ~8 s per
glyph. Neighbours now come from one O(n) pass over the runs.
- prefs.reset {"path":"type"} and Preferences > Type > Reset Section wiped
the favourite fonts; they are kept now (type.favoriteFonts by name, or
Reset All, still clear them).
- An expanded family whose own row scrolled out of view lost its style
rows, so the list below jumped up.
- Show Similar Fonts re-sorted around each font an arrow step applied, so
Down went back and forth between two fonts; the list now compares with
the font current when the filter was turned on (or the menu opened).
- Docs: an uninstalled favourite is kept but not listed; FAUX_BOLD_RADIUS
and the raster budget describe the thickened outline; the unused
"Recent font list" rows are gone; docs/scorecard.md regenerated (the
favourite fonts are one more setting).
Co-authored-by: D-monized <341904702+D-monized@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main reorganised the source tree (command modules under engine/src/cmd, tools and dialogs under ui-egui/src, large files split into modules, inline test modules moved to their own files). This branch's changes to the moved and split files are ported to their new locations.
Preserve the PR's intended behavior and regressions while retaining upstream module extraction and changes. Formatting and independent static review passed.
Local verification: 247 text tests passed (5 ignored), including reordered duplicate-font metadata regressions. Clippy and WASM did not start because the local disk safety threshold was reached.
Local UI verification is incomplete. Hosted CI must establish the remaining gates; this commit does not claim the PR is ready to merge.
Resolve conflicts with the engine's cmd/ module move and the per-crate
integration test binaries. Run a selected VectorCraft.app bundle directly
(identified by bundle id) and reject nested gzip in bounded SVG parsing.
FredApps' re-review: hanging_marks_of_a_split_run_keep_their_breaks was
pinned by the chain guard, not by the at-most-two-cluster cap — reverting
the cap alone surprised every hanging test. a_full_mark_row_does_not_
step_back_onto_a_short_line pins the cap: 'word ' followed by thirty
hyphens in a 200 px box keeps three rows and the same geometry as
hanging off; with the cap reverted the full mark row steps back and the
line overflows the box by a row's worth of marks (test fails). The
split-run test's comment now matches its 34 dots.
CodeRabbit on #9478 (outside-diff comment, layout.rs 763-782): standard,
contextual and discretionary ligatures stayed on in upright roman runs,
so a font's "fi" became one glyph and broke one letter per em. They are
off there now, as kerning already was.
Test: upright_roman_letters_stay_apart_without_ligatures (uses an installed
font with an fi ligature; skips where none is found)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CodeRabbit on #9478: the upright default for roman text also caught
half-width katakana and Hangul, which vertical_class rotates like other
half-width text; this PR leaves CJK as it was. Only roman characters now
stand upright and take one em. tab_widths gets its doc comment back.
Test: half_width_cjk_stays_rotated_beside_upright_roman
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
davidmann4's round-3 review of #8347: a marks row the box had to split
out of a run folded row after row into one line running past the edge by
almost the whole run, and a row merged into a justified paragraph's last
line kept the row above's spread instead of the last-line align.
- A marks-only row steps back only while it is at most two clusters (the
tip that hangs at the edge); a row above that already ends in a
hangable mark takes no more.
- A justified paragraph's last row keeps its own align (centered, right
or left): the placed clusters already carry the spread, and the merge
only moves x1/range.end.
- hanging_marks_of_a_split_run_keep_their_breaks pins that a run keeps
its rows and no line outgrows the box by more than two marks;
hanging_marks_stay_off_a_justified_paragraphs_last_line pins the
JustifyCenter/Right/All last lines. The old merged-row test now pins
the tip (a_stepped_mark_row_keeps_its_clusters_and_glyphs_in_step).
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>
Two layers could update the same Txt2 frame, the last one winning. An
edited layer whose object can't be refitted (a run array whose length
is no count) was rebuilt from a template naming frame <slot number>,
which can be the frame another object draws in; and a new layer whose
slot number is a path layer's frame number named that frame too. A
point layer then turned the path layer's frame into a point frame, and
the path text reopened as plain text.
A rebuilt object now keeps the frame its old object was drawn in (when
the frame list has it), and a new or rebuilt object whose slot number
is another object's frame gets a free frame past the list (padded like
the others). A frame the file already had two objects drawn in is left
as the file has it. Tests (each fails without its part):
an_object_rebuilt_from_a_template_keeps_its_own_frame,
a_new_object_gets_a_frame_no_other_object_is_drawn_in,
a_rebuilt_object_never_names_a_missing_frame,
a_frame_two_objects_name_is_kept,
a_kept_object_naming_a_far_frame_adds_no_frames (the list only grows
for frames handed out here); an_area_layer_gets_a_box_frame
covers the merge with main's area type.
Answers CodeRabbit's review thread at crates/text/src/psd.rs:1242.
Co-authored-by: Michael Higgins <55243596+Higgs32584@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolves the conflicts with main's area type (TextShape::Area): EngineData keeps main's area arm beside the path comment, and a Txt2 frame of an area layer becomes a text box over the path's bounds, as its EngineData describes it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merge #9104 (raw EngineData strings are written back as the same bytes) so a style-only type edit does not re-encode Photoshop's raw strings as UTF-16, with tests for the numbered and the legacy restyle paths. The over-budget pattern warning is written without a panic path and explained where the lock is held.