mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-08-02 10:00:17 +05:30
feat(main): wire monospace embedders into PDF/DOCX/EPUB/HTML exports
- Add getActiveMonospaceContext() helper that reads settings + resolves TTF - PDF: build fontspec header and pass --include-in-header to xelatex - DOCX: post-process zip to embed TTF and add fontTable.xml + rels - EPUB: prepend --epub-embed-font + patch manifest - HTML: embed woff2 base64 into built-in marked exporter CSS - Register get-monospace-settings / set-monospace-settings IPC at startup
This commit is contained in:
@@ -5,6 +5,7 @@ const { CrashWriter } = require('./updater/crash-writer');
|
|||||||
const { MigrationRunner } = require('./updater/migration-runner');
|
const { MigrationRunner } = require('./updater/migration-runner');
|
||||||
const updaterHandlers = require('./ipc/updater-handlers');
|
const updaterHandlers = require('./ipc/updater-handlers');
|
||||||
const crashHandlers = require('./ipc/crash-handlers');
|
const crashHandlers = require('./ipc/crash-handlers');
|
||||||
|
const monospaceHandlers = require('./ipc/monospace-handlers');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const { execFile } = require('child_process');
|
const { execFile } = require('child_process');
|
||||||
@@ -14,6 +15,24 @@ const { validatePath, resolveWritablePath, isPathAccessible } = require('./utils
|
|||||||
const fileOps = require('./files');
|
const fileOps = require('./files');
|
||||||
const menu = require('./menu');
|
const menu = require('./menu');
|
||||||
const { createMainWindow } = require('./window');
|
const { createMainWindow } = require('./window');
|
||||||
|
const MonospaceFontConfig = require('./MonospaceFontConfig');
|
||||||
|
const { buildPdfFontHeader } = require('./PdfFontHeader');
|
||||||
|
const { embedDocxFont } = require('./DocxFontEmbedder');
|
||||||
|
const { withEpubEmbedFontArgs, embedEpubFont } = require('./EpubFontEmbedder');
|
||||||
|
const { buildExportCss } = require('./ExportCss');
|
||||||
|
const { safeMonospaceSettings, DEFAULT_SETTINGS: MONO_DEFAULTS } = require('./settings/monospaceSettings');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read active monospace settings + TTF path. Returns null TTF if asset missing.
|
||||||
|
*/
|
||||||
|
function getActiveMonospaceContext() {
|
||||||
|
const settings = safeMonospaceSettings({
|
||||||
|
monospaceFont: store.get('monospaceFont', MONO_DEFAULTS.monospaceFont),
|
||||||
|
monospaceLigatures: store.get('monospaceLigatures', MONO_DEFAULTS.monospaceLigatures),
|
||||||
|
});
|
||||||
|
const ttf = MonospaceFontConfig.getMonoFontTtfPath(settings.monospaceFont, 400);
|
||||||
|
return { settings, ttf };
|
||||||
|
}
|
||||||
|
|
||||||
// Add MiKTeX to PATH for LaTeX support
|
// Add MiKTeX to PATH for LaTeX support
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
@@ -1577,6 +1596,23 @@ function performExportWithOptions(format, options) {
|
|||||||
pandocCmd += ' -V monofont="Consolas"';
|
pandocCmd += ' -V monofont="Consolas"';
|
||||||
pandocCmd += ' --highlight-style=tango';
|
pandocCmd += ' --highlight-style=tango';
|
||||||
|
|
||||||
|
// When bundled TTF asset is present, replace system Consolas with our
|
||||||
|
// active monospace font (xelatex fontspec) so ASCII art renders
|
||||||
|
// identically across machines.
|
||||||
|
const monoCtx = getActiveMonospaceContext();
|
||||||
|
if (monoCtx.ttf) {
|
||||||
|
try {
|
||||||
|
const { headerPath } = buildPdfFontHeader(
|
||||||
|
monoCtx.settings,
|
||||||
|
monoCtx.ttf,
|
||||||
|
MonospaceFontConfig.getActiveFamily(monoCtx.settings)
|
||||||
|
);
|
||||||
|
pandocCmd += ` --include-in-header="${headerPath}"`;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[monospace] PDF header build failed (non-fatal):', e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add header/footer if enabled
|
// Add header/footer if enabled
|
||||||
if (headerFooterSettings.enabled) {
|
if (headerFooterSettings.enabled) {
|
||||||
const filename = currentFile
|
const filename = currentFile
|
||||||
@@ -1726,6 +1762,12 @@ function performExportWithOptions(format, options) {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Generic export for other formats
|
// Generic export for other formats
|
||||||
|
if (format === 'epub') {
|
||||||
|
const monoCtx = getActiveMonospaceContext();
|
||||||
|
if (monoCtx.ttf) {
|
||||||
|
pandocCmd = ` --epub-embed-font="${monoCtx.ttf}"` + pandocCmd;
|
||||||
|
}
|
||||||
|
}
|
||||||
exportWithPandoc(pandocCmd, outputFile, format);
|
exportWithPandoc(pandocCmd, outputFile, format);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -1866,6 +1908,22 @@ function exportWithPandoc(pandocCmd, outputFile, format) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Embed active monospace TTF into DOCX so code blocks render in the
|
||||||
|
// bundled font even when the recipient opens the file without JetBrains
|
||||||
|
// Mono or Fira Code installed.
|
||||||
|
if (format === 'docx') {
|
||||||
|
const monoCtx = getActiveMonospaceContext();
|
||||||
|
if (monoCtx.ttf) {
|
||||||
|
try {
|
||||||
|
const family = MonospaceFontConfig.getActiveFamily(monoCtx.settings).replace(/\s+/g, '');
|
||||||
|
await embedDocxFont(outputFile, outputFile, monoCtx.ttf, family);
|
||||||
|
console.log(`[monospace] Embedded ${family} TTF into DOCX`);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[monospace] DOCX embed failed (non-fatal):', e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add headers/footers to DOCX if enabled
|
// Add headers/footers to DOCX if enabled
|
||||||
if (format === 'docx' && headerFooterSettings.enabled) {
|
if (format === 'docx' && headerFooterSettings.enabled) {
|
||||||
try {
|
try {
|
||||||
@@ -1895,6 +1953,20 @@ function exportWithPandoc(pandocCmd, outputFile, format) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Patch EPUB manifest so the font table references the embedded TTF.
|
||||||
|
if (format === 'epub') {
|
||||||
|
const monoCtx = getActiveMonospaceContext();
|
||||||
|
if (monoCtx.ttf) {
|
||||||
|
try {
|
||||||
|
const family = MonospaceFontConfig.getActiveFamily(monoCtx.settings);
|
||||||
|
await embedEpubFont(outputFile, monoCtx.ttf, family);
|
||||||
|
console.log(`[monospace] Patched EPUB manifest for ${family}`);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[monospace] EPUB manifest patch failed (non-fatal):', e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ODT header/footer is not supported by Pandoc's ODT output
|
// ODT header/footer is not supported by Pandoc's ODT output
|
||||||
// Headers/footers are applied only for DOCX format
|
// Headers/footers are applied only for DOCX format
|
||||||
|
|
||||||
@@ -2003,6 +2075,18 @@ function exportToHTML(outputFile) {
|
|||||||
word-wrap: normal;
|
word-wrap: normal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
${(() => {
|
||||||
|
try {
|
||||||
|
const monoCtx = getActiveMonospaceContext();
|
||||||
|
const familyName = MonospaceFontConfig.getActiveFamily(monoCtx.settings);
|
||||||
|
const woff2Name = familyName === 'Fira Code' ? 'FiraCode-Regular.woff2' : 'JetBrainsMono-Regular.woff2';
|
||||||
|
const woff2Path = path.join(path.dirname(monoCtx.ttf || ''), woff2Name);
|
||||||
|
if (!fs.existsSync(woff2Path)) return '';
|
||||||
|
return buildExportCss(monoCtx.settings, { woff2: fs.readFileSync(woff2Path) });
|
||||||
|
} catch (e) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
})()}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -3172,6 +3256,7 @@ app.whenReady().then(() => {
|
|||||||
crash,
|
crash,
|
||||||
getMainWindow: () => mainWindow,
|
getMainWindow: () => mainWindow,
|
||||||
});
|
});
|
||||||
|
monospaceHandlers.register();
|
||||||
// --------------------------------------------------------------------
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
// Register file ops IPC handlers
|
// Register file ops IPC handlers
|
||||||
|
|||||||
Reference in New Issue
Block a user