Most form validation libraries answer one question: is this field valid?
Document-generation products need to answer a harder question: is this file safe to export, and if it is technically exportable, does a human still need to review something?
We ran into this problem while building a print-oriented web app. The inputs included structured facts, free-form text, photos, template choices, and page-format decisions. A green check beside every form field did not guarantee a usable PDF.
The model that worked for us has three states:
type CheckerStatus = "pass" | "needs_review" | "cannot_export";
Enter fullscreen mode Exit fullscreen mode
This article explains why three states are better than two and how to connect them to a real rendering pipeline.
Why valid/invalid is too crude
Consider three different problems:
- A required name is blank.
- A portrait may look slightly soft when printed.
- An obituary overflows its text frame and part of it will be missing.
The first and third should block export. The second should trigger a visible warning, but the user may intentionally continue because it is the only available photo.
Mapping all three to invalid makes the app needlessly rigid. Mapping all three to valid hides meaningful risk.
Our checker returns:
type CheckerResult = {
status: "pass" | "needs_review" | "cannot_export";
issues: CheckerIssue[];
exportBlocked: boolean;
warningCount: number;
errorCount: number;
};
Enter fullscreen mode Exit fullscreen mode
The status is derived from issue severity and whether warnings have been explicitly reviewed:
const status =
errorCount > 0
? "cannot_export"
: unresolvedWarnings
? "needs_review"
: "pass";
Enter fullscreen mode Exit fullscreen mode
That small distinction creates a much more honest interface.
Layer 1: content completeness
Start with the checks users expect from a form:
- required names;
- required dates and times;
- venue or location;
- template or format selection;
- sections required by the chosen document format.
We also scan printable text for unresolved placeholders such as:
[Name]
TBD
To be determined
Name here
Enter fullscreen mode Exit fullscreen mode
Placeholder detection is useful because a field can be non-empty and still be unfinished. The same idea applies to invoice templates, certificates, proposals, and generated reports.
Every issue should identify a section and a repair destination:
type CheckerIssue = {
id: string;
level: "warning" | "error";
section: string;
message: string;
fixAnchor?: string;
fixLabel?: string;
};
Enter fullscreen mode Exit fullscreen mode
The UI can then offer a “Fix service details” or “Review obituary” action instead of leaving the user to hunt through a long builder.
Layer 2: document structure
Before checking visual layout, verify that the renderer produced the expected structure.
For a multi-page printable document, this includes:
- the selected format has a known page-imposition map;
- the rendered logical page count matches the format;
- all expected pages exist;
- the front/back or booklet ordering can be produced.
These checks catch failures that no field validator can see. A four-page document with three rendered pages may contain perfectly valid strings and still be unusable.
Layer 3: geometry and text fit
The most valuable checks run against the same layout plan used to generate the PDF.
For each page and text box, validate:
Bounds
x >= 0
y >= 0
x + width <= page width
y + height <= page height
Enter fullscreen mode Exit fullscreen mode
Template containment
If a text box belongs to the obituary section, confirm that its rectangle stays inside the obituary frame defined by the selected template.
Avoid regions
Templates often contain areas that content must not cover: decorative artwork, fold zones, binding space, or photo cutouts. Model those as explicit “avoid” rectangles and check for overlap.
Overflow
The layout engine should report whether all text fits after line wrapping and font-size adjustment. If text still overflows, block export and send the user to the relevant content section.
Minimum print size
Responsive web text can shrink freely. Printed text cannot. Define a minimum acceptable font size and treat anything smaller as an error instead of silently producing unreadable copy.
The key is that the checker consumes renderer output. Reimplementing layout rules separately in validation will eventually create disagreement between “the checker says it is fine” and the actual PDF.
Layer 4: photo suitability
Image validation should happen twice.
At upload time, check basic properties:
- supported MIME type;
- width and height;
- obvious low-resolution cases.
At layout time, calculate effective resolution based on placement. The same image can be acceptable in a small frame and poor as a full-page image.
effective PPI = available source pixels / printed inches
Enter fullscreen mode Exit fullscreen mode
The calculation should account for crop geometry. If only half the source width remains after cropping, the full original width should not be used in the quality estimate.
We treat borderline resolution as a warning and an unusable or unsupported file as an error. The message describes the likely outcome—soft or pixelated print—rather than presenting a number without context.
Warnings require an explicit decision
A warning should never be a decorative yellow icon that users can ignore accidentally.
Our document state includes a warning-override flag. Until the user reviews and accepts the warnings, the checker returns needs_review. After acceptance, it can return pass as long as no blocking errors remain.
This creates useful semantics:
-
passmeans the system found no unresolved issue; -
needs_reviewmeans the system needs a human decision; -
cannot_exportmeans the system knows the output would be invalid.
For auditability, store the acceptance with the relevant document state. If the user changes the photo or layout, recompute the checks and require review again when appropriate.
Keep error messages actionable
Compare these messages:
Validation failed.
Enter fullscreen mode Exit fullscreen mode
and:
The obituary does not fit on the inside page. Shorten the text or choose a roomier layout.
Enter fullscreen mode Exit fullscreen mode
The second message identifies the affected content, the consequence, and two ways to fix it.
Good document-validation messages answer four questions:
- What is wrong?
- Where is it wrong?
- What will happen if it is not fixed?
- What can the user do next?
Direct fix anchors are especially helpful in long, multi-step editors. Validation becomes navigation, not just judgment.
Run the checker at meaningful moments
Running every expensive layout check on every keystroke can make the interface feel unstable. We use layers:
- cheap field feedback during editing;
- image checks after upload or crop changes;
- complete layout checks when the preview is generated or materially changed;
- a final check immediately before export or checkout.
The final check must use the exact data and photos sent to the renderer. Avoid validating one state object and exporting another.
Test the checker as a product contract
Useful fixtures include:
- empty required fields;
- unresolved placeholders;
- extremely long names;
- long obituary and service-order text;
- unsupported image types;
- low-resolution photos in small and large placements;
- aggressive crops;
- missing template frames;
- boxes outside the page bounds;
- avoid-region overlap;
- incorrect page counts;
- warning accepted and warning not accepted.
Snapshotting the issue IDs and severity levels is often more stable than snapshotting the entire PDF. Renderer tests and checker tests should complement each other.
The general pattern
The three-state model is useful anywhere software generates an artifact that has both machine constraints and human judgment:
- resumes and portfolios;
- invoices and proposals;
- certificates and badges;
- photo books;
- labels and packaging;
- reports and regulatory documents;
- print-on-demand products.
The central idea is simple:
Do not ask only whether the input is valid. Ask whether the rendered artifact is complete, technically safe, and ready for human approval.
We applied this model in the free Funeral Program PDF Checker. The domain is specific, but the validation architecture is broadly reusable.
Disclosure: I work on Funeral Program Maker, the product used as the implementation case study.
답글 남기기