mirror of
https://github.com/vbalien/voca.git
synced 2025-12-06 11:26:21 +09:00
initial commit
This commit is contained in:
BIN
NanumGothic.ttf
Normal file
BIN
NanumGothic.ttf
Normal file
Binary file not shown.
26
deno.jsonc
Normal file
26
deno.jsonc
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
145
main.ts
Normal file
145
main.ts
Normal file
@@ -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 =
|
||||||
|
/<p class="word">(?<id>\d+?)\. (?<word>.+?)<\/p>.*?<span class="af_answer">(?<answer>.+?)<\/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,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user