diff --git a/src/main/PdfFontHeader.js b/src/main/PdfFontHeader.js new file mode 100644 index 0000000..523ed30 --- /dev/null +++ b/src/main/PdfFontHeader.js @@ -0,0 +1,29 @@ +'use strict'; + +const fs = require('fs'); +const os = require('os'); +const path = require('path'); + +function buildPdfFontHeader(settings, ttfPath, fontFamily) { + if (!ttfPath || !fs.existsSync(ttfPath)) { + throw new Error(`PdfFontHeader: TTF not found at ${ttfPath}`); + } + const ligatures = !!(settings && settings.monospaceLigatures === true); + const fontDir = path.dirname(ttfPath); + const basename = path.basename(ttfPath); + const boldName = basename.replace('Regular', 'Bold').replace('Medium', 'Bold'); + const lines = [ + `\\setmonofont[Path = ${fontDir}/,`, + ` Extension = .ttf,`, + ` UprightFont = ${basename},`, + ` BoldFont = ${boldName},`, + ligatures ? ' Ligatures=TeX,' : '', + ` Scale = 0.9]`, + `{${fontFamily}}`, + ].filter(Boolean); + const headerPath = path.join(os.tmpdir(), `monospace-pdf-${Date.now()}-${process.pid}.tex`); + fs.writeFileSync(headerPath, lines.join('\n'), 'utf-8'); + return { headerPath, familyName: fontFamily }; +} + +module.exports = { buildPdfFontHeader }; diff --git a/tests/unit/main/monospace/PdfFontHeader.test.js b/tests/unit/main/monospace/PdfFontHeader.test.js new file mode 100644 index 0000000..d109e71 --- /dev/null +++ b/tests/unit/main/monospace/PdfFontHeader.test.js @@ -0,0 +1,64 @@ +const fs = require('fs'); +const path = require('path'); +const os = require('os'); + +const { buildPdfFontHeader } = require('../../../../src/main/PdfFontHeader'); + +describe('PdfFontHeader', () => { + let tmpDir; + beforeEach(() => { + tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mono-pdfhdr-')); + }); + afterEach(() => { + fs.rmSync(tmpDir, { recursive: true, force: true }); + }); + + test('writes a fontspec header for JetBrains Mono Regular', () => { + const ttf = path.join(tmpDir, 'JetBrainsMono-Regular.ttf'); + fs.writeFileSync(ttf, 'fake'); + const out = buildPdfFontHeader( + { monospaceFont: 'jetbrains-mono', monospaceLigatures: false }, + ttf, + 'JetBrains Mono' + ); + expect(typeof out.headerPath).toBe('string'); + expect(fs.existsSync(out.headerPath)).toBe(true); + const content = fs.readFileSync(out.headerPath, 'utf-8'); + expect(content).toContain('\\setmonofont'); + expect(content).toContain('JetBrainsMono-Regular.ttf'); + expect(out.familyName).toBe('JetBrains Mono'); + }); + + test('writes header with ligatures when enabled', () => { + const ttf = path.join(tmpDir, 'FiraCode-Regular.ttf'); + fs.writeFileSync(ttf, 'fake'); + const out = buildPdfFontHeader( + { monospaceFont: 'fira-code', monospaceLigatures: true }, + ttf, + 'Fira Code' + ); + const content = fs.readFileSync(out.headerPath, 'utf-8'); + expect(content).toContain('Ligatures=TeX'); + }); + + test('header is written under os.tmpdir', () => { + const ttf = path.join(tmpDir, 'JetBrainsMono-Regular.ttf'); + fs.writeFileSync(ttf, 'fake'); + const out = buildPdfFontHeader( + { monospaceFont: 'jetbrains-mono', monospaceLigatures: false }, + ttf, + 'JetBrains Mono' + ); + expect(out.headerPath.startsWith(os.tmpdir())).toBe(true); + }); + + test('throws if ttfPath does not exist', () => { + expect(() => + buildPdfFontHeader( + { monospaceFont: 'jetbrains-mono', monospaceLigatures: false }, + '/nonexistent.ttf', + 'JetBrains Mono' + ) + ).toThrow(/TTF/); + }); +});