-
Notifications
You must be signed in to change notification settings - Fork 470
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c2438c3
commit aa2a4ba
Showing
9 changed files
with
323 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
|
||
# Downloaded by `just setup` | ||
/fonts/*.otf | ||
|
||
# Generated by tytanic | ||
/tests/**/.gitignore |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2025 Y.D.X. | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
# Typst Tricorder——占三格法花名册 | ||
|
||
Record Chinese names with a rhythm of three characters. | ||
按占三格的节奏记录中文人名。 | ||
|
||
```typst | ||
#import "@preview/tricorder:0.1.0": tricorder | ||
#tricorder( | ||
.."丁声树、黎锦熙、李荣、陆志韦、陆宗达、吕叔湘、石明远、王力、魏建功、叶籁士、叶圣陶、周定一、周浩然、周祖谟、朱文叔".split("、"), | ||
"成吉思汗(蒙古族,古人)", "张大三", "李小四", | ||
) | ||
``` | ||
|
||
 | ||
|
||
## 用法 Usage | ||
|
||
- [如何控制布局 How to control the layout](#如何控制布局-how-to-control-the-layout) | ||
- [如何记录名字 How to record names](#如何记录名字-how-to-record-names) | ||
|
||
### 如何控制布局 How to control the layout | ||
|
||
- `columns`:列数 Number of columns | ||
|
||
**默认:**`auto`,会填充所有可用空间。若从头设计版面,无需更改。 | ||
|
||
**Default:** `auto`, which will use all available space. This is suitable if you design the whole layout from the beginning. | ||
|
||
可用正的`int`直接规定列数。 | ||
|
||
Use a positive `int` to specify the number of columns explicitly. | ||
|
||
```typst | ||
#tricorder(columns: auto, ..names) | ||
#align( | ||
center, | ||
tricorder(columns: 3, ..names), | ||
) | ||
``` | ||
|
||
 | ||
|
||
- `row-gutter`/`column-gutter`:行/列间隙 Gaps between rows/columns | ||
|
||
**默认:**`1em`,这会在行/列间空一个字。 | ||
|
||
**Default:** `1em`, which will leave a space of one Han character between rows/columns. This is suitable if you have | ||
|
||
如果两个字的名字较多,默认`1em`易引发歧义,可适当增加。 | ||
|
||
If there are too many two-character names, the default `1em` may cause ambiguity. A larger value would be better. | ||
|
||
这两个选项类似[`grid`][grid-gutter],但只有`1em`、`1.5em`、`2em`认真测试过。请谨慎使用其它值。 | ||
|
||
These two arguments are similar to [`grid`][grid-gutter], but only `1em`, `1.5em`, and `2em` are properly tested. Use other values at your own risk. | ||
|
||
[grid-gutter]: https://typst.app/docs/reference/layout/grid/#parameters-column-gutter | ||
|
||
### 如何记录名字 How to record names | ||
|
||
有两种方法。There are two ways. | ||
|
||
- `str`:按标准方法处理 Process in the standard way | ||
|
||
例 Example: `"张飞"` | ||
|
||
- `((span, overflow), …)`:不再自动处理名字或计算列数 Do not touch the name or calculate the span automatically | ||
|
||
例 Example: `((span: 1, overflow: false), "张 飞")` | ||
|
||
- `span`:这个名字占据的列数 The amount of columns spanned by this name | ||
|
||
**默认:**`1` | ||
|
||
**Default:** `1` | ||
|
||
- `overflow`:是否允许溢出到列间 Whether to allow overflowing into the column gutter | ||
|
||
**默认:**`false` | ||
|
||
**Default:** `false` | ||
|
||
此外,`((span: 1, overflow: false), […])`可被省略成`[…]`。 | ||
|
||
Besides, `((span: 1, overflow: false), […])` can be shortened into `[…]`. | ||
|
||
 | ||
|
||
## 实现细节 Implementation detail | ||
|
||
`#tricorder(…)`始终生成块级容器,但固定列数和自动列数的实现不同。 | ||
|
||
`#tricorder(…)` always gives a block-level container, but the implementation of fixed columns and auto columns are different. | ||
|
||
- `#tricorder(columns: 2, …)` ≈ | ||
|
||
```typst | ||
#grid( | ||
columns: (3em,) * 2, | ||
column-gutter: column-gutter, | ||
row-gutter: row-gutter, | ||
[孔乙己], [孔乙己], | ||
) | ||
``` | ||
|
||
- `#tricorder(columns: auto, …)` ≈ | ||
|
||
```typst | ||
#set par(leading: row-gutter) | ||
#block( | ||
box(width: 3em, "孔乙己"), | ||
h(column-gutter, weak: true), | ||
box(width: 3em, "孔乙己"), | ||
) | ||
``` | ||
|
||
固定列数会略快一点。 | ||
|
||
Fixed columns would be slightly faster. |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
#import "./tricorder.typ": tricorder |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
/// U+3000 CJK Symbols and Punctuation IDEOGRAPHIC SPACE | ||
#let SPACE = "\u{3000}" | ||
|
||
/// Insert a space for 2-character names. | ||
/// Do nothing for other names | ||
/// | ||
/// - name (str): | ||
/// -> str | ||
#let fill-name(name) = { | ||
// Name without the annotation | ||
let bare = name.match(regex("^[^(]+")).text | ||
|
||
if bare.clusters().len() == 2 { | ||
let (a, ..b) = name.clusters() | ||
(a, SPACE, ..b).join() | ||
} else { | ||
name | ||
} | ||
} | ||
|
||
/// Measure the number of cells that will be occupied by the `name` | ||
/// | ||
/// - name (str): | ||
/// - column-gutter (length): | ||
/// -> span (int): | ||
/// -> overflow (bool): | ||
#let measure-span(name, column-gutter) = { | ||
let len = name | ||
.clusters() | ||
// TODO: Hard-coded | ||
.map(c => if "·,()".contains(c) { | ||
0.5em | ||
} else { 1em }) | ||
.sum() | ||
|
||
let span = 1 + calc.floor(len / (3em + column-gutter)) | ||
( | ||
span: span, | ||
overflow: span != calc.ceil((len + column-gutter) / (3em + column-gutter)), | ||
) | ||
} | ||
|
||
/// Put blocks before a wall | ||
/// - blocks (array): A list of ((span, overflow), content) | ||
/// - wall (int): Number of available cells in each row | ||
/// -> array | ||
#let put-before-wall(blocks, wall) = { | ||
let result = () | ||
// Rest space to the wall | ||
let rest = wall | ||
|
||
for ((span, overflow), value) in blocks { | ||
assert( | ||
span + int(overflow) <= wall, | ||
message: "impossible to place such a long block: (span: " | ||
+ repr(span) | ||
+ ", overflow: " | ||
+ repr(overflow) | ||
+ ", " | ||
+ repr(value) | ||
+ ")", | ||
) | ||
|
||
if span + int(overflow) > rest { | ||
if rest > 0 { | ||
// Fill the rest space with empty blocks | ||
result.push(((span: rest, overflow: false), none)) | ||
} | ||
// Start a new row | ||
rest = wall | ||
} | ||
|
||
// Put a block | ||
result.push(((span: span, overflow: overflow), value)) | ||
rest -= span | ||
} | ||
|
||
result | ||
} | ||
|
||
#let tricorder(columns: auto, column-gutter: 1em, row-gutter: 1em, ..names) = { | ||
assert(names.named().len() == 0) | ||
let names = names.pos() | ||
assert(columns == auto or (type(columns) == int and columns > 0)) | ||
|
||
// Convert `names` to a list of `((span, overflow), name)` | ||
|
||
let default-span = (span: 1, overflow: false) | ||
let spans-and-names = names | ||
.map(n => if type(n) == str { | ||
// Fill name for regular names | ||
fill-name(n) | ||
} else { | ||
// Skip others | ||
n | ||
}) | ||
.map(n => if type(n) == str { | ||
// Measure span for regular names | ||
(measure-span(n, column-gutter), n) | ||
} else if type(n) != array { | ||
// Set default span for content-ish items | ||
(default-span, n) | ||
} else { | ||
// Parse others | ||
assert( | ||
n.len() == 2 | ||
and type(n.first()) == dictionary | ||
and (not n.first().keys().contains("span") or (type(n.first().span) == int and n.first().span > 0)) | ||
and (not n.first().keys().contains("overflow") or type(n.first().overflow) == bool), | ||
message: "fail to parse as ((span, overflow), name): " + repr(n), | ||
) | ||
let (span, name) = n | ||
(default-span + span, name) | ||
}) | ||
|
||
// TODO: Hard-coded | ||
show "(": it => h(-0.5em) + it | ||
show regex("[,)]"): it => it + h(-0.5em) | ||
|
||
// Write out contents | ||
|
||
if columns == auto { | ||
// Auto columns | ||
block({ | ||
set par(leading: row-gutter) | ||
|
||
// The whole block can be arbitrarily aligned, | ||
// but its last line is always aligned left. | ||
set align(start) | ||
|
||
spans-and-names | ||
.map((((span, overflow), name)) => ( | ||
// If not overflow, write `name` and a weak gutter. | ||
// Otherwise, write `name` and the gutter into a single box. | ||
box( | ||
width: (3em + column-gutter) * span - if not overflow { column-gutter } else { 0em }, | ||
name, | ||
), | ||
if not overflow { h(column-gutter, weak: true) } else { none }, | ||
)) | ||
.flatten() | ||
.join() | ||
}) | ||
} else { | ||
// Fixed columns | ||
grid( | ||
columns: (3em,) * columns, | ||
column-gutter: column-gutter, | ||
row-gutter: row-gutter, | ||
align: start, | ||
..put-before-wall(spans-and-names, columns).map((((span, overflow), name)) => grid.cell( | ||
colspan: span, | ||
inset: if overflow { (right: -column-gutter) } else { (:) }, | ||
name, | ||
)) | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
[package] | ||
name = "tricorder" | ||
version = "0.1.0" | ||
entrypoint = "src/lib.typ" | ||
authors = ["Y.D.X."] | ||
license = "MIT" | ||
description = "按占三格的节奏记录中文人名(花名册)Record Chinese names with a rhythm of three characters" | ||
repository = "https://github.com/YDX-2147483647/typst-tricorder" | ||
categories = [ | ||
"components", | ||
"layout", | ||
"languages", | ||
] | ||
exclude = [ | ||
"assets", | ||
] |