codeberg.org/go-pdf/fpdf@v0.11.1/fpdf_example_test.go (about)

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