React'te PDF Dosyası Nasıl Oluşturulur
React uygulamasından PDF belgeleri oluşturma eğitimine hoş geldiniz! Bu öğreticide, PDF oluşturmak için çeşitli kütüphaneleri keşfedeceğiz ve React bileşenlerinden doğrudan PDF dosyaları oluşturmak için popüler jsPDF kütüphanesini nasıl kullanacağınızı öğreneceğiz. O halde, başlayalım!
PDF (Taşınabilir Belge Formatı), belgelerin düzeni ve biçimlendirmesi korunarak paylaşılması ve yazdırılması için yaygın olarak kullanılan bir dosya formatıdır. Bir React web geliştiricisi olarak, React uygulamanızdan doğrudan faturalar, raporlar veya satış sözleşmeleri gibi PDF belgeleri üretmeniz gereken senaryolarla karşılaşabilirsiniz.
React PDF Kütüphanesi Seçmek
React uygulamasında PDF belgeleri oluşturmak, özellikle bu alanda yeniyseniz büyük bir iş olabilir. Neyse ki, bu süreci önemli ölçüde basitleştiren bir dizi üçüncü taraf kütüphaneye sahibiz. Her kütüphane, farklı kullanım durumlarına hitap eden benzersiz özellikler ve yardımcı programlarla birlikte gelir. Bu kütüphaneleri biraz daha ayrıntılı olarak inceleyelim.
jsPDF
jsPDF, JavaScript'ten PDF dosyaları oluşturmak için geliştiriciler arasında yaygın olarak popüler olan bir kütüphanedir. Ana satış noktalarından biri sadeliğidir. Süntaksı ve kullanımı oldukça basittir, HTML içeriğinizi kısa sürede PDF dosyasına dönüştürmenize olanak tanır.
Font boyutunu ve rengini değiştirmekten sayfa yönlendirmesini ve boyutunu ayarlamaya kadar, PDF'lerinizin biçimlendirmesini ve düzenini kontrol etmenizi sağlar. jsPDF, hem tarayıcı hem de sunucu ortamlarında çalışan sağlam bir çözüm sunarak, geniş bir yelpazedeki JavaScript uygulamaları için mükemmel bir seçenek olur.
pdfmake
pdfmake, saf JavaScript'te bir istemci/sunucu-tarafı PDF yazdırma çözümü olarak öne çıkar. Kompleks PDF'ler oluşturmak için mükemmel bir seçimdir, kapsamlı API'si ve esnek düzen seçenekleri sayesinde. pdfmake ile, basit bir JavaScript nesnesi kullanarak belge içeriğinizi ve yapınızı tanımlayabilir, ardından bunu geçerli bir PDF belgesine dönüştürebilirsiniz.
React-PDF
React-PDF, React bileşenleri kullanarak PDF dosyaları oluşturmak için güçlü işlevler sunan benzersiz bir kütüphanedir. Belge yapınızı bir JavaScript nesnesi içinde manuel olarak yazmak yerine, yeniden kullanılabilir bileşenler ve özdevrenler kullanarak React uygulaması gibi PDF'nizi oluşturabilirsiniz. IronPDF web sitesi, React-PDF kütüphanesini kullanarak PDF'ler oluşturma eğitimi içerir.
Neden jsPDF'yi Seçmelisiniz?
Her üç kütüphane de React'te PDF belgeleri oluşturmak için güçlü araçlar sağlarken, bu öğreticide toplulukta basitliği, esnekliği ve yaygın olarak benimsenmesi nedeniyle jsPDF kullanacağız. Yeni başlayanlar için düşük bir giriş engeli sağlar ve sağlam özellik seti onu birçok kullanım durumu için uygun bir seçim haline getirir. jsPDF ile inceleyeceğimiz prensipler, size PDF oluşturma konusunda sağlam bir temel sağlayacak ve projeniz bunu gerektirdiğinde diğer kütüphaneleri daha kolay öğrenebileceksiniz.
Ön Koşullar
Bu eğitime dalmadan önce, sorunsuz bir şekilde takip etmek için gerekli araçlar ve bilgiyle donatıldığınızdan emin olmanız esastır. Bu eğitimin ön koşulları aşağıdaki gibidir:
React'ın Temel Anlayışı
Öncelikle, kullanıcı arayüzleri, özellikle tek sayfalı uygulamalar oluşturmak için popüler bir JavaScript kütüphanesi olan React'a yönelik temel bir anlayışa sahip olmalısınız. React'ta JSX (JavaScript XML), bileşenler, durum ve özdevler gibi kavramlara aşina olmalısınız.
Geliştirme Ortamı
Bilgisayarınızda React uygulamaları geliştirmek için bir geliştirme ortamınızın kurulmuş olması da gereklidir. Bu bir metin editörü veya ve Entegre Geliştirme Ortamı (IDE) içerir. Visual Studio Code, Atom veya Sublime Text gibi metin editörleri iyi seçeneklerdir.
Node.js ve npm
Projemizi ve bağımlılıklarını yönetmek için, Node.js ve npm (Node Package Manager) kullanacağız. Bilgisayarınızda Node.js'in kurulu olduğundan emin olun. Node.js, sunucularımızda JavaScript çalıştırmamıza olanak tanıyan bir JavaScript yürütme ortamıdır. Birlikte npm yüklü gelir, böylece projeniz için gereken kütüphaneleri yönetebilirsiniz.
Node.js ve npm'nin kurulu olup olmadığını, aşağıdaki terminal komutlarını çalıştırarak kontrol edebilirsiniz:
node -v
npm -vnode -v
npm -vBu komutlar sırasıyla sisteminizde kurulu olan Node.js ve npm'nin sürümünü gösterecektir. Henüz kurulu değilseniz veya sürümleriniz eskiyse, en son Uzun Süreli Destek (LTS) sürümünü resmi indirme sayfasından indirip yüklemelisiniz.
1. Adım: Projeyi Kurmak
Hadi React projemizi kurarak başlayalım. Terminalinizi açın ve projenizi oluşturmak istediğiniz dizine gidin. Yeni bir React uygulaması oluşturmak için aşağıdaki komutu çalıştırın:
npx create-react-app pdf-from-reactnpx create-react-app pdf-from-react
Bu komut, temel bir React proje yapısı ile pdf-from-react adında yeni bir dizin oluşturacak.
Sonra, proje dizinine girin:
cd pdf-from-reactcd pdf-from-reactŞimdi, projemizi kod editörümüzde açabilir ve uygulamaya devam edebiliriz.
2. Adım: Gerekli Bağımlılıkları Eklemek
Öncelikle, gerekli paketleri yüklememiz gerekiyor. react, react-dom, @mui/material ve jspdf'yi aşağıdaki terminal komutuyla yükleyin.
npm install jspdf @mui/material @emotion/react @emotion/styled @mui/icons-materialnpm install jspdf @mui/material @emotion/react @emotion/styled @mui/icons-material3. Adım: PDF Oluşturma Özelliğini İnşa Etmek
Kütüphaneleri İçe Aktarma
Uygulamamız için gerekli bağımlılıkları içe aktarmaya başlıyoruz. Bunlar arasında, PDF oluşturmak için jsPDF kütüphanesi ve stil düzenleme araçları gibi Material-UI kütüphanesinden çeşitli bileşenler bulunmaktadır.
import React, { useState } from "react";
import "./App.css";
import {
Button,
TextField,
Box,
Container,
Typography,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Paper,
IconButton,
Snackbar,
Alert,
} from "@mui/material";
import Grid from "@mui/material/Grid";
import DeleteIcon from "@mui/icons-material/Delete";
import jsPDF from "jspdf";
import { styled } from "@mui/material/styles";
import { tableCellClasses } from "@mui/material/TableCell";import React, { useState } from "react";
import "./App.css";
import {
Button,
TextField,
Box,
Container,
Typography,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Paper,
IconButton,
Snackbar,
Alert,
} from "@mui/material";
import Grid from "@mui/material/Grid";
import DeleteIcon from "@mui/icons-material/Delete";
import jsPDF from "jspdf";
import { styled } from "@mui/material/styles";
import { tableCellClasses } from "@mui/material/TableCell";Stil Bileşenleri Oluşturma
Uygulamamıza tutarlı tarayıcılar arası davranış eklemek için MUI kütüphanesinden styled aracını kullanarak StyledTableCell ve StyledTableRow oluşturduk.
const StyledTableCell = styled(TableCell)(({ theme }) => ({
[`&.${tableCellClasses.head}`]: {
backgroundColor: theme.palette.common.black,
color: theme.palette.common.white,
},
[`&.${tableCellClasses.body}`]: {
fontSize: 14,
},
}));
const StyledTableRow = styled(TableRow)(({ theme }) => ({
"&:nth-of-type(odd)": {
backgroundColor: theme.palette.action.hover,
},
"&:last-child td, &:last-child th": {
border: 0,
},
}));const StyledTableCell = styled(TableCell)(({ theme }) => ({
[`&.${tableCellClasses.head}`]: {
backgroundColor: theme.palette.common.black,
color: theme.palette.common.white,
},
[`&.${tableCellClasses.body}`]: {
fontSize: 14,
},
}));
const StyledTableRow = styled(TableRow)(({ theme }) => ({
"&:nth-of-type(odd)": {
backgroundColor: theme.palette.action.hover,
},
"&:last-child td, &:last-child th": {
border: 0,
},
}));Uygulama Bileşenini Oluşturma
Uygulamamızın ana bileşeni App bileşenidir. Müşteri verilerini takip etmek için customerName ve customerAddress, faturadaki öğe listesini takip etmek için items ve gerekirse hata mesajı göstermek için error olmak üzere dört durumda değişkenimiz var.
function App() {
// State variables
const [customerName, setCustomerName] = useState("");
const [customerAddress, setCustomerAddress] = useState("");
const [items, setItems] = useState([{ name: "", quantity: "", price: "" }]);
const [error, setError] = useState(false);function App() {
// State variables
const [customerName, setCustomerName] = useState("");
const [customerAddress, setCustomerAddress] = useState("");
const [items, setItems] = useState([{ name: "", quantity: "", price: "" }]);
const [error, setError] = useState(false);Kullanıcı Girdisini Yönetme
Bu kod bloğunda, kullanıcı etkileşimlerini yönetmek için işlevler tanımladık: öğe ayrıntılarını değiştirme, yeni bir öğe ekleme ve bir öğeyi silme. handleItemChange fonksiyonu, bir kullanıcı bir öğeyi değiştirdiğinde o öğenin özelliklerini günceller. addItem fonksiyonu, listeye yeni bir öğe ekler. deleteItem fonksiyonu, bir öğeyi listeden çıkarır.
const handleItemChange = (index, event) => {
let newItems = [...items];
newItems[index][event.target.name] = event.target.value;
setItems(newItems);
};
const addItem = () => {
setItems([...items, { name: "", quantity: "", price: "" }]);
};
const deleteItem = (index) => {
let newItems = [...items];
newItems.splice(index, 1);
setItems(newItems);
};const handleItemChange = (index, event) => {
let newItems = [...items];
newItems[index][event.target.name] = event.target.value;
setItems(newItems);
};
const addItem = () => {
setItems([...items, { name: "", quantity: "", price: "" }]);
};
const deleteItem = (index) => {
let newItems = [...items];
newItems.splice(index, 1);
setItems(newItems);
};Faturayı Oluşturma
Aşağıda generateInvoice fonksiyon kodu verilmiştir:
// Generate invoice
const generateInvoice = () => {
// Validate the input fields
if (
!customerName ||
!customerAddress ||
items.some((item) => !item.name || !item.quantity || !item.price)
) {
setError(true);
return;
}
// Create a new jsPDF instance
let doc = new jsPDF("p", "pt");
// Add invoice header
doc.setFontSize(24);
doc.text("Invoice", 40, 60);
doc.setFontSize(10);
doc.text("Invoice Number: 123456", 40, 90);
doc.text("Date: " + new Date().toDateString(), 40, 110);
doc.text(`Customer Name: ${customerName}`, 40, 130);
doc.text(`Customer Address: ${customerAddress}`, 40, 150);
// Add items section
doc.setFontSize(14);
doc.text("Items:", 40, 200);
doc.line(40, 210, 550, 210);
// Add item details
doc.setFontSize(12);
let yOffset = 240;
let total = 0;
items.forEach((item) => {
let itemTotal = item.quantity * item.price;
total += itemTotal;
doc.text(`Item: ${item.name}`, 40, yOffset);
doc.text(`Quantity: ${item.quantity}`, 200, yOffset);
doc.text(`Price: $${item.price}`, 300, yOffset);
doc.text(`Total: $${itemTotal}`, 400, yOffset);
yOffset += 20;
});
// Add total
doc.line(40, yOffset, 550, yOffset);
doc.setFontSize(14);
doc.text(`Total: $${total}`, 400, yOffset + 30);
// Save the generated PDF as "invoice.pdf"
doc.save("invoice.pdf");
// Reset error state
setError(false);
};// Generate invoice
const generateInvoice = () => {
// Validate the input fields
if (
!customerName ||
!customerAddress ||
items.some((item) => !item.name || !item.quantity || !item.price)
) {
setError(true);
return;
}
// Create a new jsPDF instance
let doc = new jsPDF("p", "pt");
// Add invoice header
doc.setFontSize(24);
doc.text("Invoice", 40, 60);
doc.setFontSize(10);
doc.text("Invoice Number: 123456", 40, 90);
doc.text("Date: " + new Date().toDateString(), 40, 110);
doc.text(`Customer Name: ${customerName}`, 40, 130);
doc.text(`Customer Address: ${customerAddress}`, 40, 150);
// Add items section
doc.setFontSize(14);
doc.text("Items:", 40, 200);
doc.line(40, 210, 550, 210);
// Add item details
doc.setFontSize(12);
let yOffset = 240;
let total = 0;
items.forEach((item) => {
let itemTotal = item.quantity * item.price;
total += itemTotal;
doc.text(`Item: ${item.name}`, 40, yOffset);
doc.text(`Quantity: ${item.quantity}`, 200, yOffset);
doc.text(`Price: $${item.price}`, 300, yOffset);
doc.text(`Total: $${itemTotal}`, 400, yOffset);
yOffset += 20;
});
// Add total
doc.line(40, yOffset, 550, yOffset);
doc.setFontSize(14);
doc.text(`Total: $${total}`, 400, yOffset + 30);
// Save the generated PDF as "invoice.pdf"
doc.save("invoice.pdf");
// Reset error state
setError(false);
};generateInvoice fonksiyonunda, önce müşteri adı, müşteri adresi ve öğe ayrıntılarının doldurulmuş olduğundan emin olmak için giriş alanlarında doğrulama yapıyoruz. Bu alanlardan herhangi biri boşsa, error durumunu true olarak ayarlayıp erken döneriz.
Ardından, jsPDF yeni bir örneğini new jsPDF("p", "pt") çağırarak oluşturuyoruz. İlk argüman "p", sayfa yönlendirmesini portre olarak belirler ve ikinci argüman "pt"" ölçü birimini puan olarak belirtir.
Sonra, PDF belgemize içerik eklemeye başlıyoruz. Font boyutunu doc.setFontSize kullanarak ayarlıyoruz ve sayfada belirli koordinatlara metin eklemek için doc.text metodunu kullanıyoruz. Başlığı, fatura numarasını, tarihi, müşteri adını ve müşteri adresini içeren fatura başlığını ekliyoruz.
Başlıktan sonra, "Öğeler" bölümünü font boyutunu ayarlayarak ve başlıktan öğeleri ayırmak için bir çizgi ekleyerek doc.line kullanarak ekliyoruz. Daha sonra items dizisindeki her bir öğe üzerinde döngü yapıp miktarla fiyatı çarparak her öğenin toplam fiyatını hesaplıyoruz. Tüm öğe toplamlarının toplamıyla total değişkenini güncelliyoruz.
Her bir öğe için, öğe adını, miktarını, fiyatını ve öğe-toplamını PDF belgesine eklemek için doc.text kullanıyoruz. Her bir öğe için bir sonraki satıra geçmek üzere yOffset değişkenini artırıyoruz. Son olarak, öğeleri toplamdan ayırmak için bir çizgi ekliyoruz ve belge alt sağında toplam tutarı eklemek için doc.text kullanıyoruz.
İçerik eklendikten sonra, üretilen PDF'i kullanıcının bilgisayarında "invoice.pdf" olarak kaydetmek için doc.save("invoice.pdf") kullanıyoruz. Son olarak, önceki doğrulama hatalarını temizlemek için error durumunu false olarak sıfırlıyoruz.
Adım 4: UI'yı Oluşturma
return ifadesi, render işlemini yöneten JSX kodunu içerir. Bu, müşteri adı ve adresi için girdi alanları, öğe detaylarını girmek için bir tablo, öğe ekleme ve fatura oluşturma düğmeleri ve doğrulama hatalarını göstermek için bir hata snackbar'ı içerir.
Material-UI kütüphanesinden Button, TextField, Box, Container, Typography, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper, IconButton, Snackbar, ve Alert gibi bileşenleri kullanarak temel bileşenler oluşturur. Bu bileşenler, form alanları, tablolar, düğmeler ve hata bildirimlerini oluşturmak için kullanılır.
return (
<Container maxWidth="md">
<Box sx={{ my: 4 }}>
<Typography variant="h3" component="h1" gutterBottom>
Create Invoice
</Typography>
{/* Customer Name and Address fields */}
<Grid container spacing={3}>
<Grid item xs={6}>
<TextField
label="Customer Name"
fullWidth
margin="normal"
value={customerName}
onChange={(e) => setCustomerName(e.target.value)}
/>
</Grid>
<Grid item xs={6}>
<TextField
label="Customer Address"
fullWidth
margin="normal"
value={customerAddress}
onChange={(e) => setCustomerAddress(e.target.value)}
/>
</Grid>
</Grid>
{/* Items table */}
<TableContainer component={Paper}>
<Table sx={{ minWidth: 700 }} aria-label="invoice table">
<TableHead>
<TableRow>
<StyledTableCell>Item Name</StyledTableCell>
<StyledTableCell align="left">Quantity</StyledTableCell>
<StyledTableCell align="left">Price</StyledTableCell>
<StyledTableCell align="left">Action</StyledTableCell>
</TableRow>
</TableHead>
<TableBody>
{items.map((item, index) => (
<StyledTableRow key={index}>
<StyledTableCell component="th" scope="row">
<TextField
fullWidth
value={item.name}
onChange={(event) => handleItemChange(index, event)}
name="name"
/>
</StyledTableCell>
<StyledTableCell align="right">
<TextField
fullWidth
value={item.quantity}
onChange={(event) => handleItemChange(index, event)}
name="quantity"
/>
</StyledTableCell>
<StyledTableCell align="right">
<TextField
fullWidth
value={item.price}
onChange={(event) => handleItemChange(index, event)}
name="price"
/>
</StyledTableCell>
<StyledTableCell align="right">
<IconButton onClick={() => deleteItem(index)}>
<DeleteIcon />
</IconButton>
</StyledTableCell>
</StyledTableRow>
))}
</TableBody>
</Table>
</TableContainer>
{/* Buttons */}
<Box mt={2} display="flex" gap={2}>
<Button variant="contained" onClick={addItem}>
Add Item
</Button>
<Button variant="outlined" color="success" onClick={generateInvoice}>
Generate Invoice
</Button>
</Box>
</Box>
{/* Error Snackbar */}
<Snackbar
open={error}
autoHideDuration={6000}
onClose={() => setError(false)}
anchorOrigin={{ vertical: "top", horizontal: "right" }}
>
<Alert onClose={() => setError(false)} severity="error">
Please fill in all required fields.
</Alert>
</Snackbar>
</Container>
);return (
<Container maxWidth="md">
<Box sx={{ my: 4 }}>
<Typography variant="h3" component="h1" gutterBottom>
Create Invoice
</Typography>
{/* Customer Name and Address fields */}
<Grid container spacing={3}>
<Grid item xs={6}>
<TextField
label="Customer Name"
fullWidth
margin="normal"
value={customerName}
onChange={(e) => setCustomerName(e.target.value)}
/>
</Grid>
<Grid item xs={6}>
<TextField
label="Customer Address"
fullWidth
margin="normal"
value={customerAddress}
onChange={(e) => setCustomerAddress(e.target.value)}
/>
</Grid>
</Grid>
{/* Items table */}
<TableContainer component={Paper}>
<Table sx={{ minWidth: 700 }} aria-label="invoice table">
<TableHead>
<TableRow>
<StyledTableCell>Item Name</StyledTableCell>
<StyledTableCell align="left">Quantity</StyledTableCell>
<StyledTableCell align="left">Price</StyledTableCell>
<StyledTableCell align="left">Action</StyledTableCell>
</TableRow>
</TableHead>
<TableBody>
{items.map((item, index) => (
<StyledTableRow key={index}>
<StyledTableCell component="th" scope="row">
<TextField
fullWidth
value={item.name}
onChange={(event) => handleItemChange(index, event)}
name="name"
/>
</StyledTableCell>
<StyledTableCell align="right">
<TextField
fullWidth
value={item.quantity}
onChange={(event) => handleItemChange(index, event)}
name="quantity"
/>
</StyledTableCell>
<StyledTableCell align="right">
<TextField
fullWidth
value={item.price}
onChange={(event) => handleItemChange(index, event)}
name="price"
/>
</StyledTableCell>
<StyledTableCell align="right">
<IconButton onClick={() => deleteItem(index)}>
<DeleteIcon />
</IconButton>
</StyledTableCell>
</StyledTableRow>
))}
</TableBody>
</Table>
</TableContainer>
{/* Buttons */}
<Box mt={2} display="flex" gap={2}>
<Button variant="contained" onClick={addItem}>
Add Item
</Button>
<Button variant="outlined" color="success" onClick={generateInvoice}>
Generate Invoice
</Button>
</Box>
</Box>
{/* Error Snackbar */}
<Snackbar
open={error}
autoHideDuration={6000}
onClose={() => setError(false)}
anchorOrigin={{ vertical: "top", horizontal: "right" }}
>
<Alert onClose={() => setError(false)} severity="error">
Please fill in all required fields.
</Alert>
</Snackbar>
</Container>
);Tam App.js ve App.css Kodu
İşte projenize kopyalayıp yapıştırabileceğiniz tam App.js kodu:
import React, { useState } from "react";
import "./App.css";
import {
Button,
TextField,
Box,
Container,
Typography,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Paper,
IconButton,
Snackbar,
Alert,
} from "@mui/material";
import Grid from "@mui/material/Grid";
import DeleteIcon from "@mui/icons-material/Delete";
import jsPDF from "jspdf";
import { styled } from "@mui/material/styles";
import { tableCellClasses } from "@mui/material/TableCell";
const StyledTableCell = styled(TableCell)(({ theme }) => ({
[`&.${tableCellClasses.head}`]: {
backgroundColor: theme.palette.common.black,
color: theme.palette.common.white,
},
[`&.${tableCellClasses.body}`]: {
fontSize: 14,
},
}));
const StyledTableRow = styled(TableRow)(({ theme }) => ({
"&:nth-of-type(odd)": {
backgroundColor: theme.palette.action.hover,
},
"&:last-child td, &:last-child th": {
border: 0,
},
}));
function App() {
// State variables
const [customerName, setCustomerName] = useState("");
const [customerAddress, setCustomerAddress] = useState("");
const [items, setItems] = useState([{ name: "", quantity: "", price: "" }]);
const [error, setError] = useState(false);
// Event handler for item changes
const handleItemChange = (index, event) => {
let newItems = [...items];
newItems[index][event.target.name] = event.target.value;
setItems(newItems);
};
// Add new item to the list
const addItem = () => {
setItems([...items, { name: "", quantity: "", price: "" }]);
};
// Delete an item from the list
const deleteItem = (index) => {
let newItems = [...items];
newItems.splice(index, 1);
setItems(newItems);
};
// Generate invoice
const generateInvoice = () => {
// Validate the input fields
if (
!customerName ||
!customerAddress ||
items.some((item) => !item.name || !item.quantity || !item.price)
) {
setError(true);
return;
}
// Create a new jsPDF instance
let doc = new jsPDF("p", "pt");
// Add invoice header
doc.setFontSize(24);
doc.text("Invoice", 40, 60);
doc.setFontSize(10);
doc.text("Invoice Number: 123456", 40, 90);
doc.text("Date: " + new Date().toDateString(), 40, 110);
doc.text(`Customer Name: ${customerName}`, 40, 130);
doc.text(`Customer Address: ${customerAddress}`, 40, 150);
// Add items section
doc.setFontSize(14);
doc.text("Items:", 40, 200);
doc.line(40, 210, 550, 210);
// Add item details
doc.setFontSize(12);
let yOffset = 240;
let total = 0;
items.forEach((item) => {
let itemTotal = item.quantity * item.price;
total += itemTotal;
doc.text(`Item: ${item.name}`, 40, yOffset);
doc.text(`Quantity: ${item.quantity}`, 200, yOffset);
doc.text(`Price: $${item.price}`, 300, yOffset);
doc.text(`Total: $${itemTotal}`, 400, yOffset);
yOffset += 20;
});
// Add total
doc.line(40, yOffset, 550, yOffset);
doc.setFontSize(14);
doc.text(`Total: $${total}`, 400, yOffset + 30);
// Save the generated PDF as "invoice.pdf"
doc.save("invoice.pdf");
// Reset error state
setError(false);
};
return (
<Container maxWidth="md">
<Box sx={{ my: 4 }}>
<Typography variant="h3" component="h1" gutterBottom>
Create Invoice
</Typography>
{/* Customer Name and Address fields */}
<Grid container spacing={3}>
<Grid item xs={6}>
<TextField
label="Customer Name"
fullWidth
margin="normal"
value={customerName}
onChange={(e) => setCustomerName(e.target.value)}
/>
</Grid>
<Grid item xs={6}>
<TextField
label="Customer Address"
fullWidth
margin="normal"
value={customerAddress}
onChange={(e) => setCustomerAddress(e.target.value)}
/>
</Grid>
</Grid>
{/* Items table */}
<TableContainer component={Paper}>
<Table sx={{ minWidth: 700 }} aria-label="invoice table">
<TableHead>
<TableRow>
<StyledTableCell>Item Name</StyledTableCell>
<StyledTableCell align="left">Quantity</StyledTableCell>
<StyledTableCell align="left">Price</StyledTableCell>
<StyledTableCell align="left">Action</StyledTableCell>
</TableRow>
</TableHead>
<TableBody>
{items.map((item, index) => (
<StyledTableRow key={index}>
<StyledTableCell component="th" scope="row">
<TextField
fullWidth
value={item.name}
onChange={(event) => handleItemChange(index, event)}
name="name"
/>
</StyledTableCell>
<StyledTableCell align="right">
<TextField
fullWidth
value={item.quantity}
onChange={(event) => handleItemChange(index, event)}
name="quantity"
/>
</StyledTableCell>
<StyledTableCell align="right">
<TextField
fullWidth
value={item.price}
onChange={(event) => handleItemChange(index, event)}
name="price"
/>
</StyledTableCell>
<StyledTableCell align="right">
<IconButton onClick={() => deleteItem(index)}>
<DeleteIcon />
</IconButton>
</StyledTableCell>
</StyledTableRow>
))}
</TableBody>
</Table>
</TableContainer>
{/* Buttons */}
<Box mt={2} display="flex" gap={2}>
<Button variant="contained" onClick={addItem}>
Add Item
</Button>
<Button variant="outlined" color="success" onClick={generateInvoice}>
Generate Invoice
</Button>
</Box>
</Box>
{/* Error Snackbar */}
<Snackbar
open={error}
autoHideDuration={6000}
onClose={() => setError(false)}
anchorOrigin={{ vertical: "top", horizontal: "right" }}
>
<Alert onClose={() => setError(false)} severity="error">
Please fill in all required fields.
</Alert>
</Snackbar>
</Container>
);
}
export default App;import React, { useState } from "react";
import "./App.css";
import {
Button,
TextField,
Box,
Container,
Typography,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Paper,
IconButton,
Snackbar,
Alert,
} from "@mui/material";
import Grid from "@mui/material/Grid";
import DeleteIcon from "@mui/icons-material/Delete";
import jsPDF from "jspdf";
import { styled } from "@mui/material/styles";
import { tableCellClasses } from "@mui/material/TableCell";
const StyledTableCell = styled(TableCell)(({ theme }) => ({
[`&.${tableCellClasses.head}`]: {
backgroundColor: theme.palette.common.black,
color: theme.palette.common.white,
},
[`&.${tableCellClasses.body}`]: {
fontSize: 14,
},
}));
const StyledTableRow = styled(TableRow)(({ theme }) => ({
"&:nth-of-type(odd)": {
backgroundColor: theme.palette.action.hover,
},
"&:last-child td, &:last-child th": {
border: 0,
},
}));
function App() {
// State variables
const [customerName, setCustomerName] = useState("");
const [customerAddress, setCustomerAddress] = useState("");
const [items, setItems] = useState([{ name: "", quantity: "", price: "" }]);
const [error, setError] = useState(false);
// Event handler for item changes
const handleItemChange = (index, event) => {
let newItems = [...items];
newItems[index][event.target.name] = event.target.value;
setItems(newItems);
};
// Add new item to the list
const addItem = () => {
setItems([...items, { name: "", quantity: "", price: "" }]);
};
// Delete an item from the list
const deleteItem = (index) => {
let newItems = [...items];
newItems.splice(index, 1);
setItems(newItems);
};
// Generate invoice
const generateInvoice = () => {
// Validate the input fields
if (
!customerName ||
!customerAddress ||
items.some((item) => !item.name || !item.quantity || !item.price)
) {
setError(true);
return;
}
// Create a new jsPDF instance
let doc = new jsPDF("p", "pt");
// Add invoice header
doc.setFontSize(24);
doc.text("Invoice", 40, 60);
doc.setFontSize(10);
doc.text("Invoice Number: 123456", 40, 90);
doc.text("Date: " + new Date().toDateString(), 40, 110);
doc.text(`Customer Name: ${customerName}`, 40, 130);
doc.text(`Customer Address: ${customerAddress}`, 40, 150);
// Add items section
doc.setFontSize(14);
doc.text("Items:", 40, 200);
doc.line(40, 210, 550, 210);
// Add item details
doc.setFontSize(12);
let yOffset = 240;
let total = 0;
items.forEach((item) => {
let itemTotal = item.quantity * item.price;
total += itemTotal;
doc.text(`Item: ${item.name}`, 40, yOffset);
doc.text(`Quantity: ${item.quantity}`, 200, yOffset);
doc.text(`Price: $${item.price}`, 300, yOffset);
doc.text(`Total: $${itemTotal}`, 400, yOffset);
yOffset += 20;
});
// Add total
doc.line(40, yOffset, 550, yOffset);
doc.setFontSize(14);
doc.text(`Total: $${total}`, 400, yOffset + 30);
// Save the generated PDF as "invoice.pdf"
doc.save("invoice.pdf");
// Reset error state
setError(false);
};
return (
<Container maxWidth="md">
<Box sx={{ my: 4 }}>
<Typography variant="h3" component="h1" gutterBottom>
Create Invoice
</Typography>
{/* Customer Name and Address fields */}
<Grid container spacing={3}>
<Grid item xs={6}>
<TextField
label="Customer Name"
fullWidth
margin="normal"
value={customerName}
onChange={(e) => setCustomerName(e.target.value)}
/>
</Grid>
<Grid item xs={6}>
<TextField
label="Customer Address"
fullWidth
margin="normal"
value={customerAddress}
onChange={(e) => setCustomerAddress(e.target.value)}
/>
</Grid>
</Grid>
{/* Items table */}
<TableContainer component={Paper}>
<Table sx={{ minWidth: 700 }} aria-label="invoice table">
<TableHead>
<TableRow>
<StyledTableCell>Item Name</StyledTableCell>
<StyledTableCell align="left">Quantity</StyledTableCell>
<StyledTableCell align="left">Price</StyledTableCell>
<StyledTableCell align="left">Action</StyledTableCell>
</TableRow>
</TableHead>
<TableBody>
{items.map((item, index) => (
<StyledTableRow key={index}>
<StyledTableCell component="th" scope="row">
<TextField
fullWidth
value={item.name}
onChange={(event) => handleItemChange(index, event)}
name="name"
/>
</StyledTableCell>
<StyledTableCell align="right">
<TextField
fullWidth
value={item.quantity}
onChange={(event) => handleItemChange(index, event)}
name="quantity"
/>
</StyledTableCell>
<StyledTableCell align="right">
<TextField
fullWidth
value={item.price}
onChange={(event) => handleItemChange(index, event)}
name="price"
/>
</StyledTableCell>
<StyledTableCell align="right">
<IconButton onClick={() => deleteItem(index)}>
<DeleteIcon />
</IconButton>
</StyledTableCell>
</StyledTableRow>
))}
</TableBody>
</Table>
</TableContainer>
{/* Buttons */}
<Box mt={2} display="flex" gap={2}>
<Button variant="contained" onClick={addItem}>
Add Item
</Button>
<Button variant="outlined" color="success" onClick={generateInvoice}>
Generate Invoice
</Button>
</Box>
</Box>
{/* Error Snackbar */}
<Snackbar
open={error}
autoHideDuration={6000}
onClose={() => setError(false)}
anchorOrigin={{ vertical: "top", horizontal: "right" }}
>
<Alert onClose={() => setError(false)} severity="error">
Please fill in all required fields.
</Alert>
</Snackbar>
</Container>
);
}
export default App;İşte App.css kodu:
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&display=swap');
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-weight: bold;
/* This is the weight for bold in Poppins */
color: #FF6347;
/* This is the color Tomato. Replace with your preferred color */
}
body {
font-family: 'Poppins', sans-serif;
background-color: #E9F8F4;
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}Adım 5: Uygulamayı Test Etme
PDF oluşturma özelliğini test etmek için terminalde aşağıdaki komutu çalıştırın:
npm startnpm startBu, geliştirme sunucusunu başlatacak ve uygulamayı tarayıcınızda http://localhost:3000 adresinde görüntüleyebileceksiniz.

Girdi alanlarına müşteri adı, adres ve öğe detaylarını doldurun ve "Fatura Oluştur" düğmesine tıklayın. PDF dosyası bilgisayarınıza indirilecektir ve oluşturulan faturanın tam sayfa görünümünü görmek için açabilirsiniz.

"Fatura Oluştur" düğmesine tıkladığınızda, PDF dosyası oluşturulacaktır.

Herhangi bir boş alanla PDF oluşturmaya çalıştığınızda, sağ üst köşede bir hata mesajı gösterilecektir.

IronPDF - Node.js PDF Kütüphanesi
IronPDF for Node.js, doğruluk, kullanım kolaylığı ve hızda üstün olan kapsamlı bir Node.js PDF kütüphanesidir. HTML, URL'ler ve React'teki görüntülerden doğrudan PDF oluşturma, düzenleme ve biçimlendirme için geniş bir özellik yelpazesi sunar. Windows, MacOS, Linux, Docker ve Azure, AWS gibi bulut platformları dahil çeşitli platformlar için destek sunarak, IronPDF çapraz platform uyumluluğu sağlar. Kullanıcı dostu API'si, geliştiricilerin PDF oluşturma ve manipülasyonunu hızla Node.js projelerine entegre etmelerini sağlar.
IronPDF Node.js'nin kayda değer özellikleri: piksel mükemmellikte render, genişletilmiş biçimlendirme seçenekleri ve PDF'leri birleştirme ve ayırma, açıklama ekleme ve PDF formları oluşturma gibi gelişmiş düzenleme imkanları.
İşte bir HTML Dosyasından, HTML Dizesi ve URL'den PDF belge oluşturma örneği:
import { PdfDocument } from "@ironsoftware/ironpdf";
(async () => {
const pdfFromUrl = await PdfDocument.fromUrl("https://getbootstrap.com/");
await pdfFromUrl.saveAs("website.pdf");
const pdfFromHtmlFile = await PdfDocument.fromHtml("design.html");
await pdfFromHtmlFile.saveAs("markup.pdf");
const pdfFromHtmlString = await PdfDocument.fromHtml("<p>Hello World</p>");
await pdfFromHtmlString.saveAs("markup_with_assets.pdf");
})();import { PdfDocument } from "@ironsoftware/ironpdf";
(async () => {
const pdfFromUrl = await PdfDocument.fromUrl("https://getbootstrap.com/");
await pdfFromUrl.saveAs("website.pdf");
const pdfFromHtmlFile = await PdfDocument.fromHtml("design.html");
await pdfFromHtmlFile.saveAs("markup.pdf");
const pdfFromHtmlString = await PdfDocument.fromHtml("<p>Hello World</p>");
await pdfFromHtmlString.saveAs("markup_with_assets.pdf");
})();PDF ile ilgili görevler hakkında daha fazla kod örneği için lütfen bu IronPDF kod örnekleri sayfasını ziyaret edin.
Sonuç
Sonuç olarak, bir React uygulamasında PDF oluşturmak göz korkutucu olmak zorunda değildir. Doğru araçlar ve net bir anlayışla, şık ve iyi yapılandırılmış PDF belgelerini zahmetsizce oluşturabilirsiniz. jsPDF, pdfmake, ve React-PDF gibi her biri kendi güçlü yanlarına ve benzersiz özelliklerine sahip kütüphaneleri inceledik.
IronPDF'nin JavaScript çerçeveleri ve kütüphaneleri için anlaşılır entegrasyon süreci, mükemmel belgeleri ve duyarlı teknik desteği sayesinde, geliştiriciler kısa sürede çalışabilir duruma gelebilir, bu da onu Node.js uygulamalarında profesyonel kalitede PDF'ler oluşturmak için tercih edilen bir seçenek haline getirir.
IronPDF, tam işlevselliğinin ücretsiz bir deneme sürümünü sunar. Ayrıca diğer diller için de mevcuttur, örneğin C# .NET, Java ve Python. Daha fazla bilgi için IronPDF web sitesini ziyaret edin.








