Rendering is only the start. zplCore.com checks labels against GS1 and carrier rules, reads every barcode back, simulates printers including their faults and makes labels testable like code. All offline, in your own process and in CI.
Compliance pre-check
84 rules in 8 packs: GS1 logistic label, Sunrise 2027, Amazon FBA, UPS, FedEx, DHL Paket, UDI and RFID. Measured on the rendered label (X-dimension, quiet zones, human-readable text, AI formats, check digits); every finding with line, position and source, as JSON, SARIF or an annotated PNG. A pre-check, not a certification.
Every barcode is read back from the rendered image by two independent decoders and compared with the intended data. X-dimension, quiet zones, height and overlaps are measured, plus an A–F grade as a digital pre-check modelled on ISO/IEC 15415/15416. It does not replace a verifier on the printed label.
Existing ZPL becomes a data-bound template: text, barcodes, shapes, images and RFID as typed items, ^FN fields as data fields. Every item is checked pixel by pixel; all 318 files of the test corpus round-trip pixel-exact.
Reads recipient, sender, tracking numbers with check digit, SSCC, GTIN, dates and weights from the ZPL, with location and confidence. Redaction replaces personal data in the same layout, also in comments and 2D codes, and searches again afterwards. Rule-based, no network; review the output before sharing it.
Finds the GTIN in EAN/UPC, GS1-128, ITF-14 or DataBar and adds a QR Code with a GS1 Digital Link URI or a GS1 DataMatrix in free space, with quiet zone and a Sunrise pre-check. Builds and parses Digital Link URIs per URI Syntax 1.7.0.
Encodes and decodes EPCs per GS1 EPC Tag Data Standard 2.3 (SGTIN, SSCC, SGLN, GRAI, GIAI and the TDS 2.0 plus schemes) with the matching ^RF commands. Checks that the tag carries the same SSCC or GTIN as the printed label.
Will this label print on this model? 24 device profiles (including ZD421, ZD621, ZT411, ZT610, ZQ630*) report fields beyond the printhead width, missing options such as cutter or RFID, firmware limits, fonts and memory, plus a print time estimate. Every profile fact cites its source or is marked unknown.
The virtual printer on TCP 9100 fails on request: paper or ribbon out, head open, disconnects, timeouts, scripted and reproducible. ~HS and ~HQES answer like a printer. The cutter simulation shows after which labels the printer cuts, with cut lines in PNG, SVG and PDF.
Test labels like code: ZplAssert.Label(zpl) checks that the SSCC barcode decodes to the expected data, plus overlaps, clipping, compliance, printer profile, pixel and semantic snapshots. Failures name the element, its position and the ZPL line. zplcore check and a GitHub Action comment on pull requests with before/after images.
zplcore-mcp gives Claude Code, Claude Desktop, VS Code and Cursor 26 tools: render, lint, compliance, scan-proof, redaction, printer twin and more. stdio only, no network port; every call runs in its own worker process with time and memory limits and no access to files or the network.
Vector-Lift turns carrier and ERP PDFs or SVGs into real ZPL instead of a ^GF raster image: text as ^A0 fields, barcodes regenerated and read back, lines and boxes as ^GB/^GC/^GE. With measured agreement per label and element.
SDK, testing package, CLI and MCP server build without trim and AOT warnings. As a NativeAOT container on runtime-deps:10.0-noble-chiseled-extra (~79–105 MB), everything starts without a .NET runtime – verified on macOS, Linux (arm64/x64) and as a local label print server on a Synology DS923+ (793/793 outputs identical to JIT, 24 % faster).
A broken template that shows up in production gets expensive: barcodes that do not scan at the dock, lines that stop, shipments the carrier rejects, and waves of errors across every site. zplCore checks every template where it is made: in the commit, in the pull request, in the pipeline and already in the container build.
Commit
template changed in Git
Gate
Pull request
before, after and diff images in the PR comment, findings as SARIF in code scanning
Gate
CI pipeline
zplcore check with JUnit XML and SARIF; exit code 1 stops the build
Gate
Container build
a check stage in the Dockerfile: no image is built with a broken template
Staging
virtual printer with cutter and RFID: cuts, EPCs and VOID as on the real device
Production
only checked and approved templates
What the gates check
Compliance84 rules in 8 packs, e.g. GS1 logistic label and DHL Paket
Scan-proofevery barcode read back, minimum grade with --min-grade
Snapshotspixels plus semantics: what changed, not only how much
Layout & contentoverlaps, clipping, fields, texts, number of labels
Base comparison--baseline: which data and positions the PR changes
Cutswhere the cutter cuts: ^MM, ^PQ, ~JK, ^CN
RFIDwhich EPC goes on which tag, matching the printed barcode
What a stopped gate looks like
# real output, shortenedPASS templates/dhl-100x150.zpl
FAIL templates/express-100x150.zpl
FAIL comply gs1-logistic
:38:1 error ZPL3104 X-dimension 0.375 mm < 0.495 mm
:38:1 error ZPL3105 bar height 13.0 mm < 31.75 mm
FAIL scan-proof
:32:1 error ZT1301 QR Code grade D, required B
ok printer ZD421d (ZD421d-203)
FAILED (fail-on error) · exit 1
Checked automatically, explained by AI
The gates decide deterministically: same template, same result, exit code 0 or 1.
A coding agent (Claude Code, GitHub Copilot, Cursor) checks changes through zplcore-mcp, explains the findings and proposes the fix as a commit.
Approval stays with you, e.g. with required reviewers for label changes through CODEOWNERS and branch protection.
Also in Azure DevOps, GitLab CI or Jenkins: CLI, exit codes and JUnit XML.
MCP tools:complyverifyprinter_checkrenderdiff
CI gateany pipeline
# exit code 1 on errors, reports for the pipeline
zplcore check templates/ --pack gs1-logistic \
--verify --min-grade B --printer ZD421d-203 \
--junit out/labels.junit.xml --sarif out/labels.sarif
Gate in the container buildDockerfile
# build stage: check the templates before an image is built
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS label-gate
WORKDIR /src
COPY nuget.config ./
COPY .config/ .config/
COPY templates/ templates/
RUN dotnet tool restore \
&& dotnet zplcore check templates/ --pack gs1-logistic \
--verify --min-grade B --printer ZD421d-203
FROM mcr.microsoft.com/dotnet/aspnet:10.0
# only checked templates end up in the image
COPY --from=label-gate /src/templates /app/templates
A small change to ^MM or ^PQ suddenly cuts every label or none; a fixed ^RF writes the same EPC to every copy. Both usually show up only on the line. In the gate they are settled beforehand.
1…0000
2…0000
3…0000
4…0000
5…0000
6…0000
cut after labels 3 and 6 (^MMC, ^PQ6,3)
ZPL3909: 6 labels, the same EPC 3156…8000000 six times, the gate stops
fixed: one format per pallet, EPC = printed SSCC (00)390012340047110001
Real output: virtual printer (zplcore serve --rfid) and RFID check. Also checked: EPC matches the barcode (ZPL3904), memory bank and PC bits, ^RS/^HV, locks without password.
// cuts: where the cutter cutsvar plan = ZplCutPlan.Analyze(zpl);
Assert.Equal([2, 5], plan.CutAfter); // after labels 3 and 6
Assert.Empty(plan.Diagnostics);
// RFID: EPC matches the barcode, no duplicate EPC
ZplAssert.Label(zpl)
.Complies(CompliancePacks.Rfid)
.FitsPrinter(PrinterProfiles.Get("ZT411-203")
.WithOptions(PrinterOption.Cutter | PrinterOption.Rfid)
.AsPrinterFit());
# CI: RFID pack in the gate, show the EPC per label
zplcore check templates/ --pack gs1-logistic,rfid
zplcore rfid simulate templates/palette.zpl
# staging: simulate a printer with cutter and RFID
zplcore serve --profile ZT411-203 --option cutter,rfid \
--rfid --cut-marks
Available in the SDK today: ZplCore.Testing, zplcore check and zplcore-mcp. The GitHub Action ships with it (tools/github-action); publishing it on the Marketplace as zplcore/label-check is planned. Compliance and grades are pre-checks, not certifications.