Mundarija (24)
- Bu darsda
- 1. Nega bu kerak?
- 2. PDF ichida nima bor
- 2.1 Qo'lda yozilgan PDF — o'quv modeli
- 2.2 Apostrof tuzog'i: /Encoding
- 3. Hujumchi nigohi: PDF ichiga buyruq qo'shish
- 4. PDF'ni ko'rsatish
- 4.1 Brauzerning o'z ko'ruvchisi
- 4.2 react-pdf: pdf.js React'da
- 5. PDF'ni brauzerda yaratish
- 5.1 jsPDF: buyruqlar bilan
- 5.2 @react-pdf/renderer: React komponentlari bilan
- 6. Chop etish: kutubxonasiz PDF
- 7. Qayerda yaratish kerak: brauzer yoki server
- 8. Biz sinab ko'rdik: hajm va CSP
- 9. Ko'p uchraydigan xatolar
- 10. Mashqlar
- 1-mashq (oson): bo'sh joylarni to'ldiring
- 2-mashq (o'rta): xavfsiz qatorlar
- 3-mashq (qiyin): 32 belgilik chek qatorlari
- 4-mashq: tajriba qadami (ixtiyoriy)
- 11. Real ishda
- Xulosa
- Manbalar
PDF React'da: ko'rsatish, chek yaratish va chop etish
Qisqacha: PDF'ni ko'rsatishning eng arzon yo'li — brauzerning o'z ko'ruvchisi (
<iframe>yoki havola). O'z interfeysingiz kerak bo'lsa — react-pdf (pdf.js, alohida worker'da). Chek yoki hisobotni brauzerda yaratish — jsPDF (buyruqlar:text,line) yoki @react-pdf/renderer (React komponentlari). Ko'pincha esa PDF kutubxonasi umuman kerak emas:window.print()+@media print— foydalanuvchi "PDF sifatida saqlash" ni tanlaydi. Narx katta: jsPDF +229.2 KB gzip, @react-pdf/renderer +429.9, react-pdf +550.9. Standart shriftlarda kirill harflari yo'q. Rasmiy hujjat, imzo va katta hisobot — serverda.
Bu darsda
- PDF fayl ichida nima borligini kichik qo'lda yozilgan generator orqali ko'rasiz.
- PDF'ni iframe va react-pdf bilan ko'rsatasiz, pdf.js nega worker'da ishlashini tushuntirasiz.
- jsPDF va @react-pdf/renderer bilan chek yaratasiz va kirill shrift tuzog'ini bilasiz.
- Chop etish CSS'i va
window.print()bilan kutubxonasiz "PDF" qilasiz. - PDF qachon klientda, qachon serverda yaratilishini asoslab tanlaysiz.
Oldin bilishingiz kerak: Rasm UI React'da, File va Blob, Foydalanuvchi afzalliklari va qurilma media'lari (@media print), Web Workers, XSS va DOM xavfsizligi.
1. Nega bu kerak?
Oldingi darsda video va audio bilan ishladik. Endi hujjatlar. «Bahor» kassasida uch xil talab bor:
- chek — mijoz buyurtmani to'lagach, "Chekni yuklab olish" tugmasi (80 mm lik termal printer o'lchamida);
- menyu — restoran menyusi PDF fayl, saytda varaqlab ko'rish;
- oylik hisobot — Jasur aka buxgalterga yuboradigan jadval.
PDF (Portable Document Format) — "ko'chma hujjat": har qurilmada, har printerda bir xil ko'rinadi. Sahifa o'lchami qat'iy, shriftlar fayl ichida. Veb sahifa esa ekranga moslashadi. Bu farq butun darsning asosi: PDF yaratish — har bir harfning joyini santimetrgacha aniq aytish demak.
Hayotiy o'xshatish: veb sahifa — suv, idishga qarab shakl oladi. PDF — muz: shakli muzlatilgan.
2. PDF ichida nima bor
2.1 Qo'lda yozilgan PDF — o'quv modeli
Kutubxonalar nega 200–500 KB ekanini tushunish uchun avval eng kichik PDF'ni o'zimiz yozamiz. PDF — asosan matn fayl: obyektlar (katalog, sahifalar, sahifa, mazmun, shrift) va oxirida xref jadvali — har obyekt fayl boshidan necha baytda turgani. Bu funksiya — o'quv modeli, real loyihada kutubxona ishlatiladi:
function makePdf(lines) {
const text = lines
.map((s, i) => `BT /F1 11 Tf 16 ${270 - i * 16} Td (${s}) Tj ET`)
.join("\n");
const objects = [
"<< /Type /Catalog /Pages 2 0 R >>",
"<< /Type /Pages /Kids [3 0 R] /Count 1 >>",
"<< /Type /Page /Parent 2 0 R /MediaBox [0 0 226 300]" +
" /Contents 4 0 R /Resources << /Font << /F1 5 0 R >> >> >>",
`<< /Length ${text.length} >>\nstream\n${text}\nendstream`,
"<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica" +
" /Encoding /WinAnsiEncoding >>",
];
let pdf = "%PDF-1.4\n";
const offsets = [];
objects.forEach((body, i) => {
offsets.push(pdf.length); // obyekt qayerdan boshlanadi (bayt)
pdf += `${i + 1} 0 obj\n${body}\nendobj\n`;
});
const xref = pdf.length;
pdf += `xref\n0 ${objects.length + 1}\n0000000000 65535 f \n`;
for (const offset of offsets) {
pdf += `${String(offset).padStart(10, "0")} 00000 n \n`;
}
pdf += `trailer\n<< /Size ${objects.length + 1} /Root 1 0 R >>\n`;
pdf += `startxref\n${xref}\n%%EOF\n`;
return pdf;
}
const pdf = makePdf([
"Bahor oshxonasi",
"Osh x1 35 000 so'm",
"Jami: 35 000 so'm",
]);
console.log(pdf.length, "bayt");
console.log(pdf.slice(pdf.indexOf("BT"), pdf.indexOf("ET") + 2));
console.log(pdf.slice(pdf.indexOf("xref")).split("\n").slice(0, 4));Konsolda:
717 bayt
BT /F1 11 Tf 16 270 Td (Bahor oshxonasi) Tj ET
[ 'xref', '0 6', '0000000000 65535 f ', '0000000009 00000 n ' ]717 baytlik to'liq PDF. Biz uni faylga yozib, pdf.js (Firefox'ning PDF ko'ruvchisi ichidagi kutubxona) bilan o'qib tekshirdik — bitta sahifa, uch qator matn. Ikkinchi qatorni o'qing — bu sahifaga chizish buyrug'i:
| Bo'lak | Ma'nosi |
|---|---|
BT … ET |
matn bloki boshi va oxiri |
/F1 11 Tf |
1-shrift, 11 punkt |
16 270 Td |
joy: chapdan 16, pastdan 270 punkt |
(…) Tj |
qavs ichidagi matnni chiz |
Uch narsaga e'tibor bering:
- Koordinata pastdan. PDF'da (0, 0) — sahifaning chap past burchagi. Shuning uchun har keyingi qator
270 - i * 16: pastga tushadi. Veb'dagi CSS'da esa yuqoridan. - O'lcham punktda.
MediaBox [0 0 226 300]— 226 × 300 punkt. 1 punkt = 1/72 dyuym, 226 pt ≈ 80 mm — termal chek eni. - Xref — baytlar bilan.
0000000009— birinchi obyekt 9-baytdan boshlanadi (%PDF-1.4\n— 9 bayt). Bitta harf qo'shilsa, keyingi hamma raqamlar siljiydi. Shuning uchun PDF'ni "qo'lda tahrirlash" qiyin, kutubxonalar esa bu hisobni o'zi qiladi.
2.2 Apostrof tuzog'i: /Encoding
Shrift obyektidagi /Encoding /WinAnsiEncoding ni bekorga qo'ymadik. Avval usiz sinadik va pdf.js matnni shunday o'qidi:
Bahor oshxonasi | Osh x1 35 000 so’m | Jami: 35 000 so’mso'm emas, so’m — "jingalak" apostrof. Sabab: kodlash berilmasa, Helvetica PDF'ning eski "standart kodlashi"ni oladi va unda ' belgisining bayti (39) o'ng tirnoq (’) degani. WinAnsiEncoding da esa 39 — oddiy apostrof. O'zbek lotinidagi o' va g' uchun bu kichik, lekin haqiqiy farq: qidiruv va nusxa ko'chirish boshqa harf topadi.
Bundan ham katta muammo — boshqa alifbolar. Standart 14 ta PDF shriftida (Helvetica, Times, Courier …) kirill harflari yo'q. Rus tilidagi chek yoki rasmiy imlodagi ʻ belgisi (U+02BB) uchun shrift faylini PDF ichiga joylash (embed) kerak. Buni jsPDF bilan pastda ko'ramiz.
Tekshirib ko'ring:
makePdfga uchta emas, 20 ta qator bersak, nima bo'ladi?
Javob
Har qator 16 punkt pastga tushadi: 20-qator 270 - 19 × 16 = -34 — sahifaning pastki chetidan tashqarida. PDF xato bermaydi, matn shunchaki ko'rinmaydi. Haqiqiy kutubxonalar ham avtomatik sahifa qo'shmaydi (jsPDF'da addPage() ni o'zingiz chaqirasiz), @react-pdf/renderer esa matnni o'zi keyingi sahifaga o'tkazadi — bu uning ustunligi.
3. Hujumchi nigohi: PDF ichiga buyruq qo'shish
makePdf matnni qavs ichiga tekshirmasdan qo'yadi: (${s}). Taom nomini esa admin panelda xodim yozadi. Hujumchi xodim taom nomiga shuni kiritadi:
Osh) Tj ET BT /F1 30 Tf 16 150 Td (BEPULNatijada mazmun qatori (Osh) Tj ET BT /F1 30 Tf 16 150 Td (BEPUL) Tj ET bo'ladi: birinchi ) matnni yopdi, qolgani esa yangi buyruqlar — 30 punktli katta «BEPUL» yozuvi chekning o'rtasida. Biz buni sinadik: pdf.js «Osh» va alohida katta «BEPUL» ni o'qidi. Bu — PDF injection (PDF ichiga buyruq qo'shish): XSS va SQL injection bilan bir oilada — ma'lumot kodga aylandi.
Himoya — PDF satridagi maxsus belgilarni (\, (, )) teskari chiziq bilan "qochirish" (escape):
const pdfString = (s) => s.replace(/[\\()]/g, "\\$&");
console.log(pdfString("Osh) Tj ET BT /F1 30 Tf 16 150 Td (BEPUL"));
console.log(pdfString("Lag'mon (katta)"));Konsolda:
Osh\) Tj ET BT /F1 30 Tf 16 150 Td \(BEPUL
Lag'mon \(katta\)Endi qavslar matnning bir qismi: pdf.js butun satrni bitta matn sifatida o'qidi, «BEPUL» buyrug'i bajarilmadi. Kutubxonalar (jsPDF, @react-pdf/renderer) buni o'zi qiladi — o'z generatoringizni yozmang, yozsangiz — qochirishni unutmang.
Ikkinchi xavf — begona PDF'ni ko'rsatish. PDF ichida havolalar, formalar va hatto JavaScript bo'lishi mumkin. pdf.js'ning 4.2.67 dan eski versiyalarida (pdfjs-dist ≤ 4.1.392) maxsus yasalgan PDF ko'ruvchi sahifasida begona JavaScript bajarishga imkon beradigan zaiflik bor edi (CVE-2024-4367). Eski react-pdf versiyalari ichida aynan shunday pdf.js bor edi. Qoidalar: pdf.js/react-pdf'ni yangilab turing, foydalanuvchi yuklagan PDF'larni asosiy domeningizdan emas, alohida domendan va Content-Disposition: attachment bilan bering. Fayl turi va hajmi esa serverda qayta tekshiriladi (Fayl yuklash UI).
4. PDF'ni ko'rsatish
4.1 Brauzerning o'z ko'ruvchisi
Kompyuterdagi Chrome, Firefox, Safari va Edge'da PDF ko'ruvchi ichida qurilgan. Eng arzon yo'l:
<iframe
src="/menyu/bahor-menyu.pdf"
title="«Bahor» menyusi (PDF)"
width="100%"
height={600}
/>
<a href="/menyu/bahor-menyu.pdf" download>
Menyuni yuklab olish (PDF, 240 KB)
</a>0 KB JavaScript, qidiruv, kattalashtirish, chop etish — hammasi tayyor. Kamchiliklari:
- Telefonda. Ko'p mobil brauzerlarda PDF iframe ichida ko'rinmaydi — fayl yuklab olinadi yoki alohida ilovada ochiladi. Shuning uchun yuklab olish havolasi doim yonida bo'lsin, hajmi bilan.
- Ko'rinishni boshqarib bo'lmaydi. Tugmalar, ranglar, sahifa raqami — brauzerniki.
- CSP. PDF iframe'da ochiladi —
frame-srcunga ruxsat bersin.<object>/<embed>esaobject-src 'none'bilan to'siladi (vazifalarCSP'sida shunday).
4.2 react-pdf: pdf.js React'da
O'z interfeysingiz kerak bo'lsa (menyu sahifalarini kartochka qilib ko'rsatish, sahifa ustiga belgi qo'yish) — react-pdf. U Mozilla'ning pdf.js kutubxonasini React komponentlariga o'raydi. Kod hujjat bo'yicha (natija oynasida yo'q):
import { useState } from "react";
import { Document, Page, pdfjs } from "react-pdf";
import "react-pdf/dist/Page/TextLayer.css";
import "react-pdf/dist/Page/AnnotationLayer.css";
// pdf.js faylni alohida thread'da (worker) o'qiydi
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
"pdfjs-dist/build/pdf.worker.min.mjs",
import.meta.url,
).toString();
export function MenuPdf({ file }) {
const [pages, setPages] = useState(0);
return (
<Document
file={file}
loading="Menyu yuklanmoqda…"
onLoadSuccess={({ numPages }) => setPages(numPages)}
>
{Array.from({ length: pages }, (_, i) => (
<Page key={i} pageNumber={i + 1} width={360} />
))}
</Document>
);
}Documentfaylni ochadi,Pagebitta sahifani canvas'ga chizadi. Ustiga shaffof matn qatlami qo'yiladi — sichqoncha bilan belgilash va nusxalash ishlaydi (shuning uchun CSS importlari).- Worker. PDF'ni tahlil qilish og'ir ish: 50 sahifali menyu interfeysni muzlatib qo'yardi. pdf.js uni Web Worker ga chiqaradi.
workerSrc— worker skriptining manzili; Vitenew URL(…, import.meta.url)ni build'da to'g'ri faylga aylantiradi. - Hamma sahifa birdan — kichik menyu uchun. 300 sahifali hujjatda ko'rinadigan sahifalarni chizing (virtualizatsiya g'oyasi).
Kanon tajribasida (react-pdf 11.0.0) natija halol aytilishi kerak: PDF chizilmadi. vazifalar CSP'si va Trusted Types ostida:
This document requires 'TrustedScriptURL' assignment. The action has been blocked.
Setting up fake worker failed: "Failed to fetch dynamically imported module: http://127.0.0.1:5870/cBirinchi qator: worker skript manzili Trusted Types siyosatisiz to'sildi. Ikkinchisi: pdf.js "zaxira" — worker'siz ishlashga urindi, u ham ochilmadi. CSP'siz ochilganda ham ikkinchi xato takrorlandi. Demak, gap faqat CSP'da emas: worker fayli brauzer so'ragan manzilda topilmagan. Tajriba kodi yuqoridagi hujjat qatorining aynan o'zi edi. Sababini biz qazimadik va qayta o'lchamadik — shuning uchun "react-pdf ishlamaydi" demaymiz. O'z loyihangizda build'dan keyin DevTools → Network'da pdf.worker faylini toping: u 200 bilan kelishi kerak. Hajm esa aniq: +550.9 KB gzip — PDF ko'ruvchi o'zi bir ilova. Faqat PDF sahifasida, lazy bilan.
5. PDF'ni brauzerda yaratish
5.1 jsPDF: buyruqlar bilan
jsPDF — "qalam" uslubi: shu nuqtaga matn yoz, shu yerga chiziq tort, rasm qo'y. Chek (kod hujjat bo'yicha, natija oynasida yo'q):
import { jsPDF } from "jspdf";
const doc = new jsPDF({ unit: "mm", format: [80, 120] });
doc.setFontSize(11);
doc.text("Bahor oshxonasi", 6, 10);
doc.text("Osh x1 35 000 so'm", 6, 18);
doc.line(6, 21, 74, 21); // ajratuvchi chiziq
doc.save("chek.pdf"); // Blob → <a download>, W-55 dagi kabiunit: "mm",format: [80, 120]— chek eni 80 mm. jsPDF'da koordinata yuqoridan (PDF'nikidan farqli) — kutubxona o'zi aylantiradi.save— ichida o'sha retsept: Blob, obyekt URL,<a download>. Faylni serverga yuborish kerak bo'lsa —doc.output("blob").
Biz jsPDF 4.2.1 ni dars tayyorlashda Node 24 da ishga tushirdik. Kanon tajribasidagi bir qatorli chek ("Chek: osh 35 000") — 3168 bayt, xuddi brauzerdagidek. Keyin yuqoridagi chekka bitta ruscha qator qo'shdik: doc.text("Плов x1", 6, 26). pdf.js shuni o'qidi:
Bahor oshxonasi | Osh x1 35 000 so'm | ; > 2 | | x 1Apostrof joyida (jsPDF WinAnsiEncoding ni o'zi qo'yadi), lekin Плов o'rnida — ; > 2. Xato ham, ogohlantirish ham chiqmadi. Kirill uchun shrift faylini (.ttf) PDF'ga joylash kerak:
doc.addFileToVFS("NotoSans.ttf", notoSansBase64); // shrift — base64
doc.addFont("NotoSans.ttf", "NotoSans", "normal");
doc.setFont("NotoSans");
doc.text("Плов x1", 6, 26); // endi to'g'riNarxi — shrift faylining o'zi: u ham JavaScript to'plamiga (yoki alohida yuklanadigan faylga) qo'shiladi. Faqat kerakli harflarni qoldirib kichraytirish (subsetting) — yaxshi amaliyot.
html2canvas + jsPDF — mashhur, lekin xavfli yorliq: sahifadagi HTML'ni rasmga aylantirib, PDF'ga rasm qilib qo'yish. Natija tez, lekin matn rasm bo'lib qoladi — qidirib, nusxalab bo'lmaydi, ekran o'quvchi o'qimaydi, hajmi katta va xira. Biz bu juftlikni o'lchamadik. Chek uchun jsPDF'ning text(), sahifaning o'zi uchun — chop etish CSS'i.
5.2 @react-pdf/renderer: React komponentlari bilan
@react-pdf/renderer — PDF'ni React komponentlaridan yig'adi. Flexbox bilan joylashtirish, uzun matn keyingi sahifaga o'zi o'tadi:
import {
Document,
Page,
Text,
View,
StyleSheet,
pdf,
} from "@react-pdf/renderer";
const s = StyleSheet.create({
page: { padding: 16, fontSize: 11 },
row: { flexDirection: "row", justifyContent: "space-between" },
});
function Receipt({ items }) {
return (
<Document>
<Page size={[226, 300]} style={s.page}>
<Text>Bahor oshxonasi</Text>
{items.map((item) => (
<View key={item.name} style={s.row}>
<Text>{item.name}</Text>
<Text>{item.price} so'm</Text>
</View>
))}
</Page>
</Document>
);
}
const blob = await pdf(<Receipt items={items} />).toBlob();Bu komponentlar sahifaga chizilmaydi — ular faqat PDF'ga aylanadi. <div> emas <View>, <p> emas <Text>; CSS'ning bir qismi ishlaydi. Narx o'ng tomonga flexbox bilan tekislandi — jsPDF'da buning uchun matn enini o'zingiz hisoblaysiz.
Kanon tajribasida (4.9.0) +429.9 KB gzip va vazifalar CSP'si ostida PDF yaratilmadi:
Connecting to 'data:application/octet-stream;base64,AGFzbQEAAAABugM3YAF/AGACf38AYAF/AX9gA39/fwBgAn98
Aborted(CompileError: WebAssembly.instantiate(): Compiling or instantiating WebAssembly module violaKutubxona joylashtirish hisobini (flexbox) WebAssembly modulida qiladi va uni data: manzildan yuklaydi. CSP ikki joyda to'sdi: connect-src (data: ga so'rov) va WebAssembly'ni kompilyatsiya qilish (script-src da 'wasm-unsafe-eval' yo'q). CSP'siz ochilganda esa bir qatorli chek yaratildi — 1528 bayt. Bu kutubxonani tanlasangiz, CSP'ni ongli kengaytirasiz.
Tekshirib ko'ring: Nega jsPDF'dagi 3168 baytlik chek bizning 717 baytlik
makePdfdan 4 baravar katta, garchi matn bir xil bo'lsa ham?
Javob
jsPDF to'liqroq hujjat yozadi: metama'lumot (yaratuvchi dastur, sana), barcha standart shriftlar ro'yxati va kodlashlari, ko'proq sahifa sozlamalari. Bu "ortiqcha" baytlar turli PDF o'quvchilarda hujjat bir xil ochilishini kafolatlaydi. 3 KB baribir juda kichik — muhimi PDF fayl emas, kutubxonaning JavaScript'i: +229.2 KB.
6. Chop etish: kutubxonasiz PDF
Ko'p hollarda eng yaxshi "PDF generator" — brauzerning chop etish oynasi. Unda "PDF sifatida saqlash" bor. Siz faqat sahifaning qog'ozdagi ko'rinishini @media print bilan tayyorlaysiz:
@media print {
nav,
.print-hidden {
display: none;
}
body {
font: 11pt/1.4 system-ui, sans-serif;
color: black;
}
@page {
size: A4;
margin: 15mm;
}
}React'da tugma — bitta qator:
<button className="print-hidden" onClick={() => window.print()}>
Chop etish yoki PDF sifatida saqlash
</button>Saytdagi natija oynasida bu tugma ishlamaydi: oyna himoyalangan (sandbox) va unda modal oynalarga — chop etish oynasi ham shunga kiradi — ruxsat yo'q. O'z loyihangizda ishlaydi.
Chop etishdan oldin sahifani tayyorlash kerak bo'lsa (masalan, yopiq details teglarini ochish), beforeprint va afterprint hodisalari bor — React'da ularga effektda obuna bo'linadi (useEffect I). Narx: 0 KB, matn tanlanadi, shriftlar brauzerniki — kirill ham muammosiz.
7. Qayerda yaratish kerak: brauzer yoki server
flowchart TB
A{"Hujjat rasmiymi? (hisob-faktura, shartnoma, imzo)"} -- ha --> S["Server (backend)"]
A -- yo'q --> B{"Sahifaning o'zi qog'ozga kerakmi?"}
B -- ha --> P["window.print + @media print (0 KB)"]
B -- yo'q --> C{"Ma'lumot brauzerda, hujjat kichikmi?"}
C -- ha --> K["Klient: jsPDF (+229 KB, kech yuklash)"]
C -- yo'q --> SDiagrammaga qarang. Server tomonga uchta kuchli sabab bor:
- Bir xil natija. Brauzerlar shriftni turlicha chizadi. Hisob-faktura har kimda bir xil ko'rinishi kerak — server bitta shrift va bitta dastur bilan yaratadi.
- Ishonch. Klientda yaratilgan PDF'ni foydalanuvchi o'zgartira oladi: summani tahrirlab, "chek" ko'rsatadi. Rasmiy hujjat serverdagi ma'lumotdan, kerak bo'lsa imzo bilan yaratiladi.
- Hajm. 300 sahifali yillik hisobot telefonda muzlatadi; serverda — fon ishi, tayyor bo'lgach havola.
Serverda PDF yasash (PDFKit, pdfmake yoki headless brauzerda page.pdf()) — backend qismida, PDF generatsiya darsida. Klientda esa — oflayn ishlaydigan va darhol kerak bo'ladigan narsalar: kassadagi chek, foydalanuvchining o'z ma'lumotlari.
8. Biz sinab ko'rdik: hajm va CSP
- window.print + @media print0 KB
- jsPDF 4.2.1CSP 0, chek 3168 bayt229,2 KB
- @react-pdf/renderer 4.9.0CSP: WASM data: to'sildi429,9 KB
- react-pdf 11.0.0TT: worker to'sildi550,9 KB
Manba: O'lchandi: kanon tajribasi, Vite 8.3.3, gzip -9, Chrome 155, natijalar/tajriba/kutubxonalar-21.txt, 2026-10-17
Taqqoslash uchun: vazifalar ning butun birinchi yuklashi — ~221 KB. jsPDF bittasi shunga teng, PDF ko'ruvchi — 2.5 baravar. Uchala kutubxona ham faqat kech yuklanishi kerak: foydalanuvchi «Chekni yuklab olish» ni bosgandagina (lazy va code splitting — import("jspdf") tugma hodisasida).
CSP natijasi (kutubxonalar-21.txt, xom). Qatorlar uzun — har birida faqat CSP so'zidan keyingi qismga qarang: 0 — buzilish yo'q, boshqasi — qaysi qoida to'sgani:
pdf-jspdf JS 302388 (+ 229.2 KB) CSS 0 | CSP 0 natija=3168 | xato: yo'q | ochiq: tashqi yo'q
pdf-react-pdf JS 631791 (+ 550.9 KB) CSS 0 | CSP require-trusted-types-for×1 | xato: This document requires 'TrustedScriptURL' assignment. The action has been blocked. ; Setting up fake worker failed: "Failed to fetch dynamically imported module: http://127.0.0.1:5870/c | ochiq: tashqi yo'q xato: Setting up fake worker failed: "Failed to fetch dynamically imported module: http://127.0.0.1:5870/o
pdf-renderer JS 507953 (+ 429.9 KB) CSS 0 | CSP connect-src×1,script-src×1 | xato: Connecting to 'data:application/octet-stream;base64,AGFzbQEAAAABugM3YAF/AGACf38AYAF/AX9gA39/fwBgAn98 ; Fetch API cannot load data:application/octet-stream;base64,AGFzbQEAAAABugM3YAF/AGACf38AYAF/AX9gA39/f | ochiq: tashqi yo'qUchtasidan faqat jsPDF qattiq siyosat ostida toza ishladi. Hech biri tashqi serverga bormadi.
Nega vazifalar da PDF yo'q. Ro'yxatni ilovadan olib chiqish uchun JSON eksport bor, keyingi darsda CSV qo'shiladi — Excel'da ochiladi. PDF uchun kutubxona +229 KB bo'lardi, foydasi esa kam: ro'yxatni qog'ozga chiqarish kerak bo'lsa, brauzerning chop etish oynasi yetadi. Kanon PDF kutubxonasini qo'shmadi.
9. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
ruscha matn o'rnida ; > 2 |
standart shriftda kirill yo'q | .ttf shriftni joylash (addFont) |
so'm → so’m |
shriftda kodlash yo'q | /Encoding /WinAnsiEncoding (kutubxona o'zi qiladi) |
| matn sahifa tagidan chiqib ketdi | PDF koordinatasi pastdan, sahifa avtomatik qo'shilmaydi | addPage(); yoki @react-pdf/renderer |
react-pdf: Setting up fake worker failed |
worker fayli o'sha manzilda yo'q | Network'da worker so'rovini tekshiring; workerSrc — hujjatdagidek |
requires 'TrustedScriptURL' |
worker manzili Trusted Types'siz | siyosat yoki boshqa yo'l |
WebAssembly … violates |
@react-pdf/renderer WASM'i | 'wasm-unsafe-eval' va connect-src data: — ongli |
| telefonda PDF iframe bo'sh | mobil brauzer inline ko'rsatmaydi | yonida yuklab olish havolasi |
| chekda «BEPUL» paydo bo'ldi | PDF satri qochirilmagan | kutubxona yoki pdfString |
10. Mashqlar
Mashqlar kurs/mashqlar/21/14-pdf/ da. Yechimlarni «Ishga tushir» bilan sinang.
1-mashq (oson): bo'sh joylarni to'ldiring
- PDF'da koordinata (0, 0) sahifaning chap burchagi
- 1 punkt = 1/ dyuym
- jsPDF'da kirill uchun shrift qo'shadigan metod:
doc.(…) - react-pdf faylni alohida thread'da o'qiydi — bu
- Kutubxonasiz PDF:
window.()va@media print
Yechim
- past — veb'dan farqli; har keyingi qator kichikroq
ybilan. - 72 — 80 mm ≈ 226 punkt.
- addFont —
addFileToVFSdan keyin; so'ngsetFont. - worker — pdf.js tahlili interfeysni muzlatmasin.
- print — foydalanuvchi chop etish oynasida "PDF sifatida saqlash" ni tanlaydi.
2-mashq (o'rta): xavfsiz qatorlar
makePdf ni xavfsiz qiling: har qator chizishdan oldin pdfString dan o'tsin. Bundan tashqari, satrda chop etib bo'lmaydigan belgilar bo'lsa (kod nuqtasi 127 dan katta — masalan kirill yoki emoji), ular ? ga almashsin — standart shrift ularni baribir chiza olmaydi. Faqat mazmun qatorini yasaydigan contentStream(lines) funksiyasini yozing va sinang.
Ishora: belgilarni almashtirish — muntazam ifoda /[^\x20-\x7e]/g ("bo'shliqdan ~ gacha bo'lmagan hamma narsa", Belgilar va belgi klasslari).
Yechim
const pdfString = (s) =>
s.replace(/[^\x20-\x7e]/g, "?").replace(/[\\()]/g, "\\$&");
function contentStream(lines) {
return lines
.map((s, i) => {
const y = 270 - i * 16;
return `BT /F1 11 Tf 16 ${y} Td (${pdfString(s)}) Tj ET`;
})
.join("\n");
}
console.log(contentStream(["Lag'mon (katta)", "Плов", "a\\b"]));Konsolda:
BT /F1 11 Tf 16 270 Td (Lag'mon \(katta\)) Tj ET
BT /F1 11 Tf 16 254 Td (????) Tj ET
BT /F1 11 Tf 16 238 Td (a\\b) Tj ETTartib muhim: avval keraksiz belgilarni ? ga, keyin qochirish. Teskari tartibda ham bu yerda farq chiqmaydi, lekin "avval tozalash, keyin qochirish" — umumiy qoida: qochirilgan natijani boshqa almashtirish buzmasin. "Плов" — 4 ta ?: ma'lumot yo'qolganini foydalanuvchi ko'radi. Jim buzilgan ; > 2 dan yaxshiroq, lekin to'g'ri yechim — shrift joylash.
3-mashq (qiyin): 32 belgilik chek qatorlari
Termal printer cheki odatda 32 belgilik qatorlar va bir xil enli shrift (Courier). receiptLines(items) funksiyasini yozing:
- Har taom: chapda
Nomi xN, o'ngda summa (70 000), oraliq bo'shliq bilan — qator aniq 32 belgi. - Nom sig'masa — qisqartirilib, oxiriga
..qo'yilsin (xNva summa doim ko'rinsin). - Oxirida 32 ta
-vaJami:qatori.
Sinov: Osh ×2 (35 000), Ko'k choy ×1 (5 000), «Lag'mon maxsus katta porsiya» ×1 (28 000). Har qatorni |…| ichida chiqaring — eni ko'rinsin.
Ishora: son guruhlari — Grafiklar I darsidagi som() muntazam ifodasi; to'ldirish — " ".repeat(n); jami — reduce.
Yechim
const WIDTH = 32;
function som(n) {
return String(n).replace(/\B(?=(\d{3})+(?!\d))/g, " ");
}
function row(name, suffix, price) {
const right = som(price);
const max = WIDTH - right.length - 1 - suffix.length;
const short =
name.length > max ? name.slice(0, max - 2) + ".." : name;
const left = short + suffix;
const gap = WIDTH - left.length - right.length;
return left + " ".repeat(gap) + right;
}
function receiptLines(items) {
const total = items.reduce((s, i) => s + i.qty * i.price, 0);
return [
...items.map((i) => row(i.name, ` x${i.qty}`, i.qty * i.price)),
"-".repeat(WIDTH),
row("Jami:", "", total),
];
}
const lines = receiptLines([
{ name: "Osh", qty: 2, price: 35000 },
{ name: "Ko'k choy", qty: 1, price: 5000 },
{ name: "Lag'mon maxsus katta porsiya", qty: 1, price: 28000 },
]);
for (const line of lines) console.log(`|${line}|`);Konsolda:
|Osh x2 70 000|
|Ko'k choy x1 5 000|
|Lag'mon maxsus katta.. x1 28 000|
|--------------------------------|
|Jami: 103 000|max — nom uchun qolgan joy: 32 dan summa, bitta majburiy bo'shliq va x1 ayiriladi. Bu qatorlarni makePdf ga /BaseFont /Courier bilan bersangiz, ustunlar qog'ozda ham tekis turadi — Courier'da har harf bir xil enli. Helvetica'da esa i va m turlicha keng, ustunlar siljiydi.
4-mashq: tajriba qadami (ixtiyoriy)
vazifalar ga tegmang. Alohida Vite + React loyihada «Chekni yuklab olish» tugmasini yasang: jsPDF tugma bosilganda import("jspdf") bilan kech yuklansin, chek 3-mashqdagi qatorlar bilan, setFont("courier"). Keyin ruscha qator qo'shing va shrift joylab tuzating.
Yechim
Tekshiruv ro'yxati:
npm i -E jspdf@4.2.1;npm run build— jsPDF alohida chunk'da, birinchi yuklashga kirmagan (kanonda ~229 KB gzip).- DevTools → Network: chunk faqat birinchi bosishda keladi, ikkinchisida — keshdan.
- Yuklangan PDF'da matnni sichqoncha bilan belgilab nusxalang —
so'moddiy apostrof bilan. - Ruscha qator: shriftsiz — buzilgan harflar;
addFileToVFS+addFont+setFontdan keyin — to'g'ri. Build hajmi shrift fayli qadar o'sadi. vazifalarCSP'si bilan sinasangiz: jsPDF — buzilish yo'q (kanonda CSP 0).
11. Real ishda
- Chek va kvitansiyalar — kassa va onlayn do'konlarda; ko'pincha server yaratadi, oflayn kassada — klient (jsPDF).
- Hujjatlar — shartnoma, sertifikat, hisob-faktura: serverda, bir xil shrift bilan, ba'zan elektron imzo bilan.
- Ko'rish — kitob, menyu, qo'llanma: oddiy holda brauzer ko'ruvchisi, maxsus interfeysda react-pdf.
- Intervyu savollari: "PDF'ni klientda yoki serverda yaratish — qachon qaysi?", "html2canvas nega yomon yechim?", "pdf.js nega worker ishlatadi?", "kirill matn PDF'da nega buziladi?".
Xulosa
- PDF — obyektlar va bayt manzillari (xref); koordinata pastdan, o'lcham punktda; satrlar qochiriladi.
- Ko'rsatish: brauzer ko'ruvchisi (0 KB, telefonda yuklab olish havolasi bilan) yoki react-pdf (pdf.js, worker, +550.9 KB).
- Yaratish: jsPDF (+229.2 KB, CSP 0) — buyruqlar; @react-pdf/renderer (+429.9 KB) — komponentlar, CSP'da WASM.
- Standart shriftlarda kirill yo'q —
.ttfjoylash; kodlashWinAnsiEncoding(apostrof). - Ko'pincha eng yaxshisi —
window.print()+@media print. - Rasmiy, katta va bir xil bo'lishi shart hujjat — serverda (PDF generatsiya).
Keyingi dars: Excel va CSV: import va eksport — vazifalar ro'yxatini Excel'da ochiladigan CSV qilib yuklab olish va Excel'dan saqlangan faylni qayta o'qish (o'z CSV o'quvchisi, PapaParse, SheetJS, ExcelJS).
Manbalar
- PDF 1.7 (ISO 32000-1): fayl tuzilishi, matn operatorlari (
BT,Tf,Td,Tj), standart shriftlar va kodlashlar - react-pdf — github.com/wojtekmaj/react-pdf ("Configure PDF.js worker"); pdf.js — mozilla.github.io/pdf.js
- jsPDF — artskydj.github.io/jsPDF/docs ("addFont", "output"); @react-pdf/renderer — react-pdf.org
- CVE-2024-4367 (pdf.js, 4.2.67 da tuzatilgan) — github.com/mozilla/pdf.js/security
- MDN:
window.print(),beforeprint/afterprint,@page - Kanon:
natijalar/tajriba/kutubxonalar-21.txt,tajriba/kutubxonalar/src/k/pdf-*.tsx(2026-10-17); jsPDF 4.2.1 va pdf.js bilan o'qish — dars tayyorlashda, Node 24 (2026-10-18)
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!