diff --git a/asset/official-single-page-footer.css b/asset/official-single-page-footer.css index dce1754..25a1930 100644 --- a/asset/official-single-page-footer.css +++ b/asset/official-single-page-footer.css @@ -8,15 +8,9 @@ footer { padding-top: 30px !important; text-align: center; font-size: 20px; - letter-spacing: 2px; line-height: 1.5em; font-weight: bold; height: auto !important; padding-bottom: 30px !important; border-bottom: 1px solid #333 !important; } - -footer::after { - content: "O O O O O   O\AO O O O O O O"; - white-space: pre; -} \ No newline at end of file diff --git a/lib/index.d.ts b/lib/index.d.ts index 3dcb5f5..f5603aa 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -1,4 +1,5 @@ /** @format */ +/// import { Command, flags } from "@oclif/command"; declare class Zigzi extends Command { static description: string; @@ -19,6 +20,8 @@ declare class Zigzi extends Command { marp(): Promise; toHtml(): Promise; toPdf(): Promise; + toPdfOfficial(): Promise; + mergePdfBuffers(target1: Buffer, target2: Buffer): Promise; setVSCodeSetting(): Promise; } export = Zigzi; diff --git a/lib/index.js b/lib/index.js index ae19ae5..851c163 100644 --- a/lib/index.js +++ b/lib/index.js @@ -6,8 +6,12 @@ const child_process = require("child_process"); const marked = require("marked"); const puppeteer = require("puppeteer"); const shell = require("shelljs"); +const pdfParse = require("pdf-parse"); +const opentype = require("opentype.js"); +const fm = require("front-matter"); const command_1 = require("@oclif/command"); const fs_1 = require("fs"); +const pdf_lib_1 = require("pdf-lib"); const convertCss = { zb_ppt: __dirname + "/../asset/ppt.css", zb_ppt_large: __dirname + "/../asset/ppt-large.css", @@ -36,7 +40,12 @@ class Zigzi extends command_1.Command { else { await this.toHtml(); if (output === "pdf") { - await this.toPdf(); + if (flags.template === "zb_official") { + await this.toPdfOfficial(); + } + else { + await this.toPdf(); + } } } } @@ -44,7 +53,9 @@ class Zigzi extends command_1.Command { async marp() { const { args, flags } = this.parse(Zigzi); let md = fs.readFileSync(this.file, "utf8"); - const cssFile = (md.indexOf("theme: ppt-large") > 0) ? convertCss["zb_ppt_large"] : convertCss["zb_ppt"]; + const cssFile = md.indexOf("theme: ppt-large") > 0 + ? convertCss["zb_ppt_large"] + : convertCss["zb_ppt"]; const pdfFileName = `${this.file.replace(".md", ".pdf")}`; await child_process.execSync(`npx @marp-team/marp-cli@latest --allow-local-files ${this.file} -o ${pdfFileName} --theme ${cssFile}`); } @@ -52,19 +63,25 @@ class Zigzi extends command_1.Command { const { flags } = this.parse(Zigzi); const cssType = convertCss[this.converType]; const htmlFileName = `${this.file.replace(".md", ".html")}`; - let md = fs.readFileSync(this.file, "utf8"); + const md = fs.readFileSync(this.file, "utf8"); + let css = fs.readFileSync(cssType, "utf8"); const pageBreak = //g; - md = md.replace(pageBreak, `
`); - md = md.replace(RegExp(//g), ""); + const modifiedMd = md + .replace(pageBreak, `
`) + .replace(RegExp(//g), "") + .replace(/^---$.*^---$/ms, ""); let markedHtml; if (flags.template === "zb_official") { - markedHtml = `
${marked(md)}
`; + const { attributes: { 주소, 전화번호 } = { 주소: "", 전화번호: "" }, } = fm(md); + markedHtml = `
${marked(modifiedMd)}
`; + css += `header::after{ + content: "${주소}\\ATel) ${전화번호}" + }`; } else { - markedHtml = marked(md); + markedHtml = marked(modifiedMd); } - const css = fs.readFileSync(cssType, "utf8"); const html = ` @@ -84,9 +101,6 @@ class Zigzi extends command_1.Command { const { flags } = this.parse(Zigzi); const htmlFileName = `${this.file.replace(".md", ".html")}`; const pdfFileName = `${this.file.replace(".md", ".pdf")}`; - const headerFooterTemplate = (flags.template === "zb_doc") ? { - displayHeaderFooter: true - } : {}; const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(`file://${path.resolve(htmlFileName)}`, { @@ -99,7 +113,7 @@ class Zigzi extends command_1.Command { printBackground: true, headerTemplate: `
`, footerTemplate: ``, - displayHeaderFooter: (flags.template === "zb_doc"), + displayHeaderFooter: flags.template === "zb_doc", margin: { top: 50, bottom: 50, @@ -110,13 +124,111 @@ class Zigzi extends command_1.Command { await browser.close(); await fs.unlinkSync(`${htmlFileName}`); } + async toPdfOfficial() { + const md = fs.readFileSync(this.file, "utf8"); + const { attributes: { footer = [] }, } = fm(md); + const htmlFileName = `${this.file.replace(".md", ".html")}`; + const pdfFileName = `${this.file.replace(".md", ".pdf")}`; + const browser = await puppeteer.launch(); + const page = await browser.newPage(); + await page.goto(`file://${path.resolve(htmlFileName)}`, { + waitUntil: "networkidle2", + timeout: 60000 * 2, + }); + await page.emulateMediaType("screen"); + const font = opentype.loadSync(path.resolve(__dirname, "../asset/SpoqaHanSansBold.ttf")); + const footerSvg = footer + .map((str) => { + const p = font.getPath(str, 0, 0, 21); + const { x1, x2, y1, y2 } = p.getBoundingBox(); + const height = y2 - y1; + const width = x2 - x1; + const svgPath = p.toSVG(2); + return `${svgPath}`; + }) + .join(""); + const headerTemplate = "
"; + const footerTemplate = ` + + + `; + const baseOpt = { + format: "a4", + path: `${path.resolve(pdfFileName)}`, + headerTemplate, + footerTemplate, + displayHeaderFooter: true, + margin: { + top: 50, + bottom: 50, + left: 58, + right: 58, + }, + timeout: 1000 * 60 * 10, + }; + await page.pdf(baseOpt); + const dataBuffer = fs.readFileSync(path.resolve(pdfFileName)); + const pdfInfo = await pdfParse(dataBuffer); + const numPages = pdfInfo.numpages; + if (numPages === 1) { + await page.addStyleTag({ + path: path.resolve(__dirname, "../asset/official-single-page-footer.css"), + }); + await page.addStyleTag({ + content: ` + footer::after { + content: "${footer.join("\\A")}"; + white-space: pre; + }`, + }); + await page.pdf(Object.assign(Object.assign({}, baseOpt), { displayHeaderFooter: false })); + } + else { + const restBuffer = await page.pdf(Object.assign(Object.assign({}, baseOpt), { displayHeaderFooter: false, pageRanges: `-${numPages - 1}` })); + const lastPdfName = `${pdfFileName}-last.pdf`; + const lastBuffer = await page.pdf(Object.assign(Object.assign({}, baseOpt), { displayHeaderFooter: true, pageRanges: `${numPages}`, path: lastPdfName })); + await fs.unlinkSync(lastPdfName); + const mergedPdfBuffer = await this.mergePdfBuffers(restBuffer, lastBuffer); + await fs.writeFileSync(`${path.resolve(pdfFileName)}`, mergedPdfBuffer); + } + await browser.close(); + await fs.unlinkSync(`${htmlFileName}`); + } + async mergePdfBuffers(target1, target2) { + const pdf1 = await pdf_lib_1.PDFDocument.load(target1); + const pdf2 = await pdf_lib_1.PDFDocument.load(target2); + const mergedPdf = await pdf_lib_1.PDFDocument.create(); + const copiedPagesA = await mergedPdf.copyPages(pdf1, pdf1.getPageIndices()); + copiedPagesA.forEach((page) => mergedPdf.addPage(page)); + const copiedPagesB = await mergedPdf.copyPages(pdf2, pdf2.getPageIndices()); + copiedPagesB.forEach((page) => mergedPdf.addPage(page)); + return await mergedPdf.save(); + } async setVSCodeSetting() { const location = shell.exec("pwd"); - const setting_location = process.platform === "win32" ? "%APPDATA%\/Code\/User\/" : "~/Library/Application Support/Code/User/"; + const setting_location = process.platform === "win32" + ? "%APPDATA%/Code/User/" + : "~/Library/Application Support/Code/User/"; JSON.stringify; shell.cd(setting_location); shell.exec("pwd"); - const settingsBuffer = await (0, fs_1.readFileSync)("settings.json", { encoding: "utf8" }); + const settingsBuffer = await (0, fs_1.readFileSync)("settings.json", { + encoding: "utf8", + }); let settings = settingsBuffer; const css = ` "https://zigbang.github.io/zigzi/asset/ppt.css", @@ -151,7 +263,7 @@ Zigzi.flags = { }), vscode: command_1.flags.boolean({ description: "vscode markdown template setting", - }) + }), }; Zigzi.args = [ { diff --git a/package.json b/package.json index 501d975..1ae15c5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zigzi", - "version": "1.0.6", + "version": "1.0.11", "author": "ZIGBANG Co., Ltd.", "keywords": [ "zigbang", diff --git a/sample/official.md b/sample/official.md index ed5add1..cd16221 100644 --- a/sample/official.md +++ b/sample/official.md @@ -30,13 +30,10 @@ _-- 아래 --_ 붙임 : - 붙임 내용 - - # test 1 테스트 테스트 테스트 테스트 테스트 테스트 테스트 테스트 테스트 테스트 테스트 테스트 테스트 테스트 - # test 2 diff --git a/sample/official.pdf b/sample/official.pdf index 82456a6..5775f76 100644 Binary files a/sample/official.pdf and b/sample/official.pdf differ diff --git a/src/index.ts b/src/index.ts index 6af9e8e..d37a99f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -202,15 +202,15 @@ class Zigzi extends Command { const footerTemplate = `