gopdf/fpdf_test.go

2925 lines
86 KiB
Go
Raw Permalink Normal View History

2021-03-02 13:27:45 +01:00
/*
* Copyright (c) 2013-2015 Kurt Jung (Gmail: kurt.w.jung)
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package gofpdf_test
import (
"bufio"
"bytes"
"fmt"
"io"
"io/ioutil"
"math"
"math/rand"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"testing"
"time"
"github.com/jung-kurt/gofpdf"
"github.com/jung-kurt/gofpdf/internal/example"
"github.com/jung-kurt/gofpdf/internal/files"
)
func init() {
cleanup()
}
func cleanup() {
filepath.Walk(example.PdfDir(),
func(path string, info os.FileInfo, err error) (reterr error) {
if info.Mode().IsRegular() {
dir, _ := filepath.Split(path)
if "reference" != filepath.Base(dir) {
if len(path) > 3 {
if path[len(path)-4:] == ".pdf" {
os.Remove(path)
}
}
}
}
return
})
}
func TestFpdfImplementPdf(t *testing.T) {
// this will not compile if Fpdf and Tpl
// do not implement Pdf
var _ gofpdf.Pdf = (*gofpdf.Fpdf)(nil)
var _ gofpdf.Pdf = (*gofpdf.Tpl)(nil)
}
// TestPagedTemplate ensures new paged templates work
func TestPagedTemplate(t *testing.T) {
pdf := gofpdf.New("P", "mm", "A4", "")
tpl := pdf.CreateTemplate(func(t *gofpdf.Tpl) {
// this will be the second page, as a page is already
// created by default
t.AddPage()
t.AddPage()
t.AddPage()
})
if tpl.NumPages() != 4 {
t.Fatalf("The template does not have the correct number of pages %d", tpl.NumPages())
}
tplPages := tpl.FromPages()
for x := 0; x < len(tplPages); x++ {
pdf.AddPage()
pdf.UseTemplate(tplPages[x])
}
// get the last template
tpl2, err := tpl.FromPage(tpl.NumPages())
if err != nil {
t.Fatal(err)
}
// the objects should be the exact same, as the
// template will represent the last page by default
// therefore no new id should be set, and the object
// should be the same object
if fmt.Sprintf("%p", tpl2) != fmt.Sprintf("%p", tpl) {
t.Fatal("Template no longer respecting initial template object")
}
}
// TestIssue0116 addresses issue 116 in which library silently fails after
// calling CellFormat when no font has been set.
func TestIssue0116(t *testing.T) {
var pdf *gofpdf.Fpdf
pdf = gofpdf.New("P", "mm", "A4", "")
pdf.AddPage()
pdf.SetFont("Arial", "B", 16)
pdf.Cell(40, 10, "OK")
if pdf.Error() != nil {
t.Fatalf("not expecting error when rendering text")
}
pdf = gofpdf.New("P", "mm", "A4", "")
pdf.AddPage()
pdf.Cell(40, 10, "Not OK") // Font not set
if pdf.Error() == nil {
t.Fatalf("expecting error when rendering text without having set font")
}
}
// TestIssue0193 addresses issue 193 in which the error io.EOF is incorrectly
// assigned to the FPDF instance error.
func TestIssue0193(t *testing.T) {
var png []byte
var pdf *gofpdf.Fpdf
var err error
var rdr *bytes.Reader
png, err = ioutil.ReadFile(example.ImageFile("sweden.png"))
if err == nil {
rdr = bytes.NewReader(png)
pdf = gofpdf.New("P", "mm", "A4", "")
pdf.AddPage()
_ = pdf.RegisterImageOptionsReader("sweden", gofpdf.ImageOptions{ImageType: "png", ReadDpi: true}, rdr)
err = pdf.Error()
}
if err != nil {
t.Fatalf("issue 193 error: %s", err)
}
}
// TestIssue0209SplitLinesEqualMultiCell addresses issue 209
// make SplitLines and MultiCell split at the same place
func TestIssue0209SplitLinesEqualMultiCell(t *testing.T) {
var pdf *gofpdf.Fpdf
pdf = gofpdf.New("P", "mm", "A4", "")
pdf.AddPage()
pdf.SetFont("Arial", "", 8)
// this sentence should not be splited
str := "Guochin Amandine"
lines := pdf.SplitLines([]byte(str), 26)
_, FontSize := pdf.GetFontSize()
y_start := pdf.GetY()
pdf.MultiCell(26, FontSize, str, "", "L", false)
y_end := pdf.GetY()
if len(lines) != 1 {
t.Fatalf("expect SplitLines split in one line")
}
if int(y_end-y_start) != int(FontSize) {
t.Fatalf("expect MultiCell split in one line %.2f != %.2f", y_end-y_start, FontSize)
}
// this sentence should be splited in two lines
str = "Guiochini Amandine"
lines = pdf.SplitLines([]byte(str), 26)
y_start = pdf.GetY()
pdf.MultiCell(26, FontSize, str, "", "L", false)
y_end = pdf.GetY()
if len(lines) != 2 {
t.Fatalf("expect SplitLines split in two lines")
}
if int(y_end-y_start) != int(FontSize*2) {
t.Fatalf("expect MultiCell split in two lines %.2f != %.2f", y_end-y_start, FontSize*2)
}
}
// TestFooterFuncLpi tests to make sure the footer is not call twice and SetFooterFuncLpi can work
// without SetFooterFunc.
func TestFooterFuncLpi(t *testing.T) {
pdf := gofpdf.New("P", "mm", "A4", "")
var (
oldFooterFnc = "oldFooterFnc"
bothPages = "bothPages"
firstPageOnly = "firstPageOnly"
lastPageOnly = "lastPageOnly"
)
// This set just for testing, only set SetFooterFuncLpi.
pdf.SetFooterFunc(func() {
pdf.SetY(-15)
pdf.SetFont("Arial", "I", 8)
pdf.CellFormat(0, 10, oldFooterFnc,
"", 0, "C", false, 0, "")
})
pdf.SetFooterFuncLpi(func(lastPage bool) {
pdf.SetY(-15)
pdf.SetFont("Arial", "I", 8)
pdf.CellFormat(0, 10, bothPages, "", 0, "L", false, 0, "")
if !lastPage {
pdf.CellFormat(0, 10, firstPageOnly, "", 0, "C", false, 0, "")
} else {
pdf.CellFormat(0, 10, lastPageOnly, "", 0, "C", false, 0, "")
}
})
pdf.AddPage()
pdf.SetFont("Arial", "B", 16)
for j := 1; j <= 40; j++ {
pdf.CellFormat(0, 10, fmt.Sprintf("Printing line number %d", j),
"", 1, "", false, 0, "")
}
if pdf.Error() != nil {
t.Fatalf("not expecting error when rendering text")
}
w := &bytes.Buffer{}
if err := pdf.Output(w); err != nil {
t.Errorf("unexpected err: %s", err)
}
b := w.Bytes()
if bytes.Contains(b, []byte(oldFooterFnc)) {
t.Errorf("not expecting %s render on pdf when FooterFncLpi is set", oldFooterFnc)
}
got := bytes.Count(b, []byte("bothPages"))
if got != 2 {
t.Errorf("footer %s should render on two page got:%d", bothPages, got)
}
got = bytes.Count(b, []byte(firstPageOnly))
if got != 1 {
t.Errorf("footer %s should render only on first page got: %d", firstPageOnly, got)
}
got = bytes.Count(b, []byte(lastPageOnly))
if got != 1 {
t.Errorf("footer %s should render only on first page got: %d", lastPageOnly, got)
}
f := bytes.Index(b, []byte(firstPageOnly))
l := bytes.Index(b, []byte(lastPageOnly))
if f > l {
t.Errorf("index %d (%s) should less than index %d (%s)", f, firstPageOnly, l, lastPageOnly)
}
}
type fontResourceType struct {
}
func (f fontResourceType) Open(name string) (rdr io.Reader, err error) {
var buf []byte
buf, err = ioutil.ReadFile(example.FontFile(name))
if err == nil {
rdr = bytes.NewReader(buf)
fmt.Printf("Generalized font loader reading %s\n", name)
}
return
}
// strDelimit converts 'ABCDEFG' to, for example, 'A,BCD,EFG'
func strDelimit(str string, sepstr string, sepcount int) string {
pos := len(str) - sepcount
for pos > 0 {
str = str[:pos] + sepstr + str[pos:]
pos = pos - sepcount
}
return str
}
func loremList() []string {
return []string{
"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod " +
"tempor incididunt ut labore et dolore magna aliqua.",
"Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut " +
"aliquip ex ea commodo consequat.",
"Duis aute irure dolor in reprehenderit in voluptate velit esse cillum " +
"dolore eu fugiat nulla pariatur.",
"Excepteur sint occaecat cupidatat non proident, sunt in culpa qui " +
"officia deserunt mollit anim id est laborum.",
}
}
func lorem() string {
return strings.Join(loremList(), " ")
}
// Example demonstrates the generation of a simple PDF document. Note that
// since only core fonts are used (in this case Arial, a synonym for
// Helvetica), an empty string can be specified for the font directory in the
// call to New(). Note also that the example.Filename() and example.Summary()
// functions belong to a separate, internal package and are not part of the
// gofpdf library. If an error occurs at some point during the construction of
// the document, subsequent method calls exit immediately and the error is
// finally retrieved with the output call where it can be handled by the
// application.
func Example() {
pdf := gofpdf.New(gofpdf.OrientationPortrait, "mm", "A4", "")
pdf.AddPage()
pdf.SetFont("Arial", "B", 16)
pdf.Cell(40, 10, "Hello World!")
fileStr := example.Filename("basic")
err := pdf.OutputFileAndClose(fileStr)
example.Summary(err, fileStr)
// Output:
// Successfully generated pdf/basic.pdf
}
// ExampleFpdf_AddPage demonsrates the generation of headers, footers and page breaks.
func ExampleFpdf_AddPage() {
pdf := gofpdf.New("P", "mm", "A4", "")
pdf.SetTopMargin(30)
pdf.SetHeaderFuncMode(func() {
pdf.Image(example.ImageFile("logo.png"), 10, 6, 30, 0, false, "", 0, "")
pdf.SetY(5)
pdf.SetFont("Arial", "B", 15)
pdf.Cell(80, 0, "")
pdf.CellFormat(30, 10, "Title", "1", 0, "C", false, 0, "")
pdf.Ln(20)
}, true)
pdf.SetFooterFunc(func() {
pdf.SetY(-15)
pdf.SetFont("Arial", "I", 8)
pdf.CellFormat(0, 10, fmt.Sprintf("Page %d/{nb}", pdf.PageNo()),
"", 0, "C", false, 0, "")
})
pdf.AliasNbPages("")
pdf.AddPage()
pdf.SetFont("Times", "", 12)
for j := 1; j <= 40; j++ {
pdf.CellFormat(0, 10, fmt.Sprintf("Printing line number %d", j),
"", 1, "", false, 0, "")
}
fileStr := example.Filename("Fpdf_AddPage")
err := pdf.OutputFileAndClose(fileStr)
example.Summary(err, fileStr)
// Output:
// Successfully generated pdf/Fpdf_AddPage.pdf
}
// ExampleFpdf_MultiCell demonstrates word-wrapping, line justification and
// page-breaking.
func ExampleFpdf_MultiCell() {
pdf := gofpdf.New("P", "mm", "A4", "")
titleStr := "20000 Leagues Under the Seas"
pdf.SetTitle(titleStr, false)
pdf.SetAuthor("Jules Verne", false)
pdf.SetHeaderFunc(func() {
// Arial bold 15
pdf.SetFont("Arial", "B", 15)
// Calculate width of title and position
wd := pdf.GetStringWidth(titleStr) + 6
pdf.SetX((210 - wd) / 2)
// Colors of frame, background and text
pdf.SetDrawColor(0, 80, 180)
pdf.SetFillColor(230, 230, 0)
pdf.SetTextColor(220, 50, 50)
// Thickness of frame (1 mm)
pdf.SetLineWidth(1)
// Title
pdf.CellFormat(wd, 9, titleStr, "1", 1, "C", true, 0, "")
// Line break
pdf.Ln(10)
})
pdf.SetFooterFunc(func() {
// Position at 1.5 cm from bottom
pdf.SetY(-15)
// Arial italic 8
pdf.SetFont("Arial", "I", 8)
// Text color in gray
pdf.SetTextColor(128, 128, 128)
// Page number
pdf.CellFormat(0, 10, fmt.Sprintf("Page %d", pdf.PageNo()),
"", 0, "C", false, 0, "")
})
chapterTitle := func(chapNum int, titleStr string) {
// // Arial 12
pdf.SetFont("Arial", "", 12)
// Background color
pdf.SetFillColor(200, 220, 255)
// Title
pdf.CellFormat(0, 6, fmt.Sprintf("Chapter %d : %s", chapNum, titleStr),
"", 1, "L", true, 0, "")
// Line break
pdf.Ln(4)
}
chapterBody := func(fileStr string) {
// Read text file
txtStr, err := ioutil.ReadFile(fileStr)
if err != nil {
pdf.SetError(err)
}
// Times 12
pdf.SetFont("Times", "", 12)
// Output justified text
pdf.MultiCell(0, 5, string(txtStr), "", "", false)
// Line break
pdf.Ln(-1)
// Mention in italics
pdf.SetFont("", "I", 0)
pdf.Cell(0, 5, "(end of excerpt)")
}
printChapter := func(chapNum int, titleStr, fileStr string) {
pdf.AddPage()
chapterTitle(chapNum, titleStr)
chapterBody(fileStr)
}
printChapter(1, "A RUNAWAY REEF", example.TextFile("20k_c1.txt"))
printChapter(2, "THE PROS AND CONS", example.TextFile("20k_c2.txt"))
fileStr := example.Filename("Fpdf_MultiCell")
err := pdf.OutputFileAndClose(fileStr)
example.Summary(err, fileStr)
// Output:
// Successfully generated pdf/Fpdf_MultiCell.pdf
}
// ExampleFpdf_SetLeftMargin demonstrates the generation of a PDF document that has multiple
// columns. This is accomplished with the SetLeftMargin() and Cell() methods.
func ExampleFpdf_SetLeftMargin() {
var y0 float64
var crrntCol int
pdf := gofpdf.New("P", "mm", "A4", "")
pdf.SetDisplayMode("fullpage", "TwoColumnLeft")
titleStr := "20000 Leagues Under the Seas"
pdf.SetTitle(titleStr, false)
pdf.SetAuthor("Jules Verne", false)
setCol := func(col int) {
// Set position at a given column
crrntCol = col
x := 10.0 + float64(col)*65.0
pdf.SetLeftMargin(x)
pdf.SetX(x)
}
chapterTitle := func(chapNum int, titleStr string) {
// Arial 12
pdf.SetFont("Arial", "", 12)
// Background color
pdf.SetFillColor(200, 220, 255)
// Title
pdf.CellFormat(0, 6, fmt.Sprintf("Chapter %d : %s", chapNum, titleStr),
"", 1, "L", true, 0, "")
// Line break
pdf.Ln(4)
y0 = pdf.GetY()
}
chapterBody := func(fileStr string) {
// Read text file
txtStr, err := ioutil.ReadFile(fileStr)
if err != nil {
pdf.SetError(err)
}
// Font
pdf.SetFont("Times", "", 12)
// Output text in a 6 cm width column
pdf.MultiCell(60, 5, string(txtStr), "", "", false)
pdf.Ln(-1)
// Mention
pdf.SetFont("", "I", 0)
pdf.Cell(0, 5, "(end of excerpt)")
// Go back to first column
setCol(0)
}
printChapter := func(num int, titleStr, fileStr string) {
// Add chapter
pdf.AddPage()
chapterTitle(num, titleStr)
chapterBody(fileStr)
}
pdf.SetAcceptPageBreakFunc(func() bool {
// Method accepting or not automatic page break
if crrntCol < 2 {
// Go to next column
setCol(crrntCol + 1)
// Set ordinate to top
pdf.SetY(y0)
// Keep on page
return false
}
// Go back to first column
setCol(0)
// Page break
return true
})
pdf.SetHeaderFunc(func() {
// Arial bold 15
pdf.SetFont("Arial", "B", 15)
// Calculate width of title and position
wd := pdf.GetStringWidth(titleStr) + 6
pdf.SetX((210 - wd) / 2)
// Colors of frame, background and text
pdf.SetDrawColor(0, 80, 180)
pdf.SetFillColor(230, 230, 0)
pdf.SetTextColor(220, 50, 50)
// Thickness of frame (1 mm)
pdf.SetLineWidth(1)
// Title
pdf.CellFormat(wd, 9, titleStr, "1", 1, "C", true, 0, "")
// Line break
pdf.Ln(10)
// Save ordinate
y0 = pdf.GetY()
})
pdf.SetFooterFunc(func() {
// Position at 1.5 cm from bottom
pdf.SetY(-15)
// Arial italic 8
pdf.SetFont("Arial", "I", 8)
// Text color in gray
pdf.SetTextColor(128, 128, 128)
// Page number
pdf.CellFormat(0, 10, fmt.Sprintf("Page %d", pdf.PageNo()),
"", 0, "C", false, 0, "")
})
printChapter(1, "A RUNAWAY REEF", example.TextFile("20k_c1.txt"))
printChapter(2, "THE PROS AND CONS", example.TextFile("20k_c2.txt"))
fileStr := example.Filename("Fpdf_SetLeftMargin_multicolumn")
err := pdf.OutputFileAndClose(fileStr)
example.Summary(err, fileStr)
// Output:
// Successfully generated pdf/Fpdf_SetLeftMargin_multicolumn.pdf
}
// ExampleFpdf_SplitLines_tables demonstrates word-wrapped table cells
func ExampleFpdf_SplitLines_tables() {
const (
colCount = 3
colWd = 60.0
marginH = 15.0
lineHt = 5.5
cellGap = 2.0
)
// var colStrList [colCount]string
type cellType struct {
str string
list [][]byte
ht float64
}
var (
cellList [colCount]cellType
cell cellType
)
pdf := gofpdf.New("P", "mm", "A4", "") // 210 x 297
header := [colCount]string{"Column A", "Column B", "Column C"}
alignList := [colCount]string{"L", "C", "R"}
strList := loremList()
pdf.SetMargins(marginH, 15, marginH)
pdf.SetFont("Arial", "", 14)
pdf.AddPage()
// Headers
pdf.SetTextColor(224, 224, 224)
pdf.SetFillColor(64, 64, 64)
for colJ := 0; colJ < colCount; colJ++ {
pdf.CellFormat(colWd, 10, header[colJ], "1", 0, "CM", true, 0, "")
}
pdf.Ln(-1)
pdf.SetTextColor(24, 24, 24)
pdf.SetFillColor(255, 255, 255)
// Rows
y := pdf.GetY()
count := 0
for rowJ := 0; rowJ < 2; rowJ++ {
maxHt := lineHt
// Cell height calculation loop
for colJ := 0; colJ < colCount; colJ++ {
count++
if count > len(strList) {
count = 1
}
cell.str = strings.Join(strList[0:count], " ")
cell.list = pdf.SplitLines([]byte(cell.str), colWd-cellGap-cellGap)
cell.ht = float64(len(cell.list)) * lineHt
if cell.ht > maxHt {
maxHt = cell.ht
}
cellList[colJ] = cell
}
// Cell render loop
x := marginH
for colJ := 0; colJ < colCount; colJ++ {
pdf.Rect(x, y, colWd, maxHt+cellGap+cellGap, "D")
cell = cellList[colJ]
cellY := y + cellGap + (maxHt-cell.ht)/2
for splitJ := 0; splitJ < len(cell.list); splitJ++ {
pdf.SetXY(x+cellGap, cellY)
pdf.CellFormat(colWd-cellGap-cellGap, lineHt, string(cell.list[splitJ]), "", 0,
alignList[colJ], false, 0, "")
cellY += lineHt
}
x += colWd
}
y += maxHt + cellGap + cellGap
}
fileStr := example.Filename("Fpdf_SplitLines_tables")
err := pdf.OutputFileAndClose(fileStr)
example.Summary(err, fileStr)
// Output:
// Successfully generated pdf/Fpdf_SplitLines_tables.pdf
}
// ExampleFpdf_CellFormat_tables demonstrates various table styles.
func ExampleFpdf_CellFormat_tables() {
pdf := gofpdf.New("P", "mm", "A4", "")
type countryType struct {
nameStr, capitalStr, areaStr, popStr string
}
countryList := make([]countryType, 0, 8)
header := []string{"Country", "Capital", "Area (sq km)", "Pop. (thousands)"}
loadData := func(fileStr string) {
fl, err := os.Open(fileStr)
if err == nil {
scanner := bufio.NewScanner(fl)
var c countryType
for scanner.Scan() {
// Austria;Vienna;83859;8075
lineStr := scanner.Text()
list := strings.Split(lineStr, ";")
if len(list) == 4 {
c.nameStr = list[0]
c.capitalStr = list[1]
c.areaStr = list[2]
c.popStr = list[3]
countryList = append(countryList, c)
} else {
err = fmt.Errorf("error tokenizing %s", lineStr)
}
}
fl.Close()
if len(countryList) == 0 {
err = fmt.Errorf("error loading data from %s", fileStr)
}
}
if err != nil {
pdf.SetError(err)
}
}
// Simple table
basicTable := func() {
left := (210.0 - 4*40) / 2
pdf.SetX(left)
for _, str := range header {
pdf.CellFormat(40, 7, str, "1", 0, "", false, 0, "")
}
pdf.Ln(-1)
for _, c := range countryList {
pdf.SetX(left)
pdf.CellFormat(40, 6, c.nameStr, "1", 0, "", false, 0, "")
pdf.CellFormat(40, 6, c.capitalStr, "1", 0, "", false, 0, "")
pdf.CellFormat(40, 6, c.areaStr, "1", 0, "", false, 0, "")
pdf.CellFormat(40, 6, c.popStr, "1", 0, "", false, 0, "")
pdf.Ln(-1)
}
}
// Better table
improvedTable := func() {
// Column widths
w := []float64{40.0, 35.0, 40.0, 45.0}
wSum := 0.0
for _, v := range w {
wSum += v
}
left := (210 - wSum) / 2
// Header
pdf.SetX(left)
for j, str := range header {
pdf.CellFormat(w[j], 7, str, "1", 0, "C", false, 0, "")
}
pdf.Ln(-1)
// Data
for _, c := range countryList {
pdf.SetX(left)
pdf.CellFormat(w[0], 6, c.nameStr, "LR", 0, "", false, 0, "")
pdf.CellFormat(w[1], 6, c.capitalStr, "LR", 0, "", false, 0, "")
pdf.CellFormat(w[2], 6, strDelimit(c.areaStr, ",", 3),
"LR", 0, "R", false, 0, "")
pdf.CellFormat(w[3], 6, strDelimit(c.popStr, ",", 3),
"LR", 0, "R", false, 0, "")
pdf.Ln(-1)
}
pdf.SetX(left)
pdf.CellFormat(wSum, 0, "", "T", 0, "", false, 0, "")
}
// Colored table
fancyTable := func() {
// Colors, line width and bold font
pdf.SetFillColor(255, 0, 0)
pdf.SetTextColor(255, 255, 255)
pdf.SetDrawColor(128, 0, 0)
pdf.SetLineWidth(.3)
pdf.SetFont("", "B", 0)
// Header
w := []float64{40, 35, 40, 45}
wSum := 0.0
for _, v := range w {
wSum += v
}
left := (210 - wSum) / 2
pdf.SetX(left)
for j, str := range header {
pdf.CellFormat(w[j], 7, str, "1", 0, "C", true, 0, "")
}
pdf.Ln(-1)
// Color and font restoration
pdf.SetFillColor(224, 235, 255)
pdf.SetTextColor(0, 0, 0)
pdf.SetFont("", "", 0)
// Data
fill := false
for _, c := range countryList {
pdf.SetX(left)
pdf.CellFormat(w[0], 6, c.nameStr, "LR", 0, "", fill, 0, "")
pdf.CellFormat(w[1], 6, c.capitalStr, "LR", 0, "", fill, 0, "")
pdf.CellFormat(w[2], 6, strDelimit(c.areaStr, ",", 3),
"LR", 0, "R", fill, 0, "")
pdf.CellFormat(w[3], 6, strDelimit(c.popStr, ",", 3),
"LR", 0, "R", fill, 0, "")
pdf.Ln(-1)
fill = !fill
}
pdf.SetX(left)
pdf.CellFormat(wSum, 0, "", "T", 0, "", false, 0, "")
}
loadData(example.TextFile("countries.txt"))
pdf.SetFont("Arial", "", 14)
pdf.AddPage()
basicTable()
pdf.AddPage()
improvedTable()
pdf.AddPage()
fancyTable()
fileStr := example.Filename("Fpdf_CellFormat_tables")
err := pdf.OutputFileAndClose(fileStr)
example.Summary(err, fileStr)
// Output:
// Successfully generated pdf/Fpdf_CellFormat_tables.pdf
}
// ExampleFpdf_HTMLBasicNew demonstrates internal and external links with and without basic
// HTML.
func ExampleFpdf_HTMLBasicNew() {
pdf := gofpdf.New("P", "mm", "A4", "")
// First page: manual local link
pdf.AddPage()
pdf.SetFont("Helvetica", "", 20)
_, lineHt := pdf.GetFontSize()
pdf.Write(lineHt, "To find out what's new in this tutorial, click ")
pdf.SetFont("", "U", 0)
link := pdf.AddLink()
pdf.WriteLinkID(lineHt, "here", link)
pdf.SetFont("", "", 0)
// Second page: image link and basic HTML with link
pdf.AddPage()
pdf.SetLink(link, 0, -1)
pdf.Image(example.ImageFile("logo.png"), 10, 12, 30, 0, false, "", 0, "http://www.fpdf.org")
pdf.SetLeftMargin(45)
pdf.SetFontSize(14)
_, lineHt = pdf.GetFontSize()
htmlStr := `You can now easily print text mixing different styles: <b>bold</b>, ` +
`<i>italic</i>, <u>underlined</u>, or <b><i><u>all at once</u></i></b>!<br><br>` +
`<center>You can also center text.</center>` +
`<right>Or align it to the right.</right>` +
`You can also insert links on text, such as ` +
`<a href="http://www.fpdf.org">www.fpdf.org</a>, or on an image: click on the logo.`
html := pdf.HTMLBasicNew()
html.Write(lineHt, htmlStr)
fileStr := example.Filename("Fpdf_HTMLBasicNew")
err := pdf.OutputFileAndClose(fileStr)
example.Summary(err, fileStr)
// Output:
// Successfully generated pdf/Fpdf_HTMLBasicNew.pdf
}
// ExampleFpdf_AddFont demonstrates the use of a non-standard font.
func ExampleFpdf_AddFont() {
pdf := gofpdf.New("P", "mm", "A4", example.FontDir())
pdf.AddFont("Calligrapher", "", "calligra.json")
pdf.AddPage()
pdf.SetFont("Calligrapher", "", 35)
pdf.Cell(0, 10, "Enjoy new fonts with FPDF!")
fileStr := example.Filename("Fpdf_AddFont")
err := pdf.OutputFileAndClose(fileStr)
example.Summary(err, fileStr)
// Output:
// Successfully generated pdf/Fpdf_AddFont.pdf
}
// ExampleFpdf_WriteAligned demonstrates how to align text with the Write function.
func ExampleFpdf_WriteAligned() {
pdf := gofpdf.New("P", "mm", "A4", example.FontDir())
pdf.SetLeftMargin(50.0)
pdf.SetRightMargin(50.0)
pdf.AddPage()
pdf.SetFont("Helvetica", "", 12)
pdf.WriteAligned(0, 35, "This text is the default alignment, Left", "")
pdf.Ln(35)
pdf.WriteAligned(0, 35, "This text is aligned Left", "L")
pdf.Ln(35)
pdf.WriteAligned(0, 35, "This text is aligned Center", "C")
pdf.Ln(35)
pdf.WriteAligned(0, 35, "This text is aligned Right", "R")
pdf.Ln(35)
line := "This can by used to write justified text"
leftMargin, _, rightMargin, _ := pdf.GetMargins()
pageWidth, _ := pdf.GetPageSize()
pageWidth -= leftMargin + rightMargin
pdf.SetWordSpacing((pageWidth - pdf.GetStringWidth(line)) / float64(strings.Count(line, " ")))
pdf.WriteAligned(pageWidth, 35, line, "L")
fileStr := example.Filename("Fpdf_WriteAligned")
err := pdf.OutputFileAndClose(fileStr)
example.Summary(err, fileStr)
// Output:
// Successfully generated pdf/Fpdf_WriteAligned.pdf
}
// ExampleFpdf_Image demonstrates how images are included in documents.
func ExampleFpdf_Image() {
pdf := gofpdf.New("P", "mm", "A4", "")
pdf.AddPage()
pdf.SetFont("Arial", "", 11)
pdf.Image(example.ImageFile("logo.png"), 10, 10, 30, 0, false, "", 0, "")
pdf.Text(50, 20, "logo.png")
pdf.Image(example.ImageFile("logo.gif"), 10, 40, 30, 0, false, "", 0, "")
pdf.Text(50, 50, "logo.gif")
pdf.Image(example.ImageFile("logo-gray.png"), 10, 70, 30, 0, false, "", 0, "")
pdf.Text(50, 80, "logo-gray.png")
pdf.Image(example.ImageFile("logo-rgb.png"), 10, 100, 30, 0, false, "", 0, "")
pdf.Text(50, 110, "logo-rgb.png")
pdf.Image(example.ImageFile("logo.jpg"), 10, 130, 30, 0, false, "", 0, "")
pdf.Text(50, 140, "logo.jpg")
fileStr := example.Filename("Fpdf_Image")
err := pdf.OutputFileAndClose(fileStr)
example.Summary(err, fileStr)
// Output:
// Successfully generated pdf/Fpdf_Image.pdf
}
// ExampleFpdf_ImageOptions demonstrates how the AllowNegativePosition field of the
// ImageOption struct can be used to affect horizontal image placement.
func ExampleFpdf_ImageOptions() {
var opt gofpdf.ImageOptions
pdf := gofpdf.New("P", "mm", "A4", "")
pdf.AddPage()
pdf.SetFont("Arial", "", 11)
pdf.SetX(60)
opt.ImageType = "png"
pdf.ImageOptions(example.ImageFile("logo.png"), -10, 10, 30, 0, false, opt, 0, "")
opt.AllowNegativePosition = true
pdf.ImageOptions(example.ImageFile("logo.png"), -10, 50, 30, 0, false, opt, 0, "")
fileStr := example.Filename("Fpdf_ImageOptions")
err := pdf.OutputFileAndClose(fileStr)
example.Summary(err, fileStr)
// Output:
// Successfully generated pdf/Fpdf_ImageOptions.pdf
}
// ExampleFpdf_RegisterImageOptionsReader demonstrates how to load an image
// from a io.Reader (in this case, a file) and register it with options.
func ExampleFpdf_RegisterImageOptionsReader() {
var (
opt gofpdf.ImageOptions
pdfStr string
fl *os.File
err error
)
pdfStr = example.Filename("Fpdf_RegisterImageOptionsReader")
pdf := gofpdf.New("P", "mm", "A4", "")
pdf.AddPage()
pdf.SetFont("Arial", "", 11)
fl, err = os.Open(example.ImageFile("logo.png"))
if err == nil {
opt.ImageType = "png"
opt.AllowNegativePosition = true
_ = pdf.RegisterImageOptionsReader("logo", opt, fl)
fl.Close()
for x := -20.0; x <= 40.0; x += 5 {
pdf.ImageOptions("logo", x, x+30, 0, 0, false, opt, 0, "")
}
err = pdf.OutputFileAndClose(pdfStr)
}
example.Summary(err, pdfStr)
// Output:
// Successfully generated pdf/Fpdf_RegisterImageOptionsReader.pdf
}
// This example demonstrates Landscape mode with images.
func ExampleFpdf_SetAcceptPageBreakFunc() {
var y0 float64
var crrntCol int
loremStr := lorem()
pdf := gofpdf.New("L", "mm", "A4", "")
const (
pageWd = 297.0 // A4 210.0 x 297.0
margin = 10.0
gutter = 4
colNum = 3
colWd = (pageWd - 2*margin - (colNum-1)*gutter) / colNum
)
setCol := func(col int) {
crrntCol = col
x := margin + float64(col)*(colWd+gutter)
pdf.SetLeftMargin(x)
pdf.SetX(x)
}
pdf.SetHeaderFunc(func() {
titleStr := "gofpdf"
pdf.SetFont("Helvetica", "B", 48)
wd := pdf.GetStringWidth(titleStr) + 6
pdf.SetX((pageWd - wd) / 2)
pdf.SetTextColor(128, 128, 160)
pdf.Write(12, titleStr[:2])
pdf.SetTextColor(128, 128, 128)
pdf.Write(12, titleStr[2:])
pdf.Ln(20)
y0 = pdf.GetY()
})
pdf.SetAcceptPageBreakFunc(func() bool {
if crrntCol < colNum-1 {
setCol(crrntCol + 1)
pdf.SetY(y0)
// Start new column, not new page
return false
}
setCol(0)
return true
})
pdf.AddPage()
pdf.SetFont("Times", "", 12)
for j := 0; j < 20; j++ {
if j == 1 {
pdf.Image(example.ImageFile("fpdf.png"), -1, 0, colWd, 0, true, "", 0, "")
} else if j == 5 {
pdf.Image(example.ImageFile("golang-gopher.png"),
-1, 0, colWd, 0, true, "", 0, "")
}
pdf.MultiCell(colWd, 5, loremStr, "", "", false)
pdf.Ln(-1)
}
fileStr := example.Filename("Fpdf_SetAcceptPageBreakFunc_landscape")
err := pdf.OutputFileAndClose(fileStr)
example.Summary(err, fileStr)
// Output:
// Successfully generated pdf/Fpdf_SetAcceptPageBreakFunc_landscape.pdf
}
// This example tests corner cases as reported by the gocov tool.
func ExampleFpdf_SetKeywords() {
var err error
fileStr := example.Filename("Fpdf_SetKeywords")
err = gofpdf.MakeFont(example.FontFile("CalligrapherRegular.pfb"),
example.FontFile("cp1252.map"), example.FontDir(), nil, true)
if err == nil {
pdf := gofpdf.New("", "", "", "")
pdf.SetFontLocation(example.FontDir())
pdf.SetTitle("世界", true)
pdf.SetAuthor("世界", true)
pdf.SetSubject("世界", true)
pdf.SetCreator("世界", true)
pdf.SetKeywords("世界", true)
pdf.AddFont("Calligrapher", "", "CalligrapherRegular.json")
pdf.AddPage()
pdf.SetFont("Calligrapher", "", 16)
pdf.Writef(5, "\x95 %s \x95", pdf)
err = pdf.OutputFileAndClose(fileStr)
}
example.Summary(err, fileStr)
// Output:
// Successfully generated pdf/Fpdf_SetKeywords.pdf
}
// ExampleFpdf_Circle demonstrates the construction of various geometric figures,
func ExampleFpdf_Circle() {
const (
thin = 0.2
thick = 3.0
)
pdf := gofpdf.New("", "", "", "")
pdf.SetFont("Helvetica", "", 12)
pdf.SetFillColor(200, 200, 220)
pdf.AddPage()
y := 15.0
pdf.Text(10, y, "Circles")
pdf.SetFillColor(200, 200, 220)
pdf.SetLineWidth(thin)
pdf.Circle(20, y+15, 10, "D")
pdf.Circle(45, y+15, 10, "F")
pdf.Circle(70, y+15, 10, "FD")
pdf.SetLineWidth(thick)
pdf.Circle(95, y+15, 10, "FD")
pdf.SetLineWidth(thin)
y += 40.0
pdf.Text(10, y, "Ellipses")
pdf.SetFillColor(220, 200, 200)
pdf.Ellipse(30, y+15, 20, 10, 0, "D")
pdf.Ellipse(75, y+15, 20, 10, 0, "F")
pdf.Ellipse(120, y+15, 20, 10, 0, "FD")
pdf.SetLineWidth(thick)
pdf.Ellipse(165, y+15, 20, 10, 0, "FD")
pdf.SetLineWidth(thin)
y += 40.0
pdf.Text(10, y, "Curves (quadratic)")
pdf.SetFillColor(220, 220, 200)
pdf.Curve(10, y+30, 15, y-20, 40, y+30, "D")
pdf.Curve(45, y+30, 50, y-20, 75, y+30, "F")
pdf.Curve(80, y+30, 85, y-20, 110, y+30, "FD")
pdf.SetLineWidth(thick)
pdf.Curve(115, y+30, 120, y-20, 145, y+30, "FD")
pdf.SetLineCapStyle("round")
pdf.Curve(150, y+30, 155, y-20, 180, y+30, "FD")
pdf.SetLineWidth(thin)
pdf.SetLineCapStyle("butt")
y += 40.0
pdf.Text(10, y, "Curves (cubic)")
pdf.SetFillColor(220, 200, 220)
pdf.CurveBezierCubic(10, y+30, 15, y-20, 10, y+30, 40, y+30, "D")
pdf.CurveBezierCubic(45, y+30, 50, y-20, 45, y+30, 75, y+30, "F")
pdf.CurveBezierCubic(80, y+30, 85, y-20, 80, y+30, 110, y+30, "FD")
pdf.SetLineWidth(thick)
pdf.CurveBezierCubic(115, y+30, 120, y-20, 115, y+30, 145, y+30, "FD")
pdf.SetLineCapStyle("round")
pdf.CurveBezierCubic(150, y+30, 155, y-20, 150, y+30, 180, y+30, "FD")
pdf.SetLineWidth(thin)
pdf.SetLineCapStyle("butt")
y += 40.0
pdf.Text(10, y, "Arcs")
pdf.SetFillColor(200, 220, 220)
pdf.SetLineWidth(thick)
pdf.Arc(45, y+35, 20, 10, 0, 0, 180, "FD")
pdf.SetLineWidth(thin)
pdf.Arc(45, y+35, 25, 15, 0, 90, 270, "D")
pdf.SetLineWidth(thick)
pdf.Arc(45, y+35, 30, 20, 0, 0, 360, "D")
pdf.SetLineCapStyle("round")
pdf.Arc(135, y+35, 20, 10, 135, 0, 180, "FD")
pdf.SetLineWidth(thin)
pdf.Arc(135, y+35, 25, 15, 135, 90, 270, "D")
pdf.SetLineWidth(thick)
pdf.Arc(135, y+35, 30, 20, 135, 0, 360, "D")
pdf.SetLineWidth(thin)
pdf.SetLineCapStyle("butt")
fileStr := example.Filename("Fpdf_Circle_figures")
err := pdf.OutputFileAndClose(fileStr)
example.Summary(err, fileStr)
// Output:
// Successfully generated pdf/Fpdf_Circle_figures.pdf
}
// ExampleFpdf_SetAlpha demonstrates alpha transparency.
func ExampleFpdf_SetAlpha() {
const (
gapX = 10.0
gapY = 9.0
rectW = 40.0
rectH = 58.0
pageW = 210
pageH = 297
)
modeList := []string{"Normal", "Multiply", "Screen", "Overlay",
"Darken", "Lighten", "ColorDodge", "ColorBurn", "HardLight", "SoftLight",
"Difference", "Exclusion", "Hue", "Saturation", "Color", "Luminosity"}
pdf := gofpdf.New("", "", "", "")
pdf.SetLineWidth(2)
pdf.SetAutoPageBreak(false, 0)
pdf.AddPage()
pdf.SetFont("Helvetica", "", 18)
pdf.SetXY(0, gapY)
pdf.SetTextColor(0, 0, 0)
pdf.CellFormat(pageW, gapY, "Alpha Blending Modes", "", 0, "C", false, 0, "")
j := 0
y := 3 * gapY
for col := 0; col < 4; col++ {
x := gapX
for row := 0; row < 4; row++ {
pdf.Rect(x, y, rectW, rectH, "D")