From f8ed716415e0dec544a3beb5c7c15361fa957f22 Mon Sep 17 00:00:00 2001 From: Johnathan Fercher Date: Mon, 20 May 2019 22:29:49 -0300 Subject: [PATCH] Kick-off of Maroto project --- .gitignore | 3 + Gopkg.lock | 50 + Gopkg.toml | 34 + assets/images/dhl.png | Bin 0 -> 11408 bytes assets/images/mercado_livre.png | Bin 0 -> 47303 bytes enums/horizontal_align.go | 9 + enums/orientation.go | 7 + enums/page_size.go | 7 + enums/vertical_align.go | 9 + font/family.go | 11 + font/font.go | 109 + font/font_test.go | 439 ++ font/style.go | 10 + main.go | 105 + maroto/maroto.go | 274 + maroto/maroto_test.go | 111 + math/math.go | 23 + math/math_test.go | 141 + mocks/font.go | 129 + mocks/math.go | 24 + mocks/pdf.go | 1520 ++++++ sign/sign.go | 35 + text/text.go | 43 + text/text_test.go | 124 + .../github.com/boombuler/barcode/.gitignore | 1 + vendor/github.com/boombuler/barcode/LICENSE | 21 + vendor/github.com/boombuler/barcode/README.md | 53 + .../boombuler/barcode/aztec/azteccode.go | 62 + .../boombuler/barcode/aztec/encoder.go | 268 + .../barcode/aztec/errorcorrection.go | 61 + .../boombuler/barcode/aztec/highlevel.go | 171 + .../boombuler/barcode/aztec/state.go | 264 + .../boombuler/barcode/aztec/token.go | 75 + .../github.com/boombuler/barcode/barcode.go | 42 + .../boombuler/barcode/codabar/encoder.go | 49 + .../boombuler/barcode/code128/encode.go | 203 + .../barcode/code128/encodingtable.go | 143 + .../boombuler/barcode/code39/encoder.go | 152 + .../barcode/datamatrix/codelayout.go | 213 + .../boombuler/barcode/datamatrix/codesize.go | 73 + .../barcode/datamatrix/datamatrixcode.go | 50 + .../boombuler/barcode/datamatrix/encoder.go | 75 + .../barcode/datamatrix/errorcorrection.go | 45 + .../boombuler/barcode/ean/encoder.go | 187 + .../boombuler/barcode/qr/alphanumeric.go | 66 + .../boombuler/barcode/qr/automatic.go | 23 + .../github.com/boombuler/barcode/qr/blocks.go | 59 + .../boombuler/barcode/qr/encoder.go | 416 ++ .../boombuler/barcode/qr/errorcorrection.go | 29 + .../boombuler/barcode/qr/numeric.go | 56 + .../github.com/boombuler/barcode/qr/qrcode.go | 166 + .../boombuler/barcode/qr/unicode.go | 27 + .../boombuler/barcode/qr/versioninfo.go | 310 ++ .../boombuler/barcode/scaledbarcode.go | 134 + .../boombuler/barcode/twooffive/encoder.go | 138 + .../boombuler/barcode/utils/base1dcode.go | 57 + .../boombuler/barcode/utils/bitlist.go | 119 + .../boombuler/barcode/utils/galoisfield.go | 65 + .../boombuler/barcode/utils/gfpoly.go | 103 + .../boombuler/barcode/utils/reedsolomon.go | 44 + .../boombuler/barcode/utils/runeint.go | 19 + .../github.com/jung-kurt/gofpdf/.gitattribute | 1 + vendor/github.com/jung-kurt/gofpdf/.gitignore | 22 + .../github.com/jung-kurt/gofpdf/.travis.yml | 14 + vendor/github.com/jung-kurt/gofpdf/LICENSE | 21 + vendor/github.com/jung-kurt/gofpdf/Makefile | 29 + vendor/github.com/jung-kurt/gofpdf/README.md | 255 + vendor/github.com/jung-kurt/gofpdf/compare.go | 146 + .../gofpdf/contrib/barcode/barcode.go | 302 ++ vendor/github.com/jung-kurt/gofpdf/def.go | 731 +++ vendor/github.com/jung-kurt/gofpdf/doc.go | 267 + .../github.com/jung-kurt/gofpdf/embedded.go | 559 ++ vendor/github.com/jung-kurt/gofpdf/font.go | 474 ++ vendor/github.com/jung-kurt/gofpdf/fpdf.go | 4771 +++++++++++++++++ .../github.com/jung-kurt/gofpdf/fpdftrans.go | 213 + vendor/github.com/jung-kurt/gofpdf/go.mod | 9 + vendor/github.com/jung-kurt/gofpdf/go.sum | 11 + vendor/github.com/jung-kurt/gofpdf/grid.go | 446 ++ .../github.com/jung-kurt/gofpdf/htmlbasic.go | 220 + vendor/github.com/jung-kurt/gofpdf/label.go | 82 + vendor/github.com/jung-kurt/gofpdf/layer.go | 121 + vendor/github.com/jung-kurt/gofpdf/png.go | 213 + vendor/github.com/jung-kurt/gofpdf/protect.go | 114 + .../github.com/jung-kurt/gofpdf/splittext.go | 54 + .../github.com/jung-kurt/gofpdf/spotcolor.go | 184 + .../github.com/jung-kurt/gofpdf/subwrite.go | 35 + .../github.com/jung-kurt/gofpdf/svgbasic.go | 212 + .../github.com/jung-kurt/gofpdf/svgwrite.go | 60 + .../github.com/jung-kurt/gofpdf/template.go | 273 + .../jung-kurt/gofpdf/template_impl.go | 299 ++ .../github.com/jung-kurt/gofpdf/ttfparser.go | 374 ++ .../jung-kurt/gofpdf/utf8fontfile.go | 1140 ++++ vendor/github.com/jung-kurt/gofpdf/util.go | 446 ++ .../github.com/ruudk/golang-pdf417/.gitignore | 1 + .../ruudk/golang-pdf417/.travis.yml | 4 + vendor/github.com/ruudk/golang-pdf417/LICENSE | 21 + .../github.com/ruudk/golang-pdf417/README.md | 14 + .../ruudk/golang-pdf417/byte_encoder.go | 28 + .../ruudk/golang-pdf417/data_encoder.go | 99 + vendor/github.com/ruudk/golang-pdf417/go.mod | 9 + vendor/github.com/ruudk/golang-pdf417/go.sum | 10 + .../ruudk/golang-pdf417/number_encoder.go | 87 + .../github.com/ruudk/golang-pdf417/pdf417.go | 633 +++ .../ruudk/golang-pdf417/reed_solomon.go | 164 + .../ruudk/golang-pdf417/text_encoder.go | 261 + 105 files changed, 20685 insertions(+) create mode 100644 Gopkg.lock create mode 100644 Gopkg.toml create mode 100644 assets/images/dhl.png create mode 100644 assets/images/mercado_livre.png create mode 100644 enums/horizontal_align.go create mode 100644 enums/orientation.go create mode 100644 enums/page_size.go create mode 100644 enums/vertical_align.go create mode 100644 font/family.go create mode 100644 font/font.go create mode 100644 font/font_test.go create mode 100644 font/style.go create mode 100644 main.go create mode 100644 maroto/maroto.go create mode 100644 maroto/maroto_test.go create mode 100644 math/math.go create mode 100644 math/math_test.go create mode 100644 mocks/font.go create mode 100644 mocks/math.go create mode 100644 mocks/pdf.go create mode 100644 sign/sign.go create mode 100644 text/text.go create mode 100644 text/text_test.go create mode 100644 vendor/github.com/boombuler/barcode/.gitignore create mode 100644 vendor/github.com/boombuler/barcode/LICENSE create mode 100644 vendor/github.com/boombuler/barcode/README.md create mode 100644 vendor/github.com/boombuler/barcode/aztec/azteccode.go create mode 100644 vendor/github.com/boombuler/barcode/aztec/encoder.go create mode 100644 vendor/github.com/boombuler/barcode/aztec/errorcorrection.go create mode 100644 vendor/github.com/boombuler/barcode/aztec/highlevel.go create mode 100644 vendor/github.com/boombuler/barcode/aztec/state.go create mode 100644 vendor/github.com/boombuler/barcode/aztec/token.go create mode 100644 vendor/github.com/boombuler/barcode/barcode.go create mode 100644 vendor/github.com/boombuler/barcode/codabar/encoder.go create mode 100644 vendor/github.com/boombuler/barcode/code128/encode.go create mode 100644 vendor/github.com/boombuler/barcode/code128/encodingtable.go create mode 100644 vendor/github.com/boombuler/barcode/code39/encoder.go create mode 100644 vendor/github.com/boombuler/barcode/datamatrix/codelayout.go create mode 100644 vendor/github.com/boombuler/barcode/datamatrix/codesize.go create mode 100644 vendor/github.com/boombuler/barcode/datamatrix/datamatrixcode.go create mode 100644 vendor/github.com/boombuler/barcode/datamatrix/encoder.go create mode 100644 vendor/github.com/boombuler/barcode/datamatrix/errorcorrection.go create mode 100644 vendor/github.com/boombuler/barcode/ean/encoder.go create mode 100644 vendor/github.com/boombuler/barcode/qr/alphanumeric.go create mode 100644 vendor/github.com/boombuler/barcode/qr/automatic.go create mode 100644 vendor/github.com/boombuler/barcode/qr/blocks.go create mode 100644 vendor/github.com/boombuler/barcode/qr/encoder.go create mode 100644 vendor/github.com/boombuler/barcode/qr/errorcorrection.go create mode 100644 vendor/github.com/boombuler/barcode/qr/numeric.go create mode 100644 vendor/github.com/boombuler/barcode/qr/qrcode.go create mode 100644 vendor/github.com/boombuler/barcode/qr/unicode.go create mode 100644 vendor/github.com/boombuler/barcode/qr/versioninfo.go create mode 100644 vendor/github.com/boombuler/barcode/scaledbarcode.go create mode 100644 vendor/github.com/boombuler/barcode/twooffive/encoder.go create mode 100644 vendor/github.com/boombuler/barcode/utils/base1dcode.go create mode 100644 vendor/github.com/boombuler/barcode/utils/bitlist.go create mode 100644 vendor/github.com/boombuler/barcode/utils/galoisfield.go create mode 100644 vendor/github.com/boombuler/barcode/utils/gfpoly.go create mode 100644 vendor/github.com/boombuler/barcode/utils/reedsolomon.go create mode 100644 vendor/github.com/boombuler/barcode/utils/runeint.go create mode 100644 vendor/github.com/jung-kurt/gofpdf/.gitattribute create mode 100644 vendor/github.com/jung-kurt/gofpdf/.gitignore create mode 100644 vendor/github.com/jung-kurt/gofpdf/.travis.yml create mode 100644 vendor/github.com/jung-kurt/gofpdf/LICENSE create mode 100644 vendor/github.com/jung-kurt/gofpdf/Makefile create mode 100644 vendor/github.com/jung-kurt/gofpdf/README.md create mode 100644 vendor/github.com/jung-kurt/gofpdf/compare.go create mode 100644 vendor/github.com/jung-kurt/gofpdf/contrib/barcode/barcode.go create mode 100644 vendor/github.com/jung-kurt/gofpdf/def.go create mode 100644 vendor/github.com/jung-kurt/gofpdf/doc.go create mode 100644 vendor/github.com/jung-kurt/gofpdf/embedded.go create mode 100644 vendor/github.com/jung-kurt/gofpdf/font.go create mode 100644 vendor/github.com/jung-kurt/gofpdf/fpdf.go create mode 100644 vendor/github.com/jung-kurt/gofpdf/fpdftrans.go create mode 100644 vendor/github.com/jung-kurt/gofpdf/go.mod create mode 100644 vendor/github.com/jung-kurt/gofpdf/go.sum create mode 100644 vendor/github.com/jung-kurt/gofpdf/grid.go create mode 100644 vendor/github.com/jung-kurt/gofpdf/htmlbasic.go create mode 100644 vendor/github.com/jung-kurt/gofpdf/label.go create mode 100644 vendor/github.com/jung-kurt/gofpdf/layer.go create mode 100644 vendor/github.com/jung-kurt/gofpdf/png.go create mode 100644 vendor/github.com/jung-kurt/gofpdf/protect.go create mode 100644 vendor/github.com/jung-kurt/gofpdf/splittext.go create mode 100644 vendor/github.com/jung-kurt/gofpdf/spotcolor.go create mode 100644 vendor/github.com/jung-kurt/gofpdf/subwrite.go create mode 100644 vendor/github.com/jung-kurt/gofpdf/svgbasic.go create mode 100644 vendor/github.com/jung-kurt/gofpdf/svgwrite.go create mode 100644 vendor/github.com/jung-kurt/gofpdf/template.go create mode 100644 vendor/github.com/jung-kurt/gofpdf/template_impl.go create mode 100644 vendor/github.com/jung-kurt/gofpdf/ttfparser.go create mode 100644 vendor/github.com/jung-kurt/gofpdf/utf8fontfile.go create mode 100644 vendor/github.com/jung-kurt/gofpdf/util.go create mode 100644 vendor/github.com/ruudk/golang-pdf417/.gitignore create mode 100644 vendor/github.com/ruudk/golang-pdf417/.travis.yml create mode 100644 vendor/github.com/ruudk/golang-pdf417/LICENSE create mode 100644 vendor/github.com/ruudk/golang-pdf417/README.md create mode 100644 vendor/github.com/ruudk/golang-pdf417/byte_encoder.go create mode 100644 vendor/github.com/ruudk/golang-pdf417/data_encoder.go create mode 100644 vendor/github.com/ruudk/golang-pdf417/go.mod create mode 100644 vendor/github.com/ruudk/golang-pdf417/go.sum create mode 100644 vendor/github.com/ruudk/golang-pdf417/number_encoder.go create mode 100644 vendor/github.com/ruudk/golang-pdf417/pdf417.go create mode 100644 vendor/github.com/ruudk/golang-pdf417/reed_solomon.go create mode 100644 vendor/github.com/ruudk/golang-pdf417/text_encoder.go diff --git a/.gitignore b/.gitignore index a95f8fbf..9403150d 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,6 @@ # JetBrains .idea + +# PDFs +*.pdf \ No newline at end of file diff --git a/Gopkg.lock b/Gopkg.lock new file mode 100644 index 00000000..bd52500a --- /dev/null +++ b/Gopkg.lock @@ -0,0 +1,50 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + digest = "1:42cf657e9a38dbea61b503ec5bc2792abd5027e0395dcc7548a86090701a073a" + name = "github.com/boombuler/barcode" + packages = [ + ".", + "aztec", + "codabar", + "code128", + "code39", + "datamatrix", + "ean", + "qr", + "twooffive", + "utils", + ] + pruneopts = "UT" + revision = "3cfea5ab600ae37946be2b763b8ec2c1cf2d272d" + version = "v1.0.0" + +[[projects]] + digest = "1:169c17fc74f48ff403c5b3de15c2905b4764ae42062d8b7346881558ca832245" + name = "github.com/jung-kurt/gofpdf" + packages = [ + ".", + "contrib/barcode", + ] + pruneopts = "UT" + revision = "8060f8371088d63b5935a6eeb617328705387ace" + version = "v1.4.2" + +[[projects]] + branch = "master" + digest = "1:5b064c863d2532deee1f1c8d25e7370db1090a8522717f35b552a8026434893e" + name = "github.com/ruudk/golang-pdf417" + packages = ["."] + pruneopts = "UT" + revision = "1af4ab5afa58c956c53ed723dee66437498b9415" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + input-imports = [ + "github.com/jung-kurt/gofpdf", + "github.com/jung-kurt/gofpdf/contrib/barcode", + ] + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml new file mode 100644 index 00000000..893b415e --- /dev/null +++ b/Gopkg.toml @@ -0,0 +1,34 @@ +# Gopkg.toml example +# +# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html +# for detailed Gopkg.toml documentation. +# +# required = ["github.com/user/thing/cmd/thing"] +# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] +# +# [[constraint]] +# name = "github.com/user/project" +# version = "1.0.0" +# +# [[constraint]] +# name = "github.com/user/project2" +# branch = "dev" +# source = "github.com/myfork/project2" +# +# [[override]] +# name = "github.com/x/y" +# version = "2.4.0" +# +# [prune] +# non-go = false +# go-tests = true +# unused-packages = true + + +[[constraint]] + name = "github.com/jung-kurt/gofpdf" + version = "1.4.2" + +[prune] + go-tests = true + unused-packages = true diff --git a/assets/images/dhl.png b/assets/images/dhl.png new file mode 100644 index 0000000000000000000000000000000000000000..72d37bc90c60cd7326d1281d4c4e6a30e2634f17 GIT binary patch literal 11408 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 Tbt<_)WXuVlKjYN2_)Y%_KPsw3 literal 0 HcmV?d00001 diff --git a/assets/images/mercado_livre.png b/assets/images/mercado_livre.png new file mode 100644 index 0000000000000000000000000000000000000000..51ff58d4ce9f66cafb9da005f85904f17bf76a67 GIT binary patch literal 47303 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 literal 0 HcmV?d00001 diff --git a/enums/horizontal_align.go b/enums/horizontal_align.go new file mode 100644 index 00000000..3fc56e13 --- /dev/null +++ b/enums/horizontal_align.go @@ -0,0 +1,9 @@ +package enums + +type HorizontalAlign int + +const ( + Left HorizontalAlign = 0 + Right HorizontalAlign = 1 + CenterH HorizontalAlign = 2 +) diff --git a/enums/orientation.go b/enums/orientation.go new file mode 100644 index 00000000..946a9ad6 --- /dev/null +++ b/enums/orientation.go @@ -0,0 +1,7 @@ +package enums + +type Orientation int + +const ( + Vertical Orientation = 0 +) diff --git a/enums/page_size.go b/enums/page_size.go new file mode 100644 index 00000000..7e279d75 --- /dev/null +++ b/enums/page_size.go @@ -0,0 +1,7 @@ +package enums + +type PageSize int + +const ( + A4 PageSize = 0 +) diff --git a/enums/vertical_align.go b/enums/vertical_align.go new file mode 100644 index 00000000..67f02bb4 --- /dev/null +++ b/enums/vertical_align.go @@ -0,0 +1,9 @@ +package enums + +type VerticalAlign int + +const ( + Top VerticalAlign = 0 + Bottom VerticalAlign = 1 + CenterV VerticalAlign = 2 +) diff --git a/font/family.go b/font/family.go new file mode 100644 index 00000000..8ed9fa44 --- /dev/null +++ b/font/family.go @@ -0,0 +1,11 @@ +package font + +type Family int + +const ( + Arial Family = 0 + Helvetica Family = 1 + Symbol Family = 2 + ZapBats Family = 3 + Courier Family = 4 +) diff --git a/font/font.go b/font/font.go new file mode 100644 index 00000000..e8db223c --- /dev/null +++ b/font/font.go @@ -0,0 +1,109 @@ +package font + +import ( + "github.com/jung-kurt/gofpdf" +) + +type Font interface { + SetFamily(family Family) + SetStyle(style Style) + SetSize(size float64) + SetFont(family Family, style Style, size float64) + GetFamily() Family + GetStyle() Style + GetSize() float64 + GetFont() (Family, Style, float64) + GetStyleString(style Style) string + GetFamilyString(font Family) string +} + +type font struct { + pdf gofpdf.Pdf + size float64 + family Family + style Style +} + +func NewFont(pdf gofpdf.Pdf, size float64, family Family, style Style) Font { + return &font{ + pdf, + size, + family, + style, + } +} + +func (f *font) GetFamily() Family { + return f.family +} + +func (f *font) GetStyle() Style { + return f.style +} + +func (f *font) GetSize() float64 { + return f.size +} + +func (f *font) GetFont() (Family, Style, float64) { + return f.family, f.style, f.size +} + +func (f *font) SetFamily(family Family) { + f.family = family + familyString := f.GetFamilyString(f.family) + styleString := f.GetStyleString(f.style) + + f.pdf.SetFont(familyString, styleString, f.size) +} + +func (f *font) SetStyle(style Style) { + f.style = style + styleString := f.GetStyleString(f.style) + + f.pdf.SetFontStyle(styleString) +} + +func (f *font) SetSize(size float64) { + f.size = size + f.pdf.SetFontSize(f.size) +} + +func (f *font) SetFont(family Family, style Style, size float64) { + f.family = family + f.style = style + f.size = size + + familyString := f.GetFamilyString(f.family) + styleString := f.GetStyleString(f.style) + + f.pdf.SetFont(familyString, styleString, f.size) +} + +func (f *font) GetFamilyString(font Family) string { + switch font { + case Courier: + return "courier" + case Helvetica: + return "helvetica" + case Symbol: + return "symbol" + case ZapBats: + return "zapfdingbats" + default: + return "arial" + } +} + +func (f *font) GetStyleString(style Style) string { + switch style { + case Italic: + return "I" + case Bold: + return "B" + case BoldItalic: + return "BI" + default: + return "" + } +} diff --git a/font/font_test.go b/font/font_test.go new file mode 100644 index 00000000..ce817240 --- /dev/null +++ b/font/font_test.go @@ -0,0 +1,439 @@ +package font_test + +import ( + "fmt" + "github.com/johnfercher/maroto/font" + "github.com/johnfercher/maroto/mocks" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "testing" +) + +func TestNewFont(t *testing.T) { + _font := font.NewFont(&mocks.Pdf{}, 10, font.Arial, font.Bold) + + assert.NotNil(t, _font) + assert.Equal(t, fmt.Sprintf("%T", _font), "*font.font") + assert.Equal(t, _font.GetFamily(), font.Arial) + assert.Equal(t, _font.GetStyle(), font.Bold) + assert.Equal(t, _font.GetSize(), 10.0) +} + +func TestFont_GetSetFamily(t *testing.T) { + cases := []struct { + name string + family font.Family + pdf func() *mocks.Pdf + assertCalls func(t *testing.T, pdf *mocks.Pdf) + assertFont func(t *testing.T, family font.Family) + }{ + { + "font.Arial", + font.Arial, + func() *mocks.Pdf { + pdf := &mocks.Pdf{} + pdf.On("SetFont", mock.Anything, mock.Anything, mock.Anything) + return pdf + }, + func(t *testing.T, pdf *mocks.Pdf) { + pdf.AssertNumberOfCalls(t, "SetFont", 1) + pdf.AssertCalled(t, "SetFont", "arial", "B", 10.0) + }, + func(t *testing.T, family font.Family) { + assert.Equal(t, family, font.Arial) + }, + }, + { + "font.Helvetica", + font.Helvetica, + func() *mocks.Pdf { + pdf := &mocks.Pdf{} + pdf.On("SetFont", mock.Anything, mock.Anything, mock.Anything) + return pdf + }, + func(t *testing.T, pdf *mocks.Pdf) { + pdf.AssertNumberOfCalls(t, "SetFont", 1) + pdf.AssertCalled(t, "SetFont", "helvetica", "B", 10.0) + }, + func(t *testing.T, family font.Family) { + assert.Equal(t, family, font.Helvetica) + }, + }, + { + "font.Symbol", + font.Symbol, + func() *mocks.Pdf { + pdf := &mocks.Pdf{} + pdf.On("SetFont", mock.Anything, mock.Anything, mock.Anything) + return pdf + }, + func(t *testing.T, pdf *mocks.Pdf) { + pdf.AssertNumberOfCalls(t, "SetFont", 1) + pdf.AssertCalled(t, "SetFont", "symbol", "B", 10.0) + }, + func(t *testing.T, family font.Family) { + assert.Equal(t, family, font.Symbol) + }, + }, + { + "font.ZapBats", + font.ZapBats, + func() *mocks.Pdf { + pdf := &mocks.Pdf{} + pdf.On("SetFont", mock.Anything, mock.Anything, mock.Anything) + return pdf + }, + func(t *testing.T, pdf *mocks.Pdf) { + pdf.AssertNumberOfCalls(t, "SetFont", 1) + pdf.AssertCalled(t, "SetFont", "zapfdingbats", "B", 10.0) + }, + func(t *testing.T, family font.Family) { + assert.Equal(t, family, font.ZapBats) + }, + }, + { + "font.Courier", + font.Courier, + func() *mocks.Pdf { + pdf := &mocks.Pdf{} + pdf.On("SetFont", mock.Anything, mock.Anything, mock.Anything) + return pdf + }, + func(t *testing.T, pdf *mocks.Pdf) { + pdf.AssertNumberOfCalls(t, "SetFont", 1) + pdf.AssertCalled(t, "SetFont", "courier", "B", 10.0) + }, + func(t *testing.T, family font.Family) { + assert.Equal(t, family, font.Courier) + }, + }, + } + + for _, c := range cases { + // Arrange + pdf := c.pdf() + font := font.NewFont(pdf, 10, font.Arial, font.Bold) + + // Act + font.SetFamily(c.family) + + // Assert + c.assertCalls(t, pdf) + c.assertFont(t, font.GetFamily()) + } +} + +func TestFont_GetSetStyle(t *testing.T) { + cases := []struct { + name string + style font.Style + pdf func() *mocks.Pdf + assertCalls func(t *testing.T, pdf *mocks.Pdf) + assertStyle func(t *testing.T, style font.Style) + }{ + { + "font.Normal", + font.Normal, + func() *mocks.Pdf { + pdf := &mocks.Pdf{} + pdf.On("SetFontStyle", mock.Anything) + return pdf + }, + func(t *testing.T, pdf *mocks.Pdf) { + pdf.AssertNumberOfCalls(t, "SetFontStyle", 1) + pdf.AssertCalled(t, "SetFontStyle", "") + }, + func(t *testing.T, style font.Style) { + assert.Equal(t, style, font.Normal) + }, + }, + { + "font.Bold", + font.Bold, + func() *mocks.Pdf { + pdf := &mocks.Pdf{} + pdf.On("SetFontStyle", mock.Anything) + return pdf + }, + func(t *testing.T, pdf *mocks.Pdf) { + pdf.AssertNumberOfCalls(t, "SetFontStyle", 1) + pdf.AssertCalled(t, "SetFontStyle", "B") + }, + func(t *testing.T, style font.Style) { + assert.Equal(t, style, font.Bold) + }, + }, + { + "font.Italic", + font.Italic, + func() *mocks.Pdf { + pdf := &mocks.Pdf{} + pdf.On("SetFontStyle", mock.Anything) + return pdf + }, + func(t *testing.T, pdf *mocks.Pdf) { + pdf.AssertNumberOfCalls(t, "SetFontStyle", 1) + pdf.AssertCalled(t, "SetFontStyle", "I") + }, + func(t *testing.T, style font.Style) { + assert.Equal(t, style, font.Italic) + }, + }, + { + "font.BoldItalic", + font.BoldItalic, + func() *mocks.Pdf { + pdf := &mocks.Pdf{} + pdf.On("SetFontStyle", mock.Anything) + return pdf + }, + func(t *testing.T, pdf *mocks.Pdf) { + pdf.AssertNumberOfCalls(t, "SetFontStyle", 1) + pdf.AssertCalled(t, "SetFontStyle", "BI") + }, + func(t *testing.T, style font.Style) { + assert.Equal(t, style, font.BoldItalic) + }, + }, + } + + for _, c := range cases { + // Arrange + pdf := c.pdf() + font := font.NewFont(pdf, 10, font.Arial, font.Bold) + + // Act + font.SetStyle(c.style) + + // Assert + c.assertCalls(t, pdf) + c.assertStyle(t, font.GetStyle()) + } +} + +func TestFont_GetSetSize(t *testing.T) { + // Arrange + pdf := &mocks.Pdf{} + pdf.On("SetFontSize", mock.Anything) + font := font.NewFont(pdf, 10, font.Arial, font.Bold) + + // Act + font.SetSize(16) + + // Assert + pdf.AssertNumberOfCalls(t, "SetFontSize", 1) + pdf.MethodCalled("SetFontSize", 16) + assert.Equal(t, font.GetSize(), 16.0) +} + +func TestFont_GetSetFont(t *testing.T) { + cases := []struct { + name string + family font.Family + style font.Style + size float64 + pdf func() *mocks.Pdf + assertCalls func(t *testing.T, pdf *mocks.Pdf) + assertFont func(t *testing.T, family font.Family, style font.Style, size float64) + }{ + { + "font.Arial, font.Normal, 16", + font.Arial, + font.Normal, + 16.0, + func() *mocks.Pdf { + pdf := &mocks.Pdf{} + pdf.On("SetFont", mock.Anything, mock.Anything, mock.Anything) + return pdf + }, + func(t *testing.T, pdf *mocks.Pdf) { + pdf.AssertNumberOfCalls(t, "SetFont", 1) + pdf.AssertCalled(t, "SetFont", "arial", "", 16.0) + }, + func(t *testing.T, family font.Family, style font.Style, size float64) { + assert.Equal(t, family, font.Arial) + assert.Equal(t, style, font.Normal) + assert.Equal(t, 16, int(size)) + }, + }, + { + "font.Helvetica, font.Bold, 13", + font.Helvetica, + font.Bold, + 13, + func() *mocks.Pdf { + pdf := &mocks.Pdf{} + pdf.On("SetFont", mock.Anything, mock.Anything, mock.Anything) + return pdf + }, + func(t *testing.T, pdf *mocks.Pdf) { + pdf.AssertNumberOfCalls(t, "SetFont", 1) + pdf.AssertCalled(t, "SetFont", "helvetica", "B", 13.0) + }, + func(t *testing.T, family font.Family, style font.Style, size float64) { + assert.Equal(t, family, font.Helvetica) + assert.Equal(t, style, font.Bold) + assert.Equal(t, 13, int(size)) + }, + }, + { + "font.Symbol, font.Italic, 10", + font.Symbol, + font.Italic, + 10, + func() *mocks.Pdf { + pdf := &mocks.Pdf{} + pdf.On("SetFont", mock.Anything, mock.Anything, mock.Anything) + return pdf + }, + func(t *testing.T, pdf *mocks.Pdf) { + pdf.AssertNumberOfCalls(t, "SetFont", 1) + pdf.AssertCalled(t, "SetFont", "symbol", "I", 10.0) + }, + func(t *testing.T, family font.Family, style font.Style, size float64) { + assert.Equal(t, family, font.Symbol) + assert.Equal(t, style, font.Italic) + assert.Equal(t, 10, int(size)) + }, + }, + { + "font.ZapBats, font.BoldItalic, 5", + font.ZapBats, + font.BoldItalic, + 5, + func() *mocks.Pdf { + pdf := &mocks.Pdf{} + pdf.On("SetFont", mock.Anything, mock.Anything, mock.Anything) + return pdf + }, + func(t *testing.T, pdf *mocks.Pdf) { + pdf.AssertNumberOfCalls(t, "SetFont", 1) + pdf.AssertCalled(t, "SetFont", "zapfdingbats", "BI", 5.0) + }, + func(t *testing.T, family font.Family, style font.Style, size float64) { + assert.Equal(t, family, font.ZapBats) + assert.Equal(t, style, font.BoldItalic) + assert.Equal(t, 5, int(size)) + }, + }, + { + "font.Courier, font.Normal, 12", + font.Courier, + font.Normal, + 12, + func() *mocks.Pdf { + pdf := &mocks.Pdf{} + pdf.On("SetFont", mock.Anything, mock.Anything, mock.Anything) + return pdf + }, + func(t *testing.T, pdf *mocks.Pdf) { + pdf.AssertNumberOfCalls(t, "SetFont", 1) + pdf.AssertCalled(t, "SetFont", "courier", "", 12.0) + }, + func(t *testing.T, family font.Family, style font.Style, size float64) { + assert.Equal(t, family, font.Courier) + assert.Equal(t, style, font.Normal) + assert.Equal(t, 12, int(size)) + }, + }, + } + + for _, c := range cases { + // Arrange + pdf := c.pdf() + font := font.NewFont(pdf, 10, font.Arial, font.Bold) + + // Act + font.SetFont(c.family, c.style, c.size) + family, style, size := font.GetFont() + + // Assert + c.assertCalls(t, pdf) + c.assertFont(t, family, style, size) + } +} + +func TestFont_GetStyleString(t *testing.T) { + cases := []struct { + name string + style font.Style + styleString string + }{ + { + "font.Normal", + font.Normal, + "", + }, + { + "font.Bold", + font.Bold, + "B", + }, + { + "font.Italic", + font.Italic, + "I", + }, + { + "font.BoldItalic", + font.BoldItalic, + "BI", + }, + } + + for _, c := range cases { + // Arrange + _font := font.NewFont(&mocks.Pdf{}, 16.0, font.Arial, font.Bold) + + // Act + styleString := _font.GetStyleString(c.style) + + // Assert + assert.Equal(t, styleString, c.styleString) + } +} + +func TestFont_GetFamilyString(t *testing.T) { + cases := []struct { + name string + family font.Family + familyString string + }{ + { + "font.Arial", + font.Arial, + "arial", + }, + { + "font.Helvetica", + font.Helvetica, + "helvetica", + }, + { + "font.Symbol", + font.Symbol, + "symbol", + }, + { + "font.ZapBats", + font.ZapBats, + "zapfdingbats", + }, + { + "font.Courier", + font.Courier, + "courier", + }, + } + + for _, c := range cases { + // Arrange + _font := font.NewFont(&mocks.Pdf{}, 16.0, font.Arial, font.Bold) + + // Act + familyString := _font.GetFamilyString(c.family) + + // Assert + assert.Equal(t, familyString, c.familyString) + } +} diff --git a/font/style.go b/font/style.go new file mode 100644 index 00000000..560aff0c --- /dev/null +++ b/font/style.go @@ -0,0 +1,10 @@ +package font + +type Style int + +const ( + Normal Style = 0 + Bold Style = 1 + Italic Style = 2 + BoldItalic Style = 3 +) diff --git a/main.go b/main.go new file mode 100644 index 00000000..0accb8a8 --- /dev/null +++ b/main.go @@ -0,0 +1,105 @@ +package main + +import ( + "github.com/johnfercher/maroto/enums" + "github.com/johnfercher/maroto/font" + "github.com/johnfercher/maroto/maroto" +) + +func main() { + m := maroto.NewMaroto(enums.Vertical, enums.A4) + //m.SetDebugMode(true) + header, contents := getContents() + + m.Row("MeliBarcode", 20, func() { + m.Col("Logo", func() { + m.Image("assets/images/mercado_livre.png", 4) + }) + + m.ColSpaces(2) + + m.Col("Barcode", func() { + id := "123456789" + m.Barcode(id, 30, 9, 5) + m.Text(id, font.Arial, font.Bold, 8, 17, enums.CenterH) + }) + }) + + m.Line() + + m.Row("Destiny", 12, func() { + m.Col("Logo", func() { + m.Image("assets/images/mercado_livre.png", 1) + }) + + m.ColSpace() + + m.Col("Packages", func() { + m.Text("Vendedor: The Collector", font.Arial, font.Normal, 9, 5, enums.Left) + m.Text("Endereco: Nowhere", font.Arial, font.Normal, 9, 9, enums.Left) + }) + + m.ColSpace() + + m.Col("Route", func() { + m.Text("ROUTE.XDA.6", font.Arial, font.Bold, 15, 7.5, enums.Left) + }) + }) + + m.Line() + + m.Row("Packages Title", 22, func() { + m.ColSpaces(2) + + m.Col("Packages", func() { + m.Text("24", font.Arial, font.Bold, 20, 10.5, enums.CenterH) + m.Text("Pacotes Devolvidos", font.Arial, font.Normal, 12, 16, enums.CenterH) + }) + + m.ColSpaces(2) + }) + + m.Line() + + m.RowTableList("Packages", header, contents) + + m.Row("Signature", 15, func() { + m.Col("Carrier", func() { + m.Sign("Transportadora", font.Arial, font.Bold, 8) + }) + + m.ColSpace() + + m.Col("LogisticOperator", func() { + m.Sign("Operador Logistico", font.Arial, font.Bold, 8) + }) + + m.ColSpace() + + m.Col("Seller", func() { + m.Sign("Vendedor", font.Arial, font.Bold, 8) + }) + }) + + m.OutputFileAndClose("maroto.pdf") +} + +func getContents() ([]string, [][]string) { + header := []string{"Envio", "Venda", "Comprador", "Motivo"} + + contents := [][]string{ + {"678445", "678543", "Thanos", "Produto queimado"}, + {"489423", "579894", "Peter Parker", "Compra cancelada"}, + {"679076", "272747", "Thor", "Produto errado"}, + {"854364", "996634", "Nebula", "Fraude"}, + {"679095", "768690", "Steve Rogers", "Venda cancelada"}, + {"234512", "356469", "Tony Stark", "Produto errado"}, + {"123451", "996755", "Steve Strange", "Produto errado"}, + {"675523", "352364", "Star Lord", "Compra cancelada"}, + {"787894", "693595", "Gamora", "Fraude"}, + {"908907", "967867", "Scott Lang", "Compra cancelada"}, + {"876453", "797934", "Hank Pyn", "Produto errado"}, + } + + return header, contents +} diff --git a/maroto/maroto.go b/maroto/maroto.go new file mode 100644 index 00000000..ba6bdc25 --- /dev/null +++ b/maroto/maroto.go @@ -0,0 +1,274 @@ +package maroto + +import ( + "github.com/boombuler/barcode/code128" + "github.com/boombuler/barcode/qr" + "github.com/johnfercher/maroto/enums" + "github.com/johnfercher/maroto/font" + "github.com/johnfercher/maroto/math" + "github.com/johnfercher/maroto/sign" + "github.com/johnfercher/maroto/text" + "github.com/jung-kurt/gofpdf" + "github.com/jung-kurt/gofpdf/contrib/barcode" +) + +type Maroto interface { + // Grid System + Row(label string, height float64, closure func()) + Col(label string, closure func()) + ColSpace() + ColSpaces(qtd int) + + // Features + RowTableList(label string, header []string, contents [][]string) + SetDebugMode(on bool) + Text(text string, fontFamily font.Family, fontStyle font.Style, fontSize float64, marginTop float64, align enums.HorizontalAlign) + Image(filePathName string, marginTop float64) + Barcode(code string, width float64, height float64, marginTop float64) error + QrCode(code string) + Sign(label string, fontFamily font.Family, fontStyle font.Style, fontSize float64) + Line() + + // File System + OutputFileAndClose(filePathName string) error +} + +type maroto struct { + fpdf gofpdf.Pdf + math math.Math + font font.Font + text text.Text + sign sign.Sign + offsetY float64 + rowHeight float64 + rowColCount float64 + colsClosures []func() + debugMode bool +} + +func NewMaroto(orientation enums.Orientation, pageSize enums.PageSize) Maroto { + fpdfOrientation := "P" + fpdfPageSize := "A4" + + if orientation == enums.Vertical { + fpdfOrientation = "P" + } + + if pageSize == enums.A4 { + fpdfPageSize = "A4" + } + + fpdf := gofpdf.New(fpdfOrientation, "mm", fpdfPageSize, "") + fpdf.SetMargins(10, 10, 10) + + _math := math.NewMath(fpdf) + _font := font.NewFont(fpdf, 16, font.Arial, font.Bold) + _text := text.NewText(fpdf, _math, _font) + + _sign := sign.NewSign(fpdf, _math, _text) + + maroto := &maroto{ + fpdf: fpdf, + math: _math, + font: _font, + text: _text, + sign: _sign, + } + + maroto.font.SetFamily(font.Arial) + maroto.font.SetStyle(font.Bold) + maroto.font.SetSize(16) + maroto.debugMode = false + + return maroto +} + +func (m *maroto) Sign(label string, fontFamily font.Family, fontStyle font.Style, fontSize float64) { + qtdCols := float64(len(m.colsClosures)) + sumOfYOffsets := m.offsetY + m.rowHeight + + m.sign.Sign(label, fontFamily, fontStyle, fontSize, qtdCols, sumOfYOffsets, m.rowColCount) +} + +func (m *maroto) RowTableList(label string, header []string, contents [][]string) { + headerHeight := 7.0 + + m.Row("", headerHeight, func() { + headerMarginTop := 2.0 + qtdCols := float64(len(header)) + + for i, h := range header { + hs := h + is := i + + m.Col("", func() { + if headerMarginTop > m.rowHeight { + headerMarginTop = m.rowHeight + } + + reason := hs + + sumOyYOffesets := headerMarginTop + m.offsetY + 2.5 + + m.text.Add(reason, font.Arial, font.Bold, 10, sumOyYOffesets, enums.Left, float64(is), qtdCols) + }) + } + }) + + m.Row("", 4.0, func() { + m.ColSpace() + }) + + contentHeight := 5.0 + contentMarginTop := 2.0 + + for _, content := range contents { + m.Row("", contentHeight, func() { + for j, c := range content { + cs := c + js := j + hs := float64(len(header)) + sumOyYOffesets := contentMarginTop + m.offsetY + 2.0 + + m.Col("", func() { + m.text.Add(cs, font.Arial, font.Normal, 10, sumOyYOffesets, enums.Left, float64(js), hs) + }) + } + }) + } +} + +func (m *maroto) SetDebugMode(on bool) { + m.debugMode = on +} + +func (m *maroto) Line() { + m.Row("", 1, func() { + m.Col("", func() { + width, _ := m.fpdf.GetPageSize() + left, top, right, _ := m.fpdf.GetMargins() + + m.fpdf.Line(left, m.offsetY+top, width-right, m.offsetY+top) + }) + }) +} + +func (m *maroto) Row(label string, height float64, closure func()) { + m.fpdf.Ln(m.rowHeight) + m.rowHeight = height + m.rowColCount = 0 + + if m.fpdf.PageCount() == 0 { + m.fpdf.AddPage() + } + + closure() + + for _, colClosure := range m.colsClosures { + colClosure() + } + + m.colsClosures = nil + m.offsetY += m.rowHeight +} + +func (m *maroto) Col(label string, closure func()) { + m.colsClosures = append(m.colsClosures, func() { + widthPerCol := m.math.GetWidthPerCol(float64(len(m.colsClosures))) + m.createColSpace(widthPerCol) + closure() + m.rowColCount++ + }) + +} + +func (m *maroto) ColSpace() { + m.colsClosures = append(m.colsClosures, func() { + widthPerCol := m.math.GetWidthPerCol(float64(len(m.colsClosures))) + m.createColSpace(widthPerCol) + m.rowColCount++ + }) +} + +func (m *maroto) ColSpaces(qtd int) { + for i := 0; i < qtd; i++ { + m.colsClosures = append(m.colsClosures, func() { + widthPerCol := m.math.GetWidthPerCol(float64(len(m.colsClosures))) + m.createColSpace(widthPerCol) + m.rowColCount++ + }) + } +} + +func (m *maroto) Text(text string, fontFamily font.Family, fontStyle font.Style, fontSize float64, marginTop float64, align enums.HorizontalAlign) { + if marginTop > m.rowHeight { + marginTop = m.rowHeight + } + + sumOfYOffsets := marginTop + m.offsetY + + m.text.Add(text, fontFamily, fontStyle, fontSize, sumOfYOffsets, align, m.rowColCount, float64(len(m.colsClosures))) +} + +func (m *maroto) Image(filePathName string, marginTop float64) { + var opt gofpdf.ImageOptions + actualWidthPerCol := m.math.GetWidthPerCol(float64(len(m.colsClosures))) + + left, top, _, _ := m.fpdf.GetMargins() + + m.fpdf.ImageOptions(filePathName, actualWidthPerCol*m.rowColCount+left, m.offsetY+marginTop+top, actualWidthPerCol, 0, false, opt, 0, "") +} + +func (m *maroto) OutputFileAndClose(filePathName string) (err error) { + err = m.fpdf.OutputFileAndClose(filePathName) + return +} + +func (m *maroto) Barcode(code string, width float64, height float64, marginTop float64) (err error) { + bcode, err := code128.Encode(code) + + if err != nil { + return + } + + actualWidthPerCol := m.math.GetWidthPerCol(float64(len(m.colsClosures))) + + if width > actualWidthPerCol { + width = actualWidthPerCol + } + + if height > m.rowHeight { + height = m.rowHeight + } + + left, top, _, _ := m.fpdf.GetMargins() + + barcode.Barcode(m.fpdf, barcode.Register(bcode), actualWidthPerCol*m.rowColCount+((actualWidthPerCol-width)/2)+left, marginTop+top, width, height, false) + return +} + +func (m *maroto) QrCode(code string) { + key := barcode.RegisterQR(m.fpdf, code, qr.H, qr.Unicode) + + actualWidthPerCol := m.math.GetWidthPerCol(float64(len(m.colsClosures))) + + qrSide := actualWidthPerCol + + if m.rowHeight < qrSide { + qrSide = m.rowHeight + } + + barcode.Barcode(m.fpdf, key, actualWidthPerCol*m.rowColCount, 0, qrSide, qrSide, false) + + return +} + +func (m *maroto) createColSpace(actualWidthPerCol float64) { + border := "" + + if m.debugMode { + border = "1" + } + + m.fpdf.CellFormat(actualWidthPerCol, m.rowHeight, "", border, 0, "C", false, 0, "") +} diff --git a/maroto/maroto_test.go b/maroto/maroto_test.go new file mode 100644 index 00000000..6ca1d421 --- /dev/null +++ b/maroto/maroto_test.go @@ -0,0 +1,111 @@ +package maroto_test + +import ( + "github.com/johnfercher/maroto/enums" + "github.com/johnfercher/maroto/font" + "github.com/johnfercher/maroto/maroto" + "testing" +) + +func BenchmarkMaroto(b *testing.B) { + for i := 0; i < b.N; i++ { + Pdf() + } +} + +func Pdf() { + m := maroto.NewMaroto(enums.Vertical, enums.A4) + header, contents := getContents() + + m.Row("MeliBarcode", 20, func() { + m.Col("Logo", func() { + m.Image("assets/images/mercado_livre.png", 4) + }) + + m.ColSpaces(2) + + m.Col("Barcode", func() { + id := "123456789" + m.Barcode(id, 30, 9, 5) + m.Text(id, font.Arial, font.Bold, 8, 17, enums.CenterH) + }) + }) + + m.Line() + + m.Row("Destiny", 12, func() { + m.Col("Logo", func() { + m.Image("assets/images/mercado_livre.png", 1) + }) + + m.ColSpace() + + m.Col("Packages", func() { + m.Text("Vendedor: The Collector", font.Arial, font.Normal, 9, 5, enums.Left) + m.Text("Endereco: Nowhere", font.Arial, font.Normal, 9, 9, enums.Left) + }) + + m.ColSpace() + + m.Col("Route", func() { + m.Text("ROUTE.XDA.6", font.Arial, font.Bold, 15, 7.5, enums.Left) + }) + }) + + m.Line() + + m.Row("Packages Title", 22, func() { + m.ColSpaces(2) + + m.Col("Packages", func() { + m.Text("24", font.Arial, font.Bold, 20, 10.5, enums.CenterH) + m.Text("Pacotes Devolvidos", font.Arial, font.Normal, 12, 16, enums.CenterH) + }) + + m.ColSpaces(2) + }) + + m.Line() + + m.RowTableList("Packages", header, contents) + + m.Row("Signature", 15, func() { + m.Col("Carrier", func() { + m.Sign("Transportadora", font.Arial, font.Bold, 8) + }) + + m.ColSpace() + + m.Col("LogisticOperator", func() { + m.Sign("Operador Logistico", font.Arial, font.Bold, 8) + }) + + m.ColSpace() + + m.Col("Seller", func() { + m.Sign("Vendedor", font.Arial, font.Bold, 8) + }) + }) + + m.OutputFileAndClose("maroto.pdf") +} + +func getContents() ([]string, [][]string) { + header := []string{"Envio", "Venda", "Comprador", "Motivo"} + + contents := [][]string{ + {"678445", "678543", "Thanos", "Produto queimado"}, + {"489423", "579894", "Peter Parker", "Compra cancelada"}, + {"679076", "272747", "Thor", "Produto errado"}, + {"854364", "996634", "Nebula", "Fraude"}, + {"679095", "768690", "Steve Rogers", "Venda cancelada"}, + {"234512", "356469", "Tony Stark", "Produto errado"}, + {"123451", "996755", "Steve Strange", "Produto errado"}, + {"675523", "352364", "Star Lord", "Compra cancelada"}, + {"787894", "693595", "Gamora", "Fraude"}, + {"908907", "967867", "Scott Lang", "Compra cancelada"}, + {"876453", "797934", "Hank Pyn", "Produto errado"}, + } + + return header, contents +} diff --git a/math/math.go b/math/math.go new file mode 100644 index 00000000..3e503ed3 --- /dev/null +++ b/math/math.go @@ -0,0 +1,23 @@ +package math + +import "github.com/jung-kurt/gofpdf" + +type Math interface { + GetWidthPerCol(qtdCols float64) float64 +} + +type math struct { + pdf gofpdf.Pdf +} + +func NewMath(pdf gofpdf.Pdf) Math { + return &math{ + pdf, + } +} + +func (m *math) GetWidthPerCol(qtdCols float64) float64 { + width, _ := m.pdf.GetPageSize() + left, _, right, _ := m.pdf.GetMargins() + return (width - right - left) / qtdCols +} diff --git a/math/math_test.go b/math/math_test.go new file mode 100644 index 00000000..3f2a9dbe --- /dev/null +++ b/math/math_test.go @@ -0,0 +1,141 @@ +package math + +import ( + "fmt" + "github.com/johnfercher/maroto/mocks" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestNewMath(t *testing.T) { + math := NewMath(&mocks.Pdf{}) + + assert.NotNil(t, math) + assert.Equal(t, fmt.Sprintf("%T", math), "*math.math") +} + +func TestMath_GetWidthPerCol(t *testing.T) { + cases := []struct { + name string + qtdCols float64 + pdf func() *mocks.Pdf + assertCalls func(t *testing.T, pdf *mocks.Pdf) + assertWidth func(t *testing.T, width float64) + }{ + { + "1 col, margins 10 10", + 1, + func() *mocks.Pdf { + pdf := &mocks.Pdf{} + pdf.On("GetPageSize").Return(210.0, 0.0) + pdf.On("GetMargins").Return(10.0, 10.0, 10.0, 10.0) + return pdf + }, + func(t *testing.T, pdf *mocks.Pdf) { + pdf.AssertNumberOfCalls(t, "GetPageSize", 1) + pdf.AssertNumberOfCalls(t, "GetMargins", 1) + }, + func(t *testing.T, width float64) { + assert.Equal(t, int(width), 190) + }, + }, + { + "2 col, margins 10 10", + 2, + func() *mocks.Pdf { + pdf := &mocks.Pdf{} + pdf.On("GetPageSize").Return(210.0, 0.0) + pdf.On("GetMargins").Return(10.0, 10.0, 10.0, 10.0) + return pdf + }, + func(t *testing.T, pdf *mocks.Pdf) { + pdf.AssertNumberOfCalls(t, "GetPageSize", 1) + pdf.AssertNumberOfCalls(t, "GetMargins", 1) + }, + func(t *testing.T, width float64) { + assert.Equal(t, int(width), 95) + }, + }, + { + "4 col, margins 10 10", + 4, + func() *mocks.Pdf { + pdf := &mocks.Pdf{} + pdf.On("GetPageSize").Return(210.0, 0.0) + pdf.On("GetMargins").Return(10.0, 10.0, 10.0, 10.0) + return pdf + }, + func(t *testing.T, pdf *mocks.Pdf) { + pdf.AssertNumberOfCalls(t, "GetPageSize", 1) + pdf.AssertNumberOfCalls(t, "GetMargins", 1) + }, + func(t *testing.T, width float64) { + assert.Equal(t, int(width), 47) + }, + }, + { + "1 col, margins 20 20", + 1, + func() *mocks.Pdf { + pdf := &mocks.Pdf{} + pdf.On("GetPageSize").Return(210.0, 0.0) + pdf.On("GetMargins").Return(20.0, 20.0, 20.0, 20.0) + return pdf + }, + func(t *testing.T, pdf *mocks.Pdf) { + pdf.AssertNumberOfCalls(t, "GetPageSize", 1) + pdf.AssertNumberOfCalls(t, "GetMargins", 1) + }, + func(t *testing.T, width float64) { + assert.Equal(t, int(width), 170) + }, + }, + { + "2 col, margins 20 20", + 2, + func() *mocks.Pdf { + pdf := &mocks.Pdf{} + pdf.On("GetPageSize").Return(210.0, 0.0) + pdf.On("GetMargins").Return(20.0, 20.0, 20.0, 20.0) + return pdf + }, + func(t *testing.T, pdf *mocks.Pdf) { + pdf.AssertNumberOfCalls(t, "GetPageSize", 1) + pdf.AssertNumberOfCalls(t, "GetMargins", 1) + }, + func(t *testing.T, width float64) { + assert.Equal(t, int(width), 85) + }, + }, + { + "4 col, margins 20 20", + 4, + func() *mocks.Pdf { + pdf := &mocks.Pdf{} + pdf.On("GetPageSize").Return(210.0, 0.0) + pdf.On("GetMargins").Return(20.0, 20.0, 20.0, 20.0) + return pdf + }, + func(t *testing.T, pdf *mocks.Pdf) { + pdf.AssertNumberOfCalls(t, "GetPageSize", 1) + pdf.AssertNumberOfCalls(t, "GetMargins", 1) + }, + func(t *testing.T, width float64) { + assert.Equal(t, int(width), 42) + }, + }, + } + + for _, c := range cases { + // Arrange + pdf := c.pdf() + math := NewMath(pdf) + + // Act + width := math.GetWidthPerCol(c.qtdCols) + + // Assert + c.assertWidth(t, width) + c.assertCalls(t, pdf) + } +} diff --git a/mocks/font.go b/mocks/font.go new file mode 100644 index 00000000..6628b4e4 --- /dev/null +++ b/mocks/font.go @@ -0,0 +1,129 @@ +// Code generated by mockery v1.0.0. DO NOT EDIT. + +package mocks + +import font "github.com/johnfercher/maroto/font" +import mock "github.com/stretchr/testify/mock" + +// Font is an autogenerated mock type for the Font type +type Font struct { + mock.Mock +} + +// GetFamily provides a mock function with given fields: +func (_m *Font) GetFamily() font.Family { + ret := _m.Called() + + var r0 font.Family + if rf, ok := ret.Get(0).(func() font.Family); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(font.Family) + } + + return r0 +} + +// GetFamilyString provides a mock function with given fields: _a0 +func (_m *Font) GetFamilyString(_a0 font.Family) string { + ret := _m.Called(_a0) + + var r0 string + if rf, ok := ret.Get(0).(func(font.Family) string); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// GetFont provides a mock function with given fields: +func (_m *Font) GetFont() (font.Family, font.Style, float64) { + ret := _m.Called() + + var r0 font.Family + if rf, ok := ret.Get(0).(func() font.Family); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(font.Family) + } + + var r1 font.Style + if rf, ok := ret.Get(1).(func() font.Style); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(font.Style) + } + + var r2 float64 + if rf, ok := ret.Get(2).(func() float64); ok { + r2 = rf() + } else { + r2 = ret.Get(2).(float64) + } + + return r0, r1, r2 +} + +// GetSize provides a mock function with given fields: +func (_m *Font) GetSize() float64 { + ret := _m.Called() + + var r0 float64 + if rf, ok := ret.Get(0).(func() float64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(float64) + } + + return r0 +} + +// GetStyle provides a mock function with given fields: +func (_m *Font) GetStyle() font.Style { + ret := _m.Called() + + var r0 font.Style + if rf, ok := ret.Get(0).(func() font.Style); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(font.Style) + } + + return r0 +} + +// GetStyleString provides a mock function with given fields: style +func (_m *Font) GetStyleString(style font.Style) string { + ret := _m.Called(style) + + var r0 string + if rf, ok := ret.Get(0).(func(font.Style) string); ok { + r0 = rf(style) + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// SetFamily provides a mock function with given fields: family +func (_m *Font) SetFamily(family font.Family) { + _m.Called(family) +} + +// SetFont provides a mock function with given fields: family, style, size +func (_m *Font) SetFont(family font.Family, style font.Style, size float64) { + _m.Called(family, style, size) +} + +// SetSize provides a mock function with given fields: size +func (_m *Font) SetSize(size float64) { + _m.Called(size) +} + +// SetStyle provides a mock function with given fields: style +func (_m *Font) SetStyle(style font.Style) { + _m.Called(style) +} diff --git a/mocks/math.go b/mocks/math.go new file mode 100644 index 00000000..452f2c13 --- /dev/null +++ b/mocks/math.go @@ -0,0 +1,24 @@ +// Code generated by mockery v1.0.0. DO NOT EDIT. + +package mocks + +import mock "github.com/stretchr/testify/mock" + +// Math is an autogenerated mock type for the Math type +type Math struct { + mock.Mock +} + +// GetWidthPerCol provides a mock function with given fields: qtdCols +func (_m *Math) GetWidthPerCol(qtdCols float64) float64 { + ret := _m.Called(qtdCols) + + var r0 float64 + if rf, ok := ret.Get(0).(func(float64) float64); ok { + r0 = rf(qtdCols) + } else { + r0 = ret.Get(0).(float64) + } + + return r0 +} diff --git a/mocks/pdf.go b/mocks/pdf.go new file mode 100644 index 00000000..e644ab5c --- /dev/null +++ b/mocks/pdf.go @@ -0,0 +1,1520 @@ +// Code generated by mockery v1.0.0. DO NOT EDIT. + +package mocks + +import gofpdf "github.com/jung-kurt/gofpdf" +import io "io" +import mock "github.com/stretchr/testify/mock" +import time "time" + +// Pdf is an autogenerated mock type for the Pdf type +type Pdf struct { + mock.Mock +} + +// AddFont provides a mock function with given fields: familyStr, styleStr, fileStr +func (_m *Pdf) AddFont(familyStr string, styleStr string, fileStr string) { + _m.Called(familyStr, styleStr, fileStr) +} + +// AddFontFromBytes provides a mock function with given fields: familyStr, styleStr, jsonFileBytes, zFileBytes +func (_m *Pdf) AddFontFromBytes(familyStr string, styleStr string, jsonFileBytes []byte, zFileBytes []byte) { + _m.Called(familyStr, styleStr, jsonFileBytes, zFileBytes) +} + +// AddFontFromReader provides a mock function with given fields: familyStr, styleStr, r +func (_m *Pdf) AddFontFromReader(familyStr string, styleStr string, r io.Reader) { + _m.Called(familyStr, styleStr, r) +} + +// AddLayer provides a mock function with given fields: name, visible +func (_m *Pdf) AddLayer(name string, visible bool) int { + ret := _m.Called(name, visible) + + var r0 int + if rf, ok := ret.Get(0).(func(string, bool) int); ok { + r0 = rf(name, visible) + } else { + r0 = ret.Get(0).(int) + } + + return r0 +} + +// AddLink provides a mock function with given fields: +func (_m *Pdf) AddLink() int { + ret := _m.Called() + + var r0 int + if rf, ok := ret.Get(0).(func() int); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int) + } + + return r0 +} + +// AddPage provides a mock function with given fields: +func (_m *Pdf) AddPage() { + _m.Called() +} + +// AddPageFormat provides a mock function with given fields: orientationStr, size +func (_m *Pdf) AddPageFormat(orientationStr string, size gofpdf.SizeType) { + _m.Called(orientationStr, size) +} + +// AddSpotColor provides a mock function with given fields: nameStr, c, m, y, k +func (_m *Pdf) AddSpotColor(nameStr string, c byte, m byte, y byte, k byte) { + _m.Called(nameStr, c, m, y, k) +} + +// AliasNbPages provides a mock function with given fields: aliasStr +func (_m *Pdf) AliasNbPages(aliasStr string) { + _m.Called(aliasStr) +} + +// Arc provides a mock function with given fields: x, y, rx, ry, degRotate, degStart, degEnd, styleStr +func (_m *Pdf) Arc(x float64, y float64, rx float64, ry float64, degRotate float64, degStart float64, degEnd float64, styleStr string) { + _m.Called(x, y, rx, ry, degRotate, degStart, degEnd, styleStr) +} + +// ArcTo provides a mock function with given fields: x, y, rx, ry, degRotate, degStart, degEnd +func (_m *Pdf) ArcTo(x float64, y float64, rx float64, ry float64, degRotate float64, degStart float64, degEnd float64) { + _m.Called(x, y, rx, ry, degRotate, degStart, degEnd) +} + +// BeginLayer provides a mock function with given fields: id +func (_m *Pdf) BeginLayer(id int) { + _m.Called(id) +} + +// Beziergon provides a mock function with given fields: points, styleStr +func (_m *Pdf) Beziergon(points []gofpdf.PointType, styleStr string) { + _m.Called(points, styleStr) +} + +// Bookmark provides a mock function with given fields: txtStr, level, y +func (_m *Pdf) Bookmark(txtStr string, level int, y float64) { + _m.Called(txtStr, level, y) +} + +// Cell provides a mock function with given fields: w, h, txtStr +func (_m *Pdf) Cell(w float64, h float64, txtStr string) { + _m.Called(w, h, txtStr) +} + +// CellFormat provides a mock function with given fields: w, h, txtStr, borderStr, ln, alignStr, fill, link, linkStr +func (_m *Pdf) CellFormat(w float64, h float64, txtStr string, borderStr string, ln int, alignStr string, fill bool, link int, linkStr string) { + _m.Called(w, h, txtStr, borderStr, ln, alignStr, fill, link, linkStr) +} + +// Cellf provides a mock function with given fields: w, h, fmtStr, args +func (_m *Pdf) Cellf(w float64, h float64, fmtStr string, args ...interface{}) { + var _ca []interface{} + _ca = append(_ca, w, h, fmtStr) + _ca = append(_ca, args...) + _m.Called(_ca...) +} + +// Circle provides a mock function with given fields: x, y, r, styleStr +func (_m *Pdf) Circle(x float64, y float64, r float64, styleStr string) { + _m.Called(x, y, r, styleStr) +} + +// ClearError provides a mock function with given fields: +func (_m *Pdf) ClearError() { + _m.Called() +} + +// ClipCircle provides a mock function with given fields: x, y, r, outline +func (_m *Pdf) ClipCircle(x float64, y float64, r float64, outline bool) { + _m.Called(x, y, r, outline) +} + +// ClipEllipse provides a mock function with given fields: x, y, rx, ry, outline +func (_m *Pdf) ClipEllipse(x float64, y float64, rx float64, ry float64, outline bool) { + _m.Called(x, y, rx, ry, outline) +} + +// ClipEnd provides a mock function with given fields: +func (_m *Pdf) ClipEnd() { + _m.Called() +} + +// ClipPolygon provides a mock function with given fields: points, outline +func (_m *Pdf) ClipPolygon(points []gofpdf.PointType, outline bool) { + _m.Called(points, outline) +} + +// ClipRect provides a mock function with given fields: x, y, w, h, outline +func (_m *Pdf) ClipRect(x float64, y float64, w float64, h float64, outline bool) { + _m.Called(x, y, w, h, outline) +} + +// ClipRoundedRect provides a mock function with given fields: x, y, w, h, r, outline +func (_m *Pdf) ClipRoundedRect(x float64, y float64, w float64, h float64, r float64, outline bool) { + _m.Called(x, y, w, h, r, outline) +} + +// ClipText provides a mock function with given fields: x, y, txtStr, outline +func (_m *Pdf) ClipText(x float64, y float64, txtStr string, outline bool) { + _m.Called(x, y, txtStr, outline) +} + +// Close provides a mock function with given fields: +func (_m *Pdf) Close() { + _m.Called() +} + +// ClosePath provides a mock function with given fields: +func (_m *Pdf) ClosePath() { + _m.Called() +} + +// CreateTemplate provides a mock function with given fields: fn +func (_m *Pdf) CreateTemplate(fn func(*gofpdf.Tpl)) gofpdf.Template { + ret := _m.Called(fn) + + var r0 gofpdf.Template + if rf, ok := ret.Get(0).(func(func(*gofpdf.Tpl)) gofpdf.Template); ok { + r0 = rf(fn) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(gofpdf.Template) + } + } + + return r0 +} + +// CreateTemplateCustom provides a mock function with given fields: corner, size, fn +func (_m *Pdf) CreateTemplateCustom(corner gofpdf.PointType, size gofpdf.SizeType, fn func(*gofpdf.Tpl)) gofpdf.Template { + ret := _m.Called(corner, size, fn) + + var r0 gofpdf.Template + if rf, ok := ret.Get(0).(func(gofpdf.PointType, gofpdf.SizeType, func(*gofpdf.Tpl)) gofpdf.Template); ok { + r0 = rf(corner, size, fn) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(gofpdf.Template) + } + } + + return r0 +} + +// Curve provides a mock function with given fields: x0, y0, cx, cy, x1, y1, styleStr +func (_m *Pdf) Curve(x0 float64, y0 float64, cx float64, cy float64, x1 float64, y1 float64, styleStr string) { + _m.Called(x0, y0, cx, cy, x1, y1, styleStr) +} + +// CurveBezierCubic provides a mock function with given fields: x0, y0, cx0, cy0, cx1, cy1, x1, y1, styleStr +func (_m *Pdf) CurveBezierCubic(x0 float64, y0 float64, cx0 float64, cy0 float64, cx1 float64, cy1 float64, x1 float64, y1 float64, styleStr string) { + _m.Called(x0, y0, cx0, cy0, cx1, cy1, x1, y1, styleStr) +} + +// CurveBezierCubicTo provides a mock function with given fields: cx0, cy0, cx1, cy1, x, y +func (_m *Pdf) CurveBezierCubicTo(cx0 float64, cy0 float64, cx1 float64, cy1 float64, x float64, y float64) { + _m.Called(cx0, cy0, cx1, cy1, x, y) +} + +// CurveCubic provides a mock function with given fields: x0, y0, cx0, cy0, x1, y1, cx1, cy1, styleStr +func (_m *Pdf) CurveCubic(x0 float64, y0 float64, cx0 float64, cy0 float64, x1 float64, y1 float64, cx1 float64, cy1 float64, styleStr string) { + _m.Called(x0, y0, cx0, cy0, x1, y1, cx1, cy1, styleStr) +} + +// CurveTo provides a mock function with given fields: cx, cy, x, y +func (_m *Pdf) CurveTo(cx float64, cy float64, x float64, y float64) { + _m.Called(cx, cy, x, y) +} + +// DrawPath provides a mock function with given fields: styleStr +func (_m *Pdf) DrawPath(styleStr string) { + _m.Called(styleStr) +} + +// Ellipse provides a mock function with given fields: x, y, rx, ry, degRotate, styleStr +func (_m *Pdf) Ellipse(x float64, y float64, rx float64, ry float64, degRotate float64, styleStr string) { + _m.Called(x, y, rx, ry, degRotate, styleStr) +} + +// EndLayer provides a mock function with given fields: +func (_m *Pdf) EndLayer() { + _m.Called() +} + +// Err provides a mock function with given fields: +func (_m *Pdf) Err() bool { + ret := _m.Called() + + var r0 bool + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// Error provides a mock function with given fields: +func (_m *Pdf) Error() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// GetAlpha provides a mock function with given fields: +func (_m *Pdf) GetAlpha() (float64, string) { + ret := _m.Called() + + var r0 float64 + if rf, ok := ret.Get(0).(func() float64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(float64) + } + + var r1 string + if rf, ok := ret.Get(1).(func() string); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(string) + } + + return r0, r1 +} + +// GetAutoPageBreak provides a mock function with given fields: +func (_m *Pdf) GetAutoPageBreak() (bool, float64) { + ret := _m.Called() + + var r0 bool + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + var r1 float64 + if rf, ok := ret.Get(1).(func() float64); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(float64) + } + + return r0, r1 +} + +// GetCellMargin provides a mock function with given fields: +func (_m *Pdf) GetCellMargin() float64 { + ret := _m.Called() + + var r0 float64 + if rf, ok := ret.Get(0).(func() float64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(float64) + } + + return r0 +} + +// GetConversionRatio provides a mock function with given fields: +func (_m *Pdf) GetConversionRatio() float64 { + ret := _m.Called() + + var r0 float64 + if rf, ok := ret.Get(0).(func() float64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(float64) + } + + return r0 +} + +// GetDrawColor provides a mock function with given fields: +func (_m *Pdf) GetDrawColor() (int, int, int) { + ret := _m.Called() + + var r0 int + if rf, ok := ret.Get(0).(func() int); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int) + } + + var r1 int + if rf, ok := ret.Get(1).(func() int); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(int) + } + + var r2 int + if rf, ok := ret.Get(2).(func() int); ok { + r2 = rf() + } else { + r2 = ret.Get(2).(int) + } + + return r0, r1, r2 +} + +// GetDrawSpotColor provides a mock function with given fields: +func (_m *Pdf) GetDrawSpotColor() (string, byte, byte, byte, byte) { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + var r1 byte + if rf, ok := ret.Get(1).(func() byte); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(byte) + } + + var r2 byte + if rf, ok := ret.Get(2).(func() byte); ok { + r2 = rf() + } else { + r2 = ret.Get(2).(byte) + } + + var r3 byte + if rf, ok := ret.Get(3).(func() byte); ok { + r3 = rf() + } else { + r3 = ret.Get(3).(byte) + } + + var r4 byte + if rf, ok := ret.Get(4).(func() byte); ok { + r4 = rf() + } else { + r4 = ret.Get(4).(byte) + } + + return r0, r1, r2, r3, r4 +} + +// GetFillColor provides a mock function with given fields: +func (_m *Pdf) GetFillColor() (int, int, int) { + ret := _m.Called() + + var r0 int + if rf, ok := ret.Get(0).(func() int); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int) + } + + var r1 int + if rf, ok := ret.Get(1).(func() int); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(int) + } + + var r2 int + if rf, ok := ret.Get(2).(func() int); ok { + r2 = rf() + } else { + r2 = ret.Get(2).(int) + } + + return r0, r1, r2 +} + +// GetFillSpotColor provides a mock function with given fields: +func (_m *Pdf) GetFillSpotColor() (string, byte, byte, byte, byte) { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + var r1 byte + if rf, ok := ret.Get(1).(func() byte); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(byte) + } + + var r2 byte + if rf, ok := ret.Get(2).(func() byte); ok { + r2 = rf() + } else { + r2 = ret.Get(2).(byte) + } + + var r3 byte + if rf, ok := ret.Get(3).(func() byte); ok { + r3 = rf() + } else { + r3 = ret.Get(3).(byte) + } + + var r4 byte + if rf, ok := ret.Get(4).(func() byte); ok { + r4 = rf() + } else { + r4 = ret.Get(4).(byte) + } + + return r0, r1, r2, r3, r4 +} + +// GetFontDesc provides a mock function with given fields: familyStr, styleStr +func (_m *Pdf) GetFontDesc(familyStr string, styleStr string) gofpdf.FontDescType { + ret := _m.Called(familyStr, styleStr) + + var r0 gofpdf.FontDescType + if rf, ok := ret.Get(0).(func(string, string) gofpdf.FontDescType); ok { + r0 = rf(familyStr, styleStr) + } else { + r0 = ret.Get(0).(gofpdf.FontDescType) + } + + return r0 +} + +// GetFontSize provides a mock function with given fields: +func (_m *Pdf) GetFontSize() (float64, float64) { + ret := _m.Called() + + var r0 float64 + if rf, ok := ret.Get(0).(func() float64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(float64) + } + + var r1 float64 + if rf, ok := ret.Get(1).(func() float64); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(float64) + } + + return r0, r1 +} + +// GetImageInfo provides a mock function with given fields: imageStr +func (_m *Pdf) GetImageInfo(imageStr string) *gofpdf.ImageInfoType { + ret := _m.Called(imageStr) + + var r0 *gofpdf.ImageInfoType + if rf, ok := ret.Get(0).(func(string) *gofpdf.ImageInfoType); ok { + r0 = rf(imageStr) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*gofpdf.ImageInfoType) + } + } + + return r0 +} + +// GetLineWidth provides a mock function with given fields: +func (_m *Pdf) GetLineWidth() float64 { + ret := _m.Called() + + var r0 float64 + if rf, ok := ret.Get(0).(func() float64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(float64) + } + + return r0 +} + +// GetMargins provides a mock function with given fields: +func (_m *Pdf) GetMargins() (float64, float64, float64, float64) { + ret := _m.Called() + + var r0 float64 + if rf, ok := ret.Get(0).(func() float64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(float64) + } + + var r1 float64 + if rf, ok := ret.Get(1).(func() float64); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(float64) + } + + var r2 float64 + if rf, ok := ret.Get(2).(func() float64); ok { + r2 = rf() + } else { + r2 = ret.Get(2).(float64) + } + + var r3 float64 + if rf, ok := ret.Get(3).(func() float64); ok { + r3 = rf() + } else { + r3 = ret.Get(3).(float64) + } + + return r0, r1, r2, r3 +} + +// GetPageSize provides a mock function with given fields: +func (_m *Pdf) GetPageSize() (float64, float64) { + ret := _m.Called() + + var r0 float64 + if rf, ok := ret.Get(0).(func() float64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(float64) + } + + var r1 float64 + if rf, ok := ret.Get(1).(func() float64); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(float64) + } + + return r0, r1 +} + +// GetPageSizeStr provides a mock function with given fields: sizeStr +func (_m *Pdf) GetPageSizeStr(sizeStr string) gofpdf.SizeType { + ret := _m.Called(sizeStr) + + var r0 gofpdf.SizeType + if rf, ok := ret.Get(0).(func(string) gofpdf.SizeType); ok { + r0 = rf(sizeStr) + } else { + r0 = ret.Get(0).(gofpdf.SizeType) + } + + return r0 +} + +// GetStringWidth provides a mock function with given fields: s +func (_m *Pdf) GetStringWidth(s string) float64 { + ret := _m.Called(s) + + var r0 float64 + if rf, ok := ret.Get(0).(func(string) float64); ok { + r0 = rf(s) + } else { + r0 = ret.Get(0).(float64) + } + + return r0 +} + +// GetTextColor provides a mock function with given fields: +func (_m *Pdf) GetTextColor() (int, int, int) { + ret := _m.Called() + + var r0 int + if rf, ok := ret.Get(0).(func() int); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int) + } + + var r1 int + if rf, ok := ret.Get(1).(func() int); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(int) + } + + var r2 int + if rf, ok := ret.Get(2).(func() int); ok { + r2 = rf() + } else { + r2 = ret.Get(2).(int) + } + + return r0, r1, r2 +} + +// GetTextSpotColor provides a mock function with given fields: +func (_m *Pdf) GetTextSpotColor() (string, byte, byte, byte, byte) { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + var r1 byte + if rf, ok := ret.Get(1).(func() byte); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(byte) + } + + var r2 byte + if rf, ok := ret.Get(2).(func() byte); ok { + r2 = rf() + } else { + r2 = ret.Get(2).(byte) + } + + var r3 byte + if rf, ok := ret.Get(3).(func() byte); ok { + r3 = rf() + } else { + r3 = ret.Get(3).(byte) + } + + var r4 byte + if rf, ok := ret.Get(4).(func() byte); ok { + r4 = rf() + } else { + r4 = ret.Get(4).(byte) + } + + return r0, r1, r2, r3, r4 +} + +// GetX provides a mock function with given fields: +func (_m *Pdf) GetX() float64 { + ret := _m.Called() + + var r0 float64 + if rf, ok := ret.Get(0).(func() float64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(float64) + } + + return r0 +} + +// GetXY provides a mock function with given fields: +func (_m *Pdf) GetXY() (float64, float64) { + ret := _m.Called() + + var r0 float64 + if rf, ok := ret.Get(0).(func() float64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(float64) + } + + var r1 float64 + if rf, ok := ret.Get(1).(func() float64); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(float64) + } + + return r0, r1 +} + +// GetY provides a mock function with given fields: +func (_m *Pdf) GetY() float64 { + ret := _m.Called() + + var r0 float64 + if rf, ok := ret.Get(0).(func() float64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(float64) + } + + return r0 +} + +// HTMLBasicNew provides a mock function with given fields: +func (_m *Pdf) HTMLBasicNew() gofpdf.HTMLBasicType { + ret := _m.Called() + + var r0 gofpdf.HTMLBasicType + if rf, ok := ret.Get(0).(func() gofpdf.HTMLBasicType); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(gofpdf.HTMLBasicType) + } + + return r0 +} + +// Image provides a mock function with given fields: imageNameStr, x, y, w, h, flow, tp, link, linkStr +func (_m *Pdf) Image(imageNameStr string, x float64, y float64, w float64, h float64, flow bool, tp string, link int, linkStr string) { + _m.Called(imageNameStr, x, y, w, h, flow, tp, link, linkStr) +} + +// ImageOptions provides a mock function with given fields: imageNameStr, x, y, w, h, flow, options, link, linkStr +func (_m *Pdf) ImageOptions(imageNameStr string, x float64, y float64, w float64, h float64, flow bool, options gofpdf.ImageOptions, link int, linkStr string) { + _m.Called(imageNameStr, x, y, w, h, flow, options, link, linkStr) +} + +// ImageTypeFromMime provides a mock function with given fields: mimeStr +func (_m *Pdf) ImageTypeFromMime(mimeStr string) string { + ret := _m.Called(mimeStr) + + var r0 string + if rf, ok := ret.Get(0).(func(string) string); ok { + r0 = rf(mimeStr) + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// Line provides a mock function with given fields: x1, y1, x2, y2 +func (_m *Pdf) Line(x1 float64, y1 float64, x2 float64, y2 float64) { + _m.Called(x1, y1, x2, y2) +} + +// LineTo provides a mock function with given fields: x, y +func (_m *Pdf) LineTo(x float64, y float64) { + _m.Called(x, y) +} + +// LinearGradient provides a mock function with given fields: x, y, w, h, r1, g1, b1, r2, g2, b2, x1, y1, x2, y2 +func (_m *Pdf) LinearGradient(x float64, y float64, w float64, h float64, r1 int, g1 int, b1 int, r2 int, g2 int, b2 int, x1 float64, y1 float64, x2 float64, y2 float64) { + _m.Called(x, y, w, h, r1, g1, b1, r2, g2, b2, x1, y1, x2, y2) +} + +// Link provides a mock function with given fields: x, y, w, h, link +func (_m *Pdf) Link(x float64, y float64, w float64, h float64, link int) { + _m.Called(x, y, w, h, link) +} + +// LinkString provides a mock function with given fields: x, y, w, h, linkStr +func (_m *Pdf) LinkString(x float64, y float64, w float64, h float64, linkStr string) { + _m.Called(x, y, w, h, linkStr) +} + +// Ln provides a mock function with given fields: h +func (_m *Pdf) Ln(h float64) { + _m.Called(h) +} + +// MoveTo provides a mock function with given fields: x, y +func (_m *Pdf) MoveTo(x float64, y float64) { + _m.Called(x, y) +} + +// MultiCell provides a mock function with given fields: w, h, txtStr, borderStr, alignStr, fill +func (_m *Pdf) MultiCell(w float64, h float64, txtStr string, borderStr string, alignStr string, fill bool) { + _m.Called(w, h, txtStr, borderStr, alignStr, fill) +} + +// Ok provides a mock function with given fields: +func (_m *Pdf) Ok() bool { + ret := _m.Called() + + var r0 bool + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// OpenLayerPane provides a mock function with given fields: +func (_m *Pdf) OpenLayerPane() { + _m.Called() +} + +// Output provides a mock function with given fields: w +func (_m *Pdf) Output(w io.Writer) error { + ret := _m.Called(w) + + var r0 error + if rf, ok := ret.Get(0).(func(io.Writer) error); ok { + r0 = rf(w) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// OutputAndClose provides a mock function with given fields: w +func (_m *Pdf) OutputAndClose(w io.WriteCloser) error { + ret := _m.Called(w) + + var r0 error + if rf, ok := ret.Get(0).(func(io.WriteCloser) error); ok { + r0 = rf(w) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// OutputFileAndClose provides a mock function with given fields: fileStr +func (_m *Pdf) OutputFileAndClose(fileStr string) error { + ret := _m.Called(fileStr) + + var r0 error + if rf, ok := ret.Get(0).(func(string) error); ok { + r0 = rf(fileStr) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// PageCount provides a mock function with given fields: +func (_m *Pdf) PageCount() int { + ret := _m.Called() + + var r0 int + if rf, ok := ret.Get(0).(func() int); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int) + } + + return r0 +} + +// PageNo provides a mock function with given fields: +func (_m *Pdf) PageNo() int { + ret := _m.Called() + + var r0 int + if rf, ok := ret.Get(0).(func() int); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int) + } + + return r0 +} + +// PageSize provides a mock function with given fields: pageNum +func (_m *Pdf) PageSize(pageNum int) (float64, float64, string) { + ret := _m.Called(pageNum) + + var r0 float64 + if rf, ok := ret.Get(0).(func(int) float64); ok { + r0 = rf(pageNum) + } else { + r0 = ret.Get(0).(float64) + } + + var r1 float64 + if rf, ok := ret.Get(1).(func(int) float64); ok { + r1 = rf(pageNum) + } else { + r1 = ret.Get(1).(float64) + } + + var r2 string + if rf, ok := ret.Get(2).(func(int) string); ok { + r2 = rf(pageNum) + } else { + r2 = ret.Get(2).(string) + } + + return r0, r1, r2 +} + +// PointConvert provides a mock function with given fields: pt +func (_m *Pdf) PointConvert(pt float64) float64 { + ret := _m.Called(pt) + + var r0 float64 + if rf, ok := ret.Get(0).(func(float64) float64); ok { + r0 = rf(pt) + } else { + r0 = ret.Get(0).(float64) + } + + return r0 +} + +// PointToUnitConvert provides a mock function with given fields: pt +func (_m *Pdf) PointToUnitConvert(pt float64) float64 { + ret := _m.Called(pt) + + var r0 float64 + if rf, ok := ret.Get(0).(func(float64) float64); ok { + r0 = rf(pt) + } else { + r0 = ret.Get(0).(float64) + } + + return r0 +} + +// Polygon provides a mock function with given fields: points, styleStr +func (_m *Pdf) Polygon(points []gofpdf.PointType, styleStr string) { + _m.Called(points, styleStr) +} + +// RadialGradient provides a mock function with given fields: x, y, w, h, r1, g1, b1, r2, g2, b2, x1, y1, x2, y2, r +func (_m *Pdf) RadialGradient(x float64, y float64, w float64, h float64, r1 int, g1 int, b1 int, r2 int, g2 int, b2 int, x1 float64, y1 float64, x2 float64, y2 float64, r float64) { + _m.Called(x, y, w, h, r1, g1, b1, r2, g2, b2, x1, y1, x2, y2, r) +} + +// RawWriteBuf provides a mock function with given fields: r +func (_m *Pdf) RawWriteBuf(r io.Reader) { + _m.Called(r) +} + +// RawWriteStr provides a mock function with given fields: str +func (_m *Pdf) RawWriteStr(str string) { + _m.Called(str) +} + +// Rect provides a mock function with given fields: x, y, w, h, styleStr +func (_m *Pdf) Rect(x float64, y float64, w float64, h float64, styleStr string) { + _m.Called(x, y, w, h, styleStr) +} + +// RegisterAlias provides a mock function with given fields: alias, replacement +func (_m *Pdf) RegisterAlias(alias string, replacement string) { + _m.Called(alias, replacement) +} + +// RegisterImage provides a mock function with given fields: fileStr, tp +func (_m *Pdf) RegisterImage(fileStr string, tp string) *gofpdf.ImageInfoType { + ret := _m.Called(fileStr, tp) + + var r0 *gofpdf.ImageInfoType + if rf, ok := ret.Get(0).(func(string, string) *gofpdf.ImageInfoType); ok { + r0 = rf(fileStr, tp) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*gofpdf.ImageInfoType) + } + } + + return r0 +} + +// RegisterImageOptions provides a mock function with given fields: fileStr, options +func (_m *Pdf) RegisterImageOptions(fileStr string, options gofpdf.ImageOptions) *gofpdf.ImageInfoType { + ret := _m.Called(fileStr, options) + + var r0 *gofpdf.ImageInfoType + if rf, ok := ret.Get(0).(func(string, gofpdf.ImageOptions) *gofpdf.ImageInfoType); ok { + r0 = rf(fileStr, options) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*gofpdf.ImageInfoType) + } + } + + return r0 +} + +// RegisterImageOptionsReader provides a mock function with given fields: imgName, options, r +func (_m *Pdf) RegisterImageOptionsReader(imgName string, options gofpdf.ImageOptions, r io.Reader) *gofpdf.ImageInfoType { + ret := _m.Called(imgName, options, r) + + var r0 *gofpdf.ImageInfoType + if rf, ok := ret.Get(0).(func(string, gofpdf.ImageOptions, io.Reader) *gofpdf.ImageInfoType); ok { + r0 = rf(imgName, options, r) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*gofpdf.ImageInfoType) + } + } + + return r0 +} + +// RegisterImageReader provides a mock function with given fields: imgName, tp, r +func (_m *Pdf) RegisterImageReader(imgName string, tp string, r io.Reader) *gofpdf.ImageInfoType { + ret := _m.Called(imgName, tp, r) + + var r0 *gofpdf.ImageInfoType + if rf, ok := ret.Get(0).(func(string, string, io.Reader) *gofpdf.ImageInfoType); ok { + r0 = rf(imgName, tp, r) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*gofpdf.ImageInfoType) + } + } + + return r0 +} + +// SVGBasicWrite provides a mock function with given fields: sb, scale +func (_m *Pdf) SVGBasicWrite(sb *gofpdf.SVGBasicType, scale float64) { + _m.Called(sb, scale) +} + +// SetAcceptPageBreakFunc provides a mock function with given fields: fnc +func (_m *Pdf) SetAcceptPageBreakFunc(fnc func() bool) { + _m.Called(fnc) +} + +// SetAlpha provides a mock function with given fields: alpha, blendModeStr +func (_m *Pdf) SetAlpha(alpha float64, blendModeStr string) { + _m.Called(alpha, blendModeStr) +} + +// SetAuthor provides a mock function with given fields: authorStr, isUTF8 +func (_m *Pdf) SetAuthor(authorStr string, isUTF8 bool) { + _m.Called(authorStr, isUTF8) +} + +// SetAutoPageBreak provides a mock function with given fields: auto, margin +func (_m *Pdf) SetAutoPageBreak(auto bool, margin float64) { + _m.Called(auto, margin) +} + +// SetCatalogSort provides a mock function with given fields: flag +func (_m *Pdf) SetCatalogSort(flag bool) { + _m.Called(flag) +} + +// SetCellMargin provides a mock function with given fields: margin +func (_m *Pdf) SetCellMargin(margin float64) { + _m.Called(margin) +} + +// SetCompression provides a mock function with given fields: compress +func (_m *Pdf) SetCompression(compress bool) { + _m.Called(compress) +} + +// SetCreationDate provides a mock function with given fields: tm +func (_m *Pdf) SetCreationDate(tm time.Time) { + _m.Called(tm) +} + +// SetCreator provides a mock function with given fields: creatorStr, isUTF8 +func (_m *Pdf) SetCreator(creatorStr string, isUTF8 bool) { + _m.Called(creatorStr, isUTF8) +} + +// SetDashPattern provides a mock function with given fields: dashArray, dashPhase +func (_m *Pdf) SetDashPattern(dashArray []float64, dashPhase float64) { + _m.Called(dashArray, dashPhase) +} + +// SetDisplayMode provides a mock function with given fields: zoomStr, layoutStr +func (_m *Pdf) SetDisplayMode(zoomStr string, layoutStr string) { + _m.Called(zoomStr, layoutStr) +} + +// SetDrawColor provides a mock function with given fields: r, g, b +func (_m *Pdf) SetDrawColor(r int, g int, b int) { + _m.Called(r, g, b) +} + +// SetDrawSpotColor provides a mock function with given fields: nameStr, tint +func (_m *Pdf) SetDrawSpotColor(nameStr string, tint byte) { + _m.Called(nameStr, tint) +} + +// SetError provides a mock function with given fields: err +func (_m *Pdf) SetError(err error) { + _m.Called(err) +} + +// SetErrorf provides a mock function with given fields: fmtStr, args +func (_m *Pdf) SetErrorf(fmtStr string, args ...interface{}) { + var _ca []interface{} + _ca = append(_ca, fmtStr) + _ca = append(_ca, args...) + _m.Called(_ca...) +} + +// SetFillColor provides a mock function with given fields: r, g, b +func (_m *Pdf) SetFillColor(r int, g int, b int) { + _m.Called(r, g, b) +} + +// SetFillSpotColor provides a mock function with given fields: nameStr, tint +func (_m *Pdf) SetFillSpotColor(nameStr string, tint byte) { + _m.Called(nameStr, tint) +} + +// SetFont provides a mock function with given fields: familyStr, styleStr, size +func (_m *Pdf) SetFont(familyStr string, styleStr string, size float64) { + _m.Called(familyStr, styleStr, size) +} + +// SetFontLoader provides a mock function with given fields: loader +func (_m *Pdf) SetFontLoader(loader gofpdf.FontLoader) { + _m.Called(loader) +} + +// SetFontLocation provides a mock function with given fields: fontDirStr +func (_m *Pdf) SetFontLocation(fontDirStr string) { + _m.Called(fontDirStr) +} + +// SetFontSize provides a mock function with given fields: size +func (_m *Pdf) SetFontSize(size float64) { + _m.Called(size) +} + +// SetFontStyle provides a mock function with given fields: styleStr +func (_m *Pdf) SetFontStyle(styleStr string) { + _m.Called(styleStr) +} + +// SetFontUnitSize provides a mock function with given fields: size +func (_m *Pdf) SetFontUnitSize(size float64) { + _m.Called(size) +} + +// SetFooterFunc provides a mock function with given fields: fnc +func (_m *Pdf) SetFooterFunc(fnc func()) { + _m.Called(fnc) +} + +// SetFooterFuncLpi provides a mock function with given fields: fnc +func (_m *Pdf) SetFooterFuncLpi(fnc func(bool)) { + _m.Called(fnc) +} + +// SetHeaderFunc provides a mock function with given fields: fnc +func (_m *Pdf) SetHeaderFunc(fnc func()) { + _m.Called(fnc) +} + +// SetHeaderFuncMode provides a mock function with given fields: fnc, homeMode +func (_m *Pdf) SetHeaderFuncMode(fnc func(), homeMode bool) { + _m.Called(fnc, homeMode) +} + +// SetHomeXY provides a mock function with given fields: +func (_m *Pdf) SetHomeXY() { + _m.Called() +} + +// SetJavascript provides a mock function with given fields: script +func (_m *Pdf) SetJavascript(script string) { + _m.Called(script) +} + +// SetKeywords provides a mock function with given fields: keywordsStr, isUTF8 +func (_m *Pdf) SetKeywords(keywordsStr string, isUTF8 bool) { + _m.Called(keywordsStr, isUTF8) +} + +// SetLeftMargin provides a mock function with given fields: margin +func (_m *Pdf) SetLeftMargin(margin float64) { + _m.Called(margin) +} + +// SetLineCapStyle provides a mock function with given fields: styleStr +func (_m *Pdf) SetLineCapStyle(styleStr string) { + _m.Called(styleStr) +} + +// SetLineJoinStyle provides a mock function with given fields: styleStr +func (_m *Pdf) SetLineJoinStyle(styleStr string) { + _m.Called(styleStr) +} + +// SetLineWidth provides a mock function with given fields: width +func (_m *Pdf) SetLineWidth(width float64) { + _m.Called(width) +} + +// SetLink provides a mock function with given fields: link, y, page +func (_m *Pdf) SetLink(link int, y float64, page int) { + _m.Called(link, y, page) +} + +// SetMargins provides a mock function with given fields: left, top, right +func (_m *Pdf) SetMargins(left float64, top float64, right float64) { + _m.Called(left, top, right) +} + +// SetPage provides a mock function with given fields: pageNum +func (_m *Pdf) SetPage(pageNum int) { + _m.Called(pageNum) +} + +// SetPageBox provides a mock function with given fields: t, x, y, wd, ht +func (_m *Pdf) SetPageBox(t string, x float64, y float64, wd float64, ht float64) { + _m.Called(t, x, y, wd, ht) +} + +// SetPageBoxRec provides a mock function with given fields: t, pb +func (_m *Pdf) SetPageBoxRec(t string, pb gofpdf.PageBox) { + _m.Called(t, pb) +} + +// SetProtection provides a mock function with given fields: actionFlag, userPassStr, ownerPassStr +func (_m *Pdf) SetProtection(actionFlag byte, userPassStr string, ownerPassStr string) { + _m.Called(actionFlag, userPassStr, ownerPassStr) +} + +// SetRightMargin provides a mock function with given fields: margin +func (_m *Pdf) SetRightMargin(margin float64) { + _m.Called(margin) +} + +// SetSubject provides a mock function with given fields: subjectStr, isUTF8 +func (_m *Pdf) SetSubject(subjectStr string, isUTF8 bool) { + _m.Called(subjectStr, isUTF8) +} + +// SetTextColor provides a mock function with given fields: r, g, b +func (_m *Pdf) SetTextColor(r int, g int, b int) { + _m.Called(r, g, b) +} + +// SetTextSpotColor provides a mock function with given fields: nameStr, tint +func (_m *Pdf) SetTextSpotColor(nameStr string, tint byte) { + _m.Called(nameStr, tint) +} + +// SetTitle provides a mock function with given fields: titleStr, isUTF8 +func (_m *Pdf) SetTitle(titleStr string, isUTF8 bool) { + _m.Called(titleStr, isUTF8) +} + +// SetTopMargin provides a mock function with given fields: margin +func (_m *Pdf) SetTopMargin(margin float64) { + _m.Called(margin) +} + +// SetX provides a mock function with given fields: x +func (_m *Pdf) SetX(x float64) { + _m.Called(x) +} + +// SetXY provides a mock function with given fields: x, y +func (_m *Pdf) SetXY(x float64, y float64) { + _m.Called(x, y) +} + +// SetXmpMetadata provides a mock function with given fields: xmpStream +func (_m *Pdf) SetXmpMetadata(xmpStream []byte) { + _m.Called(xmpStream) +} + +// SetY provides a mock function with given fields: y +func (_m *Pdf) SetY(y float64) { + _m.Called(y) +} + +// SplitLines provides a mock function with given fields: txt, w +func (_m *Pdf) SplitLines(txt []byte, w float64) [][]byte { + ret := _m.Called(txt, w) + + var r0 [][]byte + if rf, ok := ret.Get(0).(func([]byte, float64) [][]byte); ok { + r0 = rf(txt, w) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([][]byte) + } + } + + return r0 +} + +// String provides a mock function with given fields: +func (_m *Pdf) String() string { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// Text provides a mock function with given fields: x, y, txtStr +func (_m *Pdf) Text(x float64, y float64, txtStr string) { + _m.Called(x, y, txtStr) +} + +// Transform provides a mock function with given fields: tm +func (_m *Pdf) Transform(tm gofpdf.TransformMatrix) { + _m.Called(tm) +} + +// TransformBegin provides a mock function with given fields: +func (_m *Pdf) TransformBegin() { + _m.Called() +} + +// TransformEnd provides a mock function with given fields: +func (_m *Pdf) TransformEnd() { + _m.Called() +} + +// TransformMirrorHorizontal provides a mock function with given fields: x +func (_m *Pdf) TransformMirrorHorizontal(x float64) { + _m.Called(x) +} + +// TransformMirrorLine provides a mock function with given fields: angle, x, y +func (_m *Pdf) TransformMirrorLine(angle float64, x float64, y float64) { + _m.Called(angle, x, y) +} + +// TransformMirrorPoint provides a mock function with given fields: x, y +func (_m *Pdf) TransformMirrorPoint(x float64, y float64) { + _m.Called(x, y) +} + +// TransformMirrorVertical provides a mock function with given fields: y +func (_m *Pdf) TransformMirrorVertical(y float64) { + _m.Called(y) +} + +// TransformRotate provides a mock function with given fields: angle, x, y +func (_m *Pdf) TransformRotate(angle float64, x float64, y float64) { + _m.Called(angle, x, y) +} + +// TransformScale provides a mock function with given fields: scaleWd, scaleHt, x, y +func (_m *Pdf) TransformScale(scaleWd float64, scaleHt float64, x float64, y float64) { + _m.Called(scaleWd, scaleHt, x, y) +} + +// TransformScaleX provides a mock function with given fields: scaleWd, x, y +func (_m *Pdf) TransformScaleX(scaleWd float64, x float64, y float64) { + _m.Called(scaleWd, x, y) +} + +// TransformScaleXY provides a mock function with given fields: s, x, y +func (_m *Pdf) TransformScaleXY(s float64, x float64, y float64) { + _m.Called(s, x, y) +} + +// TransformScaleY provides a mock function with given fields: scaleHt, x, y +func (_m *Pdf) TransformScaleY(scaleHt float64, x float64, y float64) { + _m.Called(scaleHt, x, y) +} + +// TransformSkew provides a mock function with given fields: angleX, angleY, x, y +func (_m *Pdf) TransformSkew(angleX float64, angleY float64, x float64, y float64) { + _m.Called(angleX, angleY, x, y) +} + +// TransformSkewX provides a mock function with given fields: angleX, x, y +func (_m *Pdf) TransformSkewX(angleX float64, x float64, y float64) { + _m.Called(angleX, x, y) +} + +// TransformSkewY provides a mock function with given fields: angleY, x, y +func (_m *Pdf) TransformSkewY(angleY float64, x float64, y float64) { + _m.Called(angleY, x, y) +} + +// TransformTranslate provides a mock function with given fields: tx, ty +func (_m *Pdf) TransformTranslate(tx float64, ty float64) { + _m.Called(tx, ty) +} + +// TransformTranslateX provides a mock function with given fields: tx +func (_m *Pdf) TransformTranslateX(tx float64) { + _m.Called(tx) +} + +// TransformTranslateY provides a mock function with given fields: ty +func (_m *Pdf) TransformTranslateY(ty float64) { + _m.Called(ty) +} + +// UnicodeTranslatorFromDescriptor provides a mock function with given fields: cpStr +func (_m *Pdf) UnicodeTranslatorFromDescriptor(cpStr string) func(string) string { + ret := _m.Called(cpStr) + + var r0 func(string) string + if rf, ok := ret.Get(0).(func(string) func(string) string); ok { + r0 = rf(cpStr) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(func(string) string) + } + } + + return r0 +} + +// UnitToPointConvert provides a mock function with given fields: u +func (_m *Pdf) UnitToPointConvert(u float64) float64 { + ret := _m.Called(u) + + var r0 float64 + if rf, ok := ret.Get(0).(func(float64) float64); ok { + r0 = rf(u) + } else { + r0 = ret.Get(0).(float64) + } + + return r0 +} + +// UseTemplate provides a mock function with given fields: t +func (_m *Pdf) UseTemplate(t gofpdf.Template) { + _m.Called(t) +} + +// UseTemplateScaled provides a mock function with given fields: t, corner, size +func (_m *Pdf) UseTemplateScaled(t gofpdf.Template, corner gofpdf.PointType, size gofpdf.SizeType) { + _m.Called(t, corner, size) +} + +// Write provides a mock function with given fields: h, txtStr +func (_m *Pdf) Write(h float64, txtStr string) { + _m.Called(h, txtStr) +} + +// WriteAligned provides a mock function with given fields: width, lineHeight, textStr, alignStr +func (_m *Pdf) WriteAligned(width float64, lineHeight float64, textStr string, alignStr string) { + _m.Called(width, lineHeight, textStr, alignStr) +} + +// WriteLinkID provides a mock function with given fields: h, displayStr, linkID +func (_m *Pdf) WriteLinkID(h float64, displayStr string, linkID int) { + _m.Called(h, displayStr, linkID) +} + +// WriteLinkString provides a mock function with given fields: h, displayStr, targetStr +func (_m *Pdf) WriteLinkString(h float64, displayStr string, targetStr string) { + _m.Called(h, displayStr, targetStr) +} + +// Writef provides a mock function with given fields: h, fmtStr, args +func (_m *Pdf) Writef(h float64, fmtStr string, args ...interface{}) { + var _ca []interface{} + _ca = append(_ca, h, fmtStr) + _ca = append(_ca, args...) + _m.Called(_ca...) +} diff --git a/sign/sign.go b/sign/sign.go new file mode 100644 index 00000000..72955289 --- /dev/null +++ b/sign/sign.go @@ -0,0 +1,35 @@ +package sign + +import ( + "github.com/johnfercher/maroto/enums" + "github.com/johnfercher/maroto/font" + "github.com/johnfercher/maroto/math" + "github.com/johnfercher/maroto/text" + "github.com/jung-kurt/gofpdf" +) + +type Sign interface { + Sign(label string, fontFamily font.Family, fontStyle font.Style, fontSize float64, qtdCols float64, marginTop float64, actualCol float64) +} + +type sign struct { + pdf gofpdf.Pdf + math math.Math + text text.Text +} + +func NewSign(pdf gofpdf.Pdf, math math.Math, text text.Text) Sign { + return &sign{ + pdf, + math, + text, + } +} + +func (s *sign) Sign(label string, fontFamily font.Family, fontStyle font.Style, fontSize float64, qtdCols float64, marginTop float64, actualCol float64) { + widthPerCol := s.math.GetWidthPerCol(qtdCols) + left, _, right, _ := s.pdf.GetMargins() + + s.pdf.Line((widthPerCol*actualCol)+left, marginTop+5.0, widthPerCol*(actualCol+1)+right, marginTop+5.0) + s.text.Add(label, fontFamily, fontStyle, fontSize, marginTop, enums.CenterH, actualCol, qtdCols) +} diff --git a/text/text.go b/text/text.go new file mode 100644 index 00000000..3b3e0af6 --- /dev/null +++ b/text/text.go @@ -0,0 +1,43 @@ +package text + +import ( + "github.com/johnfercher/maroto/enums" + "github.com/johnfercher/maroto/font" + "github.com/johnfercher/maroto/math" + "github.com/jung-kurt/gofpdf" +) + +type Text interface { + Add(text string, fontFamily font.Family, fontStyle font.Style, fontSize float64, marginTop float64, align enums.HorizontalAlign, actualCol float64, qtdCols float64) +} + +type text struct { + pdf gofpdf.Pdf + math math.Math + font font.Font +} + +func NewText(pdf gofpdf.Pdf, math math.Math, font font.Font) Text { + return &text{ + pdf, + math, + font, + } +} + +func (m *text) Add(text string, fontFamily font.Family, fontStyle font.Style, fontSize float64, marginTop float64, align enums.HorizontalAlign, actualCol float64, qtdCols float64) { + actualWidthPerCol := m.math.GetWidthPerCol(qtdCols) + + m.font.SetFont(fontFamily, fontStyle, fontSize) + + left, top, _, _ := m.pdf.GetMargins() + + if align == enums.Left { + m.pdf.Text(actualCol*actualWidthPerCol+left, marginTop+top, text) + return + } + + stringWidth := m.pdf.GetStringWidth(text) + dx := (actualWidthPerCol - stringWidth) / 2 + m.pdf.Text(dx+actualCol*actualWidthPerCol+left, marginTop+top, text) +} diff --git a/text/text_test.go b/text/text_test.go new file mode 100644 index 00000000..e37ac33a --- /dev/null +++ b/text/text_test.go @@ -0,0 +1,124 @@ +package text + +import ( + "fmt" + "github.com/johnfercher/maroto/enums" + "github.com/johnfercher/maroto/font" + "github.com/johnfercher/maroto/mocks" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "testing" +) + +func TestNewText(t *testing.T) { + text := NewText(&mocks.Pdf{}, &mocks.Math{}, &mocks.Font{}) + + assert.NotNil(t, text) + assert.Equal(t, fmt.Sprintf("%T", text), "*text.text") +} + +func TestText_Add(t *testing.T) { + cases := []struct { + name string + align enums.HorizontalAlign + pdf func() *mocks.Pdf + math func() *mocks.Math + font func() *mocks.Font + assertPdf func(t *testing.T, pdf *mocks.Pdf) + assertMath func(t *testing.T, math *mocks.Math) + assertFont func(t *testing.T, font *mocks.Font) + }{ + { + "Left Align", + enums.Left, + func() *mocks.Pdf { + _pdf := &mocks.Pdf{} + _pdf.On("GetStringWidth", mock.Anything).Return(12.0) + _pdf.On("GetMargins").Return(10.0, 10.0, 10.0, 10.0) + _pdf.On("Text", mock.Anything, mock.Anything, mock.Anything) + return _pdf + }, + func() *mocks.Math { + _math := &mocks.Math{} + _math.On("GetWidthPerCol", mock.Anything).Return(123.0) + return _math + }, + func() *mocks.Font { + _font := &mocks.Font{} + _font.On("SetFont", mock.Anything, mock.Anything, mock.Anything) + return _font + }, + func(t *testing.T, _pdf *mocks.Pdf) { + _pdf.AssertNotCalled(t, "GetStringWidth") + + _pdf.AssertNumberOfCalls(t, "GetMargins", 1) + + _pdf.AssertNumberOfCalls(t, "Text", 1) + _pdf.AssertCalled(t, "Text", 133.0, 15.0, "Text") + }, + func(t *testing.T, _math *mocks.Math) { + _math.AssertNumberOfCalls(t, "GetWidthPerCol", 1) + _math.AssertCalled(t, "GetWidthPerCol", 15.0) + }, + func(t *testing.T, _font *mocks.Font) { + _font.AssertNumberOfCalls(t, "SetFont", 1) + _font.AssertCalled(t, "SetFont", font.Arial, font.BoldItalic, 16.0) + }, + }, + { + "Center Align", + enums.CenterH, + func() *mocks.Pdf { + _pdf := &mocks.Pdf{} + _pdf.On("GetStringWidth", mock.Anything).Return(12.0) + _pdf.On("GetMargins").Return(10.0, 10.0, 10.0, 10.0) + _pdf.On("Text", mock.Anything, mock.Anything, mock.Anything) + return _pdf + }, + func() *mocks.Math { + _math := &mocks.Math{} + _math.On("GetWidthPerCol", mock.Anything).Return(123.0) + return _math + }, + func() *mocks.Font { + _font := &mocks.Font{} + _font.On("SetFont", mock.Anything, mock.Anything, mock.Anything) + return _font + }, + func(t *testing.T, _pdf *mocks.Pdf) { + _pdf.AssertNumberOfCalls(t, "GetStringWidth", 1) + _pdf.AssertCalled(t, "GetStringWidth", "Text") + + _pdf.AssertNumberOfCalls(t, "GetMargins", 1) + + _pdf.AssertNumberOfCalls(t, "Text", 1) + _pdf.AssertCalled(t, "Text", 188.5, 15.0, "Text") + }, + func(t *testing.T, _math *mocks.Math) { + _math.AssertNumberOfCalls(t, "GetWidthPerCol", 1) + _math.AssertCalled(t, "GetWidthPerCol", 15.0) + }, + func(t *testing.T, _font *mocks.Font) { + _font.AssertNumberOfCalls(t, "SetFont", 1) + _font.AssertCalled(t, "SetFont", font.Arial, font.BoldItalic, 16.0) + }, + }, + } + + for _, c := range cases { + // Arrange + _pdf := c.pdf() + _math := c.math() + _font := c.font() + + text := NewText(_pdf, _math, _font) + + // Act + text.Add("Text", font.Arial, font.BoldItalic, 16.0, 5.0, c.align, 1, 15.0) + + // Assert + c.assertPdf(t, _pdf) + c.assertMath(t, _math) + c.assertFont(t, _font) + } +} diff --git a/vendor/github.com/boombuler/barcode/.gitignore b/vendor/github.com/boombuler/barcode/.gitignore new file mode 100644 index 00000000..1d74e219 --- /dev/null +++ b/vendor/github.com/boombuler/barcode/.gitignore @@ -0,0 +1 @@ +.vscode/ diff --git a/vendor/github.com/boombuler/barcode/LICENSE b/vendor/github.com/boombuler/barcode/LICENSE new file mode 100644 index 00000000..862b0ddc --- /dev/null +++ b/vendor/github.com/boombuler/barcode/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Florian Sundermann + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/boombuler/barcode/README.md b/vendor/github.com/boombuler/barcode/README.md new file mode 100644 index 00000000..2a988db3 --- /dev/null +++ b/vendor/github.com/boombuler/barcode/README.md @@ -0,0 +1,53 @@ +[![Join the chat at https://gitter.im/golang-barcode/Lobby](https://badges.gitter.im/golang-barcode/Lobby.svg)](https://gitter.im/golang-barcode/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +## Introduction ## + +This is a package for GO which can be used to create different types of barcodes. + +## Supported Barcode Types ## +* 2 of 5 +* Aztec Code +* Codabar +* Code 128 +* Code 39 +* Code 93 +* Datamatrix +* EAN 13 +* EAN 8 +* PDF 417 +* QR Code + +## Example ## + +This is a simple example on how to create a QR-Code and write it to a png-file +```go +package main + +import ( + "image/png" + "os" + + "github.com/boombuler/barcode" + "github.com/boombuler/barcode/qr" +) + +func main() { + // Create the barcode + qrCode, _ := qr.Encode("Hello World", qr.M, qr.Auto) + + // Scale the barcode to 200x200 pixels + qrCode, _ = barcode.Scale(qrCode, 200, 200) + + // create the output file + file, _ := os.Create("qrcode.png") + defer file.Close() + + // encode the barcode as png + png.Encode(file, qrCode) +} +``` + +## Documentation ## +See [GoDoc](https://godoc.org/github.com/boombuler/barcode) + +To create a barcode use the Encode function from one of the subpackages. diff --git a/vendor/github.com/boombuler/barcode/aztec/azteccode.go b/vendor/github.com/boombuler/barcode/aztec/azteccode.go new file mode 100644 index 00000000..8ffa7fda --- /dev/null +++ b/vendor/github.com/boombuler/barcode/aztec/azteccode.go @@ -0,0 +1,62 @@ +package aztec + +import ( + "bytes" + "image" + "image/color" + + "github.com/boombuler/barcode" + "github.com/boombuler/barcode/utils" +) + +type aztecCode struct { + *utils.BitList + size int + content []byte +} + +func newAztecCode(size int) *aztecCode { + return &aztecCode{utils.NewBitList(size * size), size, nil} +} + +func (c *aztecCode) Content() string { + return string(c.content) +} + +func (c *aztecCode) Metadata() barcode.Metadata { + return barcode.Metadata{barcode.TypeAztec, 2} +} + +func (c *aztecCode) ColorModel() color.Model { + return color.Gray16Model +} + +func (c *aztecCode) Bounds() image.Rectangle { + return image.Rect(0, 0, c.size, c.size) +} + +func (c *aztecCode) At(x, y int) color.Color { + if c.GetBit(x*c.size + y) { + return color.Black + } + return color.White +} + +func (c *aztecCode) set(x, y int) { + c.SetBit(x*c.size+y, true) +} + +func (c *aztecCode) string() string { + buf := new(bytes.Buffer) + for y := 0; y < c.size; y++ { + for x := 0; x < c.size; x++ { + if c.GetBit(x*c.size + y) { + buf.WriteString("X ") + } else { + buf.WriteString(" ") + } + } + buf.WriteRune('\n') + } + return buf.String() +} diff --git a/vendor/github.com/boombuler/barcode/aztec/encoder.go b/vendor/github.com/boombuler/barcode/aztec/encoder.go new file mode 100644 index 00000000..e62f8fb9 --- /dev/null +++ b/vendor/github.com/boombuler/barcode/aztec/encoder.go @@ -0,0 +1,268 @@ +// Package aztec can create Aztec Code barcodes +package aztec + +import ( + "fmt" + + "github.com/boombuler/barcode" + "github.com/boombuler/barcode/utils" +) + +const ( + DEFAULT_EC_PERCENT = 33 + DEFAULT_LAYERS = 0 + max_nb_bits = 32 + max_nb_bits_compact = 4 +) + +var ( + word_size = []int{ + 4, 6, 6, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + } +) + +func totalBitsInLayer(layers int, compact bool) int { + tmp := 112 + if compact { + tmp = 88 + } + return (tmp + 16*layers) * layers +} + +func stuffBits(bits *utils.BitList, wordSize int) *utils.BitList { + out := new(utils.BitList) + n := bits.Len() + mask := (1 << uint(wordSize)) - 2 + for i := 0; i < n; i += wordSize { + word := 0 + for j := 0; j < wordSize; j++ { + if i+j >= n || bits.GetBit(i+j) { + word |= 1 << uint(wordSize-1-j) + } + } + if (word & mask) == mask { + out.AddBits(word&mask, byte(wordSize)) + i-- + } else if (word & mask) == 0 { + out.AddBits(word|1, byte(wordSize)) + i-- + } else { + out.AddBits(word, byte(wordSize)) + } + } + return out +} + +func generateModeMessage(compact bool, layers, messageSizeInWords int) *utils.BitList { + modeMessage := new(utils.BitList) + if compact { + modeMessage.AddBits(layers-1, 2) + modeMessage.AddBits(messageSizeInWords-1, 6) + modeMessage = generateCheckWords(modeMessage, 28, 4) + } else { + modeMessage.AddBits(layers-1, 5) + modeMessage.AddBits(messageSizeInWords-1, 11) + modeMessage = generateCheckWords(modeMessage, 40, 4) + } + return modeMessage +} + +func drawModeMessage(matrix *aztecCode, compact bool, matrixSize int, modeMessage *utils.BitList) { + center := matrixSize / 2 + if compact { + for i := 0; i < 7; i++ { + offset := center - 3 + i + if modeMessage.GetBit(i) { + matrix.set(offset, center-5) + } + if modeMessage.GetBit(i + 7) { + matrix.set(center+5, offset) + } + if modeMessage.GetBit(20 - i) { + matrix.set(offset, center+5) + } + if modeMessage.GetBit(27 - i) { + matrix.set(center-5, offset) + } + } + } else { + for i := 0; i < 10; i++ { + offset := center - 5 + i + i/5 + if modeMessage.GetBit(i) { + matrix.set(offset, center-7) + } + if modeMessage.GetBit(i + 10) { + matrix.set(center+7, offset) + } + if modeMessage.GetBit(29 - i) { + matrix.set(offset, center+7) + } + if modeMessage.GetBit(39 - i) { + matrix.set(center-7, offset) + } + } + } +} + +func drawBullsEye(matrix *aztecCode, center, size int) { + for i := 0; i < size; i += 2 { + for j := center - i; j <= center+i; j++ { + matrix.set(j, center-i) + matrix.set(j, center+i) + matrix.set(center-i, j) + matrix.set(center+i, j) + } + } + matrix.set(center-size, center-size) + matrix.set(center-size+1, center-size) + matrix.set(center-size, center-size+1) + matrix.set(center+size, center-size) + matrix.set(center+size, center-size+1) + matrix.set(center+size, center+size-1) +} + +// Encode returns an aztec barcode with the given content +func Encode(data []byte, minECCPercent int, userSpecifiedLayers int) (barcode.Barcode, error) { + bits := highlevelEncode(data) + eccBits := ((bits.Len() * minECCPercent) / 100) + 11 + totalSizeBits := bits.Len() + eccBits + var layers, TotalBitsInLayer, wordSize int + var compact bool + var stuffedBits *utils.BitList + if userSpecifiedLayers != DEFAULT_LAYERS { + compact = userSpecifiedLayers < 0 + if compact { + layers = -userSpecifiedLayers + } else { + layers = userSpecifiedLayers + } + if (compact && layers > max_nb_bits_compact) || (!compact && layers > max_nb_bits) { + return nil, fmt.Errorf("Illegal value %d for layers", userSpecifiedLayers) + } + TotalBitsInLayer = totalBitsInLayer(layers, compact) + wordSize = word_size[layers] + usableBitsInLayers := TotalBitsInLayer - (TotalBitsInLayer % wordSize) + stuffedBits = stuffBits(bits, wordSize) + if stuffedBits.Len()+eccBits > usableBitsInLayers { + return nil, fmt.Errorf("Data to large for user specified layer") + } + if compact && stuffedBits.Len() > wordSize*64 { + return nil, fmt.Errorf("Data to large for user specified layer") + } + } else { + wordSize = 0 + stuffedBits = nil + // We look at the possible table sizes in the order Compact1, Compact2, Compact3, + // Compact4, Normal4,... Normal(i) for i < 4 isn't typically used since Compact(i+1) + // is the same size, but has more data. + for i := 0; ; i++ { + if i > max_nb_bits { + return nil, fmt.Errorf("Data too large for an aztec code") + } + compact = i <= 3 + layers = i + if compact { + layers = i + 1 + } + TotalBitsInLayer = totalBitsInLayer(layers, compact) + if totalSizeBits > TotalBitsInLayer { + continue + } + // [Re]stuff the bits if this is the first opportunity, or if the + // wordSize has changed + if wordSize != word_size[layers] { + wordSize = word_size[layers] + stuffedBits = stuffBits(bits, wordSize) + } + usableBitsInLayers := TotalBitsInLayer - (TotalBitsInLayer % wordSize) + if compact && stuffedBits.Len() > wordSize*64 { + // Compact format only allows 64 data words, though C4 can hold more words than that + continue + } + if stuffedBits.Len()+eccBits <= usableBitsInLayers { + break + } + } + } + messageBits := generateCheckWords(stuffedBits, TotalBitsInLayer, wordSize) + messageSizeInWords := stuffedBits.Len() / wordSize + modeMessage := generateModeMessage(compact, layers, messageSizeInWords) + + // allocate symbol + var baseMatrixSize int + if compact { + baseMatrixSize = 11 + layers*4 + } else { + baseMatrixSize = 14 + layers*4 + } + alignmentMap := make([]int, baseMatrixSize) + var matrixSize int + + if compact { + // no alignment marks in compact mode, alignmentMap is a no-op + matrixSize = baseMatrixSize + for i := 0; i < len(alignmentMap); i++ { + alignmentMap[i] = i + } + } else { + matrixSize = baseMatrixSize + 1 + 2*((baseMatrixSize/2-1)/15) + origCenter := baseMatrixSize / 2 + center := matrixSize / 2 + for i := 0; i < origCenter; i++ { + newOffset := i + i/15 + alignmentMap[origCenter-i-1] = center - newOffset - 1 + alignmentMap[origCenter+i] = center + newOffset + 1 + } + } + code := newAztecCode(matrixSize) + code.content = data + + // draw data bits + for i, rowOffset := 0, 0; i < layers; i++ { + rowSize := (layers - i) * 4 + if compact { + rowSize += 9 + } else { + rowSize += 12 + } + + for j := 0; j < rowSize; j++ { + columnOffset := j * 2 + for k := 0; k < 2; k++ { + if messageBits.GetBit(rowOffset + columnOffset + k) { + code.set(alignmentMap[i*2+k], alignmentMap[i*2+j]) + } + if messageBits.GetBit(rowOffset + rowSize*2 + columnOffset + k) { + code.set(alignmentMap[i*2+j], alignmentMap[baseMatrixSize-1-i*2-k]) + } + if messageBits.GetBit(rowOffset + rowSize*4 + columnOffset + k) { + code.set(alignmentMap[baseMatrixSize-1-i*2-k], alignmentMap[baseMatrixSize-1-i*2-j]) + } + if messageBits.GetBit(rowOffset + rowSize*6 + columnOffset + k) { + code.set(alignmentMap[baseMatrixSize-1-i*2-j], alignmentMap[i*2+k]) + } + } + } + rowOffset += rowSize * 8 + } + + // draw mode message + drawModeMessage(code, compact, matrixSize, modeMessage) + + // draw alignment marks + if compact { + drawBullsEye(code, matrixSize/2, 5) + } else { + drawBullsEye(code, matrixSize/2, 7) + for i, j := 0, 0; i < baseMatrixSize/2-1; i, j = i+15, j+16 { + for k := (matrixSize / 2) & 1; k < matrixSize; k += 2 { + code.set(matrixSize/2-j, k) + code.set(matrixSize/2+j, k) + code.set(k, matrixSize/2-j) + code.set(k, matrixSize/2+j) + } + } + } + return code, nil +} diff --git a/vendor/github.com/boombuler/barcode/aztec/errorcorrection.go b/vendor/github.com/boombuler/barcode/aztec/errorcorrection.go new file mode 100644 index 00000000..6ee0be3a --- /dev/null +++ b/vendor/github.com/boombuler/barcode/aztec/errorcorrection.go @@ -0,0 +1,61 @@ +package aztec + +import ( + "github.com/boombuler/barcode/utils" +) + +func bitsToWords(stuffedBits *utils.BitList, wordSize int, wordCount int) []int { + message := make([]int, wordCount) + + for i := 0; i < wordCount; i++ { + value := 0 + for j := 0; j < wordSize; j++ { + if stuffedBits.GetBit(i*wordSize + j) { + value |= (1 << uint(wordSize-j-1)) + } + } + message[i] = value + } + return message +} + +func generateCheckWords(bits *utils.BitList, totalBits, wordSize int) *utils.BitList { + rs := utils.NewReedSolomonEncoder(getGF(wordSize)) + + // bits is guaranteed to be a multiple of the wordSize, so no padding needed + messageWordCount := bits.Len() / wordSize + totalWordCount := totalBits / wordSize + eccWordCount := totalWordCount - messageWordCount + + messageWords := bitsToWords(bits, wordSize, messageWordCount) + eccWords := rs.Encode(messageWords, eccWordCount) + startPad := totalBits % wordSize + + messageBits := new(utils.BitList) + messageBits.AddBits(0, byte(startPad)) + + for _, messageWord := range messageWords { + messageBits.AddBits(messageWord, byte(wordSize)) + } + for _, eccWord := range eccWords { + messageBits.AddBits(eccWord, byte(wordSize)) + } + return messageBits +} + +func getGF(wordSize int) *utils.GaloisField { + switch wordSize { + case 4: + return utils.NewGaloisField(0x13, 16, 1) + case 6: + return utils.NewGaloisField(0x43, 64, 1) + case 8: + return utils.NewGaloisField(0x012D, 256, 1) + case 10: + return utils.NewGaloisField(0x409, 1024, 1) + case 12: + return utils.NewGaloisField(0x1069, 4096, 1) + default: + return nil + } +} diff --git a/vendor/github.com/boombuler/barcode/aztec/highlevel.go b/vendor/github.com/boombuler/barcode/aztec/highlevel.go new file mode 100644 index 00000000..fe34c88c --- /dev/null +++ b/vendor/github.com/boombuler/barcode/aztec/highlevel.go @@ -0,0 +1,171 @@ +package aztec + +import ( + "github.com/boombuler/barcode/utils" +) + +func highlevelEncode(data []byte) *utils.BitList { + states := stateSlice{initialState} + + for index := 0; index < len(data); index++ { + pairCode := 0 + nextChar := byte(0) + if index+1 < len(data) { + nextChar = data[index+1] + } + + switch cur := data[index]; { + case cur == '\r' && nextChar == '\n': + pairCode = 2 + case cur == '.' && nextChar == ' ': + pairCode = 3 + case cur == ',' && nextChar == ' ': + pairCode = 4 + case cur == ':' && nextChar == ' ': + pairCode = 5 + } + if pairCode > 0 { + // We have one of the four special PUNCT pairs. Treat them specially. + // Get a new set of states for the two new characters. + states = updateStateListForPair(states, data, index, pairCode) + index++ + } else { + // Get a new set of states for the new character. + states = updateStateListForChar(states, data, index) + } + } + minBitCnt := int((^uint(0)) >> 1) + var result *state = nil + for _, s := range states { + if s.bitCount < minBitCnt { + minBitCnt = s.bitCount + result = s + } + } + if result != nil { + return result.toBitList(data) + } else { + return new(utils.BitList) + } +} + +func simplifyStates(states stateSlice) stateSlice { + var result stateSlice = nil + for _, newState := range states { + add := true + var newResult stateSlice = nil + + for _, oldState := range result { + if add && oldState.isBetterThanOrEqualTo(newState) { + add = false + } + if !(add && newState.isBetterThanOrEqualTo(oldState)) { + newResult = append(newResult, oldState) + } + } + + if add { + result = append(newResult, newState) + } else { + result = newResult + } + + } + + return result +} + +// We update a set of states for a new character by updating each state +// for the new character, merging the results, and then removing the +// non-optimal states. +func updateStateListForChar(states stateSlice, data []byte, index int) stateSlice { + var result stateSlice = nil + for _, s := range states { + if r := updateStateForChar(s, data, index); len(r) > 0 { + result = append(result, r...) + } + } + return simplifyStates(result) +} + +// Return a set of states that represent the possible ways of updating this +// state for the next character. The resulting set of states are added to +// the "result" list. +func updateStateForChar(s *state, data []byte, index int) stateSlice { + var result stateSlice = nil + ch := data[index] + charInCurrentTable := charMap[s.mode][ch] > 0 + + var stateNoBinary *state = nil + for mode := mode_upper; mode <= mode_punct; mode++ { + charInMode := charMap[mode][ch] + if charInMode > 0 { + if stateNoBinary == nil { + // Only create stateNoBinary the first time it's required. + stateNoBinary = s.endBinaryShift(index) + } + // Try generating the character by latching to its mode + if !charInCurrentTable || mode == s.mode || mode == mode_digit { + // If the character is in the current table, we don't want to latch to + // any other mode except possibly digit (which uses only 4 bits). Any + // other latch would be equally successful *after* this character, and + // so wouldn't save any bits. + res := stateNoBinary.latchAndAppend(mode, charInMode) + result = append(result, res) + } + // Try generating the character by switching to its mode. + if _, ok := shiftTable[s.mode][mode]; !charInCurrentTable && ok { + // It never makes sense to temporarily shift to another mode if the + // character exists in the current mode. That can never save bits. + res := stateNoBinary.shiftAndAppend(mode, charInMode) + result = append(result, res) + } + } + } + if s.bShiftByteCount > 0 || charMap[s.mode][ch] == 0 { + // It's never worthwhile to go into binary shift mode if you're not already + // in binary shift mode, and the character exists in your current mode. + // That can never save bits over just outputting the char in the current mode. + res := s.addBinaryShiftChar(index) + result = append(result, res) + } + return result +} + +// We update a set of states for a new character by updating each state +// for the new character, merging the results, and then removing the +// non-optimal states. +func updateStateListForPair(states stateSlice, data []byte, index int, pairCode int) stateSlice { + var result stateSlice = nil + for _, s := range states { + if r := updateStateForPair(s, data, index, pairCode); len(r) > 0 { + result = append(result, r...) + } + } + return simplifyStates(result) +} + +func updateStateForPair(s *state, data []byte, index int, pairCode int) stateSlice { + var result stateSlice + stateNoBinary := s.endBinaryShift(index) + // Possibility 1. Latch to MODE_PUNCT, and then append this code + result = append(result, stateNoBinary.latchAndAppend(mode_punct, pairCode)) + if s.mode != mode_punct { + // Possibility 2. Shift to MODE_PUNCT, and then append this code. + // Every state except MODE_PUNCT (handled above) can shift + result = append(result, stateNoBinary.shiftAndAppend(mode_punct, pairCode)) + } + if pairCode == 3 || pairCode == 4 { + // both characters are in DIGITS. Sometimes better to just add two digits + digitState := stateNoBinary. + latchAndAppend(mode_digit, 16-pairCode). // period or comma in DIGIT + latchAndAppend(mode_digit, 1) // space in DIGIT + result = append(result, digitState) + } + if s.bShiftByteCount > 0 { + // It only makes sense to do the characters as binary if we're already + // in binary mode. + result = append(result, s.addBinaryShiftChar(index).addBinaryShiftChar(index+1)) + } + return result +} diff --git a/vendor/github.com/boombuler/barcode/aztec/state.go b/vendor/github.com/boombuler/barcode/aztec/state.go new file mode 100644 index 00000000..0d3f0a7a --- /dev/null +++ b/vendor/github.com/boombuler/barcode/aztec/state.go @@ -0,0 +1,264 @@ +package aztec + +import ( + "fmt" + + "github.com/boombuler/barcode/utils" +) + +type encodingMode byte + +const ( + mode_upper encodingMode = iota // 5 bits + mode_lower // 5 bits + mode_digit // 4 bits + mode_mixed // 5 bits + mode_punct // 5 bits +) + +var ( + // The Latch Table shows, for each pair of Modes, the optimal method for + // getting from one mode to another. In the worst possible case, this can + // be up to 14 bits. In the best possible case, we are already there! + // The high half-word of each entry gives the number of bits. + // The low half-word of each entry are the actual bits necessary to change + latchTable = map[encodingMode]map[encodingMode]int{ + mode_upper: { + mode_upper: 0, + mode_lower: (5 << 16) + 28, + mode_digit: (5 << 16) + 30, + mode_mixed: (5 << 16) + 29, + mode_punct: (10 << 16) + (29 << 5) + 30, + }, + mode_lower: { + mode_upper: (9 << 16) + (30 << 4) + 14, + mode_lower: 0, + mode_digit: (5 << 16) + 30, + mode_mixed: (5 << 16) + 29, + mode_punct: (10 << 16) + (29 << 5) + 30, + }, + mode_digit: { + mode_upper: (4 << 16) + 14, + mode_lower: (9 << 16) + (14 << 5) + 28, + mode_digit: 0, + mode_mixed: (9 << 16) + (14 << 5) + 29, + mode_punct: (14 << 16) + (14 << 10) + (29 << 5) + 30, + }, + mode_mixed: { + mode_upper: (5 << 16) + 29, + mode_lower: (5 << 16) + 28, + mode_digit: (10 << 16) + (29 << 5) + 30, + mode_mixed: 0, + mode_punct: (5 << 16) + 30, + }, + mode_punct: { + mode_upper: (5 << 16) + 31, + mode_lower: (10 << 16) + (31 << 5) + 28, + mode_digit: (10 << 16) + (31 << 5) + 30, + mode_mixed: (10 << 16) + (31 << 5) + 29, + mode_punct: 0, + }, + } + // A map showing the available shift codes. (The shifts to BINARY are not shown) + shiftTable = map[encodingMode]map[encodingMode]int{ + mode_upper: { + mode_punct: 0, + }, + mode_lower: { + mode_punct: 0, + mode_upper: 28, + }, + mode_mixed: { + mode_punct: 0, + }, + mode_digit: { + mode_punct: 0, + mode_upper: 15, + }, + } + charMap map[encodingMode][]int +) + +type state struct { + mode encodingMode + tokens token + bShiftByteCount int + bitCount int +} +type stateSlice []*state + +var initialState *state = &state{ + mode: mode_upper, + tokens: nil, + bShiftByteCount: 0, + bitCount: 0, +} + +func init() { + charMap = make(map[encodingMode][]int) + charMap[mode_upper] = make([]int, 256) + charMap[mode_lower] = make([]int, 256) + charMap[mode_digit] = make([]int, 256) + charMap[mode_mixed] = make([]int, 256) + charMap[mode_punct] = make([]int, 256) + + charMap[mode_upper][' '] = 1 + for c := 'A'; c <= 'Z'; c++ { + charMap[mode_upper][int(c)] = int(c - 'A' + 2) + } + + charMap[mode_lower][' '] = 1 + for c := 'a'; c <= 'z'; c++ { + charMap[mode_lower][c] = int(c - 'a' + 2) + } + charMap[mode_digit][' '] = 1 + for c := '0'; c <= '9'; c++ { + charMap[mode_digit][c] = int(c - '0' + 2) + } + charMap[mode_digit][','] = 12 + charMap[mode_digit]['.'] = 13 + + mixedTable := []int{ + 0, ' ', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 27, 28, 29, 30, 31, '@', '\\', '^', + '_', '`', '|', '~', 127, + } + for i, v := range mixedTable { + charMap[mode_mixed][v] = i + } + + punctTable := []int{ + 0, '\r', 0, 0, 0, 0, '!', '\'', '#', '$', '%', '&', '\'', + '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', + '[', ']', '{', '}', + } + for i, v := range punctTable { + if v > 0 { + charMap[mode_punct][v] = i + } + } +} + +func (em encodingMode) BitCount() byte { + if em == mode_digit { + return 4 + } + return 5 +} + +// Create a new state representing this state with a latch to a (not +// necessary different) mode, and then a code. +func (s *state) latchAndAppend(mode encodingMode, value int) *state { + bitCount := s.bitCount + tokens := s.tokens + + if mode != s.mode { + latch := latchTable[s.mode][mode] + tokens = newSimpleToken(tokens, latch&0xFFFF, byte(latch>>16)) + bitCount += latch >> 16 + } + tokens = newSimpleToken(tokens, value, mode.BitCount()) + return &state{ + mode: mode, + tokens: tokens, + bShiftByteCount: 0, + bitCount: bitCount + int(mode.BitCount()), + } +} + +// Create a new state representing this state, with a temporary shift +// to a different mode to output a single value. +func (s *state) shiftAndAppend(mode encodingMode, value int) *state { + tokens := s.tokens + + // Shifts exist only to UPPER and PUNCT, both with tokens size 5. + tokens = newSimpleToken(tokens, shiftTable[s.mode][mode], s.mode.BitCount()) + tokens = newSimpleToken(tokens, value, 5) + + return &state{ + mode: s.mode, + tokens: tokens, + bShiftByteCount: 0, + bitCount: s.bitCount + int(s.mode.BitCount()) + 5, + } +} + +// Create a new state representing this state, but an additional character +// output in Binary Shift mode. +func (s *state) addBinaryShiftChar(index int) *state { + tokens := s.tokens + mode := s.mode + bitCnt := s.bitCount + if s.mode == mode_punct || s.mode == mode_digit { + latch := latchTable[s.mode][mode_upper] + tokens = newSimpleToken(tokens, latch&0xFFFF, byte(latch>>16)) + bitCnt += latch >> 16 + mode = mode_upper + } + deltaBitCount := 8 + if s.bShiftByteCount == 0 || s.bShiftByteCount == 31 { + deltaBitCount = 18 + } else if s.bShiftByteCount == 62 { + deltaBitCount = 9 + } + result := &state{ + mode: mode, + tokens: tokens, + bShiftByteCount: s.bShiftByteCount + 1, + bitCount: bitCnt + deltaBitCount, + } + if result.bShiftByteCount == 2047+31 { + // The string is as long as it's allowed to be. We should end it. + result = result.endBinaryShift(index + 1) + } + + return result +} + +// Create the state identical to this one, but we are no longer in +// Binary Shift mode. +func (s *state) endBinaryShift(index int) *state { + if s.bShiftByteCount == 0 { + return s + } + tokens := newShiftToken(s.tokens, index-s.bShiftByteCount, s.bShiftByteCount) + return &state{ + mode: s.mode, + tokens: tokens, + bShiftByteCount: 0, + bitCount: s.bitCount, + } +} + +// Returns true if "this" state is better (or equal) to be in than "that" +// state under all possible circumstances. +func (this *state) isBetterThanOrEqualTo(other *state) bool { + mySize := this.bitCount + (latchTable[this.mode][other.mode] >> 16) + + if other.bShiftByteCount > 0 && (this.bShiftByteCount == 0 || this.bShiftByteCount > other.bShiftByteCount) { + mySize += 10 // Cost of entering Binary Shift mode. + } + return mySize <= other.bitCount +} + +func (s *state) toBitList(text []byte) *utils.BitList { + tokens := make([]token, 0) + se := s.endBinaryShift(len(text)) + + for t := se.tokens; t != nil; t = t.prev() { + tokens = append(tokens, t) + } + res := new(utils.BitList) + for i := len(tokens) - 1; i >= 0; i-- { + tokens[i].appendTo(res, text) + } + return res +} + +func (s *state) String() string { + tokens := make([]token, 0) + for t := s.tokens; t != nil; t = t.prev() { + tokens = append([]token{t}, tokens...) + } + return fmt.Sprintf("M:%d bits=%d bytes=%d: %v", s.mode, s.bitCount, s.bShiftByteCount, tokens) +} diff --git a/vendor/github.com/boombuler/barcode/aztec/token.go b/vendor/github.com/boombuler/barcode/aztec/token.go new file mode 100644 index 00000000..aac0f7a3 --- /dev/null +++ b/vendor/github.com/boombuler/barcode/aztec/token.go @@ -0,0 +1,75 @@ +package aztec + +import ( + "fmt" + + "github.com/boombuler/barcode/utils" +) + +type token interface { + fmt.Stringer + prev() token + appendTo(bits *utils.BitList, text []byte) +} + +type simpleToken struct { + token + value int + bitCount byte +} + +type binaryShiftToken struct { + token + bShiftStart int + bShiftByteCnt int +} + +func newSimpleToken(prev token, value int, bitCount byte) token { + return &simpleToken{prev, value, bitCount} +} +func newShiftToken(prev token, bShiftStart int, bShiftCnt int) token { + return &binaryShiftToken{prev, bShiftStart, bShiftCnt} +} + +func (st *simpleToken) prev() token { + return st.token +} +func (st *simpleToken) appendTo(bits *utils.BitList, text []byte) { + bits.AddBits(st.value, st.bitCount) +} +func (st *simpleToken) String() string { + value := st.value & ((1 << st.bitCount) - 1) + value |= 1 << st.bitCount + return "<" + fmt.Sprintf("%b", value)[1:] + ">" +} + +func (bst *binaryShiftToken) prev() token { + return bst.token +} +func (bst *binaryShiftToken) appendTo(bits *utils.BitList, text []byte) { + for i := 0; i < bst.bShiftByteCnt; i++ { + if i == 0 || (i == 31 && bst.bShiftByteCnt <= 62) { + // We need a header before the first character, and before + // character 31 when the total byte code is <= 62 + bits.AddBits(31, 5) // BINARY_SHIFT + if bst.bShiftByteCnt > 62 { + bits.AddBits(bst.bShiftByteCnt-31, 16) + } else if i == 0 { + // 1 <= binaryShiftByteCode <= 62 + if bst.bShiftByteCnt < 31 { + bits.AddBits(bst.bShiftByteCnt, 5) + } else { + bits.AddBits(31, 5) + } + } else { + // 32 <= binaryShiftCount <= 62 and i == 31 + bits.AddBits(bst.bShiftByteCnt-31, 5) + } + } + bits.AddByte(text[bst.bShiftStart+i]) + } +} + +func (bst *binaryShiftToken) String() string { + return fmt.Sprintf("<%d::%d>", bst.bShiftStart, (bst.bShiftStart + bst.bShiftByteCnt - 1)) +} diff --git a/vendor/github.com/boombuler/barcode/barcode.go b/vendor/github.com/boombuler/barcode/barcode.go new file mode 100644 index 00000000..25f4a693 --- /dev/null +++ b/vendor/github.com/boombuler/barcode/barcode.go @@ -0,0 +1,42 @@ +package barcode + +import "image" + +const ( + TypeAztec = "Aztec" + TypeCodabar = "Codabar" + TypeCode128 = "Code 128" + TypeCode39 = "Code 39" + TypeCode93 = "Code 93" + TypeDataMatrix = "DataMatrix" + TypeEAN8 = "EAN 8" + TypeEAN13 = "EAN 13" + TypePDF = "PDF417" + TypeQR = "QR Code" + Type2of5 = "2 of 5" + Type2of5Interleaved = "2 of 5 (interleaved)" +) + +// Contains some meta information about a barcode +type Metadata struct { + // the name of the barcode kind + CodeKind string + // contains 1 for 1D barcodes or 2 for 2D barcodes + Dimensions byte +} + +// a rendered and encoded barcode +type Barcode interface { + image.Image + // returns some meta information about the barcode + Metadata() Metadata + // the data that was encoded in this barcode + Content() string +} + +// Additional interface that some barcodes might implement to provide +// the value of its checksum. +type BarcodeIntCS interface { + Barcode + CheckSum() int +} diff --git a/vendor/github.com/boombuler/barcode/codabar/encoder.go b/vendor/github.com/boombuler/barcode/codabar/encoder.go new file mode 100644 index 00000000..7e14c0e9 --- /dev/null +++ b/vendor/github.com/boombuler/barcode/codabar/encoder.go @@ -0,0 +1,49 @@ +// Package codabar can create Codabar barcodes +package codabar + +import ( + "fmt" + "regexp" + + "github.com/boombuler/barcode" + "github.com/boombuler/barcode/utils" +) + +var encodingTable = map[rune][]bool{ + '0': []bool{true, false, true, false, true, false, false, true, true}, + '1': []bool{true, false, true, false, true, true, false, false, true}, + '2': []bool{true, false, true, false, false, true, false, true, true}, + '3': []bool{true, true, false, false, true, false, true, false, true}, + '4': []bool{true, false, true, true, false, true, false, false, true}, + '5': []bool{true, true, false, true, false, true, false, false, true}, + '6': []bool{true, false, false, true, false, true, false, true, true}, + '7': []bool{true, false, false, true, false, true, true, false, true}, + '8': []bool{true, false, false, true, true, false, true, false, true}, + '9': []bool{true, true, false, true, false, false, true, false, true}, + '-': []bool{true, false, true, false, false, true, true, false, true}, + '$': []bool{true, false, true, true, false, false, true, false, true}, + ':': []bool{true, true, false, true, false, true, true, false, true, true}, + '/': []bool{true, true, false, true, true, false, true, false, true, true}, + '.': []bool{true, true, false, true, true, false, true, true, false, true}, + '+': []bool{true, false, true, true, false, false, true, true, false, false, true, true}, + 'A': []bool{true, false, true, true, false, false, true, false, false, true}, + 'B': []bool{true, false, true, false, false, true, false, false, true, true}, + 'C': []bool{true, false, false, true, false, false, true, false, true, true}, + 'D': []bool{true, false, true, false, false, true, true, false, false, true}, +} + +// Encode creates a codabar barcode for the given content +func Encode(content string) (barcode.Barcode, error) { + checkValid, _ := regexp.Compile(`[ABCD][0123456789\-\$\:/\.\+]*[ABCD]$`) + if content == "!" || checkValid.ReplaceAllString(content, "!") != "!" { + return nil, fmt.Errorf("can not encode \"%s\"", content) + } + resBits := new(utils.BitList) + for i, r := range content { + if i > 0 { + resBits.AddBit(false) + } + resBits.AddBit(encodingTable[r]...) + } + return utils.New1DCode(barcode.TypeCodabar, content, resBits), nil +} diff --git a/vendor/github.com/boombuler/barcode/code128/encode.go b/vendor/github.com/boombuler/barcode/code128/encode.go new file mode 100644 index 00000000..3a00f6c0 --- /dev/null +++ b/vendor/github.com/boombuler/barcode/code128/encode.go @@ -0,0 +1,203 @@ +// Package code128 can create Code128 barcodes +package code128 + +import ( + "fmt" + "strings" + "unicode/utf8" + + "github.com/boombuler/barcode" + "github.com/boombuler/barcode/utils" +) + +func strToRunes(str string) []rune { + result := make([]rune, utf8.RuneCountInString(str)) + i := 0 + for _, r := range str { + result[i] = r + i++ + } + return result +} + +func shouldUseCTable(nextRunes []rune, curEncoding byte) bool { + requiredDigits := 4 + if curEncoding == startCSymbol { + requiredDigits = 2 + } + if len(nextRunes) < requiredDigits { + return false + } + for i := 0; i < requiredDigits; i++ { + if i%2 == 0 && nextRunes[i] == FNC1 { + requiredDigits++ + if len(nextRunes) < requiredDigits { + return false + } + continue + } + if nextRunes[i] < '0' || nextRunes[i] > '9' { + return false + } + } + return true +} + +func tableContainsRune(table string, r rune) bool { + return strings.ContainsRune(table, r) || r == FNC1 || r == FNC2 || r == FNC3 || r == FNC4 +} + +func shouldUseATable(nextRunes []rune, curEncoding byte) bool { + nextRune := nextRunes[0] + if !tableContainsRune(bTable, nextRune) || curEncoding == startASymbol { + return tableContainsRune(aTable, nextRune) + } + if curEncoding == 0 { + for _, r := range nextRunes { + if tableContainsRune(abTable, r) { + continue + } + if strings.ContainsRune(aOnlyTable, r) { + return true + } + break + } + } + return false +} + +func getCodeIndexList(content []rune) *utils.BitList { + result := new(utils.BitList) + curEncoding := byte(0) + for i := 0; i < len(content); i++ { + if shouldUseCTable(content[i:], curEncoding) { + if curEncoding != startCSymbol { + if curEncoding == byte(0) { + result.AddByte(startCSymbol) + } else { + result.AddByte(codeCSymbol) + } + curEncoding = startCSymbol + } + if content[i] == FNC1 { + result.AddByte(102) + } else { + idx := (content[i] - '0') * 10 + i++ + idx = idx + (content[i] - '0') + result.AddByte(byte(idx)) + } + } else if shouldUseATable(content[i:], curEncoding) { + if curEncoding != startASymbol { + if curEncoding == byte(0) { + result.AddByte(startASymbol) + } else { + result.AddByte(codeASymbol) + } + curEncoding = startASymbol + } + var idx int + switch content[i] { + case FNC1: + idx = 102 + break + case FNC2: + idx = 97 + break + case FNC3: + idx = 96 + break + case FNC4: + idx = 101 + break + default: + idx = strings.IndexRune(aTable, content[i]) + break + } + if idx < 0 { + return nil + } + result.AddByte(byte(idx)) + } else { + if curEncoding != startBSymbol { + if curEncoding == byte(0) { + result.AddByte(startBSymbol) + } else { + result.AddByte(codeBSymbol) + } + curEncoding = startBSymbol + } + var idx int + switch content[i] { + case FNC1: + idx = 102 + break + case FNC2: + idx = 97 + break + case FNC3: + idx = 96 + break + case FNC4: + idx = 100 + break + default: + idx = strings.IndexRune(bTable, content[i]) + break + } + + if idx < 0 { + return nil + } + result.AddByte(byte(idx)) + } + } + return result +} + +// Encode creates a Code 128 barcode for the given content +func Encode(content string) (barcode.BarcodeIntCS, error) { + contentRunes := strToRunes(content) + if len(contentRunes) <= 0 || len(contentRunes) > 80 { + return nil, fmt.Errorf("content length should be between 1 and 80 runes but got %d", len(contentRunes)) + } + idxList := getCodeIndexList(contentRunes) + + if idxList == nil { + return nil, fmt.Errorf("\"%s\" could not be encoded", content) + } + + result := new(utils.BitList) + sum := 0 + for i, idx := range idxList.GetBytes() { + if i == 0 { + sum = int(idx) + } else { + sum += i * int(idx) + } + result.AddBit(encodingTable[idx]...) + } + sum = sum % 103 + result.AddBit(encodingTable[sum]...) + result.AddBit(encodingTable[stopSymbol]...) + return utils.New1DCodeIntCheckSum(barcode.TypeCode128, content, result, sum), nil +} + +func EncodeWithoutChecksum(content string) (barcode.Barcode, error) { + contentRunes := strToRunes(content) + if len(contentRunes) <= 0 || len(contentRunes) > 80 { + return nil, fmt.Errorf("content length should be between 1 and 80 runes but got %d", len(contentRunes)) + } + idxList := getCodeIndexList(contentRunes) + + if idxList == nil { + return nil, fmt.Errorf("\"%s\" could not be encoded", content) + } + + result := new(utils.BitList) + for _, idx := range idxList.GetBytes() { + result.AddBit(encodingTable[idx]...) + } + result.AddBit(encodingTable[stopSymbol]...) + return utils.New1DCode(barcode.TypeCode128, content, result), nil +} diff --git a/vendor/github.com/boombuler/barcode/code128/encodingtable.go b/vendor/github.com/boombuler/barcode/code128/encodingtable.go new file mode 100644 index 00000000..8162a07c --- /dev/null +++ b/vendor/github.com/boombuler/barcode/code128/encodingtable.go @@ -0,0 +1,143 @@ +package code128 + +var encodingTable = [107][]bool{ + []bool{true, true, false, true, true, false, false, true, true, false, false}, + []bool{true, true, false, false, true, true, false, true, true, false, false}, + []bool{true, true, false, false, true, true, false, false, true, true, false}, + []bool{true, false, false, true, false, false, true, true, false, false, false}, + []bool{true, false, false, true, false, false, false, true, true, false, false}, + []bool{true, false, false, false, true, false, false, true, true, false, false}, + []bool{true, false, false, true, true, false, false, true, false, false, false}, + []bool{true, false, false, true, true, false, false, false, true, false, false}, + []bool{true, false, false, false, true, true, false, false, true, false, false}, + []bool{true, true, false, false, true, false, false, true, false, false, false}, + []bool{true, true, false, false, true, false, false, false, true, false, false}, + []bool{true, true, false, false, false, true, false, false, true, false, false}, + []bool{true, false, true, true, false, false, true, true, true, false, false}, + []bool{true, false, false, true, true, false, true, true, true, false, false}, + []bool{true, false, false, true, true, false, false, true, true, true, false}, + []bool{true, false, true, true, true, false, false, true, true, false, false}, + []bool{true, false, false, true, true, true, false, true, true, false, false}, + []bool{true, false, false, true, true, true, false, false, true, true, false}, + []bool{true, true, false, false, true, true, true, false, false, true, false}, + []bool{true, true, false, false, true, false, true, true, true, false, false}, + []bool{true, true, false, false, true, false, false, true, true, true, false}, + []bool{true, true, false, true, true, true, false, false, true, false, false}, + []bool{true, true, false, false, true, true, true, false, true, false, false}, + []bool{true, true, true, false, true, true, false, true, true, true, false}, + []bool{true, true, true, false, true, false, false, true, true, false, false}, + []bool{true, true, true, false, false, true, false, true, true, false, false}, + []bool{true, true, true, false, false, true, false, false, true, true, false}, + []bool{true, true, true, false, true, true, false, false, true, false, false}, + []bool{true, true, true, false, false, true, true, false, true, false, false}, + []bool{true, true, true, false, false, true, true, false, false, true, false}, + []bool{true, true, false, true, true, false, true, true, false, false, false}, + []bool{true, true, false, true, true, false, false, false, true, true, false}, + []bool{true, true, false, false, false, true, true, false, true, true, false}, + []bool{true, false, true, false, false, false, true, true, false, false, false}, + []bool{true, false, false, false, true, false, true, true, false, false, false}, + []bool{true, false, false, false, true, false, false, false, true, true, false}, + []bool{true, false, true, true, false, false, false, true, false, false, false}, + []bool{true, false, false, false, true, true, false, true, false, false, false}, + []bool{true, false, false, false, true, true, false, false, false, true, false}, + []bool{true, true, false, true, false, false, false, true, false, false, false}, + []bool{true, true, false, false, false, true, false, true, false, false, false}, + []bool{true, true, false, false, false, true, false, false, false, true, false}, + []bool{true, false, true, true, false, true, true, true, false, false, false}, + []bool{true, false, true, true, false, false, false, true, true, true, false}, + []bool{true, false, false, false, true, true, false, true, true, true, false}, + []bool{true, false, true, true, true, false, true, true, false, false, false}, + []bool{true, false, true, true, true, false, false, false, true, true, false}, + []bool{true, false, false, false, true, true, true, false, true, true, false}, + []bool{true, true, true, false, true, true, true, false, true, true, false}, + []bool{true, true, false, true, false, false, false, true, true, true, false}, + []bool{true, true, false, false, false, true, false, true, true, true, false}, + []bool{true, true, false, true, true, true, false, true, false, false, false}, + []bool{true, true, false, true, true, true, false, false, false, true, false}, + []bool{true, true, false, true, true, true, false, true, true, true, false}, + []bool{true, true, true, false, true, false, true, true, false, false, false}, + []bool{true, true, true, false, true, false, false, false, true, true, false}, + []bool{true, true, true, false, false, false, true, false, true, true, false}, + []bool{true, true, true, false, true, true, false, true, false, false, false}, + []bool{true, true, true, false, true, true, false, false, false, true, false}, + []bool{true, true, true, false, false, false, true, true, false, true, false}, + []bool{true, true, true, false, true, true, true, true, false, true, false}, + []bool{true, true, false, false, true, false, false, false, false, true, false}, + []bool{true, true, true, true, false, false, false, true, false, true, false}, + []bool{true, false, true, false, false, true, true, false, false, false, false}, + []bool{true, false, true, false, false, false, false, true, true, false, false}, + []bool{true, false, false, true, false, true, true, false, false, false, false}, + []bool{true, false, false, true, false, false, false, false, true, true, false}, + []bool{true, false, false, false, false, true, false, true, true, false, false}, + []bool{true, false, false, false, false, true, false, false, true, true, false}, + []bool{true, false, true, true, false, false, true, false, false, false, false}, + []bool{true, false, true, true, false, false, false, false, true, false, false}, + []bool{true, false, false, true, true, false, true, false, false, false, false}, + []bool{true, false, false, true, true, false, false, false, false, true, false}, + []bool{true, false, false, false, false, true, true, false, true, false, false}, + []bool{true, false, false, false, false, true, true, false, false, true, false}, + []bool{true, true, false, false, false, false, true, false, false, true, false}, + []bool{true, true, false, false, true, false, true, false, false, false, false}, + []bool{true, true, true, true, false, true, true, true, false, true, false}, + []bool{true, true, false, false, false, false, true, false, true, false, false}, + []bool{true, false, false, false, true, true, true, true, false, true, false}, + []bool{true, false, true, false, false, true, true, true, true, false, false}, + []bool{true, false, false, true, false, true, true, true, true, false, false}, + []bool{true, false, false, true, false, false, true, true, true, true, false}, + []bool{true, false, true, true, true, true, false, false, true, false, false}, + []bool{true, false, false, true, true, true, true, false, true, false, false}, + []bool{true, false, false, true, true, true, true, false, false, true, false}, + []bool{true, true, true, true, false, true, false, false, true, false, false}, + []bool{true, true, true, true, false, false, true, false, true, false, false}, + []bool{true, true, true, true, false, false, true, false, false, true, false}, + []bool{true, true, false, true, true, false, true, true, true, true, false}, + []bool{true, true, false, true, true, true, true, false, true, true, false}, + []bool{true, true, true, true, false, true, true, false, true, true, false}, + []bool{true, false, true, false, true, true, true, true, false, false, false}, + []bool{true, false, true, false, false, false, true, true, true, true, false}, + []bool{true, false, false, false, true, false, true, true, true, true, false}, + []bool{true, false, true, true, true, true, false, true, false, false, false}, + []bool{true, false, true, true, true, true, false, false, false, true, false}, + []bool{true, true, true, true, false, true, false, true, false, false, false}, + []bool{true, true, true, true, false, true, false, false, false, true, false}, + []bool{true, false, true, true, true, false, true, true, true, true, false}, + []bool{true, false, true, true, true, true, false, true, true, true, false}, + []bool{true, true, true, false, true, false, true, true, true, true, false}, + []bool{true, true, true, true, false, true, false, true, true, true, false}, + []bool{true, true, false, true, false, false, false, false, true, false, false}, + []bool{true, true, false, true, false, false, true, false, false, false, false}, + []bool{true, true, false, true, false, false, true, true, true, false, false}, + []bool{true, true, false, false, false, true, true, true, false, true, false, true, true}, +} + +const startASymbol byte = 103 +const startBSymbol byte = 104 +const startCSymbol byte = 105 + +const codeASymbol byte = 101 +const codeBSymbol byte = 100 +const codeCSymbol byte = 99 + +const stopSymbol byte = 106 + +const ( + // FNC1 - Special Function 1 + FNC1 = '\u00f1' + // FNC2 - Special Function 2 + FNC2 = '\u00f2' + // FNC3 - Special Function 3 + FNC3 = '\u00f3' + // FNC4 - Special Function 4 + FNC4 = '\u00f4' +) + +const abTable = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" +const bTable = abTable + "`abcdefghijklmnopqrstuvwxyz{|}~\u007F" +const aOnlyTable = "\u0000\u0001\u0002\u0003\u0004" + // NUL, SOH, STX, ETX, EOT + "\u0005\u0006\u0007\u0008\u0009" + // ENQ, ACK, BEL, BS, HT + "\u000A\u000B\u000C\u000D\u000E" + // LF, VT, FF, CR, SO + "\u000F\u0010\u0011\u0012\u0013" + // SI, DLE, DC1, DC2, DC3 + "\u0014\u0015\u0016\u0017\u0018" + // DC4, NAK, SYN, ETB, CAN + "\u0019\u001A\u001B\u001C\u001D" + // EM, SUB, ESC, FS, GS + "\u001E\u001F" // RS, US +const aTable = abTable + aOnlyTable diff --git a/vendor/github.com/boombuler/barcode/code39/encoder.go b/vendor/github.com/boombuler/barcode/code39/encoder.go new file mode 100644 index 00000000..63b3e716 --- /dev/null +++ b/vendor/github.com/boombuler/barcode/code39/encoder.go @@ -0,0 +1,152 @@ +// Package code39 can create Code39 barcodes +package code39 + +import ( + "errors" + "strconv" + "strings" + + "github.com/boombuler/barcode" + "github.com/boombuler/barcode/utils" +) + +type encodeInfo struct { + value int + data []bool +} + +var encodeTable = map[rune]encodeInfo{ + '0': encodeInfo{0, []bool{true, false, true, false, false, true, true, false, true, true, false, true}}, + '1': encodeInfo{1, []bool{true, true, false, true, false, false, true, false, true, false, true, true}}, + '2': encodeInfo{2, []bool{true, false, true, true, false, false, true, false, true, false, true, true}}, + '3': encodeInfo{3, []bool{true, true, false, true, true, false, false, true, false, true, false, true}}, + '4': encodeInfo{4, []bool{true, false, true, false, false, true, true, false, true, false, true, true}}, + '5': encodeInfo{5, []bool{true, true, false, true, false, false, true, true, false, true, false, true}}, + '6': encodeInfo{6, []bool{true, false, true, true, false, false, true, true, false, true, false, true}}, + '7': encodeInfo{7, []bool{true, false, true, false, false, true, false, true, true, false, true, true}}, + '8': encodeInfo{8, []bool{true, true, false, true, false, false, true, false, true, true, false, true}}, + '9': encodeInfo{9, []bool{true, false, true, true, false, false, true, false, true, true, false, true}}, + 'A': encodeInfo{10, []bool{true, true, false, true, false, true, false, false, true, false, true, true}}, + 'B': encodeInfo{11, []bool{true, false, true, true, false, true, false, false, true, false, true, true}}, + 'C': encodeInfo{12, []bool{true, true, false, true, true, false, true, false, false, true, false, true}}, + 'D': encodeInfo{13, []bool{true, false, true, false, true, true, false, false, true, false, true, true}}, + 'E': encodeInfo{14, []bool{true, true, false, true, false, true, true, false, false, true, false, true}}, + 'F': encodeInfo{15, []bool{true, false, true, true, false, true, true, false, false, true, false, true}}, + 'G': encodeInfo{16, []bool{true, false, true, false, true, false, false, true, true, false, true, true}}, + 'H': encodeInfo{17, []bool{true, true, false, true, false, true, false, false, true, true, false, true}}, + 'I': encodeInfo{18, []bool{true, false, true, true, false, true, false, false, true, true, false, true}}, + 'J': encodeInfo{19, []bool{true, false, true, false, true, true, false, false, true, true, false, true}}, + 'K': encodeInfo{20, []bool{true, true, false, true, false, true, false, true, false, false, true, true}}, + 'L': encodeInfo{21, []bool{true, false, true, true, false, true, false, true, false, false, true, true}}, + 'M': encodeInfo{22, []bool{true, true, false, true, true, false, true, false, true, false, false, true}}, + 'N': encodeInfo{23, []bool{true, false, true, false, true, true, false, true, false, false, true, true}}, + 'O': encodeInfo{24, []bool{true, true, false, true, false, true, true, false, true, false, false, true}}, + 'P': encodeInfo{25, []bool{true, false, true, true, false, true, true, false, true, false, false, true}}, + 'Q': encodeInfo{26, []bool{true, false, true, false, true, false, true, true, false, false, true, true}}, + 'R': encodeInfo{27, []bool{true, true, false, true, false, true, false, true, true, false, false, true}}, + 'S': encodeInfo{28, []bool{true, false, true, true, false, true, false, true, true, false, false, true}}, + 'T': encodeInfo{29, []bool{true, false, true, false, true, true, false, true, true, false, false, true}}, + 'U': encodeInfo{30, []bool{true, true, false, false, true, false, true, false, true, false, true, true}}, + 'V': encodeInfo{31, []bool{true, false, false, true, true, false, true, false, true, false, true, true}}, + 'W': encodeInfo{32, []bool{true, true, false, false, true, true, false, true, false, true, false, true}}, + 'X': encodeInfo{33, []bool{true, false, false, true, false, true, true, false, true, false, true, true}}, + 'Y': encodeInfo{34, []bool{true, true, false, false, true, false, true, true, false, true, false, true}}, + 'Z': encodeInfo{35, []bool{true, false, false, true, true, false, true, true, false, true, false, true}}, + '-': encodeInfo{36, []bool{true, false, false, true, false, true, false, true, true, false, true, true}}, + '.': encodeInfo{37, []bool{true, true, false, false, true, false, true, false, true, true, false, true}}, + ' ': encodeInfo{38, []bool{true, false, false, true, true, false, true, false, true, true, false, true}}, + '$': encodeInfo{39, []bool{true, false, false, true, false, false, true, false, false, true, false, true}}, + '/': encodeInfo{40, []bool{true, false, false, true, false, false, true, false, true, false, false, true}}, + '+': encodeInfo{41, []bool{true, false, false, true, false, true, false, false, true, false, false, true}}, + '%': encodeInfo{42, []bool{true, false, true, false, false, true, false, false, true, false, false, true}}, + '*': encodeInfo{-1, []bool{true, false, false, true, false, true, true, false, true, true, false, true}}, +} + +var extendedTable = map[rune]string{ + 0: `%U`, 1: `$A`, 2: `$B`, 3: `$C`, 4: `$D`, 5: `$E`, 6: `$F`, 7: `$G`, 8: `$H`, 9: `$I`, 10: `$J`, + 11: `$K`, 12: `$L`, 13: `$M`, 14: `$N`, 15: `$O`, 16: `$P`, 17: `$Q`, 18: `$R`, 19: `$S`, 20: `$T`, + 21: `$U`, 22: `$V`, 23: `$W`, 24: `$X`, 25: `$Y`, 26: `$Z`, 27: `%A`, 28: `%B`, 29: `%C`, 30: `%D`, + 31: `%E`, 33: `/A`, 34: `/B`, 35: `/C`, 36: `/D`, 37: `/E`, 38: `/F`, 39: `/G`, 40: `/H`, 41: `/I`, + 42: `/J`, 43: `/K`, 44: `/L`, 47: `/O`, 58: `/Z`, 59: `%F`, 60: `%G`, 61: `%H`, 62: `%I`, 63: `%J`, + 64: `%V`, 91: `%K`, 92: `%L`, 93: `%M`, 94: `%N`, 95: `%O`, 96: `%W`, 97: `+A`, 98: `+B`, 99: `+C`, + 100: `+D`, 101: `+E`, 102: `+F`, 103: `+G`, 104: `+H`, 105: `+I`, 106: `+J`, 107: `+K`, 108: `+L`, + 109: `+M`, 110: `+N`, 111: `+O`, 112: `+P`, 113: `+Q`, 114: `+R`, 115: `+S`, 116: `+T`, 117: `+U`, + 118: `+V`, 119: `+W`, 120: `+X`, 121: `+Y`, 122: `+Z`, 123: `%P`, 124: `%Q`, 125: `%R`, 126: `%S`, + 127: `%T`, +} + +func getChecksum(content string) string { + sum := 0 + for _, r := range content { + info, ok := encodeTable[r] + if !ok || info.value < 0 { + return "#" + } + + sum += info.value + } + + sum = sum % 43 + for r, v := range encodeTable { + if v.value == sum { + return string(r) + } + } + return "#" +} + +func prepare(content string) (string, error) { + result := "" + for _, r := range content { + if r > 127 { + return "", errors.New("Only ASCII strings can be encoded") + } + val, ok := extendedTable[r] + if ok { + result += val + } else { + result += string([]rune{r}) + } + } + return result, nil +} + +// Encode returns a code39 barcode for the given content +// if includeChecksum is set to true, a checksum character is calculated and added to the content +func Encode(content string, includeChecksum bool, fullASCIIMode bool) (barcode.BarcodeIntCS, error) { + if fullASCIIMode { + var err error + content, err = prepare(content) + if err != nil { + return nil, err + } + } else if strings.ContainsRune(content, '*') { + return nil, errors.New("invalid data! try full ascii mode") + } + + data := "*" + content + if includeChecksum { + data += getChecksum(content) + } + data += "*" + + result := new(utils.BitList) + + for i, r := range data { + if i != 0 { + result.AddBit(false) + } + + info, ok := encodeTable[r] + if !ok { + return nil, errors.New("invalid data! try full ascii mode") + } + result.AddBit(info.data...) + } + + checkSum, err := strconv.ParseInt(getChecksum(content), 10, 64) + if err != nil { + checkSum = 0 + } + return utils.New1DCodeIntCheckSum(barcode.TypeCode39, content, result, int(checkSum)), nil +} diff --git a/vendor/github.com/boombuler/barcode/datamatrix/codelayout.go b/vendor/github.com/boombuler/barcode/datamatrix/codelayout.go new file mode 100644 index 00000000..923b1353 --- /dev/null +++ b/vendor/github.com/boombuler/barcode/datamatrix/codelayout.go @@ -0,0 +1,213 @@ +package datamatrix + +import ( + "github.com/boombuler/barcode/utils" + "strconv" +) + +type setValFunc func(byte) + +type codeLayout struct { + matrix *utils.BitList + occupy *utils.BitList + size *dmCodeSize +} + +func newCodeLayout(size *dmCodeSize) *codeLayout { + result := new(codeLayout) + result.matrix = utils.NewBitList(size.MatrixColumns() * size.MatrixRows()) + result.occupy = utils.NewBitList(size.MatrixColumns() * size.MatrixRows()) + result.size = size + return result +} + +func (l *codeLayout) Occupied(row, col int) bool { + return l.occupy.GetBit(col + row*l.size.MatrixColumns()) +} + +func (l *codeLayout) Set(row, col int, value, bitNum byte) { + val := ((value >> (7 - bitNum)) & 1) == 1 + if row < 0 { + row += l.size.MatrixRows() + col += 4 - ((l.size.MatrixRows() + 4) % 8) + } + if col < 0 { + col += l.size.MatrixColumns() + row += 4 - ((l.size.MatrixColumns() + 4) % 8) + } + if l.Occupied(row, col) { + panic("Field already occupied row: " + strconv.Itoa(row) + " col: " + strconv.Itoa(col)) + } + + l.occupy.SetBit(col+row*l.size.MatrixColumns(), true) + + l.matrix.SetBit(col+row*l.size.MatrixColumns(), val) +} + +func (l *codeLayout) SetSimple(row, col int, value byte) { + l.Set(row-2, col-2, value, 0) + l.Set(row-2, col-1, value, 1) + l.Set(row-1, col-2, value, 2) + l.Set(row-1, col-1, value, 3) + l.Set(row-1, col-0, value, 4) + l.Set(row-0, col-2, value, 5) + l.Set(row-0, col-1, value, 6) + l.Set(row-0, col-0, value, 7) +} + +func (l *codeLayout) Corner1(value byte) { + l.Set(l.size.MatrixRows()-1, 0, value, 0) + l.Set(l.size.MatrixRows()-1, 1, value, 1) + l.Set(l.size.MatrixRows()-1, 2, value, 2) + l.Set(0, l.size.MatrixColumns()-2, value, 3) + l.Set(0, l.size.MatrixColumns()-1, value, 4) + l.Set(1, l.size.MatrixColumns()-1, value, 5) + l.Set(2, l.size.MatrixColumns()-1, value, 6) + l.Set(3, l.size.MatrixColumns()-1, value, 7) +} + +func (l *codeLayout) Corner2(value byte) { + l.Set(l.size.MatrixRows()-3, 0, value, 0) + l.Set(l.size.MatrixRows()-2, 0, value, 1) + l.Set(l.size.MatrixRows()-1, 0, value, 2) + l.Set(0, l.size.MatrixColumns()-4, value, 3) + l.Set(0, l.size.MatrixColumns()-3, value, 4) + l.Set(0, l.size.MatrixColumns()-2, value, 5) + l.Set(0, l.size.MatrixColumns()-1, value, 6) + l.Set(1, l.size.MatrixColumns()-1, value, 7) +} + +func (l *codeLayout) Corner3(value byte) { + l.Set(l.size.MatrixRows()-3, 0, value, 0) + l.Set(l.size.MatrixRows()-2, 0, value, 1) + l.Set(l.size.MatrixRows()-1, 0, value, 2) + l.Set(0, l.size.MatrixColumns()-2, value, 3) + l.Set(0, l.size.MatrixColumns()-1, value, 4) + l.Set(1, l.size.MatrixColumns()-1, value, 5) + l.Set(2, l.size.MatrixColumns()-1, value, 6) + l.Set(3, l.size.MatrixColumns()-1, value, 7) +} + +func (l *codeLayout) Corner4(value byte) { + l.Set(l.size.MatrixRows()-1, 0, value, 0) + l.Set(l.size.MatrixRows()-1, l.size.MatrixColumns()-1, value, 1) + l.Set(0, l.size.MatrixColumns()-3, value, 2) + l.Set(0, l.size.MatrixColumns()-2, value, 3) + l.Set(0, l.size.MatrixColumns()-1, value, 4) + l.Set(1, l.size.MatrixColumns()-3, value, 5) + l.Set(1, l.size.MatrixColumns()-2, value, 6) + l.Set(1, l.size.MatrixColumns()-1, value, 7) +} + +func (l *codeLayout) SetValues(data []byte) { + idx := 0 + row := 4 + col := 0 + + for (row < l.size.MatrixRows()) || (col < l.size.MatrixColumns()) { + if (row == l.size.MatrixRows()) && (col == 0) { + l.Corner1(data[idx]) + idx++ + } + if (row == l.size.MatrixRows()-2) && (col == 0) && (l.size.MatrixColumns()%4 != 0) { + l.Corner2(data[idx]) + idx++ + } + if (row == l.size.MatrixRows()-2) && (col == 0) && (l.size.MatrixColumns()%8 == 4) { + l.Corner3(data[idx]) + idx++ + } + + if (row == l.size.MatrixRows()+4) && (col == 2) && (l.size.MatrixColumns()%8 == 0) { + l.Corner4(data[idx]) + idx++ + } + + for true { + if (row < l.size.MatrixRows()) && (col >= 0) && !l.Occupied(row, col) { + l.SetSimple(row, col, data[idx]) + idx++ + } + row -= 2 + col += 2 + if (row < 0) || (col >= l.size.MatrixColumns()) { + break + } + } + row += 1 + col += 3 + + for true { + if (row >= 0) && (col < l.size.MatrixColumns()) && !l.Occupied(row, col) { + l.SetSimple(row, col, data[idx]) + idx++ + } + row += 2 + col -= 2 + if (row >= l.size.MatrixRows()) || (col < 0) { + break + } + } + row += 3 + col += 1 + } + + if !l.Occupied(l.size.MatrixRows()-1, l.size.MatrixColumns()-1) { + l.Set(l.size.MatrixRows()-1, l.size.MatrixColumns()-1, 255, 0) + l.Set(l.size.MatrixRows()-2, l.size.MatrixColumns()-2, 255, 0) + } +} + +func (l *codeLayout) Merge() *datamatrixCode { + result := newDataMatrixCode(l.size) + + //dotted horizontal lines + for r := 0; r < l.size.Rows; r += (l.size.RegionRows() + 2) { + for c := 0; c < l.size.Columns; c += 2 { + result.set(c, r, true) + } + } + + //solid horizontal line + for r := l.size.RegionRows() + 1; r < l.size.Rows; r += (l.size.RegionRows() + 2) { + for c := 0; c < l.size.Columns; c++ { + result.set(c, r, true) + } + } + + //dotted vertical lines + for c := l.size.RegionColumns() + 1; c < l.size.Columns; c += (l.size.RegionColumns() + 2) { + for r := 1; r < l.size.Rows; r += 2 { + result.set(c, r, true) + } + } + + //solid vertical line + for c := 0; c < l.size.Columns; c += (l.size.RegionColumns() + 2) { + for r := 0; r < l.size.Rows; r++ { + result.set(c, r, true) + } + } + count := 0 + for hRegion := 0; hRegion < l.size.RegionCountHorizontal; hRegion++ { + for vRegion := 0; vRegion < l.size.RegionCountVertical; vRegion++ { + for x := 0; x < l.size.RegionColumns(); x++ { + colMatrix := (l.size.RegionColumns() * hRegion) + x + colResult := ((2 + l.size.RegionColumns()) * hRegion) + x + 1 + + for y := 0; y < l.size.RegionRows(); y++ { + rowMatrix := (l.size.RegionRows() * vRegion) + y + rowResult := ((2 + l.size.RegionRows()) * vRegion) + y + 1 + val := l.matrix.GetBit(colMatrix + rowMatrix*l.size.MatrixColumns()) + if val { + count++ + } + + result.set(colResult, rowResult, val) + } + } + } + } + + return result +} diff --git a/vendor/github.com/boombuler/barcode/datamatrix/codesize.go b/vendor/github.com/boombuler/barcode/datamatrix/codesize.go new file mode 100644 index 00000000..c63eb8c6 --- /dev/null +++ b/vendor/github.com/boombuler/barcode/datamatrix/codesize.go @@ -0,0 +1,73 @@ +package datamatrix + +type dmCodeSize struct { + Rows int + Columns int + RegionCountHorizontal int + RegionCountVertical int + ECCCount int + BlockCount int +} + +func (s *dmCodeSize) RegionRows() int { + return (s.Rows - (s.RegionCountHorizontal * 2)) / s.RegionCountHorizontal +} + +func (s *dmCodeSize) RegionColumns() int { + return (s.Columns - (s.RegionCountVertical * 2)) / s.RegionCountVertical +} + +func (s *dmCodeSize) MatrixRows() int { + return s.RegionRows() * s.RegionCountHorizontal +} + +func (s *dmCodeSize) MatrixColumns() int { + return s.RegionColumns() * s.RegionCountVertical +} + +func (s *dmCodeSize) DataCodewords() int { + return ((s.MatrixColumns() * s.MatrixRows()) / 8) - s.ECCCount +} + +func (s *dmCodeSize) DataCodewordsForBlock(idx int) int { + if s.Rows == 144 && s.Columns == 144 { + // Special Case... + if idx < 8 { + return 156 + } else { + return 155 + } + } + return s.DataCodewords() / s.BlockCount +} + +func (s *dmCodeSize) ErrorCorrectionCodewordsPerBlock() int { + return s.ECCCount / s.BlockCount +} + +var codeSizes []*dmCodeSize = []*dmCodeSize{ + &dmCodeSize{10, 10, 1, 1, 5, 1}, + &dmCodeSize{12, 12, 1, 1, 7, 1}, + &dmCodeSize{14, 14, 1, 1, 10, 1}, + &dmCodeSize{16, 16, 1, 1, 12, 1}, + &dmCodeSize{18, 18, 1, 1, 14, 1}, + &dmCodeSize{20, 20, 1, 1, 18, 1}, + &dmCodeSize{22, 22, 1, 1, 20, 1}, + &dmCodeSize{24, 24, 1, 1, 24, 1}, + &dmCodeSize{26, 26, 1, 1, 28, 1}, + &dmCodeSize{32, 32, 2, 2, 36, 1}, + &dmCodeSize{36, 36, 2, 2, 42, 1}, + &dmCodeSize{40, 40, 2, 2, 48, 1}, + &dmCodeSize{44, 44, 2, 2, 56, 1}, + &dmCodeSize{48, 48, 2, 2, 68, 1}, + &dmCodeSize{52, 52, 2, 2, 84, 2}, + &dmCodeSize{64, 64, 4, 4, 112, 2}, + &dmCodeSize{72, 72, 4, 4, 144, 4}, + &dmCodeSize{80, 80, 4, 4, 192, 4}, + &dmCodeSize{88, 88, 4, 4, 224, 4}, + &dmCodeSize{96, 96, 4, 4, 272, 4}, + &dmCodeSize{104, 104, 4, 4, 336, 6}, + &dmCodeSize{120, 120, 6, 6, 408, 6}, + &dmCodeSize{132, 132, 6, 6, 496, 8}, + &dmCodeSize{144, 144, 6, 6, 620, 10}, +} diff --git a/vendor/github.com/boombuler/barcode/datamatrix/datamatrixcode.go b/vendor/github.com/boombuler/barcode/datamatrix/datamatrixcode.go new file mode 100644 index 00000000..8f53021a --- /dev/null +++ b/vendor/github.com/boombuler/barcode/datamatrix/datamatrixcode.go @@ -0,0 +1,50 @@ +package datamatrix + +import ( + "image" + "image/color" + + "github.com/boombuler/barcode" + "github.com/boombuler/barcode/utils" +) + +type datamatrixCode struct { + *utils.BitList + *dmCodeSize + content string +} + +func newDataMatrixCode(size *dmCodeSize) *datamatrixCode { + return &datamatrixCode{utils.NewBitList(size.Rows * size.Columns), size, ""} +} + +func (c *datamatrixCode) Content() string { + return c.content +} + +func (c *datamatrixCode) Metadata() barcode.Metadata { + return barcode.Metadata{barcode.TypeDataMatrix, 2} +} + +func (c *datamatrixCode) ColorModel() color.Model { + return color.Gray16Model +} + +func (c *datamatrixCode) Bounds() image.Rectangle { + return image.Rect(0, 0, c.Columns, c.Rows) +} + +func (c *datamatrixCode) At(x, y int) color.Color { + if c.get(x, y) { + return color.Black + } + return color.White +} + +func (c *datamatrixCode) get(x, y int) bool { + return c.GetBit(x*c.Rows + y) +} + +func (c *datamatrixCode) set(x, y int, value bool) { + c.SetBit(x*c.Rows+y, value) +} diff --git a/vendor/github.com/boombuler/barcode/datamatrix/encoder.go b/vendor/github.com/boombuler/barcode/datamatrix/encoder.go new file mode 100644 index 00000000..b8921dcb --- /dev/null +++ b/vendor/github.com/boombuler/barcode/datamatrix/encoder.go @@ -0,0 +1,75 @@ +// Package datamatrix can create Datamatrix barcodes +package datamatrix + +import ( + "errors" + + "github.com/boombuler/barcode" +) + +// Encode returns a Datamatrix barcode for the given content +func Encode(content string) (barcode.Barcode, error) { + data := encodeText(content) + + var size *dmCodeSize + for _, s := range codeSizes { + if s.DataCodewords() >= len(data) { + size = s + break + } + } + if size == nil { + return nil, errors.New("to much data to encode") + } + data = addPadding(data, size.DataCodewords()) + data = ec.calcECC(data, size) + code := render(data, size) + if code != nil { + code.content = content + return code, nil + } + return nil, errors.New("unable to render barcode") +} + +func render(data []byte, size *dmCodeSize) *datamatrixCode { + cl := newCodeLayout(size) + + cl.SetValues(data) + + return cl.Merge() +} + +func encodeText(content string) []byte { + var result []byte + input := []byte(content) + + for i := 0; i < len(input); { + c := input[i] + i++ + + if c >= '0' && c <= '9' && i < len(input) && input[i] >= '0' && input[i] <= '9' { + // two numbers... + c2 := input[i] + i++ + cw := byte(((c-'0')*10 + (c2 - '0')) + 130) + result = append(result, cw) + } else if c > 127 { + // not correct... needs to be redone later... + result = append(result, 235, c-127) + } else { + result = append(result, c+1) + } + } + return result +} + +func addPadding(data []byte, toCount int) []byte { + if len(data) < toCount { + data = append(data, 129) + } + for len(data) < toCount { + R := ((149 * (len(data) + 1)) % 253) + 1 + data = append(data, byte((129+R)%254)) + } + return data +} diff --git a/vendor/github.com/boombuler/barcode/datamatrix/errorcorrection.go b/vendor/github.com/boombuler/barcode/datamatrix/errorcorrection.go new file mode 100644 index 00000000..9617e328 --- /dev/null +++ b/vendor/github.com/boombuler/barcode/datamatrix/errorcorrection.go @@ -0,0 +1,45 @@ +package datamatrix + +import ( + "github.com/boombuler/barcode/utils" +) + +type errorCorrection struct { + rs *utils.ReedSolomonEncoder +} + +var ec *errorCorrection = newErrorCorrection() + +func newErrorCorrection() *errorCorrection { + gf := utils.NewGaloisField(301, 256, 1) + + return &errorCorrection{utils.NewReedSolomonEncoder(gf)} +} + +func (ec *errorCorrection) calcECC(data []byte, size *dmCodeSize) []byte { + dataSize := len(data) + // make some space for error correction codes + data = append(data, make([]byte, size.ECCCount)...) + + for block := 0; block < size.BlockCount; block++ { + dataCnt := size.DataCodewordsForBlock(block) + + buff := make([]int, dataCnt) + // copy the data for the current block to buff + j := 0 + for i := block; i < dataSize; i += size.BlockCount { + buff[j] = int(data[i]) + j++ + } + // calc the error correction codes + ecc := ec.rs.Encode(buff, size.ErrorCorrectionCodewordsPerBlock()) + // and append them to the result + j = 0 + for i := block; i < size.ErrorCorrectionCodewordsPerBlock()*size.BlockCount; i += size.BlockCount { + data[dataSize+i] = byte(ecc[j]) + j++ + } + } + + return data +} diff --git a/vendor/github.com/boombuler/barcode/ean/encoder.go b/vendor/github.com/boombuler/barcode/ean/encoder.go new file mode 100644 index 00000000..a2190f1f --- /dev/null +++ b/vendor/github.com/boombuler/barcode/ean/encoder.go @@ -0,0 +1,187 @@ +// Package ean can create EAN 8 and EAN 13 barcodes. +package ean + +import ( + "errors" + + "github.com/boombuler/barcode" + "github.com/boombuler/barcode/utils" +) + +type encodedNumber struct { + LeftOdd []bool + LeftEven []bool + Right []bool + CheckSum []bool +} + +var encoderTable = map[rune]encodedNumber{ + '0': encodedNumber{ + []bool{false, false, false, true, true, false, true}, + []bool{false, true, false, false, true, true, true}, + []bool{true, true, true, false, false, true, false}, + []bool{false, false, false, false, false, false}, + }, + '1': encodedNumber{ + []bool{false, false, true, true, false, false, true}, + []bool{false, true, true, false, false, true, true}, + []bool{true, true, false, false, true, true, false}, + []bool{false, false, true, false, true, true}, + }, + '2': encodedNumber{ + []bool{false, false, true, false, false, true, true}, + []bool{false, false, true, true, false, true, true}, + []bool{true, true, false, true, true, false, false}, + []bool{false, false, true, true, false, true}, + }, + '3': encodedNumber{ + []bool{false, true, true, true, true, false, true}, + []bool{false, true, false, false, false, false, true}, + []bool{true, false, false, false, false, true, false}, + []bool{false, false, true, true, true, false}, + }, + '4': encodedNumber{ + []bool{false, true, false, false, false, true, true}, + []bool{false, false, true, true, true, false, true}, + []bool{true, false, true, true, true, false, false}, + []bool{false, true, false, false, true, true}, + }, + '5': encodedNumber{ + []bool{false, true, true, false, false, false, true}, + []bool{false, true, true, true, false, false, true}, + []bool{true, false, false, true, true, true, false}, + []bool{false, true, true, false, false, true}, + }, + '6': encodedNumber{ + []bool{false, true, false, true, true, true, true}, + []bool{false, false, false, false, true, false, true}, + []bool{true, false, true, false, false, false, false}, + []bool{false, true, true, true, false, false}, + }, + '7': encodedNumber{ + []bool{false, true, true, true, false, true, true}, + []bool{false, false, true, false, false, false, true}, + []bool{true, false, false, false, true, false, false}, + []bool{false, true, false, true, false, true}, + }, + '8': encodedNumber{ + []bool{false, true, true, false, true, true, true}, + []bool{false, false, false, true, false, false, true}, + []bool{true, false, false, true, false, false, false}, + []bool{false, true, false, true, true, false}, + }, + '9': encodedNumber{ + []bool{false, false, false, true, false, true, true}, + []bool{false, false, true, false, true, true, true}, + []bool{true, true, true, false, true, false, false}, + []bool{false, true, true, false, true, false}, + }, +} + +func calcCheckNum(code string) rune { + x3 := len(code) == 7 + sum := 0 + for _, r := range code { + curNum := utils.RuneToInt(r) + if curNum < 0 || curNum > 9 { + return 'B' + } + if x3 { + curNum = curNum * 3 + } + x3 = !x3 + sum += curNum + } + + return utils.IntToRune((10 - (sum % 10)) % 10) +} + +func encodeEAN8(code string) *utils.BitList { + result := new(utils.BitList) + result.AddBit(true, false, true) + + for cpos, r := range code { + num, ok := encoderTable[r] + if !ok { + return nil + } + var data []bool + if cpos < 4 { + data = num.LeftOdd + } else { + data = num.Right + } + + if cpos == 4 { + result.AddBit(false, true, false, true, false) + } + result.AddBit(data...) + } + result.AddBit(true, false, true) + + return result +} + +func encodeEAN13(code string) *utils.BitList { + result := new(utils.BitList) + result.AddBit(true, false, true) + + var firstNum []bool + for cpos, r := range code { + num, ok := encoderTable[r] + if !ok { + return nil + } + if cpos == 0 { + firstNum = num.CheckSum + continue + } + + var data []bool + if cpos < 7 { // Left + if firstNum[cpos-1] { + data = num.LeftEven + } else { + data = num.LeftOdd + } + } else { + data = num.Right + } + + if cpos == 7 { + result.AddBit(false, true, false, true, false) + } + result.AddBit(data...) + } + result.AddBit(true, false, true) + return result +} + +// Encode returns a EAN 8 or EAN 13 barcode for the given code +func Encode(code string) (barcode.BarcodeIntCS, error) { + var checkSum int + if len(code) == 7 || len(code) == 12 { + code += string(calcCheckNum(code)) + checkSum = utils.RuneToInt(calcCheckNum(code)) + } else if len(code) == 8 || len(code) == 13 { + check := code[0 : len(code)-1] + check += string(calcCheckNum(check)) + if check != code { + return nil, errors.New("checksum missmatch") + } + checkSum = utils.RuneToInt(rune(code[len(code)-1])) + } + + if len(code) == 8 { + result := encodeEAN8(code) + if result != nil { + return utils.New1DCodeIntCheckSum(barcode.TypeEAN8, code, result, checkSum), nil + } + } else if len(code) == 13 { + result := encodeEAN13(code) + if result != nil { + return utils.New1DCodeIntCheckSum(barcode.TypeEAN13, code, result, checkSum), nil + } + } + return nil, errors.New("invalid ean code data") +} diff --git a/vendor/github.com/boombuler/barcode/qr/alphanumeric.go b/vendor/github.com/boombuler/barcode/qr/alphanumeric.go new file mode 100644 index 00000000..4ded7c8e --- /dev/null +++ b/vendor/github.com/boombuler/barcode/qr/alphanumeric.go @@ -0,0 +1,66 @@ +package qr + +import ( + "errors" + "fmt" + "strings" + + "github.com/boombuler/barcode/utils" +) + +const charSet string = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:" + +func stringToAlphaIdx(content string) <-chan int { + result := make(chan int) + go func() { + for _, r := range content { + idx := strings.IndexRune(charSet, r) + result <- idx + if idx < 0 { + break + } + } + close(result) + }() + + return result +} + +func encodeAlphaNumeric(content string, ecl ErrorCorrectionLevel) (*utils.BitList, *versionInfo, error) { + + contentLenIsOdd := len(content)%2 == 1 + contentBitCount := (len(content) / 2) * 11 + if contentLenIsOdd { + contentBitCount += 6 + } + vi := findSmallestVersionInfo(ecl, alphaNumericMode, contentBitCount) + if vi == nil { + return nil, nil, errors.New("To much data to encode") + } + + res := new(utils.BitList) + res.AddBits(int(alphaNumericMode), 4) + res.AddBits(len(content), vi.charCountBits(alphaNumericMode)) + + encoder := stringToAlphaIdx(content) + + for idx := 0; idx < len(content)/2; idx++ { + c1 := <-encoder + c2 := <-encoder + if c1 < 0 || c2 < 0 { + return nil, nil, fmt.Errorf("\"%s\" can not be encoded as %s", content, AlphaNumeric) + } + res.AddBits(c1*45+c2, 11) + } + if contentLenIsOdd { + c := <-encoder + if c < 0 { + return nil, nil, fmt.Errorf("\"%s\" can not be encoded as %s", content, AlphaNumeric) + } + res.AddBits(c, 6) + } + + addPaddingAndTerminator(res, vi) + + return res, vi, nil +} diff --git a/vendor/github.com/boombuler/barcode/qr/automatic.go b/vendor/github.com/boombuler/barcode/qr/automatic.go new file mode 100644 index 00000000..e7c56013 --- /dev/null +++ b/vendor/github.com/boombuler/barcode/qr/automatic.go @@ -0,0 +1,23 @@ +package qr + +import ( + "fmt" + + "github.com/boombuler/barcode/utils" +) + +func encodeAuto(content string, ecl ErrorCorrectionLevel) (*utils.BitList, *versionInfo, error) { + bits, vi, _ := Numeric.getEncoder()(content, ecl) + if bits != nil && vi != nil { + return bits, vi, nil + } + bits, vi, _ = AlphaNumeric.getEncoder()(content, ecl) + if bits != nil && vi != nil { + return bits, vi, nil + } + bits, vi, _ = Unicode.getEncoder()(content, ecl) + if bits != nil && vi != nil { + return bits, vi, nil + } + return nil, nil, fmt.Errorf("No encoding found to encode \"%s\"", content) +} diff --git a/vendor/github.com/boombuler/barcode/qr/blocks.go b/vendor/github.com/boombuler/barcode/qr/blocks.go new file mode 100644 index 00000000..d3173787 --- /dev/null +++ b/vendor/github.com/boombuler/barcode/qr/blocks.go @@ -0,0 +1,59 @@ +package qr + +type block struct { + data []byte + ecc []byte +} +type blockList []*block + +func splitToBlocks(data <-chan byte, vi *versionInfo) blockList { + result := make(blockList, vi.NumberOfBlocksInGroup1+vi.NumberOfBlocksInGroup2) + + for b := 0; b < int(vi.NumberOfBlocksInGroup1); b++ { + blk := new(block) + blk.data = make([]byte, vi.DataCodeWordsPerBlockInGroup1) + for cw := 0; cw < int(vi.DataCodeWordsPerBlockInGroup1); cw++ { + blk.data[cw] = <-data + } + blk.ecc = ec.calcECC(blk.data, vi.ErrorCorrectionCodewordsPerBlock) + result[b] = blk + } + + for b := 0; b < int(vi.NumberOfBlocksInGroup2); b++ { + blk := new(block) + blk.data = make([]byte, vi.DataCodeWordsPerBlockInGroup2) + for cw := 0; cw < int(vi.DataCodeWordsPerBlockInGroup2); cw++ { + blk.data[cw] = <-data + } + blk.ecc = ec.calcECC(blk.data, vi.ErrorCorrectionCodewordsPerBlock) + result[int(vi.NumberOfBlocksInGroup1)+b] = blk + } + + return result +} + +func (bl blockList) interleave(vi *versionInfo) []byte { + var maxCodewordCount int + if vi.DataCodeWordsPerBlockInGroup1 > vi.DataCodeWordsPerBlockInGroup2 { + maxCodewordCount = int(vi.DataCodeWordsPerBlockInGroup1) + } else { + maxCodewordCount = int(vi.DataCodeWordsPerBlockInGroup2) + } + resultLen := (vi.DataCodeWordsPerBlockInGroup1+vi.ErrorCorrectionCodewordsPerBlock)*vi.NumberOfBlocksInGroup1 + + (vi.DataCodeWordsPerBlockInGroup2+vi.ErrorCorrectionCodewordsPerBlock)*vi.NumberOfBlocksInGroup2 + + result := make([]byte, 0, resultLen) + for i := 0; i < maxCodewordCount; i++ { + for b := 0; b < len(bl); b++ { + if len(bl[b].data) > i { + result = append(result, bl[b].data[i]) + } + } + } + for i := 0; i < int(vi.ErrorCorrectionCodewordsPerBlock); i++ { + for b := 0; b < len(bl); b++ { + result = append(result, bl[b].ecc[i]) + } + } + return result +} diff --git a/vendor/github.com/boombuler/barcode/qr/encoder.go b/vendor/github.com/boombuler/barcode/qr/encoder.go new file mode 100644 index 00000000..2c6ab211 --- /dev/null +++ b/vendor/github.com/boombuler/barcode/qr/encoder.go @@ -0,0 +1,416 @@ +// Package qr can be used to create QR barcodes. +package qr + +import ( + "image" + + "github.com/boombuler/barcode" + "github.com/boombuler/barcode/utils" +) + +type encodeFn func(content string, eccLevel ErrorCorrectionLevel) (*utils.BitList, *versionInfo, error) + +// Encoding mode for QR Codes. +type Encoding byte + +const ( + // Auto will choose ths best matching encoding + Auto Encoding = iota + // Numeric encoding only encodes numbers [0-9] + Numeric + // AlphaNumeric encoding only encodes uppercase letters, numbers and [Space], $, %, *, +, -, ., /, : + AlphaNumeric + // Unicode encoding encodes the string as utf-8 + Unicode + // only for testing purpose + unknownEncoding +) + +func (e Encoding) getEncoder() encodeFn { + switch e { + case Auto: + return encodeAuto + case Numeric: + return encodeNumeric + case AlphaNumeric: + return encodeAlphaNumeric + case Unicode: + return encodeUnicode + } + return nil +} + +func (e Encoding) String() string { + switch e { + case Auto: + return "Auto" + case Numeric: + return "Numeric" + case AlphaNumeric: + return "AlphaNumeric" + case Unicode: + return "Unicode" + } + return "" +} + +// Encode returns a QR barcode with the given content, error correction level and uses the given encoding +func Encode(content string, level ErrorCorrectionLevel, mode Encoding) (barcode.Barcode, error) { + bits, vi, err := mode.getEncoder()(content, level) + if err != nil { + return nil, err + } + + blocks := splitToBlocks(bits.IterateBytes(), vi) + data := blocks.interleave(vi) + result := render(data, vi) + result.content = content + return result, nil +} + +func render(data []byte, vi *versionInfo) *qrcode { + dim := vi.modulWidth() + results := make([]*qrcode, 8) + for i := 0; i < 8; i++ { + results[i] = newBarcode(dim) + } + + occupied := newBarcode(dim) + + setAll := func(x int, y int, val bool) { + occupied.Set(x, y, true) + for i := 0; i < 8; i++ { + results[i].Set(x, y, val) + } + } + + drawFinderPatterns(vi, setAll) + drawAlignmentPatterns(occupied, vi, setAll) + + //Timing Pattern: + var i int + for i = 0; i < dim; i++ { + if !occupied.Get(i, 6) { + setAll(i, 6, i%2 == 0) + } + if !occupied.Get(6, i) { + setAll(6, i, i%2 == 0) + } + } + // Dark Module + setAll(8, dim-8, true) + + drawVersionInfo(vi, setAll) + drawFormatInfo(vi, -1, occupied.Set) + for i := 0; i < 8; i++ { + drawFormatInfo(vi, i, results[i].Set) + } + + // Write the data + var curBitNo int + + for pos := range iterateModules(occupied) { + var curBit bool + if curBitNo < len(data)*8 { + curBit = ((data[curBitNo/8] >> uint(7-(curBitNo%8))) & 1) == 1 + } else { + curBit = false + } + + for i := 0; i < 8; i++ { + setMasked(pos.X, pos.Y, curBit, i, results[i].Set) + } + curBitNo++ + } + + lowestPenalty := ^uint(0) + lowestPenaltyIdx := -1 + for i := 0; i < 8; i++ { + p := results[i].calcPenalty() + if p < lowestPenalty { + lowestPenalty = p + lowestPenaltyIdx = i + } + } + return results[lowestPenaltyIdx] +} + +func setMasked(x, y int, val bool, mask int, set func(int, int, bool)) { + switch mask { + case 0: + val = val != (((y + x) % 2) == 0) + break + case 1: + val = val != ((y % 2) == 0) + break + case 2: + val = val != ((x % 3) == 0) + break + case 3: + val = val != (((y + x) % 3) == 0) + break + case 4: + val = val != (((y/2 + x/3) % 2) == 0) + break + case 5: + val = val != (((y*x)%2)+((y*x)%3) == 0) + break + case 6: + val = val != ((((y*x)%2)+((y*x)%3))%2 == 0) + break + case 7: + val = val != ((((y+x)%2)+((y*x)%3))%2 == 0) + } + set(x, y, val) +} + +func iterateModules(occupied *qrcode) <-chan image.Point { + result := make(chan image.Point) + allPoints := make(chan image.Point) + go func() { + curX := occupied.dimension - 1 + curY := occupied.dimension - 1 + isUpward := true + + for true { + if isUpward { + allPoints <- image.Pt(curX, curY) + allPoints <- image.Pt(curX-1, curY) + curY-- + if curY < 0 { + curY = 0 + curX -= 2 + if curX == 6 { + curX-- + } + if curX < 0 { + break + } + isUpward = false + } + } else { + allPoints <- image.Pt(curX, curY) + allPoints <- image.Pt(curX-1, curY) + curY++ + if curY >= occupied.dimension { + curY = occupied.dimension - 1 + curX -= 2 + if curX == 6 { + curX-- + } + isUpward = true + if curX < 0 { + break + } + } + } + } + + close(allPoints) + }() + go func() { + for pt := range allPoints { + if !occupied.Get(pt.X, pt.Y) { + result <- pt + } + } + close(result) + }() + return result +} + +func drawFinderPatterns(vi *versionInfo, set func(int, int, bool)) { + dim := vi.modulWidth() + drawPattern := func(xoff int, yoff int) { + for x := -1; x < 8; x++ { + for y := -1; y < 8; y++ { + val := (x == 0 || x == 6 || y == 0 || y == 6 || (x > 1 && x < 5 && y > 1 && y < 5)) && (x <= 6 && y <= 6 && x >= 0 && y >= 0) + + if x+xoff >= 0 && x+xoff < dim && y+yoff >= 0 && y+yoff < dim { + set(x+xoff, y+yoff, val) + } + } + } + } + drawPattern(0, 0) + drawPattern(0, dim-7) + drawPattern(dim-7, 0) +} + +func drawAlignmentPatterns(occupied *qrcode, vi *versionInfo, set func(int, int, bool)) { + drawPattern := func(xoff int, yoff int) { + for x := -2; x <= 2; x++ { + for y := -2; y <= 2; y++ { + val := x == -2 || x == 2 || y == -2 || y == 2 || (x == 0 && y == 0) + set(x+xoff, y+yoff, val) + } + } + } + positions := vi.alignmentPatternPlacements() + + for _, x := range positions { + for _, y := range positions { + if occupied.Get(x, y) { + continue + } + drawPattern(x, y) + } + } +} + +var formatInfos = map[ErrorCorrectionLevel]map[int][]bool{ + L: { + 0: []bool{true, true, true, false, true, true, true, true, true, false, false, false, true, false, false}, + 1: []bool{true, true, true, false, false, true, false, true, true, true, true, false, false, true, true}, + 2: []bool{true, true, true, true, true, false, true, true, false, true, false, true, false, true, false}, + 3: []bool{true, true, true, true, false, false, false, true, false, false, true, true, true, false, true}, + 4: []bool{true, true, false, false, true, true, false, false, false, true, false, true, true, true, true}, + 5: []bool{true, true, false, false, false, true, true, false, false, false, true, true, false, false, false}, + 6: []bool{true, true, false, true, true, false, false, false, true, false, false, false, false, false, true}, + 7: []bool{true, true, false, true, false, false, true, false, true, true, true, false, true, true, false}, + }, + M: { + 0: []bool{true, false, true, false, true, false, false, false, false, false, true, false, false, true, false}, + 1: []bool{true, false, true, false, false, false, true, false, false, true, false, false, true, false, true}, + 2: []bool{true, false, true, true, true, true, false, false, true, true, true, true, true, false, false}, + 3: []bool{true, false, true, true, false, true, true, false, true, false, false, true, false, true, true}, + 4: []bool{true, false, false, false, true, false, true, true, true, true, true, true, false, false, true}, + 5: []bool{true, false, false, false, false, false, false, true, true, false, false, true, true, true, false}, + 6: []bool{true, false, false, true, true, true, true, true, false, false, true, false, true, true, true}, + 7: []bool{true, false, false, true, false, true, false, true, false, true, false, false, false, false, false}, + }, + Q: { + 0: []bool{false, true, true, false, true, false, true, false, true, false, true, true, true, true, true}, + 1: []bool{false, true, true, false, false, false, false, false, true, true, false, true, false, false, false}, + 2: []bool{false, true, true, true, true, true, true, false, false, true, true, false, false, false, true}, + 3: []bool{false, true, true, true, false, true, false, false, false, false, false, false, true, true, false}, + 4: []bool{false, true, false, false, true, false, false, true, false, true, true, false, true, false, false}, + 5: []bool{false, true, false, false, false, false, true, true, false, false, false, false, false, true, true}, + 6: []bool{false, true, false, true, true, true, false, true, true, false, true, true, false, true, false}, + 7: []bool{false, true, false, true, false, true, true, true, true, true, false, true, true, false, true}, + }, + H: { + 0: []bool{false, false, true, false, true, true, false, true, false, false, false, true, false, false, true}, + 1: []bool{false, false, true, false, false, true, true, true, false, true, true, true, true, true, false}, + 2: []bool{false, false, true, true, true, false, false, true, true, true, false, false, true, true, true}, + 3: []bool{false, false, true, true, false, false, true, true, true, false, true, false, false, false, false}, + 4: []bool{false, false, false, false, true, true, true, false, true, true, false, false, false, true, false}, + 5: []bool{false, false, false, false, false, true, false, false, true, false, true, false, true, false, true}, + 6: []bool{false, false, false, true, true, false, true, false, false, false, false, true, true, false, false}, + 7: []bool{false, false, false, true, false, false, false, false, false, true, true, true, false, true, true}, + }, +} + +func drawFormatInfo(vi *versionInfo, usedMask int, set func(int, int, bool)) { + var formatInfo []bool + + if usedMask == -1 { + formatInfo = []bool{true, true, true, true, true, true, true, true, true, true, true, true, true, true, true} // Set all to true cause -1 --> occupied mask. + } else { + formatInfo = formatInfos[vi.Level][usedMask] + } + + if len(formatInfo) == 15 { + dim := vi.modulWidth() + set(0, 8, formatInfo[0]) + set(1, 8, formatInfo[1]) + set(2, 8, formatInfo[2]) + set(3, 8, formatInfo[3]) + set(4, 8, formatInfo[4]) + set(5, 8, formatInfo[5]) + set(7, 8, formatInfo[6]) + set(8, 8, formatInfo[7]) + set(8, 7, formatInfo[8]) + set(8, 5, formatInfo[9]) + set(8, 4, formatInfo[10]) + set(8, 3, formatInfo[11]) + set(8, 2, formatInfo[12]) + set(8, 1, formatInfo[13]) + set(8, 0, formatInfo[14]) + + set(8, dim-1, formatInfo[0]) + set(8, dim-2, formatInfo[1]) + set(8, dim-3, formatInfo[2]) + set(8, dim-4, formatInfo[3]) + set(8, dim-5, formatInfo[4]) + set(8, dim-6, formatInfo[5]) + set(8, dim-7, formatInfo[6]) + set(dim-8, 8, formatInfo[7]) + set(dim-7, 8, formatInfo[8]) + set(dim-6, 8, formatInfo[9]) + set(dim-5, 8, formatInfo[10]) + set(dim-4, 8, formatInfo[11]) + set(dim-3, 8, formatInfo[12]) + set(dim-2, 8, formatInfo[13]) + set(dim-1, 8, formatInfo[14]) + } +} + +var versionInfoBitsByVersion = map[byte][]bool{ + 7: []bool{false, false, false, true, true, true, true, true, false, false, true, false, false, true, false, true, false, false}, + 8: []bool{false, false, true, false, false, false, false, true, false, true, true, false, true, true, true, true, false, false}, + 9: []bool{false, false, true, false, false, true, true, false, true, false, true, false, false, true, true, false, false, true}, + 10: []bool{false, false, true, false, true, false, false, true, false, false, true, true, false, true, false, false, true, true}, + 11: []bool{false, false, true, false, true, true, true, false, true, true, true, true, true, true, false, true, true, false}, + 12: []bool{false, false, true, true, false, false, false, true, true, true, false, true, true, false, false, false, true, false}, + 13: []bool{false, false, true, true, false, true, true, false, false, false, false, true, false, false, false, true, true, true}, + 14: []bool{false, false, true, true, true, false, false, true, true, false, false, false, false, false, true, true, false, true}, + 15: []bool{false, false, true, true, true, true, true, false, false, true, false, false, true, false, true, false, false, false}, + 16: []bool{false, true, false, false, false, false, true, false, true, true, false, true, true, true, true, false, false, false}, + 17: []bool{false, true, false, false, false, true, false, true, false, false, false, true, false, true, true, true, false, true}, + 18: []bool{false, true, false, false, true, false, true, false, true, false, false, false, false, true, false, true, true, true}, + 19: []bool{false, true, false, false, true, true, false, true, false, true, false, false, true, true, false, false, true, false}, + 20: []bool{false, true, false, true, false, false, true, false, false, true, true, false, true, false, false, true, true, false}, + 21: []bool{false, true, false, true, false, true, false, true, true, false, true, false, false, false, false, false, true, true}, + 22: []bool{false, true, false, true, true, false, true, false, false, false, true, true, false, false, true, false, false, true}, + 23: []bool{false, true, false, true, true, true, false, true, true, true, true, true, true, false, true, true, false, false}, + 24: []bool{false, true, true, false, false, false, true, true, true, false, true, true, false, false, false, true, false, false}, + 25: []bool{false, true, true, false, false, true, false, false, false, true, true, true, true, false, false, false, false, true}, + 26: []bool{false, true, true, false, true, false, true, true, true, true, true, false, true, false, true, false, true, true}, + 27: []bool{false, true, true, false, true, true, false, false, false, false, true, false, false, false, true, true, true, false}, + 28: []bool{false, true, true, true, false, false, true, true, false, false, false, false, false, true, true, false, true, false}, + 29: []bool{false, true, true, true, false, true, false, false, true, true, false, false, true, true, true, true, true, true}, + 30: []bool{false, true, true, true, true, false, true, true, false, true, false, true, true, true, false, true, false, true}, + 31: []bool{false, true, true, true, true, true, false, false, true, false, false, true, false, true, false, false, false, false}, + 32: []bool{true, false, false, false, false, false, true, false, false, true, true, true, false, true, false, true, false, true}, + 33: []bool{true, false, false, false, false, true, false, true, true, false, true, true, true, true, false, false, false, false}, + 34: []bool{true, false, false, false, true, false, true, false, false, false, true, false, true, true, true, false, true, false}, + 35: []bool{true, false, false, false, true, true, false, true, true, true, true, false, false, true, true, true, true, true}, + 36: []bool{true, false, false, true, false, false, true, false, true, true, false, false, false, false, true, false, true, true}, + 37: []bool{true, false, false, true, false, true, false, true, false, false, false, false, true, false, true, true, true, false}, + 38: []bool{true, false, false, true, true, false, true, false, true, false, false, true, true, false, false, true, false, false}, + 39: []bool{true, false, false, true, true, true, false, true, false, true, false, true, false, false, false, false, false, true}, + 40: []bool{true, false, true, false, false, false, true, true, false, false, false, true, true, false, true, false, false, true}, +} + +func drawVersionInfo(vi *versionInfo, set func(int, int, bool)) { + versionInfoBits, ok := versionInfoBitsByVersion[vi.Version] + + if ok && len(versionInfoBits) > 0 { + for i := 0; i < len(versionInfoBits); i++ { + x := (vi.modulWidth() - 11) + i%3 + y := i / 3 + set(x, y, versionInfoBits[len(versionInfoBits)-i-1]) + set(y, x, versionInfoBits[len(versionInfoBits)-i-1]) + } + } + +} + +func addPaddingAndTerminator(bl *utils.BitList, vi *versionInfo) { + for i := 0; i < 4 && bl.Len() < vi.totalDataBytes()*8; i++ { + bl.AddBit(false) + } + + for bl.Len()%8 != 0 { + bl.AddBit(false) + } + + for i := 0; bl.Len() < vi.totalDataBytes()*8; i++ { + if i%2 == 0 { + bl.AddByte(236) + } else { + bl.AddByte(17) + } + } +} diff --git a/vendor/github.com/boombuler/barcode/qr/errorcorrection.go b/vendor/github.com/boombuler/barcode/qr/errorcorrection.go new file mode 100644 index 00000000..08ebf0ce --- /dev/null +++ b/vendor/github.com/boombuler/barcode/qr/errorcorrection.go @@ -0,0 +1,29 @@ +package qr + +import ( + "github.com/boombuler/barcode/utils" +) + +type errorCorrection struct { + rs *utils.ReedSolomonEncoder +} + +var ec = newErrorCorrection() + +func newErrorCorrection() *errorCorrection { + fld := utils.NewGaloisField(285, 256, 0) + return &errorCorrection{utils.NewReedSolomonEncoder(fld)} +} + +func (ec *errorCorrection) calcECC(data []byte, eccCount byte) []byte { + dataInts := make([]int, len(data)) + for i := 0; i < len(data); i++ { + dataInts[i] = int(data[i]) + } + res := ec.rs.Encode(dataInts, int(eccCount)) + result := make([]byte, len(res)) + for i := 0; i < len(res); i++ { + result[i] = byte(res[i]) + } + return result +} diff --git a/vendor/github.com/boombuler/barcode/qr/numeric.go b/vendor/github.com/boombuler/barcode/qr/numeric.go new file mode 100644 index 00000000..49b44cc4 --- /dev/null +++ b/vendor/github.com/boombuler/barcode/qr/numeric.go @@ -0,0 +1,56 @@ +package qr + +import ( + "errors" + "fmt" + "strconv" + + "github.com/boombuler/barcode/utils" +) + +func encodeNumeric(content string, ecl ErrorCorrectionLevel) (*utils.BitList, *versionInfo, error) { + contentBitCount := (len(content) / 3) * 10 + switch len(content) % 3 { + case 1: + contentBitCount += 4 + case 2: + contentBitCount += 7 + } + vi := findSmallestVersionInfo(ecl, numericMode, contentBitCount) + if vi == nil { + return nil, nil, errors.New("To much data to encode") + } + res := new(utils.BitList) + res.AddBits(int(numericMode), 4) + res.AddBits(len(content), vi.charCountBits(numericMode)) + + for pos := 0; pos < len(content); pos += 3 { + var curStr string + if pos+3 <= len(content) { + curStr = content[pos : pos+3] + } else { + curStr = content[pos:] + } + + i, err := strconv.Atoi(curStr) + if err != nil || i < 0 { + return nil, nil, fmt.Errorf("\"%s\" can not be encoded as %s", content, Numeric) + } + var bitCnt byte + switch len(curStr) % 3 { + case 0: + bitCnt = 10 + case 1: + bitCnt = 4 + break + case 2: + bitCnt = 7 + break + } + + res.AddBits(i, bitCnt) + } + + addPaddingAndTerminator(res, vi) + return res, vi, nil +} diff --git a/vendor/github.com/boombuler/barcode/qr/qrcode.go b/vendor/github.com/boombuler/barcode/qr/qrcode.go new file mode 100644 index 00000000..13607604 --- /dev/null +++ b/vendor/github.com/boombuler/barcode/qr/qrcode.go @@ -0,0 +1,166 @@ +package qr + +import ( + "image" + "image/color" + "math" + + "github.com/boombuler/barcode" + "github.com/boombuler/barcode/utils" +) + +type qrcode struct { + dimension int + data *utils.BitList + content string +} + +func (qr *qrcode) Content() string { + return qr.content +} + +func (qr *qrcode) Metadata() barcode.Metadata { + return barcode.Metadata{barcode.TypeQR, 2} +} + +func (qr *qrcode) ColorModel() color.Model { + return color.Gray16Model +} + +func (qr *qrcode) Bounds() image.Rectangle { + return image.Rect(0, 0, qr.dimension, qr.dimension) +} + +func (qr *qrcode) At(x, y int) color.Color { + if qr.Get(x, y) { + return color.Black + } + return color.White +} + +func (qr *qrcode) Get(x, y int) bool { + return qr.data.GetBit(x*qr.dimension + y) +} + +func (qr *qrcode) Set(x, y int, val bool) { + qr.data.SetBit(x*qr.dimension+y, val) +} + +func (qr *qrcode) calcPenalty() uint { + return qr.calcPenaltyRule1() + qr.calcPenaltyRule2() + qr.calcPenaltyRule3() + qr.calcPenaltyRule4() +} + +func (qr *qrcode) calcPenaltyRule1() uint { + var result uint + for x := 0; x < qr.dimension; x++ { + checkForX := false + var cntX uint + checkForY := false + var cntY uint + + for y := 0; y < qr.dimension; y++ { + if qr.Get(x, y) == checkForX { + cntX++ + } else { + checkForX = !checkForX + if cntX >= 5 { + result += cntX - 2 + } + cntX = 1 + } + + if qr.Get(y, x) == checkForY { + cntY++ + } else { + checkForY = !checkForY + if cntY >= 5 { + result += cntY - 2 + } + cntY = 1 + } + } + + if cntX >= 5 { + result += cntX - 2 + } + if cntY >= 5 { + result += cntY - 2 + } + } + + return result +} + +func (qr *qrcode) calcPenaltyRule2() uint { + var result uint + for x := 0; x < qr.dimension-1; x++ { + for y := 0; y < qr.dimension-1; y++ { + check := qr.Get(x, y) + if qr.Get(x, y+1) == check && qr.Get(x+1, y) == check && qr.Get(x+1, y+1) == check { + result += 3 + } + } + } + return result +} + +func (qr *qrcode) calcPenaltyRule3() uint { + pattern1 := []bool{true, false, true, true, true, false, true, false, false, false, false} + pattern2 := []bool{false, false, false, false, true, false, true, true, true, false, true} + + var result uint + for x := 0; x <= qr.dimension-len(pattern1); x++ { + for y := 0; y < qr.dimension; y++ { + pattern1XFound := true + pattern2XFound := true + pattern1YFound := true + pattern2YFound := true + + for i := 0; i < len(pattern1); i++ { + iv := qr.Get(x+i, y) + if iv != pattern1[i] { + pattern1XFound = false + } + if iv != pattern2[i] { + pattern2XFound = false + } + iv = qr.Get(y, x+i) + if iv != pattern1[i] { + pattern1YFound = false + } + if iv != pattern2[i] { + pattern2YFound = false + } + } + if pattern1XFound || pattern2XFound { + result += 40 + } + if pattern1YFound || pattern2YFound { + result += 40 + } + } + } + + return result +} + +func (qr *qrcode) calcPenaltyRule4() uint { + totalNum := qr.data.Len() + trueCnt := 0 + for i := 0; i < totalNum; i++ { + if qr.data.GetBit(i) { + trueCnt++ + } + } + percDark := float64(trueCnt) * 100 / float64(totalNum) + floor := math.Abs(math.Floor(percDark/5) - 10) + ceil := math.Abs(math.Ceil(percDark/5) - 10) + return uint(math.Min(floor, ceil) * 10) +} + +func newBarcode(dim int) *qrcode { + res := new(qrcode) + res.dimension = dim + res.data = utils.NewBitList(dim * dim) + return res +} diff --git a/vendor/github.com/boombuler/barcode/qr/unicode.go b/vendor/github.com/boombuler/barcode/qr/unicode.go new file mode 100644 index 00000000..a9135ab6 --- /dev/null +++ b/vendor/github.com/boombuler/barcode/qr/unicode.go @@ -0,0 +1,27 @@ +package qr + +import ( + "errors" + + "github.com/boombuler/barcode/utils" +) + +func encodeUnicode(content string, ecl ErrorCorrectionLevel) (*utils.BitList, *versionInfo, error) { + data := []byte(content) + + vi := findSmallestVersionInfo(ecl, byteMode, len(data)*8) + if vi == nil { + return nil, nil, errors.New("To much data to encode") + } + + // It's not correct to add the unicode bytes to the result directly but most readers can't handle the + // required ECI header... + res := new(utils.BitList) + res.AddBits(int(byteMode), 4) + res.AddBits(len(content), vi.charCountBits(byteMode)) + for _, b := range data { + res.AddByte(b) + } + addPaddingAndTerminator(res, vi) + return res, vi, nil +} diff --git a/vendor/github.com/boombuler/barcode/qr/versioninfo.go b/vendor/github.com/boombuler/barcode/qr/versioninfo.go new file mode 100644 index 00000000..6852a576 --- /dev/null +++ b/vendor/github.com/boombuler/barcode/qr/versioninfo.go @@ -0,0 +1,310 @@ +package qr + +import "math" + +// ErrorCorrectionLevel indicates the amount of "backup data" stored in the QR code +type ErrorCorrectionLevel byte + +const ( + // L recovers 7% of data + L ErrorCorrectionLevel = iota + // M recovers 15% of data + M + // Q recovers 25% of data + Q + // H recovers 30% of data + H +) + +func (ecl ErrorCorrectionLevel) String() string { + switch ecl { + case L: + return "L" + case M: + return "M" + case Q: + return "Q" + case H: + return "H" + } + return "unknown" +} + +type encodingMode byte + +const ( + numericMode encodingMode = 1 + alphaNumericMode encodingMode = 2 + byteMode encodingMode = 4 + kanjiMode encodingMode = 8 +) + +type versionInfo struct { + Version byte + Level ErrorCorrectionLevel + ErrorCorrectionCodewordsPerBlock byte + NumberOfBlocksInGroup1 byte + DataCodeWordsPerBlockInGroup1 byte + NumberOfBlocksInGroup2 byte + DataCodeWordsPerBlockInGroup2 byte +} + +var versionInfos = []*versionInfo{ + &versionInfo{1, L, 7, 1, 19, 0, 0}, + &versionInfo{1, M, 10, 1, 16, 0, 0}, + &versionInfo{1, Q, 13, 1, 13, 0, 0}, + &versionInfo{1, H, 17, 1, 9, 0, 0}, + &versionInfo{2, L, 10, 1, 34, 0, 0}, + &versionInfo{2, M, 16, 1, 28, 0, 0}, + &versionInfo{2, Q, 22, 1, 22, 0, 0}, + &versionInfo{2, H, 28, 1, 16, 0, 0}, + &versionInfo{3, L, 15, 1, 55, 0, 0}, + &versionInfo{3, M, 26, 1, 44, 0, 0}, + &versionInfo{3, Q, 18, 2, 17, 0, 0}, + &versionInfo{3, H, 22, 2, 13, 0, 0}, + &versionInfo{4, L, 20, 1, 80, 0, 0}, + &versionInfo{4, M, 18, 2, 32, 0, 0}, + &versionInfo{4, Q, 26, 2, 24, 0, 0}, + &versionInfo{4, H, 16, 4, 9, 0, 0}, + &versionInfo{5, L, 26, 1, 108, 0, 0}, + &versionInfo{5, M, 24, 2, 43, 0, 0}, + &versionInfo{5, Q, 18, 2, 15, 2, 16}, + &versionInfo{5, H, 22, 2, 11, 2, 12}, + &versionInfo{6, L, 18, 2, 68, 0, 0}, + &versionInfo{6, M, 16, 4, 27, 0, 0}, + &versionInfo{6, Q, 24, 4, 19, 0, 0}, + &versionInfo{6, H, 28, 4, 15, 0, 0}, + &versionInfo{7, L, 20, 2, 78, 0, 0}, + &versionInfo{7, M, 18, 4, 31, 0, 0}, + &versionInfo{7, Q, 18, 2, 14, 4, 15}, + &versionInfo{7, H, 26, 4, 13, 1, 14}, + &versionInfo{8, L, 24, 2, 97, 0, 0}, + &versionInfo{8, M, 22, 2, 38, 2, 39}, + &versionInfo{8, Q, 22, 4, 18, 2, 19}, + &versionInfo{8, H, 26, 4, 14, 2, 15}, + &versionInfo{9, L, 30, 2, 116, 0, 0}, + &versionInfo{9, M, 22, 3, 36, 2, 37}, + &versionInfo{9, Q, 20, 4, 16, 4, 17}, + &versionInfo{9, H, 24, 4, 12, 4, 13}, + &versionInfo{10, L, 18, 2, 68, 2, 69}, + &versionInfo{10, M, 26, 4, 43, 1, 44}, + &versionInfo{10, Q, 24, 6, 19, 2, 20}, + &versionInfo{10, H, 28, 6, 15, 2, 16}, + &versionInfo{11, L, 20, 4, 81, 0, 0}, + &versionInfo{11, M, 30, 1, 50, 4, 51}, + &versionInfo{11, Q, 28, 4, 22, 4, 23}, + &versionInfo{11, H, 24, 3, 12, 8, 13}, + &versionInfo{12, L, 24, 2, 92, 2, 93}, + &versionInfo{12, M, 22, 6, 36, 2, 37}, + &versionInfo{12, Q, 26, 4, 20, 6, 21}, + &versionInfo{12, H, 28, 7, 14, 4, 15}, + &versionInfo{13, L, 26, 4, 107, 0, 0}, + &versionInfo{13, M, 22, 8, 37, 1, 38}, + &versionInfo{13, Q, 24, 8, 20, 4, 21}, + &versionInfo{13, H, 22, 12, 11, 4, 12}, + &versionInfo{14, L, 30, 3, 115, 1, 116}, + &versionInfo{14, M, 24, 4, 40, 5, 41}, + &versionInfo{14, Q, 20, 11, 16, 5, 17}, + &versionInfo{14, H, 24, 11, 12, 5, 13}, + &versionInfo{15, L, 22, 5, 87, 1, 88}, + &versionInfo{15, M, 24, 5, 41, 5, 42}, + &versionInfo{15, Q, 30, 5, 24, 7, 25}, + &versionInfo{15, H, 24, 11, 12, 7, 13}, + &versionInfo{16, L, 24, 5, 98, 1, 99}, + &versionInfo{16, M, 28, 7, 45, 3, 46}, + &versionInfo{16, Q, 24, 15, 19, 2, 20}, + &versionInfo{16, H, 30, 3, 15, 13, 16}, + &versionInfo{17, L, 28, 1, 107, 5, 108}, + &versionInfo{17, M, 28, 10, 46, 1, 47}, + &versionInfo{17, Q, 28, 1, 22, 15, 23}, + &versionInfo{17, H, 28, 2, 14, 17, 15}, + &versionInfo{18, L, 30, 5, 120, 1, 121}, + &versionInfo{18, M, 26, 9, 43, 4, 44}, + &versionInfo{18, Q, 28, 17, 22, 1, 23}, + &versionInfo{18, H, 28, 2, 14, 19, 15}, + &versionInfo{19, L, 28, 3, 113, 4, 114}, + &versionInfo{19, M, 26, 3, 44, 11, 45}, + &versionInfo{19, Q, 26, 17, 21, 4, 22}, + &versionInfo{19, H, 26, 9, 13, 16, 14}, + &versionInfo{20, L, 28, 3, 107, 5, 108}, + &versionInfo{20, M, 26, 3, 41, 13, 42}, + &versionInfo{20, Q, 30, 15, 24, 5, 25}, + &versionInfo{20, H, 28, 15, 15, 10, 16}, + &versionInfo{21, L, 28, 4, 116, 4, 117}, + &versionInfo{21, M, 26, 17, 42, 0, 0}, + &versionInfo{21, Q, 28, 17, 22, 6, 23}, + &versionInfo{21, H, 30, 19, 16, 6, 17}, + &versionInfo{22, L, 28, 2, 111, 7, 112}, + &versionInfo{22, M, 28, 17, 46, 0, 0}, + &versionInfo{22, Q, 30, 7, 24, 16, 25}, + &versionInfo{22, H, 24, 34, 13, 0, 0}, + &versionInfo{23, L, 30, 4, 121, 5, 122}, + &versionInfo{23, M, 28, 4, 47, 14, 48}, + &versionInfo{23, Q, 30, 11, 24, 14, 25}, + &versionInfo{23, H, 30, 16, 15, 14, 16}, + &versionInfo{24, L, 30, 6, 117, 4, 118}, + &versionInfo{24, M, 28, 6, 45, 14, 46}, + &versionInfo{24, Q, 30, 11, 24, 16, 25}, + &versionInfo{24, H, 30, 30, 16, 2, 17}, + &versionInfo{25, L, 26, 8, 106, 4, 107}, + &versionInfo{25, M, 28, 8, 47, 13, 48}, + &versionInfo{25, Q, 30, 7, 24, 22, 25}, + &versionInfo{25, H, 30, 22, 15, 13, 16}, + &versionInfo{26, L, 28, 10, 114, 2, 115}, + &versionInfo{26, M, 28, 19, 46, 4, 47}, + &versionInfo{26, Q, 28, 28, 22, 6, 23}, + &versionInfo{26, H, 30, 33, 16, 4, 17}, + &versionInfo{27, L, 30, 8, 122, 4, 123}, + &versionInfo{27, M, 28, 22, 45, 3, 46}, + &versionInfo{27, Q, 30, 8, 23, 26, 24}, + &versionInfo{27, H, 30, 12, 15, 28, 16}, + &versionInfo{28, L, 30, 3, 117, 10, 118}, + &versionInfo{28, M, 28, 3, 45, 23, 46}, + &versionInfo{28, Q, 30, 4, 24, 31, 25}, + &versionInfo{28, H, 30, 11, 15, 31, 16}, + &versionInfo{29, L, 30, 7, 116, 7, 117}, + &versionInfo{29, M, 28, 21, 45, 7, 46}, + &versionInfo{29, Q, 30, 1, 23, 37, 24}, + &versionInfo{29, H, 30, 19, 15, 26, 16}, + &versionInfo{30, L, 30, 5, 115, 10, 116}, + &versionInfo{30, M, 28, 19, 47, 10, 48}, + &versionInfo{30, Q, 30, 15, 24, 25, 25}, + &versionInfo{30, H, 30, 23, 15, 25, 16}, + &versionInfo{31, L, 30, 13, 115, 3, 116}, + &versionInfo{31, M, 28, 2, 46, 29, 47}, + &versionInfo{31, Q, 30, 42, 24, 1, 25}, + &versionInfo{31, H, 30, 23, 15, 28, 16}, + &versionInfo{32, L, 30, 17, 115, 0, 0}, + &versionInfo{32, M, 28, 10, 46, 23, 47}, + &versionInfo{32, Q, 30, 10, 24, 35, 25}, + &versionInfo{32, H, 30, 19, 15, 35, 16}, + &versionInfo{33, L, 30, 17, 115, 1, 116}, + &versionInfo{33, M, 28, 14, 46, 21, 47}, + &versionInfo{33, Q, 30, 29, 24, 19, 25}, + &versionInfo{33, H, 30, 11, 15, 46, 16}, + &versionInfo{34, L, 30, 13, 115, 6, 116}, + &versionInfo{34, M, 28, 14, 46, 23, 47}, + &versionInfo{34, Q, 30, 44, 24, 7, 25}, + &versionInfo{34, H, 30, 59, 16, 1, 17}, + &versionInfo{35, L, 30, 12, 121, 7, 122}, + &versionInfo{35, M, 28, 12, 47, 26, 48}, + &versionInfo{35, Q, 30, 39, 24, 14, 25}, + &versionInfo{35, H, 30, 22, 15, 41, 16}, + &versionInfo{36, L, 30, 6, 121, 14, 122}, + &versionInfo{36, M, 28, 6, 47, 34, 48}, + &versionInfo{36, Q, 30, 46, 24, 10, 25}, + &versionInfo{36, H, 30, 2, 15, 64, 16}, + &versionInfo{37, L, 30, 17, 122, 4, 123}, + &versionInfo{37, M, 28, 29, 46, 14, 47}, + &versionInfo{37, Q, 30, 49, 24, 10, 25}, + &versionInfo{37, H, 30, 24, 15, 46, 16}, + &versionInfo{38, L, 30, 4, 122, 18, 123}, + &versionInfo{38, M, 28, 13, 46, 32, 47}, + &versionInfo{38, Q, 30, 48, 24, 14, 25}, + &versionInfo{38, H, 30, 42, 15, 32, 16}, + &versionInfo{39, L, 30, 20, 117, 4, 118}, + &versionInfo{39, M, 28, 40, 47, 7, 48}, + &versionInfo{39, Q, 30, 43, 24, 22, 25}, + &versionInfo{39, H, 30, 10, 15, 67, 16}, + &versionInfo{40, L, 30, 19, 118, 6, 119}, + &versionInfo{40, M, 28, 18, 47, 31, 48}, + &versionInfo{40, Q, 30, 34, 24, 34, 25}, + &versionInfo{40, H, 30, 20, 15, 61, 16}, +} + +func (vi *versionInfo) totalDataBytes() int { + g1Data := int(vi.NumberOfBlocksInGroup1) * int(vi.DataCodeWordsPerBlockInGroup1) + g2Data := int(vi.NumberOfBlocksInGroup2) * int(vi.DataCodeWordsPerBlockInGroup2) + return (g1Data + g2Data) +} + +func (vi *versionInfo) charCountBits(m encodingMode) byte { + switch m { + case numericMode: + if vi.Version < 10 { + return 10 + } else if vi.Version < 27 { + return 12 + } + return 14 + + case alphaNumericMode: + if vi.Version < 10 { + return 9 + } else if vi.Version < 27 { + return 11 + } + return 13 + + case byteMode: + if vi.Version < 10 { + return 8 + } + return 16 + + case kanjiMode: + if vi.Version < 10 { + return 8 + } else if vi.Version < 27 { + return 10 + } + return 12 + default: + return 0 + } +} + +func (vi *versionInfo) modulWidth() int { + return ((int(vi.Version) - 1) * 4) + 21 +} + +func (vi *versionInfo) alignmentPatternPlacements() []int { + if vi.Version == 1 { + return make([]int, 0) + } + + first := 6 + last := vi.modulWidth() - 7 + space := float64(last - first) + count := int(math.Ceil(space/28)) + 1 + + result := make([]int, count) + result[0] = first + result[len(result)-1] = last + if count > 2 { + step := int(math.Ceil(float64(last-first) / float64(count-1))) + if step%2 == 1 { + frac := float64(last-first) / float64(count-1) + _, x := math.Modf(frac) + if x >= 0.5 { + frac = math.Ceil(frac) + } else { + frac = math.Floor(frac) + } + + if int(frac)%2 == 0 { + step-- + } else { + step++ + } + } + + for i := 1; i <= count-2; i++ { + result[i] = last - (step * (count - 1 - i)) + } + } + + return result +} + +func findSmallestVersionInfo(ecl ErrorCorrectionLevel, mode encodingMode, dataBits int) *versionInfo { + dataBits = dataBits + 4 // mode indicator + for _, vi := range versionInfos { + if vi.Level == ecl { + if (vi.totalDataBytes() * 8) >= (dataBits + int(vi.charCountBits(mode))) { + return vi + } + } + } + return nil +} diff --git a/vendor/github.com/boombuler/barcode/scaledbarcode.go b/vendor/github.com/boombuler/barcode/scaledbarcode.go new file mode 100644 index 00000000..152b1801 --- /dev/null +++ b/vendor/github.com/boombuler/barcode/scaledbarcode.go @@ -0,0 +1,134 @@ +package barcode + +import ( + "errors" + "fmt" + "image" + "image/color" + "math" +) + +type wrapFunc func(x, y int) color.Color + +type scaledBarcode struct { + wrapped Barcode + wrapperFunc wrapFunc + rect image.Rectangle +} + +type intCSscaledBC struct { + scaledBarcode +} + +func (bc *scaledBarcode) Content() string { + return bc.wrapped.Content() +} + +func (bc *scaledBarcode) Metadata() Metadata { + return bc.wrapped.Metadata() +} + +func (bc *scaledBarcode) ColorModel() color.Model { + return bc.wrapped.ColorModel() +} + +func (bc *scaledBarcode) Bounds() image.Rectangle { + return bc.rect +} + +func (bc *scaledBarcode) At(x, y int) color.Color { + return bc.wrapperFunc(x, y) +} + +func (bc *intCSscaledBC) CheckSum() int { + if cs, ok := bc.wrapped.(BarcodeIntCS); ok { + return cs.CheckSum() + } + return 0 +} + +// Scale returns a resized barcode with the given width and height. +func Scale(bc Barcode, width, height int) (Barcode, error) { + switch bc.Metadata().Dimensions { + case 1: + return scale1DCode(bc, width, height) + case 2: + return scale2DCode(bc, width, height) + } + + return nil, errors.New("unsupported barcode format") +} + +func newScaledBC(wrapped Barcode, wrapperFunc wrapFunc, rect image.Rectangle) Barcode { + result := &scaledBarcode{ + wrapped: wrapped, + wrapperFunc: wrapperFunc, + rect: rect, + } + + if _, ok := wrapped.(BarcodeIntCS); ok { + return &intCSscaledBC{*result} + } + return result +} + +func scale2DCode(bc Barcode, width, height int) (Barcode, error) { + orgBounds := bc.Bounds() + orgWidth := orgBounds.Max.X - orgBounds.Min.X + orgHeight := orgBounds.Max.Y - orgBounds.Min.Y + + factor := int(math.Min(float64(width)/float64(orgWidth), float64(height)/float64(orgHeight))) + if factor <= 0 { + return nil, fmt.Errorf("can not scale barcode to an image smaller than %dx%d", orgWidth, orgHeight) + } + + offsetX := (width - (orgWidth * factor)) / 2 + offsetY := (height - (orgHeight * factor)) / 2 + + wrap := func(x, y int) color.Color { + if x < offsetX || y < offsetY { + return color.White + } + x = (x - offsetX) / factor + y = (y - offsetY) / factor + if x >= orgWidth || y >= orgHeight { + return color.White + } + return bc.At(x, y) + } + + return newScaledBC( + bc, + wrap, + image.Rect(0, 0, width, height), + ), nil +} + +func scale1DCode(bc Barcode, width, height int) (Barcode, error) { + orgBounds := bc.Bounds() + orgWidth := orgBounds.Max.X - orgBounds.Min.X + factor := int(float64(width) / float64(orgWidth)) + + if factor <= 0 { + return nil, fmt.Errorf("can not scale barcode to an image smaller than %dx1", orgWidth) + } + offsetX := (width - (orgWidth * factor)) / 2 + + wrap := func(x, y int) color.Color { + if x < offsetX { + return color.White + } + x = (x - offsetX) / factor + + if x >= orgWidth { + return color.White + } + return bc.At(x, 0) + } + + return newScaledBC( + bc, + wrap, + image.Rect(0, 0, width, height), + ), nil +} diff --git a/vendor/github.com/boombuler/barcode/twooffive/encoder.go b/vendor/github.com/boombuler/barcode/twooffive/encoder.go new file mode 100644 index 00000000..8ad0946a --- /dev/null +++ b/vendor/github.com/boombuler/barcode/twooffive/encoder.go @@ -0,0 +1,138 @@ +// Package twooffive can create interleaved and standard "2 of 5" barcodes. +package twooffive + +import ( + "errors" + "fmt" + + "github.com/boombuler/barcode" + "github.com/boombuler/barcode/utils" +) + +const patternWidth = 5 + +type pattern [patternWidth]bool +type encodeInfo struct { + start []bool + end []bool + widths map[bool]int +} + +var ( + encodingTable = map[rune]pattern{ + '0': pattern{false, false, true, true, false}, + '1': pattern{true, false, false, false, true}, + '2': pattern{false, true, false, false, true}, + '3': pattern{true, true, false, false, false}, + '4': pattern{false, false, true, false, true}, + '5': pattern{true, false, true, false, false}, + '6': pattern{false, true, true, false, false}, + '7': pattern{false, false, false, true, true}, + '8': pattern{true, false, false, true, false}, + '9': pattern{false, true, false, true, false}, + } + + modes = map[bool]encodeInfo{ + false: encodeInfo{ // non-interleaved + start: []bool{true, true, false, true, true, false, true, false}, + end: []bool{true, true, false, true, false, true, true}, + widths: map[bool]int{ + true: 3, + false: 1, + }, + }, + true: encodeInfo{ // interleaved + start: []bool{true, false, true, false}, + end: []bool{true, true, false, true}, + widths: map[bool]int{ + true: 3, + false: 1, + }, + }, + } + nonInterleavedSpace = pattern{false, false, false, false, false} +) + +// AddCheckSum calculates the correct check-digit and appends it to the given content. +func AddCheckSum(content string) (string, error) { + if content == "" { + return "", errors.New("content is empty") + } + + even := len(content)%2 == 1 + sum := 0 + for _, r := range content { + if _, ok := encodingTable[r]; ok { + value := utils.RuneToInt(r) + if even { + sum += value * 3 + } else { + sum += value + } + even = !even + } else { + return "", fmt.Errorf("can not encode \"%s\"", content) + } + } + + return content + string(utils.IntToRune(sum%10)), nil +} + +// Encode creates a codabar barcode for the given content +func Encode(content string, interleaved bool) (barcode.Barcode, error) { + if content == "" { + return nil, errors.New("content is empty") + } + + if interleaved && len(content)%2 == 1 { + return nil, errors.New("can only encode even number of digits in interleaved mode") + } + + mode := modes[interleaved] + resBits := new(utils.BitList) + resBits.AddBit(mode.start...) + + var lastRune *rune + for _, r := range content { + var a, b pattern + if interleaved { + if lastRune == nil { + lastRune = new(rune) + *lastRune = r + continue + } else { + var o1, o2 bool + a, o1 = encodingTable[*lastRune] + b, o2 = encodingTable[r] + if !o1 || !o2 { + return nil, fmt.Errorf("can not encode \"%s\"", content) + } + lastRune = nil + } + } else { + var ok bool + a, ok = encodingTable[r] + if !ok { + return nil, fmt.Errorf("can not encode \"%s\"", content) + } + b = nonInterleavedSpace + } + + for i := 0; i < patternWidth; i++ { + for x := 0; x < mode.widths[a[i]]; x++ { + resBits.AddBit(true) + } + for x := 0; x < mode.widths[b[i]]; x++ { + resBits.AddBit(false) + } + } + } + + resBits.AddBit(mode.end...) + + if interleaved { + return utils.New1DCode(barcode.Type2of5Interleaved, content, resBits), nil + } else { + return utils.New1DCode(barcode.Type2of5, content, resBits), nil + } +} diff --git a/vendor/github.com/boombuler/barcode/utils/base1dcode.go b/vendor/github.com/boombuler/barcode/utils/base1dcode.go new file mode 100644 index 00000000..75e50048 --- /dev/null +++ b/vendor/github.com/boombuler/barcode/utils/base1dcode.go @@ -0,0 +1,57 @@ +// Package utils contain some utilities which are needed to create barcodes +package utils + +import ( + "image" + "image/color" + + "github.com/boombuler/barcode" +) + +type base1DCode struct { + *BitList + kind string + content string +} + +type base1DCodeIntCS struct { + base1DCode + checksum int +} + +func (c *base1DCode) Content() string { + return c.content +} + +func (c *base1DCode) Metadata() barcode.Metadata { + return barcode.Metadata{c.kind, 1} +} + +func (c *base1DCode) ColorModel() color.Model { + return color.Gray16Model +} + +func (c *base1DCode) Bounds() image.Rectangle { + return image.Rect(0, 0, c.Len(), 1) +} + +func (c *base1DCode) At(x, y int) color.Color { + if c.GetBit(x) { + return color.Black + } + return color.White +} + +func (c *base1DCodeIntCS) CheckSum() int { + return c.checksum +} + +// New1DCode creates a new 1D barcode where the bars are represented by the bits in the bars BitList +func New1DCodeIntCheckSum(codeKind, content string, bars *BitList, checksum int) barcode.BarcodeIntCS { + return &base1DCodeIntCS{base1DCode{bars, codeKind, content}, checksum} +} + +// New1DCode creates a new 1D barcode where the bars are represented by the bits in the bars BitList +func New1DCode(codeKind, content string, bars *BitList) barcode.Barcode { + return &base1DCode{bars, codeKind, content} +} diff --git a/vendor/github.com/boombuler/barcode/utils/bitlist.go b/vendor/github.com/boombuler/barcode/utils/bitlist.go new file mode 100644 index 00000000..bb05e53b --- /dev/null +++ b/vendor/github.com/boombuler/barcode/utils/bitlist.go @@ -0,0 +1,119 @@ +package utils + +// BitList is a list that contains bits +type BitList struct { + count int + data []int32 +} + +// NewBitList returns a new BitList with the given length +// all bits are initialize with false +func NewBitList(capacity int) *BitList { + bl := new(BitList) + bl.count = capacity + x := 0 + if capacity%32 != 0 { + x = 1 + } + bl.data = make([]int32, capacity/32+x) + return bl +} + +// Len returns the number of contained bits +func (bl *BitList) Len() int { + return bl.count +} + +func (bl *BitList) grow() { + growBy := len(bl.data) + if growBy < 128 { + growBy = 128 + } else if growBy >= 1024 { + growBy = 1024 + } + + nd := make([]int32, len(bl.data)+growBy) + copy(nd, bl.data) + bl.data = nd +} + +// AddBit appends the given bits to the end of the list +func (bl *BitList) AddBit(bits ...bool) { + for _, bit := range bits { + itmIndex := bl.count / 32 + for itmIndex >= len(bl.data) { + bl.grow() + } + bl.SetBit(bl.count, bit) + bl.count++ + } +} + +// SetBit sets the bit at the given index to the given value +func (bl *BitList) SetBit(index int, value bool) { + itmIndex := index / 32 + itmBitShift := 31 - (index % 32) + if value { + bl.data[itmIndex] = bl.data[itmIndex] | 1<> uint(itmBitShift)) & 1) == 1 +} + +// AddByte appends all 8 bits of the given byte to the end of the list +func (bl *BitList) AddByte(b byte) { + for i := 7; i >= 0; i-- { + bl.AddBit(((b >> uint(i)) & 1) == 1) + } +} + +// AddBits appends the last (LSB) 'count' bits of 'b' the the end of the list +func (bl *BitList) AddBits(b int, count byte) { + for i := int(count) - 1; i >= 0; i-- { + bl.AddBit(((b >> uint(i)) & 1) == 1) + } +} + +// GetBytes returns all bits of the BitList as a []byte +func (bl *BitList) GetBytes() []byte { + len := bl.count >> 3 + if (bl.count % 8) != 0 { + len++ + } + result := make([]byte, len) + for i := 0; i < len; i++ { + shift := (3 - (i % 4)) * 8 + result[i] = (byte)((bl.data[i/4] >> uint(shift)) & 0xFF) + } + return result +} + +// IterateBytes iterates through all bytes contained in the BitList +func (bl *BitList) IterateBytes() <-chan byte { + res := make(chan byte) + + go func() { + c := bl.count + shift := 24 + i := 0 + for c > 0 { + res <- byte((bl.data[i] >> uint(shift)) & 0xFF) + shift -= 8 + if shift < 0 { + shift = 24 + i++ + } + c -= 8 + } + close(res) + }() + + return res +} diff --git a/vendor/github.com/boombuler/barcode/utils/galoisfield.go b/vendor/github.com/boombuler/barcode/utils/galoisfield.go new file mode 100644 index 00000000..68726fbf --- /dev/null +++ b/vendor/github.com/boombuler/barcode/utils/galoisfield.go @@ -0,0 +1,65 @@ +package utils + +// GaloisField encapsulates galois field arithmetics +type GaloisField struct { + Size int + Base int + ALogTbl []int + LogTbl []int +} + +// NewGaloisField creates a new galois field +func NewGaloisField(pp, fieldSize, b int) *GaloisField { + result := new(GaloisField) + + result.Size = fieldSize + result.Base = b + result.ALogTbl = make([]int, fieldSize) + result.LogTbl = make([]int, fieldSize) + + x := 1 + for i := 0; i < fieldSize; i++ { + result.ALogTbl[i] = x + x = x * 2 + if x >= fieldSize { + x = (x ^ pp) & (fieldSize - 1) + } + } + + for i := 0; i < fieldSize; i++ { + result.LogTbl[result.ALogTbl[i]] = int(i) + } + + return result +} + +func (gf *GaloisField) Zero() *GFPoly { + return NewGFPoly(gf, []int{0}) +} + +// AddOrSub add or substract two numbers +func (gf *GaloisField) AddOrSub(a, b int) int { + return a ^ b +} + +// Multiply multiplys two numbers +func (gf *GaloisField) Multiply(a, b int) int { + if a == 0 || b == 0 { + return 0 + } + return gf.ALogTbl[(gf.LogTbl[a]+gf.LogTbl[b])%(gf.Size-1)] +} + +// Divide divides two numbers +func (gf *GaloisField) Divide(a, b int) int { + if b == 0 { + panic("divide by zero") + } else if a == 0 { + return 0 + } + return gf.ALogTbl[(gf.LogTbl[a]-gf.LogTbl[b])%(gf.Size-1)] +} + +func (gf *GaloisField) Invers(num int) int { + return gf.ALogTbl[(gf.Size-1)-gf.LogTbl[num]] +} diff --git a/vendor/github.com/boombuler/barcode/utils/gfpoly.go b/vendor/github.com/boombuler/barcode/utils/gfpoly.go new file mode 100644 index 00000000..c56bb40b --- /dev/null +++ b/vendor/github.com/boombuler/barcode/utils/gfpoly.go @@ -0,0 +1,103 @@ +package utils + +type GFPoly struct { + gf *GaloisField + Coefficients []int +} + +func (gp *GFPoly) Degree() int { + return len(gp.Coefficients) - 1 +} + +func (gp *GFPoly) Zero() bool { + return gp.Coefficients[0] == 0 +} + +// GetCoefficient returns the coefficient of x ^ degree +func (gp *GFPoly) GetCoefficient(degree int) int { + return gp.Coefficients[gp.Degree()-degree] +} + +func (gp *GFPoly) AddOrSubstract(other *GFPoly) *GFPoly { + if gp.Zero() { + return other + } else if other.Zero() { + return gp + } + smallCoeff := gp.Coefficients + largeCoeff := other.Coefficients + if len(smallCoeff) > len(largeCoeff) { + largeCoeff, smallCoeff = smallCoeff, largeCoeff + } + sumDiff := make([]int, len(largeCoeff)) + lenDiff := len(largeCoeff) - len(smallCoeff) + copy(sumDiff, largeCoeff[:lenDiff]) + for i := lenDiff; i < len(largeCoeff); i++ { + sumDiff[i] = int(gp.gf.AddOrSub(int(smallCoeff[i-lenDiff]), int(largeCoeff[i]))) + } + return NewGFPoly(gp.gf, sumDiff) +} + +func (gp *GFPoly) MultByMonominal(degree int, coeff int) *GFPoly { + if coeff == 0 { + return gp.gf.Zero() + } + size := len(gp.Coefficients) + result := make([]int, size+degree) + for i := 0; i < size; i++ { + result[i] = int(gp.gf.Multiply(int(gp.Coefficients[i]), int(coeff))) + } + return NewGFPoly(gp.gf, result) +} + +func (gp *GFPoly) Multiply(other *GFPoly) *GFPoly { + if gp.Zero() || other.Zero() { + return gp.gf.Zero() + } + aCoeff := gp.Coefficients + aLen := len(aCoeff) + bCoeff := other.Coefficients + bLen := len(bCoeff) + product := make([]int, aLen+bLen-1) + for i := 0; i < aLen; i++ { + ac := int(aCoeff[i]) + for j := 0; j < bLen; j++ { + bc := int(bCoeff[j]) + product[i+j] = int(gp.gf.AddOrSub(int(product[i+j]), gp.gf.Multiply(ac, bc))) + } + } + return NewGFPoly(gp.gf, product) +} + +func (gp *GFPoly) Divide(other *GFPoly) (quotient *GFPoly, remainder *GFPoly) { + quotient = gp.gf.Zero() + remainder = gp + fld := gp.gf + denomLeadTerm := other.GetCoefficient(other.Degree()) + inversDenomLeadTerm := fld.Invers(int(denomLeadTerm)) + for remainder.Degree() >= other.Degree() && !remainder.Zero() { + degreeDiff := remainder.Degree() - other.Degree() + scale := int(fld.Multiply(int(remainder.GetCoefficient(remainder.Degree())), inversDenomLeadTerm)) + term := other.MultByMonominal(degreeDiff, scale) + itQuot := NewMonominalPoly(fld, degreeDiff, scale) + quotient = quotient.AddOrSubstract(itQuot) + remainder = remainder.AddOrSubstract(term) + } + return +} + +func NewMonominalPoly(field *GaloisField, degree int, coeff int) *GFPoly { + if coeff == 0 { + return field.Zero() + } + result := make([]int, degree+1) + result[0] = coeff + return NewGFPoly(field, result) +} + +func NewGFPoly(field *GaloisField, coefficients []int) *GFPoly { + for len(coefficients) > 1 && coefficients[0] == 0 { + coefficients = coefficients[1:] + } + return &GFPoly{field, coefficients} +} diff --git a/vendor/github.com/boombuler/barcode/utils/reedsolomon.go b/vendor/github.com/boombuler/barcode/utils/reedsolomon.go new file mode 100644 index 00000000..53af91ad --- /dev/null +++ b/vendor/github.com/boombuler/barcode/utils/reedsolomon.go @@ -0,0 +1,44 @@ +package utils + +import ( + "sync" +) + +type ReedSolomonEncoder struct { + gf *GaloisField + polynomes []*GFPoly + m *sync.Mutex +} + +func NewReedSolomonEncoder(gf *GaloisField) *ReedSolomonEncoder { + return &ReedSolomonEncoder{ + gf, []*GFPoly{NewGFPoly(gf, []int{1})}, new(sync.Mutex), + } +} + +func (rs *ReedSolomonEncoder) getPolynomial(degree int) *GFPoly { + rs.m.Lock() + defer rs.m.Unlock() + + if degree >= len(rs.polynomes) { + last := rs.polynomes[len(rs.polynomes)-1] + for d := len(rs.polynomes); d <= degree; d++ { + next := last.Multiply(NewGFPoly(rs.gf, []int{1, rs.gf.ALogTbl[d-1+rs.gf.Base]})) + rs.polynomes = append(rs.polynomes, next) + last = next + } + } + return rs.polynomes[degree] +} + +func (rs *ReedSolomonEncoder) Encode(data []int, eccCount int) []int { + generator := rs.getPolynomial(eccCount) + info := NewGFPoly(rs.gf, data) + info = info.MultByMonominal(eccCount, 1) + _, remainder := info.Divide(generator) + + result := make([]int, eccCount) + numZero := int(eccCount) - len(remainder.Coefficients) + copy(result[numZero:], remainder.Coefficients) + return result +} diff --git a/vendor/github.com/boombuler/barcode/utils/runeint.go b/vendor/github.com/boombuler/barcode/utils/runeint.go new file mode 100644 index 00000000..d2e5e61e --- /dev/null +++ b/vendor/github.com/boombuler/barcode/utils/runeint.go @@ -0,0 +1,19 @@ +package utils + +// RuneToInt converts a rune between '0' and '9' to an integer between 0 and 9 +// If the rune is outside of this range -1 is returned. +func RuneToInt(r rune) int { + if r >= '0' && r <= '9' { + return int(r - '0') + } + return -1 +} + +// IntToRune converts a digit 0 - 9 to the rune '0' - '9'. If the given int is outside +// of this range 'F' is returned! +func IntToRune(i int) rune { + if i >= 0 && i <= 9 { + return rune(i + '0') + } + return 'F' +} diff --git a/vendor/github.com/jung-kurt/gofpdf/.gitattribute b/vendor/github.com/jung-kurt/gofpdf/.gitattribute new file mode 100644 index 00000000..d72fd520 --- /dev/null +++ b/vendor/github.com/jung-kurt/gofpdf/.gitattribute @@ -0,0 +1 @@ +*.pdf binary diff --git a/vendor/github.com/jung-kurt/gofpdf/.gitignore b/vendor/github.com/jung-kurt/gofpdf/.gitignore new file mode 100644 index 00000000..87a26976 --- /dev/null +++ b/vendor/github.com/jung-kurt/gofpdf/.gitignore @@ -0,0 +1,22 @@ +*.0 +coverage +font/CalligrapherRegular.json +font/CalligrapherRegular.z +font/Ubuntu-* +internal/files/bin/bin +look +makefont/makefont +open +**/*.out +pdf/*.pdf +pdf.txt +private +*.sublime* +*.swp +**/*.test +.idea/ +doc/body.html +doc/body.md +doc/index.html +doc/index.html.ok +coverage.html diff --git a/vendor/github.com/jung-kurt/gofpdf/.travis.yml b/vendor/github.com/jung-kurt/gofpdf/.travis.yml new file mode 100644 index 00000000..32bd6fde --- /dev/null +++ b/vendor/github.com/jung-kurt/gofpdf/.travis.yml @@ -0,0 +1,14 @@ +language: go + +sudo: false + +go: + - master + +os: + - linux + +script: go test -v + +notifications: + email: false diff --git a/vendor/github.com/jung-kurt/gofpdf/LICENSE b/vendor/github.com/jung-kurt/gofpdf/LICENSE new file mode 100644 index 00000000..946bb79e --- /dev/null +++ b/vendor/github.com/jung-kurt/gofpdf/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Kurt Jung and contributors acknowledged in the documentation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/jung-kurt/gofpdf/Makefile b/vendor/github.com/jung-kurt/gofpdf/Makefile new file mode 100644 index 00000000..90624c5c --- /dev/null +++ b/vendor/github.com/jung-kurt/gofpdf/Makefile @@ -0,0 +1,29 @@ +all : documentation + +documentation : doc/index.html doc.go README.md + +cov : all + go test -v -coverprofile=coverage && go tool cover -html=coverage -o=coverage.html + +check : + golint . + go vet -all . + gofmt -s -l . + goreportcard-cli -v | grep -v cyclomatic + +README.md : doc/document.md + pandoc --read=markdown --write=gfm < $< > $@ + +doc/index.html : doc/document.md doc/html.txt + pandoc --read=markdown --write=html --template=doc/html.txt \ + --metadata pagetitle="GoFPDF Document Generator" < $< > $@ + +doc.go : doc/document.md doc/go.awk + pandoc --read=markdown --write=plain $< | awk --assign=package_name=gofpdf --file=doc/go.awk > $@ + gofmt -s -w $@ + +build : + go build -v + +clean : + rm -f coverage.html coverage doc/index.html doc.go README.md diff --git a/vendor/github.com/jung-kurt/gofpdf/README.md b/vendor/github.com/jung-kurt/gofpdf/README.md new file mode 100644 index 00000000..811bbc30 --- /dev/null +++ b/vendor/github.com/jung-kurt/gofpdf/README.md @@ -0,0 +1,255 @@ +# GoFPDF document generator + +[![MIT +licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/jung-kurt/gofpdf/master/LICENSE) +[![Report](https://goreportcard.com/badge/github.com/jung-kurt/gofpdf)](https://goreportcard.com/report/github.com/jung-kurt/gofpdf) +[![GoDoc](https://img.shields.io/badge/godoc-GoFPDF-blue.svg)](https://godoc.org/github.com/jung-kurt/gofpdf) + +![](https://github.com/jung-kurt/gofpdf/raw/master/image/logo_gofpdf.jpg?raw=true) + +Package gofpdf implements a PDF document generator with high level +support for text, drawing and images. + +## Features + + - UTF-8 support + - Choice of measurement unit, page format and margins + - Page header and footer management + - Automatic page breaks, line breaks, and text justification + - Inclusion of JPEG, PNG, GIF, TIFF and basic path-only SVG images + - Colors, gradients and alpha channel transparency + - Outline bookmarks + - Internal and external links + - TrueType, Type1 and encoding support + - Page compression + - Lines, Bézier curves, arcs, and ellipses + - Rotation, scaling, skewing, translation, and mirroring + - Clipping + - Document protection + - Layers + - Templates + - Barcodes + - Charting facility + - Import PDFs as templates + +gofpdf has no dependencies other than the Go standard library. All tests +pass on Linux, Mac and Windows platforms. + +gofpdf supports UTF-8 TrueType fonts and “right-to-left” languages. Note +that Chinese, Japanese, and Korean characters may not be included in +many general purpose fonts. For these languages, a specialized font (for +example, +[NotoSansSC](https://github.com/jsntn/webfonts/blob/master/NotoSansSC-Regular.ttf) +for simplified Chinese) can be used. + +Also, support is provided to automatically translate UTF-8 runes to code +page encodings for languages that have fewer than 256 glyphs. + +## Installation + +To install the package on your system, run + +``` shell +go get github.com/jung-kurt/gofpdf +``` + +Later, to receive updates, run + +``` shell +go get -u -v github.com/jung-kurt/gofpdf/... +``` + +## Quick Start + +The following Go code generates a simple PDF file. + +``` go +pdf := gofpdf.New("P", "mm", "A4", "") +pdf.AddPage() +pdf.SetFont("Arial", "B", 16) +pdf.Cell(40, 10, "Hello, world") +err := pdf.OutputFileAndClose("hello.pdf") +``` + +See the functions in the +[fpdf\_test.go](https://github.com/jung-kurt/gofpdf/blob/master/fpdf_test.go) +file (shown as examples in this documentation) for more advanced PDF +examples. + +## Errors + +If an error occurs in an Fpdf method, an internal error field is set. +After this occurs, Fpdf method calls typically return without performing +any operations and the error state is retained. This error management +scheme facilitates PDF generation since individual method calls do not +need to be examined for failure; it is generally sufficient to wait +until after `Output()` is called. For the same reason, if an error +occurs in the calling application during PDF generation, it may be +desirable for the application to transfer the error to the Fpdf instance +by calling the `SetError()` method or the `SetErrorf()` method. At any +time during the life cycle of the Fpdf instance, the error state can be +determined with a call to `Ok()` or `Err()`. The error itself can be +retrieved with a call to `Error()`. + +## Conversion Notes + +This package is a relatively straightforward translation from the +original [FPDF](http://www.fpdf.org/) library written in PHP (despite +the caveat in the introduction to [Effective +Go](https://golang.org/doc/effective_go.html)). The API names have been +retained even though the Go idiom would suggest otherwise (for example, +`pdf.GetX()` is used rather than simply `pdf.X()`). The similarity of +the two libraries makes the original FPDF website a good source of +information. It includes a forum and FAQ. + +However, some internal changes have been made. Page content is built up +using buffers (of type bytes.Buffer) rather than repeated string +concatenation. Errors are handled as explained above rather than +panicking. Output is generated through an interface of type io.Writer or +io.WriteCloser. A number of the original PHP methods behave differently +based on the type of the arguments that are passed to them; in these +cases additional methods have been exported to provide similar +functionality. Font definition files are produced in JSON rather than +PHP. + +## Example PDFs + +A side effect of running `go test ./...` is the production of a number +of example PDFs. These can be found in the gofpdf/pdf directory after +the tests complete. + +Please note that these examples run in the context of a test. In order +run an example as a standalone application, you’ll need to examine +[fpdf\_test.go](https://github.com/jung-kurt/gofpdf/blob/master/fpdf_test.go) +for some helper routines, for example `exampleFilename()` and +`summary()`. + +Example PDFs can be compared with reference copies in order to verify +that they have been generated as expected. This comparison will be +performed if a PDF with the same name as the example PDF is placed in +the gofpdf/pdf/reference directory and if the third argument to +`ComparePDFFiles()` in internal/example/example.go is true. (By default +it is false.) The routine that summarizes an example will look for this +file and, if found, will call `ComparePDFFiles()` to check the example +PDF for equality with its reference PDF. If differences exist between +the two files they will be printed to standard output and the test will +fail. If the reference file is missing, the comparison is considered to +succeed. In order to successfully compare two PDFs, the placement of +internal resources must be consistent and the internal creation +timestamps must be the same. To do this, the methods `SetCatalogSort()` +and `SetCreationDate()` need to be called for both files. This is done +automatically for all examples. + +## Nonstandard Fonts + +Nothing special is required to use the standard PDF fonts (courier, +helvetica, times, zapfdingbats) in your documents other than calling +`SetFont()`. + +You should use `AddUTF8Font()` or `AddUTF8FontFromBytes()` to add a +TrueType UTF-8 encoded font. Use `RTL()` and `LTR()` methods switch +between “right-to-left” and “left-to-right” mode. + +In order to use a different non-UTF-8 TrueType or Type1 font, you will +need to generate a font definition file and, if the font will be +embedded into PDFs, a compressed version of the font file. This is done +by calling the MakeFont function or using the included makefont command +line utility. To create the utility, cd into the makefont subdirectory +and run “go build”. This will produce a standalone executable named +makefont. Select the appropriate encoding file from the font +subdirectory and run the command as in the following example. + +``` shell +./makefont --embed --enc=../font/cp1252.map --dst=../font ../font/calligra.ttf +``` + +In your PDF generation code, call `AddFont()` to load the font and, as +with the standard fonts, SetFont() to begin using it. Most examples, +including the package example, demonstrate this method. Good sources of +free, open-source fonts include [Google +Fonts](https://fonts.google.com/) and [DejaVu +Fonts](http://dejavu-fonts.org/). + +## Related Packages + +The [draw2d](https://github.com/llgcode/draw2d) package is a two +dimensional vector graphics library that can generate output in +different forms. It uses gofpdf for its document production mode. + +## Contributing Changes + +gofpdf is a global community effort and you are invited to make it even +better. If you have implemented a new feature or corrected a problem, +please consider contributing your change to the project. A contribution +that does not directly pertain to the core functionality of gofpdf +should be placed in its own directory directly beneath the `contrib` +directory. + +Here are guidelines for making submissions. Your change should + + - be compatible with the MIT License + - be properly documented + - be formatted with `go fmt` + - include an example in + [fpdf\_test.go](https://github.com/jung-kurt/gofpdf/blob/master/fpdf_test.go) + if appropriate + - conform to the standards of [golint](https://github.com/golang/lint) + and [go vet](https://golang.org/cmd/vet/), that is, `golint .` and + `go vet .` should not generate any warnings + - not diminish [test coverage](https://blog.golang.org/cover) + +[Pull requests](https://help.github.com/articles/using-pull-requests/) +are the preferred means of accepting your changes. + +## License + +gofpdf is released under the MIT License. It is copyrighted by Kurt Jung +and the contributors acknowledged below. + +## Acknowledgments + +This package’s code and documentation are closely derived from the +[FPDF](http://www.fpdf.org/) library created by Olivier Plathey, and a +number of font and image resources are copied directly from it. Bruno +Michel has provided valuable assistance with the code. Drawing support +is adapted from the FPDF geometric figures script by David Hernández +Sanz. Transparency support is adapted from the FPDF transparency script +by Martin Hall-May. Support for gradients and clipping is adapted from +FPDF scripts by Andreas Würmser. Support for outline bookmarks is +adapted from Olivier Plathey by Manuel Cornes. Layer support is adapted +from Olivier Plathey. Support for transformations is adapted from the +FPDF transformation script by Moritz Wagner and Andreas Würmser. PDF +protection is adapted from the work of Klemen Vodopivec for the FPDF +product. Lawrence Kesteloot provided code to allow an image’s extent to +be determined prior to placement. Support for vertical alignment within +a cell was provided by Stefan Schroeder. Ivan Daniluk generalized the +font and image loading code to use the Reader interface while +maintaining backward compatibility. Anthony Starks provided code for the +Polygon function. Robert Lillack provided the Beziergon function and +corrected some naming issues with the internal curve function. Claudio +Felber provided implementations for dashed line drawing and generalized +font loading. Stani Michiels provided support for multi-segment path +drawing with smooth line joins, line join styles, enhanced fill modes, +and has helped greatly with package presentation and tests. Templating +is adapted by Marcus Downing from the FPDF\_Tpl library created by Jan +Slabon and Setasign. Jelmer Snoeck contributed packages that generate a +variety of barcodes and help with registering images on the web. Jelmer +Snoek and Guillermo Pascual augmented the basic HTML functionality with +aligned text. Kent Quirk implemented backwards-compatible support for +reading DPI from images that support it, and for setting DPI manually +and then having it properly taken into account when calculating image +size. Paulo Coutinho provided support for static embedded fonts. Dan +Meyers added support for embedded JavaScript. David Fish added a generic +alias-replacement function to enable, among other things, table of +contents functionality. Andy Bakun identified and corrected a problem in +which the internal catalogs were not sorted stably. Paul Montag added +encoding and decoding functionality for templates, including images that +are embedded in templates; this allows templates to be stored +independently of gofpdf. Paul also added support for page boxes used in +printing PDF documents. Wojciech Matusiak added supported for word +spacing. Artem Korotkiy added support of UTF-8 fonts. Dave Barnes added +support for imported objects and templates. + +## Roadmap + + - Improve test coverage as reported by the coverage tool. diff --git a/vendor/github.com/jung-kurt/gofpdf/compare.go b/vendor/github.com/jung-kurt/gofpdf/compare.go new file mode 100644 index 00000000..f0c286e4 --- /dev/null +++ b/vendor/github.com/jung-kurt/gofpdf/compare.go @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2015 Kurt Jung (Gmail: kurt.w.jung) + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package gofpdf + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "sort" +) + +type sortType struct { + length int + less func(int, int) bool + swap func(int, int) +} + +func (s *sortType) Len() int { + return s.length +} + +func (s *sortType) Less(i, j int) bool { + return s.less(i, j) +} + +func (s *sortType) Swap(i, j int) { + s.swap(i, j) +} + +func gensort(Len int, Less func(int, int) bool, Swap func(int, int)) { + sort.Sort(&sortType{length: Len, less: Less, swap: Swap}) +} + +func writeBytes(leadStr string, startPos int, sl []byte) { + var pos, max int + var b byte + fmt.Printf("%s %07x", leadStr, startPos) + max = len(sl) + for pos < max { + fmt.Printf(" ") + for k := 0; k < 8; k++ { + if pos < max { + fmt.Printf(" %02x", sl[pos]) + } else { + fmt.Printf(" ") + } + pos++ + } + } + fmt.Printf(" |") + pos = 0 + for pos < max { + b = sl[pos] + if b < 32 || b >= 128 { + b = '.' + } + fmt.Printf("%c", b) + pos++ + } + fmt.Printf("|\n") +} + +func checkBytes(pos int, sl1, sl2 []byte, printDiff bool) (eq bool) { + eq = bytes.Equal(sl1, sl2) + if !eq && printDiff { + writeBytes("<", pos, sl1) + writeBytes(">", pos, sl2) + } + return +} + +// CompareBytes compares the bytes referred to by sl1 with those referred to by +// sl2. Nil is returned if the buffers are equal, otherwise an error. +func CompareBytes(sl1, sl2 []byte, printDiff bool) (err error) { + var posStart, posEnd, len1, len2, length int + var diffs bool + + len1 = len(sl1) + len2 = len(sl2) + length = len1 + if length > len2 { + length = len2 + } + for posStart < length-1 { + posEnd = posStart + 16 + if posEnd > length { + posEnd = length + } + if !checkBytes(posStart, sl1[posStart:posEnd], sl2[posStart:posEnd], printDiff) { + diffs = true + } + posStart = posEnd + } + if diffs { + err = fmt.Errorf("documents are different") + } + return +} + +// ComparePDFs reads and compares the full contents of the two specified +// readers byte-for-byte. Nil is returned if the buffers are equal, otherwise +// an error. +func ComparePDFs(rdr1, rdr2 io.Reader, printDiff bool) (err error) { + var b1, b2 *bytes.Buffer + _, err = b1.ReadFrom(rdr1) + if err == nil { + _, err = b2.ReadFrom(rdr2) + if err == nil { + err = CompareBytes(b1.Bytes(), b2.Bytes(), printDiff) + } + } + return +} + +// ComparePDFFiles reads and compares the full contents of the two specified +// files byte-for-byte. Nil is returned if the file contents are equal, or if +// the second file is missing, otherwise an error. +func ComparePDFFiles(file1Str, file2Str string, printDiff bool) (err error) { + var sl1, sl2 []byte + sl1, err = ioutil.ReadFile(file1Str) + if err == nil { + sl2, err = ioutil.ReadFile(file2Str) + if err == nil { + err = CompareBytes(sl1, sl2, printDiff) + } else { + // Second file is missing; treat this as success + err = nil + } + } + return +} diff --git a/vendor/github.com/jung-kurt/gofpdf/contrib/barcode/barcode.go b/vendor/github.com/jung-kurt/gofpdf/contrib/barcode/barcode.go new file mode 100644 index 00000000..7e0ac0db --- /dev/null +++ b/vendor/github.com/jung-kurt/gofpdf/contrib/barcode/barcode.go @@ -0,0 +1,302 @@ +// Copyright (c) 2015 Jelmer Snoeck (Gmail: jelmer.snoeck) +// +// Permission to use, copy, modify, and distribute this software for any purpose +// with or without fee is hereby granted, provided that the above copyright notice +// and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +// Package barcode provides helper methods for adding barcodes of different +// types to your pdf document. It relies on the github.com/boombuler/barcode +// package for the barcode creation. +package barcode + +import ( + "bytes" + "errors" + "image/jpeg" + "io" + "strconv" + "sync" + + "github.com/boombuler/barcode" + "github.com/boombuler/barcode/aztec" + "github.com/boombuler/barcode/codabar" + "github.com/boombuler/barcode/code128" + "github.com/boombuler/barcode/code39" + "github.com/boombuler/barcode/datamatrix" + "github.com/boombuler/barcode/ean" + "github.com/boombuler/barcode/qr" + "github.com/boombuler/barcode/twooffive" + "github.com/jung-kurt/gofpdf" + "github.com/ruudk/golang-pdf417" +) + +// barcodes represents the barcodes that have been registered through this +// package. They will later be used to be scaled and put on the page. +// RubenN: made this a struct with a mutex to prevent race condition +var barcodes struct { + sync.Mutex + cache map[string]barcode.Barcode +} + +// barcodePdf is a partial PDF implementation that only implements a subset of +// functions that are required to add the barcode to the PDF. +type barcodePdf interface { + GetConversionRatio() float64 + GetImageInfo(imageStr string) *gofpdf.ImageInfoType + Image(imageNameStr string, x, y, w, h float64, flow bool, tp string, link int, linkStr string) + RegisterImageReader(imgName, tp string, r io.Reader) *gofpdf.ImageInfoType + SetError(err error) +} + +// printBarcode internally prints the scaled or unscaled barcode to the PDF. Used by both +// Barcode() and BarcodeUnscalable(). +func printBarcode(pdf barcodePdf, code string, x, y float64, w, h *float64, flow bool) { + barcodes.Lock() + unscaled, ok := barcodes.cache[code] + barcodes.Unlock() + + if !ok { + err := errors.New("Barcode not found") + pdf.SetError(err) + return + } + + bname := uniqueBarcodeName(code, x, y) + info := pdf.GetImageInfo(bname) + scaleToWidth := unscaled.Bounds().Dx() + scaleToHeight := unscaled.Bounds().Dy() + + if info == nil { + bcode, err := barcode.Scale( + unscaled, + scaleToWidth, + scaleToHeight, + ) + + if err != nil { + pdf.SetError(err) + return + } + + err = registerScaledBarcode(pdf, bname, bcode) + if err != nil { + pdf.SetError(err) + return + } + } + + scaleToWidthF := float64(scaleToWidth) + scaleToHeightF := float64(scaleToHeight) + + if w != nil { + scaleToWidthF = *w + } + if h != nil { + scaleToHeightF = *h + } + + pdf.Image(bname, x, y, scaleToWidthF, scaleToHeightF, flow, "jpg", 0, "") + +} + +// BarcodeUnscalable puts a registered barcode in the current page. +// +// Its arguments work in the same way as that of Barcode(). However, it allows for an unscaled +// barcode in the width and/or height dimensions. This can be useful if you want to prevent +// side effects of upscaling. +func BarcodeUnscalable(pdf barcodePdf, code string, x, y float64, w, h *float64, flow bool) { + printBarcode(pdf, code, x, y, w, h, flow) +} + +// Barcode puts a registered barcode in the current page. +// +// The size should be specified in the units used to create the PDF document. +// If width or height are left unspecfied, the barcode is not scaled in the unspecified dimensions. +// +// Positioning with x, y and flow is inherited from Fpdf.Image(). +func Barcode(pdf barcodePdf, code string, x, y, w, h float64, flow bool) { + printBarcode(pdf, code, x, y, &w, &h, flow) +} + +// GetUnscaledBarcodeDimensions returns the width and height of the +// unscaled barcode associated with the given code. +func GetUnscaledBarcodeDimensions(pdf barcodePdf, code string) (w, h float64) { + barcodes.Lock() + unscaled, ok := barcodes.cache[code] + barcodes.Unlock() + + if !ok { + err := errors.New("Barcode not found") + pdf.SetError(err) + return + } + + return convertFrom96Dpi(pdf, float64(unscaled.Bounds().Dx())), + convertFrom96Dpi(pdf, float64(unscaled.Bounds().Dy())) +} + +// Register registers a barcode but does not put it on the page. Use Barcode() +// with the same code to put the barcode on the PDF page. +func Register(bcode barcode.Barcode) string { + barcodes.Lock() + if len(barcodes.cache) == 0 { + barcodes.cache = make(map[string]barcode.Barcode) + } + + key := barcodeKey(bcode) + barcodes.cache[key] = bcode + barcodes.Unlock() + + return key +} + +// RegisterAztec registers a barcode of type Aztec to the PDF, but not to +// the page. Use Barcode() with the return value to put the barcode on the page. +// code is the string to be encoded. minECCPercent is the error correction percentage. 33 is the default. +// userSpecifiedLayers can be a value between -4 and 32 inclusive. +func RegisterAztec(pdf barcodePdf, code string, minECCPercent int, userSpecifiedLayers int) string { + bcode, err := aztec.Encode([]byte(code), minECCPercent, userSpecifiedLayers) + return registerBarcode(pdf, bcode, err) +} + +// RegisterCodabar registers a barcode of type Codabar to the PDF, but not to +// the page. Use Barcode() with the return value to put the barcode on the page. +func RegisterCodabar(pdf barcodePdf, code string) string { + bcode, err := codabar.Encode(code) + return registerBarcode(pdf, bcode, err) +} + +// RegisterCode128 registers a barcode of type Code128 to the PDF, but not to +// the page. Use Barcode() with the return value to put the barcode on the page. +func RegisterCode128(pdf barcodePdf, code string) string { + bcode, err := code128.Encode(code) + return registerBarcode(pdf, bcode, err) +} + +// RegisterCode39 registers a barcode of type Code39 to the PDF, but not to +// the page. Use Barcode() with the return value to put the barcode on the page. +// +// includeChecksum and fullASCIIMode are inherited from code39.Encode(). +func RegisterCode39(pdf barcodePdf, code string, includeChecksum, fullASCIIMode bool) string { + bcode, err := code39.Encode(code, includeChecksum, fullASCIIMode) + return registerBarcode(pdf, bcode, err) +} + +// RegisterDataMatrix registers a barcode of type DataMatrix to the PDF, but not +// to the page. Use Barcode() with the return value to put the barcode on the +// page. +func RegisterDataMatrix(pdf barcodePdf, code string) string { + bcode, err := datamatrix.Encode(code) + return registerBarcode(pdf, bcode, err) +} + +// RegisterPdf417 registers a barcode of type Pdf417 to the PDF, but not to the +// page. code is the string to be encoded. columns specifies the number of +// barcode columns; this should be a value between 1 and 30 inclusive. +// securityLevel specifies an error correction level between zero and 8 +// inclusive. Barcodes for use with FedEx must set columns to 10 and +// securityLevel to 5. Use Barcode() with the return value to put the barcode +// on the page. +func RegisterPdf417(pdf barcodePdf, code string, columns int, securityLevel int) string { + bcode := pdf417.Encode(code, columns, securityLevel) + return registerBarcode(pdf, bcode, nil) +} + +// RegisterEAN registers a barcode of type EAN to the PDF, but not to the page. +// It will automatically detect if the barcode is EAN8 or EAN13. Use Barcode() +// with the return value to put the barcode on the page. +func RegisterEAN(pdf barcodePdf, code string) string { + bcode, err := ean.Encode(code) + return registerBarcode(pdf, bcode, err) +} + +// RegisterQR registers a barcode of type QR to the PDF, but not to the page. +// Use Barcode() with the return value to put the barcode on the page. +// +// The ErrorCorrectionLevel and Encoding mode are inherited from qr.Encode(). +func RegisterQR(pdf barcodePdf, code string, ecl qr.ErrorCorrectionLevel, mode qr.Encoding) string { + bcode, err := qr.Encode(code, ecl, mode) + return registerBarcode(pdf, bcode, err) +} + +// RegisterTwoOfFive registers a barcode of type TwoOfFive to the PDF, but not +// to the page. Use Barcode() with the return value to put the barcode on the +// page. +// +// The interleaved bool is inherited from twooffive.Encode(). +func RegisterTwoOfFive(pdf barcodePdf, code string, interleaved bool) string { + bcode, err := twooffive.Encode(code, interleaved) + return registerBarcode(pdf, bcode, err) +} + +// registerBarcode registers a barcode internally using the Register() function. +// In case of an error generating the barcode it will not be registered and will +// set an error on the PDF. It will return a unique key for the barcode type and +// content that can be used to put the barcode on the page. +func registerBarcode(pdf barcodePdf, bcode barcode.Barcode, err error) string { + if err != nil { + pdf.SetError(err) + return "" + } + + return Register(bcode) +} + +// uniqueBarcodeName makes sure every barcode has a unique name for its +// dimensions. Scaling a barcode image results in quality loss, which could be +// a problem for barcode readers. +func uniqueBarcodeName(code string, x, y float64) string { + xStr := strconv.FormatFloat(x, 'E', -1, 64) + yStr := strconv.FormatFloat(y, 'E', -1, 64) + + return "barcode-" + code + "-" + xStr + yStr +} + +// barcodeKey combines the code type and code value into a unique identifier for +// a barcode type. This is so that we can store several barcodes with the same +// code but different type in the barcodes map. +func barcodeKey(bcode barcode.Barcode) string { + return bcode.Metadata().CodeKind + bcode.Content() +} + +// registerScaledBarcode registers a barcode with its exact dimensions to the +// PDF but does not put it on the page. Use Fpdf.Image() with the same code to +// add the barcode to the page. +func registerScaledBarcode(pdf barcodePdf, code string, bcode barcode.Barcode) error { + buf := new(bytes.Buffer) + err := jpeg.Encode(buf, bcode, nil) + + if err != nil { + return err + } + + reader := bytes.NewReader(buf.Bytes()) + pdf.RegisterImageReader(code, "jpg", reader) + + return nil +} + +// convertTo96DPI converts the given value, which is based on a 72 DPI value +// like the rest of the PDF document, to a 96 DPI value that is required for +// an Image. +// +// Doing this through the Fpdf.Image() function would mean that it uses a 72 DPI +// value and stretches it to a 96 DPI value. This results in quality loss which +// could be problematic for barcode scanners. +func convertTo96Dpi(pdf barcodePdf, value float64) float64 { + return value * pdf.GetConversionRatio() / 72 * 96 +} + +// convertFrom96Dpi converts the given value, which is based on a 96 DPI value +// required for an Image, to a 72 DPI value like the rest of the PDF document. +func convertFrom96Dpi(pdf barcodePdf, value float64) float64 { + return value / pdf.GetConversionRatio() * 72 / 96 +} diff --git a/vendor/github.com/jung-kurt/gofpdf/def.go b/vendor/github.com/jung-kurt/gofpdf/def.go new file mode 100644 index 00000000..efa573e6 --- /dev/null +++ b/vendor/github.com/jung-kurt/gofpdf/def.go @@ -0,0 +1,731 @@ +/* + * Copyright (c) 2013-2014 Kurt Jung (Gmail: kurt.w.jung) + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package gofpdf + +import ( + "bytes" + "crypto/sha1" + "encoding/gob" + "encoding/json" + "fmt" + "io" + "time" +) + +// Version of FPDF from which this package is derived +const ( + cnFpdfVersion = "1.7" +) + +type blendModeType struct { + strokeStr, fillStr, modeStr string + objNum int +} + +type gradientType struct { + tp int // 2: linear, 3: radial + clr1Str, clr2Str string + x1, y1, x2, y2, r float64 + objNum int +} + +const ( + // OrientationPortrait represents the portrait orientation. + OrientationPortrait = "portrait" + + // OrientationLandscape represents the landscape orientation. + OrientationLandscape = "landscape" +) + +const ( + // UnitPoint represents the size unit point + UnitPoint = "pt" + // UnitMillimeter represents the size unit millimeter + UnitMillimeter = "mm" + // UnitCentimeter represents the size unit centimeter + UnitCentimeter = "cm" + // UnitInch represents the size unit inch + UnitInch = "inch" +) + +const ( + // PageSizeA3 represents DIN/ISO A3 page size + PageSizeA3 = "A3" + // PageSizeA4 represents DIN/ISO A4 page size + PageSizeA4 = "A4" + // PageSizeA5 represents DIN/ISO A5 page size + PageSizeA5 = "A5" + // PageSizeLetter represents US Letter page size + PageSizeLetter = "Letter" + // PageSizeLegal represents US Legal page size + PageSizeLegal = "Legal" +) + +const ( + // BorderNone set no border + BorderNone = "" + // BorderFull sets a full border + BorderFull = "1" + // BorderLeft sets the border on the left side + BorderLeft = "L" + // BorderTop sets the border at the top + BorderTop = "T" + // BorderRight sets the border on the right side + BorderRight = "R" + // BorderBottom sets the border on the bottom + BorderBottom = "B" +) + +const ( + // LineBreakNone disables linebreak + LineBreakNone = 0 + // LineBreakNormal enables normal linebreak + LineBreakNormal = 1 + // LineBreakBelow enables linebreak below + LineBreakBelow = 2 +) + +const ( + // AlignLeft left aligns the cell + AlignLeft = "L" + // AlignRight right aligns the cell + AlignRight = "R" + // AlignCenter centers the cell + AlignCenter = "C" + // AlignTop aligns the cell to the top + AlignTop = "T" + // AlignBottom aligns the cell to the bottom + AlignBottom = "B" + // AlignMiddle aligns the cell to the middle + AlignMiddle = "M" + // AlignBaseline aligns the cell to the baseline + AlignBaseline = "B" +) + +type colorMode int + +const ( + colorModeRGB colorMode = iota + colorModeSpot + colorModeCMYK +) + +type colorType struct { + r, g, b float64 + ir, ig, ib int + mode colorMode + spotStr string // name of current spot color + gray bool + str string +} + +// SpotColorType specifies a named spot color value +type spotColorType struct { + id, objID int + val cmykColorType +} + +// CMYKColorType specifies an ink-based CMYK color value +type cmykColorType struct { + c, m, y, k byte // 0% to 100% +} + +// SizeType fields Wd and Ht specify the horizontal and vertical extents of a +// document element such as a page. +type SizeType struct { + Wd, Ht float64 +} + +// PointType fields X and Y specify the horizontal and vertical coordinates of +// a point, typically used in drawing. +type PointType struct { + X, Y float64 +} + +// XY returns the X and Y components of the receiver point. +func (p PointType) XY() (float64, float64) { + return p.X, p.Y +} + +// ImageInfoType contains size, color and other information about an image. +// Changes to this structure should be reflected in its GobEncode and GobDecode +// methods. +type ImageInfoType struct { + data []byte + smask []byte + n int + w float64 + h float64 + cs string + pal []byte + bpc int + f string + dp string + trns []int + scale float64 // document scaling factor + dpi float64 + i string +} + +func generateImageID(info *ImageInfoType) (string, error) { + b, err := info.GobEncode() + return fmt.Sprintf("%x", sha1.Sum(b)), err +} + +// GobEncode encodes the receiving image to a byte slice. +func (info *ImageInfoType) GobEncode() (buf []byte, err error) { + fields := []interface{}{info.data, info.smask, info.n, info.w, info.h, info.cs, + info.pal, info.bpc, info.f, info.dp, info.trns, info.scale, info.dpi} + w := new(bytes.Buffer) + encoder := gob.NewEncoder(w) + for j := 0; j < len(fields) && err == nil; j++ { + err = encoder.Encode(fields[j]) + } + if err == nil { + buf = w.Bytes() + } + return +} + +// GobDecode decodes the specified byte buffer (generated by GobEncode) into +// the receiving image. +func (info *ImageInfoType) GobDecode(buf []byte) (err error) { + fields := []interface{}{&info.data, &info.smask, &info.n, &info.w, &info.h, + &info.cs, &info.pal, &info.bpc, &info.f, &info.dp, &info.trns, &info.scale, &info.dpi} + r := bytes.NewBuffer(buf) + decoder := gob.NewDecoder(r) + for j := 0; j < len(fields) && err == nil; j++ { + err = decoder.Decode(fields[j]) + } + + info.i, err = generateImageID(info) + return +} + +// PointConvert returns the value of pt, expressed in points (1/72 inch), as a +// value expressed in the unit of measure specified in New(). Since font +// management in Fpdf uses points, this method can help with line height +// calculations and other methods that require user units. +func (f *Fpdf) PointConvert(pt float64) (u float64) { + return pt / f.k +} + +// PointToUnitConvert is an alias for PointConvert. +func (f *Fpdf) PointToUnitConvert(pt float64) (u float64) { + return pt / f.k +} + +// UnitToPointConvert returns the value of u, expressed in the unit of measure +// specified in New(), as a value expressed in points (1/72 inch). Since font +// management in Fpdf uses points, this method can help with setting font sizes +// based on the sizes of other non-font page elements. +func (f *Fpdf) UnitToPointConvert(u float64) (pt float64) { + return u * f.k +} + +// Extent returns the width and height of the image in the units of the Fpdf +// object. +func (info *ImageInfoType) Extent() (wd, ht float64) { + return info.Width(), info.Height() +} + +// Width returns the width of the image in the units of the Fpdf object. +func (info *ImageInfoType) Width() float64 { + return info.w / (info.scale * info.dpi / 72) +} + +// Height returns the height of the image in the units of the Fpdf object. +func (info *ImageInfoType) Height() float64 { + return info.h / (info.scale * info.dpi / 72) +} + +// SetDpi sets the dots per inch for an image. PNG images MAY have their dpi +// set automatically, if the image specifies it. DPI information is not +// currently available automatically for JPG and GIF images, so if it's +// important to you, you can set it here. It defaults to 72 dpi. +func (info *ImageInfoType) SetDpi(dpi float64) { + info.dpi = dpi +} + +type fontFileType struct { + length1, length2 int64 + n int + embedded bool + content []byte + fontType string +} + +type linkType struct { + x, y, wd, ht float64 + link int // Auto-generated internal link ID or... + linkStr string // ...application-provided external link string +} + +type intLinkType struct { + page int + y float64 +} + +// outlineType is used for a sidebar outline of bookmarks +type outlineType struct { + text string + level, parent, first, last, next, prev int + y float64 + p int +} + +// InitType is used with NewCustom() to customize an Fpdf instance. +// OrientationStr, UnitStr, SizeStr and FontDirStr correspond to the arguments +// accepted by New(). If the Wd and Ht fields of Size are each greater than +// zero, Size will be used to set the default page size rather than SizeStr. Wd +// and Ht are specified in the units of measure indicated by UnitStr. +type InitType struct { + OrientationStr string + UnitStr string + SizeStr string + Size SizeType + FontDirStr string +} + +// FontLoader is used to read fonts (JSON font specification and zlib compressed font binaries) +// from arbitrary locations (e.g. files, zip files, embedded font resources). +// +// Open provides an io.Reader for the specified font file (.json or .z). The file name +// never includes a path. Open returns an error if the specified file cannot be opened. +type FontLoader interface { + Open(name string) (io.Reader, error) +} + +// Pdf defines the interface used for various methods. It is implemented by the +// main FPDF instance as well as templates. +type Pdf interface { + AddFont(familyStr, styleStr, fileStr string) + AddFontFromBytes(familyStr, styleStr string, jsonFileBytes, zFileBytes []byte) + AddFontFromReader(familyStr, styleStr string, r io.Reader) + AddLayer(name string, visible bool) (layerID int) + AddLink() int + AddPage() + AddPageFormat(orientationStr string, size SizeType) + AddSpotColor(nameStr string, c, m, y, k byte) + AliasNbPages(aliasStr string) + ArcTo(x, y, rx, ry, degRotate, degStart, degEnd float64) + Arc(x, y, rx, ry, degRotate, degStart, degEnd float64, styleStr string) + BeginLayer(id int) + Beziergon(points []PointType, styleStr string) + Bookmark(txtStr string, level int, y float64) + CellFormat(w, h float64, txtStr, borderStr string, ln int, alignStr string, fill bool, link int, linkStr string) + Cellf(w, h float64, fmtStr string, args ...interface{}) + Cell(w, h float64, txtStr string) + Circle(x, y, r float64, styleStr string) + ClearError() + ClipCircle(x, y, r float64, outline bool) + ClipEllipse(x, y, rx, ry float64, outline bool) + ClipEnd() + ClipPolygon(points []PointType, outline bool) + ClipRect(x, y, w, h float64, outline bool) + ClipRoundedRect(x, y, w, h, r float64, outline bool) + ClipText(x, y float64, txtStr string, outline bool) + Close() + ClosePath() + CreateTemplateCustom(corner PointType, size SizeType, fn func(*Tpl)) Template + CreateTemplate(fn func(*Tpl)) Template + CurveBezierCubicTo(cx0, cy0, cx1, cy1, x, y float64) + CurveBezierCubic(x0, y0, cx0, cy0, cx1, cy1, x1, y1 float64, styleStr string) + CurveCubic(x0, y0, cx0, cy0, x1, y1, cx1, cy1 float64, styleStr string) + CurveTo(cx, cy, x, y float64) + Curve(x0, y0, cx, cy, x1, y1 float64, styleStr string) + DrawPath(styleStr string) + Ellipse(x, y, rx, ry, degRotate float64, styleStr string) + EndLayer() + Err() bool + Error() error + GetAlpha() (alpha float64, blendModeStr string) + GetAutoPageBreak() (auto bool, margin float64) + GetCellMargin() float64 + GetConversionRatio() float64 + GetDrawColor() (int, int, int) + GetDrawSpotColor() (name string, c, m, y, k byte) + GetFillColor() (int, int, int) + GetFillSpotColor() (name string, c, m, y, k byte) + GetFontDesc(familyStr, styleStr string) FontDescType + GetFontSize() (ptSize, unitSize float64) + GetImageInfo(imageStr string) (info *ImageInfoType) + GetLineWidth() float64 + GetMargins() (left, top, right, bottom float64) + GetPageSizeStr(sizeStr string) (size SizeType) + GetPageSize() (width, height float64) + GetStringWidth(s string) float64 + GetTextColor() (int, int, int) + GetTextSpotColor() (name string, c, m, y, k byte) + GetX() float64 + GetXY() (float64, float64) + GetY() float64 + HTMLBasicNew() (html HTMLBasicType) + Image(imageNameStr string, x, y, w, h float64, flow bool, tp string, link int, linkStr string) + ImageOptions(imageNameStr string, x, y, w, h float64, flow bool, options ImageOptions, link int, linkStr string) + ImageTypeFromMime(mimeStr string) (tp string) + LinearGradient(x, y, w, h float64, r1, g1, b1, r2, g2, b2 int, x1, y1, x2, y2 float64) + LineTo(x, y float64) + Line(x1, y1, x2, y2 float64) + LinkString(x, y, w, h float64, linkStr string) + Link(x, y, w, h float64, link int) + Ln(h float64) + MoveTo(x, y float64) + MultiCell(w, h float64, txtStr, borderStr, alignStr string, fill bool) + Ok() bool + OpenLayerPane() + OutputAndClose(w io.WriteCloser) error + OutputFileAndClose(fileStr string) error + Output(w io.Writer) error + PageCount() int + PageNo() int + PageSize(pageNum int) (wd, ht float64, unitStr string) + PointConvert(pt float64) (u float64) + PointToUnitConvert(pt float64) (u float64) + Polygon(points []PointType, styleStr string) + RadialGradient(x, y, w, h float64, r1, g1, b1, r2, g2, b2 int, x1, y1, x2, y2, r float64) + RawWriteBuf(r io.Reader) + RawWriteStr(str string) + Rect(x, y, w, h float64, styleStr string) + RegisterAlias(alias, replacement string) + RegisterImage(fileStr, tp string) (info *ImageInfoType) + RegisterImageOptions(fileStr string, options ImageOptions) (info *ImageInfoType) + RegisterImageOptionsReader(imgName string, options ImageOptions, r io.Reader) (info *ImageInfoType) + RegisterImageReader(imgName, tp string, r io.Reader) (info *ImageInfoType) + SetAcceptPageBreakFunc(fnc func() bool) + SetAlpha(alpha float64, blendModeStr string) + SetAuthor(authorStr string, isUTF8 bool) + SetAutoPageBreak(auto bool, margin float64) + SetCatalogSort(flag bool) + SetCellMargin(margin float64) + SetCompression(compress bool) + SetCreationDate(tm time.Time) + SetCreator(creatorStr string, isUTF8 bool) + SetDashPattern(dashArray []float64, dashPhase float64) + SetDisplayMode(zoomStr, layoutStr string) + SetDrawColor(r, g, b int) + SetDrawSpotColor(nameStr string, tint byte) + SetError(err error) + SetErrorf(fmtStr string, args ...interface{}) + SetFillColor(r, g, b int) + SetFillSpotColor(nameStr string, tint byte) + SetFont(familyStr, styleStr string, size float64) + SetFontLoader(loader FontLoader) + SetFontLocation(fontDirStr string) + SetFontSize(size float64) + SetFontStyle(styleStr string) + SetFontUnitSize(size float64) + SetFooterFunc(fnc func()) + SetFooterFuncLpi(fnc func(lastPage bool)) + SetHeaderFunc(fnc func()) + SetHeaderFuncMode(fnc func(), homeMode bool) + SetHomeXY() + SetJavascript(script string) + SetKeywords(keywordsStr string, isUTF8 bool) + SetLeftMargin(margin float64) + SetLineCapStyle(styleStr string) + SetLineJoinStyle(styleStr string) + SetLineWidth(width float64) + SetLink(link int, y float64, page int) + SetMargins(left, top, right float64) + SetPageBoxRec(t string, pb PageBox) + SetPageBox(t string, x, y, wd, ht float64) + SetPage(pageNum int) + SetProtection(actionFlag byte, userPassStr, ownerPassStr string) + SetRightMargin(margin float64) + SetSubject(subjectStr string, isUTF8 bool) + SetTextColor(r, g, b int) + SetTextSpotColor(nameStr string, tint byte) + SetTitle(titleStr string, isUTF8 bool) + SetTopMargin(margin float64) + SetXmpMetadata(xmpStream []byte) + SetX(x float64) + SetXY(x, y float64) + SetY(y float64) + SplitLines(txt []byte, w float64) [][]byte + String() string + SVGBasicWrite(sb *SVGBasicType, scale float64) + Text(x, y float64, txtStr string) + TransformBegin() + TransformEnd() + TransformMirrorHorizontal(x float64) + TransformMirrorLine(angle, x, y float64) + TransformMirrorPoint(x, y float64) + TransformMirrorVertical(y float64) + TransformRotate(angle, x, y float64) + TransformScale(scaleWd, scaleHt, x, y float64) + TransformScaleX(scaleWd, x, y float64) + TransformScaleXY(s, x, y float64) + TransformScaleY(scaleHt, x, y float64) + TransformSkew(angleX, angleY, x, y float64) + TransformSkewX(angleX, x, y float64) + TransformSkewY(angleY, x, y float64) + Transform(tm TransformMatrix) + TransformTranslate(tx, ty float64) + TransformTranslateX(tx float64) + TransformTranslateY(ty float64) + UnicodeTranslatorFromDescriptor(cpStr string) (rep func(string) string) + UnitToPointConvert(u float64) (pt float64) + UseTemplateScaled(t Template, corner PointType, size SizeType) + UseTemplate(t Template) + WriteAligned(width, lineHeight float64, textStr, alignStr string) + Writef(h float64, fmtStr string, args ...interface{}) + Write(h float64, txtStr string) + WriteLinkID(h float64, displayStr string, linkID int) + WriteLinkString(h float64, displayStr, targetStr string) +} + +// PageBox defines the coordinates and extent of the various page box types +type PageBox struct { + SizeType + PointType +} + +// Fpdf is the principal structure for creating a single PDF document +type Fpdf struct { + isCurrentUTF8 bool // is current font used in utf-8 mode + isRTL bool // is is right to left mode enabled + page int // current page number + n int // current object number + offsets []int // array of object offsets + templates map[string]Template // templates used in this document + templateObjects map[string]int // template object IDs within this document + importedObjs map[string][]byte // imported template objects (gofpdi) + importedObjPos map[string]map[int]string // imported template objects hashes and their positions (gofpdi) + importedTplObjs map[string]string // imported template names and IDs (hashed) (gofpdi) + importedTplIDs map[string]int // imported template ids hash to object id int (gofpdi) + buffer fmtBuffer // buffer holding in-memory PDF + pages []*bytes.Buffer // slice[page] of page content; 1-based + state int // current document state + compress bool // compression flag + k float64 // scale factor (number of points in user unit) + defOrientation string // default orientation + curOrientation string // current orientation + stdPageSizes map[string]SizeType // standard page sizes + defPageSize SizeType // default page size + defPageBoxes map[string]PageBox // default page size + curPageSize SizeType // current page size + pageSizes map[int]SizeType // used for pages with non default sizes or orientations + pageBoxes map[int]map[string]PageBox // used to define the crop, trim, bleed and art boxes + unitStr string // unit of measure for all rendered objects except fonts + wPt, hPt float64 // dimensions of current page in points + w, h float64 // dimensions of current page in user unit + lMargin float64 // left margin + tMargin float64 // top margin + rMargin float64 // right margin + bMargin float64 // page break margin + cMargin float64 // cell margin + x, y float64 // current position in user unit + lasth float64 // height of last printed cell + lineWidth float64 // line width in user unit + fontpath string // path containing fonts + fontLoader FontLoader // used to load font files from arbitrary locations + coreFonts map[string]bool // array of core font names + fonts map[string]fontDefType // array of used fonts + fontFiles map[string]fontFileType // array of font files + diffs []string // array of encoding differences + fontFamily string // current font family + fontStyle string // current font style + underline bool // underlining flag + currentFont fontDefType // current font info + fontSizePt float64 // current font size in points + fontSize float64 // current font size in user unit + ws float64 // word spacing + images map[string]*ImageInfoType // array of used images + aliasMap map[string]string // map of alias->replacement + pageLinks [][]linkType // pageLinks[page][link], both 1-based + links []intLinkType // array of internal links + outlines []outlineType // array of outlines + outlineRoot int // root of outlines + autoPageBreak bool // automatic page breaking + acceptPageBreak func() bool // returns true to accept page break + pageBreakTrigger float64 // threshold used to trigger page breaks + inHeader bool // flag set when processing header + headerFnc func() // function provided by app and called to write header + headerHomeMode bool // set position to home after headerFnc is called + inFooter bool // flag set when processing footer + footerFnc func() // function provided by app and called to write footer + footerFncLpi func(bool) // function provided by app and called to write footer with last page flag + zoomMode string // zoom display mode + layoutMode string // layout display mode + xmp []byte // XMP metadata + title string // title + subject string // subject + author string // author + keywords string // keywords + creator string // creator + creationDate time.Time // override for dcoument CreationDate value + aliasNbPagesStr string // alias for total number of pages + pdfVersion string // PDF version number + fontDirStr string // location of font definition files + capStyle int // line cap style: butt 0, round 1, square 2 + joinStyle int // line segment join style: miter 0, round 1, bevel 2 + dashArray []float64 // dash array + dashPhase float64 // dash phase + blendList []blendModeType // slice[idx] of alpha transparency modes, 1-based + blendMap map[string]int // map into blendList + blendMode string // current blend mode + alpha float64 // current transpacency + gradientList []gradientType // slice[idx] of gradient records + clipNest int // Number of active clipping contexts + transformNest int // Number of active transformation contexts + err error // Set if error occurs during life cycle of instance + protect protectType // document protection structure + layer layerRecType // manages optional layers in document + catalogSort bool // sort resource catalogs in document + nJs int // JavaScript object number + javascript *string // JavaScript code to include in the PDF + colorFlag bool // indicates whether fill and text colors are different + color struct { + // Composite values of colors + draw, fill, text colorType + } + spotColorMap map[string]spotColorType // Map of named ink-based colors +} + +type encType struct { + uv int + name string +} + +type encListType [256]encType + +type fontBoxType struct { + Xmin, Ymin, Xmax, Ymax int +} + +// Font flags for FontDescType.Flags as defined in the pdf specification. +const ( + // FontFlagFixedPitch is set if all glyphs have the same width (as + // opposed to proportional or variable-pitch fonts, which have + // different widths). + FontFlagFixedPitch = 1 << 0 + // FontFlagSerif is set if glyphs have serifs, which are short + // strokes drawn at an angle on the top and bottom of glyph stems. + // (Sans serif fonts do not have serifs.) + FontFlagSerif = 1 << 1 + // FontFlagSymbolic is set if font contains glyphs outside the + // Adobe standard Latin character set. This flag and the + // Nonsymbolic flag shall not both be set or both be clear. + FontFlagSymbolic = 1 << 2 + // FontFlagScript is set if glyphs resemble cursive handwriting. + FontFlagScript = 1 << 3 + // FontFlagNonsymbolic is set if font uses the Adobe standard + // Latin character set or a subset of it. + FontFlagNonsymbolic = 1 << 5 + // FontFlagItalic is set if glyphs have dominant vertical strokes + // that are slanted. + FontFlagItalic = 1 << 6 + // FontFlagAllCap is set if font contains no lowercase letters; + // typically used for display purposes, such as for titles or + // headlines. + FontFlagAllCap = 1 << 16 + // SmallCap is set if font contains both uppercase and lowercase + // letters. The uppercase letters are similar to those in the + // regular version of the same typeface family. The glyphs for the + // lowercase letters have the same shapes as the corresponding + // uppercase letters, but they are sized and their proportions + // adjusted so that they have the same size and stroke weight as + // lowercase glyphs in the same typeface family. + SmallCap = 1 << 18 + // ForceBold determines whether bold glyphs shall be painted with + // extra pixels even at very small text sizes by a conforming + // reader. If the ForceBold flag is set, features of bold glyphs + // may be thickened at small text sizes. + ForceBold = 1 << 18 +) + +// FontDescType (font descriptor) specifies metrics and other +// attributes of a font, as distinct from the metrics of individual +// glyphs (as defined in the pdf specification). +type FontDescType struct { + // The maximum height above the baseline reached by glyphs in this + // font (for example for "S"). The height of glyphs for accented + // characters shall be excluded. + Ascent int + // The maximum depth below the baseline reached by glyphs in this + // font. The value shall be a negative number. + Descent int + // The vertical coordinate of the top of flat capital letters, + // measured from the baseline (for example "H"). + CapHeight int + // A collection of flags defining various characteristics of the + // font. (See the FontFlag* constants.) + Flags int + // A rectangle, expressed in the glyph coordinate system, that + // shall specify the font bounding box. This should be the smallest + // rectangle enclosing the shape that would result if all of the + // glyphs of the font were placed with their origins coincident + // and then filled. + FontBBox fontBoxType + // The angle, expressed in degrees counterclockwise from the + // vertical, of the dominant vertical strokes of the font. (The + // 9-o’clock position is 90 degrees, and the 3-o’clock position + // is –90 degrees.) The value shall be negative for fonts that + // slope to the right, as almost all italic fonts do. + ItalicAngle int + // The thickness, measured horizontally, of the dominant vertical + // stems of glyphs in the font. + StemV int + // The width to use for character codes whose widths are not + // specified in a font dictionary’s Widths array. This shall have + // a predictable effect only if all such codes map to glyphs whose + // actual widths are the same as the value of the MissingWidth + // entry. (Default value: 0.) + MissingWidth int +} + +type fontDefType struct { + Tp string // "Core", "TrueType", ... + Name string // "Courier-Bold", ... + Desc FontDescType // Font descriptor + Up int // Underline position + Ut int // Underline thickness + Cw []int // Character width by ordinal + Enc string // "cp1252", ... + Diff string // Differences from reference encoding + File string // "Redressed.z" + Size1, Size2 int // Type1 values + OriginalSize int // Size of uncompressed font file + N int // Set by font loader + DiffN int // Position of diff in app array, set by font loader + i string // 1-based position in font list, set by font loader, not this program + utf8File *utf8FontFile // UTF-8 font + usedRunes map[int]int // Array of used runes +} + +// generateFontID generates a font Id from the font definition +func generateFontID(fdt fontDefType) (string, error) { + // file can be different if generated in different instance + fdt.File = "" + b, err := json.Marshal(&fdt) + return fmt.Sprintf("%x", sha1.Sum(b)), err +} + +type fontInfoType struct { + Data []byte + File string + OriginalSize int + FontName string + Bold bool + IsFixedPitch bool + UnderlineThickness int + UnderlinePosition int + Widths []int + Size1, Size2 uint32 + Desc FontDescType +} diff --git a/vendor/github.com/jung-kurt/gofpdf/doc.go b/vendor/github.com/jung-kurt/gofpdf/doc.go new file mode 100644 index 00000000..6ad04613 --- /dev/null +++ b/vendor/github.com/jung-kurt/gofpdf/doc.go @@ -0,0 +1,267 @@ +/* +Package gofpdf implements a PDF document generator with high level +support for text, drawing and images. + + +Features + + +- UTF-8 support + +- Choice of measurement unit, page format and margins + +- Page header and footer management + +- Automatic page breaks, line breaks, and text justification + +- Inclusion of JPEG, PNG, GIF, TIFF and basic path-only SVG images + +- Colors, gradients and alpha channel transparency + +- Outline bookmarks + +- Internal and external links + +- TrueType, Type1 and encoding support + +- Page compression + +- Lines, Bézier curves, arcs, and ellipses + +- Rotation, scaling, skewing, translation, and mirroring + +- Clipping + +- Document protection + +- Layers + +- Templates + +- Barcodes + +- Charting facility + +- Import PDFs as templates + +gofpdf has no dependencies other than the Go standard library. All tests +pass on Linux, Mac and Windows platforms. + +gofpdf supports UTF-8 TrueType fonts and “right-to-left” languages. Note +that Chinese, Japanese, and Korean characters may not be included in +many general purpose fonts. For these languages, a specialized font (for +example, NotoSansSC for simplified Chinese) can be used. + +Also, support is provided to automatically translate UTF-8 runes to code +page encodings for languages that have fewer than 256 glyphs. + + +Installation + +To install the package on your system, run + + go get github.com/jung-kurt/gofpdf + +Later, to receive updates, run + + go get -u -v github.com/jung-kurt/gofpdf/... + + +Quick Start + +The following Go code generates a simple PDF file. + + pdf := gofpdf.New("P", "mm", "A4", "") + pdf.AddPage() + pdf.SetFont("Arial", "B", 16) + pdf.Cell(40, 10, "Hello, world") + err := pdf.OutputFileAndClose("hello.pdf") + +See the functions in the fpdf_test.go file (shown as examples in this +documentation) for more advanced PDF examples. + + +Errors + +If an error occurs in an Fpdf method, an internal error field is set. +After this occurs, Fpdf method calls typically return without performing +any operations and the error state is retained. This error management +scheme facilitates PDF generation since individual method calls do not +need to be examined for failure; it is generally sufficient to wait +until after Output() is called. For the same reason, if an error occurs +in the calling application during PDF generation, it may be desirable +for the application to transfer the error to the Fpdf instance by +calling the SetError() method or the SetErrorf() method. At any time +during the life cycle of the Fpdf instance, the error state can be +determined with a call to Ok() or Err(). The error itself can be +retrieved with a call to Error(). + + +Conversion Notes + +This package is a relatively straightforward translation from the +original FPDF library written in PHP (despite the caveat in the +introduction to Effective Go). The API names have been retained even +though the Go idiom would suggest otherwise (for example, pdf.GetX() is +used rather than simply pdf.X()). The similarity of the two libraries +makes the original FPDF website a good source of information. It +includes a forum and FAQ. + +However, some internal changes have been made. Page content is built up +using buffers (of type bytes.Buffer) rather than repeated string +concatenation. Errors are handled as explained above rather than +panicking. Output is generated through an interface of type io.Writer or +io.WriteCloser. A number of the original PHP methods behave differently +based on the type of the arguments that are passed to them; in these +cases additional methods have been exported to provide similar +functionality. Font definition files are produced in JSON rather than +PHP. + + +Example PDFs + +A side effect of running go test ./... is the production of a number of +example PDFs. These can be found in the gofpdf/pdf directory after the +tests complete. + +Please note that these examples run in the context of a test. In order +run an example as a standalone application, you’ll need to examine +fpdf_test.go for some helper routines, for example exampleFilename() and +summary(). + +Example PDFs can be compared with reference copies in order to verify +that they have been generated as expected. This comparison will be +performed if a PDF with the same name as the example PDF is placed in +the gofpdf/pdf/reference directory and if the third argument to +ComparePDFFiles() in internal/example/example.go is true. (By default it +is false.) The routine that summarizes an example will look for this +file and, if found, will call ComparePDFFiles() to check the example PDF +for equality with its reference PDF. If differences exist between the +two files they will be printed to standard output and the test will +fail. If the reference file is missing, the comparison is considered to +succeed. In order to successfully compare two PDFs, the placement of +internal resources must be consistent and the internal creation +timestamps must be the same. To do this, the methods SetCatalogSort() +and SetCreationDate() need to be called for both files. This is done +automatically for all examples. + + +Nonstandard Fonts + +Nothing special is required to use the standard PDF fonts (courier, +helvetica, times, zapfdingbats) in your documents other than calling +SetFont(). + +You should use AddUTF8Font() or AddUTF8FontFromBytes() to add a TrueType +UTF-8 encoded font. Use RTL() and LTR() methods switch between +“right-to-left” and “left-to-right” mode. + +In order to use a different non-UTF-8 TrueType or Type1 font, you will +need to generate a font definition file and, if the font will be +embedded into PDFs, a compressed version of the font file. This is done +by calling the MakeFont function or using the included makefont command +line utility. To create the utility, cd into the makefont subdirectory +and run “go build”. This will produce a standalone executable named +makefont. Select the appropriate encoding file from the font +subdirectory and run the command as in the following example. + + ./makefont --embed --enc=../font/cp1252.map --dst=../font ../font/calligra.ttf + +In your PDF generation code, call AddFont() to load the font and, as +with the standard fonts, SetFont() to begin using it. Most examples, +including the package example, demonstrate this method. Good sources of +free, open-source fonts include Google Fonts and DejaVu Fonts. + + +Related Packages + +The draw2d package is a two dimensional vector graphics library that can +generate output in different forms. It uses gofpdf for its document +production mode. + + +Contributing Changes + +gofpdf is a global community effort and you are invited to make it even +better. If you have implemented a new feature or corrected a problem, +please consider contributing your change to the project. A contribution +that does not directly pertain to the core functionality of gofpdf +should be placed in its own directory directly beneath the contrib +directory. + +Here are guidelines for making submissions. Your change should + + +- be compatible with the MIT License + +- be properly documented + +- be formatted with go fmt + +- include an example in fpdf_test.go if appropriate + +- conform to the standards of golint and go vet, that is, golint . and +go vet . should not generate any warnings + +- not diminish test coverage + +Pull requests are the preferred means of accepting your changes. + + +License + +gofpdf is released under the MIT License. It is copyrighted by Kurt Jung +and the contributors acknowledged below. + + +Acknowledgments + +This package’s code and documentation are closely derived from the FPDF +library created by Olivier Plathey, and a number of font and image +resources are copied directly from it. Bruno Michel has provided +valuable assistance with the code. Drawing support is adapted from the +FPDF geometric figures script by David Hernández Sanz. Transparency +support is adapted from the FPDF transparency script by Martin Hall-May. +Support for gradients and clipping is adapted from FPDF scripts by +Andreas Würmser. Support for outline bookmarks is adapted from Olivier +Plathey by Manuel Cornes. Layer support is adapted from Olivier Plathey. +Support for transformations is adapted from the FPDF transformation +script by Moritz Wagner and Andreas Würmser. PDF protection is adapted +from the work of Klemen Vodopivec for the FPDF product. Lawrence +Kesteloot provided code to allow an image’s extent to be determined +prior to placement. Support for vertical alignment within a cell was +provided by Stefan Schroeder. Ivan Daniluk generalized the font and +image loading code to use the Reader interface while maintaining +backward compatibility. Anthony Starks provided code for the Polygon +function. Robert Lillack provided the Beziergon function and corrected +some naming issues with the internal curve function. Claudio Felber +provided implementations for dashed line drawing and generalized font +loading. Stani Michiels provided support for multi-segment path drawing +with smooth line joins, line join styles, enhanced fill modes, and has +helped greatly with package presentation and tests. Templating is +adapted by Marcus Downing from the FPDF_Tpl library created by Jan +Slabon and Setasign. Jelmer Snoeck contributed packages that generate a +variety of barcodes and help with registering images on the web. Jelmer +Snoek and Guillermo Pascual augmented the basic HTML functionality with +aligned text. Kent Quirk implemented backwards-compatible support for +reading DPI from images that support it, and for setting DPI manually +and then having it properly taken into account when calculating image +size. Paulo Coutinho provided support for static embedded fonts. Dan +Meyers added support for embedded JavaScript. David Fish added a generic +alias-replacement function to enable, among other things, table of +contents functionality. Andy Bakun identified and corrected a problem in +which the internal catalogs were not sorted stably. Paul Montag added +encoding and decoding functionality for templates, including images that +are embedded in templates; this allows templates to be stored +independently of gofpdf. Paul also added support for page boxes used in +printing PDF documents. Wojciech Matusiak added supported for word +spacing. Artem Korotkiy added support of UTF-8 fonts. Dave Barnes added +support for imported objects and templates. + + +Roadmap + + +- Improve test coverage as reported by the coverage tool. +*/ +package gofpdf diff --git a/vendor/github.com/jung-kurt/gofpdf/embedded.go b/vendor/github.com/jung-kurt/gofpdf/embedded.go new file mode 100644 index 00000000..1d947b03 --- /dev/null +++ b/vendor/github.com/jung-kurt/gofpdf/embedded.go @@ -0,0 +1,559 @@ +/* + * Copyright (c) 2014 Kurt Jung (Gmail: kurt.w.jung) + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package gofpdf + +// Embedded standard fonts + +import ( + "strings" +) + +var embeddedFontList = map[string]string{ + "courierBI": `{"Tp":"Core","Name":"Courier-BoldOblique","Up":-100,"Ut":50,"I":256,"Cw":[600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600]}`, + "courierB": `{"Tp":"Core","Name":"Courier-Bold","Up":-100,"Ut":50,"I":256,"Cw":[600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600]}`, + "courierI": `{"Tp":"Core","Name":"Courier-Oblique","Up":-100,"Ut":50,"I":256,"Cw":[600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600]}`, + "courier": `{"Tp":"Core","Name":"Courier","Up":-100,"Ut":50,"I":256,"Cw":[600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600]}`, + "helveticaBI": `{"Tp":"Core","Name":"Helvetica-BoldOblique","Up":-100,"Ut":50,"Cw":[278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,333,474,556,556,889,722,238,333,333,389,584,278,333,278,278,556,556,556,556,556,556,556,556,556,556,333,333,584,584,584,611,975,722,722,722,722,667,611,778,722,278,556,722,611,833,722,778,667,778,722,667,611,722,667,944,667,667,611,333,278,333,584,556,333,556,611,556,611,556,333,611,611,278,278,556,278,889,611,611,611,611,389,556,333,611,556,778,556,556,500,389,280,389,584,350,556,350,278,556,500,1000,556,556,333,1000,667,333,1000,350,611,350,350,278,278,500,500,350,556,1000,333,1000,556,333,944,350,500,667,278,333,556,556,556,556,280,556,333,737,370,556,584,333,737,333,400,584,333,333,333,611,556,278,333,333,365,556,834,834,834,611,722,722,722,722,722,722,1000,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722,722,667,667,611,556,556,556,556,556,556,889,556,556,556,556,556,278,278,278,278,611,611,611,611,611,611,611,584,611,611,611,611,611,556,611,556]}`, + "helveticaB": `{"Tp":"Core","Name":"Helvetica-Bold","Up":-100,"Ut":50,"Cw":[278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,333,474,556,556,889,722,238,333,333,389,584,278,333,278,278,556,556,556,556,556,556,556,556,556,556,333,333,584,584,584,611,975,722,722,722,722,667,611,778,722,278,556,722,611,833,722,778,667,778,722,667,611,722,667,944,667,667,611,333,278,333,584,556,333,556,611,556,611,556,333,611,611,278,278,556,278,889,611,611,611,611,389,556,333,611,556,778,556,556,500,389,280,389,584,350,556,350,278,556,500,1000,556,556,333,1000,667,333,1000,350,611,350,350,278,278,500,500,350,556,1000,333,1000,556,333,944,350,500,667,278,333,556,556,556,556,280,556,333,737,370,556,584,333,737,333,400,584,333,333,333,611,556,278,333,333,365,556,834,834,834,611,722,722,722,722,722,722,1000,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722,722,667,667,611,556,556,556,556,556,556,889,556,556,556,556,556,278,278,278,278,611,611,611,611,611,611,611,584,611,611,611,611,611,556,611,556]}`, + "helveticaI": `{"Tp":"Core","Name":"Helvetica-Oblique","Up":-100,"Ut":50,"Cw":[278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,355,556,556,889,667,191,333,333,389,584,278,333,278,278,556,556,556,556,556,556,556,556,556,556,278,278,584,584,584,556,1015,667,667,722,722,667,611,778,722,278,500,667,556,833,722,778,667,778,722,667,611,722,667,944,667,667,611,278,278,278,469,556,333,556,556,500,556,556,278,556,556,222,222,500,222,833,556,556,556,556,333,500,278,556,500,722,500,500,500,334,260,334,584,350,556,350,222,556,333,1000,556,556,333,1000,667,333,1000,350,611,350,350,222,222,333,333,350,556,1000,333,1000,500,333,944,350,500,667,278,333,556,556,556,556,260,556,333,737,370,556,584,333,737,333,400,584,333,333,333,556,537,278,333,333,365,556,834,834,834,611,667,667,667,667,667,667,1000,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722,722,667,667,611,556,556,556,556,556,556,889,500,556,556,556,556,278,278,278,278,556,556,556,556,556,556,556,584,611,556,556,556,556,500,556,500]}`, + "helvetica": `{"Tp":"Core","Name":"Helvetica","Up":-100,"Ut":50,"Cw":[278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,355,556,556,889,667,191,333,333,389,584,278,333,278,278,556,556,556,556,556,556,556,556,556,556,278,278,584,584,584,556,1015,667,667,722,722,667,611,778,722,278,500,667,556,833,722,778,667,778,722,667,611,722,667,944,667,667,611,278,278,278,469,556,333,556,556,500,556,556,278,556,556,222,222,500,222,833,556,556,556,556,333,500,278,556,500,722,500,500,500,334,260,334,584,350,556,350,222,556,333,1000,556,556,333,1000,667,333,1000,350,611,350,350,222,222,333,333,350,556,1000,333,1000,500,333,944,350,500,667,278,333,556,556,556,556,260,556,333,737,370,556,584,333,737,333,400,584,333,333,333,556,537,278,333,333,365,556,834,834,834,611,667,667,667,667,667,667,1000,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722,722,667,667,611,556,556,556,556,556,556,889,500,556,556,556,556,278,278,278,278,556,556,556,556,556,556,556,584,611,556,556,556,556,500,556,500]}`, + "timesBI": `{"Tp":"Core","Name":"Times-BoldItalic","Up":-100,"Ut":50,"Cw":[250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,389,555,500,500,833,778,278,333,333,500,570,250,333,250,278,500,500,500,500,500,500,500,500,500,500,333,333,570,570,570,500,832,667,667,667,722,667,667,722,778,389,500,667,611,889,722,722,611,722,667,556,611,722,667,889,667,611,611,333,278,333,570,500,333,500,500,444,500,444,333,500,556,278,278,500,278,778,556,500,500,500,389,389,278,556,444,667,500,444,389,348,220,348,570,350,500,350,333,500,500,1000,500,500,333,1000,556,333,944,350,611,350,350,333,333,500,500,350,500,1000,333,1000,389,333,722,350,389,611,250,389,500,500,500,500,220,500,333,747,266,500,606,333,747,333,400,570,300,300,333,576,500,250,333,300,300,500,750,750,750,500,667,667,667,667,667,667,944,667,667,667,667,667,389,389,389,389,722,722,722,722,722,722,722,570,722,722,722,722,722,611,611,500,500,500,500,500,500,500,722,444,444,444,444,444,278,278,278,278,500,556,500,500,500,500,500,570,500,556,556,556,556,444,500,444]}`, + "timesB": `{"Tp":"Core","Name":"Times-Bold","Up":-100,"Ut":50,"Cw":[250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,333,555,500,500,1000,833,278,333,333,500,570,250,333,250,278,500,500,500,500,500,500,500,500,500,500,333,333,570,570,570,500,930,722,667,722,722,667,611,778,778,389,500,778,667,944,722,778,611,778,722,556,667,722,722,1000,722,722,667,333,278,333,581,500,333,500,556,444,556,444,333,500,556,278,333,556,278,833,556,500,556,556,444,389,333,556,500,722,500,500,444,394,220,394,520,350,500,350,333,500,500,1000,500,500,333,1000,556,333,1000,350,667,350,350,333,333,500,500,350,500,1000,333,1000,389,333,722,350,444,722,250,333,500,500,500,500,220,500,333,747,300,500,570,333,747,333,400,570,300,300,333,556,540,250,333,300,330,500,750,750,750,500,722,722,722,722,722,722,1000,722,667,667,667,667,389,389,389,389,722,722,778,778,778,778,778,570,778,722,722,722,722,722,611,556,500,500,500,500,500,500,722,444,444,444,444,444,278,278,278,278,500,556,500,500,500,500,500,570,500,556,556,556,556,500,556,500]}`, + "timesI": `{"Tp":"Core","Name":"Times-Italic","Up":-100,"Ut":50,"Cw":[250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,333,420,500,500,833,778,214,333,333,500,675,250,333,250,278,500,500,500,500,500,500,500,500,500,500,333,333,675,675,675,500,920,611,611,667,722,611,611,722,722,333,444,667,556,833,667,722,611,722,611,500,556,722,611,833,611,556,556,389,278,389,422,500,333,500,500,444,500,444,278,500,500,278,278,444,278,722,500,500,500,500,389,389,278,500,444,667,444,444,389,400,275,400,541,350,500,350,333,500,556,889,500,500,333,1000,500,333,944,350,556,350,350,333,333,556,556,350,500,889,333,980,389,333,667,350,389,556,250,389,500,500,500,500,275,500,333,760,276,500,675,333,760,333,400,675,300,300,333,500,523,250,333,300,310,500,750,750,750,500,611,611,611,611,611,611,889,667,611,611,611,611,333,333,333,333,722,667,722,722,722,722,722,675,722,722,722,722,722,556,611,500,500,500,500,500,500,500,667,444,444,444,444,444,278,278,278,278,500,500,500,500,500,500,500,675,500,500,500,500,500,444,500,444]}`, + "times": `{"Tp":"Core","Name":"Times-Roman","Up":-100,"Ut":50,"Cw":[250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,333,408,500,500,833,778,180,333,333,500,564,250,333,250,278,500,500,500,500,500,500,500,500,500,500,278,278,564,564,564,444,921,722,667,667,722,611,556,722,722,333,389,722,611,889,722,722,556,722,667,556,611,722,722,944,722,722,611,333,278,333,469,500,333,444,500,444,500,444,333,500,500,278,278,500,278,778,500,500,500,500,333,389,278,500,500,722,500,500,444,480,200,480,541,350,500,350,333,500,444,1000,500,500,333,1000,556,333,889,350,611,350,350,333,333,444,444,350,500,1000,333,980,389,333,722,350,444,722,250,333,500,500,500,500,200,500,333,760,276,500,564,333,760,333,400,564,300,300,333,500,453,250,333,300,310,500,750,750,750,444,722,722,722,722,722,722,889,667,611,611,611,611,333,333,333,333,722,722,722,722,722,722,722,564,722,722,722,722,722,722,556,500,444,444,444,444,444,444,667,444,444,444,444,444,278,278,278,278,500,500,500,500,500,500,500,564,500,500,500,500,500,500,500,500]}`, + "zapfdingbats": `{"Tp":"Core","Name":"ZapfDingbats","Up":-100,"Ut":50,"Cw":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,278,974,961,974,980,719,789,790,791,690,960,939,549,855,911,933,911,945,974,755,846,762,761,571,677,763,760,759,754,494,552,537,577,692,786,788,788,790,793,794,816,823,789,841,823,833,816,831,923,744,723,749,790,792,695,776,768,792,759,707,708,682,701,826,815,789,789,707,687,696,689,786,787,713,791,785,791,873,761,762,762,759,759,892,892,788,784,438,138,277,415,392,392,668,668,0,390,390,317,317,276,276,509,509,410,410,234,234,334,334,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,732,544,544,910,667,760,760,776,595,694,626,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,894,838,1016,458,748,924,748,918,927,928,928,834,873,828,924,924,917,930,931,463,883,836,836,867,867,696,696,874,0,874,760,946,771,865,771,888,967,888,831,873,927,970,918,0]}`, +} + +func (f *Fpdf) coreFontReader(familyStr, styleStr string) (r *strings.Reader) { + key := familyStr + styleStr + str, ok := embeddedFontList[key] + if ok { + r = strings.NewReader(str) + } else { + f.SetErrorf("could not locate \"%s\" among embedded core font definition files", key) + } + return +} + +var embeddedMapList = map[string]string{ + "cp1250": ` +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+20AC Euro +!82 U+201A quotesinglbase +!84 U+201E quotedblbase +!85 U+2026 ellipsis +!86 U+2020 dagger +!87 U+2021 daggerdbl +!89 U+2030 perthousand +!8A U+0160 Scaron +!8B U+2039 guilsinglleft +!8C U+015A Sacute +!8D U+0164 Tcaron +!8E U+017D Zcaron +!8F U+0179 Zacute +!91 U+2018 quoteleft +!92 U+2019 quoteright +!93 U+201C quotedblleft +!94 U+201D quotedblright +!95 U+2022 bullet +!96 U+2013 endash +!97 U+2014 emdash +!99 U+2122 trademark +!9A U+0161 scaron +!9B U+203A guilsinglright +!9C U+015B sacute +!9D U+0165 tcaron +!9E U+017E zcaron +!9F U+017A zacute +!A0 U+00A0 space +!A1 U+02C7 caron +!A2 U+02D8 breve +!A3 U+0141 Lslash +!A4 U+00A4 currency +!A5 U+0104 Aogonek +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+00A9 copyright +!AA U+015E Scedilla +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AE U+00AE registered +!AF U+017B Zdotaccent +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+02DB ogonek +!B3 U+0142 lslash +!B4 U+00B4 acute +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+00B8 cedilla +!B9 U+0105 aogonek +!BA U+015F scedilla +!BB U+00BB guillemotright +!BC U+013D Lcaron +!BD U+02DD hungarumlaut +!BE U+013E lcaron +!BF U+017C zdotaccent +!C0 U+0154 Racute +!C1 U+00C1 Aacute +!C2 U+00C2 Acircumflex +!C3 U+0102 Abreve +!C4 U+00C4 Adieresis +!C5 U+0139 Lacute +!C6 U+0106 Cacute +!C7 U+00C7 Ccedilla +!C8 U+010C Ccaron +!C9 U+00C9 Eacute +!CA U+0118 Eogonek +!CB U+00CB Edieresis +!CC U+011A Ecaron +!CD U+00CD Iacute +!CE U+00CE Icircumflex +!CF U+010E Dcaron +!D0 U+0110 Dcroat +!D1 U+0143 Nacute +!D2 U+0147 Ncaron +!D3 U+00D3 Oacute +!D4 U+00D4 Ocircumflex +!D5 U+0150 Ohungarumlaut +!D6 U+00D6 Odieresis +!D7 U+00D7 multiply +!D8 U+0158 Rcaron +!D9 U+016E Uring +!DA U+00DA Uacute +!DB U+0170 Uhungarumlaut +!DC U+00DC Udieresis +!DD U+00DD Yacute +!DE U+0162 Tcommaaccent +!DF U+00DF germandbls +!E0 U+0155 racute +!E1 U+00E1 aacute +!E2 U+00E2 acircumflex +!E3 U+0103 abreve +!E4 U+00E4 adieresis +!E5 U+013A lacute +!E6 U+0107 cacute +!E7 U+00E7 ccedilla +!E8 U+010D ccaron +!E9 U+00E9 eacute +!EA U+0119 eogonek +!EB U+00EB edieresis +!EC U+011B ecaron +!ED U+00ED iacute +!EE U+00EE icircumflex +!EF U+010F dcaron +!F0 U+0111 dcroat +!F1 U+0144 nacute +!F2 U+0148 ncaron +!F3 U+00F3 oacute +!F4 U+00F4 ocircumflex +!F5 U+0151 ohungarumlaut +!F6 U+00F6 odieresis +!F7 U+00F7 divide +!F8 U+0159 rcaron +!F9 U+016F uring +!FA U+00FA uacute +!FB U+0171 uhungarumlaut +!FC U+00FC udieresis +!FD U+00FD yacute +!FE U+0163 tcommaaccent +!FF U+02D9 dotaccent + `, + "cp1252": ` +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+20AC Euro +!82 U+201A quotesinglbase +!83 U+0192 florin +!84 U+201E quotedblbase +!85 U+2026 ellipsis +!86 U+2020 dagger +!87 U+2021 daggerdbl +!88 U+02C6 circumflex +!89 U+2030 perthousand +!8A U+0160 Scaron +!8B U+2039 guilsinglleft +!8C U+0152 OE +!8E U+017D Zcaron +!91 U+2018 quoteleft +!92 U+2019 quoteright +!93 U+201C quotedblleft +!94 U+201D quotedblright +!95 U+2022 bullet +!96 U+2013 endash +!97 U+2014 emdash +!98 U+02DC tilde +!99 U+2122 trademark +!9A U+0161 scaron +!9B U+203A guilsinglright +!9C U+0153 oe +!9E U+017E zcaron +!9F U+0178 Ydieresis +!A0 U+00A0 space +!A1 U+00A1 exclamdown +!A2 U+00A2 cent +!A3 U+00A3 sterling +!A4 U+00A4 currency +!A5 U+00A5 yen +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+00A9 copyright +!AA U+00AA ordfeminine +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AE U+00AE registered +!AF U+00AF macron +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+00B2 twosuperior +!B3 U+00B3 threesuperior +!B4 U+00B4 acute +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+00B8 cedilla +!B9 U+00B9 onesuperior +!BA U+00BA ordmasculine +!BB U+00BB guillemotright +!BC U+00BC onequarter +!BD U+00BD onehalf +!BE U+00BE threequarters +!BF U+00BF questiondown +!C0 U+00C0 Agrave +!C1 U+00C1 Aacute +!C2 U+00C2 Acircumflex +!C3 U+00C3 Atilde +!C4 U+00C4 Adieresis +!C5 U+00C5 Aring +!C6 U+00C6 AE +!C7 U+00C7 Ccedilla +!C8 U+00C8 Egrave +!C9 U+00C9 Eacute +!CA U+00CA Ecircumflex +!CB U+00CB Edieresis +!CC U+00CC Igrave +!CD U+00CD Iacute +!CE U+00CE Icircumflex +!CF U+00CF Idieresis +!D0 U+00D0 Eth +!D1 U+00D1 Ntilde +!D2 U+00D2 Ograve +!D3 U+00D3 Oacute +!D4 U+00D4 Ocircumflex +!D5 U+00D5 Otilde +!D6 U+00D6 Odieresis +!D7 U+00D7 multiply +!D8 U+00D8 Oslash +!D9 U+00D9 Ugrave +!DA U+00DA Uacute +!DB U+00DB Ucircumflex +!DC U+00DC Udieresis +!DD U+00DD Yacute +!DE U+00DE Thorn +!DF U+00DF germandbls +!E0 U+00E0 agrave +!E1 U+00E1 aacute +!E2 U+00E2 acircumflex +!E3 U+00E3 atilde +!E4 U+00E4 adieresis +!E5 U+00E5 aring +!E6 U+00E6 ae +!E7 U+00E7 ccedilla +!E8 U+00E8 egrave +!E9 U+00E9 eacute +!EA U+00EA ecircumflex +!EB U+00EB edieresis +!EC U+00EC igrave +!ED U+00ED iacute +!EE U+00EE icircumflex +!EF U+00EF idieresis +!F0 U+00F0 eth +!F1 U+00F1 ntilde +!F2 U+00F2 ograve +!F3 U+00F3 oacute +!F4 U+00F4 ocircumflex +!F5 U+00F5 otilde +!F6 U+00F6 odieresis +!F7 U+00F7 divide +!F8 U+00F8 oslash +!F9 U+00F9 ugrave +!FA U+00FA uacute +!FB U+00FB ucircumflex +!FC U+00FC udieresis +!FD U+00FD yacute +!FE U+00FE thorn +!FF U+00FF ydieresis + `, +} diff --git a/vendor/github.com/jung-kurt/gofpdf/font.go b/vendor/github.com/jung-kurt/gofpdf/font.go new file mode 100644 index 00000000..29417bb0 --- /dev/null +++ b/vendor/github.com/jung-kurt/gofpdf/font.go @@ -0,0 +1,474 @@ +/* + * Copyright (c) 2013 Kurt Jung (Gmail: kurt.w.jung) + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package gofpdf + +// Utility to generate font definition files + +// Version: 1.2 +// Date: 2011-06-18 +// Author: Olivier PLATHEY +// Port to Go: Kurt Jung, 2013-07-15 + +import ( + "bufio" + "compress/zlib" + "encoding/binary" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strconv" + "strings" +) + +func baseNoExt(fileStr string) string { + str := filepath.Base(fileStr) + extLen := len(filepath.Ext(str)) + if extLen > 0 { + str = str[:len(str)-extLen] + } + return str +} + +func loadMap(encodingFileStr string) (encList encListType, err error) { + // printf("Encoding file string [%s]\n", encodingFileStr) + var f *os.File + // f, err = os.Open(encodingFilepath(encodingFileStr)) + f, err = os.Open(encodingFileStr) + if err == nil { + defer f.Close() + for j := range encList { + encList[j].uv = -1 + encList[j].name = ".notdef" + } + scanner := bufio.NewScanner(f) + var enc encType + var pos int + for scanner.Scan() { + // "!3F U+003F question" + _, err = fmt.Sscanf(scanner.Text(), "!%x U+%x %s", &pos, &enc.uv, &enc.name) + if err == nil { + if pos < 256 { + encList[pos] = enc + } else { + err = fmt.Errorf("map position 0x%2X exceeds 0xFF", pos) + return + } + } else { + return + } + } + if err = scanner.Err(); err != nil { + return + } + } + return +} + +// getInfoFromTrueType returns information from a TrueType font +func getInfoFromTrueType(fileStr string, msgWriter io.Writer, embed bool, encList encListType) (info fontInfoType, err error) { + info.Widths = make([]int, 256) + var ttf TtfType + ttf, err = TtfParse(fileStr) + if err != nil { + return + } + if embed { + if !ttf.Embeddable { + err = fmt.Errorf("font license does not allow embedding") + return + } + info.Data, err = ioutil.ReadFile(fileStr) + if err != nil { + return + } + info.OriginalSize = len(info.Data) + } + k := 1000.0 / float64(ttf.UnitsPerEm) + info.FontName = ttf.PostScriptName + info.Bold = ttf.Bold + info.Desc.ItalicAngle = int(ttf.ItalicAngle) + info.IsFixedPitch = ttf.IsFixedPitch + info.Desc.Ascent = round(k * float64(ttf.TypoAscender)) + info.Desc.Descent = round(k * float64(ttf.TypoDescender)) + info.UnderlineThickness = round(k * float64(ttf.UnderlineThickness)) + info.UnderlinePosition = round(k * float64(ttf.UnderlinePosition)) + info.Desc.FontBBox = fontBoxType{ + round(k * float64(ttf.Xmin)), + round(k * float64(ttf.Ymin)), + round(k * float64(ttf.Xmax)), + round(k * float64(ttf.Ymax)), + } + // printf("FontBBox\n") + // dump(info.Desc.FontBBox) + info.Desc.CapHeight = round(k * float64(ttf.CapHeight)) + info.Desc.MissingWidth = round(k * float64(ttf.Widths[0])) + var wd int + for j := 0; j < len(info.Widths); j++ { + wd = info.Desc.MissingWidth + if encList[j].name != ".notdef" { + uv := encList[j].uv + pos, ok := ttf.Chars[uint16(uv)] + if ok { + wd = round(k * float64(ttf.Widths[pos])) + } else { + fmt.Fprintf(msgWriter, "Character %s is missing\n", encList[j].name) + } + } + info.Widths[j] = wd + } + // printf("getInfoFromTrueType/FontBBox\n") + // dump(info.Desc.FontBBox) + return +} + +type segmentType struct { + marker uint8 + tp uint8 + size uint32 + data []byte +} + +func segmentRead(r io.Reader) (s segmentType, err error) { + if err = binary.Read(r, binary.LittleEndian, &s.marker); err != nil { + return + } + if s.marker != 128 { + err = fmt.Errorf("font file is not a valid binary Type1") + return + } + if err = binary.Read(r, binary.LittleEndian, &s.tp); err != nil { + return + } + if err = binary.Read(r, binary.LittleEndian, &s.size); err != nil { + return + } + s.data = make([]byte, s.size) + _, err = r.Read(s.data) + return +} + +// -rw-r--r-- 1 root root 9532 2010-04-22 11:27 /usr/share/fonts/type1/mathml/Symbol.afm +// -rw-r--r-- 1 root root 37744 2010-04-22 11:27 /usr/share/fonts/type1/mathml/Symbol.pfb + +// getInfoFromType1 return information from a Type1 font +func getInfoFromType1(fileStr string, msgWriter io.Writer, embed bool, encList encListType) (info fontInfoType, err error) { + info.Widths = make([]int, 256) + if embed { + var f *os.File + f, err = os.Open(fileStr) + if err != nil { + return + } + defer f.Close() + // Read first segment + var s1, s2 segmentType + s1, err = segmentRead(f) + if err != nil { + return + } + s2, err = segmentRead(f) + if err != nil { + return + } + info.Data = s1.data + info.Data = append(info.Data, s2.data...) + info.Size1 = s1.size + info.Size2 = s2.size + } + afmFileStr := fileStr[0:len(fileStr)-3] + "afm" + size, ok := fileSize(afmFileStr) + if !ok { + err = fmt.Errorf("font file (ATM) %s not found", afmFileStr) + return + } else if size == 0 { + err = fmt.Errorf("font file (AFM) %s empty or not readable", afmFileStr) + return + } + var f *os.File + f, err = os.Open(afmFileStr) + if err != nil { + return + } + defer f.Close() + scanner := bufio.NewScanner(f) + var fields []string + var wd int + var wt, name string + wdMap := make(map[string]int) + for scanner.Scan() { + fields = strings.Fields(strings.TrimSpace(scanner.Text())) + // Comment Generated by FontForge 20080203 + // FontName Symbol + // C 32 ; WX 250 ; N space ; B 0 0 0 0 ; + if len(fields) >= 2 { + switch fields[0] { + case "C": + if wd, err = strconv.Atoi(fields[4]); err == nil { + name = fields[7] + wdMap[name] = wd + } + case "FontName": + info.FontName = fields[1] + case "Weight": + wt = strings.ToLower(fields[1]) + case "ItalicAngle": + info.Desc.ItalicAngle, err = strconv.Atoi(fields[1]) + case "Ascender": + info.Desc.Ascent, err = strconv.Atoi(fields[1]) + case "Descender": + info.Desc.Descent, err = strconv.Atoi(fields[1]) + case "UnderlineThickness": + info.UnderlineThickness, err = strconv.Atoi(fields[1]) + case "UnderlinePosition": + info.UnderlinePosition, err = strconv.Atoi(fields[1]) + case "IsFixedPitch": + info.IsFixedPitch = fields[1] == "true" + case "FontBBox": + if info.Desc.FontBBox.Xmin, err = strconv.Atoi(fields[1]); err == nil { + if info.Desc.FontBBox.Ymin, err = strconv.Atoi(fields[2]); err == nil { + if info.Desc.FontBBox.Xmax, err = strconv.Atoi(fields[3]); err == nil { + info.Desc.FontBBox.Ymax, err = strconv.Atoi(fields[4]) + } + } + } + case "CapHeight": + info.Desc.CapHeight, err = strconv.Atoi(fields[1]) + case "StdVW": + info.Desc.StemV, err = strconv.Atoi(fields[1]) + } + } + if err != nil { + return + } + } + if err = scanner.Err(); err != nil { + return + } + if info.FontName == "" { + err = fmt.Errorf("the field FontName missing in AFM file %s", afmFileStr) + return + } + info.Bold = wt == "bold" || wt == "black" + var missingWd int + missingWd, ok = wdMap[".notdef"] + if ok { + info.Desc.MissingWidth = missingWd + } + for j := 0; j < len(info.Widths); j++ { + info.Widths[j] = info.Desc.MissingWidth + } + for j := 0; j < len(info.Widths); j++ { + name = encList[j].name + if name != ".notdef" { + wd, ok = wdMap[name] + if ok { + info.Widths[j] = wd + } else { + fmt.Fprintf(msgWriter, "Character %s is missing\n", name) + } + } + } + // printf("getInfoFromType1/FontBBox\n") + // dump(info.Desc.FontBBox) + return +} + +func makeFontDescriptor(info *fontInfoType) { + if info.Desc.CapHeight == 0 { + info.Desc.CapHeight = info.Desc.Ascent + } + info.Desc.Flags = 1 << 5 + if info.IsFixedPitch { + info.Desc.Flags |= 1 + } + if info.Desc.ItalicAngle != 0 { + info.Desc.Flags |= 1 << 6 + } + if info.Desc.StemV == 0 { + if info.Bold { + info.Desc.StemV = 120 + } else { + info.Desc.StemV = 70 + } + } + // printf("makeFontDescriptor/FontBBox\n") + // dump(info.Desc.FontBBox) +} + +// makeFontEncoding builds differences from reference encoding +func makeFontEncoding(encList encListType, refEncFileStr string) (diffStr string, err error) { + var refList encListType + if refList, err = loadMap(refEncFileStr); err != nil { + return + } + var buf fmtBuffer + last := 0 + for j := 32; j < 256; j++ { + if encList[j].name != refList[j].name { + if j != last+1 { + buf.printf("%d ", j) + } + last = j + buf.printf("/%s ", encList[j].name) + } + } + diffStr = strings.TrimSpace(buf.String()) + return +} + +func makeDefinitionFile(fileStr, tpStr, encodingFileStr string, embed bool, encList encListType, info fontInfoType) error { + var err error + var def fontDefType + def.Tp = tpStr + def.Name = info.FontName + makeFontDescriptor(&info) + def.Desc = info.Desc + // printf("makeDefinitionFile/FontBBox\n") + // dump(def.Desc.FontBBox) + def.Up = info.UnderlinePosition + def.Ut = info.UnderlineThickness + def.Cw = info.Widths + def.Enc = baseNoExt(encodingFileStr) + // fmt.Printf("encodingFileStr [%s], def.Enc [%s]\n", encodingFileStr, def.Enc) + // fmt.Printf("reference [%s]\n", filepath.Join(filepath.Dir(encodingFileStr), "cp1252.map")) + def.Diff, err = makeFontEncoding(encList, filepath.Join(filepath.Dir(encodingFileStr), "cp1252.map")) + if err != nil { + return err + } + def.File = info.File + def.Size1 = int(info.Size1) + def.Size2 = int(info.Size2) + def.OriginalSize = info.OriginalSize + // printf("Font definition file [%s]\n", fileStr) + var buf []byte + buf, err = json.Marshal(def) + if err != nil { + return err + } + var f *os.File + f, err = os.Create(fileStr) + if err != nil { + return err + } + defer f.Close() + _, err = f.Write(buf) + if err != nil { + return err + } + err = f.Close() + if err != nil { + return err + } + + return err +} + +// MakeFont generates a font definition file in JSON format. A definition file +// of this type is required to use non-core fonts in the PDF documents that +// gofpdf generates. See the makefont utility in the gofpdf package for a +// command line interface to this function. +// +// fontFileStr is the name of the TrueType file (extension .ttf), OpenType file +// (extension .otf) or binary Type1 file (extension .pfb) from which to +// generate a definition file. If an OpenType file is specified, it must be one +// that is based on TrueType outlines, not PostScript outlines; this cannot be +// determined from the file extension alone. If a Type1 file is specified, a +// metric file with the same pathname except with the extension .afm must be +// present. +// +// encodingFileStr is the name of the encoding file that corresponds to the +// font. +// +// dstDirStr is the name of the directory in which to save the definition file +// and, if embed is true, the compressed font file. +// +// msgWriter is the writer that is called to display messages throughout the +// process. Use nil to turn off messages. +// +// embed is true if the font is to be embedded in the PDF files. +func MakeFont(fontFileStr, encodingFileStr, dstDirStr string, msgWriter io.Writer, embed bool) error { + if msgWriter == nil { + msgWriter = ioutil.Discard + } + if !fileExist(fontFileStr) { + return fmt.Errorf("font file not found: %s", fontFileStr) + } + extStr := strings.ToLower(fontFileStr[len(fontFileStr)-3:]) + // printf("Font file extension [%s]\n", extStr) + var tpStr string + switch extStr { + case "ttf": + fallthrough + case "otf": + tpStr = "TrueType" + case "pfb": + tpStr = "Type1" + default: + return fmt.Errorf("unrecognized font file extension: %s", extStr) + } + + var info fontInfoType + encList, err := loadMap(encodingFileStr) + if err != nil { + return err + } + // printf("Encoding table\n") + // dump(encList) + if tpStr == "TrueType" { + info, err = getInfoFromTrueType(fontFileStr, msgWriter, embed, encList) + if err != nil { + return err + } + } else { + info, err = getInfoFromType1(fontFileStr, msgWriter, embed, encList) + if err != nil { + return err + } + } + baseStr := baseNoExt(fontFileStr) + // fmt.Printf("Base [%s]\n", baseStr) + if embed { + var f *os.File + info.File = baseStr + ".z" + zFileStr := filepath.Join(dstDirStr, info.File) + f, err = os.Create(zFileStr) + if err != nil { + return err + } + defer f.Close() + cmp := zlib.NewWriter(f) + _, err = cmp.Write(info.Data) + if err != nil { + return err + } + err = cmp.Close() + if err != nil { + return err + } + fmt.Fprintf(msgWriter, "Font file compressed: %s\n", zFileStr) + } + defFileStr := filepath.Join(dstDirStr, baseStr+".json") + err = makeDefinitionFile(defFileStr, tpStr, encodingFileStr, embed, encList, info) + if err != nil { + return err + } + fmt.Fprintf(msgWriter, "Font definition file successfully generated: %s\n", defFileStr) + return nil +} diff --git a/vendor/github.com/jung-kurt/gofpdf/fpdf.go b/vendor/github.com/jung-kurt/gofpdf/fpdf.go new file mode 100644 index 00000000..3e4188c3 --- /dev/null +++ b/vendor/github.com/jung-kurt/gofpdf/fpdf.go @@ -0,0 +1,4771 @@ +/* + * Copyright (c) 2013-2014 Kurt Jung (Gmail: kurt.w.jung) + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package gofpdf + +// Version: 1.7 +// Date: 2011-06-18 +// Author: Olivier PLATHEY +// Port to Go: Kurt Jung, 2013-07-15 + +import ( + "bytes" + "encoding/binary" + "encoding/json" + "fmt" + "image" + "image/color" + "image/gif" + "image/jpeg" + "image/png" + "io" + "io/ioutil" + "math" + "os" + "path" + "sort" + "strconv" + "strings" + "time" +) + +var gl struct { + catalogSort bool + noCompress bool // Initial zero value indicates compression + creationDate time.Time +} + +type fmtBuffer struct { + bytes.Buffer +} + +func (b *fmtBuffer) printf(fmtStr string, args ...interface{}) { + b.Buffer.WriteString(fmt.Sprintf(fmtStr, args...)) +} + +func fpdfNew(orientationStr, unitStr, sizeStr, fontDirStr string, size SizeType) (f *Fpdf) { + f = new(Fpdf) + if orientationStr == "" { + orientationStr = "p" + } else { + orientationStr = strings.ToLower(orientationStr) + } + if unitStr == "" { + unitStr = "mm" + } + if sizeStr == "" { + sizeStr = "A4" + } + if fontDirStr == "" { + fontDirStr = "." + } + f.page = 0 + f.n = 2 + f.pages = make([]*bytes.Buffer, 0, 8) + f.pages = append(f.pages, bytes.NewBufferString("")) // pages[0] is unused (1-based) + f.pageSizes = make(map[int]SizeType) + f.pageBoxes = make(map[int]map[string]PageBox) + f.defPageBoxes = make(map[string]PageBox) + f.state = 0 + f.fonts = make(map[string]fontDefType) + f.fontFiles = make(map[string]fontFileType) + f.diffs = make([]string, 0, 8) + f.templates = make(map[string]Template) + f.templateObjects = make(map[string]int) + f.importedObjs = make(map[string][]byte, 0) + f.importedObjPos = make(map[string]map[int]string, 0) + f.importedTplObjs = make(map[string]string) + f.importedTplIDs = make(map[string]int, 0) + f.images = make(map[string]*ImageInfoType) + f.pageLinks = make([][]linkType, 0, 8) + f.pageLinks = append(f.pageLinks, make([]linkType, 0, 0)) // pageLinks[0] is unused (1-based) + f.links = make([]intLinkType, 0, 8) + f.links = append(f.links, intLinkType{}) // links[0] is unused (1-based) + f.aliasMap = make(map[string]string) + f.inHeader = false + f.inFooter = false + f.lasth = 0 + f.fontFamily = "" + f.fontStyle = "" + f.SetFontSize(12) + f.underline = false + f.setDrawColor(0, 0, 0) + f.setFillColor(0, 0, 0) + f.setTextColor(0, 0, 0) + f.colorFlag = false + f.ws = 0 + f.fontpath = fontDirStr + // Core fonts + f.coreFonts = map[string]bool{ + "courier": true, + "helvetica": true, + "times": true, + "symbol": true, + "zapfdingbats": true, + } + // Scale factor + switch unitStr { + case "pt", "point": + f.k = 1.0 + case "mm": + f.k = 72.0 / 25.4 + case "cm": + f.k = 72.0 / 2.54 + case "in", "inch": + f.k = 72.0 + default: + f.err = fmt.Errorf("incorrect unit %s", unitStr) + return + } + f.unitStr = unitStr + // Page sizes + f.stdPageSizes = make(map[string]SizeType) + f.stdPageSizes["a3"] = SizeType{841.89, 1190.55} + f.stdPageSizes["a4"] = SizeType{595.28, 841.89} + f.stdPageSizes["a5"] = SizeType{420.94, 595.28} + f.stdPageSizes["a6"] = SizeType{297.64, 420.94} + f.stdPageSizes["a2"] = SizeType{1190.55, 1683.78} + f.stdPageSizes["a1"] = SizeType{1683.78, 2383.94} + f.stdPageSizes["letter"] = SizeType{612, 792} + f.stdPageSizes["legal"] = SizeType{612, 1008} + f.stdPageSizes["tabloid"] = SizeType{792, 1224} + if size.Wd > 0 && size.Ht > 0 { + f.defPageSize = size + } else { + f.defPageSize = f.getpagesizestr(sizeStr) + if f.err != nil { + return + } + } + f.curPageSize = f.defPageSize + // Page orientation + switch orientationStr { + case "p", "portrait": + f.defOrientation = "P" + f.w = f.defPageSize.Wd + f.h = f.defPageSize.Ht + // dbg("Assign h: %8.2f", f.h) + case "l", "landscape": + f.defOrientation = "L" + f.w = f.defPageSize.Ht + f.h = f.defPageSize.Wd + default: + f.err = fmt.Errorf("incorrect orientation: %s", orientationStr) + return + } + f.curOrientation = f.defOrientation + f.wPt = f.w * f.k + f.hPt = f.h * f.k + // Page margins (1 cm) + margin := 28.35 / f.k + f.SetMargins(margin, margin, margin) + // Interior cell margin (1 mm) + f.cMargin = margin / 10 + // Line width (0.2 mm) + f.lineWidth = 0.567 / f.k + // Automatic page break + f.SetAutoPageBreak(true, 2*margin) + // Default display mode + f.SetDisplayMode("default", "default") + if f.err != nil { + return + } + f.acceptPageBreak = func() bool { + return f.autoPageBreak + } + // Enable compression + f.SetCompression(!gl.noCompress) + f.spotColorMap = make(map[string]spotColorType) + f.blendList = make([]blendModeType, 0, 8) + f.blendList = append(f.blendList, blendModeType{}) // blendList[0] is unused (1-based) + f.blendMap = make(map[string]int) + f.blendMode = "Normal" + f.alpha = 1 + f.gradientList = make([]gradientType, 0, 8) + f.gradientList = append(f.gradientList, gradientType{}) // gradientList[0] is unused + // Set default PDF version number + f.pdfVersion = "1.3" + f.layerInit() + f.catalogSort = gl.catalogSort + f.creationDate = gl.creationDate + return +} + +// NewCustom returns a pointer to a new Fpdf instance. Its methods are +// subsequently called to produce a single PDF document. NewCustom() is an +// alternative to New() that provides additional customization. The PageSize() +// example demonstrates this method. +func NewCustom(init *InitType) (f *Fpdf) { + return fpdfNew(init.OrientationStr, init.UnitStr, init.SizeStr, init.FontDirStr, init.Size) +} + +// New returns a pointer to a new Fpdf instance. Its methods are subsequently +// called to produce a single PDF document. +// +// orientationStr specifies the default page orientation. For portrait mode, +// specify "P" or "Portrait". For landscape mode, specify "L" or "Landscape". +// An empty string will be replaced with "P". +// +// unitStr specifies the unit of length used in size parameters for elements +// other than fonts, which are always measured in points. Specify "pt" for +// point, "mm" for millimeter, "cm" for centimeter, or "in" for inch. An empty +// string will be replaced with "mm". +// +// sizeStr specifies the page size. Acceptable values are "A3", "A4", "A5", +// "Letter", "Legal", or "Tabloid". An empty string will be replaced with "A4". +// +// fontDirStr specifies the file system location in which font resources will +// be found. An empty string is replaced with ".". This argument only needs to +// reference an actual directory if a font other than one of the core +// fonts is used. The core fonts are "courier", "helvetica" (also called +// "arial"), "times", and "zapfdingbats" (also called "symbol"). +func New(orientationStr, unitStr, sizeStr, fontDirStr string) (f *Fpdf) { + return fpdfNew(orientationStr, unitStr, sizeStr, fontDirStr, SizeType{0, 0}) +} + +// Ok returns true if no processing errors have occurred. +func (f *Fpdf) Ok() bool { + return f.err == nil +} + +// Err returns true if a processing error has occurred. +func (f *Fpdf) Err() bool { + return f.err != nil +} + +// ClearError unsets the internal Fpdf error. This method should be used with +// care, as an internal error condition usually indicates an unrecoverable +// problem with the generation of a document. It is intended to deal with cases +// in which an error is used to select an alternate form of the document. +func (f *Fpdf) ClearError() { + f.err = nil +} + +// SetErrorf sets the internal Fpdf error with formatted text to halt PDF +// generation; this may facilitate error handling by application. If an error +// condition is already set, this call is ignored. +// +// See the documentation for printing in the standard fmt package for details +// about fmtStr and args. +func (f *Fpdf) SetErrorf(fmtStr string, args ...interface{}) { + if f.err == nil { + f.err = fmt.Errorf(fmtStr, args...) + } +} + +// String satisfies the fmt.Stringer interface and summarizes the Fpdf +// instance. +func (f *Fpdf) String() string { + return "Fpdf " + cnFpdfVersion +} + +// SetError sets an error to halt PDF generation. This may facilitate error +// handling by application. See also Ok(), Err() and Error(). +func (f *Fpdf) SetError(err error) { + if f.err == nil && err != nil { + f.err = err + } +} + +// Error returns the internal Fpdf error; this will be nil if no error has occurred. +func (f *Fpdf) Error() error { + return f.err +} + +// GetPageSize returns the current page's width and height. This is the paper's +// size. To compute the size of the area being used, subtract the margins (see +// GetMargins()). +func (f *Fpdf) GetPageSize() (width, height float64) { + width = f.w + height = f.h + return +} + +// GetMargins returns the left, top, right, and bottom margins. The first three +// are set with the SetMargins() method. The bottom margin is set with the +// SetAutoPageBreak() method. +func (f *Fpdf) GetMargins() (left, top, right, bottom float64) { + left = f.lMargin + top = f.tMargin + right = f.rMargin + bottom = f.bMargin + return +} + +// SetMargins defines the left, top and right margins. By default, they equal 1 +// cm. Call this method to change them. If the value of the right margin is +// less than zero, it is set to the same as the left margin. +func (f *Fpdf) SetMargins(left, top, right float64) { + f.lMargin = left + f.tMargin = top + if right < 0 { + right = left + } + f.rMargin = right +} + +// SetLeftMargin defines the left margin. The method can be called before +// creating the first page. If the current abscissa gets out of page, it is +// brought back to the margin. +func (f *Fpdf) SetLeftMargin(margin float64) { + f.lMargin = margin + if f.page > 0 && f.x < margin { + f.x = margin + } +} + +// GetCellMargin returns the cell margin. This is the amount of space before +// and after the text within a cell that's left blank, and is in units passed +// to New(). It defaults to 1mm. +func (f *Fpdf) GetCellMargin() float64 { + return f.cMargin +} + +// SetCellMargin sets the cell margin. This is the amount of space before and +// after the text within a cell that's left blank, and is in units passed to +// New(). +func (f *Fpdf) SetCellMargin(margin float64) { + f.cMargin = margin +} + +// SetPageBoxRec sets the page box for the current page, and any following +// pages. Allowable types are trim, trimbox, crop, cropbox, bleed, bleedbox, +// art and artbox box types are case insensitive. See SetPageBox() for a method +// that specifies the coordinates and extent of the page box individually. +func (f *Fpdf) SetPageBoxRec(t string, pb PageBox) { + switch strings.ToLower(t) { + case "trim": + fallthrough + case "trimbox": + t = "TrimBox" + case "crop": + fallthrough + case "cropbox": + t = "CropBox" + case "bleed": + fallthrough + case "bleedbox": + t = "BleedBox" + case "art": + fallthrough + case "artbox": + t = "ArtBox" + default: + f.err = fmt.Errorf("%s is not a valid page box type", t) + return + } + + pb.X = pb.X * f.k + pb.Y = pb.Y * f.k + pb.Wd = (pb.Wd * f.k) + pb.X + pb.Ht = (pb.Ht * f.k) + pb.Y + + if f.page > 0 { + f.pageBoxes[f.page][t] = pb + } + + // always override. page defaults are supplied in addPage function + f.defPageBoxes[t] = pb +} + +// SetPageBox sets the page box for the current page, and any following pages. +// Allowable types are trim, trimbox, crop, cropbox, bleed, bleedbox, art and +// artbox box types are case insensitive. +func (f *Fpdf) SetPageBox(t string, x, y, wd, ht float64) { + f.SetPageBoxRec(t, PageBox{SizeType{Wd: wd, Ht: ht}, PointType{X: x, Y: y}}) +} + +// SetPage sets the current page to that of a valid page in the PDF document. +// pageNum is one-based. The SetPage() example demonstrates this method. +func (f *Fpdf) SetPage(pageNum int) { + if (pageNum > 0) && (pageNum < len(f.pages)) { + f.page = pageNum + } +} + +// PageCount returns the number of pages currently in the document. Since page +// numbers in gofpdf are one-based, the page count is the same as the page +// number of the current last page. +func (f *Fpdf) PageCount() int { + return len(f.pages) - 1 +} + +// SetFontLocation sets the location in the file system of the font and font +// definition files. +func (f *Fpdf) SetFontLocation(fontDirStr string) { + f.fontpath = fontDirStr +} + +// SetFontLoader sets a loader used to read font files (.json and .z) from an +// arbitrary source. If a font loader has been specified, it is used to load +// the named font resources when AddFont() is called. If this operation fails, +// an attempt is made to load the resources from the configured font directory +// (see SetFontLocation()). +func (f *Fpdf) SetFontLoader(loader FontLoader) { + f.fontLoader = loader +} + +// SetHeaderFuncMode sets the function that lets the application render the +// page header. See SetHeaderFunc() for more details. The value for homeMode +// should be set to true to have the current position set to the left and top +// margin after the header function is called. +func (f *Fpdf) SetHeaderFuncMode(fnc func(), homeMode bool) { + f.headerFnc = fnc + f.headerHomeMode = homeMode +} + +// SetHeaderFunc sets the function that lets the application render the page +// header. The specified function is automatically called by AddPage() and +// should not be called directly by the application. The implementation in Fpdf +// is empty, so you have to provide an appropriate function if you want page +// headers. fnc will typically be a closure that has access to the Fpdf +// instance and other document generation variables. +// +// A header is a convenient place to put background content that repeats on +// each page such as a watermark. When this is done, remember to reset the X +// and Y values so the normal content begins where expected. Including a +// watermark on each page is demonstrated in the example for TransformRotate. +// +// This method is demonstrated in the example for AddPage(). +func (f *Fpdf) SetHeaderFunc(fnc func()) { + f.headerFnc = fnc +} + +// SetFooterFunc sets the function that lets the application render the page +// footer. The specified function is automatically called by AddPage() and +// Close() and should not be called directly by the application. The +// implementation in Fpdf is empty, so you have to provide an appropriate +// function if you want page footers. fnc will typically be a closure that has +// access to the Fpdf instance and other document generation variables. See +// SetFooterFuncLpi for a similar function that passes a last page indicator. +// +// This method is demonstrated in the example for AddPage(). +func (f *Fpdf) SetFooterFunc(fnc func()) { + f.footerFnc = fnc + f.footerFncLpi = nil +} + +// SetFooterFuncLpi sets the function that lets the application render the page +// footer. The specified function is automatically called by AddPage() and +// Close() and should not be called directly by the application. It is passed a +// boolean that is true if the last page of the document is being rendered. The +// implementation in Fpdf is empty, so you have to provide an appropriate +// function if you want page footers. fnc will typically be a closure that has +// access to the Fpdf instance and other document generation variables. +func (f *Fpdf) SetFooterFuncLpi(fnc func(lastPage bool)) { + f.footerFncLpi = fnc + f.footerFnc = nil +} + +// SetTopMargin defines the top margin. The method can be called before +// creating the first page. +func (f *Fpdf) SetTopMargin(margin float64) { + f.tMargin = margin +} + +// SetRightMargin defines the right margin. The method can be called before +// creating the first page. +func (f *Fpdf) SetRightMargin(margin float64) { + f.rMargin = margin +} + +// GetAutoPageBreak returns true if automatic pages breaks are enabled, false +// otherwise. This is followed by the triggering limit from the bottom of the +// page. This value applies only if automatic page breaks are enabled. +func (f *Fpdf) GetAutoPageBreak() (auto bool, margin float64) { + auto = f.autoPageBreak + margin = f.bMargin + return +} + +// SetAutoPageBreak enables or disables the automatic page breaking mode. When +// enabling, the second parameter is the distance from the bottom of the page +// that defines the triggering limit. By default, the mode is on and the margin +// is 2 cm. +func (f *Fpdf) SetAutoPageBreak(auto bool, margin float64) { + f.autoPageBreak = auto + f.bMargin = margin + f.pageBreakTrigger = f.h - margin +} + +// SetDisplayMode sets advisory display directives for the document viewer. +// Pages can be displayed entirely on screen, occupy the full width of the +// window, use real size, be scaled by a specific zooming factor or use viewer +// default (configured in the Preferences menu of Adobe Reader). The page +// layout can be specified so that pages are displayed individually or in +// pairs. +// +// zoomStr can be "fullpage" to display the entire page on screen, "fullwidth" +// to use maximum width of window, "real" to use real size (equivalent to 100% +// zoom) or "default" to use viewer default mode. +// +// layoutStr can be "single" (or "SinglePage") to display one page at once, +// "continuous" (or "OneColumn") to display pages continuously, "two" (or +// "TwoColumnLeft") to display two pages on two columns with odd-numbered pages +// on the left, or "TwoColumnRight" to display two pages on two columns with +// odd-numbered pages on the right, or "TwoPageLeft" to display pages two at a +// time with odd-numbered pages on the left, or "TwoPageRight" to display pages +// two at a time with odd-numbered pages on the right, or "default" to use +// viewer default mode. +func (f *Fpdf) SetDisplayMode(zoomStr, layoutStr string) { + if f.err != nil { + return + } + if layoutStr == "" { + layoutStr = "default" + } + switch zoomStr { + case "fullpage", "fullwidth", "real", "default": + f.zoomMode = zoomStr + default: + f.err = fmt.Errorf("incorrect zoom display mode: %s", zoomStr) + return + } + switch layoutStr { + case "single", "continuous", "two", "default", "SinglePage", "OneColumn", + "TwoColumnLeft", "TwoColumnRight", "TwoPageLeft", "TwoPageRight": + f.layoutMode = layoutStr + default: + f.err = fmt.Errorf("incorrect layout display mode: %s", layoutStr) + return + } +} + +// SetDefaultCompression controls the default setting of the internal +// compression flag. See SetCompression() for more details. Compression is on +// by default. +func SetDefaultCompression(compress bool) { + gl.noCompress = !compress +} + +// SetCompression activates or deactivates page compression with zlib. When +// activated, the internal representation of each page is compressed, which +// leads to a compression ratio of about 2 for the resulting document. +// Compression is on by default. +func (f *Fpdf) SetCompression(compress bool) { + f.compress = compress +} + +// SetTitle defines the title of the document. isUTF8 indicates if the string +// is encoded in ISO-8859-1 (false) or UTF-8 (true). +func (f *Fpdf) SetTitle(titleStr string, isUTF8 bool) { + if isUTF8 { + titleStr = utf8toutf16(titleStr) + } + f.title = titleStr +} + +// SetSubject defines the subject of the document. isUTF8 indicates if the +// string is encoded in ISO-8859-1 (false) or UTF-8 (true). +func (f *Fpdf) SetSubject(subjectStr string, isUTF8 bool) { + if isUTF8 { + subjectStr = utf8toutf16(subjectStr) + } + f.subject = subjectStr +} + +// SetAuthor defines the author of the document. isUTF8 indicates if the string +// is encoded in ISO-8859-1 (false) or UTF-8 (true). +func (f *Fpdf) SetAuthor(authorStr string, isUTF8 bool) { + if isUTF8 { + authorStr = utf8toutf16(authorStr) + } + f.author = authorStr +} + +// SetKeywords defines the keywords of the document. keywordStr is a +// space-delimited string, for example "invoice August". isUTF8 indicates if +// the string is encoded +func (f *Fpdf) SetKeywords(keywordsStr string, isUTF8 bool) { + if isUTF8 { + keywordsStr = utf8toutf16(keywordsStr) + } + f.keywords = keywordsStr +} + +// SetCreator defines the creator of the document. isUTF8 indicates if the +// string is encoded in ISO-8859-1 (false) or UTF-8 (true). +func (f *Fpdf) SetCreator(creatorStr string, isUTF8 bool) { + if isUTF8 { + creatorStr = utf8toutf16(creatorStr) + } + f.creator = creatorStr +} + +// SetXmpMetadata defines XMP metadata that will be embedded with the document. +func (f *Fpdf) SetXmpMetadata(xmpStream []byte) { + f.xmp = xmpStream +} + +// AliasNbPages defines an alias for the total number of pages. It will be +// substituted as the document is closed. An empty string is replaced with the +// string "{nb}". +// +// See the example for AddPage() for a demonstration of this method. +func (f *Fpdf) AliasNbPages(aliasStr string) { + if aliasStr == "" { + aliasStr = "{nb}" + } + f.aliasNbPagesStr = aliasStr +} + +// RTL enables right-to-left mode +func (f *Fpdf) RTL() { + f.isRTL = true +} + +// LTR disables right-to-left mode +func (f *Fpdf) LTR() { + f.isRTL = false +} + +// open begins a document +func (f *Fpdf) open() { + f.state = 1 +} + +// Close terminates the PDF document. It is not necessary to call this method +// explicitly because Output(), OutputAndClose() and OutputFileAndClose() do it +// automatically. If the document contains no page, AddPage() is called to +// prevent the generation of an invalid document. +func (f *Fpdf) Close() { + if f.err == nil { + if f.clipNest > 0 { + f.err = fmt.Errorf("clip procedure must be explicitly ended") + } else if f.transformNest > 0 { + f.err = fmt.Errorf("transformation procedure must be explicitly ended") + } + } + if f.err != nil { + return + } + if f.state == 3 { + return + } + if f.page == 0 { + f.AddPage() + if f.err != nil { + return + } + } + // Page footer + f.inFooter = true + if f.footerFnc != nil { + f.footerFnc() + } else if f.footerFncLpi != nil { + f.footerFncLpi(true) + } + f.inFooter = false + + // Close page + f.endpage() + // Close document + f.enddoc() + return +} + +// PageSize returns the width and height of the specified page in the units +// established in New(). These return values are followed by the unit of +// measure itself. If pageNum is zero or otherwise out of bounds, it returns +// the default page size, that is, the size of the page that would be added by +// AddPage(). +func (f *Fpdf) PageSize(pageNum int) (wd, ht float64, unitStr string) { + sz, ok := f.pageSizes[pageNum] + if ok { + sz.Wd, sz.Ht = sz.Wd/f.k, sz.Ht/f.k + } else { + sz = f.defPageSize // user units + } + return sz.Wd, sz.Ht, f.unitStr +} + +// AddPageFormat adds a new page with non-default orientation or size. See +// AddPage() for more details. +// +// See New() for a description of orientationStr. +// +// size specifies the size of the new page in the units established in New(). +// +// The PageSize() example demonstrates this method. +func (f *Fpdf) AddPageFormat(orientationStr string, size SizeType) { + if f.err != nil { + return + } + if f.page != len(f.pages)-1 { + f.page = len(f.pages) - 1 + } + if f.state == 0 { + f.open() + } + familyStr := f.fontFamily + style := f.fontStyle + if f.underline { + style += "U" + } + fontsize := f.fontSizePt + lw := f.lineWidth + dc := f.color.draw + fc := f.color.fill + tc := f.color.text + cf := f.colorFlag + + if f.page > 0 { + f.inFooter = true + // Page footer avoid double call on footer. + if f.footerFnc != nil { + f.footerFnc() + + } else if f.footerFncLpi != nil { + f.footerFncLpi(false) // not last page. + } + f.inFooter = false + // Close page + f.endpage() + } + // Start new page + f.beginpage(orientationStr, size) + // Set line cap style to current value + // f.out("2 J") + f.outf("%d J", f.capStyle) + // Set line join style to current value + f.outf("%d j", f.joinStyle) + // Set line width + f.lineWidth = lw + f.outf("%.2f w", lw*f.k) + // Set dash pattern + if len(f.dashArray) > 0 { + f.outputDashPattern() + } + // Set font + if familyStr != "" { + f.SetFont(familyStr, style, fontsize) + if f.err != nil { + return + } + } + // Set colors + f.color.draw = dc + if dc.str != "0 G" { + f.out(dc.str) + } + f.color.fill = fc + if fc.str != "0 g" { + f.out(fc.str) + } + f.color.text = tc + f.colorFlag = cf + // Page header + if f.headerFnc != nil { + f.inHeader = true + f.headerFnc() + f.inHeader = false + if f.headerHomeMode { + f.SetHomeXY() + } + } + // Restore line width + if f.lineWidth != lw { + f.lineWidth = lw + f.outf("%.2f w", lw*f.k) + } + // Restore font + if familyStr != "" { + f.SetFont(familyStr, style, fontsize) + if f.err != nil { + return + } + } + // Restore colors + if f.color.draw.str != dc.str { + f.color.draw = dc + f.out(dc.str) + } + if f.color.fill.str != fc.str { + f.color.fill = fc + f.out(fc.str) + } + f.color.text = tc + f.colorFlag = cf + return +} + +// AddPage adds a new page to the document. If a page is already present, the +// Footer() method is called first to output the footer. Then the page is +// added, the current position set to the top-left corner according to the left +// and top margins, and Header() is called to display the header. +// +// The font which was set before calling is automatically restored. There is no +// need to call SetFont() again if you want to continue with the same font. The +// same is true for colors and line width. +// +// The origin of the coordinate system is at the top-left corner and increasing +// ordinates go downwards. +// +// See AddPageFormat() for a version of this method that allows the page size +// and orientation to be different than the default. +func (f *Fpdf) AddPage() { + if f.err != nil { + return + } + // dbg("AddPage") + f.AddPageFormat(f.defOrientation, f.defPageSize) + return +} + +// PageNo returns the current page number. +// +// See the example for AddPage() for a demonstration of this method. +func (f *Fpdf) PageNo() int { + return f.page +} + +func colorComp(v int) (int, float64) { + if v < 0 { + v = 0 + } else if v > 255 { + v = 255 + } + return v, float64(v) / 255.0 +} + +func rgbColorValue(r, g, b int, grayStr, fullStr string) (clr colorType) { + clr.ir, clr.r = colorComp(r) + clr.ig, clr.g = colorComp(g) + clr.ib, clr.b = colorComp(b) + clr.mode = colorModeRGB + clr.gray = clr.ir == clr.ig && clr.r == clr.b + if len(grayStr) > 0 { + if clr.gray { + clr.str = sprintf("%.3f %s", clr.r, grayStr) + } else { + clr.str = sprintf("%.3f %.3f %.3f %s", clr.r, clr.g, clr.b, fullStr) + } + } else { + clr.str = sprintf("%.3f %.3f %.3f", clr.r, clr.g, clr.b) + } + return +} + +// SetDrawColor defines the color used for all drawing operations (lines, +// rectangles and cell borders). It is expressed in RGB components (0 - 255). +// The method can be called before the first page is created. The value is +// retained from page to page. +func (f *Fpdf) SetDrawColor(r, g, b int) { + f.setDrawColor(r, g, b) +} + +func (f *Fpdf) setDrawColor(r, g, b int) { + f.color.draw = rgbColorValue(r, g, b, "G", "RG") + if f.page > 0 { + f.out(f.color.draw.str) + } +} + +// GetDrawColor returns the most recently set draw color as RGB components (0 - +// 255). This will not be the current value if a draw color of some other type +// (for example, spot) has been more recently set. +func (f *Fpdf) GetDrawColor() (int, int, int) { + return f.color.draw.ir, f.color.draw.ig, f.color.draw.ib +} + +// SetFillColor defines the color used for all filling operations (filled +// rectangles and cell backgrounds). It is expressed in RGB components (0 +// -255). The method can be called before the first page is created and the +// value is retained from page to page. +func (f *Fpdf) SetFillColor(r, g, b int) { + f.setFillColor(r, g, b) +} + +func (f *Fpdf) setFillColor(r, g, b int) { + f.color.fill = rgbColorValue(r, g, b, "g", "rg") + f.colorFlag = f.color.fill.str != f.color.text.str + if f.page > 0 { + f.out(f.color.fill.str) + } +} + +// GetFillColor returns the most recently set fill color as RGB components (0 - +// 255). This will not be the current value if a fill color of some other type +// (for example, spot) has been more recently set. +func (f *Fpdf) GetFillColor() (int, int, int) { + return f.color.fill.ir, f.color.fill.ig, f.color.fill.ib +} + +// SetTextColor defines the color used for text. It is expressed in RGB +// components (0 - 255). The method can be called before the first page is +// created. The value is retained from page to page. +func (f *Fpdf) SetTextColor(r, g, b int) { + f.setTextColor(r, g, b) +} + +func (f *Fpdf) setTextColor(r, g, b int) { + f.color.text = rgbColorValue(r, g, b, "g", "rg") + f.colorFlag = f.color.fill.str != f.color.text.str +} + +// GetTextColor returns the most recently set text color as RGB components (0 - +// 255). This will not be the current value if a text color of some other type +// (for example, spot) has been more recently set. +func (f *Fpdf) GetTextColor() (int, int, int) { + return f.color.text.ir, f.color.text.ig, f.color.text.ib +} + +// GetStringWidth returns the length of a string in user units. A font must be +// currently selected. +func (f *Fpdf) GetStringWidth(s string) float64 { + if f.err != nil { + return 0 + } + w := f.GetStringSymbolWidth(s) + return float64(w) * f.fontSize / 1000 +} + +// GetStringSymbolWidth returns the length of a string in glyf units. A font must be +// currently selected. +func (f *Fpdf) GetStringSymbolWidth(s string) int { + if f.err != nil { + return 0 + } + w := 0 + if f.isCurrentUTF8 { + unicode := []rune(s) + for _, char := range unicode { + intChar := int(char) + if len(f.currentFont.Cw) >= intChar && f.currentFont.Cw[intChar] > 0 { + if f.currentFont.Cw[intChar] != 65535 { + w += f.currentFont.Cw[intChar] + } + } else if f.currentFont.Desc.MissingWidth != 0 { + w += f.currentFont.Desc.MissingWidth + } else { + w += 500 + } + } + } else { + for _, ch := range []byte(s) { + if ch == 0 { + break + } + w += f.currentFont.Cw[ch] + } + } + return w +} + +// SetLineWidth defines the line width. By default, the value equals 0.2 mm. +// The method can be called before the first page is created. The value is +// retained from page to page. +func (f *Fpdf) SetLineWidth(width float64) { + f.setLineWidth(width) +} + +func (f *Fpdf) setLineWidth(width float64) { + f.lineWidth = width + if f.page > 0 { + f.outf("%.2f w", width*f.k) + } +} + +// GetLineWidth returns the current line thickness. +func (f *Fpdf) GetLineWidth() float64 { + return f.lineWidth +} + +// SetLineCapStyle defines the line cap style. styleStr should be "butt", +// "round" or "square". A square style projects from the end of the line. The +// method can be called before the first page is created. The value is +// retained from page to page. +func (f *Fpdf) SetLineCapStyle(styleStr string) { + var capStyle int + switch styleStr { + case "round": + capStyle = 1 + case "square": + capStyle = 2 + default: + capStyle = 0 + } + f.capStyle = capStyle + if f.page > 0 { + f.outf("%d J", f.capStyle) + } +} + +// SetLineJoinStyle defines the line cap style. styleStr should be "miter", +// "round" or "bevel". The method can be called before the first page +// is created. The value is retained from page to page. +func (f *Fpdf) SetLineJoinStyle(styleStr string) { + var joinStyle int + switch styleStr { + case "round": + joinStyle = 1 + case "bevel": + joinStyle = 2 + default: + joinStyle = 0 + } + f.joinStyle = joinStyle + if f.page > 0 { + f.outf("%d j", f.joinStyle) + } +} + +// SetDashPattern sets the dash pattern that is used to draw lines. The +// dashArray elements are numbers that specify the lengths, in units +// established in New(), of alternating dashes and gaps. The dash phase +// specifies the distance into the dash pattern at which to start the dash. The +// dash pattern is retained from page to page. Call this method with an empty +// array to restore solid line drawing. +// +// The Beziergon() example demonstrates this method. +func (f *Fpdf) SetDashPattern(dashArray []float64, dashPhase float64) { + scaled := make([]float64, len(dashArray)) + for i, value := range dashArray { + scaled[i] = value * f.k + } + dashPhase *= f.k + + f.dashArray = scaled + f.dashPhase = dashPhase + if f.page > 0 { + f.outputDashPattern() + } + +} + +func (f *Fpdf) outputDashPattern() { + var buf bytes.Buffer + buf.WriteByte('[') + for i, value := range f.dashArray { + if i > 0 { + buf.WriteByte(' ') + } + buf.WriteString(strconv.FormatFloat(value, 'f', 2, 64)) + } + buf.WriteString("] ") + buf.WriteString(strconv.FormatFloat(f.dashPhase, 'f', 2, 64)) + buf.WriteString(" d") + f.outbuf(&buf) +} + +// Line draws a line between points (x1, y1) and (x2, y2) using the current +// draw color, line width and cap style. +func (f *Fpdf) Line(x1, y1, x2, y2 float64) { + f.outf("%.2f %.2f m %.2f %.2f l S", x1*f.k, (f.h-y1)*f.k, x2*f.k, (f.h-y2)*f.k) +} + +// fillDrawOp corrects path painting operators +func fillDrawOp(styleStr string) (opStr string) { + switch strings.ToUpper(styleStr) { + case "", "D": + // Stroke the path. + opStr = "S" + case "F": + // fill the path, using the nonzero winding number rule + opStr = "f" + case "F*": + // fill the path, using the even-odd rule + opStr = "f*" + case "FD", "DF": + // fill and then stroke the path, using the nonzero winding number rule + opStr = "B" + case "FD*", "DF*": + // fill and then stroke the path, using the even-odd rule + opStr = "B*" + default: + opStr = styleStr + } + return +} + +// Rect outputs a rectangle of width w and height h with the upper left corner +// positioned at point (x, y). +// +// It can be drawn (border only), filled (with no border) or both. styleStr can +// be "F" for filled, "D" for outlined only, or "DF" or "FD" for outlined and +// filled. An empty string will be replaced with "D". Drawing uses the current +// draw color and line width centered on the rectangle's perimeter. Filling +// uses the current fill color. +func (f *Fpdf) Rect(x, y, w, h float64, styleStr string) { + f.outf("%.2f %.2f %.2f %.2f re %s", x*f.k, (f.h-y)*f.k, w*f.k, -h*f.k, fillDrawOp(styleStr)) +} + +// Circle draws a circle centered on point (x, y) with radius r. +// +// styleStr can be "F" for filled, "D" for outlined only, or "DF" or "FD" for +// outlined and filled. An empty string will be replaced with "D". Drawing uses +// the current draw color and line width centered on the circle's perimeter. +// Filling uses the current fill color. +func (f *Fpdf) Circle(x, y, r float64, styleStr string) { + f.Ellipse(x, y, r, r, 0, styleStr) +} + +// Ellipse draws an ellipse centered at point (x, y). rx and ry specify its +// horizontal and vertical radii. +// +// degRotate specifies the counter-clockwise angle in degrees that the ellipse +// will be rotated. +// +// styleStr can be "F" for filled, "D" for outlined only, or "DF" or "FD" for +// outlined and filled. An empty string will be replaced with "D". Drawing uses +// the current draw color and line width centered on the ellipse's perimeter. +// Filling uses the current fill color. +// +// The Circle() example demonstrates this method. +func (f *Fpdf) Ellipse(x, y, rx, ry, degRotate float64, styleStr string) { + f.arc(x, y, rx, ry, degRotate, 0, 360, styleStr, false) +} + +// Polygon draws a closed figure defined by a series of vertices specified by +// points. The x and y fields of the points use the units established in New(). +// The last point in the slice will be implicitly joined to the first to close +// the polygon. +// +// styleStr can be "F" for filled, "D" for outlined only, or "DF" or "FD" for +// outlined and filled. An empty string will be replaced with "D". Drawing uses +// the current draw color and line width centered on the ellipse's perimeter. +// Filling uses the current fill color. +func (f *Fpdf) Polygon(points []PointType, styleStr string) { + if len(points) > 2 { + for j, pt := range points { + if j == 0 { + f.point(pt.X, pt.Y) + } else { + f.outf("%.5f %.5f l ", pt.X*f.k, (f.h-pt.Y)*f.k) + } + } + f.outf("%.5f %.5f l ", points[0].X*f.k, (f.h-points[0].Y)*f.k) + f.DrawPath(styleStr) + } +} + +// Beziergon draws a closed figure defined by a series of cubic Bézier curve +// segments. The first point in the slice defines the starting point of the +// figure. Each three following points p1, p2, p3 represent a curve segment to +// the point p3 using p1 and p2 as the Bézier control points. +// +// The x and y fields of the points use the units established in New(). +// +// styleStr can be "F" for filled, "D" for outlined only, or "DF" or "FD" for +// outlined and filled. An empty string will be replaced with "D". Drawing uses +// the current draw color and line width centered on the ellipse's perimeter. +// Filling uses the current fill color. +func (f *Fpdf) Beziergon(points []PointType, styleStr string) { + + // Thanks, Robert Lillack, for contributing this function. + + if len(points) < 4 { + return + } + f.point(points[0].XY()) + + points = points[1:] + for len(points) >= 3 { + cx0, cy0 := points[0].XY() + cx1, cy1 := points[1].XY() + x1, y1 := points[2].XY() + f.curve(cx0, cy0, cx1, cy1, x1, y1) + points = points[3:] + } + + f.DrawPath(styleStr) +} + +// point outputs current point +func (f *Fpdf) point(x, y float64) { + f.outf("%.2f %.2f m", x*f.k, (f.h-y)*f.k) +} + +// curve outputs a single cubic Bézier curve segment from current point +func (f *Fpdf) curve(cx0, cy0, cx1, cy1, x, y float64) { + // Thanks, Robert Lillack, for straightening this out + f.outf("%.5f %.5f %.5f %.5f %.5f %.5f c", cx0*f.k, (f.h-cy0)*f.k, cx1*f.k, + (f.h-cy1)*f.k, x*f.k, (f.h-y)*f.k) +} + +// Curve draws a single-segment quadratic Bézier curve. The curve starts at +// the point (x0, y0) and ends at the point (x1, y1). The control point (cx, +// cy) specifies the curvature. At the start point, the curve is tangent to the +// straight line between the start point and the control point. At the end +// point, the curve is tangent to the straight line between the end point and +// the control point. +// +// styleStr can be "F" for filled, "D" for outlined only, or "DF" or "FD" for +// outlined and filled. An empty string will be replaced with "D". Drawing uses +// the current draw color, line width, and cap style centered on the curve's +// path. Filling uses the current fill color. +// +// The Circle() example demonstrates this method. +func (f *Fpdf) Curve(x0, y0, cx, cy, x1, y1 float64, styleStr string) { + f.point(x0, y0) + f.outf("%.5f %.5f %.5f %.5f v %s", cx*f.k, (f.h-cy)*f.k, x1*f.k, (f.h-y1)*f.k, + fillDrawOp(styleStr)) +} + +// CurveCubic draws a single-segment cubic Bézier curve. This routine performs +// the same function as CurveBezierCubic() but has a nonstandard argument order. +// It is retained to preserve backward compatibility. +func (f *Fpdf) CurveCubic(x0, y0, cx0, cy0, x1, y1, cx1, cy1 float64, styleStr string) { + // f.point(x0, y0) + // f.outf("%.5f %.5f %.5f %.5f %.5f %.5f c %s", cx0*f.k, (f.h-cy0)*f.k, + // cx1*f.k, (f.h-cy1)*f.k, x1*f.k, (f.h-y1)*f.k, fillDrawOp(styleStr)) + f.CurveBezierCubic(x0, y0, cx0, cy0, cx1, cy1, x1, y1, styleStr) +} + +// CurveBezierCubic draws a single-segment cubic Bézier curve. The curve starts at +// the point (x0, y0) and ends at the point (x1, y1). The control points (cx0, +// cy0) and (cx1, cy1) specify the curvature. At the start point, the curve is +// tangent to the straight line between the start point and the control point +// (cx0, cy0). At the end point, the curve is tangent to the straight line +// between the end point and the control point (cx1, cy1). +// +// styleStr can be "F" for filled, "D" for outlined only, or "DF" or "FD" for +// outlined and filled. An empty string will be replaced with "D". Drawing uses +// the current draw color, line width, and cap style centered on the curve's +// path. Filling uses the current fill color. +// +// This routine performs the same function as CurveCubic() but uses standard +// argument order. +// +// The Circle() example demonstrates this method. +func (f *Fpdf) CurveBezierCubic(x0, y0, cx0, cy0, cx1, cy1, x1, y1 float64, styleStr string) { + f.point(x0, y0) + f.outf("%.5f %.5f %.5f %.5f %.5f %.5f c %s", cx0*f.k, (f.h-cy0)*f.k, + cx1*f.k, (f.h-cy1)*f.k, x1*f.k, (f.h-y1)*f.k, fillDrawOp(styleStr)) +} + +// Arc draws an elliptical arc centered at point (x, y). rx and ry specify its +// horizontal and vertical radii. +// +// degRotate specifies the angle that the arc will be rotated. degStart and +// degEnd specify the starting and ending angle of the arc. All angles are +// specified in degrees and measured counter-clockwise from the 3 o'clock +// position. +// +// styleStr can be "F" for filled, "D" for outlined only, or "DF" or "FD" for +// outlined and filled. An empty string will be replaced with "D". Drawing uses +// the current draw color, line width, and cap style centered on the arc's +// path. Filling uses the current fill color. +// +// The Circle() example demonstrates this method. +func (f *Fpdf) Arc(x, y, rx, ry, degRotate, degStart, degEnd float64, styleStr string) { + f.arc(x, y, rx, ry, degRotate, degStart, degEnd, styleStr, false) +} + +// GetAlpha returns the alpha blending channel, which consists of the +// alpha transparency value and the blend mode. See SetAlpha for more +// details. +func (f *Fpdf) GetAlpha() (alpha float64, blendModeStr string) { + return f.alpha, f.blendMode +} + +// SetAlpha sets the alpha blending channel. The blending effect applies to +// text, drawings and images. +// +// alpha must be a value between 0.0 (fully transparent) to 1.0 (fully opaque). +// Values outside of this range result in an error. +// +// blendModeStr must be one of "Normal", "Multiply", "Screen", "Overlay", +// "Darken", "Lighten", "ColorDodge", "ColorBurn","HardLight", "SoftLight", +// "Difference", "Exclusion", "Hue", "Saturation", "Color", or "Luminosity". An +// empty string is replaced with "Normal". +// +// To reset normal rendering after applying a blending mode, call this method +// with alpha set to 1.0 and blendModeStr set to "Normal". +func (f *Fpdf) SetAlpha(alpha float64, blendModeStr string) { + if f.err != nil { + return + } + var bl blendModeType + switch blendModeStr { + case "Normal", "Multiply", "Screen", "Overlay", + "Darken", "Lighten", "ColorDodge", "ColorBurn", "HardLight", "SoftLight", + "Difference", "Exclusion", "Hue", "Saturation", "Color", "Luminosity": + bl.modeStr = blendModeStr + case "": + bl.modeStr = "Normal" + default: + f.err = fmt.Errorf("unrecognized blend mode \"%s\"", blendModeStr) + return + } + if alpha < 0.0 || alpha > 1.0 { + f.err = fmt.Errorf("alpha value (0.0 - 1.0) is out of range: %.3f", alpha) + return + } + f.alpha = alpha + f.blendMode = blendModeStr + alphaStr := sprintf("%.3f", alpha) + keyStr := sprintf("%s %s", alphaStr, blendModeStr) + pos, ok := f.blendMap[keyStr] + if !ok { + pos = len(f.blendList) // at least 1 + f.blendList = append(f.blendList, blendModeType{alphaStr, alphaStr, blendModeStr, 0}) + f.blendMap[keyStr] = pos + } + f.outf("/GS%d gs", pos) +} + +func (f *Fpdf) gradientClipStart(x, y, w, h float64) { + // Save current graphic state and set clipping area + f.outf("q %.2f %.2f %.2f %.2f re W n", x*f.k, (f.h-y)*f.k, w*f.k, -h*f.k) + // Set up transformation matrix for gradient + f.outf("%.5f 0 0 %.5f %.5f %.5f cm", w*f.k, h*f.k, x*f.k, (f.h-(y+h))*f.k) +} + +func (f *Fpdf) gradientClipEnd() { + // Restore previous graphic state + f.out("Q") +} + +func (f *Fpdf) gradient(tp, r1, g1, b1, r2, g2, b2 int, x1, y1, x2, y2, r float64) { + pos := len(f.gradientList) + clr1 := rgbColorValue(r1, g1, b1, "", "") + clr2 := rgbColorValue(r2, g2, b2, "", "") + f.gradientList = append(f.gradientList, gradientType{tp, clr1.str, clr2.str, + x1, y1, x2, y2, r, 0}) + f.outf("/Sh%d sh", pos) +} + +// LinearGradient draws a rectangular area with a blending of one color to +// another. The rectangle is of width w and height h. Its upper left corner is +// positioned at point (x, y). +// +// Each color is specified with three component values, one each for red, green +// and blue. The values range from 0 to 255. The first color is specified by +// (r1, g1, b1) and the second color by (r2, g2, b2). +// +// The blending is controlled with a gradient vector that uses normalized +// coordinates in which the lower left corner is position (0, 0) and the upper +// right corner is (1, 1). The vector's origin and destination are specified by +// the points (x1, y1) and (x2, y2). In a linear gradient, blending occurs +// perpendicularly to the vector. The vector does not necessarily need to be +// anchored on the rectangle edge. Color 1 is used up to the origin of the +// vector and color 2 is used beyond the vector's end point. Between the points +// the colors are gradually blended. +func (f *Fpdf) LinearGradient(x, y, w, h float64, r1, g1, b1, r2, g2, b2 int, x1, y1, x2, y2 float64) { + f.gradientClipStart(x, y, w, h) + f.gradient(2, r1, g1, b1, r2, g2, b2, x1, y1, x2, y2, 0) + f.gradientClipEnd() +} + +// RadialGradient draws a rectangular area with a blending of one color to +// another. The rectangle is of width w and height h. Its upper left corner is +// positioned at point (x, y). +// +// Each color is specified with three component values, one each for red, green +// and blue. The values range from 0 to 255. The first color is specified by +// (r1, g1, b1) and the second color by (r2, g2, b2). +// +// The blending is controlled with a point and a circle, both specified with +// normalized coordinates in which the lower left corner of the rendered +// rectangle is position (0, 0) and the upper right corner is (1, 1). Color 1 +// begins at the origin point specified by (x1, y1). Color 2 begins at the +// circle specified by the center point (x2, y2) and radius r. Colors are +// gradually blended from the origin to the circle. The origin and the circle's +// center do not necessarily have to coincide, but the origin must be within +// the circle to avoid rendering problems. +// +// The LinearGradient() example demonstrates this method. +func (f *Fpdf) RadialGradient(x, y, w, h float64, r1, g1, b1, r2, g2, b2 int, x1, y1, x2, y2, r float64) { + f.gradientClipStart(x, y, w, h) + f.gradient(3, r1, g1, b1, r2, g2, b2, x1, y1, x2, y2, r) + f.gradientClipEnd() +} + +// ClipRect begins a rectangular clipping operation. The rectangle is of width +// w and height h. Its upper left corner is positioned at point (x, y). outline +// is true to draw a border with the current draw color and line width centered +// on the rectangle's perimeter. Only the outer half of the border will be +// shown. After calling this method, all rendering operations (for example, +// Image(), LinearGradient(), etc) will be clipped by the specified rectangle. +// Call ClipEnd() to restore unclipped operations. +// +// This ClipText() example demonstrates this method. +func (f *Fpdf) ClipRect(x, y, w, h float64, outline bool) { + f.clipNest++ + f.outf("q %.2f %.2f %.2f %.2f re W %s", x*f.k, (f.h-y)*f.k, w*f.k, -h*f.k, strIf(outline, "S", "n")) +} + +// ClipText begins a clipping operation in which rendering is confined to the +// character string specified by txtStr. The origin (x, y) is on the left of +// the first character at the baseline. The current font is used. outline is +// true to draw a border with the current draw color and line width centered on +// the perimeters of the text characters. Only the outer half of the border +// will be shown. After calling this method, all rendering operations (for +// example, Image(), LinearGradient(), etc) will be clipped. Call ClipEnd() to +// restore unclipped operations. +func (f *Fpdf) ClipText(x, y float64, txtStr string, outline bool) { + f.clipNest++ + f.outf("q BT %.5f %.5f Td %d Tr (%s) Tj ET", x*f.k, (f.h-y)*f.k, intIf(outline, 5, 7), f.escape(txtStr)) +} + +func (f *Fpdf) clipArc(x1, y1, x2, y2, x3, y3 float64) { + h := f.h + f.outf("%.5f %.5f %.5f %.5f %.5f %.5f c ", x1*f.k, (h-y1)*f.k, + x2*f.k, (h-y2)*f.k, x3*f.k, (h-y3)*f.k) +} + +// ClipRoundedRect begins a rectangular clipping operation. The rectangle is of +// width w and height h. Its upper left corner is positioned at point (x, y). +// The rounded corners of the rectangle are specified by radius r. outline is +// true to draw a border with the current draw color and line width centered on +// the rectangle's perimeter. Only the outer half of the border will be shown. +// After calling this method, all rendering operations (for example, Image(), +// LinearGradient(), etc) will be clipped by the specified rectangle. Call +// ClipEnd() to restore unclipped operations. +// +// This ClipText() example demonstrates this method. +func (f *Fpdf) ClipRoundedRect(x, y, w, h, r float64, outline bool) { + f.clipNest++ + k := f.k + hp := f.h + myArc := (4.0 / 3.0) * (math.Sqrt2 - 1.0) + f.outf("q %.5f %.5f m", (x+r)*k, (hp-y)*k) + xc := x + w - r + yc := y + r + f.outf("%.5f %.5f l", xc*k, (hp-y)*k) + f.clipArc(xc+r*myArc, yc-r, xc+r, yc-r*myArc, xc+r, yc) + xc = x + w - r + yc = y + h - r + f.outf("%.5f %.5f l", (x+w)*k, (hp-yc)*k) + f.clipArc(xc+r, yc+r*myArc, xc+r*myArc, yc+r, xc, yc+r) + xc = x + r + yc = y + h - r + f.outf("%.5f %.5f l", xc*k, (hp-(y+h))*k) + f.clipArc(xc-r*myArc, yc+r, xc-r, yc+r*myArc, xc-r, yc) + xc = x + r + yc = y + r + f.outf("%.5f %.5f l", x*k, (hp-yc)*k) + f.clipArc(xc-r, yc-r*myArc, xc-r*myArc, yc-r, xc, yc-r) + f.outf(" W %s", strIf(outline, "S", "n")) +} + +// ClipEllipse begins an elliptical clipping operation. The ellipse is centered +// at (x, y). Its horizontal and vertical radii are specified by rx and ry. +// outline is true to draw a border with the current draw color and line width +// centered on the ellipse's perimeter. Only the outer half of the border will +// be shown. After calling this method, all rendering operations (for example, +// Image(), LinearGradient(), etc) will be clipped by the specified ellipse. +// Call ClipEnd() to restore unclipped operations. +// +// This ClipText() example demonstrates this method. +func (f *Fpdf) ClipEllipse(x, y, rx, ry float64, outline bool) { + f.clipNest++ + lx := (4.0 / 3.0) * rx * (math.Sqrt2 - 1) + ly := (4.0 / 3.0) * ry * (math.Sqrt2 - 1) + k := f.k + h := f.h + f.outf("q %.5f %.5f m %.5f %.5f %.5f %.5f %.5f %.5f c", + (x+rx)*k, (h-y)*k, + (x+rx)*k, (h-(y-ly))*k, + (x+lx)*k, (h-(y-ry))*k, + x*k, (h-(y-ry))*k) + f.outf("%.5f %.5f %.5f %.5f %.5f %.5f c", + (x-lx)*k, (h-(y-ry))*k, + (x-rx)*k, (h-(y-ly))*k, + (x-rx)*k, (h-y)*k) + f.outf("%.5f %.5f %.5f %.5f %.5f %.5f c", + (x-rx)*k, (h-(y+ly))*k, + (x-lx)*k, (h-(y+ry))*k, + x*k, (h-(y+ry))*k) + f.outf("%.5f %.5f %.5f %.5f %.5f %.5f c W %s", + (x+lx)*k, (h-(y+ry))*k, + (x+rx)*k, (h-(y+ly))*k, + (x+rx)*k, (h-y)*k, + strIf(outline, "S", "n")) +} + +// ClipCircle begins a circular clipping operation. The circle is centered at +// (x, y) and has radius r. outline is true to draw a border with the current +// draw color and line width centered on the circle's perimeter. Only the outer +// half of the border will be shown. After calling this method, all rendering +// operations (for example, Image(), LinearGradient(), etc) will be clipped by +// the specified circle. Call ClipEnd() to restore unclipped operations. +// +// The ClipText() example demonstrates this method. +func (f *Fpdf) ClipCircle(x, y, r float64, outline bool) { + f.ClipEllipse(x, y, r, r, outline) +} + +// ClipPolygon begins a clipping operation within a polygon. The figure is +// defined by a series of vertices specified by points. The x and y fields of +// the points use the units established in New(). The last point in the slice +// will be implicitly joined to the first to close the polygon. outline is true +// to draw a border with the current draw color and line width centered on the +// polygon's perimeter. Only the outer half of the border will be shown. After +// calling this method, all rendering operations (for example, Image(), +// LinearGradient(), etc) will be clipped by the specified polygon. Call +// ClipEnd() to restore unclipped operations. +// +// The ClipText() example demonstrates this method. +func (f *Fpdf) ClipPolygon(points []PointType, outline bool) { + f.clipNest++ + var s fmtBuffer + h := f.h + k := f.k + s.printf("q ") + for j, pt := range points { + s.printf("%.5f %.5f %s ", pt.X*k, (h-pt.Y)*k, strIf(j == 0, "m", "l")) + } + s.printf("h W %s", strIf(outline, "S", "n")) + f.out(s.String()) +} + +// ClipEnd ends a clipping operation that was started with a call to +// ClipRect(), ClipRoundedRect(), ClipText(), ClipEllipse(), ClipCircle() or +// ClipPolygon(). Clipping operations can be nested. The document cannot be +// successfully output while a clipping operation is active. +// +// The ClipText() example demonstrates this method. +func (f *Fpdf) ClipEnd() { + if f.err == nil { + if f.clipNest > 0 { + f.clipNest-- + f.out("Q") + } else { + f.err = fmt.Errorf("error attempting to end clip operation out of sequence") + } + } +} + +// AddFont imports a TrueType, OpenType or Type1 font and makes it available. +// It is necessary to generate a font definition file first with the makefont +// utility. It is not necessary to call this function for the core PDF fonts +// (courier, helvetica, times, zapfdingbats). +// +// The JSON definition file (and the font file itself when embedding) must be +// present in the font directory. If it is not found, the error "Could not +// include font definition file" is set. +// +// family specifies the font family. The name can be chosen arbitrarily. If it +// is a standard family name, it will override the corresponding font. This +// string is used to subsequently set the font with the SetFont method. +// +// style specifies the font style. Acceptable values are (case insensitive) the +// empty string for regular style, "B" for bold, "I" for italic, or "BI" or +// "IB" for bold and italic combined. +// +// fileStr specifies the base name with ".json" extension of the font +// definition file to be added. The file will be loaded from the font directory +// specified in the call to New() or SetFontLocation(). +func (f *Fpdf) AddFont(familyStr, styleStr, fileStr string) { + f.addFont(familyStr, styleStr, fileStr, false) +} + +// AddUTF8Font imports a TrueType font with utf-8 symbols and makes it available. +// It is necessary to generate a font definition file first with the makefont +// utility. It is not necessary to call this function for the core PDF fonts +// (courier, helvetica, times, zapfdingbats). +// +// The JSON definition file (and the font file itself when embedding) must be +// present in the font directory. If it is not found, the error "Could not +// include font definition file" is set. +// +// family specifies the font family. The name can be chosen arbitrarily. If it +// is a standard family name, it will override the corresponding font. This +// string is used to subsequently set the font with the SetFont method. +// +// style specifies the font style. Acceptable values are (case insensitive) the +// empty string for regular style, "B" for bold, "I" for italic, or "BI" or +// "IB" for bold and italic combined. +// +// fileStr specifies the base name with ".json" extension of the font +// definition file to be added. The file will be loaded from the font directory +// specified in the call to New() or SetFontLocation(). +func (f *Fpdf) AddUTF8Font(familyStr, styleStr, fileStr string) { + f.addFont(familyStr, styleStr, fileStr, true) +} + +func (f *Fpdf) addFont(familyStr, styleStr, fileStr string, isUTF8 bool) { + if fileStr == "" { + if isUTF8 { + fileStr = strings.Replace(familyStr, " ", "", -1) + strings.ToLower(styleStr) + ".ttf" + } else { + fileStr = strings.Replace(familyStr, " ", "", -1) + strings.ToLower(styleStr) + ".json" + } + } + if isUTF8 { + fontKey := getFontKey(familyStr, styleStr) + _, ok := f.fonts[fontKey] + if ok { + return + } + var ttfStat os.FileInfo + var err error + fileStr = path.Join(f.fontpath, fileStr) + ttfStat, err = os.Stat(fileStr) + if err != nil { + f.SetError(err) + return + } + originalSize := ttfStat.Size() + Type := "UTF8" + var utf8Bytes []byte + utf8Bytes, err = ioutil.ReadFile(fileStr) + if err != nil { + f.SetError(err) + return + } + reader := fileReader{readerPosition: 0, array: utf8Bytes} + utf8File := newUTF8Font(&reader) + err = utf8File.parseFile() + if err != nil { + f.SetError(err) + return + } + + desc := FontDescType{ + Ascent: int(utf8File.Ascent), + Descent: int(utf8File.Descent), + CapHeight: utf8File.CapHeight, + Flags: utf8File.Flags, + FontBBox: utf8File.Bbox, + ItalicAngle: utf8File.ItalicAngle, + StemV: utf8File.StemV, + MissingWidth: round(utf8File.DefaultWidth), + } + + var sbarr map[int]int + if f.aliasNbPagesStr == "" { + sbarr = makeSubsetRange(57) + } else { + sbarr = makeSubsetRange(32) + } + def := fontDefType{ + Tp: Type, + Name: fontKey, + Desc: desc, + Up: int(round(utf8File.UnderlinePosition)), + Ut: round(utf8File.UnderlineThickness), + Cw: utf8File.CharWidths, + usedRunes: sbarr, + File: fileStr, + utf8File: utf8File, + } + def.i, _ = generateFontID(def) + f.fonts[fontKey] = def + f.fontFiles[fontKey] = fontFileType{ + length1: originalSize, + fontType: "UTF8", + } + f.fontFiles[fileStr] = fontFileType{ + fontType: "UTF8", + } + } else { + if f.fontLoader != nil { + reader, err := f.fontLoader.Open(fileStr) + if err == nil { + f.AddFontFromReader(familyStr, styleStr, reader) + if closer, ok := reader.(io.Closer); ok { + closer.Close() + } + return + } + } + + fileStr = path.Join(f.fontpath, fileStr) + file, err := os.Open(fileStr) + if err != nil { + f.err = err + return + } + defer file.Close() + + f.AddFontFromReader(familyStr, styleStr, file) + } +} + +func makeSubsetRange(end int) map[int]int { + answer := make(map[int]int) + for i := 0; i < end; i++ { + answer[i] = 0 + } + return answer +} + +// AddFontFromBytes imports a TrueType, OpenType or Type1 font from static +// bytes within the executable and makes it available for use in the generated +// document. +// +// family specifies the font family. The name can be chosen arbitrarily. If it +// is a standard family name, it will override the corresponding font. This +// string is used to subsequently set the font with the SetFont method. +// +// style specifies the font style. Acceptable values are (case insensitive) the +// empty string for regular style, "B" for bold, "I" for italic, or "BI" or +// "IB" for bold and italic combined. +// +// jsonFileBytes contain all bytes of JSON file. +// +// zFileBytes contain all bytes of Z file. +func (f *Fpdf) AddFontFromBytes(familyStr, styleStr string, jsonFileBytes, zFileBytes []byte) { + f.addFontFromBytes(familyStr, styleStr, jsonFileBytes, zFileBytes, nil) +} + +// AddUTF8FontFromBytes imports a TrueType font with utf-8 symbols from static +// bytes within the executable and makes it available for use in the generated +// document. +// +// family specifies the font family. The name can be chosen arbitrarily. If it +// is a standard family name, it will override the corresponding font. This +// string is used to subsequently set the font with the SetFont method. +// +// style specifies the font style. Acceptable values are (case insensitive) the +// empty string for regular style, "B" for bold, "I" for italic, or "BI" or +// "IB" for bold and italic combined. +// +// jsonFileBytes contain all bytes of JSON file. +// +// zFileBytes contain all bytes of Z file. +func (f *Fpdf) AddUTF8FontFromBytes(familyStr, styleStr string, utf8Bytes []byte) { + f.addFontFromBytes(familyStr, styleStr, nil, nil, utf8Bytes) +} + +func (f *Fpdf) addFontFromBytes(familyStr, styleStr string, jsonFileBytes, zFileBytes, utf8Bytes []byte) { + if f.err != nil { + return + } + + // load font key + var ok bool + fontkey := getFontKey(familyStr, styleStr) + _, ok = f.fonts[fontkey] + + if ok { + return + } + + if utf8Bytes != nil { + + // if styleStr == "IB" { + // styleStr = "BI" + // } + + Type := "UTF8" + reader := fileReader{readerPosition: 0, array: utf8Bytes} + + utf8File := newUTF8Font(&reader) + + err := utf8File.parseFile() + if err != nil { + fmt.Printf("get metrics Error: %e\n", err) + return + } + desc := FontDescType{ + Ascent: int(utf8File.Ascent), + Descent: int(utf8File.Descent), + CapHeight: utf8File.CapHeight, + Flags: utf8File.Flags, + FontBBox: utf8File.Bbox, + ItalicAngle: utf8File.ItalicAngle, + StemV: utf8File.StemV, + MissingWidth: round(utf8File.DefaultWidth), + } + + var sbarr map[int]int + if f.aliasNbPagesStr == "" { + sbarr = makeSubsetRange(57) + } else { + sbarr = makeSubsetRange(32) + } + def := fontDefType{ + Tp: Type, + Name: fontkey, + Desc: desc, + Up: int(round(utf8File.UnderlinePosition)), + Ut: round(utf8File.UnderlineThickness), + Cw: utf8File.CharWidths, + utf8File: utf8File, + usedRunes: sbarr, + } + def.i, _ = generateFontID(def) + f.fonts[fontkey] = def + } else { + // load font definitions + var info fontDefType + err := json.Unmarshal(jsonFileBytes, &info) + + if err != nil { + f.err = err + } + + if f.err != nil { + return + } + + if info.i, err = generateFontID(info); err != nil { + f.err = err + return + } + + // search existing encodings + if len(info.Diff) > 0 { + n := -1 + + for j, str := range f.diffs { + if str == info.Diff { + n = j + 1 + break + } + } + + if n < 0 { + f.diffs = append(f.diffs, info.Diff) + n = len(f.diffs) + } + + info.DiffN = n + } + + // embed font + if len(info.File) > 0 { + if info.Tp == "TrueType" { + f.fontFiles[info.File] = fontFileType{ + length1: int64(info.OriginalSize), + embedded: true, + content: zFileBytes, + } + } else { + f.fontFiles[info.File] = fontFileType{ + length1: int64(info.Size1), + length2: int64(info.Size2), + embedded: true, + content: zFileBytes, + } + } + } + + f.fonts[fontkey] = info + } +} + +// getFontKey is used by AddFontFromReader and GetFontDesc +func getFontKey(familyStr, styleStr string) string { + familyStr = strings.ToLower(familyStr) + styleStr = strings.ToUpper(styleStr) + if styleStr == "IB" { + styleStr = "BI" + } + return familyStr + styleStr +} + +// AddFontFromReader imports a TrueType, OpenType or Type1 font and makes it +// available using a reader that satisifies the io.Reader interface. See +// AddFont for details about familyStr and styleStr. +func (f *Fpdf) AddFontFromReader(familyStr, styleStr string, r io.Reader) { + if f.err != nil { + return + } + // dbg("Adding family [%s], style [%s]", familyStr, styleStr) + var ok bool + fontkey := getFontKey(familyStr, styleStr) + _, ok = f.fonts[fontkey] + if ok { + return + } + var info fontDefType + info = f.loadfont(r) + if f.err != nil { + return + } + if len(info.Diff) > 0 { + // Search existing encodings + n := -1 + for j, str := range f.diffs { + if str == info.Diff { + n = j + 1 + break + } + } + if n < 0 { + f.diffs = append(f.diffs, info.Diff) + n = len(f.diffs) + } + info.DiffN = n + } + // dbg("font [%s], type [%s]", info.File, info.Tp) + if len(info.File) > 0 { + // Embedded font + if info.Tp == "TrueType" { + f.fontFiles[info.File] = fontFileType{length1: int64(info.OriginalSize)} + } else { + f.fontFiles[info.File] = fontFileType{length1: int64(info.Size1), length2: int64(info.Size2)} + } + } + f.fonts[fontkey] = info + return +} + +// GetFontDesc returns the font descriptor, which can be used for +// example to find the baseline of a font. If familyStr is empty +// current font descriptor will be returned. +// See FontDescType for documentation about the font descriptor. +// See AddFont for details about familyStr and styleStr. +func (f *Fpdf) GetFontDesc(familyStr, styleStr string) FontDescType { + if familyStr == "" { + return f.currentFont.Desc + } + return f.fonts[getFontKey(familyStr, styleStr)].Desc +} + +// SetFont sets the font used to print character strings. It is mandatory to +// call this method at least once before printing text or the resulting +// document will not be valid. +// +// The font can be either a standard one or a font added via the AddFont() +// method or AddFontFromReader() method. Standard fonts use the Windows +// encoding cp1252 (Western Europe). +// +// The method can be called before the first page is created and the font is +// kept from page to page. If you just wish to change the current font size, it +// is simpler to call SetFontSize(). +// +// Note: the font definition file must be accessible. An error is set if the +// file cannot be read. +// +// familyStr specifies the font family. It can be either a name defined by +// AddFont(), AddFontFromReader() or one of the standard families (case +// insensitive): "Courier" for fixed-width, "Helvetica" or "Arial" for sans +// serif, "Times" for serif, "Symbol" or "ZapfDingbats" for symbolic. +// +// styleStr can be "B" (bold), "I" (italic), "U" (underscore) or any +// combination. The default value (specified with an empty string) is regular. +// Bold and italic styles do not apply to Symbol and ZapfDingbats. +// +// size is the font size measured in points. The default value is the current +// size. If no size has been specified since the beginning of the document, the +// value taken is 12. +func (f *Fpdf) SetFont(familyStr, styleStr string, size float64) { + // dbg("SetFont x %.2f, lMargin %.2f", f.x, f.lMargin) + + if f.err != nil { + return + } + // dbg("SetFont") + var ok bool + if familyStr == "" { + familyStr = f.fontFamily + } else { + familyStr = strings.ToLower(familyStr) + } + styleStr = strings.ToUpper(styleStr) + f.underline = strings.Contains(styleStr, "U") + if f.underline { + styleStr = strings.Replace(styleStr, "U", "", -1) + } + if styleStr == "IB" { + styleStr = "BI" + } + if size == 0.0 { + size = f.fontSizePt + } + + // Test if font is already loaded + fontKey := familyStr + styleStr + _, ok = f.fonts[fontKey] + if !ok { + // Test if one of the core fonts + if familyStr == "arial" { + familyStr = "helvetica" + } + _, ok = f.coreFonts[familyStr] + if ok { + if familyStr == "symbol" { + familyStr = "zapfdingbats" + } + if familyStr == "zapfdingbats" { + styleStr = "" + } + fontKey = familyStr + styleStr + _, ok = f.fonts[fontKey] + if !ok { + rdr := f.coreFontReader(familyStr, styleStr) + if f.err == nil { + f.AddFontFromReader(familyStr, styleStr, rdr) + } + if f.err != nil { + return + } + } + } else { + f.err = fmt.Errorf("undefined font: %s %s", familyStr, styleStr) + return + } + } + // Select it + f.fontFamily = familyStr + f.fontStyle = styleStr + f.fontSizePt = size + f.fontSize = size / f.k + f.currentFont = f.fonts[fontKey] + if f.currentFont.Tp == "UTF8" { + f.isCurrentUTF8 = true + } else { + f.isCurrentUTF8 = false + } + if f.page > 0 { + f.outf("BT /F%s %.2f Tf ET", f.currentFont.i, f.fontSizePt) + } + return +} + +// SetFontStyle sets the style of the current font. See also SetFont() +func (f *Fpdf) SetFontStyle(styleStr string) { + f.SetFont(f.fontFamily, styleStr, f.fontSizePt) +} + +// SetFontSize defines the size of the current font. Size is specified in +// points (1/ 72 inch). See also SetFontUnitSize(). +func (f *Fpdf) SetFontSize(size float64) { + f.fontSizePt = size + f.fontSize = size / f.k + if f.page > 0 { + f.outf("BT /F%s %.2f Tf ET", f.currentFont.i, f.fontSizePt) + } +} + +// SetFontUnitSize defines the size of the current font. Size is specified in +// the unit of measure specified in New(). See also SetFontSize(). +func (f *Fpdf) SetFontUnitSize(size float64) { + f.fontSizePt = size * f.k + f.fontSize = size + if f.page > 0 { + f.outf("BT /F%s %.2f Tf ET", f.currentFont.i, f.fontSizePt) + } +} + +// GetFontSize returns the size of the current font in points followed by the +// size in the unit of measure specified in New(). The second value can be used +// as a line height value in drawing operations. +func (f *Fpdf) GetFontSize() (ptSize, unitSize float64) { + return f.fontSizePt, f.fontSize +} + +// AddLink creates a new internal link and returns its identifier. An internal +// link is a clickable area which directs to another place within the document. +// The identifier can then be passed to Cell(), Write(), Image() or Link(). The +// destination is defined with SetLink(). +func (f *Fpdf) AddLink() int { + f.links = append(f.links, intLinkType{}) + return len(f.links) - 1 +} + +// SetLink defines the page and position a link points to. See AddLink(). +func (f *Fpdf) SetLink(link int, y float64, page int) { + if y == -1 { + y = f.y + } + if page == -1 { + page = f.page + } + f.links[link] = intLinkType{page, y} +} + +// newLink adds a new clickable link on current page +func (f *Fpdf) newLink(x, y, w, h float64, link int, linkStr string) { + // linkList, ok := f.pageLinks[f.page] + // if !ok { + // linkList = make([]linkType, 0, 8) + // f.pageLinks[f.page] = linkList + // } + f.pageLinks[f.page] = append(f.pageLinks[f.page], + linkType{x * f.k, f.hPt - y*f.k, w * f.k, h * f.k, link, linkStr}) +} + +// Link puts a link on a rectangular area of the page. Text or image links are +// generally put via Cell(), Write() or Image(), but this method can be useful +// for instance to define a clickable area inside an image. link is the value +// returned by AddLink(). +func (f *Fpdf) Link(x, y, w, h float64, link int) { + f.newLink(x, y, w, h, link, "") +} + +// LinkString puts a link on a rectangular area of the page. Text or image +// links are generally put via Cell(), Write() or Image(), but this method can +// be useful for instance to define a clickable area inside an image. linkStr +// is the target URL. +func (f *Fpdf) LinkString(x, y, w, h float64, linkStr string) { + f.newLink(x, y, w, h, 0, linkStr) +} + +// Bookmark sets a bookmark that will be displayed in a sidebar outline. txtStr +// is the title of the bookmark. level specifies the level of the bookmark in +// the outline; 0 is the top level, 1 is just below, and so on. y specifies the +// vertical position of the bookmark destination in the current page; -1 +// indicates the current position. +func (f *Fpdf) Bookmark(txtStr string, level int, y float64) { + if y == -1 { + y = f.y + } + f.outlines = append(f.outlines, outlineType{text: txtStr, level: level, y: y, p: f.PageNo(), prev: -1, last: -1, next: -1, first: -1}) +} + +// Text prints a character string. The origin (x, y) is on the left of the +// first character at the baseline. This method permits a string to be placed +// precisely on the page, but it is usually easier to use Cell(), MultiCell() +// or Write() which are the standard methods to print text. +func (f *Fpdf) Text(x, y float64, txtStr string) { + var txt2 string + if f.isCurrentUTF8 { + if f.isRTL { + txtStr = reverseText(txtStr) + x -= f.GetStringWidth(txtStr) + } + txt2 = f.escape(utf8toutf16(txtStr, false)) + for _, uni := range []rune(txtStr) { + f.currentFont.usedRunes[int(uni)] = int(uni) + } + } else { + txt2 = f.escape(txtStr) + } + s := sprintf("BT %.2f %.2f Td (%s) Tj ET", x*f.k, (f.h-y)*f.k, txt2) + if f.underline && txtStr != "" { + s += " " + f.dounderline(x, y, txtStr) + } + if f.colorFlag { + s = sprintf("q %s %s Q", f.color.text.str, s) + } + f.out(s) +} + +// SetWordSpacing sets spacing between words of following text. See the +// WriteAligned() example for a demonstration of its use. +func (f *Fpdf) SetWordSpacing(space float64) { + f.out(sprintf("%.5f Tw", space*f.k)) +} + +// SetAcceptPageBreakFunc allows the application to control where page breaks +// occur. +// +// fnc is an application function (typically a closure) that is called by the +// library whenever a page break condition is met. The break is issued if true +// is returned. The default implementation returns a value according to the +// mode selected by SetAutoPageBreak. The function provided should not be +// called by the application. +// +// See the example for SetLeftMargin() to see how this function can be used to +// manage multiple columns. +func (f *Fpdf) SetAcceptPageBreakFunc(fnc func() bool) { + f.acceptPageBreak = fnc +} + +// CellFormat prints a rectangular cell with optional borders, background color +// and character string. The upper-left corner of the cell corresponds to the +// current position. The text can be aligned or centered. After the call, the +// current position moves to the right or to the next line. It is possible to +// put a link on the text. +// +// An error will be returned if a call to SetFont() has not already taken +// place before this method is called. +// +// If automatic page breaking is enabled and the cell goes beyond the limit, a +// page break is done before outputting. +// +// w and h specify the width and height of the cell. If w is 0, the cell +// extends up to the right margin. Specifying 0 for h will result in no output, +// but the current position will be advanced by w. +// +// txtStr specifies the text to display. +// +// borderStr specifies how the cell border will be drawn. An empty string +// indicates no border, "1" indicates a full border, and one or more of "L", +// "T", "R" and "B" indicate the left, top, right and bottom sides of the +// border. +// +// ln indicates where the current position should go after the call. Possible +// values are 0 (to the right), 1 (to the beginning of the next line), and 2 +// (below). Putting 1 is equivalent to putting 0 and calling Ln() just after. +// +// alignStr specifies how the text is to be positioned within the cell. +// Horizontal alignment is controlled by including "L", "C" or "R" (left, +// center, right) in alignStr. Vertical alignment is controlled by including +// "T", "M", "B" or "A" (top, middle, bottom, baseline) in alignStr. The default +// alignment is left middle. +// +// fill is true to paint the cell background or false to leave it transparent. +// +// link is the identifier returned by AddLink() or 0 for no internal link. +// +// linkStr is a target URL or empty for no external link. A non--zero value for +// link takes precedence over linkStr. +func (f *Fpdf) CellFormat(w, h float64, txtStr, borderStr string, ln int, + alignStr string, fill bool, link int, linkStr string) { + // dbg("CellFormat. h = %.2f, borderStr = %s", h, borderStr) + if f.err != nil { + return + } + + if f.currentFont.Name == "" { + f.err = fmt.Errorf("font has not been set; unable to render text") + return + } + + borderStr = strings.ToUpper(borderStr) + k := f.k + if f.y+h > f.pageBreakTrigger && !f.inHeader && !f.inFooter && f.acceptPageBreak() { + // Automatic page break + x := f.x + ws := f.ws + // dbg("auto page break, x %.2f, ws %.2f", x, ws) + if ws > 0 { + f.ws = 0 + f.out("0 Tw") + } + f.AddPageFormat(f.curOrientation, f.curPageSize) + if f.err != nil { + return + } + f.x = x + if ws > 0 { + f.ws = ws + f.outf("%.3f Tw", ws*k) + } + } + if w == 0 { + w = f.w - f.rMargin - f.x + } + var s fmtBuffer + if fill || borderStr == "1" { + var op string + if fill { + if borderStr == "1" { + op = "B" + // dbg("border is '1', fill") + } else { + op = "f" + // dbg("border is empty, fill") + } + } else { + // dbg("border is '1', no fill") + op = "S" + } + /// dbg("(CellFormat) f.x %.2f f.k %.2f", f.x, f.k) + s.printf("%.2f %.2f %.2f %.2f re %s ", f.x*k, (f.h-f.y)*k, w*k, -h*k, op) + } + if len(borderStr) > 0 && borderStr != "1" { + // fmt.Printf("border is '%s', no fill\n", borderStr) + x := f.x + y := f.y + left := x * k + top := (f.h - y) * k + right := (x + w) * k + bottom := (f.h - (y + h)) * k + if strings.Contains(borderStr, "L") { + s.printf("%.2f %.2f m %.2f %.2f l S ", left, top, left, bottom) + } + if strings.Contains(borderStr, "T") { + s.printf("%.2f %.2f m %.2f %.2f l S ", left, top, right, top) + } + if strings.Contains(borderStr, "R") { + s.printf("%.2f %.2f m %.2f %.2f l S ", right, top, right, bottom) + } + if strings.Contains(borderStr, "B") { + s.printf("%.2f %.2f m %.2f %.2f l S ", left, bottom, right, bottom) + } + } + if len(txtStr) > 0 { + var dx, dy float64 + // Horizontal alignment + switch { + case strings.Contains(alignStr, "R"): + dx = w - f.cMargin - f.GetStringWidth(txtStr) + case strings.Contains(alignStr, "C"): + dx = (w - f.GetStringWidth(txtStr)) / 2 + default: + dx = f.cMargin + } + + // Vertical alignment + switch { + case strings.Contains(alignStr, "T"): + dy = (f.fontSize - h) / 2.0 + case strings.Contains(alignStr, "B"): + dy = (h - f.fontSize) / 2.0 + case strings.Contains(alignStr, "A"): + var descent float64 + d := f.currentFont.Desc + if d.Descent == 0 { + // not defined (standard font?), use average of 19% + descent = -0.19 * f.fontSize + } else { + descent = float64(d.Descent) * f.fontSize / float64(d.Ascent-d.Descent) + } + dy = (h-f.fontSize)/2.0 - descent + default: + dy = 0 + } + if f.colorFlag { + s.printf("q %s ", f.color.text.str) + } + //If multibyte, Tw has no effect - do word spacing using an adjustment before each space + if (f.ws != 0 || alignStr == "J") && f.isCurrentUTF8 { // && f.ws != 0 + if f.isRTL { + txtStr = reverseText(txtStr) + } + wmax := int(math.Ceil((w - 2*f.cMargin) * 1000 / f.fontSize)) + for _, uni := range []rune(txtStr) { + f.currentFont.usedRunes[int(uni)] = int(uni) + } + space := f.escape(utf8toutf16(" ", false)) + strSize := f.GetStringSymbolWidth(txtStr) + s.printf("BT 0 Tw %.2f %.2f Td [", (f.x+dx)*k, (f.h-(f.y+.5*h+.3*f.fontSize))*k) + t := strings.Split(txtStr, " ") + shift := float64((wmax - strSize)) / float64(len(t)-1) + numt := len(t) + for i := 0; i < numt; i++ { + tx := t[i] + tx = "(" + f.escape(utf8toutf16(tx, false)) + ")" + s.printf("%s ", tx) + if (i + 1) < numt { + s.printf("%.3f(%s) ", -shift, space) + } + } + s.printf("] TJ ET") + } else { + var txt2 string + if f.isCurrentUTF8 { + if f.isRTL { + txtStr = reverseText(txtStr) + } + txt2 = f.escape(utf8toutf16(txtStr, false)) + for _, uni := range []rune(txtStr) { + f.currentFont.usedRunes[int(uni)] = int(uni) + } + } else { + + txt2 = strings.Replace(txtStr, "\\", "\\\\", -1) + txt2 = strings.Replace(txt2, "(", "\\(", -1) + txt2 = strings.Replace(txt2, ")", "\\)", -1) + } + bt := (f.x + dx) * k + td := (f.h - (f.y + dy + .5*h + .3*f.fontSize)) * k + s.printf("BT %.2f %.2f Td (%s)Tj ET", bt, td, txt2) + //BT %.2F %.2F Td (%s) Tj ET',(f.x+dx)*k,(f.h-(f.y+.5*h+.3*f.FontSize))*k,txt2); + } + + if f.underline { + s.printf(" %s", f.dounderline(f.x+dx, f.y+dy+.5*h+.3*f.fontSize, txtStr)) + } + if f.colorFlag { + s.printf(" Q") + } + if link > 0 || len(linkStr) > 0 { + f.newLink(f.x+dx, f.y+dy+.5*h-.5*f.fontSize, f.GetStringWidth(txtStr), f.fontSize, link, linkStr) + } + } + str := s.String() + if len(str) > 0 { + f.out(str) + } + f.lasth = h + if ln > 0 { + // Go to next line + f.y += h + if ln == 1 { + f.x = f.lMargin + } + } else { + f.x += w + } + return +} + +// Revert string to use in RTL languages +func reverseText(text string) string { + oldText := []rune(text) + newText := make([]rune, len(oldText)) + length := len(oldText) - 1 + for i, r := range oldText { + newText[length-i] = r + } + return string(newText) +} + +// Cell is a simpler version of CellFormat with no fill, border, links or +// special alignment. +func (f *Fpdf) Cell(w, h float64, txtStr string) { + f.CellFormat(w, h, txtStr, "", 0, "L", false, 0, "") +} + +// Cellf is a simpler printf-style version of CellFormat with no fill, border, +// links or special alignment. See documentation for the fmt package for +// details on fmtStr and args. +func (f *Fpdf) Cellf(w, h float64, fmtStr string, args ...interface{}) { + f.CellFormat(w, h, sprintf(fmtStr, args...), "", 0, "L", false, 0, "") +} + +// SplitLines splits text into several lines using the current font. Each line +// has its length limited to a maximum width given by w. This function can be +// used to determine the total height of wrapped text for vertical placement +// purposes. +// +// This method is useful for codepage-based fonts only. For UTF-8 encoded text, +// use SplitText(). +// +// You can use MultiCell if you want to print a text on several lines in a +// simple way. +func (f *Fpdf) SplitLines(txt []byte, w float64) [][]byte { + // Function contributed by Bruno Michel + lines := [][]byte{} + cw := f.currentFont.Cw + wmax := int(math.Ceil((w - 2*f.cMargin) * 1000 / f.fontSize)) + s := bytes.Replace(txt, []byte("\r"), []byte{}, -1) + nb := len(s) + for nb > 0 && s[nb-1] == '\n' { + nb-- + } + s = s[0:nb] + sep := -1 + i := 0 + j := 0 + l := 0 + for i < nb { + c := s[i] + l += cw[c] + if c == ' ' || c == '\t' || c == '\n' { + sep = i + } + if c == '\n' || l > wmax { + if sep == -1 { + if i == j { + i++ + } + sep = i + } else { + i = sep + 1 + } + lines = append(lines, s[j:sep]) + sep = -1 + j = i + l = 0 + } else { + i++ + } + } + if i != j { + lines = append(lines, s[j:i]) + } + return lines +} + +// MultiCell supports printing text with line breaks. They can be automatic (as +// soon as the text reaches the right border of the cell) or explicit (via the +// \n character). As many cells as necessary are output, one below the other. +// +// Text can be aligned, centered or justified. The cell block can be framed and +// the background painted. See CellFormat() for more details. +// +// The current position after calling MultiCell() is the beginning of the next +// line, equivalent to calling CellFormat with ln equal to 1. +// +// w is the width of the cells. A value of zero indicates cells that reach to +// the right margin. +// +// h indicates the line height of each cell in the unit of measure specified in New(). +func (f *Fpdf) MultiCell(w, h float64, txtStr, borderStr, alignStr string, fill bool) { + // dbg("MultiCell") + if alignStr == "" { + alignStr = "J" + } + cw := f.currentFont.Cw + if w == 0 { + w = f.w - f.rMargin - f.x + } + wmax := int(math.Ceil((w - 2*f.cMargin) * 1000 / f.fontSize)) + s := strings.Replace(txtStr, "\r", "", -1) + + var nb int + if f.isCurrentUTF8 { + nb = len([]rune(s)) + for nb > 0 && []rune(s)[nb-1] == '\n' { + nb-- + s = string([]rune(s)[0:nb]) + } + } else { + nb = len(s) + if nb > 0 && []byte(s)[nb-1] == '\n' { + nb-- + s = s[0:nb] + } + } + // dbg("[%s]\n", s) + var b, b2 string + b = "0" + if len(borderStr) > 0 { + if borderStr == "1" { + borderStr = "LTRB" + b = "LRT" + b2 = "LR" + } else { + b2 = "" + if strings.Contains(borderStr, "L") { + b2 += "L" + } + if strings.Contains(borderStr, "R") { + b2 += "R" + } + if strings.Contains(borderStr, "T") { + b = b2 + "T" + } else { + b = b2 + } + } + } + sep := -1 + i := 0 + j := 0 + l := 0 + ls := 0 + ns := 0 + nl := 1 + for i < nb { + // Get next character + var c rune + if f.isCurrentUTF8 { + c = []rune(s)[i] + } else { + c = rune([]byte(s)[i]) + } + if c == '\n' { + // Explicit line break + if f.ws > 0 { + f.ws = 0 + f.out("0 Tw") + } + + if f.isCurrentUTF8 { + newAlignStr := alignStr + if newAlignStr == "J" { + if f.isRTL { + newAlignStr = "R" + } else { + newAlignStr = "L" + } + } + f.CellFormat(w, h, string([]rune(s)[j:i]), b, 2, newAlignStr, fill, 0, "") + } else { + f.CellFormat(w, h, s[j:i], b, 2, alignStr, fill, 0, "") + } + i++ + sep = -1 + j = i + l = 0 + ns = 0 + nl++ + if len(borderStr) > 0 && nl == 2 { + b = b2 + } + continue + } + if c == ' ' { + sep = i + ls = l + ns++ + } + if cw[int(c)] == 0 { //Marker width 0 used for missing symbols + l += f.currentFont.Desc.MissingWidth + } else if cw[int(c)] != 65535 { //Marker width 65535 used for zero width symbols + l += cw[int(c)] + } + if l > wmax { + // Automatic line break + if sep == -1 { + if i == j { + i++ + } + if f.ws > 0 { + f.ws = 0 + f.out("0 Tw") + } + if f.isCurrentUTF8 { + f.CellFormat(w, h, string([]rune(s)[j:i]), b, 2, alignStr, fill, 0, "") + } else { + f.CellFormat(w, h, s[j:i], b, 2, alignStr, fill, 0, "") + } + } else { + if alignStr == "J" { + if ns > 1 { + f.ws = float64((wmax-ls)/1000) * f.fontSize / float64(ns-1) + } else { + f.ws = 0 + } + f.outf("%.3f Tw", f.ws*f.k) + } + if f.isCurrentUTF8 { + f.CellFormat(w, h, string([]rune(s)[j:sep]), b, 2, alignStr, fill, 0, "") + } else { + f.CellFormat(w, h, s[j:sep], b, 2, alignStr, fill, 0, "") + } + i = sep + 1 + } + sep = -1 + j = i + l = 0 + ns = 0 + nl++ + if len(borderStr) > 0 && nl == 2 { + b = b2 + } + } else { + i++ + } + } + // Last chunk + if f.ws > 0 { + f.ws = 0 + f.out("0 Tw") + } + if len(borderStr) > 0 && strings.Contains(borderStr, "B") { + b += "B" + } + if f.isCurrentUTF8 { + if alignStr == "J" { + if f.isRTL { + alignStr = "R" + } else { + alignStr = "" + } + } + f.CellFormat(w, h, string([]rune(s)[j:i]), b, 2, alignStr, fill, 0, "") + } else { + f.CellFormat(w, h, s[j:i], b, 2, alignStr, fill, 0, "") + } + f.x = f.lMargin +} + +// write outputs text in flowing mode +func (f *Fpdf) write(h float64, txtStr string, link int, linkStr string) { + // dbg("Write") + cw := f.currentFont.Cw + w := f.w - f.rMargin - f.x + wmax := (w - 2*f.cMargin) * 1000 / f.fontSize + s := strings.Replace(txtStr, "\r", "", -1) + var nb int + if f.isCurrentUTF8 { + nb = len([]rune(s)) + if nb == 1 && s == " " { + f.x += f.GetStringWidth(s) + return + } + } else { + nb = len(s) + } + sep := -1 + i := 0 + j := 0 + l := 0.0 + nl := 1 + for i < nb { + // Get next character + var c rune + if f.isCurrentUTF8 { + c = []rune(s)[i] + } else { + c = rune([]byte(s)[i]) + } + if c == '\n' { + // Explicit line break + if f.isCurrentUTF8 { + f.CellFormat(w, h, string([]rune(s)[j:i]), "", 2, "", false, link, linkStr) + } else { + f.CellFormat(w, h, s[j:i], "", 2, "", false, link, linkStr) + } + i++ + sep = -1 + j = i + l = 0.0 + if nl == 1 { + f.x = f.lMargin + w = f.w - f.rMargin - f.x + wmax = (w - 2*f.cMargin) * 1000 / f.fontSize + } + nl++ + continue + } + if c == ' ' { + sep = i + } + l += float64(cw[int(c)]) + if l > wmax { + // Automatic line break + if sep == -1 { + if f.x > f.lMargin { + // Move to next line + f.x = f.lMargin + f.y += h + w = f.w - f.rMargin - f.x + wmax = (w - 2*f.cMargin) * 1000 / f.fontSize + i++ + nl++ + continue + } + if i == j { + i++ + } + if f.isCurrentUTF8 { + f.CellFormat(w, h, string([]rune(s)[j:i]), "", 2, "", false, link, linkStr) + } else { + f.CellFormat(w, h, s[j:i], "", 2, "", false, link, linkStr) + } + } else { + if f.isCurrentUTF8 { + f.CellFormat(w, h, string([]rune(s)[j:sep]), "", 2, "", false, link, linkStr) + } else { + f.CellFormat(w, h, s[j:sep], "", 2, "", false, link, linkStr) + } + i = sep + 1 + } + sep = -1 + j = i + l = 0.0 + if nl == 1 { + f.x = f.lMargin + w = f.w - f.rMargin - f.x + wmax = (w - 2*f.cMargin) * 1000 / f.fontSize + } + nl++ + } else { + i++ + } + } + // Last chunk + if i != j { + if f.isCurrentUTF8 { + f.CellFormat(l/1000*f.fontSize, h, string([]rune(s)[j:]), "", 0, "", false, link, linkStr) + } else { + f.CellFormat(l/1000*f.fontSize, h, s[j:], "", 0, "", false, link, linkStr) + } + } +} + +// Write prints text from the current position. When the right margin is +// reached (or the \n character is met) a line break occurs and text continues +// from the left margin. Upon method exit, the current position is left just at +// the end of the text. +// +// It is possible to put a link on the text. +// +// h indicates the line height in the unit of measure specified in New(). +func (f *Fpdf) Write(h float64, txtStr string) { + f.write(h, txtStr, 0, "") +} + +// Writef is like Write but uses printf-style formatting. See the documentation +// for package fmt for more details on fmtStr and args. +func (f *Fpdf) Writef(h float64, fmtStr string, args ...interface{}) { + f.write(h, sprintf(fmtStr, args...), 0, "") +} + +// WriteLinkString writes text that when clicked launches an external URL. See +// Write() for argument details. +func (f *Fpdf) WriteLinkString(h float64, displayStr, targetStr string) { + f.write(h, displayStr, 0, targetStr) +} + +// WriteLinkID writes text that when clicked jumps to another location in the +// PDF. linkID is an identifier returned by AddLink(). See Write() for argument +// details. +func (f *Fpdf) WriteLinkID(h float64, displayStr string, linkID int) { + f.write(h, displayStr, linkID, "") +} + +// WriteAligned is an implementation of Write that makes it possible to align +// text. +// +// width indicates the width of the box the text will be drawn in. This is in +// the unit of measure specified in New(). If it is set to 0, the bounding box +//of the page will be taken (pageWidth - leftMargin - rightMargin). +// +// lineHeight indicates the line height in the unit of measure specified in +// New(). +// +// alignStr sees to horizontal alignment of the given textStr. The options are +// "L", "C" and "R" (Left, Center, Right). The default is "L". +func (f *Fpdf) WriteAligned(width, lineHeight float64, textStr, alignStr string) { + lMargin, _, rMargin, _ := f.GetMargins() + + if width == 0 { + pageWidth, _ := f.GetPageSize() + width = pageWidth - (lMargin + rMargin) + } + + lines := f.SplitLines([]byte(textStr), width) + + for _, lineBt := range lines { + lineStr := string(lineBt) + lineWidth := f.GetStringWidth(lineStr) + + switch alignStr { + case "C": + f.SetLeftMargin(lMargin + ((width - lineWidth) / 2)) + f.Write(lineHeight, lineStr) + f.SetLeftMargin(lMargin) + case "R": + f.SetLeftMargin(lMargin + (width - lineWidth) - 2.01*f.cMargin) + f.Write(lineHeight, lineStr) + f.SetLeftMargin(lMargin) + default: + f.Write(lineHeight, lineStr) + } + } +} + +// Ln performs a line break. The current abscissa goes back to the left margin +// and the ordinate increases by the amount passed in parameter. A negative +// value of h indicates the height of the last printed cell. +// +// This method is demonstrated in the example for MultiCell. +func (f *Fpdf) Ln(h float64) { + f.x = f.lMargin + if h < 0 { + f.y += f.lasth + } else { + f.y += h + } +} + +// ImageTypeFromMime returns the image type used in various image-related +// functions (for example, Image()) that is associated with the specified MIME +// type. For example, "jpg" is returned if mimeStr is "image/jpeg". An error is +// set if the specified MIME type is not supported. +func (f *Fpdf) ImageTypeFromMime(mimeStr string) (tp string) { + switch mimeStr { + case "image/png": + tp = "png" + case "image/jpg": + tp = "jpg" + case "image/jpeg": + tp = "jpg" + case "image/gif": + tp = "gif" + default: + f.SetErrorf("unsupported image type: %s", mimeStr) + } + return +} + +func (f *Fpdf) imageOut(info *ImageInfoType, x, y, w, h float64, allowNegativeX, flow bool, link int, linkStr string) { + // Automatic width and height calculation if needed + if w == 0 && h == 0 { + // Put image at 96 dpi + w = -96 + h = -96 + } + if w == -1 { + // Set image width to whatever value for dpi we read + // from the image or that was set manually + w = -info.dpi + } + if h == -1 { + // Set image height to whatever value for dpi we read + // from the image or that was set manually + h = -info.dpi + } + if w < 0 { + w = -info.w * 72.0 / w / f.k + } + if h < 0 { + h = -info.h * 72.0 / h / f.k + } + if w == 0 { + w = h * info.w / info.h + } + if h == 0 { + h = w * info.h / info.w + } + // Flowing mode + if flow { + if f.y+h > f.pageBreakTrigger && !f.inHeader && !f.inFooter && f.acceptPageBreak() { + // Automatic page break + x2 := f.x + f.AddPageFormat(f.curOrientation, f.curPageSize) + if f.err != nil { + return + } + f.x = x2 + } + y = f.y + f.y += h + } + if !allowNegativeX { + if x < 0 { + x = f.x + } + } + // dbg("h %.2f", h) + // q 85.04 0 0 NaN 28.35 NaN cm /I2 Do Q + f.outf("q %.5f 0 0 %.5f %.5f %.5f cm /I%s Do Q", w*f.k, h*f.k, x*f.k, (f.h-(y+h))*f.k, info.i) + if link > 0 || len(linkStr) > 0 { + f.newLink(x, y, w, h, link, linkStr) + } +} + +// Image puts a JPEG, PNG or GIF image in the current page. +// +// Deprecated in favor of ImageOptions -- see that function for +// details on the behavior of arguments +func (f *Fpdf) Image(imageNameStr string, x, y, w, h float64, flow bool, tp string, link int, linkStr string) { + options := ImageOptions{ + ReadDpi: false, + ImageType: tp, + } + f.ImageOptions(imageNameStr, x, y, w, h, flow, options, link, linkStr) +} + +// ImageOptions puts a JPEG, PNG or GIF image in the current page. The size it +// will take on the page can be specified in different ways. If both w and h +// are 0, the image is rendered at 96 dpi. If either w or h is zero, it will be +// calculated from the other dimension so that the aspect ratio is maintained. +// If w and/or h are -1, the dpi for that dimension will be read from the +// ImageInfoType object. PNG files can contain dpi information, and if present, +// this information will be populated in the ImageInfoType object and used in +// Width, Height, and Extent calculations. Otherwise, the SetDpi function can +// be used to change the dpi from the default of 72. +// +// If w and h are any other negative value, their absolute values +// indicate their dpi extents. +// +// Supported JPEG formats are 24 bit, 32 bit and gray scale. Supported PNG +// formats are 24 bit, indexed color, and 8 bit indexed gray scale. If a GIF +// image is animated, only the first frame is rendered. Transparency is +// supported. It is possible to put a link on the image. +// +// imageNameStr may be the name of an image as registered with a call to either +// RegisterImageReader() or RegisterImage(). In the first case, the image is +// loaded using an io.Reader. This is generally useful when the image is +// obtained from some other means than as a disk-based file. In the second +// case, the image is loaded as a file. Alternatively, imageNameStr may +// directly specify a sufficiently qualified filename. +// +// However the image is loaded, if it is used more than once only one copy is +// embedded in the file. +// +// If x is negative, the current abscissa is used. +// +// If flow is true, the current y value is advanced after placing the image and +// a page break may be made if necessary. +// +// If link refers to an internal page anchor (that is, it is non-zero; see +// AddLink()), the image will be a clickable internal link. Otherwise, if +// linkStr specifies a URL, the image will be a clickable external link. +func (f *Fpdf) ImageOptions(imageNameStr string, x, y, w, h float64, flow bool, options ImageOptions, link int, linkStr string) { + if f.err != nil { + return + } + info := f.RegisterImageOptions(imageNameStr, options) + if f.err != nil { + return + } + f.imageOut(info, x, y, w, h, options.AllowNegativePosition, flow, link, linkStr) + return +} + +// RegisterImageReader registers an image, reading it from Reader r, adding it +// to the PDF file but not adding it to the page. +// +// This function is now deprecated in favor of RegisterImageOptionsReader +func (f *Fpdf) RegisterImageReader(imgName, tp string, r io.Reader) (info *ImageInfoType) { + options := ImageOptions{ + ReadDpi: false, + ImageType: tp, + } + return f.RegisterImageOptionsReader(imgName, options, r) +} + +// ImageOptions provides a place to hang any options we want to use while +// parsing an image. +// +// ImageType's possible values are (case insensitive): +// "JPG", "JPEG", "PNG" and "GIF". If empty, the type is inferred from +// the file extension. +// +// ReadDpi defines whether to attempt to automatically read the image +// dpi information from the image file. Normally, this should be set +// to true (understanding that not all images will have this info +// available). However, for backwards compatibility with previous +// versions of the API, it defaults to false. +// +// AllowNegativePosition can be set to true in order to prevent the default +// coercion of negative x values to the current x position. +type ImageOptions struct { + ImageType string + ReadDpi bool + AllowNegativePosition bool +} + +// RegisterImageOptionsReader registers an image, reading it from Reader r, adding it +// to the PDF file but not adding it to the page. Use Image() with the same +// name to add the image to the page. Note that tp should be specified in this +// case. +// +// See Image() for restrictions on the image and the options parameters. +func (f *Fpdf) RegisterImageOptionsReader(imgName string, options ImageOptions, r io.Reader) (info *ImageInfoType) { + // Thanks, Ivan Daniluk, for generalizing this code to use the Reader interface. + if f.err != nil { + return + } + info, ok := f.images[imgName] + if ok { + return + } + + // First use of this image, get info + if options.ImageType == "" { + f.err = fmt.Errorf("image type should be specified if reading from custom reader") + return + } + options.ImageType = strings.ToLower(options.ImageType) + if options.ImageType == "jpeg" { + options.ImageType = "jpg" + } + switch options.ImageType { + case "jpg": + info = f.parsejpg(r) + case "png": + info = f.parsepng(r, options.ReadDpi) + case "gif": + info = f.parsegif(r) + default: + f.err = fmt.Errorf("unsupported image type: %s", options.ImageType) + } + if f.err != nil { + return + } + + if info.i, f.err = generateImageID(info); f.err != nil { + return + } + f.images[imgName] = info + + return +} + +// RegisterImage registers an image, adding it to the PDF file but not adding +// it to the page. Use Image() with the same filename to add the image to the +// page. Note that Image() calls this function, so this function is only +// necessary if you need information about the image before placing it. +// +// This function is now deprecated in favor of RegisterImageOptions. +// See Image() for restrictions on the image and the "tp" parameters. +func (f *Fpdf) RegisterImage(fileStr, tp string) (info *ImageInfoType) { + options := ImageOptions{ + ReadDpi: false, + ImageType: tp, + } + return f.RegisterImageOptions(fileStr, options) +} + +// RegisterImageOptions registers an image, adding it to the PDF file but not +// adding it to the page. Use Image() with the same filename to add the image +// to the page. Note that Image() calls this function, so this function is only +// necessary if you need information about the image before placing it. See +// Image() for restrictions on the image and the "tp" parameters. +func (f *Fpdf) RegisterImageOptions(fileStr string, options ImageOptions) (info *ImageInfoType) { + info, ok := f.images[fileStr] + if ok { + return + } + + file, err := os.Open(fileStr) + if err != nil { + f.err = err + return + } + defer file.Close() + + // First use of this image, get info + if options.ImageType == "" { + pos := strings.LastIndex(fileStr, ".") + if pos < 0 { + f.err = fmt.Errorf("image file has no extension and no type was specified: %s", fileStr) + return + } + options.ImageType = fileStr[pos+1:] + } + + return f.RegisterImageOptionsReader(fileStr, options, file) +} + +// GetImageInfo returns information about the registered image specified by +// imageStr. If the image has not been registered, nil is returned. The +// internal error is not modified by this method. +func (f *Fpdf) GetImageInfo(imageStr string) (info *ImageInfoType) { + return f.images[imageStr] +} + +// ImportObjects imports objects from gofpdi into current document +func (f *Fpdf) ImportObjects(objs map[string][]byte) { + for k, v := range objs { + f.importedObjs[k] = v + } +} + +// ImportObjPos imports object hash positions from gofpdi +func (f *Fpdf) ImportObjPos(objPos map[string]map[int]string) { + for k, v := range objPos { + f.importedObjPos[k] = v + } +} + +// putImportedTemplates writes the imported template objects to the PDF +func (f *Fpdf) putImportedTemplates() { + nOffset := f.n + 1 + + // keep track of list of sha1 hashes (to be replaced with integers) + objsIDHash := make([]string, len(f.importedObjs)) + + // actual object data with new id + objsIDData := make([][]byte, len(f.importedObjs)) + + // Populate hash slice and data slice + i := 0 + for k, v := range f.importedObjs { + objsIDHash[i] = k + objsIDData[i] = v + + i++ + } + + // Populate a lookup table to get an object id from a hash + hashToObjID := make(map[string]int, len(f.importedObjs)) + for i = 0; i < len(objsIDHash); i++ { + hashToObjID[objsIDHash[i]] = i + nOffset + } + + // Now, replace hashes inside data with %040d object id + for i = 0; i < len(objsIDData); i++ { + // get hash + hash := objsIDHash[i] + + for pos, h := range f.importedObjPos[hash] { + // Convert object id into a 40 character string padded with spaces + objIDPadded := fmt.Sprintf("%40s", fmt.Sprintf("%d", hashToObjID[h])) + + // Convert objIDPadded into []byte + objIDBytes := []byte(objIDPadded) + + // Replace sha1 hash with object id padded + for j := pos; j < pos+40; j++ { + objsIDData[i][j] = objIDBytes[j-pos] + } + } + + // Save objsIDHash so that procset dictionary has the correct object ids + f.importedTplIDs[hash] = i + nOffset + } + + // Now, put objects + for i = 0; i < len(objsIDData); i++ { + f.newobj() + f.out(string(objsIDData[i])) + } +} + +// UseImportedTemplate uses imported template from gofpdi - draws imported PDF page onto page +func (f *Fpdf) UseImportedTemplate(tplName string, scaleX float64, scaleY float64, tX float64, tY float64) { + f.outf("q 0 J 1 w 0 j 0 G 0 g q %.4F 0 0 %.4F %.4F %.4F cm %s Do Q Q\n", scaleX*f.k, scaleY*f.k, tX*f.k, (tY+f.h)*f.k, tplName) +} + +// ImportTemplates imports gofpdi template names into importedTplObjs - to be included in the procset dictionary +func (f *Fpdf) ImportTemplates(tpls map[string]string) { + for tplName, tplID := range tpls { + f.importedTplObjs[tplName] = tplID + } +} + +// GetConversionRatio returns the conversion ratio based on the unit given when +// creating the PDF. +func (f *Fpdf) GetConversionRatio() float64 { + return f.k +} + +// GetXY returns the abscissa and ordinate of the current position. +// +// Note: the value returned for the abscissa will be affected by the current +// cell margin. To account for this, you may need to either add the value +// returned by GetCellMargin() to it or call SetCellMargin(0) to remove the +// cell margin. +func (f *Fpdf) GetXY() (float64, float64) { + return f.x, f.y +} + +// GetX returns the abscissa of the current position. +// +// Note: the value returned will be affected by the current cell margin. To +// account for this, you may need to either add the value returned by +// GetCellMargin() to it or call SetCellMargin(0) to remove the cell margin. +func (f *Fpdf) GetX() float64 { + return f.x +} + +// SetX defines the abscissa of the current position. If the passed value is +// negative, it is relative to the right of the page. +func (f *Fpdf) SetX(x float64) { + if x >= 0 { + f.x = x + } else { + f.x = f.w + x + } +} + +// GetY returns the ordinate of the current position. +func (f *Fpdf) GetY() float64 { + return f.y +} + +// SetY moves the current abscissa back to the left margin and sets the +// ordinate. If the passed value is negative, it is relative to the bottom of +// the page. +func (f *Fpdf) SetY(y float64) { + // dbg("SetY x %.2f, lMargin %.2f", f.x, f.lMargin) + f.x = f.lMargin + if y >= 0 { + f.y = y + } else { + f.y = f.h + y + } +} + +// SetHomeXY is a convenience method that sets the current position to the left +// and top margins. +func (f *Fpdf) SetHomeXY() { + f.SetY(f.tMargin) + f.SetX(f.lMargin) +} + +// SetXY defines the abscissa and ordinate of the current position. If the +// passed values are negative, they are relative respectively to the right and +// bottom of the page. +func (f *Fpdf) SetXY(x, y float64) { + f.SetY(y) + f.SetX(x) +} + +// SetProtection applies certain constraints on the finished PDF document. +// +// actionFlag is a bitflag that controls various document operations. +// CnProtectPrint allows the document to be printed. CnProtectModify allows a +// document to be modified by a PDF editor. CnProtectCopy allows text and +// images to be copied into the system clipboard. CnProtectAnnotForms allows +// annotations and forms to be added by a PDF editor. These values can be +// combined by or-ing them together, for example, +// CnProtectCopy|CnProtectModify. This flag is advisory; not all PDF readers +// implement the constraints that this argument attempts to control. +// +// userPassStr specifies the password that will need to be provided to view the +// contents of the PDF. The permissions specified by actionFlag will apply. +// +// ownerPassStr specifies the password that will need to be provided to gain +// full access to the document regardless of the actionFlag value. An empty +// string for this argument will be replaced with a random value, effectively +// prohibiting full access to the document. +func (f *Fpdf) SetProtection(actionFlag byte, userPassStr, ownerPassStr string) { + if f.err != nil { + return + } + f.protect.setProtection(actionFlag, userPassStr, ownerPassStr) +} + +// OutputAndClose sends the PDF document to the writer specified by w. This +// method will close both f and w, even if an error is detected and no document +// is produced. +func (f *Fpdf) OutputAndClose(w io.WriteCloser) error { + f.Output(w) + w.Close() + return f.err +} + +// OutputFileAndClose creates or truncates the file specified by fileStr and +// writes the PDF document to it. This method will close f and the newly +// written file, even if an error is detected and no document is produced. +// +// Most examples demonstrate the use of this method. +func (f *Fpdf) OutputFileAndClose(fileStr string) error { + if f.err == nil { + pdfFile, err := os.Create(fileStr) + if err == nil { + f.Output(pdfFile) + pdfFile.Close() + } else { + f.err = err + } + } + return f.err +} + +// Output sends the PDF document to the writer specified by w. No output will +// take place if an error has occurred in the document generation process. w +// remains open after this function returns. After returning, f is in a closed +// state and its methods should not be called. +func (f *Fpdf) Output(w io.Writer) error { + if f.err != nil { + return f.err + } + // dbg("Output") + if f.state < 3 { + f.Close() + } + _, err := f.buffer.WriteTo(w) + if err != nil { + f.err = err + } + return f.err +} + +func (f *Fpdf) getpagesizestr(sizeStr string) (size SizeType) { + if f.err != nil { + return + } + sizeStr = strings.ToLower(sizeStr) + // dbg("Size [%s]", sizeStr) + var ok bool + size, ok = f.stdPageSizes[sizeStr] + if ok { + // dbg("found %s", sizeStr) + size.Wd /= f.k + size.Ht /= f.k + + } else { + f.err = fmt.Errorf("unknown page size %s", sizeStr) + } + return +} + +// GetPageSizeStr returns the SizeType for the given sizeStr (that is A4, A3, etc..) +func (f *Fpdf) GetPageSizeStr(sizeStr string) (size SizeType) { + return f.getpagesizestr(sizeStr) +} + +func (f *Fpdf) _getpagesize(size SizeType) SizeType { + if size.Wd > size.Ht { + size.Wd, size.Ht = size.Ht, size.Wd + } + return size +} + +func (f *Fpdf) beginpage(orientationStr string, size SizeType) { + if f.err != nil { + return + } + f.page++ + // add the default page boxes, if any exist, to the page + f.pageBoxes[f.page] = make(map[string]PageBox) + for box, pb := range f.defPageBoxes { + f.pageBoxes[f.page][box] = pb + } + f.pages = append(f.pages, bytes.NewBufferString("")) + f.pageLinks = append(f.pageLinks, make([]linkType, 0, 0)) + f.state = 2 + f.x = f.lMargin + f.y = f.tMargin + f.fontFamily = "" + // Check page size and orientation + if orientationStr == "" { + orientationStr = f.defOrientation + } else { + orientationStr = strings.ToUpper(orientationStr[0:1]) + } + if orientationStr != f.curOrientation || size.Wd != f.curPageSize.Wd || size.Ht != f.curPageSize.Ht { + // New size or orientation + if orientationStr == "P" { + f.w = size.Wd + f.h = size.Ht + } else { + f.w = size.Ht + f.h = size.Wd + } + f.wPt = f.w * f.k + f.hPt = f.h * f.k + f.pageBreakTrigger = f.h - f.bMargin + f.curOrientation = orientationStr + f.curPageSize = size + } + if orientationStr != f.defOrientation || size.Wd != f.defPageSize.Wd || size.Ht != f.defPageSize.Ht { + f.pageSizes[f.page] = SizeType{f.wPt, f.hPt} + } + return +} + +func (f *Fpdf) endpage() { + f.EndLayer() + f.state = 1 +} + +// Load a font definition file from the given Reader +func (f *Fpdf) loadfont(r io.Reader) (def fontDefType) { + if f.err != nil { + return + } + // dbg("Loading font [%s]", fontStr) + var buf bytes.Buffer + _, err := buf.ReadFrom(r) + if err != nil { + f.err = err + return + } + err = json.Unmarshal(buf.Bytes(), &def) + if err != nil { + f.err = err + return + } + + if def.i, err = generateFontID(def); err != nil { + f.err = err + } + // dump(def) + return +} + +// Escape special characters in strings +func (f *Fpdf) escape(s string) string { + s = strings.Replace(s, "\\", "\\\\", -1) + s = strings.Replace(s, "(", "\\(", -1) + s = strings.Replace(s, ")", "\\)", -1) + s = strings.Replace(s, "\r", "\\r", -1) + return s +} + +// textstring formats a text string +func (f *Fpdf) textstring(s string) string { + if f.protect.encrypted { + b := []byte(s) + f.protect.rc4(uint32(f.n), &b) + s = string(b) + } + return "(" + f.escape(s) + ")" +} + +func blankCount(str string) (count int) { + l := len(str) + for j := 0; j < l; j++ { + if byte(' ') == str[j] { + count++ + } + } + return +} + +// Underline text +func (f *Fpdf) dounderline(x, y float64, txt string) string { + up := float64(f.currentFont.Up) + ut := float64(f.currentFont.Ut) + w := f.GetStringWidth(txt) + f.ws*float64(blankCount(txt)) + return sprintf("%.2f %.2f %.2f %.2f re f", x*f.k, + (f.h-(y-up/1000*f.fontSize))*f.k, w*f.k, -ut/1000*f.fontSizePt) +} + +func bufEqual(buf []byte, str string) bool { + return string(buf[0:len(str)]) == str +} + +func be16(buf []byte) int { + return 256*int(buf[0]) + int(buf[1]) +} + +func (f *Fpdf) newImageInfo() *ImageInfoType { + // default dpi to 72 unless told otherwise + return &ImageInfoType{scale: f.k, dpi: 72} +} + +// parsejpg extracts info from io.Reader with JPEG data +// Thank you, Bruno Michel, for providing this code. +func (f *Fpdf) parsejpg(r io.Reader) (info *ImageInfoType) { + info = f.newImageInfo() + var ( + data bytes.Buffer + err error + ) + _, err = data.ReadFrom(r) + if err != nil { + f.err = err + return + } + info.data = data.Bytes() + + config, err := jpeg.DecodeConfig(bytes.NewReader(info.data)) + if err != nil { + f.err = err + return + } + info.w = float64(config.Width) + info.h = float64(config.Height) + info.f = "DCTDecode" + info.bpc = 8 + switch config.ColorModel { + case color.GrayModel: + info.cs = "DeviceGray" + case color.YCbCrModel: + info.cs = "DeviceRGB" + case color.CMYKModel: + info.cs = "DeviceCMYK" + default: + f.err = fmt.Errorf("image JPEG buffer has unsupported color space (%v)", config.ColorModel) + return + } + return +} + +// parsepng extracts info from a PNG data +func (f *Fpdf) parsepng(r io.Reader, readdpi bool) (info *ImageInfoType) { + buf, err := bufferFromReader(r) + if err != nil { + f.err = err + return + } + return f.parsepngstream(buf, readdpi) +} + +func (f *Fpdf) readBeInt32(r io.Reader) (val int32) { + err := binary.Read(r, binary.BigEndian, &val) + if err != nil && err != io.EOF { + f.err = err + } + return +} + +func (f *Fpdf) readByte(r io.Reader) (val byte) { + err := binary.Read(r, binary.BigEndian, &val) + if err != nil { + f.err = err + } + return +} + +// parsegif extracts info from a GIF data (via PNG conversion) +func (f *Fpdf) parsegif(r io.Reader) (info *ImageInfoType) { + data, err := bufferFromReader(r) + if err != nil { + f.err = err + return + } + var img image.Image + img, err = gif.Decode(data) + if err != nil { + f.err = err + return + } + pngBuf := new(bytes.Buffer) + err = png.Encode(pngBuf, img) + if err != nil { + f.err = err + return + } + return f.parsepngstream(pngBuf, false) +} + +// newobj begins a new object +func (f *Fpdf) newobj() { + // dbg("newobj") + f.n++ + for j := len(f.offsets); j <= f.n; j++ { + f.offsets = append(f.offsets, 0) + } + f.offsets[f.n] = f.buffer.Len() + f.outf("%d 0 obj", f.n) +} + +func (f *Fpdf) putstream(b []byte) { + // dbg("putstream") + if f.protect.encrypted { + f.protect.rc4(uint32(f.n), &b) + } + f.out("stream") + f.out(string(b)) + f.out("endstream") +} + +// out; Add a line to the document +func (f *Fpdf) out(s string) { + if f.state == 2 { + f.pages[f.page].WriteString(s) + f.pages[f.page].WriteString("\n") + } else { + f.buffer.WriteString(s) + f.buffer.WriteString("\n") + } +} + +// outbuf adds a buffered line to the document +func (f *Fpdf) outbuf(r io.Reader) { + if f.state == 2 { + f.pages[f.page].ReadFrom(r) + f.pages[f.page].WriteString("\n") + } else { + f.buffer.ReadFrom(r) + f.buffer.WriteString("\n") + } +} + +// RawWriteStr writes a string directly to the PDF generation buffer. This is a +// low-level function that is not required for normal PDF construction. An +// understanding of the PDF specification is needed to use this method +// correctly. +func (f *Fpdf) RawWriteStr(str string) { + f.out(str) +} + +// RawWriteBuf writes the contents of the specified buffer directly to the PDF +// generation buffer. This is a low-level function that is not required for +// normal PDF construction. An understanding of the PDF specification is needed +// to use this method correctly. +func (f *Fpdf) RawWriteBuf(r io.Reader) { + f.outbuf(r) +} + +// outf adds a formatted line to the document +func (f *Fpdf) outf(fmtStr string, args ...interface{}) { + f.out(sprintf(fmtStr, args...)) +} + +// SetDefaultCatalogSort sets the default value of the catalog sort flag that +// will be used when initializing a new Fpdf instance. See SetCatalogSort() for +// more details. +func SetDefaultCatalogSort(flag bool) { + gl.catalogSort = flag +} + +// SetCatalogSort sets a flag that will be used, if true, to consistently order +// the document's internal resource catalogs. This method is typically only +// used for test purposes to facilitate PDF comparison. +func (f *Fpdf) SetCatalogSort(flag bool) { + f.catalogSort = flag +} + +// SetDefaultCreationDate sets the default value of the document creation date +// that will be used when initializing a new Fpdf instance. See +// SetCreationDate() for more details. +func SetDefaultCreationDate(tm time.Time) { + gl.creationDate = tm +} + +// SetCreationDate fixes the document's internal CreationDate value. By +// default, the time when the document is generated is used for this value. +// This method is typically only used for testing purposes to facilitate PDF +// comparison. Specify a zero-value time to revert to the default behavior. +func (f *Fpdf) SetCreationDate(tm time.Time) { + f.creationDate = tm +} + +// SetJavascript adds Adobe JavaScript to the document. +func (f *Fpdf) SetJavascript(script string) { + f.javascript = &script +} + +// RegisterAlias adds an (alias, replacement) pair to the document so we can +// replace all occurrences of that alias after writing but before the +// document is closed. +func (f *Fpdf) RegisterAlias(alias, replacement string) { + f.aliasMap[alias] = replacement +} + +func (f *Fpdf) replaceAliases() { + for alias, replacement := range f.aliasMap { + for n := 1; n <= f.page; n++ { + s := f.pages[n].String() + if strings.Contains(s, alias) { + s = strings.Replace(s, alias, replacement, -1) + f.pages[n].Truncate(0) + f.pages[n].WriteString(s) + } + } + } +} + +func (f *Fpdf) putpages() { + var wPt, hPt float64 + var pageSize SizeType + // var linkList []linkType + var ok bool + nb := f.page + if len(f.aliasNbPagesStr) > 0 { + // Replace number of pages + alias := utf8toutf16(f.aliasNbPagesStr, false) + r := utf8toutf16(sprintf("%d", nb), false) + f.RegisterAlias(alias, r) + f.RegisterAlias(f.aliasNbPagesStr, sprintf("%d", nb)) + } + f.replaceAliases() + if f.defOrientation == "P" { + wPt = f.defPageSize.Wd * f.k + hPt = f.defPageSize.Ht * f.k + } else { + wPt = f.defPageSize.Ht * f.k + hPt = f.defPageSize.Wd * f.k + } + for n := 1; n <= nb; n++ { + // Page + f.newobj() + f.out("< 0 { + var annots fmtBuffer + annots.printf("/Annots [") + for _, pl := range f.pageLinks[n] { + annots.printf("<>>>", f.textstring(pl.linkStr)) + } else { + l := f.links[pl.link] + var sz SizeType + var h float64 + sz, ok = f.pageSizes[l.page] + if ok { + h = sz.Ht + } else { + h = hPt + } + // dbg("h [%.2f], l.y [%.2f] f.k [%.2f]\n", h, l.y, f.k) + annots.printf("/Dest [%d 0 R /XYZ 0 %.2f null]>>", 1+2*l.page, h-l.y*f.k) + } + } + annots.printf("]") + f.out(annots.String()) + } + if f.pdfVersion > "1.3" { + f.out("/Group <>") + } + f.outf("/Contents %d 0 R>>", f.n+1) + f.out("endobj") + // Page content + f.newobj() + if f.compress { + data := sliceCompress(f.pages[n].Bytes()) + f.outf("<>", len(data)) + f.putstream(data) + } else { + f.outf("<>", f.pages[n].Len()) + f.putstream(f.pages[n].Bytes()) + } + f.out("endobj") + } + // Pages root + f.offsets[1] = f.buffer.Len() + f.out("1 0 obj") + f.out("<>") + f.out("endobj") +} + +func (f *Fpdf) putfonts() { + if f.err != nil { + return + } + nf := f.n + for _, diff := range f.diffs { + // Encodings + f.newobj() + f.outf("<>", diff) + f.out("endobj") + } + { + var fileList []string + var info fontFileType + var file string + for file = range f.fontFiles { + fileList = append(fileList, file) + } + if f.catalogSort { + sort.SliceStable(fileList, func(i, j int) bool { return fileList[i] < fileList[j] }) + } + for _, file = range fileList { + info = f.fontFiles[file] + if info.fontType != "UTF8" { + f.newobj() + info.n = f.n + f.fontFiles[file] = info + + var font []byte + + if info.embedded { + font = info.content + } else { + var err error + font, err = f.loadFontFile(file) + if err != nil { + f.err = err + return + } + } + compressed := file[len(file)-2:] == ".z" + if !compressed && info.length2 > 0 { + buf := font[6:info.length1] + buf = append(buf, font[6+info.length1+6:info.length2]...) + font = buf + } + f.outf("< 0 { + f.outf("/Length2 %d /Length3 0", info.length2) + } + f.out(">>") + f.putstream(font) + f.out("endobj") + } + } + } + { + var keyList []string + var font fontDefType + var key string + for key = range f.fonts { + keyList = append(keyList, key) + } + if f.catalogSort { + sort.SliceStable(keyList, func(i, j int) bool { return keyList[i] < keyList[j] }) + } + for _, key = range keyList { + font = f.fonts[key] + // Font objects + font.N = f.n + 1 + f.fonts[key] = font + tp := font.Tp + name := font.Name + switch tp { + case "Core": + // Core font + f.newobj() + f.out("<>") + f.out("endobj") + case "Type1": + fallthrough + case "TrueType": + // Additional Type1 or TrueType/OpenType font + f.newobj() + f.out("< 0 { + f.outf("/Encoding %d 0 R", nf+font.DiffN) + } else { + f.out("/Encoding /WinAnsiEncoding") + } + f.out(">>") + f.out("endobj") + // Widths + f.newobj() + var s fmtBuffer + s.WriteString("[") + for j := 32; j < 256; j++ { + s.printf("%d ", font.Cw[j]) + } + s.WriteString("]") + f.out(s.String()) + f.out("endobj") + // Descriptor + f.newobj() + s.Truncate(0) + s.printf("<>", suffix, f.fontFiles[font.File].n) + f.out(s.String()) + f.out("endobj") + case "UTF8": + fontName := "utf8" + font.Name + usedRunes := font.usedRunes + delete(usedRunes, 0) + utf8FontStream := font.utf8File.GenerateСutFont(usedRunes) + utf8FontSize := len(utf8FontStream) + compressedFontStream := sliceCompress(utf8FontStream) + CodeSignDictionary := font.utf8File.CodeSymbolDictionary + delete(CodeSignDictionary, 0) + + f.newobj() + f.out(fmt.Sprintf("<>\n"+"endobj", fontName, f.n+1, f.n+2)) + + f.newobj() + f.out("<>") + f.out("endobj") + + f.newobj() + f.out("<>") + f.putstream([]byte(toUnicode)) + f.out("endobj") + + // CIDInfo + f.newobj() + f.out("<>") + f.out("endobj") + + // Font descriptor + f.newobj() + var s fmtBuffer + s.printf("<>") + f.out(s.String()) + f.out("endobj") + + // Embed CIDToGIDMap + cidToGidMap := make([]byte, 256*256*2) + + for cc, glyph := range CodeSignDictionary { + cidToGidMap[cc*2] = byte(glyph >> 8) + cidToGidMap[cc*2+1] = byte(glyph & 0xFF) + } + + cidToGidMap = sliceCompress(cidToGidMap) + f.newobj() + f.out("<>") + f.putstream(cidToGidMap) + f.out("endobj") + + //Font file + f.newobj() + f.out("<>") + f.putstream(compressedFontStream) + f.out("endobj") + default: + f.err = fmt.Errorf("unsupported font type: %s", tp) + return + } + } + } + return +} + +func (f *Fpdf) generateCIDFontMap(font *fontDefType, LastRune int) { + rangeID := 0 + cidArray := make(map[int]*untypedKeyMap) + cidArrayKeys := make([]int, 0) + prevCid := -2 + prevWidth := -1 + interval := false + startCid := 1 + cwLen := LastRune + 1 + + // for each character + for cid := startCid; cid < cwLen; cid++ { + if font.Cw[cid] == 0x00 { + continue + } + width := font.Cw[cid] + if width == 65535 { + width = 0 + } + if numb, OK := font.usedRunes[cid]; cid > 255 && (!OK || numb == 0) { + continue + } + + if cid == prevCid+1 { + if width == prevWidth { + + if width == cidArray[rangeID].get(0) { + cidArray[rangeID].put(nil, width) + } else { + cidArray[rangeID].pop() + rangeID = prevCid + r := untypedKeyMap{ + valueSet: make([]int, 0), + keySet: make([]interface{}, 0), + } + cidArray[rangeID] = &r + cidArrayKeys = append(cidArrayKeys, rangeID) + cidArray[rangeID].put(nil, prevWidth) + cidArray[rangeID].put(nil, width) + } + interval = true + cidArray[rangeID].put("interval", 1) + } else { + if interval { + // new range + rangeID = cid + r := untypedKeyMap{ + valueSet: make([]int, 0), + keySet: make([]interface{}, 0), + } + cidArray[rangeID] = &r + cidArrayKeys = append(cidArrayKeys, rangeID) + cidArray[rangeID].put(nil, width) + } else { + cidArray[rangeID].put(nil, width) + } + interval = false + } + } else { + rangeID = cid + r := untypedKeyMap{ + valueSet: make([]int, 0), + keySet: make([]interface{}, 0), + } + cidArray[rangeID] = &r + cidArrayKeys = append(cidArrayKeys, rangeID) + cidArray[rangeID].put(nil, width) + interval = false + } + prevCid = cid + prevWidth = width + + } + previousKey := -1 + nextKey := -1 + isInterval := false + for g := 0; g < len(cidArrayKeys); { + key := cidArrayKeys[g] + ws := *cidArray[key] + cws := len(ws.keySet) + if (key == nextKey) && (!isInterval) && (ws.getIndex("interval") < 0 || cws < 4) { + if cidArray[key].getIndex("interval") >= 0 { + cidArray[key].delete("interval") + } + cidArray[previousKey] = arrayMerge(cidArray[previousKey], cidArray[key]) + cidArrayKeys = remove(cidArrayKeys, key) + } else { + g++ + previousKey = key + } + nextKey = key + cws + // ui := ws.getIndex("interval") + // ui = ui + 1 + if ws.getIndex("interval") >= 0 { + if cws > 3 { + isInterval = true + } else { + isInterval = false + } + cidArray[key].delete("interval") + nextKey-- + } else { + isInterval = false + } + } + var w fmtBuffer + for _, k := range cidArrayKeys { + ws := cidArray[k] + if len(arrayCountValues(ws.valueSet)) == 1 { + w.printf(" %d %d %d", k, k+len(ws.valueSet)-1, ws.get(0)) + } else { + w.printf(" %d [ %s ]\n", k, implode(" ", ws.valueSet)) + } + } + f.out("/W [" + w.String() + " ]") +} + +func implode(sep string, arr []int) string { + var s fmtBuffer + for i := 0; i < len(arr)-1; i++ { + s.printf("%v", arr[i]) + s.printf(sep) + } + if len(arr) > 0 { + s.printf("%v", arr[len(arr)-1]) + } + return s.String() +} + +func arrayCountValues(mp []int) map[int]int { + answer := make(map[int]int) + for _, v := range mp { + answer[v] = answer[v] + 1 + } + return answer +} + +func (f *Fpdf) loadFontFile(name string) ([]byte, error) { + if f.fontLoader != nil { + reader, err := f.fontLoader.Open(name) + if err == nil { + data, err := ioutil.ReadAll(reader) + if closer, ok := reader.(io.Closer); ok { + closer.Close() + } + return data, err + } + } + return ioutil.ReadFile(path.Join(f.fontpath, name)) +} + +func (f *Fpdf) putimages() { + var keyList []string + var key string + for key = range f.images { + keyList = append(keyList, key) + } + if f.catalogSort { + sort.SliceStable(keyList, func(i, j int) bool { return f.images[keyList[i]].w < f.images[keyList[j]].w }) + } + for _, key = range keyList { + f.putimage(f.images[key]) + } +} + +func (f *Fpdf) putimage(info *ImageInfoType) { + f.newobj() + info.n = f.n + f.out("< 0 { + f.outf("/Filter /%s", info.f) + } + if len(info.dp) > 0 { + f.outf("/DecodeParms <<%s>>", info.dp) + } + if len(info.trns) > 0 { + var trns fmtBuffer + for _, v := range info.trns { + trns.printf("%d %d ", v, v) + } + f.outf("/Mask [%s]", trns.String()) + } + if info.smask != nil { + f.outf("/SMask %d 0 R", f.n+1) + } + f.outf("/Length %d>>", len(info.data)) + f.putstream(info.data) + f.out("endobj") + // Soft mask + if len(info.smask) > 0 { + smask := &ImageInfoType{ + w: info.w, + h: info.h, + cs: "DeviceGray", + bpc: 8, + f: info.f, + dp: sprintf("/Predictor 15 /Colors 1 /BitsPerComponent 8 /Columns %d", int(info.w)), + data: info.smask, + scale: f.k, + } + f.putimage(smask) + } + // Palette + if info.cs == "Indexed" { + f.newobj() + if f.compress { + pal := sliceCompress(info.pal) + f.outf("<>", len(pal)) + f.putstream(pal) + } else { + f.outf("<>", len(info.pal)) + f.putstream(info.pal) + } + f.out("endobj") + } +} + +func (f *Fpdf) putxobjectdict() { + { + var image *ImageInfoType + var key string + var keyList []string + for key = range f.images { + keyList = append(keyList, key) + } + if f.catalogSort { + sort.SliceStable(keyList, func(i, j int) bool { return f.images[keyList[i]].i < f.images[keyList[j]].i }) + } + for _, key = range keyList { + image = f.images[key] + f.outf("/I%s %d 0 R", image.i, image.n) + } + } + { + var keyList []string + var key string + var tpl Template + keyList = templateKeyList(f.templates, f.catalogSort) + for _, key = range keyList { + tpl = f.templates[key] + // for _, tpl := range f.templates { + id := tpl.ID() + if objID, ok := f.templateObjects[id]; ok { + f.outf("/TPL%s %d 0 R", id, objID) + } + } + } + { + for tplName, objID := range f.importedTplObjs { + // here replace obj id hash with n + f.outf("%s %d 0 R", tplName, f.importedTplIDs[objID]) + } + } +} + +func (f *Fpdf) putresourcedict() { + f.out("/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]") + f.out("/Font <<") + { + var keyList []string + var font fontDefType + var key string + for key = range f.fonts { + keyList = append(keyList, key) + } + if f.catalogSort { + sort.SliceStable(keyList, func(i, j int) bool { return f.fonts[keyList[i]].i < f.fonts[keyList[j]].i }) + } + for _, key = range keyList { + font = f.fonts[key] + f.outf("/F%s %d 0 R", font.i, font.N) + } + } + f.out(">>") + f.out("/XObject <<") + f.putxobjectdict() + f.out(">>") + count := len(f.blendList) + if count > 1 { + f.out("/ExtGState <<") + for j := 1; j < count; j++ { + f.outf("/GS%d %d 0 R", j, f.blendList[j].objNum) + } + f.out(">>") + } + count = len(f.gradientList) + if count > 1 { + f.out("/Shading <<") + for j := 1; j < count; j++ { + f.outf("/Sh%d %d 0 R", j, f.gradientList[j].objNum) + } + f.out(">>") + } + // Layers + f.layerPutResourceDict() + f.spotColorPutResourceDict() +} + +func (f *Fpdf) putBlendModes() { + count := len(f.blendList) + for j := 1; j < count; j++ { + bl := f.blendList[j] + f.newobj() + f.blendList[j].objNum = f.n + f.outf("<>", + bl.fillStr, bl.strokeStr, bl.modeStr) + f.out("endobj") + } +} + +func (f *Fpdf) putGradients() { + count := len(f.gradientList) + for j := 1; j < count; j++ { + var f1 int + gr := f.gradientList[j] + if gr.tp == 2 || gr.tp == 3 { + f.newobj() + f.outf("<>", gr.clr1Str, gr.clr2Str) + f.out("endobj") + f1 = f.n + } + f.newobj() + f.outf("<>", + gr.x1, gr.y1, gr.x2, gr.y2, f1) + } else if gr.tp == 3 { + f.outf("/Coords [%.5f %.5f 0 %.5f %.5f %.5f] /Function %d 0 R /Extend [true true]>>", + gr.x1, gr.y1, gr.x2, gr.y2, gr.r, f1) + } + f.out("endobj") + f.gradientList[j].objNum = f.n + } +} + +func (f *Fpdf) putjavascript() { + if f.javascript == nil { + return + } + + f.newobj() + f.nJs = f.n + f.out("<<") + f.outf("/Names [(EmbeddedJS) %d 0 R]", f.n+1) + f.out(">>") + f.out("endobj") + f.newobj() + f.out("<<") + f.out("/S /JavaScript") + f.outf("/JS %s", f.textstring(*f.javascript)) + f.out(">>") + f.out("endobj") +} + +func (f *Fpdf) putresources() { + if f.err != nil { + return + } + f.layerPutLayers() + f.putBlendModes() + f.putGradients() + f.putSpotColors() + f.putfonts() + if f.err != nil { + return + } + f.putimages() + f.putTemplates() + f.putImportedTemplates() // gofpdi + // Resource dictionary + f.offsets[2] = f.buffer.Len() + f.out("2 0 obj") + f.out("<<") + f.putresourcedict() + f.out(">>") + f.out("endobj") + f.putjavascript() + if f.protect.encrypted { + f.newobj() + f.protect.objNum = f.n + f.out("<<") + f.out("/Filter /Standard") + f.out("/V 1") + f.out("/R 2") + f.outf("/O (%s)", f.escape(string(f.protect.oValue))) + f.outf("/U (%s)", f.escape(string(f.protect.uValue))) + f.outf("/P %d", f.protect.pValue) + f.out(">>") + f.out("endobj") + } + return +} + +func (f *Fpdf) putinfo() { + var tm time.Time + f.outf("/Producer %s", f.textstring("FPDF "+cnFpdfVersion)) + if len(f.title) > 0 { + f.outf("/Title %s", f.textstring(f.title)) + } + if len(f.subject) > 0 { + f.outf("/Subject %s", f.textstring(f.subject)) + } + if len(f.author) > 0 { + f.outf("/Author %s", f.textstring(f.author)) + } + if len(f.keywords) > 0 { + f.outf("/Keywords %s", f.textstring(f.keywords)) + } + if len(f.creator) > 0 { + f.outf("/Creator %s", f.textstring(f.creator)) + } + if f.creationDate.IsZero() { + tm = time.Now() + } else { + tm = f.creationDate + } + f.outf("/CreationDate %s", f.textstring("D:"+tm.Format("20060102150405"))) +} + +func (f *Fpdf) putcatalog() { + f.out("/Type /Catalog") + f.out("/Pages 1 0 R") + switch f.zoomMode { + case "fullpage": + f.out("/OpenAction [3 0 R /Fit]") + case "fullwidth": + f.out("/OpenAction [3 0 R /FitH null]") + case "real": + f.out("/OpenAction [3 0 R /XYZ null null 1]") + } + // } else if !is_string($this->zoomMode)) + // $this->out('/OpenAction [3 0 R /XYZ null null '.sprintf('%.2f',$this->zoomMode/100).']'); + switch f.layoutMode { + case "single", "SinglePage": + f.out("/PageLayout /SinglePage") + case "continuous", "OneColumn": + f.out("/PageLayout /OneColumn") + case "two", "TwoColumnLeft": + f.out("/PageLayout /TwoColumnLeft") + case "TwoColumnRight": + f.out("/PageLayout /TwoColumnRight") + case "TwoPageLeft", "TwoPageRight": + if f.pdfVersion < "1.5" { + f.pdfVersion = "1.5" + } + f.out("/PageLayout /" + f.layoutMode) + } + // Bookmarks + if len(f.outlines) > 0 { + f.outf("/Outlines %d 0 R", f.outlineRoot) + f.out("/PageMode /UseOutlines") + } + // Layers + f.layerPutCatalog() + // JavaScript + if f.javascript != nil { + f.outf("/Names <>", f.nJs) + } +} + +func (f *Fpdf) putheader() { + if len(f.blendMap) > 0 && f.pdfVersion < "1.4" { + f.pdfVersion = "1.4" + } + f.outf("%%PDF-%s", f.pdfVersion) +} + +func (f *Fpdf) puttrailer() { + f.outf("/Size %d", f.n+1) + f.outf("/Root %d 0 R", f.n) + f.outf("/Info %d 0 R", f.n-1) + if f.protect.encrypted { + f.outf("/Encrypt %d 0 R", f.protect.objNum) + f.out("/ID [()()]") + } +} + +func (f *Fpdf) putxmp() { + if len(f.xmp) == 0 { + return + } + f.newobj() + f.outf("<< /Type /Metadata /Subtype /XML /Length %d >>", len(f.xmp)) + f.putstream(f.xmp) + f.out("endobj") +} + +func (f *Fpdf) putbookmarks() { + nb := len(f.outlines) + if nb > 0 { + lru := make(map[int]int) + level := 0 + for i, o := range f.outlines { + if o.level > 0 { + parent := lru[o.level-1] + f.outlines[i].parent = parent + f.outlines[parent].last = i + if o.level > level { + f.outlines[parent].first = i + } + } else { + f.outlines[i].parent = nb + } + if o.level <= level && i > 0 { + prev := lru[o.level] + f.outlines[prev].next = i + f.outlines[i].prev = prev + } + lru[o.level] = i + level = o.level + } + n := f.n + 1 + for _, o := range f.outlines { + f.newobj() + f.outf("<>") + f.out("endobj") + } + f.newobj() + f.outlineRoot = f.n + f.outf("<>", n+lru[0]) + f.out("endobj") + } +} + +func (f *Fpdf) enddoc() { + if f.err != nil { + return + } + f.layerEndDoc() + f.putheader() + f.putpages() + f.putresources() + if f.err != nil { + return + } + // Bookmarks + f.putbookmarks() + // Metadata + f.putxmp() + // Info + f.newobj() + f.out("<<") + f.putinfo() + f.out(">>") + f.out("endobj") + // Catalog + f.newobj() + f.out("<<") + f.putcatalog() + f.out(">>") + f.out("endobj") + // Cross-ref + o := f.buffer.Len() + f.out("xref") + f.outf("0 %d", f.n+1) + f.out("0000000000 65535 f ") + for j := 1; j <= f.n; j++ { + f.outf("%010d 00000 n ", f.offsets[j]) + } + // Trailer + f.out("trailer") + f.out("<<") + f.puttrailer() + f.out(">>") + f.out("startxref") + f.outf("%d", o) + f.out("%%EOF") + f.state = 3 + return +} + +// Path Drawing + +// MoveTo moves the stylus to (x, y) without drawing the path from the +// previous point. Paths must start with a MoveTo to set the original +// stylus location or the result is undefined. +// +// Create a "path" by moving a virtual stylus around the page (with +// MoveTo, LineTo, CurveTo, CurveBezierCubicTo, ArcTo & ClosePath) +// then draw it or fill it in (with DrawPath). The main advantage of +// using the path drawing routines rather than multiple Fpdf.Line is +// that PDF creates nice line joins at the angles, rather than just +// overlaying the lines. +func (f *Fpdf) MoveTo(x, y float64) { + f.point(x, y) + f.x, f.y = x, y +} + +// LineTo creates a line from the current stylus location to (x, y), which +// becomes the new stylus location. Note that this only creates the line in +// the path; it does not actually draw the line on the page. +// +// The MoveTo() example demonstrates this method. +func (f *Fpdf) LineTo(x, y float64) { + f.outf("%.2f %.2f l", x*f.k, (f.h-y)*f.k) + f.x, f.y = x, y +} + +// CurveTo creates a single-segment quadratic Bézier curve. The curve starts at +// the current stylus location and ends at the point (x, y). The control point +// (cx, cy) specifies the curvature. At the start point, the curve is tangent +// to the straight line between the current stylus location and the control +// point. At the end point, the curve is tangent to the straight line between +// the end point and the control point. +// +// The MoveTo() example demonstrates this method. +func (f *Fpdf) CurveTo(cx, cy, x, y float64) { + f.outf("%.5f %.5f %.5f %.5f v", cx*f.k, (f.h-cy)*f.k, x*f.k, (f.h-y)*f.k) + f.x, f.y = x, y +} + +// CurveBezierCubicTo creates a single-segment cubic Bézier curve. The curve +// starts at the current stylus location and ends at the point (x, y). The +// control points (cx0, cy0) and (cx1, cy1) specify the curvature. At the +// current stylus, the curve is tangent to the straight line between the +// current stylus location and the control point (cx0, cy0). At the end point, +// the curve is tangent to the straight line between the end point and the +// control point (cx1, cy1). +// +// The MoveTo() example demonstrates this method. +func (f *Fpdf) CurveBezierCubicTo(cx0, cy0, cx1, cy1, x, y float64) { + f.curve(cx0, cy0, cx1, cy1, x, y) + f.x, f.y = x, y +} + +// ClosePath creates a line from the current location to the last MoveTo point +// (if not the same) and mark the path as closed so the first and last lines +// join nicely. +// +// The MoveTo() example demonstrates this method. +func (f *Fpdf) ClosePath() { + f.outf("h") +} + +// DrawPath actually draws the path on the page. +// +// styleStr can be "F" for filled, "D" for outlined only, or "DF" or "FD" for +// outlined and filled. An empty string will be replaced with "D". +// Path-painting operators as defined in the PDF specification are also +// allowed: "S" (Stroke the path), "s" (Close and stroke the path), +// "f" (fill the path, using the nonzero winding number), "f*" +// (Fill the path, using the even-odd rule), "B" (Fill and then stroke +// the path, using the nonzero winding number rule), "B*" (Fill and +// then stroke the path, using the even-odd rule), "b" (Close, fill, +// and then stroke the path, using the nonzero winding number rule) and +// "b*" (Close, fill, and then stroke the path, using the even-odd +// rule). +// Drawing uses the current draw color, line width, and cap style +// centered on the +// path. Filling uses the current fill color. +// +// The MoveTo() example demonstrates this method. +func (f *Fpdf) DrawPath(styleStr string) { + f.outf(fillDrawOp(styleStr)) +} + +// ArcTo draws an elliptical arc centered at point (x, y). rx and ry specify its +// horizontal and vertical radii. If the start of the arc is not at +// the current position, a connecting line will be drawn. +// +// degRotate specifies the angle that the arc will be rotated. degStart and +// degEnd specify the starting and ending angle of the arc. All angles are +// specified in degrees and measured counter-clockwise from the 3 o'clock +// position. +// +// styleStr can be "F" for filled, "D" for outlined only, or "DF" or "FD" for +// outlined and filled. An empty string will be replaced with "D". Drawing uses +// the current draw color, line width, and cap style centered on the arc's +// path. Filling uses the current fill color. +// +// The MoveTo() example demonstrates this method. +func (f *Fpdf) ArcTo(x, y, rx, ry, degRotate, degStart, degEnd float64) { + f.arc(x, y, rx, ry, degRotate, degStart, degEnd, "", true) +} + +func (f *Fpdf) arc(x, y, rx, ry, degRotate, degStart, degEnd float64, + styleStr string, path bool) { + x *= f.k + y = (f.h - y) * f.k + rx *= f.k + ry *= f.k + segments := int(degEnd-degStart) / 60 + if segments < 2 { + segments = 2 + } + angleStart := degStart * math.Pi / 180 + angleEnd := degEnd * math.Pi / 180 + angleTotal := angleEnd - angleStart + dt := angleTotal / float64(segments) + dtm := dt / 3 + if degRotate != 0 { + a := -degRotate * math.Pi / 180 + f.outf("q %.5f %.5f %.5f %.5f %.5f %.5f cm", + math.Cos(a), -1*math.Sin(a), + math.Sin(a), math.Cos(a), x, y) + x = 0 + y = 0 + } + t := angleStart + a0 := x + rx*math.Cos(t) + b0 := y + ry*math.Sin(t) + c0 := -rx * math.Sin(t) + d0 := ry * math.Cos(t) + sx := a0 / f.k // start point of arc + sy := f.h - (b0 / f.k) + if path { + if f.x != sx || f.y != sy { + // Draw connecting line to start point + f.LineTo(sx, sy) + } + } else { + f.point(sx, sy) + } + for j := 1; j <= segments; j++ { + // Draw this bit of the total curve + t = (float64(j) * dt) + angleStart + a1 := x + rx*math.Cos(t) + b1 := y + ry*math.Sin(t) + c1 := -rx * math.Sin(t) + d1 := ry * math.Cos(t) + f.curve((a0+(c0*dtm))/f.k, + f.h-((b0+(d0*dtm))/f.k), + (a1-(c1*dtm))/f.k, + f.h-((b1-(d1*dtm))/f.k), + a1/f.k, + f.h-(b1/f.k)) + a0 = a1 + b0 = b1 + c0 = c1 + d0 = d1 + if path { + f.x = a1 / f.k + f.y = f.h - (b1 / f.k) + } + } + if !path { + f.out(fillDrawOp(styleStr)) + } + if degRotate != 0 { + f.out("Q") + } +} diff --git a/vendor/github.com/jung-kurt/gofpdf/fpdftrans.go b/vendor/github.com/jung-kurt/gofpdf/fpdftrans.go new file mode 100644 index 00000000..9cda3977 --- /dev/null +++ b/vendor/github.com/jung-kurt/gofpdf/fpdftrans.go @@ -0,0 +1,213 @@ +package gofpdf + +import ( + "fmt" + "math" +) + +// Routines in this file are translated from the work of Moritz Wagner and +// Andreas Würmser. + +// TransformMatrix is used for generalized transformations of text, drawings +// and images. +type TransformMatrix struct { + A, B, C, D, E, F float64 +} + +// TransformBegin sets up a transformation context for subsequent text, +// drawings and images. The typical usage is to immediately follow a call to +// this method with a call to one or more of the transformation methods such as +// TransformScale(), TransformSkew(), etc. This is followed by text, drawing or +// image output and finally a call to TransformEnd(). All transformation +// contexts must be properly ended prior to outputting the document. +func (f *Fpdf) TransformBegin() { + f.transformNest++ + f.out("q") +} + +// TransformScaleX scales the width of the following text, drawings and images. +// scaleWd is the percentage scaling factor. (x, y) is center of scaling. +// +// The TransformBegin() example demonstrates this method. +func (f *Fpdf) TransformScaleX(scaleWd, x, y float64) { + f.TransformScale(scaleWd, 100, x, y) +} + +// TransformScaleY scales the height of the following text, drawings and +// images. scaleHt is the percentage scaling factor. (x, y) is center of +// scaling. +// +// The TransformBegin() example demonstrates this method. +func (f *Fpdf) TransformScaleY(scaleHt, x, y float64) { + f.TransformScale(100, scaleHt, x, y) +} + +// TransformScaleXY uniformly scales the width and height of the following +// text, drawings and images. s is the percentage scaling factor for both width +// and height. (x, y) is center of scaling. +// +// The TransformBegin() example demonstrates this method. +func (f *Fpdf) TransformScaleXY(s, x, y float64) { + f.TransformScale(s, s, x, y) +} + +// TransformScale generally scales the following text, drawings and images. +// scaleWd and scaleHt are the percentage scaling factors for width and height. +// (x, y) is center of scaling. +// +// The TransformBegin() example demonstrates this method. +func (f *Fpdf) TransformScale(scaleWd, scaleHt, x, y float64) { + if scaleWd == 0 || scaleHt == 0 { + f.err = fmt.Errorf("scale factor cannot be zero") + return + } + y = (f.h - y) * f.k + x *= f.k + scaleWd /= 100 + scaleHt /= 100 + f.Transform(TransformMatrix{scaleWd, 0, 0, + scaleHt, x * (1 - scaleWd), y * (1 - scaleHt)}) +} + +// TransformMirrorHorizontal horizontally mirrors the following text, drawings +// and images. x is the axis of reflection. +// +// The TransformBegin() example demonstrates this method. +func (f *Fpdf) TransformMirrorHorizontal(x float64) { + f.TransformScale(-100, 100, x, f.y) +} + +// TransformMirrorVertical vertically mirrors the following text, drawings and +// images. y is the axis of reflection. +// +// The TransformBegin() example demonstrates this method. +func (f *Fpdf) TransformMirrorVertical(y float64) { + f.TransformScale(100, -100, f.x, y) +} + +// TransformMirrorPoint symmetrically mirrors the following text, drawings and +// images on the point specified by (x, y). +// +// The TransformBegin() example demonstrates this method. +func (f *Fpdf) TransformMirrorPoint(x, y float64) { + f.TransformScale(-100, -100, x, y) +} + +// TransformMirrorLine symmetrically mirrors the following text, drawings and +// images on the line defined by angle and the point (x, y). angles is +// specified in degrees and measured counter-clockwise from the 3 o'clock +// position. +// +// The TransformBegin() example demonstrates this method. +func (f *Fpdf) TransformMirrorLine(angle, x, y float64) { + f.TransformScale(-100, 100, x, y) + f.TransformRotate(-2*(angle-90), x, y) +} + +// TransformTranslateX moves the following text, drawings and images +// horizontally by the amount specified by tx. +// +// The TransformBegin() example demonstrates this method. +func (f *Fpdf) TransformTranslateX(tx float64) { + f.TransformTranslate(tx, 0) +} + +// TransformTranslateY moves the following text, drawings and images vertically +// by the amount specified by ty. +// +// The TransformBegin() example demonstrates this method. +func (f *Fpdf) TransformTranslateY(ty float64) { + f.TransformTranslate(0, ty) +} + +// TransformTranslate moves the following text, drawings and images +// horizontally and vertically by the amounts specified by tx and ty. +// +// The TransformBegin() example demonstrates this method. +func (f *Fpdf) TransformTranslate(tx, ty float64) { + f.Transform(TransformMatrix{1, 0, 0, 1, tx * f.k, -ty * f.k}) +} + +// TransformRotate rotates the following text, drawings and images around the +// center point (x, y). angle is specified in degrees and measured +// counter-clockwise from the 3 o'clock position. +// +// The TransformBegin() example demonstrates this method. +func (f *Fpdf) TransformRotate(angle, x, y float64) { + y = (f.h - y) * f.k + x *= f.k + angle = angle * math.Pi / 180 + var tm TransformMatrix + tm.A = math.Cos(angle) + tm.B = math.Sin(angle) + tm.C = -tm.B + tm.D = tm.A + tm.E = x + tm.B*y - tm.A*x + tm.F = y - tm.A*y - tm.B*x + f.Transform(tm) +} + +// TransformSkewX horizontally skews the following text, drawings and images +// keeping the point (x, y) stationary. angleX ranges from -90 degrees (skew to +// the left) to 90 degrees (skew to the right). +// +// The TransformBegin() example demonstrates this method. +func (f *Fpdf) TransformSkewX(angleX, x, y float64) { + f.TransformSkew(angleX, 0, x, y) +} + +// TransformSkewY vertically skews the following text, drawings and images +// keeping the point (x, y) stationary. angleY ranges from -90 degrees (skew to +// the bottom) to 90 degrees (skew to the top). +// +// The TransformBegin() example demonstrates this method. +func (f *Fpdf) TransformSkewY(angleY, x, y float64) { + f.TransformSkew(0, angleY, x, y) +} + +// TransformSkew generally skews the following text, drawings and images +// keeping the point (x, y) stationary. angleX ranges from -90 degrees (skew to +// the left) to 90 degrees (skew to the right). angleY ranges from -90 degrees +// (skew to the bottom) to 90 degrees (skew to the top). +// +// The TransformBegin() example demonstrates this method. +func (f *Fpdf) TransformSkew(angleX, angleY, x, y float64) { + if angleX <= -90 || angleX >= 90 || angleY <= -90 || angleY >= 90 { + f.err = fmt.Errorf("skew values must be between -90° and 90°") + return + } + x *= f.k + y = (f.h - y) * f.k + var tm TransformMatrix + tm.A = 1 + tm.B = math.Tan(angleY * math.Pi / 180) + tm.C = math.Tan(angleX * math.Pi / 180) + tm.D = 1 + tm.E = -tm.C * y + tm.F = -tm.B * x + f.Transform(tm) +} + +// Transform generally transforms the following text, drawings and images +// according to the specified matrix. It is typically easier to use the various +// methods such as TransformRotate() and TransformMirrorVertical() instead. +func (f *Fpdf) Transform(tm TransformMatrix) { + if f.transformNest > 0 { + f.outf("%.5f %.5f %.5f %.5f %.5f %.5f cm", + tm.A, tm.B, tm.C, tm.D, tm.E, tm.F) + } else if f.err == nil { + f.err = fmt.Errorf("transformation context is not active") + } +} + +// TransformEnd applies a transformation that was begun with a call to TransformBegin(). +// +// The TransformBegin() example demonstrates this method. +func (f *Fpdf) TransformEnd() { + if f.transformNest > 0 { + f.transformNest-- + f.out("Q") + } else { + f.err = fmt.Errorf("error attempting to end transformation operation out of sequence") + } +} diff --git a/vendor/github.com/jung-kurt/gofpdf/go.mod b/vendor/github.com/jung-kurt/gofpdf/go.mod new file mode 100644 index 00000000..663e024c --- /dev/null +++ b/vendor/github.com/jung-kurt/gofpdf/go.mod @@ -0,0 +1,9 @@ +module github.com/jung-kurt/gofpdf + +go 1.12 + +require ( + github.com/boombuler/barcode v1.0.0 + github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58 + golang.org/x/image v0.0.0-20190507092727-e4e5bf290fec +) diff --git a/vendor/github.com/jung-kurt/gofpdf/go.sum b/vendor/github.com/jung-kurt/gofpdf/go.sum new file mode 100644 index 00000000..88488106 --- /dev/null +++ b/vendor/github.com/jung-kurt/gofpdf/go.sum @@ -0,0 +1,11 @@ +github.com/boombuler/barcode v1.0.0 h1:s1TvRnXwL2xJRaccrdcBQMZxq6X7DvsMogtmJeHDdrc= +github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58 h1:nlG4Wa5+minh3S9LVFtNoY+GVRiudA2e3EVfcCi3RCA= +github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +golang.org/x/image v0.0.0-20190507092727-e4e5bf290fec h1:arXJwtMuk5vqI1NHX0UTnNw977rYk5Sl4jQqHj+hun4= +golang.org/x/image v0.0.0-20190507092727-e4e5bf290fec/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/vendor/github.com/jung-kurt/gofpdf/grid.go b/vendor/github.com/jung-kurt/gofpdf/grid.go new file mode 100644 index 00000000..aca8508c --- /dev/null +++ b/vendor/github.com/jung-kurt/gofpdf/grid.go @@ -0,0 +1,446 @@ +package gofpdf + +import ( + "math" + "strconv" +) + +func unused(args ...interface{}) { +} + +// RGBType holds fields for red, green and blue color components (0..255) +type RGBType struct { + R, G, B int +} + +// RGBAType holds fields for red, green and blue color components (0..255) and +// an alpha transparency value (0..1) +type RGBAType struct { + R, G, B int + Alpha float64 +} + +// StateType holds various commonly used drawing values for convenient +// retrieval (StateGet()) and restore (Put) methods. +type StateType struct { + clrDraw, clrText, clrFill RGBType + lineWd float64 + fontSize float64 + alpha float64 + blendStr string + cellMargin float64 +} + +// StateGet returns a variable that contains common state values. +func StateGet(pdf *Fpdf) (st StateType) { + st.clrDraw.R, st.clrDraw.G, st.clrDraw.B = pdf.GetDrawColor() + st.clrFill.R, st.clrFill.G, st.clrFill.B = pdf.GetFillColor() + st.clrText.R, st.clrText.G, st.clrText.B = pdf.GetTextColor() + st.lineWd = pdf.GetLineWidth() + _, st.fontSize = pdf.GetFontSize() + st.alpha, st.blendStr = pdf.GetAlpha() + st.cellMargin = pdf.GetCellMargin() + return +} + +// Put sets the common state values contained in the state structure +// specified by st. +func (st StateType) Put(pdf *Fpdf) { + pdf.SetDrawColor(st.clrDraw.R, st.clrDraw.G, st.clrDraw.B) + pdf.SetFillColor(st.clrFill.R, st.clrFill.G, st.clrFill.B) + pdf.SetTextColor(st.clrText.R, st.clrText.G, st.clrText.B) + pdf.SetLineWidth(st.lineWd) + pdf.SetFontUnitSize(st.fontSize) + pdf.SetAlpha(st.alpha, st.blendStr) + pdf.SetCellMargin(st.cellMargin) +} + +// TickFormatFncType defines a callback for label drawing. +type TickFormatFncType func(val float64, precision int) string + +// defaultFormatter returns the string form of val with precision decimal places. +func defaultFormatter(val float64, precision int) string { + return strconv.FormatFloat(val, 'f', precision, 64) +} + +// GridType assists with the generation of graphs. It allows the application to +// work with logical data coordinates rather than page coordinates and assists +// with the drawing of a background grid. +type GridType struct { + // Chart coordinates in page units + x, y, w, h float64 + // X, Y, Wd, Ht float64 + // Slopes and intercepts scale data points to graph coordinates linearly + xm, xb, ym, yb float64 + // Tickmarks + xTicks, yTicks []float64 + // Labels are inside of graph boundary + XLabelIn, YLabelIn bool + // Labels on X-axis should be rotated + XLabelRotate bool + // Formatters; use nil to eliminate labels + XTickStr, YTickStr TickFormatFncType + // Subdivisions between tickmarks + XDiv, YDiv int + // Formatting precision + xPrecision, yPrecision int + // Line and label colors + ClrText, ClrMain, ClrSub RGBAType + // Line thickness + WdMain, WdSub float64 + // Label height in points + TextSize float64 +} + +// linear returns the slope and y-intercept of the straight line joining the +// two specified points. For scaling purposes, associate the arguments as +// follows: x1: observed low value, y1: desired low value, x2: observed high +// value, y2: desired high value. +func linear(x1, y1, x2, y2 float64) (slope, intercept float64) { + if x2 != x1 { + slope = (y2 - y1) / (x2 - x1) + intercept = y2 - x2*slope + } + return +} + +// linearTickmark returns the slope and intercept that will linearly map data +// values (the range of which is specified by the tickmark slice tm) to page +// values (the range of which is specified by lo and hi). +func linearTickmark(tm []float64, lo, hi float64) (slope, intercept float64) { + ln := len(tm) + if ln > 0 { + slope, intercept = linear(tm[0], lo, tm[ln-1], hi) + } + return +} + +// NewGrid returns a variable of type GridType that is initialized to draw on a +// rectangle of width w and height h with the upper left corner positioned at +// point (x, y). The coordinates are in page units, that is, the same as those +// specified in New(). +// +// The returned variable is initialized with a very simple default tickmark +// layout that ranges from 0 to 1 in both axes. Prior to calling Grid(), the +// application may establish a more suitable tickmark layout by calling the +// methods TickmarksContainX() and TickmarksContainY(). These methods bound the +// data range with appropriate boundaries and divisions. Alternatively, if the +// exact extent and divisions of the tickmark layout are known, the methods +// TickmarksExtentX() and TickmarksExtentY may be called instead. +func NewGrid(x, y, w, h float64) (grid GridType) { + grid.x = x + grid.y = y + grid.w = w + grid.h = h + grid.TextSize = 7 // Points + grid.TickmarksExtentX(0, 1, 1) + grid.TickmarksExtentY(0, 1, 1) + grid.XLabelIn = false + grid.YLabelIn = false + grid.XLabelRotate = false + grid.XDiv = 10 + grid.YDiv = 10 + grid.ClrText = RGBAType{R: 0, G: 0, B: 0, Alpha: 1} + grid.ClrMain = RGBAType{R: 128, G: 160, B: 128, Alpha: 1} + grid.ClrSub = RGBAType{R: 192, G: 224, B: 192, Alpha: 1} + grid.WdMain = 0.1 + grid.WdSub = 0.1 + grid.YTickStr = defaultFormatter + grid.XTickStr = defaultFormatter + return +} + +// WdAbs returns the absolute value of dataWd, specified in logical data units, +// that has been converted to the unit of measure specified in New(). +func (g GridType) WdAbs(dataWd float64) float64 { + return math.Abs(g.xm * dataWd) +} + +// Wd converts dataWd, specified in logical data units, to the unit of measure +// specified in New(). +func (g GridType) Wd(dataWd float64) float64 { + return g.xm * dataWd +} + +// XY converts dataX and dataY, specified in logical data units, to the X and Y +// position on the current page. +func (g GridType) XY(dataX, dataY float64) (x, y float64) { + return g.xm*dataX + g.xb, g.ym*dataY + g.yb +} + +// Pos returns the point, in page units, indicated by the relative positions +// xRel and yRel. These are values between 0 and 1. xRel specifies the relative +// position between the grid's left and right edges. yRel specifies the +// relative position between the grid's bottom and top edges. +func (g GridType) Pos(xRel, yRel float64) (x, y float64) { + x = g.w*xRel + g.x + y = g.h*(1-yRel) + g.y + return +} + +// X converts dataX, specified in logical data units, to the X position on the +// current page. +func (g GridType) X(dataX float64) float64 { + return g.xm*dataX + g.xb +} + +// HtAbs returns the absolute value of dataHt, specified in logical data units, +// that has been converted to the unit of measure specified in New(). +func (g GridType) HtAbs(dataHt float64) float64 { + return math.Abs(g.ym * dataHt) +} + +// Ht converts dataHt, specified in logical data units, to the unit of measure +// specified in New(). +func (g GridType) Ht(dataHt float64) float64 { + return g.ym * dataHt +} + +// Y converts dataY, specified in logical data units, to the Y position on the +// current page. +func (g GridType) Y(dataY float64) float64 { + return g.ym*dataY + g.yb +} + +// XRange returns the minimum and maximum values for the current tickmark +// sequence. These correspond to the data values of the graph's left and right +// edges. +func (g GridType) XRange() (min, max float64) { + min = g.xTicks[0] + max = g.xTicks[len(g.xTicks)-1] + return +} + +// YRange returns the minimum and maximum values for the current tickmark +// sequence. These correspond to the data values of the graph's bottom and top +// edges. +func (g GridType) YRange() (min, max float64) { + min = g.yTicks[0] + max = g.yTicks[len(g.yTicks)-1] + return +} + +// TickmarksContainX sets the tickmarks to be shown by Grid() in the horizontal +// dimension. The argument min and max specify the minimum and maximum values +// to be contained within the grid. The tickmark values that are generated are +// suitable for general purpose graphs. +// +// See TickmarkExtentX() for an alternative to this method to be used when the +// exact values of the tickmarks are to be set by the application. +func (g *GridType) TickmarksContainX(min, max float64) { + g.xTicks, g.xPrecision = Tickmarks(min, max) + g.xm, g.xb = linearTickmark(g.xTicks, g.x, g.x+g.w) +} + +// TickmarksContainY sets the tickmarks to be shown by Grid() in the vertical +// dimension. The argument min and max specify the minimum and maximum values +// to be contained within the grid. The tickmark values that are generated are +// suitable for general purpose graphs. +// +// See TickmarkExtentY() for an alternative to this method to be used when the +// exact values of the tickmarks are to be set by the application. +func (g *GridType) TickmarksContainY(min, max float64) { + g.yTicks, g.yPrecision = Tickmarks(min, max) + g.ym, g.yb = linearTickmark(g.yTicks, g.y+g.h, g.y) +} + +func extent(min, div float64, count int) (tm []float64, precision int) { + tm = make([]float64, count+1) + for j := 0; j <= count; j++ { + tm[j] = min + min += div + } + precision = TickmarkPrecision(div) + return +} + +// TickmarksExtentX sets the tickmarks to be shown by Grid() in the horizontal +// dimension. count specifies number of major tickmark subdivisions to be +// graphed. min specifies the leftmost data value. div specifies, in data +// units, the extent of each major tickmark subdivision. +// +// See TickmarkContainX() for an alternative to this method to be used when +// viewer-friendly tickmarks are to be determined automatically. +func (g *GridType) TickmarksExtentX(min, div float64, count int) { + g.xTicks, g.xPrecision = extent(min, div, count) + g.xm, g.xb = linearTickmark(g.xTicks, g.x, g.x+g.w) +} + +// TickmarksExtentY sets the tickmarks to be shown by Grid() in the vertical +// dimension. count specifies number of major tickmark subdivisions to be +// graphed. min specifies the bottommost data value. div specifies, in data +// units, the extent of each major tickmark subdivision. +// +// See TickmarkContainY() for an alternative to this method to be used when +// viewer-friendly tickmarks are to be determined automatically. +func (g *GridType) TickmarksExtentY(min, div float64, count int) { + g.yTicks, g.yPrecision = extent(min, div, count) + g.ym, g.yb = linearTickmark(g.yTicks, g.y+g.h, g.y) +} + +// func (g *GridType) SetXExtent(dataLf, paperLf, dataRt, paperRt float64) { +// g.xm, g.xb = linear(dataLf, paperLf, dataRt, paperRt) +// } + +// func (g *GridType) SetYExtent(dataTp, paperTp, dataBt, paperBt float64) { +// g.ym, g.yb = linear(dataTp, paperTp, dataBt, paperBt) +// } + +func lineAttr(pdf *Fpdf, clr RGBAType, lineWd float64) { + pdf.SetLineWidth(lineWd) + pdf.SetAlpha(clr.Alpha, "Normal") + pdf.SetDrawColor(clr.R, clr.G, clr.B) +} + +// Grid generates a graph-paperlike set of grid lines on the current page. +func (g GridType) Grid(pdf *Fpdf) { + var st StateType + var yLen, xLen int + var textSz, halfTextSz, yMin, yMax, xMin, xMax, yDiv, xDiv float64 + var str string + var strOfs, strWd, tp, bt, lf, rt, drawX, drawY float64 + + xLen = len(g.xTicks) + yLen = len(g.yTicks) + if xLen > 1 && yLen > 1 { + + st = StateGet(pdf) + + line := func(x1, y1, x2, y2 float64, heavy bool) { + if heavy { + lineAttr(pdf, g.ClrMain, g.WdMain) + } else { + lineAttr(pdf, g.ClrSub, g.WdSub) + } + pdf.Line(x1, y1, x2, y2) + } + + textSz = pdf.PointToUnitConvert(g.TextSize) + halfTextSz = textSz / 2 + + pdf.SetAutoPageBreak(false, 0) + pdf.SetFontUnitSize(textSz) + strOfs = pdf.GetStringWidth("0") + pdf.SetFillColor(255, 255, 255) + pdf.SetCellMargin(0) + + xMin = g.xTicks[0] + xMax = g.xTicks[xLen-1] + + yMin = g.yTicks[0] + yMax = g.yTicks[yLen-1] + + lf = g.X(xMin) + rt = g.X(xMax) + bt = g.Y(yMin) + tp = g.Y(yMax) + + // Verticals along X axis + xDiv = g.xTicks[1] - g.xTicks[0] + if g.XDiv > 0 { + xDiv = xDiv / float64(g.XDiv) + } + xDiv = g.Wd(xDiv) + for j, x := range g.xTicks { + drawX = g.X(x) + line(drawX, tp, drawX, bt, true) + if j < xLen-1 { + for k := 1; k < g.XDiv; k++ { + drawX += xDiv + line(drawX, tp, drawX, bt, false) + } + } + } + + // Horizontals along Y axis + yDiv = g.yTicks[1] - g.yTicks[0] + if g.YDiv > 0 { + yDiv = yDiv / float64(g.YDiv) + } + yDiv = g.Ht(yDiv) + for j, y := range g.yTicks { + drawY = g.Y(y) + line(lf, drawY, rt, drawY, true) + if j < yLen-1 { + for k := 1; k < g.YDiv; k++ { + drawY += yDiv + line(lf, drawY, rt, drawY, false) + } + } + } + + // X labels + if g.XTickStr != nil { + drawY = bt + for _, x := range g.xTicks { + str = g.XTickStr(x, g.xPrecision) + strWd = pdf.GetStringWidth(str) + drawX = g.X(x) + if g.XLabelRotate { + pdf.TransformBegin() + pdf.TransformRotate(90, drawX, drawY) + if g.XLabelIn { + pdf.SetXY(drawX+strOfs, drawY-halfTextSz) + } else { + pdf.SetXY(drawX-strOfs-strWd, drawY-halfTextSz) + } + pdf.CellFormat(strWd, textSz, str, "", 0, "L", true, 0, "") + pdf.TransformEnd() + } else { + drawX -= strWd / 2.0 + if g.XLabelIn { + pdf.SetXY(drawX, drawY-textSz-strOfs) + } else { + pdf.SetXY(drawX, drawY+strOfs) + } + pdf.CellFormat(strWd, textSz, str, "", 0, "L", true, 0, "") + } + } + } + + // Y labels + if g.YTickStr != nil { + drawX = lf + for _, y := range g.yTicks { + // str = strconv.FormatFloat(y, 'f', g.yPrecision, 64) + str = g.YTickStr(y, g.yPrecision) + strWd = pdf.GetStringWidth(str) + if g.YLabelIn { + pdf.SetXY(drawX+strOfs, g.Y(y)-halfTextSz) + } else { + pdf.SetXY(lf-strOfs-strWd, g.Y(y)-halfTextSz) + } + pdf.CellFormat(strWd, textSz, str, "", 0, "L", true, 0, "") + } + } + + // Restore drawing attributes + st.Put(pdf) + + } + +} + +// Plot plots a series of count line segments from xMin to xMax. It repeatedly +// calls fnc(x) to retrieve the y value associate with x. The currently +// selected line drawing attributes are used. +func (g GridType) Plot(pdf *Fpdf, xMin, xMax float64, count int, fnc func(x float64) (y float64)) { + if count > 0 { + var x, delta, drawX0, drawY0, drawX1, drawY1 float64 + delta = (xMax - xMin) / float64(count) + x = xMin + for j := 0; j <= count; j++ { + if j == 0 { + drawX1 = g.X(x) + drawY1 = g.Y(fnc(x)) + } else { + pdf.Line(drawX0, drawY0, drawX1, drawY1) + } + x += delta + drawX0 = drawX1 + drawY0 = drawY1 + drawX1 = g.X(x) + drawY1 = g.Y(fnc(x)) + } + } +} diff --git a/vendor/github.com/jung-kurt/gofpdf/htmlbasic.go b/vendor/github.com/jung-kurt/gofpdf/htmlbasic.go new file mode 100644 index 00000000..9e4eca4e --- /dev/null +++ b/vendor/github.com/jung-kurt/gofpdf/htmlbasic.go @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2014 Kurt Jung (Gmail: kurt.w.jung) + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package gofpdf + +import ( + "regexp" + "strings" +) + +// HTMLBasicSegmentType defines a segment of literal text in which the current +// attributes do not vary, or an open tag or a close tag. +type HTMLBasicSegmentType struct { + Cat byte // 'O' open tag, 'C' close tag, 'T' text + Str string // Literal text unchanged, tags are lower case + Attr map[string]string // Attribute keys are lower case +} + +// HTMLBasicTokenize returns a list of HTML tags and literal elements. This is +// done with regular expressions, so the result is only marginally better than +// useless. +func HTMLBasicTokenize(htmlStr string) (list []HTMLBasicSegmentType) { + // This routine is adapted from http://www.fpdf.org/ + list = make([]HTMLBasicSegmentType, 0, 16) + htmlStr = strings.Replace(htmlStr, "\n", " ", -1) + htmlStr = strings.Replace(htmlStr, "\r", "", -1) + tagRe, _ := regexp.Compile(`(?U)<.*>`) + attrRe, _ := regexp.Compile(`([^=]+)=["']?([^"']+)`) + capList := tagRe.FindAllStringIndex(htmlStr, -1) + if capList != nil { + var seg HTMLBasicSegmentType + var parts []string + pos := 0 + for _, cap := range capList { + if pos < cap[0] { + seg.Cat = 'T' + seg.Str = htmlStr[pos:cap[0]] + seg.Attr = nil + list = append(list, seg) + } + if htmlStr[cap[0]+1] == '/' { + seg.Cat = 'C' + seg.Str = strings.ToLower(htmlStr[cap[0]+2 : cap[1]-1]) + seg.Attr = nil + list = append(list, seg) + } else { + // Extract attributes + parts = strings.Split(htmlStr[cap[0]+1:cap[1]-1], " ") + if len(parts) > 0 { + for j, part := range parts { + if j == 0 { + seg.Cat = 'O' + seg.Str = strings.ToLower(parts[0]) + seg.Attr = make(map[string]string) + } else { + attrList := attrRe.FindAllStringSubmatch(part, -1) + if attrList != nil { + for _, attr := range attrList { + seg.Attr[strings.ToLower(attr[1])] = attr[2] + } + } + } + } + list = append(list, seg) + } + } + pos = cap[1] + } + if len(htmlStr) > pos { + seg.Cat = 'T' + seg.Str = htmlStr[pos:] + seg.Attr = nil + list = append(list, seg) + } + } else { + list = append(list, HTMLBasicSegmentType{Cat: 'T', Str: htmlStr, Attr: nil}) + } + return +} + +// HTMLBasicType is used for rendering a very basic subset of HTML. It supports +// only hyperlinks and bold, italic and underscore attributes. In the Link +// structure, the ClrR, ClrG and ClrB fields (0 through 255) define the color +// of hyperlinks. The Bold, Italic and Underscore values define the hyperlink +// style. +type HTMLBasicType struct { + pdf *Fpdf + Link struct { + ClrR, ClrG, ClrB int + Bold, Italic, Underscore bool + } +} + +// HTMLBasicNew returns an instance that facilitates writing basic HTML in the +// specified PDF file. +func (f *Fpdf) HTMLBasicNew() (html HTMLBasicType) { + html.pdf = f + html.Link.ClrR, html.Link.ClrG, html.Link.ClrB = 0, 0, 128 + html.Link.Bold, html.Link.Italic, html.Link.Underscore = false, false, true + return +} + +// Write prints text from the current position using the currently selected +// font. See HTMLBasicNew() to create a receiver that is associated with the +// PDF document instance. The text can be encoded with a basic subset of HTML +// that includes hyperlinks and tags for italic (I), bold (B), underscore +// (U) and center (CENTER) attributes. When the right margin is reached a line +// break occurs and text continues from the left margin. Upon method exit, the +// current position is left at the end of the text. +// +// lineHt indicates the line height in the unit of measure specified in New(). +func (html *HTMLBasicType) Write(lineHt float64, htmlStr string) { + var boldLvl, italicLvl, underscoreLvl, linkBold, linkItalic, linkUnderscore int + var textR, textG, textB = html.pdf.GetTextColor() + var hrefStr string + if html.Link.Bold { + linkBold = 1 + } + if html.Link.Italic { + linkItalic = 1 + } + if html.Link.Underscore { + linkUnderscore = 1 + } + setStyle := func(boldAdj, italicAdj, underscoreAdj int) { + styleStr := "" + boldLvl += boldAdj + if boldLvl > 0 { + styleStr += "B" + } + italicLvl += italicAdj + if italicLvl > 0 { + styleStr += "I" + } + underscoreLvl += underscoreAdj + if underscoreLvl > 0 { + styleStr += "U" + } + html.pdf.SetFont("", styleStr, 0) + } + putLink := func(urlStr, txtStr string) { + // Put a hyperlink + html.pdf.SetTextColor(html.Link.ClrR, html.Link.ClrG, html.Link.ClrB) + setStyle(linkBold, linkItalic, linkUnderscore) + html.pdf.WriteLinkString(lineHt, txtStr, urlStr) + setStyle(-linkBold, -linkItalic, -linkUnderscore) + html.pdf.SetTextColor(textR, textG, textB) + } + list := HTMLBasicTokenize(htmlStr) + var ok bool + alignStr := "L" + for _, el := range list { + switch el.Cat { + case 'T': + if len(hrefStr) > 0 { + putLink(hrefStr, el.Str) + hrefStr = "" + } else { + if alignStr == "C" || alignStr == "R" { + html.pdf.WriteAligned(0, lineHt, el.Str, alignStr) + } else { + html.pdf.Write(lineHt, el.Str) + } + } + case 'O': + switch el.Str { + case "b": + setStyle(1, 0, 0) + case "i": + setStyle(0, 1, 0) + case "u": + setStyle(0, 0, 1) + case "br": + html.pdf.Ln(lineHt) + case "center": + html.pdf.Ln(lineHt) + alignStr = "C" + case "right": + html.pdf.Ln(lineHt) + alignStr = "R" + case "left": + html.pdf.Ln(lineHt) + alignStr = "L" + case "a": + hrefStr, ok = el.Attr["href"] + if !ok { + hrefStr = "" + } + } + case 'C': + switch el.Str { + case "b": + setStyle(-1, 0, 0) + case "i": + setStyle(0, -1, 0) + case "u": + setStyle(0, 0, -1) + case "center": + html.pdf.Ln(lineHt) + alignStr = "L" + case "right": + html.pdf.Ln(lineHt) + alignStr = "L" + } + } + } +} diff --git a/vendor/github.com/jung-kurt/gofpdf/label.go b/vendor/github.com/jung-kurt/gofpdf/label.go new file mode 100644 index 00000000..b90d8f32 --- /dev/null +++ b/vendor/github.com/jung-kurt/gofpdf/label.go @@ -0,0 +1,82 @@ +package gofpdf + +// Adapted from Nice Numbers for Graph Labels by Paul Heckbert from "Graphics +// Gems", Academic Press, 1990 + +// Paul Heckbert 2 Dec 88 + +// https://github.com/erich666/GraphicsGems + +// LICENSE + +// This code repository predates the concept of Open Source, and predates most +// licenses along such lines. As such, the official license truly is: + +// EULA: The Graphics Gems code is copyright-protected. In other words, you +// cannot claim the text of the code as your own and resell it. Using the code +// is permitted in any program, product, or library, non-commercial or +// commercial. Giving credit is not required, though is a nice gesture. The +// code comes as-is, and if there are any flaws or problems with any Gems code, +// nobody involved with Gems - authors, editors, publishers, or webmasters - +// are to be held responsible. Basically, don't be a jerk, and remember that +// anything free comes with no guarantee. + +import ( + "math" +) + +// niceNum returns a "nice" number approximately equal to x. The number is +// rounded if round is true, converted to its ceiling otherwise. +func niceNum(val float64, round bool) float64 { + var nf float64 + + exp := int(math.Floor(math.Log10(val))) + f := val / math.Pow10(exp) + if round { + switch { + case f < 1.5: + nf = 1 + case f < 3.0: + nf = 2 + case f < 7.0: + nf = 5 + default: + nf = 10 + } + } else { + switch { + case f <= 1: + nf = 1 + case f <= 2.0: + nf = 2 + case f <= 5.0: + nf = 5 + default: + nf = 10 + } + } + return nf * math.Pow10(exp) +} + +// TickmarkPrecision returns an appropriate precision value for label +// formatting. +func TickmarkPrecision(div float64) int { + return int(math.Max(-math.Floor(math.Log10(div)), 0)) +} + +// Tickmarks returns a slice of tickmarks appropriate for a chart axis and an +// appropriate precision for formatting purposes. The values min and max will +// be contained within the tickmark range. +func Tickmarks(min, max float64) (list []float64, precision int) { + if max > min { + spread := niceNum(max-min, false) + d := niceNum((spread / 4), true) + graphMin := math.Floor(min/d) * d + graphMax := math.Ceil(max/d) * d + precision = TickmarkPrecision(d) + for x := graphMin; x < graphMax+0.5*d; x += d { + list = append(list, x) + } + } + return +} diff --git a/vendor/github.com/jung-kurt/gofpdf/layer.go b/vendor/github.com/jung-kurt/gofpdf/layer.go new file mode 100644 index 00000000..bca364f9 --- /dev/null +++ b/vendor/github.com/jung-kurt/gofpdf/layer.go @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2014 Kurt Jung (Gmail: kurt.w.jung) + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package gofpdf + +// Routines in this file are translated from +// http://www.fpdf.org/en/script/script97.php + +type layerType struct { + name string + visible bool + objNum int // object number +} + +type layerRecType struct { + list []layerType + currentLayer int + openLayerPane bool +} + +func (f *Fpdf) layerInit() { + f.layer.list = make([]layerType, 0) + f.layer.currentLayer = -1 + f.layer.openLayerPane = false +} + +// AddLayer defines a layer that can be shown or hidden when the document is +// displayed. name specifies the layer name that the document reader will +// display in the layer list. visible specifies whether the layer will be +// initially visible. The return value is an integer ID that is used in a call +// to BeginLayer(). +func (f *Fpdf) AddLayer(name string, visible bool) (layerID int) { + layerID = len(f.layer.list) + f.layer.list = append(f.layer.list, layerType{name: name, visible: visible}) + return +} + +// BeginLayer is called to begin adding content to the specified layer. All +// content added to the page between a call to BeginLayer and a call to +// EndLayer is added to the layer specified by id. See AddLayer for more +// details. +func (f *Fpdf) BeginLayer(id int) { + f.EndLayer() + if id >= 0 && id < len(f.layer.list) { + f.outf("/OC /OC%d BDC", id) + f.layer.currentLayer = id + } +} + +// EndLayer is called to stop adding content to the currently active layer. See +// BeginLayer for more details. +func (f *Fpdf) EndLayer() { + if f.layer.currentLayer >= 0 { + f.out("EMC") + f.layer.currentLayer = -1 + } +} + +// OpenLayerPane advises the document reader to open the layer pane when the +// document is initially displayed. +func (f *Fpdf) OpenLayerPane() { + f.layer.openLayerPane = true +} + +func (f *Fpdf) layerEndDoc() { + if len(f.layer.list) > 0 { + if f.pdfVersion < "1.5" { + f.pdfVersion = "1.5" + } + } +} + +func (f *Fpdf) layerPutLayers() { + for j, l := range f.layer.list { + f.newobj() + f.layer.list[j].objNum = f.n + f.outf("<>", f.textstring(utf8toutf16(l.name))) + f.out("endobj") + } +} + +func (f *Fpdf) layerPutResourceDict() { + if len(f.layer.list) > 0 { + f.out("/Properties <<") + for j, layer := range f.layer.list { + f.outf("/OC%d %d 0 R", j, layer.objNum) + } + f.out(">>") + } + +} + +func (f *Fpdf) layerPutCatalog() { + if len(f.layer.list) > 0 { + onStr := "" + offStr := "" + for _, layer := range f.layer.list { + onStr += sprintf("%d 0 R ", layer.objNum) + if !layer.visible { + offStr += sprintf("%d 0 R ", layer.objNum) + } + } + f.outf("/OCProperties <>>>", onStr, offStr, onStr) + if f.layer.openLayerPane { + f.out("/PageMode /UseOC") + } + } +} diff --git a/vendor/github.com/jung-kurt/gofpdf/png.go b/vendor/github.com/jung-kurt/gofpdf/png.go new file mode 100644 index 00000000..15002b3f --- /dev/null +++ b/vendor/github.com/jung-kurt/gofpdf/png.go @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2013-2016 Kurt Jung (Gmail: kurt.w.jung) + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package gofpdf + +import ( + "bytes" + "fmt" + "strings" +) + +func (f *Fpdf) pngColorSpace(ct byte) (colspace string, colorVal int) { + colorVal = 1 + switch ct { + case 0, 4: + colspace = "DeviceGray" + case 2, 6: + colspace = "DeviceRGB" + colorVal = 3 + case 3: + colspace = "Indexed" + default: + f.err = fmt.Errorf("unknown color type in PNG buffer: %d", ct) + } + return +} + +func (f *Fpdf) parsepngstream(buf *bytes.Buffer, readdpi bool) (info *ImageInfoType) { + info = f.newImageInfo() + // Check signature + if string(buf.Next(8)) != "\x89PNG\x0d\x0a\x1a\x0a" { + f.err = fmt.Errorf("not a PNG buffer") + return + } + // Read header chunk + _ = buf.Next(4) + if string(buf.Next(4)) != "IHDR" { + f.err = fmt.Errorf("incorrect PNG buffer") + return + } + w := f.readBeInt32(buf) + h := f.readBeInt32(buf) + bpc := f.readByte(buf) + if bpc > 8 { + f.err = fmt.Errorf("16-bit depth not supported in PNG file") + } + ct := f.readByte(buf) + var colspace string + var colorVal int + colspace, colorVal = f.pngColorSpace(ct) + if f.err != nil { + return + } + if f.readByte(buf) != 0 { + f.err = fmt.Errorf("'unknown compression method in PNG buffer") + return + } + if f.readByte(buf) != 0 { + f.err = fmt.Errorf("'unknown filter method in PNG buffer") + return + } + if f.readByte(buf) != 0 { + f.err = fmt.Errorf("interlacing not supported in PNG buffer") + return + } + _ = buf.Next(4) + dp := sprintf("/Predictor 15 /Colors %d /BitsPerComponent %d /Columns %d", colorVal, bpc, w) + // Scan chunks looking for palette, transparency and image data + pal := make([]byte, 0, 32) + var trns []int + data := make([]byte, 0, 32) + loop := true + for loop { + n := int(f.readBeInt32(buf)) + // dbg("Loop [%d]", n) + switch string(buf.Next(4)) { + case "PLTE": + // dbg("PLTE") + // Read palette + pal = buf.Next(n) + _ = buf.Next(4) + case "tRNS": + // dbg("tRNS") + // Read transparency info + t := buf.Next(n) + switch ct { + case 0: + trns = []int{int(t[1])} // ord(substr($t,1,1))); + case 2: + trns = []int{int(t[1]), int(t[3]), int(t[5])} // array(ord(substr($t,1,1)), ord(substr($t,3,1)), ord(substr($t,5,1))); + default: + pos := strings.Index(string(t), "\x00") + if pos >= 0 { + trns = []int{pos} // array($pos); + } + } + _ = buf.Next(4) + case "IDAT": + // dbg("IDAT") + // Read image data block + data = append(data, buf.Next(n)...) + _ = buf.Next(4) + case "IEND": + // dbg("IEND") + loop = false + case "pHYs": + // dbg("pHYs") + // png files theoretically support different x/y dpi + // but we ignore files like this + // but if they're the same then we can stamp our info + // object with it + x := int(f.readBeInt32(buf)) + y := int(f.readBeInt32(buf)) + units := buf.Next(1)[0] + // fmt.Printf("got a pHYs block, x=%d, y=%d, u=%d, readdpi=%t\n", + // x, y, int(units), readdpi) + // only modify the info block if the user wants us to + if x == y && readdpi { + switch units { + // if units is 1 then measurement is px/meter + case 1: + info.dpi = float64(x) / 39.3701 // inches per meter + default: + info.dpi = float64(x) + } + } + _ = buf.Next(4) + default: + // dbg("default") + _ = buf.Next(n + 4) + } + if loop { + loop = n > 0 + } + } + if colspace == "Indexed" && len(pal) == 0 { + f.err = fmt.Errorf("missing palette in PNG buffer") + } + info.w = float64(w) + info.h = float64(h) + info.cs = colspace + info.bpc = int(bpc) + info.f = "FlateDecode" + info.dp = dp + info.pal = pal + info.trns = trns + // dbg("ct [%d]", ct) + if ct >= 4 { + // Separate alpha and color channels + var err error + data, err = sliceUncompress(data) + if err != nil { + f.err = err + return + } + var color, alpha bytes.Buffer + if ct == 4 { + // Gray image + width := int(w) + height := int(h) + length := 2 * width + var pos, elPos int + for i := 0; i < height; i++ { + pos = (1 + length) * i + color.WriteByte(data[pos]) + alpha.WriteByte(data[pos]) + elPos = pos + 1 + for k := 0; k < width; k++ { + color.WriteByte(data[elPos]) + alpha.WriteByte(data[elPos+1]) + elPos += 2 + } + } + } else { + // RGB image + width := int(w) + height := int(h) + length := 4 * width + var pos, elPos int + for i := 0; i < height; i++ { + pos = (1 + length) * i + color.WriteByte(data[pos]) + alpha.WriteByte(data[pos]) + elPos = pos + 1 + for k := 0; k < width; k++ { + color.Write(data[elPos : elPos+3]) + alpha.WriteByte(data[elPos+3]) + elPos += 4 + } + } + } + data = sliceCompress(color.Bytes()) + info.smask = sliceCompress(alpha.Bytes()) + if f.pdfVersion < "1.4" { + f.pdfVersion = "1.4" + } + } + info.data = data + return +} diff --git a/vendor/github.com/jung-kurt/gofpdf/protect.go b/vendor/github.com/jung-kurt/gofpdf/protect.go new file mode 100644 index 00000000..9a335345 --- /dev/null +++ b/vendor/github.com/jung-kurt/gofpdf/protect.go @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2013-2014 Kurt Jung (Gmail: kurt.w.jung) + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +// PDF protection is adapted from the work of Klemen VODOPIVEC for the fpdf +// product. + +package gofpdf + +import ( + "crypto/md5" + "crypto/rc4" + "encoding/binary" + "math/rand" +) + +// Advisory bitflag constants that control document activities +const ( + CnProtectPrint = 4 + CnProtectModify = 8 + CnProtectCopy = 16 + CnProtectAnnotForms = 32 +) + +type protectType struct { + encrypted bool + uValue []byte + oValue []byte + pValue int + padding []byte + encryptionKey []byte + objNum int + rc4cipher *rc4.Cipher + rc4n uint32 // Object number associated with rc4 cipher +} + +func (p *protectType) rc4(n uint32, buf *[]byte) { + if p.rc4cipher == nil || p.rc4n != n { + p.rc4cipher, _ = rc4.NewCipher(p.objectKey(n)) + p.rc4n = n + } + p.rc4cipher.XORKeyStream(*buf, *buf) +} + +func (p *protectType) objectKey(n uint32) []byte { + var nbuf, b []byte + nbuf = make([]byte, 8, 8) + binary.LittleEndian.PutUint32(nbuf, n) + b = append(b, p.encryptionKey...) + b = append(b, nbuf[0], nbuf[1], nbuf[2], 0, 0) + s := md5.Sum(b) + return s[0:10] +} + +func oValueGen(userPass, ownerPass []byte) (v []byte) { + var c *rc4.Cipher + tmp := md5.Sum(ownerPass) + c, _ = rc4.NewCipher(tmp[0:5]) + size := len(userPass) + v = make([]byte, size, size) + c.XORKeyStream(v, userPass) + return +} + +func (p *protectType) uValueGen() (v []byte) { + var c *rc4.Cipher + c, _ = rc4.NewCipher(p.encryptionKey) + size := len(p.padding) + v = make([]byte, size, size) + c.XORKeyStream(v, p.padding) + return +} + +func (p *protectType) setProtection(privFlag byte, userPassStr, ownerPassStr string) { + privFlag = 192 | (privFlag & (CnProtectCopy | CnProtectModify | CnProtectPrint | CnProtectAnnotForms)) + p.padding = []byte{ + 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, + 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08, + 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, + 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A, + } + userPass := []byte(userPassStr) + var ownerPass []byte + if ownerPassStr == "" { + ownerPass = make([]byte, 8, 8) + binary.LittleEndian.PutUint64(ownerPass, uint64(rand.Int63())) + } else { + ownerPass = []byte(ownerPassStr) + } + userPass = append(userPass, p.padding...)[0:32] + ownerPass = append(ownerPass, p.padding...)[0:32] + p.encrypted = true + p.oValue = oValueGen(userPass, ownerPass) + var buf []byte + buf = append(buf, userPass...) + buf = append(buf, p.oValue...) + buf = append(buf, privFlag, 0xff, 0xff, 0xff) + sum := md5.Sum(buf) + p.encryptionKey = sum[0:5] + p.uValue = p.uValueGen() + p.pValue = -(int(privFlag^255) + 1) +} diff --git a/vendor/github.com/jung-kurt/gofpdf/splittext.go b/vendor/github.com/jung-kurt/gofpdf/splittext.go new file mode 100644 index 00000000..3902199b --- /dev/null +++ b/vendor/github.com/jung-kurt/gofpdf/splittext.go @@ -0,0 +1,54 @@ +package gofpdf + +import ( + "math" + // "strings" + "unicode" +) + +// SplitText splits UTF-8 encoded text into several lines using the current +// font. Each line has its length limited to a maximum width given by w. This +// function can be used to determine the total height of wrapped text for +// vertical placement purposes. +func (f *Fpdf) SplitText(txt string, w float64) (lines []string) { + cw := f.currentFont.Cw + wmax := int(math.Ceil((w - 2*f.cMargin) * 1000 / f.fontSize)) + s := []rune(txt) // Return slice of UTF-8 runes + nb := len(s) + for nb > 0 && s[nb-1] == '\n' { + nb-- + } + s = s[0:nb] + sep := -1 + i := 0 + j := 0 + l := 0 + for i < nb { + c := s[i] + l += cw[c] + if unicode.IsSpace(c) { + // if c == ' ' || c == '\t' || c == '\n' { + sep = i + } + if c == '\n' || l > wmax { + if sep == -1 { + if i == j { + i++ + } + sep = i + } else { + i = sep + 1 + } + lines = append(lines, string(s[j:sep])) + sep = -1 + j = i + l = 0 + } else { + i++ + } + } + if i != j { + lines = append(lines, string(s[j:i])) + } + return lines +} diff --git a/vendor/github.com/jung-kurt/gofpdf/spotcolor.go b/vendor/github.com/jung-kurt/gofpdf/spotcolor.go new file mode 100644 index 00000000..33aca827 --- /dev/null +++ b/vendor/github.com/jung-kurt/gofpdf/spotcolor.go @@ -0,0 +1,184 @@ +// Copyright (c) Kurt Jung (Gmail: kurt.w.jung) +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +// Adapted from http://www.fpdf.org/en/script/script89.php by Olivier PLATHEY + +package gofpdf + +import ( + "fmt" + "strings" +) + +func byteBound(v byte) byte { + if v > 100 { + return 100 + } + return v +} + +// AddSpotColor adds an ink-based CMYK color to the gofpdf instance and +// associates it with the specified name. The individual components specify +// percentages ranging from 0 to 100. Values above this are quietly capped to +// 100. An error occurs if the specified name is already associated with a +// color. +func (f *Fpdf) AddSpotColor(nameStr string, c, m, y, k byte) { + if f.err == nil { + _, ok := f.spotColorMap[nameStr] + if !ok { + id := len(f.spotColorMap) + 1 + f.spotColorMap[nameStr] = spotColorType{ + id: id, + val: cmykColorType{ + c: byteBound(c), + m: byteBound(m), + y: byteBound(y), + k: byteBound(k), + }, + } + } else { + f.err = fmt.Errorf("name \"%s\" is already associated with a spot color", nameStr) + } + } +} + +func (f *Fpdf) getSpotColor(nameStr string) (clr spotColorType, ok bool) { + if f.err == nil { + clr, ok = f.spotColorMap[nameStr] + if !ok { + f.err = fmt.Errorf("spot color name \"%s\" is not registered", nameStr) + } + } + return +} + +// SetDrawSpotColor sets the current draw color to the spot color associated +// with nameStr. An error occurs if the name is not associated with a color. +// The value for tint ranges from 0 (no intensity) to 100 (full intensity). It +// is quietly bounded to this range. +func (f *Fpdf) SetDrawSpotColor(nameStr string, tint byte) { + var clr spotColorType + var ok bool + + clr, ok = f.getSpotColor(nameStr) + if ok { + f.color.draw.mode = colorModeSpot + f.color.draw.spotStr = nameStr + f.color.draw.str = sprintf("/CS%d CS %.3f SCN", clr.id, float64(byteBound(tint))/100) + if f.page > 0 { + f.out(f.color.draw.str) + } + } +} + +// SetFillSpotColor sets the current fill color to the spot color associated +// with nameStr. An error occurs if the name is not associated with a color. +// The value for tint ranges from 0 (no intensity) to 100 (full intensity). It +// is quietly bounded to this range. +func (f *Fpdf) SetFillSpotColor(nameStr string, tint byte) { + var clr spotColorType + var ok bool + + clr, ok = f.getSpotColor(nameStr) + if ok { + f.color.fill.mode = colorModeSpot + f.color.fill.spotStr = nameStr + f.color.fill.str = sprintf("/CS%d cs %.3f scn", clr.id, float64(byteBound(tint))/100) + f.colorFlag = f.color.fill.str != f.color.text.str + if f.page > 0 { + f.out(f.color.fill.str) + } + } +} + +// SetTextSpotColor sets the current text color to the spot color associated +// with nameStr. An error occurs if the name is not associated with a color. +// The value for tint ranges from 0 (no intensity) to 100 (full intensity). It +// is quietly bounded to this range. +func (f *Fpdf) SetTextSpotColor(nameStr string, tint byte) { + var clr spotColorType + var ok bool + + clr, ok = f.getSpotColor(nameStr) + if ok { + f.color.text.mode = colorModeSpot + f.color.text.spotStr = nameStr + f.color.text.str = sprintf("/CS%d cs %.3f scn", clr.id, float64(byteBound(tint))/100) + f.colorFlag = f.color.text.str != f.color.text.str + } +} + +func (f *Fpdf) returnSpotColor(clr colorType) (name string, c, m, y, k byte) { + var spotClr spotColorType + var ok bool + + name = clr.spotStr + if name != "" { + spotClr, ok = f.getSpotColor(name) + if ok { + c = spotClr.val.c + m = spotClr.val.m + y = spotClr.val.y + k = spotClr.val.k + } + } + return +} + +// GetDrawSpotColor returns the most recently used spot color information for +// drawing. This will not be the current drawing color if some other color type +// such as RGB is active. If no spot color has been set for drawing, zero +// values are returned. +func (f *Fpdf) GetDrawSpotColor() (name string, c, m, y, k byte) { + return f.returnSpotColor(f.color.draw) +} + +// GetTextSpotColor returns the most recently used spot color information for +// text output. This will not be the current text color if some other color +// type such as RGB is active. If no spot color has been set for text, zero +// values are returned. +func (f *Fpdf) GetTextSpotColor() (name string, c, m, y, k byte) { + return f.returnSpotColor(f.color.text) +} + +// GetFillSpotColor returns the most recently used spot color information for +// fill output. This will not be the current fill color if some other color +// type such as RGB is active. If no fill spot color has been set, zero values +// are returned. +func (f *Fpdf) GetFillSpotColor() (name string, c, m, y, k byte) { + return f.returnSpotColor(f.color.fill) +} + +func (f *Fpdf) putSpotColors() { + for k, v := range f.spotColorMap { + f.newobj() + f.outf("[/Separation /%s", strings.Replace(k, " ", "#20", -1)) + f.out("/DeviceCMYK <<") + f.out("/Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0] ") + f.outf("/C1 [%.3f %.3f %.3f %.3f] ", float64(v.val.c)/100, float64(v.val.m)/100, + float64(v.val.y)/100, float64(v.val.k)/100) + f.out("/FunctionType 2 /Domain [0 1] /N 1>>]") + f.out("endobj") + v.objID = f.n + f.spotColorMap[k] = v + } +} + +func (f *Fpdf) spotColorPutResourceDict() { + f.out("/ColorSpace <<") + for _, clr := range f.spotColorMap { + f.outf("/CS%d %d 0 R", clr.id, clr.objID) + } + f.out(">>") +} diff --git a/vendor/github.com/jung-kurt/gofpdf/subwrite.go b/vendor/github.com/jung-kurt/gofpdf/subwrite.go new file mode 100644 index 00000000..e7ebd774 --- /dev/null +++ b/vendor/github.com/jung-kurt/gofpdf/subwrite.go @@ -0,0 +1,35 @@ +package gofpdf + +// Adapted from http://www.fpdf.org/en/script/script61.php by Wirus and released with the FPDF license. + +// SubWrite prints text from the current position in the same way as Write(). +// ht is the line height in the unit of measure specified in New(). str +// specifies the text to write. subFontSize is the size of the font in points. +// subOffset is the vertical offset of the text in points; a positive value +// indicates a superscript, a negative value indicates a subscript. link is the +// identifier returned by AddLink() or 0 for no internal link. linkStr is a +// target URL or empty for no external link. A non--zero value for link takes +// precedence over linkStr. +// +// The SubWrite example demonstrates this method. +func (f *Fpdf) SubWrite(ht float64, str string, subFontSize, subOffset float64, link int, linkStr string) { + if f.err != nil { + return + } + // resize font + subFontSizeOld := f.fontSizePt + f.SetFontSize(subFontSize) + // reposition y + subOffset = (((subFontSize - subFontSizeOld) / f.k) * 0.3) + (subOffset / f.k) + subX := f.x + subY := f.y + f.SetXY(subX, subY-subOffset) + //Output text + f.write(ht, str, link, linkStr) + // restore y position + subX = f.x + subY = f.y + f.SetXY(subX, subY+subOffset) + // restore font size + f.SetFontSize(subFontSizeOld) +} diff --git a/vendor/github.com/jung-kurt/gofpdf/svgbasic.go b/vendor/github.com/jung-kurt/gofpdf/svgbasic.go new file mode 100644 index 00000000..cc350736 --- /dev/null +++ b/vendor/github.com/jung-kurt/gofpdf/svgbasic.go @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2014 Kurt Jung (Gmail: kurt.w.jung) + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package gofpdf + +import ( + "encoding/xml" + "fmt" + "io/ioutil" + "strconv" + "strings" +) + +var pathCmdSub *strings.Replacer + +func init() { + // Handle permitted constructions like "100L200,230" + pathCmdSub = strings.NewReplacer(",", " ", + "L", " L ", "l", " l ", + "C", " C ", "c", " c ", + "M", " M ", "m", " m ") +} + +// SVGBasicSegmentType describes a single curve or position segment +type SVGBasicSegmentType struct { + Cmd byte // See http://www.w3.org/TR/SVG/paths.html for path command structure + Arg [6]float64 +} + +func absolutizePath(segs []SVGBasicSegmentType) { + var x, y float64 + var segPtr *SVGBasicSegmentType + adjust := func(pos int, adjX, adjY float64) { + segPtr.Arg[pos] += adjX + segPtr.Arg[pos+1] += adjY + } + for j, seg := range segs { + segPtr = &segs[j] + if j == 0 && seg.Cmd == 'm' { + segPtr.Cmd = 'M' + } + switch segPtr.Cmd { + case 'M': + x = seg.Arg[0] + y = seg.Arg[1] + case 'm': + adjust(0, x, y) + segPtr.Cmd = 'M' + x = segPtr.Arg[0] + y = segPtr.Arg[1] + case 'L': + x = seg.Arg[0] + y = seg.Arg[1] + case 'l': + adjust(0, x, y) + segPtr.Cmd = 'L' + x = segPtr.Arg[0] + y = segPtr.Arg[1] + case 'C': + x = seg.Arg[4] + y = seg.Arg[5] + case 'c': + adjust(0, x, y) + adjust(2, x, y) + adjust(4, x, y) + segPtr.Cmd = 'C' + x = segPtr.Arg[4] + y = segPtr.Arg[5] + } + } +} + +func pathParse(pathStr string) (segs []SVGBasicSegmentType, err error) { + var seg SVGBasicSegmentType + var j, argJ, argCount, prevArgCount int + setup := func(n int) { + // It is not strictly necessary to clear arguments, but result may be clearer + // to caller + for j := 0; j < len(seg.Arg); j++ { + seg.Arg[j] = 0.0 + } + argJ = 0 + argCount = n + prevArgCount = n + } + var str string + var c byte + pathStr = pathCmdSub.Replace(pathStr) + strList := strings.Fields(pathStr) + count := len(strList) + for j = 0; j < count && err == nil; j++ { + str = strList[j] + if argCount == 0 { // Look for path command or argument continuation + c = str[0] + if c == '-' || (c >= '0' && c <= '9') { // More arguments + if j > 0 { + setup(prevArgCount) + // Repeat previous action + if seg.Cmd == 'M' { + seg.Cmd = 'L' + } else if seg.Cmd == 'm' { + seg.Cmd = 'l' + } + } else { + err = fmt.Errorf("expecting SVG path command at first position, got %s", str) + } + } + } + if err == nil { + if argCount == 0 { + seg.Cmd = str[0] + switch seg.Cmd { + case 'M', 'm': // Absolute/relative moveto: x, y + setup(2) + case 'C', 'c': // Absolute/relative Bézier curve: cx0, cy0, cx1, cy1, x1, y1 + setup(6) + case 'L', 'l': // Absolute/relative lineto: x, y + setup(2) + case 'Z', 'z': // closepath instruction (takes no arguments) + break + default: + err = fmt.Errorf("expecting SVG path command at position %d, got %s", j, str) + } + } else { + seg.Arg[argJ], err = strconv.ParseFloat(str, 64) + if err == nil { + argJ++ + argCount-- + if argCount == 0 { + segs = append(segs, seg) + } + } + } + } + } + if err == nil { + if argCount == 0 { + absolutizePath(segs) + } else { + err = fmt.Errorf("expecting additional (%d) numeric arguments", argCount) + } + } + return +} + +// SVGBasicType aggregates the information needed to describe a multi-segment +// basic vector image +type SVGBasicType struct { + Wd, Ht float64 + Segments [][]SVGBasicSegmentType +} + +// SVGBasicParse parses a simple scalable vector graphics (SVG) buffer into a +// descriptor. Only a small subset of the SVG standard, in particular the path +// information generated by jSignature, is supported. The returned path data +// includes only the commands 'M' (absolute moveto: x, y), 'L' (absolute +// lineto: x, y), 'C' (absolute cubic Bézier curve: cx0, cy0, cx1, cy1, +// x1,y1) and 'Z' (closepath). +func SVGBasicParse(buf []byte) (sig SVGBasicType, err error) { + type pathType struct { + D string `xml:"d,attr"` + } + type srcType struct { + Wd float64 `xml:"width,attr"` + Ht float64 `xml:"height,attr"` + Paths []pathType `xml:"path"` + } + var src srcType + err = xml.Unmarshal(buf, &src) + if err == nil { + if src.Wd > 0 && src.Ht > 0 { + sig.Wd, sig.Ht = src.Wd, src.Ht + var segs []SVGBasicSegmentType + for _, path := range src.Paths { + if err == nil { + segs, err = pathParse(path.D) + if err == nil { + sig.Segments = append(sig.Segments, segs) + } + } + } + } else { + err = fmt.Errorf("unacceptable values for basic SVG extent: %.2f x %.2f", + sig.Wd, sig.Ht) + } + } + return +} + +// SVGBasicFileParse parses a simple scalable vector graphics (SVG) file into a +// basic descriptor. The SVGBasicWrite() example demonstrates this method. +func SVGBasicFileParse(svgFileStr string) (sig SVGBasicType, err error) { + var buf []byte + buf, err = ioutil.ReadFile(svgFileStr) + if err == nil { + sig, err = SVGBasicParse(buf) + } + return +} diff --git a/vendor/github.com/jung-kurt/gofpdf/svgwrite.go b/vendor/github.com/jung-kurt/gofpdf/svgwrite.go new file mode 100644 index 00000000..7e455fb5 --- /dev/null +++ b/vendor/github.com/jung-kurt/gofpdf/svgwrite.go @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2014 Kurt Jung (Gmail: kurt.w.jung) + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package gofpdf + +// SVGBasicWrite renders the paths encoded in the basic SVG image specified by +// sb. The scale value is used to convert the coordinates in the path to the +// unit of measure specified in New(). The current position (as set with a call +// to SetXY()) is used as the origin of the image. The current line cap style +// (as set with SetLineCapStyle()), line width (as set with SetLineWidth()), +// and draw color (as set with SetDrawColor()) are used in drawing the image +// paths. +func (f *Fpdf) SVGBasicWrite(sb *SVGBasicType, scale float64) { + originX, originY := f.GetXY() + var x, y, newX, newY float64 + var cx0, cy0, cx1, cy1 float64 + var path []SVGBasicSegmentType + var seg SVGBasicSegmentType + val := func(arg int) (float64, float64) { + return originX + scale*seg.Arg[arg], originY + scale*seg.Arg[arg+1] + } + for j := 0; j < len(sb.Segments) && f.Ok(); j++ { + path = sb.Segments[j] + for k := 0; k < len(path) && f.Ok(); k++ { + seg = path[k] + switch seg.Cmd { + case 'M': + x, y = val(0) + f.SetXY(x, y) + case 'L': + newX, newY = val(0) + f.Line(x, y, newX, newY) + x, y = newX, newY + case 'C': + cx0, cy0 = val(0) + cx1, cy1 = val(2) + newX, newY = val(4) + f.CurveCubic(x, y, cx0, cy0, newX, newY, cx1, cy1, "D") + x, y = newX, newY + case 'Z': + f.Line(x, y, originX, originY) + default: + f.SetErrorf("Unexpected path command '%c'", seg.Cmd) + } + } + } +} diff --git a/vendor/github.com/jung-kurt/gofpdf/template.go b/vendor/github.com/jung-kurt/gofpdf/template.go new file mode 100644 index 00000000..31204c2f --- /dev/null +++ b/vendor/github.com/jung-kurt/gofpdf/template.go @@ -0,0 +1,273 @@ +package gofpdf + +/* + * Copyright (c) 2015 Kurt Jung (Gmail: kurt.w.jung), + * Marcus Downing, Jan Slabon (Setasign) + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +import ( + "encoding/gob" + "sort" +) + +// CreateTemplate defines a new template using the current page size. +func (f *Fpdf) CreateTemplate(fn func(*Tpl)) Template { + return newTpl(PointType{0, 0}, f.curPageSize, f.defOrientation, f.unitStr, f.fontDirStr, fn, f) +} + +// CreateTemplateCustom starts a template, using the given bounds. +func (f *Fpdf) CreateTemplateCustom(corner PointType, size SizeType, fn func(*Tpl)) Template { + return newTpl(corner, size, f.defOrientation, f.unitStr, f.fontDirStr, fn, f) +} + +// CreateTemplate creates a template that is not attached to any document. +// +// This function is deprecated; it incorrectly assumes that a page with a width +// smaller than its height is oriented in portrait mode, otherwise it assumes +// landscape mode. This causes problems when placing the template in a master +// document where this condition does not apply. CreateTpl() is a similar +// function that lets you specify the orientation to avoid this problem. +func CreateTemplate(corner PointType, size SizeType, unitStr, fontDirStr string, fn func(*Tpl)) Template { + orientationStr := "p" + if size.Wd > size.Ht { + orientationStr = "l" + } + + return CreateTpl(corner, size, orientationStr, unitStr, fontDirStr, fn) +} + +// CreateTpl creates a template not attached to any document +func CreateTpl(corner PointType, size SizeType, orientationStr, unitStr, fontDirStr string, fn func(*Tpl)) Template { + return newTpl(corner, size, orientationStr, unitStr, fontDirStr, fn, nil) +} + +// UseTemplate adds a template to the current page or another template, +// using the size and position at which it was originally written. +func (f *Fpdf) UseTemplate(t Template) { + if t == nil { + f.SetErrorf("template is nil") + return + } + corner, size := t.Size() + f.UseTemplateScaled(t, corner, size) +} + +// UseTemplateScaled adds a template to the current page or another template, +// using the given page coordinates. +func (f *Fpdf) UseTemplateScaled(t Template, corner PointType, size SizeType) { + if t == nil { + f.SetErrorf("template is nil") + return + } + + // You have to add at least a page first + if f.page <= 0 { + f.SetErrorf("cannot use a template without first adding a page") + return + } + + // make a note of the fact that we actually use this template, as well as any other templates, + // images or fonts it uses + f.templates[t.ID()] = t + for _, tt := range t.Templates() { + f.templates[tt.ID()] = tt + } + for name, ti := range t.Images() { + name = sprintf("t%s-%s", t.ID(), name) + f.images[name] = ti + } + + // template data + _, templateSize := t.Size() + scaleX := size.Wd / templateSize.Wd + scaleY := size.Ht / templateSize.Ht + tx := corner.X * f.k + ty := (f.curPageSize.Ht - corner.Y - size.Ht) * f.k + + f.outf("q %.4f 0 0 %.4f %.4f %.4f cm", scaleX, scaleY, tx, ty) // Translate + f.outf("/TPL%s Do Q", t.ID()) +} + +// Template is an object that can be written to, then used and re-used any number of times within a document. +type Template interface { + ID() string + Size() (PointType, SizeType) + Bytes() []byte + Images() map[string]*ImageInfoType + Templates() []Template + NumPages() int + FromPage(int) (Template, error) + FromPages() []Template + Serialize() ([]byte, error) + gob.GobDecoder + gob.GobEncoder +} + +func (f *Fpdf) templateFontCatalog() { + var keyList []string + var font fontDefType + var key string + f.out("/Font <<") + for key = range f.fonts { + keyList = append(keyList, key) + } + if f.catalogSort { + sort.Strings(keyList) + } + for _, key = range keyList { + font = f.fonts[key] + f.outf("/F%s %d 0 R", font.i, font.N) + } + f.out(">>") +} + +// putTemplates writes the templates to the PDF +func (f *Fpdf) putTemplates() { + filter := "" + if f.compress { + filter = "/Filter /FlateDecode " + } + + templates := sortTemplates(f.templates, f.catalogSort) + var t Template + for _, t = range templates { + corner, size := t.Size() + + f.newobj() + f.templateObjects[t.ID()] = f.n + f.outf("<<%s/Type /XObject", filter) + f.out("/Subtype /Form") + f.out("/Formtype 1") + f.outf("/BBox [%.2f %.2f %.2f %.2f]", corner.X*f.k, corner.Y*f.k, (corner.X+size.Wd)*f.k, (corner.Y+size.Ht)*f.k) + if corner.X != 0 || corner.Y != 0 { + f.outf("/Matrix [1 0 0 1 %.5f %.5f]", -corner.X*f.k*2, corner.Y*f.k*2) + } + + // Template's resource dictionary + f.out("/Resources ") + f.out("< 0 || len(tTemplates) > 0 { + f.out("/XObject <<") + { + var key string + var keyList []string + var ti *ImageInfoType + for key = range tImages { + keyList = append(keyList, key) + } + if gl.catalogSort { + sort.Strings(keyList) + } + for _, key = range keyList { + // for _, ti := range tImages { + ti = tImages[key] + f.outf("/I%s %d 0 R", ti.i, ti.n) + } + } + for _, tt := range tTemplates { + id := tt.ID() + if objID, ok := f.templateObjects[id]; ok { + f.outf("/TPL%s %d 0 R", id, objID) + } + } + f.out(">>") + } + + f.out(">>") + + // Write the template's byte stream + buffer := t.Bytes() + // fmt.Println("Put template bytes", string(buffer[:])) + if f.compress { + buffer = sliceCompress(buffer) + } + f.outf("/Length %d >>", len(buffer)) + f.putstream(buffer) + f.out("endobj") + } +} + +func templateKeyList(mp map[string]Template, sort bool) (keyList []string) { + var key string + for key = range mp { + keyList = append(keyList, key) + } + if sort { + gensort(len(keyList), + func(a, b int) bool { + return keyList[a] < keyList[b] + }, + func(a, b int) { + keyList[a], keyList[b] = keyList[b], keyList[a] + }) + } + return +} + +// sortTemplates puts templates in a suitable order based on dependices +func sortTemplates(templates map[string]Template, catalogSort bool) []Template { + chain := make([]Template, 0, len(templates)*2) + + // build a full set of dependency chains + var keyList []string + var key string + var t Template + keyList = templateKeyList(templates, catalogSort) + for _, key = range keyList { + t = templates[key] + tlist := templateChainDependencies(t) + for _, tt := range tlist { + if tt != nil { + chain = append(chain, tt) + } + } + } + + // reduce that to make a simple list + sorted := make([]Template, 0, len(templates)) +chain: + for _, t := range chain { + for _, already := range sorted { + if t == already { + continue chain + } + } + sorted = append(sorted, t) + } + + return sorted +} + +// templateChainDependencies is a recursive function for determining the full chain of template dependencies +func templateChainDependencies(template Template) []Template { + requires := template.Templates() + chain := make([]Template, len(requires)*2) + for _, req := range requires { + chain = append(chain, templateChainDependencies(req)...) + } + chain = append(chain, template) + return chain +} + +// < 0002640 31 20 31 32 20 30 20 52 0a 2f 54 50 4c 32 20 31 |1 12 0 R./TPL2 1| +// < 0002650 35 20 30 20 52 0a 2f 54 50 4c 31 20 31 34 20 30 |5 0 R./TPL1 14 0| + +// > 0002640 31 20 31 32 20 30 20 52 0a 2f 54 50 4c 31 20 31 |1 12 0 R./TPL1 1| +// > 0002650 34 20 30 20 52 0a 2f 54 50 4c 32 20 31 35 20 30 |4 0 R./TPL2 15 0| diff --git a/vendor/github.com/jung-kurt/gofpdf/template_impl.go b/vendor/github.com/jung-kurt/gofpdf/template_impl.go new file mode 100644 index 00000000..c5d7be89 --- /dev/null +++ b/vendor/github.com/jung-kurt/gofpdf/template_impl.go @@ -0,0 +1,299 @@ +package gofpdf + +import ( + "bytes" + "crypto/sha1" + "encoding/gob" + "errors" + "fmt" +) + +/* + * Copyright (c) 2015 Kurt Jung (Gmail: kurt.w.jung), + * Marcus Downing, Jan Slabon (Setasign) + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +// newTpl creates a template, copying graphics settings from a template if one is given +func newTpl(corner PointType, size SizeType, orientationStr, unitStr, fontDirStr string, fn func(*Tpl), copyFrom *Fpdf) Template { + sizeStr := "" + + fpdf := fpdfNew(orientationStr, unitStr, sizeStr, fontDirStr, size) + tpl := Tpl{*fpdf} + if copyFrom != nil { + tpl.loadParamsFromFpdf(copyFrom) + } + tpl.Fpdf.AddPage() + fn(&tpl) + + bytes := make([][]byte, len(tpl.Fpdf.pages)) + // skip the first page as it will always be empty + for x := 1; x < len(bytes); x++ { + bytes[x] = tpl.Fpdf.pages[x].Bytes() + } + + templates := make([]Template, 0, len(tpl.Fpdf.templates)) + for _, key := range templateKeyList(tpl.Fpdf.templates, true) { + templates = append(templates, tpl.Fpdf.templates[key]) + } + images := tpl.Fpdf.images + + template := FpdfTpl{corner, size, bytes, images, templates, tpl.Fpdf.page} + return &template +} + +// FpdfTpl is a concrete implementation of the Template interface. +type FpdfTpl struct { + corner PointType + size SizeType + bytes [][]byte + images map[string]*ImageInfoType + templates []Template + page int +} + +// ID returns the global template identifier +func (t *FpdfTpl) ID() string { + return fmt.Sprintf("%x", sha1.Sum(t.Bytes())) +} + +// Size gives the bounding dimensions of this template +func (t *FpdfTpl) Size() (corner PointType, size SizeType) { + return t.corner, t.size +} + +// Bytes returns the actual template data, not including resources +func (t *FpdfTpl) Bytes() []byte { + return t.bytes[t.page] +} + +// FromPage creates a new template from a specific Page +func (t *FpdfTpl) FromPage(page int) (Template, error) { + // pages start at 1 + if page == 0 { + return nil, errors.New("Pages start at 1 No template will have a page 0") + } + + if page > t.NumPages() { + return nil, fmt.Errorf("The template does not have a page %d", page) + } + // if it is already pointing to the correct page + // there is no need to create a new template + if t.page == page { + return t, nil + } + + t2 := *t + t2.page = page + return &t2, nil +} + +// FromPages creates a template slice with all the pages within a template. +func (t *FpdfTpl) FromPages() []Template { + p := make([]Template, t.NumPages()) + for x := 1; x <= t.NumPages(); x++ { + // the only error is when accessing a + // non existing template... that can't happen + // here + p[x-1], _ = t.FromPage(x) + } + + return p +} + +// Images returns a list of the images used in this template +func (t *FpdfTpl) Images() map[string]*ImageInfoType { + return t.images +} + +// Templates returns a list of templates used in this template +func (t *FpdfTpl) Templates() []Template { + return t.templates +} + +// NumPages returns the number of available pages within the template. Look at FromPage and FromPages on access to that content. +func (t *FpdfTpl) NumPages() int { + // the first page is empty to + // make the pages begin at one + return len(t.bytes) - 1 +} + +// Serialize turns a template into a byte string for later deserialization +func (t *FpdfTpl) Serialize() ([]byte, error) { + b := new(bytes.Buffer) + enc := gob.NewEncoder(b) + err := enc.Encode(t) + + return b.Bytes(), err +} + +// DeserializeTemplate creaties a template from a previously serialized +// template +func DeserializeTemplate(b []byte) (Template, error) { + tpl := new(FpdfTpl) + dec := gob.NewDecoder(bytes.NewBuffer(b)) + err := dec.Decode(tpl) + return tpl, err +} + +// childrenImages returns the next layer of children images, it doesn't dig into +// children of children. Applies template namespace to keys to ensure +// no collisions. See UseTemplateScaled +func (t *FpdfTpl) childrenImages() map[string]*ImageInfoType { + childrenImgs := make(map[string]*ImageInfoType) + + for x := 0; x < len(t.templates); x++ { + imgs := t.templates[x].Images() + for key, val := range imgs { + name := sprintf("t%s-%s", t.templates[x].ID(), key) + childrenImgs[name] = val + } + } + + return childrenImgs +} + +// childrensTemplates returns the next layer of children templates, it doesn't dig into +// children of children. +func (t *FpdfTpl) childrensTemplates() []Template { + childrenTmpls := make([]Template, 0) + + for x := 0; x < len(t.templates); x++ { + tmpls := t.templates[x].Templates() + childrenTmpls = append(childrenTmpls, tmpls...) + } + + return childrenTmpls +} + +// GobEncode encodes the receiving template into a byte buffer. Use GobDecode +// to decode the byte buffer back to a template. +func (t *FpdfTpl) GobEncode() ([]byte, error) { + w := new(bytes.Buffer) + encoder := gob.NewEncoder(w) + + childrensTemplates := t.childrensTemplates() + firstClassTemplates := make([]Template, 0) + +found_continue: + for x := 0; x < len(t.templates); x++ { + for y := 0; y < len(childrensTemplates); y++ { + if childrensTemplates[y].ID() == t.templates[x].ID() { + continue found_continue + } + } + + firstClassTemplates = append(firstClassTemplates, t.templates[x]) + } + err := encoder.Encode(firstClassTemplates) + + childrenImgs := t.childrenImages() + firstClassImgs := make(map[string]*ImageInfoType) + + for key, img := range t.images { + if _, ok := childrenImgs[key]; !ok { + firstClassImgs[key] = img + } + } + + if err == nil { + err = encoder.Encode(firstClassImgs) + } + if err == nil { + err = encoder.Encode(t.corner) + } + if err == nil { + err = encoder.Encode(t.size) + } + if err == nil { + err = encoder.Encode(t.bytes) + } + if err == nil { + err = encoder.Encode(t.page) + } + + return w.Bytes(), err +} + +// GobDecode decodes the specified byte buffer into the receiving template. +func (t *FpdfTpl) GobDecode(buf []byte) error { + r := bytes.NewBuffer(buf) + decoder := gob.NewDecoder(r) + + firstClassTemplates := make([]*FpdfTpl, 0) + err := decoder.Decode(&firstClassTemplates) + t.templates = make([]Template, len(firstClassTemplates)) + + for x := 0; x < len(t.templates); x++ { + t.templates[x] = Template(firstClassTemplates[x]) + } + + firstClassImages := t.childrenImages() + + t.templates = append(t.childrensTemplates(), t.templates...) + + t.images = make(map[string]*ImageInfoType) + if err == nil { + err = decoder.Decode(&t.images) + } + + for k, v := range firstClassImages { + t.images[k] = v + } + + if err == nil { + err = decoder.Decode(&t.corner) + } + if err == nil { + err = decoder.Decode(&t.size) + } + if err == nil { + err = decoder.Decode(&t.bytes) + } + if err == nil { + err = decoder.Decode(&t.page) + } + + return err +} + +// Tpl is an Fpdf used for writing a template. It has most of the facilities of +// an Fpdf, but cannot add more pages. Tpl is used directly only during the +// limited time a template is writable. +type Tpl struct { + Fpdf +} + +func (t *Tpl) loadParamsFromFpdf(f *Fpdf) { + t.Fpdf.compress = false + + t.Fpdf.k = f.k + t.Fpdf.x = f.x + t.Fpdf.y = f.y + t.Fpdf.lineWidth = f.lineWidth + t.Fpdf.capStyle = f.capStyle + t.Fpdf.joinStyle = f.joinStyle + + t.Fpdf.color.draw = f.color.draw + t.Fpdf.color.fill = f.color.fill + t.Fpdf.color.text = f.color.text + + t.Fpdf.fonts = f.fonts + t.Fpdf.currentFont = f.currentFont + t.Fpdf.fontFamily = f.fontFamily + t.Fpdf.fontSize = f.fontSize + t.Fpdf.fontSizePt = f.fontSizePt + t.Fpdf.fontStyle = f.fontStyle + t.Fpdf.ws = f.ws +} diff --git a/vendor/github.com/jung-kurt/gofpdf/ttfparser.go b/vendor/github.com/jung-kurt/gofpdf/ttfparser.go new file mode 100644 index 00000000..669ab4d8 --- /dev/null +++ b/vendor/github.com/jung-kurt/gofpdf/ttfparser.go @@ -0,0 +1,374 @@ +/* + * Copyright (c) 2013 Kurt Jung (Gmail: kurt.w.jung) + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package gofpdf + +// Utility to parse TTF font files +// Version: 1.0 +// Date: 2011-06-18 +// Author: Olivier PLATHEY +// Port to Go: Kurt Jung, 2013-07-15 + +import ( + "encoding/binary" + "fmt" + "os" + "regexp" + "strings" +) + +// TtfType contains metrics of a TrueType font. +type TtfType struct { + Embeddable bool + UnitsPerEm uint16 + PostScriptName string + Bold bool + ItalicAngle int16 + IsFixedPitch bool + TypoAscender int16 + TypoDescender int16 + UnderlinePosition int16 + UnderlineThickness int16 + Xmin, Ymin, Xmax, Ymax int16 + CapHeight int16 + Widths []uint16 + Chars map[uint16]uint16 +} + +type ttfParser struct { + rec TtfType + f *os.File + tables map[string]uint32 + numberOfHMetrics uint16 + numGlyphs uint16 +} + +// TtfParse extracts various metrics from a TrueType font file. +func TtfParse(fileStr string) (TtfRec TtfType, err error) { + var t ttfParser + t.f, err = os.Open(fileStr) + if err != nil { + return + } + version, err := t.ReadStr(4) + if err != nil { + return + } + if version == "OTTO" { + err = fmt.Errorf("fonts based on PostScript outlines are not supported") + return + } + if version != "\x00\x01\x00\x00" { + err = fmt.Errorf("unrecognized file format") + return + } + numTables := int(t.ReadUShort()) + t.Skip(3 * 2) // searchRange, entrySelector, rangeShift + t.tables = make(map[string]uint32) + var tag string + for j := 0; j < numTables; j++ { + tag, err = t.ReadStr(4) + if err != nil { + return + } + t.Skip(4) // checkSum + offset := t.ReadULong() + t.Skip(4) // length + t.tables[tag] = offset + } + err = t.ParseComponents() + if err != nil { + return + } + t.f.Close() + TtfRec = t.rec + return +} + +func (t *ttfParser) ParseComponents() (err error) { + err = t.ParseHead() + if err == nil { + err = t.ParseHhea() + if err == nil { + err = t.ParseMaxp() + if err == nil { + err = t.ParseHmtx() + if err == nil { + err = t.ParseCmap() + if err == nil { + err = t.ParseName() + if err == nil { + err = t.ParseOS2() + if err == nil { + err = t.ParsePost() + } + } + } + } + } + } + } + return +} + +func (t *ttfParser) ParseHead() (err error) { + err = t.Seek("head") + t.Skip(3 * 4) // version, fontRevision, checkSumAdjustment + magicNumber := t.ReadULong() + if magicNumber != 0x5F0F3CF5 { + err = fmt.Errorf("incorrect magic number") + return + } + t.Skip(2) // flags + t.rec.UnitsPerEm = t.ReadUShort() + t.Skip(2 * 8) // created, modified + t.rec.Xmin = t.ReadShort() + t.rec.Ymin = t.ReadShort() + t.rec.Xmax = t.ReadShort() + t.rec.Ymax = t.ReadShort() + return +} + +func (t *ttfParser) ParseHhea() (err error) { + err = t.Seek("hhea") + if err == nil { + t.Skip(4 + 15*2) + t.numberOfHMetrics = t.ReadUShort() + } + return +} + +func (t *ttfParser) ParseMaxp() (err error) { + err = t.Seek("maxp") + if err == nil { + t.Skip(4) + t.numGlyphs = t.ReadUShort() + } + return +} + +func (t *ttfParser) ParseHmtx() (err error) { + err = t.Seek("hmtx") + if err == nil { + t.rec.Widths = make([]uint16, 0, 8) + for j := uint16(0); j < t.numberOfHMetrics; j++ { + t.rec.Widths = append(t.rec.Widths, t.ReadUShort()) + t.Skip(2) // lsb + } + if t.numberOfHMetrics < t.numGlyphs { + lastWidth := t.rec.Widths[t.numberOfHMetrics-1] + for j := t.numberOfHMetrics; j < t.numGlyphs; j++ { + t.rec.Widths = append(t.rec.Widths, lastWidth) + } + } + } + return +} + +func (t *ttfParser) ParseCmap() (err error) { + var offset int64 + if err = t.Seek("cmap"); err != nil { + return + } + t.Skip(2) // version + numTables := int(t.ReadUShort()) + offset31 := int64(0) + for j := 0; j < numTables; j++ { + platformID := t.ReadUShort() + encodingID := t.ReadUShort() + offset = int64(t.ReadULong()) + if platformID == 3 && encodingID == 1 { + offset31 = offset + } + } + if offset31 == 0 { + err = fmt.Errorf("no Unicode encoding found") + return + } + startCount := make([]uint16, 0, 8) + endCount := make([]uint16, 0, 8) + idDelta := make([]int16, 0, 8) + idRangeOffset := make([]uint16, 0, 8) + t.rec.Chars = make(map[uint16]uint16) + t.f.Seek(int64(t.tables["cmap"])+offset31, os.SEEK_SET) + format := t.ReadUShort() + if format != 4 { + err = fmt.Errorf("unexpected subtable format: %d", format) + return + } + t.Skip(2 * 2) // length, language + segCount := int(t.ReadUShort() / 2) + t.Skip(3 * 2) // searchRange, entrySelector, rangeShift + for j := 0; j < segCount; j++ { + endCount = append(endCount, t.ReadUShort()) + } + t.Skip(2) // reservedPad + for j := 0; j < segCount; j++ { + startCount = append(startCount, t.ReadUShort()) + } + for j := 0; j < segCount; j++ { + idDelta = append(idDelta, t.ReadShort()) + } + offset, _ = t.f.Seek(int64(0), os.SEEK_CUR) + for j := 0; j < segCount; j++ { + idRangeOffset = append(idRangeOffset, t.ReadUShort()) + } + for j := 0; j < segCount; j++ { + c1 := startCount[j] + c2 := endCount[j] + d := idDelta[j] + ro := idRangeOffset[j] + if ro > 0 { + t.f.Seek(offset+2*int64(j)+int64(ro), os.SEEK_SET) + } + for c := c1; c <= c2; c++ { + if c == 0xFFFF { + break + } + var gid int32 + if ro > 0 { + gid = int32(t.ReadUShort()) + if gid > 0 { + gid += int32(d) + } + } else { + gid = int32(c) + int32(d) + } + if gid >= 65536 { + gid -= 65536 + } + if gid > 0 { + t.rec.Chars[c] = uint16(gid) + } + } + } + return +} + +func (t *ttfParser) ParseName() (err error) { + err = t.Seek("name") + if err == nil { + tableOffset, _ := t.f.Seek(0, os.SEEK_CUR) + t.rec.PostScriptName = "" + t.Skip(2) // format + count := t.ReadUShort() + stringOffset := t.ReadUShort() + for j := uint16(0); j < count && t.rec.PostScriptName == ""; j++ { + t.Skip(3 * 2) // platformID, encodingID, languageID + nameID := t.ReadUShort() + length := t.ReadUShort() + offset := t.ReadUShort() + if nameID == 6 { + // PostScript name + t.f.Seek(int64(tableOffset)+int64(stringOffset)+int64(offset), os.SEEK_SET) + var s string + s, err = t.ReadStr(int(length)) + if err != nil { + return + } + s = strings.Replace(s, "\x00", "", -1) + var re *regexp.Regexp + if re, err = regexp.Compile("[(){}<> /%[\\]]"); err != nil { + return + } + t.rec.PostScriptName = re.ReplaceAllString(s, "") + } + } + if t.rec.PostScriptName == "" { + err = fmt.Errorf("the name PostScript was not found") + } + } + return +} + +func (t *ttfParser) ParseOS2() (err error) { + err = t.Seek("OS/2") + if err == nil { + version := t.ReadUShort() + t.Skip(3 * 2) // xAvgCharWidth, usWeightClass, usWidthClass + fsType := t.ReadUShort() + t.rec.Embeddable = (fsType != 2) && (fsType&0x200) == 0 + t.Skip(11*2 + 10 + 4*4 + 4) + fsSelection := t.ReadUShort() + t.rec.Bold = (fsSelection & 32) != 0 + t.Skip(2 * 2) // usFirstCharIndex, usLastCharIndex + t.rec.TypoAscender = t.ReadShort() + t.rec.TypoDescender = t.ReadShort() + if version >= 2 { + t.Skip(3*2 + 2*4 + 2) + t.rec.CapHeight = t.ReadShort() + } else { + t.rec.CapHeight = 0 + } + } + return +} + +func (t *ttfParser) ParsePost() (err error) { + err = t.Seek("post") + if err == nil { + t.Skip(4) // version + t.rec.ItalicAngle = t.ReadShort() + t.Skip(2) // Skip decimal part + t.rec.UnderlinePosition = t.ReadShort() + t.rec.UnderlineThickness = t.ReadShort() + t.rec.IsFixedPitch = t.ReadULong() != 0 + } + return +} + +func (t *ttfParser) Seek(tag string) (err error) { + ofs, ok := t.tables[tag] + if ok { + t.f.Seek(int64(ofs), os.SEEK_SET) + } else { + err = fmt.Errorf("table not found: %s", tag) + } + return +} + +func (t *ttfParser) Skip(n int) { + t.f.Seek(int64(n), os.SEEK_CUR) +} + +func (t *ttfParser) ReadStr(length int) (str string, err error) { + var n int + buf := make([]byte, length) + n, err = t.f.Read(buf) + if err == nil { + if n == length { + str = string(buf) + } else { + err = fmt.Errorf("unable to read %d bytes", length) + } + } + return +} + +func (t *ttfParser) ReadUShort() (val uint16) { + binary.Read(t.f, binary.BigEndian, &val) + return +} + +func (t *ttfParser) ReadShort() (val int16) { + binary.Read(t.f, binary.BigEndian, &val) + return +} + +func (t *ttfParser) ReadULong() (val uint32) { + binary.Read(t.f, binary.BigEndian, &val) + return +} diff --git a/vendor/github.com/jung-kurt/gofpdf/utf8fontfile.go b/vendor/github.com/jung-kurt/gofpdf/utf8fontfile.go new file mode 100644 index 00000000..794bae2f --- /dev/null +++ b/vendor/github.com/jung-kurt/gofpdf/utf8fontfile.go @@ -0,0 +1,1140 @@ +/* + * Copyright (c) 2019 Arteom Korotkiy (Gmail: arteomkorotkiy) + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package gofpdf + +import ( + "bytes" + "encoding/binary" + "fmt" + "math" + "sort" +) + +// flags +const symbolWords = 1 << 0 +const symbolScale = 1 << 3 +const symbolContinue = 1 << 5 +const symbolAllScale = 1 << 6 +const symbol2x2 = 1 << 7 + +// CID map Init +const toUnicode = "/CIDInit /ProcSet findresource begin\n12 dict begin\nbegincmap\n/CIDSystemInfo\n<> def\n/CMapName /Adobe-Identity-UCS def\n/CMapType 2 def\n1 begincodespacerange\n<0000> \nendcodespacerange\n1 beginbfrange\n<0000> <0000>\nendbfrange\nendcmap\nCMapName currentdict /CMap defineresource pop\nend\nend" + +type utf8FontFile struct { + fileReader *fileReader + LastRune int + tableDescriptions map[string]*tableDescription + outTablesData map[string][]byte + symbolPosition []int + charSymbolDictionary map[int]int + Ascent int + Descent int + fontElementSize int + Bbox fontBoxType + CapHeight int + StemV int + ItalicAngle int + Flags int + UnderlinePosition float64 + UnderlineThickness float64 + CharWidths []int + DefaultWidth float64 + symbolData map[int]map[string][]int + CodeSymbolDictionary map[int]int +} + +type tableDescription struct { + name string + checksum []int + position int + size int +} + +type fileReader struct { + readerPosition int64 + array []byte +} + +func (fr *fileReader) Read(s int) []byte { + b := fr.array[fr.readerPosition : fr.readerPosition+int64(s)] + fr.readerPosition += int64(s) + return b +} + +func (fr *fileReader) seek(shift int64, flag int) (int64, error) { + if flag == 0 { + fr.readerPosition = shift + } else if flag == 1 { + fr.readerPosition += shift + } else if flag == 2 { + fr.readerPosition = int64(len(fr.array)) - shift + } + return int64(fr.readerPosition), nil +} + +func newUTF8Font(reader *fileReader) *utf8FontFile { + utf := utf8FontFile{ + fileReader: reader, + } + return &utf +} + +func (utf *utf8FontFile) parseFile() error { + utf.fileReader.readerPosition = 0 + utf.symbolPosition = make([]int, 0) + utf.charSymbolDictionary = make(map[int]int) + utf.tableDescriptions = make(map[string]*tableDescription) + utf.outTablesData = make(map[string][]byte) + utf.Ascent = 0 + utf.Descent = 0 + codeType := uint32(utf.readUint32()) + if codeType == 0x4F54544F { + return fmt.Errorf("not supported\n ") + } + if codeType == 0x74746366 { + return fmt.Errorf("not supported\n ") + } + if codeType != 0x00010000 && codeType != 0x74727565 { + return fmt.Errorf("Not a TrueType font: codeType=%v\n ", codeType) + } + utf.generateTableDescriptions() + utf.parseTables() + return nil +} + +func (utf *utf8FontFile) generateTableDescriptions() { + + tablesCount := utf.readUint16() + _ = utf.readUint16() + _ = utf.readUint16() + _ = utf.readUint16() + utf.tableDescriptions = make(map[string]*tableDescription) + + for i := 0; i < tablesCount; i++ { + record := tableDescription{ + name: utf.readTableName(), + checksum: []int{utf.readUint16(), utf.readUint16()}, + position: utf.readUint32(), + size: utf.readUint32(), + } + utf.tableDescriptions[record.name] = &record + } +} + +func (utf *utf8FontFile) readTableName() string { + return string(utf.fileReader.Read(4)) +} + +func (utf *utf8FontFile) readUint16() int { + s := utf.fileReader.Read(2) + return (int(s[0]) << 8) + int(s[1]) +} + +func (utf *utf8FontFile) readUint32() int { + s := utf.fileReader.Read(4) + return (int(s[0]) * 16777216) + (int(s[1]) << 16) + (int(s[2]) << 8) + int(s[3]) // 16777216 = 1<<24 +} + +func (utf *utf8FontFile) calcInt32(x, y []int) []int { + answer := make([]int, 2) + if y[1] > x[1] { + x[1] += 1 << 16 + x[0]++ + } + answer[1] = x[1] - y[1] + if y[0] > x[0] { + x[0] += 1 << 16 + } + answer[0] = x[0] - y[0] + answer[0] = answer[0] & 0xFFFF + return answer +} + +func (utf *utf8FontFile) generateChecksum(data []byte) []int { + if (len(data) % 4) != 0 { + for i := 0; (len(data) % 4) != 0; i++ { + data = append(data, 0) + } + } + answer := []int{0x0000, 0x0000} + for i := 0; i < len(data); i += 4 { + answer[0] += (int(data[i]) << 8) + int(data[i+1]) + answer[1] += (int(data[i+2]) << 8) + int(data[i+3]) + answer[0] += answer[1] >> 16 + answer[1] = answer[1] & 0xFFFF + answer[0] = answer[0] & 0xFFFF + } + return answer +} + +func (utf *utf8FontFile) seek(shift int) { + _, _ = utf.fileReader.seek(int64(shift), 0) +} + +func (utf *utf8FontFile) skip(delta int) { + _, _ = utf.fileReader.seek(int64(delta), 1) +} + +//SeekTable position +func (utf *utf8FontFile) SeekTable(name string) int { + return utf.seekTable(name, 0) +} + +func (utf *utf8FontFile) seekTable(name string, offsetInTable int) int { + _, _ = utf.fileReader.seek(int64(utf.tableDescriptions[name].position+offsetInTable), 0) + return int(utf.fileReader.readerPosition) +} + +func (utf *utf8FontFile) readInt16() int16 { + s := utf.fileReader.Read(2) + a := (int16(s[0]) << 8) + int16(s[1]) + if (int(a) & (1 << 15)) == 0 { + a = int16(int(a) - (1 << 16)) + } + return a +} + +func (utf *utf8FontFile) getUint16(pos int) int { + _, _ = utf.fileReader.seek(int64(pos), 0) + s := utf.fileReader.Read(2) + return (int(s[0]) << 8) + int(s[1]) +} + +func (utf *utf8FontFile) splice(stream []byte, offset int, value []byte) []byte { + return append(append(stream[:offset], value...), stream[offset+len(value):]...) +} + +func (utf *utf8FontFile) insertUint16(stream []byte, offset int, value int) []byte { + up := make([]byte, 2) + binary.BigEndian.PutUint16(up, uint16(value)) + return utf.splice(stream, offset, up) +} + +func (utf *utf8FontFile) getRange(pos, length int) []byte { + utf.fileReader.seek(int64(pos), 0) + if length < 1 { + return make([]byte, 0) + } + s := utf.fileReader.Read(length) + return s +} + +func (utf *utf8FontFile) getTableData(name string) []byte { + desckrip := utf.tableDescriptions[name] + if desckrip == nil { + return nil + } + if desckrip.size == 0 { + return nil + } + utf.fileReader.seek(int64(desckrip.position), 0) + s := utf.fileReader.Read(desckrip.size) + return s +} + +func (utf *utf8FontFile) setOutTable(name string, data []byte) { + if data == nil { + return + } + if name == "head" { + data = utf.splice(data, 8, []byte{0, 0, 0, 0}) + } + utf.outTablesData[name] = data +} + +func arrayKeys(arr map[int]string) []int { + answer := make([]int, len(arr)) + i := 0 + for key := range arr { + answer[i] = key + i++ + } + return answer +} + +func inArray(s int, arr []int) bool { + for _, i := range arr { + if s == i { + return true + } + } + return false +} + +func (utf *utf8FontFile) parseNAMETable() int { + namePosition := utf.SeekTable("name") + format := utf.readUint16() + if format != 0 { + fmt.Printf("Illegal format %d\n", format) + return format + } + nameCount := utf.readUint16() + stringDataPosition := namePosition + utf.readUint16() + names := map[int]string{1: "", 2: "", 3: "", 4: "", 6: ""} + keys := arrayKeys(names) + counter := len(names) + for i := 0; i < nameCount; i++ { + system := utf.readUint16() + code := utf.readUint16() + local := utf.readUint16() + nameID := utf.readUint16() + size := utf.readUint16() + position := utf.readUint16() + if !inArray(nameID, keys) { + continue + } + currentName := "" + if system == 3 && code == 1 && local == 0x409 { + oldPos := utf.fileReader.readerPosition + utf.seek(stringDataPosition + position) + if size%2 != 0 { + fmt.Printf("name is not binar byte format\n") + return format + } + size /= 2 + currentName = "" + for size > 0 { + char := utf.readUint16() + currentName += string(rune(char)) + size-- + } + utf.fileReader.readerPosition = oldPos + utf.seek(int(oldPos)) + } else if system == 1 && code == 0 && local == 0 { + oldPos := utf.fileReader.readerPosition + currentName = string(utf.getRange(stringDataPosition+position, size)) + utf.fileReader.readerPosition = oldPos + utf.seek(int(oldPos)) + } + if currentName != "" && names[nameID] == "" { + names[nameID] = currentName + counter-- + if counter == 0 { + break + } + } + } + return format +} + +func (utf *utf8FontFile) parseHEADTable() { + utf.SeekTable("head") + utf.skip(18) + utf.fontElementSize = utf.readUint16() + scale := 1000.0 / float64(utf.fontElementSize) + utf.skip(16) + xMin := utf.readInt16() + yMin := utf.readInt16() + xMax := utf.readInt16() + yMax := utf.readInt16() + utf.Bbox = fontBoxType{int(float64(xMin) * scale), int(float64(yMin) * scale), int(float64(xMax) * scale), int(float64(yMax) * scale)} + utf.skip(3 * 2) + _ = utf.readUint16() + symbolDataFormat := utf.readUint16() + if symbolDataFormat != 0 { + fmt.Printf("Unknown symbol data format %d\n", symbolDataFormat) + return + } +} + +func (utf *utf8FontFile) parseHHEATable() int { + metricsCount := 0 + if _, OK := utf.tableDescriptions["hhea"]; OK { + scale := 1000.0 / float64(utf.fontElementSize) + utf.SeekTable("hhea") + utf.skip(4) + hheaAscender := utf.readInt16() + hheaDescender := utf.readInt16() + utf.Ascent = int(float64(hheaAscender) * scale) + utf.Descent = int(float64(hheaDescender) * scale) + utf.skip(24) + metricDataFormat := utf.readUint16() + if metricDataFormat != 0 { + fmt.Printf("Unknown horizontal metric data format %d\n", metricDataFormat) + return 0 + } + metricsCount = utf.readUint16() + if metricsCount == 0 { + fmt.Printf("Number of horizontal metrics is 0\n") + return 0 + } + } + return metricsCount +} + +func (utf *utf8FontFile) parseOS2Table() int { + var weightType int + scale := 1000.0 / float64(utf.fontElementSize) + if _, OK := utf.tableDescriptions["OS/2"]; OK { + utf.SeekTable("OS/2") + version := utf.readUint16() + utf.skip(2) + weightType = utf.readUint16() + utf.skip(2) + fsType := utf.readUint16() + if fsType == 0x0002 || (fsType&0x0300) != 0 { + fmt.Printf("ERROR - copyright restrictions.\n") + return 0 + } + utf.skip(20) + _ = utf.readInt16() + + utf.skip(36) + sTypoAscender := utf.readInt16() + sTypoDescender := utf.readInt16() + if utf.Ascent == 0 { + utf.Ascent = int(float64(sTypoAscender) * scale) + } + if utf.Descent == 0 { + utf.Descent = int(float64(sTypoDescender) * scale) + } + if version > 1 { + utf.skip(16) + sCapHeight := utf.readInt16() + utf.CapHeight = int(float64(sCapHeight) * scale) + } else { + utf.CapHeight = utf.Ascent + } + } else { + weightType = 500 + if utf.Ascent == 0 { + utf.Ascent = int(float64(utf.Bbox.Ymax) * scale) + } + if utf.Descent == 0 { + utf.Descent = int(float64(utf.Bbox.Ymin) * scale) + } + utf.CapHeight = utf.Ascent + } + utf.StemV = 50 + int(math.Pow(float64(weightType)/65.0, 2)) + return weightType +} + +func (utf *utf8FontFile) parsePOSTTable(weight int) { + utf.SeekTable("post") + utf.skip(4) + utf.ItalicAngle = int(utf.readInt16()) + utf.readUint16()/65536.0 + scale := 1000.0 / float64(utf.fontElementSize) + utf.UnderlinePosition = float64(utf.readInt16()) * scale + utf.UnderlineThickness = float64(utf.readInt16()) * scale + fixed := utf.readUint32() + + utf.Flags = 4 + + if utf.ItalicAngle != 0 { + utf.Flags = utf.Flags | 64 + } + if weight >= 600 { + utf.Flags = utf.Flags | 262144 + } + if fixed != 0 { + utf.Flags = utf.Flags | 1 + } +} + +func (utf *utf8FontFile) parseCMAPTable(format int) int { + cmapPosition := utf.SeekTable("cmap") + utf.skip(2) + cmapTableCount := utf.readUint16() + cidCMAPPosition := 0 + for i := 0; i < cmapTableCount; i++ { + system := utf.readUint16() + coded := utf.readUint16() + position := utf.readUint32() + oldReaderPosition := utf.fileReader.readerPosition + if (system == 3 && coded == 1) || system == 0 { // Microsoft, Unicode + format = utf.getUint16(cmapPosition + position) + if format == 4 { + if cidCMAPPosition == 0 { + cidCMAPPosition = cmapPosition + position + } + break + } + } + utf.seek(int(oldReaderPosition)) + } + if cidCMAPPosition == 0 { + fmt.Printf("Font does not have cmap for Unicode\n") + return cidCMAPPosition + } + return cidCMAPPosition +} + +func (utf *utf8FontFile) parseTables() { + f := utf.parseNAMETable() + utf.parseHEADTable() + n := utf.parseHHEATable() + w := utf.parseOS2Table() + utf.parsePOSTTable(w) + runeCMAPPosition := utf.parseCMAPTable(f) + + utf.SeekTable("maxp") + utf.skip(4) + numSymbols := utf.readUint16() + + symbolCharDictionary := make(map[int][]int) + charSymbolDictionary := make(map[int]int) + utf.generateSCCSDictionaries(runeCMAPPosition, symbolCharDictionary, charSymbolDictionary) + + scale := 1000.0 / float64(utf.fontElementSize) + utf.parseHMTXTable(n, numSymbols, symbolCharDictionary, scale) +} + +func (utf *utf8FontFile) generateCMAP() map[int][]int { + cmapPosition := utf.SeekTable("cmap") + utf.skip(2) + cmapTableCount := utf.readUint16() + runeCmapPosition := 0 + for i := 0; i < cmapTableCount; i++ { + system := utf.readUint16() + coder := utf.readUint16() + position := utf.readUint32() + oldPosition := utf.fileReader.readerPosition + if (system == 3 && coder == 1) || system == 0 { + format := utf.getUint16(cmapPosition + position) + if format == 4 { + runeCmapPosition = cmapPosition + position + break + } + } + utf.seek(int(oldPosition)) + } + + if runeCmapPosition == 0 { + fmt.Printf("Font does not have cmap for Unicode\n") + return nil + } + + symbolCharDictionary := make(map[int][]int) + charSymbolDictionary := make(map[int]int) + utf.generateSCCSDictionaries(runeCmapPosition, symbolCharDictionary, charSymbolDictionary) + + utf.charSymbolDictionary = charSymbolDictionary + + return symbolCharDictionary +} + +func (utf *utf8FontFile) parseSymbols(usedRunes map[int]int) (map[int]int, map[int]int, map[int]int, []int) { + symbolCollection := map[int]int{0: 0} + charSymbolPairCollection := make(map[int]int) + for _, char := range usedRunes { + if _, OK := utf.charSymbolDictionary[char]; OK { + symbolCollection[utf.charSymbolDictionary[char]] = char + charSymbolPairCollection[char] = utf.charSymbolDictionary[char] + + } + utf.LastRune = max(utf.LastRune, char) + } + + begin := utf.tableDescriptions["glyf"].position + + symbolArray := make(map[int]int) + symbolCollectionKeys := keySortInt(symbolCollection) + + symbolCounter := 0 + maxRune := 0 + for _, oldSymbolIndex := range symbolCollectionKeys { + maxRune = max(maxRune, symbolCollection[oldSymbolIndex]) + symbolArray[oldSymbolIndex] = symbolCounter + symbolCounter++ + } + charSymbolPairCollectionKeys := keySortInt(charSymbolPairCollection) + runeSymbolPairCollection := make(map[int]int) + for _, runa := range charSymbolPairCollectionKeys { + runeSymbolPairCollection[runa] = symbolArray[charSymbolPairCollection[runa]] + } + utf.CodeSymbolDictionary = runeSymbolPairCollection + + symbolCollectionKeys = keySortInt(symbolCollection) + for _, oldSymbolIndex := range symbolCollectionKeys { + _, symbolArray, symbolCollection, symbolCollectionKeys = utf.getSymbols(oldSymbolIndex, &begin, symbolArray, symbolCollection, symbolCollectionKeys) + } + + return runeSymbolPairCollection, symbolArray, symbolCollection, symbolCollectionKeys +} + +func (utf *utf8FontFile) generateCMAPTable(cidSymbolPairCollection map[int]int, numSymbols int) []byte { + cidSymbolPairCollectionKeys := keySortInt(cidSymbolPairCollection) + cidID := 0 + cidArray := make(map[int][]int) + prevCid := -2 + prevSymbol := -1 + for _, cid := range cidSymbolPairCollectionKeys { + if cid == (prevCid+1) && cidSymbolPairCollection[cid] == (prevSymbol+1) { + if n, OK := cidArray[cidID]; !OK || n == nil { + cidArray[cidID] = make([]int, 0) + } + cidArray[cidID] = append(cidArray[cidID], cidSymbolPairCollection[cid]) + } else { + cidID = cid + cidArray[cidID] = make([]int, 0) + cidArray[cidID] = append(cidArray[cidID], cidSymbolPairCollection[cid]) + } + prevCid = cid + prevSymbol = cidSymbolPairCollection[cid] + } + cidArrayKeys := keySortArrayRangeMap(cidArray) + segCount := len(cidArray) + 1 + + searchRange := 1 + entrySelector := 0 + for searchRange*2 <= segCount { + searchRange = searchRange * 2 + entrySelector = entrySelector + 1 + } + searchRange = searchRange * 2 + rangeShift := segCount*2 - searchRange + length := 16 + (8 * segCount) + (numSymbols + 1) + cmap := []int{0, 1, 3, 1, 0, 12, 4, length, 0, segCount * 2, searchRange, entrySelector, rangeShift} + + for _, start := range cidArrayKeys { + endCode := start + (len(cidArray[start]) - 1) + cmap = append(cmap, endCode) + } + cmap = append(cmap, 0xFFFF) + cmap = append(cmap, 0) + + for _, cidKey := range cidArrayKeys { + cmap = append(cmap, cidKey) + } + cmap = append(cmap, 0xFFFF) + for _, cidKey := range cidArrayKeys { + idDelta := -(cidKey - cidArray[cidKey][0]) + cmap = append(cmap, idDelta) + } + cmap = append(cmap, 1) + for range cidArray { + cmap = append(cmap, 0) + + } + cmap = append(cmap, 0) + for _, start := range cidArrayKeys { + for _, glidx := range cidArray[start] { + cmap = append(cmap, glidx) + } + } + cmap = append(cmap, 0) + cmapstr := make([]byte, 0) + for _, cm := range cmap { + cmapstr = append(cmapstr, packUint16(cm)...) + } + return cmapstr +} + +//GenerateСutFont fill utf8FontFile from .utf file, only with runes from usedRunes +func (utf *utf8FontFile) GenerateСutFont(usedRunes map[int]int) []byte { + utf.fileReader.readerPosition = 0 + utf.symbolPosition = make([]int, 0) + utf.charSymbolDictionary = make(map[int]int) + utf.tableDescriptions = make(map[string]*tableDescription) + utf.outTablesData = make(map[string][]byte) + utf.Ascent = 0 + utf.Descent = 0 + utf.skip(4) + utf.LastRune = 0 + utf.generateTableDescriptions() + + utf.SeekTable("head") + utf.skip(50) + LocaFormat := utf.readUint16() + + utf.SeekTable("hhea") + utf.skip(34) + metricsCount := utf.readUint16() + oldMetrics := metricsCount + + utf.SeekTable("maxp") + utf.skip(4) + numSymbols := utf.readUint16() + + symbolCharDictionary := utf.generateCMAP() + if symbolCharDictionary == nil { + return nil + } + + utf.parseHMTXTable(metricsCount, numSymbols, symbolCharDictionary, 1.0) + + utf.parseLOCATable(LocaFormat, numSymbols) + + cidSymbolPairCollection, symbolArray, symbolCollection, symbolCollectionKeys := utf.parseSymbols(usedRunes) + + metricsCount = len(symbolCollection) + numSymbols = metricsCount + + utf.setOutTable("name", utf.getTableData("name")) + utf.setOutTable("cvt ", utf.getTableData("cvt ")) + utf.setOutTable("fpgm", utf.getTableData("fpgm")) + utf.setOutTable("prep", utf.getTableData("prep")) + utf.setOutTable("gasp", utf.getTableData("gasp")) + + postTable := utf.getTableData("post") + postTable = append(append([]byte{0x00, 0x03, 0x00, 0x00}, postTable[4:16]...), []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}...) + utf.setOutTable("post", postTable) + + delete(cidSymbolPairCollection, 0) + + utf.setOutTable("cmap", utf.generateCMAPTable(cidSymbolPairCollection, numSymbols)) + + symbolData := utf.getTableData("glyf") + + offsets := make([]int, 0) + glyfData := make([]byte, 0) + pos := 0 + hmtxData := make([]byte, 0) + utf.symbolData = make(map[int]map[string][]int, 0) + + for _, originalSymbolIdx := range symbolCollectionKeys { + hm := utf.getMetrics(oldMetrics, originalSymbolIdx) + hmtxData = append(hmtxData, hm...) + + offsets = append(offsets, pos) + symbolPos := utf.symbolPosition[originalSymbolIdx] + symbolLen := utf.symbolPosition[originalSymbolIdx+1] - symbolPos + data := symbolData[symbolPos : symbolPos+symbolLen] + var up int + if symbolLen > 0 { + up = unpackUint16(data[0:2]) + } + + if symbolLen > 2 && (up&(1<<15)) != 0 { + posInSymbol := 10 + flags := symbolContinue + nComponentElements := 0 + for (flags & symbolContinue) != 0 { + nComponentElements++ + up = unpackUint16(data[posInSymbol : posInSymbol+2]) + flags = up + up = unpackUint16(data[posInSymbol+2 : posInSymbol+4]) + symbolIdx := up + if _, OK := utf.symbolData[originalSymbolIdx]; !OK { + utf.symbolData[originalSymbolIdx] = make(map[string][]int) + } + if _, OK := utf.symbolData[originalSymbolIdx]["compSymbols"]; !OK { + utf.symbolData[originalSymbolIdx]["compSymbols"] = make([]int, 0) + } + utf.symbolData[originalSymbolIdx]["compSymbols"] = append(utf.symbolData[originalSymbolIdx]["compSymbols"], symbolIdx) + data = utf.insertUint16(data, posInSymbol+2, symbolArray[symbolIdx]) + posInSymbol += 4 + if (flags & symbolWords) != 0 { + posInSymbol += 4 + } else { + posInSymbol += 2 + } + if (flags & symbolScale) != 0 { + posInSymbol += 2 + } else if (flags & symbolAllScale) != 0 { + posInSymbol += 4 + } else if (flags & symbol2x2) != 0 { + posInSymbol += 8 + } + } + } + + glyfData = append(glyfData, data...) + pos += symbolLen + if pos%4 != 0 { + padding := 4 - (pos % 4) + glyfData = append(glyfData, make([]byte, padding)...) + pos += padding + } + } + + offsets = append(offsets, pos) + utf.setOutTable("glyf", glyfData) + + utf.setOutTable("hmtx", hmtxData) + + locaData := make([]byte, 0) + if ((pos + 1) >> 1) > 0xFFFF { + LocaFormat = 1 + for _, offset := range offsets { + locaData = append(locaData, packUint32(offset)...) + } + } else { + LocaFormat = 0 + for _, offset := range offsets { + locaData = append(locaData, packUint16(offset/2)...) + } + } + utf.setOutTable("loca", locaData) + + headData := utf.getTableData("head") + headData = utf.insertUint16(headData, 50, LocaFormat) + utf.setOutTable("head", headData) + + hheaData := utf.getTableData("hhea") + hheaData = utf.insertUint16(hheaData, 34, metricsCount) + utf.setOutTable("hhea", hheaData) + + maxp := utf.getTableData("maxp") + maxp = utf.insertUint16(maxp, 4, numSymbols) + utf.setOutTable("maxp", maxp) + + os2Data := utf.getTableData("OS/2") + utf.setOutTable("OS/2", os2Data) + + return utf.assembleTables() +} + +func (utf *utf8FontFile) getSymbols(originalSymbolIdx int, start *int, symbolSet map[int]int, SymbolsCollection map[int]int, SymbolsCollectionKeys []int) (*int, map[int]int, map[int]int, []int) { + symbolPos := utf.symbolPosition[originalSymbolIdx] + symbolSize := utf.symbolPosition[originalSymbolIdx+1] - symbolPos + if symbolSize == 0 { + return start, symbolSet, SymbolsCollection, SymbolsCollectionKeys + } + utf.seek(*start + symbolPos) + + lineCount := utf.readInt16() + + if lineCount < 0 { + utf.skip(8) + flags := symbolContinue + for flags&symbolContinue != 0 { + flags = utf.readUint16() + symbolIndex := utf.readUint16() + if _, OK := symbolSet[symbolIndex]; !OK { + symbolSet[symbolIndex] = len(SymbolsCollection) + SymbolsCollection[symbolIndex] = 1 + SymbolsCollectionKeys = append(SymbolsCollectionKeys, symbolIndex) + } + oldPosition, _ := utf.fileReader.seek(0, 1) + _, _, _, SymbolsCollectionKeys = utf.getSymbols(symbolIndex, start, symbolSet, SymbolsCollection, SymbolsCollectionKeys) + utf.seek(int(oldPosition)) + if flags&symbolWords != 0 { + utf.skip(4) + } else { + utf.skip(2) + } + if flags&symbolScale != 0 { + utf.skip(2) + } else if flags&symbolAllScale != 0 { + utf.skip(4) + } else if flags&symbol2x2 != 0 { + utf.skip(8) + } + } + } + return start, symbolSet, SymbolsCollection, SymbolsCollectionKeys +} + +func (utf *utf8FontFile) parseHMTXTable(numberOfHMetrics, numSymbols int, symbolToChar map[int][]int, scale float64) { + var widths int + start := utf.SeekTable("hmtx") + arrayWidths := 0 + var arr []int + utf.CharWidths = make([]int, 256*256) + charCount := 0 + arr = unpackUint16Array(utf.getRange(start, numberOfHMetrics*4)) + for symbol := 0; symbol < numberOfHMetrics; symbol++ { + arrayWidths = arr[(symbol*2)+1] + if _, OK := symbolToChar[symbol]; OK || symbol == 0 { + + if arrayWidths >= (1 << 15) { + arrayWidths = 0 + } + if symbol == 0 { + utf.DefaultWidth = scale * float64(arrayWidths) + continue + } + for _, char := range symbolToChar[symbol] { + if char != 0 && char != 65535 { + widths = int(math.Round(scale * float64(arrayWidths))) + if widths == 0 { + widths = 65535 + } + if char < 196608 { + utf.CharWidths[char] = widths + charCount++ + } + } + } + } + } + diff := numSymbols - numberOfHMetrics + for pos := 0; pos < diff; pos++ { + symbol := pos + numberOfHMetrics + if _, OK := symbolToChar[symbol]; OK { + for _, char := range symbolToChar[symbol] { + if char != 0 && char != 65535 { + widths = int(math.Round(scale * float64(arrayWidths))) + if widths == 0 { + widths = 65535 + } + if char < 196608 { + utf.CharWidths[char] = widths + charCount++ + } + } + } + } + } + utf.CharWidths[0] = charCount +} + +func (utf *utf8FontFile) getMetrics(metricCount, gid int) []byte { + start := utf.SeekTable("hmtx") + var metrics []byte + if gid < metricCount { + utf.seek(start + (gid * 4)) + metrics = utf.fileReader.Read(4) + } else { + utf.seek(start + ((metricCount - 1) * 4)) + metrics = utf.fileReader.Read(2) + utf.seek(start + (metricCount * 2) + (gid * 2)) + metrics = append(metrics, utf.fileReader.Read(2)...) + } + return metrics +} + +func (utf *utf8FontFile) parseLOCATable(format, numSymbols int) { + start := utf.SeekTable("loca") + utf.symbolPosition = make([]int, 0) + if format == 0 { + data := utf.getRange(start, (numSymbols*2)+2) + arr := unpackUint16Array(data) + for n := 0; n <= numSymbols; n++ { + utf.symbolPosition = append(utf.symbolPosition, arr[n+1]*2) + } + } else if format == 1 { + data := utf.getRange(start, (numSymbols*4)+4) + arr := unpackUint32Array(data) + for n := 0; n <= numSymbols; n++ { + utf.symbolPosition = append(utf.symbolPosition, arr[n+1]) + } + } else { + fmt.Printf("Unknown loca table format %d\n", format) + return + } +} + +func (utf *utf8FontFile) generateSCCSDictionaries(runeCmapPosition int, symbolCharDictionary map[int][]int, charSymbolDictionary map[int]int) { + maxRune := 0 + utf.seek(runeCmapPosition + 2) + size := utf.readUint16() + rim := runeCmapPosition + size + utf.skip(2) + + segmentSize := utf.readUint16() / 2 + utf.skip(6) + completers := make([]int, 0) + for i := 0; i < segmentSize; i++ { + completers = append(completers, utf.readUint16()) + } + utf.skip(2) + beginners := make([]int, 0) + for i := 0; i < segmentSize; i++ { + beginners = append(beginners, utf.readUint16()) + } + sizes := make([]int, 0) + for i := 0; i < segmentSize; i++ { + sizes = append(sizes, int(utf.readInt16())) + } + readerPositionStart := utf.fileReader.readerPosition + positions := make([]int, 0) + for i := 0; i < segmentSize; i++ { + positions = append(positions, utf.readUint16()) + } + var symbol int + for n := 0; n < segmentSize; n++ { + completePosition := completers[n] + 1 + for char := beginners[n]; char < completePosition; char++ { + if positions[n] == 0 { + symbol = (char + sizes[n]) & 0xFFFF + } else { + position := (char-beginners[n])*2 + positions[n] + position = int(readerPositionStart) + 2*n + position + if position >= rim { + symbol = 0 + } else { + symbol = utf.getUint16(position) + if symbol != 0 { + symbol = (symbol + sizes[n]) & 0xFFFF + } + } + } + charSymbolDictionary[char] = symbol + if char < 196608 { + maxRune = max(char, maxRune) + } + symbolCharDictionary[symbol] = append(symbolCharDictionary[symbol], char) + } + } +} + +func max(i, n int) int { + if n > i { + return n + } + return i +} + +func (utf *utf8FontFile) assembleTables() []byte { + answer := make([]byte, 0) + tablesCount := len(utf.outTablesData) + findSize := 1 + writer := 0 + for findSize*2 <= tablesCount { + findSize = findSize * 2 + writer = writer + 1 + } + findSize = findSize * 16 + rOffset := tablesCount*16 - findSize + + answer = append(answer, packHeader(0x00010000, tablesCount, findSize, writer, rOffset)...) + + tables := utf.outTablesData + tablesNames := keySortStrings(tables) + + offset := 12 + tablesCount*16 + begin := 0 + + for _, name := range tablesNames { + if name == "head" { + begin = offset + } + answer = append(answer, []byte(name)...) + checksum := utf.generateChecksum(tables[name]) + answer = append(answer, pack2Uint16(checksum[0], checksum[1])...) + answer = append(answer, pack2Uint32(offset, len(tables[name]))...) + paddedLength := (len(tables[name]) + 3) &^ 3 + offset = offset + paddedLength + } + + for _, key := range tablesNames { + data := tables[key] + data = append(data, []byte{0, 0, 0}...) + answer = append(answer, data[:(len(data)&^3)]...) + } + + checksum := utf.generateChecksum([]byte(answer)) + checksum = utf.calcInt32([]int{0xB1B0, 0xAFBA}, checksum) + answer = utf.splice(answer, (begin + 8), pack2Uint16(checksum[0], checksum[1])) + return answer +} + +func unpackUint16Array(data []byte) []int { + answer := make([]int, 1) + r := bytes.NewReader(data) + bs := make([]byte, 2) + var e error + var c int + c, e = r.Read(bs) + for e == nil && c > 0 { + answer = append(answer, int(binary.BigEndian.Uint16(bs))) + c, e = r.Read(bs) + } + return answer +} + +func unpackUint32Array(data []byte) []int { + answer := make([]int, 1) + r := bytes.NewReader(data) + bs := make([]byte, 4) + var e error + var c int + c, e = r.Read(bs) + for e == nil && c > 0 { + answer = append(answer, int(binary.BigEndian.Uint32(bs))) + c, e = r.Read(bs) + } + return answer +} + +func unpackUint16(data []byte) int { + return int(binary.BigEndian.Uint16(data)) +} + +func packHeader(N uint32, n1, n2, n3, n4 int) []byte { + answer := make([]byte, 0) + bs4 := make([]byte, 4) + binary.BigEndian.PutUint32(bs4, N) + answer = append(answer, bs4...) + bs := make([]byte, 2) + binary.BigEndian.PutUint16(bs, uint16(n1)) + answer = append(answer, bs...) + binary.BigEndian.PutUint16(bs, uint16(n2)) + answer = append(answer, bs...) + binary.BigEndian.PutUint16(bs, uint16(n3)) + answer = append(answer, bs...) + binary.BigEndian.PutUint16(bs, uint16(n4)) + answer = append(answer, bs...) + return answer +} + +func pack2Uint16(n1, n2 int) []byte { + answer := make([]byte, 0) + bs := make([]byte, 2) + binary.BigEndian.PutUint16(bs, uint16(n1)) + answer = append(answer, bs...) + binary.BigEndian.PutUint16(bs, uint16(n2)) + answer = append(answer, bs...) + return answer +} + +func pack2Uint32(n1, n2 int) []byte { + answer := make([]byte, 0) + bs := make([]byte, 4) + binary.BigEndian.PutUint32(bs, uint32(n1)) + answer = append(answer, bs...) + binary.BigEndian.PutUint32(bs, uint32(n2)) + answer = append(answer, bs...) + return answer +} + +func packUint32(n1 int) []byte { + bs := make([]byte, 4) + binary.BigEndian.PutUint32(bs, uint32(n1)) + return bs +} + +func packUint16(n1 int) []byte { + bs := make([]byte, 2) + binary.BigEndian.PutUint16(bs, uint16(n1)) + return bs +} + +func keySortStrings(s map[string][]byte) []string { + keys := make([]string, len(s)) + i := 0 + for key := range s { + keys[i] = key + i++ + } + sort.Strings(keys) + return keys +} + +func keySortInt(s map[int]int) []int { + keys := make([]int, len(s)) + i := 0 + for key := range s { + keys[i] = key + i++ + } + sort.Ints(keys) + return keys +} + +func keySortArrayRangeMap(s map[int][]int) []int { + keys := make([]int, len(s)) + i := 0 + for key := range s { + keys[i] = key + i++ + } + sort.Ints(keys) + return keys +} diff --git a/vendor/github.com/jung-kurt/gofpdf/util.go b/vendor/github.com/jung-kurt/gofpdf/util.go new file mode 100644 index 00000000..39025005 --- /dev/null +++ b/vendor/github.com/jung-kurt/gofpdf/util.go @@ -0,0 +1,446 @@ +/* + * Copyright (c) 2013 Kurt Jung (Gmail: kurt.w.jung) + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package gofpdf + +import ( + "bufio" + "bytes" + "compress/zlib" + "fmt" + "io" + "math" + "os" + "path/filepath" + "strings" +) + +func round(f float64) int { + if f < 0 { + return -int(math.Floor(-f + 0.5)) + } + return int(math.Floor(f + 0.5)) +} + +func sprintf(fmtStr string, args ...interface{}) string { + return fmt.Sprintf(fmtStr, args...) +} + +// fileExist returns true if the specified normal file exists +func fileExist(filename string) (ok bool) { + info, err := os.Stat(filename) + if err == nil { + if ^os.ModePerm&info.Mode() == 0 { + ok = true + } + } + return ok +} + +// fileSize returns the size of the specified file; ok will be false +// if the file does not exist or is not an ordinary file +func fileSize(filename string) (size int64, ok bool) { + info, err := os.Stat(filename) + ok = err == nil + if ok { + size = info.Size() + } + return +} + +// bufferFromReader returns a new buffer populated with the contents of the specified Reader +func bufferFromReader(r io.Reader) (b *bytes.Buffer, err error) { + b = new(bytes.Buffer) + _, err = b.ReadFrom(r) + return +} + +// slicesEqual returns true if the two specified float slices are equal +func slicesEqual(a, b []float64) bool { + if len(a) != len(b) { + return false + } + for i := range a { + if a[i] != b[i] { + return false + } + } + return true +} + +// sliceCompress returns a zlib-compressed copy of the specified byte array +func sliceCompress(data []byte) []byte { + var buf bytes.Buffer + cmp, _ := zlib.NewWriterLevel(&buf, zlib.BestSpeed) + cmp.Write(data) + cmp.Close() + return buf.Bytes() +} + +// sliceUncompress returns an uncompressed copy of the specified zlib-compressed byte array +func sliceUncompress(data []byte) (outData []byte, err error) { + inBuf := bytes.NewReader(data) + r, err := zlib.NewReader(inBuf) + defer r.Close() + if err == nil { + var outBuf bytes.Buffer + _, err = outBuf.ReadFrom(r) + if err == nil { + outData = outBuf.Bytes() + } + } + return +} + +// utf8toutf16 converts UTF-8 to UTF-16BE; from http://www.fpdf.org/ +func utf8toutf16(s string, withBOM ...bool) string { + bom := true + if len(withBOM) > 0 { + bom = withBOM[0] + } + res := make([]byte, 0, 8) + if bom { + res = append(res, 0xFE, 0xFF) + } + nb := len(s) + i := 0 + for i < nb { + c1 := byte(s[i]) + i++ + switch { + case c1 >= 224: + // 3-byte character + c2 := byte(s[i]) + i++ + c3 := byte(s[i]) + i++ + res = append(res, ((c1&0x0F)<<4)+((c2&0x3C)>>2), + ((c2&0x03)<<6)+(c3&0x3F)) + case c1 >= 192: + // 2-byte character + c2 := byte(s[i]) + i++ + res = append(res, ((c1 & 0x1C) >> 2), + ((c1&0x03)<<6)+(c2&0x3F)) + default: + // Single-byte character + res = append(res, 0, c1) + } + } + return string(res) +} + +// intIf returns a if cnd is true, otherwise b +func intIf(cnd bool, a, b int) int { + if cnd { + return a + } + return b +} + +// strIf returns aStr if cnd is true, otherwise bStr +func strIf(cnd bool, aStr, bStr string) string { + if cnd { + return aStr + } + return bStr +} + +// doNothing returns the passed string with no translation. +func doNothing(s string) string { + return s +} + +// Dump the internals of the specified values +// func dump(fileStr string, a ...interface{}) { +// fl, err := os.OpenFile(fileStr, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600) +// if err == nil { +// fmt.Fprintf(fl, "----------------\n") +// spew.Fdump(fl, a...) +// fl.Close() +// } +// } + +func repClosure(m map[rune]byte) func(string) string { + var buf bytes.Buffer + return func(str string) string { + var ch byte + var ok bool + buf.Truncate(0) + for _, r := range str { + if r < 0x80 { + ch = byte(r) + } else { + ch, ok = m[r] + if !ok { + ch = byte('.') + } + } + buf.WriteByte(ch) + } + return buf.String() + } +} + +// UnicodeTranslator returns a function that can be used to translate, where +// possible, utf-8 strings to a form that is compatible with the specified code +// page. The returned function accepts a string and returns a string. +// +// r is a reader that should read a buffer made up of content lines that +// pertain to the code page of interest. Each line is made up of three +// whitespace separated fields. The first begins with "!" and is followed by +// two hexadecimal digits that identify the glyph position in the code page of +// interest. The second field begins with "U+" and is followed by the unicode +// code point value. The third is the glyph name. A number of these code page +// map files are packaged with the gfpdf library in the font directory. +// +// An error occurs only if a line is read that does not conform to the expected +// format. In this case, the returned function is valid but does not perform +// any rune translation. +func UnicodeTranslator(r io.Reader) (f func(string) string, err error) { + m := make(map[rune]byte) + var uPos, cPos uint32 + var lineStr, nameStr string + sc := bufio.NewScanner(r) + for sc.Scan() { + lineStr = sc.Text() + lineStr = strings.TrimSpace(lineStr) + if len(lineStr) > 0 { + _, err = fmt.Sscanf(lineStr, "!%2X U+%4X %s", &cPos, &uPos, &nameStr) + if err == nil { + if cPos >= 0x80 { + m[rune(uPos)] = byte(cPos) + } + } + } + } + if err == nil { + f = repClosure(m) + } else { + f = doNothing + } + return +} + +// UnicodeTranslatorFromFile returns a function that can be used to translate, +// where possible, utf-8 strings to a form that is compatible with the +// specified code page. See UnicodeTranslator for more details. +// +// fileStr identifies a font descriptor file that maps glyph positions to names. +// +// If an error occurs reading the file, the returned function is valid but does +// not perform any rune translation. +func UnicodeTranslatorFromFile(fileStr string) (f func(string) string, err error) { + var fl *os.File + fl, err = os.Open(fileStr) + if err == nil { + f, err = UnicodeTranslator(fl) + fl.Close() + } else { + f = doNothing + } + return +} + +// UnicodeTranslatorFromDescriptor returns a function that can be used to +// translate, where possible, utf-8 strings to a form that is compatible with +// the specified code page. See UnicodeTranslator for more details. +// +// cpStr identifies a code page. A descriptor file in the font directory, set +// with the fontDirStr argument in the call to New(), should have this name +// plus the extension ".map". If cpStr is empty, it will be replaced with +// "cp1252", the gofpdf code page default. +// +// If an error occurs reading the descriptor, the returned function is valid +// but does not perform any rune translation. +// +// The CellFormat_codepage example demonstrates this method. +func (f *Fpdf) UnicodeTranslatorFromDescriptor(cpStr string) (rep func(string) string) { + var str string + var ok bool + if f.err == nil { + if len(cpStr) == 0 { + cpStr = "cp1252" + } + str, ok = embeddedMapList[cpStr] + if ok { + rep, f.err = UnicodeTranslator(strings.NewReader(str)) + } else { + rep, f.err = UnicodeTranslatorFromFile(filepath.Join(f.fontpath, cpStr) + ".map") + } + } else { + rep = doNothing + } + return +} + +// Transform moves a point by given X, Y offset +func (p *PointType) Transform(x, y float64) PointType { + return PointType{p.X + x, p.Y + y} +} + +// Orientation returns the orientation of a given size: +// "P" for portrait, "L" for landscape +func (s *SizeType) Orientation() string { + if s == nil || s.Ht == s.Wd { + return "" + } + if s.Wd > s.Ht { + return "L" + } + return "P" +} + +// ScaleBy expands a size by a certain factor +func (s *SizeType) ScaleBy(factor float64) SizeType { + return SizeType{s.Wd * factor, s.Ht * factor} +} + +// ScaleToWidth adjusts the height of a size to match the given width +func (s *SizeType) ScaleToWidth(width float64) SizeType { + height := s.Ht * width / s.Wd + return SizeType{width, height} +} + +// ScaleToHeight adjusts the width of a size to match the given height +func (s *SizeType) ScaleToHeight(height float64) SizeType { + width := s.Wd * height / s.Ht + return SizeType{width, height} +} + +//The untypedKeyMap structure and its methods are copyrighted 2019 by Arteom Korotkiy (Gmail: arteomkorotkiy). +//Imitation of untyped Map Array +type untypedKeyMap struct { + keySet []interface{} + valueSet []int +} + +//Get position of key=>value in PHP Array +func (pa *untypedKeyMap) getIndex(key interface{}) int { + if key != nil { + for i, mKey := range pa.keySet { + if mKey == key { + return i + } + } + return -1 + } + return -1 +} + +//Put key=>value in PHP Array +func (pa *untypedKeyMap) put(key interface{}, value int) { + if key == nil { + var i int + for n := 0; ; n++ { + i = pa.getIndex(n) + if i < 0 { + key = n + break + } + } + pa.keySet = append(pa.keySet, key) + pa.valueSet = append(pa.valueSet, value) + } else { + i := pa.getIndex(key) + if i < 0 { + pa.keySet = append(pa.keySet, key) + pa.valueSet = append(pa.valueSet, value) + } else { + pa.valueSet[i] = value + } + } +} + +//Delete value in PHP Array +func (pa *untypedKeyMap) delete(key interface{}) { + if pa == nil || pa.keySet == nil || pa.valueSet == nil { + return + } + i := pa.getIndex(key) + if i >= 0 { + if i == 0 { + pa.keySet = pa.keySet[1:] + pa.valueSet = pa.valueSet[1:] + } else if i == len(pa.keySet)-1 { + pa.keySet = pa.keySet[:len(pa.keySet)-1] + pa.valueSet = pa.valueSet[:len(pa.valueSet)-1] + } else { + pa.keySet = append(pa.keySet[:i], pa.keySet[i+1:]...) + pa.valueSet = append(pa.valueSet[:i], pa.valueSet[i+1:]...) + } + } +} + +//Get value from PHP Array +func (pa *untypedKeyMap) get(key interface{}) int { + i := pa.getIndex(key) + if i >= 0 { + return pa.valueSet[i] + } + return 0 +} + +//Imitation of PHP function pop() +func (pa *untypedKeyMap) pop() { + pa.keySet = pa.keySet[:len(pa.keySet)-1] + pa.valueSet = pa.valueSet[:len(pa.valueSet)-1] +} + +//Imitation of PHP function array_merge() +func arrayMerge(arr1, arr2 *untypedKeyMap) *untypedKeyMap { + answer := untypedKeyMap{} + if arr1 == nil && arr2 == nil { + answer = untypedKeyMap{ + make([]interface{}, 0), + make([]int, 0), + } + } else if arr2 == nil { + answer.keySet = arr1.keySet[:] + answer.valueSet = arr1.valueSet[:] + } else if arr1 == nil { + answer.keySet = arr2.keySet[:] + answer.valueSet = arr2.valueSet[:] + } else { + answer.keySet = arr1.keySet[:] + answer.valueSet = arr1.valueSet[:] + for i := 0; i < len(arr2.keySet); i++ { + if arr2.keySet[i] == "interval" { + if arr1.getIndex("interval") < 0 { + answer.put("interval", arr2.valueSet[i]) + } + } else { + answer.put(nil, arr2.valueSet[i]) + } + } + } + return &answer +} + +func remove(arr []int, key int) []int { + n := 0 + for i, mKey := range arr { + if mKey == key { + n = i + } + } + if n == 0 { + return arr[1:] + } else if n == len(arr)-1 { + return arr[:len(arr)-1] + } + return append(arr[:n], arr[n+1:]...) +} diff --git a/vendor/github.com/ruudk/golang-pdf417/.gitignore b/vendor/github.com/ruudk/golang-pdf417/.gitignore new file mode 100644 index 00000000..a09c56df --- /dev/null +++ b/vendor/github.com/ruudk/golang-pdf417/.gitignore @@ -0,0 +1 @@ +/.idea diff --git a/vendor/github.com/ruudk/golang-pdf417/.travis.yml b/vendor/github.com/ruudk/golang-pdf417/.travis.yml new file mode 100644 index 00000000..17f1ba55 --- /dev/null +++ b/vendor/github.com/ruudk/golang-pdf417/.travis.yml @@ -0,0 +1,4 @@ +language: go + +go: + - 1.11 diff --git a/vendor/github.com/ruudk/golang-pdf417/LICENSE b/vendor/github.com/ruudk/golang-pdf417/LICENSE new file mode 100644 index 00000000..3837cf3f --- /dev/null +++ b/vendor/github.com/ruudk/golang-pdf417/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Ruud Kamphuis + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/ruudk/golang-pdf417/README.md b/vendor/github.com/ruudk/golang-pdf417/README.md new file mode 100644 index 00000000..29bc8f48 --- /dev/null +++ b/vendor/github.com/ruudk/golang-pdf417/README.md @@ -0,0 +1,14 @@ +# PDF417 barcodes in Golang + +[![Build Status](https://travis-ci.org/ruudk/golang-pdf417.svg?branch=master)](https://travis-ci.org/ruudk/golang-pdf417) + +This is a port of https://github.com/ihabunek/pdf417-php + +This library encodes data to a PixelGrid that can be used to display the barcode. +You can use the PixelGrid to draw the barcode on anything. Check [pdf417_test.go](pdf417_test.go) for an example. + +I only needed a way to draw PDF417 barcodes onto a [FPDF](https://github.com/jung-kurt/gofpdf) PDF file. +See example: [examples/fpdf.go](examples/fpdf.go) + +If you want to export the barcode to an image, you have to create something that puts the PixelGrid onto the image. If you find a way, please submit a PR :) + diff --git a/vendor/github.com/ruudk/golang-pdf417/byte_encoder.go b/vendor/github.com/ruudk/golang-pdf417/byte_encoder.go new file mode 100644 index 00000000..0c3dfb41 --- /dev/null +++ b/vendor/github.com/ruudk/golang-pdf417/byte_encoder.go @@ -0,0 +1,28 @@ +package pdf417 + +// Code word used to switch to Text mode. +const BYTE_SWITCH_CODE_WORD = 901 + +// Alternate code word used to switch to Text mode; used when number of +// Texts to encode is divisible by 6. +const BYTE_SWITCH_CODE_WORD_ALT = 924 + +type ByteEncoder struct { + +} + +func CreateByteEncoder() *ByteEncoder { + return new(ByteEncoder) +} + +func (ByteEncoder) CanEncode(char string) bool { + return true +} + +func (ByteEncoder) GetSwitchCode(data string) int { + if (len(data) % 6 == 0) { + return BYTE_SWITCH_CODE_WORD_ALT + } + + return BYTE_SWITCH_CODE_WORD +} diff --git a/vendor/github.com/ruudk/golang-pdf417/data_encoder.go b/vendor/github.com/ruudk/golang-pdf417/data_encoder.go new file mode 100644 index 00000000..cd7d2066 --- /dev/null +++ b/vendor/github.com/ruudk/golang-pdf417/data_encoder.go @@ -0,0 +1,99 @@ +package pdf417 + +type Encoder interface { + GetName() string + CanEncode(char string) bool + GetSwitchCode(data string) int + Encode(data string, addSwitchCode bool) []int +} + +type DataEncoder struct { + Encoders []Encoder + DefaultEncoder Encoder +} + +type Chain struct { + Data string + Encoder Encoder +} + +func CreateDataEncoder() DataEncoder { + numberEncoder := CreateNumberEncoder() + textEncoder := CreateTextEncoder() + + encoder := DataEncoder{ + []Encoder{numberEncoder, textEncoder}, + textEncoder, + } + + return encoder +} + +func (dataEncoder DataEncoder) Encode(data string) []int { + chains := dataEncoder.SplitToChains(data) + + if len(chains) == 0 { + panic("hmmm") + } + + + firstEncoder := chains[0].Encoder + addSwitchCode := firstEncoder.GetName() != dataEncoder.DefaultEncoder.GetName() + + cws := []int{} + + for _, chainWithEncoder := range chains { + encoded := chainWithEncoder.Encoder.Encode( + chainWithEncoder.Data, + addSwitchCode, + ) + + cws = append(cws, encoded...) + + addSwitchCode = true + } + + return cws +} + +func (dataEncoder DataEncoder) SplitToChains(data string) []Chain { + chains := []Chain{} + chainData := "" + encoder := dataEncoder.DefaultEncoder + + for i := 0; i < len(data); i++ { + char := string(data[i]) + + newEncoder := getEncoder( + dataEncoder.Encoders, + char, + ) + + if newEncoder != encoder { + if len(chainData) > 0 { + chains = append(chains, Chain{chainData, encoder}) + chainData = "" + } + + encoder = newEncoder + } + + chainData = chainData + char + } + + if len(chainData) > 0 { + chains = append(chains, Chain{chainData, encoder}) + } + + return chains +} + +func getEncoder(encoders []Encoder, char string) Encoder { + for _, encoder := range encoders { + if encoder.CanEncode(char) { + return encoder + } + } + + panic("Cannot encode character " + char) +} diff --git a/vendor/github.com/ruudk/golang-pdf417/go.mod b/vendor/github.com/ruudk/golang-pdf417/go.mod new file mode 100644 index 00000000..6fe8fdfe --- /dev/null +++ b/vendor/github.com/ruudk/golang-pdf417/go.mod @@ -0,0 +1,9 @@ +module github.com/ruudk/golang-pdf417 + +require ( + github.com/boombuler/barcode v1.0.0 + github.com/davecgh/go-spew v1.1.0 // indirect + github.com/jung-kurt/gofpdf v1.0.0 + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/testify v1.2.2 +) diff --git a/vendor/github.com/ruudk/golang-pdf417/go.sum b/vendor/github.com/ruudk/golang-pdf417/go.sum new file mode 100644 index 00000000..518eae8d --- /dev/null +++ b/vendor/github.com/ruudk/golang-pdf417/go.sum @@ -0,0 +1,10 @@ +github.com/boombuler/barcode v1.0.0 h1:s1TvRnXwL2xJRaccrdcBQMZxq6X7DvsMogtmJeHDdrc= +github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/jung-kurt/gofpdf v1.0.0 h1:EroSdlP9BOoL5ssLYf3uLJXhCQMMM2fFxCJDKA3RhnA= +github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= diff --git a/vendor/github.com/ruudk/golang-pdf417/number_encoder.go b/vendor/github.com/ruudk/golang-pdf417/number_encoder.go new file mode 100644 index 00000000..2b0ab54b --- /dev/null +++ b/vendor/github.com/ruudk/golang-pdf417/number_encoder.go @@ -0,0 +1,87 @@ +package pdf417 + +import ( + "regexp" + "math" + "math/big" +) + +const NUMBER_SWITCH_CODE_WORD int = 902 + +type NumberEncoder struct { + +} + +func CreateNumberEncoder() *NumberEncoder { + return new(NumberEncoder) +} + +func (encoder NumberEncoder) GetName() string { + return "number" +} + +func (encoder NumberEncoder) CanEncode(char string) bool { + match, err := regexp.MatchString("^[0-9]{1}$", char) + + if err != nil { + return false + } + + return match +} + +func (encoder NumberEncoder) GetSwitchCode(data string) int { + return NUMBER_SWITCH_CODE_WORD +} + +func (encoder NumberEncoder) Encode(digits string, addSwitchCode bool) []int { + digitCount := len(digits) + chunkCount := int(math.Ceil(float64(digitCount) / float64(44))) + + codeWords := []int{} + + if (addSwitchCode) { + codeWords = append(codeWords, NUMBER_SWITCH_CODE_WORD) + } + + for i := 0; i < chunkCount; i++ { + start := i * 44 + end := start + 44 + if end > digitCount { + end = digitCount + } + chunk := digits[start:end] + + cws := encodeChunk(chunk) + + codeWords = append(codeWords, cws...) + } + + return codeWords +} + +func encodeChunk(chunkInput string) []int { + chunk := big.NewInt(0) + + _, ok := chunk.SetString("1" + chunkInput, 10) + + if ! ok { + panic("Failed converting") + } + + cws := []int{} + + for chunk.Cmp(big.NewInt(0)) > 0 { + newChunk, cw := chunk.DivMod( + chunk, + big.NewInt(900), + big.NewInt(0), + ) + + chunk = newChunk + + cws = append([]int{int(cw.Int64())}, cws...) + } + + return cws +} diff --git a/vendor/github.com/ruudk/golang-pdf417/pdf417.go b/vendor/github.com/ruudk/golang-pdf417/pdf417.go new file mode 100644 index 00000000..24e43751 --- /dev/null +++ b/vendor/github.com/ruudk/golang-pdf417/pdf417.go @@ -0,0 +1,633 @@ +package pdf417 + +import ( + "math" + "strconv" + "github.com/boombuler/barcode" + "image" + "image/color" +) + +const MIN_COLUMNS = 1; +const MAX_COLUMNS = 30; +const DEFAULT_COLUMNS = 6; + +const MIN_SECURITY_LEVEL = 0; +const MAX_SECURITY_LEVEL = 8; +const DEFAULT_SECURITY_LEVEL = 2; + +const MIN_ROWS = 3; +const MAX_ROWS = 90; +const MAX_CODE_WORDS = 925; + +const START_CHARACTER = 0x1fea8; +const STOP_CHARACTER = 0x3fa29; + +const PADDING_CODE_WORD = 900; + +type Barcode struct { + Data string + CodeWords []int + Columns int + Rows int + Codes [][]int + SecurityLevel int + pixelGrid [][]bool +} + +func (c *Barcode) Metadata() barcode.Metadata { + return barcode.Metadata{"Pdf417", 2} +} + +func (c *Barcode) Content() string { + return c.Data +} + +func (c *Barcode) CheckSum() int { + return 0 +} + +func (c *Barcode) ColorModel() color.Model { + return color.Gray16Model +} + +func (c *Barcode) Bounds() image.Rectangle { + grid := c.PixelGrid() + + return image.Rect(0, 0, len(grid[0]), len(grid)) +} + +func (c *Barcode) At(x, y int) color.Color { + grid := c.PixelGrid() + + if grid[y] != nil && grid[y][x] == true { + return color.Black + } + + return color.White +} + +func Encode(data string, columns int, securityLevel int) *Barcode { + barcode := new(Barcode) + barcode.Data = data + barcode.Columns = columns + barcode.SecurityLevel = securityLevel + + codeWords := encodeData(barcode) + + grid := [][]int{}; + + for i := 0; i < len(codeWords); i += barcode.Columns { + grid = append( + grid, + codeWords[i:min(i + barcode.Columns, len(codeWords))], + ) + } + + rows := len(grid) + + codes := [][]int{} + for rowNum, row := range grid { + table := rowNum % 3 + rowCodes := []int{START_CHARACTER} + + left := getLeftCodeWord( + rowNum, + rows, + barcode.Columns, + barcode.SecurityLevel, + ) + rowCodes = append(rowCodes, getCode(table, left)) + + for _, word := range row { + rowCodes = append(rowCodes, getCode(table, word)) + } + + right := getRightCodeWord(rowNum, rows, barcode.Columns, barcode.SecurityLevel) + rowCodes = append(rowCodes, getCode(table, right)) + + rowCodes = append(rowCodes, STOP_CHARACTER) + + codes = append(codes, rowCodes) + } + + barcode.Rows = rows + barcode.Codes = codes + barcode.CodeWords = codeWords + + return barcode +} + +func getLeftCodeWord(rowNum int, rows int, columns int, securityLevel int) int { + tableId := rowNum % 3 + + var x int + + switch tableId { + case 0: + x = (rows - 3) / 3 + case 1: + x = securityLevel * 3 + x += (rows - 1) % 3 + case 2: + x = columns - 1 + } + + return 30 * (rowNum / 3) + x +} + +func getRightCodeWord(rowNum int, rows int, columns int, securityLevel int) int { + tableId := rowNum % 3 + + var x int + + switch tableId { + case 0: + x = columns - 1 + case 1: + x = (rows - 1) / 3 + case 2: + x = securityLevel * 3 + x += (rows - 1) % 3 + } + + return 30 * (rowNum / 3) + x +} + +func min(a, b int) int { + if a <= b { + return a + } + return b +} + +func encodeData(barcode *Barcode) []int { + dataEncoder := CreateDataEncoder() + dataWords := dataEncoder.Encode(barcode.Data) + + ecCount := int(math.Pow(2, float64(barcode.SecurityLevel + 1))) + dataCount := len(dataWords) + + padWords := getPadding(dataCount, ecCount, barcode.Columns) + dataWords = append(dataWords, padWords...) + + length := len(dataWords) + 1 + dataWords = append([]int{length}, dataWords...) + + ecWords := ComputeReedSolomon(dataWords, barcode.SecurityLevel) + + return append(dataWords, ecWords...) +} + +func getPadding(dataCount int, ecCount int, columns int) []int { + totalCount := dataCount + ecCount + 1 + mod := totalCount % columns + + padding := []int{} + + if mod > 0 { + padCount := columns - mod + padding = make([]int, padCount) + for i := 0; i < padCount; i++ { + padding[i] = PADDING_CODE_WORD + } + } + + return padding +} + +var codes = [][]int{ + []int{ + 0x1d5c0, 0x1eaf0, 0x1f57c, 0x1d4e0, 0x1ea78, 0x1f53e, 0x1a8c0, + 0x1d470, 0x1a860, 0x15040, 0x1a830, 0x15020, 0x1adc0, 0x1d6f0, + 0x1eb7c, 0x1ace0, 0x1d678, 0x1eb3e, 0x158c0, 0x1ac70, 0x15860, + 0x15dc0, 0x1aef0, 0x1d77c, 0x15ce0, 0x1ae78, 0x1d73e, 0x15c70, + 0x1ae3c, 0x15ef0, 0x1af7c, 0x15e78, 0x1af3e, 0x15f7c, 0x1f5fa, + 0x1d2e0, 0x1e978, 0x1f4be, 0x1a4c0, 0x1d270, 0x1e93c, 0x1a460, + 0x1d238, 0x14840, 0x1a430, 0x1d21c, 0x14820, 0x1a418, 0x14810, + 0x1a6e0, 0x1d378, 0x1e9be, 0x14cc0, 0x1a670, 0x1d33c, 0x14c60, + 0x1a638, 0x1d31e, 0x14c30, 0x1a61c, 0x14ee0, 0x1a778, 0x1d3be, + 0x14e70, 0x1a73c, 0x14e38, 0x1a71e, 0x14f78, 0x1a7be, 0x14f3c, + 0x14f1e, 0x1a2c0, 0x1d170, 0x1e8bc, 0x1a260, 0x1d138, 0x1e89e, + 0x14440, 0x1a230, 0x1d11c, 0x14420, 0x1a218, 0x14410, 0x14408, + 0x146c0, 0x1a370, 0x1d1bc, 0x14660, 0x1a338, 0x1d19e, 0x14630, + 0x1a31c, 0x14618, 0x1460c, 0x14770, 0x1a3bc, 0x14738, 0x1a39e, + 0x1471c, 0x147bc, 0x1a160, 0x1d0b8, 0x1e85e, 0x14240, 0x1a130, + 0x1d09c, 0x14220, 0x1a118, 0x1d08e, 0x14210, 0x1a10c, 0x14208, + 0x1a106, 0x14360, 0x1a1b8, 0x1d0de, 0x14330, 0x1a19c, 0x14318, + 0x1a18e, 0x1430c, 0x14306, 0x1a1de, 0x1438e, 0x14140, 0x1a0b0, + 0x1d05c, 0x14120, 0x1a098, 0x1d04e, 0x14110, 0x1a08c, 0x14108, + 0x1a086, 0x14104, 0x141b0, 0x14198, 0x1418c, 0x140a0, 0x1d02e, + 0x1a04c, 0x1a046, 0x14082, 0x1cae0, 0x1e578, 0x1f2be, 0x194c0, + 0x1ca70, 0x1e53c, 0x19460, 0x1ca38, 0x1e51e, 0x12840, 0x19430, + 0x12820, 0x196e0, 0x1cb78, 0x1e5be, 0x12cc0, 0x19670, 0x1cb3c, + 0x12c60, 0x19638, 0x12c30, 0x12c18, 0x12ee0, 0x19778, 0x1cbbe, + 0x12e70, 0x1973c, 0x12e38, 0x12e1c, 0x12f78, 0x197be, 0x12f3c, + 0x12fbe, 0x1dac0, 0x1ed70, 0x1f6bc, 0x1da60, 0x1ed38, 0x1f69e, + 0x1b440, 0x1da30, 0x1ed1c, 0x1b420, 0x1da18, 0x1ed0e, 0x1b410, + 0x1da0c, 0x192c0, 0x1c970, 0x1e4bc, 0x1b6c0, 0x19260, 0x1c938, + 0x1e49e, 0x1b660, 0x1db38, 0x1ed9e, 0x16c40, 0x12420, 0x19218, + 0x1c90e, 0x16c20, 0x1b618, 0x16c10, 0x126c0, 0x19370, 0x1c9bc, + 0x16ec0, 0x12660, 0x19338, 0x1c99e, 0x16e60, 0x1b738, 0x1db9e, + 0x16e30, 0x12618, 0x16e18, 0x12770, 0x193bc, 0x16f70, 0x12738, + 0x1939e, 0x16f38, 0x1b79e, 0x16f1c, 0x127bc, 0x16fbc, 0x1279e, + 0x16f9e, 0x1d960, 0x1ecb8, 0x1f65e, 0x1b240, 0x1d930, 0x1ec9c, + 0x1b220, 0x1d918, 0x1ec8e, 0x1b210, 0x1d90c, 0x1b208, 0x1b204, + 0x19160, 0x1c8b8, 0x1e45e, 0x1b360, 0x19130, 0x1c89c, 0x16640, + 0x12220, 0x1d99c, 0x1c88e, 0x16620, 0x12210, 0x1910c, 0x16610, + 0x1b30c, 0x19106, 0x12204, 0x12360, 0x191b8, 0x1c8de, 0x16760, + 0x12330, 0x1919c, 0x16730, 0x1b39c, 0x1918e, 0x16718, 0x1230c, + 0x12306, 0x123b8, 0x191de, 0x167b8, 0x1239c, 0x1679c, 0x1238e, + 0x1678e, 0x167de, 0x1b140, 0x1d8b0, 0x1ec5c, 0x1b120, 0x1d898, + 0x1ec4e, 0x1b110, 0x1d88c, 0x1b108, 0x1d886, 0x1b104, 0x1b102, + 0x12140, 0x190b0, 0x1c85c, 0x16340, 0x12120, 0x19098, 0x1c84e, + 0x16320, 0x1b198, 0x1d8ce, 0x16310, 0x12108, 0x19086, 0x16308, + 0x1b186, 0x16304, 0x121b0, 0x190dc, 0x163b0, 0x12198, 0x190ce, + 0x16398, 0x1b1ce, 0x1638c, 0x12186, 0x16386, 0x163dc, 0x163ce, + 0x1b0a0, 0x1d858, 0x1ec2e, 0x1b090, 0x1d84c, 0x1b088, 0x1d846, + 0x1b084, 0x1b082, 0x120a0, 0x19058, 0x1c82e, 0x161a0, 0x12090, + 0x1904c, 0x16190, 0x1b0cc, 0x19046, 0x16188, 0x12084, 0x16184, + 0x12082, 0x120d8, 0x161d8, 0x161cc, 0x161c6, 0x1d82c, 0x1d826, + 0x1b042, 0x1902c, 0x12048, 0x160c8, 0x160c4, 0x160c2, 0x18ac0, + 0x1c570, 0x1e2bc, 0x18a60, 0x1c538, 0x11440, 0x18a30, 0x1c51c, + 0x11420, 0x18a18, 0x11410, 0x11408, 0x116c0, 0x18b70, 0x1c5bc, + 0x11660, 0x18b38, 0x1c59e, 0x11630, 0x18b1c, 0x11618, 0x1160c, + 0x11770, 0x18bbc, 0x11738, 0x18b9e, 0x1171c, 0x117bc, 0x1179e, + 0x1cd60, 0x1e6b8, 0x1f35e, 0x19a40, 0x1cd30, 0x1e69c, 0x19a20, + 0x1cd18, 0x1e68e, 0x19a10, 0x1cd0c, 0x19a08, 0x1cd06, 0x18960, + 0x1c4b8, 0x1e25e, 0x19b60, 0x18930, 0x1c49c, 0x13640, 0x11220, + 0x1cd9c, 0x1c48e, 0x13620, 0x19b18, 0x1890c, 0x13610, 0x11208, + 0x13608, 0x11360, 0x189b8, 0x1c4de, 0x13760, 0x11330, 0x1cdde, + 0x13730, 0x19b9c, 0x1898e, 0x13718, 0x1130c, 0x1370c, 0x113b8, + 0x189de, 0x137b8, 0x1139c, 0x1379c, 0x1138e, 0x113de, 0x137de, + 0x1dd40, 0x1eeb0, 0x1f75c, 0x1dd20, 0x1ee98, 0x1f74e, 0x1dd10, + 0x1ee8c, 0x1dd08, 0x1ee86, 0x1dd04, 0x19940, 0x1ccb0, 0x1e65c, + 0x1bb40, 0x19920, 0x1eedc, 0x1e64e, 0x1bb20, 0x1dd98, 0x1eece, + 0x1bb10, 0x19908, 0x1cc86, 0x1bb08, 0x1dd86, 0x19902, 0x11140, + 0x188b0, 0x1c45c, 0x13340, 0x11120, 0x18898, 0x1c44e, 0x17740, + 0x13320, 0x19998, 0x1ccce, 0x17720, 0x1bb98, 0x1ddce, 0x18886, + 0x17710, 0x13308, 0x19986, 0x17708, 0x11102, 0x111b0, 0x188dc, + 0x133b0, 0x11198, 0x188ce, 0x177b0, 0x13398, 0x199ce, 0x17798, + 0x1bbce, 0x11186, 0x13386, 0x111dc, 0x133dc, 0x111ce, 0x177dc, + 0x133ce, 0x1dca0, 0x1ee58, 0x1f72e, 0x1dc90, 0x1ee4c, 0x1dc88, + 0x1ee46, 0x1dc84, 0x1dc82, 0x198a0, 0x1cc58, 0x1e62e, 0x1b9a0, + 0x19890, 0x1ee6e, 0x1b990, 0x1dccc, 0x1cc46, 0x1b988, 0x19884, + 0x1b984, 0x19882, 0x1b982, 0x110a0, 0x18858, 0x1c42e, 0x131a0, + 0x11090, 0x1884c, 0x173a0, 0x13190, 0x198cc, 0x18846, 0x17390, + 0x1b9cc, 0x11084, 0x17388, 0x13184, 0x11082, 0x13182, 0x110d8, + 0x1886e, 0x131d8, 0x110cc, 0x173d8, 0x131cc, 0x110c6, 0x173cc, + 0x131c6, 0x110ee, 0x173ee, 0x1dc50, 0x1ee2c, 0x1dc48, 0x1ee26, + 0x1dc44, 0x1dc42, 0x19850, 0x1cc2c, 0x1b8d0, 0x19848, 0x1cc26, + 0x1b8c8, 0x1dc66, 0x1b8c4, 0x19842, 0x1b8c2, 0x11050, 0x1882c, + 0x130d0, 0x11048, 0x18826, 0x171d0, 0x130c8, 0x19866, 0x171c8, + 0x1b8e6, 0x11042, 0x171c4, 0x130c2, 0x171c2, 0x130ec, 0x171ec, + 0x171e6, 0x1ee16, 0x1dc22, 0x1cc16, 0x19824, 0x19822, 0x11028, + 0x13068, 0x170e8, 0x11022, 0x13062, 0x18560, 0x10a40, 0x18530, + 0x10a20, 0x18518, 0x1c28e, 0x10a10, 0x1850c, 0x10a08, 0x18506, + 0x10b60, 0x185b8, 0x1c2de, 0x10b30, 0x1859c, 0x10b18, 0x1858e, + 0x10b0c, 0x10b06, 0x10bb8, 0x185de, 0x10b9c, 0x10b8e, 0x10bde, + 0x18d40, 0x1c6b0, 0x1e35c, 0x18d20, 0x1c698, 0x18d10, 0x1c68c, + 0x18d08, 0x1c686, 0x18d04, 0x10940, 0x184b0, 0x1c25c, 0x11b40, + 0x10920, 0x1c6dc, 0x1c24e, 0x11b20, 0x18d98, 0x1c6ce, 0x11b10, + 0x10908, 0x18486, 0x11b08, 0x18d86, 0x10902, 0x109b0, 0x184dc, + 0x11bb0, 0x10998, 0x184ce, 0x11b98, 0x18dce, 0x11b8c, 0x10986, + 0x109dc, 0x11bdc, 0x109ce, 0x11bce, 0x1cea0, 0x1e758, 0x1f3ae, + 0x1ce90, 0x1e74c, 0x1ce88, 0x1e746, 0x1ce84, 0x1ce82, 0x18ca0, + 0x1c658, 0x19da0, 0x18c90, 0x1c64c, 0x19d90, 0x1cecc, 0x1c646, + 0x19d88, 0x18c84, 0x19d84, 0x18c82, 0x19d82, 0x108a0, 0x18458, + 0x119a0, 0x10890, 0x1c66e, 0x13ba0, 0x11990, 0x18ccc, 0x18446, + 0x13b90, 0x19dcc, 0x10884, 0x13b88, 0x11984, 0x10882, 0x11982, + 0x108d8, 0x1846e, 0x119d8, 0x108cc, 0x13bd8, 0x119cc, 0x108c6, + 0x13bcc, 0x119c6, 0x108ee, 0x119ee, 0x13bee, 0x1ef50, 0x1f7ac, + 0x1ef48, 0x1f7a6, 0x1ef44, 0x1ef42, 0x1ce50, 0x1e72c, 0x1ded0, + 0x1ef6c, 0x1e726, 0x1dec8, 0x1ef66, 0x1dec4, 0x1ce42, 0x1dec2, + 0x18c50, 0x1c62c, 0x19cd0, 0x18c48, 0x1c626, 0x1bdd0, 0x19cc8, + 0x1ce66, 0x1bdc8, 0x1dee6, 0x18c42, 0x1bdc4, 0x19cc2, 0x1bdc2, + 0x10850, 0x1842c, 0x118d0, 0x10848, 0x18426, 0x139d0, 0x118c8, + 0x18c66, 0x17bd0, 0x139c8, 0x19ce6, 0x10842, 0x17bc8, 0x1bde6, + 0x118c2, 0x17bc4, 0x1086c, 0x118ec, 0x10866, 0x139ec, 0x118e6, + 0x17bec, 0x139e6, 0x17be6, 0x1ef28, 0x1f796, 0x1ef24, 0x1ef22, + 0x1ce28, 0x1e716, 0x1de68, 0x1ef36, 0x1de64, 0x1ce22, 0x1de62, + 0x18c28, 0x1c616, 0x19c68, 0x18c24, 0x1bce8, 0x19c64, 0x18c22, + 0x1bce4, 0x19c62, 0x1bce2, 0x10828, 0x18416, 0x11868, 0x18c36, + 0x138e8, 0x11864, 0x10822, 0x179e8, 0x138e4, 0x11862, 0x179e4, + 0x138e2, 0x179e2, 0x11876, 0x179f6, 0x1ef12, 0x1de34, 0x1de32, + 0x19c34, 0x1bc74, 0x1bc72, 0x11834, 0x13874, 0x178f4, 0x178f2, + 0x10540, 0x10520, 0x18298, 0x10510, 0x10508, 0x10504, 0x105b0, + 0x10598, 0x1058c, 0x10586, 0x105dc, 0x105ce, 0x186a0, 0x18690, + 0x1c34c, 0x18688, 0x1c346, 0x18684, 0x18682, 0x104a0, 0x18258, + 0x10da0, 0x186d8, 0x1824c, 0x10d90, 0x186cc, 0x10d88, 0x186c6, + 0x10d84, 0x10482, 0x10d82, 0x104d8, 0x1826e, 0x10dd8, 0x186ee, + 0x10dcc, 0x104c6, 0x10dc6, 0x104ee, 0x10dee, 0x1c750, 0x1c748, + 0x1c744, 0x1c742, 0x18650, 0x18ed0, 0x1c76c, 0x1c326, 0x18ec8, + 0x1c766, 0x18ec4, 0x18642, 0x18ec2, 0x10450, 0x10cd0, 0x10448, + 0x18226, 0x11dd0, 0x10cc8, 0x10444, 0x11dc8, 0x10cc4, 0x10442, + 0x11dc4, 0x10cc2, 0x1046c, 0x10cec, 0x10466, 0x11dec, 0x10ce6, + 0x11de6, 0x1e7a8, 0x1e7a4, 0x1e7a2, 0x1c728, 0x1cf68, 0x1e7b6, + 0x1cf64, 0x1c722, 0x1cf62, 0x18628, 0x1c316, 0x18e68, 0x1c736, + 0x19ee8, 0x18e64, 0x18622, 0x19ee4, 0x18e62, 0x19ee2, 0x10428, + 0x18216, 0x10c68, 0x18636, 0x11ce8, 0x10c64, 0x10422, 0x13de8, + 0x11ce4, 0x10c62, 0x13de4, 0x11ce2, 0x10436, 0x10c76, 0x11cf6, + 0x13df6, 0x1f7d4, 0x1f7d2, 0x1e794, 0x1efb4, 0x1e792, 0x1efb2, + 0x1c714, 0x1cf34, 0x1c712, 0x1df74, 0x1cf32, 0x1df72, 0x18614, + 0x18e34, 0x18612, 0x19e74, 0x18e32, 0x1bef4, + }, + []int{ + 0x1f560, 0x1fab8, 0x1ea40, 0x1f530, 0x1fa9c, 0x1ea20, 0x1f518, + 0x1fa8e, 0x1ea10, 0x1f50c, 0x1ea08, 0x1f506, 0x1ea04, 0x1eb60, + 0x1f5b8, 0x1fade, 0x1d640, 0x1eb30, 0x1f59c, 0x1d620, 0x1eb18, + 0x1f58e, 0x1d610, 0x1eb0c, 0x1d608, 0x1eb06, 0x1d604, 0x1d760, + 0x1ebb8, 0x1f5de, 0x1ae40, 0x1d730, 0x1eb9c, 0x1ae20, 0x1d718, + 0x1eb8e, 0x1ae10, 0x1d70c, 0x1ae08, 0x1d706, 0x1ae04, 0x1af60, + 0x1d7b8, 0x1ebde, 0x15e40, 0x1af30, 0x1d79c, 0x15e20, 0x1af18, + 0x1d78e, 0x15e10, 0x1af0c, 0x15e08, 0x1af06, 0x15f60, 0x1afb8, + 0x1d7de, 0x15f30, 0x1af9c, 0x15f18, 0x1af8e, 0x15f0c, 0x15fb8, + 0x1afde, 0x15f9c, 0x15f8e, 0x1e940, 0x1f4b0, 0x1fa5c, 0x1e920, + 0x1f498, 0x1fa4e, 0x1e910, 0x1f48c, 0x1e908, 0x1f486, 0x1e904, + 0x1e902, 0x1d340, 0x1e9b0, 0x1f4dc, 0x1d320, 0x1e998, 0x1f4ce, + 0x1d310, 0x1e98c, 0x1d308, 0x1e986, 0x1d304, 0x1d302, 0x1a740, + 0x1d3b0, 0x1e9dc, 0x1a720, 0x1d398, 0x1e9ce, 0x1a710, 0x1d38c, + 0x1a708, 0x1d386, 0x1a704, 0x1a702, 0x14f40, 0x1a7b0, 0x1d3dc, + 0x14f20, 0x1a798, 0x1d3ce, 0x14f10, 0x1a78c, 0x14f08, 0x1a786, + 0x14f04, 0x14fb0, 0x1a7dc, 0x14f98, 0x1a7ce, 0x14f8c, 0x14f86, + 0x14fdc, 0x14fce, 0x1e8a0, 0x1f458, 0x1fa2e, 0x1e890, 0x1f44c, + 0x1e888, 0x1f446, 0x1e884, 0x1e882, 0x1d1a0, 0x1e8d8, 0x1f46e, + 0x1d190, 0x1e8cc, 0x1d188, 0x1e8c6, 0x1d184, 0x1d182, 0x1a3a0, + 0x1d1d8, 0x1e8ee, 0x1a390, 0x1d1cc, 0x1a388, 0x1d1c6, 0x1a384, + 0x1a382, 0x147a0, 0x1a3d8, 0x1d1ee, 0x14790, 0x1a3cc, 0x14788, + 0x1a3c6, 0x14784, 0x14782, 0x147d8, 0x1a3ee, 0x147cc, 0x147c6, + 0x147ee, 0x1e850, 0x1f42c, 0x1e848, 0x1f426, 0x1e844, 0x1e842, + 0x1d0d0, 0x1e86c, 0x1d0c8, 0x1e866, 0x1d0c4, 0x1d0c2, 0x1a1d0, + 0x1d0ec, 0x1a1c8, 0x1d0e6, 0x1a1c4, 0x1a1c2, 0x143d0, 0x1a1ec, + 0x143c8, 0x1a1e6, 0x143c4, 0x143c2, 0x143ec, 0x143e6, 0x1e828, + 0x1f416, 0x1e824, 0x1e822, 0x1d068, 0x1e836, 0x1d064, 0x1d062, + 0x1a0e8, 0x1d076, 0x1a0e4, 0x1a0e2, 0x141e8, 0x1a0f6, 0x141e4, + 0x141e2, 0x1e814, 0x1e812, 0x1d034, 0x1d032, 0x1a074, 0x1a072, + 0x1e540, 0x1f2b0, 0x1f95c, 0x1e520, 0x1f298, 0x1f94e, 0x1e510, + 0x1f28c, 0x1e508, 0x1f286, 0x1e504, 0x1e502, 0x1cb40, 0x1e5b0, + 0x1f2dc, 0x1cb20, 0x1e598, 0x1f2ce, 0x1cb10, 0x1e58c, 0x1cb08, + 0x1e586, 0x1cb04, 0x1cb02, 0x19740, 0x1cbb0, 0x1e5dc, 0x19720, + 0x1cb98, 0x1e5ce, 0x19710, 0x1cb8c, 0x19708, 0x1cb86, 0x19704, + 0x19702, 0x12f40, 0x197b0, 0x1cbdc, 0x12f20, 0x19798, 0x1cbce, + 0x12f10, 0x1978c, 0x12f08, 0x19786, 0x12f04, 0x12fb0, 0x197dc, + 0x12f98, 0x197ce, 0x12f8c, 0x12f86, 0x12fdc, 0x12fce, 0x1f6a0, + 0x1fb58, 0x16bf0, 0x1f690, 0x1fb4c, 0x169f8, 0x1f688, 0x1fb46, + 0x168fc, 0x1f684, 0x1f682, 0x1e4a0, 0x1f258, 0x1f92e, 0x1eda0, + 0x1e490, 0x1fb6e, 0x1ed90, 0x1f6cc, 0x1f246, 0x1ed88, 0x1e484, + 0x1ed84, 0x1e482, 0x1ed82, 0x1c9a0, 0x1e4d8, 0x1f26e, 0x1dba0, + 0x1c990, 0x1e4cc, 0x1db90, 0x1edcc, 0x1e4c6, 0x1db88, 0x1c984, + 0x1db84, 0x1c982, 0x1db82, 0x193a0, 0x1c9d8, 0x1e4ee, 0x1b7a0, + 0x19390, 0x1c9cc, 0x1b790, 0x1dbcc, 0x1c9c6, 0x1b788, 0x19384, + 0x1b784, 0x19382, 0x1b782, 0x127a0, 0x193d8, 0x1c9ee, 0x16fa0, + 0x12790, 0x193cc, 0x16f90, 0x1b7cc, 0x193c6, 0x16f88, 0x12784, + 0x16f84, 0x12782, 0x127d8, 0x193ee, 0x16fd8, 0x127cc, 0x16fcc, + 0x127c6, 0x16fc6, 0x127ee, 0x1f650, 0x1fb2c, 0x165f8, 0x1f648, + 0x1fb26, 0x164fc, 0x1f644, 0x1647e, 0x1f642, 0x1e450, 0x1f22c, + 0x1ecd0, 0x1e448, 0x1f226, 0x1ecc8, 0x1f666, 0x1ecc4, 0x1e442, + 0x1ecc2, 0x1c8d0, 0x1e46c, 0x1d9d0, 0x1c8c8, 0x1e466, 0x1d9c8, + 0x1ece6, 0x1d9c4, 0x1c8c2, 0x1d9c2, 0x191d0, 0x1c8ec, 0x1b3d0, + 0x191c8, 0x1c8e6, 0x1b3c8, 0x1d9e6, 0x1b3c4, 0x191c2, 0x1b3c2, + 0x123d0, 0x191ec, 0x167d0, 0x123c8, 0x191e6, 0x167c8, 0x1b3e6, + 0x167c4, 0x123c2, 0x167c2, 0x123ec, 0x167ec, 0x123e6, 0x167e6, + 0x1f628, 0x1fb16, 0x162fc, 0x1f624, 0x1627e, 0x1f622, 0x1e428, + 0x1f216, 0x1ec68, 0x1f636, 0x1ec64, 0x1e422, 0x1ec62, 0x1c868, + 0x1e436, 0x1d8e8, 0x1c864, 0x1d8e4, 0x1c862, 0x1d8e2, 0x190e8, + 0x1c876, 0x1b1e8, 0x1d8f6, 0x1b1e4, 0x190e2, 0x1b1e2, 0x121e8, + 0x190f6, 0x163e8, 0x121e4, 0x163e4, 0x121e2, 0x163e2, 0x121f6, + 0x163f6, 0x1f614, 0x1617e, 0x1f612, 0x1e414, 0x1ec34, 0x1e412, + 0x1ec32, 0x1c834, 0x1d874, 0x1c832, 0x1d872, 0x19074, 0x1b0f4, + 0x19072, 0x1b0f2, 0x120f4, 0x161f4, 0x120f2, 0x161f2, 0x1f60a, + 0x1e40a, 0x1ec1a, 0x1c81a, 0x1d83a, 0x1903a, 0x1b07a, 0x1e2a0, + 0x1f158, 0x1f8ae, 0x1e290, 0x1f14c, 0x1e288, 0x1f146, 0x1e284, + 0x1e282, 0x1c5a0, 0x1e2d8, 0x1f16e, 0x1c590, 0x1e2cc, 0x1c588, + 0x1e2c6, 0x1c584, 0x1c582, 0x18ba0, 0x1c5d8, 0x1e2ee, 0x18b90, + 0x1c5cc, 0x18b88, 0x1c5c6, 0x18b84, 0x18b82, 0x117a0, 0x18bd8, + 0x1c5ee, 0x11790, 0x18bcc, 0x11788, 0x18bc6, 0x11784, 0x11782, + 0x117d8, 0x18bee, 0x117cc, 0x117c6, 0x117ee, 0x1f350, 0x1f9ac, + 0x135f8, 0x1f348, 0x1f9a6, 0x134fc, 0x1f344, 0x1347e, 0x1f342, + 0x1e250, 0x1f12c, 0x1e6d0, 0x1e248, 0x1f126, 0x1e6c8, 0x1f366, + 0x1e6c4, 0x1e242, 0x1e6c2, 0x1c4d0, 0x1e26c, 0x1cdd0, 0x1c4c8, + 0x1e266, 0x1cdc8, 0x1e6e6, 0x1cdc4, 0x1c4c2, 0x1cdc2, 0x189d0, + 0x1c4ec, 0x19bd0, 0x189c8, 0x1c4e6, 0x19bc8, 0x1cde6, 0x19bc4, + 0x189c2, 0x19bc2, 0x113d0, 0x189ec, 0x137d0, 0x113c8, 0x189e6, + 0x137c8, 0x19be6, 0x137c4, 0x113c2, 0x137c2, 0x113ec, 0x137ec, + 0x113e6, 0x137e6, 0x1fba8, 0x175f0, 0x1bafc, 0x1fba4, 0x174f8, + 0x1ba7e, 0x1fba2, 0x1747c, 0x1743e, 0x1f328, 0x1f996, 0x132fc, + 0x1f768, 0x1fbb6, 0x176fc, 0x1327e, 0x1f764, 0x1f322, 0x1767e, + 0x1f762, 0x1e228, 0x1f116, 0x1e668, 0x1e224, 0x1eee8, 0x1f776, + 0x1e222, 0x1eee4, 0x1e662, 0x1eee2, 0x1c468, 0x1e236, 0x1cce8, + 0x1c464, 0x1dde8, 0x1cce4, 0x1c462, 0x1dde4, 0x1cce2, 0x1dde2, + 0x188e8, 0x1c476, 0x199e8, 0x188e4, 0x1bbe8, 0x199e4, 0x188e2, + 0x1bbe4, 0x199e2, 0x1bbe2, 0x111e8, 0x188f6, 0x133e8, 0x111e4, + 0x177e8, 0x133e4, 0x111e2, 0x177e4, 0x133e2, 0x177e2, 0x111f6, + 0x133f6, 0x1fb94, 0x172f8, 0x1b97e, 0x1fb92, 0x1727c, 0x1723e, + 0x1f314, 0x1317e, 0x1f734, 0x1f312, 0x1737e, 0x1f732, 0x1e214, + 0x1e634, 0x1e212, 0x1ee74, 0x1e632, 0x1ee72, 0x1c434, 0x1cc74, + 0x1c432, 0x1dcf4, 0x1cc72, 0x1dcf2, 0x18874, 0x198f4, 0x18872, + 0x1b9f4, 0x198f2, 0x1b9f2, 0x110f4, 0x131f4, 0x110f2, 0x173f4, + 0x131f2, 0x173f2, 0x1fb8a, 0x1717c, 0x1713e, 0x1f30a, 0x1f71a, + 0x1e20a, 0x1e61a, 0x1ee3a, 0x1c41a, 0x1cc3a, 0x1dc7a, 0x1883a, + 0x1987a, 0x1b8fa, 0x1107a, 0x130fa, 0x171fa, 0x170be, 0x1e150, + 0x1f0ac, 0x1e148, 0x1f0a6, 0x1e144, 0x1e142, 0x1c2d0, 0x1e16c, + 0x1c2c8, 0x1e166, 0x1c2c4, 0x1c2c2, 0x185d0, 0x1c2ec, 0x185c8, + 0x1c2e6, 0x185c4, 0x185c2, 0x10bd0, 0x185ec, 0x10bc8, 0x185e6, + 0x10bc4, 0x10bc2, 0x10bec, 0x10be6, 0x1f1a8, 0x1f8d6, 0x11afc, + 0x1f1a4, 0x11a7e, 0x1f1a2, 0x1e128, 0x1f096, 0x1e368, 0x1e124, + 0x1e364, 0x1e122, 0x1e362, 0x1c268, 0x1e136, 0x1c6e8, 0x1c264, + 0x1c6e4, 0x1c262, 0x1c6e2, 0x184e8, 0x1c276, 0x18de8, 0x184e4, + 0x18de4, 0x184e2, 0x18de2, 0x109e8, 0x184f6, 0x11be8, 0x109e4, + 0x11be4, 0x109e2, 0x11be2, 0x109f6, 0x11bf6, 0x1f9d4, 0x13af8, + 0x19d7e, 0x1f9d2, 0x13a7c, 0x13a3e, 0x1f194, 0x1197e, 0x1f3b4, + 0x1f192, 0x13b7e, 0x1f3b2, 0x1e114, 0x1e334, 0x1e112, 0x1e774, + 0x1e332, 0x1e772, 0x1c234, 0x1c674, 0x1c232, 0x1cef4, 0x1c672, + 0x1cef2, 0x18474, 0x18cf4, 0x18472, 0x19df4, 0x18cf2, 0x19df2, + 0x108f4, 0x119f4, 0x108f2, 0x13bf4, 0x119f2, 0x13bf2, 0x17af0, + 0x1bd7c, 0x17a78, 0x1bd3e, 0x17a3c, 0x17a1e, 0x1f9ca, 0x1397c, + 0x1fbda, 0x17b7c, 0x1393e, 0x17b3e, 0x1f18a, 0x1f39a, 0x1f7ba, + 0x1e10a, 0x1e31a, 0x1e73a, 0x1ef7a, 0x1c21a, 0x1c63a, 0x1ce7a, + 0x1defa, 0x1843a, 0x18c7a, 0x19cfa, 0x1bdfa, 0x1087a, 0x118fa, + 0x139fa, 0x17978, 0x1bcbe, 0x1793c, 0x1791e, 0x138be, 0x179be, + 0x178bc, 0x1789e, 0x1785e, 0x1e0a8, 0x1e0a4, 0x1e0a2, 0x1c168, + 0x1e0b6, 0x1c164, 0x1c162, 0x182e8, 0x1c176, 0x182e4, 0x182e2, + 0x105e8, 0x182f6, 0x105e4, 0x105e2, 0x105f6, 0x1f0d4, 0x10d7e, + 0x1f0d2, 0x1e094, 0x1e1b4, 0x1e092, 0x1e1b2, 0x1c134, 0x1c374, + 0x1c132, 0x1c372, 0x18274, 0x186f4, 0x18272, 0x186f2, 0x104f4, + 0x10df4, 0x104f2, 0x10df2, 0x1f8ea, 0x11d7c, 0x11d3e, 0x1f0ca, + 0x1f1da, 0x1e08a, 0x1e19a, 0x1e3ba, 0x1c11a, 0x1c33a, 0x1c77a, + 0x1823a, 0x1867a, 0x18efa, 0x1047a, 0x10cfa, 0x11dfa, 0x13d78, + 0x19ebe, 0x13d3c, 0x13d1e, 0x11cbe, 0x13dbe, 0x17d70, 0x1bebc, + 0x17d38, 0x1be9e, 0x17d1c, 0x17d0e, 0x13cbc, 0x17dbc, 0x13c9e, + 0x17d9e, 0x17cb8, 0x1be5e, 0x17c9c, 0x17c8e, 0x13c5e, 0x17cde, + 0x17c5c, 0x17c4e, 0x17c2e, 0x1c0b4, 0x1c0b2, 0x18174, 0x18172, + 0x102f4, 0x102f2, 0x1e0da, 0x1c09a, 0x1c1ba, 0x1813a, 0x1837a, + 0x1027a, 0x106fa, 0x10ebe, 0x11ebc, 0x11e9e, 0x13eb8, 0x19f5e, + 0x13e9c, 0x13e8e, 0x11e5e, 0x13ede, 0x17eb0, 0x1bf5c, 0x17e98, + 0x1bf4e, 0x17e8c, 0x17e86, 0x13e5c, 0x17edc, 0x13e4e, 0x17ece, + 0x17e58, 0x1bf2e, 0x17e4c, 0x17e46, 0x13e2e, 0x17e6e, 0x17e2c, + 0x17e26, 0x10f5e, 0x11f5c, 0x11f4e, 0x13f58, 0x19fae, 0x13f4c, + 0x13f46, 0x11f2e, 0x13f6e, 0x13f2c, 0x13f26, + }, + []int{ + 0x1abe0, 0x1d5f8, 0x153c0, 0x1a9f0, 0x1d4fc, 0x151e0, 0x1a8f8, + 0x1d47e, 0x150f0, 0x1a87c, 0x15078, 0x1fad0, 0x15be0, 0x1adf8, + 0x1fac8, 0x159f0, 0x1acfc, 0x1fac4, 0x158f8, 0x1ac7e, 0x1fac2, + 0x1587c, 0x1f5d0, 0x1faec, 0x15df8, 0x1f5c8, 0x1fae6, 0x15cfc, + 0x1f5c4, 0x15c7e, 0x1f5c2, 0x1ebd0, 0x1f5ec, 0x1ebc8, 0x1f5e6, + 0x1ebc4, 0x1ebc2, 0x1d7d0, 0x1ebec, 0x1d7c8, 0x1ebe6, 0x1d7c4, + 0x1d7c2, 0x1afd0, 0x1d7ec, 0x1afc8, 0x1d7e6, 0x1afc4, 0x14bc0, + 0x1a5f0, 0x1d2fc, 0x149e0, 0x1a4f8, 0x1d27e, 0x148f0, 0x1a47c, + 0x14878, 0x1a43e, 0x1483c, 0x1fa68, 0x14df0, 0x1a6fc, 0x1fa64, + 0x14cf8, 0x1a67e, 0x1fa62, 0x14c7c, 0x14c3e, 0x1f4e8, 0x1fa76, + 0x14efc, 0x1f4e4, 0x14e7e, 0x1f4e2, 0x1e9e8, 0x1f4f6, 0x1e9e4, + 0x1e9e2, 0x1d3e8, 0x1e9f6, 0x1d3e4, 0x1d3e2, 0x1a7e8, 0x1d3f6, + 0x1a7e4, 0x1a7e2, 0x145e0, 0x1a2f8, 0x1d17e, 0x144f0, 0x1a27c, + 0x14478, 0x1a23e, 0x1443c, 0x1441e, 0x1fa34, 0x146f8, 0x1a37e, + 0x1fa32, 0x1467c, 0x1463e, 0x1f474, 0x1477e, 0x1f472, 0x1e8f4, + 0x1e8f2, 0x1d1f4, 0x1d1f2, 0x1a3f4, 0x1a3f2, 0x142f0, 0x1a17c, + 0x14278, 0x1a13e, 0x1423c, 0x1421e, 0x1fa1a, 0x1437c, 0x1433e, + 0x1f43a, 0x1e87a, 0x1d0fa, 0x14178, 0x1a0be, 0x1413c, 0x1411e, + 0x141be, 0x140bc, 0x1409e, 0x12bc0, 0x195f0, 0x1cafc, 0x129e0, + 0x194f8, 0x1ca7e, 0x128f0, 0x1947c, 0x12878, 0x1943e, 0x1283c, + 0x1f968, 0x12df0, 0x196fc, 0x1f964, 0x12cf8, 0x1967e, 0x1f962, + 0x12c7c, 0x12c3e, 0x1f2e8, 0x1f976, 0x12efc, 0x1f2e4, 0x12e7e, + 0x1f2e2, 0x1e5e8, 0x1f2f6, 0x1e5e4, 0x1e5e2, 0x1cbe8, 0x1e5f6, + 0x1cbe4, 0x1cbe2, 0x197e8, 0x1cbf6, 0x197e4, 0x197e2, 0x1b5e0, + 0x1daf8, 0x1ed7e, 0x169c0, 0x1b4f0, 0x1da7c, 0x168e0, 0x1b478, + 0x1da3e, 0x16870, 0x1b43c, 0x16838, 0x1b41e, 0x1681c, 0x125e0, + 0x192f8, 0x1c97e, 0x16de0, 0x124f0, 0x1927c, 0x16cf0, 0x1b67c, + 0x1923e, 0x16c78, 0x1243c, 0x16c3c, 0x1241e, 0x16c1e, 0x1f934, + 0x126f8, 0x1937e, 0x1fb74, 0x1f932, 0x16ef8, 0x1267c, 0x1fb72, + 0x16e7c, 0x1263e, 0x16e3e, 0x1f274, 0x1277e, 0x1f6f4, 0x1f272, + 0x16f7e, 0x1f6f2, 0x1e4f4, 0x1edf4, 0x1e4f2, 0x1edf2, 0x1c9f4, + 0x1dbf4, 0x1c9f2, 0x1dbf2, 0x193f4, 0x193f2, 0x165c0, 0x1b2f0, + 0x1d97c, 0x164e0, 0x1b278, 0x1d93e, 0x16470, 0x1b23c, 0x16438, + 0x1b21e, 0x1641c, 0x1640e, 0x122f0, 0x1917c, 0x166f0, 0x12278, + 0x1913e, 0x16678, 0x1b33e, 0x1663c, 0x1221e, 0x1661e, 0x1f91a, + 0x1237c, 0x1fb3a, 0x1677c, 0x1233e, 0x1673e, 0x1f23a, 0x1f67a, + 0x1e47a, 0x1ecfa, 0x1c8fa, 0x1d9fa, 0x191fa, 0x162e0, 0x1b178, + 0x1d8be, 0x16270, 0x1b13c, 0x16238, 0x1b11e, 0x1621c, 0x1620e, + 0x12178, 0x190be, 0x16378, 0x1213c, 0x1633c, 0x1211e, 0x1631e, + 0x121be, 0x163be, 0x16170, 0x1b0bc, 0x16138, 0x1b09e, 0x1611c, + 0x1610e, 0x120bc, 0x161bc, 0x1209e, 0x1619e, 0x160b8, 0x1b05e, + 0x1609c, 0x1608e, 0x1205e, 0x160de, 0x1605c, 0x1604e, 0x115e0, + 0x18af8, 0x1c57e, 0x114f0, 0x18a7c, 0x11478, 0x18a3e, 0x1143c, + 0x1141e, 0x1f8b4, 0x116f8, 0x18b7e, 0x1f8b2, 0x1167c, 0x1163e, + 0x1f174, 0x1177e, 0x1f172, 0x1e2f4, 0x1e2f2, 0x1c5f4, 0x1c5f2, + 0x18bf4, 0x18bf2, 0x135c0, 0x19af0, 0x1cd7c, 0x134e0, 0x19a78, + 0x1cd3e, 0x13470, 0x19a3c, 0x13438, 0x19a1e, 0x1341c, 0x1340e, + 0x112f0, 0x1897c, 0x136f0, 0x11278, 0x1893e, 0x13678, 0x19b3e, + 0x1363c, 0x1121e, 0x1361e, 0x1f89a, 0x1137c, 0x1f9ba, 0x1377c, + 0x1133e, 0x1373e, 0x1f13a, 0x1f37a, 0x1e27a, 0x1e6fa, 0x1c4fa, + 0x1cdfa, 0x189fa, 0x1bae0, 0x1dd78, 0x1eebe, 0x174c0, 0x1ba70, + 0x1dd3c, 0x17460, 0x1ba38, 0x1dd1e, 0x17430, 0x1ba1c, 0x17418, + 0x1ba0e, 0x1740c, 0x132e0, 0x19978, 0x1ccbe, 0x176e0, 0x13270, + 0x1993c, 0x17670, 0x1bb3c, 0x1991e, 0x17638, 0x1321c, 0x1761c, + 0x1320e, 0x1760e, 0x11178, 0x188be, 0x13378, 0x1113c, 0x17778, + 0x1333c, 0x1111e, 0x1773c, 0x1331e, 0x1771e, 0x111be, 0x133be, + 0x177be, 0x172c0, 0x1b970, 0x1dcbc, 0x17260, 0x1b938, 0x1dc9e, + 0x17230, 0x1b91c, 0x17218, 0x1b90e, 0x1720c, 0x17206, 0x13170, + 0x198bc, 0x17370, 0x13138, 0x1989e, 0x17338, 0x1b99e, 0x1731c, + 0x1310e, 0x1730e, 0x110bc, 0x131bc, 0x1109e, 0x173bc, 0x1319e, + 0x1739e, 0x17160, 0x1b8b8, 0x1dc5e, 0x17130, 0x1b89c, 0x17118, + 0x1b88e, 0x1710c, 0x17106, 0x130b8, 0x1985e, 0x171b8, 0x1309c, + 0x1719c, 0x1308e, 0x1718e, 0x1105e, 0x130de, 0x171de, 0x170b0, + 0x1b85c, 0x17098, 0x1b84e, 0x1708c, 0x17086, 0x1305c, 0x170dc, + 0x1304e, 0x170ce, 0x17058, 0x1b82e, 0x1704c, 0x17046, 0x1302e, + 0x1706e, 0x1702c, 0x17026, 0x10af0, 0x1857c, 0x10a78, 0x1853e, + 0x10a3c, 0x10a1e, 0x10b7c, 0x10b3e, 0x1f0ba, 0x1e17a, 0x1c2fa, + 0x185fa, 0x11ae0, 0x18d78, 0x1c6be, 0x11a70, 0x18d3c, 0x11a38, + 0x18d1e, 0x11a1c, 0x11a0e, 0x10978, 0x184be, 0x11b78, 0x1093c, + 0x11b3c, 0x1091e, 0x11b1e, 0x109be, 0x11bbe, 0x13ac0, 0x19d70, + 0x1cebc, 0x13a60, 0x19d38, 0x1ce9e, 0x13a30, 0x19d1c, 0x13a18, + 0x19d0e, 0x13a0c, 0x13a06, 0x11970, 0x18cbc, 0x13b70, 0x11938, + 0x18c9e, 0x13b38, 0x1191c, 0x13b1c, 0x1190e, 0x13b0e, 0x108bc, + 0x119bc, 0x1089e, 0x13bbc, 0x1199e, 0x13b9e, 0x1bd60, 0x1deb8, + 0x1ef5e, 0x17a40, 0x1bd30, 0x1de9c, 0x17a20, 0x1bd18, 0x1de8e, + 0x17a10, 0x1bd0c, 0x17a08, 0x1bd06, 0x17a04, 0x13960, 0x19cb8, + 0x1ce5e, 0x17b60, 0x13930, 0x19c9c, 0x17b30, 0x1bd9c, 0x19c8e, + 0x17b18, 0x1390c, 0x17b0c, 0x13906, 0x17b06, 0x118b8, 0x18c5e, + 0x139b8, 0x1189c, 0x17bb8, 0x1399c, 0x1188e, 0x17b9c, 0x1398e, + 0x17b8e, 0x1085e, 0x118de, 0x139de, 0x17bde, 0x17940, 0x1bcb0, + 0x1de5c, 0x17920, 0x1bc98, 0x1de4e, 0x17910, 0x1bc8c, 0x17908, + 0x1bc86, 0x17904, 0x17902, 0x138b0, 0x19c5c, 0x179b0, 0x13898, + 0x19c4e, 0x17998, 0x1bcce, 0x1798c, 0x13886, 0x17986, 0x1185c, + 0x138dc, 0x1184e, 0x179dc, 0x138ce, 0x179ce, 0x178a0, 0x1bc58, + 0x1de2e, 0x17890, 0x1bc4c, 0x17888, 0x1bc46, 0x17884, 0x17882, + 0x13858, 0x19c2e, 0x178d8, 0x1384c, 0x178cc, 0x13846, 0x178c6, + 0x1182e, 0x1386e, 0x178ee, 0x17850, 0x1bc2c, 0x17848, 0x1bc26, + 0x17844, 0x17842, 0x1382c, 0x1786c, 0x13826, 0x17866, 0x17828, + 0x1bc16, 0x17824, 0x17822, 0x13816, 0x17836, 0x10578, 0x182be, + 0x1053c, 0x1051e, 0x105be, 0x10d70, 0x186bc, 0x10d38, 0x1869e, + 0x10d1c, 0x10d0e, 0x104bc, 0x10dbc, 0x1049e, 0x10d9e, 0x11d60, + 0x18eb8, 0x1c75e, 0x11d30, 0x18e9c, 0x11d18, 0x18e8e, 0x11d0c, + 0x11d06, 0x10cb8, 0x1865e, 0x11db8, 0x10c9c, 0x11d9c, 0x10c8e, + 0x11d8e, 0x1045e, 0x10cde, 0x11dde, 0x13d40, 0x19eb0, 0x1cf5c, + 0x13d20, 0x19e98, 0x1cf4e, 0x13d10, 0x19e8c, 0x13d08, 0x19e86, + 0x13d04, 0x13d02, 0x11cb0, 0x18e5c, 0x13db0, 0x11c98, 0x18e4e, + 0x13d98, 0x19ece, 0x13d8c, 0x11c86, 0x13d86, 0x10c5c, 0x11cdc, + 0x10c4e, 0x13ddc, 0x11cce, 0x13dce, 0x1bea0, 0x1df58, 0x1efae, + 0x1be90, 0x1df4c, 0x1be88, 0x1df46, 0x1be84, 0x1be82, 0x13ca0, + 0x19e58, 0x1cf2e, 0x17da0, 0x13c90, 0x19e4c, 0x17d90, 0x1becc, + 0x19e46, 0x17d88, 0x13c84, 0x17d84, 0x13c82, 0x17d82, 0x11c58, + 0x18e2e, 0x13cd8, 0x11c4c, 0x17dd8, 0x13ccc, 0x11c46, 0x17dcc, + 0x13cc6, 0x17dc6, 0x10c2e, 0x11c6e, 0x13cee, 0x17dee, 0x1be50, + 0x1df2c, 0x1be48, 0x1df26, 0x1be44, 0x1be42, 0x13c50, 0x19e2c, + 0x17cd0, 0x13c48, 0x19e26, 0x17cc8, 0x1be66, 0x17cc4, 0x13c42, + 0x17cc2, 0x11c2c, 0x13c6c, 0x11c26, 0x17cec, 0x13c66, 0x17ce6, + 0x1be28, 0x1df16, 0x1be24, 0x1be22, 0x13c28, 0x19e16, 0x17c68, + 0x13c24, 0x17c64, 0x13c22, 0x17c62, 0x11c16, 0x13c36, 0x17c76, + 0x1be14, 0x1be12, 0x13c14, 0x17c34, 0x13c12, 0x17c32, 0x102bc, + 0x1029e, 0x106b8, 0x1835e, 0x1069c, 0x1068e, 0x1025e, 0x106de, + 0x10eb0, 0x1875c, 0x10e98, 0x1874e, 0x10e8c, 0x10e86, 0x1065c, + 0x10edc, 0x1064e, 0x10ece, 0x11ea0, 0x18f58, 0x1c7ae, 0x11e90, + 0x18f4c, 0x11e88, 0x18f46, 0x11e84, 0x11e82, 0x10e58, 0x1872e, + 0x11ed8, 0x18f6e, 0x11ecc, 0x10e46, 0x11ec6, 0x1062e, 0x10e6e, + 0x11eee, 0x19f50, 0x1cfac, 0x19f48, 0x1cfa6, 0x19f44, 0x19f42, + 0x11e50, 0x18f2c, 0x13ed0, 0x19f6c, 0x18f26, 0x13ec8, 0x11e44, + 0x13ec4, 0x11e42, 0x13ec2, 0x10e2c, 0x11e6c, 0x10e26, 0x13eec, + 0x11e66, 0x13ee6, 0x1dfa8, 0x1efd6, 0x1dfa4, 0x1dfa2, 0x19f28, + 0x1cf96, 0x1bf68, 0x19f24, 0x1bf64, 0x19f22, 0x1bf62, 0x11e28, + 0x18f16, 0x13e68, 0x11e24, 0x17ee8, 0x13e64, 0x11e22, 0x17ee4, + 0x13e62, 0x17ee2, 0x10e16, 0x11e36, 0x13e76, 0x17ef6, 0x1df94, + 0x1df92, 0x19f14, 0x1bf34, 0x19f12, 0x1bf32, 0x11e14, 0x13e34, + 0x11e12, 0x17e74, 0x13e32, 0x17e72, 0x1df8a, 0x19f0a, 0x1bf1a, + 0x11e0a, 0x13e1a, 0x17e3a, 0x1035c, 0x1034e, 0x10758, 0x183ae, + 0x1074c, 0x10746, 0x1032e, 0x1076e, 0x10f50, 0x187ac, 0x10f48, + 0x187a6, 0x10f44, 0x10f42, 0x1072c, 0x10f6c, 0x10726, 0x10f66, + 0x18fa8, 0x1c7d6, 0x18fa4, 0x18fa2, 0x10f28, 0x18796, 0x11f68, + 0x18fb6, 0x11f64, 0x10f22, 0x11f62, 0x10716, 0x10f36, 0x11f76, + 0x1cfd4, 0x1cfd2, 0x18f94, 0x19fb4, 0x18f92, 0x19fb2, 0x10f14, + 0x11f34, 0x10f12, 0x13f74, 0x11f32, 0x13f72, 0x1cfca, 0x18f8a, + 0x19f9a, 0x10f0a, 0x11f1a, 0x13f3a, 0x103ac, 0x103a6, 0x107a8, + 0x183d6, 0x107a4, 0x107a2, 0x10396, 0x107b6, 0x187d4, 0x187d2, + 0x10794, 0x10fb4, 0x10792, 0x10fb2, 0x1c7ea, + }, +} + +func getCode(tableId int, word int) int { + return codes[tableId][word] +} + +func (barcode *Barcode) PixelGrid() [][]bool { + if len(barcode.pixelGrid) != 0 { + return barcode.pixelGrid + } + + barcode.pixelGrid = [][]bool{} + + for _, row := range barcode.Codes { + pixelRow := []bool{} + for _, value := range row { + bin := strconv.FormatInt(int64(value), 2) + length := len(bin) + + for i := 0; i < length; i++ { + pixelRow = append(pixelRow, bin[i] == 49) + } + } + + barcode.pixelGrid = append(barcode.pixelGrid, pixelRow) + } + + return barcode.pixelGrid +} diff --git a/vendor/github.com/ruudk/golang-pdf417/reed_solomon.go b/vendor/github.com/ruudk/golang-pdf417/reed_solomon.go new file mode 100644 index 00000000..31189165 --- /dev/null +++ b/vendor/github.com/ruudk/golang-pdf417/reed_solomon.go @@ -0,0 +1,164 @@ +package pdf417 + +import ( + "math" +) + +var correctionFactors = [][]int{ + // Level 0 + []int{27, 917}, + + // Level 1 + []int{522, 568, 723, 809}, + + // Level 2 + []int{237, 308, 436, 284, 646, 653, 428, 379}, + + // Level 3 + []int{ + 274, 562, 232, 755, 599, 524, 801, 132, 295, 116, 442, 428, 295, 42, + 176, 65, + }, + + // Level 4 + []int{ + 361, 575, 922, 525, 176, 586, 640, 321, 536, 742, 677, 742, 687, + 284, 193, 517, 273, 494, 263, 147, 593, 800, 571, 320, 803, 133, + 231, 390, 685, 330, 63, 410, + }, + + // Level 5 + []int{ + 539, 422, 6, 93, 862, 771, 453, 106, 610, 287, 107, 505, 733, 877, + 381, 612, 723, 476, 462, 172, 430, 609, 858, 822, 543, 376, 511, + 400, 672, 762, 283, 184, 440, 35, 519, 31, 460, 594, 225, 535, 517, + 352, 605, 158, 651, 201, 488, 502, 648, 733, 717, 83, 404, 97, 280, + 771, 840, 629, 4, 381, 843, 623, 264, 543, + }, + + // Level 6 + []int{ + 521, 310, 864, 547, 858, 580, 296, 379, 53, 779, 897, 444, 400, 925, + 749, 415, 822, 93, 217, 208, 928, 244, 583, 620, 246, 148, 447, 631, + 292, 908, 490, 704, 516, 258, 457, 907, 594, 723, 674, 292, 272, 96, + 684, 432, 686, 606, 860, 569, 193, 219, 129, 186, 236, 287, 192, + 775, 278, 173, 40, 379, 712, 463, 646, 776, 171, 491, 297, 763, 156, + 732, 95, 270, 447, 90, 507, 48, 228, 821, 808, 898, 784, 663, 627, + 378, 382, 262, 380, 602, 754, 336, 89, 614, 87, 432, 670, 616, 157, + 374, 242, 726, 600, 269, 375, 898, 845, 454, 354, 130, 814, 587, + 804, 34, 211, 330, 539, 297, 827, 865, 37, 517, 834, 315, 550, 86, + 801, 4, 108, 539, + }, + + // Level 7 + []int{ + 524, 894, 75, 766, 882, 857, 74, 204, 82, 586, 708, 250, 905, 786, + 138, 720, 858, 194, 311, 913, 275, 190, 375, 850, 438, 733, 194, + 280, 201, 280, 828, 757, 710, 814, 919, 89, 68, 569, 11, 204, 796, + 605, 540, 913, 801, 700, 799, 137, 439, 418, 592, 668, 353, 859, + 370, 694, 325, 240, 216, 257, 284, 549, 209, 884, 315, 70, 329, 793, + 490, 274, 877, 162, 749, 812, 684, 461, 334, 376, 849, 521, 307, + 291, 803, 712, 19, 358, 399, 908, 103, 511, 51, 8, 517, 225, 289, + 470, 637, 731, 66, 255, 917, 269, 463, 830, 730, 433, 848, 585, 136, + 538, 906, 90, 2, 290, 743, 199, 655, 903, 329, 49, 802, 580, 355, + 588, 188, 462, 10, 134, 628, 320, 479, 130, 739, 71, 263, 318, 374, + 601, 192, 605, 142, 673, 687, 234, 722, 384, 177, 752, 607, 640, + 455, 193, 689, 707, 805, 641, 48, 60, 732, 621, 895, 544, 261, 852, + 655, 309, 697, 755, 756, 60, 231, 773, 434, 421, 726, 528, 503, 118, + 49, 795, 32, 144, 500, 238, 836, 394, 280, 566, 319, 9, 647, 550, + 73, 914, 342, 126, 32, 681, 331, 792, 620, 60, 609, 441, 180, 791, + 893, 754, 605, 383, 228, 749, 760, 213, 54, 297, 134, 54, 834, 299, + 922, 191, 910, 532, 609, 829, 189, 20, 167, 29, 872, 449, 83, 402, + 41, 656, 505, 579, 481, 173, 404, 251, 688, 95, 497, 555, 642, 543, + 307, 159, 924, 558, 648, 55, 497, 10, + }, + + // Level 8 + []int{ + 352, 77, 373, 504, 35, 599, 428, 207, 409, 574, 118, 498, 285, 380, + 350, 492, 197, 265, 920, 155, 914, 299, 229, 643, 294, 871, 306, 88, + 87, 193, 352, 781, 846, 75, 327, 520, 435, 543, 203, 666, 249, 346, + 781, 621, 640, 268, 794, 534, 539, 781, 408, 390, 644, 102, 476, + 499, 290, 632, 545, 37, 858, 916, 552, 41, 542, 289, 122, 272, 383, + 800, 485, 98, 752, 472, 761, 107, 784, 860, 658, 741, 290, 204, 681, + 407, 855, 85, 99, 62, 482, 180, 20, 297, 451, 593, 913, 142, 808, + 684, 287, 536, 561, 76, 653, 899, 729, 567, 744, 390, 513, 192, 516, + 258, 240, 518, 794, 395, 768, 848, 51, 610, 384, 168, 190, 826, 328, + 596, 786, 303, 570, 381, 415, 641, 156, 237, 151, 429, 531, 207, + 676, 710, 89, 168, 304, 402, 40, 708, 575, 162, 864, 229, 65, 861, + 841, 512, 164, 477, 221, 92, 358, 785, 288, 357, 850, 836, 827, 736, + 707, 94, 8, 494, 114, 521, 2, 499, 851, 543, 152, 729, 771, 95, 248, + 361, 578, 323, 856, 797, 289, 51, 684, 466, 533, 820, 669, 45, 902, + 452, 167, 342, 244, 173, 35, 463, 651, 51, 699, 591, 452, 578, 37, + 124, 298, 332, 552, 43, 427, 119, 662, 777, 475, 850, 764, 364, 578, + 911, 283, 711, 472, 420, 245, 288, 594, 394, 511, 327, 589, 777, + 699, 688, 43, 408, 842, 383, 721, 521, 560, 644, 714, 559, 62, 145, + 873, 663, 713, 159, 672, 729, 624, 59, 193, 417, 158, 209, 563, 564, + 343, 693, 109, 608, 563, 365, 181, 772, 677, 310, 248, 353, 708, + 410, 579, 870, 617, 841, 632, 860, 289, 536, 35, 777, 618, 586, 424, + 833, 77, 597, 346, 269, 757, 632, 695, 751, 331, 247, 184, 45, 787, + 680, 18, 66, 407, 369, 54, 492, 228, 613, 830, 922, 437, 519, 644, + 905, 789, 420, 305, 441, 207, 300, 892, 827, 141, 537, 381, 662, + 513, 56, 252, 341, 242, 797, 838, 837, 720, 224, 307, 631, 61, 87, + 560, 310, 756, 665, 397, 808, 851, 309, 473, 795, 378, 31, 647, 915, + 459, 806, 590, 731, 425, 216, 548, 249, 321, 881, 699, 535, 673, + 782, 210, 815, 905, 303, 843, 922, 281, 73, 469, 791, 660, 162, 498, + 308, 155, 422, 907, 817, 187, 62, 16, 425, 535, 336, 286, 437, 375, + 273, 610, 296, 183, 923, 116, 667, 751, 353, 62, 366, 691, 379, 687, + 842, 37, 357, 720, 742, 330, 5, 39, 923, 311, 424, 242, 749, 321, + 54, 669, 316, 342, 299, 534, 105, 667, 488, 640, 672, 576, 540, 316, + 486, 721, 610, 46, 656, 447, 171, 616, 464, 190, 531, 297, 321, 762, + 752, 533, 175, 134, 14, 381, 433, 717, 45, 111, 20, 596, 284, 736, + 138, 646, 411, 877, 669, 141, 919, 45, 780, 407, 164, 332, 899, 165, + 726, 600, 325, 498, 655, 357, 752, 768, 223, 849, 647, 63, 310, 863, + 251, 366, 304, 282, 738, 675, 410, 389, 244, 31, 121, 303, 263, + }, +} + +func ComputeReedSolomon(data []int, level int) []int { + // Correction factors for the given level + factors := correctionFactors[level] + + // Number of correction code words + count := int(math.Pow(2, float64(level + 1))) + + // Correction code words array, prepopulated with zeros + ecWords := make([]int, count) + + // Index of the last correction code word + last := count - 1 + + for _, value := range data { + temp := (value + ecWords[last]) % 929; + + for i := last; i >= 0; i-- { + add := 0 + + y := i - 1 + if y >= 0 && y <= len(ecWords) { + add = ecWords[y] + } + + ecWords[i] = (add + 929 - (temp * factors[i]) % 929) % 929; + } + } + + for key, word := range ecWords { + if word > 0 { + ecWords[key] = 929 - word + } + } + + return reverse(ecWords) + +} + +func reverse(input []int) []int { + output := []int{} + + for i := len(input) - 1; i >= 0; i-- { + output = append(output, input[i]) + } + + return output +} diff --git a/vendor/github.com/ruudk/golang-pdf417/text_encoder.go b/vendor/github.com/ruudk/golang-pdf417/text_encoder.go new file mode 100644 index 00000000..2ac97910 --- /dev/null +++ b/vendor/github.com/ruudk/golang-pdf417/text_encoder.go @@ -0,0 +1,261 @@ +package pdf417 + +// Code word used to switch to Text mode. +const TEXT_SWITCH_CODE_WORD int = 900 + +// Since each code word consists of 2 characters, a padding value is +// needed when encoding a single character. 29 is used as padding because +// it's a switch in all 4 submodes, and doesn't add any data. +const PADDING_VALUE = 29 + +// Uppercase submode. +const SUBMODE_UPPER = "SUBMODE_UPPER" + +// Lowercase submode. +const SUBMODE_LOWER = "SUBMODE_LOWER" + +// mixed submode (numbers and some punctuation). +const SUBMODE_MIXED = "SUBMODE_MIXED" + +// Punctuation submode. +const SUBMODE_PUNCT = "SUBMODE_PUNCT" + +// Switch to uppercase submode. +const SWITCH_UPPER = "SWITCH_UPPER" + +// Switch to uppercase submode for a single character. +const SWITCH_UPPER_SINGLE = "SWITCH_UPPER_SINGLE" + +// Switch to lowercase submode. +const SWITCH_LOWER = "SWITCH_LOWER" + +// Switch to mixed submode. +const SWITCH_MIXED = "SWITCH_MIXED" + +// Switch to punctuation submode. +const SWITCH_PUNCT = "SWITCH_PUNCT" + +// Switch to punctuation submode for a single character. +const SWITCH_PUNCT_SINGLE = "SWITCH_PUNCT_SINGLE" + +type TextEncoder struct { + CharacterTables map[string][]string + Switching map[string]map[string][]string + SwitchSubmode map[string]string + ReverseLookup map[string]map[string]int +} + +func CreateTextEncoder() *TextEncoder { + encoder := new(TextEncoder) + encoder.CharacterTables = map[string][]string{ + SUBMODE_UPPER: []string{ + "A", "B", "C", "D", "E", "F", "G", "H", "I", + "J", "K", "L", "M", "N", "O", "P", "Q", "R", + "S", "T", "U", "V", "W", "X", "Y", "Z", " ", + SWITCH_LOWER, + SWITCH_MIXED, + SWITCH_PUNCT_SINGLE, + }, + + SUBMODE_LOWER: []string{ + "a", "b", "c", "d", "e", "f", "g", "h", "i", + "j", "k", "l", "m", "n", "o", "p", "q", "r", + "s", "t", "u", "v", "w", "x", "y", "z", " ", + SWITCH_UPPER_SINGLE, + SWITCH_MIXED, + SWITCH_PUNCT_SINGLE, + }, + + SUBMODE_MIXED: []string{ + "0", "1", "2", "3", "4", "5", "6", "7", "8", + "9", "&", "\r", "\t", ",", ":", "#", "-", ".", + "$", "/", "+", "%", "*", "=", "^", + SWITCH_PUNCT, " ", + SWITCH_LOWER, + SWITCH_UPPER, + SWITCH_PUNCT_SINGLE, + }, + + SUBMODE_PUNCT: []string{ + ";", "<", ">", "@", "[", "\\", "]", "_", "`", + "~", "!", "\r", "\t", ",", ":", "\n", "-", ".", + "$", "/", "\"", "|", "*", "(", ")", "?", "{", "}", "'", + SWITCH_UPPER, + }, + } + + encoder.Switching = map[string]map[string][]string{ + SUBMODE_UPPER: map[string][]string{ + SUBMODE_LOWER: []string{SWITCH_LOWER}, + SUBMODE_MIXED: []string{SWITCH_MIXED}, + SUBMODE_PUNCT: []string{SWITCH_MIXED, SWITCH_PUNCT}, + }, + + SUBMODE_LOWER: map[string][]string{ + SUBMODE_UPPER: []string{SWITCH_MIXED, SWITCH_UPPER}, + SUBMODE_MIXED: []string{SWITCH_MIXED}, + SUBMODE_PUNCT: []string{SWITCH_MIXED, SWITCH_PUNCT}, + }, + + SUBMODE_MIXED: map[string][]string{ + SUBMODE_UPPER: []string{SWITCH_UPPER}, + SUBMODE_LOWER: []string{SWITCH_LOWER}, + SUBMODE_PUNCT: []string{SWITCH_PUNCT}, + }, + + SUBMODE_PUNCT: map[string][]string{ + SUBMODE_UPPER: []string{SWITCH_UPPER}, + SUBMODE_LOWER: []string{SWITCH_UPPER, SWITCH_LOWER}, + SUBMODE_MIXED: []string{SWITCH_UPPER, SWITCH_MIXED}, + }, + } + + encoder.SwitchSubmode = map[string]string{ + SWITCH_UPPER: SUBMODE_UPPER, + SWITCH_LOWER: SUBMODE_LOWER, + SWITCH_PUNCT: SUBMODE_PUNCT, + SWITCH_MIXED: SUBMODE_MIXED, + } + + encoder.ReverseLookup = make(map[string]map[string]int) + for submode, codes := range encoder.CharacterTables { + for row, char := range codes { + if encoder.ReverseLookup[char] == nil { + encoder.ReverseLookup[char] = make(map[string]int) + } + + encoder.ReverseLookup[char][submode] = int(row) + } + } + + return encoder +} + +func (encoder TextEncoder) GetName() string { + return "text" +} + +func (encoder TextEncoder) CanEncode(char string) bool { + return encoder.ReverseLookup[char] != nil +} + +func (TextEncoder) GetSwitchCode(data string) int { + return TEXT_SWITCH_CODE_WORD +} + +func (encoder TextEncoder) Encode(data string, addSwitchCode bool) []int { + interim := encodeinterim(encoder, data); + + return encodeFinal(interim, addSwitchCode) +} + +func encodeinterim(encoder TextEncoder, data string) []int { + submode := SUBMODE_UPPER + + codes := []int{} + + for i := 0; i < len(data); i++ { + char := string(data[i]) + + if (existsInSubmode(encoder, char, submode) == false) { + prevSubmode := submode + + submode = getSubmode(encoder, char) + + switchCodes := getSwitchCodes(encoder, prevSubmode, submode) + + codes = append(codes, switchCodes...) + } + + codes = append( + codes, + getCharacterCode(encoder, char, submode), + ) + } + + return codes +} + +func getSubmode(encoder TextEncoder, char string) string { + _, ok := encoder.ReverseLookup[char] + + if ! ok { + panic("Weird, not found") + } + + for key := range encoder.ReverseLookup[char] { + return key + } + + return "INVALID" +} + +func getSwitchCodes(encoder TextEncoder, from string, to string) []int { + switches := encoder.Switching[from][to] + + codes := []int{} + + for _, switcher := range switches { + codes = append(codes, getCharacterCode(encoder, switcher, from)) + + from = encoder.SwitchSubmode[switcher] + } + + return codes +} + +func encodeFinal(codes []int, addSwitchCode bool) []int { + codeWords := []int{} + + if addSwitchCode { + codeWords = append(codeWords, TEXT_SWITCH_CODE_WORD) + } + + chunks := [][]int{} + chunkPart := []int{} + i := 0 + for _, code := range codes { + chunkPart = append(chunkPart, code) + + i++ + + if i % 2 == 0 { + chunks = append(chunks, chunkPart) + + chunkPart = []int{} + } + } + + if len(chunkPart) > 0 { + chunks = append(chunks, chunkPart) + } + + for _, chunk := range chunks { + if len(chunk) == 1 { + chunk = append(chunk, PADDING_VALUE) + } + + codeWords = append( + codeWords, + 30 * chunk[0] + chunk[1], + ) + } + + return codeWords +} + +func getCharacterCode(encoder TextEncoder, char string, submode string) int { + cw, ok := encoder.ReverseLookup[char][submode] + + if ! ok { + panic("This is not possible") + } + + return cw +} + +func existsInSubmode(encoder TextEncoder, char string, submode string) bool { + _, ok := encoder.ReverseLookup[char][submode] + + return ok +}