Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 5 additions & 0 deletions ci/release/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,11 @@ closed when the variable is absent; no draft is uploaded.

## build.sh

Release verification limits each binary to 45,000,000 bytes and each archive to
20,000,000 bytes. The archive limit accommodates native isometric rendering on
macOS as well as Linux and Windows. All six release targets retain size limits
and metadata/checksum verification.

- ./build.sh builds the release archives for each platform into ./build/<VERSION>/*.tar.gz
Run with --help for usage.

Expand Down
4 changes: 3 additions & 1 deletion ci/release/_build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ sh_c GOOS="$GOOS" GOARCH="$GOARCH" go build -mod=readonly -pgo=off \

# Allow the bundled TALA engine and native exporters on all six release targets.
MAX_RELEASE_BINARY_BYTES=${MAX_RELEASE_BINARY_BYTES:-45000000}
MAX_RELEASE_ARCHIVE_BYTES=${MAX_RELEASE_ARCHIVE_BYTES:-19000000}
# Mach-O archives are larger than Linux archives; the isometric renderer puts
# the macOS/amd64 archive above 19 MB even with maximum gzip compression.
MAX_RELEASE_ARCHIVE_BYTES=${MAX_RELEASE_ARCHIVE_BYTES:-20000000}
sh_c "$RELEASE_TOOL" verify-binary \
--path "'$BINARY'" \
--goos "$GOOS" \
Expand Down
1 change: 1 addition & 0 deletions ci/release/changelogs/next.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#### Features 🚀

- elk: configure the spacing between edges routed between the same pair of layers with `--elk-edgeEdgeBetweenLayers`. It was previously fixed at 50, which forced large gaps between layers on graphs with many links between adjacent layers.
- renders: add native isometric SVG, PNG, GIF, PDF, and PPTX exports with `--isometric`, source configuration, and Go/JavaScript options

#### Improvements 🧹

Expand Down
25 changes: 25 additions & 0 deletions ci/release/template/man/d2.1
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,29 @@ making style maps in D2 light/dark mode specific. See
.It Fl s , -sketch Ar false
Renders the diagram to look like it was sketched by hand
.Ns .
.It Fl -isometric Ar false
Render SVG, PNG, GIF, PDF or PPTX with the native Go isometric renderer.
Preserve compiled positions and dimensions, and render nested source containers as platforms.
Like sketch, this can also be enabled using D2_ISOMETRIC=true or
vars: {d2-config: {isometric: true}} in D2 source.
An explicit flag takes precedence over the environment, which takes precedence over source config.
Use --isometric=false to disable a source or environment setting.
Without an output filename, --isometric defaults to SVG.
No browser or GPU is required.
Images fit the diagram within 1600 by 1000 pixels, or 1000 by 625 pixels for
GIF, with a fixed camera. Use --scale to change those maximum dimensions.
A single-board GIF uses an 8.33-second traffic cycle, or an 8-second
cycle when it includes animated shapes or source panels, so both loop cleanly.
Authored style.animated enables motion.
Multiple boards export to SVG or PNG directories, PDF pages, PPTX slides or one GIF slideshow, in root,
layers, scenarios, steps order. Folder-only boards are skipped.
PDF and PPTX preserve board navigation and clickable source links.
Their pages embed the same native image as PNG export, with projected link regions.
GIF slides default to 1000ms per board; --animate-interval changes that delay.
Use --target to select a board or --target='' for the root without subboards.
Cannot be combined with sketch or force-appendix. Isometric SVG currently rejects dark-theme
and animated multi-board SVG; use separate themed SVGs or a GIF slideshow.
.Ns .
.It Fl -center Ar flag
Center the SVG in the containing viewbox, such as your browser screen
.Ns .
Expand Down Expand Up @@ -218,6 +241,8 @@ See --pad flag.
See --center flag.
.It Ev Sy D2_SKETCH
See -s[ketch] flag.
.It Ev Sy D2_ISOMETRIC
See --isometric flag.
.It Ev Sy D2_BUNDLE
See -b[undle] flag.
.It Ev Sy D2_ALLOW_PRIVATE_NETWORK
Expand Down
17 changes: 15 additions & 2 deletions d2cli/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@ const PDF exportExtension = ".pdf"
const SVG exportExtension = ".svg"
const TXT exportExtension = ".txt"

// Internal selectors keep the opt-in isometric renderer separate from 2D exports.
const isometricSVG exportExtension = "isometric-svg"
const isometricPNG exportExtension = "isometric-png"
const isometricGIF exportExtension = "isometric-gif"
const isometricPDF exportExtension = "isometric-pdf"
const isometricPPTX exportExtension = "isometric-pptx"

func (ex exportExtension) isIsometric() bool {
return ex == isometricSVG || ex == isometricPNG || ex == isometricGIF || ex == isometricPDF || ex == isometricPPTX
}

var SUPPORTED_EXTENSIONS = []exportExtension{SVG, PNG, PDF, PPTX, GIF, TXT}

var STDOUT_FORMAT_MAP = map[string]exportExtension{
Expand Down Expand Up @@ -52,9 +63,11 @@ func getExportExtension(outputPath string) exportExtension {
}

func (ex exportExtension) supportsAnimation() bool {
return ex == SVG || ex == GIF
return ex == SVG || ex == GIF || ex == isometricGIF
}

func (ex exportExtension) supportsDarkTheme() bool {
return ex == SVG
// Preserve a requested isometric dark theme until its renderer can issue a
// clear unsupported-feature error, rather than silently stripping the flag.
return ex == SVG || ex == isometricSVG
}
40 changes: 40 additions & 0 deletions d2cli/export_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package d2cli

import (
"encoding/xml"
"os"
"path/filepath"
"strings"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -87,3 +91,39 @@ func TestOutputFormat(t *testing.T) {
})
}
}

func TestUnsupportedStdoutFormatDoesNotWrite(t *testing.T) {
for _, format := range []string{"html", "HTML", "unknown"} {
t.Run(format, func(t *testing.T) {
data, err := imageCLI(t, t.TempDir(), "a -> b", "--stdout-format="+format, "-")
if err == nil || !strings.Contains(err.Error(), "is not a supported format") {
t.Fatalf("unsupported format %q: got %v", format, err)
}
if len(data) != 0 {
t.Fatal("unsupported format wrote output")
}
})
}
}

func TestUnknownOutputExtensionUsesSVG(t *testing.T) {
for _, name := range []string{"out.html", "out.unknown"} {
t.Run(name, func(t *testing.T) {
dir := t.TempDir()
if _, err := imageCLI(t, dir, "a -> b", name); err != nil {
t.Fatal(err)
}
data, err := os.ReadFile(filepath.Join(dir, name))
if err != nil {
t.Fatal(err)
}
var document struct{ XMLName xml.Name }
if err := xml.Unmarshal(data, &document); err != nil {
t.Fatal(err)
}
if document.XMLName.Local != "svg" || document.XMLName.Space != "http://www.w3.org/2000/svg" {
t.Fatalf("unknown extension no longer defaults to SVG: %v", document.XMLName)
}
})
}
}
13 changes: 13 additions & 0 deletions d2cli/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,19 @@ Usage:

%[1]s compiles and renders file.d2 to file.svg | file.png | file.pdf | file.pptx | file.gif | file.txt
It defaults to file.svg if an output path is not provided.
Use --isometric for SVG, PNG, GIF, PDF or PPTX output with the native isometric renderer:
%[1]s --isometric file.d2 file.svg
%[1]s --isometric file.d2 file.png
%[1]s --isometric file.d2 file.gif
%[1]s --isometric file.d2 file.pdf
%[1]s --isometric file.d2 file.pptx
You can also set D2_ISOMETRIC=true or vars: {d2-config: {isometric: true}} in D2 source.
Flags override environment variables, which override source configuration; --isometric=false disables it.
Isometric images preserve the compiled layout and render entirely in Go; no browser or GPU is required.
Images fit the diagram within 1600x1000 pixels (1000x625 for GIF); --scale changes those maximum dimensions.
Multiple boards export to SVG or PNG directories, PDF pages, PPTX slides or one GIF slideshow.
PDF and PPTX retain board navigation and clickable links. --animate-interval sets the GIF slide delay.
Use --target to select a board, or --target='' for the root without its subboards.

Use - to have d2 read from stdin or write to stdout.

Expand Down
37 changes: 37 additions & 0 deletions d2cli/isometric.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package d2cli

import (
"fmt"

"github.com/d2lang/util-go/xmain"
)

// Keep an unset mode distinct from an explicit false so embedded D2 config has
// exactly the same precedence as sketch: flags, then environment, then source.
func resolveIsometricFlag(ms *xmain.State) (*bool, error) {
isometric, err := ms.Opts.Flags.GetBool("isometric")
if err != nil {
return nil, err
}
if ms.Opts.Flags.Changed("isometric") || ms.Env.Getenv("D2_ISOMETRIC") != "" {
return &isometric, nil
}
return nil, nil
}

func isometricExportExtension(ext exportExtension) (exportExtension, error) {
switch ext {
case SVG, isometricSVG:
return isometricSVG, nil
case PNG, isometricPNG:
return isometricPNG, nil
case GIF, isometricGIF:
return isometricGIF, nil
case PDF, isometricPDF:
return isometricPDF, nil
case PPTX, isometricPPTX:
return isometricPPTX, nil
default:
return "", fmt.Errorf("--isometric exports SVG, PNG, GIF, PDF or PPTX; use a matching filename or --stdout-format")
}
}
Loading
Loading