Commit Graph
149 Commits
Author SHA1 Message Date
David Mann 8c04b175ec Merge remote-tracking branch 'origin/main' into integration-batch-7
# Conflicts:
#	crates/automation/src/rpc.rs
#	crates/automation/src/server.rs
#	crates/gpu/src/health.rs
#	crates/text/src/tests.rs
#	crates/ui-egui/src/i18n/zh-hans.tsv
#	crates/ui-egui/src/panels.rs
#	docs/control-protocol.md
#	docs/ui-parity.md
2026-10-24 00:23:15 +02:00
NetterSilverandClaude Haiku 5.5 c1158f92e8 Accept all-caps possessives in spell check (#8628)
Found while testing crates/text/src/spell.rs:91 (Dictionary::check)

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-23 17:40:22 -04:00
jturnbachandJohn Turnbach 316a07c8c9 fix(text): lay out a variable font's named instance from its PostScri… (#8909)
* fix(text): lay out a variable font's named instance from its PostScript name

A PSD type style set in a named instance of a variable font, such as
Bahnschrift Light Condensed, keeps the instance only as its PostScript
name ("Bahnschrift-LightCondensed"); its variations are empty. When
PhotoCraft re-laid the layer out, FontDb::select_named_face looked for a
face with that PostScript name, or one whose style and weight matched. A
variable family is a single face, so neither matched, and the text was
laid out at the font's default instance: about 35% wider than the
instance Photoshop drew with Bahnschrift.

select_named_face now falls back to the family's variable faces when the
PostScript name matches no face. It reads each face's fvar named
instances and compares the name with the instance's own PostScript name,
or, when the instance has none, with the name Adobe Technical Note #5902
derives (variations prefix or family name, a hyphen, then the subfamily
without spaces). Names compare without punctuation or case. On a match,
that face is selected and the instance's axis values are added to the
style's variations, keeping any axis the style already sets. Static
faces still match exactly as before, and PSD import is unchanged.

Tests:
- font_names_tests::postscript_name_of_a_named_instance_selects_its_axis_values
  rebuilds the bundled Inter at test time as a variable font (fvar with
  wght/wdth axes and two named instances, one with its own PostScript
  name and one derived) and checks the axis values each name selects, an
  unknown name, and a style's own axis value. It needs no installed font
  and no font file in the repository.
- font_names_tests::exact_static_face_gets_no_instance_axes checks that
  an exact static match gets no axis values.
- tests::postscript_name_of_a_variable_instance_lays_out_at_that_instance
  lays out "Hamburgefonstiv 0123" with Bahnschrift (Windows) or Ubuntu
  Sans (Ubuntu) when installed and checks that the instance's PostScript
  name gives the same width as its axis values. With Ubuntu Sans before
  the fix: UbuntuSans-CondensedLight 980.3 px (= the default instance),
  its axes 766.4 px; after the fix both are 766.4 px.

A style with only a weight (no PostScript name) is still not turned into
a wght axis value on a variable font; the issue reports that too, but its
fix and test are about the PostScript name, so that is left for a
separate change.

Fixes #8797

* fix(text): try a variable font's named instance before the style fallback

Review pointed out that select_named_face ran the style-and-weight
fallback before the named-instance lookup. A style that carries a named
instance's PostScript name and also a populated font_style matching the
variable face itself ("Regular", 400) selected that face at its default
instance, and the instance's axis values were never added.

Faces are now tried in order: exact PostScript name, named instance,
then style, weight and italic.

Review also noted that the layout-width test only runs when Bahnschrift
or Ubuntu Sans is installed. The synthesized variable font in
font_names_tests now carries an HVAR table that narrows every advance on
wdth, so layout width shows which instance was used. Two tests run
everywhere:

- named_instance_wins_over_a_matching_font_style
- postscript_name_of_a_named_instance_lays_out_at_that_instance

Both fail before this change. The installed-font test is kept as a check
against a real font.

---------

Co-authored-by: John Turnbach <jturnbach@users.noreply.github.com>
2026-10-23 17:39:30 -04:00
NetterSilverandClaude Haiku 5.5 45833ece7c Fix EngineData format_real emitting a bare trailing dot (#8486)
Found while testing crates/text/src/engine_data.rs:331 (format_real)

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-23 17:39:13 -04:00
FinnNkandClaude Opus 5.5 b8ee65e030 test(text): per-colour passes match full-size passes (#8716)
The rasteriser made one coverage pass per distinct colour over the whole
text's ink rect (nine with faux bold), so text with many coloured runs
cost colours x area: 400 colours of faux-bold 48 pt text took 13.0 s.
Each colour's pass now covers only that colour's glyph bounds (all bold
offsets and decorations included) and composites into that part of the
accumulator: 163 ms for the same text. A test compares the visible
(premultiplied) pixels with full-size passes for plain, faux bold,
underlined, rotated and warped text in RGB and CMYK.

Part of #8602

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 17:38:42 -04:00
aiongg 7aaa59712a Merge main into integration-batch-7 2026-10-23 20:37:05 +00:00
Dylan M. TaylorandCodebuff 96042b8e7a fix(text): bound untrusted font and PSD text processing (#8768)
* fix(text): bound font paths and imported text processing

Reject encoded path tricks and cap resource-heavy font and PSD text processing to keep untrusted inputs bounded.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>

* fix(text): reject percent-encoded DEL in served paths; satisfy clippy chunks_exact lint

---------

Co-authored-by: Codebuff <noreply@codebuff.com>
2026-10-23 09:40:04 -04:00
David Mann 8d23a06eae Merge PR #6094: Fix legacy PSD point-text placement after font resolution 2026-10-22 23:40:19 +02:00
fd8a2856d4 Integration: line the layout's ink box up with the TySh ink box (#6094)
refine_legacy_point_origin moved the layout's logical box (line x0,
baseline - ascent) onto the TySh boundingBox, but Photoshop writes
boundingBox as the tight ink box (Arial 120 pt "..." has bounds top
-102.98, boundingBox top -11). Legacy point text whose ink starts below
the ascent line ("...", "_", lowercase) opened up to ~116 pt too low.

Match like with like: use the layout's unrounded text-space ink bounds
(new render::ink_bounds). The PR's tests built boundingBox from the
logical box, so they now write bounds = layout box and boundingBox =
ink box and pin the exact anchor; legacy_origin_lines_up_ink_with_the_
ink_bounding_box covers "Name", "..." and "_" and fails without this.

Co-authored-by: bogusyogi <102761952+bogusyogi@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 21:29:29 +02:00
David MannandClaude Opus 5.5 018d3605b1 Integration: merge main (#6094)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 21:13:36 +02:00
David MannandClaude Opus 5.5 f5533e5d82 Integration: merge main into batch 6
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>
2026-10-22 19:20:26 +02:00
aiongg 576ba0e02b Integration: track t12
# Conflicts:
#	contributors/people.toml
#	crates/compose/src/lib.rs
#	crates/compose/src/tests.rs
#	crates/engine/src/print_cmds.rs
#	crates/engine/src/print_cmds/tests.rs
#	docs/target-app-parity.md
2026-10-22 15:38:33 +00:00
David MannandClaude Opus 5.5 7a54fa9c77 Integration: merge main into batch 6
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>
2026-10-22 12:09:25 +02:00
aiongg 5fd5b7adcb Merge PR #7152: Type: register craft fonts of every script, not only the Japanese ones 2026-10-22 02:25:56 +00:00
aiongg bf68c22689 Merge PR #6001: Text: admit the rasterizer's peak working bytes (#4979) 2026-10-22 02:23:35 +00:00
aiongg 9e97c9bd79 Merge PR #8146: fix(psd): an edited type layer updates the the reference app-era Txt2 objects too (#6971) 2026-10-22 02:23:11 +00:00
aiongg 11c3f1ec92 Merge PR #5180: Type tool: click a path to start type on it (TYPE-218-1, #218)
contributors/people.toml: union of both sides' entries.
crates/ui-egui/src/canvas.rs: main's Move cursor (arrow) plus the PR's Type path-cursor arm.
crates/ui-egui/src/type_tool.rs: main's has_transform in draw_overlay; the PR's earlier `shape` binding kept, the duplicate binding dropped.
crates/ui-egui/src/vector_ui.rs: main's draw_overlay (paths already show with any tool), the PR's vector_tool line dropped.
docs/gaps.md: header line from main, G8 text from main with the click-on-path clause, change-log lines of both.
2026-10-22 02:22:58 +00:00
aiongg 0fe1d3233d Merge PR #5907: Text: apply anti-alias mode shaping when rasterizing type (#5902) 2026-10-21 21:56:20 +00:00
David Mann a8f91f54ad Merge PR #6230: feat(type): area type, text that wraps inside a closed path or shape
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.
2026-10-21 23:46:05 +02:00
David Mann 27b8d133d8 Merge PR #4771: Fix mixed-order layered TIFF type geometry and unsafe text fallback 2026-10-21 11:28:23 +02:00
VishnuByrraju 013863da95 fix(psd): an edited type layer updates the Photoshop-era Txt2 objects too (#6971)
Older Photoshop keeps each type layer's text object at
/DocumentObjects /TextObjects (named keys, run lengths under /Length) and
re-renders from it. type.edit refreshed TySh and the numbered /1 /1 list but
left the old text there, so the layer came back with its old text.

build_txt2_at now refits that entry like edited_object does for the numbered
ones: the model text, one ParagraphRun run per paragraph, every other run
array cut or extended to the new UTF-16 total. Entries are only edited in
place (never grown), a malformed run array leaves the entry as the file has
it, and an unchanged block is still written back byte for byte. A legacy
block holding both lists with a stale older copy is repaired on re-save.
2026-10-21 10:39:00 +05:30
aiongg b097800e78 Merge pull request #8095 from aiongg/integration-7
Integration batch: 9 PRs on main
2026-10-20 20:44:51 -04:00
152c9677da Integration: keep unreadable canonical type as a type layer (#4771)
A normal big-endian TySh that the typed reader rejects (an unknown text
descriptor version, a stub from another writer) opened as a text layer on
main, with the best-effort text and its TySh saved back verbatim. With the
PR it became a raster layer, and the PSD export strips TySh from rasters,
so open + save turned the type layer into pixels for Photoshop. Now only
foreign-order blocks and unsafe transforms become pixels; the old fallback
runs under the same transform gate (shared as
photocraft_text::psd::transform_is_safe). The warning no longer claims the
type data is kept.

Test: canonical_type_the_typed_reader_rejects_stays_type_and_saves_its_block
(descriptor version 17, ordinary matrix) fails without this change.

Co-authored-by: Sean Lavery <702713+TheSeanLavery@users.noreply.github.com>
Co-authored-by: Tanish Manku <13985398+tanish2k09@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 01:14:50 +02:00
David MannandClaude Opus 5.5 d41aae7849 Integration: merge main, resolve conflicts (#4771)
crates/text/src/psd.rs: keep both appended test modules (transform_safety_tests
and main's nonfinite_tests). docs/file-format-parity.md: add the #4758 item to
main's current change line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 00:54:54 +02:00
13733ba3a9 Integration: hide area type that reaches the shape's narrow bottom (#6230)
When area type overflowed a closed shape, a word too wide for the narrow
bottom rows of an ellipse stayed in its span and was drawn past the outline
(a 260x160 bubble at 12 pt drew "misunderstanding" from x 110 to 212 over a
110..149 span). The shape's wider rows would hold such a word, so the shape
has just run out of room: that line and the rest are now hidden as overflow.
The check looks at every row of the shape, not just the paragraph's own (a
later paragraph starts below the wide top of a funnel), and also runs when
the line-breaking pass budget is spent. Only a word no span in the shape
holds still overflows its span.

The TYPE-218-10 note said such a word overflows "the way box text does";
since #6694 box text breaks it at the edge. Corrected, and the other known
gaps (first line about 0.24 em lower than box text, PSD area type opening as
box text, no handles for the area path) are listed.

Co-authored-by: Silly Silk <31193727+SillySilk@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 23:23:54 +02:00
f44e8e4be8 Integration: merge main, resolve conflicts (#6230)
docs/scorecard.md was the only conflict: regenerated with `cargo xtask scorecard`
on the merged tree.

Co-authored-by: Silly Silk <31193727+SillySilk@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 23:08:25 +02:00
David Mann 0cbfb859ed Merge PR #6169: Preserve imported PSD font metadata during text edits 2026-10-20 22:39:15 +02:00
David Mann 2adfe0c4e2 Merge PR #5807: Type: keep superscript and subscript when a text layer is re-rendered (#5645) 2026-10-20 22:39:12 +02:00
aiongg 44ef277439 Merge PR #6693: macOS: list fonts activated outside the font folders (#6683) 2026-10-20 18:39:29 +00:00
aiongg b82540845f Merge PR #7773: Give a new type layer a full Txt2 object and its own frame 2026-10-20 18:39:28 +00:00
NetterSilverandClaude Opus 5.5 e86e6910ae fix(psd): bound the base style run a save copies per run
The base run comes from the file and is cloned once per character run, so
it now has to fit the template node budget, and each font is looked up
once per name. Also names the reopened-layer tuple in psd_kerning for
clippy::type_complexity.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 20:07:39 +02:00
NetterSilverandClaude Opus 5.5 20c9103148 fix(psd): write complete Txt2 style runs from the layer, number only file indices as kept
Photoshop reads the character style from Txt2 and quit on the bare run
(`/0 0 /11 mode`) that new objects got. New and edited objects now get one
style run per character run, built on one of the object's own runs with
every key kept, and font, size, faux styles, leading, scales, tracking,
fill colour, OpenType keys and auto-kern mode set from the layer. A font
the block's font list lacks is appended to it. Edited objects therefore no
longer keep the old text's features by position.

build_tysh no longer writes TextIndex 0 for a layer without one: the
export took it as a kept number, so a Type tool layer landed on object 0
(a deleted layer's, or the one of a layer above it). set_text_index puts a
missing TextIndex right before EngineData, where Photoshop writes it.

pad_frames pads to at most MAX_TEXT_INDEX + 1 frames.

edited_text_keeps_the_files_txt2_object (#7220) now expects style runs
that follow the layer's runs and size.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 19:48:40 +02:00
NetterSilverandClaude Opus 5.5 760109e23c fix(psd): give a new Txt2 object its own style and a number past the file's objects
A new object kept the template's style runs, and PhotoCraft's reader takes
font features from Txt2, so a new type layer came back with the template
layer's features (salt, ss01). Its style runs are now generated like the
stub's; only the template's structure is copied.

New type layers were numbered past the kept TextIndex values only, so after
deleting the layer with the highest index a new layer landed on its object
and took its style. They are now numbered past the file's Txt2 objects too.

txt2_extras_survive_a_resave_of_unchanged_text now gives its layer the TySh
index a file's layer has; without one the layer is new and no longer lands
on object 0.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 15:57:10 +02:00
NetterSilverandClaude Opus 5.5 304f055021 fix(psd): point a new Txt2 object's frame strike at its own frame
new_object copied the template's rendered strikes, whose frame strike
(/99 /F) still named the template's frame in /10. It now names the new
object's frame, like the view's /0.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 14:04:33 +02:00
nrq6993-a11yandCursor 5be2b29463 Merge upstream/main into stream/issue-5902-text-antialias.
Resolve render.rs by routing parallel composite through shape_antialias_coverage.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-20 08:11:03 +03:00
jonathnwxh 28f27f5a11 Index imported font metadata fallback by name 2026-10-20 07:14:09 +08:00
aiongg b8e856dffe Integration: #7022 write_region stays serial; the text rasterizer uses the parallel variant 2026-10-19 17:18:46 +00:00
David Mann 8e54897d43 Merge PR #7022: perf(text): rasterize a type layer's pixels on all cores, bit for bit the same 2026-10-19 16:51:41 +02:00
NetterSilverandClaude Opus 5.5 604007490b fix(psd): replace an earlier save's Txt2 stub also when its text is edited
The stub filter applied only to the kept object, so an edited layer whose
slot held a stub from an earlier save went through edited_object and kept
the bare stub. The slot is now filtered before both lookups.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 15:33:05 +02:00
NetterSilverandClaude Opus 5.5 198fa59c7d fix(psd): a new type layer gets a full Txt2 object and its own frame (#7698)
A type layer the file's Txt2 has no object for (one added in PhotoCraft)
was written as a bare stub with no view, no paragraph runs and no frame
in /0 /8 /0. Photopea and Photoshop refused the file once the objects
outnumbered the frames.

build_txt2 now gives such a layer an object shaped like one of the
file's own (refitted with edited_object from #7220), with its view
naming its own frame, and pads the frame list to one per object. A stub
written by an earlier save is replaced the same way, so re-saving an
affected file repairs it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 14:56:15 +02:00
Brian TorresandClaude Sonnet 5.5 ea0eec98a3 macOS: list fonts activated outside the font folders (#6683)
The installed-font scan read only /System/Library/Fonts, /Library/Fonts and
~/Library/Fonts (plus PingFang). Fonts macOS offers from anywhere else, such
as those a font manager, Adobe Fonts or Office activated, never reached the
font menus although other apps showed them.

load_system_fonts now also registers every file Core Text offers. The list
comes from fontique's Core Text backend, enabled for macOS only, used as a
scratch collection whose families are discarded: only its file paths are read
and registered in our own collection, so generic-family defaults and the merge
of installed faces into the bundled families are unchanged. The reserved UI
fonts under /System/Library/PrivateFrameworks are skipped (fontique cannot
draw their outlines).

On a stock macOS install the folder scan already listed all 183 Core Text
families; the gap is fonts Core Text knows from elsewhere. With Hack activated
from a scratch folder via CTFontManagerRegisterFontsForURL, the list had 313
families before and 314 after (Hack added, none removed), type.fonts lists it
and a type layer renders in it. The scan stays 37-57 ms.

Tests: font_files_of_lists_each_font_file_once (all platforms);
coretext_lists_the_system_font_files and every_family_coretext_offers_is_listed
(macOS). The last fails without the call when a font is activated elsewhere
("lacks: Hack") and passes with it.

Cargo.lock gains fontique's optional per-platform dependencies for the
`system` feature; `cargo tree` shows the feature only for aarch64-apple-darwin.

Still open: Windows fonts outside the two folders (#3336) and the web build.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-18 16:36:36 -07:00
PanPDX b2f18f7e4e Merge remote-tracking branch 'upstream/main' into area-type 2026-10-18 12:47:22 -07:00
aiongg ccb62fcd8f Merge PR #6038: Type: Korean paragraph text wraps between words, not syllables (fixes #5959) 2026-10-18 14:18:59 +00:00
PanPDXandClaude Opus 5.5 27ad078860 fix(text): area type breaks at the span width divided by horizontal scale
Horizontal scale is applied after shaping, so box text already breaks at
the box width divided by it (#5565). Area text broke at the full span
width, so widened text ran past its spans and outside the shape, and
centred lines were off by the same amount. Lines now break at
span / scale and the overflow check compares scaled widths. Raised in
review on #6230.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V5GRiHVR8XvTrbL1j8y9jW
2026-10-18 02:47:01 -07:00
PanPDXandClaude Opus 5.5 6ed536ce34 Merge upstream/main into area-type
Conflicts: upstream's horizontal-scale line breaking (#5565) touched the
same break and alignment code as area type. Box text keeps breaking at
the scaled width; area rows break at their span widths as before, with
the scale fixed at 1, since area spans are already in layout space.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V5GRiHVR8XvTrbL1j8y9jW
2026-10-18 00:16:17 -07:00
Higgs32584andClaude Opus 5.5 204c76ce36 fix(psd): an edited type layer keeps its Txt2 object instead of a bare stub (#6968)
When a type layer's text changed, build_txt2 replaced the layer's object in
Photoshop's own Txt2 block with a bare `<< /0 << /0 text /6 runs >> >>`:
no paragraph runs (/5), no view (/1, which names the object's frame) and none
of the style keys. Photoshop 27.10 quits while opening such a file
(layer_effects.psd after type.edit), as it refused stripped objects in #2374.

An edited object now starts from the file's object at its slot: the text is
replaced, the paragraph runs are remade one per paragraph of the new text,
and every other run array of the model (style runs, ...) is cut or extended
to the new length before the auto-kern modes are applied. The view and other
keys are kept; only the old text's glyph pen positions (/21) are dropped.
An object whose run arrays aren't shaped as Photoshop writes them still falls
back to a fresh one.

Test: text edited_text_keeps_the_files_txt2_object.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 20:03:18 -04:00
dotCooCoo 263dd041a6 Type: register craft fonts of every script, not only the Japanese ones
build.rs embeds every font in craft-fonts' fonts/manifest.txt, but
FontDb::new registered only the entries whose scripts include Jpan.
craft-fonts 8dcdacd5 adds Noto Sans CJK SC and Noto Sans Arabic, and
3d9ab3a1 adds Noto Sans Thai. Built with either checkout, the binary
carried those fonts, but the font list did not show them, text layers
did not fall back to them, and works_without_craft_fonts failed with
"Noto Sans CJK SC registered under its manifest name". Chinese and
Arabic set in Inter in a TextEngine::new() engine drew .notdef glyphs.

FontDb::new now registers every CRAFT_FONTS entry. A single-face file
registers under its manifest family, whatever family its name table
gives; each face of a collection keeps the family its name table gives.

With Noto Sans CJK SC built in and no Simplified Chinese system font,
Japanese text set in Inter takes Noto Sans CJK SC when the UI locale is
not Japanese, following cjk::script_order.
craft_fonts_cover_japanese_without_system_fonts now expects the first
CJK craft family in that order.

New tests register in-memory craft entries made from Inter (Hans, Arab
and Jpan entries, two whose name tables give another family, and a
collection), and lay out Chinese, Arabic and Thai in Inter when the
build has a craft font for the script.
2026-10-17 09:17:03 -07:00
ebosido444-ai fcd337e1fc perf(text): rasterize a type layer's pixels on all cores, bit for bit the same
A type render spends most of its time after the glyphs are drawn: composing each
colour's coverage over the premultiplied accumulator, dividing it back to straight
colour, and converting the tile bytes. All three are per pixel - no pixel reads a
slot another wrote - so they ran one after another on one core for no reason.

Measured on a 3.75 MP paragraph (4000 characters, 14 pt at 300 dpi, 1851 glyphs),
release, Windows 11, i5-10300H: rasterize 98.3-103.9 ms before, 60.3-61.8 ms after
(1.6-1.7x). Whole-render stage profile before: ink rect 4.1, draw 18.6-22.9, prefix
sum 7.0-8.1, composite 22.6-33.4, unpremultiply 11.2-14.0, write_region 23.8, prune
3.1 ms.

The glyph drawing, the winding prefix sum and the ink-rect pass stay on one thread:
the first two carry a running accumulator across pixels, and the band split that
would keep them bit-identical costs a second glyph traversal per band.

Native only (rayon, as the other crates): the web build keeps the serial loops.
2026-10-17 17:42:50 +03:00
Muhammad Saad f13731a51a Text: count both coverage buffers on the plain path (#4979)
Coverage::finish allocates its output while the accumulator it reads is
still live, so a plain pass peaks at two coverage buffers rather than
one. The budget charged one, which contradicted finish's own doc comment
and left the plain path under its real peak. The bold path's three
buffers were already right.

The exactness assertion in the budget test now pins 18 bytes a pixel for
a grey 8-bit plain render, and fails if the undercount comes back.

Caught in review by @FredApps.
2026-10-17 14:26:50 +04:00
David MannandClaude Opus 5.5 3d2914368d PSD type: clamp warp bend and distortion before the f32 cast (#6856) (#6857)
A TySh warpValue, warpPerspective or warpPerspectiveOther beyond f32 became inf and the warp was silently dropped; they are now clamped to -100..100 like type.warpText.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 20:17:27 -06:00