Skip to content

Commit efa1030

Browse files
Bill LeoutsakosBill Leoutsakos
authored andcommitted
feat(files): add Markdown PDF download
1 parent e0292fc commit efa1030

9 files changed

Lines changed: 745 additions & 10 deletions

File tree

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/**
2+
* @vitest-environment node
3+
*/
4+
import { PDFDocument } from 'pdf-lib'
5+
import sharp from 'sharp'
6+
import { describe, expect, it } from 'vitest'
7+
import { renderMarkdownPdf } from '@/app/api/files/export/[id]/markdown-pdf'
8+
9+
describe('Markdown PDF rendering', () => {
10+
it('creates a valid multi-page PDF with GFM and an embedded image', async () => {
11+
const image = await sharp({
12+
create: {
13+
width: 120,
14+
height: 60,
15+
channels: 3,
16+
background: '#4f46e5',
17+
},
18+
})
19+
.png()
20+
.toBuffer()
21+
const repeatedParagraphs = Array.from(
22+
{ length: 70 },
23+
(_, index) => `Paragraph ${index + 1} with **bold**, _italic_, and \`inline code\`.`
24+
).join('\n\n')
25+
const markdown = `# Export title
26+
27+
> A useful blockquote with a [link](https://sim.ai).
28+
29+
Smart quotes “work”, Greek Ω stays readable, and unsupported emoji 🚀 falls back safely.
30+
31+
- First item
32+
- Second item
33+
34+
| Name | Value |
35+
| --- | ---: |
36+
| Alpha | 1 |
37+
| Beta | 2 |
38+
39+
\`\`\`ts
40+
const exported = true
41+
\`\`\`
42+
43+
![Embedded image](/api/files/view/image-1)
44+
45+
${repeatedParagraphs}`
46+
47+
const buffer = await renderMarkdownPdf({
48+
markdown,
49+
title: 'Export title',
50+
images: new Map([['image-1', image]]),
51+
})
52+
53+
expect(buffer.subarray(0, 4).toString()).toBe('%PDF')
54+
expect(buffer.length).toBeGreaterThan(1_000)
55+
56+
const document = await PDFDocument.load(buffer)
57+
expect(document.getTitle()).toBe('Export title')
58+
expect(document.getPageCount()).toBeGreaterThan(1)
59+
})
60+
})

0 commit comments

Comments
 (0)