@airmark/quiver 0.32.1 → 0.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -18
- package/package.json +8 -6
- package/quills/af4141/0.1.0/Quill.yaml +1 -1
- package/quills/afmc_moa/0.0.1/Quill.yaml +10 -17
- package/quills/classic_resume/0.0.1/Quill.yaml +361 -0
- package/quills/classic_resume/0.0.1/packages/ttq-classic-resume/LICENSE +21 -0
- package/quills/classic_resume/0.0.1/packages/ttq-classic-resume/fonts/EBGaramond/EBGaramond-Bold.ttf +0 -0
- package/quills/classic_resume/0.0.1/packages/ttq-classic-resume/fonts/EBGaramond/EBGaramond-BoldItalic.ttf +0 -0
- package/quills/classic_resume/0.0.1/packages/ttq-classic-resume/fonts/EBGaramond/EBGaramond-Italic.ttf +0 -0
- package/quills/classic_resume/0.0.1/packages/ttq-classic-resume/fonts/EBGaramond/EBGaramond-Regular.ttf +0 -0
- package/quills/classic_resume/0.0.1/packages/ttq-classic-resume/fonts/EBGaramond/OFL.txt +94 -0
- package/quills/classic_resume/0.0.1/packages/ttq-classic-resume/src/components.typ +181 -0
- package/quills/classic_resume/0.0.1/packages/ttq-classic-resume/src/config.typ +53 -0
- package/quills/classic_resume/0.0.1/packages/ttq-classic-resume/src/layout.typ +57 -0
- package/quills/classic_resume/0.0.1/packages/ttq-classic-resume/src/lib.typ +6 -0
- package/quills/classic_resume/0.0.1/packages/ttq-classic-resume/src/util.typ +32 -0
- package/quills/classic_resume/0.0.1/packages/ttq-classic-resume/typst.toml +26 -0
- package/quills/classic_resume/0.0.1/plate.typ +143 -0
- package/quills/cyber_ribbon_chart/0.0.1/Quill.yaml +320 -0
- package/quills/cyber_ribbon_chart/0.0.1/plate.typ +428 -0
- package/quills/daf1206/0.1.0/Quill.yaml +1 -2
- package/quills/daf4392/0.1.0/Quill.yaml +15 -1
- package/quills/daf4392/0.1.0/plate.typ +2 -2
- package/quills/usaf_letter/0.1.0/Quill.yaml +214 -0
- package/quills/usaf_letter/0.1.0/assets/dod_seal.png +0 -0
- package/quills/usaf_letter/0.1.0/assets/dow_seal.png +0 -0
- package/quills/usaf_letter/0.1.0/packages/tonguetoquill-usaf-letter/.gitignore +7 -0
- package/quills/usaf_letter/0.1.0/packages/tonguetoquill-usaf-letter/LICENSE +201 -0
- package/quills/usaf_letter/0.1.0/packages/tonguetoquill-usaf-letter/fonts/Cinzel/Cinzel-Regular.ttf +0 -0
- package/quills/usaf_letter/0.1.0/packages/tonguetoquill-usaf-letter/fonts/Cinzel/LICENSE +93 -0
- package/quills/usaf_letter/0.1.0/packages/tonguetoquill-usaf-letter/fonts/CopperplateCC/CopperplateCC-Bold.otf +0 -0
- package/quills/usaf_letter/0.1.0/packages/tonguetoquill-usaf-letter/fonts/CopperplateCC/CopperplateCC-Heavy.otf +0 -0
- package/quills/usaf_letter/0.1.0/packages/tonguetoquill-usaf-letter/fonts/CopperplateCC/LICENSE.md +79 -0
- package/quills/usaf_letter/0.1.0/packages/tonguetoquill-usaf-letter/fonts/LiberationMono/LICENSE +102 -0
- package/quills/usaf_letter/0.1.0/packages/tonguetoquill-usaf-letter/fonts/LiberationMono/LiberationMono-Regular.ttf +0 -0
- package/quills/usaf_letter/0.1.0/packages/tonguetoquill-usaf-letter/fonts/NimbusRomanNo9L/GNU General Public License.txt +340 -0
- package/quills/usaf_letter/0.1.0/packages/tonguetoquill-usaf-letter/fonts/NimbusRomanNo9L/NimbusRomNo9L-Med.otf +0 -0
- package/quills/usaf_letter/0.1.0/packages/tonguetoquill-usaf-letter/fonts/NimbusRomanNo9L/NimbusRomNo9L-MedIta.otf +0 -0
- package/quills/usaf_letter/0.1.0/packages/tonguetoquill-usaf-letter/fonts/NimbusRomanNo9L/NimbusRomNo9L-Reg.otf +0 -0
- package/quills/usaf_letter/0.1.0/packages/tonguetoquill-usaf-letter/fonts/NimbusRomanNo9L/NimbusRomNo9L-RegIta.otf +0 -0
- package/quills/usaf_letter/0.1.0/packages/tonguetoquill-usaf-letter/src/backmatter.typ +32 -0
- package/quills/usaf_letter/0.1.0/packages/tonguetoquill-usaf-letter/src/config.typ +45 -0
- package/quills/usaf_letter/0.1.0/packages/tonguetoquill-usaf-letter/src/frontmatter.typ +226 -0
- package/quills/usaf_letter/0.1.0/packages/tonguetoquill-usaf-letter/src/lib.typ +35 -0
- package/quills/usaf_letter/0.1.0/packages/tonguetoquill-usaf-letter/src/mainmatter.typ +140 -0
- package/quills/usaf_letter/0.1.0/packages/tonguetoquill-usaf-letter/src/primitives.typ +544 -0
- package/quills/usaf_letter/0.1.0/packages/tonguetoquill-usaf-letter/src/utils.typ +334 -0
- package/quills/usaf_letter/0.1.0/packages/tonguetoquill-usaf-letter/typst.toml +22 -0
- package/quills/usaf_letter/0.1.0/plate.typ +124 -0
- package/quills/usaf_memo/0.2.0/Quill.yaml +1 -1
- package/quills/usaf_memo/0.2.0/packages/tonguetoquill-usaf-memo/LICENSE +201 -21
- package/quills/usaf_memo/0.2.0/packages/tonguetoquill-usaf-memo/typst.toml +1 -1
- package/quills/usaf_memo/0.3.0/Quill.yaml +75 -56
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/LICENSE +201 -21
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/fonts/Cinzel/Cinzel-Regular.ttf +0 -0
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/src/backmatter.typ +29 -17
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/src/body.typ +97 -126
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/src/config.typ +12 -45
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/src/frontmatter.typ +81 -87
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/src/indorsement.typ +58 -66
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/src/lib.typ +14 -36
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/src/mainmatter.typ +47 -19
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/src/primitives.typ +178 -121
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/src/utils.typ +41 -141
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/typst.toml +3 -8
- package/quills/usaf_memo/0.3.0/plate.typ +89 -85
- package/templates/af4141.md +23 -0
- package/templates/appointment_letter.md +58 -0
- package/templates/daf1206.md +23 -0
- package/templates/daf4392.md +34 -0
- package/templates/pass_request.md +48 -0
- package/templates/rebuttal.md +63 -0
- package/templates/templates.json +58 -0
- package/templates/usaf_template.md +30 -0
- package/templates/ussf_template.md +30 -0
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/fonts/Cinzel/Cinzel[wght].ttf +0 -0
|
@@ -1,19 +1,11 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
// This module implements the visual rendering functions that produce AFH 33-337
|
|
4
|
-
// compliant formatting for all sections of a USAF memorandum. Each function
|
|
5
|
-
// corresponds to specific placement and formatting requirements from Chapter 14.
|
|
1
|
+
// One rendering function per section of the memorandum, each placing its
|
|
2
|
+
// section where AFH 33-337 Chapter 14 puts it.
|
|
6
3
|
|
|
7
4
|
#import "config.typ": *
|
|
8
5
|
#import "utils.typ": *
|
|
9
6
|
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
// =============================================================================
|
|
13
|
-
// AFH 33-337 §1: "Use printed letterhead, computer-generated letterhead, or plain bond paper"
|
|
14
|
-
// Letterhead placement is not explicitly specified in AFH 33-337, but follows
|
|
15
|
-
// standard USAF memo formatting conventions
|
|
16
|
-
|
|
7
|
+
// AFH 33-337 does not specify letterhead placement; the geometry below follows
|
|
8
|
+
// standard USAF memo convention.
|
|
17
9
|
#let render-letterhead(
|
|
18
10
|
title,
|
|
19
11
|
caption,
|
|
@@ -24,8 +16,8 @@
|
|
|
24
16
|
letterhead-emblem-height: 1in, // emblem fit-box height; reduce for shorter emblems
|
|
25
17
|
) = {
|
|
26
18
|
font = ensure-array(font)
|
|
27
|
-
// `upper`
|
|
28
|
-
// survives
|
|
19
|
+
// `upper` takes content as readily as `str`, so the letterhead's uppercasing
|
|
20
|
+
// survives a title or caption handed over as markup.
|
|
29
21
|
title = upper(join-lines(title))
|
|
30
22
|
caption = upper(join-lines(caption))
|
|
31
23
|
|
|
@@ -78,8 +70,8 @@
|
|
|
78
70
|
#fit-box(width: corner-width, height: band-height)[#letterhead-seal]
|
|
79
71
|
]
|
|
80
72
|
} else {
|
|
81
|
-
// Isolate seal column from document `
|
|
82
|
-
// must not scale with body text (see frontmatter `set text(size:
|
|
73
|
+
// Isolate seal column from document `font-size`: stack `em` spacing and subtitle
|
|
74
|
+
// must not scale with body text (see frontmatter `set text(size: font-size)`).
|
|
83
75
|
// Subtitle is wrapped in `box` so it stays on one line and may extend past
|
|
84
76
|
// the seal's 2in column rather than wrapping.
|
|
85
77
|
block[
|
|
@@ -115,15 +107,6 @@
|
|
|
115
107
|
}
|
|
116
108
|
}
|
|
117
109
|
|
|
118
|
-
// =============================================================================
|
|
119
|
-
// HEADER SECTIONS
|
|
120
|
-
// =============================================================================
|
|
121
|
-
// AFH 33-337 "The Heading Section" specifies exact placement and format for:
|
|
122
|
-
// - Date: 1 inch from right edge, 1.75 inches from top
|
|
123
|
-
// - MEMORANDUM FOR: Second line below date
|
|
124
|
-
// - FROM: Second line below MEMORANDUM FOR
|
|
125
|
-
// - SUBJECT: Second line below FROM
|
|
126
|
-
|
|
127
110
|
// AFH 33-337 "Date": "Place the date 1 inch from the right edge, 1.75 inches from the top"
|
|
128
111
|
#let render-date-section(date, memo-style: "usaf") = {
|
|
129
112
|
align(right)[#display-date(date, memo-style: memo-style)]
|
|
@@ -160,12 +143,10 @@
|
|
|
160
143
|
|
|
161
144
|
/// Whether a `references` entry carries no citation text.
|
|
162
145
|
///
|
|
163
|
-
/// Blank entries reach the template routinely
|
|
164
|
-
///
|
|
165
|
-
///
|
|
166
|
-
///
|
|
167
|
-
/// satisfies the "exactly one reference" test below and renders as an empty
|
|
168
|
-
/// `()` after the subject.
|
|
146
|
+
/// Blank entries reach the template routinely — a stub entry left under
|
|
147
|
+
/// `references:` for the user to fill in. Such an entry must not count as a
|
|
148
|
+
/// reference: a lone blank one would otherwise satisfy the "exactly one
|
|
149
|
+
/// reference" test below and render as an empty `()` after the subject.
|
|
169
150
|
///
|
|
170
151
|
/// - entry (any): A single `references` element
|
|
171
152
|
/// -> bool
|
|
@@ -205,25 +186,48 @@
|
|
|
205
186
|
blank-line()
|
|
206
187
|
grid(
|
|
207
188
|
columns: (auto, auto, 1fr),
|
|
208
|
-
//
|
|
209
|
-
// lettered "(a) (b) (c)" per AFH 33-337.
|
|
189
|
+
// Spread the entries as enum items lettered "(a) (b) (c)" per AFH 33-337.
|
|
210
190
|
"References:", " ", enum(..references, numbering: "(a) ", body-indent: 0pt),
|
|
211
191
|
)
|
|
212
192
|
}
|
|
213
193
|
}
|
|
214
194
|
|
|
215
|
-
// =============================================================================
|
|
216
|
-
// SIGNATURE BLOCK
|
|
217
|
-
// =============================================================================
|
|
218
195
|
// AFH 33-337 "Signature Block": "Start the signature block on the fifth line below
|
|
219
196
|
// the last line of text and 4.5 inches from the left edge of the page or three
|
|
220
197
|
// spaces to the right of page center"
|
|
221
198
|
// AFH 33-337 "Do not place the signature element on a continuation page by itself"
|
|
222
199
|
// AFH 33-337 long-name example: "Signature block adjusted to the left" when a
|
|
223
200
|
// long name would otherwise exceed the right margin.
|
|
224
|
-
|
|
225
|
-
|
|
201
|
+
//
|
|
202
|
+
// A closing line may open the section above the block, and AFH 33-337 gives it
|
|
203
|
+
// one geometry across the two documents that have one: on the second line below
|
|
204
|
+
// the text at the block's own anchor, with the signature then five lines below
|
|
205
|
+
// the line rather than below the body. The memorandum's occupant is the
|
|
206
|
+
// authority line, "FOR THE COMMANDER"; the personal letter's is the
|
|
207
|
+
// complimentary close, "Sincerely", which the handbook bars the authority line
|
|
208
|
+
// from. Hence the neutral parameter, and hence case belongs to the caller — the
|
|
209
|
+
// authority line is uppercased, "Sincerely" is not.
|
|
210
|
+
|
|
211
|
+
/// The memorandum's occupant of that slot, uppercased per AFH 33-337. Blank is
|
|
212
|
+
/// no line: the handbook forbids one where the commander signs.
|
|
213
|
+
///
|
|
214
|
+
/// - value (str | content | none): The authority line as authored
|
|
215
|
+
/// -> content | none
|
|
216
|
+
#let format-authority-line(value) = if falsey(value) { none } else { upper(value) }
|
|
217
|
+
|
|
218
|
+
#let render-signature-block(
|
|
219
|
+
signature-lines,
|
|
220
|
+
closing-line: none,
|
|
221
|
+
signature-blank-lines: 4,
|
|
222
|
+
signing-field: none,
|
|
223
|
+
// Lines of breaking height reserved below the block for the backmatter
|
|
224
|
+
// lead-in and its continuation note. Reclaimed immediately after, so it moves
|
|
225
|
+
// where the block may break and nothing else.
|
|
226
|
+
reserved-lines: 0,
|
|
227
|
+
) = {
|
|
226
228
|
signature-lines = ensure-array(signature-lines)
|
|
229
|
+
// Blank is no line, whichever occupant the caller passes.
|
|
230
|
+
if falsey(closing-line) { closing-line = none }
|
|
227
231
|
// AFH 33-337 allows two equivalent anchors: 4.5in from the left edge, or three
|
|
228
232
|
// spaces right of page center. On 8.5in stock these coincide (page center =
|
|
229
233
|
// 4.25in; three TNR-12pt spaces ≈ 0.25in), so we use 4.5in as the canonical
|
|
@@ -231,19 +235,17 @@
|
|
|
231
235
|
let default-pad = 4.5in - spacing.margin
|
|
232
236
|
context {
|
|
233
237
|
// Measure each line at its rendered settings to detect long-name overflow.
|
|
238
|
+
// The closing line shares the anchor, so it joins the measurement: the
|
|
239
|
+
// wider of the two decides the shift and they stay aligned.
|
|
234
240
|
let body-width = page.width - 2 * spacing.margin
|
|
241
|
+
let anchored-lines = signature-lines
|
|
242
|
+
if closing-line != none { anchored-lines.push(closing-line) }
|
|
235
243
|
let widest = 0pt
|
|
236
|
-
for line in
|
|
244
|
+
for line in anchored-lines {
|
|
237
245
|
let w = measure(text(hyphenate: false, line)).width
|
|
238
246
|
if w > widest { widest = w }
|
|
239
247
|
}
|
|
240
|
-
let stride =
|
|
241
|
-
let s = LINE_STRIDE.get()
|
|
242
|
-
if s == none {
|
|
243
|
-
let one-line = measure(par(spacing: 0pt)[x]).height
|
|
244
|
-
measure(par(spacing: 0pt)[x#linebreak()x]).height - one-line
|
|
245
|
-
} else { s }
|
|
246
|
-
}
|
|
248
|
+
let stride = line-stride()
|
|
247
249
|
// If the widest line would overflow the right margin at the standard
|
|
248
250
|
// anchor, shift the block left just enough to fit. Clamp at 0 so the
|
|
249
251
|
// block never crosses the left margin.
|
|
@@ -262,25 +264,43 @@
|
|
|
262
264
|
// and the block one indivisible unit. The total space above the block is
|
|
263
265
|
// identical either way — `block.above` still contributes the same 0.5em —
|
|
264
266
|
// but a gap left outside can be consumed at the foot of one page while
|
|
265
|
-
// the block starts at the top margin of the next
|
|
266
|
-
//
|
|
267
|
-
|
|
267
|
+
// the block starts at the top margin of the next.
|
|
268
|
+
//
|
|
269
|
+
// The closing line takes one of those lines above it and the gap then
|
|
270
|
+
// measures from the line, not from the text. Inside the block, so no page
|
|
271
|
+
// break can put it on a different page from the signature.
|
|
272
|
+
#if closing-line != none {
|
|
273
|
+
v(stride)
|
|
274
|
+
pad(left: left-pad, text(hyphenate: false, closing-line))
|
|
275
|
+
}
|
|
268
276
|
#if signing-field != none {
|
|
269
277
|
// The signing field covers those blank lines — where a signature is
|
|
270
|
-
// actually written — so it is placed over the gap.
|
|
271
|
-
// the
|
|
272
|
-
//
|
|
273
|
-
//
|
|
274
|
-
//
|
|
275
|
-
// the
|
|
276
|
-
//
|
|
277
|
-
//
|
|
278
|
+
// actually written — so it is placed over the gap. It is placed BEFORE
|
|
279
|
+
// the gap is emitted: `place` anchors at the current flow position, so
|
|
280
|
+
// placing it after `v(gap)` anchors the box at the first name line and
|
|
281
|
+
// paints it down over the printed signature block. Anchored here it
|
|
282
|
+
// still travels with the block onto whatever page the block lands on,
|
|
283
|
+
// since the gap is inside the unbreakable block rather than ahead of
|
|
284
|
+
// it. The current position is below the closing line when there is
|
|
285
|
+
// one, which is where the signature is written.
|
|
286
|
+
//
|
|
287
|
+
// The widget keeps its own size (the helper's default is 50pt tall and
|
|
288
|
+
// it positions itself, so an `align` around it does nothing) and is
|
|
289
|
+
// offset to the BOTTOM of the gap: it always ends where the printed
|
|
290
|
+
// name begins, and whatever the gap has beyond the widget's height
|
|
291
|
+
// stays clear between the body text and the widget's frame.
|
|
292
|
+
let widget-height = {
|
|
293
|
+
let h = measure(signing-field).height
|
|
294
|
+
if h > 0pt { h } else { 50pt }
|
|
295
|
+
}
|
|
296
|
+
let drop = gap - widget-height - 3pt
|
|
278
297
|
place(
|
|
279
298
|
dx: left-pad,
|
|
280
|
-
dy: 0pt,
|
|
281
|
-
box(width: body-width - left-pad, height:
|
|
299
|
+
dy: if drop > 0pt { drop } else { 0pt },
|
|
300
|
+
box(width: body-width - left-pad, height: widget-height, signing-field),
|
|
282
301
|
)
|
|
283
302
|
}
|
|
303
|
+
#v(gap)
|
|
284
304
|
#align(left)[
|
|
285
305
|
#pad(left: left-pad)[
|
|
286
306
|
#text(hyphenate: false)[
|
|
@@ -289,31 +309,26 @@
|
|
|
289
309
|
// of the line above" — 2-character indent ≈ 1em in Times New Roman 12pt
|
|
290
310
|
par(hanging-indent: .5em, line)
|
|
291
311
|
}
|
|
312
|
+
// The page the backmatter compares its own against. Must stay
|
|
313
|
+
// inside the unbreakable block: a marker outside one travels with
|
|
314
|
+
// the section that moves.
|
|
315
|
+
#metadata(none)<usaf-memo-flow-anchor>
|
|
316
|
+
#v(stride * reserved-lines)
|
|
292
317
|
]
|
|
293
318
|
]
|
|
294
319
|
]
|
|
295
320
|
]
|
|
321
|
+
// The reservation counted toward breaking height only; the backmatter
|
|
322
|
+
// emits its own lead-in below.
|
|
323
|
+
v(stride * -reserved-lines)
|
|
296
324
|
}
|
|
297
325
|
}
|
|
298
326
|
|
|
299
|
-
//
|
|
300
|
-
//
|
|
301
|
-
//
|
|
302
|
-
//
|
|
303
|
-
//
|
|
304
|
-
//
|
|
305
|
-
// Two option pairs are supported, reflecting the two roles an indorsement
|
|
306
|
-
// plays in a coordination chain: coordinating officials Concur / Nonconcur,
|
|
307
|
-
// while the final approval authority Approves / Disapproves.
|
|
308
|
-
//
|
|
309
|
-
// Which pair prints is not the endorser's to choose — it follows from the
|
|
310
|
-
// role, so the caller supplies the role via `approval-authority` and the same
|
|
311
|
-
// three `action` values (affirm, reject, or leave open) carry over both pairs.
|
|
312
|
-
//
|
|
313
|
-
// The "undecided" form renders both options plain, for printing a memo the
|
|
314
|
-
// endorser marks by hand when signing.
|
|
315
|
-
//
|
|
316
|
-
// Empty/none suppression is handled by the caller before this is invoked.
|
|
327
|
+
// The indorsement decision line is an "either / or" pair. Which pair prints
|
|
328
|
+
// follows from the indorsement's role in the coordination chain rather than
|
|
329
|
+
// from the endorser's choice, so the caller supplies the role and the same
|
|
330
|
+
// three `action` values carry over both pairs. The caller also suppresses the
|
|
331
|
+
// line entirely; reaching here means one is wanted.
|
|
317
332
|
|
|
318
333
|
// The option pair for each role, ordered (affirmative, negative).
|
|
319
334
|
#let APPROVAL_OPTIONS = ("Approve", "Disapprove")
|
|
@@ -368,24 +383,16 @@
|
|
|
368
383
|
}
|
|
369
384
|
}
|
|
370
385
|
|
|
371
|
-
|
|
372
|
-
// TABLE RENDERING
|
|
373
|
-
// =============================================================================
|
|
374
|
-
// AFH 33-337 does not specify table formatting, so we follow the general
|
|
375
|
-
// aesthetic principles of the standard: plain black borders, no decorative
|
|
376
|
-
// fills, and the body font inherited throughout.
|
|
377
|
-
|
|
378
|
-
/// Renders a table with USAF memorandum–consistent formatting.
|
|
386
|
+
/// Renders a table in the memorandum's style.
|
|
379
387
|
///
|
|
380
|
-
///
|
|
381
|
-
///
|
|
382
|
-
///
|
|
388
|
+
/// AFH 33-337 does not specify table formatting, so this follows the general
|
|
389
|
+
/// aesthetic of the standard: plain black borders, a bold header row, no
|
|
390
|
+
/// decorative fills, and the body font and size inherited from the surrounding
|
|
391
|
+
/// text.
|
|
383
392
|
///
|
|
384
393
|
/// - it (content): The table element to style and render
|
|
385
394
|
/// -> content
|
|
386
395
|
#let render-memo-table(it) = {
|
|
387
|
-
// AFH 33-337 does not specify table formatting, so we follow the general
|
|
388
|
-
// aesthetic principles of the standard: bold headers for clarity.
|
|
389
396
|
show table.cell.where(y: 0): set text(weight: "bold")
|
|
390
397
|
set table(
|
|
391
398
|
stroke: 0.5pt + black,
|
|
@@ -394,23 +401,18 @@
|
|
|
394
401
|
it
|
|
395
402
|
}
|
|
396
403
|
|
|
397
|
-
// =============================================================================
|
|
398
|
-
// BACKMATTER SECTIONS
|
|
399
|
-
// =============================================================================
|
|
400
|
-
// AFH 33-337 "Attachment or Attachments": "Place 'Attachment:' (for a single attachment)
|
|
401
|
-
// or '# Attachments:' (for two or more attachments) at the left margin, on the third
|
|
402
|
-
// line below the signature element"
|
|
403
|
-
// AFH 33-337 "Courtesy Copy Element": "place 'cc:' flush with the left margin, on the
|
|
404
|
-
// second line below the attachment element"
|
|
405
|
-
|
|
406
404
|
#let render-backmatter-section(
|
|
407
405
|
content,
|
|
408
406
|
section-label,
|
|
409
407
|
numbering-style: none,
|
|
410
408
|
continuation-label: none,
|
|
409
|
+
// Lines of breaking height reserved below this section for the lead-in and
|
|
410
|
+
// continuation note of the section that follows it. Reclaimed immediately
|
|
411
|
+
// after, so it moves where a break may fall and nothing else.
|
|
412
|
+
reserved-lines: 0,
|
|
411
413
|
) = {
|
|
412
414
|
let formatted-content = {
|
|
413
|
-
//
|
|
415
|
+
// `text()` keeps the label from being laid out as a paragraph of its own.
|
|
414
416
|
text()[#section-label]
|
|
415
417
|
linebreak()
|
|
416
418
|
if numbering-style != none {
|
|
@@ -421,30 +423,66 @@
|
|
|
421
423
|
}
|
|
422
424
|
}
|
|
423
425
|
|
|
426
|
+
// Attachments pass continuation-label ("… (listed on next page):" per AFH 33-337).
|
|
427
|
+
// cc: and DISTRIBUTION: use a neutral default — "listed" applies to attachment lists only.
|
|
428
|
+
let continuation-text = if continuation-label != none {
|
|
429
|
+
text()[#continuation-label]
|
|
430
|
+
} else {
|
|
431
|
+
text()[#(section-label + " (continued on next page)")]
|
|
432
|
+
}
|
|
433
|
+
|
|
424
434
|
context {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
435
|
+
// `breakable: false` lets Typst's own breaker move the section to the next
|
|
436
|
+
// page as a unit; an explicit pagebreak here would feed this context's
|
|
437
|
+
// layout query back into its own input and the two would chase each other
|
|
438
|
+
// until layout gave up.
|
|
439
|
+
//
|
|
440
|
+
// The note is decided by observing where that breaker put the block. The
|
|
441
|
+
// nearest anchor above is the signature block or the preceding section — an
|
|
442
|
+
// element with real height that stays on the page this section departs.
|
|
443
|
+
//
|
|
444
|
+
// Do not reduce this to a `here()` reading. `here()` sits in the section and
|
|
445
|
+
// travels with it, so once the section has moved it reports the top of the
|
|
446
|
+
// page it moved to and every section measures as fitting. The room the note
|
|
447
|
+
// needs is reserved inside the anchor's own unbreakable block, so the
|
|
448
|
+
// decision can never move its own input.
|
|
449
|
+
let above = query(selector(<usaf-memo-flow-anchor>).before(here()))
|
|
450
|
+
let below = query(selector(<usaf-memo-flow-anchor>).after(here()))
|
|
451
|
+
if above.len() > 0 and below.len() > 0 {
|
|
452
|
+
if below.first().location().page() > above.last().location().page() {
|
|
453
|
+
continuation-text
|
|
433
454
|
}
|
|
434
|
-
continuation-text
|
|
435
|
-
pagebreak()
|
|
436
455
|
}
|
|
437
|
-
|
|
456
|
+
// A section taller than a page cannot move as a unit, so it splits where
|
|
457
|
+
// the page ends rather than overflowing it, and anchors its last page too:
|
|
458
|
+
// the section below measures from where this one ends.
|
|
459
|
+
let reserve = line-stride() * reserved-lines
|
|
460
|
+
layout(region => {
|
|
461
|
+
let oversized = measure(formatted-content, width: region.width).height + reserve > region.height
|
|
462
|
+
block(breakable: oversized)[
|
|
463
|
+
#metadata(none)<usaf-memo-flow-anchor>
|
|
464
|
+
#formatted-content
|
|
465
|
+
#if oversized [#metadata(none)<usaf-memo-flow-anchor>]
|
|
466
|
+
#v(reserve)
|
|
467
|
+
]
|
|
468
|
+
})
|
|
469
|
+
v(-reserve)
|
|
438
470
|
}
|
|
439
471
|
}
|
|
440
472
|
|
|
441
473
|
#let calculate-backmatter-spacing(is-first-section) = {
|
|
442
474
|
context {
|
|
443
|
-
let
|
|
444
|
-
blank-lines(
|
|
475
|
+
let line-count = if is-first-section { 2 } else { 1 }
|
|
476
|
+
blank-lines(line-count)
|
|
445
477
|
}
|
|
446
478
|
}
|
|
447
479
|
|
|
480
|
+
// AFH 33-337 "Attachment or Attachments": "Place 'Attachment:' (for a single attachment)
|
|
481
|
+
// or '# Attachments:' (for two or more attachments) at the left margin, on the third
|
|
482
|
+
// line below the signature element"
|
|
483
|
+
// AFH 33-337 "Courtesy Copy Element": "place 'cc:' flush with the left margin, on the
|
|
484
|
+
// second line below the attachment element"
|
|
485
|
+
|
|
448
486
|
#let render-backmatter-sections(
|
|
449
487
|
attachments: none,
|
|
450
488
|
cc: none,
|
|
@@ -461,27 +499,46 @@
|
|
|
461
499
|
pagebreak(weak: true)
|
|
462
500
|
}
|
|
463
501
|
|
|
502
|
+
// Collected first because a section's reservation depends on whether another
|
|
503
|
+
// follows it: with nothing below, no note can be owed.
|
|
504
|
+
let sections = ()
|
|
505
|
+
|
|
464
506
|
if attachments != none and attachments.len() > 0 {
|
|
465
|
-
calculate-backmatter-spacing(true)
|
|
466
507
|
let attachment-count = attachments.len()
|
|
467
508
|
let section-label = if attachment-count == 1 { "Attachment:" } else { str(attachment-count) + " Attachments:" }
|
|
468
509
|
let continuation-label = (
|
|
469
510
|
(if attachment-count == 1 { "Attachment" } else { str(attachment-count) + " Attachments" })
|
|
470
511
|
+ " (listed on next page):"
|
|
471
512
|
)
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
513
|
+
sections.push((
|
|
514
|
+
content: attachments,
|
|
515
|
+
label: section-label,
|
|
516
|
+
// AFH 33-337: a single attachment is not numbered; numbering applies to two or more.
|
|
517
|
+
numbering-style: if attachment-count == 1 { none } else { "1." },
|
|
518
|
+
continuation-label: continuation-label,
|
|
519
|
+
))
|
|
475
520
|
}
|
|
476
521
|
|
|
477
522
|
if cc != none and cc.len() > 0 {
|
|
478
|
-
|
|
479
|
-
render-backmatter-section(cc, "cc:")
|
|
523
|
+
sections.push((content: cc, label: "cc:", numbering-style: none, continuation-label: none))
|
|
480
524
|
}
|
|
481
525
|
|
|
482
526
|
if distribution != none and distribution.len() > 0 {
|
|
483
|
-
|
|
484
|
-
|
|
527
|
+
sections.push((content: distribution, label: "DISTRIBUTION:", numbering-style: none, continuation-label: none))
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
for (index, section) in sections.enumerate() {
|
|
531
|
+
calculate-backmatter-spacing(index == 0)
|
|
532
|
+
// The next section's one blank line of lead-in, plus one for its note. The
|
|
533
|
+
// signature block reserves 3: the first section's lead-in is two lines.
|
|
534
|
+
let reserved-lines = if index + 1 < sections.len() { 2 } else { 0 }
|
|
535
|
+
render-backmatter-section(
|
|
536
|
+
section.content,
|
|
537
|
+
section.label,
|
|
538
|
+
numbering-style: section.numbering-style,
|
|
539
|
+
continuation-label: section.continuation-label,
|
|
540
|
+
reserved-lines: reserved-lines,
|
|
541
|
+
)
|
|
485
542
|
}
|
|
486
543
|
}
|
|
487
544
|
|