mirror of
https://github.com/kovetskiy/mark.git
synced 2025-06-05 13:42:40 +08:00
108 lines
2.7 KiB
Go
108 lines
2.7 KiB
Go
package d2
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/chromedp/cdproto/dom"
|
|
"github.com/chromedp/chromedp"
|
|
|
|
"github.com/kovetskiy/mark/attachment"
|
|
"github.com/reconquest/pkg/log"
|
|
|
|
"oss.terrastruct.com/d2/d2graph"
|
|
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
|
|
"oss.terrastruct.com/d2/d2lib"
|
|
"oss.terrastruct.com/d2/d2renderers/d2svg"
|
|
"oss.terrastruct.com/d2/d2themes/d2themescatalog"
|
|
d2log "oss.terrastruct.com/d2/lib/log"
|
|
"oss.terrastruct.com/d2/lib/textmeasure"
|
|
"oss.terrastruct.com/util-go/go2"
|
|
)
|
|
|
|
var renderTimeout = 120 * time.Second
|
|
|
|
func ProcessD2(title string, d2Diagram []byte, scale float64) (attachment.Attachment, error) {
|
|
ctx, cancel := context.WithTimeout(context.TODO(), renderTimeout)
|
|
ctx = d2log.WithDefault(ctx)
|
|
defer cancel()
|
|
|
|
ruler, err := textmeasure.NewRuler()
|
|
if err != nil {
|
|
return attachment.Attachment{}, err
|
|
}
|
|
layoutResolver := func(engine string) (d2graph.LayoutGraph, error) {
|
|
return d2dagrelayout.DefaultLayout, nil
|
|
}
|
|
renderOpts := &d2svg.RenderOpts{
|
|
Pad: go2.Pointer(int64(5)),
|
|
ThemeID: &d2themescatalog.GrapeSoda.ID,
|
|
}
|
|
compileOpts := &d2lib.CompileOptions{
|
|
LayoutResolver: layoutResolver,
|
|
Ruler: ruler,
|
|
}
|
|
|
|
diagram, _, err := d2lib.Compile(ctx, string(d2Diagram), compileOpts, renderOpts)
|
|
if err != nil {
|
|
return attachment.Attachment{}, err
|
|
}
|
|
|
|
out, err := d2svg.Render(diagram, renderOpts)
|
|
if err != nil {
|
|
return attachment.Attachment{}, err
|
|
}
|
|
|
|
log.Debugf(nil, "Rendering: %q", title)
|
|
pngBytes, boxModel, err := convertSVGtoPNG(ctx, out, scale)
|
|
if err != nil {
|
|
return attachment.Attachment{}, err
|
|
}
|
|
|
|
checkSum, err := attachment.GetChecksum(bytes.NewReader(d2Diagram))
|
|
log.Debugf(nil, "Checksum: %q -> %s", title, checkSum)
|
|
|
|
if err != nil {
|
|
return attachment.Attachment{}, err
|
|
}
|
|
if title == "" {
|
|
title = checkSum
|
|
}
|
|
|
|
fileName := title + ".png"
|
|
|
|
return attachment.Attachment{
|
|
ID: "",
|
|
Name: title,
|
|
Filename: fileName,
|
|
FileBytes: pngBytes,
|
|
Checksum: checkSum,
|
|
Replace: title,
|
|
Width: strconv.FormatInt(boxModel.Width, 10),
|
|
Height: strconv.FormatInt(boxModel.Height, 10),
|
|
}, nil
|
|
}
|
|
|
|
func convertSVGtoPNG(ctx context.Context, svg []byte, scale float64) (png []byte, m *dom.BoxModel, err error) {
|
|
var (
|
|
result []byte
|
|
model *dom.BoxModel
|
|
)
|
|
ctx, cancel := chromedp.NewContext(ctx)
|
|
defer cancel()
|
|
|
|
err = chromedp.Run(ctx,
|
|
chromedp.Navigate(fmt.Sprintf("data:image/svg+xml;base64,%s", base64.StdEncoding.EncodeToString(svg))),
|
|
chromedp.ScreenshotScale(`document.querySelector("svg > svg")`, scale, &result, chromedp.ByJSPath),
|
|
chromedp.Dimensions(`document.querySelector("svg > svg")`, &model, chromedp.ByJSPath),
|
|
)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return result, model, err
|
|
}
|