mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-08-24 07:20:16 +05:30
- Make the HTML preprocessor code-block and inline-code aware so code examples containing <style> / <div> / comments are preserved. - Strip all raw <div> tags (not just alignment attributes) to avoid malformed output from unmatched closing tags. - Handle uppercase tags and single/unquoted attributes. - Create temporary DOCX input files inside private mkdtemp directories instead of predictable names in the shared temp directory. - Wrap batch DOCX preprocessing in try/catch so one unreadable file does not abort the entire batch. - Add regression tests for the edge cases above.
878 lines
26 KiB
JavaScript
878 lines
26 KiB
JavaScript
/**
|
|
* Word Template Exporter
|
|
* Loads word_template.docx, preserves first 2 pages (cover + TOC),
|
|
* and adds markdown content starting from page 3 using template styles
|
|
*/
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const PizZip = require('pizzip');
|
|
|
|
class WordTemplateExporter {
|
|
constructor(templatePath, startPage = 3, pageSettings = null) {
|
|
this.templatePath = templatePath || path.join(__dirname, '../word_template.docx');
|
|
this.startPage = startPage; // Which page to start inserting content
|
|
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 <div> tags (including
|
|
* alignment attributes). Code blocks and inline code spans are preserved.
|
|
*/
|
|
static preprocessMarkdownForWordExport(markdown) {
|
|
if (typeof markdown !== 'string') return markdown;
|
|
|
|
// Split markdown into code-block and non-code-block segments so we do
|
|
// not strip HTML that the author intentionally placed inside fences.
|
|
const segments = [];
|
|
let inCodeBlock = false;
|
|
let currentLines = [];
|
|
|
|
const flush = () => {
|
|
if (currentLines.length === 0) return;
|
|
segments.push({
|
|
type: inCodeBlock ? 'code' : 'text',
|
|
text: currentLines.join('\n'),
|
|
});
|
|
currentLines = [];
|
|
};
|
|
|
|
for (const line of markdown.split('\n')) {
|
|
if (/^\s*```/.test(line)) {
|
|
if (inCodeBlock) {
|
|
currentLines.push(line);
|
|
flush();
|
|
inCodeBlock = false;
|
|
} else {
|
|
flush();
|
|
currentLines.push(line);
|
|
inCodeBlock = true;
|
|
}
|
|
} else {
|
|
currentLines.push(line);
|
|
}
|
|
}
|
|
flush();
|
|
|
|
const stripArtifacts = (text) => {
|
|
// Protect inline code spans so HTML inside backticks is preserved.
|
|
const codeSpans = [];
|
|
const protectedText = text.replace(/`[^`]+`/g, (match) => {
|
|
codeSpans.push(match);
|
|
return ` |