Skip to content

Release 2026.44.1 - #841

Merged
DutchSailor merged 13 commits into
mainfrom
integratie/release-2026.44.1
Oct 29, 2026
Merged

DutchSailor merged 13 commits into
mainfrom
integratie/release-2026.44.1

Conversation

@DutchSailor

Copy link
Copy Markdown
Collaborator

Open PDF Studio 2026.44.1

Merge with "Create a merge commit" (not squash or rebase), so each contributor's commit and authorship stays in the history.

Each reviewed pull request is included as one squashed commit with its original author. Problems found in review were fixed on top, with regression tests, inside the same commit; each commit message lists those fixes.

New

Faster

Fixes

Fixes #377
Fixes #376
Fixes #383
Fixes #519
Fixes #312
Fixes #385
Fixes #384
Fixes #509

Not in this release (still open)

#777 (split view) and #779 (tool sets) follow in 2026.44.2, after a change to the shared bitmap cache and a naming decision. The heavier PRs (#783, #773, #778, #789, #806, #803, #805, #801, #804, #796, #747, #810, #828) follow in later rounds.

Verification

  • Unit tests: 5903 tests, 0 failures (the known flaky view test aside). The Vite build passes.
  • No AI attribution and no commercial product names in any commit or added line.
  • The release gate on the combined desktop build, run headless, follows as a comment.

oscar981-op and others added 13 commits October 29, 2026 09:52
A vector snippet no longer carries a full copy of its source page. The new
js/pdf/vector-cull.js trims the page content to what the cut box can reach:
paths, text objects, images and Form XObjects that are provably outside the
box are dropped, while graphics state, clipping paths, marked content,
shadings and BX/EX sections are kept. Anything that cannot be read with
certainty leaves the page untouched. The trimmed mini-PDF is only used when
it is at most half the size of the full one. Mini-PDFs no longer carry the
source page's annotations, and fonts and images shared by trimmed snippets
of the same page are merged into one copy on save.

Refs #509

Review: included as is; no changes were needed on top.
…ort (#769)

The comments panel gets a filter bar for author, type, page range and date
range, alongside the existing status filter, plus a clear-filters action
and a bulk status change for the selection or all listed comments in one
undo step. Sorting and grouping move into a pure module shared by the list
and the exports. The CSV export now carries all fields (ids, dates, status
history, replies, layer and bounds), and "Summarize Comments" builds a PDF
report of the listed comments with a crop of each comment's location.

Fixes #383

Review: included as is; no changes were needed on top. Note that the CSV
column set has changed.
…775)

The Layers panel follows the /OCProperties /D tree from the file: nested
layers, labels, /Locked layers (shown with a lock, checkbox disabled) and
/RBGroups (radio behaviour). "Set as default" stores the toggled state, and
the next save writes it as /D /ON /OFF. The toggled state also applies to
raster export, printing and the vector "save as PDF" print. The properties
panel gets a "Layer" field: a markup can be put on a markup layer or on a
layer of the drawing, in which case it gets /OC to that OCG on save, loads
back on it and hides and shows with it.

Fixes #377

Review fixes on top:
- An adopted drawing layer whose markup layer was removed while other
  markup layers remained was dropped from /OCGs, /Order, /Locked,
  /RBGroups and /ON /OFF. Every adopted OCG known from the file now keeps
  its place; only the app's own keys are removed.
- The drawing layer's /Name is no longer overwritten by the markup layer
  name (for example a " (2)" suffix or a rename in the panel).
- Adopting a layer no longer turns the current view state into the file's
  default. Adopted layers keep their /D state; only "Set as default"
  changes it (laagstandZonderEigenLagen).
- Regression tests for all three cases (save, reopen, check /OCGs, /Order,
  /RBGroups, /Locked, name and default state).
…771)

Adds the paperclip annotation (PDF /FileAttachment): a ribbon tool that
places an icon and embeds a picked file, with select, move and delete,
properties (icon, description, file name and size, Open and Save As) and
double-click to open. The saver writes /FS with an embedded file, /AF and
its own appearance; the loader reads attachment annotations written by
other tools, keeping undecodable streams raw. The attachments panel also
lists page attachments and can open, save and delete them, and opening an
executable attachment asks for confirmation first.

Fixes #376

Review fixes on top:
- The new ribbon button is added to the ribbon command list used for
  keyboard shortcuts.
- A loaded attachment annotation whose content cannot be read is no longer
  rewritten without its embedded file: when it was not edited the original
  annotation stays untouched (file specification, unknown keys, /AP and
  popup), and when it was edited the new annotation reuses the original
  file specification.
- Attachment content in the store is released when its document closes,
  unless another open document or the clipboard still uses it.
- The confirmation list for executable attachments is extended (.msc, .chm,
  .url, .iso, .lnk, .scf, .application, .inf, .vhd, scripts and others),
  trailing dots and spaces no longer bypass it, and temp file names drop
  characters Windows does not allow.
…oving (#786)

Move mode (G / mv) gets an inference engine on top of object snap:

- Tracking lines: the cursor or a snap point of the moving selection
  aligns horizontally/vertically with endpoints, midpoints, corners and
  centres of other geometry (annotations and, when enabled, PDF content);
  H/V through the base point; hovering a point briefly acquires it as a
  tracking point, several guides give intersection snaps.
- Extension lines of existing lines and wall faces beyond their endpoints.
- Parallel / perpendicular to nearby lines, with guide and marker.
- Polar guides around the base point from the existing polar settings.
- Colour coding (red X, green Y, magenta parallel/perpendicular) with a
  tooltip naming the inference and the distance; guides drawn in single-page
  and continuous mode, axis colours follow a rotated view.
- Shift locks the shown guide (falls back to the angle constraint when no
  guide is shown); X/Y axis lock intersects with guides.
- A typed distance moves along the inferred direction; Tab switches the
  typed entry between distance and angle.
- Priority: point object snaps > guide intersections > single guides >
  edge/grid. The selection never infers from or snaps onto itself.

The pure geometry lives in js/tools/inference-engine.js (sorted-axis and
grid point index, segment grid), session glue in move-inference.js and the
overlay in inference-overlay.js. Corner-to-corner object snap now uses the
point index instead of a 6000-point cap. New preferences toggle tracking,
extension and parallel inference, translated for all locales.

Fixes on top during review: with the Object snap master switch off the
move is free again; tracking (including the H/V guides through the base
point), extension and parallel guides are disabled then, polar stays
independent as in the snap engine. The distance in the guide tooltip uses
the real measure scale and shows points on a page without a scale instead
of the 1 pt = 1 mm fallback. Regression tests for both.

Fixes #519
Adds Dutch structural drawing tools as parametric symbols, together with
a reinforcement category in the quantities engine so drawn bars feed a
bending schedule and a weight schedule.

- Rebar core (quantities/wapening.js): notation (12Ø10-150), bending
  shapes 00/11/21/51 with developed length per NEN-EN 1992-1-1 mandrel
  diameters, mass 0.00617·d² kg/m, bar collection from annotations,
  automatic bar marks and merging into bending-schedule rows.
- New symbols: rebar detail (bending shape), bar in plan, reinforcement
  mesh in plan, slab with frost edge, end plate at beam, base plate at
  steel column (with edge-distance check per NEN-EN 1993-1-8 table 3.3).
- Existing reinforcement cage, reinforcement bar and bar series now feed
  the schedule; cage gets beam length, bar marks and steel grade.
- Standard schedules "buigstaat" and "wapeningsstaat"; per-field
  non-summable columns in the engine.
- Palette entries, IFC classes, assistant prompt and MCP template docs.
- Translations for all 40 locales; design notes in
  docs/constructie/wapening-en-staalverbindingen.md.
- Tests for geometry, notation, schedule aggregation and save/load.

Fixes on top during review: bolt holes follow NEN-EN 1090-2 table 11 for
normal round holes (M12/M14 d+1, M16-M24 d+2, M27 and up d+3). Anchor
holes in the base plate are a separate, documented size (default d+6 for
setting tolerance, adjustable per symbol) that also drives the automatic
edge distances and the distance check. A bar series now carries its own
bar mark and steel grade (properties panel, save and load) and these reach
the bending schedule; a bar series without a bar length no longer shows up
as a 0 mm / 0 kg schedule line. Regression tests for each.

Fixes #312
- Freehand tool reads every coalesced pointer sample (getCoalescedEvents)
  before the per-frame move batching; pen pressure drives a per-point
  width, mouse and touch keep a constant width.
- Zero-phase smoothing plus centripetal Catmull-Rom; variable-width
  strokes render as filled outline rings with round caps, split at sharp
  corners and folding inner offsets (no spikes or holes, nonzero fill).
- Palm rejection: touch is ignored while a pen is near, a second pointer
  never joins a stroke, optional pen-only mode.
- Save: /Ink keeps the centre line in /InkList and the max width in /BS;
  the appearance stream draws the smoothed variable-width stroke. Per-point
  pressure, minimum width and smoothing round-trip in OPS_ keys.
- Properties panel Ink section and freehand preferences (pressure on/off,
  minimum width, smoothing, pen only), translated in all locales.
- Tests: width mapping, smoothing, outline geometry, palm filter, coalesced
  samples, mouse fallback and real saver/loader round-trip incl. rotation.

Fixes on top during review: a dashed freehand stroke now writes its dash
pattern into the appearance stream, so other readers show it dashed too.
A filled outline cannot carry a dash, so a dashed stroke with pressure
falls back to a stroked, dashed centre line at full width, on screen and
in the appearance; the line style is part of the geometry cache key. The
Ink key read in the loader no longer sits inside the arc comment block.
Tests check the dash operator in the saved appearance and that the dash
patterns match the screen.

Fixes #385
…AD (#788)

Eleven new assistant skills, grouped in tabs in the panel:
- Reading the drawing: find on sheet, explain selection, compare sheets
- Measuring and quantities: measure this, take off (schedule on the
  sheet), check the scale
- Marking up: review sheet (numbered clouds on a "Review" layer), stamp
  and date, tidy markups
- CAD drawings: bring in a drawing, export to CAD

Skills are declarative: group, required app state (document, selection,
measuring scale, two pages), input checks, the MCP tools they may use and
an English guide that goes into the system prompt. A chip whose
precondition fails shows the reason in the panel instead of sending.
Skill messages carry the app state (page, selection, scale, date) for the
model. Tests check every tool name and argument in the prompt against
mcp-stdio/tools.json, the gates, the message composition and the
translations in all 40 locales.

Review fixes on top:
- The tools a skill never calls on its own now also cover the indirect
  routes to saving, printing and irreversible edits: app_run_command,
  app_key, app_click_element, app_replace_text, app_snippet_cut,
  app_snippet_flatten, app_merge_pdf and app_install_extension.
  SKILL_TOOL_EXCEPTIONS allows the structural overview skill exactly the
  three it needs (copy a snippet, send it to the back, replace its own
  snippet), and the prompt states each exception and its single use.
- Export to CAD asks before it overwrites an existing file, and always
  when the path is a drawing that was brought in.
- The undo promise is limited to markups; changing an existing measuring
  scale, filling title block fields and importing CAD on an open document
  ask for confirmation first.
- Regression tests for the forbidden list, the exceptions, the undo rule
  and the overwrite question.
…rt (#785)

- Read aloud of the selection or the document from the current page on,
  in structure-tree order for tagged PDFs (content order otherwise), with
  play/pause/stop, voice and rate; the view follows the page being read.
  Web Speech is used when it has voices, otherwise native commands
  (SAPI via PowerShell, say, spd-say/espeak-ng) with a clear message when
  no engine exists.
- Tags panel reads /StructTreeRoot lazily with pdf-lib (RoleMap, MCR/OBJR,
  MCIDs mapped to page text via pdf.js marked content), lets the user
  reorder children and edit /Alt; edits are written by the saver.
- Accessibility check dialog: untagged documents, /MarkInfo, /Lang,
  figures without alt text, untagged or orphaned page content, and
  annotations without a description.
- Translations for all 40 locales; tests for parsing, reading order,
  save round-trip, report and engine selection.

Fixes #384.

Review fixes on top:
- Privacy: read aloud uses only local voices (localService true) by
  default. Online voices need an explicit opt-in in the read-aloud bar
  (preference readAloudAllowOnline, off by default) and are marked
  "(online)" in the voice list. An utterance always gets a local voice
  instead of the browser default; without a local voice for the language
  the bar says so, and with only online voices it offers the opt-in.
- Save race: the saver remembers the structure edits it writes and clears
  only those. An alt text or order changed during a long save is kept,
  rebased on the saved structure, and keeps the document modified.
- The tags panel, the check and read aloud first ask the open pdf.js
  document whether there is a structure tree; untagged files are no
  longer loaded whole into pdf-lib.
- PowerShell for SAPI runs without -ExecutionPolicy Bypass (not needed
  for -Command).
- Tests for the voice filter, the save race (pure and through the saver)
  and the structure check.
A replacement in a Type3 font was drawn live in Helvetica because the
painter cannot use a Type3 font as a CSS font. The preview now renders the
edited text through PDF.js from a small temporary PDF built with pdf-lib:
one page with the same box, the page's fonts copied in (CharProcs and
Resources included), and the new text drawn by the saver's own code path,
so the glyph codes, positions and Standard-14 fallback for missing glyphs
are the same as in the saved file.

- text-edit-inplace: split the read-only analysis (analyseerPagina) and
  the encoder (maakInPlaceResultaat) out of applyForPage; Type3 widths are
  scaled by the FontMatrix and a Type3 glyph only counts as present when
  its name has a CharProcs entry.
- text-edits: export tekenTextEdit (draw one edit like the saver).
- type3-voorbeeld: debounced build per edit signature, stale builds and
  renders discarded or cancelled, cache per run and zoom, temp document
  freed when the edit goes away or the document closes; Helvetica preview
  stays while pending or on failure.
- rendering: draw the bitmap in the page frame when available.
- The pixel test builds the standard font directory with forward slashes,
  so pdf.js accepts it on Windows.

Review fixes on top:
- A cheap pre-check looks only at the page resources (/Font of the page
  and of its form XObjects) for a Type3 font, cached per pdf-lib document
  and page. Without one the build returns at once, before the page
  analysis and the temporary PDF, and the painter does not even create a
  cache entry or schedule a build when the shared document is loaded.
- Tests that an edit in a regular font does no analysis, draws no
  temporary document and schedules nothing, and that a Type3 font inside
  a form XObject still counts.
An attachment annotation that came from the file and was not edited was
rewritten on every save when its content could be read. The rebuilt file
specification lost keys the app does not know, such as the collection
item /CI or keys of other tools, and the original appearance was
replaced.

An unedited attachment annotation now stays in the file as it was,
whether or not its content can be read. Edited ones are written anew,
and keep the original file specification only when the content cannot
be read, so the embedded file never disappears.

Found by the release gate on a file with an attachment annotation made
by another tool.
@DutchSailor

Copy link
Copy Markdown
Collaborator Author

Release gate (headless desktop build, ProductVersion 2026.44.1)

All runs on an isolated test instance that was never the foreground window (13,609 samples, 0 hits). All 67 verification PDFs were md5-identical before and after.

# Check Result (vs 2026.44.0)
1 Text rotation PASS (same)
2 No duplicate annotations after save PASS (same)
3 Placement under the cursor PASS, 0.00 pt (same)
4 Continuous view shows ink PASS, identical counts (same)
5 Quick zoom anchor PASS, end drift 1 px (same)
6 Search, 54 checks PASS (same)
7 Save round trip, 35 files PASS, 35/35 saved; damage figures identical
8 Rotation sweep PASS, 46/46, every per-file percentage identical
9 Comparison with an external renderer PASS, only the known flags (capture timing on two slow files)
10 GUI checks issues 1-94 PASS, 19 OK

New features, saved files checked with three external PDF libraries

  • Layers: with 41 drawing layers, a radio group, a locked layer and layers off by default, every drawing layer keeps its name, order, radio group, lock and default state after adopting, deleting and renaming markup layers. Toggling without "Set as default" leaves the default unchanged; "Set as default" writes it.
  • Attachments: a new attachment round-trips byte-identically. An existing attachment from another tool keeps its embedded file, its collection item and unknown keys when saved unedited or after other edits (fixed during the gate in 7715c2c, then rechecked).
  • Ink: dashed pressure strokes carry a dash in their appearance and render dashed externally; pressures round-trip.
  • Structural: seven symbols round-trip with all parameters; bar bending and weight schedules are placed (8 and 7 rows).
  • Move with Object snap off: no tracking or extension guides, free movement; with snap on the guides work.
  • Accessibility: only on-device voices are offered by default; an alt text edit survives save and reopen; the tags panel lists 783 rows on a tagged report.
  • Annotation summary: CSV with 88 rows matching the panel; a 16-page report.
  • Snippets: a snippet from a 3.2 MB drawing adds 11.6 KB to the target file and renders completely.
  • Close and reopen: 0 exceptions (the 2026.44.0 regression stays fixed).
  • All new panels and dialogs open with 0 console errors; boot has 0 console errors; the crash log has 0 JavaScript errors from this build.

Known, not new in this release (follow-ups): a plain dashed ink stroke without pressure is drawn solid by other readers; the layer view state is lost on Save As.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment