Created
March 8, 2026 17:37
-
-
Save NotBad4U/6dd2e53c3d1722fdeea880d4f31a43b2 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #set page( | |
| background: image("img/parchment.jpg", width: 100%, height: 100%), | |
| ) | |
| #let DemonGreen = rgb(34, 114, 98) | |
| #let DragonRed = rgb(231, 47, 50) | |
| #let InkBlack = rgb(35, 31, 32) | |
| #let LightGreen = rgb(223, 235, 227) | |
| #let BoneWhite = rgb(245, 242, 235) | |
| #let ScrollBrown = rgb(85, 34, 0) | |
| #let StainBrown = rgb(102, 85, 45) | |
| #let Sand = rgb(232, 219, 191) | |
| #show heading.where(level: 1): set text(DemonGreen, weight: "bold") | |
| #show heading.where(level: 2): set text(black, weight: "bold") | |
| #show heading.where(level: 3): set text(BoneWhite, weight: "bold") | |
| #set par(justify: true) | |
| #set text(font: "Libertinus Serif", size: 10pt) | |
| #set page(footer: context { | |
| let current = counter(page).display() | |
| [ | |
| #let pagenb-corner-mark( | |
| w: 18pt, | |
| peak: 20pt, | |
| side: 10pt, | |
| fill: DemonGreen, | |
| ) = curve( | |
| fill: fill, | |
| stroke: 1pt, | |
| curve.move((0pt, 0pt)), | |
| curve.quad((0.10 * w, 0pt), (w / 8, -side)), | |
| curve.quad((0.30 * w, -5pt), (w / 2, -peak)), | |
| curve.quad((0.70 * w, -5pt), (7 * w / 8, -side)), | |
| curve.quad((0.90 * w, 0pt), (w, 0pt)), | |
| curve.close(mode: "straight"), | |
| ) | |
| #align(center)[ | |
| #layout(size => { | |
| let b = block(inset: 5pt)[ | |
| #text(font: "Colus", fill: DemonGreen)[DRAGONBANE SUPPLEMENT] | |
| ] | |
| let h = measure(b).height | |
| let w = measure(b).width | |
| b | |
| place(top, dx: size.width / 2 - w / 2, dy: -0pt, pagenb-corner-mark(w: w)) | |
| place(bottom, dx: size.width / 2 - w / 2, dy: 0pt, std.rotate(180deg)[#pagenb-corner-mark(w: w)]) | |
| place(bottom, dx: size.width / 2 - 3pt, dy: 10pt, text(font: "Colus", fill: white)[#current]) | |
| }) | |
| ] | |
| ] | |
| }) | |
| #let corner-mark(w: 18pt, h: 10pt, fill: black) = curve( | |
| fill: fill, | |
| stroke: 1pt, | |
| curve.move((0pt, h / 2)), | |
| // left -> top | |
| curve.quad((0.28 * w, 0.34 * h), (w / 2, 0pt)), | |
| // top -> right | |
| curve.quad((0.72 * w, 0.34 * h), (w, h / 2)), | |
| // right -> bottom | |
| curve.quad((0.72 * w, 0.66 * h), (w / 2, h)), | |
| // bottom -> left | |
| curve.quad((0.28 * w, 0.66 * h), (0pt, h / 2)), | |
| curve.close(mode: "straight"), | |
| ) | |
| #let corner-mark2(w: 18pt, hp: 40pt, fill: rgb("#2b7f73")) = curve( | |
| fill: fill, | |
| // stroke: 1pt, | |
| curve.move((0pt, 0pt)), | |
| // small top shoulder | |
| curve.quad((-0.18 * w, 0.00 * hp), (-w / 4, -5pt)), | |
| // long upper arc to the left tip | |
| curve.quad((-0.20 * w, 0.10 * hp), (-w / 2, 0.50 * hp)), | |
| // long lower arc from the tip | |
| curve.quad((-0.20 * w, 0.90 * hp), (-w / 4, 5pt + hp)), | |
| // small bottom shoulder back to the side | |
| curve.quad((-0.18 * w, 1.00 * hp), (0pt, hp)), | |
| // curve.close(mode: "straight"), | |
| ) | |
| #let corner-mark-up(w: 20pt, h: 5pt, fill: red) = curve( | |
| fill: fill, | |
| curve.move((-w, 0pt)), | |
| curve.quad((-0.18 * w, 0.00 * h), (0pt, -h)), | |
| curve.quad((0.18 * w, 0.00 * h), (w, 0pt)), | |
| ) | |
| #set page(header: context { | |
| let current = counter(page).display() | |
| { | |
| align(center)[ | |
| #layout(size => { | |
| let b = block( | |
| width: size.width, | |
| height: 2pt, | |
| stroke: 1pt + DemonGreen, | |
| fill: DemonGreen, | |
| inset: 10pt, | |
| [], | |
| ) | |
| let s = measure(b) | |
| let topw = 20pt | |
| let toph = 3pt | |
| block( | |
| width: s.width, | |
| height: s.height, | |
| { | |
| place(top + left, corner-mark2(w: 30pt, hp: s.height, fill: DemonGreen)) | |
| place(top + right, std.rotate(180deg)[ | |
| #corner-mark2(w: 30pt, hp: s.height, fill: DemonGreen) | |
| ]) | |
| b | |
| place( | |
| top + center, | |
| dx: topw / 2, | |
| box( | |
| width: 2 * topw, | |
| height: toph, | |
| corner-mark-up(w: topw, h: toph, fill: DemonGreen), | |
| ), | |
| ) | |
| place( | |
| bottom + center, | |
| dx: -topw / 2, | |
| box( | |
| width: 2 * topw, | |
| height: toph, | |
| std.rotate(180deg)[#corner-mark-up(w: topw, h: toph, fill: DemonGreen)], | |
| ), | |
| ) | |
| }, | |
| ) | |
| }) | |
| ] | |
| } | |
| }) | |
| #show heading: set text(font: ("Colus", "Libertinus Serif")) | |
| #show heading.where(level: 2): set text(font: ("Colus", "Libertinus Serif"), size: 18pt) | |
| #show heading.where(level: 1): it => block( | |
| width: 100%, | |
| below: 1em, | |
| align(center, text(size: 32pt, weight: "bold")[#it.body]), | |
| ) | |
| #let resolve-length(len, max: 0pt) = { | |
| if type(len) == length { return len } | |
| if type(len) == ratio { return max * len } | |
| if type(len) == fraction { return len / 1fr * max } | |
| panic("Cannot resolve the length " + repr(len)) | |
| } | |
| #let eqcols(n, body, shift: 0cm, gutter: 4%, debug: false) = { | |
| let eqcols-id = counter("__eqcols") | |
| eqcols-id.step() | |
| context { | |
| let floating-figures = state("__figs_" + str(eqcols-id.get().at(0)), ()) | |
| show figure.where(scope: "parent"): it => { floating-figures.update(x => x + (it,)) + it } | |
| layout(size => { | |
| let container-width = size.width | |
| let container-height = size.height | |
| let line-height = 1em.to-absolute() | |
| let gutter = resolve-length(gutter, max: container-width) | |
| let shift = resolve-length(shift, max: container-height) | |
| let column-width = { (container-width - (n - 1) * gutter) / n } | |
| let float-free-body = { | |
| show figure.where(scope: "parent"): none | |
| body | |
| } | |
| let float-free-one-column-size = measure(width: column-width, float-free-body) | |
| context { | |
| let all-floating-figures = floating-figures.final() | |
| let float-height = all-floating-figures | |
| .map(fig => measure(width: container-width, fig).height) | |
| .sum(default: 0pt) | |
| let raw-new-height = float-free-one-column-size.height / n + float-height + shift | |
| let new-height = calc.ceil((raw-new-height / 1pt) / (line-height / 1pt)) * line-height | |
| block(columns(n, body, gutter: gutter), height: new-height, stroke: if debug { red }) | |
| } | |
| }) | |
| } | |
| } | |
| = BOXES | |
| #eqcols(2)[ | |
| #lorem(50) | |
| #v(1em) | |
| #layout(size => context { | |
| let content = block( | |
| width: size.width, | |
| inset: 5pt, | |
| )[ | |
| #lorem(30) | |
| ] | |
| let s = measure(content) | |
| block( | |
| stroke: (top: 2pt, bottom: 2pt), | |
| width: size.width, | |
| height: s.height, | |
| clip: false, | |
| )[ | |
| // Background first | |
| #place( | |
| top + left, | |
| image( | |
| "img/scroll.png", | |
| fit: "cover", | |
| width: size.width, | |
| height: s.height, | |
| ), | |
| ) | |
| // Then the text/content | |
| #content | |
| // Then decorations on top | |
| #place(top + right, dx: 14pt, dy: -5pt, corner-mark()) | |
| #place(top + left, dx: -14pt, dy: -5pt, corner-mark()) | |
| #place(bottom + right, dx: 14pt, dy: 5pt, corner-mark()) | |
| #place(bottom + left, dx: -14pt, dy: 5pt, corner-mark()) | |
| #place(top + center, dy: -15pt, { | |
| let b = block( | |
| stroke: 1pt + DemonGreen, | |
| fill: DemonGreen, | |
| inset: 10pt, | |
| [=== dragon box], | |
| ) | |
| let s = measure(b) | |
| let topw = 20pt | |
| let toph = 3pt | |
| block( | |
| width: s.width, | |
| height: s.height, | |
| { | |
| place(top + left, corner-mark2(w: 30pt, hp: s.height, fill: DemonGreen)) | |
| place(top + right, std.rotate(180deg)[ | |
| #corner-mark2(w: 30pt, hp: s.height, fill: DemonGreen) | |
| ]) | |
| b | |
| place( | |
| top + center, | |
| dx: topw / 2, | |
| box( | |
| width: 2 * topw, | |
| height: toph, | |
| corner-mark-up(w: topw, h: toph, fill: DemonGreen), | |
| ), | |
| ) | |
| place( | |
| bottom + center, | |
| dx: -topw / 2, | |
| box( | |
| width: 2 * topw, | |
| height: toph, | |
| std.rotate(180deg)[#corner-mark-up(w: topw, h: toph, fill: DemonGreen)], | |
| ), | |
| ) | |
| }, | |
| ) | |
| }) | |
| #place(bottom + center, dy: 5pt, corner-mark()) | |
| ] | |
| }) | |
| #lorem(25) | |
| #align(center)[ | |
| #layout(size => { | |
| let b = block( | |
| width: 180pt, | |
| stroke: 1pt + DemonGreen, | |
| fill: DemonGreen, | |
| inset: 10pt, | |
| [=== empty box], | |
| ) | |
| let s = measure(b) | |
| let topw = 20pt | |
| let toph = 3pt | |
| block( | |
| width: s.width, | |
| height: s.height, | |
| { | |
| place(top + left, corner-mark2(w: 30pt, hp: s.height, fill: DemonGreen)) | |
| place(top + right, std.rotate(180deg)[ | |
| #corner-mark2(w: 30pt, hp: s.height, fill: DemonGreen) | |
| ]) | |
| b | |
| place( | |
| top + center, | |
| dx: topw / 2, | |
| box( | |
| width: 2 * topw, | |
| height: toph, | |
| corner-mark-up(w: topw, h: toph, fill: DemonGreen), | |
| ), | |
| ) | |
| place( | |
| bottom + center, | |
| dx: -topw / 2, | |
| box( | |
| width: 2 * topw, | |
| height: toph, | |
| std.rotate(180deg)[#corner-mark-up(w: topw, h: toph, fill: DemonGreen)], | |
| ), | |
| ) | |
| }, | |
| ) | |
| }) | |
| ] | |
| #lorem(50) | |
| ] | |
| = Segment | |
| #eqcols(2)[ | |
| #lorem(50) | |
| == Hello world | |
| #lorem(30) | |
| #let corner-mark3(w: 18pt, hp: 40pt, fill: rgb("#2b7f73")) = curve( | |
| fill: fill, | |
| curve.move((0pt, 0pt)), | |
| // small top shoulder | |
| curve.quad((-0.03 * w, 0.00 * hp), (-w / 8, -3pt)), | |
| // long upper arc to the left tip | |
| curve.quad((-3pt, 3pt), (-w / 2 + 3pt, 10pt)), | |
| curve.quad((-0.10 * w, 0.10 * hp), (0pt, 0.20 * hp)), | |
| ) | |
| #align(center)[ | |
| #layout(size => { | |
| let b = block( | |
| width: 180pt, | |
| stroke: 1pt + DemonGreen, | |
| fill: DemonGreen, | |
| inset: 10pt, | |
| [ | |
| #set text(fill: BoneWhite) | |
| #table( | |
| columns: 1, | |
| stroke: none, | |
| table.header([=== Substance]), | |
| table.hline(stroke: 1pt), | |
| [ #lorem(30) ], | |
| ) | |
| ], | |
| ) | |
| let s = measure(b) | |
| let topw = 20pt | |
| let toph = 3pt | |
| block( | |
| width: s.width, | |
| height: s.height, | |
| { | |
| b | |
| place(top + left, corner-mark3(w: 30pt, hp: s.height, fill: DemonGreen)) | |
| place(top + right, std.scale(x: -100%)[#corner-mark3(w: 30pt, hp: s.height, fill: DemonGreen)]) | |
| place(bottom + right, std.rotate(180deg)[#corner-mark3(w: 30pt, hp: s.height, fill: DemonGreen)]) | |
| place(bottom + left, std.rotate(180deg)[#std.scale(x: -100%)[#corner-mark3( | |
| w: 30pt, | |
| hp: s.height, | |
| fill: DemonGreen, | |
| )]]) | |
| place( | |
| top + center, | |
| dx: topw / 2, | |
| box( | |
| width: 2 * topw, | |
| height: toph, | |
| corner-mark-up(w: topw, h: toph, fill: DemonGreen), | |
| ), | |
| ) | |
| place( | |
| bottom + center, | |
| dx: -topw / 2, | |
| box( | |
| width: 2 * topw, | |
| height: toph, | |
| std.rotate(180deg)[#corner-mark-up(w: topw, h: toph, fill: DemonGreen)], | |
| ), | |
| ) | |
| }, | |
| ) | |
| }) | |
| ] | |
| ] | |
| #show table.cell.where(y: 0): set text(weight: "bold", font: "Colus") | |
| #place( | |
| bottom + center, | |
| scope: "parent", | |
| float: true, | |
| { | |
| align(center)[ | |
| #layout(size => { | |
| let b = block( | |
| width: size.width, | |
| stroke: 1pt + DemonGreen, | |
| fill: DemonGreen, | |
| inset: 10pt, | |
| [=== empty box], | |
| ) | |
| let s = measure(b) | |
| let topw = 20pt | |
| let toph = 3pt | |
| block( | |
| width: s.width, | |
| height: s.height, | |
| { | |
| place(top + left, corner-mark2(w: 30pt, hp: s.height, fill: DemonGreen)) | |
| place(top + right, std.rotate(180deg)[ | |
| #corner-mark2(w: 30pt, hp: s.height, fill: DemonGreen) | |
| ]) | |
| b | |
| place( | |
| top + center, | |
| dx: topw / 2, | |
| box( | |
| width: 2 * topw, | |
| height: toph, | |
| corner-mark-up(w: topw, h: toph, fill: DemonGreen), | |
| ), | |
| ) | |
| place( | |
| bottom + center, | |
| dx: -topw / 2, | |
| box( | |
| width: 2 * topw, | |
| height: toph, | |
| std.rotate(180deg)[#corner-mark-up(w: topw, h: toph, fill: DemonGreen)], | |
| ), | |
| ) | |
| }, | |
| ) | |
| }) | |
| ] | |
| table( | |
| columns: 4, | |
| align: (left, left, left, left), | |
| fill: (_, y) => if calc.odd(y) { color.mix((ScrollBrown, 10%), (Sand, 90%)) }, | |
| stroke: none, | |
| table.header[Day][Location][Hotel ][Activities], | |
| table.hline(stroke: 1pt), | |
| [1], [Paris, France], [Hôtel de l'Europe], [Arrival, Evening River Cruise], | |
| [2], [Paris, France], [Hôtel de l'Europe], [Louvre Museum, Eiffel Tower], | |
| [3], [Lyon, France], [Lyon City Hotel], [City Tour, Local Cuisine Tasting], | |
| [4], [Geneva, Switzerland], [Lakeview Inn], [Lake Geneva, Red Cross Museum], | |
| ) | |
| }, | |
| ) | |
| #lorem(100) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment