commit
0de29f08c3
9 changed files with 476 additions and 45 deletions
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
#### Improvements 🔧
|
||||
|
||||
- Local images can now be included, e.g. `icon: ./my_img.png`.
|
||||
[#146](https://github.com/terrastruct/d2/issues/146)
|
||||
- ELK layout engine now defaults to top-down to be consistent with dagre.
|
||||
[#251](https://github.com/terrastruct/d2/pull/251)
|
||||
- [install.sh](./install.sh) prints the dry run message more visibly.
|
||||
|
|
@ -27,3 +29,5 @@
|
|||
[#159](https://github.com/terrastruct/d2/issues/159)
|
||||
- Fixes markdown newlines created with a trailing double space or backslash.
|
||||
[#214](https://github.com/terrastruct/d2/pull/214)
|
||||
- Fixes images not loading in PNG exports
|
||||
[#224](https://github.com/terrastruct/d2/pull/224)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"oss.terrastruct.com/d2/d2renderers/textmeasure"
|
||||
"oss.terrastruct.com/d2/d2themes"
|
||||
"oss.terrastruct.com/d2/d2themes/d2themescatalog"
|
||||
"oss.terrastruct.com/d2/lib/imgbundler"
|
||||
"oss.terrastruct.com/d2/lib/png"
|
||||
"oss.terrastruct.com/d2/lib/version"
|
||||
"oss.terrastruct.com/d2/lib/xmain"
|
||||
|
|
@ -170,7 +171,7 @@ func run(ctx context.Context, ms *xmain.State) (err error) {
|
|||
_ = 343
|
||||
}
|
||||
|
||||
_, err = compile(ctx, ms, plugin, *themeFlag, inputPath, outputPath, pw.Page)
|
||||
_, err = compile(ctx, ms, false, plugin, *themeFlag, inputPath, outputPath, pw.Page)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -178,7 +179,7 @@ func run(ctx context.Context, ms *xmain.State) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, themeID int64, inputPath, outputPath string, page playwright.Page) ([]byte, error) {
|
||||
func compile(ctx context.Context, ms *xmain.State, isWatching bool, plugin d2plugin.Plugin, themeID int64, inputPath, outputPath string, page playwright.Page) ([]byte, error) {
|
||||
input, err := ms.ReadPath(inputPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -211,9 +212,18 @@ func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, theme
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
svg, err = imgbundler.InlineLocal(ctx, ms, svg)
|
||||
if err != nil {
|
||||
ms.Log.Error.Printf("missing/broken local image(s), writing partial output: %v", err)
|
||||
}
|
||||
|
||||
out := svg
|
||||
if filepath.Ext(outputPath) == ".png" {
|
||||
svg, err = imgbundler.InlineRemote(ctx, ms, svg)
|
||||
if err != nil {
|
||||
ms.Log.Error.Printf("missing/broken remote image(s), writing partial output: %v", err)
|
||||
}
|
||||
|
||||
out, err = png.ConvertSVG(ms, page, svg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -224,6 +234,13 @@ func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, theme
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Missing/broken images are fine during watch mode, as the user is likely building up a diagram.
|
||||
// Otherwise, the assumption is that this diagram is building for production, and broken images are not okay.
|
||||
if !isWatching && ms.Log.Nerrors() > 0 {
|
||||
return nil, xmain.ExitErrorf(1, "errors logged while rendering, partial output written to %v", outputPath)
|
||||
}
|
||||
|
||||
return svg, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -345,7 +345,7 @@ func (w *watcher) compileLoop(ctx context.Context) error {
|
|||
w.pw = newPW
|
||||
}
|
||||
|
||||
b, err := compile(ctx, w.ms, w.layoutPlugin, w.themeID, w.inputPath, w.outputPath, w.pw.Page)
|
||||
b, err := compile(ctx, w.ms, true, w.layoutPlugin, w.themeID, w.inputPath, w.outputPath, w.pw.Page)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to %scompile: %w", recompiledPrefix, err)
|
||||
w.ms.Log.Error.Print(err)
|
||||
|
|
|
|||
32
go.mod
generated
32
go.mod
generated
|
|
@ -13,15 +13,16 @@ require (
|
|||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8
|
||||
github.com/playwright-community/playwright-go v0.2000.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/yuin/goldmark v1.5.2
|
||||
golang.org/x/exp v0.0.0-20221026004748-78e5e7837ae6
|
||||
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69
|
||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e
|
||||
golang.org/x/text v0.3.7
|
||||
github.com/stretchr/testify v1.8.1
|
||||
github.com/yuin/goldmark v1.5.3
|
||||
go.uber.org/multierr v1.8.0
|
||||
golang.org/x/exp v0.0.0-20221126150942-6ab00d035af9
|
||||
golang.org/x/image v0.1.0
|
||||
golang.org/x/net v0.2.0
|
||||
golang.org/x/text v0.4.0
|
||||
gonum.org/v1/plot v0.12.0
|
||||
nhooyr.io/websocket v1.8.7
|
||||
oss.terrastruct.com/cmdlog v0.0.0-20221116181457-07977d95ac37
|
||||
oss.terrastruct.com/cmdlog v0.0.0-20221129200109-540ef52ff07d
|
||||
oss.terrastruct.com/diff v1.0.2-0.20221116222035-8bf4dd3ab541
|
||||
oss.terrastruct.com/xcontext v0.0.0-20221018000442-50fdafb12f4f
|
||||
oss.terrastruct.com/xdefer v0.0.0-20221017222355-6f3b6e4d1557
|
||||
|
|
@ -36,7 +37,7 @@ require (
|
|||
github.com/andybalholm/cascadia v1.3.1 // indirect
|
||||
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dlclark/regexp2 v1.4.0 // indirect
|
||||
github.com/dlclark/regexp2 v1.7.0 // indirect
|
||||
github.com/fatih/color v1.13.0 // indirect
|
||||
github.com/gin-gonic/gin v1.7.7 // indirect
|
||||
github.com/go-playground/validator/v10 v10.10.0 // indirect
|
||||
|
|
@ -45,21 +46,22 @@ require (
|
|||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.13.6 // indirect
|
||||
github.com/kr/pretty v0.3.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.8.0 // indirect
|
||||
github.com/ugorji/go/codec v1.2.6 // indirect
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect
|
||||
golang.org/x/sys v0.1.0 // indirect
|
||||
golang.org/x/term v0.0.0-20221017184919-83659145692c // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
golang.org/x/crypto v0.3.0 // indirect
|
||||
golang.org/x/sys v0.2.0 // indirect
|
||||
golang.org/x/term v0.2.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
|
|||
73
go.sum
generated
73
go.sum
generated
|
|
@ -93,8 +93,9 @@ github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9
|
|||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
|
||||
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo=
|
||||
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
|
|
@ -255,11 +256,12 @@ github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ic
|
|||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mazznoer/csscolorparser v0.1.3 h1:vug4zh6loQxAUxfU1DZEu70gTPufDPspamZlHAkKcxE=
|
||||
github.com/mazznoer/csscolorparser v0.1.3/go.mod h1:Aj22+L/rYN/Y6bj3bYqO3N6g1dtdHtGfQ32xZ5PJQic=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
|
|
@ -285,12 +287,17 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO
|
|||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
|
|
@ -301,25 +308,32 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
|||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.5.2 h1:ALmeCk/px5FSm1MAcFBAsVKZjDuMVj8Tm7FFIlMJnqU=
|
||||
github.com/yuin/goldmark v1.5.2/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.5.3 h1:3HUJmBFbQW9fhQOzMgseU134xfi6hU+mjWywx5Ty+/M=
|
||||
github.com/yuin/goldmark v1.5.3/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
|
||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=
|
||||
go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE=
|
||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
|
||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
|
|
@ -330,12 +344,12 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
|||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20221026004748-78e5e7837ae6 h1:mC6uOkPi9SUk8A59jZvw7//rlyc+MlELtQUCyOUSKZQ=
|
||||
golang.org/x/exp v0.0.0-20221026004748-78e5e7837ae6/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
|
||||
golang.org/x/exp v0.0.0-20221126150942-6ab00d035af9 h1:yZNXmy+j/JpX19vZkVktWqAo7Gny4PBWYYK3zskGpx4=
|
||||
golang.org/x/exp v0.0.0-20221126150942-6ab00d035af9/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 h1:Lj6HJGCSn5AjxRAH2+r35Mir4icalbqku+CLUtjnvXY=
|
||||
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
|
||||
golang.org/x/image v0.1.0 h1:r8Oj8ZA2Xy12/b5KZYj3tuv7NG/fBz3TwQVvpJ9l8Rk=
|
||||
golang.org/x/image v0.1.0/go.mod h1:iyPr49SD/G/TBxYVB/9RRtGUT5eNbo2u4NamWeQcD5c=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
|
|
@ -359,6 +373,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
|
@ -401,8 +416,9 @@ golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su
|
|||
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e h1:TsQ7F31D3bUCLeqPT0u+yjp1guoArKaNKmCr22PYgTQ=
|
||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
|
|
@ -435,6 +451,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
|
@ -483,7 +500,6 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
|
@ -496,13 +512,15 @@ golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.0.0-20221017184919-83659145692c h1:dveknrit5futqEmXAvd2I1BbZIDhxRijsyWHM86NlcA=
|
||||
golang.org/x/term v0.0.0-20221017184919-83659145692c/go.mod h1:VTIZ7TEbF0BS9Sv9lPTvGbtW8i4z6GGbJBCM37uMCzY=
|
||||
golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
|
@ -511,8 +529,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
|
@ -567,6 +586,7 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
|||
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
|
@ -766,8 +786,9 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
|
@ -777,10 +798,8 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
|
|||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
|
||||
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
|
||||
oss.terrastruct.com/cmdlog v0.0.0-20221116181457-07977d95ac37 h1:Xy1JKJHc4hcuwi57s0BvGUY16GjxTtBmLUybsuGDU7E=
|
||||
oss.terrastruct.com/cmdlog v0.0.0-20221116181457-07977d95ac37 h1:Xy1JKJHc4hcuwi57s0BvGUY16GjxTtBmLUybsuGDU7E=
|
||||
oss.terrastruct.com/cmdlog v0.0.0-20221116181457-07977d95ac37/go.mod h1:ROL3yxl2X+S3O+Rls00qdX6aMh+p1dF8IdxDRwDDpsg=
|
||||
oss.terrastruct.com/cmdlog v0.0.0-20221116181457-07977d95ac37/go.mod h1:ROL3yxl2X+S3O+Rls00qdX6aMh+p1dF8IdxDRwDDpsg=
|
||||
oss.terrastruct.com/cmdlog v0.0.0-20221129200109-540ef52ff07d h1:oc3cqW4bWaosVpfM8yNLAPSDMjLrVYP2ztF7w3tRvws=
|
||||
oss.terrastruct.com/cmdlog v0.0.0-20221129200109-540ef52ff07d/go.mod h1:ROL3yxl2X+S3O+Rls00qdX6aMh+p1dF8IdxDRwDDpsg=
|
||||
oss.terrastruct.com/diff v1.0.2-0.20221116222035-8bf4dd3ab541 h1:I9B1O1IJ6spivIQxbFRZmbhAwVeLwrcQRR1JbYUOvrI=
|
||||
oss.terrastruct.com/diff v1.0.2-0.20221116222035-8bf4dd3ab541/go.mod h1:ags2QDy/T6jr69hT6bpmAmhr2H98n9o8Atf3QlUJPiU=
|
||||
oss.terrastruct.com/xcontext v0.0.0-20221018000442-50fdafb12f4f h1:7voRCwKM7TZkTo9u7hj+uV/zXoVB8czWrTq6MVIh3dg=
|
||||
|
|
|
|||
161
lib/imgbundler/imgbundler.go
Normal file
161
lib/imgbundler/imgbundler.go
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
package imgbundler
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.uber.org/multierr"
|
||||
"oss.terrastruct.com/xdefer"
|
||||
|
||||
"oss.terrastruct.com/d2/lib/xmain"
|
||||
)
|
||||
|
||||
const maxImageSize int64 = 1 << 25 // 33_554_432
|
||||
|
||||
var imageRe = regexp.MustCompile(`<image href="([^"]+)"`)
|
||||
|
||||
type resp struct {
|
||||
srctxt string
|
||||
data string
|
||||
err error
|
||||
}
|
||||
|
||||
func InlineLocal(ctx context.Context, ms *xmain.State, in []byte) ([]byte, error) {
|
||||
return inline(ctx, ms, in, false)
|
||||
}
|
||||
|
||||
func InlineRemote(ctx context.Context, ms *xmain.State, in []byte) ([]byte, error) {
|
||||
return inline(ctx, ms, in, true)
|
||||
}
|
||||
|
||||
func inline(ctx context.Context, ms *xmain.State, svg []byte, isRemote bool) (_ []byte, err error) {
|
||||
defer xdefer.Errorf(&err, "failed to bundle images")
|
||||
imgs := imageRe.FindAllSubmatch(svg, -1)
|
||||
|
||||
var filtered [][][]byte
|
||||
for _, img := range imgs {
|
||||
u, err := url.Parse(string(img[1]))
|
||||
isRemoteImg := err == nil && strings.HasPrefix(u.Scheme, "http")
|
||||
if isRemoteImg == isRemote {
|
||||
filtered = append(filtered, img)
|
||||
}
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
respChan := make(chan resp)
|
||||
// Limits the number of workers to 16.
|
||||
sema := make(chan struct{}, 16)
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Minute*5)
|
||||
defer cancel()
|
||||
|
||||
wg.Add(len(filtered))
|
||||
// Start workers as the sema allows.
|
||||
go func() {
|
||||
for _, img := range filtered {
|
||||
sema <- struct{}{}
|
||||
go func(src, href string) {
|
||||
defer func() {
|
||||
wg.Done()
|
||||
<-sema
|
||||
}()
|
||||
|
||||
var data string
|
||||
var err error
|
||||
if isRemote {
|
||||
data, err = fetch(ctx, href)
|
||||
} else {
|
||||
data, err = read(href)
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case respChan <- resp{
|
||||
srctxt: src,
|
||||
data: data,
|
||||
err: err,
|
||||
}:
|
||||
}
|
||||
}(string(img[0]), string(img[1]))
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(respChan)
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, fmt.Errorf("failed to wait for imgbundler workers: %w", ctx.Err())
|
||||
case <-time.After(time.Second * 5):
|
||||
ms.Log.Info.Printf("fetching images...")
|
||||
case resp, ok := <-respChan:
|
||||
if !ok {
|
||||
return svg, err
|
||||
}
|
||||
if resp.err != nil {
|
||||
err = multierr.Combine(err, resp.err)
|
||||
continue
|
||||
}
|
||||
svg = bytes.Replace(svg, []byte(resp.srctxt), []byte(fmt.Sprintf(`<image href="%s"`, resp.data)), 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var imgClient = &http.Client{}
|
||||
|
||||
func fetch(ctx context.Context, href string) (string, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Minute)
|
||||
defer cancel()
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", href, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
imgResp, err := imgClient.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer imgResp.Body.Close()
|
||||
if imgResp.StatusCode != 200 {
|
||||
return "", fmt.Errorf("img %s returned status code %d", href, imgResp.StatusCode)
|
||||
}
|
||||
r := http.MaxBytesReader(nil, imgResp.Body, maxImageSize)
|
||||
data, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
mimeType := http.DetectContentType(data)
|
||||
mimeType = strings.Replace(mimeType, "text/xml", "image/svg+xml", 1)
|
||||
|
||||
enc := base64.StdEncoding.EncodeToString(data)
|
||||
|
||||
return fmt.Sprintf("data:%s;base64,%s", mimeType, enc), nil
|
||||
}
|
||||
|
||||
func read(href string) (string, error) {
|
||||
data, err := os.ReadFile(href)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
mimeType := http.DetectContentType(data)
|
||||
mimeType = strings.Replace(mimeType, "text/xml", "image/svg+xml", 1)
|
||||
|
||||
enc := base64.StdEncoding.EncodeToString(data)
|
||||
|
||||
return fmt.Sprintf("data:%s;base64,%s", mimeType, enc), nil
|
||||
}
|
||||
221
lib/imgbundler/imgbundler_test.go
Normal file
221
lib/imgbundler/imgbundler_test.go
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
package imgbundler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"oss.terrastruct.com/cmdlog"
|
||||
"oss.terrastruct.com/xos"
|
||||
|
||||
"oss.terrastruct.com/d2/lib/xmain"
|
||||
)
|
||||
|
||||
//go:embed test_png.png
|
||||
var testPNGFile []byte
|
||||
|
||||
type roundTripFunc func(req *http.Request) *http.Response
|
||||
|
||||
func (f roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
return f(req), nil
|
||||
}
|
||||
|
||||
func TestRegex(t *testing.T) {
|
||||
urls := []string{
|
||||
"https://icons.terrastruct.com/essentials/004-picture.svg",
|
||||
"http://icons.terrastruct.com/essentials/004-picture.svg",
|
||||
}
|
||||
|
||||
notURLs := []string{
|
||||
"hi.png",
|
||||
"./cat.png",
|
||||
"/cat.png",
|
||||
}
|
||||
|
||||
for _, href := range append(urls, notURLs...) {
|
||||
str := fmt.Sprintf(`<image href="%s" />`, href)
|
||||
matches := imageRe.FindAllStringSubmatch(str, -1)
|
||||
if len(matches) != 1 {
|
||||
t.Fatalf("uri regex didn't match %s", str)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInlineRemote(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
svgURL := "https://icons.terrastruct.com/essentials/004-picture.svg"
|
||||
pngURL := "https://cdn4.iconfinder.com/data/icons/smart-phones-technologies/512/android-phone.png"
|
||||
|
||||
sampleSVG := fmt.Sprintf(`<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg
|
||||
style="background: white;"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="328" height="587" viewBox="-100 -131 328 587"><style type="text/css">
|
||||
<![CDATA[
|
||||
.shape {
|
||||
shape-rendering: geometricPrecision;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
.connection {
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
|
||||
]]>
|
||||
</style><g id="a"><g class="shape" ><image href="%s" x="0" y="0" width="128" height="128" style="fill:#FFFFFF;stroke:#0D32B2;opacity:1.000000;stroke-width:2;" /></g><text class="text-bold" x="64.000000" y="-15.000000" style="text-anchor:middle;font-size:16px;fill:#0A0F25">a</text></g><g id="b"><g class="shape" ><image href="%s" x="0" y="228" width="128" height="128" style="fill:#FFFFFF;stroke:#0D32B2;opacity:1.000000;stroke-width:2;" /></g><text class="text-bold" x="64.000000" y="213.000000" style="text-anchor:middle;font-size:16px;fill:#0A0F25">b</text></g><g id="(a -> b)[0]"><marker id="mk-3990223579" markerWidth="10.000000" markerHeight="12.000000" refX="7.000000" refY="6.000000" viewBox="0.000000 0.000000 10.000000 12.000000" orient="auto" markerUnits="userSpaceOnUse"> <polygon class="connection" fill="#0D32B2" stroke-width="2" points="0.000000,0.000000 10.000000,6.000000 0.000000,12.000000" /> </marker><path d="M 64.000000 130.000000 C 64.000000 168.000000 64.000000 188.000000 64.000000 224.000000" class="connection" style="fill:none;stroke:#0D32B2;opacity:1.000000;stroke-width:2;" marker-end="url(#mk-3990223579)" /></g><style type="text/css"><![CDATA[
|
||||
.text-bold {
|
||||
font-family: "font-bold";
|
||||
}
|
||||
@font-face {
|
||||
font-family: font-bold;
|
||||
src: url("REMOVED");
|
||||
}]]></style></svg>
|
||||
`, svgURL, pngURL)
|
||||
|
||||
ms := &xmain.State{
|
||||
Name: "test",
|
||||
|
||||
Stdin: os.Stdin,
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
|
||||
Env: xos.NewEnv(os.Environ()),
|
||||
}
|
||||
ms.Log = cmdlog.Log(ms.Env, os.Stderr)
|
||||
|
||||
imgClient.Transport = roundTripFunc(func(req *http.Request) *http.Response {
|
||||
respRecorder := httptest.NewRecorder()
|
||||
switch req.URL.String() {
|
||||
case svgURL:
|
||||
respRecorder.WriteString(`<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\r\n<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->\r\n<svg version=\"1.1\" id=\"Capa_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\r\n\t viewBox=\"0 0 58 58\" style=\"enable-background:new 0 0 58 58;\" xml:space=\"preserve\">\r\n<rect x=\"1\" y=\"7\" style=\"fill:#C3E1ED;stroke:#E7ECED;stroke-width:2;stroke-miterlimit:10;\" width=\"56\" height=\"44\"/>\r\n<circle style=\"fill:#ED8A19;\" cx=\"16\" cy=\"17.569\" r=\"6.569\"/>\r\n<polygon style=\"fill:#1A9172;\" points=\"56,36.111 55,35 43,24 32.5,35.5 37.983,40.983 42,45 56,45 \"/>\r\n<polygon style=\"fill:#1A9172;\" points=\"2,49 26,49 21.983,44.983 11.017,34.017 2,41.956 \"/>\r\n<rect x=\"2\" y=\"45\" style=\"fill:#6B5B4B;\" width=\"54\" height=\"5\"/>\r\n<polygon style=\"fill:#25AE88;\" points=\"37.983,40.983 27.017,30.017 10,45 42,45 \"/>\r\n<g>\r\n</g>\r\n<g>\r\n</g>\r\n<g>\r\n</g>\r\n<g>\r\n</g>\r\n<g>\r\n</g>\r\n<g>\r\n</g>\r\n<g>\r\n</g>\r\n<g>\r\n</g>\r\n<g>\r\n</g>\r\n<g>\r\n</g>\r\n<g>\r\n</g>\r\n<g>\r\n</g>\r\n<g>\r\n</g>\r\n<g>\r\n</g>\r\n<g>\r\n</g>\r\n</svg>`)
|
||||
case pngURL:
|
||||
respRecorder.Write(testPNGFile)
|
||||
default:
|
||||
t.Fatal(req.URL)
|
||||
}
|
||||
respRecorder.WriteHeader(200)
|
||||
return respRecorder.Result()
|
||||
})
|
||||
|
||||
out, err := InlineRemote(ctx, ms, []byte(sampleSVG))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if strings.Contains(string(out), "https://") {
|
||||
t.Fatal("links still exist")
|
||||
}
|
||||
if !strings.Contains(string(out), "image/svg+xml") {
|
||||
t.Fatal("no svg image inserted")
|
||||
}
|
||||
if !strings.Contains(string(out), "image/png") {
|
||||
t.Fatal("no png image inserted")
|
||||
}
|
||||
|
||||
// Test almost too large response
|
||||
imgClient.Transport = roundTripFunc(func(req *http.Request) *http.Response {
|
||||
respRecorder := httptest.NewRecorder()
|
||||
bytes := make([]byte, maxImageSize)
|
||||
rand.Read(bytes)
|
||||
respRecorder.Write(bytes)
|
||||
respRecorder.WriteHeader(200)
|
||||
return respRecorder.Result()
|
||||
})
|
||||
_, err = InlineRemote(ctx, ms, []byte(sampleSVG))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Test too large response
|
||||
imgClient.Transport = roundTripFunc(func(req *http.Request) *http.Response {
|
||||
respRecorder := httptest.NewRecorder()
|
||||
bytes := make([]byte, maxImageSize+1)
|
||||
rand.Read(bytes)
|
||||
respRecorder.Write(bytes)
|
||||
respRecorder.WriteHeader(200)
|
||||
return respRecorder.Result()
|
||||
})
|
||||
_, err = InlineRemote(ctx, ms, []byte(sampleSVG))
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
|
||||
// Test error response
|
||||
imgClient.Transport = roundTripFunc(func(req *http.Request) *http.Response {
|
||||
respRecorder := httptest.NewRecorder()
|
||||
respRecorder.WriteHeader(500)
|
||||
return respRecorder.Result()
|
||||
})
|
||||
_, err = InlineRemote(ctx, ms, []byte(sampleSVG))
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInlineLocal(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
svgURL, err := filepath.Abs("./test_svg.svg")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pngURL, err := filepath.Abs("./test_png.png")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sampleSVG := fmt.Sprintf(`<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg
|
||||
style="background: white;"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="328" height="587" viewBox="-100 -131 328 587"><style type="text/css">
|
||||
<![CDATA[
|
||||
.shape {
|
||||
shape-rendering: geometricPrecision;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
.connection {
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
|
||||
]]>
|
||||
</style><g id="a"><g class="shape" ><image href="%s" x="0" y="0" width="128" height="128" style="fill:#FFFFFF;stroke:#0D32B2;opacity:1.000000;stroke-width:2;" /></g><text class="text-bold" x="64.000000" y="-15.000000" style="text-anchor:middle;font-size:16px;fill:#0A0F25">a</text></g><g id="b"><g class="shape" ><image href="%s" x="0" y="228" width="128" height="128" style="fill:#FFFFFF;stroke:#0D32B2;opacity:1.000000;stroke-width:2;" /></g><text class="text-bold" x="64.000000" y="213.000000" style="text-anchor:middle;font-size:16px;fill:#0A0F25">b</text></g><g id="(a -> b)[0]"><marker id="mk-3990223579" markerWidth="10.000000" markerHeight="12.000000" refX="7.000000" refY="6.000000" viewBox="0.000000 0.000000 10.000000 12.000000" orient="auto" markerUnits="userSpaceOnUse"> <polygon class="connection" fill="#0D32B2" stroke-width="2" points="0.000000,0.000000 10.000000,6.000000 0.000000,12.000000" /> </marker><path d="M 64.000000 130.000000 C 64.000000 168.000000 64.000000 188.000000 64.000000 224.000000" class="connection" style="fill:none;stroke:#0D32B2;opacity:1.000000;stroke-width:2;" marker-end="url(#mk-3990223579)" /></g><style type="text/css"><![CDATA[
|
||||
.text-bold {
|
||||
font-family: "font-bold";
|
||||
}
|
||||
@font-face {
|
||||
font-family: font-bold;
|
||||
src: url("REMOVED");
|
||||
}]]></style></svg>
|
||||
`, svgURL, pngURL)
|
||||
|
||||
ms := &xmain.State{
|
||||
Name: "test",
|
||||
|
||||
Stdin: os.Stdin,
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
|
||||
Env: xos.NewEnv(os.Environ()),
|
||||
}
|
||||
ms.Log = cmdlog.Log(ms.Env, os.Stderr)
|
||||
out, err := InlineLocal(ctx, ms, []byte(sampleSVG))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if strings.Contains(string(out), svgURL) {
|
||||
t.Fatal("links still exist")
|
||||
}
|
||||
if !strings.Contains(string(out), "image/svg+xml") {
|
||||
t.Fatal("no svg image inserted")
|
||||
}
|
||||
if !strings.Contains(string(out), "image/png") {
|
||||
t.Fatal("no png image inserted")
|
||||
}
|
||||
}
|
||||
BIN
lib/imgbundler/test_png.png
Normal file
BIN
lib/imgbundler/test_png.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
7
lib/imgbundler/test_svg.svg
Normal file
7
lib/imgbundler/test_svg.svg
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><svg width="165" height="58" viewBox="0 0 165 58" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M74.2187 57.213C73.5457 57.213 73 56.6673 73 55.9943V1.21875C73 0.545653 73.5457 0 74.2187 0H89.146C94.996 0 100.056 0.994499 104.327 2.9835C108.597 4.9725 111.903 8.073 114.243 12.285C116.641 16.4385 117.84 21.7912 117.84 28.3432C117.84 34.8952 116.67 40.3065 114.33 44.577C111.99 48.8475 108.714 52.0358 104.502 54.1418C100.349 56.1893 95.464 57.213 89.848 57.213H74.2187ZM85.8993 45.552C85.8993 46.2251 86.4449 46.7707 87.118 46.7707H88.3563C91.6323 46.7707 94.4695 46.1858 96.868 45.0158C99.325 43.8458 101.226 41.9152 102.572 39.2242C103.917 36.5332 104.59 32.9062 104.59 28.3432C104.59 23.7802 103.917 20.2117 102.572 17.6377C101.226 15.0052 99.325 13.1625 96.868 12.1095C94.4695 10.998 91.6323 10.4422 88.3563 10.4422H87.118C86.4449 10.4422 85.8993 10.9879 85.8993 11.661V45.552Z" fill="#0A0F25"/>
|
||||
<path d="M126.409 57.213C125.736 57.213 125.191 56.6673 125.191 55.9943V50.365C125.191 50.031 125.328 49.7116 125.57 49.4814C130.153 45.1203 134.209 41.1747 137.739 37.6448C141.424 33.9593 144.291 30.5955 146.338 27.5535C148.386 24.453 149.41 21.5865 149.41 18.954C149.41 16.2045 148.678 14.1277 147.216 12.7237C145.812 11.2612 143.911 10.53 141.512 10.53C139.523 10.53 137.71 11.115 136.072 12.285C134.816 13.1689 133.615 14.1822 132.47 15.3251C131.982 15.8119 131.19 15.8323 130.7 15.3481L125.423 10.137C124.952 9.67228 124.937 8.91536 125.403 8.44619C127.888 5.94519 130.42 4.03654 133 2.72025C135.867 1.19925 139.289 0.438748 143.267 0.438748C146.953 0.438748 150.17 1.17 152.92 2.6325C155.669 4.095 157.804 6.17175 159.325 8.86275C160.905 11.4952 161.695 14.5957 161.695 18.1642C161.695 21.2647 160.817 24.453 159.062 27.729C157.366 30.9465 155.172 34.1932 152.481 37.4692C149.79 40.6867 146.923 43.875 143.881 47.034C145.285 46.8585 146.865 46.7123 148.62 46.5953C150.375 46.4198 151.896 46.332 153.183 46.332H163.196C163.869 46.332 164.415 46.8777 164.415 47.5507V55.9943C164.415 56.6673 163.869 57.213 163.196 57.213H126.409Z" fill="#0A0F25"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M40.5718 51.8158C40.5718 54.3741 40.5718 55.6533 39.6615 56.3965C38.7512 57.1397 37.5897 56.9026 35.2666 56.4282C27.1711 54.7753 19.4539 50.8089 13.1742 44.5291C7.07441 38.4294 3.15733 30.9735 1.42293 23.132C0.904136 20.7865 0.644741 19.6137 1.38829 18.6877C2.13183 17.7617 3.42795 17.7617 6.02019 17.7617L35.6186 17.7617C37.9536 17.7617 39.1211 17.7617 39.8465 18.4871C40.5718 19.2125 40.5718 20.38 40.5718 22.715L40.5718 51.8158Z" fill="#476CEF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M52.9077 38.7915C55.2308 39.2658 56.3923 39.503 57.3026 38.7598C58.2129 38.0165 58.213 36.7374 58.213 34.179L58.213 5.07828C58.213 2.74329 58.213 1.5758 57.4876 0.850412C56.7622 0.125025 55.5947 0.125026 53.2597 0.125025L23.6613 0.125025C21.0691 0.125026 19.7729 0.125026 19.0294 1.051C18.2859 1.97698 18.5452 3.14974 19.064 5.49527C20.7984 13.3368 24.7155 20.7927 30.8153 26.8924C37.0951 33.1722 44.8122 37.1386 52.9077 38.7915Z" fill="#94ABFF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M40.5698 34.2456L40.5698 22.715L40.5698 22.715C40.5698 20.38 40.5698 19.2125 39.8444 18.4871C39.119 17.7617 37.9515 17.7617 35.6165 17.7617L23.8125 17.7617C25.6894 21.0268 28.023 24.101 30.8134 26.8914C33.7817 29.8597 37.0711 32.3111 40.5698 34.2456Z" fill="#234CDA"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
Loading…
Reference in a new issue