Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

PHP cannot render HTML into a PDF by itself. Without installing a PDF package or running a rendering service, the practical solution is to generate a print-ready HTML page in PHP and let the visitor use the browser’s print dialog to choose a PDF destination. This creates a user-driven PDF, not an automatically generated file on your server.

If your application must return a PDF automatically from an HTTP request, you need a renderer—such as a PHP HTML-to-PDF library, a headless browser, or a managed conversion API. That requirement is different from the strict no-library workflow covered here.

What “without a library” means in PHP

PHP’s print construct writes a string to the response. It does not interpret CSS, lay out pages, rasterize fonts, or create PDF objects. Sending HTML with echo or print therefore produces an HTML document, not a PDF.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The browser is the component that renders the HTML. JavaScript’s window.print() then opens the print dialog for the current document. When the user selects “Save as PDF” or an equivalent PDF destination, the browser creates the file locally.

  • Works without a PHP PDF dependency: PHP serves HTML and CSS only.
  • Requires user interaction: the visitor confirms the print dialog and destination.
  • Not a server-side PDF: PHP cannot silently attach or store the resulting PDF through this method.
  • Output varies: browser settings, installed fonts, paper choices and print-engine support affect the result.

Build a print-ready PHP page

The following complete example serves a report, hides the print button on paper/PDF, defines an A4 portrait page and provides a visible action for the user.

<?php
$title = 'Quarterly report';
$customer = 'Example customer';
$body = 'This report was generated by PHP and prepared for printing.';

// Escape values before placing untrusted data in HTML.
$titleEsc = htmlspecialchars($title, ENT_QUOTES, 'UTF-8');
$customerEsc = htmlspecialchars($customer, ENT_QUOTES, 'UTF-8');
$bodyEsc = htmlspecialchars($body, ENT_QUOTES, 'UTF-8');

header('Content-Type: text/html; charset=utf-8');
?><!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title><?= $titleEsc ?></title>
  <style>
    * { box-sizing: border-box; }
    body {
      margin: 0;
      color: #222;
      background: #eee;
      font: 16px/1.5 system-ui, sans-serif;
    }
    .sheet {
      max-width: 210mm;
      min-height: 297mm;
      margin: 1rem auto;
      padding: 18mm;
      background: #fff;
    }
    .no-print { margin-top: 2rem; }

    @page {
      size: A4 portrait;
      margin: 18mm;
    }
    @media print {
      body { background: #fff; color: #000; }
      .sheet {
        max-width: none;
        min-height: auto;
        margin: 0;
        padding: 0;
      }
      .no-print { display: none !important; }
      a { color: #000; text-decoration: none; }
      h1, h2, h3 { break-after: avoid; }
      table, figure, img { break-inside: avoid; }
      .page-break { break-before: page; }
    }
  </style>
</head>
<body>
  <main class="sheet">
    <h1><?= $titleEsc ?></h1>
    <p><strong>Customer:</strong> <?= $customerEsc ?></p>
    <p><?= $bodyEsc ?></p>
    <button class="no-print" type="button" onclick="window.print()">
      Print or save as PDF
    </button>
  </main>
</body>
</html>

Save the file as, for example, report.php, serve it through PHP, and open its URL in a browser. The response must remain text/html; changing the header to application/pdf does not convert the content.

Prepare the layout with print CSS

Use a normal screen layout for reading and a print-specific layout for paper or PDF. MDN’s printing guide documents @media print and print-only styles.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Remove interface elements

Give navigation, forms, modal controls, advertisements and the print button a class such as no-print, then hide that class in @media print. Keep the content needed in the report; do not hide a parent element that contains required text.

Set page size and margins

The @page rule can specify paper size, orientation and margins:

@page {
  size: Letter landscape;
  margin: 12mm 15mm;
}

Use A4 for many international workflows or Letter where that is the expected paper size. A CSS page size is a request to the print engine, not a guarantee that the user’s selected destination will honor every property.

Control page breaks

Modern break properties are clearer than the older page-break-* names:

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
.chapter { break-before: page; }
.keep-together { break-inside: avoid; }
h2 { break-after: avoid; }

Apply break-inside: avoid selectively. Large tables or long blocks cannot always fit on one page, so forcing them together may leave excessive blank space or still be ignored by the browser.

Make colors and links useful on paper

Print backgrounds may be disabled by the user’s browser. Ensure text remains readable without background colors, use sufficient contrast, and consider removing link underlines only when the destination is not needed. For reports where URLs matter, print them explicitly rather than relying on color alone.

Use the browser print flow

  1. Open the PHP page in the target browser.
  2. Wait for the document and images to finish loading.
  3. Click Print or save as PDF, or use the browser’s print shortcut.
  4. In the dialog, choose the browser or operating system’s PDF destination if one is offered.
  5. Set paper size, orientation, margins, scale, headers/footers and background-graphics options as required.
  6. Preview every page, choose a filename and save the PDF.

window.print() opens a user-facing dialog; it does not provide a JavaScript API for selecting a destination or silently downloading a file. Browsers may wait for the document to finish loading before displaying the dialog, but asynchronous application data should still be rendered before the user clicks the button.

Handle dynamic and untrusted content safely

Escape text values

Use htmlspecialchars($value, ENT_QUOTES, 'UTF-8') for text inserted into HTML. Do not concatenate raw request parameters into markup. For rich content that intentionally contains HTML, use a trusted template and an allowlist sanitizer rather than treating the value as plain text.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Wait for data before printing

Fetch database data on the server where possible so the first HTML response is complete. If the page fills content with JavaScript, disable the print action until the request finishes and show a loading state. Otherwise, a user can create a PDF containing an empty chart or incomplete table.

Manage images and fonts

Use stable URLs, dimensions and alt text. Cross-origin resources, blocked requests, web-font loading and browser privacy settings can change the appearance. A print preview on one machine does not establish identical output on another.

Choose the right route

Requirement Suitable route Trade-off
A person can open a page and choose Save as PDF PHP-generated HTML, print CSS and window.print() No PHP PDF package, but the person completes the dialog and controls the destination.
A request must automatically return a PDF HTML-to-PDF library, headless browser or managed conversion service Supports automation, but no longer meets a strict no-library requirement.
Layout depends on advanced browser CSS or JavaScript Evaluate a browser-based renderer and test actual documents Different engines support different CSS and JavaScript behavior; there is no universal best renderer established here.

If a renderer is acceptable, documented alternatives include Dompdf, which describes PHP HTML-to-PDF support for most CSS 2.1 and some CSS3; PHP’s wkhtmltox documentation, describing an HTML renderer based on QtWebKit; and chrome-php/chrome, which documents PDF output through headless Chrome/Chromium. Their documentation describes different approaches, not a comparative test proving one is best for every document.

Troubleshooting

The button shows HTML source or downloads an HTML file

Check that the PHP response is Content-Type: text/html; charset=utf-8 and that the file is executed by a PHP-capable server. Do not send application/pdf unless the response actually contains PDF bytes.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The print button appears in the PDF

Confirm the button has the no-print class and that @media print { .no-print { display: none !important; } } is loaded. Cached CSS or a selector overridden later in the stylesheet can also cause this symptom.

Margins or paper orientation are wrong

Check both @page and the print dialog. User-selected dialog settings can override CSS, and some destinations expose different options. Test the exact browser and PDF destination used by your audience.

Content is cut off or overlaps

Look for fixed heights, absolute positioning, viewport-only widths and elements that cannot shrink. Remove screen-only constraints inside print media, use flexible widths, and add deliberate break rules around large sections.

Images or fonts are missing

Verify that resource URLs are reachable from the browser, use HTTPS where the page is HTTPS, and wait for assets to load before calling window.print(). Provide fallback fonts and avoid relying on background images for essential information.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The PDF is blank or incomplete

Inspect the page before printing. A JavaScript error, a failed API request or a print action triggered before rendering can leave an empty document. Render required data in the initial PHP response when possible and log client-side failures.

Performance, reliability and cost considerations

The no-library method shifts rendering work to the visitor’s browser. Server cost is generally the cost of producing and serving HTML, CSS, images and data; there is no server-side PDF rendering job. Reliability depends on the user’s browser, print settings, installed fonts, network access to assets and willingness to complete the dialog.

It is a good fit for internal tools, invoices or reports where a person is present. It is not suitable for scheduled exports, emailed attachments, API consumers, archival pipelines or a workflow that must produce byte-identical PDFs. Those cases require a renderer or conversion service and should be tested with representative documents, including long tables, page breaks, images, non-Latin text and authenticated resources.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

When the goal is a clean screenshot or PDF of a web page rather than a PHP-generated document, ScreenshotNeo provides a single-call API. It accepts cookie and consent banners like a visitor, removes more than 60 known consent platforms plus newsletter popups and chat widgets before capture, and reports whether a response was a clean shot, cache hit or failure. Bot checks/CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For a PDF capture, use the documented API options at ScreenshotNeo’s documentation. The following examples target a public page; replace the URL and credentials with your own.

cURL

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python

import requests
r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const bytes = new Uint8Array(await res.arrayBuffer());
await Bun.write('shot.webp', bytes);

ScreenshotNeo also offers PDF paper size, margins, orientation and page ranges; full-page capture with lazy images loaded; CSS-selector element capture; custom JavaScript and CSS; waits for selectors, delays or network idle; custom headers, cookies, user agents and authorization; blocking controls; timezone and geolocation; caching with a chosen TTL; signed links; asynchronous jobs with signed webhooks; bulk capture of up to 100 URLs per call; a usage API; and an OpenAPI specification. Its MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients.

The Free plan includes 1,000 shots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is available on every plan, and yearly billing provides two months free. Create a free ScreenshotNeo account to try it without a card.

Frequently Asked Questions

Can PHP’s print() function create a PDF file?

No. PHP’s print() outputs characters in the HTTP response. A browser or another rendering engine must turn the HTML into PDF output.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Can window.print() save a PDF without showing a dialog?

No. It opens the browser’s print dialog. The user or an externally controlled browser must choose the PDF destination.

Will @page force every browser to use A4 and the specified margins?

No. @page requests page dimensions and margins, but print destinations and user settings can override or partially support them.

What should I use for unattended PDF generation?

Use a server-side HTML renderer, headless browser or managed conversion service, then test it with the CSS, fonts, images and JavaScript your documents require.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.