【问题标题】:Downloading to PDF in Angular with puppeter it is saving empty pdf使用 puppeter 在 Angular 中下载到 PDF 它正在保存空 pdf
【发布时间】:2021-03-03 17:22:29
【问题描述】:

我正在尝试根据我在 HTML 中的设计生成 PDF。 我正在使用pupeeter 和后端mongodb 我在前端有不同的模板,它们有不同的 HTML。 我试图用puppeeter 保存,但我无法更进一步。 这是我尝试过的代码。

PDF Controller

const puppeteer = require('puppeteer');
const fs = require('fs');

module.exports =  {
    async  createPDF () {
  // launch a new chrome instance
  const browser = await puppeteer.launch({
    headless: true
  })

  // create a new page
  const page = await browser.newPage()

  // set your html as the pages content
  const html = fs.readFileSync(`${__dirname}/template.html`, 'utf8')
  // create a pdf buffer
  const pdfBuffer = await page.pdf({
    format: 'A4'
  })

  // or a .pdf file
  await page.pdf({
    format: 'A4',
    path: `${__dirname}/cv.pdf`
  })

  // close the browser
  await browser.close()
}
}

routes.post("/pdf", PDFController.createPDF);

FE 中的服务。

@Injectable({providedIn: "root"})
export class PdfService {
  public baseUrl = environment.backend;


  constructor(private http: HttpClient) {
  }
  public setPDF(data) {
    return this.http.post(`${this.baseUrl}/pdf`, data).subscribe(res => console.log(res));
  }
  getPdf() {


    const httpOptions = {
      responseType: "blob" as "json",

    };

    return this.http.get(`${this.baseUrl}/help/cv`, httpOptions);
  }

}

这是我的 TS。

    public downloadPdf() {
    this.pdfService.setPDF(this.model);
 
      this.pdfService.getPdf().subscribe((data) => {

          console.log(data);

      });

  }

这是我的“HTML”代码。

<app-paginated-view [pageSize]="'A4'" *ngIf="model && model.theme === 'firstTemplate'"
                      [pageNumbers]="model?.showPageNumbers"
                      class="Grid-grid-column" id="content"
                      [style.color]="model.style?.color">
    <ng-container>
      <div class="Grid-grid-row" pageContent (click)="setFirstCat()" class="row" #content
           [ngClass]="{ 'isCatActive': selectedFirstCat}">
        <div class="Grid-grid-column Grid-grid-column-12">
          <div class="Header-header-header Header-header-minHeight first-template-header">
            <div class="Title-title-titleWrapper first-template-titleWrapper">
              <h4 *ngIf="model?.hideName">{{model.personalData[0].firstName}} {{model.personalData[0].lastName}}</h4>
              <h5>{{model?.newJobTitle}}</h5>
              <div [innerHtml]="model?.description"></div>
            </div>
            <div class="Photo-photo-photoWrapper" *ngIf="model?.showCVPhoto">
              <div class="Photo-photo-photo first-template-photo">
                <img [src]="model?.photo" height="100" style="cursor:  pointer">
              </div>
            </div>
          </div>
          <div *ngIf="selectedFirstCat">
            <div clickOutside (clickOutside)="removeClick()">
              <ul>
                <li class="fa fa-pencil addIconTop" (click)="editHeaderDialog({edit: true, model: model})"></li>
                <li class="fa fa-plus addIconBottom" (click)="openDialog({model: model})"></li>
              </ul>
            </div>
          </div>
        </div>
      </div>
    </ng-container>
    <ng-container class="Grid-grid-grid">
      <ng-container class="Unit-unit-unitGroup" *ngFor="let personalData of model?.personalData; let id = index">
        <div pageContent *ngIf="personalData.visible">
          <div class="Unit-unit-unitGroup" pageContent
               [ngClass]="{ 'isCatActive': selectedCategory === category.PersonalData}">
            <div *ngIf="selectedCategory === category.PersonalData">
              <div clickOutside (clickOutside)="removeClick()">
                <ul>
                  <li class="fa fa-plus addIconTop" (click)="openDialog({model: model})"></li>
                  <li class="fa fa-plus addIconBottom" (click)="openDialog({model: model})"></li>
                  <li class="fa fa-arrow-down moveIconDown"></li>
                  <li class="fa fa-arrow-up moveIconTop"></li>
                </ul>
              </div>
            </div>

            <div pageContent class="col-md-12" (click)="setCategory(category.PersonalData)">
              <div class="row height">
                <div class="col-md-4 col-sm-6 text-right tLine" [style.background]="model.style.color"></div>
                <h3 class="first-template-paragraphTitle Paragraph-paragraph-title height" [style.color]="model.style.color">
                  <div class="Text-text-wrapper">
                    <div class="Text-Text-text" >{{'category.PersonalData' | translate}}</div>
                  </div>
                </h3>
              </div>
            </div>
            <div pageContent class="container-fluid">
              <app-personal-data [personalData]="personalData" [model]="model" [id]="id"
                                 (deselectCategory)="test($event)">
              </app-personal-data>
            </div>
          </div>
        </div>
      </ng-container>

      <!-- Career Component -->
      <ng-container *ngFor="let careers of model?.careers; let id = index" class="Unit-unit-unitGroup">
        <div *ngIf="selectedCategory === category.Career">
          <div clickOutside (clickOutside)="removeClick()">
            <ul>
              <li class="fa fa-plus addIconTop" (click)="openDialog({model: model})"></li>
              <li class="fa fa-plus addIconBottom" (click)="openDialog({model: model})"></li>
              <button (click)="deleteCareerCategory(id)" class="btn"><i
                class="fa fa-trash deleteIconRight"></i></button>
              <li class="fa fa-arrow-down moveIconDown"></li>
              <li class="fa fa-arrow-up moveIconTop"></li>
            </ul>
          </div>
        </div>
        <div pageContent class="col-md-12" (click)="setCategory(category.Career)">
          <div class="row height">
            <div class="col-md-4 col-sm-6 text-right tLine" [style.background]="model.style.color"></div>
            <h3 class="first-template-paragraphTitle Paragraph-paragraph-title height" [style.color]="model.style.color">
              <div class="Text-text-wrapper">
                <div class="Text-Text-text">{{'category.Career' | translate}}</div>
              </div>
            </h3>
          </div>
        </div>
        <ng-container *ngFor="let careerObj of careers.subCategories; let i = index">
          <div pageContent class="container-fluid">
            <div pageContent [ngClass]="{ 'isActive': selectedCareerIndex === i}">
              <div class="Line-line-container" (click)="setCareerIndex(i)">
                <div class="Line-line-line">
                  <div class="Field-field-fieldBase first-template-fieldField">
                    <div class="Text-Text-wrapper">
                      <div pageContent class="Text-Text-text">
                        {{careerObj.startDate | date:'MM/yyyy'}}
                        <div class="float-right" *ngIf="!careerObj.today">{{careerObj.endDate | date:'MM/yyyy'}}</div>
                        <div class="float-right" *ngIf="careerObj.today">{{'career.present' | translate}}</div>
                      </div>
                    </div>
                  </div>
                  <div class="Field-field-fieldBase first-template-fieldValue">
                    <div class="Text-Text-wrapper">
                      <div pageContent class="Text-Text-text-wrapper">
                        <b>{{careerObj.role}}</b>
                      </div>
                    </div>
                  </div>
                  <div class="Field-field-fieldBase first-template-fieldValue">
                    <div class="Text-Text-wrapper">
                      <div pageContent class="Text-Text-text-wrapper">
                        {{careerObj.name}}
                      </div>
                    </div>
                  </div>
                  <div class="Field-field-fieldBase first-template-fieldValue">
                    <div class="Text-Text-wrapper">
                      <div pageContent class="Text-Text-text-wrapper" aria-multiline="true"
                           [innerHTML]="careerObj.description">
                      </div>
                    </div>
                  </div>
                  <div class="Field-field-fieldBase first-template-fieldValue" *ngIf="careerObj.showCompanyUrl">
                    <div class="Text-Text-wrapper">
                      <div pageContent class="Text-Text-text-wrapper">
                        <a target="_blank" [href]="careerObj.companyUrl">{{careerObj.companyUrl}}
                        </a>
                      </div>
                    </div>
                  </div>

                  <ng-container pageContent *ngIf="selectedCareerIndex === i">
                    <div clickOutside (clickOutside)="removeClick()">
                      <ul>
                        <li class="fa fa-pencil addIconTop"
                            (click)="editCareer({edit: true, career: careerObj, model: model})">
                        </li>
                        <li class="fa fa-plus addIconBottom"
                            (click)="addCareer({edit: false, model: model, career: false})"></li>
                        <button [disabled]="careers.subCategories.length < 2" (click)="deleteCareerSubCategory(i)"
                                class="btn"><i class="fa fa-trash deleteIconRight"></i></button>
                        <li class="fa fa-arrow-down moveIconDown"></li>
                        <li class="fa fa-arrow-up moveIconTop"></li>
                      </ul>
                    </div>
                  </ng-container>
                </div>
              </div>
            </div>
          </div>
        </ng-container>
        <ng-container *ngFor="let emptyObj of careers.emptySubContents; let iEmpty = index">
          <app-empty-object pageContent [emptyObj]="emptyObj" [iEmpty]="iEmpty" [model]="model" [isFromCareer]="true">
          </app-empty-object>
        </ng-container>
      </ng-container>

      <!--Education Component-->
      <ng-container *ngFor="let education of model?.education; let index = index" class="Unit-unit-unitGroup">
        <ng-container *ngIf="education.subCategories.length > 0">
          <div *ngIf="selectedCategory === category.Education"
               [ngClass]="{ 'isCatActive': selectedCategory === category.Education}">
            <div clickOutside (clickOutside)="removeClick()">
              <ul>
                <li class="fa fa-plus addIconTop" (click)="openDialog({model: model})"></li>
                <li class="fa fa-plus addIconBottom" (click)="openDialog({model: model})"></li>
                <li class="fa fa-trash deleteIconRight" (click)="deleteEducationCategory(index)"></li>
                <li class="fa fa-arrow-down moveIconDown"></li>
                <li class="fa fa-arrow-up moveIconTop"></li>
              </ul>
            </div>
          </div>
          <div pageContent class="col-md-12" (click)="setCategory(category.Education)">
            <div class="row height">
              <div class="col-md-4 col-sm-6 text-right tLine" [style.background]="model.style.color"></div>
              <h3 class="first-template-paragraphTitle Paragraph-paragraph-title height">
                <div class="Text-text-wrapper">
                  <div class="Text-Text-text">{{'category.Education' | translate}}</div>
                </div>
              </h3>
            </div>
          </div>
          <ng-container *ngFor="let educationObj of education.subCategories; let i = index">
            <div pageContent class="container-fluid">
              <div pageContent [ngClass]="{ 'isActive': selectedIndex === i}">
                <div pageContent class="Line-line-container" (click)="setIndex(i)">
                  <div class="Line-line-line">
                    <div class="Field-field-fieldBase first-template-fieldField">
                      <div class="Text-Text-wrapper">
                        <div pageContent class="Text-Text-text">
                          {{educationObj.startDate | date:'MM/yyyy'}}
                          <div class="float-right" *ngIf="!educationObj.today">{{educationObj.endDate | date:'MM/yyyy'}}
                          </div>
                          <div class="float-right" *ngIf="educationObj.today">{{'present' | translate}}</div>
                        </div>
                      </div>
                    </div>
                  </div>
                  <div class="Field-field-fieldBase first-template-fieldValue">
                    <div class="Text-Text-wrapper">
                      <div class="Text-Text-text-wrapper">
                        <b>{{educationObj.title}}</b>
                      </div>
                    </div>
                  </div>
                  <div class="Field-field-fieldBase first-template-fieldValue">
                    <div class="Text-Text-wrapper">
                      <div class="Text-Text-text-wrapper">
                        {{educationObj.name}}
                      </div>
                    </div>
                  </div>
                  <div class="Field-field-fieldBase first-template-fieldValue">
                    <div class="Text-Text-wrapper">
                      <div class="Text-Text-text-wrapper" aria-multiline="true" [innerHTML]="educationObj.description">
                      </div>
                    </div>
                  </div>
                  <ng-container pageContent *ngIf="selectedIndex === i">
                    <div clickOutside (clickOutside)="removeClick()">
                      <ul>
                        <li class="fa fa-pencil addIconTop"
                            (click)="editEducation({edit: true, education: educationObj, model: model})"></li>
                        <button class="btn"><i class="fa fa-plus addIconBottom"
                                               (click)="addEducation({edit: false, model: model})"></i></button>
                        <button [disabled]="education.subCategories.length === 1"
                                (click)="deleteEducationSubCategory(i)" class="btn"><i
                          class="fa fa-trash deleteIconRight"></i></button>
                        <li class="fa fa-arrow-down moveIconDown"></li>
                        <li class="fa fa-arrow-up moveIconTop"></li>
                      </ul>
                    </div>
                  </ng-container>
                </div>
              </div>
            </div>
            <!--- <app-education pageContent [educationObj]="educationObj" [id]="i" [education]="education" [model]="model">
              </app-education> -->
          </ng-container>
        </ng-container>
      </ng-container>


      <!-- Skills Component-->
      <ng-container *ngFor="let skills of model?.skills; let i = index" class="Unit-unit-unitGroup">
        <div class="Unit-unit-unitGroup" pageContent [ngClass]="{ 'isCatActive': selectedCategory === category.Skills}">
          <div *ngIf="selectedCategory === category.Skills" (clickOutsideInner)="removeClick()">
            <div clickOutside (clickOutside)="removeClick()">
              <ul>
                <li class="fa fa-plus addIconTop" (click)="openDialog({model: model})"></li>
                <li class="fa fa-plus addIconBottom" (click)="openDialog({model: model})"></li>
                <li class="fa fa-trash deleteIconRight" (click)="deleteSkillsCategory(i)"></li>
                <li class="fa fa-arrow-down moveIconDown"></li>
                <li class="fa fa-arrow-up moveIconTop"></li>
              </ul>
            </div>
          </div>
          <div pageContent class="col-md-12" (click)="setCategory(category.Skills)">
            <div class="row height">
              <div class="col-md-4 col-sm-6 text-right tLine" [style.background]="model.style.color"></div>
              <h3 class="first-template-paragraphTitle Paragraph-paragraph-title height">
                <div class="Text-text-wrapper">
                  <div class="Text-Text-text">{{'category.Skills' | translate}}</div>
                </div>
              </h3>
            </div>
          </div>
          <div pageContent class="container-fluid">
            <ng-container *ngFor="let skillObj of skills.subCategories; let i = index" class="col-md-12">
              <app-skills pageContent [skillObj]="skillObj" [id]="i" [skills]="skills" [model]="model"></app-skills>
            </ng-container>
          </div>
        </div>
      </ng-container>
      <!-- Files Component -->
      <ng-container *ngFor="let file of model?.files; let index = index" class="Unit-unit-unitGroup">

        <div *ngIf="selectedCategory === category.Files"
             [ngClass]="{ 'isCatActive': selectedCategory === category.Files}">
          <div clickOutside (clickOutside)="removeClick()">
            <ul>
              <li class="fa fa-plus addIconTop" (click)="openDialog({model:model})"></li>
              <li class="fa fa-plus addIconBottom" (click)="openDialog({model: model})"></li>
              <li class="fa fa-trash deleteIconRight" (click)="deleteEducationCategory(index)"></li>
              <li class="fa fa-arrow-down moveIconDown"></li>
              <li class="fa fa-arrow-up moveIconTop"></li>
            </ul>
          </div>
        </div>
        <div pageContent class="col-md-12" (click)="setCategory(category.Files)">
          <div class="row height">
            <div class="col-md-4 col-sm-6 text-right tLine" [style.background]="model.style.color"></div>
            <h3 class="first-template-paragraphTitle Paragraph-paragraph-title height">
              <div class="Text-text-wrapper">
                <div class="Text-Text-text">{{'category.Files' | translate}}</div>
              </div>
            </h3>
          </div>
        </div>
        <ng-container *ngFor="let fileObj of file.subCategories; let i = index" class="col-md-12">
          <div pageContent class="container-fluid">
            <div pageContent [ngClass]="{ 'isActive': selectedFileIndex === i}">
              <div pageContent class="Line-line-container" (click)="setFileIndex(i)">
                <div class="Line-line-line">
                  <div class="Field-field-fieldBase first-template-fieldField">
                    <div pageContent class="Text-Text-wrapper">
                      <div class="Text-Text-text">
                        {{fileObj.name}}</div>
                    </div>
                  </div>
                </div>
                <div class="Field-field-fieldBase first-template-fieldValue" *ngIf="fileObj.link">
                  <div class="Text-Text-wrapper">
                    <div class="Text-Text-text-wrapper">
                      <a target="_blank" [href]="fileObj.link">{{fileObj.link}}
                      </a>
                    </div>
                  </div>
                </div>
                <div class="Field-field-fieldBase first-template-fieldValue">
                  <div class="Text-Text-wrapper">
                    <div class="Text-Text-text-wrapper" aria-multiline="true" [innerHTML]="fileObj.description">
                    </div>
                  </div>
                </div>
                <ng-container pageContent *ngIf="selectedFileIndex === i">
                  <div clickOutside (clickOutside)="removeClick()">
                    <ul>
                      <li class="fa fa-pencil addIconTop" (click)="editFile({edit: true, file: fileObj, model: model})">
                      </li>
                      <button (click)="addFile({edit: false, model: model})" class="btn"><i
                        class="fa fa-plus addIconBottom"></i></button>
                      <button [disabled]="file.subCategories.length === 1" (click)="deleteSubFile(i)" class="btn"><i
                        class="fa fa-trash deleteIconRight"></i></button>
                      <li class="fa fa-arrow-down moveIconDown"></li>
                      <li class="fa fa-arrow-up moveIconTop"></li>
                    </ul>
                  </div>
                </ng-container>
              </div>
            </div>
          </div>
        </ng-container>
      </ng-container>
      <!-- Empty Category -->
      <ng-container *ngFor="let emptyCat of model?.emptyCategory; let i = index" class="Unit-unit-unitGroup">
        <div *ngIf="selectedCategory === category.Another"
             [ngClass]="{ 'isCatActive': selectedCategory === category.Another}">
          <div clickOutside (clickOutside)="removeClick()">
            <ul>
              <li class="fa fa-pencil addIconTop"
                  (click)="editEmptyCategory({edit: true, model: model, emptyCategory: emptyCat})"></li>
              <li class="fa fa-plus addIconBottom" (click)="openDialog({model: model})"></li>
              <li class="fa fa-trash deleteIconRight" (click)="deleteEmptyCategory(i)"></li>
              <li class="fa fa-arrow-down moveIconDown"></li>
              <li class="fa fa-arrow-up moveIconTop"></li>
            </ul>
          </div>
        </div>
        <div pageContent class="col-md-12" (click)="setCategory(category.Another)">
          <div class="row height">
            <div class="col-md-4 col-sm-6 text-right tLine" [style.background]="model.style.color"></div>
            <h3 class="first-template-paragraphTitle Paragraph-paragraph-title height">
              <div class="Text-text-wrapper">
                <div class="Text-Text-text">{{emptyCat.name}}</div>
              </div>
            </h3>
          </div>
        </div>
        <ng-container *ngFor="let emptySubObj of emptyCat.emptySubContents; let i = index" class="col-md-12">
          <div pageContent class="container-fluid">
            <div pageContent [ngClass]="{ 'isActive': selectedEmptySubCat === i}">
              <div pageContent class="Line-line-container" (click)="setEmptySubCatIndex(i)">
                <div class="Line-line-line">
                  <div class="Field-field-fieldBase first-template-fieldField">
                    <div class="Text-Text-wrapper">
                      <div pageContent class="Text-Text-text">
                        {{emptySubObj.name}}</div>
                    </div>
                  </div>
                </div>
                <div class="Field-field-fieldBase first-template-fieldValue">
                  <div class="Text-Text-wrapper">
                    <div pageContent class="Text-Text-text-wrapper" aria-multiline="true"
                         [innerHTML]="emptySubObj.description">
                    </div>
                  </div>
                </div>
                <ng-container pageContent *ngIf="selectedEmptySubCat === i">
                  <div clickOutside (clickOutside)="removeClick()">
                    <ul>
                      <li class="fa fa-pencil addIconTop"
                          (click)="editEmptySubCat({edit: true, empty: emptySubObj, model: model, emptySubCat: true})">
                      </li>
                      <button (click)="addEmptySubCat({edit: false, model: model, emptySubCat: true})" class="btn"><i
                        class="fa fa-plus addIconBottom"></i></button>
                      <button [disabled]="emptyCat.emptySubContents.length === 1" (click)="deleteEmptyCatFile(i)"
                              class="btn"><i class="fa fa-trash deleteIconRight"></i></button>
                      <li class="fa fa-arrow-down moveIconDown"></li>
                      <li class="fa fa-arrow-up moveIconTop"></li>
                    </ul>
                  </div>
                </ng-container>
              </div>
            </div>
          </div>
        </ng-container>
      </ng-container>
      <!--Here closes the container for the whole page-->
    </ng-container>
    <div class="PageNumber-page-number-container">
      <div class="PageNumber-page-number-pageNumber">
        <span class="PageNumber-page-number-page">
          Page
        </span>
        <span>1 / 2</span>
      </div>
    </div>

  </app-paginated-view>

我基于这个页面在这里他们如何下载 PDF。

https://lebenslauf.com/

他们采用前端的设计,他们可以在 UI 中给出的设计和日期内下载 pdf。 有可能这样做吗? 在我的 PDF 中,我无法复制文本或编辑文本,image 或 PDF 非常小。

如果有人不明白,我可以添加更多代码或更好地解释它 请参阅所附照片 pdf 的外观。 我在 google chrome 中调整了大小只是为了看得更好,但它是 A4 页面的普通字体。

这是我的 UI 的照片。

【问题讨论】:

  • 我也遇到过同样的问题。以这种方式复制或编辑文本是不可能的(AFAIK)。 html2canvas 本质上只是对您传递的元素进行“截图”,所以它基本上只是一张图片。我建议使用 puppeteer 和任何类型的模板框架(我使用 Nunjucks 完成)在您的后端生成 PDF。一些额外的词,但最终值得。 (pptr.dev, mozilla.github.io/nunjucks/templating.html)
  • @chrnx 感谢您的回答。我查看了pupeteer,但我没有找到太多关于它的文档,如何实现使用哪些类以及类似的东西。
  • 您可以在 puppeteer 文档中搜索“PDF”,您将看到如何创建并下载它。您只需要提供一个带有.setContent 函数的模板就可以了。如果你需要,我可以用一些更详细的例子写一个答案
  • @chrnx 如果您能写下答案,您将非常友好。
  • 我使用 pdfmake 和 Angular 以及交互式 SVG 和文本。这都是客户端,并且运行良好。也许您可以按照stackoverflow.com/questions/34049956/… 的方式做一些事情?

标签: javascript html angular typescript pdf


【解决方案1】:

据我所知,您要实现的目标是使用html2canvas 是不可能的。由于html2canvas 仅创建您传递给它的元素的“屏幕截图”,因此它只是内容的图像,不可编辑。我建议使用 puppeteers .pdf() 函数和某种模板框架(在我的案例中我使用 Nunjucks,除了我们在工作中使用相同的设置并且我有示例之外没有其他原因)在您的后端生成 PDF(例如谷歌云函数)。

假设您有一个src 文件夹,其中包含一个pdf.service.ts 和一个template.njk(或.html,没关系)

在你的服务中,你可以有一个看起来像这样的函数:

function getTemplate(data: YourDataInterface): string {
  const env = new Environment(new FileSystemLoader(__dirname));
  const tpl = env.getTemplate('template.njk');
  return tpl.render({ data });
}

data 将是传递给模板的对象。很像 Angular 中的 @Input() 或 react 中的 props。

__dirname 是您的目录路径,如果您的模板位于某种子文件夹中,您可以附加任何路径。

其余的是 Nunjucks 函数和类。你可以在这里阅读更多关于这些的信息:https://mozilla.github.io/nunjucks/api.html

您的模板可能如下所示:

...
  <div class="Title-title-titleWrapper first-template-titleWrapper">
    <h4 *ngIf="model.hideName">{{ data.personalData[0].firstName }} {{ data.personalData[0].lastName }}</h4>
    <h5>{{ data.newJobTitle }}</h5>
    <div>{{ data?.description }}</div>
  </div>
...

data 又是.render 函数中传递的对象。

您可以在此处阅读有关编写 Nunjucks 模板的更多信息:https://mozilla.github.io/nunjucks/templating.html

要使用 puppeteer 实际生成 PDF,这可能是起点:

async function generatePdf(data: YourDataInterface): Promise<Buffer> {
  const browser = await launch({ args: ['--no-sandbox', '--disable-setup-sandbox', '--disable-web-security'] });

  try {
    const page = await browser.newPage();

    await page.setContent(getTemplate(data), {
      waitUntil: ['load', 'domcontentloaded', 'networkidle0']
    });
    await page.addStyleTag({ path: __dirname + '/styles.css' })

    return await page.pdf({ format: 'A4', printBackground: true });
  } finally {
    browser.close().catch(console.error);
  }
}

我不打算在这里详细介绍,您可以在 puppeteer 文档中查找所有标志和功能等 (https://pptr.dev)

这个函数返回一个Promise&lt;Buffer&gt;,可以在你的前端下载。关于如何做到这一点,stackoverflow 上已经有足够多的答案了。

【讨论】:

  • 感谢您的宝贵时间,但我需要在哪里呈现此代码?在前端的角度应用程序或后端。因为我在后端使用 mongoDB。
  • 我真的不知道你的设置,不过这必须在你的后端运行。如果您还没有看过 NestJS 或一些类似的框架,您可能会觉得很有意义。然后你只需要对你的后端 URL 进行 API 调用(使用 axios、graphql、angulars http 客户端......),连同你想要显示的数据,其余的应该发生在你后端的某种服务中。
  • 我的设置是这样的。我拥有 FE -> CSS、HTML、TS 中的所有逻辑。而且我只使用后端来保存数据、验证用户、保存新用户和为不同用户保存数据。这些用户有哪些数据,可以在 PDF 中下载。但是在 FE 中使用正确的样式。
  • 如果您的用户可以更改 cv 的设计,您可以将组件 elementRef 传递到后端并将其修改为 puppeteer 可用的格式。你必须在某种节点后端运行 puppeteer,否则我不认为它不起作用(不要引用我的话)。顺便说一句,您是直接从前端与数据库通信吗?
  • 我正在后端与前端的服务进行直接通信。
猜你喜欢
  • 2019-03-22
  • 2016-07-26
  • 2021-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-17
  • 1970-01-01
相关资源
最近更新 更多