如何使用Angular 7生成PDF?

51

我需要从用户输入的数据中生成PDF报告,并将其保存在一个对象中。目前,我找到的方法是生成HTML页面,然后截图并将其转换为PDF。我想直接从存储在对象中的数据生成PDF。有什么建议吗?

8个回答

35

你可以使用jspdf

演示

.html

<div id="pdfTable" #pdfTable>
  <h1>{{name}}</h1>

  <table>
    <tr>
      <th>Company</th>
      <th>Contact</th>
      <th>Country</th>
    </tr>
    <tr>
      <td>Alfreds Futterkiste</td>
      <td>Maria Anders</td>
      <td>Germany</td>
    </tr>
    <tr>
      <td>Centro comercial Moctezuma</td>
      <td>Francisco Chang</td>
      <td>Mexico</td>
   </tr>
    <tr>
      <td>Ernst Handel</td>
      <td>Roland Mendel</td>
      <td>Austria</td>
    </tr>
    <tr>
      <td>Island Trading</td>
      <td>Helen Bennett</td>
      <td>UK</td>
    </tr>
    <tr>
      <td>Laughing Bacchus Winecellars</td>
      <td>Yoshi Tannamuri</td>
      <td>Canada</td>
    </tr>
    <tr>
      <td>Magazzini Alimentari Riuniti</td>
      <td>Giovanni Rovelli</td>
      <td>Italy</td>
    </tr>
  </table>
</div>

<div> <button  (click)="downloadAsPDF()">Export To PDF</button></div>

.ts

  public downloadAsPDF() {
    const doc = new jsPDF();

    const specialElementHandlers = {
      '#editor': function (element, renderer) {
        return true;
      }
    };

    const pdfTable = this.pdfTable.nativeElement;

    doc.fromHTML(pdfTable.innerHTML, 15, 15, {
      width: 190,
      'elementHandlers': specialElementHandlers
    });

    doc.save('tableToPdf.pdf');
  }

有没有办法将pdf转换为base64而无需下载表单? - Sai Manoj
doc.output('blob')将返回base64。 - nipun-kavishka
1
但这并不允许您创建样式或对齐方式。感觉这是制作PDF的一种很差的方式...指定x-y位置等。 - Jitin
1
使用doc.html代替doc.fromHTML - sebas2day
能够解释一下这段代码在做什么就太好了。例如,specialElementHandlers是什么,为什么需要它们。它们似乎在最新版本中不存在。 - mwilson
现在 jspdf 2.5.1 版本已经发布。方法 fromHTML 已更改为 html: doc.html(cheque, { callback: (doc) => { doc.save('doc.pdf');}, x: 10, y: 10,}); 您可以从这里获取所有文档:https://raw.githack.com/MrRio/jsPDF/master/docs/index.html - Odilbek Utamuratov

24

您需要在一个 DIV 中显示要打印的内容。显示内容后,使用以下代码,此代码已经在我的项目中使用并起作用

步骤1:

运行以下命令安装 npm 包

> npm install jspdf
> npm install html2canvas

步骤2:

在app.components.ts中导入已安装的包。我没有在constructor()中导入那些包。

> import * as jspdf from 'jspdf';
> import html2canvas from 'html2canvas';

步骤 3:

为需要导出为 PDF 的 HTML div 添加一个 id。同时添加一个可以激活此功能的按钮。

<div id="MyDIv" style="margin-left: 45px;" class="main-container">
        
</div>
<div class="icon_image " title="Share As PDF" (click)="exportAsPDF('MyDIv');"><img src="assets/img/pdf.png"></div>

第四步:

按以下方式调用生成PDF的代码

    exportAsPDF(divId)
    {
        let data = document.getElementById('divId');  
        html2canvas(data).then(canvas => {
        const contentDataURL = canvas.toDataURL('image/png')  // 'image/jpeg' for lower quality output.
        let pdf = new jspdf('l', 'cm', 'a4'); //Generates PDF in landscape mode
        // let pdf = new jspdf('p', 'cm', 'a4'); Generates PDF in portrait mode
        pdf.addImage(contentDataURL, 'PNG', 0, 0, 29.7, 21.0);  
        pdf.save('Filename.pdf');   
      }); 
    }

3
这样做是可行的,但请记住,如果您不需要高质量的输出,可以使用image/jpeg替代PNG。这将把一个约25MB大小的两页文件减小到只有1MB。 - Hasher
1
@Hasher,我已经编辑了代码并将其添加到注释中。 - Basil
有没有其他库可以实现这个功能?我有一个包含多个子组件和多页图表的组件.. 我能否只在父组件中放置一些代码将页面转换为单个pdf?而不是像这样定义单独的图表? - ScipioAfricanus
@ScipioAfricanus,我们也可以选择替代库。您可以定义一个服务,然后在父组件中重复使用它。 - Basil
嘿,我使用jsPDF和html2canvas让它工作了。只是需要稍微调整一下。 - ScipioAfricanus
@ScipioAfricanus,在这里我们也只使用了同样的两个库。 - Basil

9

这是一个常见的需求,但您没有提供有关PDF具体要求的详细信息。您可以参考以下内容:

https://www.npmjs.com/package/angular-pdf-generator

或者

https://parall.ax/products/jspdf

作为客户端选项。根据您生成PDF后可能想要做什么,还有其他选项可供选择。例如,如果您有服务器,则将数据发送回服务器以生成PDF并将其存储在数据库中可能更有意义。希望这可以帮到您。

Angular-pdf-generator 有没有文档或教程可用?看起来在 npmjs 页面下的两个 GitHub 链接都无法使用。 - 06serseri

7

用户输入的数据可以在HTML页面中显示,并可转换为PDF格式。如果您想要在HTML模板中绑定对象,请使用handlebars。然后可以使用jspdf和html2canvas在angular 2/4/6/7中将HTML模板转换为PDF格式。HTML内容应按以下方式处理。此代码支持创建多页PDF。

这里的数据--> htmlcontent

TS:

generatePdf(data) {
     html2canvas(data, { allowTaint: true }).then(canvas => {
      let HTML_Width = canvas.width;
      let HTML_Height = canvas.height;
      let top_left_margin = 15;
      let PDF_Width = HTML_Width + (top_left_margin * 2);
      let PDF_Height = (PDF_Width * 1.5) + (top_left_margin * 2);
      let canvas_image_width = HTML_Width;
      let canvas_image_height = HTML_Height;
      let totalPDFPages = Math.ceil(HTML_Height / PDF_Height) - 1;
      canvas.getContext('2d');
      let imgData = canvas.toDataURL("image/jpeg", 1.0);
      let pdf = new jsPDF('p', 'pt', [PDF_Width, PDF_Height]);
      pdf.addImage(imgData, 'JPG', top_left_margin, top_left_margin, canvas_image_width, canvas_image_height);
      for (let i = 1; i <= totalPDFPages; i++) {
        pdf.addPage([PDF_Width, PDF_Height], 'p');
        pdf.addImage(imgData, 'JPG', top_left_margin, -(PDF_Height * i) + (top_left_margin * 4), canvas_image_width, canvas_image_height);
      }
       pdf.save("HTML-Document.pdf");
    });
  }

HTML:

<div #contentToConvert> Some content here </div>

<button (click)="generatePdf(contentToConvert)">Generate PDF</button>

你的回答真是太棒了,我的朋友!它对我来说完美地解决了问题! - Andre

4

之前的回答对我无效,后来我找到了这个解决方案。在 angular 9 上测试通过。

jspdf 版本 "^2.3.0"

npm install jspdf --save
npm install @types/jspdf --save-dev
  1. 在组件的HTML文件中创建表格。<table class="table" id="content" #content></table>

  2. 在组件的typescript文件中获取表格。 @ViewChild('content') content: ElementRef;

savePdf() {
    const doc = new jsPDF();

    const pdfTable = this.content.nativeElement;

    doc.html(pdfTable.innerHTML, {
      callback(rst) {
        rst.save('one.pdf');
      },
      x: 10,
      y: 10
    });

  }

尝试这个解决方案。


3
您可以使用PDFMAKE插件进行打印。 通过此链接在Git上访问 https://www.npmjs.com/package/ng-pdf-make。 安装后非常容易,只需在将要打印的组件中导入并添加这些库:

import * as pdfMake from "pdfmake/build/pdfmake";

import * as pdfFonts from "pdfmake/build/vfs_fonts";

After that, create a function that will print your document such as:
            
            printDoc(){
            
            pdfMake.vfs = pdfFonts.pdfMake.vfs;
             let dd = {
           
// Here you put the page settings as your footer, for example:
                      footer: function(currentPage, pageCount) {
                          return [
                            {
                              text:
                                currentPage.toString() +
                                " de " +
                                pageCount +
                                "\n" +
                                "Footer Name",
                              alignment: currentPage ? "center" : "center"
                            }
                          ];
                        },
// Here you can enter the page size and orientation:
                pageSize: "A4",
                pageOrientation: "Portrait",
            //in pageOrientation you can put "Portrait" or "landscape"
        
// start the body of your impression:
        content: [
        
                     {
                        table: {
                          widths: ["*"],
                          body: [
                            [
                              {
                                text: [
                                  { text: `${'Text Example'}`, bold: true }
                                ],
                                style: "header",
                                width: "150",
                                alignment: "left",
                                border: [true, true, true, false],
                                margin: [0, 15, 0, 15]
                              }
                            ]
                          ]
                        }
                      },
        ]
        
            }
        
        pdfMake.createPdf(dd).download("Name of Print");
            }

1

安装依赖项:

npm install jspdf
npm install html2canvas

my service

import * as jspdf from 'jspdf';
import html2canvas from 'html2canvas';

...
// make gap from left and right sides. In my case container has margin: 0
// that means that after pdf generation, content has no gaps, looks a bit ugly.
pdfView = false;
constructor(private cdRef: ChangeDetectorRef) {}
...

exportAsPdf() {
  // make gap from left and right sides. In my case container has margin: 0
  // that means that after pdf generation, content has no gaps, looks a bit ugly.
  this.pdfView = true;

  // workaround to wait until pdfView will affect page;
  setTimeout(() => {
    let article = document.getElementById('article');

    if (article) {
      const rect = article.getClientRects();
      const h = rect.item(0)!.height;
      const w = rect.item(0)!.width;

      const orientation = h > w ? 'portrait' : 'landscape';

      html2canvas(article).then((canvas) => {
        const contentDataURL = canvas.toDataURL('image/jpeg');
        let pdf = new jspdf.jsPDF(orientation, 'px', [w, h]);
        pdf.addImage(contentDataURL, 'jpeg', 0, 0, w, h);
        pdf.save('Filename.pdf');

        // back margin to 0 after generation.
        this.pdfView = false;
        this.cdRef.detectChanges();
      });
    }
  }, 0);
}

我的HTML
// tailwind class name, mx-2 === margin (left and right) = 0.5rem;
<div id="article" class="mx-2" [ngClass]="{ 'mx-2': servce.pdfView }"> ... </div>

展示它如何工作

https://youtu.be/Mwe5nGPhhB8


-1

我找到的最可靠和简单的解决方案是使用html2pdf.js。我已经在angular 8中使用它了。我尝试创建stackblitz,但它有一些分辨率错误。因此,我在下面粘贴代码:

import * as html2pdf from 'html2pdf.js';

let options = {
        margin: 1,
        filename: 'receipt.pdf',
        html2canvas: { 
          dpi: 192,
          letterRendering: true, 
          allowTaint: true, 
          useCORS: true, 
          logging: false, 
          scrollX: 0,
          scrollY: 0 
        },
        jsPDF: {
          orientation: 'portrait',
          unit: 'cm',
          format: 'a4'
        }
      }; 
      html2pdf().set(options).from(nativeElement).save().finally(() => anyfunc());

这对我有效。这里省略了关于元素的引用,可以通过在元素上设置 ID 并使用 var nativeElement = document.getElementById('name-used-as-id'); 进行选择来完成。 - Superman.Lopez
转换后的PDF文本是否可选择或搜索?还是被转换为图像? - zorlac
@zorlac,它已转换为图像。 - kaushik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接