2lenet/pdf-generator-bundle

PDF生成器

安装次数: 12,202

依赖项: 0

建议者: 0

安全性: 0

星级: 3

关注者: 6

分支: 2

开放问题: 0

类型:symfony-bundle

4.1.0 2024-06-05 10:16 UTC

README

安装

composer require 2lenet/pdf-generator-bundle

需求:unoserver(用于word_to_pdf)

    unoserver:
        image: registry.2le.net/2le/2le:unoserver

配置

config(默认值)

lle_pdf_generator:
  path: "data/pdfmodel"
  default_generator: "word_to_pdf"
  class: 'Lle\PdfGeneratorBundle\Entity\PdfModel'

添加路由(通过<a href="{{ path('lle_pdf_generator_show_ressource', {'id': item.id}) }}">显示资源)

lle_pdf_generator:
    resource: "@LlePdfGeneratorBundle/Resources/config/routes.yaml"
    prefix: /

如果您创建了一个没有类型且资源为mydoc.doc的模型,生成器将基于data/pdfmodel/mydoc.doc使用word_to_pdf生成器创建PDF。

配置您的标签

您可以轻松列出模型中使用的标签。

为此,只需声明将列出标签的页面的路由。路由名为" lle_pdf_generator_admin_balise"。

在Crudit中的示例

public function getListActions(): array
    {
        $actions = parent::getListActions();

        array_unshift($actions, ListAction::new(
            "action.balise",
            Path::new('lle_pdf_generator_admin_balise'),
            Icon::new("bookmark")
        ));

        return $actions;
    }

如果您使用多个与不同模块链接的文档模板,您可以在项目的pdf_generator.yaml中声明您的注解

lle_pdf_generator:
  path: "data/pdfmodel"
  default_generator: "word_to_pdf"
  data_models:
    - facture
    - commande

为了完成标签列表,使用在pdf_generator.yaml文件中声明的Symfony注解。例如

<?php

namespace App\Entity

use Symfony\Component\Serializer\Annotation\Groups;

class Commande
{
    /**
     * @Groups({"commande"})
     */
    private $type;
}

如果您只有一个模板或一个模块使用多个模板,您不必在pdf_generator.yaml文件中声明"data_models"。默认情况下,Symfony注解将是"pdfgenerator"。然后,在您的实体中,您将拥有

<?php

namespace App\Entity

use Symfony\Component\Serializer\Annotation\Groups;

class Commande
{
    /**
     * @Groups({"pdfgenerator"})
     */
    private $type;
}

使用方法

您可以使用PDF生成器与数据库或直接在代码中使用

<?php
/**
 * @Route("/pdf")
 */
public function pdf(PdfGenerator $generator, UserRepository $userRepository)
{
    $data = [];
    foreach($userRepository->findAll() as $user){
        $data[] = ['name' => $user->getName()];
    }
    //create an response by Bdd
    return $generator->generateResponse('MYMODELCODE', $data);
    //or
    //create an PdfMerger by Bdd
    $generator->generate('MYMODELCODE', $data)->merge('pdf.pdf','F');
    //or
    //create an PdfMerger by ressource
    return $generator->generateByRessourceResponse(TcpdfGenerator::getName(), MyTcpdfClass::class, $data);
    //or
    //create an response by ressource
    $generator->generateByRessource(TcpdfGenerator::getName(), MyTcpdfClass::class, $data)->merge('pdf.pdf','F');
}

您可以使用PdfMerger创建TcpdfFpdi(Tcpdf和Fpdi)的实例

<?php
$pdfMerger = $generator->generate('MYMODELCODE', $data)->merge('pdf.pdf','F');
$pdf = $pdfMerger->toTcpdfFpdi();
$pdf->addPage('P');
$pdf->writeHTML('Hello', true, 0, true, 0);
$pdf->Output('file.pdf', 'F');
return new ResponseBinaryFile('file.pdf');

与实体一起使用

更改PDF生成器配置中的"类"为"App/Entity/MyModelPdf"

<?php

namespace App\Entity;

use Vich\UploaderBundle\Mapping\Annotation as Vich;
use Doctrine\ORM\Mapping as ORM;
use Lle\PdfGeneratorBundle\Entity as PDF;

/**
 *
 * @ORM\Table(name="lle_pdf_model", indexes={@ORM\Index(name="code_idx", columns={"code"})})
 * @ORM\Entity
 * @Vich\Uploadable
 */
class MyModelPdf implements PDF\PdfModelInterface
{
    use PDF\PdfModelTrait;
}

与数据库一起使用

模型是具有以下内容的PdfModel:

代码、资源、名称、类型和描述

您可以使用以下方式创建模型:

php bin/console lle:pdf-generator:create-model

!!警告:如果您使用自己的类并且该类有其他带有约束的字段,则命令将无法工作。!!

创建自己的类型

您可以创建多种PDF类型(已存在tcpdf和word_to_pdf)

  • word_to_pdf(资源是.docx格式Microsoft Word XML的路径)
  • tcpdf(资源是扩展Lle\PdfGeneratorBundle\Lib\Pdf(Tcpdf和Fpdi)的类)

您可以通过扩展Lle\PdfGeneratorBundle\Generator\AbstractPdfGenerator的类创建自己的类型

AbstractPdfGenerator实现了Lle\PdfGeneratorBundle\Generator\PdfGeneratorInterface(自动标记为lle.pdf.generator)

    public static function getName():string; //name of type
    public function generate(string $source, iterable $params, string $savePath):void; //generate the pdf with the ressource $source and parameters $params in a tmp file $savePath
    public function getRessource(string $pdfPath, string $modelRessource): string; //calcule the ressource with pdfPath or not (the ressource can be an class name for exemple)

示例

<?php
class TcpdfGenerator extends AbstractPdfGenerator
{

    private $pdfPath;
    
    public function generate(string $source, iterable $params, string $savePath):void{
        $reflex = new \ReflectionClass($source);
        $pdf = $reflex->newInstance();
        if ($pdf instanceof Pdf) {
            $pdf->setRootPath($this->pdfPath);
            $pdf->setData($params['vars']);
            $pdf->initiate();
            $pdf->generate();
            $pdf->setTitle($pdf->title());
        } else {
            throw new \Exception('PDF GENERATOR ERROR: ressource '.$source.' n\'est pas une class PDF');
        }
        $pdf->output($savePath, 'F');
    }

    //$pdfPath is in config lle_pdf_generator.path (default:data/pdfmodel)
    public function getRessource(string $pdfPath, string $modelRessource): string{
        $this->pdfPath = $pdfPath;
        return $modelRessource;
    }

    public static function getName(): string{
        return 'tcpdf';
    }
}

使用tcpdf类型

tcpdf的示例

<?php

namespace App\Service\Pdf;

use Lle\PdfGeneratorBundle\Lib\Pdf;

class MyTcpdfClass extends Pdf
{

    //$this->rootPath is in config lle_pdf_generator.path (default:data/pdfmodel)
    public function init()
    {
        $this->setSourceFile($this->rootPath . 'background.pdf');
    }
    
    public function myColors()
    {
        return ['blanc' => 'FFFFFF','default'=> '000000', 'red' => 'FF0000'];
    }

    //the fonts is in $this->rootPath.'/fonts'
    public function myFonts()
    {
        return ['titre' => ['size'=>12,'color'=>'noir','family'=>'courier', 'style'=>'BU']];
    }

    public function generate()
    {
        $this->AddPage('P');
        $this->showGrid(5); //is an debug function which show an gride by 5px
        $this->changeFont('titre');
        $this->w(10,10,'Hello <b>'. $this->data['name'] .'</b>');
    }

    public function footer()
    {
    }
}
<?php
/**
 * @Route("/pdf")
 */
public function pdf(PdfGenerator $generator, UserRepository $userRepository)
{
    $data = [];
    foreach($userRepository->findAll() as $user){
        $data[] = ['name' => $user->getName()];
    }
    return $generator->generateByRessourceResponse(TcpdfGenerator::getName(), MyTcpdfClass::class, $data);
}

您可以在数据库中创建一个PDF模型,资源为"App\Service\Pdf\MyTcpdfClass",代码为MYTCPDF类型"tcpdf"

<?php
/**
 * @Route("/pdf")
 */
public function pdf(PdfGenerator $generator, UserRepository $userRepository)
{
    $data = [];
    foreach($userRepository->findAll() as $user){
        $data[] = ['name' => $user->getName()];
    }
    return $generator->generateResponse('MYTCPDF', $data);
}

使用word_to_pdf(格式Microsoft Word XML)

创建一个包含"Hello ${name}"的.docx文件/data/pdfmodel/test.docx

<?php
/**
 * @Route("/pdf")
 */
public function pdf(PdfGenerator $generator, UserRepository $userRepository)
{
    $data = [];
    foreach($userRepository->findAll() as $user){
        $data[] = ['name' => $user->getName()];
    }
    return $generator->generateByRessourceResponse(WordToPdfGenerator::getName(), 'test.docx', $data);
}

您可以在数据库中创建一个PDF模型,资源为"test.docx",代码为MYDOC类型"word_to_pdf"

<?php
/**
 * @Route("/pdf")
 */
public function pdf(PdfGenerator $generator, UserRepository $userRepository)
{
    $data = [];
    foreach($userRepository->findAll() as $user){
        $data[] = ['name' => $user->getName()];
    }
    return $generator->generateResponse('MYDOC', $data);
}

您可以使用变量${@img[logo]:100x200}或${@img[logo]}来创建图像。(正则表达式是#^@img[(\w+)](:(\d+)x( \d+))?$#)

$generator->generateResponse('MYDOC', [['logo'=> 'logo.png']]);

搜索到{{lle_pdf_generator.path}}/logo.png,因此默认是data/pdfmodel/logo.png

$generator->generateResponse('MYDOC', [['logo'=> '/logo.png']]);

搜索到/logo.png

https://phpword.readthedocs.io/en/latest/templates-processing.html

##签名PDF

使用Lle\PdfGeneratorBundle\Lib\Signature类,您可以对PDF响应或PdfMerge进行签名

创建签名

openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt
openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12
<?php
$password = '***';
$info = [
    'Name' => 'name',
    'Location' => 'location',
    'Reason' => 'reason',
    'ContactInfo' => 'url',
];
$signature = new Signature($generator->getPath().'cert/tcpdf.crt', $password, $info);

您还可以添加带有签名的绘制

<?php
/*...*/
$picture = 'signature.png';
$signature = new Signature($generator->getPath().'cert/tcpdf.crt', $password, $info, $pictur);
//or
$pos = [
    'w' => 40, //width default 40
    'h' => 20, //heght default 20
    'x' => 10, //x default pageWidth - w
    'y' => 10, //y default pageHeight - (h*2+5)
    'p' => 1 //page default last page
];
$signature = new Signature($generator->getPath().'cert/tcpdf.crt', $password, $info, $pictur, $pos);

您还可以添加段或点来创建签名图片

<?php
$signature = new Signature($certif, $password, $info);

$signature->setSegments([[$x1,$y1],[$x2,$y2]], $pos);
//or
$signature->setPoints([$x1,$y1,$x2,$y2], $pos);
//or
$signature->setImage('signe.png', $pos);

$signature->setPosition($pos); // you can use it also

PDF响应

<?php
return $generator->generateByRessourceResponse(WordToPdfGenerator::getName(), 'test.docx', $data, [$signature]);
//or
return $generator->generateResponse('MYMODELCODE', $data, [$signature]);

PDF合并

pdfMerge是生成器返回的实例的类

<?php
$pdfMerger = $generator->generateByRessource(WordToPdfGenerator::getName(), 'test.docx', $data);
//or
$pdfMerger = $generator->generate('MYMODELCODE', $data);
$pdf = $generator->signes($pdfMerger, [$signature]); //return an TcpdfFpdi (signe($pdfMerger, $signature) exist also)
$pdf->Output('My pdf', 'D'); // return a signed pdf
$pdfMerger->merge('My pdf', 'D'); // return a unsigned pdf

您不能对pdfMerger进行签名,您必须通过TcpdfFpdi传递。PdfMerger实例永远不能签名

您可以使用$generator->signeTcpdfFpdi($pdf, $signature)继续对TcpdfFpdi进行签名

您还可以直接使用签名实例来对PdfMerger或TcpdfFpdi进行签名。

<?php
$pdfMerger = $generator->generate('MYMODELCODE', $data);
$signature->signe($pdfMerger)->Output('My pdf', 'D');

或者

<?php
$pdfMerger = $generator->generate('MYMODELCODE', $data);
$pdf = $pdfMerger->toTcpdfFpdi();
$pdf = $signature->signeTcpdfFpdi($pdf);
$pdf = $signature2->signeTcpdfFpdi($pdf);
$pdf->Output('My pdf', 'D');

您不能在PdfMerger中使用多个签名。

可迭代数据

创建一个.docx文件并创建2个表格(1行,3列)

  • 第一个表格的单元格1写${eleves.nom},单元格2写${eleves.etablissement.nom},单元格3写${@img[eleves.logo]}
  • 第二个表格的单元格1写${users.[nom]},单元格2写${users.[adresse][rue]},单元格3写${@img[users.[logo]])

使用Lle\PdfGeneratorBundle\Lib\PdfIterable类将文件保存为myiterable.docx

<?php
$data = [
    'eleves' => new PdfIterable($this->em->getRepository(Eleve::class)->findAll()),
    'users' => new PdfIterable([['nom'=>'saenger','adresse'=>['rue'=>'rue du chat'], 'logo'=>'logo.png'], ['nom'=>'boehler', 'adresse'=>['rue'=>'rue du chien'], 'logo.png']]),            
];
return $generator->generateByRessourceResponse(WordToPdfGenerator::getName(), 'myiterable.docx', $data);

显示它

警告:只有第一层数据可以作为PdfIterable,您不能使用${etablissement.eleves}

<?php
$data = [
    'etablissement' => $etablissement,
    'eleves' => PdfIterable($etablissement->getEleves())
];

理解属性(单词转PDF)

属性通过propertyAccesor(Symfony)读取,但第一个位于两个“[]”之间:[first].rest

the vars ${eleve.etablissement.nom} -> $propertyAccess->getValue($params, '[eleve].etablissement.nom')
the vars ${eleve.etablissement[nom]} -> $propertyAccess->getValue($params, '[eleve].etablissement[nom]')

!!!警告:如果创建自己的类型,请使用相同的系统!!!

合并多个模型

<?php
return $generator->generateByRessourceResponse(
    TcpdfGenerator::getName(), 
    [MyTcpdfClass::class,AnotherTcpdfClass::class], 
    $data);
<?php
return $generator->generateByRessourceResponse(
    [TcpdfGenerator::getName(),WortdToPdfGenerator::getName()], 
    [MyTcpdfClass::class,'mydoc.docx'], 
    $data);

在数据库中

INSERT INTO `lle_pdf_model` (`code`, `path`, `type`) VALUES
('RELANCE_1ANS', 'mydoc.docx,App\\Service\\Pdf\\LotInvitation', 'word_to_pdf,tcpdf')

默认类型总是第一个(这里为"word_to_pdf")

如果没有定义任何类型,则类型为lle_pdf_generator.default_generator配置

迁移到pdf生成器v3

在v3中,向后兼容性有少量中断。以下是迁移步骤

  • @LlePdfGeneratorBundle/Resources/routing/routes.yaml现在在@LlePdfGeneratorBundle/Resources/config/routes.yaml
  • Lle\PdfGeneratorBundle\Entity\PdfModelCustomFileTraitLle\PdfGeneratorBundle\Entity\PdfModelTrait替代,并且现在特性包含$file属性
  • 路由lle_pdf_generator_show_ressource改为lle_pdf_generator_show_model
  • 路由lle_pdf_generator_show_pdf改为lle_pdf_generator_download_model