Merge branch 'master' into link-layers
This commit is contained in:
commit
a02896c6da
10 changed files with 88 additions and 28 deletions
|
|
@ -2,4 +2,9 @@
|
|||
|
||||
#### Improvements 🧹
|
||||
|
||||
- PDF exports now support external links on shapes [#891](https://github.com/terrastruct/d2/issues/891)
|
||||
|
||||
#### Bugfixes ⛑️
|
||||
|
||||
- Fixes a regression where PNG backgrounds could be cut off in the appendix. [#941](https://github.com/terrastruct/d2/pull/941)
|
||||
- Fixes zooming not working in watch mode. [#944](https://github.com/terrastruct/d2/pull/944)
|
||||
|
|
|
|||
2
ci/sub
2
ci/sub
|
|
@ -1 +1 @@
|
|||
Subproject commit 5198280010adc30aabb611579579916abfe20a45
|
||||
Subproject commit 690bc39e545cae76314fa32effe343a088e2a52e
|
||||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
|
@ -488,7 +489,16 @@ func renderPDF(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, ske
|
|||
return svg, err
|
||||
}
|
||||
|
||||
err = pdf.AddPDFPage(pngImg, currBoardPath, themeID, rootFill)
|
||||
viewboxSlice := appendix.FindViewboxSlice(svg)
|
||||
viewboxX, err := strconv.ParseFloat(viewboxSlice[0], 64)
|
||||
if err != nil {
|
||||
return svg, err
|
||||
}
|
||||
viewboxY, err := strconv.ParseFloat(viewboxSlice[1], 64)
|
||||
if err != nil {
|
||||
return svg, err
|
||||
}
|
||||
err = pdf.AddPDFPage(pngImg, currBoardPath, themeID, rootFill, diagram.Shapes, pad, viewboxX, viewboxY)
|
||||
if err != nil {
|
||||
return svg, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,3 @@
|
|||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#d2-svg-container > svg {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#d2-err {
|
||||
/* This style was copied from Chrome's svg parser error style. */
|
||||
white-space: pre-wrap;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ function init(reconnectDelay) {
|
|||
const ws = new WebSocket(
|
||||
`ws://${window.location.host}${window.location.pathname}watch`
|
||||
);
|
||||
let isInit = true;
|
||||
let ratio;
|
||||
ws.onopen = () => {
|
||||
reconnectDelay = 1000;
|
||||
console.info("watch websocket opened");
|
||||
|
|
@ -31,6 +33,29 @@ function init(reconnectDelay) {
|
|||
// setting innerHTML to only the actual svg innards. However then you also need to parse
|
||||
// out the width, height and viewbox out of the top level SVG tag and update those manually.
|
||||
d2SVG.innerHTML = msg.svg;
|
||||
|
||||
const svgEl = d2SVG.querySelector("#d2-svg");
|
||||
// just use inner SVG in watch mode
|
||||
svgEl.parentElement.replaceWith(svgEl);
|
||||
let width = parseInt(svgEl.getAttribute("width"), 10);
|
||||
let height = parseInt(svgEl.getAttribute("height"), 10);
|
||||
if (isInit) {
|
||||
if (width > height) {
|
||||
if (width > window.innerWidth) {
|
||||
ratio = window.innerWidth / width;
|
||||
}
|
||||
} else if (height > window.innerHeight) {
|
||||
ratio = window.innerHeight / height;
|
||||
}
|
||||
// Scale svg fit to zoom
|
||||
isInit = false;
|
||||
}
|
||||
if (ratio) {
|
||||
// body padding is 8px
|
||||
svgEl.setAttribute("width", width * ratio - 16);
|
||||
svgEl.setAttribute("height", height * ratio - 16);
|
||||
}
|
||||
|
||||
d2ErrDiv.style.display = "none";
|
||||
}
|
||||
if (msg.err) {
|
||||
|
|
|
|||
|
|
@ -52,6 +52,14 @@ const (
|
|||
var viewboxRegex = regexp.MustCompile(`viewBox=\"([0-9\- ]+)\"`)
|
||||
var widthRegex = regexp.MustCompile(`width=\"([.0-9]+)\"`)
|
||||
var heightRegex = regexp.MustCompile(`height=\"([.0-9]+)\"`)
|
||||
var svgRegex = regexp.MustCompile(`<svg(.*?)>`)
|
||||
|
||||
func FindViewboxSlice(svg []byte) []string {
|
||||
viewboxMatches := viewboxRegex.FindAllStringSubmatch(string(svg), 2)
|
||||
viewboxMatch := viewboxMatches[1]
|
||||
viewboxRaw := viewboxMatch[1]
|
||||
return strings.Split(viewboxRaw, " ")
|
||||
}
|
||||
|
||||
func Append(diagram *d2target.Diagram, ruler *textmeasure.Ruler, in []byte) []byte {
|
||||
svg := string(in)
|
||||
|
|
@ -91,14 +99,22 @@ func Append(diagram *d2target.Diagram, ruler *textmeasure.Ruler, in []byte) []by
|
|||
newOuterViewbox := fmt.Sprintf(`viewBox="0 0 %d %d"`, viewboxWidth, viewboxHeight)
|
||||
newViewbox := fmt.Sprintf(`viewBox="%s %s %s %s"`, viewboxSlice[0], viewboxSlice[1], strconv.Itoa(viewboxWidth), strconv.Itoa(viewboxHeight))
|
||||
|
||||
widthMatches := widthRegex.FindAllStringSubmatch(svg, 2)
|
||||
heightMatches := heightRegex.FindAllStringSubmatch(svg, 2)
|
||||
dimensionsToUpdate := 2
|
||||
outerSVG := svgRegex.FindString(svg)
|
||||
// if outer svg has dimensions set we also need to update it
|
||||
if widthRegex.FindString(outerSVG) != "" {
|
||||
dimensionsToUpdate++
|
||||
}
|
||||
|
||||
// update 1st 3 matches of width and height 1st is outer svg (if dimensions are set), 2nd inner svg, 3rd is background color rect
|
||||
widthMatches := widthRegex.FindAllStringSubmatch(svg, dimensionsToUpdate)
|
||||
heightMatches := heightRegex.FindAllStringSubmatch(svg, dimensionsToUpdate)
|
||||
newWidth := fmt.Sprintf(`width="%s"`, strconv.Itoa(viewboxWidth))
|
||||
newHeight := fmt.Sprintf(`height="%s"`, strconv.Itoa(viewboxHeight))
|
||||
|
||||
svg = strings.Replace(svg, viewboxMatches[0][0], newOuterViewbox, 1)
|
||||
svg = strings.Replace(svg, viewboxMatch[0], newViewbox, 1)
|
||||
for i := 0; i < 2; i++ {
|
||||
for i := 0; i < dimensionsToUpdate; i++ {
|
||||
svg = strings.Replace(svg, widthMatches[i][0], newWidth, 1)
|
||||
svg = strings.Replace(svg, heightMatches[i][0], newHeight, 1)
|
||||
}
|
||||
|
|
|
|||
2
go.mod
generated
2
go.mod
generated
|
|
@ -23,7 +23,7 @@ require (
|
|||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2
|
||||
gonum.org/v1/plot v0.12.0
|
||||
nhooyr.io/websocket v1.8.7
|
||||
oss.terrastruct.com/util-go v0.0.0-20230228050345-d1fed4d6be62
|
||||
oss.terrastruct.com/util-go v0.0.0-20230301015829-35b30391c74d
|
||||
)
|
||||
|
||||
require (
|
||||
|
|
|
|||
4
go.sum
generated
4
go.sum
generated
|
|
@ -277,6 +277,6 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh
|
|||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
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/util-go v0.0.0-20230228050345-d1fed4d6be62 h1:XQZNMkHQr2q1eJIcHcgja29X04oDG8SqqdICCgxe5Bk=
|
||||
oss.terrastruct.com/util-go v0.0.0-20230228050345-d1fed4d6be62/go.mod h1:Fwy72FDIOOM4K8F96ScXkxHHppR1CPfUyo9+x9c1PBU=
|
||||
oss.terrastruct.com/util-go v0.0.0-20230301015829-35b30391c74d h1:+1Bp2bYA7bieedJuqbiwOLhnMs6GQQLB4sNX7BcDbSQ=
|
||||
oss.terrastruct.com/util-go v0.0.0-20230301015829-35b30391c74d/go.mod h1:Fwy72FDIOOM4K8F96ScXkxHHppR1CPfUyo9+x9c1PBU=
|
||||
rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4=
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/jung-kurt/gofpdf"
|
||||
|
||||
"oss.terrastruct.com/d2/d2renderers/d2fonts"
|
||||
"oss.terrastruct.com/d2/d2target"
|
||||
"oss.terrastruct.com/d2/d2themes"
|
||||
"oss.terrastruct.com/d2/d2themes/d2themescatalog"
|
||||
"oss.terrastruct.com/d2/lib/color"
|
||||
|
|
@ -19,13 +20,13 @@ type GoFPDF struct {
|
|||
|
||||
func Init() *GoFPDF {
|
||||
newGofPDF := gofpdf.NewCustom(&gofpdf.InitType{
|
||||
UnitStr: "in",
|
||||
UnitStr: "pt",
|
||||
})
|
||||
|
||||
newGofPDF.AddUTF8FontFromBytes("source", "", d2fonts.FontFaces[d2fonts.SourceSansPro.Font(0, d2fonts.FONT_STYLE_REGULAR)])
|
||||
newGofPDF.AddUTF8FontFromBytes("source", "B", d2fonts.FontFaces[d2fonts.SourceSansPro.Font(0, d2fonts.FONT_STYLE_BOLD)])
|
||||
newGofPDF.SetAutoPageBreak(false, 0)
|
||||
newGofPDF.SetLineWidth(0.05)
|
||||
newGofPDF.SetLineWidth(2)
|
||||
newGofPDF.SetMargins(0, 0, 0)
|
||||
|
||||
fpdf := GoFPDF{
|
||||
|
|
@ -57,7 +58,7 @@ func (g *GoFPDF) GetFillRGB(themeID int64, fill string) (color.RGB, error) {
|
|||
return color.Hex2RGB(fill)
|
||||
}
|
||||
|
||||
func (g *GoFPDF) AddPDFPage(png []byte, boardPath []string, themeID int64, fill string) error {
|
||||
func (g *GoFPDF) AddPDFPage(png []byte, boardPath []string, themeID int64, fill string, shapes []d2target.Shape, pad int64, viewboxX, viewboxY float64) error {
|
||||
var opt gofpdf.ImageOptions
|
||||
opt.ImageType = "png"
|
||||
imageInfo := g.pdf.RegisterImageOptionsReader(strings.Join(boardPath, "/"), opt, bytes.NewReader(png))
|
||||
|
|
@ -73,10 +74,10 @@ func (g *GoFPDF) AddPDFPage(png []byte, boardPath []string, themeID int64, fill
|
|||
|
||||
g.pdf.SetFont("source", "B", 14)
|
||||
pathString := strings.Join(boardPath, " / ")
|
||||
headerMargin := 0.3
|
||||
headerMargin := 28.0
|
||||
headerWidth := g.pdf.GetStringWidth(pathString) + 2*headerMargin
|
||||
|
||||
minPageDimension := 6.0
|
||||
minPageDimension := 576.0
|
||||
pageWidth = math.Max(math.Max(minPageDimension, imageWidth), headerWidth)
|
||||
pageHeight = math.Max(minPageDimension, imageHeight)
|
||||
|
||||
|
|
@ -86,7 +87,7 @@ func (g *GoFPDF) AddPDFPage(png []byte, boardPath []string, themeID int64, fill
|
|||
}
|
||||
|
||||
// Add page
|
||||
headerHeight := 0.75
|
||||
headerHeight := 72.0
|
||||
g.pdf.AddPageFormat("", gofpdf.SizeType{Wd: pageWidth, Ht: pageHeight + headerHeight})
|
||||
|
||||
// Draw header
|
||||
|
|
@ -117,17 +118,30 @@ func (g *GoFPDF) AddPDFPage(png []byte, boardPath []string, themeID int64, fill
|
|||
g.pdf.CellFormat(pageWidth-prefixWidth-headerMargin, headerHeight, boardName, "", 0, "", false, 0, "")
|
||||
|
||||
// Draw image
|
||||
g.pdf.ImageOptions(strings.Join(boardPath, "/"), (pageWidth-imageWidth)/2, headerHeight+(pageHeight-imageHeight)/2, imageWidth, imageHeight, false, opt, 0, "")
|
||||
imageX := (pageWidth - imageWidth) / 2
|
||||
imageY := headerHeight + (pageHeight-imageHeight)/2
|
||||
g.pdf.ImageOptions(strings.Join(boardPath, "/"), imageX, imageY, imageWidth, imageHeight, false, opt, 0, "")
|
||||
|
||||
// Draw external links
|
||||
for _, shape := range shapes {
|
||||
if shape.Link != "" {
|
||||
linkX := imageX + float64(shape.Pos.X) - viewboxX - float64(shape.StrokeWidth)
|
||||
linkY := imageY + float64(shape.Pos.Y) - viewboxY - float64(shape.StrokeWidth)
|
||||
linkWidth := float64(shape.Width) + float64(shape.StrokeWidth*2)
|
||||
linkHeight := float64(shape.Height) + float64(shape.StrokeWidth*2)
|
||||
g.pdf.LinkString(linkX, linkY, linkWidth, linkHeight, shape.Link)
|
||||
}
|
||||
}
|
||||
|
||||
// Draw header/img seperator
|
||||
g.pdf.SetXY(headerMargin, headerHeight)
|
||||
g.pdf.SetLineWidth(0.01)
|
||||
g.pdf.SetLineWidth(1)
|
||||
if fillRGB.IsLight() {
|
||||
g.pdf.SetDrawColor(10, 15, 37) // steel-900
|
||||
} else {
|
||||
g.pdf.SetDrawColor(255, 255, 255)
|
||||
}
|
||||
g.pdf.CellFormat(pageWidth-(headerMargin*2), 0.01, "", "T", 0, "", false, 0, "")
|
||||
g.pdf.CellFormat(pageWidth-(headerMargin*2), 1, "", "T", 0, "", false, 0, "")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package version
|
||||
|
||||
// Pre-built binaries will have version set correctly during build time.
|
||||
var Version = "v0.2.1-HEAD"
|
||||
var Version = "v0.2.2-HEAD"
|
||||
|
|
|
|||
Loading…
Reference in a new issue