md2pdf2 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +99 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +88 -0
- package/dist/cli.js.map +1 -0
- package/dist/converter.d.ts +13 -0
- package/dist/converter.d.ts.map +1 -0
- package/dist/converter.js +25 -0
- package/dist/converter.js.map +1 -0
- package/dist/pdf-generator.d.ts +5 -0
- package/dist/pdf-generator.d.ts.map +1 -0
- package/dist/pdf-generator.js +36 -0
- package/dist/pdf-generator.js.map +1 -0
- package/dist/template-engine.d.ts +14 -0
- package/dist/template-engine.d.ts.map +1 -0
- package/dist/template-engine.js +97 -0
- package/dist/template-engine.js.map +1 -0
- package/dist/types.d.ts +32 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +16 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +7 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +39 -0
- package/dist/utils.js.map +1 -0
- package/package.json +52 -0
- package/templates/default.hbs +24 -0
- package/templates/parts/footer.hbs +4 -0
- package/templates/parts/header.hbs +9 -0
- package/templates/styles.css +95 -0
package/README.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# MD2PDF
|
|
2
|
+
|
|
3
|
+
A CLI tool that converts Markdown to PDF using customizable templates — think Astro, but for PDFs.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Convert Markdown to PDF with a single command
|
|
8
|
+
- Customizable templates with layout, styling, and components
|
|
9
|
+
- Supports multiple output formats (letter, a4, etc.)
|
|
10
|
+
- Template inheritance and partials
|
|
11
|
+
- Frontmatter support for metadata
|
|
12
|
+
- Built-in template engine (Handlebars-like syntax)
|
|
13
|
+
- CLI flags for quick customization
|
|
14
|
+
- Watch mode for development
|
|
15
|
+
|
|
16
|
+
## Quick Start
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# Install globally (once published)
|
|
20
|
+
npm install -g md2pdf
|
|
21
|
+
|
|
22
|
+
# Or use npx
|
|
23
|
+
npx md2pdf convert input.md -o output.pdf
|
|
24
|
+
|
|
25
|
+
# With a custom template
|
|
26
|
+
md2pdf convert input.md --template my-template.hbs -o output.pdf
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Project Structure
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
my-doc/
|
|
33
|
+
├── content/
|
|
34
|
+
│ └── my-doc.md
|
|
35
|
+
├── templates/
|
|
36
|
+
│ ├── default.hbs
|
|
37
|
+
│ ├── parts/
|
|
38
|
+
│ │ └── header.hbs
|
|
39
|
+
│ └── styles.css
|
|
40
|
+
├── md2pdf.config.js
|
|
41
|
+
└── package.json
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Templates
|
|
45
|
+
|
|
46
|
+
Templates use handlebars-like syntax for placeholders and partials:
|
|
47
|
+
|
|
48
|
+
```handlebars
|
|
49
|
+
<!DOCTYPE html>
|
|
50
|
+
<html>
|
|
51
|
+
<head>
|
|
52
|
+
<meta charset="UTF-8">
|
|
53
|
+
<style>{{{styles}}}</style>
|
|
54
|
+
</head>
|
|
55
|
+
<body>
|
|
56
|
+
{{> header}}
|
|
57
|
+
<main>
|
|
58
|
+
{{{content}}}
|
|
59
|
+
</main>
|
|
60
|
+
</body>
|
|
61
|
+
</html>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Configuration
|
|
65
|
+
|
|
66
|
+
`md2pdf.config.js`:
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
export default {
|
|
70
|
+
template: './templates/default.hbs',
|
|
71
|
+
style: './templates/styles.css',
|
|
72
|
+
pdfOptions: {
|
|
73
|
+
format: 'A4',
|
|
74
|
+
margin: { top: '1cm', right: '1cm', bottom: '1cm', left: '1cm' }
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Development
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# Clone and setup
|
|
83
|
+
git clone https://github.com/areai51/md2pdf.git
|
|
84
|
+
cd md2pdf
|
|
85
|
+
npm install
|
|
86
|
+
|
|
87
|
+
# Build
|
|
88
|
+
npm run build
|
|
89
|
+
|
|
90
|
+
# Test
|
|
91
|
+
npm test
|
|
92
|
+
|
|
93
|
+
# Link for global use
|
|
94
|
+
npm link
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## License
|
|
98
|
+
|
|
99
|
+
MIT
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { readFileAsync, writeFileAsync, fileExists, resolvePath, getFileName } from './utils.js';
|
|
4
|
+
import { Converter } from './converter.js';
|
|
5
|
+
import { TemplateEngine } from './template-engine.js';
|
|
6
|
+
import { PDFGenerator } from './pdf-generator.js';
|
|
7
|
+
import { DEFAULT_CONFIG } from './types.js';
|
|
8
|
+
const program = new Command();
|
|
9
|
+
program
|
|
10
|
+
.name('md2pdf')
|
|
11
|
+
.description('Convert Markdown to PDF using customizable templates')
|
|
12
|
+
.version('0.1.0');
|
|
13
|
+
program
|
|
14
|
+
.command('convert')
|
|
15
|
+
.description('Convert a Markdown file to PDF')
|
|
16
|
+
.argument('<input>', 'Input Markdown file')
|
|
17
|
+
.option('-o, --output <file>', 'Output PDF file')
|
|
18
|
+
.option('-t, --template <file>', 'Custom template file (Handlebars)')
|
|
19
|
+
.option('-s, --style <file>', 'Custom CSS file')
|
|
20
|
+
.option('-c, --config <file>', 'Configuration file (md2pdf.config.js)')
|
|
21
|
+
.option('--no-pdf', 'Only generate HTML, do not create PDF')
|
|
22
|
+
.action(async (input, options) => {
|
|
23
|
+
try {
|
|
24
|
+
// Load configuration
|
|
25
|
+
const config = await loadConfig(options);
|
|
26
|
+
// Override with CLI options
|
|
27
|
+
if (options.template)
|
|
28
|
+
config.template = options.template;
|
|
29
|
+
if (options.style)
|
|
30
|
+
config.style = options.style;
|
|
31
|
+
// Validate input file
|
|
32
|
+
if (!(await fileExists(input))) {
|
|
33
|
+
console.error(`Error: Input file not found: ${input}`);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
// Read input markdown
|
|
37
|
+
const markdown = await readFileAsync(input);
|
|
38
|
+
// Convert to HTML
|
|
39
|
+
const converter = new Converter();
|
|
40
|
+
const { html, frontMatter } = converter.convert(markdown);
|
|
41
|
+
// Load styles
|
|
42
|
+
let styles = '';
|
|
43
|
+
if (config.style && await fileExists(config.style)) {
|
|
44
|
+
styles = await readFileAsync(config.style);
|
|
45
|
+
}
|
|
46
|
+
// Load and render template
|
|
47
|
+
const engine = new TemplateEngine(config);
|
|
48
|
+
if (config.template && await fileExists(config.template)) {
|
|
49
|
+
await engine.loadTemplate(config.template);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
// Use built-in default template
|
|
53
|
+
engine.loadDefaultTemplate();
|
|
54
|
+
}
|
|
55
|
+
const renderedHtml = engine.render(html, frontMatter, styles);
|
|
56
|
+
// Determine output path
|
|
57
|
+
const outputPath = options.output || (await getOutputPath(input));
|
|
58
|
+
const htmlPath = outputPath.replace(/\.pdf$/, '.html');
|
|
59
|
+
// Save HTML for debugging
|
|
60
|
+
await writeFileAsync(htmlPath, renderedHtml);
|
|
61
|
+
console.log(`✓ HTML generated: ${htmlPath}`);
|
|
62
|
+
// Generate PDF unless disabled
|
|
63
|
+
if (!options.pdf) {
|
|
64
|
+
const generator = new PDFGenerator();
|
|
65
|
+
await generator.generate(renderedHtml, outputPath, config.pdfOptions);
|
|
66
|
+
console.log(`✓ PDF generated: ${outputPath}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
console.error('Error:', error instanceof Error ? error.message : error);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
async function loadConfig(options) {
|
|
75
|
+
const config = { ...DEFAULT_CONFIG };
|
|
76
|
+
if (options.config && await fileExists(options.config)) {
|
|
77
|
+
const configModule = await import(resolvePath(process.cwd(), options.config));
|
|
78
|
+
const userConfig = configModule.default || configModule;
|
|
79
|
+
Object.assign(config, userConfig);
|
|
80
|
+
}
|
|
81
|
+
return config;
|
|
82
|
+
}
|
|
83
|
+
function getOutputPath(inputPath) {
|
|
84
|
+
const baseName = getFileName(inputPath);
|
|
85
|
+
return `${baseName}.pdf`;
|
|
86
|
+
}
|
|
87
|
+
program.parse();
|
|
88
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { FrontMatter } from './types.js';
|
|
2
|
+
export declare class Converter {
|
|
3
|
+
constructor();
|
|
4
|
+
convert(markdown: string): {
|
|
5
|
+
html: string;
|
|
6
|
+
frontMatter: FrontMatter;
|
|
7
|
+
};
|
|
8
|
+
convertFile(filePath: string): Promise<{
|
|
9
|
+
html: string;
|
|
10
|
+
frontMatter: FrontMatter;
|
|
11
|
+
}>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=converter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"converter.d.ts","sourceRoot":"","sources":["../src/converter.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,qBAAa,SAAS;;IASpB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,WAAW,CAAA;KAAE;IAS/D,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,WAAW,CAAA;KAAE,CAAC;CAKzF"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import fm from 'front-matter';
|
|
2
|
+
import { marked } from 'marked';
|
|
3
|
+
export class Converter {
|
|
4
|
+
constructor() {
|
|
5
|
+
// Configure marked options
|
|
6
|
+
marked.setOptions({
|
|
7
|
+
gfm: true,
|
|
8
|
+
breaks: true
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
convert(markdown) {
|
|
12
|
+
const { attributes, body } = fm(markdown);
|
|
13
|
+
const html = marked(body, { async: false });
|
|
14
|
+
return {
|
|
15
|
+
html,
|
|
16
|
+
frontMatter: attributes
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
async convertFile(filePath) {
|
|
20
|
+
// Will be implemented with file reading
|
|
21
|
+
const content = await (await import('./utils.js')).readFileAsync(filePath);
|
|
22
|
+
return this.convert(content);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=converter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"converter.js","sourceRoot":"","sources":["../src/converter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,cAAc,CAAC;AAC9B,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGhC,MAAM,OAAO,SAAS;IACpB;QACE,2BAA2B;QAC3B,MAAM,CAAC,UAAU,CAAC;YAChB,GAAG,EAAE,IAAI;YACT,MAAM,EAAE,IAAI;SACb,CAAC,CAAC;IACL,CAAC;IAED,OAAO,CAAC,QAAgB;QACtB,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,CAAC;QAC1C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAW,CAAC;QACtD,OAAO;YACL,IAAI;YACJ,WAAW,EAAE,UAAyB;SACvC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,QAAgB;QAChC,wCAAwC;QACxC,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAC3E,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdf-generator.d.ts","sourceRoot":"","sources":["../src/pdf-generator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,qBAAa,YAAY;IACjB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;CAmCzF"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import puppeteer from 'puppeteer';
|
|
2
|
+
export class PDFGenerator {
|
|
3
|
+
async generate(html, outputPath, pdfOptions) {
|
|
4
|
+
const browser = await puppeteer.launch({
|
|
5
|
+
headless: true,
|
|
6
|
+
args: ['--no-sandbox', '--disable-setuid-sandbox']
|
|
7
|
+
});
|
|
8
|
+
try {
|
|
9
|
+
const page = await browser.newPage();
|
|
10
|
+
// Set HTML content
|
|
11
|
+
await page.setContent(html, { waitUntil: ['networkidle0', 'domcontentloaded'] });
|
|
12
|
+
// Set PDF options
|
|
13
|
+
const options = {
|
|
14
|
+
format: pdfOptions?.format || 'A4',
|
|
15
|
+
printBackground: pdfOptions?.printBackground ?? true,
|
|
16
|
+
margin: {
|
|
17
|
+
top: pdfOptions?.margin?.top || '1cm',
|
|
18
|
+
right: pdfOptions?.margin?.right || '1cm',
|
|
19
|
+
bottom: pdfOptions?.margin?.bottom || '1cm',
|
|
20
|
+
left: pdfOptions?.margin?.left || '1cm'
|
|
21
|
+
},
|
|
22
|
+
...(pdfOptions?.landscape && { landscape: pdfOptions.landscape }),
|
|
23
|
+
...(pdfOptions?.width && { width: pdfOptions.width }),
|
|
24
|
+
...(pdfOptions?.height && { height: pdfOptions.height })
|
|
25
|
+
};
|
|
26
|
+
await page.pdf({
|
|
27
|
+
path: outputPath,
|
|
28
|
+
...options
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
finally {
|
|
32
|
+
await browser.close();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=pdf-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdf-generator.js","sourceRoot":"","sources":["../src/pdf-generator.ts"],"names":[],"mappings":"AAAA,OAAO,SAAgD,MAAM,WAAW,CAAC;AAGzE,MAAM,OAAO,YAAY;IACvB,KAAK,CAAC,QAAQ,CAAC,IAAY,EAAE,UAAkB,EAAE,UAAuB;QACtE,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC;YACrC,QAAQ,EAAE,IAAI;YACd,IAAI,EAAE,CAAC,cAAc,EAAE,0BAA0B,CAAC;SACnD,CAAC,CAAC;QAEH,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;YAErC,mBAAmB;YACnB,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,CAAC,cAAc,EAAE,kBAAkB,CAAC,EAAE,CAAC,CAAC;YAEjF,kBAAkB;YAClB,MAAM,OAAO,GAAe;gBAC1B,MAAM,EAAE,UAAU,EAAE,MAAM,IAAI,IAAI;gBAClC,eAAe,EAAE,UAAU,EAAE,eAAe,IAAI,IAAI;gBACpD,MAAM,EAAE;oBACN,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,IAAI,KAAK;oBACrC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,IAAI,KAAK;oBACzC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,IAAI,KAAK;oBAC3C,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,IAAI,KAAK;iBACxC;gBACD,GAAG,CAAC,UAAU,EAAE,SAAS,IAAI,EAAE,SAAS,EAAE,UAAU,CAAC,SAAS,EAAE,CAAC;gBACjE,GAAG,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE,KAAK,EAAE,UAAU,CAAC,KAAK,EAAE,CAAC;gBACrD,GAAG,CAAC,UAAU,EAAE,MAAM,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC;aACzD,CAAC;YAEF,MAAM,IAAI,CAAC,GAAG,CAAC;gBACb,IAAI,EAAE,UAAU;gBAChB,GAAG,OAAO;aACY,CAAC,CAAC;QAC5B,CAAC;gBAAS,CAAC;YACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;QACxB,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { MD2PDFConfig } from './types.js';
|
|
2
|
+
export declare class TemplateEngine {
|
|
3
|
+
private template;
|
|
4
|
+
private partials;
|
|
5
|
+
constructor(config: MD2PDFConfig);
|
|
6
|
+
private registerHelpers;
|
|
7
|
+
private loadPartialsFromConfig;
|
|
8
|
+
loadTemplate(templatePath: string): Promise<void>;
|
|
9
|
+
loadDefaultTemplate(): void;
|
|
10
|
+
private compileTemplate;
|
|
11
|
+
registerPartial(name: string, content: string): void;
|
|
12
|
+
render(content: string, frontMatter: Record<string, any>, styles: string): string;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=template-engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-engine.d.ts","sourceRoot":"","sources":["../src/template-engine.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAA8B;IAC9C,OAAO,CAAC,QAAQ,CAAkD;gBAEtD,MAAM,EAAE,YAAY;IAUhC,OAAO,CAAC,eAAe;YAMT,sBAAsB;IAW9B,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKvD,mBAAmB,IAAI,IAAI;IAiD3B,OAAO,CAAC,eAAe;IAIvB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAIpD,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM;CAQlF"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import Handlebars from 'handlebars';
|
|
2
|
+
import { readFileAsync } from './utils.js';
|
|
3
|
+
export class TemplateEngine {
|
|
4
|
+
template;
|
|
5
|
+
partials = {};
|
|
6
|
+
constructor(config) {
|
|
7
|
+
// Register built-in helpers
|
|
8
|
+
this.registerHelpers();
|
|
9
|
+
// Load partials from config if provided
|
|
10
|
+
if (config.partials) {
|
|
11
|
+
this.loadPartialsFromConfig(config.partials);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
registerHelpers() {
|
|
15
|
+
Handlebars.registerHelper('hasPartial', (name) => {
|
|
16
|
+
return !!this.partials[name];
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
async loadPartialsFromConfig(partialsConfig) {
|
|
20
|
+
for (const [name, path] of Object.entries(partialsConfig)) {
|
|
21
|
+
try {
|
|
22
|
+
const content = await readFileAsync(path);
|
|
23
|
+
this.partials[name] = Handlebars.compile(content);
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
console.warn(`Warning: Could not load partial "${name}" from ${path}: ${error}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async loadTemplate(templatePath) {
|
|
31
|
+
const templateContent = await readFileAsync(templatePath);
|
|
32
|
+
this.template = this.compileTemplate(templateContent);
|
|
33
|
+
}
|
|
34
|
+
loadDefaultTemplate() {
|
|
35
|
+
const defaultTemplate = `<!DOCTYPE html>
|
|
36
|
+
<html>
|
|
37
|
+
<head>
|
|
38
|
+
<meta charset="UTF-8">
|
|
39
|
+
<style>
|
|
40
|
+
body {
|
|
41
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
42
|
+
line-height: 1.6;
|
|
43
|
+
max-width: 800px;
|
|
44
|
+
margin: 0 auto;
|
|
45
|
+
padding: 2rem;
|
|
46
|
+
}
|
|
47
|
+
h1, h2, h3, h4, h5, h6 {
|
|
48
|
+
margin-top: 2rem;
|
|
49
|
+
margin-bottom: 1rem;
|
|
50
|
+
line-height: 1.25;
|
|
51
|
+
}
|
|
52
|
+
p { margin-bottom: 1rem; }
|
|
53
|
+
code {
|
|
54
|
+
background: #f4f4f4;
|
|
55
|
+
padding: 0.2em 0.4em;
|
|
56
|
+
border-radius: 3px;
|
|
57
|
+
font-family: monospace;
|
|
58
|
+
}
|
|
59
|
+
pre {
|
|
60
|
+
background: #f4f4f4;
|
|
61
|
+
padding: 1rem;
|
|
62
|
+
border-radius: 5px;
|
|
63
|
+
overflow-x: auto;
|
|
64
|
+
}
|
|
65
|
+
blockquote {
|
|
66
|
+
border-left: 4px solid #ddd;
|
|
67
|
+
padding-left: 1rem;
|
|
68
|
+
margin-left: 0;
|
|
69
|
+
color: #666;
|
|
70
|
+
}
|
|
71
|
+
@media print { body { padding: 1cm; } }
|
|
72
|
+
</style>
|
|
73
|
+
</head>
|
|
74
|
+
<body>
|
|
75
|
+
{{#if (hasPartial "header")}}{{> header}}{{/if}}
|
|
76
|
+
<main>{{{content}}}</main>
|
|
77
|
+
{{#if (hasPartial "footer")}}{{> footer}}{{/if}}
|
|
78
|
+
</body>
|
|
79
|
+
</html>`;
|
|
80
|
+
this.template = this.compileTemplate(defaultTemplate);
|
|
81
|
+
}
|
|
82
|
+
compileTemplate(content) {
|
|
83
|
+
return Handlebars.compile(content, { strict: true });
|
|
84
|
+
}
|
|
85
|
+
registerPartial(name, content) {
|
|
86
|
+
this.partials[name] = Handlebars.compile(content);
|
|
87
|
+
}
|
|
88
|
+
render(content, frontMatter, styles) {
|
|
89
|
+
return this.template({
|
|
90
|
+
content,
|
|
91
|
+
frontMatter,
|
|
92
|
+
styles,
|
|
93
|
+
partials: this.partials
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=template-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-engine.js","sourceRoot":"","sources":["../src/template-engine.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAG3C,MAAM,OAAO,cAAc;IACjB,QAAQ,CAA8B;IACtC,QAAQ,GAA+C,EAAE,CAAC;IAElE,YAAY,MAAoB;QAC9B,4BAA4B;QAC5B,IAAI,CAAC,eAAe,EAAE,CAAC;QAEvB,wCAAwC;QACxC,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YACpB,IAAI,CAAC,sBAAsB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAEO,eAAe;QACrB,UAAU,CAAC,cAAc,CAAC,YAAY,EAAE,CAAC,IAAY,EAAE,EAAE;YACvD,OAAO,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC/B,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,sBAAsB,CAAC,cAAsC;QACzE,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC;YAC1D,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,IAAI,CAAC,CAAC;gBAC1C,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YACpD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,IAAI,CAAC,oCAAoC,IAAI,UAAU,IAAI,KAAK,KAAK,EAAE,CAAC,CAAC;YACnF,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,YAAoB;QACrC,MAAM,eAAe,GAAG,MAAM,aAAa,CAAC,YAAY,CAAC,CAAC;QAC1D,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,eAAe,CAAC,CAAC;IACxD,CAAC;IAED,mBAAmB;QACjB,MAAM,eAAe,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA4CpB,CAAC;QACL,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,eAAe,CAAC,CAAC;IACxD,CAAC;IAEO,eAAe,CAAC,OAAe;QACrC,OAAO,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC;IAED,eAAe,CAAC,IAAY,EAAE,OAAe;QAC3C,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACpD,CAAC;IAED,MAAM,CAAC,OAAe,EAAE,WAAgC,EAAE,MAAc;QACtE,OAAO,IAAI,CAAC,QAAQ,CAAC;YACnB,OAAO;YACP,WAAW;YACX,MAAM;YACN,QAAQ,EAAE,IAAI,CAAC,QAAQ;SACxB,CAAC,CAAC;IACL,CAAC;CACF"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface MD2PDFConfig {
|
|
2
|
+
template?: string;
|
|
3
|
+
style?: string;
|
|
4
|
+
pdfOptions?: PDFOptions;
|
|
5
|
+
partials?: Record<string, string>;
|
|
6
|
+
}
|
|
7
|
+
export interface PDFOptions {
|
|
8
|
+
format?: string;
|
|
9
|
+
width?: string;
|
|
10
|
+
height?: string;
|
|
11
|
+
margin?: {
|
|
12
|
+
top?: string;
|
|
13
|
+
right?: string;
|
|
14
|
+
bottom?: string;
|
|
15
|
+
left?: string;
|
|
16
|
+
};
|
|
17
|
+
printBackground?: boolean;
|
|
18
|
+
landscape?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export declare const DEFAULT_CONFIG: MD2PDFConfig;
|
|
21
|
+
export interface FrontMatter {
|
|
22
|
+
title?: string;
|
|
23
|
+
author?: string;
|
|
24
|
+
date?: string;
|
|
25
|
+
[key: string]: any;
|
|
26
|
+
}
|
|
27
|
+
export interface ConversionResult {
|
|
28
|
+
success: boolean;
|
|
29
|
+
outputPath?: string;
|
|
30
|
+
error?: string;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE;QACP,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;IACF,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAGD,eAAO,MAAM,cAAc,EAAE,YAa5B,CAAC;AAGF,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAGD,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Default configuration
|
|
2
|
+
export const DEFAULT_CONFIG = {
|
|
3
|
+
template: './templates/default.hbs',
|
|
4
|
+
style: './templates/styles.css',
|
|
5
|
+
pdfOptions: {
|
|
6
|
+
format: 'A4',
|
|
7
|
+
margin: {
|
|
8
|
+
top: '1cm',
|
|
9
|
+
right: '1cm',
|
|
10
|
+
bottom: '1cm',
|
|
11
|
+
left: '1cm'
|
|
12
|
+
},
|
|
13
|
+
printBackground: true
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAsBA,wBAAwB;AACxB,MAAM,CAAC,MAAM,cAAc,GAAiB;IAC1C,QAAQ,EAAE,yBAAyB;IACnC,KAAK,EAAE,wBAAwB;IAC/B,UAAU,EAAE;QACV,MAAM,EAAE,IAAI;QACZ,MAAM,EAAE;YACN,GAAG,EAAE,KAAK;YACV,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,KAAK;YACb,IAAI,EAAE,KAAK;SACZ;QACD,eAAe,EAAE,IAAI;KACtB;CACF,CAAC"}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function readFileAsync(path: string): Promise<string>;
|
|
2
|
+
export declare function writeFileAsync(path: string, content: string): Promise<void>;
|
|
3
|
+
export declare function fileExists(path: string): Promise<boolean>;
|
|
4
|
+
export declare function resolvePath(from: string, to: string): string;
|
|
5
|
+
export declare function getFileName(path: string): string;
|
|
6
|
+
export declare function slugify(text: string): string;
|
|
7
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAOA,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEjE;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIjF;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO/D;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIhD;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAO5C"}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { readFile, stat, writeFile, mkdir } from 'fs/promises';
|
|
2
|
+
import { dirname, resolve, extname } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
5
|
+
const __dirname = dirname(__filename);
|
|
6
|
+
export async function readFileAsync(path) {
|
|
7
|
+
return await readFile(path, 'utf-8');
|
|
8
|
+
}
|
|
9
|
+
export async function writeFileAsync(path, content) {
|
|
10
|
+
const dir = dirname(path);
|
|
11
|
+
await mkdir(dir, { recursive: true });
|
|
12
|
+
await writeFile(path, content, 'utf-8');
|
|
13
|
+
}
|
|
14
|
+
export async function fileExists(path) {
|
|
15
|
+
try {
|
|
16
|
+
await stat(path);
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function resolvePath(from, to) {
|
|
24
|
+
return resolve(dirname(from), to);
|
|
25
|
+
}
|
|
26
|
+
export function getFileName(path) {
|
|
27
|
+
const name = resolve(path).split('/').pop() || 'output';
|
|
28
|
+
const ext = extname(name);
|
|
29
|
+
return ext ? name.slice(0, -ext.length) : name;
|
|
30
|
+
}
|
|
31
|
+
export function slugify(text) {
|
|
32
|
+
return text
|
|
33
|
+
.toLowerCase()
|
|
34
|
+
.trim()
|
|
35
|
+
.replace(/[^\w\s-]/g, '')
|
|
36
|
+
.replace(/[\s_-]+/g, '-')
|
|
37
|
+
.replace(/^-+|-+$/g, '');
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAQ,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAY;IAC9C,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAY,EAAE,OAAe;IAChE,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,MAAM,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAY;IAC3C,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,EAAU;IAClD,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,QAAQ,CAAC;IACxD,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,OAAO,IAAI;SACR,WAAW,EAAE;SACb,IAAI,EAAE;SACN,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;SACxB,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC;SACxB,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;AAC7B,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "md2pdf2",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Convert Markdown to PDF using customizable templates",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/cli.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"md2pdf": "dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsc --watch",
|
|
13
|
+
"start": "node dist/cli.js",
|
|
14
|
+
"test": "node dist/cli.js convert examples/demo.md -o demo.pdf",
|
|
15
|
+
"lint": "eslint src --ext .ts",
|
|
16
|
+
"prepublishOnly": "npm run build"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"markdown",
|
|
20
|
+
"pdf",
|
|
21
|
+
"converter",
|
|
22
|
+
"cli",
|
|
23
|
+
"templates"
|
|
24
|
+
],
|
|
25
|
+
"author": "Vinci Rufus",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "git+https://github.com/areai51/md2pdf.git"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"commander": "^12.1.0",
|
|
33
|
+
"front-matter": "^4.0.2",
|
|
34
|
+
"handlebars": "^4.7.8",
|
|
35
|
+
"marked": "^11.1.1",
|
|
36
|
+
"puppeteer": "^23.0.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^20.11.0",
|
|
40
|
+
"@typescript-eslint/eslint-plugin": "^6.19.0",
|
|
41
|
+
"@typescript-eslint/parser": "^6.19.0",
|
|
42
|
+
"eslint": "^8.56.0",
|
|
43
|
+
"typescript": "^5.3.3"
|
|
44
|
+
},
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=18.0.0"
|
|
47
|
+
},
|
|
48
|
+
"files": [
|
|
49
|
+
"dist",
|
|
50
|
+
"templates"
|
|
51
|
+
]
|
|
52
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>{{#if frontMatter.title}}{{frontMatter.title}}{{else}}Document{{/if}}</title>
|
|
7
|
+
<style>
|
|
8
|
+
{{{styles}}}
|
|
9
|
+
</style>
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
{{#if (hasPartial "header")}}
|
|
13
|
+
{{> header}}
|
|
14
|
+
{{/if}}
|
|
15
|
+
|
|
16
|
+
<main>
|
|
17
|
+
{{{content}}}
|
|
18
|
+
</main>
|
|
19
|
+
|
|
20
|
+
{{#if (hasPartial "footer")}}
|
|
21
|
+
{{> footer}}
|
|
22
|
+
{{/if}}
|
|
23
|
+
</body>
|
|
24
|
+
</html>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<header style="border-bottom: 1px solid #ddd; padding-bottom: 1rem; margin-bottom: 2rem;">
|
|
2
|
+
<h1 style="margin: 0;">{{#if frontMatter.title}}{{frontMatter.title}}{{else}}Document{{/if}}</h1>
|
|
3
|
+
{{#if frontMatter.author}}
|
|
4
|
+
<p style="color: #666; margin: 0.5rem 0 0 0;">By {{frontMatter.author}}</p>
|
|
5
|
+
{{/if}}
|
|
6
|
+
{{#if frontMatter.date}}
|
|
7
|
+
<p style="color: #888; font-size: 0.9rem; margin: 0.25rem 0 0 0;">{{frontMatter.date}}</p>
|
|
8
|
+
{{/if}}
|
|
9
|
+
</header>
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/* Default styles for md2pdf */
|
|
2
|
+
body {
|
|
3
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
4
|
+
line-height: 1.7;
|
|
5
|
+
font-size: 12pt;
|
|
6
|
+
color: #333;
|
|
7
|
+
max-width: 800px;
|
|
8
|
+
margin: 0 auto;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
h1, h2, h3, h4, h5, h6 {
|
|
12
|
+
font-weight: 600;
|
|
13
|
+
line-height: 1.25;
|
|
14
|
+
margin-top: 2rem;
|
|
15
|
+
margin-bottom: 1rem;
|
|
16
|
+
color: #111;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
h1 { font-size: 2.25rem; border-bottom: 1px solid #eee; padding-bottom: 0.5rem; }
|
|
20
|
+
h2 { font-size: 1.75rem; }
|
|
21
|
+
h3 { font-size: 1.5rem; }
|
|
22
|
+
|
|
23
|
+
p {
|
|
24
|
+
margin-bottom: 1rem;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
a {
|
|
28
|
+
color: #0066cc;
|
|
29
|
+
text-decoration: none;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
a:hover {
|
|
33
|
+
text-decoration: underline;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
code {
|
|
37
|
+
background: #f4f4f4;
|
|
38
|
+
padding: 0.2em 0.4em;
|
|
39
|
+
border-radius: 3px;
|
|
40
|
+
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
|
|
41
|
+
font-size: 0.9em;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
pre {
|
|
45
|
+
background: #f4f4f4;
|
|
46
|
+
padding: 1rem;
|
|
47
|
+
border-radius: 5px;
|
|
48
|
+
overflow-x: auto;
|
|
49
|
+
font-size: 0.85rem;
|
|
50
|
+
line-height: 1.5;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
pre code {
|
|
54
|
+
background: none;
|
|
55
|
+
padding: 0;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
blockquote {
|
|
59
|
+
border-left: 4px solid #ddd;
|
|
60
|
+
padding-left: 1rem;
|
|
61
|
+
margin-left: 0;
|
|
62
|
+
color: #555;
|
|
63
|
+
font-style: italic;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
table {
|
|
67
|
+
width: 100%;
|
|
68
|
+
border-collapse: collapse;
|
|
69
|
+
margin-bottom: 1.5rem;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
th, td {
|
|
73
|
+
border: 1px solid #ddd;
|
|
74
|
+
padding: 0.5rem;
|
|
75
|
+
text-align: left;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
th {
|
|
79
|
+
background: #f9f9f9;
|
|
80
|
+
font-weight: 600;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
img {
|
|
84
|
+
max-width: 100%;
|
|
85
|
+
height: auto;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* Print-specific adjustments */
|
|
89
|
+
@media print {
|
|
90
|
+
body {
|
|
91
|
+
font-size: 10pt;
|
|
92
|
+
}
|
|
93
|
+
h1 { font-size: 1.8rem; }
|
|
94
|
+
h2 { font-size: 1.4rem; }
|
|
95
|
+
}
|