Effect › Distort › Glass… offered only its four built-in textures. The
reference app's Texture menu also has Load Texture, which takes an
image file as the glass surface.
The Glass dialog now has a Load Texture… button. It reads a PNG, JPEG,
GIF, WebP, TIFF, BMP, PSD or PSB file within the pixel allowance the
renderer draws images in, and the Texture menu gains Loaded Texture.
The effect stores the image's gray levels in `textureImage` as a
grayscale PNG in base64, so the document saves the texture with the
effect, and copies of the effect carry it. effect.apply and
effect.setParams take `textureImage` as {path}, {name, dataBase64} or
the stored string; any other value is an error.
The texture lies one image pixel per point at Scaling 100%, centered on
the object and repeated. Each pixel's luma is the surface's height,
from 0 for black to 1 pt for white, and a transparent pixel counts as
middle gray. A Glass without `textureImage` draws and saves as before.
Docs: docs/mcp.md (Glass textures), docs/effects-parity.md,
docs/gaps.md, docs/target-app-parity.md.
Unit fields and length strings read a percentage operand as a share of
a value. After * or /, the percentage scales the term before it:
3cm*50% is 1.5 cm and 3cm/50% is 6 cm. After + or -, it adds or takes
away that share of everything before it: 50pt+25% is 62.5 pt and
100pt+20pt-50% is 60 pt. Before this change these expressions read as
nothing, and the field kept its old value.
The parser now splits a sum at its last + or -, so a percentage applies
to the whole sum before it. Text without % gives the same values as
before, apart from float rounding. A percentage on its own (50%) still
reads as nothing, and percent and degree fields still read % as their
suffix.
Command length strings use the same parser: file.new's width and
height, the distance params and the length preferences.
Docs: docs/mcp.md, docs/ui-parity.md, docs/target-app-parity.md.
Exports bake live effects into plain art, and a Transform effect on
type or on its group or layer turned the type into glyph outlines, even
when the effect only moved it or made moved copies. A PDF reader could
not select or search that text.
The export bake now keeps such type as type, moved into each place the
effect puts its outlines. It does this for Transform effects without
scale, rotation, reflection or Random, for type whose characters are in
solid colors without underline or strikethrough, and that has no fills
or strokes of its own, inline graphics or perspective, and when the
copies do not overlap. Type under such an effect on its group or layer
also needs no transparency or effects of its own, and the group or
layer needs no fill, stroke or Pathfinder effect of its own. The export
still outlines other type under geometry effects, and the canvas and
Expand Appearance still draw outlines.
Docs: docs/mcp.md, docs/target-app-parity.md.
A new import fuzz test, pdf_resources_the_reader_cannot_parse_never_panic,
imports a page whose resources hold:
- a value the reader can't parse (/#zz, 12-.5) or one it reads (/Fine)
in the color space dictionary, before a Separation space the page
paints in, and in the font dictionary, before the font of its text;
- a calculator function written as the shading's function, or inside an
array or a dictionary in the page's /Properties: a plain one, one with
Columns 0, one with a huge Columns, or one of 257 MiB of deflated
spaces.
The import must not panic. The file opens with its art, or it is refused
with "would decode to more than 256 MB" when the function is not plain.
With the nesting check, the font scan or the ink scan reading
Dict::entries again, the test fails on a value the reader can't parse.
With the stream check made on the file's objects alone, the test fails
on every function that is not plain: the file opens instead of being
refused.
The calculator helper of pdf_streams_the_reader_would_fail_on_are_refused
is now a function of the file, and the function of spaces is made once.
The PostScript interpreter that reads EPS and PostScript files turned
the samples of a CMYK image into display RGB and stored an RGBA PNG,
so the image lost its ink amounts. The DCTDecode filter decoded a CMYK
JPEG to RGB, so it lost its ink amounts too. The PDF importer keeps
the ink amounts of 8- and 16-bit CMYK images (in a CMYK TIFF, or the
CMYK JPEG itself).
A CMYK image (image or colorimage in DeviceCMYK, CIEBasedDEFG or a
four-component ICCBased space) now keeps its samples in a CMYK TIFF
(ImageBlob::cmyk_tiff), read through its Decode array, at every bit
depth the interpreter reads. A CMYK JPEG gives its samples as stored,
and the image's Decode array reads them: [1 0 1 0 1 0 1 0] undoes the
inverted samples of Adobe's CMYK JPEGs. The pixels that a MaskColor
or an ImageType 3 mask hides become a grayscale opacity mask over the
image, as in PDF import. A mask that hides nothing adds no mask. Gray,
RGB, indexed and spot color images and image masks stay RGBA PNGs.
Docs: docs/file-format-parity.md, docs/target-app-parity.md.
The editing data reader sized each row of a 1-bit image as width ×
channels bits, padded to a whole byte, but read one bit per pixel and
made the pixel gray from it. A 1-bit RGB or CMYK image came out as a
black and white image of the wrong bits. When that made the layers
differ from the page by more than 5%, the file opened from its PDF
part instead, with a warning.
The reader now takes channel c of pixel x from bit x × channels + c of
the row, a set bit being the channel at full strength, and converts
the samples as it converts 8-bit ones. 1-bit gray images read as
before.
When a graphics driver crashed the process while the window started,
as Intel's Direct3D 12 driver (igd12umd64.dll) did on an Intel UHD
Graphics 605 (#2243), the next start picked the same adapter again. The
restart without a failing adapter and the first-frame watch run inside
the process, so the crash ended them along with it.
Each start now writes graphics-start.json in the log folder before the
graphics start, adds the adapter once one is picked, and clears both
when the first frame reaches the screen or the app ends without
crashing. A start that finds an earlier start still recorded there
leaves that adapter out, as a restart does, logs it and shows a note in
the status bar. When the crash came while the adapters were being
listed (wgpu's Direct3D 12 backend creates a device on each GPU to list
it), the next starts list OpenGL's adapters alone, except on macOS. The
adapters stay left out until the app version or the Graphics Processor
preference changes, a start shows its first frame on one, or no adapter
is left. WGPU_BACKEND and WGPU_ADAPTER_NAME choose regardless of the
record.
Docs: docs/development.md, docs/target-app-parity.md.
A PDF, PDF-compatible .ai or .ait opened from its pages had an empty
File Info. Nothing read the file's document information
dictionary or its XMP packet, so file.info reported no author,
description or keywords, even for a PDF that VectorCraft exported.
vectorcraft_pdf::file_info reads both. The author, description,
keywords, copyright status, notice and URL and the created and modified
dates come from the XMP packet (dc:creator, dc:description, dc:subject
or pdf:Keywords, xmpRights:Marked, dc:rights, xmpRights:WebStatement,
xmp:CreateDate, xmp:ModifyDate), else from the dictionary's Author,
Subject, Keywords, CreationDate and ModDate. document.open,
file.newFromTemplate and file.revert put each value through file.info's
checks and leave out a value that file.info refuses. A packet that is
not well-formed XML leaves the dictionary's values. File Info does not
set the title, and a file restored from the document it carries keeps
that document's File Info. vectorcraft-pdf depends on roxmltree 0.21,
which usvg already brings into Cargo.lock.
Docs: docs/mcp.md (File Info), docs/file-format-parity.md,
docs/target-app-parity.md.
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.
Adds a Finnish (Suomi, `fi`) catalog with 4,770 entries, written in
sentence case from the English labels with ordinary Finnish
vector-illustration vocabulary. It covers menus, `tl!` literals and the
668 `@msg` status and error messages: `fi` is in KEEPS_MENU_NAMES and
COMPLETE_MESSAGES. Like the other complete catalogs, it leaves the
product, workspace and perspective preset names in English.
Plurals are one/other with the partitive singular after any number but
1 (1 taso, 2 tasoa). Any fi-* locale selects Finnish; sv-FI does not.
Tests: `cargo test -p vectorcraft-ui-egui --lib i18n` passes (47),
including the new finnish_reads_as_finnish and the bundled-font glyph
check.
Clean-room, AI-assisted (Claude Code) translation; a native speaker
checked it in the running app, but it has not been proofread line by
line.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds Swedish (Svenska) as an interface language: 4770 strings, written from the English source.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Flatpak opens only /usr/share/fonts and ~/.local/share/fonts to a sandbox, so the
fonts a font manager such as FontBase activates into ~/.fonts were missing from the
font list while native apps showed them. Granted read-only: VectorCraft only reads
fonts.
Verified on the 0.11.0 release bundle by applying the same grant as a user override
(`flatpak override --user --filesystem=~/.fonts:ro`): in a fresh sandbox the FontBase
families went from none to all 167, `fc-match` resolved a family that had been missing
from the app, and the font then appeared in the font list.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1290 (from #1275) declared the glibc floor as `libc.so.6(GLIBC_2.35)(64bit)`. That doesn't stop
RHEL 9: its current glibc, `2.34-283.el9_8`, provides `libc.so.6(GLIBC_2.35)` through backports. On
Rocky Linux 9, dnf upgrades glibc to that build, installs VectorCraft 0.10.0, and `vectorcraft-cli`
then fails with ``/lib64/libm.so.6: version `GLIBC_2.35' not found``. The binaries' only GLIBC_2.35
symbols are `hypot` and `hypotf`, from libm, and RHEL 9 doesn't provide `libm.so.6(GLIBC_2.35)`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>