diff --git a/packages/docx/src/docx/exportDocx.ts b/packages/docx/src/docx/exportDocx.ts index 94b7648..936dfd1 100644 --- a/packages/docx/src/docx/exportDocx.ts +++ b/packages/docx/src/docx/exportDocx.ts @@ -4,7 +4,9 @@ import { ElementType, TitleLevel, ListStyle, - Command + Command, + RowFlex, + IEditorData } from '@hufe921/canvas-editor' import { Document, @@ -22,7 +24,8 @@ import { WidthType, TableRow, TableCell, - MathRun + MathRun, + AlignmentType } from 'docx' import { saveAs } from './utils' @@ -36,6 +39,18 @@ const titleLevelToHeadingLevel = { [TitleLevel.SIXTH]: HeadingLevel.HEADING_6 } +// 水平对齐映射 +const RowFlexToAlignmentType = { + [RowFlex.LEFT]: AlignmentType.LEFT, + [RowFlex.CENTER]: AlignmentType.CENTER, + [RowFlex.RIGHT]: AlignmentType.RIGHT, + [RowFlex.ALIGNMENT]: AlignmentType.BOTH +} + +type PxToPtHandler = (size?: number) => number + +let pxToPtHandler: PxToPtHandler = (size?: number) => (size || 16) / 0.75 + function convertElementToParagraphChild(element: IElement): ParagraphChild { if (element.type === ElementType.IMAGE) { return new ImageRun({ @@ -69,7 +84,7 @@ function convertElementToParagraphChild(element: IElement): ParagraphChild { font: element.font, text: element.value, bold: element.bold, - size: `${(element.size || 16) / 0.75}pt`, + size: `${pxToPtHandler(element.size)}pt`, color: Color(element.color).hex() || '#000000', italics: element.italic, strike: element.strikeout, @@ -88,14 +103,18 @@ function convertElementListToDocxChildren( let paragraphChild: ParagraphChild[] = [] + let alignment: AlignmentType | undefined = undefined + function appendParagraph() { if (paragraphChild.length) { children.push( new Paragraph({ + alignment, children: paragraphChild }) ) paragraphChild = [] + alignment = undefined } } @@ -103,39 +122,45 @@ function convertElementListToDocxChildren( const element = elementList[e] if (element.type === ElementType.TITLE) { appendParagraph() + const valueList = element.valueList || [] + const rowFlex = valueList[0]?.rowFlex children.push( new Paragraph({ heading: titleLevelToHeadingLevel[element.level!], - children: - element.valueList?.map(child => - convertElementToParagraphChild(child) - ) || [] + alignment: rowFlex ? RowFlexToAlignmentType[rowFlex] : undefined, + children: valueList.map(child => + convertElementToParagraphChild(child) + ) }) ) } else if (element.type === ElementType.LIST) { appendParagraph() // 拆分列表 - const listChildren = - element.valueList - ?.map(item => item.value) - .join('') - .split('\n') - .map( - (text, index) => - new Paragraph({ - children: [ - new TextRun({ - text: `${ - !element.listStyle || - element.listStyle === ListStyle.DECIMAL - ? `${index + 1}. ` - : `• ` - }${text}` - }) - ] - }) - ) || [] - children.push(...listChildren) + const valueList = element.valueList || [] + const isDecimal = + !element.listStyle || element.listStyle === ListStyle.DECIMAL + valueList.reduce((count, cur) => { + if (cur.value !== '\n') { + cur.value + .replace(/^\n/, '') + .split('\n') + .forEach(text => { + children.push( + new Paragraph({ + alignment: cur.rowFlex + ? RowFlexToAlignmentType[cur.rowFlex] + : undefined, + children: [ + new TextRun({ + text: `${isDecimal ? `${++count}. ` : `• `}${text}` + }) + ] + }) + ) + }) + } + return count + }, 0) } else if (element.type === ElementType.TABLE) { appendParagraph() const { trList } = element @@ -169,17 +194,38 @@ function convertElementListToDocxChildren( }) ) } else if (element.type === ElementType.DATE) { + const valueList = element.valueList || [] + const rowFlex = valueList[0]?.rowFlex + if (rowFlex && !alignment) { + alignment = RowFlexToAlignmentType[rowFlex] + } paragraphChild.push( - ...(element.valueList?.map(child => - convertElementToParagraphChild(child) - ) || []) + ...valueList.map(child => convertElementToParagraphChild(child)) ) } else { - if (/^\n/.test(element.value)) { - appendParagraph() - element.value = element.value.replace(/^\n/, '') + if (/\n/.test(element.value)) { + let valueList = element.value.split('\n') + // \n\n... + if (valueList.every(value => value === '')) { + valueList = valueList.slice(1) + } + valueList.forEach(value => { + if (value !== '') { + if (element.rowFlex && !alignment) { + alignment = RowFlexToAlignmentType[element.rowFlex] + } + paragraphChild.push( + convertElementToParagraphChild({ ...element, value }) + ) + } + appendParagraph() + }) + } else { + if (element.rowFlex && !alignment) { + alignment = RowFlexToAlignmentType[element.rowFlex] + } + paragraphChild.push(convertElementToParagraphChild(element)) } - paragraphChild.push(convertElementToParagraphChild(element)) } } appendParagraph() @@ -196,31 +242,38 @@ declare module '@hufe921/canvas-editor' { } } +export function createDocumentByData(data: IEditorData) { + const { header, main, footer } = data + return new Document({ + sections: [ + { + headers: { + default: new Header({ + children: convertElementListToDocxChildren(header || []) + }) + }, + footers: { + default: new Footer({ + children: convertElementListToDocxChildren(footer || []) + }) + }, + children: convertElementListToDocxChildren(main || []) + } + ] + }) +} + +export function setPxToPtHandler(handler: PxToPtHandler) { + if (typeof handler === 'function') { + pxToPtHandler = handler + } +} + export default function (command: Command) { return function (options: IExportDocxOption) { const { fileName } = options - const { - data: { header, main, footer } - } = command.getValue() - - const doc = new Document({ - sections: [ - { - headers: { - default: new Header({ - children: convertElementListToDocxChildren(header || []) - }) - }, - footers: { - default: new Footer({ - children: convertElementListToDocxChildren(footer || []) - }) - }, - children: convertElementListToDocxChildren(main || []) - } - ] - }) - + const { data } = command.getValue() + const doc = createDocumentByData(data) Packer.toBlob(doc).then(blob => { saveAs(blob, `${fileName}.docx`) })