diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md
index f3c0d2a77..17c909a71 100644
--- a/ci/release/changelogs/next.md
+++ b/ci/release/changelogs/next.md
@@ -1,5 +1,7 @@
#### Features ๐
+- `paper` is available as a `fill-pattern` option [#1070](https://github.com/terrastruct/d2/pull/1070)
+
#### Improvements ๐งน
#### Bugfixes โ๏ธ
diff --git a/d2compiler/compile_test.go b/d2compiler/compile_test.go
index 8e284f6b6..584dd5115 100644
--- a/d2compiler/compile_test.go
+++ b/d2compiler/compile_test.go
@@ -259,7 +259,7 @@ containers: {
}
}
`,
- expErr: `d2/testdata/d2compiler/TestCompile/invalid-fill-pattern.d2:3:19: expected "fill-pattern" to be one of: dots, lines, grain`,
+ expErr: `d2/testdata/d2compiler/TestCompile/invalid-fill-pattern.d2:3:19: expected "fill-pattern" to be one of: dots, lines, grain, paper`,
},
{
name: "shape_unquoted_hex",
diff --git a/d2graph/d2graph.go b/d2graph/d2graph.go
index 3b921e5d0..54c658a21 100644
--- a/d2graph/d2graph.go
+++ b/d2graph/d2graph.go
@@ -1586,6 +1586,7 @@ var FillPatterns = []string{
"dots",
"lines",
"grain",
+ "paper",
}
// BoardKeywords contains the keywords that create new boards.
diff --git a/d2renderers/d2sketch/sketch_test.go b/d2renderers/d2sketch/sketch_test.go
index c95746624..5ed75626e 100644
--- a/d2renderers/d2sketch/sketch_test.go
+++ b/d2renderers/d2sketch/sketch_test.go
@@ -1107,6 +1107,58 @@ something
}
`,
},
+ {
+ name: "paper-real",
+ script: `style.fill-pattern: paper
+style.fill: "#947A6D"
+NETWORK: {
+ style: {
+ stroke: black
+ fill-pattern: dots
+ double-border: true
+ fill: "#E7E9EE"
+ font: mono
+ }
+ CELL TOWER: {
+ style: {
+ stroke: black
+ fill-pattern: dots
+ fill: "#F5F6F9"
+ font: mono
+ }
+ satellites: SATELLITES {
+ shape: stored_data
+ style: {
+ font: mono
+ fill: white
+ stroke: black
+ multiple: true
+ }
+ }
+
+ transmitter: TRANSMITTER {
+ style: {
+ font: mono
+ fill: white
+ stroke: black
+ }
+ }
+
+ satellites -> transmitter: SEND {
+ style.stroke: black
+ style.font: mono
+ }
+ satellites -> transmitter: SEND {
+ style.stroke: black
+ style.font: mono
+ }
+ satellites -> transmitter: SEND {
+ style.stroke: black
+ style.font: mono
+ }
+ }
+}
+`},
{
name: "dots-real",
script: `
diff --git a/d2renderers/d2sketch/testdata/paper-real/sketch.exp.svg b/d2renderers/d2sketch/testdata/paper-real/sketch.exp.svg
new file mode 100644
index 000000000..447aab916
--- /dev/null
+++ b/d2renderers/d2sketch/testdata/paper-real/sketch.exp.svg
@@ -0,0 +1,1213 @@
+
\ No newline at end of file
diff --git a/d2renderers/d2svg/d2svg.go b/d2renderers/d2svg/d2svg.go
index 36292decc..f7b9b915f 100644
--- a/d2renderers/d2svg/d2svg.go
+++ b/d2renderers/d2svg/d2svg.go
@@ -68,6 +68,9 @@ var lines string
//go:embed grain.txt
var grain string
+//go:embed paper.txt
+var paper string
+
type RenderOpts struct {
Pad int
Sketch bool
@@ -1798,7 +1801,7 @@ func Render(diagram *d2target.Diagram, opts *RenderOpts) ([]byte, error) {
bufStr := buf.String()
patternDefs := ""
for _, pattern := range d2graph.FillPatterns {
- if strings.Contains(bufStr, fmt.Sprintf("%s-overlay", pattern)) || diagram.Root.FillPattern != "" {
+ if strings.Contains(bufStr, fmt.Sprintf("%s-overlay", pattern)) || diagram.Root.FillPattern == pattern {
if patternDefs == "" {
fmt.Fprint(upperBuf, `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+rectanglesquarepageparallelogramdocumentcylinderqueuepackagestepcalloutstored_datapersondiamondovalcirclehexagoncloud
+
+
+
\ No newline at end of file
diff --git a/e2etests/testdata/patterns/root-dots-with-fill/dagre/sketch.exp.svg b/e2etests/testdata/patterns/root-dots-with-fill/dagre/sketch.exp.svg
index 91b2c9836..3f6b2a89d 100644
--- a/e2etests/testdata/patterns/root-dots-with-fill/dagre/sketch.exp.svg
+++ b/e2etests/testdata/patterns/root-dots-with-fill/dagre/sketch.exp.svg
@@ -93,14 +93,6 @@
.dots-overlay {
fill: url(#dots);
mix-blend-mode: multiply;
-}
-.lines-overlay {
- fill: url(#lines);
- mix-blend-mode: multiply;
-}
-.grain-overlay {
- fill: url(#grain);
- mix-blend-mode: multiply;
}]]>
@@ -130,33 +122,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
xyzabcdg
diff --git a/e2etests/testdata/patterns/root-dots/dagre/sketch.exp.svg b/e2etests/testdata/patterns/root-dots/dagre/sketch.exp.svg
index e59f2a086..d69bbd129 100644
--- a/e2etests/testdata/patterns/root-dots/dagre/sketch.exp.svg
+++ b/e2etests/testdata/patterns/root-dots/dagre/sketch.exp.svg
@@ -93,14 +93,6 @@
.dots-overlay {
fill: url(#dots);
mix-blend-mode: multiply;
-}
-.lines-overlay {
- fill: url(#lines);
- mix-blend-mode: multiply;
-}
-.grain-overlay {
- fill: url(#grain);
- mix-blend-mode: multiply;
}]]>
@@ -130,33 +122,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
xyzabcdg
diff --git a/testdata/d2compiler/TestCompile/invalid-fill-pattern.exp.json b/testdata/d2compiler/TestCompile/invalid-fill-pattern.exp.json
index 7d8ea713e..8c1a03f92 100644
--- a/testdata/d2compiler/TestCompile/invalid-fill-pattern.exp.json
+++ b/testdata/d2compiler/TestCompile/invalid-fill-pattern.exp.json
@@ -5,7 +5,7 @@
"errs": [
{
"range": "d2/testdata/d2compiler/TestCompile/invalid-fill-pattern.d2,2:18:33-2:23:38",
- "errmsg": "d2/testdata/d2compiler/TestCompile/invalid-fill-pattern.d2:3:19: expected \"fill-pattern\" to be one of: dots, lines, grain"
+ "errmsg": "d2/testdata/d2compiler/TestCompile/invalid-fill-pattern.d2:3:19: expected \"fill-pattern\" to be one of: dots, lines, grain, paper"
}
]
}