From 2d480503781a3705e763ad66c3125877bfd5e241 Mon Sep 17 00:00:00 2001 From: AljGe Date: Fri, 17 Oct 2025 13:02:52 +0200 Subject: [PATCH 1/3] Add demo and template files for sharepic layout examples --- demo.typ | 92 ++++++++++++++++++++ kew.jpg | Bin 0 -> 3859 bytes template.typ | 238 +++++++++++++++++++++++++++++++++++++++++++++++++++ tux.svg | 4 + 4 files changed, 334 insertions(+) create mode 100644 demo.typ create mode 100644 kew.jpg create mode 100644 template.typ create mode 100644 tux.svg diff --git a/demo.typ b/demo.typ new file mode 100644 index 0000000..66b2d22 --- /dev/null +++ b/demo.typ @@ -0,0 +1,92 @@ +#import "template.typ": sharepic + +// Demo 1: Horizontales layout mit 3 logos +#sharepic( + logos: ( + (path: "kew.jpg", height: 3.5cm, rotation: -8deg), + (path: "connection.png", height: 3.5cm, rotation: 0deg), + (path: "tux.svg", height: 3.5cm, rotation: 8deg) + ), + layout: "horizontal", + title: [ÜberwachtAtlas – „Gefährliche Orte" & MWVZ], + when: [22.10.2025, 17:00 Uhr], + where: [Uni Leipzig, Hörsaal 16 (Hauptcampus)], + about: [ + *Vortrag:* Sogenannte „gefährliche Orte" und Messer- und Waffenverbotszonen ermöglichen der Polizei ortsbezogene, verdachtsunabhängige Kontrollen. Die Polizei setzt diese Kontrollen um und legt die Orte zumeist selbst fest. Im ÜberwachtAtlas haben wir die „gefährlichen Orte" und MWVZ visualisiert. + ] +) + +// Demo 2: Vertikales layout mit 2 logos +#sharepic( + logos: ( + (path: "tux.svg", height: 3.5cm, rotation: -25deg), + (path: "connection.png", height: 3.5cm, rotation: 0deg) + ), + layout: "vertical", + title: [AG Link goes KEW], + when: [23.10.2025 13:00], + where: [Uni Leipzig \ Seminargebäude \ Raum S125], + about: [ + Wir versuchen in diesem Vortrag zu zeigen warum es ein Problem ist wenn Konzerne unsere Daten sammeln und was man dagegen tun kann. + \ *Alle sind willkommen. + Kein Vorwissen benötigt.* + ] +) + +// Demo 3: Einfaches layout mit einem Logo +#sharepic( + logos: ("tux.svg",), + layout: "horizontal", + title: [Simple Event], + about: [This is a simple event with just one logo.] +) + +// Demo 4: Vier Logos in horizontalem Layout (Gitter) +#sharepic( + logos: ( + (path: "tux.svg", height: 3cm), + (path: "connection.png", height: 3cm, rotation: -10deg), + (path: "kew.jpg", height: 3cm, rotation: 10deg), + (path: "tux.svg", height: 3cm, rotation: -5deg) + ), + layout: "horizontal", + title: [Four Logo Event], + about: [This demonstrates how four logos are arranged in a grid pattern in horizontal layout.] +) + +// Demo 5: Vertikales layout mit benutzerdefinierter Positionierung +#sharepic( + logos: ( + (path: "tux.svg", height: 3cm, rotation: 0deg), + (path: "connection.png", height: 3cm, rotation: 0deg), + (path: "kew.jpg", height: 3cm, rotation: 0deg) + ), + layout: "vertical", + title: [Vertical Layout Demo], + about: [This shows how logos are stacked vertically on the left side.] +) + +// Demo 6: Verwendung von String-Logos (einfache Verwendung) +#sharepic( + logos: ("tux.svg", "connection.png", "kew.jpg"), + layout: "horizontal", + title: [String Logos Demo], + when: [Today at 2 PM], + where: [Main Hall], + about: [This demonstrates the simplest usage - just pass logo file names as strings.] +) + +// Demo 7: Benutzerdefinierte Theme-Überschreibung +#sharepic( + logos: ( + (path: "connection.png", height: 4cm, rotation: 0deg) + ), + layout: "horizontal", + title: [Custom Theme Demo], + about: [This uses a custom theme override for different colors.], + theme-override: ( + bg-color: rgb("#f0f8ff"), + fg-color: rgb("#2f4f4f"), + accent-color: rgb("#4682b4") + ) +) diff --git a/kew.jpg b/kew.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e0a1b29833b3d4822f87305a4723f4e80858bd93 GIT binary patch literal 3859 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 + + + From 957dfef98d52f4acda2d88fd2458463b6eb1c57c Mon Sep 17 00:00:00 2001 From: AljGe Date: Fri, 17 Oct 2025 13:18:00 +0200 Subject: [PATCH 2/3] Refactor demo and template files: removed horizontal logo demo, added responsive sizing and spacing functions for text elements, and improved logo placement logic for vertical layouts. --- demo.typ | 13 ---- template.typ | 165 ++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 137 insertions(+), 41 deletions(-) diff --git a/demo.typ b/demo.typ index 66b2d22..61ddf23 100644 --- a/demo.typ +++ b/demo.typ @@ -41,19 +41,6 @@ about: [This is a simple event with just one logo.] ) -// Demo 4: Vier Logos in horizontalem Layout (Gitter) -#sharepic( - logos: ( - (path: "tux.svg", height: 3cm), - (path: "connection.png", height: 3cm, rotation: -10deg), - (path: "kew.jpg", height: 3cm, rotation: 10deg), - (path: "tux.svg", height: 3cm, rotation: -5deg) - ), - layout: "horizontal", - title: [Four Logo Event], - about: [This demonstrates how four logos are arranged in a grid pattern in horizontal layout.] -) - // Demo 5: Vertikales layout mit benutzerdefinierter Positionierung #sharepic( logos: ( diff --git a/template.typ b/template.typ index abe6393..4b420f4 100644 --- a/template.typ +++ b/template.typ @@ -7,10 +7,21 @@ body-font: "DejaVu Sans", border-radius: 0.8cm, border-stroke: 4pt, + // Fixed sizes (fallback if responsive is disabled) title-size: 20pt, label-size: 12pt, body-size: 12pt, info-text-size: 12pt, + // Responsive sizing configuration + title-size-max: 28pt, + title-size-min: 14pt, + title-base-length: 45, // Character count baseline + body-size-max: 14pt, + body-size-min: 9pt, + body-base-length: 200, // Character count baseline + info-size-max: 11pt, + info-size-min: 9pt, + info-base-length: 60, // Character count baseline ) // Default logo configuration @@ -18,7 +29,7 @@ path: none, height: 4cm, fit: "contain", - rotation: 0deg, + rotation: 10deg, offset: (x: 0cm, y: 0cm), pin-position: none, // "top-left", "top-center", "top-right", etc. ) @@ -68,51 +79,147 @@ place-logo(logos.at(0), top + left, dx: 1.5cm, dy: 0.7cm) place-logo(logos.at(1), top + center, dy: 0.7cm) place-logo(logos.at(2), top + right, dx: -1.5cm, dy: 0.7cm) - } else { - // 4+ logos - anordnen in Gitter - let cols = calc.min(2, logos.len()) - let rows = calc.ceil(logos.len() / cols) - for (i, logo) in logos.enumerate() { - let row = calc.floor(i / cols) - let col = calc.rem(i, cols) - let x = -2.5cm + col * 5cm - let y = 0.5cm + row * 2.2cm - place-logo(logo, top + left, dx: x, dy: y) - } } } else if layout == "vertical" { - // Logos auf der rechten Seite positioniert, vertikal gestapelt + // Logos auf der rechten Seite positioniert, vertikal gestapelt und zentral angeordnet + let card-height = 14cm + let logo-height = 4cm // Standard logo height + if logos.len() == 1 { - place-logo(logos.first(), top + right, dx: -1cm, dy: 0.7cm) + // Single logo: centered vertically + let dy = (card-height - logo-height) / 2 + place-logo(logos.first(), top + right, dx: -1cm, dy: dy) } else if logos.len() == 2 { - place-logo(logos.at(0), top + right, dx: -1cm, dy: 0.7cm) - place-logo(logos.at(1), top + right, dx: -1cm, dy: 4.2cm) + // Two logos: vertically spaced around center + let total-height = 2 * logo-height + let start-dy = (card-height - total-height) / 2 + place-logo(logos.at(0), top + right, dx: -1cm, dy: start-dy) + place-logo(logos.at(1), top + right, dx: -1cm, dy: start-dy + logo-height) } else { - // 3+ logos: vertikal auf der rechten Seite mit leichtem Offset gestapelt + // 3+ logos: evenly distributed vertically around center + let total-height = logos.len() * logo-height + let start-dy = (card-height - total-height) / 2 for (i, logo) in logos.enumerate() { - let dy = 0.7cm + i * 3.5cm + let dy = start-dy + i * logo-height place-logo(logo, top + right, dx: -1cm, dy: dy) } } } } +// Hilfsfunktion um responsive Schriftgröße zu berechnen basierend auf Textlänge +#let calculate-responsive-size(text-content, max-size: 28pt, min-size: 14pt, base-length: 50) = { + // Zähle Zeichen in Text + let text-length = if type(text-content) == str { + text-content.len() + } else if type(text-content) == content { + // Für Content-Blöcke: Schätzung der Länge + repr(text-content).len() + } else { + 0 + } + + // Berechne Reduktionsfaktor + let ratio = if text-length > base-length { + (base-length / text-length) + } else { + 1.0 + } + + // Skaliere die Größe, halte sie in Grenzen + let range = max-size - min-size + let scaled = min-size + range * ratio + + // Stelle sicher, dass die Größe in Grenzen bleibt + if scaled > max-size { + max-size + } else if scaled < min-size { + min-size + } else { + scaled + } +} + +// Hilfsfunktion um responsive Abstände basierend auf Textmenge zu berechnen +#let calculate-responsive-spacing(about-content, max-spacing: 1.2em, min-spacing: 0.6em, base-length: 200) = { + let text-length = if type(about-content) == str { + about-content.len() + } else if type(about-content) == content { + repr(about-content).len() + } else { + 0 + } + + // Reduziere Abstand wenn viel Text vorhanden + let ratio = if text-length > base-length { + (base-length / text-length) + } else { + 1.0 + } + + let range = max-spacing - min-spacing + let scaled = min-spacing + range * ratio + + if scaled > max-spacing { + max-spacing + } else if scaled < min-spacing { + min-spacing + } else { + scaled + } +} + // Hilfsfunktion um den Inhalt zu arrangieren #let arrange-content(title: [], when: [], where: [], about: [], layout: "horizontal", current-theme: theme) = { let left-padding = if layout == "vertical" { 1.5cm } else { 1.5cm } let right-padding = if layout == "vertical" { 5cm } else { 1.5cm } let top-padding = if layout == "vertical" { 0.8cm } else { 5.5cm } + // Berechne responsive Schriftgrößen + let responsive-title-size = calculate-responsive-size( + title, + max-size: current-theme.title-size-max, + min-size: current-theme.title-size-min, + base-length: current-theme.title-base-length + ) + + let responsive-body-size = calculate-responsive-size( + about, + max-size: current-theme.body-size-max, + min-size: current-theme.body-size-min, + base-length: current-theme.body-base-length + ) + + let responsive-info-size = calculate-responsive-size( + when + where, + max-size: current-theme.info-size-max, + min-size: current-theme.info-size-min, + base-length: current-theme.info-base-length + ) + + // Berechne responsive Abstände + let responsive-spacing = calculate-responsive-spacing( + about, + max-spacing: 1.2em, + min-spacing: 0.5em, + base-length: current-theme.body-base-length + ) + + // Berechne maximale Höhe für Beschreibungstext basierend auf verfügbarem Platz + // Card: 14cm x 14cm, Padding: 1.5cm L+R, 1.5cm B, 5.5cm T (horizontal) + // Ungefähre verfügbare Höhe: 14cm - 5.5cm - 1.5cm (Title/Info space) - 1.5cm (padding) = 5.5cm + let max-about-height = 5.5cm + pad(top: top-padding, bottom: 1.5cm, left: left-padding, right: right-padding)[ #stack( dir: ttb, - spacing: 1em, - // Title + spacing: responsive-spacing, + // Title mit responsiver Größe if title != [] { - text(size: current-theme.title-size, weight: "bold", font: current-theme.heading-font, title) + text(size: responsive-title-size, weight: "bold", font: current-theme.heading-font, title) }, - // Info-Block für wann/wo + // Info-Block für wann/wo mit responsiver Größe if when != [] or where != [] { block( inset: 0.8em, @@ -127,7 +234,7 @@ dir: ltr, spacing: 0.5em, align(center + horizon, emoji.calendar), - text(size: current-theme.info-text-size, when), + text(size: responsive-info-size, when), )) }, if where != [] { @@ -135,19 +242,21 @@ dir: ltr, spacing: 0.5em, align(center + horizon, emoji.pin), - text(size: current-theme.info-text-size, where), + text(size: responsive-info-size, where), )) }, ), ) }, - // Beschreibungstext + // Beschreibungstext mit responsiver Größe und maximaler Höhe if about != [] { let about-align = if layout == "vertical" { left } else { center } - block( - spacing: 0.1em, - align(about-align, text(size: current-theme.body-size, about)) + box( + height: max-about-height, + width: 100%, + clip: true, + align(about-align, text(size: responsive-body-size, about)) ) } ) From 199fb6f473b7cb6a548651d365aa9165d3502b6c Mon Sep 17 00:00:00 2001 From: AljGe Date: Mon, 20 Oct 2025 15:50:40 +0200 Subject: [PATCH 3/3] Update template.typ: remove trailing dash from documentation comment --- template.typ | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/template.typ b/template.typ index 4b420f4..e590602 100644 --- a/template.typ +++ b/template.typ @@ -311,7 +311,7 @@ ] } -// Verwendungsbeispiele und Dokumentation +// Verwendungsbeispiele und Dokumentation - /* #sharepic( logos: (