Mundarija (23)
- Bu darsda
- 1. Nega bu kerak?
- 2. PDF ichida shrift
- 2.1 14 standart shrift va WinAnsi
- 2.2 Shriftni embed qilish
- 3. PDFKit: qo'lda chizish
- 4. pdfmake: deklarativ hujjat
- 4.1 Kanon topilmasi: sukut siyosati yo'q
- 5. Headless brauzer: HTML → PDF
- 5.1 page.pdf()
- 5.2 Foydalanuvchi matnini ekranlash
- 6. Qaysi birini tanlash
- 7. Fon rejimi
- 8. Hujumchi nigohi
- 9. Ko'p uchraydigan xatolar
- 10. Mashqlar
- 1-mashq (oson): bo'shliqlarni to'ldiring
- 2-mashq (o'rta): sahifalarga bo'lish
- 3-mashq (qiyin): pdfmake hujjati
- 11. Loyiha qadami: kanon tajribasi, loyihaga hali kirmaydi
- 12. Real ishda
- Xulosa
- Manbalar
PDF generatsiya: PDFKit, pdfmake va brauzerdan PDF
Qisqacha: Sertifikat, chek yoki hisobotni PDF qilishning uch yo'li bor. PDFKit — har matn va chiziqni koordinata bilan o'zingiz chizasiz; pdfmake — hujjatni JSON (jadval, uslub) bilan tasvirlaysiz; headless brauzer (
page.pdf()) — tayyor HTML/CSS shablonni bosmaga chiqarasiz. O'zbek tili uchun asosiy tuzoq — shrift. Standart Helveticaʻva kirill harflarini bilmaydi. To'liq shriftni (masalan DejaVu Sans) embed qilish kerak. pdfmake server rejimida tashqi URL va lokal fayllarni sukutda o'qiydi — siyosat bilan yoping.
Bu darsda
- PDF ichida shrift qanday ishlashini va nega
Oʻyo'qolishi mumkinligini tushunasiz. - PDFKit bilan sahifalanadigan hisobotni chizasiz, sahifa raqamlarini qo'yasiz.
- pdfmake'ning deklarativ hujjatini va xavfsizlik siyosatlarini bilasiz.
- HTML shablonni headless Chromium bilan PDF qilasiz va foydalanuvchi matnini ekranlaysiz.
- Uch usulni hajm, tezlik va resurs bo'yicha solishtirib, birini tanlaysiz.
Oldin bilishingiz kerak: Template engine'lar va server-side HTML, Rasmlarni qayta ishlash: sharp, SSRF va tashqi URL'lar bilan ishlash, Stream nima: to'rt turi va rejimlari.
Oldingi ikki darsda foydalanuvchi yuborgan rasm va videoni qayta ishladik. Endi teskari yo'nalish: server o'zi hujjat yasaydi.
1. Nega bu kerak?
Jasur aka oy oxirida "oktabr bo'yicha vazifalar hisoboti"ni direktorga yuborishi kerak. Bu PDF bo'lishi kerak: chop etiladi, har qurilmada bir xil ko'rinadi, o'zgartirib bo'lmaydi (oson). Boshqa misollar:
- kurs tugatgani haqidagi sertifikat;
- to'lov cheki (38-qism
bozorloyihasida); - shartnoma yoki ariza — imzo uchun.
HTML sahifani "Chop etish → PDF" qilish foydalanuvchiga qoladi va har brauzerda boshqacha. Server yasagan PDF esa bir xil, avtomatik va xatga ilova qilinadi.
2. PDF ichida shrift
2.1 14 standart shrift va WinAnsi
PDF fayl matnni harflar sifatida emas, shrift gliflari (belgi rasmlari) sifatida saqlaydi. PDF standarti 14 ta "standart" shriftni (Helvetica, Times, Courier…) belgilaydi — ular faylga qo'shilmaydi, ko'ruvchi dastur o'zida bor deb hisoblanadi. Bu shriftlar WinAnsi (Windows-1252) kodlashida — 256 belgi.
Kanon PDFKit'ning standart Helvetica'si bilan o'zbekcha matn yozdi:
hajm 1528 bayt; Helvetica embed qilinmaydi (14 standart shrift). Kodlash WinAnsi — ʻ, ў, қ, ғ, ҳ uchun glif YO'Q → PDF da noto'g'ri belgi/bo'shliq (vizual tekshiruv: brauzer PDF ko'ruvchisida — sinalmadi, faqat kodlash bo'yicha)Qaysi belgilar WinAnsi'ga sig'maydi — tekshiramiz:
// WinAnsi: ASCII, 0xA0–0xFF va 0x80–0x9F o'rnidagi belgilar
const CP1252 = "€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ";
function winAnsidaYoq(matn) {
const yoq = new Set();
for (const ch of matn) {
const k = ch.codePointAt(0);
const bor = (k < 0x80) || (k >= 0xa0 && k <= 0xff) ||
CP1252.includes(ch);
const kod = k.toString(16).toUpperCase().padStart(4, "0");
if (!bor) yoq.add(`${ch} U+${kod}`);
}
return yoq.size ? [...yoq].join(", ") : "(hammasi bor)";
}
console.log(winAnsidaYoq("Oʻzbekiston, gʻalaba"));
console.log(winAnsidaYoq("O'zbekiston, g'alaba, o’qish"));
console.log(winAnsidaYoq("ўқиш, ғоя"));Konsolda:
ʻ U+02BB
(hammasi bor)
ў U+045E, қ U+049B, и U+0438, ш U+0448, ғ U+0493, о U+043E, я U+044Fʻ(U+02BB) — o'zbek lotin alifbosidagi rasmiy "o'" belgisi — WinAnsi'da yo'q.- Oddiy apostrof
'va tipografik’(U+2019) — bor. Shuning uchun ba'zan "ishlaganday" ko'rinadi. - Kirill — umuman yo'q.
Xulosa: o'zbek matni uchun standart shriftga tayanmang.
Tekshirib ko'ring: Dasturchi sinab ko'rdi:
O'zbekiston(oddiy apostrof bilan) Helvetica'da to'g'ri chiqdi. "Muammo yo'q" degan xulosa to'g'rimi?
Javob
Yo'q. Oddiy apostrof WinAnsi'da bor, shuning uchun sinov o'tdi. Lekin foydalanuvchilar ʻ (U+02BB) yoki kirill bilan yozadi — ular chiqmaydi. Sinov ma'lumoti haqiqiy ma'lumotdagi barcha belgilarni qamrashi kerak.
2.2 Shriftni embed qilish
Yechim — o'zbek harflari bor shriftni (TTF/OTF) PDF ichiga qo'shish. Kanon DejaVu Sans ni tanladi — erkin litsenziya bilan tarqatiladi (Arial — yo'q: uning litsenziyasi faylni tarqatishga ruxsat bermaydi). Avval glif bormi — tekshirildi (fontkit kutubxonasi bilan):
ʻ U+02BB: DejaVu bor
’ U+2019: DejaVu bor
ў U+045E: DejaVu bor
қ U+049B: DejaVu bor
ғ U+0493: DejaVu bor
ҳ U+04B3: DejaVu bor
₽ U+20BD: DejaVu borEmbed qilish ikki narsani beradi:
- Subset (qism) — faqat ishlatilgan gliflar qo'shiladi, butun shrift (700 KB) emas.
- ToUnicode jadvali — PDF dan matnni nusxalash va qidirish to'g'ri ishlaydi.
Diqqat: Shrift faylini loyihaga qo'shishdan oldin litsenziyasini o'qing. DejaVu, Noto, Inter, PT Sans — embed va tarqatishga ruxsat beradi. Tizim papkasidagi shriftlar (
C:\Windows\Fonts) — har doim ham emas.
3. PDFKit: qo'lda chizish
npm i pdfkit@0.20.2PDFKit hujjati — oqim (stream): doc.pipe(res) bilan to'g'ridan javobga yoki buferga. Kanon hisobotining asosiy qismi (telefon uchun bo'lingan):
import PDFDocument from "pdfkit";
const doc = new PDFDocument({
size: "A4", margin: 50, bufferPages: true,
info: { Title: "Vazifalar hisoboti", Author: "vazifalar-api" },
});
doc.registerFont("asosiy", "fonts/DejaVuSans.ttf")
.registerFont("qalin", "fonts/DejaVuSans-Bold.ttf");
doc.font("qalin").fontSize(18).text("Vazifalar hisoboti — oktabr");
doc.moveDown().font("asosiy").fontSize(11);
let y = doc.y;
for (const [n, matn, holat] of qatorlar) {
if (y > 760) { doc.addPage(); y = 50; }
doc.text(n, 50, y, { width: 30 })
.text(matn, 90, y, { width: 330 })
.text(holat, 430, y, { width: 100 });
y += 18;
}
const s = doc.bufferedPageRange();
for (let i = s.start; i < s.start + s.count; i++) {
doc.switchToPage(i);
doc.fontSize(9).text(`${i + 1} / ${s.count}`, 50, 800,
{ align: "center", width: 495, lineBreak: false });
}
doc.end();Asosiy g'oyalar:
- Koordinatalar — punktlarda (1 pt = 1/72 dyuym); A4 = 595 × 842 pt.
y > 760— sahifa pastiga yetdik, yangi sahifa. - Sahifa raqami "n / N" — N oxirida ma'lum bo'ladi.
bufferPages: truesahifalarni xotirada ushlaydi, oxirida har biriga qaytib (switchToPage) raqam yoziladi. lineBreak: false— raqam pastki chegaradan oshib, yangi sahifa ochib yubormasin.
Kanon natijasi (40 qator, DejaVu subset):
hajm 19.9 KB, sahifalar 4, FontFile2 (embed) 2 ta, ToUnicode 2 ta (matnni nusxalash/qidirish uchun)
vaqt: mediana 40.0 ms, p95 53.3 ms- 4 sahifa, 19.9 KB — ikki shrift (oddiy va qalin) subset bilan.
FontFile2— embed qilingan TrueType shrift;ToUnicode— nusxalash uchun jadval.- 40 ms — 20 marta yasashning medianasi.
4. pdfmake: deklarativ hujjat
npm i pdfmake@0.3.12pdfmake'da chizmaysiz — tasvirlaysiz. Hujjat — oddiy JS obyekti (document definition):
import pdfmake from "pdfmake";
pdfmake.setFonts({ DejaVu: {
normal: "fonts/DejaVuSans.ttf", bold: "fonts/DejaVuSans-Bold.ttf",
italics: "fonts/DejaVuSans.ttf",
bolditalics: "fonts/DejaVuSans-Bold.ttf",
} });
pdfmake.setUrlAccessPolicy(() => false);
pdfmake.setLocalAccessPolicy((yol) => yol.startsWith("fonts/"));
const buf = await pdfmake.createPdf({
content: [
{ text: "Vazifalar hisoboti", style: "s" },
{ table: { headerRows: 1, widths: [30, "*", 90], body: [
["#", "Vazifa", "Holat"], ...qatorlar,
] } },
],
defaultStyle: { font: "DejaVu", fontSize: 11 },
styles: { s: { fontSize: 18, bold: true } },
}).getBuffer();Jadval sahifaga sig'masa — o'zi keyingi sahifaga o'tadi, headerRows: 1 sarlavha qatorini har sahifada takrorlaydi. widths: [30, "*", 90] — "*" qolgan joyni oladi. PDFKit'da bularning hammasini qo'lda yozardingiz.
4.1 Kanon topilmasi: sukut siyosati yo'q
hajm 22.4 KB; sukut ogohlantirishlari:
[warn] No URL access policy defined. Consider using setUrlAccessPolicy() to restrict external resource downloads.
[warn] No local access policy defined. Consider using setLocalAccessPolicy() to restrict local file system access.
vaqt (policy bilan): mediana 43.2 ms
urlAccessPolicy false — tashqi rasm URL (SSRF sinovi): Access to local file denied by resource access policy: http://169.254.169.254/latest/meta-data/x.pngpdfmake 0.3 server rejimida hujjatdagi image: "http://…" yoki fayl yo'lini o'zi yuklaydi. Ogohlantirishlar: "URL kirish siyosati belgilanmagan" va "lokal kirish siyosati belgilanmagan". Agar hujjat tarkibiga foydalanuvchi ma'lumoti tushsa (masalan, "avatar URL"), hujumchi:
http://169.254.169.254/…— bulut metama'lumot xizmati (SSRF);/etc/passwdyoki.env— server fayli (LFI).
Siyosat bilan tashqi URL rad etildi. Xabarga e'tibor bering: URL uchun ham "Access to local file denied" deyiladi — kutubxona xabari noaniq, lekin rad etildi.
Diqqat: pdfmake'ni serverda ishlatsangiz, birinchi qator —
setUrlAccessPolicy(() => false)va faqat shrift papkasiga ruxsat beruvchisetLocalAccessPolicy. Rasm kerak bo'lsa — o'zingiz omborda o'qib, bufer (data URL) qilib bering.
5. Headless brauzer: HTML → PDF
5.1 page.pdf()
Eng moslashuvchan yo'l: dizayner HTML/CSS shablon yasaydi, brauzer uni chop etadi. Kanon Playwright (playwright-core 1.62.0 + Chromium 151) bilan:
import { chromium } from "playwright-core";
const brauzer = await chromium.launch();
const sahifa = await brauzer.newPage();
await sahifa.setContent(htmlShablon);
const pdf = await sahifa.pdf({
format: "A4", printBackground: true,
displayHeaderFooter: true,
footerTemplate: `<div style="font-size:8pt;width:100%;
text-align:center"><span class="pageNumber"></span> /
<span class="totalPages"></span></div>`,
margin: { top: "15mm", bottom: "15mm" },
});
await brauzer.close();pageNumber, totalPages — Chromium o'zi to'ldiradigan maxsus klasslar. Shrift — brauzer biladigan har qanday shrift (@font-face bilan ham).
brauzer ishga tushishi 99 ms; birinchi PDF 237 ms; keyingilari mediana 12 ms; hajm 33.2 KB- Brauzerni ishga tushirish — 99 ms, birinchi PDF — 237 ms.
- Keyingilari — 12 ms: iliq brauzerda eng tez usul.
- Lekin brauzer — alohida jarayon, 100+ MB xotira. Kanon xulosasi: "iliq brauzerda eng tez, sovuq start bilan eng sekin".
Shuning uchun brauzer har so'rovda emas, bir marta ishga tushiriladi va qayta ishlatiladi (bir nechta page). Puppeteer — xuddi shu g'oya; kanon uni sinamadi (Playwright yetarli).
5.2 Foydalanuvchi matnini ekranlash
Shablonga vazifa matni tushadi. Hujumchi vazifani shunday nomladi:
const ALMASH = { "&": "&", "<": "<", ">": ">",
'"': """, "'": "'" };
const html = (s) => String(s).replace(/[&<>"']/g, (c) => ALMASH[c]);
const matn = `<img src="http://169.254.169.254/x"` +
` onerror="alert(1)">`;
console.log(`<td>${html(matn)}</td>`);
console.log(html("O'qish & yozish"));Konsolda:
<td><img src="http://169.254.169.254/x" onerror="alert(1)"></td>
O'qish & yozishEkranlashsiz headless brauzer <img> ni haqiqiy teg deb ko'rib, server ichidan 169.254.169.254 ga so'rov yuboradi (SSRF), skriptni bajaradi. PDF yasovchi brauzer — server ichidagi brauzer: u ichki tarmoqni ko'radi. Himoya: har foydalanuvchi qiymatini ekranlash (shablon dvigateli buni sukutda qiladi — Template engine'lar), qo'shimcha ravishda sahifada tarmoq so'rovlarini bloklash (Playwright page.route) — kanonda sinalmagan.
6. Qaysi birini tanlash
| PDFKit | pdfmake | page.pdf() |
|
|---|---|---|---|
| Hajm (kanon, 40 qator) | 19.9 KB | 22.4 KB | 33.2 KB |
| Vaqt | 40 ms | 43 ms | 12 ms (iliq), 237 ms (birinchi) |
| Joylashuv | qo'lda koordinata | JSON, avtomatik | HTML/CSS |
| Jadval, sahifalash | qo'lda | tayyor | CSS (@page, break-inside) |
| Resurs | kichik | kichik | brauzer jarayoni, 100+ MB |
| Kim tahrirlaydi | dasturchi | dasturchi | dizayner ham |
flowchart TD
A{Hujjat qanday?} -->|chek, yorliq, sertifikat: aniq joylashuv| P[PDFKit]
A -->|jadvalli hisobot, ko'p sahifa| M[pdfmake]
A -->|murakkab dizayn, dizayner shabloni| C[Headless brauzer]
C --> W[Alohida worker, bitta iliq brauzer]Vaqtlar — bitta mashinada, bitta o'lchov. Asosiy farq tezlikda emas, qulaylik va resursda.
7. Fon rejimi
100 sahifalik hisobot yoki 500 ta sertifikat — soniyalar. So'rov ichida yasamang:
- Foydalanuvchi "Hisobot yasash" ni bosadi → API navbatga vazifa qo'yadi,
202qaytaradi. - Worker PDF yasaydi va omborga yozadi (
hisobotlar/<egasi>/<uuid>.pdf). - Foydalanuvchiga bildirishnoma yoki xat: muddatli havola bilan (Presigned URL'lar).
Kichik chek (1 sahifa, 40 ms) — so'rov ichida ham bo'ladi: doc.pipe(res) bilan oqim, faylni hech qayerga yozmasdan.
8. Hujumchi nigohi
- HTML injection → SSRF/XSS headless brauzerda. Himoya: ekranlash, tarmoqni cheklash.
- pdfmake URL/fayl o'qish. Himoya:
setUrlAccessPolicy,setLocalAccessPolicy. - Resurs tugashi. 10 000 qatorli hisobotni hamma bir vaqtda so'raydi. Himoya: navbat, limit, kesh (bir xil hisobot — bir marta).
- Begona hisobot.
GET /hisobot?egasi=8. Himoya: egasi — sessiyadan (28-qism), so'rov parametridan emas. - PDF ichidagi metama'lumot.
Author,Creatormaydonlariga ichki ma'lumot tushmasin (server nomi, foydalanuvchi emaili).
9. Ko'p uchraydigan xatolar
| Xato | Sabab | Davosi |
|---|---|---|
Oʻ o'rniga bo'shliq yoki g'alati belgi |
standart shrift (WinAnsi) | to'liq shriftni embed |
| Matnni nusxalab bo'lmaydi | ToUnicode yo'q | embed (PDFKit/pdfmake o'zi qo'shadi) |
| Sahifa raqami "1 / ?" | N oldindan noma'lum | bufferPages + oxirida yozish |
| Matn sahifadan chiqib ketdi | y tekshiruvi yo'q |
addPage yoki pdfmake |
| pdfmake ogohlantirishi "No URL access policy" | siyosat yo'q | ikkala siyosatni o'rnating |
| Har PDF 300 ms | har safar yangi brauzer | bitta brauzer, qayta ishlatish |
| Server xotirasi to'ldi | ko'p brauzer jarayoni | worker, parallellik limiti |
10. Mashqlar
Mashqlar kurs/mashqlar/29/09-pdf/ da.
1-mashq (oson): bo'shliqlarni to'ldiring
- PDF standartidagi faylga qo'shilmaydigan shriftlar soni —
- Ularning kodlashi —
- Faqat ishlatilgan gliflarni qo'shish —
- PDFKit'da "n / N" uchun sahifalarni ushlab turish parametri —
- pdfmake'da tashqi URL'larni yopish —
Yechim
- 14 — Helvetica, Times, Courier oilalari va Symbol, ZapfDingbats.
- WinAnsi — Windows-1252, 256 belgi;
ʻyo'q. - subset — PDF kichik bo'ladi.
- bufferPages — keyin
switchToPage. - setUrlAccessPolicy — va lokal fayllar uchun
setLocalAccessPolicy.
2-mashq (o'rta): sahifalarga bo'lish
Hisobot birinchi sahifasiga sarlavha sababli 30 qator, keyingilariga 40 qator sig'adi. sahifalar(qatorlar, birinchi = 30, keyingi = 40) funksiyasini yozing: har sahifa uchun "n / N: a–b" satrini qaytarsin (qator raqamlari 1 dan). 40, 100 va 0 qator bilan sinang.
Yechim
function sahifalar(qatorlar, birinchi = 30, keyingi = 40) {
const natija = [];
let i = 0;
while (i < qatorlar) {
const sigim = natija.length === 0 ? birinchi : keyingi;
const oxir = Math.min(qatorlar, i + sigim);
natija.push(`${i + 1}–${oxir}`);
i = oxir;
}
return natija.map((q, n) => `${n + 1} / ${natija.length}: ${q}`);
}
console.log(sahifalar(40));
console.log(sahifalar(100));
console.log(sahifalar(0));Konsolda:
[ '1 / 2: 1–30', '2 / 2: 31–40' ]
[ '1 / 3: 1–30', '2 / 3: 31–70', '3 / 3: 71–100' ]
[]N (jami sahifa) faqat hammasi taqsimlangandan keyin ma'lum — shuning uchun avval ro'yxat, keyin map bilan raqam. PDFKit'dagi bufferPages aynan shu g'oya. 0 qator — bo'sh hisobot: haqiqiy ilovada "ma'lumot yo'q" sahifasini chiqaring.
3-mashq (qiyin): pdfmake hujjati
hujjat(royxat) funksiyasini yozing: { matn, bajarildi } massividan pdfmake hujjat obyektini qaytarsin — sarlavha (style: "s") va jadval: headerRows: 1, widths: [30, "*", 90], birinchi qator ["#", "Vazifa", "Holat"], keyin ["1", matn, "bajarildi" | "kutilmoqda"]. Jadval tanasini JSON qilib va qatorlar sonini chiqaring.
Yechim
const vazifalar = [
{ matn: "Non olish", bajarildi: true },
{ matn: "Oʻqish: 1-bob", bajarildi: false },
];
function hujjat(royxat) {
return {
content: [
{ text: "Vazifalar hisoboti", style: "s" },
{
table: {
headerRows: 1,
widths: [30, "*", 90],
body: [
["#", "Vazifa", "Holat"],
...royxat.map((v, i) => [String(i + 1), v.matn,
v.bajarildi ? "bajarildi" : "kutilmoqda"]),
],
},
},
],
defaultStyle: { font: "DejaVu", fontSize: 11 },
};
}
const d = hujjat(vazifalar);
console.log(JSON.stringify(d.content[1].table.body));
console.log(d.content[1].table.body.length, "qator");Konsolda:
[["#","Vazifa","Holat"],["1","Non olish","bajarildi"],["2","Oʻqish: 1-bob","kutilmoqda"]]
3 qatorHujjat — oddiy ma'lumot. Uni test bilan tekshirish oson, PDF baytlarini solishtirish shart emas. pdfmake matnni HTML deb talqin qilmaydi: teg yozilsa, u oddiy matn bo'lib chiqadi. Lekin image maydoniga foydalanuvchi URL'ini hech qachon bermang. String(i + 1) — pdfmake jadval katakchasida satr kutadi.
11. Loyiha qadami: kanon tajribasi, loyihaga hali kirmaydi
Bu darsda vazifalar-api ga kod qo'shilmaydi. Kanon uch usulni faqat tajribada solishtirdi (glif tekshiruvi, Helvetica, PDFKit + DejaVu, pdfmake siyosatlari, Playwright). Sabab: hisobot eksporti uchun avval navbat kerak (BullMQ asoslari), va eksport formati sifatida jadvallar uchun CSV/Excel tabiiyroq — keyingi dars.
O'zingiz sinab ko'ring: alohida papkada npm i pdfkit@0.20.2, DejaVu Sans shriftini (dejavu-fonts.github.io) fonts/ ga qo'ying va «PDFKit: qo'lda chizish» bo'limidagi kodni 40 qator bilan yurgizing. Keyin registerFont ni olib tashlab, Helvetica bilan Oʻzbekiston yozib, natijani PDF ko'ruvchida solishtiring.
12. Real ishda
- Chek, hisob-varaq, yorliq kabi qat'iy shakldagi hujjatlar ko'pincha serverda dasturiy chizish (PDFKit tipidagi) bilan yasaladi.
- SaaS hisobotlar — HTML shablon + headless Chromium, alohida "renderer" xizmatida.
- Elektron hujjat aylanishi — PDF/A (arxiv formati) va raqamli imzo talab qilinadi; bu kursda yo'q, hujjat bo'yicha o'rganing.
- Intervyuda: "PDF'da o'zbek harflari nega chiqmaydi?", "HTML'dan PDF yasashda qanday xavf bor?", "1 000 ta sertifikatni qanday yasaysiz?"
Xulosa
- 14 standart shrift WinAnsi'da:
ʻva kirill yo'q — to'liq shriftni embed qiling (subset + ToUnicode). - PDFKit — koordinatali chizish, eng kichik fayl; "n / N" —
bufferPages. - pdfmake — deklarativ JSON, jadval va sahifalash tayyor; serverda URL va lokal siyosatini albatta o'rnating.
page.pdf()— HTML/CSS shablon; iliq brauzerda 12 ms, lekin brauzer jarayoni og'ir.- Foydalanuvchi matni shablonda ekranlanadi — aks holda server ichidagi brauzer orqali SSRF.
- Katta hisobot — navbat va worker, natija omborda, havola muddatli.
Keyingi dars: Excel va CSV: eksport va import — 100 000 qatorni xotirani to'ldirmasdan eksport qilish va foydalanuvchi jadvalini xavfsiz import qilish.
Manbalar
- PDFKit hujjati (shriftlar,
bufferPages,switchToPage): https://pdfkit.org/ - pdfmake 0.3 — server rejimi va resurs siyosatlari: https://pdfmake.github.io/docs/
- Playwright
page.pdf(): https://playwright.dev/docs/api/class-page#page-pdf - PDF 32000-1:2008 (14 standart shrift, WinAnsiEncoding): https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/PDF32000_2008.pdf
- DejaVu shriftlari (litsenziya): https://dejavu-fonts.github.io/License.html
- Kanon tajribasi (IlmHamroh, 2026-10-27): pdfkit 0.20.2, pdfmake 0.3.12, playwright-core 1.62.0 (Chromium 151), fontkit 2.0.4, Node 24.21.0, Windows 11. PDF ko'ruvchida vizual tekshiruv, Puppeteer — sinalmagan. WinAnsi, ekranlash hisobi va mashq yechimlari — biz ishga tushirdik (2026-10-28).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!