hachem.wtf
aboutsummaryrefslogtreecommitdiff
path: root/templates
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-09-18 13:59:45 +0200
committerhachem <im@hachem.wtf>2026-09-18 13:59:45 +0200
commit2720a126aa5a8412f8d355f8937cf6c37975be78 (patch)
tree1070b2bbc875b6bb56ca1c19e652f882dfe10279 /templates
parentab2774b23c66521e13541ab3e361bba95c394a8a (diff)
feat: add letter template
Diffstat (limited to 'templates')
-rw-r--r--templates/letter.typ90
-rw-r--r--templates/paper.typ94
2 files changed, 150 insertions, 34 deletions
diff --git a/templates/letter.typ b/templates/letter.typ
new file mode 100644
index 0000000..361f47b
--- /dev/null
+++ b/templates/letter.typ
@@ -0,0 +1,90 @@
+#let sender-block(sender) = if type(sender) == dictionary {
+ text(weight: "medium", tracking: 0.03em)[#smallcaps(sender.name)]
+ if sender.at("address", default: none) != none {
+ v(0.35em)
+ text(size: 0.85em, fill: luma(30%))[#sender.address]
+ }
+} else {
+ sender
+}
+
+#let recipient-block(recipient) = if type(recipient) == dictionary {
+ recipient.name
+ if recipient.at("address", default: none) != none {
+ linebreak()
+ text(size: 0.9em, fill: luma(30%))[#recipient.address]
+ }
+} else {
+ recipient
+}
+
+#let letter(
+ sender: none,
+ recipient: none,
+ place: none,
+ date: none,
+ subject: none,
+ opening: [Madame, Monsieur,],
+ closing: [Veuillez agréer, Madame, Monsieur, l'expression de mes salutations distinguées.],
+ signature: none,
+ lang: "fr",
+ region: "FR",
+ font: "EB Garamond",
+ size: 11pt,
+ doc,
+) = {
+ set page(paper: "a4", margin: (x: 26mm, top: 30mm, bottom: 26mm), numbering: none)
+ set par(justify: true, leading: 0.72em, spacing: 1.3em)
+ set text(font: font, size: size, lang: lang, region: region, number-type: "old-style")
+
+ let dateline = if place != none and date != none {
+ [#place, le #date]
+ } else if date != none {
+ [le #date]
+ } else {
+ place
+ }
+
+ let sign = if signature != none {
+ signature
+ } else if type(sender) == dictionary {
+ sender.name
+ } else {
+ none
+ }
+
+ grid(
+ columns: (1fr, 1fr),
+ align: (left + top, right + top),
+ sender-block(sender),
+ {
+ recipient-block(recipient)
+ if dateline != none {
+ if recipient != none { v(1.4em) }
+ dateline
+ }
+ },
+ )
+
+ v(1.9em)
+ line(length: 100%, stroke: 0.4pt)
+ v(1.9em)
+
+ if subject != none {
+ [#text(weight: "bold")[Objet :] #subject]
+ v(1.4em)
+ }
+
+ opening
+
+ v(0.6em)
+ doc
+
+ v(1.6em)
+ closing
+
+ if sign != none {
+ v(3.2em)
+ align(right, smallcaps(sign))
+ }
+}
diff --git a/templates/paper.typ b/templates/paper.typ
index c7658ec..caf33b6 100644
--- a/templates/paper.typ
+++ b/templates/paper.typ
@@ -1,38 +1,59 @@
-#let paper-title(title, date) = [
- #place(top + center, float: true)[
- #block(width: 100%)[
- #align(center)[
- #text(size: 18pt)[#smallcaps[#title]] \
- #v(0em)
- #text(size: 12pt)[--- #smallcaps(date) ---]
- ]
- ]
- ]
+#let title-block(title, date) = align(center)[
+ #text(size: 18pt)[#smallcaps[#title]] \
+ #v(0.15em)
+ #text(size: 12pt)[--- #smallcaps(date) ---]
]
-#let author-block(authors) = [
- #align(center)[
- #for author in authors [
- #author.name \
- #text(style: "italic")[#author.location]
- ]
+#let author-block(authors) = align(center)[
+ #for author in authors [
+ #author.name \
+ #text(style: "italic")[#author.location]
]
- #v(1.2em)
]
-#let abstract-block(body) = [
- #block(width: 100%)[
- #text(weight: "bold")[Abstract---]
- #body
- ]
- #v(0.8em)
+#let abstract-block(prefix, body) = block(width: 100%, inset: (x: 10%))[
+ #set par(justify: true, first-line-indent: 0pt)
+ #text(size: 9pt)[#text(weight: "bold")[#prefix]#body]
+]
+
+#let frontmatter(title, date, authors, abstract, abstract-prefix) = place(
+ top + center,
+ scope: "parent",
+ float: true,
+)[
+ #title-block(title, date)
+ #v(0.7em)
+ #author-block(authors)
+ #v(0.9em)
+ #if abstract != none {
+ abstract-block(abstract-prefix, abstract)
+ v(0.7em)
+ }
+ #line(length: 100%, stroke: 0.4pt)
+ #v(0.5em)
]
+// a booktabs table: a top rule, a rule under the header, a bottom rule, and
+// nothing else. header is the row of heading cells; the rest are body cells.
+#let booktabs(columns: auto, align: auto, header: (), ..rows) = table(
+ columns: columns,
+ align: align,
+ stroke: none,
+ inset: (x: 6pt, y: 4pt),
+ table.hline(stroke: 0.8pt),
+ table.header(..header.map(cell => strong(cell))),
+ table.hline(stroke: 0.4pt),
+ ..rows.pos(),
+ table.hline(stroke: 0.8pt),
+)
+
#let paper(
title: none,
date: none,
authors: (),
abstract: none,
+ lang: "fr",
+ region: "FR",
font: "EB Garamond",
size: 10pt,
doc,
@@ -46,18 +67,25 @@
numbering: "- 1/1 -",
)
- set par(justify: true, leading: 0.55em)
- set text(font: font, size: size)
+ set par(
+ justify: true,
+ leading: 0.62em,
+ spacing: 0.62em,
+ first-line-indent: (amount: 1.2em, all: false),
+ linebreaks: "optimized",
+ )
+ set text(font: font, size: size, lang: lang, region: region, number-type: "old-style")
set figure(gap: 0.65em)
+ set table(inset: (x: 6pt, y: 4pt))
show heading.where(level: 1): it => [
- #v(0.8em)
- #text(size: 12pt, weight: "medium")[#align(center)[#smallcaps[#it.body]]]
- #v(0.35em)
+ #v(0.9em)
+ #align(center)[#text(size: 11pt, weight: "medium")[#smallcaps[#it.body]]]
+ #v(0.4em)
]
show heading.where(level: 2): it => [
- #v(0.6em)
+ #v(0.7em)
#align(right)[#text(size: 10pt, style: "italic")[#it.body]]
#v(0.25em)
]
@@ -69,14 +97,12 @@
]
show figure.where(kind: table): set text(size: 8.5pt)
+ show figure.caption: set text(size: 8.5pt)
show bibliography: set text(size: 8.5pt)
- paper-title(title, date)
- author-block(authors)
+ let abstract-prefix = if lang == "fr" { [Résumé --- ] } else { [Abstract---] }
- if abstract != none {
- abstract-block(abstract)
- }
+ frontmatter(title, date, authors, abstract, abstract-prefix)
doc
}