mirror of https://github.com/etcd-io/dbtester.git
*: glide for vendoring
This commit is contained in:
parent
ade19344a1
commit
747d55fe19
|
|
@ -1,448 +0,0 @@
|
|||
{
|
||||
"ImportPath": "github.com/coreos/dbtester",
|
||||
"GoVersion": "go1.6",
|
||||
"GodepVersion": "v71",
|
||||
"Deps": [
|
||||
{
|
||||
"ImportPath": "bitbucket.org/zombiezen/gopdf/pdf",
|
||||
"Comment": "go.weekly.2012-02-22-18",
|
||||
"Rev": "'1c63dc69751bc45441c2ce1f56b631c55294b4d5'"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/Sirupsen/logrus",
|
||||
"Comment": "v0.10.0-18-g6d9ae30",
|
||||
"Rev": "6d9ae300aaf85d6acd2e5424081c7fcddb21dab8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ajstarks/svgo",
|
||||
"Comment": "go.weekly.2012-01-27-130-g672fe54",
|
||||
"Rev": "672fe547df4e49efc6db67a74391368bcb149b37"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/cheggaaa/pb",
|
||||
"Comment": "v1.0.3-1-gc1f48d5",
|
||||
"Rev": "c1f48d5ce4f292dfb775ef52aaedd15be323510d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/coreos/etcd/auth/authpb",
|
||||
"Comment": "v3.0.0-beta.0-234-g20fc3e9",
|
||||
"Rev": "20fc3e968fc9f742a9eb32f8eb44e70a822e9ae4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/coreos/etcd/client",
|
||||
"Comment": "v3.0.0-beta.0-234-g20fc3e9",
|
||||
"Rev": "20fc3e968fc9f742a9eb32f8eb44e70a822e9ae4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/coreos/etcd/clientv3",
|
||||
"Comment": "v3.0.0-beta.0-234-g20fc3e9",
|
||||
"Rev": "20fc3e968fc9f742a9eb32f8eb44e70a822e9ae4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes",
|
||||
"Comment": "v3.0.0-beta.0-234-g20fc3e9",
|
||||
"Rev": "20fc3e968fc9f742a9eb32f8eb44e70a822e9ae4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/coreos/etcd/etcdserver/etcdserverpb",
|
||||
"Comment": "v3.0.0-beta.0-234-g20fc3e9",
|
||||
"Rev": "20fc3e968fc9f742a9eb32f8eb44e70a822e9ae4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/coreos/etcd/mvcc/mvccpb",
|
||||
"Comment": "v3.0.0-beta.0-234-g20fc3e9",
|
||||
"Rev": "20fc3e968fc9f742a9eb32f8eb44e70a822e9ae4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/coreos/etcd/pkg/pathutil",
|
||||
"Comment": "v3.0.0-beta.0-234-g20fc3e9",
|
||||
"Rev": "20fc3e968fc9f742a9eb32f8eb44e70a822e9ae4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/coreos/etcd/pkg/tlsutil",
|
||||
"Comment": "v3.0.0-beta.0-234-g20fc3e9",
|
||||
"Rev": "20fc3e968fc9f742a9eb32f8eb44e70a822e9ae4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/coreos/etcd/pkg/types",
|
||||
"Comment": "v3.0.0-beta.0-234-g20fc3e9",
|
||||
"Rev": "20fc3e968fc9f742a9eb32f8eb44e70a822e9ae4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/cpuguy83/go-md2man/md2man",
|
||||
"Comment": "v1.0.4",
|
||||
"Rev": "71acacd42f85e5e82f70a55327789582a5200a90"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/dustin/go-humanize",
|
||||
"Rev": "8929fe90cee4b2cb9deb468b51fb34eba64d1bf0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/fatih/color",
|
||||
"Comment": "v0.1-17-g533cd7f",
|
||||
"Rev": "533cd7fd8a85905f67a1753afb4deddc85ea174f"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ghodss/yaml",
|
||||
"Rev": "73d445a93680fa1a78ae23a5839bad48f32ba1ee"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gogo/protobuf/gogoproto",
|
||||
"Comment": "v0.2-13-gc3995ae",
|
||||
"Rev": "c3995ae437bb78d1189f4f147dfe5f87ad3596e4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gogo/protobuf/proto",
|
||||
"Comment": "v0.2-13-gc3995ae",
|
||||
"Rev": "c3995ae437bb78d1189f4f147dfe5f87ad3596e4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gogo/protobuf/protoc-gen-gogo/descriptor",
|
||||
"Comment": "v0.2-13-gc3995ae",
|
||||
"Rev": "c3995ae437bb78d1189f4f147dfe5f87ad3596e4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/golang/freetype",
|
||||
"Comment": "release-130-ga31d014",
|
||||
"Rev": "a31d0146dac6f09aa27eda40995aeca7d7adc293"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/golang/freetype/raster",
|
||||
"Comment": "release-130-ga31d014",
|
||||
"Rev": "a31d0146dac6f09aa27eda40995aeca7d7adc293"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/golang/freetype/truetype",
|
||||
"Comment": "release-130-ga31d014",
|
||||
"Rev": "a31d0146dac6f09aa27eda40995aeca7d7adc293"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/golang/protobuf/proto",
|
||||
"Rev": "6aaa8d47701fa6cf07e914ec01fde3d4a1fe79c3"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gonum/floats",
|
||||
"Rev": "856ee8119ad91596c5394fa0a5c0470b6f16e676"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gonum/internal/asm",
|
||||
"Rev": "b5df70d76416ba52f34f94cb3bfbab2a3d958e4e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gonum/plot",
|
||||
"Rev": "754210960d0ffa304547dcfeafac8583698d0e1a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gonum/plot/palette",
|
||||
"Rev": "754210960d0ffa304547dcfeafac8583698d0e1a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gonum/plot/plotter",
|
||||
"Rev": "754210960d0ffa304547dcfeafac8583698d0e1a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gonum/plot/plotutil",
|
||||
"Rev": "754210960d0ffa304547dcfeafac8583698d0e1a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gonum/plot/vg",
|
||||
"Rev": "754210960d0ffa304547dcfeafac8583698d0e1a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gonum/plot/vg/draw",
|
||||
"Rev": "754210960d0ffa304547dcfeafac8583698d0e1a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gonum/plot/vg/fonts",
|
||||
"Rev": "754210960d0ffa304547dcfeafac8583698d0e1a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gonum/plot/vg/vgeps",
|
||||
"Rev": "754210960d0ffa304547dcfeafac8583698d0e1a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gonum/plot/vg/vgimg",
|
||||
"Rev": "754210960d0ffa304547dcfeafac8583698d0e1a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gonum/plot/vg/vgpdf",
|
||||
"Rev": "754210960d0ffa304547dcfeafac8583698d0e1a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gonum/plot/vg/vgsvg",
|
||||
"Rev": "754210960d0ffa304547dcfeafac8583698d0e1a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gyuho/dataframe",
|
||||
"Rev": "573cd728a011e5473510a6a1df0f39023c305e04"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gyuho/psn/ps",
|
||||
"Rev": "88c9e2a3a8857e3be57fe67694db47bb23f29c31"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/hashicorp/consul/api",
|
||||
"Comment": "v0.6.4-341-g22938ab",
|
||||
"Rev": "22938ab8b3ca069e490a4897a8ec13308ac61605"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/hashicorp/go-cleanhttp",
|
||||
"Rev": "ad28ea4487f05916463e2423a55166280e8254b5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/hashicorp/serf/coordinate",
|
||||
"Comment": "v0.7.0-62-gb60a6d9",
|
||||
"Rev": "b60a6d928fe726a588f79a1d500582507f9d79de"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/inconshreveable/mousetrap",
|
||||
"Rev": "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/llgcode/draw2d",
|
||||
"Comment": "v1-71-g5e675a3",
|
||||
"Rev": "5e675a3055a6f81f9b014b1dc2d5bb5fb8890618"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/llgcode/draw2d/draw2dbase",
|
||||
"Comment": "v1-71-g5e675a3",
|
||||
"Rev": "5e675a3055a6f81f9b014b1dc2d5bb5fb8890618"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/llgcode/draw2d/draw2dimg",
|
||||
"Comment": "v1-71-g5e675a3",
|
||||
"Rev": "5e675a3055a6f81f9b014b1dc2d5bb5fb8890618"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/mattn/go-colorable",
|
||||
"Comment": "v0.0.4-1-ge8a10dd",
|
||||
"Rev": "e8a10ddc7d2a7fe255f6fd7f501070a4cb62c183"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/mattn/go-isatty",
|
||||
"Rev": "56b76bdf51f7708750eac80fa38b952bb9f32639"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/mattn/go-runewidth",
|
||||
"Comment": "v0.0.1",
|
||||
"Rev": "d6bea18f789704b5f83375793155289da36a3c7f"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/olekukonko/tablewriter",
|
||||
"Rev": "cca8bbc0798408af109aaaa239cbd2634846b340"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/russross/blackfriday",
|
||||
"Comment": "v1.4-2-g300106c",
|
||||
"Rev": "300106c228d52c8941d4b3de6054a6062a86dda3"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/samuel/go-zookeeper/zk",
|
||||
"Rev": "6eb1b09c6ce23f305f4c81bf748b22fbc6f3f9e9"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/shurcooL/sanitized_anchor_name",
|
||||
"Rev": "10ef21a441db47d8b13ebcc5fd2310f636973c77"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/spf13/cobra",
|
||||
"Rev": "1c44ec8d3f1552cac48999f9306da23c4d8a288b"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/spf13/pflag",
|
||||
"Rev": "08b1a584251b5b62f458943640fc8ebd4d50aaa5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ugorji/go/codec",
|
||||
"Rev": "f1f1a805ed361a0e078bb537e4ea78cd37dcf065"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/image/draw",
|
||||
"Rev": "f551d3a6b7fc11df315ad9e18b404280680f8bec"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/image/font",
|
||||
"Rev": "f551d3a6b7fc11df315ad9e18b404280680f8bec"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/image/math/f64",
|
||||
"Rev": "f551d3a6b7fc11df315ad9e18b404280680f8bec"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/image/math/fixed",
|
||||
"Rev": "f551d3a6b7fc11df315ad9e18b404280680f8bec"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/image/tiff",
|
||||
"Rev": "f551d3a6b7fc11df315ad9e18b404280680f8bec"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/image/tiff/lzw",
|
||||
"Rev": "f551d3a6b7fc11df315ad9e18b404280680f8bec"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/context",
|
||||
"Rev": "6acef71eb69611914f7a30939ea9f6e194c78172"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/context/ctxhttp",
|
||||
"Rev": "6acef71eb69611914f7a30939ea9f6e194c78172"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2",
|
||||
"Rev": "6acef71eb69611914f7a30939ea9f6e194c78172"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||
"Rev": "6acef71eb69611914f7a30939ea9f6e194c78172"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/internal/timeseries",
|
||||
"Rev": "6acef71eb69611914f7a30939ea9f6e194c78172"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/trace",
|
||||
"Rev": "6acef71eb69611914f7a30939ea9f6e194c78172"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2",
|
||||
"Rev": "c406a4cc4ba462e5dc2f16225c5bd9488f9cbe10"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2/google",
|
||||
"Rev": "c406a4cc4ba462e5dc2f16225c5bd9488f9cbe10"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2/internal",
|
||||
"Rev": "c406a4cc4ba462e5dc2f16225c5bd9488f9cbe10"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2/jws",
|
||||
"Rev": "c406a4cc4ba462e5dc2f16225c5bd9488f9cbe10"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2/jwt",
|
||||
"Rev": "c406a4cc4ba462e5dc2f16225c5bd9488f9cbe10"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/sys/unix",
|
||||
"Rev": "9c60d1c508f5134d1ca726b4641db998f2523357"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/time/rate",
|
||||
"Rev": "a4bde12657593d5e90d0533a3e4fd95e635124cb"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/api/gensupport",
|
||||
"Rev": "aa89374d6f4a7f9ab85dcb3b01c95d2d3bd7f10a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/api/googleapi",
|
||||
"Rev": "aa89374d6f4a7f9ab85dcb3b01c95d2d3bd7f10a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/api/googleapi/internal/uritemplates",
|
||||
"Rev": "aa89374d6f4a7f9ab85dcb3b01c95d2d3bd7f10a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/api/storage/v1",
|
||||
"Rev": "aa89374d6f4a7f9ab85dcb3b01c95d2d3bd7f10a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/appengine",
|
||||
"Rev": "7f59a8c76b8594d06044bfe0bcbe475cb2020482"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/appengine/internal",
|
||||
"Rev": "7f59a8c76b8594d06044bfe0bcbe475cb2020482"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/appengine/internal/app_identity",
|
||||
"Rev": "7f59a8c76b8594d06044bfe0bcbe475cb2020482"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/appengine/internal/base",
|
||||
"Rev": "7f59a8c76b8594d06044bfe0bcbe475cb2020482"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/appengine/internal/datastore",
|
||||
"Rev": "7f59a8c76b8594d06044bfe0bcbe475cb2020482"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/appengine/internal/modules",
|
||||
"Rev": "7f59a8c76b8594d06044bfe0bcbe475cb2020482"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/appengine/internal/remote_api",
|
||||
"Rev": "7f59a8c76b8594d06044bfe0bcbe475cb2020482"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/cloud",
|
||||
"Rev": "4da660f59adf463f364fca03a6f8422758101a84"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/cloud/compute/metadata",
|
||||
"Rev": "4da660f59adf463f364fca03a6f8422758101a84"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/cloud/internal",
|
||||
"Rev": "4da660f59adf463f364fca03a6f8422758101a84"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/cloud/internal/opts",
|
||||
"Rev": "4da660f59adf463f364fca03a6f8422758101a84"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/cloud/internal/transport",
|
||||
"Rev": "4da660f59adf463f364fca03a6f8422758101a84"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/cloud/storage",
|
||||
"Rev": "4da660f59adf463f364fca03a6f8422758101a84"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/grpc",
|
||||
"Rev": "15e50a43c679d14f4f83a83d3177864cfd751cdd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/grpc/codes",
|
||||
"Rev": "15e50a43c679d14f4f83a83d3177864cfd751cdd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/grpc/credentials",
|
||||
"Rev": "15e50a43c679d14f4f83a83d3177864cfd751cdd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/grpc/credentials/oauth",
|
||||
"Rev": "15e50a43c679d14f4f83a83d3177864cfd751cdd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/grpc/grpclog",
|
||||
"Rev": "15e50a43c679d14f4f83a83d3177864cfd751cdd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/grpc/internal",
|
||||
"Rev": "15e50a43c679d14f4f83a83d3177864cfd751cdd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/grpc/metadata",
|
||||
"Rev": "15e50a43c679d14f4f83a83d3177864cfd751cdd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/grpc/naming",
|
||||
"Rev": "15e50a43c679d14f4f83a83d3177864cfd751cdd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/grpc/peer",
|
||||
"Rev": "15e50a43c679d14f4f83a83d3177864cfd751cdd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/grpc/transport",
|
||||
"Rev": "15e50a43c679d14f4f83a83d3177864cfd751cdd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "gopkg.in/yaml.v2",
|
||||
"Rev": "53feefa2559fb8dfa8d81baad31be332c97d6c77"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
hash: 5d55e960cf32753438f359a810a38dc01a952faa491ec376f51cb6ea2e47ef0d
|
||||
updated: 2016-05-29T11:48:49.670861703-07:00
|
||||
imports:
|
||||
- name: bitbucket.org/zombiezen/gopdf
|
||||
version: 1c63dc69751bc45441c2ce1f56b631c55294b4d5
|
||||
subpackages:
|
||||
- pdf
|
||||
- name: github.com/ajstarks/svgo
|
||||
version: 672fe547df4e49efc6db67a74391368bcb149b37
|
||||
- name: github.com/cheggaaa/pb
|
||||
version: c1f48d5ce4f292dfb775ef52aaedd15be323510d
|
||||
- name: github.com/cloudfoundry-incubator/candiedyaml
|
||||
version: 99c3df83b51532e3615f851d8c2dbb638f5313bf
|
||||
- name: github.com/coreos/etcd
|
||||
version: 84e7fa149e37272899e495a357c580fc5feddec9
|
||||
subpackages:
|
||||
- client
|
||||
- clientv3
|
||||
- pkg/pathutil
|
||||
- pkg/types
|
||||
- auth/authpb
|
||||
- etcdserver/api/v3rpc/rpctypes
|
||||
- etcdserver/etcdserverpb
|
||||
- mvcc/mvccpb
|
||||
- pkg/tlsutil
|
||||
- name: github.com/dustin/go-humanize
|
||||
version: 9436b7a0ebd4b3a35fb30aa244f8f85244088bfa
|
||||
- name: github.com/fatih/color
|
||||
version: 533cd7fd8a85905f67a1753afb4deddc85ea174f
|
||||
- name: github.com/ghodss/yaml
|
||||
version: e8e0db9016175449df0e9c4b6e6995a9433a395c
|
||||
- name: github.com/gogo/protobuf
|
||||
version: c18eea6ad611eecf94a9ba38471f59706199409e
|
||||
subpackages:
|
||||
- gogoproto
|
||||
- proto
|
||||
- protoc-gen-gogo/descriptor
|
||||
- name: github.com/golang/freetype
|
||||
version: a31d0146dac6f09aa27eda40995aeca7d7adc293
|
||||
subpackages:
|
||||
- truetype
|
||||
- raster
|
||||
- name: github.com/golang/protobuf
|
||||
version: 9e6977f30c91c78396e719e164e57f9287fff42c
|
||||
subpackages:
|
||||
- proto
|
||||
- name: github.com/gonum/floats
|
||||
version: 856ee8119ad91596c5394fa0a5c0470b6f16e676
|
||||
- name: github.com/gonum/internal
|
||||
version: b5df70d76416ba52f34f94cb3bfbab2a3d958e4e
|
||||
subpackages:
|
||||
- asm
|
||||
- name: github.com/gonum/plot
|
||||
version: 15005ec89f739168a773c3571bc1b852eb94bf8b
|
||||
subpackages:
|
||||
- plotter
|
||||
- plotutil
|
||||
- vg
|
||||
- vg/draw
|
||||
- palette
|
||||
- vg/fonts
|
||||
- vg/vgeps
|
||||
- vg/vgimg
|
||||
- vg/vgpdf
|
||||
- vg/vgsvg
|
||||
- name: github.com/gyuho/dataframe
|
||||
version: 573cd728a011e5473510a6a1df0f39023c305e04
|
||||
- name: github.com/gyuho/psn
|
||||
version: 88c9e2a3a8857e3be57fe67694db47bb23f29c31
|
||||
subpackages:
|
||||
- ps
|
||||
- name: github.com/hashicorp/consul
|
||||
version: b43f900766ad92eebbd5a8f931fe0fe244f9969d
|
||||
subpackages:
|
||||
- api
|
||||
- name: github.com/hashicorp/go-cleanhttp
|
||||
version: 875fb671b3ddc66f8e2f0acc33829c8cb989a38d
|
||||
- name: github.com/hashicorp/serf
|
||||
version: e4ec8cc423bbe20d26584b96efbeb9102e16d05f
|
||||
subpackages:
|
||||
- coordinate
|
||||
- serf
|
||||
- name: github.com/inconshreveable/mousetrap
|
||||
version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
|
||||
- name: github.com/llgcode/draw2d
|
||||
version: 5e675a3055a6f81f9b014b1dc2d5bb5fb8890618
|
||||
subpackages:
|
||||
- draw2dimg
|
||||
- draw2dbase
|
||||
- name: github.com/mattn/go-colorable
|
||||
version: 9cbef7c35391cca05f15f8181dc0b18bc9736dbb
|
||||
repo: https://github.com/mattn/go-colorable
|
||||
- name: github.com/mattn/go-isatty
|
||||
version: 56b76bdf51f7708750eac80fa38b952bb9f32639
|
||||
- name: github.com/mattn/go-runewidth
|
||||
version: d6bea18f789704b5f83375793155289da36a3c7f
|
||||
- name: github.com/olekukonko/tablewriter
|
||||
version: 8d0265a48283795806b872b4728c67bf5c777f20
|
||||
- name: github.com/samuel/go-zookeeper
|
||||
version: 6eb1b09c6ce23f305f4c81bf748b22fbc6f3f9e9
|
||||
subpackages:
|
||||
- zk
|
||||
- name: github.com/Sirupsen/logrus
|
||||
version: 6d9ae300aaf85d6acd2e5424081c7fcddb21dab8
|
||||
- name: github.com/spf13/cobra
|
||||
version: f368244301305f414206f889b1735a54cfc8bde8
|
||||
- name: github.com/spf13/pflag
|
||||
version: cb88ea77998c3f024757528e3305022ab50b43be
|
||||
- name: github.com/ugorji/go
|
||||
version: a396ed22fc049df733440d90efe17475e3929ccb
|
||||
subpackages:
|
||||
- codec
|
||||
- name: golang.org/x/image
|
||||
version: 97680175a5267bb8b31f1923e7a66df98013b11a
|
||||
subpackages:
|
||||
- math/fixed
|
||||
- font
|
||||
- tiff
|
||||
- draw
|
||||
- math/f64
|
||||
- tiff/lzw
|
||||
- name: golang.org/x/net
|
||||
version: 30db96677b74e24b967e23f911eb3364fc61a011
|
||||
subpackages:
|
||||
- context
|
||||
- http2
|
||||
- trace
|
||||
- http2/hpack
|
||||
- lex/httplex
|
||||
- internal/timeseries
|
||||
- context/ctxhttp
|
||||
- name: golang.org/x/oauth2
|
||||
version: c406a4cc4ba462e5dc2f16225c5bd9488f9cbe10
|
||||
subpackages:
|
||||
- google
|
||||
- jwt
|
||||
- internal
|
||||
- jws
|
||||
- name: golang.org/x/sys
|
||||
version: d4feaf1a7e61e1d9e79e6c4e76c6349e9cab0a03
|
||||
subpackages:
|
||||
- unix
|
||||
- name: golang.org/x/time
|
||||
version: a4bde12657593d5e90d0533a3e4fd95e635124cb
|
||||
subpackages:
|
||||
- rate
|
||||
- name: google.golang.org/api
|
||||
version: aa89374d6f4a7f9ab85dcb3b01c95d2d3bd7f10a
|
||||
subpackages:
|
||||
- googleapi
|
||||
- storage/v1
|
||||
- googleapi/internal/uritemplates
|
||||
- gensupport
|
||||
- name: google.golang.org/appengine
|
||||
version: 7f59a8c76b8594d06044bfe0bcbe475cb2020482
|
||||
subpackages:
|
||||
- urlfetch
|
||||
- internal
|
||||
- internal/app_identity
|
||||
- internal/modules
|
||||
- internal/urlfetch
|
||||
- internal/base
|
||||
- internal/datastore
|
||||
- internal/remote_api
|
||||
- name: google.golang.org/cloud
|
||||
version: 4da660f59adf463f364fca03a6f8422758101a84
|
||||
subpackages:
|
||||
- storage
|
||||
- compute/metadata
|
||||
- internal
|
||||
- internal/opts
|
||||
- internal/transport
|
||||
- name: google.golang.org/grpc
|
||||
version: 8213ee577a465c1f314d85748fb29e4eeed59baf
|
||||
subpackages:
|
||||
- codes
|
||||
- credentials
|
||||
- grpclog
|
||||
- internal
|
||||
- metadata
|
||||
- naming
|
||||
- transport
|
||||
- peer
|
||||
- credentials/oauth
|
||||
- name: gopkg.in/yaml.v2
|
||||
version: a83829b6f1293c91addabc89d0571c246397bbf4
|
||||
devImports: []
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package: github.com/coreos/dbtester
|
||||
import:
|
||||
- package: github.com/Sirupsen/logrus
|
||||
- package: github.com/cheggaaa/pb
|
||||
- package: github.com/coreos/etcd
|
||||
subpackages:
|
||||
- client
|
||||
- clientv3
|
||||
- package: github.com/dustin/go-humanize
|
||||
- package: github.com/gogo/protobuf
|
||||
subpackages:
|
||||
- gogoproto
|
||||
- package: github.com/golang/protobuf
|
||||
subpackages:
|
||||
- proto
|
||||
- package: github.com/gonum/plot
|
||||
subpackages:
|
||||
- plotter
|
||||
- plotutil
|
||||
- vg
|
||||
- package: github.com/gyuho/dataframe
|
||||
- package: github.com/gyuho/psn
|
||||
subpackages:
|
||||
- ps
|
||||
- package: github.com/hashicorp/consul
|
||||
subpackages:
|
||||
- api
|
||||
- package: github.com/samuel/go-zookeeper
|
||||
subpackages:
|
||||
- zk
|
||||
- package: github.com/spf13/cobra
|
||||
- package: golang.org/x/net
|
||||
subpackages:
|
||||
- context
|
||||
- package: golang.org/x/oauth2
|
||||
subpackages:
|
||||
- google
|
||||
- jwt
|
||||
- package: google.golang.org/cloud
|
||||
subpackages:
|
||||
- storage
|
||||
- package: google.golang.org/grpc
|
||||
- package: gopkg.in/yaml.v2
|
||||
Binary file not shown.
|
|
@ -0,0 +1 @@
|
|||
default
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
1c63dc69751bc45441c2ce1f56b631c55294b4d5 100
|
||||
1c63dc69751bc45441c2ce1f56b631c55294b4d5 default
|
||||
d9caad8ab6e8668628f6559454dd13ce4695c7dc go.r60
|
||||
4cc534bed36a0bbdba404e3fe481de5569161222 go.weekly.2011-11-01
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
100 1c63dc69751bc45441c2ce1f56b631c55294b4d5 7931f9f2de065e6924fd82c20c21f6919051a4af
|
||||
|
||||
61f2f88960fd453c40d1655472cb201a6917f226 go.weekly.2011-12-22
|
||||
641e2e5bd59c136d6e78cba91839aa3f96f4430a go.weekly.2012-02-22
|
||||
678fd0e19f2de643e44e52a131920733d75d400d go.weekly.2011-11-01
|
||||
678fd0e19f2de643e44e52a131920733d75d400d go.weekly.2011-11-01
|
||||
0000000000000000000000000000000000000000 go.weekly.2011-11-01
|
||||
d21733eb8d7dac896bbc3fb4f7a31cbd6edc4f22 go.weekly.2011-11-02
|
||||
d21733eb8d7dac896bbc3fb4f7a31cbd6edc4f22 go.weekly.2011-11-02
|
||||
d53f28b5260733b2d4d865c8dd00bfd451fbd373 go.weekly.2011-11-02
|
||||
Binary file not shown.
|
|
@ -0,0 +1,2 @@
|
|||
[paths]
|
||||
default = https://bitbucket.org/zombiezen/gopdf
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
dotencode
|
||||
fncache
|
||||
revlogv1
|
||||
store
|
||||
Binary file not shown.
Binary file not shown.
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/_a_u_t_h_o_r_s.i
generated
vendored
Normal file
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/_a_u_t_h_o_r_s.i
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/_c_o_n_t_r_i_b_u_t_o_r_s.i
generated
vendored
Normal file
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/_c_o_n_t_r_i_b_u_t_o_r_s.i
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/_l_i_c_e_n_s_e.i
generated
vendored
Normal file
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/_l_i_c_e_n_s_e.i
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/_r_e_a_d_m_e.rst.i
generated
vendored
Normal file
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/_r_e_a_d_m_e.rst.i
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/buildmetrics.bash.i
generated
vendored
Normal file
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/buildmetrics.bash.i
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/generate-image-testdata/main.go.i
generated
vendored
Normal file
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/generate-image-testdata/main.go.i
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/_makefile.i
generated
vendored
Normal file
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/_makefile.i
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/canvas.go.i
generated
vendored
Normal file
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/canvas.go.i
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/canvas__test.go.i
generated
vendored
Normal file
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/canvas__test.go.i
generated
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/encode.go.i
generated
vendored
Normal file
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/encode.go.i
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/encode__test.go.i
generated
vendored
Normal file
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/encode__test.go.i
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/image.go.i
generated
vendored
Normal file
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/image.go.i
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/image__test.go.i
generated
vendored
Normal file
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/image__test.go.i
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/marshal.go.i
generated
vendored
Normal file
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/marshal.go.i
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/marshal__test.go.i
generated
vendored
Normal file
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/marshal__test.go.i
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/metrics.go.i
generated
vendored
Normal file
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/metrics.go.i
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/objects.go.i
generated
vendored
Normal file
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/objects.go.i
generated
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/pdf__test.go.i
generated
vendored
Normal file
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/pdf__test.go.i
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/stream.go.i
generated
vendored
Normal file
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/stream.go.i
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/stream__test.go.i
generated
vendored
Normal file
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/stream__test.go.i
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/testdata/suzanne.bmp.i
generated
vendored
Normal file
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/testdata/suzanne.bmp.i
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/testdata/suzanne.bmp.pdf.i
generated
vendored
Normal file
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/testdata/suzanne.bmp.pdf.i
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/testdata/suzanne.jpg.i
generated
vendored
Normal file
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/testdata/suzanne.jpg.i
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/testdata/suzanne.jpg.pdf.d
generated
vendored
Normal file
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/testdata/suzanne.jpg.pdf.d
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/testdata/suzanne.jpg.pdf.i
generated
vendored
Normal file
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/testdata/suzanne.jpg.pdf.i
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/testdata/suzanne.png.i
generated
vendored
Normal file
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/testdata/suzanne.png.i
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/testdata/suzanne.png.pdf.i
generated
vendored
Normal file
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/testdata/suzanne.png.pdf.i
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/text.go.i
generated
vendored
Normal file
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/text.go.i
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/text__test.go.i
generated
vendored
Normal file
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/pdf/text__test.go.i
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/~2ehgignore.i
generated
vendored
Normal file
BIN
vendor/bitbucket.org/zombiezen/gopdf/.hg/store/data/~2ehgignore.i
generated
vendored
Normal file
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,33 @@
|
|||
data/AUTHORS.i
|
||||
data/CONTRIBUTORS.i
|
||||
data/pdf/doc.go.i
|
||||
data/pdf/pdf.go.i
|
||||
data/buildmetrics.bash.i
|
||||
data/pdf/image_test.go.i
|
||||
data/pdf/canvas.go.i
|
||||
data/pdf/marshal_test.go.i
|
||||
data/pdf/testdata/suzanne.bmp.i
|
||||
data/pdf/stream.go.i
|
||||
data/pdf/objects.go.i
|
||||
data/pdf/testdata/suzanne.jpg.i
|
||||
data/pdf/encode_test.go.i
|
||||
data/pdf/testdata/suzanne.png.i
|
||||
data/LICENSE.i
|
||||
data/pdf/stream_test.go.i
|
||||
data/pdf/Makefile.i
|
||||
data/pdf/encode.go.i
|
||||
data/pdf/testdata/suzanne.png.pdf.i
|
||||
data/.hgignore.i
|
||||
data/pdf/text_test.go.i
|
||||
data/pdf/marshal.go.i
|
||||
data/pdf/canvas_test.go.i
|
||||
data/pdf/testdata/suzanne.jpg.pdf.i
|
||||
data/pdf/image.go.i
|
||||
data/pdf/text.go.i
|
||||
data/generate-image-testdata/main.go.i
|
||||
data/pdf/testdata/suzanne.jpg.pdf.d
|
||||
data/.hgtags.i
|
||||
data/pdf/pdf_test.go.i
|
||||
data/README.rst.i
|
||||
data/pdf/testdata/suzanne.bmp.pdf.i
|
||||
data/pdf/metrics.go.i
|
||||
Binary file not shown.
|
|
@ -0,0 +1 @@
|
|||
default
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
0
|
||||
pull
|
||||
https://bitbucket.org/zombiezen/gopdf
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
syntax:glob
|
||||
*.[568ao]
|
||||
[568a].out
|
||||
*.cgo*.go
|
||||
*.cgo*.c
|
||||
_cgo_*
|
||||
_obj
|
||||
_test
|
||||
_testmain.go
|
||||
build.out
|
||||
test.out
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
678fd0e19f2de643e44e52a131920733d75d400d go.weekly.2011-11-01
|
||||
d21733eb8d7dac896bbc3fb4f7a31cbd6edc4f22 go.weekly.2011-11-02
|
||||
d21733eb8d7dac896bbc3fb4f7a31cbd6edc4f22 go.weekly.2011-11-02
|
||||
d53f28b5260733b2d4d865c8dd00bfd451fbd373 go.weekly.2011-11-02
|
||||
678fd0e19f2de643e44e52a131920733d75d400d go.weekly.2011-11-01
|
||||
0000000000000000000000000000000000000000 go.weekly.2011-11-01
|
||||
61f2f88960fd453c40d1655472cb201a6917f226 go.weekly.2011-12-22
|
||||
641e2e5bd59c136d6e78cba91839aa3f96f4430a go.weekly.2012-02-22
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
# This is the official list of gopdf authors for copyright purposes.
|
||||
# This file is distinct from the CONTRIBUTORS files.
|
||||
# See the latter for an explanation.
|
||||
|
||||
# Names should be added to this file as
|
||||
# Name or Organization <email address>
|
||||
# The email address is not required for organizations.
|
||||
|
||||
# Please keep the list sorted.
|
||||
|
||||
Ross Light <ross@zombiezen.com>
|
||||
Sheng Yu <yusheng.sjtu@gmail.com>
|
||||
Tarmigan Casebolt <tarmigan@gmail.com>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# This is the official list of people who can contribute
|
||||
# (and typically have contributed) code to the gopdf repository.
|
||||
# The AUTHORS file lists the copyright holders; this file
|
||||
# lists people. For example, Google employees are listed here
|
||||
# but not in AUTHORS, because Google holds the copyright.
|
||||
#
|
||||
# Names should be added to this file like so:
|
||||
# Name <email address>
|
||||
|
||||
# Please keep the list sorted.
|
||||
|
||||
Ross Light <ross@zombiezen.com>
|
||||
Sheng Yu <yusheng.sjtu@gmail.com>
|
||||
Tarmigan Casebolt <tarmigan@gmail.com>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
*********
|
||||
gopdf
|
||||
*********
|
||||
|
||||
gopdf is a Go library for creating PDF files.
|
||||
|
||||
To install gopdf::
|
||||
|
||||
go get bitbucket.org/zombiezen/gopdf/pdf
|
||||
|
||||
gopdf is released under a 2-clause BSD license.
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
#!/bin/bash
|
||||
|
||||
DIR="$(mktemp -d -t buildmetricsXXX)"
|
||||
TARBALL="Core14_AFMs.tar"
|
||||
URL="https://partners.adobe.com/public/developer/en/pdf/$TARBALL"
|
||||
OUTPUT="pdf/metrics.go"
|
||||
|
||||
echo "$DIR"
|
||||
curl "$URL" > "$DIR/$TARBALL"
|
||||
(cd "$DIR" ; tar -xf "$TARBALL")
|
||||
|
||||
rm -f "$OUTPUT"
|
||||
touch "$OUTPUT"
|
||||
echo "package pdf" >> "$OUTPUT"
|
||||
|
||||
for afmFile in "$DIR"/*.afm
|
||||
do
|
||||
fontName="$(basename "$afmFile" .afm)"
|
||||
varName="$(echo "$fontName" | sed 's/-//')Widths"
|
||||
echo >> "$OUTPUT"
|
||||
echo "// $fontName" >> "$OUTPUT"
|
||||
echo "var $varName = []uint16{" >> "$OUTPUT"
|
||||
sed -n 's/^C \([0-9]\|[0-9][0-9]\|1[012][0-9]\) ; WX \([0-9]\+\) ; N \([a-zA-Z]\+\).*/\1: \2, \/\/ \3/p' < "$afmFile" >> "$OUTPUT"
|
||||
echo "}" >> "$OUTPUT"
|
||||
done
|
||||
|
||||
gofmt -w "$OUTPUT"
|
||||
rm -rf "$DIR"
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (C) 2011, Ross Light
|
||||
|
||||
package pdf
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
const pathExpectedOutput = `12.00000 34.00000 m
|
||||
-56.00000 78.00000 l
|
||||
h
|
||||
3.10000 -5.90000 21.10000 80.90000 re
|
||||
`
|
||||
|
||||
func TestPath(t *testing.T) {
|
||||
path := new(Path)
|
||||
path.Move(Point{12, 34})
|
||||
path.Line(Point{-56, 78})
|
||||
path.Close()
|
||||
path.Rectangle(Rectangle{Point{3.1, -5.9}, Point{24.2, 75.0}})
|
||||
|
||||
if path.buf.String() != pathExpectedOutput {
|
||||
t.Errorf("Output was %q, expected %q", path.buf.String(), pathExpectedOutput)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright (C) 2011, Ross Light
|
||||
|
||||
package pdf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const encodingTestData = "%PDF-1.7\r\n" +
|
||||
"%\x93\x8c\x8b\x9e\r\n" +
|
||||
"1 0 obj\r\n" +
|
||||
"(Hello, World!)\r\n" +
|
||||
"endobj\r\n" +
|
||||
"2 0 obj\r\n" +
|
||||
"42\r\n" +
|
||||
"endobj\r\n" +
|
||||
"xref\r\n" +
|
||||
"0 3\r\n" +
|
||||
"0000000000 65535 f\r\n" +
|
||||
"0000000017 00000 n\r\n" +
|
||||
"0000000051 00000 n\r\n" +
|
||||
"trailer\r\n" +
|
||||
"<< /Size 3 /Root 0 0 R >>\r\n" +
|
||||
"startxref\r\n" +
|
||||
"72\r\n" +
|
||||
"%%EOF\r\n"
|
||||
|
||||
func TestEncoder(t *testing.T) {
|
||||
var e encoder
|
||||
if ref := e.add("Hello, World!"); !reflect.DeepEqual(ref, Reference{1, 0}) {
|
||||
t.Errorf("After adding first object, reference is %#v", ref)
|
||||
}
|
||||
if ref := e.add(42); !reflect.DeepEqual(ref, Reference{2, 0}) {
|
||||
t.Errorf("After adding second object, reference is %#v", ref)
|
||||
}
|
||||
|
||||
var b bytes.Buffer
|
||||
if err := e.encode(&b); err != nil {
|
||||
t.Fatalf("Encoding error: %v", err)
|
||||
}
|
||||
if b.String() != encodingTestData {
|
||||
t.Errorf("Encoding result %q, want %q", b.String(), encodingTestData)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,220 @@
|
|||
package pdf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
"image/jpeg"
|
||||
"image/png"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/image/bmp"
|
||||
)
|
||||
|
||||
const suzanneBytes = 512 * 512 * 3
|
||||
|
||||
func loadSuzanneRGBA() (*image.RGBA, error) {
|
||||
f, err := os.Open("testdata/suzanne.bmp")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
img, err := bmp.Decode(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return img.(*image.RGBA), nil
|
||||
}
|
||||
|
||||
func loadSuzanneNRGBA() (*image.NRGBA, error) {
|
||||
f, err := os.Open("testdata/suzanne.png")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
img, err := png.Decode(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return img.(*image.NRGBA), nil
|
||||
}
|
||||
|
||||
func loadSuzanneYCbCr() (*image.YCbCr, error) {
|
||||
f, err := os.Open("testdata/suzanne.jpg")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
img, err := jpeg.Decode(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return img.(*image.YCbCr), nil
|
||||
}
|
||||
|
||||
func BenchmarkEncodeRGBAGeneric(b *testing.B) {
|
||||
b.StopTimer()
|
||||
img, _ := loadSuzanneRGBA()
|
||||
b.SetBytes(suzanneBytes)
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
encodeImageStream(ioutil.Discard, img)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncodeRGBA(b *testing.B) {
|
||||
b.StopTimer()
|
||||
img, _ := loadSuzanneRGBA()
|
||||
b.SetBytes(suzanneBytes)
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
encodeRGBAStream(ioutil.Discard, img)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncodeNRGBAGeneric(b *testing.B) {
|
||||
b.StopTimer()
|
||||
img, _ := loadSuzanneNRGBA()
|
||||
b.SetBytes(suzanneBytes)
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
encodeImageStream(ioutil.Discard, img)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncodeNRGBA(b *testing.B) {
|
||||
b.StopTimer()
|
||||
img, _ := loadSuzanneNRGBA()
|
||||
b.SetBytes(suzanneBytes)
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
encodeNRGBAStream(ioutil.Discard, img)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncodeYCbCrGeneric(b *testing.B) {
|
||||
b.StopTimer()
|
||||
img, _ := loadSuzanneYCbCr()
|
||||
b.SetBytes(suzanneBytes)
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
encodeImageStream(ioutil.Discard, img)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncodeYCbCr(b *testing.B) {
|
||||
b.StopTimer()
|
||||
img, _ := loadSuzanneYCbCr()
|
||||
b.SetBytes(suzanneBytes)
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
encodeYCbCrStream(ioutil.Discard, img)
|
||||
}
|
||||
}
|
||||
|
||||
func expectImageBuffer(buf []byte, r image.Rectangle, c color.Color, t *testing.T) {
|
||||
nc := color.NRGBAModel.Convert(c).(color.NRGBA)
|
||||
if n := 3 * r.Dx() * r.Dy(); len(buf) != n {
|
||||
t.Errorf("stream length = %d; want %d", len(buf), n)
|
||||
}
|
||||
for i := 0; i+2 < len(buf); i += 3 {
|
||||
r, g, b := uint8(buf[i]), uint8(buf[i+1]), uint8(buf[i+2])
|
||||
if r != nc.R || g != nc.G || b != nc.B {
|
||||
t.Errorf("buf[%d:%d] = [%#02x %#02x %#02x]; want [%#02x %#02x %#02x]", i, i+3, r, g, b, nc.R, nc.G, nc.B)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeRGBAStream(t *testing.T) {
|
||||
r := image.Rect(0, 0, 16, 16)
|
||||
img := image.NewRGBA(r)
|
||||
c := color.RGBA{R: 40, G: 9, B: 33, A: 85}
|
||||
draw.Draw(img, r, image.NewUniform(c), image.ZP, draw.Src)
|
||||
|
||||
var buf bytes.Buffer
|
||||
encodeImageStream(&buf, img)
|
||||
expectImageBuffer(buf.Bytes(), r, c, t)
|
||||
}
|
||||
|
||||
func TestEncodeRGBAStreamGeneric(t *testing.T) {
|
||||
r := image.Rect(0, 0, 16, 16)
|
||||
img := image.NewRGBA(r)
|
||||
c := color.RGBA{R: 40, G: 9, B: 33, A: 85}
|
||||
draw.Draw(img, r, image.NewUniform(c), image.ZP, draw.Src)
|
||||
|
||||
var buf bytes.Buffer
|
||||
encodeRGBAStream(&buf, img)
|
||||
expectImageBuffer(buf.Bytes(), r, c, t)
|
||||
}
|
||||
|
||||
func TestEncodeNRGBAStream(t *testing.T) {
|
||||
r := image.Rect(0, 0, 16, 16)
|
||||
img := image.NewNRGBA(r)
|
||||
c := color.NRGBA{R: 120, G: 27, B: 99, A: 85}
|
||||
draw.Draw(img, r, image.NewUniform(c), image.ZP, draw.Src)
|
||||
|
||||
var buf bytes.Buffer
|
||||
encodeNRGBAStream(&buf, img)
|
||||
expectImageBuffer(buf.Bytes(), r, c, t)
|
||||
}
|
||||
|
||||
func TestEncodeNRGBAStreamGeneric(t *testing.T) {
|
||||
r := image.Rect(0, 0, 16, 16)
|
||||
img := image.NewNRGBA(r)
|
||||
c := color.NRGBA{R: 120, G: 27, B: 99, A: 85}
|
||||
draw.Draw(img, r, image.NewUniform(c), image.ZP, draw.Src)
|
||||
|
||||
var buf bytes.Buffer
|
||||
encodeImageStream(&buf, img)
|
||||
expectImageBuffer(buf.Bytes(), r, c, t)
|
||||
}
|
||||
|
||||
func TestEncodeYCbCrStream(t *testing.T) {
|
||||
r := image.Rect(0, 0, 16, 16)
|
||||
img := image.NewYCbCr(r, image.YCbCrSubsampleRatio444)
|
||||
c := color.YCbCr{Y: 70, Cb: 146, Cr: 164}
|
||||
for i := range img.Y {
|
||||
img.Y[i] = c.Y
|
||||
}
|
||||
for i := range img.Cb {
|
||||
img.Cb[i] = c.Cb
|
||||
}
|
||||
for i := range img.Cr {
|
||||
img.Cr[i] = c.Cr
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
encodeYCbCrStream(&buf, img)
|
||||
expectImageBuffer(buf.Bytes(), r, c, t)
|
||||
}
|
||||
|
||||
func TestEncodeYCbCrStreamGeneric(t *testing.T) {
|
||||
r := image.Rect(0, 0, 16, 16)
|
||||
img := image.NewYCbCr(r, image.YCbCrSubsampleRatio444)
|
||||
c := color.YCbCr{Y: 70, Cb: 146, Cr: 164}
|
||||
for i := range img.Y {
|
||||
img.Y[i] = c.Y
|
||||
}
|
||||
for i := range img.Cb {
|
||||
img.Cb[i] = c.Cb
|
||||
}
|
||||
for i := range img.Cr {
|
||||
img.Cr[i] = c.Cr
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
encodeImageStream(&buf, img)
|
||||
expectImageBuffer(buf.Bytes(), r, c, t)
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright (C) 2011, Ross Light
|
||||
|
||||
package pdf
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
type marshalTest struct {
|
||||
Value interface{}
|
||||
Expected string
|
||||
}
|
||||
|
||||
type fooStruct struct {
|
||||
Size int64
|
||||
Params map[name]string
|
||||
NotHere string `pdf:"-"`
|
||||
Rename float64 `pdf:"Pi"`
|
||||
TheVoid []int `pdf:",omitempty"`
|
||||
}
|
||||
|
||||
var marshalTests = []marshalTest{
|
||||
{nil, "null"},
|
||||
{"", "()"},
|
||||
{"This is a string", "(This is a string)"},
|
||||
{"Strings may contain newlines\nand such.", "(Strings may contain newlines\nand such.)"},
|
||||
{"Escape (this).", `(Escape \(this\).)`},
|
||||
{int(123), "123"},
|
||||
{int(-321), "-321"},
|
||||
{float64(-3.141599), "-3.14160"},
|
||||
{float64(1e9), "1000000000.00000"},
|
||||
{name(""), "/"},
|
||||
{name("foo"), "/foo"},
|
||||
{[]interface{}{}, `[ ]`},
|
||||
{[]string{"foo", "(parens)"}, `[ (foo) (\(parens\)) ]`},
|
||||
{map[name]string{}, `<< >>`},
|
||||
{map[name]string{name("foo"): "bar"}, `<< /foo (bar) >>`},
|
||||
{indirectObject{Reference{42, 0}, "foo"}, "42 0 obj\r\n(foo)\r\nendobj"},
|
||||
{Reference{42, 0}, `42 0 R`},
|
||||
{
|
||||
fooStruct{
|
||||
Size: 42,
|
||||
Params: map[name]string{name("this"): "that"},
|
||||
NotHere: "XXX",
|
||||
Rename: 3.141592,
|
||||
TheVoid: []int{1, 2, 3},
|
||||
},
|
||||
`<< /Size 42 /Params << /this (that) >> /Pi 3.14159 /TheVoid [ 1 2 3 ] >>`,
|
||||
},
|
||||
{
|
||||
fooStruct{
|
||||
Size: 42,
|
||||
Params: map[name]string{name("this"): "that"},
|
||||
NotHere: "XXX",
|
||||
Rename: 3.141592,
|
||||
TheVoid: nil,
|
||||
},
|
||||
`<< /Size 42 /Params << /this (that) >> /Pi 3.14159 >>`,
|
||||
},
|
||||
{
|
||||
Rectangle{Point{1, 2}, Point{3, 4}},
|
||||
`[ 1.00000 2.00000 3.00000 4.00000 ]`,
|
||||
},
|
||||
}
|
||||
|
||||
func TestMarshal(t *testing.T) {
|
||||
for i, tt := range marshalTests {
|
||||
result, err := marshal(nil, tt.Value)
|
||||
switch {
|
||||
case err != nil:
|
||||
t.Errorf("%d. Marshal(%#v) error: %v", i, tt.Value, err)
|
||||
case string(result) != tt.Expected:
|
||||
t.Errorf("%d. Marshal(%#v) != %q (got %q)", i, tt.Value, tt.Expected, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
package pdf
|
||||
|
||||
import (
|
||||
"compress/lzw"
|
||||
"compress/zlib"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const streamTestString = "Hello, 世界!\n"
|
||||
|
||||
func TestUnfilteredStream(t *testing.T) {
|
||||
st := newStream(streamNoFilter)
|
||||
st.WriteString(streamTestString)
|
||||
st.Close()
|
||||
|
||||
if st.String() != streamTestString {
|
||||
t.Errorf("Stream is %q, wanted %q", st.String(), streamTestString)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLZWStream(t *testing.T) {
|
||||
st := newStream(streamLZWDecode)
|
||||
st.WriteString(streamTestString)
|
||||
st.Close()
|
||||
|
||||
output, _ := ioutil.ReadAll(lzw.NewReader(st, lzw.MSB, 8))
|
||||
if string(output) != streamTestString {
|
||||
t.Errorf("Stream is %q, wanted %q", output, streamTestString)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlateStream(t *testing.T) {
|
||||
st := newStream(streamFlateDecode)
|
||||
st.WriteString(streamTestString)
|
||||
st.Close()
|
||||
|
||||
r, _ := zlib.NewReader(st)
|
||||
output, _ := ioutil.ReadAll(r)
|
||||
if string(output) != streamTestString {
|
||||
t.Errorf("Stream is %q, wanted %q", output, streamTestString)
|
||||
}
|
||||
}
|
||||
|
||||
const expectedMarshalStreamOutput = "<< /Length 15 >> stream\r\n" + streamTestString + "\r\nendstream"
|
||||
|
||||
func TestMarshalStream(t *testing.T) {
|
||||
b, err := marshalStream(nil, streamInfo{Length: len(streamTestString)}, []byte(streamTestString))
|
||||
if err == nil {
|
||||
if string(b) != expectedMarshalStreamOutput {
|
||||
t.Errorf("marshalStream(...) != %q (got %q)", expectedMarshalStreamOutput, b)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("Error: %v", err)
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 768 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
|
|
@ -0,0 +1,99 @@
|
|||
// Copyright (C) 2011, Ross Light
|
||||
|
||||
package pdf
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const textExpectedOutput = `/Helvetica 12.00000 Tf
|
||||
14.40000 TL
|
||||
14.00000 TL
|
||||
(Hello, World!) Tj
|
||||
T*
|
||||
(This is SPARTA!!1!) Tj
|
||||
`
|
||||
|
||||
func TestText(t *testing.T) {
|
||||
text := new(Text)
|
||||
text.SetFont(Helvetica, 12)
|
||||
text.SetLeading(14)
|
||||
text.Text("Hello, World!")
|
||||
text.NextLine()
|
||||
text.Text("This is SPARTA!!1!")
|
||||
|
||||
if text.buf.String() != textExpectedOutput {
|
||||
t.Errorf("Output was %q, expected %q", text.buf.String(), textExpectedOutput)
|
||||
}
|
||||
|
||||
if len(text.fonts) == 1 {
|
||||
if !text.fonts[Helvetica] {
|
||||
t.Error("Helvetica missing from fonts")
|
||||
}
|
||||
} else {
|
||||
t.Errorf("Got %d fonts, expected %d", len(text.fonts), 1)
|
||||
}
|
||||
}
|
||||
|
||||
func floatEq(a, b, epsilon float64) bool {
|
||||
return math.Abs(a-b) < epsilon
|
||||
}
|
||||
|
||||
func TestTextX(t *testing.T) {
|
||||
text := new(Text)
|
||||
if text.X() != 0 {
|
||||
t.Errorf("Text does not start at X=0 (got %.5f)", text.X())
|
||||
}
|
||||
|
||||
text.SetFont(Helvetica, 12)
|
||||
|
||||
text.Text("Hello!")
|
||||
if !floatEq(float64(text.X()), 30.672, 1e-5) {
|
||||
t.Errorf("\"Hello!\" has wrong X (=%.5f) when %.5f is desired", text.X(), 30.672)
|
||||
}
|
||||
|
||||
text.NextLine()
|
||||
if text.X() != 0 {
|
||||
t.Errorf("Performing NextLine does not reset X (got %.5f)", text.X())
|
||||
}
|
||||
|
||||
text.Text("Hello World")
|
||||
if !floatEq(float64(text.X()), 62.004, 1e-5) {
|
||||
t.Errorf("\"Hello World\" has wrong X (=%.5f) when %.5f is desired", text.X(), 62.004)
|
||||
}
|
||||
|
||||
text.NextLineOffset(41.23, 55.555)
|
||||
if !floatEq(float64(text.X()), 41.23, 1e-3) {
|
||||
t.Errorf("NextLineOffset has wrong X (=%.5f) when %.5f is desired", text.X(), 41.23)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTextY(t *testing.T) {
|
||||
text := new(Text)
|
||||
if text.Y() != 0 {
|
||||
t.Errorf("Text does not start at Y=0 (got %.5f)", text.Y())
|
||||
}
|
||||
|
||||
text.SetFont(Helvetica, 12)
|
||||
text.Text("Hello!")
|
||||
if text.Y() != 0 {
|
||||
t.Errorf("\"Hello!\" changes baseline (got %.5f)", text.Y())
|
||||
}
|
||||
|
||||
text.NextLine()
|
||||
if !floatEq(float64(text.Y()), -14.400, 1e-4) {
|
||||
t.Errorf("NextLine y = %.5f (expected %.5f)", text.Y(), -14.400)
|
||||
}
|
||||
|
||||
text.SetLeading(41.23)
|
||||
text.NextLine()
|
||||
if !floatEq(float64(text.Y()), -55.630, 1e-4) {
|
||||
t.Errorf("NextLine does not respect leading, y = %.5f (expected %.5f)", text.Y(), -55.630)
|
||||
}
|
||||
|
||||
text.NextLineOffset(1.0, 5.5)
|
||||
if !floatEq(float64(text.Y()), -50.130, 1e-4) {
|
||||
t.Errorf("NextLineOffset does not set Y correctly, y = %.5f (expected %.5f)", text.Y(), -50.130)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
package logrus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestEntryWithError(t *testing.T) {
|
||||
|
||||
assert := assert.New(t)
|
||||
|
||||
defer func() {
|
||||
ErrorKey = "error"
|
||||
}()
|
||||
|
||||
err := fmt.Errorf("kaboom at layer %d", 4711)
|
||||
|
||||
assert.Equal(err, WithError(err).Data["error"])
|
||||
|
||||
logger := New()
|
||||
logger.Out = &bytes.Buffer{}
|
||||
entry := NewEntry(logger)
|
||||
|
||||
assert.Equal(err, entry.WithError(err).Data["error"])
|
||||
|
||||
ErrorKey = "err"
|
||||
|
||||
assert.Equal(err, entry.WithError(err).Data["err"])
|
||||
|
||||
}
|
||||
|
||||
func TestEntryPanicln(t *testing.T) {
|
||||
errBoom := fmt.Errorf("boom time")
|
||||
|
||||
defer func() {
|
||||
p := recover()
|
||||
assert.NotNil(t, p)
|
||||
|
||||
switch pVal := p.(type) {
|
||||
case *Entry:
|
||||
assert.Equal(t, "kaboom", pVal.Message)
|
||||
assert.Equal(t, errBoom, pVal.Data["err"])
|
||||
default:
|
||||
t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal)
|
||||
}
|
||||
}()
|
||||
|
||||
logger := New()
|
||||
logger.Out = &bytes.Buffer{}
|
||||
entry := NewEntry(logger)
|
||||
entry.WithField("err", errBoom).Panicln("kaboom")
|
||||
}
|
||||
|
||||
func TestEntryPanicf(t *testing.T) {
|
||||
errBoom := fmt.Errorf("boom again")
|
||||
|
||||
defer func() {
|
||||
p := recover()
|
||||
assert.NotNil(t, p)
|
||||
|
||||
switch pVal := p.(type) {
|
||||
case *Entry:
|
||||
assert.Equal(t, "kaboom true", pVal.Message)
|
||||
assert.Equal(t, errBoom, pVal.Data["err"])
|
||||
default:
|
||||
t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal)
|
||||
}
|
||||
}()
|
||||
|
||||
logger := New()
|
||||
logger.Out = &bytes.Buffer{}
|
||||
entry := NewEntry(logger)
|
||||
entry.WithField("err", errBoom).Panicf("kaboom %v", true)
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
var log = logrus.New()
|
||||
|
||||
func init() {
|
||||
log.Formatter = new(logrus.JSONFormatter)
|
||||
log.Formatter = new(logrus.TextFormatter) // default
|
||||
log.Level = logrus.DebugLevel
|
||||
}
|
||||
|
||||
func main() {
|
||||
defer func() {
|
||||
err := recover()
|
||||
if err != nil {
|
||||
log.WithFields(logrus.Fields{
|
||||
"omg": true,
|
||||
"err": err,
|
||||
"number": 100,
|
||||
}).Fatal("The ice breaks!")
|
||||
}
|
||||
}()
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"animal": "walrus",
|
||||
"number": 8,
|
||||
}).Debug("Started observing beach")
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"animal": "walrus",
|
||||
"size": 10,
|
||||
}).Info("A group of walrus emerges from the ocean")
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"omg": true,
|
||||
"number": 122,
|
||||
}).Warn("The group's number increased tremendously!")
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"temperature": -4,
|
||||
}).Debug("Temperature changes")
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"animal": "orca",
|
||||
"size": 9009,
|
||||
}).Panic("It's over 9000!")
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/Sirupsen/logrus"
|
||||
"gopkg.in/gemnasium/logrus-airbrake-hook.v2"
|
||||
)
|
||||
|
||||
var log = logrus.New()
|
||||
|
||||
func init() {
|
||||
log.Formatter = new(logrus.TextFormatter) // default
|
||||
log.Hooks.Add(airbrake.NewHook(123, "xyz", "development"))
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.WithFields(logrus.Fields{
|
||||
"animal": "walrus",
|
||||
"size": 10,
|
||||
}).Info("A group of walrus emerges from the ocean")
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"omg": true,
|
||||
"number": 122,
|
||||
}).Warn("The group's number increased tremendously!")
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"omg": true,
|
||||
"number": 100,
|
||||
}).Fatal("The ice breaks!")
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
package logrus
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// smallFields is a small size data set for benchmarking
|
||||
var smallFields = Fields{
|
||||
"foo": "bar",
|
||||
"baz": "qux",
|
||||
"one": "two",
|
||||
"three": "four",
|
||||
}
|
||||
|
||||
// largeFields is a large size data set for benchmarking
|
||||
var largeFields = Fields{
|
||||
"foo": "bar",
|
||||
"baz": "qux",
|
||||
"one": "two",
|
||||
"three": "four",
|
||||
"five": "six",
|
||||
"seven": "eight",
|
||||
"nine": "ten",
|
||||
"eleven": "twelve",
|
||||
"thirteen": "fourteen",
|
||||
"fifteen": "sixteen",
|
||||
"seventeen": "eighteen",
|
||||
"nineteen": "twenty",
|
||||
"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",
|
||||
"this": "will",
|
||||
"make": "thirty",
|
||||
"entries": "yeah",
|
||||
}
|
||||
|
||||
var errorFields = Fields{
|
||||
"foo": fmt.Errorf("bar"),
|
||||
"baz": fmt.Errorf("qux"),
|
||||
}
|
||||
|
||||
func BenchmarkErrorTextFormatter(b *testing.B) {
|
||||
doBenchmark(b, &TextFormatter{DisableColors: true}, errorFields)
|
||||
}
|
||||
|
||||
func BenchmarkSmallTextFormatter(b *testing.B) {
|
||||
doBenchmark(b, &TextFormatter{DisableColors: true}, smallFields)
|
||||
}
|
||||
|
||||
func BenchmarkLargeTextFormatter(b *testing.B) {
|
||||
doBenchmark(b, &TextFormatter{DisableColors: true}, largeFields)
|
||||
}
|
||||
|
||||
func BenchmarkSmallColoredTextFormatter(b *testing.B) {
|
||||
doBenchmark(b, &TextFormatter{ForceColors: true}, smallFields)
|
||||
}
|
||||
|
||||
func BenchmarkLargeColoredTextFormatter(b *testing.B) {
|
||||
doBenchmark(b, &TextFormatter{ForceColors: true}, largeFields)
|
||||
}
|
||||
|
||||
func BenchmarkSmallJSONFormatter(b *testing.B) {
|
||||
doBenchmark(b, &JSONFormatter{}, smallFields)
|
||||
}
|
||||
|
||||
func BenchmarkLargeJSONFormatter(b *testing.B) {
|
||||
doBenchmark(b, &JSONFormatter{}, largeFields)
|
||||
}
|
||||
|
||||
func doBenchmark(b *testing.B, formatter Formatter, fields Fields) {
|
||||
entry := &Entry{
|
||||
Time: time.Time{},
|
||||
Level: InfoLevel,
|
||||
Message: "message",
|
||||
Data: fields,
|
||||
}
|
||||
var d []byte
|
||||
var err error
|
||||
for i := 0; i < b.N; i++ {
|
||||
d, err = formatter.Format(entry)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
b.SetBytes(int64(len(d)))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
package logstash
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Formatter generates json in logstash format.
|
||||
// Logstash site: http://logstash.net/
|
||||
type LogstashFormatter struct {
|
||||
Type string // if not empty use for logstash type field.
|
||||
|
||||
// TimestampFormat sets the format used for timestamps.
|
||||
TimestampFormat string
|
||||
}
|
||||
|
||||
func (f *LogstashFormatter) Format(entry *logrus.Entry) ([]byte, error) {
|
||||
fields := make(logrus.Fields)
|
||||
for k, v := range entry.Data {
|
||||
fields[k] = v
|
||||
}
|
||||
|
||||
fields["@version"] = 1
|
||||
|
||||
timeStampFormat := f.TimestampFormat
|
||||
|
||||
if timeStampFormat == "" {
|
||||
timeStampFormat = logrus.DefaultTimestampFormat
|
||||
}
|
||||
|
||||
fields["@timestamp"] = entry.Time.Format(timeStampFormat)
|
||||
|
||||
// set message field
|
||||
v, ok := entry.Data["message"]
|
||||
if ok {
|
||||
fields["fields.message"] = v
|
||||
}
|
||||
fields["message"] = entry.Message
|
||||
|
||||
// set level field
|
||||
v, ok = entry.Data["level"]
|
||||
if ok {
|
||||
fields["fields.level"] = v
|
||||
}
|
||||
fields["level"] = entry.Level.String()
|
||||
|
||||
// set type field
|
||||
if f.Type != "" {
|
||||
v, ok = entry.Data["type"]
|
||||
if ok {
|
||||
fields["fields.type"] = v
|
||||
}
|
||||
fields["type"] = f.Type
|
||||
}
|
||||
|
||||
serialized, err := json.Marshal(fields)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
|
||||
}
|
||||
return append(serialized, '\n'), nil
|
||||
}
|
||||
52
vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash_test.go
generated
vendored
Normal file
52
vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
package logstash
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLogstashFormatter(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
lf := LogstashFormatter{Type: "abc"}
|
||||
|
||||
fields := logrus.Fields{
|
||||
"message": "def",
|
||||
"level": "ijk",
|
||||
"type": "lmn",
|
||||
"one": 1,
|
||||
"pi": 3.14,
|
||||
"bool": true,
|
||||
}
|
||||
|
||||
entry := logrus.WithFields(fields)
|
||||
entry.Message = "msg"
|
||||
entry.Level = logrus.InfoLevel
|
||||
|
||||
b, _ := lf.Format(entry)
|
||||
|
||||
var data map[string]interface{}
|
||||
dec := json.NewDecoder(bytes.NewReader(b))
|
||||
dec.UseNumber()
|
||||
dec.Decode(&data)
|
||||
|
||||
// base fields
|
||||
assert.Equal(json.Number("1"), data["@version"])
|
||||
assert.NotEmpty(data["@timestamp"])
|
||||
assert.Equal("abc", data["type"])
|
||||
assert.Equal("msg", data["message"])
|
||||
assert.Equal("info", data["level"])
|
||||
|
||||
// substituted fields
|
||||
assert.Equal("def", data["fields.message"])
|
||||
assert.Equal("ijk", data["fields.level"])
|
||||
assert.Equal("lmn", data["fields.type"])
|
||||
|
||||
// formats
|
||||
assert.Equal(json.Number("1"), data["one"])
|
||||
assert.Equal(json.Number("3.14"), data["pi"])
|
||||
assert.Equal(true, data["bool"])
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
package logrus
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type TestHook struct {
|
||||
Fired bool
|
||||
}
|
||||
|
||||
func (hook *TestHook) Fire(entry *Entry) error {
|
||||
hook.Fired = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hook *TestHook) Levels() []Level {
|
||||
return []Level{
|
||||
DebugLevel,
|
||||
InfoLevel,
|
||||
WarnLevel,
|
||||
ErrorLevel,
|
||||
FatalLevel,
|
||||
PanicLevel,
|
||||
}
|
||||
}
|
||||
|
||||
func TestHookFires(t *testing.T) {
|
||||
hook := new(TestHook)
|
||||
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Hooks.Add(hook)
|
||||
assert.Equal(t, hook.Fired, false)
|
||||
|
||||
log.Print("test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, hook.Fired, true)
|
||||
})
|
||||
}
|
||||
|
||||
type ModifyHook struct {
|
||||
}
|
||||
|
||||
func (hook *ModifyHook) Fire(entry *Entry) error {
|
||||
entry.Data["wow"] = "whale"
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hook *ModifyHook) Levels() []Level {
|
||||
return []Level{
|
||||
DebugLevel,
|
||||
InfoLevel,
|
||||
WarnLevel,
|
||||
ErrorLevel,
|
||||
FatalLevel,
|
||||
PanicLevel,
|
||||
}
|
||||
}
|
||||
|
||||
func TestHookCanModifyEntry(t *testing.T) {
|
||||
hook := new(ModifyHook)
|
||||
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Hooks.Add(hook)
|
||||
log.WithField("wow", "elephant").Print("test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, fields["wow"], "whale")
|
||||
})
|
||||
}
|
||||
|
||||
func TestCanFireMultipleHooks(t *testing.T) {
|
||||
hook1 := new(ModifyHook)
|
||||
hook2 := new(TestHook)
|
||||
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Hooks.Add(hook1)
|
||||
log.Hooks.Add(hook2)
|
||||
|
||||
log.WithField("wow", "elephant").Print("test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, fields["wow"], "whale")
|
||||
assert.Equal(t, hook2.Fired, true)
|
||||
})
|
||||
}
|
||||
|
||||
type ErrorHook struct {
|
||||
Fired bool
|
||||
}
|
||||
|
||||
func (hook *ErrorHook) Fire(entry *Entry) error {
|
||||
hook.Fired = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hook *ErrorHook) Levels() []Level {
|
||||
return []Level{
|
||||
ErrorLevel,
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorHookShouldntFireOnInfo(t *testing.T) {
|
||||
hook := new(ErrorHook)
|
||||
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Hooks.Add(hook)
|
||||
log.Info("test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, hook.Fired, false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestErrorHookShouldFireOnError(t *testing.T) {
|
||||
hook := new(ErrorHook)
|
||||
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Hooks.Add(hook)
|
||||
log.Error("test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, hook.Fired, true)
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
# Syslog Hooks for Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/>
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
import (
|
||||
"log/syslog"
|
||||
"github.com/Sirupsen/logrus"
|
||||
logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log := logrus.New()
|
||||
hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
|
||||
|
||||
if err == nil {
|
||||
log.Hooks.Add(hook)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you want to connect to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). Just assign empty string to the first two parameters of `NewSyslogHook`. It should look like the following.
|
||||
|
||||
```go
|
||||
import (
|
||||
"log/syslog"
|
||||
"github.com/Sirupsen/logrus"
|
||||
logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log := logrus.New()
|
||||
hook, err := logrus_syslog.NewSyslogHook("", "", syslog.LOG_INFO, "")
|
||||
|
||||
if err == nil {
|
||||
log.Hooks.Add(hook)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// +build !windows,!nacl,!plan9
|
||||
|
||||
package logrus_syslog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Sirupsen/logrus"
|
||||
"log/syslog"
|
||||
"os"
|
||||
)
|
||||
|
||||
// SyslogHook to send logs via syslog.
|
||||
type SyslogHook struct {
|
||||
Writer *syslog.Writer
|
||||
SyslogNetwork string
|
||||
SyslogRaddr string
|
||||
}
|
||||
|
||||
// Creates a hook to be added to an instance of logger. This is called with
|
||||
// `hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_DEBUG, "")`
|
||||
// `if err == nil { log.Hooks.Add(hook) }`
|
||||
func NewSyslogHook(network, raddr string, priority syslog.Priority, tag string) (*SyslogHook, error) {
|
||||
w, err := syslog.Dial(network, raddr, priority, tag)
|
||||
return &SyslogHook{w, network, raddr}, err
|
||||
}
|
||||
|
||||
func (hook *SyslogHook) Fire(entry *logrus.Entry) error {
|
||||
line, err := entry.String()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Unable to read entry, %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
switch entry.Level {
|
||||
case logrus.PanicLevel:
|
||||
return hook.Writer.Crit(line)
|
||||
case logrus.FatalLevel:
|
||||
return hook.Writer.Crit(line)
|
||||
case logrus.ErrorLevel:
|
||||
return hook.Writer.Err(line)
|
||||
case logrus.WarnLevel:
|
||||
return hook.Writer.Warning(line)
|
||||
case logrus.InfoLevel:
|
||||
return hook.Writer.Info(line)
|
||||
case logrus.DebugLevel:
|
||||
return hook.Writer.Debug(line)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (hook *SyslogHook) Levels() []logrus.Level {
|
||||
return logrus.AllLevels
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package logrus_syslog
|
||||
|
||||
import (
|
||||
"github.com/Sirupsen/logrus"
|
||||
"log/syslog"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLocalhostAddAndPrint(t *testing.T) {
|
||||
log := logrus.New()
|
||||
hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Unable to connect to local syslog.")
|
||||
}
|
||||
|
||||
log.Hooks.Add(hook)
|
||||
|
||||
for _, level := range hook.Levels() {
|
||||
if len(log.Hooks[level]) != 1 {
|
||||
t.Errorf("SyslogHook was not added. The length of log.Hooks[%v]: %v", level, len(log.Hooks[level]))
|
||||
}
|
||||
}
|
||||
|
||||
log.Info("Congratulations!")
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
// test.Hook is a hook designed for dealing with logs in test scenarios.
|
||||
type Hook struct {
|
||||
Entries []*logrus.Entry
|
||||
}
|
||||
|
||||
// Installs a test hook for the global logger.
|
||||
func NewGlobal() *Hook {
|
||||
|
||||
hook := new(Hook)
|
||||
logrus.AddHook(hook)
|
||||
|
||||
return hook
|
||||
|
||||
}
|
||||
|
||||
// Installs a test hook for a given local logger.
|
||||
func NewLocal(logger *logrus.Logger) *Hook {
|
||||
|
||||
hook := new(Hook)
|
||||
logger.Hooks.Add(hook)
|
||||
|
||||
return hook
|
||||
|
||||
}
|
||||
|
||||
// Creates a discarding logger and installs the test hook.
|
||||
func NewNullLogger() (*logrus.Logger, *Hook) {
|
||||
|
||||
logger := logrus.New()
|
||||
logger.Out = ioutil.Discard
|
||||
|
||||
return logger, NewLocal(logger)
|
||||
|
||||
}
|
||||
|
||||
func (t *Hook) Fire(e *logrus.Entry) error {
|
||||
t.Entries = append(t.Entries, e)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Hook) Levels() []logrus.Level {
|
||||
return logrus.AllLevels
|
||||
}
|
||||
|
||||
// LastEntry returns the last entry that was logged or nil.
|
||||
func (t *Hook) LastEntry() (l *logrus.Entry) {
|
||||
|
||||
if i := len(t.Entries) - 1; i < 0 {
|
||||
return nil
|
||||
} else {
|
||||
return t.Entries[i]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Reset removes all Entries from this test hook.
|
||||
func (t *Hook) Reset() {
|
||||
t.Entries = make([]*logrus.Entry, 0)
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAllHooks(t *testing.T) {
|
||||
|
||||
assert := assert.New(t)
|
||||
|
||||
logger, hook := NewNullLogger()
|
||||
assert.Nil(hook.LastEntry())
|
||||
assert.Equal(0, len(hook.Entries))
|
||||
|
||||
logger.Error("Hello error")
|
||||
assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level)
|
||||
assert.Equal("Hello error", hook.LastEntry().Message)
|
||||
assert.Equal(1, len(hook.Entries))
|
||||
|
||||
logger.Warn("Hello warning")
|
||||
assert.Equal(logrus.WarnLevel, hook.LastEntry().Level)
|
||||
assert.Equal("Hello warning", hook.LastEntry().Message)
|
||||
assert.Equal(2, len(hook.Entries))
|
||||
|
||||
hook.Reset()
|
||||
assert.Nil(hook.LastEntry())
|
||||
assert.Equal(0, len(hook.Entries))
|
||||
|
||||
hook = NewGlobal()
|
||||
|
||||
logrus.Error("Hello error")
|
||||
assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level)
|
||||
assert.Equal("Hello error", hook.LastEntry().Message)
|
||||
assert.Equal(1, len(hook.Entries))
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
package logrus
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestErrorNotLost(t *testing.T) {
|
||||
formatter := &JSONFormatter{}
|
||||
|
||||
b, err := formatter.Format(WithField("error", errors.New("wild walrus")))
|
||||
if err != nil {
|
||||
t.Fatal("Unable to format entry: ", err)
|
||||
}
|
||||
|
||||
entry := make(map[string]interface{})
|
||||
err = json.Unmarshal(b, &entry)
|
||||
if err != nil {
|
||||
t.Fatal("Unable to unmarshal formatted entry: ", err)
|
||||
}
|
||||
|
||||
if entry["error"] != "wild walrus" {
|
||||
t.Fatal("Error field not set")
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorNotLostOnFieldNotNamedError(t *testing.T) {
|
||||
formatter := &JSONFormatter{}
|
||||
|
||||
b, err := formatter.Format(WithField("omg", errors.New("wild walrus")))
|
||||
if err != nil {
|
||||
t.Fatal("Unable to format entry: ", err)
|
||||
}
|
||||
|
||||
entry := make(map[string]interface{})
|
||||
err = json.Unmarshal(b, &entry)
|
||||
if err != nil {
|
||||
t.Fatal("Unable to unmarshal formatted entry: ", err)
|
||||
}
|
||||
|
||||
if entry["omg"] != "wild walrus" {
|
||||
t.Fatal("Error field not set")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFieldClashWithTime(t *testing.T) {
|
||||
formatter := &JSONFormatter{}
|
||||
|
||||
b, err := formatter.Format(WithField("time", "right now!"))
|
||||
if err != nil {
|
||||
t.Fatal("Unable to format entry: ", err)
|
||||
}
|
||||
|
||||
entry := make(map[string]interface{})
|
||||
err = json.Unmarshal(b, &entry)
|
||||
if err != nil {
|
||||
t.Fatal("Unable to unmarshal formatted entry: ", err)
|
||||
}
|
||||
|
||||
if entry["fields.time"] != "right now!" {
|
||||
t.Fatal("fields.time not set to original time field")
|
||||
}
|
||||
|
||||
if entry["time"] != "0001-01-01T00:00:00Z" {
|
||||
t.Fatal("time field not set to current time, was: ", entry["time"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestFieldClashWithMsg(t *testing.T) {
|
||||
formatter := &JSONFormatter{}
|
||||
|
||||
b, err := formatter.Format(WithField("msg", "something"))
|
||||
if err != nil {
|
||||
t.Fatal("Unable to format entry: ", err)
|
||||
}
|
||||
|
||||
entry := make(map[string]interface{})
|
||||
err = json.Unmarshal(b, &entry)
|
||||
if err != nil {
|
||||
t.Fatal("Unable to unmarshal formatted entry: ", err)
|
||||
}
|
||||
|
||||
if entry["fields.msg"] != "something" {
|
||||
t.Fatal("fields.msg not set to original msg field")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFieldClashWithLevel(t *testing.T) {
|
||||
formatter := &JSONFormatter{}
|
||||
|
||||
b, err := formatter.Format(WithField("level", "something"))
|
||||
if err != nil {
|
||||
t.Fatal("Unable to format entry: ", err)
|
||||
}
|
||||
|
||||
entry := make(map[string]interface{})
|
||||
err = json.Unmarshal(b, &entry)
|
||||
if err != nil {
|
||||
t.Fatal("Unable to unmarshal formatted entry: ", err)
|
||||
}
|
||||
|
||||
if entry["fields.level"] != "something" {
|
||||
t.Fatal("fields.level not set to original level field")
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONEntryEndsWithNewline(t *testing.T) {
|
||||
formatter := &JSONFormatter{}
|
||||
|
||||
b, err := formatter.Format(WithField("level", "something"))
|
||||
if err != nil {
|
||||
t.Fatal("Unable to format entry: ", err)
|
||||
}
|
||||
|
||||
if b[len(b)-1] != '\n' {
|
||||
t.Fatal("Expected JSON log entry to end with a newline")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,361 @@
|
|||
package logrus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func LogAndAssertJSON(t *testing.T, log func(*Logger), assertions func(fields Fields)) {
|
||||
var buffer bytes.Buffer
|
||||
var fields Fields
|
||||
|
||||
logger := New()
|
||||
logger.Out = &buffer
|
||||
logger.Formatter = new(JSONFormatter)
|
||||
|
||||
log(logger)
|
||||
|
||||
err := json.Unmarshal(buffer.Bytes(), &fields)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assertions(fields)
|
||||
}
|
||||
|
||||
func LogAndAssertText(t *testing.T, log func(*Logger), assertions func(fields map[string]string)) {
|
||||
var buffer bytes.Buffer
|
||||
|
||||
logger := New()
|
||||
logger.Out = &buffer
|
||||
logger.Formatter = &TextFormatter{
|
||||
DisableColors: true,
|
||||
}
|
||||
|
||||
log(logger)
|
||||
|
||||
fields := make(map[string]string)
|
||||
for _, kv := range strings.Split(buffer.String(), " ") {
|
||||
if !strings.Contains(kv, "=") {
|
||||
continue
|
||||
}
|
||||
kvArr := strings.Split(kv, "=")
|
||||
key := strings.TrimSpace(kvArr[0])
|
||||
val := kvArr[1]
|
||||
if kvArr[1][0] == '"' {
|
||||
var err error
|
||||
val, err = strconv.Unquote(val)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
fields[key] = val
|
||||
}
|
||||
assertions(fields)
|
||||
}
|
||||
|
||||
func TestPrint(t *testing.T) {
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Print("test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, fields["msg"], "test")
|
||||
assert.Equal(t, fields["level"], "info")
|
||||
})
|
||||
}
|
||||
|
||||
func TestInfo(t *testing.T) {
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Info("test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, fields["msg"], "test")
|
||||
assert.Equal(t, fields["level"], "info")
|
||||
})
|
||||
}
|
||||
|
||||
func TestWarn(t *testing.T) {
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Warn("test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, fields["msg"], "test")
|
||||
assert.Equal(t, fields["level"], "warning")
|
||||
})
|
||||
}
|
||||
|
||||
func TestInfolnShouldAddSpacesBetweenStrings(t *testing.T) {
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Infoln("test", "test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, fields["msg"], "test test")
|
||||
})
|
||||
}
|
||||
|
||||
func TestInfolnShouldAddSpacesBetweenStringAndNonstring(t *testing.T) {
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Infoln("test", 10)
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, fields["msg"], "test 10")
|
||||
})
|
||||
}
|
||||
|
||||
func TestInfolnShouldAddSpacesBetweenTwoNonStrings(t *testing.T) {
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Infoln(10, 10)
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, fields["msg"], "10 10")
|
||||
})
|
||||
}
|
||||
|
||||
func TestInfoShouldAddSpacesBetweenTwoNonStrings(t *testing.T) {
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Infoln(10, 10)
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, fields["msg"], "10 10")
|
||||
})
|
||||
}
|
||||
|
||||
func TestInfoShouldNotAddSpacesBetweenStringAndNonstring(t *testing.T) {
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Info("test", 10)
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, fields["msg"], "test10")
|
||||
})
|
||||
}
|
||||
|
||||
func TestInfoShouldNotAddSpacesBetweenStrings(t *testing.T) {
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Info("test", "test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, fields["msg"], "testtest")
|
||||
})
|
||||
}
|
||||
|
||||
func TestWithFieldsShouldAllowAssignments(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
var fields Fields
|
||||
|
||||
logger := New()
|
||||
logger.Out = &buffer
|
||||
logger.Formatter = new(JSONFormatter)
|
||||
|
||||
localLog := logger.WithFields(Fields{
|
||||
"key1": "value1",
|
||||
})
|
||||
|
||||
localLog.WithField("key2", "value2").Info("test")
|
||||
err := json.Unmarshal(buffer.Bytes(), &fields)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, "value2", fields["key2"])
|
||||
assert.Equal(t, "value1", fields["key1"])
|
||||
|
||||
buffer = bytes.Buffer{}
|
||||
fields = Fields{}
|
||||
localLog.Info("test")
|
||||
err = json.Unmarshal(buffer.Bytes(), &fields)
|
||||
assert.Nil(t, err)
|
||||
|
||||
_, ok := fields["key2"]
|
||||
assert.Equal(t, false, ok)
|
||||
assert.Equal(t, "value1", fields["key1"])
|
||||
}
|
||||
|
||||
func TestUserSuppliedFieldDoesNotOverwriteDefaults(t *testing.T) {
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.WithField("msg", "hello").Info("test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, fields["msg"], "test")
|
||||
})
|
||||
}
|
||||
|
||||
func TestUserSuppliedMsgFieldHasPrefix(t *testing.T) {
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.WithField("msg", "hello").Info("test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, fields["msg"], "test")
|
||||
assert.Equal(t, fields["fields.msg"], "hello")
|
||||
})
|
||||
}
|
||||
|
||||
func TestUserSuppliedTimeFieldHasPrefix(t *testing.T) {
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.WithField("time", "hello").Info("test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, fields["fields.time"], "hello")
|
||||
})
|
||||
}
|
||||
|
||||
func TestUserSuppliedLevelFieldHasPrefix(t *testing.T) {
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.WithField("level", 1).Info("test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, fields["level"], "info")
|
||||
assert.Equal(t, fields["fields.level"], 1.0) // JSON has floats only
|
||||
})
|
||||
}
|
||||
|
||||
func TestDefaultFieldsAreNotPrefixed(t *testing.T) {
|
||||
LogAndAssertText(t, func(log *Logger) {
|
||||
ll := log.WithField("herp", "derp")
|
||||
ll.Info("hello")
|
||||
ll.Info("bye")
|
||||
}, func(fields map[string]string) {
|
||||
for _, fieldName := range []string{"fields.level", "fields.time", "fields.msg"} {
|
||||
if _, ok := fields[fieldName]; ok {
|
||||
t.Fatalf("should not have prefixed %q: %v", fieldName, fields)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestDoubleLoggingDoesntPrefixPreviousFields(t *testing.T) {
|
||||
|
||||
var buffer bytes.Buffer
|
||||
var fields Fields
|
||||
|
||||
logger := New()
|
||||
logger.Out = &buffer
|
||||
logger.Formatter = new(JSONFormatter)
|
||||
|
||||
llog := logger.WithField("context", "eating raw fish")
|
||||
|
||||
llog.Info("looks delicious")
|
||||
|
||||
err := json.Unmarshal(buffer.Bytes(), &fields)
|
||||
assert.NoError(t, err, "should have decoded first message")
|
||||
assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields")
|
||||
assert.Equal(t, fields["msg"], "looks delicious")
|
||||
assert.Equal(t, fields["context"], "eating raw fish")
|
||||
|
||||
buffer.Reset()
|
||||
|
||||
llog.Warn("omg it is!")
|
||||
|
||||
err = json.Unmarshal(buffer.Bytes(), &fields)
|
||||
assert.NoError(t, err, "should have decoded second message")
|
||||
assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields")
|
||||
assert.Equal(t, fields["msg"], "omg it is!")
|
||||
assert.Equal(t, fields["context"], "eating raw fish")
|
||||
assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry")
|
||||
|
||||
}
|
||||
|
||||
func TestConvertLevelToString(t *testing.T) {
|
||||
assert.Equal(t, "debug", DebugLevel.String())
|
||||
assert.Equal(t, "info", InfoLevel.String())
|
||||
assert.Equal(t, "warning", WarnLevel.String())
|
||||
assert.Equal(t, "error", ErrorLevel.String())
|
||||
assert.Equal(t, "fatal", FatalLevel.String())
|
||||
assert.Equal(t, "panic", PanicLevel.String())
|
||||
}
|
||||
|
||||
func TestParseLevel(t *testing.T) {
|
||||
l, err := ParseLevel("panic")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, PanicLevel, l)
|
||||
|
||||
l, err = ParseLevel("PANIC")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, PanicLevel, l)
|
||||
|
||||
l, err = ParseLevel("fatal")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, FatalLevel, l)
|
||||
|
||||
l, err = ParseLevel("FATAL")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, FatalLevel, l)
|
||||
|
||||
l, err = ParseLevel("error")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, ErrorLevel, l)
|
||||
|
||||
l, err = ParseLevel("ERROR")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, ErrorLevel, l)
|
||||
|
||||
l, err = ParseLevel("warn")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, WarnLevel, l)
|
||||
|
||||
l, err = ParseLevel("WARN")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, WarnLevel, l)
|
||||
|
||||
l, err = ParseLevel("warning")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, WarnLevel, l)
|
||||
|
||||
l, err = ParseLevel("WARNING")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, WarnLevel, l)
|
||||
|
||||
l, err = ParseLevel("info")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, InfoLevel, l)
|
||||
|
||||
l, err = ParseLevel("INFO")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, InfoLevel, l)
|
||||
|
||||
l, err = ParseLevel("debug")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, DebugLevel, l)
|
||||
|
||||
l, err = ParseLevel("DEBUG")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, DebugLevel, l)
|
||||
|
||||
l, err = ParseLevel("invalid")
|
||||
assert.Equal(t, "not a valid logrus Level: \"invalid\"", err.Error())
|
||||
}
|
||||
|
||||
func TestGetSetLevelRace(t *testing.T) {
|
||||
wg := sync.WaitGroup{}
|
||||
for i := 0; i < 100; i++ {
|
||||
wg.Add(1)
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
if i%2 == 0 {
|
||||
SetLevel(InfoLevel)
|
||||
} else {
|
||||
GetLevel()
|
||||
}
|
||||
}(i)
|
||||
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestLoggingRace(t *testing.T) {
|
||||
logger := New()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(100)
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
go func() {
|
||||
logger.Info("info")
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// Compile test
|
||||
func TestLogrusInterface(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
fn := func(l FieldLogger) {
|
||||
b := l.WithField("key", "value")
|
||||
b.Debug("Test")
|
||||
}
|
||||
// test logger
|
||||
logger := New()
|
||||
logger.Out = &buffer
|
||||
fn(logger)
|
||||
|
||||
// test Entry
|
||||
e := logger.WithField("another", "value")
|
||||
fn(e)
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
package logrus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestQuoting(t *testing.T) {
|
||||
tf := &TextFormatter{DisableColors: true}
|
||||
|
||||
checkQuoting := func(q bool, value interface{}) {
|
||||
b, _ := tf.Format(WithField("test", value))
|
||||
idx := bytes.Index(b, ([]byte)("test="))
|
||||
cont := bytes.Contains(b[idx+5:], []byte{'"'})
|
||||
if cont != q {
|
||||
if q {
|
||||
t.Errorf("quoting expected for: %#v", value)
|
||||
} else {
|
||||
t.Errorf("quoting not expected for: %#v", value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkQuoting(false, "abcd")
|
||||
checkQuoting(false, "v1.0")
|
||||
checkQuoting(false, "1234567890")
|
||||
checkQuoting(true, "/foobar")
|
||||
checkQuoting(true, "x y")
|
||||
checkQuoting(true, "x,y")
|
||||
checkQuoting(false, errors.New("invalid"))
|
||||
checkQuoting(true, errors.New("invalid argument"))
|
||||
}
|
||||
|
||||
func TestTimestampFormat(t *testing.T) {
|
||||
checkTimeStr := func(format string) {
|
||||
customFormatter := &TextFormatter{DisableColors: true, TimestampFormat: format}
|
||||
customStr, _ := customFormatter.Format(WithField("test", "test"))
|
||||
timeStart := bytes.Index(customStr, ([]byte)("time="))
|
||||
timeEnd := bytes.Index(customStr, ([]byte)("level="))
|
||||
timeStr := customStr[timeStart+5 : timeEnd-1]
|
||||
if timeStr[0] == '"' && timeStr[len(timeStr)-1] == '"' {
|
||||
timeStr = timeStr[1 : len(timeStr)-1]
|
||||
}
|
||||
if format == "" {
|
||||
format = time.RFC3339
|
||||
}
|
||||
_, e := time.Parse(format, (string)(timeStr))
|
||||
if e != nil {
|
||||
t.Errorf("time string \"%s\" did not match provided time format \"%s\": %s", timeStr, format, e)
|
||||
}
|
||||
}
|
||||
|
||||
checkTimeStr("2006-01-02T15:04:05.000000000Z07:00")
|
||||
checkTimeStr("Mon Jan _2 15:04:05 2006")
|
||||
checkTimeStr("")
|
||||
}
|
||||
|
||||
// TODO add tests for sorting etc., this requires a parser for the text
|
||||
// formatter output.
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
// android draws bugdroid, the Android mascot
|
||||
// +build !appengine
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/ajstarks/svgo"
|
||||
)
|
||||
|
||||
var (
|
||||
width = 500
|
||||
height = 500
|
||||
canvas = svg.New(os.Stdout)
|
||||
)
|
||||
|
||||
const androidcolor = "rgb(164,198,57)"
|
||||
|
||||
func background(v int) { canvas.Rect(0, 0, width, height, canvas.RGB(v, v, v)) }
|
||||
|
||||
func android(x, y int, fill string, opacity float64) {
|
||||
var linestyle = []string{`stroke="` + fill + `"`, `stroke-linecap="round"`, `stroke-width="5"`}
|
||||
globalstyle := fmt.Sprintf("fill:%s;opacity:%.2f", fill, opacity)
|
||||
canvas.Gstyle(globalstyle)
|
||||
canvas.Arc(x+30, y+70, 35, 35, 0, false, true, x+130, y+70) // head
|
||||
canvas.Line(x+60, y+25, x+50, y+10, linestyle[0], linestyle[1], linestyle[2]) // left antenna
|
||||
canvas.Line(x+100, y+25, x+110, y+10, linestyle[0], linestyle[1], linestyle[2]) // right antenna
|
||||
canvas.Circle(x+60, y+45, 5, "fill:white") // left eye
|
||||
canvas.Circle(x+100, y+45, 5, `fill="white"`) // right eye
|
||||
canvas.Roundrect(x+30, y+75, 100, 90, 10, 10) // body
|
||||
canvas.Rect(x+30, y+75, 100, 80)
|
||||
canvas.Roundrect(x+5, y+80, 20, 70, 10, 10) // left arm
|
||||
canvas.Roundrect(x+135, y+80, 20, 70, 10, 10) // right arm
|
||||
canvas.Roundrect(x+50, y+150, 20, 50, 10, 10) // left leg
|
||||
canvas.Roundrect(x+90, y+150, 20, 50, 10, 10) // right leg
|
||||
canvas.Gend()
|
||||
}
|
||||
|
||||
func main() {
|
||||
canvas.Start(width, height)
|
||||
canvas.Title("Android")
|
||||
background(255)
|
||||
|
||||
android(100, 100, androidcolor, 1.0)
|
||||
canvas.Scale(3.0)
|
||||
android(50, 50, "gray", 0.5)
|
||||
canvas.Gend()
|
||||
|
||||
canvas.Scale(0.5)
|
||||
android(100, 100, "red", 1.0)
|
||||
canvas.Gend()
|
||||
canvas.End()
|
||||
}
|
||||
|
|
@ -0,0 +1,472 @@
|
|||
// barchart - bar chart
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/ajstarks/svgo"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
width, height, iscale, fontsize, barheight, gutter, cornerRadius, labelimit int
|
||||
bgcolor, barcolor, title, inbar, valformat string
|
||||
showtitle, showdata, showgrid, showscale, endtitle, trace bool
|
||||
)
|
||||
|
||||
const (
|
||||
gstyle = "font-family:Calibri,sans-serif;font-size:%dpx"
|
||||
borderstyle = "stroke:lightgray;stroke-width:1px"
|
||||
scalestyle = "text-anchor:middle;font-size:75%"
|
||||
btitlestyle = "font-style:italic;font-size:150%;text-anchor:"
|
||||
notestyle = "font-style:italic;text-anchor:"
|
||||
datastyle = "text-anchor:end;fill:"
|
||||
titlestyle = "text-anchor:start;font-size:300%"
|
||||
labelstyle = "fill:black;baseline-shift:-25%"
|
||||
)
|
||||
|
||||
// a Barchart Defintion
|
||||
// <barchart title="Bullet Graph" top="50" left="250" right="50">
|
||||
// <note>This is a note</note>
|
||||
// <note>More expository text</note>
|
||||
// <bdata title="Browser Market Share" scale="0,100,20" showdata="true" color="red" unit="%"/>
|
||||
// <bitem name="Firefox" value="22.5" color="green"/>
|
||||
// <bitem name="Chrome" value="12.3"/>
|
||||
// <bitem name="IE8" value="63.5"/>
|
||||
// <bdata>
|
||||
// </barchart>
|
||||
|
||||
type Barchart struct {
|
||||
Top int `xml:"top,attr"`
|
||||
Left int `xml:"left,attr"`
|
||||
Right int `xml:"right,attr"`
|
||||
Title string `xml:"title,attr"`
|
||||
Bdata []bdata `xml:"bdata"`
|
||||
Note []note `xml:"note"`
|
||||
}
|
||||
|
||||
type bdata struct {
|
||||
Title string `xml:"title,attr"`
|
||||
Scale string `xml:"scale,attr"`
|
||||
Color string `xml:"color,attr"`
|
||||
Unit string `xml:"unit,attr"`
|
||||
Showdata bool `xml:"showdata,attr"`
|
||||
Showgrid bool `xml:"showgrid,attr"`
|
||||
Samebar bool `xml:"samebar,attr"`
|
||||
Bitem []bitem `xml:"bitem"`
|
||||
Bstack []bstack `xml:"bstack"`
|
||||
Note []note `xml:"note"`
|
||||
}
|
||||
|
||||
type bitem struct {
|
||||
Name string `xml:"name,attr"`
|
||||
Value float64 `xml:"value,attr"`
|
||||
Color string `xml:"color,attr"`
|
||||
Samebar bool `xml:"samebar,attr"`
|
||||
}
|
||||
|
||||
type bstack struct {
|
||||
Name string `xml:"name,attr"`
|
||||
Value string `xml:"value,attr"`
|
||||
Color string `xml:"color,attr"`
|
||||
}
|
||||
|
||||
type note struct {
|
||||
Text string `xml:",chardata"`
|
||||
}
|
||||
|
||||
// dobc does file i/o
|
||||
func dobc(location string, s *svg.SVG) {
|
||||
var f *os.File
|
||||
var err error
|
||||
if len(location) > 0 {
|
||||
f, err = os.Open(location)
|
||||
} else {
|
||||
f = os.Stdin
|
||||
}
|
||||
if err == nil {
|
||||
readbc(f, s)
|
||||
f.Close()
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// readbc reads and parses the XML specification
|
||||
func readbc(r io.Reader, s *svg.SVG) {
|
||||
var bc Barchart
|
||||
if err := xml.NewDecoder(r).Decode(&bc); err == nil {
|
||||
drawbc(bc, s)
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// drawbc draws the bar chart
|
||||
func drawbc(bg Barchart, canvas *svg.SVG) {
|
||||
|
||||
if bg.Left == 0 {
|
||||
bg.Left = 250
|
||||
}
|
||||
if bg.Right == 0 {
|
||||
bg.Right = 50
|
||||
}
|
||||
if bg.Top == 0 {
|
||||
bg.Top = 50
|
||||
}
|
||||
if len(title) > 0 {
|
||||
bg.Title = title
|
||||
}
|
||||
labelimit = bg.Left/8
|
||||
cr := cornerRadius
|
||||
maxwidth := width - (bg.Left + bg.Right)
|
||||
x := bg.Left
|
||||
y := bg.Top
|
||||
sep := 10
|
||||
color := barcolor
|
||||
scfmt := "%v"
|
||||
canvas.Title(bg.Title)
|
||||
|
||||
// for each bdata element...
|
||||
for _, b := range bg.Bdata {
|
||||
if trace {
|
||||
fmt.Fprintf(os.Stderr, "# %s\n", b.Title)
|
||||
}
|
||||
// overide the color if specified
|
||||
if len(b.Color) > 0 {
|
||||
color = b.Color
|
||||
} else {
|
||||
color = barcolor
|
||||
}
|
||||
// extract the scale data from the XML attributes
|
||||
// if not specified, compute the scale factors
|
||||
sc := strings.Split(b.Scale, ",")
|
||||
var scalemin, scalemax, scaleincr float64
|
||||
|
||||
if len(sc) != 3 {
|
||||
if len(b.Bitem) > 0 {
|
||||
scalemin, scalemax, scaleincr = scalevalues(b.Bitem)
|
||||
}
|
||||
|
||||
if len(b.Bstack) > 0 {
|
||||
scalemin, scalemax, scaleincr = scalestack(b.Bstack)
|
||||
}
|
||||
} else {
|
||||
scalemin, _ = strconv.ParseFloat(sc[0], 64)
|
||||
scalemax, _ = strconv.ParseFloat(sc[1], 64)
|
||||
scaleincr, _ = strconv.ParseFloat(sc[2], 64)
|
||||
}
|
||||
// label the graph
|
||||
canvas.Text(x, y, b.Title, btitlestyle+anchor())
|
||||
|
||||
y += sep * 2
|
||||
chartop := y
|
||||
|
||||
// draw the data items
|
||||
canvas.Gstyle(datastyle + color)
|
||||
|
||||
// stacked bars
|
||||
for _, stack := range b.Bstack {
|
||||
if trace {
|
||||
fmt.Fprintf(os.Stderr, "%s~%s\n", stack.Value, stack.Name)
|
||||
}
|
||||
stackdata := stackvalues(stack.Value)
|
||||
if len(stackdata) < 1 {
|
||||
continue
|
||||
}
|
||||
sx := x
|
||||
canvas.Text(x-sep, y+barheight/2, textlimit(stack.Name, labelimit), labelstyle)
|
||||
barop := colorange(1.0, 0.3, len(stackdata))
|
||||
for ns, sd := range stackdata {
|
||||
dw := vmap(sd, scalemin, scalemax, 0, float64(maxwidth))
|
||||
if len(stack.Color) > 0 {
|
||||
canvas.Roundrect(sx, y, int(dw), barheight, cr, cr, fmt.Sprintf("fill:%s;fill-opacity:%.2f", stack.Color, barop[ns]))
|
||||
} else {
|
||||
canvas.Roundrect(sx, y, int(dw), barheight, cr, cr, fmt.Sprintf("fill-opacity:%.2f", barop[ns]))
|
||||
}
|
||||
|
||||
|
||||
if (showdata || b.Showdata) && sd > 0 {
|
||||
var valuestyle = "fill-opacity:1;font-style:italic;font-size:75%;text-anchor:middle;baseline-shift:-25%;"
|
||||
var ditem string
|
||||
var datax int
|
||||
if len(b.Unit) > 0 {
|
||||
ditem = fmt.Sprintf(valformat+"%s", sd, b.Unit)
|
||||
} else {
|
||||
ditem = fmt.Sprintf(valformat, sd)
|
||||
}
|
||||
if len(inbar) > 0 {
|
||||
valuestyle += inbar
|
||||
} else {
|
||||
valuestyle += "fill:black"
|
||||
}
|
||||
datax = sx + int(dw)/2
|
||||
canvas.Text(datax, y+barheight/2, ditem, valuestyle)
|
||||
}
|
||||
sx += int(dw)
|
||||
}
|
||||
y += barheight + gutter
|
||||
}
|
||||
|
||||
// plain bars
|
||||
for _, d := range b.Bitem {
|
||||
if trace {
|
||||
fmt.Fprintf(os.Stderr, "%.2f~%s\n", d.Value, d.Name)
|
||||
}
|
||||
canvas.Text(x-sep, y+barheight/2, textlimit(d.Name, labelimit), labelstyle)
|
||||
dw := vmap(d.Value, scalemin, scalemax, 0, float64(maxwidth))
|
||||
var barop float64
|
||||
if b.Samebar {
|
||||
barop = 0.3
|
||||
} else {
|
||||
barop = 1.0
|
||||
}
|
||||
if len(d.Color) > 0 {
|
||||
canvas.Roundrect(x, y, int(dw), barheight, cr, cr, fmt.Sprintf("fill:%s;fill-opacity:%.2f", d.Color, barop))
|
||||
} else {
|
||||
canvas.Roundrect(x, y, int(dw), barheight, cr, cr, fmt.Sprintf("fill-opacity:%.2f", barop))
|
||||
}
|
||||
if showdata || b.Showdata {
|
||||
var valuestyle = "fill-opacity:1;font-style:italic;font-size:75%;text-anchor:start;baseline-shift:-25%;"
|
||||
var ditem string
|
||||
var datax int
|
||||
if len(b.Unit) > 0 {
|
||||
ditem = fmt.Sprintf(valformat+"%s", d.Value, b.Unit)
|
||||
} else {
|
||||
ditem = fmt.Sprintf(valformat, d.Value)
|
||||
}
|
||||
if len(inbar) > 0 {
|
||||
valuestyle += inbar
|
||||
datax = x + fontsize/2
|
||||
} else {
|
||||
valuestyle += "fill:black"
|
||||
datax = x + int(dw) + fontsize/2
|
||||
}
|
||||
canvas.Text(datax, y+barheight/2, ditem, valuestyle)
|
||||
}
|
||||
if !d.Samebar {
|
||||
y += barheight + gutter
|
||||
}
|
||||
}
|
||||
canvas.Gend()
|
||||
|
||||
// draw the scale and borders
|
||||
chartbot := y + gutter
|
||||
if showgrid || b.Showgrid {
|
||||
canvas.Line(x, chartop, x+maxwidth, chartop, borderstyle) // top border
|
||||
canvas.Line(x, chartbot-gutter, x+maxwidth, chartbot-gutter, borderstyle) // bottom border
|
||||
}
|
||||
if showscale {
|
||||
if scaleincr < 1 {
|
||||
scfmt = "%.1f"
|
||||
} else {
|
||||
scfmt = "%0.f"
|
||||
}
|
||||
canvas.Gstyle(scalestyle)
|
||||
for sc := scalemin; sc <= scalemax; sc += scaleincr {
|
||||
scx := vmap(sc, scalemin, scalemax, 0, float64(maxwidth))
|
||||
canvas.Text(x+int(scx), chartbot+fontsize, fmt.Sprintf(scfmt, sc))
|
||||
if showgrid || b.Showgrid {
|
||||
canvas.Line(x+int(scx), chartbot, x+int(scx), chartop, borderstyle) // grid line
|
||||
}
|
||||
}
|
||||
canvas.Gend()
|
||||
}
|
||||
|
||||
// apply the note if present
|
||||
if len(b.Note) > 0 {
|
||||
canvas.Gstyle(notestyle + anchor())
|
||||
y += fontsize * 2
|
||||
leading := 3
|
||||
for _, note := range b.Note {
|
||||
canvas.Text(bg.Left, y, note.Text)
|
||||
y += fontsize + leading
|
||||
}
|
||||
canvas.Gend()
|
||||
}
|
||||
y += sep * 7 // advance vertically for the next chart
|
||||
}
|
||||
// if requested, place the title below the last chart
|
||||
if showtitle && len(bg.Title) > 0 {
|
||||
y += fontsize * 2
|
||||
canvas.Text(bg.Left, y, bg.Title, titlestyle)
|
||||
}
|
||||
// apply overall note if present
|
||||
if len(bg.Note) > 0 {
|
||||
canvas.Gstyle(notestyle + anchor())
|
||||
y += fontsize * 2
|
||||
leading := 3
|
||||
for _, note := range bg.Note {
|
||||
canvas.Text(bg.Left, y, note.Text)
|
||||
y += fontsize + leading
|
||||
}
|
||||
canvas.Gend()
|
||||
}
|
||||
}
|
||||
|
||||
func anchor() string {
|
||||
if endtitle {
|
||||
return "end"
|
||||
}
|
||||
return "start"
|
||||
}
|
||||
|
||||
// vmap maps one interval to another
|
||||
func vmap(value float64, low1 float64, high1 float64, low2 float64, high2 float64) float64 {
|
||||
return low2 + (high2-low2)*(value-low1)/(high1-low1)
|
||||
}
|
||||
|
||||
// maxitem finds the maxima is a collection of bar items
|
||||
func maxitem(data []bitem) float64 {
|
||||
max := -math.SmallestNonzeroFloat64
|
||||
for _, d := range data {
|
||||
if d.Value > max {
|
||||
max = d.Value
|
||||
}
|
||||
}
|
||||
return max
|
||||
}
|
||||
|
||||
// maxstack finds the maxima is a stack of bars
|
||||
func maxstack(stacks []bstack) float64 {
|
||||
max := -math.SmallestNonzeroFloat64
|
||||
for _, s := range stacks {
|
||||
sv := stackvalues(s.Value)
|
||||
sum := 0.0
|
||||
for _, d := range sv {
|
||||
sum += d
|
||||
}
|
||||
if sum > max {
|
||||
max = sum
|
||||
}
|
||||
}
|
||||
return max
|
||||
}
|
||||
|
||||
// scale values returns the min, max, increment from a set of bar items
|
||||
func scalevalues(data []bitem) (float64, float64, float64) {
|
||||
var m, max, increment float64
|
||||
rui := 5
|
||||
m = maxitem(data)
|
||||
max = roundup(m, 100)
|
||||
if max > 2 {
|
||||
increment = roundup(max/float64(rui), 10)
|
||||
} else {
|
||||
increment = 0.4
|
||||
}
|
||||
return 0, max, increment
|
||||
}
|
||||
|
||||
// scalestack returns the min, max, increment from a stack of bars
|
||||
func scalestack(data []bstack) (float64, float64, float64) {
|
||||
var m, max, increment float64
|
||||
rui := 5
|
||||
m = maxstack(data)
|
||||
max = roundup(m, 100)
|
||||
if max > 2 {
|
||||
increment = roundup(max/float64(rui), 10)
|
||||
} else {
|
||||
increment = 0.4
|
||||
}
|
||||
return 0, max, increment
|
||||
}
|
||||
|
||||
// roundup rouds a floating point number up
|
||||
func roundup(n float64, m int) float64 {
|
||||
i := int(n)
|
||||
if i <= 2 {
|
||||
return 2
|
||||
}
|
||||
for ; i%m != 0; i++ {
|
||||
}
|
||||
return float64(i)
|
||||
}
|
||||
|
||||
// stack value returns the values from the value string of a stack
|
||||
func stackvalues(s string) []float64 {
|
||||
v := strings.Split(s, "/")
|
||||
if len(v) <= 0 {
|
||||
return nil
|
||||
}
|
||||
vals := make([]float64, len(v))
|
||||
for i, x := range v {
|
||||
f, err := strconv.ParseFloat(x, 64)
|
||||
if err != nil {
|
||||
vals[i] = 0
|
||||
} else {
|
||||
vals[i] = f
|
||||
}
|
||||
}
|
||||
return vals
|
||||
}
|
||||
|
||||
|
||||
// colorange evenly distributes opacity across a range of values
|
||||
func colorange(start, end float64, n int) []float64 {
|
||||
v := make([]float64, n)
|
||||
v[0] = start
|
||||
v[n-1] = end
|
||||
if n == 2 {
|
||||
return v
|
||||
}
|
||||
incr := (end-start)/float64(n-1)
|
||||
for i:=1; i < n-1; i++ {
|
||||
v[i] = v[i-1] + incr
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
|
||||
func textlimit(s string, n int) string {
|
||||
l := len(s)
|
||||
if l <= n {
|
||||
return s
|
||||
}
|
||||
|
||||
return s[0:n-3]+"..."
|
||||
}
|
||||
|
||||
// init sets up the command flags
|
||||
func init() {
|
||||
flag.StringVar(&bgcolor, "bg", "white", "background color")
|
||||
flag.StringVar(&barcolor, "bc", "rgb(200,200,200)", "bar color")
|
||||
flag.StringVar(&valformat, "vfmt", "%v", "value format")
|
||||
flag.IntVar(&width, "w", 1024, "width")
|
||||
flag.IntVar(&height, "h", 800, "height")
|
||||
flag.IntVar(&barheight, "bh", 20, "bar height")
|
||||
flag.IntVar(&gutter, "g", 5, "gutter")
|
||||
flag.IntVar(&cornerRadius, "cr", 0, "corner radius")
|
||||
flag.IntVar(&fontsize, "f", 18, "fontsize (px)")
|
||||
flag.BoolVar(&showscale, "showscale", true, "show scale")
|
||||
flag.BoolVar(&showgrid, "showgrid", false, "show grid")
|
||||
flag.BoolVar(&showdata, "showdata", false, "show data values")
|
||||
flag.BoolVar(&showtitle, "showtitle", false, "show title")
|
||||
flag.BoolVar(&endtitle, "endtitle", false, "align title to the end")
|
||||
flag.BoolVar(&trace, "trace", false, "show name/value pairs")
|
||||
flag.StringVar(&inbar, "inbar", "", "data in bar format")
|
||||
flag.StringVar(&title, "t", "", "title")
|
||||
}
|
||||
|
||||
// for every input file (or stdin), draw a bar graph
|
||||
// as specified by command flags
|
||||
func main() {
|
||||
flag.Parse()
|
||||
canvas := svg.New(os.Stdout)
|
||||
canvas.Start(width, height)
|
||||
canvas.Rect(0, 0, width, height, "fill:"+bgcolor)
|
||||
canvas.Gstyle(fmt.Sprintf(gstyle, fontsize))
|
||||
if len(flag.Args()) == 0 {
|
||||
dobc("", canvas)
|
||||
} else {
|
||||
for _, f := range flag.Args() {
|
||||
dobc(f, canvas)
|
||||
}
|
||||
}
|
||||
canvas.Gend()
|
||||
canvas.End()
|
||||
}
|
||||
|
|
@ -0,0 +1,238 @@
|
|||
// benchviz: visualize benchmark data from benchcmp
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ajstarks/svgo"
|
||||
)
|
||||
|
||||
// geometry defines the layout of the visualization
|
||||
type geometry struct {
|
||||
top, left, width, height, vwidth, vp, barHeight int
|
||||
dolines, coldata bool
|
||||
title, rcolor, scolor, style string
|
||||
deltamax, speedupmax float64
|
||||
}
|
||||
|
||||
// process reads the input and calls the visualization function
|
||||
func process(canvas *svg.SVG, filename string, g geometry) int {
|
||||
if filename == "" {
|
||||
return g.visualize(canvas, filename, os.Stdin)
|
||||
}
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
return 0
|
||||
}
|
||||
defer f.Close()
|
||||
return g.visualize(canvas, filename, f)
|
||||
}
|
||||
|
||||
// vmap maps world to canvas coordinates
|
||||
func vmap(value, low1, high1, low2, high2 float64) float64 {
|
||||
return low2 + (high2-low2)*(value-low1)/(high1-low1)
|
||||
}
|
||||
|
||||
// visualize performs the visualization of the input, reading a line a time
|
||||
func (g *geometry) visualize(canvas *svg.SVG, filename string, f io.Reader) int {
|
||||
var (
|
||||
err error
|
||||
line, vs, bmtitle string
|
||||
dmin, dmax float64
|
||||
)
|
||||
|
||||
bh := g.barHeight
|
||||
vizwidth := g.vwidth
|
||||
vspacing := g.barHeight + (g.barHeight / 3) // vertical spacing
|
||||
bmtype := "delta"
|
||||
|
||||
in := bufio.NewReader(f)
|
||||
canvas.Gstyle(fmt.Sprintf("font-size:%dpx;font-family:sans-serif", bh))
|
||||
if g.title == "" {
|
||||
bmtitle = filename
|
||||
} else {
|
||||
bmtitle = g.title
|
||||
}
|
||||
canvas.Text(g.left, g.top, bmtitle, "font-size:150%")
|
||||
|
||||
height := 0
|
||||
for x, y, nr := g.left+g.vp, g.top+vspacing, 0; err == nil; nr++ {
|
||||
line, err = in.ReadString('\n')
|
||||
fields := strings.Split(strings.TrimSpace(line), ` `)
|
||||
|
||||
if len(fields) <= 1 || len(line) < 2 {
|
||||
continue
|
||||
}
|
||||
name := fields[0]
|
||||
value := fields[len(fields)-1]
|
||||
if len(value) > 2 {
|
||||
vs = value[:len(value)-1]
|
||||
}
|
||||
v, _ := strconv.ParseFloat(vs, 64)
|
||||
av := math.Abs(v)
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(value, "delt"):
|
||||
bmtype = "delta"
|
||||
dmin = 0.0
|
||||
dmax = g.deltamax // 100.0
|
||||
y += vspacing * 2
|
||||
continue
|
||||
|
||||
case strings.HasPrefix(value, "speed"):
|
||||
bmtype = "speedup"
|
||||
dmin = 0.0
|
||||
dmax = g.speedupmax // 10.0
|
||||
y += vspacing * 2
|
||||
continue
|
||||
|
||||
case strings.HasPrefix(name, "#"):
|
||||
y += vspacing
|
||||
canvas.Text(g.left, y, line[1:], "font-style:italic;fill:gray")
|
||||
continue
|
||||
}
|
||||
|
||||
bw := int(vmap(av, dmin, dmax, 0, float64(vizwidth)))
|
||||
switch g.style {
|
||||
case "bar":
|
||||
g.bars(canvas, x, y, bw, bh, vspacing/2, bmtype, name, value, v)
|
||||
case "inline":
|
||||
g.inline(canvas, g.left, y, bw, bh, bmtype, name, value, v)
|
||||
default:
|
||||
g.bars(canvas, x, y, bw, bh, vspacing/2, bmtype, name, value, v)
|
||||
}
|
||||
y += vspacing
|
||||
height = y
|
||||
}
|
||||
canvas.Gend()
|
||||
return height
|
||||
}
|
||||
|
||||
// inline makes the inline style pf visualization
|
||||
func (g *geometry) inline(canvas *svg.SVG, x, y, w, h int, bmtype, name, value string, v float64) {
|
||||
var color string
|
||||
switch bmtype {
|
||||
case "delta":
|
||||
if v > 0 {
|
||||
color = g.rcolor
|
||||
} else {
|
||||
color = g.scolor
|
||||
}
|
||||
case "speedup":
|
||||
if v < 1.0 {
|
||||
color = g.rcolor
|
||||
} else {
|
||||
color = g.scolor
|
||||
}
|
||||
}
|
||||
canvas.Text(x-10, y, value, "text-anchor:end")
|
||||
canvas.Text(x, y, name)
|
||||
canvas.Rect(x, y-h, w, h, "fill-opacity:0.3;fill:"+color)
|
||||
}
|
||||
|
||||
// bars creates barchart style visualization
|
||||
func (g *geometry) bars(canvas *svg.SVG, x, y, w, h, vs int, bmtype, name, value string, v float64) {
|
||||
canvas.Gstyle("font-style:italic;font-size:75%")
|
||||
toffset := h / 4
|
||||
var tx int
|
||||
var tstyle string
|
||||
switch bmtype {
|
||||
case "delta":
|
||||
if v > 0 {
|
||||
canvas.Rect(x-w, y-h/2, w, h, "fill-opacity:0.3;fill:"+g.rcolor)
|
||||
tx = x - w - toffset
|
||||
tstyle = "text-anchor:end"
|
||||
} else {
|
||||
canvas.Rect(x, y-h/2, w, h, "fill-opacity:0.3;fill:"+g.scolor)
|
||||
tx = x + w + toffset
|
||||
tstyle = "text-anchor:start"
|
||||
}
|
||||
case "speedup":
|
||||
if v < 1.0 {
|
||||
canvas.Rect(x-w, y-h/2, w, h, "fill-opacity:0.3;fill:"+g.rcolor)
|
||||
tx = x - w - toffset
|
||||
tstyle = "text-anchor:end"
|
||||
} else {
|
||||
canvas.Rect(x, y-h/2, w, h, "fill-opacity:0.3;fill:"+g.scolor)
|
||||
tx = x + w + toffset
|
||||
tstyle = "text-anchor:start"
|
||||
}
|
||||
}
|
||||
if g.coldata {
|
||||
canvas.Text(x-toffset, y+toffset, value, "text-anchor:end")
|
||||
} else {
|
||||
canvas.Text(tx, y+toffset, value, tstyle)
|
||||
}
|
||||
canvas.Gend()
|
||||
canvas.Text(g.left, y+(h/2), name, "text-anchor:start")
|
||||
if g.dolines {
|
||||
canvas.Line(g.left, y+vs, g.left+(g.width-g.left), y+vs, "stroke:lightgray;stroke-width:1")
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
var (
|
||||
width = flag.Int("w", 1024, "width")
|
||||
top = flag.Int("top", 50, "top")
|
||||
left = flag.Int("left", 100, "left margin")
|
||||
vp = flag.Int("vp", 512, "visualization point")
|
||||
vw = flag.Int("vw", 300, "visual area width")
|
||||
bh = flag.Int("bh", 20, "bar height")
|
||||
smax = flag.Float64("sm", 10, "maximum speedup")
|
||||
dmax = flag.Float64("dm", 100, "maximum delta")
|
||||
title = flag.String("title", "", "title")
|
||||
speedcolor = flag.String("scolor", "green", "speedup color")
|
||||
regresscolor = flag.String("rcolor", "red", "regression color")
|
||||
style = flag.String("style", "bar", "set the style (bar or inline)")
|
||||
lines = flag.Bool("line", false, "show lines between entries")
|
||||
coldata = flag.Bool("col", false, "show data in a single column")
|
||||
)
|
||||
flag.Parse()
|
||||
|
||||
g := geometry{
|
||||
width: *width,
|
||||
top: *top,
|
||||
left: *left,
|
||||
vp: *vp,
|
||||
vwidth: *vw,
|
||||
barHeight: *bh,
|
||||
title: *title,
|
||||
scolor: *speedcolor,
|
||||
rcolor: *regresscolor,
|
||||
style: *style,
|
||||
dolines: *lines,
|
||||
coldata: *coldata,
|
||||
speedupmax: *smax,
|
||||
deltamax: *dmax,
|
||||
}
|
||||
|
||||
// For every named file or stdin, render the SVG in memory, accumulating the height.
|
||||
var b bytes.Buffer
|
||||
canvas := svg.New(&b)
|
||||
height := 0
|
||||
if len(flag.Args()) > 0 {
|
||||
for _, f := range flag.Args() {
|
||||
height = process(canvas, f, g)
|
||||
g.top = height + 50
|
||||
}
|
||||
} else {
|
||||
height = process(canvas, "", g)
|
||||
}
|
||||
g.height = height + 15
|
||||
|
||||
// Write the rendered SVG to stdout
|
||||
out := svg.New(os.Stdout)
|
||||
out.Start(g.width, g.height)
|
||||
out.Rect(0, 0, g.width, g.height, "fill:white;stroke-width:2px;stroke:lightgray")
|
||||
b.WriteTo(os.Stdout)
|
||||
out.End()
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
// bubtrail draws a randmonized trail of bubbles
|
||||
// +build !appengine
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/ajstarks/svgo"
|
||||
)
|
||||
|
||||
var (
|
||||
width = 1200
|
||||
height = 600
|
||||
opacity = 0.5
|
||||
size = 40
|
||||
niter = 200
|
||||
canvas = svg.New(os.Stdout)
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.IntVar(&size, "s", 40, "bubble size")
|
||||
flag.IntVar(&niter, "n", 200, "number of iterations")
|
||||
flag.Float64Var(&opacity, "o", 0.5, "opacity")
|
||||
flag.Parse()
|
||||
rand.Seed(int64(time.Now().Nanosecond()) % 1e9)
|
||||
}
|
||||
|
||||
func background(v int) { canvas.Rect(0, 0, width, height, canvas.RGB(v, v, v)) }
|
||||
|
||||
func random(howsmall, howbig int) int {
|
||||
if howsmall >= howbig {
|
||||
return howsmall
|
||||
}
|
||||
return rand.Intn(howbig-howsmall) + howsmall
|
||||
}
|
||||
|
||||
func main() {
|
||||
var style string
|
||||
|
||||
canvas.Start(width, height)
|
||||
canvas.Title("Bubble Trail")
|
||||
background(200)
|
||||
canvas.Gstyle(fmt.Sprintf("fill-opacity:%.2f;stroke:none", opacity))
|
||||
for i := 0; i < niter; i++ {
|
||||
x := random(0, width)
|
||||
y := random(height/3, (height*2)/3)
|
||||
r := random(0, 10000)
|
||||
switch {
|
||||
case r >= 0 && r <= 2500:
|
||||
style = "fill:rgb(255,255,255)"
|
||||
case r > 2500 && r <= 5000:
|
||||
style = "fill:rgb(127,0,0)"
|
||||
case r > 5000 && r <= 7500:
|
||||
style = "fill:rgb(127,127,127)"
|
||||
case r > 7500 && r <= 10000:
|
||||
style = "fill:rgb(0,0,0)"
|
||||
}
|
||||
canvas.Circle(x, y, size, style)
|
||||
}
|
||||
canvas.Gend()
|
||||
canvas.End()
|
||||
}
|
||||
|
|
@ -0,0 +1,233 @@
|
|||
// bulletgraph - bullet graphs
|
||||
// (Design Specification http://www.perceptualedge.com/articles/misc/Bullet_Graph_Design_Spec.pdf)
|
||||
// +build !appengine
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ajstarks/svgo"
|
||||
)
|
||||
|
||||
var (
|
||||
width, height, fontsize, barheight, gutter, circleradius int
|
||||
bgcolor, barcolor, datacolor, compcolor, title, font string
|
||||
showtitle, circlemark bool
|
||||
gstyle = "font-family:'%s',sans-serif;font-size:%dpx"
|
||||
)
|
||||
|
||||
// a Bulletgraph Defintion
|
||||
// <bulletgraph title="Bullet Graph" top="50" left="250" right="50">
|
||||
// <note>This is a note</note>
|
||||
// <note>More expository text</note>
|
||||
// <bdata title="Revenue 2005" subtitle="USD (1,000)" scale="0,300,50" qmeasure="150,225" cmeasure="250" measure="275"/>
|
||||
// <bdata title="Profit" subtitle="%" scale="0,30,5" qmeasure="20,25" cmeasure="27" measure="22.5"/>
|
||||
// <bdata title="Avg Order Size subtitle="USD" scale="0,600,100" qmeasure="350,500" cmeasure="550" measure="320"/>
|
||||
// <bdata title="New Customers" subtitle="Count" scale="0,2500,500" qmeasure="1700,2000" cmeasure="2100" measure="1750"/>
|
||||
// <bdata title="Cust Satisfaction" subtitle="Top rating of 5" scale="0,5,1" qmeasure="3.5,4.5" cmeasure="4.7" measure="4.85"/>
|
||||
// </bulletgraph>
|
||||
|
||||
// Bulletgraph is the top-level drawing
|
||||
type Bulletgraph struct {
|
||||
Top int `xml:"top,attr"`
|
||||
Left int `xml:"left,attr"`
|
||||
Right int `xml:"right,attr"`
|
||||
Title string `xml:"title,attr"`
|
||||
Bdata []bdata `xml:"bdata"`
|
||||
Note []note `xml:"note"`
|
||||
}
|
||||
|
||||
type bdata struct {
|
||||
Title string `xml:"title,attr"`
|
||||
Subtitle string `xml:"subtitle,attr"`
|
||||
Scale string `xml:"scale,attr"`
|
||||
Qmeasure string `xml:"qmeasure,attr"`
|
||||
Cmeasure float64 `xml:"cmeasure,attr"`
|
||||
Measure float64 `xml:"measure,attr"`
|
||||
}
|
||||
|
||||
type note struct {
|
||||
Text string `xml:",chardata"`
|
||||
}
|
||||
|
||||
// dobg does file i/o
|
||||
func dobg(location string, s *svg.SVG) {
|
||||
var f *os.File
|
||||
var err error
|
||||
if len(location) > 0 {
|
||||
f, err = os.Open(location)
|
||||
} else {
|
||||
f = os.Stdin
|
||||
}
|
||||
if err == nil {
|
||||
readbg(f, s)
|
||||
f.Close()
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// readbg reads and parses the XML specification
|
||||
func readbg(r io.Reader, s *svg.SVG) {
|
||||
var bg Bulletgraph
|
||||
if err := xml.NewDecoder(r).Decode(&bg); err == nil {
|
||||
drawbg(bg, s)
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// drawbg draws the bullet graph
|
||||
func drawbg(bg Bulletgraph, canvas *svg.SVG) {
|
||||
qmheight := barheight / 3
|
||||
|
||||
if bg.Left == 0 {
|
||||
bg.Left = 250
|
||||
}
|
||||
if bg.Right == 0 {
|
||||
bg.Right = 50
|
||||
}
|
||||
if bg.Top == 0 {
|
||||
bg.Top = 50
|
||||
}
|
||||
if len(title) > 0 {
|
||||
bg.Title = title
|
||||
}
|
||||
|
||||
maxwidth := width - (bg.Left + bg.Right)
|
||||
x := bg.Left
|
||||
y := bg.Top
|
||||
scalesep := 4
|
||||
tx := x - fontsize
|
||||
|
||||
canvas.Title(bg.Title)
|
||||
// for each bdata element...
|
||||
for _, v := range bg.Bdata {
|
||||
|
||||
// extract the data from the XML attributes
|
||||
sc := strings.Split(v.Scale, ",")
|
||||
qm := strings.Split(v.Qmeasure, ",")
|
||||
|
||||
// you must have min,max,increment for the scale, at least one qualitative measure
|
||||
if len(sc) != 3 || len(qm) < 1 {
|
||||
continue
|
||||
}
|
||||
// get the qualitative measures
|
||||
qmeasures := make([]float64, len(qm))
|
||||
for i, q := range qm {
|
||||
qmeasures[i], _ = strconv.ParseFloat(q, 64)
|
||||
}
|
||||
scalemin, _ := strconv.ParseFloat(sc[0], 64)
|
||||
scalemax, _ := strconv.ParseFloat(sc[1], 64)
|
||||
scaleincr, _ := strconv.ParseFloat(sc[2], 64)
|
||||
|
||||
// label the graph
|
||||
canvas.Text(tx, y+(barheight/2), fmt.Sprintf("%s (%g)", v.Title, v.Measure), "text-anchor:end;font-weight:bold")
|
||||
canvas.Text(tx, y+(barheight/2)+fontsize, v.Subtitle, "fill:darkgray;text-anchor:end;font-size:75%")
|
||||
|
||||
// draw the scale
|
||||
scfmt := "%g"
|
||||
if fraction(scaleincr) > 0 {
|
||||
scfmt = "%.1f"
|
||||
}
|
||||
canvas.Gstyle("text-anchor:middle;font-size:75%")
|
||||
for sc := scalemin; sc <= scalemax; sc += scaleincr {
|
||||
scx := vmap(sc, scalemin, scalemax, 0, float64(maxwidth))
|
||||
canvas.Text(x+int(scx), y+scalesep+barheight+fontsize/2, fmt.Sprintf(scfmt, sc))
|
||||
}
|
||||
canvas.Gend()
|
||||
|
||||
// draw the qualitative measures
|
||||
canvas.Gstyle("fill-opacity:0.5;fill:" + barcolor)
|
||||
canvas.Rect(x, y, maxwidth, barheight)
|
||||
for _, q := range qmeasures {
|
||||
qbarlength := vmap(q, scalemin, scalemax, 0, float64(maxwidth))
|
||||
canvas.Rect(x, y, int(qbarlength), barheight)
|
||||
}
|
||||
canvas.Gend()
|
||||
|
||||
// draw the measure and the comparative measure
|
||||
barlength := int(vmap(v.Measure, scalemin, scalemax, 0, float64(maxwidth)))
|
||||
canvas.Rect(x, y+qmheight, barlength, qmheight, "fill:"+datacolor)
|
||||
cmx := int(vmap(v.Cmeasure, scalemin, scalemax, 0, float64(maxwidth)))
|
||||
if circlemark {
|
||||
canvas.Circle(x+cmx, y+barheight/2, circleradius, "fill-opacity:0.3;fill:"+compcolor)
|
||||
} else {
|
||||
cbh := barheight / 4
|
||||
canvas.Line(x+cmx, y+cbh, x+cmx, y+barheight-cbh, "stroke-width:3;stroke:"+compcolor)
|
||||
}
|
||||
|
||||
y += barheight + gutter // adjust vertical position for the next iteration
|
||||
}
|
||||
// if requested, place the title below the last bar
|
||||
if showtitle && len(bg.Title) > 0 {
|
||||
y += fontsize * 2
|
||||
canvas.Text(bg.Left, y, bg.Title, "text-anchor:start;font-size:200%")
|
||||
}
|
||||
|
||||
if len(bg.Note) > 0 {
|
||||
canvas.Gstyle("font-size:100%;text-anchor:start")
|
||||
y += fontsize * 2
|
||||
leading := 3
|
||||
for _, note := range bg.Note {
|
||||
canvas.Text(bg.Left, y, note.Text)
|
||||
y += fontsize + leading
|
||||
}
|
||||
canvas.Gend()
|
||||
}
|
||||
}
|
||||
|
||||
//vmap maps one interval to another
|
||||
func vmap(value float64, low1 float64, high1 float64, low2 float64, high2 float64) float64 {
|
||||
return low2 + (high2-low2)*(value-low1)/(high1-low1)
|
||||
}
|
||||
|
||||
// fraction returns the fractions portion of a floating point number
|
||||
func fraction(n float64) float64 {
|
||||
i := int(n)
|
||||
return n - float64(i)
|
||||
}
|
||||
|
||||
// init sets up the command flags
|
||||
func init() {
|
||||
flag.StringVar(&bgcolor, "bg", "white", "background color")
|
||||
flag.StringVar(&barcolor, "bc", "rgb(240,240,240)", "bar color")
|
||||
flag.StringVar(&datacolor, "dc", "rgb(200,200,200)", "data color")
|
||||
flag.StringVar(&compcolor, "cc", "rgb(127,0,0)", "comparative color")
|
||||
flag.StringVar(&font, "font", "Calibri", "font")
|
||||
flag.IntVar(&width, "w", 1024, "width")
|
||||
flag.IntVar(&height, "h", 800, "height")
|
||||
flag.IntVar(&barheight, "bh", 32, "bar height")
|
||||
flag.IntVar(&circleradius, "cr", 8, "circle radius")
|
||||
flag.IntVar(&gutter, "g", 36, "gutter")
|
||||
flag.IntVar(&fontsize, "f", 18, "fontsize (px)")
|
||||
flag.BoolVar(&circlemark, "circle", false, "circle mark")
|
||||
flag.BoolVar(&showtitle, "showtitle", true, "show title")
|
||||
flag.StringVar(&title, "t", "", "title")
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
// for every input file (or stdin), draw a bullet graph
|
||||
// as specified by command flags
|
||||
func main() {
|
||||
canvas := svg.New(os.Stdout)
|
||||
canvas.Start(width, height)
|
||||
canvas.Rect(0, 0, width, height, "fill:"+bgcolor)
|
||||
canvas.Gstyle(fmt.Sprintf(gstyle, font, fontsize))
|
||||
if len(flag.Args()) == 0 {
|
||||
dobg("", canvas)
|
||||
} else {
|
||||
for _, f := range flag.Args() {
|
||||
dobg(f, canvas)
|
||||
}
|
||||
}
|
||||
canvas.Gend()
|
||||
canvas.End()
|
||||
}
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
// codepic -- produce code+output sample suitable for slides
|
||||
// +build !appengine
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/xml"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/ajstarks/svgo"
|
||||
)
|
||||
|
||||
var (
|
||||
canvas = svg.New(os.Stdout)
|
||||
font string
|
||||
codeframe, picframe, syntax bool
|
||||
linespacing, fontsize, top, left, boxwidth, width, height int
|
||||
)
|
||||
|
||||
const (
|
||||
framestyle = "stroke:gray;stroke-dasharray:1,1;fill:none"
|
||||
codefmt = "font-family:%s;font-size:%dpx"
|
||||
labelfmt = "text-anchor:middle;" + codefmt
|
||||
kwfmt = `<tspan %s>%s</tspan>`
|
||||
commentfmt = `<tspan font-style="italic" fill="rgb(127,127,127)">%s</tspan>`
|
||||
textfmt = "<text x=\"%d\" y=\"%d\" xml:space=\"preserve\">%s</text>\n"
|
||||
svgofmt = `font-weight="bold" fill="rgb(0,0,127)"`
|
||||
gokwfmt = `font-style="italic" fill="rgb(127,0,0)"`
|
||||
)
|
||||
|
||||
// SVG is the incoming SVG file, capture everything into between <svg..> and </svg>
|
||||
// in the Doc string. This code will be translated to form the "picture" portion
|
||||
type SVG struct {
|
||||
Width int `xml:"width,attr"`
|
||||
Height int `xml:"height,attr"`
|
||||
Doc string `xml:",innerxml"`
|
||||
}
|
||||
|
||||
var gokw = []string{
|
||||
"defer ", "go ", "range ", "chan ", " continue", "if ", "for ", "func ",
|
||||
"uint8", "uint", "uint16", "uint32", "complex64", "complex128", " byte", "int8", "int16", "int32",
|
||||
"int64", " int", "float64", "float32", " string", "import ", "const ",
|
||||
"package ", "return", "var ", "type ", "switch ", "case ", "default:",
|
||||
}
|
||||
|
||||
var svgokw = []string{
|
||||
".Start", ".Startview,", ".End", ".Script", ".Gstyle", ".Gtransform", ".Scale", ".Offcolor",
|
||||
".ScaleXY", ".SkewX", ".SkewY", ".SkewXY,", ".Rotate", ".TranslateRotate", ".RotateTranslate", ".Translate",
|
||||
".Group", ".Gid", ".Gend", ".ClipPath", ".ClipEnd", ".DefEnd", ".Def", ".Desc", ".Title", ".Linkf",
|
||||
".LinkEnd", ".Use", ".Mask", ".MaskEnd", ".Circle", ".Ellipse", ".Polygon", ".Rect", ".CenterRect",
|
||||
".Roundrect", ".Square", ".Path", ".Arc", ".Bezier", ".Qbez", ".Qbezier", ".Line", ".Polyline", ".Image",
|
||||
".Textpath", ".Textlines,", ".Text", ".RGBA", ".RGB", ".LinearGradient", ".RadialGradient", ".Grid",
|
||||
}
|
||||
|
||||
// codepic makes a code+picture SVG file, given a go source file
|
||||
// and conventionally named output -- given <name>.go, <name>.svg
|
||||
func codepic(filename string) {
|
||||
var basename string
|
||||
|
||||
bn := strings.Split(filename, ".")
|
||||
if len(bn) > 0 {
|
||||
basename = bn[0]
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "cannot get the basename for %s\n", filename)
|
||||
return
|
||||
}
|
||||
canvas.Start(width, height)
|
||||
canvas.Title(basename)
|
||||
canvas.Rect(0, 0, width, height, "fill:white")
|
||||
placepic(width/2, top, basename)
|
||||
canvas.Gstyle(fmt.Sprintf(codefmt, font, fontsize))
|
||||
placecode(left+fontsize, top+fontsize*2, filename)
|
||||
canvas.Gend()
|
||||
canvas.End()
|
||||
}
|
||||
|
||||
// placecode places the code section on the left
|
||||
func placecode(x, y int, filename string) {
|
||||
var rerr error
|
||||
var line string
|
||||
var ic bool
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
in := bufio.NewReader(f)
|
||||
|
||||
for xp := left + fontsize; rerr == nil; y += linespacing {
|
||||
line, rerr = in.ReadString('\n')
|
||||
if len(line) > 0 {
|
||||
line, ic = svgtext(xp, y, line[0:len(line)-1])
|
||||
if !ic && syntax {
|
||||
line = keyword(line, gokwfmt, gokw)
|
||||
line = keyword(line, svgofmt, svgokw)
|
||||
}
|
||||
io.WriteString(canvas.Writer, line)
|
||||
}
|
||||
}
|
||||
if codeframe {
|
||||
canvas.Rect(top, left, left+boxwidth, y, framestyle)
|
||||
}
|
||||
}
|
||||
|
||||
// keyword styles keywords in a line of code
|
||||
func keyword(line string, style string, kw []string) string {
|
||||
for _, k := range kw {
|
||||
line = strings.Replace(line, k, fmt.Sprintf(kwfmt, style, k), 1)
|
||||
}
|
||||
return line
|
||||
}
|
||||
|
||||
// svgtext
|
||||
func svgtext(x, y int, s string) (string, bool) {
|
||||
var iscomment = false
|
||||
s = strings.Replace(s, "&", "&", -1)
|
||||
s = strings.Replace(s, "<", "<", -1)
|
||||
s = strings.Replace(s, ">", ">", -1)
|
||||
s = strings.Replace(s, "\t", " ", -1)
|
||||
|
||||
if syntax {
|
||||
i := strings.Index(s, "// ")
|
||||
if i >= 0 {
|
||||
iscomment = true
|
||||
s = strings.Replace(s, s[i:], fmt.Sprintf(commentfmt, s[i:]), 1)
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf(textfmt, x, y, s), iscomment
|
||||
}
|
||||
|
||||
// placepic places the picture on the right
|
||||
func placepic(x, y int, basename string) {
|
||||
var s SVG
|
||||
f, err := os.Open(basename + ".svg")
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
if err := xml.NewDecoder(f).Decode(&s); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Unable to parse (%v)\n", err)
|
||||
return
|
||||
}
|
||||
canvas.Text(x, height-10, basename+".go", fmt.Sprintf(labelfmt, font, fontsize*2))
|
||||
canvas.Group(`clip-path="url(#pic)"`, fmt.Sprintf(`transform="translate(%d,%d)"`, x, y))
|
||||
canvas.ClipPath(`id="pic"`)
|
||||
canvas.Rect(0, 0, s.Width, s.Height)
|
||||
canvas.ClipEnd()
|
||||
io.WriteString(canvas.Writer, s.Doc)
|
||||
canvas.Gend()
|
||||
if picframe {
|
||||
canvas.Rect(x, y, s.Width, s.Height, framestyle)
|
||||
}
|
||||
}
|
||||
|
||||
// init initializes flags
|
||||
func init() {
|
||||
flag.BoolVar(&codeframe, "codeframe", false, "frame the code")
|
||||
flag.BoolVar(&picframe, "picframe", false, "frame the picture")
|
||||
flag.BoolVar(&syntax, "syntax", false, "syntax coloring")
|
||||
flag.IntVar(&width, "w", 1024, "width")
|
||||
flag.IntVar(&height, "h", 768, "height")
|
||||
flag.IntVar(&linespacing, "ls", 16, "linespacing")
|
||||
flag.IntVar(&fontsize, "fs", 14, "fontsize")
|
||||
flag.IntVar(&top, "top", 20, "top")
|
||||
flag.IntVar(&left, "left", 20, "left")
|
||||
flag.IntVar(&boxwidth, "boxwidth", 450, "boxwidth")
|
||||
flag.StringVar(&font, "font", "Inconsolata", "font name")
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
// for every file, make a code+pic SVG file
|
||||
func main() {
|
||||
for _, f := range flag.Args() {
|
||||
codepic(f)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
// colortab -- make a color/code placemat
|
||||
// +build !appengine
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/ajstarks/svgo"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
canvas = svg.New(os.Stdout)
|
||||
filename = flag.String("f", "svgcolors.txt", "input file")
|
||||
fontname = flag.String("font", "Calibri,sans-serif", "fontname")
|
||||
outline = flag.Bool("o", false, "outline")
|
||||
neg = flag.Bool("n", false, "negative")
|
||||
showrgb = flag.Bool("rgb", false, "show RGB")
|
||||
showcode = flag.Bool("showcode", true, "only show colors")
|
||||
circsw = flag.Bool("circle", true, "circle swatch")
|
||||
fontsize = flag.Int("fs", 12, "fontsize")
|
||||
width = flag.Int("w", 1600, "width")
|
||||
height = flag.Int("h", 900, "height")
|
||||
rowsize = flag.Int("r", 32, "rowsize")
|
||||
colw = flag.Int("c", 320, "column size")
|
||||
swatch = flag.Int("s", 16, "swatch size")
|
||||
gutter = flag.Int("g", 11, "gutter")
|
||||
err error
|
||||
colorfmt, tcolor, line string
|
||||
)
|
||||
|
||||
flag.Parse()
|
||||
f, oerr := os.Open(*filename)
|
||||
if oerr != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", oerr)
|
||||
return
|
||||
}
|
||||
canvas.Start(*width, *height)
|
||||
canvas.Title("SVG Color Table")
|
||||
if *neg {
|
||||
canvas.Rect(0, 0, *width, *height, "fill:black")
|
||||
tcolor = "white"
|
||||
} else {
|
||||
canvas.Rect(0, 0, *width, *height, "fill:white")
|
||||
tcolor = "black"
|
||||
}
|
||||
top := 32
|
||||
left := 32
|
||||
in := bufio.NewReader(f)
|
||||
canvas.Gstyle(fmt.Sprintf("font-family:%s;font-size:%dpt;fill:%s",
|
||||
*fontname, *fontsize, tcolor))
|
||||
for x, y, nr := left, top, 0; err == nil; nr++ {
|
||||
line, err = in.ReadString('\n')
|
||||
fields := strings.Split(strings.TrimSpace(line), "\t")
|
||||
if nr%*rowsize == 0 && nr > 0 {
|
||||
x += *colw
|
||||
y = top
|
||||
}
|
||||
if len(fields) == 3 {
|
||||
colorfmt = "fill:" + fields[1]
|
||||
if *outline {
|
||||
colorfmt = colorfmt + ";stroke-width:1;stroke:" + tcolor
|
||||
}
|
||||
if *circsw {
|
||||
canvas.Circle(x, y, *swatch/2, colorfmt)
|
||||
} else {
|
||||
canvas.CenterRect(x, y, *swatch, *swatch, colorfmt)
|
||||
}
|
||||
canvas.Text(x+*swatch+*fontsize/2, y+(*swatch/4), fields[0], "stroke:none")
|
||||
var label string
|
||||
if *showcode {
|
||||
if *showrgb {
|
||||
label = fields[1]
|
||||
} else {
|
||||
label = fields[2]
|
||||
}
|
||||
canvas.Text(x+((*colw*4)/5), y+(*swatch/4), label, "text-anchor:end;fill:gray")
|
||||
}
|
||||
}
|
||||
y += (*swatch + *gutter)
|
||||
}
|
||||
canvas.Gend()
|
||||
canvas.End()
|
||||
}
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
aliceblue #F0F8FF 240,248,255
|
||||
antiquewhite #FAEBD7 250,235,215
|
||||
aqua #00FFFF 0,255,25
|
||||
aquamarine #7FFFD4 127,255,21
|
||||
azure #F0FFFF 240,255,255
|
||||
beige #F5F5DC 245,245,220
|
||||
bisque #FFE4C4 255,228,196
|
||||
black #000000 0,0,0
|
||||
blanchedalmond #FFEBCD 255,235,205
|
||||
blue #0000FF 0,0,255
|
||||
blueviolet #8A2BE2 138,43,226
|
||||
brown #A52A2A 165,42,42
|
||||
burlywood #DEB887 222,184,135
|
||||
cadetblue #5F9EA0 95,158,160
|
||||
chartreuse #7FFF00 127,255,0
|
||||
chocolate #D2691E 210,105,30
|
||||
coral #FF7F50 255,127,80
|
||||
cornflowerblue #6495ED 100,149,237
|
||||
cornsilk #FFF8DC 255,248,220
|
||||
crimson #DC143C 220,20,60
|
||||
cyan #00FFFF 0,255,255
|
||||
darkblue #00008B 0,0,139
|
||||
darkcyan #008B8B 0,139,139
|
||||
darkgoldenrod #B8860B 184,134,11
|
||||
darkgray #A9A9A9 169,169,169
|
||||
darkgreen #006400 0,100,0
|
||||
darkgrey #A9A9A9 169,169,169
|
||||
darkkhaki #BDB76B 189,183,107
|
||||
darkmagenta #8B008B 139,0,139
|
||||
darkolivegreen #556B2F 85,107,47
|
||||
darkorange #FF8C00 255,140,0
|
||||
darkorchid #9932CC 153,50,204
|
||||
darkred #8B0000 139,0,0
|
||||
darksalmon #E9967A 233,150,122
|
||||
darkseagreen #8FBC8F 143,188,143
|
||||
darkslateblue #483D8B 72,61,139
|
||||
darkslategray #2F4F4F 47,79,79
|
||||
darkslategrey #2F4F4F 47,79,79
|
||||
darkturquoise #00CED1 0,206,209
|
||||
darkviolet #9400D3 148,0,211
|
||||
deeppink #FF1493 255,20,147
|
||||
deepskyblue #00BFFF 0,191,255
|
||||
dimgray #696969 105,105,105
|
||||
dimgrey #696969 105,105,105
|
||||
dodgerblue #1E90FF 30,144,255
|
||||
firebrick #B22222 178,34,34
|
||||
floralwhite #FFFAF0 255,250,240
|
||||
forestgreen #228B22 34,139,34
|
||||
fuchsia #FF00FF 255,0,255
|
||||
gainsboro #DCDCDC 220,220,220
|
||||
ghostwhite #F8F8FF 248,248,255
|
||||
gold #FFD700 255,215,0
|
||||
goldenrod #DAA520 218,165,32
|
||||
gray #808080 128,128,128
|
||||
green #008000 0,128,0
|
||||
greenyellow #ADFF2F 173,255,47
|
||||
grey #808080 128,128,128
|
||||
honeydew #F0FFF0 240,255,240
|
||||
hotpink #FF69B4 255,105,180
|
||||
indianred #CD5C5C 205,92,92
|
||||
indigo #4B0082 75,0,130
|
||||
ivory #FFFFF0 255,255,240
|
||||
khaki #F0E68C 240,230,140
|
||||
lavender #E6E6FA 230,230,250
|
||||
lavenderblush #FFF0F5 255,240,245
|
||||
lawngreen #7CFC00 124,252,0
|
||||
lemonchiffon #FFFACD 255,250,205
|
||||
lightblue #ADD8E6 173,216,230
|
||||
lightcoral #F08080 240,128,128
|
||||
lightcyan #E0FFFF 224,255,255
|
||||
lightgoldenrodyellow #FAFAD2 250,250,210
|
||||
lightgray #D3D3D3 211,211,211
|
||||
lightgreen #90EE90 144,238,144
|
||||
lightgrey #D3D3D3 211,211,211
|
||||
lightpink #FFB6C1 255,182,193
|
||||
lightsalmon #FFA07A 255,160,122
|
||||
lightseagreen #20B2AA 32,178,170
|
||||
lightskyblue #87CEFA 135,206,250
|
||||
lightslategray #778899 119,136,153
|
||||
lightslategrey #778899 119,136,153
|
||||
lightsteelblue #B0C4DE 176,196,222
|
||||
lightyellow #FFFFE0 255,255,224
|
||||
lime #00FF00 0,255,0
|
||||
limegreen #32CD32 50,205,50
|
||||
linen #FAF0E6 250,240,230
|
||||
magenta #FF00FF 255,0,255
|
||||
maroon #800000 128,0,0
|
||||
mediumaquamarine #66CDAA 102,205,170
|
||||
mediumblue #0000CD 0,0,205
|
||||
mediumorchid #BA55D3 186,85,211
|
||||
mediumpurple #9370DB 147,112,219
|
||||
mediumseagreen #3CB371 60,179,113
|
||||
mediumslateblue #7B68EE 123,104,238
|
||||
mediumspringgreen #00FA9A 0,250,154
|
||||
mediumturquoise #48D1CC 72,209,204
|
||||
mediumvioletred #C71585 199,21,133
|
||||
midnightblue #191970 25,25,112
|
||||
mintcream #F5FFFA 245,255,250
|
||||
mistyrose #FFE4E1 255,228,225
|
||||
moccasin #FFE4B5 255,228,181
|
||||
navajowhite #FFDEAD 255,222,173
|
||||
navy #000080 0,0,128
|
||||
oldlace #FDF5E6 253,245,230
|
||||
olive #808000 128,128,0
|
||||
olivedrab #6B8E23 107,142,35
|
||||
orange #FFA500 255,165,0
|
||||
orangered #FF4500 255,69,0
|
||||
orchid #DA70D6 218,112,214
|
||||
palegoldenrod #EEE8AA 238,232,170
|
||||
palegreen #98FB98 152,251,152
|
||||
paleturquoise #AFEEEE 175,238,238
|
||||
palevioletred #DB7093 219,112,147
|
||||
papayawhip #FFEFD5 255,239,213
|
||||
peachpuff #FFDAB9 255,218,185
|
||||
peru #CD853F 205,133,63
|
||||
pink #FFC0CB 255,192,203
|
||||
plum #DDA0DD 221,160,221
|
||||
powderblue #B0E0E6 176,224,230
|
||||
purple #800080 128,0,128
|
||||
red #FF0000 255,0,0
|
||||
rosybrown #BC8F8F 188,143,143
|
||||
royalblue #4169E1 65,105,225
|
||||
saddlebrown #8B4513 139,69,19
|
||||
salmon #FA8072 250,128,114
|
||||
sandybrown #F4A460 244,164,96
|
||||
seagreen #2E8B57 46,139,87
|
||||
seashell #FFF5EE 255,245,238
|
||||
sienna #A0522D 160,82,45
|
||||
silver #C0C0C0 192,192,192
|
||||
skyblue #87CEEB 135,206,235
|
||||
slateblue #6A5ACD 106,90,205
|
||||
slategray #708090 112,128,144
|
||||
slategrey #708090 112,128,144
|
||||
snow #FFFAFA 255,250,250
|
||||
springgreen #00FF7F 0,255,127
|
||||
steelblue #4682B4 70,130,180
|
||||
tan #D2B48C 210,180,140
|
||||
teal #008080 0,128,128
|
||||
thistle #D8BFD8 216,191,216
|
||||
tomato #FF6347 255,99,71
|
||||
turquoise #40E0D0 64,224,208
|
||||
violet #EE82EE 238,130,238
|
||||
wheat #F5DEB3 245,222,179
|
||||
white #FFFFFF 255,255,255
|
||||
whitesmoke #F5F5F5 245,245,245
|
||||
yellow #FFFF00 255,255,0
|
||||
yellowgreen #9ACD32 154,205,50
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<components gutter="50" top="50" left="50" gc="black">
|
||||
<comp id="plain" name="Plain" row="0" col="0" width="200" height="120" shape="plain"/>
|
||||
<comp id="comp" name="Widget" row="0" col="1" width="200" height="120" shape="" sw="Dignus\nHere" os="Widget/OS"/>
|
||||
<comp id="folder" name="Folder" row="0" col="2" width="200" height="120" shape="folder" sw="Nonsense" os="Stuff" />
|
||||
|
||||
<comp id="screen" name="Tablet" row="1" col="0" width="200" height="120" shape="screen" sw="Dignus Here" os="Widget/OS" />
|
||||
<comp id="desktop" name="Desktop" row="1" col="1" width="200" height="120" shape="desktop" sw="Workstation" os="Computer" />
|
||||
<comp id="db" name="Data" row="1" col="2" width="200" height="120" shape="db" sw="database" />
|
||||
|
||||
<comp id="server" name="Server" row="2" col="0" width="200" height="120" shape="server"/>
|
||||
<comp id="cloud" name="Cloud" row="2" col="1" width="200" height="120" shape="cloud" />
|
||||
<comp id="message" name="Envelope" row="2" col="2" width="200" height="120" shape="message" os="message"/>
|
||||
|
||||
<comp id="face" name="Face" row="3" col="0" width="200" height="120" shape="face"/>
|
||||
<comp id="role" name="A Role" row="3" col="1" width="200" height="120" shape="role"/>
|
||||
</components>
|
||||
|
|
@ -0,0 +1,894 @@
|
|||
// compx: display components and connections on a grid, given a XML description
|
||||
// +build !appengine
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/ajstarks/svgo"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Component XML structures
|
||||
type Component struct {
|
||||
Top int `xml:"top,attr"`
|
||||
Left int `xml:"left,attr"`
|
||||
Gutter int `xml:"gutter,attr"`
|
||||
Gw int `xml:"gw,attr"`
|
||||
Gh int `xml:"gh,attr"`
|
||||
Gc string `xml:"gc,attr"`
|
||||
Legend []legend `xml:"legend"`
|
||||
Note []note `xml:"note"`
|
||||
Group []group `xml:"group"`
|
||||
Comp []comp `xml:"comp"`
|
||||
}
|
||||
|
||||
type group struct {
|
||||
Brow int `xml:"brow,attr"`
|
||||
Bcol int `xml:"bcol,attr"`
|
||||
Erow int `xml:"erow,attr"`
|
||||
Ecol int `xml:"ecol,attr"`
|
||||
Width int `xml:"width,attr"`
|
||||
Height int `xml:"height,attr"`
|
||||
Label string `xml:"label,attr"`
|
||||
Color string `xml:"color,attr"`
|
||||
Opacity float64 `xml:"opacity,attr"`
|
||||
}
|
||||
|
||||
type note struct {
|
||||
Row int `xml:"row,attr"`
|
||||
Col int `xml:"col,attr"`
|
||||
Width int `xml:"width,attr"`
|
||||
Height int `xml:"height,attr"`
|
||||
Size int `xml:"size,attr"`
|
||||
Spacing int `xml:"spacing,attr"`
|
||||
Align string `xml:"align,attr"`
|
||||
Nitem []nitem `xml:"nitem"`
|
||||
}
|
||||
|
||||
type legend struct {
|
||||
Title string `xml:"title,attr"`
|
||||
Row int `xml:"row,attr"`
|
||||
Col int `xml:"col,attr"`
|
||||
Width int `xml:"width,attr"`
|
||||
Height int `xml:"height,attr"`
|
||||
Size int `xml:"size,attr"`
|
||||
Litem []litem `xml:"litem"`
|
||||
}
|
||||
|
||||
type comp struct {
|
||||
Id string `xml:"id,attr"`
|
||||
Col int `xml:"col,attr"`
|
||||
Row int `xml:"row,attr"`
|
||||
Width int `xml:"width,attr"`
|
||||
Height int `xml:"height,attr"`
|
||||
Name string `xml:"name,attr"`
|
||||
Os string `xml:"os,attr"`
|
||||
Sw string `xml:"sw,attr"`
|
||||
Color string `xml:"color,attr"`
|
||||
Shape string `xml:"shape,attr"`
|
||||
Image string `xml:"image,attr"`
|
||||
Connect []Connect `xml:"connect"`
|
||||
}
|
||||
|
||||
type litem struct {
|
||||
Color string `xml:"color,attr"`
|
||||
Type string `xml:"type,attr"`
|
||||
Label string `xml:",chardata"`
|
||||
}
|
||||
|
||||
type nitem struct {
|
||||
Color string `xml:"color,attr"`
|
||||
Align string `xml:"align,attr"`
|
||||
Text string `xml:",chardata"`
|
||||
}
|
||||
|
||||
// Connect defines connections
|
||||
type Connect struct {
|
||||
Sloc string `xml:"sloc,attr"`
|
||||
Dloc string `xml:"dloc,attr"`
|
||||
Dest string `xml:"dest,attr"`
|
||||
Mark string `xml:"mark,attr"`
|
||||
Color string `xml:"color,attr"`
|
||||
Dir string `xml:"dir,attr"`
|
||||
Label string `xml:",chardata"`
|
||||
}
|
||||
|
||||
type gcomp struct {
|
||||
x, y, w, h int
|
||||
}
|
||||
|
||||
var (
|
||||
width, height, fontscale int
|
||||
linesize, labelfs, notchsize, groupmargin int
|
||||
showtitle, showtimestamp, roundbox, arc, italiclabel bool
|
||||
title, bgcolor, guide string
|
||||
gridw = 0
|
||||
gridh = 0
|
||||
globalcolor = "black"
|
||||
canvas = svg.New(os.Stdout)
|
||||
)
|
||||
|
||||
const (
|
||||
lcolor = "rgb(190,190,190)"
|
||||
boxradius = 10
|
||||
lopacity = "1.0"
|
||||
defcolor = "black"
|
||||
linefmt = "stroke:%s;fill:none"
|
||||
globalstyle = "font-family:Calibri,sans-serif;font-size:%dpx;fill:black;text-anchor:middle;stroke-linecap:round;stroke-width:%dpx;stroke-opacity:%s"
|
||||
ltstyle = "text-anchor:%s;fill:black"
|
||||
legendstyle = "text-anchor:start;fill:black;font-size:%dpx"
|
||||
gridstyle = "fill:none; stroke:gray; stroke-opacity:0.3"
|
||||
notefmt = "font-size:%dpx"
|
||||
ntfmt = "text-anchor:%s;fill:%s"
|
||||
)
|
||||
|
||||
func background(fc string) { canvas.Rect(0, 0, width, height, "fill:"+fc) }
|
||||
|
||||
// docomp does XML file processing
|
||||
func docomp(location string) {
|
||||
var f *os.File
|
||||
var err error
|
||||
if len(location) > 0 {
|
||||
f, err = os.Open(location)
|
||||
} else {
|
||||
f = os.Stdin
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
readcomp(f)
|
||||
f.Close()
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// readcomp reads the XML into the component data structure
|
||||
func readcomp(r io.Reader) {
|
||||
var c Component
|
||||
if err := xml.NewDecoder(r).Decode(&c); err == nil {
|
||||
drawc(c)
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "Unable to parse components (%v)\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// drawc interprets the compoment data structure, and displays it
|
||||
func drawc(c Component) {
|
||||
gridw = c.Gw
|
||||
gridh = c.Gh
|
||||
if len(c.Gc) == 0 {
|
||||
globalcolor = c.Gc
|
||||
} else {
|
||||
globalcolor = c.Gc
|
||||
}
|
||||
|
||||
// Groups
|
||||
for _, group := range c.Group {
|
||||
dogroup(group, c.Top, c.Left, c.Gutter)
|
||||
}
|
||||
|
||||
// Legends
|
||||
for _, leg := range c.Legend {
|
||||
dolegend(leg, c.Top, c.Left, c.Gutter, labelfs, labelfs+4)
|
||||
}
|
||||
|
||||
// Notes
|
||||
for _, note := range c.Note {
|
||||
donote(note, c.Top, c.Left, c.Gutter)
|
||||
}
|
||||
|
||||
// Components
|
||||
for _, x := range c.Comp {
|
||||
for _, y := range x.Connect {
|
||||
connect(gc(x, c.Top, c.Left, c.Gutter), y.Sloc,
|
||||
lookup(y.Dest, c.Comp, c.Top, c.Left, c.Gutter),
|
||||
y.Dloc, y.Label, y.Mark, y.Dir, y.Color)
|
||||
}
|
||||
display(x, c.Top, c.Left, c.Gutter)
|
||||
}
|
||||
|
||||
if len(guide) > 0 {
|
||||
grid(c.Top, c.Left, c.Gutter)
|
||||
}
|
||||
if showtitle {
|
||||
dotitle(c.Left, 30, title)
|
||||
}
|
||||
if showtimestamp {
|
||||
timestamp(30)
|
||||
}
|
||||
}
|
||||
|
||||
// lookup returns a graphic object given an id
|
||||
func lookup(id string, c []comp, t, l, g int) gcomp {
|
||||
var x gcomp
|
||||
for _, v := range c {
|
||||
if id == v.Id {
|
||||
return gc(v, t, l, g)
|
||||
}
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// dotitle positions the title relative to the bottom of the drawing
|
||||
func dotitle(left, offset int, t string) {
|
||||
canvas.Text(left, height-offset, t, "font-size:200%;text-anchor:start")
|
||||
}
|
||||
|
||||
// timestamp draws a timestamp in the lower right of the drawing
|
||||
func timestamp(offset int) {
|
||||
t := time.Now()
|
||||
canvas.Text(width-offset, height-offset, t.Format(time.ANSIC), "text-anchor:end")
|
||||
}
|
||||
|
||||
// grid displays a grid overlay, useful for determining optimal positioning
|
||||
func grid(top, left, gutter int) {
|
||||
gs := strings.SplitN(guide, `x`, 4)
|
||||
if len(gs) != 4 {
|
||||
return
|
||||
}
|
||||
w, _ := strconv.Atoi(gs[0])
|
||||
h, _ := strconv.Atoi(gs[1])
|
||||
nr, _ := strconv.Atoi(gs[2])
|
||||
nc, _ := strconv.Atoi(gs[3])
|
||||
canvas.Gstyle(gridstyle)
|
||||
y := top
|
||||
for r := 0; r < nr; r++ {
|
||||
x := left
|
||||
for c := 0; c < nc; c++ {
|
||||
canvas.Rect(x, y, w, h)
|
||||
canvas.Text(x+w/2, y+h/2, fmt.Sprintf("%d,%d", r, c),
|
||||
"font-size:150%;fill:lightgray;stroke:none")
|
||||
x += w + gutter
|
||||
}
|
||||
y += h + gutter
|
||||
}
|
||||
canvas.Gend()
|
||||
}
|
||||
|
||||
// dogroup displays a colored rectangular area
|
||||
func dogroup(g group, top, left, gutter int) {
|
||||
margin := groupmargin
|
||||
bx := colx(g.Bcol, g.Width, gutter, left)
|
||||
by := rowy(g.Brow, g.Height, gutter, top)
|
||||
ex := colx(g.Ecol, g.Width, gutter, left)
|
||||
ey := rowy(g.Erow, g.Height, gutter, top)
|
||||
gw := (ex + g.Width) - bx
|
||||
gh := (ey + g.Height) - by
|
||||
var gop float64
|
||||
if g.Opacity <= 0 {
|
||||
gop = 1.0
|
||||
} else {
|
||||
gop = g.Opacity
|
||||
}
|
||||
canvas.Rect(bx-margin, by-margin, gw+margin*2, gh+margin*2,
|
||||
fmt.Sprintf("fill-opacity:%.2f;fill:%s", gop, g.Color))
|
||||
if len(g.Label) > 0 {
|
||||
canvas.Text(bx+gw/2, by+gh/2, g.Label, "fill:gray")
|
||||
}
|
||||
}
|
||||
|
||||
// dolegend displays the legend
|
||||
func dolegend(leg legend, top, left, gutter, fs, ls int) {
|
||||
if leg.Size > 0 {
|
||||
fs = leg.Size
|
||||
ls = fs + 4
|
||||
}
|
||||
fsh := fs / 2
|
||||
x := colx(leg.Col, leg.Width, gutter, left)
|
||||
y := rowy(leg.Row, leg.Height, gutter, top)
|
||||
|
||||
canvas.Gstyle(fmt.Sprintf(legendstyle, fs))
|
||||
for _, v := range leg.Litem {
|
||||
if v.Type == "line" {
|
||||
canvas.Rect(x, y+fs/4, fs, fs/4, "fill:"+v.Color)
|
||||
} else {
|
||||
canvas.Square(x, y, fs, "fill:"+v.Color)
|
||||
}
|
||||
canvas.Text(x+(fs*2), y+fsh, v.Label, "baseline-shift:-30%")
|
||||
y += ls
|
||||
}
|
||||
canvas.Gend()
|
||||
}
|
||||
|
||||
// donote displays a note
|
||||
func donote(n note, top, left, gutter int) {
|
||||
var align, color string
|
||||
|
||||
size := n.Size
|
||||
ls := n.Spacing
|
||||
x := colx(n.Col, n.Width, gutter, left)
|
||||
y := rowy(n.Row, n.Height, gutter, top)
|
||||
if n.Align == "middle" {
|
||||
y += n.Height / 2
|
||||
}
|
||||
if size <= 0 {
|
||||
size = labelfs
|
||||
}
|
||||
if ls == 0 {
|
||||
ls = size + 2
|
||||
}
|
||||
xp := x
|
||||
canvas.Gstyle(fmt.Sprintf(notefmt, size))
|
||||
for _, v := range n.Nitem {
|
||||
switch v.Align {
|
||||
case "left", "start", "begin":
|
||||
align = "start"
|
||||
xp = x
|
||||
case "right", "end":
|
||||
align = "end"
|
||||
xp = x + n.Width
|
||||
case "middle", "mid", "center":
|
||||
align = "middle"
|
||||
xp = x + (n.Width / 2)
|
||||
default:
|
||||
align = "start"
|
||||
xp = x
|
||||
}
|
||||
if len(v.Color) == 0 {
|
||||
color = "black"
|
||||
} else {
|
||||
color = v.Color
|
||||
}
|
||||
canvas.Text(xp, y, v.Text, fmt.Sprintf(ntfmt, align, color))
|
||||
y += ls
|
||||
}
|
||||
canvas.Gend()
|
||||
}
|
||||
|
||||
// gc computes the components coordinates
|
||||
func gc(c comp, top, left, gutter int) gcomp {
|
||||
var g gcomp
|
||||
|
||||
// the object and grid dimensions equal, unless explicitly overridden
|
||||
|
||||
if gridw > 0 && gridh > 0 {
|
||||
g.x = colx(c.Col, gridw, gutter, left)
|
||||
g.y = rowy(c.Row, gridh, gutter, top)
|
||||
} else {
|
||||
g.x = colx(c.Col, c.Width, gutter, left)
|
||||
g.y = rowy(c.Row, c.Height, gutter, top)
|
||||
}
|
||||
g.w = c.Width
|
||||
g.h = c.Height
|
||||
return g
|
||||
}
|
||||
|
||||
// display a component in the context of the grid
|
||||
func display(c comp, top, left, gutter int) {
|
||||
g := gc(c, top, left, gutter)
|
||||
component(g, c)
|
||||
}
|
||||
|
||||
// component positions and draws a components and its attributes
|
||||
func component(g gcomp, c comp) {
|
||||
x := g.x
|
||||
y := g.y
|
||||
w := g.w
|
||||
h := g.h
|
||||
fs := w / fontscale
|
||||
fs2 := fs / 2
|
||||
w2 := w / 2
|
||||
h3 := h / 3
|
||||
|
||||
var boxcolor string
|
||||
if len(c.Color) == 0 {
|
||||
boxcolor = globalcolor
|
||||
} else {
|
||||
boxcolor = c.Color
|
||||
}
|
||||
rectstyle := fmt.Sprintf("stroke:%s;stroke-width:1;fill:%s", boxcolor, boxcolor)
|
||||
|
||||
if len(c.Image) > 0 {
|
||||
canvas.Image(x, y, w, h, c.Image)
|
||||
if len(c.Name) > 0 {
|
||||
canvas.Text(x+w2, y+h/3, c.Name,
|
||||
fmt.Sprintf("font-size:%dpx;fill:%s;baseline-shift:50%%", fs, boxcolor))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if strings.HasPrefix(c.Shape, "#") {
|
||||
uselibrary(x, y, w, h, c.Shape)
|
||||
return
|
||||
}
|
||||
|
||||
switch c.Shape {
|
||||
|
||||
case "mobile", "screen":
|
||||
screen(x, y, w, h, 10, boxcolor, bgcolor)
|
||||
canvas.Gstyle(fmt.Sprintf("font-size:%dpx", fs))
|
||||
canvas.Text(x+w/2, y+h/3, c.Name)
|
||||
if len(c.Os) > 0 {
|
||||
canvas.Text(x+w/2, y+h3+fs+2, c.Os, "font-size:60%")
|
||||
}
|
||||
if len(c.Sw) > 0 {
|
||||
canvas.Text(x+w/2, y+h3+fs*2, c.Sw, "font-size:75%")
|
||||
}
|
||||
canvas.Gend()
|
||||
|
||||
case "server":
|
||||
server(x, y, w, h, 10, boxcolor, lcolor)
|
||||
canvas.Text(x+w/2, y+h-10, c.Name, fmt.Sprintf("font-size:%dpx;fill:white", fs))
|
||||
|
||||
case "desktop":
|
||||
l := h / 20
|
||||
desktop(x, y, w, h, l, boxcolor, bgcolor)
|
||||
canvas.Gstyle(fmt.Sprintf("font-size:%dpx", fs))
|
||||
canvas.Text(x+w/2, y+h/3, c.Name)
|
||||
if len(c.Os) > 0 {
|
||||
canvas.Text(x+w/2, y+h3+fs+2, c.Os, "font-size:60%")
|
||||
}
|
||||
if len(c.Sw) > 0 {
|
||||
canvas.Text(x+w/2, y+h3+fs*2, c.Sw, "font-size:75%")
|
||||
}
|
||||
canvas.Gend()
|
||||
|
||||
case "message":
|
||||
l := h / 20
|
||||
pmy := h / 8
|
||||
message(x, y, w, h, l, lcolor, boxcolor)
|
||||
canvas.Gstyle(fmt.Sprintf("font-size:%dpx;fill:white", fs))
|
||||
if len(c.Os) > 0 {
|
||||
canvas.Text(x+w2, y+pmy+fs, c.Os, "font-size:75%")
|
||||
}
|
||||
canvas.Text(x+w2, y+h-10, c.Name, fmt.Sprintf("font-size:%dpx;fill:%s", fs, bgcolor))
|
||||
canvas.Gend()
|
||||
|
||||
case "cloud":
|
||||
r := w / 3
|
||||
xc := (x + w/2) + r/4
|
||||
yc := y + h/2
|
||||
cloud(xc, yc, r, boxcolor)
|
||||
canvas.Text(xc-(r/4), yc+r/2, c.Name, fmt.Sprintf("font-size:%dpx;fill:white", fs))
|
||||
|
||||
case "db":
|
||||
cylinder(x, y+(h/4), w, h-(h/2), h/4, lcolor, boxcolor)
|
||||
canvas.Text(x+w2, y+h3, c.Name, fmt.Sprintf("font-size:%dpx;fill:white", fs))
|
||||
if len(c.Sw) > 0 {
|
||||
canvas.Text(x+w2, y+2*h3, c.Sw, "font-size:75%")
|
||||
}
|
||||
|
||||
case "folder":
|
||||
l := w / 10
|
||||
folder(x, y, w, h, l, boxcolor, lcolor)
|
||||
yp := y + h/2
|
||||
canvas.Gstyle(fmt.Sprintf("font-size:%dpx;fill:white", fs))
|
||||
canvas.Text(x+w/2, yp, c.Name)
|
||||
if len(c.Os) > 0 {
|
||||
canvas.Text(x+w/2, yp+fs+2, c.Os, "font-size:60%")
|
||||
}
|
||||
if len(c.Sw) > 0 {
|
||||
canvas.Text(x+w/2, yp+fs*2, c.Sw, "font-size:75%")
|
||||
}
|
||||
canvas.Gend()
|
||||
|
||||
case "face":
|
||||
fr := (w / 4) - (fs / 2) // (h*3)/8
|
||||
face(x+w/2, y+h/2, fr, linesize, boxcolor, bgcolor)
|
||||
canvas.Text(x+w/2, y+(h/2)+fr+15, c.Name, fmt.Sprintf("font-size:%dpx;fill:black", fs))
|
||||
|
||||
case "role":
|
||||
role(x, y, w, h, boxcolor)
|
||||
canvas.Text(x+w/2, y+h-5, c.Name, fmt.Sprintf("font-size:%dpx;fill:%s", fs, bgcolor))
|
||||
|
||||
case "eaec":
|
||||
l := w / 10
|
||||
eaec(x, y, w, h, l, boxcolor, bgcolor)
|
||||
canvas.Text(x+w/2, y+h-5, c.Name, fmt.Sprintf("font-size:%dpx;fill:%s", fs, bgcolor))
|
||||
|
||||
case "plain":
|
||||
if roundbox {
|
||||
canvas.Roundrect(x, y, w, h, fs2, fs2, rectstyle)
|
||||
} else {
|
||||
canvas.Rect(x, y, w, h, rectstyle)
|
||||
}
|
||||
canvas.Text(x+w2, y+(h/2), c.Name, fmt.Sprintf("font-size:%dpx;fill:white;baseline-shift:-25%%", fs))
|
||||
|
||||
default:
|
||||
canvas.Rect(x, y, w, h3, rectstyle)
|
||||
canvas.Rect(x, y+h3, w, h-h3, "fill:white;stroke-width:1;stroke:gray")
|
||||
canvas.Gstyle(fmt.Sprintf("font-size:%dpx", fs))
|
||||
canvas.Text(x+w2, y+h3, c.Name, "fill:white;baseline-shift:50%")
|
||||
canvas.Text(x+w2, y+h3+fs2, c.Os, "font-size:60%")
|
||||
wordstack(x+w2, (y+h)-fs2, fs, strings.Split(c.Sw, `\n`), "fill-opacity:0.75;font-size:75%")
|
||||
canvas.Gend()
|
||||
}
|
||||
}
|
||||
|
||||
// uselibrary draws a previously defined object
|
||||
func uselibrary(x, y, w, h int, name string) {
|
||||
canvas.Use(x, y, name, fmt.Sprintf(`width="%d"`, w), fmt.Sprintf(`height="%d"`, h))
|
||||
}
|
||||
|
||||
// Object functions
|
||||
func cylinder(x, y, w, h, eh int, fill, tfill string) {
|
||||
f := "fill:" + fill
|
||||
tf := "fill:" + tfill
|
||||
canvas.Rect(x, y, w, h, f)
|
||||
canvas.Ellipse(x+w/2, y+h, w/2, eh, f)
|
||||
canvas.Ellipse(x+w/2, y, w/2, eh, tf)
|
||||
}
|
||||
|
||||
// folder object
|
||||
func folder(x, y, w, h, l int, bcolor, color string) {
|
||||
nl := w / 10
|
||||
xl := x + nl
|
||||
xw := x + w
|
||||
yl := y + nl
|
||||
yh := y + h
|
||||
l2 := nl * 2
|
||||
l3 := nl * 3
|
||||
lh := nl / 2
|
||||
var (
|
||||
xn = []int{xl, xl + l2, xl + l3, xw, xw, xl}
|
||||
yn = []int{y, y, y + lh, y + lh, yh, yh}
|
||||
xf = []int{xw, xw - l, x, x + l}
|
||||
yf = []int{yh, yl + lh, yl + lh, yh}
|
||||
)
|
||||
canvas.Polygon(xn, yn, "fill:"+color)
|
||||
canvas.Polygon(xf, yf, "fill:"+bcolor)
|
||||
}
|
||||
|
||||
// cloud object
|
||||
func cloud(x, y, r int, style string) {
|
||||
small := r / 2
|
||||
medium := (r * 6) / 10
|
||||
canvas.Gstyle("fill:" + style)
|
||||
canvas.Circle(x, y, r)
|
||||
canvas.Circle(x+r, y+small, small)
|
||||
canvas.Circle(x-r-small, y+small, small)
|
||||
canvas.Circle(x-r, y, medium)
|
||||
canvas.Rect(x-r-small, y, r*2+small, r)
|
||||
canvas.Gend()
|
||||
}
|
||||
|
||||
// message object
|
||||
func message(x, y, w, h, l int, bcolor, scolor string) {
|
||||
et := h / 3
|
||||
w2 := w / 2
|
||||
px := w / 8
|
||||
py := h / 8
|
||||
e1x := []int{x, x, x + w, x + w, x + w2, x}
|
||||
e1y := []int{y + et, y + h, y + h, y + et, y + (et * 2), y + et}
|
||||
e2x := []int{x, x + w2, x + w, x + w2, x}
|
||||
e2y := []int{y + et, y, y + et, y + (et * 2), y + et}
|
||||
|
||||
canvas.Polygon(e2x, e2y, "fill:"+bcolor)
|
||||
canvas.Polygon(e1x, e1y, "fill:"+scolor)
|
||||
canvas.Roundrect(x+px, y+py, w-(px*2), h-py, l, l, "fill:"+scolor)
|
||||
canvas.Line(x, y+et, x+w2, y+(et*2), "stroke-width:1;stroke:"+bcolor)
|
||||
canvas.Line(x+w, y+et, x+w2, y+(et*2), "stroke-width:1;stroke:"+bcolor)
|
||||
}
|
||||
|
||||
// eaec person object
|
||||
func eaec(x, y, w, h, l int, scolor, bcolor string) {
|
||||
wu := w / 8
|
||||
hu := h / 12
|
||||
wh := w / 2
|
||||
hh := h / 2
|
||||
sx := []int{x + wu*2, x + wu*6, x + wh}
|
||||
sy := []int{y + hu*6, y + hu*6, y + hu*11}
|
||||
tx := []int{x + wh, x + wh + wu, x + wh, x + wh - wu, x + wh}
|
||||
ty := []int{y + hu*6, y + hu*7, y + hu*10, y + hu*7, y + hu*6}
|
||||
|
||||
canvas.Ellipse(x+wh, y+hu*4, wu+wu/2, hu*2, "fill:"+bcolor)
|
||||
canvas.Roundrect(x+wu, y+hh, w-wu*2, hu*6, l, l, "fill:"+bcolor)
|
||||
canvas.Polygon(sx, sy, "fill:"+scolor)
|
||||
canvas.Polygon(tx, ty, "fill:"+bcolor)
|
||||
}
|
||||
|
||||
// screen object
|
||||
func screen(x, y, w, h, l int, bcolor, color string) {
|
||||
canvas.Roundrect(x, y, w, h, l, l, "fill:"+bcolor)
|
||||
canvas.Rect(x+l, y+l, w-(l*2), h-(l*2), "fill:"+color)
|
||||
}
|
||||
|
||||
// kb (keyboard) object
|
||||
func kb(x, y, w, h, l int, color string) {
|
||||
var xp = []int{x + l, x, x + w, x + w - l}
|
||||
var yp = []int{y, y + h, y + h, y}
|
||||
canvas.Polygon(xp, yp, "fill:"+color)
|
||||
}
|
||||
|
||||
// desktop object
|
||||
func desktop(x, y, w, h, l int, bcolor, color string) {
|
||||
screen(x, y, w, h-l*3, l, bcolor, color)
|
||||
kb(x, y+h-(l*2), w+l, l*2, l*2, bcolor)
|
||||
}
|
||||
|
||||
// face object
|
||||
func face(x, y, r, l int, color, fcolor string) {
|
||||
fu := r / 10 // "face unit"
|
||||
ep := 3 * fu
|
||||
my := y + ep
|
||||
canvas.Circle(x, y, r, fmt.Sprintf("fill:%s;stroke-width:%dpx;stroke:%s", fcolor, l, color))
|
||||
canvas.Circle(x+ep, y-ep, r/10, "fill:"+color)
|
||||
canvas.Circle(x-ep, y-ep, r/10, "fill:"+color)
|
||||
canvas.Qbez(x+ep, my, x, y+ep*2, x-ep, my, fmt.Sprintf("fill:%s;stroke-width:%dpx;stroke:%s", color, l, color))
|
||||
}
|
||||
|
||||
// server object
|
||||
func server(x, y, w, h, l int, bcolor, color string) {
|
||||
var xp = []int{x + (l * 2), (x + w) - (l * 2), (x + w) - l, x + l}
|
||||
var yp = []int{y, y, y + l, y + l}
|
||||
canvas.Polygon(xp, yp, "fill:"+color)
|
||||
canvas.Roundrect(x, y+l, w, h-l, 5, 5, "fill:"+bcolor)
|
||||
canvas.Gstyle("stroke:" + color)
|
||||
yl := y + (l / 2) + h/4
|
||||
spacing := w / 5
|
||||
for r := 0; r < 2; r++ {
|
||||
xl := x + l
|
||||
for c := 0; c < 2; c++ {
|
||||
canvas.Line(xl, yl, xl+spacing, yl)
|
||||
xl += spacing + 10
|
||||
}
|
||||
yl += h / 4
|
||||
}
|
||||
canvas.Gend()
|
||||
canvas.Circle((x+w)-l, y+h/2, l/2, "fill:"+color)
|
||||
}
|
||||
|
||||
// role object
|
||||
func role(x, y, w, h int, color string) {
|
||||
hs := h / 20
|
||||
var xp = []int{x, x, x + w/3, x + w/2, x + (w / 2) + (w / 6), x + w, x + w}
|
||||
var yp = []int{y + h, y + (16 * hs), y + (12 * hs), y + (14 * hs), y + (12 * hs), y + (16 * hs), y + h}
|
||||
|
||||
// var xp = []int{x, x, x + w/2, x + w, x + w}
|
||||
// var yp = []int{y + h, y + (h5 * 4), y + (h5 * 2), y + (h5 * 4), y + h}
|
||||
canvas.Gstyle("fill:" + color)
|
||||
canvas.Polygon(xp, yp)
|
||||
canvas.Ellipse(x+w/2, (y + h/3), w/5, h/3) // "stroke:white;stroke-width:2")
|
||||
canvas.Gend()
|
||||
}
|
||||
|
||||
// sloper computes the slope and r of a line
|
||||
func sloper(x1, y1, x2, y2 int) (m, r float64) {
|
||||
dy := float64(y1 - y2)
|
||||
dx := float64(x1 - x2)
|
||||
m = dy / dx
|
||||
r = math.Atan2(dy, dx) * (180 / math.Pi)
|
||||
return m, r
|
||||
}
|
||||
|
||||
// rowy computes the y position of a row
|
||||
func rowy(n, h, g, t int) int { return t + (n * g) + (n * h) }
|
||||
|
||||
// colx computes the x position of a column
|
||||
func colx(n, w, g, l int) int { return l + (n * g) + (n * w) }
|
||||
|
||||
// compass returns the coordinates of a compass point
|
||||
func compass(g gcomp, point string) (cx, cy int, dir string) {
|
||||
switch point {
|
||||
case "nw":
|
||||
cx, cy, dir = g.x, g.y, "r"
|
||||
case "nnw":
|
||||
cx, cy, dir = g.x+g.w/4, g.y, "d"
|
||||
case "nne":
|
||||
cx, cy, dir = (g.x+g.w)-(g.w/4), g.y, "d"
|
||||
case "n":
|
||||
cx, cy, dir = g.x+(g.w/2), g.y, "d"
|
||||
case "ne":
|
||||
cx, cy, dir = g.x+g.w, g.y, "l"
|
||||
case "w":
|
||||
cx, cy, dir = g.x, g.y+g.h/2, "r"
|
||||
case "wnw":
|
||||
cx, cy, dir = g.x, g.y+g.h/4, "r"
|
||||
case "wsw":
|
||||
cx, cy, dir = g.x, (g.y+g.h)-(g.h/4), "r"
|
||||
case "ese":
|
||||
cx, cy, dir = g.x+g.w, (g.y+g.h)-(g.h/4), "l"
|
||||
case "ene":
|
||||
cx, cy, dir = g.x+g.w, g.y+(g.h/4), "l"
|
||||
case "c":
|
||||
cx, cy, dir = g.x+(g.w/2), g.y+(g.h/2), "n"
|
||||
case "e":
|
||||
cx, cy, dir = g.x+g.w, g.y+(g.h/2), "l"
|
||||
case "sw":
|
||||
cx, cy, dir = g.x, g.y+g.h, "r"
|
||||
case "ssw":
|
||||
cx, cy, dir = g.x+(g.w/4), g.y+g.h, "u"
|
||||
case "sse":
|
||||
cx, cy, dir = (g.x+g.w)-(g.w/4), g.y+g.h, "u"
|
||||
case "s":
|
||||
cx, cy, dir = g.x+(g.w/2), g.y+g.h, "u"
|
||||
case "se":
|
||||
cx, cy, dir = g.x+g.w, g.y+g.h, "l"
|
||||
}
|
||||
return cx, cy, dir
|
||||
}
|
||||
|
||||
// connect two components
|
||||
func connect(c1 gcomp, p1 string, c2 gcomp, p2 string, label string, mark string, dir string, color string) {
|
||||
x1, y1, d1 := compass(c1, p1)
|
||||
x2, y2, d2 := compass(c2, p2)
|
||||
linelabel(x1, y1, x2, y2, label, mark, d1, d2, dir, color)
|
||||
}
|
||||
|
||||
// linestyle returns the style for lines
|
||||
func linestyle(color string) string {
|
||||
return fmt.Sprintf(linefmt, color)
|
||||
}
|
||||
|
||||
// linelabel determines the connection and arrow geometry
|
||||
func linelabel(x1, y1, x2, y2 int, label string, mark string, d1 string, d2 string, dir string, color string) {
|
||||
aw := linesize * 4
|
||||
ah := linesize * 3
|
||||
|
||||
if len(color) == 0 {
|
||||
color = lcolor
|
||||
}
|
||||
switch mark {
|
||||
case "b":
|
||||
lx1, ly1 := arrow(x1, y1, aw, ah, d1, color)
|
||||
lx2, ly2 := arrow(x2, y2, aw, ah, d2, color)
|
||||
doline(lx1, ly1, lx2, ly2, linestyle(color), dir, label)
|
||||
|
||||
case "s":
|
||||
lx1, ly1 := arrow(x1, y1, aw, ah, d1, color)
|
||||
doline(lx1, ly1, x2, y2, linestyle(color), dir, label)
|
||||
|
||||
case "d":
|
||||
lx2, ly2 := arrow(x2, y2, aw, ah, d2, color)
|
||||
doline(x1, y1, lx2, ly2, linestyle(color), dir, label)
|
||||
|
||||
default:
|
||||
doline(x1, y1, x2, y2, linestyle(color), dir, label)
|
||||
}
|
||||
}
|
||||
|
||||
// doline draws a line between to coordinates
|
||||
func doline(x1, y1, x2, y2 int, style, direction, label string) {
|
||||
var labelstyle string
|
||||
var upflag bool
|
||||
|
||||
if italiclabel {
|
||||
labelstyle = "font-style:italic;"
|
||||
}
|
||||
|
||||
tadjust := 6
|
||||
mx := (x2 - x1) / 2
|
||||
my := (y2 - y1) / 2
|
||||
lx := x1 + mx
|
||||
ly := y1 + my
|
||||
m, _ := sloper(x1, y1, x2, y2)
|
||||
hline := m == 0
|
||||
vline := m == math.Inf(-1) || m == math.Inf(1)
|
||||
straight := hline || vline
|
||||
|
||||
switch {
|
||||
case m < 0: // upwards line
|
||||
upflag = true
|
||||
labelstyle += "text-anchor:end;"
|
||||
lx -= tadjust
|
||||
case hline: // horizontal line
|
||||
labelstyle += "text-anchor:middle;baseline-shift:20%;"
|
||||
ly -= tadjust
|
||||
case m > 0: // downwards line
|
||||
upflag = false
|
||||
labelstyle += "text-anchor:start;"
|
||||
lx += tadjust
|
||||
}
|
||||
if arc && !straight {
|
||||
cx, cy := x1, y2 // initial control points
|
||||
// fmt.Fprintf(os.Stderr, "%s slope = %.3f\n", label, m)
|
||||
if upflag {
|
||||
if direction == "ccw" {
|
||||
cx, cy = x2, y1
|
||||
} else {
|
||||
cx, cy = x1, y2
|
||||
}
|
||||
} else {
|
||||
if direction == "ccw" {
|
||||
cx, cy = x1, y2
|
||||
} else {
|
||||
cx, cy = x2, y1
|
||||
}
|
||||
}
|
||||
canvas.Qbez(x1, y1, cx, cy, x2, y2, style)
|
||||
labelstyle += "text-anchor:middle"
|
||||
canvas.Text(lx, ly, label, labelstyle)
|
||||
} else {
|
||||
canvas.Line(x1, y1, x2, y2, style)
|
||||
canvas.Text(lx, ly, label, labelstyle) // midpoint
|
||||
}
|
||||
}
|
||||
|
||||
// wordstack displays text in a left-justified stack
|
||||
func wordstack(x, y, fs int, s []string, style ...string) {
|
||||
ls := fs + 2
|
||||
for i := len(s); i > 0; i-- {
|
||||
canvas.Text(x, y, s[i-1], style...)
|
||||
y -= ls
|
||||
}
|
||||
}
|
||||
|
||||
// arrow constructs line-ending arrows according to connecting points
|
||||
func arrow(x, y, w, h int, dir string, color string) (xl, yl int) {
|
||||
var xp = []int{x, x, x, x}
|
||||
var yp = []int{y, y, y, y}
|
||||
|
||||
n := notchsize
|
||||
switch dir {
|
||||
case "r":
|
||||
xp[1] = x - w
|
||||
yp[1] = y - h/2
|
||||
xp[2] = (x - w) + n
|
||||
yp[2] = y
|
||||
xp[3] = x - w
|
||||
yp[3] = y + h/2
|
||||
xl, yl = xp[2], y
|
||||
case "l":
|
||||
xp[1] = x + w
|
||||
yp[1] = y - h/2
|
||||
xp[2] = (x + w) - n
|
||||
yp[2] = y
|
||||
xp[3] = x + w
|
||||
yp[3] = y + h/2
|
||||
xl, yl = xp[2], y
|
||||
case "u":
|
||||
xp[1] = x - w/2
|
||||
yp[1] = y + h
|
||||
xp[2] = x
|
||||
yp[2] = (y + h) - n
|
||||
xp[3] = x + w/2
|
||||
yp[3] = y + h
|
||||
xl, yl = x, yp[2]
|
||||
case "d":
|
||||
xp[1] = x - w/2
|
||||
yp[1] = y - h
|
||||
xp[2] = x
|
||||
yp[2] = (y - h) + n
|
||||
xp[3] = x + w/2
|
||||
yp[3] = y - h
|
||||
xl, yl = x, yp[2]
|
||||
}
|
||||
canvas.Polygon(xp, yp, "fill:"+color+";fill-opacity:"+lopacity)
|
||||
return xl, yl
|
||||
}
|
||||
|
||||
// init processes command line arguments
|
||||
func init() {
|
||||
flag.IntVar(&width, "w", 1024, "width")
|
||||
flag.IntVar(&height, "h", 768, "height")
|
||||
flag.IntVar(&linesize, "l", 4, "line weight")
|
||||
flag.IntVar(&labelfs, "lf", 16, "label font size (px)")
|
||||
flag.IntVar(&fontscale, "f", 10, "font scaling factor")
|
||||
flag.IntVar(¬chsize, "n", 0, "arrow notch size")
|
||||
flag.IntVar(&groupmargin, "gm", 10, "group margin")
|
||||
flag.StringVar(&bgcolor, "bg", "white", "background color")
|
||||
flag.StringVar(&title, "t", "comp grid", "title")
|
||||
flag.BoolVar(&italiclabel, "il", false, "italic labels")
|
||||
flag.BoolVar(&showtitle, "showtitle", false, "Show the title")
|
||||
flag.BoolVar(&showtimestamp, "time", false, "Show a timestamp")
|
||||
flag.BoolVar(&roundbox, "roundbox", false, "make boxes round")
|
||||
flag.BoolVar(&arc, "arc", false, "use arcs to connect")
|
||||
flag.StringVar(&guide, "g", "", "grid guide: WxHxRxC")
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
// for every file (or stdin) make a component diagram
|
||||
func main() {
|
||||
canvas.Start(width, height)
|
||||
canvas.Title(title)
|
||||
background(bgcolor)
|
||||
canvas.Gstyle(fmt.Sprintf(globalstyle, labelfs, linesize, lopacity))
|
||||
|
||||
if len(flag.Args()) == 0 {
|
||||
docomp("")
|
||||
} else {
|
||||
for _, f := range flag.Args() {
|
||||
docomp(f)
|
||||
}
|
||||
}
|
||||
canvas.Gend()
|
||||
canvas.End()
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
<!-- compx test pattern -->
|
||||
<components gutter="20" top="20" left="20" gc="black">
|
||||
<legend row="3" col="0" width="200" height="120">
|
||||
<litem color="rgb(0,0,0)">Component</litem>
|
||||
<litem color="rgb(127,127,127)">Another Thing</litem>
|
||||
</legend>
|
||||
<legend row="4" col="1" width="200" height="120">
|
||||
<litem color="rgb(0,127,0)" type="line">Public</litem>
|
||||
<litem color="rgb(127,127,127)" type="line">Confidential</litem>
|
||||
<litem color="rgb(0,0,127)" type="line">Restricted</litem>
|
||||
<litem color="rgb(127,0,0)" type="line">Highly Restricted</litem>
|
||||
</legend>
|
||||
<note row="0" col="4" width="200" height="120" size="16" align="middle">
|
||||
<nitem align="center" color="red">Note</nitem>
|
||||
<nitem align="left">This is the test pattern for compx</nitem>
|
||||
<nitem>for best results, set the width to be</nitem>
|
||||
<nitem>at least 1300</nitem>
|
||||
<nitem></nitem>
|
||||
<nitem align="right" color="red">the management</nitem>
|
||||
</note>
|
||||
<note row="5" col="0" width="200" height="120" size="30">
|
||||
<nitem>Here is another Note</nitem>
|
||||
<nitem color="blue">This one is bigger</nitem>
|
||||
</note>
|
||||
<note>
|
||||
<nitem>a stupid note</nitem>
|
||||
<nitem>you did not specify anything</nitem>
|
||||
</note>
|
||||
<group brow="0" bcol="0" erow="4" ecol="3" width="200" height="120" color="lightgray"/>
|
||||
<group brow="2" bcol="0" erow="2" ecol="4" width="200" height="120" color="lightsteelblue"/>
|
||||
<group brow="0" bcol="3" erow="4" ecol="3" width="200" height="120" color="gray"/>
|
||||
<comp id="screen" name="Tablet" row="3" col="4" width="200" height="120" sw="Dignus Here" os="Widget/OS" shape="screen"/>
|
||||
<comp id="face" name="Face it" row="0" col="3" width="200" height="120" shape="face"></comp>
|
||||
<comp id="role" name="A Role" row="4" col="3" width="200" height="120" shape="role" color="steelblue" />
|
||||
<comp id="center" name="Envelope" row="2" col="2" width="200" height="120" shape="message" os="message" color="maroon" />
|
||||
<comp id="dummy" name="Widget" row="4" col="4" width="200" height="120" sw="Dignus\nHere" os="Widget/OS" />
|
||||
<comp id="cloud" name="Internet" row="4" col="0" width="200" height="120" sw="C" os="mid" color="gray" shape="cloud" />
|
||||
<comp id="null" name="Nameless" row="1" col="0" width="200" height="60" sw="foo" shape="plain">
|
||||
<connect mark="d" sloc="s" dloc="n" dest="west" />
|
||||
</comp>
|
||||
<comp id="north" name="North" row="0" col="2" width="200" height="120" sw="N" os="top" shape="db">
|
||||
<connect mark="d" dest="center" sloc="ssw" dloc="nnw" color="rgb(127,0,0)">[1]</connect>
|
||||
<connect mark="d" dest="center" sloc="s" dloc="n" color="rgb(127,0,0)">[2]</connect>
|
||||
<connect mark="d" dest="center" sloc="sse" dloc="nne" color="rgb(127,0,0)">[3]</connect>
|
||||
<connect mark="d" dest="east" sloc="e" dloc="n">North to East</connect>
|
||||
</comp>
|
||||
<comp id="west" name="West" row="2" col="0" width="200" height="120" sw="W" os="left" shape="server">
|
||||
<connect mark="d" dest="center" sloc="ne" dloc="nw" color="rgb(0,0,127)">[4]</connect>
|
||||
<connect mark="d" dest="center" sloc="ene" dloc="wnw" color="rgb(0,0,127)">[5]</connect>
|
||||
<connect mark="d" dest="center" sloc="e" dloc="w" color="rgb(0,0,127)">[6]</connect>
|
||||
<connect mark="d" dest="center" sloc="ese" dloc="wsw" color="rgb(0,0,127)">[7]</connect>
|
||||
<connect mark="d" dest="center" sloc="se" dloc="sw" color="rgb(0,0,127)">[8]</connect>
|
||||
<connect mark="d" dest="north" sloc="n" dloc="w">West to North</connect>
|
||||
</comp>
|
||||
<comp id="south" name="South" row="4" col="2" width="200" height="120" sw="S" os="bottom" color="gray" shape="folder">
|
||||
<connect mark="d" dest="center" sloc="nnw" dloc="ssw" color="rgb(0,127,0)">[9]</connect>
|
||||
<connect mark="d" dest="center" sloc="n" dloc="s" color="rgb(0,127,0)">[10]</connect>
|
||||
<connect mark="d" dest="center" sloc="nne" dloc="sse" color="rgb(0,127,0)">[11]</connect>
|
||||
<connect mark="d" dest="west" sloc="w" dloc="s">South to West</connect>
|
||||
</comp>
|
||||
<comp id="east" name="East" row="2" col="4" width="200" height="120" sw="E" os="right" shape="desktop">
|
||||
<connect mark="d" dest="center" sloc="nw" dloc="ne" color="rgb(0,0,127)">[12]</connect>
|
||||
<connect mark="d" dest="center" sloc="wnw" dloc="ene" color="rgb(0,0,127)">[13]</connect>
|
||||
<connect mark="d" dest="center" sloc="w" dloc="e" color="rgb(0,0,127)">[14]</connect>
|
||||
<connect mark="d" dest="center" sloc="wsw" dloc="ese" color="rgb(0,0,127)">[15]</connect>
|
||||
<connect mark="d" dest="center" sloc="sw" dloc="se" color="rgb(0,0,127)">[16]</connect>
|
||||
<connect mark="d" dest="south" sloc="s" dloc="e">East to South</connect>
|
||||
</comp>
|
||||
</components>
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
// cube: draw cubes
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/ajstarks/svgo"
|
||||
)
|
||||
|
||||
var canvas = svg.New(os.Stdout)
|
||||
|
||||
// randcolor returns a random color
|
||||
func randcolor() string {
|
||||
rgb := []byte{0, 0, 0} // read error returns black
|
||||
rand.Read(rgb)
|
||||
return fmt.Sprintf("fill:rgb(%d,%d,%d)", rgb[0], rgb[1], rgb[2])
|
||||
}
|
||||
|
||||
// rcube makes a cube with three visible faces, each with a random color
|
||||
func rcube(x, y, l int) {
|
||||
tx := []int{x, x + (l * 3), x, x - (l * 3), x}
|
||||
ty := []int{y, y + (l * 2), y + (l * 4), y + (l * 2), y}
|
||||
|
||||
lx := []int{x - (l * 3), x, x, x - (l * 3), x - (l * 3)}
|
||||
ly := []int{y + (l * 2), y + (l * 4), y + (l * 8), y + (l * 6), y + (l * 2)}
|
||||
|
||||
rx := []int{x + (l * 3), x + (l * 3), x, x, x + (l * 3)}
|
||||
ry := []int{y + (l * 2), y + (l * 6), y + (l * 8), y + (l * 4), y + (l * 2)}
|
||||
|
||||
canvas.Polygon(tx, ty, randcolor())
|
||||
canvas.Polygon(lx, ly, randcolor())
|
||||
canvas.Polygon(rx, ry, randcolor())
|
||||
}
|
||||
|
||||
// lattice draws a grid of cubes, n rows deep.
|
||||
// The grid begins at (xp, yp), with hspace between cubes in a row, and vspace between rows.
|
||||
func lattice(xp, yp, w, h, size, hspace, vspace, n int, bgcolor string) {
|
||||
if bgcolor == "" {
|
||||
canvas.Rect(0, 0, w, h, randcolor())
|
||||
} else {
|
||||
canvas.Rect(0, 0, w, h, "fill:"+bgcolor)
|
||||
}
|
||||
y := yp
|
||||
for r := 0; r < n; r++ {
|
||||
for x := xp; x < w; x += hspace {
|
||||
rcube(x, y, size)
|
||||
}
|
||||
y += vspace
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
var (
|
||||
width = flag.Int("w", 600, "canvas width")
|
||||
height = flag.Int("h", 600, "canvas height")
|
||||
x = flag.Int("x", 60, "begin x location")
|
||||
y = flag.Int("y", 60, "begin y location")
|
||||
size = flag.Int("size", 20, "cube size")
|
||||
rows = flag.Int("rows", 3, "rows")
|
||||
hs = flag.Int("hs", 120, "horizontal spacing")
|
||||
vs = flag.Int("vs", 160, "vertical spacing")
|
||||
bg = flag.String("bg", "", "background")
|
||||
)
|
||||
flag.Parse()
|
||||
canvas.Start(*width, *height)
|
||||
lattice(*x, *y, *width, *height, *size, *hs, *vs, *rows, *bg)
|
||||
canvas.End()
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
// f50 -- given a search term, display 10x5 image grid, sorted by interestingness
|
||||
// +build !appengine
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
"github.com/ajstarks/svgo"
|
||||
)
|
||||
|
||||
// FlickrResp defines the Flickr response
|
||||
type FlickrResp struct {
|
||||
Stat string `xml:"stat,attr"`
|
||||
Photos Photos `xml:"photos"`
|
||||
}
|
||||
|
||||
// Photos defines a set of Flickr photos
|
||||
type Photos struct {
|
||||
Page string `xml:"page,attr"`
|
||||
Pages string `xml:"pages,attr"`
|
||||
Perpage string `xml:"perpage,attr"`
|
||||
Total string `xml:"total,attr"`
|
||||
Photo []Photo `xml:"photo"`
|
||||
}
|
||||
|
||||
// Photo defines a Flickr photo
|
||||
type Photo struct {
|
||||
Id string `xml:"id,attr"`
|
||||
Owner string `xml:"owner,attr"`
|
||||
Secret string `xml:"secret,attr"`
|
||||
Server string `xml:"server,attr"`
|
||||
Farm string `xml:"farm,attr"`
|
||||
Title string `xml:"title,attr"`
|
||||
Ispublic string `xml:"ispublic,attr"`
|
||||
Isfriend string `xml:"isfriend,attr"`
|
||||
IsFamily string `xml:"isfamily,attr"`
|
||||
}
|
||||
|
||||
var (
|
||||
width = 805
|
||||
height = 500
|
||||
canvas = svg.New(os.Stdout)
|
||||
)
|
||||
|
||||
const (
|
||||
apifmt = "https://api.flickr.com/services/rest/?method=%s&api_key=%s&%s=%s&per_page=50&sort=interestingness-desc"
|
||||
urifmt = "http://farm%s.static.flickr.com/%s/%s.jpg"
|
||||
apiKey = "YOURKEY"
|
||||
textStyle = "font-family:Calibri,sans-serif; font-size:48px; fill:white; text-anchor:start"
|
||||
imageWidth = 75
|
||||
imageHeight = 75
|
||||
)
|
||||
|
||||
// FlickrAPI calls the API given a method with single name/value pair
|
||||
func flickrAPI(method, name, value string) string {
|
||||
return fmt.Sprintf(apifmt, method, apiKey, name, value)
|
||||
}
|
||||
|
||||
// makeURI converts the elements of a photo into a Flickr photo URI
|
||||
func makeURI(p Photo, imsize string) string {
|
||||
im := p.Id + "_" + p.Secret
|
||||
|
||||
if len(imsize) > 0 {
|
||||
im += "_" + imsize
|
||||
}
|
||||
return fmt.Sprintf(urifmt, p.Farm, p.Server, im)
|
||||
}
|
||||
|
||||
// imageGrid reads the response from Flickr, and creates a grid of images
|
||||
func imageGrid(f FlickrResp, x, y, cols, gutter int, imgsize string) {
|
||||
if f.Stat != "ok" {
|
||||
fmt.Fprintf(os.Stderr, "Status: %v\n", f.Stat)
|
||||
return
|
||||
}
|
||||
xpos := x
|
||||
for i, p := range f.Photos.Photo {
|
||||
if i%cols == 0 && i > 0 {
|
||||
xpos = x
|
||||
y += (imageHeight + gutter)
|
||||
}
|
||||
canvas.Link(makeURI(p, ""), p.Title)
|
||||
canvas.Image(xpos, y, imageWidth, imageHeight, makeURI(p, "s"))
|
||||
canvas.LinkEnd()
|
||||
xpos += (imageWidth + gutter)
|
||||
}
|
||||
}
|
||||
|
||||
// fs calls the Flickr API to perform a photo search
|
||||
func fs(s string) {
|
||||
var f FlickrResp
|
||||
r, weberr := http.Get(flickrAPI("flickr.photos.search", "text", s))
|
||||
if weberr != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", weberr)
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
xmlerr := xml.NewDecoder(r.Body).Decode(&f)
|
||||
if xmlerr != nil || r.StatusCode != http.StatusOK {
|
||||
fmt.Fprintf(os.Stderr, "%v (status=%d)\n", xmlerr, r.StatusCode)
|
||||
return
|
||||
}
|
||||
canvas.Title(s)
|
||||
imageGrid(f, 5, 5, 10, 5, "s")
|
||||
canvas.Text(20, height-40, s, textStyle)
|
||||
}
|
||||
|
||||
// for each search term on the commandline, create a photo grid
|
||||
func main() {
|
||||
for i := 1; i < len(os.Args); i++ {
|
||||
canvas.Start(width, height)
|
||||
canvas.Rect(0, 0, width, height, "fill:black")
|
||||
fs(url.QueryEscape(os.Args[i]))
|
||||
canvas.End()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
// fe: SVG Filter Effect example from http://www.w3.org/TR/SVG/filters.html#AnExample
|
||||
// +build !appengine
|
||||
|
||||
package main
|
||||
|
||||
|
||||
import (
|
||||
"github.com/ajstarks/svgo"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
canvas := svg.New(os.Stdout)
|
||||
width := 410
|
||||
height := 120
|
||||
|
||||
canvas.Start(width, height)
|
||||
canvas.Title(`SVGo Filter Example`)
|
||||
canvas.Desc(`Combines multiple filter primitives to produce a 3D lighting effect`)
|
||||
|
||||
gfs := svg.Filterspec{In: "SourceAlpha", Result: "blur"}
|
||||
ofs := svg.Filterspec{In: "blur", Result: "offsetBlur"}
|
||||
sfs := svg.Filterspec{In: "blur", Result: "specOut"}
|
||||
cfs1 := svg.Filterspec{In: "specOut", In2: "SourceAlpha", Result: "specOut"}
|
||||
cfs2 := svg.Filterspec{In: "SourceGraphic", In2: "specOut", Result: "litPaint"}
|
||||
|
||||
// define the filters
|
||||
canvas.Def()
|
||||
canvas.Filter("myFilter")
|
||||
canvas.FeGaussianBlur(gfs, 4, 4)
|
||||
canvas.FeOffset(ofs, 4, 4)
|
||||
canvas.FeSpecularLighting(sfs, 5, .75, 20, "#bbbbbb")
|
||||
canvas.FePointLight(-5000, -10000, 20000)
|
||||
canvas.FeSpecEnd()
|
||||
canvas.FeComposite(cfs1, "in", 0, 0, 0, 0)
|
||||
canvas.FeComposite(cfs2, "arithmetic", 0, 1, 1, 0)
|
||||
canvas.FeMerge([]string{ofs.Result, cfs2.Result})
|
||||
canvas.Fend()
|
||||
canvas.DefEnd()
|
||||
|
||||
// specify the graphic
|
||||
canvas.Gid("SVG")
|
||||
canvas.Path("M50,90 C0,90 0,30 50,30 L150,30 C200,30 200,90 150,90 z", "fill:none;stroke:#D90000;stroke-width:10")
|
||||
canvas.Path("M60,80 C30,80 30,40 60,40 L140,40 C170,40 170,80 140,80 z", "fill:#D90000")
|
||||
canvas.Text(52, 76, "SVG", "fill:white;stroke:black;font-size:45;font-family:Verdana")
|
||||
canvas.Gend()
|
||||
|
||||
canvas.Rect(0, 0, width, height, "stroke:black;fill:white")
|
||||
canvas.Use(0, 0, "#SVG") // plain graphic
|
||||
canvas.Use(200, 0, "#SVG", `filter="url(#myFilter)"`) // filter applied
|
||||
canvas.End()
|
||||
}
|
||||
|
|
@ -0,0 +1,634 @@
|
|||
#SVGo: A Go library for SVG generation#
|
||||
|
||||
The library generates SVG as defined by the Scalable Vector Graphics 1.1 Specification (<http://www.w3.org/TR/SVG11/>).
|
||||
Output goes to the specified io.Writer, operation occur with Go's float64 type.
|
||||
|
||||
## Supported SVG elements and functions ##
|
||||
|
||||
### Shapes, lines, text
|
||||
|
||||
circle, ellipse, polygon, polyline, rect (including roundrects), line, text
|
||||
|
||||
### Paths
|
||||
|
||||
general, arc, cubic and quadratic bezier paths,
|
||||
|
||||
### Image and Gradients
|
||||
|
||||
image, linearGradient, radialGradient,
|
||||
|
||||
### Transforms ###
|
||||
|
||||
translate, rotate, scale, skewX, skewY
|
||||
|
||||
### Filter Effects
|
||||
|
||||
filter, feBlend, feColorMatrix, feColorMatrix, feComponentTransfer, feComposite, feConvolveMatrix, feDiffuseLighting,
|
||||
feDisplacementMap, feDistantLight, feFlood, feGaussianBlur, feImage, feMerge, feMorphology, feOffset, fePointLight,
|
||||
feSpecularLighting, feSpotLight,feTile, feTurbulence
|
||||
|
||||
|
||||
### Metadata elements ###
|
||||
|
||||
desc, defs, g (style, transform, id), marker, mask, pattern, title, (a)ddress, link, script, use
|
||||
|
||||
## Building and Usage ##
|
||||
|
||||
See svgdef.[svg|png|pdf] for a graphical view of the function calls
|
||||
|
||||
|
||||
Usage: (assuming GOPATH is set)
|
||||
|
||||
go get github.com/ajstarks/svgo/float
|
||||
|
||||
|
||||
You can use godoc to browse the documentation from the command line:
|
||||
|
||||
$ go doc github.com/ajstarks/svgo/float
|
||||
|
||||
|
||||
a minimal program, to generate SVG to standard output.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/ajstarks/svgo/float"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
width := 500.0
|
||||
height := 500.0
|
||||
canvas := svg.New(os.Stdout)
|
||||
canvas.Start(width, height)
|
||||
canvas.Circle(width/2, height/2, 100)
|
||||
canvas.Text(width/2, height/2, "Hello, SVG", "text-anchor:middle;font-size:30px;fill:white")
|
||||
canvas.End()
|
||||
}
|
||||
|
||||
Drawing in a web server: (http://localhost:2003/circle)
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"github.com/ajstarks/svgo/float"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func main() {
|
||||
http.Handle("/circle", http.HandlerFunc(circle))
|
||||
err := http.ListenAndServe(":2003", nil)
|
||||
if err != nil {
|
||||
log.Fatal("ListenAndServe:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func circle(w http.ResponseWriter, req *http.Request) {
|
||||
w.Header().Set("Content-Type", "image/svg+xml")
|
||||
s := svg.New(w)
|
||||
s.Start(500, 500)
|
||||
s.Circle(250, 250, 125, "fill:none;stroke:black")
|
||||
s.End()
|
||||
}
|
||||
|
||||
You may view the SVG output with a browser that supports SVG (tested on Chrome, Opera, Firefox and Safari), or any other SVG user-agent such as Batik Squiggle.
|
||||
|
||||
### Graphics Sketching with SVGo and svgplay ###
|
||||
|
||||
Combined with the svgplay command, SVGo can be used to "sketch" with code in a browser.
|
||||
|
||||
To use svgplay and SVGo, first go to a directory with your code, and run:
|
||||
|
||||
$ svgplay -f # use the floating point version
|
||||
2014/06/25 22:05:28 ☠ ☠ ☠ Warning: this server allows a client connecting to 127.0.0.1:1999 to execute code on this computer ☠ ☠ ☠
|
||||
|
||||
Next open your browser to the svgplay server you just started.
|
||||
svgplay only listens on localhost, and uses port 1999 (guess which year SVG was first introduced) by default
|
||||
|
||||
http://localhost:1999/
|
||||
|
||||
Enter your code in the textarea, and when you are ready to run press Shift--Enter. The code will be compiled, with the results
|
||||
on the right. To update, change the code and repeat. Note that compilation errors are shown in red under the code. In order for svgplay/SVGo to work, make sure that the io.Writer specified with the New function is os.Stdout.
|
||||
|
||||
|
||||
If you want to sketch with an existing file, enter its URL:
|
||||
|
||||
http://localhost:1999/foo.go
|
||||
|
||||

|
||||
|
||||
|
||||
### SVGo Papers and presentations ###
|
||||
|
||||
* SVGo paper from SVGOpen 2011 <http://www.svgopen.org/2011/papers/34-SVGo_a_Go_Library_for_SVG_generation>
|
||||
|
||||
* Programming Pictures with SVGo <https://speakerdeck.com/u/ajstarks/p/programming-pictures-with-svgo>
|
||||
|
||||
* SVGo Workshop <https://speakerdeck.com/u/ajstarks/p/svgo-workshop>
|
||||
|
||||
* The Other Side of Go: Programming Pictures <https://www.youtube.com/watch?v=nuDO1oQxARs>
|
||||
|
||||
|
||||
|
||||
## Functions and types ##
|
||||
|
||||
Many functions use x, y to specify an object's location, and w, h to specify the object's width and height.
|
||||
Where applicable, a final optional argument specifies the style to be applied to the object.
|
||||
The style strings follow the SVG standard; name:value pairs delimited by semicolons, or a
|
||||
series of name="value" pairs. For example: `"fill:none; opacity:0.3"` or `fill="none" opacity="0.3"` (see: <http://www.w3.org/TR/SVG11/styling.html>)
|
||||
|
||||
The SVG type:
|
||||
|
||||
type SVG struct {
|
||||
Writer io.Writer
|
||||
Decimals int
|
||||
}
|
||||
|
||||
Most operations are methods on this type, specifying the destination io.Writer and decimal precision.
|
||||
|
||||
|
||||
The Offcolor type:
|
||||
|
||||
type Offcolor struct {
|
||||
Offset uint8
|
||||
Color string
|
||||
Opacity float64
|
||||
}
|
||||
|
||||
is used to specify the offset, color, and opacity of stop colors in linear and radial gradients
|
||||
|
||||
The Filterspec type:
|
||||
|
||||
type Filterspec struct {
|
||||
In string
|
||||
In2 string
|
||||
Result string
|
||||
}
|
||||
|
||||
is used to specify inputs and results for filter effects
|
||||
|
||||
|
||||
### Structure, Scripting, Metadata, Transformation and Links ###
|
||||
|
||||
New(w io.Writer) *SVG
|
||||
Constructor, Specify the output destination, and the number of digits after the decimal point (default 2)
|
||||
|
||||
Start(w float64, h float64, attributes ...string)
|
||||
begin the SVG document with the width w and height h. Optionally add additional elements
|
||||
(such as additional namespaces or scripting events)
|
||||
<http://www.w3.org/TR/SVG11/struct.html#SVGElement>
|
||||
|
||||
Startview(w, h, minx, miny, vw, vh float64)
|
||||
begin the SVG document with the width w, height h, with a viewBox at minx, miny, vw, vh.
|
||||
<http://www.w3.org/TR/SVG11/struct.html#SVGElement>
|
||||
|
||||
Startunit(w float64, h float64, unit string, ns ...string)
|
||||
begin the SVG document, with width and height in the specified units. Optionally add additional elements
|
||||
(such as additional namespaces or scripting events)
|
||||
<http://www.w3.org/TR/SVG11/struct.html#SVGElement>
|
||||
|
||||
|
||||
Startpercent(w float64, h float64, ns ...string)
|
||||
begin the SVG document, with width and height in percent. Optionally add additional elements
|
||||
(such as additional namespaces or scripting events)
|
||||
<http://www.w3.org/TR/SVG11/struct.html#SVGElement>
|
||||
|
||||
|
||||
StartviewUnit(w, h float64, unit string, minx, miny, vw, vh float64)
|
||||
begin the SVG document with the width w, height h, in the specified unit, with a viewBox at minx, miny, vw, vh.
|
||||
<http://www.w3.org/TR/SVG11/struct.html#SVGElement>
|
||||
|
||||
End()
|
||||
end the SVG document
|
||||
|
||||
Script(scriptype string, data ...string)
|
||||
Script defines a script with a specified type, (for example "application/javascript").
|
||||
if the first variadic argument is a link, use only the link reference.
|
||||
Otherwise, treat variadic arguments as the text of the script (marked up as CDATA).
|
||||
if no data is specified, simply close the script element.
|
||||
<http://www.w3.org/TR/SVG/script.html>
|
||||
|
||||
Group(s ...string)
|
||||
begin a group, with arbitrary attributes
|
||||
<http://www.w3.org/TR/SVG11/struct.html#GElement>
|
||||
|
||||
Gstyle(s string)
|
||||
begin a group, with the specified style.
|
||||
<http://www.w3.org/TR/SVG11/struct.html#GElement>
|
||||
|
||||
Gid(s string)
|
||||
begin a group, with the specified id.
|
||||
|
||||
Gtransform(s string)
|
||||
begin a group, with the specified transform, end with Gend().
|
||||
<http://www.w3.org/TR/SVG11/coords.html#TransformAttribute>
|
||||
|
||||
Translate(x, y float64)
|
||||
begins coordinate translation to (x,y), end with Gend().
|
||||
<http://www.w3.org/TR/SVG11/coords.html#TransformAttribute>
|
||||
|
||||
Scale(n float64)
|
||||
scales the coordinate system by n, end with Gend().
|
||||
<http://www.w3.org/TR/SVG11/coords.html#TransformAttribute>
|
||||
|
||||
ScaleXY(x, y float64)
|
||||
scales the coordinate system by x, y. End with Gend().
|
||||
<http://www.w3.org/TR/SVG11/coords.html#TransformAttribute>
|
||||
|
||||
SkewX(a float64)
|
||||
SkewX skews the x coordinate system by angle a, end with Gend().
|
||||
<http://www.w3.org/TR/SVG11/coords.html#TransformAttribute>
|
||||
|
||||
SkewY(a float64)
|
||||
SkewY skews the y coordinate system by angle a, end with Gend().
|
||||
<http://www.w3.org/TR/SVG11/coords.html#TransformAttribute>
|
||||
|
||||
SkewXY(ax, ay float64)
|
||||
SkewXY skews x and y coordinate systems by ax, ay respectively, end with Gend().
|
||||
<http://www.w3.org/TR/SVG11/coords.html#TransformAttribute>
|
||||
|
||||
Rotate(r float64)
|
||||
rotates the coordinate system by r degrees, end with Gend().
|
||||
<http://www.w3.org/TR/SVG11/coords.html#TransformAttribute>
|
||||
|
||||
TranslateRotate(x, y float64, r float64)
|
||||
translates the coordinate system to (x,y), then rotates to r degrees, end with Gend().
|
||||
|
||||
RotateTranslate(x, y float64, r float64)
|
||||
rotates the coordinate system r degrees, then translates to (x,y), end with Gend().
|
||||
|
||||
Gend()
|
||||
end the group (must be paired with Gstyle, Gtransform, Gid).
|
||||
|
||||
ClipPath(s ...string)
|
||||
Begin a ClipPath
|
||||
<http://www.w3.org/TR/SVG/masking.html#ClippingPaths>
|
||||
|
||||
ClipEnd()
|
||||
End a ClipPath
|
||||
<http://www.w3.org/TR/SVG/masking.html#ClippingPaths>
|
||||
|
||||
Def()
|
||||
begin a definition block.
|
||||
<http://www.w3.org/TR/SVG11/struct.html#DefsElement>
|
||||
|
||||
DefEnd()
|
||||
end a definition block.
|
||||
|
||||
Marker(id string, x, y, w, h float64, s ...string)
|
||||
define a marker
|
||||
<http://www.w3.org/TR/SVG11/painting.html#MarkerElement>
|
||||
|
||||
|
||||
MarkerEnd()
|
||||
end a marker
|
||||
|
||||
|
||||
Mask(id string, x float64, y float64, w float64, h float64, s ...string)
|
||||
creates a mask with a specified id, dimension, and optional style.
|
||||
<http://www.w3.org/TR/SVG/masking.html>
|
||||
|
||||
MaskEnd()
|
||||
ends the Mask element.
|
||||
|
||||
|
||||
Pattern(id string, x, y, width, height float64, putype string, s ...string)
|
||||
define a Pattern with the specified dimensions, the putype can be either "user" or "obj", which sets the patternUnits
|
||||
attribute to be either userSpaceOnUse or objectBoundingBox.
|
||||
<http://www.w3.org/TR/SVG11/pservers.html#Patterns>
|
||||
|
||||
Desc(s string)
|
||||
specify the text of the description.
|
||||
<http://www.w3.org/TR/SVG11/struct.html#DescElement>
|
||||
|
||||
Title(s string)
|
||||
specify the text of the title.
|
||||
<http://www.w3.org/TR/SVG11/struct.html#TitleElement>
|
||||
|
||||
Link(href string, title string)
|
||||
begin a link named "href", with the specified title.
|
||||
<http://www.w3.org/TR/SVG11/linking.html#Links>
|
||||
|
||||
LinkEnd()
|
||||
end the link.
|
||||
|
||||
Use(x float64, y float64, link string, s ...string)
|
||||
place the object referenced at link at the location x, y.
|
||||
<http://www.w3.org/TR/SVG11/struct.html#UseElement>
|
||||
|
||||
### Shapes ###
|
||||
|
||||
Circle(x float64, y float64, r float64, s ...string)
|
||||
draw a circle, centered at x,y with radius r.
|
||||
<http://www.w3.org/TR/SVG11/shapes.html#CircleElement>
|
||||
|
||||

|
||||
|
||||
Ellipse(x float64, y float64, w float64, h float64, s ...string)
|
||||
draw an ellipse, centered at x,y with radii w, and h.
|
||||
<http://www.w3.org/TR/SVG11/shapes.html#EllipseElement>
|
||||
|
||||

|
||||
|
||||
Polygon(x []int, y []int, s ...string)
|
||||
draw a series of line segments using an array of x, y coordinates.
|
||||
<http://www.w3.org/TR/SVG11/shapes.html#PolygonElement>
|
||||
|
||||

|
||||
|
||||
Rect(x float64, y float64, w float64, h float64, s ...string)
|
||||
draw a rectangle with upper left-hand corner at x,y, with width w, and height h.
|
||||
<http://www.w3.org/TR/SVG11/shapes.html#RectElement>
|
||||
|
||||

|
||||
|
||||
CenterRect(x float64, y float64, w float64, h float64, s ...string)
|
||||
draw a rectangle with its center at x,y, with width w, and height h.
|
||||
|
||||
Roundrect(x float64, y float64, w float64, h float64, rx float64, ry float64, s ...string)
|
||||
draw a rounded rectangle with upper the left-hand corner at x,y,
|
||||
with width w, and height h. The radii for the rounded portion
|
||||
is specified by rx (width), and ry (height).
|
||||
|
||||

|
||||
|
||||
Square(x float64, y float64, s float64, style ...string)
|
||||
draw a square with upper left corner at x,y with sides of length s.
|
||||
|
||||

|
||||
|
||||
### Paths ###
|
||||
|
||||
Path(p string, s ...style)
|
||||
draw the arbitrary path as specified in p, according to the style specified in s. <http://www.w3.org/TR/SVG11/paths.html>
|
||||
|
||||
|
||||
Arc(sx float64, sy float64, ax float64, ay float64, r float64, large bool, sweep bool, ex float64, ey float64, s ...string)
|
||||
draw an elliptical arc beginning coordinate at sx,sy, ending coordinate at ex, ey
|
||||
width and height of the arc are specified by ax, ay, the x axis rotation is r
|
||||
|
||||
if sweep is true, then the arc will be drawn in a "positive-angle" direction (clockwise),
|
||||
if false, the arc is drawn counterclockwise.
|
||||
|
||||
if large is true, the arc sweep angle is greater than or equal to 180 degrees,
|
||||
otherwise the arc sweep is less than 180 degrees.
|
||||
<http://www.w3.org/TR/SVG11/paths.html#PathDataEllipticalArcCommands>
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
Bezier(sx float64, sy float64, cx float64, cy float64, px float64, py float64, ex float64, ey float64, s ...string)
|
||||
draw a cubic bezier curve, beginning at sx,sy, ending at ex,ey
|
||||
with control points at cx,cy and px,py.
|
||||
<http://www.w3.org/TR/SVG11/paths.html#PathDataCubicBezierCommands>
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
Qbezier(sx float64, sy float64, cx float64, cy float64, ex float64, ey float64, tx float64, ty float64, s ...string)
|
||||
draw a quadratic bezier curve, beginning at sx, sy, ending at tx,ty
|
||||
with control points are at cx,cy, ex,ey.
|
||||
<http://www.w3.org/TR/SVG11/paths.html#PathDataQuadraticBezierCommands>
|
||||
|
||||

|
||||
|
||||
|
||||
Qbez(sx float64, sy float64, cx float64, cy float64, ex float64, ey float64, s...string)
|
||||
draws a quadratic bezier curver, with optional style beginning at sx,sy, ending at ex, sy
|
||||
with the control point at cx, cy.
|
||||
<http://www.w3.org/TR/SVG11/paths.html#PathDataQuadraticBezierCommands>
|
||||
|
||||

|
||||
|
||||
### Lines ###
|
||||
|
||||
Line(x1 float64, y1 float64, x2 float64, y2 float64, s ...string)
|
||||
draw a line segment between x1,y1 and x2,y2.
|
||||
<http://www.w3.org/TR/SVG11/shapes.html#LineElement>
|
||||
|
||||

|
||||
|
||||
|
||||
Polyline(x []int, y []int, s ...string)
|
||||
draw a polygon using coordinates specified in x,y arrays.
|
||||
<http://www.w3.org/TR/SVG11/shapes.html#PolylineElement>
|
||||
|
||||

|
||||
|
||||
### Image and Text ###
|
||||
|
||||
Image(x float64, y float64, w float64, h float64, link string, s ...string)
|
||||
place at x,y (upper left hand corner), the image with width w, and height h, referenced at link.
|
||||
<http://www.w3.org/TR/SVG11/struct.html#ImageElement>
|
||||
|
||||

|
||||
|
||||
Text(x float64, y float64, t string, s ...string)
|
||||
Place the specified text, t at x,y according to the style specified in s.
|
||||
<http://www.w3.org/TR/SVG11/text.html#TextElement>
|
||||
|
||||
Textlines(x, y float64, s []string, size, spacing float64, fill, align string)
|
||||
Places lines of text in s, starting at x,y, at the specified size, fill, and alignment, and spacing.
|
||||
|
||||
Textpath(t string, pathid string, s ...string)
|
||||
places optionally styled text along a previously defined path.
|
||||
<http://www.w3.org/TR/SVG11/text.html#TextPathElement>
|
||||

|
||||
|
||||
### Color ###
|
||||
|
||||
RGB(r float64, g float64, b float64) string
|
||||
creates a style string for the fill color designated
|
||||
by the (r)ed, g(reen), (b)lue components.
|
||||
<http://www.w3.org/TR/css3-color/>
|
||||
|
||||
RGBA(r float64, g float64, b float64, a float64) string
|
||||
as above, but includes the color's opacity as a value
|
||||
between 0.0 (fully transparent) and 1.0 (opaque).
|
||||
|
||||
### Gradients ###
|
||||
|
||||
LinearGradient(id string, x1, y1, x2, y2 uint8, sc []Offcolor)
|
||||
constructs a linear color gradient identified by id,
|
||||
along the vector defined by (x1,y1), and (x2,y2).
|
||||
The stop color sequence defined in sc. Coordinates are expressed as percentages.
|
||||
<http://www.w3.org/TR/SVG11/pservers.html#LinearGradients>
|
||||

|
||||
|
||||
RadialGradient(id string, cx, cy, r, fx, fy uint8, sc []Offcolor)
|
||||
constructs a radial color gradient identified by id,
|
||||
centered at (cx,cy), with a radius of r.
|
||||
(fx, fy) define the location of the focal point of the light source.
|
||||
The stop color sequence defined in sc.
|
||||
Coordinates are expressed as percentages.
|
||||
<http://www.w3.org/TR/SVG11/pservers.html#RadialGradients>
|
||||
|
||||

|
||||
|
||||
### Filter Effects ###
|
||||
|
||||
Filter(id string, s ...string)
|
||||
Filter begins a filter set
|
||||
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#FilterElement>
|
||||
|
||||
Fend()
|
||||
Fend ends a filter set
|
||||
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#FilterElement>
|
||||
|
||||
FeBlend(fs Filterspec, mode string, s ...string)
|
||||
FeBlend specifies a Blend filter primitive
|
||||
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feBlendElement>
|
||||
|
||||
FeColorMatrix(fs Filterspec, values [20]float64, s ...string)
|
||||
FeColorMatrix specifies a color matrix filter primitive, with matrix values
|
||||
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feColorMatrixElement>
|
||||
|
||||
FeColorMatrixHue(fs Filterspec, value float64, s ...string)
|
||||
FeColorMatrix specifies a color matrix filter primitive, with hue values
|
||||
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feColorMatrixElement>
|
||||
|
||||
FeColorMatrixSaturate(fs Filterspec, value float64, s ...string)
|
||||
FeColorMatrix specifies a color matrix filter primitive, with saturation values
|
||||
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feColorMatrixElement>
|
||||
|
||||
FeColorMatrixLuminence(fs Filterspec, s ...string)
|
||||
FeColorMatrix specifies a color matrix filter primitive, with luminence values
|
||||
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feColorMatrixElement>
|
||||
|
||||
FeComponentTransfer()
|
||||
FeComponentTransfer begins a feComponent filter Element>
|
||||
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement>
|
||||
|
||||
FeCompEnd()
|
||||
FeCompEnd ends a feComponent filter Element>
|
||||
|
||||
FeComposite(fs Filterspec, operator string, k1, k2, k3, k4 float64, s ...string)
|
||||
FeComposite specifies a feComposite filter primitive
|
||||
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feCompositeElement>
|
||||
|
||||
FeConvolveMatrix(fs Filterspec, matrix [9]int, s ...string)
|
||||
FeConvolveMatrix specifies a feConvolveMatrix filter primitive
|
||||
Standard referencd: <http://www.w3.org/TR/SVG11/filters.html#feConvolveMatrixElement>
|
||||
|
||||
|
||||
FeDiffuseLighting(fs Filterspec, scale, constant float64, s ...string)
|
||||
FeDiffuseLighting specifies a diffuse lighting filter primitive,
|
||||
a container for light source Element>s, end with DiffuseEnd()
|
||||
|
||||
FeDiffEnd()
|
||||
FeDiffuseEnd ends a diffuse lighting filter primitive container
|
||||
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feDiffuseLightingElement>
|
||||
|
||||
|
||||
FeDisplacementMap(fs Filterspec, scale float64, xchannel, ychannel string, s ...string)
|
||||
FeDisplacementMap specifies a feDisplacementMap filter primitive
|
||||
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feDisplacementMapElement>
|
||||
|
||||
FeDistantLight(fs Filterspec, azimuth, elevation float64, s ...string)
|
||||
FeDistantLight specifies a feDistantLight filter primitive
|
||||
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feDistantLightElement>
|
||||
|
||||
FeFlood(fs Filterspec, color string, opacity float64, s ...string)
|
||||
FeFlood specifies a flood filter primitive
|
||||
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feFloodElement>
|
||||
|
||||
FeFuncLinear(channel string, slope, intercept float64)
|
||||
FeFuncLinear is the linear form of feFunc
|
||||
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement>
|
||||
|
||||
FeFuncGamma(channel, amplitude, exponent, offset float64)
|
||||
FeFuncGamma is the gamma curve form of feFunc
|
||||
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement>
|
||||
|
||||
FeFuncTable(channel string, tv []float64)
|
||||
FeFuncGamma is the form of feFunc using a table of values
|
||||
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement>
|
||||
|
||||
FeFuncDiscrete(channel string, tv []float64)
|
||||
FeFuncGamma is the form of feFunc using discrete values
|
||||
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement>
|
||||
|
||||
FeGaussianBlur(fs Filterspec, stdx, stdy float64, s ...string)
|
||||
FeGaussianBlur specifies a Gaussian Blur filter primitive
|
||||
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feGaussianBlurElement>
|
||||
|
||||
FeImage(href string, result string, s ...string)
|
||||
FeImage specifies a feImage filter primitive
|
||||
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feImageElement>
|
||||
|
||||
FeMerge(nodes []string, s ...string)
|
||||
FeMerge specifies a feMerge filter primitive, containing feMerge Element>s
|
||||
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feMergeElement>
|
||||
|
||||
FeMorphology(fs Filterspec, operator string, xradius, yradius float64, s ...string)
|
||||
FeMorphologyLight specifies a feMorphologyLight filter primitive
|
||||
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feMorphologyElement>
|
||||
|
||||
FeOffset(fs Filterspec, dx, dy float64, s ...string)
|
||||
FeOffset specifies the feOffset filter primitive
|
||||
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feOffsetElement>
|
||||
|
||||
FePointLight(x, y, z float64, s ...string)
|
||||
FePointLight specifies a fePpointLight filter primitive
|
||||
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#fePointLightElement>
|
||||
|
||||
FeSpecularLighting(fs Filterspec, scale, constant float64, exponent float64, color string, s ...string)
|
||||
FeSpecularLighting specifies a specular lighting filter primitive,
|
||||
a container for light source elements, end with SpecularEnd()
|
||||
|
||||
|
||||
FeSpecEnd()
|
||||
FeSpecularEnd ends a specular lighting filter primitive container
|
||||
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feSpecularLightingElement>
|
||||
|
||||
|
||||
FeSpotLight(fs Filterspec, x, y, z, px, py, pz float64, s ...string)
|
||||
FeSpotLight specifies a feSpotLight filter primitive
|
||||
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feSpotLightElement>
|
||||
|
||||
FeTile(fs Filterspec, in string, s ...string)
|
||||
FeTile specifies the tile utility filter primitive
|
||||
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feTileElement>
|
||||
|
||||
|
||||
FeTurbulence(fs Filterspec, ftype string, bfx, bfy float64, octaves float64, seed int64, stitch bool, s ...string)
|
||||
FeTurbulence specifies a turbulence filter primitive
|
||||
Standard reference: <http://www.w3.org/TR/SVG11/filters.html#feTurbulenceElement>
|
||||
|
||||
### Filter convenience functions (modeled on CSS filter effects) ###
|
||||
|
||||
Blur(p float64)
|
||||
Blur function by standard deviation
|
||||
|
||||
Brightness(p float64)
|
||||
Brightness function (0-100)
|
||||
|
||||
Grayscale()
|
||||
Apply a grayscale filter to the image
|
||||
|
||||
HueRotate(a float64)
|
||||
Rotate Hues (0-360 degrees)
|
||||
|
||||
Invert()
|
||||
Invert the image's colors
|
||||
|
||||
Saturate(p float64)
|
||||
Percent saturation, 0 is grayscale
|
||||
|
||||
Sepia()
|
||||
Apply sepia tone
|
||||
|
||||
|
||||
### Utility ###
|
||||
|
||||
Grid(x float64, y float64, w float64, h float64, n float64, s ...string)
|
||||
draws a grid of straight lines starting at x,y, with a width w, and height h, and a size of n.
|
||||
|
||||

|
||||
|
||||
### Credits ###
|
||||
|
||||
Thanks to Jonathan Wright for the io.Writer update.
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
Package svg generates SVG as defined by the Scalable Vector Graphics 1.1 Specification (<http://www.w3.org/TR/SVG11/>).
|
||||
Output goes to the specified io.Writer.
|
||||
|
||||
Supported SVG elements and functions
|
||||
|
||||
Shapes, lines, text
|
||||
|
||||
circle, ellipse, polygon, polyline, rect (including roundrects), line, text
|
||||
|
||||
Paths
|
||||
|
||||
general, arc, cubic and quadratic bezier paths,
|
||||
|
||||
Image and Gradients
|
||||
|
||||
image, linearGradient, radialGradient,
|
||||
|
||||
Transforms
|
||||
|
||||
translate, rotate, scale, skewX, skewY
|
||||
|
||||
Filter Effects
|
||||
|
||||
filter, feBlend, feColorMatrix, feColorMatrix, feComponentTransfer, feComposite, feConvolveMatrix, feDiffuseLighting,
|
||||
feDisplacementMap, feDistantLight, feFlood, feGaussianBlur, feImage, feMerge, feMorphology, feOffset, fePointLight,
|
||||
feSpecularLighting, feSpotLight,feTile, feTurbulence
|
||||
|
||||
|
||||
Metadata elements
|
||||
|
||||
desc, defs, g (style, transform, id), mask, marker, pattern, title, (a)ddress, link, script, use
|
||||
|
||||
Usage: (assuming GOPATH is set)
|
||||
|
||||
go get github.com/ajstarks/svgo/float
|
||||
|
||||
|
||||
You can use godoc to browse the documentation from the command line:
|
||||
|
||||
$ go doc github.com/ajstarks/svgo/float
|
||||
|
||||
|
||||
a minimal program, to generate SVG to standard output.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/ajstarks/svgo/float"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
width := 500.0
|
||||
height := 500.0
|
||||
canvas := svg.New(os.Stdout)
|
||||
canvas.Start(width, height)
|
||||
canvas.Circle(width/2, height/2, 100)
|
||||
canvas.Text(width/2, height/2, "Hello, SVG", "text-anchor:middle;font-size:30px;fill:white")
|
||||
canvas.End()
|
||||
}
|
||||
|
||||
Drawing in a web server: (http://localhost:2003/circle)
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"github.com/ajstarks/svgo"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func main() {
|
||||
http.Handle("/circle", http.HandlerFunc(circle))
|
||||
err := http.ListenAndServe(":2003", nil)
|
||||
if err != nil {
|
||||
log.Fatal("ListenAndServe:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func circle(w http.ResponseWriter, req *http.Request) {
|
||||
w.Header().Set("Content-Type", "image/svg+xml")
|
||||
s := svg.New(w)
|
||||
s.Start(500, 500)
|
||||
s.Circle(250, 250, 125, "fill:none;stroke:black")
|
||||
s.End()
|
||||
}
|
||||
|
||||
Functions and types
|
||||
|
||||
Many functions use x, y to specify an object's location, and w, h to specify the object's width and height.
|
||||
Where applicable, a final optional argument specifies the style to be applied to the object.
|
||||
The style strings follow the SVG standard; name:value pairs delimited by semicolons, or a
|
||||
series of name="value" pairs. For example: `"fill:none; opacity:0.3"` or `fill="none" opacity="0.3"` (see: <http://www.w3.org/TR/SVG11/styling.html>)
|
||||
|
||||
The SVG type:
|
||||
|
||||
type SVG struct {
|
||||
Writer io.Writer
|
||||
}
|
||||
|
||||
Most operations are methods on this type, specifying the destination io.Writer.
|
||||
|
||||
The Offcolor type:
|
||||
|
||||
type Offcolor struct {
|
||||
Offset uint8
|
||||
Color string
|
||||
Opacity float64
|
||||
}
|
||||
|
||||
is used to specify the offset, color, and opacity of stop colors in linear and radial gradients
|
||||
|
||||
The Filterspec type:
|
||||
|
||||
type Filterspec struct {
|
||||
In string
|
||||
In2 string
|
||||
Result string
|
||||
}
|
||||
|
||||
is used to specify inputs and results for filter effects
|
||||
|
||||
*/
|
||||
package svg
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue