-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathrubby.typ
121 lines (111 loc) · 3.33 KB
/
rubby.typ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// Copyright (C) 2023 Andrew Voynov
// AGPL-3.0-only license is in the LICENSE file in the root of the project
// or at https://www.gnu.org/licenses/agpl-3.0.txt
#let _ruby(rt, rb, size, pos, dy, alignment, delimiter, auto-spacing) = {
if not ("center", "start", "between", "around").contains(alignment) {
panic("'" + repr(alignment) + "' is not a valid ruby alignment")
}
if not (top, bottom).contains(pos) {
panic("pos can be either top or bottom but '" + repr(pos) + "'")
}
let extract-content(content, fn: it => it) = {
let func = content.func()
return if func == text or func == raw {
(content.text, fn)
} else {
extract-content(content.body, fn: it => func(fn(it)))
}
}
let add-spacing-if-enabled(text) = {
if auto-spacing != true { return text }
return (
if text.first() != delimiter { delimiter }
+ text
+ if text.last() != delimiter { delimiter }
)
}
let rt-array = if type(rt) == content {
let (inner, func) = extract-content(rt)
add-spacing-if-enabled(inner).split(delimiter).map(func)
} else if type(rt) == str {
add-spacing-if-enabled(rt).split(delimiter)
} else {(rt,)}
assert(type(rt-array) == array)
let rb-array = if type(rb) == content {
let (inner, func) = extract-content(rb)
add-spacing-if-enabled(inner).split(delimiter).map(func)
} else if type(rb) == str {
add-spacing-if-enabled(rb).split(delimiter)
} else {(rb,)}
assert(type(rb-array) == array)
if rt-array.len() != rb-array.len() {
rt-array = (rt,)
rb-array = (rb,)
}
let gutter = if (alignment == "center" or alignment == "start") {
h(0pt)
} else if (alignment == "between" or alignment == "around") {
h(1fr)
}
box(style(st => {
let sum-body = []
let sum-width = 0pt
let i = 0
while i < rb-array.len() {
let (body, ruby) = (rb-array.at(i), rt-array.at(i))
let bodysize = measure(body, st)
let rt-plain-width = measure(text(size: size, ruby), st).width
let width = if rt-plain-width > bodysize.width {
rt-plain-width
} else {
bodysize.width
}
let chars = if(alignment == "around") {
h(0.5fr) + ruby.clusters().join(gutter) + h(0.5fr)
} else {
ruby.clusters().join(gutter)
}
let rubytext = box(
width: width,
align(
if (alignment == "start") { left } else { center },
text(size: size, chars)
)
)
let textsize = measure(rubytext, st)
let dx = textsize.width - bodysize.width
let (t-dx, l-dx, r-dx) = if (alignment == "start") {
(0pt, 0pt, dx)
} else {
(-dx/2, dx/2, dx/2)
}
let (l, r) = (i != 0, i != rb-array.len() - 1)
sum-width += if l { 0pt } else { t-dx }
let dy = if pos == top {
-1.5 * textsize.height - dy
} else {
bodysize.height + textsize.height/2 + dy
}
place(
top + left,
dx: sum-width,
dy: dy,
rubytext
)
sum-width += width
sum-body += if l { h(l-dx) } + body + if r { h(r-dx) }
i += 1
}
sum-body
}))
}
#let get-ruby(
size: 0.5em,
dy: 0pt,
pos: top,
alignment: "center",
delimiter: "|",
auto-spacing: true
) = (rt, rb, alignment: alignment) => (
_ruby(rt, rb, size, pos, dy, alignment, delimiter, auto-spacing)
)