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