213 lines
6.0 KiB
Typst
213 lines
6.0 KiB
Typst
// Таблицы — как в «Пример работы с Typst.typ» и gost: figure + table + table.header.
|
||
// Разрыв длинных таблиц и подпись сверху задаёт шаблон modern-g7-32 (style.typ).
|
||
|
||
#show table: set text(hyphenate: true)
|
||
|
||
#let pz-appendix-title(body) = heading(level: 1)[#body]
|
||
|
||
#let pz-sig-line(label) = box(
|
||
width: 5.5cm,
|
||
stroke: (bottom: 0.5pt),
|
||
inset: (bottom: 3pt),
|
||
)[#label]
|
||
|
||
// Без heading(level: 1): в gost перед ним всегда pagebreak (add-pagebreaks).
|
||
#let pz-front-heading(body, page-break: true) = {
|
||
if page-break {
|
||
pagebreak(weak: true)
|
||
}
|
||
align(center)[
|
||
#text(weight: "bold")[#upper(body)]
|
||
]
|
||
v(0.75em)
|
||
}
|
||
|
||
// Официальная тема ВКР (приказ, титул, аннотация, ТЗ).
|
||
#let pz-thesis-topic = "Мобильное приложение для защищённого хранения пользовательских данных"
|
||
#let pz-thesis-topic-en = "Mobile application for secure storage of user data"
|
||
#let pz-thesis-subject = "мобильном приложении для защищённого хранения пользовательских данных"
|
||
|
||
// Бланк аннотации: 2×2 без рамок; левый столбец — отступ, текст во 2-м (2 ячейки).
|
||
#let pz-dept-mop = "кафедра МОП ЭВМ"
|
||
#let pz-dept-sait = "кафедра системного анализа и телекоммуникаций"
|
||
|
||
#let pz-biblio-strip(head, tail) = {
|
||
table(
|
||
columns: (0.82fr, 1fr),
|
||
stroke: none,
|
||
inset: (x: 0pt, y: 5pt),
|
||
align: (left, left),
|
||
[], head,
|
||
[], tail,
|
||
)
|
||
v(0.65em)
|
||
}
|
||
|
||
#let pz-biblio-strip-ru(
|
||
udk: "",
|
||
author: "",
|
||
title: "",
|
||
direction: "09.03.04",
|
||
institute: "Южный федеральный университет",
|
||
faculty: "ИКТИБ",
|
||
department: pz-dept-mop,
|
||
year: 2026,
|
||
) = {
|
||
let pages = context counter(page).final().first()
|
||
pz-biblio-strip(
|
||
[
|
||
УДК #udk
|
||
#linebreak()
|
||
#upper(author)
|
||
#linebreak()
|
||
#quote[#title]
|
||
],
|
||
[
|
||
Квалификационная работа на степень
|
||
#linebreak()
|
||
«БАКАЛАВР» по направлению #direction
|
||
#linebreak()
|
||
#institute,
|
||
#linebreak()
|
||
#faculty, #department — #year#sym.space.nobreak г.,
|
||
#linebreak()
|
||
#pages#sym.space.nobreak с.
|
||
],
|
||
)
|
||
}
|
||
|
||
#let pz-biblio-strip-en(
|
||
udk: "",
|
||
author: "",
|
||
title: "",
|
||
direction: "09.03.04",
|
||
institute: "Southern Federal University",
|
||
faculty: "ICTIS",
|
||
department: "the Department of Mathematical Support and Application of Computers (MOP EVM)",
|
||
year: 2026,
|
||
) = {
|
||
let pages = context counter(page).final().first()
|
||
pz-biblio-strip(
|
||
[
|
||
UDC #udk
|
||
#linebreak()
|
||
#upper(author)
|
||
#linebreak()
|
||
#quote[#title]
|
||
],
|
||
[
|
||
Qualification work for the degree of
|
||
#linebreak()
|
||
BACHELOR in the direction of #direction
|
||
#linebreak()
|
||
#institute,
|
||
#linebreak()
|
||
#faculty, #department — #year,
|
||
#linebreak()
|
||
#pages#sym.space.nobreak p.
|
||
],
|
||
)
|
||
}
|
||
|
||
#let pz-column-count(columns) = if type(columns) == int {
|
||
columns
|
||
} else if type(columns) == array {
|
||
columns.len()
|
||
} else {
|
||
1
|
||
}
|
||
|
||
// Строка из ..range().map(i => ([a], [b])) в body.pos() — один аргумент-массив.
|
||
#let pz-flatten-cells(cells) = cells.fold((), (acc, cell) => {
|
||
if type(cell) == array { acc + cell } else { acc + (cell,) }
|
||
})
|
||
|
||
#let pz-table-inner(columns, header-cells, tab-num, start-page, ..rows) = {
|
||
let col-n = pz-column-count(columns)
|
||
let cont-cell = table.cell(
|
||
colspan: col-n,
|
||
stroke: none,
|
||
inset: (x: 0pt, y: 2pt),
|
||
align: left,
|
||
)[
|
||
#context {
|
||
if start-page.get() == none {
|
||
start-page.update(here().page())
|
||
}
|
||
let first-page = start-page.get()
|
||
if first-page != none and here().page() > first-page {
|
||
[Продолжение таблицы #tab-num]
|
||
}
|
||
}
|
||
]
|
||
let header-row = if header-cells.len() > 0 {
|
||
(cont-cell, ..header-cells)
|
||
} else {
|
||
(cont-cell,)
|
||
}
|
||
table(
|
||
columns: columns,
|
||
table.header(..header-row),
|
||
..rows,
|
||
)
|
||
}
|
||
|
||
#let pz-table(caption, columns, ..body) = {
|
||
let pos = body.pos()
|
||
let has-header = pos.len() > 0 and pos.first().func() == table.header
|
||
let header-cells = if has-header { pos.first().children } else { () }
|
||
let tail = pz-flatten-cells(if has-header { pos.slice(1) } else { pos })
|
||
let start-page = state("pz-table-start-page", none)
|
||
figure(
|
||
kind: table,
|
||
context {
|
||
let fig-loc = here()
|
||
let tab-num = counter(figure.where(kind: table)).at(fig-loc).first()
|
||
start-page.update(none)
|
||
pz-table-inner(columns, header-cells, tab-num, start-page, ..arguments(..tail))
|
||
},
|
||
caption: caption,
|
||
)
|
||
}
|
||
|
||
// Реестры unit-тестов: перенос длинных идентификаторов (camelCase, _); сетка как в примере Typst.
|
||
#let pz-test-table(caption, columns, ..body) = {
|
||
let pos = body.pos()
|
||
let has-header = pos.len() > 0 and pos.first().func() == table.header
|
||
let header-cells = if has-header { pos.first().children } else { () }
|
||
let tail = pz-flatten-cells(if has-header { pos.slice(1) } else { pos })
|
||
let start-page = state("pz-table-start-page", none)
|
||
figure(
|
||
kind: table,
|
||
{
|
||
show table: set text(hyphenate: true, size: 9pt)
|
||
show table.cell: cell => {
|
||
show regex("[a-z0-9][A-Z]"): m => {
|
||
m.text.first() + sym.zws + m.text.last()
|
||
}
|
||
show regex("[_]"): it => it + sym.zws
|
||
cell
|
||
}
|
||
context {
|
||
let fig-loc = here()
|
||
let tab-num = counter(figure.where(kind: table)).at(fig-loc).first()
|
||
start-page.update(none)
|
||
pz-table-inner(columns, header-cells, tab-num, start-page, ..arguments(..tail))
|
||
}
|
||
},
|
||
caption: caption,
|
||
)
|
||
}
|
||
|
||
#let pz-fig(path, caption, lbl) = [
|
||
#figure(
|
||
image(
|
||
"../images/" + path,
|
||
width: 100%,
|
||
fit: "contain",
|
||
),
|
||
caption: caption,
|
||
)
|
||
#label(lbl)
|
||
]
|