[Golang]สร้าง PDF ด้วย chromedp
4 min readJan 20, 2025
คุณกำลังหาวิธีสร้าง PDF ของภาษา GO อยู่หรือเปล่า คงใช่แหละไม่งั้นคงไม่เขามาหรอกใช่ไหม 55555
เหมือนกันครับช่วงนี้ระบบต้องสร้างไฟล์ PDF ก็เลยมาเขียนบันทึกไว้สักหน่อย จะไม่อธิบายเยอะน่ะครับหาก
อยากอ่าน code เลยเชิญที่นี้ คลิกนี้
โดนในที่นี้ใช้ lib chromedp
หรือมีอีกตัวที่เคยใช้
ลำดับการทำงาน
- สร้างไฟล์ html ที่จะ gen PDF
- อ่านไฟล์ template html
- สร้างไฟล์ PDF
- บันทึกไฟล์ / response APIs
สร้างไฟล์ html ที่จะ gen PDF
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>A simple, clean, and responsive HTML invoice template</title>
<!-- Favicon -->
<link rel="icon" href="./images/favicon.png" type="image/x-icon" />
<!-- Invoice styling -->
<style>
body {
font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;
text-align: center;
color: #777;
}
body h1 {
font-weight: 300;
margin-bottom: 0px;
padding-bottom: 0px;
color: #000;
}
body h3 {
font-weight: 300;
margin-top: 10px;
margin-bottom: 20px;
font-style: italic;
color: #555;
}
body a {
color: #06f;
}
.invoice-box {
max-width: 800px;
margin: auto;
padding: 30px;
border: 1px solid #eee;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.15);
font-size: 16px;
line-height: 24px;
font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;
color: #555;
}
.invoice-box table {
width: 100%;
line-height: inherit;
text-align: left;
border-collapse: collapse;
}
.invoice-box table td {
padding: 5px;
vertical-align: top;
}
.invoice-box table tr td:nth-child(2) {
text-align: right;
}
.invoice-box table tr.top table td {
padding-bottom: 20px;
}
.invoice-box table tr.top table td.title {
font-size: 45px;
line-height: 45px;
color: #333;
}
.invoice-box table tr.information table td {
padding-bottom: 40px;
}
.invoice-box table tr.heading td {
background: #eee;
border-bottom: 1px solid #ddd;
font-weight: bold;
}
.invoice-box table tr.details td {
padding-bottom: 20px;
}
.invoice-box table tr.item td {
border-bottom: 1px solid #eee;
}
.invoice-box table tr.item.last td {
border-bottom: none;
}
.invoice-box table tr.total td:nth-child(2) {
border-top: 2px solid #eee;
font-weight: bold;
}
@media only screen and (max-width: 600px) {
.invoice-box table tr.top table td {
width: 100%;
display: block;
text-align: center;
}
.invoice-box table tr.information table td {
width: 100%;
display: block;
text-align: center;
}
}
</style>
</head>
<body>
<h1>A simple, clean, and responsive HTML invoice template</h1>
<h3>Because sometimes, all you need is something simple.</h3>
Find the code on <a href="https://github.com/sparksuite/simple-html-invoice-template">GitHub</a>. Licensed under the
<a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>.<br /><br /><br />
<div class="invoice-box">
<table>
<tr class="top">
<td colspan="2">
<table>
<tr>
<td class="title">
<img src="https://github.com/sparksuite/simple-html-invoice-template/blob/master/website/images/logo.png?raw=true" alt="Company logo" style="width: 100%; max-width: 300px" />
</td>
<td>
Invoice #: 123<br />
Created: January 1, 2023<br />
Due: February 1, 2023
</td>
</tr>
</table>
</td>
</tr>
<tr class="information">
<td colspan="2">
<table>
<tr>
<td>
Sparksuite, Inc.<br />
12345 Sunny Road<br />
Sunnyville, TX 12345
</td>
<td>
Acme Corp.<br />
John Doe<br />
john@example.com
</td>
</tr>
</table>
</td>
</tr>
<tr class="heading">
<td>Payment Method</td>
<td>Check #</td>
</tr>
<tr class="details">
<td>Check</td>
<td>1000</td>
</tr>
<tr class="heading">
<td>Item</td>
<td>Price</td>
</tr>
<tr class="item">
<td>Website design</td>
<td>$300.00</td>
</tr>
<tr class="item">
<td>Hosting (3 months)</td>
<td>$75.00</td>
</tr>
<tr class="item last">
<td>Domain name (1 year)</td>
<td>$10.00</td>
</tr>
<tr class="total">
<td></td>
<td>Total: $385.00</td>
</tr>
</table>
</div>
</body>
</html>
อ่านไฟล์ template html
file, err := os.Open("invoice.html")
if err != nil {
log.Fatalln(err)
}
defer file.Close()
stat, err := file.Stat()
if err != nil {
fmt.Println(err)
}
// Read the file into a byte slice
f := make([]byte, stat.Size())
_, err = bufio.NewReader(file).Read(f)
if err != nil && err != io.EOF {
fmt.Println(err)
}
return f
สร้างไฟล์ PDF
options := []chromedp.ExecAllocatorOption{
chromedp.DisableGPU,
chromedp.NoSandbox,
chromedp.Headless,
chromedp.Flag("no-zygote", true),
}
cctx, cancel := chromedp.NewExecAllocator(ctx, options...)
defer cancel()
cctx, cancel = chromedp.NewContext(cctx)
defer cancel()
footerHTML := `
<span style='width: 100%; text-align: right; font-size: 10px;padding-right:24px;'>
<span class='pageNumber'></span>/<span class='totalPages'></span>
</span>`
return chromedp.Run(cctx, chromedp.Tasks{
chromedp.Navigate(url),
chromedp.ActionFunc(func(ctx context.Context) error {
buf, _, err := page.PrintToPDF().
WithPaperHeight(11.7).
WithPaperWidth(8.3).
WithScale(0.62).
WithDisplayHeaderFooter(true).
WithHeaderTemplate(`<style>body { margin: 0; }</style>`).
WithFooterTemplate(footerHTML).
Do(ctx)
if err != nil {
return err
}
*res = buf
return nil
}),
})
บันทึกไฟล์ / response APIs
บันทึกไฟล์
fe, err := os.Create("invoice.pdf")
if err != nil {
log.Println("os.Create", err)
}
defer fe.Close()
_, err = fe.Write(pdfContent)
if err != nil {
log.Println("os.Write", err)
}
response APIs
serve := http.NewServeMux()
serve.Handle("/invoice", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("content-type", "application/pdf")
w.Write(pdfContent)
}))
http.ListenAndServe(":3000", pdfContent)