6. prepress:: namespace — prepress
← visual:: · Index · Next: codes:: →
30 functions covering what a print shop must verify before going to plate: ink coverage, color separations, fonts, strokes and page boxes.
6.1 Ink coverage (TAC)
TAC (Total Area Coverage) is the sum of all four inks at a single point. Going over the press limit causes smearing, slow drying and set-off between sheets. The usual limit for offset on coated stock is 300%.
There are two ways to measure it, and the difference matters:
prepress::calculate_exact_tac([page]) — the trustworthy number
Reads the colors declared in the file (the PDF color operators). This is the real value.
check "Ink limit" {
tac = prepress::calculate_exact_tac()
assert tac <= 300,
"ink coverage of #{tac}% exceeds the 300% limit"
// Per page, to locate the problem
doc.pages.each { |page|
value = prepress::calculate_exact_tac(page.number)
assert value <= 300,
"page #{page.number}: #{value}% ink"
}
}
prepress::calculate_tac([page]) — the estimate
Computed by rendering the page in RGB. It is a lower bound: dark neutral colors (rich black) collapse toward 100% in the estimate.
check "Comparing both methods" {
print("exact (declared in the file):", prepress::calculate_exact_tac(), "%")
print("estimated (by rendering):", prepress::calculate_tac(), "%")
// In real files under test: exact 324% vs estimated 299%
// — only the exact number reveals the file is over the limit.
}
Always use calculate_exact_tac to validate an ink limit. The estimate is
for a quick read when colors are not declared.
prepress::validate_tac_limits([limit])
True when every page is within the limit (default 300). Uses the render-based estimate.
check "Limit by paper profile" {
// newsprint takes far less ink than coated stock
assert prepress::validate_tac_limits(240),
"exceeds the 240% limit for newsprint"
}
prepress::calculate_ink_coverage([page])
Average ink coverage (%) — an indicator of consumption, not of smear risk.
check "Ink consumption" {
average = prepress::calculate_ink_coverage()
print("average coverage:", average, "%")
// Heavily covered pages make the print run more expensive
assert average < 200, "high average coverage: #{average}%"
}
prepress::calculate_tac_by_region(page, region)
[max_tac, average_coverage] within a specific area.
check "Ink on the fold" {
// Too much ink on the fold cracks during finishing
fold = region(290, 0, 15, 842, "center fold")
measured = prepress::calculate_tac_by_region(1, fold)
assert measured.first() < 240,
"TAC of #{measured.first()}% on the fold (max 240%)"
print("average on the fold:", measured.last(), "%")
}
6.2 Colors and separations
prepress::detect_spot_colors()
Lists special inks (Pantone, varnish, die lines) declared as Separation or
DeviceN.
The reserved separations
AllandNoneare excluded —Allis a registration mark, not an ink.
check "Special colors" {
spots = prepress::detect_spot_colors()
print("special inks:", spots.join(", "))
// A job quoted as 4-color cannot carry extra inks
assert spots.length == 0,
"file uses an unquoted special ink: #{spots.join(", ")}"
}
check "Varnish expected" {
// When the special ink IS expected
spots = prepress::detect_spot_colors()
assert spots.contains("Varnish"),
"the spot varnish layer is missing"
}
prepress::detect_color_mode()
Returns "CMYK", "RGB", "Mixed", "None" or "Other", based on the images.
check "Document color mode" {
mode = prepress::detect_color_mode()
assert mode == "CMYK" || mode == "None",
"document is #{mode} — offset printing requires CMYK"
}
prepress::validate_color_space(space)
True when all images are in the given space.
check "Everything in CMYK" {
assert prepress::validate_color_space("DeviceCMYK"),
"there are images outside CMYK"
}
prepress::compare_colors_delta_e(color_a, color_b)
Perceptual difference between two colors (Delta-E CIE76). Colors are lists: 4 values = CMYK, 3 = RGB, 1 = gray.
Rule of thumb: ΔE below 1 is imperceptible; up to 3 is acceptable in print; above 5 is visibly different.
check "Brand color" {
// The approved corporate blue
brand = [1.0, 0.6, 0.0, 0.1]
used = [1.0, 0.62, 0.0, 0.12]
difference = prepress::compare_colors_delta_e(brand, used)
assert difference < 3.0,
"brand color out of tolerance (ΔE #{difference})"
}
prepress::detect_rich_black()
True when black is built from several inks (K ≥ 60% with C+M+Y ≥ 20%).
check "Correct black for text" {
// Small text in rich black shows registration wobble
assert !prepress::detect_rich_black(),
"rich black found — use flat black (0/0/0/100) for text"
}
prepress::validate_overprint_settings()
True when no overprint is enabled.
check "Overprint" {
// Accidental overprint makes elements disappear on press
assert prepress::validate_overprint_settings(),
"overprint is enabled — verify that it is intentional"
}
prepress::validate_output_intent([name])
Without an argument: true when an Output Intent is declared. With a name: true when the intent contains that text.
check "Output profile" {
assert prepress::validate_output_intent(),
"PDF has no Output Intent — the shop cannot know the target color profile"
// Requiring a specific profile
assert prepress::validate_output_intent("Coated FOGRA39"),
"Output Intent differs from the shop standard"
}
prepress::check_rendering_intent([expected])
Without an argument: lists the declared intents. With one: true when all of them match.
check "Rendering intent" {
print("intents in the file:", prepress::check_rendering_intent().join(", "))
assert prepress::check_rendering_intent("RelativeColorimetric"),
"rendering intent differs from the production standard"
}
6.3 Strokes and thin lines
Very thin lines disappear in print or come out uneven.
prepress::detect_hairlines([limit])
True when a stroke falls below the limit (default 0.25 pt).
check "No hairlines" {
assert !prepress::detect_hairlines(0.25),
"there are strokes below 0.25 pt — they will disappear in print"
}
prepress::detect_hairlines_exact()
True when a stroke has zero width — the classic PostScript hairline, which the press renders at the device minimum (unpredictable).
check "Zero-width stroke" {
assert !prepress::detect_hairlines_exact(),
"there is a stroke with 0 width — set a real thickness"
}
prepress::detect_fine_lines([limit])
Like detect_hairlines, with a wider limit (default 1 pt).
check "Thin lines over color" {
// Over a background, lines below 1pt vanish
assert !prepress::detect_fine_lines(1.0),
"there are lines below 1 pt"
}
prepress::validate_minimum_stroke_width(minimum)
True when no stroke falls below the required minimum.
check "Minimum thickness in the contract" {
assert prepress::validate_minimum_stroke_width(0.5),
"the shop contract requires strokes of at least 0.5 pt"
}
6.4 Fonts
prepress::list_fonts()
Names of the fonts in use.
check "Font inventory" {
fonts = prepress::list_fonts()
print("fonts:", fonts.join(", "))
assert fonts.length <= 8,
"#{fonts.length} different fonts — inconsistent design?"
}
prepress::validate_font_embedding()
True when every font is embedded.
check "Embedded fonts" {
assert prepress::validate_font_embedding(),
"there are non-embedded fonts — the text will change at the RIP"
}
prepress::detect_text_substitution()
Lists the non-embedded fonts, which the reader will substitute.
check "Which fonts are missing" {
missing = prepress::detect_text_substitution()
assert missing.length == 0,
"fonts not embedded: #{missing.join(", ")}"
}
prepress::detect_missing_glyphs()
Lists fonts with no widths table — the reader has to guess the metrics, which misaligns the text.
check "Complete metrics" {
problems = prepress::detect_missing_glyphs()
assert problems.length == 0,
"fonts without a widths table: #{problems.join(", ")}"
}
prepress::subset_fonts()
True when every embedded font is subset (only the glyphs actually used), which keeps the file lean.
check "Fonts are subset" {
assert prepress::subset_fonts(),
"a full font is embedded — the file is larger than it needs to be"
}
prepress::check_font_licensing()
Lists fonts that carry licensing risk: Type3 or non-embedded.
check "Licensing" {
risky = prepress::check_font_licensing()
assert risky.length == 0,
"fonts with licensing risk: #{risky.join(", ")}"
}
prepress::validate_font_size([minimum])
True when no text falls below the minimum size (default 6 pt).
check "Legibility" {
// Regulators require minimum body size on package inserts;
// contracts often have similar requirements
assert prepress::validate_font_size(6),
"there is text below 6 pt — illegible once printed"
}
6.5 Pages and boxes
PDF boxes define the working areas: MediaBox (sheet), BleedBox (bleed), TrimBox (final trim), CropBox (viewing), ArtBox (content).
prepress::get_page_size([page])
[width, height] in points.
check "Format" {
size = prepress::get_page_size(1)
print("page 1:", size.first(), "x", size.last(), "pt")
assert abs(size.first() - 595.0) < 5, "width is outside A4"
}
prepress::get_page_boxes([page])
List of the defined boxes, formatted as text.
check "Boxes on the first page" {
prepress::get_page_boxes(1).each { |box| print(box) }
// Sample output:
// MediaBox: [0, 0, 467.2, 665.6]
// TrimBox: [35.2, 35.2, 432, 630.4]
}
validate_media_box(), validate_trim_box(), validate_bleed_box()
True when the box exists on every page.
check "Boxes required for printing" {
require prepress::validate_media_box()
assert prepress::validate_trim_box(),
"no TrimBox — the shop cannot know where to trim"
assert prepress::validate_bleed_box(),
"no BleedBox — no bleed area is defined"
}
prepress::check_page_geometry([margin])
True when the BleedBox exceeds the TrimBox by the given margin on all sides, on every page. Default: 3 mm.
check "Sufficient bleed" {
// Use unit literals: readable, and conversion is automatic
assert prepress::check_page_geometry(3mm),
"bleed smaller than 3 mm on some page"
// Packaging shops usually demand more
assert prepress::check_page_geometry(5mm),
"this shop requires 5 mm of bleed"
}
6.6 Complete example
// offset_magazine.pdfl — full preflight for offset printing
// Usage: pdfl run offset_magazine.pdfl magazine.pdf --output html --output-file report.html
profile "offset-magazine" {
const TAC_LIMIT = 300%
const BLEED = 3mm
const MIN_DPI = 300
check "Ink coverage" tags: ["prepress", "colors"] {
// Always the exact figure when validating a limit
doc.pages.each { |page|
tac = prepress::calculate_exact_tac(page.number)
assert tac <= TAC_LIMIT,
"page #{page.number}: #{tac}% ink (limit #{TAC_LIMIT}%)"
}
print("average coverage:", prepress::calculate_ink_coverage(), "%")
}
check "Colors" tags: ["prepress", "colors"] {
assert prepress::detect_color_mode() != "RGB", "document is in RGB"
spots = prepress::detect_spot_colors()
assert spots.length == 0, "unquoted special ink: #{spots.join(", ")}"
assert !prepress::detect_rich_black(), "rich black in text"
assert prepress::validate_output_intent(), "no Output Intent"
}
check "Fonts" tags: ["fonts"] {
missing = prepress::detect_text_substitution()
assert missing.length == 0, "fonts not embedded: #{missing.join(", ")}"
assert prepress::validate_font_size(6), "text below 6 pt"
print("fonts:", prepress::list_fonts().join(", "))
}
check "Strokes" tags: ["prepress"] {
assert !prepress::detect_hairlines(0.25), "strokes below 0.25 pt"
assert !prepress::detect_hairlines_exact(), "stroke with 0 width"
}
check "Geometry" tags: ["prepress", "boxes"] {
require prepress::validate_trim_box()
require prepress::validate_bleed_box()
assert prepress::check_page_geometry(BLEED),
"bleed smaller than 3 mm"
}
check "Images" tags: ["images"] {
doc.images.each { |img|
assert img.dpi >= MIN_DPI,
"image on page #{img.page_number}: #{round(img.dpi)} DPI"
assert img.color_space != "DeviceRGB",
"RGB image on page #{img.page_number}"
}
}
}