// ========================================================= // style.typ – Szablon Pracy Dyplomowej // Wydział Informatyki, ZUT w Szczecinie // Typst 0.12+ // ========================================================= // #let blue-wi = cmyk(60%, 20%, 0%, 0%) // blueWI – jasny niebieski WI #let blue-zut = cmyk(100%, 75%, 0%, 20%) // blueZUT – granatowy ZUT #let gray-zut = cmyk(0%, 0%, 0%, 40%) // grayZUT – szary ZUT // Pochodne (odpowiedniki blueWI!20, blueZUT!60 itp.) #let blue-wi-20 = blue-wi.lighten(80%) #let blue-zut-60 = blue-zut.lighten(40%) #let gray-zut-10 = gray-zut.lighten(90%) // Kolory kodu źródłowego #let code-green = rgb(0, 154, 0) #let code-gray = rgb(128, 128, 128) #let code-violet = rgb(148, 0, 211) #let theorem(title: none, body) = block( width: 100%, fill: luma(97%), stroke: (top: 1.2pt + blue-zut, bottom: 1.2pt + blue-zut, left: 1.2pt + blue-zut, right: 1.2pt + blue-zut), inset: 8pt, above: 8pt, below: 8pt, )[ #text(size: 10pt, weight: "bold", font: "Fira Sans", fill: blue-zut)[ Twierdzenie#if title != none [ — #title] ] #parbreak() #body ] /// Definicja – lewa kreska 2pt blueZUT (dBox) #let definition(title: none, body) = block( width: 100%, stroke: (left: 2pt + blue-zut), inset: (left: 10pt, right: 8pt, top: 4pt, bottom: 4pt), above: 8pt, below: 8pt, )[ #text(size: 10pt, weight: "bold", font: "Fira Sans")[ Definicja#if title != none [ — #title] ] #parbreak() #body ] #let example(title: none, body) = block( above: 6pt, below: 6pt, )[ #text(size: 10pt, weight: "bold", font: "Fira Sans", fill: blue-zut)[ Przykład#if title != none [ — #title] ] #parbreak() #body #h(1fr) $blacksquare$ ] // ╔═══════════════════════════════════════════════════════╗ // ║ 3. GŁÓWNA FUNKCJA SZABLONU ║ // ╚═══════════════════════════════════════════════════════╝ /// Główna funkcja opakowująca dokument. /// /// Parametry: /// shorttitle – krótki tytuł pracy wyświetlany w nagłówku parzystych stron /// body – treść dokumentu #let zut-template( shorttitle: "", body, ) = { // ── 3.1 STRONA ────────────────────────────────────────── set page( paper: "a4", margin: ( top: 3.2cm, bottom: 3.5cm, inside: 3.5cm, // 2.5cm tekst + 1cm outside: 2.5cm, ), // ── Nagłówek ───────────────────────────────────────── header: context { let pg = counter(page).get().first() let is-odd = calc.odd(pg) // is chapter's first page? let chapter-locs = query(heading.where(level: 1)) let is-chapter-page = chapter-locs.any(h => counter(page).at(h.location()).first() == pg) // Detect blank pages inserted by pagebreak(to: "odd"). // The marker is placed before the pagebreak, so it lands on the last page // of the previous chapter (pg - 1 relative to the blank even page). let chapter-break-markers = query() let is-blank-page = ( not is-odd and chapter-break-markers.any( m => counter(page).at(m.location()).first() == pg - 1, ) ) if chapter-locs.len() == 0 {} else if not is-chapter-page and not is-blank-page { // Tytuł bieżącego rozdziału do nagłówka let prev = query(heading.where(level: 1).before(here())) let ch-label = if prev.len() > 0 { let h = prev.last() let num = counter(heading).at(h.location()) [#num.first().#h.body] } else { [] } set text(font: "Fira Sans", size: 10pt) if is-odd { // ── Nieparzysta (prawa) ────────────────────────── grid( columns: (1fr, auto), align: horizon, box( fill: blue-wi-20, width: 100%, inset: (x: 7pt, y: 4pt), )[#ch-label], box( fill: blue-zut, inset: (x: 7pt, y: 4pt), )[#text(fill: white, weight: "bold")[#pg]], ) } else { // ── Parzysta (lewa) ────────────────────────────── grid( columns: (auto, 1fr), align: horizon, box( fill: blue-zut, inset: (x: 7pt, y: 4pt), )[#text(fill: white, weight: "bold")[#pg]], box( fill: blue-wi-20, width: 100%, inset: (x: 7pt, y: 4pt), )[#shorttitle], ) } } }, // /header // ── Stopka ─────────────────────────────────────────── footer: context { let pg = counter(page).get().first() let chapter-locs = query(heading.where(level: 1)) let is-chapter-page = chapter-locs.any(h => counter(page).at(h.location()).first() == pg) if is-chapter-page { align(right, box(fill: blue-zut, inset: (x: 7pt, y: 4pt))[ #text(font: "Fira Sans", size: 10pt, fill: white, weight: "bold")[#pg] ]) } }, numbering: "1", ) // /set page // ── 3.2 TEKST ─────────────────────────────────────────── set text(font: "New Computer Modern", size: 12pt, lang: "pl") set par(justify: true, leading: 0.65em) // ── 3.3 LISTY ─────────────────────────────────────────── set list(marker: [--], spacing: 0.4em, indent: 1em) set enum(spacing: 0.4em, indent: 1em) // ── 3.4 NAGŁÓWKI ───────────── set heading(numbering: "1.1") show heading: it => { let num = if it.numbering != none and it.outlined { counter(heading).display(it.numbering) } if it.level == 1 { // Wymuszenie nowej strony dla głównego rozdziału // Marker placed before the break so it appears on the last page of the // previous chapter; used by the header to detect blank even pages. [#metadata("chapter-break") ] pagebreak(to: "odd", weak: true) v(1.5em, weak: true) block(width: 100%, inset: (bottom: 1em, top: 7em))[ #if num != none { place(left + top, dx: -1.5cm, dy: 0.1em)[ #text(size: 26pt, fill: blue-zut, weight: "bold", font: "Fira Sans", num) ] } #align(left)[ #text(size: 26pt, fill: blue-zut, weight: "bold", font: "Fira Sans", it.body) ] ] } else { let txt-size = if it.level == 2 { 14pt } else if it.level == 3 { 12pt } else { 11pt } let spacing-top = if it.level == 2 { 1.5em } else if it.level == 3 { 1.2em } else { 1em } let spacing-bottom = if it.level == 2 { 0.5em } else { 0.4em } v(spacing-top, weak: true) block(width: 100%, inset: (bottom: spacing-bottom))[ #if num != none { let dy-val = if it.level == 2 { 0.0em } else { 0.05em } place(left + top, dx: -1.5cm, dy: dy-val)[ #text(size: txt-size, fill: blue-zut, weight: "bold", font: "Fira Sans", num) ] } // Tytuł na czarno, dopasowany do tekstu #text(size: txt-size, fill: black, weight: "bold", font: "Fira Sans", it.body) ] } } // ── 3.6 BLOKI KODU ────────────────────────────────────── show raw.where(block: true): it => { block( width: 100%, fill: gray-zut.lighten(93%), stroke: (left: 2pt + blue-wi), inset: (x: 12pt, y: 9pt), above: 9pt, below: 9pt, radius: (right: 2pt), )[ #set text(size: 9.5pt, font: ("Fira Mono", "Courier New")) #it ] } set raw(theme: "zut.tmTheme") show raw.where(block: true): it => { let lines = it.lines block( fill: gray-zut.lighten(100%), radius: 4pt, clip: true, width: 100%, )[ #set text(size: 10pt, font: ("Fira Mono", "Courier New")) #grid( columns: (auto, 1fr), inset: (left: 10pt, right: 10pt, top: 2pt, bottom: 2pt), align: (x, y) => if x == 0 { right + top } else { left + top }, grid.vline(x: 1, stroke: (paint: luma(160), thickness: 1pt)), ..lines .map(line => ( text(fill: luma(160), size: 9pt)[#line.number], line.body, )) .flatten(), ) ] } // ── 3.7 PODPISY RYSUNKÓW I TABEL ──────────────────────── set figure(gap: 0.8em) let raw-fig-counter = counter("raw-fig") let img-fig-counter = counter("img-fig") show heading.where(level: 1): it => { raw-fig-counter.update(1) img-fig-counter.update(1) it } show figure.where(kind: raw): it => { // Step the custom counter raw-fig-counter.step() // Build the number: heading-index.figure-index let heading-num = counter(heading.where(level: 1)).display() let fig-num = raw-fig-counter.display("1") let full-number = heading-num + "." + fig-num it.body v(10pt, weak: true) align(center, block(width: auto)[ #text(weight: "bold", fill: blue-zut, font: "Fira Sans")[Program #full-number:] #text(font: "Fira Sans")[#it.caption.body] ]) } show figure.where(kind: image): it => { // Step the custom counter img-fig-counter.step() // Build the number: heading-index.figure-index let heading-num = counter(heading.where(level: 1)).display() let fig-num = img-fig-counter.display("1") let full-number = heading-num + "." + fig-num it.body v(10pt, weak: true) align(center, block(width: auto)[ #text(weight: "bold", fill: blue-zut, font: "Fira Sans")[Rysunek #full-number:] #text(font: "Fira Sans")[#it.caption.body] ]) } // ── 3.8 TABELE ────────────────────────────────────────── set table( stroke: none, inset: (x: 8pt, y: 5pt), ) show table.cell.where(y: 0): set text(weight: "bold") show table: set block(above: 8pt, below: 8pt) body } // /zut-template /// Spis rysunków – wywołaj jako ostatni element wewnątrz zut-template. /// /// Wyszukuje wszystkie figure(kind: image) w dokumencie i odtwarza /// numery X.Y identyczne z tymi generowanymi przez show figure.where(kind: image) /// w zut-template. #let list-of-figures() = { [ #set heading(numbering: none) #show heading: it => { v(1em) block(width: 100%, inset: (bottom: 3em, top: 7em, left: 2em))[ #align(left)[ #text(size: 25pt, fill: blue-zut, weight: "regular", font: "Fira Sans", it.body) ] ] } #heading(level: 1, outlined: false)[Spis rysunków] ] context { let all-figs = query(figure.where(kind: image)) let local = 1 let prev-ch = -1 for fig in all-figs { let ch = counter(heading.where(level: 1)).at(fig.location()).first() if ch != prev-ch { local = 0 prev-ch = ch } local = local + 1 let full-number = str(ch) + "." + str(local) let pg = counter(page).at(fig.location()).first() let cap = fig.caption.body v(8pt, weak: true) link(fig.location())[ #grid( columns: (3em, 1fr, auto), align: (left, left, right), text( size: 11pt, weight: "regular", font: "Fira Sans", )[#full-number], // Podpis + wypełnienie kropkami [ #text(size: 11pt, font: "Fira Sans")[#cap] #box(width: 1fr, inset: (x: 0pt))[ #text[#repeat[ . ]] ] ], // Numer strony text( size: 10pt, weight: "regular", font: "Fira Sans", )[#pg], ) ] } } } /// Funkcja rysująca abstrakt – polska wersja u góry, angielska na dole, jedna strona A4. /// /// Parametry: /// pl-title – nagłówek sekcji polskiej (domyślnie "Streszczenie") /// pl-body – treść streszczenia po polsku /// pl-keywords – słowa kluczowe (pojedynczy ciąg, np. "A, B, C") /// en-title – nagłówek sekcji angielskiej (domyślnie "Abstract") /// en-body – treść abstraktu po angielsku /// en-keywords – keywords (pojedynczy ciąg, np. "A, B, C") #let abstract( pl-title: "Streszczenie", pl-body: str, pl-keywords: "", en-title: "Abstract", en-body: str, en-keywords: "", ) = { set page( paper: "a4", margin: ( top: 3.2cm, bottom: 3.5cm, inside: 3.5cm, outside: 2.5cm, ), header: none, footer: none, numbering: none, ) set text(font: "New Computer Modern", size: 12pt, lang: "pl") set par(justify: true, leading: 0.65em) set heading(numbering: none, outlined: false) show heading: it => { if it.level == 1 { align(center)[ #text(size: 17pt, fill: blue-zut, weight: "regular", font: "Fira Sans", it.body) ] } } // polish version of the abstract v(0.7cm) [= #pl-title] v(0.5cm) par(pl-body, first-line-indent: 2em) if pl-keywords != "" { [*słowa kluczowe:* #pl-keywords] } v(1fr) // english version [= #en-title] set text(lang: "en") v(0.5cm) par(en-body) if en-keywords != "" { [*keywords:* #en-keywords] } }