Example E048 : page boxes prepress

The five page boxes (MediaBox, CropBox, BleedBox, TrimBox, ArtBox) with their BoxColorInfo guidelines drawn on the page.

The five page boxes (MediaBox, CropBox, BleedBox, TrimBox, ArtBox) with their BoxColorInfo guidelines drawn on the page.

PDF

Page 1 of E048_page_boxes_prepress.pdf

Source Code

<?php

declare(strict_types=1);

/**
 * E048_page_boxes_prepress.php
 *
 * Demonstrates Media/Crop/Bleed/Trim/Art page boxes for print-oriented
 * workflows, plus visible guides for quick prepress validation.
 *
 * @since       2026-05-01
 * @category    Library
 * @package     Pdf
 * @author      Nicola Asuni <info@tecnick.com>
 * @copyright   2002-2026 Nicola Asuni - Tecnick.com LTD
 * @license     https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
 * @link        https://github.com/tecnickcom/tc-lib-pdf
 *
 * This file is part of tc-lib-pdf software library.
 */

// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
// trusted paths that covers this package tree, so run the examples in place. To read assets
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
// parameter (see E047_remote_resources_security.php).

// NOTE: run make fonts in the project root to generate the dependencies and example fonts.

require __DIR__ . '/../vendor/autoload.php';

define('K_PATH_FONTS', (string) realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));

$pdf = new \Com\Tecnick\Pdf\Tcpdf(
    unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
    isunicode: true,
    subsetfont: false,
    compress: true,
    mode: \Com\Tecnick\Pdf\PdfConformance::None,
    objEncrypt: null,
);

$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 048');
$pdf->setTitle('Page Boxes and Prepress Guides');
$pdf->setKeywords('TCPDF tc-lib-pdf example page boxes MediaBox CropBox BleedBox TrimBox ArtBox');
$pdf->setPDFFilename('048_page_boxes_prepress.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
$pdf->enableDefaultPageContent();

$probe = false;
if (PHP_SAPI === 'cli') {
    $probe = (string) getenv('TC_EXAMPLE_PROBE') === '1';
} else {
    $probe = isset($_GET['probe']) && $_GET['probe'] === '1';
}

$titlefont = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 13);
$textfont = $pdf->font->insert($pdf->pon, 'helvetica', '', 9);

$pageBoxesMm = [
    'CropBox' => ['llx' => 5.0, 'lly' => 5.0, 'urx' => 205.0, 'ury' => 292.0],
    'BleedBox' => ['llx' => 3.0, 'lly' => 3.0, 'urx' => 207.0, 'ury' => 294.0],
    'TrimBox' => ['llx' => 10.0, 'lly' => 10.0, 'urx' => 200.0, 'ury' => 287.0],
    'ArtBox' => ['llx' => 20.0, 'lly' => 20.0, 'urx' => 190.0, 'ury' => 277.0],
];

$pageBoxesPt = [];
foreach ($pageBoxesMm as $name => $coords) {
    $pageBoxesPt[$name] = [
        'llx' => $pdf->toPoints((float) $coords['llx']),
        'lly' => $pdf->toPoints((float) $coords['lly']),
        'urx' => $pdf->toPoints((float) $coords['urx']),
        'ury' => $pdf->toPoints((float) $coords['ury']),
    ];
}

$page = $pdf->addPage([
    'format' => 'A4',
    'box' => $pageBoxesPt,
]);

$pdf->page->addContent($titlefont['out']);
$pdf->page->addContent($pdf->getTextCell(
    txt: 'Page Boxes: Media/Crop/Bleed/Trim/Art',
    posx: 25,
    posy: 14,
    width: 180,
    height: 0,
    offset: 0,
    linespace: 1,
    valign: \Com\Tecnick\Pdf\TextVAlign::Top,
    halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));

$pdf->page->addContent($textfont['out']);
$pdf->page->addContent($pdf->getTextCell(
    txt: "This sheet overlays the configured page boxes to verify prepress bounds.\n"
    . 'Coordinates are defined in page-box metadata and rendered here as guides.',
    posx: 25,
    posy: 24,
    width: 180,
    height: 0,
    offset: 0,
    linespace: 1.2,
    valign: \Com\Tecnick\Pdf\TextVAlign::Top,
    halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));

$styles = [
    'MediaBox' => ['all' => ['lineWidth' => 0.3, 'lineColor' => '#000000', 'dashArray' => []]],
    'CropBox' => ['all' => ['lineWidth' => 0.3, 'lineColor' => '#1f77b4', 'dashArray' => [2, 1]]],
    'BleedBox' => ['all' => ['lineWidth' => 0.3, 'lineColor' => '#d62728', 'dashArray' => [1, 1]]],
    'TrimBox' => ['all' => ['lineWidth' => 0.35, 'lineColor' => '#2ca02c', 'dashArray' => []]],
    'ArtBox' => ['all' => ['lineWidth' => 0.3, 'lineColor' => '#9467bd', 'dashArray' => [3, 1]]],
];

$boxes =
    [
        'MediaBox' => [
            'llx' => 0.0,
            'lly' => 0.0,
            'urx' => (float) $page['width'],
            'ury' => (float) $page['height'],
        ],
    ] + $pageBoxesMm;
$pageHeight = (float) $page['height'];

$labelY = 46.0;
foreach (['MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox'] as $name) {
    if (!isset($boxes[$name])) {
        continue;
    }

    $box = $boxes[$name];
    $x = (float) ($box['llx'] ?? 0.0);
    $y = $pageHeight - (float) ($box['ury'] ?? 0.0);
    $w = (float) ($box['urx'] ?? 0.0) - (float) ($box['llx'] ?? 0.0);
    $h = (float) ($box['ury'] ?? 0.0) - (float) ($box['lly'] ?? 0.0);

    $pdf->page->addContent($pdf->graph->getRect($x, $y, $w, $h, 'D', $styles[$name]));

    $pdf->page->addContent($pdf->getTextCell(
        txt: sprintf(
            '%s [%.1f, %.1f, %.1f, %.1f]',
            $name,
            (float) ($box['llx'] ?? 0.0),
            (float) ($box['lly'] ?? 0.0),
            (float) ($box['urx'] ?? 0.0),
            (float) ($box['ury'] ?? 0.0),
        ),
        posx: 25,
        posy: $labelY,
        width: 180,
        height: 0,
        offset: 0,
        linespace: 1,
        valign: \Com\Tecnick\Pdf\TextVAlign::Top,
        halign: \Com\Tecnick\Pdf\TextHAlign::Left,
    ));

    $labelY += 5.0;
}

$pdf->page->addContent($pdf->getTextCell(
    txt: "Tip: use TrimBox as the final cut size and keep critical artwork inside ArtBox.\n"
    . 'BleedBox extends beyond trim to avoid white edges after cutting.',
    posx: 25,
    posy: 72,
    width: 180,
    height: 0,
    offset: 0,
    linespace: 1.2,
    valign: \Com\Tecnick\Pdf\TextVAlign::Top,
    halign: \Com\Tecnick\Pdf\TextHAlign::Left,
));

$rawpdf = $pdf->getOutPDFString();

if ($probe) {
    $matches = [];
    $probeRows = [];
    $pointsPerUnit = $pdf->toPoints(1.0);
    $pattern = '/\\/(MediaBox|CropBox|BleedBox|TrimBox|ArtBox)\\s*\\[\\s*([0-9.+-]+)\\s+([0-9.+-]+)\\s+([0-9.+-]+)\\s+([0-9.+-]+)\\s*\\]/';
    if (\preg_match_all($pattern, $rawpdf, $matches, PREG_SET_ORDER) === false) {
        $probeRows[] = 'Page-box probe: unable to parse page-box dictionaries.';
    } else {
        foreach ($matches as $vals) {
            $name = (string) $vals[1];
            $llx = (float) $vals[2];
            $lly = (float) $vals[3];
            $urx = (float) $vals[4];
            $ury = (float) $vals[5];
            $wPt = $urx - $llx;
            $hPt = $ury - $lly;
            $probeRows[] = sprintf(
                '%s PDF=[%.3f %.3f %.3f %.3f] sizePt=%.3fx%.3f sizeUnit=%.3fx%.3f',
                $name,
                $llx,
                $lly,
                $urx,
                $ury,
                $wPt,
                $hPt,
                $pointsPerUnit > 0 ? $wPt / $pointsPerUnit : 0.0,
                $pointsPerUnit > 0 ? $hPt / $pointsPerUnit : 0.0,
            );
        }
    }

    $output = "\n[E048 page-box probe]\n" . implode("\n", $probeRows) . "\n";
    if (PHP_SAPI === 'cli') {
        fwrite(STDERR, $output);
    } else {
        error_log($output);
    }
}

$pdf->renderPDF(rawpdf: $rawpdf);