| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294 | 
							- // Package wkhtmltopdf contains wrappers around the wkhtmltopdf commandline tool
 
- package wkhtmltopdf
 
- import (
 
- 	"bytes"
 
- 	"errors"
 
- 	"fmt"
 
- 	"io"
 
- 	"io/ioutil"
 
- 	"os"
 
- 	"os/exec"
 
- 	"path/filepath"
 
- 	"strings"
 
- )
 
- var binPath string //the cached paths as used by findPath()
 
- // SetPath sets the path to wkhtmltopdf
 
- func SetPath(path string) {
 
- 	binPath = path
 
- }
 
- // GetPath gets the path to wkhtmltopdf
 
- func GetPath() string {
 
- 	return binPath
 
- }
 
- // Page is the input struct for each page
 
- type Page struct {
 
- 	Input string
 
- 	PageOptions
 
- }
 
- // InputFile returns the input string and is part of the page interface
 
- func (p *Page) InputFile() string {
 
- 	return p.Input
 
- }
 
- // Args returns the argument slice and is part of the page interface
 
- func (p *Page) Args() []string {
 
- 	return p.PageOptions.Args()
 
- }
 
- // Reader returns the io.Reader and is part of the page interface
 
- func (p *Page) Reader() io.Reader {
 
- 	return nil
 
- }
 
- // NewPage creates a new input page from a local or web resource (filepath or URL)
 
- func NewPage(input string) *Page {
 
- 	return &Page{
 
- 		Input:       input,
 
- 		PageOptions: NewPageOptions(),
 
- 	}
 
- }
 
- // PageReader is one input page (a HTML document) that is read from an io.Reader
 
- // You can add only one Page from a reader
 
- type PageReader struct {
 
- 	Input io.Reader
 
- 	PageOptions
 
- }
 
- // InputFile returns the input string and is part of the page interface
 
- func (pr *PageReader) InputFile() string {
 
- 	return "-"
 
- }
 
- // Args returns the argument slice and is part of the page interface
 
- func (pr *PageReader) Args() []string {
 
- 	return pr.PageOptions.Args()
 
- }
 
- //Reader returns the io.Reader and is part of the page interface
 
- func (pr *PageReader) Reader() io.Reader {
 
- 	return pr.Input
 
- }
 
- // NewPageReader creates a new PageReader from an io.Reader
 
- func NewPageReader(input io.Reader) *PageReader {
 
- 	return &PageReader{
 
- 		Input:       input,
 
- 		PageOptions: NewPageOptions(),
 
- 	}
 
- }
 
- type page interface {
 
- 	Args() []string
 
- 	InputFile() string
 
- 	Reader() io.Reader
 
- }
 
- // PageOptions are options for each input page
 
- type PageOptions struct {
 
- 	pageOptions
 
- 	headerAndFooterOptions
 
- }
 
- // Args returns the argument slice
 
- func (po *PageOptions) Args() []string {
 
- 	return append(append([]string{}, po.pageOptions.Args()...), po.headerAndFooterOptions.Args()...)
 
- }
 
- // NewPageOptions returns a new PageOptions struct with all options
 
- func NewPageOptions() PageOptions {
 
- 	return PageOptions{
 
- 		pageOptions:            newPageOptions(),
 
- 		headerAndFooterOptions: newHeaderAndFooterOptions(),
 
- 	}
 
- }
 
- // cover page
 
- type cover struct {
 
- 	Input string
 
- 	pageOptions
 
- }
 
- // table of contents
 
- type toc struct {
 
- 	Include bool
 
- 	allTocOptions
 
- }
 
- type allTocOptions struct {
 
- 	pageOptions
 
- 	tocOptions
 
- }
 
- // PDFGenerator is the main wkhtmltopdf struct, always use NewPDFGenerator to obtain a new PDFGenerator struct
 
- type PDFGenerator struct {
 
- 	globalOptions
 
- 	outlineOptions
 
- 	Cover      cover
 
- 	TOC        toc
 
- 	OutputFile string //filename to write to, default empty (writes to internal buffer)
 
- 	binPath string
 
- 	outbuf  bytes.Buffer
 
- 	pages   []page
 
- }
 
- //Args returns the commandline arguments as a string slice
 
- func (pdfg *PDFGenerator) Args() []string {
 
- 	args := []string{}
 
- 	args = append(args, pdfg.globalOptions.Args()...)
 
- 	args = append(args, pdfg.outlineOptions.Args()...)
 
- 	if pdfg.Cover.Input != "" {
 
- 		args = append(args, "cover")
 
- 		args = append(args, pdfg.Cover.Input)
 
- 		args = append(args, pdfg.Cover.pageOptions.Args()...)
 
- 	}
 
- 	if pdfg.TOC.Include {
 
- 		args = append(args, "toc")
 
- 		args = append(args, pdfg.TOC.pageOptions.Args()...)
 
- 		args = append(args, pdfg.TOC.tocOptions.Args()...)
 
- 	}
 
- 	for _, page := range pdfg.pages {
 
- 		args = append(args, "page")
 
- 		args = append(args, page.InputFile())
 
- 		args = append(args, page.Args()...)
 
- 	}
 
- 	if pdfg.OutputFile != "" {
 
- 		args = append(args, pdfg.OutputFile)
 
- 	} else {
 
- 		args = append(args, "-")
 
- 	}
 
- 	return args
 
- }
 
- // ArgString returns Args as a single string
 
- func (pdfg *PDFGenerator) ArgString() string {
 
- 	return strings.Join(pdfg.Args(), " ")
 
- }
 
- // AddPage adds a new input page to the document.
 
- // A page is an input HTML page, it can span multiple pages in the output document.
 
- // It is a Page when read from file or URL or a PageReader when read from memory.
 
- func (pdfg *PDFGenerator) AddPage(p page) {
 
- 	pdfg.pages = append(pdfg.pages, p)
 
- }
 
- // SetPages resets all pages
 
- func (pdfg *PDFGenerator) SetPages(p []page) {
 
- 	pdfg.pages = p
 
- }
 
- // Buffer returns the embedded output buffer used if OutputFile is empty
 
- func (pdfg *PDFGenerator) Buffer() *bytes.Buffer {
 
- 	return &pdfg.outbuf
 
- }
 
- // Bytes returns the output byte slice from the output buffer used if OutputFile is empty
 
- func (pdfg *PDFGenerator) Bytes() []byte {
 
- 	return pdfg.outbuf.Bytes()
 
- }
 
- // WriteFile writes the contents of the output buffer to a file
 
- func (pdfg *PDFGenerator) WriteFile(filename string) error {
 
- 	return ioutil.WriteFile(filename, pdfg.Bytes(), 0666)
 
- }
 
- //findPath finds the path to wkhtmltopdf by
 
- //- first looking in the current dir
 
- //- looking in the PATH and PATHEXT environment dirs
 
- //- using the WKHTMLTOPDF_PATH environment dir
 
- //The path is cached, meaning you can not change the location of wkhtmltopdf in
 
- //a running program once it has been found
 
- func (pdfg *PDFGenerator) findPath() error {
 
- 	const exe = "wkhtmltopdf"
 
- 	if binPath != "" {
 
- 		pdfg.binPath = binPath
 
- 		return nil
 
- 	}
 
- 	exeDir, err := filepath.Abs(filepath.Dir(os.Args[0]))
 
- 	if err != nil {
 
- 		return err
 
- 	}
 
- 	path, err := exec.LookPath(filepath.Join(exeDir, exe))
 
- 	if err == nil && path != "" {
 
- 		binPath = path
 
- 		pdfg.binPath = path
 
- 		return nil
 
- 	}
 
- 	path, err = exec.LookPath(exe)
 
- 	if err == nil && path != "" {
 
- 		binPath = path
 
- 		pdfg.binPath = path
 
- 		return nil
 
- 	}
 
- 	dir := os.Getenv("WKHTMLTOPDF_PATH")
 
- 	if dir == "" {
 
- 		return fmt.Errorf("%s not found", exe)
 
- 	}
 
- 	path, err = exec.LookPath(filepath.Join(dir, exe))
 
- 	if err == nil && path != "" {
 
- 		binPath = path
 
- 		pdfg.binPath = path
 
- 		return nil
 
- 	}
 
- 	return fmt.Errorf("%s not found", exe)
 
- }
 
- // Create creates the PDF document and stores it in the internal buffer if no error is returned
 
- func (pdfg *PDFGenerator) Create() error {
 
- 	return pdfg.run()
 
- }
 
- func (pdfg *PDFGenerator) run() error {
 
- 	errbuf := &bytes.Buffer{}
 
- 	cmd := exec.Command(pdfg.binPath, pdfg.Args()...)
 
- 	cmd.Stdout = &pdfg.outbuf
 
- 	cmd.Stderr = errbuf
 
- 	//if there is a pageReader page (from Stdin) we set Stdin to that reader
 
- 	for _, page := range pdfg.pages {
 
- 		if page.Reader() != nil {
 
- 			cmd.Stdin = page.Reader()
 
- 			break
 
- 		}
 
- 	}
 
- 	err := cmd.Run()
 
- 	if err != nil {
 
- 		errStr := errbuf.String()
 
- 		if strings.TrimSpace(errStr) == "" {
 
- 			errStr = err.Error()
 
- 		}
 
- 		return errors.New(errStr)
 
- 	}
 
- 	return nil
 
- }
 
- // NewPDFGenerator returns a new PDFGenerator struct with all options created and
 
- // checks if wkhtmltopdf can be found on the system
 
- func NewPDFGenerator() (*PDFGenerator, error) {
 
- 	pdfg := &PDFGenerator{
 
- 		globalOptions:  newGlobalOptions(),
 
- 		outlineOptions: newOutlineOptions(),
 
- 		Cover: cover{
 
- 			pageOptions: newPageOptions(),
 
- 		},
 
- 		TOC: toc{
 
- 			allTocOptions: allTocOptions{
 
- 				tocOptions:  newTocOptions(),
 
- 				pageOptions: newPageOptions(),
 
- 			},
 
- 		},
 
- 	}
 
- 	err := pdfg.findPath()
 
- 	return pdfg, err
 
- }
 
 
  |