Skip to content

Release 2026.44.5 - #915

Merged
DutchSailor merged 15 commits into
mainfrom
integratie/release-2026.44.5
Oct 31, 2026
Merged

DutchSailor merged 15 commits into
mainfrom
integratie/release-2026.44.5

Conversation

@DutchSailor

Copy link
Copy Markdown
Collaborator

Open PDF Studio 2026.44.5

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

  • Live watermark preview (feat: Live watermerkvoorbeeld met verslepen en paginanavigatie (#848) #871, thanks @oscar981-op): the watermark dialog shows the page with the watermark, which you can drag into place, and you can browse the pages. Review fixes:

    • Escape in the document behind the dialog no longer closes it;
    • dragging keeps the watermark on the page;
    • watermarks already in the file can be edited in the preview;
    • hidden layers stay hidden;
    • the preview is lighter on large images.
  • Edit the vertices of area measurements (feat: Hoekpunten van oppervlaktemetingen direct bewerken (#872) #874, thanks @oscar981-op):

    • right-click an edge to insert a vertex at the cursor, or a vertex to delete it;
    • double-click an edge to insert a vertex at its midpoint, or a vertex to delete it.

    Review fixes: the bounding box (rotation handle) updates after deleting a vertex, and the hit distance is the same 8 screen pixels everywhere. A new save round trip test covers all page rotations.

  • Own pivot point for shapes (feat: versleepbaar draaipunt voor afbeeldingen en vormen (#826) #828, thanks @oscar981-op): the pivot point from 2026.44.0 now also works on rectangles, masks, ellipses, highlights, polygons and clouds. Scaling keeps the dragged handle under the pointer. The PR's separate pivot implementation was folded into the existing one. Review fixes:

    • scaling around a moved pivot follows the pointer;
    • locked annotations cannot be rotated through the pivot;
    • polygon vertices move along;
    • Escape restores the pivot;
    • no crosshair in a multi-selection or on tiny shapes.
  • Poster overview and step buttons (feat(print): selecteerbaar posteroverzicht en numerieke stapknoppen (#853) #866, thanks @oscar981-op): a selectable overview of all poster sheets above the preview, and step buttons on number fields. Review fixes: selecting a tile no longer rebuilds the overview or loses keyboard focus, the step buttons no longer flicker, and the step buttons got unit tests.

  • Wall button on the Drawing tab (feat: Wand-gereedschap op tabblad Tekenen (#864) #870, thanks @oscar981-op): draw walls from the Drawing tab whatever the palette country. Review fix: a regression test for the button and its default wall.

  • Bolts, nuts and anchors drawn properly: bolt heads and nuts show the hexagon facets with chamfer arcs; washers follow ISO 7089; the shaft shows a plain shank, thread with thin core lines and a chamfered tip; the nut top view shows the thread convention. A new "Anchor in concrete" tool draws cast-in anchor bolts (nut, anchor plate, L-hook, J-hook), chemical anchors, wedge anchors and headed studs, in side view in hatched concrete and in plan view.

  • Cleaner NL IFC Bouw palette icons: all icons share one style (margins, line weights, size), with no tiny or clipped text; the two pile types have their own icons.

  • Larger rotate icons: the rotate left/right icons in the ribbon are larger and easier to read.

Fixed

Fixes #848
Fixes #872
Fixes #826
Fixes #853
Fixes #864
Fixes #873
Fixes #865

Verification

  • Unit tests: 6404, 6393 pass, 0 failures, 11 skipped. 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.

DutchSailor and others added 15 commits October 30, 2026 09:19
The thin rotate arrows were hard to read at 14px. They now use a thicker
arc with a large filled arrowhead and fill the 16px icon slot, for both
the page rotate and view rotate buttons.
…ce streams

Rectangles and ellipses saved with a dashed, dotted or other non-solid line
style stored the pattern in /BS /D, but their appearance stream (/AP /N)
stroked the outline without a dash operator. Other PDF readers draw the
appearance, so these shapes showed solid lines there. Dashed and dotted
rectangles and ellipses now look dashed in other PDF readers too.

The appearance now writes the same pattern as /BS /D (streepOperator, the
helper the polygon, ink, text box and callout appearances already use), for
the outline and for the cross, as the screen draws them. The pattern is set
after the hatch fill, which stays solid as on screen. Solid shapes get no
dash operator. The wipeout shape shares this appearance and follows its own
/BS /D the same way.

Tests: a new unit test generates the rectangle and ellipse appearance for
every line style (empty, filled, with cross, without border, invisible area,
with hatch) and checks the dash operator against /BS /D; the line style
round-trip test now also checks the appearance of rectangles and ellipses
after saving, reopening and saving again.
Saved watermark definitions are stored as one JSON string in the PDF
catalog. pdf-lib's decodeText() passes every decoded code point as a
separate function argument, so a definition with a large embedded image
exceeded the engine's argument limit (the limit differs per platform).
Reading then failed silently and all watermarks disappeared from the
Watermark dialog after reopening the file, even though the burned-in
content was still there.

The definitions are now decoded with TextDecoder for UTF-16 (BE and LE,
selected by the byte order mark) and with PDFDocEncoding in bounded
chunks for legacy strings. The stored format and the drawing of
watermarks are unchanged.

Regression tests save and reload a 400,000-character image definition
with Unicode text, and legacy PDFDocEncoding and UTF-16LE strings.

Review fixes:
- None; version bump hunks left out.
The Watermark dialog only showed the chosen image; where the watermark
would land on the page was only visible after adding it. The dialog now
shows a live preview of a page of the open PDF next to the settings.

- Text, font, size or auto-fit, colour, opacity, rotation, image, scale,
  position, page range and layer update the preview immediately. It uses
  the same placement and drawing functions as the screen and the saved
  file, so centre, corner and custom positions, rotation and auto-fit per
  page size match the result.
- Previous/next buttons show other pages, with their own (rotated) size;
  the auto-fit size and the Custom X/Y prefill follow the previewed page.
- Dragging the watermark in the preview switches the position to Custom
  and fills in X/Y. Ctrl+wheel zooms the preview, the middle mouse button
  pans it.
- The dialog no longer blocks the document behind it. Switching documents
  or replacing the PDF closes it.
- When an OPS watermark that is already in the file is edited, its stream
  is removed only from a private copy for the preview. Cancel leaves the
  document, its bytes and the undo history unchanged; Apply uses the
  existing add/update path.

Review fixes:
- Escape in the document behind the non-modal dialog (cancelling a tool or
  selection) no longer closes the dialog; Escape still closes it when the
  dialog has focus or was clicked last (dialog-escape.js, with tests).
- Dragging keeps the watermark centre on the page instead of letting it
  be dragged out of sight; an image that has not loaded yet cannot be
  dragged as an invisible box (with tests).
- Regression test that the hit test follows the clockwise rotation of the
  renderer (the 90 degree case could not tell the direction).
- The preview of an edited in-file watermark falls back to reading the
  file when the byte cache is empty, as the apply path does, instead of
  showing a load error; the private pdf.js document uses the same safe
  options as the loader (no eval).
- Layers switched off in the document stay off in the preview.
- No JSON copy of the whole watermark (including large image data) on
  every keystroke just to track changes.
- Rendered page canvases are released on page change and on close.
- The preview background uses an existing theme colour (dark theme).
- Test list entry without a stray blank line; version bump hunks left out.
A selected area measurement can now be reshaped without dragging nodes:

- Right-click on an edge offers "Insert vertex here", which adds a vertex
  at the cursor, projected onto the edge. Right-click on a vertex offers
  "Delete vertex".
- Double-click on an edge adds a vertex at the middle of that edge, as the
  issue asks; double-click on a vertex removes it.

This works on the outer contour and on holes. Every ring keeps at least
three vertices, and arc segments are left to the existing contour editing,
so no curve changes silently. The area value and its label update at once,
each edit is one undo step, and the hit reach is 8 screen pixels at every
zoom level and view rotation. Locked annotations, locked layers, hidden
annotations, read-only documents and other pages are refused.

Perimeter and polyline measurements are not covered by this change.

Review fixes:
- Removing a corner now also refreshes the cached box of the area
  (x/y/width/height), as a node drag does. Loaded areas carry that box; it
  places the rotate grip and is the rotation centre, so it went stale.
- One hit reach (AREA_VERTEX_HIT_PX) for the context menu, the double-click
  and the select tool; the context menu used a smaller reach, so a click
  that kept the selection could miss the edge in the menu.
- The "Delete vertex" item gets the delete icon, like the polygon menu.
- New round-trip test with the real saver and loader: after inserting and
  removing vertices (outer ring and hole) on /Rotate 0, 90, 180 and 270, the
  /Vertices, /OPS_Holes, the appearance stream and the reopened model hold
  the same vertices and the same area. It also checks that one edit is one
  undo step and that redo restores points and label.
- Unit tests for the box refresh and for the zoom-independent hit reach.
- The browser e2e script no longer writes a screenshot to a fixed path.
…#826)

The own pivot point from 2026.44.0 (images, stamps and signatures) now
also works on rectangles, masks, ellipses, area highlights, polygons and
clouds. The crosshair sits at the centre by default; drag it (with object
snap) to any point, also outside the shape. The rotation handle then
rotates around it and the edge and corner handles keep it at the same
place on the page. Double-click resets it to the centre. The pivot stays
a field of the annotation (undo, copy and paste keep it) and is not
written to the PDF, so it is back at the centre after reopening.

While a shape's pivot is at the centre, the centre move grip makes way
for it (the shape still moves by its body or border, and Ctrl+drag on the
crosshair still copies); once the pivot is moved, the move grip returns.

Review fixes:
- Scaling around a moved pivot now keeps the dragged handle under the
  pointer; before, it moved only part of the way (half, for a centred
  pivot), so a handle could not be put on a snap point.
- Image size snapping is skipped while scaling around a pivot; its guides
  belonged to the unshifted box.
- Rotating and scaling around the pivot shift through applyMove, so a
  polygon's own vertices move along with its box.
- Shift with angle snap rotates in steps around the pivot, as the normal
  rotation handle does; grabbing exactly on the pivot no longer jumps.
- Escape during a pivot drag removes a pivot that only appeared in that
  drag (Object.assign kept the new field).
- Cropping an image keeps the pivot on the same point of the page, and
  the crop snapshot (Escape, undo) restores it.
- A pivot exactly on another handle stays grabbable and is drawn on top.
- No crosshair on locked annotations, on another program's count polygon,
  in a multi-selection, or by default on a shape that is tiny on screen
  (it would cover the shape and block moving it).
- Locked annotations can no longer be rotated or get a pivot through the
  pivot path.
)

Poster printing absorbs ISO and point rounding with a small allowance per
tile, so an A1 on borderless A3 at 100 % is 2 x 2 and an A0 on borderless
A4 is 4 x 4. The few tenths of a millimetre that fall off the outer edges
were never shown to the user. The print dialog now has "Crop small outer
edges", on by default, with the largest crop of the current print next to
it. Turning it off keeps every millimetre of the page on paper, at the cost
of extra sheets (A1 on A3 becomes 3 x 3 again). The choice applies to the
current print only and is on again when the dialog reopens. Labels are
translated in all 40 locales.

Review fixes:
- Keep the per-tile rounding allowance from main as the default instead of
  replacing it with an opt-in fixed 0.75 mm bound; the opt-in default
  brought back the 3 x 3 (A1 on A3) and 5 x 5 (A0 on A4) of #855.
- rasterLangs keeps its signature and default; randAfsnijden false is the
  strict full-coverage grid. The existing #855 tests stay unchanged.
- The hint shows the actual largest crop of the print (posterRandAfsnijding,
  computed as if the option were on) instead of a fixed maximum, which was
  wrong for grids of more than two tiles.
- Format the hint number with the locale decimal separator (toonGetal).
- Tests for the default, the strict mode, full edge coverage over scales,
  orientations, overlap and cut marks, the per-axis crop bound and the
  reported crop; print = preview check for the strict mode.
- Dropped the browser end-to-end script: it assumed the opt-in default.
Poster printing showed one sheet at a time, so checking the overall layout
meant paging through every tile. The print dialog now shows an overview of
the selected source page above the sheet preview: dashed tile boundaries and
shaded overlap strips, computed from the same poster layout as the print.
Clicking a tile, or Enter/Space on it, shows that sheet below; the sheet
preview shades the strips that repeat on neighbouring sheets. The printed
PDF is unchanged.

Tile scale and overlap get integer step buttons. Every other locale-aware
numeric field in the app shows hover/focus step buttons that send the
field's own arrow-key step, so limits, fractional steps and change events
stay as they were. Disabled and read-only fields and fields with their own
buttons are skipped. New labels are translated in all 40 locales.

Review fixes:
- The overview memoizes its layout, page, markup and colour inputs. The
  dialog hands them over through the selected-sheet accessor, so every tile
  selection rebuilt the tile elements (losing keyboard focus after Enter)
  and re-rendered the overview image.
- Focusing a hovered numeric field, including through a step click, no
  longer hides and re-shows its floating buttons.
- Unit tests for the floating step buttons (hover, click, focus moving,
  skipped fields, covered and removed fields) with a stand-in document, and
  a source check for the overview memos; the new labels join the poster
  locale check.
- Dropped the browser end-to-end script; it is not part of the unit suite.
In the web build the Fullscreen button turned on even when the browser
refused full screen, and it could not be turned off again: every next
click asked again, failed again and left the button on. That happens for
example when the app runs inside a page whose iframe does not allow full
screen. Ctrl+L behaved the same way.

The button now shows the actual state of the window after every request,
in the browser and on the desktop. When the browser or the embedding page
refuses full screen, the app says so and points to the browser's own
full-screen option, instead of doing nothing. Leaving full screen with Esc
or the browser UI turns the button off as before.

Review fixes:
- The browser helper now reports whether a request was accepted; a
  refusal, a missing API or a page that does not allow full screen
  (document.fullscreenEnabled false) is no longer treated as success.
- Explain a refused request in the web build with a translated message
  (view.fullscreenUnavailable, all 40 locales); no message on the desktop
  or in a headless test instance.
- Support Safari's prefixed Fullscreen API (webkitRequestFullscreen,
  webkitFullscreenElement, webkitfullscreenchange).
- Re-read the state shortly after an accepted request that has not taken
  effect yet (animated macOS transition), and after window resizes, so a
  window-manager change also updates the button; without this the stricter
  read-back could leave the button off while the window went full screen.
- Esc clears a stale button state even when the window is not full screen.
- Unit test js/ui/chrome/fullscreen.test.mjs covers refused, repeated,
  not-allowed and missing requests, enter/leave, a refused exit, the
  fullscreenchange event, the webkit prefix, desktop success/failure, a
  delayed native transition, headless and the translations.
- The browser e2e script closes the new message between steps and also
  removes the prefixed API when it simulates a browser without one.
The Layer dropdown on the Format tab only ever listed "None" and did
nothing. It is now the same layer choice as Properties > General: it lists
the layers of the Markup Layers panel and the layers of the drawing, shows
the layer of the selected markup (or "Mixed" for a selection on different
layers), and moves the selection to the chosen layer in one undo step. The
list follows new and renamed layers and the active document. The field is
disabled without a selection, while drawing, and when a selected markup is
locked.

Review fixes:
- The selection's layer is computed by a pure helper,
  selectieLaagKeuze() in annotations/laag-keuze.js, that reads the
  document's own annotations, so the ribbon does not show a stale layer
  when the selection holds another object than the document (moving and
  undo work on the document's annotations).
- A long layer name can no longer widen the ribbon (max width on
  #fmt-layer).
- Unit tests for the helper (empty, single, mixed, drawing layer, stale
  selection copy) and for the ribbon wiring in laag-keuze.test.mjs.
The Wall tool could only be started from the NL IFC Bouw category of the
tool palette, which is hidden when the palette country is not Nederland.
The Drawing tab now has a Wall button next to Line and Polyline. It works
for every palette country and starts the same wall tool as the palette
with the standard wall (100 mm brick): click start and end, keep clicking
to continue the chain, Esc or right-click to stop. Corners and T-joins,
thickness, material and the join options in Properties work as for palette
walls. The palette entry is unchanged and keeps its own material and
thickness. Like every ribbon button, Wall can get a keyboard shortcut in
Preferences > Keyboard Shortcuts, and its tooltip then shows it; the name
uses the existing translated "Wall" label in all 40 languages.

Review fixes:
- Unit test js/solid/components/ribbon/wand-knop.test.mjs: the button's
  place next to Line and Polyline, its reset of palette overrides, a wall
  from the button equal to the palette's standard wall (thickness,
  material, IfcWall, colour), the assignable shortcut shown in the
  tooltip, and the label in every locale.
- Side views show the hex head and nut square onto a flat: three faces
  (middle face e/2), facet lines and the true 30 degree chamfer curves;
  the head is chamfered on top, the nut on both ends.
- Washers follow ISO 7089 (outer diameter and thickness per size).
- Shank: solid outline, plain shank plus threaded length per ISO 4014
  (b = 2d + 6 / 2d + 12 / 2d + 25), thin continuous minor-diameter lines,
  a thick thread-end line and a 45 degree chamfered tip; thin dash-dot axis.
- Nut top view: hexagon, chamfer circle, thick minor circle and thin
  three-quarter major circle (ISO 6410); bolt-head plan view: hexagon,
  chamfer circle and optional washer ring.
- Side-view heights use max(across corners, washer diameter); params,
  defaults, snap points and BOUT_MATEN keys are unchanged.
- End plate and base plate connections reuse the same nut, washer and
  thread drawing.
- Unit tests for sizes, view extents, drawn extent vs realSizeMm and snap
  points.
- One style for all 43 icons: viewBox 64, content within about 6..58,
  main stroke 2, detail stroke 1.25, text at least 11 units and at most
  one short label; tiny and clipped labels removed.
- Steel sections get root radii and use the theme colour; brick, timber,
  concrete and insulation keep their material colours and hatches.
- The bolt icon now shows the hex head, washer and threaded shank at the
  same optical size as the other icons.
- Pile type 1 (narrow) and type 2 (wide) get their own icons with the type
  number, so they are distinguishable and no longer share one SVG.
- Pattern and clip ids are prefixed and unique across the category.
- The 16x16 category cube gets a symmetric top face.
- Unit test guards the viewBox, text size, stroke weights and unique ids.
New parametric template "Anker in beton" (id anker) in the NL IFC Bouw
palette, drawn with the same building blocks as the bolt:
- cast-in anchor bolt with nut and washer, anchor plate, L-hook or J-hook;
- chemical anchor: threaded rod in a drilled hole (d + 2 / d + 4) with
  mortar and a 118 degree drill point;
- wedge anchor: thread, neck, cone and expansion clip in a drilled hole;
- headed stud under a plate cast flush, with weld collar and head.
Side view is a section: hatched concrete with the anchor or hole left
open, surface line, fixture plate with clearance hole, ISO 7089 washer
and chamfered hex nut on top. A plan view shows washer, nut and the rod
end (ISO 6410), or the headed stud.

Parameters: type, size M8-M24, embedment depth hef (default per type and
size), plate thickness, protrusion (auto when 0), hook length, concrete
hatch on/off and scale. realSizeMm equals the drawn extent; snap points
add the anchor axis at the concrete surface. IFC: IfcMechanicalFastener.
The palette gets a matching 48px icon; the bolt entry is now "Bout".
Unit tests cover defaults, all variants and sizes, drawn extent, parts
and snap points.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment