A path passes when it lies inside one of the roots of its kind. The roots'
common parent, a sibling named like a root, `..` or a link out of them and
the other kind's roots stay refused; a folder given twice counts once.
path.appendAnchor read `subpath` with as_u64, so -1 or "0" counted as omitted
and the point went onto the last subpath. Only an omitted (or null) subpath
means the last one now; any other value must be a valid index.
Found by CodeRabbit on #1657.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Delete key already ran path.deletePoints under Direct Selection, but
Edit › Clear (the menu item, a native accelerator, or the control channel)
called edit.clear, which removed every path whose points were selected. With
points selected and no ids given, edit.clear now deletes those points, as
InDesign's Clear does.
Found by CodeRabbit on #1657.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Write GTS_PDFXConformance (PDF/X-1a:2003) in the document information and
XMP; check_pdfx1a requires it.
- A registered printing condition (Coated FOGRA39) has no DestOutputProfile,
so no ICC v4 profile goes into the PDF 1.4 file; an unregistered condition
needs an ICC v2 profile, else the export fails.
- Images the flattener can't read fail the export, naming the page and its
graphics.
- Shadow mask coverage (with - without) / (255 - without), so partly
transparent objects drawn over their shadow give the rendered pixel back.
- The dropped-inks warning says their tints were dropped and won't print.
- Type 3 fonts pass; their glyph procedures are checked like page content.
- selection.toggle dropping an item from the selection now drops its selected
points too. They stayed in selection.anchors, so the arrow keys still moved
points of a frame that was no longer selected.
- path.appendAnchor at the start moves the sub-path's selected points up one
place, as path.reverse remaps them, so they keep naming the same anchors.
Found by CodeRabbit on #1657.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New packages are created with the ordinary 0666 request; the process umask
decides the effective mode. Annotated with that reason so the analyzer's
world-writable warning does not change the intended sharing behaviour.
🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
As InDesign: a selected anchor shows both its direction handles, and its
neighbours the handle that shapes the segment to it; with no anchor
selected no handle shows. Handles that don't show can't be grabbed (one
rule, select::handle_shown, for drawing and hit testing). The pen and the
anchor tools still show every handle. Under the anchor tools the selection
is drawn as its path and points, without the bounding box.
Found in the Learn Skills Daily course, chapter 43.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both change the Direct Selection tool: #902's corner pull now builds the
anchor drag with this branch's list of other selected anchors (none), so the
two build together; both sets of tests are kept.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Autoflow (Shift-click, autoflow on Place, text.thread with autoflow) inserted
new pages right after the story's last frame, in front of the document's own
pages. As in InDesign, it now threads one frame per column on each page that
follows, whatever else the page holds, and only then adds pages at the end of
the document. It fills whole pages, so an estimate that stops inside a page
can't leave its later columns out of the thread. Frames it set past the end
of the text are removed again; the pages they were on stay. Smart Text Reflow
of the primary text frame keeps adding pages after the story's last page.
Shrinks gap 26: flowing into the primary text frame of the following pages
is left.
A paragraph attribute records that the Bulleted or Numbered List button set
the hanging indent; turning the list off takes the indent away only then
(and while it is still as applied), and clears the mark. A paragraph whose
own indents were 18/-18 before the list keeps them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Paste: other apps' text goes into a text caret even after copying objects (#212)
* Paste: text that matches a copied frame still pastes; groups with text frames aren't anchored (#212)
A selection ending right after a return (a whole paragraph, as a triple
click selects it) applies the list to that paragraph only, and the button
shows pressed for it; a whole frame still takes in its last, empty
paragraph. Turning a list off clears only a hanging indent set on the
paragraph itself: one the paragraph style gives stays.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nothing checked type size. `type.char {"size": 0}` set type with no size at
all, a negative size went in the same way, and 1e9 pt asked the composer for a
line a million times the height of the page — all of it silent, and all of it
saved to the document.
The range is the one this engine already applies to a ruby annotation's size,
0.1 to 1296 pt, which is InDesign's: "size 0: 0.1 to 1296 pt".
It sits in `validate_char_fill`'s place in the chain, so every command that
takes character attributes gets it: `type.char`, `find.change`, the four
`style.character` / `style.paragraph` create and edit commands, and the two
table paths that already validated a fill.
The test covers five refused sizes, the three in range including both ends, and
two of the style commands. Without the check, `{"size": 0}` applies again.
Not from a ticket: found probing `type.char`.
`anchor.create` took any name, including one another anchor already had, so a
document could hold two anchors called "Photo 1". Every reference to the name
then means whichever the search finds first: `xref.insert {"anchor": "Photo 1"}`
and `hyperlink.create {"anchor": "Photo 1"}` take the first, and an export
writes both destinations under the one name. A name nobody can resolve is not a
name.
A name already in the document is a usage error now, naming it and where to
look: "a text anchor named `Photo 1` is already in this document (anchor.list
names them)". A name that is empty or only spaces is refused too — a
cross-reference built from `<txtAnchrName />` would show nothing.
Case still distinguishes names, as it does elsewhere in the document, and
nothing about existing anchors changes: the check is on creation.
The test covers the same name in the same story and in another one, the empty
names, that only one anchor exists afterwards, and that another name and a
name differing in case are both fine. Without the check the duplicate is
created again.
Not from a ticket: found probing `anchor.create`.
Found probing `layout.pages.insert` on a two-page document:
{"count": 0} → one page inserted (0 clamped up to 1)
{"count": -1} → one page (`as_u64` fails, the default applies)
{"count": 1.5} → one page (the same way)
{"count": 10000} → 9999 pages, quietly capped
A count that is given is a number of pages, so each of those is a usage error
now: "count 0: 1 to 9999 pages". A count that is absent still means one page.
`after` is left alone. My first attempt also refused a position past the last
page, and `insert_after_u64_max_appends_at_the_end` caught it: appending is
what an out-of-range `after` is meant to do, and the test now says so in the
same place.
The test covers the five counts, that none of them inserted anything, that a
position past the end still appends, and that `{"count": 2, "after": 0}` still
inserts two pages after the first. Without the check, `{"count": 0}` inserts a
page again.
Not from a ticket: I found it probing the command's parameters.
The same selection written two ways gave different files:
file.exportPdf {"pages": "2,2"} → 1 page
file.exportPdf {"pages": [2, 2]} → 2 pages, the second a copy
file.exportPdf {"pages": [2,2,2]} → 3 pages
`parse_page_range` keeps the first of a repeat (`"2-2,2"` is one page too); the
array form appended whatever it was given. A list says which pages to export,
not how many times, so the array now drops a repeat as the string does. Both
`file.exportPdf` and `file.exportPptx` read the same parameter.
The test pins the agreement between the two forms over nine lists, in PDF and
PPTX, and the two facts I measured while checking it: the pages come out in the
document's order whichever order the list names them in, and `[2, 2]` is now
byte for byte the file `"2"` gives. A page that does not exist is still
refused, and an empty list still exports nothing.
Not from a ticket: I found it probing the page-range parameter.
A cell's text rotation (Cell Options › Text › Rotation) was already read from
IDML and written back, but only 0 is composed, so an imported form's narrow
header columns of vertical labels came in horizontal and wrapped mid-word
("Eingela / ngt am") — and nothing anywhere said why. The reporter's file has
30 such cells.
Composing rotated cell text is a larger change than this. What this does is
stop it being silent, and let the value be set from the API:
- the IDML import reports it once per file: "2 table cell(s) have rotated text
(Cell Options › Text › Rotation): the angle is kept and written back, but the
text is composed horizontally";
- `table.setCell` takes `rotation` (0, 90, 180 or 270, any other angle refused,
negatives and multiples normalised), which nothing could set before;
- `table.get` reports each cell's rotation, so a round trip can be checked.
Tests: the angle through an export and back with the warning counted once for
the file (and no warning when no cell is rotated), and the command's accepted
and refused angles. Each fails without its own part.
A document made from a preset keeps the preset's units on both rulers; the
default origin and increments still apply. Preferences with no document open
passes the increments it reads to `prefs.set` unclamped: a Cursor Key of 0,
or text that isn't a measure or number, is refused and the dialog stays open
with the reason; a Kerning/Tracking of 0.5 is kept.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`after` or `before` past the last page is refused without overflowing and
changes nothing; inserting after the last page still appends.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`path.moveAnchors` points may carry their own spread-space movement
(`[id, subpath, index, dx, dy]`). Dragging and the arrow keys send each
selected point's movement converted for its spread, so on a spread turned on
screen a point moves the way the pointer or arrow does. Delete and Backspace
with points selected run `path.deletePoints`. Properties › Transform for
selected points shows the points' bounds; X/Y move them and W/H spread or
gather them about the reference point, in one step (`transform_anchors`).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
As in InDesign, Preferences with no document open has Units & Increments:
the horizontal and vertical ruler units, the ruler origin and the cursor-key
and kerning increments. They are kept with the application preferences
(`prefs.set {defaultUnits}`) and new documents start with them; New Document
opens in those units, and its [Default] card uses them. A document keeps its
own Units & Increments once made, and Preferences with a document open still
edits that document's.
`file.new` without a preset or `units` uses the default units; units picked
in New Document set both rulers unless they are the default horizontal ones,
which keep the default vertical units.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`object.textWrap` takes `mode: null` for each object's own mode. Properties
sends it with an offset or Invert edit, so objects with another wrap mode
than the first keep theirs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>