summaryrefslogtreecommitdiff
path: root/conf.typ
diff options
context:
space:
mode:
Diffstat (limited to 'conf.typ')
-rw-r--r--conf.typ788
1 files changed, 539 insertions, 249 deletions
diff --git a/conf.typ b/conf.typ
index fa97841..eb03436 100644
--- a/conf.typ
+++ b/conf.typ
@@ -1,265 +1,555 @@
-#let header() = {
- set align(center)
- set text(font: "Cambria")
- text("МИНОБРНАУКИ РОССИИ\nФедеральное государственное бюджетное образовательное учреждение\nвысшего образования\n")
- v(0.2em)
- text(weight: "bold", "«САРАТОВСКИЙ НАЦИОНАЛЬНЫЙ ИССЛЕДОВАТЕЛЬСКИЙ
- ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ
- ИМЕНИ Н. Г. ЧЕРНЫШЕВСКОГО»\n")
- set align(left)
-}
+/*
+ * Примечания:
+ * - Код разбит по модулям, каждый из которых объявляется и инициализируется
+ * в переменной modules. Методы именуются в змеином стиле, при этом
+ * приватные начинаются с подчёркивания.
+ *
+ * В силу отсутствия адекватной реализации приватных методов в Typst
+ * приватность --- лишь условное соглашение: такие методы не предназначены
+ * для вызова извне, хотя фактически такая возможность имеется.
+ * Не делайте так!
+ *
+ * - Чтобы не засорять кодовую базу, строковые константы вынесены в
+ * переменную strings.
+ */
-#let signature(post, name) = {
- grid(
- columns: (1fr,) * 3,
- align: (left, center, right),
- row-gutter: 5pt,
- post, block(inset: (y: 13pt), line(length: 3cm, stroke: .4pt)), name
- )
-}
-#let get_student_word(sex) = {
- if sex == "male" {
- return "студента"
- } else if sex == "female" {
- return "студентки"
- } else if sex == "plural" {
- return "студентов"
- }
-}
-#let get_speciality(group) = {
- let specialities = (
- [02.03.02 --- Фундаментальная информатика и информационные технологии],
- [09.03.01 --- Информатика и вычислительная техника],
- [10.05.01 --- Компьютерная безопасность],
- [02.03.03 --- Математическое обеспечение и администрирование информационных систем],
- [09.03.04 --- Программная инженерия],
- [], // x6x
- [09.04.01 --- Информатика и вычислительная техника],
- [02.04.03 --- Математическое обеспечение и администрирование информационных систем],
- [], // x8x
- )
- if group.at(1) == "7" { // x7x
- if group.at(2) == "1" { // x71
- return specialities.at(6) // ивт
- } else if group.at(2) == "3" { // x73
- return specialities.at(7) // моаис
- }
- return []
- }
- let id = int(group.at(1)) - 1;
-
- if id < 0 or id > 8 {
- return []
- }
- return specialities.at(id)
-}
-
-#let default_title(type, info) = {
- let author = info.at("author", default: (:))
- set align(center)
- v(3cm)
- if type == "autoref" {
- info.title = [АВТОРЕФЕРАТ]
- }
- if type == "nir" {
- info.title = [ОТЧЁТ О НАУЧНО-ИССЛЕДОВАТЕЛЬСКОЙ РАБОТЕ]
- }
- text(weight: "bold", upper(info.at("title", default: [Тема работы])))
- let worktypes = (
- referat: [РЕФЕРАТ],
- coursework: [КУРСОВАЯ РАБОТА],
- diploma: [Выпускная квалификационная работа],
- autoref: [работа],
- nir: [работа],
- pract: [Отчёт о практике]
- )
- par(worktypes.at(type, default: []))
- v(1.5cm)
- set align(left)
- let author_string = get_student_word(author.at("sex", default: "male"))
- author_string = author_string + " " + author.group.at(0) + " курса " + author.group + " группы\n"
- if author.faculty == [КНиИТ] {
- author_string = author_string + "направления " + get_speciality(author.group) + "\n"
- } else if author.at("speciality", default: []) != [] {
- author_string = author_string + "направления " + author.speciality + "\n"
- }
-
- text(author_string + "факультета " + author.faculty + "\n" + author.name)
- v(1fr)
- text("Проверено:\n")
- signature(info.inspector.degree, info.inspector.name)
-}
-
-#let otchet_title(info) = {
- let author = info.at("author", default: (:))
- set align(center)
-}
-
-#let make_title(
- type: str,
- info: ()
-) = {
- set page(
- paper: "a4",
- margin: (
- top: 2cm,
- bottom: 2cm,
- left: 2.5cm,
- right: 1.5cm
- )
- )
-
- header()
- default_title(type, info)
-
- v(1fr)
- set align(center)
- text("Саратов 2024")
-}
-
-#let make_toc(
- info: ()
-) = {
+#let strings = (
+ title: (
+ minobrnauki: "МИНОБРНАУКИ РОССИИ\nФедеральное государственное бюджетное образовательное учреждение\nвысшего образования\n",
+ sgu: "«САРАТОВСКИЙ НАЦИОНАЛЬНЫЙ ИССЛЕДОВАТЕЛЬСКИЙ
+ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ
+ИМЕНИ Н. Г. ЧЕРНЫШЕВСКОГО»\n",
+ city: "Саратов",
+ worktypes: (
+ referat: [РЕФЕРАТ],
+ coursework: [КУРСОВАЯ РАБОТА],
+ diploma: [Выпускная квалификационная работа],
+ autoref: [работа],
+ nir: [работа],
+ pract: [Отчёт о практике]
+ ),
+ from_course: "курса",
+ from_group: "группы",
+ from_speciality: "направления"
+ ),
+ student: (
+ male: "студента",
+ female: "студентки",
+ plural: "студентов",
+ ),
+ caps_headings: (
+ [Содержание],
+ [Введение],
+ [Заключение],
+ [Список использованных источников],
+ [Определения, обозначения и сокращения],
+ [Обозначения и сокращения]
+ ),
+ specialities: (
+ spec_kb: [10.05.01 --- Компьютерная безопасность],
+ bac_ivt: [09.03.01 --- Информатика и вычислительная техника],
+ bac_moais: [02.03.03 --- Математическое обеспечение и администрирование информационных систем],
+ bac_fiit: [02.03.02 --- Фундаментальная информатика и информационные технологии],
+ bac_pi: [09.03.04 --- Программная инженерия],
+ mag_ivt: [09.04.01 --- Информатика и вычислительная техника],
+ mag_moais: [02.04.03 --- Математическое обеспечение и администрирование информационных систем],
+ ),
+ error: (
+ no_sex: [*6.21 КоАП РФ*],
+ undefined_spec: [*НЕИЗВЕСТНАЯ СПЕЦИАЛЬНОСТЬ*]
+ )
+)
+#let indent = 1cm
+#let styled = [#set text(red)].func()
+#let space = [ ].func()
+#let sequence = [].func()
- outline(indent: 2%, title: [Содержание])
- pagebreak(weak: true)
-}
-#let aboba(settings: ()) = {
-
-}
+#let modules = (
+ /*
+ * Модуль информации об авторе
+ * Позволяет получать более полную и отформатированную
+ * информацию об авторе: студент/студентки/студентов (get_author_sex),
+ * номер курса, группы и так далее.
+ * Конечно, некоторые из этих вещей в открытом виде
+ * лежат в словаре author, но некоторые данные
+ * нуждаются в постобработке или вовсе получаются
+ * косвенным путём. Необходимость в постобработке
+ * может возникнуть и позднее, поэтому настоятельно
+ * рекомендуется пользоваться именно этими методами, а
+ * не получать код напрямую.
+ */
+ author_info: (
+ /*
+ * Определяет студенческо-половую сущность автора.
+ * Принимает:
+ * - author - словарь про автора
+ * Возвращает:
+ * - В зависимости от пола: "студента", "студентки", "студентов"
+ */
+ get_author_sex:
+ (author) => {
+ return strings.student.at(
+ author.at("sex"),
+ default: strings.error.no_sex
+ )
+ },
+ /*
+ * Определяет курс автора (по номеру группы)
+ * Принимает:
+ * - author - словарь про автора
+ * Возвращает:
+ * - Номер курса в строковом формате
+ */
+ get_author_course:
+ (author) => {
+ return author.group.at(0)
+ },
+ /*
+ * Определяет группу автора
+ * Принимает:
+ * - author - словарь про автора
+ * Возвращает:
+ * - Номер группы в строковом формате
+ * Примечание: на текущий момент функция
+ * ничего не делает, но, возможно, это
+ * не навсегда и в будущем здесь что-то
+ * может появиться. Чтобы потом не мучиться
+ * с рефакторингом, лучше получать номер группы
+ * через неё.
+ */
+ get_author_group:
+ (author) => {
+ return author.group
+ },
+ /*
+ * Определяет код и название направления автора
+ * Принимает:
+ * - author - словарь про автора
+ * Возвращает:
+ * - author.speciality, если таковой указан
+ * - В противном случае, если студент учится на КНиИТе,
+ * для студента работает аттракцион невиданной
+ * технологичности: код и название специальности
+ * определяются автоматически
+ */
+ get_speciality:
+ (author) => {
+ if author.at("speciality", default: none) != none {
+ return author.speciality
+ } else if author.faculty == [КНиИТ] {
+ let specialities = (
+ strings.specialities.bac_fiit,
+ strings.specialities.bac_ivt,
+ strings.specialities.spec_kb,
+ strings.specialities.bac_moais,
+ strings.specialities.bac_pi,
+ [], // x6x
+ strings.specialities.mag_ivt,
+ strings.specialities.mag_moais,
+ [], // x8x
+ )
+ if author.group.at(1) == "7" { // x7x
+ if author.group.at(2) == "1" { // x71
+ return specialities.at(6) // ивт
+ } else if author.group.at(2) == "3" { // x73
+ return specialities.at(7) // моаис
+ }
+ return []
+ }
+ let id = int(author.group.at(1)) - 1;
+
+ if id < 0 or id > 8 {
+ return []
+ }
+ return specialities.at(id)
+ }
+ return strings.error.undefined_spec
+ }
+ ),
-#let conf(
- title: none,
- info: (),
- type: "referat",
- settings: (),
- doc
-) = {
- info.title = title
- settings.title_page = settings.at("title_page", default: (:))
+ /*
+ * Модуль титульного листа
+ * Здесь происходит генерация титульного листа и
+ * определяются все необходимые для этого методы.
+ * Поскольку его создание --- задача не такая уж
+ * и тривиальная, здесь есть много приватных методов
+ * и ваш покорный слуга ещё раз напоминает о
+ * нежелательности их вызова извне. Если такая необходимость
+ * всё же возникает, лучше переименовать метод и убрать
+ * подчёркивание: впоследствии это можно будет хотя бы как-то
+ * отладить.
+ *
+ * Изначально задумывалось, что единственный доступный
+ * для вызова извне метод --- make, а в остальных не
+ * должно возникнуть необходимости.
+ */
+ title: (
+ /*
+ * Отвечает за вывод названия министерства и
+ * университета на титульном листе
+ */
+ _default_header:
+ () => {
+ set align(center)
+ set text(font: "Tempora")
+ text(strings.title.minobrnauki)
+ v(0.2em)
+ text(weight: "bold", strings.title.sgu)
+ set align(left)
+ },
+ /*
+ * Отвечает за вывод тела титульного листа:
+ * заголовок (название работы, если не определено иное),
+ * тип работы, информация об авторе
+ *
+ * Информация о проверяющем преподавателе
+ * генерируется не здесь по историческим причинам.
+ */
+ _default_body:
+ (data) => {
+ set align(center)
+ v(3cm)
+ text(weight: "bold", upper(data.title))
+ par(data.worktype)
+ v(1.5cm)
+ set align(left)
+ text(data.group + "\n")
+ text(data.speciality + "\n")
+ text(data.faculty + "\n" + data.author)
+ },
+ /*
+ * Отвечает за вывод города и года на титульном листе
+ */
+ _default_footer:
+ () => {
+ v(1fr)
+ set align(center)
+ text(strings.title.city + " " + str(datetime.today().year()))
+ },
+ /*
+ * Подпись "Проверено:" для титульного листа
+ */
+ _signature:
+ (post, name) => {
+ text("Проверено:\n")
+ grid(
+ columns: (1fr,) * 3,
+ align: (left, center, right),
+ row-gutter: 5pt,
+ post, block(inset: (y: 13pt), line(length: 3cm, stroke: .4pt)), name
+ )
+ },
+ /*
+ * Строка "студента такой-то группы" для титульного листа
+ */
+ _get_author_string:
+ (self, author) => {
+ let sex = (self.author_info.get_author_sex)(author)
+ let course = (self.author_info.get_author_course)(author)
+ let group = (self.author_info.get_author_group)(author)
+ if course.len() > 0 {
+ course = (self.utils.strglue)(course, strings.title.from_course)
+ }
+ if group.len() > 0 {
+ group = (self.utils.strglue)(group, strings.title.from_group)
+ }
+ let result = (self.utils.strglue)(sex, course, group)
+ return result
+ },
+ /*
+ * Получает заголовок титульного листа.
+ * Принимает:
+ * - info - информация о документе
+ * Возвращает:
+ * - В зависимости от типа:
+ * - Тему работы, если это не автореферат и не отчёт по НИРу
+ * - В противном случае названия соответствующих типов работ
+ */
+ _get_title_string:
+ (info) => {
+ if info.type == "autoref" {
+ return [АВТОРЕФЕРАТ]
+ }
+ if info.type == "nir" {
+ return [ОТЧЁТ О НАУЧНО-ИССЛЕДОВАТЕЛЬСКОЙ РАБОТЕ]
+ }
+ return info.at("title", default: [Тема работы])
+ },
+ /*
+ * Генерирует строки текста для вывода на титульном листе
+ * Принимает:
+ * - info - информация о документе
+ * Возвращает:
+ * - Словарь:
+ * title: Заголовок
+ * worktype: Тип работы
+ * group: студент(а|ки|ов) s курса sex группы
+ * specialty: направления 69.14.88 --- Специальность
+ * faculty: факультета XXX
+ * author: автор(ы)? работы
+ */
+ _get_strings:
+ (self, info) => {
+ let author = info.at("author", default: (:))
+ let title_string = (self.title._get_title_string)(info)
+ let worktype = strings.title.worktypes.at(info.type, default: [])
+ let group_string = (self.title._get_author_string)(self, author)
+ let speciality_string = (self.utils.strglue)(
+ strings.title.from_speciality,
+ (self.author_info.get_speciality)(author)
+ )
+ let faculty_string = "факультета " + author.faculty
+ return (
+ title: title_string,
+ worktype: worktype,
+ group: group_string,
+ speciality: speciality_string,
+ faculty: faculty_string,
+ author: author.name
+ )
+ },
+ /*
+ * Генерирует титульный лист
+ * Принимает:
+ * - info - информация о документе
+ */
+ make:
+ (self, info) => {
+ let strs = (self.title._get_strings)(self, info)
+ (self.title._default_header)()
+ (self.title._default_body)(strs)
+ v(1fr)
+ (self.title._signature)(info.inspector.degree, info.inspector.name)
+ (self.title._default_footer)()
+ },
+ ),
- set text(
- size: 14pt
- )
+ /*
+ * Модуль генерации документа
+ * Здесь содержатся методы, влияющие на вид всего
+ * документа в целом. Главный из них --- make ---
+ * вызывается из точки входа в стилевой файл и
+ * отвечает за всё оформление выходного документа.
+ */
+ document: (
+ apply_heading_styles:
+ (it) => {
+ set text(size: 14pt)
+ if it.depth == 1 {
+ pagebreak(weak: true)
+ v(4.3pt * (3 + 1 - 0.2))
+ }
+ if strings.caps_headings.contains(it.body) {
+ set align(center)
+ //counter(heading).update(i => i - 1)
+ upper(it.body)
+ } else {
+ it
+ }
+ v(4.3pt * (0.4 + 0.2))
+ },
+ /*
+ * Генерирует страницу содержания
+ * Принимает:
+ * - info - информация о документе
+ */
+ make_toc:
+ (
+ info: ()
+ ) => {
+ show outline.entry.where(
+ level: 1
+ ): it => {
+ let heading = it.at("element", default: (:)).at("body", default: "")
+ if not strings.caps_headings.contains(heading) {
+ it
+ return
+ }
+ grid(
+ columns: (auto,1pt, 1fr,1pt, auto),
+ align: (left, center, right),
+ row-gutter: 0pt,
+ rows: (auto),
+ inset: 0pt,
+ heading, none, it.fill, none, it.page
+ )
+ v(-28pt) // TODO разобраться, откуда такой отступ после grid
+ }
+ outline(indent: 2%, title: [Содержание])
+ },
+ /*
+ * Генерирует весь документ
+ * Принимает:
+ * - info - информация о документе
+ * - doc - содержимое документа
+ */
+ make:
+ (
+ self,
+ info: (),
+ settings,
+ doc
+ ) => {
+ set page(
+ paper: "a4",
+ margin: (
+ top: 2cm,
+ bottom: 2cm,
+ left: 2.5cm,
+ right: 1.5cm
+ )
+ )
+ set text(
+ size: 14pt
+ )
- if settings.title_page.at("enabled", default: true) {
- make_title(type: type, info: info)
- }
- set align(left)
+ if settings.title_page.at("enabled", default: true) {
+ (self.title.make)(self, info)
+ }
+ set align(left)
- set page(footer: context [
- #h(1fr)
- #counter(page).display(
- "1"
- )
- ])
-
-
- let caps_headings = (
- [Содержание],
- [Введение],
- [Заключение],
- [Список использованных источников],
- [Определения, обозначения и сокращения],
- [Обозначения и сокращения]
- )
- show heading: it => {
- set text(size: 14pt)
- if it.depth == 1 {
- pagebreak(weak: true)
- v(4.3pt * (3 + 1 - 0.2))
- }
- if caps_headings.contains(it.body) {
- set align(center)
- counter(heading).update(i => i - 1)
- upper(it.body)
- } else {
- it
- }
- v(4.3pt * (0.4 + 0.2))
- }
- set heading(numbering: "1.1")
- set page(numbering: "1")
- // Отступ начала абазаца 1.25 см и выравнивание по ширине
- set par(
- justify: true
- )
- if settings.contents_page.enabled {
- make_toc(info: info)
- }
- set math.equation(numbering: "(1)", supplement: [])
- set figure(supplement: "Рис.")
- doc
+
+ show heading: self.document.apply_heading_styles
+
+ // Отступ начала абазаца 1.25 см и выравнивание по ширине
+ set par(
+ justify: true
+ )
- /*let count = authors.len()
- let ncols = calc.min(count, 3)
- grid(
- columns: (1fr,) * ncols,
- row-gutter: 24pt,
- ..authors.map(author => [
- #author.name \
- #author.affiliation \
- #link("mailto:" + author.email)
- ]),
- )
+ // Вывод содержания
+ if settings.contents_page.enabled {
+ (self.document.make_toc)(info: info)
+ }
- par(justify: false)[
- *Abstract* \
- #abstract
- ]*/
-}
+ // Оформление элементов содержимого документа
+ set heading(numbering: "1.1")
+ set page(footer: context [
+ #h(1fr)
+ #counter(page).display(
+ "1"
+ )
+ ])
+ set page(numbering: "1")
+ set math.equation(numbering: "(1)", supplement: [])
+ set figure(supplement: "Рис.")
+ set quote(block: true)
+ // Вывод самого документа
+ (self.document.first_line_indentation)(self, doc)
+ },
+ /*
+ * Подставляет красные строки в начала абзацев
+ */
+ first_line_indentation:
+ (
+ self,
+ doc,
+ last-is-heading: false, // space and parbreak are ignored
+ indent-already-added: false,
+ ) => {
+ for (i, elem) in doc.children.enumerate() {
+ let element = elem.func()
+ if element == text {
+ let previous-elem = doc.children.at(i - 1)
+ if i == 0 or last-is-heading or previous-elem.func() == parbreak {
+ if not indent-already-added {
+ indent-already-added = true
+ h(indent)
+ }
+ }
+ elem
+ } else if element == heading {
+ indent-already-added = false
+ last-is-heading = true
+ elem
+ } else if element == space {
+ elem
+ } else if element == parbreak {
+ indent-already-added = false
+ elem
+ } else if element == sequence {
+ (self.document.first_line_indentation)(
+ self,
+ elem,
+ last-is-heading: last-is-heading,
+ indent-already-added: indent-already-added,
+ )
+ } else if element == styled {
+ styled((self.document.first_line_indentation)(
+ self,
+ elem.child,
+ last-is-heading: last-is-heading,
+ indent-already-added: indent-already-added,
+ ), elem.styles)
+ } else {
+ indent-already-added = false
+ last-is-heading = false
+ elem
+ }
+ }
+ }
+ ),
-#let indent = 1cm
-#let styled = [#set text(red)].func()
-#let space = [ ].func()
-#let sequence = [].func()
+ /*
+ * Помощник
+ * Модуль-помощник не содержит особой
+ * функциональности и не имеет конкретного
+ * назначения, но содержащиеся в нём методы
+ * могут быть полезны где угодно и не
+ * привязаны к конкретной части документа
+ */
+ utils: (
+ /*
+ * Склеивает строки, игнорируя пустые
+ * Принимает:
+ * - divider - разделитель (по умолчанию пробел)
+ * - соединяемые строки в неограниченном количестве
+ * Примечание: метод похож по своей сути
+ * на join, но отличается от него наличием проверки
+ * на пустые строки: рядом с ними разделитель не
+ * ставится, что исключает присутствие двух разделителей
+ * подряд.
+ */
+ strglue:
+ (
+ divider: " ",
+ ..strings
+ ) => {
+ let result = ""
+ for string in strings.pos() {
+ if(
+ (result.len() > 0)
+ and
+ ((type(string) != str) or (string.len() > 0))
+ ) {
+ result = result + " "
+ }
+ // TODO: string было бы неплохо обернуть
+ // в str(), но баг в системе типов Typst
+ // не позволяет этого сделать
+ result = result + string
+ }
+ return result
+ }
+ )
+)
-#let turn-on-first-line-indentation(
- doc,
- last-is-heading: false, // space and parbreak are ignored
- indent-already-added: false,
+/*
+ * Точка входа, просто вызывает modules.document.make
+ */
+#let conf(
+ title: none,
+ info: (),
+ type: "referat",
+ settings: (),
+ doc
) = {
- for (i, elem) in doc.children.enumerate() {
- let element = elem.func()
- if element == text {
- let previous-elem = doc.children.at(i - 1)
- if i == 0 or last-is-heading or previous-elem.func() == parbreak {
- if not indent-already-added {
- indent-already-added = true
- h(indent)
- }
- }
- elem
- } else if element == heading {
- indent-already-added = false
- last-is-heading = true
- elem
- } else if element == space {
- elem
- } else if element == parbreak {
- indent-already-added = false
- elem
- } else if element == sequence {
- turn-on-first-line-indentation(
- elem,
- last-is-heading: last-is-heading,
- indent-already-added: indent-already-added,
- )
- } else if element == styled {
- styled(turn-on-first-line-indentation(
- elem.child,
- last-is-heading: last-is-heading,
- indent-already-added: indent-already-added,
- ), elem.styles)
- } else {
- indent-already-added = false
- last-is-heading = false
- elem
- }
- }
-}
+ info.title = title
+ info.type = type
+ settings.title_page = settings.at("title_page", default: (:))
+ (modules.document.make)(modules, info: info, settings, doc)
+} \ No newline at end of file