setPageSize('A4')->setOrientation('portrait'); * $client->convertUrlToFile('https://example.com', 'example.pdf'); */ namespace Pdfly; class Error extends \Exception { } class HtmlToPdfClient { private array $fields = []; private int $timeout = 120; public function __construct(private string $apiKey, private string $endpoint = 'https://pdfly.host/v1/convert') { } // ---- Conversions ------------------------------------------------------ /** @return string PDF bytes */ public function convertUrl(string $url): string { return $this->request(['url' => $url]); } public function convertUrlToFile(string $url, string $filePath): void { $this->save($this->convertUrl($url), $filePath); } /** @return string PDF bytes */ public function convertString(string $html): string { return $this->request(['text' => $html]); } public function convertStringToFile(string $html, string $filePath): void { $this->save($this->convertString($html), $filePath); } /** Converts a local .html file (asset paths inside it must be absolute URLs). */ public function convertFile(string $htmlFilePath): string { $html = @file_get_contents($htmlFilePath); if ($html === false) { throw new Error("Cannot read $htmlFilePath", 0); } return $this->convertString($html); } public function convertFileToFile(string $htmlFilePath, string $filePath): void { $this->save($this->convertFile($htmlFilePath), $filePath); } /** Sends the PDF straight to the browser. */ public function convertUrlToBrowser(string $url, string $filename = 'document.pdf', bool $download = false): void { $this->output($this->convertUrl($url), $filename, $download); } public function convertStringToBrowser(string $html, string $filename = 'document.pdf', bool $download = false): void { $this->output($this->convertString($html), $filename, $download); } // ---- Options (all return $this, so they can be chained) --------------- /** A0–A6, Letter, Legal, Tabloid, Ledger. Default A4. */ public function setPageSize(string $size): static { return $this->set('page_size', $size); } /** e.g. "210mm", "8.5in" */ public function setPageWidth(string $width): static { return $this->set('page_width', $width); } public function setPageHeight(string $height): static { return $this->set('page_height', $height); } public function setPageDimensions(string $width, string $height): static { return $this->setPageWidth($width)->setPageHeight($height); } /** "portrait" or "landscape" */ public function setOrientation(string $orientation): static { return $this->set('orientation', $orientation); } /** e.g. "10mm", "0.5in". Default 0.4in. */ public function setMarginTop(string $margin): static { return $this->set('margin_top', $margin); } public function setMarginRight(string $margin): static { return $this->set('margin_right', $margin); } public function setMarginBottom(string $margin): static { return $this->set('margin_bottom', $margin); } public function setMarginLeft(string $margin): static { return $this->set('margin_left', $margin); } public function setPageMargins(string $top, string $right, string $bottom, string $left): static { return $this->setMarginTop($top)->setMarginRight($right)->setMarginBottom($bottom)->setMarginLeft($left); } public function setNoMargins(bool $value = true): static { return $this->set('no_margins', $value); } /** HTML for every page header/footer. Use and . */ public function setHeaderHtml(string $html): static { return $this->set('header_html', $html); } public function setFooterHtml(string $html): static { return $this->set('footer_html', $html); } public function setNoBackground(bool $value = true): static { return $this->set('no_background', $value); } /** Use the page's @media print CSS instead of screen CSS. */ public function setUsePrintMedia(bool $value = true): static { return $this->set('use_print_media', $value); } /** Wait this many milliseconds after load before printing (max 30000). */ public function setJavascriptDelay(int $ms): static { return $this->set('javascript_delay', $ms); } /** Wait until this CSS selector exists on the page before printing. */ public function setWaitForElement(string $selector): static { return $this->set('wait_for_element', $selector); } /** Percent, 10–200. Default 100. */ public function setScaleFactor(int $percent): static { return $this->set('scale_factor', $percent); } /** e.g. "1-3,5" */ public function setPrintPageRange(string $range): static { return $this->set('print_page_range', $range); } /** Put the whole page on one tall PDF page. */ public function setSinglePage(bool $value = true): static { return $this->set('single_page', $value); } /** Let CSS @page { size: ... } decide the page size. */ public function setCssPageSize(bool $value = true): static { return $this->set('css_page_size', $value); } public function setTimeout(int $seconds): static { $this->timeout = $seconds; return $this; } /** Pass any API parameter directly. */ public function set(string $name, string|int|bool|null $value): static { if ($value === null) { unset($this->fields[$name]); } else { $this->fields[$name] = is_bool($value) ? ($value ? 'true' : 'false') : (string) $value; } return $this; } // ---- Internals -------------------------------------------------------- private function request(array $source): string { $ch = curl_init($this->endpoint); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => http_build_query($source + $this->fields), CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $this->apiKey], CURLOPT_RETURNTRANSFER => true, CURLOPT_CONNECTTIMEOUT => 10, CURLOPT_TIMEOUT => $this->timeout, ]); $body = curl_exec($ch); $status = (int) curl_getinfo($ch, CURLINFO_RESPONSE_CODE); if ($body === false) { throw new Error('pdfly: ' . curl_error($ch), 0); } if ($status !== 200) { $json = json_decode($body, true); throw new Error('pdfly: ' . ($json['error']['message'] ?? "HTTP $status"), $status); } return $body; } private function save(string $pdf, string $filePath): void { if (@file_put_contents($filePath, $pdf) === false) { throw new Error("Cannot write $filePath", 0); } } private function output(string $pdf, string $filename, bool $download): void { header('Content-Type: application/pdf'); header('Content-Length: ' . strlen($pdf)); header('Content-Disposition: ' . ($download ? 'attachment' : 'inline') . '; filename="' . addcslashes($filename, '"\\') . '"'); echo $pdf; } }