foundation.typ 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. // ==========================================
  2. // PRESENTATION: ОБЩАЯ ОСНОВА ВЁРСТКИ
  3. // ==========================================
  4. #let design-tokens(company, overrides: (:)) = {
  5. let defaults = (
  6. brand-color: company.brand.at("color", default: rgb("e39f49")),
  7. text-font: "Times New Roman",
  8. sans-font: "Arial",
  9. text-size: 12pt,
  10. small-size: 8pt,
  11. line-height: 1.5em,
  12. compact-line-height: 0.65em,
  13. paper: "a4",
  14. page-margin: (left: 3cm, right: 1.5cm, top: 2cm, bottom: 2cm),
  15. )
  16. let result = defaults
  17. for (key, value) in overrides {
  18. if key not in defaults {
  19. panic("Scientia / design-tokens: неизвестный token " + repr(key))
  20. }
  21. result.insert(key, value)
  22. }
  23. result
  24. }
  25. #let watermark-layer(value, font: "Arial") = {
  26. if value == none {
  27. none
  28. } else {
  29. [
  30. #place(center + horizon, dx: -4.8cm, dy: -8.5cm)[
  31. #rotate(35deg)[#text(font: font, size: 40pt, weight: "bold", fill: rgb("e8c9c9").transparentize(65%))[#value]]
  32. ]
  33. #place(center + horizon)[
  34. #rotate(35deg)[#text(font: font, size: 40pt, weight: "bold", fill: rgb("e8c9c9").transparentize(65%))[#value]]
  35. ]
  36. #place(center + horizon, dx: 4.8cm, dy: 8.5cm)[
  37. #rotate(35deg)[#text(font: font, size: 40pt, weight: "bold", fill: rgb("e8c9c9").transparentize(65%))[#value]]
  38. ]
  39. ]
  40. }
  41. }
  42. #let render-media-slot(
  43. resource,
  44. policy: "placeholder",
  45. width: 4cm,
  46. height: 2cm,
  47. label: "РЕСУРС НЕ ЗАДАН",
  48. fit: "contain",
  49. ) = {
  50. if policy not in ("hide", "placeholder", "reserve-space") {
  51. panic("Scientia / media-slot: неизвестная политика " + repr(policy))
  52. }
  53. if resource == none {
  54. if policy == "hide" {
  55. none
  56. } else if policy == "reserve-space" {
  57. box(width: width, height: height)
  58. } else {
  59. box(
  60. width: width,
  61. height: height,
  62. stroke: 0.6pt + luma(150),
  63. fill: luma(245),
  64. inset: 4pt,
  65. align(center + horizon)[
  66. #text(font: "Arial", size: 7pt, fill: luma(110), weight: "bold")[#label]
  67. ],
  68. )
  69. }
  70. } else if type(resource) == path {
  71. image(resource, width: width, height: height, fit: fit)
  72. } else if type(resource) == content {
  73. box(width: width, height: height, align(center + horizon, resource))
  74. } else {
  75. panic("Scientia / media-slot: ожидались none, path или content")
  76. }
  77. }
  78. #let apply-foundation(
  79. body,
  80. ctx: none,
  81. tokens: (:),
  82. page-options: (:),
  83. text-options: (:),
  84. par-options: (:),
  85. ) = {
  86. if ctx == none {
  87. panic("Scientia / foundation: document context обязателен")
  88. }
  89. let tokens = design-tokens(ctx.company, overrides: tokens)
  90. let page-defaults = (
  91. paper: tokens.paper,
  92. margin: tokens.at("page-margin"),
  93. foreground: watermark-layer(ctx.options.watermark, font: tokens.at("sans-font")),
  94. )
  95. let resolved-page = page-defaults
  96. for (key, value) in page-options { resolved-page.insert(key, value) }
  97. set page(..resolved-page)
  98. set text(
  99. font: tokens.at("text-font"),
  100. size: tokens.at("text-size"),
  101. lang: "ru",
  102. ..text-options,
  103. )
  104. set par(
  105. justify: true,
  106. first-line-indent: (amount: 1.25cm, all: true),
  107. leading: tokens.at("line-height"),
  108. ..par-options,
  109. )
  110. show table.cell: set par(first-line-indent: 0pt)
  111. show figure.caption.where(kind: table): set par(first-line-indent: 0pt)
  112. body
  113. }