github.com/phpdave11/gofpdf@v1.4.2/fpdf_test.go (about)

     1  /*
     2   * Copyright (c) 2013-2015 Kurt Jung (Gmail: kurt.w.jung)
     3   *
     4   * Permission to use, copy, modify, and distribute this software for any
     5   * purpose with or without fee is hereby granted, provided that the above
     6   * copyright notice and this permission notice appear in all copies.
     7   *
     8   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     9   * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
    10   * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
    11   * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    12   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
    13   * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    14   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    15   */
    16  
    17  package gofpdf_test
    18  
    19  import (
    20  	"bufio"
    21  	"bytes"
    22  	"fmt"
    23  	"io"
    24  	"io/ioutil"
    25  	"math"
    26  	"math/rand"
    27  	"net/http"
    28  	"os"
    29  	"path/filepath"
    30  	"strconv"
    31  	"strings"
    32  	"testing"
    33  	"time"
    34  
    35  	"github.com/phpdave11/gofpdf"
    36  	"github.com/phpdave11/gofpdf/internal/example"
    37  	"github.com/phpdave11/gofpdf/internal/files"
    38  )
    39  
    40  func init() {
    41  	cleanup()
    42  }
    43  
    44  func cleanup() {
    45  	filepath.Walk(example.PdfDir(),
    46  		func(path string, info os.FileInfo, err error) (reterr error) {
    47  			if info.Mode().IsRegular() {
    48  				dir, _ := filepath.Split(path)
    49  				if "reference" != filepath.Base(dir) {
    50  					if len(path) > 3 {
    51  						if path[len(path)-4:] == ".pdf" {
    52  							os.Remove(path)
    53  						}
    54  					}
    55  				}
    56  			}
    57  			return
    58  		})
    59  }
    60  
    61  func TestFpdfImplementPdf(t *testing.T) {
    62  	// this will not compile if Fpdf and Tpl
    63  	// do not implement Pdf
    64  	var _ gofpdf.Pdf = (*gofpdf.Fpdf)(nil)
    65  	var _ gofpdf.Pdf = (*gofpdf.Tpl)(nil)
    66  }
    67  
    68  // TestPagedTemplate ensures new paged templates work
    69  func TestPagedTemplate(t *testing.T) {
    70  	pdf := gofpdf.New("P", "mm", "A4", "")
    71  	tpl := pdf.CreateTemplate(func(t *gofpdf.Tpl) {
    72  		// this will be the second page, as a page is already
    73  		// created by default
    74  		t.AddPage()
    75  		t.AddPage()
    76  		t.AddPage()
    77  	})
    78  
    79  	if tpl.NumPages() != 4 {
    80  		t.Fatalf("The template does not have the correct number of pages %d", tpl.NumPages())
    81  	}
    82  
    83  	tplPages := tpl.FromPages()
    84  	for x := 0; x < len(tplPages); x++ {
    85  		pdf.AddPage()
    86  		pdf.UseTemplate(tplPages[x])
    87  	}
    88  
    89  	// get the last template
    90  	tpl2, err := tpl.FromPage(tpl.NumPages())
    91  	if err != nil {
    92  		t.Fatal(err)
    93  	}
    94  
    95  	// the objects should be the exact same, as the
    96  	// template will represent the last page by default
    97  	// therefore no new id should be set, and the object
    98  	// should be the same object
    99  	if fmt.Sprintf("%p", tpl2) != fmt.Sprintf("%p", tpl) {
   100  		t.Fatal("Template no longer respecting initial template object")
   101  	}
   102  }
   103  
   104  // TestIssue0116 addresses issue 116 in which library silently fails after
   105  // calling CellFormat when no font has been set.
   106  func TestIssue0116(t *testing.T) {
   107  	var pdf *gofpdf.Fpdf
   108  
   109  	pdf = gofpdf.New("P", "mm", "A4", "")
   110  	pdf.AddPage()
   111  	pdf.SetFont("Arial", "B", 16)
   112  	pdf.Cell(40, 10, "OK")
   113  	if pdf.Error() != nil {
   114  		t.Fatalf("not expecting error when rendering text")
   115  	}
   116  
   117  	pdf = gofpdf.New("P", "mm", "A4", "")
   118  	pdf.AddPage()
   119  	pdf.Cell(40, 10, "Not OK") // Font not set
   120  	if pdf.Error() == nil {
   121  		t.Fatalf("expecting error when rendering text without having set font")
   122  	}
   123  }
   124  
   125  // TestIssue0193 addresses issue 193 in which the error io.EOF is incorrectly
   126  // assigned to the FPDF instance error.
   127  func TestIssue0193(t *testing.T) {
   128  	var png []byte
   129  	var pdf *gofpdf.Fpdf
   130  	var err error
   131  	var rdr *bytes.Reader
   132  
   133  	png, err = ioutil.ReadFile(example.ImageFile("sweden.png"))
   134  	if err == nil {
   135  		rdr = bytes.NewReader(png)
   136  		pdf = gofpdf.New("P", "mm", "A4", "")
   137  		pdf.AddPage()
   138  		_ = pdf.RegisterImageOptionsReader("sweden", gofpdf.ImageOptions{ImageType: "png", ReadDpi: true}, rdr)
   139  		err = pdf.Error()
   140  	}
   141  	if err != nil {
   142  		t.Fatalf("issue 193 error: %s", err)
   143  	}
   144  
   145  }
   146  
   147  // TestIssue0209SplitLinesEqualMultiCell addresses issue 209
   148  // make SplitLines and MultiCell split at the same place
   149  func TestIssue0209SplitLinesEqualMultiCell(t *testing.T) {
   150  	var pdf *gofpdf.Fpdf
   151  
   152  	pdf = gofpdf.New("P", "mm", "A4", "")
   153  	pdf.AddPage()
   154  	pdf.SetFont("Arial", "", 8)
   155  	// this sentence should not be splited
   156  	str := "Guochin Amandine"
   157  	lines := pdf.SplitLines([]byte(str), 26)
   158  	_, FontSize := pdf.GetFontSize()
   159  	y_start := pdf.GetY()
   160  	pdf.MultiCell(26, FontSize, str, "", "L", false)
   161  	y_end := pdf.GetY()
   162  
   163  	if len(lines) != 1 {
   164  		t.Fatalf("expect SplitLines split in one line")
   165  	}
   166  	if int(y_end-y_start) != int(FontSize) {
   167  		t.Fatalf("expect MultiCell split in one line %.2f != %.2f", y_end-y_start, FontSize)
   168  	}
   169  
   170  	// this sentence should be splited in two lines
   171  	str = "Guiochini Amandine"
   172  	lines = pdf.SplitLines([]byte(str), 26)
   173  	y_start = pdf.GetY()
   174  	pdf.MultiCell(26, FontSize, str, "", "L", false)
   175  	y_end = pdf.GetY()
   176  
   177  	if len(lines) != 2 {
   178  		t.Fatalf("expect SplitLines split in two lines")
   179  	}
   180  	if int(y_end-y_start) != int(FontSize*2) {
   181  		t.Fatalf("expect MultiCell split in two lines %.2f != %.2f", y_end-y_start, FontSize*2)
   182  	}
   183  }
   184  
   185  // TestFooterFuncLpi tests to make sure the footer is not call twice and SetFooterFuncLpi can work
   186  // without SetFooterFunc.
   187  func TestFooterFuncLpi(t *testing.T) {
   188  	pdf := gofpdf.New("P", "mm", "A4", "")
   189  	var (
   190  		oldFooterFnc  = "oldFooterFnc"
   191  		bothPages     = "bothPages"
   192  		firstPageOnly = "firstPageOnly"
   193  		lastPageOnly  = "lastPageOnly"
   194  	)
   195  
   196  	// This set just for testing, only set SetFooterFuncLpi.
   197  	pdf.SetFooterFunc(func() {
   198  		pdf.SetY(-15)
   199  		pdf.SetFont("Arial", "I", 8)
   200  		pdf.CellFormat(0, 10, oldFooterFnc,
   201  			"", 0, "C", false, 0, "")
   202  	})
   203  	pdf.SetFooterFuncLpi(func(lastPage bool) {
   204  		pdf.SetY(-15)
   205  		pdf.SetFont("Arial", "I", 8)
   206  		pdf.CellFormat(0, 10, bothPages, "", 0, "L", false, 0, "")
   207  		if !lastPage {
   208  			pdf.CellFormat(0, 10, firstPageOnly, "", 0, "C", false, 0, "")
   209  		} else {
   210  			pdf.CellFormat(0, 10, lastPageOnly, "", 0, "C", false, 0, "")
   211  		}
   212  	})
   213  	pdf.AddPage()
   214  	pdf.SetFont("Arial", "B", 16)
   215  	for j := 1; j <= 40; j++ {
   216  		pdf.CellFormat(0, 10, fmt.Sprintf("Printing line number %d", j),
   217  			"", 1, "", false, 0, "")
   218  	}
   219  	if pdf.Error() != nil {
   220  		t.Fatalf("not expecting error when rendering text")
   221  	}
   222  	w := &bytes.Buffer{}
   223  	if err := pdf.Output(w); err != nil {
   224  		t.Errorf("unexpected err: %s", err)
   225  	}
   226  	b := w.Bytes()
   227  	if bytes.Contains(b, []byte(oldFooterFnc)) {
   228  		t.Errorf("not expecting %s render on pdf when FooterFncLpi is set", oldFooterFnc)
   229  	}
   230  	got := bytes.Count(b, []byte("bothPages"))
   231  	if got != 2 {
   232  		t.Errorf("footer %s should render on two page got:%d", bothPages, got)
   233  	}
   234  	got = bytes.Count(b, []byte(firstPageOnly))
   235  	if got != 1 {
   236  		t.Errorf("footer %s should render only on first page got: %d", firstPageOnly, got)
   237  	}
   238  	got = bytes.Count(b, []byte(lastPageOnly))
   239  	if got != 1 {
   240  		t.Errorf("footer %s should render only on first page got: %d", lastPageOnly, got)
   241  	}
   242  	f := bytes.Index(b, []byte(firstPageOnly))
   243  	l := bytes.Index(b, []byte(lastPageOnly))
   244  	if f > l {
   245  		t.Errorf("index %d (%s) should less than index %d (%s)", f, firstPageOnly, l, lastPageOnly)
   246  	}
   247  }
   248  
   249  type fontResourceType struct {
   250  }
   251  
   252  func (f fontResourceType) Open(name string) (rdr io.Reader, err error) {
   253  	var buf []byte
   254  	buf, err = ioutil.ReadFile(example.FontFile(name))
   255  	if err == nil {
   256  		rdr = bytes.NewReader(buf)
   257  		fmt.Printf("Generalized font loader reading %s\n", name)
   258  	}
   259  	return
   260  }
   261  
   262  // strDelimit converts 'ABCDEFG' to, for example, 'A,BCD,EFG'
   263  func strDelimit(str string, sepstr string, sepcount int) string {
   264  	pos := len(str) - sepcount
   265  	for pos > 0 {
   266  		str = str[:pos] + sepstr + str[pos:]
   267  		pos = pos - sepcount
   268  	}
   269  	return str
   270  }
   271  
   272  func loremList() []string {
   273  	return []string{
   274  		"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod " +
   275  			"tempor incididunt ut labore et dolore magna aliqua.",
   276  		"Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut " +
   277  			"aliquip ex ea commodo consequat.",
   278  		"Duis aute irure dolor in reprehenderit in voluptate velit esse cillum " +
   279  			"dolore eu fugiat nulla pariatur.",
   280  		"Excepteur sint occaecat cupidatat non proident, sunt in culpa qui " +
   281  			"officia deserunt mollit anim id est laborum.",
   282  	}
   283  }
   284  
   285  func lorem() string {
   286  	return strings.Join(loremList(), " ")
   287  }
   288  
   289  // Example demonstrates the generation of a simple PDF document. Note that
   290  // since only core fonts are used (in this case Arial, a synonym for
   291  // Helvetica), an empty string can be specified for the font directory in the
   292  // call to New(). Note also that the example.Filename() and example.Summary()
   293  // functions belong to a separate, internal package and are not part of the
   294  // gofpdf library. If an error occurs at some point during the construction of
   295  // the document, subsequent method calls exit immediately and the error is
   296  // finally retrieved with the output call where it can be handled by the
   297  // application.
   298  func Example() {
   299  	pdf := gofpdf.New(gofpdf.OrientationPortrait, "mm", "A4", "")
   300  	pdf.AddPage()
   301  	pdf.SetFont("Arial", "B", 16)
   302  	pdf.Cell(40, 10, "Hello World!")
   303  	fileStr := example.Filename("basic")
   304  	err := pdf.OutputFileAndClose(fileStr)
   305  	example.Summary(err, fileStr)
   306  	// Output:
   307  	// Successfully generated pdf/basic.pdf
   308  }
   309  
   310  // ExampleFpdf_AddPage demonsrates the generation of headers, footers and page breaks.
   311  func ExampleFpdf_AddPage() {
   312  	pdf := gofpdf.New("P", "mm", "A4", "")
   313  	pdf.SetTopMargin(30)
   314  	pdf.SetHeaderFuncMode(func() {
   315  		pdf.Image(example.ImageFile("logo.png"), 10, 6, 30, 0, false, "", 0, "")
   316  		pdf.SetY(5)
   317  		pdf.SetFont("Arial", "B", 15)
   318  		pdf.Cell(80, 0, "")
   319  		pdf.CellFormat(30, 10, "Title", "1", 0, "C", false, 0, "")
   320  		pdf.Ln(20)
   321  	}, true)
   322  	pdf.SetFooterFunc(func() {
   323  		pdf.SetY(-15)
   324  		pdf.SetFont("Arial", "I", 8)
   325  		pdf.CellFormat(0, 10, fmt.Sprintf("Page %d/{nb}", pdf.PageNo()),
   326  			"", 0, "C", false, 0, "")
   327  	})
   328  	pdf.AliasNbPages("")
   329  	pdf.AddPage()
   330  	pdf.SetFont("Times", "", 12)
   331  	for j := 1; j <= 40; j++ {
   332  		pdf.CellFormat(0, 10, fmt.Sprintf("Printing line number %d", j),
   333  			"", 1, "", false, 0, "")
   334  	}
   335  	fileStr := example.Filename("Fpdf_AddPage")
   336  	err := pdf.OutputFileAndClose(fileStr)
   337  	example.Summary(err, fileStr)
   338  	// Output:
   339  	// Successfully generated pdf/Fpdf_AddPage.pdf
   340  }
   341  
   342  // ExampleFpdf_MultiCell demonstrates word-wrapping, line justification and
   343  // page-breaking.
   344  func ExampleFpdf_MultiCell() {
   345  	pdf := gofpdf.New("P", "mm", "A4", "")
   346  	titleStr := "20000 Leagues Under the Seas"
   347  	pdf.SetTitle(titleStr, false)
   348  	pdf.SetAuthor("Jules Verne", false)
   349  	pdf.SetHeaderFunc(func() {
   350  		// Arial bold 15
   351  		pdf.SetFont("Arial", "B", 15)
   352  		// Calculate width of title and position
   353  		wd := pdf.GetStringWidth(titleStr) + 6
   354  		pdf.SetX((210 - wd) / 2)
   355  		// Colors of frame, background and text
   356  		pdf.SetDrawColor(0, 80, 180)
   357  		pdf.SetFillColor(230, 230, 0)
   358  		pdf.SetTextColor(220, 50, 50)
   359  		// Thickness of frame (1 mm)
   360  		pdf.SetLineWidth(1)
   361  		// Title
   362  		pdf.CellFormat(wd, 9, titleStr, "1", 1, "C", true, 0, "")
   363  		// Line break
   364  		pdf.Ln(10)
   365  	})
   366  	pdf.SetFooterFunc(func() {
   367  		// Position at 1.5 cm from bottom
   368  		pdf.SetY(-15)
   369  		// Arial italic 8
   370  		pdf.SetFont("Arial", "I", 8)
   371  		// Text color in gray
   372  		pdf.SetTextColor(128, 128, 128)
   373  		// Page number
   374  		pdf.CellFormat(0, 10, fmt.Sprintf("Page %d", pdf.PageNo()),
   375  			"", 0, "C", false, 0, "")
   376  	})
   377  	chapterTitle := func(chapNum int, titleStr string) {
   378  		// 	// Arial 12
   379  		pdf.SetFont("Arial", "", 12)
   380  		// Background color
   381  		pdf.SetFillColor(200, 220, 255)
   382  		// Title
   383  		pdf.CellFormat(0, 6, fmt.Sprintf("Chapter %d : %s", chapNum, titleStr),
   384  			"", 1, "L", true, 0, "")
   385  		// Line break
   386  		pdf.Ln(4)
   387  	}
   388  	chapterBody := func(fileStr string) {
   389  		// Read text file
   390  		txtStr, err := ioutil.ReadFile(fileStr)
   391  		if err != nil {
   392  			pdf.SetError(err)
   393  		}
   394  		// Times 12
   395  		pdf.SetFont("Times", "", 12)
   396  		// Output justified text
   397  		pdf.MultiCell(0, 5, string(txtStr), "", "", false)
   398  		// Line break
   399  		pdf.Ln(-1)
   400  		// Mention in italics
   401  		pdf.SetFont("", "I", 0)
   402  		pdf.Cell(0, 5, "(end of excerpt)")
   403  	}
   404  	printChapter := func(chapNum int, titleStr, fileStr string) {
   405  		pdf.AddPage()
   406  		chapterTitle(chapNum, titleStr)
   407  		chapterBody(fileStr)
   408  	}
   409  	printChapter(1, "A RUNAWAY REEF", example.TextFile("20k_c1.txt"))
   410  	printChapter(2, "THE PROS AND CONS", example.TextFile("20k_c2.txt"))
   411  	fileStr := example.Filename("Fpdf_MultiCell")
   412  	err := pdf.OutputFileAndClose(fileStr)
   413  	example.Summary(err, fileStr)
   414  	// Output:
   415  	// Successfully generated pdf/Fpdf_MultiCell.pdf
   416  }
   417  
   418  // ExampleFpdf_SetLeftMargin demonstrates the generation of a PDF document that has multiple
   419  // columns. This is accomplished with the SetLeftMargin() and Cell() methods.
   420  func ExampleFpdf_SetLeftMargin() {
   421  	var y0 float64
   422  	var crrntCol int
   423  	pdf := gofpdf.New("P", "mm", "A4", "")
   424  	pdf.SetDisplayMode("fullpage", "TwoColumnLeft")
   425  	titleStr := "20000 Leagues Under the Seas"
   426  	pdf.SetTitle(titleStr, false)
   427  	pdf.SetAuthor("Jules Verne", false)
   428  	setCol := func(col int) {
   429  		// Set position at a given column
   430  		crrntCol = col
   431  		x := 10.0 + float64(col)*65.0
   432  		pdf.SetLeftMargin(x)
   433  		pdf.SetX(x)
   434  	}
   435  	chapterTitle := func(chapNum int, titleStr string) {
   436  		// Arial 12
   437  		pdf.SetFont("Arial", "", 12)
   438  		// Background color
   439  		pdf.SetFillColor(200, 220, 255)
   440  		// Title
   441  		pdf.CellFormat(0, 6, fmt.Sprintf("Chapter %d : %s", chapNum, titleStr),
   442  			"", 1, "L", true, 0, "")
   443  		// Line break
   444  		pdf.Ln(4)
   445  		y0 = pdf.GetY()
   446  	}
   447  	chapterBody := func(fileStr string) {
   448  		// Read text file
   449  		txtStr, err := ioutil.ReadFile(fileStr)
   450  		if err != nil {
   451  			pdf.SetError(err)
   452  		}
   453  		// Font
   454  		pdf.SetFont("Times", "", 12)
   455  		// Output text in a 6 cm width column
   456  		pdf.MultiCell(60, 5, string(txtStr), "", "", false)
   457  		pdf.Ln(-1)
   458  		// Mention
   459  		pdf.SetFont("", "I", 0)
   460  		pdf.Cell(0, 5, "(end of excerpt)")
   461  		// Go back to first column
   462  		setCol(0)
   463  	}
   464  	printChapter := func(num int, titleStr, fileStr string) {
   465  		// Add chapter
   466  		pdf.AddPage()
   467  		chapterTitle(num, titleStr)
   468  		chapterBody(fileStr)
   469  	}
   470  	pdf.SetAcceptPageBreakFunc(func() bool {
   471  		// Method accepting or not automatic page break
   472  		if crrntCol < 2 {
   473  			// Go to next column
   474  			setCol(crrntCol + 1)
   475  			// Set ordinate to top
   476  			pdf.SetY(y0)
   477  			// Keep on page
   478  			return false
   479  		}
   480  		// Go back to first column
   481  		setCol(0)
   482  		// Page break
   483  		return true
   484  	})
   485  	pdf.SetHeaderFunc(func() {
   486  		// Arial bold 15
   487  		pdf.SetFont("Arial", "B", 15)
   488  		// Calculate width of title and position
   489  		wd := pdf.GetStringWidth(titleStr) + 6
   490  		pdf.SetX((210 - wd) / 2)
   491  		// Colors of frame, background and text
   492  		pdf.SetDrawColor(0, 80, 180)
   493  		pdf.SetFillColor(230, 230, 0)
   494  		pdf.SetTextColor(220, 50, 50)
   495  		// Thickness of frame (1 mm)
   496  		pdf.SetLineWidth(1)
   497  		// Title
   498  		pdf.CellFormat(wd, 9, titleStr, "1", 1, "C", true, 0, "")
   499  		// Line break
   500  		pdf.Ln(10)
   501  		// Save ordinate
   502  		y0 = pdf.GetY()
   503  	})
   504  	pdf.SetFooterFunc(func() {
   505  		// Position at 1.5 cm from bottom
   506  		pdf.SetY(-15)
   507  		// Arial italic 8
   508  		pdf.SetFont("Arial", "I", 8)
   509  		// Text color in gray
   510  		pdf.SetTextColor(128, 128, 128)
   511  		// Page number
   512  		pdf.CellFormat(0, 10, fmt.Sprintf("Page %d", pdf.PageNo()),
   513  			"", 0, "C", false, 0, "")
   514  	})
   515  	printChapter(1, "A RUNAWAY REEF", example.TextFile("20k_c1.txt"))
   516  	printChapter(2, "THE PROS AND CONS", example.TextFile("20k_c2.txt"))
   517  	fileStr := example.Filename("Fpdf_SetLeftMargin_multicolumn")
   518  	err := pdf.OutputFileAndClose(fileStr)
   519  	example.Summary(err, fileStr)
   520  	// Output:
   521  	// Successfully generated pdf/Fpdf_SetLeftMargin_multicolumn.pdf
   522  }
   523  
   524  // ExampleFpdf_SplitLines_tables demonstrates word-wrapped table cells
   525  func ExampleFpdf_SplitLines_tables() {
   526  	const (
   527  		colCount = 3
   528  		colWd    = 60.0
   529  		marginH  = 15.0
   530  		lineHt   = 5.5
   531  		cellGap  = 2.0
   532  	)
   533  	// var colStrList [colCount]string
   534  	type cellType struct {
   535  		str  string
   536  		list [][]byte
   537  		ht   float64
   538  	}
   539  	var (
   540  		cellList [colCount]cellType
   541  		cell     cellType
   542  	)
   543  
   544  	pdf := gofpdf.New("P", "mm", "A4", "") // 210 x 297
   545  	header := [colCount]string{"Column A", "Column B", "Column C"}
   546  	alignList := [colCount]string{"L", "C", "R"}
   547  	strList := loremList()
   548  	pdf.SetMargins(marginH, 15, marginH)
   549  	pdf.SetFont("Arial", "", 14)
   550  	pdf.AddPage()
   551  
   552  	// Headers
   553  	pdf.SetTextColor(224, 224, 224)
   554  	pdf.SetFillColor(64, 64, 64)
   555  	for colJ := 0; colJ < colCount; colJ++ {
   556  		pdf.CellFormat(colWd, 10, header[colJ], "1", 0, "CM", true, 0, "")
   557  	}
   558  	pdf.Ln(-1)
   559  	pdf.SetTextColor(24, 24, 24)
   560  	pdf.SetFillColor(255, 255, 255)
   561  
   562  	// Rows
   563  	y := pdf.GetY()
   564  	count := 0
   565  	for rowJ := 0; rowJ < 2; rowJ++ {
   566  		maxHt := lineHt
   567  		// Cell height calculation loop
   568  		for colJ := 0; colJ < colCount; colJ++ {
   569  			count++
   570  			if count > len(strList) {
   571  				count = 1
   572  			}
   573  			cell.str = strings.Join(strList[0:count], " ")
   574  			cell.list = pdf.SplitLines([]byte(cell.str), colWd-cellGap-cellGap)
   575  			cell.ht = float64(len(cell.list)) * lineHt
   576  			if cell.ht > maxHt {
   577  				maxHt = cell.ht
   578  			}
   579  			cellList[colJ] = cell
   580  		}
   581  		// Cell render loop
   582  		x := marginH
   583  		for colJ := 0; colJ < colCount; colJ++ {
   584  			pdf.Rect(x, y, colWd, maxHt+cellGap+cellGap, "D")
   585  			cell = cellList[colJ]
   586  			cellY := y + cellGap + (maxHt-cell.ht)/2
   587  			for splitJ := 0; splitJ < len(cell.list); splitJ++ {
   588  				pdf.SetXY(x+cellGap, cellY)
   589  				pdf.CellFormat(colWd-cellGap-cellGap, lineHt, string(cell.list[splitJ]), "", 0,
   590  					alignList[colJ], false, 0, "")
   591  				cellY += lineHt
   592  			}
   593  			x += colWd
   594  		}
   595  		y += maxHt + cellGap + cellGap
   596  	}
   597  
   598  	fileStr := example.Filename("Fpdf_SplitLines_tables")
   599  	err := pdf.OutputFileAndClose(fileStr)
   600  	example.Summary(err, fileStr)
   601  	// Output:
   602  	// Successfully generated pdf/Fpdf_SplitLines_tables.pdf
   603  }
   604  
   605  // ExampleFpdf_CellFormat_tables demonstrates various table styles.
   606  func ExampleFpdf_CellFormat_tables() {
   607  	pdf := gofpdf.New("P", "mm", "A4", "")
   608  	type countryType struct {
   609  		nameStr, capitalStr, areaStr, popStr string
   610  	}
   611  	countryList := make([]countryType, 0, 8)
   612  	header := []string{"Country", "Capital", "Area (sq km)", "Pop. (thousands)"}
   613  	loadData := func(fileStr string) {
   614  		fl, err := os.Open(fileStr)
   615  		if err == nil {
   616  			scanner := bufio.NewScanner(fl)
   617  			var c countryType
   618  			for scanner.Scan() {
   619  				// Austria;Vienna;83859;8075
   620  				lineStr := scanner.Text()
   621  				list := strings.Split(lineStr, ";")
   622  				if len(list) == 4 {
   623  					c.nameStr = list[0]
   624  					c.capitalStr = list[1]
   625  					c.areaStr = list[2]
   626  					c.popStr = list[3]
   627  					countryList = append(countryList, c)
   628  				} else {
   629  					err = fmt.Errorf("error tokenizing %s", lineStr)
   630  				}
   631  			}
   632  			fl.Close()
   633  			if len(countryList) == 0 {
   634  				err = fmt.Errorf("error loading data from %s", fileStr)
   635  			}
   636  		}
   637  		if err != nil {
   638  			pdf.SetError(err)
   639  		}
   640  	}
   641  	// Simple table
   642  	basicTable := func() {
   643  		left := (210.0 - 4*40) / 2
   644  		pdf.SetX(left)
   645  		for _, str := range header {
   646  			pdf.CellFormat(40, 7, str, "1", 0, "", false, 0, "")
   647  		}
   648  		pdf.Ln(-1)
   649  		for _, c := range countryList {
   650  			pdf.SetX(left)
   651  			pdf.CellFormat(40, 6, c.nameStr, "1", 0, "", false, 0, "")
   652  			pdf.CellFormat(40, 6, c.capitalStr, "1", 0, "", false, 0, "")
   653  			pdf.CellFormat(40, 6, c.areaStr, "1", 0, "", false, 0, "")
   654  			pdf.CellFormat(40, 6, c.popStr, "1", 0, "", false, 0, "")
   655  			pdf.Ln(-1)
   656  		}
   657  	}
   658  	// Better table
   659  	improvedTable := func() {
   660  		// Column widths
   661  		w := []float64{40.0, 35.0, 40.0, 45.0}
   662  		wSum := 0.0
   663  		for _, v := range w {
   664  			wSum += v
   665  		}
   666  		left := (210 - wSum) / 2
   667  		// 	Header
   668  		pdf.SetX(left)
   669  		for j, str := range header {
   670  			pdf.CellFormat(w[j], 7, str, "1", 0, "C", false, 0, "")
   671  		}
   672  		pdf.Ln(-1)
   673  		// Data
   674  		for _, c := range countryList {
   675  			pdf.SetX(left)
   676  			pdf.CellFormat(w[0], 6, c.nameStr, "LR", 0, "", false, 0, "")
   677  			pdf.CellFormat(w[1], 6, c.capitalStr, "LR", 0, "", false, 0, "")
   678  			pdf.CellFormat(w[2], 6, strDelimit(c.areaStr, ",", 3),
   679  				"LR", 0, "R", false, 0, "")
   680  			pdf.CellFormat(w[3], 6, strDelimit(c.popStr, ",", 3),
   681  				"LR", 0, "R", false, 0, "")
   682  			pdf.Ln(-1)
   683  		}
   684  		pdf.SetX(left)
   685  		pdf.CellFormat(wSum, 0, "", "T", 0, "", false, 0, "")
   686  	}
   687  	// Colored table
   688  	fancyTable := func() {
   689  		// Colors, line width and bold font
   690  		pdf.SetFillColor(255, 0, 0)
   691  		pdf.SetTextColor(255, 255, 255)
   692  		pdf.SetDrawColor(128, 0, 0)
   693  		pdf.SetLineWidth(.3)
   694  		pdf.SetFont("", "B", 0)
   695  		// 	Header
   696  		w := []float64{40, 35, 40, 45}
   697  		wSum := 0.0
   698  		for _, v := range w {
   699  			wSum += v
   700  		}
   701  		left := (210 - wSum) / 2
   702  		pdf.SetX(left)
   703  		for j, str := range header {
   704  			pdf.CellFormat(w[j], 7, str, "1", 0, "C", true, 0, "")
   705  		}
   706  		pdf.Ln(-1)
   707  		// Color and font restoration
   708  		pdf.SetFillColor(224, 235, 255)
   709  		pdf.SetTextColor(0, 0, 0)
   710  		pdf.SetFont("", "", 0)
   711  		// 	Data
   712  		fill := false
   713  		for _, c := range countryList {
   714  			pdf.SetX(left)
   715  			pdf.CellFormat(w[0], 6, c.nameStr, "LR", 0, "", fill, 0, "")
   716  			pdf.CellFormat(w[1], 6, c.capitalStr, "LR", 0, "", fill, 0, "")
   717  			pdf.CellFormat(w[2], 6, strDelimit(c.areaStr, ",", 3),
   718  				"LR", 0, "R", fill, 0, "")
   719  			pdf.CellFormat(w[3], 6, strDelimit(c.popStr, ",", 3),
   720  				"LR", 0, "R", fill, 0, "")
   721  			pdf.Ln(-1)
   722  			fill = !fill
   723  		}
   724  		pdf.SetX(left)
   725  		pdf.CellFormat(wSum, 0, "", "T", 0, "", false, 0, "")
   726  	}
   727  	loadData(example.TextFile("countries.txt"))
   728  	pdf.SetFont("Arial", "", 14)
   729  	pdf.AddPage()
   730  	basicTable()
   731  	pdf.AddPage()
   732  	improvedTable()
   733  	pdf.AddPage()
   734  	fancyTable()
   735  	fileStr := example.Filename("Fpdf_CellFormat_tables")
   736  	err := pdf.OutputFileAndClose(fileStr)
   737  	example.Summary(err, fileStr)
   738  	// Output:
   739  	// Successfully generated pdf/Fpdf_CellFormat_tables.pdf
   740  }
   741  
   742  // ExampleFpdf_HTMLBasicNew demonstrates internal and external links with and without basic
   743  // HTML.
   744  func ExampleFpdf_HTMLBasicNew() {
   745  	pdf := gofpdf.New("P", "mm", "A4", "")
   746  	// First page: manual local link
   747  	pdf.AddPage()
   748  	pdf.SetFont("Helvetica", "", 20)
   749  	_, lineHt := pdf.GetFontSize()
   750  	pdf.Write(lineHt, "To find out what's new in this tutorial, click ")
   751  	pdf.SetFont("", "U", 0)
   752  	link := pdf.AddLink()
   753  	pdf.WriteLinkID(lineHt, "here", link)
   754  	pdf.SetFont("", "", 0)
   755  	// Second page: image link and basic HTML with link
   756  	pdf.AddPage()
   757  	pdf.SetLink(link, 0, -1)
   758  	pdf.Image(example.ImageFile("logo.png"), 10, 12, 30, 0, false, "", 0, "http://www.fpdf.org")
   759  	pdf.SetLeftMargin(45)
   760  	pdf.SetFontSize(14)
   761  	_, lineHt = pdf.GetFontSize()
   762  	htmlStr := `You can now easily print text mixing different styles: <b>bold</b>, ` +
   763  		`<i>italic</i>, <u>underlined</u>, or <b><i><u>all at once</u></i></b>!<br><br>` +
   764  		`<center>You can also center text.</center>` +
   765  		`<right>Or align it to the right.</right>` +
   766  		`You can also insert links on text, such as ` +
   767  		`<a href="http://www.fpdf.org">www.fpdf.org</a>, or on an image: click on the logo.`
   768  	html := pdf.HTMLBasicNew()
   769  	html.Write(lineHt, htmlStr)
   770  	fileStr := example.Filename("Fpdf_HTMLBasicNew")
   771  	err := pdf.OutputFileAndClose(fileStr)
   772  	example.Summary(err, fileStr)
   773  	// Output:
   774  	// Successfully generated pdf/Fpdf_HTMLBasicNew.pdf
   775  }
   776  
   777  // ExampleFpdf_AddFont demonstrates the use of a non-standard font.
   778  func ExampleFpdf_AddFont() {
   779  	pdf := gofpdf.New("P", "mm", "A4", example.FontDir())
   780  	pdf.AddFont("Calligrapher", "", "calligra.json")
   781  	pdf.AddPage()
   782  	pdf.SetFont("Calligrapher", "", 35)
   783  	pdf.Cell(0, 10, "Enjoy new fonts with FPDF!")
   784  	fileStr := example.Filename("Fpdf_AddFont")
   785  	err := pdf.OutputFileAndClose(fileStr)
   786  	example.Summary(err, fileStr)
   787  	// Output:
   788  	// Successfully generated pdf/Fpdf_AddFont.pdf
   789  }
   790  
   791  // ExampleFpdf_WriteAligned demonstrates how to align text with the Write function.
   792  func ExampleFpdf_WriteAligned() {
   793  	pdf := gofpdf.New("P", "mm", "A4", example.FontDir())
   794  	pdf.SetLeftMargin(50.0)
   795  	pdf.SetRightMargin(50.0)
   796  	pdf.AddPage()
   797  	pdf.SetFont("Helvetica", "", 12)
   798  	pdf.WriteAligned(0, 35, "This text is the default alignment, Left", "")
   799  	pdf.Ln(35)
   800  	pdf.WriteAligned(0, 35, "This text is aligned Left", "L")
   801  	pdf.Ln(35)
   802  	pdf.WriteAligned(0, 35, "This text is aligned Center", "C")
   803  	pdf.Ln(35)
   804  	pdf.WriteAligned(0, 35, "This text is aligned Right", "R")
   805  	pdf.Ln(35)
   806  	line := "This can by used to write justified text"
   807  	leftMargin, _, rightMargin, _ := pdf.GetMargins()
   808  	pageWidth, _ := pdf.GetPageSize()
   809  	pageWidth -= leftMargin + rightMargin
   810  	pdf.SetWordSpacing((pageWidth - pdf.GetStringWidth(line)) / float64(strings.Count(line, " ")))
   811  	pdf.WriteAligned(pageWidth, 35, line, "L")
   812  	fileStr := example.Filename("Fpdf_WriteAligned")
   813  	err := pdf.OutputFileAndClose(fileStr)
   814  	example.Summary(err, fileStr)
   815  	// Output:
   816  	// Successfully generated pdf/Fpdf_WriteAligned.pdf
   817  }
   818  
   819  // ExampleFpdf_Image demonstrates how images are included in documents.
   820  func ExampleFpdf_Image() {
   821  	pdf := gofpdf.New("P", "mm", "A4", "")
   822  	pdf.AddPage()
   823  	pdf.SetFont("Arial", "", 11)
   824  	pdf.Image(example.ImageFile("logo.png"), 10, 10, 30, 0, false, "", 0, "")
   825  	pdf.Text(50, 20, "logo.png")
   826  	pdf.Image(example.ImageFile("logo.gif"), 10, 40, 30, 0, false, "", 0, "")
   827  	pdf.Text(50, 50, "logo.gif")
   828  	pdf.Image(example.ImageFile("logo-gray.png"), 10, 70, 30, 0, false, "", 0, "")
   829  	pdf.Text(50, 80, "logo-gray.png")
   830  	pdf.Image(example.ImageFile("logo-rgb.png"), 10, 100, 30, 0, false, "", 0, "")
   831  	pdf.Text(50, 110, "logo-rgb.png")
   832  	pdf.Image(example.ImageFile("logo.jpg"), 10, 130, 30, 0, false, "", 0, "")
   833  	pdf.Text(50, 140, "logo.jpg")
   834  	fileStr := example.Filename("Fpdf_Image")
   835  	err := pdf.OutputFileAndClose(fileStr)
   836  	example.Summary(err, fileStr)
   837  	// Output:
   838  	// Successfully generated pdf/Fpdf_Image.pdf
   839  }
   840  
   841  // ExampleFpdf_ImageOptions demonstrates how the AllowNegativePosition field of the
   842  // ImageOption struct can be used to affect horizontal image placement.
   843  func ExampleFpdf_ImageOptions() {
   844  	var opt gofpdf.ImageOptions
   845  
   846  	pdf := gofpdf.New("P", "mm", "A4", "")
   847  	pdf.AddPage()
   848  	pdf.SetFont("Arial", "", 11)
   849  	pdf.SetX(60)
   850  	opt.ImageType = "png"
   851  	pdf.ImageOptions(example.ImageFile("logo.png"), -10, 10, 30, 0, false, opt, 0, "")
   852  	opt.AllowNegativePosition = true
   853  	pdf.ImageOptions(example.ImageFile("logo.png"), -10, 50, 30, 0, false, opt, 0, "")
   854  	fileStr := example.Filename("Fpdf_ImageOptions")
   855  	err := pdf.OutputFileAndClose(fileStr)
   856  	example.Summary(err, fileStr)
   857  	// Output:
   858  	// Successfully generated pdf/Fpdf_ImageOptions.pdf
   859  }
   860  
   861  // ExampleFpdf_RegisterImageOptionsReader demonstrates how to load an image
   862  // from a io.Reader (in this case, a file) and register it with options.
   863  func ExampleFpdf_RegisterImageOptionsReader() {
   864  	var (
   865  		opt    gofpdf.ImageOptions
   866  		pdfStr string
   867  		fl     *os.File
   868  		err    error
   869  	)
   870  
   871  	pdfStr = example.Filename("Fpdf_RegisterImageOptionsReader")
   872  	pdf := gofpdf.New("P", "mm", "A4", "")
   873  	pdf.AddPage()
   874  	pdf.SetFont("Arial", "", 11)
   875  	fl, err = os.Open(example.ImageFile("logo.png"))
   876  	if err == nil {
   877  		opt.ImageType = "png"
   878  		opt.AllowNegativePosition = true
   879  		_ = pdf.RegisterImageOptionsReader("logo", opt, fl)
   880  		fl.Close()
   881  		for x := -20.0; x <= 40.0; x += 5 {
   882  			pdf.ImageOptions("logo", x, x+30, 0, 0, false, opt, 0, "")
   883  		}
   884  		err = pdf.OutputFileAndClose(pdfStr)
   885  	}
   886  	example.Summary(err, pdfStr)
   887  	// Output:
   888  	// Successfully generated pdf/Fpdf_RegisterImageOptionsReader.pdf
   889  }
   890  
   891  // This example demonstrates Landscape mode with images.
   892  func ExampleFpdf_SetAcceptPageBreakFunc() {
   893  	var y0 float64
   894  	var crrntCol int
   895  	loremStr := lorem()
   896  	pdf := gofpdf.New("L", "mm", "A4", "")
   897  	const (
   898  		pageWd = 297.0 // A4 210.0 x 297.0
   899  		margin = 10.0
   900  		gutter = 4
   901  		colNum = 3
   902  		colWd  = (pageWd - 2*margin - (colNum-1)*gutter) / colNum
   903  	)
   904  	setCol := func(col int) {
   905  		crrntCol = col
   906  		x := margin + float64(col)*(colWd+gutter)
   907  		pdf.SetLeftMargin(x)
   908  		pdf.SetX(x)
   909  	}
   910  	pdf.SetHeaderFunc(func() {
   911  		titleStr := "gofpdf"
   912  		pdf.SetFont("Helvetica", "B", 48)
   913  		wd := pdf.GetStringWidth(titleStr) + 6
   914  		pdf.SetX((pageWd - wd) / 2)
   915  		pdf.SetTextColor(128, 128, 160)
   916  		pdf.Write(12, titleStr[:2])
   917  		pdf.SetTextColor(128, 128, 128)
   918  		pdf.Write(12, titleStr[2:])
   919  		pdf.Ln(20)
   920  		y0 = pdf.GetY()
   921  	})
   922  	pdf.SetAcceptPageBreakFunc(func() bool {
   923  		if crrntCol < colNum-1 {
   924  			setCol(crrntCol + 1)
   925  			pdf.SetY(y0)
   926  			// Start new column, not new page
   927  			return false
   928  		}
   929  		setCol(0)
   930  		return true
   931  	})
   932  	pdf.AddPage()
   933  	pdf.SetFont("Times", "", 12)
   934  	for j := 0; j < 20; j++ {
   935  		if j == 1 {
   936  			pdf.Image(example.ImageFile("fpdf.png"), -1, 0, colWd, 0, true, "", 0, "")
   937  		} else if j == 5 {
   938  			pdf.Image(example.ImageFile("golang-gopher.png"),
   939  				-1, 0, colWd, 0, true, "", 0, "")
   940  		}
   941  		pdf.MultiCell(colWd, 5, loremStr, "", "", false)
   942  		pdf.Ln(-1)
   943  	}
   944  	fileStr := example.Filename("Fpdf_SetAcceptPageBreakFunc_landscape")
   945  	err := pdf.OutputFileAndClose(fileStr)
   946  	example.Summary(err, fileStr)
   947  	// Output:
   948  	// Successfully generated pdf/Fpdf_SetAcceptPageBreakFunc_landscape.pdf
   949  }
   950  
   951  // This example tests corner cases as reported by the gocov tool.
   952  func ExampleFpdf_SetKeywords() {
   953  	var err error
   954  	fileStr := example.Filename("Fpdf_SetKeywords")
   955  	err = gofpdf.MakeFont(example.FontFile("CalligrapherRegular.pfb"),
   956  		example.FontFile("cp1252.map"), example.FontDir(), nil, true)
   957  	if err == nil {
   958  		pdf := gofpdf.New("", "", "", "")
   959  		pdf.SetFontLocation(example.FontDir())
   960  		pdf.SetTitle("世界", true)
   961  		pdf.SetAuthor("世界", true)
   962  		pdf.SetSubject("世界", true)
   963  		pdf.SetCreator("世界", true)
   964  		pdf.SetKeywords("世界", true)
   965  		pdf.AddFont("Calligrapher", "", "CalligrapherRegular.json")
   966  		pdf.AddPage()
   967  		pdf.SetFont("Calligrapher", "", 16)
   968  		pdf.Writef(5, "\x95 %s \x95", pdf)
   969  		err = pdf.OutputFileAndClose(fileStr)
   970  	}
   971  	example.Summary(err, fileStr)
   972  	// Output:
   973  	// Successfully generated pdf/Fpdf_SetKeywords.pdf
   974  }
   975  
   976  // ExampleFpdf_Circle demonstrates the construction of various geometric figures,
   977  func ExampleFpdf_Circle() {
   978  	const (
   979  		thin  = 0.2
   980  		thick = 3.0
   981  	)
   982  	pdf := gofpdf.New("", "", "", "")
   983  	pdf.SetFont("Helvetica", "", 12)
   984  	pdf.SetFillColor(200, 200, 220)
   985  	pdf.AddPage()
   986  
   987  	y := 15.0
   988  	pdf.Text(10, y, "Circles")
   989  	pdf.SetFillColor(200, 200, 220)
   990  	pdf.SetLineWidth(thin)
   991  	pdf.Circle(20, y+15, 10, "D")
   992  	pdf.Circle(45, y+15, 10, "F")
   993  	pdf.Circle(70, y+15, 10, "FD")
   994  	pdf.SetLineWidth(thick)
   995  	pdf.Circle(95, y+15, 10, "FD")
   996  	pdf.SetLineWidth(thin)
   997  
   998  	y += 40.0
   999  	pdf.Text(10, y, "Ellipses")
  1000  	pdf.SetFillColor(220, 200, 200)
  1001  	pdf.Ellipse(30, y+15, 20, 10, 0, "D")
  1002  	pdf.Ellipse(75, y+15, 20, 10, 0, "F")
  1003  	pdf.Ellipse(120, y+15, 20, 10, 0, "FD")
  1004  	pdf.SetLineWidth(thick)
  1005  	pdf.Ellipse(165, y+15, 20, 10, 0, "FD")
  1006  	pdf.SetLineWidth(thin)
  1007  
  1008  	y += 40.0
  1009  	pdf.Text(10, y, "Curves (quadratic)")
  1010  	pdf.SetFillColor(220, 220, 200)
  1011  	pdf.Curve(10, y+30, 15, y-20, 40, y+30, "D")
  1012  	pdf.Curve(45, y+30, 50, y-20, 75, y+30, "F")
  1013  	pdf.Curve(80, y+30, 85, y-20, 110, y+30, "FD")
  1014  	pdf.SetLineWidth(thick)
  1015  	pdf.Curve(115, y+30, 120, y-20, 145, y+30, "FD")
  1016  	pdf.SetLineCapStyle("round")
  1017  	pdf.Curve(150, y+30, 155, y-20, 180, y+30, "FD")
  1018  	pdf.SetLineWidth(thin)
  1019  	pdf.SetLineCapStyle("butt")
  1020  
  1021  	y += 40.0
  1022  	pdf.Text(10, y, "Curves (cubic)")
  1023  	pdf.SetFillColor(220, 200, 220)
  1024  	pdf.CurveBezierCubic(10, y+30, 15, y-20, 10, y+30, 40, y+30, "D")
  1025  	pdf.CurveBezierCubic(45, y+30, 50, y-20, 45, y+30, 75, y+30, "F")
  1026  	pdf.CurveBezierCubic(80, y+30, 85, y-20, 80, y+30, 110, y+30, "FD")
  1027  	pdf.SetLineWidth(thick)
  1028  	pdf.CurveBezierCubic(115, y+30, 120, y-20, 115, y+30, 145, y+30, "FD")
  1029  	pdf.SetLineCapStyle("round")
  1030  	pdf.CurveBezierCubic(150, y+30, 155, y-20, 150, y+30, 180, y+30, "FD")
  1031  	pdf.SetLineWidth(thin)
  1032  	pdf.SetLineCapStyle("butt")
  1033  
  1034  	y += 40.0
  1035  	pdf.Text(10, y, "Arcs")
  1036  	pdf.SetFillColor(200, 220, 220)
  1037  	pdf.SetLineWidth(thick)
  1038  	pdf.Arc(45, y+35, 20, 10, 0, 0, 180, "FD")
  1039  	pdf.SetLineWidth(thin)
  1040  	pdf.Arc(45, y+35, 25, 15, 0, 90, 270, "D")
  1041  	pdf.SetLineWidth(thick)
  1042  	pdf.Arc(45, y+35, 30, 20, 0, 0, 360, "D")
  1043  	pdf.SetLineCapStyle("round")
  1044  	pdf.Arc(135, y+35, 20, 10, 135, 0, 180, "FD")
  1045  	pdf.SetLineWidth(thin)
  1046  	pdf.Arc(135, y+35, 25, 15, 135, 90, 270, "D")
  1047  	pdf.SetLineWidth(thick)
  1048  	pdf.Arc(135, y+35, 30, 20, 135, 0, 360, "D")
  1049  	pdf.SetLineWidth(thin)
  1050  	pdf.SetLineCapStyle("butt")
  1051  
  1052  	fileStr := example.Filename("Fpdf_Circle_figures")
  1053  	err := pdf.OutputFileAndClose(fileStr)
  1054  	example.Summary(err, fileStr)
  1055  	// Output:
  1056  	// Successfully generated pdf/Fpdf_Circle_figures.pdf
  1057  }
  1058  
  1059  // ExampleFpdf_SetAlpha demonstrates alpha transparency.
  1060  func ExampleFpdf_SetAlpha() {
  1061  	const (
  1062  		gapX  = 10.0
  1063  		gapY  = 9.0
  1064  		rectW = 40.0
  1065  		rectH = 58.0
  1066  		pageW = 210
  1067  		pageH = 297
  1068  	)
  1069  	modeList := []string{"Normal", "Multiply", "Screen", "Overlay",
  1070  		"Darken", "Lighten", "ColorDodge", "ColorBurn", "HardLight", "SoftLight",
  1071  		"Difference", "Exclusion", "Hue", "Saturation", "Color", "Luminosity"}
  1072  	pdf := gofpdf.New("", "", "", "")
  1073  	pdf.SetLineWidth(2)
  1074  	pdf.SetAutoPageBreak(false, 0)
  1075  	pdf.AddPage()
  1076  	pdf.SetFont("Helvetica", "", 18)
  1077  	pdf.SetXY(0, gapY)
  1078  	pdf.SetTextColor(0, 0, 0)
  1079  	pdf.CellFormat(pageW, gapY, "Alpha Blending Modes", "", 0, "C", false, 0, "")
  1080  	j := 0
  1081  	y := 3 * gapY
  1082  	for col := 0; col < 4; col++ {
  1083  		x := gapX
  1084  		for row := 0; row < 4; row++ {
  1085  			pdf.Rect(x, y, rectW, rectH, "D")
  1086  			pdf.SetFont("Helvetica", "B", 12)
  1087  			pdf.SetFillColor(0, 0, 0)
  1088  			pdf.SetTextColor(250, 250, 230)
  1089  			pdf.SetXY(x, y+rectH-4)
  1090  			pdf.CellFormat(rectW, 5, modeList[j], "", 0, "C", true, 0, "")
  1091  			pdf.SetFont("Helvetica", "I", 150)
  1092  			pdf.SetTextColor(80, 80, 120)
  1093  			pdf.SetXY(x, y+2)
  1094  			pdf.CellFormat(rectW, rectH, "A", "", 0, "C", false, 0, "")
  1095  			pdf.SetAlpha(0.5, modeList[j])
  1096  			pdf.Image(example.ImageFile("golang-gopher.png"),
  1097  				x-gapX, y, rectW+2*gapX, 0, false, "", 0, "")
  1098  			pdf.SetAlpha(1.0, "Normal")
  1099  			x += rectW + gapX
  1100  			j++
  1101  		}
  1102  		y += rectH + gapY
  1103  	}
  1104  	fileStr := example.Filename("Fpdf_SetAlpha_transparency")
  1105  	err := pdf.OutputFileAndClose(fileStr)
  1106  	example.Summary(err, fileStr)
  1107  	// Output:
  1108  	// Successfully generated pdf/Fpdf_SetAlpha_transparency.pdf
  1109  }
  1110  
  1111  // ExampleFpdf_LinearGradient deomstrates various gradients.
  1112  func ExampleFpdf_LinearGradient() {
  1113  	pdf := gofpdf.New("", "", "", "")
  1114  	pdf.SetFont("Helvetica", "", 12)
  1115  	pdf.AddPage()
  1116  	pdf.LinearGradient(0, 0, 210, 100, 250, 250, 255, 220, 220, 225, 0, 0, 0, .5)
  1117  	pdf.LinearGradient(20, 25, 75, 75, 220, 220, 250, 80, 80, 220, 0, .2, 0, .8)
  1118  	pdf.Rect(20, 25, 75, 75, "D")
  1119  	pdf.LinearGradient(115, 25, 75, 75, 220, 220, 250, 80, 80, 220, 0, 0, 1, 1)
  1120  	pdf.Rect(115, 25, 75, 75, "D")
  1121  	pdf.RadialGradient(20, 120, 75, 75, 220, 220, 250, 80, 80, 220,
  1122  		0.25, 0.75, 0.25, 0.75, 1)
  1123  	pdf.Rect(20, 120, 75, 75, "D")
  1124  	pdf.RadialGradient(115, 120, 75, 75, 220, 220, 250, 80, 80, 220,
  1125  		0.25, 0.75, 0.75, 0.75, 0.75)
  1126  	pdf.Rect(115, 120, 75, 75, "D")
  1127  	fileStr := example.Filename("Fpdf_LinearGradient_gradient")
  1128  	err := pdf.OutputFileAndClose(fileStr)
  1129  	example.Summary(err, fileStr)
  1130  	// Output:
  1131  	// Successfully generated pdf/Fpdf_LinearGradient_gradient.pdf
  1132  }
  1133  
  1134  // ExampleFpdf_ClipText demonstrates clipping.
  1135  func ExampleFpdf_ClipText() {
  1136  	pdf := gofpdf.New("", "", "", "")
  1137  	y := 10.0
  1138  	pdf.AddPage()
  1139  
  1140  	pdf.SetFont("Helvetica", "", 24)
  1141  	pdf.SetXY(0, y)
  1142  	pdf.ClipText(10, y+12, "Clipping examples", false)
  1143  	pdf.RadialGradient(10, y, 100, 20, 128, 128, 160, 32, 32, 48,
  1144  		0.25, 0.5, 0.25, 0.5, 0.2)
  1145  	pdf.ClipEnd()
  1146  
  1147  	y += 12
  1148  	pdf.SetFont("Helvetica", "B", 120)
  1149  	pdf.SetDrawColor(64, 80, 80)
  1150  	pdf.SetLineWidth(.5)
  1151  	pdf.ClipText(10, y+40, pdf.String(), true)
  1152  	pdf.RadialGradient(10, y, 200, 50, 220, 220, 250, 80, 80, 220,
  1153  		0.25, 0.5, 0.25, 0.5, 1)
  1154  	pdf.ClipEnd()
  1155  
  1156  	y += 55
  1157  	pdf.ClipRect(10, y, 105, 20, true)
  1158  	pdf.SetFillColor(255, 255, 255)
  1159  	pdf.Rect(10, y, 105, 20, "F")
  1160  	pdf.ClipCircle(40, y+10, 15, false)
  1161  	pdf.RadialGradient(25, y, 30, 30, 220, 250, 220, 40, 60, 40, 0.3,
  1162  		0.85, 0.3, 0.85, 0.5)
  1163  	pdf.ClipEnd()
  1164  	pdf.ClipEllipse(80, y+10, 20, 15, false)
  1165  	pdf.RadialGradient(60, y, 40, 30, 250, 220, 220, 60, 40, 40, 0.3,
  1166  		0.85, 0.3, 0.85, 0.5)
  1167  	pdf.ClipEnd()
  1168  	pdf.ClipEnd()
  1169  
  1170  	y += 28
  1171  	pdf.ClipEllipse(26, y+10, 16, 10, true)
  1172  	pdf.Image(example.ImageFile("logo.jpg"), 10, y, 32, 0, false, "JPG", 0, "")
  1173  	pdf.ClipEnd()
  1174  
  1175  	pdf.ClipCircle(60, y+10, 10, true)
  1176  	pdf.RadialGradient(50, y, 20, 20, 220, 220, 250, 40, 40, 60, 0.3,
  1177  		0.7, 0.3, 0.7, 0.5)
  1178  	pdf.ClipEnd()
  1179  
  1180  	pdf.ClipPolygon([]gofpdf.PointType{{X: 80, Y: y + 20}, {X: 90, Y: y},
  1181  		{X: 100, Y: y + 20}}, true)
  1182  	pdf.LinearGradient(80, y, 20, 20, 250, 220, 250, 60, 40, 60, 0.5,
  1183  		1, 0.5, 0.5)
  1184  	pdf.ClipEnd()
  1185  
  1186  	y += 30
  1187  	pdf.SetLineWidth(.1)
  1188  	pdf.SetDrawColor(180, 180, 180)
  1189  	pdf.ClipRoundedRect(10, y, 120, 20, 5, true)
  1190  	pdf.RadialGradient(10, y, 120, 20, 255, 255, 255, 240, 240, 220,
  1191  		0.25, 0.75, 0.25, 0.75, 0.5)
  1192  	pdf.SetXY(5, y-5)
  1193  	pdf.SetFont("Times", "", 12)
  1194  	pdf.MultiCell(130, 5, lorem(), "", "", false)
  1195  	pdf.ClipEnd()
  1196  
  1197  	y += 30
  1198  	pdf.SetDrawColor(180, 100, 180)
  1199  	pdf.ClipRoundedRectExt(10, y, 120, 20, 5, 10, 5, 10, true)
  1200  	pdf.RadialGradient(10, y, 120, 20, 255, 255, 255, 240, 240, 220,
  1201  		0.25, 0.75, 0.25, 0.75, 0.5)
  1202  	pdf.SetXY(5, y-5)
  1203  	pdf.SetFont("Times", "", 12)
  1204  	pdf.MultiCell(130, 5, lorem(), "", "", false)
  1205  	pdf.ClipEnd()
  1206  
  1207  	fileStr := example.Filename("Fpdf_ClipText")
  1208  	err := pdf.OutputFileAndClose(fileStr)
  1209  	example.Summary(err, fileStr)
  1210  	// Output:
  1211  	// Successfully generated pdf/Fpdf_ClipText.pdf
  1212  }
  1213  
  1214  // ExampleFpdf_PageSize generates a PDF document with various page sizes.
  1215  func ExampleFpdf_PageSize() {
  1216  	pdf := gofpdf.NewCustom(&gofpdf.InitType{
  1217  		UnitStr:    "in",
  1218  		Size:       gofpdf.SizeType{Wd: 6, Ht: 6},
  1219  		FontDirStr: example.FontDir(),
  1220  	})
  1221  	pdf.SetMargins(0.5, 1, 0.5)
  1222  	pdf.SetFont("Times", "", 14)
  1223  	pdf.AddPageFormat("L", gofpdf.SizeType{Wd: 3, Ht: 12})
  1224  	pdf.SetXY(0.5, 1.5)
  1225  	pdf.CellFormat(11, 0.2, "12 in x 3 in", "", 0, "C", false, 0, "")
  1226  	pdf.AddPage() // Default size established in NewCustom()
  1227  	pdf.SetXY(0.5, 3)
  1228  	pdf.CellFormat(5, 0.2, "6 in x 6 in", "", 0, "C", false, 0, "")
  1229  	pdf.AddPageFormat("P", gofpdf.SizeType{Wd: 3, Ht: 12})
  1230  	pdf.SetXY(0.5, 6)
  1231  	pdf.CellFormat(2, 0.2, "3 in x 12 in", "", 0, "C", false, 0, "")
  1232  	for j := 0; j <= 3; j++ {
  1233  		wd, ht, u := pdf.PageSize(j)
  1234  		fmt.Printf("%d: %6.2f %s, %6.2f %s\n", j, wd, u, ht, u)
  1235  	}
  1236  	fileStr := example.Filename("Fpdf_PageSize")
  1237  	err := pdf.OutputFileAndClose(fileStr)
  1238  	example.Summary(err, fileStr)
  1239  	// Output:
  1240  	// 0:   6.00 in,   6.00 in
  1241  	// 1:  12.00 in,   3.00 in
  1242  	// 2:   6.00 in,   6.00 in
  1243  	// 3:   3.00 in,  12.00 in
  1244  	// Successfully generated pdf/Fpdf_PageSize.pdf
  1245  }
  1246  
  1247  // ExampleFpdf_Bookmark demonstrates the Bookmark method.
  1248  func ExampleFpdf_Bookmark() {
  1249  	pdf := gofpdf.New("P", "mm", "A4", "")
  1250  	pdf.AddPage()
  1251  	pdf.SetFont("Arial", "", 15)
  1252  	pdf.Bookmark("Page 1", 0, 0)
  1253  	pdf.Bookmark("Paragraph 1", 1, -1)
  1254  	pdf.Cell(0, 6, "Paragraph 1")
  1255  	pdf.Ln(50)
  1256  	pdf.Bookmark("Paragraph 2", 1, -1)
  1257  	pdf.Cell(0, 6, "Paragraph 2")
  1258  	pdf.AddPage()
  1259  	pdf.Bookmark("Page 2", 0, 0)
  1260  	pdf.Bookmark("Paragraph 3", 1, -1)
  1261  	pdf.Cell(0, 6, "Paragraph 3")
  1262  	fileStr := example.Filename("Fpdf_Bookmark")
  1263  	err := pdf.OutputFileAndClose(fileStr)
  1264  	example.Summary(err, fileStr)
  1265  	// Output:
  1266  	// Successfully generated pdf/Fpdf_Bookmark.pdf
  1267  }
  1268  
  1269  // ExampleFpdf_TransformBegin demonstrates various transformations. It is adapted from an
  1270  // example script by Moritz Wagner and Andreas Würmser.
  1271  func ExampleFpdf_TransformBegin() {
  1272  	const (
  1273  		light = 200
  1274  		dark  = 0
  1275  	)
  1276  	var refX, refY float64
  1277  	var refStr string
  1278  	pdf := gofpdf.New("P", "mm", "A4", "")
  1279  	pdf.AddPage()
  1280  	color := func(val int) {
  1281  		pdf.SetDrawColor(val, val, val)
  1282  		pdf.SetTextColor(val, val, val)
  1283  	}
  1284  	reference := func(str string, x, y float64, val int) {
  1285  		color(val)
  1286  		pdf.Rect(x, y, 40, 10, "D")
  1287  		pdf.Text(x, y-1, str)
  1288  	}
  1289  	refDraw := func(str string, x, y float64) {
  1290  		refStr = str
  1291  		refX = x
  1292  		refY = y
  1293  		reference(str, x, y, light)
  1294  	}
  1295  	refDupe := func() {
  1296  		reference(refStr, refX, refY, dark)
  1297  	}
  1298  
  1299  	titleStr := "Transformations"
  1300  	titlePt := 36.0
  1301  	titleHt := pdf.PointConvert(titlePt)
  1302  	pdf.SetFont("Helvetica", "", titlePt)
  1303  	titleWd := pdf.GetStringWidth(titleStr)
  1304  	titleX := (210 - titleWd) / 2
  1305  	pdf.Text(titleX, 10+titleHt, titleStr)
  1306  	pdf.TransformBegin()
  1307  	pdf.TransformMirrorVertical(10 + titleHt + 0.5)
  1308  	pdf.ClipText(titleX, 10+titleHt, titleStr, false)
  1309  	// Remember that the transform will mirror the gradient box too
  1310  	pdf.LinearGradient(titleX, 10, titleWd, titleHt+4, 120, 120, 120,
  1311  		255, 255, 255, 0, 0, 0, 0.6)
  1312  	pdf.ClipEnd()
  1313  	pdf.TransformEnd()
  1314  
  1315  	pdf.SetFont("Helvetica", "", 12)
  1316  
  1317  	// Scale by 150% centered by lower left corner of the rectangle
  1318  	refDraw("Scale", 50, 60)
  1319  	pdf.TransformBegin()
  1320  	pdf.TransformScaleXY(150, 50, 70)
  1321  	refDupe()
  1322  	pdf.TransformEnd()
  1323  
  1324  	// Translate 7 to the right, 5 to the bottom
  1325  	refDraw("Translate", 125, 60)
  1326  	pdf.TransformBegin()
  1327  	pdf.TransformTranslate(7, 5)
  1328  	refDupe()
  1329  	pdf.TransformEnd()
  1330  
  1331  	// Rotate 20 degrees counter-clockwise centered by the lower left corner of
  1332  	// the rectangle
  1333  	refDraw("Rotate", 50, 110)
  1334  	pdf.TransformBegin()
  1335  	pdf.TransformRotate(20, 50, 120)
  1336  	refDupe()
  1337  	pdf.TransformEnd()
  1338  
  1339  	// Skew 30 degrees along the x-axis centered by the lower left corner of the
  1340  	// rectangle
  1341  	refDraw("Skew", 125, 110)
  1342  	pdf.TransformBegin()
  1343  	pdf.TransformSkewX(30, 125, 110)
  1344  	refDupe()
  1345  	pdf.TransformEnd()
  1346  
  1347  	// Mirror horizontally with axis of reflection at left side of the rectangle
  1348  	refDraw("Mirror horizontal", 50, 160)
  1349  	pdf.TransformBegin()
  1350  	pdf.TransformMirrorHorizontal(50)
  1351  	refDupe()
  1352  	pdf.TransformEnd()
  1353  
  1354  	// Mirror vertically with axis of reflection at bottom side of the rectangle
  1355  	refDraw("Mirror vertical", 125, 160)
  1356  	pdf.TransformBegin()
  1357  	pdf.TransformMirrorVertical(170)
  1358  	refDupe()
  1359  	pdf.TransformEnd()
  1360  
  1361  	// Reflect against a point at the lower left point of rectangle
  1362  	refDraw("Mirror point", 50, 210)
  1363  	pdf.TransformBegin()
  1364  	pdf.TransformMirrorPoint(50, 220)
  1365  	refDupe()
  1366  	pdf.TransformEnd()
  1367  
  1368  	// Mirror against a straight line described by a point and an angle
  1369  	angle := -20.0
  1370  	px := 120.0
  1371  	py := 220.0
  1372  	refDraw("Mirror line", 125, 210)
  1373  	pdf.TransformBegin()
  1374  	pdf.TransformRotate(angle, px, py)
  1375  	pdf.Line(px-1, py-1, px+1, py+1)
  1376  	pdf.Line(px-1, py+1, px+1, py-1)
  1377  	pdf.Line(px-5, py, px+60, py)
  1378  	pdf.TransformEnd()
  1379  	pdf.TransformBegin()
  1380  	pdf.TransformMirrorLine(angle, px, py)
  1381  	refDupe()
  1382  	pdf.TransformEnd()
  1383  
  1384  	fileStr := example.Filename("Fpdf_TransformBegin")
  1385  	err := pdf.OutputFileAndClose(fileStr)
  1386  	example.Summary(err, fileStr)
  1387  	// Output:
  1388  	// Successfully generated pdf/Fpdf_TransformBegin.pdf
  1389  }
  1390  
  1391  // ExampleFpdf_RegisterImage demonstrates Lawrence Kesteloot's image registration code.
  1392  func ExampleFpdf_RegisterImage() {
  1393  	const (
  1394  		margin = 10
  1395  		wd     = 210
  1396  		ht     = 297
  1397  	)
  1398  	fileList := []string{
  1399  		"logo-gray.png",
  1400  		"logo.jpg",
  1401  		"logo.png",
  1402  		"logo-rgb.png",
  1403  		"logo-progressive.jpg",
  1404  	}
  1405  	var infoPtr *gofpdf.ImageInfoType
  1406  	var imageFileStr string
  1407  	var imgWd, imgHt, lf, tp float64
  1408  	pdf := gofpdf.New("P", "mm", "A4", "")
  1409  	pdf.AddPage()
  1410  	pdf.SetMargins(10, 10, 10)
  1411  	pdf.SetFont("Helvetica", "", 15)
  1412  	for j, str := range fileList {
  1413  		imageFileStr = example.ImageFile(str)
  1414  		infoPtr = pdf.RegisterImage(imageFileStr, "")
  1415  		imgWd, imgHt = infoPtr.Extent()
  1416  		switch j {
  1417  		case 0:
  1418  			lf = margin
  1419  			tp = margin
  1420  		case 1:
  1421  			lf = wd - margin - imgWd
  1422  			tp = margin
  1423  		case 2:
  1424  			lf = (wd - imgWd) / 2.0
  1425  			tp = (ht - imgHt) / 2.0
  1426  		case 3:
  1427  			lf = margin
  1428  			tp = ht - imgHt - margin
  1429  		case 4:
  1430  			lf = wd - imgWd - margin
  1431  			tp = ht - imgHt - margin
  1432  		}
  1433  		pdf.Image(imageFileStr, lf, tp, imgWd, imgHt, false, "", 0, "")
  1434  	}
  1435  	fileStr := example.Filename("Fpdf_RegisterImage")
  1436  	// Test the image information retrieval method
  1437  	infoShow := func(imageStr string) {
  1438  		imageStr = example.ImageFile(imageStr)
  1439  		info := pdf.GetImageInfo(imageStr)
  1440  		if info != nil {
  1441  			if info.Width() > 0.0 {
  1442  				fmt.Printf("Image %s is registered\n", filepath.ToSlash(imageStr))
  1443  			} else {
  1444  				fmt.Printf("Incorrect information for image %s\n", filepath.ToSlash(imageStr))
  1445  			}
  1446  		} else {
  1447  			fmt.Printf("Image %s is not registered\n", filepath.ToSlash(imageStr))
  1448  		}
  1449  	}
  1450  	infoShow(fileList[0])
  1451  	infoShow("foo.png")
  1452  	err := pdf.OutputFileAndClose(fileStr)
  1453  	example.Summary(err, fileStr)
  1454  	// Output:
  1455  	// Image image/logo-gray.png is registered
  1456  	// Image image/foo.png is not registered
  1457  	// Successfully generated pdf/Fpdf_RegisterImage.pdf
  1458  }
  1459  
  1460  // ExampleFpdf_SplitLines demonstrates Bruno Michel's line splitting function.
  1461  func ExampleFpdf_SplitLines() {
  1462  	const (
  1463  		fontPtSize = 18.0
  1464  		wd         = 100.0
  1465  	)
  1466  	pdf := gofpdf.New("P", "mm", "A4", "") // A4 210.0 x 297.0
  1467  	pdf.SetFont("Times", "", fontPtSize)
  1468  	_, lineHt := pdf.GetFontSize()
  1469  	pdf.AddPage()
  1470  	pdf.SetMargins(10, 10, 10)
  1471  	lines := pdf.SplitLines([]byte(lorem()), wd)
  1472  	ht := float64(len(lines)) * lineHt
  1473  	y := (297.0 - ht) / 2.0
  1474  	pdf.SetDrawColor(128, 128, 128)
  1475  	pdf.SetFillColor(255, 255, 210)
  1476  	x := (210.0 - (wd + 40.0)) / 2.0
  1477  	pdf.Rect(x, y-20.0, wd+40.0, ht+40.0, "FD")
  1478  	pdf.SetY(y)
  1479  	for _, line := range lines {
  1480  		pdf.CellFormat(190.0, lineHt, string(line), "", 1, "C", false, 0, "")
  1481  	}
  1482  	fileStr := example.Filename("Fpdf_Splitlines")
  1483  	err := pdf.OutputFileAndClose(fileStr)
  1484  	example.Summary(err, fileStr)
  1485  	// Output:
  1486  	// Successfully generated pdf/Fpdf_Splitlines.pdf
  1487  }
  1488  
  1489  // ExampleFpdf_SVGBasicWrite demonstrates how to render a simple path-only SVG image of the
  1490  // type generated by the jSignature web control.
  1491  func ExampleFpdf_SVGBasicWrite() {
  1492  	const (
  1493  		fontPtSize = 16.0
  1494  		wd         = 100.0
  1495  		sigFileStr = "signature.svg"
  1496  	)
  1497  	var (
  1498  		sig gofpdf.SVGBasicType
  1499  		err error
  1500  	)
  1501  	pdf := gofpdf.New("P", "mm", "A4", "") // A4 210.0 x 297.0
  1502  	pdf.SetFont("Times", "", fontPtSize)
  1503  	lineHt := pdf.PointConvert(fontPtSize)
  1504  	pdf.AddPage()
  1505  	pdf.SetMargins(10, 10, 10)
  1506  	htmlStr := `This example renders a simple ` +
  1507  		`<a href="http://www.w3.org/TR/SVG/">SVG</a> (scalable vector graphics) ` +
  1508  		`image that contains only basic path commands without any styling, ` +
  1509  		`color fill, reflection or endpoint closures. In particular, the ` +
  1510  		`type of vector graphic returned from a ` +
  1511  		`<a href="http://willowsystems.github.io/jSignature/#/demo/">jSignature</a> ` +
  1512  		`web control is supported and is used in this example.`
  1513  	html := pdf.HTMLBasicNew()
  1514  	html.Write(lineHt, htmlStr)
  1515  	sig, err = gofpdf.SVGBasicFileParse(example.ImageFile(sigFileStr))
  1516  	if err == nil {
  1517  		scale := 100 / sig.Wd
  1518  		scaleY := 30 / sig.Ht
  1519  		if scale > scaleY {
  1520  			scale = scaleY
  1521  		}
  1522  		pdf.SetLineCapStyle("round")
  1523  		pdf.SetLineWidth(0.25)
  1524  		pdf.SetDrawColor(0, 0, 128)
  1525  		pdf.SetXY((210.0-scale*sig.Wd)/2.0, pdf.GetY()+10)
  1526  		pdf.SVGBasicWrite(&sig, scale)
  1527  	} else {
  1528  		pdf.SetError(err)
  1529  	}
  1530  	fileStr := example.Filename("Fpdf_SVGBasicWrite")
  1531  	err = pdf.OutputFileAndClose(fileStr)
  1532  	example.Summary(err, fileStr)
  1533  	// Output:
  1534  	// Successfully generated pdf/Fpdf_SVGBasicWrite.pdf
  1535  }
  1536  
  1537  // ExampleFpdf_CellFormat_align demonstrates Stefan Schroeder's code to control vertical
  1538  // alignment.
  1539  func ExampleFpdf_CellFormat_align() {
  1540  	type recType struct {
  1541  		align, txt string
  1542  	}
  1543  	recList := []recType{
  1544  		{"TL", "top left"},
  1545  		{"TC", "top center"},
  1546  		{"TR", "top right"},
  1547  		{"LM", "middle left"},
  1548  		{"CM", "middle center"},
  1549  		{"RM", "middle right"},
  1550  		{"BL", "bottom left"},
  1551  		{"BC", "bottom center"},
  1552  		{"BR", "bottom right"},
  1553  	}
  1554  	recListBaseline := []recType{
  1555  		{"AL", "baseline left"},
  1556  		{"AC", "baseline center"},
  1557  		{"AR", "baseline right"},
  1558  	}
  1559  	var formatRect = func(pdf *gofpdf.Fpdf, recList []recType) {
  1560  		linkStr := ""
  1561  		for pageJ := 0; pageJ < 2; pageJ++ {
  1562  			pdf.AddPage()
  1563  			pdf.SetMargins(10, 10, 10)
  1564  			pdf.SetAutoPageBreak(false, 0)
  1565  			borderStr := "1"
  1566  			for _, rec := range recList {
  1567  				pdf.SetXY(20, 20)
  1568  				pdf.CellFormat(170, 257, rec.txt, borderStr, 0, rec.align, false, 0, linkStr)
  1569  				borderStr = ""
  1570  			}
  1571  			linkStr = "https://github.com/phpdave11/gofpdf"
  1572  		}
  1573  	}
  1574  	pdf := gofpdf.New("P", "mm", "A4", "") // A4 210.0 x 297.0
  1575  	pdf.SetFont("Helvetica", "", 16)
  1576  	formatRect(pdf, recList)
  1577  	formatRect(pdf, recListBaseline)
  1578  	var fr fontResourceType
  1579  	pdf.SetFontLoader(fr)
  1580  	pdf.AddFont("Calligrapher", "", "calligra.json")
  1581  	pdf.SetFont("Calligrapher", "", 16)
  1582  	formatRect(pdf, recListBaseline)
  1583  	fileStr := example.Filename("Fpdf_CellFormat_align")
  1584  	err := pdf.OutputFileAndClose(fileStr)
  1585  	example.Summary(err, fileStr)
  1586  	// Output:
  1587  	// Generalized font loader reading calligra.json
  1588  	// Generalized font loader reading calligra.z
  1589  	// Successfully generated pdf/Fpdf_CellFormat_align.pdf
  1590  }
  1591  
  1592  // ExampleFpdf_CellFormat_codepageescape demonstrates the use of characters in the high range of the
  1593  // Windows-1252 code page (gofdpf default). See the example for CellFormat (4)
  1594  // for a way to do this automatically.
  1595  func ExampleFpdf_CellFormat_codepageescape() {
  1596  	pdf := gofpdf.New("P", "mm", "A4", "") // A4 210.0 x 297.0
  1597  	fontSize := 16.0
  1598  	pdf.SetFont("Helvetica", "", fontSize)
  1599  	ht := pdf.PointConvert(fontSize)
  1600  	write := func(str string) {
  1601  		pdf.CellFormat(190, ht, str, "", 1, "C", false, 0, "")
  1602  		pdf.Ln(ht)
  1603  	}
  1604  	pdf.AddPage()
  1605  	htmlStr := `Until gofpdf supports UTF-8 encoded source text, source text needs ` +
  1606  		`to be specified with all special characters escaped to match the code page ` +
  1607  		`layout of the currently selected font. By default, gofdpf uses code page 1252.` +
  1608  		` See <a href="http://en.wikipedia.org/wiki/Windows-1252">Wikipedia</a> for ` +
  1609  		`a table of this layout.`
  1610  	html := pdf.HTMLBasicNew()
  1611  	html.Write(ht, htmlStr)
  1612  	pdf.Ln(2 * ht)
  1613  	write("Voix ambigu\xeb d'un c\x9cur qui au z\xe9phyr pr\xe9f\xe8re les jattes de kiwi.")
  1614  	write("Falsches \xdcben von Xylophonmusik qu\xe4lt jeden gr\xf6\xdferen Zwerg.")
  1615  	write("Heiz\xf6lr\xfccksto\xdfabd\xe4mpfung")
  1616  	write("For\xe5rsj\xe6vnd\xf8gn / Efter\xe5rsj\xe6vnd\xf8gn")
  1617  	fileStr := example.Filename("Fpdf_CellFormat_codepageescape")
  1618  	err := pdf.OutputFileAndClose(fileStr)
  1619  	example.Summary(err, fileStr)
  1620  	// Output:
  1621  	// Successfully generated pdf/Fpdf_CellFormat_codepageescape.pdf
  1622  }
  1623  
  1624  // ExampleFpdf_CellFormat_codepage demonstrates the automatic conversion of UTF-8 strings to an
  1625  // 8-bit font encoding.
  1626  func ExampleFpdf_CellFormat_codepage() {
  1627  	pdf := gofpdf.New("P", "mm", "A4", example.FontDir()) // A4 210.0 x 297.0
  1628  	// See documentation for details on how to generate fonts
  1629  	pdf.AddFont("Helvetica-1251", "", "helvetica_1251.json")
  1630  	pdf.AddFont("Helvetica-1253", "", "helvetica_1253.json")
  1631  	fontSize := 16.0
  1632  	pdf.SetFont("Helvetica", "", fontSize)
  1633  	ht := pdf.PointConvert(fontSize)
  1634  	tr := pdf.UnicodeTranslatorFromDescriptor("") // "" defaults to "cp1252"
  1635  	write := func(str string) {
  1636  		// pdf.CellFormat(190, ht, tr(str), "", 1, "C", false, 0, "")
  1637  		pdf.MultiCell(190, ht, tr(str), "", "C", false)
  1638  		pdf.Ln(ht)
  1639  	}
  1640  	pdf.AddPage()
  1641  	str := `Gofpdf provides a translator that will convert any UTF-8 code point ` +
  1642  		`that is present in the specified code page.`
  1643  	pdf.MultiCell(190, ht, str, "", "L", false)
  1644  	pdf.Ln(2 * ht)
  1645  	write("Voix ambiguë d'un cœur qui au zéphyr préfère les jattes de kiwi.")
  1646  	write("Falsches Üben von Xylophonmusik quält jeden größeren Zwerg.")
  1647  	write("Heizölrückstoßabdämpfung")
  1648  	write("Forårsjævndøgn / Efterårsjævndøgn")
  1649  	write("À noite, vovô Kowalsky vê o ímã cair no pé do pingüim queixoso e vovó" +
  1650  		"põe açúcar no chá de tâmaras do jabuti feliz.")
  1651  	pdf.SetFont("Helvetica-1251", "", fontSize) // Name matches one specified in AddFont()
  1652  	tr = pdf.UnicodeTranslatorFromDescriptor("cp1251")
  1653  	write("Съешь же ещё этих мягких французских булок, да выпей чаю.")
  1654  
  1655  	pdf.SetFont("Helvetica-1253", "", fontSize)
  1656  	tr = pdf.UnicodeTranslatorFromDescriptor("cp1253")
  1657  	write("Θέλει αρετή και τόλμη η ελευθερία. (Ανδρέας Κάλβος)")
  1658  
  1659  	fileStr := example.Filename("Fpdf_CellFormat_codepage")
  1660  	err := pdf.OutputFileAndClose(fileStr)
  1661  	example.Summary(err, fileStr)
  1662  	// Output:
  1663  	// Successfully generated pdf/Fpdf_CellFormat_codepage.pdf
  1664  }
  1665  
  1666  // ExampleFpdf_SetProtection demonstrates password protection for documents.
  1667  func ExampleFpdf_SetProtection() {
  1668  	pdf := gofpdf.New("P", "mm", "A4", "")
  1669  	pdf.SetProtection(gofpdf.CnProtectPrint, "123", "abc")
  1670  	pdf.AddPage()
  1671  	pdf.SetFont("Arial", "", 12)
  1672  	pdf.Write(10, "Password-protected.")
  1673  	fileStr := example.Filename("Fpdf_SetProtection")
  1674  	err := pdf.OutputFileAndClose(fileStr)
  1675  	example.Summary(err, fileStr)
  1676  	// Output:
  1677  	// Successfully generated pdf/Fpdf_SetProtection.pdf
  1678  }
  1679  
  1680  // ExampleFpdf_Polygon displays equilateral polygons in a demonstration of the Polygon
  1681  // function.
  1682  func ExampleFpdf_Polygon() {
  1683  	const rowCount = 5
  1684  	const colCount = 4
  1685  	const ptSize = 36
  1686  	var x, y, radius, gap, advance float64
  1687  	var rgVal int
  1688  	var pts []gofpdf.PointType
  1689  	vertices := func(count int) (res []gofpdf.PointType) {
  1690  		var pt gofpdf.PointType
  1691  		res = make([]gofpdf.PointType, 0, count)
  1692  		mlt := 2.0 * math.Pi / float64(count)
  1693  		for j := 0; j < count; j++ {
  1694  			pt.Y, pt.X = math.Sincos(float64(j) * mlt)
  1695  			res = append(res, gofpdf.PointType{
  1696  				X: x + radius*pt.X,
  1697  				Y: y + radius*pt.Y})
  1698  		}
  1699  		return
  1700  	}
  1701  	pdf := gofpdf.New("P", "mm", "A4", "") // A4 210.0 x 297.0
  1702  	pdf.AddPage()
  1703  	pdf.SetFont("Helvetica", "", ptSize)
  1704  	pdf.SetDrawColor(0, 80, 180)
  1705  	gap = 12.0
  1706  	pdf.SetY(gap)
  1707  	pdf.CellFormat(190.0, gap, "Equilateral polygons", "", 1, "C", false, 0, "")
  1708  	radius = (210.0 - float64(colCount+1)*gap) / (2.0 * float64(colCount))
  1709  	advance = gap + 2.0*radius
  1710  	y = 2*gap + pdf.PointConvert(ptSize) + radius
  1711  	rgVal = 230
  1712  	for row := 0; row < rowCount; row++ {
  1713  		pdf.SetFillColor(rgVal, rgVal, 0)
  1714  		rgVal -= 12
  1715  		x = gap + radius
  1716  		for col := 0; col < colCount; col++ {
  1717  			pts = vertices(row*colCount + col + 3)
  1718  			pdf.Polygon(pts, "FD")
  1719  			x += advance
  1720  		}
  1721  		y += advance
  1722  	}
  1723  	fileStr := example.Filename("Fpdf_Polygon")
  1724  	err := pdf.OutputFileAndClose(fileStr)
  1725  	example.Summary(err, fileStr)
  1726  	// Output:
  1727  	// Successfully generated pdf/Fpdf_Polygon.pdf
  1728  }
  1729  
  1730  // ExampleFpdf_AddLayer demonstrates document layers. The initial visibility of a layer
  1731  // is specified with the second parameter to AddLayer(). The layer list
  1732  // displayed by the document reader allows layer visibility to be controlled
  1733  // interactively.
  1734  func ExampleFpdf_AddLayer() {
  1735  
  1736  	pdf := gofpdf.New("P", "mm", "A4", "")
  1737  	pdf.AddPage()
  1738  	pdf.SetFont("Arial", "", 15)
  1739  	pdf.Write(8, "This line doesn't belong to any layer.\n")
  1740  
  1741  	// Define layers
  1742  	l1 := pdf.AddLayer("Layer 1", true)
  1743  	l2 := pdf.AddLayer("Layer 2", true)
  1744  
  1745  	// Open layer pane in PDF viewer
  1746  	pdf.OpenLayerPane()
  1747  
  1748  	// First layer
  1749  	pdf.BeginLayer(l1)
  1750  	pdf.Write(8, "This line belongs to layer 1.\n")
  1751  	pdf.EndLayer()
  1752  
  1753  	// Second layer
  1754  	pdf.BeginLayer(l2)
  1755  	pdf.Write(8, "This line belongs to layer 2.\n")
  1756  	pdf.EndLayer()
  1757  
  1758  	// First layer again
  1759  	pdf.BeginLayer(l1)
  1760  	pdf.Write(8, "This line belongs to layer 1 again.\n")
  1761  	pdf.EndLayer()
  1762  
  1763  	fileStr := example.Filename("Fpdf_AddLayer")
  1764  	err := pdf.OutputFileAndClose(fileStr)
  1765  	example.Summary(err, fileStr)
  1766  	// Output:
  1767  	// Successfully generated pdf/Fpdf_AddLayer.pdf
  1768  }
  1769  
  1770  // ExampleFpdf_RegisterImageReader demonstrates the use of an image that is retrieved from a web
  1771  // server.
  1772  func ExampleFpdf_RegisterImageReader() {
  1773  
  1774  	const (
  1775  		margin   = 10
  1776  		wd       = 210
  1777  		ht       = 297
  1778  		fontSize = 15
  1779  		urlStr   = "https://github.com/phpdave11/gofpdf/blob/master/image/gofpdf.png?raw=true"
  1780  		msgStr   = `Images from the web can be easily embedded when a PDF document is generated.`
  1781  	)
  1782  
  1783  	var (
  1784  		rsp *http.Response
  1785  		err error
  1786  		tp  string
  1787  	)
  1788  
  1789  	pdf := gofpdf.New("P", "mm", "A4", "")
  1790  	pdf.AddPage()
  1791  	pdf.SetFont("Helvetica", "", fontSize)
  1792  	ln := pdf.PointConvert(fontSize)
  1793  	pdf.MultiCell(wd-margin-margin, ln, msgStr, "", "L", false)
  1794  	rsp, err = http.Get(urlStr)
  1795  	if err == nil {
  1796  		tp = pdf.ImageTypeFromMime(rsp.Header["Content-Type"][0])
  1797  		infoPtr := pdf.RegisterImageReader(urlStr, tp, rsp.Body)
  1798  		if pdf.Ok() {
  1799  			imgWd, imgHt := infoPtr.Extent()
  1800  			pdf.Image(urlStr, (wd-imgWd)/2.0, pdf.GetY()+ln,
  1801  				imgWd, imgHt, false, tp, 0, "")
  1802  		}
  1803  	} else {
  1804  		pdf.SetError(err)
  1805  	}
  1806  	fileStr := example.Filename("Fpdf_RegisterImageReader_url")
  1807  	err = pdf.OutputFileAndClose(fileStr)
  1808  	example.Summary(err, fileStr)
  1809  	// Output:
  1810  	// Successfully generated pdf/Fpdf_RegisterImageReader_url.pdf
  1811  
  1812  }
  1813  
  1814  // ExampleFpdf_Beziergon demonstrates the Beziergon function.
  1815  func ExampleFpdf_Beziergon() {
  1816  
  1817  	const (
  1818  		margin      = 10
  1819  		wd          = 210
  1820  		unit        = (wd - 2*margin) / 6
  1821  		ht          = 297
  1822  		fontSize    = 15
  1823  		msgStr      = `Demonstration of Beziergon function`
  1824  		coefficient = 0.6
  1825  		delta       = coefficient * unit
  1826  		ln          = fontSize * 25.4 / 72
  1827  		offsetX     = (wd - 4*unit) / 2.0
  1828  		offsetY     = offsetX + 2*ln
  1829  	)
  1830  
  1831  	srcList := []gofpdf.PointType{
  1832  		{X: 0, Y: 0},
  1833  		{X: 1, Y: 0},
  1834  		{X: 1, Y: 1},
  1835  		{X: 2, Y: 1},
  1836  		{X: 2, Y: 2},
  1837  		{X: 3, Y: 2},
  1838  		{X: 3, Y: 3},
  1839  		{X: 4, Y: 3},
  1840  		{X: 4, Y: 4},
  1841  		{X: 1, Y: 4},
  1842  		{X: 1, Y: 3},
  1843  		{X: 0, Y: 3},
  1844  	}
  1845  
  1846  	ctrlList := []gofpdf.PointType{
  1847  		{X: 1, Y: -1},
  1848  		{X: 1, Y: 1},
  1849  		{X: 1, Y: 1},
  1850  		{X: 1, Y: 1},
  1851  		{X: 1, Y: 1},
  1852  		{X: 1, Y: 1},
  1853  		{X: 1, Y: 1},
  1854  		{X: 1, Y: 1},
  1855  		{X: -1, Y: 1},
  1856  		{X: -1, Y: -1},
  1857  		{X: -1, Y: -1},
  1858  		{X: -1, Y: -1},
  1859  	}
  1860  
  1861  	pdf := gofpdf.New("P", "mm", "A4", "")
  1862  	pdf.AddPage()
  1863  	pdf.SetFont("Helvetica", "", fontSize)
  1864  	for j, src := range srcList {
  1865  		srcList[j].X = offsetX + src.X*unit
  1866  		srcList[j].Y = offsetY + src.Y*unit
  1867  	}
  1868  	for j, ctrl := range ctrlList {
  1869  		ctrlList[j].X = ctrl.X * delta
  1870  		ctrlList[j].Y = ctrl.Y * delta
  1871  	}
  1872  	jPrev := len(srcList) - 1
  1873  	srcPrev := srcList[jPrev]
  1874  	curveList := []gofpdf.PointType{srcPrev} // point [, control 0, control 1, point]*
  1875  	control := func(x, y float64) {
  1876  		curveList = append(curveList, gofpdf.PointType{X: x, Y: y})
  1877  	}
  1878  	for j, src := range srcList {
  1879  		ctrl := ctrlList[jPrev]
  1880  		control(srcPrev.X+ctrl.X, srcPrev.Y+ctrl.Y) // Control 0
  1881  		ctrl = ctrlList[j]
  1882  		control(src.X-ctrl.X, src.Y-ctrl.Y) // Control 1
  1883  		curveList = append(curveList, src)  // Destination
  1884  		jPrev = j
  1885  		srcPrev = src
  1886  	}
  1887  	pdf.MultiCell(wd-margin-margin, ln, msgStr, "", "C", false)
  1888  	pdf.SetDashPattern([]float64{0.8, 0.8}, 0)
  1889  	pdf.SetDrawColor(160, 160, 160)
  1890  	pdf.Polygon(srcList, "D")
  1891  	pdf.SetDashPattern([]float64{}, 0)
  1892  	pdf.SetDrawColor(64, 64, 128)
  1893  	pdf.SetLineWidth(pdf.GetLineWidth() * 3)
  1894  	pdf.Beziergon(curveList, "D")
  1895  	fileStr := example.Filename("Fpdf_Beziergon")
  1896  	err := pdf.OutputFileAndClose(fileStr)
  1897  	example.Summary(err, fileStr)
  1898  	// Output:
  1899  	// Successfully generated pdf/Fpdf_Beziergon.pdf
  1900  
  1901  }
  1902  
  1903  // ExampleFpdf_SetFontLoader demonstrates loading a non-standard font using a generalized
  1904  // font loader. fontResourceType implements the FontLoader interface and is
  1905  // defined locally in the test source code.
  1906  func ExampleFpdf_SetFontLoader() {
  1907  	var fr fontResourceType
  1908  	pdf := gofpdf.New("P", "mm", "A4", "")
  1909  	pdf.SetFontLoader(fr)
  1910  	pdf.AddFont("Calligrapher", "", "calligra.json")
  1911  	pdf.AddPage()
  1912  	pdf.SetFont("Calligrapher", "", 35)
  1913  	pdf.Cell(0, 10, "Load fonts from any source")
  1914  	fileStr := example.Filename("Fpdf_SetFontLoader")
  1915  	err := pdf.OutputFileAndClose(fileStr)
  1916  	example.Summary(err, fileStr)
  1917  	// Output:
  1918  	// Generalized font loader reading calligra.json
  1919  	// Generalized font loader reading calligra.z
  1920  	// Successfully generated pdf/Fpdf_SetFontLoader.pdf
  1921  }
  1922  
  1923  // ExampleFpdf_MoveTo demonstrates the Path Drawing functions, such as: MoveTo,
  1924  // LineTo, CurveTo, ..., ClosePath and DrawPath.
  1925  func ExampleFpdf_MoveTo() {
  1926  	pdf := gofpdf.New("P", "mm", "A4", "")
  1927  	pdf.AddPage()
  1928  	pdf.MoveTo(20, 20)
  1929  	pdf.LineTo(170, 20)
  1930  	pdf.ArcTo(170, 40, 20, 20, 0, 90, 0)
  1931  	pdf.CurveTo(190, 100, 105, 100)
  1932  	pdf.CurveBezierCubicTo(20, 100, 105, 200, 20, 200)
  1933  	pdf.ClosePath()
  1934  	pdf.SetFillColor(200, 200, 200)
  1935  	pdf.SetLineWidth(3)
  1936  	pdf.DrawPath("DF")
  1937  	fileStr := example.Filename("Fpdf_MoveTo_path")
  1938  	err := pdf.OutputFileAndClose(fileStr)
  1939  	example.Summary(err, fileStr)
  1940  	// Output:
  1941  	// Successfully generated pdf/Fpdf_MoveTo_path.pdf
  1942  }
  1943  
  1944  // ExampleFpdf_SetLineJoinStyle demonstrates various line cap and line join styles.
  1945  func ExampleFpdf_SetLineJoinStyle() {
  1946  	const offset = 75.0
  1947  	pdf := gofpdf.New("L", "mm", "A4", "")
  1948  	pdf.AddPage()
  1949  	var draw = func(cap, join string, x0, y0, x1, y1 float64) {
  1950  		// transform begin & end needed to isolate caps and joins
  1951  		pdf.SetLineCapStyle(cap)
  1952  		pdf.SetLineJoinStyle(join)
  1953  
  1954  		// Draw thick line
  1955  		pdf.SetDrawColor(0x33, 0x33, 0x33)
  1956  		pdf.SetLineWidth(30.0)
  1957  		pdf.MoveTo(x0, y0)
  1958  		pdf.LineTo((x0+x1)/2+offset, (y0+y1)/2)
  1959  		pdf.LineTo(x1, y1)
  1960  		pdf.DrawPath("D")
  1961  
  1962  		// Draw thin helping line
  1963  		pdf.SetDrawColor(0xFF, 0x33, 0x33)
  1964  		pdf.SetLineWidth(2.56)
  1965  		pdf.MoveTo(x0, y0)
  1966  		pdf.LineTo((x0+x1)/2+offset, (y0+y1)/2)
  1967  		pdf.LineTo(x1, y1)
  1968  		pdf.DrawPath("D")
  1969  
  1970  	}
  1971  	x := 35.0
  1972  	caps := []string{"butt", "square", "round"}
  1973  	joins := []string{"bevel", "miter", "round"}
  1974  	for i := range caps {
  1975  		draw(caps[i], joins[i], x, 50, x, 160)
  1976  		x += offset
  1977  	}
  1978  	fileStr := example.Filename("Fpdf_SetLineJoinStyle_caps")
  1979  	err := pdf.OutputFileAndClose(fileStr)
  1980  	example.Summary(err, fileStr)
  1981  	// Output:
  1982  	// Successfully generated pdf/Fpdf_SetLineJoinStyle_caps.pdf
  1983  }
  1984  
  1985  // ExampleFpdf_DrawPath demonstrates various fill modes.
  1986  func ExampleFpdf_DrawPath() {
  1987  	pdf := gofpdf.New("P", "mm", "A4", "")
  1988  	pdf.SetDrawColor(0xff, 0x00, 0x00)
  1989  	pdf.SetFillColor(0x99, 0x99, 0x99)
  1990  	pdf.SetFont("Helvetica", "", 15)
  1991  	pdf.AddPage()
  1992  	pdf.SetAlpha(1, "Multiply")
  1993  	var (
  1994  		polygon = func(cx, cy, r, n, dir float64) {
  1995  			da := 2 * math.Pi / n
  1996  			pdf.MoveTo(cx+r, cy)
  1997  			pdf.Text(cx+r, cy, "0")
  1998  			i := 1
  1999  			for a := da; a < 2*math.Pi; a += da {
  2000  				x, y := cx+r*math.Cos(dir*a), cy+r*math.Sin(dir*a)
  2001  				pdf.LineTo(x, y)
  2002  				pdf.Text(x, y, strconv.Itoa(i))
  2003  				i++
  2004  			}
  2005  			pdf.ClosePath()
  2006  		}
  2007  		polygons = func(cx, cy, r, n, dir float64) {
  2008  			d := 1.0
  2009  			for rf := r; rf > 0; rf -= 10 {
  2010  				polygon(cx, cy, rf, n, d)
  2011  				d *= dir
  2012  			}
  2013  		}
  2014  		star = func(cx, cy, r, n float64) {
  2015  			da := 4 * math.Pi / n
  2016  			pdf.MoveTo(cx+r, cy)
  2017  			for a := da; a < 4*math.Pi+da; a += da {
  2018  				x, y := cx+r*math.Cos(a), cy+r*math.Sin(a)
  2019  				pdf.LineTo(x, y)
  2020  			}
  2021  			pdf.ClosePath()
  2022  		}
  2023  	)
  2024  	// triangle
  2025  	polygons(55, 45, 40, 3, 1)
  2026  	pdf.DrawPath("B")
  2027  	pdf.Text(15, 95, "B (same direction, non zero winding)")
  2028  
  2029  	// square
  2030  	polygons(155, 45, 40, 4, 1)
  2031  	pdf.DrawPath("B*")
  2032  	pdf.Text(115, 95, "B* (same direction, even odd)")
  2033  
  2034  	// pentagon
  2035  	polygons(55, 145, 40, 5, -1)
  2036  	pdf.DrawPath("B")
  2037  	pdf.Text(15, 195, "B (different direction, non zero winding)")
  2038  
  2039  	// hexagon
  2040  	polygons(155, 145, 40, 6, -1)
  2041  	pdf.DrawPath("B*")
  2042  	pdf.Text(115, 195, "B* (different direction, even odd)")
  2043  
  2044  	// star
  2045  	star(55, 245, 40, 5)
  2046  	pdf.DrawPath("B")
  2047  	pdf.Text(15, 290, "B (non zero winding)")
  2048  
  2049  	// star
  2050  	star(155, 245, 40, 5)
  2051  	pdf.DrawPath("B*")
  2052  	pdf.Text(115, 290, "B* (even odd)")
  2053  
  2054  	fileStr := example.Filename("Fpdf_DrawPath_fill")
  2055  	err := pdf.OutputFileAndClose(fileStr)
  2056  	example.Summary(err, fileStr)
  2057  	// Output:
  2058  	// Successfully generated pdf/Fpdf_DrawPath_fill.pdf
  2059  }
  2060  
  2061  // ExampleFpdf_CreateTemplate demonstrates creating and using templates
  2062  func ExampleFpdf_CreateTemplate() {
  2063  	pdf := gofpdf.New("P", "mm", "A4", "")
  2064  	pdf.SetCompression(false)
  2065  	// pdf.SetFont("Times", "", 12)
  2066  	template := pdf.CreateTemplate(func(tpl *gofpdf.Tpl) {
  2067  		tpl.Image(example.ImageFile("logo.png"), 6, 6, 30, 0, false, "", 0, "")
  2068  		tpl.SetFont("Arial", "B", 16)
  2069  		tpl.Text(40, 20, "Template says hello")
  2070  		tpl.SetDrawColor(0, 100, 200)
  2071  		tpl.SetLineWidth(2.5)
  2072  		tpl.Line(95, 12, 105, 22)
  2073  	})
  2074  	_, tplSize := template.Size()
  2075  	// fmt.Println("Size:", tplSize)
  2076  	// fmt.Println("Scaled:", tplSize.ScaleBy(1.5))
  2077  
  2078  	template2 := pdf.CreateTemplate(func(tpl *gofpdf.Tpl) {
  2079  		tpl.UseTemplate(template)
  2080  		subtemplate := tpl.CreateTemplate(func(tpl2 *gofpdf.Tpl) {
  2081  			tpl2.Image(example.ImageFile("logo.png"), 6, 86, 30, 0, false, "", 0, "")
  2082  			tpl2.SetFont("Arial", "B", 16)
  2083  			tpl2.Text(40, 100, "Subtemplate says hello")
  2084  			tpl2.SetDrawColor(0, 200, 100)
  2085  			tpl2.SetLineWidth(2.5)
  2086  			tpl2.Line(102, 92, 112, 102)
  2087  		})
  2088  		tpl.UseTemplate(subtemplate)
  2089  	})
  2090  
  2091  	pdf.SetDrawColor(200, 100, 0)
  2092  	pdf.SetLineWidth(2.5)
  2093  	pdf.SetFont("Arial", "B", 16)
  2094  
  2095  	// serialize and deserialize template
  2096  	b, _ := template2.Serialize()
  2097  	template3, _ := gofpdf.DeserializeTemplate(b)
  2098  
  2099  	pdf.AddPage()
  2100  	pdf.UseTemplate(template3)
  2101  	pdf.UseTemplateScaled(template3, gofpdf.PointType{X: 0, Y: 30}, tplSize)
  2102  	pdf.Line(40, 210, 60, 210)
  2103  	pdf.Text(40, 200, "Template example page 1")
  2104  
  2105  	pdf.AddPage()
  2106  	pdf.UseTemplate(template2)
  2107  	pdf.UseTemplateScaled(template3, gofpdf.PointType{X: 0, Y: 30}, tplSize.ScaleBy(1.4))
  2108  	pdf.Line(60, 210, 80, 210)
  2109  	pdf.Text(40, 200, "Template example page 2")
  2110  
  2111  	fileStr := example.Filename("Fpdf_CreateTemplate")
  2112  	err := pdf.OutputFileAndClose(fileStr)
  2113  	example.Summary(err, fileStr)
  2114  	// Output:
  2115  	// Successfully generated pdf/Fpdf_CreateTemplate.pdf
  2116  }
  2117  
  2118  // ExampleFpdf_AddFontFromBytes demonstrate how to use embedded fonts from byte array
  2119  func ExampleFpdf_AddFontFromBytes() {
  2120  	pdf := gofpdf.New("P", "mm", "A4", "")
  2121  	pdf.AddPage()
  2122  	pdf.AddFontFromBytes("calligra", "", files.CalligraJson, files.CalligraZ)
  2123  	pdf.SetFont("calligra", "", 16)
  2124  	pdf.Cell(40, 10, "Hello World With Embedded Font!")
  2125  	fileStr := example.Filename("Fpdf_EmbeddedFont")
  2126  	err := pdf.OutputFileAndClose(fileStr)
  2127  	example.Summary(err, fileStr)
  2128  	// Output:
  2129  	// Successfully generated pdf/Fpdf_EmbeddedFont.pdf
  2130  }
  2131  
  2132  // This example demonstrate Clipped table cells
  2133  func ExampleFpdf_ClipRect() {
  2134  	marginCell := 2. // margin of top/bottom of cell
  2135  	pdf := gofpdf.New("P", "mm", "A4", "")
  2136  	pdf.SetFont("Arial", "", 12)
  2137  	pdf.AddPage()
  2138  	pagew, pageh := pdf.GetPageSize()
  2139  	mleft, mright, _, mbottom := pdf.GetMargins()
  2140  
  2141  	cols := []float64{60, 100, pagew - mleft - mright - 100 - 60}
  2142  	rows := [][]string{}
  2143  	for i := 1; i <= 50; i++ {
  2144  		word := fmt.Sprintf("%d:%s", i, strings.Repeat("A", i%100))
  2145  		rows = append(rows, []string{word, word, word})
  2146  	}
  2147  
  2148  	for _, row := range rows {
  2149  		_, lineHt := pdf.GetFontSize()
  2150  		height := lineHt + marginCell
  2151  
  2152  		x, y := pdf.GetXY()
  2153  		// add a new page if the height of the row doesn't fit on the page
  2154  		if y+height >= pageh-mbottom {
  2155  			pdf.AddPage()
  2156  			x, y = pdf.GetXY()
  2157  		}
  2158  		for i, txt := range row {
  2159  			width := cols[i]
  2160  			pdf.Rect(x, y, width, height, "")
  2161  			pdf.ClipRect(x, y, width, height, false)
  2162  			pdf.Cell(width, height, txt)
  2163  			pdf.ClipEnd()
  2164  			x += width
  2165  		}
  2166  		pdf.Ln(-1)
  2167  	}
  2168  	fileStr := example.Filename("Fpdf_ClippedTableCells")
  2169  	err := pdf.OutputFileAndClose(fileStr)
  2170  	example.Summary(err, fileStr)
  2171  	// Output:
  2172  	// Successfully generated pdf/Fpdf_ClippedTableCells.pdf
  2173  }
  2174  
  2175  // This example demonstrate wrapped table cells
  2176  func ExampleFpdf_Rect() {
  2177  	marginCell := 2. // margin of top/bottom of cell
  2178  	pdf := gofpdf.New("P", "mm", "A4", "")
  2179  	pdf.SetFont("Arial", "", 12)
  2180  	pdf.AddPage()
  2181  	pagew, pageh := pdf.GetPageSize()
  2182  	mleft, mright, _, mbottom := pdf.GetMargins()
  2183  
  2184  	cols := []float64{60, 100, pagew - mleft - mright - 100 - 60}
  2185  	rows := [][]string{}
  2186  	for i := 1; i <= 30; i++ {
  2187  		word := fmt.Sprintf("%d:%s", i, strings.Repeat("A", i%100))
  2188  		rows = append(rows, []string{word, word, word})
  2189  	}
  2190  
  2191  	for _, row := range rows {
  2192  		curx, y := pdf.GetXY()
  2193  		x := curx
  2194  
  2195  		height := 0.
  2196  		_, lineHt := pdf.GetFontSize()
  2197  
  2198  		for i, txt := range row {
  2199  			lines := pdf.SplitLines([]byte(txt), cols[i])
  2200  			h := float64(len(lines))*lineHt + marginCell*float64(len(lines))
  2201  			if h > height {
  2202  				height = h
  2203  			}
  2204  		}
  2205  		// add a new page if the height of the row doesn't fit on the page
  2206  		if pdf.GetY()+height > pageh-mbottom {
  2207  			pdf.AddPage()
  2208  			y = pdf.GetY()
  2209  		}
  2210  		for i, txt := range row {
  2211  			width := cols[i]
  2212  			pdf.Rect(x, y, width, height, "")
  2213  			pdf.MultiCell(width, lineHt+marginCell, txt, "", "", false)
  2214  			x += width
  2215  			pdf.SetXY(x, y)
  2216  		}
  2217  		pdf.SetXY(curx, y+height)
  2218  	}
  2219  	fileStr := example.Filename("Fpdf_WrappedTableCells")
  2220  	err := pdf.OutputFileAndClose(fileStr)
  2221  	example.Summary(err, fileStr)
  2222  	// Output:
  2223  	// Successfully generated pdf/Fpdf_WrappedTableCells.pdf
  2224  }
  2225  
  2226  // ExampleFpdf_SetJavascript demonstrates including JavaScript in the document.
  2227  func ExampleFpdf_SetJavascript() {
  2228  	pdf := gofpdf.New("P", "mm", "A4", "")
  2229  	pdf.SetJavascript("print(true);")
  2230  	pdf.AddPage()
  2231  	pdf.SetFont("Arial", "", 12)
  2232  	pdf.Write(10, "Auto-print.")
  2233  	fileStr := example.Filename("Fpdf_SetJavascript")
  2234  	err := pdf.OutputFileAndClose(fileStr)
  2235  	example.Summary(err, fileStr)
  2236  	// Output:
  2237  	// Successfully generated pdf/Fpdf_SetJavascript.pdf
  2238  }
  2239  
  2240  // ExampleFpdf_AddSpotColor demonstrates spot color use
  2241  func ExampleFpdf_AddSpotColor() {
  2242  	pdf := gofpdf.New("P", "mm", "A4", "")
  2243  	pdf.AddSpotColor("PANTONE 145 CVC", 0, 42, 100, 25)
  2244  	pdf.AddPage()
  2245  	pdf.SetFillSpotColor("PANTONE 145 CVC", 90)
  2246  	pdf.Rect(80, 40, 50, 50, "F")
  2247  	fileStr := example.Filename("Fpdf_AddSpotColor")
  2248  	err := pdf.OutputFileAndClose(fileStr)
  2249  	example.Summary(err, fileStr)
  2250  	// Output:
  2251  	// Successfully generated pdf/Fpdf_AddSpotColor.pdf
  2252  }
  2253  
  2254  // ExampleFpdf_RegisterAlias demonstrates how to use `RegisterAlias` to create a table of
  2255  // contents.
  2256  func ExampleFpdf_RegisterAlias() {
  2257  	pdf := gofpdf.New("P", "mm", "A4", "")
  2258  	pdf.SetFont("Arial", "", 12)
  2259  	pdf.AliasNbPages("")
  2260  	pdf.AddPage()
  2261  
  2262  	// Write the table of contents. We use aliases instead of the page number
  2263  	// because we don't know which page the section will begin on.
  2264  	numSections := 3
  2265  	for i := 1; i <= numSections; i++ {
  2266  		pdf.Cell(0, 10, fmt.Sprintf("Section %d begins on page {mark %d}", i, i))
  2267  		pdf.Ln(10)
  2268  	}
  2269  
  2270  	// Write the sections. Before we start writing, we use `RegisterAlias` to
  2271  	// ensure that the alias written in the table of contents will be replaced
  2272  	// by the current page number.
  2273  	for i := 1; i <= numSections; i++ {
  2274  		pdf.AddPage()
  2275  		pdf.RegisterAlias(fmt.Sprintf("{mark %d}", i), fmt.Sprintf("%d", pdf.PageNo()))
  2276  		pdf.Write(10, fmt.Sprintf("Section %d, page %d of {nb}", i, pdf.PageNo()))
  2277  	}
  2278  
  2279  	fileStr := example.Filename("Fpdf_RegisterAlias")
  2280  	err := pdf.OutputFileAndClose(fileStr)
  2281  	example.Summary(err, fileStr)
  2282  	// Output:
  2283  	// Successfully generated pdf/Fpdf_RegisterAlias.pdf
  2284  }
  2285  
  2286  // ExampleFpdf_RegisterAlias_utf8 demonstrates how to use `RegisterAlias` to
  2287  // create a table of contents. This particular example demonstrates the use of
  2288  // UTF-8 aliases.
  2289  func ExampleFpdf_RegisterAlias_utf8() {
  2290  	pdf := gofpdf.New("P", "mm", "A4", "")
  2291  	pdf.AddUTF8Font("dejavu", "", example.FontFile("DejaVuSansCondensed.ttf"))
  2292  	pdf.SetFont("dejavu", "", 12)
  2293  	pdf.AliasNbPages("{entute}")
  2294  	pdf.AddPage()
  2295  
  2296  	// Write the table of contents. We use aliases instead of the page number
  2297  	// because we don't know which page the section will begin on.
  2298  	numSections := 3
  2299  	for i := 1; i <= numSections; i++ {
  2300  		pdf.Cell(0, 10, fmt.Sprintf("Sekcio %d komenciĝas ĉe paĝo {ĉi tiu marko %d}", i, i))
  2301  		pdf.Ln(10)
  2302  	}
  2303  
  2304  	// Write the sections. Before we start writing, we use `RegisterAlias` to
  2305  	// ensure that the alias written in the table of contents will be replaced
  2306  	// by the current page number.
  2307  	for i := 1; i <= numSections; i++ {
  2308  		pdf.AddPage()
  2309  		pdf.RegisterAlias(fmt.Sprintf("{ĉi tiu marko %d}", i), fmt.Sprintf("%d", pdf.PageNo()))
  2310  		pdf.Write(10, fmt.Sprintf("Sekcio %d, paĝo %d de {entute}", i, pdf.PageNo()))
  2311  	}
  2312  
  2313  	fileStr := example.Filename("Fpdf_RegisterAliasUTF8")
  2314  	err := pdf.OutputFileAndClose(fileStr)
  2315  	example.Summary(err, fileStr)
  2316  	// Output:
  2317  	// Successfully generated pdf/Fpdf_RegisterAliasUTF8.pdf
  2318  }
  2319  
  2320  // ExampleNewGrid demonstrates the generation of graph grids.
  2321  func ExampleNewGrid() {
  2322  	pdf := gofpdf.New("P", "mm", "A4", "")
  2323  	pdf.SetFont("Arial", "", 12)
  2324  	pdf.AddPage()
  2325  
  2326  	gr := gofpdf.NewGrid(13, 10, 187, 130)
  2327  	gr.TickmarksExtentX(0, 10, 4)
  2328  	gr.TickmarksExtentY(0, 10, 3)
  2329  	gr.Grid(pdf)
  2330  
  2331  	gr = gofpdf.NewGrid(13, 154, 187, 128)
  2332  	gr.XLabelRotate = true
  2333  	gr.TickmarksExtentX(0, 1, 12)
  2334  	gr.XDiv = 5
  2335  	gr.TickmarksContainY(0, 1.1)
  2336  	gr.YDiv = 20
  2337  	// Replace X label formatter with month abbreviation
  2338  	gr.XTickStr = func(val float64, precision int) string {
  2339  		return time.Month(math.Mod(val, 12) + 1).String()[0:3]
  2340  	}
  2341  	gr.Grid(pdf)
  2342  	dot := func(x, y float64) {
  2343  		pdf.Circle(gr.X(x), gr.Y(y), 0.5, "F")
  2344  	}
  2345  	pts := []float64{0.39, 0.457, 0.612, 0.84, 0.998, 1.037, 1.015, 0.918, 0.772, 0.659, 0.593, 0.164}
  2346  	for month, val := range pts {
  2347  		dot(float64(month)+0.5, val)
  2348  	}
  2349  	pdf.SetDrawColor(255, 64, 64)
  2350  	pdf.SetAlpha(0.5, "Normal")
  2351  	pdf.SetLineWidth(1.2)
  2352  	gr.Plot(pdf, 0.5, 11.5, 50, func(x float64) float64 {
  2353  		// http://www.xuru.org/rt/PR.asp
  2354  		return 0.227 * math.Exp(-0.0373*x*x+0.471*x)
  2355  	})
  2356  	pdf.SetAlpha(1.0, "Normal")
  2357  	pdf.SetXY(gr.X(0.5), gr.Y(1.35))
  2358  	pdf.SetFontSize(14)
  2359  	pdf.Write(0, "Solar energy (MWh) per month, 2016")
  2360  	pdf.AddPage()
  2361  
  2362  	gr = gofpdf.NewGrid(13, 10, 187, 274)
  2363  	gr.TickmarksContainX(2.3, 3.4)
  2364  	gr.TickmarksContainY(10.4, 56.8)
  2365  	gr.Grid(pdf)
  2366  
  2367  	fileStr := example.Filename("Fpdf_Grid")
  2368  	err := pdf.OutputFileAndClose(fileStr)
  2369  	example.Summary(err, fileStr)
  2370  	// Output:
  2371  	// Successfully generated pdf/Fpdf_Grid.pdf
  2372  }
  2373  
  2374  // ExampleFpdf_SetPageBox demonstrates the use of a page box
  2375  func ExampleFpdf_SetPageBox() {
  2376  	// pdfinfo (from http://www.xpdfreader.com) reports the following for this example:
  2377  	// ~ pdfinfo -box pdf/Fpdf_PageBox.pdf
  2378  	// Producer:       FPDF 1.7
  2379  	// CreationDate:   Sat Jan  1 00:00:00 2000
  2380  	// ModDate:   	   Sat Jan  1 00:00:00 2000
  2381  	// Tagged:         no
  2382  	// Form:           none
  2383  	// Pages:          1
  2384  	// Encrypted:      no
  2385  	// Page size:      493.23 x 739.85 pts (rotated 0 degrees)
  2386  	// MediaBox:           0.00     0.00   595.28   841.89
  2387  	// CropBox:           51.02    51.02   544.25   790.87
  2388  	// BleedBox:          51.02    51.02   544.25   790.87
  2389  	// TrimBox:           51.02    51.02   544.25   790.87
  2390  	// ArtBox:            51.02    51.02   544.25   790.87
  2391  	// File size:      1053 bytes
  2392  	// Optimized:      no
  2393  	// PDF version:    1.3
  2394  	const (
  2395  		wd        = 210
  2396  		ht        = 297
  2397  		fontsize  = 6
  2398  		boxmargin = 3 * fontsize
  2399  	)
  2400  	pdf := gofpdf.New("P", "mm", "A4", "") // 210mm x 297mm
  2401  	pdf.SetPageBox("crop", boxmargin, boxmargin, wd-2*boxmargin, ht-2*boxmargin)
  2402  	pdf.SetFont("Arial", "", pdf.UnitToPointConvert(fontsize))
  2403  	pdf.AddPage()
  2404  	pdf.MoveTo(fontsize, fontsize)
  2405  	pdf.Write(fontsize, "This will be cropped from printed output")
  2406  	pdf.MoveTo(boxmargin+fontsize, boxmargin+fontsize)
  2407  	pdf.Write(fontsize, "This will be displayed in cropped output")
  2408  	fileStr := example.Filename("Fpdf_PageBox")
  2409  	err := pdf.OutputFileAndClose(fileStr)
  2410  	example.Summary(err, fileStr)
  2411  	// Output:
  2412  	// Successfully generated pdf/Fpdf_PageBox.pdf
  2413  }
  2414  
  2415  // ExampleFpdf_SubWrite demonstrates subscripted and superscripted text
  2416  // Adapted from http://www.fpdf.org/en/script/script61.php
  2417  func ExampleFpdf_SubWrite() {
  2418  
  2419  	const (
  2420  		fontSize = 12
  2421  		halfX    = 105
  2422  	)
  2423  
  2424  	pdf := gofpdf.New("P", "mm", "A4", "") // 210mm x 297mm
  2425  	pdf.AddPage()
  2426  	pdf.SetFont("Arial", "", fontSize)
  2427  	_, lineHt := pdf.GetFontSize()
  2428  
  2429  	pdf.Write(lineHt, "Hello World!")
  2430  	pdf.SetX(halfX)
  2431  	pdf.Write(lineHt, "This is standard text.\n")
  2432  	pdf.Ln(lineHt * 2)
  2433  
  2434  	pdf.SubWrite(10, "H", 33, 0, 0, "")
  2435  	pdf.Write(10, "ello World!")
  2436  	pdf.SetX(halfX)
  2437  	pdf.Write(10, "This is text with a capital first letter.\n")
  2438  	pdf.Ln(lineHt * 2)
  2439  
  2440  	pdf.SubWrite(lineHt, "Y", 6, 0, 0, "")
  2441  	pdf.Write(lineHt, "ou can also begin the sentence with a small letter. And word wrap also works if the line is too long, like this one is.")
  2442  	pdf.SetX(halfX)
  2443  	pdf.Write(lineHt, "This is text with a small first letter.\n")
  2444  	pdf.Ln(lineHt * 2)
  2445  
  2446  	pdf.Write(lineHt, "The world has a lot of km")
  2447  	pdf.SubWrite(lineHt, "2", 6, 4, 0, "")
  2448  	pdf.SetX(halfX)
  2449  	pdf.Write(lineHt, "This is text with a superscripted letter.\n")
  2450  	pdf.Ln(lineHt * 2)
  2451  
  2452  	pdf.Write(lineHt, "The world has a lot of H")
  2453  	pdf.SubWrite(lineHt, "2", 6, -3, 0, "")
  2454  	pdf.Write(lineHt, "O")
  2455  	pdf.SetX(halfX)
  2456  	pdf.Write(lineHt, "This is text with a subscripted letter.\n")
  2457  
  2458  	fileStr := example.Filename("Fpdf_SubWrite")
  2459  	err := pdf.OutputFileAndClose(fileStr)
  2460  	example.Summary(err, fileStr)
  2461  	// Output:
  2462  	// Successfully generated pdf/Fpdf_SubWrite.pdf
  2463  }
  2464  
  2465  // ExampleFpdf_SetPage demomstrates the SetPage() method, allowing content
  2466  // generation to be deferred until all pages have been added.
  2467  func ExampleFpdf_SetPage() {
  2468  	rnd := rand.New(rand.NewSource(0)) // Make reproducible documents
  2469  	pdf := gofpdf.New("L", "cm", "A4", "")
  2470  	pdf.SetFont("Times", "", 12)
  2471  
  2472  	var time []float64
  2473  	temperaturesFromSensors := make([][]float64, 5)
  2474  	maxs := []float64{25, 41, 89, 62, 11}
  2475  	for i := range temperaturesFromSensors {
  2476  		temperaturesFromSensors[i] = make([]float64, 0)
  2477  	}
  2478  
  2479  	for i := 0.0; i < 100; i += 0.5 {
  2480  		time = append(time, i)
  2481  		for j, sensor := range temperaturesFromSensors {
  2482  			dataValue := rnd.Float64() * maxs[j]
  2483  			sensor = append(sensor, dataValue)
  2484  			temperaturesFromSensors[j] = sensor
  2485  		}
  2486  	}
  2487  	var graphs []gofpdf.GridType
  2488  	var pageNums []int
  2489  	xMax := time[len(time)-1]
  2490  	for i := range temperaturesFromSensors {
  2491  		//Create a new page and graph for each sensor we want to graph.
  2492  		pdf.AddPage()
  2493  		pdf.Ln(1)
  2494  		//Custom label per sensor
  2495  		pdf.WriteAligned(0, 0, "Temperature Sensor "+strconv.Itoa(i+1)+" (C) vs Time (min)", "C")
  2496  		pdf.Ln(0.5)
  2497  		graph := gofpdf.NewGrid(pdf.GetX(), pdf.GetY(), 20, 10)
  2498  		graph.TickmarksContainX(0, xMax)
  2499  		//Custom Y axis
  2500  		graph.TickmarksContainY(0, maxs[i])
  2501  		graph.Grid(pdf)
  2502  		//Save references and locations.
  2503  		graphs = append(graphs, graph)
  2504  		pageNums = append(pageNums, pdf.PageNo())
  2505  	}
  2506  	// For each X, graph the Y in each sensor.
  2507  	for i, currTime := range time {
  2508  		for j, sensor := range temperaturesFromSensors {
  2509  			pdf.SetPage(pageNums[j])
  2510  			graph := graphs[j]
  2511  			temperature := sensor[i]
  2512  			pdf.Circle(graph.X(currTime), graph.Y(temperature), 0.04, "D")
  2513  		}
  2514  	}
  2515  
  2516  	fileStr := example.Filename("Fpdf_SetPage")
  2517  	err := pdf.OutputFileAndClose(fileStr)
  2518  	example.Summary(err, fileStr)
  2519  	// Output:
  2520  	// Successfully generated pdf/Fpdf_SetPage.pdf
  2521  }
  2522  
  2523  // ExampleFpdf_SetFillColor demonstrates how graphic attributes are properly
  2524  // assigned within multiple transformations. See issue #234.
  2525  func ExampleFpdf_SetFillColor() {
  2526  	pdf := gofpdf.New("P", "mm", "A4", "")
  2527  
  2528  	pdf.AddPage()
  2529  	pdf.SetFont("Arial", "", 8)
  2530  
  2531  	draw := func(trX, trY float64) {
  2532  		pdf.TransformBegin()
  2533  		pdf.TransformTranslateX(trX)
  2534  		pdf.TransformTranslateY(trY)
  2535  		pdf.SetLineJoinStyle("round")
  2536  		pdf.SetLineWidth(0.5)
  2537  		pdf.SetDrawColor(128, 64, 0)
  2538  		pdf.SetFillColor(255, 127, 0)
  2539  		pdf.SetAlpha(0.5, "Normal")
  2540  		pdf.SetDashPattern([]float64{5, 10}, 0)
  2541  		pdf.Rect(0, 0, 40, 40, "FD")
  2542  		pdf.SetFontSize(12)
  2543  		pdf.SetXY(5, 5)
  2544  		pdf.Write(0, "Test")
  2545  		pdf.TransformEnd()
  2546  	}
  2547  
  2548  	draw(5, 5)
  2549  	draw(50, 50)
  2550  
  2551  	fileStr := example.Filename("Fpdf_SetFillColor")
  2552  	err := pdf.OutputFileAndClose(fileStr)
  2553  	example.Summary(err, fileStr)
  2554  	// Output:
  2555  	// Successfully generated pdf/Fpdf_SetFillColor.pdf
  2556  }
  2557  
  2558  // ExampleFpdf_TransformRotate demonstrates how to rotate text within a header
  2559  // to make a watermark that appears on each page.
  2560  func ExampleFpdf_TransformRotate() {
  2561  
  2562  	loremStr := lorem() + "\n\n"
  2563  	pdf := gofpdf.New("P", "mm", "A4", "")
  2564  	margin := 25.0
  2565  	pdf.SetMargins(margin, margin, margin)
  2566  
  2567  	fontHt := 13.0
  2568  	lineHt := pdf.PointToUnitConvert(fontHt)
  2569  	markFontHt := 50.0
  2570  	markLineHt := pdf.PointToUnitConvert(markFontHt)
  2571  	markY := (297.0 - markLineHt) / 2.0
  2572  	ctrX := 210.0 / 2.0
  2573  	ctrY := 297.0 / 2.0
  2574  
  2575  	pdf.SetHeaderFunc(func() {
  2576  		pdf.SetFont("Arial", "B", markFontHt)
  2577  		pdf.SetTextColor(206, 216, 232)
  2578  		pdf.SetXY(margin, markY)
  2579  		pdf.TransformBegin()
  2580  		pdf.TransformRotate(45, ctrX, ctrY)
  2581  		pdf.CellFormat(0, markLineHt, "W A T E R M A R K   D E M O", "", 0, "C", false, 0, "")
  2582  		pdf.TransformEnd()
  2583  		pdf.SetXY(margin, margin)
  2584  	})
  2585  
  2586  	pdf.AddPage()
  2587  	pdf.SetFont("Arial", "", 8)
  2588  	for j := 0; j < 25; j++ {
  2589  		pdf.MultiCell(0, lineHt, loremStr, "", "L", false)
  2590  	}
  2591  
  2592  	fileStr := example.Filename("Fpdf_RotateText")
  2593  	err := pdf.OutputFileAndClose(fileStr)
  2594  	example.Summary(err, fileStr)
  2595  	// Output:
  2596  	// Successfully generated pdf/Fpdf_RotateText.pdf
  2597  }
  2598  
  2599  // ExampleFpdf_AddUTF8Font demonstrates how use the font
  2600  // with utf-8 mode
  2601  func ExampleFpdf_AddUTF8Font() {
  2602  	var fileStr string
  2603  	var txtStr []byte
  2604  	var err error
  2605  
  2606  	pdf := gofpdf.New("P", "mm", "A4", "")
  2607  
  2608  	pdf.AddPage()
  2609  
  2610  	pdf.AddUTF8Font("dejavu", "", example.FontFile("DejaVuSansCondensed.ttf"))
  2611  	pdf.AddUTF8Font("dejavu", "B", example.FontFile("DejaVuSansCondensed-Bold.ttf"))
  2612  	pdf.AddUTF8Font("dejavu", "I", example.FontFile("DejaVuSansCondensed-Oblique.ttf"))
  2613  	pdf.AddUTF8Font("dejavu", "BI", example.FontFile("DejaVuSansCondensed-BoldOblique.ttf"))
  2614  
  2615  	fileStr = example.Filename("Fpdf_AddUTF8Font")
  2616  	txtStr, err = ioutil.ReadFile(example.TextFile("utf-8test.txt"))
  2617  	if err == nil {
  2618  
  2619  		pdf.SetFont("dejavu", "B", 17)
  2620  		pdf.MultiCell(100, 8, "Text in different languages :", "", "C", false)
  2621  		pdf.SetFont("dejavu", "", 14)
  2622  		pdf.MultiCell(100, 5, string(txtStr), "", "C", false)
  2623  		pdf.Ln(15)
  2624  
  2625  		txtStr, err = ioutil.ReadFile(example.TextFile("utf-8test2.txt"))
  2626  		if err == nil {
  2627  
  2628  			pdf.SetFont("dejavu", "BI", 17)
  2629  			pdf.MultiCell(100, 8, "Greek text with alignStr = \"J\":", "", "C", false)
  2630  			pdf.SetFont("dejavu", "I", 14)
  2631  			pdf.MultiCell(100, 5, string(txtStr), "", "J", false)
  2632  			err = pdf.OutputFileAndClose(fileStr)
  2633  
  2634  		}
  2635  	}
  2636  	example.Summary(err, fileStr)
  2637  	// Output:
  2638  	// Successfully generated pdf/Fpdf_AddUTF8Font.pdf
  2639  }
  2640  
  2641  // ExampleUTF8CutFont demonstrates how generate a TrueType font subset.
  2642  func ExampleUTF8CutFont() {
  2643  	var pdfFileStr, fullFontFileStr, subFontFileStr string
  2644  	var subFont, fullFont []byte
  2645  	var err error
  2646  
  2647  	pdfFileStr = example.Filename("Fpdf_UTF8CutFont")
  2648  	fullFontFileStr = example.FontFile("calligra.ttf")
  2649  	fullFont, err = ioutil.ReadFile(fullFontFileStr)
  2650  	if err == nil {
  2651  		subFontFileStr = "calligra_abcde.ttf"
  2652  		subFont = gofpdf.UTF8CutFont(fullFont, "abcde")
  2653  		err = ioutil.WriteFile(subFontFileStr, subFont, 0600)
  2654  		if err == nil {
  2655  			y := 24.0
  2656  			pdf := gofpdf.New("P", "mm", "A4", "")
  2657  			fontHt := 17.0
  2658  			lineHt := pdf.PointConvert(fontHt)
  2659  			write := func(format string, args ...interface{}) {
  2660  				pdf.SetXY(24.0, y)
  2661  				pdf.Cell(200.0, lineHt, fmt.Sprintf(format, args...))
  2662  				y += lineHt
  2663  			}
  2664  			writeSize := func(fileStr string) {
  2665  				var info os.FileInfo
  2666  				var err error
  2667  				info, err = os.Stat(fileStr)
  2668  				if err == nil {
  2669  					write("%6d: size of %s", info.Size(), fileStr)
  2670  				}
  2671  			}
  2672  			pdf.AddPage()
  2673  			pdf.AddUTF8Font("calligra", "", subFontFileStr)
  2674  			pdf.SetFont("calligra", "", fontHt)
  2675  			write("cabbed")
  2676  			write("vwxyz")
  2677  			pdf.SetFont("courier", "", fontHt)
  2678  			writeSize(fullFontFileStr)
  2679  			writeSize(subFontFileStr)
  2680  			err = pdf.OutputFileAndClose(pdfFileStr)
  2681  			os.Remove(subFontFileStr)
  2682  		}
  2683  	}
  2684  	example.Summary(err, pdfFileStr)
  2685  	// Output:
  2686  	// Successfully generated pdf/Fpdf_UTF8CutFont.pdf
  2687  }
  2688  
  2689  func ExampleFpdf_RoundedRect() {
  2690  	const (
  2691  		wd     = 40.0
  2692  		hgap   = 10.0
  2693  		radius = 10.0
  2694  		ht     = 60.0
  2695  		vgap   = 10.0
  2696  	)
  2697  	corner := func(b1, b2, b3, b4 bool) (cstr string) {
  2698  		if b1 {
  2699  			cstr = "1"
  2700  		}
  2701  		if b2 {
  2702  			cstr += "2"
  2703  		}
  2704  		if b3 {
  2705  			cstr += "3"
  2706  		}
  2707  		if b4 {
  2708  			cstr += "4"
  2709  		}
  2710  		return
  2711  	}
  2712  	pdf := gofpdf.New("P", "mm", "A4", "") // 210 x 297
  2713  	pdf.AddPage()
  2714  	pdf.SetLineWidth(0.5)
  2715  	y := vgap
  2716  	r := 40
  2717  	g := 30
  2718  	b := 20
  2719  	for row := 0; row < 4; row++ {
  2720  		x := hgap
  2721  		for col := 0; col < 4; col++ {
  2722  			pdf.SetFillColor(r, g, b)
  2723  			pdf.RoundedRect(x, y, wd, ht, radius, corner(row&1 == 1, row&2 == 2, col&1 == 1, col&2 == 2), "FD")
  2724  			r += 8
  2725  			g += 10
  2726  			b += 12
  2727  			x += wd + hgap
  2728  		}
  2729  		y += ht + vgap
  2730  	}
  2731  	pdf.AddPage()
  2732  	pdf.RoundedRectExt(10, 20, 40, 80, 4., 0., 20, 0., "FD")
  2733  
  2734  	fileStr := example.Filename("Fpdf_RoundedRect")
  2735  	err := pdf.OutputFileAndClose(fileStr)
  2736  	example.Summary(err, fileStr)
  2737  	// Output:
  2738  	// Successfully generated pdf/Fpdf_RoundedRect.pdf
  2739  }
  2740  
  2741  // ExampleFpdf_SetUnderlineThickness demonstrates how to adjust the text
  2742  // underline thickness.
  2743  func ExampleFpdf_SetUnderlineThickness() {
  2744  	pdf := gofpdf.New("P", "mm", "A4", "") // 210mm x 297mm
  2745  	pdf.AddPage()
  2746  	pdf.SetFont("Arial", "U", 12)
  2747  
  2748  	pdf.SetUnderlineThickness(0.5)
  2749  	pdf.CellFormat(0, 10, "Thin underline", "", 1, "", false, 0, "")
  2750  
  2751  	pdf.SetUnderlineThickness(1)
  2752  	pdf.CellFormat(0, 10, "Normal underline", "", 1, "", false, 0, "")
  2753  
  2754  	pdf.SetUnderlineThickness(2)
  2755  	pdf.CellFormat(0, 10, "Thicker underline", "", 1, "", false, 0, "")
  2756  
  2757  	fileStr := example.Filename("Fpdf_UnderlineThickness")
  2758  	err := pdf.OutputFileAndClose(fileStr)
  2759  	example.Summary(err, fileStr)
  2760  	// Output:
  2761  	// Successfully generated pdf/Fpdf_UnderlineThickness.pdf
  2762  }
  2763  
  2764  // ExampleFpdf_Cell_strikeout demonstrates striked-out text
  2765  func ExampleFpdf_Cell_strikeout() {
  2766  
  2767  	pdf := gofpdf.New("P", "mm", "A4", "") // 210mm x 297mm
  2768  	pdf.AddPage()
  2769  
  2770  	for fontSize := 4; fontSize < 40; fontSize += 10 {
  2771  		pdf.SetFont("Arial", "S", float64(fontSize))
  2772  		pdf.SetXY(0, float64(fontSize))
  2773  		pdf.Cell(40, 10, "Hello World")
  2774  	}
  2775  
  2776  	fileStr := example.Filename("Fpdf_Cell_strikeout")
  2777  	err := pdf.OutputFileAndClose(fileStr)
  2778  	example.Summary(err, fileStr)
  2779  	// Output:
  2780  	// Successfully generated pdf/Fpdf_Cell_strikeout.pdf
  2781  }
  2782  
  2783  // ExampleFpdf_SetTextRenderingMode demonstrates rendering modes in PDFs.
  2784  func ExampleFpdf_SetTextRenderingMode() {
  2785  
  2786  	pdf := gofpdf.New("P", "mm", "A4", "") // 210mm x 297mm
  2787  	pdf.AddPage()
  2788  	fontSz := float64(16)
  2789  	lineSz := pdf.PointToUnitConvert(fontSz)
  2790  	pdf.SetFont("Times", "", fontSz)
  2791  	pdf.Write(lineSz, "This document demonstrates various modes of text rendering. Search for \"Mode 3\" "+
  2792  		"to locate text that has been rendered invisibly. This selection can be copied "+
  2793  		"into the clipboard as usual and is useful for overlaying onto non-textual elements such "+
  2794  		"as images to make them searchable.\n\n")
  2795  	fontSz = float64(125)
  2796  	lineSz = pdf.PointToUnitConvert(fontSz)
  2797  	pdf.SetFontSize(fontSz)
  2798  	pdf.SetTextColor(170, 170, 190)
  2799  	pdf.SetDrawColor(50, 60, 90)
  2800  
  2801  	write := func(mode int) {
  2802  		pdf.SetTextRenderingMode(mode)
  2803  		pdf.CellFormat(210, lineSz, fmt.Sprintf("Mode %d", mode), "", 1, "", false, 0, "")
  2804  	}
  2805  
  2806  	for mode := 0; mode < 4; mode++ {
  2807  		write(mode)
  2808  	}
  2809  	write(0)
  2810  
  2811  	fileStr := example.Filename("Fpdf_TextRenderingMode")
  2812  	err := pdf.OutputFileAndClose(fileStr)
  2813  	example.Summary(err, fileStr)
  2814  	// Output:
  2815  	// Successfully generated pdf/Fpdf_TextRenderingMode.pdf
  2816  }
  2817  
  2818  // TestIssue0316 addresses issue 316 in which AddUTF8FromBytes modifies its argument
  2819  // utf8bytes resulting in a panic if you generate two PDFs with the "same" font bytes.
  2820  func TestIssue0316(t *testing.T) {
  2821  	pdf := gofpdf.New(gofpdf.OrientationPortrait, "mm", "A4", "")
  2822  	pdf.AddPage()
  2823  	fontBytes, _ := ioutil.ReadFile(example.FontFile("DejaVuSansCondensed.ttf"))
  2824  	ofontBytes := append([]byte{}, fontBytes...)
  2825  	pdf.AddUTF8FontFromBytes("dejavu", "", fontBytes)
  2826  	pdf.SetFont("dejavu", "", 16)
  2827  	pdf.Cell(40, 10, "Hello World!")
  2828  	fileStr := example.Filename("TestIssue0316")
  2829  	err := pdf.OutputFileAndClose(fileStr)
  2830  	example.Summary(err, fileStr)
  2831  	pdf.AddPage()
  2832  	if !bytes.Equal(fontBytes, ofontBytes) {
  2833  		t.Fatal("Font data changed during pdf generation")
  2834  	}
  2835  }
  2836  
  2837  func TestMultiCellUnsupportedChar(t *testing.T) {
  2838  	pdf := gofpdf.New("P", "mm", "A4", "")
  2839  	pdf.AddPage()
  2840  	fontBytes, _ := ioutil.ReadFile(example.FontFile("DejaVuSansCondensed.ttf"))
  2841  	pdf.AddUTF8FontFromBytes("dejavu", "", fontBytes)
  2842  	pdf.SetFont("dejavu", "", 16)
  2843  
  2844  	defer func() {
  2845  		if r := recover(); r != nil {
  2846  			t.Errorf("unexpected panic: %v", r)
  2847  		}
  2848  	}()
  2849  
  2850  	pdf.MultiCell(0, 5, "😀", "", "", false)
  2851  
  2852  	fileStr := example.Filename("TestMultiCellUnsupportedChar")
  2853  	pdf.OutputFileAndClose(fileStr)
  2854  }
  2855  
  2856  // ExampleFpdf_SetTextRenderingMode demonstrates embedding files in PDFs,
  2857  // at the top-level.
  2858  func ExampleFpdf_SetAttachments() {
  2859  	pdf := gofpdf.New("P", "mm", "A4", "")
  2860  
  2861  	// Global attachments
  2862  	file, err := ioutil.ReadFile("grid.go")
  2863  	if err != nil {
  2864  		pdf.SetError(err)
  2865  	}
  2866  	a1 := gofpdf.Attachment{Content: file, Filename: "grid.go"}
  2867  	file, err = ioutil.ReadFile("LICENSE")
  2868  	if err != nil {
  2869  		pdf.SetError(err)
  2870  	}
  2871  	a2 := gofpdf.Attachment{Content: file, Filename: "License"}
  2872  	pdf.SetAttachments([]gofpdf.Attachment{a1, a2})
  2873  
  2874  	fileStr := example.Filename("Fpdf_EmbeddedFiles")
  2875  	err = pdf.OutputFileAndClose(fileStr)
  2876  	example.Summary(err, fileStr)
  2877  	// Output:
  2878  	// Successfully generated pdf/Fpdf_EmbeddedFiles.pdf
  2879  }
  2880  
  2881  func ExampleFpdf_AddAttachmentAnnotation() {
  2882  	pdf := gofpdf.New("P", "mm", "A4", "")
  2883  	pdf.SetFont("Arial", "", 12)
  2884  	pdf.AddPage()
  2885  
  2886  	// Per page attachment
  2887  	file, err := ioutil.ReadFile("grid.go")
  2888  	if err != nil {
  2889  		pdf.SetError(err)
  2890  	}
  2891  	a := gofpdf.Attachment{Content: file, Filename: "grid.go", Description: "Some amazing code !"}
  2892  
  2893  	pdf.SetXY(5, 10)
  2894  	pdf.Rect(2, 10, 50, 15, "D")
  2895  	pdf.AddAttachmentAnnotation(&a, 2, 10, 50, 15)
  2896  	pdf.Cell(50, 15, "A first link")
  2897  
  2898  	pdf.SetXY(5, 80)
  2899  	pdf.Rect(2, 80, 50, 15, "D")
  2900  	pdf.AddAttachmentAnnotation(&a, 2, 80, 50, 15)
  2901  	pdf.Cell(50, 15, "A second link (no copy)")
  2902  
  2903  	fileStr := example.Filename("Fpdf_FileAnnotations")
  2904  	err = pdf.OutputFileAndClose(fileStr)
  2905  	example.Summary(err, fileStr)
  2906  	// Output:
  2907  	// Successfully generated pdf/Fpdf_FileAnnotations.pdf
  2908  }
  2909  
  2910  func ExampleFpdf_SetModificationDate() {
  2911  	// pdfinfo (from http://www.xpdfreader.com) reports the following for this example :
  2912  	// ~ pdfinfo -box pdf/Fpdf_PageBox.pdf
  2913  	// Producer:       FPDF 1.7
  2914  	// CreationDate:   Sat Jan  1 00:00:00 2000
  2915  	// ModDate:        Sun Jan  2 10:22:30 2000
  2916  	pdf := gofpdf.New("", "", "", "")
  2917  	pdf.AddPage()
  2918  	pdf.SetModificationDate(time.Date(2000, 1, 2, 10, 22, 30, 0, time.UTC))
  2919  	fileStr := example.Filename("Fpdf_SetModificationDate")
  2920  	err := pdf.OutputFileAndClose(fileStr)
  2921  	example.Summary(err, fileStr)
  2922  	// Output:
  2923  	// Successfully generated pdf/Fpdf_SetModificationDate.pdf
  2924  }