UNPKG

html-to-pptx

Version:

Convert page elements to pptx. See README for more details.

214 lines (199 loc) 8.28 kB
import PptxGenJS from 'pptxgenjs'; /** * 将 markdown 转换为 pptx * @param pageClass 页面样式类名 * @param ppt pptx 实例对象 */ export function markdown2pptx(pageClass: string): PptxGenJS { const ppt = new PptxGenJS(); const pageDoms = Array.from(document.querySelectorAll(pageClass)).filter(element => { // @ts-ignore return element.offsetWidth > 0 && element.offsetHeight > 0 && getComputedStyle(element).visibility !== 'hidden'; }); Array.from(pageDoms).forEach(dom => { const slide = ppt.addSlide(); const {styles, attributes} = traverseElements(dom as Element); // 调用遍历函数 const backgroundImage = styles['background-image'] && styles['background-image']?.match(/url\(["']?([^"']*)["']?\)/)?.length ? styles['background-image']?.match(/url\(["']?([^"']*)["']?\)/)[1] : null; slide.background = {color: rgbToHex(styles['background-color']), path: backgroundImage}; // 遍历子元素 dom.childNodes.forEach((child: any) => { if (child.nodeType === Node.ELEMENT_NODE) { // 确保是元素节点 const text = child.innerHTML; const {styles, attributes} = traverseElements(child as Element); // 调用遍历函数 const {width, height, top, left, color} = styles; const alignItems = styles['align-items']; const justifyContent = styles['justify-content']; const fontSize = parseInt(styles['font-size'], 10) * .3; const fontWeight = styles['font-weight']; const borderColor = styles['border-color']; if (attributes['type'] === 'text') { if (text.startsWith('<ul') || text.startsWith('<li')) { let listItems; listItems = child.querySelectorAll('li'); let computeHeight = 0; //当前是否是第一个加粗的 默认为true let isFirstBold = true; let prePaddingBottom = 0 listItems.forEach((item: Element) => { const {styles: liStyles} = traverseElements(item); // 调用遍历函数 const { width: liWidth, height: liHeight, top: liTop, left: liLeft, color: liColor } = liStyles; const paddingLeft = liStyles['padding-left'] const paddingRight = liStyles['padding-right'] const paddingTop = liStyles['padding-top'] const paddingBottom = liStyles['padding-bottom'] const liAlignItems = liStyles['align-items']; const liJustifyContent = liStyles['justify-content']; const liFontSize = parseInt(liStyles['font-size'], 10) * .3; const liFontFace = liStyles['font-family']; const liFontWeight = liStyles['font-weight']; const liBorderColor = liStyles['border-color']; const {x: liX, y: liY, height: h, width: w} = item.getBoundingClientRect() let bold = false; if (liFontWeight === 'bold' || liFontWeight > 400) { bold = true if (!isFirstBold){ //不是第一个加粗的 控制一下间距 computeHeight += prePaddingBottom; } if (isFirstBold) { //如果不是 isFirstBold = false; } } // @ts-ignore slide.addText(item.textContent.trim(), { w: PxToPPT(liWidth), h: PxToPPT(liHeight), x: PxToPPT(left), y: PxToPPT(top) + computeHeight, align: justifyContentToPPTalign(liJustifyContent), valign: alignItemsToPPTvalign(liAlignItems), fontSize: liFontSize, color: rgbToHex(liColor), bold: bold, bullet: text.startsWith('<li'), fontFace: liFontFace.split(',')[0]?.trim(), margin: [PxToPPT(paddingTop), PxToPPT(paddingRight), PxToPPT(paddingBottom), PxToPPT(paddingLeft)], }); computeHeight += PxToPPT(liHeight); prePaddingBottom = PxToPPT(paddingBottom); }) } else { slide.addText(text, { w: PxToPPT(width), h: PxToPPT(height), x: PxToPPT(left), y: PxToPPT(top), align: justifyContentToPPTalign(justifyContent), valign: alignItemsToPPTvalign(alignItems), fontSize, color: rgbToHex(color), bold: fontWeight === 'bold' || fontWeight > 400, }); } } else if (attributes['type'] === 'image') { slide.addImage({ path: attributes['src'], x: PxToPPT(left), y: PxToPPT(top), w: PxToPPT(width), h: PxToPPT(height), }); } else if (attributes['type'] === 'table') { try { const tableData: any = []; // 假设您需要从表格中提取数据 const rows = child.getElementsByTagName('tr'); for (let row of rows) { const rowData = []; const cells = row.getElementsByTagName('td'); for (let cell of cells) { rowData.push(cell.innerText); } tableData.push(rowData); } slide.addTable(tableData, { w: PxToPPT(width), h: PxToPPT(height), x: PxToPPT(left), y: PxToPPT(top), align: justifyContentToPPTalign(justifyContent), valign: alignItemsToPPTvalign(alignItems), fontSize, color: rgbToHex(color), bold: fontWeight === 'bold' || fontWeight > 400, border: {color: rgbToHex(borderColor)}, }); } catch (error) { console.error('Error parsing table data:', error); } } } }); }) return ppt; } /** 像素转为 16:9 比例的 pptx 页宽 */ function PxToPPT(pxVal: string): number { return parseInt(pxVal, 10) / 192; } /** 垂直对齐方式转换为 pptx 的 valign */ function alignItemsToPPTvalign(val: string): any { switch (val) { case 'flex-start': case 'top': return 'top'; // PPTX 中对应于顶部对齐 case 'center': return 'middle'; // PPTX 中对应于垂直居中 case 'flex-end': case 'bottom': return 'bottom'; // PPTX 中对应于底部对齐 case 'stretch': return 'middle'; // 默认为中间,可以根据需求调整 default: return 'middle'; // 默认返回中间对齐 } } /** 水平对齐方式转换为 pptx 的 align */ function justifyContentToPPTalign(val: string): any { switch (val) { case 'flex-start': case 'left': return 'left'; // PPTX 中对应于左对齐 case 'center': return 'center'; // PPTX 中对应于水平居中 case 'flex-end': case 'right': return 'right'; // PPTX 中对应于右对齐 case 'space-between': return 'justify'; // PPTX 中的两端对齐 case 'space-around': case 'space-evenly': return 'justify'; // PPTX 中的均匀对齐 default: return 'left'; // 默认返回左对齐 } } /** rgb 转 hex */ function rgbToHex(rgb: string) { const result = rgb.match(/\d+/g)?.map(x => { const hex = parseInt(x).toString(16); return hex.length === 1 ? '0' + hex : hex; }); return `#${result?.join('')}`; } function traverseElements(element: Element): { styles: any, attributes: any } { // 获取元素的属性 const attributes: any = {}; Array.from(element.attributes).forEach(attr => attributes[attr.name] = attr.value); // 获取元素的样式 const computedStyle = window.getComputedStyle(element); const styles: any = {}; Array.from(computedStyle).forEach(style => styles[style] = computedStyle.getPropertyValue(style)); return {styles, attributes} }