report-print-pdf

Installation
SKILL.md

Platform Notes

  • Optional helper plugins may help in some environments, but they must not be treated as required for this skill.

Report Export (PDF + Print)

Acknowledgement: Shared by Peter Bamuhigire, techguypeter.com, +256 784 464178.

Use When

  • Guidance for building report templates that serve both mPDF exports and the browser-based print workflow, including the auto-print standard introduced in the report-printing-style guide.
  • The task needs reusable judgment, domain constraints, or a proven workflow rather than ad hoc advice.

Do Not Use When

  • The task is unrelated to report-print-pdf or would be better handled by a more specific companion skill.
  • The request only needs a trivial answer and none of this skill's constraints or references materially help.

Required Inputs

  • Gather relevant project context, constraints, and the concrete problem to solve.
  • Confirm the desired deliverable: design, code, review, migration plan, audit, or documentation.

Workflow

  • Read this SKILL.md first, then load only the referenced deep-dive files that are necessary for the task.
  • Apply the ordered guidance, checklists, and decision rules in this skill instead of cherry-picking isolated snippets.
  • Produce the deliverable with assumptions, risks, and follow-up work made explicit when they matter.

Quality Standards

  • Keep outputs execution-oriented, concise, and aligned with the repository's baseline engineering standards.
  • Preserve compatibility with existing project conventions unless the skill explicitly requires a stronger standard.
  • Prefer deterministic, reviewable steps over vague advice or tool-specific magic.

Anti-Patterns

  • Treating examples as copy-paste truth without checking fit, constraints, or failure modes.
  • Loading every reference file by default instead of using progressive disclosure.

Outputs

  • A concrete result that fits the task: implementation guidance, review findings, architecture decisions, templates, or generated artifacts.
  • Clear assumptions, tradeoffs, or unresolved gaps when the task cannot be completed from available context alone.
  • References used, companion skills, or follow-up actions when they materially improve execution.

Evidence Produced

Category Artifact Format Example
Correctness Report template test plan Markdown doc covering mPDF export, browser print parity, page break behaviour, and watermark rendering docs/reports/template-tests.md

References

  • Use the links and companion skills already referenced in this file when deeper context is needed.

Build a single, minimal HTML report that renders consistently in mPDF and browser print (HTML → print dialog). Keep typography compact, tables pagination-friendly, and metadata consistent across outputs.

Security Baseline (Required)

Always load and apply the Vibe Security Skill when reports touch user input, filters, file paths, or authentication. Treat output encoding and access control as mandatory.

Cross-Platform: Reports deploy to Windows dev, Ubuntu staging, and Debian production. Use forward-slash paths (/) for file references. Use sys_get_temp_dir() for temporary PDF files. Ensure font paths work on both OS (use relative paths from project root).

When to Use

  • Generating PDF reports with mPDF in PHP
  • Printing HTML reports via browser print dialog
  • Needing a shared HTML layout for both PDF and print
  • Requiring strict number/date formatting and tight spacing
  • Producing multi-page tables with repeating headers

Core Workflow

  1. Prepare data and metadata

    • Resolve organization name, address, and logo path
    • Compute report title and optional period label (e.g., 23 Aug, 2025)
    • Resolve “Printed By” from logged-in user name
    • Format “Printed On” as d F Y, h:i A (e.g., 23 September 2025, 05:36 PM)
    • Never render raw SQL timestamps (e.g., 2026-01-25 00:00:00). Always format dates as d M Y or d F Y, and include time only when explicitly required.
    • When building standalone PHP scripts outside of a namespace, never add use DateTime (it has no effect) or other global symbol use statements; just instantiate the built-in class via new \DateTime() so your files stay warning-free.
  2. Build minimal HTML (shared by PDF + Print)

    • Compact header: logo left, org name/address center/left, report title + period right
    • Tight vertical rhythm: 6–12px gaps, no large vertical spacing
    • DejaVu Sans for clarity at small sizes
    • Table with thead/tbody and subtle borders
  3. Render with mPDF

    • Use small margins and a clean footer
    • Keep header inside body HTML (set empty mPDF header)
  4. Render with browser print

    • API returns full HTML (text/html)
    • UI opens new window/tab, writes HTML, calls window.print() when ready
    • Print CSS mirrors the PDF layout (compact header, repeating headers, small footer)

Formatting Helpers

final class ReportFormatting {
    public static function formatDateShort(?string $value): string {
        if (empty($value)) {
            return '-';
        }
        return (new DateTime(substr($value, 0, 10)))->format('d M Y');
    }

    public static function formatDateLong(?string $value): string {
        if (empty($value)) {
            return '-';
        }
        return (new DateTime(substr($value, 0, 10)))->format('d F Y');
    }

    public static function formatPeriod(?string $startDate, ?string $endDate): string {
        if (empty($startDate) && empty($endDate)) {
            return '';
        }
        $start = $startDate ? (new DateTime($startDate))->format('d M, Y') : '';
        $end = $endDate ? (new DateTime($endDate))->format('d M, Y') : '';
        if ($start && $end) {
            return $start === $end ? $start : "$start - $end";
        }
        return $start ?: $end;
    }

    public static function formatPrintedOn(DateTime $date): string {
        return $date->format('d F Y, h:i A');
    }

    public static function formatNumber($value): string {
        if ($value === null || $value === '') {
            return '0';
        }
        $num = (float)$value;
        $isWhole = abs($num - round($num)) < 0.00001;
        return $isWhole
            ? number_format($num, 0, '.', ',')
            : number_format($num, 2, '.', ',');
    }
}

HTML Template (Minimalistic + Clean)

final class ReportHtmlTemplate {
    public static function build(
        array $meta,
        string $tableHtml,
        string $summaryHtml = '',
        bool $includeFooter = false,
        string $printedBy = '',
        string $printedOn = ''
    ): string {
        $orgName = htmlspecialchars($meta['org_name'] ?? '');
        $orgAddress = htmlspecialchars($meta['org_address'] ?? '');
        $reportTitle = htmlspecialchars($meta['title'] ?? '');
        $periodLabel = htmlspecialchars($meta['period'] ?? '');
        $logoPath = htmlspecialchars($meta['logo_path'] ?? '');

        $periodHtml = $periodLabel !== ''
            ? '<div class="period">Period: ' . $periodLabel . '</div>'
            : '';

        $footerHtml = $includeFooter
            ? '<div class="print-footer">'
                . '<div class="print-left">Printed By: ' . htmlspecialchars($printedBy) . '</div>'
                . '<div class="print-right">Printed On ' . htmlspecialchars($printedOn) . '</div>'
            . '</div>'
            : '';

        return <<<HTML
<style>
    body { font-family: dejavu sans; font-size: 10px; color: #111; }
    .report-header { width: 100%; border-bottom: 1px solid #e6e6e6; padding-bottom: 6px; }
    .header-row { display: table; width: 100%; }
    .header-cell { display: table-cell; vertical-align: middle; }
    .logo { width: 54px; height: 54px; }
    .org-name { font-size: 13px; font-weight: bold; margin-bottom: 2px; }
    .org-address { font-size: 9px; color: #555; }
    .report-title { font-size: 12px; font-weight: 600; text-align: right; }
    .period { font-size: 9px; color: #555; text-align: right; margin-top: 2px; }
    .section-gap { margin-top: 8px; }

    table { width: 100%; border-collapse: collapse; margin-top: 6px; }
    thead { display: table-header-group; }
    th { background: #f7f7f7; font-weight: 600; border: 1px solid #e5e5e5; padding: 5px; }
    td { border: 1px solid #e5e5e5; padding: 5px; }
    .text-right { text-align: right; }
    .text-center { text-align: center; }

    .print-footer { width: 100%; font-size: 7px; color: #555; margin-top: 8px; }
    .print-left { float: left; }
    .print-right { float: right; }

    @media print {
        body { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
        .print-footer { position: fixed; bottom: 8px; left: 0; right: 0; }
        .section-gap { margin-top: 6px; }
        tr, td, th { page-break-inside: avoid; }
    }
</style>

<div class="report-header">
    <div class="header-row">
        <div class="header-cell" style="width: 60px;">
            <img class="logo" src="{$logoPath}" alt="Logo" />
        </div>
        <div class="header-cell">
            <div class="org-name">{$orgName}</div>
            <div class="org-address">{$orgAddress}</div>
        </div>
        <div class="header-cell" style="text-align: right;">
            <div class="report-title">{$reportTitle}</div>
            {$periodHtml}
        </div>
    </div>
</div>

<div class="section-gap">{$summaryHtml}</div>
{$tableHtml}
{$footerHtml}
HTML;
    }
}

Table Layout Rules (Multi-Page Friendly)

  • Use a proper thead + tbody
  • Set thead to display: table-header-group for repeating headers
  • Keep padding tight (4–6px)
  • Use thin borders and subtle header shading
  • Avoid breaking rows across pages (page-break-inside: avoid)

mPDF Wrapper Setup

$mpdf = new \Mpdf\Mpdf([
    'format' => 'A4',
    'margin_left' => 10,
    'margin_right' => 10,
    'margin_top' => 38,
    'margin_bottom' => 18,
    'default_font' => 'dejavusans'
]);

$footerHtml = '<div style="width:100%; font-size:7px; color:#555;">'
    . '<div style="float:left;">Printed By: ' . htmlspecialchars($printedBy) . '</div>'
    . '<div style="float:right;">Printed On ' . ReportFormatting::formatPrintedOn(new DateTime()) . '</div>'
    . '</div>';

$mpdf->SetHTMLHeader('');
$mpdf->SetHTMLFooter($footerHtml);
$mpdf->WriteHTML(ReportHtmlTemplate::build($meta, $tableHtml, $summaryHtml));
$mpdf->Output($fileName, 'I');

Print Flow (HTML → Print Dialog)

  1. UI calls report API endpoint with filters (date range, status, warehouse, etc.)
  2. API renders full HTML report and returns it as text/html
  3. UI opens a new window/tab, writes HTML, waits for load, then calls window.print()
  4. Print CSS mirrors the PDF layout (compact header, repeating thead, small footer)
  5. User prints or saves as PDF from the browser dialog

Auto-Print Helper

  • When building dedicated print views (e.g., *-print.php), append this snippet before </body> and keep docs/report-printing-standard.md in sync:
<script>
  window.addEventListener("DOMContentLoaded", function () {
    setTimeout(function () {
      window.print();
    }, 400);
  });
</script>
  • The short timeout allows logos, fonts, and computed layout to settle before the native dialog grabs the page.
  • Keep no-print helpers for reprint actions so the dialog can be reopened without a full refresh.

Browser Print Example (JS)

async function openReportPrint(url, params) {
  const query = new URLSearchParams(params).toString();
  const html = await fetch(`${url}?${query}`, { credentials: "include" }).then(
    (r) => r.text(),
  );

  const printWindow = window.open("", "_blank");
  printWindow.document.open();
  printWindow.document.write(html);
  printWindow.document.close();

  printWindow.onload = () => {
    printWindow.focus();
    printWindow.print();
  };
}

API Endpoint Example (PHP)

header('Content-Type: text/html; charset=utf-8');

$meta = [
    'org_name' => $org->name,
    'org_address' => $org->address,
    'logo_path' => $org->logo_path,
    'title' => $reportTitle,
    'period' => ReportFormatting::formatPeriod($startDate, $endDate)
];

$printedOn = ReportFormatting::formatPrintedOn(new DateTime());
$printedBy = $currentUser->name;

echo ReportHtmlTemplate::build(
    $meta,
    $tableHtml,
    $summaryHtml,
    true,
    $printedBy,
    $printedOn
);

Visual Standards

  • Font: DejaVu Sans
  • Body text: 10–11px
  • Table text: 9–10px
  • Footer text: 7–8px
  • Spacing: 6–12px between header and content
  • Color: White background, subtle gray borders

Number Formatting Rules

  • Whole numbers → commas, no decimals
  • Non-whole numbers → commas, 2 decimals

Summary

Use this skill to produce consistent, clean, minimal PDF exports and browser-printed reports with the same HTML: compact header/footer, repeatable table headers, strict number/date formatting, and tight spacing for professional multi-page output.

Related skills
Installs
27
GitHub Stars
12
First Seen
Feb 28, 2026