dompdf engine
This commit is contained in:
@@ -51,8 +51,10 @@ class TableTag implements Helper
|
||||
|
||||
$content = $function !== null ? $function() : '';
|
||||
|
||||
$style = "border: {$border}; border-spacing: 0; border-collapse: collapse;";
|
||||
|
||||
return Result::createSafeString(
|
||||
"<table border=\"{$border}\" cellpadding=\"{$cellpadding}\" {$attributesPart}>" .
|
||||
"<table style=\"{$style}\" border=\"{$border}\" cellpadding=\"{$cellpadding}\" {$attributesPart}>" .
|
||||
$content .
|
||||
"</table>"
|
||||
);
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
namespace Espo\Modules\Crm\Tools\Campaign;
|
||||
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Field\LinkParent;
|
||||
use Espo\Core\FileStorage\Manager as FileStorageManager;
|
||||
use Espo\Core\Record\ServiceContainer;
|
||||
use Espo\Core\Utils\Config;
|
||||
@@ -45,6 +46,7 @@ use Espo\Tools\Pdf\Data\DataLoaderManager;
|
||||
use Espo\Tools\Pdf\IdDataMap;
|
||||
use Espo\Tools\Pdf\Params;
|
||||
use Espo\Tools\Pdf\TemplateWrapper;
|
||||
use Espo\Tools\Pdf\ZipContents;
|
||||
|
||||
class MailMergeGenerator
|
||||
{
|
||||
@@ -127,21 +129,26 @@ class MailMergeGenerator
|
||||
|
||||
$contents = $printer->printCollection($collection, $params, $idDataMap);
|
||||
|
||||
$filename = Util::sanitizeFileName($name) . '.pdf';
|
||||
$type = $contents instanceof ZipContents ?
|
||||
'application/zip' :
|
||||
'application/pdf';
|
||||
|
||||
$filename = $contents instanceof ZipContents ?
|
||||
Util::sanitizeFileName($name) . '.zip' :
|
||||
Util::sanitizeFileName($name) . '.pdf';
|
||||
|
||||
/** @var Attachment $attachment */
|
||||
$attachment = $this->entityManager->getNewEntity(Attachment::ENTITY_TYPE);
|
||||
|
||||
$attachment->set([
|
||||
'relatedType' => Campaign::ENTITY_TYPE,
|
||||
'relatedId' => $campaignId,
|
||||
]);
|
||||
$relatedLink = $campaignId ?
|
||||
LinkParent::create(Campaign::ENTITY_TYPE, $campaignId) : null;
|
||||
|
||||
$attachment
|
||||
->setRelated($relatedLink)
|
||||
->setSize($contents->getStream()->getSize())
|
||||
->setRole(self::ATTACHMENT_MAIL_MERGE_ROLE)
|
||||
->setName($filename)
|
||||
->setType('application/pdf');
|
||||
->setType($type);
|
||||
|
||||
$this->entityManager->saveEntity($attachment);
|
||||
|
||||
|
||||
@@ -204,7 +204,7 @@ return [
|
||||
'auth2FAMethodList' => ['Totp'],
|
||||
'personNameFormat' => 'firstLast',
|
||||
'newNotificationCountInTitle' => false,
|
||||
'pdfEngine' => 'Tcpdf',
|
||||
'pdfEngine' => 'Dompdf',
|
||||
'smsProvider' => null,
|
||||
'defaultFileStorage' => 'EspoUploadDir',
|
||||
'ldapUserNameAttribute' => 'sAMAccountName',
|
||||
|
||||
@@ -158,7 +158,8 @@
|
||||
"oidcFallback": "OIDC Fallback Login",
|
||||
"oidcAllowRegularUserFallback": "OIDC Allow fallback login for regular users",
|
||||
"oidcAllowAdminUser": "OIDC Allow OIDC login for admin users",
|
||||
"oidcLogoutUrl": "OIDC Logout URL"
|
||||
"oidcLogoutUrl": "OIDC Logout URL",
|
||||
"pdfEngine": "PDF Engine"
|
||||
},
|
||||
"options": {
|
||||
"authenticationMethod": {
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
"rows": [
|
||||
[{"name": "followCreatedEntities"}, {"name": "emailAddressIsOptedOutByDefault"}],
|
||||
[{"name": "aclAllowDeleteCreated"}, {"name": "cleanupDeletedRecords"}],
|
||||
[{"name": "exportDisabled"}, {"name": "b2cMode"}]
|
||||
[{"name": "exportDisabled"}, {"name": "b2cMode"}],
|
||||
[{"name": "pdfEngine"}, false]
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -34,5 +34,20 @@
|
||||
"symbol",
|
||||
"times"
|
||||
]
|
||||
},
|
||||
"Dompdf": {
|
||||
"implementationClassNameMap": {
|
||||
"entity": "Espo\\Tools\\Pdf\\Dompdf\\EntityPrinter"
|
||||
},
|
||||
"fontFaceList": [
|
||||
"Courier",
|
||||
"Helvetica",
|
||||
"Times",
|
||||
"Symbol",
|
||||
"ZapfDingbats",
|
||||
"DejaVu Sans",
|
||||
"DejaVu Serif",
|
||||
"DejaVu Sans Mono"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -852,6 +852,10 @@
|
||||
"oidcLogoutUrl": {
|
||||
"type": "varchar",
|
||||
"tooltip": true
|
||||
},
|
||||
"pdfEngine": {
|
||||
"type": "enum",
|
||||
"view": "views/settings/fields/pdf-engine"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
},
|
||||
"headerPosition": {
|
||||
"type": "float",
|
||||
"default": 10
|
||||
"default": 0
|
||||
},
|
||||
"teams": {
|
||||
"type": "linkMultiple"
|
||||
|
||||
@@ -29,23 +29,15 @@
|
||||
|
||||
namespace Espo\Tools\Pdf;
|
||||
|
||||
use Espo\Core\{
|
||||
Exceptions\Error,
|
||||
InjectableFactory,
|
||||
};
|
||||
use Espo\Core\InjectableFactory;
|
||||
use RuntimeException;
|
||||
|
||||
class Builder
|
||||
{
|
||||
private ?Template $template = null;
|
||||
|
||||
private ?string $engine = null;
|
||||
|
||||
private InjectableFactory $injectableFactory;
|
||||
|
||||
public function __construct(InjectableFactory $injectableFactory)
|
||||
{
|
||||
$this->injectableFactory = $injectableFactory;
|
||||
}
|
||||
public function __construct(private InjectableFactory $injectableFactory) {}
|
||||
|
||||
public function setTemplate(Template $template): self
|
||||
{
|
||||
@@ -64,11 +56,11 @@ class Builder
|
||||
public function build(): PrinterController
|
||||
{
|
||||
if (!$this->engine) {
|
||||
throw new Error('Engine is not set.');
|
||||
throw new RuntimeException('Engine is not set.');
|
||||
}
|
||||
|
||||
if (!$this->template) {
|
||||
throw new Error('Template is not set.');
|
||||
throw new RuntimeException('Template is not set.');
|
||||
}
|
||||
|
||||
return $this->injectableFactory->createWith(
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
<?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\Tools\Pdf\Contents as ContentsInterface;
|
||||
|
||||
use GuzzleHttp\Psr7\Stream;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use Dompdf\Dompdf;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class Contents implements ContentsInterface
|
||||
{
|
||||
private ?string $string = null;
|
||||
|
||||
public function __construct(private Dompdf $pdf) {}
|
||||
|
||||
public function getStream(): StreamInterface
|
||||
{
|
||||
$resource = fopen('php://temp', 'r+');
|
||||
|
||||
if ($resource === false) {
|
||||
throw new RuntimeException("Could not open temp.");
|
||||
}
|
||||
|
||||
fwrite($resource, $this->getString());
|
||||
rewind($resource);
|
||||
|
||||
return new Stream($resource);
|
||||
}
|
||||
|
||||
public function getString(): string
|
||||
{
|
||||
if ($this->string === null) {
|
||||
$this->string = $this->pdf->output();
|
||||
}
|
||||
|
||||
return $this->string ?? '';
|
||||
}
|
||||
|
||||
public function getLength(): int
|
||||
{
|
||||
return strlen($this->getString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?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 Dompdf\Dompdf;
|
||||
use Dompdf\Options;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\Pdf\Template;
|
||||
|
||||
class DompdfInitializer
|
||||
{
|
||||
private string $defaultFontFace = 'Courier';
|
||||
|
||||
public function __construct(
|
||||
private Config $config
|
||||
) {}
|
||||
|
||||
public function initialize(Template $template, ?Entity $entity = null): Dompdf
|
||||
{
|
||||
$options = new Options();
|
||||
|
||||
$options->setDefaultFont($this->getFontFace($template));
|
||||
|
||||
$pdf = new Dompdf($options);
|
||||
|
||||
$size = $template->getPageFormat() === Template::PAGE_FORMAT_CUSTOM ?
|
||||
[$template->getPageWidth(), $template->getPageHeight()] :
|
||||
$template->getPageFormat();
|
||||
|
||||
$orientation = $template->getPageOrientation() === Template::PAGE_ORIENTATION_PORTRAIT ?
|
||||
'portrait' :
|
||||
'landscape';
|
||||
|
||||
$pdf->setPaper($size, $orientation);
|
||||
|
||||
|
||||
|
||||
return $pdf;
|
||||
}
|
||||
|
||||
private function getFontFace(Template $template): string
|
||||
{
|
||||
return
|
||||
$template->getFontFace() ??
|
||||
$this->config->get('pdfFontFace') ??
|
||||
$this->defaultFontFace;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?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\ORM\Entity;
|
||||
use Espo\Tools\Pdf\Contents;
|
||||
use Espo\Tools\Pdf\Data;
|
||||
use Espo\Tools\Pdf\Dompdf\Contents as DompdfContents;
|
||||
use Espo\Tools\Pdf\EntityPrinter as EntityPrinterInterface;
|
||||
use Espo\Tools\Pdf\Params;
|
||||
use Espo\Tools\Pdf\Template;
|
||||
|
||||
class EntityPrinter implements EntityPrinterInterface
|
||||
{
|
||||
public function __construct(
|
||||
private DompdfInitializer $dompdfInitializer,
|
||||
private HtmlComposer $htmlComposer
|
||||
) {}
|
||||
|
||||
public function print(Template $template, Entity $entity, Params $params, Data $data): Contents
|
||||
{
|
||||
$pdf = $this->dompdfInitializer->initialize($template, $entity);
|
||||
|
||||
$headHtml = $this->htmlComposer->composeHead($template, $entity);
|
||||
$headerFooterHtml = $this->htmlComposer->composeHeaderFooter($template, $entity, $params, $data);
|
||||
$mainHtml = $this->htmlComposer->composeMain($template, $entity, $params, $data);
|
||||
|
||||
$html = $headHtml . "\n<body>" . $headerFooterHtml . $mainHtml . "</body>";
|
||||
|
||||
$pdf->loadHtml($html);
|
||||
$pdf->render();
|
||||
|
||||
return new DompdfContents($pdf);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
<?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());
|
||||
|
||||
// @todo Apply pagination tags.
|
||||
|
||||
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
|
||||
{
|
||||
// @todo Convert barcode tags.
|
||||
|
||||
$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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?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\Exceptions\Forbidden;
|
||||
use Espo\Core\FileStorage\Manager as FileStorageManager;
|
||||
use Espo\Entities\Attachment;
|
||||
use Espo\ORM\EntityManager;
|
||||
use Espo\Tools\Attachment\Checker;
|
||||
|
||||
class ImageSourceProvider
|
||||
{
|
||||
public function __construct(
|
||||
private EntityManager $entityManager,
|
||||
private Checker $checker,
|
||||
private FileStorageManager $fileStorageManager,
|
||||
) {}
|
||||
|
||||
public function get(string $id): ?string
|
||||
{
|
||||
/** @var Attachment $attachment */
|
||||
$attachment = $this->entityManager->getEntityById(Attachment::ENTITY_TYPE, $id);
|
||||
|
||||
if (!$attachment) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->checker->checkTypeImage($attachment);
|
||||
}
|
||||
catch (Forbidden) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$type = $attachment->getType();
|
||||
|
||||
if (!$type) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$contents = $this->fileStorageManager->getContents($attachment);
|
||||
|
||||
return 'data:image/' . $type . ';base64,' . base64_encode($contents);
|
||||
}
|
||||
}
|
||||
@@ -179,14 +179,20 @@ class MassService
|
||||
|
||||
$entityTypeTranslated = $this->defaultLanguage->translateLabel($entityType, 'scopeNamesPlural');
|
||||
|
||||
$filename = Util::sanitizeFileName($entityTypeTranslated) . '.pdf';
|
||||
$type = $contents instanceof ZipContents ?
|
||||
'application/zip' :
|
||||
'application/pdf';
|
||||
|
||||
$filename = $contents instanceof ZipContents ?
|
||||
Util::sanitizeFileName($entityTypeTranslated) . '.zip' :
|
||||
Util::sanitizeFileName($entityTypeTranslated) . '.pdf';
|
||||
|
||||
/** @var Attachment $attachment */
|
||||
$attachment = $this->entityManager->getNewEntity(Attachment::ENTITY_TYPE);
|
||||
|
||||
$attachment
|
||||
->setName($filename)
|
||||
->setType('application/pdf')
|
||||
->setType($type)
|
||||
->setRole(self::ATTACHMENT_MASS_PDF_ROLE)
|
||||
->setSize($contents->getStream()->getSize());
|
||||
|
||||
|
||||
@@ -29,40 +29,24 @@
|
||||
|
||||
namespace Espo\Tools\Pdf;
|
||||
|
||||
use Espo\Core\{
|
||||
Exceptions\Error,
|
||||
Utils\Metadata,
|
||||
InjectableFactory,
|
||||
};
|
||||
|
||||
use Espo\{
|
||||
ORM\Entity,
|
||||
ORM\Collection,
|
||||
};
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\InjectableFactory;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\ORM\Collection;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class PrinterController
|
||||
{
|
||||
private Metadata $metadata;
|
||||
|
||||
private InjectableFactory $injectableFactory;
|
||||
|
||||
private Template $template;
|
||||
|
||||
private string $engine;
|
||||
|
||||
public function __construct(
|
||||
Metadata $metadata,
|
||||
InjectableFactory $injectableFactory,
|
||||
Template $template,
|
||||
string $engine
|
||||
) {
|
||||
$this->metadata = $metadata;
|
||||
$this->injectableFactory = $injectableFactory;
|
||||
|
||||
$this->template = $template;
|
||||
$this->engine = $engine;
|
||||
}
|
||||
private Metadata $metadata,
|
||||
private InjectableFactory $injectableFactory,
|
||||
private Template $template,
|
||||
private string $engine
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @throws Error
|
||||
*/
|
||||
public function printEntity(Entity $entity, ?Params $params, ?Data $data = null): Contents
|
||||
{
|
||||
$params = $params ?? new Params();
|
||||
@@ -75,12 +59,34 @@ class PrinterController
|
||||
* @param Collection<Entity> $collection
|
||||
* @throws Error
|
||||
*/
|
||||
public function printCollection(Collection $collection, ?Params $params, ?IdDataMap $IdDataMap = null): Contents
|
||||
{
|
||||
$params = $params ?? new Params();
|
||||
$IdDataMap = $IdDataMap ?? new IdDataMap();
|
||||
public function printCollection(
|
||||
Collection $collection,
|
||||
?Params $params,
|
||||
?IdDataMap $idDataMap = null
|
||||
): Contents {
|
||||
|
||||
return $this->createCollectionPrinter()->print($this->template, $collection, $params, $IdDataMap);
|
||||
$params = $params ?? new Params();
|
||||
$idDataMap = $idDataMap ?? new IdDataMap();
|
||||
|
||||
if ($this->hasCollectionPrinter()) {
|
||||
return $this->createCollectionPrinter()->print($this->template, $collection, $params, $idDataMap);
|
||||
}
|
||||
|
||||
$printer = $this->createEntityPrinter();
|
||||
|
||||
$zipper = new Zipper();
|
||||
|
||||
foreach ($collection as $entity) {
|
||||
$data = $idDataMap->get($entity->getId()) ?? new Data();
|
||||
|
||||
$itemContents = $printer->print($this->template, $entity, $params, $data);
|
||||
|
||||
$zipper->add($itemContents, $entity->getId());
|
||||
}
|
||||
|
||||
$zipper->archive();
|
||||
|
||||
return new ZipContents($zipper->getFilePath());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,9 +110,7 @@ class PrinterController
|
||||
*/
|
||||
private function createCollectionPrinter(): CollectionPrinter
|
||||
{
|
||||
/** @var ?class-string<CollectionPrinter> $className */
|
||||
$className = $this->metadata
|
||||
->get(['app', 'pdfEngines', $this->engine, 'implementationClassNameMap', 'collection']) ?? null;
|
||||
$className = $this->getCollectionPrinterClassName();
|
||||
|
||||
if (!$className) {
|
||||
throw new Error("Unknown PDF engine '{$this->engine}', type 'collection'.");
|
||||
@@ -114,4 +118,20 @@ class PrinterController
|
||||
|
||||
return $this->injectableFactory->create($className);
|
||||
}
|
||||
|
||||
private function hasCollectionPrinter(): bool
|
||||
{
|
||||
return (bool) $this->getCollectionPrinterClassName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ?class-string<CollectionPrinter>
|
||||
*/
|
||||
private function getCollectionPrinterClassName(): ?string
|
||||
{
|
||||
/** @var ?class-string<CollectionPrinter> */
|
||||
return $this->metadata
|
||||
->get(['app', 'pdfEngines', $this->engine, 'implementationClassNameMap', 'collection']) ?? null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,6 +31,11 @@ namespace Espo\Tools\Pdf;
|
||||
|
||||
interface Template
|
||||
{
|
||||
public const PAGE_FORMAT_CUSTOM = 'Custom';
|
||||
|
||||
public const PAGE_ORIENTATION_PORTRAIT = 'Portrait';
|
||||
public const PAGE_ORIENTATION_LANDSCAPE = 'Landscape';
|
||||
|
||||
public function getFontFace(): ?string;
|
||||
|
||||
public function getBottomMargin(): float;
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
<?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;
|
||||
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use GuzzleHttp\Psr7\Stream;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class ZipContents implements Contents
|
||||
{
|
||||
public function __construct(private string $filePath) {}
|
||||
|
||||
public function getStream(): StreamInterface
|
||||
{
|
||||
$resource = fopen($this->filePath, 'r+');
|
||||
|
||||
if ($resource === false) {
|
||||
throw new RuntimeException("Could not open {$this->filePath}.");
|
||||
}
|
||||
|
||||
return new Stream($resource);
|
||||
}
|
||||
|
||||
public function getString(): string
|
||||
{
|
||||
return $this->getStream()->getContents();
|
||||
}
|
||||
|
||||
public function getLength(): int
|
||||
{
|
||||
return (int) $this->getStream()->getSize();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<?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;
|
||||
|
||||
use Espo\Core\Utils\Util;
|
||||
use LogicException;
|
||||
use RuntimeException;
|
||||
use ZipArchive;
|
||||
|
||||
class Zipper
|
||||
{
|
||||
private ?string $filePath = null;
|
||||
/** @var array{string, string}[] */
|
||||
private array $itemList = [];
|
||||
|
||||
public function __construct() {}
|
||||
|
||||
public function add(Contents $contents, string $name): void
|
||||
{
|
||||
$tempPath = tempnam(sys_get_temp_dir(), 'espo-pdf-zip-item');
|
||||
|
||||
if ($tempPath === false) {
|
||||
throw new RuntimeException("Could not create a temp file.");
|
||||
}
|
||||
|
||||
$fp = fopen($tempPath, 'w');
|
||||
|
||||
if ($fp === false) {
|
||||
throw new RuntimeException("Could not open a temp file {$tempPath}.");
|
||||
}
|
||||
|
||||
fwrite($fp, $contents->getString());
|
||||
fclose($fp);
|
||||
|
||||
$this->itemList[] = [$tempPath, Util::sanitizeFileName($name) . '.pdf'];
|
||||
}
|
||||
|
||||
public function archive(): void
|
||||
{
|
||||
$tempPath = tempnam(sys_get_temp_dir(), 'espo-pdf-zip');
|
||||
|
||||
if ($tempPath === false) {
|
||||
throw new RuntimeException("Could not create a temp file.");
|
||||
}
|
||||
|
||||
$archive = new ZipArchive();
|
||||
$archive->open($tempPath, ZipArchive::CREATE);
|
||||
|
||||
foreach ($this->itemList as $item) {
|
||||
$archive->addFile($item[0], $item[1]);
|
||||
}
|
||||
|
||||
$archive->close();
|
||||
|
||||
$this->filePath = $tempPath;
|
||||
}
|
||||
|
||||
public function getFilePath(): string
|
||||
{
|
||||
if (!$this->filePath) {
|
||||
throw new LogicException();
|
||||
}
|
||||
|
||||
return $this->filePath;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/************************************************************************
|
||||
* 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.
|
||||
************************************************************************/
|
||||
|
||||
define('views/settings/fields/pdf-engine', ['views/fields/enum'], function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
setupOptions: function () {
|
||||
this.params.options = Object.keys(this.getMetadata().get(['app', 'pdfEngines']));
|
||||
|
||||
if (this.params.options.length === 0) {
|
||||
this.params.options = [''];
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
+2
-1
@@ -46,7 +46,8 @@
|
||||
"league/flysystem-async-aws-s3": "^2.0",
|
||||
"johngrogg/ics-parser": "^3.0",
|
||||
"phpseclib/phpseclib": "^3.0",
|
||||
"openspout/openspout": "^4.9"
|
||||
"openspout/openspout": "^4.9",
|
||||
"dompdf/dompdf": "^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9",
|
||||
|
||||
Generated
+275
-1
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "6221dcfbe6738d65122c1a60dcf4c1b2",
|
||||
"content-hash": "ffda69f674ecffbd773206c0e88aeb52",
|
||||
"packages": [
|
||||
{
|
||||
"name": "async-aws/core",
|
||||
@@ -620,6 +620,68 @@
|
||||
],
|
||||
"time": "2020-05-29T18:28:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "dompdf/dompdf",
|
||||
"version": "v2.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/dompdf/dompdf.git",
|
||||
"reference": "c5310df0e22c758c85ea5288175fc6cd777bc085"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/dompdf/dompdf/zipball/c5310df0e22c758c85ea5288175fc6cd777bc085",
|
||||
"reference": "c5310df0e22c758c85ea5288175fc6cd777bc085",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*",
|
||||
"ext-mbstring": "*",
|
||||
"masterminds/html5": "^2.0",
|
||||
"phenx/php-font-lib": ">=0.5.4 <1.0.0",
|
||||
"phenx/php-svg-lib": ">=0.3.3 <1.0.0",
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-json": "*",
|
||||
"ext-zip": "*",
|
||||
"mockery/mockery": "^1.3",
|
||||
"phpunit/phpunit": "^7.5 || ^8 || ^9",
|
||||
"squizlabs/php_codesniffer": "^3.5"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-gd": "Needed to process images",
|
||||
"ext-gmagick": "Improves image processing performance",
|
||||
"ext-imagick": "Improves image processing performance",
|
||||
"ext-zlib": "Needed for pdf stream compression"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Dompdf\\": "src/"
|
||||
},
|
||||
"classmap": [
|
||||
"lib/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"LGPL-2.1"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "The Dompdf Community",
|
||||
"homepage": "https://github.com/dompdf/dompdf/blob/master/AUTHORS.md"
|
||||
}
|
||||
],
|
||||
"description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter",
|
||||
"homepage": "https://github.com/dompdf/dompdf",
|
||||
"support": {
|
||||
"issues": "https://github.com/dompdf/dompdf/issues",
|
||||
"source": "https://github.com/dompdf/dompdf/tree/v2.0.1"
|
||||
},
|
||||
"time": "2022-09-22T13:43:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "dragonmantank/cron-expression",
|
||||
"version": "v3.0.2",
|
||||
@@ -2090,6 +2152,75 @@
|
||||
},
|
||||
"time": "2021-01-23T16:37:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "masterminds/html5",
|
||||
"version": "2.7.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Masterminds/html5-php.git",
|
||||
"reference": "897eb517a343a2281f11bc5556d6548db7d93947"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Masterminds/html5-php/zipball/897eb517a343a2281f11bc5556d6548db7d93947",
|
||||
"reference": "897eb517a343a2281f11bc5556d6548db7d93947",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-ctype": "*",
|
||||
"ext-dom": "*",
|
||||
"ext-libxml": "*",
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.7-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Masterminds\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Matt Butcher",
|
||||
"email": "technosophos@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Matt Farina",
|
||||
"email": "matt@mattfarina.com"
|
||||
},
|
||||
{
|
||||
"name": "Asmir Mustafic",
|
||||
"email": "goetas@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "An HTML5 parser and serializer.",
|
||||
"homepage": "http://masterminds.github.io/html5-php",
|
||||
"keywords": [
|
||||
"HTML5",
|
||||
"dom",
|
||||
"html",
|
||||
"parser",
|
||||
"querypath",
|
||||
"serializer",
|
||||
"xml"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Masterminds/html5-php/issues",
|
||||
"source": "https://github.com/Masterminds/html5-php/tree/2.7.6"
|
||||
},
|
||||
"time": "2022-08-18T16:18:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "michelf/php-markdown",
|
||||
"version": "1.9.0",
|
||||
@@ -2725,6 +2856,96 @@
|
||||
},
|
||||
"time": "2020-10-15T08:29:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phenx/php-font-lib",
|
||||
"version": "0.5.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/dompdf/php-font-lib.git",
|
||||
"reference": "dd448ad1ce34c63d09baccd05415e361300c35b4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/dompdf/php-font-lib/zipball/dd448ad1ce34c63d09baccd05415e361300c35b4",
|
||||
"reference": "dd448ad1ce34c63d09baccd05415e361300c35b4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-mbstring": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/phpunit-bridge": "^3 || ^4 || ^5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"FontLib\\": "src/FontLib"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"LGPL-3.0"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Ménager",
|
||||
"email": "fabien.menager@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "A library to read, parse, export and make subsets of different types of font files.",
|
||||
"homepage": "https://github.com/PhenX/php-font-lib",
|
||||
"support": {
|
||||
"issues": "https://github.com/dompdf/php-font-lib/issues",
|
||||
"source": "https://github.com/dompdf/php-font-lib/tree/0.5.4"
|
||||
},
|
||||
"time": "2021-12-17T19:44:54+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phenx/php-svg-lib",
|
||||
"version": "0.5.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/dompdf/php-svg-lib.git",
|
||||
"reference": "76876c6cf3080bcb6f249d7d59705108166a6685"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/dompdf/php-svg-lib/zipball/76876c6cf3080bcb6f249d7d59705108166a6685",
|
||||
"reference": "76876c6cf3080bcb6f249d7d59705108166a6685",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-mbstring": "*",
|
||||
"php": "^7.1 || ^8.0",
|
||||
"sabberworm/php-css-parser": "^8.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Svg\\": "src/Svg"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"LGPL-3.0"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Ménager",
|
||||
"email": "fabien.menager@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "A library to read, parse and export to PDF SVG files.",
|
||||
"homepage": "https://github.com/PhenX/php-svg-lib",
|
||||
"support": {
|
||||
"issues": "https://github.com/dompdf/php-svg-lib/issues",
|
||||
"source": "https://github.com/dompdf/php-svg-lib/tree/0.5.0"
|
||||
},
|
||||
"time": "2022-09-06T12:16:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpoffice/phpspreadsheet",
|
||||
"version": "1.16.0",
|
||||
@@ -3953,6 +4174,59 @@
|
||||
},
|
||||
"time": "2019-06-21T08:51:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sabberworm/php-css-parser",
|
||||
"version": "8.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sabberworm/PHP-CSS-Parser.git",
|
||||
"reference": "e41d2140031d533348b2192a83f02d8dd8a71d30"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sabberworm/PHP-CSS-Parser/zipball/e41d2140031d533348b2192a83f02d8dd8a71d30",
|
||||
"reference": "e41d2140031d533348b2192a83f02d8dd8a71d30",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-iconv": "*",
|
||||
"php": ">=5.6.20"
|
||||
},
|
||||
"require-dev": {
|
||||
"codacy/coverage": "^1.4",
|
||||
"phpunit/phpunit": "^4.8.36"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-mbstring": "for parsing UTF-8 CSS"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Sabberworm\\CSS\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Raphael Schweikert"
|
||||
}
|
||||
],
|
||||
"description": "Parser for CSS Files written in PHP",
|
||||
"homepage": "https://www.sabberworm.com/blog/2010/6/10/php-css-parser",
|
||||
"keywords": [
|
||||
"css",
|
||||
"parser",
|
||||
"stylesheet"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sabberworm/PHP-CSS-Parser/issues",
|
||||
"source": "https://github.com/sabberworm/PHP-CSS-Parser/tree/8.4.0"
|
||||
},
|
||||
"time": "2021-12-11T13:40:54+00:00"
|
||||
},
|
||||
{
|
||||
"name": "slim/psr7",
|
||||
"version": "1.4",
|
||||
|
||||
Reference in New Issue
Block a user