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
//! 数式を suki で組んで、ページに置くものに直す
//!
//! suki は位置と寸法を em で返す。ここでは字の大きさを掛けて点に直し、
//! 字形をそのまま PDF へ渡す。字形の番号はフォントのものなので、埋め込みと合う。
//!
//! 数式の書体が持たない字(`\text{}` の中の日本語)は和文の書体で置く。
use crate::draw::{Letter, Mark};
use crate::error::Error;
use crate::font::{self, Library};
use suki::layout::{Content, Layout};
/// 組み上がったもの
#[derive(Debug, Clone, PartialEq)]
pub struct Built {
/// 左端とベースラインを原点として置いたもの
pub mark: Vec<Mark>,
pub width: f64,
/// ベースラインから上
pub height: f64,
/// ベースラインから下
pub depth: f64,
}
/// 数式を組む。size は字の大きさ(点)
pub fn build(source: &str, library: &Library, size: f64, display: bool) -> Result<Built, Error> {
let node = suki::parse(source)
.map_err(|reason| Error::new(format!("数式が読めません: ${source}$: {reason}"), 0))?;
let face = &library.face[library.role.math];
let placed = suki::layout::layout(&node, &face.font, 1.0, suki::Style::Auto, display);
let mut mark = Vec::new();
write(&placed, library, size, 0.0, 0.0, &mut mark);
Ok(Built {
mark,
width: placed.width * size,
height: placed.height * size,
depth: placed.depth * size,
})
}
/// 組み上がったものをたどって、置くものに直す
fn write(placed: &Layout, library: &Library, size: f64, x: f64, y: f64, out: &mut Vec<Mark>) {
match &placed.content {
Content::Glyph {text, glyph, size: em, stretch, ..} => {
let (face, found) = choose(library, *glyph, text);
if found == 0 {
return;
}
// 伸ばす率は、文字の当たっている字形にだけ掛ける。
// 当たっていない字形は、フォントが大きさ違いを持っているのでそのままでよい
let vertical = if text.is_empty() {1.0} else {*stretch};
out.push(Mark::Text {
face,
size: em * size,
stretch: vertical,
x: x * size,
y: y * size,
letter: vec![Letter {glyph: found, text: text.clone()}],
});
}
Content::Rule => out.push(Mark::Rule {
x: x * size,
y: (y - placed.height) * size,
width: placed.width * size,
height: (placed.height + placed.depth) * size,
}),
Content::Group(children) => {
for child in children {
write(&child.layout, library, size, x + child.x, y + child.y, out);
}
}
}
}
/// 字形が数式の書体にないときは、和文か地の文の書体から探す
fn choose(library: &Library, glyph: u16, text: &str) -> (usize, u16) {
if glyph != 0 {
return (library.role.math, glyph);
}
let character = match text.chars().next() {
Some(character) => character,
None => return (library.role.math, 0),
};
for face in [library.role.japanese, library.role.roman] {
if let Some(found) = font::glyph(&library.face[face], character) {
return (face, found);
}
}
(library.role.math, 0)
}