Commit Graph
158 Commits
Author SHA1 Message Date
sanyamsingh001andCodex c07945b934 fix: keep wrapped Type fallback on grapheme boundaries
Co-authored-by: Codex <noreply@openai.com>
2026-10-19 22:19:53 +07:00
sanyamsingh001andCodex de4c6b1a6d fix: keep Type arrows on grapheme boundaries
Co-authored-by: Codex <noreply@openai.com>
2026-10-19 22:19:53 +07:00
bflatastic 2ae992870b Type: a zero width non-joiner is shaped in the face of the letters around it
Squashed from #2670 (its merge of main dropped).
2026-10-19 00:37:50 +07:00
bflatastic eec304dde1 SVG export: a Japanese face's numbered weight (W0–W9) is its font-weight, W6 600 (#2873)
style_weight reads W0–W9 style names (Hiragino, Morisawa…) as their weight classes before the English words, so SVG and CSS export write font-weight 600 for W6, and nearest-style matching weighs them too.
2026-10-18 19:02:52 +07:00
bflatastic 3041b6a672 Tests: the font-service checks of #579 and #2576 are one test, since the platform lister is set once per process (#2884) 2026-10-18 18:56:38 +07:00
nrq6993-a11yandCursor 640c87b537 Fix #2576: poll for fonts font managers activate while the app is in front
Font services (NexusFont, Adobe Fonts) register faces with the OS without
touching font-folder mtimes; only re-asking the platform lister sees them.
Check every two seconds as well as on window focus, and test that path churn
is detected on the system font scan.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-18 18:46:03 +07:00
Davit Matchakhelidze 7cf8dd4764 i18n: Georgian (ka) interface language
Squashed from #2045 (merge commits from main dropped).
2026-10-18 18:09:14 +07:00
bflatastic e342c2843a Tests: the bidi caret test's glyphs carry Glyph Scaling's scale of 1 (#2771) 2026-10-18 13:26:54 +07:00
VictorandClaude Opus 5.5 a275496ef4 G4: Paragraph panel menu › Justification… (word and letter spacing, glyph scaling, auto leading, single word justification)
The Paragraph panel menu's Justification… was a disabled stub, and justified
lines stretched word spaces between fixed limits (80% to 150%).

Each paragraph now has Justification options: Word Spacing, Letter Spacing
and Glyph Scaling (Minimum, Desired, Maximum, in percent), Auto Leading and
Single Word Justification. Desired applies to every line; a justified line
fills its measure with word spacing first, then letter spacing, then glyph
scaling, each within its range, and past them its word spaces take the
rest. The Every-line Composer scores lines by that flexibility. Auto
Leading sets the leading of characters whose leading is Auto. A justified
line holding one word is spread between its letters (Full Justify) or
aligned left, centre or right.

The defaults are the documented ones (80/100/133, 0/0/0, 100/100/100, 120,
Full Justify), saved only when they differ. Because the default maximum
word spacing is 133%, justified paragraphs break differently than before;
the Every-line Composer's second pass is now unbounded for justified text
(it used to give up past four times the stretch and fall back to the
Single-line Composer, which the narrower default range would make common),
and lines that can't stretch at all are still refused.

text.setFormat takes wordSpacing, letterSpacing, glyphScaling ([min,
desired, max] or {min?, desired?, max?}), autoLeading and
singleWordJustification; Minimum ≤ Desired ≤ Maximum must hold or nothing
changes. Paragraph styles carry the options (New and Redefine capture
them), and Reset Panel resets them. Justification… opens a dialog
(ui.justification) with Preview; OK is disabled while a row is out of
order. Live SVG text places characters the options move, and a glyph that
Glyph Scaling scales is stretched to its width; PDF uses the glyphs'
transforms. Vertical type isn't glyph scaled; type on a path doesn't use
the options.

Docs: docs/mcp.md, docs/type-parity.md, docs/gaps.md.

Closes #2755.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 12:40:14 +07:00
dotCooCoo bb7d6b487d Type: Left and Right reach both sides of a character drawn with no width
In type that holds right-to-left characters, Left and Right move the
caret through the visual caret stops (`caret_horizontal`). A cluster
drawn with no advance, such as a zero width non-joiner that the font
shapes as a glyph of its own, puts two stops at one x. A step kept the
stop with the lower byte, and the next step passed over the other one,
so the arrow keys never reached it. In Persian می‌خواهم laid out on
macOS, the caret could not be put between the non-joiner and خ with
the arrow keys.

Stops at one x are now visited one at a time in byte order. Going
right, the caret takes them in increasing order on left-to-right
glyphs and in decreasing order on right-to-left glyphs; going left,
it takes them in the reverse order. The end of a soft-wrapped line,
which the caret shows on the next line, is not one of those stops.
Where no two stops share an x, a step goes where it went before.
2026-10-18 12:34:32 +07:00
bflatastic 6a42b3bf10 Fonts: Han and kana keep the bundled fallback order; only the weight and slant of fallback glyphs change (#2491) 2026-10-18 01:11:38 +07:00
vangel76andClaude Opus 5.5 43356a1cfb Fonts: fallback glyphs keep the weight and slant, Bold Italic of a missing family keeps the bold, Han and kana prefer an installed sans (#2491)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-18 01:09:54 +07:00
vangel76andClaude Opus 5.5 b55d277b49 Fonts: a variable font's default face is named and weighed by its default instance (#2521)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-18 01:09:53 +07:00
VictorandClaude Opus 5.5 6190525482 G4: Paragraph panel menu › Roman Hanging Punctuation
The Paragraph panel menu's Roman Hanging Punctuation was a disabled stub.

It is now a per-paragraph attribute: in a left-to-right paragraph of point
or area type with it on, the punctuation Optical Margin Alignment hangs
(quotation marks, hyphens, full stops and commas wholly; asterisks,
tildes, ellipses, en and em dashes, colons and semicolons by half) hangs
outside the margin when it starts or ends a line, on the same sides
(left-aligned lines at their left, right-aligned at their right, centered
and justified at both); letters don't hang. With Optical Margin Alignment
on as well, letters hang too. East Asian punctuation keeps hanging only
through burasagari. The layout shares Optical Margin Alignment's hanging
code, given the share of each glyph that hangs.

text.setFormat takes romanHangingPunctuation; it is off by default, saved
only when on, carried by paragraph styles (New and Redefine capture it)
and turned off by Reset Panel. The panel menu item is a checkbox enabled
with type selected.

Docs: docs/mcp.md, docs/type-parity.md, docs/gaps.md.

Closes #2756.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 01:09:34 +07:00
dotCooCoo 4712fb2c2f Character: variable fonts get a slider for each axis
VectorCraft listed a variable font's named instances as styles, but
type could not be set between or past them. The reference app's
Character panel has a Variable Font control that adjusts a variable
font's axes, such as Weight, Width and Slant.

Type now keeps Variable Font settings by axis tag
(CharStyle::font_axes, saved as fontAxes only when set). text.setFormat
and text.setRangeStyle take fontAxes: each value is clamped to its
axis's range, a tag the font has no axis for is left out, and null
goes back to the style's own setting. Another font or style clears
them, including one from a character style that does not set them. A
character style captured from type sets them, with {} for none.
text.fontList {family, style} lists a font's axes.

Layout, outlines, the substituted-glyph highlight and PDF export use
the font instanced at the settings, and neighboring runs at different
settings are shaped apart, as runs in different fonts are. SVG export
writes them as font-variation-settings, the wght setting as the weight
and the wdth setting as font-stretch, and embeds the font instanced
there; SVG import reads font-variation-settings back. Legacy .ai export
warns that the settings are left out.

The Character panel shows a Variable Font button for a variable font.
It opens a slider and a field for each axis the font does not hide,
labeled with the font's name for the axis, and screen readers get each
slider under that name. A drag is one undo step. Reset Panel leaves
the settings, as it leaves the font and style.

Missing Glyph Protection now keeps the settings and the font version
on the characters that keep their font; before, it kept only the
family and style. A character style applied or redefined with another
font now clears the old font's version, as text.setStyle does, unless
the style sets one. SVG export with embedded fonts warns when faces of
one weight, style and width share characters, which browsers then draw
in one of the faces.

Docs: docs/mcp.md, docs/target-app-parity.md, docs/type-parity.md.
2026-10-17 19:38:46 +07:00
dotCooCoo d949c3d8b7 Type: Optical Margin Alignment hangs punctuation and letter edges outside the frame
Type › Optical Margin Alignment was a disabled menu item. The reference
app's Optical Margin Alignment hangs Roman punctuation and the edges of
letters such as W and A outside the margins of every paragraph of a
type object.

type.opticalMarginAlignment (Type › Optical Margin Alignment) turns it
on or off for the selected type in one undo step, and the menu item is
checked while the type has it on. In every left-to-right paragraph of
point and area type, quotation marks, hyphens, full stops and commas at
a line's start or end hang wholly outside the margin there. Asterisks,
tildes, ellipses, en and em dashes, colons and semicolons hang by half
their width, and A, V, W, Y, v, w and y hang by a tenth of theirs. A
punctuation mark followed by a quotation mark hangs with it.
Left-aligned lines hang at their left margin, right-aligned lines hang
at their right margin, and centered and justified lines hang at both.
Lines break where they did before, and the space that justification
adds between glyphs leaves out the gaps next to a hanging punctuation
mark.

The setting is off for new type and for documents from before it, and
the type object saves it only when it is on.

The Type menu's checks for Optical Margin Alignment, Type Orientation
and the Type on a Path effects now read the type inside a selected
group, as the commands do.

Docs: docs/mcp.md, docs/target-app-parity.md, docs/type-parity.md.
2026-10-17 15:14:42 +07:00
Marco Arnulfo 63b95c8dd3 Type: emptying the text while editing keeps its character style (fixes #2119) 2026-10-17 14:43:13 +07:00
dotCooCoo 8f71c94473 Type: a change of paint, baseline shift or rotation on one character keeps the font's kerning
Each style run was shaped on its own, so restyling one character, even
only its fill, dropped the font's kerning pairs at the new run
boundaries. A fill change on the V of 40 pt "AVAV" moved it 0.56 pt and
moved the characters after it 1.12 pt. Manual kerning on a character
also dropped the font's kerning with the character before it.

Adjacent runs that differ only in fill, stroke, underline,
strikethrough, overprint, character style name, leading, baseline
shift, rotation, Character Alignment, kerning, or tracking (as long as
both values leave standard ligatures on or both turn them off) are now
shaped together, and each glyph takes those attributes from its own
run. The font's kerning pairs apply after characters with automatic
kerning: a manual value replaces the pair after its character, and the
pair before it stays, except before a character that All Caps or Small
Caps sets as two letters (ß as SS). A change of font, size, scale or
OpenType features still shapes the characters apart, and a ligature or
a mark still does not join characters in different styles.

Docs: docs/target-app-parity.md.
2026-10-17 13:52:05 +07:00
dotCooCoo 5a3e833ec8 Type: a click in a Devanagari syllable goes to its nearer end, and shaping is tested
A syllable drawn with several glyphs that share one cluster, such as a
pre-base i matra and its consonant (कि), had a caret stop at the edge
of each glyph. A click just right of the matra, in the left half of
the syllable, put the caret after the syllable. byte_in_line now takes
one extent for each cluster, and type on a path compares the click
with the middle of the cluster, from the start of its first glyph to
the end of its last.

New tests with an installed OpenType Devanagari font check that क्ष
forms one conjunct glyph, that र्क draws the reph after its base, that
कि draws the matra before its consonant, that Ka with a nukta matches
the precomposed क़, and that each syllable is one cluster with the
arrow keys stopping at its ends. They print SKIPPED when no such font
is installed.

Docs: docs/localization-parity.md (Hindi row), docs/target-app-parity.md.
2026-10-17 13:49:49 +07:00
EuanTop 0746519517 Fix localized legacy font family name fallback 2026-10-17 13:20:26 +07:00
vangel76andClaude Opus 5.5 285a3da81a Type: centre and right aligned lines set their tabs as if they started at the left (#2457)
A tab's width depends on where it falls from the tab origin. Line breaking and the line width measure it from the left edge, but a centred or right aligned line is placed from its own start, so its tabs stopped elsewhere. The line is now laid out with its tabs measured from the left and then aligned as a whole.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 13:20:26 +07:00
vangel76andClaude Opus 5.5 101fd55fa0 Type: a tab takes its width when area type breaks and justifies its lines (#2457)
Line breaking, the line's natural width (and so justification) and the composer counted a tab as its glyph's width, a space, while it is set up to its next tab stop. A paragraph starting with a tab, as in the report, filled a justified line as if the tab were one space, and the line ran past the frame. break_line and the line width now measure tabs with tab_advance, and a paragraph with a tab is broken line by line, since the composer works with fixed glyph widths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 13:20:26 +07:00
dotCooCoo 24583effe0 Paragraph: the Hyphenation dialog sets each paragraph's hyphenation limits
The Paragraph panel menu's Hyphenation… was always disabled, and
automatic hyphenation used fixed limits: words of 6 letters or more,
3 letters on each side of the hyphen, any number of hyphenated lines
in a row, and capitalized words hyphenated.

Each paragraph now has hyphenation options: Words Longer Than, After
First, Before Last, Hyphen Limit, Hyphenation Zone and Hyphenate
Capitalized Words. The defaults are the fixed limits, and documents
keep their line breaks. The options are saved only when they differ
from the defaults. text.setFormat takes wordsLongerThan, afterFirst,
beforeLast, hyphenLimit, hyphenationZone and hyphenateCapitalized, and
paragraph styles carry the options. Hyphen Limit applies with both
composers. Hyphenation Zone applies with the Single-line Composer: a
line whose text before the next word already ends within the zone
breaks before that word instead of hyphenating it.

Hyphenation… is enabled with type selected and opens a Hyphenation
dialog (ui.hyphenation) on the values the Paragraph panel shows. OK
runs text.setFormat with the values that changed, as one undo step.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-16 15:27:55 +07:00
imachusandClaude Opus 5.5 4265c2f301 Type: whitespace the primary font doesn't map takes the fallback face too (#1844)
An ideographic space in a face without U+3000 was shaped with that face
and could set at zero width, removing an indent. Only what the layout
handles itself (tab, line and paragraph breaks) stays with the primary.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 06:16:35 +07:00
dotCooCoo d1089270ba Fonts: a removed font that type used leaves the list at the next font scan
When type had used a font, removing the font while the app ran left
it in the font menus, and the type kept drawing in it until the app
restarted. The font list is the loaded faces plus the scanned catalog,
and a scan replaced the catalog but kept every loaded face.

A scan now drops each loaded face whose file it did not catalog, with
the face's cached outlines. A family with no face left leaves the font
lists, and type set in it lays out in the fallback font and is
reported missing, as when a document opens without the font. When the
font is installed again, the next scan lists it and the type draws in
it again. Bundled fonts and fonts added as bytes have no file and
stay.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-16 06:16:34 +07:00
Lucas LerouxandClaude Opus 5.5 55fadb6afe Text: a bundled-fonts feature (on by default) lets embedders that bring their own fonts leave ours out
The nine OFL faces in `assets/fonts` are compiled into every build of
vectorcraft-text: 3.4 MB of the binary, about 1.6 MB of a gzipped wasm.
An embedder that loads every font at run time with `FontDb::add_font`
(a web app serving its own font catalogue, say) ships them twice and
can't turn them off.

- vectorcraft-text: `bundled-fonts`, a default feature, holds the
  `include_bytes!` list. Off, a database starts without them (craft-fonts
  and system fonts are unaffected) and text has no glyphs until a font
  is added, as when the bundled fonts fail to parse.
- vectorcraft-engine: a `bundled-fonts` default feature forwards to it,
  so the apps and any embedder using the engine keep the fonts unless
  they build it with `default-features = false`.
- The workspace declares vectorcraft-text without default features, so
  no library crate turns the fonts back on. The tests of cad, metafile
  and tools, which lay type out, ask for the feature in their
  dev-dependencies; the other crates' tests get it through testkit.

`cargo test --workspace` builds with the fonts, as before. A new
integration test, `without_bundled_fonts`, covers the configuration
without them (`cargo test -p vectorcraft-text --no-default-features
--test without_bundled_fonts`): the database starts without them, text
in no font lays out with no glyphs, and a font added later is used and
is the fallback for families the database lacks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 04:19:05 +07:00
IanHollow 83f40f135a Text: make every supplied font family available to documents
Assisted-by: GPT-6
Signed-off-by: IanHollow <72767437+IanHollow@users.noreply.github.com>
2026-10-16 04:19:05 +07:00
YukiHiko 3a7d422046 G4: keep fullwidth punctuation upright without vertical alternates 2026-10-16 04:19:05 +07:00
dotCooCoo 867e72b085 Type: Auto Size area type in columns usually lays the columns out twice an edit
For Auto Size area type in columns, every text edit, each keystroke of
the Type tool included, laid the text out in one column and then laid
the columns out at that height and at each step of a bisection for the
least height that holds the text. In the release-build timing test
auto_height_in_columns_typing_timing (stories of 5,000 to 50,000
characters at 9 pt in a frame 500 pt wide), a keystroke laid the
columns out 18 to 22 times, and typing at the start of the story of
50,000 characters in 2 columns took 1,670 ms a keystroke.

The bisection now runs on the lines of the one-column layout.
columns_hold computes where those lines would sit in the columns, with
each column's first line at its first baseline below the column's top
(LineInfo::first_baseline, new in vectorcraft-text). The columns are
then laid out at the upper height the bisection ends on and, when they
hold the text there, at the lower one, unless that is the 1 pt it starts
from. When they hold the text at the upper height and not at the lower
one, Auto Size takes the upper height. When taller columns always hold
the text, the old search ends between the same two heights. When the
text wraps around other objects or does not fit the one-column layout,
or when that check fails, the columns are laid out at every step as
before. The same keystroke now takes 292 ms.

Docs: docs/target-app-parity.md.
2026-10-16 04:04:50 +07:00
dotCooCoo 0c3a5929c7 Type: area type opens and lays out with at most 100 rows and 100 columns
In a dev build, opening a native file whose area type has 2^60 rows
or 2^60 columns failed with an internal error ("capacity overflow"),
and opening one with 2^32 rows and 2^32 columns failed with "attempt
to multiply with overflow". Text layout reserved room for rows times
columns frame cells. `text.areaOptions` keeps both counts within 1 to
100, but reading a document kept the counts the file gave.

Reading a document now clamps each count to 1 to 100
(`AreaOptions::MAX_ROWS_COLUMNS`): a larger count reads as 100, and 0
reads as 1, as layout already treated it. Text layout clamps both
counts the same way. Area type with 1 to 100 rows and columns lays out
as before.

The cells of a frame now share one copy of the frame's flattened
polygons and one copy of the flattened text wrap shapes. Each cell held
its own copy: in a dev build, a test process that laid out text in 100
rows and 100 columns of a circle frame of radius 1,000,000 pt peaked at
1.33 GB of memory, and now peaks at 100 MB.
2026-10-16 02:21:47 +07:00
Dan Green 6ce1a622be Part of #868: legacy .ai type as text objects and hidden objects kept; legacy type read
Type is written as version 7 text objects (§15): point type, area type and type on a path, with
fonts by PostScript name re-encoded to Windows-1252, and each run's paint, size, scaling,
tracking, leading, alignment and indents; type 8-bit text can't hold (or all, with outlineText)
is outlines. The format hides only layers, so hidden objects go to a hidden layer above their
own (hiddenObjects "layer", the default), are left out ("omit") or show ("visible"). The .ai
reader reads legacy type (To … TO, TE, %AI3_BeginEncoding) and ignores Ar; the import fuzzer
gets legacy type tokens.
2026-10-16 01:36:24 +07:00
Marco Arnulfo eff2ccdc34 Text: draw tab leaders in the tab's gap (fixes #1877) 2026-10-14 12:27:03 +07:00
dotCooCoo a1a27636e1 Fonts: a missing font's name is looked up once until fonts are added or rescanned
For a name that is neither a loaded nor an installed family,
FontDb::canonical built the list of names of every loaded face on
every call. Layout asks for a style's face for each span it shapes
and three more times per paragraph, so the time to lay out type set
in a font that is not installed grew with the number of fonts loaded:
a story of 1,000 paragraphs took a median of 409 to 507 ms in a
release build with 400 fonts added.

The database now keeps canonical's answer for each such name and style
until fonts are added or rescanned, and it keeps at most 4,096
answers. The same story takes a median of 56 to 71 ms, and canonical
gives the same answers as before.

Docs: docs/target-app-parity.md.
2026-10-14 06:01:10 +07:00
Gert Pellin eb25a85d90 Type: stylistic sets, alternates, titling, figure styles, superscript and subscript
A character style's OpenType features took nine tags. OtFeatures now
also keeps ss01-ss20, salt, titl, pnum, lnum, sups and subs (off by
default, one bit each in `extra`): they are accepted by the type
commands, stored with the style and passed to the shaper, so a font
that has them uses them.

The Affinity import test used ss01 as its example of a feature kept but
not rendered; it now uses cv01 for that (character variants still
aren't rendered) and keeps ss01 as one that is.

Fixes #1607
2026-10-13 13:36:40 +07:00
Marco Arnulfo 36dc6e0c11 Area type: a word too wide for the top lines of a shape moves down instead of breaking (fixes #1615) 2026-10-13 12:58:05 +07:00
bflatastic 3a88ad0855 Fix #1529: Linux reads the font folders fontconfig is set up with
The font scan read /usr/share/fonts, /usr/local/share/fonts, ~/.fonts and the
XDG data folders. Packages add folders outside those to fontconfig's
configuration (TeX Gyre under /usr/share/texmf/fonts), so fc-list and other
apps showed fonts VectorCraft didn't have. The scan now also reads the <dir>
folders of /etc/fonts/fonts.conf and the files it includes (conf.d, the user's
own), as the fontconfig documentation describes them. The files are read as
they are: no library is linked and no program is run.
2026-10-13 06:56:37 +07:00
dotCooCoo 8754646784 Type: frames and paths beyond 1,000,000,000 pt hold no text, and layout limits its search for room
Opening an EPS whose threaded area type frame has a control point at
x = 8e115 failed with an internal error ("capacity overflow").
`eps_text_frames_never_panic` in `import_fuzz` found it with
PROPTEST_CASES=20000. Text layout flattens a frame to within 0.1 pt,
and for such a curve kurbo 0.13.1's `flatten` calls `reserve` for
`usize::MAX` quadratic pieces. Laying out text around a wrap shape with
such a curve failed the same way. Laying out type on a path along such
a curve panicked in kurbo's `solve_itp` ("attempt to shift left with
overflow") in builds with overflow checks, and in a release build it
did not finish within 20 s. Laying out area type in a triangle frame
that comes to a point at its top, at y = 1e20, did not finish either:
adding a lead to a baseline that large leaves the baseline where it is.

Text layout now leaves out a frame, a text wrap shape or the path of
type on a path that has a coordinate beyond 1,000,000,000 pt either
way, control points included, or a NaN coordinate in an anchor point.
Such a frame or path holds no text, and such a wrap shape is ignored.
PDF export uses the same bound (`MAX_COORD` in
`crates/pdf/src/export.rs`).

Text layout also steps a line down a frame cell at most 100,000 times
to find room for it. In a cell taller than 100,000 leads, a step is the
cell's height divided by 100,000; in a shorter cell, a step is one
lead, as before. Before this change, in a dev build, with triangle
frames that come to a point at their top, laying out text in one
2,000,000,000 pt tall took 9.8 s, and laying out a paragraph whose
Space Before is -1e300 pt in one 1,000 pt tall did not finish within
20 s.
2026-10-13 00:34:40 +07:00
bflatastic 988f52ecc8 Fix #1423: an Affinity text frame too short for its first line is made taller instead of showing nothing 2026-10-12 23:42:56 +07:00
bflatasticandsanyamsingh001 4251e337c1 Fonts: a character no font covered is looked for again after a font rescan (#992)
A character missing from every installed font was remembered as missing for the whole
session, so installing a font that has it didn't help until a restart. A rescan of the
font folders now forgets those misses, and a search a rescan overtook doesn't record one.

Taken from #992 by sanyamsingh001, without its font file probe.

Co-authored-by: sanyamsingh001 <314051937+sanyamsingh001@users.noreply.github.com>
2026-10-12 16:43:41 +07:00
imachusandClaude Opus 5.5 876f8e283c Type: Proportional Metrics bring their kerning — apkn with palt, vkrn and vapk with vpal (#966)
The follow-up #966 left room for. With automatic kerning, horizontal type
with Proportional Metrics also applies `apkn`, and vertical type applies
`vkrn` and `vapk` along with `vpal`. Manual kerning turns them off, as it
turns off `kern`.

Vertical kerning used to cancel out: both top-to-bottom shapings had
harfrust's default `vkrn`. The baseline now has neither `vpal` nor the
kerning, so their difference carries both. A third shaping (`vpal` alone)
tells which advances `vpal` itself changed, and mojikumi steps aside only
for those. Vertical type without Proportional Metrics isn't kerned, as
before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 07:25:55 +07:00
Ikki Nishihara ce38eef890 fix: フォントのフルネームを正しい書体へ解決する #1258 2026-10-12 07:25:51 +07:00
sanyamsingh001andCodex 21603e6c81 TEST.1: correct font shaping and embedding oracles
Co-authored-by: Codex <noreply@openai.com>
2026-10-12 06:59:49 +07:00
zakariayacineandClaude Fable 5.1 16af3cc3fa Type: Digits — Default, Arabic, Hindi and Farsi numeral systems
Adds the Character panel menu's Digits option (with the Middle Eastern /
Indic options, beside Paragraph Direction in the Paragraph panel) as a
character attribute: CharStyle::digits, saved only when set, with
text.setFormat {digits} and text.setRangeStyle {id, start, end, digits}
("default" | "arabic" | "hindi" | "farsi"; a bad value is an error).

Shaping draws every digit of the Western, Arabic-Indic (U+0660–U+0669)
and Eastern Arabic-Indic (U+06F0–U+06F9) sets in the chosen system,
whatever was typed, before font fallback, so a font without the
Arabic-Indic digits gets a fallback for them. The text, its clusters,
the caret, selection and search stay as typed, as with All Caps; SVG
export writes the digits as drawn.

Catalog rows for Digits, Arabic, Hindi and Farsi in the ten translated
languages. Docs: docs/mcp.md (Digits), type-parity.md (digit types
done; Character Direction, kashidas, composers and the split caret
still missing), target-app-parity.md and gaps.md.

Behaviour from the public documentation (a character-level option with
the values Arabic, Hindi and Farsi in the Character panel menu):
https://helpx.adobe.com/indesign/desktop/language-and-proofing/arabic-and-hebrew/choose-digit-types.html

Tests: doc (the mapping and serialization), text (glyphs of "page 3" in
Hindi/Farsi equal the typed Arabic-Indic digits' on the typed clusters;
skips without a font covering the three sets), engine (setFormat,
setRangeStyle, undo, error), svg (digits written as drawn), ui-egui
(the menu shows with the Indic options).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-11 22:18:01 +07:00
bflatasticandClaude Opus 5.5 947b294f87 Release: embed the current craft-fonts (Thai, Arabic, Chinese)
Releases pinned craft-fonts at its first commit, with the Japanese fonts
only. The pin moves to its current main, as PdfCraft, LightCraft and
WordCraft have moved theirs: desktop builds embed Noto Sans Thai, Noto
Sans Arabic and Noto Sans CJK SC too (the web build still embeds only
the UI font and fetches Thai and Arabic beside the wasm). Packages
already copy every embedded font's licence.

The Thai loanword test no longer assumes the bundled fonts' widths: it
checks that no line ends inside โปรแกรม, whichever Thai font sets it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:47:46 +07:00
bflatasticandClaude Opus 5.5 a08ca07c69 Type: the caret and double-click take Thai word by word
Ctrl/Alt+arrows and double-click treated a run of Thai (Lao, Khmer,
Myanmar) without spaces as one word, and stopped at a word's tone and
vowel marks, which didn't count as part of a word. They now stop at the
word boundaries the line segmenter finds in those scripts (shared with
line breaking, so no more data ships), and combining marks count as
part of a word, also for combining accents in other scripts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:06:39 +07:00
bflatasticandClaude Opus 5.5 70e99987e9 Type: Noto Sans Thai from craft-fonts is the Thai fallback, on the web too
The web build has no installed fonts, so Thai there showed as empty
boxes. With a craft-fonts checkout that has Noto Sans Thai, its Thai
font joins the document font database after the Arabic ones (the
fallback for Thai where no Thai font is installed) and the web build
fetches it before the app starts, as it does Noto Sans Arabic.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:52:08 +07:00
bflatasticandClaude Opus 5.5 cc2f45b129 Font menu: a Thai script filter
The font menu's script filter offered Japanese and Latin only, so Thai
fonts had to be found by name among every installed family. It now has
Thai too: the families whose character map has Thai. Latin lists the
families with neither Japanese nor Thai.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:51:32 +07:00
bflatasticandClaude Opus 5.5 5a855971aa Type: Thai, Lao, Khmer and Myanmar lines break between words
These scripts don't separate words with spaces, and lines only broke at
spaces, hyphens and CJK characters, so a Thai paragraph in area type ran
on to the next space (or was cut wherever it overflowed). Each
paragraph's glyphs now carry the word ends ICU4X's line segmenter finds
in text of those scripts (its dictionaries on the desktop, its far
smaller LSTM models on the web), and a line may break after them as
after a space. Other text keeps its own breaks, and clusters (a
consonant with its vowel and tone marks) still never split.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:31:37 +07:00
bflatasticandClaude Opus 5.5 3fb51869ac Type: the caret steps over whole clusters (Thai marks, accents, emoji)
The Type tool's Left and Right arrows and Delete moved one code point at
a time, so the caret could stop between a Thai consonant and its vowel
or tone marks (or inside an accented letter or emoji sequence), where
typing or deleting splits the cluster. They now step over extended
grapheme clusters. Backspace still takes back one character at a time,
as Thai typists expect for a mistyped tone mark.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:29:16 +07:00
bflatasticandClaude Opus 5.5 fba3702103 Type: Thai falls back to a font made for Thai
Thai typed in a font without Thai glyphs (Source Sans 3, a Latin font)
took the first loaded face that had them, or the first installed file
that did, in path order: on one Windows machine a display face (Kanit)
rather than Leelawadee UI. Characters of Thai, Lao, Khmer and Myanmar
now try fonts made for their script first, installed ones loaded on
demand (Leelawadee UI, Tahoma, Thonburi, Sukhumvit Set, Noto Sans Thai,
the TLWG fonts for Thai), then the fallback as before. Latin beside
them keeps the font chosen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 18:42:31 +07:00