summaryrefslogtreecommitdiff
path: root/vendor/github.com
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com')
-rw-r--r--vendor/github.com/Wessie/appdirs/LICENSE18
-rw-r--r--vendor/github.com/Wessie/appdirs/appdirs.go80
-rw-r--r--vendor/github.com/Wessie/appdirs/appdirs_darwin.go63
-rw-r--r--vendor/github.com/Wessie/appdirs/appdirs_unix.go102
-rw-r--r--vendor/github.com/Wessie/appdirs/appdirs_windows.go171
-rw-r--r--vendor/github.com/Wessie/appdirs/doc.go108
-rw-r--r--vendor/github.com/boombuler/barcode/LICENSE21
-rw-r--r--vendor/github.com/boombuler/barcode/barcode.go21
-rw-r--r--vendor/github.com/boombuler/barcode/codabar/encoder.go49
-rw-r--r--vendor/github.com/boombuler/barcode/code128/encode.go130
-rw-r--r--vendor/github.com/boombuler/barcode/code128/encodingtable.go133
-rw-r--r--vendor/github.com/boombuler/barcode/code39/encoder.go152
-rw-r--r--vendor/github.com/boombuler/barcode/datamatrix/codelayout.go213
-rw-r--r--vendor/github.com/boombuler/barcode/datamatrix/codesize.go73
-rw-r--r--vendor/github.com/boombuler/barcode/datamatrix/datamatrixcode.go53
-rw-r--r--vendor/github.com/boombuler/barcode/datamatrix/encoder.go75
-rw-r--r--vendor/github.com/boombuler/barcode/datamatrix/errorcorrection.go85
-rw-r--r--vendor/github.com/boombuler/barcode/ean/encoder.go187
-rw-r--r--vendor/github.com/boombuler/barcode/qr/alphanumeric.go66
-rw-r--r--vendor/github.com/boombuler/barcode/qr/automatic.go23
-rw-r--r--vendor/github.com/boombuler/barcode/qr/blocks.go59
-rw-r--r--vendor/github.com/boombuler/barcode/qr/encoder.go416
-rw-r--r--vendor/github.com/boombuler/barcode/qr/errorcorrection.go54
-rw-r--r--vendor/github.com/boombuler/barcode/qr/numeric.go56
-rw-r--r--vendor/github.com/boombuler/barcode/qr/qrcode.go170
-rw-r--r--vendor/github.com/boombuler/barcode/qr/unicode.go27
-rw-r--r--vendor/github.com/boombuler/barcode/qr/versioninfo.go310
-rw-r--r--vendor/github.com/boombuler/barcode/scaledbarcode.go115
-rw-r--r--vendor/github.com/boombuler/barcode/twooffive/encoder.go138
-rw-r--r--vendor/github.com/boombuler/barcode/utils/base1dcode.go48
-rw-r--r--vendor/github.com/boombuler/barcode/utils/bitlist.go119
-rw-r--r--vendor/github.com/boombuler/barcode/utils/galoisfield.go62
-rw-r--r--vendor/github.com/boombuler/barcode/utils/gfpoly.go103
-rw-r--r--vendor/github.com/boombuler/barcode/utils/runeint.go19
-rw-r--r--vendor/github.com/flopp/go-coordsparser/LICENSE21
-rw-r--r--vendor/github.com/flopp/go-coordsparser/coordsparser.go172
-rw-r--r--vendor/github.com/flopp/go-staticmaps/LICENSE21
-rw-r--r--vendor/github.com/flopp/go-staticmaps/area.go94
-rw-r--r--vendor/github.com/flopp/go-staticmaps/color.go65
-rw-r--r--vendor/github.com/flopp/go-staticmaps/context.go277
-rw-r--r--vendor/github.com/flopp/go-staticmaps/create-static-map/create-static-map.go134
-rw-r--r--vendor/github.com/flopp/go-staticmaps/map_object.go13
-rw-r--r--vendor/github.com/flopp/go-staticmaps/marker.go125
-rw-r--r--vendor/github.com/flopp/go-staticmaps/path.go103
-rw-r--r--vendor/github.com/flopp/go-staticmaps/tile_fetcher.go153
-rw-r--r--vendor/github.com/flopp/go-staticmaps/tile_provider.go98
-rw-r--r--vendor/github.com/flopp/go-staticmaps/util.go18
-rw-r--r--vendor/github.com/fogleman/gg/LICENSE.md19
-rw-r--r--vendor/github.com/fogleman/gg/bezier.go59
-rw-r--r--vendor/github.com/fogleman/gg/context.go738
-rw-r--r--vendor/github.com/fogleman/gg/examples/beziers.go84
-rw-r--r--vendor/github.com/fogleman/gg/examples/circle.go11
-rw-r--r--vendor/github.com/fogleman/gg/examples/clip.go15
-rw-r--r--vendor/github.com/fogleman/gg/examples/cubic.go38
-rw-r--r--vendor/github.com/fogleman/gg/examples/ellipse.go20
-rw-r--r--vendor/github.com/fogleman/gg/examples/lines.go31
-rw-r--r--vendor/github.com/fogleman/gg/examples/linewidth.go19
-rw-r--r--vendor/github.com/fogleman/gg/examples/lorem.go28
-rw-r--r--vendor/github.com/fogleman/gg/examples/mask.go20
-rw-r--r--vendor/github.com/fogleman/gg/examples/meme.go30
-rw-r--r--vendor/github.com/fogleman/gg/examples/openfill.go31
-rw-r--r--vendor/github.com/fogleman/gg/examples/quadratic.go54
-rw-r--r--vendor/github.com/fogleman/gg/examples/scatter.go66
-rw-r--r--vendor/github.com/fogleman/gg/examples/sine.go29
-rw-r--r--vendor/github.com/fogleman/gg/examples/spiral.go27
-rw-r--r--vendor/github.com/fogleman/gg/examples/star.go40
-rw-r--r--vendor/github.com/fogleman/gg/examples/stars.go51
-rw-r--r--vendor/github.com/fogleman/gg/examples/text.go16
-rw-r--r--vendor/github.com/fogleman/gg/examples/tiling.go21
-rw-r--r--vendor/github.com/fogleman/gg/examples/wrap.go40
-rw-r--r--vendor/github.com/fogleman/gg/matrix.go88
-rw-r--r--vendor/github.com/fogleman/gg/path.go140
-rw-r--r--vendor/github.com/fogleman/gg/point.go25
-rw-r--r--vendor/github.com/fogleman/gg/util.go116
-rw-r--r--vendor/github.com/fogleman/gg/wrap.go58
-rw-r--r--vendor/github.com/gocarina/gocsv/LICENSE21
-rw-r--r--vendor/github.com/gocarina/gocsv/csv.go217
-rw-r--r--vendor/github.com/gocarina/gocsv/decode.go296
-rw-r--r--vendor/github.com/gocarina/gocsv/encode.go137
-rw-r--r--vendor/github.com/gocarina/gocsv/reflect.go104
-rw-r--r--vendor/github.com/gocarina/gocsv/types.go437
-rw-r--r--vendor/github.com/golang/freetype/raster/LICENSE12
-rw-r--r--vendor/github.com/golang/freetype/raster/geom.go245
-rw-r--r--vendor/github.com/golang/freetype/raster/paint.go287
-rw-r--r--vendor/github.com/golang/freetype/raster/raster.go601
-rw-r--r--vendor/github.com/golang/freetype/raster/stroke.go483
-rw-r--r--vendor/github.com/golang/freetype/truetype/LICENSE12
-rw-r--r--vendor/github.com/golang/freetype/truetype/face.go507
-rw-r--r--vendor/github.com/golang/freetype/truetype/glyph.go517
-rw-r--r--vendor/github.com/golang/freetype/truetype/hint.go1770
-rw-r--r--vendor/github.com/golang/freetype/truetype/opcodes.go289
-rw-r--r--vendor/github.com/golang/freetype/truetype/truetype.go643
-rw-r--r--vendor/github.com/golang/geo/r1/LICENSE202
-rw-r--r--vendor/github.com/golang/geo/r1/doc.go22
-rw-r--r--vendor/github.com/golang/geo/r1/interval.go161
-rw-r--r--vendor/github.com/golang/geo/r2/LICENSE202
-rw-r--r--vendor/github.com/golang/geo/r2/doc.go22
-rw-r--r--vendor/github.com/golang/geo/r2/rect.go257
-rw-r--r--vendor/github.com/golang/geo/r3/LICENSE202
-rw-r--r--vendor/github.com/golang/geo/r3/doc.go22
-rw-r--r--vendor/github.com/golang/geo/r3/precisevector.go200
-rw-r--r--vendor/github.com/golang/geo/r3/vector.go184
-rw-r--r--vendor/github.com/golang/geo/s1/LICENSE202
-rw-r--r--vendor/github.com/golang/geo/s1/angle.go119
-rw-r--r--vendor/github.com/golang/geo/s1/chordangle.go202
-rw-r--r--vendor/github.com/golang/geo/s1/doc.go22
-rw-r--r--vendor/github.com/golang/geo/s1/interval.go350
-rw-r--r--vendor/github.com/golang/geo/s2/LICENSE202
-rw-r--r--vendor/github.com/golang/geo/s2/cap.go406
-rw-r--r--vendor/github.com/golang/geo/s2/cell.go385
-rw-r--r--vendor/github.com/golang/geo/s2/cellid.go729
-rw-r--r--vendor/github.com/golang/geo/s2/cellunion.go236
-rw-r--r--vendor/github.com/golang/geo/s2/doc.go31
-rw-r--r--vendor/github.com/golang/geo/s2/edgeutil.go1293
-rw-r--r--vendor/github.com/golang/geo/s2/latlng.go96
-rw-r--r--vendor/github.com/golang/geo/s2/loop.go282
-rw-r--r--vendor/github.com/golang/geo/s2/matrix3x3.go127
-rw-r--r--vendor/github.com/golang/geo/s2/metric.go166
-rw-r--r--vendor/github.com/golang/geo/s2/paddedcell.go254
-rw-r--r--vendor/github.com/golang/geo/s2/point.go291
-rw-r--r--vendor/github.com/golang/geo/s2/polygon.go211
-rw-r--r--vendor/github.com/golang/geo/s2/polyline.go177
-rw-r--r--vendor/github.com/golang/geo/s2/predicates.go238
-rw-r--r--vendor/github.com/golang/geo/s2/rect.go426
-rw-r--r--vendor/github.com/golang/geo/s2/region.go50
-rw-r--r--vendor/github.com/golang/geo/s2/regioncoverer.go465
-rw-r--r--vendor/github.com/golang/geo/s2/shapeindex.go202
-rw-r--r--vendor/github.com/golang/geo/s2/stuv.go310
-rw-r--r--vendor/github.com/jessevdk/go-flags/LICENSE26
-rw-r--r--vendor/github.com/jessevdk/go-flags/arg.go27
-rw-r--r--vendor/github.com/jessevdk/go-flags/closest.go59
-rw-r--r--vendor/github.com/jessevdk/go-flags/command.go455
-rw-r--r--vendor/github.com/jessevdk/go-flags/completion.go300
-rw-r--r--vendor/github.com/jessevdk/go-flags/convert.go341
-rw-r--r--vendor/github.com/jessevdk/go-flags/error.go134
-rw-r--r--vendor/github.com/jessevdk/go-flags/examples/add.go23
-rw-r--r--vendor/github.com/jessevdk/go-flags/examples/main.go79
-rw-r--r--vendor/github.com/jessevdk/go-flags/examples/rm.go23
-rw-r--r--vendor/github.com/jessevdk/go-flags/flags.go258
-rw-r--r--vendor/github.com/jessevdk/go-flags/group.go385
-rw-r--r--vendor/github.com/jessevdk/go-flags/help.go485
-rw-r--r--vendor/github.com/jessevdk/go-flags/ini.go601
-rw-r--r--vendor/github.com/jessevdk/go-flags/man.go205
-rw-r--r--vendor/github.com/jessevdk/go-flags/multitag.go140
-rw-r--r--vendor/github.com/jessevdk/go-flags/option.go456
-rw-r--r--vendor/github.com/jessevdk/go-flags/optstyle_other.go67
-rw-r--r--vendor/github.com/jessevdk/go-flags/optstyle_windows.go108
-rw-r--r--vendor/github.com/jessevdk/go-flags/parser.go700
-rw-r--r--vendor/github.com/jessevdk/go-flags/termsize.go28
-rw-r--r--vendor/github.com/jessevdk/go-flags/termsize_linux.go7
-rw-r--r--vendor/github.com/jessevdk/go-flags/termsize_nosysioctl.go7
-rw-r--r--vendor/github.com/jessevdk/go-flags/termsize_other.go7
-rw-r--r--vendor/github.com/jessevdk/go-flags/termsize_unix.go7
-rw-r--r--vendor/github.com/jung-kurt/gofpdf/compare.go146
-rw-r--r--vendor/github.com/jung-kurt/gofpdf/contrib/barcode/barcode.go238
-rw-r--r--vendor/github.com/jung-kurt/gofpdf/contrib/ghostscript/ghostscript.go68
-rw-r--r--vendor/github.com/jung-kurt/gofpdf/contrib/httpimg/httpimg.go43
-rw-r--r--vendor/github.com/jung-kurt/gofpdf/contrib/tiff/tiff.go83
-rw-r--r--vendor/github.com/jung-kurt/gofpdf/def.go378
-rw-r--r--vendor/github.com/jung-kurt/gofpdf/doc.go234
-rw-r--r--vendor/github.com/jung-kurt/gofpdf/embedded.go559
-rw-r--r--vendor/github.com/jung-kurt/gofpdf/font.go456
-rw-r--r--vendor/github.com/jung-kurt/gofpdf/fpdf.go3856
-rw-r--r--vendor/github.com/jung-kurt/gofpdf/fpdftrans.go213
-rw-r--r--vendor/github.com/jung-kurt/gofpdf/htmlbasic.go211
-rw-r--r--vendor/github.com/jung-kurt/gofpdf/internal/example/example.go126
-rw-r--r--vendor/github.com/jung-kurt/gofpdf/internal/files/bin/bin.go28
-rw-r--r--vendor/github.com/jung-kurt/gofpdf/internal/files/files.go1691
-rw-r--r--vendor/github.com/jung-kurt/gofpdf/layer.go121
-rw-r--r--vendor/github.com/jung-kurt/gofpdf/license.txt23
-rw-r--r--vendor/github.com/jung-kurt/gofpdf/list/list.go59
-rw-r--r--vendor/github.com/jung-kurt/gofpdf/makefont/doc.go23
-rwxr-xr-xvendor/github.com/jung-kurt/gofpdf/makefont/makefontbin0 -> 3568652 bytes
-rw-r--r--vendor/github.com/jung-kurt/gofpdf/makefont/makefont.go71
-rw-r--r--vendor/github.com/jung-kurt/gofpdf/png.go212
-rw-r--r--vendor/github.com/jung-kurt/gofpdf/protect.go114
-rw-r--r--vendor/github.com/jung-kurt/gofpdf/svgbasic.go210
-rw-r--r--vendor/github.com/jung-kurt/gofpdf/svgwrite.go58
-rw-r--r--vendor/github.com/jung-kurt/gofpdf/template.go269
-rw-r--r--vendor/github.com/jung-kurt/gofpdf/template_impl.go123
-rw-r--r--vendor/github.com/jung-kurt/gofpdf/ttfparser.go374
-rw-r--r--vendor/github.com/jung-kurt/gofpdf/util.go314
-rw-r--r--vendor/github.com/op/go-logging/LICENSE27
-rw-r--r--vendor/github.com/op/go-logging/backend.go39
-rw-r--r--vendor/github.com/op/go-logging/examples/example.go49
-rw-r--r--vendor/github.com/op/go-logging/format.go414
-rw-r--r--vendor/github.com/op/go-logging/level.go128
-rw-r--r--vendor/github.com/op/go-logging/log_nix.go109
-rw-r--r--vendor/github.com/op/go-logging/log_windows.go107
-rw-r--r--vendor/github.com/op/go-logging/logger.go259
-rw-r--r--vendor/github.com/op/go-logging/memory.go237
-rw-r--r--vendor/github.com/op/go-logging/multi.go65
-rw-r--r--vendor/github.com/op/go-logging/syslog.go53
-rw-r--r--vendor/github.com/op/go-logging/syslog_fallback.go28
-rw-r--r--vendor/github.com/ruudk/golang-pdf417/byte_encoder.go28
-rw-r--r--vendor/github.com/ruudk/golang-pdf417/data_encoder.go99
-rw-r--r--vendor/github.com/ruudk/golang-pdf417/number_encoder.go87
-rw-r--r--vendor/github.com/ruudk/golang-pdf417/pdf417.go633
-rw-r--r--vendor/github.com/ruudk/golang-pdf417/reed_solomon.go164
-rw-r--r--vendor/github.com/ruudk/golang-pdf417/text_encoder.go261
-rw-r--r--vendor/github.com/skip2/go-qrcode/LICENSE19
-rw-r--r--vendor/github.com/skip2/go-qrcode/bitset/bitset.go273
-rw-r--r--vendor/github.com/skip2/go-qrcode/encoder.go455
-rw-r--r--vendor/github.com/skip2/go-qrcode/qrcode.go459
-rw-r--r--vendor/github.com/skip2/go-qrcode/reedsolomon/gf2_8.go387
-rw-r--r--vendor/github.com/skip2/go-qrcode/reedsolomon/gf_poly.go216
-rw-r--r--vendor/github.com/skip2/go-qrcode/reedsolomon/reed_solomon.go73
-rw-r--r--vendor/github.com/skip2/go-qrcode/regular_symbol.go309
-rw-r--r--vendor/github.com/skip2/go-qrcode/symbol.go309
-rw-r--r--vendor/github.com/skip2/go-qrcode/version.go3050
-rw-r--r--vendor/github.com/tkrajina/gpxgo/gpx/LICENSE.txt202
-rw-r--r--vendor/github.com/tkrajina/gpxgo/gpx/converters.go616
-rw-r--r--vendor/github.com/tkrajina/gpxgo/gpx/geo.go357
-rw-r--r--vendor/github.com/tkrajina/gpxgo/gpx/gpx.go1582
-rw-r--r--vendor/github.com/tkrajina/gpxgo/gpx/gpx10.go240
-rw-r--r--vendor/github.com/tkrajina/gpxgo/gpx/gpx11.go283
-rw-r--r--vendor/github.com/tkrajina/gpxgo/gpx/nullable_float64.go100
-rw-r--r--vendor/github.com/tkrajina/gpxgo/gpx/nullable_int.go100
-rw-r--r--vendor/github.com/tkrajina/gpxgo/gpx/nullable_string.go41
-rw-r--r--vendor/github.com/tkrajina/gpxgo/gpx/nullable_time.go43
-rw-r--r--vendor/github.com/tkrajina/gpxgo/gpx/xml.go178
221 files changed, 49559 insertions, 0 deletions
diff --git a/vendor/github.com/Wessie/appdirs/LICENSE b/vendor/github.com/Wessie/appdirs/LICENSE
new file mode 100644
index 0000000..6b2f3ee
--- /dev/null
+++ b/vendor/github.com/Wessie/appdirs/LICENSE
@@ -0,0 +1,18 @@
+Copyright (c) 2013 Wesley Bitter
+
+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/Wessie/appdirs/appdirs.go b/vendor/github.com/Wessie/appdirs/appdirs.go
new file mode 100644
index 0000000..1577cb9
--- /dev/null
+++ b/vendor/github.com/Wessie/appdirs/appdirs.go
@@ -0,0 +1,80 @@
+// A port of the excellent python module `appdirs`.
+// See https://github.com/ActiveState/appdirs for the python version.
+package appdirs
+
+import (
+ "os/user"
+ "strings"
+)
+
+// App is a helper type to create easy access across your program to the appdirs
+// functions.
+//
+// The *App type has 6 methods that map to the 6 functions exported by `appdirs`.
+// All methods take no arguments, and supply the function it wraps with arguments
+// pre-set in the struct on creation.
+type App struct {
+ Name string
+ Author string
+ Version string
+ Roaming bool
+ Opinion bool
+}
+
+// New returns a new App helper that has various methods for receiving
+// relevant directories for your application.
+//
+// The following defaults are used for the two fields not settable by New:
+// Roaming: false, Opinion: true
+//
+// If you want to set these, create your own App struct by the usual means.
+func New(name, author, version string) *App {
+ return &App{
+ Name: name,
+ Author: author,
+ Version: version,
+ Roaming: false,
+ Opinion: true,
+ }
+}
+
+// UserData returns the full path to the user-specific data directory
+func (app *App) UserData() string {
+ return UserDataDir(app.Name, app.Author, app.Version, app.Roaming)
+}
+
+// SiteData returns the full path to the user-shared data directory
+func (app *App) SiteData() string {
+ return SiteDataDir(app.Name, app.Author, app.Version)
+}
+
+// SiteConfig returns the full path to the user-shared configuration directory
+func (app *App) SiteConfig() string {
+ return SiteConfigDir(app.Name, app.Author, app.Version)
+}
+
+// UserConfig returns the full path to the user-specific configuration directory
+func (app *App) UserConfig() string {
+ return UserConfigDir(app.Name, app.Author, app.Version, app.Roaming)
+}
+
+// UserCache returns the full path to the user-specific cache directory
+func (app *App) UserCache() string {
+ return UserCacheDir(app.Name, app.Author, app.Version, app.Opinion)
+}
+
+// UserLog returns the full path to the user-specific log directory
+func (app *App) UserLog() string {
+ return UserLogDir(app.Name, app.Author, app.Version, app.Opinion)
+}
+
+// ExpandUser is a helper function that expands the first '~' it finds in the
+// passed path with the home directory of the current user.
+//
+// Note: This only works on environments similar to bash.
+func ExpandUser(path string) string {
+ if u, err := user.Current(); err == nil {
+ return strings.Replace(path, "~", u.HomeDir, -1)
+ }
+ return path
+}
diff --git a/vendor/github.com/Wessie/appdirs/appdirs_darwin.go b/vendor/github.com/Wessie/appdirs/appdirs_darwin.go
new file mode 100644
index 0000000..29b6ae8
--- /dev/null
+++ b/vendor/github.com/Wessie/appdirs/appdirs_darwin.go
@@ -0,0 +1,63 @@
+package appdirs
+
+import (
+ "path/filepath"
+)
+
+func userDataDir(name, author, version string, roaming bool) (path string) {
+ path = ExpandUser("~/Library/Application Support")
+
+ if name != "" {
+ path = filepath.Join(path, name)
+ }
+
+ if name != "" && version != "" {
+ path = filepath.Join(path, version)
+ }
+ return path
+}
+
+func siteDataDir(name, author, version string) (path string) {
+ path = ExpandUser("/Library/Application Support")
+
+ if name != "" {
+ path = filepath.Join(path, name)
+ }
+
+ if name != "" && version != "" {
+ path = filepath.Join(path, version)
+ }
+ return path
+}
+
+func userConfigDir(name, author, version string, roaming bool) (path string) {
+ return UserDataDir(name, author, version, roaming)
+}
+
+func siteConfigDir(name, author, version string) (path string) {
+ return SiteDataDir(name, author, version)
+}
+
+func userCacheDir(name, author, version string, opinion bool) (path string) {
+ path = ExpandUser("~/Library/Caches")
+
+ if name != "" {
+ path = filepath.Join(path, name)
+ }
+
+ if name != "" && version != "" {
+ path = filepath.Join(path, version)
+ }
+ return path
+}
+
+func userLogDir(name, author, version string, opinion bool) (path string) {
+ path = ExpandUser("~/Library/Logs")
+
+ path = filepath.Join(path, name)
+
+ if name != "" && version != "" {
+ path = filepath.Join(path, version)
+ }
+ return path
+}
diff --git a/vendor/github.com/Wessie/appdirs/appdirs_unix.go b/vendor/github.com/Wessie/appdirs/appdirs_unix.go
new file mode 100644
index 0000000..99c9917
--- /dev/null
+++ b/vendor/github.com/Wessie/appdirs/appdirs_unix.go
@@ -0,0 +1,102 @@
+// +build linux freebsd netbsd openbsd
+
+package appdirs
+
+import (
+ "os"
+ "path/filepath"
+)
+
+func userDataDir(name, author, version string, roaming bool) (path string) {
+ if path = os.Getenv("XDG_DATA_HOME"); path == "" {
+ path = ExpandUser("~/.local/share")
+ }
+
+ if name != "" {
+ path = filepath.Join(path, name, version)
+ }
+
+ return path
+}
+
+func SiteDataDirs(name, author, version string) (paths []string) {
+ var path string
+
+ if path = os.Getenv("XDG_DATA_DIRS"); path == "" {
+ paths = []string{"/usr/local/share", "/usr/share"}
+ } else {
+ paths = filepath.SplitList(path)
+ }
+
+ for i, path := range paths {
+ path = ExpandUser(path)
+
+ if name != "" {
+ path = filepath.Join(path, name, version)
+ }
+
+ paths[i] = path
+ }
+
+ return paths
+}
+
+func siteDataDir(name, author, version string) (path string) {
+ return SiteDataDirs(name, author, version)[0]
+}
+
+func userConfigDir(name, author, version string, roaming bool) (path string) {
+ if path = os.Getenv("XDG_CONFIG_HOME"); path == "" {
+ path = ExpandUser("~/.config")
+ }
+
+ if name != "" {
+ path = filepath.Join(path, name, version)
+ }
+
+ return path
+}
+
+func SiteConfigDirs(name, author, version string) (paths []string) {
+ var path string
+
+ if path = os.Getenv("XDG_CONFIG_DIRS"); path == "" {
+ paths = []string{"/etc/xdg"}
+ } else {
+ paths = filepath.SplitList(path)
+ }
+
+ for i, path := range paths {
+ path = ExpandUser(path)
+
+ if name != "" {
+ path = filepath.Join(path, name, version)
+ }
+
+ paths[i] = path
+ }
+
+ return paths
+}
+
+func siteConfigDir(name, author, version string) (path string) {
+ return SiteConfigDirs(name, author, version)[0]
+}
+
+func userCacheDir(name, author, version string, opinion bool) (path string) {
+ if path = os.Getenv("XDG_CACHE_HOME"); path == "" {
+ path = ExpandUser("~/.cache")
+ }
+
+ if name != "" {
+ path = filepath.Join(path, name, version)
+ }
+
+ return path
+}
+
+func userLogDir(name, author, version string, opinion bool) (path string) {
+ path = UserCacheDir(name, author, version, opinion)
+
+ return filepath.Join(path, "log")
+}
diff --git a/vendor/github.com/Wessie/appdirs/appdirs_windows.go b/vendor/github.com/Wessie/appdirs/appdirs_windows.go
new file mode 100644
index 0000000..ca897af
--- /dev/null
+++ b/vendor/github.com/Wessie/appdirs/appdirs_windows.go
@@ -0,0 +1,171 @@
+package appdirs
+
+import (
+ "path/filepath"
+ "syscall"
+ "unsafe"
+)
+
+var (
+ shell32, _ = syscall.LoadLibrary("shell32.dll")
+ getKnownFolderPath, _ = syscall.GetProcAddress(shell32, "SHGetKnownFolderPath")
+
+ ole32, _ = syscall.LoadLibrary("Ole32.dll")
+ coTaskMemFree, _ = syscall.GetProcAddress(ole32, "CoTaskMemFree")
+)
+
+// These are KNOWNFOLDERID constants that are passed to GetKnownFolderPath
+var (
+ rfidLocalAppData = syscall.GUID{
+ 0xf1b32785,
+ 0x6fba,
+ 0x4fcf,
+ [8]byte{0x9d, 0x55, 0x7b, 0x8e, 0x7f, 0x15, 0x70, 0x91},
+ }
+ rfidRoamingAppData = syscall.GUID{
+ 0x3eb685db,
+ 0x65f9,
+ 0x4cf6,
+ [8]byte{0xa0, 0x3a, 0xe3, 0xef, 0x65, 0x72, 0x9f, 0x3d},
+ }
+ rfidProgramData = syscall.GUID{
+ 0x62ab5d82,
+ 0xfdc1,
+ 0x4dc3,
+ [8]byte{0xa9, 0xdd, 0x07, 0x0d, 0x1d, 0x49, 0x5d, 0x97},
+ }
+)
+
+func userDataDir(name, author, version string, roaming bool) (path string) {
+ if author == "" {
+ author = name
+ }
+
+ var rfid syscall.GUID
+ if roaming {
+ rfid = rfidRoamingAppData
+ } else {
+ rfid = rfidLocalAppData
+ }
+
+ path, err := getFolderPath(rfid)
+
+ if err != nil {
+ return ""
+ }
+
+ if path, err = filepath.Abs(path); err != nil {
+ return ""
+ }
+
+ if name != "" {
+ path = filepath.Join(path, author, name)
+ }
+
+ if name != "" && version != "" {
+ path = filepath.Join(path, version)
+ }
+
+ return path
+}
+
+func siteDataDir(name, author, version string) (path string) {
+ path, err := getFolderPath(rfidProgramData)
+
+ if err != nil {
+ return ""
+ }
+
+ if path, err = filepath.Abs(path); err != nil {
+ return ""
+ }
+
+ if author == "" {
+ author = name
+ }
+
+ if name != "" {
+ path = filepath.Join(path, author, name)
+ }
+
+ if name != "" && version != "" {
+ path = filepath.Join(path, version)
+ }
+
+ return path
+}
+
+func userConfigDir(name, author, version string, roaming bool) string {
+ return UserDataDir(name, author, version, roaming)
+}
+
+func siteConfigDir(name, author, version string) (path string) {
+ return SiteDataDir(name, author, version)
+}
+
+func userCacheDir(name, author, version string, opinion bool) (path string) {
+ if author == "" {
+ author = name
+ }
+
+ path, err := getFolderPath(rfidLocalAppData)
+
+ if err != nil {
+ return ""
+ }
+
+ if path, err = filepath.Abs(path); err != nil {
+ return ""
+ }
+
+ if name != "" {
+ path = filepath.Join(path, author, name)
+ if opinion {
+ path = filepath.Join(path, "Cache")
+ }
+ }
+
+ if name != "" && version != "" {
+ path = filepath.Join(path, version)
+ }
+
+ return path
+}
+
+func userLogDir(name, author, version string, opinion bool) (path string) {
+ path = UserDataDir(name, author, version, false)
+
+ if opinion {
+ path = filepath.Join(path, "Logs")
+ }
+
+ return path
+}
+
+func getFolderPath(rfid syscall.GUID) (string, error) {
+ var res uintptr
+
+ ret, _, callErr := syscall.Syscall6(
+ uintptr(getKnownFolderPath),
+ 4,
+ uintptr(unsafe.Pointer(&rfid)),
+ 0,
+ 0,
+ uintptr(unsafe.Pointer(&res)),
+ 0,
+ 0,
+ )
+
+ if callErr != 0 && ret != 0 {
+ return "", callErr
+ }
+
+ defer syscall.Syscall(uintptr(coTaskMemFree), 1, res, 0, 0)
+ return ucs2PtrToString(res), nil
+}
+
+func ucs2PtrToString(p uintptr) string {
+ ptr := (*[4096]uint16)(unsafe.Pointer(p))
+
+ return syscall.UTF16ToString((*ptr)[:])
+}
diff --git a/vendor/github.com/Wessie/appdirs/doc.go b/vendor/github.com/Wessie/appdirs/doc.go
new file mode 100644
index 0000000..4311627
--- /dev/null
+++ b/vendor/github.com/Wessie/appdirs/doc.go
@@ -0,0 +1,108 @@
+// appdirs project doc.go
+
+/*
+This is a port of a python module used for finding what directory you 'should'
+be using for saving your application data such as configuration, cache files or
+other files.
+
+The location of these directories is often hard to get right. The original python
+module set out to change this into a simple API that returns you the exact
+directory you need. This is a port of it to Go.
+
+Depending on platform, this package exports you at the least 6 functions that
+return various system directories. And one helper struct type that combines the
+functions into methods for less arguments in your code.
+
+Each function defined accepts a number of arguments, each argument is optional
+and can be left to the types default value if omitted. Often the function will
+ignore arguments if the name given is empty.
+
+Passing in all default values into any of the functions will return you the base
+directory without any of the arguments appended to it.
+*/
+package appdirs
+
+// UserDataDir returns the full path to the user-specific data directory.
+//
+// This function uses XDG_DATA_HOME as defined by the XDG spec on *nix like systems.
+//
+// Examples of return values:
+// Mac OS X: ~/Library/Application Support/<AppName>
+// Unix: ~/.local/share/<AppName> # or in $XDG_DATA_HOME, if defined
+// Win XP (not roaming): C:\Documents and Settings\<username>\Application Data\<AppAuthor>\<AppName>
+// Win XP (roaming): C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>
+// Win 7 (not roaming): C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>
+// Win 7 (roaming): C:\Users\<username>\AppData\Roaming\<AppAuthor>\<AppName>
+func UserDataDir(name, author, version string, roaming bool) string {
+ return userDataDir(name, author, version, roaming)
+}
+
+// SiteDataDir returns the full path to the user-shared data directory.
+//
+// This function uses XDG_DATA_DIRS[0] as by the XDG spec on *nix like systems.
+//
+// Examples of return values:
+// Mac OS X: /Library/Application Support/<AppName>
+// Unix: /usr/local/share/<AppName> or /usr/share/<AppName>
+// Win XP: C:\Documents and Settings\All Users\Application Data\<AppAuthor>\<AppName>
+// Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.)
+// Win 7: C:\ProgramData\<AppAuthor>\<AppName> # Hidden, but writeable on Win 7.
+//
+// WARNING: Do not use this on Windows Vista, See the note above.
+func SiteDataDir(name, author, version string) string {
+ return siteDataDir(name, author, version)
+}
+
+// UserConfigDir returns the full path to the user-specific configuration directory
+//
+// This function uses XDG_CONFIG_HOME as by the XDG spec on *nix like systems.
+//
+// Examples of return values:
+// Mac OS X: same as UserDataDir
+// Unix: ~/.config/<AppName> # or in $XDG_CONFIG_HOME, if defined
+// Win *: same as UserDataDir
+func UserConfigDir(name, author, version string, roaming bool) string {
+ return userConfigDir(name, author, version, roaming)
+}
+
+// SiteConfigDir returns the full path to the user-shared data directory.
+//
+// This function uses XDG_CONFIG_DIRS[0] as by the XDG spec on *nix like systems.
+//
+// Examples of return values:
+// Mac OS X: same as SiteDataDir
+// Unix: /etc/xdg/<AppName> or $XDG_CONFIG_DIRS[i]/<AppName> for each value in $XDG_CONFIG_DIRS
+// Win *: same as SiteDataDir
+// Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.)
+//
+// WARNING: Do not use this on Windows Vista, see the note above.
+func SiteConfigDir(name, author, version string) string {
+ return siteConfigDir(name, author, version)
+}
+
+// UserCacheDir returns the full path to the user-specific cache directory.
+//
+// The opinion argument will append 'Cache' to the base directory if set to true.
+//
+// Examples of return values:
+// Mac OS X: ~/Library/Caches/<AppName>
+// Unix: ~/.cache/<AppName> (XDG default)
+// Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Cache
+// Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Cache
+func UserCacheDir(name, author, version string, opinion bool) string {
+ return userCacheDir(name, author, version, opinion)
+}
+
+// UserLogDir returns the full path to the user-specific log directory.
+//
+// The opinion argument will append either 'Logs' (windows) or 'log' (unix) to
+// the base directory when set to true.
+//
+// Examples of return values:
+// Mac OS X: ~/Library/Logs/<AppName>
+// Unix: ~/.cache/<AppName>/log # or under $XDG_CACHE_HOME if defined
+// Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Logs
+// Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Logs
+func UserLogDir(name, author, version string, opinion bool) string {
+ return userLogDir(name, author, version, opinion)
+}
diff --git a/vendor/github.com/boombuler/barcode/LICENSE b/vendor/github.com/boombuler/barcode/LICENSE
new file mode 100644
index 0000000..862b0dd
--- /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/barcode.go b/vendor/github.com/boombuler/barcode/barcode.go
new file mode 100644
index 0000000..7e147f2
--- /dev/null
+++ b/vendor/github.com/boombuler/barcode/barcode.go
@@ -0,0 +1,21 @@
+package barcode
+
+import "image"
+
+// 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
+ 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 0000000..69cf1a9
--- /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("Codabar", content, resBits, 0), 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 0000000..757347e
--- /dev/null
+++ b/vendor/github.com/boombuler/barcode/code128/encode.go
@@ -0,0 +1,130 @@
+// 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 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 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.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)
+ sum := 0
+ for i, idx := range idxList.GetBytes() {
+ if i == 0 {
+ sum = int(idx)
+ } else {
+ sum += i * int(idx)
+ }
+ result.AddBit(encodingTable[idx]...)
+ }
+ result.AddBit(encodingTable[sum%103]...)
+ result.AddBit(encodingTable[stopSymbol]...)
+ return utils.New1DCode("Code 128", content, result, sum%103), 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 0000000..a32b31c
--- /dev/null
+++ b/vendor/github.com/boombuler/barcode/code128/encodingtable.go
@@ -0,0 +1,133 @@
+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 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 bTable = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
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 0000000..669450f
--- /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.Barcode, 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.New1DCode("Code 39", 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 0000000..923b135
--- /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 0000000..c63eb8c
--- /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 0000000..0e60269
--- /dev/null
+++ b/vendor/github.com/boombuler/barcode/datamatrix/datamatrixcode.go
@@ -0,0 +1,53 @@
+package datamatrix
+
+import (
+ "github.com/boombuler/barcode"
+ "github.com/boombuler/barcode/utils"
+ "image"
+ "image/color"
+)
+
+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{"DataMatrix", 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) CheckSum() int {
+ return 0
+}
+
+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 0000000..b8921dc
--- /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 0000000..ef5876b
--- /dev/null
+++ b/vendor/github.com/boombuler/barcode/datamatrix/errorcorrection.go
@@ -0,0 +1,85 @@
+package datamatrix
+
+import (
+ "github.com/boombuler/barcode/utils"
+)
+
+type errorCorrection struct {
+ fld *utils.GaloisField
+ polynomes map[int][]int
+}
+
+var ec *errorCorrection = newErrorCorrection()
+
+func newErrorCorrection() *errorCorrection {
+ result := new(errorCorrection)
+ result.fld = utils.NewGaloisField(301)
+ result.polynomes = make(map[int][]int)
+ return result
+}
+
+func (ec *errorCorrection) getPolynomial(count int) []int {
+ poly, ok := ec.polynomes[count]
+ if !ok {
+ idx := 1
+ poly = make([]int, count+1)
+ poly[0] = 1
+ for i := 1; i <= count; i++ {
+ poly[i] = 1
+ for j := i - 1; j > 0; j-- {
+ if poly[j] != 0 {
+ poly[j] = ec.fld.ALogTbl[(int(ec.fld.LogTbl[poly[j]])+idx)%255]
+ }
+ poly[j] = ec.fld.AddOrSub(poly[j], poly[j-1])
+ }
+ poly[0] = ec.fld.ALogTbl[(int(ec.fld.LogTbl[poly[0]])+idx)%255]
+ idx++
+ }
+ poly = poly[0:count]
+ ec.polynomes[count] = poly
+ }
+ return poly
+}
+
+func (ec *errorCorrection) calcECCBlock(data []byte, poly []int) []byte {
+ ecc := make([]byte, len(poly)+1)
+
+ for i := 0; i < len(data); i++ {
+ k := ec.fld.AddOrSub(int(ecc[0]), int(data[i]))
+ for j := 0; j < len(ecc)-1; j++ {
+ ecc[j] = byte(ec.fld.AddOrSub(int(ecc[j+1]), ec.fld.Multiply(k, poly[len(ecc)-j-2])))
+ }
+ }
+ return ecc
+}
+
+func (ec *errorCorrection) calcECC(data []byte, size *dmCodeSize) []byte {
+
+ poly := ec.getPolynomial(size.ErrorCorrectionCodewordsPerBlock())
+
+ 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([]byte, dataCnt)
+ // copy the data for the current block to buff
+ j := 0
+ for i := block; i < dataSize; i += size.BlockCount {
+ buff[j] = data[i]
+ j++
+ }
+ // calc the error correction codes
+ ecc := ec.calcECCBlock(buff, poly)
+ // and append them to the result
+ j = 0
+ for i := block; i < size.ErrorCorrectionCodewordsPerBlock()*size.BlockCount; i += size.BlockCount {
+ data[dataSize+i] = 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 0000000..d89294f
--- /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.Barcode, 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.New1DCode("EAN 8", code, result, checkSum), nil
+ }
+ } else if len(code) == 13 {
+ result := encodeEAN13(code)
+ if result != nil {
+ return utils.New1DCode("EAN 13", 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 0000000..4ded7c8
--- /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 0000000..e7c5601
--- /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 0000000..d317378
--- /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 0000000..2c6ab21
--- /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 0000000..950fa82
--- /dev/null
+++ b/vendor/github.com/boombuler/barcode/qr/errorcorrection.go
@@ -0,0 +1,54 @@
+package qr
+
+import (
+ "github.com/boombuler/barcode/utils"
+ "sync"
+)
+
+type errorCorrection struct {
+ fld *utils.GaloisField
+
+ m *sync.Mutex
+ polynomes []*utils.GFPoly
+}
+
+var ec = newGF()
+
+func newGF() *errorCorrection {
+ fld := utils.NewGaloisField(285)
+
+ return &errorCorrection{fld,
+ new(sync.Mutex),
+ []*utils.GFPoly{
+ utils.NewGFPoly(fld, []byte{1}),
+ },
+ }
+}
+
+func (ec *errorCorrection) getPolynomial(degree int) *utils.GFPoly {
+ ec.m.Lock()
+ defer ec.m.Unlock()
+
+ if degree >= len(ec.polynomes) {
+ last := ec.polynomes[len(ec.polynomes)-1]
+ for d := len(ec.polynomes); d <= degree; d++ {
+ next := last.Multiply(utils.NewGFPoly(ec.fld, []byte{1, byte(ec.fld.ALogTbl[d-1])}))
+ ec.polynomes = append(ec.polynomes, next)
+ last = next
+ }
+ }
+ return ec.polynomes[degree]
+}
+
+func (ec *errorCorrection) calcECC(data []byte, eccCount byte) []byte {
+ generator := ec.getPolynomial(int(eccCount))
+ info := utils.NewGFPoly(ec.fld, data)
+ info = info.MultByMonominal(int(eccCount), 1)
+
+ _, remainder := info.Divide(generator)
+
+ result := make([]byte, eccCount)
+ numZero := int(eccCount) - len(remainder.Coefficients)
+ copy(result[numZero:], remainder.Coefficients)
+ 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 0000000..49b44cc
--- /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 0000000..ab123ce
--- /dev/null
+++ b/vendor/github.com/boombuler/barcode/qr/qrcode.go
@@ -0,0 +1,170 @@
+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{"QR Code", 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) CheckSum() int {
+ return 0
+}
+
+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 0000000..a9135ab
--- /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 0000000..6852a57
--- /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 0000000..c59c9fe
--- /dev/null
+++ b/vendor/github.com/boombuler/barcode/scaledbarcode.go
@@ -0,0 +1,115 @@
+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
+}
+
+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 *scaledBarcode) CheckSum() int {
+ return bc.wrapped.CheckSum()
+}
+
+// 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 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 then %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 &scaledBarcode{
+ 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 then %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 &scaledBarcode{
+ 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 0000000..de3ae97
--- /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: 2,
+ 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...)
+
+ kindTxt := ""
+ if interleaved {
+ kindTxt = " (interleaved)"
+ }
+ return utils.New1DCode("2 of 5"+kindTxt, content, resBits, -1), 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 0000000..22a2e30
--- /dev/null
+++ b/vendor/github.com/boombuler/barcode/utils/base1dcode.go
@@ -0,0 +1,48 @@
+// 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
+ 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 *base1DCode) CheckSum() int {
+ return c.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, checksum int) barcode.Barcode {
+ return &base1DCode{bars, codeKind, content, checksum}
+}
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 0000000..5cdb581
--- /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)
+ } else {
+ bl.data[itmIndex] = bl.data[itmIndex] & ^(1 << uint(itmBitShift))
+ }
+}
+
+// GetBit returns the bit at the given index
+func (bl *BitList) GetBit(index int) bool {
+ itmIndex := index / 32
+ itmBitShift := 31 - (index % 32)
+ return ((bl.data[itmIndex] >> 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 0000000..6f077d7
--- /dev/null
+++ b/vendor/github.com/boombuler/barcode/utils/galoisfield.go
@@ -0,0 +1,62 @@
+package utils
+
+// GaloisField encapsulates galois field arithmetics
+type GaloisField struct {
+ ALogTbl []int
+ LogTbl []int
+}
+
+// NewGaloisField creates a new falois field
+func NewGaloisField(pp int) *GaloisField {
+ result := new(GaloisField)
+ fldSize := 256
+
+ result.ALogTbl = make([]int, fldSize)
+ result.LogTbl = make([]int, fldSize)
+
+ x := 1
+ for i := 0; i < fldSize; i++ {
+ result.ALogTbl[i] = x
+ x = x * 2
+ if x >= fldSize {
+ x = (x ^ pp) & (fldSize - 1)
+ }
+ }
+
+ for i := 0; i < fldSize; i++ {
+ result.LogTbl[result.ALogTbl[i]] = int(i)
+ }
+
+ return result
+}
+
+func (gf *GaloisField) Zero() *GFPoly {
+ return NewGFPoly(gf, []byte{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])%255]
+}
+
+// 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])%255]
+}
+
+func (gf *GaloisField) Invers(num int) int {
+ return gf.ALogTbl[255-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 0000000..54686de
--- /dev/null
+++ b/vendor/github.com/boombuler/barcode/utils/gfpoly.go
@@ -0,0 +1,103 @@
+package utils
+
+type GFPoly struct {
+ gf *GaloisField
+ Coefficients []byte
+}
+
+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) byte {
+ 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([]byte, len(largeCoeff))
+ lenDiff := len(largeCoeff) - len(smallCoeff)
+ copy(sumDiff, largeCoeff[:lenDiff])
+ for i := lenDiff; i < len(largeCoeff); i++ {
+ sumDiff[i] = byte(gp.gf.AddOrSub(int(smallCoeff[i-lenDiff]), int(largeCoeff[i])))
+ }
+ return NewGFPoly(gp.gf, sumDiff)
+}
+
+func (gp *GFPoly) MultByMonominal(degree int, coeff byte) *GFPoly {
+ if coeff == 0 {
+ return gp.gf.Zero()
+ }
+ size := len(gp.Coefficients)
+ result := make([]byte, size+degree)
+ for i := 0; i < size; i++ {
+ result[i] = byte(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([]byte, 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] = byte(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 := byte(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 byte) *GFPoly {
+ if coeff == 0 {
+ return field.Zero()
+ }
+ result := make([]byte, degree+1)
+ result[0] = coeff
+ return NewGFPoly(field, result)
+}
+
+func NewGFPoly(field *GaloisField, coefficients []byte) *GFPoly {
+ for len(coefficients) > 1 && coefficients[0] == 0 {
+ coefficients = coefficients[1:]
+ }
+ return &GFPoly{field, coefficients}
+}
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 0000000..d2e5e61
--- /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/flopp/go-coordsparser/LICENSE b/vendor/github.com/flopp/go-coordsparser/LICENSE
new file mode 100644
index 0000000..7417b3d
--- /dev/null
+++ b/vendor/github.com/flopp/go-coordsparser/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Florian Pigorsch
+
+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/flopp/go-coordsparser/coordsparser.go b/vendor/github.com/flopp/go-coordsparser/coordsparser.go
new file mode 100644
index 0000000..6836892
--- /dev/null
+++ b/vendor/github.com/flopp/go-coordsparser/coordsparser.go
@@ -0,0 +1,172 @@
+// Copyright 2016 Florian Pigorsch. All rights reserved.
+//
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package coordsparser is a library for parsing (geographic) coordinates in various string formats
+package coordsparser
+
+import (
+ "fmt"
+ "regexp"
+ "strconv"
+)
+
+// Parse parses a coordinate string and returns a lat/lng pair or an error
+func Parse(s string) (float64, float64, error) {
+ lat, lng, err := ParseD(s)
+ if err == nil {
+ return lat, lng, nil
+ }
+
+ lat, lng, err = ParseHD(s)
+ if err == nil {
+ return lat, lng, nil
+ }
+
+ lat, lng, err = ParseHDM(s)
+ if err == nil {
+ return lat, lng, nil
+ }
+
+ lat, lng, err = ParseHDMS(s)
+ if err == nil {
+ return lat, lng, nil
+ }
+
+ return 0, 0, fmt.Errorf("Cannot parse coordinates: %s", s)
+}
+
+// ParseD parses a coordinate string of the form "D.DDDD D.DDDD" and returns a lat/lng pair or an error
+func ParseD(s string) (float64, float64, error) {
+ re := regexp.MustCompile(`^\s*([+-]?[\d\.]+)\s*(,|;|:|\s)\s*([+-]?[\d\.]+)\s*$`)
+
+ matches := re.FindStringSubmatch(s)
+ if matches == nil {
+ return 0, 0, fmt.Errorf("Cannot parse 'D' string: %s", s)
+ }
+
+ lat, err := strconv.ParseFloat(matches[1], 64)
+ if err != nil || lat < -90 || lat > 90 {
+ return 0, 0, fmt.Errorf("Cannot parse 'D' string: %s", s)
+ }
+
+ lng, err := strconv.ParseFloat(matches[3], 64)
+ if err != nil || lng < -180 || lng > 180 {
+ return 0, 0, fmt.Errorf("Cannot parse 'D' string: %s", s)
+ }
+
+ return lat, lng, nil
+}
+
+// ParseHD parses a coordinate string of the form "H D.DDDD H D.DDDD" and returns a lat/lng pair or an error
+func ParseHD(s string) (float64, float64, error) {
+ re := regexp.MustCompile(`^\s*([NnSs])\s*([\d\.]+)\s+([EeWw])\s*([\d\.]+)\s*$`)
+
+ matches := re.FindStringSubmatch(s)
+ if matches == nil {
+ return 0, 0, fmt.Errorf("Cannot parse 'HD' string: %s", s)
+ }
+
+ lat, err := strconv.ParseFloat(matches[2], 64)
+ if err != nil || lat > 90 {
+ return 0, 0, fmt.Errorf("Cannot parse 'HD' string: %s", s)
+ }
+ if matches[1] == "S" || matches[1] == "s" {
+ lat = -lat
+ }
+
+ lng, err := strconv.ParseFloat(matches[4], 64)
+ if err != nil || lng > 180 {
+ return 0, 0, fmt.Errorf("Cannot parse 'HD' string: %s", s)
+ }
+ if matches[3] == "W" || matches[3] == "w" {
+ lng = -lng
+ }
+
+ return lat, lng, nil
+}
+
+// ParseHDM parses a coordinate string of the form "H D M.MMM H D M.MMM" and returns a lat/lng pair or an error
+func ParseHDM(s string) (float64, float64, error) {
+ re := regexp.MustCompile(`^\s*([NnSs])\s*([\d]+)\s+([\d.]+)\s+([EeWw])\s*([\d]+)\s+([\d.]+)\s*$`)
+
+ matches := re.FindStringSubmatch(s)
+ if matches == nil {
+ return 0, 0, fmt.Errorf("Cannot parse 'HDM' string: %s", s)
+ }
+
+ latDeg, err := strconv.ParseFloat(matches[2], 64)
+ if err != nil || latDeg > 90 {
+ return 0, 0, fmt.Errorf("Cannot parse 'HDM' string: %s", s)
+ }
+ latMin, err := strconv.ParseFloat(matches[3], 64)
+ if err != nil || latMin >= 60 {
+ return 0, 0, fmt.Errorf("Cannot parse 'HDM' string: %s", s)
+ }
+ lat := latDeg + latMin/60.0
+ if matches[1] == "S" || matches[1] == "s" {
+ lat = -lat
+ }
+
+ lngDeg, err := strconv.ParseFloat(matches[5], 64)
+ if err != nil || lngDeg > 180 {
+ return 0, 0, fmt.Errorf("Cannot parse 'HDM' string: %s", s)
+ }
+ lngMin, err := strconv.ParseFloat(matches[6], 64)
+ if err != nil || lngMin >= 60 {
+ return 0, 0, fmt.Errorf("Cannot parse 'HDM' string: %s", s)
+ }
+ lng := lngDeg + lngMin/60.0
+ if matches[4] == "W" || matches[4] == "w" {
+ lng = -lng
+ }
+
+ return lat, lng, nil
+}
+
+// ParseHDMS parses a coordinate string of the form "H D M S.SSS H D M S.SSS" and returns a lat/lng pair or an error
+func ParseHDMS(s string) (float64, float64, error) {
+ re := regexp.MustCompile(`^\s*([NnSs])\s*([\d]+)\s+([\d]+)\s+([\d.]+)\s+([EeWw])\s*([\d]+)\s+([\d]+)\s+([\d.]+)\s*$`)
+
+ matches := re.FindStringSubmatch(s)
+ if matches == nil {
+ return 0, 0, fmt.Errorf("Cannot parse 'HDMS' string: %s", s)
+ }
+
+ latDeg, err := strconv.ParseFloat(matches[2], 64)
+ if err != nil || latDeg > 90 {
+ return 0, 0, fmt.Errorf("Cannot parse 'HDMS' string: %s", s)
+ }
+ latMin, err := strconv.ParseFloat(matches[3], 64)
+ if err != nil || latMin >= 60 {
+ return 0, 0, fmt.Errorf("Cannot parse 'HDMS' string: %s", s)
+ }
+ latSec, err := strconv.ParseFloat(matches[4], 64)
+ if err != nil || latSec >= 60 {
+ return 0, 0, fmt.Errorf("Cannot parse 'HDMS' string: %s", s)
+ }
+ lat := latDeg + latMin/60.0 + latSec/3600.0
+ if matches[1] == "S" || matches[1] == "s" {
+ lat = -lat
+ }
+
+ lngDeg, err := strconv.ParseFloat(matches[6], 64)
+ if err != nil || lngDeg > 180 {
+ return 0, 0, fmt.Errorf("Cannot parse 'HDMS' string: %s", s)
+ }
+ lngMin, err := strconv.ParseFloat(matches[7], 64)
+ if err != nil || lngMin >= 60 {
+ return 0, 0, fmt.Errorf("Cannot parse 'HDMS' string: %s", s)
+ }
+ lngSec, err := strconv.ParseFloat(matches[8], 64)
+ if err != nil || lngSec >= 60 {
+ return 0, 0, fmt.Errorf("Cannot parse 'HDMS' string: %s", s)
+ }
+ lng := lngDeg + lngMin/60.0 + lngSec/3600.0
+ if matches[5] == "W" || matches[5] == "w" {
+ lng = -lng
+ }
+
+ return lat, lng, nil
+}
diff --git a/vendor/github.com/flopp/go-staticmaps/LICENSE b/vendor/github.com/flopp/go-staticmaps/LICENSE
new file mode 100644
index 0000000..7417b3d
--- /dev/null
+++ b/vendor/github.com/flopp/go-staticmaps/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Florian Pigorsch
+
+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/flopp/go-staticmaps/area.go b/vendor/github.com/flopp/go-staticmaps/area.go
new file mode 100644
index 0000000..387105a
--- /dev/null
+++ b/vendor/github.com/flopp/go-staticmaps/area.go
@@ -0,0 +1,94 @@
+// Copyright 2016 Florian Pigorsch. All rights reserved.
+//
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package sm
+
+import (
+ "image/color"
+ "strconv"
+ "strings"
+
+ "github.com/flopp/go-coordsparser"
+ "github.com/fogleman/gg"
+ "github.com/golang/geo/s2"
+)
+
+// Area represents a area or area on the map
+type Area struct {
+ MapObject
+ Positions []s2.LatLng
+ Color color.Color
+ Fill color.Color
+ Weight float64
+}
+
+// ParseAreaString parses a string and returns an area
+func ParseAreaString(s string) (*Area, error) {
+ area := new(Area)
+ area.Color = color.RGBA{0xff, 0, 0, 0xff}
+ area.Fill = color.Transparent
+ area.Weight = 5.0
+
+ for _, ss := range strings.Split(s, "|") {
+ if ok, suffix := hasPrefix(ss, "color:"); ok {
+ var err error
+ area.Color, err = ParseColorString(suffix)
+ if err != nil {
+ return nil, err
+ }
+ } else if ok, suffix := hasPrefix(ss, "fill:"); ok {
+ var err error
+ area.Fill, err = ParseColorString(suffix)
+ if err != nil {
+ return nil, err
+ }
+ } else if ok, suffix := hasPrefix(ss, "weight:"); ok {
+ var err error
+ area.Weight, err = strconv.ParseFloat(suffix, 64)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ lat, lng, err := coordsparser.Parse(ss)
+ if err != nil {
+ return nil, err
+ }
+ area.Positions = append(area.Positions, s2.LatLngFromDegrees(lat, lng))
+ }
+
+ }
+ return area, nil
+}
+
+func (p *Area) extraMarginPixels() float64 {
+ return 0.5 * p.Weight
+}
+
+func (p *Area) bounds() s2.Rect {
+ r := s2.EmptyRect()
+ for _, ll := range p.Positions {
+ r = r.AddPoint(ll)
+ }
+ return r
+}
+
+func (p *Area) draw(gc *gg.Context, trans *transformer) {
+ if len(p.Positions) <= 1 {
+ return
+ }
+
+ gc.ClearPath()
+ gc.SetLineWidth(p.Weight)
+ gc.SetLineCap(gg.LineCapRound)
+ gc.SetLineJoin(gg.LineJoinRound)
+ for _, ll := range p.Positions {
+ gc.LineTo(trans.ll2p(ll))
+ }
+ gc.ClosePath()
+ gc.SetColor(p.Fill)
+ gc.FillPreserve()
+ gc.SetColor(p.Color)
+ gc.Stroke()
+}
diff --git a/vendor/github.com/flopp/go-staticmaps/color.go b/vendor/github.com/flopp/go-staticmaps/color.go
new file mode 100644
index 0000000..ea73ffd
--- /dev/null
+++ b/vendor/github.com/flopp/go-staticmaps/color.go
@@ -0,0 +1,65 @@
+// Copyright 2016 Florian Pigorsch. All rights reserved.
+//
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package sm
+
+import (
+ "fmt"
+ "image/color"
+ "regexp"
+ "strings"
+)
+
+// ParseColorString parses hex color strings (i.e. `0xRRGGBB`, `#RRGGBB`, `0xRRGGBBAA`, `#RRGGBBAA`), and names colors (e.g. 'black', 'blue', ...)
+func ParseColorString(s string) (color.Color, error) {
+ s = strings.ToLower(strings.TrimSpace(s))
+
+ re := regexp.MustCompile(`^(0x|#)([A-Fa-f0-9]{6})$`)
+ matches := re.FindStringSubmatch(s)
+ if matches != nil {
+ var r, g, b int
+ fmt.Sscanf(matches[2], "%2x%2x%2x", &r, &g, &b)
+ return color.RGBA{uint8(r), uint8(g), uint8(b), 0xff}, nil
+ }
+
+ re = regexp.MustCompile(`^(0x|#)([A-Fa-f0-9]{8})$`)
+ matches = re.FindStringSubmatch(s)
+ if matches != nil {
+ var r, g, b, a int
+ fmt.Sscanf(matches[2], "%2x%2x%2x%2x", &r, &g, &b, &a)
+ rr := float64(r) * float64(a) / 256.0
+ gg := float64(g) * float64(a) / 256.0
+ bb := float64(b) * float64(a) / 256.0
+ return color.RGBA{uint8(rr), uint8(gg), uint8(bb), uint8(a)}, nil
+ }
+
+ switch s {
+ case "black":
+ return color.RGBA{0x00, 0x00, 0x00, 0xff}, nil
+ case "blue":
+ return color.RGBA{0x00, 0x00, 0xff, 0xff}, nil
+ case "brown":
+ return color.RGBA{0x96, 0x4b, 0x00, 0xff}, nil
+ case "green":
+ return color.RGBA{0x00, 0xff, 0x00, 0xff}, nil
+ case "orange":
+ return color.RGBA{0xff, 0x7f, 0x00, 0xff}, nil
+ case "purple":
+ return color.RGBA{0x7f, 0x00, 0x7f, 0xff}, nil
+ case "red":
+ return color.RGBA{0xff, 0x00, 0x00, 0xff}, nil
+ case "yellow":
+ return color.RGBA{0xff, 0xff, 0x00, 0xff}, nil
+ case "white":
+ return color.RGBA{0xff, 0xff, 0xff, 0xff}, nil
+ }
+ return color.Transparent, fmt.Errorf("Cannot parse color string: %s", s)
+}
+
+// Luminance computes the luminance (~ brightness) of the given color. Range: 0.0 for black to 1.0 for white.
+func Luminance(col color.Color) float64 {
+ r, g, b, _ := col.RGBA()
+ return (float64(r)*0.299 + float64(g)*0.587 + float64(b)*0.114) / float64(0xffff)
+}
diff --git a/vendor/github.com/flopp/go-staticmaps/context.go b/vendor/github.com/flopp/go-staticmaps/context.go
new file mode 100644
index 0000000..3541f44
--- /dev/null
+++ b/vendor/github.com/flopp/go-staticmaps/context.go
@@ -0,0 +1,277 @@
+// Copyright 2016 Florian Pigorsch. All rights reserved.
+//
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package sm (~ static maps) renders static map images from OSM tiles with markers, paths, and filled areas.
+package sm
+
+import (
+ "errors"
+ "image"
+ "image/draw"
+ "math"
+
+ "github.com/fogleman/gg"
+ "github.com/golang/geo/s2"
+)
+
+// Context holds all information about the map image that is to be rendered
+type Context struct {
+ width int
+ height int
+
+ hasZoom bool
+ zoom int
+
+ hasCenter bool
+ center s2.LatLng
+
+ markers []*Marker
+ paths []*Path
+ areas []*Area
+
+ tileProvider *TileProvider
+}
+
+// NewContext creates a new instance of Context
+func NewContext() *Context {
+ t := new(Context)
+ t.width = 512
+ t.height = 512
+ t.hasZoom = false
+ t.hasCenter = false
+ t.tileProvider = NewTileProviderMapQuest()
+ return t
+}
+
+// SetTileProvider sets the TileProvider to be used
+func (m *Context) SetTileProvider(t *TileProvider) {
+ m.tileProvider = t
+}
+
+// SetSize sets the size of the generated image
+func (m *Context) SetSize(width, height int) {
+ m.width = width
+ m.height = height
+}
+
+// SetZoom sets the zoom level
+func (m *Context) SetZoom(zoom int) {
+ m.zoom = zoom
+ m.hasZoom = true
+}
+
+// SetCenter sets the center coordinates
+func (m *Context) SetCenter(center s2.LatLng) {
+ m.center = center
+ m.hasCenter = true
+}
+
+// AddMarker adds a marker to the Context
+func (m *Context) AddMarker(marker *Marker) {
+ m.markers = append(m.markers, marker)
+}
+
+// ClearMarkers removes all markers from the Context
+func (m *Context) ClearMarkers() {
+ m.markers = nil
+}
+
+// AddPath adds a path to the Context
+func (m *Context) AddPath(path *Path) {
+ m.paths = append(m.paths, path)
+}
+
+// ClearPaths removes all paths from the Context
+func (m *Context) ClearPaths() {
+ m.paths = nil
+}
+
+// AddArea adds an area to the Context
+func (m *Context) AddArea(area *Area) {
+ m.areas = append(m.areas, area)
+}
+
+// ClearAreas removes all areas from the Context
+func (m *Context) ClearAreas() {
+ m.areas = nil
+}
+
+func (m *Context) determineBounds() s2.Rect {
+ r := s2.EmptyRect()
+ for _, marker := range m.markers {
+ r = r.Union(marker.bounds())
+ }
+ for _, path := range m.paths {
+ r = r.Union(path.bounds())
+ }
+ for _, area := range m.areas {
+ r = r.Union(area.bounds())
+ }
+ return r
+}
+
+func (m *Context) determineExtraMarginPixels() float64 {
+ p := 0.0
+ for _, marker := range m.markers {
+ if pp := marker.extraMarginPixels(); pp > p {
+ p = pp
+ }
+ }
+ for _, path := range m.paths {
+ if pp := path.extraMarginPixels(); pp > p {
+ p = pp
+ }
+ }
+ for _, area := range m.areas {
+ if pp := area.extraMarginPixels(); pp > p {
+ p = pp
+ }
+ }
+ return p
+}
+
+func (m *Context) determineZoom(bounds s2.Rect, center s2.LatLng) int {
+ b := bounds.AddPoint(center)
+ if b.IsEmpty() || b.IsPoint() {
+ return 15
+ }
+
+ tileSize := m.tileProvider.TileSize
+ margin := 4.0 + m.determineExtraMarginPixels()
+ w := (float64(m.width) - 2.0*margin) / float64(tileSize)
+ h := (float64(m.height) - 2.0*margin) / float64(tileSize)
+ minX := (b.Lo().Lng.Degrees() + 180.0) / 360.0
+ maxX := (b.Hi().Lng.Degrees() + 180.0) / 360.0
+ minY := (1.0 - math.Log(math.Tan(b.Lo().Lat.Radians())+(1.0/math.Cos(b.Lo().Lat.Radians())))/math.Pi) / 2.0
+ maxY := (1.0 - math.Log(math.Tan(b.Hi().Lat.Radians())+(1.0/math.Cos(b.Hi().Lat.Radians())))/math.Pi) / 2.0
+ dx := math.Abs(maxX - minX)
+ dy := math.Abs(maxY - minY)
+
+ zoom := 1
+ for zoom < 30 {
+ tiles := float64(uint(1) << uint(zoom))
+ if dx*tiles > w || dy*tiles > h {
+ return zoom - 1
+ }
+ zoom = zoom + 1
+ }
+
+ return 15
+}
+
+type transformer struct {
+ zoom int
+ tileSize int
+ pWidth, pHeight int
+ pCenterX, pCenterY int
+ tCountX, tCountY int
+ tCenterX, tCenterY float64
+ tOriginX, tOriginY int
+}
+
+func newTransformer(width int, height int, zoom int, llCenter s2.LatLng, tileSize int) *transformer {
+ t := new(transformer)
+ t.zoom = zoom
+ t.tileSize = tileSize
+ t.tCenterX, t.tCenterY = t.ll2t(llCenter)
+
+ ww := float64(width) / float64(tileSize)
+ hh := float64(height) / float64(tileSize)
+
+ t.tOriginX = int(math.Floor(t.tCenterX - 0.5*ww))
+ t.tOriginY = int(math.Floor(t.tCenterY - 0.5*hh))
+
+ t.tCountX = 1 + int(math.Floor(t.tCenterX+0.5*ww)) - t.tOriginX
+ t.tCountY = 1 + int(math.Floor(t.tCenterY+0.5*hh)) - t.tOriginY
+
+ t.pWidth = t.tCountX * tileSize
+ t.pHeight = t.tCountY * tileSize
+
+ t.pCenterX = int((t.tCenterX - float64(t.tOriginX)) * float64(tileSize))
+ t.pCenterY = int((t.tCenterY - float64(t.tOriginY)) * float64(tileSize))
+
+ return t
+}
+
+func (t *transformer) ll2t(ll s2.LatLng) (float64, float64) {
+ tiles := math.Exp2(float64(t.zoom))
+ x := tiles * (ll.Lng.Degrees() + 180.0) / 360.0
+ y := tiles * (1 - math.Log(math.Tan(ll.Lat.Radians())+(1.0/math.Cos(ll.Lat.Radians())))/math.Pi) / 2.0
+ return x, y
+}
+
+func (t *transformer) ll2p(ll s2.LatLng) (float64, float64) {
+ x, y := t.ll2t(ll)
+ x = float64(t.pCenterX) + (x-t.tCenterX)*float64(t.tileSize)
+ y = float64(t.pCenterY) + (y-t.tCenterY)*float64(t.tileSize)
+ return x, y
+}
+
+// Render actually renders the map image including all map objects (markers, paths, areas)
+func (m *Context) Render() (image.Image, error) {
+ bounds := m.determineBounds()
+
+ center := m.center
+ if !m.hasCenter {
+ if bounds.IsEmpty() {
+ return nil, errors.New("No center coordinates specified, cannot determine center from markers")
+ }
+ center = bounds.Center()
+ }
+
+ zoom := m.zoom
+ if !m.hasZoom {
+ zoom = m.determineZoom(bounds, center)
+ }
+
+ tileSize := m.tileProvider.TileSize
+ trans := newTransformer(m.width, m.height, zoom, center, tileSize)
+ img := image.NewRGBA(image.Rect(0, 0, trans.pWidth, trans.pHeight))
+ gc := gg.NewContextForRGBA(img)
+
+ // fetch and draw tiles to img
+ t := NewTileFetcher(m.tileProvider)
+ for xx := 0; xx < trans.tCountX; xx++ {
+ x := trans.tOriginX + xx
+ if x < 0 {
+ x = x + (1 << uint(zoom))
+ }
+ for yy := 0; yy < trans.tCountY; yy++ {
+ y := trans.tOriginY + yy
+ if tileImg, err := t.Fetch(zoom, x, y); err == nil {
+ gc.DrawImage(tileImg, xx*tileSize, yy*tileSize)
+ }
+ }
+ }
+
+ // draw map objects
+ for _, area := range m.areas {
+ area.draw(gc, trans)
+ }
+ for _, path := range m.paths {
+ path.draw(gc, trans)
+ }
+ for _, marker := range m.markers {
+ marker.draw(gc, trans)
+ }
+
+ // crop image
+ croppedImg := image.NewRGBA(image.Rect(0, 0, int(m.width), int(m.height)))
+ draw.Draw(croppedImg, image.Rect(0, 0, int(m.width), int(m.height)),
+ img, image.Point{trans.pCenterX - int(m.width)/2, trans.pCenterY - int(m.height)/2},
+ draw.Src)
+
+ // draw attribution
+ _, textHeight := gc.MeasureString(m.tileProvider.Attribution)
+ boxHeight := textHeight + 4.0
+ gc = gg.NewContextForRGBA(croppedImg)
+ gc.SetRGBA(0.0, 0.0, 0.0, 0.5)
+ gc.DrawRectangle(0.0, float64(m.height)-boxHeight, float64(m.width), boxHeight)
+ gc.Fill()
+ gc.SetRGBA(1.0, 1.0, 1.0, 0.75)
+ gc.DrawString(m.tileProvider.Attribution, 4.0, float64(m.height)-4.0)
+
+ return croppedImg, nil
+}
diff --git a/vendor/github.com/flopp/go-staticmaps/create-static-map/create-static-map.go b/vendor/github.com/flopp/go-staticmaps/create-static-map/create-static-map.go
new file mode 100644
index 0000000..7b8d429
--- /dev/null
+++ b/vendor/github.com/flopp/go-staticmaps/create-static-map/create-static-map.go
@@ -0,0 +1,134 @@
+// Copyright 2016 Florian Pigorsch. All rights reserved.
+//
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "fmt"
+ "log"
+ "os"
+ "sort"
+
+ "github.com/flopp/go-coordsparser"
+ "github.com/flopp/go-staticmaps"
+ "github.com/fogleman/gg"
+ "github.com/golang/geo/s2"
+ "github.com/jessevdk/go-flags"
+)
+
+func getTileProviderOrExit(name string) *sm.TileProvider {
+ tileProviders := sm.GetTileProviders()
+ tp := tileProviders[name]
+ if tp != nil {
+ return tp
+ }
+
+ if name != "list" {
+ fmt.Println("Bad map type:", name)
+ }
+ fmt.Println("Possible map types (to be used with --type/-t):")
+ // print sorted keys
+ keys := make([]string, 0, len(tileProviders))
+ for k := range tileProviders {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+ for _, k := range keys {
+ fmt.Println(k)
+ }
+ os.Exit(0)
+
+ return nil
+}
+
+func main() {
+ var opts struct {
+ // ClearCache bool `long:"clear-cache" description:"Clears the tile cache"`
+ Width int `long:"width" description:"Width of the generated static map image" value-name:"PIXELS" default:"512"`
+ Height int `long:"height" description:"Height of the generated static map image" value-name:"PIXELS" default:"512"`
+ Output string `short:"o" long:"output" description:"Output file name" value-name:"FILENAME" default:"map.png"`
+ Type string `short:"t" long:"type" description:"Select the map type; list possible map types with '--type list'" value-name:"MAPTYPE"`
+ Center string `short:"c" long:"center" description:"Center coordinates (lat,lng) of the static map" value-name:"LATLNG"`
+ Zoom int `short:"z" long:"zoom" description:"Zoom factor" value-name:"ZOOMLEVEL"`
+ Markers []string `short:"m" long:"marker" description:"Add a marker to the static map" value-name:"MARKER"`
+ Paths []string `short:"p" long:"path" description:"Add a path to the static map" value-name:"PATH"`
+ Areas []string `short:"a" long:"area" description:"Add an area to the static map" value-name:"AREA"`
+ }
+
+ parser := flags.NewParser(&opts, flags.HelpFlag|flags.PassDoubleDash)
+ parser.LongDescription = `Creates a static map`
+ _, err := parser.Parse()
+
+ if parser.FindOptionByLongName("help").IsSet() {
+ parser.WriteHelp(os.Stdout)
+ os.Exit(0)
+ }
+
+ ctx := sm.NewContext()
+
+ if parser.FindOptionByLongName("type").IsSet() {
+ tp := getTileProviderOrExit(opts.Type)
+ if tp != nil {
+ ctx.SetTileProvider(tp)
+ }
+ }
+
+ ctx.SetSize(opts.Width, opts.Height)
+
+ if parser.FindOptionByLongName("zoom").IsSet() {
+ ctx.SetZoom(opts.Zoom)
+ }
+
+ if parser.FindOptionByLongName("center").IsSet() {
+ lat, lng, err := coordsparser.Parse(opts.Center)
+ if err != nil {
+ log.Fatal(err)
+ } else {
+ ctx.SetCenter(s2.LatLngFromDegrees(lat, lng))
+ }
+ }
+
+ for _, markerString := range opts.Markers {
+ markers, err := sm.ParseMarkerString(markerString)
+ if err != nil {
+ log.Fatal(err)
+ } else {
+ for _, marker := range markers {
+ ctx.AddMarker(marker)
+ }
+ }
+ }
+
+ for _, pathString := range opts.Paths {
+ paths, err := sm.ParsePathString(pathString)
+ if err != nil {
+ log.Fatal(err)
+ } else {
+ for _, path := range paths {
+ ctx.AddPath(path)
+ }
+ }
+ }
+
+ for _, areaString := range opts.Areas {
+ area, err := sm.ParseAreaString(areaString)
+ if err != nil {
+ log.Fatal(err)
+ } else {
+ ctx.AddArea(area)
+ }
+ }
+
+ img, err := ctx.Render()
+ if err != nil {
+ log.Fatal(err)
+ return
+ }
+
+ if err = gg.SavePNG(opts.Output, img); err != nil {
+ log.Fatal(err)
+ return
+ }
+}
diff --git a/vendor/github.com/flopp/go-staticmaps/map_object.go b/vendor/github.com/flopp/go-staticmaps/map_object.go
new file mode 100644
index 0000000..d174b51
--- /dev/null
+++ b/vendor/github.com/flopp/go-staticmaps/map_object.go
@@ -0,0 +1,13 @@
+package sm
+
+import (
+ "github.com/fogleman/gg"
+ "github.com/golang/geo/s2"
+)
+
+// MapObject is the interface for all objects on the map
+type MapObject interface {
+ bounds() s2.Rect
+ extraMarginPixels() float64
+ draw(dc *gg.Context, trans *transformer)
+}
diff --git a/vendor/github.com/flopp/go-staticmaps/marker.go b/vendor/github.com/flopp/go-staticmaps/marker.go
new file mode 100644
index 0000000..6e88984
--- /dev/null
+++ b/vendor/github.com/flopp/go-staticmaps/marker.go
@@ -0,0 +1,125 @@
+// Copyright 2016 Florian Pigorsch. All rights reserved.
+//
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package sm
+
+import (
+ "fmt"
+ "image/color"
+ "math"
+ "strconv"
+ "strings"
+
+ "github.com/flopp/go-coordsparser"
+ "github.com/fogleman/gg"
+ "github.com/golang/geo/s2"
+)
+
+// Marker represents a marker on the map
+type Marker struct {
+ MapObject
+ Position s2.LatLng
+ Color color.Color
+ Size float64
+ Label string
+}
+
+// NewMarker creates a new Marker
+func NewMarker(pos s2.LatLng, col color.Color, size float64) *Marker {
+ m := new(Marker)
+ m.Position = pos
+ m.Color = col
+ m.Size = size
+ m.Label = ""
+ return m
+}
+
+func parseSizeString(s string) (float64, error) {
+ switch {
+ case s == "mid":
+ return 16.0, nil
+ case s == "small":
+ return 12.0, nil
+ case s == "tiny":
+ return 8.0, nil
+ }
+
+ if ss, err := strconv.ParseFloat(s, 64); err != nil && ss > 0 {
+ return ss, nil
+ }
+
+ return 0.0, fmt.Errorf("Cannot parse size string: %s", s)
+}
+
+// ParseMarkerString parses a string and returns an array of markers
+func ParseMarkerString(s string) ([]*Marker, error) {
+ markers := make([]*Marker, 0, 0)
+
+ var color color.Color = color.RGBA{0xff, 0, 0, 0xff}
+ size := 16.0
+ label := ""
+
+ for _, ss := range strings.Split(s, "|") {
+ if ok, suffix := hasPrefix(ss, "color:"); ok {
+ var err error
+ color, err = ParseColorString(suffix)
+ if err != nil {
+ return nil, err
+ }
+ } else if ok, suffix := hasPrefix(ss, "label:"); ok {
+ label = suffix
+ } else if ok, suffix := hasPrefix(ss, "size:"); ok {
+ var err error
+ size, err = parseSizeString(suffix)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ lat, lng, err := coordsparser.Parse(ss)
+ if err != nil {
+ return nil, err
+ }
+ m := NewMarker(s2.LatLngFromDegrees(lat, lng), color, size)
+ m.Label = label
+ markers = append(markers, m)
+ }
+ }
+ return markers, nil
+}
+
+func (m *Marker) extraMarginPixels() float64 {
+ return 1.0 + 1.5*m.Size
+}
+
+func (m *Marker) bounds() s2.Rect {
+ r := s2.EmptyRect()
+ r = r.AddPoint(m.Position)
+ return r
+}
+
+func (m *Marker) draw(gc *gg.Context, trans *transformer) {
+ gc.ClearPath()
+ gc.SetLineJoin(gg.LineJoinRound)
+ gc.SetLineWidth(1.0)
+
+ radius := 0.5 * m.Size
+ x, y := trans.ll2p(m.Position)
+ gc.DrawArc(x, y-m.Size, radius, (90.0+60.0)*math.Pi/180.0, (360.0+90.0-60.0)*math.Pi/180.0)
+ gc.LineTo(x, y)
+ gc.ClosePath()
+ gc.SetColor(m.Color)
+ gc.FillPreserve()
+ gc.SetRGB(0, 0, 0)
+ gc.Stroke()
+
+ if m.Label != "" {
+ if Luminance(m.Color) >= 0.5 {
+ gc.SetColor(color.RGBA{0x00, 0x00, 0x00, 0xff})
+ } else {
+ gc.SetColor(color.RGBA{0xff, 0xff, 0xff, 0xff})
+ }
+ gc.DrawStringAnchored(m.Label, x, y-m.Size, 0.5, 0.5)
+ }
+}
diff --git a/vendor/github.com/flopp/go-staticmaps/path.go b/vendor/github.com/flopp/go-staticmaps/path.go
new file mode 100644
index 0000000..1c580e6
--- /dev/null
+++ b/vendor/github.com/flopp/go-staticmaps/path.go
@@ -0,0 +1,103 @@
+// Copyright 2016 Florian Pigorsch. All rights reserved.
+//
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package sm
+
+import (
+ "image/color"
+ "strconv"
+ "strings"
+
+ "github.com/flopp/go-coordsparser"
+ "github.com/fogleman/gg"
+ "github.com/golang/geo/s2"
+ "github.com/tkrajina/gpxgo/gpx"
+)
+
+// Path represents a path or area on the map
+type Path struct {
+ MapObject
+ Positions []s2.LatLng
+ Color color.Color
+ Weight float64
+}
+
+// ParsePathString parses a string and returns a path
+func ParsePathString(s string) ([]*Path, error) {
+ paths := make([]*Path, 0, 0)
+ currentPath := new(Path)
+ currentPath.Color = color.RGBA{0xff, 0, 0, 0xff}
+ currentPath.Weight = 5.0
+
+ for _, ss := range strings.Split(s, "|") {
+ if ok, suffix := hasPrefix(ss, "color:"); ok {
+ var err error
+ if currentPath.Color, err = ParseColorString(suffix); err != nil {
+ return nil, err
+ }
+ } else if ok, suffix := hasPrefix(ss, "weight:"); ok {
+ var err error
+ if currentPath.Weight, err = strconv.ParseFloat(suffix, 64); err != nil {
+ return nil, err
+ }
+ } else if ok, suffix := hasPrefix(ss, "gpx:"); ok {
+ gpxData, err := gpx.ParseFile(suffix)
+ if err != nil {
+ return nil, err
+ }
+ for _, trk := range gpxData.Tracks {
+ for _, seg := range trk.Segments {
+ p := new(Path)
+ p.Color = currentPath.Color
+ p.Weight = currentPath.Weight
+ for _, pt := range seg.Points {
+ p.Positions = append(p.Positions, s2.LatLngFromDegrees(pt.GetLatitude(), pt.GetLongitude()))
+ }
+ if len(p.Positions) > 0 {
+ paths = append(paths, p)
+ }
+ }
+ }
+ } else {
+ lat, lng, err := coordsparser.Parse(ss)
+ if err != nil {
+ return nil, err
+ }
+ currentPath.Positions = append(currentPath.Positions, s2.LatLngFromDegrees(lat, lng))
+ }
+ }
+ if len(currentPath.Positions) > 0 {
+ paths = append(paths, currentPath)
+ }
+ return paths, nil
+}
+
+func (p *Path) extraMarginPixels() float64 {
+ return 0.5 * p.Weight
+}
+
+func (p *Path) bounds() s2.Rect {
+ r := s2.EmptyRect()
+ for _, ll := range p.Positions {
+ r = r.AddPoint(ll)
+ }
+ return r
+}
+
+func (p *Path) draw(gc *gg.Context, trans *transformer) {
+ if len(p.Positions) <= 1 {
+ return
+ }
+
+ gc.ClearPath()
+ gc.SetLineWidth(p.Weight)
+ gc.SetLineCap(gg.LineCapRound)
+ gc.SetLineJoin(gg.LineJoinRound)
+ for _, ll := range p.Positions {
+ gc.LineTo(trans.ll2p(ll))
+ }
+ gc.SetColor(p.Color)
+ gc.Stroke()
+}
diff --git a/vendor/github.com/flopp/go-staticmaps/tile_fetcher.go b/vendor/github.com/flopp/go-staticmaps/tile_fetcher.go
new file mode 100644
index 0000000..e02a71f
--- /dev/null
+++ b/vendor/github.com/flopp/go-staticmaps/tile_fetcher.go
@@ -0,0 +1,153 @@
+// Copyright 2016 Florian Pigorsch. All rights reserved.
+//
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package sm
+
+import (
+ "bytes"
+ "fmt"
+ "image"
+ _ "image/jpeg" // to be able to decode jpegs
+ _ "image/png" // to be able to decode pngs
+ "io"
+ "io/ioutil"
+ "log"
+ "net/http"
+ "os"
+ "path/filepath"
+
+ "github.com/Wessie/appdirs"
+)
+
+// TileFetcher downloads map tile images from a TileProvider
+type TileFetcher struct {
+ tileProvider *TileProvider
+ cacheDir string
+ useCaching bool
+}
+
+// NewTileFetcher creates a new Tilefetcher struct
+func NewTileFetcher(tileProvider *TileProvider) *TileFetcher {
+ t := new(TileFetcher)
+ t.tileProvider = tileProvider
+ app := appdirs.New("go-staticmaps", "flopp.net", "0.1")
+ t.cacheDir = fmt.Sprintf("%s/%s", app.UserCache(), tileProvider.Name)
+ t.useCaching = true
+ return t
+}
+
+func (t *TileFetcher) url(zoom, x, y int) string {
+ shard := ""
+ ss := len(t.tileProvider.Shards)
+ if len(t.tileProvider.Shards) > 0 {
+ shard = t.tileProvider.Shards[(x+y)%ss]
+ }
+ return t.tileProvider.getURL(shard, zoom, x, y)
+}
+
+func (t *TileFetcher) cacheFileName(zoom int, x, y int) string {
+ return fmt.Sprintf("%s/%d/%d/%d", t.cacheDir, zoom, x, y)
+}
+
+// ToggleCaching enables/disables caching
+func (t *TileFetcher) ToggleCaching(enabled bool) {
+ t.useCaching = enabled
+}
+
+// Fetch download (or retrieves from the cache) a tile image for the specified zoom level and tile coordinates
+func (t *TileFetcher) Fetch(zoom, x, y int) (image.Image, error) {
+ if t.useCaching {
+ fileName := t.cacheFileName(zoom, x, y)
+ cachedImg, err := t.loadCache(fileName)
+ if err == nil {
+ return cachedImg, nil
+ }
+ }
+
+ url := t.url(zoom, x, y)
+ data, err := t.download(url)
+ if err != nil {
+ return nil, err
+ }
+
+ img, _, err := image.Decode(bytes.NewBuffer(data))
+ if err != nil {
+ return nil, err
+ }
+
+ if t.useCaching {
+ fileName := t.cacheFileName(zoom, x, y)
+ if err := t.storeCache(fileName, data); err != nil {
+ log.Printf("Failed to store map tile as '%s': %s", fileName, err)
+ }
+ }
+
+ return img, nil
+}
+
+func (t *TileFetcher) download(url string) ([]byte, error) {
+ resp, err := http.Get(url)
+ defer resp.Body.Close()
+
+ if err != nil {
+ return nil, err
+ }
+
+ contents, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return nil, err
+ }
+
+ return contents, nil
+}
+
+func (t *TileFetcher) loadCache(fileName string) (image.Image, error) {
+ file, err := os.Open(fileName)
+ if err != nil {
+ return nil, err
+ }
+ defer file.Close()
+
+ img, _, err := image.Decode(file)
+ if err != nil {
+ return nil, err
+ }
+
+ return img, nil
+}
+
+func (t *TileFetcher) createCacheDir(path string) error {
+ src, err := os.Stat(path)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return os.MkdirAll(path, 0777)
+ }
+ return err
+ }
+ if src.IsDir() {
+ return nil
+ }
+ return fmt.Errorf("File exists but is not a directory: %s", path)
+}
+
+func (t *TileFetcher) storeCache(fileName string, data []byte) error {
+ dir, _ := filepath.Split(fileName)
+
+ if err := t.createCacheDir(dir); err != nil {
+ return err
+ }
+
+ file, err := os.Create(fileName)
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+
+ if _, err = io.Copy(file, bytes.NewBuffer(data)); err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/flopp/go-staticmaps/tile_provider.go b/vendor/github.com/flopp/go-staticmaps/tile_provider.go
new file mode 100644
index 0000000..e54400c
--- /dev/null
+++ b/vendor/github.com/flopp/go-staticmaps/tile_provider.go
@@ -0,0 +1,98 @@
+// Copyright 2016 Florian Pigorsch. All rights reserved.
+//
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package sm
+
+import "fmt"
+
+// TileProvider encapsulates all infos about a map tile provider service (name, url scheme, attribution, etc.)
+type TileProvider struct {
+ Name string
+ Attribution string
+ TileSize int
+ URLPattern string // "%[1]s" => shard, "%[2]d" => zoom, "%[3]d" => x, "%[4]d" => y
+ Shards []string
+}
+
+func (t *TileProvider) getURL(shard string, zoom, x, y int) string {
+ return fmt.Sprintf(t.URLPattern, shard, zoom, x, y)
+}
+
+// NewTileProviderMapQuest creates a TileProvider struct for mapquest's tile service
+func NewTileProviderMapQuest() *TileProvider {
+ t := new(TileProvider)
+ t.Name = "mapquest"
+ t.Attribution = "Maps (c) MapQuest; Data (c) OSM and contributors, ODbL"
+ t.TileSize = 256
+ t.URLPattern = "http://otile%[1]s.mqcdn.com/tiles/1.0.0/osm/%[2]d/%[3]d/%[4]d.png"
+ t.Shards = []string{"1", "2", "3", "4"}
+ return t
+}
+
+func newTileProviderThunderforest(name string) *TileProvider {
+ t := new(TileProvider)
+ t.Name = fmt.Sprintf("thunderforest-%s", name)
+ t.Attribution = "Maps (c) Thundeforest; Data (c) OSM and contributors, ODbL"
+ t.TileSize = 256
+ t.URLPattern = "https://%[1]s.tile.thunderforest.com/" + name + "/%[2]d/%[3]d/%[4]d.png"
+ t.Shards = []string{"a", "b", "c"}
+ return t
+}
+
+// NewTileProviderThunderforestLandscape creates a TileProvider struct for thundeforests's 'landscape' tile service
+func NewTileProviderThunderforestLandscape() *TileProvider {
+ return newTileProviderThunderforest("landscape")
+}
+
+// NewTileProviderThunderforestOutdoors creates a TileProvider struct for thundeforests's 'outdoors' tile service
+func NewTileProviderThunderforestOutdoors() *TileProvider {
+ return newTileProviderThunderforest("outdoors")
+}
+
+// NewTileProviderThunderforestTransport creates a TileProvider struct for thundeforests's 'transport' tile service
+func NewTileProviderThunderforestTransport() *TileProvider {
+ return newTileProviderThunderforest("transport")
+}
+
+// NewTileProviderStamenToner creates a TileProvider struct for stamens' 'toner' tile service
+func NewTileProviderStamenToner() *TileProvider {
+ t := new(TileProvider)
+ t.Name = "stamen-toner"
+ t.Attribution = "Maps (c) Stamen; Data (c) OSM and contributors, ODbL"
+ t.TileSize = 256
+ t.URLPattern = "http://%[1]s.tile.stamen.com/toner/%[2]d/%[3]d/%[4]d.png"
+ t.Shards = []string{"a", "b", "c", "d"}
+ return t
+}
+
+// NewTileProviderOpenTopoMap creates a TileProvider struct for opentopomaps's tile service
+func NewTileProviderOpenTopoMap() *TileProvider {
+ t := new(TileProvider)
+ t.Name = "opentopomap"
+ t.Attribution = "Maps (c) OpenTopoMap [CC-BY-SA]; Data (c) OSM and contributors [ODbL]; Data (c) SRTM"
+ t.TileSize = 256
+ t.URLPattern = "http://%[1]s.tile.opentopomap.org/%[2]d/%[3]d/%[4]d.png"
+ t.Shards = []string{"a", "b", "c"}
+ return t
+}
+
+// GetTileProviders returns a map of all available TileProviders
+func GetTileProviders() map[string]*TileProvider {
+ m := make(map[string]*TileProvider)
+
+ list := []*TileProvider{
+ NewTileProviderMapQuest(),
+ NewTileProviderThunderforestLandscape(),
+ NewTileProviderThunderforestOutdoors(),
+ NewTileProviderThunderforestTransport(),
+ NewTileProviderStamenToner(),
+ NewTileProviderOpenTopoMap()}
+
+ for _, tp := range list {
+ m[tp.Name] = tp
+ }
+
+ return m
+}
diff --git a/vendor/github.com/flopp/go-staticmaps/util.go b/vendor/github.com/flopp/go-staticmaps/util.go
new file mode 100644
index 0000000..3f5d4ad
--- /dev/null
+++ b/vendor/github.com/flopp/go-staticmaps/util.go
@@ -0,0 +1,18 @@
+// Copyright 2016 Florian Pigorsch. All rights reserved.
+//
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package sm
+
+import (
+ "strings"
+)
+
+// hasPrefix checks if 's' has prefix 'prefix'; returns 'true' and the remainder on success, and 'false', 's' otherwise.
+func hasPrefix(s string, prefix string) (bool, string) {
+ if strings.HasPrefix(s, prefix) {
+ return true, strings.TrimPrefix(s, prefix)
+ }
+ return false, s
+}
diff --git a/vendor/github.com/fogleman/gg/LICENSE.md b/vendor/github.com/fogleman/gg/LICENSE.md
new file mode 100644
index 0000000..d7b4099
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/LICENSE.md
@@ -0,0 +1,19 @@
+Copyright (C) 2016 Michael Fogleman
+
+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/fogleman/gg/bezier.go b/vendor/github.com/fogleman/gg/bezier.go
new file mode 100644
index 0000000..f2cd7ab
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/bezier.go
@@ -0,0 +1,59 @@
+package gg
+
+import "math"
+
+func quadratic(x0, y0, x1, y1, x2, y2, t float64) (x, y float64) {
+ u := 1 - t
+ a := u * u
+ b := 2 * u * t
+ c := t * t
+ x = a*x0 + b*x1 + c*x2
+ y = a*y0 + b*y1 + c*y2
+ return
+}
+
+func QuadraticBezier(x0, y0, x1, y1, x2, y2 float64) []Point {
+ l := (math.Hypot(x1-x0, y1-y0) +
+ math.Hypot(x2-x1, y2-y1))
+ n := int(l + 0.5)
+ if n < 4 {
+ n = 4
+ }
+ d := float64(n) - 1
+ result := make([]Point, n)
+ for i := 0; i < n; i++ {
+ t := float64(i) / d
+ x, y := quadratic(x0, y0, x1, y1, x2, y2, t)
+ result[i] = Point{x, y}
+ }
+ return result
+}
+
+func cubic(x0, y0, x1, y1, x2, y2, x3, y3, t float64) (x, y float64) {
+ u := 1 - t
+ a := u * u * u
+ b := 3 * u * u * t
+ c := 3 * u * t * t
+ d := t * t * t
+ x = a*x0 + b*x1 + c*x2 + d*x3
+ y = a*y0 + b*y1 + c*y2 + d*y3
+ return
+}
+
+func CubicBezier(x0, y0, x1, y1, x2, y2, x3, y3 float64) []Point {
+ l := (math.Hypot(x1-x0, y1-y0) +
+ math.Hypot(x2-x1, y2-y1) +
+ math.Hypot(x3-x2, y3-y2))
+ n := int(l + 0.5)
+ if n < 4 {
+ n = 4
+ }
+ d := float64(n) - 1
+ result := make([]Point, n)
+ for i := 0; i < n; i++ {
+ t := float64(i) / d
+ x, y := cubic(x0, y0, x1, y1, x2, y2, x3, y3, t)
+ result[i] = Point{x, y}
+ }
+ return result
+}
diff --git a/vendor/github.com/fogleman/gg/context.go b/vendor/github.com/fogleman/gg/context.go
new file mode 100644
index 0000000..e186027
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/context.go
@@ -0,0 +1,738 @@
+// Package gg provides a simple API for rendering 2D graphics in pure Go.
+package gg
+
+import (
+ "image"
+ "image/color"
+ "image/draw"
+ "image/png"
+ "io"
+ "math"
+
+ "github.com/golang/freetype/raster"
+ "golang.org/x/image/font"
+ "golang.org/x/image/font/basicfont"
+)
+
+type LineCap int
+
+const (
+ LineCapRound LineCap = iota
+ LineCapButt
+ LineCapSquare
+)
+
+type LineJoin int
+
+const (
+ LineJoinRound LineJoin = iota
+ LineJoinBevel
+)
+
+type FillRule int
+
+const (
+ FillRuleWinding FillRule = iota
+ FillRuleEvenOdd
+)
+
+type Align int
+
+const (
+ AlignLeft Align = iota
+ AlignCenter
+ AlignRight
+)
+
+type Context struct {
+ width int
+ height int
+ im *image.RGBA
+ mask *image.Alpha
+ color color.Color
+ strokePath raster.Path
+ fillPath raster.Path
+ start Point
+ current Point
+ hasCurrent bool
+ dashes []float64
+ lineWidth float64
+ lineCap LineCap
+ lineJoin LineJoin
+ fillRule FillRule
+ fontFace font.Face
+ fontHeight float64
+ matrix Matrix
+ stack []*Context
+}
+
+// NewContext creates a new image.RGBA with the specified width and height
+// and prepares a context for rendering onto that image.
+func NewContext(width, height int) *Context {
+ return NewContextForRGBA(image.NewRGBA(image.Rect(0, 0, width, height)))
+}
+
+// NewContextForImage copies the specified image into a new image.RGBA
+// and prepares a context for rendering onto that image.
+func NewContextForImage(im image.Image) *Context {
+ return NewContextForRGBA(imageToRGBA(im))
+}
+
+// NewContextForRGBA prepares a context for rendering onto the specified image.
+// No copy is made.
+func NewContextForRGBA(im *image.RGBA) *Context {
+ return &Context{
+ width: im.Bounds().Size().X,
+ height: im.Bounds().Size().Y,
+ im: im,
+ color: color.Transparent,
+ lineWidth: 1,
+ fillRule: FillRuleWinding,
+ fontFace: basicfont.Face7x13,
+ fontHeight: 13,
+ matrix: Identity(),
+ }
+}
+
+// Image returns the image that has been drawn by this context.
+func (dc *Context) Image() image.Image {
+ return dc.im
+}
+
+// Width returns the width of the image in pixels.
+func (dc *Context) Width() int {
+ return dc.width
+}
+
+// Height returns the height of the image in pixels.
+func (dc *Context) Height() int {
+ return dc.height
+}
+
+// SavePNG encodes the image as a PNG and writes it to disk.
+func (dc *Context) SavePNG(path string) error {
+ return SavePNG(path, dc.im)
+}
+
+// EncodePNG encodes the image as a PNG and writes it to the provided io.Writer.
+func (dc *Context) EncodePNG(w io.Writer) error {
+ return png.Encode(w, dc.im)
+}
+
+// SetDash sets the current dash pattern to use. Call with zero arguments to
+// disable dashes. The values specify the lengths of each dash, with
+// alternating on and off lengths.
+func (dc *Context) SetDash(dashes ...float64) {
+ dc.dashes = dashes
+}
+
+func (dc *Context) SetLineWidth(lineWidth float64) {
+ dc.lineWidth = lineWidth
+}
+
+func (dc *Context) SetLineCap(lineCap LineCap) {
+ dc.lineCap = lineCap
+}
+
+func (dc *Context) SetLineCapRound() {
+ dc.lineCap = LineCapRound
+}
+
+func (dc *Context) SetLineCapButt() {
+ dc.lineCap = LineCapButt
+}
+
+func (dc *Context) SetLineCapSquare() {
+ dc.lineCap = LineCapSquare
+}
+
+func (dc *Context) SetLineJoin(lineJoin LineJoin) {
+ dc.lineJoin = lineJoin
+}
+
+func (dc *Context) SetLineJoinRound() {
+ dc.lineJoin = LineJoinRound
+}
+
+func (dc *Context) SetLineJoinBevel() {
+ dc.lineJoin = LineJoinBevel
+}
+
+func (dc *Context) SetFillRule(fillRule FillRule) {
+ dc.fillRule = fillRule
+}
+
+func (dc *Context) SetFillRuleWinding() {
+ dc.fillRule = FillRuleWinding
+}
+
+func (dc *Context) SetFillRuleEvenOdd() {
+ dc.fillRule = FillRuleEvenOdd
+}
+
+// Color Setters
+
+// SetColor sets the current color.
+func (dc *Context) SetColor(c color.Color) {
+ dc.color = c
+}
+
+// SetHexColor sets the current color using a hex string. The leading pound
+// sign (#) is optional. Both 3- and 6-digit variations are supported. 8 digits
+// may be provided to set the alpha value as well.
+func (dc *Context) SetHexColor(x string) {
+ r, g, b, a := parseHexColor(x)
+ dc.SetRGBA255(r, g, b, a)
+}
+
+// SetRGBA255 sets the current color. r, g, b, a values should be between 0 and
+// 255, inclusive.
+func (dc *Context) SetRGBA255(r, g, b, a int) {
+ dc.color = color.NRGBA{uint8(r), uint8(g), uint8(b), uint8(a)}
+}
+
+// SetRGB255 sets the current color. r, g, b values should be between 0 and 255,
+// inclusive. Alpha will be set to 255 (fully opaque).
+func (dc *Context) SetRGB255(r, g, b int) {
+ dc.SetRGBA255(r, g, b, 255)
+}
+
+// SetRGBA sets the current color. r, g, b, a values should be between 0 and 1,
+// inclusive.
+func (dc *Context) SetRGBA(r, g, b, a float64) {
+ dc.color = color.NRGBA{
+ uint8(r * 255),
+ uint8(g * 255),
+ uint8(b * 255),
+ uint8(a * 255),
+ }
+}
+
+// SetRGB sets the current color. r, g, b values should be between 0 and 1,
+// inclusive. Alpha will be set to 1 (fully opaque).
+func (dc *Context) SetRGB(r, g, b float64) {
+ dc.SetRGBA(r, g, b, 1)
+}
+
+// Path Manipulation
+
+// MoveTo starts a new subpath within the current path starting at the
+// specified point.
+func (dc *Context) MoveTo(x, y float64) {
+ if dc.hasCurrent {
+ dc.fillPath.Add1(dc.start.Fixed())
+ }
+ x, y = dc.TransformPoint(x, y)
+ p := Point{x, y}
+ dc.strokePath.Start(p.Fixed())
+ dc.fillPath.Start(p.Fixed())
+ dc.start = p
+ dc.current = p
+ dc.hasCurrent = true
+}
+
+// LineTo adds a line segment to the current path starting at the current
+// point. If there is no current point, it is equivalent to MoveTo(x, y)
+func (dc *Context) LineTo(x, y float64) {
+ if !dc.hasCurrent {
+ dc.MoveTo(x, y)
+ } else {
+ x, y = dc.TransformPoint(x, y)
+ p := Point{x, y}
+ dc.strokePath.Add1(p.Fixed())
+ dc.fillPath.Add1(p.Fixed())
+ dc.current = p
+ }
+}
+
+// QuadraticTo adds a quadratic bezier curve to the current path starting at
+// the current point. If there is no current point, it first performs
+// MoveTo(x1, y1)
+func (dc *Context) QuadraticTo(x1, y1, x2, y2 float64) {
+ if !dc.hasCurrent {
+ dc.MoveTo(x1, y1)
+ }
+ x1, y1 = dc.TransformPoint(x1, y1)
+ x2, y2 = dc.TransformPoint(x2, y2)
+ p1 := Point{x1, y1}
+ p2 := Point{x2, y2}
+ dc.strokePath.Add2(p1.Fixed(), p2.Fixed())
+ dc.fillPath.Add2(p1.Fixed(), p2.Fixed())
+ dc.current = p2
+}
+
+// CubicTo adds a cubic bezier curve to the current path starting at the
+// current point. If there is no current point, it first performs
+// MoveTo(x1, y1). Because freetype/raster does not support cubic beziers,
+// this is emulated with many small line segments.
+func (dc *Context) CubicTo(x1, y1, x2, y2, x3, y3 float64) {
+ if !dc.hasCurrent {
+ dc.MoveTo(x1, y1)
+ }
+ x0, y0 := dc.current.X, dc.current.Y
+ x1, y1 = dc.TransformPoint(x1, y1)
+ x2, y2 = dc.TransformPoint(x2, y2)
+ x3, y3 = dc.TransformPoint(x3, y3)
+ points := CubicBezier(x0, y0, x1, y1, x2, y2, x3, y3)
+ previous := dc.current.Fixed()
+ for _, p := range points[1:] {
+ f := p.Fixed()
+ if f == previous {
+ // TODO: this fixes some rendering issues but not all
+ continue
+ }
+ previous = f
+ dc.strokePath.Add1(f)
+ dc.fillPath.Add1(f)
+ dc.current = p
+ }
+}
+
+// ClosePath adds a line segment from the current point to the beginning
+// of the current subpath. If there is no current point, this is a no-op.
+func (dc *Context) ClosePath() {
+ if dc.hasCurrent {
+ dc.strokePath.Add1(dc.start.Fixed())
+ dc.fillPath.Add1(dc.start.Fixed())
+ dc.current = dc.start
+ }
+}
+
+// ClearPath clears the current path. There is no current point after this
+// operation.
+func (dc *Context) ClearPath() {
+ dc.strokePath.Clear()
+ dc.fillPath.Clear()
+ dc.hasCurrent = false
+}
+
+// NewSubPath starts a new subpath within the current path. There is no current
+// point after this operation.
+func (dc *Context) NewSubPath() {
+ if dc.hasCurrent {
+ dc.fillPath.Add1(dc.start.Fixed())
+ }
+ dc.hasCurrent = false
+}
+
+// Path Drawing
+
+func (dc *Context) capper() raster.Capper {
+ switch dc.lineCap {
+ case LineCapButt:
+ return raster.ButtCapper
+ case LineCapRound:
+ return raster.RoundCapper
+ case LineCapSquare:
+ return raster.SquareCapper
+ }
+ return nil
+}
+
+func (dc *Context) joiner() raster.Joiner {
+ switch dc.lineJoin {
+ case LineJoinBevel:
+ return raster.BevelJoiner
+ case LineJoinRound:
+ return raster.RoundJoiner
+ }
+ return nil
+}
+
+func (dc *Context) stroke(painter raster.Painter) {
+ path := dc.strokePath
+ if len(dc.dashes) > 0 {
+ path = dashed(path, dc.dashes)
+ } else {
+ // TODO: this is a temporary workaround to remove tiny segments
+ // that result in rendering issues
+ path = rasterPath(flattenPath(path))
+ }
+ r := raster.NewRasterizer(dc.width, dc.height)
+ r.UseNonZeroWinding = true
+ r.AddStroke(path, fix(dc.lineWidth), dc.capper(), dc.joiner())
+ r.Rasterize(painter)
+}
+
+func (dc *Context) fill(painter raster.Painter) {
+ path := dc.fillPath
+ if dc.hasCurrent {
+ path = make(raster.Path, len(dc.fillPath))
+ copy(path, dc.fillPath)
+ path.Add1(dc.start.Fixed())
+ }
+ r := raster.NewRasterizer(dc.width, dc.height)
+ r.UseNonZeroWinding = dc.fillRule == FillRuleWinding
+ r.AddPath(path)
+ r.Rasterize(painter)
+}
+
+// StrokePreserve strokes the current path with the current color, line width,
+// line cap, line join and dash settings. The path is preserved after this
+// operation.
+func (dc *Context) StrokePreserve() {
+ if dc.mask == nil {
+ painter := raster.NewRGBAPainter(dc.im)
+ painter.SetColor(dc.color)
+ dc.stroke(painter)
+ } else {
+ im := image.NewRGBA(image.Rect(0, 0, dc.width, dc.height))
+ painter := raster.NewRGBAPainter(im)
+ painter.SetColor(dc.color)
+ dc.stroke(painter)
+ draw.DrawMask(dc.im, dc.im.Bounds(), im, image.ZP, dc.mask, image.ZP, draw.Over)
+ }
+}
+
+// Stroke strokes the current path with the current color, line width,
+// line cap, line join and dash settings. The path is cleared after this
+// operation.
+func (dc *Context) Stroke() {
+ dc.StrokePreserve()
+ dc.ClearPath()
+}
+
+// FillPreserve fills the current path with the current color. Open subpaths
+// are implicity closed. The path is preserved after this operation.
+func (dc *Context) FillPreserve() {
+ if dc.mask == nil {
+ painter := raster.NewRGBAPainter(dc.im)
+ painter.SetColor(dc.color)
+ dc.fill(painter)
+ } else {
+ im := image.NewRGBA(image.Rect(0, 0, dc.width, dc.height))
+ painter := raster.NewRGBAPainter(im)
+ painter.SetColor(dc.color)
+ dc.fill(painter)
+ draw.DrawMask(dc.im, dc.im.Bounds(), im, image.ZP, dc.mask, image.ZP, draw.Over)
+ }
+}
+
+// Fill fills the current path with the current color. Open subpaths
+// are implicity closed. The path is cleared after this operation.
+func (dc *Context) Fill() {
+ dc.FillPreserve()
+ dc.ClearPath()
+}
+
+// ClipPreserve updates the clipping region by intersecting the current
+// clipping region with the current path as it would be filled by dc.Fill().
+// The path is preserved after this operation.
+func (dc *Context) ClipPreserve() {
+ clip := image.NewAlpha(image.Rect(0, 0, dc.width, dc.height))
+ painter := raster.NewAlphaOverPainter(clip)
+ dc.fill(painter)
+ if dc.mask == nil {
+ dc.mask = clip
+ } else {
+ mask := image.NewAlpha(image.Rect(0, 0, dc.width, dc.height))
+ draw.DrawMask(mask, mask.Bounds(), clip, image.ZP, dc.mask, image.ZP, draw.Over)
+ dc.mask = mask
+ }
+}
+
+// Clip updates the clipping region by intersecting the current
+// clipping region with the current path as it would be filled by dc.Fill().
+// The path is cleared after this operation.
+func (dc *Context) Clip() {
+ dc.ClipPreserve()
+ dc.ClearPath()
+}
+
+// ResetClip clears the clipping region.
+func (dc *Context) ResetClip() {
+ dc.mask = nil
+}
+
+// Convenient Drawing Functions
+
+// Clear fills the entire image with the current color.
+func (dc *Context) Clear() {
+ src := image.NewUniform(dc.color)
+ draw.Draw(dc.im, dc.im.Bounds(), src, image.ZP, draw.Src)
+}
+
+func (dc *Context) DrawLine(x1, y1, x2, y2 float64) {
+ dc.MoveTo(x1, y1)
+ dc.LineTo(x2, y2)
+}
+
+func (dc *Context) DrawRectangle(x, y, w, h float64) {
+ dc.NewSubPath()
+ dc.MoveTo(x, y)
+ dc.LineTo(x+w, y)
+ dc.LineTo(x+w, y+h)
+ dc.LineTo(x, y+h)
+ dc.ClosePath()
+}
+
+func (dc *Context) DrawRoundedRectangle(x, y, w, h, r float64) {
+ x0, x1, x2, x3 := x, x+r, x+w-r, x+w
+ y0, y1, y2, y3 := y, y+r, y+h-r, y+h
+ dc.NewSubPath()
+ dc.MoveTo(x1, y0)
+ dc.LineTo(x2, y0)
+ dc.DrawArc(x2, y1, r, Radians(270), Radians(360))
+ dc.LineTo(x3, y2)
+ dc.DrawArc(x2, y2, r, Radians(0), Radians(90))
+ dc.LineTo(x1, y3)
+ dc.DrawArc(x1, y2, r, Radians(90), Radians(180))
+ dc.LineTo(x0, y1)
+ dc.DrawArc(x1, y1, r, Radians(180), Radians(270))
+ dc.ClosePath()
+}
+
+func (dc *Context) DrawEllipticalArc(x, y, rx, ry, angle1, angle2 float64) {
+ const n = 16
+ for i := 0; i < n; i++ {
+ p1 := float64(i+0) / n
+ p2 := float64(i+1) / n
+ a1 := angle1 + (angle2-angle1)*p1
+ a2 := angle1 + (angle2-angle1)*p2
+ x0 := x + rx*math.Cos(a1)
+ y0 := y + ry*math.Sin(a1)
+ x1 := x + rx*math.Cos(a1+(a2-a1)/2)
+ y1 := y + ry*math.Sin(a1+(a2-a1)/2)
+ x2 := x + rx*math.Cos(a2)
+ y2 := y + ry*math.Sin(a2)
+ cx := 2*x1 - x0/2 - x2/2
+ cy := 2*y1 - y0/2 - y2/2
+ if i == 0 && !dc.hasCurrent {
+ dc.MoveTo(x0, y0)
+ }
+ dc.QuadraticTo(cx, cy, x2, y2)
+ }
+}
+
+func (dc *Context) DrawEllipse(x, y, rx, ry float64) {
+ dc.NewSubPath()
+ dc.DrawEllipticalArc(x, y, rx, ry, 0, 2*math.Pi)
+ dc.ClosePath()
+}
+
+func (dc *Context) DrawArc(x, y, r, angle1, angle2 float64) {
+ dc.DrawEllipticalArc(x, y, r, r, angle1, angle2)
+}
+
+func (dc *Context) DrawCircle(x, y, r float64) {
+ dc.NewSubPath()
+ dc.DrawEllipticalArc(x, y, r, r, 0, 2*math.Pi)
+ dc.ClosePath()
+}
+
+func (dc *Context) DrawRegularPolygon(n int, x, y, r, rotation float64) {
+ angle := 2 * math.Pi / float64(n)
+ rotation -= math.Pi / 2
+ if n%2 == 0 {
+ rotation += angle / 2
+ }
+ dc.NewSubPath()
+ for i := 0; i < n; i++ {
+ a := rotation + angle*float64(i)
+ dc.LineTo(x+r*math.Cos(a), y+r*math.Sin(a))
+ }
+ dc.ClosePath()
+}
+
+// DrawImage draws the specified image at the specified point.
+// Currently, rotation and scaling transforms are not supported.
+func (dc *Context) DrawImage(im image.Image, x, y int) {
+ dc.DrawImageAnchored(im, x, y, 0, 0)
+}
+
+// DrawImageAnchored draws the specified image at the specified anchor point.
+// The anchor point is x - w * ax, y - h * ay, where w, h is the size of the
+// image. Use ax=0.5, ay=0.5 to center the image at the specified point.
+func (dc *Context) DrawImageAnchored(im image.Image, x, y int, ax, ay float64) {
+ s := im.Bounds().Size()
+ x -= int(ax * float64(s.X))
+ y -= int(ay * float64(s.Y))
+ p := image.Pt(x, y)
+ r := image.Rectangle{p, p.Add(s)}
+ if dc.mask == nil {
+ draw.Draw(dc.im, r, im, image.ZP, draw.Over)
+ } else {
+ draw.DrawMask(dc.im, r, im, image.ZP, dc.mask, p, draw.Over)
+ }
+}
+
+// Text Functions
+
+func (dc *Context) SetFontFace(fontFace font.Face) {
+ dc.fontFace = fontFace
+}
+
+func (dc *Context) LoadFontFace(path string, points float64) error {
+ face, err := loadFontFace(path, points)
+ if err == nil {
+ dc.fontFace = face
+ dc.fontHeight = points * 72 / 96
+ }
+ return err
+}
+
+func (dc *Context) drawString(im *image.RGBA, s string, x, y float64) {
+ d := &font.Drawer{
+ Dst: im,
+ Src: image.NewUniform(dc.color),
+ Face: dc.fontFace,
+ Dot: fixp(x, y),
+ }
+ d.DrawString(s)
+}
+
+// DrawString draws the specified text at the specified point.
+// Currently, rotation and scaling transforms are not supported.
+func (dc *Context) DrawString(s string, x, y float64) {
+ dc.DrawStringAnchored(s, x, y, 0, 0)
+}
+
+// DrawStringAnchored draws the specified text at the specified anchor point.
+// The anchor point is x - w * ax, y - h * ay, where w, h is the size of the
+// text. Use ax=0.5, ay=0.5 to center the text at the specified point.
+func (dc *Context) DrawStringAnchored(s string, x, y, ax, ay float64) {
+ w, h := dc.MeasureString(s)
+ x, y = dc.TransformPoint(x, y)
+ x -= ax * w
+ y += ay * h
+ if dc.mask == nil {
+ dc.drawString(dc.im, s, x, y)
+ } else {
+ im := image.NewRGBA(image.Rect(0, 0, dc.width, dc.height))
+ dc.drawString(im, s, x, y)
+ draw.DrawMask(dc.im, dc.im.Bounds(), im, image.ZP, dc.mask, image.ZP, draw.Over)
+ }
+}
+
+// DrawStringWrapped word-wraps the specified string to the given max width
+// and then draws it at the specified anchor point using the given line
+// spacing and text alignment.
+func (dc *Context) DrawStringWrapped(s string, x, y, ax, ay, width, lineSpacing float64, align Align) {
+ lines := dc.WordWrap(s, width)
+ h := float64(len(lines)) * dc.fontHeight * lineSpacing
+ h -= (lineSpacing - 1) * dc.fontHeight
+ x -= ax * width
+ y -= ay * h
+ switch align {
+ case AlignLeft:
+ ax = 0
+ case AlignCenter:
+ ax = 0.5
+ x += width / 2
+ case AlignRight:
+ ax = 1
+ x += width
+ }
+ ay = 1
+ for _, line := range lines {
+ dc.DrawStringAnchored(line, x, y, ax, ay)
+ y += dc.fontHeight * lineSpacing
+ }
+}
+
+// MeasureString returns the rendered width and height of the specified text
+// given the current font face.
+func (dc *Context) MeasureString(s string) (w, h float64) {
+ d := &font.Drawer{
+ Face: dc.fontFace,
+ }
+ a := d.MeasureString(s)
+ return float64(a >> 6), dc.fontHeight
+}
+
+// WordWrap wraps the specified string to the given max width and current
+// font face.
+func (dc *Context) WordWrap(s string, w float64) []string {
+ return wordWrap(dc, s, w)
+}
+
+// Transformation Matrix Operations
+
+// Identity resets the current transformation matrix to the identity matrix.
+// This results in no translating, scaling, rotating, or shearing.
+func (dc *Context) Identity() {
+ dc.matrix = Identity()
+}
+
+// Translate updates the current matrix with a translation.
+func (dc *Context) Translate(x, y float64) {
+ dc.matrix = dc.matrix.Translate(x, y)
+}
+
+// Scale updates the current matrix with a scaling factor.
+// Scaling occurs about the origin.
+func (dc *Context) Scale(x, y float64) {
+ dc.matrix = dc.matrix.Scale(x, y)
+}
+
+// ScaleAbout updates the current matrix with a scaling factor.
+// Scaling occurs about the specified point.
+func (dc *Context) ScaleAbout(sx, sy, x, y float64) {
+ dc.Translate(x, y)
+ dc.Scale(sx, sy)
+ dc.Translate(-x, -y)
+}
+
+// Rotate updates the current matrix with a clockwise rotation.
+// Rotation occurs about the origin. Angle is specified in radians.
+func (dc *Context) Rotate(angle float64) {
+ dc.matrix = dc.matrix.Rotate(angle)
+}
+
+// RotateAbout updates the current matrix with a clockwise rotation.
+// Rotation occurs about the specified point. Angle is specified in radians.
+func (dc *Context) RotateAbout(angle, x, y float64) {
+ dc.Translate(x, y)
+ dc.Rotate(angle)
+ dc.Translate(-x, -y)
+}
+
+// Shear updates the current matrix with a shearing angle.
+// Shearing occurs about the origin.
+func (dc *Context) Shear(x, y float64) {
+ dc.matrix = dc.matrix.Shear(x, y)
+}
+
+// ShearAbout updates the current matrix with a shearing angle.
+// Shearing occurs about the specified point.
+func (dc *Context) ShearAbout(sx, sy, x, y float64) {
+ dc.Translate(x, y)
+ dc.Shear(sx, sy)
+ dc.Translate(-x, -y)
+}
+
+// TransformPoint multiplies the specified point by the current matrix,
+// returning a transformed position.
+func (dc *Context) TransformPoint(x, y float64) (tx, ty float64) {
+ return dc.matrix.TransformPoint(x, y)
+}
+
+// InvertY flips the Y axis so that Y grows from bottom to top and Y=0 is at
+// the bottom of the image.
+func (dc *Context) InvertY() {
+ dc.Translate(0, float64(dc.height))
+ dc.Scale(1, -1)
+}
+
+// Stack
+
+// Push saves the current state of the context for later retrieval. These
+// can be nested.
+func (dc *Context) Push() {
+ x := *dc
+ dc.stack = append(dc.stack, &x)
+}
+
+// Pop restores the last saved context state from the stack.
+func (dc *Context) Pop() {
+ before := *dc
+ s := dc.stack
+ x, s := s[len(s)-1], s[:len(s)-1]
+ *dc = *x
+ dc.mask = before.mask
+ dc.strokePath = before.strokePath
+ dc.fillPath = before.fillPath
+ dc.start = before.start
+ dc.current = before.current
+ dc.hasCurrent = before.hasCurrent
+}
diff --git a/vendor/github.com/fogleman/gg/examples/beziers.go b/vendor/github.com/fogleman/gg/examples/beziers.go
new file mode 100644
index 0000000..e4cfa8d
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/examples/beziers.go
@@ -0,0 +1,84 @@
+package main
+
+import (
+ "math/rand"
+
+ "github.com/fogleman/gg"
+)
+
+func random() float64 {
+ return rand.Float64()*2 - 1
+}
+
+func point() (x, y float64) {
+ return random(), random()
+}
+
+func drawCurve(dc *gg.Context) {
+ dc.SetRGBA(0, 0, 0, 0.1)
+ dc.FillPreserve()
+ dc.SetRGB(0, 0, 0)
+ dc.SetLineWidth(12)
+ dc.Stroke()
+}
+
+func drawPoints(dc *gg.Context) {
+ dc.SetRGBA(1, 0, 0, 0.5)
+ dc.SetLineWidth(2)
+ dc.Stroke()
+}
+
+func randomQuadratic(dc *gg.Context) {
+ x0, y0 := point()
+ x1, y1 := point()
+ x2, y2 := point()
+ dc.MoveTo(x0, y0)
+ dc.QuadraticTo(x1, y1, x2, y2)
+ drawCurve(dc)
+ dc.MoveTo(x0, y0)
+ dc.LineTo(x1, y1)
+ dc.LineTo(x2, y2)
+ drawPoints(dc)
+}
+
+func randomCubic(dc *gg.Context) {
+ x0, y0 := point()
+ x1, y1 := point()
+ x2, y2 := point()
+ x3, y3 := point()
+ dc.MoveTo(x0, y0)
+ dc.CubicTo(x1, y1, x2, y2, x3, y3)
+ drawCurve(dc)
+ dc.MoveTo(x0, y0)
+ dc.LineTo(x1, y1)
+ dc.LineTo(x2, y2)
+ dc.LineTo(x3, y3)
+ drawPoints(dc)
+}
+
+func main() {
+ const (
+ S = 256
+ W = 8
+ H = 8
+ )
+ dc := gg.NewContext(S*W, S*H)
+ dc.SetRGB(1, 1, 1)
+ dc.Clear()
+ for j := 0; j < H; j++ {
+ for i := 0; i < W; i++ {
+ x := float64(i)*S + S/2
+ y := float64(j)*S + S/2
+ dc.Push()
+ dc.Translate(x, y)
+ dc.Scale(S/2, S/2)
+ if j%2 == 0 {
+ randomCubic(dc)
+ } else {
+ randomQuadratic(dc)
+ }
+ dc.Pop()
+ }
+ }
+ dc.SavePNG("out.png")
+}
diff --git a/vendor/github.com/fogleman/gg/examples/circle.go b/vendor/github.com/fogleman/gg/examples/circle.go
new file mode 100644
index 0000000..5debf66
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/examples/circle.go
@@ -0,0 +1,11 @@
+package main
+
+import "github.com/fogleman/gg"
+
+func main() {
+ dc := gg.NewContext(1000, 1000)
+ dc.DrawCircle(500, 500, 400)
+ dc.SetRGB(0, 0, 0)
+ dc.Fill()
+ dc.SavePNG("out.png")
+}
diff --git a/vendor/github.com/fogleman/gg/examples/clip.go b/vendor/github.com/fogleman/gg/examples/clip.go
new file mode 100644
index 0000000..73e6f28
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/examples/clip.go
@@ -0,0 +1,15 @@
+package main
+
+import "github.com/fogleman/gg"
+
+func main() {
+ dc := gg.NewContext(1000, 1000)
+ dc.DrawCircle(350, 500, 300)
+ dc.Clip()
+ dc.DrawCircle(650, 500, 300)
+ dc.Clip()
+ dc.DrawRectangle(0, 0, 1000, 1000)
+ dc.SetRGB(0, 0, 0)
+ dc.Fill()
+ dc.SavePNG("out.png")
+}
diff --git a/vendor/github.com/fogleman/gg/examples/cubic.go b/vendor/github.com/fogleman/gg/examples/cubic.go
new file mode 100644
index 0000000..bedc585
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/examples/cubic.go
@@ -0,0 +1,38 @@
+package main
+
+import "github.com/fogleman/gg"
+
+func main() {
+ const S = 1000
+ dc := gg.NewContext(S, S)
+ dc.SetRGB(1, 1, 1)
+ dc.Clear()
+ dc.Translate(S/2, S/2)
+ dc.Scale(40, 40)
+
+ var x0, y0, x1, y1, x2, y2, x3, y3 float64
+ x0, y0 = -10, 0
+ x1, y1 = -8, -8
+ x2, y2 = 8, 8
+ x3, y3 = 10, 0
+
+ dc.MoveTo(x0, y0)
+ dc.CubicTo(x1, y1, x2, y2, x3, y3)
+ dc.SetRGBA(0, 0, 0, 0.2)
+ dc.SetLineWidth(8)
+ dc.FillPreserve()
+ dc.SetRGB(0, 0, 0)
+ dc.SetDash(16, 24)
+ dc.Stroke()
+
+ dc.MoveTo(x0, y0)
+ dc.LineTo(x1, y1)
+ dc.LineTo(x2, y2)
+ dc.LineTo(x3, y3)
+ dc.SetRGBA(1, 0, 0, 0.4)
+ dc.SetLineWidth(2)
+ dc.SetDash(4, 8, 1, 8)
+ dc.Stroke()
+
+ dc.SavePNG("out.png")
+}
diff --git a/vendor/github.com/fogleman/gg/examples/ellipse.go b/vendor/github.com/fogleman/gg/examples/ellipse.go
new file mode 100644
index 0000000..e0de9f4
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/examples/ellipse.go
@@ -0,0 +1,20 @@
+package main
+
+import "github.com/fogleman/gg"
+
+func main() {
+ const S = 1024
+ dc := gg.NewContext(S, S)
+ dc.SetRGBA(0, 0, 0, 0.1)
+ for i := 0; i < 360; i += 15 {
+ dc.Push()
+ dc.RotateAbout(gg.Radians(float64(i)), S/2, S/2)
+ dc.DrawEllipse(S/2, S/2, S*7/16, S/8)
+ dc.Fill()
+ dc.Pop()
+ }
+ if im, err := gg.LoadImage("examples/gopher.png"); err == nil {
+ dc.DrawImageAnchored(im, S/2, S/2, 0.5, 0.5)
+ }
+ dc.SavePNG("out.png")
+}
diff --git a/vendor/github.com/fogleman/gg/examples/lines.go b/vendor/github.com/fogleman/gg/examples/lines.go
new file mode 100644
index 0000000..9581dd6
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/examples/lines.go
@@ -0,0 +1,31 @@
+package main
+
+import (
+ "math/rand"
+
+ "github.com/fogleman/gg"
+)
+
+func main() {
+ const W = 1024
+ const H = 1024
+ dc := gg.NewContext(W, H)
+ dc.SetRGB(0, 0, 0)
+ dc.Clear()
+ for i := 0; i < 1000; i++ {
+ x1 := rand.Float64() * W
+ y1 := rand.Float64() * H
+ x2 := rand.Float64() * W
+ y2 := rand.Float64() * H
+ r := rand.Float64()
+ g := rand.Float64()
+ b := rand.Float64()
+ a := rand.Float64()*0.5 + 0.5
+ w := rand.Float64()*4 + 1
+ dc.SetRGBA(r, g, b, a)
+ dc.SetLineWidth(w)
+ dc.DrawLine(x1, y1, x2, y2)
+ dc.Stroke()
+ }
+ dc.SavePNG("out.png")
+}
diff --git a/vendor/github.com/fogleman/gg/examples/linewidth.go b/vendor/github.com/fogleman/gg/examples/linewidth.go
new file mode 100644
index 0000000..8cdfdca
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/examples/linewidth.go
@@ -0,0 +1,19 @@
+package main
+
+import "github.com/fogleman/gg"
+
+func main() {
+ dc := gg.NewContext(1000, 1000)
+ dc.SetRGB(1, 1, 1)
+ dc.Clear()
+ dc.SetRGB(0, 0, 0)
+ w := 0.1
+ for i := 100; i <= 900; i += 20 {
+ x := float64(i)
+ dc.DrawLine(x+50, 0, x-50, 1000)
+ dc.SetLineWidth(w)
+ dc.Stroke()
+ w += 0.1
+ }
+ dc.SavePNG("out.png")
+}
diff --git a/vendor/github.com/fogleman/gg/examples/lorem.go b/vendor/github.com/fogleman/gg/examples/lorem.go
new file mode 100644
index 0000000..07d052a
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/examples/lorem.go
@@ -0,0 +1,28 @@
+package main
+
+import "github.com/fogleman/gg"
+
+var lines = []string{
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod",
+ "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,",
+ "quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo",
+ "consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse",
+ "cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat",
+ "non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
+}
+
+func main() {
+ const W = 800
+ const H = 400
+ dc := gg.NewContext(W, H)
+ dc.SetRGB(1, 1, 1)
+ dc.Clear()
+ dc.SetRGB(0, 0, 0)
+ // dc.LoadFontFace("/Library/Fonts/Arial.ttf", 18)
+ const h = 24
+ for i, line := range lines {
+ y := H/2 - h*len(lines)/2 + i*h
+ dc.DrawStringAnchored(line, 400, float64(y), 0.5, 0.5)
+ }
+ dc.SavePNG("out.png")
+}
diff --git a/vendor/github.com/fogleman/gg/examples/mask.go b/vendor/github.com/fogleman/gg/examples/mask.go
new file mode 100644
index 0000000..fb6b195
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/examples/mask.go
@@ -0,0 +1,20 @@
+package main
+
+import (
+ "log"
+
+ "github.com/fogleman/gg"
+)
+
+func main() {
+ im, err := gg.LoadImage("examples/lenna.png")
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ dc := gg.NewContext(512, 512)
+ dc.DrawRoundedRectangle(0, 0, 512, 512, 64)
+ dc.Clip()
+ dc.DrawImage(im, 0, 0)
+ dc.SavePNG("out.png")
+}
diff --git a/vendor/github.com/fogleman/gg/examples/meme.go b/vendor/github.com/fogleman/gg/examples/meme.go
new file mode 100644
index 0000000..cd21592
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/examples/meme.go
@@ -0,0 +1,30 @@
+package main
+
+import "github.com/fogleman/gg"
+
+func main() {
+ const S = 1024
+ dc := gg.NewContext(S, S)
+ dc.SetRGB(1, 1, 1)
+ dc.Clear()
+ if err := dc.LoadFontFace("/Library/Fonts/Impact.ttf", 96); err != nil {
+ panic(err)
+ }
+ dc.SetRGB(0, 0, 0)
+ s := "ONE DOES NOT SIMPLY"
+ n := 6 // "stroke" size
+ for dy := -n; dy <= n; dy++ {
+ for dx := -n; dx <= n; dx++ {
+ if dx*dx+dy*dy >= n*n {
+ // give it rounded corners
+ continue
+ }
+ x := S/2 + float64(dx)
+ y := S/2 + float64(dy)
+ dc.DrawStringAnchored(s, x, y, 0.5, 0.5)
+ }
+ }
+ dc.SetRGB(1, 1, 1)
+ dc.DrawStringAnchored(s, S/2, S/2, 0.5, 0.5)
+ dc.SavePNG("out.png")
+}
diff --git a/vendor/github.com/fogleman/gg/examples/openfill.go b/vendor/github.com/fogleman/gg/examples/openfill.go
new file mode 100644
index 0000000..6c57d76
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/examples/openfill.go
@@ -0,0 +1,31 @@
+package main
+
+import (
+ "math"
+ "math/rand"
+
+ "github.com/fogleman/gg"
+)
+
+func main() {
+ dc := gg.NewContext(1000, 1000)
+ for j := 0; j < 10; j++ {
+ for i := 0; i < 10; i++ {
+ x := float64(i)*100 + 50
+ y := float64(j)*100 + 50
+ a1 := rand.Float64() * 2 * math.Pi
+ a2 := a1 + rand.Float64()*math.Pi + math.Pi/2
+ dc.DrawArc(x, y, 40, a1, a2)
+ // dc.ClosePath()
+ }
+ }
+ dc.SetRGB(0, 0, 0)
+ dc.FillPreserve()
+ dc.SetRGB(1, 1, 1)
+ dc.SetLineWidth(8)
+ dc.StrokePreserve()
+ dc.SetRGB(1, 0, 0)
+ dc.SetLineWidth(4)
+ dc.StrokePreserve()
+ dc.SavePNG("out.png")
+}
diff --git a/vendor/github.com/fogleman/gg/examples/quadratic.go b/vendor/github.com/fogleman/gg/examples/quadratic.go
new file mode 100644
index 0000000..a4bba71
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/examples/quadratic.go
@@ -0,0 +1,54 @@
+package main
+
+import "github.com/fogleman/gg"
+
+func main() {
+ const S = 1000
+ dc := gg.NewContext(S, S)
+ dc.SetRGB(1, 1, 1)
+ dc.Clear()
+ dc.Translate(S/2, S/2)
+ dc.Scale(40, 40)
+
+ var x0, y0, x1, y1, x2, y2, x3, y3, x4, y4 float64
+ x0, y0 = -10, 0
+ x1, y1 = -5, -10
+ x2, y2 = 0, 0
+ x3, y3 = 5, 10
+ x4, y4 = 10, 0
+
+ dc.MoveTo(x0, y0)
+ dc.LineTo(x1, y1)
+ dc.LineTo(x2, y2)
+ dc.LineTo(x3, y3)
+ dc.LineTo(x4, y4)
+ dc.SetHexColor("FF2D00")
+ dc.SetLineWidth(8)
+ dc.Stroke()
+
+ dc.MoveTo(x0, y0)
+ dc.QuadraticTo(x1, y1, x2, y2)
+ dc.QuadraticTo(x3, y3, x4, y4)
+ dc.SetHexColor("3E606F")
+ dc.SetLineWidth(16)
+ dc.FillPreserve()
+ dc.SetRGB(0, 0, 0)
+ dc.Stroke()
+
+ dc.DrawCircle(x0, y0, 0.5)
+ dc.DrawCircle(x1, y1, 0.5)
+ dc.DrawCircle(x2, y2, 0.5)
+ dc.DrawCircle(x3, y3, 0.5)
+ dc.DrawCircle(x4, y4, 0.5)
+ dc.SetRGB(1, 1, 1)
+ dc.FillPreserve()
+ dc.SetRGB(0, 0, 0)
+ dc.SetLineWidth(4)
+ dc.Stroke()
+
+ dc.LoadFontFace("/Library/Fonts/Arial.ttf", 200)
+ dc.DrawStringAnchored("g", -5, 5, 0.5, 0.5)
+ dc.DrawStringAnchored("G", 5, -5, 0.5, 0.5)
+
+ dc.SavePNG("out.png")
+}
diff --git a/vendor/github.com/fogleman/gg/examples/scatter.go b/vendor/github.com/fogleman/gg/examples/scatter.go
new file mode 100644
index 0000000..3dca3c0
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/examples/scatter.go
@@ -0,0 +1,66 @@
+package main
+
+import (
+ "math/rand"
+
+ "github.com/fogleman/gg"
+)
+
+func CreatePoints(n int) []gg.Point {
+ points := make([]gg.Point, n)
+ for i := 0; i < n; i++ {
+ x := 0.5 + rand.NormFloat64()*0.1
+ y := x + rand.NormFloat64()*0.1
+ points[i] = gg.Point{x, y}
+ }
+ return points
+}
+
+func main() {
+ const S = 1024
+ const P = 64
+ dc := gg.NewContext(S, S)
+ dc.InvertY()
+ dc.SetRGB(1, 1, 1)
+ dc.Clear()
+ points := CreatePoints(1000)
+ dc.Translate(P, P)
+ dc.Scale(S-P*2, S-P*2)
+ // draw minor grid
+ for i := 1; i <= 10; i++ {
+ x := float64(i) / 10
+ dc.MoveTo(x, 0)
+ dc.LineTo(x, 1)
+ dc.MoveTo(0, x)
+ dc.LineTo(1, x)
+ }
+ dc.SetRGBA(0, 0, 0, 0.25)
+ dc.SetLineWidth(1)
+ dc.Stroke()
+ // draw axes
+ dc.MoveTo(0, 0)
+ dc.LineTo(1, 0)
+ dc.MoveTo(0, 0)
+ dc.LineTo(0, 1)
+ dc.SetRGB(0, 0, 0)
+ dc.SetLineWidth(4)
+ dc.Stroke()
+ // draw points
+ dc.SetRGBA(0, 0, 1, 0.5)
+ for _, p := range points {
+ dc.DrawCircle(p.X, p.Y, 3.0/S)
+ dc.Fill()
+ }
+ // draw text
+ dc.Identity()
+ dc.SetRGB(0, 0, 0)
+ if err := dc.LoadFontFace("/Library/Fonts/Arial Bold.ttf", 24); err != nil {
+ panic(err)
+ }
+ dc.DrawStringAnchored("Chart Title", S/2, P/2, 0.5, 0.5)
+ if err := dc.LoadFontFace("/Library/Fonts/Arial.ttf", 18); err != nil {
+ panic(err)
+ }
+ dc.DrawStringAnchored("X Axis Title", S/2, S-P/2, 0.5, 0.5)
+ dc.SavePNG("out.png")
+}
diff --git a/vendor/github.com/fogleman/gg/examples/sine.go b/vendor/github.com/fogleman/gg/examples/sine.go
new file mode 100644
index 0000000..679da06
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/examples/sine.go
@@ -0,0 +1,29 @@
+package main
+
+import (
+ "math"
+
+ "github.com/fogleman/gg"
+)
+
+func main() {
+ const W = 1200
+ const H = 60
+ dc := gg.NewContext(W, H)
+ // dc.SetHexColor("#FFFFFF")
+ // dc.Clear()
+ dc.ScaleAbout(0.95, 0.75, W/2, H/2)
+ for i := 0; i < W; i++ {
+ a := float64(i) * 2 * math.Pi / W * 8
+ x := float64(i)
+ y := (math.Sin(a) + 1) / 2 * H
+ dc.LineTo(x, y)
+ }
+ dc.ClosePath()
+ dc.SetHexColor("#3E606F")
+ dc.FillPreserve()
+ dc.SetHexColor("#19344180")
+ dc.SetLineWidth(8)
+ dc.Stroke()
+ dc.SavePNG("out.png")
+}
diff --git a/vendor/github.com/fogleman/gg/examples/spiral.go b/vendor/github.com/fogleman/gg/examples/spiral.go
new file mode 100644
index 0000000..47e8ee5
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/examples/spiral.go
@@ -0,0 +1,27 @@
+package main
+
+import (
+ "math"
+
+ "github.com/fogleman/gg"
+)
+
+func main() {
+ const S = 1024
+ const N = 2048
+ dc := gg.NewContext(S, S)
+ dc.SetRGB(1, 1, 1)
+ dc.Clear()
+ dc.SetRGB(0, 0, 0)
+ for i := 0; i <= N; i++ {
+ t := float64(i) / N
+ d := t*S*0.4 + 10
+ a := t * math.Pi * 2 * 20
+ x := S/2 + math.Cos(a)*d
+ y := S/2 + math.Sin(a)*d
+ r := t * 8
+ dc.DrawCircle(x, y, r)
+ }
+ dc.Fill()
+ dc.SavePNG("out.png")
+}
diff --git a/vendor/github.com/fogleman/gg/examples/star.go b/vendor/github.com/fogleman/gg/examples/star.go
new file mode 100644
index 0000000..05c08d6
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/examples/star.go
@@ -0,0 +1,40 @@
+package main
+
+import (
+ "math"
+
+ "github.com/fogleman/gg"
+)
+
+type Point struct {
+ X, Y float64
+}
+
+func Polygon(n int, x, y, r float64) []Point {
+ result := make([]Point, n)
+ for i := 0; i < n; i++ {
+ a := float64(i)*2*math.Pi/float64(n) - math.Pi/2
+ result[i] = Point{x + r*math.Cos(a), y + r*math.Sin(a)}
+ }
+ return result
+}
+
+func main() {
+ n := 5
+ points := Polygon(n, 512, 512, 400)
+ dc := gg.NewContext(1024, 1024)
+ dc.SetHexColor("fff")
+ dc.Clear()
+ for i := 0; i < n+1; i++ {
+ index := (i * 2) % n
+ p := points[index]
+ dc.LineTo(p.X, p.Y)
+ }
+ dc.SetRGBA(0, 0.5, 0, 1)
+ dc.SetFillRule(gg.FillRuleEvenOdd)
+ dc.FillPreserve()
+ dc.SetRGBA(0, 1, 0, 0.5)
+ dc.SetLineWidth(16)
+ dc.Stroke()
+ dc.SavePNG("out.png")
+}
diff --git a/vendor/github.com/fogleman/gg/examples/stars.go b/vendor/github.com/fogleman/gg/examples/stars.go
new file mode 100644
index 0000000..8999d12
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/examples/stars.go
@@ -0,0 +1,51 @@
+package main
+
+import (
+ "math"
+ "math/rand"
+
+ "github.com/fogleman/gg"
+)
+
+type Point struct {
+ X, Y float64
+}
+
+func Polygon(n int) []Point {
+ result := make([]Point, n)
+ for i := 0; i < n; i++ {
+ a := float64(i)*2*math.Pi/float64(n) - math.Pi/2
+ result[i] = Point{math.Cos(a), math.Sin(a)}
+ }
+ return result
+}
+
+func main() {
+ const W = 1200
+ const H = 120
+ const S = 100
+ dc := gg.NewContext(W, H)
+ dc.SetHexColor("#FFFFFF")
+ dc.Clear()
+ n := 5
+ points := Polygon(n)
+ for x := S / 2; x < W; x += S {
+ dc.Push()
+ s := rand.Float64()*S/4 + S/4
+ dc.Translate(float64(x), H/2)
+ dc.Rotate(rand.Float64() * 2 * math.Pi)
+ dc.Scale(s, s)
+ for i := 0; i < n+1; i++ {
+ index := (i * 2) % n
+ p := points[index]
+ dc.LineTo(p.X, p.Y)
+ }
+ dc.SetLineWidth(10)
+ dc.SetHexColor("#FFCC00")
+ dc.StrokePreserve()
+ dc.SetHexColor("#FFE43A")
+ dc.Fill()
+ dc.Pop()
+ }
+ dc.SavePNG("out.png")
+}
diff --git a/vendor/github.com/fogleman/gg/examples/text.go b/vendor/github.com/fogleman/gg/examples/text.go
new file mode 100644
index 0000000..2b16aee
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/examples/text.go
@@ -0,0 +1,16 @@
+package main
+
+import "github.com/fogleman/gg"
+
+func main() {
+ const S = 1024
+ dc := gg.NewContext(S, S)
+ dc.SetRGB(1, 1, 1)
+ dc.Clear()
+ dc.SetRGB(0, 0, 0)
+ if err := dc.LoadFontFace("/Library/Fonts/Arial.ttf", 96); err != nil {
+ panic(err)
+ }
+ dc.DrawStringAnchored("Hello, world!", S/2, S/2, 0.5, 0.5)
+ dc.SavePNG("out.png")
+}
diff --git a/vendor/github.com/fogleman/gg/examples/tiling.go b/vendor/github.com/fogleman/gg/examples/tiling.go
new file mode 100644
index 0000000..9688722
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/examples/tiling.go
@@ -0,0 +1,21 @@
+package main
+
+import "github.com/fogleman/gg"
+
+func main() {
+ const NX = 4
+ const NY = 3
+ im, err := gg.LoadPNG("examples/gopher.png")
+ if err != nil {
+ panic(err)
+ }
+ w := im.Bounds().Size().X
+ h := im.Bounds().Size().Y
+ dc := gg.NewContext(w*NX, h*NY)
+ for y := 0; y < NY; y++ {
+ for x := 0; x < NX; x++ {
+ dc.DrawImage(im, x*w, y*h)
+ }
+ }
+ dc.SavePNG("out.png")
+}
diff --git a/vendor/github.com/fogleman/gg/examples/wrap.go b/vendor/github.com/fogleman/gg/examples/wrap.go
new file mode 100644
index 0000000..f654e3b
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/examples/wrap.go
@@ -0,0 +1,40 @@
+package main
+
+import "github.com/fogleman/gg"
+
+const TEXT = "Call me Ishmael. Some years ago—never mind how long precisely—having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little and see the watery part of the world. It is a way I have of driving off the spleen and regulating the circulation. Whenever I find myself growing grim about the mouth; whenever it is a damp, drizzly November in my soul; whenever I find myself involuntarily pausing before coffin warehouses, and bringing up the rear of every funeral I meet; and especially whenever my hypos get such an upper hand of me, that it requires a strong moral principle to prevent me from deliberately stepping into the street, and methodically knocking people's hats off—then, I account it high time to get to sea as soon as I can. This is my substitute for pistol and ball. With a philosophical flourish Cato throws himself upon his sword; I quietly take to the ship. There is nothing surprising in this. If they but knew it, almost all men in their degree, some time or other, cherish very nearly the same feelings towards the ocean with me."
+
+func main() {
+ const W = 1024
+ const H = 1024
+ const P = 16
+ dc := gg.NewContext(W, H)
+ dc.SetRGB(1, 1, 1)
+ dc.Clear()
+ dc.DrawLine(W/2, 0, W/2, H)
+ dc.DrawLine(0, H/2, W, H/2)
+ dc.DrawRectangle(P, P, W-P-P, H-P-P)
+ dc.SetRGBA(0, 0, 1, 0.25)
+ dc.SetLineWidth(3)
+ dc.Stroke()
+ dc.SetRGB(0, 0, 0)
+ if err := dc.LoadFontFace("/Library/Fonts/Arial Bold.ttf", 18); err != nil {
+ panic(err)
+ }
+ dc.DrawStringWrapped("UPPER LEFT", P, P, 0, 0, 0, 1.5, gg.AlignLeft)
+ dc.DrawStringWrapped("UPPER RIGHT", W-P, P, 1, 0, 0, 1.5, gg.AlignRight)
+ dc.DrawStringWrapped("BOTTOM LEFT", P, H-P, 0, 1, 0, 1.5, gg.AlignLeft)
+ dc.DrawStringWrapped("BOTTOM RIGHT", W-P, H-P, 1, 1, 0, 1.5, gg.AlignRight)
+ dc.DrawStringWrapped("UPPER MIDDLE", W/2, P, 0.5, 0, 0, 1.5, gg.AlignCenter)
+ dc.DrawStringWrapped("LOWER MIDDLE", W/2, H-P, 0.5, 1, 0, 1.5, gg.AlignCenter)
+ dc.DrawStringWrapped("LEFT MIDDLE", P, H/2, 0, 0.5, 0, 1.5, gg.AlignLeft)
+ dc.DrawStringWrapped("RIGHT MIDDLE", W-P, H/2, 1, 0.5, 0, 1.5, gg.AlignRight)
+ if err := dc.LoadFontFace("/Library/Fonts/Arial.ttf", 12); err != nil {
+ panic(err)
+ }
+ dc.DrawStringWrapped(TEXT, W/2-P, H/2-P, 1, 1, W/3, 1.75, gg.AlignLeft)
+ dc.DrawStringWrapped(TEXT, W/2+P, H/2-P, 0, 1, W/3, 2, gg.AlignLeft)
+ dc.DrawStringWrapped(TEXT, W/2-P, H/2+P, 1, 0, W/3, 2.25, gg.AlignLeft)
+ dc.DrawStringWrapped(TEXT, W/2+P, H/2+P, 0, 0, W/3, 2.5, gg.AlignLeft)
+ dc.SavePNG("out.png")
+}
diff --git a/vendor/github.com/fogleman/gg/matrix.go b/vendor/github.com/fogleman/gg/matrix.go
new file mode 100644
index 0000000..7d5b312
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/matrix.go
@@ -0,0 +1,88 @@
+package gg
+
+import "math"
+
+type Matrix struct {
+ XX, YX, XY, YY, X0, Y0 float64
+}
+
+func Identity() Matrix {
+ return Matrix{
+ 1, 0,
+ 0, 1,
+ 0, 0,
+ }
+}
+
+func Translate(x, y float64) Matrix {
+ return Matrix{
+ 1, 0,
+ 0, 1,
+ x, y,
+ }
+}
+
+func Scale(x, y float64) Matrix {
+ return Matrix{
+ x, 0,
+ 0, y,
+ 0, 0,
+ }
+}
+
+func Rotate(angle float64) Matrix {
+ c := math.Cos(angle)
+ s := math.Sin(angle)
+ return Matrix{
+ c, s,
+ -s, c,
+ 0, 0,
+ }
+}
+
+func Shear(x, y float64) Matrix {
+ return Matrix{
+ 1, y,
+ x, 1,
+ 0, 0,
+ }
+}
+
+func (a Matrix) Multiply(b Matrix) Matrix {
+ return Matrix{
+ a.XX*b.XX + a.YX*b.XY,
+ a.XX*b.YX + a.YX*b.YY,
+ a.XY*b.XX + a.YY*b.XY,
+ a.XY*b.YX + a.YY*b.YY,
+ a.X0*b.XX + a.Y0*b.XY + b.X0,
+ a.X0*b.YX + a.Y0*b.YY + b.Y0,
+ }
+}
+
+func (a Matrix) TransformVector(x, y float64) (tx, ty float64) {
+ tx = a.XX*x + a.XY*y
+ ty = a.YX*x + a.YY*y
+ return
+}
+
+func (a Matrix) TransformPoint(x, y float64) (tx, ty float64) {
+ tx = a.XX*x + a.XY*y + a.X0
+ ty = a.YX*x + a.YY*y + a.Y0
+ return
+}
+
+func (a Matrix) Translate(x, y float64) Matrix {
+ return Translate(x, y).Multiply(a)
+}
+
+func (a Matrix) Scale(x, y float64) Matrix {
+ return Scale(x, y).Multiply(a)
+}
+
+func (a Matrix) Rotate(angle float64) Matrix {
+ return Rotate(angle).Multiply(a)
+}
+
+func (a Matrix) Shear(x, y float64) Matrix {
+ return Shear(x, y).Multiply(a)
+}
diff --git a/vendor/github.com/fogleman/gg/path.go b/vendor/github.com/fogleman/gg/path.go
new file mode 100644
index 0000000..74785b6
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/path.go
@@ -0,0 +1,140 @@
+package gg
+
+import (
+ "github.com/golang/freetype/raster"
+ "golang.org/x/image/math/fixed"
+)
+
+func flattenPath(p raster.Path) [][]Point {
+ var result [][]Point
+ var path []Point
+ var cx, cy float64
+ for i := 0; i < len(p); {
+ switch p[i] {
+ case 0:
+ if len(path) > 0 {
+ result = append(result, path)
+ path = nil
+ }
+ x := unfix(p[i+1])
+ y := unfix(p[i+2])
+ path = append(path, Point{x, y})
+ cx, cy = x, y
+ i += 4
+ case 1:
+ x := unfix(p[i+1])
+ y := unfix(p[i+2])
+ path = append(path, Point{x, y})
+ cx, cy = x, y
+ i += 4
+ case 2:
+ x1 := unfix(p[i+1])
+ y1 := unfix(p[i+2])
+ x2 := unfix(p[i+3])
+ y2 := unfix(p[i+4])
+ points := QuadraticBezier(cx, cy, x1, y1, x2, y2)
+ path = append(path, points...)
+ cx, cy = x2, y2
+ i += 6
+ case 3:
+ x1 := unfix(p[i+1])
+ y1 := unfix(p[i+2])
+ x2 := unfix(p[i+3])
+ y2 := unfix(p[i+4])
+ x3 := unfix(p[i+5])
+ y3 := unfix(p[i+6])
+ points := CubicBezier(cx, cy, x1, y1, x2, y2, x3, y3)
+ path = append(path, points...)
+ cx, cy = x3, y3
+ i += 8
+ default:
+ panic("bad path")
+ }
+ }
+ if len(path) > 0 {
+ result = append(result, path)
+ }
+ return result
+}
+
+func dashPath(paths [][]Point, dashes []float64) [][]Point {
+ var result [][]Point
+ if len(dashes) == 0 {
+ return paths
+ }
+ if len(dashes) == 1 {
+ dashes = append(dashes, dashes[0])
+ }
+ for _, path := range paths {
+ if len(path) < 2 {
+ continue
+ }
+ previous := path[0]
+ pathIndex := 1
+ dashIndex := 0
+ segmentLength := 0.0
+ var segment []Point
+ segment = append(segment, previous)
+ for pathIndex < len(path) {
+ dashLength := dashes[dashIndex]
+ point := path[pathIndex]
+ d := previous.Distance(point)
+ maxd := dashLength - segmentLength
+ if d > maxd {
+ t := maxd / d
+ p := previous.Interpolate(point, t)
+ segment = append(segment, p)
+ if dashIndex%2 == 0 && len(segment) > 1 {
+ result = append(result, segment)
+ }
+ segment = nil
+ segment = append(segment, p)
+ segmentLength = 0
+ previous = p
+ dashIndex = (dashIndex + 1) % len(dashes)
+ } else {
+ segment = append(segment, point)
+ previous = point
+ segmentLength += d
+ pathIndex++
+ }
+ }
+ if dashIndex%2 == 0 && len(segment) > 1 {
+ result = append(result, segment)
+ }
+ }
+ return result
+}
+
+func rasterPath(paths [][]Point) raster.Path {
+ var result raster.Path
+ for _, path := range paths {
+ var previous fixed.Point26_6
+ for i, point := range path {
+ f := point.Fixed()
+ if i == 0 {
+ result.Start(f)
+ } else {
+ dx := f.X - previous.X
+ dy := f.Y - previous.Y
+ if dx < 0 {
+ dx = -dx
+ }
+ if dy < 0 {
+ dy = -dy
+ }
+ if dx+dy > 8 {
+ // TODO: this is a hack for cases where two points are
+ // too close - causes rendering issues with joins / caps
+ result.Add1(f)
+ }
+ }
+ previous = f
+ }
+ }
+ return result
+}
+
+func dashed(path raster.Path, dashes []float64) raster.Path {
+ return rasterPath(dashPath(flattenPath(path), dashes))
+}
diff --git a/vendor/github.com/fogleman/gg/point.go b/vendor/github.com/fogleman/gg/point.go
new file mode 100644
index 0000000..d258653
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/point.go
@@ -0,0 +1,25 @@
+package gg
+
+import (
+ "math"
+
+ "golang.org/x/image/math/fixed"
+)
+
+type Point struct {
+ X, Y float64
+}
+
+func (a Point) Fixed() fixed.Point26_6 {
+ return fixp(a.X, a.Y)
+}
+
+func (a Point) Distance(b Point) float64 {
+ return math.Hypot(a.X-b.X, a.Y-b.Y)
+}
+
+func (a Point) Interpolate(b Point, t float64) Point {
+ x := a.X + (b.X-a.X)*t
+ y := a.Y + (b.Y-a.Y)*t
+ return Point{x, y}
+}
diff --git a/vendor/github.com/fogleman/gg/util.go b/vendor/github.com/fogleman/gg/util.go
new file mode 100644
index 0000000..62577f5
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/util.go
@@ -0,0 +1,116 @@
+package gg
+
+import (
+ "fmt"
+ "image"
+ "image/draw"
+ "image/png"
+ "io/ioutil"
+ "math"
+ "os"
+ "strings"
+
+ "github.com/golang/freetype/truetype"
+
+ "golang.org/x/image/font"
+ "golang.org/x/image/math/fixed"
+)
+
+func Radians(degrees float64) float64 {
+ return degrees * math.Pi / 180
+}
+
+func Degrees(radians float64) float64 {
+ return radians * 180 / math.Pi
+}
+
+func LoadImage(path string) (image.Image, error) {
+ file, err := os.Open(path)
+ if err != nil {
+ return nil, err
+ }
+ defer file.Close()
+ im, _, err := image.Decode(file)
+ return im, err
+}
+
+func LoadPNG(path string) (image.Image, error) {
+ file, err := os.Open(path)
+ if err != nil {
+ return nil, err
+ }
+ defer file.Close()
+ return png.Decode(file)
+}
+
+func SavePNG(path string, im image.Image) error {
+ file, err := os.Create(path)
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+ return png.Encode(file, im)
+}
+
+func imageToRGBA(src image.Image) *image.RGBA {
+ dst := image.NewRGBA(src.Bounds())
+ draw.Draw(dst, dst.Rect, src, image.ZP, draw.Src)
+ return dst
+}
+
+func parseHexColor(x string) (r, g, b, a int) {
+ x = strings.TrimPrefix(x, "#")
+ a = 255
+ if len(x) == 3 {
+ format := "%1x%1x%1x"
+ fmt.Sscanf(x, format, &r, &g, &b)
+ r |= r << 4
+ g |= g << 4
+ b |= b << 4
+ }
+ if len(x) == 6 {
+ format := "%02x%02x%02x"
+ fmt.Sscanf(x, format, &r, &g, &b)
+ }
+ if len(x) == 8 {
+ format := "%02x%02x%02x%02x"
+ fmt.Sscanf(x, format, &r, &g, &b, &a)
+ }
+ return
+}
+
+func fixp(x, y float64) fixed.Point26_6 {
+ return fixed.Point26_6{fix(x), fix(y)}
+}
+
+func fix(x float64) fixed.Int26_6 {
+ return fixed.Int26_6(x * 64)
+}
+
+func unfix(x fixed.Int26_6) float64 {
+ const shift, mask = 6, 1<<6 - 1
+ if x >= 0 {
+ return float64(x>>shift) + float64(x&mask)/64
+ }
+ x = -x
+ if x >= 0 {
+ return -(float64(x>>shift) + float64(x&mask)/64)
+ }
+ return 0
+}
+
+func loadFontFace(path string, points float64) (font.Face, error) {
+ fontBytes, err := ioutil.ReadFile(path)
+ if err != nil {
+ return nil, err
+ }
+ f, err := truetype.Parse(fontBytes)
+ if err != nil {
+ return nil, err
+ }
+ face := truetype.NewFace(f, &truetype.Options{
+ Size: points,
+ // Hinting: font.HintingFull,
+ })
+ return face, nil
+}
diff --git a/vendor/github.com/fogleman/gg/wrap.go b/vendor/github.com/fogleman/gg/wrap.go
new file mode 100644
index 0000000..3dfe6e1
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/wrap.go
@@ -0,0 +1,58 @@
+package gg
+
+import (
+ "strings"
+ "unicode"
+)
+
+type measureStringer interface {
+ MeasureString(s string) (w, h float64)
+}
+
+func splitOnSpace(x string) []string {
+ var result []string
+ pi := 0
+ ps := false
+ for i, c := range x {
+ s := unicode.IsSpace(c)
+ if s != ps && i > 0 {
+ result = append(result, x[pi:i])
+ pi = i
+ }
+ ps = s
+ }
+ result = append(result, x[pi:])
+ return result
+}
+
+func wordWrap(m measureStringer, s string, width float64) []string {
+ var result []string
+ for _, line := range strings.Split(s, "\n") {
+ fields := splitOnSpace(line)
+ if len(fields)%2 == 1 {
+ fields = append(fields, "")
+ }
+ x := ""
+ for i := 0; i < len(fields); i += 2 {
+ w, _ := m.MeasureString(x + fields[i])
+ if w > width {
+ if x == "" {
+ result = append(result, fields[i])
+ x = ""
+ continue
+ } else {
+ result = append(result, x)
+ x = ""
+ }
+ }
+ x += fields[i] + fields[i+1]
+ }
+ if x != "" {
+ result = append(result, x)
+ }
+ }
+ for i, line := range result {
+ result[i] = strings.TrimSpace(line)
+ }
+ return result
+}
diff --git a/vendor/github.com/gocarina/gocsv/LICENSE b/vendor/github.com/gocarina/gocsv/LICENSE
new file mode 100644
index 0000000..052a371
--- /dev/null
+++ b/vendor/github.com/gocarina/gocsv/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Jonathan Picques
+
+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. \ No newline at end of file
diff --git a/vendor/github.com/gocarina/gocsv/csv.go b/vendor/github.com/gocarina/gocsv/csv.go
new file mode 100644
index 0000000..98cd6df
--- /dev/null
+++ b/vendor/github.com/gocarina/gocsv/csv.go
@@ -0,0 +1,217 @@
+// Copyright 2014 Jonathan Picques. All rights reserved.
+// Use of this source code is governed by a MIT license
+// The license can be found in the LICENSE file.
+
+// The GoCSV package aims to provide easy CSV serialization and deserialization to the golang programming language
+
+package gocsv
+
+import (
+ "bytes"
+ "encoding/csv"
+ "fmt"
+ "io"
+ "os"
+ "reflect"
+ "strings"
+)
+
+// FailIfUnmatchedStructTags indicates whether it is considered an error when there is an unmatched
+// struct tag.
+var FailIfUnmatchedStructTags = false
+
+// FailIfDoubleHeaderNames indicates whether it is considered an error when a header name is repeated
+// in the csv header.
+var FailIfDoubleHeaderNames = false
+
+// ShouldAlignDuplicateHeadersWithStructFieldOrder indicates whether we should align duplicate CSV
+// headers per their alignment in the struct definition.
+var ShouldAlignDuplicateHeadersWithStructFieldOrder = false
+
+// TagSeparator defines seperator string for multiple csv tags in struct fields
+var TagSeparator = ","
+
+// --------------------------------------------------------------------------
+// CSVWriter used to format CSV
+
+var selfCSVWriter = DefaultCSVWriter
+
+// DefaultCSVWriter is the default CSV writer used to format CSV (cf. csv.NewWriter)
+func DefaultCSVWriter(out io.Writer) *csv.Writer {
+ writer := csv.NewWriter(out)
+
+ // As only one rune can be defined as a CSV separator, we are going to trim
+ // the custom tag separator and use the first rune.
+ if runes := []rune(strings.TrimSpace(TagSeparator)); len(runes) > 0 {
+ writer.Comma = runes[0]
+ }
+
+ return writer
+}
+
+// SetCSVWriter sets the CSV writer used to format CSV.
+func SetCSVWriter(csvWriter func(io.Writer) *csv.Writer) {
+ selfCSVWriter = csvWriter
+}
+
+func getCSVWriter(out io.Writer) *csv.Writer {
+ return selfCSVWriter(out)
+}
+
+// --------------------------------------------------------------------------
+// CSVReader used to parse CSV
+
+var selfCSVReader = DefaultCSVReader
+
+// DefaultCSVReader is the default CSV reader used to parse CSV (cf. csv.NewReader)
+func DefaultCSVReader(in io.Reader) *csv.Reader {
+ return csv.NewReader(in)
+}
+
+// LazyCSVReader returns a lazy CSV reader, with LazyQuotes and TrimLeadingSpace.
+func LazyCSVReader(in io.Reader) *csv.Reader {
+ csvReader := csv.NewReader(in)
+ csvReader.LazyQuotes = true
+ csvReader.TrimLeadingSpace = true
+ return csvReader
+}
+
+// SetCSVReader sets the CSV reader used to parse CSV.
+func SetCSVReader(csvReader func(io.Reader) *csv.Reader) {
+ selfCSVReader = csvReader
+}
+
+func getCSVReader(in io.Reader) *csv.Reader {
+ return selfCSVReader(in)
+}
+
+// --------------------------------------------------------------------------
+// Marshal functions
+
+// MarshalFile saves the interface as CSV in the file.
+func MarshalFile(in interface{}, file *os.File) (err error) {
+ return Marshal(in, file)
+}
+
+// MarshalString returns the CSV string from the interface.
+func MarshalString(in interface{}) (out string, err error) {
+ bufferString := bytes.NewBufferString(out)
+ if err := Marshal(in, bufferString); err != nil {
+ return "", err
+ }
+ return bufferString.String(), nil
+}
+
+// MarshalBytes returns the CSV bytes from the interface.
+func MarshalBytes(in interface{}) (out []byte, err error) {
+ bufferString := bytes.NewBuffer(out)
+ if err := Marshal(in, bufferString); err != nil {
+ return nil, err
+ }
+ return bufferString.Bytes(), nil
+}
+
+// Marshal returns the CSV in writer from the interface.
+func Marshal(in interface{}, out io.Writer) (err error) {
+ writer := getCSVWriter(out)
+ return writeTo(writer, in, false)
+}
+
+// Marshal returns the CSV in writer from the interface.
+func MarshalWithoutHeaders(in interface{}, out io.Writer) (err error) {
+ writer := getCSVWriter(out)
+ return writeTo(writer, in, true)
+}
+
+// MarshalChan returns the CSV read from the channel.
+func MarshalChan(c <-chan interface{}, out *csv.Writer) error {
+ return writeFromChan(out, c)
+}
+
+// MarshalCSV returns the CSV in writer from the interface.
+func MarshalCSV(in interface{}, out *csv.Writer) (err error) {
+ return writeTo(out, in, false)
+}
+
+// --------------------------------------------------------------------------
+// Unmarshal functions
+
+// UnmarshalFile parses the CSV from the file in the interface.
+func UnmarshalFile(in *os.File, out interface{}) (err error) {
+ return Unmarshal(in, out)
+}
+
+// UnmarshalString parses the CSV from the string in the interface.
+func UnmarshalString(in string, out interface{}) (err error) {
+ return Unmarshal(strings.NewReader(in), out)
+}
+
+// UnmarshalBytes parses the CSV from the bytes in the interface.
+func UnmarshalBytes(in []byte, out interface{}) (err error) {
+ return Unmarshal(bytes.NewReader(in), out)
+}
+
+// Unmarshal parses the CSV from the reader in the interface.
+func Unmarshal(in io.Reader, out interface{}) (err error) {
+ return readTo(newDecoder(in), out)
+}
+
+// UnmarshalCSV parses the CSV from the reader in the interface.
+func UnmarshalCSV(in CSVReader, out interface{}) error {
+ return readTo(csvDecoder{in}, out)
+}
+
+// UnmarshalToChan parses the CSV from the reader and send each value in the chan c.
+// The channel must have a concrete type.
+func UnmarshalToChan(in io.Reader, c interface{}) (err error) {
+ return readEach(newDecoder(in), c)
+}
+
+// UnmarshalStringToChan parses the CSV from the string and send each value in the chan c.
+// The channel must have a concrete type.
+func UnmarshalStringToChan(in string, c interface{}) (err error) {
+ return UnmarshalToChan(strings.NewReader(in), c)
+}
+
+// UnmarshalBytesToChan parses the CSV from the bytes and send each value in the chan c.
+// The channel must have a concrete type.
+func UnmarshalBytesToChan(in []byte, c interface{}) (err error) {
+ return UnmarshalToChan(bytes.NewReader(in), c)
+}
+
+// UnmarshalToCallback parses the CSV from the reader and send each value to the given func f.
+// The func must look like func(Struct).
+func UnmarshalToCallback(in io.Reader, f interface{}) (err error) {
+ valueFunc := reflect.ValueOf(f)
+ t := reflect.TypeOf(f)
+ if t.NumIn() != 1 {
+ return fmt.Errorf("the given function must have exactly one parameter")
+ }
+ c := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, t.In(0)), 0)
+ go func() {
+ err = UnmarshalToChan(in, c.Interface())
+ }()
+ for {
+ if err != nil {
+ return err
+ }
+ v, notClosed := c.Recv()
+ if !notClosed || v.Interface() == nil {
+ break
+ }
+ valueFunc.Call([]reflect.Value{v})
+ }
+ return
+}
+
+// UnmarshalBytesToCallback parses the CSV from the bytes and send each value to the given func f.
+// The func must look like func(Struct).
+func UnmarshalBytesToCallback(in []byte, f interface{}) (err error) {
+ return UnmarshalToCallback(bytes.NewReader(in), f)
+}
+
+// UnmarshalStringToCallback parses the CSV from the string and send each value to the given func f.
+// The func must look like func(Struct).
+func UnmarshalStringToCallback(in string, c interface{}) (err error) {
+ return UnmarshalToCallback(strings.NewReader(in), c)
+}
diff --git a/vendor/github.com/gocarina/gocsv/decode.go b/vendor/github.com/gocarina/gocsv/decode.go
new file mode 100644
index 0000000..5d57383
--- /dev/null
+++ b/vendor/github.com/gocarina/gocsv/decode.go
@@ -0,0 +1,296 @@
+package gocsv
+
+import (
+ "encoding/csv"
+ "errors"
+ "fmt"
+ "io"
+ "reflect"
+)
+
+// Decoder .
+type Decoder interface {
+ getCSVRows() ([][]string, error)
+}
+
+// SimpleDecoder .
+type SimpleDecoder interface {
+ getCSVRow() ([]string, error)
+}
+
+type decoder struct {
+ in io.Reader
+ csvDecoder *csvDecoder
+}
+
+func newDecoder(in io.Reader) *decoder {
+ return &decoder{in: in}
+}
+
+func (decode *decoder) getCSVRows() ([][]string, error) {
+ return getCSVReader(decode.in).ReadAll()
+}
+
+func (decode *decoder) getCSVRow() ([]string, error) {
+ if decode.csvDecoder == nil {
+ decode.csvDecoder = &csvDecoder{getCSVReader(decode.in)}
+ }
+ return decode.csvDecoder.Read()
+}
+
+type CSVReader interface {
+ Read() ([]string, error)
+ ReadAll() ([][]string, error)
+}
+
+type csvDecoder struct {
+ CSVReader
+}
+
+func (c csvDecoder) getCSVRows() ([][]string, error) {
+ return c.ReadAll()
+}
+
+func (c csvDecoder) getCSVRow() ([]string, error) {
+ return c.Read()
+}
+
+func maybeMissingStructFields(structInfo []fieldInfo, headers []string) error {
+ if len(structInfo) == 0 {
+ return nil
+ }
+
+ headerMap := make(map[string]struct{}, len(headers))
+ for idx := range headers {
+ headerMap[headers[idx]] = struct{}{}
+ }
+
+ for _, info := range structInfo {
+ found := false
+ for _, key := range info.keys {
+ if _, ok := headerMap[key]; ok {
+ found = true
+ break
+ }
+ }
+ if !found {
+ return fmt.Errorf("found unmatched struct field with tags %v", info.keys)
+ }
+ }
+ return nil
+}
+
+// Check that no header name is repeated twice
+func maybeDoubleHeaderNames(headers []string) error {
+ headerMap := make(map[string]bool, len(headers))
+ for _, v := range headers {
+ if _, ok := headerMap[v]; ok {
+ return fmt.Errorf("Repeated header name: %v", v)
+ }
+ headerMap[v] = true
+ }
+ return nil
+}
+
+func readTo(decoder Decoder, out interface{}) error {
+ outValue, outType := getConcreteReflectValueAndType(out) // Get the concrete type (not pointer) (Slice<?> or Array<?>)
+ if err := ensureOutType(outType); err != nil {
+ return err
+ }
+ outInnerWasPointer, outInnerType := getConcreteContainerInnerType(outType) // Get the concrete inner type (not pointer) (Container<"?">)
+ if err := ensureOutInnerType(outInnerType); err != nil {
+ return err
+ }
+ csvRows, err := decoder.getCSVRows() // Get the CSV csvRows
+ if err != nil {
+ return err
+ }
+ if len(csvRows) == 0 {
+ return errors.New("empty csv file given")
+ }
+ if err := ensureOutCapacity(&outValue, len(csvRows)); err != nil { // Ensure the container is big enough to hold the CSV content
+ return err
+ }
+ outInnerStructInfo := getStructInfo(outInnerType) // Get the inner struct info to get CSV annotations
+ if len(outInnerStructInfo.Fields) == 0 {
+ return errors.New("no csv struct tags found")
+ }
+
+ headers := csvRows[0]
+ body := csvRows[1:]
+
+ csvHeadersLabels := make(map[int]*fieldInfo, len(outInnerStructInfo.Fields)) // Used to store the correspondance header <-> position in CSV
+
+ headerCount := map[string]int{}
+ for i, csvColumnHeader := range headers {
+ curHeaderCount := headerCount[csvColumnHeader]
+ if fieldInfo := getCSVFieldPosition(csvColumnHeader, outInnerStructInfo, curHeaderCount); fieldInfo != nil {
+ csvHeadersLabels[i] = fieldInfo
+ if ShouldAlignDuplicateHeadersWithStructFieldOrder {
+ curHeaderCount++
+ headerCount[csvColumnHeader] = curHeaderCount
+ }
+ }
+ }
+
+ if FailIfUnmatchedStructTags {
+ if err := maybeMissingStructFields(outInnerStructInfo.Fields, headers); err != nil {
+ return err
+ }
+ }
+ if FailIfDoubleHeaderNames {
+ if err := maybeDoubleHeaderNames(headers); err != nil {
+ return err
+ }
+ }
+
+ for i, csvRow := range body {
+ outInner := createNewOutInner(outInnerWasPointer, outInnerType)
+ for j, csvColumnContent := range csvRow {
+ if fieldInfo, ok := csvHeadersLabels[j]; ok { // Position found accordingly to header name
+ if err := setInnerField(&outInner, outInnerWasPointer, fieldInfo.IndexChain, csvColumnContent); err != nil { // Set field of struct
+ return &csv.ParseError{
+ Line: i + 2, //add 2 to account for the header & 0-indexing of arrays
+ Column: j + 1,
+ Err: err,
+ }
+ }
+ }
+ }
+ outValue.Index(i).Set(outInner)
+ }
+ return nil
+}
+
+func readEach(decoder SimpleDecoder, c interface{}) error {
+ headers, err := decoder.getCSVRow()
+ if err != nil {
+ return err
+ }
+ outValue, outType := getConcreteReflectValueAndType(c) // Get the concrete type (not pointer) (Slice<?> or Array<?>)
+ if err := ensureOutType(outType); err != nil {
+ return err
+ }
+ defer outValue.Close()
+ outInnerWasPointer, outInnerType := getConcreteContainerInnerType(outType) // Get the concrete inner type (not pointer) (Container<"?">)
+ if err := ensureOutInnerType(outInnerType); err != nil {
+ return err
+ }
+ outInnerStructInfo := getStructInfo(outInnerType) // Get the inner struct info to get CSV annotations
+ if len(outInnerStructInfo.Fields) == 0 {
+ return errors.New("no csv struct tags found")
+ }
+ csvHeadersLabels := make(map[int]*fieldInfo, len(outInnerStructInfo.Fields)) // Used to store the correspondance header <-> position in CSV
+ headerCount := map[string]int{}
+ for i, csvColumnHeader := range headers {
+ curHeaderCount := headerCount[csvColumnHeader]
+ if fieldInfo := getCSVFieldPosition(csvColumnHeader, outInnerStructInfo, curHeaderCount); fieldInfo != nil {
+ csvHeadersLabels[i] = fieldInfo
+ if ShouldAlignDuplicateHeadersWithStructFieldOrder {
+ curHeaderCount++
+ headerCount[csvColumnHeader] = curHeaderCount
+ }
+ }
+ }
+ if err := maybeMissingStructFields(outInnerStructInfo.Fields, headers); err != nil {
+ if FailIfUnmatchedStructTags {
+ return err
+ }
+ }
+ if FailIfDoubleHeaderNames {
+ if err := maybeDoubleHeaderNames(headers); err != nil {
+ return err
+ }
+ }
+ i := 0
+ for {
+ line, err := decoder.getCSVRow()
+ if err == io.EOF {
+ break
+ } else if err != nil {
+ return err
+ }
+ outInner := createNewOutInner(outInnerWasPointer, outInnerType)
+ for j, csvColumnContent := range line {
+ if fieldInfo, ok := csvHeadersLabels[j]; ok { // Position found accordingly to header name
+ if err := setInnerField(&outInner, outInnerWasPointer, fieldInfo.IndexChain, csvColumnContent); err != nil { // Set field of struct
+ return &csv.ParseError{
+ Line: i + 2, //add 2 to account for the header & 0-indexing of arrays
+ Column: j + 1,
+ Err: err,
+ }
+ }
+ }
+ }
+ outValue.Send(outInner)
+ i++
+ }
+ return nil
+}
+
+// Check if the outType is an array or a slice
+func ensureOutType(outType reflect.Type) error {
+ switch outType.Kind() {
+ case reflect.Slice:
+ fallthrough
+ case reflect.Chan:
+ fallthrough
+ case reflect.Array:
+ return nil
+ }
+ return fmt.Errorf("cannot use " + outType.String() + ", only slice or array supported")
+}
+
+// Check if the outInnerType is of type struct
+func ensureOutInnerType(outInnerType reflect.Type) error {
+ switch outInnerType.Kind() {
+ case reflect.Struct:
+ return nil
+ }
+ return fmt.Errorf("cannot use " + outInnerType.String() + ", only struct supported")
+}
+
+func ensureOutCapacity(out *reflect.Value, csvLen int) error {
+ switch out.Kind() {
+ case reflect.Array:
+ if out.Len() < csvLen-1 { // Array is not big enough to hold the CSV content (arrays are not addressable)
+ return fmt.Errorf("array capacity problem: cannot store %d %s in %s", csvLen-1, out.Type().Elem().String(), out.Type().String())
+ }
+ case reflect.Slice:
+ if !out.CanAddr() && out.Len() < csvLen-1 { // Slice is not big enough tho hold the CSV content and is not addressable
+ return fmt.Errorf("slice capacity problem and is not addressable (did you forget &?)")
+ } else if out.CanAddr() && out.Len() < csvLen-1 {
+ out.Set(reflect.MakeSlice(out.Type(), csvLen-1, csvLen-1)) // Slice is not big enough, so grows it
+ }
+ }
+ return nil
+}
+
+func getCSVFieldPosition(key string, structInfo *structInfo, curHeaderCount int) *fieldInfo {
+ matchedFieldCount := 0
+ for _, field := range structInfo.Fields {
+ if field.matchesKey(key) {
+ if matchedFieldCount >= curHeaderCount {
+ return &field
+ } else {
+ matchedFieldCount++
+ }
+ }
+ }
+ return nil
+}
+
+func createNewOutInner(outInnerWasPointer bool, outInnerType reflect.Type) reflect.Value {
+ if outInnerWasPointer {
+ return reflect.New(outInnerType)
+ }
+ return reflect.New(outInnerType).Elem()
+}
+
+func setInnerField(outInner *reflect.Value, outInnerWasPointer bool, index []int, value string) error {
+ oi := *outInner
+ if outInnerWasPointer {
+ oi = outInner.Elem()
+ }
+ return setField(oi.FieldByIndex(index), value)
+}
diff --git a/vendor/github.com/gocarina/gocsv/encode.go b/vendor/github.com/gocarina/gocsv/encode.go
new file mode 100644
index 0000000..4a87462
--- /dev/null
+++ b/vendor/github.com/gocarina/gocsv/encode.go
@@ -0,0 +1,137 @@
+package gocsv
+
+import (
+ "encoding/csv"
+ "fmt"
+ "io"
+ "reflect"
+)
+
+type encoder struct {
+ out io.Writer
+}
+
+func newEncoder(out io.Writer) *encoder {
+ return &encoder{out}
+}
+
+func writeFromChan(writer *csv.Writer, c <-chan interface{}) error {
+ // Get the first value. It wil determine the header structure.
+ firstValue := <-c
+ inValue, inType := getConcreteReflectValueAndType(firstValue) // Get the concrete type
+ if err := ensureStructOrPtr(inType); err != nil {
+ return err
+ }
+ inInnerWasPointer := inType.Kind() == reflect.Ptr
+ inInnerStructInfo := getStructInfo(inType) // Get the inner struct info to get CSV annotations
+ csvHeadersLabels := make([]string, len(inInnerStructInfo.Fields))
+ for i, fieldInfo := range inInnerStructInfo.Fields { // Used to write the header (first line) in CSV
+ csvHeadersLabels[i] = fieldInfo.getFirstKey()
+ }
+ if err := writer.Write(csvHeadersLabels); err != nil {
+ return err
+ }
+ write := func(val reflect.Value) error {
+ for j, fieldInfo := range inInnerStructInfo.Fields {
+ csvHeadersLabels[j] = ""
+ inInnerFieldValue, err := getInnerField(val, inInnerWasPointer, fieldInfo.IndexChain) // Get the correct field header <-> position
+ if err != nil {
+ return err
+ }
+ csvHeadersLabels[j] = inInnerFieldValue
+ }
+ if err := writer.Write(csvHeadersLabels); err != nil {
+ return err
+ }
+ return nil
+ }
+ if err := write(inValue); err != nil {
+ return err
+ }
+ for v := range c {
+ val, _ := getConcreteReflectValueAndType(v) // Get the concrete type (not pointer) (Slice<?> or Array<?>)
+ if err := ensureStructOrPtr(inType); err != nil {
+ return err
+ }
+ if err := write(val); err != nil {
+ return err
+ }
+ }
+ writer.Flush()
+ return writer.Error()
+}
+
+func writeTo(writer *csv.Writer, in interface{}, omitHeaders bool) error {
+ inValue, inType := getConcreteReflectValueAndType(in) // Get the concrete type (not pointer) (Slice<?> or Array<?>)
+ if err := ensureInType(inType); err != nil {
+ return err
+ }
+ inInnerWasPointer, inInnerType := getConcreteContainerInnerType(inType) // Get the concrete inner type (not pointer) (Container<"?">)
+ if err := ensureInInnerType(inInnerType); err != nil {
+ return err
+ }
+ inInnerStructInfo := getStructInfo(inInnerType) // Get the inner struct info to get CSV annotations
+ csvHeadersLabels := make([]string, len(inInnerStructInfo.Fields))
+ for i, fieldInfo := range inInnerStructInfo.Fields { // Used to write the header (first line) in CSV
+ csvHeadersLabels[i] = fieldInfo.getFirstKey()
+ }
+ if !omitHeaders {
+ if err := writer.Write(csvHeadersLabels); err != nil {
+ return err
+ }
+ }
+ inLen := inValue.Len()
+ for i := 0; i < inLen; i++ { // Iterate over container rows
+ for j, fieldInfo := range inInnerStructInfo.Fields {
+ csvHeadersLabels[j] = ""
+ inInnerFieldValue, err := getInnerField(inValue.Index(i), inInnerWasPointer, fieldInfo.IndexChain) // Get the correct field header <-> position
+ if err != nil {
+ return err
+ }
+ csvHeadersLabels[j] = inInnerFieldValue
+ }
+ if err := writer.Write(csvHeadersLabels); err != nil {
+ return err
+ }
+ }
+ writer.Flush()
+ return writer.Error()
+}
+
+func ensureStructOrPtr(t reflect.Type) error {
+ switch t.Kind() {
+ case reflect.Struct:
+ fallthrough
+ case reflect.Ptr:
+ return nil
+ }
+ return fmt.Errorf("cannot use " + t.String() + ", only slice or array supported")
+}
+
+// Check if the inType is an array or a slice
+func ensureInType(outType reflect.Type) error {
+ switch outType.Kind() {
+ case reflect.Slice:
+ fallthrough
+ case reflect.Array:
+ return nil
+ }
+ return fmt.Errorf("cannot use " + outType.String() + ", only slice or array supported")
+}
+
+// Check if the inInnerType is of type struct
+func ensureInInnerType(outInnerType reflect.Type) error {
+ switch outInnerType.Kind() {
+ case reflect.Struct:
+ return nil
+ }
+ return fmt.Errorf("cannot use " + outInnerType.String() + ", only struct supported")
+}
+
+func getInnerField(outInner reflect.Value, outInnerWasPointer bool, index []int) (string, error) {
+ oi := outInner
+ if outInnerWasPointer {
+ oi = outInner.Elem()
+ }
+ return getFieldAsString(oi.FieldByIndex(index))
+}
diff --git a/vendor/github.com/gocarina/gocsv/reflect.go b/vendor/github.com/gocarina/gocsv/reflect.go
new file mode 100644
index 0000000..e96fb57
--- /dev/null
+++ b/vendor/github.com/gocarina/gocsv/reflect.go
@@ -0,0 +1,104 @@
+package gocsv
+
+import (
+ "reflect"
+ "strings"
+ "sync"
+)
+
+// --------------------------------------------------------------------------
+// Reflection helpers
+
+type structInfo struct {
+ Fields []fieldInfo
+}
+
+// fieldInfo is a struct field that should be mapped to a CSV column, or vica-versa
+// Each IndexChain element before the last is the index of an the embedded struct field
+// that defines Key as a tag
+type fieldInfo struct {
+ keys []string
+ IndexChain []int
+}
+
+func (f fieldInfo) getFirstKey() string {
+ return f.keys[0]
+}
+
+func (f fieldInfo) matchesKey(key string) bool {
+ for _, k := range f.keys {
+ if key == k {
+ return true
+ }
+ }
+ return false
+}
+
+var structMap = make(map[reflect.Type]*structInfo)
+var structMapMutex sync.RWMutex
+
+func getStructInfo(rType reflect.Type) *structInfo {
+ structMapMutex.RLock()
+ stInfo, ok := structMap[rType]
+ structMapMutex.RUnlock()
+ if ok {
+ return stInfo
+ }
+ fieldsList := getFieldInfos(rType, []int{})
+ stInfo = &structInfo{fieldsList}
+ return stInfo
+}
+
+func getFieldInfos(rType reflect.Type, parentIndexChain []int) []fieldInfo {
+ fieldsCount := rType.NumField()
+ fieldsList := make([]fieldInfo, 0, fieldsCount)
+ for i := 0; i < fieldsCount; i++ {
+ field := rType.Field(i)
+ if field.PkgPath != "" {
+ continue
+ }
+ indexChain := append(parentIndexChain, i)
+ // if the field is an embedded struct, create a fieldInfo for each of its fields
+ if field.Anonymous && field.Type.Kind() == reflect.Struct {
+ fieldsList = append(fieldsList, getFieldInfos(field.Type, indexChain)...)
+ continue
+ }
+ fieldInfo := fieldInfo{IndexChain: indexChain}
+ fieldTag := field.Tag.Get("csv")
+ fieldTags := strings.Split(fieldTag, TagSeparator)
+ filteredTags := []string{}
+ for _, fieldTagEntry := range fieldTags {
+ if fieldTagEntry != "omitempty" {
+ filteredTags = append(filteredTags, fieldTagEntry)
+ }
+ }
+
+ if len(filteredTags) == 1 && filteredTags[0] == "-" {
+ continue
+ } else if len(filteredTags) > 0 && filteredTags[0] != "" {
+ fieldInfo.keys = filteredTags
+ } else {
+ fieldInfo.keys = []string{field.Name}
+ }
+ fieldsList = append(fieldsList, fieldInfo)
+ }
+ return fieldsList
+}
+
+func getConcreteContainerInnerType(in reflect.Type) (inInnerWasPointer bool, inInnerType reflect.Type) {
+ inInnerType = in.Elem()
+ inInnerWasPointer = false
+ if inInnerType.Kind() == reflect.Ptr {
+ inInnerWasPointer = true
+ inInnerType = inInnerType.Elem()
+ }
+ return inInnerWasPointer, inInnerType
+}
+
+func getConcreteReflectValueAndType(in interface{}) (reflect.Value, reflect.Type) {
+ value := reflect.ValueOf(in)
+ if value.Kind() == reflect.Ptr {
+ value = value.Elem()
+ }
+ return value, value.Type()
+}
diff --git a/vendor/github.com/gocarina/gocsv/types.go b/vendor/github.com/gocarina/gocsv/types.go
new file mode 100644
index 0000000..281c806
--- /dev/null
+++ b/vendor/github.com/gocarina/gocsv/types.go
@@ -0,0 +1,437 @@
+package gocsv
+
+import (
+ "encoding"
+ "fmt"
+ "reflect"
+ "strconv"
+ "strings"
+)
+
+// --------------------------------------------------------------------------
+// Conversion interfaces
+
+// TypeMarshaller is implemented by any value that has a MarshalCSV method
+// This converter is used to convert the value to it string representation
+type TypeMarshaller interface {
+ MarshalCSV() (string, error)
+}
+
+// Stringer is implemented by any value that has a String method
+// This converter is used to convert the value to it string representation
+// This converter will be used if your value does not implement TypeMarshaller
+type Stringer interface {
+ String() string
+}
+
+// TypeUnmarshaller is implemented by any value that has an UnmarshalCSV method
+// This converter is used to convert a string to your value representation of that string
+type TypeUnmarshaller interface {
+ UnmarshalCSV(string) error
+}
+
+// NoUnmarshalFuncError is the custom error type to be raised in case there is no unmarshal function defined on type
+type NoUnmarshalFuncError struct {
+ msg string
+}
+
+func (e NoUnmarshalFuncError) Error() string {
+ return e.msg
+}
+
+// NoMarshalFuncError is the custom error type to be raised in case there is no marshal function defined on type
+type NoMarshalFuncError struct {
+ msg string
+}
+
+func (e NoMarshalFuncError) Error() string {
+ return e.msg
+}
+
+var (
+ stringerType = reflect.TypeOf((*Stringer)(nil)).Elem()
+ marshallerType = reflect.TypeOf((*TypeMarshaller)(nil)).Elem()
+ unMarshallerType = reflect.TypeOf((*TypeUnmarshaller)(nil)).Elem()
+ textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
+ textUnMarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
+)
+
+// --------------------------------------------------------------------------
+// Conversion helpers
+
+func toString(in interface{}) (string, error) {
+ inValue := reflect.ValueOf(in)
+
+ switch inValue.Kind() {
+ case reflect.String:
+ return inValue.String(), nil
+ case reflect.Bool:
+ b := inValue.Bool()
+ if b {
+ return "true", nil
+ }
+ return "false", nil
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return fmt.Sprintf("%v", inValue.Int()), nil
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return fmt.Sprintf("%v", inValue.Uint()), nil
+ case reflect.Float32:
+ return strconv.FormatFloat(inValue.Float(), byte('f'), -1, 32), nil
+ case reflect.Float64:
+ return strconv.FormatFloat(inValue.Float(), byte('f'), -1, 64), nil
+ }
+ return "", fmt.Errorf("No known conversion from " + inValue.Type().String() + " to string")
+}
+
+func toBool(in interface{}) (bool, error) {
+ inValue := reflect.ValueOf(in)
+
+ switch inValue.Kind() {
+ case reflect.String:
+ s := inValue.String()
+ switch s {
+ case "yes":
+ return true, nil
+ case "no", "":
+ return false, nil
+ default:
+ return strconv.ParseBool(s)
+ }
+ case reflect.Bool:
+ return inValue.Bool(), nil
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ i := inValue.Int()
+ if i != 0 {
+ return true, nil
+ }
+ return false, nil
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ i := inValue.Uint()
+ if i != 0 {
+ return true, nil
+ }
+ return false, nil
+ case reflect.Float32, reflect.Float64:
+ f := inValue.Float()
+ if f != 0 {
+ return true, nil
+ }
+ return false, nil
+ }
+ return false, fmt.Errorf("No known conversion from " + inValue.Type().String() + " to bool")
+}
+
+func toInt(in interface{}) (int64, error) {
+ inValue := reflect.ValueOf(in)
+
+ switch inValue.Kind() {
+ case reflect.String:
+ s := strings.TrimSpace(inValue.String())
+ if s == "" {
+ return 0, nil
+ }
+ return strconv.ParseInt(s, 0, 64)
+ case reflect.Bool:
+ if inValue.Bool() {
+ return 1, nil
+ }
+ return 0, nil
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return inValue.Int(), nil
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return int64(inValue.Uint()), nil
+ case reflect.Float32, reflect.Float64:
+ return int64(inValue.Float()), nil
+ }
+ return 0, fmt.Errorf("No known conversion from " + inValue.Type().String() + " to int")
+}
+
+func toUint(in interface{}) (uint64, error) {
+ inValue := reflect.ValueOf(in)
+
+ switch inValue.Kind() {
+ case reflect.String:
+ s := strings.TrimSpace(inValue.String())
+ if s == "" {
+ return 0, nil
+ }
+
+ // support the float input
+ if strings.Contains(s, ".") {
+ f, err := strconv.ParseFloat(s, 64)
+ if err != nil {
+ return 0, err
+ }
+ return uint64(f), nil
+ }
+ return strconv.ParseUint(s, 0, 64)
+ case reflect.Bool:
+ if inValue.Bool() {
+ return 1, nil
+ }
+ return 0, nil
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return uint64(inValue.Int()), nil
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return inValue.Uint(), nil
+ case reflect.Float32, reflect.Float64:
+ return uint64(inValue.Float()), nil
+ }
+ return 0, fmt.Errorf("No known conversion from " + inValue.Type().String() + " to uint")
+}
+
+func toFloat(in interface{}) (float64, error) {
+ inValue := reflect.ValueOf(in)
+
+ switch inValue.Kind() {
+ case reflect.String:
+ s := strings.TrimSpace(inValue.String())
+ if s == "" {
+ return 0, nil
+ }
+ return strconv.ParseFloat(s, 64)
+ case reflect.Bool:
+ if inValue.Bool() {
+ return 1, nil
+ }
+ return 0, nil
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return float64(inValue.Int()), nil
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return float64(inValue.Uint()), nil
+ case reflect.Float32, reflect.Float64:
+ return inValue.Float(), nil
+ }
+ return 0, fmt.Errorf("No known conversion from " + inValue.Type().String() + " to float")
+}
+
+func setField(field reflect.Value, value string) error {
+ if field.Kind() == reflect.Ptr {
+ if field.IsNil() {
+ field.Set(reflect.New(field.Type().Elem()))
+ }
+ field = field.Elem()
+ }
+
+ switch field.Interface().(type) {
+ case string:
+ s, err := toString(value)
+ if err != nil {
+ return err
+ }
+ field.SetString(s)
+ case bool:
+ b, err := toBool(value)
+ if err != nil {
+ return err
+ }
+ field.SetBool(b)
+ case int, int8, int16, int32, int64:
+ i, err := toInt(value)
+ if err != nil {
+ return err
+ }
+ field.SetInt(i)
+ case uint, uint8, uint16, uint32, uint64:
+ ui, err := toUint(value)
+ if err != nil {
+ return err
+ }
+ field.SetUint(ui)
+ case float32, float64:
+ f, err := toFloat(value)
+ if err != nil {
+ return err
+ }
+ field.SetFloat(f)
+ default:
+ // Not a native type, check for unmarshal method
+ if err := unmarshall(field, value); err != nil {
+ if _, ok := err.(NoUnmarshalFuncError); !ok {
+ return err
+ }
+ // Could not unmarshal, check for kind, e.g. renamed type from basic type
+ switch field.Kind() {
+ case reflect.String:
+ s, err := toString(value)
+ if err != nil {
+ return err
+ }
+ field.SetString(s)
+ case reflect.Bool:
+ b, err := toBool(value)
+ if err != nil {
+ return err
+ }
+ field.SetBool(b)
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ i, err := toInt(value)
+ if err != nil {
+ return err
+ }
+ field.SetInt(i)
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ ui, err := toUint(value)
+ if err != nil {
+ return err
+ }
+ field.SetUint(ui)
+ case reflect.Float32, reflect.Float64:
+ f, err := toFloat(value)
+ if err != nil {
+ return err
+ }
+ field.SetFloat(f)
+ default:
+ return err
+ }
+ } else {
+ return nil
+ }
+ }
+ return nil
+}
+
+func getFieldAsString(field reflect.Value) (str string, err error) {
+ switch field.Kind() {
+ case reflect.Interface:
+ case reflect.Ptr:
+ if field.IsNil() {
+ return "", nil
+ }
+ return getFieldAsString(field.Elem())
+ default:
+ // Check if field is go native type
+ switch field.Interface().(type) {
+ case string:
+ return field.String(), nil
+ case bool:
+ str, err = toString(field.Bool())
+ if err != nil {
+ return str, err
+ }
+ case int, int8, int16, int32, int64:
+ str, err = toString(field.Int())
+ if err != nil {
+ return str, err
+ }
+ case uint, uint8, uint16, uint32, uint64:
+ str, err = toString(field.Uint())
+ if err != nil {
+ return str, err
+ }
+ case float32:
+ str, err = toString(float32(field.Float()))
+ if err != nil {
+ return str, err
+ }
+ case float64:
+ str, err = toString(field.Float())
+ if err != nil {
+ return str, err
+ }
+ default:
+ // Not a native type, check for marshal method
+ str, err = marshall(field)
+ if err != nil {
+ if _, ok := err.(NoMarshalFuncError); !ok {
+ return str, err
+ }
+ // If not marshal method, is field compatible with/renamed from native type
+ switch field.Kind() {
+ case reflect.String:
+ return field.String(), nil
+ case reflect.Bool:
+ str, err = toString(field.Bool())
+ if err != nil {
+ return str, err
+ }
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ str, err = toString(field.Int())
+ if err != nil {
+ return str, err
+ }
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ str, err = toString(field.Uint())
+ if err != nil {
+ return str, err
+ }
+ case reflect.Float32:
+ str, err = toString(float32(field.Float()))
+ if err != nil {
+ return str, err
+ }
+ case reflect.Float64:
+ str, err = toString(field.Float())
+ if err != nil {
+ return str, err
+ }
+ }
+ } else {
+ return str, nil
+ }
+ }
+ }
+ return str, nil
+}
+
+// --------------------------------------------------------------------------
+// Un/serializations helpers
+
+func unmarshall(field reflect.Value, value string) error {
+ dupField := field
+ unMarshallIt := func(finalField reflect.Value) error {
+ if finalField.CanInterface() && finalField.Type().Implements(unMarshallerType) {
+ if err := finalField.Interface().(TypeUnmarshaller).UnmarshalCSV(value); err != nil {
+ return err
+ }
+ return nil
+ } else if finalField.CanInterface() && finalField.Type().Implements(textUnMarshalerType) { // Otherwise try to use TextMarshaller
+ if err := finalField.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(value)); err != nil {
+ return err
+ }
+ return nil
+ }
+
+ return NoUnmarshalFuncError{"No known conversion from string to " + field.Type().String() + ", " + field.Type().String() + " does not implement TypeUnmarshaller"}
+ }
+ for dupField.Kind() == reflect.Interface || dupField.Kind() == reflect.Ptr {
+ if dupField.IsNil() {
+ dupField = reflect.New(field.Type().Elem())
+ field.Set(dupField)
+ return unMarshallIt(dupField)
+ break
+ }
+ dupField = dupField.Elem()
+ }
+ if dupField.CanAddr() {
+ return unMarshallIt(dupField.Addr())
+ }
+ return NoUnmarshalFuncError{"No known conversion from string to " + field.Type().String() + ", " + field.Type().String() + " does not implement TypeUnmarshaller"}
+}
+
+func marshall(field reflect.Value) (value string, err error) {
+ dupField := field
+ marshallIt := func(finalField reflect.Value) (string, error) {
+ if finalField.CanInterface() && finalField.Type().Implements(marshallerType) { // Use TypeMarshaller when possible
+ return finalField.Interface().(TypeMarshaller).MarshalCSV()
+ } else if finalField.CanInterface() && finalField.Type().Implements(stringerType) { // Otherwise try to use Stringer
+ return finalField.Interface().(Stringer).String(), nil
+ } else if finalField.CanInterface() && finalField.Type().Implements(textMarshalerType) { // Otherwise try to use TextMarshaller
+ text, err := finalField.Interface().(encoding.TextMarshaler).MarshalText()
+ return string(text), err
+ }
+
+ return value, NoMarshalFuncError{"No known conversion from " + field.Type().String() + " to string, " + field.Type().String() + " does not implement TypeMarshaller nor Stringer"}
+ }
+ for dupField.Kind() == reflect.Interface || dupField.Kind() == reflect.Ptr {
+ if dupField.IsNil() {
+ return value, nil
+ }
+ dupField = dupField.Elem()
+ }
+ if dupField.CanAddr() {
+ return marshallIt(dupField.Addr())
+ }
+ return value, NoMarshalFuncError{"No known conversion from " + field.Type().String() + " to string, " + field.Type().String() + " does not implement TypeMarshaller nor Stringer"}
+}
diff --git a/vendor/github.com/golang/freetype/raster/LICENSE b/vendor/github.com/golang/freetype/raster/LICENSE
new file mode 100644
index 0000000..e854ba5
--- /dev/null
+++ b/vendor/github.com/golang/freetype/raster/LICENSE
@@ -0,0 +1,12 @@
+Use of the Freetype-Go software is subject to your choice of exactly one of
+the following two licenses:
+ * The FreeType License, which is similar to the original BSD license with
+ an advertising clause, or
+ * The GNU General Public License (GPL), version 2 or later.
+
+The text of these licenses are available in the licenses/ftl.txt and the
+licenses/gpl.txt files respectively. They are also available at
+http://freetype.sourceforge.net/license.html
+
+The Luxi fonts in the testdata directory are licensed separately. See the
+testdata/COPYING file for details.
diff --git a/vendor/github.com/golang/freetype/raster/geom.go b/vendor/github.com/golang/freetype/raster/geom.go
new file mode 100644
index 0000000..f3696ea
--- /dev/null
+++ b/vendor/github.com/golang/freetype/raster/geom.go
@@ -0,0 +1,245 @@
+// Copyright 2010 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+package raster
+
+import (
+ "fmt"
+ "math"
+
+ "golang.org/x/image/math/fixed"
+)
+
+// maxAbs returns the maximum of abs(a) and abs(b).
+func maxAbs(a, b fixed.Int26_6) fixed.Int26_6 {
+ if a < 0 {
+ a = -a
+ }
+ if b < 0 {
+ b = -b
+ }
+ if a < b {
+ return b
+ }
+ return a
+}
+
+// pNeg returns the vector -p, or equivalently p rotated by 180 degrees.
+func pNeg(p fixed.Point26_6) fixed.Point26_6 {
+ return fixed.Point26_6{-p.X, -p.Y}
+}
+
+// pDot returns the dot product p·q.
+func pDot(p fixed.Point26_6, q fixed.Point26_6) fixed.Int52_12 {
+ px, py := int64(p.X), int64(p.Y)
+ qx, qy := int64(q.X), int64(q.Y)
+ return fixed.Int52_12(px*qx + py*qy)
+}
+
+// pLen returns the length of the vector p.
+func pLen(p fixed.Point26_6) fixed.Int26_6 {
+ // TODO(nigeltao): use fixed point math.
+ x := float64(p.X)
+ y := float64(p.Y)
+ return fixed.Int26_6(math.Sqrt(x*x + y*y))
+}
+
+// pNorm returns the vector p normalized to the given length, or zero if p is
+// degenerate.
+func pNorm(p fixed.Point26_6, length fixed.Int26_6) fixed.Point26_6 {
+ d := pLen(p)
+ if d == 0 {
+ return fixed.Point26_6{}
+ }
+ s, t := int64(length), int64(d)
+ x := int64(p.X) * s / t
+ y := int64(p.Y) * s / t
+ return fixed.Point26_6{fixed.Int26_6(x), fixed.Int26_6(y)}
+}
+
+// pRot45CW returns the vector p rotated clockwise by 45 degrees.
+//
+// Note that the Y-axis grows downwards, so {1, 0}.Rot45CW is {1/√2, 1/√2}.
+func pRot45CW(p fixed.Point26_6) fixed.Point26_6 {
+ // 181/256 is approximately 1/√2, or sin(π/4).
+ px, py := int64(p.X), int64(p.Y)
+ qx := (+px - py) * 181 / 256
+ qy := (+px + py) * 181 / 256
+ return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
+}
+
+// pRot90CW returns the vector p rotated clockwise by 90 degrees.
+//
+// Note that the Y-axis grows downwards, so {1, 0}.Rot90CW is {0, 1}.
+func pRot90CW(p fixed.Point26_6) fixed.Point26_6 {
+ return fixed.Point26_6{-p.Y, p.X}
+}
+
+// pRot135CW returns the vector p rotated clockwise by 135 degrees.
+//
+// Note that the Y-axis grows downwards, so {1, 0}.Rot135CW is {-1/√2, 1/√2}.
+func pRot135CW(p fixed.Point26_6) fixed.Point26_6 {
+ // 181/256 is approximately 1/√2, or sin(π/4).
+ px, py := int64(p.X), int64(p.Y)
+ qx := (-px - py) * 181 / 256
+ qy := (+px - py) * 181 / 256
+ return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
+}
+
+// pRot45CCW returns the vector p rotated counter-clockwise by 45 degrees.
+//
+// Note that the Y-axis grows downwards, so {1, 0}.Rot45CCW is {1/√2, -1/√2}.
+func pRot45CCW(p fixed.Point26_6) fixed.Point26_6 {
+ // 181/256 is approximately 1/√2, or sin(π/4).
+ px, py := int64(p.X), int64(p.Y)
+ qx := (+px + py) * 181 / 256
+ qy := (-px + py) * 181 / 256
+ return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
+}
+
+// pRot90CCW returns the vector p rotated counter-clockwise by 90 degrees.
+//
+// Note that the Y-axis grows downwards, so {1, 0}.Rot90CCW is {0, -1}.
+func pRot90CCW(p fixed.Point26_6) fixed.Point26_6 {
+ return fixed.Point26_6{p.Y, -p.X}
+}
+
+// pRot135CCW returns the vector p rotated counter-clockwise by 135 degrees.
+//
+// Note that the Y-axis grows downwards, so {1, 0}.Rot135CCW is {-1/√2, -1/√2}.
+func pRot135CCW(p fixed.Point26_6) fixed.Point26_6 {
+ // 181/256 is approximately 1/√2, or sin(π/4).
+ px, py := int64(p.X), int64(p.Y)
+ qx := (-px + py) * 181 / 256
+ qy := (-px - py) * 181 / 256
+ return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
+}
+
+// An Adder accumulates points on a curve.
+type Adder interface {
+ // Start starts a new curve at the given point.
+ Start(a fixed.Point26_6)
+ // Add1 adds a linear segment to the current curve.
+ Add1(b fixed.Point26_6)
+ // Add2 adds a quadratic segment to the current curve.
+ Add2(b, c fixed.Point26_6)
+ // Add3 adds a cubic segment to the current curve.
+ Add3(b, c, d fixed.Point26_6)
+}
+
+// A Path is a sequence of curves, and a curve is a start point followed by a
+// sequence of linear, quadratic or cubic segments.
+type Path []fixed.Int26_6
+
+// String returns a human-readable representation of a Path.
+func (p Path) String() string {
+ s := ""
+ for i := 0; i < len(p); {
+ if i != 0 {
+ s += " "
+ }
+ switch p[i] {
+ case 0:
+ s += "S0" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+3]))
+ i += 4
+ case 1:
+ s += "A1" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+3]))
+ i += 4
+ case 2:
+ s += "A2" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+5]))
+ i += 6
+ case 3:
+ s += "A3" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+7]))
+ i += 8
+ default:
+ panic("freetype/raster: bad path")
+ }
+ }
+ return s
+}
+
+// Clear cancels any previous calls to p.Start or p.AddXxx.
+func (p *Path) Clear() {
+ *p = (*p)[:0]
+}
+
+// Start starts a new curve at the given point.
+func (p *Path) Start(a fixed.Point26_6) {
+ *p = append(*p, 0, a.X, a.Y, 0)
+}
+
+// Add1 adds a linear segment to the current curve.
+func (p *Path) Add1(b fixed.Point26_6) {
+ *p = append(*p, 1, b.X, b.Y, 1)
+}
+
+// Add2 adds a quadratic segment to the current curve.
+func (p *Path) Add2(b, c fixed.Point26_6) {
+ *p = append(*p, 2, b.X, b.Y, c.X, c.Y, 2)
+}
+
+// Add3 adds a cubic segment to the current curve.
+func (p *Path) Add3(b, c, d fixed.Point26_6) {
+ *p = append(*p, 3, b.X, b.Y, c.X, c.Y, d.X, d.Y, 3)
+}
+
+// AddPath adds the Path q to p.
+func (p *Path) AddPath(q Path) {
+ *p = append(*p, q...)
+}
+
+// AddStroke adds a stroked Path.
+func (p *Path) AddStroke(q Path, width fixed.Int26_6, cr Capper, jr Joiner) {
+ Stroke(p, q, width, cr, jr)
+}
+
+// firstPoint returns the first point in a non-empty Path.
+func (p Path) firstPoint() fixed.Point26_6 {
+ return fixed.Point26_6{p[1], p[2]}
+}
+
+// lastPoint returns the last point in a non-empty Path.
+func (p Path) lastPoint() fixed.Point26_6 {
+ return fixed.Point26_6{p[len(p)-3], p[len(p)-2]}
+}
+
+// addPathReversed adds q reversed to p.
+// For example, if q consists of a linear segment from A to B followed by a
+// quadratic segment from B to C to D, then the values of q looks like:
+// index: 01234567890123
+// value: 0AA01BB12CCDD2
+// So, when adding q backwards to p, we want to Add2(C, B) followed by Add1(A).
+func addPathReversed(p Adder, q Path) {
+ if len(q) == 0 {
+ return
+ }
+ i := len(q) - 1
+ for {
+ switch q[i] {
+ case 0:
+ return
+ case 1:
+ i -= 4
+ p.Add1(
+ fixed.Point26_6{q[i-2], q[i-1]},
+ )
+ case 2:
+ i -= 6
+ p.Add2(
+ fixed.Point26_6{q[i+2], q[i+3]},
+ fixed.Point26_6{q[i-2], q[i-1]},
+ )
+ case 3:
+ i -= 8
+ p.Add3(
+ fixed.Point26_6{q[i+4], q[i+5]},
+ fixed.Point26_6{q[i+2], q[i+3]},
+ fixed.Point26_6{q[i-2], q[i-1]},
+ )
+ default:
+ panic("freetype/raster: bad path")
+ }
+ }
+}
diff --git a/vendor/github.com/golang/freetype/raster/paint.go b/vendor/github.com/golang/freetype/raster/paint.go
new file mode 100644
index 0000000..652256c
--- /dev/null
+++ b/vendor/github.com/golang/freetype/raster/paint.go
@@ -0,0 +1,287 @@
+// Copyright 2010 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+package raster
+
+import (
+ "image"
+ "image/color"
+ "image/draw"
+ "math"
+)
+
+// A Span is a horizontal segment of pixels with constant alpha. X0 is an
+// inclusive bound and X1 is exclusive, the same as for slices. A fully opaque
+// Span has Alpha == 0xffff.
+type Span struct {
+ Y, X0, X1 int
+ Alpha uint32
+}
+
+// A Painter knows how to paint a batch of Spans. Rasterization may involve
+// Painting multiple batches, and done will be true for the final batch. The
+// Spans' Y values are monotonically increasing during a rasterization. Paint
+// may use all of ss as scratch space during the call.
+type Painter interface {
+ Paint(ss []Span, done bool)
+}
+
+// The PainterFunc type adapts an ordinary function to the Painter interface.
+type PainterFunc func(ss []Span, done bool)
+
+// Paint just delegates the call to f.
+func (f PainterFunc) Paint(ss []Span, done bool) { f(ss, done) }
+
+// An AlphaOverPainter is a Painter that paints Spans onto a *image.Alpha using
+// the Over Porter-Duff composition operator.
+type AlphaOverPainter struct {
+ Image *image.Alpha
+}
+
+// Paint satisfies the Painter interface.
+func (r AlphaOverPainter) Paint(ss []Span, done bool) {
+ b := r.Image.Bounds()
+ for _, s := range ss {
+ if s.Y < b.Min.Y {
+ continue
+ }
+ if s.Y >= b.Max.Y {
+ return
+ }
+ if s.X0 < b.Min.X {
+ s.X0 = b.Min.X
+ }
+ if s.X1 > b.Max.X {
+ s.X1 = b.Max.X
+ }
+ if s.X0 >= s.X1 {
+ continue
+ }
+ base := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride - r.Image.Rect.Min.X
+ p := r.Image.Pix[base+s.X0 : base+s.X1]
+ a := int(s.Alpha >> 8)
+ for i, c := range p {
+ v := int(c)
+ p[i] = uint8((v*255 + (255-v)*a) / 255)
+ }
+ }
+}
+
+// NewAlphaOverPainter creates a new AlphaOverPainter for the given image.
+func NewAlphaOverPainter(m *image.Alpha) AlphaOverPainter {
+ return AlphaOverPainter{m}
+}
+
+// An AlphaSrcPainter is a Painter that paints Spans onto a *image.Alpha using
+// the Src Porter-Duff composition operator.
+type AlphaSrcPainter struct {
+ Image *image.Alpha
+}
+
+// Paint satisfies the Painter interface.
+func (r AlphaSrcPainter) Paint(ss []Span, done bool) {
+ b := r.Image.Bounds()
+ for _, s := range ss {
+ if s.Y < b.Min.Y {
+ continue
+ }
+ if s.Y >= b.Max.Y {
+ return
+ }
+ if s.X0 < b.Min.X {
+ s.X0 = b.Min.X
+ }
+ if s.X1 > b.Max.X {
+ s.X1 = b.Max.X
+ }
+ if s.X0 >= s.X1 {
+ continue
+ }
+ base := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride - r.Image.Rect.Min.X
+ p := r.Image.Pix[base+s.X0 : base+s.X1]
+ color := uint8(s.Alpha >> 8)
+ for i := range p {
+ p[i] = color
+ }
+ }
+}
+
+// NewAlphaSrcPainter creates a new AlphaSrcPainter for the given image.
+func NewAlphaSrcPainter(m *image.Alpha) AlphaSrcPainter {
+ return AlphaSrcPainter{m}
+}
+
+// An RGBAPainter is a Painter that paints Spans onto a *image.RGBA.
+type RGBAPainter struct {
+ // Image is the image to compose onto.
+ Image *image.RGBA
+ // Op is the Porter-Duff composition operator.
+ Op draw.Op
+ // cr, cg, cb and ca are the 16-bit color to paint the spans.
+ cr, cg, cb, ca uint32
+}
+
+// Paint satisfies the Painter interface.
+func (r *RGBAPainter) Paint(ss []Span, done bool) {
+ b := r.Image.Bounds()
+ for _, s := range ss {
+ if s.Y < b.Min.Y {
+ continue
+ }
+ if s.Y >= b.Max.Y {
+ return
+ }
+ if s.X0 < b.Min.X {
+ s.X0 = b.Min.X
+ }
+ if s.X1 > b.Max.X {
+ s.X1 = b.Max.X
+ }
+ if s.X0 >= s.X1 {
+ continue
+ }
+ // This code mimics drawGlyphOver in $GOROOT/src/image/draw/draw.go.
+ ma := s.Alpha
+ const m = 1<<16 - 1
+ i0 := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride + (s.X0-r.Image.Rect.Min.X)*4
+ i1 := i0 + (s.X1-s.X0)*4
+ if r.Op == draw.Over {
+ for i := i0; i < i1; i += 4 {
+ dr := uint32(r.Image.Pix[i+0])
+ dg := uint32(r.Image.Pix[i+1])
+ db := uint32(r.Image.Pix[i+2])
+ da := uint32(r.Image.Pix[i+3])
+ a := (m - (r.ca * ma / m)) * 0x101
+ r.Image.Pix[i+0] = uint8((dr*a + r.cr*ma) / m >> 8)
+ r.Image.Pix[i+1] = uint8((dg*a + r.cg*ma) / m >> 8)
+ r.Image.Pix[i+2] = uint8((db*a + r.cb*ma) / m >> 8)
+ r.Image.Pix[i+3] = uint8((da*a + r.ca*ma) / m >> 8)
+ }
+ } else {
+ for i := i0; i < i1; i += 4 {
+ r.Image.Pix[i+0] = uint8(r.cr * ma / m >> 8)
+ r.Image.Pix[i+1] = uint8(r.cg * ma / m >> 8)
+ r.Image.Pix[i+2] = uint8(r.cb * ma / m >> 8)
+ r.Image.Pix[i+3] = uint8(r.ca * ma / m >> 8)
+ }
+ }
+ }
+}
+
+// SetColor sets the color to paint the spans.
+func (r *RGBAPainter) SetColor(c color.Color) {
+ r.cr, r.cg, r.cb, r.ca = c.RGBA()
+}
+
+// NewRGBAPainter creates a new RGBAPainter for the given image.
+func NewRGBAPainter(m *image.RGBA) *RGBAPainter {
+ return &RGBAPainter{Image: m}
+}
+
+// A MonochromePainter wraps another Painter, quantizing each Span's alpha to
+// be either fully opaque or fully transparent.
+type MonochromePainter struct {
+ Painter Painter
+ y, x0, x1 int
+}
+
+// Paint delegates to the wrapped Painter after quantizing each Span's alpha
+// value and merging adjacent fully opaque Spans.
+func (m *MonochromePainter) Paint(ss []Span, done bool) {
+ // We compact the ss slice, discarding any Spans whose alpha quantizes to zero.
+ j := 0
+ for _, s := range ss {
+ if s.Alpha >= 0x8000 {
+ if m.y == s.Y && m.x1 == s.X0 {
+ m.x1 = s.X1
+ } else {
+ ss[j] = Span{m.y, m.x0, m.x1, 1<<16 - 1}
+ j++
+ m.y, m.x0, m.x1 = s.Y, s.X0, s.X1
+ }
+ }
+ }
+ if done {
+ // Flush the accumulated Span.
+ finalSpan := Span{m.y, m.x0, m.x1, 1<<16 - 1}
+ if j < len(ss) {
+ ss[j] = finalSpan
+ j++
+ m.Painter.Paint(ss[:j], true)
+ } else if j == len(ss) {
+ m.Painter.Paint(ss, false)
+ if cap(ss) > 0 {
+ ss = ss[:1]
+ } else {
+ ss = make([]Span, 1)
+ }
+ ss[0] = finalSpan
+ m.Painter.Paint(ss, true)
+ } else {
+ panic("unreachable")
+ }
+ // Reset the accumulator, so that this Painter can be re-used.
+ m.y, m.x0, m.x1 = 0, 0, 0
+ } else {
+ m.Painter.Paint(ss[:j], false)
+ }
+}
+
+// NewMonochromePainter creates a new MonochromePainter that wraps the given
+// Painter.
+func NewMonochromePainter(p Painter) *MonochromePainter {
+ return &MonochromePainter{Painter: p}
+}
+
+// A GammaCorrectionPainter wraps another Painter, performing gamma-correction
+// on each Span's alpha value.
+type GammaCorrectionPainter struct {
+ // Painter is the wrapped Painter.
+ Painter Painter
+ // a is the precomputed alpha values for linear interpolation, with fully
+ // opaque == 0xffff.
+ a [256]uint16
+ // gammaIsOne is whether gamma correction is a no-op.
+ gammaIsOne bool
+}
+
+// Paint delegates to the wrapped Painter after performing gamma-correction on
+// each Span.
+func (g *GammaCorrectionPainter) Paint(ss []Span, done bool) {
+ if !g.gammaIsOne {
+ const n = 0x101
+ for i, s := range ss {
+ if s.Alpha == 0 || s.Alpha == 0xffff {
+ continue
+ }
+ p, q := s.Alpha/n, s.Alpha%n
+ // The resultant alpha is a linear interpolation of g.a[p] and g.a[p+1].
+ a := uint32(g.a[p])*(n-q) + uint32(g.a[p+1])*q
+ ss[i].Alpha = (a + n/2) / n
+ }
+ }
+ g.Painter.Paint(ss, done)
+}
+
+// SetGamma sets the gamma value.
+func (g *GammaCorrectionPainter) SetGamma(gamma float64) {
+ g.gammaIsOne = gamma == 1
+ if g.gammaIsOne {
+ return
+ }
+ for i := 0; i < 256; i++ {
+ a := float64(i) / 0xff
+ a = math.Pow(a, gamma)
+ g.a[i] = uint16(0xffff * a)
+ }
+}
+
+// NewGammaCorrectionPainter creates a new GammaCorrectionPainter that wraps
+// the given Painter.
+func NewGammaCorrectionPainter(p Painter, gamma float64) *GammaCorrectionPainter {
+ g := &GammaCorrectionPainter{Painter: p}
+ g.SetGamma(gamma)
+ return g
+}
diff --git a/vendor/github.com/golang/freetype/raster/raster.go b/vendor/github.com/golang/freetype/raster/raster.go
new file mode 100644
index 0000000..7e6cd4e
--- /dev/null
+++ b/vendor/github.com/golang/freetype/raster/raster.go
@@ -0,0 +1,601 @@
+// Copyright 2010 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+// Package raster provides an anti-aliasing 2-D rasterizer.
+//
+// It is part of the larger Freetype suite of font-related packages, but the
+// raster package is not specific to font rasterization, and can be used
+// standalone without any other Freetype package.
+//
+// Rasterization is done by the same area/coverage accumulation algorithm as
+// the Freetype "smooth" module, and the Anti-Grain Geometry library. A
+// description of the area/coverage algorithm is at
+// http://projects.tuxee.net/cl-vectors/section-the-cl-aa-algorithm
+package raster // import "github.com/golang/freetype/raster"
+
+import (
+ "strconv"
+
+ "golang.org/x/image/math/fixed"
+)
+
+// A cell is part of a linked list (for a given yi co-ordinate) of accumulated
+// area/coverage for the pixel at (xi, yi).
+type cell struct {
+ xi int
+ area, cover int
+ next int
+}
+
+type Rasterizer struct {
+ // If false, the default behavior is to use the even-odd winding fill
+ // rule during Rasterize.
+ UseNonZeroWinding bool
+ // An offset (in pixels) to the painted spans.
+ Dx, Dy int
+
+ // The width of the Rasterizer. The height is implicit in len(cellIndex).
+ width int
+ // splitScaleN is the scaling factor used to determine how many times
+ // to decompose a quadratic or cubic segment into a linear approximation.
+ splitScale2, splitScale3 int
+
+ // The current pen position.
+ a fixed.Point26_6
+ // The current cell and its area/coverage being accumulated.
+ xi, yi int
+ area, cover int
+
+ // Saved cells.
+ cell []cell
+ // Linked list of cells, one per row.
+ cellIndex []int
+ // Buffers.
+ cellBuf [256]cell
+ cellIndexBuf [64]int
+ spanBuf [64]Span
+}
+
+// findCell returns the index in r.cell for the cell corresponding to
+// (r.xi, r.yi). The cell is created if necessary.
+func (r *Rasterizer) findCell() int {
+ if r.yi < 0 || r.yi >= len(r.cellIndex) {
+ return -1
+ }
+ xi := r.xi
+ if xi < 0 {
+ xi = -1
+ } else if xi > r.width {
+ xi = r.width
+ }
+ i, prev := r.cellIndex[r.yi], -1
+ for i != -1 && r.cell[i].xi <= xi {
+ if r.cell[i].xi == xi {
+ return i
+ }
+ i, prev = r.cell[i].next, i
+ }
+ c := len(r.cell)
+ if c == cap(r.cell) {
+ buf := make([]cell, c, 4*c)
+ copy(buf, r.cell)
+ r.cell = buf[0 : c+1]
+ } else {
+ r.cell = r.cell[0 : c+1]
+ }
+ r.cell[c] = cell{xi, 0, 0, i}
+ if prev == -1 {
+ r.cellIndex[r.yi] = c
+ } else {
+ r.cell[prev].next = c
+ }
+ return c
+}
+
+// saveCell saves any accumulated r.area/r.cover for (r.xi, r.yi).
+func (r *Rasterizer) saveCell() {
+ if r.area != 0 || r.cover != 0 {
+ i := r.findCell()
+ if i != -1 {
+ r.cell[i].area += r.area
+ r.cell[i].cover += r.cover
+ }
+ r.area = 0
+ r.cover = 0
+ }
+}
+
+// setCell sets the (xi, yi) cell that r is accumulating area/coverage for.
+func (r *Rasterizer) setCell(xi, yi int) {
+ if r.xi != xi || r.yi != yi {
+ r.saveCell()
+ r.xi, r.yi = xi, yi
+ }
+}
+
+// scan accumulates area/coverage for the yi'th scanline, going from
+// x0 to x1 in the horizontal direction (in 26.6 fixed point co-ordinates)
+// and from y0f to y1f fractional vertical units within that scanline.
+func (r *Rasterizer) scan(yi int, x0, y0f, x1, y1f fixed.Int26_6) {
+ // Break the 26.6 fixed point X co-ordinates into integral and fractional parts.
+ x0i := int(x0) / 64
+ x0f := x0 - fixed.Int26_6(64*x0i)
+ x1i := int(x1) / 64
+ x1f := x1 - fixed.Int26_6(64*x1i)
+
+ // A perfectly horizontal scan.
+ if y0f == y1f {
+ r.setCell(x1i, yi)
+ return
+ }
+ dx, dy := x1-x0, y1f-y0f
+ // A single cell scan.
+ if x0i == x1i {
+ r.area += int((x0f + x1f) * dy)
+ r.cover += int(dy)
+ return
+ }
+ // There are at least two cells. Apart from the first and last cells,
+ // all intermediate cells go through the full width of the cell,
+ // or 64 units in 26.6 fixed point format.
+ var (
+ p, q, edge0, edge1 fixed.Int26_6
+ xiDelta int
+ )
+ if dx > 0 {
+ p, q = (64-x0f)*dy, dx
+ edge0, edge1, xiDelta = 0, 64, 1
+ } else {
+ p, q = x0f*dy, -dx
+ edge0, edge1, xiDelta = 64, 0, -1
+ }
+ yDelta, yRem := p/q, p%q
+ if yRem < 0 {
+ yDelta -= 1
+ yRem += q
+ }
+ // Do the first cell.
+ xi, y := x0i, y0f
+ r.area += int((x0f + edge1) * yDelta)
+ r.cover += int(yDelta)
+ xi, y = xi+xiDelta, y+yDelta
+ r.setCell(xi, yi)
+ if xi != x1i {
+ // Do all the intermediate cells.
+ p = 64 * (y1f - y + yDelta)
+ fullDelta, fullRem := p/q, p%q
+ if fullRem < 0 {
+ fullDelta -= 1
+ fullRem += q
+ }
+ yRem -= q
+ for xi != x1i {
+ yDelta = fullDelta
+ yRem += fullRem
+ if yRem >= 0 {
+ yDelta += 1
+ yRem -= q
+ }
+ r.area += int(64 * yDelta)
+ r.cover += int(yDelta)
+ xi, y = xi+xiDelta, y+yDelta
+ r.setCell(xi, yi)
+ }
+ }
+ // Do the last cell.
+ yDelta = y1f - y
+ r.area += int((edge0 + x1f) * yDelta)
+ r.cover += int(yDelta)
+}
+
+// Start starts a new curve at the given point.
+func (r *Rasterizer) Start(a fixed.Point26_6) {
+ r.setCell(int(a.X/64), int(a.Y/64))
+ r.a = a
+}
+
+// Add1 adds a linear segment to the current curve.
+func (r *Rasterizer) Add1(b fixed.Point26_6) {
+ x0, y0 := r.a.X, r.a.Y
+ x1, y1 := b.X, b.Y
+ dx, dy := x1-x0, y1-y0
+ // Break the 26.6 fixed point Y co-ordinates into integral and fractional
+ // parts.
+ y0i := int(y0) / 64
+ y0f := y0 - fixed.Int26_6(64*y0i)
+ y1i := int(y1) / 64
+ y1f := y1 - fixed.Int26_6(64*y1i)
+
+ if y0i == y1i {
+ // There is only one scanline.
+ r.scan(y0i, x0, y0f, x1, y1f)
+
+ } else if dx == 0 {
+ // This is a vertical line segment. We avoid calling r.scan and instead
+ // manipulate r.area and r.cover directly.
+ var (
+ edge0, edge1 fixed.Int26_6
+ yiDelta int
+ )
+ if dy > 0 {
+ edge0, edge1, yiDelta = 0, 64, 1
+ } else {
+ edge0, edge1, yiDelta = 64, 0, -1
+ }
+ x0i, yi := int(x0)/64, y0i
+ x0fTimes2 := (int(x0) - (64 * x0i)) * 2
+ // Do the first pixel.
+ dcover := int(edge1 - y0f)
+ darea := int(x0fTimes2 * dcover)
+ r.area += darea
+ r.cover += dcover
+ yi += yiDelta
+ r.setCell(x0i, yi)
+ // Do all the intermediate pixels.
+ dcover = int(edge1 - edge0)
+ darea = int(x0fTimes2 * dcover)
+ for yi != y1i {
+ r.area += darea
+ r.cover += dcover
+ yi += yiDelta
+ r.setCell(x0i, yi)
+ }
+ // Do the last pixel.
+ dcover = int(y1f - edge0)
+ darea = int(x0fTimes2 * dcover)
+ r.area += darea
+ r.cover += dcover
+
+ } else {
+ // There are at least two scanlines. Apart from the first and last
+ // scanlines, all intermediate scanlines go through the full height of
+ // the row, or 64 units in 26.6 fixed point format.
+ var (
+ p, q, edge0, edge1 fixed.Int26_6
+ yiDelta int
+ )
+ if dy > 0 {
+ p, q = (64-y0f)*dx, dy
+ edge0, edge1, yiDelta = 0, 64, 1
+ } else {
+ p, q = y0f*dx, -dy
+ edge0, edge1, yiDelta = 64, 0, -1
+ }
+ xDelta, xRem := p/q, p%q
+ if xRem < 0 {
+ xDelta -= 1
+ xRem += q
+ }
+ // Do the first scanline.
+ x, yi := x0, y0i
+ r.scan(yi, x, y0f, x+xDelta, edge1)
+ x, yi = x+xDelta, yi+yiDelta
+ r.setCell(int(x)/64, yi)
+ if yi != y1i {
+ // Do all the intermediate scanlines.
+ p = 64 * dx
+ fullDelta, fullRem := p/q, p%q
+ if fullRem < 0 {
+ fullDelta -= 1
+ fullRem += q
+ }
+ xRem -= q
+ for yi != y1i {
+ xDelta = fullDelta
+ xRem += fullRem
+ if xRem >= 0 {
+ xDelta += 1
+ xRem -= q
+ }
+ r.scan(yi, x, edge0, x+xDelta, edge1)
+ x, yi = x+xDelta, yi+yiDelta
+ r.setCell(int(x)/64, yi)
+ }
+ }
+ // Do the last scanline.
+ r.scan(yi, x, edge0, x1, y1f)
+ }
+ // The next lineTo starts from b.
+ r.a = b
+}
+
+// Add2 adds a quadratic segment to the current curve.
+func (r *Rasterizer) Add2(b, c fixed.Point26_6) {
+ // Calculate nSplit (the number of recursive decompositions) based on how
+ // 'curvy' it is. Specifically, how much the middle point b deviates from
+ // (a+c)/2.
+ dev := maxAbs(r.a.X-2*b.X+c.X, r.a.Y-2*b.Y+c.Y) / fixed.Int26_6(r.splitScale2)
+ nsplit := 0
+ for dev > 0 {
+ dev /= 4
+ nsplit++
+ }
+ // dev is 32-bit, and nsplit++ every time we shift off 2 bits, so maxNsplit
+ // is 16.
+ const maxNsplit = 16
+ if nsplit > maxNsplit {
+ panic("freetype/raster: Add2 nsplit too large: " + strconv.Itoa(nsplit))
+ }
+ // Recursively decompose the curve nSplit levels deep.
+ var (
+ pStack [2*maxNsplit + 3]fixed.Point26_6
+ sStack [maxNsplit + 1]int
+ i int
+ )
+ sStack[0] = nsplit
+ pStack[0] = c
+ pStack[1] = b
+ pStack[2] = r.a
+ for i >= 0 {
+ s := sStack[i]
+ p := pStack[2*i:]
+ if s > 0 {
+ // Split the quadratic curve p[:3] into an equivalent set of two
+ // shorter curves: p[:3] and p[2:5]. The new p[4] is the old p[2],
+ // and p[0] is unchanged.
+ mx := p[1].X
+ p[4].X = p[2].X
+ p[3].X = (p[4].X + mx) / 2
+ p[1].X = (p[0].X + mx) / 2
+ p[2].X = (p[1].X + p[3].X) / 2
+ my := p[1].Y
+ p[4].Y = p[2].Y
+ p[3].Y = (p[4].Y + my) / 2
+ p[1].Y = (p[0].Y + my) / 2
+ p[2].Y = (p[1].Y + p[3].Y) / 2
+ // The two shorter curves have one less split to do.
+ sStack[i] = s - 1
+ sStack[i+1] = s - 1
+ i++
+ } else {
+ // Replace the level-0 quadratic with a two-linear-piece
+ // approximation.
+ midx := (p[0].X + 2*p[1].X + p[2].X) / 4
+ midy := (p[0].Y + 2*p[1].Y + p[2].Y) / 4
+ r.Add1(fixed.Point26_6{midx, midy})
+ r.Add1(p[0])
+ i--
+ }
+ }
+}
+
+// Add3 adds a cubic segment to the current curve.
+func (r *Rasterizer) Add3(b, c, d fixed.Point26_6) {
+ // Calculate nSplit (the number of recursive decompositions) based on how
+ // 'curvy' it is.
+ dev2 := maxAbs(r.a.X-3*(b.X+c.X)+d.X, r.a.Y-3*(b.Y+c.Y)+d.Y) / fixed.Int26_6(r.splitScale2)
+ dev3 := maxAbs(r.a.X-2*b.X+d.X, r.a.Y-2*b.Y+d.Y) / fixed.Int26_6(r.splitScale3)
+ nsplit := 0
+ for dev2 > 0 || dev3 > 0 {
+ dev2 /= 8
+ dev3 /= 4
+ nsplit++
+ }
+ // devN is 32-bit, and nsplit++ every time we shift off 2 bits, so
+ // maxNsplit is 16.
+ const maxNsplit = 16
+ if nsplit > maxNsplit {
+ panic("freetype/raster: Add3 nsplit too large: " + strconv.Itoa(nsplit))
+ }
+ // Recursively decompose the curve nSplit levels deep.
+ var (
+ pStack [3*maxNsplit + 4]fixed.Point26_6
+ sStack [maxNsplit + 1]int
+ i int
+ )
+ sStack[0] = nsplit
+ pStack[0] = d
+ pStack[1] = c
+ pStack[2] = b
+ pStack[3] = r.a
+ for i >= 0 {
+ s := sStack[i]
+ p := pStack[3*i:]
+ if s > 0 {
+ // Split the cubic curve p[:4] into an equivalent set of two
+ // shorter curves: p[:4] and p[3:7]. The new p[6] is the old p[3],
+ // and p[0] is unchanged.
+ m01x := (p[0].X + p[1].X) / 2
+ m12x := (p[1].X + p[2].X) / 2
+ m23x := (p[2].X + p[3].X) / 2
+ p[6].X = p[3].X
+ p[5].X = m23x
+ p[1].X = m01x
+ p[2].X = (m01x + m12x) / 2
+ p[4].X = (m12x + m23x) / 2
+ p[3].X = (p[2].X + p[4].X) / 2
+ m01y := (p[0].Y + p[1].Y) / 2
+ m12y := (p[1].Y + p[2].Y) / 2
+ m23y := (p[2].Y + p[3].Y) / 2
+ p[6].Y = p[3].Y
+ p[5].Y = m23y
+ p[1].Y = m01y
+ p[2].Y = (m01y + m12y) / 2
+ p[4].Y = (m12y + m23y) / 2
+ p[3].Y = (p[2].Y + p[4].Y) / 2
+ // The two shorter curves have one less split to do.
+ sStack[i] = s - 1
+ sStack[i+1] = s - 1
+ i++
+ } else {
+ // Replace the level-0 cubic with a two-linear-piece approximation.
+ midx := (p[0].X + 3*(p[1].X+p[2].X) + p[3].X) / 8
+ midy := (p[0].Y + 3*(p[1].Y+p[2].Y) + p[3].Y) / 8
+ r.Add1(fixed.Point26_6{midx, midy})
+ r.Add1(p[0])
+ i--
+ }
+ }
+}
+
+// AddPath adds the given Path.
+func (r *Rasterizer) AddPath(p Path) {
+ for i := 0; i < len(p); {
+ switch p[i] {
+ case 0:
+ r.Start(
+ fixed.Point26_6{p[i+1], p[i+2]},
+ )
+ i += 4
+ case 1:
+ r.Add1(
+ fixed.Point26_6{p[i+1], p[i+2]},
+ )
+ i += 4
+ case 2:
+ r.Add2(
+ fixed.Point26_6{p[i+1], p[i+2]},
+ fixed.Point26_6{p[i+3], p[i+4]},
+ )
+ i += 6
+ case 3:
+ r.Add3(
+ fixed.Point26_6{p[i+1], p[i+2]},
+ fixed.Point26_6{p[i+3], p[i+4]},
+ fixed.Point26_6{p[i+5], p[i+6]},
+ )
+ i += 8
+ default:
+ panic("freetype/raster: bad path")
+ }
+ }
+}
+
+// AddStroke adds a stroked Path.
+func (r *Rasterizer) AddStroke(q Path, width fixed.Int26_6, cr Capper, jr Joiner) {
+ Stroke(r, q, width, cr, jr)
+}
+
+// areaToAlpha converts an area value to a uint32 alpha value. A completely
+// filled pixel corresponds to an area of 64*64*2, and an alpha of 0xffff. The
+// conversion of area values greater than this depends on the winding rule:
+// even-odd or non-zero.
+func (r *Rasterizer) areaToAlpha(area int) uint32 {
+ // The C Freetype implementation (version 2.3.12) does "alpha := area>>1"
+ // without the +1. Round-to-nearest gives a more symmetric result than
+ // round-down. The C implementation also returns 8-bit alpha, not 16-bit
+ // alpha.
+ a := (area + 1) >> 1
+ if a < 0 {
+ a = -a
+ }
+ alpha := uint32(a)
+ if r.UseNonZeroWinding {
+ if alpha > 0x0fff {
+ alpha = 0x0fff
+ }
+ } else {
+ alpha &= 0x1fff
+ if alpha > 0x1000 {
+ alpha = 0x2000 - alpha
+ } else if alpha == 0x1000 {
+ alpha = 0x0fff
+ }
+ }
+ // alpha is now in the range [0x0000, 0x0fff]. Convert that 12-bit alpha to
+ // 16-bit alpha.
+ return alpha<<4 | alpha>>8
+}
+
+// Rasterize converts r's accumulated curves into Spans for p. The Spans passed
+// to p are non-overlapping, and sorted by Y and then X. They all have non-zero
+// width (and 0 <= X0 < X1 <= r.width) and non-zero A, except for the final
+// Span, which has Y, X0, X1 and A all equal to zero.
+func (r *Rasterizer) Rasterize(p Painter) {
+ r.saveCell()
+ s := 0
+ for yi := 0; yi < len(r.cellIndex); yi++ {
+ xi, cover := 0, 0
+ for c := r.cellIndex[yi]; c != -1; c = r.cell[c].next {
+ if cover != 0 && r.cell[c].xi > xi {
+ alpha := r.areaToAlpha(cover * 64 * 2)
+ if alpha != 0 {
+ xi0, xi1 := xi, r.cell[c].xi
+ if xi0 < 0 {
+ xi0 = 0
+ }
+ if xi1 >= r.width {
+ xi1 = r.width
+ }
+ if xi0 < xi1 {
+ r.spanBuf[s] = Span{yi + r.Dy, xi0 + r.Dx, xi1 + r.Dx, alpha}
+ s++
+ }
+ }
+ }
+ cover += r.cell[c].cover
+ alpha := r.areaToAlpha(cover*64*2 - r.cell[c].area)
+ xi = r.cell[c].xi + 1
+ if alpha != 0 {
+ xi0, xi1 := r.cell[c].xi, xi
+ if xi0 < 0 {
+ xi0 = 0
+ }
+ if xi1 >= r.width {
+ xi1 = r.width
+ }
+ if xi0 < xi1 {
+ r.spanBuf[s] = Span{yi + r.Dy, xi0 + r.Dx, xi1 + r.Dx, alpha}
+ s++
+ }
+ }
+ if s > len(r.spanBuf)-2 {
+ p.Paint(r.spanBuf[:s], false)
+ s = 0
+ }
+ }
+ }
+ p.Paint(r.spanBuf[:s], true)
+}
+
+// Clear cancels any previous calls to r.Start or r.AddXxx.
+func (r *Rasterizer) Clear() {
+ r.a = fixed.Point26_6{}
+ r.xi = 0
+ r.yi = 0
+ r.area = 0
+ r.cover = 0
+ r.cell = r.cell[:0]
+ for i := 0; i < len(r.cellIndex); i++ {
+ r.cellIndex[i] = -1
+ }
+}
+
+// SetBounds sets the maximum width and height of the rasterized image and
+// calls Clear. The width and height are in pixels, not fixed.Int26_6 units.
+func (r *Rasterizer) SetBounds(width, height int) {
+ if width < 0 {
+ width = 0
+ }
+ if height < 0 {
+ height = 0
+ }
+ // Use the same ssN heuristic as the C Freetype (version 2.4.0)
+ // implementation.
+ ss2, ss3 := 32, 16
+ if width > 24 || height > 24 {
+ ss2, ss3 = 2*ss2, 2*ss3
+ if width > 120 || height > 120 {
+ ss2, ss3 = 2*ss2, 2*ss3
+ }
+ }
+ r.width = width
+ r.splitScale2 = ss2
+ r.splitScale3 = ss3
+ r.cell = r.cellBuf[:0]
+ if height > len(r.cellIndexBuf) {
+ r.cellIndex = make([]int, height)
+ } else {
+ r.cellIndex = r.cellIndexBuf[:height]
+ }
+ r.Clear()
+}
+
+// NewRasterizer creates a new Rasterizer with the given bounds.
+func NewRasterizer(width, height int) *Rasterizer {
+ r := new(Rasterizer)
+ r.SetBounds(width, height)
+ return r
+}
diff --git a/vendor/github.com/golang/freetype/raster/stroke.go b/vendor/github.com/golang/freetype/raster/stroke.go
new file mode 100644
index 0000000..bcc66b2
--- /dev/null
+++ b/vendor/github.com/golang/freetype/raster/stroke.go
@@ -0,0 +1,483 @@
+// Copyright 2010 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+package raster
+
+import (
+ "golang.org/x/image/math/fixed"
+)
+
+// Two points are considered practically equal if the square of the distance
+// between them is less than one quarter (i.e. 1024 / 4096).
+const epsilon = fixed.Int52_12(1024)
+
+// A Capper signifies how to begin or end a stroked path.
+type Capper interface {
+ // Cap adds a cap to p given a pivot point and the normal vector of a
+ // terminal segment. The normal's length is half of the stroke width.
+ Cap(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6)
+}
+
+// The CapperFunc type adapts an ordinary function to be a Capper.
+type CapperFunc func(Adder, fixed.Int26_6, fixed.Point26_6, fixed.Point26_6)
+
+func (f CapperFunc) Cap(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6) {
+ f(p, halfWidth, pivot, n1)
+}
+
+// A Joiner signifies how to join interior nodes of a stroked path.
+type Joiner interface {
+ // Join adds a join to the two sides of a stroked path given a pivot
+ // point and the normal vectors of the trailing and leading segments.
+ // Both normals have length equal to half of the stroke width.
+ Join(lhs, rhs Adder, halfWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6)
+}
+
+// The JoinerFunc type adapts an ordinary function to be a Joiner.
+type JoinerFunc func(lhs, rhs Adder, halfWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6)
+
+func (f JoinerFunc) Join(lhs, rhs Adder, halfWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6) {
+ f(lhs, rhs, halfWidth, pivot, n0, n1)
+}
+
+// RoundCapper adds round caps to a stroked path.
+var RoundCapper Capper = CapperFunc(roundCapper)
+
+func roundCapper(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6) {
+ // The cubic Bézier approximation to a circle involves the magic number
+ // (√2 - 1) * 4/3, which is approximately 35/64.
+ const k = 35
+ e := pRot90CCW(n1)
+ side := pivot.Add(e)
+ start, end := pivot.Sub(n1), pivot.Add(n1)
+ d, e := n1.Mul(k), e.Mul(k)
+ p.Add3(start.Add(e), side.Sub(d), side)
+ p.Add3(side.Add(d), end.Add(e), end)
+}
+
+// ButtCapper adds butt caps to a stroked path.
+var ButtCapper Capper = CapperFunc(buttCapper)
+
+func buttCapper(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6) {
+ p.Add1(pivot.Add(n1))
+}
+
+// SquareCapper adds square caps to a stroked path.
+var SquareCapper Capper = CapperFunc(squareCapper)
+
+func squareCapper(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6) {
+ e := pRot90CCW(n1)
+ side := pivot.Add(e)
+ p.Add1(side.Sub(n1))
+ p.Add1(side.Add(n1))
+ p.Add1(pivot.Add(n1))
+}
+
+// RoundJoiner adds round joins to a stroked path.
+var RoundJoiner Joiner = JoinerFunc(roundJoiner)
+
+func roundJoiner(lhs, rhs Adder, haflWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6) {
+ dot := pDot(pRot90CW(n0), n1)
+ if dot >= 0 {
+ addArc(lhs, pivot, n0, n1)
+ rhs.Add1(pivot.Sub(n1))
+ } else {
+ lhs.Add1(pivot.Add(n1))
+ addArc(rhs, pivot, pNeg(n0), pNeg(n1))
+ }
+}
+
+// BevelJoiner adds bevel joins to a stroked path.
+var BevelJoiner Joiner = JoinerFunc(bevelJoiner)
+
+func bevelJoiner(lhs, rhs Adder, haflWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6) {
+ lhs.Add1(pivot.Add(n1))
+ rhs.Add1(pivot.Sub(n1))
+}
+
+// addArc adds a circular arc from pivot+n0 to pivot+n1 to p. The shorter of
+// the two possible arcs is taken, i.e. the one spanning <= 180 degrees. The
+// two vectors n0 and n1 must be of equal length.
+func addArc(p Adder, pivot, n0, n1 fixed.Point26_6) {
+ // r2 is the square of the length of n0.
+ r2 := pDot(n0, n0)
+ if r2 < epsilon {
+ // The arc radius is so small that we collapse to a straight line.
+ p.Add1(pivot.Add(n1))
+ return
+ }
+ // We approximate the arc by 0, 1, 2 or 3 45-degree quadratic segments plus
+ // a final quadratic segment from s to n1. Each 45-degree segment has
+ // control points {1, 0}, {1, tan(π/8)} and {1/√2, 1/√2} suitably scaled,
+ // rotated and translated. tan(π/8) is approximately 27/64.
+ const tpo8 = 27
+ var s fixed.Point26_6
+ // We determine which octant the angle between n0 and n1 is in via three
+ // dot products. m0, m1 and m2 are n0 rotated clockwise by 45, 90 and 135
+ // degrees.
+ m0 := pRot45CW(n0)
+ m1 := pRot90CW(n0)
+ m2 := pRot90CW(m0)
+ if pDot(m1, n1) >= 0 {
+ if pDot(n0, n1) >= 0 {
+ if pDot(m2, n1) <= 0 {
+ // n1 is between 0 and 45 degrees clockwise of n0.
+ s = n0
+ } else {
+ // n1 is between 45 and 90 degrees clockwise of n0.
+ p.Add2(pivot.Add(n0).Add(m1.Mul(tpo8)), pivot.Add(m0))
+ s = m0
+ }
+ } else {
+ pm1, n0t := pivot.Add(m1), n0.Mul(tpo8)
+ p.Add2(pivot.Add(n0).Add(m1.Mul(tpo8)), pivot.Add(m0))
+ p.Add2(pm1.Add(n0t), pm1)
+ if pDot(m0, n1) >= 0 {
+ // n1 is between 90 and 135 degrees clockwise of n0.
+ s = m1
+ } else {
+ // n1 is between 135 and 180 degrees clockwise of n0.
+ p.Add2(pm1.Sub(n0t), pivot.Add(m2))
+ s = m2
+ }
+ }
+ } else {
+ if pDot(n0, n1) >= 0 {
+ if pDot(m0, n1) >= 0 {
+ // n1 is between 0 and 45 degrees counter-clockwise of n0.
+ s = n0
+ } else {
+ // n1 is between 45 and 90 degrees counter-clockwise of n0.
+ p.Add2(pivot.Add(n0).Sub(m1.Mul(tpo8)), pivot.Sub(m2))
+ s = pNeg(m2)
+ }
+ } else {
+ pm1, n0t := pivot.Sub(m1), n0.Mul(tpo8)
+ p.Add2(pivot.Add(n0).Sub(m1.Mul(tpo8)), pivot.Sub(m2))
+ p.Add2(pm1.Add(n0t), pm1)
+ if pDot(m2, n1) <= 0 {
+ // n1 is between 90 and 135 degrees counter-clockwise of n0.
+ s = pNeg(m1)
+ } else {
+ // n1 is between 135 and 180 degrees counter-clockwise of n0.
+ p.Add2(pm1.Sub(n0t), pivot.Sub(m0))
+ s = pNeg(m0)
+ }
+ }
+ }
+ // The final quadratic segment has two endpoints s and n1 and the middle
+ // control point is a multiple of s.Add(n1), i.e. it is on the angle
+ // bisector of those two points. The multiple ranges between 128/256 and
+ // 150/256 as the angle between s and n1 ranges between 0 and 45 degrees.
+ //
+ // When the angle is 0 degrees (i.e. s and n1 are coincident) then
+ // s.Add(n1) is twice s and so the middle control point of the degenerate
+ // quadratic segment should be half s.Add(n1), and half = 128/256.
+ //
+ // When the angle is 45 degrees then 150/256 is the ratio of the lengths of
+ // the two vectors {1, tan(π/8)} and {1 + 1/√2, 1/√2}.
+ //
+ // d is the normalized dot product between s and n1. Since the angle ranges
+ // between 0 and 45 degrees then d ranges between 256/256 and 181/256.
+ d := 256 * pDot(s, n1) / r2
+ multiple := fixed.Int26_6(150-(150-128)*(d-181)/(256-181)) >> 2
+ p.Add2(pivot.Add(s.Add(n1).Mul(multiple)), pivot.Add(n1))
+}
+
+// midpoint returns the midpoint of two Points.
+func midpoint(a, b fixed.Point26_6) fixed.Point26_6 {
+ return fixed.Point26_6{(a.X + b.X) / 2, (a.Y + b.Y) / 2}
+}
+
+// angleGreaterThan45 returns whether the angle between two vectors is more
+// than 45 degrees.
+func angleGreaterThan45(v0, v1 fixed.Point26_6) bool {
+ v := pRot45CCW(v0)
+ return pDot(v, v1) < 0 || pDot(pRot90CW(v), v1) < 0
+}
+
+// interpolate returns the point (1-t)*a + t*b.
+func interpolate(a, b fixed.Point26_6, t fixed.Int52_12) fixed.Point26_6 {
+ s := 1<<12 - t
+ x := s*fixed.Int52_12(a.X) + t*fixed.Int52_12(b.X)
+ y := s*fixed.Int52_12(a.Y) + t*fixed.Int52_12(b.Y)
+ return fixed.Point26_6{fixed.Int26_6(x >> 12), fixed.Int26_6(y >> 12)}
+}
+
+// curviest2 returns the value of t for which the quadratic parametric curve
+// (1-t)²*a + 2*t*(1-t).b + t²*c has maximum curvature.
+//
+// The curvature of the parametric curve f(t) = (x(t), y(t)) is
+// |x′y″-y′x″| / (x′²+y′²)^(3/2).
+//
+// Let d = b-a and e = c-2*b+a, so that f′(t) = 2*d+2*e*t and f″(t) = 2*e.
+// The curvature's numerator is (2*dx+2*ex*t)*(2*ey)-(2*dy+2*ey*t)*(2*ex),
+// which simplifies to 4*dx*ey-4*dy*ex, which is constant with respect to t.
+//
+// Thus, curvature is extreme where the denominator is extreme, i.e. where
+// (x′²+y′²) is extreme. The first order condition is that
+// 2*x′*x″+2*y′*y″ = 0, or (dx+ex*t)*ex + (dy+ey*t)*ey = 0.
+// Solving for t gives t = -(dx*ex+dy*ey) / (ex*ex+ey*ey).
+func curviest2(a, b, c fixed.Point26_6) fixed.Int52_12 {
+ dx := int64(b.X - a.X)
+ dy := int64(b.Y - a.Y)
+ ex := int64(c.X - 2*b.X + a.X)
+ ey := int64(c.Y - 2*b.Y + a.Y)
+ if ex == 0 && ey == 0 {
+ return 2048
+ }
+ return fixed.Int52_12(-4096 * (dx*ex + dy*ey) / (ex*ex + ey*ey))
+}
+
+// A stroker holds state for stroking a path.
+type stroker struct {
+ // p is the destination that records the stroked path.
+ p Adder
+ // u is the half-width of the stroke.
+ u fixed.Int26_6
+ // cr and jr specify how to end and connect path segments.
+ cr Capper
+ jr Joiner
+ // r is the reverse path. Stroking a path involves constructing two
+ // parallel paths 2*u apart. The first path is added immediately to p,
+ // the second path is accumulated in r and eventually added in reverse.
+ r Path
+ // a is the most recent segment point. anorm is the segment normal of
+ // length u at that point.
+ a, anorm fixed.Point26_6
+}
+
+// addNonCurvy2 adds a quadratic segment to the stroker, where the segment
+// defined by (k.a, b, c) achieves maximum curvature at either k.a or c.
+func (k *stroker) addNonCurvy2(b, c fixed.Point26_6) {
+ // We repeatedly divide the segment at its middle until it is straight
+ // enough to approximate the stroke by just translating the control points.
+ // ds and ps are stacks of depths and points. t is the top of the stack.
+ const maxDepth = 5
+ var (
+ ds [maxDepth + 1]int
+ ps [2*maxDepth + 3]fixed.Point26_6
+ t int
+ )
+ // Initially the ps stack has one quadratic segment of depth zero.
+ ds[0] = 0
+ ps[2] = k.a
+ ps[1] = b
+ ps[0] = c
+ anorm := k.anorm
+ var cnorm fixed.Point26_6
+
+ for {
+ depth := ds[t]
+ a := ps[2*t+2]
+ b := ps[2*t+1]
+ c := ps[2*t+0]
+ ab := b.Sub(a)
+ bc := c.Sub(b)
+ abIsSmall := pDot(ab, ab) < fixed.Int52_12(1<<12)
+ bcIsSmall := pDot(bc, bc) < fixed.Int52_12(1<<12)
+ if abIsSmall && bcIsSmall {
+ // Approximate the segment by a circular arc.
+ cnorm = pRot90CCW(pNorm(bc, k.u))
+ mac := midpoint(a, c)
+ addArc(k.p, mac, anorm, cnorm)
+ addArc(&k.r, mac, pNeg(anorm), pNeg(cnorm))
+ } else if depth < maxDepth && angleGreaterThan45(ab, bc) {
+ // Divide the segment in two and push both halves on the stack.
+ mab := midpoint(a, b)
+ mbc := midpoint(b, c)
+ t++
+ ds[t+0] = depth + 1
+ ds[t-1] = depth + 1
+ ps[2*t+2] = a
+ ps[2*t+1] = mab
+ ps[2*t+0] = midpoint(mab, mbc)
+ ps[2*t-1] = mbc
+ continue
+ } else {
+ // Translate the control points.
+ bnorm := pRot90CCW(pNorm(c.Sub(a), k.u))
+ cnorm = pRot90CCW(pNorm(bc, k.u))
+ k.p.Add2(b.Add(bnorm), c.Add(cnorm))
+ k.r.Add2(b.Sub(bnorm), c.Sub(cnorm))
+ }
+ if t == 0 {
+ k.a, k.anorm = c, cnorm
+ return
+ }
+ t--
+ anorm = cnorm
+ }
+ panic("unreachable")
+}
+
+// Add1 adds a linear segment to the stroker.
+func (k *stroker) Add1(b fixed.Point26_6) {
+ bnorm := pRot90CCW(pNorm(b.Sub(k.a), k.u))
+ if len(k.r) == 0 {
+ k.p.Start(k.a.Add(bnorm))
+ k.r.Start(k.a.Sub(bnorm))
+ } else {
+ k.jr.Join(k.p, &k.r, k.u, k.a, k.anorm, bnorm)
+ }
+ k.p.Add1(b.Add(bnorm))
+ k.r.Add1(b.Sub(bnorm))
+ k.a, k.anorm = b, bnorm
+}
+
+// Add2 adds a quadratic segment to the stroker.
+func (k *stroker) Add2(b, c fixed.Point26_6) {
+ ab := b.Sub(k.a)
+ bc := c.Sub(b)
+ abnorm := pRot90CCW(pNorm(ab, k.u))
+ if len(k.r) == 0 {
+ k.p.Start(k.a.Add(abnorm))
+ k.r.Start(k.a.Sub(abnorm))
+ } else {
+ k.jr.Join(k.p, &k.r, k.u, k.a, k.anorm, abnorm)
+ }
+
+ // Approximate nearly-degenerate quadratics by linear segments.
+ abIsSmall := pDot(ab, ab) < epsilon
+ bcIsSmall := pDot(bc, bc) < epsilon
+ if abIsSmall || bcIsSmall {
+ acnorm := pRot90CCW(pNorm(c.Sub(k.a), k.u))
+ k.p.Add1(c.Add(acnorm))
+ k.r.Add1(c.Sub(acnorm))
+ k.a, k.anorm = c, acnorm
+ return
+ }
+
+ // The quadratic segment (k.a, b, c) has a point of maximum curvature.
+ // If this occurs at an end point, we process the segment as a whole.
+ t := curviest2(k.a, b, c)
+ if t <= 0 || 4096 <= t {
+ k.addNonCurvy2(b, c)
+ return
+ }
+
+ // Otherwise, we perform a de Casteljau decomposition at the point of
+ // maximum curvature and process the two straighter parts.
+ mab := interpolate(k.a, b, t)
+ mbc := interpolate(b, c, t)
+ mabc := interpolate(mab, mbc, t)
+
+ // If the vectors ab and bc are close to being in opposite directions,
+ // then the decomposition can become unstable, so we approximate the
+ // quadratic segment by two linear segments joined by an arc.
+ bcnorm := pRot90CCW(pNorm(bc, k.u))
+ if pDot(abnorm, bcnorm) < -fixed.Int52_12(k.u)*fixed.Int52_12(k.u)*2047/2048 {
+ pArc := pDot(abnorm, bc) < 0
+
+ k.p.Add1(mabc.Add(abnorm))
+ if pArc {
+ z := pRot90CW(abnorm)
+ addArc(k.p, mabc, abnorm, z)
+ addArc(k.p, mabc, z, bcnorm)
+ }
+ k.p.Add1(mabc.Add(bcnorm))
+ k.p.Add1(c.Add(bcnorm))
+
+ k.r.Add1(mabc.Sub(abnorm))
+ if !pArc {
+ z := pRot90CW(abnorm)
+ addArc(&k.r, mabc, pNeg(abnorm), z)
+ addArc(&k.r, mabc, z, pNeg(bcnorm))
+ }
+ k.r.Add1(mabc.Sub(bcnorm))
+ k.r.Add1(c.Sub(bcnorm))
+
+ k.a, k.anorm = c, bcnorm
+ return
+ }
+
+ // Process the decomposed parts.
+ k.addNonCurvy2(mab, mabc)
+ k.addNonCurvy2(mbc, c)
+}
+
+// Add3 adds a cubic segment to the stroker.
+func (k *stroker) Add3(b, c, d fixed.Point26_6) {
+ panic("freetype/raster: stroke unimplemented for cubic segments")
+}
+
+// stroke adds the stroked Path q to p, where q consists of exactly one curve.
+func (k *stroker) stroke(q Path) {
+ // Stroking is implemented by deriving two paths each k.u apart from q.
+ // The left-hand-side path is added immediately to k.p; the right-hand-side
+ // path is accumulated in k.r. Once we've finished adding the LHS to k.p,
+ // we add the RHS in reverse order.
+ k.r = make(Path, 0, len(q))
+ k.a = fixed.Point26_6{q[1], q[2]}
+ for i := 4; i < len(q); {
+ switch q[i] {
+ case 1:
+ k.Add1(
+ fixed.Point26_6{q[i+1], q[i+2]},
+ )
+ i += 4
+ case 2:
+ k.Add2(
+ fixed.Point26_6{q[i+1], q[i+2]},
+ fixed.Point26_6{q[i+3], q[i+4]},
+ )
+ i += 6
+ case 3:
+ k.Add3(
+ fixed.Point26_6{q[i+1], q[i+2]},
+ fixed.Point26_6{q[i+3], q[i+4]},
+ fixed.Point26_6{q[i+5], q[i+6]},
+ )
+ i += 8
+ default:
+ panic("freetype/raster: bad path")
+ }
+ }
+ if len(k.r) == 0 {
+ return
+ }
+ // TODO(nigeltao): if q is a closed curve then we should join the first and
+ // last segments instead of capping them.
+ k.cr.Cap(k.p, k.u, q.lastPoint(), pNeg(k.anorm))
+ addPathReversed(k.p, k.r)
+ pivot := q.firstPoint()
+ k.cr.Cap(k.p, k.u, pivot, pivot.Sub(fixed.Point26_6{k.r[1], k.r[2]}))
+}
+
+// Stroke adds q stroked with the given width to p. The result is typically
+// self-intersecting and should be rasterized with UseNonZeroWinding.
+// cr and jr may be nil, which defaults to a RoundCapper or RoundJoiner.
+func Stroke(p Adder, q Path, width fixed.Int26_6, cr Capper, jr Joiner) {
+ if len(q) == 0 {
+ return
+ }
+ if cr == nil {
+ cr = RoundCapper
+ }
+ if jr == nil {
+ jr = RoundJoiner
+ }
+ if q[0] != 0 {
+ panic("freetype/raster: bad path")
+ }
+ s := stroker{p: p, u: width / 2, cr: cr, jr: jr}
+ i := 0
+ for j := 4; j < len(q); {
+ switch q[j] {
+ case 0:
+ s.stroke(q[i:j])
+ i, j = j, j+4
+ case 1:
+ j += 4
+ case 2:
+ j += 6
+ case 3:
+ j += 8
+ default:
+ panic("freetype/raster: bad path")
+ }
+ }
+ s.stroke(q[i:])
+}
diff --git a/vendor/github.com/golang/freetype/truetype/LICENSE b/vendor/github.com/golang/freetype/truetype/LICENSE
new file mode 100644
index 0000000..e854ba5
--- /dev/null
+++ b/vendor/github.com/golang/freetype/truetype/LICENSE
@@ -0,0 +1,12 @@
+Use of the Freetype-Go software is subject to your choice of exactly one of
+the following two licenses:
+ * The FreeType License, which is similar to the original BSD license with
+ an advertising clause, or
+ * The GNU General Public License (GPL), version 2 or later.
+
+The text of these licenses are available in the licenses/ftl.txt and the
+licenses/gpl.txt files respectively. They are also available at
+http://freetype.sourceforge.net/license.html
+
+The Luxi fonts in the testdata directory are licensed separately. See the
+testdata/COPYING file for details.
diff --git a/vendor/github.com/golang/freetype/truetype/face.go b/vendor/github.com/golang/freetype/truetype/face.go
new file mode 100644
index 0000000..099006f
--- /dev/null
+++ b/vendor/github.com/golang/freetype/truetype/face.go
@@ -0,0 +1,507 @@
+// Copyright 2015 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+package truetype
+
+import (
+ "image"
+ "math"
+
+ "github.com/golang/freetype/raster"
+ "golang.org/x/image/font"
+ "golang.org/x/image/math/fixed"
+)
+
+func powerOf2(i int) bool {
+ return i != 0 && (i&(i-1)) == 0
+}
+
+// Options are optional arguments to NewFace.
+type Options struct {
+ // Size is the font size in points, as in "a 10 point font size".
+ //
+ // A zero value means to use a 12 point font size.
+ Size float64
+
+ // DPI is the dots-per-inch resolution.
+ //
+ // A zero value means to use 72 DPI.
+ DPI float64
+
+ // Hinting is how to quantize the glyph nodes.
+ //
+ // A zero value means to use no hinting.
+ Hinting font.Hinting
+
+ // GlyphCacheEntries is the number of entries in the glyph mask image
+ // cache.
+ //
+ // If non-zero, it must be a power of 2.
+ //
+ // A zero value means to use 512 entries.
+ GlyphCacheEntries int
+
+ // SubPixelsX is the number of sub-pixel locations a glyph's dot is
+ // quantized to, in the horizontal direction. For example, a value of 8
+ // means that the dot is quantized to 1/8th of a pixel. This quantization
+ // only affects the glyph mask image, not its bounding box or advance
+ // width. A higher value gives a more faithful glyph image, but reduces the
+ // effectiveness of the glyph cache.
+ //
+ // If non-zero, it must be a power of 2, and be between 1 and 64 inclusive.
+ //
+ // A zero value means to use 4 sub-pixel locations.
+ SubPixelsX int
+
+ // SubPixelsY is the number of sub-pixel locations a glyph's dot is
+ // quantized to, in the vertical direction. For example, a value of 8
+ // means that the dot is quantized to 1/8th of a pixel. This quantization
+ // only affects the glyph mask image, not its bounding box or advance
+ // width. A higher value gives a more faithful glyph image, but reduces the
+ // effectiveness of the glyph cache.
+ //
+ // If non-zero, it must be a power of 2, and be between 1 and 64 inclusive.
+ //
+ // A zero value means to use 1 sub-pixel location.
+ SubPixelsY int
+}
+
+func (o *Options) size() float64 {
+ if o != nil && o.Size > 0 {
+ return o.Size
+ }
+ return 12
+}
+
+func (o *Options) dpi() float64 {
+ if o != nil && o.DPI > 0 {
+ return o.DPI
+ }
+ return 72
+}
+
+func (o *Options) hinting() font.Hinting {
+ if o != nil {
+ switch o.Hinting {
+ case font.HintingVertical, font.HintingFull:
+ // TODO: support vertical hinting.
+ return font.HintingFull
+ }
+ }
+ return font.HintingNone
+}
+
+func (o *Options) glyphCacheEntries() int {
+ if o != nil && powerOf2(o.GlyphCacheEntries) {
+ return o.GlyphCacheEntries
+ }
+ // 512 is 128 * 4 * 1, which lets us cache 128 glyphs at 4 * 1 subpixel
+ // locations in the X and Y direction.
+ return 512
+}
+
+func (o *Options) subPixelsX() (value uint32, halfQuantum, mask fixed.Int26_6) {
+ if o != nil {
+ switch o.SubPixelsX {
+ case 1, 2, 4, 8, 16, 32, 64:
+ return subPixels(o.SubPixelsX)
+ }
+ }
+ // This default value of 4 isn't based on anything scientific, merely as
+ // small a number as possible that looks almost as good as no quantization,
+ // or returning subPixels(64).
+ return subPixels(4)
+}
+
+func (o *Options) subPixelsY() (value uint32, halfQuantum, mask fixed.Int26_6) {
+ if o != nil {
+ switch o.SubPixelsX {
+ case 1, 2, 4, 8, 16, 32, 64:
+ return subPixels(o.SubPixelsX)
+ }
+ }
+ // This default value of 1 isn't based on anything scientific, merely that
+ // vertical sub-pixel glyph rendering is pretty rare. Baseline locations
+ // can usually afford to snap to the pixel grid, so the vertical direction
+ // doesn't have the deal with the horizontal's fractional advance widths.
+ return subPixels(1)
+}
+
+// subPixels returns q and the bias and mask that leads to q quantized
+// sub-pixel locations per full pixel.
+//
+// For example, q == 4 leads to a bias of 8 and a mask of 0xfffffff0, or -16,
+// because we want to round fractions of fixed.Int26_6 as:
+// - 0 to 7 rounds to 0.
+// - 8 to 23 rounds to 16.
+// - 24 to 39 rounds to 32.
+// - 40 to 55 rounds to 48.
+// - 56 to 63 rounds to 64.
+// which means to add 8 and then bitwise-and with -16, in two's complement
+// representation.
+//
+// When q == 1, we want bias == 32 and mask == -64.
+// When q == 2, we want bias == 16 and mask == -32.
+// When q == 4, we want bias == 8 and mask == -16.
+// ...
+// When q == 64, we want bias == 0 and mask == -1. (The no-op case).
+// The pattern is clear.
+func subPixels(q int) (value uint32, bias, mask fixed.Int26_6) {
+ return uint32(q), 32 / fixed.Int26_6(q), -64 / fixed.Int26_6(q)
+}
+
+// glyphCacheEntry caches the arguments and return values of rasterize.
+type glyphCacheEntry struct {
+ key glyphCacheKey
+ val glyphCacheVal
+}
+
+type glyphCacheKey struct {
+ index Index
+ fx, fy uint8
+}
+
+type glyphCacheVal struct {
+ advanceWidth fixed.Int26_6
+ offset image.Point
+ gw int
+ gh int
+}
+
+type indexCacheEntry struct {
+ rune rune
+ index Index
+}
+
+// NewFace returns a new font.Face for the given Font.
+func NewFace(f *Font, opts *Options) font.Face {
+ a := &face{
+ f: f,
+ hinting: opts.hinting(),
+ scale: fixed.Int26_6(0.5 + (opts.size() * opts.dpi() * 64 / 72)),
+ glyphCache: make([]glyphCacheEntry, opts.glyphCacheEntries()),
+ }
+ a.subPixelX, a.subPixelBiasX, a.subPixelMaskX = opts.subPixelsX()
+ a.subPixelY, a.subPixelBiasY, a.subPixelMaskY = opts.subPixelsY()
+
+ // Fill the cache with invalid entries. Valid glyph cache entries have fx
+ // and fy in the range [0, 64). Valid index cache entries have rune >= 0.
+ for i := range a.glyphCache {
+ a.glyphCache[i].key.fy = 0xff
+ }
+ for i := range a.indexCache {
+ a.indexCache[i].rune = -1
+ }
+
+ // Set the rasterizer's bounds to be big enough to handle the largest glyph.
+ b := f.Bounds(a.scale)
+ xmin := +int(b.Min.X) >> 6
+ ymin := -int(b.Max.Y) >> 6
+ xmax := +int(b.Max.X+63) >> 6
+ ymax := -int(b.Min.Y-63) >> 6
+ a.maxw = xmax - xmin
+ a.maxh = ymax - ymin
+ a.masks = image.NewAlpha(image.Rect(0, 0, a.maxw, a.maxh*len(a.glyphCache)))
+ a.r.SetBounds(a.maxw, a.maxh)
+ a.p = facePainter{a}
+
+ return a
+}
+
+type face struct {
+ f *Font
+ hinting font.Hinting
+ scale fixed.Int26_6
+ subPixelX uint32
+ subPixelBiasX fixed.Int26_6
+ subPixelMaskX fixed.Int26_6
+ subPixelY uint32
+ subPixelBiasY fixed.Int26_6
+ subPixelMaskY fixed.Int26_6
+ masks *image.Alpha
+ glyphCache []glyphCacheEntry
+ r raster.Rasterizer
+ p raster.Painter
+ paintOffset int
+ maxw int
+ maxh int
+ glyphBuf GlyphBuf
+ indexCache [indexCacheLen]indexCacheEntry
+
+ // TODO: clip rectangle?
+}
+
+const indexCacheLen = 256
+
+func (a *face) index(r rune) Index {
+ const mask = indexCacheLen - 1
+ c := &a.indexCache[r&mask]
+ if c.rune == r {
+ return c.index
+ }
+ i := a.f.Index(r)
+ c.rune = r
+ c.index = i
+ return i
+}
+
+// Close satisfies the font.Face interface.
+func (a *face) Close() error { return nil }
+
+// Metrics satisfies the font.Face interface.
+func (a *face) Metrics() font.Metrics {
+ scale := float64(a.scale)
+ fupe := float64(a.f.FUnitsPerEm())
+ return font.Metrics{
+ Height: a.scale,
+ Ascent: fixed.Int26_6(math.Ceil(scale * float64(+a.f.ascent) / fupe)),
+ Descent: fixed.Int26_6(math.Ceil(scale * float64(-a.f.descent) / fupe)),
+ }
+}
+
+// Kern satisfies the font.Face interface.
+func (a *face) Kern(r0, r1 rune) fixed.Int26_6 {
+ i0 := a.index(r0)
+ i1 := a.index(r1)
+ kern := a.f.Kern(a.scale, i0, i1)
+ if a.hinting != font.HintingNone {
+ kern = (kern + 32) &^ 63
+ }
+ return kern
+}
+
+// Glyph satisfies the font.Face interface.
+func (a *face) Glyph(dot fixed.Point26_6, r rune) (
+ dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) {
+
+ // Quantize to the sub-pixel granularity.
+ dotX := (dot.X + a.subPixelBiasX) & a.subPixelMaskX
+ dotY := (dot.Y + a.subPixelBiasY) & a.subPixelMaskY
+
+ // Split the coordinates into their integer and fractional parts.
+ ix, fx := int(dotX>>6), dotX&0x3f
+ iy, fy := int(dotY>>6), dotY&0x3f
+
+ index := a.index(r)
+ cIndex := uint32(index)
+ cIndex = cIndex*a.subPixelX - uint32(fx/a.subPixelMaskX)
+ cIndex = cIndex*a.subPixelY - uint32(fy/a.subPixelMaskY)
+ cIndex &= uint32(len(a.glyphCache) - 1)
+ a.paintOffset = a.maxh * int(cIndex)
+ k := glyphCacheKey{
+ index: index,
+ fx: uint8(fx),
+ fy: uint8(fy),
+ }
+ var v glyphCacheVal
+ if a.glyphCache[cIndex].key != k {
+ var ok bool
+ v, ok = a.rasterize(index, fx, fy)
+ if !ok {
+ return image.Rectangle{}, nil, image.Point{}, 0, false
+ }
+ a.glyphCache[cIndex] = glyphCacheEntry{k, v}
+ } else {
+ v = a.glyphCache[cIndex].val
+ }
+
+ dr.Min = image.Point{
+ X: ix + v.offset.X,
+ Y: iy + v.offset.Y,
+ }
+ dr.Max = image.Point{
+ X: dr.Min.X + v.gw,
+ Y: dr.Min.Y + v.gh,
+ }
+ return dr, a.masks, image.Point{Y: a.paintOffset}, v.advanceWidth, true
+}
+
+func (a *face) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) {
+ if err := a.glyphBuf.Load(a.f, a.scale, a.index(r), a.hinting); err != nil {
+ return fixed.Rectangle26_6{}, 0, false
+ }
+ xmin := +a.glyphBuf.Bounds.Min.X
+ ymin := -a.glyphBuf.Bounds.Max.Y
+ xmax := +a.glyphBuf.Bounds.Max.X
+ ymax := -a.glyphBuf.Bounds.Min.Y
+ if xmin > xmax || ymin > ymax {
+ return fixed.Rectangle26_6{}, 0, false
+ }
+ return fixed.Rectangle26_6{
+ Min: fixed.Point26_6{
+ X: xmin,
+ Y: ymin,
+ },
+ Max: fixed.Point26_6{
+ X: xmax,
+ Y: ymax,
+ },
+ }, a.glyphBuf.AdvanceWidth, true
+}
+
+func (a *face) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) {
+ if err := a.glyphBuf.Load(a.f, a.scale, a.index(r), a.hinting); err != nil {
+ return 0, false
+ }
+ return a.glyphBuf.AdvanceWidth, true
+}
+
+// rasterize returns the advance width, integer-pixel offset to render at, and
+// the width and height of the given glyph at the given sub-pixel offsets.
+//
+// The 26.6 fixed point arguments fx and fy must be in the range [0, 1).
+func (a *face) rasterize(index Index, fx, fy fixed.Int26_6) (v glyphCacheVal, ok bool) {
+ if err := a.glyphBuf.Load(a.f, a.scale, index, a.hinting); err != nil {
+ return glyphCacheVal{}, false
+ }
+ // Calculate the integer-pixel bounds for the glyph.
+ xmin := int(fx+a.glyphBuf.Bounds.Min.X) >> 6
+ ymin := int(fy-a.glyphBuf.Bounds.Max.Y) >> 6
+ xmax := int(fx+a.glyphBuf.Bounds.Max.X+0x3f) >> 6
+ ymax := int(fy-a.glyphBuf.Bounds.Min.Y+0x3f) >> 6
+ if xmin > xmax || ymin > ymax {
+ return glyphCacheVal{}, false
+ }
+ // A TrueType's glyph's nodes can have negative co-ordinates, but the
+ // rasterizer clips anything left of x=0 or above y=0. xmin and ymin are
+ // the pixel offsets, based on the font's FUnit metrics, that let a
+ // negative co-ordinate in TrueType space be non-negative in rasterizer
+ // space. xmin and ymin are typically <= 0.
+ fx -= fixed.Int26_6(xmin << 6)
+ fy -= fixed.Int26_6(ymin << 6)
+ // Rasterize the glyph's vectors.
+ a.r.Clear()
+ pixOffset := a.paintOffset * a.maxw
+ clear(a.masks.Pix[pixOffset : pixOffset+a.maxw*a.maxh])
+ e0 := 0
+ for _, e1 := range a.glyphBuf.Ends {
+ a.drawContour(a.glyphBuf.Points[e0:e1], fx, fy)
+ e0 = e1
+ }
+ a.r.Rasterize(a.p)
+ return glyphCacheVal{
+ a.glyphBuf.AdvanceWidth,
+ image.Point{xmin, ymin},
+ xmax - xmin,
+ ymax - ymin,
+ }, true
+}
+
+func clear(pix []byte) {
+ for i := range pix {
+ pix[i] = 0
+ }
+}
+
+// drawContour draws the given closed contour with the given offset.
+func (a *face) drawContour(ps []Point, dx, dy fixed.Int26_6) {
+ if len(ps) == 0 {
+ return
+ }
+
+ // The low bit of each point's Flags value is whether the point is on the
+ // curve. Truetype fonts only have quadratic Bézier curves, not cubics.
+ // Thus, two consecutive off-curve points imply an on-curve point in the
+ // middle of those two.
+ //
+ // See http://chanae.walon.org/pub/ttf/ttf_glyphs.htm for more details.
+
+ // ps[0] is a truetype.Point measured in FUnits and positive Y going
+ // upwards. start is the same thing measured in fixed point units and
+ // positive Y going downwards, and offset by (dx, dy).
+ start := fixed.Point26_6{
+ X: dx + ps[0].X,
+ Y: dy - ps[0].Y,
+ }
+ var others []Point
+ if ps[0].Flags&0x01 != 0 {
+ others = ps[1:]
+ } else {
+ last := fixed.Point26_6{
+ X: dx + ps[len(ps)-1].X,
+ Y: dy - ps[len(ps)-1].Y,
+ }
+ if ps[len(ps)-1].Flags&0x01 != 0 {
+ start = last
+ others = ps[:len(ps)-1]
+ } else {
+ start = fixed.Point26_6{
+ X: (start.X + last.X) / 2,
+ Y: (start.Y + last.Y) / 2,
+ }
+ others = ps
+ }
+ }
+ a.r.Start(start)
+ q0, on0 := start, true
+ for _, p := range others {
+ q := fixed.Point26_6{
+ X: dx + p.X,
+ Y: dy - p.Y,
+ }
+ on := p.Flags&0x01 != 0
+ if on {
+ if on0 {
+ a.r.Add1(q)
+ } else {
+ a.r.Add2(q0, q)
+ }
+ } else {
+ if on0 {
+ // No-op.
+ } else {
+ mid := fixed.Point26_6{
+ X: (q0.X + q.X) / 2,
+ Y: (q0.Y + q.Y) / 2,
+ }
+ a.r.Add2(q0, mid)
+ }
+ }
+ q0, on0 = q, on
+ }
+ // Close the curve.
+ if on0 {
+ a.r.Add1(start)
+ } else {
+ a.r.Add2(q0, start)
+ }
+}
+
+// facePainter is like a raster.AlphaSrcPainter, with an additional Y offset
+// (face.paintOffset) to the painted spans.
+type facePainter struct {
+ a *face
+}
+
+func (p facePainter) Paint(ss []raster.Span, done bool) {
+ m := p.a.masks
+ b := m.Bounds()
+ b.Min.Y = p.a.paintOffset
+ b.Max.Y = p.a.paintOffset + p.a.maxh
+ for _, s := range ss {
+ s.Y += p.a.paintOffset
+ if s.Y < b.Min.Y {
+ continue
+ }
+ if s.Y >= b.Max.Y {
+ return
+ }
+ if s.X0 < b.Min.X {
+ s.X0 = b.Min.X
+ }
+ if s.X1 > b.Max.X {
+ s.X1 = b.Max.X
+ }
+ if s.X0 >= s.X1 {
+ continue
+ }
+ base := (s.Y-m.Rect.Min.Y)*m.Stride - m.Rect.Min.X
+ p := m.Pix[base+s.X0 : base+s.X1]
+ color := uint8(s.Alpha >> 8)
+ for i := range p {
+ p[i] = color
+ }
+ }
+}
diff --git a/vendor/github.com/golang/freetype/truetype/glyph.go b/vendor/github.com/golang/freetype/truetype/glyph.go
new file mode 100644
index 0000000..c2935a5
--- /dev/null
+++ b/vendor/github.com/golang/freetype/truetype/glyph.go
@@ -0,0 +1,517 @@
+// Copyright 2010 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+package truetype
+
+import (
+ "golang.org/x/image/font"
+ "golang.org/x/image/math/fixed"
+)
+
+// TODO: implement VerticalHinting.
+
+// A Point is a co-ordinate pair plus whether it is 'on' a contour or an 'off'
+// control point.
+type Point struct {
+ X, Y fixed.Int26_6
+ // The Flags' LSB means whether or not this Point is 'on' the contour.
+ // Other bits are reserved for internal use.
+ Flags uint32
+}
+
+// A GlyphBuf holds a glyph's contours. A GlyphBuf can be re-used to load a
+// series of glyphs from a Font.
+type GlyphBuf struct {
+ // AdvanceWidth is the glyph's advance width.
+ AdvanceWidth fixed.Int26_6
+ // Bounds is the glyph's bounding box.
+ Bounds fixed.Rectangle26_6
+ // Points contains all Points from all contours of the glyph. If hinting
+ // was used to load a glyph then Unhinted contains those Points before they
+ // were hinted, and InFontUnits contains those Points before they were
+ // hinted and scaled.
+ Points, Unhinted, InFontUnits []Point
+ // Ends is the point indexes of the end point of each contour. The length
+ // of Ends is the number of contours in the glyph. The i'th contour
+ // consists of points Points[Ends[i-1]:Ends[i]], where Ends[-1] is
+ // interpreted to mean zero.
+ Ends []int
+
+ font *Font
+ scale fixed.Int26_6
+ hinting font.Hinting
+ hinter hinter
+ // phantomPoints are the co-ordinates of the synthetic phantom points
+ // used for hinting and bounding box calculations.
+ phantomPoints [4]Point
+ // pp1x is the X co-ordinate of the first phantom point. The '1' is
+ // using 1-based indexing; pp1x is almost always phantomPoints[0].X.
+ // TODO: eliminate this and consistently use phantomPoints[0].X.
+ pp1x fixed.Int26_6
+ // metricsSet is whether the glyph's metrics have been set yet. For a
+ // compound glyph, a sub-glyph may override the outer glyph's metrics.
+ metricsSet bool
+ // tmp is a scratch buffer.
+ tmp []Point
+}
+
+// Flags for decoding a glyph's contours. These flags are documented at
+// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html.
+const (
+ flagOnCurve = 1 << iota
+ flagXShortVector
+ flagYShortVector
+ flagRepeat
+ flagPositiveXShortVector
+ flagPositiveYShortVector
+
+ // The remaining flags are for internal use.
+ flagTouchedX
+ flagTouchedY
+)
+
+// The same flag bits (0x10 and 0x20) are overloaded to have two meanings,
+// dependent on the value of the flag{X,Y}ShortVector bits.
+const (
+ flagThisXIsSame = flagPositiveXShortVector
+ flagThisYIsSame = flagPositiveYShortVector
+)
+
+// Load loads a glyph's contours from a Font, overwriting any previously loaded
+// contours for this GlyphBuf. scale is the number of 26.6 fixed point units in
+// 1 em, i is the glyph index, and h is the hinting policy.
+func (g *GlyphBuf) Load(f *Font, scale fixed.Int26_6, i Index, h font.Hinting) error {
+ g.Points = g.Points[:0]
+ g.Unhinted = g.Unhinted[:0]
+ g.InFontUnits = g.InFontUnits[:0]
+ g.Ends = g.Ends[:0]
+ g.font = f
+ g.hinting = h
+ g.scale = scale
+ g.pp1x = 0
+ g.phantomPoints = [4]Point{}
+ g.metricsSet = false
+
+ if h != font.HintingNone {
+ if err := g.hinter.init(f, scale); err != nil {
+ return err
+ }
+ }
+ if err := g.load(0, i, true); err != nil {
+ return err
+ }
+ // TODO: this selection of either g.pp1x or g.phantomPoints[0].X isn't ideal,
+ // and should be cleaned up once we have all the testScaling tests passing,
+ // plus additional tests for Freetype-Go's bounding boxes matching C Freetype's.
+ pp1x := g.pp1x
+ if h != font.HintingNone {
+ pp1x = g.phantomPoints[0].X
+ }
+ if pp1x != 0 {
+ for i := range g.Points {
+ g.Points[i].X -= pp1x
+ }
+ }
+
+ advanceWidth := g.phantomPoints[1].X - g.phantomPoints[0].X
+ if h != font.HintingNone {
+ if len(f.hdmx) >= 8 {
+ if n := u32(f.hdmx, 4); n > 3+uint32(i) {
+ for hdmx := f.hdmx[8:]; uint32(len(hdmx)) >= n; hdmx = hdmx[n:] {
+ if fixed.Int26_6(hdmx[0]) == scale>>6 {
+ advanceWidth = fixed.Int26_6(hdmx[2+i]) << 6
+ break
+ }
+ }
+ }
+ }
+ advanceWidth = (advanceWidth + 32) &^ 63
+ }
+ g.AdvanceWidth = advanceWidth
+
+ // Set g.Bounds to the 'control box', which is the bounding box of the
+ // Bézier curves' control points. This is easier to calculate, no smaller
+ // than and often equal to the tightest possible bounding box of the curves
+ // themselves. This approach is what C Freetype does. We can't just scale
+ // the nominal bounding box in the glyf data as the hinting process and
+ // phantom point adjustment may move points outside of that box.
+ if len(g.Points) == 0 {
+ g.Bounds = fixed.Rectangle26_6{}
+ } else {
+ p := g.Points[0]
+ g.Bounds.Min.X = p.X
+ g.Bounds.Max.X = p.X
+ g.Bounds.Min.Y = p.Y
+ g.Bounds.Max.Y = p.Y
+ for _, p := range g.Points[1:] {
+ if g.Bounds.Min.X > p.X {
+ g.Bounds.Min.X = p.X
+ } else if g.Bounds.Max.X < p.X {
+ g.Bounds.Max.X = p.X
+ }
+ if g.Bounds.Min.Y > p.Y {
+ g.Bounds.Min.Y = p.Y
+ } else if g.Bounds.Max.Y < p.Y {
+ g.Bounds.Max.Y = p.Y
+ }
+ }
+ // Snap the box to the grid, if hinting is on.
+ if h != font.HintingNone {
+ g.Bounds.Min.X &^= 63
+ g.Bounds.Min.Y &^= 63
+ g.Bounds.Max.X += 63
+ g.Bounds.Max.X &^= 63
+ g.Bounds.Max.Y += 63
+ g.Bounds.Max.Y &^= 63
+ }
+ }
+ return nil
+}
+
+func (g *GlyphBuf) load(recursion uint32, i Index, useMyMetrics bool) (err error) {
+ // The recursion limit here is arbitrary, but defends against malformed glyphs.
+ if recursion >= 32 {
+ return UnsupportedError("excessive compound glyph recursion")
+ }
+ // Find the relevant slice of g.font.glyf.
+ var g0, g1 uint32
+ if g.font.locaOffsetFormat == locaOffsetFormatShort {
+ g0 = 2 * uint32(u16(g.font.loca, 2*int(i)))
+ g1 = 2 * uint32(u16(g.font.loca, 2*int(i)+2))
+ } else {
+ g0 = u32(g.font.loca, 4*int(i))
+ g1 = u32(g.font.loca, 4*int(i)+4)
+ }
+
+ // Decode the contour count and nominal bounding box, from the first
+ // 10 bytes of the glyf data. boundsYMin and boundsXMax, at offsets 4
+ // and 6, are unused.
+ glyf, ne, boundsXMin, boundsYMax := []byte(nil), 0, fixed.Int26_6(0), fixed.Int26_6(0)
+ if g0+10 <= g1 {
+ glyf = g.font.glyf[g0:g1]
+ ne = int(int16(u16(glyf, 0)))
+ boundsXMin = fixed.Int26_6(int16(u16(glyf, 2)))
+ boundsYMax = fixed.Int26_6(int16(u16(glyf, 8)))
+ }
+
+ // Create the phantom points.
+ uhm, pp1x := g.font.unscaledHMetric(i), fixed.Int26_6(0)
+ uvm := g.font.unscaledVMetric(i, boundsYMax)
+ g.phantomPoints = [4]Point{
+ {X: boundsXMin - uhm.LeftSideBearing},
+ {X: boundsXMin - uhm.LeftSideBearing + uhm.AdvanceWidth},
+ {X: uhm.AdvanceWidth / 2, Y: boundsYMax + uvm.TopSideBearing},
+ {X: uhm.AdvanceWidth / 2, Y: boundsYMax + uvm.TopSideBearing - uvm.AdvanceHeight},
+ }
+ if len(glyf) == 0 {
+ g.addPhantomsAndScale(len(g.Points), len(g.Points), true, true)
+ copy(g.phantomPoints[:], g.Points[len(g.Points)-4:])
+ g.Points = g.Points[:len(g.Points)-4]
+ return nil
+ }
+
+ // Load and hint the contours.
+ if ne < 0 {
+ if ne != -1 {
+ // http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html says that
+ // "the values -2, -3, and so forth, are reserved for future use."
+ return UnsupportedError("negative number of contours")
+ }
+ pp1x = g.font.scale(g.scale * (boundsXMin - uhm.LeftSideBearing))
+ if err := g.loadCompound(recursion, uhm, i, glyf, useMyMetrics); err != nil {
+ return err
+ }
+ } else {
+ np0, ne0 := len(g.Points), len(g.Ends)
+ program := g.loadSimple(glyf, ne)
+ g.addPhantomsAndScale(np0, np0, true, true)
+ pp1x = g.Points[len(g.Points)-4].X
+ if g.hinting != font.HintingNone {
+ if len(program) != 0 {
+ err := g.hinter.run(
+ program,
+ g.Points[np0:],
+ g.Unhinted[np0:],
+ g.InFontUnits[np0:],
+ g.Ends[ne0:],
+ )
+ if err != nil {
+ return err
+ }
+ }
+ // Drop the four phantom points.
+ g.InFontUnits = g.InFontUnits[:len(g.InFontUnits)-4]
+ g.Unhinted = g.Unhinted[:len(g.Unhinted)-4]
+ }
+ if useMyMetrics {
+ copy(g.phantomPoints[:], g.Points[len(g.Points)-4:])
+ }
+ g.Points = g.Points[:len(g.Points)-4]
+ if np0 != 0 {
+ // The hinting program expects the []Ends values to be indexed
+ // relative to the inner glyph, not the outer glyph, so we delay
+ // adding np0 until after the hinting program (if any) has run.
+ for i := ne0; i < len(g.Ends); i++ {
+ g.Ends[i] += np0
+ }
+ }
+ }
+ if useMyMetrics && !g.metricsSet {
+ g.metricsSet = true
+ g.pp1x = pp1x
+ }
+ return nil
+}
+
+// loadOffset is the initial offset for loadSimple and loadCompound. The first
+// 10 bytes are the number of contours and the bounding box.
+const loadOffset = 10
+
+func (g *GlyphBuf) loadSimple(glyf []byte, ne int) (program []byte) {
+ offset := loadOffset
+ for i := 0; i < ne; i++ {
+ g.Ends = append(g.Ends, 1+int(u16(glyf, offset)))
+ offset += 2
+ }
+
+ // Note the TrueType hinting instructions.
+ instrLen := int(u16(glyf, offset))
+ offset += 2
+ program = glyf[offset : offset+instrLen]
+ offset += instrLen
+
+ np0 := len(g.Points)
+ np1 := np0 + int(g.Ends[len(g.Ends)-1])
+
+ // Decode the flags.
+ for i := np0; i < np1; {
+ c := uint32(glyf[offset])
+ offset++
+ g.Points = append(g.Points, Point{Flags: c})
+ i++
+ if c&flagRepeat != 0 {
+ count := glyf[offset]
+ offset++
+ for ; count > 0; count-- {
+ g.Points = append(g.Points, Point{Flags: c})
+ i++
+ }
+ }
+ }
+
+ // Decode the co-ordinates.
+ var x int16
+ for i := np0; i < np1; i++ {
+ f := g.Points[i].Flags
+ if f&flagXShortVector != 0 {
+ dx := int16(glyf[offset])
+ offset++
+ if f&flagPositiveXShortVector == 0 {
+ x -= dx
+ } else {
+ x += dx
+ }
+ } else if f&flagThisXIsSame == 0 {
+ x += int16(u16(glyf, offset))
+ offset += 2
+ }
+ g.Points[i].X = fixed.Int26_6(x)
+ }
+ var y int16
+ for i := np0; i < np1; i++ {
+ f := g.Points[i].Flags
+ if f&flagYShortVector != 0 {
+ dy := int16(glyf[offset])
+ offset++
+ if f&flagPositiveYShortVector == 0 {
+ y -= dy
+ } else {
+ y += dy
+ }
+ } else if f&flagThisYIsSame == 0 {
+ y += int16(u16(glyf, offset))
+ offset += 2
+ }
+ g.Points[i].Y = fixed.Int26_6(y)
+ }
+
+ return program
+}
+
+func (g *GlyphBuf) loadCompound(recursion uint32, uhm HMetric, i Index,
+ glyf []byte, useMyMetrics bool) error {
+
+ // Flags for decoding a compound glyph. These flags are documented at
+ // http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html.
+ const (
+ flagArg1And2AreWords = 1 << iota
+ flagArgsAreXYValues
+ flagRoundXYToGrid
+ flagWeHaveAScale
+ flagUnused
+ flagMoreComponents
+ flagWeHaveAnXAndYScale
+ flagWeHaveATwoByTwo
+ flagWeHaveInstructions
+ flagUseMyMetrics
+ flagOverlapCompound
+ )
+ np0, ne0 := len(g.Points), len(g.Ends)
+ offset := loadOffset
+ for {
+ flags := u16(glyf, offset)
+ component := Index(u16(glyf, offset+2))
+ dx, dy, transform, hasTransform := fixed.Int26_6(0), fixed.Int26_6(0), [4]int16{}, false
+ if flags&flagArg1And2AreWords != 0 {
+ dx = fixed.Int26_6(int16(u16(glyf, offset+4)))
+ dy = fixed.Int26_6(int16(u16(glyf, offset+6)))
+ offset += 8
+ } else {
+ dx = fixed.Int26_6(int16(int8(glyf[offset+4])))
+ dy = fixed.Int26_6(int16(int8(glyf[offset+5])))
+ offset += 6
+ }
+ if flags&flagArgsAreXYValues == 0 {
+ return UnsupportedError("compound glyph transform vector")
+ }
+ if flags&(flagWeHaveAScale|flagWeHaveAnXAndYScale|flagWeHaveATwoByTwo) != 0 {
+ hasTransform = true
+ switch {
+ case flags&flagWeHaveAScale != 0:
+ transform[0] = int16(u16(glyf, offset+0))
+ transform[3] = transform[0]
+ offset += 2
+ case flags&flagWeHaveAnXAndYScale != 0:
+ transform[0] = int16(u16(glyf, offset+0))
+ transform[3] = int16(u16(glyf, offset+2))
+ offset += 4
+ case flags&flagWeHaveATwoByTwo != 0:
+ transform[0] = int16(u16(glyf, offset+0))
+ transform[1] = int16(u16(glyf, offset+2))
+ transform[2] = int16(u16(glyf, offset+4))
+ transform[3] = int16(u16(glyf, offset+6))
+ offset += 8
+ }
+ }
+ savedPP := g.phantomPoints
+ np0 := len(g.Points)
+ componentUMM := useMyMetrics && (flags&flagUseMyMetrics != 0)
+ if err := g.load(recursion+1, component, componentUMM); err != nil {
+ return err
+ }
+ if flags&flagUseMyMetrics == 0 {
+ g.phantomPoints = savedPP
+ }
+ if hasTransform {
+ for j := np0; j < len(g.Points); j++ {
+ p := &g.Points[j]
+ newX := 0 +
+ fixed.Int26_6((int64(p.X)*int64(transform[0])+1<<13)>>14) +
+ fixed.Int26_6((int64(p.Y)*int64(transform[2])+1<<13)>>14)
+ newY := 0 +
+ fixed.Int26_6((int64(p.X)*int64(transform[1])+1<<13)>>14) +
+ fixed.Int26_6((int64(p.Y)*int64(transform[3])+1<<13)>>14)
+ p.X, p.Y = newX, newY
+ }
+ }
+ dx = g.font.scale(g.scale * dx)
+ dy = g.font.scale(g.scale * dy)
+ if flags&flagRoundXYToGrid != 0 {
+ dx = (dx + 32) &^ 63
+ dy = (dy + 32) &^ 63
+ }
+ for j := np0; j < len(g.Points); j++ {
+ p := &g.Points[j]
+ p.X += dx
+ p.Y += dy
+ }
+ // TODO: also adjust g.InFontUnits and g.Unhinted?
+ if flags&flagMoreComponents == 0 {
+ break
+ }
+ }
+
+ instrLen := 0
+ if g.hinting != font.HintingNone && offset+2 <= len(glyf) {
+ instrLen = int(u16(glyf, offset))
+ offset += 2
+ }
+
+ g.addPhantomsAndScale(np0, len(g.Points), false, instrLen > 0)
+ points, ends := g.Points[np0:], g.Ends[ne0:]
+ g.Points = g.Points[:len(g.Points)-4]
+ for j := range points {
+ points[j].Flags &^= flagTouchedX | flagTouchedY
+ }
+
+ if instrLen == 0 {
+ if !g.metricsSet {
+ copy(g.phantomPoints[:], points[len(points)-4:])
+ }
+ return nil
+ }
+
+ // Hint the compound glyph.
+ program := glyf[offset : offset+instrLen]
+ // Temporarily adjust the ends to be relative to this compound glyph.
+ if np0 != 0 {
+ for i := range ends {
+ ends[i] -= np0
+ }
+ }
+ // Hinting instructions of a composite glyph completely refer to the
+ // (already) hinted subglyphs.
+ g.tmp = append(g.tmp[:0], points...)
+ if err := g.hinter.run(program, points, g.tmp, g.tmp, ends); err != nil {
+ return err
+ }
+ if np0 != 0 {
+ for i := range ends {
+ ends[i] += np0
+ }
+ }
+ if !g.metricsSet {
+ copy(g.phantomPoints[:], points[len(points)-4:])
+ }
+ return nil
+}
+
+func (g *GlyphBuf) addPhantomsAndScale(np0, np1 int, simple, adjust bool) {
+ // Add the four phantom points.
+ g.Points = append(g.Points, g.phantomPoints[:]...)
+ // Scale the points.
+ if simple && g.hinting != font.HintingNone {
+ g.InFontUnits = append(g.InFontUnits, g.Points[np1:]...)
+ }
+ for i := np1; i < len(g.Points); i++ {
+ p := &g.Points[i]
+ p.X = g.font.scale(g.scale * p.X)
+ p.Y = g.font.scale(g.scale * p.Y)
+ }
+ if g.hinting == font.HintingNone {
+ return
+ }
+ // Round the 1st phantom point to the grid, shifting all other points equally.
+ // Note that "all other points" starts from np0, not np1.
+ // TODO: delete this adjustment and the np0/np1 distinction, when
+ // we update the compatibility tests to C Freetype 2.5.3.
+ // See http://git.savannah.gnu.org/cgit/freetype/freetype2.git/commit/?id=05c786d990390a7ca18e62962641dac740bacb06
+ if adjust {
+ pp1x := g.Points[len(g.Points)-4].X
+ if dx := ((pp1x + 32) &^ 63) - pp1x; dx != 0 {
+ for i := np0; i < len(g.Points); i++ {
+ g.Points[i].X += dx
+ }
+ }
+ }
+ if simple {
+ g.Unhinted = append(g.Unhinted, g.Points[np1:]...)
+ }
+ // Round the 2nd and 4th phantom point to the grid.
+ p := &g.Points[len(g.Points)-3]
+ p.X = (p.X + 32) &^ 63
+ p = &g.Points[len(g.Points)-1]
+ p.Y = (p.Y + 32) &^ 63
+}
diff --git a/vendor/github.com/golang/freetype/truetype/hint.go b/vendor/github.com/golang/freetype/truetype/hint.go
new file mode 100644
index 0000000..13f785b
--- /dev/null
+++ b/vendor/github.com/golang/freetype/truetype/hint.go
@@ -0,0 +1,1770 @@
+// Copyright 2012 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+package truetype
+
+// This file implements a Truetype bytecode interpreter.
+// The opcodes are described at https://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html
+
+import (
+ "errors"
+ "math"
+
+ "golang.org/x/image/math/fixed"
+)
+
+const (
+ twilightZone = 0
+ glyphZone = 1
+ numZone = 2
+)
+
+type pointType uint32
+
+const (
+ current pointType = 0
+ unhinted pointType = 1
+ inFontUnits pointType = 2
+ numPointType = 3
+)
+
+// callStackEntry is a bytecode call stack entry.
+type callStackEntry struct {
+ program []byte
+ pc int
+ loopCount int32
+}
+
+// hinter implements bytecode hinting. A hinter can be re-used to hint a series
+// of glyphs from a Font.
+type hinter struct {
+ stack, store []int32
+
+ // functions is a map from function number to bytecode.
+ functions map[int32][]byte
+
+ // font and scale are the font and scale last used for this hinter.
+ // Changing the font will require running the new font's fpgm bytecode.
+ // Changing either will require running the font's prep bytecode.
+ font *Font
+ scale fixed.Int26_6
+
+ // gs and defaultGS are the current and default graphics state. The
+ // default graphics state is the global default graphics state after
+ // the font's fpgm and prep programs have been run.
+ gs, defaultGS graphicsState
+
+ // points and ends are the twilight zone's points, glyph's points
+ // and glyph's contour boundaries.
+ points [numZone][numPointType][]Point
+ ends []int
+
+ // scaledCVT is the lazily initialized scaled Control Value Table.
+ scaledCVTInitialized bool
+ scaledCVT []fixed.Int26_6
+}
+
+// graphicsState is described at https://developer.apple.com/fonts/TTRefMan/RM04/Chap4.html
+type graphicsState struct {
+ // Projection vector, freedom vector and dual projection vector.
+ pv, fv, dv [2]f2dot14
+ // Reference points and zone pointers.
+ rp, zp [3]int32
+ // Control Value / Single Width Cut-In.
+ controlValueCutIn, singleWidthCutIn, singleWidth fixed.Int26_6
+ // Delta base / shift.
+ deltaBase, deltaShift int32
+ // Minimum distance.
+ minDist fixed.Int26_6
+ // Loop count.
+ loop int32
+ // Rounding policy.
+ roundPeriod, roundPhase, roundThreshold fixed.Int26_6
+ roundSuper45 bool
+ // Auto-flip.
+ autoFlip bool
+}
+
+var globalDefaultGS = graphicsState{
+ pv: [2]f2dot14{0x4000, 0}, // Unit vector along the X axis.
+ fv: [2]f2dot14{0x4000, 0},
+ dv: [2]f2dot14{0x4000, 0},
+ zp: [3]int32{1, 1, 1},
+ controlValueCutIn: (17 << 6) / 16, // 17/16 as a fixed.Int26_6.
+ deltaBase: 9,
+ deltaShift: 3,
+ minDist: 1 << 6, // 1 as a fixed.Int26_6.
+ loop: 1,
+ roundPeriod: 1 << 6, // 1 as a fixed.Int26_6.
+ roundThreshold: 1 << 5, // 1/2 as a fixed.Int26_6.
+ roundSuper45: false,
+ autoFlip: true,
+}
+
+func resetTwilightPoints(f *Font, p []Point) []Point {
+ if n := int(f.maxTwilightPoints) + 4; n <= cap(p) {
+ p = p[:n]
+ for i := range p {
+ p[i] = Point{}
+ }
+ } else {
+ p = make([]Point, n)
+ }
+ return p
+}
+
+func (h *hinter) init(f *Font, scale fixed.Int26_6) error {
+ h.points[twilightZone][0] = resetTwilightPoints(f, h.points[twilightZone][0])
+ h.points[twilightZone][1] = resetTwilightPoints(f, h.points[twilightZone][1])
+ h.points[twilightZone][2] = resetTwilightPoints(f, h.points[twilightZone][2])
+
+ rescale := h.scale != scale
+ if h.font != f {
+ h.font, rescale = f, true
+ if h.functions == nil {
+ h.functions = make(map[int32][]byte)
+ } else {
+ for k := range h.functions {
+ delete(h.functions, k)
+ }
+ }
+
+ if x := int(f.maxStackElements); x > len(h.stack) {
+ x += 255
+ x &^= 255
+ h.stack = make([]int32, x)
+ }
+ if x := int(f.maxStorage); x > len(h.store) {
+ x += 15
+ x &^= 15
+ h.store = make([]int32, x)
+ }
+ if len(f.fpgm) != 0 {
+ if err := h.run(f.fpgm, nil, nil, nil, nil); err != nil {
+ return err
+ }
+ }
+ }
+
+ if rescale {
+ h.scale = scale
+ h.scaledCVTInitialized = false
+
+ h.defaultGS = globalDefaultGS
+
+ if len(f.prep) != 0 {
+ if err := h.run(f.prep, nil, nil, nil, nil); err != nil {
+ return err
+ }
+ h.defaultGS = h.gs
+ // The MS rasterizer doesn't allow the following graphics state
+ // variables to be modified by the CVT program.
+ h.defaultGS.pv = globalDefaultGS.pv
+ h.defaultGS.fv = globalDefaultGS.fv
+ h.defaultGS.dv = globalDefaultGS.dv
+ h.defaultGS.rp = globalDefaultGS.rp
+ h.defaultGS.zp = globalDefaultGS.zp
+ h.defaultGS.loop = globalDefaultGS.loop
+ }
+ }
+ return nil
+}
+
+func (h *hinter) run(program []byte, pCurrent, pUnhinted, pInFontUnits []Point, ends []int) error {
+ h.gs = h.defaultGS
+ h.points[glyphZone][current] = pCurrent
+ h.points[glyphZone][unhinted] = pUnhinted
+ h.points[glyphZone][inFontUnits] = pInFontUnits
+ h.ends = ends
+
+ if len(program) > 50000 {
+ return errors.New("truetype: hinting: too many instructions")
+ }
+ var (
+ steps, pc, top int
+ opcode uint8
+
+ callStack [32]callStackEntry
+ callStackTop int
+ )
+
+ for 0 <= pc && pc < len(program) {
+ steps++
+ if steps == 100000 {
+ return errors.New("truetype: hinting: too many steps")
+ }
+ opcode = program[pc]
+ if top < int(popCount[opcode]) {
+ return errors.New("truetype: hinting: stack underflow")
+ }
+ switch opcode {
+
+ case opSVTCA0:
+ h.gs.pv = [2]f2dot14{0, 0x4000}
+ h.gs.fv = [2]f2dot14{0, 0x4000}
+ h.gs.dv = [2]f2dot14{0, 0x4000}
+
+ case opSVTCA1:
+ h.gs.pv = [2]f2dot14{0x4000, 0}
+ h.gs.fv = [2]f2dot14{0x4000, 0}
+ h.gs.dv = [2]f2dot14{0x4000, 0}
+
+ case opSPVTCA0:
+ h.gs.pv = [2]f2dot14{0, 0x4000}
+ h.gs.dv = [2]f2dot14{0, 0x4000}
+
+ case opSPVTCA1:
+ h.gs.pv = [2]f2dot14{0x4000, 0}
+ h.gs.dv = [2]f2dot14{0x4000, 0}
+
+ case opSFVTCA0:
+ h.gs.fv = [2]f2dot14{0, 0x4000}
+
+ case opSFVTCA1:
+ h.gs.fv = [2]f2dot14{0x4000, 0}
+
+ case opSPVTL0, opSPVTL1, opSFVTL0, opSFVTL1:
+ top -= 2
+ p1 := h.point(0, current, h.stack[top+0])
+ p2 := h.point(0, current, h.stack[top+1])
+ if p1 == nil || p2 == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ dx := f2dot14(p1.X - p2.X)
+ dy := f2dot14(p1.Y - p2.Y)
+ if dx == 0 && dy == 0 {
+ dx = 0x4000
+ } else if opcode&1 != 0 {
+ // Counter-clockwise rotation.
+ dx, dy = -dy, dx
+ }
+ v := normalize(dx, dy)
+ if opcode < opSFVTL0 {
+ h.gs.pv = v
+ h.gs.dv = v
+ } else {
+ h.gs.fv = v
+ }
+
+ case opSPVFS:
+ top -= 2
+ h.gs.pv = normalize(f2dot14(h.stack[top]), f2dot14(h.stack[top+1]))
+ h.gs.dv = h.gs.pv
+
+ case opSFVFS:
+ top -= 2
+ h.gs.fv = normalize(f2dot14(h.stack[top]), f2dot14(h.stack[top+1]))
+
+ case opGPV:
+ if top+1 >= len(h.stack) {
+ return errors.New("truetype: hinting: stack overflow")
+ }
+ h.stack[top+0] = int32(h.gs.pv[0])
+ h.stack[top+1] = int32(h.gs.pv[1])
+ top += 2
+
+ case opGFV:
+ if top+1 >= len(h.stack) {
+ return errors.New("truetype: hinting: stack overflow")
+ }
+ h.stack[top+0] = int32(h.gs.fv[0])
+ h.stack[top+1] = int32(h.gs.fv[1])
+ top += 2
+
+ case opSFVTPV:
+ h.gs.fv = h.gs.pv
+
+ case opISECT:
+ top -= 5
+ p := h.point(2, current, h.stack[top+0])
+ a0 := h.point(1, current, h.stack[top+1])
+ a1 := h.point(1, current, h.stack[top+2])
+ b0 := h.point(0, current, h.stack[top+3])
+ b1 := h.point(0, current, h.stack[top+4])
+ if p == nil || a0 == nil || a1 == nil || b0 == nil || b1 == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+
+ dbx := b1.X - b0.X
+ dby := b1.Y - b0.Y
+ dax := a1.X - a0.X
+ day := a1.Y - a0.Y
+ dx := b0.X - a0.X
+ dy := b0.Y - a0.Y
+ discriminant := mulDiv(int64(dax), int64(-dby), 0x40) +
+ mulDiv(int64(day), int64(dbx), 0x40)
+ dotProduct := mulDiv(int64(dax), int64(dbx), 0x40) +
+ mulDiv(int64(day), int64(dby), 0x40)
+ // The discriminant above is actually a cross product of vectors
+ // da and db. Together with the dot product, they can be used as
+ // surrogates for sine and cosine of the angle between the vectors.
+ // Indeed,
+ // dotproduct = |da||db|cos(angle)
+ // discriminant = |da||db|sin(angle)
+ // We use these equations to reject grazing intersections by
+ // thresholding abs(tan(angle)) at 1/19, corresponding to 3 degrees.
+ absDisc, absDotP := discriminant, dotProduct
+ if absDisc < 0 {
+ absDisc = -absDisc
+ }
+ if absDotP < 0 {
+ absDotP = -absDotP
+ }
+ if 19*absDisc > absDotP {
+ val := mulDiv(int64(dx), int64(-dby), 0x40) +
+ mulDiv(int64(dy), int64(dbx), 0x40)
+ rx := mulDiv(val, int64(dax), discriminant)
+ ry := mulDiv(val, int64(day), discriminant)
+ p.X = a0.X + fixed.Int26_6(rx)
+ p.Y = a0.Y + fixed.Int26_6(ry)
+ } else {
+ p.X = (a0.X + a1.X + b0.X + b1.X) / 4
+ p.Y = (a0.Y + a1.Y + b0.Y + b1.Y) / 4
+ }
+ p.Flags |= flagTouchedX | flagTouchedY
+
+ case opSRP0, opSRP1, opSRP2:
+ top--
+ h.gs.rp[opcode-opSRP0] = h.stack[top]
+
+ case opSZP0, opSZP1, opSZP2:
+ top--
+ h.gs.zp[opcode-opSZP0] = h.stack[top]
+
+ case opSZPS:
+ top--
+ h.gs.zp[0] = h.stack[top]
+ h.gs.zp[1] = h.stack[top]
+ h.gs.zp[2] = h.stack[top]
+
+ case opSLOOP:
+ top--
+ // https://developer.apple.com/fonts/TrueType-Reference-Manual/RM05/Chap5.html#SLOOP
+ // says that "Setting the loop variable to zero is an error". In
+ // theory, the inequality on the next line should be "<=" instead
+ // of "<". In practice, some font files' bytecode, such as the '2'
+ // glyph in the DejaVuSansMono.ttf that comes with Ubuntu 14.04,
+ // issue SLOOP with a zero on top of the stack. Just like the C
+ // Freetype code, we allow the zero.
+ if h.stack[top] < 0 {
+ return errors.New("truetype: hinting: invalid data")
+ }
+ h.gs.loop = h.stack[top]
+
+ case opRTG:
+ h.gs.roundPeriod = 1 << 6
+ h.gs.roundPhase = 0
+ h.gs.roundThreshold = 1 << 5
+ h.gs.roundSuper45 = false
+
+ case opRTHG:
+ h.gs.roundPeriod = 1 << 6
+ h.gs.roundPhase = 1 << 5
+ h.gs.roundThreshold = 1 << 5
+ h.gs.roundSuper45 = false
+
+ case opSMD:
+ top--
+ h.gs.minDist = fixed.Int26_6(h.stack[top])
+
+ case opELSE:
+ opcode = 1
+ goto ifelse
+
+ case opJMPR:
+ top--
+ pc += int(h.stack[top])
+ continue
+
+ case opSCVTCI:
+ top--
+ h.gs.controlValueCutIn = fixed.Int26_6(h.stack[top])
+
+ case opSSWCI:
+ top--
+ h.gs.singleWidthCutIn = fixed.Int26_6(h.stack[top])
+
+ case opSSW:
+ top--
+ h.gs.singleWidth = h.font.scale(h.scale * fixed.Int26_6(h.stack[top]))
+
+ case opDUP:
+ if top >= len(h.stack) {
+ return errors.New("truetype: hinting: stack overflow")
+ }
+ h.stack[top] = h.stack[top-1]
+ top++
+
+ case opPOP:
+ top--
+
+ case opCLEAR:
+ top = 0
+
+ case opSWAP:
+ h.stack[top-1], h.stack[top-2] = h.stack[top-2], h.stack[top-1]
+
+ case opDEPTH:
+ if top >= len(h.stack) {
+ return errors.New("truetype: hinting: stack overflow")
+ }
+ h.stack[top] = int32(top)
+ top++
+
+ case opCINDEX, opMINDEX:
+ x := int(h.stack[top-1])
+ if x <= 0 || x >= top {
+ return errors.New("truetype: hinting: invalid data")
+ }
+ h.stack[top-1] = h.stack[top-1-x]
+ if opcode == opMINDEX {
+ copy(h.stack[top-1-x:top-1], h.stack[top-x:top])
+ top--
+ }
+
+ case opALIGNPTS:
+ top -= 2
+ p := h.point(1, current, h.stack[top])
+ q := h.point(0, current, h.stack[top+1])
+ if p == nil || q == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ d := dotProduct(fixed.Int26_6(q.X-p.X), fixed.Int26_6(q.Y-p.Y), h.gs.pv) / 2
+ h.move(p, +d, true)
+ h.move(q, -d, true)
+
+ case opUTP:
+ top--
+ p := h.point(0, current, h.stack[top])
+ if p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ p.Flags &^= flagTouchedX | flagTouchedY
+
+ case opLOOPCALL, opCALL:
+ if callStackTop >= len(callStack) {
+ return errors.New("truetype: hinting: call stack overflow")
+ }
+ top--
+ f, ok := h.functions[h.stack[top]]
+ if !ok {
+ return errors.New("truetype: hinting: undefined function")
+ }
+ callStack[callStackTop] = callStackEntry{program, pc, 1}
+ if opcode == opLOOPCALL {
+ top--
+ if h.stack[top] == 0 {
+ break
+ }
+ callStack[callStackTop].loopCount = h.stack[top]
+ }
+ callStackTop++
+ program, pc = f, 0
+ continue
+
+ case opFDEF:
+ // Save all bytecode up until the next ENDF.
+ startPC := pc + 1
+ fdefloop:
+ for {
+ pc++
+ if pc >= len(program) {
+ return errors.New("truetype: hinting: unbalanced FDEF")
+ }
+ switch program[pc] {
+ case opFDEF:
+ return errors.New("truetype: hinting: nested FDEF")
+ case opENDF:
+ top--
+ h.functions[h.stack[top]] = program[startPC : pc+1]
+ break fdefloop
+ default:
+ var ok bool
+ pc, ok = skipInstructionPayload(program, pc)
+ if !ok {
+ return errors.New("truetype: hinting: unbalanced FDEF")
+ }
+ }
+ }
+
+ case opENDF:
+ if callStackTop == 0 {
+ return errors.New("truetype: hinting: call stack underflow")
+ }
+ callStackTop--
+ callStack[callStackTop].loopCount--
+ if callStack[callStackTop].loopCount != 0 {
+ callStackTop++
+ pc = 0
+ continue
+ }
+ program, pc = callStack[callStackTop].program, callStack[callStackTop].pc
+
+ case opMDAP0, opMDAP1:
+ top--
+ i := h.stack[top]
+ p := h.point(0, current, i)
+ if p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ distance := fixed.Int26_6(0)
+ if opcode == opMDAP1 {
+ distance = dotProduct(p.X, p.Y, h.gs.pv)
+ // TODO: metrics compensation.
+ distance = h.round(distance) - distance
+ }
+ h.move(p, distance, true)
+ h.gs.rp[0] = i
+ h.gs.rp[1] = i
+
+ case opIUP0, opIUP1:
+ iupY, mask := opcode == opIUP0, uint32(flagTouchedX)
+ if iupY {
+ mask = flagTouchedY
+ }
+ prevEnd := 0
+ for _, end := range h.ends {
+ for i := prevEnd; i < end; i++ {
+ for i < end && h.points[glyphZone][current][i].Flags&mask == 0 {
+ i++
+ }
+ if i == end {
+ break
+ }
+ firstTouched, curTouched := i, i
+ i++
+ for ; i < end; i++ {
+ if h.points[glyphZone][current][i].Flags&mask != 0 {
+ h.iupInterp(iupY, curTouched+1, i-1, curTouched, i)
+ curTouched = i
+ }
+ }
+ if curTouched == firstTouched {
+ h.iupShift(iupY, prevEnd, end, curTouched)
+ } else {
+ h.iupInterp(iupY, curTouched+1, end-1, curTouched, firstTouched)
+ if firstTouched > 0 {
+ h.iupInterp(iupY, prevEnd, firstTouched-1, curTouched, firstTouched)
+ }
+ }
+ }
+ prevEnd = end
+ }
+
+ case opSHP0, opSHP1:
+ if top < int(h.gs.loop) {
+ return errors.New("truetype: hinting: stack underflow")
+ }
+ _, _, d, ok := h.displacement(opcode&1 == 0)
+ if !ok {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ for ; h.gs.loop != 0; h.gs.loop-- {
+ top--
+ p := h.point(2, current, h.stack[top])
+ if p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ h.move(p, d, true)
+ }
+ h.gs.loop = 1
+
+ case opSHC0, opSHC1:
+ top--
+ zonePointer, i, d, ok := h.displacement(opcode&1 == 0)
+ if !ok {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ if h.gs.zp[2] == 0 {
+ // TODO: implement this when we have a glyph that does this.
+ return errors.New("hinting: unimplemented SHC instruction")
+ }
+ contour := h.stack[top]
+ if contour < 0 || len(ends) <= int(contour) {
+ return errors.New("truetype: hinting: contour out of range")
+ }
+ j0, j1 := int32(0), int32(h.ends[contour])
+ if contour > 0 {
+ j0 = int32(h.ends[contour-1])
+ }
+ move := h.gs.zp[zonePointer] != h.gs.zp[2]
+ for j := j0; j < j1; j++ {
+ if move || j != i {
+ h.move(h.point(2, current, j), d, true)
+ }
+ }
+
+ case opSHZ0, opSHZ1:
+ top--
+ zonePointer, i, d, ok := h.displacement(opcode&1 == 0)
+ if !ok {
+ return errors.New("truetype: hinting: point out of range")
+ }
+
+ // As per C Freetype, SHZ doesn't move the phantom points, or mark
+ // the points as touched.
+ limit := int32(len(h.points[h.gs.zp[2]][current]))
+ if h.gs.zp[2] == glyphZone {
+ limit -= 4
+ }
+ for j := int32(0); j < limit; j++ {
+ if i != j || h.gs.zp[zonePointer] != h.gs.zp[2] {
+ h.move(h.point(2, current, j), d, false)
+ }
+ }
+
+ case opSHPIX:
+ top--
+ d := fixed.Int26_6(h.stack[top])
+ if top < int(h.gs.loop) {
+ return errors.New("truetype: hinting: stack underflow")
+ }
+ for ; h.gs.loop != 0; h.gs.loop-- {
+ top--
+ p := h.point(2, current, h.stack[top])
+ if p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ h.move(p, d, true)
+ }
+ h.gs.loop = 1
+
+ case opIP:
+ if top < int(h.gs.loop) {
+ return errors.New("truetype: hinting: stack underflow")
+ }
+ pointType := inFontUnits
+ twilight := h.gs.zp[0] == 0 || h.gs.zp[1] == 0 || h.gs.zp[2] == 0
+ if twilight {
+ pointType = unhinted
+ }
+ p := h.point(1, pointType, h.gs.rp[2])
+ oldP := h.point(0, pointType, h.gs.rp[1])
+ oldRange := dotProduct(p.X-oldP.X, p.Y-oldP.Y, h.gs.dv)
+
+ p = h.point(1, current, h.gs.rp[2])
+ curP := h.point(0, current, h.gs.rp[1])
+ curRange := dotProduct(p.X-curP.X, p.Y-curP.Y, h.gs.pv)
+ for ; h.gs.loop != 0; h.gs.loop-- {
+ top--
+ i := h.stack[top]
+ p = h.point(2, pointType, i)
+ oldDist := dotProduct(p.X-oldP.X, p.Y-oldP.Y, h.gs.dv)
+ p = h.point(2, current, i)
+ curDist := dotProduct(p.X-curP.X, p.Y-curP.Y, h.gs.pv)
+ newDist := fixed.Int26_6(0)
+ if oldDist != 0 {
+ if oldRange != 0 {
+ newDist = fixed.Int26_6(mulDiv(int64(oldDist), int64(curRange), int64(oldRange)))
+ } else {
+ newDist = -oldDist
+ }
+ }
+ h.move(p, newDist-curDist, true)
+ }
+ h.gs.loop = 1
+
+ case opMSIRP0, opMSIRP1:
+ top -= 2
+ i := h.stack[top]
+ distance := fixed.Int26_6(h.stack[top+1])
+
+ // TODO: special case h.gs.zp[1] == 0 in C Freetype.
+ ref := h.point(0, current, h.gs.rp[0])
+ p := h.point(1, current, i)
+ if ref == nil || p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ curDist := dotProduct(p.X-ref.X, p.Y-ref.Y, h.gs.pv)
+
+ // Set-RP0 bit.
+ if opcode == opMSIRP1 {
+ h.gs.rp[0] = i
+ }
+ h.gs.rp[1] = h.gs.rp[0]
+ h.gs.rp[2] = i
+
+ // Move the point.
+ h.move(p, distance-curDist, true)
+
+ case opALIGNRP:
+ if top < int(h.gs.loop) {
+ return errors.New("truetype: hinting: stack underflow")
+ }
+ ref := h.point(0, current, h.gs.rp[0])
+ if ref == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ for ; h.gs.loop != 0; h.gs.loop-- {
+ top--
+ p := h.point(1, current, h.stack[top])
+ if p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ h.move(p, -dotProduct(p.X-ref.X, p.Y-ref.Y, h.gs.pv), true)
+ }
+ h.gs.loop = 1
+
+ case opRTDG:
+ h.gs.roundPeriod = 1 << 5
+ h.gs.roundPhase = 0
+ h.gs.roundThreshold = 1 << 4
+ h.gs.roundSuper45 = false
+
+ case opMIAP0, opMIAP1:
+ top -= 2
+ i := h.stack[top]
+ distance := h.getScaledCVT(h.stack[top+1])
+ if h.gs.zp[0] == 0 {
+ p := h.point(0, unhinted, i)
+ q := h.point(0, current, i)
+ p.X = fixed.Int26_6((int64(distance) * int64(h.gs.fv[0])) >> 14)
+ p.Y = fixed.Int26_6((int64(distance) * int64(h.gs.fv[1])) >> 14)
+ *q = *p
+ }
+ p := h.point(0, current, i)
+ oldDist := dotProduct(p.X, p.Y, h.gs.pv)
+ if opcode == opMIAP1 {
+ if fabs(distance-oldDist) > h.gs.controlValueCutIn {
+ distance = oldDist
+ }
+ // TODO: metrics compensation.
+ distance = h.round(distance)
+ }
+ h.move(p, distance-oldDist, true)
+ h.gs.rp[0] = i
+ h.gs.rp[1] = i
+
+ case opNPUSHB:
+ opcode = 0
+ goto push
+
+ case opNPUSHW:
+ opcode = 0x80
+ goto push
+
+ case opWS:
+ top -= 2
+ i := int(h.stack[top])
+ if i < 0 || len(h.store) <= i {
+ return errors.New("truetype: hinting: invalid data")
+ }
+ h.store[i] = h.stack[top+1]
+
+ case opRS:
+ i := int(h.stack[top-1])
+ if i < 0 || len(h.store) <= i {
+ return errors.New("truetype: hinting: invalid data")
+ }
+ h.stack[top-1] = h.store[i]
+
+ case opWCVTP:
+ top -= 2
+ h.setScaledCVT(h.stack[top], fixed.Int26_6(h.stack[top+1]))
+
+ case opRCVT:
+ h.stack[top-1] = int32(h.getScaledCVT(h.stack[top-1]))
+
+ case opGC0, opGC1:
+ i := h.stack[top-1]
+ if opcode == opGC0 {
+ p := h.point(2, current, i)
+ h.stack[top-1] = int32(dotProduct(p.X, p.Y, h.gs.pv))
+ } else {
+ p := h.point(2, unhinted, i)
+ // Using dv as per C Freetype.
+ h.stack[top-1] = int32(dotProduct(p.X, p.Y, h.gs.dv))
+ }
+
+ case opSCFS:
+ top -= 2
+ i := h.stack[top]
+ p := h.point(2, current, i)
+ if p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ c := dotProduct(p.X, p.Y, h.gs.pv)
+ h.move(p, fixed.Int26_6(h.stack[top+1])-c, true)
+ if h.gs.zp[2] != 0 {
+ break
+ }
+ q := h.point(2, unhinted, i)
+ if q == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ q.X = p.X
+ q.Y = p.Y
+
+ case opMD0, opMD1:
+ top--
+ pt, v, scale := pointType(0), [2]f2dot14{}, false
+ if opcode == opMD0 {
+ pt = current
+ v = h.gs.pv
+ } else if h.gs.zp[0] == 0 || h.gs.zp[1] == 0 {
+ pt = unhinted
+ v = h.gs.dv
+ } else {
+ pt = inFontUnits
+ v = h.gs.dv
+ scale = true
+ }
+ p := h.point(0, pt, h.stack[top-1])
+ q := h.point(1, pt, h.stack[top])
+ if p == nil || q == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ d := int32(dotProduct(p.X-q.X, p.Y-q.Y, v))
+ if scale {
+ d = int32(int64(d*int32(h.scale)) / int64(h.font.fUnitsPerEm))
+ }
+ h.stack[top-1] = d
+
+ case opMPPEM, opMPS:
+ if top >= len(h.stack) {
+ return errors.New("truetype: hinting: stack overflow")
+ }
+ // For MPS, point size should be irrelevant; we return the PPEM.
+ h.stack[top] = int32(h.scale) >> 6
+ top++
+
+ case opFLIPON, opFLIPOFF:
+ h.gs.autoFlip = opcode == opFLIPON
+
+ case opDEBUG:
+ // No-op.
+
+ case opLT:
+ top--
+ h.stack[top-1] = bool2int32(h.stack[top-1] < h.stack[top])
+
+ case opLTEQ:
+ top--
+ h.stack[top-1] = bool2int32(h.stack[top-1] <= h.stack[top])
+
+ case opGT:
+ top--
+ h.stack[top-1] = bool2int32(h.stack[top-1] > h.stack[top])
+
+ case opGTEQ:
+ top--
+ h.stack[top-1] = bool2int32(h.stack[top-1] >= h.stack[top])
+
+ case opEQ:
+ top--
+ h.stack[top-1] = bool2int32(h.stack[top-1] == h.stack[top])
+
+ case opNEQ:
+ top--
+ h.stack[top-1] = bool2int32(h.stack[top-1] != h.stack[top])
+
+ case opODD, opEVEN:
+ i := h.round(fixed.Int26_6(h.stack[top-1])) >> 6
+ h.stack[top-1] = int32(i&1) ^ int32(opcode-opODD)
+
+ case opIF:
+ top--
+ if h.stack[top] == 0 {
+ opcode = 0
+ goto ifelse
+ }
+
+ case opEIF:
+ // No-op.
+
+ case opAND:
+ top--
+ h.stack[top-1] = bool2int32(h.stack[top-1] != 0 && h.stack[top] != 0)
+
+ case opOR:
+ top--
+ h.stack[top-1] = bool2int32(h.stack[top-1]|h.stack[top] != 0)
+
+ case opNOT:
+ h.stack[top-1] = bool2int32(h.stack[top-1] == 0)
+
+ case opDELTAP1:
+ goto delta
+
+ case opSDB:
+ top--
+ h.gs.deltaBase = h.stack[top]
+
+ case opSDS:
+ top--
+ h.gs.deltaShift = h.stack[top]
+
+ case opADD:
+ top--
+ h.stack[top-1] += h.stack[top]
+
+ case opSUB:
+ top--
+ h.stack[top-1] -= h.stack[top]
+
+ case opDIV:
+ top--
+ if h.stack[top] == 0 {
+ return errors.New("truetype: hinting: division by zero")
+ }
+ h.stack[top-1] = int32(fdiv(fixed.Int26_6(h.stack[top-1]), fixed.Int26_6(h.stack[top])))
+
+ case opMUL:
+ top--
+ h.stack[top-1] = int32(fmul(fixed.Int26_6(h.stack[top-1]), fixed.Int26_6(h.stack[top])))
+
+ case opABS:
+ if h.stack[top-1] < 0 {
+ h.stack[top-1] = -h.stack[top-1]
+ }
+
+ case opNEG:
+ h.stack[top-1] = -h.stack[top-1]
+
+ case opFLOOR:
+ h.stack[top-1] &^= 63
+
+ case opCEILING:
+ h.stack[top-1] += 63
+ h.stack[top-1] &^= 63
+
+ case opROUND00, opROUND01, opROUND10, opROUND11:
+ // The four flavors of opROUND are equivalent. See the comment below on
+ // opNROUND for the rationale.
+ h.stack[top-1] = int32(h.round(fixed.Int26_6(h.stack[top-1])))
+
+ case opNROUND00, opNROUND01, opNROUND10, opNROUND11:
+ // No-op. The spec says to add one of four "compensations for the engine
+ // characteristics", to cater for things like "different dot-size printers".
+ // https://developer.apple.com/fonts/TTRefMan/RM02/Chap2.html#engine_compensation
+ // This code does not implement engine compensation, as we don't expect to
+ // be used to output on dot-matrix printers.
+
+ case opWCVTF:
+ top -= 2
+ h.setScaledCVT(h.stack[top], h.font.scale(h.scale*fixed.Int26_6(h.stack[top+1])))
+
+ case opDELTAP2, opDELTAP3, opDELTAC1, opDELTAC2, opDELTAC3:
+ goto delta
+
+ case opSROUND, opS45ROUND:
+ top--
+ switch (h.stack[top] >> 6) & 0x03 {
+ case 0:
+ h.gs.roundPeriod = 1 << 5
+ case 1, 3:
+ h.gs.roundPeriod = 1 << 6
+ case 2:
+ h.gs.roundPeriod = 1 << 7
+ }
+ h.gs.roundSuper45 = opcode == opS45ROUND
+ if h.gs.roundSuper45 {
+ // The spec says to multiply by √2, but the C Freetype code says 1/√2.
+ // We go with 1/√2.
+ h.gs.roundPeriod *= 46341
+ h.gs.roundPeriod /= 65536
+ }
+ h.gs.roundPhase = h.gs.roundPeriod * fixed.Int26_6((h.stack[top]>>4)&0x03) / 4
+ if x := h.stack[top] & 0x0f; x != 0 {
+ h.gs.roundThreshold = h.gs.roundPeriod * fixed.Int26_6(x-4) / 8
+ } else {
+ h.gs.roundThreshold = h.gs.roundPeriod - 1
+ }
+
+ case opJROT:
+ top -= 2
+ if h.stack[top+1] != 0 {
+ pc += int(h.stack[top])
+ continue
+ }
+
+ case opJROF:
+ top -= 2
+ if h.stack[top+1] == 0 {
+ pc += int(h.stack[top])
+ continue
+ }
+
+ case opROFF:
+ h.gs.roundPeriod = 0
+ h.gs.roundPhase = 0
+ h.gs.roundThreshold = 0
+ h.gs.roundSuper45 = false
+
+ case opRUTG:
+ h.gs.roundPeriod = 1 << 6
+ h.gs.roundPhase = 0
+ h.gs.roundThreshold = 1<<6 - 1
+ h.gs.roundSuper45 = false
+
+ case opRDTG:
+ h.gs.roundPeriod = 1 << 6
+ h.gs.roundPhase = 0
+ h.gs.roundThreshold = 0
+ h.gs.roundSuper45 = false
+
+ case opSANGW, opAA:
+ // These ops are "anachronistic" and no longer used.
+ top--
+
+ case opFLIPPT:
+ if top < int(h.gs.loop) {
+ return errors.New("truetype: hinting: stack underflow")
+ }
+ points := h.points[glyphZone][current]
+ for ; h.gs.loop != 0; h.gs.loop-- {
+ top--
+ i := h.stack[top]
+ if i < 0 || len(points) <= int(i) {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ points[i].Flags ^= flagOnCurve
+ }
+ h.gs.loop = 1
+
+ case opFLIPRGON, opFLIPRGOFF:
+ top -= 2
+ i, j, points := h.stack[top], h.stack[top+1], h.points[glyphZone][current]
+ if i < 0 || len(points) <= int(i) || j < 0 || len(points) <= int(j) {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ for ; i <= j; i++ {
+ if opcode == opFLIPRGON {
+ points[i].Flags |= flagOnCurve
+ } else {
+ points[i].Flags &^= flagOnCurve
+ }
+ }
+
+ case opSCANCTRL:
+ // We do not support dropout control, as we always rasterize grayscale glyphs.
+ top--
+
+ case opSDPVTL0, opSDPVTL1:
+ top -= 2
+ for i := 0; i < 2; i++ {
+ pt := unhinted
+ if i != 0 {
+ pt = current
+ }
+ p := h.point(1, pt, h.stack[top])
+ q := h.point(2, pt, h.stack[top+1])
+ if p == nil || q == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ dx := f2dot14(p.X - q.X)
+ dy := f2dot14(p.Y - q.Y)
+ if dx == 0 && dy == 0 {
+ dx = 0x4000
+ } else if opcode&1 != 0 {
+ // Counter-clockwise rotation.
+ dx, dy = -dy, dx
+ }
+ if i == 0 {
+ h.gs.dv = normalize(dx, dy)
+ } else {
+ h.gs.pv = normalize(dx, dy)
+ }
+ }
+
+ case opGETINFO:
+ res := int32(0)
+ if h.stack[top-1]&(1<<0) != 0 {
+ // Set the engine version. We hard-code this to 35, the same as
+ // the C freetype code, which says that "Version~35 corresponds
+ // to MS rasterizer v.1.7 as used e.g. in Windows~98".
+ res |= 35
+ }
+ if h.stack[top-1]&(1<<5) != 0 {
+ // Set that we support grayscale.
+ res |= 1 << 12
+ }
+ // We set no other bits, as we do not support rotated or stretched glyphs.
+ h.stack[top-1] = res
+
+ case opIDEF:
+ // IDEF is for ancient versions of the bytecode interpreter, and is no longer used.
+ return errors.New("truetype: hinting: unsupported IDEF instruction")
+
+ case opROLL:
+ h.stack[top-1], h.stack[top-3], h.stack[top-2] =
+ h.stack[top-3], h.stack[top-2], h.stack[top-1]
+
+ case opMAX:
+ top--
+ if h.stack[top-1] < h.stack[top] {
+ h.stack[top-1] = h.stack[top]
+ }
+
+ case opMIN:
+ top--
+ if h.stack[top-1] > h.stack[top] {
+ h.stack[top-1] = h.stack[top]
+ }
+
+ case opSCANTYPE:
+ // We do not support dropout control, as we always rasterize grayscale glyphs.
+ top--
+
+ case opINSTCTRL:
+ // TODO: support instruction execution control? It seems rare, and even when
+ // nominally used (e.g. Source Sans Pro), it seems conditional on extreme or
+ // unusual rasterization conditions. For example, the code snippet at
+ // https://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html#INSTCTRL
+ // uses INSTCTRL when grid-fitting a rotated or stretched glyph, but
+ // freetype-go does not support rotated or stretched glyphs.
+ top -= 2
+
+ default:
+ if opcode < opPUSHB000 {
+ return errors.New("truetype: hinting: unrecognized instruction")
+ }
+
+ if opcode < opMDRP00000 {
+ // PUSHxxxx opcode.
+
+ if opcode < opPUSHW000 {
+ opcode -= opPUSHB000 - 1
+ } else {
+ opcode -= opPUSHW000 - 1 - 0x80
+ }
+ goto push
+ }
+
+ if opcode < opMIRP00000 {
+ // MDRPxxxxx opcode.
+
+ top--
+ i := h.stack[top]
+ ref := h.point(0, current, h.gs.rp[0])
+ p := h.point(1, current, i)
+ if ref == nil || p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+
+ oldDist := fixed.Int26_6(0)
+ if h.gs.zp[0] == 0 || h.gs.zp[1] == 0 {
+ p0 := h.point(1, unhinted, i)
+ p1 := h.point(0, unhinted, h.gs.rp[0])
+ oldDist = dotProduct(p0.X-p1.X, p0.Y-p1.Y, h.gs.dv)
+ } else {
+ p0 := h.point(1, inFontUnits, i)
+ p1 := h.point(0, inFontUnits, h.gs.rp[0])
+ oldDist = dotProduct(p0.X-p1.X, p0.Y-p1.Y, h.gs.dv)
+ oldDist = h.font.scale(h.scale * oldDist)
+ }
+
+ // Single-width cut-in test.
+ if x := fabs(oldDist - h.gs.singleWidth); x < h.gs.singleWidthCutIn {
+ if oldDist >= 0 {
+ oldDist = +h.gs.singleWidth
+ } else {
+ oldDist = -h.gs.singleWidth
+ }
+ }
+
+ // Rounding bit.
+ // TODO: metrics compensation.
+ distance := oldDist
+ if opcode&0x04 != 0 {
+ distance = h.round(oldDist)
+ }
+
+ // Minimum distance bit.
+ if opcode&0x08 != 0 {
+ if oldDist >= 0 {
+ if distance < h.gs.minDist {
+ distance = h.gs.minDist
+ }
+ } else {
+ if distance > -h.gs.minDist {
+ distance = -h.gs.minDist
+ }
+ }
+ }
+
+ // Set-RP0 bit.
+ h.gs.rp[1] = h.gs.rp[0]
+ h.gs.rp[2] = i
+ if opcode&0x10 != 0 {
+ h.gs.rp[0] = i
+ }
+
+ // Move the point.
+ oldDist = dotProduct(p.X-ref.X, p.Y-ref.Y, h.gs.pv)
+ h.move(p, distance-oldDist, true)
+
+ } else {
+ // MIRPxxxxx opcode.
+
+ top -= 2
+ i := h.stack[top]
+ cvtDist := h.getScaledCVT(h.stack[top+1])
+ if fabs(cvtDist-h.gs.singleWidth) < h.gs.singleWidthCutIn {
+ if cvtDist >= 0 {
+ cvtDist = +h.gs.singleWidth
+ } else {
+ cvtDist = -h.gs.singleWidth
+ }
+ }
+
+ if h.gs.zp[1] == 0 {
+ // TODO: implement once we have a .ttf file that triggers
+ // this, so that we can step through C's freetype.
+ return errors.New("truetype: hinting: unimplemented twilight point adjustment")
+ }
+
+ ref := h.point(0, unhinted, h.gs.rp[0])
+ p := h.point(1, unhinted, i)
+ if ref == nil || p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ oldDist := dotProduct(p.X-ref.X, p.Y-ref.Y, h.gs.dv)
+
+ ref = h.point(0, current, h.gs.rp[0])
+ p = h.point(1, current, i)
+ if ref == nil || p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ curDist := dotProduct(p.X-ref.X, p.Y-ref.Y, h.gs.pv)
+
+ if h.gs.autoFlip && oldDist^cvtDist < 0 {
+ cvtDist = -cvtDist
+ }
+
+ // Rounding bit.
+ // TODO: metrics compensation.
+ distance := cvtDist
+ if opcode&0x04 != 0 {
+ // The CVT value is only used if close enough to oldDist.
+ if (h.gs.zp[0] == h.gs.zp[1]) &&
+ (fabs(cvtDist-oldDist) > h.gs.controlValueCutIn) {
+
+ distance = oldDist
+ }
+ distance = h.round(distance)
+ }
+
+ // Minimum distance bit.
+ if opcode&0x08 != 0 {
+ if oldDist >= 0 {
+ if distance < h.gs.minDist {
+ distance = h.gs.minDist
+ }
+ } else {
+ if distance > -h.gs.minDist {
+ distance = -h.gs.minDist
+ }
+ }
+ }
+
+ // Set-RP0 bit.
+ h.gs.rp[1] = h.gs.rp[0]
+ h.gs.rp[2] = i
+ if opcode&0x10 != 0 {
+ h.gs.rp[0] = i
+ }
+
+ // Move the point.
+ h.move(p, distance-curDist, true)
+ }
+ }
+ pc++
+ continue
+
+ ifelse:
+ // Skip past bytecode until the next ELSE (if opcode == 0) or the
+ // next EIF (for all opcodes). Opcode == 0 means that we have come
+ // from an IF. Opcode == 1 means that we have come from an ELSE.
+ {
+ ifelseloop:
+ for depth := 0; ; {
+ pc++
+ if pc >= len(program) {
+ return errors.New("truetype: hinting: unbalanced IF or ELSE")
+ }
+ switch program[pc] {
+ case opIF:
+ depth++
+ case opELSE:
+ if depth == 0 && opcode == 0 {
+ break ifelseloop
+ }
+ case opEIF:
+ depth--
+ if depth < 0 {
+ break ifelseloop
+ }
+ default:
+ var ok bool
+ pc, ok = skipInstructionPayload(program, pc)
+ if !ok {
+ return errors.New("truetype: hinting: unbalanced IF or ELSE")
+ }
+ }
+ }
+ pc++
+ continue
+ }
+
+ push:
+ // Push n elements from the program to the stack, where n is the low 7 bits of
+ // opcode. If the low 7 bits are zero, then n is the next byte from the program.
+ // The high bit being 0 means that the elements are zero-extended bytes.
+ // The high bit being 1 means that the elements are sign-extended words.
+ {
+ width := 1
+ if opcode&0x80 != 0 {
+ opcode &^= 0x80
+ width = 2
+ }
+ if opcode == 0 {
+ pc++
+ if pc >= len(program) {
+ return errors.New("truetype: hinting: insufficient data")
+ }
+ opcode = program[pc]
+ }
+ pc++
+ if top+int(opcode) > len(h.stack) {
+ return errors.New("truetype: hinting: stack overflow")
+ }
+ if pc+width*int(opcode) > len(program) {
+ return errors.New("truetype: hinting: insufficient data")
+ }
+ for ; opcode > 0; opcode-- {
+ if width == 1 {
+ h.stack[top] = int32(program[pc])
+ } else {
+ h.stack[top] = int32(int8(program[pc]))<<8 | int32(program[pc+1])
+ }
+ top++
+ pc += width
+ }
+ continue
+ }
+
+ delta:
+ {
+ if opcode >= opDELTAC1 && !h.scaledCVTInitialized {
+ h.initializeScaledCVT()
+ }
+ top--
+ n := h.stack[top]
+ if int32(top) < 2*n {
+ return errors.New("truetype: hinting: stack underflow")
+ }
+ for ; n > 0; n-- {
+ top -= 2
+ b := h.stack[top]
+ c := (b & 0xf0) >> 4
+ switch opcode {
+ case opDELTAP2, opDELTAC2:
+ c += 16
+ case opDELTAP3, opDELTAC3:
+ c += 32
+ }
+ c += h.gs.deltaBase
+ if ppem := (int32(h.scale) + 1<<5) >> 6; ppem != c {
+ continue
+ }
+ b = (b & 0x0f) - 8
+ if b >= 0 {
+ b++
+ }
+ b = b * 64 / (1 << uint32(h.gs.deltaShift))
+ if opcode >= opDELTAC1 {
+ a := h.stack[top+1]
+ if a < 0 || len(h.scaledCVT) <= int(a) {
+ return errors.New("truetype: hinting: index out of range")
+ }
+ h.scaledCVT[a] += fixed.Int26_6(b)
+ } else {
+ p := h.point(0, current, h.stack[top+1])
+ if p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ h.move(p, fixed.Int26_6(b), true)
+ }
+ }
+ pc++
+ continue
+ }
+ }
+ return nil
+}
+
+func (h *hinter) initializeScaledCVT() {
+ h.scaledCVTInitialized = true
+ if n := len(h.font.cvt) / 2; n <= cap(h.scaledCVT) {
+ h.scaledCVT = h.scaledCVT[:n]
+ } else {
+ if n < 32 {
+ n = 32
+ }
+ h.scaledCVT = make([]fixed.Int26_6, len(h.font.cvt)/2, n)
+ }
+ for i := range h.scaledCVT {
+ unscaled := uint16(h.font.cvt[2*i])<<8 | uint16(h.font.cvt[2*i+1])
+ h.scaledCVT[i] = h.font.scale(h.scale * fixed.Int26_6(int16(unscaled)))
+ }
+}
+
+// getScaledCVT returns the scaled value from the font's Control Value Table.
+func (h *hinter) getScaledCVT(i int32) fixed.Int26_6 {
+ if !h.scaledCVTInitialized {
+ h.initializeScaledCVT()
+ }
+ if i < 0 || len(h.scaledCVT) <= int(i) {
+ return 0
+ }
+ return h.scaledCVT[i]
+}
+
+// setScaledCVT overrides the scaled value from the font's Control Value Table.
+func (h *hinter) setScaledCVT(i int32, v fixed.Int26_6) {
+ if !h.scaledCVTInitialized {
+ h.initializeScaledCVT()
+ }
+ if i < 0 || len(h.scaledCVT) <= int(i) {
+ return
+ }
+ h.scaledCVT[i] = v
+}
+
+func (h *hinter) point(zonePointer uint32, pt pointType, i int32) *Point {
+ points := h.points[h.gs.zp[zonePointer]][pt]
+ if i < 0 || len(points) <= int(i) {
+ return nil
+ }
+ return &points[i]
+}
+
+func (h *hinter) move(p *Point, distance fixed.Int26_6, touch bool) {
+ fvx := int64(h.gs.fv[0])
+ pvx := int64(h.gs.pv[0])
+ if fvx == 0x4000 && pvx == 0x4000 {
+ p.X += fixed.Int26_6(distance)
+ if touch {
+ p.Flags |= flagTouchedX
+ }
+ return
+ }
+
+ fvy := int64(h.gs.fv[1])
+ pvy := int64(h.gs.pv[1])
+ if fvy == 0x4000 && pvy == 0x4000 {
+ p.Y += fixed.Int26_6(distance)
+ if touch {
+ p.Flags |= flagTouchedY
+ }
+ return
+ }
+
+ fvDotPv := (fvx*pvx + fvy*pvy) >> 14
+
+ if fvx != 0 {
+ p.X += fixed.Int26_6(mulDiv(fvx, int64(distance), fvDotPv))
+ if touch {
+ p.Flags |= flagTouchedX
+ }
+ }
+
+ if fvy != 0 {
+ p.Y += fixed.Int26_6(mulDiv(fvy, int64(distance), fvDotPv))
+ if touch {
+ p.Flags |= flagTouchedY
+ }
+ }
+}
+
+func (h *hinter) iupInterp(interpY bool, p1, p2, ref1, ref2 int) {
+ if p1 > p2 {
+ return
+ }
+ if ref1 >= len(h.points[glyphZone][current]) ||
+ ref2 >= len(h.points[glyphZone][current]) {
+ return
+ }
+
+ var ifu1, ifu2 fixed.Int26_6
+ if interpY {
+ ifu1 = h.points[glyphZone][inFontUnits][ref1].Y
+ ifu2 = h.points[glyphZone][inFontUnits][ref2].Y
+ } else {
+ ifu1 = h.points[glyphZone][inFontUnits][ref1].X
+ ifu2 = h.points[glyphZone][inFontUnits][ref2].X
+ }
+ if ifu1 > ifu2 {
+ ifu1, ifu2 = ifu2, ifu1
+ ref1, ref2 = ref2, ref1
+ }
+
+ var unh1, unh2, delta1, delta2 fixed.Int26_6
+ if interpY {
+ unh1 = h.points[glyphZone][unhinted][ref1].Y
+ unh2 = h.points[glyphZone][unhinted][ref2].Y
+ delta1 = h.points[glyphZone][current][ref1].Y - unh1
+ delta2 = h.points[glyphZone][current][ref2].Y - unh2
+ } else {
+ unh1 = h.points[glyphZone][unhinted][ref1].X
+ unh2 = h.points[glyphZone][unhinted][ref2].X
+ delta1 = h.points[glyphZone][current][ref1].X - unh1
+ delta2 = h.points[glyphZone][current][ref2].X - unh2
+ }
+
+ var xy, ifuXY fixed.Int26_6
+ if ifu1 == ifu2 {
+ for i := p1; i <= p2; i++ {
+ if interpY {
+ xy = h.points[glyphZone][unhinted][i].Y
+ } else {
+ xy = h.points[glyphZone][unhinted][i].X
+ }
+
+ if xy <= unh1 {
+ xy += delta1
+ } else {
+ xy += delta2
+ }
+
+ if interpY {
+ h.points[glyphZone][current][i].Y = xy
+ } else {
+ h.points[glyphZone][current][i].X = xy
+ }
+ }
+ return
+ }
+
+ scale, scaleOK := int64(0), false
+ for i := p1; i <= p2; i++ {
+ if interpY {
+ xy = h.points[glyphZone][unhinted][i].Y
+ ifuXY = h.points[glyphZone][inFontUnits][i].Y
+ } else {
+ xy = h.points[glyphZone][unhinted][i].X
+ ifuXY = h.points[glyphZone][inFontUnits][i].X
+ }
+
+ if xy <= unh1 {
+ xy += delta1
+ } else if xy >= unh2 {
+ xy += delta2
+ } else {
+ if !scaleOK {
+ scaleOK = true
+ scale = mulDiv(int64(unh2+delta2-unh1-delta1), 0x10000, int64(ifu2-ifu1))
+ }
+ numer := int64(ifuXY-ifu1) * scale
+ if numer >= 0 {
+ numer += 0x8000
+ } else {
+ numer -= 0x8000
+ }
+ xy = unh1 + delta1 + fixed.Int26_6(numer/0x10000)
+ }
+
+ if interpY {
+ h.points[glyphZone][current][i].Y = xy
+ } else {
+ h.points[glyphZone][current][i].X = xy
+ }
+ }
+}
+
+func (h *hinter) iupShift(interpY bool, p1, p2, p int) {
+ var delta fixed.Int26_6
+ if interpY {
+ delta = h.points[glyphZone][current][p].Y - h.points[glyphZone][unhinted][p].Y
+ } else {
+ delta = h.points[glyphZone][current][p].X - h.points[glyphZone][unhinted][p].X
+ }
+ if delta == 0 {
+ return
+ }
+ for i := p1; i < p2; i++ {
+ if i == p {
+ continue
+ }
+ if interpY {
+ h.points[glyphZone][current][i].Y += delta
+ } else {
+ h.points[glyphZone][current][i].X += delta
+ }
+ }
+}
+
+func (h *hinter) displacement(useZP1 bool) (zonePointer uint32, i int32, d fixed.Int26_6, ok bool) {
+ zonePointer, i = uint32(0), h.gs.rp[1]
+ if useZP1 {
+ zonePointer, i = 1, h.gs.rp[2]
+ }
+ p := h.point(zonePointer, current, i)
+ q := h.point(zonePointer, unhinted, i)
+ if p == nil || q == nil {
+ return 0, 0, 0, false
+ }
+ d = dotProduct(p.X-q.X, p.Y-q.Y, h.gs.pv)
+ return zonePointer, i, d, true
+}
+
+// skipInstructionPayload increments pc by the extra data that follows a
+// variable length PUSHB or PUSHW instruction.
+func skipInstructionPayload(program []byte, pc int) (newPC int, ok bool) {
+ switch program[pc] {
+ case opNPUSHB:
+ pc++
+ if pc >= len(program) {
+ return 0, false
+ }
+ pc += int(program[pc])
+ case opNPUSHW:
+ pc++
+ if pc >= len(program) {
+ return 0, false
+ }
+ pc += 2 * int(program[pc])
+ case opPUSHB000, opPUSHB001, opPUSHB010, opPUSHB011,
+ opPUSHB100, opPUSHB101, opPUSHB110, opPUSHB111:
+ pc += int(program[pc] - (opPUSHB000 - 1))
+ case opPUSHW000, opPUSHW001, opPUSHW010, opPUSHW011,
+ opPUSHW100, opPUSHW101, opPUSHW110, opPUSHW111:
+ pc += 2 * int(program[pc]-(opPUSHW000-1))
+ }
+ return pc, true
+}
+
+// f2dot14 is a 2.14 fixed point number.
+type f2dot14 int16
+
+func normalize(x, y f2dot14) [2]f2dot14 {
+ fx, fy := float64(x), float64(y)
+ l := 0x4000 / math.Hypot(fx, fy)
+ fx *= l
+ if fx >= 0 {
+ fx += 0.5
+ } else {
+ fx -= 0.5
+ }
+ fy *= l
+ if fy >= 0 {
+ fy += 0.5
+ } else {
+ fy -= 0.5
+ }
+ return [2]f2dot14{f2dot14(fx), f2dot14(fy)}
+}
+
+// fabs returns abs(x) in 26.6 fixed point arithmetic.
+func fabs(x fixed.Int26_6) fixed.Int26_6 {
+ if x < 0 {
+ return -x
+ }
+ return x
+}
+
+// fdiv returns x/y in 26.6 fixed point arithmetic.
+func fdiv(x, y fixed.Int26_6) fixed.Int26_6 {
+ return fixed.Int26_6((int64(x) << 6) / int64(y))
+}
+
+// fmul returns x*y in 26.6 fixed point arithmetic.
+func fmul(x, y fixed.Int26_6) fixed.Int26_6 {
+ return fixed.Int26_6((int64(x)*int64(y) + 1<<5) >> 6)
+}
+
+// dotProduct returns the dot product of [x, y] and q. It is almost the same as
+// px := int64(x)
+// py := int64(y)
+// qx := int64(q[0])
+// qy := int64(q[1])
+// return fixed.Int26_6((px*qx + py*qy + 1<<13) >> 14)
+// except that the computation is done with 32-bit integers to produce exactly
+// the same rounding behavior as C Freetype.
+func dotProduct(x, y fixed.Int26_6, q [2]f2dot14) fixed.Int26_6 {
+ // Compute x*q[0] as 64-bit value.
+ l := uint32((int32(x) & 0xFFFF) * int32(q[0]))
+ m := (int32(x) >> 16) * int32(q[0])
+
+ lo1 := l + (uint32(m) << 16)
+ hi1 := (m >> 16) + (int32(l) >> 31) + bool2int32(lo1 < l)
+
+ // Compute y*q[1] as 64-bit value.
+ l = uint32((int32(y) & 0xFFFF) * int32(q[1]))
+ m = (int32(y) >> 16) * int32(q[1])
+
+ lo2 := l + (uint32(m) << 16)
+ hi2 := (m >> 16) + (int32(l) >> 31) + bool2int32(lo2 < l)
+
+ // Add them.
+ lo := lo1 + lo2
+ hi := hi1 + hi2 + bool2int32(lo < lo1)
+
+ // Divide the result by 2^14 with rounding.
+ s := hi >> 31
+ l = lo + uint32(s)
+ hi += s + bool2int32(l < lo)
+ lo = l
+
+ l = lo + 0x2000
+ hi += bool2int32(l < lo)
+
+ return fixed.Int26_6((uint32(hi) << 18) | (l >> 14))
+}
+
+// mulDiv returns x*y/z, rounded to the nearest integer.
+func mulDiv(x, y, z int64) int64 {
+ xy := x * y
+ if z < 0 {
+ xy, z = -xy, -z
+ }
+ if xy >= 0 {
+ xy += z / 2
+ } else {
+ xy -= z / 2
+ }
+ return xy / z
+}
+
+// round rounds the given number. The rounding algorithm is described at
+// https://developer.apple.com/fonts/TTRefMan/RM02/Chap2.html#rounding
+func (h *hinter) round(x fixed.Int26_6) fixed.Int26_6 {
+ if h.gs.roundPeriod == 0 {
+ // Rounding is off.
+ return x
+ }
+ if x >= 0 {
+ ret := x - h.gs.roundPhase + h.gs.roundThreshold
+ if h.gs.roundSuper45 {
+ ret /= h.gs.roundPeriod
+ ret *= h.gs.roundPeriod
+ } else {
+ ret &= -h.gs.roundPeriod
+ }
+ if x != 0 && ret < 0 {
+ ret = 0
+ }
+ return ret + h.gs.roundPhase
+ }
+ ret := -x - h.gs.roundPhase + h.gs.roundThreshold
+ if h.gs.roundSuper45 {
+ ret /= h.gs.roundPeriod
+ ret *= h.gs.roundPeriod
+ } else {
+ ret &= -h.gs.roundPeriod
+ }
+ if ret < 0 {
+ ret = 0
+ }
+ return -ret - h.gs.roundPhase
+}
+
+func bool2int32(b bool) int32 {
+ if b {
+ return 1
+ }
+ return 0
+}
diff --git a/vendor/github.com/golang/freetype/truetype/opcodes.go b/vendor/github.com/golang/freetype/truetype/opcodes.go
new file mode 100644
index 0000000..1880e1e
--- /dev/null
+++ b/vendor/github.com/golang/freetype/truetype/opcodes.go
@@ -0,0 +1,289 @@
+// Copyright 2012 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+package truetype
+
+// The Truetype opcodes are summarized at
+// https://developer.apple.com/fonts/TTRefMan/RM07/appendixA.html
+
+const (
+ opSVTCA0 = 0x00 // Set freedom and projection Vectors To Coordinate Axis
+ opSVTCA1 = 0x01 // .
+ opSPVTCA0 = 0x02 // Set Projection Vector To Coordinate Axis
+ opSPVTCA1 = 0x03 // .
+ opSFVTCA0 = 0x04 // Set Freedom Vector to Coordinate Axis
+ opSFVTCA1 = 0x05 // .
+ opSPVTL0 = 0x06 // Set Projection Vector To Line
+ opSPVTL1 = 0x07 // .
+ opSFVTL0 = 0x08 // Set Freedom Vector To Line
+ opSFVTL1 = 0x09 // .
+ opSPVFS = 0x0a // Set Projection Vector From Stack
+ opSFVFS = 0x0b // Set Freedom Vector From Stack
+ opGPV = 0x0c // Get Projection Vector
+ opGFV = 0x0d // Get Freedom Vector
+ opSFVTPV = 0x0e // Set Freedom Vector To Projection Vector
+ opISECT = 0x0f // moves point p to the InterSECTion of two lines
+ opSRP0 = 0x10 // Set Reference Point 0
+ opSRP1 = 0x11 // Set Reference Point 1
+ opSRP2 = 0x12 // Set Reference Point 2
+ opSZP0 = 0x13 // Set Zone Pointer 0
+ opSZP1 = 0x14 // Set Zone Pointer 1
+ opSZP2 = 0x15 // Set Zone Pointer 2
+ opSZPS = 0x16 // Set Zone PointerS
+ opSLOOP = 0x17 // Set LOOP variable
+ opRTG = 0x18 // Round To Grid
+ opRTHG = 0x19 // Round To Half Grid
+ opSMD = 0x1a // Set Minimum Distance
+ opELSE = 0x1b // ELSE clause
+ opJMPR = 0x1c // JuMP Relative
+ opSCVTCI = 0x1d // Set Control Value Table Cut-In
+ opSSWCI = 0x1e // Set Single Width Cut-In
+ opSSW = 0x1f // Set Single Width
+ opDUP = 0x20 // DUPlicate top stack element
+ opPOP = 0x21 // POP top stack element
+ opCLEAR = 0x22 // CLEAR the stack
+ opSWAP = 0x23 // SWAP the top two elements on the stack
+ opDEPTH = 0x24 // DEPTH of the stack
+ opCINDEX = 0x25 // Copy the INDEXed element to the top of the stack
+ opMINDEX = 0x26 // Move the INDEXed element to the top of the stack
+ opALIGNPTS = 0x27 // ALIGN PoinTS
+ op_0x28 = 0x28 // deprecated
+ opUTP = 0x29 // UnTouch Point
+ opLOOPCALL = 0x2a // LOOP and CALL function
+ opCALL = 0x2b // CALL function
+ opFDEF = 0x2c // Function DEFinition
+ opENDF = 0x2d // END Function definition
+ opMDAP0 = 0x2e // Move Direct Absolute Point
+ opMDAP1 = 0x2f // .
+ opIUP0 = 0x30 // Interpolate Untouched Points through the outline
+ opIUP1 = 0x31 // .
+ opSHP0 = 0x32 // SHift Point using reference point
+ opSHP1 = 0x33 // .
+ opSHC0 = 0x34 // SHift Contour using reference point
+ opSHC1 = 0x35 // .
+ opSHZ0 = 0x36 // SHift Zone using reference point
+ opSHZ1 = 0x37 // .
+ opSHPIX = 0x38 // SHift point by a PIXel amount
+ opIP = 0x39 // Interpolate Point
+ opMSIRP0 = 0x3a // Move Stack Indirect Relative Point
+ opMSIRP1 = 0x3b // .
+ opALIGNRP = 0x3c // ALIGN to Reference Point
+ opRTDG = 0x3d // Round To Double Grid
+ opMIAP0 = 0x3e // Move Indirect Absolute Point
+ opMIAP1 = 0x3f // .
+ opNPUSHB = 0x40 // PUSH N Bytes
+ opNPUSHW = 0x41 // PUSH N Words
+ opWS = 0x42 // Write Store
+ opRS = 0x43 // Read Store
+ opWCVTP = 0x44 // Write Control Value Table in Pixel units
+ opRCVT = 0x45 // Read Control Value Table entry
+ opGC0 = 0x46 // Get Coordinate projected onto the projection vector
+ opGC1 = 0x47 // .
+ opSCFS = 0x48 // Sets Coordinate From the Stack using projection vector and freedom vector
+ opMD0 = 0x49 // Measure Distance
+ opMD1 = 0x4a // .
+ opMPPEM = 0x4b // Measure Pixels Per EM
+ opMPS = 0x4c // Measure Point Size
+ opFLIPON = 0x4d // set the auto FLIP Boolean to ON
+ opFLIPOFF = 0x4e // set the auto FLIP Boolean to OFF
+ opDEBUG = 0x4f // DEBUG call
+ opLT = 0x50 // Less Than
+ opLTEQ = 0x51 // Less Than or EQual
+ opGT = 0x52 // Greater Than
+ opGTEQ = 0x53 // Greater Than or EQual
+ opEQ = 0x54 // EQual
+ opNEQ = 0x55 // Not EQual
+ opODD = 0x56 // ODD
+ opEVEN = 0x57 // EVEN
+ opIF = 0x58 // IF test
+ opEIF = 0x59 // End IF
+ opAND = 0x5a // logical AND
+ opOR = 0x5b // logical OR
+ opNOT = 0x5c // logical NOT
+ opDELTAP1 = 0x5d // DELTA exception P1
+ opSDB = 0x5e // Set Delta Base in the graphics state
+ opSDS = 0x5f // Set Delta Shift in the graphics state
+ opADD = 0x60 // ADD
+ opSUB = 0x61 // SUBtract
+ opDIV = 0x62 // DIVide
+ opMUL = 0x63 // MULtiply
+ opABS = 0x64 // ABSolute value
+ opNEG = 0x65 // NEGate
+ opFLOOR = 0x66 // FLOOR
+ opCEILING = 0x67 // CEILING
+ opROUND00 = 0x68 // ROUND value
+ opROUND01 = 0x69 // .
+ opROUND10 = 0x6a // .
+ opROUND11 = 0x6b // .
+ opNROUND00 = 0x6c // No ROUNDing of value
+ opNROUND01 = 0x6d // .
+ opNROUND10 = 0x6e // .
+ opNROUND11 = 0x6f // .
+ opWCVTF = 0x70 // Write Control Value Table in Funits
+ opDELTAP2 = 0x71 // DELTA exception P2
+ opDELTAP3 = 0x72 // DELTA exception P3
+ opDELTAC1 = 0x73 // DELTA exception C1
+ opDELTAC2 = 0x74 // DELTA exception C2
+ opDELTAC3 = 0x75 // DELTA exception C3
+ opSROUND = 0x76 // Super ROUND
+ opS45ROUND = 0x77 // Super ROUND 45 degrees
+ opJROT = 0x78 // Jump Relative On True
+ opJROF = 0x79 // Jump Relative On False
+ opROFF = 0x7a // Round OFF
+ op_0x7b = 0x7b // deprecated
+ opRUTG = 0x7c // Round Up To Grid
+ opRDTG = 0x7d // Round Down To Grid
+ opSANGW = 0x7e // Set ANGle Weight
+ opAA = 0x7f // Adjust Angle
+ opFLIPPT = 0x80 // FLIP PoinT
+ opFLIPRGON = 0x81 // FLIP RanGe ON
+ opFLIPRGOFF = 0x82 // FLIP RanGe OFF
+ op_0x83 = 0x83 // deprecated
+ op_0x84 = 0x84 // deprecated
+ opSCANCTRL = 0x85 // SCAN conversion ConTRoL
+ opSDPVTL0 = 0x86 // Set Dual Projection Vector To Line
+ opSDPVTL1 = 0x87 // .
+ opGETINFO = 0x88 // GET INFOrmation
+ opIDEF = 0x89 // Instruction DEFinition
+ opROLL = 0x8a // ROLL the top three stack elements
+ opMAX = 0x8b // MAXimum of top two stack elements
+ opMIN = 0x8c // MINimum of top two stack elements
+ opSCANTYPE = 0x8d // SCANTYPE
+ opINSTCTRL = 0x8e // INSTRuction execution ConTRoL
+ op_0x8f = 0x8f
+ op_0x90 = 0x90
+ op_0x91 = 0x91
+ op_0x92 = 0x92
+ op_0x93 = 0x93
+ op_0x94 = 0x94
+ op_0x95 = 0x95
+ op_0x96 = 0x96
+ op_0x97 = 0x97
+ op_0x98 = 0x98
+ op_0x99 = 0x99
+ op_0x9a = 0x9a
+ op_0x9b = 0x9b
+ op_0x9c = 0x9c
+ op_0x9d = 0x9d
+ op_0x9e = 0x9e
+ op_0x9f = 0x9f
+ op_0xa0 = 0xa0
+ op_0xa1 = 0xa1
+ op_0xa2 = 0xa2
+ op_0xa3 = 0xa3
+ op_0xa4 = 0xa4
+ op_0xa5 = 0xa5
+ op_0xa6 = 0xa6
+ op_0xa7 = 0xa7
+ op_0xa8 = 0xa8
+ op_0xa9 = 0xa9
+ op_0xaa = 0xaa
+ op_0xab = 0xab
+ op_0xac = 0xac
+ op_0xad = 0xad
+ op_0xae = 0xae
+ op_0xaf = 0xaf
+ opPUSHB000 = 0xb0 // PUSH Bytes
+ opPUSHB001 = 0xb1 // .
+ opPUSHB010 = 0xb2 // .
+ opPUSHB011 = 0xb3 // .
+ opPUSHB100 = 0xb4 // .
+ opPUSHB101 = 0xb5 // .
+ opPUSHB110 = 0xb6 // .
+ opPUSHB111 = 0xb7 // .
+ opPUSHW000 = 0xb8 // PUSH Words
+ opPUSHW001 = 0xb9 // .
+ opPUSHW010 = 0xba // .
+ opPUSHW011 = 0xbb // .
+ opPUSHW100 = 0xbc // .
+ opPUSHW101 = 0xbd // .
+ opPUSHW110 = 0xbe // .
+ opPUSHW111 = 0xbf // .
+ opMDRP00000 = 0xc0 // Move Direct Relative Point
+ opMDRP00001 = 0xc1 // .
+ opMDRP00010 = 0xc2 // .
+ opMDRP00011 = 0xc3 // .
+ opMDRP00100 = 0xc4 // .
+ opMDRP00101 = 0xc5 // .
+ opMDRP00110 = 0xc6 // .
+ opMDRP00111 = 0xc7 // .
+ opMDRP01000 = 0xc8 // .
+ opMDRP01001 = 0xc9 // .
+ opMDRP01010 = 0xca // .
+ opMDRP01011 = 0xcb // .
+ opMDRP01100 = 0xcc // .
+ opMDRP01101 = 0xcd // .
+ opMDRP01110 = 0xce // .
+ opMDRP01111 = 0xcf // .
+ opMDRP10000 = 0xd0 // .
+ opMDRP10001 = 0xd1 // .
+ opMDRP10010 = 0xd2 // .
+ opMDRP10011 = 0xd3 // .
+ opMDRP10100 = 0xd4 // .
+ opMDRP10101 = 0xd5 // .
+ opMDRP10110 = 0xd6 // .
+ opMDRP10111 = 0xd7 // .
+ opMDRP11000 = 0xd8 // .
+ opMDRP11001 = 0xd9 // .
+ opMDRP11010 = 0xda // .
+ opMDRP11011 = 0xdb // .
+ opMDRP11100 = 0xdc // .
+ opMDRP11101 = 0xdd // .
+ opMDRP11110 = 0xde // .
+ opMDRP11111 = 0xdf // .
+ opMIRP00000 = 0xe0 // Move Indirect Relative Point
+ opMIRP00001 = 0xe1 // .
+ opMIRP00010 = 0xe2 // .
+ opMIRP00011 = 0xe3 // .
+ opMIRP00100 = 0xe4 // .
+ opMIRP00101 = 0xe5 // .
+ opMIRP00110 = 0xe6 // .
+ opMIRP00111 = 0xe7 // .
+ opMIRP01000 = 0xe8 // .
+ opMIRP01001 = 0xe9 // .
+ opMIRP01010 = 0xea // .
+ opMIRP01011 = 0xeb // .
+ opMIRP01100 = 0xec // .
+ opMIRP01101 = 0xed // .
+ opMIRP01110 = 0xee // .
+ opMIRP01111 = 0xef // .
+ opMIRP10000 = 0xf0 // .
+ opMIRP10001 = 0xf1 // .
+ opMIRP10010 = 0xf2 // .
+ opMIRP10011 = 0xf3 // .
+ opMIRP10100 = 0xf4 // .
+ opMIRP10101 = 0xf5 // .
+ opMIRP10110 = 0xf6 // .
+ opMIRP10111 = 0xf7 // .
+ opMIRP11000 = 0xf8 // .
+ opMIRP11001 = 0xf9 // .
+ opMIRP11010 = 0xfa // .
+ opMIRP11011 = 0xfb // .
+ opMIRP11100 = 0xfc // .
+ opMIRP11101 = 0xfd // .
+ opMIRP11110 = 0xfe // .
+ opMIRP11111 = 0xff // .
+)
+
+// popCount is the number of stack elements that each opcode pops.
+var popCount = [256]uint8{
+ // 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f
+ 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 5, // 0x00 - 0x0f
+ 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, // 0x10 - 0x1f
+ 1, 1, 0, 2, 0, 1, 1, 2, 0, 1, 2, 1, 1, 0, 1, 1, // 0x20 - 0x2f
+ 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 2, 2, 0, 0, 2, 2, // 0x30 - 0x3f
+ 0, 0, 2, 1, 2, 1, 1, 1, 2, 2, 2, 0, 0, 0, 0, 0, // 0x40 - 0x4f
+ 2, 2, 2, 2, 2, 2, 1, 1, 1, 0, 2, 2, 1, 1, 1, 1, // 0x50 - 0x5f
+ 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 - 0x6f
+ 2, 1, 1, 1, 1, 1, 1, 1, 2, 2, 0, 0, 0, 0, 1, 1, // 0x70 - 0x7f
+ 0, 2, 2, 0, 0, 1, 2, 2, 1, 1, 3, 2, 2, 1, 2, 0, // 0x80 - 0x8f
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90 - 0x9f
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xa0 - 0xaf
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xb0 - 0xbf
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xc0 - 0xcf
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xd0 - 0xdf
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xe0 - 0xef
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xf0 - 0xff
+}
diff --git a/vendor/github.com/golang/freetype/truetype/truetype.go b/vendor/github.com/golang/freetype/truetype/truetype.go
new file mode 100644
index 0000000..613fc17
--- /dev/null
+++ b/vendor/github.com/golang/freetype/truetype/truetype.go
@@ -0,0 +1,643 @@
+// Copyright 2010 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+// Package truetype provides a parser for the TTF and TTC file formats.
+// Those formats are documented at http://developer.apple.com/fonts/TTRefMan/
+// and http://www.microsoft.com/typography/otspec/
+//
+// Some of a font's methods provide lengths or co-ordinates, e.g. bounds, font
+// metrics and control points. All these methods take a scale parameter, which
+// is the number of pixels in 1 em, expressed as a 26.6 fixed point value. For
+// example, if 1 em is 10 pixels then scale is fixed.I(10), which is equal to
+// fixed.Int26_6(10 << 6).
+//
+// To measure a TrueType font in ideal FUnit space, use scale equal to
+// font.FUnitsPerEm().
+package truetype // import "github.com/golang/freetype/truetype"
+
+import (
+ "fmt"
+
+ "golang.org/x/image/math/fixed"
+)
+
+// An Index is a Font's index of a rune.
+type Index uint16
+
+// A NameID identifies a name table entry.
+//
+// See https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6name.html
+type NameID uint16
+
+const (
+ NameIDCopyright NameID = 0
+ NameIDFontFamily = 1
+ NameIDFontSubfamily = 2
+ NameIDUniqueSubfamilyID = 3
+ NameIDFontFullName = 4
+ NameIDNameTableVersion = 5
+ NameIDPostscriptName = 6
+ NameIDTrademarkNotice = 7
+ NameIDManufacturerName = 8
+ NameIDDesignerName = 9
+ NameIDFontDescription = 10
+ NameIDFontVendorURL = 11
+ NameIDFontDesignerURL = 12
+ NameIDFontLicense = 13
+ NameIDFontLicenseURL = 14
+ NameIDPreferredFamily = 16
+ NameIDPreferredSubfamily = 17
+ NameIDCompatibleName = 18
+ NameIDSampleText = 19
+)
+
+const (
+ // A 32-bit encoding consists of a most-significant 16-bit Platform ID and a
+ // least-significant 16-bit Platform Specific ID. The magic numbers are
+ // specified at https://www.microsoft.com/typography/otspec/name.htm
+ unicodeEncoding = 0x00000003 // PID = 0 (Unicode), PSID = 3 (Unicode 2.0)
+ microsoftSymbolEncoding = 0x00030000 // PID = 3 (Microsoft), PSID = 0 (Symbol)
+ microsoftUCS2Encoding = 0x00030001 // PID = 3 (Microsoft), PSID = 1 (UCS-2)
+ microsoftUCS4Encoding = 0x0003000a // PID = 3 (Microsoft), PSID = 10 (UCS-4)
+)
+
+// An HMetric holds the horizontal metrics of a single glyph.
+type HMetric struct {
+ AdvanceWidth, LeftSideBearing fixed.Int26_6
+}
+
+// A VMetric holds the vertical metrics of a single glyph.
+type VMetric struct {
+ AdvanceHeight, TopSideBearing fixed.Int26_6
+}
+
+// A FormatError reports that the input is not a valid TrueType font.
+type FormatError string
+
+func (e FormatError) Error() string {
+ return "freetype: invalid TrueType format: " + string(e)
+}
+
+// An UnsupportedError reports that the input uses a valid but unimplemented
+// TrueType feature.
+type UnsupportedError string
+
+func (e UnsupportedError) Error() string {
+ return "freetype: unsupported TrueType feature: " + string(e)
+}
+
+// u32 returns the big-endian uint32 at b[i:].
+func u32(b []byte, i int) uint32 {
+ return uint32(b[i])<<24 | uint32(b[i+1])<<16 | uint32(b[i+2])<<8 | uint32(b[i+3])
+}
+
+// u16 returns the big-endian uint16 at b[i:].
+func u16(b []byte, i int) uint16 {
+ return uint16(b[i])<<8 | uint16(b[i+1])
+}
+
+// readTable returns a slice of the TTF data given by a table's directory entry.
+func readTable(ttf []byte, offsetLength []byte) ([]byte, error) {
+ offset := int(u32(offsetLength, 0))
+ if offset < 0 {
+ return nil, FormatError(fmt.Sprintf("offset too large: %d", uint32(offset)))
+ }
+ length := int(u32(offsetLength, 4))
+ if length < 0 {
+ return nil, FormatError(fmt.Sprintf("length too large: %d", uint32(length)))
+ }
+ end := offset + length
+ if end < 0 || end > len(ttf) {
+ return nil, FormatError(fmt.Sprintf("offset + length too large: %d", uint32(offset)+uint32(length)))
+ }
+ return ttf[offset:end], nil
+}
+
+// parseSubtables returns the offset and platformID of the best subtable in
+// table, where best favors a Unicode cmap encoding, and failing that, a
+// Microsoft cmap encoding. offset is the offset of the first subtable in
+// table, and size is the size of each subtable.
+//
+// If pred is non-nil, then only subtables that satisfy that predicate will be
+// considered.
+func parseSubtables(table []byte, name string, offset, size int, pred func([]byte) bool) (
+ bestOffset int, bestPID uint32, retErr error) {
+
+ if len(table) < 4 {
+ return 0, 0, FormatError(name + " too short")
+ }
+ nSubtables := int(u16(table, 2))
+ if len(table) < size*nSubtables+offset {
+ return 0, 0, FormatError(name + " too short")
+ }
+ ok := false
+ for i := 0; i < nSubtables; i, offset = i+1, offset+size {
+ if pred != nil && !pred(table[offset:]) {
+ continue
+ }
+ // We read the 16-bit Platform ID and 16-bit Platform Specific ID as a single uint32.
+ // All values are big-endian.
+ pidPsid := u32(table, offset)
+ // We prefer the Unicode cmap encoding. Failing to find that, we fall
+ // back onto the Microsoft cmap encoding.
+ if pidPsid == unicodeEncoding {
+ bestOffset, bestPID, ok = offset, pidPsid>>16, true
+ break
+
+ } else if pidPsid == microsoftSymbolEncoding ||
+ pidPsid == microsoftUCS2Encoding ||
+ pidPsid == microsoftUCS4Encoding {
+
+ bestOffset, bestPID, ok = offset, pidPsid>>16, true
+ // We don't break out of the for loop, so that Unicode can override Microsoft.
+ }
+ }
+ if !ok {
+ return 0, 0, UnsupportedError(name + " encoding")
+ }
+ return bestOffset, bestPID, nil
+}
+
+const (
+ locaOffsetFormatUnknown int = iota
+ locaOffsetFormatShort
+ locaOffsetFormatLong
+)
+
+// A cm holds a parsed cmap entry.
+type cm struct {
+ start, end, delta, offset uint32
+}
+
+// A Font represents a Truetype font.
+type Font struct {
+ // Tables sliced from the TTF data. The different tables are documented
+ // at http://developer.apple.com/fonts/TTRefMan/RM06/Chap6.html
+ cmap, cvt, fpgm, glyf, hdmx, head, hhea, hmtx, kern, loca, maxp, name, os2, prep, vmtx []byte
+
+ cmapIndexes []byte
+
+ // Cached values derived from the raw ttf data.
+ cm []cm
+ locaOffsetFormat int
+ nGlyph, nHMetric, nKern int
+ fUnitsPerEm int32
+ ascent int32 // In FUnits.
+ descent int32 // In FUnits; typically negative.
+ bounds fixed.Rectangle26_6 // In FUnits.
+ // Values from the maxp section.
+ maxTwilightPoints, maxStorage, maxFunctionDefs, maxStackElements uint16
+}
+
+func (f *Font) parseCmap() error {
+ const (
+ cmapFormat4 = 4
+ cmapFormat12 = 12
+ languageIndependent = 0
+ )
+
+ offset, _, err := parseSubtables(f.cmap, "cmap", 4, 8, nil)
+ if err != nil {
+ return err
+ }
+ offset = int(u32(f.cmap, offset+4))
+ if offset <= 0 || offset > len(f.cmap) {
+ return FormatError("bad cmap offset")
+ }
+
+ cmapFormat := u16(f.cmap, offset)
+ switch cmapFormat {
+ case cmapFormat4:
+ language := u16(f.cmap, offset+4)
+ if language != languageIndependent {
+ return UnsupportedError(fmt.Sprintf("language: %d", language))
+ }
+ segCountX2 := int(u16(f.cmap, offset+6))
+ if segCountX2%2 == 1 {
+ return FormatError(fmt.Sprintf("bad segCountX2: %d", segCountX2))
+ }
+ segCount := segCountX2 / 2
+ offset += 14
+ f.cm = make([]cm, segCount)
+ for i := 0; i < segCount; i++ {
+ f.cm[i].end = uint32(u16(f.cmap, offset))
+ offset += 2
+ }
+ offset += 2
+ for i := 0; i < segCount; i++ {
+ f.cm[i].start = uint32(u16(f.cmap, offset))
+ offset += 2
+ }
+ for i := 0; i < segCount; i++ {
+ f.cm[i].delta = uint32(u16(f.cmap, offset))
+ offset += 2
+ }
+ for i := 0; i < segCount; i++ {
+ f.cm[i].offset = uint32(u16(f.cmap, offset))
+ offset += 2
+ }
+ f.cmapIndexes = f.cmap[offset:]
+ return nil
+
+ case cmapFormat12:
+ if u16(f.cmap, offset+2) != 0 {
+ return FormatError(fmt.Sprintf("cmap format: % x", f.cmap[offset:offset+4]))
+ }
+ length := u32(f.cmap, offset+4)
+ language := u32(f.cmap, offset+8)
+ if language != languageIndependent {
+ return UnsupportedError(fmt.Sprintf("language: %d", language))
+ }
+ nGroups := u32(f.cmap, offset+12)
+ if length != 12*nGroups+16 {
+ return FormatError("inconsistent cmap length")
+ }
+ offset += 16
+ f.cm = make([]cm, nGroups)
+ for i := uint32(0); i < nGroups; i++ {
+ f.cm[i].start = u32(f.cmap, offset+0)
+ f.cm[i].end = u32(f.cmap, offset+4)
+ f.cm[i].delta = u32(f.cmap, offset+8) - f.cm[i].start
+ offset += 12
+ }
+ return nil
+ }
+ return UnsupportedError(fmt.Sprintf("cmap format: %d", cmapFormat))
+}
+
+func (f *Font) parseHead() error {
+ if len(f.head) != 54 {
+ return FormatError(fmt.Sprintf("bad head length: %d", len(f.head)))
+ }
+ f.fUnitsPerEm = int32(u16(f.head, 18))
+ f.bounds.Min.X = fixed.Int26_6(int16(u16(f.head, 36)))
+ f.bounds.Min.Y = fixed.Int26_6(int16(u16(f.head, 38)))
+ f.bounds.Max.X = fixed.Int26_6(int16(u16(f.head, 40)))
+ f.bounds.Max.Y = fixed.Int26_6(int16(u16(f.head, 42)))
+ switch i := u16(f.head, 50); i {
+ case 0:
+ f.locaOffsetFormat = locaOffsetFormatShort
+ case 1:
+ f.locaOffsetFormat = locaOffsetFormatLong
+ default:
+ return FormatError(fmt.Sprintf("bad indexToLocFormat: %d", i))
+ }
+ return nil
+}
+
+func (f *Font) parseHhea() error {
+ if len(f.hhea) != 36 {
+ return FormatError(fmt.Sprintf("bad hhea length: %d", len(f.hhea)))
+ }
+ f.ascent = int32(int16(u16(f.hhea, 4)))
+ f.descent = int32(int16(u16(f.hhea, 6)))
+ f.nHMetric = int(u16(f.hhea, 34))
+ if 4*f.nHMetric+2*(f.nGlyph-f.nHMetric) != len(f.hmtx) {
+ return FormatError(fmt.Sprintf("bad hmtx length: %d", len(f.hmtx)))
+ }
+ return nil
+}
+
+func (f *Font) parseKern() error {
+ // Apple's TrueType documentation (http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html) says:
+ // "Previous versions of the 'kern' table defined both the version and nTables fields in the header
+ // as UInt16 values and not UInt32 values. Use of the older format on the Mac OS is discouraged
+ // (although AAT can sense an old kerning table and still make correct use of it). Microsoft
+ // Windows still uses the older format for the 'kern' table and will not recognize the newer one.
+ // Fonts targeted for the Mac OS only should use the new format; fonts targeted for both the Mac OS
+ // and Windows should use the old format."
+ // Since we expect that almost all fonts aim to be Windows-compatible, we only parse the "older" format,
+ // just like the C Freetype implementation.
+ if len(f.kern) == 0 {
+ if f.nKern != 0 {
+ return FormatError("bad kern table length")
+ }
+ return nil
+ }
+ if len(f.kern) < 18 {
+ return FormatError("kern data too short")
+ }
+ version, offset := u16(f.kern, 0), 2
+ if version != 0 {
+ return UnsupportedError(fmt.Sprintf("kern version: %d", version))
+ }
+ n, offset := u16(f.kern, offset), offset+2
+ if n != 1 {
+ return UnsupportedError(fmt.Sprintf("kern nTables: %d", n))
+ }
+ offset += 2
+ length, offset := int(u16(f.kern, offset)), offset+2
+ coverage, offset := u16(f.kern, offset), offset+2
+ if coverage != 0x0001 {
+ // We only support horizontal kerning.
+ return UnsupportedError(fmt.Sprintf("kern coverage: 0x%04x", coverage))
+ }
+ f.nKern, offset = int(u16(f.kern, offset)), offset+2
+ if 6*f.nKern != length-14 {
+ return FormatError("bad kern table length")
+ }
+ return nil
+}
+
+func (f *Font) parseMaxp() error {
+ if len(f.maxp) != 32 {
+ return FormatError(fmt.Sprintf("bad maxp length: %d", len(f.maxp)))
+ }
+ f.nGlyph = int(u16(f.maxp, 4))
+ f.maxTwilightPoints = u16(f.maxp, 16)
+ f.maxStorage = u16(f.maxp, 18)
+ f.maxFunctionDefs = u16(f.maxp, 20)
+ f.maxStackElements = u16(f.maxp, 24)
+ return nil
+}
+
+// scale returns x divided by f.fUnitsPerEm, rounded to the nearest integer.
+func (f *Font) scale(x fixed.Int26_6) fixed.Int26_6 {
+ if x >= 0 {
+ x += fixed.Int26_6(f.fUnitsPerEm) / 2
+ } else {
+ x -= fixed.Int26_6(f.fUnitsPerEm) / 2
+ }
+ return x / fixed.Int26_6(f.fUnitsPerEm)
+}
+
+// Bounds returns the union of a Font's glyphs' bounds.
+func (f *Font) Bounds(scale fixed.Int26_6) fixed.Rectangle26_6 {
+ b := f.bounds
+ b.Min.X = f.scale(scale * b.Min.X)
+ b.Min.Y = f.scale(scale * b.Min.Y)
+ b.Max.X = f.scale(scale * b.Max.X)
+ b.Max.Y = f.scale(scale * b.Max.Y)
+ return b
+}
+
+// FUnitsPerEm returns the number of FUnits in a Font's em-square's side.
+func (f *Font) FUnitsPerEm() int32 {
+ return f.fUnitsPerEm
+}
+
+// Index returns a Font's index for the given rune.
+func (f *Font) Index(x rune) Index {
+ c := uint32(x)
+ for i, j := 0, len(f.cm); i < j; {
+ h := i + (j-i)/2
+ cm := &f.cm[h]
+ if c < cm.start {
+ j = h
+ } else if cm.end < c {
+ i = h + 1
+ } else if cm.offset == 0 {
+ return Index(c + cm.delta)
+ } else {
+ offset := int(cm.offset) + 2*(h-len(f.cm)+int(c-cm.start))
+ return Index(u16(f.cmapIndexes, offset))
+ }
+ }
+ return 0
+}
+
+// Name returns the Font's name value for the given NameID. It returns "" if
+// there was an error, or if that name was not found.
+func (f *Font) Name(id NameID) string {
+ x, platformID, err := parseSubtables(f.name, "name", 6, 12, func(b []byte) bool {
+ return NameID(u16(b, 6)) == id
+ })
+ if err != nil {
+ return ""
+ }
+ offset, length := u16(f.name, 4)+u16(f.name, x+10), u16(f.name, x+8)
+ // Return the ASCII value of the encoded string.
+ // The string is encoded as UTF-16 on non-Apple platformIDs; Apple is platformID 1.
+ src := f.name[offset : offset+length]
+ var dst []byte
+ if platformID != 1 { // UTF-16.
+ if len(src)&1 != 0 {
+ return ""
+ }
+ dst = make([]byte, len(src)/2)
+ for i := range dst {
+ dst[i] = printable(u16(src, 2*i))
+ }
+ } else { // ASCII.
+ dst = make([]byte, len(src))
+ for i, c := range src {
+ dst[i] = printable(uint16(c))
+ }
+ }
+ return string(dst)
+}
+
+func printable(r uint16) byte {
+ if 0x20 <= r && r < 0x7f {
+ return byte(r)
+ }
+ return '?'
+}
+
+// unscaledHMetric returns the unscaled horizontal metrics for the glyph with
+// the given index.
+func (f *Font) unscaledHMetric(i Index) (h HMetric) {
+ j := int(i)
+ if j < 0 || f.nGlyph <= j {
+ return HMetric{}
+ }
+ if j >= f.nHMetric {
+ p := 4 * (f.nHMetric - 1)
+ return HMetric{
+ AdvanceWidth: fixed.Int26_6(u16(f.hmtx, p)),
+ LeftSideBearing: fixed.Int26_6(int16(u16(f.hmtx, p+2*(j-f.nHMetric)+4))),
+ }
+ }
+ return HMetric{
+ AdvanceWidth: fixed.Int26_6(u16(f.hmtx, 4*j)),
+ LeftSideBearing: fixed.Int26_6(int16(u16(f.hmtx, 4*j+2))),
+ }
+}
+
+// HMetric returns the horizontal metrics for the glyph with the given index.
+func (f *Font) HMetric(scale fixed.Int26_6, i Index) HMetric {
+ h := f.unscaledHMetric(i)
+ h.AdvanceWidth = f.scale(scale * h.AdvanceWidth)
+ h.LeftSideBearing = f.scale(scale * h.LeftSideBearing)
+ return h
+}
+
+// unscaledVMetric returns the unscaled vertical metrics for the glyph with
+// the given index. yMax is the top of the glyph's bounding box.
+func (f *Font) unscaledVMetric(i Index, yMax fixed.Int26_6) (v VMetric) {
+ j := int(i)
+ if j < 0 || f.nGlyph <= j {
+ return VMetric{}
+ }
+ if 4*j+4 <= len(f.vmtx) {
+ return VMetric{
+ AdvanceHeight: fixed.Int26_6(u16(f.vmtx, 4*j)),
+ TopSideBearing: fixed.Int26_6(int16(u16(f.vmtx, 4*j+2))),
+ }
+ }
+ // The OS/2 table has grown over time.
+ // https://developer.apple.com/fonts/TTRefMan/RM06/Chap6OS2.html
+ // says that it was originally 68 bytes. Optional fields, including
+ // the ascender and descender, are described at
+ // http://www.microsoft.com/typography/otspec/os2.htm
+ if len(f.os2) >= 72 {
+ sTypoAscender := fixed.Int26_6(int16(u16(f.os2, 68)))
+ sTypoDescender := fixed.Int26_6(int16(u16(f.os2, 70)))
+ return VMetric{
+ AdvanceHeight: sTypoAscender - sTypoDescender,
+ TopSideBearing: sTypoAscender - yMax,
+ }
+ }
+ return VMetric{
+ AdvanceHeight: fixed.Int26_6(f.fUnitsPerEm),
+ TopSideBearing: 0,
+ }
+}
+
+// VMetric returns the vertical metrics for the glyph with the given index.
+func (f *Font) VMetric(scale fixed.Int26_6, i Index) VMetric {
+ // TODO: should 0 be bounds.YMax?
+ v := f.unscaledVMetric(i, 0)
+ v.AdvanceHeight = f.scale(scale * v.AdvanceHeight)
+ v.TopSideBearing = f.scale(scale * v.TopSideBearing)
+ return v
+}
+
+// Kern returns the horizontal adjustment for the given glyph pair. A positive
+// kern means to move the glyphs further apart.
+func (f *Font) Kern(scale fixed.Int26_6, i0, i1 Index) fixed.Int26_6 {
+ if f.nKern == 0 {
+ return 0
+ }
+ g := uint32(i0)<<16 | uint32(i1)
+ lo, hi := 0, f.nKern
+ for lo < hi {
+ i := (lo + hi) / 2
+ ig := u32(f.kern, 18+6*i)
+ if ig < g {
+ lo = i + 1
+ } else if ig > g {
+ hi = i
+ } else {
+ return f.scale(scale * fixed.Int26_6(int16(u16(f.kern, 22+6*i))))
+ }
+ }
+ return 0
+}
+
+// Parse returns a new Font for the given TTF or TTC data.
+//
+// For TrueType Collections, the first font in the collection is parsed.
+func Parse(ttf []byte) (font *Font, err error) {
+ return parse(ttf, 0)
+}
+
+func parse(ttf []byte, offset int) (font *Font, err error) {
+ if len(ttf)-offset < 12 {
+ err = FormatError("TTF data is too short")
+ return
+ }
+ originalOffset := offset
+ magic, offset := u32(ttf, offset), offset+4
+ switch magic {
+ case 0x00010000:
+ // No-op.
+ case 0x74746366: // "ttcf" as a big-endian uint32.
+ if originalOffset != 0 {
+ err = FormatError("recursive TTC")
+ return
+ }
+ ttcVersion, offset := u32(ttf, offset), offset+4
+ if ttcVersion != 0x00010000 {
+ // TODO: support TTC version 2.0, once I have such a .ttc file to test with.
+ err = FormatError("bad TTC version")
+ return
+ }
+ numFonts, offset := int(u32(ttf, offset)), offset+4
+ if numFonts <= 0 {
+ err = FormatError("bad number of TTC fonts")
+ return
+ }
+ if len(ttf[offset:])/4 < numFonts {
+ err = FormatError("TTC offset table is too short")
+ return
+ }
+ // TODO: provide an API to select which font in a TrueType collection to return,
+ // not just the first one. This may require an API to parse a TTC's name tables,
+ // so users of this package can select the font in a TTC by name.
+ offset = int(u32(ttf, offset))
+ if offset <= 0 || offset > len(ttf) {
+ err = FormatError("bad TTC offset")
+ return
+ }
+ return parse(ttf, offset)
+ default:
+ err = FormatError("bad TTF version")
+ return
+ }
+ n, offset := int(u16(ttf, offset)), offset+2
+ if len(ttf) < 16*n+12 {
+ err = FormatError("TTF data is too short")
+ return
+ }
+ f := new(Font)
+ // Assign the table slices.
+ for i := 0; i < n; i++ {
+ x := 16*i + 12
+ switch string(ttf[x : x+4]) {
+ case "cmap":
+ f.cmap, err = readTable(ttf, ttf[x+8:x+16])
+ case "cvt ":
+ f.cvt, err = readTable(ttf, ttf[x+8:x+16])
+ case "fpgm":
+ f.fpgm, err = readTable(ttf, ttf[x+8:x+16])
+ case "glyf":
+ f.glyf, err = readTable(ttf, ttf[x+8:x+16])
+ case "hdmx":
+ f.hdmx, err = readTable(ttf, ttf[x+8:x+16])
+ case "head":
+ f.head, err = readTable(ttf, ttf[x+8:x+16])
+ case "hhea":
+ f.hhea, err = readTable(ttf, ttf[x+8:x+16])
+ case "hmtx":
+ f.hmtx, err = readTable(ttf, ttf[x+8:x+16])
+ case "kern":
+ f.kern, err = readTable(ttf, ttf[x+8:x+16])
+ case "loca":
+ f.loca, err = readTable(ttf, ttf[x+8:x+16])
+ case "maxp":
+ f.maxp, err = readTable(ttf, ttf[x+8:x+16])
+ case "name":
+ f.name, err = readTable(ttf, ttf[x+8:x+16])
+ case "OS/2":
+ f.os2, err = readTable(ttf, ttf[x+8:x+16])
+ case "prep":
+ f.prep, err = readTable(ttf, ttf[x+8:x+16])
+ case "vmtx":
+ f.vmtx, err = readTable(ttf, ttf[x+8:x+16])
+ }
+ if err != nil {
+ return
+ }
+ }
+ // Parse and sanity-check the TTF data.
+ if err = f.parseHead(); err != nil {
+ return
+ }
+ if err = f.parseMaxp(); err != nil {
+ return
+ }
+ if err = f.parseCmap(); err != nil {
+ return
+ }
+ if err = f.parseKern(); err != nil {
+ return
+ }
+ if err = f.parseHhea(); err != nil {
+ return
+ }
+ font = f
+ return
+}
diff --git a/vendor/github.com/golang/geo/r1/LICENSE b/vendor/github.com/golang/geo/r1/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/vendor/github.com/golang/geo/r1/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/vendor/github.com/golang/geo/r1/doc.go b/vendor/github.com/golang/geo/r1/doc.go
new file mode 100644
index 0000000..85f0cdc
--- /dev/null
+++ b/vendor/github.com/golang/geo/r1/doc.go
@@ -0,0 +1,22 @@
+/*
+Copyright 2014 Google Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/*
+Package r1 implements types and functions for working with geometry in ℝ¹.
+
+See ../s2 for a more detailed overview.
+*/
+package r1
diff --git a/vendor/github.com/golang/geo/r1/interval.go b/vendor/github.com/golang/geo/r1/interval.go
new file mode 100644
index 0000000..00fc64a
--- /dev/null
+++ b/vendor/github.com/golang/geo/r1/interval.go
@@ -0,0 +1,161 @@
+/*
+Copyright 2014 Google Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package r1
+
+import (
+ "fmt"
+ "math"
+)
+
+// Interval represents a closed interval on ℝ.
+// Zero-length intervals (where Lo == Hi) represent single points.
+// If Lo > Hi then the interval is empty.
+type Interval struct {
+ Lo, Hi float64
+}
+
+// EmptyInterval returns an empty interval.
+func EmptyInterval() Interval { return Interval{1, 0} }
+
+// IntervalFromPoint returns an interval representing a single point.
+func IntervalFromPoint(p float64) Interval { return Interval{p, p} }
+
+// IsEmpty reports whether the interval is empty.
+func (i Interval) IsEmpty() bool { return i.Lo > i.Hi }
+
+// Equal returns true iff the interval contains the same points as oi.
+func (i Interval) Equal(oi Interval) bool {
+ return i == oi || i.IsEmpty() && oi.IsEmpty()
+}
+
+// Center returns the midpoint of the interval.
+// It is undefined for empty intervals.
+func (i Interval) Center() float64 { return 0.5 * (i.Lo + i.Hi) }
+
+// Length returns the length of the interval.
+// The length of an empty interval is negative.
+func (i Interval) Length() float64 { return i.Hi - i.Lo }
+
+// Contains returns true iff the interval contains p.
+func (i Interval) Contains(p float64) bool { return i.Lo <= p && p <= i.Hi }
+
+// ContainsInterval returns true iff the interval contains oi.
+func (i Interval) ContainsInterval(oi Interval) bool {
+ if oi.IsEmpty() {
+ return true
+ }
+ return i.Lo <= oi.Lo && oi.Hi <= i.Hi
+}
+
+// InteriorContains returns true iff the the interval strictly contains p.
+func (i Interval) InteriorContains(p float64) bool {
+ return i.Lo < p && p < i.Hi
+}
+
+// InteriorContainsInterval returns true iff the interval strictly contains oi.
+func (i Interval) InteriorContainsInterval(oi Interval) bool {
+ if oi.IsEmpty() {
+ return true
+ }
+ return i.Lo < oi.Lo && oi.Hi < i.Hi
+}
+
+// Intersects returns true iff the interval contains any points in common with oi.
+func (i Interval) Intersects(oi Interval) bool {
+ if i.Lo <= oi.Lo {
+ return oi.Lo <= i.Hi && oi.Lo <= oi.Hi // oi.Lo ∈ i and oi is not empty
+ }
+ return i.Lo <= oi.Hi && i.Lo <= i.Hi // i.Lo ∈ oi and i is not empty
+}
+
+// InteriorIntersects returns true iff the interior of the interval contains any points in common with oi, including the latter's boundary.
+func (i Interval) InteriorIntersects(oi Interval) bool {
+ return oi.Lo < i.Hi && i.Lo < oi.Hi && i.Lo < i.Hi && oi.Lo <= i.Hi
+}
+
+// Intersection returns the interval containing all points common to i and j.
+func (i Interval) Intersection(j Interval) Interval {
+ // Empty intervals do not need to be special-cased.
+ return Interval{
+ Lo: math.Max(i.Lo, j.Lo),
+ Hi: math.Min(i.Hi, j.Hi),
+ }
+}
+
+// AddPoint returns the interval expanded so that it contains the given point.
+func (i Interval) AddPoint(p float64) Interval {
+ if i.IsEmpty() {
+ return Interval{p, p}
+ }
+ if p < i.Lo {
+ return Interval{p, i.Hi}
+ }
+ if p > i.Hi {
+ return Interval{i.Lo, p}
+ }
+ return i
+}
+
+// ClampPoint returns the closest point in the interval to the given point "p".
+// The interval must be non-empty.
+func (i Interval) ClampPoint(p float64) float64 {
+ return math.Max(i.Lo, math.Min(i.Hi, p))
+}
+
+// Expanded returns an interval that has been expanded on each side by margin.
+// If margin is negative, then the function shrinks the interval on
+// each side by margin instead. The resulting interval may be empty. Any
+// expansion of an empty interval remains empty.
+func (i Interval) Expanded(margin float64) Interval {
+ if i.IsEmpty() {
+ return i
+ }
+ return Interval{i.Lo - margin, i.Hi + margin}
+}
+
+// Union returns the smallest interval that contains this interval and the given interval.
+func (i Interval) Union(other Interval) Interval {
+ if i.IsEmpty() {
+ return other
+ }
+ if other.IsEmpty() {
+ return i
+ }
+ return Interval{math.Min(i.Lo, other.Lo), math.Max(i.Hi, other.Hi)}
+}
+
+func (i Interval) String() string { return fmt.Sprintf("[%.7f, %.7f]", i.Lo, i.Hi) }
+
+// epsilon is a small number that represents a reasonable level of noise between two
+// values that can be considered to be equal.
+const epsilon = 1e-14
+
+// ApproxEqual reports whether the interval can be transformed into the
+// given interval by moving each endpoint a small distance.
+// The empty interval is considered to be positioned arbitrarily on the
+// real line, so any interval with a small enough length will match
+// the empty interval.
+func (i Interval) ApproxEqual(other Interval) bool {
+ if i.IsEmpty() {
+ return other.Length() <= 2*epsilon
+ }
+ if other.IsEmpty() {
+ return i.Length() <= 2*epsilon
+ }
+ return math.Abs(other.Lo-i.Lo) <= epsilon &&
+ math.Abs(other.Hi-i.Hi) <= epsilon
+}
diff --git a/vendor/github.com/golang/geo/r2/LICENSE b/vendor/github.com/golang/geo/r2/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/vendor/github.com/golang/geo/r2/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/vendor/github.com/golang/geo/r2/doc.go b/vendor/github.com/golang/geo/r2/doc.go
new file mode 100644
index 0000000..aa962ce
--- /dev/null
+++ b/vendor/github.com/golang/geo/r2/doc.go
@@ -0,0 +1,22 @@
+/*
+Copyright 2014 Google Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/*
+Package r2 implements types and functions for working with geometry in ℝ².
+
+See package s2 for a more detailed overview.
+*/
+package r2
diff --git a/vendor/github.com/golang/geo/r2/rect.go b/vendor/github.com/golang/geo/r2/rect.go
new file mode 100644
index 0000000..7148bd4
--- /dev/null
+++ b/vendor/github.com/golang/geo/r2/rect.go
@@ -0,0 +1,257 @@
+/*
+Copyright 2014 Google Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package r2
+
+import (
+ "fmt"
+ "math"
+
+ "github.com/golang/geo/r1"
+)
+
+// Point represents a point in ℝ².
+type Point struct {
+ X, Y float64
+}
+
+// Add returns the sum of p and op.
+func (p Point) Add(op Point) Point { return Point{p.X + op.X, p.Y + op.Y} }
+
+// Sub returns the difference of p and op.
+func (p Point) Sub(op Point) Point { return Point{p.X - op.X, p.Y - op.Y} }
+
+// Mul returns the scalar product of p and m.
+func (p Point) Mul(m float64) Point { return Point{m * p.X, m * p.Y} }
+
+// Ortho returns a counterclockwise orthogonal point with the same norm.
+func (p Point) Ortho() Point { return Point{-p.Y, p.X} }
+
+// Dot returns the dot product between p and op.
+func (p Point) Dot(op Point) float64 { return p.X*op.X + p.Y*op.Y }
+
+// Cross returns the cross product of p and op.
+func (p Point) Cross(op Point) float64 { return p.X*op.Y - p.Y*op.X }
+
+// Norm returns the vector's norm.
+func (p Point) Norm() float64 { return math.Hypot(p.X, p.Y) }
+
+// Normalize returns a unit point in the same direction as p.
+func (p Point) Normalize() Point {
+ if p.X == 0 && p.Y == 0 {
+ return p
+ }
+ return p.Mul(1 / p.Norm())
+}
+
+func (p Point) String() string { return fmt.Sprintf("(%.12f, %.12f)", p.X, p.Y) }
+
+// Rect represents a closed axis-aligned rectangle in the (x,y) plane.
+type Rect struct {
+ X, Y r1.Interval
+}
+
+// RectFromPoints constructs a rect that contains the given points.
+func RectFromPoints(pts ...Point) Rect {
+ // Because the default value on interval is 0,0, we need to manually
+ // define the interval from the first point passed in as our starting
+ // interval, otherwise we end up with the case of passing in
+ // Point{0.2, 0.3} and getting the starting Rect of {0, 0.2}, {0, 0.3}
+ // instead of the Rect {0.2, 0.2}, {0.3, 0.3} which is not correct.
+ if len(pts) == 0 {
+ return Rect{}
+ }
+
+ r := Rect{
+ X: r1.Interval{Lo: pts[0].X, Hi: pts[0].X},
+ Y: r1.Interval{Lo: pts[0].Y, Hi: pts[0].Y},
+ }
+
+ for _, p := range pts[1:] {
+ r = r.AddPoint(p)
+ }
+ return r
+}
+
+// RectFromCenterSize constructs a rectangle with the given center and size.
+// Both dimensions of size must be non-negative.
+func RectFromCenterSize(center, size Point) Rect {
+ return Rect{
+ r1.Interval{Lo: center.X - size.X/2, Hi: center.X + size.X/2},
+ r1.Interval{Lo: center.Y - size.Y/2, Hi: center.Y + size.Y/2},
+ }
+}
+
+// EmptyRect constructs the canonical empty rectangle. Use IsEmpty() to test
+// for empty rectangles, since they have more than one representation. A Rect{}
+// is not the same as the EmptyRect.
+func EmptyRect() Rect {
+ return Rect{r1.EmptyInterval(), r1.EmptyInterval()}
+}
+
+// IsValid reports whether the rectangle is valid.
+// This requires the width to be empty iff the height is empty.
+func (r Rect) IsValid() bool {
+ return r.X.IsEmpty() == r.Y.IsEmpty()
+}
+
+// IsEmpty reports whether the rectangle is empty.
+func (r Rect) IsEmpty() bool {
+ return r.X.IsEmpty()
+}
+
+// Vertices returns all four vertices of the rectangle. Vertices are returned in
+// CCW direction starting with the lower left corner.
+func (r Rect) Vertices() [4]Point {
+ return [4]Point{
+ {r.X.Lo, r.Y.Lo},
+ {r.X.Hi, r.Y.Lo},
+ {r.X.Hi, r.Y.Hi},
+ {r.X.Lo, r.Y.Hi},
+ }
+}
+
+// VertexIJ returns the vertex in direction i along the X-axis (0=left, 1=right) and
+// direction j along the Y-axis (0=down, 1=up).
+func (r Rect) VertexIJ(i, j int) Point {
+ x := r.X.Lo
+ if i == 1 {
+ x = r.X.Hi
+ }
+ y := r.Y.Lo
+ if j == 1 {
+ y = r.Y.Hi
+ }
+ return Point{x, y}
+}
+
+// Lo returns the low corner of the rect.
+func (r Rect) Lo() Point {
+ return Point{r.X.Lo, r.Y.Lo}
+}
+
+// Hi returns the high corner of the rect.
+func (r Rect) Hi() Point {
+ return Point{r.X.Hi, r.Y.Hi}
+}
+
+// Center returns the center of the rectangle in (x,y)-space
+func (r Rect) Center() Point {
+ return Point{r.X.Center(), r.Y.Center()}
+}
+
+// Size returns the width and height of this rectangle in (x,y)-space. Empty
+// rectangles have a negative width and height.
+func (r Rect) Size() Point {
+ return Point{r.X.Length(), r.Y.Length()}
+}
+
+// ContainsPoint reports whether the rectangle contains the given point.
+// Rectangles are closed regions, i.e. they contain their boundary.
+func (r Rect) ContainsPoint(p Point) bool {
+ return r.X.Contains(p.X) && r.Y.Contains(p.Y)
+}
+
+// InteriorContainsPoint returns true iff the given point is contained in the interior
+// of the region (i.e. the region excluding its boundary).
+func (r Rect) InteriorContainsPoint(p Point) bool {
+ return r.X.InteriorContains(p.X) && r.Y.InteriorContains(p.Y)
+}
+
+// Contains reports whether the rectangle contains the given rectangle.
+func (r Rect) Contains(other Rect) bool {
+ return r.X.ContainsInterval(other.X) && r.Y.ContainsInterval(other.Y)
+}
+
+// InteriorContains reports whether the interior of this rectangle contains all of the
+// points of the given other rectangle (including its boundary).
+func (r Rect) InteriorContains(other Rect) bool {
+ return r.X.InteriorContainsInterval(other.X) && r.Y.InteriorContainsInterval(other.Y)
+}
+
+// Intersects reports whether this rectangle and the other rectangle have any points in common.
+func (r Rect) Intersects(other Rect) bool {
+ return r.X.Intersects(other.X) && r.Y.Intersects(other.Y)
+}
+
+// InteriorIntersects reports whether the interior of this rectangle intersects
+// any point (including the boundary) of the given other rectangle.
+func (r Rect) InteriorIntersects(other Rect) bool {
+ return r.X.InteriorIntersects(other.X) && r.Y.InteriorIntersects(other.Y)
+}
+
+// AddPoint expands the rectangle to include the given point. The rectangle is
+// expanded by the minimum amount possible.
+func (r Rect) AddPoint(p Point) Rect {
+ return Rect{r.X.AddPoint(p.X), r.Y.AddPoint(p.Y)}
+}
+
+// AddRect expands the rectangle to include the given rectangle. This is the
+// same as replacing the rectangle by the union of the two rectangles, but
+// is more efficient.
+func (r Rect) AddRect(other Rect) Rect {
+ return Rect{r.X.Union(other.X), r.Y.Union(other.Y)}
+}
+
+// ClampPoint returns the closest point in the rectangle to the given point.
+// The rectangle must be non-empty.
+func (r Rect) ClampPoint(p Point) Point {
+ return Point{r.X.ClampPoint(p.X), r.Y.ClampPoint(p.Y)}
+}
+
+// Expanded returns a rectangle that has been expanded in the x-direction
+// by margin.X, and in y-direction by margin.Y. If either margin is empty,
+// then shrink the interval on the corresponding sides instead. The resulting
+// rectangle may be empty. Any expansion of an empty rectangle remains empty.
+func (r Rect) Expanded(margin Point) Rect {
+ xx := r.X.Expanded(margin.X)
+ yy := r.Y.Expanded(margin.Y)
+ if xx.IsEmpty() || yy.IsEmpty() {
+ return EmptyRect()
+ }
+ return Rect{xx, yy}
+}
+
+// ExpandedByMargin returns a Rect that has been expanded by the amount on all sides.
+func (r Rect) ExpandedByMargin(margin float64) Rect {
+ return r.Expanded(Point{margin, margin})
+}
+
+// Union returns the smallest rectangle containing the union of this rectangle and
+// the given rectangle.
+func (r Rect) Union(other Rect) Rect {
+ return Rect{r.X.Union(other.X), r.Y.Union(other.Y)}
+}
+
+// Intersection returns the smallest rectangle containing the intersection of this
+// rectangle and the given rectangle.
+func (r Rect) Intersection(other Rect) Rect {
+ xx := r.X.Intersection(other.X)
+ yy := r.Y.Intersection(other.Y)
+ if xx.IsEmpty() || yy.IsEmpty() {
+ return EmptyRect()
+ }
+
+ return Rect{xx, yy}
+}
+
+// ApproxEquals returns true if the x- and y-intervals of the two rectangles are
+// the same up to the given tolerance.
+func (r Rect) ApproxEquals(r2 Rect) bool {
+ return r.X.ApproxEqual(r2.X) && r.Y.ApproxEqual(r2.Y)
+}
+
+func (r Rect) String() string { return fmt.Sprintf("[Lo%s, Hi%s]", r.Lo(), r.Hi()) }
diff --git a/vendor/github.com/golang/geo/r3/LICENSE b/vendor/github.com/golang/geo/r3/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/vendor/github.com/golang/geo/r3/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/vendor/github.com/golang/geo/r3/doc.go b/vendor/github.com/golang/geo/r3/doc.go
new file mode 100644
index 0000000..666bee5
--- /dev/null
+++ b/vendor/github.com/golang/geo/r3/doc.go
@@ -0,0 +1,22 @@
+/*
+Copyright 2014 Google Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/*
+Package r3 implements types and functions for working with geometry in ℝ³.
+
+See ../s2 for a more detailed overview.
+*/
+package r3
diff --git a/vendor/github.com/golang/geo/r3/precisevector.go b/vendor/github.com/golang/geo/r3/precisevector.go
new file mode 100644
index 0000000..2d92d03
--- /dev/null
+++ b/vendor/github.com/golang/geo/r3/precisevector.go
@@ -0,0 +1,200 @@
+/*
+Copyright 2016 Google Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package r3
+
+import (
+ "fmt"
+ "math/big"
+)
+
+const (
+ // prec is the number of bits of precision to use for the Float values.
+ // To keep things simple, we use the maximum allowable precision on big
+ // values. This allows us to handle all values we expect in the s2 library.
+ prec = big.MaxPrec
+)
+
+// define some commonly referenced values.
+var (
+ precise0 = precInt(0)
+ precise1 = precInt(1)
+)
+
+// precStr wraps the conversion from a string into a big.Float. For results that
+// actually can be represented exactly, this should only be used on values that
+// are integer multiples of integer powers of 2.
+func precStr(s string) *big.Float {
+ // Explicitly ignoring the bool return for this usage.
+ f, _ := new(big.Float).SetPrec(prec).SetString(s)
+ return f
+}
+
+func precInt(i int64) *big.Float {
+ return new(big.Float).SetPrec(prec).SetInt64(i)
+}
+
+func precFloat(f float64) *big.Float {
+ return new(big.Float).SetPrec(prec).SetFloat64(f)
+}
+
+func precAdd(a, b *big.Float) *big.Float {
+ return new(big.Float).SetPrec(prec).Add(a, b)
+}
+
+func precSub(a, b *big.Float) *big.Float {
+ return new(big.Float).SetPrec(prec).Sub(a, b)
+}
+
+func precMul(a, b *big.Float) *big.Float {
+ return new(big.Float).SetPrec(prec).Mul(a, b)
+}
+
+// PreciseVector represents a point in ℝ³ using high-precision values.
+// Note that this is NOT a complete implementation because there are some
+// operations that Vector supports that are not feasible with arbitrary precision
+// math. (e.g., methods that need divison like Normalize, or methods needing a
+// square root operation such as Norm)
+type PreciseVector struct {
+ X, Y, Z *big.Float
+}
+
+// PreciseVectorFromVector creates a high precision vector from the given Vector.
+func PreciseVectorFromVector(v Vector) PreciseVector {
+ return NewPreciseVector(v.X, v.Y, v.Z)
+}
+
+// NewPreciseVector creates a high precision vector from the given floating point values.
+func NewPreciseVector(x, y, z float64) PreciseVector {
+ return PreciseVector{
+ X: precFloat(x),
+ Y: precFloat(y),
+ Z: precFloat(z),
+ }
+}
+
+// Vector returns this precise vector converted to a Vector.
+func (v PreciseVector) Vector() Vector {
+ // The accuracy flag is ignored on these conversions back to float64.
+ x, _ := v.X.Float64()
+ y, _ := v.Y.Float64()
+ z, _ := v.Z.Float64()
+ return Vector{x, y, z}
+}
+
+// Equals reports whether v and ov are equal.
+func (v PreciseVector) Equals(ov PreciseVector) bool {
+ return v.X.Cmp(ov.X) == 0 && v.Y.Cmp(ov.Y) == 0 && v.Z.Cmp(ov.Z) == 0
+}
+
+func (v PreciseVector) String() string {
+ return fmt.Sprintf("(%v, %v, %v)", v.X, v.Y, v.Z)
+}
+
+// Norm2 returns the square of the norm.
+func (v PreciseVector) Norm2() *big.Float { return v.Dot(v) }
+
+// IsUnit reports whether this vector is of unit length.
+func (v PreciseVector) IsUnit() bool {
+ return v.Norm2().Cmp(precise1) == 0
+}
+
+// Abs returns the vector with nonnegative components.
+func (v PreciseVector) Abs() PreciseVector {
+ return PreciseVector{
+ X: new(big.Float).Abs(v.X),
+ Y: new(big.Float).Abs(v.Y),
+ Z: new(big.Float).Abs(v.Z),
+ }
+}
+
+// Add returns the standard vector sum of v and ov.
+func (v PreciseVector) Add(ov PreciseVector) PreciseVector {
+ return PreciseVector{
+ X: precAdd(v.X, ov.X),
+ Y: precAdd(v.Y, ov.Y),
+ Z: precAdd(v.Z, ov.Z),
+ }
+}
+
+// Sub returns the standard vector difference of v and ov.
+func (v PreciseVector) Sub(ov PreciseVector) PreciseVector {
+ return PreciseVector{
+ X: precSub(v.X, ov.X),
+ Y: precSub(v.Y, ov.Y),
+ Z: precSub(v.Z, ov.Z),
+ }
+}
+
+// Mul returns the standard scalar product of v and f.
+func (v PreciseVector) Mul(f *big.Float) PreciseVector {
+ return PreciseVector{
+ X: precMul(v.X, f),
+ Y: precMul(v.Y, f),
+ Z: precMul(v.Z, f),
+ }
+}
+
+// MulByFloat64 returns the standard scalar product of v and f.
+func (v PreciseVector) MulByFloat64(f float64) PreciseVector {
+ return v.Mul(precFloat(f))
+}
+
+// Dot returns the standard dot product of v and ov.
+func (v PreciseVector) Dot(ov PreciseVector) *big.Float {
+ return precAdd(precMul(v.X, ov.X), precAdd(precMul(v.Y, ov.Y), precMul(v.Z, ov.Z)))
+}
+
+// Cross returns the standard cross product of v and ov.
+func (v PreciseVector) Cross(ov PreciseVector) PreciseVector {
+ return PreciseVector{
+ X: precSub(precMul(v.Y, ov.Z), precMul(v.Z, ov.Y)),
+ Y: precSub(precMul(v.Z, ov.X), precMul(v.X, ov.Z)),
+ Z: precSub(precMul(v.X, ov.Y), precMul(v.Y, ov.X)),
+ }
+}
+
+// LargestComponent returns the axis that represents the largest component in this vector.
+func (v PreciseVector) LargestComponent() Axis {
+ t := v.Abs()
+
+ if t.X.Cmp(t.Y) > 0 {
+ if t.X.Cmp(t.Z) > 0 {
+ return XAxis
+ }
+ return ZAxis
+ }
+ if t.Y.Cmp(t.Z) > 0 {
+ return YAxis
+ }
+ return ZAxis
+}
+
+// SmallestComponent returns the axis that represents the smallest component in this vector.
+func (v PreciseVector) SmallestComponent() Axis {
+ t := v.Abs()
+
+ if t.X.Cmp(t.Y) < 0 {
+ if t.X.Cmp(t.Z) < 0 {
+ return XAxis
+ }
+ return ZAxis
+ }
+ if t.Y.Cmp(t.Z) < 0 {
+ return YAxis
+ }
+ return ZAxis
+}
diff --git a/vendor/github.com/golang/geo/r3/vector.go b/vendor/github.com/golang/geo/r3/vector.go
new file mode 100644
index 0000000..f39bf3a
--- /dev/null
+++ b/vendor/github.com/golang/geo/r3/vector.go
@@ -0,0 +1,184 @@
+/*
+Copyright 2014 Google Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package r3
+
+import (
+ "fmt"
+ "math"
+
+ "github.com/golang/geo/s1"
+)
+
+// Vector represents a point in ℝ³.
+type Vector struct {
+ X, Y, Z float64
+}
+
+// ApproxEqual reports whether v and ov are equal within a small epsilon.
+func (v Vector) ApproxEqual(ov Vector) bool {
+ const epsilon = 1e-16
+ return math.Abs(v.X-ov.X) < epsilon && math.Abs(v.Y-ov.Y) < epsilon && math.Abs(v.Z-ov.Z) < epsilon
+}
+
+func (v Vector) String() string { return fmt.Sprintf("(%0.24f, %0.24f, %0.24f)", v.X, v.Y, v.Z) }
+
+// Norm returns the vector's norm.
+func (v Vector) Norm() float64 { return math.Sqrt(v.Dot(v)) }
+
+// Norm2 returns the square of the norm.
+func (v Vector) Norm2() float64 { return v.Dot(v) }
+
+// Normalize returns a unit vector in the same direction as v.
+func (v Vector) Normalize() Vector {
+ if v == (Vector{0, 0, 0}) {
+ return v
+ }
+ return v.Mul(1 / v.Norm())
+}
+
+// IsUnit returns whether this vector is of approximately unit length.
+func (v Vector) IsUnit() bool {
+ const epsilon = 5e-14
+ return math.Abs(v.Norm2()-1) <= epsilon
+}
+
+// Abs returns the vector with nonnegative components.
+func (v Vector) Abs() Vector { return Vector{math.Abs(v.X), math.Abs(v.Y), math.Abs(v.Z)} }
+
+// Add returns the standard vector sum of v and ov.
+func (v Vector) Add(ov Vector) Vector { return Vector{v.X + ov.X, v.Y + ov.Y, v.Z + ov.Z} }
+
+// Sub returns the standard vector difference of v and ov.
+func (v Vector) Sub(ov Vector) Vector { return Vector{v.X - ov.X, v.Y - ov.Y, v.Z - ov.Z} }
+
+// Mul returns the standard scalar product of v and m.
+func (v Vector) Mul(m float64) Vector { return Vector{m * v.X, m * v.Y, m * v.Z} }
+
+// Dot returns the standard dot product of v and ov.
+func (v Vector) Dot(ov Vector) float64 { return v.X*ov.X + v.Y*ov.Y + v.Z*ov.Z }
+
+// Cross returns the standard cross product of v and ov.
+func (v Vector) Cross(ov Vector) Vector {
+ return Vector{
+ v.Y*ov.Z - v.Z*ov.Y,
+ v.Z*ov.X - v.X*ov.Z,
+ v.X*ov.Y - v.Y*ov.X,
+ }
+}
+
+// Distance returns the Euclidean distance between v and ov.
+func (v Vector) Distance(ov Vector) float64 { return v.Sub(ov).Norm() }
+
+// Angle returns the angle between v and ov.
+func (v Vector) Angle(ov Vector) s1.Angle {
+ return s1.Angle(math.Atan2(v.Cross(ov).Norm(), v.Dot(ov))) * s1.Radian
+}
+
+// Axis enumerates the 3 axes of ℝ³.
+type Axis int
+
+// The three axes of ℝ³.
+const (
+ XAxis Axis = iota
+ YAxis
+ ZAxis
+)
+
+// Ortho returns a unit vector that is orthogonal to v.
+// Ortho(-v) = -Ortho(v) for all v.
+func (v Vector) Ortho() Vector {
+ ov := Vector{0.012, 0.0053, 0.00457}
+ switch v.LargestComponent() {
+ case XAxis:
+ ov.Z = 1
+ case YAxis:
+ ov.X = 1
+ default:
+ ov.Y = 1
+ }
+ return v.Cross(ov).Normalize()
+}
+
+// LargestComponent returns the axis that represents the largest component in this vector.
+func (v Vector) LargestComponent() Axis {
+ t := v.Abs()
+
+ if t.X > t.Y {
+ if t.X > t.Z {
+ return XAxis
+ }
+ return ZAxis
+ }
+ if t.Y > t.Z {
+ return YAxis
+ }
+ return ZAxis
+}
+
+// SmallestComponent returns the axis that represents the smallest component in this vector.
+func (v Vector) SmallestComponent() Axis {
+ t := v.Abs()
+
+ if t.X < t.Y {
+ if t.X < t.Z {
+ return XAxis
+ }
+ return ZAxis
+ }
+ if t.Y < t.Z {
+ return YAxis
+ }
+ return ZAxis
+}
+
+// Cmp compares v and ov lexicographically and returns:
+//
+// -1 if v < ov
+// 0 if v == ov
+// +1 if v > ov
+//
+// This method is based on C++'s std::lexicographical_compare. Two entities
+// are compared element by element with the given operator. The first mismatch
+// defines which is less (or greater) than the other. If both have equivalent
+// values they are lexicographically equal.
+func (v Vector) Cmp(ov Vector) int {
+ if v.X < ov.X {
+ return -1
+ }
+ if v.X > ov.X {
+ return 1
+ }
+
+ // First elements were the same, try the next.
+ if v.Y < ov.Y {
+ return -1
+ }
+ if v.Y > ov.Y {
+ return 1
+ }
+
+ // Second elements were the same return the final compare.
+ if v.Z < ov.Z {
+ return -1
+ }
+ if v.Z > ov.Z {
+ return 1
+ }
+
+ // Both are equal
+ return 0
+}
diff --git a/vendor/github.com/golang/geo/s1/LICENSE b/vendor/github.com/golang/geo/s1/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/vendor/github.com/golang/geo/s1/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/vendor/github.com/golang/geo/s1/angle.go b/vendor/github.com/golang/geo/s1/angle.go
new file mode 100644
index 0000000..5b3a25c
--- /dev/null
+++ b/vendor/github.com/golang/geo/s1/angle.go
@@ -0,0 +1,119 @@
+/*
+Copyright 2014 Google Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package s1
+
+import (
+ "math"
+ "strconv"
+)
+
+// Angle represents a 1D angle. The internal representation is a double precision
+// value in radians, so conversion to and from radians is exact.
+// Conversions between E5, E6, E7, and Degrees are not always
+// exact. For example, Degrees(3.1) is different from E6(3100000) or E7(310000000).
+//
+// The following conversions between degrees and radians are exact:
+//
+// Degree*180 == Radian*math.Pi
+// Degree*(180/n) == Radian*(math.Pi/n) for n == 0..8
+//
+// These identities hold when the arguments are scaled up or down by any power
+// of 2. Some similar identities are also true, for example,
+//
+// Degree*60 == Radian*(math.Pi/3)
+//
+// But be aware that this type of identity does not hold in general. For example,
+//
+// Degree*3 != Radian*(math.Pi/60)
+//
+// Similarly, the conversion to radians means that (Angle(x)*Degree).Degrees()
+// does not always equal x. For example,
+//
+// (Angle(45*n)*Degree).Degrees() == 45*n for n == 0..8
+//
+// but
+//
+// (60*Degree).Degrees() != 60
+//
+// When testing for equality, you should allow for numerical errors (floatApproxEq)
+// or convert to discrete E5/E6/E7 values first.
+type Angle float64
+
+// Angle units.
+const (
+ Radian Angle = 1
+ Degree = (math.Pi / 180) * Radian
+
+ E5 = 1e-5 * Degree
+ E6 = 1e-6 * Degree
+ E7 = 1e-7 * Degree
+)
+
+// Radians returns the angle in radians.
+func (a Angle) Radians() float64 { return float64(a) }
+
+// Degrees returns the angle in degrees.
+func (a Angle) Degrees() float64 { return float64(a / Degree) }
+
+// round returns the value rounded to nearest as an int32.
+// This does not match C++ exactly for the case of x.5.
+func round(val float64) int32 {
+ if val < 0 {
+ return int32(val - 0.5)
+ }
+ return int32(val + 0.5)
+}
+
+// InfAngle returns an angle larger than any finite angle.
+func InfAngle() Angle {
+ return Angle(math.Inf(1))
+}
+
+// isInf reports whether this Angle is infinite.
+func (a Angle) isInf() bool {
+ return math.IsInf(float64(a), 0)
+}
+
+// E5 returns the angle in hundred thousandths of degrees.
+func (a Angle) E5() int32 { return round(a.Degrees() * 1e5) }
+
+// E6 returns the angle in millionths of degrees.
+func (a Angle) E6() int32 { return round(a.Degrees() * 1e6) }
+
+// E7 returns the angle in ten millionths of degrees.
+func (a Angle) E7() int32 { return round(a.Degrees() * 1e7) }
+
+// Abs returns the absolute value of the angle.
+func (a Angle) Abs() Angle { return Angle(math.Abs(float64(a))) }
+
+// Normalized returns an equivalent angle in [0, 2π).
+func (a Angle) Normalized() Angle {
+ rad := math.Mod(float64(a), 2*math.Pi)
+ if rad < 0 {
+ rad += 2 * math.Pi
+ }
+ return Angle(rad)
+}
+
+func (a Angle) String() string {
+ return strconv.FormatFloat(a.Degrees(), 'f', 7, 64) // like "%.7f"
+}
+
+// BUG(dsymonds): The major differences from the C++ version are:
+// - no unsigned E5/E6/E7 methods
+// - no S2Point or S2LatLng constructors
+// - no comparison or arithmetic operators
diff --git a/vendor/github.com/golang/geo/s1/chordangle.go b/vendor/github.com/golang/geo/s1/chordangle.go
new file mode 100644
index 0000000..5f5832a
--- /dev/null
+++ b/vendor/github.com/golang/geo/s1/chordangle.go
@@ -0,0 +1,202 @@
+/*
+Copyright 2015 Google Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package s1
+
+import (
+ "math"
+)
+
+// ChordAngle represents the angle subtended by a chord (i.e., the straight
+// line segment connecting two points on the sphere). Its representation
+// makes it very efficient for computing and comparing distances, but unlike
+// Angle it is only capable of representing angles between 0 and π radians.
+// Generally, ChordAngle should only be used in loops where many angles need
+// to be calculated and compared. Otherwise it is simpler to use Angle.
+//
+// ChordAngle loses some accuracy as the angle approaches π radians.
+// Specifically, the representation of (π - x) radians has an error of about
+// (1e-15 / x), with a maximum error of about 2e-8 radians (about 13cm on the
+// Earth's surface). For comparison, for angles up to π/2 radians (10000km)
+// the worst-case representation error is about 2e-16 radians (1 nanonmeter),
+// which is about the same as Angle.
+//
+// ChordAngles are represented by the squared chord length, which can
+// range from 0 to 4. Positive infinity represents an infinite squared length.
+type ChordAngle float64
+
+const (
+ // NegativeChordAngle represents a chord angle smaller than the zero angle.
+ // The only valid operations on a NegativeChordAngle are comparisons and
+ // Angle conversions.
+ NegativeChordAngle = ChordAngle(-1)
+
+ // RightChordAngle represents a chord angle of 90 degrees (a "right angle").
+ RightChordAngle = ChordAngle(2)
+
+ // StraightChordAngle represents a chord angle of 180 degrees (a "straight angle").
+ // This is the maximum finite chord angle.
+ StraightChordAngle = ChordAngle(4)
+)
+
+// ChordAngleFromAngle returns a ChordAngle from the given Angle.
+func ChordAngleFromAngle(a Angle) ChordAngle {
+ if a < 0 {
+ return NegativeChordAngle
+ }
+ if a.isInf() {
+ return InfChordAngle()
+ }
+ l := 2 * math.Sin(0.5*math.Min(math.Pi, a.Radians()))
+ return ChordAngle(l * l)
+}
+
+// ChordAngleFromSquaredLength returns a ChordAngle from the squared chord length.
+// Note that the argument is automatically clamped to a maximum of 4.0 to
+// handle possible roundoff errors. The argument must be non-negative.
+func ChordAngleFromSquaredLength(length2 float64) ChordAngle {
+ if length2 > 4 {
+ return StraightChordAngle
+ }
+ return ChordAngle(length2)
+}
+
+// Expanded returns a new ChordAngle that has been adjusted by the given error
+// bound (which can be positive or negative). Error should be the value
+// returned by either MaxPointError or MaxAngleError. For example:
+// a := ChordAngleFromPoints(x, y)
+// a1 := a.Expanded(a.MaxPointError())
+func (c ChordAngle) Expanded(e float64) ChordAngle {
+ // If the angle is special, don't change it. Otherwise clamp it to the valid range.
+ if c.isSpecial() {
+ return c
+ }
+ return ChordAngle(math.Max(0.0, math.Min(4.0, float64(c)+e)))
+}
+
+// Angle converts this ChordAngle to an Angle.
+func (c ChordAngle) Angle() Angle {
+ if c < 0 {
+ return -1 * Radian
+ }
+ if c.isInf() {
+ return InfAngle()
+ }
+ return Angle(2 * math.Asin(0.5*math.Sqrt(float64(c))))
+}
+
+// InfChordAngle returns a chord angle larger than any finite chord angle.
+// The only valid operations on an InfChordAngle are comparisons and Angle conversions.
+func InfChordAngle() ChordAngle {
+ return ChordAngle(math.Inf(1))
+}
+
+// isInf reports whether this ChordAngle is infinite.
+func (c ChordAngle) isInf() bool {
+ return math.IsInf(float64(c), 1)
+}
+
+// isSpecial reports whether this ChordAngle is one of the special cases.
+func (c ChordAngle) isSpecial() bool {
+ return c < 0 || c.isInf()
+}
+
+// isValid reports whether this ChordAngle is valid or not.
+func (c ChordAngle) isValid() bool {
+ return (c >= 0 && c <= 4) || c.isSpecial()
+}
+
+// MaxPointError returns the maximum error size for a ChordAngle constructed
+// from 2 Points x and y, assuming that x and y are normalized to within the
+// bounds guaranteed by s2.Point.Normalize. The error is defined with respect to
+// the true distance after the points are projected to lie exactly on the sphere.
+func (c ChordAngle) MaxPointError() float64 {
+ // There is a relative error of (2.5*dblEpsilon) when computing the squared
+ // distance, plus an absolute error of (16 * dblEpsilon**2) because the
+ // lengths of the input points may differ from 1 by up to (2*dblEpsilon) each.
+ return 2.5*dblEpsilon*float64(c) + 16*dblEpsilon*dblEpsilon
+}
+
+// MaxAngleError returns the maximum error for a ChordAngle constructed
+// as an Angle distance.
+func (c ChordAngle) MaxAngleError() float64 {
+ return dblEpsilon * float64(c)
+}
+
+// Add adds the other ChordAngle to this one and returns the resulting value.
+// This method assumes the ChordAngles are not special.
+func (c ChordAngle) Add(other ChordAngle) ChordAngle {
+ // Note that this method (and Sub) is much more efficient than converting
+ // the ChordAngle to an Angle and adding those and converting back. It
+ // requires only one square root plus a few additions and multiplications.
+
+ // Optimization for the common case where b is an error tolerance
+ // parameter that happens to be set to zero.
+ if other == 0 {
+ return c
+ }
+
+ // Clamp the angle sum to at most 180 degrees.
+ if c+other >= 4 {
+ return StraightChordAngle
+ }
+
+ // Let a and b be the (non-squared) chord lengths, and let c = a+b.
+ // Let A, B, and C be the corresponding half-angles (a = 2*sin(A), etc).
+ // Then the formula below can be derived from c = 2 * sin(A+B) and the
+ // relationships sin(A+B) = sin(A)*cos(B) + sin(B)*cos(A)
+ // cos(X) = sqrt(1 - sin^2(X))
+ x := float64(c * (1 - 0.25*other))
+ y := float64(other * (1 - 0.25*c))
+ return ChordAngle(math.Min(4.0, x+y+2*math.Sqrt(x*y)))
+}
+
+// Sub subtracts the other ChordAngle from this one and returns the resulting value.
+// This method assumes the ChordAngles are not special.
+func (c ChordAngle) Sub(other ChordAngle) ChordAngle {
+ if other == 0 {
+ return c
+ }
+ if c <= other {
+ return 0
+ }
+ x := float64(c * (1 - 0.25*other))
+ y := float64(other * (1 - 0.25*c))
+ return ChordAngle(math.Max(0.0, x+y-2*math.Sqrt(x*y)))
+}
+
+// Sin returns the sine of this chord angle. This method is more efficient
+// than converting to Angle and performing the computation.
+func (c ChordAngle) Sin() float64 {
+ // Let a be the (non-squared) chord length, and let A be the corresponding
+ // half-angle (a = 2*sin(A)). The formula below can be derived from:
+ // sin(2*A) = 2 * sin(A) * cos(A)
+ // cos^2(A) = 1 - sin^2(A)
+ // This is much faster than converting to an angle and computing its sine.
+ return math.Sqrt(float64(c * (1 - 0.25*c)))
+}
+
+// Cos returns the cosine of this chord angle. This method is more efficient
+// than converting to Angle and performing the computation.
+func (c ChordAngle) Cos() float64 {
+ // cos(2*A) = cos^2(A) - sin^2(A) = 1 - 2*sin^2(A)
+ return float64(1 - 0.5*c)
+}
+
+// Tan returns the tangent of this chord angle.
+func (c ChordAngle) Tan() float64 {
+ return c.Sin() / c.Cos()
+}
diff --git a/vendor/github.com/golang/geo/s1/doc.go b/vendor/github.com/golang/geo/s1/doc.go
new file mode 100644
index 0000000..b9fca50
--- /dev/null
+++ b/vendor/github.com/golang/geo/s1/doc.go
@@ -0,0 +1,22 @@
+/*
+Copyright 2014 Google Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/*
+Package s1 implements types and functions for working with geometry in S¹ (circular geometry).
+
+See ../s2 for a more detailed overview.
+*/
+package s1
diff --git a/vendor/github.com/golang/geo/s1/interval.go b/vendor/github.com/golang/geo/s1/interval.go
new file mode 100644
index 0000000..b9cd34b
--- /dev/null
+++ b/vendor/github.com/golang/geo/s1/interval.go
@@ -0,0 +1,350 @@
+/*
+Copyright 2014 Google Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package s1
+
+import (
+ "math"
+ "strconv"
+)
+
+// Interval represents a closed interval on a unit circle.
+// Zero-length intervals (where Lo == Hi) represent single points.
+// If Lo > Hi then the interval is "inverted".
+// The point at (-1, 0) on the unit circle has two valid representations,
+// [π,π] and [-π,-π]. We normalize the latter to the former in IntervalFromEndpoints.
+// There are two special intervals that take advantage of that:
+// - the full interval, [-π,π], and
+// - the empty interval, [π,-π].
+// Treat the exported fields as read-only.
+type Interval struct {
+ Lo, Hi float64
+}
+
+// IntervalFromEndpoints constructs a new interval from endpoints.
+// Both arguments must be in the range [-π,π]. This function allows inverted intervals
+// to be created.
+func IntervalFromEndpoints(lo, hi float64) Interval {
+ i := Interval{lo, hi}
+ if lo == -math.Pi && hi != math.Pi {
+ i.Lo = math.Pi
+ }
+ if hi == -math.Pi && lo != math.Pi {
+ i.Hi = math.Pi
+ }
+ return i
+}
+
+// IntervalFromPointPair returns the minimal interval containing the two given points.
+// Both arguments must be in [-π,π].
+func IntervalFromPointPair(a, b float64) Interval {
+ if a == -math.Pi {
+ a = math.Pi
+ }
+ if b == -math.Pi {
+ b = math.Pi
+ }
+ if positiveDistance(a, b) <= math.Pi {
+ return Interval{a, b}
+ }
+ return Interval{b, a}
+}
+
+// EmptyInterval returns an empty interval.
+func EmptyInterval() Interval { return Interval{math.Pi, -math.Pi} }
+
+// FullInterval returns a full interval.
+func FullInterval() Interval { return Interval{-math.Pi, math.Pi} }
+
+// IsValid reports whether the interval is valid.
+func (i Interval) IsValid() bool {
+ return (math.Abs(i.Lo) <= math.Pi && math.Abs(i.Hi) <= math.Pi &&
+ !(i.Lo == -math.Pi && i.Hi != math.Pi) &&
+ !(i.Hi == -math.Pi && i.Lo != math.Pi))
+}
+
+// IsFull reports whether the interval is full.
+func (i Interval) IsFull() bool { return i.Lo == -math.Pi && i.Hi == math.Pi }
+
+// IsEmpty reports whether the interval is empty.
+func (i Interval) IsEmpty() bool { return i.Lo == math.Pi && i.Hi == -math.Pi }
+
+// IsInverted reports whether the interval is inverted; that is, whether Lo > Hi.
+func (i Interval) IsInverted() bool { return i.Lo > i.Hi }
+
+// Invert returns the interval with endpoints swapped.
+func (i Interval) Invert() Interval {
+ return Interval{i.Hi, i.Lo}
+}
+
+// Center returns the midpoint of the interval.
+// It is undefined for full and empty intervals.
+func (i Interval) Center() float64 {
+ c := 0.5 * (i.Lo + i.Hi)
+ if !i.IsInverted() {
+ return c
+ }
+ if c <= 0 {
+ return c + math.Pi
+ }
+ return c - math.Pi
+}
+
+// Length returns the length of the interval.
+// The length of an empty interval is negative.
+func (i Interval) Length() float64 {
+ l := i.Hi - i.Lo
+ if l >= 0 {
+ return l
+ }
+ l += 2 * math.Pi
+ if l > 0 {
+ return l
+ }
+ return -1
+}
+
+// Assumes p ∈ (-π,π].
+func (i Interval) fastContains(p float64) bool {
+ if i.IsInverted() {
+ return (p >= i.Lo || p <= i.Hi) && !i.IsEmpty()
+ }
+ return p >= i.Lo && p <= i.Hi
+}
+
+// Contains returns true iff the interval contains p.
+// Assumes p ∈ [-π,π].
+func (i Interval) Contains(p float64) bool {
+ if p == -math.Pi {
+ p = math.Pi
+ }
+ return i.fastContains(p)
+}
+
+// ContainsInterval returns true iff the interval contains oi.
+func (i Interval) ContainsInterval(oi Interval) bool {
+ if i.IsInverted() {
+ if oi.IsInverted() {
+ return oi.Lo >= i.Lo && oi.Hi <= i.Hi
+ }
+ return (oi.Lo >= i.Lo || oi.Hi <= i.Hi) && !i.IsEmpty()
+ }
+ if oi.IsInverted() {
+ return i.IsFull() || oi.IsEmpty()
+ }
+ return oi.Lo >= i.Lo && oi.Hi <= i.Hi
+}
+
+// InteriorContains returns true iff the interior of the interval contains p.
+// Assumes p ∈ [-π,π].
+func (i Interval) InteriorContains(p float64) bool {
+ if p == -math.Pi {
+ p = math.Pi
+ }
+ if i.IsInverted() {
+ return p > i.Lo || p < i.Hi
+ }
+ return (p > i.Lo && p < i.Hi) || i.IsFull()
+}
+
+// InteriorContainsInterval returns true iff the interior of the interval contains oi.
+func (i Interval) InteriorContainsInterval(oi Interval) bool {
+ if i.IsInverted() {
+ if oi.IsInverted() {
+ return (oi.Lo > i.Lo && oi.Hi < i.Hi) || oi.IsEmpty()
+ }
+ return oi.Lo > i.Lo || oi.Hi < i.Hi
+ }
+ if oi.IsInverted() {
+ return i.IsFull() || oi.IsEmpty()
+ }
+ return (oi.Lo > i.Lo && oi.Hi < i.Hi) || i.IsFull()
+}
+
+// Intersects returns true iff the interval contains any points in common with oi.
+func (i Interval) Intersects(oi Interval) bool {
+ if i.IsEmpty() || oi.IsEmpty() {
+ return false
+ }
+ if i.IsInverted() {
+ return oi.IsInverted() || oi.Lo <= i.Hi || oi.Hi >= i.Lo
+ }
+ if oi.IsInverted() {
+ return oi.Lo <= i.Hi || oi.Hi >= i.Lo
+ }
+ return oi.Lo <= i.Hi && oi.Hi >= i.Lo
+}
+
+// InteriorIntersects returns true iff the interior of the interval contains any points in common with oi, including the latter's boundary.
+func (i Interval) InteriorIntersects(oi Interval) bool {
+ if i.IsEmpty() || oi.IsEmpty() || i.Lo == i.Hi {
+ return false
+ }
+ if i.IsInverted() {
+ return oi.IsInverted() || oi.Lo < i.Hi || oi.Hi > i.Lo
+ }
+ if oi.IsInverted() {
+ return oi.Lo < i.Hi || oi.Hi > i.Lo
+ }
+ return (oi.Lo < i.Hi && oi.Hi > i.Lo) || i.IsFull()
+}
+
+// Compute distance from a to b in [0,2π], in a numerically stable way.
+func positiveDistance(a, b float64) float64 {
+ d := b - a
+ if d >= 0 {
+ return d
+ }
+ return (b + math.Pi) - (a - math.Pi)
+}
+
+// Union returns the smallest interval that contains both the interval and oi.
+func (i Interval) Union(oi Interval) Interval {
+ if oi.IsEmpty() {
+ return i
+ }
+ if i.fastContains(oi.Lo) {
+ if i.fastContains(oi.Hi) {
+ // Either oi ⊂ i, or i ∪ oi is the full interval.
+ if i.ContainsInterval(oi) {
+ return i
+ }
+ return FullInterval()
+ }
+ return Interval{i.Lo, oi.Hi}
+ }
+ if i.fastContains(oi.Hi) {
+ return Interval{oi.Lo, i.Hi}
+ }
+
+ // Neither endpoint of oi is in i. Either i ⊂ oi, or i and oi are disjoint.
+ if i.IsEmpty() || oi.fastContains(i.Lo) {
+ return oi
+ }
+
+ // This is the only hard case where we need to find the closest pair of endpoints.
+ if positiveDistance(oi.Hi, i.Lo) < positiveDistance(i.Hi, oi.Lo) {
+ return Interval{oi.Lo, i.Hi}
+ }
+ return Interval{i.Lo, oi.Hi}
+}
+
+// Intersection returns the smallest interval that contains the intersection of the interval and oi.
+func (i Interval) Intersection(oi Interval) Interval {
+ if oi.IsEmpty() {
+ return EmptyInterval()
+ }
+ if i.fastContains(oi.Lo) {
+ if i.fastContains(oi.Hi) {
+ // Either oi ⊂ i, or i and oi intersect twice. Neither are empty.
+ // In the first case we want to return i (which is shorter than oi).
+ // In the second case one of them is inverted, and the smallest interval
+ // that covers the two disjoint pieces is the shorter of i and oi.
+ // We thus want to pick the shorter of i and oi in both cases.
+ if oi.Length() < i.Length() {
+ return oi
+ }
+ return i
+ }
+ return Interval{oi.Lo, i.Hi}
+ }
+ if i.fastContains(oi.Hi) {
+ return Interval{i.Lo, oi.Hi}
+ }
+
+ // Neither endpoint of oi is in i. Either i ⊂ oi, or i and oi are disjoint.
+ if oi.fastContains(i.Lo) {
+ return i
+ }
+ return EmptyInterval()
+}
+
+// AddPoint returns the interval expanded by the minimum amount necessary such
+// that it contains the given point "p" (an angle in the range [-Pi, Pi]).
+func (i Interval) AddPoint(p float64) Interval {
+ if math.Abs(p) > math.Pi {
+ return i
+ }
+ if p == -math.Pi {
+ p = math.Pi
+ }
+ if i.fastContains(p) {
+ return i
+ }
+ if i.IsEmpty() {
+ return Interval{p, p}
+ }
+ if positiveDistance(p, i.Lo) < positiveDistance(i.Hi, p) {
+ return Interval{p, i.Hi}
+ }
+ return Interval{i.Lo, p}
+}
+
+// Define the maximum rounding error for arithmetic operations. Depending on the
+// platform the mantissa precision may be different than others, so we choose to
+// use specific values to be consistent across all.
+// The values come from the C++ implementation.
+var (
+ // epsilon is a small number that represents a reasonable level of noise between two
+ // values that can be considered to be equal.
+ epsilon = 1e-15
+ // dblEpsilon is a smaller number for values that require more precision.
+ dblEpsilon = 2.220446049e-16
+)
+
+// Expanded returns an interval that has been expanded on each side by margin.
+// If margin is negative, then the function shrinks the interval on
+// each side by margin instead. The resulting interval may be empty or
+// full. Any expansion (positive or negative) of a full interval remains
+// full, and any expansion of an empty interval remains empty.
+func (i Interval) Expanded(margin float64) Interval {
+ if margin >= 0 {
+ if i.IsEmpty() {
+ return i
+ }
+ // Check whether this interval will be full after expansion, allowing
+ // for a rounding error when computing each endpoint.
+ if i.Length()+2*margin+2*dblEpsilon >= 2*math.Pi {
+ return FullInterval()
+ }
+ } else {
+ if i.IsFull() {
+ return i
+ }
+ // Check whether this interval will be empty after expansion, allowing
+ // for a rounding error when computing each endpoint.
+ if i.Length()+2*margin-2*dblEpsilon <= 0 {
+ return EmptyInterval()
+ }
+ }
+ result := IntervalFromEndpoints(
+ math.Remainder(i.Lo-margin, 2*math.Pi),
+ math.Remainder(i.Hi+margin, 2*math.Pi),
+ )
+ if result.Lo <= -math.Pi {
+ result.Lo = math.Pi
+ }
+ return result
+}
+
+func (i Interval) String() string {
+ // like "[%.7f, %.7f]"
+ return "[" + strconv.FormatFloat(i.Lo, 'f', 7, 64) + ", " + strconv.FormatFloat(i.Hi, 'f', 7, 64) + "]"
+}
+
+// BUG(dsymonds): The major differences from the C++ version are:
+// - no validity checking on construction, etc. (not a bug?)
+// - a few operations
diff --git a/vendor/github.com/golang/geo/s2/LICENSE b/vendor/github.com/golang/geo/s2/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/vendor/github.com/golang/geo/s2/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/vendor/github.com/golang/geo/s2/cap.go b/vendor/github.com/golang/geo/s2/cap.go
new file mode 100644
index 0000000..4f60e86
--- /dev/null
+++ b/vendor/github.com/golang/geo/s2/cap.go
@@ -0,0 +1,406 @@
+/*
+Copyright 2014 Google Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package s2
+
+import (
+ "fmt"
+ "math"
+
+ "github.com/golang/geo/r1"
+ "github.com/golang/geo/s1"
+)
+
+const (
+ emptyHeight = -1.0
+ zeroHeight = 0.0
+ fullHeight = 2.0
+
+ roundUp = 1.0 + 1.0/(1<<52)
+)
+
+var (
+ // centerPoint is the default center for S2Caps
+ centerPoint = Point{PointFromCoords(1.0, 0, 0).Normalize()}
+)
+
+// Cap represents a disc-shaped region defined by a center and radius.
+// Technically this shape is called a "spherical cap" (rather than disc)
+// because it is not planar; the cap represents a portion of the sphere that
+// has been cut off by a plane. The boundary of the cap is the circle defined
+// by the intersection of the sphere and the plane. For containment purposes,
+// the cap is a closed set, i.e. it contains its boundary.
+//
+// For the most part, you can use a spherical cap wherever you would use a
+// disc in planar geometry. The radius of the cap is measured along the
+// surface of the sphere (rather than the straight-line distance through the
+// interior). Thus a cap of radius π/2 is a hemisphere, and a cap of radius
+// π covers the entire sphere.
+//
+// The center is a point on the surface of the unit sphere. (Hence the need for
+// it to be of unit length.)
+//
+// Internally, the cap is represented by its center and "height". The height
+// is the distance from the center point to the cutoff plane. This
+// representation is much more efficient for containment tests than the
+// (center, radius) representation. There is also support for "empty" and
+// "full" caps, which contain no points and all points respectively.
+//
+// The zero value of Cap is an invalid cap. Use EmptyCap to get a valid empty cap.
+type Cap struct {
+ center Point
+ height float64
+}
+
+// CapFromPoint constructs a cap containing a single point.
+func CapFromPoint(p Point) Cap {
+ return CapFromCenterHeight(p, zeroHeight)
+}
+
+// CapFromCenterAngle constructs a cap with the given center and angle.
+func CapFromCenterAngle(center Point, angle s1.Angle) Cap {
+ return CapFromCenterHeight(center, radiusToHeight(angle))
+}
+
+// CapFromCenterHeight constructs a cap with the given center and height. A
+// negative height yields an empty cap; a height of 2 or more yields a full cap.
+// The center should be unit length.
+func CapFromCenterHeight(center Point, height float64) Cap {
+ return Cap{
+ center: center,
+ height: height,
+ }
+}
+
+// CapFromCenterArea constructs a cap with the given center and surface area.
+// Note that the area can also be interpreted as the solid angle subtended by the
+// cap (because the sphere has unit radius). A negative area yields an empty cap;
+// an area of 4*π or more yields a full cap.
+func CapFromCenterArea(center Point, area float64) Cap {
+ return CapFromCenterHeight(center, area/(math.Pi*2.0))
+}
+
+// EmptyCap returns a cap that contains no points.
+func EmptyCap() Cap {
+ return CapFromCenterHeight(centerPoint, emptyHeight)
+}
+
+// FullCap returns a cap that contains all points.
+func FullCap() Cap {
+ return CapFromCenterHeight(centerPoint, fullHeight)
+}
+
+// IsValid reports whether the Cap is considered valid.
+// Heights are normalized so that they do not exceed 2.
+func (c Cap) IsValid() bool {
+ return c.center.Vector.IsUnit() && c.height <= fullHeight
+}
+
+// IsEmpty reports whether the cap is empty, i.e. it contains no points.
+func (c Cap) IsEmpty() bool {
+ return c.height < zeroHeight
+}
+
+// IsFull reports whether the cap is full, i.e. it contains all points.
+func (c Cap) IsFull() bool {
+ return c.height == fullHeight
+}
+
+// Center returns the cap's center point.
+func (c Cap) Center() Point {
+ return c.center
+}
+
+// Height returns the cap's "height".
+func (c Cap) Height() float64 {
+ return c.height
+}
+
+// Radius returns the cap's radius.
+func (c Cap) Radius() s1.Angle {
+ if c.IsEmpty() {
+ return s1.Angle(emptyHeight)
+ }
+
+ // This could also be computed as acos(1 - height_), but the following
+ // formula is much more accurate when the cap height is small. It
+ // follows from the relationship h = 1 - cos(r) = 2 sin^2(r/2).
+ return s1.Angle(2 * math.Asin(math.Sqrt(0.5*c.height)))
+}
+
+// Area returns the surface area of the Cap on the unit sphere.
+func (c Cap) Area() float64 {
+ return 2.0 * math.Pi * math.Max(zeroHeight, c.height)
+}
+
+// Contains reports whether this cap contains the other.
+func (c Cap) Contains(other Cap) bool {
+ // In a set containment sense, every cap contains the empty cap.
+ if c.IsFull() || other.IsEmpty() {
+ return true
+ }
+ return c.Radius() >= c.center.Distance(other.center)+other.Radius()
+}
+
+// Intersects reports whether this cap intersects the other cap.
+// i.e. whether they have any points in common.
+func (c Cap) Intersects(other Cap) bool {
+ if c.IsEmpty() || other.IsEmpty() {
+ return false
+ }
+
+ return c.Radius()+other.Radius() >= c.center.Distance(other.center)
+}
+
+// InteriorIntersects reports whether this caps interior intersects the other cap.
+func (c Cap) InteriorIntersects(other Cap) bool {
+ // Make sure this cap has an interior and the other cap is non-empty.
+ if c.height <= zeroHeight || other.IsEmpty() {
+ return false
+ }
+
+ return c.Radius()+other.Radius() > c.center.Distance(other.center)
+}
+
+// ContainsPoint reports whether this cap contains the point.
+func (c Cap) ContainsPoint(p Point) bool {
+ return c.center.Sub(p.Vector).Norm2() <= 2*c.height
+}
+
+// InteriorContainsPoint reports whether the point is within the interior of this cap.
+func (c Cap) InteriorContainsPoint(p Point) bool {
+ return c.IsFull() || c.center.Sub(p.Vector).Norm2() < 2*c.height
+}
+
+// Complement returns the complement of the interior of the cap. A cap and its
+// complement have the same boundary but do not share any interior points.
+// The complement operator is not a bijection because the complement of a
+// singleton cap (containing a single point) is the same as the complement
+// of an empty cap.
+func (c Cap) Complement() Cap {
+ height := emptyHeight
+ if !c.IsFull() {
+ height = fullHeight - math.Max(c.height, zeroHeight)
+ }
+ return CapFromCenterHeight(Point{c.center.Mul(-1.0)}, height)
+}
+
+// CapBound returns a bounding spherical cap. This is not guaranteed to be exact.
+func (c Cap) CapBound() Cap {
+ return c
+}
+
+// RectBound returns a bounding latitude-longitude rectangle.
+// The bounds are not guaranteed to be tight.
+func (c Cap) RectBound() Rect {
+ if c.IsEmpty() {
+ return EmptyRect()
+ }
+
+ capAngle := c.Radius().Radians()
+ allLongitudes := false
+ lat := r1.Interval{
+ Lo: latitude(c.center).Radians() - capAngle,
+ Hi: latitude(c.center).Radians() + capAngle,
+ }
+ lng := s1.FullInterval()
+
+ // Check whether cap includes the south pole.
+ if lat.Lo <= -math.Pi/2 {
+ lat.Lo = -math.Pi / 2
+ allLongitudes = true
+ }
+
+ // Check whether cap includes the north pole.
+ if lat.Hi >= math.Pi/2 {
+ lat.Hi = math.Pi / 2
+ allLongitudes = true
+ }
+
+ if !allLongitudes {
+ // Compute the range of longitudes covered by the cap. We use the law
+ // of sines for spherical triangles. Consider the triangle ABC where
+ // A is the north pole, B is the center of the cap, and C is the point
+ // of tangency between the cap boundary and a line of longitude. Then
+ // C is a right angle, and letting a,b,c denote the sides opposite A,B,C,
+ // we have sin(a)/sin(A) = sin(c)/sin(C), or sin(A) = sin(a)/sin(c).
+ // Here "a" is the cap angle, and "c" is the colatitude (90 degrees
+ // minus the latitude). This formula also works for negative latitudes.
+ //
+ // The formula for sin(a) follows from the relationship h = 1 - cos(a).
+ sinA := math.Sqrt(c.height * (2 - c.height))
+ sinC := math.Cos(latitude(c.center).Radians())
+ if sinA <= sinC {
+ angleA := math.Asin(sinA / sinC)
+ lng.Lo = math.Remainder(longitude(c.center).Radians()-angleA, math.Pi*2)
+ lng.Hi = math.Remainder(longitude(c.center).Radians()+angleA, math.Pi*2)
+ }
+ }
+ return Rect{lat, lng}
+}
+
+// ApproxEqual reports whether this cap's center and height are within
+// a reasonable epsilon from the other cap.
+func (c Cap) ApproxEqual(other Cap) bool {
+ // Caps have a wider tolerance than the usual epsilon for approximately equal.
+ const epsilon = 1e-14
+ return c.center.ApproxEqual(other.center) &&
+ math.Abs(c.height-other.height) <= epsilon ||
+ c.IsEmpty() && other.height <= epsilon ||
+ other.IsEmpty() && c.height <= epsilon ||
+ c.IsFull() && other.height >= 2-epsilon ||
+ other.IsFull() && c.height >= 2-epsilon
+}
+
+// AddPoint increases the cap if necessary to include the given point. If this cap is empty,
+// then the center is set to the point with a zero height. p must be unit-length.
+func (c Cap) AddPoint(p Point) Cap {
+ if c.IsEmpty() {
+ return Cap{center: p}
+ }
+
+ // To make sure that the resulting cap actually includes this point,
+ // we need to round up the distance calculation. That is, after
+ // calling cap.AddPoint(p), cap.Contains(p) should be true.
+ dist2 := c.center.Sub(p.Vector).Norm2()
+ c.height = math.Max(c.height, roundUp*0.5*dist2)
+ return c
+}
+
+// AddCap increases the cap height if necessary to include the other cap. If this cap is empty,
+// it is set to the other cap.
+func (c Cap) AddCap(other Cap) Cap {
+ if c.IsEmpty() {
+ return other
+ }
+ if other.IsEmpty() {
+ return c
+ }
+
+ radius := c.center.Angle(other.center.Vector) + other.Radius()
+ c.height = math.Max(c.height, roundUp*radiusToHeight(radius))
+ return c
+}
+
+// Expanded returns a new cap expanded by the given angle. If the cap is empty,
+// it returns an empty cap.
+func (c Cap) Expanded(distance s1.Angle) Cap {
+ if c.IsEmpty() {
+ return EmptyCap()
+ }
+ return CapFromCenterAngle(c.center, c.Radius()+distance)
+}
+
+func (c Cap) String() string {
+ return fmt.Sprintf("[Center=%v, Radius=%f]", c.center.Vector, c.Radius().Degrees())
+}
+
+// radiusToHeight converts an s1.Angle into the height of the cap.
+func radiusToHeight(r s1.Angle) float64 {
+ if r.Radians() < 0 {
+ return emptyHeight
+ }
+ if r.Radians() >= math.Pi {
+ return fullHeight
+ }
+ // The height of the cap can be computed as 1 - cos(r), but this isn't very
+ // accurate for angles close to zero (where cos(r) is almost 1). The
+ // formula below has much better precision.
+ d := math.Sin(0.5 * r.Radians())
+ return 2 * d * d
+
+}
+
+// ContainsCell reports whether the cap contains the given cell.
+func (c Cap) ContainsCell(cell Cell) bool {
+ // If the cap does not contain all cell vertices, return false.
+ var vertices [4]Point
+ for k := 0; k < 4; k++ {
+ vertices[k] = cell.Vertex(k)
+ if !c.ContainsPoint(vertices[k]) {
+ return false
+ }
+ }
+ // Otherwise, return true if the complement of the cap does not intersect the cell.
+ return !c.Complement().intersects(cell, vertices)
+}
+
+// IntersectsCell reports whether the cap intersects the cell.
+func (c Cap) IntersectsCell(cell Cell) bool {
+ // If the cap contains any cell vertex, return true.
+ var vertices [4]Point
+ for k := 0; k < 4; k++ {
+ vertices[k] = cell.Vertex(k)
+ if c.ContainsPoint(vertices[k]) {
+ return true
+ }
+ }
+ return c.intersects(cell, vertices)
+}
+
+// intersects reports whether the cap intersects any point of the cell excluding
+// its vertices (which are assumed to already have been checked).
+func (c Cap) intersects(cell Cell, vertices [4]Point) bool {
+ // If the cap is a hemisphere or larger, the cell and the complement of the cap
+ // are both convex. Therefore since no vertex of the cell is contained, no other
+ // interior point of the cell is contained either.
+ if c.height >= 1 {
+ return false
+ }
+
+ // We need to check for empty caps due to the center check just below.
+ if c.IsEmpty() {
+ return false
+ }
+
+ // Optimization: return true if the cell contains the cap center. This allows half
+ // of the edge checks below to be skipped.
+ if cell.ContainsPoint(c.center) {
+ return true
+ }
+
+ // At this point we know that the cell does not contain the cap center, and the cap
+ // does not contain any cell vertex. The only way that they can intersect is if the
+ // cap intersects the interior of some edge.
+ sin2Angle := c.height * (2 - c.height)
+ for k := 0; k < 4; k++ {
+ edge := cell.Edge(k).Vector
+ dot := c.center.Vector.Dot(edge)
+ if dot > 0 {
+ // The center is in the interior half-space defined by the edge. We do not need
+ // to consider these edges, since if the cap intersects this edge then it also
+ // intersects the edge on the opposite side of the cell, because the center is
+ // not contained with the cell.
+ continue
+ }
+
+ // The Norm2() factor is necessary because "edge" is not normalized.
+ if dot*dot > sin2Angle*edge.Norm2() {
+ return false
+ }
+
+ // Otherwise, the great circle containing this edge intersects the interior of the cap. We just
+ // need to check whether the point of closest approach occurs between the two edge endpoints.
+ dir := edge.Cross(c.center.Vector)
+ if dir.Dot(vertices[k].Vector) < 0 && dir.Dot(vertices[(k+1)&3].Vector) > 0 {
+ return true
+ }
+ }
+ return false
+}
+
+// TODO(roberts): Differences from C++
+// Centroid, Union
diff --git a/vendor/github.com/golang/geo/s2/cell.go b/vendor/github.com/golang/geo/s2/cell.go
new file mode 100644
index 0000000..e106da7
--- /dev/null
+++ b/vendor/github.com/golang/geo/s2/cell.go
@@ -0,0 +1,385 @@
+/*
+Copyright 2014 Google Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package s2
+
+import (
+ "math"
+
+ "github.com/golang/geo/r1"
+ "github.com/golang/geo/r2"
+ "github.com/golang/geo/s1"
+)
+
+// Cell is an S2 region object that represents a cell. Unlike CellIDs,
+// it supports efficient containment and intersection tests. However, it is
+// also a more expensive representation.
+type Cell struct {
+ face int8
+ level int8
+ orientation int8
+ id CellID
+ uv r2.Rect
+}
+
+// CellFromCellID constructs a Cell corresponding to the given CellID.
+func CellFromCellID(id CellID) Cell {
+ c := Cell{}
+ c.id = id
+ f, i, j, o := c.id.faceIJOrientation()
+ c.face = int8(f)
+ c.level = int8(c.id.Level())
+ c.orientation = int8(o)
+ c.uv = ijLevelToBoundUV(i, j, int(c.level))
+ return c
+}
+
+// CellFromPoint constructs a cell for the given Point.
+func CellFromPoint(p Point) Cell {
+ return CellFromCellID(cellIDFromPoint(p))
+}
+
+// CellFromLatLng constructs a cell for the given LatLng.
+func CellFromLatLng(ll LatLng) Cell {
+ return CellFromCellID(CellIDFromLatLng(ll))
+}
+
+// Face returns the face this cell is on.
+func (c Cell) Face() int {
+ return int(c.face)
+}
+
+// Level returns the level of this cell.
+func (c Cell) Level() int {
+ return int(c.level)
+}
+
+// ID returns the CellID this cell represents.
+func (c Cell) ID() CellID {
+ return c.id
+}
+
+// IsLeaf returns whether this Cell is a leaf or not.
+func (c Cell) IsLeaf() bool {
+ return c.level == maxLevel
+}
+
+// SizeIJ returns the CellID value for the cells level.
+func (c Cell) SizeIJ() int {
+ return sizeIJ(int(c.level))
+}
+
+// Vertex returns the k-th vertex of the cell (k = 0,1,2,3) in CCW order
+// (lower left, lower right, upper right, upper left in the UV plane).
+func (c Cell) Vertex(k int) Point {
+ return Point{faceUVToXYZ(int(c.face), c.uv.Vertices()[k].X, c.uv.Vertices()[k].Y).Normalize()}
+}
+
+// Edge returns the inward-facing normal of the great circle passing through
+// the CCW ordered edge from vertex k to vertex k+1 (mod 4) (for k = 0,1,2,3).
+func (c Cell) Edge(k int) Point {
+ switch k {
+ case 0:
+ return Point{vNorm(int(c.face), c.uv.Y.Lo).Normalize()} // Bottom
+ case 1:
+ return Point{uNorm(int(c.face), c.uv.X.Hi).Normalize()} // Right
+ case 2:
+ return Point{vNorm(int(c.face), c.uv.Y.Hi).Mul(-1.0).Normalize()} // Top
+ default:
+ return Point{uNorm(int(c.face), c.uv.X.Lo).Mul(-1.0).Normalize()} // Left
+ }
+}
+
+// BoundUV returns the bounds of this cell in (u,v)-space.
+func (c Cell) BoundUV() r2.Rect {
+ return c.uv
+}
+
+// Center returns the direction vector corresponding to the center in
+// (s,t)-space of the given cell. This is the point at which the cell is
+// divided into four subcells; it is not necessarily the centroid of the
+// cell in (u,v)-space or (x,y,z)-space
+func (c Cell) Center() Point {
+ return Point{c.id.rawPoint().Normalize()}
+}
+
+// Children returns the four direct children of this cell in traversal order
+// and returns true. If this is a leaf cell, or the children could not be created,
+// false is returned.
+// The C++ method is called Subdivide.
+func (c Cell) Children() ([4]Cell, bool) {
+ var children [4]Cell
+
+ if c.id.IsLeaf() {
+ return children, false
+ }
+
+ // Compute the cell midpoint in uv-space.
+ uvMid := c.id.centerUV()
+
+ // Create four children with the appropriate bounds.
+ cid := c.id.ChildBegin()
+ for pos := 0; pos < 4; pos++ {
+ children[pos] = Cell{
+ face: c.face,
+ level: c.level + 1,
+ orientation: c.orientation ^ int8(posToOrientation[pos]),
+ id: cid,
+ }
+
+ // We want to split the cell in half in u and v. To decide which
+ // side to set equal to the midpoint value, we look at cell's (i,j)
+ // position within its parent. The index for i is in bit 1 of ij.
+ ij := posToIJ[c.orientation][pos]
+ i := ij >> 1
+ j := ij & 1
+ if i == 1 {
+ children[pos].uv.X.Hi = c.uv.X.Hi
+ children[pos].uv.X.Lo = uvMid.X
+ } else {
+ children[pos].uv.X.Lo = c.uv.X.Lo
+ children[pos].uv.X.Hi = uvMid.X
+ }
+ if j == 1 {
+ children[pos].uv.Y.Hi = c.uv.Y.Hi
+ children[pos].uv.Y.Lo = uvMid.Y
+ } else {
+ children[pos].uv.Y.Lo = c.uv.Y.Lo
+ children[pos].uv.Y.Hi = uvMid.Y
+ }
+ cid = cid.Next()
+ }
+ return children, true
+}
+
+// ExactArea returns the area of this cell as accurately as possible.
+func (c Cell) ExactArea() float64 {
+ v0, v1, v2, v3 := c.Vertex(0), c.Vertex(1), c.Vertex(2), c.Vertex(3)
+ return PointArea(v0, v1, v2) + PointArea(v0, v2, v3)
+}
+
+// ApproxArea returns the approximate area of this cell. This method is accurate
+// to within 3% percent for all cell sizes and accurate to within 0.1% for cells
+// at level 5 or higher (i.e. squares 350km to a side or smaller on the Earth's
+// surface). It is moderately cheap to compute.
+func (c Cell) ApproxArea() float64 {
+ // All cells at the first two levels have the same area.
+ if c.level < 2 {
+ return c.AverageArea()
+ }
+
+ // First, compute the approximate area of the cell when projected
+ // perpendicular to its normal. The cross product of its diagonals gives
+ // the normal, and the length of the normal is twice the projected area.
+ flatArea := 0.5 * (c.Vertex(2).Sub(c.Vertex(0).Vector).
+ Cross(c.Vertex(3).Sub(c.Vertex(1).Vector)).Norm())
+
+ // Now, compensate for the curvature of the cell surface by pretending
+ // that the cell is shaped like a spherical cap. The ratio of the
+ // area of a spherical cap to the area of its projected disc turns out
+ // to be 2 / (1 + sqrt(1 - r*r)) where r is the radius of the disc.
+ // For example, when r=0 the ratio is 1, and when r=1 the ratio is 2.
+ // Here we set Pi*r*r == flatArea to find the equivalent disc.
+ return flatArea * 2 / (1 + math.Sqrt(1-math.Min(1/math.Pi*flatArea, 1)))
+}
+
+// AverageArea returns the average area of cells at the level of this cell.
+// This is accurate to within a factor of 1.7.
+func (c Cell) AverageArea() float64 {
+ return AvgAreaMetric.Value(int(c.level))
+}
+
+// IntersectsCell reports whether the intersection of this cell and the other cell is not nil.
+func (c Cell) IntersectsCell(oc Cell) bool {
+ return c.id.Intersects(oc.id)
+}
+
+// ContainsCell reports whether this cell contains the other cell.
+func (c Cell) ContainsCell(oc Cell) bool {
+ return c.id.Contains(oc.id)
+}
+
+// latitude returns the latitude of the cell vertex given by (i,j), where "i" and "j" are either 0 or 1.
+func (c Cell) latitude(i, j int) float64 {
+ var u, v float64
+ switch {
+ case i == 0 && j == 0:
+ u = c.uv.X.Lo
+ v = c.uv.Y.Lo
+ case i == 0 && j == 1:
+ u = c.uv.X.Lo
+ v = c.uv.Y.Hi
+ case i == 1 && j == 0:
+ u = c.uv.X.Hi
+ v = c.uv.Y.Lo
+ case i == 1 && j == 1:
+ u = c.uv.X.Hi
+ v = c.uv.Y.Hi
+ default:
+ panic("i and/or j is out of bound")
+ }
+ return latitude(Point{faceUVToXYZ(int(c.face), u, v)}).Radians()
+}
+
+// longitude returns the longitude of the cell vertex given by (i,j), where "i" and "j" are either 0 or 1.
+func (c Cell) longitude(i, j int) float64 {
+ var u, v float64
+ switch {
+ case i == 0 && j == 0:
+ u = c.uv.X.Lo
+ v = c.uv.Y.Lo
+ case i == 0 && j == 1:
+ u = c.uv.X.Lo
+ v = c.uv.Y.Hi
+ case i == 1 && j == 0:
+ u = c.uv.X.Hi
+ v = c.uv.Y.Lo
+ case i == 1 && j == 1:
+ u = c.uv.X.Hi
+ v = c.uv.Y.Hi
+ default:
+ panic("i and/or j is out of bound")
+ }
+ return longitude(Point{faceUVToXYZ(int(c.face), u, v)}).Radians()
+}
+
+var (
+ poleMinLat = math.Asin(math.Sqrt(1.0/3)) - 0.5*dblEpsilon
+)
+
+// RectBound returns the bounding rectangle of this cell.
+func (c Cell) RectBound() Rect {
+ if c.level > 0 {
+ // Except for cells at level 0, the latitude and longitude extremes are
+ // attained at the vertices. Furthermore, the latitude range is
+ // determined by one pair of diagonally opposite vertices and the
+ // longitude range is determined by the other pair.
+ //
+ // We first determine which corner (i,j) of the cell has the largest
+ // absolute latitude. To maximize latitude, we want to find the point in
+ // the cell that has the largest absolute z-coordinate and the smallest
+ // absolute x- and y-coordinates. To do this we look at each coordinate
+ // (u and v), and determine whether we want to minimize or maximize that
+ // coordinate based on the axis direction and the cell's (u,v) quadrant.
+ u := c.uv.X.Lo + c.uv.X.Hi
+ v := c.uv.Y.Lo + c.uv.Y.Hi
+ var i, j int
+ if uAxis(int(c.face)).Z == 0 {
+ if u < 0 {
+ i = 1
+ }
+ } else if u > 0 {
+ i = 1
+ }
+ if vAxis(int(c.face)).Z == 0 {
+ if v < 0 {
+ j = 1
+ }
+ } else if v > 0 {
+ j = 1
+ }
+ lat := r1.IntervalFromPoint(c.latitude(i, j)).AddPoint(c.latitude(1-i, 1-j))
+ lng := s1.EmptyInterval().AddPoint(c.longitude(i, 1-j)).AddPoint(c.longitude(1-i, j))
+
+ // We grow the bounds slightly to make sure that the bounding rectangle
+ // contains LatLngFromPoint(P) for any point P inside the loop L defined by the
+ // four *normalized* vertices. Note that normalization of a vector can
+ // change its direction by up to 0.5 * dblEpsilon radians, and it is not
+ // enough just to add Normalize calls to the code above because the
+ // latitude/longitude ranges are not necessarily determined by diagonally
+ // opposite vertex pairs after normalization.
+ //
+ // We would like to bound the amount by which the latitude/longitude of a
+ // contained point P can exceed the bounds computed above. In the case of
+ // longitude, the normalization error can change the direction of rounding
+ // leading to a maximum difference in longitude of 2 * dblEpsilon. In
+ // the case of latitude, the normalization error can shift the latitude by
+ // up to 0.5 * dblEpsilon and the other sources of error can cause the
+ // two latitudes to differ by up to another 1.5 * dblEpsilon, which also
+ // leads to a maximum difference of 2 * dblEpsilon.
+ return Rect{lat, lng}.expanded(LatLng{s1.Angle(2 * dblEpsilon), s1.Angle(2 * dblEpsilon)}).PolarClosure()
+ }
+
+ // The 4 cells around the equator extend to +/-45 degrees latitude at the
+ // midpoints of their top and bottom edges. The two cells covering the
+ // poles extend down to +/-35.26 degrees at their vertices. The maximum
+ // error in this calculation is 0.5 * dblEpsilon.
+ var bound Rect
+ switch c.face {
+ case 0:
+ bound = Rect{r1.Interval{-math.Pi / 4, math.Pi / 4}, s1.Interval{-math.Pi / 4, math.Pi / 4}}
+ case 1:
+ bound = Rect{r1.Interval{-math.Pi / 4, math.Pi / 4}, s1.Interval{math.Pi / 4, 3 * math.Pi / 4}}
+ case 2:
+ bound = Rect{r1.Interval{poleMinLat, math.Pi / 2}, s1.FullInterval()}
+ case 3:
+ bound = Rect{r1.Interval{-math.Pi / 4, math.Pi / 4}, s1.Interval{3 * math.Pi / 4, -3 * math.Pi / 4}}
+ case 4:
+ bound = Rect{r1.Interval{-math.Pi / 4, math.Pi / 4}, s1.Interval{-3 * math.Pi / 4, -math.Pi / 4}}
+ default:
+ bound = Rect{r1.Interval{-math.Pi / 2, -poleMinLat}, s1.FullInterval()}
+ }
+
+ // Finally, we expand the bound to account for the error when a point P is
+ // converted to an LatLng to test for containment. (The bound should be
+ // large enough so that it contains the computed LatLng of any contained
+ // point, not just the infinite-precision version.) We don't need to expand
+ // longitude because longitude is calculated via a single call to math.Atan2,
+ // which is guaranteed to be semi-monotonic.
+ return bound.expanded(LatLng{s1.Angle(dblEpsilon), s1.Angle(0)})
+}
+
+// CapBound returns the bounding cap of this cell.
+func (c Cell) CapBound() Cap {
+ // We use the cell center in (u,v)-space as the cap axis. This vector is very close
+ // to GetCenter() and faster to compute. Neither one of these vectors yields the
+ // bounding cap with minimal surface area, but they are both pretty close.
+ cap := CapFromPoint(Point{faceUVToXYZ(int(c.face), c.uv.Center().X, c.uv.Center().Y).Normalize()})
+ for k := 0; k < 4; k++ {
+ cap = cap.AddPoint(c.Vertex(k))
+ }
+ return cap
+}
+
+// ContainsPoint reports whether this cell contains the given point. Note that
+// unlike Loop/Polygon, a Cell is considered to be a closed set. This means
+// that a point on a Cell's edge or vertex belong to the Cell and the relevant
+// adjacent Cells too.
+//
+// If you want every point to be contained by exactly one Cell,
+// you will need to convert the Cell to a Loop.
+func (c Cell) ContainsPoint(p Point) bool {
+ var uv r2.Point
+ var ok bool
+ if uv.X, uv.Y, ok = faceXYZToUV(int(c.face), p); !ok {
+ return false
+ }
+
+ // Expand the (u,v) bound to ensure that
+ //
+ // CellFromPoint(p).ContainsPoint(p)
+ //
+ // is always true. To do this, we need to account for the error when
+ // converting from (u,v) coordinates to (s,t) coordinates. In the
+ // normal case the total error is at most dblEpsilon.
+ return c.uv.ExpandedByMargin(dblEpsilon).ContainsPoint(uv)
+}
+
+// BUG(roberts): Differences from C++:
+// Subdivide
+// BoundUV
+// Distance/DistanceToEdge
+// VertexChordDistance
diff --git a/vendor/github.com/golang/geo/s2/cellid.go b/vendor/github.com/golang/geo/s2/cellid.go
new file mode 100644
index 0000000..fdb2954
--- /dev/null
+++ b/vendor/github.com/golang/geo/s2/cellid.go
@@ -0,0 +1,729 @@
+/*
+Copyright 2014 Google Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package s2
+
+import (
+ "bytes"
+ "fmt"
+ "math"
+ "strconv"
+ "strings"
+
+ "github.com/golang/geo/r1"
+ "github.com/golang/geo/r2"
+ "github.com/golang/geo/r3"
+)
+
+// CellID uniquely identifies a cell in the S2 cell decomposition.
+// The most significant 3 bits encode the face number (0-5). The
+// remaining 61 bits encode the position of the center of this cell
+// along the Hilbert curve on that face. The zero value and the value
+// (1<<64)-1 are invalid cell IDs. The first compares less than any
+// valid cell ID, the second as greater than any valid cell ID.
+type CellID uint64
+
+// TODO(dsymonds): Some of these constants should probably be exported.
+const (
+ faceBits = 3
+ numFaces = 6
+ maxLevel = 30
+ posBits = 2*maxLevel + 1
+ maxSize = 1 << maxLevel
+ wrapOffset = uint64(numFaces) << posBits
+)
+
+// CellIDFromFacePosLevel returns a cell given its face in the range
+// [0,5], the 61-bit Hilbert curve position pos within that face, and
+// the level in the range [0,maxLevel]. The position in the cell ID
+// will be truncated to correspond to the Hilbert curve position at
+// the center of the returned cell.
+func CellIDFromFacePosLevel(face int, pos uint64, level int) CellID {
+ return CellID(uint64(face)<<posBits + pos | 1).Parent(level)
+}
+
+// CellIDFromFace returns the cell corresponding to a given S2 cube face.
+func CellIDFromFace(face int) CellID {
+ return CellID((uint64(face) << posBits) + lsbForLevel(0))
+}
+
+// CellIDFromLatLng returns the leaf cell containing ll.
+func CellIDFromLatLng(ll LatLng) CellID {
+ return cellIDFromPoint(PointFromLatLng(ll))
+}
+
+// CellIDFromToken returns a cell given a hex-encoded string of its uint64 ID.
+func CellIDFromToken(s string) CellID {
+ if len(s) > 16 {
+ return CellID(0)
+ }
+ n, err := strconv.ParseUint(s, 16, 64)
+ if err != nil {
+ return CellID(0)
+ }
+ // Equivalent to right-padding string with zeros to 16 characters.
+ if len(s) < 16 {
+ n = n << (4 * uint(16-len(s)))
+ }
+ return CellID(n)
+}
+
+// ToToken returns a hex-encoded string of the uint64 cell id, with leading
+// zeros included but trailing zeros stripped.
+func (ci CellID) ToToken() string {
+ s := strings.TrimRight(fmt.Sprintf("%016x", uint64(ci)), "0")
+ if len(s) == 0 {
+ return "X"
+ }
+ return s
+}
+
+// IsValid reports whether ci represents a valid cell.
+func (ci CellID) IsValid() bool {
+ return ci.Face() < numFaces && (ci.lsb()&0x1555555555555555 != 0)
+}
+
+// Face returns the cube face for this cell ID, in the range [0,5].
+func (ci CellID) Face() int { return int(uint64(ci) >> posBits) }
+
+// Pos returns the position along the Hilbert curve of this cell ID, in the range [0,2^posBits-1].
+func (ci CellID) Pos() uint64 { return uint64(ci) & (^uint64(0) >> faceBits) }
+
+// Level returns the subdivision level of this cell ID, in the range [0, maxLevel].
+func (ci CellID) Level() int {
+ return maxLevel - findLSBSetNonZero64(uint64(ci))>>1
+}
+
+// IsLeaf returns whether this cell ID is at the deepest level;
+// that is, the level at which the cells are smallest.
+func (ci CellID) IsLeaf() bool { return uint64(ci)&1 != 0 }
+
+// ChildPosition returns the child position (0..3) of this cell's
+// ancestor at the given level, relative to its parent. The argument
+// should be in the range 1..kMaxLevel. For example,
+// ChildPosition(1) returns the position of this cell's level-1
+// ancestor within its top-level face cell.
+func (ci CellID) ChildPosition(level int) int {
+ return int(uint64(ci)>>uint64(2*(maxLevel-level)+1)) & 3
+}
+
+// lsbForLevel returns the lowest-numbered bit that is on for cells at the given level.
+func lsbForLevel(level int) uint64 { return 1 << uint64(2*(maxLevel-level)) }
+
+// Parent returns the cell at the given level, which must be no greater than the current level.
+func (ci CellID) Parent(level int) CellID {
+ lsb := lsbForLevel(level)
+ return CellID((uint64(ci) & -lsb) | lsb)
+}
+
+// immediateParent is cheaper than Parent, but assumes !ci.isFace().
+func (ci CellID) immediateParent() CellID {
+ nlsb := CellID(ci.lsb() << 2)
+ return (ci & -nlsb) | nlsb
+}
+
+// isFace returns whether this is a top-level (face) cell.
+func (ci CellID) isFace() bool { return uint64(ci)&(lsbForLevel(0)-1) == 0 }
+
+// lsb returns the least significant bit that is set.
+func (ci CellID) lsb() uint64 { return uint64(ci) & -uint64(ci) }
+
+// Children returns the four immediate children of this cell.
+// If ci is a leaf cell, it returns four identical cells that are not the children.
+func (ci CellID) Children() [4]CellID {
+ var ch [4]CellID
+ lsb := CellID(ci.lsb())
+ ch[0] = ci - lsb + lsb>>2
+ lsb >>= 1
+ ch[1] = ch[0] + lsb
+ ch[2] = ch[1] + lsb
+ ch[3] = ch[2] + lsb
+ return ch
+}
+
+func sizeIJ(level int) int {
+ return 1 << uint(maxLevel-level)
+}
+
+// EdgeNeighbors returns the four cells that are adjacent across the cell's four edges.
+// Edges 0, 1, 2, 3 are in the down, right, up, left directions in the face space.
+// All neighbors are guaranteed to be distinct.
+func (ci CellID) EdgeNeighbors() [4]CellID {
+ level := ci.Level()
+ size := sizeIJ(level)
+ f, i, j, _ := ci.faceIJOrientation()
+ return [4]CellID{
+ cellIDFromFaceIJWrap(f, i, j-size).Parent(level),
+ cellIDFromFaceIJWrap(f, i+size, j).Parent(level),
+ cellIDFromFaceIJWrap(f, i, j+size).Parent(level),
+ cellIDFromFaceIJWrap(f, i-size, j).Parent(level),
+ }
+}
+
+// VertexNeighbors returns the neighboring cellIDs with vertex closest to this cell at the given level.
+// (Normally there are four neighbors, but the closest vertex may only have three neighbors if it is one of
+// the 8 cube vertices.)
+func (ci CellID) VertexNeighbors(level int) []CellID {
+ halfSize := sizeIJ(level + 1)
+ size := halfSize << 1
+ f, i, j, _ := ci.faceIJOrientation()
+
+ var isame, jsame bool
+ var ioffset, joffset int
+ if i&halfSize != 0 {
+ ioffset = size
+ isame = (i + size) < maxSize
+ } else {
+ ioffset = -size
+ isame = (i - size) >= 0
+ }
+ if j&halfSize != 0 {
+ joffset = size
+ jsame = (j + size) < maxSize
+ } else {
+ joffset = -size
+ jsame = (j - size) >= 0
+ }
+
+ results := []CellID{
+ ci.Parent(level),
+ cellIDFromFaceIJSame(f, i+ioffset, j, isame).Parent(level),
+ cellIDFromFaceIJSame(f, i, j+joffset, jsame).Parent(level),
+ }
+
+ if isame || jsame {
+ results = append(results, cellIDFromFaceIJSame(f, i+ioffset, j+joffset, isame && jsame).Parent(level))
+ }
+
+ return results
+}
+
+// RangeMin returns the minimum CellID that is contained within this cell.
+func (ci CellID) RangeMin() CellID { return CellID(uint64(ci) - (ci.lsb() - 1)) }
+
+// RangeMax returns the maximum CellID that is contained within this cell.
+func (ci CellID) RangeMax() CellID { return CellID(uint64(ci) + (ci.lsb() - 1)) }
+
+// Contains returns true iff the CellID contains oci.
+func (ci CellID) Contains(oci CellID) bool {
+ return uint64(ci.RangeMin()) <= uint64(oci) && uint64(oci) <= uint64(ci.RangeMax())
+}
+
+// Intersects returns true iff the CellID intersects oci.
+func (ci CellID) Intersects(oci CellID) bool {
+ return uint64(oci.RangeMin()) <= uint64(ci.RangeMax()) && uint64(oci.RangeMax()) >= uint64(ci.RangeMin())
+}
+
+// String returns the string representation of the cell ID in the form "1/3210".
+func (ci CellID) String() string {
+ if !ci.IsValid() {
+ return "Invalid: " + strconv.FormatInt(int64(ci), 16)
+ }
+ var b bytes.Buffer
+ b.WriteByte("012345"[ci.Face()]) // values > 5 will have been picked off by !IsValid above
+ b.WriteByte('/')
+ for level := 1; level <= ci.Level(); level++ {
+ b.WriteByte("0123"[ci.ChildPosition(level)])
+ }
+ return b.String()
+}
+
+// Point returns the center of the s2 cell on the sphere as a Point.
+// The maximum directional error in Point (compared to the exact
+// mathematical result) is 1.5 * dblEpsilon radians, and the maximum length
+// error is 2 * dblEpsilon (the same as Normalize).
+func (ci CellID) Point() Point { return Point{ci.rawPoint().Normalize()} }
+
+// LatLng returns the center of the s2 cell on the sphere as a LatLng.
+func (ci CellID) LatLng() LatLng { return LatLngFromPoint(Point{ci.rawPoint()}) }
+
+// ChildBegin returns the first child in a traversal of the children of this cell, in Hilbert curve order.
+//
+// for ci := c.ChildBegin(); ci != c.ChildEnd(); ci = ci.Next() {
+// ...
+// }
+func (ci CellID) ChildBegin() CellID {
+ ol := ci.lsb()
+ return CellID(uint64(ci) - ol + ol>>2)
+}
+
+// ChildBeginAtLevel returns the first cell in a traversal of children a given level deeper than this cell, in
+// Hilbert curve order. The given level must be no smaller than the cell's level.
+// See ChildBegin for example use.
+func (ci CellID) ChildBeginAtLevel(level int) CellID {
+ return CellID(uint64(ci) - ci.lsb() + lsbForLevel(level))
+}
+
+// ChildEnd returns the first cell after a traversal of the children of this cell in Hilbert curve order.
+// The returned cell may be invalid.
+func (ci CellID) ChildEnd() CellID {
+ ol := ci.lsb()
+ return CellID(uint64(ci) + ol + ol>>2)
+}
+
+// ChildEndAtLevel returns the first cell after the last child in a traversal of children a given level deeper
+// than this cell, in Hilbert curve order.
+// The given level must be no smaller than the cell's level.
+// The returned cell may be invalid.
+func (ci CellID) ChildEndAtLevel(level int) CellID {
+ return CellID(uint64(ci) + ci.lsb() + lsbForLevel(level))
+}
+
+// Next returns the next cell along the Hilbert curve.
+// This is expected to be used with ChildBegin and ChildEnd,
+// or ChildBeginAtLevel and ChildEndAtLevel.
+func (ci CellID) Next() CellID {
+ return CellID(uint64(ci) + ci.lsb()<<1)
+}
+
+// Prev returns the previous cell along the Hilbert curve.
+func (ci CellID) Prev() CellID {
+ return CellID(uint64(ci) - ci.lsb()<<1)
+}
+
+// NextWrap returns the next cell along the Hilbert curve, wrapping from last to
+// first as necessary. This should not be used with ChildBegin and ChildEnd.
+func (ci CellID) NextWrap() CellID {
+ n := ci.Next()
+ if uint64(n) < wrapOffset {
+ return n
+ }
+ return CellID(uint64(n) - wrapOffset)
+}
+
+// PrevWrap returns the previous cell along the Hilbert curve, wrapping around from
+// first to last as necessary. This should not be used with ChildBegin and ChildEnd.
+func (ci CellID) PrevWrap() CellID {
+ p := ci.Prev()
+ if uint64(p) < wrapOffset {
+ return p
+ }
+ return CellID(uint64(p) + wrapOffset)
+}
+
+// AdvanceWrap advances or retreats the indicated number of steps along the
+// Hilbert curve at the current level and returns the new position. The
+// position wraps between the first and last faces as necessary.
+func (ci CellID) AdvanceWrap(steps int64) CellID {
+ if steps == 0 {
+ return ci
+ }
+
+ // We clamp the number of steps if necessary to ensure that we do not
+ // advance past the End() or before the Begin() of this level.
+ shift := uint(2*(maxLevel-ci.Level()) + 1)
+ if steps < 0 {
+ if min := -int64(uint64(ci) >> shift); steps < min {
+ wrap := int64(wrapOffset >> shift)
+ steps %= wrap
+ if steps < min {
+ steps += wrap
+ }
+ }
+ } else {
+ // Unlike Advance(), we don't want to return End(level).
+ if max := int64((wrapOffset - uint64(ci)) >> shift); steps > max {
+ wrap := int64(wrapOffset >> shift)
+ steps %= wrap
+ if steps > max {
+ steps -= wrap
+ }
+ }
+ }
+
+ // If steps is negative, then shifting it left has undefined behavior.
+ // Cast to uint64 for a 2's complement answer.
+ return CellID(uint64(ci) + (uint64(steps) << shift))
+}
+
+// TODO: the methods below are not exported yet. Settle on the entire API design
+// before doing this. Do we want to mirror the C++ one as closely as possible?
+
+// rawPoint returns an unnormalized r3 vector from the origin through the center
+// of the s2 cell on the sphere.
+func (ci CellID) rawPoint() r3.Vector {
+ face, si, ti := ci.faceSiTi()
+ return faceUVToXYZ(face, stToUV((0.5/maxSize)*float64(si)), stToUV((0.5/maxSize)*float64(ti)))
+
+}
+
+// faceSiTi returns the Face/Si/Ti coordinates of the center of the cell.
+func (ci CellID) faceSiTi() (face, si, ti int) {
+ face, i, j, _ := ci.faceIJOrientation()
+ delta := 0
+ if ci.IsLeaf() {
+ delta = 1
+ } else {
+ if (i^(int(ci)>>2))&1 != 0 {
+ delta = 2
+ }
+ }
+ return face, 2*i + delta, 2*j + delta
+}
+
+// faceIJOrientation uses the global lookupIJ table to unfiddle the bits of ci.
+func (ci CellID) faceIJOrientation() (f, i, j, orientation int) {
+ f = ci.Face()
+ orientation = f & swapMask
+ nbits := maxLevel - 7*lookupBits // first iteration
+
+ for k := 7; k >= 0; k-- {
+ orientation += (int(uint64(ci)>>uint64(k*2*lookupBits+1)) & ((1 << uint((2 * nbits))) - 1)) << 2
+ orientation = lookupIJ[orientation]
+ i += (orientation >> (lookupBits + 2)) << uint(k*lookupBits)
+ j += ((orientation >> 2) & ((1 << lookupBits) - 1)) << uint(k*lookupBits)
+ orientation &= (swapMask | invertMask)
+ nbits = lookupBits // following iterations
+ }
+
+ if ci.lsb()&0x1111111111111110 != 0 {
+ orientation ^= swapMask
+ }
+
+ return
+}
+
+// cellIDFromFaceIJ returns a leaf cell given its cube face (range 0..5) and IJ coordinates.
+func cellIDFromFaceIJ(f, i, j int) CellID {
+ // Note that this value gets shifted one bit to the left at the end
+ // of the function.
+ n := uint64(f) << (posBits - 1)
+ // Alternating faces have opposite Hilbert curve orientations; this
+ // is necessary in order for all faces to have a right-handed
+ // coordinate system.
+ bits := f & swapMask
+ // Each iteration maps 4 bits of "i" and "j" into 8 bits of the Hilbert
+ // curve position. The lookup table transforms a 10-bit key of the form
+ // "iiiijjjjoo" to a 10-bit value of the form "ppppppppoo", where the
+ // letters [ijpo] denote bits of "i", "j", Hilbert curve position, and
+ // Hilbert curve orientation respectively.
+ for k := 7; k >= 0; k-- {
+ mask := (1 << lookupBits) - 1
+ bits += int((i>>uint(k*lookupBits))&mask) << (lookupBits + 2)
+ bits += int((j>>uint(k*lookupBits))&mask) << 2
+ bits = lookupPos[bits]
+ n |= uint64(bits>>2) << (uint(k) * 2 * lookupBits)
+ bits &= (swapMask | invertMask)
+ }
+ return CellID(n*2 + 1)
+}
+
+func cellIDFromFaceIJWrap(f, i, j int) CellID {
+ // Convert i and j to the coordinates of a leaf cell just beyond the
+ // boundary of this face. This prevents 32-bit overflow in the case
+ // of finding the neighbors of a face cell.
+ i = clamp(i, -1, maxSize)
+ j = clamp(j, -1, maxSize)
+
+ // We want to wrap these coordinates onto the appropriate adjacent face.
+ // The easiest way to do this is to convert the (i,j) coordinates to (x,y,z)
+ // (which yields a point outside the normal face boundary), and then call
+ // xyzToFaceUV to project back onto the correct face.
+ //
+ // The code below converts (i,j) to (si,ti), and then (si,ti) to (u,v) using
+ // the linear projection (u=2*s-1 and v=2*t-1). (The code further below
+ // converts back using the inverse projection, s=0.5*(u+1) and t=0.5*(v+1).
+ // Any projection would work here, so we use the simplest.) We also clamp
+ // the (u,v) coordinates so that the point is barely outside the
+ // [-1,1]x[-1,1] face rectangle, since otherwise the reprojection step
+ // (which divides by the new z coordinate) might change the other
+ // coordinates enough so that we end up in the wrong leaf cell.
+ const scale = 1.0 / maxSize
+ limit := math.Nextafter(1, 2)
+ u := math.Max(-limit, math.Min(limit, scale*float64((i<<1)+1-maxSize)))
+ v := math.Max(-limit, math.Min(limit, scale*float64((j<<1)+1-maxSize)))
+
+ // Find the leaf cell coordinates on the adjacent face, and convert
+ // them to a cell id at the appropriate level.
+ f, u, v = xyzToFaceUV(faceUVToXYZ(f, u, v))
+ return cellIDFromFaceIJ(f, stToIJ(0.5*(u+1)), stToIJ(0.5*(v+1)))
+}
+
+func cellIDFromFaceIJSame(f, i, j int, sameFace bool) CellID {
+ if sameFace {
+ return cellIDFromFaceIJ(f, i, j)
+ }
+ return cellIDFromFaceIJWrap(f, i, j)
+}
+
+// clamp returns number closest to x within the range min..max.
+func clamp(x, min, max int) int {
+ if x < min {
+ return min
+ }
+ if x > max {
+ return max
+ }
+ return x
+}
+
+// ijToSTMin converts the i- or j-index of a leaf cell to the minimum corresponding
+// s- or t-value contained by that cell. The argument must be in the range
+// [0..2**30], i.e. up to one position beyond the normal range of valid leaf
+// cell indices.
+func ijToSTMin(i int) float64 {
+ return float64(i) / float64(maxSize)
+}
+
+// stToIJ converts value in ST coordinates to a value in IJ coordinates.
+func stToIJ(s float64) int {
+ return clamp(int(math.Floor(maxSize*s)), 0, maxSize-1)
+}
+
+// cellIDFromPoint returns a leaf cell containing point p. Usually there is
+// exactly one such cell, but for points along the edge of a cell, any
+// adjacent cell may be (deterministically) chosen. This is because
+// s2.CellIDs are considered to be closed sets. The returned cell will
+// always contain the given point, i.e.
+//
+// CellFromPoint(p).ContainsPoint(p)
+//
+// is always true.
+func cellIDFromPoint(p Point) CellID {
+ f, u, v := xyzToFaceUV(r3.Vector{p.X, p.Y, p.Z})
+ i := stToIJ(uvToST(u))
+ j := stToIJ(uvToST(v))
+ return cellIDFromFaceIJ(f, i, j)
+}
+
+// ijLevelToBoundUV returns the bounds in (u,v)-space for the cell at the given
+// level containing the leaf cell with the given (i,j)-coordinates.
+func ijLevelToBoundUV(i, j, level int) r2.Rect {
+ cellSize := sizeIJ(level)
+ xLo := i & -cellSize
+ yLo := j & -cellSize
+
+ return r2.Rect{
+ X: r1.Interval{
+ Lo: stToUV(ijToSTMin(xLo)),
+ Hi: stToUV(ijToSTMin(xLo + cellSize)),
+ },
+ Y: r1.Interval{
+ Lo: stToUV(ijToSTMin(yLo)),
+ Hi: stToUV(ijToSTMin(yLo + cellSize)),
+ },
+ }
+}
+
+// Constants related to the bit mangling in the Cell ID.
+const (
+ lookupBits = 4
+ swapMask = 0x01
+ invertMask = 0x02
+)
+
+var (
+ ijToPos = [4][4]int{
+ {0, 1, 3, 2}, // canonical order
+ {0, 3, 1, 2}, // axes swapped
+ {2, 3, 1, 0}, // bits inverted
+ {2, 1, 3, 0}, // swapped & inverted
+ }
+ posToIJ = [4][4]int{
+ {0, 1, 3, 2}, // canonical order: (0,0), (0,1), (1,1), (1,0)
+ {0, 2, 3, 1}, // axes swapped: (0,0), (1,0), (1,1), (0,1)
+ {3, 2, 0, 1}, // bits inverted: (1,1), (1,0), (0,0), (0,1)
+ {3, 1, 0, 2}, // swapped & inverted: (1,1), (0,1), (0,0), (1,0)
+ }
+ posToOrientation = [4]int{swapMask, 0, 0, invertMask | swapMask}
+ lookupIJ [1 << (2*lookupBits + 2)]int
+ lookupPos [1 << (2*lookupBits + 2)]int
+)
+
+func init() {
+ initLookupCell(0, 0, 0, 0, 0, 0)
+ initLookupCell(0, 0, 0, swapMask, 0, swapMask)
+ initLookupCell(0, 0, 0, invertMask, 0, invertMask)
+ initLookupCell(0, 0, 0, swapMask|invertMask, 0, swapMask|invertMask)
+}
+
+// initLookupCell initializes the lookupIJ table at init time.
+func initLookupCell(level, i, j, origOrientation, pos, orientation int) {
+ if level == lookupBits {
+ ij := (i << lookupBits) + j
+ lookupPos[(ij<<2)+origOrientation] = (pos << 2) + orientation
+ lookupIJ[(pos<<2)+origOrientation] = (ij << 2) + orientation
+ return
+ }
+
+ level++
+ i <<= 1
+ j <<= 1
+ pos <<= 2
+ r := posToIJ[orientation]
+ initLookupCell(level, i+(r[0]>>1), j+(r[0]&1), origOrientation, pos, orientation^posToOrientation[0])
+ initLookupCell(level, i+(r[1]>>1), j+(r[1]&1), origOrientation, pos+1, orientation^posToOrientation[1])
+ initLookupCell(level, i+(r[2]>>1), j+(r[2]&1), origOrientation, pos+2, orientation^posToOrientation[2])
+ initLookupCell(level, i+(r[3]>>1), j+(r[3]&1), origOrientation, pos+3, orientation^posToOrientation[3])
+}
+
+// CommonAncestorLevel returns the level of the common ancestor of the two S2 CellIDs.
+func (ci CellID) CommonAncestorLevel(other CellID) (level int, ok bool) {
+ bits := uint64(ci ^ other)
+ if bits < ci.lsb() {
+ bits = ci.lsb()
+ }
+ if bits < other.lsb() {
+ bits = other.lsb()
+ }
+
+ msbPos := findMSBSetNonZero64(bits)
+ if msbPos > 60 {
+ return 0, false
+ }
+ return (60 - msbPos) >> 1, true
+}
+
+// findMSBSetNonZero64 returns the index (between 0 and 63) of the most
+// significant set bit. Passing zero to this function has undefined behavior.
+func findMSBSetNonZero64(bits uint64) int {
+ val := []uint64{0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000, 0xFFFFFFFF00000000}
+ shift := []uint64{1, 2, 4, 8, 16, 32}
+ var msbPos uint64
+ for i := 5; i >= 0; i-- {
+ if bits&val[i] != 0 {
+ bits >>= shift[i]
+ msbPos |= shift[i]
+ }
+ }
+ return int(msbPos)
+}
+
+const deBruijn64 = 0x03f79d71b4ca8b09
+const digitMask = uint64(1<<64 - 1)
+
+var deBruijn64Lookup = []byte{
+ 0, 1, 56, 2, 57, 49, 28, 3, 61, 58, 42, 50, 38, 29, 17, 4,
+ 62, 47, 59, 36, 45, 43, 51, 22, 53, 39, 33, 30, 24, 18, 12, 5,
+ 63, 55, 48, 27, 60, 41, 37, 16, 46, 35, 44, 21, 52, 32, 23, 11,
+ 54, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, 6,
+}
+
+// findLSBSetNonZero64 returns the index (between 0 and 63) of the least
+// significant set bit. Passing zero to this function has undefined behavior.
+//
+// This code comes from trailingZeroBits in https://golang.org/src/math/big/nat.go
+// which references (Knuth, volume 4, section 7.3.1).
+func findLSBSetNonZero64(bits uint64) int {
+ return int(deBruijn64Lookup[((bits&-bits)*(deBruijn64&digitMask))>>58])
+}
+
+// Advance advances or retreats the indicated number of steps along the
+// Hilbert curve at the current level, and returns the new position. The
+// position is never advanced past End() or before Begin().
+func (ci CellID) Advance(steps int64) CellID {
+ if steps == 0 {
+ return ci
+ }
+
+ // We clamp the number of steps if necessary to ensure that we do not
+ // advance past the End() or before the Begin() of this level. Note that
+ // minSteps and maxSteps always fit in a signed 64-bit integer.
+ stepShift := uint(2*(maxLevel-ci.Level()) + 1)
+ if steps < 0 {
+ minSteps := -int64(uint64(ci) >> stepShift)
+ if steps < minSteps {
+ steps = minSteps
+ }
+ } else {
+ maxSteps := int64((wrapOffset + ci.lsb() - uint64(ci)) >> stepShift)
+ if steps > maxSteps {
+ steps = maxSteps
+ }
+ }
+ return ci + CellID(steps)<<stepShift
+}
+
+// centerST return the center of the CellID in (s,t)-space.
+func (ci CellID) centerST() r2.Point {
+ _, si, ti := ci.faceSiTi()
+ return r2.Point{siTiToST(uint64(si)), siTiToST(uint64(ti))}
+}
+
+// sizeST returns the edge length of this CellID in (s,t)-space at the given level.
+func (ci CellID) sizeST(level int) float64 {
+ return ijToSTMin(sizeIJ(level))
+}
+
+// boundST returns the bound of this CellID in (s,t)-space.
+func (ci CellID) boundST() r2.Rect {
+ s := ci.sizeST(ci.Level())
+ return r2.RectFromCenterSize(ci.centerST(), r2.Point{s, s})
+}
+
+// centerUV returns the center of this CellID in (u,v)-space. Note that
+// the center of the cell is defined as the point at which it is recursively
+// subdivided into four children; in general, it is not at the midpoint of
+// the (u,v) rectangle covered by the cell.
+func (ci CellID) centerUV() r2.Point {
+ _, si, ti := ci.faceSiTi()
+ return r2.Point{stToUV(siTiToST(uint64(si))), stToUV(siTiToST(uint64(ti)))}
+}
+
+// boundUV returns the bound of this CellID in (u,v)-space.
+func (ci CellID) boundUV() r2.Rect {
+ _, i, j, _ := ci.faceIJOrientation()
+ return ijLevelToBoundUV(i, j, ci.Level())
+
+}
+
+// MaxTile returns the largest cell with the same RangeMin such that
+// RangeMax < limit.RangeMin. It returns limit if no such cell exists.
+// This method can be used to generate a small set of CellIDs that covers
+// a given range (a tiling). This example shows how to generate a tiling
+// for a semi-open range of leaf cells [start, limit):
+//
+// for id := start.MaxTile(limit); id != limit; id = id.Next().MaxTile(limit)) { ... }
+//
+// Note that in general the cells in the tiling will be of different sizes;
+// they gradually get larger (near the middle of the range) and then
+// gradually get smaller as limit is approached.
+func (ci CellID) MaxTile(limit CellID) CellID {
+ start := ci.RangeMin()
+ if start >= limit.RangeMin() {
+ return limit
+ }
+
+ if ci.RangeMax() >= limit {
+ // The cell is too large, shrink it. Note that when generating coverings
+ // of CellID ranges, this loop usually executes only once. Also because
+ // ci.RangeMin() < limit.RangeMin(), we will always exit the loop by the
+ // time we reach a leaf cell.
+ for {
+ ci = ci.Children()[0]
+ if ci.RangeMax() < limit {
+ break
+ }
+ }
+ return ci
+ }
+
+ // The cell may be too small. Grow it if necessary. Note that generally
+ // this loop only iterates once.
+ for !ci.isFace() {
+ parent := ci.immediateParent()
+ if parent.RangeMin() != start || parent.RangeMax() >= limit {
+ break
+ }
+ ci = parent
+ }
+ return ci
+}
+
+// TODO: Differences from C++:
+// ExpandedByDistanceUV/ExpandEndpoint
+// CenterSiTi
+// AppendVertexNeighbors/AppendAllNeighbors
diff --git a/vendor/github.com/golang/geo/s2/cellunion.go b/vendor/github.com/golang/geo/s2/cellunion.go
new file mode 100644
index 0000000..dfdf611
--- /dev/null
+++ b/vendor/github.com/golang/geo/s2/cellunion.go
@@ -0,0 +1,236 @@
+/*
+Copyright 2014 Google Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package s2
+
+import (
+ "sort"
+)
+
+// A CellUnion is a collection of CellIDs.
+//
+// It is normalized if it is sorted, and does not contain redundancy.
+// Specifically, it may not contain the same CellID twice, nor a CellID that
+// is contained by another, nor the four sibling CellIDs that are children of
+// a single higher level CellID.
+type CellUnion []CellID
+
+// CellUnionFromRange creates a CellUnion that covers the half-open range
+// of leaf cells [begin, end). If begin == end the resulting union is empty.
+// This requires that begin and end are both leaves, and begin <= end.
+// To create a closed-ended range, pass in end.Next().
+func CellUnionFromRange(begin, end CellID) CellUnion {
+ // We repeatedly add the largest cell we can.
+ var cu CellUnion
+ for id := begin.MaxTile(end); id != end; id = id.Next().MaxTile(end) {
+ cu = append(cu, id)
+ }
+ return cu
+}
+
+// Normalize normalizes the CellUnion.
+func (cu *CellUnion) Normalize() {
+ sort.Sort(byID(*cu))
+
+ output := make([]CellID, 0, len(*cu)) // the list of accepted cells
+ // Loop invariant: output is a sorted list of cells with no redundancy.
+ for _, ci := range *cu {
+ // The first two passes here either ignore this new candidate,
+ // or remove previously accepted cells that are covered by this candidate.
+
+ // Ignore this cell if it is contained by the previous one.
+ // We only need to check the last accepted cell. The ordering of the
+ // cells implies containment (but not the converse), and output has no redundancy,
+ // so if this candidate is not contained by the last accepted cell
+ // then it cannot be contained by any previously accepted cell.
+ if len(output) > 0 && output[len(output)-1].Contains(ci) {
+ continue
+ }
+
+ // Discard any previously accepted cells contained by this one.
+ // This could be any contiguous trailing subsequence, but it can't be
+ // a discontiguous subsequence because of the containment property of
+ // sorted S2 cells mentioned above.
+ j := len(output) - 1 // last index to keep
+ for j >= 0 {
+ if !ci.Contains(output[j]) {
+ break
+ }
+ j--
+ }
+ output = output[:j+1]
+
+ // See if the last three cells plus this one can be collapsed.
+ // We loop because collapsing three accepted cells and adding a higher level cell
+ // could cascade into previously accepted cells.
+ for len(output) >= 3 {
+ fin := output[len(output)-3:]
+
+ // fast XOR test; a necessary but not sufficient condition
+ if fin[0]^fin[1]^fin[2]^ci != 0 {
+ break
+ }
+
+ // more expensive test; exact.
+ // Compute the two bit mask for the encoded child position,
+ // then see if they all agree.
+ mask := CellID(ci.lsb() << 1)
+ mask = ^(mask + mask<<1)
+ should := ci & mask
+ if (fin[0]&mask != should) || (fin[1]&mask != should) || (fin[2]&mask != should) || ci.isFace() {
+ break
+ }
+
+ output = output[:len(output)-3]
+ ci = ci.immediateParent() // checked !ci.isFace above
+ }
+ output = append(output, ci)
+ }
+ *cu = output
+}
+
+// IntersectsCellID reports whether this cell union intersects the given cell ID.
+//
+// This method assumes that the CellUnion has been normalized.
+func (cu *CellUnion) IntersectsCellID(id CellID) bool {
+ // Find index of array item that occurs directly after our probe cell:
+ i := sort.Search(len(*cu), func(i int) bool { return id < (*cu)[i] })
+
+ if i != len(*cu) && (*cu)[i].RangeMin() <= id.RangeMax() {
+ return true
+ }
+ return i != 0 && (*cu)[i-1].RangeMax() >= id.RangeMin()
+}
+
+// ContainsCellID reports whether the cell union contains the given cell ID.
+// Containment is defined with respect to regions, e.g. a cell contains its 4 children.
+//
+// This method assumes that the CellUnion has been normalized.
+func (cu *CellUnion) ContainsCellID(id CellID) bool {
+ // Find index of array item that occurs directly after our probe cell:
+ i := sort.Search(len(*cu), func(i int) bool { return id < (*cu)[i] })
+
+ if i != len(*cu) && (*cu)[i].RangeMin() <= id {
+ return true
+ }
+ return i != 0 && (*cu)[i-1].RangeMax() >= id
+}
+
+type byID []CellID
+
+func (cu byID) Len() int { return len(cu) }
+func (cu byID) Less(i, j int) bool { return cu[i] < cu[j] }
+func (cu byID) Swap(i, j int) { cu[i], cu[j] = cu[j], cu[i] }
+
+// Denormalize replaces this CellUnion with an expanded version of the
+// CellUnion where any cell whose level is less than minLevel or where
+// (level - minLevel) is not a multiple of levelMod is replaced by its
+// children, until either both of these conditions are satisfied or the
+// maximum level is reached.
+func (cu *CellUnion) Denormalize(minLevel, levelMod int) {
+ var denorm CellUnion
+ for _, id := range *cu {
+ level := id.Level()
+ newLevel := level
+ if newLevel < minLevel {
+ newLevel = minLevel
+ }
+ if levelMod > 1 {
+ newLevel += (maxLevel - (newLevel - minLevel)) % levelMod
+ if newLevel > maxLevel {
+ newLevel = maxLevel
+ }
+ }
+ if newLevel == level {
+ denorm = append(denorm, id)
+ } else {
+ end := id.ChildEndAtLevel(newLevel)
+ for ci := id.ChildBeginAtLevel(newLevel); ci != end; ci = ci.Next() {
+ denorm = append(denorm, ci)
+ }
+ }
+ }
+ *cu = denorm
+}
+
+// RectBound returns a Rect that bounds this entity.
+func (cu *CellUnion) RectBound() Rect {
+ bound := EmptyRect()
+ for _, c := range *cu {
+ bound = bound.Union(CellFromCellID(c).RectBound())
+ }
+ return bound
+}
+
+// CapBound returns a Cap that bounds this entity.
+func (cu *CellUnion) CapBound() Cap {
+ if len(*cu) == 0 {
+ return EmptyCap()
+ }
+
+ // Compute the approximate centroid of the region. This won't produce the
+ // bounding cap of minimal area, but it should be close enough.
+ var centroid Point
+
+ for _, ci := range *cu {
+ area := AvgAreaMetric.Value(ci.Level())
+ centroid = Point{centroid.Add(ci.Point().Mul(area))}
+ }
+
+ if zero := (Point{}); centroid == zero {
+ centroid = PointFromCoords(1, 0, 0)
+ } else {
+ centroid = Point{centroid.Normalize()}
+ }
+
+ // Use the centroid as the cap axis, and expand the cap angle so that it
+ // contains the bounding caps of all the individual cells. Note that it is
+ // *not* sufficient to just bound all the cell vertices because the bounding
+ // cap may be concave (i.e. cover more than one hemisphere).
+ c := CapFromPoint(centroid)
+ for _, ci := range *cu {
+ c = c.AddCap(CellFromCellID(ci).CapBound())
+ }
+
+ return c
+}
+
+// ContainsCell reports whether this cell union contains the given cell.
+func (cu *CellUnion) ContainsCell(c Cell) bool {
+ return cu.ContainsCellID(c.id)
+}
+
+// IntersectsCell reports whether this cell union intersects the given cell.
+func (cu *CellUnion) IntersectsCell(c Cell) bool {
+ return cu.IntersectsCellID(c.id)
+}
+
+// LeafCellsCovered reports the number of leaf cells covered by this cell union.
+// This will be no more than 6*2^60 for the whole sphere.
+func (cu *CellUnion) LeafCellsCovered() int64 {
+ var numLeaves int64
+ for _, c := range *cu {
+ numLeaves += 1 << uint64((maxLevel-int64(c.Level()))<<1)
+ }
+ return numLeaves
+}
+
+// BUG: Differences from C++:
+// Contains(CellUnion)/Intersects(CellUnion)
+// Union(CellUnion)/Intersection(CellUnion)/Difference(CellUnion)
+// Expand
+// ContainsPoint
+// AverageArea/ApproxArea/ExactArea
diff --git a/vendor/github.com/golang/geo/s2/doc.go b/vendor/github.com/golang/geo/s2/doc.go
new file mode 100644
index 0000000..c6dbe44
--- /dev/null
+++ b/vendor/github.com/golang/geo/s2/doc.go
@@ -0,0 +1,31 @@
+/*
+Copyright 2014 Google Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/*
+Package s2 implements types and functions for working with geometry in S² (spherical geometry).
+
+Its related packages, parallel to this one, are s1 (operates on S¹), r1 (operates on ℝ¹)
+and r3 (operates on ℝ³).
+
+This package provides types and functions for the S2 cell hierarchy and coordinate systems.
+The S2 cell hierarchy is a hierarchical decomposition of the surface of a unit sphere (S²)
+into ``cells''; it is highly efficient, scales from continental size to under 1 cm²
+and preserves spatial locality (nearby cells have close IDs).
+
+A presentation that gives an overview of S2 is
+https://docs.google.com/presentation/d/1Hl4KapfAENAOf4gv-pSngKwvS_jwNVHRPZTTDzXXn6Q/view.
+*/
+package s2
diff --git a/vendor/github.com/golang/geo/s2/edgeutil.go b/vendor/github.com/golang/geo/s2/edgeutil.go
new file mode 100644
index 0000000..c1e5c90
--- /dev/null
+++ b/vendor/github.com/golang/geo/s2/edgeutil.go
@@ -0,0 +1,1293 @@
+/*
+Copyright 2015 Google Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package s2
+
+import (
+ "math"
+
+ "github.com/golang/geo/r1"
+ "github.com/golang/geo/r2"
+ "github.com/golang/geo/r3"
+ "github.com/golang/geo/s1"
+)
+
+const (
+ // edgeClipErrorUVCoord is the maximum error in a u- or v-coordinate
+ // compared to the exact result, assuming that the points A and B are in
+ // the rectangle [-1,1]x[1,1] or slightly outside it (by 1e-10 or less).
+ edgeClipErrorUVCoord = 2.25 * dblEpsilon
+
+ // edgeClipErrorUVDist is the maximum distance from a clipped point to
+ // the corresponding exact result. It is equal to the error in a single
+ // coordinate because at most one coordinate is subject to error.
+ edgeClipErrorUVDist = 2.25 * dblEpsilon
+
+ // faceClipErrorRadians is the maximum angle between a returned vertex
+ // and the nearest point on the exact edge AB. It is equal to the
+ // maximum directional error in PointCross, plus the error when
+ // projecting points onto a cube face.
+ faceClipErrorRadians = 3 * dblEpsilon
+
+ // faceClipErrorDist is the same angle expressed as a maximum distance
+ // in (u,v)-space. In other words, a returned vertex is at most this far
+ // from the exact edge AB projected into (u,v)-space.
+ faceClipErrorUVDist = 9 * dblEpsilon
+
+ // faceClipErrorUVCoord is the maximum angle between a returned vertex
+ // and the nearest point on the exact edge AB expressed as the maximum error
+ // in an individual u- or v-coordinate. In other words, for each
+ // returned vertex there is a point on the exact edge AB whose u- and
+ // v-coordinates differ from the vertex by at most this amount.
+ faceClipErrorUVCoord = 9.0 * (1.0 / math.Sqrt2) * dblEpsilon
+
+ // intersectsRectErrorUVDist is the maximum error when computing if a point
+ // intersects with a given Rect. If some point of AB is inside the
+ // rectangle by at least this distance, the result is guaranteed to be true;
+ // if all points of AB are outside the rectangle by at least this distance,
+ // the result is guaranteed to be false. This bound assumes that rect is
+ // a subset of the rectangle [-1,1]x[-1,1] or extends slightly outside it
+ // (e.g., by 1e-10 or less).
+ intersectsRectErrorUVDist = 3 * math.Sqrt2 * dblEpsilon
+
+ // intersectionError can be set somewhat arbitrarily, because the algorithm
+ // uses more precision if necessary in order to achieve the specified error.
+ // The only strict requirement is that intersectionError >= dblEpsilon
+ // radians. However, using a larger error tolerance makes the algorithm more
+ // efficient because it reduces the number of cases where exact arithmetic is
+ // needed.
+ intersectionError = s1.Angle(4 * dblEpsilon)
+
+ // intersectionMergeRadius is used to ensure that intersection points that
+ // are supposed to be coincident are merged back together into a single
+ // vertex. This is required in order for various polygon operations (union,
+ // intersection, etc) to work correctly. It is twice the intersection error
+ // because two coincident intersection points might have errors in
+ // opposite directions.
+ intersectionMergeRadius = 2 * intersectionError
+)
+
+// SimpleCrossing reports whether edge AB crosses CD at a point that is interior
+// to both edges. Properties:
+//
+// (1) SimpleCrossing(b,a,c,d) == SimpleCrossing(a,b,c,d)
+// (2) SimpleCrossing(c,d,a,b) == SimpleCrossing(a,b,c,d)
+func SimpleCrossing(a, b, c, d Point) bool {
+ // We compute the equivalent of Sign for triangles ACB, CBD, BDA,
+ // and DAC. All of these triangles need to have the same orientation
+ // (CW or CCW) for an intersection to exist.
+
+ ab := a.Vector.Cross(b.Vector)
+ acb := -(ab.Dot(c.Vector))
+ bda := ab.Dot(d.Vector)
+ if acb*bda <= 0 {
+ return false
+ }
+
+ cd := c.Vector.Cross(d.Vector)
+ cbd := -(cd.Dot(b.Vector))
+ dac := cd.Dot(a.Vector)
+ return (acb*cbd > 0) && (acb*dac > 0)
+}
+
+// VertexCrossing reports whether two edges "cross" in such a way that point-in-polygon
+// containment tests can be implemented by counting the number of edge crossings.
+//
+// Given two edges AB and CD where at least two vertices are identical
+// (i.e. CrossingSign(a,b,c,d) == 0), the basic rule is that a "crossing"
+// occurs if AB is encountered after CD during a CCW sweep around the shared
+// vertex starting from a fixed reference point.
+//
+// Note that according to this rule, if AB crosses CD then in general CD
+// does not cross AB. However, this leads to the correct result when
+// counting polygon edge crossings. For example, suppose that A,B,C are
+// three consecutive vertices of a CCW polygon. If we now consider the edge
+// crossings of a segment BP as P sweeps around B, the crossing number
+// changes parity exactly when BP crosses BA or BC.
+//
+// Useful properties of VertexCrossing (VC):
+//
+// (1) VC(a,a,c,d) == VC(a,b,c,c) == false
+// (2) VC(a,b,a,b) == VC(a,b,b,a) == true
+// (3) VC(a,b,c,d) == VC(a,b,d,c) == VC(b,a,c,d) == VC(b,a,d,c)
+// (3) If exactly one of a,b equals one of c,d, then exactly one of
+// VC(a,b,c,d) and VC(c,d,a,b) is true
+//
+// It is an error to call this method with 4 distinct vertices.
+func VertexCrossing(a, b, c, d Point) bool {
+ // If A == B or C == D there is no intersection. We need to check this
+ // case first in case 3 or more input points are identical.
+ if a.ApproxEqual(b) || c.ApproxEqual(d) {
+ return false
+ }
+
+ // If any other pair of vertices is equal, there is a crossing if and only
+ // if OrderedCCW indicates that the edge AB is further CCW around the
+ // shared vertex O (either A or B) than the edge CD, starting from an
+ // arbitrary fixed reference point.
+ switch {
+ case a.ApproxEqual(d):
+ return OrderedCCW(Point{a.Ortho()}, c, b, a)
+ case b.ApproxEqual(c):
+ return OrderedCCW(Point{b.Ortho()}, d, a, b)
+ case a.ApproxEqual(c):
+ return OrderedCCW(Point{a.Ortho()}, d, b, a)
+ case b.ApproxEqual(d):
+ return OrderedCCW(Point{b.Ortho()}, c, a, b)
+ }
+
+ return false
+}
+
+// DistanceFraction returns the distance ratio of the point X along an edge AB.
+// If X is on the line segment AB, this is the fraction T such
+// that X == Interpolate(T, A, B).
+//
+// This requires that A and B are distinct.
+func DistanceFraction(x, a, b Point) float64 {
+ d0 := x.Angle(a.Vector)
+ d1 := x.Angle(b.Vector)
+ return float64(d0 / (d0 + d1))
+}
+
+// Interpolate returns the point X along the line segment AB whose distance from A
+// is the given fraction "t" of the distance AB. Does NOT require that "t" be
+// between 0 and 1. Note that all distances are measured on the surface of
+// the sphere, so this is more complicated than just computing (1-t)*a + t*b
+// and normalizing the result.
+func Interpolate(t float64, a, b Point) Point {
+ if t == 0 {
+ return a
+ }
+ if t == 1 {
+ return b
+ }
+ ab := a.Angle(b.Vector)
+ return InterpolateAtDistance(s1.Angle(t)*ab, a, b)
+}
+
+// InterpolateAtDistance returns the point X along the line segment AB whose
+// distance from A is the angle ax.
+func InterpolateAtDistance(ax s1.Angle, a, b Point) Point {
+ aRad := ax.Radians()
+
+ // Use PointCross to compute the tangent vector at A towards B. The
+ // result is always perpendicular to A, even if A=B or A=-B, but it is not
+ // necessarily unit length. (We effectively normalize it below.)
+ normal := a.PointCross(b)
+ tangent := normal.Vector.Cross(a.Vector)
+
+ // Now compute the appropriate linear combination of A and "tangent". With
+ // infinite precision the result would always be unit length, but we
+ // normalize it anyway to ensure that the error is within acceptable bounds.
+ // (Otherwise errors can build up when the result of one interpolation is
+ // fed into another interpolation.)
+ return Point{(a.Mul(math.Cos(aRad)).Add(tangent.Mul(math.Sin(aRad) / tangent.Norm()))).Normalize()}
+}
+
+// RectBounder is used to compute a bounding rectangle that contains all edges
+// defined by a vertex chain (v0, v1, v2, ...). All vertices must be unit length.
+// Note that the bounding rectangle of an edge can be larger than the bounding
+// rectangle of its endpoints, e.g. consider an edge that passes through the North Pole.
+//
+// The bounds are calculated conservatively to account for numerical errors
+// when points are converted to LatLngs. More precisely, this function
+// guarantees the following:
+// Let L be a closed edge chain (Loop) such that the interior of the loop does
+// not contain either pole. Now if P is any point such that L.ContainsPoint(P),
+// then RectBound(L).ContainsPoint(LatLngFromPoint(P)).
+type RectBounder struct {
+ // The previous vertex in the chain.
+ a Point
+ // The previous vertex latitude longitude.
+ aLL LatLng
+ bound Rect
+}
+
+// NewRectBounder returns a new instance of a RectBounder.
+func NewRectBounder() *RectBounder {
+ return &RectBounder{
+ bound: EmptyRect(),
+ }
+}
+
+// AddPoint adds the given point to the chain. The Point must be unit length.
+func (r *RectBounder) AddPoint(b Point) {
+ bLL := LatLngFromPoint(b)
+
+ if r.bound.IsEmpty() {
+ r.a = b
+ r.aLL = bLL
+ r.bound = r.bound.AddPoint(bLL)
+ return
+ }
+
+ // First compute the cross product N = A x B robustly. This is the normal
+ // to the great circle through A and B. We don't use RobustSign
+ // since that method returns an arbitrary vector orthogonal to A if the two
+ // vectors are proportional, and we want the zero vector in that case.
+ n := r.a.Sub(b.Vector).Cross(r.a.Add(b.Vector)) // N = 2 * (A x B)
+
+ // The relative error in N gets large as its norm gets very small (i.e.,
+ // when the two points are nearly identical or antipodal). We handle this
+ // by choosing a maximum allowable error, and if the error is greater than
+ // this we fall back to a different technique. Since it turns out that
+ // the other sources of error in converting the normal to a maximum
+ // latitude add up to at most 1.16 * dblEpsilon, and it is desirable to
+ // have the total error be a multiple of dblEpsilon, we have chosen to
+ // limit the maximum error in the normal to be 3.84 * dblEpsilon.
+ // It is possible to show that the error is less than this when
+ //
+ // n.Norm() >= 8 * sqrt(3) / (3.84 - 0.5 - sqrt(3)) * dblEpsilon
+ // = 1.91346e-15 (about 8.618 * dblEpsilon)
+ nNorm := n.Norm()
+ if nNorm < 1.91346e-15 {
+ // A and B are either nearly identical or nearly antipodal (to within
+ // 4.309 * dblEpsilon, or about 6 nanometers on the earth's surface).
+ if r.a.Dot(b.Vector) < 0 {
+ // The two points are nearly antipodal. The easiest solution is to
+ // assume that the edge between A and B could go in any direction
+ // around the sphere.
+ r.bound = FullRect()
+ } else {
+ // The two points are nearly identical (to within 4.309 * dblEpsilon).
+ // In this case we can just use the bounding rectangle of the points,
+ // since after the expansion done by GetBound this Rect is
+ // guaranteed to include the (lat,lng) values of all points along AB.
+ r.bound = r.bound.Union(RectFromLatLng(r.aLL).AddPoint(bLL))
+ }
+ r.a = b
+ r.aLL = bLL
+ return
+ }
+
+ // Compute the longitude range spanned by AB.
+ lngAB := s1.EmptyInterval().AddPoint(r.aLL.Lng.Radians()).AddPoint(bLL.Lng.Radians())
+ if lngAB.Length() >= math.Pi-2*dblEpsilon {
+ // The points lie on nearly opposite lines of longitude to within the
+ // maximum error of the calculation. The easiest solution is to assume
+ // that AB could go on either side of the pole.
+ lngAB = s1.FullInterval()
+ }
+
+ // Next we compute the latitude range spanned by the edge AB. We start
+ // with the range spanning the two endpoints of the edge:
+ latAB := r1.IntervalFromPoint(r.aLL.Lat.Radians()).AddPoint(bLL.Lat.Radians())
+
+ // This is the desired range unless the edge AB crosses the plane
+ // through N and the Z-axis (which is where the great circle through A
+ // and B attains its minimum and maximum latitudes). To test whether AB
+ // crosses this plane, we compute a vector M perpendicular to this
+ // plane and then project A and B onto it.
+ m := n.Cross(PointFromCoords(0, 0, 1).Vector)
+ mA := m.Dot(r.a.Vector)
+ mB := m.Dot(b.Vector)
+
+ // We want to test the signs of "mA" and "mB", so we need to bound
+ // the error in these calculations. It is possible to show that the
+ // total error is bounded by
+ //
+ // (1 + sqrt(3)) * dblEpsilon * nNorm + 8 * sqrt(3) * (dblEpsilon**2)
+ // = 6.06638e-16 * nNorm + 6.83174e-31
+
+ mError := 6.06638e-16*nNorm + 6.83174e-31
+ if mA*mB < 0 || math.Abs(mA) <= mError || math.Abs(mB) <= mError {
+ // Minimum/maximum latitude *may* occur in the edge interior.
+ //
+ // The maximum latitude is 90 degrees minus the latitude of N. We
+ // compute this directly using atan2 in order to get maximum accuracy
+ // near the poles.
+ //
+ // Our goal is compute a bound that contains the computed latitudes of
+ // all S2Points P that pass the point-in-polygon containment test.
+ // There are three sources of error we need to consider:
+ // - the directional error in N (at most 3.84 * dblEpsilon)
+ // - converting N to a maximum latitude
+ // - computing the latitude of the test point P
+ // The latter two sources of error are at most 0.955 * dblEpsilon
+ // individually, but it is possible to show by a more complex analysis
+ // that together they can add up to at most 1.16 * dblEpsilon, for a
+ // total error of 5 * dblEpsilon.
+ //
+ // We add 3 * dblEpsilon to the bound here, and GetBound() will pad
+ // the bound by another 2 * dblEpsilon.
+ maxLat := math.Min(
+ math.Atan2(math.Sqrt(n.X*n.X+n.Y*n.Y), math.Abs(n.Z))+3*dblEpsilon,
+ math.Pi/2)
+
+ // In order to get tight bounds when the two points are close together,
+ // we also bound the min/max latitude relative to the latitudes of the
+ // endpoints A and B. First we compute the distance between A and B,
+ // and then we compute the maximum change in latitude between any two
+ // points along the great circle that are separated by this distance.
+ // This gives us a latitude change "budget". Some of this budget must
+ // be spent getting from A to B; the remainder bounds the round-trip
+ // distance (in latitude) from A or B to the min or max latitude
+ // attained along the edge AB.
+ latBudget := 2 * math.Asin(0.5*(r.a.Sub(b.Vector)).Norm()*math.Sin(maxLat))
+ maxDelta := 0.5*(latBudget-latAB.Length()) + dblEpsilon
+
+ // Test whether AB passes through the point of maximum latitude or
+ // minimum latitude. If the dot product(s) are small enough then the
+ // result may be ambiguous.
+ if mA <= mError && mB >= -mError {
+ latAB.Hi = math.Min(maxLat, latAB.Hi+maxDelta)
+ }
+ if mB <= mError && mA >= -mError {
+ latAB.Lo = math.Max(-maxLat, latAB.Lo-maxDelta)
+ }
+ }
+ r.a = b
+ r.aLL = bLL
+ r.bound = r.bound.Union(Rect{latAB, lngAB})
+}
+
+// RectBound returns the bounding rectangle of the edge chain that connects the
+// vertices defined so far. This bound satisfies the guarantee made
+// above, i.e. if the edge chain defines a Loop, then the bound contains
+// the LatLng coordinates of all Points contained by the loop.
+func (r *RectBounder) RectBound() Rect {
+ return r.bound.expanded(LatLng{s1.Angle(2 * dblEpsilon), 0}).PolarClosure()
+}
+
+// ExpandForSubregions expands a bounding Rect so that it is guaranteed to
+// contain the bounds of any subregion whose bounds are computed using
+// ComputeRectBound. For example, consider a loop L that defines a square.
+// GetBound ensures that if a point P is contained by this square, then
+// LatLngFromPoint(P) is contained by the bound. But now consider a diamond
+// shaped loop S contained by L. It is possible that GetBound returns a
+// *larger* bound for S than it does for L, due to rounding errors. This
+// method expands the bound for L so that it is guaranteed to contain the
+// bounds of any subregion S.
+//
+// More precisely, if L is a loop that does not contain either pole, and S
+// is a loop such that L.Contains(S), then
+//
+// ExpandForSubregions(L.RectBound).Contains(S.RectBound).
+//
+func ExpandForSubregions(bound Rect) Rect {
+ // Empty bounds don't need expansion.
+ if bound.IsEmpty() {
+ return bound
+ }
+
+ // First we need to check whether the bound B contains any nearly-antipodal
+ // points (to within 4.309 * dblEpsilon). If so then we need to return
+ // FullRect, since the subregion might have an edge between two
+ // such points, and AddPoint returns Full for such edges. Note that
+ // this can happen even if B is not Full for example, consider a loop
+ // that defines a 10km strip straddling the equator extending from
+ // longitudes -100 to +100 degrees.
+ //
+ // It is easy to check whether B contains any antipodal points, but checking
+ // for nearly-antipodal points is trickier. Essentially we consider the
+ // original bound B and its reflection through the origin B', and then test
+ // whether the minimum distance between B and B' is less than 4.309 * dblEpsilon.
+
+ // lngGap is a lower bound on the longitudinal distance between B and its
+ // reflection B'. (2.5 * dblEpsilon is the maximum combined error of the
+ // endpoint longitude calculations and the Length call.)
+ lngGap := math.Max(0, math.Pi-bound.Lng.Length()-2.5*dblEpsilon)
+
+ // minAbsLat is the minimum distance from B to the equator (if zero or
+ // negative, then B straddles the equator).
+ minAbsLat := math.Max(bound.Lat.Lo, -bound.Lat.Hi)
+
+ // latGapSouth and latGapNorth measure the minimum distance from B to the
+ // south and north poles respectively.
+ latGapSouth := math.Pi/2 + bound.Lat.Lo
+ latGapNorth := math.Pi/2 - bound.Lat.Hi
+
+ if minAbsLat >= 0 {
+ // The bound B does not straddle the equator. In this case the minimum
+ // distance is between one endpoint of the latitude edge in B closest to
+ // the equator and the other endpoint of that edge in B'. The latitude
+ // distance between these two points is 2*minAbsLat, and the longitude
+ // distance is lngGap. We could compute the distance exactly using the
+ // Haversine formula, but then we would need to bound the errors in that
+ // calculation. Since we only need accuracy when the distance is very
+ // small (close to 4.309 * dblEpsilon), we substitute the Euclidean
+ // distance instead. This gives us a right triangle XYZ with two edges of
+ // length x = 2*minAbsLat and y ~= lngGap. The desired distance is the
+ // length of the third edge z, and we have
+ //
+ // z ~= sqrt(x^2 + y^2) >= (x + y) / sqrt(2)
+ //
+ // Therefore the region may contain nearly antipodal points only if
+ //
+ // 2*minAbsLat + lngGap < sqrt(2) * 4.309 * dblEpsilon
+ // ~= 1.354e-15
+ //
+ // Note that because the given bound B is conservative, minAbsLat and
+ // lngGap are both lower bounds on their true values so we do not need
+ // to make any adjustments for their errors.
+ if 2*minAbsLat+lngGap < 1.354e-15 {
+ return FullRect()
+ }
+ } else if lngGap >= math.Pi/2 {
+ // B spans at most Pi/2 in longitude. The minimum distance is always
+ // between one corner of B and the diagonally opposite corner of B'. We
+ // use the same distance approximation that we used above; in this case
+ // we have an obtuse triangle XYZ with two edges of length x = latGapSouth
+ // and y = latGapNorth, and angle Z >= Pi/2 between them. We then have
+ //
+ // z >= sqrt(x^2 + y^2) >= (x + y) / sqrt(2)
+ //
+ // Unlike the case above, latGapSouth and latGapNorth are not lower bounds
+ // (because of the extra addition operation, and because math.Pi/2 is not
+ // exactly equal to Pi/2); they can exceed their true values by up to
+ // 0.75 * dblEpsilon. Putting this all together, the region may contain
+ // nearly antipodal points only if
+ //
+ // latGapSouth + latGapNorth < (sqrt(2) * 4.309 + 1.5) * dblEpsilon
+ // ~= 1.687e-15
+ if latGapSouth+latGapNorth < 1.687e-15 {
+ return FullRect()
+ }
+ } else {
+ // Otherwise we know that (1) the bound straddles the equator and (2) its
+ // width in longitude is at least Pi/2. In this case the minimum
+ // distance can occur either between a corner of B and the diagonally
+ // opposite corner of B' (as in the case above), or between a corner of B
+ // and the opposite longitudinal edge reflected in B'. It is sufficient
+ // to only consider the corner-edge case, since this distance is also a
+ // lower bound on the corner-corner distance when that case applies.
+
+ // Consider the spherical triangle XYZ where X is a corner of B with
+ // minimum absolute latitude, Y is the closest pole to X, and Z is the
+ // point closest to X on the opposite longitudinal edge of B'. This is a
+ // right triangle (Z = Pi/2), and from the spherical law of sines we have
+ //
+ // sin(z) / sin(Z) = sin(y) / sin(Y)
+ // sin(maxLatGap) / 1 = sin(dMin) / sin(lngGap)
+ // sin(dMin) = sin(maxLatGap) * sin(lngGap)
+ //
+ // where "maxLatGap" = max(latGapSouth, latGapNorth) and "dMin" is the
+ // desired minimum distance. Now using the facts that sin(t) >= (2/Pi)*t
+ // for 0 <= t <= Pi/2, that we only need an accurate approximation when
+ // at least one of "maxLatGap" or lngGap is extremely small (in which
+ // case sin(t) ~= t), and recalling that "maxLatGap" has an error of up
+ // to 0.75 * dblEpsilon, we want to test whether
+ //
+ // maxLatGap * lngGap < (4.309 + 0.75) * (Pi/2) * dblEpsilon
+ // ~= 1.765e-15
+ if math.Max(latGapSouth, latGapNorth)*lngGap < 1.765e-15 {
+ return FullRect()
+ }
+ }
+ // Next we need to check whether the subregion might contain any edges that
+ // span (math.Pi - 2 * dblEpsilon) radians or more in longitude, since AddPoint
+ // sets the longitude bound to Full in that case. This corresponds to
+ // testing whether (lngGap <= 0) in lngExpansion below.
+
+ // Otherwise, the maximum latitude error in AddPoint is 4.8 * dblEpsilon.
+ // In the worst case, the errors when computing the latitude bound for a
+ // subregion could go in the opposite direction as the errors when computing
+ // the bound for the original region, so we need to double this value.
+ // (More analysis shows that it's okay to round down to a multiple of
+ // dblEpsilon.)
+ //
+ // For longitude, we rely on the fact that atan2 is correctly rounded and
+ // therefore no additional bounds expansion is necessary.
+
+ latExpansion := 9 * dblEpsilon
+ lngExpansion := 0.0
+ if lngGap <= 0 {
+ lngExpansion = math.Pi
+ }
+ return bound.expanded(LatLng{s1.Angle(latExpansion), s1.Angle(lngExpansion)}).PolarClosure()
+}
+
+// EdgeCrosser allows edges to be efficiently tested for intersection with a
+// given fixed edge AB. It is especially efficient when testing for
+// intersection with an edge chain connecting vertices v0, v1, v2, ...
+type EdgeCrosser struct {
+ a Point
+ b Point
+ aXb Point
+
+ // To reduce the number of calls to expensiveSign, we compute an
+ // outward-facing tangent at A and B if necessary. If the plane
+ // perpendicular to one of these tangents separates AB from CD (i.e., one
+ // edge on each side) then there is no intersection.
+ aTangent Point // Outward-facing tangent at A.
+ bTangent Point // Outward-facing tangent at B.
+
+ // The fields below are updated for each vertex in the chain.
+ c Point // Previous vertex in the vertex chain.
+ acb Direction // The orientation of triangle ACB.
+}
+
+// NewEdgeCrosser returns an EdgeCrosser with the fixed edge AB.
+func NewEdgeCrosser(a, b Point) *EdgeCrosser {
+ norm := a.PointCross(b)
+ return &EdgeCrosser{
+ a: a,
+ b: b,
+ aXb: Point{a.Cross(b.Vector)},
+ aTangent: Point{a.Cross(norm.Vector)},
+ bTangent: Point{norm.Cross(b.Vector)},
+ }
+}
+
+// A Crossing indicates how edges cross.
+type Crossing int
+
+const (
+ // Cross means the edges cross.
+ Cross Crossing = iota
+ // MaybeCross means two vertices from different edges are the same.
+ MaybeCross
+ // DoNotCross means the edges do not cross.
+ DoNotCross
+)
+
+// CrossingSign reports whether the edge AB intersects the edge CD.
+// If any two vertices from different edges are the same, returns MaybeCross.
+// If either edge is degenerate (A == B or C == D), returns DoNotCross or MaybeCross.
+//
+// Properties of CrossingSign:
+//
+// (1) CrossingSign(b,a,c,d) == CrossingSign(a,b,c,d)
+// (2) CrossingSign(c,d,a,b) == CrossingSign(a,b,c,d)
+// (3) CrossingSign(a,b,c,d) == MaybeCross if a==c, a==d, b==c, b==d
+// (3) CrossingSign(a,b,c,d) == DoNotCross or MaybeCross if a==b or c==d
+//
+// Note that if you want to check an edge against a chain of other edges,
+// it is slightly more efficient to use the single-argument version
+// ChainCrossingSign below.
+func (e *EdgeCrosser) CrossingSign(c, d Point) Crossing {
+ if c != e.c {
+ e.RestartAt(c)
+ }
+ return e.ChainCrossingSign(d)
+}
+
+// EdgeOrVertexCrossing reports whether if CrossingSign(c, d) > 0, or AB and
+// CD share a vertex and VertexCrossing(a, b, c, d) is true.
+//
+// This method extends the concept of a "crossing" to the case where AB
+// and CD have a vertex in common. The two edges may or may not cross,
+// according to the rules defined in VertexCrossing above. The rules
+// are designed so that point containment tests can be implemented simply
+// by counting edge crossings. Similarly, determining whether one edge
+// chain crosses another edge chain can be implemented by counting.
+func (e *EdgeCrosser) EdgeOrVertexCrossing(c, d Point) bool {
+ if c != e.c {
+ e.RestartAt(c)
+ }
+ return e.EdgeOrVertexChainCrossing(d)
+}
+
+// NewChainEdgeCrosser is a convenience constructor that uses AB as the fixed edge,
+// and C as the first vertex of the vertex chain (equivalent to calling RestartAt(c)).
+//
+// You don't need to use this or any of the chain functions unless you're trying to
+// squeeze out every last drop of performance. Essentially all you are saving is a test
+// whether the first vertex of the current edge is the same as the second vertex of the
+// previous edge.
+func NewChainEdgeCrosser(a, b, c Point) *EdgeCrosser {
+ e := NewEdgeCrosser(a, b)
+ e.RestartAt(c)
+ return e
+}
+
+// RestartAt sets the current point of the edge crosser to be c.
+// Call this method when your chain 'jumps' to a new place.
+// The argument must point to a value that persists until the next call.
+func (e *EdgeCrosser) RestartAt(c Point) {
+ e.c = c
+ e.acb = -triageSign(e.a, e.b, e.c)
+}
+
+// ChainCrossingSign is like CrossingSign, but uses the last vertex passed to one of
+// the crossing methods (or RestartAt) as the first vertex of the current edge.
+func (e *EdgeCrosser) ChainCrossingSign(d Point) Crossing {
+ // For there to be an edge crossing, the triangles ACB, CBD, BDA, DAC must
+ // all be oriented the same way (CW or CCW). We keep the orientation of ACB
+ // as part of our state. When each new point D arrives, we compute the
+ // orientation of BDA and check whether it matches ACB. This checks whether
+ // the points C and D are on opposite sides of the great circle through AB.
+
+ // Recall that triageSign is invariant with respect to rotating its
+ // arguments, i.e. ABD has the same orientation as BDA.
+ bda := triageSign(e.a, e.b, d)
+ if e.acb == -bda && bda != Indeterminate {
+ // The most common case -- triangles have opposite orientations. Save the
+ // current vertex D as the next vertex C, and also save the orientation of
+ // the new triangle ACB (which is opposite to the current triangle BDA).
+ e.c = d
+ e.acb = -bda
+ return DoNotCross
+ }
+ return e.crossingSign(d, bda)
+}
+
+// EdgeOrVertexChainCrossing is like EdgeOrVertexCrossing, but uses the last vertex
+// passed to one of the crossing methods (or RestartAt) as the first vertex of the current edge.
+func (e *EdgeCrosser) EdgeOrVertexChainCrossing(d Point) bool {
+ // We need to copy e.c since it is clobbered by ChainCrossingSign.
+ c := e.c
+ switch e.ChainCrossingSign(d) {
+ case DoNotCross:
+ return false
+ case Cross:
+ return true
+ }
+ return VertexCrossing(e.a, e.b, c, d)
+}
+
+// crossingSign handle the slow path of CrossingSign.
+func (e *EdgeCrosser) crossingSign(d Point, bda Direction) Crossing {
+ // Compute the actual result, and then save the current vertex D as the next
+ // vertex C, and save the orientation of the next triangle ACB (which is
+ // opposite to the current triangle BDA).
+ defer func() {
+ e.c = d
+ e.acb = -bda
+ }()
+
+ // RobustSign is very expensive, so we avoid calling it if at all possible.
+ // First eliminate the cases where two vertices are equal.
+ if e.a == e.c || e.a == d || e.b == e.c || e.b == d {
+ return MaybeCross
+ }
+
+ // At this point, a very common situation is that A,B,C,D are four points on
+ // a line such that AB does not overlap CD. (For example, this happens when
+ // a line or curve is sampled finely, or when geometry is constructed by
+ // computing the union of S2CellIds.) Most of the time, we can determine
+ // that AB and CD do not intersect using the two outward-facing
+ // tangents at A and B (parallel to AB) and testing whether AB and CD are on
+ // opposite sides of the plane perpendicular to one of these tangents. This
+ // is moderately expensive but still much cheaper than expensiveSign.
+
+ // The error in RobustCrossProd is insignificant. The maximum error in
+ // the call to CrossProd (i.e., the maximum norm of the error vector) is
+ // (0.5 + 1/sqrt(3)) * dblEpsilon. The maximum error in each call to
+ // DotProd below is dblEpsilon. (There is also a small relative error
+ // term that is insignificant because we are comparing the result against a
+ // constant that is very close to zero.)
+ maxError := (1.5 + 1/math.Sqrt(3)) * dblEpsilon
+ if (e.c.Dot(e.aTangent.Vector) > maxError && d.Dot(e.aTangent.Vector) > maxError) || (e.c.Dot(e.bTangent.Vector) > maxError && d.Dot(e.bTangent.Vector) > maxError) {
+ return DoNotCross
+ }
+
+ // Otherwise it's time to break out the big guns.
+ if e.acb == Indeterminate {
+ e.acb = -expensiveSign(e.a, e.b, e.c)
+ }
+ if bda == Indeterminate {
+ bda = expensiveSign(e.a, e.b, d)
+ }
+
+ if bda != e.acb {
+ return DoNotCross
+ }
+
+ cbd := -RobustSign(e.c, d, e.b)
+ if cbd != e.acb {
+ return DoNotCross
+ }
+ dac := RobustSign(e.c, d, e.a)
+ if dac == e.acb {
+ return Cross
+ }
+ return DoNotCross
+}
+
+// pointUVW represents a Point in (u,v,w) coordinate space of a cube face.
+type pointUVW Point
+
+// intersectsFace reports whether a given directed line L intersects the cube face F.
+// The line L is defined by its normal N in the (u,v,w) coordinates of F.
+func (p pointUVW) intersectsFace() bool {
+ // L intersects the [-1,1]x[-1,1] square in (u,v) if and only if the dot
+ // products of N with the four corner vertices (-1,-1,1), (1,-1,1), (1,1,1),
+ // and (-1,1,1) do not all have the same sign. This is true exactly when
+ // |Nu| + |Nv| >= |Nw|. The code below evaluates this expression exactly.
+ u := math.Abs(p.X)
+ v := math.Abs(p.Y)
+ w := math.Abs(p.Z)
+
+ // We only need to consider the cases where u or v is the smallest value,
+ // since if w is the smallest then both expressions below will have a
+ // positive LHS and a negative RHS.
+ return (v >= w-u) && (u >= w-v)
+}
+
+// intersectsOppositeEdges reports whether a directed line L intersects two
+// opposite edges of a cube face F. This includs the case where L passes
+// exactly through a corner vertex of F. The directed line L is defined
+// by its normal N in the (u,v,w) coordinates of F.
+func (p pointUVW) intersectsOppositeEdges() bool {
+ // The line L intersects opposite edges of the [-1,1]x[-1,1] (u,v) square if
+ // and only exactly two of the corner vertices lie on each side of L. This
+ // is true exactly when ||Nu| - |Nv|| >= |Nw|. The code below evaluates this
+ // expression exactly.
+ u := math.Abs(p.X)
+ v := math.Abs(p.Y)
+ w := math.Abs(p.Z)
+
+ // If w is the smallest, the following line returns an exact result.
+ if math.Abs(u-v) != w {
+ return math.Abs(u-v) >= w
+ }
+
+ // Otherwise u - v = w exactly, or w is not the smallest value. In either
+ // case the following returns the correct result.
+ if u >= v {
+ return u-w >= v
+ }
+ return v-w >= u
+}
+
+// axis represents the possible results of exitAxis.
+type axis int
+
+const (
+ axisU axis = iota
+ axisV
+)
+
+// exitAxis reports which axis the directed line L exits the cube face F on.
+// The directed line L is represented by its CCW normal N in the (u,v,w) coordinates
+// of F. It returns axisU if L exits through the u=-1 or u=+1 edge, and axisV if L exits
+// through the v=-1 or v=+1 edge. Either result is acceptable if L exits exactly
+// through a corner vertex of the cube face.
+func (p pointUVW) exitAxis() axis {
+ if p.intersectsOppositeEdges() {
+ // The line passes through through opposite edges of the face.
+ // It exits through the v=+1 or v=-1 edge if the u-component of N has a
+ // larger absolute magnitude than the v-component.
+ if math.Abs(p.X) >= math.Abs(p.Y) {
+ return axisV
+ }
+ return axisU
+ }
+
+ // The line passes through through two adjacent edges of the face.
+ // It exits the v=+1 or v=-1 edge if an even number of the components of N
+ // are negative. We test this using signbit() rather than multiplication
+ // to avoid the possibility of underflow.
+ var x, y, z int
+ if math.Signbit(p.X) {
+ x = 1
+ }
+ if math.Signbit(p.Y) {
+ y = 1
+ }
+ if math.Signbit(p.Z) {
+ z = 1
+ }
+
+ if x^y^z == 0 {
+ return axisV
+ }
+ return axisU
+}
+
+// exitPoint returns the UV coordinates of the point where a directed line L (represented
+// by the CCW normal of this point), exits the cube face this point is derived from along
+// the given axis.
+func (p pointUVW) exitPoint(a axis) r2.Point {
+ if a == axisU {
+ u := -1.0
+ if p.Y > 0 {
+ u = 1.0
+ }
+ return r2.Point{u, (-u*p.X - p.Z) / p.Y}
+ }
+
+ v := -1.0
+ if p.X < 0 {
+ v = 1.0
+ }
+ return r2.Point{(-v*p.Y - p.Z) / p.X, v}
+}
+
+// clipDestination returns a score which is used to indicate if the clipped edge AB
+// on the given face intersects the face at all. This function returns the score for
+// the given endpoint, which is an integer ranging from 0 to 3. If the sum of the scores
+// from both of the endpoints is 3 or more, then edge AB does not intersect this face.
+//
+// First, it clips the line segment AB to find the clipped destination B' on a given
+// face. (The face is specified implicitly by expressing *all arguments* in the (u,v,w)
+// coordinates of that face.) Second, it partially computes whether the segment AB
+// intersects this face at all. The actual condition is fairly complicated, but it
+// turns out that it can be expressed as a "score" that can be computed independently
+// when clipping the two endpoints A and B.
+func clipDestination(a, b, scaledN, aTan, bTan pointUVW, scaleUV float64) (r2.Point, int) {
+ var uv r2.Point
+
+ // Optimization: if B is within the safe region of the face, use it.
+ maxSafeUVCoord := 1 - faceClipErrorUVCoord
+ if b.Z > 0 {
+ uv = r2.Point{b.X / b.Z, b.Y / b.Z}
+ if math.Max(math.Abs(uv.X), math.Abs(uv.Y)) <= maxSafeUVCoord {
+ return uv, 0
+ }
+ }
+
+ // Otherwise find the point B' where the line AB exits the face.
+ uv = scaledN.exitPoint(scaledN.exitAxis()).Mul(scaleUV)
+
+ p := pointUVW(PointFromCoords(uv.X, uv.Y, 1.0))
+
+ // Determine if the exit point B' is contained within the segment. We do this
+ // by computing the dot products with two inward-facing tangent vectors at A
+ // and B. If either dot product is negative, we say that B' is on the "wrong
+ // side" of that point. As the point B' moves around the great circle AB past
+ // the segment endpoint B, it is initially on the wrong side of B only; as it
+ // moves further it is on the wrong side of both endpoints; and then it is on
+ // the wrong side of A only. If the exit point B' is on the wrong side of
+ // either endpoint, we can't use it; instead the segment is clipped at the
+ // original endpoint B.
+ //
+ // We reject the segment if the sum of the scores of the two endpoints is 3
+ // or more. Here is what that rule encodes:
+ // - If B' is on the wrong side of A, then the other clipped endpoint A'
+ // must be in the interior of AB (otherwise AB' would go the wrong way
+ // around the circle). There is a similar rule for A'.
+ // - If B' is on the wrong side of either endpoint (and therefore we must
+ // use the original endpoint B instead), then it must be possible to
+ // project B onto this face (i.e., its w-coordinate must be positive).
+ // This rule is only necessary to handle certain zero-length edges (A=B).
+ score := 0
+ if p.Sub(a.Vector).Dot(aTan.Vector) < 0 {
+ score = 2 // B' is on wrong side of A.
+ } else if p.Sub(b.Vector).Dot(bTan.Vector) < 0 {
+ score = 1 // B' is on wrong side of B.
+ }
+
+ if score > 0 { // B' is not in the interior of AB.
+ if b.Z <= 0 {
+ score = 3 // B cannot be projected onto this face.
+ } else {
+ uv = r2.Point{b.X / b.Z, b.Y / b.Z}
+ }
+ }
+
+ return uv, score
+}
+
+// ClipToFace returns the (u,v) coordinates for the portion of the edge AB that
+// intersects the given face, or false if the edge AB does not intersect.
+// This method guarantees that the clipped vertices lie within the [-1,1]x[-1,1]
+// cube face rectangle and are within faceClipErrorUVDist of the line AB, but
+// the results may differ from those produced by faceSegments.
+func ClipToFace(a, b Point, face int) (aUV, bUV r2.Point, intersects bool) {
+ return ClipToPaddedFace(a, b, face, 0.0)
+}
+
+// ClipToPaddedFace returns the (u,v) coordinates for the portion of the edge AB that
+// intersects the given face, but rather than clipping to the square [-1,1]x[-1,1]
+// in (u,v) space, this method clips to [-R,R]x[-R,R] where R=(1+padding).
+// Padding must be non-negative.
+func ClipToPaddedFace(a, b Point, f int, padding float64) (aUV, bUV r2.Point, intersects bool) {
+ // Fast path: both endpoints are on the given face.
+ if face(a.Vector) == f && face(b.Vector) == f {
+ au, av := validFaceXYZToUV(f, a.Vector)
+ bu, bv := validFaceXYZToUV(f, b.Vector)
+ return r2.Point{au, av}, r2.Point{bu, bv}, true
+ }
+
+ // Convert everything into the (u,v,w) coordinates of the given face. Note
+ // that the cross product *must* be computed in the original (x,y,z)
+ // coordinate system because PointCross (unlike the mathematical cross
+ // product) can produce different results in different coordinate systems
+ // when one argument is a linear multiple of the other, due to the use of
+ // symbolic perturbations.
+ normUVW := pointUVW(faceXYZtoUVW(f, a.PointCross(b)))
+ aUVW := pointUVW(faceXYZtoUVW(f, a))
+ bUVW := pointUVW(faceXYZtoUVW(f, b))
+
+ // Padding is handled by scaling the u- and v-components of the normal.
+ // Letting R=1+padding, this means that when we compute the dot product of
+ // the normal with a cube face vertex (such as (-1,-1,1)), we will actually
+ // compute the dot product with the scaled vertex (-R,-R,1). This allows
+ // methods such as intersectsFace, exitAxis, etc, to handle padding
+ // with no further modifications.
+ scaleUV := 1 + padding
+ scaledN := pointUVW{r3.Vector{X: scaleUV * normUVW.X, Y: scaleUV * normUVW.Y, Z: normUVW.Z}}
+ if !scaledN.intersectsFace() {
+ return aUV, bUV, false
+ }
+
+ // TODO(roberts): This is a workaround for extremely small vectors where some
+ // loss of precision can occur in Normalize causing underflow. When PointCross
+ // is updated to work around this, this can be removed.
+ if math.Max(math.Abs(normUVW.X), math.Max(math.Abs(normUVW.Y), math.Abs(normUVW.Z))) < math.Ldexp(1, -511) {
+ normUVW = pointUVW{normUVW.Mul(math.Ldexp(1, 563))}
+ }
+
+ normUVW = pointUVW{normUVW.Normalize()}
+
+ aTan := pointUVW{normUVW.Cross(aUVW.Vector)}
+ bTan := pointUVW{bUVW.Cross(normUVW.Vector)}
+
+ // As described in clipDestination, if the sum of the scores from clipping the two
+ // endpoints is 3 or more, then the segment does not intersect this face.
+ aUV, aScore := clipDestination(bUVW, aUVW, pointUVW{scaledN.Mul(-1)}, bTan, aTan, scaleUV)
+ bUV, bScore := clipDestination(aUVW, bUVW, scaledN, aTan, bTan, scaleUV)
+
+ return aUV, bUV, aScore+bScore < 3
+}
+
+// interpolateDouble returns a value with the same combination of a1 and b1 as the
+// given value x is of a and b. This function makes the following guarantees:
+// - If x == a, then x1 = a1 (exactly).
+// - If x == b, then x1 = b1 (exactly).
+// - If a <= x <= b, then a1 <= x1 <= b1 (even if a1 == b1).
+// This requires a != b.
+func interpolateDouble(x, a, b, a1, b1 float64) float64 {
+ // To get results that are accurate near both A and B, we interpolate
+ // starting from the closer of the two points.
+ if math.Abs(a-x) <= math.Abs(b-x) {
+ return a1 + (b1-a1)*(x-a)/(b-a)
+ }
+ return b1 + (a1-b1)*(x-b)/(a-b)
+}
+
+// updateEndpoint returns the interval with the specified endpoint updated to
+// the given value. If the value lies beyond the opposite endpoint, nothing is
+// changed and false is returned.
+func updateEndpoint(bound r1.Interval, highEndpoint bool, value float64) (r1.Interval, bool) {
+ if !highEndpoint {
+ if bound.Hi < value {
+ return bound, false
+ }
+ if bound.Lo < value {
+ bound.Lo = value
+ }
+ return bound, true
+ }
+
+ if bound.Lo > value {
+ return bound, false
+ }
+ if bound.Hi > value {
+ bound.Hi = value
+ }
+ return bound, true
+}
+
+// clipBoundAxis returns the clipped versions of the bounding intervals for the given
+// axes for the line segment from (a0,a1) to (b0,b1) so that neither extends beyond the
+// given clip interval. negSlope is a precomputed helper variable that indicates which
+// diagonal of the bounding box is spanned by AB; it is false if AB has positive slope,
+// and true if AB has negative slope. If the clipping interval doesn't overlap the bounds,
+// false is returned.
+func clipBoundAxis(a0, b0 float64, bound0 r1.Interval, a1, b1 float64, bound1 r1.Interval,
+ negSlope bool, clip r1.Interval) (bound0c, bound1c r1.Interval, updated bool) {
+
+ if bound0.Lo < clip.Lo {
+ // If the upper bound is below the clips lower bound, there is nothing to do.
+ if bound0.Hi < clip.Lo {
+ return bound0, bound1, false
+ }
+ // narrow the intervals lower bound to the clip bound.
+ bound0.Lo = clip.Lo
+ if bound1, updated = updateEndpoint(bound1, negSlope, interpolateDouble(clip.Lo, a0, b0, a1, b1)); !updated {
+ return bound0, bound1, false
+ }
+ }
+
+ if bound0.Hi > clip.Hi {
+ // If the lower bound is above the clips upper bound, there is nothing to do.
+ if bound0.Lo > clip.Hi {
+ return bound0, bound1, false
+ }
+ // narrow the intervals upper bound to the clip bound.
+ bound0.Hi = clip.Hi
+ if bound1, updated = updateEndpoint(bound1, !negSlope, interpolateDouble(clip.Hi, a0, b0, a1, b1)); !updated {
+ return bound0, bound1, false
+ }
+ }
+ return bound0, bound1, true
+}
+
+// edgeIntersectsRect reports whether the edge defined by AB intersects the
+// given closed rectangle to within the error bound.
+func edgeIntersectsRect(a, b r2.Point, r r2.Rect) bool {
+ // First check whether the bounds of a Rect around AB intersects the given rect.
+ if !r.Intersects(r2.RectFromPoints(a, b)) {
+ return false
+ }
+
+ // Otherwise AB intersects the rect if and only if all four vertices of rect
+ // do not lie on the same side of the extended line AB. We test this by finding
+ // the two vertices of rect with minimum and maximum projections onto the normal
+ // of AB, and computing their dot products with the edge normal.
+ n := b.Sub(a).Ortho()
+
+ i := 0
+ if n.X >= 0 {
+ i = 1
+ }
+ j := 0
+ if n.Y >= 0 {
+ j = 1
+ }
+
+ max := n.Dot(r.VertexIJ(i, j).Sub(a))
+ min := n.Dot(r.VertexIJ(1-i, 1-j).Sub(a))
+
+ return (max >= 0) && (min <= 0)
+}
+
+// clippedEdgeBound returns the bounding rectangle of the portion of the edge defined
+// by AB intersected by clip. The resulting bound may be empty. This is a convenience
+// function built on top of clipEdgeBound.
+func clippedEdgeBound(a, b r2.Point, clip r2.Rect) r2.Rect {
+ bound := r2.RectFromPoints(a, b)
+ if b1, intersects := clipEdgeBound(a, b, clip, bound); intersects {
+ return b1
+ }
+ return r2.EmptyRect()
+}
+
+// clipEdgeBound clips an edge AB to sequence of rectangles efficiently.
+// It represents the clipped edges by their bounding boxes rather than as a pair of
+// endpoints. Specifically, let A'B' be some portion of an edge AB, and let bound be
+// a tight bound of A'B'. This function returns the bound that is a tight bound
+// of A'B' intersected with a given rectangle. If A'B' does not intersect clip,
+// it returns false and the original bound.
+func clipEdgeBound(a, b r2.Point, clip, bound r2.Rect) (r2.Rect, bool) {
+ // negSlope indicates which diagonal of the bounding box is spanned by AB: it
+ // is false if AB has positive slope, and true if AB has negative slope. This is
+ // used to determine which interval endpoints need to be updated each time
+ // the edge is clipped.
+ negSlope := (a.X > b.X) != (a.Y > b.Y)
+
+ b0x, b0y, up1 := clipBoundAxis(a.X, b.X, bound.X, a.Y, b.Y, bound.Y, negSlope, clip.X)
+ if !up1 {
+ return bound, false
+ }
+ b1y, b1x, up2 := clipBoundAxis(a.Y, b.Y, b0y, a.X, b.X, b0x, negSlope, clip.Y)
+ if !up2 {
+ return r2.Rect{b0x, b0y}, false
+ }
+ return r2.Rect{X: b1x, Y: b1y}, true
+}
+
+// ClipEdge returns the portion of the edge defined by AB that is contained by the
+// given rectangle. If there is no intersection, false is returned and aClip and bClip
+// are undefined.
+func ClipEdge(a, b r2.Point, clip r2.Rect) (aClip, bClip r2.Point, intersects bool) {
+ // Compute the bounding rectangle of AB, clip it, and then extract the new
+ // endpoints from the clipped bound.
+ bound := r2.RectFromPoints(a, b)
+ if bound, intersects = clipEdgeBound(a, b, clip, bound); !intersects {
+ return aClip, bClip, false
+ }
+ ai := 0
+ if a.X > b.X {
+ ai = 1
+ }
+ aj := 0
+ if a.Y > b.Y {
+ aj = 1
+ }
+
+ return bound.VertexIJ(ai, aj), bound.VertexIJ(1-ai, 1-aj), true
+}
+
+// ClosestPoint returns the point along the edge AB that is closest to the point X.
+// The fractional distance of this point along the edge AB can be obtained
+// using DistanceFraction.
+//
+// This requires that all points are unit length.
+func ClosestPoint(x, a, b Point) Point {
+ aXb := a.PointCross(b)
+ // Find the closest point to X along the great circle through AB.
+ p := x.Sub(aXb.Mul(x.Dot(aXb.Vector) / aXb.Vector.Norm2()))
+
+ // If this point is on the edge AB, then it's the closest point.
+ if Sign(aXb, a, Point{p}) && Sign(Point{p}, b, aXb) {
+ return Point{p.Normalize()}
+ }
+
+ // Otherwise, the closest point is either A or B.
+ if x.Sub(a.Vector).Norm2() <= x.Sub(b.Vector).Norm2() {
+ return a
+ }
+ return b
+}
+
+// DistanceFromSegment returns the distance of point x from line segment ab.
+// The points are expected to be normalized.
+func DistanceFromSegment(x, a, b Point) s1.Angle {
+ if d, ok := interiorDist(x, a, b); ok {
+ return d.Angle()
+ }
+ // Chord distance of x to both end points a and b.
+ xa2, xb2 := (x.Sub(a.Vector)).Norm2(), x.Sub(b.Vector).Norm2()
+ return s1.ChordAngle(math.Min(xa2, xb2)).Angle()
+}
+
+// interiorDist returns the shortest distance from point x to edge ab,
+// assuming that the closest point to x is interior to ab.
+// If the closest point is not interior to ab, interiorDist returns (0, false).
+func interiorDist(x, a, b Point) (s1.ChordAngle, bool) {
+ // Chord distance of x to both end points a and b.
+ xa2, xb2 := (x.Sub(a.Vector)).Norm2(), x.Sub(b.Vector).Norm2()
+
+ // The closest point on AB could either be one of the two vertices (the
+ // vertex case) or in the interior (the interior case). Let C = A x B.
+ // If X is in the spherical wedge extending from A to B around the axis
+ // through C, then we are in the interior case. Otherwise we are in the
+ // vertex case.
+ //
+ // Check whether we might be in the interior case. For this to be true, XAB
+ // and XBA must both be acute angles. Checking this condition exactly is
+ // expensive, so instead we consider the planar triangle ABX (which passes
+ // through the sphere's interior). The planar angles XAB and XBA are always
+ // less than the corresponding spherical angles, so if we are in the
+ // interior case then both of these angles must be acute.
+ //
+ // We check this by computing the squared edge lengths of the planar
+ // triangle ABX, and testing acuteness using the law of cosines:
+ //
+ // max(XA^2, XB^2) < min(XA^2, XB^2) + AB^2
+ if math.Max(xa2, xb2) >= math.Min(xa2, xb2)+(a.Sub(b.Vector)).Norm2() {
+ return 0, false
+ }
+
+ // The minimum distance might be to a point on the edge interior. Let R
+ // be closest point to X that lies on the great circle through AB. Rather
+ // than computing the geodesic distance along the surface of the sphere,
+ // instead we compute the "chord length" through the sphere's interior.
+ //
+ // The squared chord length XR^2 can be expressed as XQ^2 + QR^2, where Q
+ // is the point X projected onto the plane through the great circle AB.
+ // The distance XQ^2 can be written as (X.C)^2 / |C|^2 where C = A x B.
+ // We ignore the QR^2 term and instead use XQ^2 as a lower bound, since it
+ // is faster and the corresponding distance on the Earth's surface is
+ // accurate to within 1% for distances up to about 1800km.
+
+ // Test for the interior case. This test is very likely to succeed because
+ // of the conservative planar test we did initially.
+ c := a.PointCross(b)
+ c2 := c.Norm2()
+ cx := c.Cross(x.Vector)
+ if a.Dot(cx) >= 0 || b.Dot(cx) <= 0 {
+ return 0, false
+ }
+
+ // Compute the squared chord length XR^2 = XQ^2 + QR^2 (see above).
+ // This calculation has good accuracy for all chord lengths since it
+ // is based on both the dot product and cross product (rather than
+ // deriving one from the other). However, note that the chord length
+ // representation itself loses accuracy as the angle approaches π.
+ xDotC := x.Dot(c.Vector)
+ xDotC2 := xDotC * xDotC
+ qr := 1 - math.Sqrt(cx.Norm2()/c2)
+ return s1.ChordAngle((xDotC2 / c2) + (qr * qr)), true
+}
+
+// WedgeRel enumerates the possible relation between two wedges A and B.
+type WedgeRel int
+
+// Define the different possible relationships between two wedges.
+const (
+ WedgeEquals WedgeRel = iota // A and B are equal.
+ WedgeProperlyContains // A is a strict superset of B.
+ WedgeIsProperlyContained // A is a strict subset of B.
+ WedgeProperlyOverlaps // A-B, B-A, and A intersect B are non-empty.
+ WedgeIsDisjoint // A and B are disjoint.
+)
+
+// WedgeRelation reports the relation between two non-empty wedges
+// A=(a0, ab1, a2) and B=(b0, ab1, b2).
+func WedgeRelation(a0, ab1, a2, b0, b2 Point) WedgeRel {
+ // There are 6 possible edge orderings at a shared vertex (all
+ // of these orderings are circular, i.e. abcd == bcda):
+ //
+ // (1) a2 b2 b0 a0: A contains B
+ // (2) a2 a0 b0 b2: B contains A
+ // (3) a2 a0 b2 b0: A and B are disjoint
+ // (4) a2 b0 a0 b2: A and B intersect in one wedge
+ // (5) a2 b2 a0 b0: A and B intersect in one wedge
+ // (6) a2 b0 b2 a0: A and B intersect in two wedges
+ //
+ // We do not distinguish between 4, 5, and 6.
+ // We pay extra attention when some of the edges overlap. When edges
+ // overlap, several of these orderings can be satisfied, and we take
+ // the most specific.
+ if a0 == b0 && a2 == b2 {
+ return WedgeEquals
+ }
+
+ // Cases 1, 2, 5, and 6
+ if OrderedCCW(a0, a2, b2, ab1) {
+ // The cases with this vertex ordering are 1, 5, and 6,
+ if OrderedCCW(b2, b0, a0, ab1) {
+ return WedgeProperlyContains
+ }
+
+ // We are in case 5 or 6, or case 2 if a2 == b2.
+ if a2 == b2 {
+ return WedgeIsProperlyContained
+ }
+ return WedgeProperlyOverlaps
+
+ }
+ // We are in case 2, 3, or 4.
+ if OrderedCCW(a0, b0, b2, ab1) {
+ return WedgeIsProperlyContained
+ }
+
+ if OrderedCCW(a0, b0, a2, ab1) {
+ return WedgeIsDisjoint
+ }
+ return WedgeProperlyOverlaps
+}
+
+// WedgeContains reports whether non-empty wedge A=(a0, ab1, a2) contains B=(b0, ab1, b2).
+// Equivalent to WedgeRelation == WedgeProperlyContains || WedgeEquals.
+func WedgeContains(a0, ab1, a2, b0, b2 Point) bool {
+ // For A to contain B (where each loop interior is defined to be its left
+ // side), the CCW edge order around ab1 must be a2 b2 b0 a0. We split
+ // this test into two parts that test three vertices each.
+ return OrderedCCW(a2, b2, b0, ab1) && OrderedCCW(b0, a0, a2, ab1)
+}
+
+// WedgeIntersects reports whether non-empty wedge A=(a0, ab1, a2) intersects B=(b0, ab1, b2).
+// Equivalent to WedgeRelation == WedgeIsDisjoint
+func WedgeIntersects(a0, ab1, a2, b0, b2 Point) bool {
+ // For A not to intersect B (where each loop interior is defined to be
+ // its left side), the CCW edge order around ab1 must be a0 b2 b0 a2.
+ // Note that it's important to write these conditions as negatives
+ // (!OrderedCCW(a,b,c,o) rather than Ordered(c,b,a,o)) to get correct
+ // results when two vertices are the same.
+ return !(OrderedCCW(a0, b2, b0, ab1) && OrderedCCW(b0, a2, a0, ab1))
+}
+
+// TODO(roberts): Differences from C++
+// LongitudePruner
+// updateMinDistanceMaxError
+// IsDistanceLess
+// UpdateMinDistance
+// IsInteriorDistanceLess
+// UpdateMinInteriorDistance
+// UpdateEdgePairMinDistance
+// EdgePairClosestPoints
+// IsEdgeBNearEdgeA
+// FaceSegments
+// PointFromExact
+// IntersectionExact
+// intersectionExactError
diff --git a/vendor/github.com/golang/geo/s2/latlng.go b/vendor/github.com/golang/geo/s2/latlng.go
new file mode 100644
index 0000000..55532c7
--- /dev/null
+++ b/vendor/github.com/golang/geo/s2/latlng.go
@@ -0,0 +1,96 @@
+/*
+Copyright 2014 Google Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package s2
+
+import (
+ "fmt"
+ "math"
+
+ "github.com/golang/geo/s1"
+)
+
+const (
+ northPoleLat = s1.Angle(math.Pi/2) * s1.Radian
+ southPoleLat = -northPoleLat
+)
+
+// LatLng represents a point on the unit sphere as a pair of angles.
+type LatLng struct {
+ Lat, Lng s1.Angle
+}
+
+// LatLngFromDegrees returns a LatLng for the coordinates given in degrees.
+func LatLngFromDegrees(lat, lng float64) LatLng {
+ return LatLng{s1.Angle(lat) * s1.Degree, s1.Angle(lng) * s1.Degree}
+}
+
+// IsValid returns true iff the LatLng is normalized, with Lat ∈ [-π/2,π/2] and Lng ∈ [-π,π].
+func (ll LatLng) IsValid() bool {
+ return math.Abs(ll.Lat.Radians()) <= math.Pi/2 && math.Abs(ll.Lng.Radians()) <= math.Pi
+}
+
+// Normalized returns the normalized version of the LatLng,
+// with Lat clamped to [-π/2,π/2] and Lng wrapped in [-π,π].
+func (ll LatLng) Normalized() LatLng {
+ lat := ll.Lat
+ if lat > northPoleLat {
+ lat = northPoleLat
+ } else if lat < southPoleLat {
+ lat = southPoleLat
+ }
+ lng := s1.Angle(math.Remainder(ll.Lng.Radians(), 2*math.Pi)) * s1.Radian
+ return LatLng{lat, lng}
+}
+
+func (ll LatLng) String() string { return fmt.Sprintf("[%v, %v]", ll.Lat, ll.Lng) }
+
+// Distance returns the angle between two LatLngs.
+func (ll LatLng) Distance(ll2 LatLng) s1.Angle {
+ // Haversine formula, as used in C++ S2LatLng::GetDistance.
+ lat1, lat2 := ll.Lat.Radians(), ll2.Lat.Radians()
+ lng1, lng2 := ll.Lng.Radians(), ll2.Lng.Radians()
+ dlat := math.Sin(0.5 * (lat2 - lat1))
+ dlng := math.Sin(0.5 * (lng2 - lng1))
+ x := dlat*dlat + dlng*dlng*math.Cos(lat1)*math.Cos(lat2)
+ return s1.Angle(2*math.Atan2(math.Sqrt(x), math.Sqrt(math.Max(0, 1-x)))) * s1.Radian
+}
+
+// NOTE(mikeperrow): The C++ implementation publicly exposes latitude/longitude
+// functions. Let's see if that's really necessary before exposing the same functionality.
+
+func latitude(p Point) s1.Angle {
+ return s1.Angle(math.Atan2(p.Z, math.Sqrt(p.X*p.X+p.Y*p.Y))) * s1.Radian
+}
+
+func longitude(p Point) s1.Angle {
+ return s1.Angle(math.Atan2(p.Y, p.X)) * s1.Radian
+}
+
+// PointFromLatLng returns an Point for the given LatLng.
+// The maximum error in the result is 1.5 * dblEpsilon. (This does not
+// include the error of converting degrees, E5, E6, or E7 into radians.)
+func PointFromLatLng(ll LatLng) Point {
+ phi := ll.Lat.Radians()
+ theta := ll.Lng.Radians()
+ cosphi := math.Cos(phi)
+ return PointFromCoords(math.Cos(theta)*cosphi, math.Sin(theta)*cosphi, math.Sin(phi))
+}
+
+// LatLngFromPoint returns an LatLng for a given Point.
+func LatLngFromPoint(p Point) LatLng {
+ return LatLng{latitude(p), longitude(p)}
+}
diff --git a/vendor/github.com/golang/geo/s2/loop.go b/vendor/github.com/golang/geo/s2/loop.go
new file mode 100644
index 0000000..4d54860
--- /dev/null
+++ b/vendor/github.com/golang/geo/s2/loop.go
@@ -0,0 +1,282 @@
+/*
+Copyright 2015 Google Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package s2
+
+import (
+ "math"
+
+ "github.com/golang/geo/r1"
+ "github.com/golang/geo/r3"
+ "github.com/golang/geo/s1"
+)
+
+// Loop represents a simple spherical polygon. It consists of a sequence
+// of vertices where the first vertex is implicitly connected to the
+// last. All loops are defined to have a CCW orientation, i.e. the interior of
+// the loop is on the left side of the edges. This implies that a clockwise
+// loop enclosing a small area is interpreted to be a CCW loop enclosing a
+// very large area.
+//
+// Loops are not allowed to have any duplicate vertices (whether adjacent or
+// not), and non-adjacent edges are not allowed to intersect. Loops must have
+// at least 3 vertices (except for the "empty" and "full" loops discussed
+// below).
+//
+// There are two special loops: the "empty" loop contains no points and the
+// "full" loop contains all points. These loops do not have any edges, but to
+// preserve the invariant that every loop can be represented as a vertex
+// chain, they are defined as having exactly one vertex each (see EmptyLoop
+// and FullLoop).
+type Loop struct {
+ vertices []Point
+
+ // originInside keeps a precomputed value whether this loop contains the origin
+ // versus computing from the set of vertices every time.
+ originInside bool
+
+ // bound is a conservative bound on all points contained by this loop.
+ // If l.ContainsPoint(P), then l.bound.ContainsPoint(P).
+ bound Rect
+
+ // Since "bound" is not exact, it is possible that a loop A contains
+ // another loop B whose bounds are slightly larger. subregionBound
+ // has been expanded sufficiently to account for this error, i.e.
+ // if A.Contains(B), then A.subregionBound.Contains(B.bound).
+ subregionBound Rect
+}
+
+// LoopFromPoints constructs a loop from the given points.
+func LoopFromPoints(pts []Point) *Loop {
+ l := &Loop{
+ vertices: pts,
+ }
+
+ l.initOriginAndBound()
+ return l
+}
+
+// LoopFromCell constructs a loop corresponding to the given cell.
+//
+// Note that the loop and cell *do not* contain exactly the same set of
+// points, because Loop and Cell have slightly different definitions of
+// point containment. For example, a Cell vertex is contained by all
+// four neighboring Cells, but it is contained by exactly one of four
+// Loops constructed from those cells. As another example, the cell
+// coverings of cell and LoopFromCell(cell) will be different, because the
+// loop contains points on its boundary that actually belong to other cells
+// (i.e., the covering will include a layer of neighboring cells).
+func LoopFromCell(c Cell) *Loop {
+ l := &Loop{
+ vertices: []Point{
+ c.Vertex(0),
+ c.Vertex(1),
+ c.Vertex(2),
+ c.Vertex(3),
+ },
+ }
+
+ l.initOriginAndBound()
+ return l
+}
+
+// EmptyLoop returns a special "empty" loop.
+func EmptyLoop() *Loop {
+ return LoopFromPoints([]Point{{r3.Vector{X: 0, Y: 0, Z: 1}}})
+}
+
+// FullLoop returns a special "full" loop.
+func FullLoop() *Loop {
+ return LoopFromPoints([]Point{{r3.Vector{X: 0, Y: 0, Z: -1}}})
+}
+
+// initOriginAndBound sets the origin containment for the given point and then calls
+// the initialization for the bounds objects and the internal index.
+func (l *Loop) initOriginAndBound() {
+ if len(l.vertices) < 3 {
+ // Check for the special "empty" and "full" loops (which have one vertex).
+ if !l.isEmptyOrFull() {
+ l.originInside = false
+ return
+ }
+
+ // This is the special empty or full loop, so the origin depends on if
+ // the vertex is in the southern hemisphere or not.
+ l.originInside = l.vertices[0].Z < 0
+ } else {
+ // Point containment testing is done by counting edge crossings starting
+ // at a fixed point on the sphere (OriginPoint). We need to know whether
+ // the reference point (OriginPoint) is inside or outside the loop before
+ // we can construct the ShapeIndex. We do this by first guessing that
+ // it is outside, and then seeing whether we get the correct containment
+ // result for vertex 1. If the result is incorrect, the origin must be
+ // inside the loop.
+ //
+ // A loop with consecutive vertices A,B,C contains vertex B if and only if
+ // the fixed vector R = B.Ortho is contained by the wedge ABC. The
+ // wedge is closed at A and open at C, i.e. the point B is inside the loop
+ // if A = R but not if C = R. This convention is required for compatibility
+ // with VertexCrossing. (Note that we can't use OriginPoint
+ // as the fixed vector because of the possibility that B == OriginPoint.)
+ l.originInside = false
+ v1Inside := OrderedCCW(Point{l.vertices[1].Ortho()}, l.vertices[0], l.vertices[2], l.vertices[1])
+ if v1Inside != l.ContainsPoint(l.vertices[1]) {
+ l.originInside = true
+ }
+ }
+
+ // We *must* call initBound before initIndex, because initBound calls
+ // ContainsPoint(s2.Point), and ContainsPoint(s2.Point) does a bounds check whenever the
+ // index is not fresh (i.e., the loop has been added to the index but the
+ // index has not been updated yet).
+ l.initBound()
+
+ // TODO(roberts): Depends on s2shapeindex being implemented.
+ // l.initIndex()
+}
+
+// initBound sets up the approximate bounding Rects for this loop.
+func (l *Loop) initBound() {
+ // Check for the special "empty" and "full" loops.
+ if l.isEmptyOrFull() {
+ if l.IsEmpty() {
+ l.bound = EmptyRect()
+ } else {
+ l.bound = FullRect()
+ }
+ l.subregionBound = l.bound
+ return
+ }
+
+ // The bounding rectangle of a loop is not necessarily the same as the
+ // bounding rectangle of its vertices. First, the maximal latitude may be
+ // attained along the interior of an edge. Second, the loop may wrap
+ // entirely around the sphere (e.g. a loop that defines two revolutions of a
+ // candy-cane stripe). Third, the loop may include one or both poles.
+ // Note that a small clockwise loop near the equator contains both poles.
+ bounder := NewRectBounder()
+ for i := 0; i <= len(l.vertices); i++ { // add vertex 0 twice
+ bounder.AddPoint(l.Vertex(i))
+ }
+ b := bounder.RectBound()
+
+ if l.ContainsPoint(Point{r3.Vector{0, 0, 1}}) {
+ b = Rect{r1.Interval{b.Lat.Lo, math.Pi / 2}, s1.FullInterval()}
+ }
+ // If a loop contains the south pole, then either it wraps entirely
+ // around the sphere (full longitude range), or it also contains the
+ // north pole in which case b.Lng.IsFull() due to the test above.
+ // Either way, we only need to do the south pole containment test if
+ // b.Lng.IsFull().
+ if b.Lng.IsFull() && l.ContainsPoint(Point{r3.Vector{0, 0, -1}}) {
+ b.Lat.Lo = -math.Pi / 2
+ }
+ l.bound = b
+ l.subregionBound = ExpandForSubregions(l.bound)
+}
+
+// ContainsOrigin reports true if this loop contains s2.OriginPoint().
+func (l Loop) ContainsOrigin() bool {
+ return l.originInside
+}
+
+// HasInterior returns true because all loops have an interior.
+func (l Loop) HasInterior() bool {
+ return true
+}
+
+// NumEdges returns the number of edges in this shape.
+func (l Loop) NumEdges() int {
+ if l.isEmptyOrFull() {
+ return 0
+ }
+ return len(l.vertices)
+}
+
+// Edge returns the endpoints for the given edge index.
+func (l Loop) Edge(i int) (a, b Point) {
+ return l.Vertex(i), l.Vertex(i + 1)
+}
+
+// IsEmpty reports true if this is the special "empty" loop that contains no points.
+func (l Loop) IsEmpty() bool {
+ return l.isEmptyOrFull() && !l.ContainsOrigin()
+}
+
+// IsFull reports true if this is the special "full" loop that contains all points.
+func (l Loop) IsFull() bool {
+ return l.isEmptyOrFull() && l.ContainsOrigin()
+}
+
+// isEmptyOrFull reports true if this loop is either the "empty" or "full" special loops.
+func (l Loop) isEmptyOrFull() bool {
+ return len(l.vertices) == 1
+}
+
+// RectBound returns a tight bounding rectangle. If the loop contains the point,
+// the bound also contains it.
+func (l Loop) RectBound() Rect {
+ return l.bound
+}
+
+// CapBound returns a bounding cap that may have more padding than the corresponding
+// RectBound. The bound is conservative such that if the loop contains a point P,
+// the bound also contains it.
+func (l Loop) CapBound() Cap {
+ return l.bound.CapBound()
+}
+
+// Vertex returns the vertex for the given index. For convenience, the vertex indices
+// wrap automatically for methods that do index math such as Edge.
+// i.e., Vertex(NumEdges() + n) is the same as Vertex(n).
+func (l Loop) Vertex(i int) Point {
+ return l.vertices[i%len(l.vertices)]
+}
+
+// Vertices returns the vertices in the loop.
+func (l Loop) Vertices() []Point {
+ return l.vertices
+}
+
+// ContainsPoint returns true if the loop contains the point.
+func (l Loop) ContainsPoint(p Point) bool {
+ // TODO(sbeckman): Move to bruteForceContains and update with ShapeIndex when available.
+ // Empty and full loops don't need a special case, but invalid loops with
+ // zero vertices do, so we might as well handle them all at once.
+ if len(l.vertices) < 3 {
+ return l.originInside
+ }
+
+ origin := OriginPoint()
+ inside := l.originInside
+ crosser := NewChainEdgeCrosser(origin, p, l.Vertex(0))
+ for i := 1; i <= len(l.vertices); i++ { // add vertex 0 twice
+ inside = inside != crosser.EdgeOrVertexChainCrossing(l.Vertex(i))
+ }
+ return inside
+}
+
+// RegularLoop creates a loop with the given number of vertices, all
+// located on a circle of the specified radius around the given center.
+func RegularLoop(center Point, radius s1.Angle, numVertices int) *Loop {
+ return RegularLoopForFrame(getFrame(center), radius, numVertices)
+}
+
+// RegularLoopForFrame creates a loop centered around the z-axis of the given
+// coordinate frame, with the first vertex in the direction of the positive x-axis.
+func RegularLoopForFrame(frame matrix3x3, radius s1.Angle, numVertices int) *Loop {
+ return LoopFromPoints(regularPointsForFrame(frame, radius, numVertices))
+}
diff --git a/vendor/github.com/golang/geo/s2/matrix3x3.go b/vendor/github.com/golang/geo/s2/matrix3x3.go
new file mode 100644
index 0000000..1f78d5d
--- /dev/null
+++ b/vendor/github.com/golang/geo/s2/matrix3x3.go
@@ -0,0 +1,127 @@
+/*
+Copyright 2015 Google Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package s2
+
+import (
+ "fmt"
+)
+
+// matrix3x3 represents a traditional 3x3 matrix of floating point values.
+// This is not a full fledged matrix. It only contains the pieces needed
+// to satisfy the computations done within the s2 package.
+type matrix3x3 [3][3]float64
+
+// col returns the given column as a Point.
+func (m *matrix3x3) col(col int) Point {
+ return PointFromCoords(m[0][col], m[1][col], m[2][col])
+}
+
+// row returns the given row as a Point.
+func (m *matrix3x3) row(row int) Point {
+ return PointFromCoords(m[row][0], m[row][1], m[row][2])
+}
+
+// setCol sets the specified column to the value in the given Point.
+func (m *matrix3x3) setCol(col int, p Point) *matrix3x3 {
+ m[0][col] = p.X
+ m[1][col] = p.Y
+ m[2][col] = p.Z
+
+ return m
+}
+
+// setRow sets the specified row to the value in the given Point.
+func (m *matrix3x3) setRow(row int, p Point) *matrix3x3 {
+ m[row][0] = p.X
+ m[row][1] = p.Y
+ m[row][2] = p.Z
+
+ return m
+}
+
+// scale multiplies the matrix by the given value.
+func (m *matrix3x3) scale(f float64) *matrix3x3 {
+ return &matrix3x3{
+ [3]float64{f * m[0][0], f * m[0][1], f * m[0][2]},
+ [3]float64{f * m[1][0], f * m[1][1], f * m[1][2]},
+ [3]float64{f * m[2][0], f * m[2][1], f * m[2][2]},
+ }
+}
+
+// mul returns the multiplication of m by the Point p and converts the
+// resulting 1x3 matrix into a Point.
+func (m *matrix3x3) mul(p Point) Point {
+ return PointFromCoords(
+ m[0][0]*p.X+m[0][1]*p.Y+m[0][2]*p.Z,
+ m[1][0]*p.X+m[1][1]*p.Y+m[1][2]*p.Z,
+ m[2][0]*p.X+m[2][1]*p.Y+m[2][2]*p.Z,
+ )
+}
+
+// det returns the determinant of this matrix.
+func (m *matrix3x3) det() float64 {
+ // | a b c |
+ // det | d e f | = aei + bfg + cdh - ceg - bdi - afh
+ // | g h i |
+ return m[0][0]*m[1][1]*m[2][2] + m[0][1]*m[1][2]*m[2][0] + m[0][2]*m[1][0]*m[2][1] -
+ m[0][2]*m[1][1]*m[2][0] - m[0][1]*m[1][0]*m[2][2] - m[0][0]*m[1][2]*m[2][1]
+}
+
+// transpose reflects the matrix along its diagonal and returns the result.
+func (m *matrix3x3) transpose() *matrix3x3 {
+ m[0][1], m[1][0] = m[1][0], m[0][1]
+ m[0][2], m[2][0] = m[2][0], m[0][2]
+ m[1][2], m[2][1] = m[2][1], m[1][2]
+
+ return m
+}
+
+// String formats the matrix into an easier to read layout.
+func (m *matrix3x3) String() string {
+ return fmt.Sprintf("[ %0.4f %0.4f %0.4f ] [ %0.4f %0.4f %0.4f ] [ %0.4f %0.4f %0.4f ]",
+ m[0][0], m[0][1], m[0][2],
+ m[1][0], m[1][1], m[1][2],
+ m[2][0], m[2][1], m[2][2],
+ )
+}
+
+// getFrame returns the orthonormal frame for the given point on the unit sphere.
+func getFrame(p Point) matrix3x3 {
+ // Given the point p on the unit sphere, extend this into a right-handed
+ // coordinate frame of unit-length column vectors m = (x,y,z). Note that
+ // the vectors (x,y) are an orthonormal frame for the tangent space at point p,
+ // while p itself is an orthonormal frame for the normal space at p.
+ m := matrix3x3{}
+ m.setCol(2, p)
+ m.setCol(1, Point{p.Ortho()})
+ m.setCol(0, Point{m.col(1).Cross(p.Vector)})
+ return m
+}
+
+// toFrame returns the coordinates of the given point with respect to its orthonormal basis m.
+// The resulting point q satisfies the identity (m * q == p).
+func toFrame(m matrix3x3, p Point) Point {
+ // The inverse of an orthonormal matrix is its transpose.
+ return m.transpose().mul(p)
+}
+
+// fromFrame returns the coordinates of the given point in standard axis-aligned basis
+// from its orthonormal basis m.
+// The resulting point p satisfies the identity (p == m * q).
+func fromFrame(m matrix3x3, q Point) Point {
+ return m.mul(q)
+}
diff --git a/vendor/github.com/golang/geo/s2/metric.go b/vendor/github.com/golang/geo/s2/metric.go
new file mode 100644
index 0000000..a005f79
--- /dev/null
+++ b/vendor/github.com/golang/geo/s2/metric.go
@@ -0,0 +1,166 @@
+/*
+Copyright 2015 Google Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package s2
+
+// This file implements functions for various S2 measurements.
+
+import "math"
+
+// A Metric is a measure for cells. It is used to describe the shape and size
+// of cells. They are useful for deciding which cell level to use in order to
+// satisfy a given condition (e.g. that cell vertices must be no further than
+// "x" apart). You can use the Value(level) method to compute the corresponding
+// length or area on the unit sphere for cells at a given level. The minimum
+// and maximum bounds are valid for cells at all levels, but they may be
+// somewhat conservative for very large cells (e.g. face cells).
+type Metric struct {
+ // Dim is either 1 or 2, for a 1D or 2D metric respectively.
+ Dim int
+ // Deriv is the scaling factor for the metric.
+ Deriv float64
+}
+
+// Defined metrics.
+// Of the projection methods defined in C++, Go only supports the quadratic projection.
+
+// Each cell is bounded by four planes passing through its four edges and
+// the center of the sphere. These metrics relate to the angle between each
+// pair of opposite bounding planes, or equivalently, between the planes
+// corresponding to two different s-values or two different t-values.
+var (
+ MinAngleSpanMetric = Metric{1, 4.0 / 3}
+ AvgAngleSpanMetric = Metric{1, math.Pi / 2}
+ MaxAngleSpanMetric = Metric{1, 1.704897179199218452}
+)
+
+// The width of geometric figure is defined as the distance between two
+// parallel bounding lines in a given direction. For cells, the minimum
+// width is always attained between two opposite edges, and the maximum
+// width is attained between two opposite vertices. However, for our
+// purposes we redefine the width of a cell as the perpendicular distance
+// between a pair of opposite edges. A cell therefore has two widths, one
+// in each direction. The minimum width according to this definition agrees
+// with the classic geometric one, but the maximum width is different. (The
+// maximum geometric width corresponds to MaxDiag defined below.)
+//
+// The average width in both directions for all cells at level k is approximately
+// AvgWidthMetric.Value(k).
+//
+// The width is useful for bounding the minimum or maximum distance from a
+// point on one edge of a cell to the closest point on the opposite edge.
+// For example, this is useful when growing regions by a fixed distance.
+var (
+ MinWidthMetric = Metric{1, 2 * math.Sqrt2 / 3}
+ AvgWidthMetric = Metric{1, 1.434523672886099389}
+ MaxWidthMetric = Metric{1, MaxAngleSpanMetric.Deriv}
+)
+
+// The edge length metrics can be used to bound the minimum, maximum,
+// or average distance from the center of one cell to the center of one of
+// its edge neighbors. In particular, it can be used to bound the distance
+// between adjacent cell centers along the space-filling Hilbert curve for
+// cells at any given level.
+var (
+ MinEdgeMetric = Metric{1, 2 * math.Sqrt2 / 3}
+ AvgEdgeMetric = Metric{1, 1.459213746386106062}
+ MaxEdgeMetric = Metric{1, MaxAngleSpanMetric.Deriv}
+
+ // MaxEdgeAspect is the maximum edge aspect ratio over all cells at any level,
+ // where the edge aspect ratio of a cell is defined as the ratio of its longest
+ // edge length to its shortest edge length.
+ MaxEdgeAspect = 1.442615274452682920
+
+ MinAreaMetric = Metric{2, 8 * math.Sqrt2 / 9}
+ AvgAreaMetric = Metric{2, 4 * math.Pi / 6}
+ MaxAreaMetric = Metric{2, 2.635799256963161491}
+)
+
+// The maximum diagonal is also the maximum diameter of any cell,
+// and also the maximum geometric width (see the comment for widths). For
+// example, the distance from an arbitrary point to the closest cell center
+// at a given level is at most half the maximum diagonal length.
+var (
+ MinDiagMetric = Metric{1, 8 * math.Sqrt2 / 9}
+ AvgDiagMetric = Metric{1, 2.060422738998471683}
+ MaxDiagMetric = Metric{1, 2.438654594434021032}
+
+ // MaxDiagAspect is the maximum diagonal aspect ratio over all cells at any
+ // level, where the diagonal aspect ratio of a cell is defined as the ratio
+ // of its longest diagonal length to its shortest diagonal length.
+ MaxDiagAspect = math.Sqrt(3)
+)
+
+// Value returns the value of the metric at the given level.
+func (m Metric) Value(level int) float64 {
+ return math.Ldexp(m.Deriv, -m.Dim*level)
+}
+
+// MinLevel returns the minimum level such that the metric is at most
+// the given value, or maxLevel (30) if there is no such level.
+//
+// For example, MinLevel(0.1) returns the minimum level such that all cell diagonal
+// lengths are 0.1 or smaller. The returned value is always a valid level.
+//
+// In C++, this is called GetLevelForMaxValue.
+func (m Metric) MinLevel(val float64) int {
+ if val < 0 {
+ return maxLevel
+ }
+
+ level := -(math.Ilogb(val/m.Deriv) >> uint(m.Dim-1))
+ if level > maxLevel {
+ level = maxLevel
+ }
+ if level < 0 {
+ level = 0
+ }
+ return level
+}
+
+// MaxLevel returns the maximum level such that the metric is at least
+// the given value, or zero if there is no such level.
+//
+// For example, MaxLevel(0.1) returns the maximum level such that all cells have a
+// minimum width of 0.1 or larger. The returned value is always a valid level.
+//
+// In C++, this is called GetLevelForMinValue.
+func (m Metric) MaxLevel(val float64) int {
+ if val <= 0 {
+ return maxLevel
+ }
+
+ level := math.Ilogb(m.Deriv/val) >> uint(m.Dim-1)
+ if level > maxLevel {
+ level = maxLevel
+ }
+ if level < 0 {
+ level = 0
+ }
+ return level
+}
+
+// ClosestLevel returns the level at which the metric has approximately the given
+// value. The return value is always a valid level. For example,
+// AvgEdgeMetric.ClosestLevel(0.1) returns the level at which the average cell edge
+// length is approximately 0.1.
+func (m Metric) ClosestLevel(val float64) int {
+ x := math.Sqrt2
+ if m.Dim == 2 {
+ x = 2
+ }
+ return m.MinLevel(x * val)
+}
diff --git a/vendor/github.com/golang/geo/s2/paddedcell.go b/vendor/github.com/golang/geo/s2/paddedcell.go
new file mode 100644
index 0000000..03d4b35
--- /dev/null
+++ b/vendor/github.com/golang/geo/s2/paddedcell.go
@@ -0,0 +1,254 @@
+/*
+Copyright 2016 Google Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package s2
+
+import (
+ "github.com/golang/geo/r1"
+ "github.com/golang/geo/r2"
+)
+
+// PaddedCell represents a Cell whose (u,v)-range has been expanded on
+// all sides by a given amount of "padding". Unlike Cell, its methods and
+// representation are optimized for clipping edges against Cell boundaries
+// to determine which cells are intersected by a given set of edges.
+type PaddedCell struct {
+ id CellID
+ padding float64
+ bound r2.Rect
+ middle r2.Rect // A rect in (u, v)-space that belongs to all four children.
+ iLo, jLo int // Minimum (i,j)-coordinates of this cell before padding
+ orientation int // Hilbert curve orientation of this cell.
+ level int
+}
+
+// PaddedCellFromCellID constructs a padded cell with the given padding.
+func PaddedCellFromCellID(id CellID, padding float64) *PaddedCell {
+ p := &PaddedCell{
+ id: id,
+ padding: padding,
+ middle: r2.EmptyRect(),
+ }
+
+ // Fast path for constructing a top-level face (the most common case).
+ if id.isFace() {
+ limit := padding + 1
+ p.bound = r2.Rect{r1.Interval{-limit, limit}, r1.Interval{-limit, limit}}
+ p.middle = r2.Rect{r1.Interval{-padding, padding}, r1.Interval{-padding, padding}}
+ p.orientation = id.Face() & 1
+ return p
+ }
+
+ _, p.iLo, p.jLo, p.orientation = id.faceIJOrientation()
+ p.level = id.Level()
+ p.bound = ijLevelToBoundUV(p.iLo, p.jLo, p.level).ExpandedByMargin(padding)
+ ijSize := sizeIJ(p.level)
+ p.iLo &= -ijSize
+ p.jLo &= -ijSize
+
+ return p
+}
+
+// PaddedCellFromParentIJ constructs the child of parent with the given (i,j) index.
+// The four child cells have indices of (0,0), (0,1), (1,0), (1,1), where the i and j
+// indices correspond to increasing u- and v-values respectively.
+func PaddedCellFromParentIJ(parent *PaddedCell, i, j int) *PaddedCell {
+ // Compute the position and orientation of the child incrementally from the
+ // orientation of the parent.
+ pos := ijToPos[parent.orientation][2*i+j]
+
+ p := &PaddedCell{
+ id: parent.id.Children()[pos],
+ padding: parent.padding,
+ bound: parent.bound,
+ orientation: parent.orientation ^ posToOrientation[pos],
+ level: parent.level + 1,
+ middle: r2.EmptyRect(),
+ }
+
+ ijSize := sizeIJ(p.level)
+ p.iLo = parent.iLo + i*ijSize
+ p.jLo = parent.jLo + j*ijSize
+
+ // For each child, one corner of the bound is taken directly from the parent
+ // while the diagonally opposite corner is taken from middle().
+ middle := parent.Middle()
+ if i == 1 {
+ p.bound.X.Lo = middle.X.Lo
+ } else {
+ p.bound.X.Hi = middle.X.Hi
+ }
+ if j == 1 {
+ p.bound.Y.Lo = middle.Y.Lo
+ } else {
+ p.bound.Y.Hi = middle.Y.Hi
+ }
+
+ return p
+}
+
+// CellID returns the CellID this padded cell represents.
+func (p PaddedCell) CellID() CellID {
+ return p.id
+}
+
+// Padding returns the amount of padding on this cell.
+func (p PaddedCell) Padding() float64 {
+ return p.padding
+}
+
+// Level returns the level this cell is at.
+func (p PaddedCell) Level() int {
+ return p.level
+}
+
+// Center returns the center of this cell.
+func (p PaddedCell) Center() Point {
+ ijSize := sizeIJ(p.level)
+ si := uint64(2*p.iLo + ijSize)
+ ti := uint64(2*p.jLo + ijSize)
+ return Point{faceSiTiToXYZ(p.id.Face(), si, ti).Normalize()}
+}
+
+// Middle returns the rectangle in the middle of this cell that belongs to
+// all four of its children in (u,v)-space.
+func (p *PaddedCell) Middle() r2.Rect {
+ // We compute this field lazily because it is not needed the majority of the
+ // time (i.e., for cells where the recursion terminates).
+ if p.middle.IsEmpty() {
+ ijSize := sizeIJ(p.level)
+ u := stToUV(siTiToST(uint64(2*p.iLo + ijSize)))
+ v := stToUV(siTiToST(uint64(2*p.jLo + ijSize)))
+ p.middle = r2.Rect{
+ r1.Interval{u - p.padding, u + p.padding},
+ r1.Interval{v - p.padding, v + p.padding},
+ }
+ }
+ return p.middle
+}
+
+// Bound returns the bounds for this cell in (u,v)-space including padding.
+func (p PaddedCell) Bound() r2.Rect {
+ return p.bound
+}
+
+// ChildIJ returns the (i,j) coordinates for the child cell at the given traversal
+// position. The traversal position corresponds to the order in which child
+// cells are visited by the Hilbert curve.
+func (p PaddedCell) ChildIJ(pos int) (i, j int) {
+ ij := posToIJ[p.orientation][pos]
+ return ij >> 1, ij & 1
+}
+
+// EntryVertex return the vertex where the space-filling curve enters this cell.
+func (p PaddedCell) EntryVertex() Point {
+ // The curve enters at the (0,0) vertex unless the axis directions are
+ // reversed, in which case it enters at the (1,1) vertex.
+ i := p.iLo
+ j := p.jLo
+ if p.orientation&invertMask != 0 {
+ ijSize := sizeIJ(p.level)
+ i += ijSize
+ j += ijSize
+ }
+ return Point{faceSiTiToXYZ(p.id.Face(), uint64(2*i), uint64(2*j)).Normalize()}
+}
+
+// ExitVertex returns the vertex where the space-filling curve exits this cell.
+func (p PaddedCell) ExitVertex() Point {
+ // The curve exits at the (1,0) vertex unless the axes are swapped or
+ // inverted but not both, in which case it exits at the (0,1) vertex.
+ i := p.iLo
+ j := p.jLo
+ ijSize := sizeIJ(p.level)
+ if p.orientation == 0 || p.orientation == swapMask+invertMask {
+ i += ijSize
+ } else {
+ j += ijSize
+ }
+ return Point{faceSiTiToXYZ(p.id.Face(), uint64(2*i), uint64(2*j)).Normalize()}
+}
+
+// ShrinkToFit returns the smallest CellID that contains all descendants of this
+// padded cell whose bounds intersect the given rect. For algorithms that use
+// recursive subdivision to find the cells that intersect a particular object, this
+// method can be used to skip all of the initial subdivision steps where only
+// one child needs to be expanded.
+//
+// Note that this method is not the same as returning the smallest cell that contains
+// the intersection of this cell with rect. Because of the padding, even if one child
+// completely contains rect it is still possible that a neighboring child may also
+// intersect the given rect.
+//
+// The provided Rect must intersect the bounds of this cell.
+func (p *PaddedCell) ShrinkToFit(rect r2.Rect) CellID {
+ // Quick rejection test: if rect contains the center of this cell along
+ // either axis, then no further shrinking is possible.
+ if p.level == 0 {
+ // Fast path (most calls to this function start with a face cell).
+ if rect.X.Contains(0) || rect.Y.Contains(0) {
+ return p.id
+ }
+ }
+
+ ijSize := sizeIJ(p.level)
+ if rect.X.Contains(stToUV(siTiToST(uint64(2*p.iLo+ijSize)))) ||
+ rect.Y.Contains(stToUV(siTiToST(uint64(2*p.jLo+ijSize)))) {
+ return p.id
+ }
+
+ // Otherwise we expand rect by the given padding on all sides and find
+ // the range of coordinates that it spans along the i- and j-axes. We then
+ // compute the highest bit position at which the min and max coordinates
+ // differ. This corresponds to the first cell level at which at least two
+ // children intersect rect.
+
+ // Increase the padding to compensate for the error in uvToST.
+ // (The constant below is a provable upper bound on the additional error.)
+ padded := rect.ExpandedByMargin(p.padding + 1.5*dblEpsilon)
+ iMin, jMin := p.iLo, p.jLo // Min i- or j- coordinate spanned by padded
+ var iXor, jXor int // XOR of the min and max i- or j-coordinates
+
+ if iMin < stToIJ(uvToST(padded.X.Lo)) {
+ iMin = stToIJ(uvToST(padded.X.Lo))
+ }
+ if a, b := p.iLo+ijSize-1, stToIJ(uvToST(padded.X.Hi)); a <= b {
+ iXor = iMin ^ a
+ } else {
+ iXor = iMin ^ b
+ }
+
+ if jMin < stToIJ(uvToST(padded.Y.Lo)) {
+ jMin = stToIJ(uvToST(padded.Y.Lo))
+ }
+ if a, b := p.jLo+ijSize-1, stToIJ(uvToST(padded.Y.Hi)); a <= b {
+ jXor = jMin ^ a
+ } else {
+ jXor = jMin ^ b
+ }
+
+ // Compute the highest bit position where the two i- or j-endpoints differ,
+ // and then choose the cell level that includes both of these endpoints. So
+ // if both pairs of endpoints are equal we choose maxLevel; if they differ
+ // only at bit 0, we choose (maxLevel - 1), and so on.
+ levelMSB := uint64(((iXor | jXor) << 1) + 1)
+ level := maxLevel - int(findMSBSetNonZero64(levelMSB))
+ if level <= p.level {
+ return p.id
+ }
+
+ return cellIDFromFaceIJ(p.id.Face(), iMin, jMin).Parent(level)
+}
diff --git a/vendor/github.com/golang/geo/s2/point.go b/vendor/github.com/golang/geo/s2/point.go
new file mode 100644
index 0000000..2b300cd
--- /dev/null
+++ b/vendor/github.com/golang/geo/s2/point.go
@@ -0,0 +1,291 @@
+/*
+Copyright 2014 Google Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package s2
+
+import (
+ "math"
+
+ "github.com/golang/geo/r3"
+ "github.com/golang/geo/s1"
+)
+
+// Point represents a point on the unit sphere as a normalized 3D vector.
+// Points are guaranteed to be close to normalized.
+// Fields should be treated as read-only. Use one of the factory methods for creation.
+type Point struct {
+ r3.Vector
+}
+
+// PointFromCoords creates a new normalized point from coordinates.
+//
+// This always returns a valid point. If the given coordinates can not be normalized
+// the origin point will be returned.
+//
+// This behavior is different from the C++ construction of a S2Point from coordinates
+// (i.e. S2Point(x, y, z)) in that in C++ they do not Normalize.
+func PointFromCoords(x, y, z float64) Point {
+ if x == 0 && y == 0 && z == 0 {
+ return OriginPoint()
+ }
+ return Point{r3.Vector{x, y, z}.Normalize()}
+}
+
+// OriginPoint returns a unique "origin" on the sphere for operations that need a fixed
+// reference point. In particular, this is the "point at infinity" used for
+// point-in-polygon testing (by counting the number of edge crossings).
+//
+// It should *not* be a point that is commonly used in edge tests in order
+// to avoid triggering code to handle degenerate cases (this rules out the
+// north and south poles). It should also not be on the boundary of any
+// low-level S2Cell for the same reason.
+func OriginPoint() Point {
+ return Point{r3.Vector{-0.0099994664350250197, 0.0025924542609324121, 0.99994664350250195}}
+}
+
+// PointCross returns a Point that is orthogonal to both p and op. This is similar to
+// p.Cross(op) (the true cross product) except that it does a better job of
+// ensuring orthogonality when the Point is nearly parallel to op, it returns
+// a non-zero result even when p == op or p == -op and the result is a Point,
+// so it will have norm 1.
+//
+// It satisfies the following properties (f == PointCross):
+//
+// (1) f(p, op) != 0 for all p, op
+// (2) f(op,p) == -f(p,op) unless p == op or p == -op
+// (3) f(-p,op) == -f(p,op) unless p == op or p == -op
+// (4) f(p,-op) == -f(p,op) unless p == op or p == -op
+func (p Point) PointCross(op Point) Point {
+ // NOTE(dnadasi): In the C++ API the equivalent method here was known as "RobustCrossProd",
+ // but PointCross more accurately describes how this method is used.
+ x := p.Add(op.Vector).Cross(op.Sub(p.Vector))
+
+ if x.ApproxEqual(r3.Vector{0, 0, 0}) {
+ // The only result that makes sense mathematically is to return zero, but
+ // we find it more convenient to return an arbitrary orthogonal vector.
+ return Point{p.Ortho()}
+ }
+
+ return Point{x.Normalize()}
+}
+
+// OrderedCCW returns true if the edges OA, OB, and OC are encountered in that
+// order while sweeping CCW around the point O.
+//
+// You can think of this as testing whether A <= B <= C with respect to the
+// CCW ordering around O that starts at A, or equivalently, whether B is
+// contained in the range of angles (inclusive) that starts at A and extends
+// CCW to C. Properties:
+//
+// (1) If OrderedCCW(a,b,c,o) && OrderedCCW(b,a,c,o), then a == b
+// (2) If OrderedCCW(a,b,c,o) && OrderedCCW(a,c,b,o), then b == c
+// (3) If OrderedCCW(a,b,c,o) && OrderedCCW(c,b,a,o), then a == b == c
+// (4) If a == b or b == c, then OrderedCCW(a,b,c,o) is true
+// (5) Otherwise if a == c, then OrderedCCW(a,b,c,o) is false
+func OrderedCCW(a, b, c, o Point) bool {
+ sum := 0
+ if RobustSign(b, o, a) != Clockwise {
+ sum++
+ }
+ if RobustSign(c, o, b) != Clockwise {
+ sum++
+ }
+ if RobustSign(a, o, c) == CounterClockwise {
+ sum++
+ }
+ return sum >= 2
+}
+
+// Distance returns the angle between two points.
+func (p Point) Distance(b Point) s1.Angle {
+ return p.Vector.Angle(b.Vector)
+}
+
+// ApproxEqual reports whether the two points are similar enough to be equal.
+func (p Point) ApproxEqual(other Point) bool {
+ return p.Vector.Angle(other.Vector) <= s1.Angle(epsilon)
+}
+
+// PointArea returns the area on the unit sphere for the triangle defined by the
+// given points.
+//
+// This method is based on l'Huilier's theorem,
+//
+// tan(E/4) = sqrt(tan(s/2) tan((s-a)/2) tan((s-b)/2) tan((s-c)/2))
+//
+// where E is the spherical excess of the triangle (i.e. its area),
+// a, b, c are the side lengths, and
+// s is the semiperimeter (a + b + c) / 2.
+//
+// The only significant source of error using l'Huilier's method is the
+// cancellation error of the terms (s-a), (s-b), (s-c). This leads to a
+// *relative* error of about 1e-16 * s / min(s-a, s-b, s-c). This compares
+// to a relative error of about 1e-15 / E using Girard's formula, where E is
+// the true area of the triangle. Girard's formula can be even worse than
+// this for very small triangles, e.g. a triangle with a true area of 1e-30
+// might evaluate to 1e-5.
+//
+// So, we prefer l'Huilier's formula unless dmin < s * (0.1 * E), where
+// dmin = min(s-a, s-b, s-c). This basically includes all triangles
+// except for extremely long and skinny ones.
+//
+// Since we don't know E, we would like a conservative upper bound on
+// the triangle area in terms of s and dmin. It's possible to show that
+// E <= k1 * s * sqrt(s * dmin), where k1 = 2*sqrt(3)/Pi (about 1).
+// Using this, it's easy to show that we should always use l'Huilier's
+// method if dmin >= k2 * s^5, where k2 is about 1e-2. Furthermore,
+// if dmin < k2 * s^5, the triangle area is at most k3 * s^4, where
+// k3 is about 0.1. Since the best case error using Girard's formula
+// is about 1e-15, this means that we shouldn't even consider it unless
+// s >= 3e-4 or so.
+func PointArea(a, b, c Point) float64 {
+ sa := float64(b.Angle(c.Vector))
+ sb := float64(c.Angle(a.Vector))
+ sc := float64(a.Angle(b.Vector))
+ s := 0.5 * (sa + sb + sc)
+ if s >= 3e-4 {
+ // Consider whether Girard's formula might be more accurate.
+ dmin := s - math.Max(sa, math.Max(sb, sc))
+ if dmin < 1e-2*s*s*s*s*s {
+ // This triangle is skinny enough to use Girard's formula.
+ ab := a.PointCross(b)
+ bc := b.PointCross(c)
+ ac := a.PointCross(c)
+ area := math.Max(0.0, float64(ab.Angle(ac.Vector)-ab.Angle(bc.Vector)+bc.Angle(ac.Vector)))
+
+ if dmin < s*0.1*area {
+ return area
+ }
+ }
+ }
+
+ // Use l'Huilier's formula.
+ return 4 * math.Atan(math.Sqrt(math.Max(0.0, math.Tan(0.5*s)*math.Tan(0.5*(s-sa))*
+ math.Tan(0.5*(s-sb))*math.Tan(0.5*(s-sc)))))
+}
+
+// TrueCentroid returns the true centroid of the spherical triangle ABC multiplied by the
+// signed area of spherical triangle ABC. The result is not normalized.
+// The reasons for multiplying by the signed area are (1) this is the quantity
+// that needs to be summed to compute the centroid of a union or difference of triangles,
+// and (2) it's actually easier to calculate this way. All points must have unit length.
+//
+// The true centroid (mass centroid) is defined as the surface integral
+// over the spherical triangle of (x,y,z) divided by the triangle area.
+// This is the point that the triangle would rotate around if it was
+// spinning in empty space.
+//
+// The best centroid for most purposes is the true centroid. Unlike the
+// planar and surface centroids, the true centroid behaves linearly as
+// regions are added or subtracted. That is, if you split a triangle into
+// pieces and compute the average of their centroids (weighted by triangle
+// area), the result equals the centroid of the original triangle. This is
+// not true of the other centroids.
+func TrueCentroid(a, b, c Point) Point {
+ ra := float64(1)
+ if sa := float64(b.Distance(c)); sa != 0 {
+ ra = sa / math.Sin(sa)
+ }
+ rb := float64(1)
+ if sb := float64(c.Distance(a)); sb != 0 {
+ rb = sb / math.Sin(sb)
+ }
+ rc := float64(1)
+ if sc := float64(a.Distance(b)); sc != 0 {
+ rc = sc / math.Sin(sc)
+ }
+
+ // Now compute a point M such that:
+ //
+ // [Ax Ay Az] [Mx] [ra]
+ // [Bx By Bz] [My] = 0.5 * det(A,B,C) * [rb]
+ // [Cx Cy Cz] [Mz] [rc]
+ //
+ // To improve the numerical stability we subtract the first row (A) from the
+ // other two rows; this reduces the cancellation error when A, B, and C are
+ // very close together. Then we solve it using Cramer's rule.
+ //
+ // This code still isn't as numerically stable as it could be.
+ // The biggest potential improvement is to compute B-A and C-A more
+ // accurately so that (B-A)x(C-A) is always inside triangle ABC.
+ x := r3.Vector{a.X, b.X - a.X, c.X - a.X}
+ y := r3.Vector{a.Y, b.Y - a.Y, c.Y - a.Y}
+ z := r3.Vector{a.Z, b.Z - a.Z, c.Z - a.Z}
+ r := r3.Vector{ra, rb - ra, rc - ra}
+
+ return Point{r3.Vector{y.Cross(z).Dot(r), z.Cross(x).Dot(r), x.Cross(y).Dot(r)}.Mul(0.5)}
+}
+
+// PlanarCentroid returns the centroid of the planar triangle ABC, which is not normalized.
+// It can be normalized to unit length to obtain the "surface centroid" of the corresponding
+// spherical triangle, i.e. the intersection of the three medians. However,
+// note that for large spherical triangles the surface centroid may be
+// nowhere near the intuitive "center" (see example in TrueCentroid comments).
+//
+// Note that the surface centroid may be nowhere near the intuitive
+// "center" of a spherical triangle. For example, consider the triangle
+// with vertices A=(1,eps,0), B=(0,0,1), C=(-1,eps,0) (a quarter-sphere).
+// The surface centroid of this triangle is at S=(0, 2*eps, 1), which is
+// within a distance of 2*eps of the vertex B. Note that the median from A
+// (the segment connecting A to the midpoint of BC) passes through S, since
+// this is the shortest path connecting the two endpoints. On the other
+// hand, the true centroid is at M=(0, 0.5, 0.5), which when projected onto
+// the surface is a much more reasonable interpretation of the "center" of
+// this triangle.
+func PlanarCentroid(a, b, c Point) Point {
+ return Point{a.Add(b.Vector).Add(c.Vector).Mul(1. / 3)}
+}
+
+// ChordAngleBetweenPoints constructs a ChordAngle corresponding to the distance
+// between the two given points. The points must be unit length.
+func ChordAngleBetweenPoints(x, y Point) s1.ChordAngle {
+ return s1.ChordAngle(math.Min(4.0, x.Sub(y.Vector).Norm2()))
+}
+
+// regularPoints generates a slice of points shaped as a regular polygon with
+// the numVertices vertices, all located on a circle of the specified angular radius
+// around the center. The radius is the actual distance from center to each vertex.
+func regularPoints(center Point, radius s1.Angle, numVertices int) []Point {
+ return regularPointsForFrame(getFrame(center), radius, numVertices)
+}
+
+// regularPointsForFrame generates a slice of points shaped as a regular polygon
+// with numVertices vertices, all on a circle of the specified angular radius around
+// the center. The radius is the actual distance from the center to each vertex.
+func regularPointsForFrame(frame matrix3x3, radius s1.Angle, numVertices int) []Point {
+ // We construct the loop in the given frame coordinates, with the center at
+ // (0, 0, 1). For a loop of radius r, the loop vertices have the form
+ // (x, y, z) where x^2 + y^2 = sin(r) and z = cos(r). The distance on the
+ // sphere (arc length) from each vertex to the center is acos(cos(r)) = r.
+ z := math.Cos(radius.Radians())
+ r := math.Sin(radius.Radians())
+ radianStep := 2 * math.Pi / float64(numVertices)
+ var vertices []Point
+
+ for i := 0; i < numVertices; i++ {
+ angle := float64(i) * radianStep
+ p := PointFromCoords(r*math.Cos(angle), r*math.Sin(angle), z)
+ vertices = append(vertices, Point{fromFrame(frame, p).Normalize()})
+ }
+
+ return vertices
+}
+
+// TODO: Differences from C++
+// Rotate
+// Angle
+// TurnAngle
+// SignedArea
diff --git a/vendor/github.com/golang/geo/s2/polygon.go b/vendor/github.com/golang/geo/s2/polygon.go
new file mode 100644
index 0000000..352cc23
--- /dev/null
+++ b/vendor/github.com/golang/geo/s2/polygon.go
@@ -0,0 +1,211 @@
+/*
+Copyright 2015 Google Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package s2
+
+// Polygon represents a sequence of zero or more loops; recall that the
+// interior of a loop is defined to be its left-hand side (see Loop).
+//
+// When the polygon is initialized, the given loops are automatically converted
+// into a canonical form consisting of "shells" and "holes". Shells and holes
+// are both oriented CCW, and are nested hierarchically. The loops are
+// reordered to correspond to a preorder traversal of the nesting hierarchy.
+//
+// Polygons may represent any region of the sphere with a polygonal boundary,
+// including the entire sphere (known as the "full" polygon). The full polygon
+// consists of a single full loop (see Loop), whereas the empty polygon has no
+// loops at all.
+//
+// Use FullPolygon() to construct a full polygon. The zero value of Polygon is
+// treated as the empty polygon.
+//
+// Polygons have the following restrictions:
+//
+// - Loops may not cross, i.e. the boundary of a loop may not intersect
+// both the interior and exterior of any other loop.
+//
+// - Loops may not share edges, i.e. if a loop contains an edge AB, then
+// no other loop may contain AB or BA.
+//
+// - Loops may share vertices, however no vertex may appear twice in a
+// single loop (see Loop).
+//
+// - No loop may be empty. The full loop may appear only in the full polygon.
+type Polygon struct {
+ loops []*Loop
+
+ // loopDepths keeps track of how deep a given loop is in this polygon.
+ // The depths tracked in this slice are kept in 1:1 lockstep with the
+ // elements in the loops list.
+ // Holes inside a polygon are stored as odd numbers, and shells are even.
+ loopDepths []int
+
+ // index is a spatial index of all the polygon loops.
+ index ShapeIndex
+
+ // hasHoles tracks if this polygon has at least one hole.
+ hasHoles bool
+
+ // numVertices keeps the running total of all of the vertices of the contained loops.
+ numVertices int
+
+ // bound is a conservative bound on all points contained by this loop.
+ // If l.ContainsPoint(P), then l.bound.ContainsPoint(P).
+ bound Rect
+
+ // Since bound is not exact, it is possible that a loop A contains
+ // another loop B whose bounds are slightly larger. subregionBound
+ // has been expanded sufficiently to account for this error, i.e.
+ // if A.Contains(B), then A.subregionBound.Contains(B.bound).
+ subregionBound Rect
+}
+
+// PolygonFromLoops constructs a polygon from the given hierarchically nested
+// loops. The polygon interior consists of the points contained by an odd
+// number of loops. (Recall that a loop contains the set of points on its
+// left-hand side.)
+//
+// This method figures out the loop nesting hierarchy and assigns every loop a
+// depth. Shells have even depths, and holes have odd depths.
+//
+// NOTE: this function is NOT YET IMPLEMENTED for more than one loop and will
+// panic if given a slice of length > 1.
+func PolygonFromLoops(loops []*Loop) *Polygon {
+ if len(loops) > 1 {
+ panic("s2.PolygonFromLoops for multiple loops is not yet implemented")
+ }
+ return &Polygon{
+ loops: loops,
+ // TODO(roberts): This is explicitly set as depth of 0 for the one loop in
+ // the polygon. When multiple loops are supported, fix this to set the depths.
+ loopDepths: []int{0},
+ numVertices: len(loops[0].Vertices()), // TODO(roberts): Once multi-loop is supported, fix this.
+ bound: EmptyRect(),
+ subregionBound: EmptyRect(),
+ }
+}
+
+// FullPolygon returns a special "full" polygon.
+func FullPolygon() *Polygon {
+ return &Polygon{
+ loops: []*Loop{
+ FullLoop(),
+ },
+ loopDepths: []int{0},
+ numVertices: len(FullLoop().Vertices()),
+ bound: FullRect(),
+ subregionBound: FullRect(),
+ }
+}
+
+// IsEmpty reports whether this is the special "empty" polygon (consisting of no loops).
+func (p *Polygon) IsEmpty() bool {
+ return len(p.loops) == 0
+}
+
+// IsFull reports whether this is the special "full" polygon (consisting of a
+// single loop that encompasses the entire sphere).
+func (p *Polygon) IsFull() bool {
+ return len(p.loops) == 1 && p.loops[0].IsFull()
+}
+
+// NumLoops returns the number of loops in this polygon.
+func (p *Polygon) NumLoops() int {
+ return len(p.loops)
+}
+
+// Loops returns the loops in this polygon.
+func (p *Polygon) Loops() []*Loop {
+ return p.loops
+}
+
+// Loop returns the loop at the given index. Note that during initialization,
+// the given loops are reordered according to a preorder traversal of the loop
+// nesting hierarchy. This implies that every loop is immediately followed by
+// its descendants. This hierarchy can be traversed using the methods Parent,
+// LastDescendant, and Loop.depth.
+func (p *Polygon) Loop(k int) *Loop {
+ return p.loops[k]
+}
+
+// Parent returns the index of the parent of loop k.
+// If the loop does not have a parent, ok=false is returned.
+func (p *Polygon) Parent(k int) (index int, ok bool) {
+ // See where we are on the depth heirarchy.
+ depth := p.loopDepths[k]
+ if depth == 0 {
+ return -1, false
+ }
+
+ // There may be several loops at the same nesting level as us that share a
+ // parent loop with us. (Imagine a slice of swiss cheese, of which we are one loop.
+ // we don't know how many may be next to us before we get back to our parent loop.)
+ // Move up one position from us, and then begin traversing back through the set of loops
+ // until we find the one that is our parent or we get to the top of the polygon.
+ for k--; k >= 0 && p.loopDepths[k] <= depth; k-- {
+ }
+ return k, true
+}
+
+// LastDescendant returns the index of the last loop that is contained within loop k.
+// If k is negative, it returns the last loop in the polygon.
+// Note that loops are indexed according to a preorder traversal of the nesting
+// hierarchy, so the immediate children of loop k can be found by iterating over
+// the loops (k+1)..LastDescendant(k) and selecting those whose depth is equal
+// to Loop(k).depth+1.
+func (p *Polygon) LastDescendant(k int) int {
+ if k < 0 {
+ return len(p.loops) - 1
+ }
+
+ depth := p.loopDepths[k]
+
+ // Find the next loop immediately past us in the set of loops, and then start
+ // moving down the list until we either get to the end or find the next loop
+ // that is higher up the heirarchy than we are.
+ for k++; k < len(p.loops) && p.loopDepths[k] > depth; k++ {
+ }
+ return k - 1
+}
+
+// loopIsHole reports whether the given loop represents a hole in this polygon.
+func (p *Polygon) loopIsHole(k int) bool {
+ return p.loopDepths[k]&1 != 0
+}
+
+// loopSign returns -1 if this loop represents a hole in this polygon.
+// Otherwise, it returns +1. This is used when computing the area of a polygon.
+// (holes are subtracted from the total area).
+func (p *Polygon) loopSign(k int) int {
+ if p.loopIsHole(k) {
+ return -1
+ }
+ return 1
+}
+
+// CapBound returns a bounding spherical cap.
+func (p *Polygon) CapBound() Cap { return p.bound.CapBound() }
+
+// RectBound returns a bounding latitude-longitude rectangle.
+func (p *Polygon) RectBound() Rect { return p.bound }
+
+// ContainsCell reports whether the polygon contains the given cell.
+// TODO(roberts)
+//func (p *Polygon) ContainsCell(c Cell) bool { ... }
+
+// IntersectsCell reports whether the polygon intersects the given cell.
+// TODO(roberts)
+//func (p *Polygon) IntersectsCell(c Cell) bool { ... }
diff --git a/vendor/github.com/golang/geo/s2/polyline.go b/vendor/github.com/golang/geo/s2/polyline.go
new file mode 100644
index 0000000..6535ede
--- /dev/null
+++ b/vendor/github.com/golang/geo/s2/polyline.go
@@ -0,0 +1,177 @@
+/*
+Copyright 2016 Google Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package s2
+
+import (
+ "math"
+
+ "github.com/golang/geo/s1"
+)
+
+// Polyline represents a sequence of zero or more vertices connected by
+// straight edges (geodesics). Edges of length 0 and 180 degrees are not
+// allowed, i.e. adjacent vertices should not be identical or antipodal.
+type Polyline []Point
+
+// PolylineFromLatLngs creates a new Polyline from the given LatLngs.
+func PolylineFromLatLngs(points []LatLng) Polyline {
+ p := make(Polyline, len(points))
+ for k, v := range points {
+ p[k] = PointFromLatLng(v)
+ }
+ return p
+}
+
+// Reverse reverses the order of the Polyline vertices.
+func (p Polyline) Reverse() {
+ for i := 0; i < len(p)/2; i++ {
+ p[i], p[len(p)-i-1] = p[len(p)-i-1], p[i]
+ }
+}
+
+// Length returns the length of this Polyline.
+func (p Polyline) Length() s1.Angle {
+ var length s1.Angle
+
+ for i := 1; i < len(p); i++ {
+ length += p[i-1].Distance(p[i])
+ }
+ return length
+}
+
+// Centroid returns the true centroid of the polyline multiplied by the length of the
+// polyline. The result is not unit length, so you may wish to normalize it.
+//
+// Scaling by the Polyline length makes it easy to compute the centroid
+// of several Polylines (by simply adding up their centroids).
+func (p Polyline) Centroid() Point {
+ var centroid Point
+ for i := 1; i < len(p); i++ {
+ // The centroid (multiplied by length) is a vector toward the midpoint
+ // of the edge, whose length is twice the sin of half the angle between
+ // the two vertices. Defining theta to be this angle, we have:
+ vSum := p[i-1].Add(p[i].Vector) // Length == 2*cos(theta)
+ vDiff := p[i-1].Sub(p[i].Vector) // Length == 2*sin(theta)
+
+ // Length == 2*sin(theta)
+ centroid = Point{centroid.Add(vSum.Mul(math.Sqrt(vDiff.Norm2() / vSum.Norm2())))}
+ }
+ return centroid
+}
+
+// Equals reports whether the given Polyline is exactly the same as this one.
+func (p Polyline) Equals(b Polyline) bool {
+ if len(p) != len(b) {
+ return false
+ }
+ for i, v := range p {
+ if v != b[i] {
+ return false
+ }
+ }
+
+ return true
+}
+
+// CapBound returns the bounding Cap for this Polyline.
+func (p Polyline) CapBound() Cap {
+ return p.RectBound().CapBound()
+}
+
+// RectBound returns the bounding Rect for this Polyline.
+func (p Polyline) RectBound() Rect {
+ rb := NewRectBounder()
+ for _, v := range p {
+ rb.AddPoint(v)
+ }
+ return rb.RectBound()
+}
+
+// ContainsCell reports whether this Polyline contains the given Cell. Always returns false
+// because "containment" is not numerically well-defined except at the Polyline vertices.
+func (p Polyline) ContainsCell(cell Cell) bool {
+ return false
+}
+
+// IntersectsCell reports whether this Polyline intersects the given Cell.
+func (p Polyline) IntersectsCell(cell Cell) bool {
+ if len(p) == 0 {
+ return false
+ }
+
+ // We only need to check whether the cell contains vertex 0 for correctness,
+ // but these tests are cheap compared to edge crossings so we might as well
+ // check all the vertices.
+ for _, v := range p {
+ if cell.ContainsPoint(v) {
+ return true
+ }
+ }
+
+ cellVertices := []Point{
+ cell.Vertex(0),
+ cell.Vertex(1),
+ cell.Vertex(2),
+ cell.Vertex(3),
+ }
+
+ for j := 0; j < 4; j++ {
+ crosser := NewChainEdgeCrosser(cellVertices[j], cellVertices[(j+1)&3], p[0])
+ for i := 1; i < len(p); i++ {
+ if crosser.ChainCrossingSign(p[i]) != DoNotCross {
+ // There is a proper crossing, or two vertices were the same.
+ return true
+ }
+ }
+ }
+ return false
+}
+
+// NumEdges returns the number of edges in this shape.
+func (p Polyline) NumEdges() int {
+ if len(p) == 0 {
+ return 0
+ }
+ return len(p) - 1
+}
+
+// Edge returns endpoints for the given edge index.
+func (p Polyline) Edge(i int) (a, b Point) {
+ return p[i], p[i+1]
+}
+
+// HasInterior returns false as Polylines are not closed.
+func (p Polyline) HasInterior() bool {
+ return false
+}
+
+// ContainsOrigin returns false because there is no interior to contain s2.Origin.
+func (p Polyline) ContainsOrigin() bool {
+ return false
+}
+
+// TODO(roberts): Differences from C++.
+// IsValid
+// Suffix
+// Interpolate/UnInterpolate
+// Project
+// IsPointOnRight
+// Intersects
+// Reverse
+// SubsampleVertices
+// ApproxEqual
+// NearlyCoversPolyline
diff --git a/vendor/github.com/golang/geo/s2/predicates.go b/vendor/github.com/golang/geo/s2/predicates.go
new file mode 100644
index 0000000..700604e
--- /dev/null
+++ b/vendor/github.com/golang/geo/s2/predicates.go
@@ -0,0 +1,238 @@
+/*
+Copyright 2016 Google Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package s2
+
+// This file contains various predicates that are guaranteed to produce
+// correct, consistent results. They are also relatively efficient. This is
+// achieved by computing conservative error bounds and falling back to high
+// precision or even exact arithmetic when the result is uncertain. Such
+// predicates are useful in implementing robust algorithms.
+//
+// See also EdgeCrosser, which implements various exact
+// edge-crossing predicates more efficiently than can be done here.
+
+import (
+ "math"
+
+ "github.com/golang/geo/r3"
+)
+
+const (
+ // epsilon is a small number that represents a reasonable level of noise between two
+ // values that can be considered to be equal.
+ epsilon = 1e-15
+ // dblEpsilon is a smaller number for values that require more precision.
+ dblEpsilon = 2.220446049250313e-16
+
+ // maxDeterminantError is the maximum error in computing (AxB).C where all vectors
+ // are unit length. Using standard inequalities, it can be shown that
+ //
+ // fl(AxB) = AxB + D where |D| <= (|AxB| + (2/sqrt(3))*|A|*|B|) * e
+ //
+ // where "fl()" denotes a calculation done in floating-point arithmetic,
+ // |x| denotes either absolute value or the L2-norm as appropriate, and
+ // e is a reasonably small value near the noise level of floating point
+ // number accuracy. Similarly,
+ //
+ // fl(B.C) = B.C + d where |d| <= (|B.C| + 2*|B|*|C|) * e .
+ //
+ // Applying these bounds to the unit-length vectors A,B,C and neglecting
+ // relative error (which does not affect the sign of the result), we get
+ //
+ // fl((AxB).C) = (AxB).C + d where |d| <= (3 + 2/sqrt(3)) * e
+ maxDeterminantError = 1.8274 * dblEpsilon
+
+ // detErrorMultiplier is the factor to scale the magnitudes by when checking
+ // for the sign of set of points with certainty. Using a similar technique to
+ // the one used for maxDeterminantError, the error is at most:
+ //
+ // |d| <= (3 + 6/sqrt(3)) * |A-C| * |B-C| * e
+ //
+ // If the determinant magnitude is larger than this value then we know
+ // its sign with certainty.
+ detErrorMultiplier = 3.2321 * dblEpsilon
+)
+
+// Direction is an indication of the ordering of a set of points.
+type Direction int
+
+// These are the three options for the direction of a set of points.
+const (
+ Clockwise Direction = -1
+ Indeterminate = 0
+ CounterClockwise = 1
+)
+
+// Sign returns true if the points A, B, C are strictly counterclockwise,
+// and returns false if the points are clockwise or collinear (i.e. if they are all
+// contained on some great circle).
+//
+// Due to numerical errors, situations may arise that are mathematically
+// impossible, e.g. ABC may be considered strictly CCW while BCA is not.
+// However, the implementation guarantees the following:
+//
+// If Sign(a,b,c), then !Sign(c,b,a) for all a,b,c.
+func Sign(a, b, c Point) bool {
+ // NOTE(dnadasi): In the C++ API the equivalent method here was known as "SimpleSign".
+
+ // We compute the signed volume of the parallelepiped ABC. The usual
+ // formula for this is (A ⨯ B) · C, but we compute it here using (C ⨯ A) · B
+ // in order to ensure that ABC and CBA are not both CCW. This follows
+ // from the following identities (which are true numerically, not just
+ // mathematically):
+ //
+ // (1) x ⨯ y == -(y ⨯ x)
+ // (2) -x · y == -(x · y)
+ return c.Cross(a.Vector).Dot(b.Vector) > 0
+}
+
+// RobustSign returns a Direction representing the ordering of the points.
+// CounterClockwise is returned if the points are in counter-clockwise order,
+// Clockwise for clockwise, and Indeterminate if any two points are the same (collinear),
+// or the sign could not completely be determined.
+//
+// This function has additional logic to make sure that the above properties hold even
+// when the three points are coplanar, and to deal with the limitations of
+// floating-point arithmetic.
+//
+// RobustSign satisfies the following conditions:
+//
+// (1) RobustSign(a,b,c) == Indeterminate if and only if a == b, b == c, or c == a
+// (2) RobustSign(b,c,a) == RobustSign(a,b,c) for all a,b,c
+// (3) RobustSign(c,b,a) == -RobustSign(a,b,c) for all a,b,c
+//
+// In other words:
+//
+// (1) The result is Indeterminate if and only if two points are the same.
+// (2) Rotating the order of the arguments does not affect the result.
+// (3) Exchanging any two arguments inverts the result.
+//
+// On the other hand, note that it is not true in general that
+// RobustSign(-a,b,c) == -RobustSign(a,b,c), or any similar identities
+// involving antipodal points.
+func RobustSign(a, b, c Point) Direction {
+ sign := triageSign(a, b, c)
+ if sign == Indeterminate {
+ sign = expensiveSign(a, b, c)
+ }
+ return sign
+}
+
+// stableSign reports the direction sign of the points in a numerically stable way.
+// Unlike triageSign, this method can usually compute the correct determinant sign
+// even when all three points are as collinear as possible. For example if three
+// points are spaced 1km apart along a random line on the Earth's surface using
+// the nearest representable points, there is only a 0.4% chance that this method
+// will not be able to find the determinant sign. The probability of failure
+// decreases as the points get closer together; if the collinear points are 1 meter
+// apart, the failure rate drops to 0.0004%.
+//
+// This method could be extended to also handle nearly-antipodal points, but antipodal
+// points are rare in practice so it seems better to simply fall back to
+// exact arithmetic in that case.
+func stableSign(a, b, c Point) Direction {
+ ab := b.Sub(a.Vector)
+ ab2 := ab.Norm2()
+ bc := c.Sub(b.Vector)
+ bc2 := bc.Norm2()
+ ca := a.Sub(c.Vector)
+ ca2 := ca.Norm2()
+
+ // Now compute the determinant ((A-C)x(B-C)).C, where the vertices have been
+ // cyclically permuted if necessary so that AB is the longest edge. (This
+ // minimizes the magnitude of cross product.) At the same time we also
+ // compute the maximum error in the determinant.
+
+ // The two shortest edges, pointing away from their common point.
+ var e1, e2, op r3.Vector
+ if ab2 >= bc2 && ab2 >= ca2 {
+ // AB is the longest edge.
+ e1, e2, op = ca, bc, c.Vector
+ } else if bc2 >= ca2 {
+ // BC is the longest edge.
+ e1, e2, op = ab, ca, a.Vector
+ } else {
+ // CA is the longest edge.
+ e1, e2, op = bc, ab, b.Vector
+ }
+
+ det := -e1.Cross(e2).Dot(op)
+ maxErr := detErrorMultiplier * math.Sqrt(e1.Norm2()*e2.Norm2())
+
+ // If the determinant isn't zero, within maxErr, we know definitively the point ordering.
+ if det > maxErr {
+ return CounterClockwise
+ }
+ if det < -maxErr {
+ return Clockwise
+ }
+ return Indeterminate
+}
+
+// triageSign returns the direction sign of the points. It returns Indeterminate if two
+// points are identical or the result is uncertain. Uncertain cases can be resolved, if
+// desired, by calling expensiveSign.
+//
+// The purpose of this method is to allow additional cheap tests to be done without
+// calling expensiveSign.
+func triageSign(a, b, c Point) Direction {
+ det := a.Cross(b.Vector).Dot(c.Vector)
+ if det > maxDeterminantError {
+ return CounterClockwise
+ }
+ if det < -maxDeterminantError {
+ return Clockwise
+ }
+ return Indeterminate
+}
+
+// expensiveSign reports the direction sign of the points. It returns Indeterminate
+// if two of the input points are the same. It uses multiple-precision arithmetic
+// to ensure that its results are always self-consistent.
+func expensiveSign(a, b, c Point) Direction {
+ // Return Indeterminate if and only if two points are the same.
+ // This ensures RobustSign(a,b,c) == Indeterminate if and only if a == b, b == c, or c == a.
+ // ie. Property 1 of RobustSign.
+ if a == b || b == c || c == a {
+ return Indeterminate
+ }
+
+ // Next we try recomputing the determinant still using floating-point
+ // arithmetic but in a more precise way. This is more expensive than the
+ // simple calculation done by triageSign, but it is still *much* cheaper
+ // than using arbitrary-precision arithmetic. This optimization is able to
+ // compute the correct determinant sign in virtually all cases except when
+ // the three points are truly collinear (e.g., three points on the equator).
+ detSign := stableSign(a, b, c)
+ if detSign != Indeterminate {
+ return detSign
+ }
+
+ // Otherwise fall back to exact arithmetic and symbolic permutations.
+ return exactSign(a, b, c, false)
+}
+
+// exactSign reports the direction sign of the points using exact precision arithmetic.
+func exactSign(a, b, c Point, perturb bool) Direction {
+ // In the C++ version, the final computation is performed using OpenSSL's
+ // Bignum exact precision math library. The existence of an equivalent
+ // library in Go is indeterminate. In C++, using the exact precision library
+ // to solve this stage is ~300x slower than the above checks.
+ // TODO(roberts): Select and incorporate an appropriate Go exact precision
+ // floating point library for the remaining calculations.
+ return Indeterminate
+}
diff --git a/vendor/github.com/golang/geo/s2/rect.go b/vendor/github.com/golang/geo/s2/rect.go
new file mode 100644
index 0000000..134dc7e
--- /dev/null
+++ b/vendor/github.com/golang/geo/s2/rect.go
@@ -0,0 +1,426 @@
+/*
+Copyright 2014 Google Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package s2
+
+import (
+ "fmt"
+ "math"
+
+ "github.com/golang/geo/r1"
+ "github.com/golang/geo/s1"
+)
+
+// Rect represents a closed latitude-longitude rectangle.
+type Rect struct {
+ Lat r1.Interval
+ Lng s1.Interval
+}
+
+var (
+ validRectLatRange = r1.Interval{-math.Pi / 2, math.Pi / 2}
+ validRectLngRange = s1.FullInterval()
+)
+
+// EmptyRect returns the empty rectangle.
+func EmptyRect() Rect { return Rect{r1.EmptyInterval(), s1.EmptyInterval()} }
+
+// FullRect returns the full rectangle.
+func FullRect() Rect { return Rect{validRectLatRange, validRectLngRange} }
+
+// RectFromLatLng constructs a rectangle containing a single point p.
+func RectFromLatLng(p LatLng) Rect {
+ return Rect{
+ Lat: r1.Interval{p.Lat.Radians(), p.Lat.Radians()},
+ Lng: s1.Interval{p.Lng.Radians(), p.Lng.Radians()},
+ }
+}
+
+// RectFromCenterSize constructs a rectangle with the given size and center.
+// center needs to be normalized, but size does not. The latitude
+// interval of the result is clamped to [-90,90] degrees, and the longitude
+// interval of the result is FullRect() if and only if the longitude size is
+// 360 degrees or more.
+//
+// Examples of clamping (in degrees):
+// center=(80,170), size=(40,60) -> lat=[60,90], lng=[140,-160]
+// center=(10,40), size=(210,400) -> lat=[-90,90], lng=[-180,180]
+// center=(-90,180), size=(20,50) -> lat=[-90,-80], lng=[155,-155]
+func RectFromCenterSize(center, size LatLng) Rect {
+ half := LatLng{size.Lat / 2, size.Lng / 2}
+ return RectFromLatLng(center).expanded(half)
+}
+
+// IsValid returns true iff the rectangle is valid.
+// This requires Lat ⊆ [-π/2,π/2] and Lng ⊆ [-π,π], and Lat = ∅ iff Lng = ∅
+func (r Rect) IsValid() bool {
+ return math.Abs(r.Lat.Lo) <= math.Pi/2 &&
+ math.Abs(r.Lat.Hi) <= math.Pi/2 &&
+ r.Lng.IsValid() &&
+ r.Lat.IsEmpty() == r.Lng.IsEmpty()
+}
+
+// IsEmpty reports whether the rectangle is empty.
+func (r Rect) IsEmpty() bool { return r.Lat.IsEmpty() }
+
+// IsFull reports whether the rectangle is full.
+func (r Rect) IsFull() bool { return r.Lat.Equal(validRectLatRange) && r.Lng.IsFull() }
+
+// IsPoint reports whether the rectangle is a single point.
+func (r Rect) IsPoint() bool { return r.Lat.Lo == r.Lat.Hi && r.Lng.Lo == r.Lng.Hi }
+
+// Vertex returns the i-th vertex of the rectangle (i = 0,1,2,3) in CCW order
+// (lower left, lower right, upper right, upper left).
+func (r Rect) Vertex(i int) LatLng {
+ var lat, lng float64
+
+ switch i {
+ case 0:
+ lat = r.Lat.Lo
+ lng = r.Lng.Lo
+ case 1:
+ lat = r.Lat.Lo
+ lng = r.Lng.Hi
+ case 2:
+ lat = r.Lat.Hi
+ lng = r.Lng.Hi
+ case 3:
+ lat = r.Lat.Hi
+ lng = r.Lng.Lo
+ }
+ return LatLng{s1.Angle(lat) * s1.Radian, s1.Angle(lng) * s1.Radian}
+}
+
+// Lo returns one corner of the rectangle.
+func (r Rect) Lo() LatLng {
+ return LatLng{s1.Angle(r.Lat.Lo) * s1.Radian, s1.Angle(r.Lng.Lo) * s1.Radian}
+}
+
+// Hi returns the other corner of the rectangle.
+func (r Rect) Hi() LatLng {
+ return LatLng{s1.Angle(r.Lat.Hi) * s1.Radian, s1.Angle(r.Lng.Hi) * s1.Radian}
+}
+
+// Center returns the center of the rectangle.
+func (r Rect) Center() LatLng {
+ return LatLng{s1.Angle(r.Lat.Center()) * s1.Radian, s1.Angle(r.Lng.Center()) * s1.Radian}
+}
+
+// Size returns the size of the Rect.
+func (r Rect) Size() LatLng {
+ return LatLng{s1.Angle(r.Lat.Length()) * s1.Radian, s1.Angle(r.Lng.Length()) * s1.Radian}
+}
+
+// Area returns the surface area of the Rect.
+func (r Rect) Area() float64 {
+ if r.IsEmpty() {
+ return 0
+ }
+ capDiff := math.Abs(math.Sin(r.Lat.Hi) - math.Sin(r.Lat.Lo))
+ return r.Lng.Length() * capDiff
+}
+
+// AddPoint increases the size of the rectangle to include the given point.
+func (r Rect) AddPoint(ll LatLng) Rect {
+ if !ll.IsValid() {
+ return r
+ }
+ return Rect{
+ Lat: r.Lat.AddPoint(ll.Lat.Radians()),
+ Lng: r.Lng.AddPoint(ll.Lng.Radians()),
+ }
+}
+
+// expanded returns a rectangle that has been expanded by margin.Lat on each side
+// in the latitude direction, and by margin.Lng on each side in the longitude
+// direction. If either margin is negative, then it shrinks the rectangle on
+// the corresponding sides instead. The resulting rectangle may be empty.
+//
+// The latitude-longitude space has the topology of a cylinder. Longitudes
+// "wrap around" at +/-180 degrees, while latitudes are clamped to range [-90, 90].
+// This means that any expansion (positive or negative) of the full longitude range
+// remains full (since the "rectangle" is actually a continuous band around the
+// cylinder), while expansion of the full latitude range remains full only if the
+// margin is positive.
+//
+// If either the latitude or longitude interval becomes empty after
+// expansion by a negative margin, the result is empty.
+//
+// Note that if an expanded rectangle contains a pole, it may not contain
+// all possible lat/lng representations of that pole, e.g., both points [π/2,0]
+// and [π/2,1] represent the same pole, but they might not be contained by the
+// same Rect.
+//
+// If you are trying to grow a rectangle by a certain distance on the
+// sphere (e.g. 5km), refer to the ExpandedByDistance() C++ method implementation
+// instead.
+func (r Rect) expanded(margin LatLng) Rect {
+ lat := r.Lat.Expanded(margin.Lat.Radians())
+ lng := r.Lng.Expanded(margin.Lng.Radians())
+
+ if lat.IsEmpty() || lng.IsEmpty() {
+ return EmptyRect()
+ }
+
+ return Rect{
+ Lat: lat.Intersection(validRectLatRange),
+ Lng: lng,
+ }
+}
+
+func (r Rect) String() string { return fmt.Sprintf("[Lo%v, Hi%v]", r.Lo(), r.Hi()) }
+
+// PolarClosure returns the rectangle unmodified if it does not include either pole.
+// If it includes either pole, PolarClosure returns an expansion of the rectangle along
+// the longitudinal range to include all possible representations of the contained poles.
+func (r Rect) PolarClosure() Rect {
+ if r.Lat.Lo == -math.Pi/2 || r.Lat.Hi == math.Pi/2 {
+ return Rect{r.Lat, s1.FullInterval()}
+ }
+ return r
+}
+
+// Union returns the smallest Rect containing the union of this rectangle and the given rectangle.
+func (r Rect) Union(other Rect) Rect {
+ return Rect{
+ Lat: r.Lat.Union(other.Lat),
+ Lng: r.Lng.Union(other.Lng),
+ }
+}
+
+// Intersection returns the smallest rectangle containing the intersection of
+// this rectangle and the given rectangle. Note that the region of intersection
+// may consist of two disjoint rectangles, in which case a single rectangle
+// spanning both of them is returned.
+func (r Rect) Intersection(other Rect) Rect {
+ lat := r.Lat.Intersection(other.Lat)
+ lng := r.Lng.Intersection(other.Lng)
+
+ if lat.IsEmpty() || lng.IsEmpty() {
+ return EmptyRect()
+ }
+ return Rect{lat, lng}
+}
+
+// Intersects reports whether this rectangle and the other have any points in common.
+func (r Rect) Intersects(other Rect) bool {
+ return r.Lat.Intersects(other.Lat) && r.Lng.Intersects(other.Lng)
+}
+
+// CapBound returns a cap that countains Rect.
+func (r Rect) CapBound() Cap {
+ // We consider two possible bounding caps, one whose axis passes
+ // through the center of the lat-long rectangle and one whose axis
+ // is the north or south pole. We return the smaller of the two caps.
+
+ if r.IsEmpty() {
+ return EmptyCap()
+ }
+
+ var poleZ, poleAngle float64
+ if r.Lat.Hi+r.Lat.Lo < 0 {
+ // South pole axis yields smaller cap.
+ poleZ = -1
+ poleAngle = math.Pi/2 + r.Lat.Hi
+ } else {
+ poleZ = 1
+ poleAngle = math.Pi/2 - r.Lat.Lo
+ }
+ poleCap := CapFromCenterAngle(PointFromCoords(0, 0, poleZ), s1.Angle(poleAngle)*s1.Radian)
+
+ // For bounding rectangles that span 180 degrees or less in longitude, the
+ // maximum cap size is achieved at one of the rectangle vertices. For
+ // rectangles that are larger than 180 degrees, we punt and always return a
+ // bounding cap centered at one of the two poles.
+ if math.Remainder(r.Lng.Hi-r.Lng.Lo, 2*math.Pi) >= 0 && r.Lng.Hi-r.Lng.Lo < 2*math.Pi {
+ midCap := CapFromPoint(PointFromLatLng(r.Center())).AddPoint(PointFromLatLng(r.Lo())).AddPoint(PointFromLatLng(r.Hi()))
+ if midCap.Height() < poleCap.Height() {
+ return midCap
+ }
+ }
+ return poleCap
+}
+
+// RectBound returns itself.
+func (r Rect) RectBound() Rect {
+ return r
+}
+
+// Contains reports whether this Rect contains the other Rect.
+func (r Rect) Contains(other Rect) bool {
+ return r.Lat.ContainsInterval(other.Lat) && r.Lng.ContainsInterval(other.Lng)
+}
+
+// ContainsCell reports whether the given Cell is contained by this Rect.
+func (r Rect) ContainsCell(c Cell) bool {
+ // A latitude-longitude rectangle contains a cell if and only if it contains
+ // the cell's bounding rectangle. This test is exact from a mathematical
+ // point of view, assuming that the bounds returned by Cell.RectBound()
+ // are tight. However, note that there can be a loss of precision when
+ // converting between representations -- for example, if an s2.Cell is
+ // converted to a polygon, the polygon's bounding rectangle may not contain
+ // the cell's bounding rectangle. This has some slightly unexpected side
+ // effects; for instance, if one creates an s2.Polygon from an s2.Cell, the
+ // polygon will contain the cell, but the polygon's bounding box will not.
+ return r.Contains(c.RectBound())
+}
+
+// ContainsLatLng reports whether the given LatLng is within the Rect.
+func (r Rect) ContainsLatLng(ll LatLng) bool {
+ if !ll.IsValid() {
+ return false
+ }
+ return r.Lat.Contains(ll.Lat.Radians()) && r.Lng.Contains(ll.Lng.Radians())
+}
+
+// ContainsPoint reports whether the given Point is within the Rect.
+func (r Rect) ContainsPoint(p Point) bool {
+ return r.ContainsLatLng(LatLngFromPoint(p))
+}
+
+// intersectsLatEdge reports whether the edge AB intersects the given edge of constant
+// latitude. Requires the points to have unit length.
+func intersectsLatEdge(a, b Point, lat s1.Angle, lng s1.Interval) bool {
+ // Unfortunately, lines of constant latitude are curves on
+ // the sphere. They can intersect a straight edge in 0, 1, or 2 points.
+
+ // First, compute the normal to the plane AB that points vaguely north.
+ z := a.PointCross(b)
+ if z.Z < 0 {
+ z = Point{z.Mul(-1)}
+ }
+
+ // Extend this to an orthonormal frame (x,y,z) where x is the direction
+ // where the great circle through AB achieves its maximium latitude.
+ y := z.PointCross(PointFromCoords(0, 0, 1))
+ x := y.Cross(z.Vector)
+
+ // Compute the angle "theta" from the x-axis (in the x-y plane defined
+ // above) where the great circle intersects the given line of latitude.
+ sinLat := math.Sin(float64(lat))
+ if math.Abs(sinLat) >= x.Z {
+ // The great circle does not reach the given latitude.
+ return false
+ }
+
+ cosTheta := sinLat / x.Z
+ sinTheta := math.Sqrt(1 - cosTheta*cosTheta)
+ theta := math.Atan2(sinTheta, cosTheta)
+
+ // The candidate intersection points are located +/- theta in the x-y
+ // plane. For an intersection to be valid, we need to check that the
+ // intersection point is contained in the interior of the edge AB and
+ // also that it is contained within the given longitude interval "lng".
+
+ // Compute the range of theta values spanned by the edge AB.
+ abTheta := s1.IntervalFromPointPair(
+ math.Atan2(a.Dot(y.Vector), a.Dot(x)),
+ math.Atan2(b.Dot(y.Vector), b.Dot(x)))
+
+ if abTheta.Contains(theta) {
+ // Check if the intersection point is also in the given lng interval.
+ isect := x.Mul(cosTheta).Add(y.Mul(sinTheta))
+ if lng.Contains(math.Atan2(isect.Y, isect.X)) {
+ return true
+ }
+ }
+
+ if abTheta.Contains(-theta) {
+ // Check if the other intersection point is also in the given lng interval.
+ isect := x.Mul(cosTheta).Sub(y.Mul(sinTheta))
+ if lng.Contains(math.Atan2(isect.Y, isect.X)) {
+ return true
+ }
+ }
+ return false
+}
+
+// intersectsLngEdge reports whether the edge AB intersects the given edge of constant
+// longitude. Requires the points to have unit length.
+func intersectsLngEdge(a, b Point, lat r1.Interval, lng s1.Angle) bool {
+ // The nice thing about edges of constant longitude is that
+ // they are straight lines on the sphere (geodesics).
+ return SimpleCrossing(a, b, PointFromLatLng(LatLng{s1.Angle(lat.Lo), lng}),
+ PointFromLatLng(LatLng{s1.Angle(lat.Hi), lng}))
+}
+
+// IntersectsCell reports whether this rectangle intersects the given cell. This is an
+// exact test and may be fairly expensive.
+func (r Rect) IntersectsCell(c Cell) bool {
+ // First we eliminate the cases where one region completely contains the
+ // other. Once these are disposed of, then the regions will intersect
+ // if and only if their boundaries intersect.
+ if r.IsEmpty() {
+ return false
+ }
+ if r.ContainsPoint(Point{c.id.rawPoint()}) {
+ return true
+ }
+ if c.ContainsPoint(PointFromLatLng(r.Center())) {
+ return true
+ }
+
+ // Quick rejection test (not required for correctness).
+ if !r.Intersects(c.RectBound()) {
+ return false
+ }
+
+ // Precompute the cell vertices as points and latitude-longitudes. We also
+ // check whether the Cell contains any corner of the rectangle, or
+ // vice-versa, since the edge-crossing tests only check the edge interiors.
+ vertices := [4]Point{}
+ latlngs := [4]LatLng{}
+
+ for i := range vertices {
+ vertices[i] = c.Vertex(i)
+ latlngs[i] = LatLngFromPoint(vertices[i])
+ if r.ContainsLatLng(latlngs[i]) {
+ return true
+ }
+ if c.ContainsPoint(PointFromLatLng(r.Vertex(i))) {
+ return true
+ }
+ }
+
+ // Now check whether the boundaries intersect. Unfortunately, a
+ // latitude-longitude rectangle does not have straight edges: two edges
+ // are curved, and at least one of them is concave.
+ for i := range vertices {
+ edgeLng := s1.IntervalFromEndpoints(latlngs[i].Lng.Radians(), latlngs[(i+1)&3].Lng.Radians())
+ if !r.Lng.Intersects(edgeLng) {
+ continue
+ }
+
+ a := vertices[i]
+ b := vertices[(i+1)&3]
+ if edgeLng.Contains(r.Lng.Lo) && intersectsLngEdge(a, b, r.Lat, s1.Angle(r.Lng.Lo)) {
+ return true
+ }
+ if edgeLng.Contains(r.Lng.Hi) && intersectsLngEdge(a, b, r.Lat, s1.Angle(r.Lng.Hi)) {
+ return true
+ }
+ if intersectsLatEdge(a, b, s1.Angle(r.Lat.Lo), r.Lng) {
+ return true
+ }
+ if intersectsLatEdge(a, b, s1.Angle(r.Lat.Hi), r.Lng) {
+ return true
+ }
+ }
+ return false
+}
+
+// BUG: The major differences from the C++ version are:
+// - GetCentroid, Get*Distance, Vertex, InteriorContains(LatLng|Rect|Point)
diff --git a/vendor/github.com/golang/geo/s2/region.go b/vendor/github.com/golang/geo/s2/region.go
new file mode 100644
index 0000000..f1e127e
--- /dev/null
+++ b/vendor/github.com/golang/geo/s2/region.go
@@ -0,0 +1,50 @@
+/*
+Copyright 2014 Google Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package s2
+
+// A Region represents a two-dimensional region on the unit sphere.
+//
+// The purpose of this interface is to allow complex regions to be
+// approximated as simpler regions. The interface is restricted to methods
+// that are useful for computing approximations.
+type Region interface {
+ // CapBound returns a bounding spherical cap. This is not guaranteed to be exact.
+ CapBound() Cap
+
+ // RectBound returns a bounding latitude-longitude rectangle that contains
+ // the region. The bounds are not guaranteed to be tight.
+ RectBound() Rect
+
+ // ContainsCell reports whether the region completely contains the given region.
+ // It returns false if containment could not be determined.
+ ContainsCell(c Cell) bool
+
+ // IntersectsCell reports whether the region intersects the given cell or
+ // if intersection could not be determined. It returns false if the region
+ // does not intersect.
+ IntersectsCell(c Cell) bool
+}
+
+// Enforce interface satisfaction.
+var (
+ _ Region = Cap{}
+ _ Region = Cell{}
+ _ Region = (*CellUnion)(nil)
+ //_ Region = (*Polygon)(nil)
+ _ Region = Polyline{}
+ _ Region = Rect{}
+)
diff --git a/vendor/github.com/golang/geo/s2/regioncoverer.go b/vendor/github.com/golang/geo/s2/regioncoverer.go
new file mode 100644
index 0000000..4a44f28
--- /dev/null
+++ b/vendor/github.com/golang/geo/s2/regioncoverer.go
@@ -0,0 +1,465 @@
+/*
+Copyright 2015 Google Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package s2
+
+import (
+ "container/heap"
+)
+
+// RegionCoverer allows arbitrary regions to be approximated as unions of cells (CellUnion).
+// This is useful for implementing various sorts of search and precomputation operations.
+//
+// Typical usage:
+//
+// rc := &s2.RegionCoverer{MaxLevel: 30, MaxCells: 5}
+// r := s2.Region(CapFromCenterArea(center, area))
+// covering := rc.Covering(r)
+//
+// This yields a CellUnion of at most 5 cells that is guaranteed to cover the
+// given region (a disc-shaped region on the sphere).
+//
+// For covering, only cells where (level - MinLevel) is a multiple of LevelMod will be used.
+// This effectively allows the branching factor of the S2 CellID hierarchy to be increased.
+// Currently the only parameter values allowed are 0/1, 2, or 3, corresponding to
+// branching factors of 4, 16, and 64 respectively.
+//
+// Note the following:
+//
+// - MinLevel takes priority over MaxCells, i.e. cells below the given level will
+// never be used even if this causes a large number of cells to be returned.
+//
+// - For any setting of MaxCells, up to 6 cells may be returned if that
+// is the minimum number of cells required (e.g. if the region intersects
+// all six face cells). Up to 3 cells may be returned even for very tiny
+// convex regions if they happen to be located at the intersection of
+// three cube faces.
+//
+// - For any setting of MaxCells, an arbitrary number of cells may be
+// returned if MinLevel is too high for the region being approximated.
+//
+// - If MaxCells is less than 4, the area of the covering may be
+// arbitrarily large compared to the area of the original region even if
+// the region is convex (e.g. a Cap or Rect).
+//
+// The approximation algorithm is not optimal but does a pretty good job in
+// practice. The output does not always use the maximum number of cells
+// allowed, both because this would not always yield a better approximation,
+// and because MaxCells is a limit on how much work is done exploring the
+// possible covering as well as a limit on the final output size.
+//
+// Because it is an approximation algorithm, one should not rely on the
+// stability of the output. In particular, the output of the covering algorithm
+// may change across different versions of the library.
+//
+// One can also generate interior coverings, which are sets of cells which
+// are entirely contained within a region. Interior coverings can be
+// empty, even for non-empty regions, if there are no cells that satisfy
+// the provided constraints and are contained by the region. Note that for
+// performance reasons, it is wise to specify a MaxLevel when computing
+// interior coverings - otherwise for regions with small or zero area, the
+// algorithm may spend a lot of time subdividing cells all the way to leaf
+// level to try to find contained cells.
+type RegionCoverer struct {
+ MinLevel int // the minimum cell level to be used.
+ MaxLevel int // the maximum cell level to be used.
+ LevelMod int // the LevelMod to be used.
+ MaxCells int // the maximum desired number of cells in the approximation.
+}
+
+type coverer struct {
+ minLevel int // the minimum cell level to be used.
+ maxLevel int // the maximum cell level to be used.
+ levelMod int // the LevelMod to be used.
+ maxCells int // the maximum desired number of cells in the approximation.
+ region Region
+ result CellUnion
+ pq priorityQueue
+ interiorCovering bool
+}
+
+type candidate struct {
+ cell Cell
+ terminal bool // Cell should not be expanded further.
+ numChildren int // Number of children that intersect the region.
+ children []*candidate // Actual size may be 0, 4, 16, or 64 elements.
+ priority int // Priority of the candiate.
+}
+
+func min(x, y int) int {
+ if x < y {
+ return x
+ }
+ return y
+}
+
+func max(x, y int) int {
+ if x > y {
+ return x
+ }
+ return y
+}
+
+type priorityQueue []*candidate
+
+func (pq priorityQueue) Len() int {
+ return len(pq)
+}
+
+func (pq priorityQueue) Less(i, j int) bool {
+ // We want Pop to give us the highest, not lowest, priority so we use greater than here.
+ return pq[i].priority > pq[j].priority
+}
+
+func (pq priorityQueue) Swap(i, j int) {
+ pq[i], pq[j] = pq[j], pq[i]
+}
+
+func (pq *priorityQueue) Push(x interface{}) {
+ item := x.(*candidate)
+ *pq = append(*pq, item)
+}
+
+func (pq *priorityQueue) Pop() interface{} {
+ item := (*pq)[len(*pq)-1]
+ *pq = (*pq)[:len(*pq)-1]
+ return item
+}
+
+func (pq *priorityQueue) Reset() {
+ *pq = (*pq)[:0]
+}
+
+// newCandidate returns a new candidate with no children if the cell intersects the given region.
+// The candidate is marked as terminal if it should not be expanded further.
+func (c *coverer) newCandidate(cell Cell) *candidate {
+ if !c.region.IntersectsCell(cell) {
+ return nil
+ }
+ cand := &candidate{cell: cell}
+ level := int(cell.level)
+ if level >= c.minLevel {
+ if c.interiorCovering {
+ if c.region.ContainsCell(cell) {
+ cand.terminal = true
+ } else if level+c.levelMod > c.maxLevel {
+ return nil
+ }
+ } else if level+c.levelMod > c.maxLevel || c.region.ContainsCell(cell) {
+ cand.terminal = true
+ }
+ }
+ return cand
+}
+
+// expandChildren populates the children of the candidate by expanding the given number of
+// levels from the given cell. Returns the number of children that were marked "terminal".
+func (c *coverer) expandChildren(cand *candidate, cell Cell, numLevels int) int {
+ numLevels--
+ var numTerminals int
+ last := cell.id.ChildEnd()
+ for ci := cell.id.ChildBegin(); ci != last; ci = ci.Next() {
+ childCell := CellFromCellID(ci)
+ if numLevels > 0 {
+ if c.region.IntersectsCell(childCell) {
+ numTerminals += c.expandChildren(cand, childCell, numLevels)
+ }
+ continue
+ }
+ if child := c.newCandidate(childCell); child != nil {
+ cand.children = append(cand.children, child)
+ cand.numChildren++
+ if child.terminal {
+ numTerminals++
+ }
+ }
+ }
+ return numTerminals
+}
+
+// addCandidate adds the given candidate to the result if it is marked as "terminal",
+// otherwise expands its children and inserts it into the priority queue.
+// Passing an argument of nil does nothing.
+func (c *coverer) addCandidate(cand *candidate) {
+ if cand.terminal {
+ c.result = append(c.result, cand.cell.id)
+ return
+ }
+
+ // Expand one level at a time until we hit minLevel to ensure that we don't skip over it.
+ numLevels := c.levelMod
+ level := int(cand.cell.level)
+ if level < c.minLevel {
+ numLevels = 1
+ }
+
+ numTerminals := c.expandChildren(cand, cand.cell, numLevels)
+ maxChildrenShift := uint(2 * c.levelMod)
+ if cand.numChildren == 0 {
+ return
+ } else if !c.interiorCovering && numTerminals == 1<<maxChildrenShift && level >= c.minLevel {
+ // Optimization: add the parent cell rather than all of its children.
+ // We can't do this for interior coverings, since the children just
+ // intersect the region, but may not be contained by it - we need to
+ // subdivide them further.
+ cand.terminal = true
+ c.addCandidate(cand)
+ } else {
+ // We negate the priority so that smaller absolute priorities are returned
+ // first. The heuristic is designed to refine the largest cells first,
+ // since those are where we have the largest potential gain. Among cells
+ // of the same size, we prefer the cells with the fewest children.
+ // Finally, among cells with equal numbers of children we prefer those
+ // with the smallest number of children that cannot be refined further.
+ cand.priority = -(((level<<maxChildrenShift)+cand.numChildren)<<maxChildrenShift + numTerminals)
+ heap.Push(&c.pq, cand)
+ }
+}
+
+// adjustLevel returns the reduced "level" so that it satisfies levelMod. Levels smaller than minLevel
+// are not affected (since cells at these levels are eventually expanded).
+func (c *coverer) adjustLevel(level int) int {
+ if c.levelMod > 1 && level > c.minLevel {
+ level -= (level - c.minLevel) % c.levelMod
+ }
+ return level
+}
+
+// adjustCellLevels ensures that all cells with level > minLevel also satisfy levelMod,
+// by replacing them with an ancestor if necessary. Cell levels smaller
+// than minLevel are not modified (see AdjustLevel). The output is
+// then normalized to ensure that no redundant cells are present.
+func (c *coverer) adjustCellLevels(cells *CellUnion) {
+ if c.levelMod == 1 {
+ return
+ }
+
+ var out int
+ for _, ci := range *cells {
+ level := ci.Level()
+ newLevel := c.adjustLevel(level)
+ if newLevel != level {
+ ci = ci.Parent(newLevel)
+ }
+ if out > 0 && (*cells)[out-1].Contains(ci) {
+ continue
+ }
+ for out > 0 && ci.Contains((*cells)[out-1]) {
+ out--
+ }
+ (*cells)[out] = ci
+ out++
+ }
+ *cells = (*cells)[:out]
+}
+
+// initialCandidates computes a set of initial candidates that cover the given region.
+func (c *coverer) initialCandidates() {
+ // Optimization: start with a small (usually 4 cell) covering of the region's bounding cap.
+ temp := &RegionCoverer{MaxLevel: c.maxLevel, LevelMod: 1, MaxCells: min(4, c.maxCells)}
+
+ cells := temp.FastCovering(c.region.CapBound())
+ c.adjustCellLevels(&cells)
+ for _, ci := range cells {
+ if cand := c.newCandidate(CellFromCellID(ci)); cand != nil {
+ c.addCandidate(cand)
+ }
+ }
+}
+
+// coveringInternal generates a covering and stores it in result.
+// Strategy: Start with the 6 faces of the cube. Discard any
+// that do not intersect the shape. Then repeatedly choose the
+// largest cell that intersects the shape and subdivide it.
+//
+// result contains the cells that will be part of the output, while pq
+// contains cells that we may still subdivide further. Cells that are
+// entirely contained within the region are immediately added to the output,
+// while cells that do not intersect the region are immediately discarded.
+// Therefore pq only contains cells that partially intersect the region.
+// Candidates are prioritized first according to cell size (larger cells
+// first), then by the number of intersecting children they have (fewest
+// children first), and then by the number of fully contained children
+// (fewest children first).
+func (c *coverer) coveringInternal(region Region) {
+ c.region = region
+
+ c.initialCandidates()
+ for c.pq.Len() > 0 && (!c.interiorCovering || len(c.result) < c.maxCells) {
+ cand := heap.Pop(&c.pq).(*candidate)
+
+ // For interior covering we keep subdividing no matter how many children
+ // candidate has. If we reach MaxCells before expanding all children,
+ // we will just use some of them.
+ // For exterior covering we cannot do this, because result has to cover the
+ // whole region, so all children have to be used.
+ // candidate.numChildren == 1 case takes care of the situation when we
+ // already have more then MaxCells in result (minLevel is too high).
+ // Subdividing of the candidate with one child does no harm in this case.
+ if c.interiorCovering || int(cand.cell.level) < c.minLevel || cand.numChildren == 1 || len(c.result)+c.pq.Len()+cand.numChildren <= c.maxCells {
+ for _, child := range cand.children {
+ if !c.interiorCovering || len(c.result) < c.maxCells {
+ c.addCandidate(child)
+ }
+ }
+ } else {
+ cand.terminal = true
+ c.addCandidate(cand)
+ }
+ }
+ c.pq.Reset()
+ c.region = nil
+}
+
+// newCoverer returns an instance of coverer.
+func (rc *RegionCoverer) newCoverer() *coverer {
+ return &coverer{
+ minLevel: max(0, min(maxLevel, rc.MinLevel)),
+ maxLevel: max(0, min(maxLevel, rc.MaxLevel)),
+ levelMod: max(1, min(3, rc.LevelMod)),
+ maxCells: rc.MaxCells,
+ }
+}
+
+// Covering returns a CellUnion that covers the given region and satisfies the various restrictions.
+func (rc *RegionCoverer) Covering(region Region) CellUnion {
+ covering := rc.CellUnion(region)
+ covering.Denormalize(max(0, min(maxLevel, rc.MinLevel)), max(1, min(3, rc.LevelMod)))
+ return covering
+}
+
+// InteriorCovering returns a CellUnion that is contained within the given region and satisfies the various restrictions.
+func (rc *RegionCoverer) InteriorCovering(region Region) CellUnion {
+ intCovering := rc.InteriorCellUnion(region)
+ intCovering.Denormalize(max(0, min(maxLevel, rc.MinLevel)), max(1, min(3, rc.LevelMod)))
+ return intCovering
+}
+
+// CellUnion returns a normalized CellUnion that covers the given region and
+// satisfies the restrictions except for minLevel and levelMod. These criteria
+// cannot be satisfied using a cell union because cell unions are
+// automatically normalized by replacing four child cells with their parent
+// whenever possible. (Note that the list of cell ids passed to the CellUnion
+// constructor does in fact satisfy all the given restrictions.)
+func (rc *RegionCoverer) CellUnion(region Region) CellUnion {
+ c := rc.newCoverer()
+ c.coveringInternal(region)
+ cu := c.result
+ cu.Normalize()
+ return cu
+}
+
+// InteriorCellUnion returns a normalized CellUnion that is contained within the given region and
+// satisfies the restrictions except for minLevel and levelMod. These criteria
+// cannot be satisfied using a cell union because cell unions are
+// automatically normalized by replacing four child cells with their parent
+// whenever possible. (Note that the list of cell ids passed to the CellUnion
+// constructor does in fact satisfy all the given restrictions.)
+func (rc *RegionCoverer) InteriorCellUnion(region Region) CellUnion {
+ c := rc.newCoverer()
+ c.interiorCovering = true
+ c.coveringInternal(region)
+ cu := c.result
+ cu.Normalize()
+ return cu
+}
+
+// FastCovering returns a CellUnion that covers the given region similar to Covering,
+// except that this method is much faster and the coverings are not as tight.
+// All of the usual parameters are respected (MaxCells, MinLevel, MaxLevel, and LevelMod),
+// except that the implementation makes no attempt to take advantage of large values of
+// MaxCells. (A small number of cells will always be returned.)
+//
+// This function is useful as a starting point for algorithms that
+// recursively subdivide cells.
+func (rc *RegionCoverer) FastCovering(cap Cap) CellUnion {
+ c := rc.newCoverer()
+ cu := c.rawFastCovering(cap)
+ c.normalizeCovering(&cu)
+ return cu
+}
+
+// rawFastCovering computes a covering of the given cap. In general the covering consists of
+// at most 4 cells (except for very large caps, which may need up to 6 cells).
+// The output is not sorted.
+func (c *coverer) rawFastCovering(cap Cap) CellUnion {
+ var covering CellUnion
+ // Find the maximum level such that the cap contains at most one cell vertex
+ // and such that CellId.VertexNeighbors() can be called.
+ level := min(MinWidthMetric.MaxLevel(2*cap.Radius().Radians()), maxLevel-1)
+ if level == 0 {
+ for face := 0; face < 6; face++ {
+ covering = append(covering, CellIDFromFace(face))
+ }
+ } else {
+ covering = append(covering, cellIDFromPoint(cap.center).VertexNeighbors(level)...)
+ }
+ return covering
+}
+
+// normalizeCovering normalizes the "covering" so that it conforms to the current covering
+// parameters (MaxCells, minLevel, maxLevel, and levelMod).
+// This method makes no attempt to be optimal. In particular, if
+// minLevel > 0 or levelMod > 1 then it may return more than the
+// desired number of cells even when this isn't necessary.
+//
+// Note that when the covering parameters have their default values, almost
+// all of the code in this function is skipped.
+func (c *coverer) normalizeCovering(covering *CellUnion) {
+ // If any cells are too small, or don't satisfy levelMod, then replace them with ancestors.
+ if c.maxLevel < maxLevel || c.levelMod > 1 {
+ for i, ci := range *covering {
+ level := ci.Level()
+ newLevel := c.adjustLevel(min(level, c.maxLevel))
+ if newLevel != level {
+ (*covering)[i] = ci.Parent(newLevel)
+ }
+ }
+ }
+ // Sort the cells and simplify them.
+ covering.Normalize()
+
+ // If there are still too many cells, then repeatedly replace two adjacent
+ // cells in CellID order by their lowest common ancestor.
+ for len(*covering) > c.maxCells {
+ bestIndex := -1
+ bestLevel := -1
+ for i := 0; i+1 < len(*covering); i++ {
+ level, ok := (*covering)[i].CommonAncestorLevel((*covering)[i+1])
+ if !ok {
+ continue
+ }
+ level = c.adjustLevel(level)
+ if level > bestLevel {
+ bestLevel = level
+ bestIndex = i
+ }
+ }
+
+ if bestLevel < c.minLevel {
+ break
+ }
+ (*covering)[bestIndex] = (*covering)[bestIndex].Parent(bestLevel)
+ covering.Normalize()
+ }
+ // Make sure that the covering satisfies minLevel and levelMod,
+ // possibly at the expense of satisfying MaxCells.
+ if c.minLevel > 0 || c.levelMod > 1 {
+ covering.Denormalize(c.minLevel, c.levelMod)
+ }
+}
+
+// BUG(akashagrawal): The differences from the C++ version FloodFill, SimpleCovering
diff --git a/vendor/github.com/golang/geo/s2/shapeindex.go b/vendor/github.com/golang/geo/s2/shapeindex.go
new file mode 100644
index 0000000..4bf15e5
--- /dev/null
+++ b/vendor/github.com/golang/geo/s2/shapeindex.go
@@ -0,0 +1,202 @@
+/*
+Copyright 2016 Google Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package s2
+
+import (
+ "github.com/golang/geo/r2"
+)
+
+// Shape defines an interface for any s2 type that needs to be indexable.
+type Shape interface {
+ // NumEdges returns the number of edges in this shape.
+ NumEdges() int
+
+ // Edge returns endpoints for the given edge index.
+ Edge(i int) (a, b Point)
+
+ // HasInterior returns true if this shape has an interior.
+ // i.e. the Shape consists of one or more closed non-intersecting loops.
+ HasInterior() bool
+
+ // ContainsOrigin returns true if this shape contains s2.Origin.
+ // Shapes that do not have an interior will return false.
+ ContainsOrigin() bool
+}
+
+// A minimal check for types that should satisfy the Shape interface.
+var (
+ _ Shape = Loop{}
+ _ Shape = Polyline{}
+)
+
+// CellRelation describes the possible relationships between a target cell
+// and the cells of the ShapeIndex. If the target is an index cell or is
+// contained by an index cell, it is Indexed. If the target is subdivided
+// into one or more index cells, it is Subdivided. Otherwise it is Disjoint.
+type CellRelation int
+
+// The possible CellRelations for a ShapeIndex.
+const (
+ Indexed CellRelation = iota
+ Subdivided
+ Disjoint
+)
+
+var (
+ // cellPadding defines the total error when clipping an edge which comes
+ // from two sources:
+ // (1) Clipping the original spherical edge to a cube face (the face edge).
+ // The maximum error in this step is faceClipErrorUVCoord.
+ // (2) Clipping the face edge to the u- or v-coordinate of a cell boundary.
+ // The maximum error in this step is edgeClipErrorUVCoord.
+ // Finally, since we encounter the same errors when clipping query edges, we
+ // double the total error so that we only need to pad edges during indexing
+ // and not at query time.
+ cellPadding = 2.0 * (faceClipErrorUVCoord + edgeClipErrorUVCoord)
+)
+
+type clippedShape struct {
+ // shapeID is the index of the shape this clipped shape is a part of.
+ shapeID int32
+
+ // containsCenter indicates if the center of the CellID this shape has been
+ // clipped to falls inside this shape. This is false for shapes that do not
+ // have an interior.
+ containsCenter bool
+
+ // edges is the ordered set of ShapeIndex original edge ids. Edges
+ // are stored in increasing order of edge id.
+ edges []int
+}
+
+// init initializes this shape for the given shapeID and number of expected edges.
+func newClippedShape(id int32, numEdges int) *clippedShape {
+ return &clippedShape{
+ shapeID: id,
+ edges: make([]int, numEdges),
+ }
+}
+
+// shapeIndexCell stores the index contents for a particular CellID.
+type shapeIndexCell struct {
+ shapes []*clippedShape
+}
+
+// add adds the given clipped shape to this index cell.
+func (s *shapeIndexCell) add(c *clippedShape) {
+ s.shapes = append(s.shapes, c)
+}
+
+// findByID returns the clipped shape that contains the given shapeID,
+// or nil if none of the clipped shapes contain it.
+func (s *shapeIndexCell) findByID(shapeID int32) *clippedShape {
+ // Linear search is fine because the number of shapes per cell is typically
+ // very small (most often 1), and is large only for pathological inputs
+ // (e.g. very deeply nested loops).
+ for _, clipped := range s.shapes {
+ if clipped.shapeID == shapeID {
+ return clipped
+ }
+ }
+ return nil
+}
+
+// faceEdge and clippedEdge store temporary edge data while the index is being
+// updated.
+//
+// While it would be possible to combine all the edge information into one
+// structure, there are two good reasons for separating it:
+//
+// - Memory usage. Separating the two means that we only need to
+// store one copy of the per-face data no matter how many times an edge is
+// subdivided, and it also lets us delay computing bounding boxes until
+// they are needed for processing each face (when the dataset spans
+// multiple faces).
+//
+// - Performance. UpdateEdges is significantly faster on large polygons when
+// the data is separated, because it often only needs to access the data in
+// clippedEdge and this data is cached more successfully.
+
+// faceEdge represents an edge that has been projected onto a given face,
+type faceEdge struct {
+ shapeID int32 // The ID of shape that this edge belongs to
+ edgeID int // Edge ID within that shape
+ maxLevel int // Not desirable to subdivide this edge beyond this level
+ hasInterior bool // Belongs to a shape that has an interior
+ a, b r2.Point // The edge endpoints, clipped to a given face
+ va, vb Point // The original Loop vertices of this edge.
+}
+
+// clippedEdge represents the portion of that edge that has been clipped to a given Cell.
+type clippedEdge struct {
+ faceEdge *faceEdge // The original unclipped edge
+ bound r2.Rect // Bounding box for the clipped portion
+}
+
+// ShapeIndex indexes a set of Shapes, where a Shape is some collection of
+// edges. A shape can be as simple as a single edge, or as complex as a set of loops.
+// For Shapes that have interiors, the index makes it very fast to determine which
+// Shape(s) contain a given point or region.
+type ShapeIndex struct {
+ // shapes maps all shapes to their index.
+ shapes map[Shape]int32
+
+ maxEdgesPerCell int
+
+ // nextID tracks the next ID to hand out. IDs are not reused when shapes
+ // are removed from the index.
+ nextID int32
+}
+
+// NewShapeIndex creates a new ShapeIndex.
+func NewShapeIndex() *ShapeIndex {
+ return &ShapeIndex{
+ maxEdgesPerCell: 10,
+ shapes: make(map[Shape]int32),
+ }
+}
+
+// Add adds the given shape to the index and assign an ID to it.
+func (s *ShapeIndex) Add(shape Shape) {
+ s.shapes[shape] = s.nextID
+ s.nextID++
+}
+
+// Remove removes the given shape from the index.
+func (s *ShapeIndex) Remove(shape Shape) {
+ delete(s.shapes, shape)
+}
+
+// Len reports the number of Shapes in this index.
+func (s *ShapeIndex) Len() int {
+ return len(s.shapes)
+}
+
+// Reset clears the contents of the index and resets it to its original state.
+func (s *ShapeIndex) Reset() {
+ s.shapes = make(map[Shape]int32)
+ s.nextID = 0
+}
+
+// NumEdges returns the number of edges in this index.
+func (s *ShapeIndex) NumEdges() int {
+ numEdges := 0
+ for shape := range s.shapes {
+ numEdges += shape.NumEdges()
+ }
+ return numEdges
+}
diff --git a/vendor/github.com/golang/geo/s2/stuv.go b/vendor/github.com/golang/geo/s2/stuv.go
new file mode 100644
index 0000000..e669b7c
--- /dev/null
+++ b/vendor/github.com/golang/geo/s2/stuv.go
@@ -0,0 +1,310 @@
+/*
+Copyright 2014 Google Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package s2
+
+import (
+ "math"
+
+ "github.com/golang/geo/r3"
+)
+
+const (
+ // maxSiTi is the maximum value of an si- or ti-coordinate.
+ // It is one shift more than maxSize.
+ maxSiTi = maxSize << 1
+)
+
+// siTiToST converts an si- or ti-value to the corresponding s- or t-value.
+// Value is capped at 1.0 because there is no DCHECK in Go.
+func siTiToST(si uint64) float64 {
+ if si > maxSiTi {
+ return 1.0
+ }
+ return float64(si) / float64(maxSiTi)
+}
+
+// stToSiTi converts the s- or t-value to the nearest si- or ti-coordinate.
+// The result may be outside the range of valid (si,ti)-values. Value of
+// 0.49999999999999994 (math.NextAfter(0.5, -1)), will be incorrectly rounded up.
+func stToSiTi(s float64) uint64 {
+ if s < 0 {
+ return uint64(s*maxSiTi - 0.5)
+ }
+ return uint64(s*maxSiTi + 0.5)
+}
+
+// stToUV converts an s or t value to the corresponding u or v value.
+// This is a non-linear transformation from [-1,1] to [-1,1] that
+// attempts to make the cell sizes more uniform.
+// This uses what the C++ version calls 'the quadratic transform'.
+func stToUV(s float64) float64 {
+ if s >= 0.5 {
+ return (1 / 3.) * (4*s*s - 1)
+ }
+ return (1 / 3.) * (1 - 4*(1-s)*(1-s))
+}
+
+// uvToST is the inverse of the stToUV transformation. Note that it
+// is not always true that uvToST(stToUV(x)) == x due to numerical
+// errors.
+func uvToST(u float64) float64 {
+ if u >= 0 {
+ return 0.5 * math.Sqrt(1+3*u)
+ }
+ return 1 - 0.5*math.Sqrt(1-3*u)
+}
+
+// face returns face ID from 0 to 5 containing the r. For points on the
+// boundary between faces, the result is arbitrary but deterministic.
+func face(r r3.Vector) int {
+ abs := r.Abs()
+ id := 0
+ value := r.X
+ if abs.Y > abs.X {
+ id = 1
+ value = r.Y
+ }
+ if abs.Z > math.Abs(value) {
+ id = 2
+ value = r.Z
+ }
+ if value < 0 {
+ id += 3
+ }
+ return id
+}
+
+// validFaceXYZToUV given a valid face for the given point r (meaning that
+// dot product of r with the face normal is positive), returns
+// the corresponding u and v values, which may lie outside the range [-1,1].
+func validFaceXYZToUV(face int, r r3.Vector) (float64, float64) {
+ switch face {
+ case 0:
+ return r.Y / r.X, r.Z / r.X
+ case 1:
+ return -r.X / r.Y, r.Z / r.Y
+ case 2:
+ return -r.X / r.Z, -r.Y / r.Z
+ case 3:
+ return r.Z / r.X, r.Y / r.X
+ case 4:
+ return r.Z / r.Y, -r.X / r.Y
+ }
+ return -r.Y / r.Z, -r.X / r.Z
+}
+
+// xyzToFaceUV converts a direction vector (not necessarily unit length) to
+// (face, u, v) coordinates.
+func xyzToFaceUV(r r3.Vector) (f int, u, v float64) {
+ f = face(r)
+ u, v = validFaceXYZToUV(f, r)
+ return f, u, v
+}
+
+// faceUVToXYZ turns face and UV coordinates into an unnormalized 3 vector.
+func faceUVToXYZ(face int, u, v float64) r3.Vector {
+ switch face {
+ case 0:
+ return r3.Vector{1, u, v}
+ case 1:
+ return r3.Vector{-u, 1, v}
+ case 2:
+ return r3.Vector{-u, -v, 1}
+ case 3:
+ return r3.Vector{-1, -v, -u}
+ case 4:
+ return r3.Vector{v, -1, -u}
+ default:
+ return r3.Vector{v, u, -1}
+ }
+}
+
+// faceXYZToUV returns the u and v values (which may lie outside the range
+// [-1, 1]) if the dot product of the point p with the given face normal is positive.
+func faceXYZToUV(face int, p Point) (u, v float64, ok bool) {
+ switch face {
+ case 0:
+ if p.X <= 0 {
+ return 0, 0, false
+ }
+ case 1:
+ if p.Y <= 0 {
+ return 0, 0, false
+ }
+ case 2:
+ if p.Z <= 0 {
+ return 0, 0, false
+ }
+ case 3:
+ if p.X >= 0 {
+ return 0, 0, false
+ }
+ case 4:
+ if p.Y >= 0 {
+ return 0, 0, false
+ }
+ default:
+ if p.Z >= 0 {
+ return 0, 0, false
+ }
+ }
+
+ u, v = validFaceXYZToUV(face, p.Vector)
+ return u, v, true
+}
+
+// faceXYZtoUVW transforms the given point P to the (u,v,w) coordinate frame of the given
+// face where the w-axis represents the face normal.
+func faceXYZtoUVW(face int, p Point) Point {
+ // The result coordinates are simply the dot products of P with the (u,v,w)
+ // axes for the given face (see faceUVWAxes).
+ switch face {
+ case 0:
+ return Point{r3.Vector{p.Y, p.Z, p.X}}
+ case 1:
+ return Point{r3.Vector{-p.X, p.Z, p.Y}}
+ case 2:
+ return Point{r3.Vector{-p.X, -p.Y, p.Z}}
+ case 3:
+ return Point{r3.Vector{-p.Z, -p.Y, -p.X}}
+ case 4:
+ return Point{r3.Vector{-p.Z, p.X, -p.Y}}
+ default:
+ return Point{r3.Vector{p.Y, p.X, -p.Z}}
+ }
+}
+
+// faceSiTiToXYZ transforms the (si, ti) coordinates to a (not necessarily
+// unit length) Point on the given face.
+func faceSiTiToXYZ(face int, si, ti uint64) Point {
+ return Point{faceUVToXYZ(face, stToUV(siTiToST(si)), stToUV(siTiToST(ti)))}
+}
+
+// xyzToFaceSiTi transforms the (not necessarily unit length) Point to
+// (face, si, ti) coordinates and the level the Point is at.
+func xyzToFaceSiTi(p Point) (face int, si, ti uint64, level int) {
+ face, u, v := xyzToFaceUV(p.Vector)
+ si = stToSiTi(uvToST(u))
+ ti = stToSiTi(uvToST(v))
+
+ // If the levels corresponding to si,ti are not equal, then p is not a cell
+ // center. The si,ti values of 0 and maxSiTi need to be handled specially
+ // because they do not correspond to cell centers at any valid level; they
+ // are mapped to level -1 by the code at the end.
+ level = maxLevel - findLSBSetNonZero64(si|maxSiTi)
+ if level < 0 || level != maxLevel-findLSBSetNonZero64(ti|maxSiTi) {
+ return face, si, ti, -1
+ }
+
+ // In infinite precision, this test could be changed to ST == SiTi. However,
+ // due to rounding errors, uvToST(xyzToFaceUV(faceUVToXYZ(stToUV(...)))) is
+ // not idempotent. On the other hand, the center is computed exactly the same
+ // way p was originally computed (if it is indeed the center of a Cell);
+ // the comparison can be exact.
+ if p.Vector == faceSiTiToXYZ(face, si, ti).Normalize() {
+ return face, si, ti, level
+ }
+
+ return face, si, ti, -1
+}
+
+// uNorm returns the right-handed normal (not necessarily unit length) for an
+// edge in the direction of the positive v-axis at the given u-value on
+// the given face. (This vector is perpendicular to the plane through
+// the sphere origin that contains the given edge.)
+func uNorm(face int, u float64) r3.Vector {
+ switch face {
+ case 0:
+ return r3.Vector{u, -1, 0}
+ case 1:
+ return r3.Vector{1, u, 0}
+ case 2:
+ return r3.Vector{1, 0, u}
+ case 3:
+ return r3.Vector{-u, 0, 1}
+ case 4:
+ return r3.Vector{0, -u, 1}
+ default:
+ return r3.Vector{0, -1, -u}
+ }
+}
+
+// vNorm returns the right-handed normal (not necessarily unit length) for an
+// edge in the direction of the positive u-axis at the given v-value on
+// the given face.
+func vNorm(face int, v float64) r3.Vector {
+ switch face {
+ case 0:
+ return r3.Vector{-v, 0, 1}
+ case 1:
+ return r3.Vector{0, -v, 1}
+ case 2:
+ return r3.Vector{0, -1, -v}
+ case 3:
+ return r3.Vector{v, -1, 0}
+ case 4:
+ return r3.Vector{1, v, 0}
+ default:
+ return r3.Vector{1, 0, v}
+ }
+}
+
+// faceUVWAxes are the U, V, and W axes for each face.
+var faceUVWAxes = [6][3]Point{
+ {Point{r3.Vector{0, 1, 0}}, Point{r3.Vector{0, 0, 1}}, Point{r3.Vector{1, 0, 0}}},
+ {Point{r3.Vector{-1, 0, 0}}, Point{r3.Vector{0, 0, 1}}, Point{r3.Vector{0, 1, 0}}},
+ {Point{r3.Vector{-1, 0, 0}}, Point{r3.Vector{0, -1, 0}}, Point{r3.Vector{0, 0, 1}}},
+ {Point{r3.Vector{0, 0, -1}}, Point{r3.Vector{0, -1, 0}}, Point{r3.Vector{-1, 0, 0}}},
+ {Point{r3.Vector{0, 0, -1}}, Point{r3.Vector{1, 0, 0}}, Point{r3.Vector{0, -1, 0}}},
+ {Point{r3.Vector{0, 1, 0}}, Point{r3.Vector{1, 0, 0}}, Point{r3.Vector{0, 0, -1}}},
+}
+
+// faceUVWFaces are the precomputed neighbors of each face.
+var faceUVWFaces = [6][3][2]int{
+ {{4, 1}, {5, 2}, {3, 0}},
+ {{0, 3}, {5, 2}, {4, 1}},
+ {{0, 3}, {1, 4}, {5, 2}},
+ {{2, 5}, {1, 4}, {0, 3}},
+ {{2, 5}, {3, 0}, {1, 4}},
+ {{4, 1}, {3, 0}, {2, 5}},
+}
+
+// uvwAxis returns the given axis of the given face.
+func uvwAxis(face, axis int) Point {
+ return faceUVWAxes[face][axis]
+}
+
+// uvwFaces returns the face in the (u,v,w) coordinate system on the given axis
+// in the given direction.
+func uvwFace(face, axis, direction int) int {
+ return faceUVWFaces[face][axis][direction]
+}
+
+// uAxis returns the u-axis for the given face.
+func uAxis(face int) Point {
+ return uvwAxis(face, 0)
+}
+
+// vAxis returns the v-axis for the given face.
+func vAxis(face int) Point {
+ return uvwAxis(face, 1)
+}
+
+// Return the unit-length normal for the given face.
+func unitNorm(face int) Point {
+ return uvwAxis(face, 2)
+}
diff --git a/vendor/github.com/jessevdk/go-flags/LICENSE b/vendor/github.com/jessevdk/go-flags/LICENSE
new file mode 100644
index 0000000..bcca0d5
--- /dev/null
+++ b/vendor/github.com/jessevdk/go-flags/LICENSE
@@ -0,0 +1,26 @@
+Copyright (c) 2012 Jesse van den Kieboom. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following disclaimer
+ in the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of Google Inc. nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/jessevdk/go-flags/arg.go b/vendor/github.com/jessevdk/go-flags/arg.go
new file mode 100644
index 0000000..8ec6204
--- /dev/null
+++ b/vendor/github.com/jessevdk/go-flags/arg.go
@@ -0,0 +1,27 @@
+package flags
+
+import (
+ "reflect"
+)
+
+// Arg represents a positional argument on the command line.
+type Arg struct {
+ // The name of the positional argument (used in the help)
+ Name string
+
+ // A description of the positional argument (used in the help)
+ Description string
+
+ // The minimal number of required positional arguments
+ Required int
+
+ // The maximum number of required positional arguments
+ RequiredMaximum int
+
+ value reflect.Value
+ tag multiTag
+}
+
+func (a *Arg) isRemaining() bool {
+ return a.value.Type().Kind() == reflect.Slice
+}
diff --git a/vendor/github.com/jessevdk/go-flags/closest.go b/vendor/github.com/jessevdk/go-flags/closest.go
new file mode 100644
index 0000000..3b51875
--- /dev/null
+++ b/vendor/github.com/jessevdk/go-flags/closest.go
@@ -0,0 +1,59 @@
+package flags
+
+func levenshtein(s string, t string) int {
+ if len(s) == 0 {
+ return len(t)
+ }
+
+ if len(t) == 0 {
+ return len(s)
+ }
+
+ dists := make([][]int, len(s)+1)
+ for i := range dists {
+ dists[i] = make([]int, len(t)+1)
+ dists[i][0] = i
+ }
+
+ for j := range t {
+ dists[0][j] = j
+ }
+
+ for i, sc := range s {
+ for j, tc := range t {
+ if sc == tc {
+ dists[i+1][j+1] = dists[i][j]
+ } else {
+ dists[i+1][j+1] = dists[i][j] + 1
+ if dists[i+1][j] < dists[i+1][j+1] {
+ dists[i+1][j+1] = dists[i+1][j] + 1
+ }
+ if dists[i][j+1] < dists[i+1][j+1] {
+ dists[i+1][j+1] = dists[i][j+1] + 1
+ }
+ }
+ }
+ }
+
+ return dists[len(s)][len(t)]
+}
+
+func closestChoice(cmd string, choices []string) (string, int) {
+ if len(choices) == 0 {
+ return "", 0
+ }
+
+ mincmd := -1
+ mindist := -1
+
+ for i, c := range choices {
+ l := levenshtein(cmd, c)
+
+ if mincmd < 0 || l < mindist {
+ mindist = l
+ mincmd = i
+ }
+ }
+
+ return choices[mincmd], mindist
+}
diff --git a/vendor/github.com/jessevdk/go-flags/command.go b/vendor/github.com/jessevdk/go-flags/command.go
new file mode 100644
index 0000000..2662843
--- /dev/null
+++ b/vendor/github.com/jessevdk/go-flags/command.go
@@ -0,0 +1,455 @@
+package flags
+
+import (
+ "reflect"
+ "sort"
+ "strconv"
+ "strings"
+ "unsafe"
+)
+
+// Command represents an application command. Commands can be added to the
+// parser (which itself is a command) and are selected/executed when its name
+// is specified on the command line. The Command type embeds a Group and
+// therefore also carries a set of command specific options.
+type Command struct {
+ // Embedded, see Group for more information
+ *Group
+
+ // The name by which the command can be invoked
+ Name string
+
+ // The active sub command (set by parsing) or nil
+ Active *Command
+
+ // Whether subcommands are optional
+ SubcommandsOptional bool
+
+ // Aliases for the command
+ Aliases []string
+
+ // Whether positional arguments are required
+ ArgsRequired bool
+
+ commands []*Command
+ hasBuiltinHelpGroup bool
+ args []*Arg
+}
+
+// Commander is an interface which can be implemented by any command added in
+// the options. When implemented, the Execute method will be called for the last
+// specified (sub)command providing the remaining command line arguments.
+type Commander interface {
+ // Execute will be called for the last active (sub)command. The
+ // args argument contains the remaining command line arguments. The
+ // error that Execute returns will be eventually passed out of the
+ // Parse method of the Parser.
+ Execute(args []string) error
+}
+
+// Usage is an interface which can be implemented to show a custom usage string
+// in the help message shown for a command.
+type Usage interface {
+ // Usage is called for commands to allow customized printing of command
+ // usage in the generated help message.
+ Usage() string
+}
+
+type lookup struct {
+ shortNames map[string]*Option
+ longNames map[string]*Option
+
+ commands map[string]*Command
+}
+
+// AddCommand adds a new command to the parser with the given name and data. The
+// data needs to be a pointer to a struct from which the fields indicate which
+// options are in the command. The provided data can implement the Command and
+// Usage interfaces.
+func (c *Command) AddCommand(command string, shortDescription string, longDescription string, data interface{}) (*Command, error) {
+ cmd := newCommand(command, shortDescription, longDescription, data)
+
+ cmd.parent = c
+
+ if err := cmd.scan(); err != nil {
+ return nil, err
+ }
+
+ c.commands = append(c.commands, cmd)
+ return cmd, nil
+}
+
+// AddGroup adds a new group to the command with the given name and data. The
+// data needs to be a pointer to a struct from which the fields indicate which
+// options are in the group.
+func (c *Command) AddGroup(shortDescription string, longDescription string, data interface{}) (*Group, error) {
+ group := newGroup(shortDescription, longDescription, data)
+
+ group.parent = c
+
+ if err := group.scanType(c.scanSubcommandHandler(group)); err != nil {
+ return nil, err
+ }
+
+ c.groups = append(c.groups, group)
+ return group, nil
+}
+
+// Commands returns a list of subcommands of this command.
+func (c *Command) Commands() []*Command {
+ return c.commands
+}
+
+// Find locates the subcommand with the given name and returns it. If no such
+// command can be found Find will return nil.
+func (c *Command) Find(name string) *Command {
+ for _, cc := range c.commands {
+ if cc.match(name) {
+ return cc
+ }
+ }
+
+ return nil
+}
+
+// FindOptionByLongName finds an option that is part of the command, or any of
+// its parent commands, by matching its long name (including the option
+// namespace).
+func (c *Command) FindOptionByLongName(longName string) (option *Option) {
+ for option == nil && c != nil {
+ option = c.Group.FindOptionByLongName(longName)
+
+ c, _ = c.parent.(*Command)
+ }
+
+ return option
+}
+
+// FindOptionByShortName finds an option that is part of the command, or any of
+// its parent commands, by matching its long name (including the option
+// namespace).
+func (c *Command) FindOptionByShortName(shortName rune) (option *Option) {
+ for option == nil && c != nil {
+ option = c.Group.FindOptionByShortName(shortName)
+
+ c, _ = c.parent.(*Command)
+ }
+
+ return option
+}
+
+// Args returns a list of positional arguments associated with this command.
+func (c *Command) Args() []*Arg {
+ ret := make([]*Arg, len(c.args))
+ copy(ret, c.args)
+
+ return ret
+}
+
+func newCommand(name string, shortDescription string, longDescription string, data interface{}) *Command {
+ return &Command{
+ Group: newGroup(shortDescription, longDescription, data),
+ Name: name,
+ }
+}
+
+func (c *Command) scanSubcommandHandler(parentg *Group) scanHandler {
+ f := func(realval reflect.Value, sfield *reflect.StructField) (bool, error) {
+ mtag := newMultiTag(string(sfield.Tag))
+
+ if err := mtag.Parse(); err != nil {
+ return true, err
+ }
+
+ positional := mtag.Get("positional-args")
+
+ if len(positional) != 0 {
+ stype := realval.Type()
+
+ for i := 0; i < stype.NumField(); i++ {
+ field := stype.Field(i)
+
+ m := newMultiTag((string(field.Tag)))
+
+ if err := m.Parse(); err != nil {
+ return true, err
+ }
+
+ name := m.Get("positional-arg-name")
+
+ if len(name) == 0 {
+ name = field.Name
+ }
+
+ required := -1
+ requiredMaximum := -1
+
+ sreq := m.Get("required")
+
+ if sreq != "" {
+ required = 1
+
+ rng := strings.SplitN(sreq, "-", 2)
+
+ if len(rng) > 1 {
+ if preq, err := strconv.ParseInt(rng[0], 10, 32); err == nil {
+ required = int(preq)
+ }
+
+ if preq, err := strconv.ParseInt(rng[1], 10, 32); err == nil {
+ requiredMaximum = int(preq)
+ }
+ } else {
+ if preq, err := strconv.ParseInt(sreq, 10, 32); err == nil {
+ required = int(preq)
+ }
+ }
+ }
+
+ arg := &Arg{
+ Name: name,
+ Description: m.Get("description"),
+ Required: required,
+ RequiredMaximum: requiredMaximum,
+
+ value: realval.Field(i),
+ tag: m,
+ }
+
+ c.args = append(c.args, arg)
+
+ if len(mtag.Get("required")) != 0 {
+ c.ArgsRequired = true
+ }
+ }
+
+ return true, nil
+ }
+
+ subcommand := mtag.Get("command")
+
+ if len(subcommand) != 0 {
+ ptrval := reflect.NewAt(realval.Type(), unsafe.Pointer(realval.UnsafeAddr()))
+
+ shortDescription := mtag.Get("description")
+ longDescription := mtag.Get("long-description")
+ subcommandsOptional := mtag.Get("subcommands-optional")
+ aliases := mtag.GetMany("alias")
+
+ subc, err := c.AddCommand(subcommand, shortDescription, longDescription, ptrval.Interface())
+ if err != nil {
+ return true, err
+ }
+
+ subc.Hidden = mtag.Get("hidden") != ""
+
+ if len(subcommandsOptional) > 0 {
+ subc.SubcommandsOptional = true
+ }
+
+ if len(aliases) > 0 {
+ subc.Aliases = aliases
+ }
+
+ return true, nil
+ }
+
+ return parentg.scanSubGroupHandler(realval, sfield)
+ }
+
+ return f
+}
+
+func (c *Command) scan() error {
+ return c.scanType(c.scanSubcommandHandler(c.Group))
+}
+
+func (c *Command) eachOption(f func(*Command, *Group, *Option)) {
+ c.eachCommand(func(c *Command) {
+ c.eachGroup(func(g *Group) {
+ for _, option := range g.options {
+ f(c, g, option)
+ }
+ })
+ }, true)
+}
+
+func (c *Command) eachCommand(f func(*Command), recurse bool) {
+ f(c)
+
+ for _, cc := range c.commands {
+ if recurse {
+ cc.eachCommand(f, true)
+ } else {
+ f(cc)
+ }
+ }
+}
+
+func (c *Command) eachActiveGroup(f func(cc *Command, g *Group)) {
+ c.eachGroup(func(g *Group) {
+ f(c, g)
+ })
+
+ if c.Active != nil {
+ c.Active.eachActiveGroup(f)
+ }
+}
+
+func (c *Command) addHelpGroups(showHelp func() error) {
+ if !c.hasBuiltinHelpGroup {
+ c.addHelpGroup(showHelp)
+ c.hasBuiltinHelpGroup = true
+ }
+
+ for _, cc := range c.commands {
+ cc.addHelpGroups(showHelp)
+ }
+}
+
+func (c *Command) makeLookup() lookup {
+ ret := lookup{
+ shortNames: make(map[string]*Option),
+ longNames: make(map[string]*Option),
+ commands: make(map[string]*Command),
+ }
+
+ parent := c.parent
+
+ var parents []*Command
+
+ for parent != nil {
+ if cmd, ok := parent.(*Command); ok {
+ parents = append(parents, cmd)
+ parent = cmd.parent
+ } else {
+ parent = nil
+ }
+ }
+
+ for i := len(parents) - 1; i >= 0; i-- {
+ parents[i].fillLookup(&ret, true)
+ }
+
+ c.fillLookup(&ret, false)
+ return ret
+}
+
+func (c *Command) fillLookup(ret *lookup, onlyOptions bool) {
+ c.eachGroup(func(g *Group) {
+ for _, option := range g.options {
+ if option.ShortName != 0 {
+ ret.shortNames[string(option.ShortName)] = option
+ }
+
+ if len(option.LongName) > 0 {
+ ret.longNames[option.LongNameWithNamespace()] = option
+ }
+ }
+ })
+
+ if onlyOptions {
+ return
+ }
+
+ for _, subcommand := range c.commands {
+ ret.commands[subcommand.Name] = subcommand
+
+ for _, a := range subcommand.Aliases {
+ ret.commands[a] = subcommand
+ }
+ }
+}
+
+func (c *Command) groupByName(name string) *Group {
+ if grp := c.Group.groupByName(name); grp != nil {
+ return grp
+ }
+
+ for _, subc := range c.commands {
+ prefix := subc.Name + "."
+
+ if strings.HasPrefix(name, prefix) {
+ if grp := subc.groupByName(name[len(prefix):]); grp != nil {
+ return grp
+ }
+ } else if name == subc.Name {
+ return subc.Group
+ }
+ }
+
+ return nil
+}
+
+type commandList []*Command
+
+func (c commandList) Less(i, j int) bool {
+ return c[i].Name < c[j].Name
+}
+
+func (c commandList) Len() int {
+ return len(c)
+}
+
+func (c commandList) Swap(i, j int) {
+ c[i], c[j] = c[j], c[i]
+}
+
+func (c *Command) sortedVisibleCommands() []*Command {
+ ret := commandList(c.visibleCommands())
+ sort.Sort(ret)
+
+ return []*Command(ret)
+}
+
+func (c *Command) visibleCommands() []*Command {
+ ret := make([]*Command, 0, len(c.commands))
+
+ for _, cmd := range c.commands {
+ if !cmd.Hidden {
+ ret = append(ret, cmd)
+ }
+ }
+
+ return ret
+}
+
+func (c *Command) match(name string) bool {
+ if c.Name == name {
+ return true
+ }
+
+ for _, v := range c.Aliases {
+ if v == name {
+ return true
+ }
+ }
+
+ return false
+}
+
+func (c *Command) hasCliOptions() bool {
+ ret := false
+
+ c.eachGroup(func(g *Group) {
+ if g.isBuiltinHelp {
+ return
+ }
+
+ for _, opt := range g.options {
+ if opt.canCli() {
+ ret = true
+ }
+ }
+ })
+
+ return ret
+}
+
+func (c *Command) fillParseState(s *parseState) {
+ s.positional = make([]*Arg, len(c.args))
+ copy(s.positional, c.args)
+
+ s.lookup = c.makeLookup()
+ s.command = c
+}
diff --git a/vendor/github.com/jessevdk/go-flags/completion.go b/vendor/github.com/jessevdk/go-flags/completion.go
new file mode 100644
index 0000000..708fa9e
--- /dev/null
+++ b/vendor/github.com/jessevdk/go-flags/completion.go
@@ -0,0 +1,300 @@
+package flags
+
+import (
+ "fmt"
+ "path/filepath"
+ "reflect"
+ "sort"
+ "strings"
+ "unicode/utf8"
+)
+
+// Completion is a type containing information of a completion.
+type Completion struct {
+ // The completed item
+ Item string
+
+ // A description of the completed item (optional)
+ Description string
+}
+
+type completions []Completion
+
+func (c completions) Len() int {
+ return len(c)
+}
+
+func (c completions) Less(i, j int) bool {
+ return c[i].Item < c[j].Item
+}
+
+func (c completions) Swap(i, j int) {
+ c[i], c[j] = c[j], c[i]
+}
+
+// Completer is an interface which can be implemented by types
+// to provide custom command line argument completion.
+type Completer interface {
+ // Complete receives a prefix representing a (partial) value
+ // for its type and should provide a list of possible valid
+ // completions.
+ Complete(match string) []Completion
+}
+
+type completion struct {
+ parser *Parser
+}
+
+// Filename is a string alias which provides filename completion.
+type Filename string
+
+func completionsWithoutDescriptions(items []string) []Completion {
+ ret := make([]Completion, len(items))
+
+ for i, v := range items {
+ ret[i].Item = v
+ }
+
+ return ret
+}
+
+// Complete returns a list of existing files with the given
+// prefix.
+func (f *Filename) Complete(match string) []Completion {
+ ret, _ := filepath.Glob(match + "*")
+ return completionsWithoutDescriptions(ret)
+}
+
+func (c *completion) skipPositional(s *parseState, n int) {
+ if n >= len(s.positional) {
+ s.positional = nil
+ } else {
+ s.positional = s.positional[n:]
+ }
+}
+
+func (c *completion) completeOptionNames(names map[string]*Option, prefix string, match string) []Completion {
+ n := make([]Completion, 0, len(names))
+
+ for k, opt := range names {
+ if strings.HasPrefix(k, match) && !opt.Hidden {
+ n = append(n, Completion{
+ Item: prefix + k,
+ Description: opt.Description,
+ })
+ }
+ }
+
+ return n
+}
+
+func (c *completion) completeLongNames(s *parseState, prefix string, match string) []Completion {
+ return c.completeOptionNames(s.lookup.longNames, prefix, match)
+}
+
+func (c *completion) completeShortNames(s *parseState, prefix string, match string) []Completion {
+ if len(match) != 0 {
+ return []Completion{
+ Completion{
+ Item: prefix + match,
+ },
+ }
+ }
+
+ return c.completeOptionNames(s.lookup.shortNames, prefix, match)
+}
+
+func (c *completion) completeCommands(s *parseState, match string) []Completion {
+ n := make([]Completion, 0, len(s.command.commands))
+
+ for _, cmd := range s.command.commands {
+ if cmd.data != c && strings.HasPrefix(cmd.Name, match) {
+ n = append(n, Completion{
+ Item: cmd.Name,
+ Description: cmd.ShortDescription,
+ })
+ }
+ }
+
+ return n
+}
+
+func (c *completion) completeValue(value reflect.Value, prefix string, match string) []Completion {
+ i := value.Interface()
+
+ var ret []Completion
+
+ if cmp, ok := i.(Completer); ok {
+ ret = cmp.Complete(match)
+ } else if value.CanAddr() {
+ if cmp, ok = value.Addr().Interface().(Completer); ok {
+ ret = cmp.Complete(match)
+ }
+ }
+
+ for i, v := range ret {
+ ret[i].Item = prefix + v.Item
+ }
+
+ return ret
+}
+
+func (c *completion) completeArg(arg *Arg, prefix string, match string) []Completion {
+ if arg.isRemaining() {
+ // For remaining positional args (that are parsed into a slice), complete
+ // based on the element type.
+ return c.completeValue(reflect.New(arg.value.Type().Elem()), prefix, match)
+ }
+
+ return c.completeValue(arg.value, prefix, match)
+}
+
+func (c *completion) complete(args []string) []Completion {
+ if len(args) == 0 {
+ args = []string{""}
+ }
+
+ s := &parseState{
+ args: args,
+ }
+
+ c.parser.fillParseState(s)
+
+ var opt *Option
+
+ for len(s.args) > 1 {
+ arg := s.pop()
+
+ if (c.parser.Options&PassDoubleDash) != None && arg == "--" {
+ opt = nil
+ c.skipPositional(s, len(s.args)-1)
+
+ break
+ }
+
+ if argumentIsOption(arg) {
+ prefix, optname, islong := stripOptionPrefix(arg)
+ optname, _, argument := splitOption(prefix, optname, islong)
+
+ if argument == nil {
+ var o *Option
+ canarg := true
+
+ if islong {
+ o = s.lookup.longNames[optname]
+ } else {
+ for i, r := range optname {
+ sname := string(r)
+ o = s.lookup.shortNames[sname]
+
+ if o == nil {
+ break
+ }
+
+ if i == 0 && o.canArgument() && len(optname) != len(sname) {
+ canarg = false
+ break
+ }
+ }
+ }
+
+ if o == nil && (c.parser.Options&PassAfterNonOption) != None {
+ opt = nil
+ c.skipPositional(s, len(s.args)-1)
+
+ break
+ } else if o != nil && o.canArgument() && !o.OptionalArgument && canarg {
+ if len(s.args) > 1 {
+ s.pop()
+ } else {
+ opt = o
+ }
+ }
+ }
+ } else {
+ if len(s.positional) > 0 {
+ if !s.positional[0].isRemaining() {
+ // Don't advance beyond a remaining positional arg (because
+ // it consumes all subsequent args).
+ s.positional = s.positional[1:]
+ }
+ } else if cmd, ok := s.lookup.commands[arg]; ok {
+ cmd.fillParseState(s)
+ }
+
+ opt = nil
+ }
+ }
+
+ lastarg := s.args[len(s.args)-1]
+ var ret []Completion
+
+ if opt != nil {
+ // Completion for the argument of 'opt'
+ ret = c.completeValue(opt.value, "", lastarg)
+ } else if argumentStartsOption(lastarg) {
+ // Complete the option
+ prefix, optname, islong := stripOptionPrefix(lastarg)
+ optname, split, argument := splitOption(prefix, optname, islong)
+
+ if argument == nil && !islong {
+ rname, n := utf8.DecodeRuneInString(optname)
+ sname := string(rname)
+
+ if opt := s.lookup.shortNames[sname]; opt != nil && opt.canArgument() {
+ ret = c.completeValue(opt.value, prefix+sname, optname[n:])
+ } else {
+ ret = c.completeShortNames(s, prefix, optname)
+ }
+ } else if argument != nil {
+ if islong {
+ opt = s.lookup.longNames[optname]
+ } else {
+ opt = s.lookup.shortNames[optname]
+ }
+
+ if opt != nil {
+ ret = c.completeValue(opt.value, prefix+optname+split, *argument)
+ }
+ } else if islong {
+ ret = c.completeLongNames(s, prefix, optname)
+ } else {
+ ret = c.completeShortNames(s, prefix, optname)
+ }
+ } else if len(s.positional) > 0 {
+ // Complete for positional argument
+ ret = c.completeArg(s.positional[0], "", lastarg)
+ } else if len(s.command.commands) > 0 {
+ // Complete for command
+ ret = c.completeCommands(s, lastarg)
+ }
+
+ sort.Sort(completions(ret))
+ return ret
+}
+
+func (c *completion) print(items []Completion, showDescriptions bool) {
+ if showDescriptions && len(items) > 1 {
+ maxl := 0
+
+ for _, v := range items {
+ if len(v.Item) > maxl {
+ maxl = len(v.Item)
+ }
+ }
+
+ for _, v := range items {
+ fmt.Printf("%s", v.Item)
+
+ if len(v.Description) > 0 {
+ fmt.Printf("%s # %s", strings.Repeat(" ", maxl-len(v.Item)), v.Description)
+ }
+
+ fmt.Printf("\n")
+ }
+ } else {
+ for _, v := range items {
+ fmt.Println(v.Item)
+ }
+ }
+}
diff --git a/vendor/github.com/jessevdk/go-flags/convert.go b/vendor/github.com/jessevdk/go-flags/convert.go
new file mode 100644
index 0000000..938c3ac
--- /dev/null
+++ b/vendor/github.com/jessevdk/go-flags/convert.go
@@ -0,0 +1,341 @@
+// Copyright 2012 Jesse van den Kieboom. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package flags
+
+import (
+ "fmt"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// Marshaler is the interface implemented by types that can marshal themselves
+// to a string representation of the flag.
+type Marshaler interface {
+ // MarshalFlag marshals a flag value to its string representation.
+ MarshalFlag() (string, error)
+}
+
+// Unmarshaler is the interface implemented by types that can unmarshal a flag
+// argument to themselves. The provided value is directly passed from the
+// command line.
+type Unmarshaler interface {
+ // UnmarshalFlag unmarshals a string value representation to the flag
+ // value (which therefore needs to be a pointer receiver).
+ UnmarshalFlag(value string) error
+}
+
+func getBase(options multiTag, base int) (int, error) {
+ sbase := options.Get("base")
+
+ var err error
+ var ivbase int64
+
+ if sbase != "" {
+ ivbase, err = strconv.ParseInt(sbase, 10, 32)
+ base = int(ivbase)
+ }
+
+ return base, err
+}
+
+func convertMarshal(val reflect.Value) (bool, string, error) {
+ // Check first for the Marshaler interface
+ if val.Type().NumMethod() > 0 && val.CanInterface() {
+ if marshaler, ok := val.Interface().(Marshaler); ok {
+ ret, err := marshaler.MarshalFlag()
+ return true, ret, err
+ }
+ }
+
+ return false, "", nil
+}
+
+func convertToString(val reflect.Value, options multiTag) (string, error) {
+ if ok, ret, err := convertMarshal(val); ok {
+ return ret, err
+ }
+
+ tp := val.Type()
+
+ // Support for time.Duration
+ if tp == reflect.TypeOf((*time.Duration)(nil)).Elem() {
+ stringer := val.Interface().(fmt.Stringer)
+ return stringer.String(), nil
+ }
+
+ switch tp.Kind() {
+ case reflect.String:
+ return val.String(), nil
+ case reflect.Bool:
+ if val.Bool() {
+ return "true", nil
+ }
+
+ return "false", nil
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ base, err := getBase(options, 10)
+
+ if err != nil {
+ return "", err
+ }
+
+ return strconv.FormatInt(val.Int(), base), nil
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ base, err := getBase(options, 10)
+
+ if err != nil {
+ return "", err
+ }
+
+ return strconv.FormatUint(val.Uint(), base), nil
+ case reflect.Float32, reflect.Float64:
+ return strconv.FormatFloat(val.Float(), 'g', -1, tp.Bits()), nil
+ case reflect.Slice:
+ if val.Len() == 0 {
+ return "", nil
+ }
+
+ ret := "["
+
+ for i := 0; i < val.Len(); i++ {
+ if i != 0 {
+ ret += ", "
+ }
+
+ item, err := convertToString(val.Index(i), options)
+
+ if err != nil {
+ return "", err
+ }
+
+ ret += item
+ }
+
+ return ret + "]", nil
+ case reflect.Map:
+ ret := "{"
+
+ for i, key := range val.MapKeys() {
+ if i != 0 {
+ ret += ", "
+ }
+
+ keyitem, err := convertToString(key, options)
+
+ if err != nil {
+ return "", err
+ }
+
+ item, err := convertToString(val.MapIndex(key), options)
+
+ if err != nil {
+ return "", err
+ }
+
+ ret += keyitem + ":" + item
+ }
+
+ return ret + "}", nil
+ case reflect.Ptr:
+ return convertToString(reflect.Indirect(val), options)
+ case reflect.Interface:
+ if !val.IsNil() {
+ return convertToString(val.Elem(), options)
+ }
+ }
+
+ return "", nil
+}
+
+func convertUnmarshal(val string, retval reflect.Value) (bool, error) {
+ if retval.Type().NumMethod() > 0 && retval.CanInterface() {
+ if unmarshaler, ok := retval.Interface().(Unmarshaler); ok {
+ return true, unmarshaler.UnmarshalFlag(val)
+ }
+ }
+
+ if retval.Type().Kind() != reflect.Ptr && retval.CanAddr() {
+ return convertUnmarshal(val, retval.Addr())
+ }
+
+ if retval.Type().Kind() == reflect.Interface && !retval.IsNil() {
+ return convertUnmarshal(val, retval.Elem())
+ }
+
+ return false, nil
+}
+
+func convert(val string, retval reflect.Value, options multiTag) error {
+ if ok, err := convertUnmarshal(val, retval); ok {
+ return err
+ }
+
+ tp := retval.Type()
+
+ // Support for time.Duration
+ if tp == reflect.TypeOf((*time.Duration)(nil)).Elem() {
+ parsed, err := time.ParseDuration(val)
+
+ if err != nil {
+ return err
+ }
+
+ retval.SetInt(int64(parsed))
+ return nil
+ }
+
+ switch tp.Kind() {
+ case reflect.String:
+ retval.SetString(val)
+ case reflect.Bool:
+ if val == "" {
+ retval.SetBool(true)
+ } else {
+ b, err := strconv.ParseBool(val)
+
+ if err != nil {
+ return err
+ }
+
+ retval.SetBool(b)
+ }
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ base, err := getBase(options, 10)
+
+ if err != nil {
+ return err
+ }
+
+ parsed, err := strconv.ParseInt(val, base, tp.Bits())
+
+ if err != nil {
+ return err
+ }
+
+ retval.SetInt(parsed)
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ base, err := getBase(options, 10)
+
+ if err != nil {
+ return err
+ }
+
+ parsed, err := strconv.ParseUint(val, base, tp.Bits())
+
+ if err != nil {
+ return err
+ }
+
+ retval.SetUint(parsed)
+ case reflect.Float32, reflect.Float64:
+ parsed, err := strconv.ParseFloat(val, tp.Bits())
+
+ if err != nil {
+ return err
+ }
+
+ retval.SetFloat(parsed)
+ case reflect.Slice:
+ elemtp := tp.Elem()
+
+ elemvalptr := reflect.New(elemtp)
+ elemval := reflect.Indirect(elemvalptr)
+
+ if err := convert(val, elemval, options); err != nil {
+ return err
+ }
+
+ retval.Set(reflect.Append(retval, elemval))
+ case reflect.Map:
+ parts := strings.SplitN(val, ":", 2)
+
+ key := parts[0]
+ var value string
+
+ if len(parts) == 2 {
+ value = parts[1]
+ }
+
+ keytp := tp.Key()
+ keyval := reflect.New(keytp)
+
+ if err := convert(key, keyval, options); err != nil {
+ return err
+ }
+
+ valuetp := tp.Elem()
+ valueval := reflect.New(valuetp)
+
+ if err := convert(value, valueval, options); err != nil {
+ return err
+ }
+
+ if retval.IsNil() {
+ retval.Set(reflect.MakeMap(tp))
+ }
+
+ retval.SetMapIndex(reflect.Indirect(keyval), reflect.Indirect(valueval))
+ case reflect.Ptr:
+ if retval.IsNil() {
+ retval.Set(reflect.New(retval.Type().Elem()))
+ }
+
+ return convert(val, reflect.Indirect(retval), options)
+ case reflect.Interface:
+ if !retval.IsNil() {
+ return convert(val, retval.Elem(), options)
+ }
+ }
+
+ return nil
+}
+
+func isPrint(s string) bool {
+ for _, c := range s {
+ if !strconv.IsPrint(c) {
+ return false
+ }
+ }
+
+ return true
+}
+
+func quoteIfNeeded(s string) string {
+ if !isPrint(s) {
+ return strconv.Quote(s)
+ }
+
+ return s
+}
+
+func quoteIfNeededV(s []string) []string {
+ ret := make([]string, len(s))
+
+ for i, v := range s {
+ ret[i] = quoteIfNeeded(v)
+ }
+
+ return ret
+}
+
+func quoteV(s []string) []string {
+ ret := make([]string, len(s))
+
+ for i, v := range s {
+ ret[i] = strconv.Quote(v)
+ }
+
+ return ret
+}
+
+func unquoteIfPossible(s string) (string, error) {
+ if len(s) == 0 || s[0] != '"' {
+ return s, nil
+ }
+
+ return strconv.Unquote(s)
+}
diff --git a/vendor/github.com/jessevdk/go-flags/error.go b/vendor/github.com/jessevdk/go-flags/error.go
new file mode 100644
index 0000000..05528d8
--- /dev/null
+++ b/vendor/github.com/jessevdk/go-flags/error.go
@@ -0,0 +1,134 @@
+package flags
+
+import (
+ "fmt"
+)
+
+// ErrorType represents the type of error.
+type ErrorType uint
+
+const (
+ // ErrUnknown indicates a generic error.
+ ErrUnknown ErrorType = iota
+
+ // ErrExpectedArgument indicates that an argument was expected.
+ ErrExpectedArgument
+
+ // ErrUnknownFlag indicates an unknown flag.
+ ErrUnknownFlag
+
+ // ErrUnknownGroup indicates an unknown group.
+ ErrUnknownGroup
+
+ // ErrMarshal indicates a marshalling error while converting values.
+ ErrMarshal
+
+ // ErrHelp indicates that the built-in help was shown (the error
+ // contains the help message).
+ ErrHelp
+
+ // ErrNoArgumentForBool indicates that an argument was given for a
+ // boolean flag (which don't not take any arguments).
+ ErrNoArgumentForBool
+
+ // ErrRequired indicates that a required flag was not provided.
+ ErrRequired
+
+ // ErrShortNameTooLong indicates that a short flag name was specified,
+ // longer than one character.
+ ErrShortNameTooLong
+
+ // ErrDuplicatedFlag indicates that a short or long flag has been
+ // defined more than once
+ ErrDuplicatedFlag
+
+ // ErrTag indicates an error while parsing flag tags.
+ ErrTag
+
+ // ErrCommandRequired indicates that a command was required but not
+ // specified
+ ErrCommandRequired
+
+ // ErrUnknownCommand indicates that an unknown command was specified.
+ ErrUnknownCommand
+
+ // ErrInvalidChoice indicates an invalid option value which only allows
+ // a certain number of choices.
+ ErrInvalidChoice
+
+ // ErrInvalidTag indicates an invalid tag or invalid use of an existing tag
+ ErrInvalidTag
+)
+
+func (e ErrorType) String() string {
+ switch e {
+ case ErrUnknown:
+ return "unknown"
+ case ErrExpectedArgument:
+ return "expected argument"
+ case ErrUnknownFlag:
+ return "unknown flag"
+ case ErrUnknownGroup:
+ return "unknown group"
+ case ErrMarshal:
+ return "marshal"
+ case ErrHelp:
+ return "help"
+ case ErrNoArgumentForBool:
+ return "no argument for bool"
+ case ErrRequired:
+ return "required"
+ case ErrShortNameTooLong:
+ return "short name too long"
+ case ErrDuplicatedFlag:
+ return "duplicated flag"
+ case ErrTag:
+ return "tag"
+ case ErrCommandRequired:
+ return "command required"
+ case ErrUnknownCommand:
+ return "unknown command"
+ case ErrInvalidChoice:
+ return "invalid choice"
+ case ErrInvalidTag:
+ return "invalid tag"
+ }
+
+ return "unrecognized error type"
+}
+
+// Error represents a parser error. The error returned from Parse is of this
+// type. The error contains both a Type and Message.
+type Error struct {
+ // The type of error
+ Type ErrorType
+
+ // The error message
+ Message string
+}
+
+// Error returns the error's message
+func (e *Error) Error() string {
+ return e.Message
+}
+
+func newError(tp ErrorType, message string) *Error {
+ return &Error{
+ Type: tp,
+ Message: message,
+ }
+}
+
+func newErrorf(tp ErrorType, format string, args ...interface{}) *Error {
+ return newError(tp, fmt.Sprintf(format, args...))
+}
+
+func wrapError(err error) *Error {
+ ret, ok := err.(*Error)
+
+ if !ok {
+ return newError(ErrUnknown, err.Error())
+ }
+
+ return ret
+}
diff --git a/vendor/github.com/jessevdk/go-flags/examples/add.go b/vendor/github.com/jessevdk/go-flags/examples/add.go
new file mode 100644
index 0000000..57d8f23
--- /dev/null
+++ b/vendor/github.com/jessevdk/go-flags/examples/add.go
@@ -0,0 +1,23 @@
+package main
+
+import (
+ "fmt"
+)
+
+type AddCommand struct {
+ All bool `short:"a" long:"all" description:"Add all files"`
+}
+
+var addCommand AddCommand
+
+func (x *AddCommand) Execute(args []string) error {
+ fmt.Printf("Adding (all=%v): %#v\n", x.All, args)
+ return nil
+}
+
+func init() {
+ parser.AddCommand("add",
+ "Add a file",
+ "The add command adds a file to the repository. Use -a to add all files.",
+ &addCommand)
+}
diff --git a/vendor/github.com/jessevdk/go-flags/examples/main.go b/vendor/github.com/jessevdk/go-flags/examples/main.go
new file mode 100644
index 0000000..632c331
--- /dev/null
+++ b/vendor/github.com/jessevdk/go-flags/examples/main.go
@@ -0,0 +1,79 @@
+package main
+
+import (
+ "errors"
+ "fmt"
+ "github.com/jessevdk/go-flags"
+ "os"
+ "strconv"
+ "strings"
+)
+
+type EditorOptions struct {
+ Input flags.Filename `short:"i" long:"input" description:"Input file" default:"-"`
+ Output flags.Filename `short:"o" long:"output" description:"Output file" default:"-"`
+}
+
+type Point struct {
+ X, Y int
+}
+
+func (p *Point) UnmarshalFlag(value string) error {
+ parts := strings.Split(value, ",")
+
+ if len(parts) != 2 {
+ return errors.New("expected two numbers separated by a ,")
+ }
+
+ x, err := strconv.ParseInt(parts[0], 10, 32)
+
+ if err != nil {
+ return err
+ }
+
+ y, err := strconv.ParseInt(parts[1], 10, 32)
+
+ if err != nil {
+ return err
+ }
+
+ p.X = int(x)
+ p.Y = int(y)
+
+ return nil
+}
+
+func (p Point) MarshalFlag() (string, error) {
+ return fmt.Sprintf("%d,%d", p.X, p.Y), nil
+}
+
+type Options struct {
+ // Example of verbosity with level
+ Verbose []bool `short:"v" long:"verbose" description:"Verbose output"`
+
+ // Example of optional value
+ User string `short:"u" long:"user" description:"User name" optional:"yes" optional-value:"pancake"`
+
+ // Example of map with multiple default values
+ Users map[string]string `long:"users" description:"User e-mail map" default:"system:system@example.org" default:"admin:admin@example.org"`
+
+ // Example of option group
+ Editor EditorOptions `group:"Editor Options"`
+
+ // Example of custom type Marshal/Unmarshal
+ Point Point `long:"point" description:"A x,y point" default:"1,2"`
+}
+
+var options Options
+
+var parser = flags.NewParser(&options, flags.Default)
+
+func main() {
+ if _, err := parser.Parse(); err != nil {
+ if flagsErr, ok := err.(*flags.Error); ok && flagsErr.Type == flags.ErrHelp {
+ os.Exit(0)
+ } else {
+ os.Exit(1)
+ }
+ }
+}
diff --git a/vendor/github.com/jessevdk/go-flags/examples/rm.go b/vendor/github.com/jessevdk/go-flags/examples/rm.go
new file mode 100644
index 0000000..c9c1dd0
--- /dev/null
+++ b/vendor/github.com/jessevdk/go-flags/examples/rm.go
@@ -0,0 +1,23 @@
+package main
+
+import (
+ "fmt"
+)
+
+type RmCommand struct {
+ Force bool `short:"f" long:"force" description:"Force removal of files"`
+}
+
+var rmCommand RmCommand
+
+func (x *RmCommand) Execute(args []string) error {
+ fmt.Printf("Removing (force=%v): %#v\n", x.Force, args)
+ return nil
+}
+
+func init() {
+ parser.AddCommand("rm",
+ "Remove a file",
+ "The rm command removes a file to the repository. Use -f to force removal of files.",
+ &rmCommand)
+}
diff --git a/vendor/github.com/jessevdk/go-flags/flags.go b/vendor/github.com/jessevdk/go-flags/flags.go
new file mode 100644
index 0000000..8144f4f
--- /dev/null
+++ b/vendor/github.com/jessevdk/go-flags/flags.go
@@ -0,0 +1,258 @@
+// Copyright 2012 Jesse van den Kieboom. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+Package flags provides an extensive command line option parser.
+The flags package is similar in functionality to the go built-in flag package
+but provides more options and uses reflection to provide a convenient and
+succinct way of specifying command line options.
+
+
+Supported features
+
+The following features are supported in go-flags:
+
+ Options with short names (-v)
+ Options with long names (--verbose)
+ Options with and without arguments (bool v.s. other type)
+ Options with optional arguments and default values
+ Option default values from ENVIRONMENT_VARIABLES, including slice and map values
+ Multiple option groups each containing a set of options
+ Generate and print well-formatted help message
+ Passing remaining command line arguments after -- (optional)
+ Ignoring unknown command line options (optional)
+ Supports -I/usr/include -I=/usr/include -I /usr/include option argument specification
+ Supports multiple short options -aux
+ Supports all primitive go types (string, int{8..64}, uint{8..64}, float)
+ Supports same option multiple times (can store in slice or last option counts)
+ Supports maps
+ Supports function callbacks
+ Supports namespaces for (nested) option groups
+
+Additional features specific to Windows:
+ Options with short names (/v)
+ Options with long names (/verbose)
+ Windows-style options with arguments use a colon as the delimiter
+ Modify generated help message with Windows-style / options
+ Windows style options can be disabled at build time using the "forceposix"
+ build tag
+
+
+Basic usage
+
+The flags package uses structs, reflection and struct field tags
+to allow users to specify command line options. This results in very simple
+and concise specification of your application options. For example:
+
+ type Options struct {
+ Verbose []bool `short:"v" long:"verbose" description:"Show verbose debug information"`
+ }
+
+This specifies one option with a short name -v and a long name --verbose.
+When either -v or --verbose is found on the command line, a 'true' value
+will be appended to the Verbose field. e.g. when specifying -vvv, the
+resulting value of Verbose will be {[true, true, true]}.
+
+Slice options work exactly the same as primitive type options, except that
+whenever the option is encountered, a value is appended to the slice.
+
+Map options from string to primitive type are also supported. On the command
+line, you specify the value for such an option as key:value. For example
+
+ type Options struct {
+ AuthorInfo string[string] `short:"a"`
+ }
+
+Then, the AuthorInfo map can be filled with something like
+-a name:Jesse -a "surname:van den Kieboom".
+
+Finally, for full control over the conversion between command line argument
+values and options, user defined types can choose to implement the Marshaler
+and Unmarshaler interfaces.
+
+
+Available field tags
+
+The following is a list of tags for struct fields supported by go-flags:
+
+ short: the short name of the option (single character)
+ long: the long name of the option
+ required: whether an option is required to appear on the command
+ line. If a required option is not present, the parser will
+ return ErrRequired (optional)
+ description: the description of the option (optional)
+ long-description: the long description of the option. Currently only
+ displayed in generated man pages (optional)
+ no-flag: if non-empty this field is ignored as an option (optional)
+
+ optional: whether an argument of the option is optional. When an
+ argument is optional it can only be specified using
+ --option=argument (optional)
+ optional-value: the value of an optional option when the option occurs
+ without an argument. This tag can be specified multiple
+ times in the case of maps or slices (optional)
+ default: the default value of an option. This tag can be specified
+ multiple times in the case of slices or maps (optional)
+ default-mask: when specified, this value will be displayed in the help
+ instead of the actual default value. This is useful
+ mostly for hiding otherwise sensitive information from
+ showing up in the help. If default-mask takes the special
+ value "-", then no default value will be shown at all
+ (optional)
+ env: the default value of the option is overridden from the
+ specified environment variable, if one has been defined.
+ (optional)
+ env-delim: the 'env' default value from environment is split into
+ multiple values with the given delimiter string, use with
+ slices and maps (optional)
+ value-name: the name of the argument value (to be shown in the help)
+ (optional)
+ choice: limits the values for an option to a set of values.
+ This tag can be specified multiple times (optional)
+ hidden: the option is not visible in the help or man page.
+
+ base: a base (radix) used to convert strings to integer values, the
+ default base is 10 (i.e. decimal) (optional)
+
+ ini-name: the explicit ini option name (optional)
+ no-ini: if non-empty this field is ignored as an ini option
+ (optional)
+
+ group: when specified on a struct field, makes the struct
+ field a separate group with the given name (optional)
+ namespace: when specified on a group struct field, the namespace
+ gets prepended to every option's long name and
+ subgroup's namespace of this group, separated by
+ the parser's namespace delimiter (optional)
+ command: when specified on a struct field, makes the struct
+ field a (sub)command with the given name (optional)
+ subcommands-optional: when specified on a command struct field, makes
+ any subcommands of that command optional (optional)
+ alias: when specified on a command struct field, adds the
+ specified name as an alias for the command. Can be
+ be specified multiple times to add more than one
+ alias (optional)
+ positional-args: when specified on a field with a struct type,
+ uses the fields of that struct to parse remaining
+ positional command line arguments into (in order
+ of the fields). If a field has a slice type,
+ then all remaining arguments will be added to it.
+ Positional arguments are optional by default,
+ unless the "required" tag is specified together
+ with the "positional-args" tag. The "required" tag
+ can also be set on the individual rest argument
+ fields, to require only the first N positional
+ arguments. If the "required" tag is set on the
+ rest arguments slice, then its value determines
+ the minimum amount of rest arguments that needs to
+ be provided (e.g. `required:"2"`) (optional)
+ positional-arg-name: used on a field in a positional argument struct; name
+ of the positional argument placeholder to be shown in
+ the help (optional)
+
+Either the `short:` tag or the `long:` must be specified to make the field eligible as an
+option.
+
+
+Option groups
+
+Option groups are a simple way to semantically separate your options. All
+options in a particular group are shown together in the help under the name
+of the group. Namespaces can be used to specify option long names more
+precisely and emphasize the options affiliation to their group.
+
+There are currently three ways to specify option groups.
+
+ 1. Use NewNamedParser specifying the various option groups.
+ 2. Use AddGroup to add a group to an existing parser.
+ 3. Add a struct field to the top-level options annotated with the
+ group:"group-name" tag.
+
+
+
+Commands
+
+The flags package also has basic support for commands. Commands are often
+used in monolithic applications that support various commands or actions.
+Take git for example, all of the add, commit, checkout, etc. are called
+commands. Using commands you can easily separate multiple functions of your
+application.
+
+There are currently two ways to specify a command.
+
+ 1. Use AddCommand on an existing parser.
+ 2. Add a struct field to your options struct annotated with the
+ command:"command-name" tag.
+
+The most common, idiomatic way to implement commands is to define a global
+parser instance and implement each command in a separate file. These
+command files should define a go init function which calls AddCommand on
+the global parser.
+
+When parsing ends and there is an active command and that command implements
+the Commander interface, then its Execute method will be run with the
+remaining command line arguments.
+
+Command structs can have options which become valid to parse after the
+command has been specified on the command line, in addition to the options
+of all the parent commands. I.e. considering a -v flag on the parser and an
+add command, the following are equivalent:
+
+ ./app -v add
+ ./app add -v
+
+However, if the -v flag is defined on the add command, then the first of
+the two examples above would fail since the -v flag is not defined before
+the add command.
+
+
+Completion
+
+go-flags has builtin support to provide bash completion of flags, commands
+and argument values. To use completion, the binary which uses go-flags
+can be invoked in a special environment to list completion of the current
+command line argument. It should be noted that this `executes` your application,
+and it is up to the user to make sure there are no negative side effects (for
+example from init functions).
+
+Setting the environment variable `GO_FLAGS_COMPLETION=1` enables completion
+by replacing the argument parsing routine with the completion routine which
+outputs completions for the passed arguments. The basic invocation to
+complete a set of arguments is therefore:
+
+ GO_FLAGS_COMPLETION=1 ./completion-example arg1 arg2 arg3
+
+where `completion-example` is the binary, `arg1` and `arg2` are
+the current arguments, and `arg3` (the last argument) is the argument
+to be completed. If the GO_FLAGS_COMPLETION is set to "verbose", then
+descriptions of possible completion items will also be shown, if there
+are more than 1 completion items.
+
+To use this with bash completion, a simple file can be written which
+calls the binary which supports go-flags completion:
+
+ _completion_example() {
+ # All arguments except the first one
+ args=("${COMP_WORDS[@]:1:$COMP_CWORD}")
+
+ # Only split on newlines
+ local IFS=$'\n'
+
+ # Call completion (note that the first element of COMP_WORDS is
+ # the executable itself)
+ COMPREPLY=($(GO_FLAGS_COMPLETION=1 ${COMP_WORDS[0]} "${args[@]}"))
+ return 0
+ }
+
+ complete -F _completion_example completion-example
+
+Completion requires the parser option PassDoubleDash and is therefore enforced if the environment variable GO_FLAGS_COMPLETION is set.
+
+Customized completion for argument values is supported by implementing
+the flags.Completer interface for the argument value type. An example
+of a type which does so is the flags.Filename type, an alias of string
+allowing simple filename completion. A slice or array argument value
+whose element type implements flags.Completer will also be completed.
+*/
+package flags
diff --git a/vendor/github.com/jessevdk/go-flags/group.go b/vendor/github.com/jessevdk/go-flags/group.go
new file mode 100644
index 0000000..b1ef6ed
--- /dev/null
+++ b/vendor/github.com/jessevdk/go-flags/group.go
@@ -0,0 +1,385 @@
+// Copyright 2012 Jesse van den Kieboom. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package flags
+
+import (
+ "errors"
+ "reflect"
+ "strings"
+ "unicode/utf8"
+ "unsafe"
+)
+
+// ErrNotPointerToStruct indicates that a provided data container is not
+// a pointer to a struct. Only pointers to structs are valid data containers
+// for options.
+var ErrNotPointerToStruct = errors.New("provided data is not a pointer to struct")
+
+// Group represents an option group. Option groups can be used to logically
+// group options together under a description. Groups are only used to provide
+// more structure to options both for the user (as displayed in the help message)
+// and for you, since groups can be nested.
+type Group struct {
+ // A short description of the group. The
+ // short description is primarily used in the built-in generated help
+ // message
+ ShortDescription string
+
+ // A long description of the group. The long
+ // description is primarily used to present information on commands
+ // (Command embeds Group) in the built-in generated help and man pages.
+ LongDescription string
+
+ // The namespace of the group
+ Namespace string
+
+ // If true, the group is not displayed in the help or man page
+ Hidden bool
+
+ // The parent of the group or nil if it has no parent
+ parent interface{}
+
+ // All the options in the group
+ options []*Option
+
+ // All the subgroups
+ groups []*Group
+
+ // Whether the group represents the built-in help group
+ isBuiltinHelp bool
+
+ data interface{}
+}
+
+type scanHandler func(reflect.Value, *reflect.StructField) (bool, error)
+
+// AddGroup adds a new group to the command with the given name and data. The
+// data needs to be a pointer to a struct from which the fields indicate which
+// options are in the group.
+func (g *Group) AddGroup(shortDescription string, longDescription string, data interface{}) (*Group, error) {
+ group := newGroup(shortDescription, longDescription, data)
+
+ group.parent = g
+
+ if err := group.scan(); err != nil {
+ return nil, err
+ }
+
+ g.groups = append(g.groups, group)
+ return group, nil
+}
+
+// Groups returns the list of groups embedded in this group.
+func (g *Group) Groups() []*Group {
+ return g.groups
+}
+
+// Options returns the list of options in this group.
+func (g *Group) Options() []*Option {
+ return g.options
+}
+
+// Find locates the subgroup with the given short description and returns it.
+// If no such group can be found Find will return nil. Note that the description
+// is matched case insensitively.
+func (g *Group) Find(shortDescription string) *Group {
+ lshortDescription := strings.ToLower(shortDescription)
+
+ var ret *Group
+
+ g.eachGroup(func(gg *Group) {
+ if gg != g && strings.ToLower(gg.ShortDescription) == lshortDescription {
+ ret = gg
+ }
+ })
+
+ return ret
+}
+
+func (g *Group) findOption(matcher func(*Option) bool) (option *Option) {
+ g.eachGroup(func(g *Group) {
+ for _, opt := range g.options {
+ if option == nil && matcher(opt) {
+ option = opt
+ }
+ }
+ })
+
+ return option
+}
+
+// FindOptionByLongName finds an option that is part of the group, or any of its
+// subgroups, by matching its long name (including the option namespace).
+func (g *Group) FindOptionByLongName(longName string) *Option {
+ return g.findOption(func(option *Option) bool {
+ return option.LongNameWithNamespace() == longName
+ })
+}
+
+// FindOptionByShortName finds an option that is part of the group, or any of
+// its subgroups, by matching its short name.
+func (g *Group) FindOptionByShortName(shortName rune) *Option {
+ return g.findOption(func(option *Option) bool {
+ return option.ShortName == shortName
+ })
+}
+
+func newGroup(shortDescription string, longDescription string, data interface{}) *Group {
+ return &Group{
+ ShortDescription: shortDescription,
+ LongDescription: longDescription,
+
+ data: data,
+ }
+}
+
+func (g *Group) optionByName(name string, namematch func(*Option, string) bool) *Option {
+ prio := 0
+ var retopt *Option
+
+ g.eachGroup(func(g *Group) {
+ for _, opt := range g.options {
+ if namematch != nil && namematch(opt, name) && prio < 4 {
+ retopt = opt
+ prio = 4
+ }
+
+ if name == opt.field.Name && prio < 3 {
+ retopt = opt
+ prio = 3
+ }
+
+ if name == opt.LongNameWithNamespace() && prio < 2 {
+ retopt = opt
+ prio = 2
+ }
+
+ if opt.ShortName != 0 && name == string(opt.ShortName) && prio < 1 {
+ retopt = opt
+ prio = 1
+ }
+ }
+ })
+
+ return retopt
+}
+
+func (g *Group) eachGroup(f func(*Group)) {
+ f(g)
+
+ for _, gg := range g.groups {
+ gg.eachGroup(f)
+ }
+}
+
+func (g *Group) scanStruct(realval reflect.Value, sfield *reflect.StructField, handler scanHandler) error {
+ stype := realval.Type()
+
+ if sfield != nil {
+ if ok, err := handler(realval, sfield); err != nil {
+ return err
+ } else if ok {
+ return nil
+ }
+ }
+
+ for i := 0; i < stype.NumField(); i++ {
+ field := stype.Field(i)
+
+ // PkgName is set only for non-exported fields, which we ignore
+ if field.PkgPath != "" && !field.Anonymous {
+ continue
+ }
+
+ mtag := newMultiTag(string(field.Tag))
+
+ if err := mtag.Parse(); err != nil {
+ return err
+ }
+
+ // Skip fields with the no-flag tag
+ if mtag.Get("no-flag") != "" {
+ continue
+ }
+
+ // Dive deep into structs or pointers to structs
+ kind := field.Type.Kind()
+ fld := realval.Field(i)
+
+ if kind == reflect.Struct {
+ if err := g.scanStruct(fld, &field, handler); err != nil {
+ return err
+ }
+ } else if kind == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct {
+ if fld.IsNil() {
+ fld.Set(reflect.New(fld.Type().Elem()))
+ }
+
+ if err := g.scanStruct(reflect.Indirect(fld), &field, handler); err != nil {
+ return err
+ }
+ }
+
+ longname := mtag.Get("long")
+ shortname := mtag.Get("short")
+
+ // Need at least either a short or long name
+ if longname == "" && shortname == "" && mtag.Get("ini-name") == "" {
+ continue
+ }
+
+ short := rune(0)
+ rc := utf8.RuneCountInString(shortname)
+
+ if rc > 1 {
+ return newErrorf(ErrShortNameTooLong,
+ "short names can only be 1 character long, not `%s'",
+ shortname)
+
+ } else if rc == 1 {
+ short, _ = utf8.DecodeRuneInString(shortname)
+ }
+
+ description := mtag.Get("description")
+ def := mtag.GetMany("default")
+
+ optionalValue := mtag.GetMany("optional-value")
+ valueName := mtag.Get("value-name")
+ defaultMask := mtag.Get("default-mask")
+
+ optional := (mtag.Get("optional") != "")
+ required := (mtag.Get("required") != "")
+ choices := mtag.GetMany("choice")
+ hidden := (mtag.Get("hidden") != "")
+
+ option := &Option{
+ Description: description,
+ ShortName: short,
+ LongName: longname,
+ Default: def,
+ EnvDefaultKey: mtag.Get("env"),
+ EnvDefaultDelim: mtag.Get("env-delim"),
+ OptionalArgument: optional,
+ OptionalValue: optionalValue,
+ Required: required,
+ ValueName: valueName,
+ DefaultMask: defaultMask,
+ Choices: choices,
+ Hidden: hidden,
+
+ group: g,
+
+ field: field,
+ value: realval.Field(i),
+ tag: mtag,
+ }
+
+ if option.isBool() && option.Default != nil {
+ return newErrorf(ErrInvalidTag,
+ "boolean flag `%s' may not have default values, they always default to `false' and can only be turned on",
+ option.shortAndLongName())
+ }
+
+ g.options = append(g.options, option)
+ }
+
+ return nil
+}
+
+func (g *Group) checkForDuplicateFlags() *Error {
+ shortNames := make(map[rune]*Option)
+ longNames := make(map[string]*Option)
+
+ var duplicateError *Error
+
+ g.eachGroup(func(g *Group) {
+ for _, option := range g.options {
+ if option.LongName != "" {
+ longName := option.LongNameWithNamespace()
+
+ if otherOption, ok := longNames[longName]; ok {
+ duplicateError = newErrorf(ErrDuplicatedFlag, "option `%s' uses the same long name as option `%s'", option, otherOption)
+ return
+ }
+ longNames[longName] = option
+ }
+ if option.ShortName != 0 {
+ if otherOption, ok := shortNames[option.ShortName]; ok {
+ duplicateError = newErrorf(ErrDuplicatedFlag, "option `%s' uses the same short name as option `%s'", option, otherOption)
+ return
+ }
+ shortNames[option.ShortName] = option
+ }
+ }
+ })
+
+ return duplicateError
+}
+
+func (g *Group) scanSubGroupHandler(realval reflect.Value, sfield *reflect.StructField) (bool, error) {
+ mtag := newMultiTag(string(sfield.Tag))
+
+ if err := mtag.Parse(); err != nil {
+ return true, err
+ }
+
+ subgroup := mtag.Get("group")
+
+ if len(subgroup) != 0 {
+ ptrval := reflect.NewAt(realval.Type(), unsafe.Pointer(realval.UnsafeAddr()))
+ description := mtag.Get("description")
+
+ group, err := g.AddGroup(subgroup, description, ptrval.Interface())
+ if err != nil {
+ return true, err
+ }
+
+ group.Namespace = mtag.Get("namespace")
+ group.Hidden = mtag.Get("hidden") != ""
+
+ return true, nil
+ }
+
+ return false, nil
+}
+
+func (g *Group) scanType(handler scanHandler) error {
+ // Get all the public fields in the data struct
+ ptrval := reflect.ValueOf(g.data)
+
+ if ptrval.Type().Kind() != reflect.Ptr {
+ panic(ErrNotPointerToStruct)
+ }
+
+ stype := ptrval.Type().Elem()
+
+ if stype.Kind() != reflect.Struct {
+ panic(ErrNotPointerToStruct)
+ }
+
+ realval := reflect.Indirect(ptrval)
+
+ if err := g.scanStruct(realval, nil, handler); err != nil {
+ return err
+ }
+
+ if err := g.checkForDuplicateFlags(); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (g *Group) scan() error {
+ return g.scanType(g.scanSubGroupHandler)
+}
+
+func (g *Group) groupByName(name string) *Group {
+ if len(name) == 0 {
+ return g
+ }
+
+ return g.Find(name)
+}
diff --git a/vendor/github.com/jessevdk/go-flags/help.go b/vendor/github.com/jessevdk/go-flags/help.go
new file mode 100644
index 0000000..1b0c2c6
--- /dev/null
+++ b/vendor/github.com/jessevdk/go-flags/help.go
@@ -0,0 +1,485 @@
+// Copyright 2012 Jesse van den Kieboom. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package flags
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io"
+ "runtime"
+ "strings"
+ "unicode/utf8"
+)
+
+type alignmentInfo struct {
+ maxLongLen int
+ hasShort bool
+ hasValueName bool
+ terminalColumns int
+ indent bool
+}
+
+const (
+ paddingBeforeOption = 2
+ distanceBetweenOptionAndDescription = 2
+)
+
+func (a *alignmentInfo) descriptionStart() int {
+ ret := a.maxLongLen + distanceBetweenOptionAndDescription
+
+ if a.hasShort {
+ ret += 2
+ }
+
+ if a.maxLongLen > 0 {
+ ret += 4
+ }
+
+ if a.hasValueName {
+ ret += 3
+ }
+
+ return ret
+}
+
+func (a *alignmentInfo) updateLen(name string, indent bool) {
+ l := utf8.RuneCountInString(name)
+
+ if indent {
+ l = l + 4
+ }
+
+ if l > a.maxLongLen {
+ a.maxLongLen = l
+ }
+}
+
+func (p *Parser) getAlignmentInfo() alignmentInfo {
+ ret := alignmentInfo{
+ maxLongLen: 0,
+ hasShort: false,
+ hasValueName: false,
+ terminalColumns: getTerminalColumns(),
+ }
+
+ if ret.terminalColumns <= 0 {
+ ret.terminalColumns = 80
+ }
+
+ var prevcmd *Command
+
+ p.eachActiveGroup(func(c *Command, grp *Group) {
+ if c != prevcmd {
+ for _, arg := range c.args {
+ ret.updateLen(arg.Name, c != p.Command)
+ }
+ }
+
+ for _, info := range grp.options {
+ if !info.canCli() {
+ continue
+ }
+
+ if info.ShortName != 0 {
+ ret.hasShort = true
+ }
+
+ if len(info.ValueName) > 0 {
+ ret.hasValueName = true
+ }
+
+ l := info.LongNameWithNamespace() + info.ValueName
+
+ if len(info.Choices) != 0 {
+ l += "[" + strings.Join(info.Choices, "|") + "]"
+ }
+
+ ret.updateLen(l, c != p.Command)
+ }
+ })
+
+ return ret
+}
+
+func wrapText(s string, l int, prefix string) string {
+ var ret string
+
+ // Basic text wrapping of s at spaces to fit in l
+ lines := strings.Split(s, "\n")
+
+ for _, line := range lines {
+ var retline string
+
+ line = strings.TrimSpace(line)
+
+ for len(line) > l {
+ // Try to split on space
+ suffix := ""
+
+ pos := strings.LastIndex(line[:l], " ")
+
+ if pos < 0 {
+ pos = l - 1
+ suffix = "-\n"
+ }
+
+ if len(retline) != 0 {
+ retline += "\n" + prefix
+ }
+
+ retline += strings.TrimSpace(line[:pos]) + suffix
+ line = strings.TrimSpace(line[pos:])
+ }
+
+ if len(line) > 0 {
+ if len(retline) != 0 {
+ retline += "\n" + prefix
+ }
+
+ retline += line
+ }
+
+ if len(ret) > 0 {
+ ret += "\n"
+
+ if len(retline) > 0 {
+ ret += prefix
+ }
+ }
+
+ ret += retline
+ }
+
+ return ret
+}
+
+func (p *Parser) writeHelpOption(writer *bufio.Writer, option *Option, info alignmentInfo) {
+ line := &bytes.Buffer{}
+
+ prefix := paddingBeforeOption
+
+ if info.indent {
+ prefix += 4
+ }
+
+ if option.Hidden {
+ return
+ }
+
+ line.WriteString(strings.Repeat(" ", prefix))
+
+ if option.ShortName != 0 {
+ line.WriteRune(defaultShortOptDelimiter)
+ line.WriteRune(option.ShortName)
+ } else if info.hasShort {
+ line.WriteString(" ")
+ }
+
+ descstart := info.descriptionStart() + paddingBeforeOption
+
+ if len(option.LongName) > 0 {
+ if option.ShortName != 0 {
+ line.WriteString(", ")
+ } else if info.hasShort {
+ line.WriteString(" ")
+ }
+
+ line.WriteString(defaultLongOptDelimiter)
+ line.WriteString(option.LongNameWithNamespace())
+ }
+
+ if option.canArgument() {
+ line.WriteRune(defaultNameArgDelimiter)
+
+ if len(option.ValueName) > 0 {
+ line.WriteString(option.ValueName)
+ }
+
+ if len(option.Choices) > 0 {
+ line.WriteString("[" + strings.Join(option.Choices, "|") + "]")
+ }
+ }
+
+ written := line.Len()
+ line.WriteTo(writer)
+
+ if option.Description != "" {
+ dw := descstart - written
+ writer.WriteString(strings.Repeat(" ", dw))
+
+ var def string
+
+ if len(option.DefaultMask) != 0 && option.DefaultMask != "-" {
+ def = option.DefaultMask
+ } else {
+ def = option.defaultLiteral
+ }
+
+ var envDef string
+ if option.EnvDefaultKey != "" {
+ var envPrintable string
+ if runtime.GOOS == "windows" {
+ envPrintable = "%" + option.EnvDefaultKey + "%"
+ } else {
+ envPrintable = "$" + option.EnvDefaultKey
+ }
+ envDef = fmt.Sprintf(" [%s]", envPrintable)
+ }
+
+ var desc string
+
+ if def != "" {
+ desc = fmt.Sprintf("%s (default: %v)%s", option.Description, def, envDef)
+ } else {
+ desc = option.Description + envDef
+ }
+
+ writer.WriteString(wrapText(desc,
+ info.terminalColumns-descstart,
+ strings.Repeat(" ", descstart)))
+ }
+
+ writer.WriteString("\n")
+}
+
+func maxCommandLength(s []*Command) int {
+ if len(s) == 0 {
+ return 0
+ }
+
+ ret := len(s[0].Name)
+
+ for _, v := range s[1:] {
+ l := len(v.Name)
+
+ if l > ret {
+ ret = l
+ }
+ }
+
+ return ret
+}
+
+// WriteHelp writes a help message containing all the possible options and
+// their descriptions to the provided writer. Note that the HelpFlag parser
+// option provides a convenient way to add a -h/--help option group to the
+// command line parser which will automatically show the help messages using
+// this method.
+func (p *Parser) WriteHelp(writer io.Writer) {
+ if writer == nil {
+ return
+ }
+
+ wr := bufio.NewWriter(writer)
+ aligninfo := p.getAlignmentInfo()
+
+ cmd := p.Command
+
+ for cmd.Active != nil {
+ cmd = cmd.Active
+ }
+
+ if p.Name != "" {
+ wr.WriteString("Usage:\n")
+ wr.WriteString(" ")
+
+ allcmd := p.Command
+
+ for allcmd != nil {
+ var usage string
+
+ if allcmd == p.Command {
+ if len(p.Usage) != 0 {
+ usage = p.Usage
+ } else if p.Options&HelpFlag != 0 {
+ usage = "[OPTIONS]"
+ }
+ } else if us, ok := allcmd.data.(Usage); ok {
+ usage = us.Usage()
+ } else if allcmd.hasCliOptions() {
+ usage = fmt.Sprintf("[%s-OPTIONS]", allcmd.Name)
+ }
+
+ if len(usage) != 0 {
+ fmt.Fprintf(wr, " %s %s", allcmd.Name, usage)
+ } else {
+ fmt.Fprintf(wr, " %s", allcmd.Name)
+ }
+
+ if len(allcmd.args) > 0 {
+ fmt.Fprintf(wr, " ")
+ }
+
+ for i, arg := range allcmd.args {
+ if i != 0 {
+ fmt.Fprintf(wr, " ")
+ }
+
+ name := arg.Name
+
+ if arg.isRemaining() {
+ name = name + "..."
+ }
+
+ if !allcmd.ArgsRequired {
+ fmt.Fprintf(wr, "[%s]", name)
+ } else {
+ fmt.Fprintf(wr, "%s", name)
+ }
+ }
+
+ if allcmd.Active == nil && len(allcmd.commands) > 0 {
+ var co, cc string
+
+ if allcmd.SubcommandsOptional {
+ co, cc = "[", "]"
+ } else {
+ co, cc = "<", ">"
+ }
+
+ visibleCommands := allcmd.visibleCommands()
+
+ if len(visibleCommands) > 3 {
+ fmt.Fprintf(wr, " %scommand%s", co, cc)
+ } else {
+ subcommands := allcmd.sortedVisibleCommands()
+ names := make([]string, len(subcommands))
+
+ for i, subc := range subcommands {
+ names[i] = subc.Name
+ }
+
+ fmt.Fprintf(wr, " %s%s%s", co, strings.Join(names, " | "), cc)
+ }
+ }
+
+ allcmd = allcmd.Active
+ }
+
+ fmt.Fprintln(wr)
+
+ if len(cmd.LongDescription) != 0 {
+ fmt.Fprintln(wr)
+
+ t := wrapText(cmd.LongDescription,
+ aligninfo.terminalColumns,
+ "")
+
+ fmt.Fprintln(wr, t)
+ }
+ }
+
+ c := p.Command
+
+ for c != nil {
+ printcmd := c != p.Command
+
+ c.eachGroup(func(grp *Group) {
+ first := true
+
+ // Skip built-in help group for all commands except the top-level
+ // parser
+ if grp.Hidden || (grp.isBuiltinHelp && c != p.Command) {
+ return
+ }
+
+ for _, info := range grp.options {
+ if !info.canCli() || info.Hidden {
+ continue
+ }
+
+ if printcmd {
+ fmt.Fprintf(wr, "\n[%s command options]\n", c.Name)
+ aligninfo.indent = true
+ printcmd = false
+ }
+
+ if first && cmd.Group != grp {
+ fmt.Fprintln(wr)
+
+ if aligninfo.indent {
+ wr.WriteString(" ")
+ }
+
+ fmt.Fprintf(wr, "%s:\n", grp.ShortDescription)
+ first = false
+ }
+
+ p.writeHelpOption(wr, info, aligninfo)
+ }
+ })
+
+ var args []*Arg
+ for _, arg := range c.args {
+ if arg.Description != "" {
+ args = append(args, arg)
+ }
+ }
+
+ if len(args) > 0 {
+ if c == p.Command {
+ fmt.Fprintf(wr, "\nArguments:\n")
+ } else {
+ fmt.Fprintf(wr, "\n[%s command arguments]\n", c.Name)
+ }
+
+ descStart := aligninfo.descriptionStart() + paddingBeforeOption
+
+ for _, arg := range args {
+ argPrefix := strings.Repeat(" ", paddingBeforeOption)
+ argPrefix += arg.Name
+
+ if len(arg.Description) > 0 {
+ argPrefix += ":"
+ wr.WriteString(argPrefix)
+
+ // Space between "arg:" and the description start
+ descPadding := strings.Repeat(" ", descStart-len(argPrefix))
+ // How much space the description gets before wrapping
+ descWidth := aligninfo.terminalColumns - 1 - descStart
+ // Whitespace to which we can indent new description lines
+ descPrefix := strings.Repeat(" ", descStart)
+
+ wr.WriteString(descPadding)
+ wr.WriteString(wrapText(arg.Description, descWidth, descPrefix))
+ } else {
+ wr.WriteString(argPrefix)
+ }
+
+ fmt.Fprintln(wr)
+ }
+ }
+
+ c = c.Active
+ }
+
+ scommands := cmd.sortedVisibleCommands()
+
+ if len(scommands) > 0 {
+ maxnamelen := maxCommandLength(scommands)
+
+ fmt.Fprintln(wr)
+ fmt.Fprintln(wr, "Available commands:")
+
+ for _, c := range scommands {
+ fmt.Fprintf(wr, " %s", c.Name)
+
+ if len(c.ShortDescription) > 0 {
+ pad := strings.Repeat(" ", maxnamelen-len(c.Name))
+ fmt.Fprintf(wr, "%s %s", pad, c.ShortDescription)
+
+ if len(c.Aliases) > 0 {
+ fmt.Fprintf(wr, " (aliases: %s)", strings.Join(c.Aliases, ", "))
+ }
+
+ }
+
+ fmt.Fprintln(wr)
+ }
+ }
+
+ wr.Flush()
+}
diff --git a/vendor/github.com/jessevdk/go-flags/ini.go b/vendor/github.com/jessevdk/go-flags/ini.go
new file mode 100644
index 0000000..f9aea2e
--- /dev/null
+++ b/vendor/github.com/jessevdk/go-flags/ini.go
@@ -0,0 +1,601 @@
+package flags
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "os"
+ "reflect"
+ "sort"
+ "strconv"
+ "strings"
+)
+
+// IniError contains location information on where an error occurred.
+type IniError struct {
+ // The error message.
+ Message string
+
+ // The filename of the file in which the error occurred.
+ File string
+
+ // The line number at which the error occurred.
+ LineNumber uint
+}
+
+// Error provides a "file:line: message" formatted message of the ini error.
+func (x *IniError) Error() string {
+ return fmt.Sprintf(
+ "%s:%d: %s",
+ x.File,
+ x.LineNumber,
+ x.Message,
+ )
+}
+
+// IniOptions for writing
+type IniOptions uint
+
+const (
+ // IniNone indicates no options.
+ IniNone IniOptions = 0
+
+ // IniIncludeDefaults indicates that default values should be written.
+ IniIncludeDefaults = 1 << iota
+
+ // IniCommentDefaults indicates that if IniIncludeDefaults is used
+ // options with default values are written but commented out.
+ IniCommentDefaults
+
+ // IniIncludeComments indicates that comments containing the description
+ // of an option should be written.
+ IniIncludeComments
+
+ // IniDefault provides a default set of options.
+ IniDefault = IniIncludeComments
+)
+
+// IniParser is a utility to read and write flags options from and to ini
+// formatted strings.
+type IniParser struct {
+ ParseAsDefaults bool // override default flags
+
+ parser *Parser
+}
+
+type iniValue struct {
+ Name string
+ Value string
+ Quoted bool
+ LineNumber uint
+}
+
+type iniSection []iniValue
+
+type ini struct {
+ File string
+ Sections map[string]iniSection
+}
+
+// NewIniParser creates a new ini parser for a given Parser.
+func NewIniParser(p *Parser) *IniParser {
+ return &IniParser{
+ parser: p,
+ }
+}
+
+// IniParse is a convenience function to parse command line options with default
+// settings from an ini formatted file. The provided data is a pointer to a struct
+// representing the default option group (named "Application Options"). For
+// more control, use flags.NewParser.
+func IniParse(filename string, data interface{}) error {
+ p := NewParser(data, Default)
+
+ return NewIniParser(p).ParseFile(filename)
+}
+
+// ParseFile parses flags from an ini formatted file. See Parse for more
+// information on the ini file format. The returned errors can be of the type
+// flags.Error or flags.IniError.
+func (i *IniParser) ParseFile(filename string) error {
+ i.parser.clearIsSet()
+
+ ini, err := readIniFromFile(filename)
+
+ if err != nil {
+ return err
+ }
+
+ return i.parse(ini)
+}
+
+// Parse parses flags from an ini format. You can use ParseFile as a
+// convenience function to parse from a filename instead of a general
+// io.Reader.
+//
+// The format of the ini file is as follows:
+//
+// [Option group name]
+// option = value
+//
+// Each section in the ini file represents an option group or command in the
+// flags parser. The default flags parser option group (i.e. when using
+// flags.Parse) is named 'Application Options'. The ini option name is matched
+// in the following order:
+//
+// 1. Compared to the ini-name tag on the option struct field (if present)
+// 2. Compared to the struct field name
+// 3. Compared to the option long name (if present)
+// 4. Compared to the option short name (if present)
+//
+// Sections for nested groups and commands can be addressed using a dot `.'
+// namespacing notation (i.e [subcommand.Options]). Group section names are
+// matched case insensitive.
+//
+// The returned errors can be of the type flags.Error or flags.IniError.
+func (i *IniParser) Parse(reader io.Reader) error {
+ i.parser.clearIsSet()
+
+ ini, err := readIni(reader, "")
+
+ if err != nil {
+ return err
+ }
+
+ return i.parse(ini)
+}
+
+// WriteFile writes the flags as ini format into a file. See Write
+// for more information. The returned error occurs when the specified file
+// could not be opened for writing.
+func (i *IniParser) WriteFile(filename string, options IniOptions) error {
+ return writeIniToFile(i, filename, options)
+}
+
+// Write writes the current values of all the flags to an ini format.
+// See Parse for more information on the ini file format. You typically
+// call this only after settings have been parsed since the default values of each
+// option are stored just before parsing the flags (this is only relevant when
+// IniIncludeDefaults is _not_ set in options).
+func (i *IniParser) Write(writer io.Writer, options IniOptions) {
+ writeIni(i, writer, options)
+}
+
+func readFullLine(reader *bufio.Reader) (string, error) {
+ var line []byte
+
+ for {
+ l, more, err := reader.ReadLine()
+
+ if err != nil {
+ return "", err
+ }
+
+ if line == nil && !more {
+ return string(l), nil
+ }
+
+ line = append(line, l...)
+
+ if !more {
+ break
+ }
+ }
+
+ return string(line), nil
+}
+
+func optionIniName(option *Option) string {
+ name := option.tag.Get("_read-ini-name")
+
+ if len(name) != 0 {
+ return name
+ }
+
+ name = option.tag.Get("ini-name")
+
+ if len(name) != 0 {
+ return name
+ }
+
+ return option.field.Name
+}
+
+func writeGroupIni(cmd *Command, group *Group, namespace string, writer io.Writer, options IniOptions) {
+ var sname string
+
+ if len(namespace) != 0 {
+ sname = namespace
+ }
+
+ if cmd.Group != group && len(group.ShortDescription) != 0 {
+ if len(sname) != 0 {
+ sname += "."
+ }
+
+ sname += group.ShortDescription
+ }
+
+ sectionwritten := false
+ comments := (options & IniIncludeComments) != IniNone
+
+ for _, option := range group.options {
+ if option.isFunc() || option.Hidden {
+ continue
+ }
+
+ if len(option.tag.Get("no-ini")) != 0 {
+ continue
+ }
+
+ val := option.value
+
+ if (options&IniIncludeDefaults) == IniNone && option.valueIsDefault() {
+ continue
+ }
+
+ if !sectionwritten {
+ fmt.Fprintf(writer, "[%s]\n", sname)
+ sectionwritten = true
+ }
+
+ if comments && len(option.Description) != 0 {
+ fmt.Fprintf(writer, "; %s\n", option.Description)
+ }
+
+ oname := optionIniName(option)
+
+ commentOption := (options&(IniIncludeDefaults|IniCommentDefaults)) == IniIncludeDefaults|IniCommentDefaults && option.valueIsDefault()
+
+ kind := val.Type().Kind()
+ switch kind {
+ case reflect.Slice:
+ kind = val.Type().Elem().Kind()
+
+ if val.Len() == 0 {
+ writeOption(writer, oname, kind, "", "", true, option.iniQuote)
+ } else {
+ for idx := 0; idx < val.Len(); idx++ {
+ v, _ := convertToString(val.Index(idx), option.tag)
+
+ writeOption(writer, oname, kind, "", v, commentOption, option.iniQuote)
+ }
+ }
+ case reflect.Map:
+ kind = val.Type().Elem().Kind()
+
+ if val.Len() == 0 {
+ writeOption(writer, oname, kind, "", "", true, option.iniQuote)
+ } else {
+ mkeys := val.MapKeys()
+ keys := make([]string, len(val.MapKeys()))
+ kkmap := make(map[string]reflect.Value)
+
+ for i, k := range mkeys {
+ keys[i], _ = convertToString(k, option.tag)
+ kkmap[keys[i]] = k
+ }
+
+ sort.Strings(keys)
+
+ for _, k := range keys {
+ v, _ := convertToString(val.MapIndex(kkmap[k]), option.tag)
+
+ writeOption(writer, oname, kind, k, v, commentOption, option.iniQuote)
+ }
+ }
+ default:
+ v, _ := convertToString(val, option.tag)
+
+ writeOption(writer, oname, kind, "", v, commentOption, option.iniQuote)
+ }
+
+ if comments {
+ fmt.Fprintln(writer)
+ }
+ }
+
+ if sectionwritten && !comments {
+ fmt.Fprintln(writer)
+ }
+}
+
+func writeOption(writer io.Writer, optionName string, optionType reflect.Kind, optionKey string, optionValue string, commentOption bool, forceQuote bool) {
+ if forceQuote || (optionType == reflect.String && !isPrint(optionValue)) {
+ optionValue = strconv.Quote(optionValue)
+ }
+
+ comment := ""
+ if commentOption {
+ comment = "; "
+ }
+
+ fmt.Fprintf(writer, "%s%s =", comment, optionName)
+
+ if optionKey != "" {
+ fmt.Fprintf(writer, " %s:%s", optionKey, optionValue)
+ } else if optionValue != "" {
+ fmt.Fprintf(writer, " %s", optionValue)
+ }
+
+ fmt.Fprintln(writer)
+}
+
+func writeCommandIni(command *Command, namespace string, writer io.Writer, options IniOptions) {
+ command.eachGroup(func(group *Group) {
+ if !group.Hidden {
+ writeGroupIni(command, group, namespace, writer, options)
+ }
+ })
+
+ for _, c := range command.commands {
+ var nns string
+
+ if c.Hidden {
+ continue
+ }
+
+ if len(namespace) != 0 {
+ nns = c.Name + "." + nns
+ } else {
+ nns = c.Name
+ }
+
+ writeCommandIni(c, nns, writer, options)
+ }
+}
+
+func writeIni(parser *IniParser, writer io.Writer, options IniOptions) {
+ writeCommandIni(parser.parser.Command, "", writer, options)
+}
+
+func writeIniToFile(parser *IniParser, filename string, options IniOptions) error {
+ file, err := os.Create(filename)
+
+ if err != nil {
+ return err
+ }
+
+ defer file.Close()
+
+ writeIni(parser, file, options)
+
+ return nil
+}
+
+func readIniFromFile(filename string) (*ini, error) {
+ file, err := os.Open(filename)
+
+ if err != nil {
+ return nil, err
+ }
+
+ defer file.Close()
+
+ return readIni(file, filename)
+}
+
+func readIni(contents io.Reader, filename string) (*ini, error) {
+ ret := &ini{
+ File: filename,
+ Sections: make(map[string]iniSection),
+ }
+
+ reader := bufio.NewReader(contents)
+
+ // Empty global section
+ section := make(iniSection, 0, 10)
+ sectionname := ""
+
+ ret.Sections[sectionname] = section
+
+ var lineno uint
+
+ for {
+ line, err := readFullLine(reader)
+
+ if err == io.EOF {
+ break
+ } else if err != nil {
+ return nil, err
+ }
+
+ lineno++
+ line = strings.TrimSpace(line)
+
+ // Skip empty lines and lines starting with ; (comments)
+ if len(line) == 0 || line[0] == ';' || line[0] == '#' {
+ continue
+ }
+
+ if line[0] == '[' {
+ if line[0] != '[' || line[len(line)-1] != ']' {
+ return nil, &IniError{
+ Message: "malformed section header",
+ File: filename,
+ LineNumber: lineno,
+ }
+ }
+
+ name := strings.TrimSpace(line[1 : len(line)-1])
+
+ if len(name) == 0 {
+ return nil, &IniError{
+ Message: "empty section name",
+ File: filename,
+ LineNumber: lineno,
+ }
+ }
+
+ sectionname = name
+ section = ret.Sections[name]
+
+ if section == nil {
+ section = make(iniSection, 0, 10)
+ ret.Sections[name] = section
+ }
+
+ continue
+ }
+
+ // Parse option here
+ keyval := strings.SplitN(line, "=", 2)
+
+ if len(keyval) != 2 {
+ return nil, &IniError{
+ Message: fmt.Sprintf("malformed key=value (%s)", line),
+ File: filename,
+ LineNumber: lineno,
+ }
+ }
+
+ name := strings.TrimSpace(keyval[0])
+ value := strings.TrimSpace(keyval[1])
+ quoted := false
+
+ if len(value) != 0 && value[0] == '"' {
+ if v, err := strconv.Unquote(value); err == nil {
+ value = v
+
+ quoted = true
+ } else {
+ return nil, &IniError{
+ Message: err.Error(),
+ File: filename,
+ LineNumber: lineno,
+ }
+ }
+ }
+
+ section = append(section, iniValue{
+ Name: name,
+ Value: value,
+ Quoted: quoted,
+ LineNumber: lineno,
+ })
+
+ ret.Sections[sectionname] = section
+ }
+
+ return ret, nil
+}
+
+func (i *IniParser) matchingGroups(name string) []*Group {
+ if len(name) == 0 {
+ var ret []*Group
+
+ i.parser.eachGroup(func(g *Group) {
+ ret = append(ret, g)
+ })
+
+ return ret
+ }
+
+ g := i.parser.groupByName(name)
+
+ if g != nil {
+ return []*Group{g}
+ }
+
+ return nil
+}
+
+func (i *IniParser) parse(ini *ini) error {
+ p := i.parser
+
+ var quotesLookup = make(map[*Option]bool)
+
+ for name, section := range ini.Sections {
+ groups := i.matchingGroups(name)
+
+ if len(groups) == 0 {
+ return newErrorf(ErrUnknownGroup, "could not find option group `%s'", name)
+ }
+
+ for _, inival := range section {
+ var opt *Option
+
+ for _, group := range groups {
+ opt = group.optionByName(inival.Name, func(o *Option, n string) bool {
+ return strings.ToLower(o.tag.Get("ini-name")) == strings.ToLower(n)
+ })
+
+ if opt != nil && len(opt.tag.Get("no-ini")) != 0 {
+ opt = nil
+ }
+
+ if opt != nil {
+ break
+ }
+ }
+
+ if opt == nil {
+ if (p.Options & IgnoreUnknown) == None {
+ return &IniError{
+ Message: fmt.Sprintf("unknown option: %s", inival.Name),
+ File: ini.File,
+ LineNumber: inival.LineNumber,
+ }
+ }
+
+ continue
+ }
+
+ // ini value is ignored if override is set and
+ // value was previously set from non default
+ if i.ParseAsDefaults && !opt.isSetDefault {
+ continue
+ }
+
+ pval := &inival.Value
+
+ if !opt.canArgument() && len(inival.Value) == 0 {
+ pval = nil
+ } else {
+ if opt.value.Type().Kind() == reflect.Map {
+ parts := strings.SplitN(inival.Value, ":", 2)
+
+ // only handle unquoting
+ if len(parts) == 2 && parts[1][0] == '"' {
+ if v, err := strconv.Unquote(parts[1]); err == nil {
+ parts[1] = v
+
+ inival.Quoted = true
+ } else {
+ return &IniError{
+ Message: err.Error(),
+ File: ini.File,
+ LineNumber: inival.LineNumber,
+ }
+ }
+
+ s := parts[0] + ":" + parts[1]
+
+ pval = &s
+ }
+ }
+ }
+
+ if err := opt.set(pval); err != nil {
+ return &IniError{
+ Message: err.Error(),
+ File: ini.File,
+ LineNumber: inival.LineNumber,
+ }
+ }
+
+ // either all INI values are quoted or only values who need quoting
+ if _, ok := quotesLookup[opt]; !inival.Quoted || !ok {
+ quotesLookup[opt] = inival.Quoted
+ }
+
+ opt.tag.Set("_read-ini-name", inival.Name)
+ }
+ }
+
+ for opt, quoted := range quotesLookup {
+ opt.iniQuote = quoted
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/jessevdk/go-flags/man.go b/vendor/github.com/jessevdk/go-flags/man.go
new file mode 100644
index 0000000..0cb114e
--- /dev/null
+++ b/vendor/github.com/jessevdk/go-flags/man.go
@@ -0,0 +1,205 @@
+package flags
+
+import (
+ "fmt"
+ "io"
+ "runtime"
+ "strings"
+ "time"
+)
+
+func manQuote(s string) string {
+ return strings.Replace(s, "\\", "\\\\", -1)
+}
+
+func formatForMan(wr io.Writer, s string) {
+ for {
+ idx := strings.IndexRune(s, '`')
+
+ if idx < 0 {
+ fmt.Fprintf(wr, "%s", manQuote(s))
+ break
+ }
+
+ fmt.Fprintf(wr, "%s", manQuote(s[:idx]))
+
+ s = s[idx+1:]
+ idx = strings.IndexRune(s, '\'')
+
+ if idx < 0 {
+ fmt.Fprintf(wr, "%s", manQuote(s))
+ break
+ }
+
+ fmt.Fprintf(wr, "\\fB%s\\fP", manQuote(s[:idx]))
+ s = s[idx+1:]
+ }
+}
+
+func writeManPageOptions(wr io.Writer, grp *Group) {
+ grp.eachGroup(func(group *Group) {
+ if group.Hidden || len(group.options) == 0 {
+ return
+ }
+
+ // If the parent (grp) has any subgroups, display their descriptions as
+ // subsection headers similar to the output of --help.
+ if group.ShortDescription != "" && len(grp.groups) > 0 {
+ fmt.Fprintf(wr, ".SS %s\n", group.ShortDescription)
+
+ if group.LongDescription != "" {
+ formatForMan(wr, group.LongDescription)
+ fmt.Fprintln(wr, "")
+ }
+ }
+
+ for _, opt := range group.options {
+ if !opt.canCli() || opt.Hidden {
+ continue
+ }
+
+ fmt.Fprintln(wr, ".TP")
+ fmt.Fprintf(wr, "\\fB")
+
+ if opt.ShortName != 0 {
+ fmt.Fprintf(wr, "\\fB\\-%c\\fR", opt.ShortName)
+ }
+
+ if len(opt.LongName) != 0 {
+ if opt.ShortName != 0 {
+ fmt.Fprintf(wr, ", ")
+ }
+
+ fmt.Fprintf(wr, "\\fB\\-\\-%s\\fR", manQuote(opt.LongNameWithNamespace()))
+ }
+
+ if len(opt.ValueName) != 0 || opt.OptionalArgument {
+ if opt.OptionalArgument {
+ fmt.Fprintf(wr, " [\\fI%s=%s\\fR]", manQuote(opt.ValueName), manQuote(strings.Join(quoteV(opt.OptionalValue), ", ")))
+ } else {
+ fmt.Fprintf(wr, " \\fI%s\\fR", manQuote(opt.ValueName))
+ }
+ }
+
+ if len(opt.Default) != 0 {
+ fmt.Fprintf(wr, " <default: \\fI%s\\fR>", manQuote(strings.Join(quoteV(opt.Default), ", ")))
+ } else if len(opt.EnvDefaultKey) != 0 {
+ if runtime.GOOS == "windows" {
+ fmt.Fprintf(wr, " <default: \\fI%%%s%%\\fR>", manQuote(opt.EnvDefaultKey))
+ } else {
+ fmt.Fprintf(wr, " <default: \\fI$%s\\fR>", manQuote(opt.EnvDefaultKey))
+ }
+ }
+
+ if opt.Required {
+ fmt.Fprintf(wr, " (\\fIrequired\\fR)")
+ }
+
+ fmt.Fprintln(wr, "\\fP")
+
+ if len(opt.Description) != 0 {
+ formatForMan(wr, opt.Description)
+ fmt.Fprintln(wr, "")
+ }
+ }
+ })
+}
+
+func writeManPageSubcommands(wr io.Writer, name string, root *Command) {
+ commands := root.sortedVisibleCommands()
+
+ for _, c := range commands {
+ var nn string
+
+ if c.Hidden {
+ continue
+ }
+
+ if len(name) != 0 {
+ nn = name + " " + c.Name
+ } else {
+ nn = c.Name
+ }
+
+ writeManPageCommand(wr, nn, root, c)
+ }
+}
+
+func writeManPageCommand(wr io.Writer, name string, root *Command, command *Command) {
+ fmt.Fprintf(wr, ".SS %s\n", name)
+ fmt.Fprintln(wr, command.ShortDescription)
+
+ if len(command.LongDescription) > 0 {
+ fmt.Fprintln(wr, "")
+
+ cmdstart := fmt.Sprintf("The %s command", manQuote(command.Name))
+
+ if strings.HasPrefix(command.LongDescription, cmdstart) {
+ fmt.Fprintf(wr, "The \\fI%s\\fP command", manQuote(command.Name))
+
+ formatForMan(wr, command.LongDescription[len(cmdstart):])
+ fmt.Fprintln(wr, "")
+ } else {
+ formatForMan(wr, command.LongDescription)
+ fmt.Fprintln(wr, "")
+ }
+ }
+
+ var usage string
+ if us, ok := command.data.(Usage); ok {
+ usage = us.Usage()
+ } else if command.hasCliOptions() {
+ usage = fmt.Sprintf("[%s-OPTIONS]", command.Name)
+ }
+
+ var pre string
+ if root.hasCliOptions() {
+ pre = fmt.Sprintf("%s [OPTIONS] %s", root.Name, command.Name)
+ } else {
+ pre = fmt.Sprintf("%s %s", root.Name, command.Name)
+ }
+
+ if len(usage) > 0 {
+ fmt.Fprintf(wr, "\n\\fBUsage\\fP: %s %s\n.TP\n", manQuote(pre), manQuote(usage))
+ }
+
+ if len(command.Aliases) > 0 {
+ fmt.Fprintf(wr, "\n\\fBAliases\\fP: %s\n\n", manQuote(strings.Join(command.Aliases, ", ")))
+ }
+
+ writeManPageOptions(wr, command.Group)
+ writeManPageSubcommands(wr, name, command)
+}
+
+// WriteManPage writes a basic man page in groff format to the specified
+// writer.
+func (p *Parser) WriteManPage(wr io.Writer) {
+ t := time.Now()
+
+ fmt.Fprintf(wr, ".TH %s 1 \"%s\"\n", manQuote(p.Name), t.Format("2 January 2006"))
+ fmt.Fprintln(wr, ".SH NAME")
+ fmt.Fprintf(wr, "%s \\- %s\n", manQuote(p.Name), manQuote(p.ShortDescription))
+ fmt.Fprintln(wr, ".SH SYNOPSIS")
+
+ usage := p.Usage
+
+ if len(usage) == 0 {
+ usage = "[OPTIONS]"
+ }
+
+ fmt.Fprintf(wr, "\\fB%s\\fP %s\n", manQuote(p.Name), manQuote(usage))
+ fmt.Fprintln(wr, ".SH DESCRIPTION")
+
+ formatForMan(wr, p.LongDescription)
+ fmt.Fprintln(wr, "")
+
+ fmt.Fprintln(wr, ".SH OPTIONS")
+
+ writeManPageOptions(wr, p.Command.Group)
+
+ if len(p.visibleCommands()) > 0 {
+ fmt.Fprintln(wr, ".SH COMMANDS")
+
+ writeManPageSubcommands(wr, "", p.Command)
+ }
+}
diff --git a/vendor/github.com/jessevdk/go-flags/multitag.go b/vendor/github.com/jessevdk/go-flags/multitag.go
new file mode 100644
index 0000000..96bb1a3
--- /dev/null
+++ b/vendor/github.com/jessevdk/go-flags/multitag.go
@@ -0,0 +1,140 @@
+package flags
+
+import (
+ "strconv"
+)
+
+type multiTag struct {
+ value string
+ cache map[string][]string
+}
+
+func newMultiTag(v string) multiTag {
+ return multiTag{
+ value: v,
+ }
+}
+
+func (x *multiTag) scan() (map[string][]string, error) {
+ v := x.value
+
+ ret := make(map[string][]string)
+
+ // This is mostly copied from reflect.StructTag.Get
+ for v != "" {
+ i := 0
+
+ // Skip whitespace
+ for i < len(v) && v[i] == ' ' {
+ i++
+ }
+
+ v = v[i:]
+
+ if v == "" {
+ break
+ }
+
+ // Scan to colon to find key
+ i = 0
+
+ for i < len(v) && v[i] != ' ' && v[i] != ':' && v[i] != '"' {
+ i++
+ }
+
+ if i >= len(v) {
+ return nil, newErrorf(ErrTag, "expected `:' after key name, but got end of tag (in `%v`)", x.value)
+ }
+
+ if v[i] != ':' {
+ return nil, newErrorf(ErrTag, "expected `:' after key name, but got `%v' (in `%v`)", v[i], x.value)
+ }
+
+ if i+1 >= len(v) {
+ return nil, newErrorf(ErrTag, "expected `\"' to start tag value at end of tag (in `%v`)", x.value)
+ }
+
+ if v[i+1] != '"' {
+ return nil, newErrorf(ErrTag, "expected `\"' to start tag value, but got `%v' (in `%v`)", v[i+1], x.value)
+ }
+
+ name := v[:i]
+ v = v[i+1:]
+
+ // Scan quoted string to find value
+ i = 1
+
+ for i < len(v) && v[i] != '"' {
+ if v[i] == '\n' {
+ return nil, newErrorf(ErrTag, "unexpected newline in tag value `%v' (in `%v`)", name, x.value)
+ }
+
+ if v[i] == '\\' {
+ i++
+ }
+ i++
+ }
+
+ if i >= len(v) {
+ return nil, newErrorf(ErrTag, "expected end of tag value `\"' at end of tag (in `%v`)", x.value)
+ }
+
+ val, err := strconv.Unquote(v[:i+1])
+
+ if err != nil {
+ return nil, newErrorf(ErrTag, "Malformed value of tag `%v:%v` => %v (in `%v`)", name, v[:i+1], err, x.value)
+ }
+
+ v = v[i+1:]
+
+ ret[name] = append(ret[name], val)
+ }
+
+ return ret, nil
+}
+
+func (x *multiTag) Parse() error {
+ vals, err := x.scan()
+ x.cache = vals
+
+ return err
+}
+
+func (x *multiTag) cached() map[string][]string {
+ if x.cache == nil {
+ cache, _ := x.scan()
+
+ if cache == nil {
+ cache = make(map[string][]string)
+ }
+
+ x.cache = cache
+ }
+
+ return x.cache
+}
+
+func (x *multiTag) Get(key string) string {
+ c := x.cached()
+
+ if v, ok := c[key]; ok {
+ return v[len(v)-1]
+ }
+
+ return ""
+}
+
+func (x *multiTag) GetMany(key string) []string {
+ c := x.cached()
+ return c[key]
+}
+
+func (x *multiTag) Set(key string, value string) {
+ c := x.cached()
+ c[key] = []string{value}
+}
+
+func (x *multiTag) SetMany(key string, value []string) {
+ c := x.cached()
+ c[key] = value
+}
diff --git a/vendor/github.com/jessevdk/go-flags/option.go b/vendor/github.com/jessevdk/go-flags/option.go
new file mode 100644
index 0000000..17a2189
--- /dev/null
+++ b/vendor/github.com/jessevdk/go-flags/option.go
@@ -0,0 +1,456 @@
+package flags
+
+import (
+ "bytes"
+ "fmt"
+ "reflect"
+ "strings"
+ "syscall"
+ "unicode/utf8"
+)
+
+// Option flag information. Contains a description of the option, short and
+// long name as well as a default value and whether an argument for this
+// flag is optional.
+type Option struct {
+ // The description of the option flag. This description is shown
+ // automatically in the built-in help.
+ Description string
+
+ // The short name of the option (a single character). If not 0, the
+ // option flag can be 'activated' using -<ShortName>. Either ShortName
+ // or LongName needs to be non-empty.
+ ShortName rune
+
+ // The long name of the option. If not "", the option flag can be
+ // activated using --<LongName>. Either ShortName or LongName needs
+ // to be non-empty.
+ LongName string
+
+ // The default value of the option.
+ Default []string
+
+ // The optional environment default value key name.
+ EnvDefaultKey string
+
+ // The optional delimiter string for EnvDefaultKey values.
+ EnvDefaultDelim string
+
+ // If true, specifies that the argument to an option flag is optional.
+ // When no argument to the flag is specified on the command line, the
+ // value of OptionalValue will be set in the field this option represents.
+ // This is only valid for non-boolean options.
+ OptionalArgument bool
+
+ // The optional value of the option. The optional value is used when
+ // the option flag is marked as having an OptionalArgument. This means
+ // that when the flag is specified, but no option argument is given,
+ // the value of the field this option represents will be set to
+ // OptionalValue. This is only valid for non-boolean options.
+ OptionalValue []string
+
+ // If true, the option _must_ be specified on the command line. If the
+ // option is not specified, the parser will generate an ErrRequired type
+ // error.
+ Required bool
+
+ // A name for the value of an option shown in the Help as --flag [ValueName]
+ ValueName string
+
+ // A mask value to show in the help instead of the default value. This
+ // is useful for hiding sensitive information in the help, such as
+ // passwords.
+ DefaultMask string
+
+ // If non empty, only a certain set of values is allowed for an option.
+ Choices []string
+
+ // If true, the option is not displayed in the help or man page
+ Hidden bool
+
+ // The group which the option belongs to
+ group *Group
+
+ // The struct field which the option represents.
+ field reflect.StructField
+
+ // The struct field value which the option represents.
+ value reflect.Value
+
+ // Determines if the option will be always quoted in the INI output
+ iniQuote bool
+
+ tag multiTag
+ isSet bool
+ isSetDefault bool
+ preventDefault bool
+
+ defaultLiteral string
+}
+
+// LongNameWithNamespace returns the option's long name with the group namespaces
+// prepended by walking up the option's group tree. Namespaces and the long name
+// itself are separated by the parser's namespace delimiter. If the long name is
+// empty an empty string is returned.
+func (option *Option) LongNameWithNamespace() string {
+ if len(option.LongName) == 0 {
+ return ""
+ }
+
+ // fetch the namespace delimiter from the parser which is always at the
+ // end of the group hierarchy
+ namespaceDelimiter := ""
+ g := option.group
+
+ for {
+ if p, ok := g.parent.(*Parser); ok {
+ namespaceDelimiter = p.NamespaceDelimiter
+
+ break
+ }
+
+ switch i := g.parent.(type) {
+ case *Command:
+ g = i.Group
+ case *Group:
+ g = i
+ }
+ }
+
+ // concatenate long name with namespace
+ longName := option.LongName
+ g = option.group
+
+ for g != nil {
+ if g.Namespace != "" {
+ longName = g.Namespace + namespaceDelimiter + longName
+ }
+
+ switch i := g.parent.(type) {
+ case *Command:
+ g = i.Group
+ case *Group:
+ g = i
+ case *Parser:
+ g = nil
+ }
+ }
+
+ return longName
+}
+
+// String converts an option to a human friendly readable string describing the
+// option.
+func (option *Option) String() string {
+ var s string
+ var short string
+
+ if option.ShortName != 0 {
+ data := make([]byte, utf8.RuneLen(option.ShortName))
+ utf8.EncodeRune(data, option.ShortName)
+ short = string(data)
+
+ if len(option.LongName) != 0 {
+ s = fmt.Sprintf("%s%s, %s%s",
+ string(defaultShortOptDelimiter), short,
+ defaultLongOptDelimiter, option.LongNameWithNamespace())
+ } else {
+ s = fmt.Sprintf("%s%s", string(defaultShortOptDelimiter), short)
+ }
+ } else if len(option.LongName) != 0 {
+ s = fmt.Sprintf("%s%s", defaultLongOptDelimiter, option.LongNameWithNamespace())
+ }
+
+ return s
+}
+
+// Value returns the option value as an interface{}.
+func (option *Option) Value() interface{} {
+ return option.value.Interface()
+}
+
+// Field returns the reflect struct field of the option.
+func (option *Option) Field() reflect.StructField {
+ return option.field
+}
+
+// IsSet returns true if option has been set
+func (option *Option) IsSet() bool {
+ return option.isSet
+}
+
+// Set the value of an option to the specified value. An error will be returned
+// if the specified value could not be converted to the corresponding option
+// value type.
+func (option *Option) set(value *string) error {
+ kind := option.value.Type().Kind()
+
+ if (kind == reflect.Map || kind == reflect.Slice) && !option.isSet {
+ option.empty()
+ }
+
+ option.isSet = true
+ option.preventDefault = true
+
+ if len(option.Choices) != 0 {
+ found := false
+
+ for _, choice := range option.Choices {
+ if choice == *value {
+ found = true
+ break
+ }
+ }
+
+ if !found {
+ allowed := strings.Join(option.Choices[0:len(option.Choices)-1], ", ")
+
+ if len(option.Choices) > 1 {
+ allowed += " or " + option.Choices[len(option.Choices)-1]
+ }
+
+ return newErrorf(ErrInvalidChoice,
+ "Invalid value `%s' for option `%s'. Allowed values are: %s",
+ *value, option, allowed)
+ }
+ }
+
+ if option.isFunc() {
+ return option.call(value)
+ } else if value != nil {
+ return convert(*value, option.value, option.tag)
+ }
+
+ return convert("", option.value, option.tag)
+}
+
+func (option *Option) canCli() bool {
+ return option.ShortName != 0 || len(option.LongName) != 0
+}
+
+func (option *Option) canArgument() bool {
+ if u := option.isUnmarshaler(); u != nil {
+ return true
+ }
+
+ return !option.isBool()
+}
+
+func (option *Option) emptyValue() reflect.Value {
+ tp := option.value.Type()
+
+ if tp.Kind() == reflect.Map {
+ return reflect.MakeMap(tp)
+ }
+
+ return reflect.Zero(tp)
+}
+
+func (option *Option) empty() {
+ if !option.isFunc() {
+ option.value.Set(option.emptyValue())
+ }
+}
+
+func (option *Option) clearDefault() {
+ usedDefault := option.Default
+
+ if envKey := option.EnvDefaultKey; envKey != "" {
+ // os.Getenv() makes no distinction between undefined and
+ // empty values, so we use syscall.Getenv()
+ if value, ok := syscall.Getenv(envKey); ok {
+ if option.EnvDefaultDelim != "" {
+ usedDefault = strings.Split(value,
+ option.EnvDefaultDelim)
+ } else {
+ usedDefault = []string{value}
+ }
+ }
+ }
+
+ option.isSetDefault = true
+
+ if len(usedDefault) > 0 {
+ option.empty()
+
+ for _, d := range usedDefault {
+ option.set(&d)
+ option.isSetDefault = true
+ }
+ } else {
+ tp := option.value.Type()
+
+ switch tp.Kind() {
+ case reflect.Map:
+ if option.value.IsNil() {
+ option.empty()
+ }
+ case reflect.Slice:
+ if option.value.IsNil() {
+ option.empty()
+ }
+ }
+ }
+}
+
+func (option *Option) valueIsDefault() bool {
+ // Check if the value of the option corresponds to its
+ // default value
+ emptyval := option.emptyValue()
+
+ checkvalptr := reflect.New(emptyval.Type())
+ checkval := reflect.Indirect(checkvalptr)
+
+ checkval.Set(emptyval)
+
+ if len(option.Default) != 0 {
+ for _, v := range option.Default {
+ convert(v, checkval, option.tag)
+ }
+ }
+
+ return reflect.DeepEqual(option.value.Interface(), checkval.Interface())
+}
+
+func (option *Option) isUnmarshaler() Unmarshaler {
+ v := option.value
+
+ for {
+ if !v.CanInterface() {
+ break
+ }
+
+ i := v.Interface()
+
+ if u, ok := i.(Unmarshaler); ok {
+ return u
+ }
+
+ if !v.CanAddr() {
+ break
+ }
+
+ v = v.Addr()
+ }
+
+ return nil
+}
+
+func (option *Option) isBool() bool {
+ tp := option.value.Type()
+
+ for {
+ switch tp.Kind() {
+ case reflect.Slice, reflect.Ptr:
+ tp = tp.Elem()
+ case reflect.Bool:
+ return true
+ case reflect.Func:
+ return tp.NumIn() == 0
+ default:
+ return false
+ }
+ }
+}
+
+func (option *Option) isSignedNumber() bool {
+ tp := option.value.Type()
+
+ for {
+ switch tp.Kind() {
+ case reflect.Slice, reflect.Ptr:
+ tp = tp.Elem()
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Float32, reflect.Float64:
+ return true
+ default:
+ return false
+ }
+ }
+}
+
+func (option *Option) isFunc() bool {
+ return option.value.Type().Kind() == reflect.Func
+}
+
+func (option *Option) call(value *string) error {
+ var retval []reflect.Value
+
+ if value == nil {
+ retval = option.value.Call(nil)
+ } else {
+ tp := option.value.Type().In(0)
+
+ val := reflect.New(tp)
+ val = reflect.Indirect(val)
+
+ if err := convert(*value, val, option.tag); err != nil {
+ return err
+ }
+
+ retval = option.value.Call([]reflect.Value{val})
+ }
+
+ if len(retval) == 1 && retval[0].Type() == reflect.TypeOf((*error)(nil)).Elem() {
+ if retval[0].Interface() == nil {
+ return nil
+ }
+
+ return retval[0].Interface().(error)
+ }
+
+ return nil
+}
+
+func (option *Option) updateDefaultLiteral() {
+ defs := option.Default
+ def := ""
+
+ if len(defs) == 0 && option.canArgument() {
+ var showdef bool
+
+ switch option.field.Type.Kind() {
+ case reflect.Func, reflect.Ptr:
+ showdef = !option.value.IsNil()
+ case reflect.Slice, reflect.String, reflect.Array:
+ showdef = option.value.Len() > 0
+ case reflect.Map:
+ showdef = !option.value.IsNil() && option.value.Len() > 0
+ default:
+ zeroval := reflect.Zero(option.field.Type)
+ showdef = !reflect.DeepEqual(zeroval.Interface(), option.value.Interface())
+ }
+
+ if showdef {
+ def, _ = convertToString(option.value, option.tag)
+ }
+ } else if len(defs) != 0 {
+ l := len(defs) - 1
+
+ for i := 0; i < l; i++ {
+ def += quoteIfNeeded(defs[i]) + ", "
+ }
+
+ def += quoteIfNeeded(defs[l])
+ }
+
+ option.defaultLiteral = def
+}
+
+func (option *Option) shortAndLongName() string {
+ ret := &bytes.Buffer{}
+
+ if option.ShortName != 0 {
+ ret.WriteRune(defaultShortOptDelimiter)
+ ret.WriteRune(option.ShortName)
+ }
+
+ if len(option.LongName) != 0 {
+ if option.ShortName != 0 {
+ ret.WriteRune('/')
+ }
+
+ ret.WriteString(option.LongName)
+ }
+
+ return ret.String()
+}
diff --git a/vendor/github.com/jessevdk/go-flags/optstyle_other.go b/vendor/github.com/jessevdk/go-flags/optstyle_other.go
new file mode 100644
index 0000000..56dfdae
--- /dev/null
+++ b/vendor/github.com/jessevdk/go-flags/optstyle_other.go
@@ -0,0 +1,67 @@
+// +build !windows forceposix
+
+package flags
+
+import (
+ "strings"
+)
+
+const (
+ defaultShortOptDelimiter = '-'
+ defaultLongOptDelimiter = "--"
+ defaultNameArgDelimiter = '='
+)
+
+func argumentStartsOption(arg string) bool {
+ return len(arg) > 0 && arg[0] == '-'
+}
+
+func argumentIsOption(arg string) bool {
+ if len(arg) > 1 && arg[0] == '-' && arg[1] != '-' {
+ return true
+ }
+
+ if len(arg) > 2 && arg[0] == '-' && arg[1] == '-' && arg[2] != '-' {
+ return true
+ }
+
+ return false
+}
+
+// stripOptionPrefix returns the option without the prefix and whether or
+// not the option is a long option or not.
+func stripOptionPrefix(optname string) (prefix string, name string, islong bool) {
+ if strings.HasPrefix(optname, "--") {
+ return "--", optname[2:], true
+ } else if strings.HasPrefix(optname, "-") {
+ return "-", optname[1:], false
+ }
+
+ return "", optname, false
+}
+
+// splitOption attempts to split the passed option into a name and an argument.
+// When there is no argument specified, nil will be returned for it.
+func splitOption(prefix string, option string, islong bool) (string, string, *string) {
+ pos := strings.Index(option, "=")
+
+ if (islong && pos >= 0) || (!islong && pos == 1) {
+ rest := option[pos+1:]
+ return option[:pos], "=", &rest
+ }
+
+ return option, "", nil
+}
+
+// addHelpGroup adds a new group that contains default help parameters.
+func (c *Command) addHelpGroup(showHelp func() error) *Group {
+ var help struct {
+ ShowHelp func() error `short:"h" long:"help" description:"Show this help message"`
+ }
+
+ help.ShowHelp = showHelp
+ ret, _ := c.AddGroup("Help Options", "", &help)
+ ret.isBuiltinHelp = true
+
+ return ret
+}
diff --git a/vendor/github.com/jessevdk/go-flags/optstyle_windows.go b/vendor/github.com/jessevdk/go-flags/optstyle_windows.go
new file mode 100644
index 0000000..f3f28ae
--- /dev/null
+++ b/vendor/github.com/jessevdk/go-flags/optstyle_windows.go
@@ -0,0 +1,108 @@
+// +build !forceposix
+
+package flags
+
+import (
+ "strings"
+)
+
+// Windows uses a front slash for both short and long options. Also it uses
+// a colon for name/argument delimter.
+const (
+ defaultShortOptDelimiter = '/'
+ defaultLongOptDelimiter = "/"
+ defaultNameArgDelimiter = ':'
+)
+
+func argumentStartsOption(arg string) bool {
+ return len(arg) > 0 && (arg[0] == '-' || arg[0] == '/')
+}
+
+func argumentIsOption(arg string) bool {
+ // Windows-style options allow front slash for the option
+ // delimiter.
+ if len(arg) > 1 && arg[0] == '/' {
+ return true
+ }
+
+ if len(arg) > 1 && arg[0] == '-' && arg[1] != '-' {
+ return true
+ }
+
+ if len(arg) > 2 && arg[0] == '-' && arg[1] == '-' && arg[2] != '-' {
+ return true
+ }
+
+ return false
+}
+
+// stripOptionPrefix returns the option without the prefix and whether or
+// not the option is a long option or not.
+func stripOptionPrefix(optname string) (prefix string, name string, islong bool) {
+ // Determine if the argument is a long option or not. Windows
+ // typically supports both long and short options with a single
+ // front slash as the option delimiter, so handle this situation
+ // nicely.
+ possplit := 0
+
+ if strings.HasPrefix(optname, "--") {
+ possplit = 2
+ islong = true
+ } else if strings.HasPrefix(optname, "-") {
+ possplit = 1
+ islong = false
+ } else if strings.HasPrefix(optname, "/") {
+ possplit = 1
+ islong = len(optname) > 2
+ }
+
+ return optname[:possplit], optname[possplit:], islong
+}
+
+// splitOption attempts to split the passed option into a name and an argument.
+// When there is no argument specified, nil will be returned for it.
+func splitOption(prefix string, option string, islong bool) (string, string, *string) {
+ if len(option) == 0 {
+ return option, "", nil
+ }
+
+ // Windows typically uses a colon for the option name and argument
+ // delimiter while POSIX typically uses an equals. Support both styles,
+ // but don't allow the two to be mixed. That is to say /foo:bar and
+ // --foo=bar are acceptable, but /foo=bar and --foo:bar are not.
+ var pos int
+ var sp string
+
+ if prefix == "/" {
+ sp = ":"
+ pos = strings.Index(option, sp)
+ } else if len(prefix) > 0 {
+ sp = "="
+ pos = strings.Index(option, sp)
+ }
+
+ if (islong && pos >= 0) || (!islong && pos == 1) {
+ rest := option[pos+1:]
+ return option[:pos], sp, &rest
+ }
+
+ return option, "", nil
+}
+
+// addHelpGroup adds a new group that contains default help parameters.
+func (c *Command) addHelpGroup(showHelp func() error) *Group {
+ // Windows CLI applications typically use /? for help, so make both
+ // that available as well as the POSIX style h and help.
+ var help struct {
+ ShowHelpWindows func() error `short:"?" description:"Show this help message"`
+ ShowHelpPosix func() error `short:"h" long:"help" description:"Show this help message"`
+ }
+
+ help.ShowHelpWindows = showHelp
+ help.ShowHelpPosix = showHelp
+
+ ret, _ := c.AddGroup("Help Options", "", &help)
+ ret.isBuiltinHelp = true
+
+ return ret
+}
diff --git a/vendor/github.com/jessevdk/go-flags/parser.go b/vendor/github.com/jessevdk/go-flags/parser.go
new file mode 100644
index 0000000..fd2fd5f
--- /dev/null
+++ b/vendor/github.com/jessevdk/go-flags/parser.go
@@ -0,0 +1,700 @@
+// Copyright 2012 Jesse van den Kieboom. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package flags
+
+import (
+ "bytes"
+ "fmt"
+ "os"
+ "path"
+ "sort"
+ "strings"
+ "unicode/utf8"
+)
+
+// A Parser provides command line option parsing. It can contain several
+// option groups each with their own set of options.
+type Parser struct {
+ // Embedded, see Command for more information
+ *Command
+
+ // A usage string to be displayed in the help message.
+ Usage string
+
+ // Option flags changing the behavior of the parser.
+ Options Options
+
+ // NamespaceDelimiter separates group namespaces and option long names
+ NamespaceDelimiter string
+
+ // UnknownOptionsHandler is a function which gets called when the parser
+ // encounters an unknown option. The function receives the unknown option
+ // name, a SplitArgument which specifies its value if set with an argument
+ // separator, and the remaining command line arguments.
+ // It should return a new list of remaining arguments to continue parsing,
+ // or an error to indicate a parse failure.
+ UnknownOptionHandler func(option string, arg SplitArgument, args []string) ([]string, error)
+
+ // CompletionHandler is a function gets called to handle the completion of
+ // items. By default, the items are printed and the application is exited.
+ // You can override this default behavior by specifying a custom CompletionHandler.
+ CompletionHandler func(items []Completion)
+
+ // CommandHandler is a function that gets called to handle execution of a
+ // command. By default, the command will simply be executed. This can be
+ // overridden to perform certain actions (such as applying global flags)
+ // just before the command is executed. Note that if you override the
+ // handler it is your responsibility to call the command.Execute function.
+ //
+ // The command passed into CommandHandler may be nil in case there is no
+ // command to be executed when parsing has finished.
+ CommandHandler func(command Commander, args []string) error
+
+ internalError error
+}
+
+// SplitArgument represents the argument value of an option that was passed using
+// an argument separator.
+type SplitArgument interface {
+ // String returns the option's value as a string, and a boolean indicating
+ // if the option was present.
+ Value() (string, bool)
+}
+
+type strArgument struct {
+ value *string
+}
+
+func (s strArgument) Value() (string, bool) {
+ if s.value == nil {
+ return "", false
+ }
+
+ return *s.value, true
+}
+
+// Options provides parser options that change the behavior of the option
+// parser.
+type Options uint
+
+const (
+ // None indicates no options.
+ None Options = 0
+
+ // HelpFlag adds a default Help Options group to the parser containing
+ // -h and --help options. When either -h or --help is specified on the
+ // command line, the parser will return the special error of type
+ // ErrHelp. When PrintErrors is also specified, then the help message
+ // will also be automatically printed to os.Stdout.
+ HelpFlag = 1 << iota
+
+ // PassDoubleDash passes all arguments after a double dash, --, as
+ // remaining command line arguments (i.e. they will not be parsed for
+ // flags).
+ PassDoubleDash
+
+ // IgnoreUnknown ignores any unknown options and passes them as
+ // remaining command line arguments instead of generating an error.
+ IgnoreUnknown
+
+ // PrintErrors prints any errors which occurred during parsing to
+ // os.Stderr. In the special case of ErrHelp, the message will be printed
+ // to os.Stdout.
+ PrintErrors
+
+ // PassAfterNonOption passes all arguments after the first non option
+ // as remaining command line arguments. This is equivalent to strict
+ // POSIX processing.
+ PassAfterNonOption
+
+ // Default is a convenient default set of options which should cover
+ // most of the uses of the flags package.
+ Default = HelpFlag | PrintErrors | PassDoubleDash
+)
+
+type parseState struct {
+ arg string
+ args []string
+ retargs []string
+ positional []*Arg
+ err error
+
+ command *Command
+ lookup lookup
+}
+
+// Parse is a convenience function to parse command line options with default
+// settings. The provided data is a pointer to a struct representing the
+// default option group (named "Application Options"). For more control, use
+// flags.NewParser.
+func Parse(data interface{}) ([]string, error) {
+ return NewParser(data, Default).Parse()
+}
+
+// ParseArgs is a convenience function to parse command line options with default
+// settings. The provided data is a pointer to a struct representing the
+// default option group (named "Application Options"). The args argument is
+// the list of command line arguments to parse. If you just want to parse the
+// default program command line arguments (i.e. os.Args), then use flags.Parse
+// instead. For more control, use flags.NewParser.
+func ParseArgs(data interface{}, args []string) ([]string, error) {
+ return NewParser(data, Default).ParseArgs(args)
+}
+
+// NewParser creates a new parser. It uses os.Args[0] as the application
+// name and then calls Parser.NewNamedParser (see Parser.NewNamedParser for
+// more details). The provided data is a pointer to a struct representing the
+// default option group (named "Application Options"), or nil if the default
+// group should not be added. The options parameter specifies a set of options
+// for the parser.
+func NewParser(data interface{}, options Options) *Parser {
+ p := NewNamedParser(path.Base(os.Args[0]), options)
+
+ if data != nil {
+ g, err := p.AddGroup("Application Options", "", data)
+
+ if err == nil {
+ g.parent = p
+ }
+
+ p.internalError = err
+ }
+
+ return p
+}
+
+// NewNamedParser creates a new parser. The appname is used to display the
+// executable name in the built-in help message. Option groups and commands can
+// be added to this parser by using AddGroup and AddCommand.
+func NewNamedParser(appname string, options Options) *Parser {
+ p := &Parser{
+ Command: newCommand(appname, "", "", nil),
+ Options: options,
+ NamespaceDelimiter: ".",
+ }
+
+ p.Command.parent = p
+
+ return p
+}
+
+// Parse parses the command line arguments from os.Args using Parser.ParseArgs.
+// For more detailed information see ParseArgs.
+func (p *Parser) Parse() ([]string, error) {
+ return p.ParseArgs(os.Args[1:])
+}
+
+// ParseArgs parses the command line arguments according to the option groups that
+// were added to the parser. On successful parsing of the arguments, the
+// remaining, non-option, arguments (if any) are returned. The returned error
+// indicates a parsing error and can be used with PrintError to display
+// contextual information on where the error occurred exactly.
+//
+// When the common help group has been added (AddHelp) and either -h or --help
+// was specified in the command line arguments, a help message will be
+// automatically printed if the PrintErrors option is enabled.
+// Furthermore, the special error type ErrHelp is returned.
+// It is up to the caller to exit the program if so desired.
+func (p *Parser) ParseArgs(args []string) ([]string, error) {
+ if p.internalError != nil {
+ return nil, p.internalError
+ }
+
+ p.eachOption(func(c *Command, g *Group, option *Option) {
+ option.isSet = false
+ option.isSetDefault = false
+ option.updateDefaultLiteral()
+ })
+
+ // Add built-in help group to all commands if necessary
+ if (p.Options & HelpFlag) != None {
+ p.addHelpGroups(p.showBuiltinHelp)
+ }
+
+ compval := os.Getenv("GO_FLAGS_COMPLETION")
+
+ if len(compval) != 0 {
+ comp := &completion{parser: p}
+ items := comp.complete(args)
+
+ if p.CompletionHandler != nil {
+ p.CompletionHandler(items)
+ } else {
+ comp.print(items, compval == "verbose")
+ os.Exit(0)
+ }
+
+ return nil, nil
+ }
+
+ s := &parseState{
+ args: args,
+ retargs: make([]string, 0, len(args)),
+ }
+
+ p.fillParseState(s)
+
+ for !s.eof() {
+ arg := s.pop()
+
+ // When PassDoubleDash is set and we encounter a --, then
+ // simply append all the rest as arguments and break out
+ if (p.Options&PassDoubleDash) != None && arg == "--" {
+ s.addArgs(s.args...)
+ break
+ }
+
+ if !argumentIsOption(arg) {
+ // Note: this also sets s.err, so we can just check for
+ // nil here and use s.err later
+ if p.parseNonOption(s) != nil {
+ break
+ }
+
+ continue
+ }
+
+ var err error
+
+ prefix, optname, islong := stripOptionPrefix(arg)
+ optname, _, argument := splitOption(prefix, optname, islong)
+
+ if islong {
+ err = p.parseLong(s, optname, argument)
+ } else {
+ err = p.parseShort(s, optname, argument)
+ }
+
+ if err != nil {
+ ignoreUnknown := (p.Options & IgnoreUnknown) != None
+ parseErr := wrapError(err)
+
+ if parseErr.Type != ErrUnknownFlag || (!ignoreUnknown && p.UnknownOptionHandler == nil) {
+ s.err = parseErr
+ break
+ }
+
+ if ignoreUnknown {
+ s.addArgs(arg)
+ } else if p.UnknownOptionHandler != nil {
+ modifiedArgs, err := p.UnknownOptionHandler(optname, strArgument{argument}, s.args)
+
+ if err != nil {
+ s.err = err
+ break
+ }
+
+ s.args = modifiedArgs
+ }
+ }
+ }
+
+ if s.err == nil {
+ p.eachOption(func(c *Command, g *Group, option *Option) {
+ if option.preventDefault {
+ return
+ }
+
+ option.clearDefault()
+ })
+
+ s.checkRequired(p)
+ }
+
+ var reterr error
+
+ if s.err != nil {
+ reterr = s.err
+ } else if len(s.command.commands) != 0 && !s.command.SubcommandsOptional {
+ reterr = s.estimateCommand()
+ } else if cmd, ok := s.command.data.(Commander); ok {
+ if p.CommandHandler != nil {
+ reterr = p.CommandHandler(cmd, s.retargs)
+ } else {
+ reterr = cmd.Execute(s.retargs)
+ }
+ } else if p.CommandHandler != nil {
+ reterr = p.CommandHandler(nil, s.retargs)
+ }
+
+ if reterr != nil {
+ var retargs []string
+
+ if ourErr, ok := reterr.(*Error); !ok || ourErr.Type != ErrHelp {
+ retargs = append([]string{s.arg}, s.args...)
+ } else {
+ retargs = s.args
+ }
+
+ return retargs, p.printError(reterr)
+ }
+
+ return s.retargs, nil
+}
+
+func (p *parseState) eof() bool {
+ return len(p.args) == 0
+}
+
+func (p *parseState) pop() string {
+ if p.eof() {
+ return ""
+ }
+
+ p.arg = p.args[0]
+ p.args = p.args[1:]
+
+ return p.arg
+}
+
+func (p *parseState) peek() string {
+ if p.eof() {
+ return ""
+ }
+
+ return p.args[0]
+}
+
+func (p *parseState) checkRequired(parser *Parser) error {
+ c := parser.Command
+
+ var required []*Option
+
+ for c != nil {
+ c.eachGroup(func(g *Group) {
+ for _, option := range g.options {
+ if !option.isSet && option.Required {
+ required = append(required, option)
+ }
+ }
+ })
+
+ c = c.Active
+ }
+
+ if len(required) == 0 {
+ if len(p.positional) > 0 {
+ var reqnames []string
+
+ for _, arg := range p.positional {
+ argRequired := (!arg.isRemaining() && p.command.ArgsRequired) || arg.Required != -1 || arg.RequiredMaximum != -1
+
+ if !argRequired {
+ continue
+ }
+
+ if arg.isRemaining() {
+ if arg.value.Len() < arg.Required {
+ var arguments string
+
+ if arg.Required > 1 {
+ arguments = "arguments, but got only " + fmt.Sprintf("%d", arg.value.Len())
+ } else {
+ arguments = "argument"
+ }
+
+ reqnames = append(reqnames, "`"+arg.Name+" (at least "+fmt.Sprintf("%d", arg.Required)+" "+arguments+")`")
+ } else if arg.RequiredMaximum != -1 && arg.value.Len() > arg.RequiredMaximum {
+ if arg.RequiredMaximum == 0 {
+ reqnames = append(reqnames, "`"+arg.Name+" (zero arguments)`")
+ } else {
+ var arguments string
+
+ if arg.RequiredMaximum > 1 {
+ arguments = "arguments, but got " + fmt.Sprintf("%d", arg.value.Len())
+ } else {
+ arguments = "argument"
+ }
+
+ reqnames = append(reqnames, "`"+arg.Name+" (at most "+fmt.Sprintf("%d", arg.RequiredMaximum)+" "+arguments+")`")
+ }
+ }
+ } else {
+ reqnames = append(reqnames, "`"+arg.Name+"`")
+ }
+ }
+
+ if len(reqnames) == 0 {
+ return nil
+ }
+
+ var msg string
+
+ if len(reqnames) == 1 {
+ msg = fmt.Sprintf("the required argument %s was not provided", reqnames[0])
+ } else {
+ msg = fmt.Sprintf("the required arguments %s and %s were not provided",
+ strings.Join(reqnames[:len(reqnames)-1], ", "), reqnames[len(reqnames)-1])
+ }
+
+ p.err = newError(ErrRequired, msg)
+ return p.err
+ }
+
+ return nil
+ }
+
+ names := make([]string, 0, len(required))
+
+ for _, k := range required {
+ names = append(names, "`"+k.String()+"'")
+ }
+
+ sort.Strings(names)
+
+ var msg string
+
+ if len(names) == 1 {
+ msg = fmt.Sprintf("the required flag %s was not specified", names[0])
+ } else {
+ msg = fmt.Sprintf("the required flags %s and %s were not specified",
+ strings.Join(names[:len(names)-1], ", "), names[len(names)-1])
+ }
+
+ p.err = newError(ErrRequired, msg)
+ return p.err
+}
+
+func (p *parseState) estimateCommand() error {
+ commands := p.command.sortedVisibleCommands()
+ cmdnames := make([]string, len(commands))
+
+ for i, v := range commands {
+ cmdnames[i] = v.Name
+ }
+
+ var msg string
+ var errtype ErrorType
+
+ if len(p.retargs) != 0 {
+ c, l := closestChoice(p.retargs[0], cmdnames)
+ msg = fmt.Sprintf("Unknown command `%s'", p.retargs[0])
+ errtype = ErrUnknownCommand
+
+ if float32(l)/float32(len(c)) < 0.5 {
+ msg = fmt.Sprintf("%s, did you mean `%s'?", msg, c)
+ } else if len(cmdnames) == 1 {
+ msg = fmt.Sprintf("%s. You should use the %s command",
+ msg,
+ cmdnames[0])
+ } else {
+ msg = fmt.Sprintf("%s. Please specify one command of: %s or %s",
+ msg,
+ strings.Join(cmdnames[:len(cmdnames)-1], ", "),
+ cmdnames[len(cmdnames)-1])
+ }
+ } else {
+ errtype = ErrCommandRequired
+
+ if len(cmdnames) == 1 {
+ msg = fmt.Sprintf("Please specify the %s command", cmdnames[0])
+ } else {
+ msg = fmt.Sprintf("Please specify one command of: %s or %s",
+ strings.Join(cmdnames[:len(cmdnames)-1], ", "),
+ cmdnames[len(cmdnames)-1])
+ }
+ }
+
+ return newError(errtype, msg)
+}
+
+func (p *Parser) parseOption(s *parseState, name string, option *Option, canarg bool, argument *string) (err error) {
+ if !option.canArgument() {
+ if argument != nil {
+ return newErrorf(ErrNoArgumentForBool, "bool flag `%s' cannot have an argument", option)
+ }
+
+ err = option.set(nil)
+ } else if argument != nil || (canarg && !s.eof()) {
+ var arg string
+
+ if argument != nil {
+ arg = *argument
+ } else {
+ arg = s.pop()
+
+ if argumentIsOption(arg) && !(option.isSignedNumber() && len(arg) > 1 && arg[0] == '-' && arg[1] >= '0' && arg[1] <= '9') {
+ return newErrorf(ErrExpectedArgument, "expected argument for flag `%s', but got option `%s'", option, arg)
+ } else if p.Options&PassDoubleDash != 0 && arg == "--" {
+ return newErrorf(ErrExpectedArgument, "expected argument for flag `%s', but got double dash `--'", option)
+ }
+ }
+
+ if option.tag.Get("unquote") != "false" {
+ arg, err = unquoteIfPossible(arg)
+ }
+
+ if err == nil {
+ err = option.set(&arg)
+ }
+ } else if option.OptionalArgument {
+ option.empty()
+
+ for _, v := range option.OptionalValue {
+ err = option.set(&v)
+
+ if err != nil {
+ break
+ }
+ }
+ } else {
+ err = newErrorf(ErrExpectedArgument, "expected argument for flag `%s'", option)
+ }
+
+ if err != nil {
+ if _, ok := err.(*Error); !ok {
+ err = newErrorf(ErrMarshal, "invalid argument for flag `%s' (expected %s): %s",
+ option,
+ option.value.Type(),
+ err.Error())
+ }
+ }
+
+ return err
+}
+
+func (p *Parser) parseLong(s *parseState, name string, argument *string) error {
+ if option := s.lookup.longNames[name]; option != nil {
+ // Only long options that are required can consume an argument
+ // from the argument list
+ canarg := !option.OptionalArgument
+
+ return p.parseOption(s, name, option, canarg, argument)
+ }
+
+ return newErrorf(ErrUnknownFlag, "unknown flag `%s'", name)
+}
+
+func (p *Parser) splitShortConcatArg(s *parseState, optname string) (string, *string) {
+ c, n := utf8.DecodeRuneInString(optname)
+
+ if n == len(optname) {
+ return optname, nil
+ }
+
+ first := string(c)
+
+ if option := s.lookup.shortNames[first]; option != nil && option.canArgument() {
+ arg := optname[n:]
+ return first, &arg
+ }
+
+ return optname, nil
+}
+
+func (p *Parser) parseShort(s *parseState, optname string, argument *string) error {
+ if argument == nil {
+ optname, argument = p.splitShortConcatArg(s, optname)
+ }
+
+ for i, c := range optname {
+ shortname := string(c)
+
+ if option := s.lookup.shortNames[shortname]; option != nil {
+ // Only the last short argument can consume an argument from
+ // the arguments list, and only if it's non optional
+ canarg := (i+utf8.RuneLen(c) == len(optname)) && !option.OptionalArgument
+
+ if err := p.parseOption(s, shortname, option, canarg, argument); err != nil {
+ return err
+ }
+ } else {
+ return newErrorf(ErrUnknownFlag, "unknown flag `%s'", shortname)
+ }
+
+ // Only the first option can have a concatted argument, so just
+ // clear argument here
+ argument = nil
+ }
+
+ return nil
+}
+
+func (p *parseState) addArgs(args ...string) error {
+ for len(p.positional) > 0 && len(args) > 0 {
+ arg := p.positional[0]
+
+ if err := convert(args[0], arg.value, arg.tag); err != nil {
+ p.err = err
+ return err
+ }
+
+ if !arg.isRemaining() {
+ p.positional = p.positional[1:]
+ }
+
+ args = args[1:]
+ }
+
+ p.retargs = append(p.retargs, args...)
+ return nil
+}
+
+func (p *Parser) parseNonOption(s *parseState) error {
+ if len(s.positional) > 0 {
+ return s.addArgs(s.arg)
+ }
+
+ if len(s.command.commands) > 0 && len(s.retargs) == 0 {
+ if cmd := s.lookup.commands[s.arg]; cmd != nil {
+ s.command.Active = cmd
+ cmd.fillParseState(s)
+
+ return nil
+ } else if !s.command.SubcommandsOptional {
+ s.addArgs(s.arg)
+ return newErrorf(ErrUnknownCommand, "Unknown command `%s'", s.arg)
+ }
+ }
+
+ if (p.Options & PassAfterNonOption) != None {
+ // If PassAfterNonOption is set then all remaining arguments
+ // are considered positional
+ if err := s.addArgs(s.arg); err != nil {
+ return err
+ }
+
+ if err := s.addArgs(s.args...); err != nil {
+ return err
+ }
+
+ s.args = []string{}
+ } else {
+ return s.addArgs(s.arg)
+ }
+
+ return nil
+}
+
+func (p *Parser) showBuiltinHelp() error {
+ var b bytes.Buffer
+
+ p.WriteHelp(&b)
+ return newError(ErrHelp, b.String())
+}
+
+func (p *Parser) printError(err error) error {
+ if err != nil && (p.Options&PrintErrors) != None {
+ flagsErr, ok := err.(*Error)
+
+ if ok && flagsErr.Type == ErrHelp {
+ fmt.Fprintln(os.Stdout, err)
+ } else {
+ fmt.Fprintln(os.Stderr, err)
+ }
+ }
+
+ return err
+}
+
+func (p *Parser) clearIsSet() {
+ p.eachCommand(func(c *Command) {
+ c.eachGroup(func(g *Group) {
+ for _, option := range g.options {
+ option.isSet = false
+ }
+ })
+ }, true)
+}
diff --git a/vendor/github.com/jessevdk/go-flags/termsize.go b/vendor/github.com/jessevdk/go-flags/termsize.go
new file mode 100644
index 0000000..df97e7e
--- /dev/null
+++ b/vendor/github.com/jessevdk/go-flags/termsize.go
@@ -0,0 +1,28 @@
+// +build !windows,!plan9,!solaris
+
+package flags
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+type winsize struct {
+ row, col uint16
+ xpixel, ypixel uint16
+}
+
+func getTerminalColumns() int {
+ ws := winsize{}
+
+ if tIOCGWINSZ != 0 {
+ syscall.Syscall(syscall.SYS_IOCTL,
+ uintptr(0),
+ uintptr(tIOCGWINSZ),
+ uintptr(unsafe.Pointer(&ws)))
+
+ return int(ws.col)
+ }
+
+ return 80
+}
diff --git a/vendor/github.com/jessevdk/go-flags/termsize_linux.go b/vendor/github.com/jessevdk/go-flags/termsize_linux.go
new file mode 100644
index 0000000..e3975e2
--- /dev/null
+++ b/vendor/github.com/jessevdk/go-flags/termsize_linux.go
@@ -0,0 +1,7 @@
+// +build linux
+
+package flags
+
+const (
+ tIOCGWINSZ = 0x5413
+)
diff --git a/vendor/github.com/jessevdk/go-flags/termsize_nosysioctl.go b/vendor/github.com/jessevdk/go-flags/termsize_nosysioctl.go
new file mode 100644
index 0000000..2a9bbe0
--- /dev/null
+++ b/vendor/github.com/jessevdk/go-flags/termsize_nosysioctl.go
@@ -0,0 +1,7 @@
+// +build windows plan9 solaris
+
+package flags
+
+func getTerminalColumns() int {
+ return 80
+}
diff --git a/vendor/github.com/jessevdk/go-flags/termsize_other.go b/vendor/github.com/jessevdk/go-flags/termsize_other.go
new file mode 100644
index 0000000..3082151
--- /dev/null
+++ b/vendor/github.com/jessevdk/go-flags/termsize_other.go
@@ -0,0 +1,7 @@
+// +build !darwin,!freebsd,!netbsd,!openbsd,!linux
+
+package flags
+
+const (
+ tIOCGWINSZ = 0
+)
diff --git a/vendor/github.com/jessevdk/go-flags/termsize_unix.go b/vendor/github.com/jessevdk/go-flags/termsize_unix.go
new file mode 100644
index 0000000..fcc1186
--- /dev/null
+++ b/vendor/github.com/jessevdk/go-flags/termsize_unix.go
@@ -0,0 +1,7 @@
+// +build darwin freebsd netbsd openbsd
+
+package flags
+
+const (
+ tIOCGWINSZ = 0x40087468
+)
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 0000000..416e545
--- /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) (eq bool) {
+ eq = bytes.Equal(sl1, sl2)
+ if !eq {
+ 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) (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]) {
+ 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) (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())
+ }
+ }
+ 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) (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)
+ } 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 0000000..0649564
--- /dev/null
+++ b/vendor/github.com/jung-kurt/gofpdf/contrib/barcode/barcode.go
@@ -0,0 +1,238 @@
+// 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"
+ "github.com/boombuler/barcode"
+ "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"
+ "image/jpeg"
+ "io"
+ "strconv"
+ "sync"
+)
+
+// 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)
+}
+
+// Barcode puts a registered barcode in the current page.
+//
+// The size should be specified in the units used to create the PDF document.
+//
+// Positioning with x, y and flow is inherited from Fpdf.Image().
+func Barcode(pdf barcodePdf, code string, x, y, 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)
+
+ if info == nil {
+ bcode, err := barcode.Scale(
+ unscaled,
+ int(convertTo96Dpi(pdf, w)),
+ int(convertTo96Dpi(pdf, h)),
+ )
+
+ if err != nil {
+ pdf.SetError(err)
+ return
+ }
+
+ err = registerScaledBarcode(pdf, bname, bcode)
+ if err != nil {
+ pdf.SetError(err)
+ return
+ }
+ }
+
+ pdf.Image(bname, x, y, 0, 0, flow, "jpg", 0, "")
+}
+
+// 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
+}
+
+// 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 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
+}
diff --git a/vendor/github.com/jung-kurt/gofpdf/contrib/ghostscript/ghostscript.go b/vendor/github.com/jung-kurt/gofpdf/contrib/ghostscript/ghostscript.go
new file mode 100644
index 0000000..3196c34
--- /dev/null
+++ b/vendor/github.com/jung-kurt/gofpdf/contrib/ghostscript/ghostscript.go
@@ -0,0 +1,68 @@
+package main
+
+// This command demonstrates the use of ghotscript to reduce the size
+// of generated PDFs. This is based on a comment made by farkerhaiku:
+// https://github.com/jung-kurt/gofpdf/issues/57#issuecomment-185843315
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+
+ "github.com/jung-kurt/gofpdf"
+)
+
+func report(fileStr string, err error) {
+ if err == nil {
+ var info os.FileInfo
+ info, err = os.Stat(fileStr)
+ if err == nil {
+ fmt.Printf("%s: OK, size %d\n", fileStr, info.Size())
+ } else {
+ fmt.Printf("%s: bad stat\n", fileStr)
+ }
+ } else {
+ fmt.Printf("%s: %s\n", fileStr, err)
+ }
+}
+
+func newPdf() (pdf *gofpdf.Fpdf) {
+ pdf = gofpdf.New("P", "mm", "A4", "../../font")
+ pdf.SetCompression(false)
+ pdf.AddFont("Calligrapher", "", "calligra.json")
+ pdf.AddPage()
+ pdf.SetFont("Calligrapher", "", 35)
+ pdf.Cell(0, 10, "Enjoy new fonts with FPDF!")
+ return
+}
+
+func full(name string) {
+ report(name, newPdf().OutputFileAndClose(name))
+}
+
+func min(name string) {
+ cmd := exec.Command("gs", "-sDEVICE=pdfwrite",
+ "-dCompatibilityLevel=1.4",
+ "-dPDFSETTINGS=/screen", "-dNOPAUSE", "-dQUIET",
+ "-dBATCH", "-sOutputFile="+name, "-")
+ inPipe, err := cmd.StdinPipe()
+ if err == nil {
+ errChan := make(chan error, 1)
+ go func() {
+ errChan <- cmd.Start()
+ }()
+ err = newPdf().Output(inPipe)
+ if err == nil {
+ report(name, <-errChan)
+ } else {
+ report(name, err)
+ }
+ } else {
+ report(name, err)
+ }
+}
+
+func main() {
+ full("full.pdf")
+ min("min.pdf")
+}
diff --git a/vendor/github.com/jung-kurt/gofpdf/contrib/httpimg/httpimg.go b/vendor/github.com/jung-kurt/gofpdf/contrib/httpimg/httpimg.go
new file mode 100644
index 0000000..7cbf281
--- /dev/null
+++ b/vendor/github.com/jung-kurt/gofpdf/contrib/httpimg/httpimg.go
@@ -0,0 +1,43 @@
+package httpimg
+
+import (
+ "io"
+ "net/http"
+
+ "github.com/jung-kurt/gofpdf"
+)
+
+// httpimgPdf is a partial interface that only implements the functions we need
+// from the PDF generator to put the HTTP images on the PDF.
+type httpimgPdf interface {
+ GetImageInfo(imageStr string) *gofpdf.ImageInfoType
+ ImageTypeFromMime(mimeStr string) string
+ RegisterImageReader(imgName, tp string, r io.Reader) *gofpdf.ImageInfoType
+ SetError(err error)
+}
+
+// Register registers a HTTP image. Downloading the image from the provided URL
+// and adding it to the PDF but not adding it to the page. Use Image() with the
+// same URL to add the image to the page.
+func Register(f httpimgPdf, urlStr, tp string) (info *gofpdf.ImageInfoType) {
+ info = f.GetImageInfo(urlStr)
+
+ if info != nil {
+ return
+ }
+
+ resp, err := http.Get(urlStr)
+
+ if err != nil {
+ f.SetError(err)
+ return
+ }
+
+ defer resp.Body.Close()
+
+ if tp == "" {
+ tp = f.ImageTypeFromMime(resp.Header["Content-Type"][0])
+ }
+
+ return f.RegisterImageReader(urlStr, tp, resp.Body)
+}
diff --git a/vendor/github.com/jung-kurt/gofpdf/contrib/tiff/tiff.go b/vendor/github.com/jung-kurt/gofpdf/contrib/tiff/tiff.go
new file mode 100644
index 0000000..3f13a38
--- /dev/null
+++ b/vendor/github.com/jung-kurt/gofpdf/contrib/tiff/tiff.go
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 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 tiff allows standard (LZW-compressed) TIFF images to be used in
+// documents generated with gofpdf.
+package tiff
+
+import (
+ "bytes"
+ "fmt"
+ "image"
+ "image/png"
+ "io"
+ "os"
+
+ "github.com/jung-kurt/gofpdf"
+ "golang.org/x/image/tiff"
+)
+
+// RegisterReader registers a TIFF image, adding it to the PDF file but not
+// adding it to the page. imgName specifies the name that will be used in the
+// call to Image() that actually places the image in the document. options
+// specifies various image properties; in this case, the ImageType property
+// should be set to "tiff". The TIFF image is a reader from the reader
+// specified by r.
+func RegisterReader(fpdf *gofpdf.Fpdf, imgName string, options gofpdf.ImageOptions, r io.Reader) (info *gofpdf.ImageInfoType) {
+ var err error
+ var img image.Image
+ var buf bytes.Buffer
+ if fpdf.Ok() {
+ if options.ImageType == "tiff" || options.ImageType == "tif" {
+ img, err = tiff.Decode(r)
+ if err == nil {
+ err = png.Encode(&buf, img)
+ if err == nil {
+ options.ImageType = "png"
+ info = fpdf.RegisterImageOptionsReader(imgName, options, &buf)
+ }
+ }
+ } else {
+ err = fmt.Errorf("expecting \"tiff\" or \"tif\" as image type, got \"%s\"", options.ImageType)
+ }
+ if err != nil {
+ fpdf.SetError(err)
+ }
+ }
+ return
+}
+
+// RegisterFile registers a TIFF image, adding it to the PDF file but not
+// adding it to the page. imgName specifies the name that will be used in the
+// call to Image() that actually places the image in the document. options
+// specifies various image properties; in this case, the ImageType property
+// should be set to "tiff". The TIFF image is read from the file specified by
+// tiffFileStr.
+func RegisterFile(fpdf *gofpdf.Fpdf, imgName string, options gofpdf.ImageOptions, tiffFileStr string) (info *gofpdf.ImageInfoType) {
+ var f *os.File
+ var err error
+
+ if fpdf.Ok() {
+ f, err = os.Open(tiffFileStr)
+ if err == nil {
+ info = RegisterReader(fpdf, imgName, options, f)
+ f.Close()
+ } else {
+ fpdf.SetError(err)
+ }
+ }
+ return
+}
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 0000000..54abbd1
--- /dev/null
+++ b/vendor/github.com/jung-kurt/gofpdf/def.go
@@ -0,0 +1,378 @@
+/*
+ * 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"
+ "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
+}
+
+// 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
+type ImageInfoType struct {
+ data []byte
+ smask []byte
+ i int
+ 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
+}
+
+// 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
+}
+
+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)
+}
+
+// Fpdf is the principal structure for creating a single PDF document
+type Fpdf struct {
+ page int // current page number
+ n int // current object number
+ offsets []int // array of object offsets
+ templates map[int64]Template // templates used in this document
+ templateObjects map[int64]int // template object IDs within this document
+ 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
+ curPageSize SizeType // current page size
+ pageSizes map[int]SizeType // used for pages with non default sizes or orientations
+ 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
+ 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
+ inFooter bool // flag set when processing footer
+ footerFnc func() // function provided by app and called to write footer
+ zoomMode string // zoom display mode
+ layoutMode string // layout display mode
+ 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
+ colorFlag bool // indicates whether fill and text colors are different
+ color struct {
+ // Composite values of colors
+ draw, fill, text clrType
+ }
+}
+
+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 [256]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
+ I int // 1-based position in font list, set by font loader, not this program
+ N int // Set by font loader
+ DiffN int // Position of diff in app array, set by font loader
+}
+
+type fontInfoType struct {
+ Data []byte
+ File string
+ OriginalSize int
+ FontName string
+ Bold bool
+ IsFixedPitch bool
+ UnderlineThickness int
+ UnderlinePosition int
+ Widths [256]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 0000000..a0af537
--- /dev/null
+++ b/vendor/github.com/jung-kurt/gofpdf/doc.go
@@ -0,0 +1,234 @@
+/*
+ * 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 implements a PDF document generator with high level support for
+text, drawing and images.
+
+Features
+
+• 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
+
+gofpdf has no dependencies other than the Go standard library. All tests pass
+on Linux, Mac and Windows platforms. Like FPDF version 1.7, from which gofpdf
+is derived, this package does not yet support UTF-8 fonts. However, support is
+provided to translate UTF-8 runes to code page encodings.
+
+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. 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().
+
+In order to use a different 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 http://www.google.com/fonts/ and http://dejavu-fonts.org/.
+
+Related Packages
+
+The draw2d package (https://github.com/llgcode/draw2d) 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 (https://github.com/golang/lint) and
+go vet (https://godoc.org/golang.org/x/tools/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/) work
+nicely as a means of contributing 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
+(http://www.fpdf.org/) created by Olivier Plathey, and a number of font and
+image resources are copied directly from it. 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. Additionally, he 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. Bruno Michel
+has provided valuable assistance with the code.
+
+Roadmap
+
+• Handle UTF-8 source text natively. Until then, automatic translation of
+UTF-8 runes to code page bytes is provided.
+
+• 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 0000000..1d947b0
--- /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 0000000..35f59f1
--- /dev/null
+++ b/vendor/github.com/jung-kurt/gofpdf/font.go
@@ -0,0 +1,456 @@
+/*
+ * 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
+}
+
+// Return informations from a TrueType font
+func getInfoFromTrueType(fileStr string, msgWriter io.Writer, embed bool, encList encListType) (info fontInfoType, err error) {
+ 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(f *os.File) (s segmentType, err error) {
+ if err = binary.Read(f, 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(f, binary.LittleEndian, &s.tp); err != nil {
+ return
+ }
+ if err = binary.Read(f, binary.LittleEndian, &s.size); err != nil {
+ return
+ }
+ s.data = make([]byte, s.size)
+ _, err = f.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
+
+// Return informations from a Type1 font
+func getInfoFromType1(fileStr string, msgWriter io.Writer, embed bool, encList encListType) (info fontInfoType, err error) {
+ 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)
+}
+
+// Build 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) (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
+ }
+ 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
+ }
+ var f *os.File
+ f, err = os.Create(fileStr)
+ if err != nil {
+ return
+ }
+ defer f.Close()
+ f.Write(buf)
+ return
+}
+
+// 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) (err error) {
+ if msgWriter == nil {
+ msgWriter = ioutil.Discard
+ }
+ if !fileExist(fontFileStr) {
+ err = fmt.Errorf("font file not found: %s", fontFileStr)
+ return
+ }
+ extStr := strings.ToLower(fontFileStr[len(fontFileStr)-3:])
+ // printf("Font file extension [%s]\n", extStr)
+ var tpStr string
+ if extStr == "ttf" || extStr == "otf" {
+ tpStr = "TrueType"
+ } else if extStr == "pfb" {
+ tpStr = "Type1"
+ } else {
+ err = fmt.Errorf("unrecognized font file extension: %s", extStr)
+ return
+ }
+ var encList encListType
+ var info fontInfoType
+ encList, err = loadMap(encodingFileStr)
+ if err != nil {
+ return
+ }
+ // printf("Encoding table\n")
+ // dump(encList)
+ if tpStr == "TrueType" {
+ info, err = getInfoFromTrueType(fontFileStr, msgWriter, embed, encList)
+ if err != nil {
+ return
+ }
+ } else {
+ info, err = getInfoFromType1(fontFileStr, msgWriter, embed, encList)
+ if err != nil {
+ return
+ }
+ }
+ 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
+ }
+ defer f.Close()
+ cmp := zlib.NewWriter(f)
+ cmp.Write(info.Data)
+ cmp.Close()
+ 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
+ }
+ fmt.Fprintf(msgWriter, "Font definition file successfully generated: %s\n", defFileStr)
+ return
+}
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 0000000..66cff72
--- /dev/null
+++ b/vendor/github.com/jung-kurt/gofpdf/fpdf.go
@@ -0,0 +1,3856 @@
+/*
+ * 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"
+ }
+ 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.state = 0
+ f.fonts = make(map[string]fontDefType)
+ f.fontFiles = make(map[string]fontFileType)
+ f.diffs = make([]string, 0, 8)
+ f.templates = make(map[int64]Template)
+ f.templateObjects = make(map[int64]int)
+ 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.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["letter"] = SizeType{612, 792}
+ f.stdPageSizes["legal"] = SizeType{612, 1008}
+ 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
+ orientationStr = strings.ToLower(orientationStr)
+ 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.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", or "Legal". 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
+}
+
+// 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
+}
+
+// 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.
+//
+// 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.
+//
+// This method is demonstrated in the example for AddPage().
+func (f *Fpdf) SetFooterFunc(fnc func()) {
+ f.footerFnc = fnc
+}
+
+// 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
+}
+
+// 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
+}
+
+// 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
+}
+
+// Begin 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
+ if f.footerFnc != nil {
+ f.inFooter = true
+ f.footerFnc()
+ 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.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 {
+ // Page footer
+ if f.footerFnc != nil {
+ f.inFooter = true
+ f.footerFnc()
+ 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
+ }
+ // 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
+}
+
+type clrType struct {
+ r, g, b float64
+ ir, ig, ib int
+ gray bool
+ str string
+}
+
+func colorComp(v int) (int, float64) {
+ if v < 0 {
+ v = 0
+ } else if v > 255 {
+ v = 255
+ }
+ return v, float64(v) / 255.0
+}
+
+func colorValue(r, g, b int, grayStr, fullStr string) (clr clrType) {
+ clr.ir, clr.r = colorComp(r)
+ clr.ig, clr.g = colorComp(g)
+ clr.ib, clr.b = colorComp(b)
+ 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.color.draw = colorValue(r, g, b, "G", "RG")
+ if f.page > 0 {
+ f.out(f.color.draw.str)
+ }
+}
+
+// GetDrawColor returns the current draw color as RGB components (0 - 255).
+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.color.fill = colorValue(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 current fill color as RGB components (0 - 255).
+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.color.text = colorValue(r, g, b, "g", "rg")
+ f.colorFlag = f.color.fill.str != f.color.text.str
+}
+
+// GetTextColor returns the current text color as RGB components (0 - 255).
+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 := 0
+ for _, ch := range []byte(s) {
+ if ch == 0 {
+ break
+ }
+ w += f.currentFont.Cw[ch]
+ }
+ return float64(w) * f.fontSize / 1000
+}
+
+// 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.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
+ }
+ if capStyle != f.capStyle {
+ 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
+ }
+ if joinStyle != f.joinStyle {
+ 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
+ if !slicesEqual(scaled, f.dashArray) || dashPhase != f.dashPhase {
+ 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)
+}
+
+// Outputs current point
+func (f *Fpdf) point(x, y float64) {
+ f.outf("%.2f %.2f m", x*f.k, (f.h-y)*f.k)
+}
+
+// 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 || (alpha == f.alpha && blendModeStr == f.blendMode) {
+ 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 int, r1, g1, b1 int, r2, g2, b2 int, x1, y1 float64, x2, y2 float64, r float64) {
+ pos := len(f.gradientList)
+ clr1 := colorValue(r1, g1, b1, "", "")
+ clr2 := colorValue(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 int, 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 int, 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) {
+ if fileStr == "" {
+ fileStr = strings.Replace(familyStr, " ", "", -1) + strings.ToLower(styleStr) + ".json"
+ }
+
+ 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)
+}
+
+// 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 string, styleStr string, jsonFileBytes []byte, zFileBytes []byte) {
+ if f.err != nil {
+ return
+ }
+
+ // load font key
+ var ok bool
+ fontkey := getFontKey(familyStr, styleStr)
+ _, ok = f.fonts[fontkey]
+
+ if ok {
+ return
+ }
+
+ // load font definitions
+ var info fontDefType
+ err := json.Unmarshal(jsonFileBytes, &info)
+
+ if err != nil {
+ f.err = err
+ }
+
+ if f.err != nil {
+ return
+ }
+
+ // search existing encodings
+ info.I = len(f.fonts)
+
+ 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
+ }
+ info.I = len(f.fonts)
+ 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 selected
+ if f.fontFamily == familyStr && f.fontStyle == styleStr && f.fontSizePt == size {
+ return
+ }
+ // 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.page > 0 {
+ f.outf("BT /F%d %.2f Tf ET", f.currentFont.I, f.fontSizePt)
+ }
+ return
+}
+
+// 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) {
+ if f.fontSizePt == size {
+ return
+ }
+ f.fontSizePt = size
+ f.fontSize = size / f.k
+ if f.page > 0 {
+ f.outf("BT /F%d %.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) {
+ if f.fontSize == size {
+ return
+ }
+ f.fontSizePt = size * f.k
+ f.fontSize = size
+ if f.page > 0 {
+ f.outf("BT /F%d %.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}
+}
+
+// Add 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) {
+ s := sprintf("BT %.2f %.2f Td (%s) Tj ET", x*f.k, (f.h-y)*f.k, f.escape(txtStr))
+ 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)
+}
+
+// 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.
+//
+// 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 string, 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
+ }
+ 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
+ if strings.Index(alignStr, "R") != -1 {
+ dx = w - f.cMargin - f.GetStringWidth(txtStr)
+ } else if strings.Index(alignStr, "C") != -1 {
+ dx = (w - f.GetStringWidth(txtStr)) / 2
+ } else {
+ dx = f.cMargin
+ }
+ // Vertical alignment
+ if strings.Index(alignStr, "T") != -1 {
+ dy = (f.fontSize - h) / 2.0
+ } else if strings.Index(alignStr, "B") != -1 {
+ dy = (h - f.fontSize) / 2.0
+ } else if strings.Index(alignStr, "A") != -1 {
+ 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
+ } else {
+ dy = 0
+ }
+ if f.colorFlag {
+ s.printf("q %s ", f.color.text.str)
+ }
+ txt2 := strings.Replace(txtStr, "\\", "\\\\", -1)
+ txt2 = strings.Replace(txt2, "(", "\\(", -1)
+ txt2 = strings.Replace(txt2, ")", "\\)", -1)
+ // if strings.Contains(txt2, "end of excerpt") {
+ // dbg("f.h %.2f, f.y %.2f, h %.2f, f.fontSize %.2f, k %.2f", f.h, f.y, h, f.fontSize, k)
+ // }
+ s.printf("BT %.2f %.2f Td (%s) Tj ET", (f.x+dx)*k, (f.h-(f.y+dy+.5*h+.3*f.fontSize))*k, txt2)
+ //BT %.2F %.2F Td (%s) Tj ET',($this->x+$dx)*$k,($this->h-($this->y+.5*$h+.3*$this->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
+}
+
+// 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.
+//
+// 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.
+//
+// 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 := (w - 2*f.cMargin) * 1000 / f.fontSize
+ s := strings.Replace(txtStr, "\r", "", -1)
+ nb := len(s)
+ // if nb > 0 && s[nb-1:nb] == "\n" {
+ 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.0
+ ls := 0.0
+ ns := 0
+ nl := 1
+ for i < nb {
+ // Get next character
+ c := []byte(s)[i]
+ if c == '\n' {
+ // Explicit line break
+ if f.ws > 0 {
+ f.ws = 0
+ f.out("0 Tw")
+ }
+ 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++
+ }
+ l += float64(cw[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")
+ }
+ f.CellFormat(w, h, s[j:i], b, 2, alignStr, fill, 0, "")
+ } else {
+ if alignStr == "J" {
+ if ns > 1 {
+ f.ws = (wmax - ls) / 1000 * f.fontSize / float64(ns-1)
+ } else {
+ f.ws = 0
+ }
+ f.outf("%.3f Tw", f.ws*f.k)
+ }
+ 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"
+ }
+ f.CellFormat(w, h, s[j:i], b, 2, alignStr, fill, 0, "")
+ f.x = f.lMargin
+}
+
+// Output 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)
+ nb := len(s)
+ sep := -1
+ i := 0
+ j := 0
+ l := 0.0
+ nl := 1
+ for i < nb {
+ // Get next character
+ c := []byte(s)[i]
+ if c == '\n' {
+ // Explicit line break
+ 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[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++
+ }
+ f.CellFormat(w, h, s[j:i], "", 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 {
+ 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) - rMargin)
+ 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, 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 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%d 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, 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.
+type ImageOptions struct {
+ ImageType string
+ ReadDpi 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
+ }
+ info.i = len(f.images) + 1
+ 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]
+}
+
+// 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
+ }
+}
+
+// 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
+}
+
+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++
+ 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
+ }
+ // 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
+}
+
+// Format 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}
+}
+
+// Extract 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
+}
+
+// Extract 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(buf *bytes.Buffer) (val int32) {
+ err := binary.Read(buf, binary.BigEndian, &val)
+ if err != nil {
+ f.err = err
+ }
+ return
+}
+
+func (f *Fpdf) readByte(buf *bytes.Buffer) (val byte) {
+ err := binary.Read(buf, binary.BigEndian, &val)
+ if err != nil {
+ f.err = err
+ }
+ return
+}
+
+// Extract 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)
+}
+
+// Begin 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")
+}
+
+// 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")
+ }
+}
+
+// Add a buffered line to the document
+func (f *Fpdf) outbuf(b *bytes.Buffer) {
+ if f.state == 2 {
+ f.pages[f.page].ReadFrom(b)
+ f.pages[f.page].WriteString("\n")
+ } else {
+ f.buffer.ReadFrom(b)
+ 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(buf *bytes.Buffer) {
+ f.outbuf(buf)
+}
+
+// Add 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
+// comparsion. Specify a zero-value time to revert to the default behavior.
+func (f *Fpdf) SetCreationDate(tm time.Time) {
+ f.creationDate = tm
+}
+
+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
+ nbStr := sprintf("%d", nb)
+ for n := 1; n <= nb; n++ {
+ s := f.pages[n].String()
+ if strings.Contains(s, f.aliasNbPagesStr) {
+ s = strings.Replace(s, f.aliasNbPagesStr, nbStr, -1)
+ f.pages[n].Truncate(0)
+ f.pages[n].WriteString(s)
+ }
+ }
+ }
+ 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("<</Type /Page")
+ f.out("/Parent 1 0 R")
+ pageSize, ok = f.pageSizes[n]
+ if ok {
+ f.outf("/MediaBox [0 0 %.2f %.2f]", pageSize.Wd, pageSize.Ht)
+ }
+ f.out("/Resources 2 0 R")
+ // Links
+ if len(f.pageLinks[n]) > 0 {
+ var annots fmtBuffer
+ annots.printf("/Annots [")
+ for _, pl := range f.pageLinks[n] {
+ annots.printf("<</Type /Annot /Subtype /Link /Rect [%.2f %.2f %.2f %.2f] /Border [0 0 0] ",
+ pl.x, pl.y, pl.x+pl.wd, pl.y-pl.ht)
+ if pl.link == 0 {
+ annots.printf("/A <</S /URI /URI %s>>>>", 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 <</Type /Group /S /Transparency /CS /DeviceRGB>>")
+ }
+ 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("<</Filter /FlateDecode /Length %d>>", len(data))
+ f.putstream(data)
+ } else {
+ f.outf("<</Length %d>>", 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("<</Type /Pages")
+ var kids fmtBuffer
+ kids.printf("/Kids [")
+ for i := 0; i < nb; i++ {
+ kids.printf("%d 0 R ", 3+2*i)
+ }
+ kids.printf("]")
+ f.out(kids.String())
+ f.outf("/Count %d", nb)
+ f.outf("/MediaBox [0 0 %.2f %.2f]", wPt, hPt)
+ 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("<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences [%s]>>", 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.Strings(fileList)
+ }
+ for _, file = range fileList {
+ info = f.fontFiles[file]
+ // Font file embedding
+ 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
+ }
+ }
+
+ // dbg("font file [%s], ext [%s]", file, file[len(file)-2:])
+ 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("<</Length %d", len(font))
+ if compressed {
+ f.out("/Filter /FlateDecode")
+ }
+ f.outf("/Length1 %d", info.length1)
+ if info.length2 > 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.Strings(keyList)
+ }
+ 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
+ if tp == "Core" {
+ // Core font
+ f.newobj()
+ f.out("<</Type /Font")
+ f.outf("/BaseFont /%s", name)
+ f.out("/Subtype /Type1")
+ if name != "Symbol" && name != "ZapfDingbats" {
+ f.out("/Encoding /WinAnsiEncoding")
+ }
+ f.out(">>")
+ f.out("endobj")
+ } else if tp == "Type1" || tp == "TrueType" {
+ // Additional Type1 or TrueType/OpenType font
+ f.newobj()
+ f.out("<</Type /Font")
+ f.outf("/BaseFont /%s", name)
+ f.outf("/Subtype /%s", tp)
+ f.out("/FirstChar 32 /LastChar 255")
+ f.outf("/Widths %d 0 R", f.n+1)
+ f.outf("/FontDescriptor %d 0 R", f.n+2)
+ if font.DiffN > 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("<</Type /FontDescriptor /FontName /%s ", name)
+ s.printf("/Ascent %d ", font.Desc.Ascent)
+ s.printf("/Descent %d ", font.Desc.Descent)
+ s.printf("/CapHeight %d ", font.Desc.CapHeight)
+ s.printf("/Flags %d ", font.Desc.Flags)
+ s.printf("/FontBBox [%d %d %d %d] ", font.Desc.FontBBox.Xmin, font.Desc.FontBBox.Ymin,
+ font.Desc.FontBBox.Xmax, font.Desc.FontBBox.Ymax)
+ s.printf("/ItalicAngle %d ", font.Desc.ItalicAngle)
+ s.printf("/StemV %d ", font.Desc.StemV)
+ s.printf("/MissingWidth %d ", font.Desc.MissingWidth)
+ var suffix string
+ if tp != "Type1" {
+ suffix = "2"
+ }
+ s.printf("/FontFile%s %d 0 R>>", suffix, f.fontFiles[font.File].n)
+ f.out(s.String())
+ f.out("endobj")
+ } else {
+ f.err = fmt.Errorf("unsupported font type: %s", tp)
+ return
+ // Allow for additional types
+ // $mtd = 'put'.strtolower($type);
+ // if(!method_exists($this,$mtd))
+ // $this->Error('Unsupported font type: '.$type);
+ // $this->$mtd($font);
+ }
+ }
+ }
+ return
+}
+
+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.Strings(keyList)
+ }
+ for _, key = range keyList {
+ f.putimage(f.images[key])
+ }
+}
+
+func (f *Fpdf) putimage(info *ImageInfoType) {
+ f.newobj()
+ info.n = f.n
+ f.out("<</Type /XObject")
+ f.out("/Subtype /Image")
+ f.outf("/Width %d", int(info.w))
+ f.outf("/Height %d", int(info.h))
+ if info.cs == "Indexed" {
+ f.outf("/ColorSpace [/Indexed /DeviceRGB %d %d 0 R]", len(info.pal)/3-1, f.n+1)
+ } else {
+ f.outf("/ColorSpace /%s", info.cs)
+ if info.cs == "DeviceCMYK" {
+ f.out("/Decode [1 0 1 0 1 0 1 0]")
+ }
+ }
+ f.outf("/BitsPerComponent %d", info.bpc)
+ if len(info.f) > 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("<</Filter /FlateDecode /Length %d>>", len(pal))
+ f.putstream(pal)
+ } else {
+ f.outf("<</Length %d>>", 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.Strings(keyList)
+ }
+ for _, key = range keyList {
+ image = f.images[key]
+ f.outf("/I%d %d 0 R", image.i, image.n)
+ }
+ }
+ {
+ var keyList []int64
+ var key int64
+ 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%d %d 0 R", id, 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.Strings(keyList)
+ }
+ for _, key = range keyList {
+ font = f.fonts[key]
+ f.outf("/F%d %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()
+}
+
+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("<</Type /ExtGState /ca %s /CA %s /BM /%s>>",
+ 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("<</FunctionType 2 /Domain [0.0 1.0] /C0 [%s] /C1 [%s] /N 1>>", gr.clr1Str, gr.clr2Str)
+ f.out("endobj")
+ f1 = f.n
+ }
+ f.newobj()
+ f.outf("<</ShadingType %d /ColorSpace /DeviceRGB", gr.tp)
+ if gr.tp == 2 {
+ f.outf("/Coords [%.5f %.5f %.5f %.5f] /Function %d 0 R /Extend [true true]>>",
+ 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) putresources() {
+ if f.err != nil {
+ return
+ }
+ f.layerPutLayers()
+ f.putBlendModes()
+ f.putGradients()
+ f.putfonts()
+ if f.err != nil {
+ return
+ }
+ f.putimages()
+ f.putTemplates()
+ // Resource dictionary
+ f.offsets[2] = f.buffer.Len()
+ f.out("2 0 obj")
+ f.out("<<")
+ f.putresourcedict()
+ f.out(">>")
+ f.out("endobj")
+ 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()
+}
+
+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) 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("<</Title %s", f.textstring(o.text))
+ f.outf("/Parent %d 0 R", n+o.parent)
+ if o.prev != -1 {
+ f.outf("/Prev %d 0 R", n+o.prev)
+ }
+ if o.next != -1 {
+ f.outf("/Next %d 0 R", n+o.next)
+ }
+ if o.first != -1 {
+ f.outf("/First %d 0 R", n+o.first)
+ }
+ if o.last != -1 {
+ f.outf("/Last %d 0 R", n+o.last)
+ }
+ f.outf("/Dest [%d 0 R /XYZ 0 %.2f null]", 1+2*o.p, (f.h-o.y)*f.k)
+ f.out("/Count 0>>")
+ f.out("endobj")
+ }
+ f.newobj()
+ f.outlineRoot = f.n
+ f.outf("<</Type /Outlines /First %d 0 R", n)
+ f.outf("/Last %d 0 R>>", 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()
+ // 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 0000000..9cda397
--- /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/htmlbasic.go b/vendor/github.com/jung-kurt/gofpdf/htmlbasic.go
new file mode 100644
index 0000000..ee3be7d
--- /dev/null
+++ b/vendor/github.com/jung-kurt/gofpdf/htmlbasic.go
@@ -0,0 +1,211 @@
+/*
+ * 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" {
+ 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 "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"
+ }
+ }
+ }
+}
diff --git a/vendor/github.com/jung-kurt/gofpdf/internal/example/example.go b/vendor/github.com/jung-kurt/gofpdf/internal/example/example.go
new file mode 100644
index 0000000..edf2388
--- /dev/null
+++ b/vendor/github.com/jung-kurt/gofpdf/internal/example/example.go
@@ -0,0 +1,126 @@
+// 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 example provides some helper routines for the test packages of
+// gofpdf and its various contributed packages located beneath the contrib
+// directory.
+package example
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "strings"
+ "time"
+
+ "github.com/jung-kurt/gofpdf"
+)
+
+var gofpdfDir string
+
+func init() {
+ setRoot()
+ gofpdf.SetDefaultCompression(false)
+ gofpdf.SetDefaultCatalogSort(true)
+ gofpdf.SetDefaultCreationDate(time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC))
+}
+
+// Assign the relative path to the gofpdfDir directory based on current working
+// directory
+func setRoot() {
+ wdStr, err := os.Getwd()
+ if err == nil {
+ gofpdfDir = ""
+ list := strings.Split(filepath.ToSlash(wdStr), "/")
+ for j := len(list) - 1; j >= 0 && list[j] != "gofpdf"; j-- {
+ gofpdfDir = filepath.Join(gofpdfDir, "..")
+ }
+ } else {
+ panic(err)
+ }
+}
+
+// ImageFile returns a qualified filename in which the path to the image
+// directory is prepended to the specified filename.
+func ImageFile(fileStr string) string {
+ return filepath.Join(gofpdfDir, "image", fileStr)
+}
+
+// FontDir returns the path to the font directory.
+func FontDir() string {
+ return filepath.Join(gofpdfDir, "font")
+}
+
+// FontFile returns a qualified filename in which the path to the font
+// directory is prepended to the specified filename.
+func FontFile(fileStr string) string {
+ return filepath.Join(FontDir(), fileStr)
+}
+
+// TextFile returns a qualified filename in which the path to the text
+// directory is prepended to the specified filename.
+func TextFile(fileStr string) string {
+ return filepath.Join(gofpdfDir, "text", fileStr)
+}
+
+// PdfDir returns the path to the PDF output directory.
+func PdfDir() string {
+ return filepath.Join(gofpdfDir, "pdf")
+}
+
+// PdfFile returns a qualified filename in which the path to the PDF output
+// directory is prepended to the specified filename.
+func PdfFile(fileStr string) string {
+ return filepath.Join(PdfDir(), fileStr)
+}
+
+// Filename returns a qualified filename in which the example PDF directory
+// path is prepended and the suffix ".pdf" is appended to the specified
+// filename.
+func Filename(baseStr string) string {
+ return PdfFile(baseStr + ".pdf")
+}
+
+// referenceCompare compares the specified file with the file's reference copy
+// located in the 'reference' subdirectory. All bytes of the two files are
+// compared except for the value of the /CreationDate field in the PDF. This
+// function succeeds if both files are equivalent except for their
+// /CreationDate values or if the reference file does not exist.
+func referenceCompare(fileStr string) (err error) {
+ var refFileStr, refDirStr, dirStr, baseFileStr string
+ dirStr, baseFileStr = filepath.Split(fileStr)
+ refDirStr = filepath.Join(dirStr, "reference")
+ err = os.MkdirAll(refDirStr, 0755)
+ if err == nil {
+ refFileStr = filepath.Join(refDirStr, baseFileStr)
+ err = gofpdf.ComparePDFFiles(fileStr, refFileStr)
+ }
+ return
+}
+
+// Summary generates a predictable report for use by test examples. If the
+// specified error is nil, the filename delimiters are normalized and the
+// filename printed to standard output with a success message. If the specified
+// error is not nil, its String() value is printed to standard output.
+func Summary(err error, fileStr string) {
+ if err == nil {
+ err = referenceCompare(fileStr)
+ }
+ if err == nil {
+ fileStr = filepath.ToSlash(fileStr)
+ fmt.Printf("Successfully generated %s\n", fileStr)
+ } else {
+ fmt.Println(err)
+ }
+}
diff --git a/vendor/github.com/jung-kurt/gofpdf/internal/files/bin/bin.go b/vendor/github.com/jung-kurt/gofpdf/internal/files/bin/bin.go
new file mode 100644
index 0000000..5b56313
--- /dev/null
+++ b/vendor/github.com/jung-kurt/gofpdf/internal/files/bin/bin.go
@@ -0,0 +1,28 @@
+package main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+)
+
+func main() {
+ var buf []byte
+ var pos int
+ var b byte
+ var err error
+ buf, err = ioutil.ReadAll(os.Stdin)
+ if err == nil {
+ for _, b = range buf {
+ fmt.Printf("0x%02X, ", b)
+ pos++
+ if pos >= 16 {
+ fmt.Println("")
+ pos = 0
+ }
+ }
+ }
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "%s\n", err)
+ }
+}
diff --git a/vendor/github.com/jung-kurt/gofpdf/internal/files/files.go b/vendor/github.com/jung-kurt/gofpdf/internal/files/files.go
new file mode 100644
index 0000000..7f73413
--- /dev/null
+++ b/vendor/github.com/jung-kurt/gofpdf/internal/files/files.go
@@ -0,0 +1,1691 @@
+package files
+
+// CalligraJson is embedded byte slice for calligra.json
+var CalligraJson = []byte{
+ 0x7B, 0x22, 0x54, 0x70, 0x22, 0x3A, 0x22, 0x54, 0x72, 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x22,
+ 0x2C, 0x22, 0x4E, 0x61, 0x6D, 0x65, 0x22, 0x3A, 0x22, 0x43, 0x61, 0x6C, 0x6C, 0x69, 0x67, 0x72,
+ 0x61, 0x70, 0x68, 0x65, 0x72, 0x52, 0x65, 0x67, 0x75, 0x6C, 0x61, 0x72, 0x22, 0x2C, 0x22, 0x44,
+ 0x65, 0x73, 0x63, 0x22, 0x3A, 0x7B, 0x22, 0x41, 0x73, 0x63, 0x65, 0x6E, 0x74, 0x22, 0x3A, 0x38,
+ 0x39, 0x39, 0x2C, 0x22, 0x44, 0x65, 0x73, 0x63, 0x65, 0x6E, 0x74, 0x22, 0x3A, 0x2D, 0x32, 0x33,
+ 0x34, 0x2C, 0x22, 0x43, 0x61, 0x70, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x3A, 0x38, 0x39,
+ 0x39, 0x2C, 0x22, 0x46, 0x6C, 0x61, 0x67, 0x73, 0x22, 0x3A, 0x33, 0x32, 0x2C, 0x22, 0x46, 0x6F,
+ 0x6E, 0x74, 0x42, 0x42, 0x6F, 0x78, 0x22, 0x3A, 0x7B, 0x22, 0x58, 0x6D, 0x69, 0x6E, 0x22, 0x3A,
+ 0x2D, 0x31, 0x37, 0x33, 0x2C, 0x22, 0x59, 0x6D, 0x69, 0x6E, 0x22, 0x3A, 0x2D, 0x32, 0x33, 0x34,
+ 0x2C, 0x22, 0x58, 0x6D, 0x61, 0x78, 0x22, 0x3A, 0x31, 0x33, 0x32, 0x38, 0x2C, 0x22, 0x59, 0x6D,
+ 0x61, 0x78, 0x22, 0x3A, 0x38, 0x39, 0x39, 0x7D, 0x2C, 0x22, 0x49, 0x74, 0x61, 0x6C, 0x69, 0x63,
+ 0x41, 0x6E, 0x67, 0x6C, 0x65, 0x22, 0x3A, 0x30, 0x2C, 0x22, 0x53, 0x74, 0x65, 0x6D, 0x56, 0x22,
+ 0x3A, 0x37, 0x30, 0x2C, 0x22, 0x4D, 0x69, 0x73, 0x73, 0x69, 0x6E, 0x67, 0x57, 0x69, 0x64, 0x74,
+ 0x68, 0x22, 0x3A, 0x38, 0x30, 0x30, 0x7D, 0x2C, 0x22, 0x55, 0x70, 0x22, 0x3A, 0x2D, 0x32, 0x30,
+ 0x30, 0x2C, 0x22, 0x55, 0x74, 0x22, 0x3A, 0x32, 0x30, 0x2C, 0x22, 0x43, 0x77, 0x22, 0x3A, 0x5B,
+ 0x38, 0x30, 0x30, 0x2C, 0x38, 0x30, 0x30, 0x2C, 0x38, 0x30, 0x30, 0x2C, 0x38, 0x30, 0x30, 0x2C,
+ 0x38, 0x30, 0x30, 0x2C, 0x38, 0x30, 0x30, 0x2C, 0x38, 0x30, 0x30, 0x2C, 0x38, 0x30, 0x30, 0x2C,
+ 0x38, 0x30, 0x30, 0x2C, 0x38, 0x30, 0x30, 0x2C, 0x38, 0x30, 0x30, 0x2C, 0x38, 0x30, 0x30, 0x2C,
+ 0x38, 0x30, 0x30, 0x2C, 0x38, 0x30, 0x30, 0x2C, 0x38, 0x30, 0x30, 0x2C, 0x38, 0x30, 0x30, 0x2C,
+ 0x38, 0x30, 0x30, 0x2C, 0x38, 0x30, 0x30, 0x2C, 0x38, 0x30, 0x30, 0x2C, 0x38, 0x30, 0x30, 0x2C,
+ 0x38, 0x30, 0x30, 0x2C, 0x38, 0x30, 0x30, 0x2C, 0x38, 0x30, 0x30, 0x2C, 0x38, 0x30, 0x30, 0x2C,
+ 0x38, 0x30, 0x30, 0x2C, 0x38, 0x30, 0x30, 0x2C, 0x38, 0x30, 0x30, 0x2C, 0x38, 0x30, 0x30, 0x2C,
+ 0x38, 0x30, 0x30, 0x2C, 0x38, 0x30, 0x30, 0x2C, 0x38, 0x30, 0x30, 0x2C, 0x38, 0x30, 0x30, 0x2C,
+ 0x32, 0x38, 0x32, 0x2C, 0x33, 0x32, 0x34, 0x2C, 0x34, 0x30, 0x35, 0x2C, 0x35, 0x38, 0x34, 0x2C,
+ 0x36, 0x33, 0x32, 0x2C, 0x39, 0x38, 0x30, 0x2C, 0x37, 0x37, 0x36, 0x2C, 0x32, 0x35, 0x39, 0x2C,
+ 0x32, 0x39, 0x39, 0x2C, 0x32, 0x39, 0x39, 0x2C, 0x33, 0x37, 0x37, 0x2C, 0x36, 0x30, 0x30, 0x2C,
+ 0x32, 0x35, 0x39, 0x2C, 0x34, 0x33, 0x32, 0x2C, 0x32, 0x35, 0x34, 0x2C, 0x35, 0x39, 0x37, 0x2C,
+ 0x35, 0x32, 0x39, 0x2C, 0x32, 0x39, 0x38, 0x2C, 0x34, 0x35, 0x31, 0x2C, 0x33, 0x35, 0x39, 0x2C,
+ 0x35, 0x32, 0x35, 0x2C, 0x34, 0x32, 0x33, 0x2C, 0x34, 0x36, 0x34, 0x2C, 0x34, 0x31, 0x37, 0x2C,
+ 0x34, 0x35, 0x37, 0x2C, 0x34, 0x37, 0x39, 0x2C, 0x32, 0x37, 0x35, 0x2C, 0x32, 0x38, 0x32, 0x2C,
+ 0x36, 0x30, 0x30, 0x2C, 0x36, 0x30, 0x30, 0x2C, 0x36, 0x30, 0x30, 0x2C, 0x35, 0x30, 0x31, 0x2C,
+ 0x38, 0x30, 0x30, 0x2C, 0x37, 0x34, 0x33, 0x2C, 0x36, 0x33, 0x36, 0x2C, 0x35, 0x39, 0x38, 0x2C,
+ 0x37, 0x31, 0x32, 0x2C, 0x36, 0x30, 0x38, 0x2C, 0x35, 0x36, 0x32, 0x2C, 0x36, 0x38, 0x30, 0x2C,
+ 0x37, 0x35, 0x36, 0x2C, 0x33, 0x30, 0x38, 0x2C, 0x33, 0x31, 0x34, 0x2C, 0x36, 0x37, 0x36, 0x2C,
+ 0x35, 0x35, 0x32, 0x2C, 0x31, 0x30, 0x34, 0x31, 0x2C, 0x38, 0x31, 0x37, 0x2C, 0x37, 0x32, 0x39,
+ 0x2C, 0x35, 0x36, 0x39, 0x2C, 0x36, 0x39, 0x38, 0x2C, 0x36, 0x37, 0x34, 0x2C, 0x36, 0x31, 0x38,
+ 0x2C, 0x36, 0x37, 0x33, 0x2C, 0x38, 0x30, 0x35, 0x2C, 0x37, 0x35, 0x33, 0x2C, 0x31, 0x32, 0x33,
+ 0x38, 0x2C, 0x37, 0x31, 0x36, 0x2C, 0x37, 0x35, 0x34, 0x2C, 0x35, 0x39, 0x39, 0x2C, 0x33, 0x31,
+ 0x35, 0x2C, 0x34, 0x36, 0x33, 0x2C, 0x33, 0x31, 0x35, 0x2C, 0x36, 0x30, 0x30, 0x2C, 0x35, 0x34,
+ 0x37, 0x2C, 0x32, 0x37, 0x38, 0x2C, 0x35, 0x38, 0x31, 0x2C, 0x35, 0x36, 0x34, 0x2C, 0x34, 0x34,
+ 0x30, 0x2C, 0x35, 0x37, 0x31, 0x2C, 0x34, 0x35, 0x30, 0x2C, 0x33, 0x34, 0x37, 0x2C, 0x36, 0x32,
+ 0x38, 0x2C, 0x36, 0x31, 0x31, 0x2C, 0x32, 0x38, 0x33, 0x2C, 0x32, 0x38, 0x33, 0x2C, 0x35, 0x36,
+ 0x30, 0x2C, 0x32, 0x35, 0x32, 0x2C, 0x39, 0x37, 0x36, 0x2C, 0x35, 0x39, 0x35, 0x2C, 0x35, 0x30,
+ 0x38, 0x2C, 0x35, 0x34, 0x39, 0x2C, 0x35, 0x34, 0x30, 0x2C, 0x33, 0x39, 0x35, 0x2C, 0x34, 0x34,
+ 0x31, 0x2C, 0x33, 0x30, 0x37, 0x2C, 0x36, 0x31, 0x34, 0x2C, 0x35, 0x35, 0x36, 0x2C, 0x39, 0x31,
+ 0x35, 0x2C, 0x35, 0x35, 0x39, 0x2C, 0x35, 0x39, 0x37, 0x2C, 0x34, 0x35, 0x32, 0x2C, 0x33, 0x31,
+ 0x35, 0x2C, 0x32, 0x32, 0x32, 0x2C, 0x33, 0x31, 0x35, 0x2C, 0x36, 0x30, 0x30, 0x2C, 0x38, 0x30,
+ 0x30, 0x2C, 0x38, 0x30, 0x30, 0x2C, 0x38, 0x30, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C,
+ 0x37, 0x38, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x32, 0x37, 0x38, 0x2C, 0x30, 0x2C, 0x30, 0x2C,
+ 0x30, 0x2C, 0x31, 0x30, 0x36, 0x34, 0x2C, 0x38, 0x30, 0x30, 0x2C, 0x30, 0x2C, 0x38, 0x30, 0x30,
+ 0x2C, 0x38, 0x30, 0x30, 0x2C, 0x32, 0x35, 0x39, 0x2C, 0x32, 0x35, 0x39, 0x2C, 0x34, 0x37, 0x30,
+ 0x2C, 0x34, 0x37, 0x30, 0x2C, 0x35, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x36, 0x30, 0x30,
+ 0x2C, 0x32, 0x37, 0x38, 0x2C, 0x39, 0x39, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x37, 0x39, 0x30,
+ 0x2C, 0x38, 0x30, 0x30, 0x2C, 0x38, 0x30, 0x30, 0x2C, 0x37, 0x35, 0x34, 0x2C, 0x32, 0x38, 0x32,
+ 0x2C, 0x33, 0x32, 0x34, 0x2C, 0x34, 0x35, 0x30, 0x2C, 0x36, 0x34, 0x30, 0x2C, 0x35, 0x31, 0x38,
+ 0x2C, 0x36, 0x30, 0x33, 0x2C, 0x30, 0x2C, 0x35, 0x31, 0x39, 0x2C, 0x32, 0x35, 0x34, 0x2C, 0x38,
+ 0x30, 0x30, 0x2C, 0x33, 0x34, 0x39, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x34, 0x33, 0x32, 0x2C, 0x38,
+ 0x30, 0x30, 0x2C, 0x32, 0x37, 0x38, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x32,
+ 0x37, 0x38, 0x2C, 0x36, 0x31, 0x34, 0x2C, 0x30, 0x2C, 0x32, 0x35, 0x34, 0x2C, 0x32, 0x37, 0x38,
+ 0x2C, 0x30, 0x2C, 0x33, 0x30, 0x35, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x35,
+ 0x30, 0x31, 0x2C, 0x37, 0x34, 0x33, 0x2C, 0x37, 0x34, 0x33, 0x2C, 0x37, 0x34, 0x33, 0x2C, 0x37,
+ 0x34, 0x33, 0x2C, 0x37, 0x34, 0x33, 0x2C, 0x37, 0x34, 0x33, 0x2C, 0x31, 0x30, 0x36, 0x30, 0x2C,
+ 0x35, 0x39, 0x38, 0x2C, 0x36, 0x30, 0x38, 0x2C, 0x36, 0x30, 0x38, 0x2C, 0x36, 0x30, 0x38, 0x2C,
+ 0x36, 0x30, 0x38, 0x2C, 0x33, 0x30, 0x38, 0x2C, 0x33, 0x30, 0x38, 0x2C, 0x33, 0x30, 0x38, 0x2C,
+ 0x33, 0x30, 0x38, 0x2C, 0x30, 0x2C, 0x38, 0x31, 0x37, 0x2C, 0x37, 0x32, 0x39, 0x2C, 0x37, 0x32,
+ 0x39, 0x2C, 0x37, 0x32, 0x39, 0x2C, 0x37, 0x32, 0x39, 0x2C, 0x37, 0x32, 0x39, 0x2C, 0x30, 0x2C,
+ 0x37, 0x32, 0x39, 0x2C, 0x38, 0x30, 0x35, 0x2C, 0x38, 0x30, 0x35, 0x2C, 0x38, 0x30, 0x35, 0x2C,
+ 0x38, 0x30, 0x35, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x36, 0x38, 0x38, 0x2C, 0x35, 0x38, 0x31, 0x2C,
+ 0x35, 0x38, 0x31, 0x2C, 0x35, 0x38, 0x31, 0x2C, 0x35, 0x38, 0x31, 0x2C, 0x35, 0x38, 0x31, 0x2C,
+ 0x35, 0x38, 0x31, 0x2C, 0x37, 0x39, 0x32, 0x2C, 0x34, 0x34, 0x30, 0x2C, 0x34, 0x35, 0x30, 0x2C,
+ 0x34, 0x35, 0x30, 0x2C, 0x34, 0x35, 0x30, 0x2C, 0x34, 0x35, 0x30, 0x2C, 0x32, 0x38, 0x33, 0x2C,
+ 0x32, 0x38, 0x33, 0x2C, 0x32, 0x38, 0x33, 0x2C, 0x32, 0x38, 0x33, 0x2C, 0x30, 0x2C, 0x35, 0x39,
+ 0x35, 0x2C, 0x35, 0x30, 0x38, 0x2C, 0x35, 0x30, 0x38, 0x2C, 0x35, 0x30, 0x38, 0x2C, 0x35, 0x30,
+ 0x38, 0x2C, 0x35, 0x30, 0x38, 0x2C, 0x30, 0x2C, 0x35, 0x30, 0x38, 0x2C, 0x36, 0x31, 0x34, 0x2C,
+ 0x36, 0x31, 0x34, 0x2C, 0x36, 0x31, 0x34, 0x2C, 0x36, 0x31, 0x34, 0x2C, 0x30, 0x2C, 0x30, 0x2C,
+ 0x35, 0x39, 0x37, 0x5D, 0x2C, 0x22, 0x45, 0x6E, 0x63, 0x22, 0x3A, 0x22, 0x63, 0x70, 0x31, 0x32,
+ 0x35, 0x32, 0x22, 0x2C, 0x22, 0x44, 0x69, 0x66, 0x66, 0x22, 0x3A, 0x22, 0x22, 0x2C, 0x22, 0x46,
+ 0x69, 0x6C, 0x65, 0x22, 0x3A, 0x22, 0x63, 0x61, 0x6C, 0x6C, 0x69, 0x67, 0x72, 0x61, 0x2E, 0x7A,
+ 0x22, 0x2C, 0x22, 0x53, 0x69, 0x7A, 0x65, 0x31, 0x22, 0x3A, 0x30, 0x2C, 0x22, 0x53, 0x69, 0x7A,
+ 0x65, 0x32, 0x22, 0x3A, 0x30, 0x2C, 0x22, 0x4F, 0x72, 0x69, 0x67, 0x69, 0x6E, 0x61, 0x6C, 0x53,
+ 0x69, 0x7A, 0x65, 0x22, 0x3A, 0x34, 0x30, 0x31, 0x32, 0x30, 0x2C, 0x22, 0x49, 0x22, 0x3A, 0x30,
+ 0x2C, 0x22, 0x4E, 0x22, 0x3A, 0x30, 0x2C, 0x22, 0x44, 0x69, 0x66, 0x66, 0x4E, 0x22, 0x3A, 0x30,
+ 0x7D}
+
+// CalligraZ is embedded byte slice for calligra.z
+var CalligraZ = []byte{
+ 0x78, 0x9C, 0xA4, 0xFC, 0x07, 0x80, 0x5C, 0x57, 0x7D, 0x2F, 0x8E, 0x9F, 0x73, 0x6E, 0xEF, 0xFD,
+ 0xDE, 0xE9, 0xBD, 0xF7, 0x9D, 0xD9, 0x99, 0xD9, 0xDD, 0x99, 0xDD, 0x99, 0xED, 0xAB, 0xDE, 0x65,
+ 0xAD, 0xEC, 0xB5, 0x25, 0x4B, 0xB2, 0xE4, 0x22, 0x5B, 0x96, 0x6D, 0x59, 0xC6, 0x35, 0xB8, 0x51,
+ 0x0D, 0xBC, 0x47, 0x4B, 0x80, 0x40, 0x02, 0x06, 0x02, 0x04, 0x4C, 0x7B, 0x0F, 0xF1, 0x02, 0xA4,
+ 0x40, 0x02, 0x04, 0x0C, 0x8F, 0xBC, 0x88, 0x16, 0x02, 0xFF, 0x07, 0x8F, 0x7F, 0x78, 0x18, 0x1B,
+ 0x1B, 0x48, 0x20, 0x41, 0xDA, 0xDF, 0xB9, 0x77, 0x66, 0x57, 0x2B, 0xD9, 0x3C, 0x7E, 0x65, 0x77,
+ 0xEE, 0xCC, 0x9D, 0x33, 0x33, 0xF7, 0x9E, 0xF2, 0x2D, 0x9F, 0x6F, 0x3B, 0x00, 0x02, 0x00, 0x64,
+ 0x50, 0x03, 0x04, 0x78, 0x6A, 0xC7, 0x9E, 0x6A, 0xFD, 0x1E, 0xA6, 0x29, 0x02, 0xF0, 0x9A, 0x77,
+ 0xE1, 0xD6, 0xED, 0x47, 0x4E, 0x1E, 0x3E, 0xE5, 0xFF, 0xD5, 0xB1, 0xDF, 0x00, 0xF0, 0x07, 0xAF,
+ 0x07, 0x80, 0xBC, 0xE6, 0xC8, 0x99, 0x3B, 0x63, 0xAF, 0xFF, 0xCD, 0xC7, 0x9E, 0x01, 0xE0, 0x95,
+ 0x5F, 0x04, 0x00, 0x9D, 0xBF, 0xE1, 0xD4, 0xF1, 0x93, 0xEF, 0xFB, 0xF6, 0xB5, 0xFF, 0xD9, 0xFD,
+ 0x0C, 0x7F, 0xFF, 0xE8, 0xF1, 0x5B, 0xEE, 0xB9, 0x21, 0xF2, 0x96, 0x57, 0x75, 0xF1, 0x7B, 0xFC,
+ 0xF9, 0x99, 0xF3, 0x27, 0x8E, 0x9E, 0x3C, 0x7B, 0xEC, 0x0B, 0xC2, 0xF5, 0xF8, 0x7A, 0x3F, 0x07,
+ 0x40, 0xF9, 0xE2, 0x89, 0x63, 0x87, 0x8F, 0x1E, 0x2F, 0xDE, 0xF5, 0x49, 0xFC, 0x5D, 0x7C, 0x0D,
+ 0xD0, 0x3A, 0x81, 0x1B, 0x58, 0x0E, 0xDD, 0x0A, 0x00, 0x4C, 0xE1, 0xF7, 0xA9, 0x13, 0x27, 0xEF,
+ 0x3C, 0xFB, 0x5F, 0xF6, 0x33, 0x0D, 0x00, 0x1E, 0xC5, 0x9F, 0x13, 0xFF, 0x74, 0xCB, 0x6D, 0x47,
+ 0x0E, 0x83, 0x4D, 0xF4, 0x5F, 0x00, 0x70, 0x2F, 0xEE, 0x0F, 0xF1, 0x83, 0x93, 0x87, 0xCF, 0x9E,
+ 0x82, 0xF7, 0xC0, 0x1F, 0xE0, 0xEF, 0x2F, 0xE1, 0xEF, 0xC7, 0x6E, 0x3D, 0x7C, 0xF2, 0x58, 0x3D,
+ 0x7D, 0xF4, 0x53, 0xF8, 0xFD, 0x09, 0xDC, 0x9F, 0xE7, 0x4F, 0xDD, 0x76, 0xC7, 0x9D, 0x9D, 0xCD,
+ 0x9D, 0x3F, 0x03, 0xE0, 0xC1, 0x87, 0xF0, 0x7B, 0xF5, 0xD4, 0xE9, 0x63, 0xA7, 0x7E, 0x4D, 0x40,
+ 0x1D, 0xF7, 0xE7, 0x2F, 0xF1, 0xF7, 0x59, 0x6F, 0xAC, 0xF8, 0x58, 0xFA, 0x8F, 0x7F, 0x50, 0xAE,
+ 0x53, 0xBA, 0xBF, 0xC2, 0xD7, 0xFC, 0x17, 0xDC, 0x02, 0xDE, 0xBB, 0x84, 0x9E, 0x5C, 0x7B, 0x5D,
+ 0xDD, 0xB3, 0x6A, 0x53, 0x35, 0xE2, 0x61, 0xFC, 0x96, 0x00, 0xC8, 0xFB, 0x85, 0xF7, 0x1B, 0xE2,
+ 0xE1, 0x55, 0x1B, 0x5F, 0xE7, 0xFC, 0xEA, 0x9E, 0x0B, 0xAF, 0xA2, 0x6A, 0xC3, 0xF6, 0x4B, 0x7F,
+ 0xFF, 0xE6, 0xB5, 0xFC, 0x1B, 0xB8, 0x19, 0x90, 0xE0, 0x30, 0x3E, 0x10, 0xBE, 0x5B, 0x1F, 0xF0,
+ 0xB8, 0xED, 0x18, 0xF8, 0x1A, 0x7E, 0x47, 0xE0, 0x33, 0x0B, 0xA2, 0xF5, 0xEF, 0x4F, 0x80, 0xF0,
+ 0xF0, 0x0C, 0x02, 0x1F, 0x38, 0x32, 0x3C, 0x47, 0x78, 0x25, 0x5E, 0x3E, 0x3C, 0x27, 0xC0, 0x41,
+ 0xF0, 0x99, 0xE1, 0x39, 0x09, 0x72, 0xB0, 0x38, 0x3C, 0xA7, 0x40, 0x1F, 0xDE, 0x31, 0x3C, 0xA7,
+ 0x71, 0xFB, 0xDF, 0xAD, 0xF7, 0x25, 0x3C, 0xE8, 0xA9, 0xF7, 0x2C, 0x82, 0xAB, 0x86, 0xE7, 0x08,
+ 0x30, 0xE0, 0xDE, 0xE1, 0x39, 0x01, 0xCA, 0xE0, 0xF5, 0xC3, 0x73, 0x12, 0x98, 0xD0, 0x19, 0x9E,
+ 0x53, 0x20, 0x06, 0xF7, 0x0C, 0xCF, 0x69, 0xDC, 0xFE, 0x71, 0xFC, 0x4D, 0x48, 0x72, 0xC3, 0x7E,
+ 0x0E, 0xCE, 0x07, 0xFD, 0x1C, 0x9C, 0x0F, 0xFA, 0x39, 0x38, 0x1F, 0xF4, 0x73, 0x70, 0x3E, 0xE8,
+ 0xE7, 0xE0, 0x7C, 0xD0, 0xCF, 0xC1, 0xB9, 0xD7, 0xCF, 0xC5, 0x63, 0xB7, 0x1E, 0x3B, 0x7D, 0xF8,
+ 0xCE, 0x63, 0x47, 0x63, 0xD7, 0xDF, 0x13, 0x5B, 0xB8, 0xED, 0xD6, 0x3B, 0x6F, 0x3B, 0x7E, 0xFA,
+ 0xF0, 0xA9, 0x13, 0xC7, 0x4E, 0xC7, 0x1A, 0x95, 0x26, 0x58, 0xC4, 0x13, 0x75, 0x2B, 0x3E, 0x4E,
+ 0xE3, 0xE9, 0xBB, 0x13, 0xBF, 0x1E, 0x05, 0x31, 0x70, 0x3D, 0xB8, 0x07, 0x3F, 0x2F, 0x80, 0xDB,
+ 0xF0, 0x27, 0x77, 0xE2, 0xE7, 0xE3, 0xDE, 0xA7, 0xA7, 0xC0, 0x09, 0xEF, 0x7B, 0x31, 0xD0, 0x00,
+ 0x15, 0xD0, 0x9C, 0x3D, 0x7C, 0xCB, 0x2D, 0x37, 0x0E, 0xAF, 0x04, 0x66, 0xF1, 0xE7, 0xB7, 0xE0,
+ 0xFF, 0x1B, 0x2F, 0xFF, 0xEE, 0xEE, 0x63, 0xC7, 0xEF, 0xBA, 0xE5, 0xF0, 0x69, 0xB0, 0x1B, 0xBF,
+ 0x39, 0x0E, 0xEE, 0xC2, 0x5F, 0x38, 0x0C, 0x4E, 0x4F, 0xDF, 0x72, 0xE7, 0x1D, 0xF7, 0xDC, 0xF1,
+ 0xA2, 0x9E, 0xC4, 0x62, 0x1B, 0xAF, 0x18, 0x5B, 0xFB, 0xE9, 0x34, 0xFE, 0xD1, 0x9D, 0xE0, 0x0E,
+ 0xDC, 0xA3, 0x3B, 0x7E, 0x7F, 0x9F, 0xF0, 0x6B, 0xEC, 0x77, 0xF5, 0x05, 0x7F, 0x72, 0x59, 0x37,
+ 0x5E, 0xF2, 0x6E, 0xFF, 0x37, 0x7F, 0xFB, 0x3B, 0x87, 0xD0, 0xAC, 0xD6, 0x5B, 0xD5, 0xF1, 0xFA,
+ 0xFF, 0x8B, 0x5E, 0x37, 0x41, 0x15, 0xD4, 0x41, 0x0B, 0x3F, 0x8F, 0x83, 0xFA, 0xFF, 0x97, 0xBE,
+ 0x61, 0xE2, 0xE9, 0x53, 0x14, 0x49, 0x20, 0x50, 0x3A, 0x33, 0x1F, 0x7B, 0x8A, 0x48, 0xCF, 0x27,
+ 0x0F, 0x9F, 0xF0, 0x25, 0x4F, 0x1C, 0x9A, 0x2B, 0x97, 0xD6, 0xDF, 0x0F, 0xDE, 0xC6, 0x62, 0x9F,
+ 0x5A, 0xFD, 0xCB, 0x31, 0xFD, 0xA3, 0x7D, 0xD8, 0x1A, 0x73, 0xDF, 0x3D, 0xD5, 0x1F, 0xD3, 0x9F,
+ 0x82, 0x2D, 0xB7, 0xB1, 0x5C, 0x82, 0x4F, 0x2D, 0x9C, 0x89, 0x2D, 0xE2, 0x5F, 0x2E, 0x1C, 0x3E,
+ 0x11, 0x3B, 0xE0, 0xFE, 0x30, 0x39, 0xF6, 0x14, 0x4A, 0xBB, 0x5F, 0x38, 0x36, 0xB6, 0x5C, 0xEE,
+ 0x43, 0xF8, 0xDA, 0x4F, 0xC1, 0xD5, 0x47, 0x31, 0x4D, 0xD6, 0x31, 0xB1, 0x3F, 0x4B, 0xC4, 0x30,
+ 0xCD, 0x31, 0x60, 0xB2, 0x1F, 0x64, 0xC8, 0xBF, 0x03, 0x34, 0xF5, 0x77, 0x90, 0x22, 0x11, 0x81,
+ 0x18, 0x1A, 0x41, 0x80, 0xE0, 0x3F, 0x62, 0x7A, 0x84, 0x60, 0xE1, 0x4C, 0xB5, 0xED, 0x03, 0xBD,
+ 0x6E, 0xAF, 0x0B, 0xAB, 0xDD, 0x0B, 0x5D, 0xF7, 0x00, 0xEA, 0x85, 0x2E, 0x7E, 0x8C, 0xD4, 0x3E,
+ 0xC6, 0x02, 0xAA, 0xD8, 0xD0, 0xE2, 0x5A, 0x16, 0x1F, 0x75, 0xF4, 0x17, 0xF5, 0x0B, 0xEF, 0x27,
+ 0x62, 0xBF, 0xFD, 0x01, 0x3E, 0xBB, 0x30, 0x87, 0xEF, 0x31, 0xBB, 0xFA, 0x73, 0xF0, 0x1F, 0xE8,
+ 0x69, 0xCC, 0xD9, 0x3E, 0x30, 0xDB, 0x0F, 0x09, 0xC0, 0x86, 0x0C, 0x81, 0x1F, 0xB4, 0xDF, 0xA6,
+ 0xFC, 0x1A, 0x03, 0x11, 0x03, 0x98, 0x15, 0x51, 0x66, 0xA1, 0xB6, 0x76, 0x93, 0x33, 0x3D, 0x1F,
+ 0xAC, 0x5E, 0x50, 0xCF, 0xE3, 0x3B, 0xA8, 0x17, 0xA0, 0xA6, 0x8F, 0xE3, 0x07, 0xAC, 0x54, 0xF0,
+ 0x9D, 0xFC, 0x1A, 0x55, 0x34, 0x14, 0x54, 0x25, 0x7A, 0xC8, 0x60, 0xE8, 0x64, 0x22, 0xD3, 0x1C,
+ 0xED, 0x11, 0x8E, 0xFD, 0x0C, 0x45, 0x9F, 0x16, 0x05, 0x8E, 0xCB, 0x17, 0x0A, 0x63, 0x47, 0x78,
+ 0x3A, 0x9A, 0x89, 0xC5, 0x89, 0x14, 0xED, 0xA0, 0xA7, 0xFF, 0xE9, 0x17, 0x5B, 0xCE, 0xDC, 0x75,
+ 0xCD, 0x35, 0xE1, 0x70, 0xE4, 0xC2, 0x63, 0xB3, 0xA8, 0x6D, 0x05, 0x7C, 0x3E, 0x14, 0x30, 0xA6,
+ 0x70, 0xAF, 0xBA, 0xF0, 0x2B, 0xF0, 0x30, 0xEE, 0x95, 0x3B, 0xF2, 0x64, 0x5F, 0xA4, 0x49, 0x84,
+ 0x59, 0x91, 0x59, 0xA1, 0x11, 0x77, 0x16, 0x9C, 0x71, 0xBB, 0xD0, 0xF5, 0x01, 0x7C, 0x4F, 0xEF,
+ 0xAE, 0x2C, 0x4D, 0x15, 0xA1, 0x02, 0xDB, 0x59, 0x7C, 0xC0, 0xC3, 0xF5, 0xBD, 0x62, 0x1A, 0x1F,
+ 0xE8, 0xE9, 0x5F, 0xF3, 0x2F, 0xA8, 0xF8, 0x70, 0xE5, 0x45, 0x12, 0x3F, 0xA5, 0xD1, 0x67, 0x40,
+ 0x10, 0x44, 0xC1, 0xD7, 0xFA, 0x47, 0x74, 0x99, 0x04, 0x8E, 0x8D, 0x7C, 0x7E, 0xBF, 0x2F, 0xCA,
+ 0xB1, 0x21, 0x14, 0xE1, 0x85, 0x70, 0x58, 0xC0, 0x2D, 0x96, 0x7B, 0x86, 0x4C, 0x4D, 0x95, 0x44,
+ 0xCB, 0x12, 0x7D, 0x7E, 0x14, 0xF0, 0x3E, 0x0F, 0xD2, 0x78, 0xBA, 0x03, 0x01, 0x26, 0x1C, 0xD2,
+ 0x35, 0x44, 0x41, 0x3E, 0xE8, 0x73, 0x4C, 0x83, 0x02, 0xE3, 0xD1, 0x88, 0xAA, 0x90, 0x14, 0xC1,
+ 0xCB, 0x12, 0xCF, 0xD1, 0x14, 0x15, 0xF0, 0xDB, 0x84, 0x25, 0x89, 0x02, 0x4B, 0x30, 0x10, 0xF4,
+ 0x9C, 0x2E, 0x7E, 0x80, 0xAA, 0xD3, 0xBD, 0xE0, 0x9D, 0xE1, 0xE7, 0xC7, 0x2B, 0xF2, 0xE7, 0xD7,
+ 0xFF, 0x7F, 0x3B, 0x78, 0xAD, 0xB8, 0x27, 0xBF, 0xFF, 0x83, 0x61, 0xA3, 0xF7, 0x1E, 0x9F, 0xAF,
+ 0x8D, 0x3C, 0x26, 0xE3, 0x91, 0x33, 0x49, 0xA6, 0x81, 0x8F, 0x64, 0xDB, 0x3B, 0xDA, 0x0D, 0xEF,
+ 0x68, 0x30, 0xF8, 0x50, 0x20, 0xFE, 0x08, 0xA5, 0x95, 0x53, 0xA9, 0x53, 0xF2, 0xA9, 0xC2, 0x42,
+ 0x61, 0x1F, 0x3E, 0x4E, 0x79, 0xEF, 0x6E, 0xCF, 0x2F, 0xE4, 0xF7, 0xBA, 0x2D, 0x7B, 0x93, 0x7B,
+ 0xE0, 0xAF, 0x17, 0xDE, 0xB7, 0xF0, 0x05, 0xFC, 0xB7, 0xFE, 0x82, 0x67, 0x3D, 0xB6, 0xFA, 0x72,
+ 0xB4, 0x87, 0xA8, 0x80, 0xAD, 0x60, 0x2F, 0x38, 0x04, 0x5E, 0xD1, 0x9F, 0x3D, 0xB0, 0x6D, 0x22,
+ 0x29, 0x6F, 0xDF, 0xA6, 0x14, 0xEA, 0x33, 0x33, 0xD5, 0x2A, 0xA7, 0x1C, 0xB8, 0xE6, 0x68, 0xDF,
+ 0xD7, 0x7C, 0xD3, 0x4A, 0xC4, 0xD1, 0x67, 0x66, 0xCC, 0xFA, 0x03, 0xD7, 0x1D, 0xDC, 0x5D, 0x10,
+ 0x78, 0x96, 0xE0, 0xB6, 0x17, 0x0B, 0x68, 0x2E, 0x1A, 0x51, 0xD0, 0xD5, 0x07, 0x17, 0xEB, 0xE4,
+ 0xB2, 0x4F, 0xD9, 0x72, 0xA0, 0x1A, 0x92, 0xC3, 0xD0, 0x37, 0x24, 0x9F, 0x1E, 0x54, 0xBF, 0xA7,
+ 0x7E, 0x6F, 0xC4, 0x9D, 0x98, 0x6E, 0xB7, 0xEA, 0x1E, 0x4E, 0x57, 0x3D, 0x8F, 0xA9, 0xA9, 0x77,
+ 0xA1, 0x77, 0xA1, 0x8B, 0x9F, 0xCE, 0xBB, 0x24, 0xA5, 0x76, 0x01, 0x7E, 0x06, 0x78, 0x98, 0xDE,
+ 0x58, 0xDD, 0xC1, 0x1E, 0xF6, 0x51, 0xC5, 0xB4, 0x0C, 0x15, 0x84, 0x0F, 0x68, 0x39, 0x32, 0x64,
+ 0xDA, 0x32, 0x4C, 0x26, 0xAA, 0xB0, 0x02, 0x31, 0x99, 0x41, 0xC7, 0x76, 0x9A, 0x55, 0x88, 0x29,
+ 0xAE, 0xD5, 0x9E, 0x82, 0x3D, 0x98, 0xED, 0x11, 0x32, 0xB4, 0xA6, 0x60, 0xA3, 0x1E, 0x41, 0x96,
+ 0xE9, 0xFE, 0x2C, 0x93, 0xA5, 0x2D, 0xD3, 0x8E, 0x22, 0xDB, 0x32, 0xD3, 0xD9, 0x04, 0xFE, 0xD0,
+ 0x74, 0x8C, 0x0A, 0xCC, 0x46, 0xF1, 0xB5, 0xDA, 0xAD, 0xE6, 0x28, 0x1A, 0xE5, 0xA2, 0x8E, 0x42,
+ 0x45, 0xDA, 0x09, 0x88, 0x7C, 0x39, 0x93, 0xD2, 0x55, 0x2D, 0x5B, 0x0B, 0x96, 0x0B, 0xCE, 0xCB,
+ 0xF8, 0xE8, 0xCC, 0x13, 0xEA, 0x96, 0x50, 0xCE, 0x8A, 0x4A, 0xD7, 0x42, 0x4D, 0xF3, 0xF9, 0x48,
+ 0x12, 0x7F, 0x18, 0x1E, 0x8B, 0x15, 0x03, 0x69, 0xDD, 0x09, 0x07, 0xCB, 0x1C, 0x24, 0x5F, 0x6F,
+ 0x6F, 0xBD, 0xF8, 0x45, 0x4A, 0xF6, 0xA5, 0x3B, 0x4F, 0x11, 0x63, 0x61, 0x84, 0xA0, 0xD5, 0xFB,
+ 0xB8, 0x9C, 0x30, 0x24, 0xCA, 0x9F, 0x50, 0x75, 0xA3, 0x2D, 0xF8, 0xB5, 0xBB, 0x08, 0x24, 0xF3,
+ 0x6A, 0xC1, 0x17, 0x14, 0x17, 0xEC, 0xBE, 0xF6, 0x74, 0x91, 0xC9, 0xCC, 0x06, 0x36, 0xA7, 0x2C,
+ 0x87, 0xAE, 0x2D, 0x0A, 0xBC, 0x22, 0xDB, 0xED, 0x00, 0x81, 0x44, 0x21, 0x6A, 0x6A, 0xBA, 0xC9,
+ 0x09, 0x62, 0xCE, 0xDF, 0x6C, 0x29, 0x81, 0x4C, 0x95, 0x4B, 0x2F, 0x16, 0xFE, 0xF7, 0xFB, 0x84,
+ 0x74, 0xBC, 0x19, 0xB8, 0xF8, 0x4C, 0xA4, 0x4E, 0xD6, 0xEA, 0xC5, 0x02, 0x1D, 0x6C, 0xB2, 0xAE,
+ 0xE6, 0x4B, 0xAE, 0xFE, 0x9C, 0xF8, 0x28, 0xFA, 0x1C, 0x28, 0x61, 0x01, 0x76, 0x13, 0x78, 0xA0,
+ 0xDF, 0xD8, 0xB7, 0x7B, 0x5B, 0xD9, 0xD4, 0xF9, 0xCD, 0xE5, 0x80, 0xC6, 0x64, 0xF2, 0xF1, 0xA3,
+ 0xC7, 0x0F, 0x8D, 0xB5, 0xC7, 0x5B, 0xA8, 0x3B, 0x39, 0xD9, 0xDD, 0x75, 0x30, 0x76, 0x73, 0x2A,
+ 0x1E, 0xCD, 0x1F, 0x8A, 0xAC, 0x20, 0xCC, 0xF5, 0xE1, 0x7C, 0xA2, 0xD3, 0x65, 0x21, 0xBF, 0x2E,
+ 0x37, 0x30, 0x43, 0x57, 0x2F, 0x80, 0x73, 0xE0, 0x38, 0x5C, 0xE9, 0x5D, 0xF0, 0x24, 0xC7, 0x39,
+ 0x70, 0x04, 0xCE, 0xF4, 0xCE, 0xAF, 0x91, 0x9C, 0xFB, 0x0F, 0x06, 0xBC, 0x0E, 0xBC, 0x15, 0x19,
+ 0xAC, 0x8B, 0xB7, 0x32, 0x37, 0xF3, 0x98, 0x0C, 0x4D, 0x19, 0x0D, 0x59, 0xBE, 0x39, 0x85, 0xF0,
+ 0x72, 0x28, 0xD0, 0x8C, 0x10, 0x8D, 0x7A, 0x0F, 0x8E, 0x56, 0xF0, 0x2A, 0xD1, 0x49, 0x26, 0xDB,
+ 0x6E, 0xE1, 0xC5, 0x80, 0x69, 0x85, 0xC4, 0xAB, 0x41, 0xD2, 0x98, 0x59, 0x5B, 0x3A, 0x5E, 0xAA,
+ 0xB6, 0x61, 0xCA, 0x84, 0xF7, 0xBB, 0x29, 0x18, 0x85, 0x0C, 0x5E, 0xA3, 0x08, 0x6A, 0xD4, 0xF1,
+ 0x82, 0x64, 0x92, 0x09, 0x05, 0x0E, 0x7F, 0x04, 0xBF, 0x14, 0x1B, 0xE3, 0xED, 0xA5, 0xFE, 0x35,
+ 0x9B, 0x11, 0xED, 0x73, 0x0A, 0x32, 0xC5, 0xF9, 0x23, 0x7A, 0x39, 0xD4, 0xD3, 0x48, 0x7A, 0xEE,
+ 0x5A, 0x46, 0xA5, 0x7C, 0xAC, 0x7F, 0x53, 0x73, 0x74, 0x77, 0x06, 0xF6, 0x17, 0x3A, 0xD7, 0xFB,
+ 0xAB, 0x62, 0x49, 0x8B, 0x97, 0x7C, 0x4A, 0x24, 0x66, 0x9D, 0xB9, 0xED, 0x2F, 0x36, 0xD1, 0x11,
+ 0xF6, 0xAB, 0x47, 0x9D, 0x62, 0x37, 0x72, 0xD5, 0x96, 0x7A, 0x5E, 0x94, 0x19, 0xBD, 0x56, 0x49,
+ 0x2B, 0x8D, 0xF8, 0xF4, 0x71, 0x45, 0xB7, 0x18, 0x7B, 0x6F, 0x21, 0xBA, 0x6B, 0x1A, 0xB1, 0xE3,
+ 0x3B, 0xA6, 0x59, 0xBD, 0xFE, 0xEA, 0x3C, 0x4D, 0xB3, 0xB9, 0x74, 0xB0, 0x41, 0xE7, 0x97, 0x7C,
+ 0xDD, 0x9C, 0x1E, 0xD9, 0x16, 0x8D, 0x4F, 0x3E, 0x8A, 0x48, 0x5E, 0xCD, 0xF7, 0xB6, 0x3C, 0x72,
+ 0xFD, 0x9E, 0x57, 0x26, 0x66, 0xD5, 0xBE, 0xBF, 0x36, 0x9B, 0x76, 0x38, 0x02, 0xFD, 0xC9, 0x5B,
+ 0xA0, 0x72, 0x3B, 0xC1, 0xA2, 0x8B, 0xF3, 0xFB, 0x0F, 0x19, 0x61, 0xF3, 0xE1, 0x46, 0xFE, 0xAA,
+ 0x34, 0x49, 0x2A, 0x85, 0x74, 0x79, 0x6F, 0x56, 0x3E, 0x90, 0x2C, 0xBF, 0x97, 0x20, 0x65, 0xA1,
+ 0x90, 0xBC, 0xDA, 0x95, 0x40, 0xF9, 0xD5, 0xE7, 0x88, 0x22, 0xFA, 0x63, 0x8C, 0xA0, 0x76, 0x82,
+ 0x53, 0xFD, 0x56, 0xBF, 0x97, 0xA7, 0x41, 0xC3, 0xE9, 0xCE, 0xF4, 0xFA, 0xA5, 0x5C, 0x9A, 0x22,
+ 0xB3, 0x60, 0xEB, 0x96, 0x8C, 0x30, 0x6D, 0xA7, 0xFB, 0xB3, 0x33, 0xC9, 0xC9, 0xEE, 0x11, 0x23,
+ 0xBC, 0xD0, 0xDE, 0xB9, 0x63, 0x86, 0x98, 0x46, 0x54, 0x1B, 0x85, 0xD3, 0xC9, 0xA2, 0x50, 0x82,
+ 0xC6, 0x1A, 0x03, 0x75, 0xBD, 0xC5, 0x5A, 0xE3, 0x1A, 0x57, 0xDC, 0x7B, 0x92, 0xDE, 0x7B, 0xC6,
+ 0x2D, 0xDE, 0x0A, 0xAD, 0x73, 0xCD, 0x2E, 0xC3, 0x15, 0x8E, 0x44, 0xD2, 0xC1, 0xEB, 0x93, 0x4C,
+ 0x54, 0x28, 0xCC, 0x1F, 0x98, 0x39, 0x2A, 0x84, 0xF7, 0x8A, 0xE7, 0xD6, 0xC1, 0xB3, 0x6B, 0x99,
+ 0x0A, 0xAC, 0x50, 0x49, 0x8F, 0x27, 0xF0, 0xE4, 0xF7, 0x10, 0x9E, 0x77, 0xBC, 0x64, 0xDE, 0x1B,
+ 0x8F, 0x99, 0x88, 0xA2, 0x3F, 0x10, 0x8F, 0x1D, 0x80, 0x63, 0xAF, 0xDA, 0x52, 0x34, 0x5B, 0x39,
+ 0x7F, 0x2B, 0x67, 0x8F, 0x76, 0x45, 0x35, 0x91, 0x30, 0xC5, 0xCD, 0xDB, 0xA6, 0x3A, 0x55, 0x95,
+ 0x29, 0xB6, 0x8C, 0xDC, 0x66, 0x9E, 0xB3, 0x43, 0xC1, 0x08, 0x14, 0x4B, 0x89, 0xC8, 0xFC, 0xE8,
+ 0xA6, 0xAD, 0xE8, 0x15, 0xD4, 0xAB, 0xD0, 0x1D, 0x27, 0x16, 0x26, 0x37, 0xDB, 0x5A, 0xA2, 0x90,
+ 0xA0, 0x20, 0xFC, 0x40, 0x20, 0x10, 0x4F, 0xDA, 0x13, 0xFB, 0xEE, 0x4D, 0xF2, 0x02, 0x1F, 0xCA,
+ 0xD4, 0x7D, 0xE9, 0xA5, 0x22, 0xC7, 0x1A, 0xC1, 0x68, 0x42, 0xCB, 0xEF, 0x2F, 0x85, 0x4C, 0xE8,
+ 0x0B, 0xB3, 0xAA, 0xAF, 0xAA, 0x8A, 0x86, 0xCE, 0x22, 0x21, 0x1F, 0x72, 0x26, 0x4C, 0x04, 0x23,
+ 0xBB, 0x6E, 0xBA, 0x9D, 0xC8, 0xD5, 0x13, 0x1D, 0x9B, 0x44, 0x54, 0x20, 0xA2, 0x60, 0xDC, 0xB6,
+ 0x04, 0xBF, 0x02, 0x9E, 0xF6, 0xB4, 0x43, 0xB0, 0xCF, 0x62, 0x08, 0xCA, 0xAC, 0x0C, 0xF4, 0x02,
+ 0xD6, 0x4C, 0x60, 0xA0, 0x89, 0xF0, 0x57, 0xB1, 0x26, 0x82, 0xED, 0xA7, 0xD7, 0x54, 0x01, 0xFE,
+ 0xD5, 0xF8, 0xEA, 0x2B, 0x61, 0x91, 0xD0, 0x30, 0xFE, 0x8B, 0xF7, 0x05, 0x13, 0xF8, 0x91, 0x68,
+ 0x56, 0x56, 0xC8, 0xEA, 0xF0, 0x87, 0x78, 0x52, 0x87, 0xBF, 0x8D, 0x88, 0x54, 0x91, 0x62, 0xB0,
+ 0x7C, 0xA9, 0x40, 0x2C, 0x5C, 0x10, 0x26, 0x4C, 0x84, 0x85, 0x09, 0x81, 0xA7, 0x03, 0x3A, 0xB0,
+ 0xD8, 0xF5, 0x8B, 0x72, 0x30, 0x67, 0x88, 0x02, 0x2F, 0xDA, 0x41, 0x9F, 0xC8, 0x70, 0x8F, 0xA8,
+ 0xF1, 0x82, 0x9D, 0x4A, 0xA7, 0x7C, 0x55, 0x5B, 0x50, 0x12, 0xDB, 0xC7, 0x33, 0xD9, 0xFA, 0x9D,
+ 0xD5, 0xE6, 0x7D, 0x57, 0x4D, 0x07, 0x04, 0xB5, 0x17, 0x59, 0x38, 0x75, 0xDB, 0xD2, 0xF2, 0x1F,
+ 0x5F, 0x33, 0xEE, 0x62, 0xD1, 0xD5, 0x57, 0x82, 0x17, 0xD6, 0xEE, 0x6F, 0x09, 0x0A, 0x42, 0xBE,
+ 0xCA, 0x8A, 0xF0, 0x12, 0xF7, 0xB7, 0x70, 0xDF, 0xB1, 0x94, 0x92, 0x09, 0x85, 0x98, 0xC2, 0xB7,
+ 0xAF, 0xA0, 0x2A, 0xC4, 0x8B, 0x15, 0xC1, 0xEC, 0x61, 0xFF, 0x84, 0xD3, 0x39, 0x3B, 0x20, 0xD0,
+ 0xDC, 0xA3, 0xA9, 0x31, 0x9B, 0x0F, 0x64, 0x52, 0x62, 0x24, 0xDC, 0x08, 0xE6, 0x0D, 0x41, 0x87,
+ 0x5F, 0xDC, 0x59, 0x6B, 0x5F, 0x9F, 0xDA, 0x32, 0xEB, 0x13, 0xD5, 0x7E, 0xFF, 0x8D, 0xB7, 0x2D,
+ 0x15, 0xF7, 0xDD, 0xBD, 0x32, 0x66, 0x58, 0x81, 0x6A, 0x62, 0xDB, 0x58, 0x66, 0x12, 0xDF, 0x3F,
+ 0x09, 0xDF, 0x02, 0xF7, 0xA3, 0x2F, 0x83, 0x49, 0xF0, 0x70, 0xBF, 0x5B, 0x4F, 0xA7, 0xA2, 0x41,
+ 0x9E, 0x03, 0x9D, 0x6C, 0x54, 0x46, 0x80, 0x2E, 0x44, 0x4B, 0x4D, 0x55, 0x05, 0x25, 0x7B, 0xB4,
+ 0x24, 0x33, 0x0C, 0x28, 0x75, 0x40, 0x49, 0xB5, 0x9A, 0xCF, 0xC7, 0x47, 0x0A, 0x7B, 0xAB, 0x95,
+ 0x52, 0x53, 0x1B, 0x45, 0x25, 0x0C, 0xC0, 0x56, 0xEC, 0xF6, 0x59, 0x98, 0x1A, 0xD0, 0xA1, 0xDB,
+ 0xE9, 0xAE, 0xDB, 0xEB, 0x2E, 0x26, 0xC4, 0xAE, 0x27, 0xBD, 0x87, 0x58, 0x40, 0xED, 0x0E, 0xE5,
+ 0xC4, 0x46, 0x71, 0xB1, 0x2E, 0x2F, 0xDC, 0x11, 0x4E, 0xA5, 0x30, 0x51, 0x62, 0xFE, 0xC6, 0x42,
+ 0x00, 0x8B, 0x6D, 0xCB, 0x9D, 0xEB, 0x4C, 0xD6, 0x15, 0xEC, 0x4C, 0x85, 0xC8, 0xBA, 0x32, 0x81,
+ 0x76, 0xE5, 0x7A, 0xB3, 0xD5, 0x76, 0xEC, 0x46, 0xBD, 0x9D, 0x99, 0x84, 0xF5, 0x56, 0x83, 0x66,
+ 0xB0, 0x34, 0x77, 0x3F, 0x73, 0x6C, 0xB8, 0x5F, 0x65, 0xC5, 0x90, 0xE4, 0x04, 0x53, 0x3E, 0x89,
+ 0xD7, 0x0D, 0xA3, 0x46, 0x12, 0x18, 0x18, 0x48, 0x52, 0xC4, 0xB2, 0x59, 0xC3, 0xEF, 0xD0, 0x56,
+ 0xC0, 0x0E, 0x4A, 0x94, 0x22, 0xA5, 0x03, 0x2C, 0x17, 0xD1, 0xE6, 0x74, 0x8D, 0x62, 0x1C, 0x41,
+ 0xB6, 0x15, 0xD5, 0x46, 0x58, 0x7E, 0x50, 0x22, 0x67, 0xA6, 0x38, 0x53, 0x33, 0xF5, 0x89, 0xA2,
+ 0x4F, 0x88, 0xF1, 0xBA, 0x42, 0x8A, 0x92, 0xC6, 0x48, 0x7A, 0xD4, 0xE2, 0x39, 0x32, 0x98, 0x4B,
+ 0x8B, 0x71, 0x22, 0x9D, 0x0D, 0xA1, 0x78, 0xD1, 0x2F, 0x38, 0x1C, 0x27, 0x50, 0x56, 0xCC, 0xB5,
+ 0x3F, 0xAA, 0x98, 0x8D, 0xF3, 0xF0, 0xDF, 0x81, 0x00, 0x76, 0xF6, 0x53, 0x14, 0x09, 0x09, 0x60,
+ 0x09, 0x3C, 0x43, 0x08, 0x00, 0xD2, 0x18, 0x02, 0x70, 0x88, 0x80, 0x88, 0x65, 0x48, 0x82, 0xE0,
+ 0xD8, 0x71, 0x02, 0x41, 0x48, 0x6D, 0x84, 0x64, 0x9E, 0xFA, 0x57, 0xBF, 0xE7, 0x3E, 0x46, 0xB0,
+ 0x90, 0xA5, 0xE1, 0xE8, 0x00, 0x11, 0xE0, 0xA9, 0x10, 0x29, 0xAC, 0xD5, 0x92, 0x56, 0xB2, 0x99,
+ 0x6C, 0x36, 0x9A, 0x0D, 0xAB, 0x81, 0xF2, 0x5F, 0x3D, 0xF4, 0x55, 0xF7, 0xE1, 0x3D, 0xB9, 0xAB,
+ 0xB6, 0xFA, 0x19, 0xF0, 0x03, 0xF0, 0xA7, 0xC0, 0x04, 0xD1, 0x3E, 0x6F, 0x88, 0x1C, 0xAD, 0x76,
+ 0x56, 0xA8, 0xEE, 0x06, 0x6A, 0xF7, 0x66, 0xD4, 0xC2, 0x34, 0xDB, 0x56, 0xC8, 0xAC, 0xAB, 0xC9,
+ 0xAA, 0xDE, 0x34, 0xB9, 0x6C, 0xFD, 0x05, 0x95, 0x0E, 0x4B, 0x2D, 0x4D, 0x08, 0x56, 0x75, 0xD9,
+ 0xA8, 0xF8, 0xF5, 0x59, 0x41, 0x22, 0x35, 0x36, 0xA5, 0x09, 0x11, 0x2B, 0x94, 0x11, 0x05, 0x2D,
+ 0x2A, 0x09, 0xCD, 0xA4, 0xE3, 0x8E, 0x2E, 0x04, 0x7E, 0x03, 0xDF, 0x08, 0xEF, 0x01, 0x1A, 0xE8,
+ 0xF4, 0x7D, 0x02, 0xD0, 0x54, 0x00, 0x44, 0x4E, 0xE0, 0xB9, 0xC7, 0x44, 0x4D, 0x55, 0x26, 0x69,
+ 0xB1, 0xB7, 0x42, 0xF7, 0xD7, 0xEE, 0xA9, 0xBA, 0x4B, 0x8E, 0x0F, 0xE0, 0xAD, 0x75, 0xD7, 0x15,
+ 0x36, 0xB8, 0x07, 0xBA, 0xE0, 0x0A, 0x1A, 0x6F, 0x0D, 0x21, 0x06, 0x25, 0xF8, 0xFE, 0x8D, 0x1E,
+ 0x84, 0x6F, 0x34, 0xF9, 0x06, 0x62, 0xA9, 0x50, 0x27, 0xB7, 0xB0, 0xE7, 0xB8, 0x93, 0xDF, 0x39,
+ 0xD2, 0x84, 0xB7, 0xFA, 0xA4, 0x09, 0x24, 0x70, 0x34, 0x09, 0xEF, 0xE6, 0x49, 0x8C, 0x5D, 0xF1,
+ 0xDD, 0x8B, 0x18, 0x83, 0xFE, 0x10, 0x8F, 0x51, 0x02, 0x85, 0xBE, 0x22, 0x60, 0xE3, 0x0A, 0xD1,
+ 0x42, 0x67, 0x05, 0x12, 0x2C, 0xA4, 0x2F, 0x07, 0x9D, 0xC0, 0x25, 0x31, 0x7C, 0x33, 0x19, 0x43,
+ 0xBE, 0xF6, 0x06, 0x84, 0xF9, 0xC3, 0x0D, 0xB0, 0xB2, 0xB9, 0x11, 0x49, 0x42, 0xC0, 0xAC, 0xBE,
+ 0x80, 0x76, 0x60, 0xAA, 0xD7, 0x81, 0xDD, 0xA7, 0x45, 0x6C, 0xA3, 0x8B, 0x33, 0x58, 0x86, 0x0E,
+ 0x59, 0xCD, 0x10, 0x3D, 0xE8, 0x88, 0x39, 0x0D, 0x77, 0x3C, 0xD9, 0xF6, 0x08, 0x0C, 0xED, 0xF0,
+ 0x27, 0x13, 0x57, 0xBF, 0xFD, 0x70, 0x44, 0x31, 0x9C, 0xAC, 0x79, 0xEC, 0x83, 0x6F, 0xDB, 0x66,
+ 0x65, 0xD1, 0x97, 0xD3, 0xD5, 0xF2, 0x03, 0xFF, 0xF4, 0xDA, 0x8A, 0x13, 0x66, 0x98, 0x57, 0xFF,
+ 0xE4, 0xFB, 0x67, 0x69, 0xC6, 0xED, 0xB7, 0xB5, 0xFA, 0x2C, 0xFC, 0x2D, 0x5A, 0x04, 0x35, 0x90,
+ 0xEB, 0x2B, 0x69, 0x53, 0xE1, 0xD3, 0xC1, 0x32, 0x1D, 0x45, 0xA8, 0x34, 0x4B, 0xCF, 0xB9, 0x48,
+ 0x07, 0x77, 0x58, 0xBD, 0x30, 0x14, 0xC6, 0xF8, 0x6E, 0x23, 0xAE, 0x9E, 0x74, 0x19, 0x1B, 0x2B,
+ 0x44, 0x17, 0xB4, 0xB8, 0xA2, 0xD5, 0x93, 0x2B, 0x64, 0xA3, 0x3E, 0x85, 0xDF, 0x57, 0x88, 0x24,
+ 0x96, 0xC9, 0xED, 0x01, 0x68, 0x81, 0xDF, 0x0F, 0x8F, 0x7F, 0x70, 0xA9, 0x73, 0xDC, 0x3F, 0x5E,
+ 0xAB, 0xE8, 0x94, 0x22, 0xB2, 0x08, 0xA9, 0xB9, 0x76, 0x28, 0x97, 0xE1, 0x47, 0xA7, 0xF7, 0x06,
+ 0x14, 0x96, 0x56, 0x94, 0xE2, 0xB8, 0x8F, 0xE6, 0xC7, 0x17, 0xE1, 0x67, 0xFA, 0xA3, 0xCD, 0x9B,
+ 0xFF, 0xDB, 0x6D, 0xFB, 0x12, 0xAD, 0x03, 0x59, 0x8B, 0xD6, 0xFD, 0x66, 0x22, 0xC0, 0xE8, 0x91,
+ 0xE2, 0x64, 0xBA, 0xDE, 0xC8, 0xD6, 0x4E, 0x76, 0xC3, 0x14, 0x4B, 0x29, 0x6C, 0x38, 0x00, 0xC5,
+ 0x69, 0xDC, 0x67, 0x76, 0xF5, 0x79, 0xF0, 0x13, 0xB4, 0x00, 0xFC, 0xA0, 0xD4, 0xD7, 0x6C, 0x0C,
+ 0x56, 0x11, 0x44, 0x7E, 0x9F, 0x43, 0x00, 0x63, 0x01, 0x2E, 0xBA, 0x4B, 0xEC, 0xEB, 0xC1, 0x01,
+ 0x38, 0x03, 0x5E, 0xCF, 0x71, 0xBF, 0x03, 0x82, 0x3B, 0xDB, 0x93, 0x58, 0x93, 0x63, 0x15, 0xC2,
+ 0x64, 0x7B, 0x70, 0x0A, 0x77, 0xD8, 0xB6, 0x30, 0x72, 0xFA, 0xC9, 0x2B, 0x08, 0xDE, 0x51, 0x05,
+ 0x5E, 0x95, 0x74, 0xC1, 0x48, 0x8D, 0x84, 0x78, 0xD5, 0x42, 0x68, 0xE9, 0xAA, 0xDA, 0x77, 0x9A,
+ 0xDD, 0xB1, 0x18, 0xA3, 0x89, 0x32, 0xA7, 0xF8, 0x63, 0x92, 0xDF, 0xDA, 0xF3, 0xF6, 0xBF, 0x77,
+ 0xE7, 0x4B, 0x5B, 0xFD, 0x35, 0xFC, 0x30, 0xDA, 0x8A, 0x79, 0x69, 0xB2, 0x1F, 0x28, 0x65, 0xC3,
+ 0x1A, 0x08, 0x39, 0x2C, 0xE4, 0xB8, 0x12, 0x05, 0xB6, 0xD6, 0xAA, 0x95, 0xF2, 0x12, 0x05, 0xE3,
+ 0xDB, 0xD0, 0xC3, 0xEE, 0xFA, 0xF4, 0x3C, 0x22, 0x1B, 0x90, 0xD8, 0x65, 0x30, 0xB0, 0xC6, 0x61,
+ 0x86, 0xC1, 0xFA, 0xCA, 0x05, 0x15, 0x83, 0xBE, 0x60, 0xC4, 0xE1, 0x76, 0x8C, 0x56, 0x50, 0x76,
+ 0x0A, 0x43, 0x07, 0xDB, 0x71, 0x49, 0xDF, 0x05, 0x89, 0x3D, 0x04, 0x3F, 0xBC, 0x23, 0x92, 0xDF,
+ 0xBF, 0x57, 0xCB, 0x09, 0x7E, 0x95, 0xD1, 0x4A, 0xDD, 0x76, 0x8C, 0xF0, 0xDD, 0x8A, 0x25, 0x09,
+ 0xAD, 0x62, 0xF9, 0x6D, 0xDE, 0x52, 0x93, 0x23, 0xA6, 0xB5, 0x48, 0x15, 0x3A, 0x7B, 0xC6, 0x26,
+ 0x66, 0xFE, 0xE0, 0x48, 0x46, 0xA4, 0x68, 0x18, 0xD1, 0x28, 0xA3, 0x38, 0xBE, 0x34, 0x1F, 0xA3,
+ 0x68, 0xE9, 0x90, 0x4C, 0x40, 0x82, 0x30, 0x38, 0x75, 0x33, 0xCB, 0xF0, 0x13, 0x5B, 0x94, 0xF2,
+ 0xA6, 0xC5, 0x1B, 0x20, 0x26, 0x59, 0x08, 0x84, 0xD5, 0x2F, 0xC3, 0x5D, 0x78, 0x16, 0x47, 0x40,
+ 0xB1, 0xAF, 0xD6, 0xCA, 0xB9, 0xA8, 0xCF, 0x90, 0x98, 0x48, 0x50, 0x57, 0x93, 0x0B, 0xF4, 0x0E,
+ 0xB7, 0xFF, 0xE0, 0x4A, 0x99, 0x57, 0x8F, 0xBA, 0xF0, 0x95, 0x70, 0x21, 0x0D, 0x26, 0xB3, 0x01,
+ 0xBE, 0x71, 0x31, 0x51, 0xFB, 0x12, 0xD4, 0xD9, 0x80, 0x56, 0xB1, 0x5C, 0xB4, 0x4C, 0xB8, 0x85,
+ 0x8A, 0xD6, 0x3B, 0x0B, 0x51, 0x23, 0x91, 0x2B, 0x33, 0xE2, 0x49, 0x93, 0x95, 0x99, 0xF1, 0x80,
+ 0x69, 0xF5, 0x6D, 0x4B, 0xD7, 0xB4, 0x93, 0x42, 0xCC, 0xA4, 0x63, 0xE5, 0x74, 0xC6, 0x54, 0x55,
+ 0xFF, 0x91, 0x77, 0xF9, 0xAA, 0x95, 0x5C, 0x4E, 0xF3, 0x59, 0xE1, 0x9A, 0x19, 0x0C, 0xEE, 0xC8,
+ 0x21, 0x2B, 0xAA, 0x9A, 0x01, 0xAC, 0x1A, 0x18, 0x56, 0xDB, 0xC3, 0xF0, 0xA4, 0x5E, 0x8F, 0xA6,
+ 0xAA, 0xA6, 0x24, 0x71, 0xD5, 0xFD, 0xB6, 0xBB, 0x12, 0xFA, 0xEA, 0x33, 0x88, 0x41, 0x07, 0x31,
+ 0xE5, 0xDE, 0xD2, 0xAF, 0x57, 0x42, 0x96, 0x04, 0x6A, 0x15, 0x2D, 0x58, 0x2B, 0x53, 0x7B, 0x46,
+ 0x8A, 0x85, 0x8C, 0x5E, 0x2C, 0x4D, 0x1A, 0x7A, 0x27, 0x5B, 0xE6, 0x30, 0x32, 0x45, 0xBC, 0xAA,
+ 0x68, 0x88, 0x2F, 0xA3, 0x6A, 0x2D, 0xB8, 0x77, 0x45, 0xDA, 0x77, 0x16, 0x5A, 0x97, 0x34, 0x80,
+ 0xCF, 0xB5, 0x37, 0xAB, 0x03, 0x81, 0xB0, 0x8E, 0x47, 0x3C, 0xFC, 0x08, 0xAF, 0xC4, 0xEF, 0x23,
+ 0x58, 0xAD, 0x41, 0x17, 0x88, 0xE3, 0x95, 0x22, 0x68, 0xA6, 0x47, 0x30, 0xC9, 0x04, 0xE6, 0x01,
+ 0xA2, 0x8D, 0xA1, 0xA2, 0x87, 0x08, 0x31, 0x3A, 0x6F, 0x44, 0x31, 0x4B, 0x28, 0xD8, 0xE6, 0x14,
+ 0xFD, 0x92, 0xA6, 0x73, 0x16, 0xCD, 0x93, 0x81, 0x46, 0x0B, 0xC3, 0x69, 0xF8, 0xA0, 0xB5, 0xA2,
+ 0xE5, 0x8E, 0x6E, 0x2B, 0xFA, 0xCD, 0x92, 0xA4, 0xEA, 0xB4, 0x41, 0x69, 0xAC, 0x63, 0x06, 0xF2,
+ 0xE4, 0x83, 0xF8, 0xC3, 0xA4, 0x9E, 0x51, 0x1D, 0x1F, 0x5E, 0xAA, 0x9B, 0xB5, 0x78, 0xA8, 0x54,
+ 0x29, 0x14, 0x51, 0x47, 0xAF, 0xCC, 0xBE, 0x7A, 0x7F, 0xC2, 0xF1, 0x8D, 0xA9, 0x76, 0x98, 0x4F,
+ 0x70, 0x29, 0x5D, 0x32, 0x1C, 0x04, 0x5F, 0xB5, 0xEB, 0x6D, 0x13, 0xEE, 0xE8, 0xF9, 0xD5, 0x2F,
+ 0xC1, 0xF7, 0xE0, 0xD5, 0x2B, 0x81, 0x91, 0xBE, 0x1A, 0x0F, 0xF9, 0x14, 0x1E, 0x70, 0x80, 0xDA,
+ 0x5C, 0xC8, 0x2C, 0x38, 0x57, 0x0D, 0x58, 0x00, 0x4B, 0xE9, 0x3C, 0x3C, 0x8C, 0x07, 0x03, 0xD6,
+ 0x57, 0x11, 0x0F, 0xA1, 0x1C, 0x1F, 0x88, 0x0B, 0x8F, 0x7F, 0xB3, 0x8C, 0xE3, 0x2D, 0x16, 0xE1,
+ 0xD1, 0x1D, 0x16, 0xBB, 0x04, 0xE6, 0x67, 0x14, 0x81, 0x1D, 0xAC, 0xAD, 0xE0, 0x7B, 0x46, 0x05,
+ 0x3D, 0x1E, 0x96, 0x1B, 0xD9, 0x02, 0x93, 0x2A, 0x76, 0xBB, 0x6C, 0xA4, 0xD5, 0x4D, 0x27, 0x13,
+ 0xA6, 0x11, 0xBD, 0x7A, 0xBF, 0x14, 0x2B, 0x27, 0x4B, 0x0D, 0x45, 0xB1, 0x27, 0xCC, 0x40, 0xB3,
+ 0x84, 0xFA, 0xCB, 0x8A, 0xA5, 0xD3, 0x94, 0x9C, 0xD5, 0x9C, 0xD2, 0xA6, 0x86, 0x92, 0xAD, 0xB4,
+ 0x2B, 0x3E, 0xCB, 0x0C, 0x63, 0xB4, 0x3B, 0xB3, 0xD4, 0x64, 0x8D, 0xC0, 0xE8, 0xD2, 0xBE, 0x80,
+ 0x9F, 0xE2, 0x09, 0xB7, 0xDF, 0xCE, 0xEA, 0x0B, 0xF0, 0x73, 0x68, 0x1B, 0x18, 0x03, 0xF9, 0xBE,
+ 0x52, 0xF2, 0x89, 0x44, 0x2B, 0xB7, 0x3C, 0x6E, 0xD4, 0x63, 0xCE, 0x41, 0x66, 0xC6, 0x93, 0x37,
+ 0xE7, 0xBB, 0x97, 0x09, 0x9C, 0x71, 0x2C, 0xDE, 0xD2, 0xB6, 0xB9, 0x26, 0x71, 0x46, 0xDD, 0xE9,
+ 0xEE, 0x51, 0x8E, 0x8B, 0xC0, 0xAD, 0x10, 0x8C, 0x20, 0x57, 0xBD, 0xBA, 0x82, 0x07, 0xDB, 0x4D,
+ 0x55, 0xE8, 0xC1, 0x41, 0x1B, 0x7E, 0x06, 0xCD, 0x3F, 0x5A, 0x1E, 0x3B, 0x66, 0x2A, 0x9D, 0x05,
+ 0x4E, 0xE0, 0x66, 0x0C, 0xC2, 0x6F, 0xF0, 0x9A, 0xC1, 0x46, 0xEA, 0x23, 0xE6, 0x58, 0x44, 0xE2,
+ 0x65, 0xCD, 0x47, 0x08, 0x6C, 0x74, 0xC6, 0x9A, 0x92, 0x32, 0xA1, 0x91, 0xF6, 0x2F, 0x18, 0x89,
+ 0x19, 0xB9, 0x66, 0xF9, 0xE8, 0xB2, 0x46, 0xDB, 0xFB, 0xAE, 0x5B, 0xE2, 0x58, 0x6E, 0x5C, 0x45,
+ 0x6A, 0x88, 0x91, 0x15, 0x26, 0xDC, 0x3C, 0xD1, 0x10, 0xD4, 0x7D, 0x13, 0x3E, 0x92, 0x72, 0x6C,
+ 0xDE, 0x8C, 0xD9, 0x13, 0x3E, 0xA6, 0xCC, 0x38, 0x47, 0x3C, 0x19, 0xF4, 0x2C, 0x7C, 0x27, 0x1E,
+ 0x47, 0x0C, 0x6C, 0xEF, 0x87, 0xF8, 0x98, 0x13, 0x96, 0x41, 0x58, 0xA6, 0x48, 0x6C, 0x66, 0x4B,
+ 0xA2, 0x2C, 0x8B, 0xBA, 0x26, 0x5F, 0x1D, 0x38, 0xB8, 0x22, 0xCE, 0xAD, 0x69, 0x9C, 0x73, 0x20,
+ 0x8C, 0x11, 0x10, 0xA6, 0xB2, 0xA1, 0x41, 0x7C, 0xA5, 0x3D, 0xE2, 0x29, 0xA0, 0xB8, 0x33, 0xA0,
+ 0x2F, 0x42, 0x46, 0x96, 0x02, 0x8D, 0x36, 0xB6, 0x39, 0xAA, 0x30, 0xE1, 0x0E, 0x0C, 0x2F, 0x0A,
+ 0xB6, 0x28, 0xDA, 0xF0, 0x9D, 0xA2, 0xC8, 0x13, 0xF9, 0xC5, 0xA0, 0xC2, 0xED, 0xDC, 0xFC, 0xF1,
+ 0x54, 0xB0, 0x15, 0xD4, 0xA3, 0xA9, 0x92, 0xC5, 0x3B, 0xCE, 0x16, 0xBB, 0xBF, 0xC5, 0x19, 0x0D,
+ 0xA2, 0xB0, 0xAC, 0xAB, 0xF4, 0xAE, 0xD7, 0x4D, 0xD5, 0xC6, 0x63, 0xC5, 0x2C, 0x3C, 0xB5, 0x09,
+ 0x42, 0xC2, 0xB0, 0x04, 0xCB, 0x9E, 0xB7, 0xB0, 0xC2, 0xC1, 0x28, 0xDE, 0x58, 0xFD, 0x39, 0x7C,
+ 0x0A, 0xCB, 0xFB, 0x16, 0x58, 0x04, 0x9B, 0xFA, 0xC1, 0x54, 0x24, 0xE8, 0x88, 0x6C, 0x21, 0x59,
+ 0x40, 0xB5, 0xCE, 0x54, 0xBB, 0x9C, 0xAD, 0x21, 0x80, 0x46, 0x67, 0x0D, 0x16, 0x3A, 0xEB, 0xB6,
+ 0xAE, 0xCB, 0x1D, 0xE7, 0xAB, 0xE7, 0xF1, 0x08, 0x66, 0xE0, 0x35, 0x03, 0xD3, 0xF6, 0x32, 0x8A,
+ 0x5A, 0x72, 0x06, 0x2A, 0x81, 0x71, 0x79, 0x3E, 0xEA, 0x29, 0x07, 0x72, 0xA0, 0x1C, 0x5C, 0x98,
+ 0x9E, 0x19, 0xE8, 0x08, 0xC7, 0xB5, 0x8A, 0x6C, 0x67, 0x5D, 0xB7, 0xE3, 0x65, 0x22, 0x46, 0xF1,
+ 0xC0, 0x3C, 0x63, 0xC9, 0x1B, 0xDA, 0x14, 0x84, 0xEF, 0x84, 0x2C, 0xC6, 0x35, 0x85, 0x05, 0xA9,
+ 0x96, 0x5D, 0x58, 0x0A, 0x74, 0x42, 0x5C, 0xBB, 0xAD, 0x07, 0x97, 0xED, 0xEB, 0x82, 0x8A, 0x6F,
+ 0x93, 0xC2, 0x85, 0x96, 0xFD, 0x8A, 0xDC, 0xD4, 0x3B, 0x66, 0x44, 0x0A, 0xE5, 0x6E, 0x69, 0x34,
+ 0x78, 0x23, 0x22, 0x26, 0x23, 0x54, 0x55, 0xA7, 0x59, 0x8C, 0x88, 0x58, 0xDD, 0x32, 0x02, 0x18,
+ 0xA2, 0x33, 0x99, 0x42, 0xB0, 0x3B, 0x59, 0x51, 0x23, 0x4C, 0xBB, 0xA4, 0x4F, 0x05, 0x76, 0xF0,
+ 0xD5, 0x6E, 0xB8, 0xA0, 0x98, 0x93, 0x82, 0x58, 0x88, 0x8D, 0x54, 0x09, 0x23, 0x60, 0x95, 0x03,
+ 0x4C, 0x5D, 0xE5, 0x53, 0x17, 0x6F, 0x8D, 0x77, 0x55, 0x42, 0x8B, 0xE7, 0x98, 0x46, 0x92, 0xF5,
+ 0xF1, 0x2A, 0x5E, 0xD3, 0x00, 0x9E, 0x9B, 0xBF, 0xC6, 0xB2, 0xBD, 0x09, 0x32, 0x7D, 0xA9, 0x1A,
+ 0x8B, 0x48, 0x28, 0xC8, 0x66, 0xAD, 0xC6, 0x36, 0x91, 0x5D, 0xA3, 0xCC, 0x0D, 0x56, 0x49, 0xAB,
+ 0xBA, 0xA6, 0x09, 0x15, 0x3A, 0xDB, 0x23, 0x87, 0xCA, 0x0F, 0x9B, 0x1C, 0x9E, 0x89, 0x0E, 0xF1,
+ 0x43, 0x81, 0xF4, 0x9A, 0x92, 0x74, 0xA9, 0xF2, 0x2F, 0x68, 0x6D, 0x62, 0x22, 0xCA, 0xCD, 0x1A,
+ 0x84, 0xCF, 0xE4, 0x0D, 0x8D, 0xC3, 0xE4, 0xA8, 0x8F, 0xC5, 0x02, 0xBC, 0x11, 0xC0, 0xF0, 0x8B,
+ 0x0F, 0xCE, 0xD9, 0x3D, 0x31, 0x19, 0xAD, 0x4D, 0x50, 0x2E, 0xED, 0x1E, 0x85, 0xEF, 0x0F, 0xDA,
+ 0x81, 0x5D, 0xD7, 0x75, 0xC3, 0xEC, 0x84, 0x47, 0x8C, 0x8A, 0xCC, 0x44, 0x46, 0x4F, 0xD4, 0xAD,
+ 0x03, 0x53, 0x3E, 0xDE, 0x36, 0x78, 0x8A, 0x0B, 0x68, 0xF6, 0xB8, 0x9F, 0x2E, 0xB3, 0xF6, 0x91,
+ 0x2A, 0x5B, 0xBB, 0x76, 0xF9, 0x98, 0x6B, 0xA1, 0x75, 0x30, 0x06, 0x79, 0x01, 0xFE, 0x14, 0x63,
+ 0x90, 0x20, 0x98, 0xE8, 0x3B, 0x84, 0x5F, 0xC0, 0x40, 0xC4, 0x4F, 0x85, 0x4C, 0x1A, 0xC9, 0x40,
+ 0x38, 0xB4, 0xA2, 0x68, 0x57, 0xA2, 0x91, 0x81, 0x93, 0x6D, 0xE8, 0xAD, 0x70, 0x47, 0x15, 0xC2,
+ 0xA8, 0x64, 0xA3, 0xDF, 0x6B, 0xE3, 0xF9, 0x0B, 0x47, 0x28, 0x21, 0xE2, 0x21, 0x14, 0xCA, 0x69,
+ 0x1C, 0xE1, 0xA9, 0xE8, 0xF0, 0x1C, 0xDE, 0x3F, 0x83, 0xDA, 0x66, 0xD0, 0x83, 0x2B, 0x93, 0x17,
+ 0xFF, 0x68, 0x76, 0xFD, 0x8D, 0xEB, 0x05, 0x6B, 0x61, 0xEC, 0xF7, 0x2B, 0xAF, 0x4F, 0x09, 0xB0,
+ 0xD8, 0x0F, 0xC7, 0x1D, 0x13, 0xE3, 0x4D, 0x3A, 0x02, 0x68, 0x03, 0x3F, 0x53, 0x49, 0x80, 0x02,
+ 0x34, 0xEE, 0x99, 0xD1, 0x3D, 0xBB, 0xD6, 0xB3, 0x75, 0x91, 0x0C, 0xD6, 0x3D, 0x73, 0xEB, 0xE6,
+ 0x20, 0xEE, 0x60, 0xF2, 0xCA, 0x0E, 0x5E, 0x86, 0x18, 0x5D, 0x95, 0xF3, 0xB3, 0x0D, 0xDD, 0x8C,
+ 0xA9, 0x54, 0x44, 0x6C, 0xAB, 0xBC, 0x87, 0x1E, 0xCB, 0xC1, 0x68, 0xF3, 0xF2, 0xBE, 0x9E, 0xBA,
+ 0x1C, 0x4B, 0x46, 0x64, 0xBB, 0x98, 0xF0, 0x7B, 0x48, 0x79, 0xF5, 0xD7, 0x28, 0x8F, 0xB0, 0x51,
+ 0x01, 0x16, 0xFA, 0x3E, 0x8A, 0x44, 0x88, 0x20, 0x30, 0x8D, 0x21, 0xD7, 0x4F, 0x49, 0x61, 0x69,
+ 0x4D, 0x5C, 0xBF, 0x02, 0x1E, 0x3E, 0xBB, 0xA1, 0xAB, 0x2E, 0x53, 0x9F, 0xFB, 0xC2, 0xF6, 0x1F,
+ 0x3F, 0x28, 0xAC, 0xB9, 0xBC, 0x7E, 0x8B, 0xDF, 0xFA, 0xFF, 0x40, 0xF8, 0x3C, 0xEE, 0x32, 0x03,
+ 0xB1, 0x31, 0x96, 0x6E, 0xA6, 0x2D, 0x09, 0xA2, 0xFC, 0x45, 0x1A, 0xFE, 0xFB, 0xC5, 0x97, 0xC3,
+ 0x07, 0xD8, 0xEF, 0xED, 0xFC, 0xDE, 0x75, 0xEF, 0x7D, 0x2F, 0x9E, 0xA1, 0x2A, 0xB8, 0x1E, 0xA3,
+ 0xF2, 0xB7, 0x7A, 0x7E, 0xC2, 0xD1, 0xBE, 0xCD, 0xD0, 0xAE, 0x7F, 0x16, 0x83, 0x3E, 0xDA, 0x22,
+ 0x31, 0x3C, 0x43, 0x04, 0x3A, 0x42, 0x91, 0x47, 0x3D, 0x14, 0x3E, 0xF4, 0x87, 0x6E, 0x54, 0xC9,
+ 0x2C, 0xBE, 0x36, 0x8C, 0x37, 0xE3, 0x1A, 0x3E, 0x86, 0x17, 0xC7, 0x07, 0x1C, 0x3B, 0x74, 0xF1,
+ 0xCB, 0x87, 0xAE, 0x1C, 0x07, 0x41, 0xE2, 0x31, 0x50, 0x34, 0x72, 0xAF, 0x0E, 0x20, 0x1E, 0x10,
+ 0xA0, 0xAE, 0x5F, 0x81, 0x2F, 0x1E, 0xC7, 0xC4, 0xC7, 0x2E, 0x1B, 0xC7, 0xC4, 0xC7, 0x36, 0x8C,
+ 0x23, 0x4D, 0x35, 0xCB, 0xB0, 0x49, 0x79, 0xB7, 0x7A, 0x00, 0x8F, 0xE3, 0xDF, 0xFF, 0xD1, 0x1D,
+ 0xC5, 0x75, 0xDF, 0xC3, 0xE3, 0xB0, 0x30, 0xF7, 0x7C, 0x1B, 0xFD, 0x77, 0xBC, 0xCE, 0x75, 0x2C,
+ 0x59, 0xA2, 0x3E, 0xDE, 0x31, 0xAA, 0xC9, 0x02, 0x87, 0x1F, 0xC7, 0x1A, 0x55, 0xAA, 0x91, 0x44,
+ 0x05, 0x6C, 0x2E, 0x06, 0x6F, 0x48, 0x65, 0xD8, 0xB3, 0x6B, 0x9E, 0x34, 0xD7, 0xFC, 0x1A, 0xFA,
+ 0x6D, 0x36, 0x38, 0x62, 0x07, 0xD2, 0x05, 0xDF, 0xB0, 0xE1, 0x5B, 0x67, 0x31, 0x22, 0xEB, 0xDA,
+ 0xFA, 0x4D, 0xCF, 0x11, 0x83, 0xF1, 0xB9, 0xE7, 0x53, 0xB1, 0x1B, 0x11, 0x82, 0x60, 0x86, 0x3C,
+ 0x86, 0x69, 0x21, 0x02, 0xE1, 0x37, 0xD1, 0xE8, 0xA6, 0xF1, 0x71, 0x49, 0x76, 0xD2, 0x59, 0x27,
+ 0xDD, 0xDF, 0x11, 0xD3, 0xAD, 0xBA, 0x2D, 0xDD, 0x9A, 0xEF, 0x68, 0x36, 0x9D, 0xC8, 0xCD, 0xD6,
+ 0xDE, 0x73, 0x8C, 0xA7, 0xE4, 0x60, 0xC8, 0x03, 0xD9, 0x7C, 0x16, 0x85, 0x45, 0xAB, 0xBA, 0x77,
+ 0x53, 0xB9, 0xC0, 0xF2, 0xA1, 0x50, 0x39, 0xD2, 0x1C, 0x6B, 0xEE, 0xF5, 0x21, 0x9E, 0x35, 0x0F,
+ 0xC4, 0x49, 0x5A, 0xAE, 0xCC, 0x5F, 0x78, 0x6E, 0x16, 0xC9, 0xF9, 0x21, 0xAD, 0x84, 0x2B, 0x78,
+ 0x84, 0xE1, 0xD5, 0x5F, 0x62, 0x3A, 0xF8, 0x0E, 0x18, 0x05, 0x7D, 0x70, 0xAC, 0x5F, 0xC9, 0x3B,
+ 0xB4, 0x1E, 0xC5, 0xFA, 0x30, 0x77, 0x72, 0x7A, 0xEC, 0x16, 0xFF, 0xCD, 0x1C, 0x11, 0x5D, 0xAE,
+ 0xA7, 0x97, 0x4B, 0x53, 0x37, 0xD1, 0xCD, 0x8E, 0x19, 0xEA, 0x20, 0x31, 0x81, 0xAA, 0x21, 0x04,
+ 0xEA, 0xB7, 0x97, 0x4E, 0xC3, 0xEA, 0x25, 0x71, 0x7A, 0xC1, 0xFD, 0xF7, 0x80, 0x06, 0x7E, 0xC1,
+ 0xFF, 0x78, 0xDE, 0xCF, 0x01, 0x05, 0x1E, 0x72, 0x9D, 0x56, 0xD8, 0x0C, 0x55, 0xCF, 0x0F, 0x29,
+ 0x7D, 0xDA, 0x15, 0x30, 0xA6, 0x47, 0xE9, 0xAE, 0xEE, 0x73, 0xDD, 0x82, 0x2E, 0x32, 0x6C, 0x37,
+ 0x08, 0x2C, 0x61, 0xD6, 0xDC, 0x4B, 0x1E, 0xE2, 0xC0, 0xD2, 0xB3, 0xB1, 0xE1, 0x2B, 0xD4, 0x86,
+ 0x0F, 0x08, 0xE2, 0xCF, 0x0F, 0xF8, 0x62, 0xF6, 0x78, 0x68, 0x6A, 0xF9, 0xBE, 0xFD, 0x99, 0x31,
+ 0x53, 0xD9, 0x31, 0x49, 0xFB, 0xAB, 0x9B, 0xFF, 0xF0, 0x81, 0x57, 0xBC, 0xF3, 0x7D, 0xAF, 0x59,
+ 0xBA, 0x23, 0xBB, 0xAB, 0xFE, 0xCE, 0x13, 0xEF, 0xFF, 0xFE, 0xF7, 0x9E, 0x7C, 0xCB, 0x57, 0x2F,
+ 0xFE, 0x28, 0x17, 0x1F, 0x5D, 0x28, 0xA4, 0xEA, 0xB3, 0xF0, 0x93, 0x6F, 0x78, 0x6B, 0x28, 0xE4,
+ 0x8B, 0x1D, 0x99, 0x3F, 0xFE, 0xC6, 0x68, 0xAA, 0x75, 0x71, 0xD5, 0x56, 0x83, 0x37, 0x6E, 0xBB,
+ 0xF3, 0xE5, 0xEF, 0x7B, 0xE8, 0x91, 0x77, 0x16, 0x73, 0xDB, 0xF7, 0xFC, 0xC3, 0xBB, 0xFE, 0xF8,
+ 0x1F, 0xDF, 0xF6, 0x97, 0xE9, 0x91, 0x83, 0x13, 0xD9, 0xD1, 0xC3, 0x58, 0xEE, 0xAC, 0xFE, 0x04,
+ 0xDB, 0x27, 0x2F, 0xA0, 0x7F, 0x00, 0x65, 0x8C, 0x25, 0xFB, 0x7D, 0xBB, 0x56, 0x31, 0x40, 0x95,
+ 0x38, 0x5C, 0xAF, 0xDC, 0xC5, 0xD5, 0xEE, 0x1C, 0x31, 0x79, 0x36, 0xF6, 0x5F, 0x8D, 0x99, 0x75,
+ 0x4C, 0xC2, 0xC1, 0x48, 0xAF, 0xEB, 0x0E, 0xB9, 0x80, 0x4F, 0xBC, 0x09, 0xB8, 0xA4, 0xEC, 0xEB,
+ 0x86, 0xE7, 0x1F, 0xCD, 0x62, 0x95, 0x47, 0x78, 0x88, 0xCA, 0xB5, 0xC5, 0xA6, 0xC8, 0x1E, 0xC2,
+ 0x10, 0xAB, 0x85, 0x25, 0x6A, 0x84, 0x8E, 0xC2, 0x74, 0x96, 0x70, 0xD0, 0x0B, 0xE9, 0x7D, 0x96,
+ 0x3F, 0x16, 0x0B, 0xD9, 0x6F, 0x79, 0x0D, 0x4D, 0xD2, 0x88, 0x3A, 0x29, 0x15, 0x34, 0x2D, 0xD3,
+ 0x1D, 0xED, 0xC9, 0x51, 0x83, 0x89, 0x26, 0x62, 0x1A, 0xEB, 0xA3, 0x05, 0x42, 0x1A, 0x5F, 0x72,
+ 0x4A, 0x35, 0x2D, 0x74, 0xF1, 0xAF, 0x6F, 0x39, 0x71, 0x70, 0x5B, 0x78, 0x02, 0xE5, 0x9A, 0x95,
+ 0x3C, 0x82, 0x58, 0x82, 0x2A, 0x85, 0x1A, 0x2D, 0xD3, 0xFE, 0xA9, 0xC7, 0xEF, 0x7E, 0x75, 0xF5,
+ 0xC0, 0x38, 0x21, 0x9B, 0x2A, 0x4B, 0xF2, 0x5A, 0x46, 0xC8, 0x3E, 0xFE, 0xA1, 0xCA, 0xAE, 0x05,
+ 0x82, 0xFA, 0xD0, 0xEA, 0x2A, 0x5E, 0x6F, 0xB4, 0x7A, 0x7E, 0xF5, 0x37, 0xE8, 0x10, 0xFA, 0x6B,
+ 0x90, 0x03, 0x9B, 0xC1, 0x74, 0x3F, 0xB4, 0xA9, 0x9A, 0x74, 0x34, 0xE4, 0x58, 0xB3, 0xC9, 0x2D,
+ 0x9D, 0xA9, 0x60, 0x13, 0xC5, 0xA6, 0x3A, 0xC8, 0x08, 0xBE, 0x4C, 0x78, 0x78, 0xA3, 0xBA, 0x74,
+ 0xDD, 0x58, 0x60, 0x30, 0xAE, 0x0D, 0x5A, 0x72, 0xCB, 0x50, 0x4B, 0xD2, 0x0C, 0x06, 0xF9, 0x1E,
+ 0xCD, 0x56, 0x11, 0x5E, 0x42, 0xD7, 0xE1, 0xBF, 0x86, 0x99, 0xB1, 0xF6, 0xC4, 0x6A, 0x9E, 0xA9,
+ 0xA0, 0x2A, 0xC2, 0x36, 0x01, 0xF2, 0xF4, 0x89, 0xA9, 0x5B, 0xAE, 0x31, 0xD0, 0xA8, 0x37, 0xDC,
+ 0x06, 0xB4, 0x42, 0xA4, 0x47, 0x04, 0x25, 0x18, 0x3C, 0xD4, 0x13, 0xC2, 0x93, 0x9B, 0x74, 0x49,
+ 0xE3, 0x67, 0x26, 0xEF, 0x0F, 0x4F, 0x07, 0x6F, 0xB9, 0x7B, 0x5F, 0xA9, 0x29, 0x63, 0x50, 0x6D,
+ 0x05, 0x5E, 0x6D, 0x54, 0xC2, 0x01, 0xBF, 0x00, 0xD5, 0x7C, 0x4E, 0xAF, 0xC6, 0xD2, 0x85, 0x18,
+ 0x32, 0xA4, 0xF8, 0xAC, 0x46, 0xD3, 0x6C, 0x32, 0x0F, 0x73, 0x9A, 0x12, 0xEF, 0x96, 0x45, 0xC1,
+ 0x30, 0x17, 0x09, 0xC6, 0xA2, 0xA8, 0xC7, 0x6E, 0xFD, 0xE1, 0x76, 0x82, 0xBE, 0x93, 0xE6, 0x95,
+ 0xEA, 0x4C, 0xAA, 0x53, 0x11, 0x25, 0x59, 0x31, 0x2B, 0x53, 0xB1, 0xB8, 0xCC, 0xD0, 0x84, 0x41,
+ 0x8A, 0x99, 0x9C, 0x51, 0x0D, 0x2B, 0x12, 0xB5, 0xF5, 0xE2, 0xD7, 0xB7, 0x70, 0x48, 0xE0, 0x11,
+ 0x55, 0x4F, 0x46, 0x5D, 0xF9, 0x18, 0x5C, 0xFD, 0x25, 0xEA, 0xA2, 0x2F, 0x62, 0x4E, 0x2F, 0xF5,
+ 0xF5, 0x62, 0x58, 0x01, 0x99, 0x7A, 0xB9, 0x51, 0xBB, 0xBF, 0x91, 0xBC, 0xD7, 0xEF, 0x7F, 0x80,
+ 0x3E, 0xED, 0x5A, 0x0F, 0xEA, 0x15, 0x6B, 0xDB, 0x50, 0xF0, 0xDA, 0xBA, 0x6E, 0x12, 0x3C, 0xF8,
+ 0xA4, 0xEB, 0xC4, 0x73, 0x2D, 0x55, 0xB2, 0xE1, 0xF9, 0xEE, 0x3C, 0xE8, 0x30, 0x60, 0x6D, 0x73,
+ 0xE0, 0x3C, 0x6D, 0xA3, 0x6E, 0xAC, 0xDE, 0xCE, 0x58, 0x58, 0x1C, 0x72, 0x3B, 0x5F, 0xDE, 0x71,
+ 0x24, 0x3B, 0xC1, 0x09, 0x76, 0xF9, 0xE0, 0x1F, 0x8C, 0x96, 0xEB, 0x61, 0x51, 0xAA, 0xEA, 0x21,
+ 0x63, 0x6F, 0x7A, 0x6C, 0x2B, 0xC7, 0xF4, 0xFF, 0xAA, 0x99, 0xCD, 0xDF, 0xE8, 0xCF, 0x25, 0x14,
+ 0x02, 0x31, 0xFB, 0x8E, 0x4E, 0x4C, 0x65, 0x72, 0x5B, 0x27, 0x54, 0x31, 0x9C, 0xED, 0x16, 0x94,
+ 0x90, 0x80, 0xD1, 0x52, 0x56, 0x32, 0xC2, 0xC9, 0xED, 0xDB, 0x54, 0xD2, 0x1A, 0xFB, 0xF4, 0xA2,
+ 0xE0, 0xAD, 0xE9, 0xAF, 0xD0, 0x87, 0xD0, 0x5F, 0x61, 0x39, 0x35, 0x06, 0xB6, 0xF5, 0x13, 0x96,
+ 0xA5, 0xB5, 0x26, 0xC7, 0x8B, 0x95, 0x07, 0x43, 0xB6, 0xA8, 0xD4, 0x6B, 0x23, 0x95, 0x6A, 0x05,
+ 0x69, 0xAA, 0x92, 0x41, 0xC0, 0x1F, 0x7A, 0x39, 0xF7, 0xF0, 0x9A, 0xA5, 0x30, 0xF4, 0x59, 0x76,
+ 0x5D, 0xDB, 0xE0, 0xFC, 0xC0, 0x5F, 0xE4, 0x49, 0x63, 0x6F, 0x91, 0x5D, 0x9C, 0x6A, 0x79, 0xDC,
+ 0xCA, 0xB8, 0xD8, 0xA7, 0x0A, 0x47, 0xF5, 0x26, 0x06, 0x6E, 0xF4, 0x60, 0x71, 0x27, 0x60, 0xBB,
+ 0x1E, 0x21, 0x19, 0x0F, 0x21, 0xA0, 0x64, 0x26, 0x91, 0xB4, 0xF0, 0x3A, 0xBB, 0x0E, 0x62, 0xF4,
+ 0x21, 0x25, 0xAC, 0x4E, 0x14, 0xAE, 0xDB, 0x53, 0xAD, 0x1E, 0x6F, 0x0A, 0x10, 0xC6, 0x6A, 0x81,
+ 0xFC, 0xAB, 0x04, 0x43, 0x0D, 0x34, 0xFC, 0x8B, 0xAF, 0x3E, 0x39, 0x99, 0xB1, 0x1F, 0x86, 0xB0,
+ 0xFF, 0x67, 0x8E, 0xC1, 0x5A, 0x34, 0x01, 0x61, 0x65, 0x8F, 0x9F, 0x9E, 0x9F, 0x86, 0x77, 0x95,
+ 0xEB, 0x8D, 0x58, 0x2F, 0x35, 0x97, 0xF5, 0xCB, 0x93, 0x70, 0x7E, 0xAA, 0x57, 0x43, 0x24, 0xFB,
+ 0x00, 0x22, 0x60, 0x60, 0x7C, 0xBE, 0xB3, 0xED, 0x3D, 0xBC, 0x4A, 0x8D, 0x7C, 0x0E, 0xCB, 0xE7,
+ 0xC7, 0x9E, 0xE8, 0x1C, 0x09, 0xB2, 0xB0, 0x0F, 0xE0, 0xEA, 0x4F, 0x31, 0x8B, 0x1E, 0x42, 0x9F,
+ 0x07, 0xE3, 0xE0, 0xC9, 0xFE, 0x8E, 0xE6, 0x68, 0x23, 0x1F, 0xB3, 0x34, 0x1E, 0xB0, 0x46, 0xA6,
+ 0x2A, 0x8E, 0x51, 0xD5, 0xB4, 0xC1, 0xA6, 0x0D, 0x7F, 0xDA, 0x68, 0x6A, 0xE0, 0x15, 0x13, 0xE9,
+ 0x87, 0x42, 0x95, 0xB2, 0xF6, 0x60, 0x26, 0x55, 0x56, 0xCB, 0x99, 0x4A, 0xF5, 0xB1, 0xCC, 0xF8,
+ 0xD8, 0x63, 0x94, 0x65, 0x3E, 0x12, 0xF5, 0x27, 0x42, 0xC8, 0xA8, 0x65, 0xD2, 0xC8, 0x54, 0x15,
+ 0x91, 0x34, 0x62, 0x4F, 0xF0, 0x14, 0x09, 0x2F, 0x85, 0xD3, 0xBA, 0x6B, 0xF6, 0x36, 0x9E, 0x8F,
+ 0xA1, 0xF5, 0x54, 0x1D, 0x38, 0xE0, 0x43, 0x70, 0xA7, 0xE7, 0x8C, 0xEF, 0x7A, 0xE4, 0x70, 0x7E,
+ 0x83, 0x42, 0xDF, 0xA8, 0xD7, 0xD7, 0xB9, 0x64, 0x42, 0x1B, 0x04, 0x48, 0x5C, 0x11, 0x80, 0x59,
+ 0xA3, 0x07, 0x47, 0x5B, 0x93, 0x90, 0x69, 0xB5, 0xB3, 0xCC, 0xC0, 0x1F, 0x84, 0x27, 0xAD, 0xD1,
+ 0xC6, 0xD4, 0x92, 0x64, 0x1C, 0x2C, 0xF4, 0x64, 0x84, 0xE1, 0xB1, 0x83, 0x61, 0x25, 0x63, 0xB9,
+ 0xED, 0xE8, 0x90, 0x95, 0x8B, 0xF8, 0x26, 0x8E, 0xDC, 0x37, 0x6E, 0x85, 0x11, 0x24, 0x1F, 0xDD,
+ 0x34, 0x01, 0x1F, 0x2A, 0x66, 0xE2, 0x76, 0xE4, 0x20, 0x9D, 0xF9, 0x1B, 0x67, 0xAB, 0xBE, 0x67,
+ 0xE4, 0xE0, 0x0A, 0x9C, 0x79, 0x78, 0x46, 0x9F, 0x99, 0xDF, 0x12, 0xA5, 0x5D, 0x0F, 0xD7, 0xE8,
+ 0x93, 0xE2, 0xEC, 0x7D, 0x81, 0x72, 0xD4, 0x76, 0xCD, 0x33, 0x54, 0x4C, 0x86, 0xDE, 0xA0, 0xA7,
+ 0x51, 0x75, 0xE6, 0x23, 0x30, 0x82, 0x61, 0x31, 0x81, 0x48, 0x5A, 0xDF, 0xEF, 0x27, 0xDF, 0x88,
+ 0x60, 0x70, 0x31, 0x80, 0x60, 0x3E, 0x33, 0x17, 0xA3, 0x39, 0x0C, 0x02, 0xDC, 0x39, 0x5D, 0x7D,
+ 0x16, 0x2D, 0xA1, 0xAF, 0x80, 0x06, 0xB8, 0xBF, 0x3F, 0x56, 0xCC, 0x06, 0x35, 0x50, 0xCC, 0x67,
+ 0x93, 0xB1, 0x68, 0x32, 0x29, 0xD9, 0x29, 0xC9, 0x2E, 0x89, 0x29, 0xC7, 0x7E, 0x85, 0x2C, 0xD1,
+ 0xAF, 0x28, 0xD5, 0x5F, 0xC6, 0xB1, 0xCC, 0x43, 0x3E, 0x14, 0x76, 0x24, 0x11, 0x45, 0x23, 0xE1,
+ 0x10, 0x99, 0x02, 0x6F, 0x0A, 0xCE, 0x9D, 0x85, 0xC5, 0x35, 0x15, 0xE8, 0xDB, 0x30, 0x79, 0xE7,
+ 0x40, 0x15, 0xCE, 0xBA, 0x56, 0x02, 0x9E, 0x45, 0x17, 0x9C, 0xC1, 0x4B, 0x33, 0xE5, 0xB9, 0xA9,
+ 0x36, 0x1A, 0xA3, 0xA3, 0xC5, 0xA1, 0xB7, 0x2A, 0xE9, 0x3E, 0x85, 0x3D, 0x10, 0xED, 0x19, 0x76,
+ 0x0C, 0x06, 0xDB, 0x4C, 0x0F, 0x7A, 0x14, 0x98, 0x6C, 0x8F, 0x66, 0x9A, 0x5E, 0x68, 0x71, 0x38,
+ 0x79, 0x68, 0x69, 0x06, 0x43, 0xCE, 0x74, 0xE4, 0xA0, 0x93, 0x81, 0xE8, 0x40, 0xA5, 0xBF, 0xB5,
+ 0x25, 0xE7, 0x6A, 0xD7, 0x3A, 0x4C, 0xBA, 0xD1, 0x86, 0x10, 0x86, 0xD5, 0xBE, 0x7A, 0x3F, 0xE5,
+ 0xF7, 0xA7, 0xD3, 0x8F, 0x85, 0xAA, 0x89, 0xCC, 0x87, 0xD2, 0x5B, 0xD1, 0x57, 0x7A, 0x01, 0x41,
+ 0x57, 0xF0, 0x6A, 0x73, 0xC5, 0xD8, 0x34, 0xCD, 0x4E, 0x28, 0x15, 0x03, 0xFE, 0x4F, 0x27, 0x9A,
+ 0x3F, 0x10, 0xB8, 0x7F, 0x05, 0xA1, 0xE3, 0xB9, 0xFB, 0x78, 0x08, 0xEF, 0x26, 0x11, 0x45, 0xBB,
+ 0x72, 0x21, 0x8C, 0xF1, 0xC6, 0xDB, 0xD1, 0x97, 0x41, 0x0F, 0x94, 0xFB, 0xFA, 0x78, 0x23, 0x84,
+ 0xA6, 0x6A, 0x66, 0x3F, 0xF6, 0x66, 0x5F, 0x0E, 0xA9, 0xBE, 0x96, 0xF8, 0xF0, 0xBA, 0x43, 0x69,
+ 0xA3, 0x60, 0xE8, 0xAB, 0x43, 0x2D, 0x4F, 0x0C, 0x05, 0x63, 0x76, 0x74, 0x8A, 0x6A, 0x78, 0x02,
+ 0x11, 0xB9, 0x4E, 0x11, 0xCF, 0xA5, 0x4F, 0x79, 0x0A, 0xCF, 0x33, 0xF2, 0xDA, 0xAE, 0x87, 0x04,
+ 0xBD, 0x85, 0xAA, 0xC7, 0x5A, 0x96, 0x5F, 0xD7, 0xA6, 0xA7, 0x6E, 0xB9, 0x67, 0x82, 0x2D, 0xEE,
+ 0xDB, 0xB4, 0x75, 0x77, 0x53, 0x6B, 0xE4, 0xA3, 0xDA, 0x9E, 0x00, 0x1B, 0x6E, 0x95, 0xAA, 0x8B,
+ 0x41, 0x5A, 0x43, 0xCD, 0x87, 0xC7, 0x84, 0x6C, 0x95, 0x60, 0x7C, 0xDB, 0x84, 0x30, 0x63, 0x37,
+ 0x33, 0x9B, 0xE1, 0xBC, 0xAD, 0x56, 0x76, 0x64, 0xAB, 0x8A, 0x2C, 0x70, 0xC9, 0xC7, 0x8F, 0xEC,
+ 0xBE, 0xEA, 0xDE, 0x03, 0x95, 0xBC, 0x4A, 0x51, 0x9C, 0xA3, 0x69, 0x62, 0x53, 0xE1, 0xFD, 0xC9,
+ 0xCE, 0x72, 0xD9, 0x37, 0xD2, 0x67, 0xEE, 0xDE, 0xCE, 0x8E, 0x14, 0x25, 0xCD, 0x37, 0x1F, 0x20,
+ 0x1C, 0x46, 0x4D, 0xE1, 0xB1, 0x2D, 0xAC, 0x3E, 0x47, 0x70, 0xE8, 0x5B, 0x60, 0x19, 0xFC, 0x97,
+ 0xFE, 0x72, 0xAB, 0x91, 0x53, 0xC0, 0xF2, 0x44, 0x16, 0x4C, 0x44, 0x46, 0xE4, 0x07, 0x0F, 0x8A,
+ 0x6F, 0x3B, 0x98, 0x36, 0x0F, 0x5A, 0xD3, 0x07, 0x47, 0xB7, 0x6D, 0x4D, 0xEC, 0xD8, 0xB5, 0xF3,
+ 0xF9, 0xDE, 0xD4, 0xEC, 0xFD, 0xC1, 0xDE, 0x54, 0x3F, 0x15, 0x0E, 0xB1, 0x34, 0x45, 0x2C, 0xB3,
+ 0x8B, 0x0B, 0x73, 0xB3, 0xE4, 0x3C, 0xC2, 0x86, 0x30, 0xDA, 0xBF, 0x6F, 0xF7, 0xAE, 0x9D, 0x3B,
+ 0xB6, 0x6F, 0xDB, 0xBA, 0x85, 0xA5, 0x45, 0x66, 0xAA, 0x13, 0x25, 0x22, 0x68, 0x64, 0xCB, 0xDB,
+ 0x57, 0xCA, 0x25, 0xD8, 0x18, 0x72, 0xD4, 0xC0, 0x55, 0xE8, 0x74, 0x3D, 0xC2, 0x50, 0xBD, 0x40,
+ 0x89, 0xD3, 0xC5, 0xD4, 0x11, 0x84, 0xD7, 0xBA, 0xCA, 0x73, 0xE0, 0xBC, 0xEC, 0x9D, 0x3F, 0x07,
+ 0xB6, 0xC3, 0xEB, 0x7B, 0xE7, 0xDD, 0x93, 0x26, 0xB6, 0x2E, 0xCF, 0x0F, 0x64, 0xEE, 0x25, 0xC7,
+ 0xC5, 0x25, 0x62, 0x39, 0xD8, 0xC0, 0x93, 0x4C, 0xBB, 0x72, 0x16, 0x39, 0x9E, 0xC1, 0xE5, 0xB9,
+ 0x2C, 0xF0, 0xEC, 0x56, 0xC9, 0xA4, 0xE2, 0x5A, 0xD2, 0xB6, 0xE7, 0xA7, 0x76, 0xE8, 0x35, 0xA3,
+ 0xA5, 0x0A, 0x27, 0x91, 0x6B, 0xB8, 0x31, 0x98, 0xBD, 0x1A, 0x98, 0xD9, 0x7A, 0x68, 0x12, 0xF6,
+ 0x88, 0x30, 0x6D, 0xD2, 0x61, 0x68, 0xDA, 0x16, 0xC1, 0x25, 0x52, 0x02, 0xB6, 0x2B, 0x20, 0x54,
+ 0x82, 0xB4, 0xCC, 0x0B, 0xD3, 0x44, 0xAD, 0xA0, 0x33, 0x41, 0x8A, 0xA0, 0x60, 0xE1, 0x7C, 0x38,
+ 0xCA, 0x50, 0xAC, 0x2C, 0xB0, 0xE2, 0x26, 0x73, 0x9C, 0x67, 0x0C, 0x8E, 0xA4, 0x90, 0x95, 0x65,
+ 0x97, 0x30, 0x7D, 0xA5, 0x33, 0x9D, 0x33, 0x1D, 0x08, 0x33, 0x99, 0x78, 0x1C, 0x52, 0xF0, 0x91,
+ 0x7C, 0x44, 0x50, 0x28, 0x55, 0x29, 0xB6, 0xD3, 0x1C, 0xAF, 0x97, 0x88, 0x64, 0x88, 0x49, 0xC5,
+ 0x1E, 0xDD, 0x47, 0x20, 0x62, 0xDC, 0x29, 0x26, 0x79, 0x81, 0x55, 0xB3, 0x6A, 0x8E, 0x0A, 0xD4,
+ 0x02, 0xDF, 0x7F, 0x63, 0x61, 0x7C, 0x93, 0xE0, 0xA3, 0x46, 0x37, 0x4F, 0x3D, 0xBC, 0x00, 0x11,
+ 0xEC, 0x74, 0x7A, 0xFD, 0x60, 0xC0, 0xF1, 0xF9, 0x2D, 0x43, 0x53, 0x24, 0x41, 0x4B, 0x68, 0x7C,
+ 0xFC, 0xB8, 0x9B, 0x49, 0x24, 0xAF, 0x3E, 0x0F, 0x9E, 0x45, 0x4F, 0x83, 0x00, 0x68, 0xF5, 0x2D,
+ 0x6C, 0x46, 0x8B, 0x24, 0x02, 0x01, 0x1A, 0xF9, 0x1D, 0x9F, 0xC9, 0x10, 0x8B, 0x67, 0xA1, 0xB2,
+ 0x91, 0xE9, 0xBA, 0x1E, 0x8F, 0x81, 0x75, 0xB8, 0x19, 0x54, 0x5C, 0x07, 0x34, 0x6A, 0x67, 0x06,
+ 0x96, 0x1C, 0x93, 0xED, 0x21, 0x0C, 0xA7, 0xA2, 0xC8, 0x32, 0x9E, 0x8D, 0xC7, 0xB6, 0x41, 0x92,
+ 0xA2, 0x69, 0x49, 0x92, 0x38, 0x99, 0xBE, 0x3B, 0x22, 0x89, 0x41, 0x1A, 0x21, 0x6A, 0x97, 0x6D,
+ 0x35, 0xBF, 0xF4, 0xC9, 0x4C, 0x67, 0xB4, 0xE8, 0x98, 0x82, 0x4E, 0xED, 0xD0, 0x99, 0x98, 0xDE,
+ 0x63, 0x2F, 0x1E, 0xF3, 0x7C, 0xB6, 0x71, 0xF0, 0x4B, 0xF4, 0x4D, 0x90, 0x05, 0x53, 0xFD, 0xA0,
+ 0xCD, 0x00, 0x4B, 0xCB, 0x66, 0x12, 0x71, 0x32, 0x92, 0x96, 0x50, 0x5A, 0x44, 0x89, 0x78, 0x3A,
+ 0xF8, 0x0E, 0xFA, 0x8F, 0xA1, 0xBD, 0xC1, 0xCA, 0xEE, 0x7A, 0x12, 0xD2, 0xE9, 0x0E, 0x3B, 0xE4,
+ 0xF6, 0x28, 0x67, 0x53, 0x45, 0xC7, 0x03, 0x41, 0xD9, 0x29, 0xA2, 0x99, 0xF5, 0xD0, 0xAF, 0x82,
+ 0xB2, 0x83, 0xB0, 0x97, 0x0B, 0x26, 0xF0, 0x5A, 0xD0, 0x56, 0x14, 0xFE, 0x52, 0x67, 0x6F, 0xE0,
+ 0x42, 0x61, 0x59, 0xB5, 0x1C, 0x9E, 0x10, 0x75, 0x9D, 0xE3, 0x44, 0x42, 0x59, 0x8A, 0xCC, 0x29,
+ 0x92, 0x9F, 0x83, 0x18, 0xEF, 0xB7, 0x14, 0x66, 0x2F, 0x13, 0x88, 0xF8, 0xB5, 0xCC, 0xC2, 0xC9,
+ 0x9B, 0xDE, 0xFA, 0xE8, 0x5D, 0x93, 0x24, 0x05, 0xCD, 0x8C, 0x59, 0xE6, 0xCA, 0x71, 0x5F, 0xD1,
+ 0xD0, 0xAA, 0xCF, 0x4F, 0x2C, 0x6D, 0x76, 0xF3, 0x87, 0x56, 0x7F, 0x8E, 0x3E, 0x88, 0xFE, 0x11,
+ 0xE4, 0xC1, 0x1C, 0xB8, 0xB9, 0x5F, 0xE9, 0x34, 0x42, 0xB6, 0xC6, 0x32, 0x60, 0x62, 0xCC, 0xC7,
+ 0xC6, 0xA2, 0x48, 0x92, 0x65, 0x29, 0xFF, 0x27, 0xF3, 0xD9, 0x33, 0xF3, 0xCC, 0xBB, 0xE6, 0xC9,
+ 0x77, 0xCE, 0x57, 0x46, 0xE6, 0x0A, 0xB3, 0x68, 0xA4, 0xF7, 0xA7, 0x15, 0x0C, 0xE8, 0x3B, 0x1B,
+ 0x27, 0xD6, 0x8B, 0xA7, 0x80, 0x35, 0x62, 0x7C, 0xBC, 0x72, 0xEE, 0xBB, 0x7B, 0x47, 0xCF, 0x0A,
+ 0x9E, 0xCF, 0xE3, 0x45, 0x3E, 0xC5, 0xF9, 0xCE, 0x00, 0x16, 0x60, 0xF4, 0x9A, 0x75, 0xDC, 0x30,
+ 0x91, 0x07, 0xF3, 0xB3, 0x58, 0x0A, 0xF4, 0x06, 0xCE, 0x04, 0x05, 0x45, 0x49, 0x97, 0x34, 0xBD,
+ 0x70, 0xD6, 0x68, 0x0B, 0xAF, 0x4E, 0xD5, 0x83, 0x0A, 0xCC, 0xD0, 0xD9, 0x08, 0x2D, 0x07, 0x7D,
+ 0x30, 0x58, 0x19, 0x53, 0xA5, 0x30, 0x6F, 0x26, 0xFC, 0xBB, 0x2A, 0xD3, 0x6F, 0x49, 0x1B, 0x65,
+ 0xC1, 0xCF, 0x6F, 0x6A, 0xDA, 0xA9, 0x49, 0xDF, 0x89, 0x43, 0x12, 0x73, 0x92, 0xD9, 0xEE, 0x37,
+ 0x04, 0xF1, 0xE2, 0x96, 0x8C, 0x83, 0x97, 0x13, 0x32, 0x90, 0x26, 0x54, 0x55, 0x12, 0xF8, 0xAB,
+ 0x93, 0x15, 0xDE, 0x96, 0x09, 0xB8, 0xDB, 0x4A, 0xC7, 0x35, 0xC9, 0x28, 0xA7, 0xEE, 0x9E, 0x5F,
+ 0x79, 0x20, 0x65, 0x1A, 0x41, 0x93, 0x16, 0x50, 0xB5, 0x40, 0xCB, 0xE1, 0x98, 0x19, 0xBC, 0xFA,
+ 0x2A, 0x99, 0x7B, 0x2B, 0x77, 0x3C, 0x46, 0x87, 0x4C, 0x29, 0xEA, 0xDF, 0xFA, 0xDA, 0x85, 0xBF,
+ 0x2F, 0x30, 0x29, 0x15, 0x63, 0xC8, 0xF1, 0x60, 0x24, 0x73, 0xE2, 0x7F, 0xFD, 0x17, 0x8F, 0xEA,
+ 0x7E, 0x8D, 0xE6, 0xD0, 0x97, 0x40, 0x1A, 0xEC, 0xEC, 0x67, 0x1C, 0x89, 0x06, 0xB6, 0x01, 0x36,
+ 0x65, 0x38, 0xF6, 0x68, 0xFA, 0xDD, 0x64, 0xE2, 0x71, 0xAC, 0x29, 0x13, 0x88, 0x89, 0xC7, 0x10,
+ 0xCF, 0xB1, 0x04, 0x13, 0x7A, 0x92, 0xBE, 0x0C, 0x2D, 0x0E, 0x18, 0xDC, 0xD5, 0x9A, 0xEA, 0x05,
+ 0x97, 0x81, 0xD7, 0x40, 0x23, 0x9E, 0x9A, 0x8C, 0x33, 0x44, 0x4C, 0x71, 0xA6, 0x3D, 0x89, 0x46,
+ 0x33, 0x59, 0x3C, 0x2D, 0x68, 0xE8, 0x66, 0x45, 0xEE, 0xC8, 0xC3, 0x10, 0x33, 0x66, 0x03, 0x61,
+ 0x28, 0x90, 0x0F, 0x5E, 0xFC, 0xDB, 0x34, 0x24, 0x20, 0x41, 0x71, 0x18, 0xF2, 0x28, 0x14, 0xE7,
+ 0xAB, 0x75, 0x53, 0x4C, 0x4C, 0x40, 0x64, 0xE1, 0x13, 0x63, 0x8F, 0x66, 0x7C, 0xA5, 0x18, 0x31,
+ 0x36, 0x3A, 0x76, 0x55, 0x75, 0x5B, 0xAE, 0xEB, 0xF3, 0x55, 0xFD, 0x1C, 0x61, 0xD8, 0xA5, 0x9C,
+ 0xB4, 0xEB, 0x0D, 0xFF, 0x8D, 0xFF, 0x73, 0xDE, 0x95, 0xDB, 0x05, 0x2C, 0xDB, 0x7E, 0x83, 0x7E,
+ 0x08, 0x66, 0xC0, 0x4C, 0xDF, 0x37, 0x3D, 0x39, 0xDE, 0x6A, 0xA6, 0x62, 0xC1, 0xBC, 0x49, 0x3B,
+ 0x55, 0xA5, 0x7A, 0xD2, 0x37, 0xFE, 0xDE, 0x15, 0x14, 0x2C, 0x0D, 0xB3, 0x56, 0x5C, 0x57, 0x96,
+ 0x02, 0xAF, 0x71, 0xFD, 0x8A, 0x97, 0xB9, 0x47, 0x07, 0x7D, 0x9E, 0x0D, 0x7A, 0xAE, 0x6D, 0x77,
+ 0xFD, 0x30, 0xFB, 0x11, 0x1E, 0xCE, 0xC5, 0x3A, 0xC9, 0x8B, 0x8F, 0xF5, 0xC8, 0xEC, 0xC0, 0x52,
+ 0xF3, 0x82, 0x05, 0x04, 0x96, 0x27, 0x5E, 0x1E, 0x84, 0x2B, 0xE9, 0x89, 0x5F, 0x04, 0x6F, 0x32,
+ 0xC2, 0x39, 0xC1, 0xCF, 0x36, 0x1D, 0x3A, 0x9C, 0x6B, 0x07, 0x52, 0x0B, 0xBD, 0xEA, 0x2C, 0x27,
+ 0x14, 0x92, 0xE3, 0x49, 0x9B, 0xD5, 0x03, 0x6A, 0xC0, 0x67, 0x4B, 0xD2, 0x78, 0x9D, 0x8D, 0x9F,
+ 0xBA, 0x67, 0x77, 0xA2, 0x33, 0x17, 0xD0, 0xD8, 0xD3, 0xE5, 0x8A, 0xE9, 0x08, 0x9A, 0x38, 0xDE,
+ 0x5C, 0x50, 0x17, 0x5A, 0x07, 0xDE, 0x35, 0x9B, 0x15, 0xF7, 0xC5, 0x30, 0xC6, 0x4D, 0xD4, 0x7A,
+ 0xCD, 0x6D, 0xF5, 0xBB, 0xAB, 0x3E, 0x3B, 0x1D, 0x78, 0xB0, 0x7C, 0xCD, 0x83, 0x3B, 0xCB, 0xE5,
+ 0x96, 0xAC, 0xC9, 0x42, 0x22, 0x5A, 0xD9, 0xFA, 0xB9, 0xC7, 0xC6, 0xEE, 0xD9, 0xBC, 0x69, 0x2E,
+ 0x1F, 0xEA, 0x07, 0x83, 0x23, 0xDD, 0x68, 0xF1, 0xC4, 0x0F, 0x1E, 0xE1, 0xDD, 0x28, 0x08, 0xC6,
+ 0x84, 0xCF, 0xA1, 0x9F, 0xA2, 0x1F, 0x60, 0xBB, 0xEE, 0x54, 0xBF, 0x59, 0x8A, 0x31, 0x34, 0xE8,
+ 0x54, 0x8B, 0x79, 0x53, 0xEC, 0x74, 0x88, 0x5E, 0x97, 0xE8, 0x89, 0xD3, 0xD3, 0xF1, 0x44, 0x32,
+ 0x15, 0x0A, 0x5A, 0x44, 0xD8, 0xEA, 0x21, 0x44, 0x84, 0x82, 0x01, 0xBF, 0x45, 0xD9, 0x28, 0x05,
+ 0xDE, 0xB7, 0x12, 0x2B, 0x9D, 0x85, 0xA5, 0xCB, 0x7C, 0x17, 0xAE, 0xE0, 0xEE, 0x7A, 0xBC, 0xAC,
+ 0xBA, 0x8E, 0x33, 0xF5, 0x52, 0x54, 0x71, 0x63, 0xDE, 0x81, 0x6B, 0xE2, 0x95, 0x86, 0x2A, 0x3C,
+ 0x0C, 0x15, 0x3C, 0x39, 0xC8, 0xB5, 0x7A, 0x3C, 0x86, 0xB6, 0x3C, 0x15, 0x4E, 0xAC, 0x53, 0xBA,
+ 0x2B, 0x87, 0x06, 0xD1, 0xD3, 0x28, 0x81, 0x35, 0x7A, 0xB6, 0x8D, 0x7E, 0x8A, 0x01, 0x0B, 0x49,
+ 0x51, 0xA1, 0x70, 0x38, 0xCC, 0xF9, 0x1B, 0xA7, 0x1B, 0x73, 0xBA, 0x49, 0x60, 0xC9, 0x48, 0xC0,
+ 0xF4, 0xD8, 0x38, 0x4D, 0xD3, 0x14, 0x6B, 0x66, 0x6B, 0xA6, 0x28, 0x89, 0x0F, 0x49, 0x82, 0x1E,
+ 0x8A, 0x07, 0x66, 0x83, 0xCD, 0x1B, 0xC7, 0xAA, 0x4A, 0x08, 0x72, 0xF2, 0x16, 0xF4, 0x83, 0x6C,
+ 0x7C, 0x76, 0xF9, 0xAB, 0x2F, 0x97, 0xAF, 0x36, 0x4D, 0xD3, 0xF6, 0x75, 0x36, 0x3F, 0x50, 0xEF,
+ 0x29, 0x9A, 0x5F, 0x71, 0x4E, 0x07, 0xF4, 0x79, 0x2E, 0x61, 0x25, 0x8A, 0x0F, 0x3D, 0xF4, 0x9A,
+ 0x27, 0x18, 0x2D, 0x16, 0xA0, 0xD4, 0xEB, 0x58, 0xDD, 0x36, 0x3A, 0xFE, 0xDA, 0xA1, 0x6E, 0x43,
+ 0xF5, 0x77, 0xEE, 0xBB, 0x6D, 0xB4, 0x8D, 0xA9, 0x25, 0x86, 0xE5, 0xC4, 0xA7, 0xD1, 0x17, 0xC0,
+ 0x04, 0x28, 0xF4, 0xD5, 0x7A, 0xCA, 0xEF, 0x68, 0xC4, 0x48, 0xD9, 0x8E, 0xBD, 0xBF, 0xD3, 0xFE,
+ 0x33, 0x81, 0x1D, 0x60, 0xFF, 0x0D, 0x74, 0x81, 0xC7, 0xD9, 0xD1, 0x5C, 0x15, 0x6F, 0xBA, 0x26,
+ 0x8E, 0x47, 0x13, 0xC4, 0xD0, 0x29, 0x48, 0x6F, 0x08, 0x1C, 0xD1, 0x5E, 0x2C, 0x75, 0x8D, 0xB4,
+ 0xD1, 0x39, 0xB8, 0x1C, 0xB4, 0xFC, 0xFB, 0x0F, 0x8F, 0xDD, 0xBB, 0x9F, 0x0B, 0xEB, 0xD5, 0xA9,
+ 0x11, 0xD9, 0x2C, 0xA7, 0xCC, 0xE2, 0x64, 0xC3, 0xC9, 0x4D, 0x25, 0x10, 0xAB, 0x95, 0x16, 0xFA,
+ 0x11, 0xC3, 0xAC, 0xE8, 0xAA, 0x94, 0x93, 0x53, 0xCB, 0x55, 0x67, 0xEB, 0x87, 0xE1, 0x07, 0x58,
+ 0x9D, 0xBD, 0xFB, 0xB6, 0x98, 0xE5, 0xF4, 0x33, 0xA7, 0xA4, 0x91, 0x6B, 0x23, 0x95, 0x95, 0x99,
+ 0x04, 0x2B, 0x77, 0xB6, 0x4F, 0xEC, 0x8A, 0x4F, 0x94, 0xF4, 0xD2, 0xFE, 0xBE, 0x66, 0x4E, 0x35,
+ 0x77, 0xCC, 0xFA, 0x19, 0x86, 0x20, 0x4C, 0x87, 0xD3, 0x0B, 0xF2, 0x8D, 0xEE, 0xDA, 0x7F, 0x6F,
+ 0xF5, 0x79, 0x34, 0x81, 0xB1, 0x5C, 0x1E, 0x6C, 0xED, 0xC7, 0xA3, 0x61, 0x5F, 0x94, 0x0D, 0xEB,
+ 0xDA, 0x43, 0x19, 0xCE, 0x94, 0x10, 0x32, 0x2D, 0x14, 0x30, 0x75, 0x03, 0xF9, 0x03, 0x99, 0x37,
+ 0xF9, 0xB0, 0xFE, 0x88, 0x5E, 0xA6, 0x3F, 0x06, 0x7A, 0x78, 0x98, 0x47, 0xE6, 0xC1, 0xB3, 0xA1,
+ 0xDC, 0x2E, 0x44, 0x87, 0x90, 0x06, 0x0D, 0x62, 0x22, 0x9E, 0xDF, 0x82, 0x89, 0xBA, 0x82, 0x1C,
+ 0x43, 0xDA, 0x4C, 0x33, 0xC1, 0xB8, 0x7E, 0x76, 0x2F, 0x2D, 0x04, 0x8D, 0x70, 0x13, 0x7B, 0xC7,
+ 0xC3, 0x29, 0x26, 0x30, 0x52, 0x10, 0xF6, 0x4C, 0x05, 0x21, 0x42, 0xD1, 0x7D, 0x31, 0x0E, 0x52,
+ 0x8B, 0x1D, 0x5F, 0xC5, 0xEF, 0x9B, 0x19, 0xAB, 0x6E, 0xBB, 0xFD, 0x18, 0x6A, 0x05, 0x03, 0xC5,
+ 0xCD, 0x55, 0x93, 0x13, 0x2D, 0x29, 0xD0, 0x4E, 0xCB, 0x93, 0x13, 0xE8, 0xD9, 0xC7, 0x1E, 0x35,
+ 0x26, 0x7C, 0x9F, 0x19, 0x79, 0xEC, 0xFB, 0xA2, 0x60, 0x56, 0x82, 0x41, 0x8E, 0x0E, 0xCE, 0x0E,
+ 0x2C, 0xB3, 0x36, 0x55, 0x43, 0x7F, 0x8F, 0x25, 0xF8, 0x78, 0xDF, 0xD7, 0x2C, 0x64, 0x31, 0xE2,
+ 0x54, 0x46, 0xC0, 0x34, 0x99, 0xD9, 0x3A, 0x3F, 0xF7, 0xE7, 0xF3, 0xD3, 0xEF, 0x27, 0x7D, 0x1F,
+ 0x26, 0x3F, 0xB2, 0xC1, 0x3E, 0xBB, 0x0C, 0x83, 0xBB, 0xF2, 0x58, 0x19, 0x64, 0x0E, 0xE0, 0x55,
+ 0x62, 0xDD, 0xB4, 0x81, 0xE1, 0x7A, 0x38, 0x61, 0x37, 0x3B, 0x46, 0x21, 0x3C, 0x5F, 0x5B, 0xA6,
+ 0x3A, 0x34, 0xD6, 0xF0, 0xF2, 0x71, 0x2E, 0xEC, 0xA4, 0x6A, 0xA5, 0x7B, 0x5F, 0xFF, 0x64, 0xFF,
+ 0xF0, 0x6B, 0xC6, 0x4E, 0x6D, 0xBD, 0x71, 0x76, 0x36, 0xDF, 0xED, 0xE9, 0xBE, 0x4C, 0x6F, 0x31,
+ 0x7B, 0xCB, 0xC9, 0x34, 0x8D, 0xB8, 0xE2, 0x98, 0x8F, 0x0C, 0xC9, 0x5A, 0x24, 0xE6, 0x77, 0xD2,
+ 0xBB, 0x0B, 0xDB, 0xEB, 0x8C, 0x3F, 0x56, 0xEB, 0x1D, 0x3C, 0x7D, 0xDB, 0xD6, 0x7B, 0x9F, 0xD8,
+ 0x74, 0xFD, 0xAD, 0xFD, 0xFB, 0x94, 0x82, 0x29, 0x26, 0xD4, 0x42, 0x7A, 0xB1, 0xB3, 0xEF, 0x68,
+ 0x7D, 0xEA, 0xD5, 0x5B, 0x74, 0x27, 0x9E, 0xB5, 0xEE, 0xB9, 0x7A, 0x32, 0x29, 0x52, 0xE2, 0xC4,
+ 0xB6, 0xAE, 0x8F, 0x08, 0xF2, 0xD1, 0xDE, 0x58, 0xE3, 0xFA, 0x7C, 0xB4, 0x5A, 0xDE, 0x53, 0xCD,
+ 0x87, 0xAE, 0xEA, 0x4E, 0x4E, 0xCC, 0x4D, 0x15, 0x03, 0x51, 0x9B, 0xE2, 0xBD, 0xF5, 0x7B, 0x06,
+ 0x7D, 0x0A, 0xDB, 0xA2, 0xD3, 0xE0, 0xFA, 0x7E, 0xB9, 0x95, 0x0E, 0x83, 0x74, 0x72, 0xFA, 0xA3,
+ 0x33, 0x3D, 0x6B, 0x86, 0xBA, 0x67, 0xC6, 0xD6, 0x9D, 0x12, 0x6F, 0xFB, 0xED, 0x64, 0x22, 0x12,
+ 0x97, 0xD1, 0x88, 0xCF, 0xB1, 0xB1, 0x00, 0x8D, 0x97, 0x1E, 0x08, 0xEF, 0x3B, 0x0B, 0xD3, 0x57,
+ 0x60, 0x81, 0xAE, 0xB7, 0x92, 0xDD, 0x41, 0x0C, 0xC8, 0xB3, 0xED, 0x36, 0x1A, 0xF1, 0x78, 0x6A,
+ 0x66, 0xD2, 0xAE, 0x6C, 0xF3, 0x30, 0x2A, 0xCA, 0xAE, 0x7B, 0xA3, 0x92, 0x74, 0x52, 0x8B, 0x42,
+ 0x05, 0xB3, 0x65, 0xAB, 0x5D, 0x75, 0x23, 0x76, 0xC4, 0x9A, 0x3D, 0x8F, 0x0D, 0x7D, 0x0C, 0xED,
+ 0x08, 0xD7, 0x98, 0xFD, 0x94, 0xEF, 0x08, 0x2D, 0x0A, 0xA1, 0x4D, 0xCB, 0x71, 0xA5, 0xAF, 0x8D,
+ 0x4D, 0xEF, 0x0E, 0xD2, 0x34, 0x47, 0x22, 0x22, 0x9F, 0x4B, 0xA6, 0x48, 0x8C, 0xA1, 0xE0, 0xEC,
+ 0x42, 0x04, 0xFF, 0xE5, 0x9B, 0x81, 0xBB, 0x97, 0xAF, 0x3A, 0x10, 0x4F, 0x55, 0x74, 0x8D, 0x4D,
+ 0x62, 0x83, 0xB7, 0x9E, 0x12, 0x49, 0x76, 0xB7, 0xD2, 0x27, 0xE9, 0xFD, 0x6F, 0x5C, 0xCA, 0x27,
+ 0xF8, 0xE2, 0x68, 0x61, 0x5A, 0x46, 0x17, 0xEF, 0xE8, 0x74, 0x83, 0x41, 0x9F, 0x6F, 0xDB, 0xC7,
+ 0x9F, 0x3E, 0xE4, 0xF3, 0x41, 0x32, 0x1C, 0x0E, 0x85, 0x59, 0x96, 0x09, 0x6E, 0xE9, 0xC4, 0xA6,
+ 0xE2, 0x49, 0x3E, 0x20, 0xB4, 0x9A, 0xB2, 0x36, 0x9D, 0x72, 0xF3, 0xA4, 0xA3, 0x98, 0x53, 0xB7,
+ 0x61, 0x4E, 0xED, 0x81, 0xF9, 0x7E, 0x64, 0xAA, 0xE4, 0x97, 0x1A, 0x5A, 0x3B, 0xA9, 0x7D, 0xB2,
+ 0x6F, 0x7E, 0xA2, 0x6F, 0x7F, 0xBC, 0x5F, 0xFB, 0x58, 0xBE, 0x8D, 0x92, 0xF9, 0x3F, 0x63, 0x59,
+ 0x28, 0x6D, 0xF0, 0x5F, 0x80, 0xCB, 0x61, 0xE4, 0x3A, 0x4E, 0x97, 0x5C, 0xDB, 0xCC, 0x1B, 0xBD,
+ 0xE7, 0xDA, 0xC7, 0x28, 0xD2, 0xF6, 0x50, 0xB9, 0xEB, 0xCA, 0xF0, 0x72, 0x4C, 0x08, 0x0F, 0x94,
+ 0x60, 0x80, 0x92, 0x19, 0x10, 0xC7, 0x20, 0x53, 0x0D, 0x4D, 0x0A, 0xE5, 0x96, 0x53, 0x9B, 0xDB,
+ 0x56, 0x28, 0x2D, 0x15, 0xD4, 0x7A, 0x37, 0x26, 0xEC, 0xCF, 0xD1, 0x42, 0xA3, 0x27, 0x15, 0x1B,
+ 0xF6, 0xC7, 0x45, 0x67, 0x7C, 0x76, 0xD9, 0x7F, 0x7A, 0xAE, 0xBF, 0x95, 0x4F, 0xEC, 0x12, 0xEB,
+ 0x3D, 0x66, 0xB3, 0x7F, 0x74, 0x9C, 0xFD, 0xA3, 0x97, 0x7F, 0xC5, 0x6E, 0x8D, 0x68, 0xD1, 0xB0,
+ 0xEE, 0x4B, 0xDB, 0x82, 0x5E, 0x48, 0x1B, 0xB9, 0x25, 0xD8, 0xCA, 0x31, 0xB5, 0x8C, 0xC1, 0x8C,
+ 0x90, 0x52, 0x65, 0xB4, 0x7C, 0x6D, 0x5B, 0x2A, 0x8D, 0x38, 0x12, 0x63, 0x95, 0xB4, 0xAE, 0x34,
+ 0xA5, 0x86, 0x6B, 0x36, 0x2A, 0x24, 0xAF, 0x8E, 0x0D, 0x6C, 0xB3, 0x67, 0xD0, 0x67, 0xB1, 0x0D,
+ 0xE2, 0x07, 0x4B, 0xFD, 0x98, 0x2E, 0x01, 0x9D, 0x66, 0x5E, 0x8F, 0x71, 0xED, 0xEB, 0x9C, 0x97,
+ 0x09, 0x48, 0x61, 0x90, 0xC2, 0x22, 0x59, 0xB1, 0x41, 0xCB, 0x8D, 0x02, 0xEA, 0x97, 0x48, 0xC0,
+ 0xCB, 0x3A, 0xF5, 0x16, 0xBC, 0xE7, 0xD9, 0xAF, 0xEB, 0x0E, 0xE7, 0x80, 0xEE, 0x26, 0x28, 0xBA,
+ 0xD0, 0x19, 0x03, 0x65, 0x0C, 0x86, 0x69, 0xA6, 0x6D, 0x24, 0x32, 0x49, 0x0C, 0x88, 0x9D, 0x7A,
+ 0x0A, 0x7D, 0xF6, 0x84, 0x9C, 0x55, 0x4A, 0xC1, 0x49, 0x82, 0x66, 0xAF, 0xDA, 0x43, 0xC1, 0xF2,
+ 0xC7, 0x8A, 0xAD, 0x29, 0x55, 0x39, 0x39, 0x0F, 0xA7, 0xD1, 0x97, 0x6F, 0x90, 0x05, 0x82, 0x84,
+ 0x67, 0x8F, 0x7F, 0xE0, 0x60, 0x7D, 0xA6, 0x8B, 0x22, 0x6A, 0xBD, 0xCC, 0x0B, 0x0C, 0xE3, 0x65,
+ 0xD1, 0xFC, 0x8C, 0x48, 0xA1, 0x6F, 0x80, 0x11, 0x37, 0x06, 0x56, 0x2E, 0x15, 0x0B, 0x36, 0xF0,
+ 0xA7, 0x5E, 0x57, 0x07, 0xFF, 0x75, 0x45, 0xFE, 0x14, 0x18, 0xE6, 0x27, 0x80, 0x0D, 0xE2, 0xB3,
+ 0x6E, 0x0F, 0x62, 0x76, 0xFC, 0x20, 0xFB, 0xC9, 0x05, 0x46, 0x11, 0x14, 0xF5, 0x42, 0x42, 0xAE,
+ 0x1A, 0xCD, 0x36, 0x7B, 0x04, 0x91, 0x22, 0x29, 0x46, 0x0C, 0x1B, 0x05, 0xB9, 0x3F, 0x3F, 0x51,
+ 0x2C, 0x1A, 0xA9, 0x2D, 0xDB, 0x67, 0xCB, 0xA1, 0x6A, 0xEA, 0x5E, 0x44, 0x72, 0xC1, 0x80, 0x0A,
+ 0xB5, 0xF2, 0x81, 0x7A, 0x71, 0x2E, 0xA9, 0x62, 0x3B, 0xAE, 0x5C, 0x1A, 0x45, 0xDF, 0x08, 0xD8,
+ 0xD9, 0xCA, 0xA1, 0xF6, 0xE1, 0xC8, 0xD9, 0x1B, 0xA7, 0xA2, 0x62, 0xFB, 0xF0, 0xE9, 0x83, 0xBD,
+ 0xFB, 0xAA, 0x65, 0x73, 0x4B, 0x30, 0xBE, 0x74, 0xC3, 0x62, 0x92, 0x8C, 0x5D, 0x73, 0xE6, 0x86,
+ 0xE5, 0x5D, 0x53, 0xD9, 0x78, 0x89, 0xF1, 0x07, 0x02, 0xAE, 0xC7, 0x67, 0xF5, 0x47, 0xAB, 0xCF,
+ 0x10, 0x26, 0xFA, 0x36, 0xA8, 0xBA, 0xB1, 0x91, 0x72, 0x48, 0xE4, 0x41, 0x2C, 0x18, 0x00, 0xE7,
+ 0x56, 0xB4, 0x7D, 0x83, 0x1E, 0x5F, 0x1E, 0xE0, 0x0E, 0x0D, 0xF5, 0x9A, 0x6B, 0x8F, 0x5A, 0xA6,
+ 0x17, 0x1D, 0x65, 0xAA, 0x44, 0x85, 0x58, 0x0B, 0x11, 0xDB, 0x51, 0xD7, 0xA9, 0x07, 0x9B, 0x3D,
+ 0x48, 0x98, 0xA6, 0x1A, 0xD9, 0x77, 0x75, 0xD4, 0x12, 0x59, 0x1E, 0x43, 0xFA, 0x54, 0x42, 0x12,
+ 0xFA, 0xBB, 0x2B, 0xE3, 0x31, 0x83, 0xF5, 0xF1, 0x0F, 0x92, 0x16, 0x6B, 0xE4, 0x73, 0x07, 0x1B,
+ 0x21, 0x4B, 0xE2, 0x0A, 0xF9, 0x50, 0x24, 0x82, 0xBE, 0x9D, 0x0C, 0xB6, 0xFE, 0xE8, 0x13, 0xF3,
+ 0xB5, 0x70, 0x40, 0x52, 0x1C, 0x51, 0x12, 0x2C, 0x23, 0x19, 0xFA, 0xCF, 0x1F, 0x3A, 0x7C, 0x24,
+ 0xCE, 0x43, 0xD1, 0xDE, 0x4F, 0xF0, 0xA3, 0x8B, 0x4F, 0x9D, 0x98, 0x28, 0x87, 0xF4, 0x23, 0xD7,
+ 0x9F, 0x6A, 0xAA, 0x8A, 0xD7, 0xEF, 0xE7, 0xC8, 0xDF, 0x60, 0xCD, 0x7E, 0x35, 0x18, 0xE9, 0x1B,
+ 0x9D, 0x4C, 0x4A, 0x91, 0xC0, 0xF6, 0xA9, 0x89, 0xB1, 0x90, 0x39, 0x7D, 0x0D, 0x78, 0x5F, 0xBE,
+ 0x34, 0x74, 0x4B, 0x0E, 0x14, 0xD6, 0x46, 0x95, 0x75, 0x4D, 0xC7, 0x0D, 0xC5, 0x51, 0x5E, 0x4C,
+ 0x77, 0xD8, 0x7F, 0x54, 0x21, 0xB2, 0x5E, 0xDC, 0x11, 0x37, 0xA0, 0x2A, 0x8D, 0xC9, 0x9E, 0xC8,
+ 0xAC, 0x69, 0x67, 0x97, 0xD4, 0x1D, 0x37, 0xD1, 0x8C, 0x72, 0xF0, 0xD8, 0xDC, 0x13, 0x44, 0xFE,
+ 0x86, 0x09, 0x4C, 0x6C, 0xEB, 0xE5, 0x05, 0x24, 0x49, 0x58, 0x2F, 0xAB, 0x9A, 0x3D, 0xA6, 0x92,
+ 0x28, 0x2B, 0x76, 0x26, 0x8D, 0x80, 0xAE, 0x09, 0xB9, 0x58, 0x34, 0x80, 0x2C, 0x6B, 0xBF, 0xE4,
+ 0x08, 0x04, 0x52, 0xE4, 0x70, 0xAB, 0x99, 0x21, 0x2C, 0xD3, 0x47, 0xB2, 0xE8, 0x6E, 0x96, 0x51,
+ 0x32, 0x23, 0x85, 0x6E, 0xBA, 0x1A, 0x2B, 0x36, 0x28, 0xD7, 0x5D, 0xFF, 0x9A, 0xCC, 0xF1, 0x9C,
+ 0x98, 0x40, 0x42, 0xBA, 0x99, 0xA3, 0xB1, 0xDE, 0xFF, 0x81, 0x54, 0xBA, 0xE9, 0x3F, 0xDD, 0x7F,
+ 0x55, 0x88, 0x8C, 0x27, 0x49, 0x51, 0xE6, 0x29, 0x91, 0x57, 0x53, 0x3C, 0x8D, 0x6E, 0x8C, 0x3F,
+ 0x78, 0x57, 0xB2, 0x1C, 0x8D, 0x5B, 0x5B, 0xA7, 0xA7, 0x17, 0x4C, 0x5A, 0x16, 0xC5, 0x5A, 0xAA,
+ 0x1B, 0x66, 0xA8, 0x68, 0x6A, 0xF6, 0xD6, 0xEB, 0xE7, 0x99, 0x64, 0x12, 0xF9, 0x77, 0xD3, 0xA3,
+ 0x07, 0xB6, 0xDF, 0xBD, 0xE9, 0xF8, 0xCC, 0x75, 0xAF, 0x49, 0x48, 0x8E, 0x23, 0xEE, 0x99, 0xFB,
+ 0xCC, 0x35, 0x81, 0xDD, 0xA4, 0x6F, 0xCB, 0x99, 0xFB, 0x7B, 0x6E, 0xC2, 0x8C, 0x6B, 0xB7, 0x7C,
+ 0x07, 0x7D, 0x01, 0x7D, 0x17, 0x4B, 0xD3, 0xCD, 0xFD, 0x48, 0x3D, 0x13, 0x8F, 0xCA, 0xA0, 0x39,
+ 0x52, 0x2D, 0x4C, 0xF8, 0xE9, 0x09, 0xBF, 0x94, 0x99, 0x11, 0xDE, 0x32, 0x63, 0xFC, 0xB7, 0x19,
+ 0xFF, 0xA7, 0xA7, 0x3F, 0x93, 0xFE, 0xAC, 0xAB, 0x45, 0xCE, 0x81, 0x89, 0x41, 0xBA, 0xE5, 0x9A,
+ 0x32, 0xB9, 0x1C, 0x13, 0xBA, 0x52, 0x33, 0xE3, 0xF2, 0x8F, 0x67, 0x78, 0x7A, 0x8E, 0x3D, 0x47,
+ 0x26, 0x3C, 0xFF, 0xAE, 0xEC, 0x36, 0x86, 0x3D, 0x1A, 0xF1, 0xD2, 0x09, 0x36, 0x62, 0x9D, 0x08,
+ 0x72, 0xDC, 0x84, 0x35, 0xF4, 0x85, 0xF1, 0xF2, 0xF5, 0x7E, 0x5F, 0x30, 0xBC, 0x7F, 0x53, 0x52,
+ 0x17, 0x24, 0x2E, 0x99, 0xEE, 0xE9, 0xBC, 0x20, 0x2B, 0x77, 0x71, 0x79, 0x46, 0xA2, 0x72, 0x56,
+ 0x71, 0x25, 0xDB, 0x96, 0xDF, 0xEE, 0xF4, 0xF3, 0xEC, 0x43, 0xE1, 0xA2, 0x2A, 0xC8, 0xDC, 0xEB,
+ 0x59, 0x5A, 0xAE, 0x4C, 0xC6, 0x32, 0x62, 0xC6, 0x4F, 0x13, 0xF5, 0x8F, 0xA2, 0xAF, 0xF5, 0x47,
+ 0xCF, 0xC4, 0xC2, 0x89, 0xC2, 0xD9, 0x6B, 0x43, 0x82, 0x6D, 0xFB, 0x1A, 0x22, 0x26, 0x92, 0xF7,
+ 0x8B, 0xF3, 0x9C, 0xC6, 0x4F, 0x37, 0x22, 0x1A, 0xAB, 0x64, 0xAC, 0xA0, 0xF1, 0x89, 0xC0, 0xDE,
+ 0x2E, 0xFF, 0x54, 0x36, 0xCD, 0xA9, 0xB7, 0xD0, 0x53, 0x47, 0xEB, 0x6D, 0x71, 0xBC, 0x9D, 0x30,
+ 0x7C, 0x7B, 0x06, 0xC8, 0xE0, 0x39, 0xC2, 0x42, 0x3F, 0x05, 0x6D, 0xD0, 0xEF, 0xFB, 0x1B, 0xB5,
+ 0x18, 0x28, 0x24, 0xA3, 0xE1, 0x82, 0x60, 0x6A, 0x26, 0x92, 0x44, 0xA1, 0xFD, 0x39, 0x0D, 0x23,
+ 0xBF, 0xD8, 0x95, 0x5A, 0x04, 0x5C, 0x6E, 0x7C, 0xE3, 0x19, 0x18, 0x8B, 0x0D, 0xA2, 0xBA, 0x14,
+ 0x26, 0x9D, 0xA8, 0x3B, 0x6A, 0xA6, 0x0B, 0xDB, 0x1E, 0xE0, 0xD9, 0x88, 0xEC, 0xDC, 0x24, 0x90,
+ 0x4C, 0x16, 0x73, 0xB2, 0x65, 0x97, 0x53, 0x9A, 0xD1, 0x31, 0x2A, 0x39, 0x13, 0xC2, 0x44, 0x70,
+ 0x2C, 0x01, 0x39, 0xAB, 0xB2, 0x35, 0x37, 0x55, 0xD3, 0x38, 0x59, 0xB0, 0xEF, 0xF0, 0x07, 0x37,
+ 0x05, 0xFA, 0x99, 0x31, 0x53, 0xF5, 0x8D, 0xD8, 0x0A, 0x41, 0x8E, 0x8F, 0xB7, 0xC6, 0xD0, 0xF7,
+ 0x9D, 0xB9, 0xB1, 0x40, 0xF4, 0xDA, 0xF0, 0xC2, 0x54, 0x77, 0xA2, 0x62, 0xFB, 0xF4, 0x9C, 0x5F,
+ 0x89, 0x28, 0x3B, 0xF6, 0x4C, 0x6C, 0xBD, 0x61, 0x64, 0x21, 0x4F, 0x6B, 0xDA, 0xBE, 0x03, 0x89,
+ 0x3D, 0xED, 0x83, 0x09, 0x2B, 0x75, 0xB0, 0x5C, 0xB3, 0x39, 0x5D, 0xC4, 0xA0, 0x31, 0x8C, 0xD7,
+ 0x3A, 0xB2, 0xFA, 0x1F, 0x68, 0x09, 0x3D, 0x03, 0x3A, 0x58, 0x52, 0x26, 0x1A, 0x79, 0x8C, 0x7D,
+ 0x14, 0x16, 0x64, 0xD2, 0x71, 0x05, 0xFC, 0x4D, 0xD7, 0x34, 0x34, 0x42, 0xFF, 0x58, 0x42, 0x53,
+ 0xFF, 0x32, 0xF1, 0xF5, 0x76, 0x8E, 0x0C, 0x7D, 0x9E, 0x84, 0x03, 0xD4, 0xD0, 0x1B, 0x3A, 0x24,
+ 0x2E, 0xBC, 0xC8, 0x94, 0xEB, 0xB2, 0x58, 0x41, 0xD0, 0xCC, 0x7A, 0xD0, 0xB3, 0x47, 0x28, 0xA8,
+ 0x08, 0x07, 0xD1, 0x19, 0xE4, 0x62, 0x3D, 0xD7, 0x8F, 0xD0, 0xC6, 0xBC, 0xE2, 0xB9, 0x7B, 0x65,
+ 0x72, 0x10, 0xF9, 0x9D, 0x42, 0x68, 0x69, 0x71, 0x72, 0xBC, 0x78, 0xF7, 0x43, 0x5B, 0xEC, 0xAD,
+ 0xCB, 0x57, 0x25, 0x32, 0x95, 0xDA, 0x44, 0x4F, 0x8D, 0x28, 0x5D, 0x4B, 0x3E, 0x60, 0xA3, 0xE2,
+ 0xF6, 0xFD, 0x5B, 0x96, 0x4C, 0x9A, 0x45, 0x50, 0x49, 0xF4, 0x27, 0x36, 0xD7, 0x83, 0xBE, 0xE8,
+ 0x64, 0xA9, 0x92, 0xDF, 0xE2, 0x7B, 0xFF, 0xB5, 0x9B, 0x68, 0x29, 0x99, 0xD1, 0xC7, 0x8E, 0x1D,
+ 0x3D, 0x9A, 0xAB, 0x22, 0x04, 0x91, 0x12, 0x31, 0xC3, 0xFA, 0x21, 0x8E, 0xE4, 0x42, 0x04, 0x45,
+ 0x8B, 0x2C, 0xA3, 0x27, 0xB7, 0x6F, 0x3E, 0xBC, 0x29, 0xDB, 0xD2, 0x0C, 0x4B, 0x20, 0x58, 0x0A,
+ 0x8F, 0x77, 0x65, 0xF5, 0x11, 0x38, 0x83, 0x9E, 0x06, 0x11, 0xB0, 0xAD, 0x9F, 0xB4, 0x54, 0xF0,
+ 0x45, 0x8C, 0x49, 0xDF, 0x6A, 0x30, 0xA1, 0xED, 0xE4, 0x45, 0x03, 0xD8, 0x21, 0xDB, 0x2F, 0x31,
+ 0xBC, 0x1F, 0xF1, 0x12, 0xB3, 0xC2, 0xC0, 0x2F, 0x41, 0xE6, 0xCA, 0x62, 0x81, 0xEE, 0xE0, 0x31,
+ 0xC8, 0xF2, 0xF6, 0xC6, 0x8F, 0x07, 0x1D, 0x65, 0x5C, 0xBC, 0x34, 0x48, 0xE5, 0x19, 0xD5, 0xDD,
+ 0x25, 0x1C, 0x84, 0xB3, 0x99, 0x2C, 0x63, 0x62, 0x0C, 0xA0, 0x5B, 0xF5, 0x16, 0xC4, 0x30, 0xA4,
+ 0x1D, 0x71, 0x3A, 0x36, 0xE6, 0x64, 0x7B, 0xD2, 0x1F, 0x9E, 0xB7, 0xC4, 0x5A, 0x03, 0x43, 0xF7,
+ 0xB9, 0xE6, 0x22, 0x22, 0x49, 0x74, 0x0B, 0x9C, 0x65, 0x1E, 0xF8, 0xEC, 0x01, 0xF7, 0x74, 0x9C,
+ 0x20, 0xF6, 0x5E, 0xFC, 0xAD, 0xBE, 0xFB, 0xE2, 0x45, 0x87, 0x1E, 0x78, 0x19, 0xE1, 0x8F, 0xB0,
+ 0xEC, 0xD2, 0x41, 0xAC, 0xCF, 0xF3, 0x80, 0x07, 0xD2, 0xFB, 0x56, 0x88, 0xF5, 0x8C, 0x82, 0x75,
+ 0xBF, 0x85, 0xC1, 0x53, 0xC5, 0x38, 0xBE, 0x7D, 0x85, 0xCA, 0xE2, 0x1B, 0x47, 0x48, 0xF8, 0xA3,
+ 0xD8, 0x48, 0x36, 0xD8, 0x98, 0xBC, 0x7E, 0x6B, 0x5B, 0x0B, 0xCD, 0xFA, 0xF5, 0xEA, 0xF6, 0x6B,
+ 0x6F, 0x66, 0xF9, 0xB9, 0xE3, 0xB7, 0x7E, 0xE8, 0x55, 0x37, 0xE6, 0x04, 0x66, 0xFC, 0xA6, 0xD7,
+ 0xBF, 0xF7, 0x33, 0xF8, 0xEA, 0xBF, 0x59, 0x7D, 0x04, 0xFC, 0x33, 0x9E, 0x8D, 0x30, 0xD8, 0xDD,
+ 0xCF, 0x12, 0x8F, 0x45, 0xD8, 0x58, 0x44, 0xFF, 0xB2, 0xC1, 0x31, 0x31, 0xFA, 0x4B, 0xCA, 0xDF,
+ 0x5A, 0x0F, 0xD0, 0x3A, 0xA7, 0x70, 0xA2, 0x0F, 0x04, 0x45, 0x14, 0xF4, 0x19, 0x0C, 0xF8, 0xD2,
+ 0x95, 0xEA, 0xF2, 0xC5, 0x33, 0xE2, 0xFE, 0xBB, 0xC9, 0x9F, 0x58, 0x63, 0x3A, 0x83, 0x19, 0x69,
+ 0x3B, 0x6D, 0x77, 0x52, 0x50, 0xD3, 0x3D, 0xF7, 0x02, 0x55, 0xEE, 0xA4, 0xD8, 0xFF, 0xAC, 0x56,
+ 0xC3, 0x91, 0xDD, 0xCE, 0x70, 0x1A, 0x34, 0x3B, 0x35, 0x1E, 0x9C, 0x33, 0x08, 0x74, 0x9F, 0x3B,
+ 0x07, 0x98, 0x03, 0xF6, 0x42, 0x52, 0xDF, 0x0D, 0x31, 0xC9, 0x50, 0xD3, 0x34, 0x89, 0x6E, 0xBC,
+ 0xF8, 0x57, 0xEC, 0xFD, 0x7F, 0xED, 0xCA, 0xA6, 0x1E, 0xE4, 0x91, 0x1F, 0xFD, 0x27, 0x2F, 0x9A,
+ 0x19, 0x20, 0x90, 0x6B, 0xFF, 0x90, 0xD0, 0x8D, 0x6E, 0xD2, 0x00, 0x59, 0x14, 0x4D, 0xFD, 0x3D,
+ 0x89, 0x2D, 0x60, 0xF0, 0x15, 0xAF, 0x70, 0xC1, 0x75, 0xCF, 0x7B, 0xD1, 0xCC, 0xE0, 0xE3, 0xE3,
+ 0x5F, 0x5E, 0x8B, 0x66, 0x9E, 0xFB, 0xD1, 0x3D, 0x83, 0x37, 0x6E, 0x34, 0x93, 0xC4, 0x5C, 0x99,
+ 0x14, 0x60, 0xD2, 0x68, 0x20, 0xFF, 0xA1, 0xD7, 0xBE, 0xF6, 0xD0, 0x5F, 0x1D, 0x80, 0x3C, 0xCC,
+ 0x5D, 0xFC, 0x16, 0x7C, 0x05, 0xF0, 0x66, 0x67, 0x02, 0x15, 0x56, 0x57, 0x00, 0x01, 0x22, 0x7D,
+ 0xDE, 0x8D, 0x98, 0x12, 0xE8, 0x69, 0x08, 0xBE, 0x36, 0x88, 0xC3, 0xAE, 0x71, 0x36, 0xE9, 0x46,
+ 0x76, 0xDD, 0xC8, 0x6B, 0xE1, 0xC2, 0xD7, 0xD0, 0xC8, 0xDF, 0x26, 0xF1, 0xEF, 0x56, 0x51, 0x18,
+ 0x9C, 0x47, 0x9F, 0x5C, 0xCB, 0xEB, 0x25, 0xBE, 0xBE, 0x02, 0xFF, 0xFB, 0x4B, 0xE4, 0xF5, 0x62,
+ 0xCE, 0x3F, 0x5F, 0x7F, 0x72, 0x1F, 0x1A, 0x75, 0x0E, 0xB7, 0xBD, 0x2C, 0xBC, 0x9F, 0xA3, 0x79,
+ 0xF8, 0x2C, 0xB6, 0xA8, 0x1A, 0x7D, 0xBB, 0x96, 0x8F, 0xDB, 0x12, 0xA8, 0x96, 0x2D, 0xBB, 0x63,
+ 0xDC, 0xD1, 0x19, 0xFD, 0x46, 0x24, 0x04, 0x89, 0x20, 0xEB, 0x8D, 0xEA, 0xC5, 0x71, 0xA5, 0x4E,
+ 0xDC, 0x75, 0x12, 0xB8, 0xC2, 0x64, 0x90, 0xFA, 0x5C, 0x1F, 0xBA, 0xF6, 0xA0, 0x9B, 0x9D, 0xEA,
+ 0x25, 0x61, 0xB4, 0x5B, 0x3D, 0x22, 0xBB, 0xD1, 0x63, 0x42, 0x38, 0x68, 0xFE, 0x1E, 0x86, 0x4F,
+ 0x8D, 0x4F, 0xA5, 0xF2, 0x9C, 0x9C, 0x34, 0xF9, 0x56, 0x49, 0x64, 0x0A, 0x11, 0x11, 0x55, 0xD3,
+ 0xCD, 0x88, 0x4C, 0x13, 0xBD, 0xB9, 0xE2, 0x52, 0xB6, 0x2A, 0x99, 0x49, 0x31, 0xD8, 0xF2, 0x19,
+ 0x2A, 0x3B, 0x17, 0xCE, 0x2B, 0xD1, 0x83, 0xF9, 0x72, 0x21, 0x35, 0xBF, 0xD7, 0xF2, 0x6D, 0xE9,
+ 0x87, 0x38, 0x8A, 0xB6, 0x65, 0x32, 0x92, 0xA6, 0x15, 0x19, 0x95, 0x92, 0x21, 0xC8, 0x8C, 0x65,
+ 0xEC, 0x9D, 0x65, 0x5B, 0x42, 0xD8, 0xEA, 0x2C, 0x14, 0x78, 0x61, 0xDC, 0xE7, 0x3F, 0xD3, 0xDD,
+ 0xB4, 0x63, 0xD1, 0x9D, 0xC7, 0x7F, 0xC1, 0x96, 0x75, 0x94, 0xF0, 0x83, 0x71, 0x2C, 0x43, 0x83,
+ 0xED, 0xBC, 0x55, 0xAF, 0x94, 0x4D, 0x2D, 0x63, 0x4E, 0x54, 0xE3, 0x71, 0x24, 0x45, 0x63, 0x48,
+ 0x0A, 0xFD, 0x23, 0x53, 0xBA, 0x14, 0x64, 0x81, 0x6B, 0x29, 0xE0, 0x1B, 0x35, 0xB2, 0x1B, 0x18,
+ 0xB0, 0xBC, 0xC0, 0x00, 0xE3, 0x65, 0x80, 0x13, 0x6E, 0x95, 0x84, 0xE7, 0x08, 0xF1, 0xCC, 0xA8,
+ 0x76, 0xCB, 0xB6, 0x1C, 0x63, 0x2D, 0xE7, 0xDE, 0xB5, 0xB4, 0x86, 0x62, 0xD5, 0xF5, 0x90, 0x21,
+ 0x99, 0x2F, 0x56, 0xE4, 0xE0, 0xB5, 0xE3, 0xED, 0x1D, 0x26, 0x47, 0x10, 0x18, 0x64, 0x70, 0x8A,
+ 0xC0, 0x07, 0xE7, 0x04, 0x31, 0x26, 0xA8, 0xAA, 0x0F, 0xD1, 0x74, 0x62, 0x97, 0xB4, 0xA0, 0xA7,
+ 0x16, 0x47, 0x34, 0x7A, 0x7F, 0x38, 0x36, 0xDB, 0x50, 0x0C, 0xF6, 0x7C, 0x64, 0x2C, 0x2F, 0x3B,
+ 0xB3, 0x3B, 0x46, 0xF7, 0x3C, 0x79, 0x7A, 0x6C, 0x5B, 0x9B, 0x22, 0x15, 0xBD, 0xCE, 0xF2, 0x3E,
+ 0x0E, 0x21, 0x5D, 0xCD, 0x5C, 0x3C, 0xDC, 0xAD, 0x1F, 0x54, 0x0E, 0x6A, 0xF1, 0xE3, 0x93, 0x50,
+ 0x19, 0xD3, 0x17, 0x82, 0xE9, 0x8A, 0x59, 0x19, 0x64, 0x52, 0x3E, 0x07, 0xDF, 0x0B, 0xFF, 0x37,
+ 0x28, 0xB9, 0x31, 0xB1, 0xA4, 0xC9, 0x81, 0x48, 0xE9, 0xCF, 0xCB, 0x05, 0xB3, 0x1C, 0x3C, 0xAF,
+ 0x28, 0xDF, 0x24, 0x4A, 0x2F, 0x15, 0x13, 0x2B, 0xBB, 0x89, 0x80, 0x9E, 0xC0, 0x24, 0x86, 0xF0,
+ 0xD9, 0x4D, 0x60, 0x50, 0x28, 0xCF, 0xCF, 0x07, 0x87, 0xD6, 0x71, 0x1B, 0xBE, 0x79, 0x64, 0x77,
+ 0x6F, 0xFB, 0x6E, 0x85, 0xA7, 0x0B, 0x2B, 0xA3, 0xD3, 0x11, 0x5F, 0xC9, 0x8A, 0xC4, 0x7D, 0x9C,
+ 0xC4, 0x05, 0x89, 0x4E, 0xDA, 0x6F, 0x8B, 0x9A, 0x3D, 0x5D, 0xCA, 0xC5, 0x60, 0x28, 0xB0, 0x33,
+ 0x35, 0x79, 0x67, 0x34, 0x95, 0x54, 0xB2, 0xC7, 0x77, 0x46, 0x2D, 0x55, 0x76, 0x02, 0xC1, 0x80,
+ 0x4E, 0x73, 0x8C, 0x1C, 0x9B, 0x37, 0x05, 0x27, 0xEE, 0xEC, 0xDD, 0x1C, 0x13, 0x11, 0xE7, 0x55,
+ 0x23, 0x62, 0x8B, 0x37, 0x83, 0xFE, 0x33, 0xE8, 0x82, 0x6C, 0x5F, 0xE9, 0x94, 0x8B, 0x99, 0x90,
+ 0x2A, 0xF3, 0xBE, 0xF7, 0x4F, 0x16, 0xBE, 0xC5, 0x2C, 0x0E, 0xBA, 0xB9, 0x51, 0xAC, 0x4F, 0x66,
+ 0xDC, 0xA9, 0xA7, 0x06, 0x19, 0x17, 0x03, 0x9B, 0x1D, 0x7A, 0xAA, 0xDC, 0xB3, 0x6B, 0x99, 0x35,
+ 0xE1, 0x8E, 0x4D, 0x1D, 0x37, 0x7E, 0x89, 0xD9, 0x1B, 0x89, 0x7C, 0xA3, 0x6D, 0xCE, 0x8D, 0x87,
+ 0x77, 0xDD, 0x70, 0x4B, 0x5C, 0x08, 0x70, 0x0C, 0x9F, 0x68, 0x86, 0xB9, 0xE4, 0x08, 0x87, 0x98,
+ 0xA9, 0xE5, 0xE0, 0x99, 0xFD, 0x11, 0x95, 0x7D, 0x50, 0x55, 0x33, 0x5A, 0x90, 0x8B, 0x64, 0xF3,
+ 0x9A, 0x9D, 0x28, 0xEC, 0xF9, 0x92, 0x3D, 0x51, 0xD7, 0xAA, 0xF6, 0xA3, 0xDD, 0x23, 0xB3, 0x1A,
+ 0x9B, 0x6F, 0xE8, 0x59, 0xCD, 0x1C, 0x75, 0xE8, 0x7C, 0x5D, 0x65, 0xB4, 0xA9, 0x85, 0x9C, 0x50,
+ 0x48, 0xFA, 0x74, 0xCD, 0x60, 0x0F, 0x89, 0x92, 0x19, 0x0E, 0xF0, 0x32, 0x2D, 0xAB, 0x0A, 0xC3,
+ 0x4A, 0x76, 0x6F, 0x73, 0xD5, 0xCB, 0xFB, 0x5D, 0xFD, 0x09, 0xFC, 0x10, 0xFC, 0x19, 0x28, 0x63,
+ 0xDE, 0xB1, 0x82, 0x3A, 0x28, 0x3F, 0x56, 0x29, 0x7C, 0xA7, 0x12, 0xCB, 0x64, 0x10, 0xAF, 0x7C,
+ 0x97, 0xFA, 0xA7, 0xF5, 0x0A, 0x91, 0xDE, 0x86, 0x8A, 0xAF, 0xC1, 0xC8, 0x2A, 0xBC, 0xE7, 0x60,
+ 0x43, 0x03, 0x93, 0xC6, 0x4B, 0xBC, 0xF4, 0x92, 0x0D, 0xC8, 0x4B, 0x39, 0x8D, 0xC3, 0xF4, 0xA4,
+ 0x1E, 0x84, 0x1F, 0xD2, 0x3B, 0xB9, 0x66, 0xB8, 0xD2, 0x17, 0x76, 0x4E, 0xDD, 0xB8, 0x2D, 0xD3,
+ 0x9E, 0x92, 0xE2, 0xB1, 0x02, 0x99, 0x2B, 0x49, 0x52, 0x51, 0x14, 0xB7, 0xB1, 0xA1, 0x54, 0xFC,
+ 0xE0, 0x62, 0x32, 0x23, 0x1D, 0x55, 0x9B, 0x51, 0xA7, 0xCA, 0xEF, 0xD9, 0x3F, 0x7E, 0xDB, 0x72,
+ 0xBC, 0x9E, 0x56, 0xE3, 0x09, 0x27, 0x42, 0xF8, 0xC3, 0x1A, 0x97, 0xB6, 0x34, 0xB5, 0x6C, 0x55,
+ 0x72, 0xA5, 0x6D, 0xD7, 0x28, 0xB4, 0x87, 0x2D, 0x5E, 0x80, 0xDF, 0x21, 0x34, 0x8C, 0xB3, 0x8E,
+ 0xF4, 0x6B, 0x05, 0x03, 0x54, 0xB2, 0x06, 0x4B, 0x7E, 0xAF, 0x53, 0x48, 0xDF, 0x35, 0x22, 0xD7,
+ 0x7F, 0x19, 0x41, 0xC9, 0xA8, 0x2C, 0x11, 0x22, 0x6A, 0xA4, 0x53, 0x44, 0x3D, 0x18, 0x40, 0x49,
+ 0x3F, 0x4A, 0xC4, 0x93, 0x9D, 0x4A, 0x7C, 0xE6, 0x2C, 0x2C, 0x5C, 0x01, 0x39, 0x5C, 0x1F, 0xC4,
+ 0x20, 0x3D, 0xDB, 0xF5, 0x23, 0x76, 0x2F, 0x6C, 0xD4, 0xCD, 0x2E, 0x00, 0x2B, 0x78, 0xE5, 0x13,
+ 0xD9, 0xA1, 0xC9, 0xEE, 0x58, 0xAE, 0x33, 0x5F, 0xA1, 0xA2, 0xAE, 0x31, 0xA3, 0xA0, 0x66, 0x96,
+ 0xC8, 0xD2, 0xC9, 0x82, 0x5B, 0x6C, 0x14, 0x25, 0x9A, 0x53, 0xB4, 0xC7, 0x37, 0x8E, 0x0D, 0xBF,
+ 0x13, 0x8D, 0x88, 0x51, 0xC9, 0xA8, 0x49, 0x21, 0x6C, 0x33, 0x38, 0xED, 0xA8, 0xAA, 0xF1, 0x5C,
+ 0x42, 0xB0, 0x9B, 0x24, 0xE2, 0xC9, 0x40, 0xF0, 0x20, 0xE4, 0x50, 0x24, 0xAA, 0xA7, 0x74, 0x6C,
+ 0x77, 0x32, 0xC1, 0xB0, 0xE3, 0xA3, 0xE2, 0xB3, 0xE1, 0x6A, 0x32, 0xC9, 0x59, 0x4A, 0xC1, 0x8F,
+ 0xF9, 0xE4, 0x1F, 0x7C, 0x21, 0x3E, 0xA4, 0x98, 0xD1, 0xE9, 0xA9, 0x6A, 0x4C, 0xC6, 0x52, 0x19,
+ 0x89, 0x42, 0x5E, 0x0C, 0x32, 0xBB, 0xF2, 0x23, 0xD7, 0x24, 0x74, 0x6D, 0x32, 0x6E, 0x42, 0x6D,
+ 0x91, 0x80, 0x41, 0xBF, 0x3F, 0x88, 0xF0, 0x5F, 0xB1, 0x39, 0x31, 0xEF, 0x4F, 0x19, 0x14, 0xED,
+ 0xC8, 0x9C, 0xAB, 0xA7, 0x8D, 0xD5, 0x34, 0x3A, 0x0A, 0x7F, 0x0E, 0xAE, 0x02, 0x33, 0xFD, 0xD0,
+ 0xD6, 0xD9, 0xF1, 0x56, 0x2D, 0x21, 0x92, 0x04, 0xD8, 0xBB, 0x67, 0x97, 0x31, 0xF9, 0xBA, 0x03,
+ 0xCD, 0xFB, 0x0E, 0x2C, 0x28, 0x6D, 0x07, 0x7C, 0x3F, 0xFF, 0x83, 0x81, 0xE3, 0xDC, 0x0B, 0xC5,
+ 0x5E, 0x89, 0x47, 0xBC, 0xA1, 0x1F, 0xA8, 0x79, 0x76, 0x88, 0x6B, 0x8A, 0x0F, 0xB2, 0x84, 0xD6,
+ 0xDC, 0x16, 0x78, 0x51, 0x3C, 0xDB, 0xD4, 0x8D, 0xD6, 0x26, 0xD7, 0xDD, 0x51, 0x5E, 0x8E, 0x5A,
+ 0xDD, 0x5D, 0x6D, 0x97, 0xB4, 0x33, 0x43, 0xAB, 0x16, 0x35, 0xA2, 0x9E, 0x46, 0x47, 0x47, 0x8B,
+ 0xE5, 0x39, 0xDD, 0xF1, 0x91, 0xAA, 0x28, 0x33, 0xC4, 0x44, 0x8A, 0x97, 0x32, 0x71, 0x96, 0xE6,
+ 0x03, 0xD1, 0xDC, 0xA7, 0x95, 0xEB, 0xA6, 0x77, 0x1D, 0x58, 0xDE, 0x15, 0x67, 0x59, 0x3E, 0xE3,
+ 0xE4, 0x6D, 0x7F, 0x72, 0x42, 0x63, 0xE9, 0xDD, 0x12, 0x5B, 0xCA, 0x86, 0xF0, 0xAA, 0x35, 0x2A,
+ 0x8F, 0x67, 0xED, 0x4D, 0x53, 0x8F, 0x6D, 0x69, 0x09, 0x51, 0xD9, 0x29, 0xCF, 0xC1, 0x9F, 0xD7,
+ 0x9B, 0x94, 0x1E, 0x4D, 0x99, 0x0E, 0xA1, 0xF0, 0x92, 0x26, 0x8B, 0xA5, 0x1A, 0xC9, 0x17, 0x03,
+ 0xAA, 0xCA, 0xC9, 0xB6, 0xC8, 0x44, 0xC8, 0xD8, 0x5C, 0x7E, 0xDF, 0xCC, 0x48, 0xC2, 0xC0, 0xB2,
+ 0xC9, 0x17, 0xC0, 0x8B, 0x14, 0xA9, 0x89, 0xA2, 0x2C, 0x36, 0x44, 0x5D, 0xB1, 0x4B, 0x66, 0x3A,
+ 0x21, 0x30, 0x26, 0x4D, 0xE7, 0x46, 0xB3, 0x2B, 0x77, 0xC5, 0xBC, 0x24, 0x1E, 0xB8, 0xFA, 0xC3,
+ 0xD5, 0x67, 0x31, 0x74, 0xD0, 0x30, 0x3F, 0x2F, 0xF6, 0xC3, 0x85, 0x98, 0x4F, 0x23, 0x41, 0xB3,
+ 0x1A, 0x0D, 0xF1, 0x6A, 0xD9, 0xAE, 0x56, 0xCA, 0xC8, 0x31, 0xEC, 0x5C, 0x45, 0x25, 0xE7, 0xD6,
+ 0xB0, 0xAA, 0x57, 0x2B, 0xE5, 0x39, 0x3C, 0x2E, 0x61, 0xD5, 0x0D, 0x1A, 0x65, 0x32, 0xE6, 0xBA,
+ 0x39, 0x08, 0x2F, 0x06, 0x22, 0x13, 0x6E, 0x7E, 0x72, 0xD7, 0x45, 0xE7, 0x83, 0xE0, 0x03, 0xCC,
+ 0x66, 0xB0, 0x3A, 0xC1, 0xD2, 0xD5, 0xCD, 0xE6, 0x0B, 0xC3, 0xB6, 0x2B, 0xB4, 0x30, 0xDF, 0x13,
+ 0x68, 0x3C, 0x1A, 0x49, 0xC4, 0x29, 0x51, 0xF4, 0x85, 0xAC, 0x82, 0xAF, 0x74, 0x43, 0xDA, 0x71,
+ 0xE3, 0xAB, 0x34, 0xA3, 0x59, 0xB2, 0xC5, 0x51, 0x70, 0x53, 0x51, 0xD3, 0xFD, 0x34, 0x53, 0x40,
+ 0x28, 0xD1, 0x49, 0xCF, 0x35, 0x34, 0x5F, 0xD0, 0xE0, 0x69, 0x02, 0xCD, 0x1A, 0x96, 0xED, 0x64,
+ 0xA6, 0x8E, 0x77, 0x66, 0x13, 0x76, 0xE5, 0x91, 0x15, 0x6E, 0x74, 0xFC, 0xAA, 0x77, 0x2C, 0x06,
+ 0xAE, 0xCD, 0xE4, 0x46, 0x79, 0xDA, 0x20, 0xAA, 0x21, 0x41, 0x10, 0x89, 0xB9, 0xC2, 0x7C, 0xFA,
+ 0x87, 0x37, 0xE4, 0x16, 0xF2, 0xBD, 0x44, 0xEF, 0xEA, 0x9D, 0xFD, 0x62, 0xC6, 0xCD, 0x38, 0x79,
+ 0x61, 0xF5, 0x79, 0xF0, 0x3C, 0x7A, 0x1F, 0x90, 0x41, 0xDA, 0x1D, 0x73, 0x50, 0x89, 0x05, 0x1C,
+ 0x42, 0x48, 0x9B, 0x21, 0xD6, 0x02, 0x29, 0x0B, 0xA5, 0x30, 0x42, 0x4F, 0x0B, 0xFF, 0x3F, 0x6D,
+ 0xF1, 0x2C, 0x0C, 0x5E, 0x1E, 0x98, 0x18, 0x64, 0xDE, 0x5C, 0x0E, 0xD2, 0x5D, 0x57, 0x7B, 0x70,
+ 0x98, 0x55, 0xE6, 0xBA, 0xAB, 0x5B, 0x98, 0x14, 0x22, 0xD0, 0xC0, 0x78, 0x2E, 0xD3, 0xCC, 0x8E,
+ 0x6E, 0x74, 0xB8, 0x5B, 0xCE, 0xF3, 0xFB, 0x25, 0x1F, 0x1F, 0xB0, 0x25, 0x3A, 0x57, 0x8E, 0xC9,
+ 0x56, 0x90, 0x59, 0x9C, 0x45, 0x5C, 0x5E, 0xC0, 0xC8, 0xBC, 0x1D, 0xED, 0xFB, 0x38, 0x22, 0xA4,
+ 0x32, 0xE8, 0xE8, 0xAC, 0x40, 0xEB, 0x96, 0x94, 0x8C, 0x99, 0x4A, 0xC0, 0xBC, 0xF0, 0xE3, 0x7C,
+ 0x2B, 0x71, 0x9B, 0xFD, 0x86, 0x1D, 0x34, 0x69, 0x46, 0xAD, 0xB2, 0x4A, 0x27, 0x8A, 0xEF, 0xF0,
+ 0xFA, 0x6F, 0x0F, 0xFB, 0x9F, 0x01, 0xBD, 0xBE, 0x3F, 0x11, 0x54, 0x22, 0x6E, 0xFF, 0x75, 0x56,
+ 0x07, 0x69, 0x43, 0x47, 0x19, 0xDC, 0x79, 0xFD, 0x7F, 0xAE, 0xF5, 0x7D, 0x90, 0x83, 0x39, 0xF0,
+ 0x4B, 0x6D, 0x4C, 0x49, 0xC6, 0xFD, 0xCE, 0xBE, 0xB8, 0xDF, 0xAE, 0x61, 0xA5, 0xAD, 0x2D, 0xD6,
+ 0xA0, 0xF4, 0xCC, 0x4B, 0x48, 0xBC, 0xA2, 0xE7, 0x64, 0x71, 0x3E, 0x4E, 0xEB, 0xBA, 0xC8, 0x72,
+ 0xF2, 0xBB, 0x59, 0x4E, 0xB5, 0x68, 0x8C, 0xA0, 0x2F, 0xEF, 0xFA, 0x6F, 0x7F, 0x2A, 0x45, 0x0D,
+ 0x78, 0xDF, 0x03, 0x5B, 0x22, 0x0C, 0x6B, 0xDC, 0x42, 0xD9, 0xB5, 0xA9, 0x5D, 0x5F, 0x3A, 0xBC,
+ 0xDB, 0x9D, 0xFD, 0x7F, 0xC1, 0xFA, 0x63, 0x81, 0xB0, 0x40, 0x02, 0xCC, 0x83, 0x95, 0x7E, 0xA1,
+ 0xD7, 0x1D, 0x1D, 0xC1, 0x30, 0x20, 0x18, 0x50, 0x89, 0x76, 0x56, 0x11, 0xE7, 0xEF, 0x5F, 0x98,
+ 0x7D, 0xCB, 0x42, 0x1C, 0x24, 0x10, 0x1D, 0x8F, 0x21, 0x3A, 0x82, 0x34, 0xDA, 0xFE, 0xA1, 0x1B,
+ 0x7C, 0x53, 0x2F, 0x77, 0x9E, 0x0E, 0x92, 0xD9, 0x5E, 0x9A, 0x77, 0xDD, 0xB1, 0x2D, 0xA8, 0x54,
+ 0xB1, 0x3D, 0x88, 0xF9, 0xB8, 0x1E, 0xC7, 0x81, 0xC2, 0xF7, 0x68, 0x0F, 0xEB, 0x1F, 0xCB, 0xD1,
+ 0xAD, 0x28, 0x31, 0x74, 0x54, 0x42, 0x2F, 0x28, 0x8C, 0x39, 0x96, 0xC1, 0xAA, 0x68, 0xA0, 0x42,
+ 0xA1, 0x97, 0x34, 0xD3, 0x3E, 0x9F, 0x9E, 0xCE, 0x22, 0x48, 0x88, 0x14, 0x1D, 0x94, 0x74, 0x8E,
+ 0x39, 0x42, 0x07, 0x0C, 0x5D, 0x0C, 0x70, 0x98, 0x34, 0x31, 0x16, 0xC0, 0xCF, 0xB7, 0xEF, 0xE5,
+ 0x6D, 0x21, 0x74, 0x28, 0xA4, 0x76, 0x77, 0x90, 0x92, 0xD1, 0xE7, 0xA6, 0x03, 0x01, 0x27, 0x9C,
+ 0x08, 0x95, 0x0E, 0x5D, 0x5B, 0x2A, 0x68, 0x14, 0xBB, 0xD5, 0x28, 0x44, 0xA6, 0xEE, 0x9B, 0xBA,
+ 0xF3, 0xD6, 0x87, 0x0B, 0x69, 0x9A, 0x54, 0x17, 0x49, 0x43, 0x64, 0xA3, 0x2A, 0x37, 0x32, 0x72,
+ 0xF1, 0x47, 0xFD, 0x42, 0x33, 0x9F, 0x0C, 0xCE, 0xB1, 0xCA, 0x99, 0x84, 0xB5, 0xAD, 0x4F, 0x4A,
+ 0x7E, 0x25, 0x6C, 0xC0, 0xBA, 0x65, 0x13, 0x8C, 0xAE, 0xD9, 0xC1, 0xF4, 0xAE, 0x9B, 0x1A, 0x09,
+ 0x34, 0xF0, 0x9B, 0x3C, 0x0F, 0xBE, 0x81, 0xE7, 0x2A, 0x86, 0xB5, 0x93, 0xA1, 0x20, 0x20, 0x0B,
+ 0x2A, 0x85, 0x02, 0xFE, 0xA0, 0xF5, 0x43, 0xF4, 0x12, 0xF1, 0xC8, 0x8D, 0xC1, 0xBF, 0xB8, 0x1B,
+ 0x8E, 0xA4, 0x7F, 0xD7, 0xF0, 0x8D, 0x28, 0xF1, 0x8D, 0xF9, 0x25, 0x77, 0x6C, 0x24, 0x1D, 0x92,
+ 0xB0, 0x1C, 0xC6, 0x63, 0x33, 0x75, 0xE1, 0xD2, 0xD8, 0xB6, 0xA6, 0x5B, 0x2F, 0xDD, 0xF5, 0xF7,
+ 0xB8, 0x3D, 0xF7, 0xAC, 0x8B, 0x9F, 0x13, 0xEF, 0xC4, 0x98, 0x73, 0x27, 0x68, 0xF5, 0xED, 0x62,
+ 0x2A, 0x99, 0x50, 0xC1, 0x4C, 0xAB, 0x56, 0xC8, 0x05, 0x94, 0x1F, 0xED, 0x5A, 0x9C, 0xFC, 0x46,
+ 0x22, 0x4E, 0xB2, 0x5E, 0x6A, 0x47, 0x77, 0x1D, 0x8B, 0x6D, 0x10, 0x12, 0xBB, 0x8A, 0xAE, 0xAD,
+ 0xA7, 0x20, 0x2F, 0x22, 0xE5, 0xB9, 0xB9, 0x71, 0x27, 0xC9, 0x01, 0xD5, 0xC9, 0x88, 0x49, 0xB6,
+ 0x07, 0xB2, 0xD6, 0x8B, 0xA1, 0xBA, 0xA9, 0x5B, 0xC4, 0xBA, 0xC3, 0x1B, 0x5B, 0xBA, 0x03, 0x99,
+ 0x41, 0x11, 0xEF, 0x6C, 0xB4, 0x78, 0x8E, 0x16, 0xF5, 0x60, 0x44, 0x2F, 0x54, 0xB7, 0xD4, 0x74,
+ 0xC3, 0xCD, 0xE4, 0x45, 0x2C, 0x1D, 0xD0, 0x34, 0xD9, 0x4E, 0x97, 0xAF, 0x89, 0x87, 0xA1, 0x1C,
+ 0x93, 0x04, 0x56, 0xEB, 0xCD, 0xB3, 0xAC, 0xE2, 0x04, 0x19, 0x8C, 0xFF, 0xAB, 0x7B, 0x37, 0x13,
+ 0x92, 0xA6, 0x07, 0x22, 0x36, 0x83, 0xDA, 0xD7, 0xE7, 0xEA, 0x76, 0x42, 0x63, 0x79, 0xCD, 0xCD,
+ 0x3F, 0x58, 0x88, 0xE4, 0x58, 0xB6, 0x7B, 0xFC, 0x86, 0x4D, 0xE1, 0xFA, 0xD1, 0xFD, 0x71, 0x32,
+ 0xD1, 0xA8, 0xAA, 0x71, 0x21, 0xEB, 0x0F, 0xF2, 0x6A, 0x7C, 0xF4, 0xD5, 0xDB, 0xEA, 0x6F, 0x5D,
+ 0xEE, 0x66, 0x24, 0xBB, 0x50, 0xA7, 0x19, 0x44, 0x95, 0xBA, 0xE2, 0x2D, 0x81, 0xC9, 0xE5, 0x59,
+ 0x7C, 0xA5, 0xD6, 0xA1, 0x6D, 0xE1, 0xC5, 0x9B, 0x32, 0xD9, 0x4A, 0xBC, 0x79, 0x34, 0xA0, 0x54,
+ 0xA6, 0x86, 0x6B, 0x86, 0xA6, 0x30, 0xA2, 0x68, 0xBB, 0x95, 0x11, 0x99, 0x88, 0xA5, 0x83, 0x42,
+ 0x38, 0xC0, 0xD5, 0xE2, 0xDF, 0xD5, 0x35, 0x62, 0x71, 0x38, 0x27, 0x97, 0x87, 0xD7, 0xC7, 0x22,
+ 0x1E, 0x90, 0x63, 0xDC, 0x62, 0x3B, 0x57, 0x6A, 0xE2, 0x89, 0xC1, 0xC2, 0xA3, 0x8A, 0x36, 0x14,
+ 0x80, 0x22, 0x4C, 0xAF, 0x83, 0x7C, 0x27, 0x2C, 0x2D, 0xA7, 0x22, 0xAD, 0x1C, 0x32, 0x38, 0x3E,
+ 0x14, 0x30, 0x5B, 0x57, 0x35, 0x74, 0x6C, 0x8B, 0x04, 0x39, 0x01, 0x11, 0x74, 0x4A, 0x12, 0x38,
+ 0xE1, 0x6D, 0x0C, 0xCB, 0x19, 0x06, 0x0F, 0x49, 0x48, 0x9A, 0x46, 0xBB, 0x37, 0x1F, 0x88, 0x04,
+ 0x0D, 0x41, 0xA5, 0xD1, 0x82, 0x9A, 0x48, 0xCA, 0xAF, 0xEC, 0x8D, 0x4C, 0x16, 0x4E, 0xAE, 0xA4,
+ 0x6C, 0x5E, 0x44, 0xA5, 0x37, 0xA3, 0x13, 0xBA, 0x55, 0x60, 0x68, 0xE3, 0x34, 0x25, 0x85, 0xEB,
+ 0x01, 0x7A, 0x4E, 0xCD, 0x07, 0xFD, 0xD3, 0xB3, 0xD3, 0xF3, 0xFB, 0x16, 0x4E, 0x55, 0x0D, 0x2F,
+ 0x1A, 0xF0, 0x1C, 0xFC, 0x5F, 0x18, 0x93, 0x16, 0xDD, 0x7C, 0x8C, 0x74, 0x40, 0x57, 0x78, 0x94,
+ 0x52, 0x92, 0xD1, 0xB6, 0xEF, 0xFD, 0xA5, 0xDC, 0x37, 0xE9, 0x21, 0x26, 0xED, 0x75, 0xCF, 0x6F,
+ 0xF4, 0x36, 0x96, 0xAE, 0x2C, 0xF1, 0x41, 0x6D, 0x66, 0x98, 0xD3, 0x8C, 0x39, 0xCA, 0x2B, 0xA0,
+ 0x49, 0x5E, 0xAA, 0x85, 0x85, 0x5F, 0xB7, 0x6A, 0xEF, 0xD8, 0x35, 0xBF, 0xCB, 0xCE, 0x6C, 0x09,
+ 0x71, 0xA2, 0x14, 0x9E, 0x31, 0x34, 0xB5, 0xC3, 0x0A, 0xD5, 0xBD, 0x75, 0x86, 0xE6, 0x48, 0x8B,
+ 0xC8, 0xED, 0x6B, 0x8F, 0x2C, 0x86, 0xE0, 0xED, 0xF5, 0xF2, 0xC8, 0x35, 0x6F, 0xBE, 0x71, 0x3A,
+ 0x5A, 0x9A, 0xEC, 0xEA, 0x54, 0x3A, 0x17, 0x76, 0x4E, 0xA4, 0x78, 0x66, 0x6B, 0xB2, 0xBD, 0x3C,
+ 0x8E, 0x28, 0x3D, 0x3C, 0xDA, 0xCA, 0x78, 0xF3, 0x9E, 0x43, 0x32, 0xD6, 0xFB, 0xA3, 0xD8, 0x3E,
+ 0x8F, 0xE3, 0xF9, 0x2C, 0xA7, 0xE2, 0x6A, 0xA9, 0x5C, 0x76, 0x82, 0xF4, 0x5F, 0x34, 0x7D, 0x8E,
+ 0x80, 0x90, 0xE9, 0xA0, 0x70, 0xA8, 0x96, 0xFB, 0x7E, 0xF0, 0xC7, 0x6B, 0xDE, 0xFB, 0xDE, 0xDA,
+ 0x76, 0x01, 0x03, 0x68, 0x07, 0x2F, 0x45, 0xD9, 0x86, 0x5C, 0xD4, 0x4C, 0xAF, 0x8D, 0x86, 0x19,
+ 0x46, 0x62, 0x86, 0x29, 0x25, 0xB6, 0x57, 0x7F, 0x7D, 0x49, 0xAF, 0x79, 0x5E, 0x3C, 0x2B, 0x0A,
+ 0x87, 0xA6, 0x03, 0xE6, 0xD4, 0xD9, 0xBB, 0xBA, 0xB2, 0xD1, 0xAA, 0x71, 0xC9, 0x86, 0xE6, 0x64,
+ 0x78, 0x84, 0xF5, 0x72, 0x82, 0x80, 0xB4, 0xED, 0xC8, 0x16, 0xCD, 0x7F, 0x90, 0x16, 0x7C, 0x0A,
+ 0x82, 0x99, 0xD3, 0xF9, 0x28, 0x36, 0x93, 0xE0, 0x2D, 0xF5, 0x74, 0x67, 0x79, 0xC1, 0x32, 0x84,
+ 0x78, 0x37, 0xA7, 0x15, 0xAF, 0x8B, 0x26, 0x46, 0x4B, 0x91, 0x77, 0x57, 0x3E, 0x47, 0x5B, 0xEA,
+ 0x0F, 0xEA, 0x7F, 0xBA, 0x69, 0x87, 0x44, 0x39, 0xA7, 0xA9, 0x8C, 0x1C, 0x89, 0x84, 0xF6, 0xE6,
+ 0xE5, 0x8C, 0x87, 0x59, 0xD3, 0xF0, 0xA7, 0x98, 0xC2, 0xBC, 0x98, 0x53, 0xC0, 0x64, 0xD4, 0x3F,
+ 0x2A, 0xC8, 0xDF, 0x29, 0x70, 0x84, 0x86, 0x62, 0x58, 0x0C, 0xE7, 0x73, 0x04, 0x89, 0xF2, 0x20,
+ 0x15, 0xFB, 0x2E, 0xFC, 0xC1, 0x59, 0x18, 0xB8, 0x52, 0x46, 0x78, 0x09, 0xF9, 0xC3, 0x28, 0xC5,
+ 0xA5, 0x04, 0xB4, 0x42, 0xC0, 0x75, 0x44, 0x18, 0x3D, 0xCF, 0x8D, 0x36, 0x4C, 0xA2, 0x73, 0x18,
+ 0x7A, 0xAD, 0xBA, 0xDC, 0xB3, 0x2E, 0x30, 0xE6, 0x73, 0x11, 0x20, 0xFC, 0xE9, 0x1B, 0xB0, 0xF2,
+ 0x2D, 0xED, 0xA0, 0x17, 0x55, 0x63, 0xB2, 0x46, 0xCB, 0xAC, 0xB4, 0x3B, 0x1F, 0xCA, 0x9A, 0xF5,
+ 0x50, 0xBF, 0xB7, 0xD2, 0x0A, 0xFA, 0xE7, 0x72, 0x6E, 0x14, 0xEA, 0xA3, 0x05, 0x78, 0xF7, 0x75,
+ 0xD7, 0x06, 0xF8, 0x2D, 0xE6, 0x4C, 0x71, 0x27, 0xD2, 0x84, 0xF9, 0xA0, 0x5C, 0xDA, 0x1F, 0x69,
+ 0x1E, 0xEC, 0xF6, 0x7C, 0xB2, 0xC8, 0xEF, 0x7C, 0xF7, 0x37, 0xDB, 0xD3, 0x03, 0x6F, 0xFB, 0xCF,
+ 0xE0, 0x87, 0xE1, 0x33, 0x60, 0xC4, 0xF5, 0x17, 0xC6, 0x08, 0x50, 0x88, 0x86, 0x58, 0xA2, 0x90,
+ 0xCB, 0xE7, 0x15, 0x47, 0x15, 0x15, 0xE4, 0x33, 0x9D, 0x4A, 0xF2, 0xA7, 0xE6, 0xA7, 0x2E, 0x73,
+ 0x94, 0x75, 0x37, 0xC2, 0x8F, 0xB5, 0xBC, 0xA6, 0xB5, 0x05, 0xAB, 0xC7, 0x3C, 0xCC, 0x36, 0xEA,
+ 0x89, 0x3C, 0xAF, 0xC8, 0xC3, 0xB1, 0x14, 0x02, 0x43, 0x90, 0xE6, 0xE5, 0x4A, 0x2D, 0x8A, 0xAC,
+ 0x41, 0xF5, 0xAF, 0x0D, 0x3F, 0x6C, 0x3B, 0x4B, 0xA6, 0xC3, 0x70, 0x7E, 0x8B, 0x82, 0xC1, 0x40,
+ 0x26, 0x0B, 0x65, 0xC3, 0x60, 0x54, 0x5E, 0xF8, 0x53, 0x96, 0x15, 0x74, 0x1A, 0x11, 0xF0, 0x66,
+ 0x36, 0xAE, 0x3A, 0x11, 0x01, 0xDE, 0x67, 0x18, 0x6D, 0x35, 0x6A, 0x93, 0x13, 0x99, 0x74, 0xBC,
+ 0x17, 0x4E, 0x68, 0x86, 0x3F, 0x10, 0x9A, 0x08, 0xEB, 0xD3, 0x8F, 0xB7, 0x19, 0xC6, 0x39, 0x49,
+ 0xC9, 0x8A, 0x2F, 0x42, 0xE6, 0x0F, 0x31, 0xFE, 0x69, 0x13, 0xAF, 0x51, 0x62, 0xF5, 0x79, 0xF8,
+ 0x0E, 0xBC, 0x46, 0xB3, 0x6E, 0x56, 0xF6, 0xE4, 0x68, 0x25, 0x1F, 0x31, 0x08, 0x55, 0xAA, 0x7F,
+ 0x62, 0xAE, 0xF2, 0xB3, 0x14, 0xFF, 0xCC, 0xCC, 0xCC, 0x77, 0x53, 0x8B, 0x1B, 0x6D, 0xBA, 0x8D,
+ 0x41, 0xB4, 0xB9, 0x3C, 0x1E, 0x83, 0xAB, 0x77, 0x31, 0xEE, 0xC4, 0x9D, 0xCE, 0x24, 0x3C, 0x43,
+ 0xC2, 0x05, 0x9D, 0x5E, 0xAB, 0x17, 0x03, 0xF5, 0x82, 0x28, 0xC8, 0xB3, 0x2E, 0xBC, 0x10, 0x0A,
+ 0x91, 0x19, 0x98, 0x20, 0xF0, 0x76, 0x4D, 0x31, 0x7C, 0xBA, 0xC4, 0x5B, 0x7C, 0x39, 0xC9, 0x0A,
+ 0x04, 0xEB, 0x63, 0x28, 0x39, 0xEA, 0x37, 0x96, 0xC5, 0xCE, 0xE8, 0xB5, 0xA5, 0x7A, 0x40, 0x48,
+ 0x64, 0xC3, 0x99, 0x3A, 0xE1, 0x98, 0x8D, 0x52, 0x49, 0x6D, 0xB6, 0x6B, 0x9C, 0x3A, 0x5B, 0x8E,
+ 0x3C, 0xD8, 0x4B, 0xC2, 0x6F, 0xC9, 0xA6, 0x20, 0xDA, 0xBA, 0xC4, 0xD2, 0x52, 0x38, 0x0E, 0x69,
+ 0x9D, 0x63, 0x59, 0xC9, 0xE4, 0x31, 0x0B, 0xA6, 0x32, 0x91, 0x85, 0xED, 0x41, 0x99, 0x32, 0x4B,
+ 0xAA, 0x99, 0x8A, 0x22, 0xC5, 0x29, 0x06, 0x82, 0x5C, 0x3A, 0xC1, 0x73, 0x88, 0x8A, 0x94, 0x63,
+ 0x0D, 0xFF, 0x75, 0x83, 0x75, 0xFC, 0x39, 0x7C, 0x19, 0x3A, 0x09, 0xAA, 0xE0, 0x9A, 0x7E, 0xA9,
+ 0x54, 0x4C, 0xE8, 0x20, 0x93, 0x00, 0x19, 0x3A, 0xA2, 0x3D, 0x57, 0x53, 0x9E, 0xAD, 0x45, 0x23,
+ 0x24, 0x41, 0xDC, 0x5F, 0x4E, 0x97, 0x7E, 0x19, 0x8E, 0xF0, 0x1C, 0x45, 0x92, 0x34, 0x8A, 0x14,
+ 0x9F, 0xF7, 0xB3, 0x30, 0xB1, 0xC1, 0xB8, 0x72, 0xC3, 0xDD, 0x9E, 0x01, 0xE2, 0x74, 0x37, 0x26,
+ 0x21, 0x5D, 0x8A, 0x0A, 0x24, 0x06, 0x7E, 0x50, 0x68, 0x31, 0x21, 0x38, 0x48, 0x81, 0x6C, 0x3B,
+ 0xAE, 0x63, 0x78, 0xE0, 0x31, 0x6C, 0xBB, 0x25, 0xF6, 0x58, 0x81, 0xF7, 0x48, 0x06, 0xC3, 0x6D,
+ 0xF8, 0xB2, 0x78, 0x2B, 0x90, 0x77, 0x31, 0x24, 0xA9, 0xF9, 0x49, 0x86, 0xC7, 0xD0, 0xC5, 0xA4,
+ 0xC3, 0x49, 0x23, 0x1E, 0x26, 0xC9, 0xA8, 0x38, 0x2A, 0xB7, 0x15, 0x39, 0x4A, 0xE4, 0x72, 0xE1,
+ 0x30, 0xD3, 0xD3, 0x4A, 0xF0, 0xC7, 0xE1, 0x56, 0x0C, 0xCA, 0xF3, 0xB5, 0x1C, 0x47, 0x67, 0x22,
+ 0x7E, 0x8C, 0xB5, 0x0C, 0x55, 0x20, 0x55, 0xE3, 0xBA, 0xAB, 0xEF, 0xDE, 0x89, 0x16, 0x4D, 0x93,
+ 0x68, 0x07, 0x03, 0x86, 0xF1, 0x04, 0x89, 0xC7, 0xF9, 0x02, 0xC6, 0xCA, 0x15, 0xF8, 0x2B, 0x8C,
+ 0x95, 0x37, 0xF7, 0x63, 0xA9, 0x48, 0x3C, 0x1C, 0xA0, 0x4A, 0x7F, 0x33, 0x09, 0xBA, 0x13, 0x14,
+ 0x9A, 0x20, 0x51, 0xBB, 0x8E, 0x3A, 0x13, 0xED, 0x17, 0x56, 0x74, 0x72, 0xEE, 0x2C, 0x8C, 0xAC,
+ 0x27, 0xA9, 0xAF, 0x17, 0x89, 0x79, 0x19, 0xDA, 0xE0, 0x12, 0x5E, 0x71, 0xE1, 0x72, 0x64, 0x98,
+ 0xA5, 0xE1, 0x15, 0x60, 0x2A, 0xD4, 0x10, 0x8C, 0xAD, 0xD3, 0xAA, 0xE3, 0x4A, 0x15, 0x84, 0x75,
+ 0xB6, 0x2B, 0x3D, 0x31, 0x4A, 0x99, 0x22, 0xDA, 0x6E, 0xBC, 0xDF, 0x41, 0x95, 0x4C, 0x21, 0x1E,
+ 0xA5, 0x4D, 0x24, 0x68, 0xE9, 0x6C, 0x84, 0x20, 0x52, 0x11, 0x41, 0x09, 0x6B, 0x34, 0x1D, 0xA6,
+ 0x35, 0xBF, 0xC4, 0x32, 0xD4, 0x9B, 0x31, 0xF8, 0xD5, 0x04, 0x56, 0x84, 0x04, 0x45, 0xDB, 0xA2,
+ 0xE4, 0x4F, 0xCF, 0x53, 0xDA, 0xB4, 0x3F, 0x49, 0x10, 0xE4, 0x0E, 0x27, 0x68, 0xFB, 0xA1, 0xE2,
+ 0x47, 0x96, 0xBF, 0x5A, 0x4A, 0x92, 0x28, 0xEA, 0x67, 0x93, 0x7A, 0xF4, 0xC7, 0x8D, 0x30, 0x84,
+ 0xE6, 0x01, 0x0A, 0x2A, 0xFC, 0xED, 0xA3, 0xFB, 0x28, 0x85, 0xD5, 0x18, 0x35, 0x75, 0x64, 0x33,
+ 0x25, 0xD5, 0x54, 0x23, 0xBA, 0x58, 0x39, 0x7B, 0xE3, 0x30, 0x8A, 0x83, 0xC6, 0xE1, 0x0F, 0x41,
+ 0x01, 0x64, 0xFA, 0x72, 0x3A, 0xAE, 0x10, 0x72, 0xF8, 0x17, 0xC5, 0xBC, 0xFE, 0x4B, 0x9A, 0xD9,
+ 0xB7, 0x56, 0x61, 0x70, 0xC9, 0x3B, 0x51, 0x54, 0x86, 0x54, 0xAC, 0xA0, 0x0A, 0x95, 0x1C, 0x86,
+ 0x39, 0x5C, 0x5F, 0x35, 0xE9, 0x05, 0x3F, 0xAB, 0xC8, 0x4B, 0xC4, 0x0B, 0x68, 0xF2, 0x43, 0x1F,
+ 0x8A, 0x18, 0xF1, 0xC9, 0x6A, 0x26, 0x65, 0x0B, 0xAF, 0x64, 0x59, 0xA3, 0x9C, 0xB1, 0x9C, 0x30,
+ 0x03, 0x25, 0x48, 0x37, 0xC2, 0x7A, 0x2B, 0x69, 0x26, 0x0B, 0x95, 0x40, 0x08, 0x7E, 0x1E, 0x8F,
+ 0x26, 0xFA, 0x27, 0xCF, 0xA2, 0xFA, 0xF6, 0x07, 0xF7, 0xD7, 0x6C, 0x6B, 0x96, 0x6C, 0xCC, 0x66,
+ 0x4B, 0x93, 0x26, 0x91, 0xA1, 0x94, 0xA5, 0x91, 0x0C, 0x36, 0xEC, 0x05, 0xD2, 0x1F, 0x4F, 0x0D,
+ 0x22, 0x36, 0xC4, 0xBB, 0xE0, 0x73, 0x60, 0x0B, 0xA8, 0xF4, 0x8D, 0x4D, 0x33, 0xBD, 0xD4, 0xDC,
+ 0x68, 0x32, 0x3E, 0x91, 0xDB, 0x9A, 0xFD, 0x55, 0x30, 0x40, 0x11, 0xAE, 0xD2, 0x1A, 0x38, 0xC2,
+ 0x2E, 0xF3, 0xB1, 0x6F, 0x4D, 0x0D, 0xCB, 0xDA, 0x14, 0x88, 0x61, 0x88, 0x5B, 0xE3, 0x49, 0x7B,
+ 0xCB, 0x81, 0x01, 0x0A, 0xB9, 0x21, 0x46, 0xE3, 0x5A, 0x76, 0x9E, 0xD4, 0x70, 0xC7, 0xE1, 0x78,
+ 0xA0, 0x11, 0xE3, 0xE6, 0xB6, 0x67, 0x0B, 0xD2, 0xC4, 0x1B, 0xF2, 0xCB, 0xC9, 0x43, 0x82, 0x5F,
+ 0x26, 0x0B, 0x82, 0x22, 0x84, 0x32, 0x7E, 0x7F, 0x5E, 0x4E, 0x71, 0xBA, 0xA2, 0x89, 0x58, 0x3D,
+ 0x0B, 0xB1, 0x46, 0xB5, 0x64, 0xF2, 0x1C, 0xF7, 0x88, 0xC4, 0xF9, 0x67, 0xC6, 0x09, 0xD5, 0x66,
+ 0x03, 0xD5, 0x34, 0x1D, 0x75, 0x92, 0xD3, 0x5A, 0x22, 0xA9, 0x45, 0xCC, 0x30, 0xB7, 0x57, 0xAE,
+ 0x75, 0x8C, 0x48, 0xA1, 0xC4, 0xC1, 0x07, 0xBA, 0x0F, 0x34, 0xDE, 0xA0, 0xC2, 0x08, 0x7B, 0xD8,
+ 0x1F, 0xB1, 0xCB, 0x8D, 0x86, 0xD2, 0xD3, 0x13, 0xB1, 0x74, 0x58, 0xD4, 0x50, 0xFE, 0xBA, 0xFB,
+ 0x0F, 0x56, 0x48, 0xDF, 0x3E, 0xF6, 0xD4, 0x6B, 0xE9, 0x28, 0x86, 0x27, 0x4B, 0x13, 0x6C, 0xEE,
+ 0x64, 0xA4, 0xBB, 0xA9, 0x52, 0x8A, 0x94, 0xE3, 0x72, 0x2D, 0x48, 0xB6, 0x4A, 0xB4, 0xA0, 0xEB,
+ 0x16, 0x6D, 0x78, 0x79, 0x45, 0xCF, 0xA1, 0x04, 0xFC, 0x25, 0x98, 0x03, 0x3B, 0xFB, 0x49, 0x3C,
+ 0xBF, 0x31, 0xBF, 0xA9, 0x81, 0x52, 0x3E, 0x9D, 0xA2, 0x1B, 0x63, 0x75, 0xF1, 0x67, 0xF3, 0x73,
+ 0x7F, 0x34, 0x3F, 0x73, 0xD7, 0x7C, 0x1D, 0x8D, 0x35, 0xFE, 0xD5, 0x2A, 0xC1, 0xCC, 0x8B, 0x43,
+ 0xB8, 0x2F, 0x32, 0xEF, 0x06, 0x21, 0xFE, 0xCC, 0x30, 0xC9, 0x96, 0x66, 0x68, 0x37, 0x10, 0x43,
+ 0x58, 0x66, 0x14, 0x6B, 0x47, 0x57, 0x4B, 0x78, 0x81, 0xAB, 0xAC, 0x5B, 0xFD, 0x3C, 0x74, 0xA1,
+ 0x79, 0x6E, 0x90, 0x6C, 0xDB, 0x2B, 0x13, 0x54, 0x88, 0x41, 0x04, 0x07, 0x25, 0xFC, 0xA1, 0xB4,
+ 0x8F, 0xD6, 0x35, 0xDD, 0x6C, 0x18, 0x05, 0xDE, 0xF6, 0xCB, 0x2D, 0xF3, 0x78, 0x3A, 0x53, 0xD4,
+ 0x7D, 0x99, 0xB8, 0xBC, 0xA9, 0x56, 0x8F, 0x65, 0x82, 0xF2, 0xEB, 0x04, 0x4E, 0x0F, 0x1E, 0x93,
+ 0x5A, 0x4D, 0xF1, 0x06, 0x8D, 0xD3, 0x5B, 0x6A, 0x72, 0xD3, 0xD8, 0xD6, 0x48, 0x30, 0xC4, 0x08,
+ 0xCA, 0x0E, 0x49, 0x0F, 0x1A, 0x14, 0x42, 0x62, 0x68, 0xD1, 0xDF, 0xA5, 0x55, 0xCB, 0x62, 0xB1,
+ 0x5C, 0xDD, 0xCD, 0xB1, 0x61, 0x55, 0xD1, 0x8C, 0x88, 0x8F, 0x1F, 0x9F, 0x5F, 0x0C, 0xD8, 0x3B,
+ 0xE8, 0xEC, 0x9B, 0x98, 0x82, 0x6D, 0x87, 0xC9, 0x25, 0x86, 0x64, 0xC2, 0xB6, 0x24, 0x85, 0x27,
+ 0x2A, 0x27, 0x2A, 0x3E, 0x92, 0x1A, 0xC8, 0xAA, 0x30, 0x0A, 0xE3, 0x79, 0x19, 0x71, 0xFD, 0x03,
+ 0xB5, 0x00, 0xAC, 0xF9, 0x9D, 0xDC, 0xAF, 0xEB, 0xE2, 0xBF, 0xD5, 0xC1, 0x08, 0x85, 0x46, 0x10,
+ 0x1A, 0x89, 0xFF, 0x2B, 0xFC, 0xCD, 0x15, 0x8A, 0xF3, 0x82, 0x7A, 0xE1, 0x92, 0xC2, 0x5C, 0xD7,
+ 0x35, 0x9E, 0xCE, 0x74, 0x53, 0x64, 0x31, 0xB1, 0x20, 0x8F, 0x71, 0x89, 0x61, 0x81, 0x30, 0x31,
+ 0x08, 0x53, 0x22, 0xB7, 0xBC, 0xD0, 0xDD, 0x0B, 0x08, 0x85, 0x5F, 0x83, 0xB8, 0xC4, 0x5E, 0x5E,
+ 0x74, 0x02, 0x46, 0xCC, 0x92, 0x39, 0x41, 0xA6, 0x14, 0x41, 0x35, 0xB1, 0xB8, 0x35, 0x4D, 0xB3,
+ 0xE1, 0x04, 0x78, 0xD9, 0x20, 0x4A, 0xBC, 0x68, 0x8B, 0x69, 0x27, 0x37, 0x16, 0x0D, 0x24, 0xD1,
+ 0xC7, 0x6B, 0xF7, 0xCC, 0xFE, 0x42, 0xAA, 0xDE, 0x24, 0xCA, 0x8E, 0x55, 0xE9, 0xE6, 0x6A, 0x8B,
+ 0xDA, 0x78, 0x22, 0xA0, 0x5A, 0xAA, 0x24, 0x27, 0x64, 0x41, 0xE6, 0xBF, 0x9F, 0xCE, 0x44, 0x7C,
+ 0x8D, 0xEB, 0xBA, 0x2D, 0x78, 0x71, 0xD7, 0xC0, 0x8B, 0xF5, 0x1F, 0xF0, 0xD3, 0x88, 0x00, 0x0D,
+ 0x3C, 0xA6, 0x70, 0xD1, 0x52, 0x44, 0x10, 0x57, 0x12, 0x11, 0x05, 0xFC, 0x62, 0xB4, 0xE2, 0x1F,
+ 0xAD, 0x3D, 0x3E, 0xAA, 0xA9, 0xFF, 0xEE, 0x8F, 0x04, 0x2E, 0x90, 0xEE, 0xBE, 0x3C, 0x98, 0xFC,
+ 0x3D, 0x83, 0x70, 0x43, 0x06, 0xC7, 0x86, 0xB4, 0x60, 0x6B, 0xB8, 0xBA, 0x05, 0xC8, 0x78, 0x5A,
+ 0xA6, 0xE7, 0xAE, 0x2D, 0x1A, 0x56, 0xC8, 0x34, 0xB0, 0x80, 0x55, 0x48, 0x19, 0x0D, 0x0A, 0x55,
+ 0xDD, 0x8A, 0xC2, 0x4F, 0x87, 0xA7, 0xB3, 0xA9, 0xE0, 0x88, 0x1D, 0x8B, 0x8B, 0xE3, 0xC5, 0x0F,
+ 0xFE, 0xA1, 0xA1, 0xDA, 0x9A, 0x14, 0x8C, 0xF4, 0x90, 0x60, 0x25, 0xF6, 0xCB, 0xBB, 0x29, 0x81,
+ 0x21, 0x0F, 0x85, 0x63, 0xFE, 0x60, 0x4C, 0xCD, 0x53, 0x66, 0xB1, 0xF6, 0x9A, 0xC4, 0x7C, 0x86,
+ 0x80, 0x14, 0x61, 0xC5, 0xA3, 0xCD, 0x6F, 0x30, 0x90, 0x10, 0x34, 0x49, 0x95, 0x82, 0xBE, 0x0A,
+ 0xE7, 0x13, 0x48, 0xC8, 0x98, 0x0A, 0x47, 0x9D, 0x4A, 0xE6, 0xE2, 0x45, 0xC5, 0x10, 0x08, 0x2C,
+ 0xBA, 0xF1, 0xB8, 0xE8, 0xD5, 0x57, 0xC1, 0x26, 0x7A, 0x1A, 0xDB, 0x8A, 0xCB, 0x7D, 0xAC, 0x55,
+ 0x34, 0x10, 0xC0, 0x7A, 0x69, 0x99, 0x5B, 0x35, 0xF4, 0x11, 0x35, 0x11, 0x1F, 0x01, 0x91, 0xB0,
+ 0x43, 0xD8, 0x48, 0x12, 0x29, 0x82, 0x0C, 0x20, 0x8E, 0xD5, 0x54, 0x3C, 0xC0, 0x8B, 0x90, 0xBD,
+ 0x7C, 0x37, 0xA8, 0x81, 0x7B, 0x6B, 0x58, 0xFE, 0xD4, 0xF3, 0x86, 0xBC, 0xC6, 0xF8, 0x49, 0x16,
+ 0x2F, 0x61, 0x32, 0x91, 0x99, 0x84, 0xA3, 0x99, 0x64, 0xB3, 0xDE, 0xC2, 0xAF, 0xAD, 0x0E, 0xB4,
+ 0xB0, 0x2E, 0x71, 0x73, 0x23, 0x19, 0x37, 0x49, 0xCE, 0xB4, 0x3B, 0x10, 0x36, 0xAF, 0xCB, 0x6F,
+ 0x9B, 0x88, 0xC5, 0x26, 0xB6, 0x16, 0xAE, 0x6B, 0x46, 0xEC, 0x6E, 0xAA, 0x51, 0x0E, 0xF9, 0x9A,
+ 0x67, 0x96, 0xA6, 0xDE, 0x5C, 0x1C, 0xDD, 0xDE, 0x28, 0xBD, 0x79, 0x6A, 0x69, 0xA9, 0x9C, 0x78,
+ 0xFD, 0x44, 0x8B, 0xD7, 0x27, 0xCB, 0xAF, 0x2F, 0x25, 0xBD, 0x68, 0xC3, 0xEA, 0xB3, 0xE0, 0x9D,
+ 0xE8, 0x3B, 0x80, 0x00, 0xD1, 0xBE, 0x80, 0xAD, 0x63, 0x02, 0x10, 0xE8, 0x76, 0x88, 0x99, 0x71,
+ 0x7D, 0x8F, 0x2A, 0x30, 0x0C, 0x01, 0x38, 0x49, 0xAD, 0xF1, 0xCE, 0x43, 0x87, 0x74, 0xF4, 0x0C,
+ 0xB8, 0x72, 0xC4, 0x01, 0x3F, 0x1E, 0x53, 0x02, 0x80, 0xE5, 0xC4, 0xAA, 0xDF, 0x37, 0x12, 0xE0,
+ 0xB9, 0x11, 0xC6, 0xD5, 0xFF, 0x12, 0xF2, 0x34, 0xAA, 0x86, 0x12, 0x20, 0x18, 0x60, 0x58, 0xE6,
+ 0xFF, 0xE9, 0x88, 0xE1, 0x60, 0x78, 0x45, 0xD8, 0x6C, 0x0C, 0x86, 0xDC, 0xAE, 0x40, 0x77, 0x06,
+ 0xBC, 0x06, 0xDB, 0x1D, 0x72, 0x03, 0x36, 0xAF, 0x18, 0xAC, 0x37, 0x01, 0x90, 0x7A, 0xF1, 0x60,
+ 0x97, 0xBC, 0x59, 0xC0, 0x3D, 0x9F, 0x07, 0xEF, 0x45, 0x06, 0xDC, 0x0B, 0x1C, 0xD0, 0xEC, 0xAB,
+ 0xA2, 0x49, 0x00, 0x93, 0x60, 0x2D, 0x65, 0xD5, 0xB4, 0x9C, 0xD5, 0x95, 0xB3, 0xE0, 0xCC, 0x39,
+ 0x20, 0x40, 0x30, 0x2C, 0x66, 0x25, 0xA0, 0x17, 0xF4, 0x19, 0x46, 0x7C, 0x71, 0xAF, 0x7C, 0xA2,
+ 0xA7, 0xDC, 0x5D, 0x60, 0xE3, 0x55, 0xB5, 0x0F, 0x92, 0x60, 0x3C, 0xBF, 0x99, 0x61, 0x4E, 0x54,
+ 0xC3, 0xD5, 0xAB, 0xC3, 0x5A, 0x20, 0x67, 0xA7, 0x36, 0x9C, 0x43, 0x98, 0xAD, 0x2A, 0x45, 0x3E,
+ 0x93, 0xDC, 0x33, 0x7C, 0x05, 0xAB, 0xAB, 0x83, 0x5A, 0x22, 0xE2, 0x34, 0xC8, 0x00, 0x77, 0xF7,
+ 0x41, 0x06, 0x3C, 0x01, 0xBE, 0x09, 0xBE, 0xB8, 0xDE, 0xFE, 0xF0, 0x7A, 0xFB, 0xF7, 0xC0, 0x3F,
+ 0x81, 0x27, 0x3D, 0x0B, 0x67, 0x93, 0x57, 0x89, 0x32, 0x0D, 0xEE, 0xEF, 0x8F, 0x4C, 0x8C, 0x97,
+ 0xA3, 0x7E, 0xD0, 0x92, 0x69, 0x91, 0xE6, 0x7C, 0x0A, 0x27, 0xFB, 0x74, 0x8A, 0xA4, 0x95, 0x57,
+ 0xCC, 0x4C, 0x97, 0x67, 0x7A, 0xF7, 0xCF, 0x34, 0xEE, 0xCD, 0x8B, 0xA2, 0x8C, 0x38, 0x1F, 0xE2,
+ 0x2C, 0xC4, 0xE5, 0x1F, 0x58, 0xCB, 0xE6, 0x3A, 0x07, 0x44, 0xC8, 0xF4, 0x86, 0x32, 0x02, 0x8B,
+ 0xCE, 0x73, 0x40, 0x87, 0xC4, 0x05, 0xD5, 0xDD, 0xE4, 0x82, 0xBC, 0xB0, 0x96, 0x0B, 0x33, 0xA8,
+ 0xCC, 0xBA, 0x54, 0xBB, 0xB0, 0xD1, 0x62, 0x9A, 0x89, 0x0E, 0x21, 0x40, 0x92, 0xF1, 0xB2, 0x72,
+ 0x93, 0x89, 0xEC, 0x20, 0xDD, 0xDE, 0x75, 0x21, 0xB7, 0x2B, 0xE4, 0x4B, 0xD7, 0xBA, 0xD0, 0x8C,
+ 0xB3, 0xB1, 0xD4, 0x25, 0xA8, 0x86, 0x73, 0xF5, 0x6C, 0xB3, 0xE4, 0x57, 0x63, 0xA6, 0xA4, 0x67,
+ 0xEC, 0xC8, 0xB6, 0x47, 0x7E, 0x67, 0xD9, 0x0B, 0x8B, 0xA4, 0xF6, 0x7A, 0xDD, 0x4B, 0x09, 0x9B,
+ 0x15, 0x85, 0xAC, 0x29, 0x49, 0x3A, 0x66, 0xC6, 0x5D, 0xCC, 0xFE, 0x23, 0xBF, 0xAB, 0x06, 0x66,
+ 0x71, 0x93, 0x9A, 0x7B, 0xD7, 0xA2, 0xE0, 0xCE, 0xB2, 0x57, 0x13, 0x42, 0xDC, 0x8F, 0x67, 0x33,
+ 0xE7, 0xCD, 0xE6, 0x6B, 0xC1, 0xDB, 0xBC, 0x59, 0xF6, 0xF2, 0x20, 0x89, 0x7B, 0x70, 0xFB, 0x88,
+ 0xD7, 0xFE, 0x6D, 0x70, 0xC1, 0x6D, 0x1F, 0x64, 0xFC, 0x79, 0xAB, 0x52, 0xDF, 0xB8, 0x2A, 0x83,
+ 0x4C, 0x16, 0xAF, 0x7D, 0x6C, 0xD8, 0xFE, 0x2B, 0xAF, 0xDD, 0x8B, 0x67, 0xA1, 0x4F, 0xE2, 0xF6,
+ 0x39, 0x6F, 0xF7, 0xC2, 0xD7, 0xDE, 0x01, 0x5E, 0xD4, 0xCA, 0x60, 0x64, 0xFD, 0x66, 0x70, 0xA9,
+ 0xFD, 0xC9, 0xF5, 0xF6, 0x6F, 0x81, 0xD7, 0x6C, 0x68, 0xFF, 0xC0, 0x7A, 0xFB, 0x13, 0x97, 0x7D,
+ 0xFF, 0xA3, 0xEB, 0xED, 0xDF, 0x06, 0xAF, 0xDA, 0xD0, 0xFE, 0xCF, 0xEB, 0xED, 0xDF, 0x73, 0xDB,
+ 0xBD, 0xC8, 0xCC, 0x26, 0x2F, 0x32, 0x33, 0x03, 0xCE, 0xF4, 0xCB, 0xF9, 0x08, 0x18, 0x15, 0x49,
+ 0x9E, 0x64, 0x6C, 0x87, 0xB1, 0x25, 0x46, 0x74, 0xA4, 0x2F, 0xD9, 0xEA, 0xCC, 0x9F, 0xCF, 0xF6,
+ 0xCD, 0xD9, 0xDA, 0xF9, 0x14, 0xCF, 0xD7, 0xC6, 0x6D, 0xC4, 0x18, 0x88, 0x49, 0x7D, 0x73, 0x0D,
+ 0x00, 0x9E, 0x03, 0xFC, 0x3A, 0x7D, 0xB8, 0xE9, 0x9C, 0xE7, 0x80, 0x3A, 0xA0, 0x0F, 0xF2, 0x12,
+ 0x7D, 0x9C, 0xEF, 0x5D, 0x51, 0xDB, 0xB2, 0x9E, 0x3F, 0x34, 0x3B, 0x70, 0x0D, 0x28, 0xF0, 0x25,
+ 0x48, 0x23, 0xE1, 0xD1, 0xC6, 0xA5, 0xB8, 0xCF, 0xE8, 0x8B, 0x02, 0x3F, 0xBD, 0x86, 0x7C, 0x39,
+ 0x59, 0x04, 0xD3, 0xB3, 0xD5, 0x17, 0x47, 0x82, 0x94, 0xEA, 0x15, 0x91, 0xA0, 0x1A, 0x9F, 0xDD,
+ 0x48, 0x16, 0x9B, 0x47, 0x66, 0x5F, 0x2A, 0x32, 0x24, 0x47, 0xC7, 0x36, 0x84, 0x86, 0xF0, 0x0C,
+ 0x7A, 0xB1, 0x14, 0x6F, 0x85, 0x96, 0x06, 0xEB, 0x76, 0x2B, 0x78, 0x51, 0xEB, 0xA5, 0x75, 0x73,
+ 0xDB, 0x9F, 0xF2, 0xD6, 0x6D, 0xE9, 0xB2, 0x75, 0x1B, 0x7C, 0xFF, 0x03, 0xEB, 0x57, 0x79, 0xC2,
+ 0x5D, 0x7D, 0xD7, 0x67, 0x0B, 0x25, 0xEF, 0x2A, 0xDF, 0x1C, 0x5C, 0xFB, 0x5F, 0x87, 0xAD, 0xE0,
+ 0x97, 0x1B, 0x5A, 0x67, 0x23, 0x6B, 0xDF, 0xCD, 0x7B, 0x57, 0x1E, 0xB4, 0x7E, 0x8B, 0x72, 0x5B,
+ 0xFF, 0xFF, 0xB8, 0xB5, 0xEA, 0x5D, 0x77, 0xD0, 0xFA, 0xC4, 0x7F, 0xB8, 0xAD, 0x9E, 0xB7, 0xC6,
+ 0xA3, 0x86, 0x5D, 0x43, 0x6A, 0x78, 0x9B, 0xD7, 0x0B, 0xCF, 0xF7, 0xE1, 0x5D, 0x79, 0xF7, 0xE0,
+ 0x7E, 0xFB, 0x5E, 0xDC, 0x7A, 0x69, 0x2C, 0x83, 0xF6, 0x27, 0xD7, 0xDB, 0xBF, 0xE5, 0xEE, 0xF9,
+ 0xB9, 0xDE, 0xFE, 0x81, 0xF5, 0xAB, 0x3C, 0x71, 0xF6, 0x52, 0xEB, 0x47, 0xD7, 0x5B, 0xBF, 0x7D,
+ 0xEB, 0xA0, 0xD7, 0xD8, 0xB6, 0xF0, 0xAE, 0x7D, 0x60, 0xC8, 0x55, 0x6F, 0x00, 0x2F, 0xD5, 0x3E,
+ 0x8B, 0x49, 0xEA, 0x52, 0xFB, 0x93, 0xEB, 0xED, 0xDF, 0xC2, 0x12, 0xED, 0x52, 0xFB, 0x07, 0xD6,
+ 0xDB, 0x9F, 0x00, 0xEF, 0xF2, 0xF6, 0x0C, 0x0D, 0xAE, 0x3E, 0x06, 0x3F, 0x89, 0x76, 0x82, 0x1A,
+ 0xE8, 0x80, 0x07, 0xFB, 0xA5, 0xB0, 0xA5, 0x50, 0xA0, 0x5E, 0xCE, 0x18, 0x76, 0xC6, 0xB6, 0xBA,
+ 0xDD, 0x92, 0xD5, 0x0D, 0x7C, 0xA3, 0x55, 0x37, 0x0D, 0x8E, 0x25, 0x19, 0x54, 0xB0, 0x8D, 0x3A,
+ 0x22, 0x47, 0x50, 0x21, 0x4B, 0xAE, 0xAC, 0xB9, 0x19, 0xCF, 0x01, 0x06, 0xDA, 0x3D, 0x2C, 0xC7,
+ 0x2D, 0x68, 0x79, 0x16, 0xCD, 0x20, 0x76, 0xD4, 0x1D, 0x5A, 0x6E, 0xE7, 0x40, 0x12, 0x6A, 0x2E,
+ 0x31, 0x9F, 0x03, 0x35, 0xA8, 0xBB, 0x2F, 0x1D, 0x68, 0xAC, 0x6F, 0xC7, 0xB5, 0x01, 0x62, 0x74,
+ 0xD7, 0x4A, 0xF9, 0x1C, 0x05, 0x6B, 0x9D, 0x4C, 0x76, 0x98, 0x10, 0x98, 0x6D, 0x33, 0xC3, 0x18,
+ 0x1A, 0x36, 0xE9, 0x18, 0x77, 0x77, 0x09, 0x6C, 0xBB, 0x63, 0xDD, 0x5B, 0xC7, 0xAC, 0x03, 0x33,
+ 0x83, 0xF0, 0xB4, 0x03, 0xDF, 0x1D, 0xAF, 0x96, 0x03, 0x74, 0x4E, 0x99, 0x6A, 0xB2, 0xDA, 0xE8,
+ 0x8E, 0x9D, 0x49, 0xD4, 0x84, 0xA4, 0xA2, 0xCA, 0xA5, 0x4D, 0xC9, 0x04, 0xA9, 0x5B, 0xBC, 0xDF,
+ 0x44, 0x30, 0x55, 0x12, 0x59, 0xD9, 0xFE, 0x18, 0x69, 0x25, 0x02, 0xB4, 0x94, 0x0D, 0x5C, 0x15,
+ 0x48, 0x84, 0xF8, 0xF9, 0x38, 0x97, 0xDE, 0x24, 0xD6, 0x69, 0x73, 0xF3, 0xEC, 0xD4, 0xCB, 0x5A,
+ 0xCE, 0xCE, 0xC4, 0x09, 0x48, 0xA0, 0x91, 0x70, 0x30, 0xE0, 0x43, 0x92, 0xE0, 0x4F, 0xA9, 0xFD,
+ 0xE6, 0x8D, 0x0D, 0x0B, 0x91, 0x1F, 0x79, 0x8C, 0xD0, 0x8B, 0xB6, 0x14, 0xAF, 0x2D, 0x4A, 0x83,
+ 0x48, 0xE7, 0xBF, 0xA1, 0x9B, 0xD1, 0x57, 0xF0, 0x8C, 0x9F, 0xEE, 0x8F, 0x4D, 0x34, 0xE2, 0xAA,
+ 0x22, 0x0B, 0x40, 0xE0, 0x19, 0xBE, 0xEA, 0xEB, 0x4D, 0x4E, 0x29, 0x19, 0x0D, 0xBC, 0x6E, 0x6E,
+ 0x72, 0x66, 0x2B, 0x31, 0x3E, 0xA6, 0x12, 0xCA, 0xA3, 0x0D, 0xCB, 0x34, 0x74, 0x8D, 0xAE, 0x1D,
+ 0x99, 0xAC, 0x55, 0x91, 0x63, 0xFB, 0x42, 0x6F, 0x22, 0x1E, 0x5E, 0xF3, 0x5A, 0xF7, 0xD6, 0x63,
+ 0x26, 0xBD, 0x61, 0xAC, 0xCD, 0x8B, 0x53, 0xBB, 0x09, 0xA3, 0xF0, 0x25, 0xF2, 0xE0, 0xE7, 0x54,
+ 0xCF, 0x3F, 0x8A, 0xF1, 0xA4, 0xBB, 0x6F, 0xDC, 0x14, 0x9E, 0xA6, 0x64, 0xDB, 0xE9, 0xA0, 0x76,
+ 0xB3, 0x3D, 0xBA, 0x71, 0x8B, 0x94, 0x81, 0xFB, 0x14, 0x63, 0x94, 0x7A, 0xAB, 0xC1, 0x78, 0xB8,
+ 0x9B, 0x71, 0x1D, 0x6D, 0x58, 0x3E, 0xF4, 0x20, 0xBA, 0xB9, 0x58, 0xCD, 0xB5, 0xB7, 0x24, 0x59,
+ 0x73, 0x61, 0x24, 0x25, 0x71, 0xEF, 0x3E, 0x25, 0x49, 0x9A, 0x82, 0xE0, 0xD5, 0x6F, 0x6A, 0xAB,
+ 0xD3, 0x75, 0xC9, 0x97, 0x09, 0xA5, 0x68, 0x4E, 0x8D, 0xC4, 0x4A, 0x31, 0x39, 0xFF, 0xFE, 0xE2,
+ 0xCB, 0x2C, 0x3B, 0xE3, 0x0B, 0x61, 0xF3, 0x28, 0xA0, 0x8E, 0x5E, 0x5D, 0xD8, 0x7C, 0xFF, 0x78,
+ 0x31, 0x41, 0xBA, 0x99, 0xA5, 0x68, 0xF4, 0xD8, 0xCD, 0x95, 0x53, 0x30, 0x20, 0x56, 0x42, 0x27,
+ 0xF6, 0x32, 0x71, 0x47, 0xD4, 0x8C, 0x80, 0xA6, 0x69, 0x56, 0x38, 0x39, 0x39, 0xD9, 0xE8, 0xA0,
+ 0x97, 0xE1, 0x2F, 0xD5, 0xAA, 0x9D, 0x18, 0x42, 0x90, 0x74, 0x3D, 0xFB, 0x4F, 0xAF, 0xE6, 0x90,
+ 0x88, 0xFE, 0x1D, 0x4B, 0xF3, 0x05, 0x3C, 0x63, 0xB9, 0x5A, 0x50, 0x07, 0x91, 0x39, 0x2B, 0x03,
+ 0xAD, 0xCC, 0x14, 0xDB, 0x88, 0xB0, 0x8D, 0xC5, 0x56, 0xFC, 0x4E, 0x5F, 0x6B, 0x1A, 0x95, 0x59,
+ 0xD4, 0xC8, 0x20, 0x6B, 0x1C, 0x29, 0xBE, 0x1F, 0xAF, 0x87, 0x29, 0xCE, 0x81, 0x16, 0x0C, 0xAE,
+ 0x15, 0x38, 0x57, 0x3D, 0x45, 0x4A, 0x42, 0xDF, 0x80, 0xA0, 0x20, 0xF4, 0xBB, 0x2F, 0x73, 0x30,
+ 0xB0, 0xA1, 0xA8, 0xFF, 0xB2, 0xF4, 0xD2, 0xC5, 0xA0, 0x9B, 0x27, 0x86, 0x05, 0x20, 0x3D, 0xF4,
+ 0x36, 0x7A, 0xFE, 0x13, 0xC6, 0x7B, 0x76, 0x8B, 0x43, 0xD6, 0xEA, 0xFF, 0xD6, 0x13, 0x90, 0x9B,
+ 0x1E, 0xEA, 0x19, 0x94, 0x3C, 0x13, 0x5E, 0xC5, 0x88, 0x4B, 0x62, 0xA4, 0x33, 0x05, 0x91, 0xF8,
+ 0x40, 0x6B, 0x2C, 0x19, 0x8D, 0x58, 0xD3, 0x73, 0x37, 0x34, 0xC7, 0x5B, 0x71, 0x8A, 0x3E, 0x77,
+ 0xCB, 0xF6, 0x9B, 0x24, 0xED, 0x0C, 0x52, 0xAE, 0x4B, 0xD6, 0xE2, 0xBA, 0x91, 0x6B, 0x3F, 0x3A,
+ 0xDF, 0x9E, 0x29, 0xC9, 0xD2, 0x67, 0x6F, 0x9D, 0xEA, 0x6E, 0x4B, 0xD5, 0x1F, 0x9A, 0xC5, 0x98,
+ 0x89, 0x4F, 0x48, 0x06, 0xFA, 0xC8, 0xCE, 0x66, 0x50, 0x55, 0x8C, 0x78, 0xB9, 0x9E, 0x6E, 0x87,
+ 0x16, 0x9B, 0x8D, 0x43, 0x21, 0x35, 0x1B, 0x38, 0xF8, 0x78, 0x79, 0xCC, 0x3C, 0x88, 0xCA, 0x5B,
+ 0xFC, 0x92, 0x9E, 0x8E, 0xB7, 0xF4, 0x8A, 0x6F, 0x4F, 0xAD, 0x73, 0x34, 0xAA, 0x55, 0xFD, 0x57,
+ 0xDF, 0x99, 0xBE, 0x48, 0x86, 0xED, 0xC0, 0xFC, 0x84, 0x92, 0xF3, 0x37, 0xEE, 0x0F, 0xE9, 0x59,
+ 0x97, 0xEA, 0x16, 0xC0, 0xC7, 0xE1, 0x87, 0x51, 0x10, 0x08, 0x20, 0xD4, 0x47, 0x34, 0xF0, 0xE0,
+ 0x52, 0xA4, 0xE7, 0x96, 0x3E, 0x87, 0xD7, 0x76, 0x6D, 0x12, 0xE9, 0xF5, 0xE2, 0x6E, 0x6F, 0x00,
+ 0xF0, 0xC3, 0x37, 0x6D, 0xD9, 0x7C, 0xE3, 0x8D, 0x9B, 0xB7, 0xDC, 0x04, 0x8F, 0x6F, 0xBE, 0xE9,
+ 0xC6, 0x2D, 0x5B, 0x6E, 0x3C, 0xB1, 0x96, 0x9B, 0xFE, 0x76, 0x42, 0x03, 0xC7, 0xC0, 0x6D, 0xFD,
+ 0xD6, 0x35, 0x07, 0xA6, 0x7D, 0xBA, 0x84, 0xE6, 0xA7, 0xF2, 0xF6, 0x27, 0x6E, 0xD0, 0x7F, 0xC6,
+ 0x4E, 0x77, 0xEF, 0x5A, 0xDA, 0xF4, 0xCB, 0x4C, 0x2D, 0x5E, 0x6F, 0xA2, 0x89, 0x4A, 0x15, 0x6D,
+ 0xDE, 0xD4, 0x25, 0x3A, 0xF5, 0x11, 0x34, 0x51, 0x43, 0xE3, 0x63, 0x13, 0xBB, 0x2B, 0x63, 0xEC,
+ 0x22, 0x9C, 0xBE, 0x3C, 0xE4, 0x37, 0x0C, 0x15, 0x0F, 0x49, 0x77, 0x58, 0xAB, 0x79, 0x59, 0xB6,
+ 0xBE, 0xDB, 0xB9, 0x1B, 0xA6, 0xD7, 0x2A, 0xF2, 0x7E, 0x87, 0x9F, 0x6A, 0x2D, 0x3A, 0x2E, 0xBB,
+ 0x4E, 0xED, 0xDF, 0x19, 0x46, 0x1E, 0xEC, 0x61, 0x34, 0xF4, 0x7B, 0xD9, 0xE8, 0x96, 0xDA, 0xEF,
+ 0xF0, 0x5F, 0xCD, 0x6D, 0xC9, 0x2A, 0xD7, 0x84, 0xB2, 0x96, 0x44, 0x42, 0xE2, 0xF7, 0x87, 0x97,
+ 0x53, 0x9B, 0x4F, 0x05, 0xD7, 0x3C, 0x62, 0x50, 0x09, 0xFF, 0x9F, 0x7C, 0x5B, 0xF1, 0x5D, 0xC5,
+ 0xC8, 0xE8, 0xE2, 0x49, 0x63, 0x6C, 0xAA, 0x1A, 0x3B, 0xFD, 0x7B, 0x63, 0xCF, 0xE6, 0xE9, 0x69,
+ 0xF9, 0xF5, 0xCC, 0x25, 0xB7, 0x99, 0xBB, 0x1B, 0xF3, 0x5A, 0xC5, 0xBF, 0x80, 0x31, 0x71, 0x1A,
+ 0x54, 0x30, 0x7E, 0x68, 0xFA, 0x03, 0xC8, 0x97, 0x4E, 0xFB, 0xC2, 0x21, 0x3F, 0xE1, 0xB3, 0xD2,
+ 0x79, 0x6B, 0x99, 0x50, 0x96, 0xB9, 0x60, 0x20, 0x52, 0x2A, 0xE4, 0x23, 0x51, 0x11, 0x01, 0x1D,
+ 0xD1, 0xC5, 0x42, 0x88, 0x08, 0xA2, 0x48, 0x38, 0x83, 0x12, 0xDC, 0xED, 0xC4, 0xE9, 0xF5, 0x3D,
+ 0x37, 0x3C, 0xDE, 0x70, 0x85, 0xC7, 0x70, 0xA7, 0xCA, 0x88, 0xB7, 0x3D, 0x4F, 0xF7, 0x1C, 0x28,
+ 0xC2, 0x99, 0xB5, 0xA5, 0x70, 0xD7, 0x65, 0xB8, 0xCF, 0xEA, 0x20, 0x4F, 0xA9, 0x7A, 0x05, 0xA5,
+ 0x30, 0x1B, 0xAA, 0xFB, 0x99, 0x64, 0x36, 0x69, 0x61, 0xE3, 0x63, 0x00, 0x2D, 0xDA, 0x2E, 0xFE,
+ 0xB7, 0xBC, 0xA2, 0xFF, 0xEF, 0xFC, 0xE1, 0x5B, 0xBF, 0xFB, 0xDD, 0xB7, 0xFE, 0xE1, 0x77, 0xF6,
+ 0xBE, 0xF7, 0x9E, 0x7B, 0xDE, 0xEB, 0x1E, 0xF7, 0xED, 0x3D, 0x52, 0xDF, 0xFE, 0x91, 0xB1, 0xAD,
+ 0x63, 0xB5, 0x40, 0x29, 0x3B, 0xB7, 0x54, 0x48, 0xC3, 0xE3, 0xEF, 0xF9, 0xDA, 0xD7, 0xDE, 0xF3,
+ 0x9E, 0xAF, 0x3D, 0xFD, 0xE4, 0xBD, 0x1F, 0xF8, 0xC0, 0xBD, 0xF8, 0xD8, 0xFB, 0x89, 0x4F, 0xC0,
+ 0x77, 0x34, 0xA6, 0xC6, 0x27, 0x4E, 0x47, 0x7C, 0xB7, 0xFB, 0xB0, 0xD5, 0xB3, 0x71, 0xEC, 0x23,
+ 0x60, 0x5B, 0x3F, 0x3D, 0xE2, 0x4B, 0xA7, 0x06, 0xC3, 0xAD, 0x2C, 0x07, 0x73, 0xCB, 0xF1, 0x85,
+ 0x94, 0xE8, 0x13, 0x8B, 0x28, 0x82, 0x47, 0x8C, 0xC7, 0xFD, 0xE2, 0x91, 0xAE, 0x6D, 0x73, 0xD0,
+ 0xBB, 0xB0, 0xB6, 0xD1, 0xC1, 0x79, 0x77, 0xBB, 0xB8, 0xFA, 0xFF, 0x61, 0x40, 0xD9, 0x8D, 0x3B,
+ 0x1D, 0x24, 0x3D, 0x94, 0x3C, 0xDC, 0xE0, 0xE0, 0xD2, 0x98, 0xB6, 0x7F, 0xF0, 0x65, 0x2F, 0xFB,
+ 0xA0, 0x7B, 0x9C, 0x14, 0x8F, 0xCC, 0x1E, 0x3C, 0x7D, 0xEB, 0xB5, 0xF3, 0x87, 0xF8, 0xAD, 0x74,
+ 0x3B, 0xD3, 0xEA, 0x76, 0x5B, 0x85, 0x26, 0x79, 0x69, 0x60, 0x83, 0x6F, 0x7D, 0xB0, 0xB3, 0x69,
+ 0xDF, 0x03, 0xD7, 0x5D, 0x7F, 0xDF, 0xCE, 0xCD, 0xC9, 0xFC, 0xF6, 0xFE, 0xD4, 0x9E, 0x42, 0x06,
+ 0x6B, 0xD2, 0x29, 0x58, 0x27, 0xDE, 0x85, 0x6D, 0x34, 0x11, 0x98, 0xE0, 0xAD, 0xFD, 0x19, 0x5E,
+ 0x40, 0x04, 0x42, 0xEE, 0x16, 0x78, 0x6E, 0x4A, 0x19, 0x40, 0xF7, 0xB3, 0xBA, 0x86, 0x8D, 0xB4,
+ 0x65, 0xD6, 0x94, 0x64, 0x45, 0x46, 0x9A, 0xCA, 0x32, 0xC8, 0x0D, 0x90, 0x02, 0x43, 0x67, 0x44,
+ 0x40, 0x99, 0x86, 0x28, 0x70, 0x14, 0xCB, 0xA8, 0x0A, 0x43, 0x93, 0x04, 0x64, 0xD6, 0x12, 0xE3,
+ 0xB0, 0x56, 0xF7, 0xB6, 0xC5, 0xC3, 0x0F, 0x38, 0xDC, 0x2A, 0x6F, 0x50, 0x09, 0x3D, 0x08, 0xDF,
+ 0xBA, 0xE6, 0x9C, 0xE7, 0x1A, 0x75, 0x01, 0x65, 0xE3, 0xC2, 0x70, 0x7B, 0x8D, 0x9F, 0xDC, 0xFB,
+ 0x79, 0x47, 0x58, 0xDB, 0x6B, 0xC3, 0x7E, 0xD4, 0x7D, 0xE3, 0x6E, 0x86, 0xE7, 0x66, 0x9D, 0x27,
+ 0x35, 0xC2, 0x4D, 0x50, 0xD3, 0x1A, 0x41, 0xD8, 0xA0, 0x92, 0x1A, 0xFE, 0x6F, 0xC6, 0x89, 0x77,
+ 0x1D, 0xBC, 0x7D, 0xFE, 0x36, 0x74, 0xF0, 0xF1, 0xE3, 0xC7, 0x1F, 0xBB, 0xF0, 0xEC, 0xD9, 0x43,
+ 0x67, 0xE1, 0x4E, 0x58, 0x87, 0xB5, 0x8B, 0x5F, 0xF7, 0x8E, 0xB7, 0x5F, 0x7C, 0x16, 0xEA, 0xDB,
+ 0x2F, 0x7E, 0x0C, 0x6E, 0xDF, 0x8E, 0xE5, 0xD0, 0x08, 0xF2, 0x41, 0xC7, 0xCB, 0x45, 0x8B, 0xF4,
+ 0x69, 0x2F, 0x17, 0xCD, 0xB3, 0xE2, 0xB0, 0x31, 0x7C, 0x45, 0x3A, 0x9A, 0x1B, 0x1B, 0x81, 0xCE,
+ 0x47, 0xDA, 0x6F, 0x40, 0x0F, 0xDC, 0xE0, 0x3C, 0x86, 0xF5, 0xC0, 0x33, 0xA8, 0x00, 0x47, 0x31,
+ 0x02, 0x11, 0x81, 0x1F, 0x4C, 0xF7, 0x2D, 0x1E, 0xD0, 0x8E, 0x64, 0x48, 0xC8, 0xC0, 0x57, 0x71,
+ 0x56, 0x54, 0x19, 0x12, 0x6B, 0x5B, 0x79, 0xBA, 0xA2, 0xAD, 0x85, 0xC7, 0x7B, 0xC6, 0x1D, 0x93,
+ 0x30, 0xD8, 0x4D, 0x71, 0xB8, 0xC7, 0xDD, 0xC0, 0x2E, 0x0C, 0x18, 0x5E, 0xFA, 0xFC, 0x20, 0x7B,
+ 0x08, 0x2B, 0x55, 0xAF, 0xBE, 0x74, 0x78, 0x0E, 0x47, 0x17, 0x59, 0xCA, 0x0A, 0x62, 0x45, 0x4B,
+ 0x69, 0xEF, 0xBA, 0x74, 0x8A, 0xAE, 0xA9, 0xA1, 0x9C, 0x62, 0xAA, 0xBA, 0x21, 0x95, 0xAC, 0x4B,
+ 0xA7, 0x03, 0xDC, 0xF5, 0x4B, 0xD4, 0x25, 0x4E, 0x62, 0x64, 0x94, 0x19, 0x22, 0xA6, 0x73, 0xE0,
+ 0x8B, 0xB8, 0xBF, 0x3F, 0x5C, 0x7D, 0x81, 0x0C, 0xA3, 0xCF, 0x83, 0xBD, 0x18, 0x31, 0xBD, 0xAF,
+ 0xBF, 0x7D, 0x76, 0x2C, 0x19, 0x92, 0xC0, 0x62, 0xB3, 0x3E, 0x52, 0x2A, 0x64, 0x93, 0xD9, 0xFE,
+ 0xBE, 0x42, 0x7F, 0xDF, 0xE2, 0xE4, 0xBE, 0x78, 0x02, 0xED, 0xDF, 0xB7, 0x6F, 0x7F, 0x7F, 0x12,
+ 0xBC, 0x62, 0x99, 0xFD, 0x83, 0xE5, 0xBD, 0x7B, 0x1E, 0x23, 0x29, 0x38, 0xB9, 0x54, 0xED, 0xA6,
+ 0xAB, 0x5F, 0x3F, 0x70, 0x97, 0x7E, 0x95, 0x69, 0x68, 0xDB, 0x26, 0xFB, 0xDA, 0x01, 0xD4, 0x57,
+ 0xF7, 0xA1, 0xCD, 0xFD, 0xB1, 0x27, 0x42, 0x33, 0x67, 0x61, 0xE8, 0x8A, 0x92, 0x1F, 0x75, 0xB0,
+ 0x94, 0xAE, 0x91, 0xE8, 0x6D, 0x64, 0xE1, 0x66, 0xC4, 0xBA, 0x31, 0xB4, 0x35, 0xF6, 0x5D, 0xDF,
+ 0xE0, 0x78, 0x83, 0x8E, 0xAB, 0x5C, 0x19, 0x3E, 0x5D, 0x0E, 0xAD, 0xD7, 0xB9, 0xBB, 0x26, 0x01,
+ 0x9D, 0xF1, 0xC2, 0x85, 0xF0, 0xD2, 0x9E, 0x17, 0x44, 0x0F, 0x7A, 0x25, 0xEF, 0x75, 0xBB, 0xE1,
+ 0xED, 0x74, 0x52, 0x6F, 0xBB, 0xD5, 0xDB, 0x2E, 0x4A, 0x70, 0x0B, 0xBC, 0x4D, 0xDA, 0x2D, 0x78,
+ 0x97, 0x51, 0x92, 0x36, 0xDD, 0x40, 0x56, 0xC4, 0xAB, 0x03, 0x6C, 0x43, 0xC2, 0x69, 0x93, 0x61,
+ 0xAF, 0x06, 0x3E, 0xF7, 0x69, 0xCE, 0x2F, 0xF8, 0x15, 0x9E, 0x25, 0xDE, 0xFC, 0x5A, 0x95, 0xF4,
+ 0x51, 0xBE, 0x97, 0x2B, 0x23, 0x46, 0x70, 0xE1, 0xCE, 0x8F, 0xED, 0x9F, 0xBE, 0xCA, 0x2D, 0x89,
+ 0x57, 0xFB, 0x22, 0xC9, 0xD2, 0x31, 0x5E, 0x12, 0x9C, 0xC2, 0xA3, 0xAC, 0x54, 0x97, 0xDC, 0xE2,
+ 0xF8, 0x1D, 0x64, 0xE3, 0xB3, 0x92, 0xDE, 0x3F, 0xB8, 0x13, 0x22, 0x4A, 0x60, 0x8A, 0x6F, 0xA2,
+ 0x66, 0x2F, 0x6E, 0xFB, 0xCE, 0xF2, 0xBD, 0x83, 0x02, 0x79, 0x2C, 0x9C, 0xF3, 0xA3, 0x5C, 0x88,
+ 0x73, 0xF7, 0xCF, 0xA0, 0x63, 0xDC, 0x60, 0xFF, 0x8C, 0xE9, 0x47, 0xBE, 0x7A, 0x74, 0x97, 0x57,
+ 0x2C, 0x8F, 0xF9, 0x83, 0xA4, 0xDD, 0x72, 0x79, 0x22, 0x72, 0x16, 0x52, 0xC1, 0xF9, 0x10, 0x2D,
+ 0x91, 0x6C, 0x4E, 0xAA, 0x56, 0x64, 0x0A, 0xC1, 0x37, 0xAC, 0x02, 0x44, 0x78, 0xF5, 0xA5, 0xFF,
+ 0x82, 0xFE, 0x07, 0xFA, 0x02, 0xB6, 0xCE, 0xF6, 0x62, 0x79, 0x12, 0xDA, 0xBD, 0x79, 0xAC, 0x92,
+ 0x49, 0x85, 0x1D, 0x9B, 0x21, 0xC0, 0x52, 0x23, 0x09, 0xE2, 0xED, 0xBD, 0xDB, 0xDF, 0xBF, 0x6F,
+ 0x9F, 0x42, 0xEE, 0x9B, 0xFC, 0x33, 0x3C, 0xBF, 0x36, 0xEC, 0x0C, 0xC2, 0x2B, 0xE7, 0x80, 0x06,
+ 0xC7, 0xBD, 0xF2, 0xC8, 0x17, 0xA5, 0x09, 0xB8, 0x73, 0xB9, 0xCF, 0xAB, 0x8E, 0x40, 0x11, 0xB8,
+ 0x5E, 0x5F, 0x96, 0xF5, 0xCA, 0xE0, 0xD7, 0x0C, 0xEF, 0x56, 0x63, 0x98, 0xB3, 0x36, 0x85, 0xDA,
+ 0xDE, 0x0E, 0x4D, 0x97, 0xF2, 0x39, 0xDC, 0x8C, 0x7D, 0xC2, 0x2D, 0x55, 0x84, 0x6B, 0xF9, 0x6C,
+ 0x19, 0xF4, 0x3F, 0xA2, 0x45, 0x63, 0x4C, 0x18, 0x16, 0x9F, 0x5D, 0x3D, 0xAB, 0x3B, 0xFE, 0xAC,
+ 0x79, 0x90, 0xE1, 0x06, 0x45, 0x68, 0x1C, 0x65, 0x96, 0x13, 0x12, 0xB9, 0xF5, 0x50, 0xBA, 0xE8,
+ 0xEB, 0xB4, 0x2F, 0x2F, 0x40, 0x5B, 0x30, 0x2A, 0x76, 0xFE, 0x4F, 0x28, 0x4D, 0x7D, 0xE4, 0x4C,
+ 0xAC, 0x31, 0x28, 0x53, 0x43, 0x7F, 0x99, 0xAE, 0x9A, 0x73, 0xA7, 0xD7, 0xCB, 0xD2, 0xC6, 0xED,
+ 0x30, 0xC3, 0x1C, 0xE5, 0x84, 0xCB, 0xAA, 0xD3, 0xB6, 0x3A, 0xF4, 0xDE, 0x9B, 0x0B, 0x8D, 0xC4,
+ 0x88, 0x75, 0xA9, 0x30, 0xAD, 0xA0, 0xB5, 0x69, 0xE6, 0xE2, 0x9B, 0xD3, 0xCD, 0xC8, 0x9B, 0x1E,
+ 0x4F, 0xA7, 0x07, 0xB5, 0x6B, 0x03, 0x0B, 0xF7, 0x39, 0xF8, 0x5E, 0xCF, 0x62, 0x58, 0x18, 0xD8,
+ 0x45, 0x9E, 0x5D, 0xED, 0x55, 0xDD, 0x7B, 0x5C, 0x51, 0x18, 0x72, 0xC5, 0x37, 0x3C, 0x2B, 0xDC,
+ 0xCB, 0xA3, 0xF2, 0xBE, 0xBD, 0x79, 0xD8, 0xFE, 0x06, 0x30, 0x88, 0x02, 0x7C, 0x0E, 0xDD, 0x8D,
+ 0x7E, 0x8A, 0x51, 0xF1, 0xAB, 0xFB, 0xD3, 0xBD, 0x6E, 0x39, 0x9D, 0x4A, 0x86, 0x43, 0x41, 0x5D,
+ 0x53, 0x64, 0x89, 0x63, 0x19, 0xD0, 0xAE, 0x95, 0x0A, 0x46, 0xBB, 0xAD, 0x06, 0x1C, 0xDB, 0x56,
+ 0x03, 0xC9, 0x04, 0x4F, 0x70, 0x13, 0xE9, 0x14, 0x4B, 0x30, 0x47, 0xC2, 0x21, 0x2C, 0x15, 0x27,
+ 0xDC, 0x44, 0x0F, 0x25, 0x1A, 0x08, 0x20, 0x75, 0xF6, 0x73, 0xB0, 0xBC, 0x26, 0x07, 0x6C, 0xD8,
+ 0x5D, 0x4B, 0xB1, 0xDB, 0xB8, 0x63, 0x38, 0x5C, 0xDB, 0x4D, 0x55, 0xED, 0xBE, 0x78, 0xF5, 0x2E,
+ 0xC1, 0xE5, 0xF2, 0x20, 0x34, 0x44, 0x36, 0xAC, 0x24, 0x43, 0x33, 0xF8, 0x79, 0xB0, 0xA5, 0x2A,
+ 0x5E, 0x10, 0x77, 0x57, 0xD1, 0x4C, 0x13, 0x2F, 0x12, 0x7E, 0x1D, 0x94, 0x65, 0x6F, 0xC8, 0x99,
+ 0x27, 0x06, 0x39, 0xF3, 0xE8, 0x6E, 0x55, 0x1B, 0x91, 0x95, 0xAA, 0x70, 0xC7, 0x5B, 0xA8, 0x3C,
+ 0xF5, 0xE5, 0xAF, 0xC0, 0xA0, 0x59, 0xF3, 0x53, 0x1F, 0xFB, 0x10, 0x49, 0xFB, 0xEC, 0x3B, 0xB7,
+ 0x59, 0xF6, 0x58, 0x52, 0x66, 0x05, 0x4E, 0x5F, 0xD1, 0xAD, 0xAE, 0x59, 0x2A, 0xE6, 0x54, 0x67,
+ 0x44, 0xC3, 0x12, 0xBF, 0x52, 0x2E, 0x96, 0xD1, 0xF7, 0xAD, 0xC8, 0xA1, 0x40, 0xF4, 0x5A, 0x7B,
+ 0x96, 0xDF, 0x5E, 0x9B, 0xDD, 0x65, 0xFB, 0xD4, 0x92, 0xD3, 0x4D, 0x9D, 0xA4, 0x66, 0x35, 0xCA,
+ 0x6A, 0x65, 0x67, 0x93, 0x89, 0xED, 0xD9, 0x41, 0xFE, 0xFC, 0xE2, 0xF6, 0x1D, 0x89, 0xEC, 0xAD,
+ 0xA5, 0x8A, 0x4F, 0x90, 0x25, 0x2F, 0x7D, 0xDE, 0x8B, 0x25, 0xF9, 0xBD, 0x58, 0xD2, 0x34, 0xB8,
+ 0xB6, 0x9F, 0xCD, 0x25, 0xFC, 0xB6, 0xA9, 0xA6, 0x93, 0xF1, 0x08, 0x35, 0xF2, 0x37, 0x33, 0xE4,
+ 0xB5, 0x33, 0x65, 0x0A, 0x4C, 0xF7, 0x28, 0xD4, 0x23, 0x51, 0xB7, 0x8D, 0xFA, 0xBD, 0xEE, 0x0B,
+ 0x2B, 0x67, 0xD7, 0xC2, 0x65, 0x67, 0xDC, 0xE9, 0x9A, 0xEC, 0x5D, 0x1E, 0x56, 0xDA, 0xE0, 0x9E,
+ 0xBE, 0xAC, 0x7E, 0x22, 0x71, 0x79, 0x7C, 0x29, 0x4A, 0xB8, 0x39, 0x21, 0xCD, 0x0D, 0x11, 0xA6,
+ 0x41, 0xDC, 0xDA, 0x45, 0x5B, 0xBF, 0x37, 0xC6, 0x44, 0xBB, 0x1B, 0x5D, 0xCC, 0x2F, 0x69, 0x84,
+ 0xBA, 0x16, 0x65, 0x92, 0x59, 0x37, 0xCA, 0x44, 0xD3, 0xDC, 0xEF, 0x0B, 0x32, 0xD1, 0xC9, 0x7C,
+ 0x32, 0x97, 0x6E, 0xE5, 0x9F, 0x8A, 0xBE, 0xF9, 0xE1, 0x41, 0xA8, 0xE9, 0xFF, 0xA2, 0xEE, 0xCB,
+ 0xE3, 0xE4, 0x2A, 0xCA, 0x76, 0xDF, 0xAA, 0xD3, 0x67, 0xDF, 0x7B, 0x9D, 0xE9, 0xD9, 0xF7, 0xC9,
+ 0x64, 0x9B, 0x25, 0x99, 0xC9, 0x3E, 0x93, 0x7D, 0x21, 0x84, 0x09, 0x21, 0x3B, 0x43, 0x86, 0x24,
+ 0x06, 0x0C, 0x04, 0x48, 0x08, 0x18, 0x64, 0x5F, 0x8C, 0x88, 0x80, 0x88, 0x8A, 0x44, 0x81, 0x0F,
+ 0x90, 0x25, 0x0A, 0x22, 0x28, 0x2E, 0xEC, 0x88, 0xC8, 0x26, 0xAB, 0x22, 0x84, 0x45, 0xE4, 0x13,
+ 0xAE, 0xEC, 0x08, 0x08, 0x5C, 0xF4, 0x23, 0xF4, 0x7D, 0xAA, 0x4E, 0x77, 0x4F, 0x27, 0x99, 0x28,
+ 0xF7, 0xFE, 0xBE, 0xFB, 0xC7, 0x77, 0xA6, 0x6B, 0xBA, 0xBB, 0xBA, 0x4E, 0x55, 0x3D, 0x55, 0x6F,
+ 0x3D, 0xEF, 0x5B, 0xCB, 0xA9, 0x8A, 0x89, 0x99, 0xA6, 0xFA, 0x46, 0xDF, 0x09, 0xF6, 0x9E, 0x6A,
+ 0x82, 0xEC, 0xC9, 0xDD, 0x12, 0xA4, 0x4C, 0x8E, 0xCC, 0xCB, 0xDE, 0x5B, 0x72, 0x84, 0xA9, 0xB0,
+ 0x56, 0xAD, 0x99, 0x0E, 0x88, 0xFC, 0x99, 0x29, 0xFB, 0xD9, 0x7A, 0xAE, 0x8E, 0x4D, 0x95, 0xE1,
+ 0x47, 0xE7, 0xC3, 0x97, 0x4B, 0x66, 0xFF, 0x20, 0x97, 0x96, 0x23, 0x01, 0x19, 0xAA, 0xA6, 0x65,
+ 0x7D, 0x35, 0x95, 0x09, 0xF2, 0x8D, 0x6C, 0x5A, 0xA9, 0xAC, 0x80, 0xF9, 0xD5, 0x52, 0x55, 0xE5,
+ 0x28, 0x65, 0xE9, 0x4C, 0x9A, 0xC7, 0x94, 0x6A, 0xE5, 0x2F, 0xCC, 0x29, 0x76, 0x44, 0x60, 0xCD,
+ 0xE7, 0xC7, 0x68, 0xE5, 0x4E, 0xED, 0xA2, 0x2B, 0x92, 0x65, 0x7D, 0xBB, 0x83, 0xBC, 0x40, 0x0E,
+ 0x2D, 0xB2, 0xA9, 0x71, 0xD4, 0x91, 0x99, 0xFD, 0xAC, 0x98, 0x4A, 0x34, 0xB4, 0xE8, 0x0D, 0x3D,
+ 0x5D, 0xBB, 0x86, 0x59, 0x29, 0xB5, 0xE4, 0x84, 0xF6, 0xA9, 0xFD, 0x47, 0x6C, 0xB8, 0x61, 0xB8,
+ 0x15, 0x52, 0xFC, 0xA2, 0x95, 0x2B, 0xB7, 0x01, 0x91, 0x7C, 0xF6, 0x4E, 0x22, 0xEA, 0xCE, 0x23,
+ 0xFA, 0x11, 0x10, 0x31, 0x72, 0xD9, 0x99, 0x6C, 0x80, 0x3F, 0x86, 0x5E, 0xFC, 0xDC, 0xBE, 0x74,
+ 0x6B, 0x3D, 0x35, 0x85, 0xBE, 0x11, 0xC6, 0xB3, 0x4E, 0x4B, 0x4B, 0x3C, 0x1B, 0xE7, 0x59, 0x56,
+ 0x98, 0xB7, 0x10, 0x32, 0x02, 0xB3, 0x50, 0x58, 0x87, 0xD3, 0x7B, 0x0B, 0x1B, 0xC7, 0xCA, 0xA6,
+ 0x54, 0xBA, 0xBE, 0xA1, 0x23, 0x2B, 0xD5, 0xB7, 0x98, 0xE5, 0xF2, 0xF2, 0x8B, 0x9E, 0x32, 0xFA,
+ 0xD0, 0xD2, 0x8D, 0x5E, 0xA5, 0xA5, 0xB9, 0xE4, 0xA9, 0x1A, 0x36, 0x30, 0xDF, 0x4D, 0xD4, 0x8D,
+ 0x48, 0xC6, 0x0D, 0xBD, 0x3C, 0x50, 0xED, 0x6E, 0xAB, 0x3C, 0xAE, 0x57, 0xA5, 0x6B, 0x12, 0xE6,
+ 0xC8, 0xD6, 0xF2, 0xEE, 0xF2, 0xC0, 0x70, 0x52, 0x7A, 0x5D, 0xB5, 0xE9, 0xA8, 0x23, 0x1A, 0x82,
+ 0x44, 0x6F, 0x5B, 0x59, 0x86, 0xDD, 0xD6, 0xD3, 0x30, 0x72, 0x44, 0x52, 0x55, 0x62, 0x81, 0x5F,
+ 0x6F, 0xDA, 0x56, 0x55, 0x59, 0x5C, 0x6B, 0x2C, 0x0F, 0x3A, 0x1B, 0x6D, 0x95, 0xC5, 0x54, 0xB7,
+ 0x47, 0xD7, 0x9B, 0x6A, 0x57, 0x2C, 0xEC, 0x68, 0x17, 0xBD, 0x9E, 0x80, 0x9D, 0xC0, 0x1A, 0xF8,
+ 0xDD, 0xC0, 0xDB, 0xD2, 0xA7, 0xD7, 0x96, 0x05, 0xAE, 0xA9, 0xB8, 0x72, 0xA7, 0xE0, 0x59, 0x72,
+ 0x64, 0x61, 0x66, 0x2F, 0x95, 0x8E, 0x99, 0xB6, 0x98, 0x85, 0x29, 0xF2, 0xC8, 0xE0, 0xEB, 0x65,
+ 0x7B, 0x2C, 0x32, 0x29, 0xAD, 0x0C, 0xF4, 0x1D, 0xEC, 0x93, 0xDB, 0xDB, 0xA6, 0xA5, 0x1B, 0x6B,
+ 0x75, 0x23, 0xDE, 0xE2, 0x98, 0x56, 0xA3, 0xAA, 0xB7, 0x4F, 0xB5, 0x54, 0x97, 0x97, 0x75, 0x34,
+ 0x54, 0xB7, 0x85, 0xFC, 0x38, 0xAD, 0x35, 0x9C, 0x76, 0xDA, 0x8A, 0x91, 0x55, 0xCD, 0x35, 0x65,
+ 0x65, 0x65, 0x29, 0x7F, 0x56, 0x46, 0x8F, 0x75, 0x64, 0x1A, 0xD6, 0x30, 0xC5, 0x49, 0xD4, 0x35,
+ 0x94, 0x0B, 0xF9, 0x92, 0xB3, 0xF9, 0x92, 0x0B, 0x97, 0x45, 0xCC, 0xB9, 0x26, 0x1A, 0x19, 0x7C,
+ 0x43, 0x51, 0xD9, 0xBB, 0xB4, 0x56, 0x3C, 0xF7, 0xBD, 0x70, 0xE6, 0xF8, 0x66, 0x74, 0x78, 0xA7,
+ 0x8C, 0xEB, 0xAC, 0x9B, 0xB2, 0x7C, 0x4C, 0x76, 0xF9, 0x98, 0x85, 0x87, 0xAC, 0xDD, 0xBE, 0x6E,
+ 0x4D, 0x66, 0x5D, 0x36, 0xBD, 0x6E, 0xE0, 0xF9, 0x75, 0x65, 0xC7, 0xAF, 0xAB, 0x8D, 0xAD, 0x6B,
+ 0x98, 0xBD, 0x6E, 0xF1, 0xF2, 0xE5, 0x7C, 0x4C, 0xDF, 0xC4, 0xDF, 0xAB, 0x2F, 0x0E, 0xCD, 0xB9,
+ 0x4D, 0x19, 0x5A, 0x5C, 0x3C, 0xF4, 0x37, 0xF6, 0x19, 0x56, 0x62, 0x01, 0x94, 0x74, 0xB0, 0xD6,
+ 0x35, 0x0F, 0x2D, 0x3C, 0x6E, 0xC1, 0x4B, 0xFF, 0xBC, 0x6B, 0xF7, 0x41, 0x79, 0x25, 0xAB, 0x94,
+ 0x3B, 0x4B, 0x56, 0x29, 0xCB, 0x99, 0xAA, 0x1E, 0x61, 0x28, 0xAB, 0x85, 0x85, 0xCA, 0x9D, 0xF1,
+ 0x8A, 0x39, 0x8D, 0xE5, 0x13, 0xE2, 0x9F, 0x6F, 0x81, 0xFF, 0x82, 0xC6, 0x9A, 0xA5, 0xA5, 0xEB,
+ 0x9A, 0x63, 0xE3, 0xA2, 0x75, 0xCD, 0x41, 0x22, 0x3B, 0x6D, 0x6E, 0x43, 0xB3, 0xA2, 0xC5, 0xF2,
+ 0x6B, 0x9B, 0xC7, 0x8F, 0xAD, 0xA9, 0x35, 0x15, 0xA6, 0xFC, 0xBB, 0x47, 0x00, 0x0E, 0xEF, 0x6A,
+ 0x9B, 0x57, 0xBA, 0x04, 0x7A, 0x4C, 0x7E, 0x05, 0xF4, 0xC8, 0xD6, 0x86, 0xDA, 0x5E, 0x3F, 0xC6,
+ 0xC5, 0x78, 0xC3, 0x27, 0xB9, 0x47, 0x18, 0x5A, 0x09, 0xF5, 0x40, 0x9F, 0x9D, 0xDB, 0x37, 0xAD,
+ 0xB1, 0x26, 0x11, 0x37, 0x68, 0xDC, 0x98, 0x96, 0xCE, 0xA9, 0x63, 0x3B, 0xA7, 0x2A, 0x9D, 0x53,
+ 0x2B, 0x7B, 0x33, 0x63, 0xEA, 0x32, 0x5E, 0x5D, 0xA6, 0x72, 0xE4, 0x88, 0x9E, 0xC6, 0x77, 0xE6,
+ 0xCE, 0xDC, 0x3A, 0x77, 0xF6, 0xCE, 0xB9, 0x73, 0x9D, 0x1D, 0x73, 0xA7, 0xFE, 0x57, 0xE7, 0xB3,
+ 0x13, 0x7A, 0x61, 0x45, 0xD6, 0xF1, 0x0C, 0xAD, 0x8A, 0x1F, 0x54, 0xB0, 0xC1, 0x7B, 0x8B, 0x73,
+ 0x7D, 0xBD, 0xD1, 0xC8, 0x83, 0xC7, 0xE6, 0xE6, 0xAB, 0xA3, 0x77, 0xCA, 0xBE, 0xFB, 0x0F, 0xED,
+ 0xBD, 0x99, 0xE5, 0xDC, 0xB8, 0x34, 0x26, 0xA2, 0x6D, 0x51, 0x51, 0xEE, 0xA2, 0x6A, 0x1A, 0x7A,
+ 0x5A, 0x4A, 0xD6, 0x41, 0x65, 0x60, 0x70, 0xC1, 0xAA, 0x68, 0x6E, 0x10, 0x0F, 0xAC, 0x47, 0xB3,
+ 0xE2, 0xD2, 0x8C, 0x18, 0x87, 0x7A, 0x94, 0x76, 0x04, 0xEA, 0x81, 0xBD, 0x57, 0xDE, 0xD0, 0xA8,
+ 0xA4, 0x2A, 0xEB, 0xDA, 0x2F, 0x5F, 0xDC, 0x39, 0xAA, 0x26, 0xD3, 0x92, 0x5C, 0xAC, 0xA4, 0xF2,
+ 0x2B, 0xA4, 0x4C, 0xBD, 0x65, 0x7C, 0x66, 0x6C, 0x6F, 0x68, 0xC4, 0x7C, 0xAE, 0xB4, 0x2E, 0xED,
+ 0xAE, 0xA8, 0x98, 0x9C, 0x6A, 0x39, 0xC6, 0xAE, 0x54, 0x6D, 0xFE, 0xA5, 0x54, 0x9B, 0x19, 0x0B,
+ 0x83, 0xC9, 0xFC, 0xC0, 0xA6, 0xB1, 0x5D, 0x3C, 0x68, 0x3E, 0x48, 0xAC, 0x99, 0x6A, 0x99, 0xA6,
+ 0xEB, 0xC7, 0x29, 0x15, 0x25, 0x2B, 0xA7, 0x6A, 0x95, 0x90, 0x77, 0x1F, 0xD8, 0x30, 0x52, 0x2E,
+ 0x9C, 0x72, 0xD6, 0xC2, 0x50, 0x58, 0x0A, 0x9A, 0x32, 0x3D, 0xE5, 0xC6, 0xCA, 0x32, 0x4D, 0x87,
+ 0x9D, 0xE5, 0xE7, 0xA6, 0xB0, 0x3F, 0xF0, 0x2A, 0x0A, 0xA8, 0x93, 0xFA, 0xFB, 0xCA, 0x9A, 0x42,
+ 0x5D, 0xE7, 0x76, 0x23, 0xB7, 0xBB, 0x3A, 0x97, 0x75, 0xD9, 0x9C, 0xB7, 0xF1, 0x2A, 0x67, 0x80,
+ 0x55, 0x15, 0x58, 0x26, 0xC9, 0x16, 0x88, 0x31, 0x41, 0x8F, 0xCD, 0x2F, 0x9A, 0xEF, 0xB2, 0xE0,
+ 0xC6, 0xB0, 0xB2, 0xA1, 0x1D, 0xF9, 0xF2, 0x1B, 0xF2, 0x55, 0x45, 0x03, 0x03, 0x72, 0x35, 0x79,
+ 0xA4, 0x79, 0x12, 0x25, 0xFB, 0x79, 0xF5, 0x2A, 0x72, 0xFF, 0x98, 0xFC, 0x73, 0x5D, 0xAC, 0x87,
+ 0xAD, 0x8D, 0xE7, 0xB7, 0xDA, 0xCB, 0xCA, 0x8D, 0xF7, 0x4E, 0x3E, 0x66, 0xC4, 0x64, 0xD7, 0x75,
+ 0x5B, 0x5B, 0x67, 0xB6, 0x1B, 0xFB, 0xEE, 0xD3, 0xC7, 0x29, 0x5B, 0xDC, 0x6C, 0xCF, 0x9F, 0xC9,
+ 0xBD, 0xDD, 0xD7, 0xAD, 0xA8, 0x53, 0x74, 0x6F, 0xCC, 0xEC, 0xF1, 0xC3, 0xED, 0xD2, 0x27, 0xC6,
+ 0x43, 0x67, 0xE7, 0xC6, 0xD3, 0x6E, 0x9E, 0x20, 0x0F, 0xBA, 0x62, 0x56, 0x5F, 0x32, 0x61, 0xA5,
+ 0x75, 0x1D, 0x9D, 0x05, 0x3F, 0xC9, 0xC5, 0xCB, 0xDE, 0x56, 0x58, 0xFA, 0x0F, 0x74, 0x09, 0xB6,
+ 0xB0, 0xB7, 0xEC, 0x36, 0x70, 0xEF, 0x01, 0x85, 0xA6, 0xBA, 0xF7, 0x66, 0x83, 0x59, 0x2B, 0xDA,
+ 0xB2, 0x1E, 0xD8, 0x9A, 0x25, 0xB4, 0x84, 0xAF, 0xF4, 0xF2, 0x1A, 0xFE, 0x66, 0x22, 0xDA, 0x93,
+ 0xBB, 0x4B, 0xEC, 0xCF, 0xDD, 0x30, 0xA2, 0xAD, 0x6D, 0x44, 0x74, 0x14, 0x0C, 0xFB, 0x38, 0x5B,
+ 0xD8, 0xA2, 0xBB, 0x72, 0x26, 0xDF, 0xBD, 0xA3, 0xAA, 0xAA, 0xBA, 0x26, 0x3A, 0x0C, 0x06, 0x39,
+ 0xAB, 0x46, 0xF6, 0x9E, 0xE7, 0x77, 0xCA, 0x3D, 0x1D, 0xE7, 0xF4, 0x41, 0x65, 0xE8, 0x24, 0x4E,
+ 0xBC, 0x21, 0x5D, 0x28, 0x60, 0x85, 0xA1, 0x0B, 0xC6, 0x0A, 0x03, 0x33, 0xB7, 0x11, 0x67, 0x8B,
+ 0xF6, 0x38, 0xF4, 0x46, 0x0C, 0x9B, 0x1D, 0x08, 0xCD, 0x75, 0x1B, 0xA9, 0xE2, 0x3D, 0xBF, 0xCB,
+ 0xE3, 0x08, 0x16, 0xD6, 0x29, 0x61, 0x5D, 0xC8, 0x9F, 0xDF, 0x3D, 0x8B, 0xDF, 0x59, 0xBE, 0xFB,
+ 0x44, 0x7E, 0xE7, 0xEE, 0x01, 0x7E, 0xDE, 0xEE, 0x13, 0xA9, 0x38, 0x0F, 0xB3, 0xAC, 0x38, 0x0F,
+ 0xF3, 0x02, 0x33, 0xE8, 0xB2, 0xE2, 0x48, 0xF2, 0xED, 0xC5, 0x91, 0xE4, 0x17, 0xE8, 0x6A, 0x79,
+ 0xE2, 0xD4, 0x48, 0xE8, 0xA6, 0xB7, 0xE4, 0x6E, 0xE5, 0x15, 0x34, 0x82, 0x96, 0xF4, 0x35, 0x8B,
+ 0x8D, 0x42, 0xBD, 0x64, 0x4B, 0x65, 0x7D, 0x25, 0xAF, 0x47, 0x27, 0x8F, 0xBC, 0xFA, 0xA4, 0xC7,
+ 0x93, 0x2D, 0xE5, 0xF6, 0xE4, 0x81, 0x9A, 0xAA, 0xD0, 0x67, 0xAA, 0x32, 0xB4, 0xD6, 0x27, 0x23,
+ 0xCE, 0xAE, 0x91, 0xA2, 0x22, 0x95, 0xAB, 0x34, 0x5A, 0x86, 0x1A, 0x96, 0x58, 0x8A, 0x56, 0x9F,
+ 0x7F, 0xAC, 0x25, 0xBF, 0x47, 0x67, 0xB1, 0xCF, 0xB7, 0xD7, 0x67, 0xFE, 0x56, 0xC9, 0x9E, 0xE7,
+ 0x4F, 0xED, 0xE7, 0x73, 0xE9, 0x5E, 0xE8, 0x95, 0xFB, 0xF9, 0x5C, 0x9C, 0x1F, 0x3C, 0xB5, 0x38,
+ 0x3F, 0x38, 0x33, 0x9A, 0x89, 0xCA, 0xFB, 0x9F, 0x54, 0xF4, 0x7F, 0x8E, 0x9E, 0x2C, 0x99, 0xD1,
+ 0x3A, 0xA9, 0x38, 0xA3, 0xF5, 0x1C, 0x3D, 0x20, 0xB5, 0x33, 0xFC, 0x63, 0x35, 0xE8, 0xA3, 0xAC,
+ 0xA3, 0x1F, 0xF4, 0xF5, 0x0F, 0xAC, 0x5E, 0xBA, 0x70, 0x52, 0x5D, 0x4D, 0x99, 0x45, 0xF3, 0x67,
+ 0xCE, 0x18, 0x5F, 0x6D, 0xCC, 0x5C, 0x3D, 0xDA, 0x58, 0x3D, 0x7A, 0xCE, 0xA1, 0xA3, 0xD7, 0xAC,
+ 0x74, 0xD5, 0xCA, 0x89, 0x87, 0xD0, 0xB9, 0xEB, 0x5B, 0x77, 0xAE, 0x5F, 0x7C, 0xC6, 0x9C, 0x55,
+ 0x2B, 0x4F, 0x3F, 0xB8, 0x7F, 0xF4, 0xAA, 0xD5, 0xDB, 0x0F, 0x5E, 0xB7, 0x76, 0xBB, 0x65, 0xC6,
+ 0xA0, 0x54, 0x5C, 0xC7, 0x56, 0x46, 0xF3, 0x35, 0x87, 0x1D, 0xBC, 0x38, 0x76, 0xE8, 0xC2, 0xDE,
+ 0x1F, 0xC6, 0x8D, 0xA1, 0xE3, 0x63, 0x0A, 0xDB, 0xE9, 0xCB, 0xA1, 0x64, 0x31, 0x3E, 0x70, 0x1B,
+ 0xCD, 0xC9, 0xEF, 0x93, 0x96, 0x67, 0x2A, 0xB9, 0xBC, 0x6F, 0x9F, 0x1E, 0xE4, 0x9E, 0x0F, 0x37,
+ 0xAC, 0x2F, 0xDB, 0x6B, 0xAF, 0xB4, 0xF1, 0x43, 0x7B, 0x6B, 0x14, 0x3A, 0x3D, 0x62, 0x73, 0x0D,
+ 0xB1, 0x91, 0xA0, 0x5A, 0xD2, 0xED, 0x49, 0x67, 0xF2, 0x7B, 0x81, 0x15, 0x7A, 0x93, 0xC5, 0xAD,
+ 0xD4, 0x6A, 0x58, 0xB4, 0x91, 0x5A, 0xAC, 0xA6, 0x74, 0x23, 0x35, 0xC5, 0x5F, 0x3E, 0xB4, 0x0B,
+ 0x87, 0xE8, 0x00, 0x15, 0xB6, 0xE1, 0xC8, 0xFA, 0x46, 0xD0, 0xBD, 0xD7, 0x36, 0x1C, 0x73, 0xEA,
+ 0xAB, 0xA2, 0xED, 0xD6, 0xF2, 0x7D, 0xCA, 0xD2, 0x0D, 0xD7, 0x94, 0x7D, 0xF6, 0x5B, 0x2B, 0x53,
+ 0xDC, 0x19, 0xC3, 0xEE, 0xD5, 0x31, 0x7B, 0x46, 0x5D, 0x6A, 0x52, 0xD3, 0xE1, 0xA5, 0x7B, 0x75,
+ 0x24, 0x2A, 0xF6, 0xD9, 0x90, 0xED, 0x3C, 0xB9, 0x1D, 0x9B, 0x98, 0x03, 0x7E, 0x83, 0x7F, 0xCA,
+ 0xDE, 0xA1, 0x35, 0x74, 0x78, 0xDF, 0xD8, 0xA5, 0x07, 0xCE, 0xEB, 0x6D, 0x47, 0x8F, 0x26, 0x9B,
+ 0x89, 0xD3, 0x84, 0xAE, 0xB1, 0xF3, 0x46, 0x65, 0xFA, 0x47, 0xB5, 0xF5, 0xAC, 0xD9, 0x3E, 0x78,
+ 0x68, 0x66, 0x70, 0xD5, 0xF3, 0x83, 0xD6, 0x09, 0x83, 0x35, 0x5B, 0x07, 0xEB, 0x76, 0x0E, 0x7A,
+ 0x3B, 0x06, 0xA7, 0xBC, 0xA0, 0xBE, 0x38, 0xB4, 0x84, 0x2F, 0x28, 0xAA, 0x89, 0x29, 0x62, 0xD4,
+ 0x6A, 0x98, 0x9E, 0xE6, 0x60, 0xBC, 0x44, 0x6D, 0xD7, 0xEF, 0xB5, 0x3E, 0x36, 0x62, 0xFE, 0x1E,
+ 0xB1, 0x4B, 0x13, 0x2F, 0xD5, 0x0D, 0x51, 0x2F, 0x73, 0x18, 0xA5, 0x2D, 0xF7, 0x3A, 0x8C, 0x2C,
+ 0x36, 0x31, 0x4B, 0xFE, 0x69, 0x41, 0x6D, 0xD7, 0xF9, 0x5C, 0x3F, 0xAA, 0x64, 0x45, 0xED, 0x3C,
+ 0xA9, 0x13, 0x0C, 0xE6, 0x8E, 0x3A, 0xA4, 0x33, 0x34, 0x23, 0xAD, 0xD1, 0xD3, 0xD1, 0x19, 0x4F,
+ 0x5B, 0x0D, 0x23, 0xF7, 0x55, 0xD8, 0x99, 0x3A, 0x4D, 0x0F, 0x0F, 0xDF, 0x43, 0x61, 0xD7, 0x67,
+ 0x8D, 0x23, 0xF6, 0x58, 0x79, 0xDB, 0xAA, 0xE9, 0x0B, 0xE3, 0xF1, 0x54, 0xEB, 0xC1, 0xD1, 0xDA,
+ 0xDB, 0x66, 0xBF, 0x3C, 0x55, 0xD7, 0xB8, 0xAF, 0xAA, 0xAE, 0x69, 0x8A, 0xC7, 0x6A, 0xAB, 0x3A,
+ 0xD6, 0x49, 0x5D, 0x2D, 0xCE, 0xA1, 0xFB, 0x88, 0x8D, 0x62, 0x87, 0x81, 0x3D, 0x1A, 0xFB, 0x34,
+ 0xF9, 0x44, 0x24, 0x93, 0x23, 0x5E, 0x87, 0x44, 0x1C, 0xB6, 0x64, 0xAF, 0xE7, 0x22, 0x9B, 0xEA,
+ 0xC6, 0xD7, 0xB1, 0x51, 0x9F, 0x3D, 0xCD, 0x46, 0x7D, 0xF4, 0xC5, 0xE8, 0x6E, 0xBE, 0xE2, 0xFF,
+ 0xEE, 0x6E, 0xBE, 0x62, 0xF7, 0x4E, 0xBE, 0x02, 0x77, 0x73, 0x98, 0x8F, 0x6F, 0xB0, 0x3B, 0x60,
+ 0x33, 0x27, 0xD1, 0x9B, 0xE8, 0xEC, 0x73, 0x46, 0x34, 0x57, 0xC7, 0x7D, 0xD2, 0x53, 0x4D, 0x03,
+ 0xE5, 0x72, 0x44, 0x4B, 0x61, 0xCB, 0x7A, 0xA7, 0x48, 0x1D, 0x36, 0x5D, 0xEA, 0xB0, 0x67, 0xA8,
+ 0x64, 0x39, 0x73, 0x75, 0xB4, 0x7F, 0x83, 0x5C, 0x02, 0xA3, 0xCA, 0x67, 0x7C, 0xA2, 0xE7, 0xF9,
+ 0xC7, 0xC8, 0xCD, 0xA9, 0xF3, 0x5E, 0x69, 0x76, 0x87, 0x37, 0x6A, 0x8C, 0xE9, 0xF4, 0x55, 0x14,
+ 0xCF, 0x9B, 0x78, 0xD8, 0x4B, 0x8C, 0xC9, 0x9A, 0x96, 0xDD, 0x2D, 0xBC, 0x9C, 0x1E, 0x78, 0x75,
+ 0xF1, 0x0E, 0xB7, 0x29, 0xAD, 0xEA, 0xE3, 0xEA, 0x33, 0x55, 0x4E, 0x2C, 0x61, 0x34, 0xC4, 0xED,
+ 0xAA, 0x74, 0x45, 0xB3, 0x2D, 0x76, 0x0E, 0x56, 0x75, 0xAB, 0xAD, 0xA1, 0x3C, 0xEB, 0xC5, 0x42,
+ 0xA3, 0x21, 0xB4, 0xAA, 0x53, 0x95, 0x6D, 0xF2, 0xD4, 0x2E, 0xF6, 0x5A, 0x69, 0xBE, 0xDB, 0xAA,
+ 0x2B, 0x1C, 0x53, 0x4B, 0xD4, 0x35, 0x0D, 0xA8, 0x32, 0xDF, 0x59, 0xB6, 0x5C, 0xE6, 0xDB, 0x1F,
+ 0x2E, 0xDF, 0x8E, 0x38, 0xFE, 0xA5, 0xB8, 0xB9, 0x71, 0x77, 0x8F, 0x30, 0x49, 0x32, 0x79, 0x8F,
+ 0xE6, 0x96, 0x68, 0x3F, 0x44, 0x31, 0x6D, 0x5C, 0x7A, 0x42, 0x86, 0x84, 0x70, 0x6F, 0x31, 0xBF,
+ 0x5E, 0x01, 0x02, 0xEF, 0x29, 0xCD, 0x6F, 0x01, 0x44, 0x4D, 0x49, 0x6E, 0x8D, 0x21, 0x10, 0xF2,
+ 0x9C, 0x95, 0x37, 0xE8, 0x65, 0xE4, 0x3B, 0x4D, 0x4D, 0x7D, 0x5A, 0x32, 0x34, 0x29, 0x1A, 0x39,
+ 0x8C, 0xA1, 0x9C, 0x4F, 0x14, 0xD3, 0xDF, 0xD3, 0x4B, 0xCE, 0xED, 0xC8, 0x98, 0xE2, 0x19, 0x94,
+ 0x7D, 0x0A, 0xF4, 0xE5, 0xE1, 0x8A, 0x6F, 0x7F, 0x25, 0x35, 0x94, 0x62, 0x52, 0xA4, 0x28, 0xCE,
+ 0x08, 0x89, 0x52, 0x54, 0xA3, 0x14, 0xFD, 0x3D, 0x52, 0x4C, 0x0D, 0x57, 0x34, 0xFB, 0x14, 0xC3,
+ 0xF0, 0x98, 0x65, 0x5A, 0xA8, 0x9A, 0x4F, 0xF8, 0xB3, 0xA5, 0xCF, 0xF6, 0x5E, 0x2E, 0x4E, 0x44,
+ 0xD8, 0xE7, 0xD9, 0x5E, 0xA1, 0x73, 0xF9, 0x27, 0xBB, 0x47, 0xF2, 0x3F, 0xF2, 0x67, 0xC5, 0xAA,
+ 0x09, 0xB1, 0xEE, 0x4C, 0xCE, 0xCB, 0x1E, 0x9A, 0x9F, 0x97, 0x95, 0x33, 0xCD, 0xD1, 0x3E, 0x01,
+ 0x72, 0x15, 0xC4, 0x94, 0xA1, 0x55, 0x10, 0xC4, 0x72, 0x4B, 0x72, 0xAF, 0xB2, 0x6E, 0x7E, 0x13,
+ 0xD2, 0x69, 0xEB, 0x33, 0x8B, 0xDB, 0x2E, 0x93, 0x18, 0x81, 0x5D, 0x25, 0xC4, 0x9E, 0xD8, 0x4A,
+ 0x24, 0x58, 0x32, 0xD6, 0x2F, 0xC6, 0x62, 0x4D, 0xD6, 0xC0, 0x58, 0xF7, 0x67, 0xC7, 0x1E, 0xCC,
+ 0xBE, 0xC9, 0x6F, 0xDA, 0xDD, 0xCD, 0x1F, 0x21, 0x79, 0x52, 0xE5, 0x3C, 0xDC, 0xBF, 0x00, 0x39,
+ 0x1F, 0x4B, 0xDB, 0xFA, 0x46, 0x57, 0x86, 0xB6, 0x95, 0x4D, 0xC6, 0x33, 0x2D, 0x34, 0x5A, 0x6D,
+ 0x40, 0x5F, 0xBC, 0xB6, 0xD1, 0x6C, 0xF3, 0xF8, 0xC2, 0xF6, 0x96, 0x54, 0x66, 0xCE, 0xE8, 0x14,
+ 0x6F, 0xE4, 0xB5, 0xC9, 0x36, 0xEE, 0x55, 0x0C, 0x98, 0xA5, 0x26, 0x10, 0x13, 0x9B, 0x76, 0x49,
+ 0x53, 0x68, 0x75, 0xFE, 0xD4, 0x1B, 0xBC, 0x48, 0x14, 0xF1, 0xA1, 0xBD, 0x72, 0x0C, 0x93, 0x0D,
+ 0x6D, 0x34, 0xB9, 0xA7, 0x65, 0x2C, 0xD6, 0xEE, 0xCA, 0x47, 0x25, 0xA3, 0xE1, 0xF9, 0x16, 0x74,
+ 0x46, 0xA2, 0xDE, 0x49, 0x4B, 0x4F, 0x46, 0x52, 0x5B, 0x06, 0x7D, 0xCE, 0x68, 0xBA, 0xAE, 0xA5,
+ 0x64, 0x30, 0x9F, 0x2B, 0x3D, 0x13, 0xB2, 0x8B, 0x46, 0xB4, 0x1D, 0x50, 0x39, 0xA1, 0x07, 0xFD,
+ 0x81, 0xB2, 0xF2, 0x89, 0x7B, 0x7D, 0x1F, 0xBF, 0x74, 0xEA, 0x94, 0x65, 0xC2, 0x9D, 0xDE, 0x33,
+ 0x31, 0x0D, 0x55, 0x3E, 0xB1, 0x67, 0x52, 0xD9, 0xA2, 0xB6, 0xD1, 0x0B, 0xCA, 0x27, 0xED, 0xF5,
+ 0xFD, 0xA2, 0xA9, 0xAB, 0x57, 0x4D, 0x83, 0x13, 0xE7, 0xAC, 0xB0, 0x34, 0xBD, 0xC2, 0x1E, 0x82,
+ 0xE5, 0xD2, 0xDE, 0x67, 0x47, 0xE7, 0xAC, 0x0C, 0xB0, 0xC2, 0xBC, 0x83, 0x60, 0x82, 0xC3, 0x24,
+ 0x48, 0x9B, 0x0D, 0xEC, 0x73, 0xDA, 0x4A, 0x62, 0x3F, 0xA7, 0xAD, 0xB0, 0xD5, 0xA5, 0xC7, 0xAD,
+ 0x14, 0x6D, 0x86, 0x4D, 0x45, 0x9B, 0x61, 0x17, 0xBD, 0x28, 0x6D, 0x89, 0x68, 0xD5, 0xCC, 0xA6,
+ 0xE2, 0xAA, 0x19, 0x39, 0xFA, 0x3C, 0x8C, 0xED, 0x71, 0x01, 0x6A, 0x7F, 0x28, 0xFC, 0x96, 0x62,
+ 0xF8, 0x0B, 0xE9, 0x27, 0x25, 0xFE, 0x43, 0xAB, 0x6F, 0x66, 0x46, 0xE1, 0xC5, 0xEE, 0x85, 0x2C,
+ 0x2E, 0xFD, 0x47, 0x45, 0xF1, 0xE4, 0xFE, 0x4B, 0xFA, 0x33, 0xF8, 0x77, 0xC9, 0x74, 0x47, 0xE5,
+ 0xD3, 0xF5, 0x65, 0x3C, 0xEF, 0xC3, 0x7F, 0xA2, 0x8C, 0x7F, 0x54, 0x3E, 0x7E, 0xB5, 0x18, 0x8F,
+ 0x51, 0x12, 0xCF, 0x4C, 0xD8, 0x38, 0x43, 0x36, 0xCF, 0xA9, 0x45, 0x9B, 0xE7, 0x02, 0xBA, 0xB5,
+ 0xC4, 0x7F, 0x53, 0xD1, 0x7F, 0x97, 0xC8, 0xCF, 0xFF, 0x7B, 0x3B, 0xD9, 0x27, 0x9D, 0x99, 0xD1,
+ 0xD8, 0x4F, 0xDE, 0x4A, 0x3D, 0xB5, 0x68, 0xA5, 0x5E, 0x40, 0xEF, 0x95, 0xF8, 0x6F, 0x2A, 0xFA,
+ 0x8B, 0x31, 0xA1, 0xE1, 0xC2, 0xCF, 0x64, 0x2D, 0xB2, 0x5D, 0x89, 0x35, 0x23, 0xAF, 0xB0, 0xBF,
+ 0x83, 0x9D, 0x7A, 0xFA, 0xE2, 0x0E, 0xD9, 0x86, 0xC6, 0x53, 0x5C, 0xE5, 0xE9, 0x01, 0xF1, 0x48,
+ 0x95, 0x53, 0xDC, 0x8A, 0x4D, 0x8C, 0x1B, 0xAF, 0xE9, 0xCD, 0x4B, 0xFA, 0xD0, 0x76, 0x09, 0x19,
+ 0x27, 0x7A, 0xAE, 0x6A, 0xDF, 0x87, 0xFB, 0x5E, 0xD9, 0xF7, 0x69, 0xBE, 0xFE, 0x61, 0x1E, 0xDF,
+ 0x13, 0x39, 0xE0, 0xA3, 0x59, 0x23, 0xBF, 0x16, 0xE2, 0x37, 0xAA, 0xCF, 0x88, 0x91, 0xAA, 0xC9,
+ 0x9D, 0x07, 0x44, 0x8B, 0x1E, 0x84, 0x4D, 0x27, 0xDA, 0x94, 0xD5, 0x2B, 0x2D, 0x79, 0xCE, 0xFA,
+ 0x76, 0x47, 0xC9, 0x46, 0xFB, 0x0D, 0xC8, 0x11, 0x25, 0xD6, 0x18, 0x0D, 0x1F, 0xF1, 0xD1, 0x2B,
+ 0x57, 0x6E, 0x43, 0x6C, 0x4F, 0xF3, 0x2E, 0x36, 0x87, 0xDF, 0x02, 0xEE, 0xEB, 0xE9, 0x0B, 0xC8,
+ 0xD3, 0xED, 0x30, 0xA6, 0x1F, 0xE5, 0x85, 0x47, 0xC5, 0xBC, 0x81, 0x98, 0x64, 0x41, 0x4B, 0x4C,
+ 0x14, 0x9E, 0x28, 0xBA, 0x6A, 0x8E, 0x38, 0x35, 0xB2, 0x64, 0xAF, 0x15, 0xC1, 0x86, 0xB6, 0xDC,
+ 0xDA, 0x53, 0xC8, 0x76, 0x71, 0xE5, 0x9D, 0x9C, 0x32, 0x63, 0x73, 0x32, 0x99, 0xEA, 0xD6, 0xD4,
+ 0x39, 0x61, 0xA2, 0xDC, 0xAD, 0xA8, 0x68, 0x6F, 0x4C, 0x9D, 0x56, 0x11, 0xAF, 0x74, 0xF9, 0x95,
+ 0x0D, 0x0D, 0xCD, 0xAD, 0xA9, 0x64, 0xB2, 0xB3, 0xA7, 0x15, 0x26, 0x12, 0xCB, 0x3D, 0xC5, 0xE7,
+ 0xB1, 0xD9, 0x7C, 0x07, 0x7A, 0x51, 0x6D, 0x7D, 0xE8, 0x57, 0x39, 0x1D, 0xAA, 0x61, 0x99, 0xBB,
+ 0x06, 0x24, 0x33, 0xAC, 0x13, 0xA5, 0x48, 0x6C, 0x2D, 0xC8, 0xB7, 0x37, 0x7A, 0x6E, 0x1A, 0x09,
+ 0xFA, 0x62, 0x01, 0xA2, 0x3C, 0xF0, 0xAE, 0x21, 0xB2, 0x24, 0xD9, 0x6C, 0x2F, 0x3B, 0x62, 0x4D,
+ 0x75, 0xDD, 0x96, 0x44, 0x7D, 0x7C, 0x70, 0xC2, 0xE8, 0x95, 0x7C, 0xC7, 0xD8, 0xCA, 0x18, 0xD7,
+ 0xE6, 0x70, 0xF9, 0x74, 0xEA, 0xCB, 0xBC, 0x0E, 0x2C, 0x78, 0x3B, 0xD9, 0xD4, 0xDC, 0xA7, 0xAB,
+ 0x64, 0xAC, 0x54, 0xEC, 0x01, 0xD9, 0x52, 0xD7, 0x8B, 0xC8, 0x35, 0x18, 0xBE, 0x12, 0x52, 0x7E,
+ 0x5E, 0x5C, 0xCD, 0x2F, 0x23, 0x6C, 0x89, 0xB6, 0x8B, 0x60, 0xDD, 0xC9, 0x55, 0x53, 0xCF, 0x1A,
+ 0x51, 0xDF, 0xB6, 0x7E, 0xD4, 0xFC, 0x80, 0x5F, 0x39, 0x75, 0xC6, 0xE9, 0xD9, 0x03, 0x9A, 0x9B,
+ 0x20, 0x83, 0x2B, 0x78, 0x1B, 0xBD, 0x28, 0x67, 0xA8, 0xC0, 0x03, 0xD6, 0x30, 0x3C, 0xD0, 0xDD,
+ 0x5B, 0x98, 0x94, 0x2A, 0xE5, 0x01, 0xB7, 0x94, 0x07, 0x78, 0x26, 0xFD, 0xE2, 0xF0, 0xD3, 0x4E,
+ 0x82, 0x73, 0x6D, 0x9E, 0x64, 0x36, 0x7F, 0x49, 0xCE, 0x7F, 0x76, 0xF5, 0xF9, 0xFE, 0x46, 0x33,
+ 0xB5, 0x51, 0x89, 0xE6, 0x3A, 0x4B, 0xD3, 0xD9, 0x20, 0x27, 0xF6, 0xDD, 0x92, 0x6D, 0xEC, 0xC5,
+ 0x6A, 0xC8, 0x7F, 0x31, 0xC7, 0xC9, 0xEC, 0xF9, 0x3D, 0xDD, 0x0B, 0xE6, 0xF7, 0x4C, 0x98, 0x37,
+ 0xAF, 0xAC, 0xB6, 0x3A, 0x2B, 0x1C, 0x3F, 0xA9, 0x63, 0xDC, 0xF8, 0xF6, 0xAE, 0xCE, 0xCE, 0xAE,
+ 0xDA, 0x86, 0xC6, 0x1A, 0x38, 0x21, 0x63, 0x9F, 0xE4, 0x5A, 0xE9, 0x35, 0x00, 0x4B, 0xD0, 0xDC,
+ 0xBE, 0xCA, 0x40, 0x0D, 0xC9, 0xF3, 0x8C, 0x64, 0x88, 0x8E, 0xD8, 0x03, 0x8A, 0x27, 0x72, 0x47,
+ 0x81, 0x4F, 0xCA, 0x5F, 0x8B, 0xA7, 0x74, 0x45, 0xFD, 0xDA, 0xFC, 0xCA, 0x6F, 0x51, 0xAE, 0x47,
+ 0xE4, 0xF7, 0x5B, 0x8E, 0x0A, 0x36, 0x89, 0x82, 0xCD, 0x44, 0xEB, 0xCF, 0xD2, 0x85, 0xE5, 0x67,
+ 0x5D, 0x7A, 0x67, 0xFA, 0xB5, 0xD9, 0x1D, 0x33, 0xBB, 0x6B, 0xEA, 0xBB, 0xC2, 0x74, 0x4B, 0x63,
+ 0x7B, 0x47, 0x45, 0xE3, 0xF8, 0x33, 0x1B, 0xC6, 0xB5, 0x57, 0x54, 0x04, 0x6E, 0x90, 0x3A, 0x7A,
+ 0x4A, 0x8B, 0x18, 0xA3, 0x79, 0x8C, 0x27, 0xD8, 0xC9, 0x72, 0x16, 0x51, 0xA7, 0x8E, 0x3E, 0x5F,
+ 0xE3, 0xE8, 0xC9, 0x0A, 0x4D, 0xA6, 0x2B, 0x4F, 0xE6, 0xCD, 0x18, 0xC6, 0xBE, 0x98, 0x3F, 0xCF,
+ 0xB4, 0xE4, 0x74, 0xCF, 0x8E, 0xFC, 0xA9, 0xA6, 0x50, 0x16, 0xA2, 0x8D, 0xB1, 0x93, 0xEF, 0x6E,
+ 0xFD, 0xE6, 0xF1, 0x70, 0xFC, 0xB4, 0xA3, 0x2A, 0xB7, 0xF7, 0xC0, 0x91, 0xEC, 0x6D, 0xB5, 0xD2,
+ 0x5F, 0x80, 0x31, 0x14, 0xCF, 0xA7, 0x99, 0xE4, 0xF3, 0xF0, 0x92, 0xB8, 0xFF, 0x00, 0x77, 0xB8,
+ 0x6A, 0x1A, 0xC4, 0xFF, 0x9A, 0x47, 0x55, 0x14, 0xF8, 0xE8, 0x94, 0x2E, 0x81, 0x24, 0x5F, 0xB4,
+ 0x62, 0xDF, 0x7B, 0x59, 0xAA, 0x3D, 0x7F, 0x19, 0xD9, 0xD9, 0x3A, 0x75, 0x46, 0xC7, 0xD8, 0xB6,
+ 0xC6, 0xB2, 0x54, 0x55, 0xD9, 0xDD, 0xD9, 0x8E, 0xD6, 0xC6, 0xB9, 0xD9, 0xD6, 0xB1, 0xD5, 0xC9,
+ 0xA6, 0x70, 0xEF, 0xF6, 0xAA, 0xA0, 0xFE, 0x62, 0x4A, 0x5E, 0x11, 0x0F, 0x46, 0x8A, 0xD8, 0x12,
+ 0x27, 0x9D, 0xCA, 0x1E, 0xF7, 0xC6, 0x42, 0x7B, 0x55, 0xE4, 0xC1, 0xA4, 0x2D, 0x5D, 0x99, 0x1E,
+ 0xD6, 0xB8, 0xE6, 0xE8, 0x23, 0x4E, 0x68, 0xEF, 0xE0, 0xD7, 0x6E, 0xDB, 0xB6, 0x64, 0x09, 0x15,
+ 0xAF, 0xF5, 0x7B, 0xB9, 0x7F, 0x46, 0x67, 0x60, 0xF3, 0x2F, 0x13, 0xC5, 0xAA, 0x89, 0x54, 0xA8,
+ 0x6E, 0xBD, 0x0C, 0x6E, 0x11, 0xDC, 0xE3, 0x44, 0x06, 0xDE, 0xCD, 0xF3, 0x89, 0xAC, 0x18, 0xDC,
+ 0x46, 0xB8, 0xD7, 0x89, 0xEC, 0x59, 0x70, 0x57, 0x12, 0x39, 0x87, 0xC0, 0x3D, 0x49, 0xE4, 0x7E,
+ 0x0F, 0x7D, 0xEF, 0x55, 0x44, 0x3E, 0xFA, 0xEB, 0x41, 0x00, 0xB7, 0x8B, 0x28, 0xBC, 0x88, 0x28,
+ 0x7E, 0x23, 0x51, 0xE2, 0x68, 0x02, 0x45, 0xC0, 0xFD, 0x82, 0x28, 0x55, 0x0F, 0x77, 0x1A, 0xDC,
+ 0x9B, 0x44, 0xE9, 0x47, 0x89, 0x32, 0xAF, 0x12, 0x95, 0xDD, 0x4D, 0x54, 0xFE, 0x0C, 0x51, 0xF6,
+ 0x62, 0xA2, 0x8A, 0xB3, 0x88, 0x2A, 0xF1, 0xB9, 0xEA, 0x5D, 0xA2, 0xEA, 0x47, 0xC4, 0xB1, 0xDC,
+ 0x70, 0x9F, 0x10, 0xD5, 0x21, 0x9E, 0x7A, 0x84, 0x6D, 0xB8, 0x8E, 0xA8, 0x11, 0xAE, 0xE9, 0x69,
+ 0xA2, 0xE6, 0xAB, 0x89, 0x5A, 0x8E, 0x23, 0x6A, 0x5D, 0x4B, 0x34, 0x02, 0x71, 0xB6, 0x6D, 0x21,
+ 0x1A, 0x89, 0x84, 0x46, 0x3E, 0x44, 0x34, 0xEA, 0x0C, 0xA2, 0xD1, 0x37, 0x83, 0x3C, 0xEF, 0x24,
+ 0x1A, 0x7B, 0x19, 0x51, 0x3B, 0xF2, 0xDF, 0x01, 0x2B, 0xA7, 0x03, 0xF9, 0xEC, 0x5C, 0x08, 0xF7,
+ 0x2B, 0xB8, 0xF7, 0x88, 0xBA, 0x9A, 0x89, 0xC6, 0x21, 0x6F, 0xE3, 0x90, 0x9F, 0xF1, 0xDF, 0x26,
+ 0xEA, 0x46, 0x5C, 0x90, 0x4A, 0x9A, 0x00, 0xCF, 0x89, 0x28, 0x9B, 0x49, 0xCB, 0x88, 0x26, 0xA7,
+ 0xE1, 0x90, 0xDE, 0x54, 0x84, 0x9B, 0x8A, 0xFB, 0xA7, 0xDD, 0x4A, 0xD4, 0x8B, 0xF4, 0xFA, 0xD0,
+ 0xDC, 0xA6, 0xE3, 0xB7, 0xE9, 0x2F, 0x13, 0xCD, 0xB8, 0x9D, 0x68, 0xE6, 0x4E, 0xA2, 0x59, 0xC0,
+ 0x3C, 0xFB, 0x42, 0xA2, 0x39, 0xD0, 0x20, 0x73, 0x91, 0xDE, 0x3C, 0x94, 0xCF, 0x7C, 0x68, 0x4B,
+ 0x31, 0x91, 0xB5, 0x00, 0x61, 0x16, 0x7C, 0x40, 0x74, 0x00, 0xB0, 0x2C, 0x6C, 0x87, 0x43, 0x99,
+ 0x2F, 0x1C, 0x24, 0x3A, 0xF0, 0x12, 0xB8, 0x9B, 0xE0, 0x1E, 0x80, 0x7B, 0x09, 0xEE, 0x63, 0xA2,
+ 0x45, 0x0E, 0x1C, 0x9A, 0xD3, 0x22, 0xC4, 0xB1, 0x08, 0x71, 0x2C, 0x42, 0x19, 0x2E, 0x3A, 0x87,
+ 0xE8, 0x20, 0x84, 0x3B, 0xE8, 0x7E, 0xB8, 0x17, 0xE1, 0x3E, 0x24, 0xEA, 0xB7, 0xE0, 0x6A, 0xE1,
+ 0xBA, 0xE1, 0x80, 0xAB, 0x1F, 0xF9, 0xEF, 0x47, 0x3D, 0xF6, 0x23, 0x1F, 0xFD, 0xC8, 0x4F, 0xFF,
+ 0xBD, 0x70, 0x28, 0xCF, 0xFE, 0xB7, 0x89, 0x16, 0xA3, 0xFE, 0x16, 0x57, 0xC2, 0x01, 0xDB, 0xE2,
+ 0x85, 0x43, 0xEE, 0x60, 0x60, 0x59, 0x82, 0x78, 0x0F, 0x41, 0x7C, 0x4B, 0xA7, 0x45, 0x6E, 0x19,
+ 0xEA, 0x64, 0x39, 0xF2, 0xBB, 0x02, 0x79, 0x5B, 0x79, 0x0A, 0x1C, 0xEA, 0x6E, 0x15, 0xE4, 0x60,
+ 0xD5, 0x06, 0xA2, 0x43, 0x91, 0xB7, 0x01, 0x7C, 0x1E, 0x58, 0x9F, 0x77, 0xF8, 0x7D, 0x00, 0x65,
+ 0x77, 0x18, 0xF2, 0xB4, 0x06, 0x75, 0x35, 0x88, 0xFA, 0x1F, 0x04, 0xE6, 0xC1, 0x3E, 0x38, 0xC8,
+ 0xD6, 0xE1, 0x71, 0x38, 0x94, 0xEF, 0xDA, 0x73, 0xE1, 0xAE, 0x80, 0xC8, 0x41, 0xC6, 0xBE, 0xB0,
+ 0x95, 0x68, 0x03, 0xAC, 0xD1, 0x0D, 0x1F, 0x47, 0xEE, 0x88, 0x0D, 0x79, 0x87, 0xBC, 0x1C, 0x71,
+ 0xC9, 0x90, 0x3B, 0xF2, 0xC8, 0xBC, 0x43, 0x7D, 0x1E, 0x79, 0x29, 0x1C, 0xEA, 0x74, 0x23, 0xD2,
+ 0x3C, 0x0A, 0xB2, 0x71, 0xB4, 0x01, 0x37, 0x07, 0xEE, 0x33, 0xA2, 0x4D, 0xC8, 0xDF, 0x31, 0x48,
+ 0xF3, 0x98, 0x6F, 0xE7, 0x1D, 0x64, 0xEB, 0x18, 0xC8, 0xCB, 0x31, 0x9F, 0x12, 0x1D, 0x8B, 0x30,
+ 0xC7, 0xF5, 0xEF, 0xC7, 0xDD, 0xBA, 0x97, 0xC3, 0x7D, 0xC7, 0xE1, 0xBE, 0xE3, 0x70, 0xDF, 0x66,
+ 0xC8, 0xD2, 0x66, 0xD4, 0xF1, 0x66, 0xC8, 0xFA, 0x66, 0xE4, 0x75, 0x33, 0xF2, 0xBC, 0x19, 0x72,
+ 0xB4, 0xF9, 0x1A, 0x38, 0xC8, 0xD5, 0x66, 0x94, 0xD7, 0x16, 0x94, 0xC5, 0x16, 0xD4, 0xD3, 0x16,
+ 0xD4, 0xD3, 0x16, 0xD4, 0xD3, 0x16, 0x94, 0xF5, 0xF1, 0xF8, 0x7E, 0x3C, 0x64, 0x73, 0x2B, 0xBA,
+ 0x9F, 0x5B, 0xD1, 0x1E, 0xB6, 0xA2, 0x1C, 0x4F, 0x98, 0x04, 0x77, 0x1F, 0xD1, 0x89, 0xC0, 0x7E,
+ 0xE2, 0x76, 0x71, 0x9E, 0xFD, 0xFF, 0x17, 0x97, 0x3F, 0x13, 0x3E, 0x37, 0x41, 0x36, 0xB8, 0x7D,
+ 0xAF, 0xFF, 0x2D, 0xD7, 0x15, 0x0B, 0xBB, 0x1C, 0xCA, 0x1F, 0xAC, 0xA2, 0x93, 0x41, 0x26, 0x59,
+ 0xD0, 0x15, 0x0E, 0x2C, 0x53, 0x0F, 0xF6, 0x59, 0x00, 0x4E, 0x8B, 0x83, 0xBB, 0x93, 0x94, 0x82,
+ 0x9D, 0x92, 0xA1, 0x32, 0x2A, 0xA7, 0x2C, 0x55, 0x50, 0x25, 0x55, 0x51, 0x35, 0xD5, 0x50, 0x2D,
+ 0xD5, 0x51, 0x3D, 0x2C, 0xAC, 0x46, 0x6A, 0x12, 0x33, 0x26, 0xB0, 0x04, 0x47, 0x50, 0x1B, 0x7A,
+ 0x89, 0xA3, 0x68, 0x34, 0x8D, 0x81, 0x95, 0xDF, 0x4E, 0x1D, 0xB0, 0x9E, 0xBA, 0xD0, 0xA8, 0xC6,
+ 0x53, 0x37, 0xF5, 0xC0, 0x02, 0x9A, 0x48, 0x93, 0x68, 0x32, 0x7A, 0x18, 0x53, 0x69, 0x1A, 0xF5,
+ 0x52, 0x1F, 0x4D, 0xA7, 0x19, 0xB0, 0xAC, 0x66, 0xD1, 0x6C, 0x9A, 0x43, 0x73, 0x69, 0x1E, 0xCD,
+ 0x47, 0xE3, 0x38, 0x80, 0x16, 0xD2, 0x81, 0xB4, 0x88, 0x0E, 0xA2, 0x7E, 0x5A, 0x4C, 0x07, 0xD3,
+ 0x12, 0x3A, 0x84, 0x96, 0xD2, 0x32, 0x5A, 0x4E, 0x2B, 0x68, 0x25, 0xAD, 0xA2, 0xD5, 0xE8, 0xB9,
+ 0x0C, 0xD0, 0x61, 0xB4, 0x86, 0x06, 0xE9, 0x70, 0x5A, 0x4B, 0xEB, 0xC0, 0x5B, 0x5F, 0xA0, 0x0D,
+ 0x74, 0x04, 0x1D, 0x49, 0x5F, 0xA4, 0x8D, 0x74, 0x14, 0x1D, 0x4D, 0x9B, 0xE8, 0x18, 0x3A, 0x96,
+ 0x8E, 0xA3, 0xCD, 0xB4, 0x85, 0x8E, 0xA7, 0xAD, 0x74, 0x02, 0x9D, 0x48, 0x5F, 0xA2, 0x6D, 0x74,
+ 0x12, 0x7D, 0x99, 0x4E, 0xA6, 0x53, 0xE8, 0x54, 0x3A, 0x8D, 0x4E, 0xA7, 0x33, 0xE8, 0x4C, 0x3A,
+ 0x8B, 0xCE, 0xA6, 0x73, 0xE8, 0x2B, 0xB4, 0x9D, 0xBE, 0x4A, 0xE7, 0xD2, 0xD7, 0xE8, 0x3C, 0xFA,
+ 0x3A, 0x9D, 0x0F, 0xAB, 0xEE, 0x42, 0xFA, 0x06, 0x5D, 0x44, 0xDF, 0xA4, 0x8B, 0xE9, 0x5B, 0xF4,
+ 0x6D, 0xFA, 0x0E, 0x5D, 0x42, 0xDF, 0xA5, 0x4B, 0x69, 0x07, 0x7D, 0x0F, 0xFD, 0xA5, 0xCB, 0xE8,
+ 0x72, 0xBA, 0x82, 0xFE, 0x83, 0xAE, 0xA4, 0xAB, 0xE8, 0x6A, 0xFA, 0x01, 0x5D, 0x43, 0xD7, 0xD2,
+ 0x75, 0x74, 0x3D, 0xED, 0xA4, 0x1F, 0xD2, 0x8F, 0xE8, 0x06, 0xBA, 0x91, 0x7E, 0x4C, 0x37, 0xC1,
+ 0x4E, 0xBE, 0x99, 0x6E, 0xA1, 0x9F, 0xD2, 0xCF, 0x60, 0xA1, 0xFE, 0x9C, 0x7E, 0x41, 0xBF, 0xA4,
+ 0x5F, 0x81, 0xAD, 0x6F, 0xA7, 0x3B, 0xE8, 0x4E, 0xBA, 0x8B, 0xEE, 0xA6, 0x7B, 0xE8, 0x5E, 0xFA,
+ 0x35, 0xDD, 0x47, 0xBF, 0xA1, 0xFB, 0xE9, 0xB7, 0xF4, 0x00, 0x3D, 0x08, 0xBB, 0xF0, 0x61, 0x7A,
+ 0x84, 0x7E, 0x47, 0x8F, 0xD2, 0x63, 0xF4, 0x38, 0x3D, 0x41, 0x4F, 0xD2, 0x53, 0xF4, 0x7B, 0xFA,
+ 0x03, 0x3D, 0x4D, 0x7F, 0xA4, 0x67, 0xE8, 0x59, 0xD8, 0xB4, 0xCF, 0xD1, 0xF3, 0xF4, 0x02, 0x2C,
+ 0xF9, 0x3F, 0xD1, 0x4B, 0xF4, 0x67, 0x7A, 0x99, 0xFE, 0x13, 0x9A, 0xE7, 0x15, 0x7A, 0x95, 0xFE,
+ 0x17, 0xFD, 0x15, 0x7A, 0xF6, 0x75, 0x7A, 0x03, 0x56, 0xEA, 0x5B, 0xF4, 0x36, 0xBD, 0x43, 0xEF,
+ 0xD2, 0xDF, 0x60, 0x99, 0xBE, 0x4F, 0x1F, 0xD0, 0xDF, 0xE9, 0x43, 0xF4, 0xE5, 0x3E, 0x96, 0x63,
+ 0xA6, 0xF2, 0x62, 0x8D, 0xF9, 0x13, 0xF9, 0x2B, 0xC5, 0x29, 0xFA, 0xF2, 0x3B, 0xB4, 0x09, 0x15,
+ 0x8F, 0xFB, 0x67, 0x8A, 0x5C, 0xA9, 0xBB, 0xE7, 0x85, 0x1F, 0x95, 0x98, 0xAA, 0xE9, 0x06, 0xBA,
+ 0xC3, 0xE2, 0x88, 0x8D, 0x20, 0x8C, 0x27, 0x92, 0xA9, 0x74, 0xA6, 0xAC, 0x3C, 0x5B, 0x51, 0x59,
+ 0x55, 0x5D, 0x53, 0x2B, 0x36, 0x17, 0x6E, 0x6A, 0x6E, 0x69, 0x1D, 0xD1, 0x36, 0x72, 0xD4, 0xE8,
+ 0x31, 0x63, 0xDB, 0x3B, 0x3A, 0xBB, 0xC6, 0x8D, 0xEF, 0xEE, 0x99, 0x30, 0x71, 0xD2, 0xE4, 0x29,
+ 0x53, 0xA7, 0xF5, 0xF6, 0x4D, 0x9F, 0x31, 0x73, 0xD6, 0xEC, 0x39, 0x73, 0xE7, 0xCD, 0x5F, 0x70,
+ 0xC0, 0xC2, 0x03, 0x17, 0x1D, 0xD4, 0xBF, 0xF8, 0xE0, 0x25, 0x87, 0x2C, 0x5D, 0xB6, 0x7C, 0xC5,
+ 0xCA, 0x55, 0xAB, 0x0F, 0x1D, 0x38, 0x6C, 0xCD, 0x20, 0xAA, 0x72, 0xDD, 0xFA, 0x2F, 0x6C, 0x38,
+ 0xE2, 0xC8, 0x2F, 0x6E, 0x3C, 0xEA, 0xE8, 0x4D, 0xC7, 0x1C, 0x7B, 0xDC, 0xE6, 0x2D, 0xC7, 0x6F,
+ 0x3D, 0xE1, 0xC4, 0x2F, 0x6D, 0x3B, 0xE9, 0xCB, 0x27, 0x9F, 0x72, 0xEA, 0x69, 0xA7, 0x9F, 0x71,
+ 0xE6, 0x59, 0x67, 0x9F, 0xF3, 0x95, 0xED, 0x5F, 0x3D, 0xF7, 0x6B, 0xE7, 0x7D, 0xFD, 0xFC, 0x0B,
+ 0x2E, 0xFC, 0xC6, 0x45, 0xDF, 0xBC, 0xF8, 0x5B, 0xDF, 0xFE, 0xCE, 0x25, 0xDF, 0xBD, 0x74, 0xC7,
+ 0xF7, 0xBE, 0x7F, 0xD9, 0xE5, 0x57, 0xFC, 0xC7, 0x95, 0x57, 0x5D, 0xFD, 0x83, 0x6B, 0xAE, 0xBD,
+ 0xEE, 0xFA, 0x9D, 0x3F, 0xFC, 0xD1, 0x0D, 0x37, 0xFE, 0xF8, 0xA6, 0x9F, 0xDC, 0x7C, 0xCB, 0x4F,
+ 0x7F, 0x76, 0xEB, 0xCF, 0x7F, 0xF1, 0xCB, 0x5F, 0xDD, 0x76, 0xFB, 0x1D, 0x77, 0xDE, 0x75, 0xF7,
+ 0x3D, 0xF7, 0xFE, 0xFA, 0xBE, 0xDF, 0xDC, 0xFF, 0xDB, 0x07, 0x1E, 0x7C, 0xE8, 0xE1, 0x47, 0x7E,
+ 0xF7, 0xE8, 0x63, 0x8F, 0x3F, 0xF1, 0xE4, 0x53, 0xBF, 0xFF, 0xC3, 0xD3, 0x7F, 0x7C, 0xE6, 0xD9,
+ 0x5D, 0xCF, 0x3D, 0xFF, 0xC2, 0x8B, 0x7F, 0x7A, 0xE9, 0xCF, 0x2F, 0xFF, 0x27, 0x50, 0xC6, 0x14,
+ 0x61, 0xA5, 0x2F, 0x82, 0x64, 0xAA, 0x68, 0x01, 0xA7, 0x51, 0x8E, 0x75, 0xB0, 0x19, 0x6C, 0x09,
+ 0x3B, 0x9C, 0x6D, 0x63, 0xA7, 0xB2, 0xD3, 0xD9, 0xC5, 0xFC, 0x76, 0xFE, 0x20, 0x7F, 0x98, 0xBF,
+ 0xA8, 0xFC, 0x40, 0xF9, 0xA1, 0x72, 0x6F, 0x6D, 0xB2, 0x36, 0x5B, 0x5B, 0x5D, 0x5B, 0x5F, 0xDB,
+ 0x5C, 0xDB, 0x5E, 0x3B, 0xA9, 0xAE, 0xBE, 0x9E, 0xD7, 0x6B, 0xF5, 0x7E, 0x7D, 0xBC, 0x3E, 0x55,
+ 0x9F, 0xAD, 0xAF, 0xAE, 0x1F, 0x59, 0x3F, 0xAF, 0x7E, 0xB0, 0xFE, 0x0B, 0x4D, 0x8F, 0xBC, 0xC7,
+ 0x45, 0x4F, 0x00, 0xED, 0xE5, 0x4A, 0xC4, 0x37, 0x9D, 0x1D, 0xCC, 0x06, 0x4B, 0xE2, 0x7B, 0x00,
+ 0xF1, 0xED, 0xCA, 0xC7, 0x97, 0xA8, 0x2D, 0xAB, 0xAD, 0xAC, 0xAD, 0x95, 0xF1, 0x4D, 0xDC, 0x23,
+ 0xBE, 0xF2, 0x62, 0x7C, 0xEB, 0x11, 0x9F, 0x1C, 0x2F, 0xFF, 0x6F, 0xBC, 0x18, 0x2D, 0x62, 0x16,
+ 0x7B, 0x08, 0x7F, 0x8F, 0xB0, 0x47, 0xD9, 0xE3, 0x25, 0x7F, 0x4F, 0xE2, 0x6F, 0x57, 0xFE, 0xEF,
+ 0x79, 0xF6, 0x12, 0xFB, 0x0B, 0xFB, 0xAB, 0xFC, 0x7B, 0x7D, 0x8F, 0xBF, 0x37, 0x8B, 0x7F, 0x6F,
+ 0xB3, 0xB7, 0xA5, 0xA4, 0xFD, 0x4F, 0xE6, 0x94, 0x1B, 0xD1, 0xA6, 0x05, 0x27, 0xDC, 0x85, 0x56,
+ 0xFF, 0x3A, 0x98, 0xE1, 0x42, 0x70, 0xC2, 0xE5, 0x68, 0xDB, 0xD7, 0x20, 0xF7, 0xE7, 0xA1, 0x05,
+ 0x9E, 0x0D, 0x4E, 0x10, 0xED, 0xEA, 0x02, 0x70, 0xC2, 0xB9, 0x68, 0xBF, 0x2F, 0xA1, 0x9D, 0x5D,
+ 0x81, 0x96, 0xFF, 0x11, 0x5A, 0xDA, 0xC7, 0x60, 0x84, 0x1F, 0xA3, 0x1D, 0x3F, 0x08, 0x06, 0x10,
+ 0xDC, 0x74, 0x11, 0xD8, 0xE9, 0x77, 0xE0, 0xA7, 0x87, 0xD0, 0xB2, 0x9F, 0xC8, 0xB7, 0xEC, 0x37,
+ 0xC0, 0x56, 0x7F, 0x90, 0xAD, 0xFB, 0x27, 0x60, 0xAD, 0xF7, 0xC0, 0x32, 0xCF, 0xC8, 0x36, 0x7E,
+ 0x24, 0xDA, 0xEE, 0x3B, 0xE0, 0xA0, 0x8D, 0xE0, 0xB1, 0xA3, 0xC0, 0x61, 0x47, 0x83, 0xC5, 0xAE,
+ 0x04, 0x8F, 0x6D, 0x06, 0x93, 0x09, 0x1E, 0x3B, 0x01, 0x4C, 0x26, 0x78, 0xEC, 0x4D, 0x30, 0xD9,
+ 0x97, 0xC1, 0x65, 0x27, 0x83, 0xC7, 0x4E, 0x01, 0xAB, 0x5C, 0x05, 0x2E, 0x3B, 0x4D, 0xB2, 0xD9,
+ 0xDB, 0x68, 0xF7, 0xB7, 0x83, 0x29, 0x04, 0x2B, 0x08, 0xF6, 0x11, 0xCC, 0x70, 0x87, 0xE4, 0x86,
+ 0xEB, 0xC0, 0x17, 0xBB, 0xC0, 0x17, 0x05, 0xFE, 0x78, 0x19, 0x5C, 0xB2, 0x03, 0x1C, 0xF6, 0x7D,
+ 0xC9, 0x50, 0x82, 0x9D, 0xEE, 0x97, 0x3C, 0xF5, 0x5B, 0x30, 0xE2, 0x7D, 0xE0, 0xC2, 0x1B, 0xC0,
+ 0x48, 0x82, 0xA3, 0xCE, 0x07, 0xEB, 0xED, 0x04, 0xEB, 0xFD, 0x0D, 0xAC, 0xF5, 0x1B, 0xF0, 0xDC,
+ 0xC5, 0x60, 0xBF, 0xEB, 0xC1, 0x8D, 0x82, 0x13, 0x6F, 0x03, 0x3B, 0x81, 0xC3, 0xCE, 0xFE, 0x54,
+ 0xA9, 0x95, 0xBD, 0x64, 0x62, 0xD9, 0xBC, 0x9B, 0x85, 0x7E, 0xEE, 0x77, 0x68, 0x0A, 0x9F, 0x47,
+ 0x0D, 0x7C, 0x1B, 0xD5, 0x2A, 0x4F, 0x53, 0x83, 0x62, 0xD0, 0x08, 0x30, 0xCB, 0x3C, 0x36, 0x92,
+ 0x26, 0xC2, 0x11, 0x3B, 0x09, 0xBF, 0xAD, 0xA0, 0xB1, 0xF0, 0x6B, 0x60, 0x37, 0xA3, 0xFE, 0x3F,
+ 0xA3, 0x91, 0x7C, 0x29, 0xE9, 0x3C, 0xA4, 0x14, 0x6B, 0x23, 0x83, 0xFD, 0x06, 0x5D, 0xDF, 0x23,
+ 0xD0, 0x47, 0x70, 0x29, 0xCE, 0xAE, 0x47, 0x27, 0xE8, 0x49, 0xCA, 0xB0, 0xAB, 0xE0, 0xFF, 0x30,
+ 0x25, 0xD8, 0x9F, 0x29, 0xCB, 0x12, 0x34, 0x19, 0x69, 0x75, 0x8B, 0x38, 0x0A, 0x8E, 0x7D, 0x44,
+ 0x29, 0xE4, 0xA5, 0x8A, 0xBF, 0x96, 0x7B, 0x83, 0x9F, 0x92, 0x7B, 0x86, 0x2F, 0xA3, 0x0A, 0xFE,
+ 0x10, 0xDE, 0x07, 0x73, 0x6F, 0xF1, 0x4E, 0xB8, 0x9D, 0xF8, 0xED, 0x43, 0x9A, 0xC3, 0xC6, 0x91,
+ 0xC7, 0x26, 0x21, 0xAD, 0x6B, 0xC8, 0xE3, 0xAD, 0xE4, 0xC5, 0x42, 0x6A, 0x53, 0x3A, 0x10, 0xEE,
+ 0x39, 0xAA, 0xE5, 0x13, 0x73, 0x7F, 0x42, 0x4F, 0xAF, 0x82, 0x5F, 0x8D, 0xF7, 0x8D, 0x54, 0xC3,
+ 0xAF, 0xCA, 0xBD, 0xA5, 0x34, 0x81, 0x10, 0xDF, 0xCF, 0xBD, 0x1A, 0x7B, 0x26, 0xF7, 0x2A, 0x7F,
+ 0x14, 0xF7, 0x7D, 0x80, 0xDF, 0x96, 0x53, 0x35, 0x9B, 0x4C, 0x03, 0xEC, 0x89, 0xDC, 0x5B, 0x6C,
+ 0x72, 0xEE, 0x1F, 0x48, 0xBF, 0x97, 0x37, 0xE4, 0xFE, 0xC1, 0xD2, 0xB9, 0x1C, 0x9F, 0x4D, 0x29,
+ 0x3E, 0x2E, 0xF7, 0x3A, 0xFB, 0x15, 0xDE, 0x27, 0x03, 0xEB, 0x7D, 0x54, 0xC9, 0x56, 0xE3, 0x9E,
+ 0xAD, 0x94, 0xE0, 0xEB, 0x72, 0xAF, 0xB0, 0x8A, 0xDC, 0x07, 0xC2, 0xF1, 0xF6, 0xDC, 0xEB, 0xF4,
+ 0x69, 0xEE, 0x55, 0xE5, 0x49, 0xE4, 0x4D, 0x8C, 0xB4, 0x7D, 0x8A, 0x74, 0x9B, 0x90, 0x46, 0x25,
+ 0xC2, 0x7F, 0x1D, 0xF1, 0xDE, 0x46, 0xF5, 0xAC, 0x0B, 0xBF, 0x6D, 0x40, 0xD8, 0x51, 0x08, 0xF7,
+ 0x2D, 0xFC, 0x36, 0x16, 0x79, 0x5E, 0x8A, 0xDF, 0xEE, 0x47, 0x98, 0xC9, 0x68, 0x63, 0x2F, 0x51,
+ 0xAF, 0x78, 0x47, 0xFA, 0xB3, 0x25, 0x6E, 0xE1, 0x04, 0x6E, 0x60, 0x2E, 0x60, 0x92, 0xF9, 0x17,
+ 0x79, 0x1A, 0xC6, 0x89, 0x3C, 0xCA, 0xFC, 0x95, 0xBA, 0x7C, 0xFE, 0xF2, 0xEE, 0xB5, 0x42, 0xDE,
+ 0xF6, 0x76, 0x32, 0x5F, 0x25, 0xAE, 0x48, 0x27, 0xF7, 0xE1, 0xB7, 0x33, 0x90, 0xA6, 0x9E, 0x7B,
+ 0x9C, 0x7D, 0x28, 0xD7, 0x79, 0x11, 0xBF, 0x39, 0xF7, 0x27, 0x51, 0x37, 0xD2, 0xBD, 0x44, 0xD3,
+ 0x58, 0x1A, 0x2D, 0xF9, 0xB3, 0xDC, 0xDB, 0x22, 0xAF, 0xB1, 0xC6, 0xDC, 0x2B, 0xC8, 0xA7, 0x27,
+ 0xF2, 0x22, 0xC3, 0x8A, 0x7A, 0x12, 0x65, 0xB5, 0x1A, 0x78, 0x45, 0xDC, 0x88, 0x43, 0x96, 0x1B,
+ 0xEA, 0x4C, 0x96, 0x1B, 0xEA, 0x85, 0x0D, 0x90, 0xCB, 0x3A, 0x28, 0x60, 0xDF, 0xA7, 0x7A, 0xA5,
+ 0x0C, 0x18, 0x3E, 0xCD, 0x7D, 0x82, 0xFA, 0xF7, 0x99, 0x68, 0xF9, 0x22, 0x8E, 0x4F, 0xC4, 0x0C,
+ 0x26, 0xA8, 0x48, 0x60, 0x2F, 0xC9, 0x9B, 0xE2, 0xD0, 0x48, 0x21, 0xA3, 0xC5, 0xB2, 0x42, 0xF9,
+ 0xC4, 0x5A, 0x51, 0x46, 0x69, 0x60, 0x12, 0x9D, 0x66, 0x58, 0x69, 0xEC, 0x19, 0xC8, 0xA4, 0x70,
+ 0x4A, 0xE4, 0xE4, 0x7D, 0x15, 0x90, 0x57, 0x94, 0xBB, 0xA8, 0x7B, 0xBA, 0x3E, 0xB7, 0x84, 0x6B,
+ 0x05, 0x7D, 0xBA, 0xD7, 0xF5, 0x99, 0x5C, 0x2D, 0x54, 0xBC, 0x64, 0x1A, 0x42, 0xFE, 0x0A, 0xEF,
+ 0x70, 0x52, 0xFE, 0xC6, 0xA1, 0xE7, 0x36, 0x2E, 0xF7, 0xBE, 0xF8, 0x2C, 0xE5, 0x4E, 0xD4, 0x93,
+ 0x48, 0x23, 0x5F, 0x5F, 0x05, 0x27, 0xEB, 0x20, 0x2D, 0xDD, 0xD3, 0x70, 0x4F, 0xC1, 0xBD, 0x8C,
+ 0x34, 0x56, 0xA0, 0xFC, 0x6C, 0x7C, 0xFE, 0x04, 0xEE, 0x31, 0x7C, 0xAE, 0x15, 0x61, 0xFE, 0x1B,
+ 0x35, 0x42, 0xC9, 0x05, 0x5D, 0x73, 0x15, 0xB0, 0x06, 0x60, 0xB6, 0x5B, 0xF9, 0xE3, 0xB9, 0xF7,
+ 0xD8, 0xA3, 0xA8, 0xD1, 0xEB, 0x69, 0x16, 0xBF, 0x0B, 0x8C, 0xF7, 0x29, 0xB8, 0xB5, 0x1F, 0x5C,
+ 0xBA, 0x1C, 0x1C, 0x3A, 0x08, 0xD6, 0x3A, 0x0E, 0x61, 0x4F, 0x03, 0xAB, 0xFD, 0x52, 0x19, 0x93,
+ 0x3B, 0x4B, 0xB2, 0x72, 0x2F, 0xDA, 0xE6, 0x3D, 0xD2, 0x36, 0xDB, 0x0C, 0x06, 0xDB, 0x06, 0xE6,
+ 0x58, 0x47, 0x1B, 0xF9, 0x15, 0xB9, 0xBF, 0xD1, 0x97, 0x94, 0x30, 0xF7, 0x35, 0xB0, 0xF6, 0x3C,
+ 0x70, 0xAB, 0x98, 0xAD, 0x3C, 0x86, 0x3F, 0xC2, 0xBE, 0x4B, 0x8F, 0xB1, 0x7F, 0x82, 0xBF, 0xAF,
+ 0xCE, 0xDD, 0x25, 0x38, 0x0F, 0x2C, 0xF1, 0x0F, 0xF2, 0x50, 0xE3, 0x73, 0x73, 0xEF, 0xA2, 0x6B,
+ 0x3D, 0x27, 0xF7, 0x3E, 0x38, 0x30, 0xB2, 0xD6, 0xCE, 0xE4, 0x0B, 0xA9, 0x3B, 0xF7, 0x08, 0x38,
+ 0x72, 0x0B, 0xB4, 0xC9, 0x8D, 0x7C, 0x55, 0xEE, 0x6D, 0xEA, 0xC9, 0x3D, 0x0C, 0x26, 0x5A, 0xC4,
+ 0x0F, 0x04, 0x47, 0x0B, 0x8B, 0x2C, 0x4E, 0xDF, 0x60, 0x6F, 0x91, 0xC1, 0x39, 0xBB, 0x14, 0x6C,
+ 0xFC, 0x20, 0x7F, 0x0A, 0x5A, 0xE0, 0x20, 0xF0, 0x58, 0x3D, 0xF8, 0x7F, 0x15, 0x18, 0x6C, 0x01,
+ 0x58, 0xFF, 0x2C, 0xFE, 0x7C, 0xEE, 0xC3, 0x02, 0xFF, 0xF3, 0x27, 0xC0, 0x7A, 0x3A, 0xBF, 0x0F,
+ 0xCC, 0xE8, 0xD0, 0xD1, 0xFC, 0x21, 0xC9, 0xB2, 0x36, 0xFF, 0x75, 0xEE, 0x23, 0x69, 0x53, 0x9E,
+ 0x08, 0xDE, 0x3D, 0x05, 0x08, 0x2E, 0x05, 0x33, 0xC2, 0x76, 0xE3, 0xF7, 0x83, 0x81, 0xCF, 0xC8,
+ 0x5B, 0x87, 0xDF, 0x07, 0x33, 0x7C, 0x17, 0xDC, 0x3D, 0x11, 0x1A, 0x6E, 0x1D, 0x1D, 0xCB, 0x77,
+ 0xF1, 0x67, 0x73, 0x75, 0xD0, 0x7E, 0x29, 0xFA, 0x02, 0xFF, 0x23, 0xF4, 0xC9, 0x04, 0xE8, 0xF9,
+ 0x57, 0xF8, 0xCB, 0xB4, 0x4C, 0xEE, 0x65, 0x3E, 0x1D, 0x9C, 0xBB, 0x9E, 0xFF, 0x2E, 0xD7, 0x03,
+ 0x4D, 0xB1, 0x16, 0xDA, 0x45, 0x68, 0xA5, 0xFB, 0x20, 0xF1, 0xBF, 0xCF, 0xBD, 0x03, 0x09, 0xB8,
+ 0x05, 0xDA, 0xB2, 0x99, 0xBF, 0x90, 0x7B, 0x9E, 0xBF, 0x05, 0xEE, 0xBD, 0x0C, 0x5A, 0x60, 0x27,
+ 0x7F, 0x1B, 0xE5, 0xD7, 0x0E, 0xCD, 0x37, 0x37, 0x77, 0x0E, 0x4D, 0xE7, 0x17, 0x33, 0x8B, 0x1A,
+ 0x72, 0x03, 0xB9, 0x49, 0xFC, 0xE7, 0xBC, 0x8A, 0xBD, 0x0B, 0x99, 0xEB, 0x50, 0x32, 0x74, 0x3B,
+ 0x7B, 0x93, 0x7F, 0x0B, 0x5A, 0xF5, 0x70, 0xF6, 0x4E, 0xEE, 0x0D, 0xE4, 0xF2, 0x4E, 0xF6, 0x5E,
+ 0xAE, 0x89, 0x5F, 0x9F, 0x4B, 0x2B, 0x29, 0x68, 0xDA, 0x6C, 0xAE, 0x15, 0x79, 0xCB, 0x88, 0x59,
+ 0x62, 0x68, 0xE9, 0x2B, 0xA0, 0xDF, 0x96, 0x42, 0xFB, 0x5D, 0xC8, 0x37, 0xA1, 0xDD, 0x9C, 0xCF,
+ 0x5E, 0x61, 0x7F, 0x43, 0x5B, 0xD5, 0xE8, 0xA7, 0xB9, 0x2A, 0xE8, 0x93, 0x6B, 0xE8, 0x06, 0xAE,
+ 0xE4, 0xCE, 0x63, 0x87, 0xD0, 0x75, 0xCA, 0x16, 0xE5, 0x6C, 0x68, 0xE5, 0xB3, 0xA0, 0x83, 0x27,
+ 0xE7, 0xE6, 0x2B, 0xA7, 0x2A, 0x27, 0xF1, 0x6B, 0xF9, 0x8F, 0xF8, 0x2D, 0xFC, 0x25, 0xD4, 0xF7,
+ 0x57, 0x90, 0xF3, 0x9F, 0x43, 0x9F, 0xED, 0xA0, 0x23, 0x79, 0x7F, 0x6E, 0x3B, 0x4A, 0xE3, 0x00,
+ 0xE8, 0x94, 0xAF, 0xF1, 0x7F, 0xD2, 0xCF, 0x78, 0x05, 0x34, 0xCB, 0x24, 0xC8, 0x40, 0x9F, 0xB4,
+ 0x70, 0xBF, 0x0E, 0xED, 0xB1, 0x13, 0xBA, 0x71, 0x19, 0xB4, 0xEC, 0x26, 0xC8, 0x89, 0xD0, 0xCD,
+ 0x67, 0xC3, 0x06, 0xFA, 0x2A, 0x64, 0xA2, 0x87, 0xCE, 0x63, 0x9D, 0xBC, 0x8C, 0xB7, 0x29, 0x9B,
+ 0x80, 0x77, 0x04, 0x2D, 0xCC, 0xBD, 0x9E, 0xBB, 0x27, 0x57, 0x8E, 0xFC, 0xF5, 0x83, 0x81, 0x1F,
+ 0x63, 0x67, 0xF2, 0xBB, 0xD9, 0x09, 0xB0, 0x26, 0x02, 0xD8, 0x0C, 0xF7, 0xF3, 0xAA, 0xDC, 0x14,
+ 0x9E, 0xC8, 0x8D, 0xA7, 0x72, 0x7E, 0xA7, 0xB2, 0x8C, 0xDF, 0xCE, 0x62, 0xB4, 0x81, 0x1D, 0x06,
+ 0x59, 0xAC, 0x66, 0x6F, 0xB0, 0xD7, 0xA8, 0x92, 0xDF, 0x04, 0xD6, 0x58, 0x00, 0x4D, 0xF3, 0x10,
+ 0x4B, 0xB3, 0xBF, 0xF3, 0xD1, 0xBC, 0x8B, 0xEF, 0xE0, 0xF3, 0x78, 0x1D, 0x6D, 0xE7, 0x49, 0xE8,
+ 0xEC, 0x19, 0x3C, 0x01, 0xDB, 0x42, 0x5C, 0xF7, 0xC0, 0x46, 0x51, 0x87, 0x11, 0x78, 0xF6, 0xAF,
+ 0xDB, 0xC3, 0xF4, 0xA3, 0xB7, 0x1E, 0x0F, 0x4C, 0xB5, 0xEF, 0x71, 0xE5, 0xEC, 0x1C, 0xFA, 0xFC,
+ 0x28, 0x9D, 0x37, 0xE5, 0x0F, 0xE8, 0xEB, 0xD2, 0xA7, 0xA6, 0xAD, 0x93, 0xA2, 0x08, 0x5B, 0xD9,
+ 0xD4, 0x39, 0x3E, 0xA8, 0x3C, 0xC6, 0x55, 0x55, 0x89, 0x29, 0x6A, 0x4C, 0x5C, 0x5C, 0x51, 0x71,
+ 0xE9, 0xBA, 0xA6, 0x8A, 0x73, 0x2C, 0x75, 0x45, 0xD1, 0x54, 0x53, 0xD7, 0x55, 0x0D, 0x97, 0xAE,
+ 0xDB, 0x9A, 0x2E, 0x43, 0xAA, 0x78, 0xC5, 0xD4, 0x18, 0x7E, 0x13, 0xF7, 0x9B, 0xE2, 0x8E, 0x98,
+ 0xF8, 0x66, 0xA8, 0xE2, 0x5C, 0x4D, 0x05, 0xB7, 0x8B, 0x28, 0x74, 0x55, 0x8D, 0x7E, 0xC1, 0xA5,
+ 0x88, 0x94, 0xE4, 0x25, 0x62, 0x82, 0x21, 0x8B, 0xD8, 0x09, 0x31, 0x9A, 0x0A, 0x57, 0x2D, 0x3D,
+ 0x46, 0x1A, 0xD2, 0x43, 0xDC, 0x8A, 0x86, 0x9C, 0xE1, 0x3E, 0x31, 0x0E, 0xA6, 0x23, 0x98, 0xAE,
+ 0x20, 0x1A, 0x4B, 0x17, 0x79, 0xE3, 0x9C, 0x74, 0x55, 0xE7, 0x51, 0x79, 0x08, 0xEB, 0x1F, 0xDF,
+ 0x10, 0x10, 0x97, 0x1E, 0x5D, 0xE2, 0xA3, 0xC2, 0xE5, 0xFF, 0xFD, 0x95, 0x8C, 0xE5, 0x18, 0x12,
+ 0xBB, 0xA6, 0x59, 0x86, 0x7C, 0x57, 0x24, 0x06, 0x81, 0x05, 0x7F, 0x22, 0x79, 0x71, 0xAA, 0x87,
+ 0x0E, 0xA8, 0x78, 0x19, 0xE2, 0xE0, 0x4B, 0x0B, 0xDF, 0x50, 0x18, 0xBA, 0x61, 0x38, 0xBA, 0xA1,
+ 0x29, 0xAA, 0x08, 0xAE, 0x69, 0x31, 0xF9, 0x5F, 0xE4, 0xD6, 0xD2, 0xF2, 0xD8, 0x35, 0x13, 0xB7,
+ 0x22, 0x79, 0x11, 0x5A, 0x33, 0x10, 0x56, 0x5E, 0x31, 0x81, 0x58, 0xE4, 0x48, 0x2B, 0xC1, 0x2E,
+ 0x4A, 0x96, 0x10, 0xA3, 0x05, 0x6F, 0x1B, 0xD8, 0x71, 0x83, 0x66, 0x68, 0x11, 0x76, 0x43, 0x60,
+ 0x37, 0xC8, 0x40, 0x30, 0x43, 0x00, 0xB3, 0x0D, 0x71, 0x2B, 0x10, 0x19, 0x9A, 0x91, 0xC7, 0x2E,
+ 0xD0, 0xE9, 0x22, 0x87, 0x11, 0x76, 0xA4, 0x65, 0x18, 0xCA, 0xD0, 0xB5, 0x3F, 0xEC, 0xB6, 0x67,
+ 0x02, 0x7B, 0x2C, 0xA6, 0xE9, 0xB6, 0x29, 0xB0, 0xA3, 0x50, 0x81, 0x0F, 0xD8, 0xA3, 0xAC, 0xC9,
+ 0x62, 0xD4, 0x4C, 0x43, 0x44, 0x98, 0xC7, 0x6E, 0x9B, 0x48, 0x14, 0xBE, 0xA6, 0xE1, 0x19, 0x46,
+ 0x3E, 0xB8, 0xA8, 0x27, 0x35, 0x26, 0x0A, 0x5B, 0x53, 0x6C, 0x5D, 0x22, 0x14, 0xA5, 0x24, 0xEA,
+ 0x8E, 0x2B, 0xBA, 0x78, 0x94, 0xC8, 0x34, 0xCC, 0x08, 0xBB, 0x5A, 0xC0, 0xAE, 0x6B, 0x91, 0xF4,
+ 0xE8, 0x3A, 0xB0, 0xE3, 0x0B, 0x19, 0xA6, 0x69, 0xC3, 0xDB, 0x31, 0x50, 0x0C, 0xBA, 0x48, 0x10,
+ 0x5F, 0x20, 0x55, 0xB8, 0x2F, 0x26, 0xEC, 0x71, 0x04, 0x33, 0x15, 0x80, 0x72, 0x4C, 0x45, 0xD6,
+ 0x30, 0x7C, 0x0C, 0xF1, 0x88, 0x57, 0x1E, 0x3B, 0x00, 0x47, 0x95, 0x6D, 0x18, 0x48, 0xCB, 0x34,
+ 0x3F, 0x07, 0x76, 0xC7, 0xB7, 0x04, 0x76, 0x55, 0x17, 0x71, 0xC6, 0xF0, 0x2E, 0xC1, 0xC4, 0x04,
+ 0x76, 0x21, 0xE5, 0x11, 0x76, 0x0B, 0xE0, 0x21, 0xEA, 0x86, 0x19, 0x8B, 0x19, 0xBA, 0x63, 0x99,
+ 0x02, 0x8D, 0x61, 0x99, 0xBE, 0x69, 0xEA, 0x31, 0x4D, 0x40, 0xD6, 0x85, 0xC4, 0xC5, 0x64, 0x6E,
+ 0x15, 0x47, 0xD4, 0x80, 0x2A, 0x4A, 0xC2, 0x86, 0x77, 0x0C, 0xC5, 0x89, 0x60, 0xBA, 0x65, 0x5A,
+ 0x91, 0x2C, 0x0E, 0x8F, 0xDD, 0x10, 0xD8, 0x2D, 0xCB, 0x81, 0xB7, 0x6B, 0xAA, 0x24, 0x4B, 0x0B,
+ 0xC0, 0x75, 0xE4, 0xC8, 0x12, 0xD8, 0xD1, 0x13, 0x91, 0xD8, 0x01, 0xCA, 0x35, 0x63, 0xB2, 0x86,
+ 0xE1, 0x63, 0x96, 0x60, 0x37, 0x45, 0x0E, 0x45, 0x7B, 0x32, 0x4D, 0xA4, 0x65, 0x59, 0x9F, 0x03,
+ 0xBB, 0x1B, 0x58, 0x84, 0x1B, 0x54, 0xC3, 0x70, 0x2D, 0x81, 0x1D, 0x02, 0xA5, 0x18, 0x40, 0x04,
+ 0x2C, 0x40, 0xAF, 0x01, 0x2C, 0xF2, 0x61, 0x59, 0xA2, 0x30, 0x75, 0xD3, 0x42, 0xCC, 0xBA, 0x67,
+ 0x23, 0x19, 0x14, 0xAD, 0x65, 0x05, 0xA6, 0x65, 0x00, 0x3B, 0x82, 0x1B, 0x12, 0xAF, 0x61, 0xC4,
+ 0x20, 0x37, 0xAE, 0x10, 0x0A, 0x55, 0x78, 0x8B, 0x36, 0x01, 0xEC, 0x86, 0xB8, 0xDD, 0x46, 0x58,
+ 0x79, 0x45, 0x82, 0x1F, 0x13, 0xA5, 0x28, 0x6B, 0x17, 0x17, 0x91, 0x10, 0x24, 0x42, 0x8C, 0x6E,
+ 0x4C, 0x31, 0x3C, 0x53, 0x23, 0x13, 0xBE, 0x88, 0x1B, 0xF2, 0x0E, 0xEC, 0x80, 0x84, 0x3E, 0x98,
+ 0x58, 0x6F, 0x66, 0xC5, 0x2C, 0xCB, 0xF4, 0xAC, 0x98, 0x11, 0x61, 0x37, 0x2C, 0xAE, 0x17, 0xB1,
+ 0xE3, 0x9B, 0x48, 0xBF, 0x88, 0x3D, 0xE2, 0x15, 0xF9, 0x7F, 0x7F, 0xD8, 0xBD, 0xD0, 0x06, 0x76,
+ 0x55, 0x33, 0x4C, 0xCF, 0x96, 0x65, 0x10, 0x81, 0xD1, 0x54, 0x59, 0x2D, 0x7A, 0x84, 0xDD, 0x46,
+ 0xC5, 0x8B, 0x17, 0x42, 0x98, 0x86, 0x6F, 0x23, 0x19, 0x94, 0xBF, 0x6D, 0x87, 0x96, 0x2D, 0x83,
+ 0x1B, 0x78, 0xA1, 0x79, 0xAA, 0xA6, 0x90, 0x8B, 0x98, 0x27, 0x9A, 0x1B, 0xE0, 0x99, 0x86, 0x6B,
+ 0x14, 0xB1, 0x9B, 0x36, 0xC2, 0x0E, 0x87, 0x1D, 0x11, 0x99, 0xC0, 0x8E, 0x3A, 0x23, 0xCB, 0xB6,
+ 0x3D, 0x78, 0xFB, 0x96, 0x46, 0x48, 0xCC, 0xB4, 0x11, 0x9B, 0x09, 0xF9, 0x13, 0xD2, 0x83, 0xDE,
+ 0xA7, 0x8D, 0x60, 0x36, 0xB0, 0x5B, 0xBE, 0x2D, 0x6E, 0x05, 0x22, 0xDB, 0xB0, 0xF3, 0xD8, 0x05,
+ 0x3A, 0x00, 0x16, 0xE9, 0xC7, 0x10, 0x04, 0x69, 0xD9, 0x76, 0x6C, 0xE8, 0xDA, 0x1F, 0x76, 0x3F,
+ 0xE1, 0x48, 0xEC, 0xA6, 0xE9, 0x3B, 0x08, 0xA6, 0x99, 0x31, 0x91, 0x6B, 0x90, 0xAB, 0xA1, 0x09,
+ 0x4A, 0x13, 0xC5, 0x68, 0x1A, 0x0E, 0xC0, 0xDB, 0x02, 0xBB, 0xAA, 0x5A, 0x46, 0xE0, 0xA0, 0x0E,
+ 0x91, 0x92, 0x63, 0x27, 0x6C, 0xDB, 0x84, 0x7C, 0x20, 0xB8, 0x6C, 0x13, 0x79, 0xEC, 0xBE, 0xA8,
+ 0x34, 0x4D, 0x78, 0x7B, 0x06, 0x3C, 0xD1, 0x60, 0x11, 0xCC, 0x74, 0x6C, 0xC7, 0x94, 0x97, 0x44,
+ 0x2C, 0xEB, 0x47, 0x16, 0x45, 0x84, 0x5D, 0x47, 0x9D, 0x81, 0x76, 0x1D, 0x1F, 0xDE, 0x01, 0x34,
+ 0x8F, 0x2C, 0x5B, 0x00, 0x87, 0xCE, 0xD0, 0x1C, 0xC3, 0x10, 0xD8, 0x1D, 0x04, 0x73, 0x62, 0x00,
+ 0x15, 0x38, 0xAA, 0x94, 0x6E, 0xF8, 0xD8, 0xE8, 0x8D, 0x15, 0xB0, 0xDB, 0xB8, 0x41, 0xAA, 0x18,
+ 0xDB, 0x46, 0x5A, 0x8E, 0xF3, 0x39, 0xB0, 0x07, 0x49, 0x97, 0x04, 0xDD, 0x9A, 0x56, 0xE0, 0x80,
+ 0x82, 0x35, 0x2B, 0xA6, 0xC7, 0x2C, 0x03, 0xB5, 0x2E, 0xAB, 0xC5, 0x80, 0xC8, 0xA1, 0xD6, 0x5C,
+ 0x47, 0x14, 0xA6, 0x69, 0x3B, 0x88, 0xD9, 0x0C, 0x5D, 0x24, 0x8A, 0xA2, 0x75, 0x9D, 0xA4, 0xED,
+ 0x58, 0xC0, 0x6E, 0x99, 0x28, 0x20, 0x60, 0xD7, 0x2C, 0x4B, 0x55, 0xCD, 0x58, 0x80, 0x5A, 0x36,
+ 0x35, 0xE1, 0xED, 0xA3, 0x8D, 0x0B, 0xA1, 0x75, 0x70, 0xBB, 0xEB, 0xB8, 0x25, 0xD8, 0x45, 0x1E,
+ 0xA3, 0x36, 0x80, 0xE8, 0xAD, 0x3C, 0x76, 0xDB, 0x75, 0x03, 0x54, 0x5B, 0xE8, 0xE8, 0x64, 0xC3,
+ 0x17, 0x71, 0xA3, 0xAD, 0xAB, 0xBA, 0x2B, 0xB0, 0x3B, 0xE4, 0x22, 0x98, 0xA3, 0x02, 0x54, 0xE8,
+ 0xAA, 0x52, 0xBA, 0xE1, 0xE3, 0x94, 0x60, 0x77, 0x70, 0x83, 0x25, 0xE2, 0x75, 0x1C, 0xA4, 0xE5,
+ 0xBA, 0xEA, 0xBF, 0xC7, 0x1E, 0xA6, 0x3C, 0x12, 0x9A, 0xDB, 0xB2, 0x43, 0xB7, 0x88, 0xDD, 0x44,
+ 0xAD, 0x9B, 0x52, 0x55, 0xA8, 0x02, 0xBB, 0xE9, 0xB9, 0xB6, 0x88, 0xD9, 0x71, 0x05, 0xF6, 0xB8,
+ 0x87, 0x64, 0x50, 0xFE, 0x9E, 0x9B, 0x72, 0x5C, 0x0B, 0x4A, 0x1C, 0xC1, 0x2D, 0x0B, 0xB9, 0xD7,
+ 0x6C, 0x91, 0xDB, 0x58, 0x68, 0x49, 0x84, 0xF0, 0x0E, 0x50, 0x6C, 0x02, 0xBB, 0xEB, 0x5A, 0x96,
+ 0xE7, 0x78, 0x96, 0xBC, 0x64, 0x0B, 0x97, 0xD8, 0x65, 0x51, 0x44, 0xD8, 0x41, 0x07, 0x26, 0x39,
+ 0x9E, 0x17, 0xAA, 0x31, 0x2B, 0x0E, 0xEC, 0x10, 0x34, 0x0B, 0x71, 0xAB, 0xB6, 0xC0, 0x8E, 0x92,
+ 0x14, 0xA3, 0x2D, 0x08, 0xE6, 0xAA, 0xAE, 0xEB, 0xC4, 0x0B, 0xD8, 0x3D, 0xCB, 0x55, 0xCC, 0x22,
+ 0x76, 0x17, 0x37, 0xE4, 0xB1, 0x23, 0x2D, 0xCF, 0x8B, 0x8C, 0x09, 0xF9, 0x7F, 0x7F, 0xD8, 0xE3,
+ 0xE9, 0x08, 0xBB, 0x6D, 0xC7, 0x45, 0x70, 0xDD, 0x06, 0x18, 0x60, 0x00, 0x76, 0x29, 0x91, 0x42,
+ 0x84, 0x6C, 0x81, 0xDD, 0x16, 0xF0, 0x3D, 0x4D, 0x73, 0xAC, 0x84, 0x8F, 0x64, 0x20, 0x56, 0x9E,
+ 0x97, 0x76, 0x3D, 0x5B, 0x93, 0x90, 0x2D, 0xC1, 0x34, 0x9A, 0x2D, 0xDA, 0x84, 0x1A, 0x17, 0x4D,
+ 0x42, 0x17, 0xDE, 0x01, 0xDA, 0x38, 0x14, 0x3F, 0xEA, 0xD3, 0xB6, 0x7D, 0x37, 0x8F, 0xDD, 0x28,
+ 0x60, 0xB7, 0x25, 0x76, 0x44, 0x6F, 0x03, 0xBB, 0x13, 0x61, 0x8F, 0xC3, 0x3B, 0xE1, 0x1A, 0x24,
+ 0xCB, 0x16, 0xC0, 0x05, 0x76, 0x4F, 0x60, 0xF7, 0xC8, 0x43, 0x30, 0x0F, 0xD8, 0xDD, 0x84, 0xA7,
+ 0xDA, 0x22, 0x06, 0xF8, 0x78, 0x79, 0xEC, 0x02, 0x9D, 0x8B, 0x1C, 0xDA, 0x82, 0x4B, 0x5C, 0xD7,
+ 0x73, 0x0B, 0xD8, 0xD5, 0x7F, 0x89, 0x3D, 0x51, 0xE6, 0x0B, 0xEC, 0x86, 0xED, 0x24, 0x7C, 0xF0,
+ 0x7A, 0x1E, 0x3B, 0x6A, 0xDD, 0x92, 0x6A, 0x52, 0x62, 0xB7, 0x7C, 0xCF, 0xB1, 0xF1, 0x72, 0x81,
+ 0xDD, 0xB5, 0x92, 0x81, 0x07, 0x34, 0x8E, 0xEB, 0x7B, 0x65, 0x1E, 0xF2, 0x67, 0x0A, 0xEC, 0x36,
+ 0x78, 0xC7, 0xD4, 0x1D, 0xD1, 0x26, 0xD4, 0x84, 0x2D, 0x11, 0x6A, 0x8E, 0x1D, 0xA2, 0xD8, 0x04,
+ 0x76, 0x04, 0x73, 0x02, 0xCF, 0xB7, 0xE5, 0x25, 0x5B, 0xB8, 0xC4, 0x2E, 0x8B, 0x02, 0x42, 0xEC,
+ 0x40, 0x41, 0x41, 0xAA, 0xC8, 0xF5, 0xFD, 0x04, 0xBC, 0x93, 0x9E, 0x41, 0xB2, 0x6C, 0x1D, 0x4D,
+ 0x85, 0xBE, 0x04, 0xF7, 0x59, 0x02, 0xBB, 0x8F, 0x60, 0xBE, 0x0A, 0x50, 0x49, 0x5F, 0x93, 0x2D,
+ 0x9B, 0x7C, 0x60, 0xB7, 0x8A, 0xD8, 0x51, 0xD9, 0x8E, 0x23, 0xB0, 0x7B, 0x9E, 0xEF, 0xF9, 0xBE,
+ 0xFF, 0x39, 0xB0, 0x27, 0xCB, 0x03, 0x12, 0x46, 0x94, 0xE3, 0x26, 0x03, 0x55, 0xBC, 0xAB, 0xA6,
+ 0xEA, 0x00, 0x11, 0xCC, 0x27, 0xA1, 0xC7, 0x50, 0xD1, 0x68, 0xAD, 0x81, 0xEF, 0x3A, 0x02, 0xBE,
+ 0x8F, 0x98, 0xED, 0x54, 0x80, 0x44, 0x81, 0x3D, 0xF0, 0xCB, 0x3D, 0xDF, 0x01, 0x76, 0x04, 0x77,
+ 0x04, 0xD3, 0xE8, 0x2E, 0xD2, 0xB6, 0xD5, 0xA4, 0x63, 0x09, 0x84, 0xF0, 0x8B, 0x83, 0xDF, 0x80,
+ 0xDD, 0xF1, 0x7D, 0xC7, 0x09, 0xFC, 0xC0, 0x91, 0x97, 0xAC, 0x6D, 0x91, 0x47, 0xA7, 0x14, 0x3B,
+ 0xE4, 0x95, 0xBC, 0x20, 0x48, 0x02, 0x6E, 0xCA, 0x37, 0x09, 0xF1, 0x3B, 0x88, 0x5B, 0x13, 0xD8,
+ 0x03, 0x81, 0xDD, 0xA7, 0x00, 0xC1, 0x02, 0x0D, 0xA0, 0x52, 0x81, 0xE6, 0x48, 0xEC, 0x81, 0xE3,
+ 0x97, 0x60, 0xF7, 0x71, 0x83, 0xC4, 0xEE, 0xFB, 0x48, 0x2B, 0x08, 0x22, 0x43, 0x4A, 0x8B, 0xEC,
+ 0xC6, 0xE1, 0xAF, 0x54, 0x36, 0x04, 0x76, 0xDD, 0x74, 0xDC, 0x94, 0xC4, 0xEE, 0x02, 0xBB, 0x0B,
+ 0xEC, 0x42, 0x21, 0x43, 0x76, 0xE5, 0x79, 0x9A, 0x76, 0x18, 0xB8, 0xAE, 0x80, 0x1F, 0x68, 0xBA,
+ 0xE7, 0xA4, 0x43, 0x24, 0x03, 0xB1, 0x0A, 0x83, 0xAC, 0x1F, 0xB8, 0x30, 0xAF, 0x11, 0x1C, 0x90,
+ 0x1C, 0x60, 0x77, 0x91, 0x5B, 0x60, 0x07, 0xD1, 0x02, 0xBB, 0xEB, 0x24, 0xC4, 0x52, 0x7A, 0x34,
+ 0xD8, 0x20, 0x10, 0xFB, 0x22, 0x84, 0x7B, 0x63, 0x97, 0x62, 0x80, 0xE8, 0x5D, 0x81, 0x1D, 0x92,
+ 0xEF, 0x85, 0x61, 0x4A, 0x53, 0xDD, 0xB4, 0xC4, 0x0E, 0xDE, 0x73, 0x75, 0x0D, 0x2F, 0x13, 0xD2,
+ 0x23, 0x7A, 0x46, 0xA1, 0x2B, 0xB0, 0x07, 0x81, 0x9F, 0x0E, 0x35, 0xD9, 0xB2, 0xE1, 0x13, 0xC4,
+ 0xEC, 0x22, 0xF6, 0x00, 0x37, 0xB8, 0x11, 0x76, 0xA4, 0x15, 0x86, 0x9F, 0x03, 0x7B, 0xBA, 0x22,
+ 0x2E, 0xB1, 0xBB, 0x1E, 0xE2, 0x04, 0xB1, 0xBB, 0xB0, 0x44, 0x5D, 0x07, 0x12, 0xEF, 0x08, 0xEC,
+ 0x76, 0x84, 0x3D, 0x1E, 0x78, 0x88, 0xD9, 0xF1, 0x43, 0x5D, 0xF7, 0x9D, 0x4C, 0x3C, 0x80, 0x74,
+ 0x7A, 0x7E, 0x3C, 0xAC, 0x08, 0x42, 0x89, 0xDD, 0xC1, 0x1D, 0x60, 0x1A, 0xC3, 0x13, 0xD8, 0xB5,
+ 0x94, 0x0B, 0x15, 0x03, 0x86, 0xF7, 0x9C, 0x24, 0xB8, 0xB0, 0x80, 0x3D, 0x1E, 0xC4, 0x5D, 0x79,
+ 0xC9, 0xDA, 0x96, 0xED, 0x52, 0x62, 0x87, 0x10, 0x7B, 0x50, 0xCE, 0x1E, 0xB0, 0xFB, 0xF1, 0x78,
+ 0x1A, 0xDE, 0x19, 0x58, 0x5B, 0xB2, 0x6C, 0x01, 0x1C, 0xB6, 0x42, 0x84, 0x3D, 0xA4, 0x38, 0x82,
+ 0x85, 0xC0, 0x1E, 0x64, 0x42, 0xDD, 0x15, 0x31, 0x00, 0x7B, 0x98, 0xC7, 0xAE, 0x15, 0xB0, 0x0B,
+ 0x1D, 0x12, 0x04, 0x61, 0x10, 0x8F, 0xC7, 0xB5, 0xA1, 0x6B, 0x7F, 0xD8, 0x33, 0x95, 0x79, 0xEC,
+ 0x7E, 0x26, 0x2E, 0x3A, 0x52, 0x1E, 0xB0, 0x7B, 0x12, 0xBB, 0x34, 0x11, 0x44, 0xF3, 0xF1, 0x1C,
+ 0xF1, 0xBC, 0xA1, 0x80, 0x0F, 0xEC, 0x81, 0x5B, 0x96, 0x40, 0x81, 0xA3, 0xF9, 0x26, 0xC2, 0xCA,
+ 0x20, 0xF4, 0x60, 0xB8, 0x02, 0xB2, 0xEB, 0x4A, 0xEC, 0x9E, 0xAE, 0xBB, 0x5A, 0xDA, 0x83, 0x92,
+ 0x11, 0xD8, 0xDD, 0x14, 0xF8, 0x0D, 0xD8, 0x51, 0x9F, 0x9E, 0x97, 0x08, 0x13, 0x25, 0xD8, 0x45,
+ 0x1E, 0x3D, 0x29, 0x06, 0x79, 0xEC, 0x90, 0x57, 0x42, 0x8E, 0x33, 0x48, 0xAF, 0x2C, 0xB4, 0x28,
+ 0x80, 0x2F, 0xE2, 0xD6, 0x05, 0xF6, 0xB8, 0xE3, 0x08, 0xEC, 0x09, 0x04, 0x8B, 0x6B, 0xA8, 0xD0,
+ 0xB2, 0x38, 0x4C, 0x4B, 0x64, 0x0C, 0xA5, 0x11, 0xC6, 0x9C, 0x22, 0xF6, 0x10, 0x37, 0x78, 0x22,
+ 0xDE, 0x30, 0x8C, 0x87, 0x89, 0x44, 0x42, 0xFF, 0xF7, 0xD8, 0xCB, 0xAA, 0x13, 0xE8, 0x39, 0x18,
+ 0x96, 0xE7, 0x97, 0x25, 0x8A, 0xD8, 0x5D, 0x74, 0x6C, 0x5C, 0x89, 0x1D, 0xC9, 0x03, 0x7B, 0x22,
+ 0xEE, 0x7B, 0xA1, 0xEF, 0x06, 0x71, 0xDD, 0x08, 0xDC, 0xF2, 0x24, 0x92, 0x41, 0x93, 0x4A, 0xC4,
+ 0xAB, 0xC3, 0xB8, 0x67, 0xD8, 0xA8, 0x6E, 0x04, 0x02, 0x2D, 0x1B, 0xBE, 0x2F, 0xB0, 0x67, 0x80,
+ 0xDD, 0x45, 0xD7, 0xD8, 0x77, 0x85, 0x1E, 0x30, 0x54, 0xC3, 0x8B, 0xC7, 0x3D, 0x3F, 0x19, 0x26,
+ 0x3C, 0x79, 0xC9, 0xDA, 0x1E, 0xC2, 0x8E, 0x06, 0xEC, 0xC3, 0x30, 0x11, 0xD8, 0x91, 0xE3, 0x32,
+ 0x54, 0x75, 0x39, 0x2C, 0xCD, 0x40, 0xC8, 0x95, 0x6F, 0xE8, 0x78, 0x59, 0x09, 0x07, 0x5D, 0xCD,
+ 0x38, 0x25, 0x10, 0x2C, 0xA1, 0xC7, 0xE3, 0x61, 0x36, 0x91, 0xC7, 0x9E, 0xF0, 0xE2, 0x25, 0xD8,
+ 0xE3, 0xB8, 0xC1, 0x8F, 0xB0, 0x27, 0x24, 0x76, 0x71, 0xC9, 0xEE, 0x82, 0xBE, 0x3F, 0xEC, 0xFF,
+ 0x27, 0x00, 0x00, 0xFF, 0xFF, 0xA4, 0xB4, 0xB8, 0xA4}
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 0000000..bca364f
--- /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("<</Type /OCG /Name %s>>", 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 <</OCGs [%s] /D <</OFF [%s] /Order [%s]>>>>", onStr, offStr, onStr)
+ if f.layer.openLayerPane {
+ f.out("/PageMode /UseOC")
+ }
+ }
+}
diff --git a/vendor/github.com/jung-kurt/gofpdf/license.txt b/vendor/github.com/jung-kurt/gofpdf/license.txt
new file mode 100644
index 0000000..eb1abac
--- /dev/null
+++ b/vendor/github.com/jung-kurt/gofpdf/license.txt
@@ -0,0 +1,23 @@
+MIT License
+
+Copyright (c) 2013-2016 Kurt Jung (Gmail: kurt.w.jung)
+
+Portions copyright by the 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/list/list.go b/vendor/github.com/jung-kurt/gofpdf/list/list.go
new file mode 100644
index 0000000..8099404
--- /dev/null
+++ b/vendor/github.com/jung-kurt/gofpdf/list/list.go
@@ -0,0 +1,59 @@
+package main
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+func matchTail(str, tailStr string) (match bool, headStr string) {
+ sln := len(str)
+ ln := len(tailStr)
+ if sln > ln {
+ match = str[sln-ln:] == tailStr
+ if match {
+ headStr = str[:sln-ln]
+ }
+ }
+ return
+}
+
+func matchHead(str, headStr string) (match bool, tailStr string) {
+ ln := len(headStr)
+ if len(str) > ln {
+ match = str[:ln] == headStr
+ if match {
+ tailStr = str[ln:]
+ }
+ }
+ return
+}
+
+func main() {
+ var err error
+ var ok bool
+ var showStr, name string
+ err = filepath.Walk("pdf/reference", func(path string, info os.FileInfo, err error) error {
+ if info.Mode().IsRegular() {
+ name = filepath.Base(path)
+ ok, name = matchTail(name, ".pdf")
+ if ok {
+ name = strings.Replace(name, "_", " ", -1)
+ ok, showStr = matchHead(name, "Fpdf ")
+ if ok {
+ fmt.Printf("[%s](%s)\n", showStr, path)
+ } else {
+ ok, showStr = matchHead(name, "contrib ")
+ if ok {
+ fmt.Printf("[%s](%s)\n", showStr, path)
+ }
+ }
+ }
+ }
+ return nil
+ })
+ if err != nil {
+ fmt.Println(err)
+ }
+}
diff --git a/vendor/github.com/jung-kurt/gofpdf/makefont/doc.go b/vendor/github.com/jung-kurt/gofpdf/makefont/doc.go
new file mode 100644
index 0000000..306ead2
--- /dev/null
+++ b/vendor/github.com/jung-kurt/gofpdf/makefont/doc.go
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2013 Kurt 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.
+ */
+
+/*
+Command makefont generates a font definition file.
+
+This utility is used to generate a font definition file that allows TrueType
+and Type1 fonts to be used in PDFs produced with the fpdf package.
+*/
+package main
diff --git a/vendor/github.com/jung-kurt/gofpdf/makefont/makefont b/vendor/github.com/jung-kurt/gofpdf/makefont/makefont
new file mode 100755
index 0000000..2e49bc1
--- /dev/null
+++ b/vendor/github.com/jung-kurt/gofpdf/makefont/makefont
Binary files differ
diff --git a/vendor/github.com/jung-kurt/gofpdf/makefont/makefont.go b/vendor/github.com/jung-kurt/gofpdf/makefont/makefont.go
new file mode 100644
index 0000000..eb2993e
--- /dev/null
+++ b/vendor/github.com/jung-kurt/gofpdf/makefont/makefont.go
@@ -0,0 +1,71 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "github.com/jung-kurt/gofpdf"
+ "os"
+)
+
+func errPrintf(fmtStr string, args ...interface{}) {
+ fmt.Fprintf(os.Stderr, fmtStr, args...)
+}
+
+func showHelp() {
+ errPrintf("Usage: %s [options] font_file [font_file...]\n", os.Args[0])
+ flag.PrintDefaults()
+ fmt.Fprintln(os.Stderr, "\n"+
+ "font_file is the name of the TrueType file (extension .ttf), OpenType file\n"+
+ "(extension .otf) or binary Type1 file (extension .pfb) from which to\n"+
+ "generate a definition file. If an OpenType file is specified, it must be one\n"+
+ "that is based on TrueType outlines, not PostScript outlines; this cannot be\n"+
+ "determined from the file extension alone. If a Type1 file is specified, a\n"+
+ "metric file with the same pathname except with the extension .afm must be\n"+
+ "present.")
+ errPrintf("\nExample: %s --embed --enc=../font/cp1252.map --dst=../font calligra.ttf /opt/font/symbol.pfb\n", os.Args[0])
+}
+
+func tutorialSummary(f *gofpdf.Fpdf, fileStr string) {
+ if f.Ok() {
+ fl, err := os.Create(fileStr)
+ defer fl.Close()
+ if err == nil {
+ f.Output(fl)
+ } else {
+ f.SetError(err)
+ }
+ }
+ if f.Ok() {
+ fmt.Printf("Successfully generated %s\n", fileStr)
+ } else {
+ errPrintf("%s\n", f.Error())
+ }
+}
+
+func main() {
+ var dstDirStr, encodingFileStr string
+ var err error
+ var help, embed bool
+ flag.StringVar(&dstDirStr, "dst", ".", "directory for output files (*.z, *.json)")
+ flag.StringVar(&encodingFileStr, "enc", "cp1252.map", "code page file")
+ flag.BoolVar(&embed, "embed", false, "embed font into PDF")
+ flag.BoolVar(&help, "help", false, "command line usage")
+ flag.Parse()
+ if help {
+ showHelp()
+ } else {
+ args := flag.Args()
+ if len(args) > 0 {
+ for _, fileStr := range args {
+ err = gofpdf.MakeFont(fileStr, encodingFileStr, dstDirStr, os.Stderr, embed)
+ if err != nil {
+ errPrintf("%s\n", err)
+ }
+ // errPrintf("Font file [%s], Encoding file [%s], Embed [%v]\n", fileStr, encodingFileStr, embed)
+ }
+ } else {
+ errPrintf("At least one Type1 or TrueType font must be specified\n")
+ showHelp()
+ }
+ }
+}
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 0000000..854b003
--- /dev/null
+++ b/vendor/github.com/jung-kurt/gofpdf/png.go
@@ -0,0 +1,212 @@
+/*
+ * 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)
+ if ct == 0 {
+ trns = []int{int(t[1])} // ord(substr($t,1,1)));
+ } else if ct == 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)));
+ } else {
+ 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 0000000..e934c2b
--- /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])
+ cap := len(userPass)
+ v = make([]byte, cap, cap)
+ c.XORKeyStream(v, userPass)
+ return
+}
+
+func (p *protectType) uValueGen() (v []byte) {
+ var c *rc4.Cipher
+ c, _ = rc4.NewCipher(p.encryptionKey)
+ cap := len(p.padding)
+ v = make([]byte, cap, cap)
+ 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/svgbasic.go b/vendor/github.com/jung-kurt/gofpdf/svgbasic.go
new file mode 100644
index 0000000..cb8e91e
--- /dev/null
+++ b/vendor/github.com/jung-kurt/gofpdf/svgbasic.go
@@ -0,0 +1,210 @@
+/*
+ * 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)
+ 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), and 'C' (absolute cubic Bézier curve: cx0, cy0, cx1, cy1,
+// x1,y1).
+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 0000000..04f6cb3
--- /dev/null
+++ b/vendor/github.com/jung-kurt/gofpdf/svgwrite.go
@@ -0,0 +1,58 @@
+/*
+ * 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
+ 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 0000000..1826fd7
--- /dev/null
+++ b/vendor/github.com/jung-kurt/gofpdf/template.go
@@ -0,0 +1,269 @@
+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 (
+ "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.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.unitStr, f.fontDirStr, fn, f)
+}
+
+// CreateTemplate creates a template not attached to any document
+func CreateTemplate(corner PointType, size SizeType, unitStr, fontDirStr string, fn func(*Tpl)) Template {
+ return newTpl(corner, size, 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%d-%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%d Do Q", t.ID())
+}
+
+var nextTemplateIDChannel = func() chan int64 {
+ ch := make(chan int64)
+ go func() {
+ var nextID int64 = 1
+ for {
+ ch <- nextID
+ nextID++
+ }
+ }()
+ return ch
+}()
+
+// GenerateTemplateID gives the next template ID. These numbers are global so that they can never clash.
+func GenerateTemplateID() int64 {
+ return <-nextTemplateIDChannel
+}
+
+// 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() int64
+ Size() (PointType, SizeType)
+ Bytes() []byte
+ Images() map[string]*ImageInfoType
+ Templates() []Template
+}
+
+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%d %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("<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]")
+
+ f.templateFontCatalog()
+
+ tImages := t.Images()
+ tTemplates := t.Templates()
+ if len(tImages) > 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%d %d 0 R", ti.i, ti.n)
+ }
+ }
+ for _, tt := range tTemplates {
+ id := tt.ID()
+ if objID, ok := f.templateObjects[id]; ok {
+ f.outf("/TPL%d %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[int64]Template, sort bool) (keyList []int64) {
+ var key int64
+ 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[int64]Template, catalogSort bool) []Template {
+ chain := make([]Template, 0, len(templates)*2)
+
+ // build a full set of dependency chains
+ var keyList []int64
+ var key int64
+ 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 {
+ for _, sub := range templateChainDependencies(req) {
+ chain = append(chain, sub)
+ }
+ }
+ 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 0000000..01bb040
--- /dev/null
+++ b/vendor/github.com/jung-kurt/gofpdf/template_impl.go
@@ -0,0 +1,123 @@
+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.
+ */
+
+// newTpl creates a template, copying graphics settings from a template if one is given
+func newTpl(corner PointType, size SizeType, unitStr, fontDirStr string, fn func(*Tpl), copyFrom *Fpdf) Template {
+ orientationStr := "p"
+ if size.Wd > size.Ht {
+ orientationStr = "l"
+ }
+ sizeStr := ""
+
+ fpdf := fpdfNew(orientationStr, unitStr, sizeStr, fontDirStr, size)
+ tpl := Tpl{*fpdf}
+ if copyFrom != nil {
+ tpl.loadParamsFromFpdf(copyFrom)
+ }
+ tpl.Fpdf.SetAutoPageBreak(false, 0)
+ tpl.Fpdf.AddPage()
+ fn(&tpl)
+ bytes := tpl.Fpdf.pages[tpl.Fpdf.page].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
+
+ id := GenerateTemplateID()
+ template := FpdfTpl{id, corner, size, bytes, images, templates}
+ return &template
+}
+
+// FpdfTpl is a concrete implementation of the Template interface.
+type FpdfTpl struct {
+ id int64
+ corner PointType
+ size SizeType
+ bytes []byte
+ images map[string]*ImageInfoType
+ templates []Template
+}
+
+// ID returns the global template identifier
+func (t *FpdfTpl) ID() int64 {
+ return t.id
+}
+
+// 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
+}
+
+// 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
+}
+
+// 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
+}
+
+// AddPage does nothing because you cannot add pages to a template
+func (t *Tpl) AddPage() {
+}
+
+// AddPageFormat does nothign because you cannot add pages to a template
+func (t *Tpl) AddPageFormat(orientationStr string, size SizeType) {
+}
+
+// SetAutoPageBreak does nothing because you cannot add pages to a template
+func (t *Tpl) SetAutoPageBreak(auto bool, margin float64) {
+}
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 0000000..669ab4d
--- /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/util.go b/vendor/github.com/jung-kurt/gofpdf/util.go
new file mode 100644
index 0000000..149271c
--- /dev/null
+++ b/vendor/github.com/jung-kurt/gofpdf/util.go
@@ -0,0 +1,314 @@
+/*
+ * 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 (
+ "bytes"
+ "compress/zlib"
+ "fmt"
+ // "github.com/davecgh/go-spew/spew"
+ "bufio"
+ "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...)
+}
+
+// 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
+}
+
+// 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
+}
+
+// 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
+}
+
+// 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
+}
+
+// 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()
+}
+
+// Returns an uncompressed copy of the specified zlib-compressed byte array
+func sliceUncompress(data []byte) (outData []byte, err error) {
+ inBuf := bytes.NewBuffer(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
+}
+
+// Convert UTF-8 to UTF-16BE with BOM; from http://www.fpdf.org/
+func utf8toutf16(s string) string {
+ res := make([]byte, 0, 8)
+ res = append(res, 0xFE, 0xFF)
+ nb := len(s)
+ i := 0
+ for i < nb {
+ c1 := byte(s[i])
+ i++
+ if 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))
+ } else if c1 >= 192 {
+ // 2-byte character
+ c2 := byte(s[i])
+ i++
+ res = append(res, ((c1 & 0x1C) >> 2),
+ ((c1&0x03)<<6)+(c2&0x3F))
+ } else {
+ // Single-byte character
+ res = append(res, 0, c1)
+ }
+ }
+ return string(res)
+}
+
+// Return a if cnd is true, otherwise b
+func intIf(cnd bool, a, b int) int {
+ if cnd {
+ return a
+ }
+ return b
+}
+
+// Return aStr if cnd is true, otherwise bStr
+func strIf(cnd bool, aStr, bStr string) string {
+ if cnd {
+ return aStr
+ }
+ return bStr
+}
+
+// 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 = func(s string) string {
+ return s
+ }
+ }
+ 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 = func(s string) string {
+ return s
+ }
+ }
+ 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 (4) example demonstrates this method.
+func (f *Fpdf) UnicodeTranslatorFromDescriptor(cpStr string) (rep func(string) string) {
+ var str string
+ var ok bool
+ if f.err != nil {
+ return
+ }
+ 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")
+ }
+ 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}
+}
diff --git a/vendor/github.com/op/go-logging/LICENSE b/vendor/github.com/op/go-logging/LICENSE
new file mode 100644
index 0000000..f1f6cfc
--- /dev/null
+++ b/vendor/github.com/op/go-logging/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2013 Örjan Persson. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/op/go-logging/backend.go b/vendor/github.com/op/go-logging/backend.go
new file mode 100644
index 0000000..74d9201
--- /dev/null
+++ b/vendor/github.com/op/go-logging/backend.go
@@ -0,0 +1,39 @@
+// Copyright 2013, Örjan Persson. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package logging
+
+// defaultBackend is the backend used for all logging calls.
+var defaultBackend LeveledBackend
+
+// Backend is the interface which a log backend need to implement to be able to
+// be used as a logging backend.
+type Backend interface {
+ Log(Level, int, *Record) error
+}
+
+// SetBackend replaces the backend currently set with the given new logging
+// backend.
+func SetBackend(backends ...Backend) LeveledBackend {
+ var backend Backend
+ if len(backends) == 1 {
+ backend = backends[0]
+ } else {
+ backend = MultiLogger(backends...)
+ }
+
+ defaultBackend = AddModuleLevel(backend)
+ return defaultBackend
+}
+
+// SetLevel sets the logging level for the specified module. The module
+// corresponds to the string specified in GetLogger.
+func SetLevel(level Level, module string) {
+ defaultBackend.SetLevel(level, module)
+}
+
+// GetLevel returns the logging level for the specified module.
+func GetLevel(module string) Level {
+ return defaultBackend.GetLevel(module)
+}
diff --git a/vendor/github.com/op/go-logging/examples/example.go b/vendor/github.com/op/go-logging/examples/example.go
new file mode 100644
index 0000000..9f4ddee
--- /dev/null
+++ b/vendor/github.com/op/go-logging/examples/example.go
@@ -0,0 +1,49 @@
+package main
+
+import (
+ "os"
+
+ "github.com/op/go-logging"
+)
+
+var log = logging.MustGetLogger("example")
+
+// Example format string. Everything except the message has a custom color
+// which is dependent on the log level. Many fields have a custom output
+// formatting too, eg. the time returns the hour down to the milli second.
+var format = logging.MustStringFormatter(
+ `%{color}%{time:15:04:05.000} %{shortfunc} ▶ %{level:.4s} %{id:03x}%{color:reset} %{message}`,
+)
+
+// Password is just an example type implementing the Redactor interface. Any
+// time this is logged, the Redacted() function will be called.
+type Password string
+
+func (p Password) Redacted() interface{} {
+ return logging.Redact(string(p))
+}
+
+func main() {
+ // For demo purposes, create two backend for os.Stderr.
+ backend1 := logging.NewLogBackend(os.Stderr, "", 0)
+ backend2 := logging.NewLogBackend(os.Stderr, "", 0)
+
+ // For messages written to backend2 we want to add some additional
+ // information to the output, including the used log level and the name of
+ // the function.
+ backend2Formatter := logging.NewBackendFormatter(backend2, format)
+
+ // Only errors and more severe messages should be sent to backend1
+ backend1Leveled := logging.AddModuleLevel(backend1)
+ backend1Leveled.SetLevel(logging.ERROR, "")
+
+ // Set the backends to be used.
+ logging.SetBackend(backend1Leveled, backend2Formatter)
+
+ log.Debugf("debug %s", Password("secret"))
+ log.Info("info")
+ log.Notice("notice")
+ log.Warning("warning")
+ log.Error("err")
+ log.Critical("crit")
+}
diff --git a/vendor/github.com/op/go-logging/format.go b/vendor/github.com/op/go-logging/format.go
new file mode 100644
index 0000000..7160674
--- /dev/null
+++ b/vendor/github.com/op/go-logging/format.go
@@ -0,0 +1,414 @@
+// Copyright 2013, Örjan Persson. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package logging
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+ "os"
+ "path"
+ "path/filepath"
+ "regexp"
+ "runtime"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+)
+
+// TODO see Formatter interface in fmt/print.go
+// TODO try text/template, maybe it have enough performance
+// TODO other template systems?
+// TODO make it possible to specify formats per backend?
+type fmtVerb int
+
+const (
+ fmtVerbTime fmtVerb = iota
+ fmtVerbLevel
+ fmtVerbID
+ fmtVerbPid
+ fmtVerbProgram
+ fmtVerbModule
+ fmtVerbMessage
+ fmtVerbLongfile
+ fmtVerbShortfile
+ fmtVerbLongpkg
+ fmtVerbShortpkg
+ fmtVerbLongfunc
+ fmtVerbShortfunc
+ fmtVerbCallpath
+ fmtVerbLevelColor
+
+ // Keep last, there are no match for these below.
+ fmtVerbUnknown
+ fmtVerbStatic
+)
+
+var fmtVerbs = []string{
+ "time",
+ "level",
+ "id",
+ "pid",
+ "program",
+ "module",
+ "message",
+ "longfile",
+ "shortfile",
+ "longpkg",
+ "shortpkg",
+ "longfunc",
+ "shortfunc",
+ "callpath",
+ "color",
+}
+
+const rfc3339Milli = "2006-01-02T15:04:05.999Z07:00"
+
+var defaultVerbsLayout = []string{
+ rfc3339Milli,
+ "s",
+ "d",
+ "d",
+ "s",
+ "s",
+ "s",
+ "s",
+ "s",
+ "s",
+ "s",
+ "s",
+ "s",
+ "0",
+ "",
+}
+
+var (
+ pid = os.Getpid()
+ program = filepath.Base(os.Args[0])
+)
+
+func getFmtVerbByName(name string) fmtVerb {
+ for i, verb := range fmtVerbs {
+ if name == verb {
+ return fmtVerb(i)
+ }
+ }
+ return fmtVerbUnknown
+}
+
+// Formatter is the required interface for a custom log record formatter.
+type Formatter interface {
+ Format(calldepth int, r *Record, w io.Writer) error
+}
+
+// formatter is used by all backends unless otherwise overriden.
+var formatter struct {
+ sync.RWMutex
+ def Formatter
+}
+
+func getFormatter() Formatter {
+ formatter.RLock()
+ defer formatter.RUnlock()
+ return formatter.def
+}
+
+var (
+ // DefaultFormatter is the default formatter used and is only the message.
+ DefaultFormatter = MustStringFormatter("%{message}")
+
+ // GlogFormatter mimics the glog format
+ GlogFormatter = MustStringFormatter("%{level:.1s}%{time:0102 15:04:05.999999} %{pid} %{shortfile}] %{message}")
+)
+
+// SetFormatter sets the default formatter for all new backends. A backend will
+// fetch this value once it is needed to format a record. Note that backends
+// will cache the formatter after the first point. For now, make sure to set
+// the formatter before logging.
+func SetFormatter(f Formatter) {
+ formatter.Lock()
+ defer formatter.Unlock()
+ formatter.def = f
+}
+
+var formatRe = regexp.MustCompile(`%{([a-z]+)(?::(.*?[^\\]))?}`)
+
+type part struct {
+ verb fmtVerb
+ layout string
+}
+
+// stringFormatter contains a list of parts which explains how to build the
+// formatted string passed on to the logging backend.
+type stringFormatter struct {
+ parts []part
+}
+
+// NewStringFormatter returns a new Formatter which outputs the log record as a
+// string based on the 'verbs' specified in the format string.
+//
+// The verbs:
+//
+// General:
+// %{id} Sequence number for log message (uint64).
+// %{pid} Process id (int)
+// %{time} Time when log occurred (time.Time)
+// %{level} Log level (Level)
+// %{module} Module (string)
+// %{program} Basename of os.Args[0] (string)
+// %{message} Message (string)
+// %{longfile} Full file name and line number: /a/b/c/d.go:23
+// %{shortfile} Final file name element and line number: d.go:23
+// %{callpath} Callpath like main.a.b.c...c "..." meaning recursive call ~. meaning truncated path
+// %{color} ANSI color based on log level
+//
+// For normal types, the output can be customized by using the 'verbs' defined
+// in the fmt package, eg. '%{id:04d}' to make the id output be '%04d' as the
+// format string.
+//
+// For time.Time, use the same layout as time.Format to change the time format
+// when output, eg "2006-01-02T15:04:05.999Z-07:00".
+//
+// For the 'color' verb, the output can be adjusted to either use bold colors,
+// i.e., '%{color:bold}' or to reset the ANSI attributes, i.e.,
+// '%{color:reset}' Note that if you use the color verb explicitly, be sure to
+// reset it or else the color state will persist past your log message. e.g.,
+// "%{color:bold}%{time:15:04:05} %{level:-8s}%{color:reset} %{message}" will
+// just colorize the time and level, leaving the message uncolored.
+//
+// For the 'callpath' verb, the output can be adjusted to limit the printing
+// the stack depth. i.e. '%{callpath:3}' will print '~.a.b.c'
+//
+// Colors on Windows is unfortunately not supported right now and is currently
+// a no-op.
+//
+// There's also a couple of experimental 'verbs'. These are exposed to get
+// feedback and needs a bit of tinkering. Hence, they might change in the
+// future.
+//
+// Experimental:
+// %{longpkg} Full package path, eg. github.com/go-logging
+// %{shortpkg} Base package path, eg. go-logging
+// %{longfunc} Full function name, eg. littleEndian.PutUint32
+// %{shortfunc} Base function name, eg. PutUint32
+// %{callpath} Call function path, eg. main.a.b.c
+func NewStringFormatter(format string) (Formatter, error) {
+ var fmter = &stringFormatter{}
+
+ // Find the boundaries of all %{vars}
+ matches := formatRe.FindAllStringSubmatchIndex(format, -1)
+ if matches == nil {
+ return nil, errors.New("logger: invalid log format: " + format)
+ }
+
+ // Collect all variables and static text for the format
+ prev := 0
+ for _, m := range matches {
+ start, end := m[0], m[1]
+ if start > prev {
+ fmter.add(fmtVerbStatic, format[prev:start])
+ }
+
+ name := format[m[2]:m[3]]
+ verb := getFmtVerbByName(name)
+ if verb == fmtVerbUnknown {
+ return nil, errors.New("logger: unknown variable: " + name)
+ }
+
+ // Handle layout customizations or use the default. If this is not for the
+ // time, color formatting or callpath, we need to prefix with %.
+ layout := defaultVerbsLayout[verb]
+ if m[4] != -1 {
+ layout = format[m[4]:m[5]]
+ }
+ if verb != fmtVerbTime && verb != fmtVerbLevelColor && verb != fmtVerbCallpath {
+ layout = "%" + layout
+ }
+
+ fmter.add(verb, layout)
+ prev = end
+ }
+ end := format[prev:]
+ if end != "" {
+ fmter.add(fmtVerbStatic, end)
+ }
+
+ // Make a test run to make sure we can format it correctly.
+ t, err := time.Parse(time.RFC3339, "2010-02-04T21:00:57-08:00")
+ if err != nil {
+ panic(err)
+ }
+ testFmt := "hello %s"
+ r := &Record{
+ ID: 12345,
+ Time: t,
+ Module: "logger",
+ Args: []interface{}{"go"},
+ fmt: &testFmt,
+ }
+ if err := fmter.Format(0, r, &bytes.Buffer{}); err != nil {
+ return nil, err
+ }
+
+ return fmter, nil
+}
+
+// MustStringFormatter is equivalent to NewStringFormatter with a call to panic
+// on error.
+func MustStringFormatter(format string) Formatter {
+ f, err := NewStringFormatter(format)
+ if err != nil {
+ panic("Failed to initialized string formatter: " + err.Error())
+ }
+ return f
+}
+
+func (f *stringFormatter) add(verb fmtVerb, layout string) {
+ f.parts = append(f.parts, part{verb, layout})
+}
+
+func (f *stringFormatter) Format(calldepth int, r *Record, output io.Writer) error {
+ for _, part := range f.parts {
+ if part.verb == fmtVerbStatic {
+ output.Write([]byte(part.layout))
+ } else if part.verb == fmtVerbTime {
+ output.Write([]byte(r.Time.Format(part.layout)))
+ } else if part.verb == fmtVerbLevelColor {
+ doFmtVerbLevelColor(part.layout, r.Level, output)
+ } else if part.verb == fmtVerbCallpath {
+ depth, err := strconv.Atoi(part.layout)
+ if err != nil {
+ depth = 0
+ }
+ output.Write([]byte(formatCallpath(calldepth+1, depth)))
+ } else {
+ var v interface{}
+ switch part.verb {
+ case fmtVerbLevel:
+ v = r.Level
+ break
+ case fmtVerbID:
+ v = r.ID
+ break
+ case fmtVerbPid:
+ v = pid
+ break
+ case fmtVerbProgram:
+ v = program
+ break
+ case fmtVerbModule:
+ v = r.Module
+ break
+ case fmtVerbMessage:
+ v = r.Message()
+ break
+ case fmtVerbLongfile, fmtVerbShortfile:
+ _, file, line, ok := runtime.Caller(calldepth + 1)
+ if !ok {
+ file = "???"
+ line = 0
+ } else if part.verb == fmtVerbShortfile {
+ file = filepath.Base(file)
+ }
+ v = fmt.Sprintf("%s:%d", file, line)
+ case fmtVerbLongfunc, fmtVerbShortfunc,
+ fmtVerbLongpkg, fmtVerbShortpkg:
+ // TODO cache pc
+ v = "???"
+ if pc, _, _, ok := runtime.Caller(calldepth + 1); ok {
+ if f := runtime.FuncForPC(pc); f != nil {
+ v = formatFuncName(part.verb, f.Name())
+ }
+ }
+ default:
+ panic("unhandled format part")
+ }
+ fmt.Fprintf(output, part.layout, v)
+ }
+ }
+ return nil
+}
+
+// formatFuncName tries to extract certain part of the runtime formatted
+// function name to some pre-defined variation.
+//
+// This function is known to not work properly if the package path or name
+// contains a dot.
+func formatFuncName(v fmtVerb, f string) string {
+ i := strings.LastIndex(f, "/")
+ j := strings.Index(f[i+1:], ".")
+ if j < 1 {
+ return "???"
+ }
+ pkg, fun := f[:i+j+1], f[i+j+2:]
+ switch v {
+ case fmtVerbLongpkg:
+ return pkg
+ case fmtVerbShortpkg:
+ return path.Base(pkg)
+ case fmtVerbLongfunc:
+ return fun
+ case fmtVerbShortfunc:
+ i = strings.LastIndex(fun, ".")
+ return fun[i+1:]
+ }
+ panic("unexpected func formatter")
+}
+
+func formatCallpath(calldepth int, depth int) string {
+ v := ""
+ callers := make([]uintptr, 64)
+ n := runtime.Callers(calldepth+2, callers)
+ oldPc := callers[n-1]
+
+ start := n - 3
+ if depth > 0 && start >= depth {
+ start = depth - 1
+ v += "~."
+ }
+ recursiveCall := false
+ for i := start; i >= 0; i-- {
+ pc := callers[i]
+ if oldPc == pc {
+ recursiveCall = true
+ continue
+ }
+ oldPc = pc
+ if recursiveCall {
+ recursiveCall = false
+ v += ".."
+ }
+ if i < start {
+ v += "."
+ }
+ if f := runtime.FuncForPC(pc); f != nil {
+ v += formatFuncName(fmtVerbShortfunc, f.Name())
+ }
+ }
+ return v
+}
+
+// backendFormatter combines a backend with a specific formatter making it
+// possible to have different log formats for different backends.
+type backendFormatter struct {
+ b Backend
+ f Formatter
+}
+
+// NewBackendFormatter creates a new backend which makes all records that
+// passes through it beeing formatted by the specific formatter.
+func NewBackendFormatter(b Backend, f Formatter) Backend {
+ return &backendFormatter{b, f}
+}
+
+// Log implements the Log function required by the Backend interface.
+func (bf *backendFormatter) Log(level Level, calldepth int, r *Record) error {
+ // Make a shallow copy of the record and replace any formatter
+ r2 := *r
+ r2.formatter = bf.f
+ return bf.b.Log(level, calldepth+1, &r2)
+}
diff --git a/vendor/github.com/op/go-logging/level.go b/vendor/github.com/op/go-logging/level.go
new file mode 100644
index 0000000..98dd191
--- /dev/null
+++ b/vendor/github.com/op/go-logging/level.go
@@ -0,0 +1,128 @@
+// Copyright 2013, Örjan Persson. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package logging
+
+import (
+ "errors"
+ "strings"
+ "sync"
+)
+
+// ErrInvalidLogLevel is used when an invalid log level has been used.
+var ErrInvalidLogLevel = errors.New("logger: invalid log level")
+
+// Level defines all available log levels for log messages.
+type Level int
+
+// Log levels.
+const (
+ CRITICAL Level = iota
+ ERROR
+ WARNING
+ NOTICE
+ INFO
+ DEBUG
+)
+
+var levelNames = []string{
+ "CRITICAL",
+ "ERROR",
+ "WARNING",
+ "NOTICE",
+ "INFO",
+ "DEBUG",
+}
+
+// String returns the string representation of a logging level.
+func (p Level) String() string {
+ return levelNames[p]
+}
+
+// LogLevel returns the log level from a string representation.
+func LogLevel(level string) (Level, error) {
+ for i, name := range levelNames {
+ if strings.EqualFold(name, level) {
+ return Level(i), nil
+ }
+ }
+ return ERROR, ErrInvalidLogLevel
+}
+
+// Leveled interface is the interface required to be able to add leveled
+// logging.
+type Leveled interface {
+ GetLevel(string) Level
+ SetLevel(Level, string)
+ IsEnabledFor(Level, string) bool
+}
+
+// LeveledBackend is a log backend with additional knobs for setting levels on
+// individual modules to different levels.
+type LeveledBackend interface {
+ Backend
+ Leveled
+}
+
+type moduleLeveled struct {
+ levels map[string]Level
+ backend Backend
+ formatter Formatter
+ once sync.Once
+}
+
+// AddModuleLevel wraps a log backend with knobs to have different log levels
+// for different modules.
+func AddModuleLevel(backend Backend) LeveledBackend {
+ var leveled LeveledBackend
+ var ok bool
+ if leveled, ok = backend.(LeveledBackend); !ok {
+ leveled = &moduleLeveled{
+ levels: make(map[string]Level),
+ backend: backend,
+ }
+ }
+ return leveled
+}
+
+// GetLevel returns the log level for the given module.
+func (l *moduleLeveled) GetLevel(module string) Level {
+ level, exists := l.levels[module]
+ if exists == false {
+ level, exists = l.levels[""]
+ // no configuration exists, default to debug
+ if exists == false {
+ level = DEBUG
+ }
+ }
+ return level
+}
+
+// SetLevel sets the log level for the given module.
+func (l *moduleLeveled) SetLevel(level Level, module string) {
+ l.levels[module] = level
+}
+
+// IsEnabledFor will return true if logging is enabled for the given module.
+func (l *moduleLeveled) IsEnabledFor(level Level, module string) bool {
+ return level <= l.GetLevel(module)
+}
+
+func (l *moduleLeveled) Log(level Level, calldepth int, rec *Record) (err error) {
+ if l.IsEnabledFor(level, rec.Module) {
+ // TODO get rid of traces of formatter here. BackendFormatter should be used.
+ rec.formatter = l.getFormatterAndCacheCurrent()
+ err = l.backend.Log(level, calldepth+1, rec)
+ }
+ return
+}
+
+func (l *moduleLeveled) getFormatterAndCacheCurrent() Formatter {
+ l.once.Do(func() {
+ if l.formatter == nil {
+ l.formatter = getFormatter()
+ }
+ })
+ return l.formatter
+}
diff --git a/vendor/github.com/op/go-logging/log_nix.go b/vendor/github.com/op/go-logging/log_nix.go
new file mode 100644
index 0000000..4ff2ab1
--- /dev/null
+++ b/vendor/github.com/op/go-logging/log_nix.go
@@ -0,0 +1,109 @@
+// +build !windows
+
+// Copyright 2013, Örjan Persson. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package logging
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "log"
+)
+
+type color int
+
+const (
+ ColorBlack = iota + 30
+ ColorRed
+ ColorGreen
+ ColorYellow
+ ColorBlue
+ ColorMagenta
+ ColorCyan
+ ColorWhite
+)
+
+var (
+ colors = []string{
+ CRITICAL: ColorSeq(ColorMagenta),
+ ERROR: ColorSeq(ColorRed),
+ WARNING: ColorSeq(ColorYellow),
+ NOTICE: ColorSeq(ColorGreen),
+ DEBUG: ColorSeq(ColorCyan),
+ }
+ boldcolors = []string{
+ CRITICAL: ColorSeqBold(ColorMagenta),
+ ERROR: ColorSeqBold(ColorRed),
+ WARNING: ColorSeqBold(ColorYellow),
+ NOTICE: ColorSeqBold(ColorGreen),
+ DEBUG: ColorSeqBold(ColorCyan),
+ }
+)
+
+// LogBackend utilizes the standard log module.
+type LogBackend struct {
+ Logger *log.Logger
+ Color bool
+ ColorConfig []string
+}
+
+// NewLogBackend creates a new LogBackend.
+func NewLogBackend(out io.Writer, prefix string, flag int) *LogBackend {
+ return &LogBackend{Logger: log.New(out, prefix, flag)}
+}
+
+// Log implements the Backend interface.
+func (b *LogBackend) Log(level Level, calldepth int, rec *Record) error {
+ if b.Color {
+ col := colors[level]
+ if len(b.ColorConfig) > int(level) && b.ColorConfig[level] != "" {
+ col = b.ColorConfig[level]
+ }
+
+ buf := &bytes.Buffer{}
+ buf.Write([]byte(col))
+ buf.Write([]byte(rec.Formatted(calldepth + 1)))
+ buf.Write([]byte("\033[0m"))
+ // For some reason, the Go logger arbitrarily decided "2" was the correct
+ // call depth...
+ return b.Logger.Output(calldepth+2, buf.String())
+ }
+
+ return b.Logger.Output(calldepth+2, rec.Formatted(calldepth+1))
+}
+
+// ConvertColors takes a list of ints representing colors for log levels and
+// converts them into strings for ANSI color formatting
+func ConvertColors(colors []int, bold bool) []string {
+ converted := []string{}
+ for _, i := range colors {
+ if bold {
+ converted = append(converted, ColorSeqBold(color(i)))
+ } else {
+ converted = append(converted, ColorSeq(color(i)))
+ }
+ }
+
+ return converted
+}
+
+func ColorSeq(color color) string {
+ return fmt.Sprintf("\033[%dm", int(color))
+}
+
+func ColorSeqBold(color color) string {
+ return fmt.Sprintf("\033[%d;1m", int(color))
+}
+
+func doFmtVerbLevelColor(layout string, level Level, output io.Writer) {
+ if layout == "bold" {
+ output.Write([]byte(boldcolors[level]))
+ } else if layout == "reset" {
+ output.Write([]byte("\033[0m"))
+ } else {
+ output.Write([]byte(colors[level]))
+ }
+}
diff --git a/vendor/github.com/op/go-logging/log_windows.go b/vendor/github.com/op/go-logging/log_windows.go
new file mode 100644
index 0000000..b8dc92c
--- /dev/null
+++ b/vendor/github.com/op/go-logging/log_windows.go
@@ -0,0 +1,107 @@
+// +build windows
+// Copyright 2013, Örjan Persson. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package logging
+
+import (
+ "bytes"
+ "io"
+ "log"
+ "syscall"
+)
+
+var (
+ kernel32DLL = syscall.NewLazyDLL("kernel32.dll")
+ setConsoleTextAttributeProc = kernel32DLL.NewProc("SetConsoleTextAttribute")
+)
+
+// Character attributes
+// Note:
+// -- The attributes are combined to produce various colors (e.g., Blue + Green will create Cyan).
+// Clearing all foreground or background colors results in black; setting all creates white.
+// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms682088(v=vs.85).aspx#_win32_character_attributes.
+const (
+ fgBlack = 0x0000
+ fgBlue = 0x0001
+ fgGreen = 0x0002
+ fgCyan = 0x0003
+ fgRed = 0x0004
+ fgMagenta = 0x0005
+ fgYellow = 0x0006
+ fgWhite = 0x0007
+ fgIntensity = 0x0008
+ fgMask = 0x000F
+)
+
+var (
+ colors = []uint16{
+ INFO: fgWhite,
+ CRITICAL: fgMagenta,
+ ERROR: fgRed,
+ WARNING: fgYellow,
+ NOTICE: fgGreen,
+ DEBUG: fgCyan,
+ }
+ boldcolors = []uint16{
+ INFO: fgWhite | fgIntensity,
+ CRITICAL: fgMagenta | fgIntensity,
+ ERROR: fgRed | fgIntensity,
+ WARNING: fgYellow | fgIntensity,
+ NOTICE: fgGreen | fgIntensity,
+ DEBUG: fgCyan | fgIntensity,
+ }
+)
+
+type file interface {
+ Fd() uintptr
+}
+
+// LogBackend utilizes the standard log module.
+type LogBackend struct {
+ Logger *log.Logger
+ Color bool
+
+ // f is set to a non-nil value if the underlying writer which logs writes to
+ // implements the file interface. This makes us able to colorise the output.
+ f file
+}
+
+// NewLogBackend creates a new LogBackend.
+func NewLogBackend(out io.Writer, prefix string, flag int) *LogBackend {
+ b := &LogBackend{Logger: log.New(out, prefix, flag)}
+
+ // Unfortunately, the API used only takes an io.Writer where the Windows API
+ // need the actual fd to change colors.
+ if f, ok := out.(file); ok {
+ b.f = f
+ }
+
+ return b
+}
+
+func (b *LogBackend) Log(level Level, calldepth int, rec *Record) error {
+ if b.Color && b.f != nil {
+ buf := &bytes.Buffer{}
+ setConsoleTextAttribute(b.f, colors[level])
+ buf.Write([]byte(rec.Formatted(calldepth + 1)))
+ err := b.Logger.Output(calldepth+2, buf.String())
+ setConsoleTextAttribute(b.f, fgWhite)
+ return err
+ }
+ return b.Logger.Output(calldepth+2, rec.Formatted(calldepth+1))
+}
+
+// setConsoleTextAttribute sets the attributes of characters written to the
+// console screen buffer by the WriteFile or WriteConsole function.
+// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms686047(v=vs.85).aspx.
+func setConsoleTextAttribute(f file, attribute uint16) bool {
+ ok, _, _ := setConsoleTextAttributeProc.Call(f.Fd(), uintptr(attribute), 0)
+ return ok != 0
+}
+
+func doFmtVerbLevelColor(layout string, level Level, output io.Writer) {
+ // TODO not supported on Windows since the io.Writer here is actually a
+ // bytes.Buffer.
+}
diff --git a/vendor/github.com/op/go-logging/logger.go b/vendor/github.com/op/go-logging/logger.go
new file mode 100644
index 0000000..535ed9b
--- /dev/null
+++ b/vendor/github.com/op/go-logging/logger.go
@@ -0,0 +1,259 @@
+// Copyright 2013, Örjan Persson. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package logging implements a logging infrastructure for Go. It supports
+// different logging backends like syslog, file and memory. Multiple backends
+// can be utilized with different log levels per backend and logger.
+package logging
+
+import (
+ "bytes"
+ "fmt"
+ "log"
+ "os"
+ "strings"
+ "sync/atomic"
+ "time"
+)
+
+// Redactor is an interface for types that may contain sensitive information
+// (like passwords), which shouldn't be printed to the log. The idea was found
+// in relog as part of the vitness project.
+type Redactor interface {
+ Redacted() interface{}
+}
+
+// Redact returns a string of * having the same length as s.
+func Redact(s string) string {
+ return strings.Repeat("*", len(s))
+}
+
+var (
+ // Sequence number is incremented and utilized for all log records created.
+ sequenceNo uint64
+
+ // timeNow is a customizable for testing purposes.
+ timeNow = time.Now
+)
+
+// Record represents a log record and contains the timestamp when the record
+// was created, an increasing id, filename and line and finally the actual
+// formatted log line.
+type Record struct {
+ ID uint64
+ Time time.Time
+ Module string
+ Level Level
+ Args []interface{}
+
+ // message is kept as a pointer to have shallow copies update this once
+ // needed.
+ message *string
+ fmt *string
+ formatter Formatter
+ formatted string
+}
+
+// Formatted returns the formatted log record string.
+func (r *Record) Formatted(calldepth int) string {
+ if r.formatted == "" {
+ var buf bytes.Buffer
+ r.formatter.Format(calldepth+1, r, &buf)
+ r.formatted = buf.String()
+ }
+ return r.formatted
+}
+
+// Message returns the log record message.
+func (r *Record) Message() string {
+ if r.message == nil {
+ // Redact the arguments that implements the Redactor interface
+ for i, arg := range r.Args {
+ if redactor, ok := arg.(Redactor); ok == true {
+ r.Args[i] = redactor.Redacted()
+ }
+ }
+ var buf bytes.Buffer
+ if r.fmt != nil {
+ fmt.Fprintf(&buf, *r.fmt, r.Args...)
+ } else {
+ // use Fprintln to make sure we always get space between arguments
+ fmt.Fprintln(&buf, r.Args...)
+ buf.Truncate(buf.Len() - 1) // strip newline
+ }
+ msg := buf.String()
+ r.message = &msg
+ }
+ return *r.message
+}
+
+// Logger is the actual logger which creates log records based on the functions
+// called and passes them to the underlying logging backend.
+type Logger struct {
+ Module string
+ backend LeveledBackend
+ haveBackend bool
+
+ // ExtraCallDepth can be used to add additional call depth when getting the
+ // calling function. This is normally used when wrapping a logger.
+ ExtraCalldepth int
+}
+
+// SetBackend overrides any previously defined backend for this logger.
+func (l *Logger) SetBackend(backend LeveledBackend) {
+ l.backend = backend
+ l.haveBackend = true
+}
+
+// TODO call NewLogger and remove MustGetLogger?
+
+// GetLogger creates and returns a Logger object based on the module name.
+func GetLogger(module string) (*Logger, error) {
+ return &Logger{Module: module}, nil
+}
+
+// MustGetLogger is like GetLogger but panics if the logger can't be created.
+// It simplifies safe initialization of a global logger for eg. a package.
+func MustGetLogger(module string) *Logger {
+ logger, err := GetLogger(module)
+ if err != nil {
+ panic("logger: " + module + ": " + err.Error())
+ }
+ return logger
+}
+
+// Reset restores the internal state of the logging library.
+func Reset() {
+ // TODO make a global Init() method to be less magic? or make it such that
+ // if there's no backends at all configured, we could use some tricks to
+ // automatically setup backends based if we have a TTY or not.
+ sequenceNo = 0
+ b := SetBackend(NewLogBackend(os.Stderr, "", log.LstdFlags))
+ b.SetLevel(DEBUG, "")
+ SetFormatter(DefaultFormatter)
+ timeNow = time.Now
+}
+
+// IsEnabledFor returns true if the logger is enabled for the given level.
+func (l *Logger) IsEnabledFor(level Level) bool {
+ return defaultBackend.IsEnabledFor(level, l.Module)
+}
+
+func (l *Logger) log(lvl Level, format *string, args ...interface{}) {
+ if !l.IsEnabledFor(lvl) {
+ return
+ }
+
+ // Create the logging record and pass it in to the backend
+ record := &Record{
+ ID: atomic.AddUint64(&sequenceNo, 1),
+ Time: timeNow(),
+ Module: l.Module,
+ Level: lvl,
+ fmt: format,
+ Args: args,
+ }
+
+ // TODO use channels to fan out the records to all backends?
+ // TODO in case of errors, do something (tricky)
+
+ // calldepth=2 brings the stack up to the caller of the level
+ // methods, Info(), Fatal(), etc.
+ // ExtraCallDepth allows this to be extended further up the stack in case we
+ // are wrapping these methods, eg. to expose them package level
+ if l.haveBackend {
+ l.backend.Log(lvl, 2+l.ExtraCalldepth, record)
+ return
+ }
+
+ defaultBackend.Log(lvl, 2+l.ExtraCalldepth, record)
+}
+
+// Fatal is equivalent to l.Critical(fmt.Sprint()) followed by a call to os.Exit(1).
+func (l *Logger) Fatal(args ...interface{}) {
+ l.log(CRITICAL, nil, args...)
+ os.Exit(1)
+}
+
+// Fatalf is equivalent to l.Critical followed by a call to os.Exit(1).
+func (l *Logger) Fatalf(format string, args ...interface{}) {
+ l.log(CRITICAL, &format, args...)
+ os.Exit(1)
+}
+
+// Panic is equivalent to l.Critical(fmt.Sprint()) followed by a call to panic().
+func (l *Logger) Panic(args ...interface{}) {
+ l.log(CRITICAL, nil, args...)
+ panic(fmt.Sprint(args...))
+}
+
+// Panicf is equivalent to l.Critical followed by a call to panic().
+func (l *Logger) Panicf(format string, args ...interface{}) {
+ l.log(CRITICAL, &format, args...)
+ panic(fmt.Sprintf(format, args...))
+}
+
+// Critical logs a message using CRITICAL as log level.
+func (l *Logger) Critical(args ...interface{}) {
+ l.log(CRITICAL, nil, args...)
+}
+
+// Criticalf logs a message using CRITICAL as log level.
+func (l *Logger) Criticalf(format string, args ...interface{}) {
+ l.log(CRITICAL, &format, args...)
+}
+
+// Error logs a message using ERROR as log level.
+func (l *Logger) Error(args ...interface{}) {
+ l.log(ERROR, nil, args...)
+}
+
+// Errorf logs a message using ERROR as log level.
+func (l *Logger) Errorf(format string, args ...interface{}) {
+ l.log(ERROR, &format, args...)
+}
+
+// Warning logs a message using WARNING as log level.
+func (l *Logger) Warning(args ...interface{}) {
+ l.log(WARNING, nil, args...)
+}
+
+// Warningf logs a message using WARNING as log level.
+func (l *Logger) Warningf(format string, args ...interface{}) {
+ l.log(WARNING, &format, args...)
+}
+
+// Notice logs a message using NOTICE as log level.
+func (l *Logger) Notice(args ...interface{}) {
+ l.log(NOTICE, nil, args...)
+}
+
+// Noticef logs a message using NOTICE as log level.
+func (l *Logger) Noticef(format string, args ...interface{}) {
+ l.log(NOTICE, &format, args...)
+}
+
+// Info logs a message using INFO as log level.
+func (l *Logger) Info(args ...interface{}) {
+ l.log(INFO, nil, args...)
+}
+
+// Infof logs a message using INFO as log level.
+func (l *Logger) Infof(format string, args ...interface{}) {
+ l.log(INFO, &format, args...)
+}
+
+// Debug logs a message using DEBUG as log level.
+func (l *Logger) Debug(args ...interface{}) {
+ l.log(DEBUG, nil, args...)
+}
+
+// Debugf logs a message using DEBUG as log level.
+func (l *Logger) Debugf(format string, args ...interface{}) {
+ l.log(DEBUG, &format, args...)
+}
+
+func init() {
+ Reset()
+}
diff --git a/vendor/github.com/op/go-logging/memory.go b/vendor/github.com/op/go-logging/memory.go
new file mode 100644
index 0000000..8d5152c
--- /dev/null
+++ b/vendor/github.com/op/go-logging/memory.go
@@ -0,0 +1,237 @@
+// Copyright 2013, Örjan Persson. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !appengine
+
+package logging
+
+import (
+ "sync"
+ "sync/atomic"
+ "time"
+ "unsafe"
+)
+
+// TODO pick one of the memory backends and stick with it or share interface.
+
+// InitForTesting is a convenient method when using logging in a test. Once
+// called, the time will be frozen to January 1, 1970 UTC.
+func InitForTesting(level Level) *MemoryBackend {
+ Reset()
+
+ memoryBackend := NewMemoryBackend(10240)
+
+ leveledBackend := AddModuleLevel(memoryBackend)
+ leveledBackend.SetLevel(level, "")
+ SetBackend(leveledBackend)
+
+ timeNow = func() time.Time {
+ return time.Unix(0, 0).UTC()
+ }
+ return memoryBackend
+}
+
+// Node is a record node pointing to an optional next node.
+type node struct {
+ next *node
+ Record *Record
+}
+
+// Next returns the next record node. If there's no node available, it will
+// return nil.
+func (n *node) Next() *node {
+ return n.next
+}
+
+// MemoryBackend is a simple memory based logging backend that will not produce
+// any output but merly keep records, up to the given size, in memory.
+type MemoryBackend struct {
+ size int32
+ maxSize int32
+ head, tail unsafe.Pointer
+}
+
+// NewMemoryBackend creates a simple in-memory logging backend.
+func NewMemoryBackend(size int) *MemoryBackend {
+ return &MemoryBackend{maxSize: int32(size)}
+}
+
+// Log implements the Log method required by Backend.
+func (b *MemoryBackend) Log(level Level, calldepth int, rec *Record) error {
+ var size int32
+
+ n := &node{Record: rec}
+ np := unsafe.Pointer(n)
+
+ // Add the record to the tail. If there's no records available, tail and
+ // head will both be nil. When we successfully set the tail and the previous
+ // value was nil, it's safe to set the head to the current value too.
+ for {
+ tailp := b.tail
+ swapped := atomic.CompareAndSwapPointer(
+ &b.tail,
+ tailp,
+ np,
+ )
+ if swapped == true {
+ if tailp == nil {
+ b.head = np
+ } else {
+ (*node)(tailp).next = n
+ }
+ size = atomic.AddInt32(&b.size, 1)
+ break
+ }
+ }
+
+ // Since one record was added, we might have overflowed the list. Remove
+ // a record if that is the case. The size will fluctate a bit, but
+ // eventual consistent.
+ if b.maxSize > 0 && size > b.maxSize {
+ for {
+ headp := b.head
+ head := (*node)(b.head)
+ if head.next == nil {
+ break
+ }
+ swapped := atomic.CompareAndSwapPointer(
+ &b.head,
+ headp,
+ unsafe.Pointer(head.next),
+ )
+ if swapped == true {
+ atomic.AddInt32(&b.size, -1)
+ break
+ }
+ }
+ }
+ return nil
+}
+
+// Head returns the oldest record node kept in memory. It can be used to
+// iterate over records, one by one, up to the last record.
+//
+// Note: new records can get added while iterating. Hence the number of records
+// iterated over might be larger than the maximum size.
+func (b *MemoryBackend) Head() *node {
+ return (*node)(b.head)
+}
+
+type event int
+
+const (
+ eventFlush event = iota
+ eventStop
+)
+
+// ChannelMemoryBackend is very similar to the MemoryBackend, except that it
+// internally utilizes a channel.
+type ChannelMemoryBackend struct {
+ maxSize int
+ size int
+ incoming chan *Record
+ events chan event
+ mu sync.Mutex
+ running bool
+ flushWg sync.WaitGroup
+ stopWg sync.WaitGroup
+ head, tail *node
+}
+
+// NewChannelMemoryBackend creates a simple in-memory logging backend which
+// utilizes a go channel for communication.
+//
+// Start will automatically be called by this function.
+func NewChannelMemoryBackend(size int) *ChannelMemoryBackend {
+ backend := &ChannelMemoryBackend{
+ maxSize: size,
+ incoming: make(chan *Record, 1024),
+ events: make(chan event),
+ }
+ backend.Start()
+ return backend
+}
+
+// Start launches the internal goroutine which starts processing data from the
+// input channel.
+func (b *ChannelMemoryBackend) Start() {
+ b.mu.Lock()
+ defer b.mu.Unlock()
+
+ // Launch the goroutine unless it's already running.
+ if b.running != true {
+ b.running = true
+ b.stopWg.Add(1)
+ go b.process()
+ }
+}
+
+func (b *ChannelMemoryBackend) process() {
+ defer b.stopWg.Done()
+ for {
+ select {
+ case rec := <-b.incoming:
+ b.insertRecord(rec)
+ case e := <-b.events:
+ switch e {
+ case eventStop:
+ return
+ case eventFlush:
+ for len(b.incoming) > 0 {
+ b.insertRecord(<-b.incoming)
+ }
+ b.flushWg.Done()
+ }
+ }
+ }
+}
+
+func (b *ChannelMemoryBackend) insertRecord(rec *Record) {
+ prev := b.tail
+ b.tail = &node{Record: rec}
+ if prev == nil {
+ b.head = b.tail
+ } else {
+ prev.next = b.tail
+ }
+
+ if b.maxSize > 0 && b.size >= b.maxSize {
+ b.head = b.head.next
+ } else {
+ b.size++
+ }
+}
+
+// Flush waits until all records in the buffered channel have been processed.
+func (b *ChannelMemoryBackend) Flush() {
+ b.flushWg.Add(1)
+ b.events <- eventFlush
+ b.flushWg.Wait()
+}
+
+// Stop signals the internal goroutine to exit and waits until it have.
+func (b *ChannelMemoryBackend) Stop() {
+ b.mu.Lock()
+ if b.running == true {
+ b.running = false
+ b.events <- eventStop
+ }
+ b.mu.Unlock()
+ b.stopWg.Wait()
+}
+
+// Log implements the Log method required by Backend.
+func (b *ChannelMemoryBackend) Log(level Level, calldepth int, rec *Record) error {
+ b.incoming <- rec
+ return nil
+}
+
+// Head returns the oldest record node kept in memory. It can be used to
+// iterate over records, one by one, up to the last record.
+//
+// Note: new records can get added while iterating. Hence the number of records
+// iterated over might be larger than the maximum size.
+func (b *ChannelMemoryBackend) Head() *node {
+ return b.head
+}
diff --git a/vendor/github.com/op/go-logging/multi.go b/vendor/github.com/op/go-logging/multi.go
new file mode 100644
index 0000000..3731653
--- /dev/null
+++ b/vendor/github.com/op/go-logging/multi.go
@@ -0,0 +1,65 @@
+// Copyright 2013, Örjan Persson. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package logging
+
+// TODO remove Level stuff from the multi logger. Do one thing.
+
+// multiLogger is a log multiplexer which can be used to utilize multiple log
+// backends at once.
+type multiLogger struct {
+ backends []LeveledBackend
+}
+
+// MultiLogger creates a logger which contain multiple loggers.
+func MultiLogger(backends ...Backend) LeveledBackend {
+ var leveledBackends []LeveledBackend
+ for _, backend := range backends {
+ leveledBackends = append(leveledBackends, AddModuleLevel(backend))
+ }
+ return &multiLogger{leveledBackends}
+}
+
+// Log passes the log record to all backends.
+func (b *multiLogger) Log(level Level, calldepth int, rec *Record) (err error) {
+ for _, backend := range b.backends {
+ if backend.IsEnabledFor(level, rec.Module) {
+ // Shallow copy of the record for the formatted cache on Record and get the
+ // record formatter from the backend.
+ r2 := *rec
+ if e := backend.Log(level, calldepth+1, &r2); e != nil {
+ err = e
+ }
+ }
+ }
+ return
+}
+
+// GetLevel returns the highest level enabled by all backends.
+func (b *multiLogger) GetLevel(module string) Level {
+ var level Level
+ for _, backend := range b.backends {
+ if backendLevel := backend.GetLevel(module); backendLevel > level {
+ level = backendLevel
+ }
+ }
+ return level
+}
+
+// SetLevel propagates the same level to all backends.
+func (b *multiLogger) SetLevel(level Level, module string) {
+ for _, backend := range b.backends {
+ backend.SetLevel(level, module)
+ }
+}
+
+// IsEnabledFor returns true if any of the backends are enabled for it.
+func (b *multiLogger) IsEnabledFor(level Level, module string) bool {
+ for _, backend := range b.backends {
+ if backend.IsEnabledFor(level, module) {
+ return true
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/op/go-logging/syslog.go b/vendor/github.com/op/go-logging/syslog.go
new file mode 100644
index 0000000..4faa531
--- /dev/null
+++ b/vendor/github.com/op/go-logging/syslog.go
@@ -0,0 +1,53 @@
+// Copyright 2013, Örjan Persson. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//+build !windows,!plan9
+
+package logging
+
+import "log/syslog"
+
+// SyslogBackend is a simple logger to syslog backend. It automatically maps
+// the internal log levels to appropriate syslog log levels.
+type SyslogBackend struct {
+ Writer *syslog.Writer
+}
+
+// NewSyslogBackend connects to the syslog daemon using UNIX sockets with the
+// given prefix. If prefix is not given, the prefix will be derived from the
+// launched command.
+func NewSyslogBackend(prefix string) (b *SyslogBackend, err error) {
+ var w *syslog.Writer
+ w, err = syslog.New(syslog.LOG_CRIT, prefix)
+ return &SyslogBackend{w}, err
+}
+
+// NewSyslogBackendPriority is the same as NewSyslogBackend, but with custom
+// syslog priority, like syslog.LOG_LOCAL3|syslog.LOG_DEBUG etc.
+func NewSyslogBackendPriority(prefix string, priority syslog.Priority) (b *SyslogBackend, err error) {
+ var w *syslog.Writer
+ w, err = syslog.New(priority, prefix)
+ return &SyslogBackend{w}, err
+}
+
+// Log implements the Backend interface.
+func (b *SyslogBackend) Log(level Level, calldepth int, rec *Record) error {
+ line := rec.Formatted(calldepth + 1)
+ switch level {
+ case CRITICAL:
+ return b.Writer.Crit(line)
+ case ERROR:
+ return b.Writer.Err(line)
+ case WARNING:
+ return b.Writer.Warning(line)
+ case NOTICE:
+ return b.Writer.Notice(line)
+ case INFO:
+ return b.Writer.Info(line)
+ case DEBUG:
+ return b.Writer.Debug(line)
+ default:
+ }
+ panic("unhandled log level")
+}
diff --git a/vendor/github.com/op/go-logging/syslog_fallback.go b/vendor/github.com/op/go-logging/syslog_fallback.go
new file mode 100644
index 0000000..91bc18d
--- /dev/null
+++ b/vendor/github.com/op/go-logging/syslog_fallback.go
@@ -0,0 +1,28 @@
+// Copyright 2013, Örjan Persson. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//+build windows plan9
+
+package logging
+
+import (
+ "fmt"
+)
+
+type Priority int
+
+type SyslogBackend struct {
+}
+
+func NewSyslogBackend(prefix string) (b *SyslogBackend, err error) {
+ return nil, fmt.Errorf("Platform does not support syslog")
+}
+
+func NewSyslogBackendPriority(prefix string, priority Priority) (b *SyslogBackend, err error) {
+ return nil, fmt.Errorf("Platform does not support syslog")
+}
+
+func (b *SyslogBackend) Log(level Level, calldepth int, rec *Record) error {
+ return fmt.Errorf("Platform does not support syslog")
+}
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 0000000..0c3dfb4
--- /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 0000000..cd7d206
--- /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/number_encoder.go b/vendor/github.com/ruudk/golang-pdf417/number_encoder.go
new file mode 100644
index 0000000..2b0ab54
--- /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 0000000..849bdcf
--- /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, rowCodes, 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 0000000..3118916
--- /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 0000000..80da926
--- /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", "-", ".",
+ "$", "/", "g", "|", "*", "(", ")", "?", "{", "}", "'",
+ 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
+}
diff --git a/vendor/github.com/skip2/go-qrcode/LICENSE b/vendor/github.com/skip2/go-qrcode/LICENSE
new file mode 100644
index 0000000..342c5e5
--- /dev/null
+++ b/vendor/github.com/skip2/go-qrcode/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2014 Tom Harwood
+
+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/skip2/go-qrcode/bitset/bitset.go b/vendor/github.com/skip2/go-qrcode/bitset/bitset.go
new file mode 100644
index 0000000..fba3bf4
--- /dev/null
+++ b/vendor/github.com/skip2/go-qrcode/bitset/bitset.go
@@ -0,0 +1,273 @@
+// go-qrcode
+// Copyright 2014 Tom Harwood
+
+// Package bitset implements an append only bit array.
+//
+// To create a Bitset and append some bits:
+// // Bitset Contents
+// b := bitset.New() // {}
+// b.AppendBools(true, true, false) // {1, 1, 0}
+// b.AppendBools(true) // {1, 1, 0, 1}
+// b.AppendValue(0x02, 4) // {1, 1, 0, 1, 0, 0, 1, 0}
+//
+// To read values:
+//
+// len := b.Len() // 8
+// v := b.At(0) // 1
+// v = b.At(1) // 1
+// v = b.At(2) // 0
+// v = b.At(8) // 0
+package bitset
+
+import (
+ "bytes"
+ "fmt"
+ "log"
+)
+
+const (
+ b0 = false
+ b1 = true
+)
+
+// Bitset stores an array of bits.
+type Bitset struct {
+ // The number of bits stored.
+ numBits int
+
+ // Storage for individual bits.
+ bits []byte
+}
+
+// New returns an initialised Bitset with optional initial bits v.
+func New(v ...bool) *Bitset {
+ b := &Bitset{numBits: 0, bits: make([]byte, 0)}
+ b.AppendBools(v...)
+
+ return b
+}
+
+// Clone returns a copy.
+func Clone(from *Bitset) *Bitset {
+ return &Bitset{numBits: from.numBits, bits: from.bits[:]}
+}
+
+// Substr returns a substring, consisting of the bits from indexes start to end.
+func (b *Bitset) Substr(start int, end int) *Bitset {
+ if start > end || end > b.numBits {
+ log.Panicf("Out of range start=%d end=%d numBits=%d", start, end, b.numBits)
+ }
+
+ result := New()
+ result.ensureCapacity(end - start)
+
+ for i := start; i < end; i++ {
+ if b.At(i) {
+ result.bits[result.numBits/8] |= 0x80 >> uint(result.numBits%8)
+ }
+ result.numBits++
+ }
+
+ return result
+}
+
+// NewFromBase2String constructs and returns a Bitset from a string. The string
+// consists of '1', '0' or ' ' characters, e.g. "1010 0101". The '1' and '0'
+// characters represent true/false bits respectively, and ' ' characters are
+// ignored.
+//
+// The function panics if the input string contains other characters.
+func NewFromBase2String(b2string string) *Bitset {
+ b := &Bitset{numBits: 0, bits: make([]byte, 0)}
+
+ for _, c := range b2string {
+ switch c {
+ case '1':
+ b.AppendBools(true)
+ case '0':
+ b.AppendBools(false)
+ case ' ':
+ default:
+ log.Panicf("Invalid char %c in NewFromBase2String", c)
+ }
+ }
+
+ return b
+}
+
+// AppendBytes appends a list of whole bytes.
+func (b *Bitset) AppendBytes(data []byte) {
+ for _, d := range data {
+ b.AppendByte(d, 8)
+ }
+}
+
+// AppendByte appends the numBits least significant bits from value.
+func (b *Bitset) AppendByte(value byte, numBits int) {
+ b.ensureCapacity(numBits)
+
+ if numBits > 8 {
+ log.Panicf("numBits %d out of range 0-8", numBits)
+ }
+
+ for i := numBits - 1; i >= 0; i-- {
+ if value&(1<<uint(i)) != 0 {
+ b.bits[b.numBits/8] |= 0x80 >> uint(b.numBits%8)
+ }
+
+ b.numBits++
+ }
+}
+
+// AppendUint32 appends the numBits least significant bits from value.
+func (b *Bitset) AppendUint32(value uint32, numBits int) {
+ b.ensureCapacity(numBits)
+
+ if numBits > 32 {
+ log.Panicf("numBits %d out of range 0-32", numBits)
+ }
+
+ for i := numBits - 1; i >= 0; i-- {
+ if value&(1<<uint(i)) != 0 {
+ b.bits[b.numBits/8] |= 0x80 >> uint(b.numBits%8)
+ }
+
+ b.numBits++
+ }
+}
+
+// ensureCapacity ensures the Bitset can store an additional |numBits|.
+//
+// The underlying array is expanded if necessary. To prevent frequent
+// reallocation, expanding the underlying array at least doubles its capacity.
+func (b *Bitset) ensureCapacity(numBits int) {
+ numBits += b.numBits
+
+ newNumBytes := numBits / 8
+ if numBits%8 != 0 {
+ newNumBytes++
+ }
+
+ if len(b.bits) >= newNumBytes {
+ return
+ }
+
+ b.bits = append(b.bits, make([]byte, newNumBytes+2*len(b.bits))...)
+}
+
+// Append bits copied from |other|.
+//
+// The new length is b.Len() + other.Len().
+func (b *Bitset) Append(other *Bitset) {
+ b.ensureCapacity(other.numBits)
+
+ for i := 0; i < other.numBits; i++ {
+ if other.At(i) {
+ b.bits[b.numBits/8] |= 0x80 >> uint(b.numBits%8)
+ }
+ b.numBits++
+ }
+}
+
+// AppendBools appends bits to the Bitset.
+func (b *Bitset) AppendBools(bits ...bool) {
+ b.ensureCapacity(len(bits))
+
+ for _, v := range bits {
+ if v {
+ b.bits[b.numBits/8] |= 0x80 >> uint(b.numBits%8)
+ }
+ b.numBits++
+ }
+}
+
+// AppendNumBools appends num bits of value value.
+func (b *Bitset) AppendNumBools(num int, value bool) {
+ for i := 0; i < num; i++ {
+ b.AppendBools(value)
+ }
+}
+
+// String returns a human readable representation of the Bitset's contents.
+func (b *Bitset) String() string {
+ var bitString string
+ for i := 0; i < b.numBits; i++ {
+ if (i % 8) == 0 {
+ bitString += " "
+ }
+
+ if (b.bits[i/8] & (0x80 >> byte(i%8))) != 0 {
+ bitString += "1"
+ } else {
+ bitString += "0"
+ }
+ }
+
+ return fmt.Sprintf("numBits=%d, bits=%s", b.numBits, bitString)
+}
+
+// Len returns the length of the Bitset in bits.
+func (b *Bitset) Len() int {
+ return b.numBits
+}
+
+// Bits returns the contents of the Bitset.
+func (b *Bitset) Bits() []bool {
+ result := make([]bool, b.numBits)
+
+ var i int
+ for i = 0; i < b.numBits; i++ {
+ result[i] = (b.bits[i/8] & (0x80 >> byte(i%8))) != 0
+ }
+
+ return result
+}
+
+// At returns the value of the bit at |index|.
+func (b *Bitset) At(index int) bool {
+ if index >= b.numBits {
+ log.Panicf("Index %d out of range", index)
+ }
+
+ return (b.bits[index/8] & (0x80 >> byte(index%8))) != 0
+}
+
+// Equals returns true if the Bitset equals other.
+func (b *Bitset) Equals(other *Bitset) bool {
+ if b.numBits != other.numBits {
+ return false
+ }
+
+ if !bytes.Equal(b.bits[0:b.numBits/8], other.bits[0:b.numBits/8]) {
+ return false
+ }
+
+ for i := 8 * (b.numBits / 8); i < b.numBits; i++ {
+ a := (b.bits[i/8] & (0x80 >> byte(i%8)))
+ b := (other.bits[i/8] & (0x80 >> byte(i%8)))
+
+ if a != b {
+ return false
+ }
+ }
+
+ return true
+}
+
+// ByteAt returns a byte consisting of upto 8 bits starting at index.
+func (b *Bitset) ByteAt(index int) byte {
+ if index < 0 || index >= b.numBits {
+ log.Panicf("Index %d out of range", index)
+ }
+
+ var result byte
+
+ for i := index; i < index+8 && i < b.numBits; i++ {
+ result <<= 1
+ if b.At(i) {
+ result |= 1
+ }
+ }
+
+ return result
+}
diff --git a/vendor/github.com/skip2/go-qrcode/encoder.go b/vendor/github.com/skip2/go-qrcode/encoder.go
new file mode 100644
index 0000000..713378c
--- /dev/null
+++ b/vendor/github.com/skip2/go-qrcode/encoder.go
@@ -0,0 +1,455 @@
+// go-qrcode
+// Copyright 2014 Tom Harwood
+
+package qrcode
+
+import (
+ "errors"
+ "log"
+
+ bitset "github.com/skip2/go-qrcode/bitset"
+)
+
+// Data encoding.
+//
+// The main data portion of a QR Code consists of one or more segments of data.
+// A segment consists of:
+//
+// - The segment Data Mode: numeric, alphanumeric, or byte.
+// - The length of segment in bits.
+// - Encoded data.
+//
+// For example, the string "123ZZ#!#!" may be represented as:
+//
+// [numeric, 3, "123"] [alphanumeric, 2, "ZZ"] [byte, 4, "#!#!"]
+//
+// Multiple data modes exist to minimise the size of encoded data. For example,
+// 8-bit bytes require 8 bits to encode each, but base 10 numeric data can be
+// encoded at a higher density of 3 numbers (e.g. 123) per 10 bits.
+//
+// Some data can be represented in multiple modes. Numeric data can be
+// represented in all three modes, whereas alphanumeric data (e.g. 'A') can be
+// represented in alphanumeric and byte mode.
+//
+// Starting a new segment (to use a different Data Mode) has a cost, the bits to
+// state the new segment Data Mode and length. To minimise each QR Code's symbol
+// size, an optimisation routine coalesces segment types where possible, to
+// reduce the encoded data length.
+//
+// There are several other data modes available (e.g. Kanji mode) which are not
+// implemented here.
+
+// A segment encoding mode.
+type dataMode uint8
+
+const (
+ // Each dataMode is a subset of the subsequent dataMode:
+ // dataModeNone < dataModeNumeric < dataModeAlphanumeric < dataModeByte
+ //
+ // This ordering is important for determining which data modes a character can
+ // be encoded with. E.g. 'E' can be encoded in both dataModeAlphanumeric and
+ // dataModeByte.
+ dataModeNone dataMode = 1 << iota
+ dataModeNumeric
+ dataModeAlphanumeric
+ dataModeByte
+)
+
+// dataModeString returns d as a short printable string.
+func dataModeString(d dataMode) string {
+ switch d {
+ case dataModeNone:
+ return "none"
+ case dataModeNumeric:
+ return "numeric"
+ case dataModeAlphanumeric:
+ return "alphanumeric"
+ case dataModeByte:
+ return "byte"
+ }
+
+ return "unknown"
+}
+
+type dataEncoderType uint8
+
+const (
+ dataEncoderType1To9 dataEncoderType = iota
+ dataEncoderType10To26
+ dataEncoderType27To40
+)
+
+// segment is a single segment of data.
+type segment struct {
+ // Data Mode (e.g. numeric).
+ dataMode dataMode
+
+ // segment data (e.g. "abc").
+ data []byte
+}
+
+// A dataEncoder encodes data for a particular QR Code version.
+type dataEncoder struct {
+ // Minimum & maximum versions supported.
+ minVersion int
+ maxVersion int
+
+ // Mode indicator bit sequences.
+ numericModeIndicator *bitset.Bitset
+ alphanumericModeIndicator *bitset.Bitset
+ byteModeIndicator *bitset.Bitset
+
+ // Character count lengths.
+ numNumericCharCountBits int
+ numAlphanumericCharCountBits int
+ numByteCharCountBits int
+
+ // The raw input data.
+ data []byte
+
+ // The data classified into unoptimised segments.
+ actual []segment
+
+ // The data classified into optimised segments.
+ optimised []segment
+}
+
+// newDataEncoder constructs a dataEncoder.
+func newDataEncoder(t dataEncoderType) *dataEncoder {
+ d := &dataEncoder{}
+
+ switch t {
+ case dataEncoderType1To9:
+ d = &dataEncoder{
+ minVersion: 1,
+ maxVersion: 9,
+ numericModeIndicator: bitset.New(b0, b0, b0, b1),
+ alphanumericModeIndicator: bitset.New(b0, b0, b1, b0),
+ byteModeIndicator: bitset.New(b0, b1, b0, b0),
+ numNumericCharCountBits: 10,
+ numAlphanumericCharCountBits: 9,
+ numByteCharCountBits: 8,
+ }
+ case dataEncoderType10To26:
+ d = &dataEncoder{
+ minVersion: 10,
+ maxVersion: 26,
+ numericModeIndicator: bitset.New(b0, b0, b0, b1),
+ alphanumericModeIndicator: bitset.New(b0, b0, b1, b0),
+ byteModeIndicator: bitset.New(b0, b1, b0, b0),
+ numNumericCharCountBits: 12,
+ numAlphanumericCharCountBits: 11,
+ numByteCharCountBits: 16,
+ }
+ case dataEncoderType27To40:
+ d = &dataEncoder{
+ minVersion: 27,
+ maxVersion: 40,
+ numericModeIndicator: bitset.New(b0, b0, b0, b1),
+ alphanumericModeIndicator: bitset.New(b0, b0, b1, b0),
+ byteModeIndicator: bitset.New(b0, b1, b0, b0),
+ numNumericCharCountBits: 14,
+ numAlphanumericCharCountBits: 13,
+ numByteCharCountBits: 16,
+ }
+ default:
+ log.Panic("Unknown dataEncoderType")
+ }
+
+ return d
+}
+
+// encode data as one or more segments and return the encoded data.
+//
+// The returned data does not include the terminator bit sequence.
+func (d *dataEncoder) encode(data []byte) (*bitset.Bitset, error) {
+ d.data = data
+ d.actual = nil
+ d.optimised = nil
+
+ if len(data) == 0 {
+ return nil, errors.New("no data to encode")
+ }
+
+ // Classify data into unoptimised segments.
+ d.classifyDataModes()
+
+ // Optimise segments.
+ err := d.optimiseDataModes()
+ if err != nil {
+ return nil, err
+ }
+
+ // Encode data.
+ encoded := bitset.New()
+ for _, s := range d.optimised {
+ d.encodeDataRaw(s.data, s.dataMode, encoded)
+ }
+
+ return encoded, nil
+}
+
+// classifyDataModes classifies the raw data into unoptimised segments.
+// e.g. "123ZZ#!#!" =>
+// [numeric, 3, "123"] [alphanumeric, 2, "ZZ"] [byte, 4, "#!#!"].
+func (d *dataEncoder) classifyDataModes() {
+ var start int
+ mode := dataModeNone
+
+ for i, v := range d.data {
+ newMode := dataModeNone
+ switch {
+ case v >= 0x30 && v <= 0x39:
+ newMode = dataModeNumeric
+ case v == 0x20 || v == 0x24 || v == 0x25 || v == 0x2a || v == 0x2b || v ==
+ 0x2d || v == 0x2e || v == 0x2f || v == 0x3a || (v >= 0x41 && v <= 0x5a):
+ newMode = dataModeAlphanumeric
+ default:
+ newMode = dataModeByte
+ }
+
+ if newMode != mode {
+ if i > 0 {
+ d.actual = append(d.actual, segment{dataMode: mode, data: d.data[start:i]})
+
+ start = i
+ }
+
+ mode = newMode
+ }
+ }
+
+ d.actual = append(d.actual, segment{dataMode: mode, data: d.data[start:len(d.data)]})
+}
+
+// optimiseDataModes optimises the list of segments to reduce the overall output
+// encoded data length.
+//
+// The algorithm coalesces adjacent segments. segments are only coalesced when
+// the Data Modes are compatible, and when the coalesced segment has a shorter
+// encoded length than separate segments.
+//
+// Multiple segments may be coalesced. For example a string of alternating
+// alphanumeric/numeric segments ANANANANA can be optimised to just A.
+func (d *dataEncoder) optimiseDataModes() error {
+ for i := 0; i < len(d.actual); {
+ mode := d.actual[i].dataMode
+ numChars := len(d.actual[i].data)
+
+ j := i + 1
+ for j < len(d.actual) {
+ nextNumChars := len(d.actual[j].data)
+ nextMode := d.actual[j].dataMode
+
+ if nextMode > mode {
+ break
+ }
+
+ coalescedLength, err := d.encodedLength(mode, numChars+nextNumChars)
+
+ if err != nil {
+ return err
+ }
+
+ seperateLength1, err := d.encodedLength(mode, numChars)
+
+ if err != nil {
+ return err
+ }
+
+ seperateLength2, err := d.encodedLength(nextMode, nextNumChars)
+
+ if err != nil {
+ return err
+ }
+
+ if coalescedLength < seperateLength1+seperateLength2 {
+ j++
+ numChars += nextNumChars
+ } else {
+ break
+ }
+ }
+
+ optimised := segment{dataMode: mode,
+ data: make([]byte, 0, numChars)}
+
+ for k := i; k < j; k++ {
+ optimised.data = append(optimised.data, d.actual[k].data...)
+ }
+
+ d.optimised = append(d.optimised, optimised)
+
+ i = j
+ }
+
+ return nil
+}
+
+// encodeDataRaw encodes data in dataMode. The encoded data is appended to
+// encoded.
+func (d *dataEncoder) encodeDataRaw(data []byte, dataMode dataMode, encoded *bitset.Bitset) {
+ modeIndicator := d.modeIndicator(dataMode)
+ charCountBits := d.charCountBits(dataMode)
+
+ // Append mode indicator.
+ encoded.Append(modeIndicator)
+
+ // Append character count.
+ encoded.AppendUint32(uint32(len(data)), charCountBits)
+
+ // Append data.
+ switch dataMode {
+ case dataModeNumeric:
+ for i := 0; i < len(data); i += 3 {
+ charsRemaining := len(data) - i
+
+ var value uint32
+ bitsUsed := 1
+
+ for j := 0; j < charsRemaining && j < 3; j++ {
+ value *= 10
+ value += uint32(data[i+j] - 0x30)
+ bitsUsed += 3
+ }
+ encoded.AppendUint32(value, bitsUsed)
+ }
+ case dataModeAlphanumeric:
+ for i := 0; i < len(data); i += 2 {
+ charsRemaining := len(data) - i
+
+ var value uint32
+ for j := 0; j < charsRemaining && j < 2; j++ {
+ value *= 45
+ value += encodeAlphanumericCharacter(data[i+j])
+ }
+
+ bitsUsed := 6
+ if charsRemaining > 1 {
+ bitsUsed = 11
+ }
+
+ encoded.AppendUint32(value, bitsUsed)
+ }
+ case dataModeByte:
+ for _, b := range data {
+ encoded.AppendByte(b, 8)
+ }
+ }
+}
+
+// modeIndicator returns the segment header bits for a segment of type dataMode.
+func (d *dataEncoder) modeIndicator(dataMode dataMode) *bitset.Bitset {
+ switch dataMode {
+ case dataModeNumeric:
+ return d.numericModeIndicator
+ case dataModeAlphanumeric:
+ return d.alphanumericModeIndicator
+ case dataModeByte:
+ return d.byteModeIndicator
+ default:
+ log.Panic("Unknown data mode")
+ }
+
+ return nil
+}
+
+// charCountBits returns the number of bits used to encode the length of a data
+// segment of type dataMode.
+func (d *dataEncoder) charCountBits(dataMode dataMode) int {
+ switch dataMode {
+ case dataModeNumeric:
+ return d.numNumericCharCountBits
+ case dataModeAlphanumeric:
+ return d.numAlphanumericCharCountBits
+ case dataModeByte:
+ return d.numByteCharCountBits
+ default:
+ log.Panic("Unknown data mode")
+ }
+
+ return 0
+}
+
+// encodedLength returns the number of bits required to encode n symbols in
+// dataMode.
+//
+// The number of bits required is affected by:
+// - QR code type - Mode Indicator length.
+// - Data mode - number of bits used to represent data length.
+// - Data mode - how the data is encoded.
+// - Number of symbols encoded.
+//
+// An error is returned if the mode is not supported, or the length requested is
+// too long to be represented.
+func (d *dataEncoder) encodedLength(dataMode dataMode, n int) (int, error) {
+ modeIndicator := d.modeIndicator(dataMode)
+ charCountBits := d.charCountBits(dataMode)
+
+ if modeIndicator == nil {
+ return 0, errors.New("mode not supported")
+ }
+
+ maxLength := (1 << uint8(charCountBits)) - 1
+
+ if n > maxLength {
+ return 0, errors.New("length too long to be represented")
+ }
+
+ length := modeIndicator.Len() + charCountBits
+
+ switch dataMode {
+ case dataModeNumeric:
+ length += 10 * (n / 3)
+
+ if n%3 != 0 {
+ length += 1 + 3*(n%3)
+ }
+ case dataModeAlphanumeric:
+ length += 11 * (n / 2)
+ length += 6 * (n % 2)
+ case dataModeByte:
+ length += 8 * n
+ }
+
+ return length, nil
+}
+
+// encodeAlphanumericChar returns the QR Code encoded value of v.
+//
+// v must be a QR Code defined alphanumeric character: 0-9, A-Z, SP, $%*+-./ or
+// :. The characters are mapped to values in the range 0-44 respectively.
+func encodeAlphanumericCharacter(v byte) uint32 {
+ c := uint32(v)
+
+ switch {
+ case c >= '0' && c <= '9':
+ // 0-9 encoded as 0-9.
+ return c - '0'
+ case c >= 'A' && c <= 'Z':
+ // A-Z encoded as 10-35.
+ return c - 'A' + 10
+ case c == ' ':
+ return 36
+ case c == '$':
+ return 37
+ case c == '%':
+ return 38
+ case c == '*':
+ return 39
+ case c == '+':
+ return 40
+ case c == '-':
+ return 41
+ case c == '.':
+ return 42
+ case c == '/':
+ return 43
+ case c == ':':
+ return 44
+ default:
+ log.Panicf("encodeAlphanumericCharacter() with non alphanumeric char %v.", v)
+ }
+
+ return 0
+}
diff --git a/vendor/github.com/skip2/go-qrcode/qrcode.go b/vendor/github.com/skip2/go-qrcode/qrcode.go
new file mode 100644
index 0000000..49f8b4b
--- /dev/null
+++ b/vendor/github.com/skip2/go-qrcode/qrcode.go
@@ -0,0 +1,459 @@
+// go-qrcode
+// Copyright 2014 Tom Harwood
+
+/*
+Package qrcode implements a QR Code encoder.
+
+A QR Code is a matrix (two-dimensional) barcode. Arbitrary content may be
+encoded.
+
+A QR Code contains error recovery information to aid reading damaged or
+obscured codes. There are four levels of error recovery: qrcode.{Low, Medium,
+High, Highest}. QR Codes with a higher recovery level are more robust to damage,
+at the cost of being physically larger.
+
+Two functions cover most use cases:
+
+- Create a PNG image:
+
+ var png []byte
+ png, err := qrcode.Encode("https://example.org", qrcode.Medium, 256)
+
+- Create a PNG image and write to a file:
+
+ err := qrcode.WriteFile("https://example.org", qrcode.Medium, 256, "qr.png")
+
+Both examples use the qrcode.Medium error Recovery Level and create a 256x256
+pixel, black on white QR Code.
+
+The maximum capacity of a QR Code varies according to the content encoded and
+the error recovery level. The maximum capacity is 2,953 bytes, 4,296
+alphanumeric characters, 7,089 numeric digits, or a combination of these.
+
+This package implements a subset of QR Code 2005, as defined in ISO/IEC
+18004:2006.
+*/
+package qrcode
+
+import (
+ "bytes"
+ "errors"
+ "image"
+ "image/color"
+ "image/png"
+ "io/ioutil"
+ "log"
+ "os"
+
+ bitset "github.com/skip2/go-qrcode/bitset"
+ reedsolomon "github.com/skip2/go-qrcode/reedsolomon"
+)
+
+// Encode a QR Code and return a raw PNG image.
+//
+// size is both the image width and height in pixels. If size is too small then
+// a larger image is silently returned.
+//
+// To serve over HTTP, remember to send a Content-Type: image/png header.
+func Encode(content string, level RecoveryLevel, size int) ([]byte, error) {
+ var q *QRCode
+
+ q, err := New(content, level)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return q.PNG(size)
+}
+
+// WriteFile encodes, then writes a QR Code to the given filename in PNG format.
+//
+// size is both the width and height in pixels. If size is too small then a
+// larger image is silently written.
+func WriteFile(content string, level RecoveryLevel, size int, filename string) error {
+ var q *QRCode
+
+ q, err := New(content, level)
+
+ if err != nil {
+ return err
+ }
+
+ return q.WriteFile(size, filename)
+}
+
+// A QRCode represents a valid encoded QRCode.
+type QRCode struct {
+ // Original content encoded.
+ Content string
+
+ // QR Code type.
+ Level RecoveryLevel
+ VersionNumber int
+
+ // User settable drawing options.
+ ForegroundColor color.Color
+ BackgroundColor color.Color
+
+ encoder *dataEncoder
+ version qrCodeVersion
+
+ data *bitset.Bitset
+ symbol *symbol
+ mask int
+}
+
+// New constructs a QRCode.
+//
+// var q *qrcode.QRCode
+// q, err := qrcode.New("my content", qrcode.Medium)
+//
+// An error occurs if the content is too long.
+func New(content string, level RecoveryLevel) (*QRCode, error) {
+ encoders := []dataEncoderType{dataEncoderType1To9, dataEncoderType10To26,
+ dataEncoderType27To40}
+
+ var encoder *dataEncoder
+ var encoded *bitset.Bitset
+ var chosenVersion *qrCodeVersion
+ var err error
+
+ for _, t := range encoders {
+ encoder = newDataEncoder(t)
+ encoded, err = encoder.encode([]byte(content))
+
+ if err != nil {
+ continue
+ }
+
+ chosenVersion = chooseQRCodeVersion(level, encoder, encoded.Len())
+
+ if chosenVersion != nil {
+ break
+ }
+ }
+
+ if err != nil {
+ return nil, err
+ } else if chosenVersion == nil {
+ return nil, errors.New("content too long to encode")
+ }
+
+ q := &QRCode{
+ Content: content,
+
+ Level: level,
+ VersionNumber: chosenVersion.version,
+
+ ForegroundColor: color.Black,
+ BackgroundColor: color.White,
+
+ encoder: encoder,
+ data: encoded,
+ version: *chosenVersion,
+ }
+
+ q.encode(chosenVersion.numTerminatorBitsRequired(encoded.Len()))
+
+ return q, nil
+}
+
+func newWithForcedVersion(content string, version int, level RecoveryLevel) (*QRCode, error) {
+ var encoder *dataEncoder
+
+ switch {
+ case version >= 1 && version <= 9:
+ encoder = newDataEncoder(dataEncoderType1To9)
+ case version >= 10 && version <= 26:
+ encoder = newDataEncoder(dataEncoderType10To26)
+ case version >= 27 && version <= 40:
+ encoder = newDataEncoder(dataEncoderType27To40)
+ default:
+ log.Fatalf("Invalid version %d (expected 1-40 inclusive)", version)
+ }
+
+ var encoded *bitset.Bitset
+ encoded, err := encoder.encode([]byte(content))
+
+ if err != nil {
+ return nil, err
+ }
+
+ chosenVersion := getQRCodeVersion(level, version)
+
+ if chosenVersion == nil {
+ return nil, errors.New("cannot find QR Code version")
+ }
+
+ q := &QRCode{
+ Content: content,
+
+ Level: level,
+ VersionNumber: chosenVersion.version,
+
+ ForegroundColor: color.Black,
+ BackgroundColor: color.White,
+
+ encoder: encoder,
+ data: encoded,
+ version: *chosenVersion,
+ }
+
+ q.encode(chosenVersion.numTerminatorBitsRequired(encoded.Len()))
+
+ return q, nil
+}
+
+// Bitmap returns the QR Code as a 2D array of 1-bit pixels.
+//
+// bitmap[y][x] is true if the pixel at (x, y) is set.
+//
+// The bitmap includes the required "quiet zone" around the QR Code to aid
+// decoding.
+func (q *QRCode) Bitmap() [][]bool {
+ return q.symbol.bitmap()
+}
+
+// Image returns the QR Code as an image.Image.
+//
+// size is both the width and height in pixels.
+func (q *QRCode) Image(size int) image.Image {
+ // Minimum pixels (both width and height) required.
+ realSize := q.symbol.size
+
+ // Actual pixels available to draw the symbol. Automatically increase the
+ // image size if it's not large enough.
+ if size < realSize {
+ size = realSize
+ }
+
+ // Size of each module drawn.
+ pixelsPerModule := size / realSize
+
+ // Center the symbol within the image.
+ offset := (size - realSize*pixelsPerModule) / 2
+
+ rect := image.Rectangle{Min: image.Point{0, 0}, Max: image.Point{size, size}}
+ img := image.NewRGBA(rect)
+
+ for i := 0; i < size; i++ {
+ for j := 0; j < size; j++ {
+ img.Set(i, j, q.BackgroundColor)
+ }
+ }
+
+ bitmap := q.symbol.bitmap()
+ for y, row := range bitmap {
+ for x, v := range row {
+ if v {
+ startX := x*pixelsPerModule + offset
+ startY := y*pixelsPerModule + offset
+ for i := startX; i < startX+pixelsPerModule; i++ {
+ for j := startY; j < startY+pixelsPerModule; j++ {
+ img.Set(i, j, q.ForegroundColor)
+ }
+ }
+ }
+ }
+ }
+
+ return img
+}
+
+// PNG returns the QR Code as a PNG image.
+//
+// size is both the image width and height in pixels. If size is too small then
+// a larger image is silently returned.
+func (q *QRCode) PNG(size int) ([]byte, error) {
+ img := q.Image(size)
+
+ var b bytes.Buffer
+ err := png.Encode(&b, img)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return b.Bytes(), nil
+}
+
+// WriteFile writes the QR Code as a PNG image to the specified file.
+//
+// size is both the image width and height in pixels. If size is too small then
+// a larger image is silently written.
+func (q *QRCode) WriteFile(size int, filename string) error {
+ var png []byte
+
+ png, err := q.PNG(size)
+
+ if err != nil {
+ return err
+ }
+
+ return ioutil.WriteFile(filename, png, os.FileMode(0644))
+}
+
+// encode completes the steps required to encode the QR Code. These include
+// adding the terminator bits and padding, splitting the data into blocks and
+// applying the error correction, and selecting the best data mask.
+func (q *QRCode) encode(numTerminatorBits int) {
+ q.addTerminatorBits(numTerminatorBits)
+ q.addPadding()
+
+ encoded := q.encodeBlocks()
+
+ const numMasks int = 8
+ penalty := 0
+
+ for mask := 0; mask < numMasks; mask++ {
+ var s *symbol
+ var err error
+
+ s, err = buildRegularSymbol(q.version, mask, encoded)
+
+ if err != nil {
+ log.Panic(err.Error())
+ }
+
+ numEmptyModules := s.numEmptyModules()
+ if numEmptyModules != 0 {
+ log.Panicf("bug: numEmptyModules is %d (expected 0) (version=%d)",
+ numEmptyModules, q.VersionNumber)
+ }
+
+ p := s.penaltyScore()
+
+ //log.Printf("mask=%d p=%3d p1=%3d p2=%3d p3=%3d p4=%d\n", mask, p, s.penalty1(), s.penalty2(), s.penalty3(), s.penalty4())
+
+ if q.symbol == nil || p < penalty {
+ q.symbol = s
+ q.mask = mask
+ penalty = p
+ }
+ }
+}
+
+// addTerminatorBits adds final terminator bits to the encoded data.
+//
+// The number of terminator bits required is determined when the QR Code version
+// is chosen (which itself depends on the length of the data encoded). The
+// terminator bits are thus added after the QR Code version
+// is chosen, rather than at the data encoding stage.
+func (q *QRCode) addTerminatorBits(numTerminatorBits int) {
+ q.data.AppendNumBools(numTerminatorBits, false)
+}
+
+// encodeBlocks takes the completed (terminated & padded) encoded data, splits
+// the data into blocks (as specified by the QR Code version), applies error
+// correction to each block, then interleaves the blocks together.
+//
+// The QR Code's final data sequence is returned.
+func (q *QRCode) encodeBlocks() *bitset.Bitset {
+ // Split into blocks.
+ type dataBlock struct {
+ data *bitset.Bitset
+ ecStartOffset int
+ }
+
+ block := make([]dataBlock, q.version.numBlocks())
+
+ start := 0
+ end := 0
+ blockID := 0
+
+ for _, b := range q.version.block {
+ for j := 0; j < b.numBlocks; j++ {
+ start = end
+ end = start + b.numDataCodewords*8
+
+ // Apply error correction to each block.
+ numErrorCodewords := b.numCodewords - b.numDataCodewords
+ block[blockID].data = reedsolomon.Encode(q.data.Substr(start, end), numErrorCodewords)
+ block[blockID].ecStartOffset = end - start
+
+ blockID++
+ }
+ }
+
+ // Interleave the blocks.
+
+ result := bitset.New()
+
+ // Combine data blocks.
+ working := true
+ for i := 0; working; i += 8 {
+ working = false
+
+ for j, b := range block {
+ if i >= block[j].ecStartOffset {
+ continue
+ }
+
+ result.Append(b.data.Substr(i, i+8))
+
+ working = true
+ }
+ }
+
+ // Combine error correction blocks.
+ working = true
+ for i := 0; working; i += 8 {
+ working = false
+
+ for j, b := range block {
+ offset := i + block[j].ecStartOffset
+ if offset >= block[j].data.Len() {
+ continue
+ }
+
+ result.Append(b.data.Substr(offset, offset+8))
+
+ working = true
+ }
+ }
+
+ // Append remainder bits.
+ result.AppendNumBools(q.version.numRemainderBits, false)
+
+ return result
+}
+
+// max returns the maximum of a and b.
+func max(a int, b int) int {
+ if a > b {
+ return a
+ }
+
+ return b
+}
+
+// addPadding pads the encoded data upto the full length required.
+func (q *QRCode) addPadding() {
+ numDataBits := q.version.numDataBits()
+
+ if q.data.Len() == numDataBits {
+ return
+ }
+
+ // Pad to the nearest codeword boundary.
+ q.data.AppendNumBools(q.version.numBitsToPadToCodeword(q.data.Len()), false)
+
+ // Pad codewords 0b11101100 and 0b00010001.
+ padding := [2]*bitset.Bitset{
+ bitset.New(true, true, true, false, true, true, false, false),
+ bitset.New(false, false, false, true, false, false, false, true),
+ }
+
+ // Insert pad codewords alternately.
+ i := 0
+ for numDataBits-q.data.Len() >= 8 {
+ q.data.Append(padding[i])
+
+ i = 1 - i // Alternate between 0 and 1.
+ }
+
+ if q.data.Len() != numDataBits {
+ log.Panicf("BUG: got len %d, expected %d", q.data.Len(), numDataBits)
+ }
+}
diff --git a/vendor/github.com/skip2/go-qrcode/reedsolomon/gf2_8.go b/vendor/github.com/skip2/go-qrcode/reedsolomon/gf2_8.go
new file mode 100644
index 0000000..6a7003f
--- /dev/null
+++ b/vendor/github.com/skip2/go-qrcode/reedsolomon/gf2_8.go
@@ -0,0 +1,387 @@
+// go-qrcode
+// Copyright 2014 Tom Harwood
+
+package reedsolomon
+
+// Addition, subtraction, multiplication, and division in GF(2^8).
+// Operations are performed modulo x^8 + x^4 + x^3 + x^2 + 1.
+
+// http://en.wikipedia.org/wiki/Finite_field_arithmetic
+
+import "log"
+
+const (
+ gfZero = gfElement(0)
+ gfOne = gfElement(1)
+)
+
+var (
+ gfExpTable = [256]gfElement{
+ /* 0 - 9 */ 1, 2, 4, 8, 16, 32, 64, 128, 29, 58,
+ /* 10 - 19 */ 116, 232, 205, 135, 19, 38, 76, 152, 45, 90,
+ /* 20 - 29 */ 180, 117, 234, 201, 143, 3, 6, 12, 24, 48,
+ /* 30 - 39 */ 96, 192, 157, 39, 78, 156, 37, 74, 148, 53,
+ /* 40 - 49 */ 106, 212, 181, 119, 238, 193, 159, 35, 70, 140,
+ /* 50 - 59 */ 5, 10, 20, 40, 80, 160, 93, 186, 105, 210,
+ /* 60 - 69 */ 185, 111, 222, 161, 95, 190, 97, 194, 153, 47,
+ /* 70 - 79 */ 94, 188, 101, 202, 137, 15, 30, 60, 120, 240,
+ /* 80 - 89 */ 253, 231, 211, 187, 107, 214, 177, 127, 254, 225,
+ /* 90 - 99 */ 223, 163, 91, 182, 113, 226, 217, 175, 67, 134,
+ /* 100 - 109 */ 17, 34, 68, 136, 13, 26, 52, 104, 208, 189,
+ /* 110 - 119 */ 103, 206, 129, 31, 62, 124, 248, 237, 199, 147,
+ /* 120 - 129 */ 59, 118, 236, 197, 151, 51, 102, 204, 133, 23,
+ /* 130 - 139 */ 46, 92, 184, 109, 218, 169, 79, 158, 33, 66,
+ /* 140 - 149 */ 132, 21, 42, 84, 168, 77, 154, 41, 82, 164,
+ /* 150 - 159 */ 85, 170, 73, 146, 57, 114, 228, 213, 183, 115,
+ /* 160 - 169 */ 230, 209, 191, 99, 198, 145, 63, 126, 252, 229,
+ /* 170 - 179 */ 215, 179, 123, 246, 241, 255, 227, 219, 171, 75,
+ /* 180 - 189 */ 150, 49, 98, 196, 149, 55, 110, 220, 165, 87,
+ /* 190 - 199 */ 174, 65, 130, 25, 50, 100, 200, 141, 7, 14,
+ /* 200 - 209 */ 28, 56, 112, 224, 221, 167, 83, 166, 81, 162,
+ /* 210 - 219 */ 89, 178, 121, 242, 249, 239, 195, 155, 43, 86,
+ /* 220 - 229 */ 172, 69, 138, 9, 18, 36, 72, 144, 61, 122,
+ /* 230 - 239 */ 244, 245, 247, 243, 251, 235, 203, 139, 11, 22,
+ /* 240 - 249 */ 44, 88, 176, 125, 250, 233, 207, 131, 27, 54,
+ /* 250 - 255 */ 108, 216, 173, 71, 142, 1}
+
+ gfLogTable = [256]int{
+ /* 0 - 9 */ -1, 0, 1, 25, 2, 50, 26, 198, 3, 223,
+ /* 10 - 19 */ 51, 238, 27, 104, 199, 75, 4, 100, 224, 14,
+ /* 20 - 29 */ 52, 141, 239, 129, 28, 193, 105, 248, 200, 8,
+ /* 30 - 39 */ 76, 113, 5, 138, 101, 47, 225, 36, 15, 33,
+ /* 40 - 49 */ 53, 147, 142, 218, 240, 18, 130, 69, 29, 181,
+ /* 50 - 59 */ 194, 125, 106, 39, 249, 185, 201, 154, 9, 120,
+ /* 60 - 69 */ 77, 228, 114, 166, 6, 191, 139, 98, 102, 221,
+ /* 70 - 79 */ 48, 253, 226, 152, 37, 179, 16, 145, 34, 136,
+ /* 80 - 89 */ 54, 208, 148, 206, 143, 150, 219, 189, 241, 210,
+ /* 90 - 99 */ 19, 92, 131, 56, 70, 64, 30, 66, 182, 163,
+ /* 100 - 109 */ 195, 72, 126, 110, 107, 58, 40, 84, 250, 133,
+ /* 110 - 119 */ 186, 61, 202, 94, 155, 159, 10, 21, 121, 43,
+ /* 120 - 129 */ 78, 212, 229, 172, 115, 243, 167, 87, 7, 112,
+ /* 130 - 139 */ 192, 247, 140, 128, 99, 13, 103, 74, 222, 237,
+ /* 140 - 149 */ 49, 197, 254, 24, 227, 165, 153, 119, 38, 184,
+ /* 150 - 159 */ 180, 124, 17, 68, 146, 217, 35, 32, 137, 46,
+ /* 160 - 169 */ 55, 63, 209, 91, 149, 188, 207, 205, 144, 135,
+ /* 170 - 179 */ 151, 178, 220, 252, 190, 97, 242, 86, 211, 171,
+ /* 180 - 189 */ 20, 42, 93, 158, 132, 60, 57, 83, 71, 109,
+ /* 190 - 199 */ 65, 162, 31, 45, 67, 216, 183, 123, 164, 118,
+ /* 200 - 209 */ 196, 23, 73, 236, 127, 12, 111, 246, 108, 161,
+ /* 210 - 219 */ 59, 82, 41, 157, 85, 170, 251, 96, 134, 177,
+ /* 220 - 229 */ 187, 204, 62, 90, 203, 89, 95, 176, 156, 169,
+ /* 230 - 239 */ 160, 81, 11, 245, 22, 235, 122, 117, 44, 215,
+ /* 240 - 249 */ 79, 174, 213, 233, 230, 231, 173, 232, 116, 214,
+ /* 250 - 255 */ 244, 234, 168, 80, 88, 175}
+)
+
+// gfElement is an element in GF(2^8).
+type gfElement uint8
+
+// newGFElement creates and returns a new gfElement.
+func newGFElement(data byte) gfElement {
+ return gfElement(data)
+}
+
+// gfAdd returns a + b.
+func gfAdd(a, b gfElement) gfElement {
+ return a ^ b
+}
+
+// gfSub returns a - b.
+//
+// Note addition is equivalent to subtraction in GF(2).
+func gfSub(a, b gfElement) gfElement {
+ return a ^ b
+}
+
+// gfMultiply returns a * b.
+func gfMultiply(a, b gfElement) gfElement {
+ if a == gfZero || b == gfZero {
+ return gfZero
+ }
+
+ return gfExpTable[(gfLogTable[a]+gfLogTable[b])%255]
+}
+
+// gfDivide returns a / b.
+//
+// Divide by zero results in a panic.
+func gfDivide(a, b gfElement) gfElement {
+ if a == gfZero {
+ return gfZero
+ } else if b == gfZero {
+ log.Panicln("Divide by zero")
+ }
+
+ return gfMultiply(a, gfInverse(b))
+}
+
+// gfInverse returns the multiplicative inverse of a, a^-1.
+//
+// a * a^-1 = 1
+func gfInverse(a gfElement) gfElement {
+ if a == gfZero {
+ log.Panicln("No multiplicative inverse of 0")
+ }
+
+ return gfExpTable[255-gfLogTable[a]]
+}
+
+// a^i | bits | polynomial | decimal
+// --------------------------------------------------------------------------
+// 0 | 000000000 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 0
+// a^0 | 000000001 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 1
+// a^1 | 000000010 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 2
+// a^2 | 000000100 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 4
+// a^3 | 000001000 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 8
+// a^4 | 000010000 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 16
+// a^5 | 000100000 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 32
+// a^6 | 001000000 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 64
+// a^7 | 010000000 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 128
+// a^8 | 000011101 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 29
+// a^9 | 000111010 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 58
+// a^10 | 001110100 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 116
+// a^11 | 011101000 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 232
+// a^12 | 011001101 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 205
+// a^13 | 010000111 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 135
+// a^14 | 000010011 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 19
+// a^15 | 000100110 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 38
+// a^16 | 001001100 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 76
+// a^17 | 010011000 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 152
+// a^18 | 000101101 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 45
+// a^19 | 001011010 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 90
+// a^20 | 010110100 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 180
+// a^21 | 001110101 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 117
+// a^22 | 011101010 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 234
+// a^23 | 011001001 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 201
+// a^24 | 010001111 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 143
+// a^25 | 000000011 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 3
+// a^26 | 000000110 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 6
+// a^27 | 000001100 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 12
+// a^28 | 000011000 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 24
+// a^29 | 000110000 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 48
+// a^30 | 001100000 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 96
+// a^31 | 011000000 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 192
+// a^32 | 010011101 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 157
+// a^33 | 000100111 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 39
+// a^34 | 001001110 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 78
+// a^35 | 010011100 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 156
+// a^36 | 000100101 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 37
+// a^37 | 001001010 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 74
+// a^38 | 010010100 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 148
+// a^39 | 000110101 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 53
+// a^40 | 001101010 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 106
+// a^41 | 011010100 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 212
+// a^42 | 010110101 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 181
+// a^43 | 001110111 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 119
+// a^44 | 011101110 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 238
+// a^45 | 011000001 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 193
+// a^46 | 010011111 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 159
+// a^47 | 000100011 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 35
+// a^48 | 001000110 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 70
+// a^49 | 010001100 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 140
+// a^50 | 000000101 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 5
+// a^51 | 000001010 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 10
+// a^52 | 000010100 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 20
+// a^53 | 000101000 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 40
+// a^54 | 001010000 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 80
+// a^55 | 010100000 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 160
+// a^56 | 001011101 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 93
+// a^57 | 010111010 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 186
+// a^58 | 001101001 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 105
+// a^59 | 011010010 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 210
+// a^60 | 010111001 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 185
+// a^61 | 001101111 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 111
+// a^62 | 011011110 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 222
+// a^63 | 010100001 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 161
+// a^64 | 001011111 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 95
+// a^65 | 010111110 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 190
+// a^66 | 001100001 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 97
+// a^67 | 011000010 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 194
+// a^68 | 010011001 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 153
+// a^69 | 000101111 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 47
+// a^70 | 001011110 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 94
+// a^71 | 010111100 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 188
+// a^72 | 001100101 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 101
+// a^73 | 011001010 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 202
+// a^74 | 010001001 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 137
+// a^75 | 000001111 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 15
+// a^76 | 000011110 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 30
+// a^77 | 000111100 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 60
+// a^78 | 001111000 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 120
+// a^79 | 011110000 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 240
+// a^80 | 011111101 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 253
+// a^81 | 011100111 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 231
+// a^82 | 011010011 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 211
+// a^83 | 010111011 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 187
+// a^84 | 001101011 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 107
+// a^85 | 011010110 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 214
+// a^86 | 010110001 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 177
+// a^87 | 001111111 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 127
+// a^88 | 011111110 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 254
+// a^89 | 011100001 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 225
+// a^90 | 011011111 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 223
+// a^91 | 010100011 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 163
+// a^92 | 001011011 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 91
+// a^93 | 010110110 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 182
+// a^94 | 001110001 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 113
+// a^95 | 011100010 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 226
+// a^96 | 011011001 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 217
+// a^97 | 010101111 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 175
+// a^98 | 001000011 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 67
+// a^99 | 010000110 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 134
+// a^100 | 000010001 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 17
+// a^101 | 000100010 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 34
+// a^102 | 001000100 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 68
+// a^103 | 010001000 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 136
+// a^104 | 000001101 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 13
+// a^105 | 000011010 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 26
+// a^106 | 000110100 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 52
+// a^107 | 001101000 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 104
+// a^108 | 011010000 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 208
+// a^109 | 010111101 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 189
+// a^110 | 001100111 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 103
+// a^111 | 011001110 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 206
+// a^112 | 010000001 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 129
+// a^113 | 000011111 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 31
+// a^114 | 000111110 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 62
+// a^115 | 001111100 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 124
+// a^116 | 011111000 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 248
+// a^117 | 011101101 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 237
+// a^118 | 011000111 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 199
+// a^119 | 010010011 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 147
+// a^120 | 000111011 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 59
+// a^121 | 001110110 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 118
+// a^122 | 011101100 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 236
+// a^123 | 011000101 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 197
+// a^124 | 010010111 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 151
+// a^125 | 000110011 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 51
+// a^126 | 001100110 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 102
+// a^127 | 011001100 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 204
+// a^128 | 010000101 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 133
+// a^129 | 000010111 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 23
+// a^130 | 000101110 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 46
+// a^131 | 001011100 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 92
+// a^132 | 010111000 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 184
+// a^133 | 001101101 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 109
+// a^134 | 011011010 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 218
+// a^135 | 010101001 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 169
+// a^136 | 001001111 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 79
+// a^137 | 010011110 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 158
+// a^138 | 000100001 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 33
+// a^139 | 001000010 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 66
+// a^140 | 010000100 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 132
+// a^141 | 000010101 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 21
+// a^142 | 000101010 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 42
+// a^143 | 001010100 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 84
+// a^144 | 010101000 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 168
+// a^145 | 001001101 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 77
+// a^146 | 010011010 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 154
+// a^147 | 000101001 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 41
+// a^148 | 001010010 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 82
+// a^149 | 010100100 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 164
+// a^150 | 001010101 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 85
+// a^151 | 010101010 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 170
+// a^152 | 001001001 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 73
+// a^153 | 010010010 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 146
+// a^154 | 000111001 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 57
+// a^155 | 001110010 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 114
+// a^156 | 011100100 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 228
+// a^157 | 011010101 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 213
+// a^158 | 010110111 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 183
+// a^159 | 001110011 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 115
+// a^160 | 011100110 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 230
+// a^161 | 011010001 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 209
+// a^162 | 010111111 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 191
+// a^163 | 001100011 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 99
+// a^164 | 011000110 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 198
+// a^165 | 010010001 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 145
+// a^166 | 000111111 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 63
+// a^167 | 001111110 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 126
+// a^168 | 011111100 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 252
+// a^169 | 011100101 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 229
+// a^170 | 011010111 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 215
+// a^171 | 010110011 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 179
+// a^172 | 001111011 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 123
+// a^173 | 011110110 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 246
+// a^174 | 011110001 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 241
+// a^175 | 011111111 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 255
+// a^176 | 011100011 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 227
+// a^177 | 011011011 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 219
+// a^178 | 010101011 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 171
+// a^179 | 001001011 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 75
+// a^180 | 010010110 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 150
+// a^181 | 000110001 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 49
+// a^182 | 001100010 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 98
+// a^183 | 011000100 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 196
+// a^184 | 010010101 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 149
+// a^185 | 000110111 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 55
+// a^186 | 001101110 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 110
+// a^187 | 011011100 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 220
+// a^188 | 010100101 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 165
+// a^189 | 001010111 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 87
+// a^190 | 010101110 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 174
+// a^191 | 001000001 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 65
+// a^192 | 010000010 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 130
+// a^193 | 000011001 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 25
+// a^194 | 000110010 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 50
+// a^195 | 001100100 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 100
+// a^196 | 011001000 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 200
+// a^197 | 010001101 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 141
+// a^198 | 000000111 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 7
+// a^199 | 000001110 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 14
+// a^200 | 000011100 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 28
+// a^201 | 000111000 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 56
+// a^202 | 001110000 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 112
+// a^203 | 011100000 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 224
+// a^204 | 011011101 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 221
+// a^205 | 010100111 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 167
+// a^206 | 001010011 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 83
+// a^207 | 010100110 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 166
+// a^208 | 001010001 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 81
+// a^209 | 010100010 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 162
+// a^210 | 001011001 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 89
+// a^211 | 010110010 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 178
+// a^212 | 001111001 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 121
+// a^213 | 011110010 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 242
+// a^214 | 011111001 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 249
+// a^215 | 011101111 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 239
+// a^216 | 011000011 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 195
+// a^217 | 010011011 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 155
+// a^218 | 000101011 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 43
+// a^219 | 001010110 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 86
+// a^220 | 010101100 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 172
+// a^221 | 001000101 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 69
+// a^222 | 010001010 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 138
+// a^223 | 000001001 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 9
+// a^224 | 000010010 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 18
+// a^225 | 000100100 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 36
+// a^226 | 001001000 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 72
+// a^227 | 010010000 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 144
+// a^228 | 000111101 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 61
+// a^229 | 001111010 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 122
+// a^230 | 011110100 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 244
+// a^231 | 011110101 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 245
+// a^232 | 011110111 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 247
+// a^233 | 011110011 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 243
+// a^234 | 011111011 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 251
+// a^235 | 011101011 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 235
+// a^236 | 011001011 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 203
+// a^237 | 010001011 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 139
+// a^238 | 000001011 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 11
+// a^239 | 000010110 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 22
+// a^240 | 000101100 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 44
+// a^241 | 001011000 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 88
+// a^242 | 010110000 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 176
+// a^243 | 001111101 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 125
+// a^244 | 011111010 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 250
+// a^245 | 011101001 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 233
+// a^246 | 011001111 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 207
+// a^247 | 010000011 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 131
+// a^248 | 000011011 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 27
+// a^249 | 000110110 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 54
+// a^250 | 001101100 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 108
+// a^251 | 011011000 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 216
+// a^252 | 010101101 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 173
+// a^253 | 001000111 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 71
+// a^254 | 010001110 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 142
+// a^255 | 000000001 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 1
diff --git a/vendor/github.com/skip2/go-qrcode/reedsolomon/gf_poly.go b/vendor/github.com/skip2/go-qrcode/reedsolomon/gf_poly.go
new file mode 100644
index 0000000..962f545
--- /dev/null
+++ b/vendor/github.com/skip2/go-qrcode/reedsolomon/gf_poly.go
@@ -0,0 +1,216 @@
+// go-qrcode
+// Copyright 2014 Tom Harwood
+
+package reedsolomon
+
+import (
+ "fmt"
+ "log"
+
+ bitset "github.com/skip2/go-qrcode/bitset"
+)
+
+// gfPoly is a polynomial over GF(2^8).
+type gfPoly struct {
+ // The ith value is the coefficient of the ith degree of x.
+ // term[0]*(x^0) + term[1]*(x^1) + term[2]*(x^2) ...
+ term []gfElement
+}
+
+// newGFPolyFromData returns |data| as a polynomial over GF(2^8).
+//
+// Each data byte becomes the coefficient of an x term.
+//
+// For an n byte input the polynomial is:
+// data[n-1]*(x^n-1) + data[n-2]*(x^n-2) ... + data[0]*(x^0).
+func newGFPolyFromData(data *bitset.Bitset) gfPoly {
+ numTotalBytes := data.Len() / 8
+ if data.Len()%8 != 0 {
+ numTotalBytes++
+ }
+
+ result := gfPoly{term: make([]gfElement, numTotalBytes)}
+
+ i := numTotalBytes - 1
+ for j := 0; j < data.Len(); j += 8 {
+ result.term[i] = gfElement(data.ByteAt(j))
+ i--
+ }
+
+ return result
+}
+
+// newGFPolyMonomial returns term*(x^degree).
+func newGFPolyMonomial(term gfElement, degree int) gfPoly {
+ if term == gfZero {
+ return gfPoly{}
+ }
+
+ result := gfPoly{term: make([]gfElement, degree+1)}
+ result.term[degree] = term
+
+ return result
+}
+
+func (e gfPoly) data(numTerms int) []byte {
+ result := make([]byte, numTerms)
+
+ i := numTerms - len(e.term)
+ for j := len(e.term) - 1; j >= 0; j-- {
+ result[i] = byte(e.term[j])
+ i++
+ }
+
+ return result
+}
+
+// numTerms returns the number of
+func (e gfPoly) numTerms() int {
+ return len(e.term)
+}
+
+// gfPolyMultiply returns a * b.
+func gfPolyMultiply(a, b gfPoly) gfPoly {
+ numATerms := a.numTerms()
+ numBTerms := b.numTerms()
+
+ result := gfPoly{term: make([]gfElement, numATerms+numBTerms)}
+
+ for i := 0; i < numATerms; i++ {
+ for j := 0; j < numBTerms; j++ {
+ if a.term[i] != 0 && b.term[j] != 0 {
+ monomial := gfPoly{term: make([]gfElement, i+j+1)}
+ monomial.term[i+j] = gfMultiply(a.term[i], b.term[j])
+
+ result = gfPolyAdd(result, monomial)
+ }
+ }
+ }
+
+ return result.normalised()
+}
+
+// gfPolyRemainder return the remainder of numerator / denominator.
+func gfPolyRemainder(numerator, denominator gfPoly) gfPoly {
+ if denominator.equals(gfPoly{}) {
+ log.Panicln("Remainder by zero")
+ }
+
+ remainder := numerator
+
+ for remainder.numTerms() >= denominator.numTerms() {
+ degree := remainder.numTerms() - denominator.numTerms()
+ coefficient := gfDivide(remainder.term[remainder.numTerms()-1],
+ denominator.term[denominator.numTerms()-1])
+
+ divisor := gfPolyMultiply(denominator,
+ newGFPolyMonomial(coefficient, degree))
+
+ remainder = gfPolyAdd(remainder, divisor)
+ }
+
+ return remainder.normalised()
+}
+
+// gfPolyAdd returns a + b.
+func gfPolyAdd(a, b gfPoly) gfPoly {
+ numATerms := a.numTerms()
+ numBTerms := b.numTerms()
+
+ numTerms := numATerms
+ if numBTerms > numTerms {
+ numTerms = numBTerms
+ }
+
+ result := gfPoly{term: make([]gfElement, numTerms)}
+
+ for i := 0; i < numTerms; i++ {
+ switch {
+ case numATerms > i && numBTerms > i:
+ result.term[i] = gfAdd(a.term[i], b.term[i])
+ case numATerms > i:
+ result.term[i] = a.term[i]
+ default:
+ result.term[i] = b.term[i]
+ }
+ }
+
+ return result.normalised()
+}
+
+func (e gfPoly) normalised() gfPoly {
+ numTerms := e.numTerms()
+ maxNonzeroTerm := numTerms - 1
+
+ for i := numTerms - 1; i >= 0; i-- {
+ if e.term[i] != 0 {
+ break
+ }
+
+ maxNonzeroTerm = i - 1
+ }
+
+ if maxNonzeroTerm < 0 {
+ return gfPoly{}
+ } else if maxNonzeroTerm < numTerms-1 {
+ e.term = e.term[0 : maxNonzeroTerm+1]
+ }
+
+ return e
+}
+
+func (e gfPoly) string(useIndexForm bool) string {
+ var str string
+ numTerms := e.numTerms()
+
+ for i := numTerms - 1; i >= 0; i-- {
+ if e.term[i] > 0 {
+ if len(str) > 0 {
+ str += " + "
+ }
+
+ if !useIndexForm {
+ str += fmt.Sprintf("%dx^%d", e.term[i], i)
+ } else {
+ str += fmt.Sprintf("a^%dx^%d", gfLogTable[e.term[i]], i)
+ }
+ }
+ }
+
+ if len(str) == 0 {
+ str = "0"
+ }
+
+ return str
+}
+
+// equals returns true if e == other.
+func (e gfPoly) equals(other gfPoly) bool {
+ var minecPoly *gfPoly
+ var maxecPoly *gfPoly
+
+ if e.numTerms() > other.numTerms() {
+ minecPoly = &other
+ maxecPoly = &e
+ } else {
+ minecPoly = &e
+ maxecPoly = &other
+ }
+
+ numMinTerms := minecPoly.numTerms()
+ numMaxTerms := maxecPoly.numTerms()
+
+ for i := 0; i < numMinTerms; i++ {
+ if e.term[i] != other.term[i] {
+ return false
+ }
+ }
+
+ for i := numMinTerms; i < numMaxTerms; i++ {
+ if maxecPoly.term[i] != 0 {
+ return false
+ }
+ }
+
+ return true
+}
diff --git a/vendor/github.com/skip2/go-qrcode/reedsolomon/reed_solomon.go b/vendor/github.com/skip2/go-qrcode/reedsolomon/reed_solomon.go
new file mode 100644
index 0000000..561697b
--- /dev/null
+++ b/vendor/github.com/skip2/go-qrcode/reedsolomon/reed_solomon.go
@@ -0,0 +1,73 @@
+// go-qrcode
+// Copyright 2014 Tom Harwood
+
+// Package reedsolomon provides error correction encoding for QR Code 2005.
+//
+// QR Code 2005 uses a Reed-Solomon error correcting code to detect and correct
+// errors encountered during decoding.
+//
+// The generated RS codes are systematic, and consist of the input data with
+// error correction bytes appended.
+package reedsolomon
+
+import (
+ "log"
+
+ bitset "github.com/skip2/go-qrcode/bitset"
+)
+
+// Encode data for QR Code 2005 using the appropriate Reed-Solomon code.
+//
+// numECBytes is the number of error correction bytes to append, and is
+// determined by the target QR Code's version and error correction level.
+//
+// ISO/IEC 18004 table 9 specifies the numECBytes required. e.g. a 1-L code has
+// numECBytes=7.
+func Encode(data *bitset.Bitset, numECBytes int) *bitset.Bitset {
+ // Create a polynomial representing |data|.
+ //
+ // The bytes are interpreted as the sequence of coefficients of a polynomial.
+ // The last byte's value becomes the x^0 coefficient, the second to last
+ // becomes the x^1 coefficient and so on.
+ ecpoly := newGFPolyFromData(data)
+ ecpoly = gfPolyMultiply(ecpoly, newGFPolyMonomial(gfOne, numECBytes))
+
+ // Pick the generator polynomial.
+ generator := rsGeneratorPoly(numECBytes)
+
+ // Generate the error correction bytes.
+ remainder := gfPolyRemainder(ecpoly, generator)
+
+ // Combine the data & error correcting bytes.
+ // The mathematically correct answer is:
+ //
+ // result := gfPolyAdd(ecpoly, remainder).
+ //
+ // The encoding used by QR Code 2005 is slightly different this result: To
+ // preserve the original |data| bit sequence exactly, the data and remainder
+ // are combined manually below. This ensures any most significant zero bits
+ // are preserved (and not optimised away).
+ result := bitset.Clone(data)
+ result.AppendBytes(remainder.data(numECBytes))
+
+ return result
+}
+
+// rsGeneratorPoly returns the Reed-Solomon generator polynomial with |degree|.
+//
+// The generator polynomial is calculated as:
+// (x + a^0)(x + a^1)...(x + a^degree-1)
+func rsGeneratorPoly(degree int) gfPoly {
+ if degree < 2 {
+ log.Panic("degree < 2")
+ }
+
+ generator := gfPoly{term: []gfElement{1}}
+
+ for i := 0; i < degree; i++ {
+ nextPoly := gfPoly{term: []gfElement{gfExpTable[i], 1}}
+ generator = gfPolyMultiply(generator, nextPoly)
+ }
+
+ return generator
+}
diff --git a/vendor/github.com/skip2/go-qrcode/regular_symbol.go b/vendor/github.com/skip2/go-qrcode/regular_symbol.go
new file mode 100644
index 0000000..134be18
--- /dev/null
+++ b/vendor/github.com/skip2/go-qrcode/regular_symbol.go
@@ -0,0 +1,309 @@
+// go-qrcode
+// Copyright 2014 Tom Harwood
+
+package qrcode
+
+import (
+ bitset "github.com/skip2/go-qrcode/bitset"
+)
+
+type regularSymbol struct {
+ version qrCodeVersion
+ mask int
+
+ data *bitset.Bitset
+
+ symbol *symbol
+ size int
+}
+
+// Abbreviated true/false.
+const (
+ b0 = false
+ b1 = true
+)
+
+var (
+ alignmentPatternCenter = [][]int{
+ {}, // Version 0 doesn't exist.
+ {}, // Version 1 doesn't use alignment patterns.
+ {6, 18},
+ {6, 22},
+ {6, 26},
+ {6, 30},
+ {6, 34},
+ {6, 22, 38},
+ {6, 24, 42},
+ {6, 26, 46},
+ {6, 28, 50},
+ {6, 30, 54},
+ {6, 32, 58},
+ {6, 34, 62},
+ {6, 26, 46, 66},
+ {6, 26, 48, 70},
+ {6, 26, 50, 74},
+ {6, 30, 54, 78},
+ {6, 30, 56, 82},
+ {6, 30, 58, 86},
+ {6, 34, 62, 90},
+ {6, 28, 50, 72, 94},
+ {6, 26, 50, 74, 98},
+ {6, 30, 54, 78, 102},
+ {6, 28, 54, 80, 106},
+ {6, 32, 58, 84, 110},
+ {6, 30, 58, 86, 114},
+ {6, 34, 62, 90, 118},
+ {6, 26, 50, 74, 98, 122},
+ {6, 30, 54, 78, 102, 126},
+ {6, 26, 52, 78, 104, 130},
+ {6, 30, 56, 82, 108, 134},
+ {6, 34, 60, 86, 112, 138},
+ {6, 30, 58, 86, 114, 142},
+ {6, 34, 62, 90, 118, 146},
+ {6, 30, 54, 78, 102, 126, 150},
+ {6, 24, 50, 76, 102, 128, 154},
+ {6, 28, 54, 80, 106, 132, 158},
+ {6, 32, 58, 84, 110, 136, 162},
+ {6, 26, 54, 82, 110, 138, 166},
+ {6, 30, 58, 86, 114, 142, 170},
+ }
+
+ finderPattern = [][]bool{
+ {b1, b1, b1, b1, b1, b1, b1},
+ {b1, b0, b0, b0, b0, b0, b1},
+ {b1, b0, b1, b1, b1, b0, b1},
+ {b1, b0, b1, b1, b1, b0, b1},
+ {b1, b0, b1, b1, b1, b0, b1},
+ {b1, b0, b0, b0, b0, b0, b1},
+ {b1, b1, b1, b1, b1, b1, b1},
+ }
+
+ finderPatternSize = 7
+
+ finderPatternHorizontalBorder = [][]bool{
+ {b0, b0, b0, b0, b0, b0, b0, b0},
+ }
+
+ finderPatternVerticalBorder = [][]bool{
+ {b0},
+ {b0},
+ {b0},
+ {b0},
+ {b0},
+ {b0},
+ {b0},
+ {b0},
+ }
+
+ alignmentPattern = [][]bool{
+ {b1, b1, b1, b1, b1},
+ {b1, b0, b0, b0, b1},
+ {b1, b0, b1, b0, b1},
+ {b1, b0, b0, b0, b1},
+ {b1, b1, b1, b1, b1},
+ }
+)
+
+func buildRegularSymbol(version qrCodeVersion, mask int,
+ data *bitset.Bitset) (*symbol, error) {
+ m := &regularSymbol{
+ version: version,
+ mask: mask,
+ data: data,
+
+ symbol: newSymbol(version.symbolSize(), version.quietZoneSize()),
+ size: version.symbolSize(),
+ }
+
+ m.addFinderPatterns()
+ m.addAlignmentPatterns()
+ m.addTimingPatterns()
+ m.addFormatInfo()
+ m.addVersionInfo()
+
+ ok, err := m.addData()
+ if !ok {
+ return nil, err
+ }
+
+ return m.symbol, nil
+}
+
+func (m *regularSymbol) addFinderPatterns() {
+ fpSize := finderPatternSize
+ fp := finderPattern
+ fpHBorder := finderPatternHorizontalBorder
+ fpVBorder := finderPatternVerticalBorder
+
+ // Top left Finder Pattern.
+ m.symbol.set2dPattern(0, 0, fp)
+ m.symbol.set2dPattern(0, fpSize, fpHBorder)
+ m.symbol.set2dPattern(fpSize, 0, fpVBorder)
+
+ // Top right Finder Pattern.
+ m.symbol.set2dPattern(m.size-fpSize, 0, fp)
+ m.symbol.set2dPattern(m.size-fpSize-1, fpSize, fpHBorder)
+ m.symbol.set2dPattern(m.size-fpSize-1, 0, fpVBorder)
+
+ // Bottom left Finder Pattern.
+ m.symbol.set2dPattern(0, m.size-fpSize, fp)
+ m.symbol.set2dPattern(0, m.size-fpSize-1, fpHBorder)
+ m.symbol.set2dPattern(fpSize, m.size-fpSize-1, fpVBorder)
+}
+
+func (m *regularSymbol) addAlignmentPatterns() {
+ for _, x := range alignmentPatternCenter[m.version.version] {
+ for _, y := range alignmentPatternCenter[m.version.version] {
+ if !m.symbol.empty(x, y) {
+ continue
+ }
+
+ m.symbol.set2dPattern(x-2, y-2, alignmentPattern)
+ }
+ }
+}
+
+func (m *regularSymbol) addTimingPatterns() {
+ value := true
+
+ for i := finderPatternSize + 1; i < m.size-finderPatternSize; i++ {
+ m.symbol.set(i, finderPatternSize-1, value)
+ m.symbol.set(finderPatternSize-1, i, value)
+
+ value = !value
+ }
+}
+
+func (m *regularSymbol) addFormatInfo() {
+ fpSize := finderPatternSize
+ l := formatInfoLengthBits - 1
+
+ f := m.version.formatInfo(m.mask)
+
+ // Bits 0-7, under the top right finder pattern.
+ for i := 0; i <= 7; i++ {
+ m.symbol.set(m.size-i-1, fpSize+1, f.At(l-i))
+ }
+
+ // Bits 0-5, right of the top left finder pattern.
+ for i := 0; i <= 5; i++ {
+ m.symbol.set(fpSize+1, i, f.At(l-i))
+ }
+
+ // Bits 6-8 on the corner of the top left finder pattern.
+ m.symbol.set(fpSize+1, fpSize, f.At(l-6))
+ m.symbol.set(fpSize+1, fpSize+1, f.At(l-7))
+ m.symbol.set(fpSize, fpSize+1, f.At(l-8))
+
+ // Bits 9-14 on the underside of the top left finder pattern.
+ for i := 9; i <= 14; i++ {
+ m.symbol.set(14-i, fpSize+1, f.At(l-i))
+ }
+
+ // Bits 8-14 on the right side of the bottom left finder pattern.
+ for i := 8; i <= 14; i++ {
+ m.symbol.set(fpSize+1, m.size-fpSize+i-8, f.At(l-i))
+ }
+
+ // Always dark symbol.
+ m.symbol.set(fpSize+1, m.size-fpSize-1, true)
+}
+
+func (m *regularSymbol) addVersionInfo() {
+ fpSize := finderPatternSize
+
+ v := m.version.versionInfo()
+ l := versionInfoLengthBits - 1
+
+ if v == nil {
+ return
+ }
+
+ for i := 0; i < v.Len(); i++ {
+ // Above the bottom left finder pattern.
+ m.symbol.set(i/3, m.size-fpSize-4+i%3, v.At(l-i))
+
+ // Left of the top right finder pattern.
+ m.symbol.set(m.size-fpSize-4+i%3, i/3, v.At(l-i))
+ }
+}
+
+type direction uint8
+
+const (
+ up direction = iota
+ down
+)
+
+func (m *regularSymbol) addData() (bool, error) {
+ xOffset := 1
+ dir := up
+
+ x := m.size - 2
+ y := m.size - 1
+
+ for i := 0; i < m.data.Len(); i++ {
+ var mask bool
+ switch m.mask {
+ case 0:
+ mask = (y+x+xOffset)%2 == 0
+ case 1:
+ mask = y%2 == 0
+ case 2:
+ mask = (x+xOffset)%3 == 0
+ case 3:
+ mask = (y+x+xOffset)%3 == 0
+ case 4:
+ mask = (y/2+(x+xOffset)/3)%2 == 0
+ case 5:
+ mask = (y*(x+xOffset))%2+(y*(x+xOffset))%3 == 0
+ case 6:
+ mask = ((y*(x+xOffset))%2+((y*(x+xOffset))%3))%2 == 0
+ case 7:
+ mask = ((y+x+xOffset)%2+((y*(x+xOffset))%3))%2 == 0
+ }
+
+ // != is equivalent to XOR.
+ m.symbol.set(x+xOffset, y, mask != m.data.At(i))
+
+ if i == m.data.Len()-1 {
+ break
+ }
+
+ // Find next free bit in the symbol.
+ for {
+ if xOffset == 1 {
+ xOffset = 0
+ } else {
+ xOffset = 1
+
+ if dir == up {
+ if y > 0 {
+ y--
+ } else {
+ dir = down
+ x -= 2
+ }
+ } else {
+ if y < m.size-1 {
+ y++
+ } else {
+ dir = up
+ x -= 2
+ }
+ }
+ }
+
+ // Skip over the vertical timing pattern entirely.
+ if x == 5 {
+ x--
+ }
+
+ if m.symbol.empty(x+xOffset, y) {
+ break
+ }
+ }
+ }
+
+ return true, nil
+}
diff --git a/vendor/github.com/skip2/go-qrcode/symbol.go b/vendor/github.com/skip2/go-qrcode/symbol.go
new file mode 100644
index 0000000..0cb1327
--- /dev/null
+++ b/vendor/github.com/skip2/go-qrcode/symbol.go
@@ -0,0 +1,309 @@
+// go-qrcode
+// Copyright 2014 Tom Harwood
+
+package qrcode
+
+// symbol is a 2D array of bits representing a QR Code symbol.
+//
+// A symbol consists of size*size modules, with each module normally drawn as a
+// black or white square. The symbol also has a border of quietZoneSize modules.
+//
+// A (fictional) size=2, quietZoneSize=1 QR Code looks like:
+//
+// +----+
+// | |
+// | ab |
+// | cd |
+// | |
+// +----+
+//
+// For ease of implementation, the functions to set/get bits ignore the border,
+// so (0,0)=a, (0,1)=b, (1,0)=c, and (1,1)=d. The entire symbol (including the
+// border) is returned by bitmap().
+//
+type symbol struct {
+ // Value of module at [y][x]. True is set.
+ module [][]bool
+
+ // True if the module at [y][x] is used (to either true or false).
+ // Used to identify unused modules.
+ isUsed [][]bool
+
+ // Combined width/height of the symbol and quiet zones.
+ //
+ // size = symbolSize + 2*quietZoneSize.
+ size int
+
+ // Width/height of the symbol only.
+ symbolSize int
+
+ // Width/height of a single quiet zone.
+ quietZoneSize int
+}
+
+// newSymbol constructs a symbol of size size*size, with a border of
+// quietZoneSize.
+func newSymbol(size int, quietZoneSize int) *symbol {
+ var m symbol
+
+ m.module = make([][]bool, size+2*quietZoneSize)
+ m.isUsed = make([][]bool, size+2*quietZoneSize)
+
+ for i := range m.module {
+ m.module[i] = make([]bool, size+2*quietZoneSize)
+ m.isUsed[i] = make([]bool, size+2*quietZoneSize)
+ }
+
+ m.size = size + 2*quietZoneSize
+ m.symbolSize = size
+ m.quietZoneSize = quietZoneSize
+
+ return &m
+}
+
+// get returns the module value at (x, y).
+func (m *symbol) get(x int, y int) (v bool) {
+ v = m.module[y+m.quietZoneSize][x+m.quietZoneSize]
+ return
+}
+
+// empty returns true if the module at (x, y) has not been set (to either true
+// or false).
+func (m *symbol) empty(x int, y int) bool {
+ return !m.isUsed[y+m.quietZoneSize][x+m.quietZoneSize]
+}
+
+// numEmptyModules returns the number of empty modules.
+//
+// Initially numEmptyModules is symbolSize * symbolSize. After every module has
+// been set (to either true or false), the number of empty modules is zero.
+func (m *symbol) numEmptyModules() int {
+ var count int
+ for y := 0; y < m.symbolSize; y++ {
+ for x := 0; x < m.symbolSize; x++ {
+ if !m.isUsed[y+m.quietZoneSize][x+m.quietZoneSize] {
+ count++
+ }
+ }
+ }
+
+ return count
+}
+
+// set sets the module at (x, y) to v.
+func (m *symbol) set(x int, y int, v bool) {
+ m.module[y+m.quietZoneSize][x+m.quietZoneSize] = v
+ m.isUsed[y+m.quietZoneSize][x+m.quietZoneSize] = true
+}
+
+// set2dPattern sets a 2D array of modules, starting at (x, y).
+func (m *symbol) set2dPattern(x int, y int, v [][]bool) {
+ for j, row := range v {
+ for i, value := range row {
+ m.set(x+i, y+j, value)
+ }
+ }
+}
+
+// bitmap returns the entire symbol, including the quiet zone.
+func (m *symbol) bitmap() [][]bool {
+ module := make([][]bool, len(m.module))
+
+ for i := range m.module {
+ module[i] = m.module[i][:]
+ }
+
+ return module
+}
+
+// string returns a pictorial representation of the symbol, suitable for
+// printing in a TTY.
+func (m *symbol) string() string {
+ var result string
+
+ for _, row := range m.module {
+ for _, value := range row {
+ switch value {
+ case true:
+ result += " "
+ case false:
+ // Unicode 'FULL BLOCK' (U+2588).
+ result += "██"
+ }
+ }
+ result += "\n"
+ }
+
+ return result
+}
+
+// Constants used to weight penalty calculations. Specified by ISO/IEC
+// 18004:2006.
+const (
+ penaltyWeight1 = 3
+ penaltyWeight2 = 3
+ penaltyWeight3 = 40
+ penaltyWeight4 = 10
+)
+
+// penaltyScore returns the penalty score of the symbol. The penalty score
+// consists of the sum of the four individual penalty types.
+func (m *symbol) penaltyScore() int {
+ return m.penalty1() + m.penalty2() + m.penalty3() + m.penalty4()
+}
+
+// penalty1 returns the penalty score for "adjacent modules in row/column with
+// same colour".
+//
+// The numbers of adjacent matching modules and scores are:
+// 0-5: score = 0
+// 6+ : score = penaltyWeight1 + (numAdjacentModules - 5)
+func (m *symbol) penalty1() int {
+ penalty := 0
+
+ for x := 0; x < m.symbolSize; x++ {
+ lastValue := m.get(x, 0)
+ count := 1
+
+ for y := 1; y < m.symbolSize; y++ {
+ v := m.get(x, y)
+
+ if v != lastValue {
+ count = 1
+ lastValue = v
+ } else {
+ count++
+ if count == 6 {
+ penalty += penaltyWeight1 + 1
+ } else if count > 6 {
+ penalty++
+ }
+ }
+ }
+ }
+
+ for y := 0; y < m.symbolSize; y++ {
+ lastValue := m.get(0, y)
+ count := 1
+
+ for x := 1; x < m.symbolSize; x++ {
+ v := m.get(x, y)
+
+ if v != lastValue {
+ count = 1
+ lastValue = v
+ } else {
+ count++
+ if count == 6 {
+ penalty += penaltyWeight1 + 1
+ } else if count > 6 {
+ penalty++
+ }
+ }
+ }
+ }
+
+ return penalty
+}
+
+// penalty2 returns the penalty score for "block of modules in the same colour".
+//
+// m*n: score = penaltyWeight2 * (m-1) * (n-1).
+func (m *symbol) penalty2() int {
+ penalty := 0
+
+ for y := 1; y < m.symbolSize; y++ {
+ for x := 1; x < m.symbolSize; x++ {
+ topLeft := m.get(x-1, y-1)
+ above := m.get(x, y-1)
+ left := m.get(x-1, y)
+ current := m.get(x, y)
+
+ if current == left && current == above && current == topLeft {
+ penalty++
+ }
+ }
+ }
+
+ return penalty * penaltyWeight2
+}
+
+// penalty3 returns the penalty score for "1:1:3:1:1 ratio
+// (dark:light:dark:light:dark) pattern in row/column, preceded or followed by
+// light area 4 modules wide".
+//
+// Existence of the pattern scores penaltyWeight3.
+func (m *symbol) penalty3() int {
+ penalty := 0
+
+ for y := 0; y < m.symbolSize; y++ {
+ var bitBuffer int16 = 0x00
+
+ for x := 0; x < m.symbolSize; x++ {
+ bitBuffer <<= 1
+ if v := m.get(x, y); v {
+ bitBuffer |= 1
+ }
+
+ switch bitBuffer & 0x7ff {
+ // 0b000 0101 1101 or 0b10111010000
+ // 0x05d or 0x5d0
+ case 0x05d, 0x5d0:
+ penalty += penaltyWeight3
+ bitBuffer = 0xFF
+ default:
+ if x == m.symbolSize-1 && (bitBuffer&0x7f) == 0x5d {
+ penalty += penaltyWeight3
+ bitBuffer = 0xFF
+ }
+ }
+ }
+ }
+
+ for x := 0; x < m.symbolSize; x++ {
+ var bitBuffer int16 = 0x00
+
+ for y := 0; y < m.symbolSize; y++ {
+ bitBuffer <<= 1
+ if v := m.get(x, y); v {
+ bitBuffer |= 1
+ }
+
+ switch bitBuffer & 0x7ff {
+ // 0b000 0101 1101 or 0b10111010000
+ // 0x05d or 0x5d0
+ case 0x05d, 0x5d0:
+ penalty += penaltyWeight3
+ bitBuffer = 0xFF
+ default:
+ if y == m.symbolSize-1 && (bitBuffer&0x7f) == 0x5d {
+ penalty += penaltyWeight3
+ bitBuffer = 0xFF
+ }
+ }
+ }
+ }
+
+ return penalty
+}
+
+// penalty4 returns the penalty score...
+func (m *symbol) penalty4() int {
+ numModules := m.symbolSize * m.symbolSize
+ numDarkModules := 0
+
+ for x := 0; x < m.symbolSize; x++ {
+ for y := 0; y < m.symbolSize; y++ {
+ if v := m.get(x, y); v {
+ numDarkModules++
+ }
+ }
+ }
+
+ numDarkModuleDeviation := numModules/2 - numDarkModules
+ if numDarkModuleDeviation < 0 {
+ numDarkModuleDeviation *= -1
+ }
+
+ return penaltyWeight4 * (numDarkModuleDeviation / (numModules / 20))
+}
diff --git a/vendor/github.com/skip2/go-qrcode/version.go b/vendor/github.com/skip2/go-qrcode/version.go
new file mode 100644
index 0000000..738cf3d
--- /dev/null
+++ b/vendor/github.com/skip2/go-qrcode/version.go
@@ -0,0 +1,3050 @@
+// go-qrcode
+// Copyright 2014 Tom Harwood
+
+package qrcode
+
+import (
+ "log"
+
+ bitset "github.com/skip2/go-qrcode/bitset"
+)
+
+// Error detection/recovery capacity.
+//
+// There are several levels of error detection/recovery capacity. Higher levels
+// of error recovery are able to correct more errors, with the trade-off of
+// increased symbol size.
+type RecoveryLevel int
+
+const (
+ // Level L: 7% error recovery.
+ Low RecoveryLevel = iota
+
+ // Level M: 15% error recovery. Good default choice.
+ Medium
+
+ // Level Q: 25% error recovery.
+ High
+
+ // Level H: 30% error recovery.
+ Highest
+)
+
+// qrCodeVersion describes the data length and encoding order of a single QR
+// Code version. There are 40 versions numbers x 4 recovery levels == 160
+// possible qrCodeVersion structures.
+type qrCodeVersion struct {
+ // Version number (1-40 inclusive).
+ version int
+
+ // Error recovery level.
+ level RecoveryLevel
+
+ dataEncoderType dataEncoderType
+
+ // Encoded data can be split into multiple blocks. Each block contains data
+ // and error recovery bytes.
+ //
+ // Larger QR Codes contain more blocks.
+ block []block
+
+ // Number of bits required to pad the combined data & error correction bit
+ // stream up to the symbol's full capacity.
+ numRemainderBits int
+}
+
+type block struct {
+ numBlocks int
+
+ // Total codewords (numCodewords == numErrorCodewords+numDataCodewords).
+ numCodewords int
+
+ // Number of data codewords.
+ numDataCodewords int
+}
+
+var (
+ versions = []qrCodeVersion{
+ {
+ 1,
+ Low,
+ dataEncoderType1To9,
+ []block{
+ {
+ 1,
+ 26,
+ 19,
+ },
+ },
+ 0,
+ },
+ {
+ 1,
+ Medium,
+ dataEncoderType1To9,
+ []block{
+ {
+ 1,
+ 26,
+ 16,
+ },
+ },
+ 0,
+ },
+ {
+ 1,
+ High,
+ dataEncoderType1To9,
+ []block{
+ {
+ 1,
+ 26,
+ 13,
+ },
+ },
+ 0,
+ },
+ {
+ 1,
+ Highest,
+ dataEncoderType1To9,
+ []block{
+ {
+ 1,
+ 26,
+ 9,
+ },
+ },
+ 0,
+ },
+ {
+ 2,
+ Low,
+ dataEncoderType1To9,
+ []block{
+ {
+ 1,
+ 44,
+ 34,
+ },
+ },
+ 7,
+ },
+ {
+ 2,
+ Medium,
+ dataEncoderType1To9,
+ []block{
+ {
+ 1,
+ 44,
+ 28,
+ },
+ },
+ 7,
+ },
+ {
+ 2,
+ High,
+ dataEncoderType1To9,
+ []block{
+ {
+ 1,
+ 44,
+ 22,
+ },
+ },
+ 7,
+ },
+ {
+ 2,
+ Highest,
+ dataEncoderType1To9,
+ []block{
+ {
+ 1,
+ 44,
+ 16,
+ },
+ },
+ 7,
+ },
+ {
+ 3,
+ Low,
+ dataEncoderType1To9,
+ []block{
+ {
+ 1,
+ 70,
+ 55,
+ },
+ },
+ 7,
+ },
+ {
+ 3,
+ Medium,
+ dataEncoderType1To9,
+ []block{
+ {
+ 1,
+ 70,
+ 44,
+ },
+ },
+ 7,
+ },
+ {
+ 3,
+ High,
+ dataEncoderType1To9,
+ []block{
+ {
+ 2,
+ 35,
+ 17,
+ },
+ },
+ 7,
+ },
+ {
+ 3,
+ Highest,
+ dataEncoderType1To9,
+ []block{
+ {
+ 2,
+ 35,
+ 13,
+ },
+ },
+ 7,
+ },
+ {
+ 4,
+ Low,
+ dataEncoderType1To9,
+ []block{
+ {
+ 1,
+ 100,
+ 80,
+ },
+ },
+ 7,
+ },
+ {
+ 4,
+ Medium,
+ dataEncoderType1To9,
+ []block{
+ {
+ 2,
+ 50,
+ 32,
+ },
+ },
+ 7,
+ },
+ {
+ 4,
+ High,
+ dataEncoderType1To9,
+ []block{
+ {
+ 2,
+ 50,
+ 24,
+ },
+ },
+ 7,
+ },
+ {
+ 4,
+ Highest,
+ dataEncoderType1To9,
+ []block{
+ {
+ 4,
+ 25,
+ 9,
+ },
+ },
+ 7,
+ },
+ {
+ 5,
+ Low,
+ dataEncoderType1To9,
+ []block{
+ {
+ 1,
+ 134,
+ 108,
+ },
+ },
+ 7,
+ },
+ {
+ 5,
+ Medium,
+ dataEncoderType1To9,
+ []block{
+ {
+ 2,
+ 67,
+ 43,
+ },
+ },
+ 7,
+ },
+ {
+ 5,
+ High,
+ dataEncoderType1To9,
+ []block{
+ {
+ 2,
+ 33,
+ 15,
+ },
+ {
+ 2,
+ 34,
+ 16,
+ },
+ },
+ 7,
+ },
+ {
+ 5,
+ Highest,
+ dataEncoderType1To9,
+ []block{
+ {
+ 2,
+ 33,
+ 11,
+ },
+ {
+ 2,
+ 34,
+ 12,
+ },
+ },
+ 7,
+ },
+ {
+ 6,
+ Low,
+ dataEncoderType1To9,
+ []block{
+ {
+ 2,
+ 86,
+ 68,
+ },
+ },
+ 7,
+ },
+ {
+ 6,
+ Medium,
+ dataEncoderType1To9,
+ []block{
+ {
+ 4,
+ 43,
+ 27,
+ },
+ },
+ 7,
+ },
+ {
+ 6,
+ High,
+ dataEncoderType1To9,
+ []block{
+ {
+ 4,
+ 43,
+ 19,
+ },
+ },
+ 7,
+ },
+ {
+ 6,
+ Highest,
+ dataEncoderType1To9,
+ []block{
+ {
+ 4,
+ 43,
+ 15,
+ },
+ },
+ 7,
+ },
+ {
+ 7,
+ Low,
+ dataEncoderType1To9,
+ []block{
+ {
+ 2,
+ 98,
+ 78,
+ },
+ },
+ 0,
+ },
+ {
+ 7,
+ Medium,
+ dataEncoderType1To9,
+ []block{
+ {
+ 4,
+ 49,
+ 31,
+ },
+ },
+ 0,
+ },
+ {
+ 7,
+ High,
+ dataEncoderType1To9,
+ []block{
+ {
+ 2,
+ 32,
+ 14,
+ },
+ {
+ 4,
+ 33,
+ 15,
+ },
+ },
+ 0,
+ },
+ {
+ 7,
+ Highest,
+ dataEncoderType1To9,
+ []block{
+ {
+ 4,
+ 39,
+ 13,
+ },
+ {
+ 1,
+ 40,
+ 14,
+ },
+ },
+ 0,
+ },
+ {
+ 8,
+ Low,
+ dataEncoderType1To9,
+ []block{
+ {
+ 2,
+ 121,
+ 97,
+ },
+ },
+ 0,
+ },
+ {
+ 8,
+ Medium,
+ dataEncoderType1To9,
+ []block{
+ {
+ 2,
+ 60,
+ 38,
+ },
+ {
+ 2,
+ 61,
+ 39,
+ },
+ },
+ 0,
+ },
+ {
+ 8,
+ High,
+ dataEncoderType1To9,
+ []block{
+ {
+ 4,
+ 40,
+ 18,
+ },
+ {
+ 2,
+ 41,
+ 19,
+ },
+ },
+ 0,
+ },
+ {
+ 8,
+ Highest,
+ dataEncoderType1To9,
+ []block{
+ {
+ 4,
+ 40,
+ 14,
+ },
+ {
+ 2,
+ 41,
+ 15,
+ },
+ },
+ 0,
+ },
+ {
+ 9,
+ Low,
+ dataEncoderType1To9,
+ []block{
+ {
+ 2,
+ 146,
+ 116,
+ },
+ },
+ 0,
+ },
+ {
+ 9,
+ Medium,
+ dataEncoderType1To9,
+ []block{
+ {
+ 3,
+ 58,
+ 36,
+ },
+ {
+ 2,
+ 59,
+ 37,
+ },
+ },
+ 0,
+ },
+ {
+ 9,
+ High,
+ dataEncoderType1To9,
+ []block{
+ {
+ 4,
+ 36,
+ 16,
+ },
+ {
+ 4,
+ 37,
+ 17,
+ },
+ },
+ 0,
+ },
+ {
+ 9,
+ Highest,
+ dataEncoderType1To9,
+ []block{
+ {
+ 4,
+ 36,
+ 12,
+ },
+ {
+ 4,
+ 37,
+ 13,
+ },
+ },
+ 0,
+ },
+ {
+ 10,
+ Low,
+ dataEncoderType10To26,
+ []block{
+ {
+ 2,
+ 86,
+ 68,
+ },
+ {
+ 2,
+ 87,
+ 69,
+ },
+ },
+ 0,
+ },
+ {
+ 10,
+ Medium,
+ dataEncoderType10To26,
+ []block{
+ {
+ 4,
+ 69,
+ 43,
+ },
+ {
+ 1,
+ 70,
+ 44,
+ },
+ },
+ 0,
+ },
+ {
+ 10,
+ High,
+ dataEncoderType10To26,
+ []block{
+ {
+ 6,
+ 43,
+ 19,
+ },
+ {
+ 2,
+ 44,
+ 20,
+ },
+ },
+ 0,
+ },
+ {
+ 10,
+ Highest,
+ dataEncoderType10To26,
+ []block{
+ {
+ 6,
+ 43,
+ 15,
+ },
+ {
+ 2,
+ 44,
+ 16,
+ },
+ },
+ 0,
+ },
+ {
+ 11,
+ Low,
+ dataEncoderType10To26,
+ []block{
+ {
+ 4,
+ 101,
+ 81,
+ },
+ },
+ 0,
+ },
+ {
+ 11,
+ Medium,
+ dataEncoderType10To26,
+ []block{
+ {
+ 1,
+ 80,
+ 50,
+ },
+ {
+ 4,
+ 81,
+ 51,
+ },
+ },
+ 0,
+ },
+ {
+ 11,
+ High,
+ dataEncoderType10To26,
+ []block{
+ {
+ 4,
+ 50,
+ 22,
+ },
+ {
+ 4,
+ 51,
+ 23,
+ },
+ },
+ 0,
+ },
+ {
+ 11,
+ Highest,
+ dataEncoderType10To26,
+ []block{
+ {
+ 3,
+ 36,
+ 12,
+ },
+ {
+ 8,
+ 37,
+ 13,
+ },
+ },
+ 0,
+ },
+ {
+ 12,
+ Low,
+ dataEncoderType10To26,
+ []block{
+ {
+ 2,
+ 116,
+ 92,
+ },
+ {
+ 2,
+ 117,
+ 93,
+ },
+ },
+ 0,
+ },
+ {
+ 12,
+ Medium,
+ dataEncoderType10To26,
+ []block{
+ {
+ 6,
+ 58,
+ 36,
+ },
+ {
+ 2,
+ 59,
+ 37,
+ },
+ },
+ 0,
+ },
+ {
+ 12,
+ High,
+ dataEncoderType10To26,
+ []block{
+ {
+ 4,
+ 46,
+ 20,
+ },
+ {
+ 6,
+ 47,
+ 21,
+ },
+ },
+ 0,
+ },
+ {
+ 12,
+ Highest,
+ dataEncoderType10To26,
+ []block{
+ {
+ 7,
+ 42,
+ 14,
+ },
+ {
+ 4,
+ 43,
+ 15,
+ },
+ },
+ 0,
+ },
+ {
+ 13,
+ Low,
+ dataEncoderType10To26,
+ []block{
+ {
+ 4,
+ 133,
+ 107,
+ },
+ },
+ 0,
+ },
+ {
+ 13,
+ Medium,
+ dataEncoderType10To26,
+ []block{
+ {
+ 8,
+ 59,
+ 37,
+ },
+ {
+ 1,
+ 60,
+ 38,
+ },
+ },
+ 0,
+ },
+ {
+ 13,
+ High,
+ dataEncoderType10To26,
+ []block{
+ {
+ 8,
+ 44,
+ 20,
+ },
+ {
+ 4,
+ 45,
+ 21,
+ },
+ },
+ 0,
+ },
+ {
+ 13,
+ Highest,
+ dataEncoderType10To26,
+ []block{
+ {
+ 12,
+ 33,
+ 11,
+ },
+ {
+ 4,
+ 34,
+ 12,
+ },
+ },
+ 0,
+ },
+ {
+ 14,
+ Low,
+ dataEncoderType10To26,
+ []block{
+ {
+ 3,
+ 145,
+ 115,
+ },
+ {
+ 1,
+ 146,
+ 116,
+ },
+ },
+ 3,
+ },
+ {
+ 14,
+ Medium,
+ dataEncoderType10To26,
+ []block{
+ {
+ 4,
+ 64,
+ 40,
+ },
+ {
+ 5,
+ 65,
+ 41,
+ },
+ },
+ 3,
+ },
+ {
+ 14,
+ High,
+ dataEncoderType10To26,
+ []block{
+ {
+ 11,
+ 36,
+ 16,
+ },
+ {
+ 5,
+ 37,
+ 17,
+ },
+ },
+ 3,
+ },
+ {
+ 14,
+ Highest,
+ dataEncoderType10To26,
+ []block{
+ {
+ 11,
+ 36,
+ 12,
+ },
+ {
+ 5,
+ 37,
+ 13,
+ },
+ },
+ 3,
+ },
+ {
+ 15,
+ Low,
+ dataEncoderType10To26,
+ []block{
+ {
+ 5,
+ 109,
+ 87,
+ },
+ {
+ 1,
+ 110,
+ 88,
+ },
+ },
+ 3,
+ },
+ {
+ 15,
+ Medium,
+ dataEncoderType10To26,
+ []block{
+ {
+ 5,
+ 65,
+ 41,
+ },
+ {
+ 5,
+ 66,
+ 42,
+ },
+ },
+ 3,
+ },
+ {
+ 15,
+ High,
+ dataEncoderType10To26,
+ []block{
+ {
+ 5,
+ 54,
+ 24,
+ },
+ {
+ 7,
+ 55,
+ 25,
+ },
+ },
+ 3,
+ },
+ {
+ 15,
+ Highest,
+ dataEncoderType10To26,
+ []block{
+ {
+ 11,
+ 36,
+ 12,
+ },
+ {
+ 7,
+ 37,
+ 13,
+ },
+ },
+ 3,
+ },
+ {
+ 16,
+ Low,
+ dataEncoderType10To26,
+ []block{
+ {
+ 5,
+ 122,
+ 98,
+ },
+ {
+ 1,
+ 123,
+ 99,
+ },
+ },
+ 3,
+ },
+ {
+ 16,
+ Medium,
+ dataEncoderType10To26,
+ []block{
+ {
+ 7,
+ 73,
+ 45,
+ },
+ {
+ 3,
+ 74,
+ 46,
+ },
+ },
+ 3,
+ },
+ {
+ 16,
+ High,
+ dataEncoderType10To26,
+ []block{
+ {
+ 15,
+ 43,
+ 19,
+ },
+ {
+ 2,
+ 44,
+ 20,
+ },
+ },
+ 3,
+ },
+ {
+ 16,
+ Highest,
+ dataEncoderType10To26,
+ []block{
+ {
+ 3,
+ 45,
+ 15,
+ },
+ {
+ 13,
+ 46,
+ 16,
+ },
+ },
+ 3,
+ },
+ {
+ 17,
+ Low,
+ dataEncoderType10To26,
+ []block{
+ {
+ 1,
+ 135,
+ 107,
+ },
+ {
+ 5,
+ 136,
+ 108,
+ },
+ },
+ 3,
+ },
+ {
+ 17,
+ Medium,
+ dataEncoderType10To26,
+ []block{
+ {
+ 10,
+ 74,
+ 46,
+ },
+ {
+ 1,
+ 75,
+ 47,
+ },
+ },
+ 3,
+ },
+ {
+ 17,
+ High,
+ dataEncoderType10To26,
+ []block{
+ {
+ 1,
+ 50,
+ 22,
+ },
+ {
+ 15,
+ 51,
+ 23,
+ },
+ },
+ 3,
+ },
+ {
+ 17,
+ Highest,
+ dataEncoderType10To26,
+ []block{
+ {
+ 2,
+ 42,
+ 14,
+ },
+ {
+ 17,
+ 43,
+ 15,
+ },
+ },
+ 3,
+ },
+ {
+ 18,
+ Low,
+ dataEncoderType10To26,
+ []block{
+ {
+ 5,
+ 150,
+ 120,
+ },
+ {
+ 1,
+ 151,
+ 121,
+ },
+ },
+ 3,
+ },
+ {
+ 18,
+ Medium,
+ dataEncoderType10To26,
+ []block{
+ {
+ 9,
+ 69,
+ 43,
+ },
+ {
+ 4,
+ 70,
+ 44,
+ },
+ },
+ 3,
+ },
+ {
+ 18,
+ High,
+ dataEncoderType10To26,
+ []block{
+ {
+ 17,
+ 50,
+ 22,
+ },
+ {
+ 1,
+ 51,
+ 23,
+ },
+ },
+ 3,
+ },
+ {
+ 18,
+ Highest,
+ dataEncoderType10To26,
+ []block{
+ {
+ 2,
+ 42,
+ 14,
+ },
+ {
+ 19,
+ 43,
+ 15,
+ },
+ },
+ 3,
+ },
+ {
+ 19,
+ Low,
+ dataEncoderType10To26,
+ []block{
+ {
+ 3,
+ 141,
+ 113,
+ },
+ {
+ 4,
+ 142,
+ 114,
+ },
+ },
+ 3,
+ },
+ {
+ 19,
+ Medium,
+ dataEncoderType10To26,
+ []block{
+ {
+ 3,
+ 70,
+ 44,
+ },
+ {
+ 11,
+ 71,
+ 45,
+ },
+ },
+ 3,
+ },
+ {
+ 19,
+ High,
+ dataEncoderType10To26,
+ []block{
+ {
+ 17,
+ 47,
+ 21,
+ },
+ {
+ 4,
+ 48,
+ 22,
+ },
+ },
+ 3,
+ },
+ {
+ 19,
+ Highest,
+ dataEncoderType10To26,
+ []block{
+ {
+ 9,
+ 39,
+ 13,
+ },
+ {
+ 16,
+ 40,
+ 14,
+ },
+ },
+ 3,
+ },
+ {
+ 20,
+ Low,
+ dataEncoderType10To26,
+ []block{
+ {
+ 3,
+ 135,
+ 107,
+ },
+ {
+ 5,
+ 136,
+ 108,
+ },
+ },
+ 3,
+ },
+ {
+ 20,
+ Medium,
+ dataEncoderType10To26,
+ []block{
+ {
+ 3,
+ 67,
+ 41,
+ },
+ {
+ 13,
+ 68,
+ 42,
+ },
+ },
+ 3,
+ },
+ {
+ 20,
+ High,
+ dataEncoderType10To26,
+ []block{
+ {
+ 15,
+ 54,
+ 24,
+ },
+ {
+ 5,
+ 55,
+ 25,
+ },
+ },
+ 3,
+ },
+ {
+ 20,
+ Highest,
+ dataEncoderType10To26,
+ []block{
+ {
+ 15,
+ 43,
+ 15,
+ },
+ {
+ 10,
+ 44,
+ 16,
+ },
+ },
+ 3,
+ },
+ {
+ 21,
+ Low,
+ dataEncoderType10To26,
+ []block{
+ {
+ 4,
+ 144,
+ 116,
+ },
+ {
+ 4,
+ 145,
+ 117,
+ },
+ },
+ 4,
+ },
+ {
+ 21,
+ Medium,
+ dataEncoderType10To26,
+ []block{
+ {
+ 17,
+ 68,
+ 42,
+ },
+ },
+ 4,
+ },
+ {
+ 21,
+ High,
+ dataEncoderType10To26,
+ []block{
+ {
+ 17,
+ 50,
+ 22,
+ },
+ {
+ 6,
+ 51,
+ 23,
+ },
+ },
+ 4,
+ },
+ {
+ 21,
+ Highest,
+ dataEncoderType10To26,
+ []block{
+ {
+ 19,
+ 46,
+ 16,
+ },
+ {
+ 6,
+ 47,
+ 17,
+ },
+ },
+ 4,
+ },
+ {
+ 22,
+ Low,
+ dataEncoderType10To26,
+ []block{
+ {
+ 2,
+ 139,
+ 111,
+ },
+ {
+ 7,
+ 140,
+ 112,
+ },
+ },
+ 4,
+ },
+ {
+ 22,
+ Medium,
+ dataEncoderType10To26,
+ []block{
+ {
+ 17,
+ 74,
+ 46,
+ },
+ },
+ 4,
+ },
+ {
+ 22,
+ High,
+ dataEncoderType10To26,
+ []block{
+ {
+ 7,
+ 54,
+ 24,
+ },
+ {
+ 16,
+ 55,
+ 25,
+ },
+ },
+ 4,
+ },
+ {
+ 22,
+ Highest,
+ dataEncoderType10To26,
+ []block{
+ {
+ 34,
+ 37,
+ 13,
+ },
+ },
+ 4,
+ },
+ {
+ 23,
+ Low,
+ dataEncoderType10To26,
+ []block{
+ {
+ 4,
+ 151,
+ 121,
+ },
+ {
+ 5,
+ 152,
+ 122,
+ },
+ },
+ 4,
+ },
+ {
+ 23,
+ Medium,
+ dataEncoderType10To26,
+ []block{
+ {
+ 4,
+ 75,
+ 47,
+ },
+ {
+ 14,
+ 76,
+ 48,
+ },
+ },
+ 4,
+ },
+ {
+ 23,
+ High,
+ dataEncoderType10To26,
+ []block{
+ {
+ 11,
+ 54,
+ 24,
+ },
+ {
+ 14,
+ 55,
+ 25,
+ },
+ },
+ 4,
+ },
+ {
+ 23,
+ Highest,
+ dataEncoderType10To26,
+ []block{
+ {
+ 16,
+ 45,
+ 15,
+ },
+ {
+ 14,
+ 46,
+ 16,
+ },
+ },
+ 4,
+ },
+ {
+ 24,
+ Low,
+ dataEncoderType10To26,
+ []block{
+ {
+ 6,
+ 147,
+ 117,
+ },
+ {
+ 4,
+ 148,
+ 118,
+ },
+ },
+ 4,
+ },
+ {
+ 24,
+ Medium,
+ dataEncoderType10To26,
+ []block{
+ {
+ 6,
+ 73,
+ 45,
+ },
+ {
+ 14,
+ 74,
+ 46,
+ },
+ },
+ 4,
+ },
+ {
+ 24,
+ High,
+ dataEncoderType10To26,
+ []block{
+ {
+ 11,
+ 54,
+ 24,
+ },
+ {
+ 16,
+ 55,
+ 25,
+ },
+ },
+ 4,
+ },
+ {
+ 24,
+ Highest,
+ dataEncoderType10To26,
+ []block{
+ {
+ 30,
+ 46,
+ 16,
+ },
+ {
+ 2,
+ 47,
+ 17,
+ },
+ },
+ 4,
+ },
+ {
+ 25,
+ Low,
+ dataEncoderType10To26,
+ []block{
+ {
+ 8,
+ 132,
+ 106,
+ },
+ {
+ 4,
+ 133,
+ 107,
+ },
+ },
+ 4,
+ },
+ {
+ 25,
+ Medium,
+ dataEncoderType10To26,
+ []block{
+ {
+ 8,
+ 75,
+ 47,
+ },
+ {
+ 13,
+ 76,
+ 48,
+ },
+ },
+ 4,
+ },
+ {
+ 25,
+ High,
+ dataEncoderType10To26,
+ []block{
+ {
+ 7,
+ 54,
+ 24,
+ },
+ {
+ 22,
+ 55,
+ 25,
+ },
+ },
+ 4,
+ },
+ {
+ 25,
+ Highest,
+ dataEncoderType10To26,
+ []block{
+ {
+ 22,
+ 45,
+ 15,
+ },
+ {
+ 13,
+ 46,
+ 16,
+ },
+ },
+ 4,
+ },
+ {
+ 26,
+ Low,
+ dataEncoderType10To26,
+ []block{
+ {
+ 10,
+ 142,
+ 114,
+ },
+ {
+ 2,
+ 143,
+ 115,
+ },
+ },
+ 4,
+ },
+ {
+ 26,
+ Medium,
+ dataEncoderType10To26,
+ []block{
+ {
+ 19,
+ 74,
+ 46,
+ },
+ {
+ 4,
+ 75,
+ 47,
+ },
+ },
+ 4,
+ },
+ {
+ 26,
+ High,
+ dataEncoderType10To26,
+ []block{
+ {
+ 28,
+ 50,
+ 22,
+ },
+ {
+ 6,
+ 51,
+ 23,
+ },
+ },
+ 4,
+ },
+ {
+ 26,
+ Highest,
+ dataEncoderType10To26,
+ []block{
+ {
+ 33,
+ 46,
+ 16,
+ },
+ {
+ 4,
+ 47,
+ 17,
+ },
+ },
+ 4,
+ },
+ {
+ 27,
+ Low,
+ dataEncoderType27To40,
+ []block{
+ {
+ 8,
+ 152,
+ 122,
+ },
+ {
+ 4,
+ 153,
+ 123,
+ },
+ },
+ 4,
+ },
+ {
+ 27,
+ Medium,
+ dataEncoderType27To40,
+ []block{
+ {
+ 22,
+ 73,
+ 45,
+ },
+ {
+ 3,
+ 74,
+ 46,
+ },
+ },
+ 4,
+ },
+ {
+ 27,
+ High,
+ dataEncoderType27To40,
+ []block{
+ {
+ 8,
+ 53,
+ 23,
+ },
+ {
+ 26,
+ 54,
+ 24,
+ },
+ },
+ 4,
+ },
+ {
+ 27,
+ Highest,
+ dataEncoderType27To40,
+ []block{
+ {
+ 12,
+ 45,
+ 15,
+ },
+ {
+ 28,
+ 46,
+ 16,
+ },
+ },
+ 4,
+ },
+ {
+ 28,
+ Low,
+ dataEncoderType27To40,
+ []block{
+ {
+ 3,
+ 147,
+ 117,
+ },
+ {
+ 10,
+ 148,
+ 118,
+ },
+ },
+ 3,
+ },
+ {
+ 28,
+ Medium,
+ dataEncoderType27To40,
+ []block{
+ {
+ 3,
+ 73,
+ 45,
+ },
+ {
+ 23,
+ 74,
+ 46,
+ },
+ },
+ 3,
+ },
+ {
+ 28,
+ High,
+ dataEncoderType27To40,
+ []block{
+ {
+ 4,
+ 54,
+ 24,
+ },
+ {
+ 31,
+ 55,
+ 25,
+ },
+ },
+ 3,
+ },
+ {
+ 28,
+ Highest,
+ dataEncoderType27To40,
+ []block{
+ {
+ 11,
+ 45,
+ 15,
+ },
+ {
+ 31,
+ 46,
+ 16,
+ },
+ },
+ 3,
+ },
+ {
+ 29,
+ Low,
+ dataEncoderType27To40,
+ []block{
+ {
+ 7,
+ 146,
+ 116,
+ },
+ {
+ 7,
+ 147,
+ 117,
+ },
+ },
+ 3,
+ },
+ {
+ 29,
+ Medium,
+ dataEncoderType27To40,
+ []block{
+ {
+ 21,
+ 73,
+ 45,
+ },
+ {
+ 7,
+ 74,
+ 46,
+ },
+ },
+ 3,
+ },
+ {
+ 29,
+ High,
+ dataEncoderType27To40,
+ []block{
+ {
+ 1,
+ 53,
+ 23,
+ },
+ {
+ 37,
+ 54,
+ 24,
+ },
+ },
+ 3,
+ },
+ {
+ 29,
+ Highest,
+ dataEncoderType27To40,
+ []block{
+ {
+ 19,
+ 45,
+ 15,
+ },
+ {
+ 26,
+ 46,
+ 16,
+ },
+ },
+ 3,
+ },
+ {
+ 30,
+ Low,
+ dataEncoderType27To40,
+ []block{
+ {
+ 5,
+ 145,
+ 115,
+ },
+ {
+ 10,
+ 146,
+ 116,
+ },
+ },
+ 3,
+ },
+ {
+ 30,
+ Medium,
+ dataEncoderType27To40,
+ []block{
+ {
+ 19,
+ 75,
+ 47,
+ },
+ {
+ 10,
+ 76,
+ 48,
+ },
+ },
+ 3,
+ },
+ {
+ 30,
+ High,
+ dataEncoderType27To40,
+ []block{
+ {
+ 15,
+ 54,
+ 24,
+ },
+ {
+ 25,
+ 55,
+ 25,
+ },
+ },
+ 3,
+ },
+ {
+ 30,
+ Highest,
+ dataEncoderType27To40,
+ []block{
+ {
+ 23,
+ 45,
+ 15,
+ },
+ {
+ 25,
+ 46,
+ 16,
+ },
+ },
+ 3,
+ },
+ {
+ 31,
+ Low,
+ dataEncoderType27To40,
+ []block{
+ {
+ 13,
+ 145,
+ 115,
+ },
+ {
+ 3,
+ 146,
+ 116,
+ },
+ },
+ 3,
+ },
+ {
+ 31,
+ Medium,
+ dataEncoderType27To40,
+ []block{
+ {
+ 2,
+ 74,
+ 46,
+ },
+ {
+ 29,
+ 75,
+ 47,
+ },
+ },
+ 3,
+ },
+ {
+ 31,
+ High,
+ dataEncoderType27To40,
+ []block{
+ {
+ 42,
+ 54,
+ 24,
+ },
+ {
+ 1,
+ 55,
+ 25,
+ },
+ },
+ 3,
+ },
+ {
+ 31,
+ Highest,
+ dataEncoderType27To40,
+ []block{
+ {
+ 23,
+ 45,
+ 15,
+ },
+ {
+ 28,
+ 46,
+ 16,
+ },
+ },
+ 3,
+ },
+ {
+ 32,
+ Low,
+ dataEncoderType27To40,
+ []block{
+ {
+ 17,
+ 145,
+ 115,
+ },
+ },
+ 3,
+ },
+ {
+ 32,
+ Medium,
+ dataEncoderType27To40,
+ []block{
+ {
+ 10,
+ 74,
+ 46,
+ },
+ {
+ 23,
+ 75,
+ 47,
+ },
+ },
+ 3,
+ },
+ {
+ 32,
+ High,
+ dataEncoderType27To40,
+ []block{
+ {
+ 10,
+ 54,
+ 24,
+ },
+ {
+ 35,
+ 55,
+ 25,
+ },
+ },
+ 3,
+ },
+ {
+ 32,
+ Highest,
+ dataEncoderType27To40,
+ []block{
+ {
+ 19,
+ 45,
+ 15,
+ },
+ {
+ 35,
+ 46,
+ 16,
+ },
+ },
+ 3,
+ },
+ {
+ 33,
+ Low,
+ dataEncoderType27To40,
+ []block{
+ {
+ 17,
+ 145,
+ 115,
+ },
+ {
+ 1,
+ 146,
+ 116,
+ },
+ },
+ 3,
+ },
+ {
+ 33,
+ Medium,
+ dataEncoderType27To40,
+ []block{
+ {
+ 14,
+ 74,
+ 46,
+ },
+ {
+ 21,
+ 75,
+ 47,
+ },
+ },
+ 3,
+ },
+ {
+ 33,
+ High,
+ dataEncoderType27To40,
+ []block{
+ {
+ 29,
+ 54,
+ 24,
+ },
+ {
+ 19,
+ 55,
+ 25,
+ },
+ },
+ 3,
+ },
+ {
+ 33,
+ Highest,
+ dataEncoderType27To40,
+ []block{
+ {
+ 11,
+ 45,
+ 15,
+ },
+ {
+ 46,
+ 46,
+ 16,
+ },
+ },
+ 3,
+ },
+ {
+ 34,
+ Low,
+ dataEncoderType27To40,
+ []block{
+ {
+ 13,
+ 145,
+ 115,
+ },
+ {
+ 6,
+ 146,
+ 116,
+ },
+ },
+ 3,
+ },
+ {
+ 34,
+ Medium,
+ dataEncoderType27To40,
+ []block{
+ {
+ 14,
+ 74,
+ 46,
+ },
+ {
+ 23,
+ 75,
+ 47,
+ },
+ },
+ 3,
+ },
+ {
+ 34,
+ High,
+ dataEncoderType27To40,
+ []block{
+ {
+ 44,
+ 54,
+ 24,
+ },
+ {
+ 7,
+ 55,
+ 25,
+ },
+ },
+ 3,
+ },
+ {
+ 34,
+ Highest,
+ dataEncoderType27To40,
+ []block{
+ {
+ 59,
+ 46,
+ 16,
+ },
+ {
+ 1,
+ 47,
+ 17,
+ },
+ },
+ 3,
+ },
+ {
+ 35,
+ Low,
+ dataEncoderType27To40,
+ []block{
+ {
+ 12,
+ 151,
+ 121,
+ },
+ {
+ 7,
+ 152,
+ 122,
+ },
+ },
+ 0,
+ },
+ {
+ 35,
+ Medium,
+ dataEncoderType27To40,
+ []block{
+ {
+ 12,
+ 75,
+ 47,
+ },
+ {
+ 26,
+ 76,
+ 48,
+ },
+ },
+ 0,
+ },
+ {
+ 35,
+ High,
+ dataEncoderType27To40,
+ []block{
+ {
+ 39,
+ 54,
+ 24,
+ },
+ {
+ 14,
+ 55,
+ 25,
+ },
+ },
+ 0,
+ },
+ {
+ 35,
+ Highest,
+ dataEncoderType27To40,
+ []block{
+ {
+ 22,
+ 45,
+ 15,
+ },
+ {
+ 41,
+ 46,
+ 16,
+ },
+ },
+ 0,
+ },
+ {
+ 36,
+ Low,
+ dataEncoderType27To40,
+ []block{
+ {
+ 6,
+ 151,
+ 121,
+ },
+ {
+ 14,
+ 152,
+ 122,
+ },
+ },
+ 0,
+ },
+ {
+ 36,
+ Medium,
+ dataEncoderType27To40,
+ []block{
+ {
+ 6,
+ 75,
+ 47,
+ },
+ {
+ 34,
+ 76,
+ 48,
+ },
+ },
+ 0,
+ },
+ {
+ 36,
+ High,
+ dataEncoderType27To40,
+ []block{
+ {
+ 46,
+ 54,
+ 24,
+ },
+ {
+ 10,
+ 55,
+ 25,
+ },
+ },
+ 0,
+ },
+ {
+ 36,
+ Highest,
+ dataEncoderType27To40,
+ []block{
+ {
+ 2,
+ 45,
+ 15,
+ },
+ {
+ 64,
+ 46,
+ 16,
+ },
+ },
+ 0,
+ },
+ {
+ 37,
+ Low,
+ dataEncoderType27To40,
+ []block{
+ {
+ 17,
+ 152,
+ 122,
+ },
+ {
+ 4,
+ 153,
+ 123,
+ },
+ },
+ 0,
+ },
+ {
+ 37,
+ Medium,
+ dataEncoderType27To40,
+ []block{
+ {
+ 29,
+ 74,
+ 46,
+ },
+ {
+ 14,
+ 75,
+ 47,
+ },
+ },
+ 0,
+ },
+ {
+ 37,
+ High,
+ dataEncoderType27To40,
+ []block{
+ {
+ 49,
+ 54,
+ 24,
+ },
+ {
+ 10,
+ 55,
+ 25,
+ },
+ },
+ 0,
+ },
+ {
+ 37,
+ Highest,
+ dataEncoderType27To40,
+ []block{
+ {
+ 24,
+ 45,
+ 15,
+ },
+ {
+ 46,
+ 46,
+ 16,
+ },
+ },
+ 0,
+ },
+ {
+ 38,
+ Low,
+ dataEncoderType27To40,
+ []block{
+ {
+ 4,
+ 152,
+ 122,
+ },
+ {
+ 18,
+ 153,
+ 123,
+ },
+ },
+ 0,
+ },
+ {
+ 38,
+ Medium,
+ dataEncoderType27To40,
+ []block{
+ {
+ 13,
+ 74,
+ 46,
+ },
+ {
+ 32,
+ 75,
+ 47,
+ },
+ },
+ 0,
+ },
+ {
+ 38,
+ High,
+ dataEncoderType27To40,
+ []block{
+ {
+ 48,
+ 54,
+ 24,
+ },
+ {
+ 14,
+ 55,
+ 25,
+ },
+ },
+ 0,
+ },
+ {
+ 38,
+ Highest,
+ dataEncoderType27To40,
+ []block{
+ {
+ 42,
+ 45,
+ 15,
+ },
+ {
+ 32,
+ 46,
+ 16,
+ },
+ },
+ 0,
+ },
+ {
+ 39,
+ Low,
+ dataEncoderType27To40,
+ []block{
+ {
+ 20,
+ 147,
+ 117,
+ },
+ {
+ 4,
+ 148,
+ 118,
+ },
+ },
+ 0,
+ },
+ {
+ 39,
+ Medium,
+ dataEncoderType27To40,
+ []block{
+ {
+ 40,
+ 75,
+ 47,
+ },
+ {
+ 7,
+ 76,
+ 48,
+ },
+ },
+ 0,
+ },
+ {
+ 39,
+ High,
+ dataEncoderType27To40,
+ []block{
+ {
+ 43,
+ 54,
+ 24,
+ },
+ {
+ 22,
+ 55,
+ 25,
+ },
+ },
+ 0,
+ },
+ {
+ 39,
+ Highest,
+ dataEncoderType27To40,
+ []block{
+ {
+ 10,
+ 45,
+ 15,
+ },
+ {
+ 67,
+ 46,
+ 16,
+ },
+ },
+ 0,
+ },
+ {
+ 40,
+ Low,
+ dataEncoderType27To40,
+ []block{
+ {
+ 19,
+ 148,
+ 118,
+ },
+ {
+ 6,
+ 149,
+ 119,
+ },
+ },
+ 0,
+ },
+ {
+ 40,
+ Medium,
+ dataEncoderType27To40,
+ []block{
+ {
+ 18,
+ 75,
+ 47,
+ },
+ {
+ 31,
+ 76,
+ 48,
+ },
+ },
+ 0,
+ },
+ {
+ 40,
+ High,
+ dataEncoderType27To40,
+ []block{
+ {
+ 34,
+ 54,
+ 24,
+ },
+ {
+ 34,
+ 55,
+ 25,
+ },
+ },
+ 0,
+ },
+ {
+ 40,
+ Highest,
+ dataEncoderType27To40,
+ []block{
+ {
+ 20,
+ 45,
+ 15,
+ },
+ {
+ 61,
+ 46,
+ 16,
+ },
+ },
+ 0,
+ },
+ }
+)
+
+var (
+ // Each QR Code contains a 15-bit Format Information value. The 15 bits
+ // consist of 5 data bits concatenated with 10 error correction bits.
+ //
+ // The 5 data bits consist of:
+ // - 2 bits for the error correction level (L=01, M=00, G=11, H=10).
+ // - 3 bits for the data mask pattern identifier.
+ //
+ // formatBitSequence is a mapping from the 5 data bits to the completed 15-bit
+ // Format Information value.
+ //
+ // For example, a QR Code using error correction level L, and data mask
+ // pattern identifier 001:
+ //
+ // 01 | 001 = 01001 = 0x9
+ // formatBitSequence[0x9].qrCode = 0x72f3 = 111001011110011
+ formatBitSequence = []struct {
+ regular uint32
+ micro uint32
+ }{
+ {0x5412, 0x4445},
+ {0x5125, 0x4172},
+ {0x5e7c, 0x4e2b},
+ {0x5b4b, 0x4b1c},
+ {0x45f9, 0x55ae},
+ {0x40ce, 0x5099},
+ {0x4f97, 0x5fc0},
+ {0x4aa0, 0x5af7},
+ {0x77c4, 0x6793},
+ {0x72f3, 0x62a4},
+ {0x7daa, 0x6dfd},
+ {0x789d, 0x68ca},
+ {0x662f, 0x7678},
+ {0x6318, 0x734f},
+ {0x6c41, 0x7c16},
+ {0x6976, 0x7921},
+ {0x1689, 0x06de},
+ {0x13be, 0x03e9},
+ {0x1ce7, 0x0cb0},
+ {0x19d0, 0x0987},
+ {0x0762, 0x1735},
+ {0x0255, 0x1202},
+ {0x0d0c, 0x1d5b},
+ {0x083b, 0x186c},
+ {0x355f, 0x2508},
+ {0x3068, 0x203f},
+ {0x3f31, 0x2f66},
+ {0x3a06, 0x2a51},
+ {0x24b4, 0x34e3},
+ {0x2183, 0x31d4},
+ {0x2eda, 0x3e8d},
+ {0x2bed, 0x3bba},
+ }
+
+ // QR Codes version 7 and higher contain an 18-bit Version Information value,
+ // consisting of a 6 data bits and 12 error correction bits.
+ //
+ // versionBitSequence is a mapping from QR Code version to the completed
+ // 18-bit Version Information value.
+ //
+ // For example, a QR code of version 7:
+ // versionBitSequence[0x7] = 0x07c94 = 000111110010010100
+ versionBitSequence = []uint32{
+ 0x00000,
+ 0x00000,
+ 0x00000,
+ 0x00000,
+ 0x00000,
+ 0x00000,
+ 0x00000,
+ 0x07c94,
+ 0x085bc,
+ 0x09a99,
+ 0x0a4d3,
+ 0x0bbf6,
+ 0x0c762,
+ 0x0d847,
+ 0x0e60d,
+ 0x0f928,
+ 0x10b78,
+ 0x1145d,
+ 0x12a17,
+ 0x13532,
+ 0x149a6,
+ 0x15683,
+ 0x168c9,
+ 0x177ec,
+ 0x18ec4,
+ 0x191e1,
+ 0x1afab,
+ 0x1b08e,
+ 0x1cc1a,
+ 0x1d33f,
+ 0x1ed75,
+ 0x1f250,
+ 0x209d5,
+ 0x216f0,
+ 0x228ba,
+ 0x2379f,
+ 0x24b0b,
+ 0x2542e,
+ 0x26a64,
+ 0x27541,
+ 0x28c69,
+ }
+)
+
+const (
+ formatInfoLengthBits = 15
+ versionInfoLengthBits = 18
+)
+
+// formatInfo returns the 15-bit Format Information value for a QR
+// code.
+func (v qrCodeVersion) formatInfo(maskPattern int) *bitset.Bitset {
+ formatID := 0
+
+ switch v.level {
+ case Low:
+ formatID = 0x08 // 0b01000
+ case Medium:
+ formatID = 0x00 // 0b00000
+ case High:
+ formatID = 0x18 // 0b11000
+ case Highest:
+ formatID = 0x10 // 0b10000
+ default:
+ log.Panicf("Invalid level %d", v.level)
+ }
+
+ if maskPattern < 0 || maskPattern > 7 {
+ log.Panicf("Invalid maskPattern %d", maskPattern)
+ }
+
+ formatID |= maskPattern & 0x7
+
+ result := bitset.New()
+
+ result.AppendUint32(formatBitSequence[formatID].regular, formatInfoLengthBits)
+
+ return result
+}
+
+// versionInfo returns the 18-bit Version Information value for a QR Code.
+//
+// Version Information is applicable only to QR Codes versions 7-40 inclusive.
+// nil is returned if Version Information is not required.
+func (v qrCodeVersion) versionInfo() *bitset.Bitset {
+ if v.version < 7 {
+ return nil
+ }
+
+ result := bitset.New()
+ result.AppendUint32(versionBitSequence[v.version], 18)
+
+ return result
+}
+
+// numDataBits returns the data capacity in bits.
+func (v qrCodeVersion) numDataBits() int {
+ numDataBits := 0
+ for _, b := range v.block {
+ numDataBits += 8 * b.numBlocks * b.numDataCodewords // 8 bits in a byte
+ }
+
+ return numDataBits
+}
+
+// chooseQRCodeVersion chooses the most suitable QR Code version for a stated
+// data length in bits, the error recovery level required, and the data encoder
+// used.
+//
+// The chosen QR Code version is the smallest version able to fit numDataBits
+// and the optional terminator bits required by the specified encoder.
+//
+// On success the chosen QR Code version is returned.
+func chooseQRCodeVersion(level RecoveryLevel, encoder *dataEncoder, numDataBits int) *qrCodeVersion {
+ var chosenVersion *qrCodeVersion
+
+ for _, v := range versions {
+ if v.level != level {
+ continue
+ } else if v.version < encoder.minVersion {
+ continue
+ } else if v.version > encoder.maxVersion {
+ break
+ }
+
+ numFreeBits := v.numDataBits() - numDataBits
+
+ if numFreeBits >= 0 {
+ chosenVersion = &v
+ break
+ }
+ }
+
+ return chosenVersion
+}
+
+func (v qrCodeVersion) numTerminatorBitsRequired(numDataBits int) int {
+ numFreeBits := v.numDataBits() - numDataBits
+
+ var numTerminatorBits int
+
+ switch {
+ case numFreeBits >= 4:
+ numTerminatorBits = 4
+ default:
+ numTerminatorBits = numFreeBits
+ }
+
+ return numTerminatorBits
+}
+
+// numBlocks returns the number of blocks.
+func (v qrCodeVersion) numBlocks() int {
+ numBlocks := 0
+
+ for _, b := range v.block {
+ numBlocks += b.numBlocks
+ }
+
+ return numBlocks
+}
+
+// numBitsToPadToCodeword returns the number of bits required to pad data of
+// length numDataBits upto the nearest codeword size.
+func (v qrCodeVersion) numBitsToPadToCodeword(numDataBits int) int {
+ if numDataBits == v.numDataBits() {
+ return 0
+ }
+
+ return (8 - numDataBits%8) % 8
+}
+
+// symbolSize returns the size of the QR Code symbol in number of modules (which
+// is both the width and height, since QR codes are square). The QR Code has
+// size symbolSize() x symbolSize() pixels. This does not include the quiet
+// zone.
+func (v qrCodeVersion) symbolSize() int {
+ return 21 + (v.version-1)*4
+}
+
+// quietZoneSize returns the number of pixels of border space on each side of
+// the QR Code. The quiet space assists with decoding.
+func (v qrCodeVersion) quietZoneSize() int {
+ return 4
+}
+
+// getQRCodeVersion returns the QR Code version by version number and recovery
+// level. Returns nil if the requested combination is not defined.
+func getQRCodeVersion(level RecoveryLevel, version int) *qrCodeVersion {
+ for _, v := range versions {
+ if v.level == level && v.version == version {
+ return &v
+ }
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/tkrajina/gpxgo/gpx/LICENSE.txt b/vendor/github.com/tkrajina/gpxgo/gpx/LICENSE.txt
new file mode 100644
index 0000000..1af02f2
--- /dev/null
+++ b/vendor/github.com/tkrajina/gpxgo/gpx/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [2016-] [Tomo Krajina]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/vendor/github.com/tkrajina/gpxgo/gpx/converters.go b/vendor/github.com/tkrajina/gpxgo/gpx/converters.go
new file mode 100644
index 0000000..c3031ea
--- /dev/null
+++ b/vendor/github.com/tkrajina/gpxgo/gpx/converters.go
@@ -0,0 +1,616 @@
+// Copyright 2013, 2014 Peter Vasil, Tomo Krajina. All
+// rights reserved. Use of this source code is governed
+// by a BSD-style license that can be found in the
+// LICENSE file.
+
+package gpx
+
+import (
+ "strings"
+)
+
+const DEFAULT_CREATOR = "https://github.com/tkrajina/gpxgo"
+
+// ----------------------------------------------------------------------------------------------------
+// Gpx 1.0 Stuff
+// ----------------------------------------------------------------------------------------------------
+
+func convertToGpx10Models(gpxDoc *GPX) *gpx10Gpx {
+ gpx10Doc := &gpx10Gpx{}
+
+ //gpx10Doc.XMLNs = gpxDoc.XMLNs
+ gpx10Doc.XMLNs = "http://www.topografix.com/GPX/1/0"
+ gpx10Doc.XmlNsXsi = gpxDoc.XmlNsXsi
+ gpx10Doc.XmlSchemaLoc = gpxDoc.XmlSchemaLoc
+
+ gpx10Doc.Version = "1.0"
+ if len(gpxDoc.Creator) == 0 {
+ gpx10Doc.Creator = DEFAULT_CREATOR
+ } else {
+ gpx10Doc.Creator = gpxDoc.Creator
+ }
+ gpx10Doc.Name = gpxDoc.Name
+ gpx10Doc.Desc = gpxDoc.Description
+ gpx10Doc.Author = gpxDoc.AuthorName
+ gpx10Doc.Email = gpxDoc.AuthorEmail
+
+ if len(gpxDoc.AuthorLink) > 0 || len(gpxDoc.AuthorLinkText) > 0 {
+ // TODO
+ }
+
+ if len(gpxDoc.Link) > 0 || len(gpxDoc.LinkText) > 0 {
+ gpx10Doc.Url = gpxDoc.Link
+ gpx10Doc.UrlName = gpxDoc.LinkText
+ }
+
+ if gpxDoc.Time != nil {
+ gpx10Doc.Time = formatGPXTime(gpxDoc.Time)
+ }
+
+ gpx10Doc.Keywords = gpxDoc.Keywords
+
+ if gpxDoc.Waypoints != nil {
+ gpx10Doc.Waypoints = make([]*gpx10GpxPoint, len(gpxDoc.Waypoints))
+ for waypointNo, waypoint := range gpxDoc.Waypoints {
+ gpx10Doc.Waypoints[waypointNo] = convertPointToGpx10(&waypoint)
+ }
+ }
+
+ if gpxDoc.Routes != nil {
+ gpx10Doc.Routes = make([]*gpx10GpxRte, len(gpxDoc.Routes))
+ for routeNo, route := range gpxDoc.Routes {
+ r := new(gpx10GpxRte)
+ r.Name = route.Name
+ r.Cmt = route.Comment
+ r.Desc = route.Description
+ r.Src = route.Source
+ // TODO
+ //r.Links = route.Links
+ r.Number = route.Number
+ r.Type = route.Type
+ // TODO
+ //r.RoutePoints = route.RoutePoints
+
+ gpx10Doc.Routes[routeNo] = r
+
+ if route.Points != nil {
+ r.Points = make([]*gpx10GpxPoint, len(route.Points))
+ for pointNo, point := range route.Points {
+ r.Points[pointNo] = convertPointToGpx10(&point)
+ }
+ }
+ }
+ }
+
+ if gpxDoc.Tracks != nil {
+ gpx10Doc.Tracks = make([]*gpx10GpxTrk, len(gpxDoc.Tracks))
+ for trackNo, track := range gpxDoc.Tracks {
+ gpx10Track := new(gpx10GpxTrk)
+ gpx10Track.Name = track.Name
+ gpx10Track.Cmt = track.Comment
+ gpx10Track.Desc = track.Description
+ gpx10Track.Src = track.Source
+ gpx10Track.Number = track.Number
+ gpx10Track.Type = track.Type
+
+ if track.Segments != nil {
+ gpx10Track.Segments = make([]*gpx10GpxTrkSeg, len(track.Segments))
+ for segmentNo, segment := range track.Segments {
+ gpx10Segment := new(gpx10GpxTrkSeg)
+ if segment.Points != nil {
+ gpx10Segment.Points = make([]*gpx10GpxPoint, len(segment.Points))
+ for pointNo, point := range segment.Points {
+ gpx10Point := convertPointToGpx10(&point)
+ // TODO
+ //gpx10Point.Speed = point.Speed
+ //gpx10Point.Speed = point.Speed
+ gpx10Segment.Points[pointNo] = gpx10Point
+ }
+ }
+ gpx10Track.Segments[segmentNo] = gpx10Segment
+ }
+ }
+ gpx10Doc.Tracks[trackNo] = gpx10Track
+ }
+ }
+
+ return gpx10Doc
+}
+
+func convertFromGpx10Models(gpx10Doc *gpx10Gpx) *GPX {
+ gpxDoc := new(GPX)
+
+ gpxDoc.XMLNs = gpx10Doc.XMLNs
+ gpxDoc.XmlNsXsi = gpx10Doc.XmlNsXsi
+ gpxDoc.XmlSchemaLoc = gpx10Doc.XmlSchemaLoc
+
+ gpxDoc.Creator = gpx10Doc.Creator
+ gpxDoc.Version = gpx10Doc.Version
+ gpxDoc.Name = gpx10Doc.Name
+ gpxDoc.Description = gpx10Doc.Desc
+ gpxDoc.AuthorName = gpx10Doc.Author
+ gpxDoc.AuthorEmail = gpx10Doc.Email
+
+ if len(gpx10Doc.Url) > 0 || len(gpx10Doc.UrlName) > 0 {
+ gpxDoc.Link = gpx10Doc.Url
+ gpxDoc.LinkText = gpx10Doc.UrlName
+ }
+
+ if len(gpx10Doc.Time) > 0 {
+ gpxDoc.Time, _ = parseGPXTime(gpx10Doc.Time)
+ }
+
+ gpxDoc.Keywords = gpx10Doc.Keywords
+
+ if gpx10Doc.Waypoints != nil {
+ waypoints := make([]GPXPoint, len(gpx10Doc.Waypoints))
+ for waypointNo, waypoint := range gpx10Doc.Waypoints {
+ waypoints[waypointNo] = *convertPointFromGpx10(waypoint)
+ }
+ gpxDoc.Waypoints = waypoints
+ }
+
+ if gpx10Doc.Routes != nil {
+ gpxDoc.Routes = make([]GPXRoute, len(gpx10Doc.Routes))
+ for routeNo, route := range gpx10Doc.Routes {
+ r := new(GPXRoute)
+
+ r.Name = route.Name
+ r.Comment = route.Cmt
+ r.Description = route.Desc
+ r.Source = route.Src
+ // TODO
+ //r.Links = route.Links
+ r.Number = route.Number
+ r.Type = route.Type
+ // TODO
+ //r.RoutePoints = route.RoutePoints
+
+ if route.Points != nil {
+ r.Points = make([]GPXPoint, len(route.Points))
+ for pointNo, point := range route.Points {
+ r.Points[pointNo] = *convertPointFromGpx10(point)
+ }
+ }
+
+ gpxDoc.Routes[routeNo] = *r
+ }
+ }
+
+ if gpx10Doc.Tracks != nil {
+ gpxDoc.Tracks = make([]GPXTrack, len(gpx10Doc.Tracks))
+ for trackNo, track := range gpx10Doc.Tracks {
+ gpxTrack := new(GPXTrack)
+ gpxTrack.Name = track.Name
+ gpxTrack.Comment = track.Cmt
+ gpxTrack.Description = track.Desc
+ gpxTrack.Source = track.Src
+ gpxTrack.Number = track.Number
+ gpxTrack.Type = track.Type
+
+ if track.Segments != nil {
+ gpxTrack.Segments = make([]GPXTrackSegment, len(track.Segments))
+ for segmentNo, segment := range track.Segments {
+ gpxSegment := GPXTrackSegment{}
+ if segment.Points != nil {
+ gpxSegment.Points = make([]GPXPoint, len(segment.Points))
+ for pointNo, point := range segment.Points {
+ gpxSegment.Points[pointNo] = *convertPointFromGpx10(point)
+ }
+ }
+ gpxTrack.Segments[segmentNo] = gpxSegment
+ }
+ }
+ gpxDoc.Tracks[trackNo] = *gpxTrack
+ }
+ }
+
+ return gpxDoc
+}
+
+func convertPointToGpx10(original *GPXPoint) *gpx10GpxPoint {
+ result := new(gpx10GpxPoint)
+ result.Lat = original.Latitude
+ result.Lon = original.Longitude
+ result.Ele = original.Elevation
+ result.Timestamp = formatGPXTime(&original.Timestamp)
+ result.MagVar = original.MagneticVariation
+ result.GeoIdHeight = original.GeoidHeight
+ result.Name = original.Name
+ result.Cmt = original.Comment
+ result.Desc = original.Description
+ result.Src = original.Source
+ // TODO
+ //w.Links = original.Links
+ result.Sym = original.Symbol
+ result.Type = original.Type
+ result.Fix = original.TypeOfGpsFix
+ if original.Satellites.NotNull() {
+ value := original.Satellites.Value()
+ result.Sat = &value
+ }
+ if original.HorizontalDilution.NotNull() {
+ value := original.HorizontalDilution.Value()
+ result.Hdop = &value
+ }
+ if original.VerticalDilution.NotNull() {
+ value := original.VerticalDilution.Value()
+ result.Vdop = &value
+ }
+ if original.PositionalDilution.NotNull() {
+ value := original.PositionalDilution.Value()
+ result.Pdop = &value
+ }
+ if original.AgeOfDGpsData.NotNull() {
+ value := original.AgeOfDGpsData.Value()
+ result.AgeOfDGpsData = &value
+ }
+ if original.DGpsId.NotNull() {
+ value := original.DGpsId.Value()
+ result.DGpsId = &value
+ }
+ return result
+}
+
+func convertPointFromGpx10(original *gpx10GpxPoint) *GPXPoint {
+ result := new(GPXPoint)
+ result.Latitude = original.Lat
+ result.Longitude = original.Lon
+ result.Elevation = original.Ele
+ time, _ := parseGPXTime(original.Timestamp)
+ if time != nil {
+ result.Timestamp = *time
+ }
+ result.MagneticVariation = original.MagVar
+ result.GeoidHeight = original.GeoIdHeight
+ result.Name = original.Name
+ result.Comment = original.Cmt
+ result.Description = original.Desc
+ result.Source = original.Src
+ // TODO
+ //w.Links = original.Links
+ result.Symbol = original.Sym
+ result.Type = original.Type
+ result.TypeOfGpsFix = original.Fix
+ if original.Sat != nil {
+ result.Satellites = *NewNullableInt(*original.Sat)
+ }
+ if original.Hdop != nil {
+ result.HorizontalDilution = *NewNullableFloat64(*original.Hdop)
+ }
+ if original.Vdop != nil {
+ result.VerticalDilution = *NewNullableFloat64(*original.Vdop)
+ }
+ if original.Pdop != nil {
+ result.PositionalDilution = *NewNullableFloat64(*original.Pdop)
+ }
+ if original.AgeOfDGpsData != nil {
+ result.AgeOfDGpsData = *NewNullableFloat64(*original.AgeOfDGpsData)
+ }
+ if original.DGpsId != nil {
+ result.DGpsId = *NewNullableInt(*original.DGpsId)
+ }
+ return result
+}
+
+// ----------------------------------------------------------------------------------------------------
+// Gpx 1.1 Stuff
+// ----------------------------------------------------------------------------------------------------
+
+func convertToGpx11Models(gpxDoc *GPX) *gpx11Gpx {
+ gpx11Doc := &gpx11Gpx{}
+
+ gpx11Doc.Version = "1.1"
+
+ gpx11Doc.XMLNs = "http://www.topografix.com/GPX/1/1"
+ gpx11Doc.XmlNsXsi = gpxDoc.XmlNsXsi
+ gpx11Doc.XmlSchemaLoc = gpxDoc.XmlSchemaLoc
+
+ if len(gpxDoc.Creator) == 0 {
+ gpx11Doc.Creator = DEFAULT_CREATOR
+ } else {
+ gpx11Doc.Creator = gpxDoc.Creator
+ }
+ gpx11Doc.Name = gpxDoc.Name
+ gpx11Doc.Desc = gpxDoc.Description
+ gpx11Doc.AuthorName = gpxDoc.AuthorName
+
+ if len(gpxDoc.AuthorEmail) > 0 {
+ parts := strings.Split(gpxDoc.AuthorEmail, "@")
+ if len(parts) == 1 {
+ gpx11Doc.AuthorEmail = new(gpx11GpxEmail)
+ gpx11Doc.AuthorEmail.Id = parts[0]
+ } else if len(parts) > 1 {
+ gpx11Doc.AuthorEmail = new(gpx11GpxEmail)
+ gpx11Doc.AuthorEmail.Id = parts[0]
+ gpx11Doc.AuthorEmail.Domain = parts[1]
+ }
+ }
+
+ if len(gpxDoc.AuthorLink) > 0 || len(gpxDoc.AuthorLinkText) > 0 || len(gpxDoc.AuthorLinkType) > 0 {
+ gpx11Doc.AuthorLink = new(gpx11GpxLink)
+ gpx11Doc.AuthorLink.Href = gpxDoc.AuthorLink
+ gpx11Doc.AuthorLink.Text = gpxDoc.AuthorLinkText
+ gpx11Doc.AuthorLink.Type = gpxDoc.AuthorLinkType
+ }
+
+ if len(gpxDoc.Copyright) > 0 || len(gpxDoc.CopyrightYear) > 0 || len(gpxDoc.CopyrightLicense) > 0 {
+ gpx11Doc.Copyright = new(gpx11GpxCopyright)
+ gpx11Doc.Copyright.Author = gpxDoc.Copyright
+ gpx11Doc.Copyright.Year = gpxDoc.CopyrightYear
+ gpx11Doc.Copyright.License = gpxDoc.CopyrightLicense
+ }
+
+ if len(gpxDoc.Link) > 0 || len(gpxDoc.LinkText) > 0 || len(gpxDoc.LinkType) > 0 {
+ gpx11Doc.Link = new(gpx11GpxLink)
+ gpx11Doc.Link.Href = gpxDoc.Link
+ gpx11Doc.Link.Text = gpxDoc.LinkText
+ gpx11Doc.Link.Type = gpxDoc.LinkType
+ }
+
+ if gpxDoc.Time != nil {
+ gpx11Doc.Timestamp = formatGPXTime(gpxDoc.Time)
+ }
+
+ gpx11Doc.Keywords = gpxDoc.Keywords
+
+ if gpxDoc.Waypoints != nil {
+ gpx11Doc.Waypoints = make([]*gpx11GpxPoint, len(gpxDoc.Waypoints))
+ for waypointNo, waypoint := range gpxDoc.Waypoints {
+ gpx11Doc.Waypoints[waypointNo] = convertPointToGpx11(&waypoint)
+ }
+ }
+
+ if gpxDoc.Routes != nil {
+ gpx11Doc.Routes = make([]*gpx11GpxRte, len(gpxDoc.Routes))
+ for routeNo, route := range gpxDoc.Routes {
+ r := new(gpx11GpxRte)
+ r.Name = route.Name
+ r.Cmt = route.Comment
+ r.Desc = route.Description
+ r.Src = route.Source
+ // TODO
+ //r.Links = route.Links
+ r.Number = route.Number
+ r.Type = route.Type
+ // TODO
+ //r.RoutePoints = route.RoutePoints
+
+ gpx11Doc.Routes[routeNo] = r
+
+ if route.Points != nil {
+ r.Points = make([]*gpx11GpxPoint, len(route.Points))
+ for pointNo, point := range route.Points {
+ r.Points[pointNo] = convertPointToGpx11(&point)
+ }
+ }
+ }
+ }
+
+ if gpxDoc.Tracks != nil {
+ gpx11Doc.Tracks = make([]*gpx11GpxTrk, len(gpxDoc.Tracks))
+ for trackNo, track := range gpxDoc.Tracks {
+ gpx11Track := new(gpx11GpxTrk)
+ gpx11Track.Name = track.Name
+ gpx11Track.Cmt = track.Comment
+ gpx11Track.Desc = track.Description
+ gpx11Track.Src = track.Source
+ gpx11Track.Number = track.Number
+ gpx11Track.Type = track.Type
+
+ if track.Segments != nil {
+ gpx11Track.Segments = make([]*gpx11GpxTrkSeg, len(track.Segments))
+ for segmentNo, segment := range track.Segments {
+ gpx11Segment := new(gpx11GpxTrkSeg)
+ if segment.Points != nil {
+ gpx11Segment.Points = make([]*gpx11GpxPoint, len(segment.Points))
+ for pointNo, point := range segment.Points {
+ gpx11Segment.Points[pointNo] = convertPointToGpx11(&point)
+ }
+ }
+ gpx11Track.Segments[segmentNo] = gpx11Segment
+ }
+ }
+ gpx11Doc.Tracks[trackNo] = gpx11Track
+ }
+ }
+
+ return gpx11Doc
+}
+
+func convertFromGpx11Models(gpx11Doc *gpx11Gpx) *GPX {
+ gpxDoc := new(GPX)
+
+ gpxDoc.XMLNs = gpxDoc.XMLNs
+ gpxDoc.XmlNsXsi = gpxDoc.XmlNsXsi
+ gpxDoc.XmlSchemaLoc = gpxDoc.XmlSchemaLoc
+
+ gpxDoc.Creator = gpx11Doc.Creator
+ gpxDoc.Version = gpx11Doc.Version
+ gpxDoc.Name = gpx11Doc.Name
+ gpxDoc.Description = gpx11Doc.Desc
+ gpxDoc.AuthorName = gpx11Doc.AuthorName
+
+ if gpx11Doc.AuthorEmail != nil {
+ gpxDoc.AuthorEmail = gpx11Doc.AuthorEmail.Id + "@" + gpx11Doc.AuthorEmail.Domain
+ }
+ if gpx11Doc.AuthorLink != nil {
+ gpxDoc.AuthorLink = gpx11Doc.AuthorLink.Href
+ gpxDoc.AuthorLinkText = gpx11Doc.AuthorLink.Text
+ gpxDoc.AuthorLinkType = gpx11Doc.AuthorLink.Type
+ }
+
+ /* TODO
+ if gpx11Doc.Extensions != nil {
+ gpxDoc.Extensions = &gpx11Doc.Extensions.Bytes
+ }
+ */
+
+ if len(gpx11Doc.Timestamp) > 0 {
+ gpxDoc.Time, _ = parseGPXTime(gpx11Doc.Timestamp)
+ }
+
+ if gpx11Doc.Copyright != nil {
+ gpxDoc.Copyright = gpx11Doc.Copyright.Author
+ gpxDoc.CopyrightYear = gpx11Doc.Copyright.Year
+ gpxDoc.CopyrightLicense = gpx11Doc.Copyright.License
+ }
+
+ if gpx11Doc.Link != nil {
+ gpxDoc.Link = gpx11Doc.Link.Href
+ gpxDoc.LinkText = gpx11Doc.Link.Text
+ gpxDoc.LinkType = gpx11Doc.Link.Type
+ }
+
+ gpxDoc.Keywords = gpx11Doc.Keywords
+
+ if gpx11Doc.Waypoints != nil {
+ waypoints := make([]GPXPoint, len(gpx11Doc.Waypoints))
+ for waypointNo, waypoint := range gpx11Doc.Waypoints {
+ waypoints[waypointNo] = *convertPointFromGpx11(waypoint)
+ }
+ gpxDoc.Waypoints = waypoints
+ }
+
+ if gpx11Doc.Routes != nil {
+ gpxDoc.Routes = make([]GPXRoute, len(gpx11Doc.Routes))
+ for routeNo, route := range gpx11Doc.Routes {
+ r := new(GPXRoute)
+
+ r.Name = route.Name
+ r.Comment = route.Cmt
+ r.Description = route.Desc
+ r.Source = route.Src
+ // TODO
+ //r.Links = route.Links
+ r.Number = route.Number
+ r.Type = route.Type
+ // TODO
+ //r.RoutePoints = route.RoutePoints
+
+ if route.Points != nil {
+ r.Points = make([]GPXPoint, len(route.Points))
+ for pointNo, point := range route.Points {
+ r.Points[pointNo] = *convertPointFromGpx11(point)
+ }
+ }
+
+ gpxDoc.Routes[routeNo] = *r
+ }
+ }
+
+ if gpx11Doc.Tracks != nil {
+ gpxDoc.Tracks = make([]GPXTrack, len(gpx11Doc.Tracks))
+ for trackNo, track := range gpx11Doc.Tracks {
+ gpxTrack := new(GPXTrack)
+ gpxTrack.Name = track.Name
+ gpxTrack.Comment = track.Cmt
+ gpxTrack.Description = track.Desc
+ gpxTrack.Source = track.Src
+ gpxTrack.Number = track.Number
+ gpxTrack.Type = track.Type
+
+ if track.Segments != nil {
+ gpxTrack.Segments = make([]GPXTrackSegment, len(track.Segments))
+ for segmentNo, segment := range track.Segments {
+ gpxSegment := GPXTrackSegment{}
+ if segment.Points != nil {
+ gpxSegment.Points = make([]GPXPoint, len(segment.Points))
+ for pointNo, point := range segment.Points {
+ gpxSegment.Points[pointNo] = *convertPointFromGpx11(point)
+ }
+ }
+ gpxTrack.Segments[segmentNo] = gpxSegment
+ }
+ }
+ gpxDoc.Tracks[trackNo] = *gpxTrack
+ }
+ }
+
+ return gpxDoc
+}
+
+func convertPointToGpx11(original *GPXPoint) *gpx11GpxPoint {
+ result := new(gpx11GpxPoint)
+ result.Lat = original.Latitude
+ result.Lon = original.Longitude
+ result.Ele = original.Elevation
+ result.Timestamp = formatGPXTime(&original.Timestamp)
+ result.MagVar = original.MagneticVariation
+ result.GeoIdHeight = original.GeoidHeight
+ result.Name = original.Name
+ result.Cmt = original.Comment
+ result.Desc = original.Description
+ result.Src = original.Source
+ // TODO
+ //w.Links = original.Links
+ result.Sym = original.Symbol
+ result.Type = original.Type
+ result.Fix = original.TypeOfGpsFix
+ if original.Satellites.NotNull() {
+ value := original.Satellites.Value()
+ result.Sat = &value
+ }
+ if original.HorizontalDilution.NotNull() {
+ value := original.HorizontalDilution.Value()
+ result.Hdop = &value
+ }
+ if original.VerticalDilution.NotNull() {
+ value := original.VerticalDilution.Value()
+ result.Vdop = &value
+ }
+ if original.PositionalDilution.NotNull() {
+ value := original.PositionalDilution.Value()
+ result.Pdop = &value
+ }
+ if original.AgeOfDGpsData.NotNull() {
+ value := original.AgeOfDGpsData.Value()
+ result.AgeOfDGpsData = &value
+ }
+ if original.DGpsId.NotNull() {
+ value := original.DGpsId.Value()
+ result.DGpsId = &value
+ }
+ return result
+}
+
+func convertPointFromGpx11(original *gpx11GpxPoint) *GPXPoint {
+ result := new(GPXPoint)
+ result.Latitude = original.Lat
+ result.Longitude = original.Lon
+ result.Elevation = original.Ele
+ time, _ := parseGPXTime(original.Timestamp)
+ if time != nil {
+ result.Timestamp = *time
+ }
+ result.MagneticVariation = original.MagVar
+ result.GeoidHeight = original.GeoIdHeight
+ result.Name = original.Name
+ result.Comment = original.Cmt
+ result.Description = original.Desc
+ result.Source = original.Src
+ // TODO
+ //w.Links = original.Links
+ result.Symbol = original.Sym
+ result.Type = original.Type
+ result.TypeOfGpsFix = original.Fix
+ if original.Sat != nil {
+ result.Satellites = *NewNullableInt(*original.Sat)
+ }
+ if original.Hdop != nil {
+ result.HorizontalDilution = *NewNullableFloat64(*original.Hdop)
+ }
+ if original.Vdop != nil {
+ result.VerticalDilution = *NewNullableFloat64(*original.Vdop)
+ }
+ if original.Pdop != nil {
+ result.PositionalDilution = *NewNullableFloat64(*original.Pdop)
+ }
+ if original.AgeOfDGpsData != nil {
+ result.AgeOfDGpsData = *NewNullableFloat64(*original.AgeOfDGpsData)
+ }
+ if original.DGpsId != nil {
+ result.DGpsId = *NewNullableInt(*original.DGpsId)
+ }
+ return result
+}
diff --git a/vendor/github.com/tkrajina/gpxgo/gpx/geo.go b/vendor/github.com/tkrajina/gpxgo/gpx/geo.go
new file mode 100644
index 0000000..b18008d
--- /dev/null
+++ b/vendor/github.com/tkrajina/gpxgo/gpx/geo.go
@@ -0,0 +1,357 @@
+// Copyright 2013, 2014 Peter Vasil, Tomo Krajina. All
+// rights reserved. Use of this source code is governed
+// by a BSD-style license that can be found in the
+// LICENSE file.
+
+package gpx
+
+import (
+ "math"
+ "sort"
+)
+
+const oneDegree = 1000.0 * 10000.8 / 90.0
+const earthRadius = 6371 * 1000
+
+func ToRad(x float64) float64 {
+ return x / 180. * math.Pi
+}
+
+type Location interface {
+ GetLatitude() float64
+ GetLongitude() float64
+ GetElevation() NullableFloat64
+}
+
+type MovingData struct {
+ MovingTime float64
+ StoppedTime float64
+ MovingDistance float64
+ StoppedDistance float64
+ MaxSpeed float64
+}
+
+func (md MovingData) Equals(md2 MovingData) bool {
+ return md.MovingTime == md2.MovingTime &&
+ md.MovingDistance == md2.MovingDistance &&
+ md.StoppedTime == md2.StoppedTime &&
+ md.StoppedDistance == md2.StoppedDistance &&
+ md.MaxSpeed == md.MaxSpeed
+}
+
+type SpeedsAndDistances struct {
+ Speed float64
+ Distance float64
+}
+
+// HaversineDistance returns the haversine distance between two points.
+//
+// Implemented from http://www.movable-type.co.uk/scripts/latlong.html
+func HaversineDistance(lat1, lon1, lat2, lon2 float64) float64 {
+ dLat := ToRad(lat1 - lat2)
+ dLon := ToRad(lon1 - lon2)
+ thisLat1 := ToRad(lat1)
+ thisLat2 := ToRad(lat2)
+
+ a := math.Sin(dLat/2)*math.Sin(dLat/2) + math.Sin(dLon/2)*math.Sin(dLon/2)*math.Cos(thisLat1)*math.Cos(thisLat2)
+ c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))
+ d := earthRadius * c
+
+ return d
+}
+
+func length(locs []Point, threeD bool) float64 {
+ var previousLoc Point
+ var res float64
+ for k, v := range locs {
+ if k > 0 {
+ previousLoc = locs[k-1]
+ var d float64
+ if threeD {
+ d = v.Distance3D(&previousLoc)
+ } else {
+ d = v.Distance2D(&previousLoc)
+ }
+ res += d
+ }
+ }
+ return res
+}
+
+func Length2D(locs []Point) float64 {
+ return length(locs, false)
+}
+
+func Length3D(locs []Point) float64 {
+ return length(locs, true)
+}
+
+func CalcMaxSpeed(speedsDistances []SpeedsAndDistances) float64 {
+ lenArrs := len(speedsDistances)
+
+ if len(speedsDistances) < 20 {
+ //log.Println("Segment too small to compute speed, size: ", lenArrs)
+ return 0.0
+ }
+
+ var sum_dists float64
+ for _, d := range speedsDistances {
+ sum_dists += d.Distance
+ }
+ average_dist := sum_dists / float64(lenArrs)
+
+ var variance float64
+ for i := 0; i < len(speedsDistances); i++ {
+ variance += math.Pow(speedsDistances[i].Distance-average_dist, 2)
+ }
+ stdDeviation := math.Sqrt(variance)
+
+ // ignore items with distance too long
+ filteredSD := make([]SpeedsAndDistances, 0)
+ for i := 0; i < len(speedsDistances); i++ {
+ dist := math.Abs(speedsDistances[i].Distance - average_dist)
+ if dist <= stdDeviation*1.5 {
+ filteredSD = append(filteredSD, speedsDistances[i])
+ }
+ }
+
+ speeds := make([]float64, len(filteredSD))
+ for i, sd := range filteredSD {
+ speeds[i] = sd.Speed
+ }
+
+ speedsSorted := sort.Float64Slice(speeds)
+
+ if len(speedsSorted) == 0 {
+ return 0
+ }
+
+ maxIdx := int(float64(len(speedsSorted)) * 0.95)
+ if maxIdx >= len(speedsSorted) {
+ maxIdx = len(speedsSorted) - 1
+ }
+ if maxIdx < 0 {
+ maxIdx = 0
+ }
+ return speedsSorted[maxIdx]
+}
+
+func CalcUphillDownhill(elevations []NullableFloat64) (float64, float64) {
+ elevsLen := len(elevations)
+ if elevsLen == 0 {
+ return 0.0, 0.0
+ }
+
+ smoothElevations := make([]NullableFloat64, elevsLen)
+
+ for i, elev := range elevations {
+ currEle := elev
+ if 0 < i && i < elevsLen-1 {
+ prevEle := elevations[i-1]
+ nextEle := elevations[i+1]
+ if prevEle.NotNull() && nextEle.NotNull() && elev.NotNull() {
+ currEle = *NewNullableFloat64(prevEle.Value()*0.3 + elev.Value()*0.4 + nextEle.Value()*0.3)
+ }
+ }
+ smoothElevations[i] = currEle
+ }
+
+ var uphill float64
+ var downhill float64
+
+ for i := 1; i < len(smoothElevations); i++ {
+ if smoothElevations[i].NotNull() && smoothElevations[i-1].NotNull() {
+ d := smoothElevations[i].Value() - smoothElevations[i-1].Value()
+ if d > 0.0 {
+ uphill += d
+ } else {
+ downhill -= d
+ }
+ }
+ }
+
+ return uphill, downhill
+}
+
+func distance(lat1, lon1 float64, ele1 NullableFloat64, lat2, lon2 float64, ele2 NullableFloat64, threeD, haversine bool) float64 {
+ absLat := math.Abs(lat1 - lat2)
+ absLon := math.Abs(lon1 - lon2)
+ if haversine || absLat > 0.2 || absLon > 0.2 {
+ return HaversineDistance(lat1, lon1, lat2, lon2)
+ }
+
+ coef := math.Cos(ToRad(lat1))
+ x := lat1 - lat2
+ y := (lon1 - lon2) * coef
+
+ distance2d := math.Sqrt(x*x+y*y) * oneDegree
+
+ if !threeD || ele1 == ele2 {
+ return distance2d
+ }
+
+ eleDiff := 0.0
+ if ele1.NotNull() && ele2.NotNull() {
+ eleDiff = ele1.Value() - ele2.Value()
+ }
+
+ return math.Sqrt(math.Pow(distance2d, 2) + math.Pow(eleDiff, 2))
+}
+
+func distanceBetweenLocations(loc1, loc2 Location, threeD, haversine bool) float64 {
+ lat1 := loc1.GetLatitude()
+ lon1 := loc1.GetLongitude()
+ ele1 := loc1.GetElevation()
+
+ lat2 := loc2.GetLatitude()
+ lon2 := loc2.GetLongitude()
+ ele2 := loc2.GetElevation()
+
+ return distance(lat1, lon1, ele1, lat2, lon2, ele2, threeD, haversine)
+}
+
+func Distance2D(lat1, lon1, lat2, lon2 float64, haversine bool) float64 {
+ return distance(lat1, lon1, *new(NullableFloat64), lat2, lon2, *new(NullableFloat64), false, haversine)
+}
+
+func Distance3D(lat1, lon1 float64, ele1 NullableFloat64, lat2, lon2 float64, ele2 NullableFloat64, haversine bool) float64 {
+ return distance(lat1, lon1, ele1, lat2, lon2, ele2, true, haversine)
+}
+
+func ElevationAngle(loc1, loc2 Point, radians bool) float64 {
+ if loc1.Elevation.Null() || loc2.Elevation.Null() {
+ return 0.0
+ }
+
+ b := loc2.Elevation.Value() - loc1.Elevation.Value()
+ a := loc2.Distance2D(&loc1)
+
+ if a == 0.0 {
+ return 0.0
+ }
+
+ angle := math.Atan(b / a)
+
+ if radians {
+ return angle
+ }
+
+ return 180 * angle / math.Pi
+}
+
+// Distance of point from a line given with two points.
+func distanceFromLine(point Point, linePoint1, linePoint2 GPXPoint) float64 {
+ a := linePoint1.Distance2D(&linePoint2)
+
+ if a == 0 {
+ return linePoint1.Distance2D(&point)
+ }
+
+ b := linePoint1.Distance2D(&point)
+ c := linePoint2.Distance2D(&point)
+
+ s := (a + b + c) / 2.
+
+ return 2.0 * math.Sqrt(math.Abs((s * (s - a) * (s - b) * (s - c)))) / a
+}
+
+func getLineEquationCoefficients(location1, location2 Point) (float64, float64, float64) {
+ if location1.Longitude == location2.Longitude {
+ // Vertical line:
+ return 0.0, 1.0, -location1.Longitude
+ } else {
+ a := (location1.Latitude - location2.Latitude) / (location1.Longitude - location2.Longitude)
+ b := location1.Latitude - location1.Longitude*a
+ return 1.0, -a, -b
+ }
+}
+
+func simplifyPoints(points []GPXPoint, maxDistance float64) []GPXPoint {
+ if len(points) < 3 {
+ return points
+ }
+
+ begin, end := points[0], points[len(points)-1]
+
+ /*
+ Use a "normal" line just to detect the most distant point (not its real distance)
+ this is because this is faster to compute than calling distance_from_line() for
+ every point.
+
+ This is an approximation and may have some errors near the poles and if
+ the points are too distant, but it should be good enough for most use
+ cases...
+ */
+ a, b, c := getLineEquationCoefficients(begin.Point, end.Point)
+
+ tmpMaxDistance := -1000000000.0
+ tmpMaxDistancePosition := 0
+ for pointNo, point := range points {
+ d := math.Abs(a*point.Latitude + b*point.Longitude + c)
+ if d > tmpMaxDistance {
+ tmpMaxDistance = d
+ tmpMaxDistancePosition = pointNo
+ }
+ }
+
+ //fmt.Println()
+
+ //fmt.Println("tmpMaxDistancePosition=", tmpMaxDistancePosition, " len(points)=", len(points))
+
+ realMaxDistance := distanceFromLine(points[tmpMaxDistancePosition].Point, begin, end)
+ //fmt.Println("realMaxDistance=", realMaxDistance, " len(points)=", len(points))
+
+ if realMaxDistance < maxDistance {
+ return []GPXPoint{begin, end}
+ }
+
+ points1 := points[:tmpMaxDistancePosition]
+ point := points[tmpMaxDistancePosition]
+ points2 := points[tmpMaxDistancePosition+1:]
+
+ //fmt.Println("before simplify: len_points=", len(points), " l_points1=", len(points1), " l_points2=", len(points2))
+
+ points1 = simplifyPoints(points1, maxDistance)
+ points2 = simplifyPoints(points2, maxDistance)
+
+ //fmt.Println("after simplify: len_points=", len(points), " l_points1=", len(points1), " l_points2=", len(points2))
+
+ result := append(points1, point)
+ return append(result, points2...)
+}
+
+func smoothHorizontal(originalPoints []GPXPoint) []GPXPoint {
+ result := make([]GPXPoint, len(originalPoints))
+
+ for pointNo, point := range originalPoints {
+ result[pointNo] = point
+ if 1 <= pointNo && pointNo <= len(originalPoints)-2 {
+ previousPoint := originalPoints[pointNo-1]
+ nextPoint := originalPoints[pointNo+1]
+ result[pointNo] = point
+ result[pointNo].Latitude = previousPoint.Latitude*0.4 + point.Latitude*0.2 + nextPoint.Latitude*0.4
+ result[pointNo].Longitude = previousPoint.Longitude*0.4 + point.Longitude*0.2 + nextPoint.Longitude*0.4
+ //log.Println("->(%f, %f)", seg.Points[pointNo].Latitude, seg.Points[pointNo].Longitude)
+ }
+ }
+
+ return result
+}
+
+func smoothVertical(originalPoints []GPXPoint) []GPXPoint {
+ result := make([]GPXPoint, len(originalPoints))
+
+ for pointNo, point := range originalPoints {
+ result[pointNo] = point
+ if 1 <= pointNo && pointNo <= len(originalPoints)-2 {
+ previousPointElevation := originalPoints[pointNo-1].Elevation
+ nextPointElevation := originalPoints[pointNo+1].Elevation
+ if previousPointElevation.NotNull() && point.Elevation.NotNull() && nextPointElevation.NotNull() {
+ result[pointNo].Elevation = *NewNullableFloat64(previousPointElevation.Value()*0.4 + point.Elevation.Value()*0.2 + nextPointElevation.Value()*0.4)
+ //log.Println("->%f", seg.Points[pointNo].Elevation.Value())
+ }
+ }
+ }
+
+ return result
+}
diff --git a/vendor/github.com/tkrajina/gpxgo/gpx/gpx.go b/vendor/github.com/tkrajina/gpxgo/gpx/gpx.go
new file mode 100644
index 0000000..7646aa4
--- /dev/null
+++ b/vendor/github.com/tkrajina/gpxgo/gpx/gpx.go
@@ -0,0 +1,1582 @@
+// Copyright 2013, 2014 Peter Vasil, Tomo Krajina. All
+// rights reserved. Use of this source code is governed
+// by a BSD-style license that can be found in the
+// LICENSE file.
+
+package gpx
+
+import (
+ "fmt"
+ "math"
+ "time"
+)
+
+const (
+ DEFAULT_STOPPED_SPEED_THRESHOLD = 1.0
+ REMOVE_EXTREEMES_TRESHOLD = 10
+)
+
+// ----------------------------------------------------------------------------------------------------
+
+// Some basic stats all common GPX elements (GPX, track and segment) must have
+type GPXElementInfo interface {
+ Length2D() float64
+ Length3D() float64
+ Bounds() GpxBounds
+ MovingData() MovingData
+ UphillDownhill() UphillDownhill
+ TimeBounds() TimeBounds
+ GetTrackPointsNo() int
+}
+
+// Pretty prints some basic information about this GPX elements
+func GetGpxElementInfo(prefix string, gpxDoc GPXElementInfo) string {
+ result := ""
+ result += fmt.Sprint(prefix, " Points: ", gpxDoc.GetTrackPointsNo(), "\n")
+ result += fmt.Sprint(prefix, " Length 2D: ", gpxDoc.Length2D()/1000.0, "\n")
+ result += fmt.Sprint(prefix, " Length 3D: ", gpxDoc.Length3D()/1000.0, "\n")
+
+ bounds := gpxDoc.Bounds()
+ result += fmt.Sprintf("%s Bounds: %f, %f, %f, %f\n", prefix, bounds.MinLatitude, bounds.MaxLatitude, bounds.MinLongitude, bounds.MaxLongitude)
+
+ md := gpxDoc.MovingData()
+ result += fmt.Sprint(prefix, " Moving time: ", md.MovingTime, "\n")
+ result += fmt.Sprint(prefix, " Stopped time: ", md.StoppedTime, "\n")
+
+ result += fmt.Sprintf("%s Max speed: %fm/s = %fkm/h\n", prefix, md.MaxSpeed, md.MaxSpeed*60*60/1000.0)
+
+ updo := gpxDoc.UphillDownhill()
+ result += fmt.Sprint(prefix, " Total uphill: ", updo.Uphill, "\n")
+ result += fmt.Sprint(prefix, " Total downhill: ", updo.Downhill, "\n")
+
+ timeBounds := gpxDoc.TimeBounds()
+ result += fmt.Sprint(prefix, " Started: ", timeBounds.StartTime, "\n")
+ result += fmt.Sprint(prefix, " Ended: ", timeBounds.EndTime, "\n")
+ return result
+}
+
+// ----------------------------------------------------------------------------------------------------
+
+type GPX struct {
+ XMLNs string
+ XmlNsXsi string
+ XmlSchemaLoc string
+
+ Version string
+ Creator string
+ Name string
+ Description string
+ AuthorName string
+ AuthorEmail string
+ AuthorLink string
+ AuthorLinkText string
+ AuthorLinkType string
+ Copyright string
+ CopyrightYear string
+ CopyrightLicense string
+ Link string
+ LinkText string
+ LinkType string
+ Time *time.Time
+ Keywords string
+
+ // TODO
+ //Extensions []byte
+ Waypoints []GPXPoint
+ Routes []GPXRoute
+ Tracks []GPXTrack
+}
+
+// Params are optional, you can set null to use GPXs Version and no indentation.
+func (g *GPX) ToXml(params ToXmlParams) ([]byte, error) {
+ return ToXml(g, params)
+}
+
+// Pretty prints some basic information about this GPX, its track and segments
+func (g *GPX) GetGpxInfo() string {
+ result := ""
+ result += fmt.Sprint("GPX name: ", g.Name, "\n")
+ result += fmt.Sprint("GPX desctiption: ", g.Description, "\n")
+ result += fmt.Sprint("GPX version: ", g.Version, "\n")
+ result += fmt.Sprint("Author: ", g.AuthorName, "\n")
+ result += fmt.Sprint("Email: ", g.AuthorEmail, "\n\n")
+
+ result += fmt.Sprint("\nGlobal stats:", "\n")
+ result += GetGpxElementInfo("", g)
+ result += "\n"
+
+ for trackNo, track := range g.Tracks {
+ result += fmt.Sprintf("\nTrack #%d:\n", 1+trackNo)
+ result += GetGpxElementInfo(" ", &track)
+ result += "\n"
+ for segmentNo, segment := range track.Segments {
+ result += fmt.Sprintf("\nTrack #%d, segment #%d:\n", 1+trackNo, 1+segmentNo)
+ result += GetGpxElementInfo(" ", &segment)
+ result += "\n"
+ }
+ }
+ return result
+}
+
+func (g *GPX) GetTrackPointsNo() int {
+ result := 0
+ for _, track := range g.Tracks {
+ result += track.GetTrackPointsNo()
+ }
+ return result
+}
+
+// Length2D returns the 2D length of all tracks in a Gpx.
+func (g *GPX) Length2D() float64 {
+ var length2d float64
+ for _, trk := range g.Tracks {
+ length2d += trk.Length2D()
+ }
+ return length2d
+}
+
+// Length3D returns the 3D length of all tracks,
+func (g *GPX) Length3D() float64 {
+ var length3d float64
+ for _, trk := range g.Tracks {
+ length3d += trk.Length3D()
+ }
+ return length3d
+}
+
+// TimeBounds returns the time bounds of all tacks in a Gpx.
+func (g *GPX) TimeBounds() TimeBounds {
+ var tbGpx TimeBounds
+ for i, trk := range g.Tracks {
+ tbTrk := trk.TimeBounds()
+ if i == 0 {
+ tbGpx = trk.TimeBounds()
+ } else {
+ tbGpx.EndTime = tbTrk.EndTime
+ }
+ }
+ return tbGpx
+}
+
+// Bounds returns the bounds of all tracks in a Gpx.
+func (g *GPX) Bounds() GpxBounds {
+ minmax := getMaximalGpxBounds()
+ for _, trk := range g.Tracks {
+ bnds := trk.Bounds()
+ minmax.MaxLatitude = math.Max(bnds.MaxLatitude, minmax.MaxLatitude)
+ minmax.MinLatitude = math.Min(bnds.MinLatitude, minmax.MinLatitude)
+ minmax.MaxLongitude = math.Max(bnds.MaxLongitude, minmax.MaxLongitude)
+ minmax.MinLongitude = math.Min(bnds.MinLongitude, minmax.MinLongitude)
+ }
+ return minmax
+}
+
+func (g *GPX) ElevationBounds() ElevationBounds {
+ minmax := getMaximalElevationBounds()
+ for _, trk := range g.Tracks {
+ bnds := trk.ElevationBounds()
+ minmax.MaxElevation = math.Max(bnds.MaxElevation, minmax.MaxElevation)
+ minmax.MinElevation = math.Min(bnds.MinElevation, minmax.MinElevation)
+ }
+ return minmax
+}
+
+// MovingData returns the moving data for all tracks in a Gpx.
+func (g *GPX) MovingData() MovingData {
+ var (
+ movingTime float64
+ stoppedTime float64
+ movingDistance float64
+ stoppedDistance float64
+ maxSpeed float64
+ )
+
+ for _, trk := range g.Tracks {
+ md := trk.MovingData()
+ movingTime += md.MovingTime
+ stoppedTime += md.StoppedTime
+ movingDistance += md.MovingDistance
+ stoppedDistance += md.StoppedDistance
+
+ if md.MaxSpeed > maxSpeed {
+ maxSpeed = md.MaxSpeed
+ }
+ }
+ return MovingData{
+ MovingTime: movingTime,
+ MovingDistance: movingDistance,
+ StoppedTime: stoppedTime,
+ StoppedDistance: stoppedDistance,
+ MaxSpeed: maxSpeed,
+ }
+}
+
+func (g *GPX) ReduceTrackPoints(maxPointsNo int, minDistanceBetween float64) {
+ pointsNo := g.GetTrackPointsNo()
+
+ if pointsNo < maxPointsNo && minDistanceBetween <= 0 {
+ return
+ }
+
+ length := g.Length3D()
+
+ minDistance := math.Max(float64(minDistanceBetween), math.Ceil(length/float64(maxPointsNo)))
+
+ for _, track := range g.Tracks {
+ track.ReduceTrackPoints(minDistance)
+ }
+}
+
+func (g *GPX) SimplifyTracks(maxDistance float64) {
+ for _, track := range g.Tracks {
+ track.SimplifyTracks(maxDistance)
+ }
+}
+
+// Split splits the Gpx segment segNo in a given track trackNo at
+// pointNo.
+func (g *GPX) Split(trackNo, segNo, pointNo int) {
+ if trackNo >= len(g.Tracks) {
+ return
+ }
+
+ track := &g.Tracks[trackNo]
+
+ track.Split(segNo, pointNo)
+}
+
+// Duration returns the duration of all tracks in a Gpx in seconds.
+func (g *GPX) Duration() float64 {
+ if len(g.Tracks) == 0 {
+ return 0.0
+ }
+ var result float64
+ for _, trk := range g.Tracks {
+ result += trk.Duration()
+ }
+
+ return result
+}
+
+// UphillDownhill returns uphill and downhill values for all tracks in a
+// Gpx.
+func (g *GPX) UphillDownhill() UphillDownhill {
+ if len(g.Tracks) == 0 {
+ return UphillDownhill{
+ Uphill: 0.0,
+ Downhill: 0.0,
+ }
+ }
+
+ var (
+ uphill float64
+ downhill float64
+ )
+
+ for _, trk := range g.Tracks {
+ updo := trk.UphillDownhill()
+
+ uphill += updo.Uphill
+ downhill += updo.Downhill
+ }
+
+ return UphillDownhill{
+ Uphill: uphill,
+ Downhill: downhill,
+ }
+}
+
+// Checks if *tracks* and segments have time information. Routes and Waypoints are ignored.
+func (g *GPX) HasTimes() bool {
+ result := true
+ for _, track := range g.Tracks {
+ result = result && track.HasTimes()
+ }
+ return result
+}
+
+// PositionAt returns a LocationResultsPair consisting the segment index
+// and the GpxWpt at a certain time.
+func (g *GPX) PositionAt(t time.Time) []TrackPosition {
+ results := make([]TrackPosition, 0)
+
+ for trackNo, trk := range g.Tracks {
+ locs := trk.PositionAt(t)
+ if len(locs) > 0 {
+ for locNo := range locs {
+ locs[locNo].TrackNo = trackNo
+ }
+ results = append(results, locs...)
+ }
+ }
+ return results
+}
+
+func (g *GPX) StoppedPositions() []TrackPosition {
+ result := make([]TrackPosition, 0)
+ for trackNo, track := range g.Tracks {
+ positions := track.StoppedPositions()
+ for _, position := range positions {
+ position.TrackNo = trackNo
+ result = append(result, position)
+ }
+ }
+ return result
+}
+
+func (g *GPX) getDistancesFromStart(distanceBetweenPoints float64) [][][]float64 {
+ result := make([][][]float64, len(g.Tracks))
+ var fromStart float64
+ var lastSampledPoint float64
+ for trackNo, track := range g.Tracks {
+ result[trackNo] = make([][]float64, len(track.Segments))
+ for segmentNo, segment := range track.Segments {
+ result[trackNo][segmentNo] = make([]float64, len(segment.Points))
+ for pointNo, point := range segment.Points {
+ if pointNo > 0 {
+ fromStart += point.Distance2D(&segment.Points[pointNo-1])
+ }
+ if pointNo == 0 || pointNo == len(segment.Points)-1 || fromStart-lastSampledPoint > distanceBetweenPoints {
+ result[trackNo][segmentNo][pointNo] = fromStart
+ lastSampledPoint = fromStart
+ } else {
+ result[trackNo][segmentNo][pointNo] = -1
+ }
+ }
+ }
+ }
+ return result
+}
+
+// Finds locations candidates where this location is on a track. Returns an
+// array of distances from start for every given location. Used (for example)
+// for positioning waypoints on the graph.
+// The bigger the samples number the more granular the search will be. For
+// example if samples is 100 then (cca) every 100th point will be searched.
+// This is for tracks with thousands of waypoints -- computing distances for
+// each and every point is slow.
+func (g *GPX) GetLocationsPositionsOnTrack(samples int, locations ...Location) [][]float64 {
+ length2d := g.Length2D()
+ distancesFromStart := g.getDistancesFromStart(length2d / float64(samples))
+ result := make([][]float64, len(locations))
+ for locationNo, location := range locations {
+ result[locationNo] = g.getPositionsOnTrackWithPrecomputedDistances(location, distancesFromStart, length2d)
+ }
+ return result
+}
+
+// Use always GetLocationsPositionsOnTrack(...) for multiple points, it is
+// faster.
+func (g *GPX) GetLocationPositionsOnTrack(samples int, location Location) []float64 {
+ return g.GetLocationsPositionsOnTrack(samples, location)[0]
+}
+
+// distancesFromStart must have the same tracks, segments and pointsNo as this track.
+// if any distance in distancesFromStart is less than zero that point is ignored.
+func (g *GPX) getPositionsOnTrackWithPrecomputedDistances(location Location, distancesFromStart [][][]float64, length2d float64) []float64 {
+ if len(g.Tracks) == 0 {
+ return []float64{}
+ }
+
+ // The point must be closer than this value in order to be a candidate location:
+ minDistance := 0.01 * length2d
+ pointLocations := make([]float64, 0)
+
+ // True when we enter under the minDistance length
+ nearerThanMinDistance := false
+
+ var currentCandidate *GPXPoint
+ var currentCandidateFromStart float64
+ currentCandidateDistance := minDistance
+
+ var fromStart float64
+ for trackNo, track := range g.Tracks {
+ for segmentNo, segment := range track.Segments {
+ for pointNo, point := range segment.Points {
+ fromStart = distancesFromStart[trackNo][segmentNo][pointNo]
+ if fromStart >= 0 {
+ distance := point.Distance2D(location)
+ nearerThanMinDistance = distance < minDistance
+ if nearerThanMinDistance {
+ if distance < currentCandidateDistance {
+ currentCandidate = &point
+ currentCandidateDistance = distance
+ currentCandidateFromStart = fromStart
+ }
+ } else {
+ if currentCandidate != nil {
+ pointLocations = append(pointLocations, currentCandidateFromStart)
+ }
+ currentCandidate = nil
+ currentCandidateDistance = minDistance
+ }
+ }
+ }
+ }
+ }
+
+ if currentCandidate != nil {
+ pointLocations = append(pointLocations, currentCandidateFromStart)
+ }
+
+ return pointLocations
+}
+
+func (g *GPX) ExecuteOnAllPoints(executor func(*GPXPoint)) {
+ g.ExecuteOnWaypoints(executor)
+ g.ExecuteOnRoutePoints(executor)
+ g.ExecuteOnTrackPoints(executor)
+}
+
+func (g *GPX) ExecuteOnWaypoints(executor func(*GPXPoint)) {
+ for waypointNo := range g.Waypoints {
+ executor(&g.Waypoints[waypointNo])
+ }
+}
+
+func (g *GPX) ExecuteOnRoutePoints(executor func(*GPXPoint)) {
+ for _, route := range g.Routes {
+ route.ExecuteOnPoints(executor)
+ }
+}
+
+func (g *GPX) ExecuteOnTrackPoints(executor func(*GPXPoint)) {
+ for _, track := range g.Tracks {
+ track.ExecuteOnPoints(executor)
+ }
+}
+
+func (g *GPX) AddElevation(elevation float64) {
+ g.ExecuteOnAllPoints(func(point *GPXPoint) {
+ fmt.Println("setting elevation if NotNull for:", point.Elevation)
+ if point.Elevation.NotNull() {
+ fmt.Println("setting elevation")
+ point.Elevation.SetValue(point.Elevation.Value() + elevation)
+ }
+ })
+}
+
+func (g *GPX) RemoveElevation() {
+ g.ExecuteOnAllPoints(func(point *GPXPoint) {
+ point.Elevation.SetNull()
+ })
+}
+
+func (g *GPX) ReduceGpxToSingleTrack() {
+ if len(g.Tracks) <= 1 {
+ return
+ }
+
+ firstTrack := &g.Tracks[0]
+ for _, track := range g.Tracks[1:] {
+ for _, segment := range track.Segments {
+ firstTrack.AppendSegment(&segment)
+ }
+ }
+
+ g.Tracks = []GPXTrack{*firstTrack}
+}
+
+// Removes all a) segments without points and b) tracks without segments
+func (g *GPX) RemoveEmpty() {
+ if len(g.Tracks) == 0 {
+ return
+ }
+
+ for trackNo, track := range g.Tracks {
+ nonEmptySegments := make([]GPXTrackSegment, 0)
+ for _, segment := range track.Segments {
+ if len(segment.Points) > 0 {
+ //fmt.Printf("Valid segment, because of %d points!\n", len(segment.Points))
+ nonEmptySegments = append(nonEmptySegments, segment)
+ }
+ }
+ g.Tracks[trackNo].Segments = nonEmptySegments
+ }
+
+ nonEmptyTracks := make([]GPXTrack, 0)
+ for _, track := range g.Tracks {
+ if len(track.Segments) > 0 {
+ //fmt.Printf("Valid track, baceuse of %d segments!\n", len(track.Segments))
+ nonEmptyTracks = append(nonEmptyTracks, track)
+ }
+ }
+ g.Tracks = nonEmptyTracks
+}
+
+func (g *GPX) SmoothHorizontal() {
+ for trackNo := range g.Tracks {
+ g.Tracks[trackNo].SmoothHorizontal()
+ }
+}
+
+func (g *GPX) SmoothVertical() {
+ for trackNo := range g.Tracks {
+ g.Tracks[trackNo].SmoothVertical()
+ }
+}
+
+func (g *GPX) RemoveHorizontalExtremes() {
+ for trackNo := range g.Tracks {
+ g.Tracks[trackNo].RemoveHorizontalExtremes()
+ }
+}
+
+func (g *GPX) RemoveVerticalExtremes() {
+ for trackNo := range g.Tracks {
+ g.Tracks[trackNo].RemoveVerticalExtremes()
+ }
+}
+
+func (g *GPX) AddMissingTime() {
+ for trackNo := range g.Tracks {
+ g.Tracks[trackNo].AddMissingTime()
+ }
+}
+
+func (g *GPX) AppendTrack(t *GPXTrack) {
+ g.Tracks = append(g.Tracks, *t)
+}
+
+// Append segment on end of track, of not track exists an empty one will be added.
+func (g *GPX) AppendSegment(s *GPXTrackSegment) {
+ if len(g.Tracks) == 0 {
+ g.AppendTrack(new(GPXTrack))
+ }
+ g.Tracks[len(g.Tracks)-1].AppendSegment(s)
+}
+
+// Append segment on end of track, of not tracks/segments exists an empty one will be added.
+func (g *GPX) AppendPoint(p *GPXPoint) {
+ if len(g.Tracks) == 0 {
+ g.AppendTrack(new(GPXTrack))
+ }
+
+ lastTrack := &g.Tracks[len(g.Tracks)-1]
+ if len(lastTrack.Segments) == 0 {
+ lastTrack.AppendSegment(new(GPXTrackSegment))
+ }
+
+ lastSegment := &lastTrack.Segments[len(lastTrack.Segments)-1]
+
+ lastSegment.AppendPoint(p)
+}
+
+func (g *GPX) AppendRoute(r *GPXRoute) {
+ g.Routes = append(g.Routes, *r)
+}
+
+func (g *GPX) AppendWaypoint(w *GPXPoint) {
+ g.Waypoints = append(g.Waypoints, *w)
+}
+
+// ----------------------------------------------------------------------------------------------------
+
+type ElevationBounds struct {
+ MinElevation float64
+ MaxElevation float64
+}
+
+// Equals returns true if two Bounds objects are equal
+func (b ElevationBounds) Equals(b2 ElevationBounds) bool {
+ return b.MinElevation == b2.MinElevation && b.MaxElevation == b2.MaxElevation
+}
+
+func (b *ElevationBounds) String() string {
+ return fmt.Sprintf("Max: %+v Min: %+v", b.MinElevation, b.MaxElevation)
+}
+
+// ----------------------------------------------------------------------------------------------------
+
+type GpxBounds struct {
+ MinLatitude float64
+ MaxLatitude float64
+ MinLongitude float64
+ MaxLongitude float64
+}
+
+// Equals returns true if two Bounds objects are equal
+func (b GpxBounds) Equals(b2 GpxBounds) bool {
+ return b.MinLatitude == b2.MinLatitude && b.MaxLatitude == b2.MaxLatitude && b.MinLongitude == b2.MinLongitude && b.MaxLongitude == b2.MaxLongitude
+}
+
+func (b *GpxBounds) String() string {
+ return fmt.Sprintf("Max: %+v, %+v Min: %+v, %+v", b.MinLatitude, b.MinLongitude, b.MaxLatitude, b.MaxLongitude)
+}
+
+// ----------------------------------------------------------------------------------------------------
+
+// Generic point data
+type Point struct {
+ Latitude float64
+ Longitude float64
+ Elevation NullableFloat64
+}
+
+func (pt *Point) GetLatitude() float64 {
+ return pt.Latitude
+}
+
+func (pt *Point) GetLongitude() float64 {
+ return pt.Longitude
+}
+
+func (pt *Point) GetElevation() NullableFloat64 {
+ return pt.Elevation
+}
+
+// Distance2D returns the 2D distance of two GpxWpts.
+func (pt *Point) Distance2D(pt2 Location) float64 {
+ return Distance2D(pt.GetLatitude(), pt.GetLongitude(), pt2.GetLatitude(), pt2.GetLongitude(), false)
+}
+
+// Distance3D returns the 3D distance of two GpxWpts.
+func (pt *Point) Distance3D(pt2 Location) float64 {
+ return Distance3D(pt.GetLatitude(), pt.GetLongitude(), pt.GetElevation(), pt2.GetLatitude(), pt2.GetLongitude(), pt2.GetElevation(), false)
+}
+
+// ----------------------------------------------------------------------------------------------------
+
+type TimeBounds struct {
+ StartTime time.Time
+ EndTime time.Time
+}
+
+func (tb TimeBounds) Equals(tb2 TimeBounds) bool {
+ if tb.StartTime == tb2.StartTime && tb.EndTime == tb2.EndTime {
+ return true
+ }
+ return false
+}
+
+func (tb *TimeBounds) String() string {
+ return fmt.Sprintf("%+v, %+v", tb.StartTime, tb.EndTime)
+}
+
+// ----------------------------------------------------------------------------------------------------
+
+type UphillDownhill struct {
+ Uphill float64
+ Downhill float64
+}
+
+func (ud UphillDownhill) Equals(ud2 UphillDownhill) bool {
+ if ud.Uphill == ud2.Uphill && ud.Downhill == ud2.Downhill {
+ return true
+ }
+ return false
+}
+
+// ----------------------------------------------------------------------------------------------------
+
+// Position of a point on track
+type TrackPosition struct {
+ Point
+ TrackNo int
+ SegmentNo int
+ PointNo int
+}
+
+// ----------------------------------------------------------------------------------------------------
+
+type GPXPoint struct {
+ Point
+ // TODO
+ Timestamp time.Time
+ // TODO: Type
+ MagneticVariation string
+ // TODO: Type
+ GeoidHeight string
+ // Description info
+ Name string
+ Comment string
+ Description string
+ Source string
+ // TODO
+ // Links []GpxLink
+ Symbol string
+ Type string
+ // Accuracy info
+ TypeOfGpsFix string
+ Satellites NullableInt
+ HorizontalDilution NullableFloat64
+ VerticalDilution NullableFloat64
+ PositionalDilution NullableFloat64
+ AgeOfDGpsData NullableFloat64
+ DGpsId NullableInt
+}
+
+// SpeedBetween calculates the speed between two GpxWpts.
+func (pt *GPXPoint) SpeedBetween(pt2 *GPXPoint, threeD bool) float64 {
+ seconds := pt.TimeDiff(pt2)
+ var distLen float64
+ if threeD {
+ distLen = pt.Distance3D(pt2)
+ } else {
+ distLen = pt.Distance2D(pt2)
+ }
+
+ return distLen / seconds
+}
+
+// TimeDiff returns the time difference of two GpxWpts in seconds.
+func (pt *GPXPoint) TimeDiff(pt2 *GPXPoint) float64 {
+ t1 := pt.Timestamp
+ t2 := pt2.Timestamp
+
+ if t1.Equal(t2) {
+ return 0.0
+ }
+
+ var delta time.Duration
+ if t1.After(t2) {
+ delta = t1.Sub(t2)
+ } else {
+ delta = t2.Sub(t1)
+ }
+
+ return delta.Seconds()
+}
+
+// MaxDilutionOfPrecision returns the dilution precision of a GpxWpt.
+func (pt *GPXPoint) MaxDilutionOfPrecision() float64 {
+ return math.Max(pt.HorizontalDilution.Value(), math.Max(pt.VerticalDilution.Value(), pt.PositionalDilution.Value()))
+}
+
+// ----------------------------------------------------------------------------------------------------
+
+type GPXRoute struct {
+ Name string
+ Comment string
+ Description string
+ Source string
+ // TODO
+ //Links []Link
+ Number NullableInt
+ Type string
+ // TODO
+ Points []GPXPoint
+}
+
+// Length returns the length of a GPX route.
+func (rte *GPXRoute) Length() float64 {
+ // TODO: npe check
+ points := make([]Point, len(rte.Points))
+ for pointNo, point := range rte.Points {
+ points[pointNo] = point.Point
+ }
+ return Length2D(points)
+}
+
+// Center returns the center of a GPX route.
+func (rte *GPXRoute) Center() (float64, float64) {
+ lenRtePts := len(rte.Points)
+ if lenRtePts == 0 {
+ return 0.0, 0.0
+ }
+
+ var (
+ sumLat float64
+ sumLon float64
+ )
+
+ for _, pt := range rte.Points {
+ sumLat += pt.Latitude
+ sumLon += pt.Longitude
+ }
+
+ n := float64(lenRtePts)
+ return sumLat / n, sumLon / n
+}
+
+func (rte *GPXRoute) ExecuteOnPoints(executor func(*GPXPoint)) {
+ for pointNo := range rte.Points {
+ executor(&rte.Points[pointNo])
+ }
+}
+
+// ----------------------------------------------------------------------------------------------------
+
+type GPXTrackSegment struct {
+ Points []GPXPoint
+ // TODO extensions
+}
+
+// Length2D returns the 2D length of a GPX segment.
+func (seg *GPXTrackSegment) Length2D() float64 {
+ // TODO: There should be a better way to do this:
+ points := make([]Point, len(seg.Points))
+ for pointNo, point := range seg.Points {
+ points[pointNo] = point.Point
+ }
+ return Length2D(points)
+}
+
+// Length3D returns the 3D length of a GPX segment.
+func (seg *GPXTrackSegment) Length3D() float64 {
+ // TODO: There should be a better way to do this:
+ points := make([]Point, len(seg.Points))
+ for pointNo, point := range seg.Points {
+ points[pointNo] = point.Point
+ }
+ return Length3D(points)
+}
+
+func (seg *GPXTrackSegment) GetTrackPointsNo() int {
+ return len(seg.Points)
+}
+
+// TimeBounds returns the time bounds of a GPX segment.
+func (seg *GPXTrackSegment) TimeBounds() TimeBounds {
+ timeTuple := make([]time.Time, 0)
+
+ for _, trkpt := range seg.Points {
+ if len(timeTuple) < 2 {
+ timeTuple = append(timeTuple, trkpt.Timestamp)
+ } else {
+ timeTuple[1] = trkpt.Timestamp
+ }
+ }
+
+ if len(timeTuple) == 2 {
+ return TimeBounds{StartTime: timeTuple[0], EndTime: timeTuple[1]}
+ }
+
+ return TimeBounds{StartTime: time.Time{}, EndTime: time.Time{}}
+}
+
+// Bounds returns the bounds of a GPX segment.
+func (seg *GPXTrackSegment) Bounds() GpxBounds {
+ minmax := getMaximalGpxBounds()
+ for _, pt := range seg.Points {
+ minmax.MaxLatitude = math.Max(pt.Latitude, minmax.MaxLatitude)
+ minmax.MinLatitude = math.Min(pt.Latitude, minmax.MinLatitude)
+ minmax.MaxLongitude = math.Max(pt.Longitude, minmax.MaxLongitude)
+ minmax.MinLongitude = math.Min(pt.Longitude, minmax.MinLongitude)
+ }
+ return minmax
+}
+
+func (seg *GPXTrackSegment) ElevationBounds() ElevationBounds {
+ minmax := getMaximalElevationBounds()
+ for _, pt := range seg.Points {
+ if pt.Elevation.NotNull() {
+ minmax.MaxElevation = math.Max(pt.Elevation.Value(), minmax.MaxElevation)
+ minmax.MinElevation = math.Min(pt.Elevation.Value(), minmax.MinElevation)
+ }
+ }
+ return minmax
+}
+
+func (seg *GPXTrackSegment) HasTimes() bool {
+ return false
+ /*
+ withTimes := 0
+ for _, point := range seg.Points {
+ if point.Timestamp != nil {
+ withTimes += 1
+ }
+ }
+ return withTimes / len(seg.Points) >= 0.75
+ */
+}
+
+// Speed returns the speed at point number in a GPX segment.
+func (seg *GPXTrackSegment) Speed(pointIdx int) float64 {
+ trkptsLen := len(seg.Points)
+ if pointIdx >= trkptsLen {
+ pointIdx = trkptsLen - 1
+ }
+
+ point := seg.Points[pointIdx]
+
+ var prevPt *GPXPoint
+ var nextPt *GPXPoint
+
+ havePrev := false
+ haveNext := false
+ if 0 < pointIdx && pointIdx < trkptsLen {
+ prevPt = &seg.Points[pointIdx-1]
+ havePrev = true
+ }
+
+ if 0 < pointIdx && pointIdx < trkptsLen-1 {
+ nextPt = &seg.Points[pointIdx+1]
+ haveNext = true
+ }
+
+ haveSpeed1 := false
+ haveSpeed2 := false
+
+ var speed1 float64
+ var speed2 float64
+ if havePrev {
+ speed1 = math.Abs(point.SpeedBetween(prevPt, true))
+ haveSpeed1 = true
+ }
+ if haveNext {
+ speed2 = math.Abs(point.SpeedBetween(nextPt, true))
+ haveSpeed2 = true
+ }
+
+ if haveSpeed1 && haveSpeed2 {
+ return (speed1 + speed2) / 2.0
+ }
+
+ if haveSpeed1 {
+ return speed1
+ }
+ return speed2
+}
+
+// Duration returns the duration in seconds in a GPX segment.
+func (seg *GPXTrackSegment) Duration() float64 {
+ trksLen := len(seg.Points)
+ if trksLen == 0 {
+ return 0.0
+ }
+
+ first := seg.Points[0]
+ last := seg.Points[trksLen-1]
+
+ firstTimestamp := first.Timestamp
+ lastTimestamp := last.Timestamp
+
+ if firstTimestamp.Equal(lastTimestamp) {
+ return 0.0
+ }
+
+ if lastTimestamp.Before(firstTimestamp) {
+ return 0.0
+ }
+ dur := lastTimestamp.Sub(firstTimestamp)
+
+ return dur.Seconds()
+}
+
+// Elevations returns a slice with the elevations in a GPX segment.
+func (seg *GPXTrackSegment) Elevations() []NullableFloat64 {
+ elevations := make([]NullableFloat64, len(seg.Points))
+ for i, trkpt := range seg.Points {
+ elevations[i] = trkpt.Elevation
+ }
+ return elevations
+}
+
+// UphillDownhill returns uphill and dowhill in a GPX segment.
+func (seg *GPXTrackSegment) UphillDownhill() UphillDownhill {
+ if len(seg.Points) == 0 {
+ return UphillDownhill{Uphill: 0.0, Downhill: 0.0}
+ }
+
+ elevations := seg.Elevations()
+
+ uphill, downhill := CalcUphillDownhill(elevations)
+
+ return UphillDownhill{Uphill: uphill, Downhill: downhill}
+}
+
+func (seg *GPXTrackSegment) ExecuteOnPoints(executor func(*GPXPoint)) {
+ for pointNo := range seg.Points {
+ executor(&seg.Points[pointNo])
+ }
+}
+
+func (seg *GPXTrackSegment) ReduceTrackPoints(minDistance float64) {
+ if minDistance <= 0 {
+ return
+ }
+
+ if len(seg.Points) <= 1 {
+ return
+ }
+
+ newPoints := make([]GPXPoint, 0)
+ newPoints = append(newPoints, seg.Points[0])
+
+ for _, point := range seg.Points {
+ previousPoint := newPoints[len(newPoints)-1]
+ if point.Distance3D(&previousPoint) >= minDistance {
+ newPoints = append(newPoints, point)
+ }
+ }
+
+ seg.Points = newPoints
+}
+
+// Does Ramer-Douglas-Peucker algorithm for simplification of polyline
+func (seg *GPXTrackSegment) SimplifyTracks(maxDistance float64) {
+ seg.Points = simplifyPoints(seg.Points, maxDistance)
+}
+
+func (seg *GPXTrackSegment) AddElevation(elevation float64) {
+ for _, point := range seg.Points {
+ if point.Elevation.NotNull() {
+ point.Elevation.SetValue(point.Elevation.Value() + elevation)
+ }
+ }
+}
+
+// Split splits a GPX segment at point index pt. Point pt remains in
+// first part.
+func (seg *GPXTrackSegment) Split(pt int) (*GPXTrackSegment, *GPXTrackSegment) {
+ pts1 := seg.Points[:pt+1]
+ pts2 := seg.Points[pt+1:]
+
+ return &GPXTrackSegment{Points: pts1}, &GPXTrackSegment{Points: pts2}
+}
+
+// Join concatenates to GPX segments.
+func (seg *GPXTrackSegment) Join(seg2 *GPXTrackSegment) {
+ seg.Points = append(seg.Points, seg2.Points...)
+}
+
+// PositionAt returns the GpxWpt at a given time.
+func (seg *GPXTrackSegment) PositionAt(t time.Time) int {
+ lenPts := len(seg.Points)
+ if lenPts == 0 {
+ return -1
+ }
+ first := seg.Points[0]
+ last := seg.Points[lenPts-1]
+
+ firstTimestamp := first.Timestamp
+ lastTimestamp := last.Timestamp
+
+ if firstTimestamp.Equal(lastTimestamp) || firstTimestamp.After(lastTimestamp) {
+ return -1
+ }
+
+ for i := 0; i < len(seg.Points); i++ {
+ pt := seg.Points[i]
+ if t.Before(pt.Timestamp) {
+ return i
+ }
+ }
+
+ return -1
+}
+
+func (seg *GPXTrackSegment) StoppedPositions() []TrackPosition {
+ result := make([]TrackPosition, 0)
+ for pointNo, point := range seg.Points {
+ if pointNo > 0 {
+ previousPoint := seg.Points[pointNo-1]
+ if point.SpeedBetween(&previousPoint, true) < DEFAULT_STOPPED_SPEED_THRESHOLD {
+ var trackPos TrackPosition
+ trackPos.Point = point.Point
+ trackPos.PointNo = pointNo
+ trackPos.SegmentNo = -1
+ trackPos.TrackNo = -1
+ result = append(result, trackPos)
+ }
+ }
+ }
+ return result
+}
+
+// MovingData returns the moving data of a GPX segment.
+func (seg *GPXTrackSegment) MovingData() MovingData {
+ var (
+ movingTime float64
+ stoppedTime float64
+ movingDistance float64
+ stoppedDistance float64
+ )
+
+ speedsDistances := make([]SpeedsAndDistances, 0)
+
+ for i := 1; i < len(seg.Points); i++ {
+ prev := seg.Points[i-1]
+ pt := seg.Points[i]
+
+ dist := pt.Distance3D(&prev)
+
+ timedelta := pt.Timestamp.Sub(prev.Timestamp)
+ seconds := timedelta.Seconds()
+ var speedKmh float64
+
+ if seconds > 0 {
+ speedKmh = (dist / 1000.0) / (timedelta.Seconds() / math.Pow(60, 2))
+ }
+
+ if speedKmh <= DEFAULT_STOPPED_SPEED_THRESHOLD {
+ stoppedTime += timedelta.Seconds()
+ stoppedDistance += dist
+ } else {
+ movingTime += timedelta.Seconds()
+ movingDistance += dist
+
+ sd := SpeedsAndDistances{dist / timedelta.Seconds(), dist}
+ speedsDistances = append(speedsDistances, sd)
+ }
+ }
+
+ var maxSpeed float64
+ if len(speedsDistances) > 0 {
+ maxSpeed = CalcMaxSpeed(speedsDistances)
+ if math.IsNaN(maxSpeed) {
+ maxSpeed = 0
+ }
+ }
+
+ return MovingData{
+ movingTime,
+ stoppedTime,
+ movingDistance,
+ stoppedDistance,
+ maxSpeed,
+ }
+}
+
+func (seg *GPXTrackSegment) AppendPoint(p *GPXPoint) {
+ seg.Points = append(seg.Points, *p)
+}
+
+func (seg *GPXTrackSegment) SmoothVertical() {
+ seg.Points = smoothVertical(seg.Points)
+}
+
+func (seg *GPXTrackSegment) SmoothHorizontal() {
+ seg.Points = smoothHorizontal(seg.Points)
+}
+
+func (seg *GPXTrackSegment) RemoveVerticalExtremes() {
+ if len(seg.Points) < REMOVE_EXTREEMES_TRESHOLD {
+ return
+ }
+
+ elevationDeltaSum := 0.0
+ elevationDeltaNo := 0
+ for pointNo, point := range seg.Points {
+ if pointNo > 0 && point.Elevation.NotNull() && seg.Points[pointNo-1].Elevation.NotNull() {
+ elevationDeltaSum += math.Abs(point.Elevation.Value() - seg.Points[pointNo-1].Elevation.Value())
+ elevationDeltaNo += 1
+ }
+ }
+ avgElevationDelta := elevationDeltaSum / float64(elevationDeltaNo)
+ removeElevationExtremesThreshold := avgElevationDelta * 5.0
+
+ smoothedPoints := smoothVertical(seg.Points)
+ originalPoints := seg.Points
+
+ newPoints := make([]GPXPoint, 0)
+ for pointNo, point := range originalPoints {
+ smoothedPoint := smoothedPoints[pointNo]
+ if 0 < pointNo && pointNo < len(originalPoints)-1 && point.Elevation.NotNull() && smoothedPoints[pointNo].Elevation.NotNull() {
+ d := originalPoints[pointNo-1].Distance3D(&originalPoints[pointNo+1])
+ d1 := originalPoints[pointNo].Distance3D(&originalPoints[pointNo-1])
+ d2 := originalPoints[pointNo].Distance3D(&originalPoints[pointNo+1])
+ if d1+d2 > d*1.5 {
+ if math.Abs(point.Elevation.Value()-smoothedPoint.Elevation.Value()) < removeElevationExtremesThreshold {
+ newPoints = append(newPoints, point)
+ }
+ } else {
+ newPoints = append(newPoints, point)
+ }
+ } else {
+ newPoints = append(newPoints, point)
+ }
+ }
+ seg.Points = newPoints
+}
+
+func (seg *GPXTrackSegment) RemoveHorizontalExtremes() {
+ // Dont't remove extreemes if segment too small
+ if len(seg.Points) < REMOVE_EXTREEMES_TRESHOLD {
+ return
+ }
+
+ var sum float64
+ for pointNo, point := range seg.Points {
+ if pointNo > 0 {
+ sum += point.Distance2D(&seg.Points[pointNo-1])
+ }
+ }
+ // Division by zero not a problems since this is not computed on zero-length segments:
+ avgDistanceBetweenPoints := float64(sum) / float64(len(seg.Points)-1)
+
+ remove2dExtremesThreshold := 1.75 * avgDistanceBetweenPoints
+
+ smoothedPoints := smoothHorizontal(seg.Points)
+ originalPoints := seg.Points
+
+ newPoints := make([]GPXPoint, 0)
+ for pointNo, point := range originalPoints {
+ if 0 < pointNo && pointNo < len(originalPoints)-1 {
+ d := originalPoints[pointNo-1].Distance2D(&originalPoints[pointNo+1])
+ d1 := originalPoints[pointNo].Distance2D(&originalPoints[pointNo-1])
+ d2 := originalPoints[pointNo].Distance2D(&originalPoints[pointNo+1])
+ if d1+d2 > d*1.5 {
+ pointMovedBy := smoothedPoints[pointNo].Distance2D(&point)
+ if pointMovedBy < remove2dExtremesThreshold {
+ newPoints = append(newPoints, point)
+ } else {
+ // Removed!
+ }
+ } else {
+ newPoints = append(newPoints, point)
+ }
+ } else {
+ newPoints = append(newPoints, point)
+ }
+ }
+ seg.Points = newPoints
+}
+
+func (seg *GPXTrackSegment) AddMissingTime() {
+ emptySegmentStart := -1
+ for pointNo := range seg.Points {
+ timestampEmpty := seg.Points[pointNo].Timestamp.Year() <= 1
+ if timestampEmpty {
+ if emptySegmentStart == -1 {
+ emptySegmentStart = pointNo
+ }
+ } else {
+ if 0 < emptySegmentStart && pointNo < len(seg.Points) {
+ seg.addMissingTimeInSegment(emptySegmentStart, pointNo-1)
+ }
+ emptySegmentStart = -1
+ }
+ }
+}
+
+func (seg *GPXTrackSegment) addMissingTimeInSegment(start, end int) {
+ if start <= 0 {
+ return
+ }
+ if end >= len(seg.Points)-1 {
+ return
+ }
+ startTime, endTime := seg.Points[start-1].Timestamp, seg.Points[end+1].Timestamp
+ ratios := make([]float64, end-start+1)
+
+ length := 0.0
+ for i := start; i <= end; i++ {
+ length += seg.Points[i].Point.Distance2D(&seg.Points[i-1])
+ ratios[i-start] = length
+ }
+ length += seg.Points[end].Point.Distance2D(&seg.Points[end+1])
+ for i := start; i <= end; i++ {
+ ratios[i-start] = ratios[i-start] / length
+ }
+
+ for i := start; i <= end; i++ {
+ d := int64(ratios[i-start] * float64(endTime.Sub(startTime).Nanoseconds()))
+ seg.Points[i].Timestamp = startTime.Add(time.Duration(d))
+ }
+}
+
+// ----------------------------------------------------------------------------------------------------
+
+type GPXTrack struct {
+ Name string
+ Comment string
+ Description string
+ Source string
+ // TODO
+ //Links []Link
+ Number NullableInt
+ Type string
+ Segments []GPXTrackSegment
+}
+
+// Length2D returns the 2D length of a GPX track.
+func (trk *GPXTrack) Length2D() float64 {
+ var l float64
+ for _, seg := range trk.Segments {
+ d := seg.Length2D()
+ l += d
+ }
+ return l
+}
+
+// Length3D returns the 3D length of a GPX track.
+func (trk *GPXTrack) Length3D() float64 {
+ var l float64
+ for _, seg := range trk.Segments {
+ d := seg.Length3D()
+ l += d
+ }
+ return l
+}
+
+func (trk *GPXTrack) GetTrackPointsNo() int {
+ result := 0
+ for _, segment := range trk.Segments {
+ result += segment.GetTrackPointsNo()
+ }
+ return result
+}
+
+// TimeBounds returns the time bounds of a GPX track.
+func (trk *GPXTrack) TimeBounds() TimeBounds {
+ var tbTrk TimeBounds
+
+ for i, seg := range trk.Segments {
+ tbSeg := seg.TimeBounds()
+ if i == 0 {
+ tbTrk = tbSeg
+ } else {
+ tbTrk.EndTime = tbSeg.EndTime
+ }
+ }
+ return tbTrk
+}
+
+// Bounds returns the bounds of a GPX track.
+func (trk *GPXTrack) Bounds() GpxBounds {
+ minmax := getMaximalGpxBounds()
+ for _, seg := range trk.Segments {
+ bnds := seg.Bounds()
+ minmax.MaxLatitude = math.Max(bnds.MaxLatitude, minmax.MaxLatitude)
+ minmax.MinLatitude = math.Min(bnds.MinLatitude, minmax.MinLatitude)
+ minmax.MaxLongitude = math.Max(bnds.MaxLongitude, minmax.MaxLongitude)
+ minmax.MinLongitude = math.Min(bnds.MinLongitude, minmax.MinLongitude)
+ }
+ return minmax
+}
+
+func (trk *GPXTrack) ElevationBounds() ElevationBounds {
+ minmax := getMaximalElevationBounds()
+ for _, seg := range trk.Segments {
+ bnds := seg.ElevationBounds()
+ minmax.MaxElevation = math.Max(bnds.MaxElevation, minmax.MaxElevation)
+ minmax.MinElevation = math.Min(bnds.MinElevation, minmax.MinElevation)
+ }
+ return minmax
+}
+
+func (trk *GPXTrack) HasTimes() bool {
+ result := true
+ for _, segment := range trk.Segments {
+ result = result && segment.HasTimes()
+ }
+ return result
+}
+
+func (trk *GPXTrack) ReduceTrackPoints(minDistance float64) {
+ for segmentNo := range trk.Segments {
+ trk.Segments[segmentNo].ReduceTrackPoints(minDistance)
+ }
+}
+
+func (trk *GPXTrack) SimplifyTracks(maxDistance float64) {
+ for segmentNo := range trk.Segments {
+ trk.Segments[segmentNo].SimplifyTracks(maxDistance)
+ }
+}
+
+// Split splits a GPX segment at a point number ptNo in a GPX track.
+func (trk *GPXTrack) Split(segNo, ptNo int) {
+ lenSegs := len(trk.Segments)
+ if segNo >= lenSegs {
+ return
+ }
+
+ newSegs := make([]GPXTrackSegment, 0)
+ for i := 0; i < lenSegs; i++ {
+ seg := trk.Segments[i]
+
+ if i == segNo && ptNo < len(seg.Points) {
+ seg1, seg2 := seg.Split(ptNo)
+ newSegs = append(newSegs, *seg1, *seg2)
+ } else {
+ newSegs = append(newSegs, seg)
+ }
+ }
+ trk.Segments = newSegs
+}
+
+func (trk *GPXTrack) ExecuteOnPoints(executor func(*GPXPoint)) {
+ for segmentNo := range trk.Segments {
+ trk.Segments[segmentNo].ExecuteOnPoints(executor)
+ }
+}
+
+func (trk *GPXTrack) AddElevation(elevation float64) {
+ for segmentNo := range trk.Segments {
+ trk.Segments[segmentNo].AddElevation(elevation)
+ }
+}
+
+// Join joins two GPX segments in a GPX track.
+func (trk *GPXTrack) Join(segNo, segNo2 int) {
+ lenSegs := len(trk.Segments)
+ if segNo >= lenSegs && segNo2 >= lenSegs {
+ return
+ }
+ newSegs := make([]GPXTrackSegment, 0)
+ for i := 0; i < lenSegs; i++ {
+ seg := trk.Segments[i]
+ if i == segNo {
+ secondSeg := trk.Segments[segNo2]
+ seg.Join(&secondSeg)
+ newSegs = append(newSegs, seg)
+ } else if i == segNo2 {
+ // do nothing, its already joined
+ } else {
+ newSegs = append(newSegs, seg)
+ }
+ }
+ trk.Segments = newSegs
+}
+
+// JoinNext joins a GPX segment with the next segment in the current GPX
+// track.
+func (trk *GPXTrack) JoinNext(segNo int) {
+ trk.Join(segNo, segNo+1)
+}
+
+// MovingData returns the moving data of a GPX track.
+func (trk *GPXTrack) MovingData() MovingData {
+ var (
+ movingTime float64
+ stoppedTime float64
+ movingDistance float64
+ stoppedDistance float64
+ maxSpeed float64
+ )
+
+ for _, seg := range trk.Segments {
+ md := seg.MovingData()
+ movingTime += md.MovingTime
+ stoppedTime += md.StoppedTime
+ movingDistance += md.MovingDistance
+ stoppedDistance += md.StoppedDistance
+
+ if md.MaxSpeed > maxSpeed {
+ maxSpeed = md.MaxSpeed
+ }
+ }
+ return MovingData{
+ MovingTime: movingTime,
+ MovingDistance: movingDistance,
+ StoppedTime: stoppedTime,
+ StoppedDistance: stoppedDistance,
+ MaxSpeed: maxSpeed,
+ }
+}
+
+// Duration returns the duration of a GPX track.
+func (trk *GPXTrack) Duration() float64 {
+ if len(trk.Segments) == 0 {
+ return 0.0
+ }
+
+ var result float64
+ for _, seg := range trk.Segments {
+ result += seg.Duration()
+ }
+ return result
+}
+
+// UphillDownhill return the uphill and downhill values of a GPX track.
+func (trk *GPXTrack) UphillDownhill() UphillDownhill {
+ if len(trk.Segments) == 0 {
+ return UphillDownhill{
+ Uphill: 0,
+ Downhill: 0,
+ }
+ }
+
+ var (
+ uphill float64
+ downhill float64
+ )
+
+ for _, seg := range trk.Segments {
+ updo := seg.UphillDownhill()
+
+ uphill += updo.Uphill
+ downhill += updo.Downhill
+ }
+
+ return UphillDownhill{
+ Uphill: uphill,
+ Downhill: downhill,
+ }
+}
+
+// PositionAt returns a LocationResultsPair for a given time.
+func (trk *GPXTrack) PositionAt(t time.Time) []TrackPosition {
+ results := make([]TrackPosition, 0)
+
+ for i := 0; i < len(trk.Segments); i++ {
+ seg := trk.Segments[i]
+ loc := seg.PositionAt(t)
+ if loc != -1 {
+ results = append(results, TrackPosition{SegmentNo: i, PointNo: loc, Point: seg.Points[loc].Point})
+ }
+ }
+ return results
+}
+
+func (trk *GPXTrack) StoppedPositions() []TrackPosition {
+ result := make([]TrackPosition, 0)
+ for segmentNo, segment := range trk.Segments {
+ positions := segment.StoppedPositions()
+ for _, position := range positions {
+ position.SegmentNo = segmentNo
+ result = append(result, position)
+ }
+ }
+ return result
+}
+
+func (trk *GPXTrack) AppendSegment(s *GPXTrackSegment) {
+ trk.Segments = append(trk.Segments, *s)
+}
+
+func (trk *GPXTrack) SmoothVertical() {
+ for segmentNo := range trk.Segments {
+ trk.Segments[segmentNo].SmoothVertical()
+ }
+}
+
+func (trk *GPXTrack) SmoothHorizontal() {
+ for segmentNo := range trk.Segments {
+ trk.Segments[segmentNo].SmoothHorizontal()
+ }
+}
+
+func (trk *GPXTrack) RemoveVerticalExtremes() {
+ for segmentNo := range trk.Segments {
+ trk.Segments[segmentNo].RemoveVerticalExtremes()
+ }
+}
+
+func (trk *GPXTrack) RemoveHorizontalExtremes() {
+ for segmentNo := range trk.Segments {
+ trk.Segments[segmentNo].RemoveHorizontalExtremes()
+ }
+}
+
+func (trk *GPXTrack) AddMissingTime() {
+ for segmentNo := range trk.Segments {
+ trk.Segments[segmentNo].AddMissingTime()
+ }
+}
+
+// ----------------------------------------------------------------------------------------------------
+
+/**
+ * Useful when looking for smaller bounds
+ *
+ * TODO does it work is region is between 179E and 179W?
+ */
+func getMaximalGpxBounds() GpxBounds {
+ return GpxBounds{
+ MaxLatitude: -math.MaxFloat64,
+ MinLatitude: math.MaxFloat64,
+ MaxLongitude: -math.MaxFloat64,
+ MinLongitude: math.MaxFloat64,
+ }
+}
+
+func getMaximalElevationBounds() ElevationBounds {
+ return ElevationBounds{
+ MaxElevation: -math.MaxFloat64,
+ MinElevation: math.MaxFloat64,
+ }
+}
diff --git a/vendor/github.com/tkrajina/gpxgo/gpx/gpx10.go b/vendor/github.com/tkrajina/gpxgo/gpx/gpx10.go
new file mode 100644
index 0000000..daf530b
--- /dev/null
+++ b/vendor/github.com/tkrajina/gpxgo/gpx/gpx10.go
@@ -0,0 +1,240 @@
+// Copyright 2013, 2014 Peter Vasil, Tomo Krajina. All
+// rights reserved. Use of this source code is governed
+// by a BSD-style license that can be found in the
+// LICENSE file.
+
+package gpx
+
+import (
+ "encoding/xml"
+)
+
+/*
+
+The GPX XML hierarchy:
+
+gpx
+ - attr: version (xsd:string) required
+ - attr: creator (xsd:string) required
+ name
+ desc
+ author
+ email
+ url
+ urlname
+ time
+ keywords
+ bounds
+ wpt
+ - attr: lat (gpx:latitudeType) required
+ - attr: lon (gpx:longitudeType) required
+ ele
+ time
+ magvar
+ geoidheight
+ name
+ cmt
+ desc
+ src
+ url
+ urlname
+ sym
+ type
+ fix
+ sat
+ hdop
+ vdop
+ pdop
+ ageofdgpsdata
+ dgpsid
+ rte
+ name
+ cmt
+ desc
+ src
+ url
+ urlname
+ number
+ rtept
+ - attr: lat (gpx:latitudeType) required
+ - attr: lon (gpx:longitudeType) required
+ ele
+ time
+ magvar
+ geoidheight
+ name
+ cmt
+ desc
+ src
+ url
+ urlname
+ sym
+ type
+ fix
+ sat
+ hdop
+ vdop
+ pdop
+ ageofdgpsdata
+ dgpsid
+ trk
+ name
+ cmt
+ desc
+ src
+ url
+ urlname
+ number
+ trkseg
+ trkpt
+ - attr: lat (gpx:latitudeType) required
+ - attr: lon (gpx:longitudeType) required
+ ele
+ time
+ course
+ speed
+ magvar
+ geoidheight
+ name
+ cmt
+ desc
+ src
+ url
+ urlname
+ sym
+ type
+ fix
+ sat
+ hdop
+ vdop
+ pdop
+ ageofdgpsdata
+ dgpsid
+*/
+
+type gpx10Gpx struct {
+ XMLName xml.Name `xml:"gpx"`
+ XMLNs string `xml:"xmlns,attr,omitempty"`
+ XmlNsXsi string `xml:"xmlns:xsi,attr,omitempty"`
+ XmlSchemaLoc string `xml:"xsi:schemaLocation,attr,omitempty"`
+
+ Version string `xml:"version,attr"`
+ Creator string `xml:"creator,attr"`
+ Name string `xml:"name,omitempty"`
+ Desc string `xml:"desc,omitempty"`
+ Author string `xml:"author,omitempty"`
+ Email string `xml:"email,omitempty"`
+ Url string `xml:"url,omitempty"`
+ UrlName string `xml:"urlname,omitempty"`
+ Time string `xml:"time,omitempty"`
+ Keywords string `xml:"keywords,omitempty"`
+ Bounds *GpxBounds `xml:"bounds"`
+ Waypoints []*gpx10GpxPoint `xml:"wpt"`
+ Routes []*gpx10GpxRte `xml:"rte"`
+ Tracks []*gpx10GpxTrk `xml:"trk"`
+}
+
+type gpx10GpxBounds struct {
+ //XMLName xml.Name `xml:"bounds"`
+ MinLat float64 `xml:"minlat,attr"`
+ MaxLat float64 `xml:"maxlat,attr"`
+ MinLon float64 `xml:"minlon,attr"`
+ MaxLon float64 `xml:"maxlon,attr"`
+}
+
+type gpx10GpxAuthor struct {
+ Name string `xml:"name,omitempty"`
+ Email string `xml:"email,omitempty"`
+ Link *gpx10GpxLink `xml:"link"`
+}
+
+type gpx10GpxEmail struct {
+ Id string `xml:"id,attr"`
+ Domain string `xml:"domain,attr"`
+}
+
+type gpx10GpxLink struct {
+ Href string `xml:"href,attr"`
+ Text string `xml:"text,omitempty"`
+ Type string `xml:"type,omitempty"`
+}
+
+type gpx10GpxMetadata struct {
+ XMLName xml.Name `xml:"metadata"`
+ Name string `xml:"name,omitempty"`
+ Desc string `xml:"desc,omitempty"`
+ Author *gpx10GpxAuthor `xml:"author,omitempty"`
+ // Links []GpxLink `xml:"link"`
+ Timestamp string `xml:"time,omitempty"`
+ Keywords string `xml:"keywords,omitempty"`
+ // Bounds *GpxBounds `xml:"bounds"`
+}
+
+type gpx10GpxExtensions struct {
+ Bytes []byte `xml:",innerxml"`
+}
+
+/**
+ * Common struct fields for all points
+ */
+type gpx10GpxPoint struct {
+ Lat float64 `xml:"lat,attr"`
+ Lon float64 `xml:"lon,attr"`
+ // Position info
+ Ele NullableFloat64 `xml:"ele,omitempty"`
+ Timestamp string `xml:"time,omitempty"`
+ MagVar string `xml:"magvar,omitempty"`
+ GeoIdHeight string `xml:"geoidheight,omitempty"`
+ // Description info
+ Name string `xml:"name,omitempty"`
+ Cmt string `xml:"cmt,omitempty"`
+ Desc string `xml:"desc,omitempty"`
+ Src string `xml:"src,omitempty"`
+ Links []gpx10GpxLink `xml:"link"`
+ Sym string `xml:"sym,omitempty"`
+ Type string `xml:"type,omitempty"`
+ // Accuracy info
+ Fix string `xml:"fix,omitempty"`
+ Sat *int `xml:"sat,omitempty"`
+ Hdop *float64 `xml:"hdop,omitempty"`
+ Vdop *float64 `xml:"vdop,omitempty"`
+ Pdop *float64 `xml:"pdop,omitempty"`
+ AgeOfDGpsData *float64 `xml:"ageofdgpsdata,omitempty"`
+ DGpsId *int `xml:"dgpsid,omitempty"`
+
+ // Those two values are here for simplicity, but they are available only when this is part of a track segment (not route or waypoint)!
+ Course string `xml:"course,omitempty"`
+ Speed string `speed:"speed,omitempty"`
+}
+
+type gpx10GpxRte struct {
+ XMLName xml.Name `xml:"rte"`
+ Name string `xml:"name,omitempty"`
+ Cmt string `xml:"cmt,omitempty"`
+ Desc string `xml:"desc,omitempty"`
+ Src string `xml:"src,omitempty"`
+ // TODO
+ //Links []Link `xml:"link"`
+ Number NullableInt `xml:"number,omitempty"`
+ Type string `xml:"type,omitempty"`
+ Points []*gpx10GpxPoint `xml:"rtept"`
+}
+
+type gpx10GpxTrkSeg struct {
+ XMLName xml.Name `xml:"trkseg"`
+ Points []*gpx10GpxPoint `xml:"trkpt"`
+}
+
+// Trk is a GPX track
+type gpx10GpxTrk struct {
+ XMLName xml.Name `xml:"trk"`
+ Name string `xml:"name,omitempty"`
+ Cmt string `xml:"cmt,omitempty"`
+ Desc string `xml:"desc,omitempty"`
+ Src string `xml:"src,omitempty"`
+ // TODO
+ //Links []Link `xml:"link"`
+ Number NullableInt `xml:"number,omitempty"`
+ Type string `xml:"type,omitempty"`
+ Segments []*gpx10GpxTrkSeg `xml:"trkseg,omitempty"`
+}
diff --git a/vendor/github.com/tkrajina/gpxgo/gpx/gpx11.go b/vendor/github.com/tkrajina/gpxgo/gpx/gpx11.go
new file mode 100644
index 0000000..8d3afdf
--- /dev/null
+++ b/vendor/github.com/tkrajina/gpxgo/gpx/gpx11.go
@@ -0,0 +1,283 @@
+// Copyright 2013, 2014 Peter Vasil, Tomo Krajina. All
+// rights reserved. Use of this source code is governed
+// by a BSD-style license that can be found in the
+// LICENSE file.
+
+package gpx
+
+import (
+ "encoding/xml"
+)
+
+/*
+
+The GPX XML hierarchy:
+
+gpx (gpxType)
+ - attr: version (xsd:string) None
+ - attr: creator (xsd:string) None
+ metadata (metadataType)
+ name (xsd:string)
+ desc (xsd:string)
+ author (personType)
+ name (xsd:string)
+ email (emailType)
+ - attr: id (xsd:string) None
+ - attr: domain (xsd:string) None
+ link (linkType)
+ - attr: href (xsd:anyURI) None
+ text (xsd:string)
+ type (xsd:string)
+ copyright (copyrightType)
+ - attr: author (xsd:string) None
+ year (xsd:gYear)
+ license (xsd:anyURI)
+ link (linkType)
+ - attr: href (xsd:anyURI) None
+ text (xsd:string)
+ type (xsd:string)
+ time (xsd:dateTime)
+ keywords (xsd:string)
+ bounds (boundsType)
+ - attr: minlat (latitudeType) None
+ - attr: minlon (longitudeType) None
+ - attr: maxlat (latitudeType) None
+ - attr: maxlon (longitudeType) None
+ extensions (extensionsType)
+ wpt (wptType)
+ - attr: lat (latitudeType) None
+ - attr: lon (longitudeType) None
+ ele (xsd:decimal)
+ time (xsd:dateTime)
+ magvar (degreesType)
+ geoidheight (xsd:decimal)
+ name (xsd:string)
+ cmt (xsd:string)
+ desc (xsd:string)
+ src (xsd:string)
+ link (linkType)
+ - attr: href (xsd:anyURI) None
+ text (xsd:string)
+ type (xsd:string)
+ sym (xsd:string)
+ type (xsd:string)
+ fix (fixType)
+ sat (xsd:nonNegativeInteger)
+ hdop (xsd:decimal)
+ vdop (xsd:decimal)
+ pdop (xsd:decimal)
+ ageofdgpsdata (xsd:decimal)
+ dgpsid (dgpsStationType)
+ extensions (extensionsType)
+ rte (rteType)
+ name (xsd:string)
+ cmt (xsd:string)
+ desc (xsd:string)
+ src (xsd:string)
+ link (linkType)
+ - attr: href (xsd:anyURI) None
+ text (xsd:string)
+ type (xsd:string)
+ number (xsd:nonNegativeInteger)
+ type (xsd:string)
+ extensions (extensionsType)
+ rtept (wptType)
+ - attr: lat (latitudeType) None
+ - attr: lon (longitudeType) None
+ ele (xsd:decimal)
+ time (xsd:dateTime)
+ magvar (degreesType)
+ geoidheight (xsd:decimal)
+ name (xsd:string)
+ cmt (xsd:string)
+ desc (xsd:string)
+ src (xsd:string)
+ link (linkType)
+ - attr: href (xsd:anyURI) None
+ text (xsd:string)
+ type (xsd:string)
+ sym (xsd:string)
+ type (xsd:string)
+ fix (fixType)
+ sat (xsd:nonNegativeInteger)
+ hdop (xsd:decimal)
+ vdop (xsd:decimal)
+ pdop (xsd:decimal)
+ ageofdgpsdata (xsd:decimal)
+ dgpsid (dgpsStationType)
+ extensions (extensionsType)
+ trk (trkType)
+ name (xsd:string)
+ cmt (xsd:string)
+ desc (xsd:string)
+ src (xsd:string)
+ link (linkType)
+ - attr: href (xsd:anyURI) None
+ text (xsd:string)
+ type (xsd:string)
+ number (xsd:nonNegativeInteger)
+ type (xsd:string)
+ extensions (extensionsType)
+ trkseg (trksegType)
+ trkpt (wptType)
+ - attr: lat (latitudeType) None
+ - attr: lon (longitudeType) None
+ ele (xsd:decimal)
+ time (xsd:dateTime)
+ magvar (degreesType)
+ geoidheight (xsd:decimal)
+ name (xsd:string)
+ cmt (xsd:string)
+ desc (xsd:string)
+ src (xsd:string)
+ link (linkType)
+ - attr: href (xsd:anyURI) None
+ text (xsd:string)
+ type (xsd:string)
+ sym (xsd:string)
+ type (xsd:string)
+ fix (fixType)
+ sat (xsd:nonNegativeInteger)
+ hdop (xsd:decimal)
+ vdop (xsd:decimal)
+ pdop (xsd:decimal)
+ ageofdgpsdata (xsd:decimal)
+ dgpsid (dgpsStationType)
+ extensions (extensionsType)
+ extensions (extensionsType)
+ extensions (extensionsType)
+*/
+
+type gpx11Gpx struct {
+ XMLName xml.Name `xml:"gpx"`
+ XMLNs string `xml:"xmlns,attr,omitempty"`
+ XmlNsXsi string `xml:"xmlns:xsi,attr,omitempty"`
+ XmlSchemaLoc string `xml:"xsi:schemaLocation,attr,omitempty"`
+
+ Version string `xml:"version,attr"`
+ Creator string `xml:"creator,attr"`
+ Name string `xml:"metadata>name,omitempty"`
+ Desc string `xml:"metadata>desc,omitempty"`
+ AuthorName string `xml:"metadata>author>name,omitempty"`
+ AuthorEmail *gpx11GpxEmail `xml:"metadata>author>email,omitempty"`
+ // TODO: There can be more than one link?
+ AuthorLink *gpx11GpxLink `xml:"metadata>author>link,omitempty"`
+ Copyright *gpx11GpxCopyright `xml:"metadata>copyright,omitempty"`
+ Link *gpx11GpxLink `xml:"metadata>link,omitempty"`
+ Timestamp string `xml:"metadata>time,omitempty"`
+ Keywords string `xml:"metadata>keywords,omitempty"`
+ Bounds *gpx11GpxBounds `xml:"bounds"`
+ Extensions *gpx11GpxExtensions `xml:"extensions"`
+ Waypoints []*gpx11GpxPoint `xml:"wpt"`
+ Routes []*gpx11GpxRte `xml:"rte"`
+ Tracks []*gpx11GpxTrk `xml:"trk"`
+}
+
+type gpx11GpxBounds struct {
+ //XMLName xml.Name `xml:"bounds"`
+ MinLat float64 `xml:"minlat,attr"`
+ MaxLat float64 `xml:"maxlat,attr"`
+ MinLon float64 `xml:"minlon,attr"`
+ MaxLon float64 `xml:"maxlon,attr"`
+}
+
+type gpx11GpxCopyright struct {
+ XMLName xml.Name `xml:"copyright"`
+ Author string `xml:"author,attr"`
+ Year string `xml:"year,omitempty"`
+ License string `xml:"license,omitempty"`
+}
+
+type gpx11GpxAuthor struct {
+ Name string `xml:"name,omitempty"`
+ Email string `xml:"email,omitempty"`
+ Link *gpx11GpxLink `xml:"link"`
+}
+
+type gpx11GpxEmail struct {
+ Id string `xml:"id,attr"`
+ Domain string `xml:"domain,attr"`
+}
+
+type gpx11GpxLink struct {
+ Href string `xml:"href,attr"`
+ Text string `xml:"text,omitempty"`
+ Type string `xml:"type,omitempty"`
+}
+
+type gpx11GpxMetadata struct {
+ XMLName xml.Name `xml:"metadata"`
+ Name string `xml:"name,omitempty"`
+ Desc string `xml:"desc,omitempty"`
+ Author *gpx11GpxAuthor `xml:"author,omitempty"`
+ // Copyright *GpxCopyright `xml:"copyright,omitempty"`
+ // Links []GpxLink `xml:"link"`
+ Timestamp string `xml:"time,omitempty"`
+ Keywords string `xml:"keywords,omitempty"`
+ // Bounds *GpxBounds `xml:"bounds"`
+}
+
+type gpx11GpxExtensions struct {
+ Bytes []byte `xml:",innerxml"`
+}
+
+/**
+ * Common struct fields for all points
+ */
+type gpx11GpxPoint struct {
+ Lat float64 `xml:"lat,attr"`
+ Lon float64 `xml:"lon,attr"`
+ // Position info
+ Ele NullableFloat64 `xml:"ele,omitempty"`
+ Timestamp string `xml:"time,omitempty"`
+ MagVar string `xml:"magvar,omitempty"`
+ GeoIdHeight string `xml:"geoidheight,omitempty"`
+ // Description info
+ Name string `xml:"name,omitempty"`
+ Cmt string `xml:"cmt,omitempty"`
+ Desc string `xml:"desc,omitempty"`
+ Src string `xml:"src,omitempty"`
+ Links []gpx11GpxLink `xml:"link"`
+ Sym string `xml:"sym,omitempty"`
+ Type string `xml:"type,omitempty"`
+ // Accuracy info
+ Fix string `xml:"fix,omitempty"`
+ Sat *int `xml:"sat,omitempty"`
+ Hdop *float64 `xml:"hdop,omitempty"`
+ Vdop *float64 `xml:"vdop,omitempty"`
+ Pdop *float64 `xml:"pdop,omitempty"`
+ AgeOfDGpsData *float64 `xml:"ageofdgpsdata,omitempty"`
+ DGpsId *int `xml:"dgpsid,omitempty"`
+}
+
+type gpx11GpxRte struct {
+ XMLName xml.Name `xml:"rte"`
+ Name string `xml:"name,omitempty"`
+ Cmt string `xml:"cmt,omitempty"`
+ Desc string `xml:"desc,omitempty"`
+ Src string `xml:"src,omitempty"`
+ // TODO
+ //Links []Link `xml:"link"`
+ Number NullableInt `xml:"number,omitempty"`
+ Type string `xml:"type,omitempty"`
+ Points []*gpx11GpxPoint `xml:"rtept"`
+}
+
+type gpx11GpxTrkSeg struct {
+ XMLName xml.Name `xml:"trkseg"`
+ Points []*gpx11GpxPoint `xml:"trkpt"`
+}
+
+// Trk is a GPX track
+type gpx11GpxTrk struct {
+ XMLName xml.Name `xml:"trk"`
+ Name string `xml:"name,omitempty"`
+ Cmt string `xml:"cmt,omitempty"`
+ Desc string `xml:"desc,omitempty"`
+ Src string `xml:"src,omitempty"`
+ // TODO
+ //Links []Link `xml:"link"`
+ Number NullableInt `xml:"number,omitempty"`
+ Type string `xml:"type,omitempty"`
+ Segments []*gpx11GpxTrkSeg `xml:"trkseg,omitempty"`
+}
diff --git a/vendor/github.com/tkrajina/gpxgo/gpx/nullable_float64.go b/vendor/github.com/tkrajina/gpxgo/gpx/nullable_float64.go
new file mode 100644
index 0000000..3df52ef
--- /dev/null
+++ b/vendor/github.com/tkrajina/gpxgo/gpx/nullable_float64.go
@@ -0,0 +1,100 @@
+// Copyright 2013, 2014 Peter Vasil, Tomo Krajina. All
+// rights reserved. Use of this source code is governed
+// by a BSD-style license that can be found in the
+// LICENSE file.
+
+package gpx
+
+import (
+ "encoding/xml"
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+type NullableFloat64 struct {
+ data float64
+ notNull bool
+}
+
+func (n *NullableFloat64) Null() bool {
+ return !n.notNull
+}
+
+func (n *NullableFloat64) NotNull() bool {
+ return n.notNull
+}
+
+func (n *NullableFloat64) Value() float64 {
+ return n.data
+}
+
+func (n *NullableFloat64) SetValue(data float64) {
+ n.data = data
+ n.notNull = true
+}
+
+func (n *NullableFloat64) SetNull() {
+ var defaultValue float64
+ n.data = defaultValue
+ n.notNull = false
+}
+
+func NewNullableFloat64(data float64) *NullableFloat64 {
+ result := new(NullableFloat64)
+ result.data = data
+ result.notNull = true
+ return result
+}
+
+func (n *NullableFloat64) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
+ t, err := d.Token()
+ if err != nil {
+ n.SetNull()
+ return nil
+ }
+ if charData, ok := t.(xml.CharData); ok {
+ strData := strings.Trim(string(charData), " ")
+ value, err := strconv.ParseFloat(strData, 64)
+ if err != nil {
+ n.SetNull()
+ return nil
+ }
+ n.SetValue(value)
+ }
+ d.Skip()
+ return nil
+}
+
+func (n *NullableFloat64) UnmarshalXMLAttr(attr xml.Attr) error {
+ strData := strings.Trim(string(attr.Value), " ")
+ value, err := strconv.ParseFloat(strData, 64)
+ if err != nil {
+ n.SetNull()
+ return nil
+ }
+ n.SetValue(value)
+ return nil
+}
+
+func (n NullableFloat64) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if n.Null() {
+ return nil
+ }
+ xmlName := xml.Name{Local: start.Name.Local}
+ e.EncodeToken(xml.StartElement{Name: xmlName})
+ e.EncodeToken(xml.CharData([]byte(fmt.Sprintf("%g", n.Value()))))
+ e.EncodeToken(xml.EndElement{Name: xmlName})
+ return nil
+}
+
+func (n NullableFloat64) MarshalXMLAttr(name xml.Name) (xml.Attr, error) {
+ var result xml.Attr
+ if n.Null() {
+ return result, nil
+ }
+ return xml.Attr{
+ Name: xml.Name{Local: name.Local},
+ Value: fmt.Sprintf("%g", n.Value())},
+ nil
+}
diff --git a/vendor/github.com/tkrajina/gpxgo/gpx/nullable_int.go b/vendor/github.com/tkrajina/gpxgo/gpx/nullable_int.go
new file mode 100644
index 0000000..1c51f3e
--- /dev/null
+++ b/vendor/github.com/tkrajina/gpxgo/gpx/nullable_int.go
@@ -0,0 +1,100 @@
+// Copyright 2013, 2014 Peter Vasil, Tomo Krajina. All
+// rights reserved. Use of this source code is governed
+// by a BSD-style license that can be found in the
+// LICENSE file.
+
+package gpx
+
+import (
+ "encoding/xml"
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+type NullableInt struct {
+ data int
+ notNull bool
+}
+
+func (n *NullableInt) Null() bool {
+ return !n.notNull
+}
+
+func (n *NullableInt) NotNull() bool {
+ return n.notNull
+}
+
+func (n *NullableInt) Value() int {
+ return n.data
+}
+
+func (n *NullableInt) SetValue(data int) {
+ n.data = data
+ n.notNull = true
+}
+
+func (n *NullableInt) SetNull() {
+ var defaultValue int
+ n.data = defaultValue
+ n.notNull = false
+}
+
+func NewNullableInt(data int) *NullableInt {
+ result := new(NullableInt)
+ result.data = data
+ result.notNull = true
+ return result
+}
+
+func (n *NullableInt) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
+ t, err := d.Token()
+ if err != nil {
+ n.SetNull()
+ return nil
+ }
+ if charData, ok := t.(xml.CharData); ok {
+ strData := strings.Trim(string(charData), " ")
+ value, err := strconv.ParseFloat(strData, 64)
+ if err != nil {
+ n.SetNull()
+ return nil
+ }
+ n.SetValue(int(value))
+ }
+ d.Skip()
+ return nil
+}
+
+func (n *NullableInt) UnmarshalXMLAttr(attr xml.Attr) error {
+ strData := strings.Trim(string(attr.Value), " ")
+ value, err := strconv.ParseFloat(strData, 64)
+ if err != nil {
+ n.SetNull()
+ return nil
+ }
+ n.SetValue(int(value))
+ return nil
+}
+
+func (n NullableInt) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if n.Null() {
+ return nil
+ }
+ xmlName := xml.Name{Local: start.Name.Local}
+ e.EncodeToken(xml.StartElement{Name: xmlName})
+ e.EncodeToken(xml.CharData([]byte(fmt.Sprintf("%d", n.Value()))))
+ e.EncodeToken(xml.EndElement{Name: xmlName})
+ return nil
+}
+
+func (n NullableInt) MarshalXMLAttr(name xml.Name) (xml.Attr, error) {
+ var result xml.Attr
+ if n.Null() {
+ return result, nil
+ }
+ return xml.Attr{
+ Name: xml.Name{Local: name.Local},
+ Value: fmt.Sprintf("%d", n.Value())},
+ nil
+}
diff --git a/vendor/github.com/tkrajina/gpxgo/gpx/nullable_string.go b/vendor/github.com/tkrajina/gpxgo/gpx/nullable_string.go
new file mode 100644
index 0000000..9466b43
--- /dev/null
+++ b/vendor/github.com/tkrajina/gpxgo/gpx/nullable_string.go
@@ -0,0 +1,41 @@
+// Copyright 2013, 2014 Peter Vasil, Tomo Krajina. All
+// rights reserved. Use of this source code is governed
+// by a BSD-style license that can be found in the
+// LICENSE file.
+
+package gpx
+
+type NullableString struct {
+ data string
+ notNull bool
+}
+
+func (n *NullableString) Null() bool {
+ return !n.notNull
+}
+
+func (n *NullableString) NotNull() bool {
+ return n.notNull
+}
+
+func (n *NullableString) Value() string {
+ return n.data
+}
+
+func (n *NullableString) SetValue(data string) {
+ n.data = data
+ n.notNull = true
+}
+
+func (n *NullableString) SetNull() {
+ var defaultValue string
+ n.data = defaultValue
+ n.notNull = false
+}
+
+func NewNullableString(data string) *NullableString {
+ result := new(NullableString)
+ result.data = data
+ result.notNull = true
+ return result
+}
diff --git a/vendor/github.com/tkrajina/gpxgo/gpx/nullable_time.go b/vendor/github.com/tkrajina/gpxgo/gpx/nullable_time.go
new file mode 100644
index 0000000..4dafd2c
--- /dev/null
+++ b/vendor/github.com/tkrajina/gpxgo/gpx/nullable_time.go
@@ -0,0 +1,43 @@
+// Copyright 2013, 2014 Peter Vasil, Tomo Krajina. All
+// rights reserved. Use of this source code is governed
+// by a BSD-style license that can be found in the
+// LICENSE file.
+
+package gpx
+
+import "time"
+
+type NullableTime struct {
+ data time.Time
+ notNull bool
+}
+
+func (n *NullableTime) Null() bool {
+ return !n.notNull
+}
+
+func (n *NullableTime) NotNull() bool {
+ return n.notNull
+}
+
+func (n *NullableTime) Value() time.Time {
+ return n.data
+}
+
+func (n *NullableTime) SetValue(data time.Time) {
+ n.data = data
+ n.notNull = true
+}
+
+func (n *NullableTime) SetNull() {
+ var defaultValue time.Time
+ n.data = defaultValue
+ n.notNull = false
+}
+
+func NewNullableTime(data time.Time) *NullableTime {
+ result := new(NullableTime)
+ result.data = data
+ result.notNull = true
+ return result
+}
diff --git a/vendor/github.com/tkrajina/gpxgo/gpx/xml.go b/vendor/github.com/tkrajina/gpxgo/gpx/xml.go
new file mode 100644
index 0000000..e24217a
--- /dev/null
+++ b/vendor/github.com/tkrajina/gpxgo/gpx/xml.go
@@ -0,0 +1,178 @@
+// Copyright 2013, 2014 Peter Vasil, Tomo Krajina. All
+// rights reserved. Use of this source code is governed
+// by a BSD-style license that can be found in the
+// LICENSE file.
+
+package gpx
+
+import (
+ "bytes"
+ "encoding/xml"
+ "errors"
+ "io/ioutil"
+ "os"
+ "strings"
+ "time"
+)
+
+// An array cannot be constant :( The first one if the default layout:
+var TIMELAYOUTS = []string{
+ "2006-01-02T15:04:05Z",
+ "2006-01-02T15:04:05",
+ "2006-01-02 15:04:05Z",
+ "2006-01-02 15:04:05",
+}
+
+func init() {
+ /*
+ fmt.Println("----------------------------------------------------------------------------------------------------")
+ fmt.Println("This API is experimental, it *will* change")
+ fmt.Println("----------------------------------------------------------------------------------------------------")
+ */
+}
+
+type ToXmlParams struct {
+ Version string
+ Indent bool
+}
+
+/*
+ * Params are optional, you can set null to use GPXs Version and no indentation.
+ */
+func ToXml(g *GPX, params ToXmlParams) ([]byte, error) {
+ version := g.Version
+ if len(params.Version) > 0 {
+ version = params.Version
+ }
+ indentation := params.Indent
+
+ var gpxDoc interface{}
+ if version == "1.0" {
+ gpxDoc = convertToGpx10Models(g)
+ } else if version == "1.1" {
+ gpxDoc = convertToGpx11Models(g)
+ } else {
+ g.Version = "1.1"
+ gpxDoc = convertToGpx11Models(g)
+ }
+
+ var buffer bytes.Buffer
+ buffer.WriteString(xml.Header)
+ if indentation {
+ bytes, err := xml.MarshalIndent(gpxDoc, "", " ")
+ if err != nil {
+ return nil, err
+ }
+ buffer.Write(bytes)
+ } else {
+ bytes, err := xml.Marshal(gpxDoc)
+ if err != nil {
+ return nil, err
+ }
+ buffer.Write(bytes)
+ }
+ return buffer.Bytes(), nil
+}
+
+func guessGPXVersion(bytes []byte) (string, error) {
+ bytesCount := 1000
+ if len(bytes) < 1000 {
+ bytesCount = len(bytes)
+ }
+
+ startOfDocument := string(bytes[:bytesCount])
+
+ parts := strings.Split(startOfDocument, "<gpx")
+ if len(parts) <= 1 {
+ return "", errors.New("Invalid GPX file, cannot find version")
+ }
+ parts = strings.Split(parts[1], "version=")
+
+ if len(parts) <= 1 {
+ return "", errors.New("Invalid GPX file, cannot find version")
+ }
+
+ if len(parts[1]) < 10 {
+ return "", errors.New("Invalid GPX file, cannot find version")
+ }
+
+ result := parts[1][1:4]
+
+ return result, nil
+}
+
+func parseGPXTime(timestr string) (*time.Time, error) {
+ if strings.Contains(timestr, ".") {
+ // Probably seconds with milliseconds
+ timestr = strings.Split(timestr, ".")[0]
+ }
+ timestr = strings.Trim(timestr, " \t\n\r")
+ for _, timeLayout := range TIMELAYOUTS {
+ t, err := time.Parse(timeLayout, timestr)
+
+ if err == nil {
+ return &t, nil
+ }
+ }
+
+ return nil, errors.New("Cannot parse " + timestr)
+}
+
+func formatGPXTime(time *time.Time) string {
+ if time == nil {
+ return ""
+ }
+ if time.Year() <= 1 {
+ // Invalid date:
+ return ""
+ }
+ return time.Format(TIMELAYOUTS[0])
+}
+
+func ParseFile(fileName string) (*GPX, error) {
+ f, err := os.Open(fileName)
+ if err != nil {
+ return nil, err
+ }
+
+ defer f.Close()
+
+ bytes, err := ioutil.ReadAll(f)
+ if err != nil {
+ return nil, err
+ }
+
+ return ParseBytes(bytes)
+}
+
+func ParseBytes(bytes []byte) (*GPX, error) {
+ version, err := guessGPXVersion(bytes)
+ if err != nil {
+ // Unknown version, try with 1.1
+ version = "1.1"
+ }
+
+ if version == "1.0" {
+ g := &gpx10Gpx{}
+ err := xml.Unmarshal(bytes, &g)
+ if err != nil {
+ return nil, err
+ }
+
+ return convertFromGpx10Models(g), nil
+ } else if version == "1.1" {
+ g := &gpx11Gpx{}
+ err := xml.Unmarshal(bytes, &g)
+ if err != nil {
+ return nil, err
+ }
+
+ return convertFromGpx11Models(g), nil
+ } else {
+ return nil, errors.New("Invalid version:" + version)
+ }
+}
+
+func ParseString(str string) (*GPX, error) {
+ return ParseBytes([]byte(str))
+}