前端自定义导出PPT

news2024/11/24 19:54:40
1、背景

        前端导出PPT,刚接触这个需求,还是比较懵逼,然后就在网上查找资料,最终确认是可行的;这个需求也是合理的,我们做了一个可视化数据报表,报表导出成PPT,将在线报表转成文档类型留存;

2、技术方案

        实现这种复杂的功能,都得依赖前辈匠人,还好有一个比较完善好用的库:pptxgenjs

只有英文文档:Quick Start Guide | PptxGenJS,还可以搭配大家比较熟悉的库:html2canvas更好的实现完善的PPT

3、PptxGenJS运用

引入,生成一个简单的PPT文档

import pptxgen from "pptxgenjs";

let pptx = new pptxgen();

let slide = pptx.addSlide();

slide.addText("React Demo!", { x: 1, y: 1, w: 10, fontSize: 36, fill: { color: "F1F1F1" }, align: "center" });

pptx.writeFile({ fileName: "react-demo.pptx" }

pptx全局属性:

pptx.author = 'Brent Ely';
pptx.company = 'S.T.A.R. Laboratories';
pptx.revision = '15';
pptx.subject = 'Annual Report';
pptx.title = 'PptxGenJS Sample Presentation';
pptx.layout = 'LAYOUT_WIDE'; //13.5 x 7.5

其中最重要的属性,layout,顾名思义,就是设置PPT的slide的大小,默认就下面几种:

还可以自定义。这个x*y,是后面PPT页计算布局必不可少的;

slide master,自定义PPT页模板:

    ppt.defineSlideMaster({
      title: 'DEFAULT_SLIDE',
      objects: [
        { image: { x: 0.25, y: 0.3, w: 0.6, h: 0.6, path: path} },
        { image: { x: 10.8, y: 0.61, w: 1.485, h: 0.166, path: path} },
        { image: { x: 12.3, y: 0.52, w: 0.72, h: 0.36, path: path} },
        { line: { x: 0.25, y: 1, w: 12.8, h: 0, line: { color: '3874c5', width: 2 } } },
        { image: { path: path, x: 0, y: 7.2, w: '100%', h: 0.3, size: { type: 'cover' } } },
      ],
    });

我们自己创建PPT时,也会引用模板,这个就是自定义模板,就避免每页都设置;

slide,PPT页属性对象:

创建一页PPT,addSilde({masterName});masterName就是上面自定义的模板,就是上面的DEFAULT_SLIDE

4、添加表格

表格是一个常用功能,PPT的表格也比较完善,addTable();

表头和合计,这个pptx没有特殊的处理,只能作为正常row处理;

行高度问题row Height,表格设置高度h,如果不设置行高,rowH,这样表格会填充h,设置了rowH,最小高度会按rowH设置值显示;

表格一般有比较多的数据,PPT页就那么高,肯定会有超出PPT页的情况,pptx支持自定义分页,autoPage:true,然后结合autoPageCharWeight,autoPageLineWeight调试分页,在实际使用过程发现自动分页也是根据你设置的rowH和H来计算的,对表格单元格多行,还是会超出,然后导出的ppt文档,会报错,要修复啥的,所以我选择手动给它分页;

列宽问题,默认是等分的,colW,实际开发是最好根据列的宽度,然后计算colW的,记住设置了colW了,表格会严格按照设置的值展示,不会自适应,所以还要程序根据w在计算;

  setTable(data, option = {}) {
    let row = [];
    let options = { fontFace: 'Microsoft YaHei', fontSize: 12, margin: 0.05, valign: 'middle', align: 'left' };
    options['border'] = [{ pt: 1, color: 'ffffff', type: 'dash' }, { type: 'none' }, { pt: 1, color: 'ffffff', type: 'dash' }, { type: 'none' }];
    let head = [];
    let colW = [];
    data.head.forEach(item => {
      head.push({ text: item.label, options: { ...options, fill: '1E4265', color: 'ffffff' } });
      if (item.width < 85) {
        colW.push(0.1);
      } else if (item.width < 101) {
        colW.push(0.2);
      } else if (item.width < 121) {
        colW.push(0.3);
      } else {
        colW.push(0.4);
      }
    });
    // autoPage: true, newSlideStartY: 1.1, autoPageRepeatHeader: true,暂时自动分页不太行
    let page = { ...this.page, rowH: 0.5, valign: 'middle' };
    // 表格超出,ppt分页展示,最多展示三页数据
    let tableData = data.table || [];
    tableData = tableData.slice(0, 30);
    tableData.forEach((item, index) => {
      let temp = [];
      let fill = index % 2 == 1 ? 'ffffff' : 'f2f2f2';
      data.head.forEach(h => {
        temp.push({ text: item[h.prop] === null ? '' : item[h.prop], options: { ...options, fill } });
      });
      row.push(temp);
    });
    let sumW = colW.reduce((per, cur) => per + cur, 0);
    let fNum = option.h < 6 ? 5 : 10;
    let fRow = row.slice(0, fNum);
    let fOption = { ...page, ...option };
    fOption['colW'] = colW.map(item => Number((fOption.w / sumW) * item).toFixed(1));
    this.slide.addTable([head, ...fRow], fOption);
    let eNum = parseInt(fNum + 10);
    let tRow = row.slice(fNum, eNum);
    if (tRow.length) {
      // 第二页
      let slide = this.ppt.addSlide({ masterName: 'DEFAULT_SLIDE' });
      page['colW'] = colW.map(item => Number((page.w / sumW) * item).toFixed(1));
      slide.addTable([head, ...tRow], { ...page });
      tRow = row.slice(eNum, parseInt(eNum + 10));
      if (tRow.length) {
        // 第三页
        let slide = this.ppt.addSlide({ masterName: 'DEFAULT_SLIDE' });
        slide.addTable([head, ...tRow], { ...page });
      }
    }
  }
5、添加Image

addImage(),支持两种格式:data,base64数据;path,图片地址;注意,根据img的大小,然后换算实际的w和h;

  setChart(data, option = {}) {
    const { chartW, chartH, chartData } = data;
    let options = { ...this.page, ...option, sizing: { type: 'contain' } };
    let temp = (options.w / chartW) * chartH;
    if (temp > options.h) {
      temp = (options.h / chartH) * chartW;
      options.x = options.x + (options.w - temp) / 2;
      options.w = temp;
    } else {
      options.y = options.y + (options.h - temp) / 2;
      options.h = temp;
    }
    this.slide.addImage({ data: chartData, ...options });
  }

 复杂的html,可以通过html2canvas,把dom转成图片添加PPT

  async setDom2Image(dom, option = {}) {
    let result = {};
    const res = await html2Canvas(dom, { scale: 2 });
    result['chartData'] = res.toDataURL('image/jpeg', 1);
    result['chartH'] = res.height;
    result['chartW'] = res.width;
    this.setChart(result, option);
  }
6、添加文本

添加文本,这个介绍把一段html,添加成PPT文本

  setContent(content, option = {}) {
    let obj = [];
    content &&
      content.forEach(item => {
        let len = item.children.length;
        --len;
        item.children.forEach((cItem, index) => {
          obj.push({ text: cItem.text, options: { bold: cItem.bold ? true : false, color: cItem.color ? this.RGBToHex(cItem.color) : '333333', breakLine: index === len } });
        });
      });
    let options = { ...this.page, ...option, align: 'left' };
    if (options.h == 6) {
      options['fontSize'] = options.h == 6 ? 16 : 12;
    }
    this.setTitle(obj, { ...options });
  }
  setTitle(text, option = {}) {
    const options = {
      fontSize: 12, //字号
      fontFace: 'Microsoft YaHei',
      bold: false,
      color: '333333', //颜色 与背景颜色一样,一样不要 #,填满6位
      valign: 'middle', // 垂直居中 top middle bottom
    };
    this.slide.addText(text, { ...options, ...option });
  }

 由于PPT只支持16进制的颜色值,所以需要把rgb转成6位颜色值

  RGBToHex(rgb) {
    let regexp = /\d+/g;
    let res = rgb.match(regexp);
    return ((res[0] << 16) | (res[1] << 8) | res[2]).toString(16);
  }

把html转成slate的JSON

let document = new DOMParser().parseFromString(this.content, 'text/html');

result['json'] = this.deserialize(document.body);

    deserialize(el, markAttributes = {}) {
      if (el.nodeType === Node.TEXT_NODE) {
        return jsx('text', markAttributes, el.textContent);
      } else if (el.nodeType !== Node.ELEMENT_NODE) {
        return null;
      }
      const nodeAttributes = { ...markAttributes };
      // define attributes for text nodes
      switch (el.nodeName) {
        case 'STRONG':
          nodeAttributes.bold = true;
      }
      // font color
      if (el.style.color) {
        nodeAttributes.color = el.style.color;
      }
      const children = Array.from(el.childNodes)
        .map(node => this.deserialize(node, nodeAttributes))
        .flat();

      if (children.length === 0) {
        children.push(jsx('text', nodeAttributes, ''));
      }
      switch (el.nodeName) {
        case 'BR':
          return '\n';
        case 'P':
          return jsx('element', { type: 'paragraph' }, children);
        default:
          return children;
      }
    },

7、绘制图形Shapes

shapes,绘制图形,文档有详细介绍,这里我就不累述

 8、总结

整个功能实现下来还是比较耗时,主要文档都是英文,有些字段描述也不是很清晰,有的需要慢慢调试,上面一些介绍的功能,都是实际开发在使用的;总体来说,还是比较完美实现自定义导出PPT。欢迎大家一起沟通交流!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1024336.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【数据库系统概论】关系数据库中的关系数据结构

前言关系关系模式关系数据库关系模型的存储结构感谢 &#x1f496; 前言 上一篇文章【数据库系统概论】数据模型介绍了数据库系统中的数据模型的基本概念。其中提到了关系模型是最重要的一种数据模型。下面将介绍支持关系模型的数据库系统——关系数据库。 按照数据模型的三大…

蓝牙核心规范(V5.4)10.5-BLE 入门笔记之HCI

HCI全称:HOST Constroller Interface 主机控制器接口(HCI)定义了一个标准化的接口,通过该接口,主机可以向控制器发出命令,并且控制器可以与主机进行通信。规范被分成几个部分,第一部分仅从功能的角度定义接口,不考虑具体的实现机制,而其他部分定义了在使用四种可能的…

Mac 上如何安装Mysql? 如何配置 Mysql?以及如何开启并使用MySQL

前言&#xff1a; 有许多开发的小伙伴&#xff0c;使用的是mac&#xff0c;那么在mac上如何安装&#xff0c;配置Mysql&#xff0c;以及使用Mysql了&#xff0c;今天来一个系统的教程。 安装Mysql 使用mysql前&#xff0c;我们需要先下载mysql&#xff0c;并按照以下几个步骤…

【Oracle】Oracle系列之四--用户管理

文章目录 往期回顾前言1. 创建/删除用户&#xff08;1&#xff09;创建用户&#xff08;2&#xff09;修改口令&#xff08;3&#xff09;删除用户 2. 用户授权管理&#xff08;1&#xff09;对用户直接授权&#xff08;2&#xff09;通过角色对用户授权 往期回顾 【Oracle】O…

Nodejs 相关知识

Nodejs是一个js运行环境&#xff0c;可以让js开发后端程序&#xff0c;实现几乎其他后端语言实现的所有功能&#xff0c;能够让js与其他后端语言平起平坐。 nodejs是基于v8引擎&#xff0c;v8是Google发布的开源js引擎&#xff0c;本身就是用于chrome浏览器的js解释部分&#…

day43 数据库

SQL分类 DDL&#xff1a;Date definition Language 数据定义语言 主要针对的是数据库对象进行创建修改删除的操作 包括&#xff1a;create, alter, drop, show, desc truncate DML&#xff1a;Data Manipulation Language 数据操作语言 对数据库中数据进行增加&#xff0c;修…

3D成像技术概述

工业4.0时代,三维机器视觉备受关注,目前,三维机器视觉成像方法主要分为光学成像法和非光学成像法,这之中,光学成像法是市场主流。 飞行时间3D成像 飞行时间成像(Time of Flight),简称TOF,是通过给目标连续发送光脉冲,然后用传感器接收从物体返回的光,通过探测光脉…

国庆中秋特辑(二)浪漫祝福方式 使用生成对抗网络(GAN)生成具有节日氛围的画作

要用人工智能技术来庆祝国庆中秋&#xff0c;我们可以使用生成对抗网络&#xff08;GAN&#xff09;生成具有节日氛围的画作。这里将使用深度学习框架 TensorFlow 和 Keras 来实现。 一、生成对抗网络&#xff08;GAN&#xff09; 生成对抗网络&#xff08;GANs&#xff0c;…

基于Yolov8的野外烟雾检测(4):通道优先卷积注意力(CPCA),效果秒杀CBAM和SE等 | 中科院2023最新发表

目录 1.Yolov8介绍 2.野外火灾烟雾数据集介绍 3.CPCA介绍 3.1 CPCA加入到yolov8 4.训练结果分析 5.系列篇 1.Yolov8介绍 Ultralytics YOLOv8是Ultralytics公司开发的YOLO目标检测和图像分割模型的最新版本。YOLOv8是一种尖端的、最先进的&#xff08;SOTA&#xff09;模型&a…

Golang反射相关知识总结

1. Golang反射概述 Go语言的反射&#xff08;reflection&#xff09;是指在运行时动态地获取类型信息和操作对象的能力。在Go语言中&#xff0c;每个值都是一个接口类型&#xff0c;这个接口类型包含了这个值的类型信息和值的数据&#xff0c;因此&#xff0c;通过反射&#x…

Freertos学习笔记

文章目录 Freertos移植TCB控制块中断管理 (内部异常和外部中断)同步互斥与通信消息队列:邮箱:信号量:互斥量:事件组:任务通知:Freertos移植 其核心文件为,tasks.c、timers.c、queue.c、event_groups.c、croutine.c、list.c。源码兼顾了很多平台,但是我们可以删除一些…

亚马逊攀岩安全带ASTM F1772测试办理

本政策适用于主要用于攀岩或登山活动的安全带。攀岩安全带是一种装备&#xff0c;可穿戴在攀岩者或登山者的腰部和大腿处。攀岩安全带为绳子提供了一个连接点&#xff0c;并提供一种手段&#xff0c;以便在攀登、休息、绕绳下降或跌落的过程中为攀登者身体提供支撑。本政策涵盖…

整理mongodb文档:副本集一

个人博客 整理mongodb文档:副本集一 本文讲解较为粗糙&#xff0c;对于没有后台开发经验的人员&#xff0c;建议配合官网了解下相关概念。个人博客&#xff0c;日常求关注 文章概叙 文章会先花费几分钟讲解下关于垂直缩放以及水平缩放的概念&#xff0c;以方便大家对副本集…

Qt5开发及实例V2.0-第五章Qt主窗体

Qt5开发及实例V2.0-第五章Qt主窗体 第5章 Qt 5主窗体5.1.1 基本元素5.1.2 【综合实例】&#xff1a;文本编辑器5.1.3 菜单与工具栏的实现 5.2 Qt 5文件操作功能5.2.1 新建文件5.2.2 打开文件5.2.3 打印文件 5.3 Qt 5图像坐标变换5.3.1 缩放功能5.3.2 旋转功能5.3.3 镜像功能 5.…

适用于 Linux 的 Windows 子系统获得新的“镜像”网络模式

Microsoft 发布了 Windows Subsystem for Linux (WSL) 2.0.0&#xff0c;其中包含一组新的可选实验功能&#xff0c;包括新的网络模式以及自动内存和磁盘大小清理。 首先&#xff0c;新添加的“自动内存回收”功能通过回收缓存内存来动态减少 WSL 虚拟机 (VM) 的内存占用。 此…

Git学习笔记8

Gitlab&#xff1a; Gitlab是利用Ruby on Rails 一个开源的版本管理系统&#xff0c;实现一个自托管的git项目仓库&#xff0c;可通过web界面进行访问公开或私有的项目。 Gitlab安装&#xff1a; 安装之前&#xff0c;将虚拟机的内存改成了4个G。内存如果太小&#xff0c;会有…

Zipping

Zipping 信息收集端口扫描目录扫描webbanner信息收集 漏洞利用空字节绕过---->失败sqlI-preg_match bypass反弹shell 稳定维持 提权-共享库漏洞 参考&#xff1a;https://rouvin.gitbook.io/ibreakstuff/writeups/htb-season-2/zipping#sudo-privileges-greater-than-stock-…

基于Python开发的图片批量处理器(源码+可执行程序+程序配置说明书+程序使用说明书)

一、项目简介 本项目是一套基于Python开发的图片批量处理器&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Python学习者。 包含&#xff1a;项目源码、项目文档等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;…

软考高级架构师下篇-16通信系统架构设计理论与实践

目录 1. 引言2. 通信系统网络架构3. 网络构建关键技术4. 网络构建5. 前文回顾1. 引言 此章节主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本节知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中…

SpringSecurity 权限管理的实现

文章目录 前言权限管理的实现权限校验的原理FilterSecurityInterceptorAccessDescisionManagerAffirmativeBasedConsensusBasedUnanimousBased AccessDecisionVoterWebExpressionVoterAuthenticatedVoterPreInvocationAuthorizationAdviceVoterRoleVoterRoleHierarchyVoter 前言…