Shaped boxes with text
(I guess that will make a package eventually, but let it be a snippet for now)
/// author: JustForFun88
#import "@preview/oxifmt:0.2.0": strfmt
#let shadow_svg_path = `
<svg
width="{canvas-width}"
height="{canvas-height}"
viewBox="{viewbox}"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<!-- Definitions for reusable components -->
<defs>
<filter id="shadowing" >
<feGaussianBlur in="SourceGraphic" stdDeviation="{blur}" />
</filter>
</defs>
<!-- Drawing the rectangle with a fill and feGaussianBlur effect -->
<path
style="fill: {flood-color}; opacity: {flood-opacity}; filter:url(#shadowing)"
d="{vertices} Z" />
</svg>
`.text
#let parallelogram(width: 20mm, height: 5mm, angle: 30deg) = {
let δ = height * calc.tan(angle)
(
( + δ, 0pt ),
(width + δ * 2, 0pt ),
(width + δ, height),
(0pt, height),
)
}
#let hexagon(width: 100pt, height: 30pt, angle: 30deg) = {
let dy = height / 2;
let δ = dy * calc.tan(angle)
(
(0pt, dy ),
( + δ, 0pt ),
(width + δ, 0pt ),
(width + δ * 2, dy ),
(width + δ, height),
( + δ, height),
)
}
#let shape_size(vertices) = {
let x_vertices = vertices.map(array.first);
let y_vertices = vertices.map(array.last);
(
calc.max(..x_vertices) - calc.min(..x_vertices),
calc.max(..y_vertices) - calc.min(..y_vertices)
)
}
#let shadowed_shape(shape: hexagon, fill: none,
stroke: auto, angle: 30deg, shadow_fill: black, alpha: 0.5,
blur: 1.5, blur_margin: 5, dx: 0pt, dy: 0pt, ..args, content
) = layout(size => context {
let named = args.named()
for key in ("width", "height") {
if key in named and type(named.at(key)) == ratio {
named.insert(key, size.at(key) * named.at(key))
}
}
let opts = (blur: blur, flood-color: shadow_fill.to-hex())
let content = box(content, ..named)
let size = measure(content)
let vertices = shape(..size, angle: angle)
let (shape_width, shape_height) = shape_size(vertices)
let margin = opts.blur * blur_margin * 1pt
opts += (
canvas-width: shape_width + margin,
canvas-height: shape_height + margin,
flood-opacity: alpha
)
opts.viewbox = (0, 0, opts.canvas-width.pt(), opts.canvas-height.pt()).map(str).join(",")
opts.vertices = "";
let d = margin / 2;
for (i, p) in vertices.enumerate() {
let prefix = if i == 0 { "M " } else { " L " };
opts.vertices += prefix + p.map(x => str((x + d).pt())).join(", ");
}
let svg-shadow = image.decode(strfmt(shadow_svg_path, ..opts))
place(dx: dx, dy: dy, svg-shadow)
place(path(..vertices, fill: fill, stroke: stroke, closed: true))
box(h((shape_width - size.width) / 2) + content, width: shape_width)
})
#set text(3em);
#shadowed_shape(shape: hexagon,
inset: 1em, fill: teal,
stroke: 1.5pt + teal.darken(50%),
shadow_fill: red,
dx: 0.5em, dy: 0.35em, blur: 3)[Hello there!]
#shadowed_shape(shape: parallelogram,
inset: 1em, fill: teal,
stroke: 1.5pt + teal.darken(50%),
shadow_fill: red,
dx: 0.5em, dy: 0.35em, blur: 3)[Hello there!]