[Golang]สร้าง PDF ด้วย chromedp

Smalldog124
4 min readJan 20, 2025

--

คุณกำลังหาวิธีสร้าง PDF ของภาษา GO อยู่หรือเปล่า คงใช่แหละไม่งั้นคงไม่เขามาหรอกใช่ไหม 55555
เหมือนกันครับช่วงนี้ระบบต้องสร้างไฟล์ PDF ก็เลยมาเขียนบันทึกไว้สักหน่อย จะไม่อธิบายเยอะน่ะครับหาก
อยากอ่าน code เลยเชิญที่นี้ คลิกนี้

โดนในที่นี้ใช้ lib chromedp

หรือมีอีกตัวที่เคยใช้

ลำดับการทำงาน

  1. สร้างไฟล์ html ที่จะ gen PDF
  2. อ่านไฟล์ template html
  3. สร้างไฟล์ PDF
  4. บันทึกไฟล์ / response APIs
result PDF

สร้างไฟล์ 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)

--

--

No responses yet