github.com/unidoc/unidoc@v2.2.0+incompatible/pdf/creator/creator_test.go (about)

     1  /*
     2   * This file is subject to the terms and conditions defined in
     3   * file 'LICENSE.md', which is part of this source code package.
     4   */
     5  
     6  package creator
     7  
     8  // This test file contains multiple tests to generate PDFs. The outputs are written into /tmp as files.  The files
     9  // themselves need to be observed to check for correctness as we don't have a good way to automatically check
    10  // if every detail is correct.
    11  
    12  import (
    13  	"fmt"
    14  	goimage "image"
    15  	"io/ioutil"
    16  	"math"
    17  	"testing"
    18  
    19  	"github.com/boombuler/barcode"
    20  	"github.com/boombuler/barcode/qr"
    21  
    22  	"github.com/unidoc/unidoc/common"
    23  	"github.com/unidoc/unidoc/pdf/core"
    24  	"github.com/unidoc/unidoc/pdf/model"
    25  	"github.com/unidoc/unidoc/pdf/model/fonts"
    26  	"github.com/unidoc/unidoc/pdf/model/textencoding"
    27  )
    28  
    29  func init() {
    30  	common.SetLogger(common.NewConsoleLogger(common.LogLevelDebug))
    31  }
    32  
    33  const testPdfFile1 = "../../testfiles/minimal.pdf"
    34  const testPdfLoremIpsumFile = "../../testfiles/lorem.pdf"
    35  const testPdfTemplatesFile1 = "../../testfiles/templates1.pdf"
    36  const testImageFile1 = "../../testfiles/logo.png"
    37  const testImageFile2 = "../../testfiles/signature.png"
    38  const testRobotoRegularTTFFile = "../../testfiles/roboto/Roboto-Regular.ttf"
    39  const testRobotoBoldTTFFile = "../../testfiles/roboto/Roboto-Bold.ttf"
    40  
    41  func TestTemplate1(t *testing.T) {
    42  	creator := New()
    43  
    44  	pages, err := loadPagesFromFile(testPdfFile1)
    45  	if err != nil {
    46  		t.Errorf("Fail: %v\n", err)
    47  		return
    48  	}
    49  
    50  	template, err := NewBlockFromPage(pages[0])
    51  	if err != nil {
    52  		t.Errorf("Fail: %v\n", err)
    53  		return
    54  	}
    55  
    56  	template.SetPos(0, 0)
    57  	creator.Draw(template)
    58  
    59  	template.SetAngle(45)
    60  	creator.Draw(template)
    61  
    62  	template.Scale(0.5, 0.5)
    63  	creator.Draw(template)
    64  
    65  	template.Scale(4, 4)
    66  	creator.Draw(template)
    67  
    68  	template.SetAngle(90)
    69  	template.SetPos(100, 200)
    70  	creator.Draw(template)
    71  
    72  	creator.WriteToFile("/tmp/template_1.pdf")
    73  
    74  	return
    75  }
    76  
    77  // TestImage1 tests loading an image and adding to file at an absolute position.
    78  func TestImage1(t *testing.T) {
    79  	creator := New()
    80  
    81  	imgData, err := ioutil.ReadFile(testImageFile1)
    82  	if err != nil {
    83  		t.Errorf("Fail: %v\n", err)
    84  		return
    85  	}
    86  
    87  	img, err := NewImageFromData(imgData)
    88  	if err != nil {
    89  		t.Errorf("Fail: %v\n", err)
    90  		return
    91  	}
    92  
    93  	img.SetPos(0, 100)
    94  	img.ScaleToWidth(1.0 * creator.Width())
    95  
    96  	err = creator.Draw(img)
    97  	if err != nil {
    98  		t.Errorf("Fail: %v\n", err)
    99  		return
   100  	}
   101  
   102  	err = creator.WriteToFile("/tmp/1.pdf")
   103  	if err != nil {
   104  		t.Errorf("Fail: %v\n", err)
   105  		return
   106  	}
   107  }
   108  
   109  // TestImageWithEncoder tests loading inserting an image with a specified encoder.
   110  func TestImageWithEncoder(t *testing.T) {
   111  	creator := New()
   112  
   113  	imgData, err := ioutil.ReadFile(testImageFile1)
   114  	if err != nil {
   115  		t.Errorf("Fail: %v\n", err)
   116  		return
   117  	}
   118  
   119  	img, err := NewImageFromData(imgData)
   120  	if err != nil {
   121  		t.Errorf("Fail: %v\n", err)
   122  		return
   123  	}
   124  
   125  	// JPEG encoder (DCT) with quality factor 70.
   126  	encoder := core.NewDCTEncoder()
   127  	encoder.Quality = 70
   128  	encoder.Width = int(img.Width())
   129  	encoder.Height = int(img.Height())
   130  	img.SetEncoder(encoder)
   131  
   132  	img.SetPos(0, 100)
   133  	img.ScaleToWidth(1.0 * creator.Width())
   134  
   135  	err = creator.Draw(img)
   136  	if err != nil {
   137  		t.Errorf("Fail: %v\n", err)
   138  		return
   139  	}
   140  
   141  	err = creator.WriteToFile("/tmp/1_dct.pdf")
   142  	if err != nil {
   143  		t.Errorf("Fail: %v\n", err)
   144  		return
   145  	}
   146  }
   147  
   148  func TestShapes1(t *testing.T) {
   149  	creator := New()
   150  
   151  	imgData, err := ioutil.ReadFile(testImageFile1)
   152  	if err != nil {
   153  		t.Errorf("Fail: %v\n", err)
   154  		return
   155  	}
   156  
   157  	img, err := NewImageFromData(imgData)
   158  	if err != nil {
   159  		t.Errorf("Fail: %v\n", err)
   160  		return
   161  	}
   162  
   163  	img.SetPos(0, 100)
   164  	img.ScaleToWidth(1.0 * creator.Width())
   165  
   166  	err = creator.Draw(img)
   167  	if err != nil {
   168  		t.Errorf("Fail: %v\n", err)
   169  		return
   170  	}
   171  
   172  	// Add line.
   173  	line := NewLine(0, 0, 100, 100)
   174  	line.SetLineWidth(3.0)
   175  	line.SetColor(ColorRGBFromHex("#ff0000"))
   176  	err = creator.Draw(line)
   177  	if err != nil {
   178  		t.Errorf("Fail: %v\n", err)
   179  		return
   180  	}
   181  
   182  	// Add rect with default params.
   183  	rect := NewRectangle(100, 100, 100, 100)
   184  	err = creator.Draw(rect)
   185  	if err != nil {
   186  		t.Errorf("Fail: %v\n", err)
   187  		return
   188  	}
   189  
   190  	// Add rect with fill and large border
   191  	rect = NewRectangle(100, 500, 100, 100)
   192  	rect.SetBorderColor(ColorRGBFromHex("#00ff00")) // Green border
   193  	rect.SetBorderWidth(15.0)
   194  	rect.SetFillColor(ColorRGBFromHex("#0000ff")) // Blue fill
   195  	err = creator.Draw(rect)
   196  	if err != nil {
   197  		t.Errorf("Fail: %v\n", err)
   198  		return
   199  	}
   200  
   201  	// Draw a circle. (inscribed inside the previous rectangle).
   202  	ell := NewEllipse(100, 100, 100, 100)
   203  	err = creator.Draw(ell)
   204  	if err != nil {
   205  		t.Errorf("Fail: %v\n", err)
   206  		return
   207  	}
   208  
   209  	// Draw a circle around upper right page corner.
   210  	ell = NewEllipse(creator.Width(), 0, 100, 100)
   211  	err = creator.Draw(ell)
   212  	if err != nil {
   213  		t.Errorf("Fail: %v\n", err)
   214  		return
   215  	}
   216  
   217  	// Draw an ellipse with fill and border.
   218  	ell = NewEllipse(500, 100, 100, 200)
   219  	ell.SetFillColor(ColorRGBFromHex("#ccc")) // Gray fill
   220  	ell.SetBorderWidth(10.0)
   221  	err = creator.Draw(ell)
   222  	if err != nil {
   223  		t.Errorf("Fail: %v\n", err)
   224  		return
   225  	}
   226  
   227  	err = creator.WriteToFile("/tmp/1_shapes.pdf")
   228  	if err != nil {
   229  		t.Errorf("Fail: %v\n", err)
   230  		return
   231  	}
   232  }
   233  
   234  // Example drawing image and line shape on a block and applying to pages, also demonstrating block rotation.
   235  func TestShapesOnBlock(t *testing.T) {
   236  	creator := New()
   237  
   238  	block := NewBlock(creator.Width(), 200)
   239  
   240  	imgData, err := ioutil.ReadFile(testImageFile1)
   241  	if err != nil {
   242  		t.Errorf("Fail: %v\n", err)
   243  		return
   244  	}
   245  
   246  	img, err := NewImageFromData(imgData)
   247  	if err != nil {
   248  		t.Errorf("Fail: %v\n", err)
   249  		return
   250  	}
   251  
   252  	img.SetPos(50, 75)
   253  	img.ScaleToHeight(100.0)
   254  	block.Draw(img)
   255  
   256  	// Add line.
   257  	line := NewLine(0, 180, creator.Width(), 180)
   258  	line.SetLineWidth(10.0)
   259  	line.SetColor(ColorRGBFromHex("#ff0000"))
   260  	block.Draw(line)
   261  
   262  	creator.NewPage()
   263  	creator.MoveTo(0, 0)
   264  	creator.Draw(block)
   265  
   266  	creator.NewPage()
   267  	creator.MoveTo(0, 200)
   268  	creator.Draw(block)
   269  
   270  	creator.NewPage()
   271  	creator.MoveTo(0, 700)
   272  	block.SetAngle(90)
   273  	creator.Draw(block)
   274  
   275  	err = creator.WriteToFile("/tmp/1_shapes_on_block.pdf")
   276  	if err != nil {
   277  		t.Errorf("Fail: %v\n", err)
   278  		return
   279  	}
   280  }
   281  
   282  // Test image wrapping between pages when using relative context mode.
   283  func TestImageWrapping(t *testing.T) {
   284  	creator := New()
   285  
   286  	imgData, err := ioutil.ReadFile(testImageFile1)
   287  	if err != nil {
   288  		t.Errorf("Fail: %v\n", err)
   289  		return
   290  	}
   291  
   292  	img, err := NewImageFromData(imgData)
   293  	if err != nil {
   294  		t.Errorf("Fail: %v\n", err)
   295  		return
   296  	}
   297  
   298  	for j := 0; j < 40; j++ {
   299  		img.ScaleToWidth(100 + 10*float64(j+1))
   300  
   301  		err = creator.Draw(img)
   302  		if err != nil {
   303  			t.Errorf("Fail: %v\n", err)
   304  			return
   305  		}
   306  	}
   307  
   308  	err = creator.WriteToFile("/tmp/1_wrap.pdf")
   309  	if err != nil {
   310  		t.Errorf("Fail: %v\n", err)
   311  		return
   312  	}
   313  }
   314  
   315  // Test rotating image.  Rotating about upper left corner.
   316  func TestImageRotation(t *testing.T) {
   317  	creator := New()
   318  
   319  	imgData, err := ioutil.ReadFile(testImageFile1)
   320  	if err != nil {
   321  		t.Errorf("Fail: %v\n", err)
   322  		return
   323  	}
   324  
   325  	img, err := NewImageFromData(imgData)
   326  	if err != nil {
   327  		t.Errorf("Fail: %v\n", err)
   328  		return
   329  	}
   330  
   331  	//creator.MoveTo(0, 0)
   332  	img.ScaleToWidth(100)
   333  
   334  	angles := []float64{0, 90, 180, 270}
   335  	for _, angle := range angles {
   336  		creator.NewPage()
   337  
   338  		creator.MoveTo(100, 100)
   339  
   340  		img.SetAngle(angle)
   341  		err = creator.Draw(img)
   342  		if err != nil {
   343  			t.Errorf("Fail: %v\n", err)
   344  			return
   345  		}
   346  	}
   347  
   348  	err = creator.WriteToFile("/tmp/1_rotate.pdf")
   349  	if err != nil {
   350  		t.Errorf("Fail: %v\n", err)
   351  		return
   352  	}
   353  }
   354  
   355  // Test image, rotation and page wrapping.  Disadvantage here is that content is overlapping.  May be reconsidered
   356  // in the future.  And actually reconsider overall how images are used in the relative context mode.
   357  func TestImageRotationAndWrap(t *testing.T) {
   358  	creator := New()
   359  
   360  	imgData, err := ioutil.ReadFile(testImageFile1)
   361  	if err != nil {
   362  		t.Errorf("Fail: %v\n", err)
   363  		return
   364  	}
   365  
   366  	img, err := NewImageFromData(imgData)
   367  	if err != nil {
   368  		t.Errorf("Fail: %v\n", err)
   369  		return
   370  	}
   371  	img.ScaleToWidth(100)
   372  
   373  	creator.NewPage()
   374  
   375  	creator.MoveTo(100, 100)
   376  
   377  	angles := []float64{0, 90, 180, 270, 0, 90, 180, 270}
   378  	//angles := []float64{0, 0, 45, 90}
   379  
   380  	for _, angle := range angles {
   381  		img.SetAngle(angle)
   382  
   383  		err = creator.Draw(img)
   384  		if err != nil {
   385  			t.Errorf("Fail: %v\n", err)
   386  			return
   387  		}
   388  	}
   389  
   390  	err = creator.WriteToFile("/tmp/rotate_2.pdf")
   391  	if err != nil {
   392  		t.Errorf("Fail: %v\n", err)
   393  		return
   394  	}
   395  }
   396  
   397  // Test basic paragraph with default font.
   398  func TestParagraph1(t *testing.T) {
   399  	creator := New()
   400  
   401  	p := NewParagraph("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt" +
   402  		"ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut " +
   403  		"aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore" +
   404  		"eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt " +
   405  		"mollit anim id est laborum.")
   406  
   407  	err := creator.Draw(p)
   408  	if err != nil {
   409  		t.Errorf("Fail: %v\n", err)
   410  		return
   411  	}
   412  
   413  	err = creator.WriteToFile("/tmp/2_p1.pdf")
   414  	if err != nil {
   415  		t.Errorf("Fail: %v\n", err)
   416  		return
   417  	}
   418  }
   419  
   420  // Test paragraph and page and text wrapping with left, justify, center and right modes.
   421  // TODO: In the future we would like the paragraph to split up between pages.  Split up on line, never allowing
   422  // less than 2 lines to go over (common practice).
   423  // TODO: In the future we would like to implement Donald Knuth's line wrapping algorithm or something similar.
   424  func TestParagraphWrapping(t *testing.T) {
   425  	creator := New()
   426  
   427  	p := NewParagraph("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt" +
   428  		"ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut " +
   429  		"aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore" +
   430  		"eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt " +
   431  		"mollit anim id est laborum.")
   432  
   433  	p.SetMargins(0, 0, 5, 0)
   434  
   435  	alignments := []TextAlignment{TextAlignmentLeft, TextAlignmentJustify, TextAlignmentCenter, TextAlignmentRight}
   436  	for j := 0; j < 25; j++ {
   437  		//p.SetAlignment(alignments[j%4])
   438  		p.SetTextAlignment(alignments[1])
   439  
   440  		err := creator.Draw(p)
   441  		if err != nil {
   442  			t.Errorf("Fail: %v\n", err)
   443  			return
   444  		}
   445  	}
   446  
   447  	err := creator.WriteToFile("/tmp/2_pwrap.pdf")
   448  	if err != nil {
   449  		t.Errorf("Fail: %v\n", err)
   450  		return
   451  	}
   452  }
   453  
   454  func TestParagraphWrapping2(t *testing.T) {
   455  	creator := New()
   456  
   457  	p := NewParagraph("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt" +
   458  		"ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut " +
   459  		"aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore" +
   460  		"eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt " +
   461  		"mollit anim id est laborum.")
   462  
   463  	alignments := []TextAlignment{TextAlignmentLeft, TextAlignmentJustify, TextAlignmentCenter, TextAlignmentRight}
   464  	for j := 0; j < 25; j++ {
   465  		//p.SetAlignment(alignments[j%4])
   466  		p.SetMargins(50, 50, 50, 50)
   467  		p.SetTextAlignment(alignments[1])
   468  
   469  		err := creator.Draw(p)
   470  		if err != nil {
   471  			t.Errorf("Fail: %v\n", err)
   472  			return
   473  		}
   474  	}
   475  
   476  	err := creator.WriteToFile("/tmp/2_pwrap2.pdf")
   477  	if err != nil {
   478  		t.Errorf("Fail: %v\n", err)
   479  		return
   480  	}
   481  }
   482  
   483  // Test writing with TTF fonts.
   484  func TestParagraphFonts(t *testing.T) {
   485  	creator := New()
   486  
   487  	roboto, err := model.NewPdfFontFromTTFFile(testRobotoRegularTTFFile)
   488  	if err != nil {
   489  		t.Errorf("Fail: %v\n", err)
   490  		return
   491  	}
   492  
   493  	robotoBold, err := model.NewPdfFontFromTTFFile(testRobotoBoldTTFFile)
   494  	if err != nil {
   495  		t.Errorf("Fail: %v\n", err)
   496  		return
   497  	}
   498  
   499  	fonts := []fonts.Font{roboto, robotoBold, fonts.NewFontHelvetica(), roboto, robotoBold, fonts.NewFontHelvetica()}
   500  	for _, font := range fonts {
   501  		p := NewParagraph("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt" +
   502  			"ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut " +
   503  			"aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore" +
   504  			"eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt " +
   505  			"mollit anim id est laborum.")
   506  		p.SetFont(font)
   507  		p.SetFontSize(14)
   508  		//p.SetWrapWidth(0.8 * creator.Width())
   509  		p.SetTextAlignment(TextAlignmentJustify)
   510  		p.SetLineHeight(1.2)
   511  		p.SetMargins(0, 0, 5, 0)
   512  
   513  		err = creator.Draw(p)
   514  		if err != nil {
   515  			t.Errorf("Fail: %v\n", err)
   516  			return
   517  		}
   518  	}
   519  
   520  	err = creator.WriteToFile("/tmp/2_pArial.pdf")
   521  	if err != nil {
   522  		t.Errorf("Fail: %v\n", err)
   523  		return
   524  	}
   525  }
   526  
   527  // Test writing with the 14 built in fonts.
   528  func TestParagraphStandardFonts(t *testing.T) {
   529  	creator := New()
   530  
   531  	names := []string{
   532  		"Courier",
   533  		"Courier-Bold",
   534  		"Courier-BoldOblique",
   535  		"Courier-Oblique",
   536  		"Helvetica",
   537  		"Helvetica-Bold",
   538  		"Helvetica-BoldOblique",
   539  		"Helvetica-Oblique",
   540  		"Times-Roman",
   541  		"Times-Bold",
   542  		"Times-BoldItalic",
   543  		"Times-Italic",
   544  		"Symbol",
   545  		"ZapfDingbats",
   546  	}
   547  	fonts := []fonts.Font{
   548  		fonts.NewFontCourier(),
   549  		fonts.NewFontCourierBold(),
   550  		fonts.NewFontCourierBoldOblique(),
   551  		fonts.NewFontCourierOblique(),
   552  		fonts.NewFontHelvetica(),
   553  		fonts.NewFontHelveticaBold(),
   554  		fonts.NewFontHelveticaBoldOblique(),
   555  		fonts.NewFontHelveticaOblique(),
   556  		fonts.NewFontTimesRoman(),
   557  		fonts.NewFontTimesBold(),
   558  		fonts.NewFontTimesBoldItalic(),
   559  		fonts.NewFontTimesItalic(),
   560  		fonts.NewFontSymbol(),
   561  		fonts.NewFontZapfDingbats(),
   562  	}
   563  	texts := []string{
   564  		"Courier: Lorem ipsum dolor sit amet, consectetur adipiscing elit...",
   565  		"Courier-Bold: Lorem ipsum dolor sit amet, consectetur adipiscing elit...",
   566  		"Courier-BoldOblique: Lorem ipsum dolor sit amet, consectetur adipiscing elit...",
   567  		"Courier-Oblique: Lorem ipsum dolor sit amet, consectetur adipiscing elit...",
   568  
   569  		"Helvetica: Lorem ipsum dolor sit amet, consectetur adipiscing elit...",
   570  		"Helvetica-Bold: Lorem ipsum dolor sit amet, consectetur adipiscing elit...",
   571  		"Helvetica-BoldOblique: Lorem ipsum dolor sit amet, consectetur adipiscing elit...",
   572  		"Helvetica-Oblique: Lorem ipsum dolor sit amet, consectetur adipiscing elit...",
   573  
   574  		"Times-Roman: Lorem ipsum dolor sit amet, consectetur adipiscing elit...",
   575  		"Times-Bold: Lorem ipsum dolor sit amet, consectetur adipiscing elit...",
   576  		"Times-BoldItalic: Lorem ipsum dolor sit amet, consectetur adipiscing elit...",
   577  		"Times-Italic: Lorem ipsum dolor sit amet, consectetur adipiscing elit...",
   578  		"\u2206\u0393\u0020\u2192\u0020\u0030", // Delta Gamma space arrowright space zero (demonstrate Symbol font)
   579  		"\u2702\u0020\u2709\u261e\u2711\u2714", // a2 (scissors) space a117 (mail) a12 (finger) a17 (pen) a20 (checkmark)
   580  	}
   581  
   582  	for idx, font := range fonts {
   583  		p := NewParagraph(texts[idx])
   584  		p.SetFont(font)
   585  		p.SetFontSize(12)
   586  		p.SetLineHeight(1.2)
   587  		p.SetMargins(0, 0, 5, 0)
   588  
   589  		if names[idx] == "Symbol" {
   590  			// For Symbol font, need to use Symbol encoder.
   591  			p.SetEncoder(textencoding.NewSymbolEncoder())
   592  		} else if names[idx] == "ZapfDingbats" {
   593  			// Font ZapfDingbats font, need to use ZapfDingbats encoder.
   594  			p.SetEncoder(textencoding.NewZapfDingbatsEncoder())
   595  		}
   596  
   597  		err := creator.Draw(p)
   598  		if err != nil {
   599  			t.Errorf("Fail: %v\n", err)
   600  			return
   601  		}
   602  	}
   603  
   604  	err := creator.WriteToFile("/tmp/2_standard14fonts.pdf")
   605  	if err != nil {
   606  		t.Errorf("Fail: %v\n", err)
   607  		return
   608  	}
   609  }
   610  
   611  // Tests creating a chapter with paragraphs.
   612  func TestChapter(t *testing.T) {
   613  	c := New()
   614  
   615  	ch1 := c.NewChapter("Introduction")
   616  
   617  	p := NewParagraph("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt " +
   618  		"ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut " +
   619  		"aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore " +
   620  		"eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt " +
   621  		"mollit anim id est laborum.")
   622  	p.SetMargins(0, 0, 10, 0)
   623  
   624  	for j := 0; j < 55; j++ {
   625  		ch1.Add(p) // Can add any drawable..
   626  	}
   627  
   628  	c.Draw(ch1)
   629  
   630  	err := c.WriteToFile("/tmp/3_chapters.pdf")
   631  	if err != nil {
   632  		t.Errorf("Fail: %v\n", err)
   633  		return
   634  	}
   635  }
   636  
   637  // Tests creating a chapter with paragraphs.
   638  func TestChapterMargins(t *testing.T) {
   639  	c := New()
   640  
   641  	for j := 0; j < 20; j++ {
   642  		ch := c.NewChapter(fmt.Sprintf("Chapter %d", j+1))
   643  		if j < 5 {
   644  			ch.SetMargins(3*float64(j), 3*float64(j), 5+float64(j), 0)
   645  		}
   646  
   647  		p := NewParagraph("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt " +
   648  			"ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut " +
   649  			"aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore " +
   650  			"eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt " +
   651  			"mollit anim id est laborum.")
   652  		p.SetTextAlignment(TextAlignmentJustify)
   653  		ch.Add(p)
   654  		c.Draw(ch)
   655  	}
   656  
   657  	err := c.WriteToFile("/tmp/3_chapters_margins.pdf")
   658  	if err != nil {
   659  		t.Errorf("Fail: %v\n", err)
   660  		return
   661  	}
   662  }
   663  
   664  // Test creating and drawing subchapters with text content.
   665  // Also generates a front page, and a table of contents.
   666  func TestSubchaptersSimple(t *testing.T) {
   667  	c := New()
   668  
   669  	ch1 := c.NewChapter("Introduction")
   670  	subchap1 := c.NewSubchapter(ch1, "The fundamentals of the mastery of the most genious experiment of all times in modern world history. The story of the maker and the maker bot and the genius cow.")
   671  	subchap1.SetMargins(0, 0, 5, 0)
   672  
   673  	//subCh1 := NewSubchapter(ch1, "Workflow")
   674  
   675  	p := NewParagraph("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt " +
   676  		"ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut " +
   677  		"aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore " +
   678  		"eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt " +
   679  		"mollit anim id est laborum.")
   680  	p.SetTextAlignment(TextAlignmentJustify)
   681  	p.SetMargins(0, 0, 5, 0)
   682  	for j := 0; j < 1; j++ {
   683  		subchap1.Add(p)
   684  	}
   685  
   686  	subchap2 := c.NewSubchapter(ch1, "Mechanism")
   687  	subchap2.SetMargins(0, 0, 5, 0)
   688  	for j := 0; j < 1; j++ {
   689  		subchap2.Add(p)
   690  	}
   691  
   692  	subchap3 := c.NewSubchapter(ch1, "Discussion")
   693  	subchap3.SetMargins(0, 0, 5, 0)
   694  	for j := 0; j < 1; j++ {
   695  		subchap3.Add(p)
   696  	}
   697  
   698  	subchap4 := c.NewSubchapter(ch1, "Conclusion")
   699  	subchap4.SetMargins(0, 0, 5, 0)
   700  	for j := 0; j < 1; j++ {
   701  		subchap4.Add(p)
   702  	}
   703  	c.Draw(ch1)
   704  
   705  	ch2 := c.NewChapter("References")
   706  	for j := 0; j < 13; j++ {
   707  		ch2.Add(p)
   708  	}
   709  	c.Draw(ch2)
   710  
   711  	// Set a function to create the front Page.
   712  	c.CreateFrontPage(func(args FrontpageFunctionArgs) {
   713  		p := NewParagraph("Example Report")
   714  		p.SetWidth(c.Width())
   715  		p.SetTextAlignment(TextAlignmentCenter)
   716  		p.SetFontSize(32)
   717  		p.SetPos(0, 300)
   718  		c.Draw(p)
   719  
   720  		p.SetFontSize(22)
   721  		p.SetText("Example Report Data Results")
   722  		p.SetPos(0, 340)
   723  		c.Draw(p)
   724  	})
   725  
   726  	// Set a function to create the table of contents.
   727  	// Should be able to wrap..
   728  	c.CreateTableOfContents(func(toc *TableOfContents) (*Chapter, error) {
   729  		ch := c.NewChapter("Table of contents")
   730  		ch.GetHeading().SetColor(ColorRGBFromArithmetic(0.5, 0.5, 0.5))
   731  		ch.GetHeading().SetFontSize(28)
   732  		ch.GetHeading().SetMargins(0, 0, 0, 30)
   733  
   734  		table := NewTable(2) // 2 column table.
   735  		// Default, equal column sizes (4x0.25)...
   736  		table.SetColumnWidths(0.9, 0.1)
   737  
   738  		for _, entry := range toc.entries {
   739  			// Col 1. Chapter number, title.
   740  			var str string
   741  			if entry.Subchapter == 0 {
   742  				str = fmt.Sprintf("%d. %s", entry.Chapter, entry.Title)
   743  			} else {
   744  				str = fmt.Sprintf("        %d.%d. %s", entry.Chapter, entry.Subchapter, entry.Title)
   745  			}
   746  			p := NewParagraph(str)
   747  			p.SetFontSize(14)
   748  			cell := table.NewCell()
   749  			cell.SetContent(p)
   750  			// Set the paragraph width to the cell width.
   751  			p.SetWidth(cell.Width(c.Context()))
   752  			table.SetRowHeight(table.CurRow(), p.Height()*1.2)
   753  
   754  			// Col 1. Page number.
   755  			p = NewParagraph(fmt.Sprintf("%d", entry.PageNumber))
   756  			p.SetFontSize(14)
   757  			cell = table.NewCell()
   758  			cell.SetContent(p)
   759  		}
   760  		err := ch.Add(table)
   761  		if err != nil {
   762  			fmt.Printf("Error adding table: %v\n", err)
   763  			return nil, err
   764  		}
   765  
   766  		return ch, nil
   767  	})
   768  
   769  	err := c.WriteToFile("/tmp/3_subchapters_simple.pdf")
   770  	if err != nil {
   771  		t.Errorf("Fail: %v\n", err)
   772  		return
   773  	}
   774  }
   775  
   776  func TestSubchapters(t *testing.T) {
   777  	c := New()
   778  
   779  	ch1 := c.NewChapter("Introduction")
   780  	subchap1 := c.NewSubchapter(ch1, "The fundamentals")
   781  	subchap1.SetMargins(0, 0, 5, 0)
   782  
   783  	//subCh1 := NewSubchapter(ch1, "Workflow")
   784  
   785  	p := NewParagraph("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt " +
   786  		"ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut " +
   787  		"aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore " +
   788  		"eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt " +
   789  		"mollit anim id est laborum.")
   790  	p.SetTextAlignment(TextAlignmentJustify)
   791  	p.SetMargins(0, 0, 5, 0)
   792  	for j := 0; j < 18; j++ {
   793  		subchap1.Add(p)
   794  	}
   795  
   796  	subchap2 := c.NewSubchapter(ch1, "Mechanism")
   797  	subchap2.SetMargins(0, 0, 5, 0)
   798  	for j := 0; j < 15; j++ {
   799  		subchap2.Add(p)
   800  	}
   801  
   802  	subchap3 := c.NewSubchapter(ch1, "Discussion")
   803  	subchap3.SetMargins(0, 0, 5, 0)
   804  	for j := 0; j < 19; j++ {
   805  		subchap3.Add(p)
   806  	}
   807  
   808  	subchap4 := c.NewSubchapter(ch1, "Conclusion")
   809  	subchap4.SetMargins(0, 0, 5, 0)
   810  	for j := 0; j < 23; j++ {
   811  		subchap4.Add(p)
   812  	}
   813  
   814  	c.Draw(ch1)
   815  
   816  	for i := 0; i < 50; i++ {
   817  		ch2 := c.NewChapter("References")
   818  		for j := 0; j < 13; j++ {
   819  			ch2.Add(p)
   820  		}
   821  
   822  		c.Draw(ch2)
   823  	}
   824  
   825  	// Set a function to create the front Page.
   826  	c.CreateFrontPage(func(args FrontpageFunctionArgs) {
   827  		p := NewParagraph("Example Report")
   828  		p.SetWidth(c.Width())
   829  		p.SetTextAlignment(TextAlignmentCenter)
   830  		p.SetFontSize(32)
   831  		p.SetPos(0, 300)
   832  		c.Draw(p)
   833  
   834  		p.SetFontSize(22)
   835  		p.SetText("Example Report Data Results")
   836  		p.SetPos(0, 340)
   837  		c.Draw(p)
   838  	})
   839  
   840  	// Set a function to create the table of contents.
   841  	c.CreateTableOfContents(func(toc *TableOfContents) (*Chapter, error) {
   842  		ch := c.NewChapter("Table of contents")
   843  		ch.GetHeading().SetColor(ColorRGBFromArithmetic(0.5, 0.5, 0.5))
   844  		ch.GetHeading().SetFontSize(28)
   845  		ch.GetHeading().SetMargins(0, 0, 0, 30)
   846  
   847  		table := NewTable(2)
   848  		// Default, equal column sizes (4x0.25)...
   849  		table.SetColumnWidths(0.9, 0.1)
   850  
   851  		for _, entry := range toc.entries {
   852  			// Col 1. Chapter number, title.
   853  			var str string
   854  			if entry.Subchapter == 0 {
   855  				str = fmt.Sprintf("%d. %s", entry.Chapter, entry.Title)
   856  			} else {
   857  				str = fmt.Sprintf("        %d.%d. %s", entry.Chapter, entry.Subchapter, entry.Title)
   858  			}
   859  			p := NewParagraph(str)
   860  			p.SetFontSize(14)
   861  			cell := table.NewCell()
   862  			cell.SetContent(p)
   863  			// Set the paragraph width to the cell width.
   864  			p.SetWidth(cell.Width(c.Context()))
   865  			table.SetRowHeight(table.CurRow(), p.Height()*1.2)
   866  
   867  			// Col 1. Page number.
   868  			p = NewParagraph(fmt.Sprintf("%d", entry.PageNumber))
   869  			p.SetFontSize(14)
   870  			cell = table.NewCell()
   871  			cell.SetContent(p)
   872  		}
   873  		err := ch.Add(table)
   874  		if err != nil {
   875  			fmt.Printf("Error adding table: %v\n", err)
   876  			return nil, err
   877  		}
   878  
   879  		return ch, nil
   880  	})
   881  
   882  	addHeadersAndFooters(c)
   883  
   884  	err := c.WriteToFile("/tmp/3_subchapters.pdf")
   885  	if err != nil {
   886  		t.Errorf("Fail: %v\n", err)
   887  		return
   888  	}
   889  }
   890  
   891  // Test creating and drawing a table.
   892  func TestTable(t *testing.T) {
   893  	table := NewTable(4) // Mx4 table
   894  	// Default, equal column sizes (4x0.25)...
   895  	table.SetColumnWidths(0.5, 0.2, 0.2, 0.1)
   896  
   897  	cell := table.NewCell()
   898  	p := NewParagraph("1,1")
   899  	cell.SetContent(p)
   900  
   901  	cell = table.NewCell()
   902  	p = NewParagraph("1,2")
   903  	cell.SetContent(p)
   904  
   905  	cell = table.NewCell()
   906  	p = NewParagraph("1,3")
   907  	cell.SetContent(p)
   908  
   909  	cell = table.NewCell()
   910  	p = NewParagraph("1,4")
   911  	cell.SetContent(p)
   912  
   913  	cell = table.NewCell()
   914  	p = NewParagraph("2,1")
   915  	cell.SetContent(p)
   916  
   917  	cell = table.NewCell()
   918  	p = NewParagraph("2,2")
   919  	cell.SetContent(p)
   920  
   921  	table.SkipCells(1) // Skip over 2,3.
   922  
   923  	cell = table.NewCell()
   924  	p = NewParagraph("2,4")
   925  	cell.SetContent(p)
   926  
   927  	// Skip over two rows.
   928  	table.SkipRows(2)
   929  	cell = table.NewCell()
   930  	p = NewParagraph("4,4")
   931  	cell.SetContent(p)
   932  
   933  	// Move down 3 rows, 2 to the left.
   934  	table.SkipOver(3, -2)
   935  	cell = table.NewCell()
   936  	p = NewParagraph("7,2")
   937  	cell.SetContent(p)
   938  	cell.SetBackgroundColor(ColorRGBFrom8bit(255, 0, 0))
   939  
   940  	c := New()
   941  	c.Draw(table)
   942  
   943  	err := c.WriteToFile("/tmp/4_table.pdf")
   944  	if err != nil {
   945  		t.Errorf("Fail: %v\n", err)
   946  		return
   947  	}
   948  }
   949  
   950  func TestTableCellWrapping(t *testing.T) {
   951  	c := New()
   952  	c.NewPage()
   953  
   954  	table := NewTable(4) // Mx4 table
   955  	// Default, equal column sizes (4x0.25)...
   956  	table.SetColumnWidths(0.5, 0.2, 0.2, 0.1)
   957  
   958  	cell := table.NewCell()
   959  	p := NewParagraph("A Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")
   960  	cell.SetContent(p)
   961  	cell.SetBorder(CellBorderStyleBox, 1)
   962  	p.SetEnableWrap(true)
   963  	p.SetWidth(cell.Width(c.Context()))
   964  	p.SetTextAlignment(TextAlignmentJustify)
   965  
   966  	cell = table.NewCell()
   967  	cell.SetBorder(CellBorderStyleBox, 1)
   968  	p = NewParagraph("B Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.")
   969  	p.SetEnableWrap(true)
   970  	p.SetTextAlignment(TextAlignmentRight)
   971  	cell.SetContent(p)
   972  
   973  	cell = table.NewCell()
   974  	p = NewParagraph("C Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")
   975  	p.SetEnableWrap(true)
   976  	cell.SetContent(p)
   977  	cell.SetBorder(CellBorderStyleBox, 1)
   978  
   979  	cell = table.NewCell()
   980  	p = NewParagraph("1,4")
   981  	cell.SetContent(p)
   982  	cell.SetBorder(CellBorderStyleBox, 1)
   983  
   984  	cell = table.NewCell()
   985  	p = NewParagraph("2,1")
   986  	cell.SetContent(p)
   987  	cell.SetBorder(CellBorderStyleBox, 1)
   988  
   989  	cell = table.NewCell()
   990  	p = NewParagraph("2,2")
   991  	cell.SetContent(p)
   992  	cell.SetBorder(CellBorderStyleBox, 1)
   993  
   994  	cell = table.NewCell()
   995  	p = NewParagraph("2,2")
   996  	cell.SetContent(p)
   997  	cell.SetBorder(CellBorderStyleBox, 1)
   998  
   999  	//table.SkipCells(1) // Skip over 2,3.
  1000  
  1001  	cell = table.NewCell()
  1002  	cell.SetBorder(CellBorderStyleBox, 1)
  1003  	//p = NewParagraph("D Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")
  1004  	p = NewParagraph("X")
  1005  	p.SetEnableWrap(true)
  1006  	cell.SetContent(p)
  1007  
  1008  	// Skip over two rows.
  1009  	table.SkipRows(2)
  1010  	cell = table.NewCell()
  1011  	cell.SetBorder(CellBorderStyleBox, 1)
  1012  	p = NewParagraph("4,4")
  1013  	cell.SetContent(p)
  1014  
  1015  	// Move down 3 rows, 2 to the left.
  1016  	table.SkipOver(3, -2)
  1017  	cell = table.NewCell()
  1018  	p = NewParagraph("7,2")
  1019  	cell.SetContent(p)
  1020  	cell.SetBackgroundColor(ColorRGBFrom8bit(255, 0, 0))
  1021  
  1022  	table.SkipRows(1)
  1023  	cell = table.NewCell()
  1024  	cell.SetBorder(CellBorderStyleBox, 1)
  1025  	p = NewParagraph("This is\nnewline\nwrapped\n\nmulti")
  1026  	p.SetEnableWrap(true)
  1027  	cell.SetContent(p)
  1028  
  1029  	err := c.Draw(table)
  1030  	if err != nil {
  1031  		t.Fatalf("Error drawing: %v", err)
  1032  	}
  1033  
  1034  	err = c.WriteToFile("/tmp/tablecell_wrap.pdf")
  1035  	if err != nil {
  1036  		t.Fatalf("Fail: %v\n", err)
  1037  	}
  1038  }
  1039  
  1040  // Test creating and drawing a table.
  1041  func TestBorderedTable(t *testing.T) {
  1042  	table := NewTable(4) // Mx4 table
  1043  	// Default, equal column sizes (4x0.25)...
  1044  	table.SetColumnWidths(0.5, 0.2, 0.2, 0.1)
  1045  
  1046  	cell := table.NewCell()
  1047  	p := NewParagraph("1,1")
  1048  	cell.SetContent(p)
  1049  	cell.SetBorder(CellBorderStyleBox, 1)
  1050  
  1051  	cell = table.NewCell()
  1052  	p = NewParagraph("1,2")
  1053  	cell.SetContent(p)
  1054  	cell.SetBorder(CellBorderStyleBox, 1)
  1055  
  1056  	cell = table.NewCell()
  1057  	p = NewParagraph("1,3")
  1058  	cell.SetContent(p)
  1059  	cell.SetBorder(CellBorderStyleBox, 1)
  1060  
  1061  	cell = table.NewCell()
  1062  	p = NewParagraph("1,4")
  1063  	cell.SetContent(p)
  1064  	cell.SetBorder(CellBorderStyleBox, 1)
  1065  
  1066  	cell = table.NewCell()
  1067  	p = NewParagraph("2,1")
  1068  	cell.SetContent(p)
  1069  	cell.SetBorder(CellBorderStyleBox, 1)
  1070  
  1071  	cell = table.NewCell()
  1072  	p = NewParagraph("2,2")
  1073  	cell.SetContent(p)
  1074  	cell.SetBorder(CellBorderStyleBox, 1)
  1075  
  1076  	table.SkipCells(1) // Skip over 2,3.
  1077  
  1078  	cell = table.NewCell()
  1079  	p = NewParagraph("2,4")
  1080  	cell.SetContent(p)
  1081  	cell.SetBorder(CellBorderStyleBox, 1)
  1082  
  1083  	// Skip over two rows.
  1084  	table.SkipRows(2)
  1085  	cell = table.NewCell()
  1086  	p = NewParagraph("4,4")
  1087  	cell.SetContent(p)
  1088  	cell.SetBorder(CellBorderStyleBox, 1)
  1089  
  1090  	// Move down 3 rows, 2 to the left.
  1091  	table.SkipOver(3, -2)
  1092  	cell = table.NewCell()
  1093  	p = NewParagraph("7,2")
  1094  	cell.SetContent(p)
  1095  	cell.SetBackgroundColor(ColorRGBFrom8bit(255, 0, 0))
  1096  	cell.SetBorder(CellBorderStyleBox, 1)
  1097  
  1098  	c := New()
  1099  	c.Draw(table)
  1100  
  1101  	err := c.WriteToFile("/tmp/4_table_bordered.pdf")
  1102  	if err != nil {
  1103  		t.Errorf("Fail: %v\n", err)
  1104  		return
  1105  	}
  1106  }
  1107  
  1108  func TestTableInSubchapter(t *testing.T) {
  1109  	c := New()
  1110  
  1111  	fontRegular := fonts.NewFontHelvetica()
  1112  	fontBold := fonts.NewFontHelveticaBold()
  1113  
  1114  	ch := c.NewChapter("Document control")
  1115  	ch.SetMargins(0, 0, 40, 0)
  1116  	ch.GetHeading().SetFont(fontRegular)
  1117  	ch.GetHeading().SetFontSize(18)
  1118  	ch.GetHeading().SetColor(ColorRGBFrom8bit(72, 86, 95))
  1119  
  1120  	sc := c.NewSubchapter(ch, "Issuer details")
  1121  	sc.SetMargins(0, 0, 5, 0)
  1122  	sc.GetHeading().SetFont(fontRegular)
  1123  	sc.GetHeading().SetFontSize(18)
  1124  	sc.GetHeading().SetColor(ColorRGBFrom8bit(72, 86, 95))
  1125  
  1126  	issuerTable := NewTable(2)
  1127  
  1128  	p := NewParagraph("Non-Disclosure")
  1129  	p.SetFont(fontBold)
  1130  	p.SetFontSize(10)
  1131  	p.SetColor(ColorWhite)
  1132  	cell := issuerTable.NewCell()
  1133  	cell.SetContent(p)
  1134  	cell.SetBackgroundColor(ColorBlack)
  1135  	cell.SetBorder(CellBorderStyleBox, 1.0)
  1136  	cell.SetIndent(5)
  1137  
  1138  	p = NewParagraph("Company Inc.")
  1139  	p.SetFont(fontRegular)
  1140  	p.SetFontSize(10)
  1141  	p.SetColor(ColorGreen)
  1142  	cell = issuerTable.NewCell()
  1143  	cell.SetContent(p)
  1144  	cell.SetBackgroundColor(ColorRed)
  1145  	cell.SetBorder(CellBorderStyleBox, 1.0)
  1146  	cell.SetIndent(5)
  1147  
  1148  	p = NewParagraph("Belongs to")
  1149  	p.SetFont(fontBold)
  1150  	p.SetFontSize(10)
  1151  	p.SetColor(ColorWhite)
  1152  	cell = issuerTable.NewCell()
  1153  	cell.SetContent(p)
  1154  	cell.SetBackgroundColor(ColorBlack)
  1155  	cell.SetBorder(CellBorderStyleBox, 1.0)
  1156  	cell.SetIndent(5)
  1157  
  1158  	p = NewParagraph("Bezt business bureu")
  1159  	p.SetFont(fontRegular)
  1160  	p.SetFontSize(10)
  1161  	p.SetColor(ColorGreen)
  1162  	cell = issuerTable.NewCell()
  1163  	cell.SetContent(p)
  1164  	cell.SetBackgroundColor(ColorRed)
  1165  	cell.SetBorder(CellBorderStyleBox, 1.0)
  1166  	cell.SetIndent(5)
  1167  	cell.SetHorizontalAlignment(CellHorizontalAlignmentCenter)
  1168  	//cell.SetVerticalAlignment(CellVerticalAlignmentMiddle)
  1169  
  1170  	issuerTable.SetMargins(5, 5, 10, 10)
  1171  
  1172  	ch.Add(issuerTable)
  1173  
  1174  	sc = c.NewSubchapter(ch, "My Statement")
  1175  	//sc.SetMargins(0, 0, 5, 0)
  1176  	sc.GetHeading().SetFont(fontRegular)
  1177  	sc.GetHeading().SetFontSize(18)
  1178  	sc.GetHeading().SetColor(ColorRGBFrom8bit(72, 86, 95))
  1179  
  1180  	myText := "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt " +
  1181  		"ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut " +
  1182  		"aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore " +
  1183  		"eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt " +
  1184  		"mollit anim id est laborum."
  1185  
  1186  	myPara := NewParagraph(myText)
  1187  	myPara.SetFont(fontRegular)
  1188  	myPara.SetFontSize(10)
  1189  	myPara.SetColor(ColorRGBFrom8bit(72, 86, 95))
  1190  	myPara.SetTextAlignment(TextAlignmentJustify)
  1191  	myPara.SetLineHeight(1.5)
  1192  	sc.Add(myPara)
  1193  
  1194  	err := c.Draw(ch)
  1195  	if err != nil {
  1196  		t.Errorf("Fail: %v\n", err)
  1197  		return
  1198  	}
  1199  
  1200  	err = c.WriteToFile("/tmp/4_tables_in_subchap.pdf")
  1201  	if err != nil {
  1202  		t.Errorf("Fail: %v\n", err)
  1203  		return
  1204  	}
  1205  }
  1206  
  1207  // Add headers and footers via creator.
  1208  func addHeadersAndFooters(c *Creator) {
  1209  	c.DrawHeader(func(header *Block, args HeaderFunctionArgs) {
  1210  		/*
  1211  			if pageNum == 1 {
  1212  				// Skip on front Page.
  1213  				return
  1214  			}
  1215  		*/
  1216  
  1217  		// Add Page number
  1218  		p := NewParagraph(fmt.Sprintf("Page %d / %d", args.PageNum, args.TotalPages))
  1219  		p.SetPos(0.8*header.Width(), 20)
  1220  		header.Draw(p)
  1221  
  1222  		// Draw on the template...
  1223  		img, err := NewImageFromFile(testImageFile1)
  1224  		if err != nil {
  1225  			fmt.Printf("ERROR : %v\n", err)
  1226  		}
  1227  		img.ScaleToHeight(0.4 * c.pageMargins.top)
  1228  		img.SetPos(20, 10)
  1229  
  1230  		header.Draw(img)
  1231  	})
  1232  
  1233  	c.DrawFooter(func(footer *Block, args FooterFunctionArgs) {
  1234  		/*
  1235  			if pageNum == 1 {
  1236  				// Skip on front Page.
  1237  				return
  1238  			}
  1239  		*/
  1240  
  1241  		// Add company name.
  1242  		companyName := "Company inc."
  1243  		p := NewParagraph(companyName)
  1244  		p.SetPos(0.1*footer.Width(), 10)
  1245  		footer.Draw(p)
  1246  
  1247  		p = NewParagraph("July 2017")
  1248  		p.SetPos(0.8*footer.Width(), 10)
  1249  		footer.Draw(p)
  1250  	})
  1251  }
  1252  
  1253  // Test creating headers and footers.
  1254  func TestHeadersAndFooters(t *testing.T) {
  1255  	c := New()
  1256  
  1257  	ch1 := c.NewChapter("Introduction")
  1258  
  1259  	p := NewParagraph("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt " +
  1260  		"ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut " +
  1261  		"aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore " +
  1262  		"eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt " +
  1263  		"mollit anim id est laborum.")
  1264  	p.SetMargins(0, 0, 10, 0)
  1265  
  1266  	for j := 0; j < 55; j++ {
  1267  		ch1.Add(p) // Can add any drawable..
  1268  	}
  1269  
  1270  	c.Draw(ch1)
  1271  
  1272  	// Make unidoc headers and footers.
  1273  	addHeadersAndFooters(c)
  1274  
  1275  	err := c.WriteToFile("/tmp/4_headers.pdf")
  1276  	if err != nil {
  1277  		t.Errorf("Fail: %v\n", err)
  1278  		return
  1279  	}
  1280  }
  1281  
  1282  func makeQrCodeImage(text string, width float64, oversampling int) (goimage.Image, error) {
  1283  	qrCode, err := qr.Encode(text, qr.M, qr.Auto)
  1284  	if err != nil {
  1285  		return nil, err
  1286  	}
  1287  
  1288  	pixelWidth := oversampling * int(math.Ceil(width))
  1289  	qrCode, err = barcode.Scale(qrCode, pixelWidth, pixelWidth)
  1290  	if err != nil {
  1291  		return nil, err
  1292  	}
  1293  
  1294  	return qrCode, nil
  1295  }
  1296  
  1297  func TestQRCodeOnNewPage(t *testing.T) {
  1298  	creator := New()
  1299  
  1300  	qrCode, err := makeQrCodeImage("HELLO", 40, 5)
  1301  	if err != nil {
  1302  		t.Errorf("Fail: %v\n", err)
  1303  		return
  1304  	}
  1305  
  1306  	img, err := NewImageFromGoImage(qrCode)
  1307  	if err != nil {
  1308  		t.Errorf("Fail: %v\n", err)
  1309  		return
  1310  	}
  1311  	// Sets positioning to absolute coordinates.
  1312  	img.SetWidth(40)
  1313  	img.SetHeight(40)
  1314  
  1315  	for i := 0; i < 5; i++ {
  1316  		img.SetPos(100.0*float64(i), 100.0*float64(i))
  1317  		creator.Draw(img)
  1318  	}
  1319  
  1320  	creator.WriteToFile("/tmp/3_barcode_qr_newpage.pdf")
  1321  }
  1322  
  1323  // Example of using a template Page, generating and applying QR
  1324  func TestQRCodeOnTemplate(t *testing.T) {
  1325  	pages, err := loadPagesFromFile(testPdfTemplatesFile1)
  1326  	if err != nil {
  1327  		t.Errorf("Fail: %v\n", err)
  1328  		return
  1329  	}
  1330  	if len(pages) < 2 {
  1331  		t.Errorf("Fail: %v\n", err)
  1332  		return
  1333  	}
  1334  
  1335  	// Load Page 1 as template.
  1336  	tpl, err := NewBlockFromPage(pages[1])
  1337  	if err != nil {
  1338  		t.Errorf("Fail: %v\n", err)
  1339  		return
  1340  	}
  1341  	tpl.SetPos(0, 0)
  1342  
  1343  	// Generate QR code.
  1344  	qrCode, err := makeQrCodeImage("HELLO", 50, 5)
  1345  	if err != nil {
  1346  		t.Errorf("Fail: %v\n", err)
  1347  		return
  1348  	}
  1349  
  1350  	// Prepare content image.
  1351  	image, err := NewImageFromGoImage(qrCode)
  1352  	if err != nil {
  1353  		t.Errorf("Fail: %v\n", err)
  1354  		return
  1355  	}
  1356  	image.SetWidth(50)
  1357  	image.SetHeight(50)
  1358  	image.SetPos(480, 100)
  1359  
  1360  	tpl.Draw(image)
  1361  
  1362  	creator := New()
  1363  	creator.NewPage()
  1364  	creator.Draw(tpl)
  1365  
  1366  	// Add another Page where the template has been rotated.
  1367  	creator.NewPage()
  1368  	tpl.SetAngle(90)
  1369  	tpl.SetPos(-50, 750)
  1370  
  1371  	creator.Draw(tpl)
  1372  
  1373  	// Add another Page where the template is rotated 90 degrees.
  1374  	loremPages, err := loadPagesFromFile(testPdfLoremIpsumFile)
  1375  	if err != nil {
  1376  		t.Errorf("Fail: %v\n", err)
  1377  		return
  1378  	}
  1379  	if len(loremPages) != 1 {
  1380  		t.Errorf("Pages != 1")
  1381  		return
  1382  	}
  1383  
  1384  	// Add another Page where another Page is embedded on the Page.  The other Page is scaled and shifted to fit
  1385  	// on the right of the template.
  1386  	loremTpl, err := NewBlockFromPage(loremPages[0])
  1387  	if err != nil {
  1388  		t.Errorf("Fail: %v\n", err)
  1389  		return
  1390  	}
  1391  	loremTpl.ScaleToWidth(0.8 * creator.Width())
  1392  	loremTpl.SetPos(100, 100)
  1393  
  1394  	creator.Draw(loremTpl)
  1395  
  1396  	// Write the example to file.
  1397  	creator.WriteToFile("/tmp/4_barcode_on_tpl.pdf")
  1398  }
  1399  
  1400  // Test adding encryption to output.
  1401  func TestEncrypting1(t *testing.T) {
  1402  	c := New()
  1403  
  1404  	ch1 := c.NewChapter("Introduction")
  1405  
  1406  	p := NewParagraph("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt " +
  1407  		"ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut " +
  1408  		"aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore " +
  1409  		"eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt " +
  1410  		"mollit anim id est laborum.")
  1411  	p.SetMargins(0, 0, 10, 0)
  1412  
  1413  	for j := 0; j < 55; j++ {
  1414  		ch1.Add(p) // Can add any drawable..
  1415  	}
  1416  
  1417  	c.Draw(ch1)
  1418  
  1419  	c.SetPdfWriterAccessFunc(func(w *model.PdfWriter) error {
  1420  		userPass := []byte("password")
  1421  		ownerPass := []byte("password")
  1422  		err := w.Encrypt(userPass, ownerPass, nil)
  1423  		return err
  1424  	})
  1425  
  1426  	err := c.WriteToFile("/tmp/6_chapters_encrypted_password.pdf")
  1427  	if err != nil {
  1428  		t.Errorf("Fail: %v\n", err)
  1429  		return
  1430  	}
  1431  }