forked from lovis/socials-template
		
	Compare commits
	
		
			2 commits
		
	
	
		
			1d64be86f1
			...
			957dfef98d
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 957dfef98d | ||
|   | 2d48050378 | 
					 4 changed files with 430 additions and 0 deletions
				
			
		
							
								
								
									
										79
									
								
								demo.typ
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								demo.typ
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,79 @@ | |||
| #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 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") | ||||
|   ) | ||||
| ) | ||||
							
								
								
									
										
											BIN
										
									
								
								kew.jpg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								kew.jpg
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 3.8 KiB | 
							
								
								
									
										347
									
								
								template.typ
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										347
									
								
								template.typ
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,347 @@ | |||
| #let theme = ( | ||||
|   bg-color: rgb("#fdf6e3"), | ||||
|   fg-color: rgb("#073642"), | ||||
|   accent-color: rgb("#268bd2"), | ||||
|   border-color: rgb("#073642"), | ||||
|   heading-font: "Atkinson Hyperlegible", | ||||
|   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 | ||||
| #let default-logo = ( | ||||
|   path: none, | ||||
|   height: 4cm, | ||||
|   fit: "contain", | ||||
|   rotation: 10deg, | ||||
|   offset: (x: 0cm, y: 0cm), | ||||
|   pin-position: none, // "top-left", "top-center", "top-right", etc. | ||||
| ) | ||||
| 
 | ||||
| // Konfiguriert die Logos | ||||
| #let configure-logo(logo-config) = { | ||||
|   if logo-config == none { | ||||
|     return default-logo | ||||
|   } | ||||
|   if type(logo-config) == str { | ||||
|     // String = nur Pfad | ||||
|     return default-logo + (path: logo-config) | ||||
|   } | ||||
|   return default-logo + logo-config | ||||
| } | ||||
| 
 | ||||
| // Hilfsfunktion um ein einzelnes Logo zu positionieren | ||||
| #let place-logo(logo, position, dx: 0cm, dy: 0cm) = { | ||||
|   let logo = configure-logo(logo) | ||||
|   if logo.path == none { return } | ||||
| 
 | ||||
|   place(position, dx: logo.offset.x + dx, dy: logo.offset.y + dy)[ | ||||
|     #rotate(logo.rotation)[ | ||||
|       #image(logo.path, height: logo.height, fit: logo.fit) | ||||
|     ] | ||||
|   ] | ||||
| } | ||||
| 
 | ||||
| // Positioniert mehrere Logos basierend auf Layout und Anzahl | ||||
| #let position-logos(logos, layout: "horizontal") = { | ||||
|   let logos = if type(logos) == array { | ||||
|     logos.map(configure-logo) | ||||
|   } else if type(logos) == dictionary { | ||||
|     (configure-logo(logos),) | ||||
|   } else { | ||||
|     () | ||||
|   } | ||||
| 
 | ||||
|   if layout == "horizontal" { | ||||
|     // Logos nebeneinander oben | ||||
|     if logos.len() == 1 { | ||||
|       place-logo(logos.first(), top + center) | ||||
|     } else if logos.len() == 2 { | ||||
|       place-logo(logos.at(0), top + left, dx: 1.5cm, dy: 0.7cm) | ||||
|       place-logo(logos.at(1), top + right, dx: -1.5cm, dy: 0.7cm) | ||||
|     } else if logos.len() == 3 { | ||||
|       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 if layout == "vertical" { | ||||
|     // 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 { | ||||
|       // 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 { | ||||
|       // 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: 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 = 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: responsive-spacing, | ||||
|       // Title mit responsiver Größe | ||||
|       if title != [] { | ||||
|         text(size: responsive-title-size, weight: "bold", font: current-theme.heading-font, title) | ||||
|       }, | ||||
| 
 | ||||
|       // Info-Block für wann/wo mit responsiver Größe | ||||
|       if when != [] or where != [] { | ||||
|         block( | ||||
|           inset: 0.8em, | ||||
|           radius: 0.4em, | ||||
|           width: 100%, | ||||
|           fill: current-theme.bg-color.mix(current-theme.accent-color).darken(10%), | ||||
|           stack( | ||||
|             dir: ttb, | ||||
|             spacing: 0.4em, | ||||
|             if when != [] { | ||||
|               align(center + horizon, stack( | ||||
|                 dir: ltr, | ||||
|                 spacing: 0.5em, | ||||
|                 align(center + horizon, emoji.calendar), | ||||
|                 text(size: responsive-info-size, when), | ||||
|               )) | ||||
|             }, | ||||
|             if where != [] { | ||||
|               align(center + horizon, stack( | ||||
|                 dir: ltr, | ||||
|                 spacing: 0.5em, | ||||
|                 align(center + horizon, emoji.pin), | ||||
|                 text(size: responsive-info-size, where), | ||||
|               )) | ||||
|             }, | ||||
|           ), | ||||
|         ) | ||||
|       }, | ||||
| 
 | ||||
|       // Beschreibungstext mit responsiver Größe und maximaler Höhe | ||||
|       if about != [] { | ||||
|         let about-align = if layout == "vertical" { left } else { center } | ||||
|         box( | ||||
|           height: max-about-height, | ||||
|           width: 100%, | ||||
|           clip: true, | ||||
|           align(about-align, text(size: responsive-body-size, about)) | ||||
|         ) | ||||
|       } | ||||
|     ) | ||||
|   ] | ||||
| } | ||||
| 
 | ||||
| // Haupt-Vorlage-Funktion | ||||
| #let sharepic( | ||||
|   logos: (), | ||||
|   layout: "horizontal", | ||||
|   title: [], | ||||
|   when: [], | ||||
|   where: [], | ||||
|   about: [], | ||||
|   theme-override: (:) | ||||
| ) = { | ||||
|   // Überschreiben der Theme | ||||
|   let current-theme = theme + theme-override | ||||
| 
 | ||||
|   set page( | ||||
|     width: 15cm, | ||||
|     height: 15cm, | ||||
|     fill: current-theme.bg-color, | ||||
|     margin: 0cm, | ||||
|   ) | ||||
|   set text( | ||||
|     font: current-theme.body-font, | ||||
|     size: 15pt, | ||||
|     fill: current-theme.fg-color, | ||||
|   ) | ||||
| 
 | ||||
|   align(center + horizon)[ | ||||
|     #rect( | ||||
|       width: 14cm, | ||||
|       height: 14cm, | ||||
|       radius: current-theme.border-radius, | ||||
|       stroke: current-theme.border-stroke + current-theme.border-color, | ||||
|     )[ | ||||
|       // Positioniert Logos basierend auf Layout | ||||
|       #{position-logos(logos, layout: layout)} | ||||
| 
 | ||||
|       // Inhaltsbereich | ||||
|       #{arrange-content( | ||||
|         title: title, | ||||
|         when: when, | ||||
|         where: where, | ||||
|         about: about, | ||||
|         layout: layout, | ||||
|         current-theme: current-theme | ||||
|       )} | ||||
|     ] | ||||
|   ] | ||||
| } | ||||
| 
 | ||||
| // Verwendungsbeispiele und Dokumentation | ||||
| /* | ||||
| #sharepic( | ||||
|   logos: ( | ||||
|     (path: "logo1.png", height: 4cm), | ||||
|     (path: "connection.png", height: 4cm, rotation: -8deg), | ||||
|     (path: "kew.jpg", height: 4cm, rotation: 8deg) | ||||
|   ), | ||||
|   layout: "horizontal", | ||||
|   title: [Beispiel-Event], | ||||
|   when: [22.10.2025, 17:00 Uhr], | ||||
|   where: [Ort Hier], | ||||
|   about: [Beschreibung des Events...] | ||||
| ) | ||||
| 
 | ||||
| #sharepic( | ||||
|   logos: ( | ||||
|     (path: "logo1.png", height: 3cm), | ||||
|     (path: "logo2.png", height: 3cm), | ||||
|     (path: "logo3.png", height: 3cm), | ||||
|     (path: "logo4.png", height: 3cm) | ||||
|   ), | ||||
|   layout: "vertical", | ||||
|   title: [Beispiel-Event], | ||||
|   about: [Beschreibung des Events...] | ||||
| ) | ||||
| 
 | ||||
| // Einfache Verwendung mit String-Logos | ||||
| #sharepic( | ||||
|   logos: ("connection.png", "kew.jpg"), | ||||
|   layout: "horizontal", | ||||
|   title: [Beispiel-Event] | ||||
| ) | ||||
| */ | ||||
							
								
								
									
										4
									
								
								tux.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								tux.svg
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| After Width: | Height: | Size: 21 KiB | 
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue