├── .gitignore
├── manual.pdf
├── LICENSE
├── readme.md
├── ruby.typ
└── manual.typ
/.gitignore:
--------------------------------------------------------------------------------
1 | ruby.pdf
--------------------------------------------------------------------------------
/manual.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uwni/ruby-typ/HEAD/manual.pdf
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 rinmyo
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # ruby-typ
2 |
3 | A library for typst to implement ruby just like this
4 |
5 |
6 | とある科学の超電磁砲
7 |
8 |
9 |
10 | for more information. please read the manual.pdf at first.
11 |
12 | ## thanks
13 | this project is based on [@Saito Atsush](https://github.com/SaitoAtsush)’s great idea and the [original source codes](https://zenn.dev/saito_atsushi/articles/ff9490458570e1) posted on their blog.
14 |
15 | ---
16 |
17 | 組版プログラミング言語Typstのためのルビライブラリーです。下記のような効果が実現可能です
18 |
19 |
20 | とある科学の超電磁砲
21 |
22 |
23 | 使い方などは manual.pdf で紹介するので、ぜひ読んでください
24 |
25 | ## 感謝
26 | このプロジェクトは[@齊藤敦志](https://github.com/SaitoAtsush)さんのブログで[掲載されたバージョン](https://zenn.dev/saito_atsushi/articles/ff9490458570e1)に基づいて機能を拡張したものです
27 |
28 | ---
29 |
30 | ruby-typ 係爲 Typst 開發底 ruby 庫. 可以實現如下效果
31 |
32 |
33 | 某科学的超電磁砲
34 |
35 |
36 | 欲知詳情, 請閱 manual.pdf 檔
37 |
38 | ## 感謝
39 | 本案由[@齊藤敦志](https://github.com/SaitoAtsush)先生所著之[原案](https://zenn.dev/saito_atsushi/articles/ff9490458570e1)增補而得.
40 |
--------------------------------------------------------------------------------
/ruby.typ:
--------------------------------------------------------------------------------
1 | #let _ruby(rt, rb, size, pos, dy, alignment, delimiter) = {
2 | if not ("center", "start", "between", "around").contains(alignment) {
3 | panic("'" + repr(alignment) + "' is not a valid ruby alignment")
4 | }
5 |
6 | if not (top, bottom).contains(pos) {
7 | panic("pos can be either top or bottom but '"+ repr(pos) +"'")
8 | }
9 |
10 | let extract_content(content, fn: it => it) = {
11 | let func = content.func()
12 | return if func == text or func == raw {
13 | (content.text, fn)
14 | } else {
15 | extract_content(content.body, fn: it => func(fn(it)))
16 | }
17 | }
18 |
19 | let rb_array = if type(rb) == "content" {
20 | let (inner, func) = extract_content(rb)
21 | inner.split(delimiter).map(func)
22 | } else if type(rb) == "string" {
23 | rb.split(delimiter)
24 | } else {(rb,)}
25 | assert(type(rb_array) == "array")
26 |
27 | let rt_array = rt.split(delimiter)
28 |
29 | if rt_array.len() != rb_array.len() {
30 | rt_array = (rt,)
31 | rb_array = (rb,)
32 | }
33 |
34 | let gutter = if (alignment=="center" or alignment=="start") {
35 | h(0pt)
36 | } else if (alignment=="between" or alignment=="around") {
37 | h(1fr)
38 | }
39 |
40 | box(style(st=> {
41 | let sum_body = []
42 | let sum_width = 0pt
43 | let i = 0
44 | while i < rb_array.len() {
45 | let (body, ruby) = (rb_array.at(i), rt_array.at(i))
46 | let bodysize = measure(body, st)
47 | let rt_plain_width = measure(text(size: size, ruby), st).width
48 | let width = if rt_plain_width > bodysize.width {rt_plain_width} else {bodysize.width}
49 | let chars = if(alignment=="around") {
50 | h(0.5fr) + ruby.clusters().join(gutter) + h(0.5fr)
51 | } else {
52 | ruby.clusters().join(gutter)
53 | }
54 | let rubytext = box(width: width, align(if(alignment=="start"){left}else{center}, text(size: size, chars)))
55 | let textsize = measure(rubytext, st)
56 | let dx = textsize.width - bodysize.width
57 | let (t_dx, l_dx, r_dx) = if(alignment=="start"){(0pt, 0pt, dx)}else{(-dx/2, dx/2, dx/2)}
58 | let (l, r) = (i != 0, i != rb_array.len() - 1)
59 | sum_width += if l {0pt} else {t_dx}
60 | let dy = if pos == top {-1.5 * textsize.height - dy} else {bodysize.height + textsize.height/2 + dy}
61 | place(
62 | top+left,
63 | dx: sum_width,
64 | dy: dy,
65 | rubytext
66 | )
67 | sum_width += width
68 | sum_body += if l {h(l_dx)} + body + if r {h(r_dx)}
69 | i += 1
70 | }
71 | sum_body
72 | }))
73 | }
74 |
75 | #let get_ruby(
76 | size: .5em,
77 | dy: 0pt,
78 | pos: top,
79 | alignment: "center",
80 | delimiter: "|"
81 | ) = (rt, rb, alignment: alignment) => _ruby(rt, rb, size, pos, dy, alignment, delimiter)
82 |
83 | #let test() = [
84 | #set box(stroke: red+.001pt)
85 | #set text(size: 50pt)
86 | #show: align.with(center)
87 | #let ruby = get_ruby(pos: bottom)
88 | #ruby("した")[下]
89 | #let ruby = get_ruby()
90 | #ruby("うえ")[上]
91 | ]
92 |
93 | //#test()
94 |
--------------------------------------------------------------------------------
/manual.typ:
--------------------------------------------------------------------------------
1 | // Set the document's basic properties.
2 | #set document(author: ("rinmyo"), title: "ruby-typ")
3 | #set page(numbering: "1", number-align: center)
4 | #set text(font: "Linux Libertine", lang: "en")
5 | #show link: underline
6 |
7 | // Title row.
8 | #align(center)[
9 | #block(text(weight: 700, 1.75em, "ruby-typ"))
10 | ]
11 |
12 | // Author information.
13 | #pad(
14 | top: 0.5em,
15 | bottom: 0.5em,
16 | x: 2em,
17 | align(center)[rinmyo]
18 | )
19 |
20 | // Main body.
21 | #set par(justify: true)
22 |
23 | // Take a look at the file `template.typ` in the file panel
24 | // to customize this template and discover how it works.
25 | #import "ruby.typ": get_ruby
26 |
27 | #let ruby = get_ruby()
28 |
29 | = Introduction
30 | A library to implement ruby just like this:
31 |
32 | #align(center)[
33 | #ruby(alignment:"between", "レールガン")[超電磁砲]、#ruby("ページ")[頁]、#ruby("きょう")[今日]もいい#ruby("てん|き")[天|気]
34 | ]
35 |
36 | = Usage
37 |
38 | To use ruby-typ, you should import the `get_ruby` from `ruby.typ` at first.
39 |
40 | ```typ
41 | #import "ruby.typ": get_ruby
42 | ```
43 |
44 | `get_ruby` is a higher-order function which be used to configure ruby-typ. and it will return a function for ruby. and its function signature is
45 | ```typc
46 | get_ruby(
47 | size: .5em, // the font size of ruby text
48 | dy: 0pt, // to add a shifting space on y axis
49 | pos: top, // the position of ruby text, can be (top, bottom)
50 | alignment: "center", // can be ("center", "start", "between", "around")
51 | delimiter: "|" // the delimiter used in mono-ruby
52 | )
53 | ```
54 |
55 | to get a ruby function with default setting
56 | ```typ
57 | #let ruby = get_ruby()
58 | ```
59 |
60 | and the `ruby()` function will receive two arguments, the first one for ruby text and the second one for ruby body.
61 | for example, if you'd like to use a `start` alignment.
62 | ```typ
63 | #let ruby = get_ruby(alignment: "start")
64 | #ruby("しずく", "雫")
65 | ```
66 | which the result will be
67 | #let ruby = get_ruby(alignment: "start")
68 | #align(center, ruby("しずく")[雫])
69 |
70 | #let ruby = get_ruby()
71 |
72 | == ruby for content
73 | Besides the `string`, it's also allowed to pass a content except sequence as `ruby()`'s ruby body just like how other element functions do.
74 |
75 | ```typ
76 | #ruby("したた")[滴]る
77 | ```
78 |
79 | will produce
80 |
81 | #align(center)[#ruby("したた")[滴]る]
82 |
83 | Moreover, almost all kinds of content excluding sequence can be passed, which is convenient for some styled text.
84 |
85 | ```typ
86 | #ruby("エックス|せん")[#underline[_*X|ray*_]]
87 | ```
88 | will output
89 | #align(center)[#ruby("エックス|せん")[_*X|ray*_]]
90 |
91 | == mono/group ruby
92 |
93 | ruby-typ supports mono-ruby and group-ruby automatically, which depends on whether the inputs are separated by a delimiter.
94 |
95 | ```typ
96 | #ruby("とうきょうこうぎょうだいがく")[東京工業大学]
97 | #ruby("とう|きょう|こう|ぎょう|だい|がく")[東|京|工|業|大|学]
98 | ```
99 | will generate respectively
100 |
101 | #align(center)[
102 | #ruby("とうきょうこうぎょうだいがく")[東京工業大学]
103 |
104 | #ruby("とう|きょう|こう|ぎょう|だい|がく")[東|京|工|業|大|学]
105 | ]
106 |
107 | As you see, ruby-typ is able to adjust the spaces of the body texts automatically in mono-ruby mode.
108 |
109 | = Thanks
110 | this project is based on #link("https://github.com/SaitoAtsush")[Saito Atsush]'s great idea and the #link("https://zenn.dev/saito_atsushi/articles/ff9490458570e1")[original source codes] posted on their blog
111 |
112 | = Changelog
113 | - [20230425a] Release the initial version.
114 | - [20230426a] a newer implement
115 | - [20230426b] add `pos` option to specify the position of ruby texts
116 |
--------------------------------------------------------------------------------