func (p *PDF) PullError() error {
	if len(p.errors) == 0 {
		return nil
	}
	ret := p.errors[0]
	p.errors = p.errors[1:]
	return ret
}
PullError removes and returns the first error from the errors collection.
package pdf
import (
	"bytes"
	"compress/zlib"
	"crypto/sha512"
	"fmt"
	"image"
	"image/color"
	_ "image/gif"
	_ "image/jpeg"
	_ "image/png"  
	"io"
	"os"
	"reflect"
	"runtime"
	"strconv"
	"strings"
	"unicode"  
)
type PDF struct {
	paperSize    pdfPaperSize  
	pageNo       int           
	page         *pdfPage      
	pages        []pdfPage     
	fonts        []pdfFont     
	images       []pdfImage    
	columnWidths []float64     
	columnNo     int           
	units        string        
	ptPerUnit    float64       
	color        color.RGBA    
	lineWidth    float64       
	font         *pdfFont      
	fontName     string        
	fontSizePt   float64       
	horzScaling  uint16        
	compression  bool          
	content      bytes.Buffer  
	writer       io.Writer     
	objOffsets   []int         
	objIndex     int           
	errors       []error       
	isInit       bool          
	docAuthor, docCreator, docKeywords, docSubject, docTitle string
}  
func NewPDF(paperSize string) PDF {
	var p PDF
	size, err := p.init().getPaperSize(paperSize)
	if err, isT := err.(pdfError); isT {
		p.putError(0xE52F92, err.msg, paperSize)
		p.paperSize, _ = p.getPaperSize("A4")
	}
	p.paperSize = size
	return p
}  
var pdfNewFontHandler func() pdfFontHandler
type pdfFontHandler interface {
	readFont(owner *PDF, font interface{}) bool
	textWidthPt(s string) float64
	writeText(s string)
	writeFontObjects(font *pdfFont)
}  
func (p *PDF) PageCount() int { p.reservePage(); return len(p.pages) }
func (p *PDF) PageHeight() float64 { return p.ToUnits(p.paperSize.heightPt) }
func (p *PDF) PageWidth() float64 { return p.ToUnits(p.paperSize.widthPt) }
func (p *PDF) Color() color.RGBA { p.init(); return p.color }
func (p *PDF) SetColor(nameOrHTMLColor string) *PDF {
	color, err := p.init().ToColor(nameOrHTMLColor)
	if err, isT := err.(pdfError); isT {
		p.putError(0xE5B3A5, err.msg, nameOrHTMLColor)
	}
	p.color = color
	return p
}  
func (p *PDF) SetColorRGB(r, g, b byte) *PDF {
	p.init()
	p.color = color.RGBA{r, g, b, 255}
	return p
}  
func (p *PDF) Compression() bool { p.init(); return p.compression }
func (p *PDF) SetCompression(val bool) *PDF {
	p.init()
	p.compression = val
	return p
}  
func (p *PDF) CurrentPage() int { return p.pageNo + 1 }
func (p *PDF) SetCurrentPage(pageNo int) *PDF {
	if pageNo < 1 || pageNo > len(p.pages) {
		p.putError(0xE65AF0, "pageNo out of range",
			fmt.Sprint("pageNo:", pageNo, " range:1..", len(p.pages)))
		return p
	}
	p.pageNo = pageNo - 1
	return p
}  
func (p *PDF) DocAuthor() string { p.init(); return p.docAuthor }
func (p *PDF) SetDocAuthor(s string) *PDF { p.docAuthor = s; return p }
func (p *PDF) DocCreator() string { p.init(); return p.docCreator }
func (p *PDF) SetDocCreator(s string) *PDF { p.docCreator = s; return p }
func (p *PDF) DocKeywords() string { p.init(); return p.docKeywords }
func (p *PDF) SetDocKeywords(s string) *PDF { p.docKeywords = s; return p }
func (p *PDF) DocSubject() string { p.init(); return p.docSubject }
func (p *PDF) SetDocSubject(s string) *PDF { p.docSubject = s; return p }
func (p *PDF) DocTitle() string { p.init(); return p.docTitle }
func (p *PDF) SetDocTitle(s string) *PDF { p.docTitle = s; return p }
func (p *PDF) FontName() string { p.init(); return p.fontName }
func (p *PDF) SetFontName(name string) *PDF {
	p.init()
	p.fontName = name
	return p
}  
func (p *PDF) FontSize() float64 { p.init(); return p.fontSizePt }
func (p *PDF) SetFontSize(points float64) *PDF {
	p.init()
	p.fontSizePt = points
	return p
}  
func (p *PDF) SetFont(name string, points float64) *PDF {
	return p.SetFontName(name).SetFontSize(points)
}  
func (p *PDF) HorizontalScaling() uint16 { p.init(); return p.horzScaling }
func (p *PDF) SetHorizontalScaling(percent uint16) *PDF {
	p.init()
	p.horzScaling = percent
	return p
}  
func (p *PDF) LineWidth() float64 { p.init(); return p.lineWidth }
func (p *PDF) SetLineWidth(points float64) *PDF {
	p.init()
	p.lineWidth = points
	return p
}  
func (p *PDF) Units() string { p.init(); return p.units }
func (p *PDF) SetUnits(units string) *PDF {
	ppu, err := p.init().getPointsPerUnit(units)
	if err, isT := err.(pdfError); isT {
		return p.putError(0xEB4AAA, err.msg, units)
	}
	p.ptPerUnit, p.units = ppu, p.toUpperLettersDigits(units, "")
	return p
}  
func (p *PDF) X() float64 { return p.reservePage().ToUnits(p.page.x) }
func (p *PDF) SetX(x float64) *PDF {
	p.init().reservePage()
	p.page.x = x * p.ptPerUnit
	return p
}  
func (p *PDF) Y() float64 {
	return p.reservePage().ToUnits(p.paperSize.heightPt - p.page.y)
}  
func (p *PDF) SetY(y float64) *PDF {
	p.init().reservePage()
	p.page.y = p.paperSize.heightPt - y*p.ptPerUnit
	return p
}  
func (p *PDF) SetXY(x, y float64) *PDF { return p.SetX(x).SetY(y) }
func (p *PDF) AddPage() *PDF {
	COLOR := color.RGBA{1, 0, 1, 0x01}  
	p.pages = append(p.pages, pdfPage{
		x: -1, y: p.paperSize.heightPt + 1, lineWidth: 1,
		strokeColor: COLOR, nonStrokeColor: COLOR,
		fontSizePt: 10, horzScaling: 100,
	})
	p.pageNo = len(p.pages) - 1
	p.page = &p.pages[p.pageNo]
	p.writer = &p.page.content
	return p
}  
func (p *PDF) Bytes() []byte {
	p.reservePage()
	const pagesIndex = 3
	var (
		fontsIndex  = pagesIndex + len(p.pages)*2
		imagesIndex = fontsIndex + len(p.fonts)
		infoIndex   int  
		prevWriter  = p.writer
	)
	p.content.Reset()
	p.writer = &p.content
	p.objOffsets = []int{}
	p.objIndex = 0
	p.write("%PDF-1.4\n\n").
		writeObj("/Catalog").write("/Pages 2 0 R>>\n" + "endobj\n\n")
	p.writePages(pagesIndex, fontsIndex, imagesIndex)
	for _, font := range p.fonts {
		if font.handler == nil {
			p.writeObj("/Font").write("/Subtype/Type1/Name/FNT", font.id, "\n",
				"/BaseFont/", font.name, "\n",
				"/Encoding/StandardEncoding>>\n"+"endobj\n")
		} else {
			font.handler.writeFontObjects(&font)
		}
	}
	for _, img := range p.images {
		colorSpace := "DeviceRGB"
		if img.isGray {
			colorSpace = "DeviceGray"
		}
		old := p.compression
		p.compression = true
		p.writeObj("/XObject").
			write("/Subtype/Image\n",
				"/Width ", img.widthPx, "/Height ", img.heightPx,
				"/ColorSpace/", colorSpace, "/BitsPerComponent 8\n").
			writeStreamData(img.data).write("\n" + "endobj\n\n")
		p.compression = old
	}
	if p.docTitle != "" || p.docSubject != "" ||
		p.docKeywords != "" || p.docAuthor != "" || p.docCreator != "" {
		infoIndex = imagesIndex + len(p.images)
		p.writeObj("/Info")
		for _, tuple := range [][]string{
			{"/Title ", p.docTitle}, {"/Subject ", p.docSubject},
			{"/Keywords ", p.docKeywords}, {"/Author ", p.docAuthor},
			{"/Creator ", p.docCreator},
		} {
			if tuple[1] != "" {
				p.write(tuple[0], "(", p.escape(tuple[1]), ")")
			}
		}
		p.write(">>\n" + "endobj\n\n")
	}
	start := p.content.Len()
	p.write("xref\n"+
		"0 ", len(p.objOffsets), "\n"+"0000000000 65535 f \n")
	for _, offset := range p.objOffsets[1:] {
		p.write(fmt.Sprintf("%010d 00000 n \n", offset))
	}
	p.write("trailer\n"+"<</Size ", len(p.objOffsets), "/Root 1 0 R")
	if infoIndex > 0 {
		p.write("/Info ", infoIndex, " 0 R")  
	}
	p.write(">>\n"+"startxref\n", start, "\n", "%%EOF\n")
	p.writer = prevWriter
	return p.content.Bytes()
}  
func (p *PDF) DrawBox(x, y, width, height float64, optFill ...bool) *PDF {
	width, height = width*p.ptPerUnit, height*p.ptPerUnit
	x, y = x*p.ptPerUnit, p.paperSize.heightPt-y*p.ptPerUnit-height
	mode := p.writeMode(optFill...)
	return p.write(x, " ", y, " ", width, " ", height, " re ", mode, "\n")
}  
func (p *PDF) DrawCircle(x, y, radius float64, optFill ...bool) *PDF {
	return p.DrawEllipse(x, y, radius, radius, optFill...)
}  
func (p *PDF) DrawEllipse(x, y, xRadius, yRadius float64,
	optFill ...bool) *PDF {
	x, y = x*p.ptPerUnit, p.paperSize.heightPt-y*p.ptPerUnit
	const ratio = 0.552284749830794  
	var (
		r    = xRadius * p.ptPerUnit    
		v    = yRadius * p.ptPerUnit    
		m, n = r * ratio, v * ratio     
		mode = p.writeMode(optFill...)  
	)
	return p.write(x-r, " ", y, " m\n").  
		writeCurve(x-r, y+n, x-m, y+v, x+0, y+v).  
		writeCurve(x+m, y+v, x+r, y+n, x+r, y+0).  
		writeCurve(x+r, y-n, x+m, y-v, x+0, y-v).  
		writeCurve(x-m, y-v, x-r, y-n, x-r, y+0).  
		write(mode, "\n")                          
}  
func (p *PDF) DrawImage(x, y, height float64, fileNameOrBytes interface{},
	backColor ...string) *PDF {
	back := color.RGBA{R: 255, G: 255, B: 255, A: 255}  
	if len(backColor) > 0 {
		back, _ = p.ToColor(backColor[0])
	}
	p.reservePage()
	img, idx, err := p.loadImage(fileNameOrBytes, back)
	if err, isT := err.(pdfError); isT {
		return p.putError(0xE8F375, err.msg, err.val)
	}
	var found bool
	for _, id := range p.page.imageIDs {
		if id == idx {
			found = true
			break
		}
	}
	if !found {
		p.page.imageIDs = append(p.page.imageIDs, idx)
	}
	h := height * p.ptPerUnit
	w := float64(img.widthPx) / float64(img.heightPx) * h
	x, y = x*p.ptPerUnit, p.paperSize.heightPt-y*p.ptPerUnit-h
	return p.write("q\n", w, " 0 0 ", h, " ", x, " ", y, " cm\n"+
		"/IMG", idx, " Do\n"+"Q\n")
}  
func (p *PDF) DrawLine(x1, y1, x2, y2 float64) *PDF {
	x1, y1 = x1*p.ptPerUnit, p.paperSize.heightPt-y1*p.ptPerUnit
	x2, y2 = x2*p.ptPerUnit, p.paperSize.heightPt-y2*p.ptPerUnit
	p.writeMode(true)  
	return p.write(x1, " ", y1, " m ", x2, " ", y2, " l S\n")
}  
func (p *PDF) DrawText(s string) *PDF {
	if len(p.columnWidths) == 0 {
		return p.drawTextLine(s)
	}
	x := 0.0
	for i := 0; i < p.columnNo; i, x = i+1, x+p.columnWidths[i] {
	}
	p.SetX(x).drawTextLine(s)
	if p.columnNo < len(p.columnWidths)-1 {
		p.columnNo++
		return p
	}
	return p.NextLine()
}  
func (p *PDF) DrawTextAlignedToBox(
	x, y, width, height float64, align, text string) *PDF {
	return p.drawTextBox(x, y, width, height, false, align, text)
}  
func (p *PDF) DrawTextAt(x, y float64, text string) *PDF {
	return p.SetXY(x, y).DrawText(text)
}  
func (p *PDF) DrawTextInBox(
	x, y, width, height float64, align, text string) *PDF {
	return p.drawTextBox(x, y, width, height, true, align, text)
}  
func (p *PDF) DrawUnitGrid() *PDF {
	pw, ph := p.PageWidth(), p.PageHeight()
	p.SetLineWidth(0.1).SetFont("Helvetica", 8)
	for i, x := 0, 0.0; x < pw; i, x = i+1, x+1 {  
		p.SetColorRGB(200, 200, 200).DrawLine(x, 0, x, ph).
			SetColor("Indigo").SetXY(x+0.1, 0.3).DrawText(strconv.Itoa(i))
	}
	for i, y := 0, 0.0; y < ph; i, y = i+1, y+1 {  
		p.SetColorRGB(200, 200, 200).DrawLine(0, y, pw, y).
			SetColor("Indigo").SetXY(0.1, y+0.3).DrawText(strconv.Itoa(i))
	}
	return p
}  
func (p *PDF) FillBox(x, y, width, height float64) *PDF {
	return p.DrawBox(x, y, width, height, true)
}  
func (p *PDF) FillCircle(x, y, radius float64) *PDF {
	return p.DrawEllipse(x, y, radius, radius, true)
}  
func (p *PDF) FillEllipse(x, y, xRadius, yRadius float64) *PDF {
	return p.DrawEllipse(x, y, xRadius, yRadius, true)
}  
func (p *PDF) NextLine() *PDF {
	x, y := 0.0, p.Y()+p.FontSize()*p.ptPerUnit
	if len(p.columnWidths) > 0 {
		x = p.columnWidths[0]
	}
	if y > p.paperSize.heightPt*p.ptPerUnit {
		p.AddPage()
		y = 0
	}
	p.columnNo = 0
	return p.SetXY(x, y)
}  
func (p *PDF) Reset() *PDF {
	p.page, p.writer = nil, nil
	*p = NewPDF(p.paperSize.name)
	return p
}  
func (p *PDF) SaveFile(filename string) error {
	err := os.WriteFile(filename, p.Bytes(), 0644)
	if err != nil {
		p.putError(0xED3F6D, "Failed writing file", err.Error())
	}
	return err
}  
func (p *PDF) SetColumnWidths(widths ...float64) *PDF {
	p.init()
	p.columnWidths = widths
	return p
}  
func (p *PDF) TextWidth(s string) float64 {
	return p.ToUnits(p.textWidthPt(s))
}  
func (p *PDF) ToColor(nameOrHTMLColor string) (color.RGBA, error) {
	s := p.toUpperLettersDigits(nameOrHTMLColor, "#")
	if len(s) >= 7 && s[0] == '#' {
		var hex [6]byte
		for i, r := range s[1:7] {
			switch {
			case r >= '0' && r <= '9':
				hex[i] = byte(r - '0')
			case r >= 'A' && r <= 'F':
				hex[i] = byte(r - 'A' + 10)
			default:
				return pdfBlack, pdfError{id: 0xEED50B, src: "ToColor",
					msg: "Bad color code", val: nameOrHTMLColor}
			}
		}
		return color.RGBA{
			hex[0]*16 + hex[1],
			hex[2]*16 + hex[3],
			hex[4]*16 + hex[5], 255}, nil
	}
	if cl, found := PDFColorNames[s]; found {  
		return color.RGBA{cl.R, cl.G, cl.B, 255}, nil
	}
	for k, v := range PDFColorNames {  
		if p.toUpperLettersDigits(k, "") == s {
			return v, nil
		}
	}
	return pdfBlack, pdfError{id: 0xE00982, src: "ToColor",
		msg: "Unknown color name", val: nameOrHTMLColor}
}  
func (p *PDF) ToPoints(numberAndUnit string) (float64, error) {
	var num, unit string  
	for _, r := range numberAndUnit {
		switch {
		case r == '-', r == '.', unicode.IsDigit(r):
			num += string(r)
		case r == '"', unicode.IsLetter(r):
			unit += string(r)
		}
	}
	ppu := 1.0
	if unit != "" {
		var err error
		ppu, err = p.getPointsPerUnit(unit)
		if err, isT := err.(pdfError); isT {
			return 0, err
		}
	}
	n, err := strconv.ParseFloat(num, 64)
	if err != nil {
		return 0, pdfError{id: 0xE154AC, msg: "Invalid number", val: num}
	}
	return n * ppu, nil
}  
func (p *PDF) ToUnits(points float64) float64 {
	if int(p.ptPerUnit*100) == 0 {
		return points
	}
	return points / p.ptPerUnit
}  
func (p *PDF) WrapTextLines(width float64, text string) (ret []string) {
	fit := func(s string, step, n int, width float64) int {
		for max := len(s); n > 0 && n <= max; {
			w := p.TextWidth(s[:n])
			switch step {
			case 1, 3:  
				if w <= width {
					return n
				}
				n--
				if step == 1 {
					n /= 2
				}
			case 2:  
				if w > width {
					return n
				}
				n = 1 + int((float64(n) * 1.2))  
			}
		}
		return 0
	}
	for _, line := range p.splitLines(text) {
		for p.TextWidth(line) > width {
			n := len(line)  
			for i := 1; i <= 3; i++ {
				n = fit(line, i, n, width)
			}
			found, max := false, n
			for n > 0 {
				if p.isWhiteSpace(line[n-1 : n]) {
					found = true
					break
				}
				n--
			}
			if !found {
				n = max
			}
			if n <= 0 {
				break
			}
			ret = append(ret, line[:n])
			line = line[n:]
		}
		ret = append(ret, line)
	}
	return ret
}  
func (p *PDF) Clean() *PDF { p.errors = nil; return p }
func (*PDF) ErrorInfo(err error) (ret struct {
	ID            int
	Msg, Src, Val string
}) {
	if err, isT := err.(pdfError); isT {
		ret.ID, ret.Msg, ret.Src, ret.Val = err.id, err.msg, err.src, err.val
	}
	return ret
}  
func (p *PDF) Errors() []error { return p.errors }