Print-on-demand artwork fails in conversion for boring reasons: a missing color profile, a physical size that cannot be reconciled with the pixel dimensions, or an alpha channel that the printer never accepts. Short answer: treat metadata as a versioned contract, validate it before conversion, and make the converter consume only the validated record.
I build RAG and agent features in Python, so I recognize the tempting shortcut: open the image, resize it, and discover the problem in a print proof. That approach optimizes for the happy path. A metadata gate gives the pipeline a decision it can explain to an operator and an eval harness can test.
What should a print-on-demand metadata contract contain before conversion?
Start with fields that describe intent, not whatever a decoder happened to infer. My minimum record has pixel width and height, physical width and height, resolution, color space, alpha policy, orientation, file format, and a content checksum. Keep the original values beside normalized values; rounding a 12.00-inch canvas to 11.99 inches is a data change, not a formatting detail.
The contract also needs policy. For example, “CMYK only” is a business rule, while “the file contains an ICC profile” is a file observation. Mixing those concepts makes failures hard to diagnose. A rejected asset should say which observation violated which policy and identify the asset revision.
Here is a deliberately small validator. It does not convert pixels; it decides whether a later converter is allowed to run.
from dataclasses import dataclass
from typing import Optional
@dataclass(frozen=True)
class ArtworkMetadata:
width_px: int
height_px: int
width_in: float
height_in: float
dpi: int
color_space: str
has_icc_profile: bool
has_alpha: bool
orientation: int
format: str
checksum: str
def validate_for_conversion(meta: ArtworkMetadata) -> list[str]:
errors: list[str] = []
if meta.width_px <= 0 or meta.height_px <= 0:
errors.append("pixel dimensions must be positive")
if meta.width_in <= 0 or meta.height_in <= 0:
errors.append("physical dimensions must be positive")
if meta.dpi < 150:
errors.append("dpi is below the configured 150 threshold")
if meta.color_space not in {"sRGB", "Display-P3", "CMYK"}:
errors.append("color space is outside the accepted set")
if not meta.has_icc_profile:
errors.append("an ICC profile is required for conversion")
if meta.orientation not in {1, 3, 6, 8}:
errors.append("orientation must be a recognized EXIF value")
if meta.format.lower() not in {"png", "jpeg", "tiff", "webp"}:
errors.append("source format is not enabled for this workflow")
if not meta.checksum:
errors.append("checksum is missing")
return errors
The 150 DPI value above is an example policy, not a universal printing standard. Your substrate, trim size, and vendor profile should set it. I’m not sure a single threshold can represent every product catalog; the useful part is that the threshold is explicit, reviewable, and covered by tests.
How do metadata checks change the conversion pipeline?
Put discovery, normalization, validation, conversion, and proof generation in separate stages. Discovery reads the file. Normalization resolves orientation and units. Validation freezes a contract. Conversion receives that contract plus the source bytes. Proof generation records the output metadata and a new checksum.
That ordering matters when a queue retries a job. A retry should use the same validated revision, not re-read a file that an editor may have replaced under the same filename. Store the contract with the asset revision, and include its hash in job logs. This makes an unexpected crop or color shift traceable without opening every intermediate file.
Measure twice.
I once assumed that a JPEG's reported DPI would survive a library round trip. It did not: the pixels were identical, but the exported density tag was absent, so a downstream size calculator treated the art as 72 DPI. The fix was not a clever conversion flag. We made density an assertion on the output and failed the job before it reached fulfillment. That small assertion changed the handoff between the notebook and production: the notebook fixture now carries the same contract hash as the queue message, the worker logs the pre- and post-conversion records side by side, and an eval test mutates one field at a time to prove that each rejection remains understandable. When a designer replaces a source file, the checksum changes and the old proof is retained as an immutable artifact, so support can answer “which pixels did we print?” without guessing from a filename. This is extra bookkeeping, but it is cheaper than asking a customer to explain a color shift from a package they received weeks ago.
Keep the test fixture set small and adversarial: a rotated phone photo, a transparent logo, a Display-P3 screenshot, a TIFF with an embedded profile, and a file whose extension lies about its bytes. The eval should compare both the acceptance decision and the reason. A green conversion with an opaque reason is still an operations failure.
Where do common formats hide conversion risk?
PNG can carry transparency and textual chunks; JPEG cannot preserve an alpha channel. TIFF commonly carries rich color and resolution metadata, while WebP support varies across print tooling. SVG is not a raster input at all, and its dimensions can be expressed in CSS units that require careful resolution before rasterization. These are format properties, not promises about any particular service.
Use a standards-aware parser, then cross-check what the parser reports against the byte-level format and your policy. MDN’s media format guide is a good starting map, but it is not a substitute for the profile supplied by your printer or substrate team. The conversion boundary should make an intentional choice about flattening alpha, converting color, and handling orientation; silent defaults are where expensive surprises start.
A practical decision rule for teams shipping artwork
If your catalog has a few fixed products, a strict contract and a handful of fixtures are enough. If sellers upload arbitrary art, add a quarantine state and an operator-facing remediation message. Do not auto-correct physical dimensions unless the product owner has approved the rule; changing them can alter trim, bleed, or legal labeling.
The catch is that a metadata gate adds latency and rejects assets that a permissive converter might accept. It is not suitable when you are producing disposable previews where fidelity is intentionally approximate. Stick with a lightweight preview path there, but keep the strict contract for files that can reach a paid order.
Measure before copying this design: rejection rate by reason, re-upload rate, proof-to-order mismatch, and the percentage of outputs whose metadata matches the contract. Those numbers tell you whether the gate is protecting quality or merely moving confusion earlier in the queue.
Top comments (0)