├── .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 | --------------------------------------------------------------------------------