diff --git a/README.md b/README.md
index b4506d1b..b546de9c 100644
--- a/README.md
+++ b/README.md
@@ -18,12 +18,12 @@ You can write your PDFs like you are creating a site using Bootstrap. A Row may
Besides that, pages will be added when content may extrapolate the useful area. You can define a header which will be added
always when a new page appear, in this case, a header may have many rows, lines or tablelist.
-#### Maroto `v2.0.6` is here! Try out:
+#### Maroto `v2.0.7` is here! Try out:
* Installation with`go get`:
```bash
-go get github.com/johnfercher/maroto/v2@v2.0.6
+go get github.com/johnfercher/maroto/v2@v2.0.7
```
* You can see the full `v2` documentation [here](https://maroto.io/).
diff --git a/docs/README.md b/docs/README.md
index 84b2ee9c..abfc3ae1 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -19,12 +19,12 @@
* We are about to create a document processor to generate PDFs by interpreting serialized data as: yml, json or html. Please contribute with your ideas in [this discussion](https://github.com/johnfercher/maroto/discussions/390).
-#### 3. Maroto`v2.0.6`is here! Try out:
+#### 3. Maroto`v2.0.7`is here! Try out:
* Installation with`go get`:
```bash
-go get github.com/johnfercher/maroto/v2@v2.0.6
+go get github.com/johnfercher/maroto/v2@v2.0.7
```
The public API was completely redesigned with the aim of enhancing the
diff --git a/docs/_coverpage.md b/docs/_coverpage.md
index c162212e..f40bfd00 100644
--- a/docs/_coverpage.md
+++ b/docs/_coverpage.md
@@ -1,6 +1,6 @@
![logo](assets/images/logo.png)
-# Maroto v2.0.6
+# Maroto v2.0.7
> An open-source golang lib to create PDFs. Fast and Simple.
diff --git a/docs/assets/examples/background/v2/main.go b/docs/assets/examples/background/v2/main.go
index 21030e70..3a97aa47 100644
--- a/docs/assets/examples/background/v2/main.go
+++ b/docs/assets/examples/background/v2/main.go
@@ -45,7 +45,9 @@ func GetMaroto(image string) core.Maroto {
log.Fatal(err)
}
b := config.NewBuilder().
- WithMargins(0, 0, 0).
+ WithTopMargin(0).
+ WithLeftMargin(0).
+ WithRightMargin(0).
WithOrientation(orientation.Horizontal).
WithMaxGridSize(20).
WithBackgroundImage(bytes, extension.Png)
diff --git a/docs/assets/examples/billing/v2/main.go b/docs/assets/examples/billing/v2/main.go
index 71b3eee3..87021042 100644
--- a/docs/assets/examples/billing/v2/main.go
+++ b/docs/assets/examples/billing/v2/main.go
@@ -39,7 +39,9 @@ func main() {
func GetMaroto() core.Maroto {
cfg := config.NewBuilder().
WithPageNumber().
- WithMargins(10, 15, 10).
+ WithLeftMargin(10).
+ WithTopMargin(15).
+ WithRightMargin(10).
Build()
darkGrayColor := getDarkGrayColor()
diff --git a/docs/assets/examples/disablepagebreak/v2/main.go b/docs/assets/examples/disablepagebreak/v2/main.go
index 7b1b8d42..9c7463e9 100644
--- a/docs/assets/examples/disablepagebreak/v2/main.go
+++ b/docs/assets/examples/disablepagebreak/v2/main.go
@@ -39,7 +39,9 @@ func GetMaroto(image string) core.Maroto {
log.Fatal(err)
}
b := config.NewBuilder().
- WithMargins(0, 0, 0).
+ WithTopMargin(0).
+ WithRightMargin(0).
+ WithLeftMargin(0).
WithDimensions(361.8, 203.2).
WithDisableAutoPageBreak(true).
WithOrientation(orientation.Horizontal).
diff --git a/docs/assets/examples/margins/v2/main.go b/docs/assets/examples/margins/v2/main.go
index 33a3c05f..ebcb4d34 100644
--- a/docs/assets/examples/margins/v2/main.go
+++ b/docs/assets/examples/margins/v2/main.go
@@ -3,6 +3,8 @@ package main
import (
"log"
+ "github.com/johnfercher/maroto/v2/pkg/components/row"
+
"github.com/johnfercher/maroto/v2"
"github.com/johnfercher/maroto/v2/pkg/components/col"
"github.com/johnfercher/maroto/v2/pkg/components/image"
@@ -32,24 +34,35 @@ func main() {
func GetMaroto() core.Maroto {
cfg := config.NewBuilder().
- WithMargins(20, 20, 20).
+ WithTopMargin(20).
+ WithLeftMargin(20).
+ WithRightMargin(20).
WithDebug(true).
Build()
mrt := maroto.New(cfg)
m := maroto.NewMetricsDecorator(mrt)
- m.AddRow(40,
- image.NewFromFileCol(4, "docs/assets/images/gopherbw.png", props.Rect{
- Center: true,
- Percent: 50,
- }),
- text.NewCol(4, "Margins Test", props.Text{
- Top: 12,
- Size: 12,
- }),
- col.New(4),
+ err := m.RegisterHeader(
+ row.New(40).Add(
+ image.NewFromFileCol(4, "docs/assets/images/gopherbw.png", props.Rect{
+ Center: true,
+ Percent: 50,
+ }),
+ text.NewCol(4, "Margins Test", props.Text{
+ Top: 12,
+ Size: 12,
+ }),
+ col.New(4),
+ ),
)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ for i := 0; i < 10; i++ {
+ m.AddRows(text.NewRow(30, "any text"))
+ }
return m
}
diff --git a/docs/assets/pdf/backgroundv2.pdf b/docs/assets/pdf/backgroundv2.pdf
index c0ac92d6..4c4405bb 100644
Binary files a/docs/assets/pdf/backgroundv2.pdf and b/docs/assets/pdf/backgroundv2.pdf differ
diff --git a/docs/assets/pdf/marginsv2.pdf b/docs/assets/pdf/marginsv2.pdf
index 071bec0d..6db16fa7 100644
Binary files a/docs/assets/pdf/marginsv2.pdf and b/docs/assets/pdf/marginsv2.pdf differ
diff --git a/docs/assets/text/backgroundv2.txt b/docs/assets/text/backgroundv2.txt
index ce5728ad..c4ceaa31 100644
--- a/docs/assets/text/backgroundv2.txt
+++ b/docs/assets/text/backgroundv2.txt
@@ -1,3 +1,3 @@
-generate -> avg: 2169.54ms, executions: [2169.54ms]
-add_page -> avg: 8596.00ns, executions: [8.60μs]
+generate -> avg: 541.29ms, executions: [541.29ms]
+add_page -> avg: 2216.00ns, executions: [2.22μs]
file_size -> 897.39Kb
diff --git a/docs/assets/text/marginsv2.txt b/docs/assets/text/marginsv2.txt
index 0dd16139..50b27b11 100644
--- a/docs/assets/text/marginsv2.txt
+++ b/docs/assets/text/marginsv2.txt
@@ -1,3 +1,4 @@
-generate -> avg: 605.72ms, executions: [605.72ms]
-add_row -> avg: 1543.00ns, executions: [1.54μs]
-file_size -> 253.43Kb
+generate -> avg: 356.27ms, executions: [356.27ms]
+header -> avg: 549.00ns, executions: [549.00ns]
+add_rows -> avg: 94.10ns, executions: [131.00ns, 54.00ns, 26.00ns, 83.00ns, 16.00ns, 16.00ns, 16.00ns, 76.00ns, 478.00ns, 45.00ns]
+file_size -> 256.71Kb
diff --git a/internal/providers/gofpdf/builder.go b/internal/providers/gofpdf/builder.go
index 400df19d..6aa59f2e 100644
--- a/internal/providers/gofpdf/builder.go
+++ b/internal/providers/gofpdf/builder.go
@@ -55,6 +55,8 @@ func (b *builder) Build(cfg *entity.Config, cache cache.Cache) *Dependencies {
if cfg.DisableAutoPageBreak {
fpdf.SetAutoPageBreak(false, 0)
+ } else {
+ fpdf.SetAutoPageBreak(true, cfg.Margins.Bottom)
}
fpdf.SetMargins(cfg.Margins.Left, cfg.Margins.Top, cfg.Margins.Right)
diff --git a/internal/providers/gofpdf/builder_test.go b/internal/providers/gofpdf/builder_test.go
index ca5e6581..1308eaab 100644
--- a/internal/providers/gofpdf/builder_test.go
+++ b/internal/providers/gofpdf/builder_test.go
@@ -5,9 +5,10 @@ import (
"testing"
"github.com/johnfercher/maroto/v2/internal/fixture"
- "github.com/johnfercher/maroto/v2/internal/providers/gofpdf"
"github.com/johnfercher/maroto/v2/pkg/consts/fontfamily"
"github.com/johnfercher/maroto/v2/pkg/core/entity"
+
+ "github.com/johnfercher/maroto/v2/internal/providers/gofpdf"
"github.com/stretchr/testify/assert"
)
@@ -21,32 +22,64 @@ func TestNewBuilder(t *testing.T) {
}
func TestBuilder_Build(t *testing.T) {
- // Arrange
- sut := gofpdf.NewBuilder()
- font := fixture.FontProp()
- cfg := &entity.Config{
- Dimensions: &entity.Dimensions{
- Width: 100,
- Height: 200,
- },
- Margins: &entity.Margins{
- Left: 10,
- Top: 10,
- Right: 10,
- Bottom: 10,
- },
- DefaultFont: &font,
- CustomFonts: []*entity.CustomFont{
- {
- Family: fontfamily.Arial,
+ t.Run("when DisableAutoPageBreak true, should build correctly", func(t *testing.T) {
+ // Arrange
+ sut := gofpdf.NewBuilder()
+ font := fixture.FontProp()
+ cfg := &entity.Config{
+ Dimensions: &entity.Dimensions{
+ Width: 100,
+ Height: 200,
},
- },
- DisableAutoPageBreak: true,
- }
+ Margins: &entity.Margins{
+ Left: 10,
+ Top: 10,
+ Right: 10,
+ Bottom: 10,
+ },
+ DefaultFont: &font,
+ CustomFonts: []*entity.CustomFont{
+ {
+ Family: fontfamily.Arial,
+ },
+ },
+ DisableAutoPageBreak: true,
+ }
- // Act
- dep := sut.Build(cfg, nil)
+ // Act
+ dep := sut.Build(cfg, nil)
- // Assert
- assert.NotNil(t, dep)
+ // Assert
+ assert.NotNil(t, dep)
+ })
+ t.Run("when DisableAutoPageBreak false, should build correctly", func(t *testing.T) {
+ // Arrange
+ sut := gofpdf.NewBuilder()
+ font := fixture.FontProp()
+ cfg := &entity.Config{
+ Dimensions: &entity.Dimensions{
+ Width: 100,
+ Height: 200,
+ },
+ Margins: &entity.Margins{
+ Left: 10,
+ Top: 10,
+ Right: 10,
+ Bottom: 10,
+ },
+ DefaultFont: &font,
+ CustomFonts: []*entity.CustomFont{
+ {
+ Family: fontfamily.Arial,
+ },
+ },
+ DisableAutoPageBreak: false,
+ }
+
+ // Act
+ dep := sut.Build(cfg, nil)
+
+ // Assert
+ assert.NotNil(t, dep)
+ })
}
diff --git a/pkg/config/builder.go b/pkg/config/builder.go
index 884678e0..536c1714 100644
--- a/pkg/config/builder.go
+++ b/pkg/config/builder.go
@@ -25,7 +25,10 @@ import (
type Builder interface {
WithPageSize(size pagesize.Type) Builder
WithDimensions(width float64, height float64) Builder
- WithMargins(left float64, top float64, right float64) Builder
+ WithLeftMargin(left float64) Builder
+ WithTopMargin(top float64) Builder
+ WithRightMargin(right float64) Builder
+ WithBottomMargin(bottom float64) Builder
WithConcurrentMode(chunkWorkers int) Builder
WithSequentialMode() Builder
WithSequentialLowMemoryMode(chunkWorkers int) Builder
@@ -114,24 +117,43 @@ func (b *CfgBuilder) WithDimensions(width float64, height float64) Builder {
return b
}
-// WithMargins defines custom margins, bottom margin is not customizable due to gofpdf limitations.
-func (b *CfgBuilder) WithMargins(left float64, top float64, right float64) Builder {
+// WithLeftMargin customize margin.
+func (b *CfgBuilder) WithLeftMargin(left float64) Builder {
if left < pagesize.MinLeftMargin {
return b
}
- if top < pagesize.MinRightMargin {
+ b.margins.Left = left
+ return b
+}
+
+// WithTopMargin customize margin.
+func (b *CfgBuilder) WithTopMargin(top float64) Builder {
+ if top < pagesize.MinTopMargin {
return b
}
- if right < pagesize.MinTopMargin {
+ b.margins.Top = top
+ return b
+}
+
+// WithRightMargin customize margin.
+func (b *CfgBuilder) WithRightMargin(right float64) Builder {
+ if right < pagesize.MinRightMargin {
return b
}
- b.margins.Left = left
- b.margins.Top = top
b.margins.Right = right
+ return b
+}
+
+// WithBottomMargin customize margin.
+func (b *CfgBuilder) WithBottomMargin(bottom float64) Builder {
+ if bottom < pagesize.MinBottomMargin {
+ return b
+ }
+ b.margins.Bottom = bottom
return b
}
diff --git a/pkg/config/builder_test.go b/pkg/config/builder_test.go
index 88e0560f..b9b9cae4 100644
--- a/pkg/config/builder_test.go
+++ b/pkg/config/builder_test.go
@@ -135,54 +135,95 @@ func TestBuilder_WithDimensions(t *testing.T) {
})
}
-func TestBuilder_WithMargins(t *testing.T) {
- t.Run("when margins has invalid left, should not change the default value", func(t *testing.T) {
+func TestCfgBuilder_WithTopMargin(t *testing.T) {
+ t.Run("when top is invalid, should not change the default value", func(t *testing.T) {
// Arrange
sut := config.NewBuilder()
// Act
- cfg := sut.WithMargins(-1, 20, 20).Build()
+ cfg := sut.WithTopMargin(-1).Build()
// Assert
- assert.Equal(t, 10.0, cfg.Margins.Left)
assert.Equal(t, 10.0, cfg.Margins.Top)
- assert.Equal(t, 10.0, cfg.Margins.Right)
})
- t.Run("when margins has invalid right, should not change the default value", func(t *testing.T) {
+ t.Run("when top is valid, should change the default value", func(t *testing.T) {
// Arrange
sut := config.NewBuilder()
// Act
- cfg := sut.WithMargins(20, 20, -1).Build()
+ cfg := sut.WithTopMargin(5).Build()
// Assert
- assert.Equal(t, 10.0, cfg.Margins.Left)
- assert.Equal(t, 10.0, cfg.Margins.Top)
- assert.Equal(t, 10.0, cfg.Margins.Right)
+ assert.Equal(t, 5.0, cfg.Margins.Top)
})
- t.Run("when margins has invalid top, should not change the default value", func(t *testing.T) {
+}
+
+func TestCfgBuilder_WithLeftMargin(t *testing.T) {
+ t.Run("when left is invalid, should not change the default value", func(t *testing.T) {
// Arrange
sut := config.NewBuilder()
// Act
- cfg := sut.WithMargins(20, -1, 20).Build()
+ cfg := sut.WithLeftMargin(-1).Build()
// Assert
assert.Equal(t, 10.0, cfg.Margins.Left)
- assert.Equal(t, 10.0, cfg.Margins.Top)
+ })
+ t.Run("when left is valid, should change the default value", func(t *testing.T) {
+ // Arrange
+ sut := config.NewBuilder()
+
+ // Act
+ cfg := sut.WithLeftMargin(5).Build()
+
+ // Assert
+ assert.Equal(t, 5.0, cfg.Margins.Left)
+ })
+}
+
+func TestCfgBuilder_WithRightMargin(t *testing.T) {
+ t.Run("when right is invalid, should not change the default value", func(t *testing.T) {
+ // Arrange
+ sut := config.NewBuilder()
+
+ // Act
+ cfg := sut.WithRightMargin(-1).Build()
+
+ // Assert
assert.Equal(t, 10.0, cfg.Margins.Right)
})
- t.Run("when dimensions has valid values, should change the default value", func(t *testing.T) {
+ t.Run("when right is valid, should change the default value", func(t *testing.T) {
+ // Arrange
+ sut := config.NewBuilder()
+
+ // Act
+ cfg := sut.WithRightMargin(5).Build()
+
+ // Assert
+ assert.Equal(t, 5.0, cfg.Margins.Right)
+ })
+}
+
+func TestCfgBuilder_WithBottomMargin(t *testing.T) {
+ t.Run("when bottom is invalid, should not change the default value", func(t *testing.T) {
+ // Arrange
+ sut := config.NewBuilder()
+
+ // Act
+ cfg := sut.WithBottomMargin(-1).Build()
+
+ // Assert
+ assert.Equal(t, 20.0025, cfg.Margins.Bottom)
+ })
+ t.Run("when bottom is valid, should change the default value", func(t *testing.T) {
// Arrange
sut := config.NewBuilder()
// Act
- cfg := sut.WithMargins(20, 20, 20).Build()
+ cfg := sut.WithBottomMargin(5).Build()
// Assert
- assert.Equal(t, 20.0, cfg.Margins.Left)
- assert.Equal(t, 20.0, cfg.Margins.Top)
- assert.Equal(t, 20.0, cfg.Margins.Right)
+ assert.Equal(t, 5.0, cfg.Margins.Bottom)
})
}
diff --git a/pkg/config/example_test.go b/pkg/config/example_test.go
index 8be56694..4e83dbee 100644
--- a/pkg/config/example_test.go
+++ b/pkg/config/example_test.go
@@ -36,11 +36,47 @@ func ExampleCfgBuilder_WithPageSize() {
// generate document
}
-// ExampleCfgBuilder_WithMargins demonstrates how to customize margins
-func ExampleCfgBuilder_WithMargins() {
- // If any margins is smaller than zero, then ignore customization.
+// ExampleCfgBuilder_WithTopMargin demonstrates how to customize margin.
+func ExampleCfgBuilder_WithTopMargin() {
+ // If top less than minimum, ignore customization.
cfg := config.NewBuilder().
- WithMargins(5, 5, 5).
+ WithTopMargin(15).
+ Build()
+
+ _ = maroto.New(cfg)
+
+ // generate document
+}
+
+// ExampleCfgBuilder_WithRightMargin demonstrates how to customize margin.
+func ExampleCfgBuilder_WithRightMargin() {
+ // If top less than minimum, ignore customization.
+ cfg := config.NewBuilder().
+ WithRightMargin(15).
+ Build()
+
+ _ = maroto.New(cfg)
+
+ // generate document
+}
+
+// ExampleCfgBuilder_WithLeftMargin demonstrates how to customize margin.
+func ExampleCfgBuilder_WithLeftMargin() {
+ // If top less than minimum, ignore customization.
+ cfg := config.NewBuilder().
+ WithLeftMargin(15).
+ Build()
+
+ _ = maroto.New(cfg)
+
+ // generate document
+}
+
+// ExampleCfgBuilder_WithBottomMargin demonstrates how to customize margin.
+func ExampleCfgBuilder_WithBottomMargin() {
+ // If top less than minimum, ignore customization.
+ cfg := config.NewBuilder().
+ WithBottomMargin(15).
Build()
_ = maroto.New(cfg)
diff --git a/pkg/consts/pagesize/size.go b/pkg/consts/pagesize/size.go
index 87cabedb..5351512d 100644
--- a/pkg/consts/pagesize/size.go
+++ b/pkg/consts/pagesize/size.go
@@ -30,7 +30,7 @@ const (
// DefaultRightMargin represents the default right margin in page size.
DefaultRightMargin = 10.0
// DefaultBottomMargin represents the default bottom margin in page size.
- DefaultBottomMargin = MinBottomMargin
+ DefaultBottomMargin = 20.0025
// MinTopMargin represents the minimum top margin in page size.
MinTopMargin = 0.0
// MinLeftMargin represents the minimum left margin in page size.
@@ -38,7 +38,7 @@ const (
// MinRightMargin represents the minimum right margin in page size.
MinRightMargin = 0.0
// MinBottomMargin represents the minimum bottom margin in page size.
- MinBottomMargin = 20.0025
+ MinBottomMargin = 0.0
// DefaultFontSize represents the default font size in page size.
DefaultFontSize = 10.0
// DefaultMaxGridSum represents the default max grid sum in page size.
diff --git a/test/maroto/examples/margins.json b/test/maroto/examples/margins.json
index bd53a232..309a8368 100755
--- a/test/maroto/examples/margins.json
+++ b/test/maroto/examples/margins.json
@@ -59,7 +59,250 @@
]
},
{
- "value": 216.9975,
+ "value": 30,
+ "type": "row",
+ "nodes": [
+ {
+ "value": 0,
+ "type": "col",
+ "details": {
+ "is_max": true
+ },
+ "nodes": [
+ {
+ "value": "any text",
+ "type": "text"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "value": 30,
+ "type": "row",
+ "nodes": [
+ {
+ "value": 0,
+ "type": "col",
+ "details": {
+ "is_max": true
+ },
+ "nodes": [
+ {
+ "value": "any text",
+ "type": "text"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "value": 30,
+ "type": "row",
+ "nodes": [
+ {
+ "value": 0,
+ "type": "col",
+ "details": {
+ "is_max": true
+ },
+ "nodes": [
+ {
+ "value": "any text",
+ "type": "text"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "value": 30,
+ "type": "row",
+ "nodes": [
+ {
+ "value": 0,
+ "type": "col",
+ "details": {
+ "is_max": true
+ },
+ "nodes": [
+ {
+ "value": "any text",
+ "type": "text"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "value": 30,
+ "type": "row",
+ "nodes": [
+ {
+ "value": 0,
+ "type": "col",
+ "details": {
+ "is_max": true
+ },
+ "nodes": [
+ {
+ "value": "any text",
+ "type": "text"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "value": 30,
+ "type": "row",
+ "nodes": [
+ {
+ "value": 0,
+ "type": "col",
+ "details": {
+ "is_max": true
+ },
+ "nodes": [
+ {
+ "value": "any text",
+ "type": "text"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "value": 30,
+ "type": "row",
+ "nodes": [
+ {
+ "value": 0,
+ "type": "col",
+ "details": {
+ "is_max": true
+ },
+ "nodes": [
+ {
+ "value": "any text",
+ "type": "text"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "value": 6.997500000000002,
+ "type": "row",
+ "nodes": [
+ {
+ "value": 12,
+ "type": "col"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "page",
+ "nodes": [
+ {
+ "value": 40,
+ "type": "row",
+ "nodes": [
+ {
+ "value": 4,
+ "type": "col",
+ "nodes": [
+ {
+ "value": "docs/assets/images/gopherbw.png",
+ "type": "fileImage",
+ "details": {
+ "prop_center": true,
+ "prop_percent": 50
+ }
+ }
+ ]
+ },
+ {
+ "value": 4,
+ "type": "col",
+ "nodes": [
+ {
+ "value": "Margins Test",
+ "type": "text",
+ "details": {
+ "prop_font_size": 12,
+ "prop_top": 12
+ }
+ }
+ ]
+ },
+ {
+ "value": 4,
+ "type": "col"
+ }
+ ]
+ },
+ {
+ "value": 30,
+ "type": "row",
+ "nodes": [
+ {
+ "value": 0,
+ "type": "col",
+ "details": {
+ "is_max": true
+ },
+ "nodes": [
+ {
+ "value": "any text",
+ "type": "text"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "value": 30,
+ "type": "row",
+ "nodes": [
+ {
+ "value": 0,
+ "type": "col",
+ "details": {
+ "is_max": true
+ },
+ "nodes": [
+ {
+ "value": "any text",
+ "type": "text"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "value": 30,
+ "type": "row",
+ "nodes": [
+ {
+ "value": 0,
+ "type": "col",
+ "details": {
+ "is_max": true
+ },
+ "nodes": [
+ {
+ "value": "any text",
+ "type": "text"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "value": 126.9975,
"type": "row",
"nodes": [
{