Add mermaid suport
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
@@ -26,10 +26,14 @@ import (
|
||||
"github.com/yuin/goldmark"
|
||||
"github.com/yuin/goldmark/extension"
|
||||
gmhtml "github.com/yuin/goldmark/renderer/html"
|
||||
"go.abhg.dev/goldmark/mermaid"
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed mermaid.min.js
|
||||
mermaidJSSource string
|
||||
|
||||
//go:embed template.html
|
||||
htmlTemplateContent string
|
||||
|
||||
@@ -72,7 +76,13 @@ var (
|
||||
},
|
||||
"formatContent": func(content string) template.HTML {
|
||||
md := goldmark.New(
|
||||
goldmark.WithExtensions(extension.Table),
|
||||
goldmark.WithExtensions(
|
||||
extension.Table,
|
||||
&mermaid.Extender{
|
||||
RenderMode: mermaid.RenderModeClient,
|
||||
NoScript: true,
|
||||
},
|
||||
),
|
||||
goldmark.WithRendererOptions(
|
||||
gmhtml.WithUnsafe(),
|
||||
),
|
||||
@@ -209,6 +219,7 @@ type (
|
||||
PublishedAt *time.Time
|
||||
Signatures []SignatureData
|
||||
CompanyHorizontalLogoBase64 string
|
||||
MermaidJS template.JS
|
||||
}
|
||||
|
||||
SignatureData struct {
|
||||
@@ -321,6 +332,8 @@ const (
|
||||
)
|
||||
|
||||
func RenderHTML(data DocumentData) ([]byte, error) {
|
||||
data.MermaidJS = template.JS(mermaidJSSource)
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := documentTemplate.Execute(&buf, data); err != nil {
|
||||
return nil, fmt.Errorf("cannot execute template: %w", err)
|
||||
|
||||
2843
pkg/docgen/mermaid.min.js
vendored
Normal file
2843
pkg/docgen/mermaid.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -341,7 +341,31 @@
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.document-content pre.mermaid {
|
||||
border: none;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.document-content pre.mermaid svg {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
</style>
|
||||
<script>{{.MermaidJS}}</script>
|
||||
<script>
|
||||
window.__mermaidReady = false;
|
||||
mermaid.initialize({ startOnLoad: false, theme: 'neutral' });
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
mermaid.run().then(function () {
|
||||
window.__mermaidReady = true;
|
||||
}).catch(function () {
|
||||
window.__mermaidReady = true;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="document-container">
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/chromedp/cdproto/page"
|
||||
"github.com/chromedp/chromedp"
|
||||
@@ -41,14 +42,16 @@ var (
|
||||
|
||||
type (
|
||||
RenderConfig struct {
|
||||
PageFormat PageFormat
|
||||
Orientation Orientation
|
||||
MarginTop Margin
|
||||
MarginBottom Margin
|
||||
MarginLeft Margin
|
||||
MarginRight Margin
|
||||
PrintBackground bool
|
||||
Scale float64 // Print scale (0.1 to 2.0)
|
||||
PageFormat PageFormat
|
||||
Orientation Orientation
|
||||
MarginTop Margin
|
||||
MarginBottom Margin
|
||||
MarginLeft Margin
|
||||
MarginRight Margin
|
||||
PrintBackground bool
|
||||
Scale float64 // Print scale (0.1 to 2.0)
|
||||
WaitForExpression string // Optional JS expression polled until true before printing
|
||||
WaitForExpressionTimeout time.Duration // Max time to wait (default 15s)
|
||||
}
|
||||
|
||||
Option func(*Converter)
|
||||
@@ -162,6 +165,30 @@ func (c *Converter) GeneratePDF(ctx context.Context, htmlDocument []byte, cfg Re
|
||||
|
||||
htmlContent := string(htmlDocument)
|
||||
|
||||
waitTimeout := cfg.WaitForExpressionTimeout
|
||||
if waitTimeout <= 0 {
|
||||
waitTimeout = 15 * time.Second
|
||||
}
|
||||
|
||||
waitForExpr := chromedp.ActionFunc(func(ctx context.Context) error {
|
||||
if cfg.WaitForExpression == "" {
|
||||
return nil
|
||||
}
|
||||
deadline := time.Now().Add(waitTimeout)
|
||||
for time.Now().Before(deadline) {
|
||||
var ready bool
|
||||
if err := chromedp.Evaluate(cfg.WaitForExpression, &ready).Do(ctx); err != nil {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
if ready {
|
||||
return nil
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
return nil // proceed even on timeout
|
||||
})
|
||||
|
||||
err := chromedp.Run(
|
||||
ctx,
|
||||
chromedp.Navigate("about:blank"),
|
||||
@@ -178,6 +205,7 @@ func (c *Converter) GeneratePDF(ctx context.Context, htmlDocument []byte, cfg Re
|
||||
}),
|
||||
chromedp.WaitReady("body"),
|
||||
chromedp.ActionFunc(waitUntilDocumentReady),
|
||||
waitForExpr,
|
||||
chromedp.ActionFunc(
|
||||
func(ctx context.Context) (err error) {
|
||||
pdfBytes, _, err = page.PrintToPDF().
|
||||
|
||||
@@ -1884,14 +1884,15 @@ func exportDocumentPDF(
|
||||
}
|
||||
|
||||
cfg := html2pdf.RenderConfig{
|
||||
PageFormat: html2pdf.PageFormatA4,
|
||||
Orientation: html2pdf.OrientationPortrait,
|
||||
MarginTop: html2pdf.NewMarginInches(1.0),
|
||||
MarginBottom: html2pdf.NewMarginInches(1.0),
|
||||
MarginLeft: html2pdf.NewMarginInches(1.0),
|
||||
MarginRight: html2pdf.NewMarginInches(1.0),
|
||||
PrintBackground: true,
|
||||
Scale: 1.0,
|
||||
PageFormat: html2pdf.PageFormatA4,
|
||||
Orientation: html2pdf.OrientationPortrait,
|
||||
MarginTop: html2pdf.NewMarginInches(1.0),
|
||||
MarginBottom: html2pdf.NewMarginInches(1.0),
|
||||
MarginLeft: html2pdf.NewMarginInches(1.0),
|
||||
MarginRight: html2pdf.NewMarginInches(1.0),
|
||||
PrintBackground: true,
|
||||
Scale: 1.0,
|
||||
WaitForExpression: "window.__mermaidReady === true",
|
||||
}
|
||||
|
||||
pdfReader, err := html2pdfConverter.GeneratePDF(ctx, htmlContent, cfg)
|
||||
|
||||
Reference in New Issue
Block a user