letter.typ 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. // ==========================================
  2. // PROFILE: ДЕЛОВОЕ ПИСЬМО
  3. // ==========================================
  4. #import "../../domain/document.typ": document-profile, fail
  5. #import "../../domain/attachments.typ": attachment-set
  6. #import "../../domain/parties.typ": signer
  7. #import "../foundation.typ": apply-foundation
  8. #import "../components.typ": attachment-list, appendix-heading, company-footer, letter-header, signature-block
  9. #let normalize-letter(metadata) = {
  10. if type(metadata.attachments) == array {
  11. metadata.insert("attachments", attachment-set(items: metadata.attachments))
  12. }
  13. metadata
  14. }
  15. #let validate-letter(metadata) = {
  16. if metadata.recipient == none or type(metadata.recipient) != dictionary or metadata.recipient.at("kind", default: none) != "recipient" {
  17. fail("letter.recipient", "ожидался recipient")
  18. }
  19. if metadata.title != none and type(metadata.title) not in (str, content) {
  20. fail("letter.title", "ожидались none, str или content")
  21. }
  22. if metadata.signer != none and (type(metadata.signer) != dictionary or metadata.signer.at("kind", default: none) != "signer") {
  23. fail("letter.signer", "ожидались none или signer")
  24. }
  25. if type(metadata.attachments) != dictionary or metadata.attachments.at("kind", default: none) != "attachment-set" {
  26. fail("letter.attachments", "ожидался attachment-set")
  27. }
  28. metadata
  29. }
  30. #let effective-signer(data, company, include-media: true) = {
  31. let source = if data.signer == none {
  32. signer(
  33. company.director.name,
  34. company.director.title,
  35. signature: company.resources.signature,
  36. stamp: company.resources.stamp,
  37. )
  38. } else {
  39. data.signer
  40. }
  41. signer(
  42. source.name,
  43. source.title,
  44. basis: source.basis,
  45. signature: if include-media { source.signature } else { none },
  46. stamp: if include-media { source.stamp } else { none },
  47. )
  48. }
  49. #let render-letter-attachments(attachments, appendix-state) = {
  50. for (index, item) in attachments.items.enumerate() {
  51. pagebreak()
  52. appendix-state.update(true)
  53. let number = if item.number == auto { index + attachments.start } else { item.number }
  54. appendix-heading(item, str(number))
  55. v(0.5cm)
  56. if type(item.body) == function { (item.body)() } else { item.body }
  57. }
  58. }
  59. #let render-letter(body, ctx) = {
  60. let company = ctx.company
  61. let data = ctx.metadata
  62. let include-media = ctx.options.mode == "final"
  63. let person = effective-signer(data, company, include-media: include-media)
  64. let media-policy = if include-media { ctx.options.at("media-policy") } else { "reserve-space" }
  65. let appendix-state = state("scientia-letter-in-appendix", false)
  66. let footer = context {
  67. if not appendix-state.get() { company-footer(company) }
  68. }
  69. let composed = [
  70. #letter-header(
  71. company,
  72. data.recipient,
  73. date: data.date,
  74. reference: data.reference,
  75. title: data.title,
  76. media_policy: ctx.options.at("media-policy"),
  77. )
  78. #v(0.7cm)
  79. #body
  80. #if data.attachments.items.len() > 0 {
  81. v(0.8cm)
  82. attachment-list(data.attachments)
  83. }
  84. #signature-block(
  85. person,
  86. company: company,
  87. media_policy: media-policy,
  88. show_stamp: data.at("show-stamp"),
  89. note: data.note,
  90. )
  91. #if data.at("render-attachments") and data.attachments.items.len() > 0 {
  92. render-letter-attachments(data.attachments, appendix-state)
  93. }
  94. ]
  95. apply-foundation(
  96. composed,
  97. ctx: ctx,
  98. tokens: (
  99. text-font: "Arial",
  100. text-size: 10pt,
  101. line-height: 0.65em,
  102. page-margin: (top: 1.5cm, bottom: 4cm, left: 1.5cm, right: 1.5cm),
  103. ),
  104. page-options: (
  105. footer: footer,
  106. footer-descent: 20%,
  107. ),
  108. )
  109. }
  110. #let letter-profile(
  111. recipient: none,
  112. date: none,
  113. reference: none,
  114. title: "Деловое письмо",
  115. signer: none,
  116. note: none,
  117. show_stamp: true,
  118. attachments: attachment-set(),
  119. render_attachments: false,
  120. ) = {
  121. document-profile(
  122. "letter",
  123. render-letter,
  124. metadata: (
  125. recipient: recipient,
  126. date: date,
  127. reference: reference,
  128. title: title,
  129. signer: signer,
  130. note: note,
  131. show-stamp: show_stamp,
  132. parties: (),
  133. attachments: attachments,
  134. bibliographies: (),
  135. render-attachments: render_attachments,
  136. ),
  137. normalize: normalize-letter,
  138. validate: validate-letter,
  139. )
  140. }