pptx 转 支持fabricjs 渲染的 json 结构, 没有现成开源库, 只能迎难而上进行魔改了

最近在 tojson.js 开源项目中支持了 pptxjson, 该 json 满足 fabricjs 画布渲染的数据结构, 今天来探讨下实现原理

pptx-json 参考 pptx2json, 不过该仓库的逻辑和 pptx2html 一样, 只是把 html 格式换成了 json 数据

调研阶段, 认为只是把 pptx2json 返回的数据格式转化成 fabricjs 支持的数据格式就能满足需求, 不过其中对文本元素的解析不能满足需求

为什么?

  1. 文本属性未使用 json, 其中包括文本内容、字体大小、字体类型、字重
    最近在 tojson.js 开源项目中支持了 pptxjson, 该 json 满足 fabricjs 画布渲染的数据结构, 今天来探讨下实现原理

pptx-json 参考 pptx2json, 不过该仓库的逻辑和 pptx2html 一样, 只是把 html 格式换成了 json 数据

调研阶段, 认为只是把 pptx2json 返回的数据格式转化成 fabricjs 支持的数据格式就能满足需求, 不过其中对文本元素的解析不能满足需求

为什么?

  1. 文本属性未使用 json, 其中包括文本内容、字体大小、字体类型、字重
    pptx 转 支持fabricjs 渲染的 json 结构, 没有现成开源库, 只能迎难而上进行魔改了
  2. 该库使用的是 p 标签代表换行,如何在 fabricjs 中代表换行?

pptx 转 支持fabricjs 渲染的 json 结构, 没有现成开源库, 只能迎难而上进行魔改了
pptx 转 支持fabricjs 渲染的 json 结构, 没有现成开源库, 只能迎难而上进行魔改了
3. 在有序(无序)列表中一行文案被拆分成多个 span 标签表示
pptx 转 支持fabricjs 渲染的 json 结构, 没有现成开源库, 只能迎难而上进行魔改了
pptx 转 支持fabricjs 渲染的 json 结构, 没有现成开源库, 只能迎难而上进行魔改了
4. 对齐元素返回字段错误
pptx 转 支持fabricjs 渲染的 json 结构, 没有现成开源库, 只能迎难而上进行魔改了

pptx 转 支持fabricjs 渲染的 json 结构, 没有现成开源库, 只能迎难而上进行魔改了

所以只能魔改源码的形式来满足自身需求

文本 json 改造

原代码

if (fontColor) styleText += `color: ${fontColor};`;
if (fontSize) styleText += `font-size: ${fontSize};`;
if (fontType) styleText += `font-family: ${fontType};`;
if (fontBold) styleText += `font-weight: ${fontBold};`;
if (fontItalic) styleText += `font-style: ${fontItalic};`;
if (fontDecoration) styleText += `text-decoration: ${fontDecoration};`;
if (fontDecorationLine) styleText += `text-decoration-line: ${fontDecorationLine};`;
if (fontSpace) styleText += `letter-spacing: ${fontSpace};`;
if (subscript) styleText += `vertical-align: ${subscript}; font-size: smaller;`;
if (shadow) styleText += `text-shadow: ${shadow};`;
const linkID = getTextByPathList(node, ["a:rPr", "a:hlinkClick", "attrs", "r:id"]);
if (linkID) {
  const linkURL = warpObj["slideResObj"][linkID]["target"];
  return `<span style="${styleText}"><a href="${linkURL}" target="_blank">${text.replace(/\s/i, "&nbsp;")}</a></span>`;
}
return `<span style="${styleText}">${text.replace(/\s/i, "&nbsp;")}</span>`;

改造后

const styleText = {};
if (fontColor) styleText.fontColor = fontColor;
if (fontSize) styleText.fontSize = fontSize;
if (fontType) styleText.fontType = fontType;
if (fontBold) styleText.fontBold = fontBold;
if (fontItalic) styleText.fontItalic = fontItalic;
if (fontDecoration) styleText.fontDecoration = fontDecoration;
if (fontDecorationLine) styleText.fontDecorationLine = fontDecorationLine;
if (fontSpace) styleText.fontSpace = fontSpace;
if (subscript) styleText.subscript = subscript;
if (shadow) styleText["text-shadow"] = shadow;
styleText.text = text;
return styleText;

通过创建对象的形式返还需要的 json

文本内容合并

dom 转换为 json 后发现, 本来一行元素但是使用了数组来表示, 这样转换为 fabricjs 支持的 json 格式也是转换为两个文本对象吗

实验了下, 不行, why?

如果要转换为两个文本元素, 至少要该元素的位置, 包括 left, top, 但是只有一个, 不能同时代表两个位置, 这样的话位置会重合, 不能做到无缝衔接

所以, 只能通过合并文本的形式转换为一个数组

阅读源代码, 发现只有 rNode 是数组才会有以上问题, 同时测试后发现, 除了文本内容不同外, 其他属性完全相同(注: 如果某个文案下颜色、字重不同, 可能会造成属性不同, 需要感觉 fabricjs 字段进行兼容)

if (!rNode) text += genSpanElement(pNode, slideLayoutSpNode, type, warpObj, fontsizeFactor, slideFactor);
else {
  for (const rNodeItem of rNode) {
    text += genSpanElement(rNodeItem, slideLayoutSpNode, type, warpObj, fontsizeFactor, slideFactor);
  }
}

改造后

if (!rNode) styles.push(genSpanElement(pNode, slideLayoutSpNode, type, warpObj, fontsizeFactor, slideFactor, textBodySpPr));
else {
  let newStyles = [];
  for (const rNodeItem of rNode) {
    newStyles.push(genSpanElement(rNodeItem, slideLayoutSpNode, type, warpObj, fontsizeFactor, slideFactor, textBodySpPr));
  }
  let resultText = "";
  newStyles.forEach((item) => {
    resultText += item.text;
  });
  newStyles[0].text = resultText;
  styles.push(newStyles[0]);
}

文本列表合并

上文提过列表或者手动换行是通过 ul(ol)或 p 标签进行表示, 如何在 fabricjs 中表示呢?

pptx 转 支持fabricjs 渲染的 json 结构, 没有现成开源库, 只能迎难而上进行魔改了
通过以上文本元素转 json 后, 列表会返回以上数据格式, 如何区分 文本内容合并

 for (const pNode of pNodes) {
    let rNode = pNode['a:r']
    let fldNode = pNode['a:fld']
    let brNode = pNode['a:br']
    if (!rNode) text += genSpanElement(pNode, slideLayoutSpNode, type, warpObj, fontsizeFactor, slideFactor)
    else {
      for (const rNodeItem of rNode) {
        text += genSpanElement(rNodeItem, slideLayoutSpNode, type, warpObj, fontsizeFactor, slideFactor)
      }
    }

相信大家已经看出来了, 像二维数组

pNodesrNode 两个字段就能区分, 不过 fabricjs 如何换行

上文提过, 可以获取到每个 pNodes 下的位置, 但是测试下来不行, 还有一个原因就是列表本来是一个整体, 通过拆分文本的形式就会生成多个文本元素

我们知道, \n 代表换行, 在 fabricjs 中\n 也代表换行, 所以只需要把文本合并后对每段加上\n 就行

if (styles.length > 1) {
  const newText = styles.reduce((now, next) => {
    now += next.text + "\n";
    return now;
  }, "");
  styles[0].text = newText;
  return styles[0];
}

对齐字端修复

通过在 fabricjs 中测试 json 是否满足渲染要求发现, 有个文本元素未居中展示, 通过调试源码发现, 对齐使用的是 left

后来在其他库里寻求解决方案发现, pptx2html 对齐字端使用的是 alngalign, 替换后测试发现返回 center, 满足要求

通过以上步骤下来, 把 pptx2json 参考更改成我想要的

具体的数据转化看我往期文章

使用

import pptxtojson from "pptx-json";
const options = {
  uploadUrl: options.uploadUrl, // 图片上传的接口地址
  uploadCallback: options.uploadCallback, // 上传完成后的数据处理回调方法
};
const instance = new pptxtojson(options);
const result = instance.init(files);

总结

github 地址: github.com/haixin-fang…
预览地址: haixin-fang.github.io/tojson.js/p…

原文链接:https://juejin.cn/post/7321587195327610906 作者:Starfish

(0)
上一篇 2024年1月9日 上午11:03
下一篇 2024年1月9日 上午11:14

相关推荐

发表回复

登录后才能评论

评论列表(1条)

  • 头像
    1769 2024年3月1日 下午4:08

    大佬,为何运行预览地址,选择导入pptx文件,控制台显示‘暂不支持该类型‘,拉取代码后也运行失败?