Skip to content

Release 0.4.0 - #15

Merged
glenntosco merged 37 commits into
mainfrom
release/0.4.0
Nov 10, 2026
Merged

glenntosco merged 37 commits into
mainfrom
release/0.4.0

Conversation

@glenntosco

Copy link
Copy Markdown
Collaborator

Release 0.4.0.

Designer work (barcode size, relative position, editable ZPL panel), the review bug-fix waves (serial numbers never reissued, safer printing failures, atomic settings save, input limits, stricter read-only SQL guard, MCP print gate), and printing through any Windows printer as a picture with a pre-flight, per-printer mode, print station set-up and a job-size limit.

Version 0.4.0, changelog section [0.4.0]. Tests: 5304 passed, 0 failed, 2 skipped; data packs 437 passed.

Show the module as mil and mm with the printed symbol width and the quiet zone
under the module setting, add a - / + stepper, draw the quiet zone of the selected
barcode on the canvas, and warn in the Problems list about one-dot modules, thin
linear bars at 203 dpi and a symbol plus quiet zones wider than the label.
RunAsync cleared IsBusy as soon as the file was parsed, but LoadAsync applied the table, the records and the mapping afterwards. Anyone waiting for "not busy" could see the new columns with no records yet (the WithoutAHeaderRow test failed under load for this reason). The whole apply now runs inside the operation, and the Reload button is re-evaluated when the busy state ends.
An object can be anchored by one of nine points (corners, edge middles,
centre) to the label or to another object; X and Y then mean the distance
between the two anchor points. A bottom-right object stays in the corner
when the label is resized, and an object relative to another follows it
when it moves or grows, per record when a text width depends on the data.

- Core: AnchorPoint/AnchorReference enums, Anchor/RelativeTo/
  ReferenceElementId on LabelElement, Layout/AnchorResolver (absolute
  origins, cycle detection, missing references, keep-position offsets),
  ElementGeometry and ElementSizeEstimator (exact for shapes, images and
  Windows fonts; documented estimate for printer fonts and barcodes).
- ZPL generator and raster text emitter write resolved absolute ^FO/^FT;
  labels without anchors are byte-for-byte unchanged.
- Files: anchor, relativeTo and referenceElementId on elements, format
  version 11 stamped only when an anchor is used.
- Rendering: the preview adapter places anchored objects with the same
  resolver.
- App: 3x3 anchor picker and "Relative to" drop-down in the Position
  section (choosing one keeps the object where it is; a reference cycle is
  refused with a message), X/Y as offsets, anchor marker on the canvas,
  drag/nudge/align move a follower once, deleting a reference rebinds its
  followers to the label without moving them, duplicate/paste rewire
  references, and new "Align to label" commands.
- Tests: Core maths for all nine anchors, label and element references,
  cycles, serializer stamping and backward compatibility, golden fixture,
  PNG export before and after a label resize, editing and panel view-models,
  resx parity. Docs: CHANGELOG and decisions.
# Conflicts:
#	CHANGELOG.md
#	docs/decisions.md
The ZPL panel under the canvas is now an editor: typed or pasted ZPL is
re-imported through the existing importer after a 300 ms pause, as one undo
step per edit, with an explicit Generated / Edited by hand state, a Revert to
design button, a status line that counts and names the commands kept as raw
ZPL, and a hint line explaining the command under the caret from a new C#
parameter reference (ZplCommandReference) that covers every supported command.
Designer-only settings (names, bindings, conditions, locks, text fit, "do not
print" objects) are carried over to the re-imported elements.

Numbered fields (^FN) import as {{placeholders}} with the field data as the
sample value; GenerateStoredFormat writes a label as a ^DF format with one ^FN
per placeholder and GenerateFormatRecall writes the matching ^XF recall.
# Conflicts:
#	src/Rotula.App/Resources/Strings.en.resx
#	src/Rotula.App/Resources/Strings.es.resx
#	src/Rotula.Core/Zpl/ZplGenerator.cs
The hint is monospace in the code colour and sat right under the last line of ZPL, so it read as one more line of the text. A hairline and a little padding now mark where the code ends.
…n and the editable ZPL panel

Barcode size readout and quiet-zone checks, anchors relative to the label or another object
(file format 11, written only when used), the editable ZPL panel with numbered-field (^FN)
round trip, stored formats (^DF/^XF) in Core, and the data-load busy fix. Both sides added
methods, services and strings; all of them are kept. The new strings are also translated
into Portuguese (Brazil) and French (Canada).
The anchor estimated a linear barcode's width from its character count with the widest
encoding, so a Code 128 of digits came out about a third too wide and a right-anchored
tracking number landed that far too left. Where the size readout measures the symbol
(Code 128, Code 39, Code 93, Interleaved 2 of 5, EAN/UPC) the anchor now uses that width.
Refuse PostgreSQL U&-prefixed names and strings and any backslash in a quoted name, refuse words that start with a digit but are not numbers (1DROP), NUL and hidden characters outside literals, fold full-width and split non-ASCII words so a glued keyword is still seen, end line comments at every line break an engine may use, and make MySQL's -- need ASCII white space. Extend the PostgreSQL deny-list with the query_to_xml family, ts_stat, large-object and dblink functions, server file readers, logical slot readers and xid-assigning calls.
Move the PostgreSQL families that run their argument as SQL or touch server files (query_to_xml, dblink, pg_ls_, pg_read_, lo_, ts_stat, ts_rewrite, crosstab, connectby, advisory locks...) into the shared guard lists, so an ODBC connection to a PostgreSQL server is guarded with or without an engine hint.
ZPL import: one decoded-size budget shared by ^GF fields and ~DG downloads; commands over it stay raw ZPL with a warning.
Counters: counters past the 5,000 full records are compacted to their next position instead of dropped, so numbers never repeat; the file holds at most 25,000 counters and refuses a new one beyond that. Compacted files are written as format version 2.
Data files: column and cell limits for CSV and XLSX, refused with an issue before rows are padded.
Repeated-block copies: refuse a copy count whose text would exceed 64 Mi characters.
Images: total pixel cap (40 million) in the generator and ToMonochrome.
Files: validate the saved code page against the ^CI number, skip font aliases with ZPL control characters, delete the temp file when a save fails.
Generator: report an unusable encoding as an error and count QR manual bytes without throwing on unmappable characters.
…te it

The settings file was written in place, so a crash or power cut in the middle of a save left an empty file and the user's settings were lost. The new text is now written beside the file and swapped in; on failure the temporary copy is removed and the old settings stay. Also adds the autosaveSeconds setting with an explanation in the starter file.
…store failures in plain words

A run that reserves several counters is now all-or-nothing: if the counter store fails part way (disk full, locked file, access denied) the numbers already taken for earlier counters are released newest first, instead of staying reserved until someone cancels. Store errors that are not counter-store errors no longer escape Begin or Complete; they become a plain-language issue (new message key in all four languages). A release that itself fails leaves the numbers reserved, so a number that may have printed is never reissued.
…and with a risk category

The scanner sat in the MCP server's namespace although it lives in the printing project, so the desktop app could not reuse it to warn before a risky print. It now has the Rotula.Printing namespace and every command carries a category, so a user interface can describe it in the user's language. The English meaning and the MCP output are unchanged.
Tests that returned early when a font, driver or setting was missing showed a green pass for a check that never ran. A run-time Skip helper (SkippableFact) now reports them as skipped with a plain reason.
A fixed instance name let one killed run leave an instance whose master database crashed on every start, so every later run on that PC failed. The fixture now creates a uniquely named instance, fails with a plain message if LocalDB cannot create and start it, and the limited-login test skips with a reason when the PC cannot support it.
…nd DPI conversion

Finds every concrete element type by reflection and gives each public property a distinctive value, so a new type or property is covered without a new test.
CodeQL only analyses (read access plus security-events write for the upload); Dependabot only opens pull requests. Neither builds, signs or publishes a release.
Each printer now prints either in its own language (ZPL) or through its Windows driver as a picture. The mode is stored per printer in settings.json (printModes); a printer that has never been asked is asked once, with a suggestion. TCP/IP (port 9100) targets are always ZPL.

Driver printing works for a single print, the test label, the Print dialog's ticked records, batch merge from the Data window and the print station. Before it prints, a pre-flight looks at the whole job and lists what the driver cannot reproduce (raw ZPL, undrawable barcodes, UL/CSA marks), size and margin problems, and barcode modules too small to scan. Warnings need one tick; errors never print. Printer-side serial, clock and embedded-field commands are refused, and so are jobs above the label cap. A label that fits the paper only when turned is turned automatically.

The print station gets a "Set up print station" tool; acknowledgements are kept in station.acknowledged.json next to station.json. Darkness and speed are hidden for driver printers. The enum that names the two modes is PrinterOutputMode.
@glenntosco
glenntosco merged commit 8dd74f3 into main Nov 10, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant