mirror of
https://github.com/pipipi-pikachu/PPTist.git
synced 2025-04-15 02:20:00 +08:00
fix: 导出PPTX文字项目符号不正确
This commit is contained in:
parent
e4f1fa5eae
commit
129afa1f70
@ -6,7 +6,7 @@ import pptxgen from 'pptxgenjs'
|
||||
import tinycolor from 'tinycolor2'
|
||||
import { toPng, toJpeg } from 'html-to-image'
|
||||
import { useSlidesStore } from '@/store'
|
||||
import { PPTElementOutline, PPTElementShadow } from '@/types/slides'
|
||||
import { PPTElementOutline, PPTElementShadow, PPTElementLink } from '@/types/slides'
|
||||
import { getElementRange, getLineElementPath, getTableSubThemeColor } from '@/utils/element'
|
||||
import { AST, toAST } from '@/utils/htmlParser'
|
||||
import { SvgPoints, toPoints } from '@/utils/svgPathParser'
|
||||
@ -79,11 +79,15 @@ export default () => {
|
||||
// 核心思路:将HTML字符串按样式分片平铺,每个片段需要继承祖先元素的样式信息,遇到块级元素需要换行
|
||||
const formatHTML = (html: string) => {
|
||||
const ast = toAST(html)
|
||||
let bulletFlag = false
|
||||
|
||||
const slices: pptxgen.TextProps[] = []
|
||||
const parse = (obj: AST[], baseStyleObj = {}) => {
|
||||
|
||||
for (const item of obj) {
|
||||
if ('tagName' in item && ['div', 'li', 'p'].includes(item.tagName) && slices.length) {
|
||||
const isBlockTag = 'tagName' in item && ['div', 'li', 'p'].includes(item.tagName)
|
||||
|
||||
if (isBlockTag && slices.length) {
|
||||
const lastSlice = slices[slices.length - 1]
|
||||
if (!lastSlice.options) lastSlice.options = {}
|
||||
lastSlice.options.breakLine = true
|
||||
@ -100,11 +104,6 @@ export default () => {
|
||||
}
|
||||
}
|
||||
|
||||
if ('tagName' in item) {
|
||||
if (item.tagName === 'ul') styleObj['list-type'] = 'ul'
|
||||
if (item.tagName === 'ol') styleObj['list-type'] = 'ol'
|
||||
}
|
||||
|
||||
if ('tagName' in item) {
|
||||
if (item.tagName === 'em') {
|
||||
styleObj['font-style'] = 'italic'
|
||||
@ -122,6 +121,15 @@ export default () => {
|
||||
const attr = item.attributes.find(attr => attr.key === 'href')
|
||||
styleObj['href'] = attr?.value || ''
|
||||
}
|
||||
if (item.tagName === 'ul') {
|
||||
styleObj['list-type'] = 'ul'
|
||||
}
|
||||
if (item.tagName === 'ol') {
|
||||
styleObj['list-type'] = 'ol'
|
||||
}
|
||||
if (item.tagName === 'li') {
|
||||
bulletFlag = true
|
||||
}
|
||||
}
|
||||
|
||||
if ('tagName' in item && item.tagName === 'br') {
|
||||
@ -172,16 +180,18 @@ export default () => {
|
||||
if (styleObj['font-family']) options.fontFace = styleObj['font-family']
|
||||
if (styleObj['href']) options.hyperlink = { url: styleObj['href'] }
|
||||
|
||||
if (bulletFlag && styleObj['list-type'] === 'ol') {
|
||||
options.bullet = { type: 'number', indent: 20 * 0.75 }
|
||||
bulletFlag = false
|
||||
}
|
||||
if (bulletFlag && styleObj['list-type'] === 'ul') {
|
||||
options.bullet = { indent: 20 * 0.75 }
|
||||
bulletFlag = false
|
||||
}
|
||||
|
||||
slices.push({ text, options })
|
||||
}
|
||||
else if ('children' in item) parse(item.children, styleObj)
|
||||
|
||||
if ('tagName' in item && item.tagName === 'li') {
|
||||
const slice = slices[slices.length - 1]
|
||||
if (!slice.options) slice.options = {}
|
||||
if (styleObj['list-type'] === 'ol') slice.options.bullet = { type: 'number', indent: 20 * 0.75 }
|
||||
if (styleObj['list-type'] === 'ul') slice.options.bullet = { indent: 20 * 0.75 }
|
||||
}
|
||||
}
|
||||
}
|
||||
parse(ast)
|
||||
@ -266,6 +276,18 @@ export default () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取超链接配置
|
||||
const getLinkOption = (link: PPTElementLink): pptxgen.HyperlinkProps | null => {
|
||||
const { type, target } = link
|
||||
if (type === 'web') return { url: target }
|
||||
if (type === 'slide') {
|
||||
const index = slides.value.findIndex(slide => slide.id === target)
|
||||
if (index !== -1) return { slide: index + 1 }
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
// 导出PPTX文件
|
||||
const exportPPTX = () => {
|
||||
exporting.value = true
|
||||
@ -338,29 +360,30 @@ export default () => {
|
||||
if (el.flipV) options.flipV = el.flipV
|
||||
if (el.rotate) options.rotate = el.rotate
|
||||
if (el.link) {
|
||||
const { type, target } = el.link
|
||||
if (type === 'web') options.hyperlink = { url: target }
|
||||
else if (type === 'slide') {
|
||||
const index = slides.value.findIndex(slide => slide.id === target)
|
||||
if (index !== -1) options.hyperlink = { slide: index + 1 }
|
||||
}
|
||||
const linkOption = getLinkOption(el.link)
|
||||
if (linkOption) options.hyperlink = linkOption
|
||||
}
|
||||
if (el.filters?.opacity) options.transparency = 100 - parseInt(el.filters?.opacity)
|
||||
if (el.clip) {
|
||||
if (el.clip.shape === 'ellipse') options.rounding = true
|
||||
|
||||
const range = el.clip.range
|
||||
const [start, end] = el.clip.range
|
||||
const [startX, startY] = start
|
||||
const [endX, endY] = end
|
||||
|
||||
const originW = el.width / ((endX - startX) / 100)
|
||||
const originH = el.height / ((endY - startY) / 100)
|
||||
|
||||
const originW = el.width / ((range[1][0] - range[0][0]) / 100)
|
||||
const originH = el.height / ((range[1][1] - range[0][1]) / 100)
|
||||
options.w = originW / 100
|
||||
options.h = originH / 100
|
||||
|
||||
const x = range[0][0] / 100 * originW / 100
|
||||
const y = range[0][1] / 100 * originH / 100
|
||||
const w = (range[1][0] - range[0][0]) / 100 * originW / 100
|
||||
const h = (range[1][1] - range[0][1]) / 100 * originH / 100
|
||||
options.sizing = { type: 'crop', w, h, x, y }
|
||||
options.sizing = {
|
||||
type: 'crop',
|
||||
x: startX / 100 * originW / 100,
|
||||
y: startY / 100 * originH / 100,
|
||||
w: (endX - startX) / 100 * originW / 100,
|
||||
h: (endY - startY) / 100 * originH / 100,
|
||||
}
|
||||
}
|
||||
|
||||
pptxSlide.addImage(options)
|
||||
@ -380,12 +403,8 @@ export default () => {
|
||||
}
|
||||
if (el.rotate) options.rotate = el.rotate
|
||||
if (el.link) {
|
||||
const { type, target } = el.link
|
||||
if (type === 'web') options.hyperlink = { url: target }
|
||||
else if (type === 'slide') {
|
||||
const index = slides.value.findIndex(slide => slide.id === target)
|
||||
if (index !== -1) options.hyperlink = { slide: index + 1 }
|
||||
}
|
||||
const linkOption = getLinkOption(el.link)
|
||||
if (linkOption) options.hyperlink = linkOption
|
||||
}
|
||||
|
||||
pptxSlide.addImage(options)
|
||||
@ -413,12 +432,8 @@ export default () => {
|
||||
if (el.shadow) options.shadow = getShadowOption(el.shadow)
|
||||
if (el.outline?.width) options.line = getOutlineOption(el.outline)
|
||||
if (el.link) {
|
||||
const { type, target } = el.link
|
||||
if (type === 'web') options.hyperlink = { url: target }
|
||||
else if (type === 'slide') {
|
||||
const index = slides.value.findIndex(slide => slide.id === target)
|
||||
if (index !== -1) options.hyperlink = { slide: index + 1 }
|
||||
}
|
||||
const linkOption = getLinkOption(el.link)
|
||||
if (linkOption) options.hyperlink = linkOption
|
||||
}
|
||||
|
||||
pptxSlide.addShape('custGeom' as pptxgen.ShapeType, options)
|
||||
@ -635,12 +650,8 @@ export default () => {
|
||||
h: el.height / 100,
|
||||
}
|
||||
if (el.link) {
|
||||
const { type, target } = el.link
|
||||
if (type === 'web') options.hyperlink = { url: target }
|
||||
else if (type === 'slide') {
|
||||
const index = slides.value.findIndex(slide => slide.id === target)
|
||||
if (index !== -1) options.hyperlink = { slide: index + 1 }
|
||||
}
|
||||
const linkOption = getLinkOption(el.link)
|
||||
if (linkOption) options.hyperlink = linkOption
|
||||
}
|
||||
|
||||
pptxSlide.addImage(options)
|
||||
|
Loading…
x
Reference in New Issue
Block a user