← All posts

2026-11-05

Format, Validate, and Transform Label Data with Python Before Print

Format, Validate, and Transform Label Data with Python Before Print
pdf labels barcodes label merge browser based graphical layout editor google sheets addin microsoft excel addon electronic shelf labels esl barcode3 labels label editor

Format, Validate, and Transform Label Data with Python Before Print

A price label that prints 35.00 instead of 3.50 is not a design problem. It is a data problem — and it is the kind of problem that survives every visual review because the label looks fine.

This article is about the layer most label workflows skip: what happens to a field value between “the spreadsheet has a value” and “the printer puts it on paper.”

The Freelancer Who Almost Shipped a Wrong Price Label

Picture a solo developer — call her Mara — who takes on a small retail client: one shop, forty SKUs, a spreadsheet the owner maintains herself. Mara builds a label template, wires the spreadsheet in as the data source, prints a test sheet, and the first label shows a 250 g coffee pack at 35.00.

The spreadsheet said 3,50. A decimal comma. The label design expected a dot, and nobody noticed until the proof came back from the shop owner.

The rest of the column is no better:

  • Prices as 3.5, 3,50, € 3.50, and a few empty cells.
  • SKUs that Excel silently turned into numbers, eating the leading zeros a barcode needs.
  • Best-before dates in three formats across forty rows.
  • Supplier codes with a SUP- prefix the owner wants stripped for a cleaner label.

Mara’s first instinct is the standard one: write an export script that cleans the CSV, then feed the cleaned file to the print tool. That works — until the shop owner edits the spreadsheet next week and the export script lives on Mara’s laptop, undocumented, and nobody remembers to run it.

So the real question is this: how do you clean and shape field values before they reach the printer, without standing up and maintaining a second data pipeline?

The answer in RSJ LPSNG is the Python Field Script API: script blocks that are attached to individual label fields and that access and modify field values before printing. The script travels with the label instead of living beside it.

To be clear about scope: this is not about designing the label. If you want the layout side — placing text, barcodes, and images in the browser editor — that is covered in How to Design Barcode Labels in a Browser Layout Editor. This article is about the values that flow into that layout.

Why Field-Level Data Control Matters for Developers and End Users

Most label defects are data defects wearing a design costume. Wrong decimals, unformatted dates, missing leading zeros in barcodes, a truncated product name that no longer fits — the layout is doing exactly what it was told. The input was wrong.

Field-level control matters for two very different audiences, for the same underlying reason.

For developers, it removes a whole class of side infrastructure. You do not write and maintain an external pre-processing script, a scheduled job that rewrites CSV files, or glue code that sits between the data source and the print engine. Formatting and validation live in the label definition, which is the artifact that actually needs them.

For end users and small teams, it produces consistent output without requiring anyone to understand the source data. The shop owner keeps filling in her spreadsheet the way she always has. The label decides how a price is displayed.

The Python Field Script API is the middle layer between those two worlds:

  • It runs on the field, not on the file.
  • It can read and modify the value before printing.
  • It lives inside the same platform that renders the label, so there is no version drift between “the cleaner” and “the template.”

The API reference is at lpsng.rsj.de/api.html.

A Practical Playbook: Format, Validate, and Transform in Three Passes

Treat each field script as three passes over the value, in this order: format it, prove it, then derive from it. The snippets below are illustrative Python — the important part is the shape of the logic, not the exact helper names.

Pass 1: Format

Normalize the raw value into exactly what the label design expects. Prices to a fixed decimal pattern, dates to one display format, text to the casing the layout was designed around.

import re

CURRENCY = re.compile(r"[^\d,.\-]")

def format_price(raw):
    """'€ 3,50' -> '3.50'; returns None when nothing usable is left."""
    if raw is None:
        return None
    cleaned = CURRENCY.sub("", str(raw)).strip()
    if not cleaned:
        return None
    if "," in cleaned and "." in cleaned:
        cleaned = cleaned.replace(".", "").replace(",", ".")   # 1.234,56
    elif "," in cleaned:
        cleaned = cleaned.replace(",", ".")                    # 3,50
    return f"{float(cleaned):.2f}"
from datetime import datetime

DATE_FORMATS = ("%Y-%m-%d", "%d.%m.%Y", "%d/%m/%Y")

def format_date(raw, out="%d.%m.%Y"):
    text = str(raw or "").strip()
    for fmt in DATE_FORMATS:
        try:
            return datetime.strptime(text, fmt).strftime(out)
        except ValueError:
            continue
    return None   # unparseable beats guessed
def label_case(raw, style="upper"):
    text = " ".join(str(raw or "").split())   # collapse stray whitespace
    return text.upper() if style == "upper" else text.title()

Note the deliberate None returns. An unformattable value should fail visibly in the next pass, not become a plausible-looking 0.00.

Pass 2: Validate

Validation is what keeps a wrong price from quietly reaching the printer. Check that the fields you depend on exist and are well-formed, and make every message specific enough to locate the record.

def validate_record(record):
    """Return a list of 'field: reason' strings. Empty list means OK."""
    errors = []

    sku = (record.get("sku") or "").strip()
    if not sku:
        errors.append("sku: missing")
    elif len(sku) < 8:
        errors.append(f"sku: shorter than 8 characters ({sku!r})")

    price = record.get("price")
    if price is None:
        errors.append("price: not parseable")
    elif not (0.01 <= price <= 9999.99):
        errors.append(f"price: out of range ({price!r})")

    if record.get("best_before") is None:
        errors.append("best_before: unparseable date")

    return errors

A message like price: not parseable ('0,00') tells an operator which field to look at and which value caused it. A message like invalid data tells them to call you.

price: not parseable ('0,00')
sku: shorter than 8 characters ('4711')
best_before: unparseable date (row: Espresso 250g)

Pass 3: Transform

Only after the value is formatted and validated should you derive display values from it — a combined product code, a supplier prefix trimmed away, a short name for a narrow field.

def product_code(record, prefix="SUP-"):
    supplier = (record.get("supplier_code") or "").strip()
    if supplier.startswith(prefix):
        supplier = supplier[len(prefix):]
    sku = (record.get("sku") or "").strip()
    return f"{supplier}-{sku}".strip("-")

Where the script lives in the workflow

In RSJ LPSNG you attach the script block to a label field in the layout. The platform runs it as part of the normal print or render job — the same run that also handles PDF or PNG output and direct print through the web service interface. There is no sidecar process to start and no ordering problem to reason about.

That matters for handover. The formatting rule is part of the label definition, so it moves with the template rather than staying on the machine of whoever wrote it.

The Hard Way vs. the Managed Way

The hard way is the one Mara almost chose: a separate script that rewrites CSV or Excel files before they are fed to a print tool. It has its own deployment story, its own failure modes, and its own quiet assumption that everyone remembers to run it before printing.

The managed way puts the Python script block on the field itself in RSJ LPSNG. Formatting and validation travel with the label. The same label can be rendered as PDF or PNG, or sent to direct print through the web service interface, and the field logic is the same in every case because it belongs to the label, not to an external step.

Checklist for Your First Field Script

  • Start with one field and one rule. Format the price, render a sample PDF, and inspect it. Only then touch the next field.
  • Make failures locatable. Every validation message should name the field and show the offending value.
  • Test with deliberately messy input. Empty cells, extra whitespace, mixed date formats, a negative price, a price of 0.00, a SKU of the wrong length.
  • Decide transform vs. lookup. Excel files can serve as both data files and lookup files, so a supplier name may belong in a reference sheet rather than derived in script. The data-source side is covered in How to Print Barcoded Labels from Excel.
  • Render before you run. A PDF proof of three messy records costs a minute; a reprint of forty wrong shelf labels costs a morning.

What to Avoid

Do not bury business rules in the layout. Keep formatting and validation in the field script and keep design decisions in the layout editor. Mixing them means a design tweak can quietly change a price format.

Do not silently coerce bad data into a plausible value. A label that confidently prints the wrong price is worse than a job that fails loudly. Return None and let validation catch it.

Do not duplicate the same transformation across many fields. If six fields need the same price format, one shared approach beats six copy-pasted blocks that drift apart. Inconsistency between fields on the same label is one of the most common sources of errors.

Do not guess at ambiguous input. 03/04/2026 is either March 4th or April 3rd. Pin the format at the source; a script that guesses will be wrong for someone.

Do not treat the script as a substitute for testing. Render sample labels and look at them. Field scripts reduce the number of surprises, they do not eliminate the need to check.

FAQ

Where do Python script blocks run in RSJ LPSNG?

Python script blocks are attached to label fields and access and modify field values before printing, as part of the label definition in the platform.

Do I need to build a separate data-cleaning pipeline?

No. The field script handles formatting, validation, and transformation inside the label itself, so you do not need a sidecar script rewriting your data files before they reach the print engine.

Can I still submit jobs programmatically?

Yes. The web service interface lets you submit print jobs and render single labels as PDF or PNG, and the OAuth2 model covers connecting external systems with a single registration URL.

Does the same field logic apply to PDF, PNG, and direct print?

PDF/PNG rendering and direct print are documented output paths of the web service interface, and the field logic belongs to the label definition those paths use. Electronic shelf labels are a separate track with their own vendor-neutral JSON interface (ESLSEND) — see the ESL interface docs rather than assuming the print path applies unchanged.

Can non-developers use a label that contains field scripts?

Yes. Once the script is attached to the field, printing the label does not require writing or reading Python. Whoever runs the job selects data and prints; the formatting and validation happen as part of the label.

Does this replace the layout editor?

No. The layout editor handles design; the Python field scripts handle the data that flows into each field. They are complementary parts of the same label.

Conclusion

Mara’s fix was not a better export script. It was moving the rule to where the value is actually used: a price field that formats a decimal comma into 3.50, refuses to print an empty cell, and strips a supplier prefix on the way out. The shop owner kept her spreadsheet. The labels stopped being wrong.

Next Generation Label Printing System is built around exactly this split — design in the layout editor, data control in field scripts, and a web service interface when you want to drive jobs programmatically. Start with one field, one rule, and one rendered PDF. Then hand it over.


Related posts

EU label: AI-generated content