mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-08-02 10:00:17 +05:30
fix(word): strip HTML style blocks and alignment divs from DOCX input
Pre-process markdown before Word/DOCX export to remove raw HTML artifacts (<style> blocks, HTML comments, and <div align=...> tags) that were visible in the generated document. Applies to single and batch DOCX exports via both Pandoc and WordTemplateExporter paths.
This commit is contained in:
+56
-4
@@ -2562,7 +2562,23 @@ function performExportWithOptions(format, options) {
|
||||
|
||||
// Use pandoc for export with advanced options
|
||||
|
||||
let pandocCmd = `${getPandocPath()} "${currentFile}" -o "${outputFile}"`;
|
||||
let inputFile = currentFile;
|
||||
let tempInputFile = null;
|
||||
|
||||
// Pre-process markdown for Word output so that <style> blocks, HTML
|
||||
// comments, and alignment <div> tags do not appear as visible text.
|
||||
if (format === 'docx') {
|
||||
const content = fs.readFileSync(currentFile, 'utf-8');
|
||||
const cleanedContent = WordTemplateExporter.preprocessMarkdownForWordExport(content);
|
||||
tempInputFile = path.join(
|
||||
require('os').tmpdir(),
|
||||
`mc_export_${Date.now()}_${path.basename(currentFile)}`
|
||||
);
|
||||
fs.writeFileSync(tempInputFile, cleanedContent, 'utf-8');
|
||||
inputFile = tempInputFile;
|
||||
}
|
||||
|
||||
let pandocCmd = `${getPandocPath()} "${inputFile}" -o "${outputFile}"`;
|
||||
|
||||
// Add template if specified
|
||||
if (options.template && options.template !== 'default') {
|
||||
@@ -2657,7 +2673,15 @@ function performExportWithOptions(format, options) {
|
||||
});
|
||||
} else if (format === 'docx') {
|
||||
pandocCmd += ' -t docx';
|
||||
exportWithPandoc(pandocCmd, outputFile, format);
|
||||
exportWithPandoc(pandocCmd, outputFile, format, () => {
|
||||
if (tempInputFile) {
|
||||
try {
|
||||
fs.unlinkSync(tempInputFile);
|
||||
} catch {
|
||||
// Ignore cleanup errors
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (format === 'pptx') {
|
||||
// Add PowerPoint footer if enabled
|
||||
if (headerFooterSettings.enabled && headerFooterSettings.footer.center) {
|
||||
@@ -2859,7 +2883,7 @@ function showExportSuccess(outputFile) {
|
||||
}
|
||||
|
||||
// Helper function to export with pandoc (general) - uses runPandocCmd for safety
|
||||
function exportWithPandoc(pandocCmd, outputFile, format) {
|
||||
function exportWithPandoc(pandocCmd, outputFile, format, onComplete) {
|
||||
runPandocCmd(pandocCmd, async (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
console.error(`Pandoc error for ${format}:`, error);
|
||||
@@ -2924,6 +2948,9 @@ function exportWithPandoc(pandocCmd, outputFile, format) {
|
||||
}
|
||||
showExportSuccess(outputFile);
|
||||
}
|
||||
if (typeof onComplete === 'function') {
|
||||
onComplete(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3749,7 +3776,23 @@ async function performBatchConversion(inputFolder, outputFolder, format, options
|
||||
}
|
||||
|
||||
// Build pandoc command for other formats
|
||||
let pandocCmd = `${getPandocPath()} "${inputFile}" -o "${outputFile}"`;
|
||||
let pandocInputFile = inputFile;
|
||||
let batchTempInputFile = null;
|
||||
|
||||
// Pre-process markdown for Word output so that <style> blocks, HTML
|
||||
// comments, and alignment <div> tags do not appear as visible text.
|
||||
if (format === 'docx') {
|
||||
const content = fs.readFileSync(inputFile, 'utf-8');
|
||||
const cleanedContent = WordTemplateExporter.preprocessMarkdownForWordExport(content);
|
||||
batchTempInputFile = path.join(
|
||||
require('os').tmpdir(),
|
||||
`mc_batch_export_${Date.now()}_${path.basename(inputFile)}`
|
||||
);
|
||||
fs.writeFileSync(batchTempInputFile, cleanedContent, 'utf-8');
|
||||
pandocInputFile = batchTempInputFile;
|
||||
}
|
||||
|
||||
let pandocCmd = `${getPandocPath()} "${pandocInputFile}" -o "${outputFile}"`;
|
||||
|
||||
// Add template if specified
|
||||
if (options.template && options.template !== 'default') {
|
||||
@@ -3850,6 +3893,15 @@ async function performBatchConversion(inputFolder, outputFolder, format, options
|
||||
|
||||
// Execute conversion (using runPandocCmd for safety)
|
||||
runPandocCmd(pandocCmd, async (error, _stdout, stderr) => {
|
||||
// Clean up temporary pre-processed input file
|
||||
if (batchTempInputFile) {
|
||||
try {
|
||||
fs.unlinkSync(batchTempInputFile);
|
||||
} catch {
|
||||
// Ignore cleanup errors
|
||||
}
|
||||
}
|
||||
|
||||
if (error) {
|
||||
console.error(`Batch: Failed to convert ${path.basename(inputFile)}:`, error.message, stderr);
|
||||
} else {
|
||||
|
||||
@@ -15,6 +15,26 @@ class WordTemplateExporter {
|
||||
this.pageSettings = pageSettings; // Page size and orientation settings
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip HTML artifacts that Pandoc / Word cannot render and that would
|
||||
* otherwise appear as visible text in DOCX output.
|
||||
* Removes HTML comments, <style> blocks, and alignment <div> tags.
|
||||
*/
|
||||
static preprocessMarkdownForWordExport(markdown) {
|
||||
if (typeof markdown !== 'string') return markdown;
|
||||
return (
|
||||
markdown
|
||||
// HTML comments (multi-line)
|
||||
.replace(/<!--[\s\S]*?-->/g, '')
|
||||
// <style> blocks (case-insensitive, multi-line)
|
||||
.replace(/<style\b[\s\S]*?<\/style>/gi, '')
|
||||
// Opening <div align="..."> tags
|
||||
.replace(/<div\b[^>]*?\balign\s*=\s*["'][^"']*["'][^>]*>/gi, '')
|
||||
// Closing </div> tags
|
||||
.replace(/<\/div\s*>/gi, '')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert markdown to Word document using template
|
||||
*/
|
||||
@@ -33,7 +53,8 @@ class WordTemplateExporter {
|
||||
}
|
||||
|
||||
// Parse markdown and generate Word XML
|
||||
const newContentXml = this.markdownToWordXml(markdownContent);
|
||||
const cleanedContent = WordTemplateExporter.preprocessMarkdownForWordExport(markdownContent);
|
||||
const newContentXml = this.markdownToWordXml(cleanedContent);
|
||||
|
||||
// Insert new content after the specified start page
|
||||
const modifiedXml = this.insertContentAfterPage(documentXml, newContentXml, this.startPage);
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Tests for WordTemplateExporter
|
||||
*/
|
||||
|
||||
const WordTemplateExporter = require('../src/wordTemplateExporter');
|
||||
|
||||
describe('WordTemplateExporter.preprocessMarkdownForWordExport', () => {
|
||||
test('removes HTML style blocks', () => {
|
||||
const input = `<style>
|
||||
<!-- sneh-a4-print v1 -->
|
||||
@media print { body { font-size: 8pt; } }
|
||||
</style>
|
||||
|
||||
# Heading
|
||||
`;
|
||||
const output = WordTemplateExporter.preprocessMarkdownForWordExport(input);
|
||||
expect(output).not.toContain('<style');
|
||||
expect(output).not.toContain('</style>');
|
||||
expect(output).not.toContain('sneh-a4-print');
|
||||
expect(output).not.toContain('@media print');
|
||||
expect(output).toContain('# Heading');
|
||||
});
|
||||
|
||||
test('removes HTML comments outside style blocks', () => {
|
||||
const input = `<!-- comment -->
|
||||
Hello world
|
||||
`;
|
||||
const output = WordTemplateExporter.preprocessMarkdownForWordExport(input);
|
||||
expect(output).not.toContain('<!--');
|
||||
expect(output).not.toContain('-->');
|
||||
expect(output).toContain('Hello world');
|
||||
});
|
||||
|
||||
test('removes alignment div tags', () => {
|
||||
const input = `<div align="center">
|
||||
Centered content
|
||||
</div>
|
||||
`;
|
||||
const output = WordTemplateExporter.preprocessMarkdownForWordExport(input);
|
||||
expect(output).not.toContain('<div align="center">');
|
||||
expect(output).not.toContain('</div>');
|
||||
expect(output).toContain('Centered content');
|
||||
});
|
||||
|
||||
test('preserves regular markdown content', () => {
|
||||
const input = `# Title
|
||||
|
||||
| A | B |
|
||||
|---|---|
|
||||
| 1 | 2 |
|
||||
`;
|
||||
const output = WordTemplateExporter.preprocessMarkdownForWordExport(input);
|
||||
expect(output).toBe(input);
|
||||
});
|
||||
|
||||
test('handles content with no HTML artifacts', () => {
|
||||
const input = 'Plain text paragraph.';
|
||||
const output = WordTemplateExporter.preprocessMarkdownForWordExport(input);
|
||||
expect(output).toBe(input);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user