Add ability to encode raw byte slices as Typst images
Some checks failed
golangci-lint / lint (push) Successful in 20s
test / test (1.23.x, 0.12.0) (push) Failing after 4s
test / test (1.23.x, 0.13.0) (push) Failing after 3s
test / test (1.23.x, 0.13.1) (push) Failing after 3s
test / test (1.23.x, 0.14.0) (push) Failing after 3s

This commit is contained in:
David Vogel 2025-11-04 23:17:17 +01:00
parent bc69f35948
commit 513d2ae906
4 changed files with 53 additions and 2 deletions

View File

@ -22,7 +22,8 @@ Use at your own discretion for production systems.
- PDF, SVG and PNG generation.
- All Typst parameters are discoverable and documented in [cli-options.go](cli-options.go).
- Go-to-Typst Value Encoder: Seamlessly inject any Go values (Including `image.Image` with a [wrapper](image.go)) into Typst documents via the provided encoder.
- Go-to-Typst Value Encoder: Seamlessly inject any Go values.
- Encode and inject images as a Typst markup simply by [wrapping](image.go) `image.Image` types or byte slices with raw JPEG or PNG data.
- Errors from Typst CLI are returned as structured Go error objects with detailed information, such as line numbers and file paths.
- Uses stdio; No temporary files will be created.
- Good unit test coverage.

View File

@ -15,7 +15,10 @@ import (
// Image can be used to encode any image.Image into a Typst image.
//
// For this, just wrap any image.Image with this type before passing it to MarshalValue or a ValueEncoder.
// For this, just wrap any image.Image with this type before passing it to MarshalValue or a ValueEncoder:
//
// typstImage := typst.Image{img}
// typst.InjectValues(&r, map[string]any{"TestImage": typstImage})
type Image struct{ image.Image }
func (i Image) MarshalTypstValue() ([]byte, error) {
@ -38,3 +41,23 @@ func (i Image) MarshalTypstValue() ([]byte, error) {
return buf.Bytes(), nil
}
// ImageRaw can be used to pass the raw data of any image to Typst.
// This will pass the raw byte values of a PNG, JPEG or any other image format that is supported by Typst.
//
// For this, just wrap any byte slice with this type before passing it to MarshalValue or a ValueEncoder:
//
// typstImage := typst.ImageRaw(bufferPNG)
// typst.InjectValues(&r, map[string]any{"TestImage": typstImage})
type ImageRaw []byte
func (i ImageRaw) MarshalTypstValue() ([]byte, error) {
var buf bytes.Buffer
buf.WriteString("image.decode(bytes((") // TODO: Pass bytes directly to image once Typst 0.12.0 is not supported anymore
for _, b := range i {
buf.WriteString(strconv.FormatUint(uint64(b), 10) + ",")
}
buf.WriteString(")))")
return buf.Bytes(), nil
}

View File

@ -7,6 +7,7 @@ package typst_test
import (
"bytes"
_ "embed"
"image"
"image/color"
"io"
@ -61,3 +62,29 @@ func TestImage(t *testing.T) {
t.Fatalf("Failed to compile document: %v.", err)
}
}
//go:embed test-files/test.png
var testPNG []byte
func TestImageRaw(t *testing.T) {
// Wrap image.
typstImage := typst.ImageRaw(testPNG)
cli := typst.CLI{}
var r bytes.Buffer
if err := typst.InjectValues(&r, map[string]any{"TestImage": typstImage}); err != nil {
t.Fatalf("Failed to inject values into Typst markup: %v.", err)
}
r.WriteString(`= Image test
#TestImage
#assert(type(TestImage) == content, message: "TestImage is not of expected type: got " + str(type(TestImage)) + ", want content")`) // TODO: Add another assertion for the image width and height as soon as it's possible to query that
if err := cli.Compile(&r, io.Discard, nil); err != nil {
t.Fatalf("Failed to compile document: %v.", err)
}
}

BIN
test-files/test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 405 B