title-page.typ 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. // Титульный лист практической работы (СФУ).
  2. // Экспортирует функцию title-page(number) — вызывается из main.typ перед
  3. // каждой работой: #title-page(2), #title-page(3), ... Тема подставляется
  4. // из словаря topics ниже. Данные студента/преподавателя — в словаре task.
  5. // Подключается первой страницей в main.typ.
  6. #let topics = (
  7. "2": "Проектирование трассы",
  8. "3": "Конструирование тоннельных обделок сводчатого очертания",
  9. "6": "Расчёт параметров буровзрывных работ",
  10. "7": "Расчёт элементов проходческих работ",
  11. "8": "Расчёт временной крепи тоннеля",
  12. )
  13. #let title-page(work-number) = {
  14. let topic = topics.at(str(work-number), default: none)
  15. if topic == none {
  16. panic("Нет темы для практической работы №" + str(work-number) + " — добавьте её в словарь topics")
  17. }
  18. let task = (
  19. ministry: "Министерство науки и высшего образования РФ",
  20. university_prename: "Федеральное государственное автономное\nобразовательное учреждение высшего образования\n",
  21. university_shorten: "«СИБИРСКИЙ ФЕДЕРАЛЬНЫЙ УНИВЕРСИТЕТ»",
  22. institute: "Институт цветных металлов",
  23. department: "Шахтное и подземное строительство",
  24. work_type: "ПРАКТИЧЕСКАЯ РАБОТА №" + str(work-number),
  25. topic: topic,
  26. teacher: "Г. С. Курчин",
  27. group: "ГГ21-10",
  28. student_short_name: "А.С. Ткаченко",
  29. city: "Красноярск",
  30. year: "2026 г.",
  31. )
  32. // ── Страница ───────────────────────────────────────────────────────────
  33. // Поля симметричные: центр текстовой колонки совпадает с центром листа,
  34. // поэтому для центрирования достаточно обычного align(center).
  35. [
  36. #set page(paper: "a4", margin: (x: 20mm, y: 20mm), header: none, footer: none, numbering: none, foreground: none)
  37. #set text(font: "Times New Roman", size: 14pt, lang: "ru")
  38. #set par(justify: false, first-line-indent: 0pt, leading: 0.65em)
  39. // ── Поле: значение на черте + подпись под чертой ───────────────────────
  40. // Одна и та же геометрия у всех полей листа: черта значения — underline
  41. // на базовой линии текста, пустая черта — line на той же высоте
  42. // (blank-line-dy). Оба варианта лежат в слоте фиксированной высоты,
  43. // поэтому подписи всегда на одинаковом расстоянии от своих черт, а
  44. // черты соседних полей — на одной горизонтали.
  45. // value = none → пустая черта (для подписи), требуется width.
  46. #let label-size = 10pt
  47. #let field-stroke = 0.7pt + black
  48. #let slot-height = 16pt // высота слота со значением
  49. #let blank-line-dy = 11.5pt // уровень пустой черты = уровень underline
  50. #let label-gap = 2.8mm // зазор от черты до подписи
  51. #let label-lift = 10.5pt // компенсация верхнего интервала строки подписи
  52. // ── Личная подпись студента ─────────────────────────────────────────────
  53. // Картинка повёрнута на 10° по часовой, фон прозрачный.
  54. // Путь от корня проекта (как у остальных include в main.typ);
  55. // отдельная компиляция: typst compile --root . chapters/title-page.typ
  56. #let student-signature = "/assets/images/signature.png"
  57. #let signature-height = 13mm // высота картинки подписи
  58. #let signature-gap = -1mm // зазор до черты; < 0 — подпись заходит на черту
  59. #let signature-dx = 0mm // ручная подгонка: + вправо, − влево
  60. #let signature-dy = 2mm // ручная подгонка: + вниз, − вверх
  61. // Единый помощник. Одна и та же геометрия у всех полей листа:
  62. // черты соседних полей всегда лежат на одной горизонтали, подпись —
  63. // всегда под чертой на одинаковом расстоянии.
  64. // value = none → пустая черта (для подписи), требуется width;
  65. // signature → картинка подписи над пустой чертой.
  66. #let field(value, label, width: 100%, signature: none) = box(width: width)[
  67. #grid(
  68. columns: 100%,
  69. inset: 0pt,
  70. align: center + top,
  71. rows: (0pt, slot-height, auto),
  72. // строка нулевой высоты: подпись висит над чертой, ничего не сдвигая
  73. [
  74. #if signature != none [
  75. #move(
  76. dx: signature-dx,
  77. dy: blank-line-dy - signature-gap - signature-height + signature-dy,
  78. )[
  79. #align(center)[#image(signature, height: signature-height)]
  80. ]
  81. ]
  82. ],
  83. // черта со значением (или пустая черта для подписи)
  84. [
  85. #if value == none [
  86. #move(dy: blank-line-dy)[#line(length: 100%, stroke: field-stroke)]
  87. ] else [
  88. #underline(offset: 2pt, stroke: field-stroke)[#value]
  89. ]
  90. ],
  91. // подпись под чертой
  92. [
  93. #v(label-gap + slot-height - blank-line-dy - label-lift)
  94. #text(size: label-size)[#label]
  95. ],
  96. )
  97. ]
  98. // ── Шапка ──────────────────────────────────────────────────────────────
  99. #align(center)[
  100. #text(size: 14pt)[#task.ministry]
  101. #linebreak()
  102. #text(size: 12pt)[#task.university_prename]
  103. *#task.university_shorten*
  104. ]
  105. #v(5mm)
  106. // ── Институт ───────────────────────────────────────────────────────────
  107. #align(center)[#field(task.institute, [институт], width: 135mm)]
  108. #v(4mm)
  109. // ── Кафедра ────────────────────────────────────────────────────────────
  110. #align(center)[#field(task.department, [кафедра], width: 145mm)]
  111. #v(25mm)
  112. // ── Тип работы ─────────────────────────────────────────────────────────
  113. #align(center)[#text(size: 16pt, weight: "bold")[#task.work_type]]
  114. #v(0mm)
  115. // ── Тема ───────────────────────────────────────────────────────────────
  116. #align(center)[
  117. #grid(
  118. columns: 100%,
  119. inset: 0pt,
  120. align: center + top,
  121. [
  122. #underline(offset: 2pt, stroke: field-stroke)[#task.topic]
  123. ],
  124. [
  125. #v(label-gap + 2pt)
  126. #text(size: label-size)[тема]
  127. ],
  128. )
  129. ]
  130. #v(45mm)
  131. // ── Подписи ────────────────────────────────────────────────────────────
  132. // Колонки: роль | группа | зазор | подпись | зазор | ФИО.
  133. // Черты для подписи и подчёркнутые значения в одной строке таблицы
  134. // находятся на одном уровне благодаря общей геометрии field().
  135. // Колонка подписи 33 мм — вмещает картинку подписи целиком
  136. // (ячейки таблицы обрезают содержимое по своей ширине).
  137. #table(
  138. columns: (34mm, 49mm, 4mm, 33mm, 4mm, 46mm),
  139. stroke: none,
  140. inset: (x: 0pt, y: 7mm),
  141. align: (left, center, center, center, center, center),
  142. table.cell(colspan: 2)[Преподаватель],
  143. [],
  144. field(none, [подпись, дата], width: 33mm),
  145. [],
  146. field(task.teacher, [инициалы, фамилия]),
  147. [Студент],
  148. field(task.group, [номер группы, зачетной книжки]),
  149. [],
  150. field(none, [подпись, дата], width: 33mm, signature: student-signature),
  151. [],
  152. field(task.student_short_name, [инициалы, фамилия]),
  153. )
  154. // ── Город, год — по центру листа, у нижнего поля ───────────────────────
  155. #place(bottom + center)[#task.city #task.year]
  156. ]
  157. }