AI image models can render beautiful menus that fail the one requirement software teams actually need: the words must be correct. Visual inspection catches obvious nonsense, but it is inconsistent, hard to reproduce, and weak at spotting a missing price or a single-character substitution.
Treat text-bearing image generation as a testable pipeline. Define the expected strings, render the image, run OCR on deliberate crops, normalize only known formatting differences, and fail on every unexpected omission or addition.
Text layout is part of the visual output, but the acceptance contract should still be machine-readable.
1. Make the prompt and assertion share one source
Do not type menu copy separately into the prompt and the test. Store a structured specification and derive both from it. For example:
EXPECTED = {
"title": "MORNING MENU",
"items": [
("OAT LATTE", "$4.50"),
("CITRUS TOAST", "$8.00"),
("MISO EGGS", "$11.00"),
],
"footer": "ORDER AT THE COUNTER",
}
This eliminates a subtle false failure: the prompt says one thing while the fixture expects another. It also makes intentional copy changes visible in code review.
2. Generate for legibility, not maximal decoration
Use the GPT Image 2 workspace and write the exact copy in a clearly delimited block. The live page inspected on September 1, 2026 showed GPT Image 2, text-to-image and edit-image paths, several aspect ratios, Public · watermarked visibility, and a 45-credit quote. The interface can change, so recheck the live quote and selected settings.
Keep instructions concrete: front-facing menu board, high contrast, one type family, no decorative lettering through words, exact text only, and no extra prices. Ask for empty margin around each region if you plan to crop it independently.
Never treat the model’s ability to draw letters as a guarantee. The generated image is an untrusted artifact until the validator passes.
3. Crop before OCR
Whole-image OCR often mixes background marks, decorative text, and menu columns in the wrong reading order. Define normalized crop boxes for title, item block, and footer. Store them as fractions so tests survive resolution changes.
from PIL import Image
def crop_ratio(image, box):
w, h = image.size
left, top, right, bottom = box
return image.crop((left*w, top*h, right*w, bottom*h))
regions = {
"title": (0.08, 0.06, 0.92, 0.22),
"items": (0.08, 0.22, 0.92, 0.78),
"footer": (0.08, 0.80, 0.92, 0.95),
}
Save failed crops as CI artifacts. A developer should be able to see whether the generator, crop, preprocessing, or OCR engine caused the mismatch.
Separate crops make failures local: a price mismatch does not disappear inside a visually attractive poster.
4. Normalize narrowly
Uppercase conversion and whitespace collapse may be acceptable when case and line wrapping are not requirements. Replacing arbitrary characters is not. If you silently map every 0 to O, the test can approve a wrong price.
import re
def normalize(text):
text = text.upper().replace("\n", " ")
return re.sub(r"\s+", " ", text).strip()
def assert_contains_exactly(ocr_text, expected_lines):
actual = normalize(ocr_text)
missing = [line for line in expected_lines if normalize(line) not in actual]
assert not missing, f"Missing exact strings: {missing}; OCR={actual!r}"
For prices and compliance copy, assert exact tokens separately. You may allow a known currency-spacing variant such as $ 4.50, but encode that rule explicitly and test it. Never use a fuzzy threshold as the only gate for a safety warning or legal statement.
5. Add negative assertions
Positive assertions prove required text appears. Negative assertions catch invented items, duplicate prices, and forbidden placeholders. Count currency tokens and compare with the specification. Reject LOREM, obvious nonsense, and any item not present in the source data.
Also inspect reading order. OCR can find every line while the menu pairs a price with the wrong dish. Region-specific assertions or column-aware OCR make that relationship testable.
6. Build a reviewable failure report
Return the original image, annotated crop rectangles, OCR text per region, normalized text, and assertion diff. Include model and generation settings as metadata, but do not record secrets or private source images in public CI logs.
Run the test at native resolution and at the real delivery size. A menu that passes at 2048 pixels may become unreadable in a 600-pixel card. The consumer size is part of the acceptance contract.
7. Keep a human in the final gate
OCR verifies strings, not typography, hierarchy, contrast, cultural meaning, trademark clearance, or whether a menu price is correct for the business. A human reviewer should still inspect those dimensions after automated checks pass.
The useful rule is simple: generated text is data with a visual presentation, not decoration you hope is right. Create one constrained text image, then make the validator fail loudly before you trust the result.


Top comments (0)