Skip to content

Commit 5d8f0f0

Browse files
committed
feat(ctm): TextObject CTM implementation
1 parent 65ff95d commit 5d8f0f0

File tree

4 files changed

+85
-1
lines changed

4 files changed

+85
-1
lines changed

learning/ofd_notes.md

+3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66

77
参考: [http://wiki.easyofd.cn/OFD标准/页面描述](http://wiki.easyofd.cn/OFD%E6%A0%87%E5%87%86/%E9%A1%B5%E9%9D%A2%E6%8F%8F%E8%BF%B0) 中的 “变换矩阵 CTM” 部分。
88

9+
10+
理解矩阵变换: [Transformation with cairo](https://gist.github.com/bert/1164354/c0391388afffc4b287c46ac79287f77e94c712e3)
11+
912
### TextObject
1013

1114
> pdf page 63

src/elements.rs

+4
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,8 @@ pub struct PathObject {
338338
pub line_width: f64,
339339
pub stroke: Option<bool>,
340340
pub stroke_color: Option<Color>,
341+
#[serde(rename = "CTM")]
342+
pub ctm: Option<String>,
341343
}
342344

343345
#[derive(Debug, Deserialize, Clone)]
@@ -378,6 +380,8 @@ pub struct TextObject {
378380
pub size: f64,
379381
pub fill_color: Option<Color>,
380382
pub text_code: TextCode,
383+
#[serde(rename = "CTM")]
384+
pub ctm: Option<String>,
381385
}
382386

383387
#[derive(Debug, Deserialize, Clone)]

src/render.rs

+51-1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ impl Renderable for PathObject {
4242
_ofd: &mut Ofd, _document: &Document) -> Result<(), Error> {
4343
context.save()?;
4444

45+
// TODO(hualet): implement ctm.
4546
let boundary = ct::Box::from(self.boundary.clone()).to_pixel();
4647
let color = ct::Color::from(
4748
self.stroke_color.as_ref().unwrap().value.clone());
@@ -89,8 +90,21 @@ impl Renderable for TextObject {
8990
context.set_source_rgb(fill_color.value[0] as f64 / 255.0,
9091
fill_color.value[1] as f64 / 255.0,
9192
fill_color.value[2] as f64 / 255.0);
92-
context.move_to(boundary.x as f64 + mmtopx(self.text_code.x),
93+
94+
// NOTE(hualet): transform should be used together with translate,
95+
// so the coordinate system is correct.
96+
// THEY ARE BOTH TRANSFORMATIONS!
97+
context.translate(boundary.x as f64 + mmtopx(self.text_code.x),
9398
boundary.y as f64 + mmtopx(self.text_code.y));
99+
if let Some(ctm) = self.ctm.as_ref() {
100+
debug!("render text object:{:?} with ctm: {:?}",
101+
self.text_code.value, ctm);
102+
let matrix = ct::Matrix::from(ctm.clone());
103+
let cairo_matrix: cairo::Matrix = matrix.into();
104+
context.transform(cairo_matrix);
105+
}
106+
107+
context.move_to(0., 0.);
94108
context.show_text(self.text_code.value.as_str())?;
95109

96110
context.restore()
@@ -103,6 +117,7 @@ impl Renderable for ImageObject {
103117
ofd: &mut Ofd, document: &Document) -> Result<(), Error> {
104118
context.save()?;
105119

120+
// TODO(hualet): implement ctm.
106121
let boundary = ct::Box::from(self.boundary.clone()).to_pixel();
107122

108123
// find the image file:
@@ -178,4 +193,39 @@ fn _render_page_block(events: Vec<Event>, context: &mut cairo::Context,
178193
}
179194

180195
Ok(())
196+
}
197+
198+
/*
199+
ct::Matrix
200+
201+
| a b 0 |
202+
| c d 0 |
203+
| e f 1 |
204+
205+
x'=ax+cy+e
206+
y'=bx+dy+f
207+
208+
209+
cairo::Matrix
210+
211+
typedef struct {
212+
double xx; double yx;
213+
double xy; double yy;
214+
double x0; double y0;
215+
} cairo_matrix_t;
216+
217+
x_new = xx * x + xy * y + x0;
218+
y_new = yx * x + yy * y + y0;
219+
*/
220+
impl From<ct::Matrix> for cairo::Matrix {
221+
fn from(value: ct::Matrix) -> Self {
222+
Self::new(
223+
value.a, // xx
224+
value.b, // yx
225+
value.c, // xy
226+
value.d, // yy
227+
value.e, // x0
228+
value.f // y0
229+
)
230+
}
181231
}

src/types.rs

+27
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,16 @@ pub mod ct {
3535
pub alpha: i32,
3636
}
3737

38+
#[derive(Debug)]
39+
pub struct Matrix {
40+
pub a: f64,
41+
pub b: f64,
42+
pub c: f64,
43+
pub d: f64,
44+
pub e: f64,
45+
pub f: f64,
46+
}
47+
3848
// implement string to PageArea
3949
impl From<String> for PageArea {
4050
fn from(value: String) -> Self {
@@ -59,6 +69,23 @@ pub mod ct {
5969
}
6070
}
6171

72+
impl From<String> for Matrix {
73+
fn from(value: String) -> Self {
74+
let parts: Vec<f64> = value.split_whitespace().map(|s| s.parse().unwrap()).collect();
75+
// According to the spec, the matrix should be 6 elements long.
76+
assert_eq!(parts.len(), 6);
77+
78+
Matrix {
79+
a: parts[0],
80+
b: parts[1],
81+
c: parts[2],
82+
d: parts[3],
83+
e: parts[4],
84+
f: parts[5]
85+
}
86+
}
87+
}
88+
6289
// implement string to Box
6390
impl From<String> for Box {
6491
fn from(value: String) -> Self {

0 commit comments

Comments
 (0)