Release v1.9.1: ASCII Art & Code Block Preservation

Fixed export of ASCII art, charts, tables, and flowcharts to match preview exactly:
🎨 ASCII Art Preservation:
- Code blocks now export exactly as shown in preview
- Each line rendered separately to preserve exact spacing and alignment
- Monospace font (Consolas) used consistently across all export formats
- Line breaks, spacing, and box-drawing characters preserved perfectly
📝 Word Template Exporter Improvements:
- createCodeBlockXml: Renders each line as separate paragraph with exact spacing
- Added <w:keepLines/> and <w:wordWrap w:val="0"/> for no-wrap behavior
- Enhanced isAsciiArt detection with more Unicode box-drawing characters
- Added support for rounded corners (╭╮╯╰), heavy box chars, more arrows
📄 PDF Export Enhancements:
- Added -V monofont="Consolas" for proper code block rendering
- Added --highlight-style=tango for syntax highlighting
- Applied to main export, fallback engines, and CLI conversion
🖨️ HTML/Electron PDF Improvements:
- Enhanced CSS: white-space: pre, word-wrap: normal
- font-family: Consolas, Monaco, 'Courier New', monospace
- Print media queries preserve code block formatting
- overflow: visible prevents content truncation
🔧 Technical Changes:
- src/wordTemplateExporter.js: Enhanced code block and ASCII art rendering
- src/main.js: Added monofont settings to all PDF export paths
- Updated version to 1.9.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
This commit is contained in:
2025-12-12 09:01:17 +05:30
parent 5e5fcf3172
commit 4966c671de
5 changed files with 207 additions and 41 deletions
+47 -20
View File
@@ -253,23 +253,39 @@ class WordTemplateExporter {
}
/**
* Create code block XML
* Create code block XML - renders each line as separate paragraph
* to preserve exact formatting like in preview (monospace, no wrapping)
*/
createCodeBlockXml(code) {
const escapedCode = this.escapeXml(code);
const lines = code.split('\n');
let xml = '';
return `<w:p>
<w:pPr>
<w:pStyle w:val="Code"/>
</w:pPr>
<w:r>
<w:rPr>
<w:rFonts w:ascii="Consolas" w:hAnsi="Consolas"/>
<w:sz w:val="18"/>
</w:rPr>
<w:t xml:space="preserve">${escapedCode}</w:t>
</w:r>
</w:p>`;
lines.forEach((line, index) => {
const escapedLine = this.escapeXml(line);
// Each line gets its own paragraph with exact spacing and no wrapping
xml += `<w:p>
<w:pPr>
<w:spacing w:before="${index === 0 ? '120' : '0'}" w:after="${index === lines.length - 1 ? '120' : '0'}" w:line="240" w:lineRule="exact"/>
<w:ind w:left="284" w:right="0"/>
<w:jc w:val="left"/>
<w:keepLines/>
<w:wordWrap w:val="0"/>
</w:pPr>
<w:r>
<w:rPr>
<w:rFonts w:ascii="Consolas" w:hAnsi="Consolas" w:cs="Consolas"/>
<w:sz w:val="18"/>
<w:szCs w:val="18"/>
<w:shd w:val="clear" w:color="auto" w:fill="F5F5F5"/>
<w:noProof/>
</w:rPr>
<w:t xml:space="preserve">${escapedLine}</w:t>
</w:r>
</w:p>`;
});
return xml;
}
/**
@@ -397,15 +413,22 @@ class WordTemplateExporter {
isAsciiArt(line) {
// Don't treat markdown tables as ASCII art
if (line.trim().startsWith('|') && line.trim().endsWith('|')) {
return false;
// Check if it's a proper markdown table (has multiple cells)
const cells = line.split('|').filter(c => c.trim());
if (cells.length >= 2) {
return false;
}
}
// Common ASCII art characters
// Common ASCII art characters (Unicode box drawing and symbols)
const asciiArtChars = [
'─', '│', '┌', '┐', '└', '┘', '├', '┤', '┬', '┴', '┼', // Box drawing
'═', '║', '╔', '╗', '╚', '╝', '╠', '╣', '╦', '╩', '╬', // Double box
'', '', '', '', '♦', '●', '○', '■', '□', // Shapes
'', '', '', '', // Arrows
'', '', '', '', // Rounded corners
'', '', '', '', '♦', '●', '○', '■', '□', '◆', '◇', // Shapes
'↓', '→', '←', '↑', '↔', '↕', '⇒', '⇐', '⇓', '⇑', // Arrows
'┃', '━', '┏', '┓', '┗', '┛', '┣', '┫', '┳', '┻', '╋', // Heavy box
'░', '▒', '▓', '█', // Shading
];
// Check for box drawing characters
@@ -415,14 +438,18 @@ class WordTemplateExporter {
// Check for ASCII box patterns with regular characters
const asciiPatterns = [
/^\s*\+[-=_]+\+/, // +-----+
/^\s*\+[-=_+]+\+/, // +-----+ or +=====+
/^\s*\|[-=_]{3,}\|/, // |-----|
/^\s*[-=_]{5,}$/, // -----
/^\s*\+[-]+\+[-]+\+/, // +---+---+
/^\s*\([A-Z][a-z]+\)\s*\|\|/, // (Deformable) ||
/^\s*\|\s+[A-Z\s]+[-:]\s+[A-Z]/, // | TILE ADHESIVE - TYPE
/^\s*\|\s*\[[^\]]+\]/, // | [BIS Mark / CE Mark]
/^\s*\|\s{2,}\w+.*\|\|/, // || text ||
/^\s*\[[^\]]+\]/, // [Step in brackets]
/^\s*\[[^\]]+\]\s*$/, // [Step in brackets]
/^\s*<[-=]+>/, // <----> or <====>
/^\s*\/[-_\\\/]+\//, // /----/
/^\s*\*[-=\*]+\*/, // *----*
];
return asciiPatterns.some(pattern => pattern.test(line));