commit dcfd1233852dc5e7660af9987c8cd30f32cd6493 Author: Jisu Kim Date: Fri Feb 25 19:54:51 2022 +0900 initial commit diff --git a/NanumGothic.ttf b/NanumGothic.ttf new file mode 100644 index 0000000..75d010a Binary files /dev/null and b/NanumGothic.ttf differ diff --git a/deno.jsonc b/deno.jsonc new file mode 100644 index 0000000..3177366 --- /dev/null +++ b/deno.jsonc @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "allowJs": true, + "lib": ["deno.window"], + "strict": true + }, + "lint": { + "files": { + "include": ["src/"] + }, + "rules": { + "tags": ["recommended"] + } + }, + "fmt": { + "files": { + "include": ["src/"] + }, + "options": { + "useTabs": true, + "lineWidth": 80, + "indentWidth": 2, + "proseWrap": "preserve" + } + } +} diff --git a/main.ts b/main.ts new file mode 100644 index 0000000..839e2f3 --- /dev/null +++ b/main.ts @@ -0,0 +1,145 @@ +import { + PDFDocument, + PDFPage, + rgb, +} from "https://cdn.skypack.dev/pdf-lib@^1.11.1?dts"; +import fontkit from "https://cdn.skypack.dev/@pdf-lib/fontkit@^1.0.0?dts"; + +type Result = { + id: number; + word: string; + answer: string; +}; + +async function generateVoca(level: number, day: number) { + const re = + /

(?\d+?)\. (?.+?)<\/p>.*?(?.+?)<\/span>/gis; + const res = await fetch( + `https://www.hackers.co.kr/?c=s_toeic/new_voca_toeic_testpaper/toeic_study/new_paper&mode=new_view&level=${level}&level_type=&lang_text=2&question=1000&day3=${day}&day4=${day}&day_auto=N&index=1`, + ); + + const body = (await res.text()); + const matches = [...body.matchAll(re)].map((m) => ({ + ...m.groups as unknown as Result, + id: Number.parseInt(m.groups?.id as string), + })); + + const pdfDoc = await PDFDocument.create(); + + const fontBytes = await Deno.readFile("./NanumGothic.ttf"); + pdfDoc.registerFontkit(fontkit); + const customFont = await pdfDoc.embedFont(fontBytes); + + const fontSize = 12; + const margin = 20; + const perColumn = 25; + const perPage = 50; + + let page!: PDFPage; + let col = -1; + let yCursor = 1; + + // 문제 생성 + for (let i = 0; i < matches.length; ++i) { + const data = matches[i]; + if (i % perPage === 0) { + page = pdfDoc.addPage(); + yCursor = 1; + col = -1; + } + if (i % perColumn === 0) { + col++; + yCursor = 1; + } + page.drawText(`${data.id}. ${data.word}`, { + x: margin + (page.getWidth() / 2 * col), + y: page.getHeight() - (fontSize + margin) * (yCursor), + size: fontSize, + font: customFont, + color: rgb(0, 0, 0), + }); + page.drawText("_________________", { + x: margin + (page.getWidth() / 2 * col) + 140, + y: page.getHeight() - (fontSize + margin) * (yCursor) - 3, + size: fontSize, + font: customFont, + color: rgb(0, 0, 0), + }); + yCursor++; + } + + // 답 생성 + for (let i = 0; i < matches.length; ++i) { + const data = matches[i]; + if (i % perPage === 0) { + page = pdfDoc.addPage(); + yCursor = 1; + col = -1; + } + if (i % perColumn === 0) { + col++; + yCursor = 1; + } + page.drawText(`${data.id}. ${data.word}`, { + x: margin + (page.getWidth() / 2 * col), + y: page.getHeight() - (fontSize + margin) * (yCursor), + size: fontSize, + font: customFont, + color: rgb(0, 0, 0), + }); + page.drawText("_________________", { + x: margin + (page.getWidth() / 2 * col) + 140, + y: page.getHeight() - (fontSize + margin) * (yCursor) - 3, + size: fontSize, + font: customFont, + color: rgb(0, 0, 0), + }); + page.drawText(data.answer, { + x: margin + (page.getWidth() / 2 * col) + 140, + y: page.getHeight() - (fontSize + margin) * (yCursor), + size: fontSize, + font: customFont, + color: rgb(1, 0, 0), + }); + yCursor++; + } + + const pdfBytes = await pdfDoc.save(); + return pdfBytes; +} + +const port = 8055; +const server = Deno.listen({ port }); +console.log( + `HTTP webserver running. Access it at: http://localhost:${port}/`, +); + +for await (const conn of server) { + serveHttp(conn); +} + +async function serveHttp(conn: Deno.Conn) { + const httpConn = Deno.serveHttp(conn); + for await (const requestEvent of httpConn) { + const url = new URL(requestEvent.request.url); + + if (!url.searchParams.has("level") || !url.searchParams.has("day")) { + requestEvent.respondWith( + new Response("Not Found", { + status: 404, + }), + ); + return; + } + + const level = Number.parseInt(url.searchParams.get("level")!) ?? 7; + const day = Number.parseInt(url.searchParams.get("day")!) ?? 1; + const data = await generateVoca(level, day); + + requestEvent.respondWith( + new Response(data, { + status: 200, + }), + ); + } +}