nest 如何实现导出ppt ppt转pdf pdf转图片

185 阅读2分钟

大家好,自从9月份开始做nest 全栈以来遇到了很多问题

现在的目前整理自己遇到的难点和解决思路,希望大家关注

1.问题:实现数据转成ppt ppt转pdf pdf转图片的功能

import PptxGenJS from 'pptxgenjs';
import * as libre from 'libreoffice-convert';
import { PDFDocument } from 'pdf-lib';

目前后端用到的是这几个插件,也都非常基础

pptxgenjs

1.这一步是创建自己的ppt文件及增加ppt母版设置ppt的风格等

  const pres = new PptxGenJS();
      pres.defineLayout({ name: 'MAIN_LAYOUT', width: cmToInch(18), height: cmToInch(36) });
      pres.layout = 'MAIN_LAYOUT';
      pres.theme = {
        headFontFace: '微软雅黑',
        bodyFontFace: '微软雅黑',
      };
      pres.defineSlideMaster({
        title: 'MASTER_SLIDE',
        background: {
          path: 'https://deep-ai-health-test.oss-cn-hangzhou.aliyuncs.com/healthj-paln-ppt-template/H5ZT4%40%25X719O4A%40OE1UAA%5BD.png',
        },
      });

2.增加相应的内容(cmToInch 自己写的cm转英寸)

  // 添加第一个区域
      mySlide.addShape(pres.ShapeType.roundRect, {
        x: cmToInch(0.28),
        y: cmToInch(0.5),
        w: cmToInch(17.43),
        h: cmToInch(15.94),
        fill: { color: 'FFFFFF' },
        rectRadius: 0.1,
      });
      // 添加第二个区域
      mySlide.addShape(pres.ShapeType.roundRect, {
        x: cmToInch(0.28),
        y: cmToInch(16.97),
        w: cmToInch(17.43),
        h: cmToInch(18.43),
        fill: { color: 'FFFFFF' },
        // 圆角
        rectRadius: 0.1,
      });
      // 添加header的形状2个 header
      mySlide.addShape(pres.ShapeType.rect, {
        x: cmToInch(6.58),
        y: cmToInch(1.06),
        w: cmToInch(5.91),
        h: cmToInch(1.4),
        fill: { color: '00B050' },
      });

3.获取文件流

  // 获取 Buffer
      const pptx_buffer: any = await pres.stream();
      if (export_type_name === 'pptx') {
        const file_name = `${real_name}.pptx`;
        return { file_name, pptx_buffer };
      } else if (export_type_name === 'jpg') {
        const pdfBuffer = await this.convertPptToPdf(pptx_buffer);
        const file_name = `${real_name}.jpg`;
        // 验证pdfBuffer
        await this.verifyPdfBuffer(pdfBuffer);
        // 生成图片
        return { file_name, pptx_buffer: pdfBuffer };
      }

4.转化成pdf(目前常见的使用第三方软件去转化,推荐插件libreoffice-convert,基于三方软件LibreOffice ,服务器用7版本就可以)

image.png

  private async convertPptToPdf(pptxBuffer: Buffer): Promise<Buffer> {
    const pdfBuffer = await new Promise<Buffer>((resolve, reject) => {
      libre.convert(pptxBuffer, '.pdf', undefined, (err, result) => {
        if (err) {
          reject(err);
        } else {
          resolve(result);
        }
      });
    });

    return pdfBuffer;
  }
  private async verifyPdfBuffer(pdfBuffer: Buffer): Promise<void> {
    try {
      const pdfDoc = await PDFDocument.load(pdfBuffer);
      if (!pdfDoc) {
        throw new Error('Invalid PDF buffer');
      }
    } catch (error) {
      throw new Error('Invalid PDF buffer');
    }
  }

5.转化成png,用到的思路为pdfjs

1.第一步:先下载pdfjs,npm包pdfjs-dist,node > 20

2.引入入口文件 vite.config 要alias pdfjs-dist的路径才能引用pdfjs-dist/build/pdf.worker.mjs

import * as pdfjsLib from 'pdfjs-dist';
pdfjsLib.GlobalWorkerOptions.workerSrc = 'pdfjs-dist/build/pdf.worker.mjs';

6.转换成pdf

//  将文件对象转化为 Uint8Array
          const reader = new FileReader();
          console.log('res:', res);
          const { fileData, fileName } = res;
          reader.readAsArrayBuffer(fileData);
          reader.onload = (res) => {
            var typedarray = new Uint8Array(res.target.result);
            const loadingTask = pdfjsLib.getDocument(typedarray);
            // 开始加载 PDF,这里封装了一个函数
            this.loadPDFFile(loadingTask, fileName);
          };
    async loadPDFFile(loadingTask, fileName) {
      // PDF 加载任务
      const pdf = await loadingTask.promise;
      // 获取 PDF 页数
      const numPages = pdf.numPages;
      console.log('numPages: ', numPages);

      for (let curPage = 1; curPage <= numPages; curPage++) {
        const page = await pdf.getPage(curPage);
        const scale = 1.5;
        // 获取渲染视角尺寸
        const viewport = page.getViewport({ scale });
        // Support HiDPI-screens.
        const outputScale = window.devicePixelRatio || 1;
        const canvas = new OffscreenCanvas(viewport.width * outputScale, viewport.height * outputScale);
        // 获取 Canvas 上下文
        const context = canvas.getContext('2d');
        // 转换尺寸
        const transform = outputScale !== 1 ? [outputScale, 0, 0, outputScale, 0, 0] : null;
        const renderContext = {
          canvasContext: context,
          transform,
          viewport,
        };
        const renderTask = page.render(renderContext);
        await renderTask.promise;
        // 将 Canvas 转换为 Blob
        canvas.convertToBlob().then((blob) => {
          // 创建一个 URL 对象
          const link = document.createElement('a');
          link.href = URL.createObjectURL(blob);
          // 设置下载的文件名
          link.download = `${decodeURIComponent(fileName)}`;
          // 触发点击事件
          link.click();
          this.exportLoading = 1;
          setTimeout(() => {
            this.bShowExportReportDialog = false;
          }, 500);
        });
      }
    },