Skip to content

Commit 2621f59

Browse files
committedNov 8, 2023
Add new dependencies and update PDF generation
function
1 parent ce33aac commit 2621f59

File tree

8 files changed

+604
-24
lines changed

8 files changed

+604
-24
lines changed
 

‎app/(dashboard)/dashboard/certificates/tc/component.tsx

+257-2
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,62 @@
1+
/* eslint-disable jsx-a11y/alt-text */
2+
/* eslint-disable @next/next/no-img-element */
13
"use client"
24

35
import React, { useState } from "react"
46
import { Student } from "@prisma/client"
57
import fmt from "date-fns/format"
68
import { MousePointerSquareDashed } from "lucide-react"
9+
import satori from "satori"
710
import { z } from "zod"
811

912
import { tcIssueFormSchema } from "@/lib/validations/student"
1013
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
1114
import { Separator } from "@/components/ui/separator"
1215

1316
import StudentForm from "./details-form"
17+
import { generatePdf } from "./pdf-generation"
1418
import SearchUser from "./search-user"
1519

1620
function TCPage() {
1721
const [selected, setselected] = useState<Student>()
18-
const onSubmit = (data: z.infer<typeof tcIssueFormSchema>) => {}
22+
const onSubmit = async (data: z.infer<typeof tcIssueFormSchema>) => {
23+
const geistRegular = await (
24+
await fetch("/fonts/Geist-Regular.otf")
25+
).arrayBuffer()
26+
const geistMedium = await (
27+
await fetch("/fonts/Geist-Medium.otf")
28+
).arrayBuffer()
29+
30+
const geistBold = await (await fetch("/fonts/Geist-Bold.otf")).arrayBuffer()
31+
32+
const svg = await satori(TCFormat(), {
33+
height: 841.89,
34+
width: 595.28,
35+
fonts: [
36+
{
37+
name: "Geist",
38+
data: geistRegular,
39+
weight: 400,
40+
style: "normal",
41+
},
42+
{
43+
name: "Geist",
44+
data: geistMedium,
45+
weight: 500,
46+
style: "normal",
47+
},
48+
{
49+
name: "Geist",
50+
data: geistBold,
51+
weight: 800,
52+
style: "normal",
53+
},
54+
],
55+
})
56+
generatePdf(svg, 595.28, 841.89, (url) => {
57+
window.open(url, "_blank")
58+
})
59+
}
1960
return (
2061
<div className="p-5">
2162
<SearchUser value={selected} setValue={setselected} />
@@ -68,7 +109,7 @@ function TCPage() {
68109
</div>
69110
</div>
70111
<Separator />
71-
<div className="mt-3">
112+
<div className="mt-3 ">
72113
<StudentForm id={selected.id} onSubmit={onSubmit} />
73114
</div>
74115
</>
@@ -83,3 +124,217 @@ function TCPage() {
83124
}
84125

85126
export default TCPage
127+
128+
export const TCFormat = () => {
129+
const fontScale = 0.8
130+
return (
131+
<div
132+
style={{
133+
height: "100%",
134+
width: "100%",
135+
display: "flex",
136+
flexDirection: "column",
137+
alignItems: "center",
138+
justifyContent: "flex-start",
139+
fontFamily: "Geist",
140+
backgroundColor: "#fff",
141+
fontSize: fontScale * 12,
142+
paddingTop: 20,
143+
fontWeight: 500,
144+
}}
145+
>
146+
<img
147+
width={100}
148+
src={"/images/govt-logo.png"}
149+
style={{
150+
maxWidth: "80%",
151+
}}
152+
/>
153+
154+
<div
155+
style={{
156+
display: "flex",
157+
flexDirection: "column",
158+
alignItems: "center",
159+
fontSize: fontScale * 10,
160+
textTransform: "uppercase",
161+
}}
162+
>
163+
<div>Government of Kerala</div>
164+
<div>Department of Technical Education</div>
165+
</div>
166+
<div
167+
style={{
168+
fontSize: fontScale * 16,
169+
fontWeight: 800,
170+
textTransform: "uppercase",
171+
}}
172+
>
173+
Govt. Polytechnic College, Perinthalmanna
174+
</div>
175+
<div>Angadipuram, Malappuram Dist, Kerala State - 679321</div>
176+
<div>Phone: 04933 227253, Email: polypmna@gptcperinthalmanna.in</div>
177+
178+
<div
179+
style={{
180+
marginTop: 8,
181+
textDecorationLine: "underline",
182+
fontSize: fontScale * 16,
183+
fontWeight: 800,
184+
textTransform: "uppercase",
185+
}}
186+
>
187+
TRANSFER CERTIFICATE
188+
</div>
189+
190+
<div
191+
style={{
192+
marginTop: 0,
193+
display: "flex",
194+
flexDirection: "row",
195+
width: "100%",
196+
justifyContent: "space-between",
197+
paddingLeft: 20,
198+
paddingRight: 20,
199+
}}
200+
>
201+
<div
202+
style={{
203+
marginTop: 0,
204+
borderWidth: 1,
205+
paddingLeft: 10,
206+
paddingRight: 10,
207+
padding: 5,
208+
fontSize: fontScale * 14,
209+
borderColor: "#000",
210+
fontWeight: 600,
211+
}}
212+
>
213+
TC No. 178/22-23
214+
</div>
215+
<div
216+
style={{
217+
marginTop: 0,
218+
borderWidth: 1,
219+
paddingLeft: 10,
220+
paddingRight: 10,
221+
padding: 5,
222+
fontSize: fontScale * 14,
223+
borderColor: "#000",
224+
fontWeight: 600,
225+
}}
226+
>
227+
Admission No. 11948
228+
</div>
229+
</div>
230+
<div
231+
style={{
232+
display: "flex",
233+
flexDirection: "column",
234+
width: "100%",
235+
padding: 20,
236+
}}
237+
>
238+
{/* Repeat this block */}
239+
<div
240+
style={{
241+
display: "flex",
242+
flexDirection: "row",
243+
width: "100%",
244+
justifyContent: "space-between",
245+
}}
246+
>
247+
<div>Name of Pupil ( in block letters)</div>
248+
<div
249+
style={{
250+
display: "flex",
251+
flexDirection: "row",
252+
width: "50%",
253+
justifyContent: "flex-start",
254+
}}
255+
>
256+
<div style={{ marginRight: 20 }}>: </div>
257+
<div>AMAL KRISHNAN. P</div>
258+
</div>
259+
</div>
260+
</div>
261+
{/* Repeat this block */}
262+
<div
263+
style={{
264+
display: "flex",
265+
flexDirection: "row",
266+
width: "100%",
267+
justifyContent: "flex-end",
268+
padding: 20,
269+
}}
270+
>
271+
<div
272+
style={{
273+
display: "flex",
274+
flexDirection: "row",
275+
justifyContent: "flex-start",
276+
}}
277+
>
278+
<div>Signature of the Head of institution with seal</div>
279+
</div>
280+
</div>
281+
<div
282+
style={{
283+
marginTop: 20,
284+
borderTopWidth: 1,
285+
borderColor: "#eee",
286+
width: "100%",
287+
paddingTop: 20,
288+
display: "flex",
289+
flexDirection: "column",
290+
alignItems: "center",
291+
fontSize: fontScale * 12,
292+
}}
293+
>
294+
<div
295+
style={{
296+
textDecorationLine: "underline",
297+
fontSize: fontScale * 16,
298+
fontWeight: 800,
299+
marginBottom: 10,
300+
textTransform: "uppercase",
301+
textAlign: "center",
302+
}}
303+
>
304+
Course and Conduct Certificate
305+
</div>
306+
<div>This is to certify that Sri./Kum.</div>
307+
<div
308+
style={{
309+
fontWeight: 800,
310+
}}
311+
>
312+
AMAL KRISHNAN
313+
</div>
314+
<div>was as bonafide student of this institution</div>
315+
<div>from 25/07/2019 to 28/06/2022.</div>
316+
<div>She/He completed the prescribed course of studies for the</div>
317+
<div>Diplama Examination in Electronics Engg.</div>
318+
<div>His/Her Conduct and Character are Good.</div>
319+
320+
<div
321+
style={{
322+
display: "flex",
323+
flexDirection: "row",
324+
justifyContent: "space-between",
325+
width: "100%",
326+
padding: 20,
327+
alignItems: "flex-end",
328+
}}
329+
>
330+
<div style={{ display: "flex", flexDirection: "column" }}>
331+
<div>Date: 12/04/2013</div>
332+
<div>Place: Perinthalmanna</div>
333+
</div>
334+
335+
<div>Signature of the Head of institution with seal</div>
336+
</div>
337+
</div>
338+
</div>
339+
)
340+
}

‎app/(dashboard)/dashboard/certificates/tc/pdf-generation.tsx

+16-22
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,26 @@ export const generatePdf = async (
44
svg: string,
55
width: number,
66
height: number,
7-
timeOut = 20000
7+
callBack: (url: string) => void
88
) => {
99
const PDFDocument = (await import("pdfkit/js/pdfkit.standalone")).default
1010
const SVGtoPDF = (await import("svg-to-pdfkit")).default
1111
const blobstream = (await import("blob-stream")).default
1212

13-
return new Promise<string>((resolve, reject) => {
14-
const time = setTimeout(() => {
15-
reject("Timed out")
16-
}, timeOut)
17-
const doc = new PDFDocument({
18-
compress: false,
19-
size: "A4",
20-
})
21-
SVGtoPDF(doc, svg, 0, 0, {
22-
width,
23-
height,
24-
preserveAspectRatio: `xMidYMid meet`,
25-
})
26-
const stream = doc.pipe(blobstream())
27-
stream.on("finish", () => {
28-
const blob = stream.toBlob("application/pdf")
29-
clearTimeout(time)
30-
resolve(URL.createObjectURL(blob))
31-
})
32-
stream.on("")
33-
doc.end()
13+
const doc = new PDFDocument({
14+
compress: false,
15+
size: "A4",
3416
})
17+
SVGtoPDF(doc, svg, 0, 0, {
18+
width,
19+
height,
20+
preserveAspectRatio: `xMidYMid meet`,
21+
})
22+
const stream = doc.pipe(blobstream())
23+
stream.on("finish", () => {
24+
const blob = stream.toBlob("application/pdf")
25+
const url = URL.createObjectURL(blob)
26+
callBack(url)
27+
})
28+
doc.end()
3529
}

‎package-lock.json

+327
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

+4
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
"@radix-ui/react-toggle": "^1.0.2",
4949
"@radix-ui/react-toggle-group": "^1.0.3",
5050
"@radix-ui/react-tooltip": "^1.0.5",
51+
"@react-pdf/renderer": "^3.1.14",
5152
"@t3-oss/env-nextjs": "^0.7.1",
5253
"@tanstack/react-table": "^8.10.7",
5354
"@typescript-eslint/parser": "^5.62.0",
@@ -60,6 +61,7 @@
6061
"clsx": "^1.2.1",
6162
"cmdk": "^0.1.22",
6263
"date-fns": "^2.30.0",
64+
"geist": "^1.0.1",
6365
"lucide-react": "^0.292.0",
6466
"next": "14.0.1",
6567
"next-auth": "4.24.4",
@@ -72,7 +74,9 @@
7274
"react-dom": "^18.2.0",
7375
"react-editor-js": "^2.1.0",
7476
"react-hook-form": "^7.47.0",
77+
"react-pdf-tailwind": "^2.1.0",
7578
"react-textarea-autosize": "^8.5.3",
79+
"satori": "^0.10.9",
7680
"sharp": "^0.32.6",
7781
"svg-to-pdfkit": "^0.1.8",
7882
"swr": "^2.2.4",

‎public/fonts/Geist-Bold.otf

59.6 KB
Binary file not shown.

‎public/fonts/Geist-Medium.otf

58.6 KB
Binary file not shown.

‎public/fonts/Geist-Regular.otf

57 KB
Binary file not shown.

‎public/images/govt-logo.png

58.2 KB
Loading

0 commit comments

Comments
 (0)
Please sign in to comment.