mirror of
https://github.com/espocrm/espocrm.git
synced 2026-07-01 08:26:04 +00:00
301 lines
8.9 KiB
PHP
301 lines
8.9 KiB
PHP
<?php
|
|
/************************************************************************
|
|
* This file is part of EspoCRM.
|
|
*
|
|
* EspoCRM - Open Source CRM application.
|
|
* Copyright (C) 2014-2023 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
|
* Website: https://www.espocrm.com
|
|
*
|
|
* EspoCRM is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* EspoCRM is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
|
*
|
|
* The interactive user interfaces in modified source and object code versions
|
|
* of this program must display Appropriate Legal Notices, as required under
|
|
* Section 5 of the GNU General Public License version 3.
|
|
*
|
|
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
|
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
|
************************************************************************/
|
|
|
|
namespace Espo\Tools\Pdf\Dompdf;
|
|
|
|
use Espo\Core\Htmlizer\TemplateRendererFactory;
|
|
use Espo\Core\Utils\Config;
|
|
use Espo\Core\Utils\Log;
|
|
use Espo\ORM\Entity;
|
|
use Espo\Tools\Pdf\Data;
|
|
use Espo\Tools\Pdf\Params;
|
|
use Espo\Tools\Pdf\Template;
|
|
|
|
use TCPDF2DBarcode;
|
|
use TCPDFBarcode;
|
|
|
|
class HtmlComposer
|
|
{
|
|
public function __construct(
|
|
private Config $config,
|
|
private TemplateRendererFactory $templateRendererFactory,
|
|
private ImageSourceProvider $imageSourceProvider,
|
|
private Log $log
|
|
) {}
|
|
|
|
public function composeHead(Template $template, Entity $entity): string
|
|
{
|
|
$topMargin = $template->getTopMargin();
|
|
$rightMargin = $template->getRightMargin();
|
|
$bottomMargin = $template->getBottomMargin();
|
|
$leftMargin = $template->getLeftMargin();
|
|
|
|
$fontSize = $this->config->get('pdfFontSize') ?? 12;
|
|
|
|
$headerPosition = $template->getHeaderPosition();
|
|
$footerPosition = $template->getFooterPosition();
|
|
|
|
|
|
$titleHtml = '';
|
|
|
|
if ($template->hasTitle()) {
|
|
$title = $this->replacePlaceholders($template->getTitle(), $entity);
|
|
|
|
$titleHtml = "<title>" . htmlspecialchars($title) . "</title>";
|
|
}
|
|
|
|
$html = "
|
|
<head>
|
|
{$titleHtml}
|
|
</head>
|
|
<style>
|
|
@page {
|
|
margin: {$topMargin}mm {$rightMargin}mm {$bottomMargin}mm {$leftMargin}mm;
|
|
}
|
|
|
|
body {
|
|
font-size: {$fontSize}pt;
|
|
}
|
|
|
|
> header {
|
|
position: fixed;
|
|
margin-top: -{$topMargin}mm;
|
|
margin-left: -{$rightMargin}mm;
|
|
margin-right: -{$leftMargin}mm;
|
|
top: {$headerPosition}mm;
|
|
left: {$leftMargin}mm;
|
|
right: {$rightMargin}mm;
|
|
}
|
|
|
|
> footer {
|
|
position: fixed;
|
|
margin-bottom: -{$bottomMargin}mm;
|
|
margin-left: -{$leftMargin}mm;
|
|
margin-right: -{$rightMargin}mm;
|
|
height: {$footerPosition}mm;
|
|
bottom: 0;
|
|
left: {$leftMargin}mm;
|
|
right: {$rightMargin}mm;
|
|
}
|
|
|
|
> header .page-number:after,
|
|
> footer .page-number:after {
|
|
content: counter(page);
|
|
}
|
|
</style>
|
|
";
|
|
|
|
return $html;
|
|
}
|
|
|
|
public function composeHeaderFooter(Template $template, Entity $entity, Params $params, Data $data): string
|
|
{
|
|
$html = "";
|
|
|
|
$renderer = $this->templateRendererFactory
|
|
->create()
|
|
->setApplyAcl($params->applyAcl())
|
|
->setEntity($entity)
|
|
->setSkipInlineAttachmentHandling(true)
|
|
->setData($data->getAdditionalTemplateData());
|
|
|
|
if ($template->hasHeader()) {
|
|
$htmlHeader = $renderer->renderTemplate($template->getHeader());
|
|
|
|
$htmlHeader = $this->replaceHeadTags($htmlHeader);
|
|
|
|
$html .= "<header>{$htmlHeader}</header>";
|
|
}
|
|
|
|
if ($template->hasFooter()) {
|
|
$htmlFooter = $renderer->renderTemplate($template->getFooter());
|
|
|
|
$htmlFooter = $this->replaceHeadTags($htmlFooter);
|
|
|
|
$html .= "<footer>{$htmlFooter}</footer>";
|
|
}
|
|
|
|
return $html;
|
|
}
|
|
|
|
public function composeMain(
|
|
Template $template,
|
|
Entity $entity,
|
|
Params $params,
|
|
Data $data
|
|
): string {
|
|
|
|
$renderer = $this->templateRendererFactory
|
|
->create()
|
|
->setApplyAcl($params->applyAcl())
|
|
->setEntity($entity)
|
|
->setSkipInlineAttachmentHandling(true)
|
|
->setData($data->getAdditionalTemplateData());
|
|
|
|
$bodyTemplate = $template->getBody();
|
|
|
|
$html = $renderer->renderTemplate($bodyTemplate);
|
|
|
|
$html = $this->replaceTags($html);
|
|
|
|
return "<main>{$html}</main>";
|
|
}
|
|
|
|
private function replaceTags(string $html): string
|
|
{
|
|
$html = str_replace('<br pagebreak="true">', '<div style="page-break-after: always;"></div>', $html);
|
|
$html = preg_replace('/src="\@([A-Za-z0-9\+\/]*={0,2})"/', 'src="data:image/jpeg;base64,$1"', $html);
|
|
$html = str_replace('?entryPoint=attachment&', '?entryPoint=attachment&', $html ?? '');
|
|
|
|
$html = preg_replace_callback(
|
|
'/<barcodeimage data="([^"]+)"\/>/',
|
|
function ($matches) {
|
|
$dataString = $matches[1];
|
|
|
|
$data = json_decode(urldecode($dataString), true);
|
|
|
|
return $this->composeBarcode($data);
|
|
},
|
|
$html
|
|
) ?? '';
|
|
|
|
$html = preg_replace_callback(
|
|
"/src=\"\?entryPoint=attachment\&id=([A-Za-z0-9]*)\"/",
|
|
function ($matches) {
|
|
$id = $matches[1];
|
|
|
|
if (!$id) {
|
|
return '';
|
|
}
|
|
|
|
$src = $this->imageSourceProvider->get($id);
|
|
|
|
if (!$src) {
|
|
return '';
|
|
}
|
|
|
|
return "src=\"{$src}\"";
|
|
},
|
|
$html
|
|
) ?? '';
|
|
|
|
return $html;
|
|
}
|
|
|
|
private function replaceHeadTags(string $html): string
|
|
{
|
|
$html = str_replace('{pageNumber}', '<span class="page-number"></span>', $html);
|
|
|
|
return $this->replaceTags($html);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param array<string, mixed> $data
|
|
* @return string
|
|
*/
|
|
private function composeBarcode(array $data): string
|
|
{
|
|
$value = $data['value'] ?? null;
|
|
|
|
if ($value === null) {
|
|
return '';
|
|
}
|
|
|
|
$codeType = $data['type'] ?? 'CODE128';
|
|
|
|
$typeMap = [
|
|
"CODE128" => 'C128',
|
|
"CODE128A" => 'C128A',
|
|
"CODE128B" => 'C128B',
|
|
"CODE128C" => 'C128C',
|
|
"EAN13" => 'EAN13',
|
|
"EAN8" => 'EAN8',
|
|
"EAN5" => 'EAN5',
|
|
"EAN2" => 'EAN2',
|
|
"UPC" => 'UPCA',
|
|
"UPCE" => 'UPCE',
|
|
"ITF14" => 'I25',
|
|
"pharmacode" => 'PHARMA',
|
|
"QRcode" => 'QRCODE,H',
|
|
];
|
|
|
|
$type = $typeMap[$codeType] ?? null;
|
|
|
|
if ($codeType === 'QRcode') {
|
|
$width = $data['width'] ?? 40;
|
|
$height = $data['height'] ?? 40;
|
|
$color = $data['color'] ?? [0, 0, 0];
|
|
|
|
$barcode = new TCPDF2DBarcode($value, $type);
|
|
$code = $barcode->getBarcodeSVGcode($width, $height, $color);
|
|
|
|
$encoded = base64_encode($code);
|
|
|
|
$css = "width: {$width}mm; height: {$height}mm;";
|
|
|
|
return "<img src=\"data:image/svg+xml;base64,{$encoded}\" style=\"{$css}\">";
|
|
}
|
|
|
|
if (!$type) {
|
|
$this->log->warning("Not supported barcode type {$codeType}.");
|
|
|
|
return '';
|
|
}
|
|
|
|
$width = $data['width'] ?? 60;
|
|
$height = $data['height'] ?? 30;
|
|
$color = $data['color'] ?? [0, 0, 0];
|
|
|
|
$barcode = new TCPDFBarcode($value, $type);
|
|
$code = $barcode->getBarcodeSVGcode($width, $height, $color);
|
|
|
|
$encoded = base64_encode($code);
|
|
|
|
$css = "width: {$width}mm; height: {$height}mm;";
|
|
|
|
return "<img src=\"data:image/svg+xml;base64,{$encoded}\" style=\"{$css}\">";
|
|
}
|
|
|
|
private function replacePlaceholders(string $string, Entity $entity): string
|
|
{
|
|
$newString = $string;
|
|
|
|
$attributeList = ['name'];
|
|
|
|
foreach ($attributeList as $attribute) {
|
|
$value = (string) ($entity->get($attribute) ?? '');
|
|
|
|
$newString = str_replace('{$' . $attribute . '}', $value, $newString);
|
|
}
|
|
|
|
return $newString;
|
|
}
|
|
}
|