merge files inital commit
This commit is contained in:
57
src/pdf/pdfMergeService.test.ts
Normal file
57
src/pdf/pdfMergeService.test.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { PDFDocument } from 'pdf-lib';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import type { PdfFile } from './pdfTypes';
|
||||
import { mergePdfFilesAtPosition } from './pdfService';
|
||||
|
||||
async function makePdf(name: string, pageCount: number): Promise<PdfFile> {
|
||||
const doc = await PDFDocument.create();
|
||||
for (let i = 0; i < pageCount; i += 1) {
|
||||
doc.addPage([100, 100]);
|
||||
}
|
||||
|
||||
const bytes = await doc.save();
|
||||
const arrayBuffer = new ArrayBuffer(bytes.byteLength);
|
||||
new Uint8Array(arrayBuffer).set(bytes);
|
||||
|
||||
return {
|
||||
id: name,
|
||||
name,
|
||||
doc,
|
||||
pageCount,
|
||||
arrayBuffer,
|
||||
};
|
||||
}
|
||||
|
||||
describe('mergePdfFilesAtPosition', () => {
|
||||
it('merges a queue without a current base PDF', async () => {
|
||||
const first = await makePdf('first.pdf', 1);
|
||||
const second = await makePdf('second.pdf', 2);
|
||||
|
||||
const merged = await mergePdfFilesAtPosition({
|
||||
basePdf: null,
|
||||
incomingPdfs: [first, second],
|
||||
insertAt: 0,
|
||||
name: 'merged.pdf',
|
||||
});
|
||||
|
||||
expect(merged.name).toBe('merged.pdf');
|
||||
expect(merged.pageCount).toBe(3);
|
||||
expect(merged.doc.getPageCount()).toBe(3);
|
||||
});
|
||||
|
||||
it('inserts queued PDFs into a current base PDF at the requested slot', async () => {
|
||||
const base = await makePdf('base.pdf', 3);
|
||||
const incoming = await makePdf('incoming.pdf', 2);
|
||||
|
||||
const merged = await mergePdfFilesAtPosition({
|
||||
basePdf: base,
|
||||
incomingPdfs: [incoming],
|
||||
insertAt: 1,
|
||||
name: 'base_plus_incoming.pdf',
|
||||
});
|
||||
|
||||
expect(merged.name).toBe('base_plus_incoming.pdf');
|
||||
expect(merged.pageCount).toBe(5);
|
||||
expect(merged.doc.getPageCount()).toBe(5);
|
||||
});
|
||||
});
|
||||
@@ -77,6 +77,76 @@ export async function mergePdfFiles(
|
||||
};
|
||||
}
|
||||
|
||||
interface MergePdfFilesAtPositionOptions {
|
||||
basePdf: PdfFile | null;
|
||||
incomingPdfs: PdfFile[];
|
||||
insertAt: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export async function mergePdfFilesAtPosition({
|
||||
basePdf,
|
||||
incomingPdfs,
|
||||
insertAt,
|
||||
name,
|
||||
}: MergePdfFilesAtPositionOptions): Promise<PdfFile> {
|
||||
if (!basePdf && incomingPdfs.length === 0) {
|
||||
throw new Error('At least one PDF is required for merging');
|
||||
}
|
||||
|
||||
const mergedDoc = await PDFDocument.create();
|
||||
|
||||
const addAllPages = async (sourcePdf: PdfFile) => {
|
||||
const sourceDoc =
|
||||
sourcePdf.doc ?? (await PDFDocument.load(sourcePdf.arrayBuffer));
|
||||
const pageCount = sourceDoc.getPageCount();
|
||||
const pages = await mergedDoc.copyPages(
|
||||
sourceDoc,
|
||||
Array.from({ length: pageCount }, (_, i) => i)
|
||||
);
|
||||
pages.forEach((page) => mergedDoc.addPage(page));
|
||||
};
|
||||
|
||||
if (!basePdf) {
|
||||
for (const incomingPdf of incomingPdfs) {
|
||||
await addAllPages(incomingPdf);
|
||||
}
|
||||
} else {
|
||||
const baseDoc =
|
||||
basePdf.doc ?? (await PDFDocument.load(basePdf.arrayBuffer));
|
||||
const basePageCount = baseDoc.getPageCount();
|
||||
const clampedInsertAt = Math.min(Math.max(insertAt, 0), basePageCount);
|
||||
|
||||
const basePages = await mergedDoc.copyPages(
|
||||
baseDoc,
|
||||
Array.from({ length: basePageCount }, (_, i) => i)
|
||||
);
|
||||
|
||||
for (let i = 0; i < clampedInsertAt; i += 1) {
|
||||
mergedDoc.addPage(basePages[i]);
|
||||
}
|
||||
|
||||
for (const incomingPdf of incomingPdfs) {
|
||||
await addAllPages(incomingPdf);
|
||||
}
|
||||
|
||||
for (let i = clampedInsertAt; i < basePages.length; i += 1) {
|
||||
mergedDoc.addPage(basePages[i]);
|
||||
}
|
||||
}
|
||||
|
||||
const bytes = await mergedDoc.save();
|
||||
const buffer = pdfBytesToArrayBuffer(bytes);
|
||||
|
||||
return {
|
||||
id: createId(),
|
||||
name,
|
||||
arrayBuffer: buffer,
|
||||
pageCount: mergedDoc.getPageCount(),
|
||||
doc: mergedDoc,
|
||||
};
|
||||
}
|
||||
|
||||
export async function splitIntoSinglePages(
|
||||
pdf: PdfFile
|
||||
): Promise<SplitResult[]> {
|
||||
|
||||
Reference in New Issue
Block a user