SetFillColor(13, 110, 253); $this->Rect(0, 0, $this->getPageWidth(), 2.5, 'F'); } public function Footer() { $margins = $this->getMargins(); $width = $this->getPageWidth() - $margins['left'] - $margins['right']; $this->SetY(-14); $this->SetDrawColor(222, 226, 230); $this->SetLineWidth(0.2); $this->Line($margins['left'], $this->GetY() - 1.5, $this->getPageWidth() - $margins['right'], $this->GetY() - 1.5); $this->SetFont('dejavusans', '', 7); $this->SetTextColor(108, 117, 125); $this->Cell($width * 0.75, 4, $this->footer_label, 0, 0, 'L'); $this->Cell($width * 0.25, 4, 'Page ' . $this->getAliasNumPage() . ' of ' . $this->getAliasNbPages(), 0, 0, 'R'); } public function Error($msg) { throw new Exception('TCPDF: ' . $msg); } } /** How the document's classes look in the PDF - TCPDF reads a style block. */ function contractPdfStyle() { return ''; } /* * HTML TCPDF can draw: pictures in the page itself (data: URIs) kept, as * TCPDF takes them, and anything pointing elsewhere dropped - TCPDF would go * and fetch any address, and a format it cannot draw ends the PDF. */ function contractPdfSafeHtml($html) { if (stripos((string) $html, ']*>/i', function ($match) { $tag = $match[0]; $src = preg_match('/\bsrc\s*=\s*"([^"]*)"/i', $tag, $m) ? html_entity_decode($m[1], ENT_QUOTES, 'UTF-8') : ''; $alt = preg_match('/\balt\s*=\s*"([^"]*)"/i', $tag, $m) ? $m[1] : ''; $data = preg_match('#^data:image/(png|jpe?g|gif);base64,(.+)$#is', $src, $m) ? (base64_decode($m[2], true) ?: null) : null; $info = $data ? @getimagesizefromstring($data) : false; if (!$info || !in_array($info[2], [IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_GIF], true)) { return $alt !== '' ? '[' . $alt . ']' : '[image]'; } return '' . $alt . ''; }, (string) $html); } /** Draw a contract - a round's copy when $round is given, else as it is today - ['name' => file name, 'data' => the PDF]. */ function contractPdfRender(array $row, $round = null) { $company = billingCompany(); $e = function ($value) { return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8'); }; $grey = '#6c757d'; $signed = $round && !empty($round['contract_signature_client_signed_at']) && !empty($round['contract_signature_agent_signed_at']); $document = $round ? (string) $round['contract_signature_document'] : contractDocumentHtml($row); $format = in_array($company['company_country'] ?? '', ['United States', 'USA', 'US', 'Canada'], true) ? 'LETTER' : 'A4'; $pdf = new ContractPdf('P', 'mm', $format, true, 'UTF-8', false); $pdf->SetCreator('ITFlow'); $pdf->SetAuthor((string) ($company['company_name'] ?? '')); $pdf->SetTitle((string) $row['contract_name']); $pdf->SetMargins(16, 14, 16); $pdf->SetHeaderMargin(0); $pdf->SetFooterMargin(12); $pdf->SetAutoPageBreak(true, 20); $pdf->setFontSubsetting(true); $pdf->setImageScale(1.5); $pdf->footer_label = trim(($company['company_name'] ?? '') . ' · ' . $row['contract_name'] . ($signed ? ' · Signed' : ($round ? '' : ' · ' . date('M j, Y'))), ' ·'); $pdf->AddPage(); $pdf->SetFont('dejavusans', '', 8.5); $pdf->SetTextColor(33, 37, 41); // Who it is from $from_lines = array_filter([ formatAddress($company['company_address'] ?? '', $company['company_city'] ?? '', $company['company_state'] ?? '', $company['company_zip'] ?? '', $company['company_country'] ?? '', ', '), implode(' · ', array_filter([$company['company_email'] ?? '', $company['phone_display'] ?? '', $company['company_website'] ?? ''])), ]); $logo = !empty($company['logo_path']) ? '
' : ''; $status = $signed ? 'SIGNED' : ($round && !empty($round['contract_signature_client_signed_at']) ? 'SIGNED BY CLIENT' : 'CONTRACT'); $pdf->writeHTML('
' . $logo . '' . $e($company['company_name'] ?? '') . '
' . nl2br($e(implode("\n", $from_lines))) . '
' . $status . '
' . $e($round && !empty($round['contract_signature_sent_at']) ? 'Sent ' . date('M j, Y', strtotime($round['contract_signature_sent_at'])) : 'As of ' . date('M j, Y')) . '
', true, false, true, false, ''); $pdf->Ln(2); // The document - plain text when TCPDF can't draw it try { $pdf->startTransaction(); $pdf->writeHTML(contractPdfStyle() . contractPdfSafeHtml($document), true, false, true, false, ''); $pdf->commitTransaction(); } catch (Throwable $error) { $pdf = $pdf->rollbackTransaction(); $text = trim(preg_replace('/\n{3,}/', "\n\n", html_entity_decode(strip_tags(str_replace(['
', '
', '
', '

', '', '', '', ''], "\n", $document)), ENT_QUOTES, 'UTF-8'))); $pdf->writeHTML('' . nl2br($e($text)) . '', true, false, true, false, ''); } $pdf->Ln(4); // The signatures $cells = ''; $whos = []; foreach (['client' => (string) $row['client_name'], 'agent' => (string) ($company['company_name'] ?? '')] as $side => $for) { $has = $round && !empty($round["contract_signature_{$side}_signed_at"]); $mark = '


'; if ($has && !empty($round["contract_signature_{$side}_image"]) && preg_match('#^data:image/png;base64,(.+)$#s', (string) $round["contract_signature_{$side}_image"], $m)) { $mark = ''; } elseif ($has) { $mark = '' . $e($round["contract_signature_{$side}_name"]) . ''; } $who = $has ? '' . $e($round["contract_signature_{$side}_name"]) . '
' . $e(implode(' · ', array_filter([(string) $round["contract_signature_{$side}_title"], date('M j, Y', strtotime($round["contract_signature_{$side}_signed_at"]))]))) . '' : 'Name, title and date
 '; $cells .= 'SIGNED FOR ' . $e(strtoupper($for)) . '
' . $mark . ''; $cells .= $side === 'client' ? '' : ''; $whos[] = $who; } $pdf->writeHTML('' . $cells . '
' . $whos[0] . '' . $whos[1] . '
', true, false, true, false, ''); // How it was signed if ($round && $round['contract_signature_client_via'] !== null) { $rows = ''; foreach (contractSignatureTrail($round) as [$when, $what, $detail]) { $rows .= '' . $e($when) . '' . $e($what) . ($detail !== '' ? '
' . $e($detail) . '' : '') . ''; } $html = '' . $rows . '
How it was signed
Document fingerprintSHA-256 ' . $e($round['contract_signature_document_hash']) . '
'; $pdf->Ln(6); $pdf->SetFont('dejavusans', '', 7.5); $pdf->writeHTML($html, true, false, true, false, ''); } $name = preg_replace('/[^A-Za-z0-9._-]+/', '-', trim($row['client_name'] . ' ' . $row['contract_name'])) . ($signed ? '-signed' : '') . '.pdf'; return ['name' => trim($name, '-'), 'data' => $pdf->Output('', 'S')]; }