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 }