pdfium_render/pdf/path/
segment.rs1use crate::bindgen::{
5 FPDF_PATHSEGMENT, FPDF_SEGMENT_BEZIERTO, FPDF_SEGMENT_LINETO, FPDF_SEGMENT_MOVETO,
6 FPDF_SEGMENT_UNKNOWN,
7};
8use crate::bindings::PdfiumLibraryBindings;
9use crate::error::PdfiumError;
10use crate::pdf::matrix::PdfMatrix;
11use crate::pdf::points::PdfPoints;
12use std::os::raw::c_float;
13
14#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
16pub enum PdfPathSegmentType {
17 Unknown = FPDF_SEGMENT_UNKNOWN as isize,
18 LineTo = FPDF_SEGMENT_LINETO as isize,
19 BezierTo = FPDF_SEGMENT_BEZIERTO as isize,
20 MoveTo = FPDF_SEGMENT_MOVETO as isize,
21}
22
23impl PdfPathSegmentType {
24 #[inline]
25 pub(crate) fn from_pdfium(segment_type: i32) -> Result<PdfPathSegmentType, PdfiumError> {
26 if segment_type == FPDF_SEGMENT_UNKNOWN {
27 return Ok(PdfPathSegmentType::Unknown);
28 }
29
30 match segment_type as u32 {
31 FPDF_SEGMENT_LINETO => Ok(PdfPathSegmentType::LineTo),
32 FPDF_SEGMENT_BEZIERTO => Ok(PdfPathSegmentType::BezierTo),
33 FPDF_SEGMENT_MOVETO => Ok(PdfPathSegmentType::MoveTo),
34 _ => Err(PdfiumError::UnknownPathSegmentType),
35 }
36 }
37}
38
39pub struct PdfPathSegment<'a> {
41 handle: FPDF_PATHSEGMENT,
42 matrix: Option<PdfMatrix>,
43 bindings: &'a dyn PdfiumLibraryBindings,
44}
45
46impl<'a> PdfPathSegment<'a> {
47 #[inline]
48 pub(crate) fn from_pdfium(
49 handle: FPDF_PATHSEGMENT,
50 matrix: Option<PdfMatrix>,
51 bindings: &'a dyn PdfiumLibraryBindings,
52 ) -> Self {
53 Self {
54 handle,
55 matrix,
56 bindings,
57 }
58 }
59
60 #[inline]
62 pub fn bindings(&self) -> &'a dyn PdfiumLibraryBindings {
63 self.bindings
64 }
65
66 #[inline]
68 pub fn segment_type(&self) -> PdfPathSegmentType {
69 PdfPathSegmentType::from_pdfium(self.bindings().FPDFPathSegment_GetType(self.handle))
70 .unwrap_or(PdfPathSegmentType::Unknown)
71 }
72
73 #[inline]
75 pub fn is_close(&self) -> bool {
76 self.bindings()
77 .is_true(self.bindings().FPDFPathSegment_GetClose(self.handle))
78 }
79
80 pub fn point(&self) -> (PdfPoints, PdfPoints) {
82 let mut x: c_float = 0.0;
83
84 let mut y: c_float = 0.0;
85
86 if self
87 .bindings()
88 .is_true(
89 self.bindings()
90 .FPDFPathSegment_GetPoint(self.handle, &mut x, &mut y),
91 )
92 {
93 let x = PdfPoints::new(x as f32);
94
95 let y = PdfPoints::new(y as f32);
96
97 match self.matrix.as_ref() {
98 None => (x, y),
99 Some(matrix) => matrix.apply_to_points(x, y),
100 }
101 } else {
102 (PdfPoints::ZERO, PdfPoints::ZERO)
103 }
104 }
105
106 #[inline]
108 pub fn x(&self) -> PdfPoints {
109 self.point().0
110 }
111
112 #[inline]
114 pub fn y(&self) -> PdfPoints {
115 self.point().1
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use crate::prelude::*;
122 use crate::utils::test::test_bind_to_pdfium;
123
124 #[test]
125 fn test_point_transform() {
126 let pdfium = test_bind_to_pdfium();
127
128 let mut document = pdfium.create_new_pdf().unwrap();
129
130 let mut page = document
131 .pages_mut()
132 .create_page_at_start(PdfPagePaperSize::a4())
133 .unwrap();
134
135 let object = page
136 .objects_mut()
137 .create_path_object_line(
138 PdfPoints::new(100.0),
139 PdfPoints::new(200.0),
140 PdfPoints::new(300.0),
141 PdfPoints::new(400.0),
142 PdfColor::BEIGE,
143 PdfPoints::new(1.0),
144 )
145 .unwrap();
146
147 let delta_x = PdfPoints::new(50.0);
148 let delta_y = PdfPoints::new(-25.0);
149
150 let matrix = PdfMatrix::identity().translate(delta_x, delta_y).unwrap();
151
152 let raw_segment_0 = object.as_path_object().unwrap().segments().get(0).unwrap();
153 let raw_segment_1 = object.as_path_object().unwrap().segments().get(1).unwrap();
154
155 let transformed_segment_0 = object
156 .as_path_object()
157 .unwrap()
158 .segments()
159 .transform(matrix)
160 .get(0)
161 .unwrap();
162
163 let transformed_segment_1 = object
164 .as_path_object()
165 .unwrap()
166 .segments()
167 .transform(matrix)
168 .get(1)
169 .unwrap();
170
171 assert_eq!(transformed_segment_0.x(), raw_segment_0.x() + delta_x);
172 assert_eq!(transformed_segment_0.y(), raw_segment_0.y() + delta_y);
173 assert_eq!(transformed_segment_1.x(), raw_segment_1.x() + delta_x);
174 assert_eq!(transformed_segment_1.y(), raw_segment_1.y() + delta_y);
175 }
176
177 #[test]
178 fn test_point_transform_during_iteration() {
179 let pdfium = test_bind_to_pdfium();
180
181 let mut document = pdfium.create_new_pdf().unwrap();
182
183 let mut page = document
184 .pages_mut()
185 .create_page_at_start(PdfPagePaperSize::a4())
186 .unwrap();
187
188 let object = page
189 .objects_mut()
190 .create_path_object_line(
191 PdfPoints::new(100.0),
192 PdfPoints::new(200.0),
193 PdfPoints::new(300.0),
194 PdfPoints::new(400.0),
195 PdfColor::BEIGE,
196 PdfPoints::new(1.0),
197 )
198 .unwrap();
199
200 let raw_points: Vec<(PdfPoints, PdfPoints)> = object
201 .as_path_object()
202 .unwrap()
203 .segments()
204 .iter()
205 .map(|segment| segment.point())
206 .collect();
207
208 let delta_x = PdfPoints::new(50.0);
209 let delta_y = PdfPoints::new(-25.0);
210
211 let matrix = PdfMatrix::identity().translate(delta_x, delta_y).unwrap();
212
213 let transformed_points: Vec<(PdfPoints, PdfPoints)> = object
214 .as_path_object()
215 .unwrap()
216 .segments()
217 .transform(matrix)
218 .iter()
219 .map(|segment| segment.point())
220 .collect();
221
222 for (raw, transformed) in raw_points.iter().zip(transformed_points) {
223 assert_eq!(transformed.0, raw.0 + delta_x);
224 assert_eq!(transformed.1, raw.1 + delta_y);
225 }
226 }
227}