最近在 tojson.js
开源项目中支持了 pptx
转 json
, 该 json
满足 fabricjs
画布渲染的数据结构, 今天来探讨下实现原理
pptx-json
参考 pptx2json
, 不过该仓库的逻辑和 pptx2html
一样, 只是把 html 格式换成了 json 数据
调研阶段, 认为只是把 pptx2json
返回的数据格式转化成 fabricjs
支持的数据格式就能满足需求, 不过其中对文本元素的解析不能满足需求
为什么?
- 文本属性未使用 json, 其中包括文本内容、字体大小、字体类型、字重
最近在tojson.js
开源项目中支持了pptx
转json
, 该json
满足fabricjs
画布渲染的数据结构, 今天来探讨下实现原理
pptx-json
参考 pptx2json
, 不过该仓库的逻辑和 pptx2html
一样, 只是把 html 格式换成了 json 数据
调研阶段, 认为只是把 pptx2json
返回的数据格式转化成 fabricjs
支持的数据格式就能满足需求, 不过其中对文本元素的解析不能满足需求
为什么?
- 文本属性未使用 json, 其中包括文本内容、字体大小、字体类型、字重
- 该库使用的是 p 标签代表换行,如何在 fabricjs 中代表换行?
3. 在有序(无序)列表中一行文案被拆分成多个 span 标签表示
4. 对齐元素返回字段错误
所以只能魔改源码的形式来满足自身需求
文本 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, " ")}</a></span>`;
}
return `<span style="${styleText}">${text.replace(/\s/i, " ")}</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 中表示呢?
通过以上文本元素转 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)
}
}
相信大家已经看出来了, 像二维数组
pNodes
和 rNode
两个字段就能区分, 不过 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
对齐字端使用的是 alng
非 align
, 替换后测试发现返回 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
评论列表(1条)
大佬,为何运行预览地址,选择导入pptx文件,控制台显示‘暂不支持该类型‘,拉取代码后也运行失败?