Skip to main content
Glama
docxHandler.js10.2 kB
import fs from 'fs'; import path from 'path'; import { Document, Paragraph, TextRun, HeadingLevel, AlignmentType, Packer, Table, TableRow, TableCell, WidthType, BorderStyle, UnderlineType } from 'docx'; import mammoth from 'mammoth'; // 确保输出目录存在 function ensureOutputDir() { const outputDir = path.join(process.cwd(), 'documents', 'output'); if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); } } // 创建Word文档 export async function createDocument(fileName, title, content) { ensureOutputDir(); // 创建文档段落 const paragraphs = [ new Paragraph({ children: [ new TextRun({ text: title, bold: true, size: 32, }), ], heading: HeadingLevel.TITLE, alignment: AlignmentType.CENTER, }), new Paragraph({ children: [new TextRun("")], }), ]; // 处理内容(支持换行和段落) // 先处理转义的换行符(处理双反斜杠和单反斜杠的情况) let processedContent = content.replace(/\\\\n/g, '\n'); // 处理双反斜杠 processedContent = processedContent.replace(/\\n/g, '\n'); // 处理单反斜杠 const contentLines = processedContent.split('\n'); contentLines.forEach(line => { // 如果是空行,创建空段落用于分隔 if (line.trim() === '') { paragraphs.push(new Paragraph({ children: [new TextRun("")], })); } else { // 非空行创建正常段落 paragraphs.push(new Paragraph({ children: [new TextRun({ text: line.trim(), size: 24, })], })); } }); const doc = new Document({ sections: [{ properties: {}, children: paragraphs, }], }); // 保存文档 const outputPath = path.join(process.cwd(), 'documents', 'output', `${fileName}.docx`); const buffer = await Packer.toBuffer(doc); fs.writeFileSync(outputPath, buffer); return `✅ Word文档创建成功!\n📁 文件路径: documents/output/${fileName}.docx\n📝 标题: ${title}\n📄 内容长度: ${content.length} 字符`; } // 读取Word文档 export async function readDocument(filePath, format = 'text') { const fullPath = path.join(process.cwd(), 'documents', filePath); if (!fs.existsSync(fullPath)) { throw new Error(`文件不存在: ${filePath}`); } const result = await mammoth.convertToHtml(fs.readFileSync(fullPath)); let output; if (format === 'html') { output = result.value; } else if (format === 'markdown') { // 简单的HTML到Markdown转换 output = result.value .replace(/<h([1-6])>(.*?)<\/h[1-6]>/g, (match, level, text) => '#'.repeat(parseInt(level)) + ' ' + text + '\n') .replace(/<p>(.*?)<\/p>/g, '$1\n\n') .replace(/<strong>(.*?)<\/strong>/g, '**$1**') .replace(/<em>(.*?)<\/em>/g, '*$1*') .replace(/<br\s*\/?>/g, '\n') .replace(/<[^>]*>/g, ''); } else { // 处理文本格式,保留段落结构 output = result.value .replace(/<p[^>]*>(.*?)<\/p>/g, '$1\n\n') // 段落转换为换行 .replace(/<br\s*\/?>/g, '\n') // br标签转换为换行 .replace(/<[^>]*>/g, '') // 移除所有HTML标签 .replace(/\n\s*\n\s*\n/g, '\n\n') // 合并多个连续空行为两个换行 .trim(); } return `📄 文档读取成功 (${format}格式):\n\n${output}`; } // 替换文档中的文本 export async function replaceTextInDocument(filePath, searchText, replaceText, outputFileName) { ensureOutputDir(); const fullPath = path.join(process.cwd(), 'documents', filePath); if (!fs.existsSync(fullPath)) { throw new Error(`文件不存在: ${filePath}`); } // 读取原文档内容 const result = await mammoth.convertToHtml(fs.readFileSync(fullPath)); let htmlContent = result.value; // 执行文本替换 const originalCount = (htmlContent.match(new RegExp(searchText, 'g')) || []).length; htmlContent = htmlContent.replace(new RegExp(searchText, 'g'), replaceText); // 简单转换回文档格式(这里简化处理) const textContent = htmlContent.replace(/<[^>]*>/g, '').replace(/\s+/g, ' ').trim(); const lines = textContent.split(/[.!?]+/).filter(line => line.trim()); const paragraphs = [ new Paragraph({ children: [ new TextRun({ text: "已处理的文档", bold: true, size: 28, }), ], heading: HeadingLevel.HEADING_1, }), ]; lines.forEach(line => { if (line.trim()) { paragraphs.push(new Paragraph({ children: [new TextRun({ text: line.trim() + '.', size: 24, })], })); } }); const doc = new Document({ sections: [{ properties: {}, children: paragraphs, }], }); // 保存文档 const outputPath = path.join(process.cwd(), 'documents', 'output', `${outputFileName}.docx`); const buffer = await Packer.toBuffer(doc); fs.writeFileSync(outputPath, buffer); return `✅ 文本替换完成!\n🔍 查找: "${searchText}"\n✏️ 替换为: "${replaceText}"\n📊 替换次数: ${originalCount}\n📁 输出文件: documents/output/${outputFileName}.docx`; } // 创建格式化文档 export async function createFormattedDocument(fileName, title, contentArray) { ensureOutputDir(); // 创建文档段落 const paragraphs = [ new Paragraph({ children: [ new TextRun({ text: title, bold: true, size: 32, }), ], heading: HeadingLevel.TITLE, alignment: AlignmentType.CENTER, }), new Paragraph({ children: [new TextRun("")], }), ]; // 处理格式化内容 contentArray.forEach(item => { const textRun = new TextRun({ text: item.text, fontFamily: item.fontFamily, size: item.fontSize ? item.fontSize * 2 : 24, bold: item.bold || false, italics: item.italic || false, underline: item.underline ? { color: item.color || '000000', type: UnderlineType.SINGLE } : undefined, color: item.color || '000000', }); const paragraph = new Paragraph({ children: [textRun], alignment: item.alignment === 'center' ? AlignmentType.CENTER : item.alignment === 'right' ? AlignmentType.RIGHT : item.alignment === 'justify' ? AlignmentType.JUSTIFIED : AlignmentType.LEFT, heading: item.heading ? (item.heading === 1 ? HeadingLevel.HEADING_1 : item.heading === 2 ? HeadingLevel.HEADING_2 : item.heading === 3 ? HeadingLevel.HEADING_3 : item.heading === 4 ? HeadingLevel.HEADING_4 : item.heading === 5 ? HeadingLevel.HEADING_5 : item.heading === 6 ? HeadingLevel.HEADING_6 : undefined) : undefined, }); paragraphs.push(paragraph); }); const doc = new Document({ sections: [{ properties: {}, children: paragraphs, }], }); // 保存文档 const outputPath = path.join(process.cwd(), 'documents', 'output', `${fileName}.docx`); const buffer = await Packer.toBuffer(doc); fs.writeFileSync(outputPath, buffer); return `✅ 格式化Word文档创建成功!\n📁 文件路径: documents/output/${fileName}.docx\n📝 标题: ${title}\n📄 内容段落数: ${contentArray.length}`; } // 创建表格文档 export async function createTableDocument(fileName, title, tableData) { ensureOutputDir(); // 创建标题段落 const paragraphs = [ new Paragraph({ children: [ new TextRun({ text: title, bold: true, size: 32, }), ], heading: HeadingLevel.TITLE, alignment: AlignmentType.CENTER, }), new Paragraph({ children: [new TextRun("")], }), ]; // 创建表格行 const tableRows = []; // 添加标题行 const headerRow = new TableRow({ children: tableData.headers.map(header => new TableCell({ children: [ new Paragraph({ children: [ new TextRun({ text: header, bold: true, size: 24, }), ], alignment: AlignmentType.CENTER, }), ], shading: { fill: "e5f3ff", }, }) ), }); tableRows.push(headerRow); // 添加数据行 tableData.rows.forEach(row => { const tableRow = new TableRow({ children: row.map(cell => new TableCell({ children: [ new Paragraph({ children: [ new TextRun({ text: cell, size: 22, }), ], }), ], }) ), }); tableRows.push(tableRow); }); // 创建表格 const table = new Table({ rows: tableRows, width: { size: 100, type: WidthType.PERCENTAGE, }, borders: { top: { style: BorderStyle.SINGLE, size: 1, color: "000000" }, bottom: { style: BorderStyle.SINGLE, size: 1, color: "000000" }, left: { style: BorderStyle.SINGLE, size: 1, color: "000000" }, right: { style: BorderStyle.SINGLE, size: 1, color: "000000" }, insideHorizontal: { style: BorderStyle.SINGLE, size: 1, color: "000000" }, insideVertical: { style: BorderStyle.SINGLE, size: 1, color: "000000" }, }, }); paragraphs.push(new Paragraph({ children: [new TextRun("")] })); const doc = new Document({ sections: [{ properties: {}, children: [...paragraphs, table], }], }); // 保存文档 const outputPath = path.join(process.cwd(), 'documents', 'output', `${fileName}.docx`); const buffer = await Packer.toBuffer(doc); fs.writeFileSync(outputPath, buffer); return `✅ 表格Word文档创建成功!\n📁 文件路径: documents/output/${fileName}.docx\n📝 标题: ${title}\n📊 表格大小: ${tableData.headers.length} 列 × ${tableData.rows.length + 1} 行`; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/starzzzzzzzzzzzzzz/mcp-tools'

If you have feedback or need assistance with the MCP directory API, please join our Discord server