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, }), ); } }