The gate failed kept_menu_names_are_menu_labels_left_in_english: sv is in
KEEPS_MENU_NAMES, so the MENU_KEEP_AS_IS names (VectorCraft, OpenType, the
workspace names, the perspective-grid presets) must stay in English. The
coverage report had listed them as missing; the 20 rows are removed so the
names fall back to English again. The two genuinely-missing strings
(Font Size:, digits Hindi) stay.
Co-authored-by: rahul05ranjan <11793455+rahul05ranjan@users.noreply.github.com>
The gate failed kept_menu_names_are_menu_labels_left_in_english: ru is in
KEEPS_MENU_NAMES, so the MENU_KEEP_AS_IS names (VectorCraft, OpenType, the
workspace names, the perspective-grid presets) must stay in English. The
coverage report had listed them as missing; the 20 rows are removed so the
names fall back to English again. The 110 genuinely-missing strings stay.
Co-authored-by: rahul05ranjan <11793455+rahul05ranjan@users.noreply.github.com>
`gcheck` and `scheck` answered false for every operand. The PLRM (3rd
ed., `gcheck`) says they are true for a simple object, and false only
for a composite one in local VM. The eps2write prolog copies its
initial graphics state into global VM and leaves a value alone only
when `gcheck` is true. So the integers in the `currenthalftone`
dictionary took the copy path, left the stack one item off, and the
next `put` failed with a typecheck in `cp2g`.
Simple objects now answer true. Composites still answer false, since
every composite here is in local VM.
Count nine-slice symbol art with clips off, as place_symbol_art_unclipped
places it, so art crossing the guides is not over-charged; add a test that
the flattening segment budget stops a path independently of the point
budget; format.
The table chain's records and entry names now have a whole-archive allowance, only the
first name of an id is decoded and kept (no clone into the entry map), and an entry's
size is charged before it is decompressed and stays charged when it fails, with the
failure remembered so a broken entry is not decompressed again.
Object::get scanned every field for each lookup, and a shared object is looked up again
on every visit. Objects with more than 16 fields now keep a sorted tag index (first and
last position), so get and get_last are binary searches with the same first/last-
occurrence results. Rectangle corner types and radii read at most the four entries that
can matter instead of converting the whole arrays.
Flattening a path for DXF capped the points it kept but kept flattening every remaining curve
after the cap, each curve at a fixed tolerance however large it was. Symbol instances placed
inside symbols were cloned and walked once per instance with only the nesting depth bounded, so
a small symbol graph of empty symbols could fan out into billions of visits that write nothing.
- Flatten through vectorcraft_geom::flatten (a bounded number of segments per curve) and stop
at the first curve past the point or segment budget.
- Charge each symbol instance's art to MAX_SYMBOL_NODES with charge_symbol_art before placing
it, as the document does elsewhere; once spent, no further instances are expanded.
A gradient whose stops are not strictly increasing (repeated offsets, or
a file's unsorted stops) sampled each Classic off-centre midpoint, and
each freeform point when recoloring, by scanning the stops from the
start, so many stops and points cost their product. Such stops now get
an index of the furthest offset reached so far, which never decreases,
and each sample finds the pair of stops sample() would pick by binary
search, giving the same colour for any order of offsets.
Making a brush or symbol library, and gathering the resources copied
art uses, found each referenced swatch, symbol, pattern, graphic style,
text style and brush by scanning all the definitions of its kind, and
checked library membership and panel order by scanning the lists again,
so a library or document of N definitions cost N squared comparisons.
Each kind is now indexed by name the first time a reference to it is
followed, and the library code looks names up in sets and maps built
once.
The file name pattern searched the rest of the pattern for a closing
bracket at every opening bracket, so a pattern of many unmatched brackets
took quadratic time to render, in the export and its live preview. A
token's name never holds a bracket, so the search now stops at the next
opening one.
Output naming tried -2, -3 and so on from the start for every file given
a taken name, so one name pattern over many artboards rebuilt and looked
up every number used before for each file. It now goes on from the last
number given for that name, and remembers which file got a name from it.
parse_range walked every index of every interval, so a range that names
the same pages many times ("1-,1-,1-,...") cost the number of parts
times the page count. Picked indices now link to the next unpicked one,
so each page is visited about once whatever the overlap.
limit() replaced each too-deep container by its children with
Vec::splice in the middle of the sibling list, moving every later
sibling each time: a wide list of such containers took quadratic time
to load. The list is now rebuilt in order from a worklist, so each node
moves a bounded number of times.
A Repeat kept up to 250 000 instances whatever its source held, and the
nested-instance count skipped the art of opacity masks. Expanding copied
the whole source for every kept instance, and a Repeat inside an enabled
mask was evaluated again for each outer copy.
The budget now counts the Repeats inside enabled opacity masks, and keeps
a second bound, MAX_REPEAT_COPIES, on the nodes and path anchors copied
in all (nested Repeats included).
Master page copies were charged one unit per shape whatever its number
of segments, and nothing for attached vector or pixel masks, so a master
holding one huge curve or mask could be rebuilt on thousands of pages
within the copy allowance. Copy costs now count every subpath and
segment of a shape's path and its vector mask, and a pixel mask's
pixels.
A node's vector mask also skipped the document range check the node's
own art passes; its control points are now checked as mapped into the
document (non-finite ones included) and an object whose mask reaches
beyond the range is left out, as one whose art does.
The walk that skips objects it has already visited keys them by address
instead of id, so two objects that share an id in a damaged file are
both counted. An object reached through a selected group and again on
its own still counts once.
Found while testing crates/svg/src/export/nodes.rs:492 (Writer::node, `xlink:href` built with xml_escape)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A 200 x 150 instance of a 9-slice symbol draws its 10 pt corner square about 17.14 pt wide once the left guide is dragged from 25% to 5% in symbol editing mode, as the square then reaches into the stretched center band.
A guide whose saved position leaves a center band narrower than the minimum no
longer jumps to the art's edge when a drag does not move it closer to the
opposite guide. The 9-slice scaling line in docs/target-app-parity.md no longer
says guides are not drawn in symbol editing mode.
With Performance > Real-time Drawing and Editing off, tool previews are
applied only on release. A ruler guide being dragged was already exempt;
a symbol's 9-slice guide, a symbol.options preview that sets only
nineSliceGuides, now is too, so the dotted guide follows the pointer
while it is dragged. The release still leaves one Symbol Options step.
While a symbol with Enable Guides for 9-Slice Scaling on is edited in
symbol editing mode, the canvas draws its two horizontal and two vertical
guides as dotted lines across its art, with any tool and with ruler guides
hidden. With the Selection tool the pointer near a guide shows the Move
cursor, and dragging the guide sets it through `symbol.options`'s
`nineSliceGuides` as one undo step; Escape during the drag puts it back.
A guide within 4 screen pixels takes the press before the art and the
bounding box. A dragged guide stays within the art's box and stops short
of the opposite guide; positions outside the art are not supported yet.
The horizon test compares a point's signed w with the largest absolute
value in the bottom row, and the invertibility test compares the
absolute value of the determinant with the largest absolute value among
its six products, so a negative determinant counts as invertible. A
stored projection that overflows is divided by the largest absolute
value in its bottom row.
The power of two that brings a small map's entries up before a point is mapped is now reduced by the power of two of the point's larger coordinate, so no product of an entry and a coordinate reaches 1. A map that mapped a point before the scaling step does so again, and affine_at uses the same entries.
apply and affine_at multiply every entry by the power of two that brings the
largest to at least 1/2 when all entries are below 1/2, so products that would
be subnormal keep their precision. The identity times 5e-324 mapped (0.5, 0.5)
to (0, 0) before. Maps with an entry of 1/2 or more are unchanged.
adjugate_inverse no longer applies the relative determinant test to its result. That test is not symmetric between a map and its inverse: a map whose determinant is 1e-8 of its largest product can have a finite, accurate inverse whose own ratio is near 1e-16, so inverse() was None while is_invertible() was true. The result is now kept when every entry is finite and its determinant, summed from the six products in split form, is not 0.
The inverse holds every cofactor and the determinant as a mantissa and a
power of two, and forms each entry from their quotient. A map such as
diag(1e-300, 1e300, 1e-300), which counts as invertible, now gets its
inverse diag(1e300, 1e-300, 1e300) instead of none.
When a cofactor of a map with a normal but very small determinant underflows to 0, the
inverse formed from the map itself can have a w row of 0, so every point mapped through it
is refused. The inverse is now kept only when it is itself invertible, and is otherwise
formed from the map divided by a power of two that brings its determinant's products near 1.
The scale-free determinant test no longer builds a rescaled copy of the map. It
forms each of the six determinant products as a mantissa product and an
exponent sum and compares their sum with the largest of them, so a map such as
diag(1e307, 1e-157, 1e-157), whose rescaled copy overflows, is still invertible
and loads from a document. The rescaled copy is built only to invert a map whose
determinant is not finite or is below the normal range.
A homography means the same map at any nonzero uniform scale of its
entries, but `apply`, `is_invertible` and `inverse` compared w and the
determinant with absolute thresholds. A map stored with small entries
refused every point and could not be inverted, and a map with large
entries drew points past its horizon.
- `apply` refuses a point when w is at most 1e-9 times the largest
magnitude in the w row.
- A map counts as invertible when its determinant is more than 1e-15
times the largest of the six products the determinant sums, taken on
a copy rescaled by a power of two. An inverse whose determinant
overflows or underflows is taken on that copy.
A character style whose Up/Down or Left/Right offset lies past MAX_COORD
either way now opens with the nearer end, as text runs already do, so
text that takes the style no longer gets the out-of-range offset. An
offset within range is not rewritten.
An object with a gradient or a pattern on both its fill and its stroke
counted twice in Objects. It now counts once for each paint kind it uses.
With Selection Only, a selection that holds a group and an object inside
it walked that object twice, so the object counts, the paths' total
length and Repeat Objects counted it twice. The walk now visits each
object once.
In a Separation color space, the colorant name All marks every plate and
None marks no plate (ISO 32000-1:2008, 8.6.6.4). A spot swatch with one of
those names was written under its own name, so its paint printed on every
plate or on none.
PDF export now writes such an ink as All Spot or None Spot, numbered
All Spot 2, All Spot 3 and on when a spot swatch already has that name,
the same rule EPS export uses. The new name is used for fills and strokes,
tint shadings, DeviceN gradients, colorized images and the Lab alternates.
Registration still writes All.
Buttons beside the Edit tab's wheel switch between the smooth color wheel,
a segmented wheel of solid patches (24 hues by 5 saturations, its markers
moving as freely as on the smooth wheel) and color bars: one solid bar per
recolored color in row order, where a click selects a bar's row for the
color sliders and a drag moves the row without changing a color. The
dialog opens on the smooth wheel and keeps the choice while it is open;
the dialog field is `display` (`smooth`, `segmented`, `bars`).
A manual kerning value on an inline graphic's character was stored and
shown but did not move the text after it. The graphic's advance now
includes the value, as a letter's does, and in right-to-left horizontal
type its art moves right by the value, so the space goes between it and
the next character in reading order. Auto and Optical kerning still apply
no font pairs to an inline graphic.
Select › Same › Text Fill & Stroke Color (select.same.textFillAndStrokeColor) selects the type
objects with a run of characters whose fill and stroke both match the first character of the
first selected type. Type that matches only the fill or only the stroke, or has the fill in one
run and the stroke in another, is left out. The entry follows Text Stroke Color in the menu and
has a row in every catalog.
The Swedish catalog lacked 22 interface strings: the workspace names
(Essentials, Painting, Typography, Web, Touch), the 1P/2P/3P view
preset names and the Hindi digit-script name.
Adds them with the vocabulary the catalog already uses (Utskrift,
Teckenstorlek). After this the report lists nothing missing for sv.
Co-authored-by: rahul05ranjan <11793455+rahul05ranjan@users.noreply.github.com>