use scale flag for no-fit and customizing zoom

This commit is contained in:
Gavin Nishizawa 2023-06-15 15:53:23 -07:00
parent b8436358fb
commit 8563cf83c6
No known key found for this signature in database
GPG key ID: AE3B177777CE55CD
5 changed files with 84 additions and 34 deletions

View file

@ -80,6 +80,9 @@ Renders the diagram to look like it was sketched by hand
.It Fl -center Ar flag .It Fl -center Ar flag
Center the SVG in the containing viewbox, such as your browser screen Center the SVG in the containing viewbox, such as your browser screen
.Ns . .Ns .
.It Fl -scale Ar fit
Set a fixed render scale (e.g. 0.5 to halve rendered size). If none provided, SVG will fit to screen
.Ns .
.It Fl -font-regular .It Fl -font-regular
Path to .ttf file to use for the regular font. If none provided, Source Sans Pro Regular is used Path to .ttf file to use for the regular font. If none provided, Source Sans Pro Regular is used
.Ns . .Ns .

View file

@ -107,6 +107,7 @@ func Run(ctx context.Context, ms *xmain.State) (err error) {
if err != nil { if err != nil {
return err return err
} }
scaleFlag := ms.Opts.String("SCALE", "scale", "", "fit", "set a fixed render scale (e.g. 0.5 to halve rendered size). If none provided, SVG will fit to screen.")
fontRegularFlag := ms.Opts.String("D2_FONT_REGULAR", "font-regular", "", "", "path to .ttf file to use for the regular font. If none provided, Source Sans Pro Regular is used.") fontRegularFlag := ms.Opts.String("D2_FONT_REGULAR", "font-regular", "", "", "path to .ttf file to use for the regular font. If none provided, Source Sans Pro Regular is used.")
fontItalicFlag := ms.Opts.String("D2_FONT_ITALIC", "font-italic", "", "", "path to .ttf file to use for the italic font. If none provided, Source Sans Pro Regular-Italic is used.") fontItalicFlag := ms.Opts.String("D2_FONT_ITALIC", "font-italic", "", "", "path to .ttf file to use for the italic font. If none provided, Source Sans Pro Regular-Italic is used.")
@ -232,6 +233,14 @@ func Run(ctx context.Context, ms *xmain.State) (err error) {
} }
ms.Log.Debug.Printf("using dark theme %s (ID: %d)", match.Name, *darkThemeFlag) ms.Log.Debug.Printf("using dark theme %s (ID: %d)", match.Name, *darkThemeFlag)
} }
var scale *float64
if scaleFlag != nil && *scaleFlag != "fit" {
f, err := strconv.ParseFloat(*scaleFlag, 64)
if err != nil || f <= 0. {
return xmain.UsageErrorf("-scale must a positive floating point number or \"fit\".")
}
scale = &f
}
plugin, err := d2plugin.FindPlugin(ctx, ps, *layoutFlag) plugin, err := d2plugin.FindPlugin(ctx, ps, *layoutFlag)
if err != nil { if err != nil {
@ -282,6 +291,7 @@ func Run(ctx context.Context, ms *xmain.State) (err error) {
Center: *centerFlag, Center: *centerFlag,
ThemeID: *themeFlag, ThemeID: *themeFlag,
DarkThemeID: darkThemeFlag, DarkThemeID: darkThemeFlag,
Scale: scale,
} }
if *watchFlag { if *watchFlag {
@ -665,14 +675,20 @@ func render(ctx context.Context, ms *xmain.State, compileDur time.Duration, plug
func _render(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, opts d2svg.RenderOpts, outputPath string, bundle, forceAppendix bool, page playwright.Page, ruler *textmeasure.Ruler, diagram *d2target.Diagram) ([]byte, error) { func _render(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, opts d2svg.RenderOpts, outputPath string, bundle, forceAppendix bool, page playwright.Page, ruler *textmeasure.Ruler, diagram *d2target.Diagram) ([]byte, error) {
toPNG := getExportExtension(outputPath) == PNG toPNG := getExportExtension(outputPath) == PNG
var scale *float64
if opts.Scale != nil {
scale = opts.Scale
} else if toPNG {
scale = go2.Pointer(1.)
}
svg, err := d2svg.Render(diagram, &d2svg.RenderOpts{ svg, err := d2svg.Render(diagram, &d2svg.RenderOpts{
Pad: opts.Pad, Pad: opts.Pad,
Sketch: opts.Sketch, Sketch: opts.Sketch,
Center: opts.Center, Center: opts.Center,
ThemeID: opts.ThemeID, ThemeID: opts.ThemeID,
DarkThemeID: opts.DarkThemeID, DarkThemeID: opts.DarkThemeID,
MasterID: opts.MasterID, MasterID: opts.MasterID,
SetDimensions: toPNG, Scale: scale,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@ -746,11 +762,18 @@ func renderPDF(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, opt
// make the bg fill within the png transparent so that the pdf bg fill is the only bg color present // make the bg fill within the png transparent so that the pdf bg fill is the only bg color present
diagram.Root.Fill = "transparent" diagram.Root.Fill = "transparent"
var scale *float64
if opts.Scale != nil {
scale = opts.Scale
} else {
scale = go2.Pointer(1.)
}
svg, err = d2svg.Render(diagram, &d2svg.RenderOpts{ svg, err = d2svg.Render(diagram, &d2svg.RenderOpts{
Pad: opts.Pad, Pad: opts.Pad,
Sketch: opts.Sketch, Sketch: opts.Sketch,
Center: opts.Center, Center: opts.Center,
SetDimensions: true, Scale: scale,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@ -837,12 +860,20 @@ func renderPPTX(ctx context.Context, ms *xmain.State, presentation *pptx.Present
// make the bg fill within the png transparent so that the pdf bg fill is the only bg color present // make the bg fill within the png transparent so that the pdf bg fill is the only bg color present
diagram.Root.Fill = "transparent" diagram.Root.Fill = "transparent"
var scale *float64
if opts.Scale != nil {
scale = opts.Scale
} else {
scale = go2.Pointer(1.)
}
var err error var err error
svg, err = d2svg.Render(diagram, &d2svg.RenderOpts{ svg, err = d2svg.Render(diagram, &d2svg.RenderOpts{
Pad: opts.Pad, Pad: opts.Pad,
Sketch: opts.Sketch, Sketch: opts.Sketch,
Center: opts.Center, Center: opts.Center,
SetDimensions: true, Scale: scale,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@ -1076,11 +1107,18 @@ func buildBoardIDToIndex(diagram *d2target.Diagram, dictionary map[string]int, p
func renderPNGsForGIF(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, opts d2svg.RenderOpts, ruler *textmeasure.Ruler, page playwright.Page, diagram *d2target.Diagram) (svg []byte, pngs [][]byte, err error) { func renderPNGsForGIF(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, opts d2svg.RenderOpts, ruler *textmeasure.Ruler, page playwright.Page, diagram *d2target.Diagram) (svg []byte, pngs [][]byte, err error) {
if !diagram.IsFolderOnly { if !diagram.IsFolderOnly {
var scale *float64
if opts.Scale != nil {
scale = opts.Scale
} else {
scale = go2.Pointer(1.)
}
svg, err = d2svg.Render(diagram, &d2svg.RenderOpts{ svg, err = d2svg.Render(diagram, &d2svg.RenderOpts{
Pad: opts.Pad, Pad: opts.Pad,
Sketch: opts.Sketch, Sketch: opts.Sketch,
Center: opts.Center, Center: opts.Center,
SetDimensions: true, Scale: scale,
}) })
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err

View file

@ -35,12 +35,16 @@ function init(reconnectDelay) {
let width = parseInt(svgEl.getAttribute("width"), 10); let width = parseInt(svgEl.getAttribute("width"), 10);
let height = parseInt(svgEl.getAttribute("height"), 10); let height = parseInt(svgEl.getAttribute("height"), 10);
if (isInit) { if (isInit) {
if (width > height) { if (msg.scale) {
if (width > window.innerWidth) { ratio = msg.scale;
ratio = window.innerWidth / width; } else {
if (width > height) {
if (width > window.innerWidth) {
ratio = window.innerWidth / width;
}
} else if (height > window.innerHeight) {
ratio = window.innerHeight / height;
} }
} else if (height > window.innerHeight) {
ratio = window.innerHeight / height;
} }
// Scale svg fit to zoom // Scale svg fit to zoom
isInit = false; isInit = false;

View file

@ -83,8 +83,9 @@ type watcher struct {
} }
type compileResult struct { type compileResult struct {
SVG string `json:"svg"` SVG string `json:"svg"`
Err string `json:"err"` Scale *float64 `json:"scale,omitEmpty"`
Err string `json:"err"`
} }
func newWatcher(ctx context.Context, ms *xmain.State, opts watcherOpts) (*watcher, error) { func newWatcher(ctx context.Context, ms *xmain.State, opts watcherOpts) (*watcher, error) {
@ -372,8 +373,9 @@ func (w *watcher) compileLoop(ctx context.Context) error {
w.ms.Log.Error.Print(errs) w.ms.Log.Error.Print(errs)
} }
w.broadcast(&compileResult{ w.broadcast(&compileResult{
SVG: string(svg), SVG: string(svg),
Err: errs, Scale: w.renderOpts.Scale,
Err: errs,
}) })
if firstCompile { if firstCompile {

View file

@ -75,8 +75,8 @@ type RenderOpts struct {
ThemeID int64 ThemeID int64
DarkThemeID *int64 DarkThemeID *int64
Font string Font string
// disables the fit to screen behavior and ensures the exported svg has the exact dimensions // the svg will be scaled by this factor, if unset the svg will fit to screen
SetDimensions bool Scale *float64
// MasterID is passed when the diagram should use something other than its own hash for unique targeting // MasterID is passed when the diagram should use something other than its own hash for unique targeting
// Currently, that's when multi-boards are collapsed // Currently, that's when multi-boards are collapsed
@ -1648,7 +1648,7 @@ func Render(diagram *d2target.Diagram, opts *RenderOpts) ([]byte, error) {
pad := DEFAULT_PADDING pad := DEFAULT_PADDING
themeID := DEFAULT_THEME themeID := DEFAULT_THEME
darkThemeID := DEFAULT_DARK_THEME darkThemeID := DEFAULT_DARK_THEME
setDimensions := false var scale *float64
if opts != nil { if opts != nil {
pad = opts.Pad pad = opts.Pad
if opts.Sketch { if opts.Sketch {
@ -1660,7 +1660,7 @@ func Render(diagram *d2target.Diagram, opts *RenderOpts) ([]byte, error) {
} }
themeID = opts.ThemeID themeID = opts.ThemeID
darkThemeID = opts.DarkThemeID darkThemeID = opts.DarkThemeID
setDimensions = opts.SetDimensions scale = opts.Scale
} }
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
@ -1851,8 +1851,11 @@ func Render(diagram *d2target.Diagram, opts *RenderOpts) ([]byte, error) {
} }
var dimensions string var dimensions string
if setDimensions { if scale != nil {
dimensions = fmt.Sprintf(` width="%d" height="%d"`, w, h) dimensions = fmt.Sprintf(` width="%d" height="%d"`,
int(math.Ceil((*scale)*float64(w))),
int(math.Ceil((*scale)*float64(h))),
)
} }
alignment := "xMinYMin" alignment := "xMinYMin"