import {AlignmentType, Document, HeadingLevel, Packer, Paragraph, TextRun} from "docx"; import PDFDocument from "pdfkit"; import JSZip from "jszip"; import {mainStyle} from "./EpubStyle.js"; import Chapter, {ChapterContentData, CompleteChapterContent} from "./Chapter.js"; import {CompleteBookData} from "./Book.js"; import System from "../System.js"; export interface ExportResult { buffer: Buffer; fileName: string; } export default class Export { static async transformToDOCX(bookData: CompleteBookData): Promise { const bookTitle: string = bookData.title; const filename: string = `${bookTitle}.docx`; const docParagraphs: Paragraph[] = []; docParagraphs.push( new Paragraph({ children: [ new TextRun({text: bookTitle, bold: true, size: 48}), ], alignment: AlignmentType.CENTER, spacing: {after: 400}, }) ); if (bookData.subTitle) { docParagraphs.push( new Paragraph({ children: [ new TextRun({text: bookData.subTitle, italics: true, size: 32}), ], alignment: AlignmentType.CENTER, spacing: {after: 300}, }) ); } if (bookData.summary) { docParagraphs.push( new Paragraph({ children: [ new TextRun({text: bookData.summary, size: 24, italics: true}), ], alignment: AlignmentType.JUSTIFIED, spacing: {after: 400}, }) ); } const chapters: ChapterContentData[] = Chapter.getChaptersOrSheet(bookData.chapters); for (const chapter of chapters) { if (!chapter.content) continue; docParagraphs.push( new Paragraph({ text: chapter.title, heading: HeadingLevel.HEADING_1, pageBreakBefore: true, alignment: AlignmentType.CENTER, spacing: {after: 200}, }) ); const paragraphs: string[] = chapter.content.split(/\r?\n/); for (const paragraph of paragraphs) { if (paragraph.trim() === "") continue; docParagraphs.push( new Paragraph({ children: [new TextRun({text: paragraph, size: 24})], alignment: AlignmentType.JUSTIFIED, spacing: {after: 200}, }) ); } } const doc: Document = new Document({ sections: [{children: docParagraphs}], }); const buffer: Buffer = await Packer.toBuffer(doc) as Buffer; return {buffer, fileName: filename}; } static async transformToPDF(bookData: CompleteBookData): Promise { const bookTitle: string = bookData.title; const filename: string = `${bookTitle}.pdf`; const chunks: Buffer[] = []; const pdfDoc: PDFKit.PDFDocument = new PDFDocument(); pdfDoc.on('data', (chunk: Buffer): void => { chunks.push(chunk); }); pdfDoc.fontSize(20).text(bookTitle, {align: 'center'}); pdfDoc.moveDown(); if (bookData.subTitle && bookData.subTitle.trim() !== '') { pdfDoc.fontSize(16).text(bookData.subTitle, {align: 'center'}); pdfDoc.moveDown(); } if (bookData.summary && bookData.summary.trim() !== '') { pdfDoc.fontSize(12).text(bookData.summary, {align: 'justify'}); pdfDoc.moveDown(); } const chapters: ChapterContentData[] = Chapter.getChaptersOrSheet(bookData.chapters); for (const chapter of chapters) { if (!chapter.content) continue; pdfDoc.addPage(); pdfDoc.fontSize(16).text(chapter.title, {align: 'center'}); pdfDoc.moveDown(); pdfDoc.fontSize(12).text(chapter.content, {align: 'justify'}); } pdfDoc.end(); await new Promise((resolve: () => void, reject: (reason: Error) => void) => { pdfDoc.on('end', resolve); pdfDoc.on('error', reject); }); const pdfBuffer: Buffer = Buffer.concat(chunks); return {buffer: pdfBuffer, fileName: filename}; } static async transformToEpub(bookData: CompleteBookData): Promise { const bookTitle: string = bookData.title; const bookId: string = bookData.bookId; const epub: JSZip = new JSZip(); epub.file('mimetype', 'application/epub+zip', {compression: 'STORE'}); epub.file('META-INF/container.xml', ` `); let contentOpf: string = ` ${bookTitle}${bookData.subTitle ? ' - ' + bookData.subTitle : ''} fr urn:uuid:${bookId} ${bookData.userInfos.firstName} ${bookData.userInfos.lastName} ERitors Scribe `; let spine: string = ``; const hasRegularChapters: boolean = bookData.chapters.some( (chapter: CompleteChapterContent): boolean => chapter.order > 0 ); const chaptersToExport: CompleteChapterContent[] = hasRegularChapters ? bookData.chapters.filter((chapter: CompleteChapterContent): boolean => chapter.order > 0) : bookData.chapters.filter((chapter: CompleteChapterContent): boolean => chapter.order === -1); for (const chapter of chaptersToExport) { if (!chapter.content) continue; const chapterIndex: string = `chapter${chapter.order}`; const htmlContent: string = Chapter.tipTapToHtml(JSON.parse(chapter.content) as JSON); const xhtmlPage: string = ` ${chapter.title} ${htmlContent} `; epub.file(`OEBPS/${chapterIndex}.xhtml`, xhtmlPage); contentOpf += ``; spine += ``; } spine += ``; contentOpf += ` `; contentOpf += spine; contentOpf += ``; epub.file('OEBPS/content.opf', contentOpf); epub.file('OEBPS/styles.css', mainStyle); if (bookData.coverImage) { const imageBuffer: Buffer = Buffer.from(bookData.coverImage, 'base64'); epub.file('OEBPS/cover.jpg', imageBuffer); } const epubBuffer: Buffer = await epub.generateAsync({type: 'nodebuffer'}) as Buffer; return {buffer: epubBuffer, fileName: `${bookTitle}.epub`}; } }