rekhu

Seshat

Checking files

Check a print file against its order, read the findings and decisions, explain them to the customer and show a proof.

What a check is for

A check answers one question: can this file be printed for this order? Seshat reads the file, compares it with the order, and gives one finding per thing that is not right, each with what should happen next. Your app shows the result to the customer, fixes what Seshat can fix, and asks the customer the rest.

The order

The order is what the customer bought. Every key is optional except the size; a key you leave out takes its default.

Key Meaning Default
width_mm, height_mm the trimmed size
bleed_mm the bleed on every side 3
pages the number of pages
colour cmyk, or any cmyk
spot_colours_allowed true keeps spot colours
safe_mm the safe zone inside the trim 3
min_ppi the resolution asked of pictures 250
refuse_ppi below this a picture is far too small 150
max_ink the highest total ink, in % 320
min_text_pt the smallest text 6
min_line_pt the thinnest line 0.25
profile the print profile, such as fogra39 or fogra51 fogra39
preset a set of these keys for a kind of print; keys you send win. The GWG 2015 kinds of print: gwg_sheet_cmyk, gwg_sheet_spot, gwg_web_cmyk, gwg_web_spot, gwg_magazine_ads, gwg_newspaper_ads, gwg_newsprint, each also with _rgb (RGB pictures allowed). Also sheetfed_offset, sheetfed_uncoated, digital, newspaper, large_format, banner, stickers, pdfx4
strict_colour true makes RGB an error (CMYK only), not a warning
die_cut true: the product is cut to shape and needs a cut line
grommets true, or { spacing_mm, offset_mm, diameter_mm }: a banner with grommets needs grommet marks
language the language of the text, for the spelling check: en, nl, de, fr, es, it, pt found by itself
checks false turns the finer checks below off true

An order key Seshat does not know is refused with InvalidRequest, naming the nearest known key. A misspelt key never passes unnoticed, and nothing is charged.

Check a file

The file can be an upload, a path, bytes with a name, or a file_id from an earlier upload. Seshat takes PDF, EPS, ZIP, JPG, PNG, TIFF, WEBP and BMP; a ZIP is checked file by file.

use Rekhu\Core\Rekhu;

$order = ['width_mm' => 148, 'height_mm' => 210, 'bleed_mm' => 3, 'pages' => 2];

$check = Rekhu::seshat()->for($user)->check($request->file('artwork'), $order);
$check = Rekhu::seshat()->for($user)->check('/data/flyer.pdf', $order);
$check = Rekhu::seshat()->for($user)->check($bytes, $order, name: 'flyer.pdf');
const order = { width_mm: 148, height_mm: 210, bleed_mm: 3, pages: 2 };
const seshat = rekhu.seshat({ user });

let check = await seshat.check('/data/flyer.pdf', { order });
check = await seshat.check(buffer, { name: 'flyer.pdf', order });

The answer:

Field
status ok, warning or error: the worst level among the findings
file what Seshat read: pages, sizes, colours, fonts and pictures
findings the findings, see below
file_id the file, kept for a day, for a fix or a preview
safety the safety report, see File safety
ref the answer's reference
meta.usage what the call cost, one line per item

Findings and decisions

Each finding has a level, a code, Seshat's decision, and where it applies the fix, the pages and the values it measured:

['level' => 'warning', 'code' => 'bleed_missing', 'decision' => 'fix', 'fix' => 'add_bleed']
{ level: 'warning', code: 'bleed_missing', decision: 'fix', fix: 'add_bleed' }

The decision tells your app what to do:

Decision Meaning What your app does
fix Seshat repairs it with the fix on the finding fix it, with 'auto' or by name
ask the customer chooses one of the options on the finding show the options, then fix what they chose
refuse the file cannot be printed as it is ask for a new file
note for information show it, or nothing

Some of the codes you meet most: size_differs, bleed_missing, bleed_short, trim_box_missing, resolution_low, resolution_too_low, rgb_colours, spot_colours, fonts_not_embedded, pages_fewer, pages_more, text_small, lines_thin, white_overprint, text_near_edge and ink_too_heavy.

white_overprint means white that really disappears on press: CMYK 0/0/0/0 that overprints in overprint mode 1. White in grey or RGB still prints, and a spot colour at 0 % paints no ink, so neither is reported. ink_too_heavy measures total ink as the GWG 2015 specification does: the process inks (cyan, magenta, yellow, black) of the separated page, with overprint and transparency as printed, averaged within any 15 × 15 mm square inside the printed area (the bleed box, or the trim with the order's bleed). Printer's marks beyond it do not count. ink_area_mm in the order sets another square. The finding gives the places (locations). The answer also gives ink_all_inks_max, the same measure with spot colours added, for information. plates gives both numbers too (total_ink_highest_percent, total_ink_all_inks_percent), so what you show a customer always matches the check.

bleed_not_filled means the boxes give the page a bleed, but the design stops at the trim on those sides, so a thin white edge shows after cutting. Seshat finds it on the printed page along each trim edge, and gives the sides and places. Cut lines and other technical colours do not count. add_bleed fixes it by mirroring from the trim.

white_set_to_overprint follows GWG 2015 (4.8, 4.9): white text or shapes set to overprint. They may print now, but they vanish when overprint is handled differently further on. It is an error for text and a warning for shapes. White that vanishes already is white_overprint, an error. white_knockout fixes both.

white_vanishes_in_blend is a warning for white painted in blend mode Multiply or Darken. In those modes white leaves what is below unchanged, so it prints as nothing, even with overprint off. Seshat gives the places. No fix is applied, because the designer may have meant it; set the blend mode to Normal where the white should print.

Seshat follows colours and white wherever they are drawn: inside forms within forms, in tiling patterns and in shadings.

not_pdfx comes with the GWG presets: they ask for PDF/X-4 with its print profile, and make_pdfx makes it. Without a preset, a file that names no print profile gets the note output_intent_missing, which says which profile it is checked for.

spot_colours gives the pages each spot colour is used on.

rgb_colours says where the RGB is: in lists pictures, text and shapes, with the pages of each (pictures_pages, text_pages, shapes_pages), so you can tell a customer what to change.

registration_in_page follows GWG 2015 too: page content (anything not completely outside the trim) may not be in the registration colour "All", which prints on every plate. Seshat reads this from the separated page, so clipping and line widths count as printed. Crop marks and other marks beyond the trim may use it. The fix registration_to_black puts that content on the black plate and leaves the marks alone.

A question for the customer

A finding with the decision ask carries its options, so your app can offer the choice without a model:

// ['code' => 'size_differs', 'decision' => 'ask', 'options' => [
//     ['id' => 'fix:scale_to_size:fit', 'fix' => 'scale_to_size', 'option' => 'fit', 'about' => 'fit inside the ordered size'],
//     ['id' => 'fix:scale_to_size:fill', 'fix' => 'scale_to_size', 'option' => 'fill', 'about' => 'fill the ordered size, cropping the edges'],
//     ['id' => 'upload_new', 'about' => 'the customer sends a new file']]]
// { code: 'size_differs', decision: 'ask', options: [
//     { id: 'fix:scale_to_size:fit', fix: 'scale_to_size', option: 'fit', about: 'fit inside the ordered size' },
//     { id: 'fix:scale_to_size:fill', fix: 'scale_to_size', option: 'fill', about: 'fill the ordered size, cropping the edges' },
//     { id: 'upload_new', about: 'the customer sends a new file' }] }

An option's id is upload_new, print_as_is, or fix:<fix>[:<option>]. When the customer picks a fix, send it as it is: [['fix' => 'scale_to_size', 'option' => 'fill']]. See Fixing files.

Folded products

For a folder or a flyer with a fold, send the folds in the order, measured from the trim's top left: "folds": [{"axis": "x", "at_mm": 99}, {"axis": "x", "at_mm": 198}]. "x" is a vertical fold, so many mm from the trim's left edge; "y" is a horizontal one, from its top edge. Add "page" when a fold is on one page only. Work out the positions from the product yourself: roll folds with a narrower inside panel, Z-folds and gate folds differ.

Finding What it means Level
text_on_fold text crosses a fold or lies within fold_safe_mm of it (never less than the ISO 16763 fold tolerance) warning
barcode_on_fold a fold touches a barcode or its quiet zone. GS1 does not allow barcodes on a fold, crease or seam, quiet zone included error for retail barcodes (EAN, UPC), warning for other codes
face_on_fold a face in a photo is cut by a fold or lies within fold_safe_mm of it warning
graphic_on_fold a small picture or shape (a logo, an icon; up to fold_small_mm each way) is cut by a fold note

No standard sets how far text should stay from a fold, so send each shop's own fold_safe_mm and fold_small_mm. One standard does set a lower limit: ISO 16763 says how far a fold may lie from its intended place (0.6 mm for one fold up to 1.0 mm for four). Seshat never uses a safe distance below that tolerance for the number of folds on the page. A shop that asks for less gets fold_safe_below_standard, a note giving the distance used. Without them, Seshat keeps 3 mm from a fold (common prepress practice, 1/8 inch, not a formal standard) and counts pictures and shapes up to 50 mm as small. The 50 mm is Seshat's own working default, chosen because logos and icons are usually smaller and photos running across panels larger; it is not from any standard. Photos and backgrounds that run across the panels are folded by design and are not reported. Each finding gives the fold and the place.

Checking to an official standard

Send a GWG preset and Seshat checks the file against the values the Ghent Workgroup publishes for that kind of print in its GWG 2015 specification:

Preset Total ink (15 mm) Smallest text Rich black text (black ≥ 85 %) Pictures: refused / asked Line art (1-bit) Spot colours
gwg_sheet_cmyk 320 % 8 pt at most 280 % 149 / 224 ppi 549 / 799 ppi none (error)
gwg_sheet_spot 320 % 8 pt at most 280 % 149 / 224 ppi 549 / 799 ppi allowed, with a warning
gwg_web_cmyk 305 % 9 pt at most 280 % 149 / 224 ppi 549 / 799 ppi none (error)
gwg_web_spot 305 % 9 pt at most 280 % 149 / 224 ppi 549 / 799 ppi allowed, with a warning
gwg_magazine_ads 305 % 9 pt at most 280 % 149 / 224 ppi 549 / 799 ppi none (error)
gwg_newspaper_ads 245 % 10 pt at most 220 % 99 / 149 ppi 549 / 799 ppi at most 1
gwg_newsprint 245 % 10 pt at most 220 % 99 / 149 ppi 549 / 799 ppi none (error)

Every preset asks for lines of at least 0.25 pt. As the specification says, small text and thin lines are judged only when they are white or made of several inks, because one ink prints sharp at any size. Add _rgb to the name to allow RGB in pictures (never in shapes or text), as the specification's CMYK + RGB variants do.

Explain it to the customer

With explain, a model writes the result for the customer, in their language: a short summary, a text per finding, and a question for each ask. The language comes from the user (see Tenants and languages).

$check = Rekhu::seshat()->for($user)->check($request->file('artwork'), $order, explain: true);

$check['explanation']['summary'];
$check['explanation']['findings']; // [['code' => …, 'text' => …, 'question' => …], …]
const check = await seshat.check(buffer, { name: 'flyer.pdf', order, explain: true });

check.explanation.summary;
check.explanation.findings; // [{ code, text, question }, …]

The explanation is included in the check of the file (seshat_check). Without explain, show your own text per code, filled with the finding's values.

What else a check finds

Besides size, colour, fonts and pictures, every check looks for the mistakes people make who are not designers. Each of these findings says where it is: values.locations lists the page and a box in points, from the bottom left, so you can mark it on the page.

Code What it means
placeholder_text text that is a placeholder (Lorem ipsum, "your text here", XXX) is still in the file
stock_watermark a picture bank's name is in the file: a stock photo may still carry its watermark
marks_in_artwork crop marks are drawn into the artwork; the printer adds its own
screenshot a picture has the size of a screen
page_empty a page has nothing on it
picture_damaged a picture cannot be read; the press would stop on it
invisible_objects shapes or text are fully transparent; some presses print them anyway
text_rich_black, text_black_without_k small text in rich black, or black made of the colour inks only
black_four_colour a large area is black in all four inks; it will not dry
technical_colours, technical_knocks_out cut, crease, perforation, white, varnish or foil, and whether they overprint
cut_line_missing the product is cut to shape (die_cut) and has no cut line
cut_line_double, cut_line_open a cut line drawn twice, or one that does not close
grommet_marks_missing the banner gets grommets (grommets) and has no marks for them
letters_missing text uses letters its font does not hold: they print as an empty box or not at all
shapes_thin, white_thin parts of shapes thinner than print holds, and thin white on a dark ground that fills in
layers, layers_hidden the file's layers, and the hidden ones
text_hidden, text_cut_by_trim text under a picture, and text the trim cuts through
details_doubtful a phone number, email address, web address or date that cannot be right, or a word typed twice. Rows of values (colour values like 0 / 0 / 10 / 50, versions) are not taken for phone numbers
spelling words the language does not know, with a suggestion. Print words (CMYK, overprint, preflight) are known, and words with a sign the font does not name are left out
barcode, barcode_problem the barcodes and QR codes read, and the ones that may not scan

A report for the customer

report turns a check into a PDF for the customer: every page with a numbered mark where each problem is, and what was found in plain words. Send your own sentence per code in texts to word it yourself, in any language.

$report = Rekhu::seshat()->report($check['file_id'], $order, language: 'nl');
// ['file_id' => 'f_…', 'download' => '/v1/knowers/seshat/files/f_…']
const report = await seshat.report(check.file_id, { order, language: 'nl' });

Recipes per product

A recipe keeps a product's order and fixes under a name, so every upload for that product is checked and fixed the same way. Start it from a preset. check, fix and report take the recipe's name; order keys sent with the call win. Recipes are free.

Rekhu::seshat()->saveRecipe('A5 flyer', ['preset' => 'sheetfed_offset', 'width_mm' => 148, 'height_mm' => 210], 'auto');
$check = Rekhu::seshat()->check($path, recipe: 'A5 flyer');
$fixed = Rekhu::seshat()->fix($check['file_id'], recipe: 'A5 flyer');
await seshat.saveRecipe('A5 flyer', { order: { preset: 'sheetfed_offset', width_mm: 148, height_mm: 210 }, fixes: 'auto' });
const check = await seshat.check(file, { name, recipe: 'A5 flyer' });
const fixed = await seshat.fix(check.file_id, null, {}, { recipe: 'A5 flyer' });

Plates and versions

plates separates a page: every plate (cyan, magenta, yellow, black and each spot colour) as a picture with its ink coverage, and a proof of the page as printed, with overprint, through the print profile. compareFiles shows what changed from an earlier version: the words added and removed per page and the areas that look different, marked on a picture.

$plates = Rekhu::seshat()->plates($fileId, page: 1);
$changes = Rekhu::seshat()->compareFiles($newId, $oldId);
const plates = await seshat.plates(fileId, { page: 1 });
const changes = await seshat.compareFiles(newId, oldId);

Show a proof

preview draws a page as a PNG with the trim (green), the bleed (blue) and the safe zone (orange). Show it before the customer approves the order.

$proof = Rekhu::seshat()->preview($check['file_id'], page: 1);
// ['image' => 'data:image/png;base64,…']
const proof = await seshat.preview(check.file_id, { page: 1 });
// { image: 'data:image/png;base64,…' }

longest sets the longest side of the picture in pixels (1200 by default).

Every code in one list

codes() answers the versioned list of every finding code with its levels and decisions, every fix with its options, and every order key. It is free. Use it to map Seshat's names against your own tool's names, or to build your texts per code.

$list = Rekhu::seshat()->codes(); // version, order, decisions, fixes, codes
const list = await seshat.codes(); // version, order, decisions, fixes, codes

When the customer removes the file

Rekhu keeps a file for a day. When a customer takes a file out of their order, remove it at once:

Rekhu::seshat()->remove($check['file_id']);
await seshat.remove(check.file_id);

A file_id past its day, or removed, is refused with FileNotFound.

Next