skia_safe/docs/
pdf_document.rs

1pub mod pdf {
2    use std::{ffi::CString, fmt, io, mem, ptr};
3
4    use crate::Canvas;
5
6    use skia_bindings::{
7        self as sb, SkPDF_AttributeList, SkPDF_DateTime, SkPDF_Metadata, SkPDF_StructureElementNode,
8    };
9
10    use crate::{
11        interop::{AsStr, RustWStream, SetStr},
12        prelude::*,
13        scalar, Document, MILESTONE,
14    };
15
16    pub type AttributeList = Handle<SkPDF_AttributeList>;
17    unsafe_send_sync!(AttributeList);
18
19    impl NativeDrop for SkPDF_AttributeList {
20        fn drop(&mut self) {
21            unsafe { sb::C_SkPDF_AttributeList_destruct(self) }
22        }
23    }
24
25    impl Default for AttributeList {
26        fn default() -> Self {
27            AttributeList::from_native_c(unsafe { SkPDF_AttributeList::new() })
28        }
29    }
30
31    impl fmt::Debug for AttributeList {
32        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33            f.debug_struct("AttributeList").finish()
34        }
35    }
36
37    /// Attributes for nodes in the PDF tree.
38    ///
39    /// Each attribute must have an owner (e.g. "Layout", "List", "Table", etc)
40    /// and an attribute name (e.g. "BBox", "RowSpan", etc.) from PDF32000_2008 14.8.5,
41    /// and then a value of the proper type according to the spec.
42    impl AttributeList {
43        pub fn append_int(
44            &mut self,
45            owner: impl AsRef<str>,
46            name: impl AsRef<str>,
47            value: i32,
48        ) -> &mut Self {
49            let owner = CString::new(owner.as_ref()).unwrap();
50            let name = CString::new(name.as_ref()).unwrap();
51            unsafe {
52                self.native_mut()
53                    .appendInt(owner.as_ptr(), name.as_ptr(), value)
54            }
55            self
56        }
57
58        pub fn append_float(
59            &mut self,
60            owner: impl AsRef<str>,
61            name: impl AsRef<str>,
62            value: f32,
63        ) -> &mut Self {
64            let owner = CString::new(owner.as_ref()).unwrap();
65            let name = CString::new(name.as_ref()).unwrap();
66            unsafe {
67                self.native_mut()
68                    .appendFloat(owner.as_ptr(), name.as_ptr(), value)
69            }
70            self
71        }
72
73        pub fn append_float_array(
74            &mut self,
75            owner: impl AsRef<str>,
76            name: impl AsRef<str>,
77            value: &[f32],
78        ) -> &mut Self {
79            let owner = CString::new(owner.as_ref()).unwrap();
80            let name = CString::new(name.as_ref()).unwrap();
81            unsafe {
82                sb::C_SkPDF_AttributeList_appendFloatArray(
83                    self.native_mut(),
84                    owner.as_ptr(),
85                    name.as_ptr(),
86                    value.as_ptr(),
87                    value.len(),
88                )
89            }
90            self
91        }
92    }
93
94    #[repr(transparent)]
95    pub struct StructureElementNode(ptr::NonNull<SkPDF_StructureElementNode>);
96
97    impl NativeAccess for StructureElementNode {
98        type Native = SkPDF_StructureElementNode;
99
100        fn native(&self) -> &SkPDF_StructureElementNode {
101            unsafe { self.0.as_ref() }
102        }
103        fn native_mut(&mut self) -> &mut SkPDF_StructureElementNode {
104            unsafe { self.0.as_mut() }
105        }
106    }
107
108    impl Drop for StructureElementNode {
109        fn drop(&mut self) {
110            unsafe { sb::C_SkPDF_StructureElementNode_delete(self.native_mut()) }
111        }
112    }
113
114    impl Default for StructureElementNode {
115        fn default() -> Self {
116            Self(ptr::NonNull::new(unsafe { sb::C_SkPDF_StructureElementNode_new() }).unwrap())
117        }
118    }
119
120    impl fmt::Debug for StructureElementNode {
121        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122            f.debug_struct("StructureElementNode")
123                .field("type_string", &self.type_string())
124                .field("child_vector", &self.child_vector())
125                .field("node_id", &self.node_id())
126                .field("attributes", &self.attributes())
127                .field("alt", &self.alt())
128                .field("lang", &self.lang())
129                .finish()
130        }
131    }
132
133    /// A node in a PDF structure tree, giving a semantic representation
134    /// of the content.  Each node ID is associated with content
135    /// by passing the [`crate::Canvas`] and node ID to [`set_node_id()`] when drawing.
136    /// NodeIDs should be unique within each tree.
137    impl StructureElementNode {
138        pub fn new(type_string: impl AsRef<str>) -> Self {
139            let mut node = Self::default();
140            node.set_type_string(type_string);
141            node
142        }
143
144        pub fn set_type_string(&mut self, type_string: impl AsRef<str>) -> &mut Self {
145            self.native_mut().fTypeString.set_str(type_string);
146            self
147        }
148
149        pub fn type_string(&self) -> &str {
150            self.native().fTypeString.as_str()
151        }
152
153        pub fn set_child_vector(
154            &mut self,
155            mut child_vector: Vec<StructureElementNode>,
156        ) -> &mut Self {
157            // strategy is to move them out by setting them to nullptr (drop() will handle a nullptr on the rust side)
158            unsafe {
159                sb::C_SkPDF_StructureElementNode_setChildVector(
160                    self.native_mut(),
161                    child_vector.as_mut_ptr() as _,
162                    child_vector.len(),
163                )
164            }
165            self
166        }
167
168        pub fn append_child(&mut self, node: StructureElementNode) -> &mut Self {
169            unsafe {
170                sb::C_SkPDF_StructElementNode_appendChild(self.native_mut(), node.0.as_ptr());
171            }
172            mem::forget(node);
173            self
174        }
175
176        pub fn child_vector(&self) -> &[StructureElementNode] {
177            let mut ptr = ptr::null();
178            unsafe {
179                let len = sb::C_SkPDF_StructureElementNode_getChildVector(self.native(), &mut ptr);
180                safer::from_raw_parts(ptr as _, len)
181            }
182        }
183
184        pub fn set_node_id(&mut self, node_id: i32) -> &mut Self {
185            self.native_mut().fNodeId = node_id;
186            self
187        }
188
189        pub fn node_id(&self) -> i32 {
190            self.native().fNodeId
191        }
192
193        pub fn attributes(&self) -> &AttributeList {
194            AttributeList::from_native_ref(&self.native().fAttributes)
195        }
196
197        pub fn attributes_mut(&mut self) -> &mut AttributeList {
198            AttributeList::from_native_ref_mut(&mut self.native_mut().fAttributes)
199        }
200
201        pub fn set_alt(&mut self, alt: impl AsRef<str>) -> &mut Self {
202            self.native_mut().fAlt.set_str(alt);
203            self
204        }
205
206        pub fn alt(&self) -> &str {
207            self.native().fAlt.as_str()
208        }
209
210        pub fn set_lang(&mut self, lang: impl AsRef<str>) -> &mut Self {
211            self.native_mut().fLang.set_str(lang);
212            self
213        }
214
215        pub fn lang(&self) -> &str {
216            self.native().fLang.as_str()
217        }
218    }
219
220    #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
221    #[repr(C)]
222    pub struct DateTime {
223        /// The number of minutes that this is ahead of or behind UTC.
224        pub time_zone_minutes: i16,
225        /// e.g. 2005
226        pub year: u16,
227        /// 1..12
228        pub month: u8,
229        /// 0..6, 0==Sunday
230        pub day_of_week: u8,
231        /// 1..31
232        pub day: u8,
233        /// 0..23
234        pub hour: u8,
235        /// 0..59
236        pub minute: u8,
237        /// 0..59
238        pub second: u8,
239    }
240
241    native_transmutable!(SkPDF_DateTime, DateTime, date_time_layout);
242
243    /// Optional metadata to be passed into the PDF factory function.
244    #[derive(Debug)]
245    pub struct Metadata {
246        /// The document's title.
247        pub title: String,
248        /// The name of the person who created the document.
249        pub author: String,
250        /// The subject of the document.
251        pub subject: String,
252        /// Keywords associated with the document. Commas may be used to delineate keywords within
253        /// the string.
254        pub keywords: String,
255        /// If the document was converted to PDF from another format, the name of the conforming
256        /// product that created the original document from which it was converted.
257        pub creator: String,
258        /// The product that is converting this document to PDF.
259        pub producer: String,
260        /// The date and time the document was created.
261        pub creation: Option<DateTime>,
262        /// The date and time the document was most recently modified.
263        pub modified: Option<DateTime>,
264        /// The natural language of the text in the PDF. If `lang` is empty, the root
265        /// StructureElementNode::lang will be used (if not empty). Text not in
266        /// this language should be marked with StructureElementNode::lang.
267        pub lang: String,
268        /// The DPI (pixels-per-inch) at which features without native PDF support
269        /// will be rasterized (e.g. draw image with perspective, draw text with
270        /// perspective, ...)  A larger DPI would create a PDF that reflects the
271        /// original intent with better fidelity, but it can make for larger PDF
272        /// files too, which would use more memory while rendering, and it would be
273        /// slower to be processed or sent online or to printer.
274        pub raster_dpi: Option<scalar>,
275        /// If `true`, include XMP metadata, a document UUID, and `s_rgb` output intent
276        /// information.  This adds length to the document and makes it
277        /// non-reproducible, but are necessary features for PDF/A-2b conformance
278        pub pdf_a: bool,
279        /// Encoding quality controls the trade-off between size and quality. By default this is set
280        /// to 101 percent, which corresponds to lossless encoding. If this value is set to a value
281        /// <= 100, and the image is opaque, it will be encoded (using JPEG) with that quality
282        /// setting.
283        pub encoding_quality: Option<i32>,
284
285        pub structure_element_tree_root: Option<StructureElementNode>,
286
287        pub outline: Outline,
288
289        /// PDF streams may be compressed to save space.
290        /// Use this to specify the desired compression vs time tradeoff.
291        pub compression_level: CompressionLevel,
292    }
293
294    impl Default for Metadata {
295        fn default() -> Self {
296            Self {
297                title: Default::default(),
298                author: Default::default(),
299                subject: Default::default(),
300                keywords: Default::default(),
301                creator: Default::default(),
302                producer: format!("Skia/PDF m{MILESTONE}"),
303                creation: Default::default(),
304                modified: Default::default(),
305                lang: Default::default(),
306                raster_dpi: Default::default(),
307                pdf_a: Default::default(),
308                encoding_quality: Default::default(),
309                structure_element_tree_root: None,
310                outline: Outline::None,
311                compression_level: Default::default(),
312            }
313        }
314    }
315
316    pub type Outline = skia_bindings::SkPDF_Metadata_Outline;
317    variant_name!(Outline::StructureElements);
318
319    pub type CompressionLevel = skia_bindings::SkPDF_Metadata_CompressionLevel;
320    variant_name!(CompressionLevel::HighButSlow);
321
322    /// Create a PDF-backed document.
323    ///
324    /// PDF pages are sized in point units. 1 pt == 1/72 inch == 127/360 mm.
325    ///
326    /// * `metadata` - a PDFmetadata object.  Any fields may be left empty.
327    ///
328    /// @returns `None` if there is an error, otherwise a newly created PDF-backed [`Document`].
329    pub fn new_document<'a>(
330        writer: &'a mut impl io::Write,
331        metadata: Option<&Metadata>,
332    ) -> Document<'a> {
333        let mut md = InternalMetadata::default();
334        if let Some(metadata) = metadata {
335            let internal = md.native_mut();
336            internal.fTitle.set_str(&metadata.title);
337            internal.fAuthor.set_str(&metadata.author);
338            internal.fSubject.set_str(&metadata.subject);
339            internal.fKeywords.set_str(&metadata.keywords);
340            internal.fCreator.set_str(&metadata.creator);
341            internal.fProducer.set_str(&metadata.producer);
342            if let Some(creation) = metadata.creation {
343                internal.fCreation = creation.into_native();
344            }
345            if let Some(modified) = metadata.modified {
346                internal.fModified = modified.into_native();
347            }
348            internal.fLang.set_str(&metadata.lang);
349            if let Some(raster_dpi) = metadata.raster_dpi {
350                internal.fRasterDPI = raster_dpi;
351            }
352            internal.fPDFA = metadata.pdf_a;
353            if let Some(encoding_quality) = metadata.encoding_quality {
354                internal.fEncodingQuality = encoding_quality
355            }
356            if let Some(structure_element_tree) = &metadata.structure_element_tree_root {
357                internal.fStructureElementTreeRoot = structure_element_tree.0.as_ptr();
358            }
359            internal.fOutline = metadata.outline;
360            internal.fCompressionLevel = metadata.compression_level
361        }
362
363        // We enable harfbuzz font sub-setting in PDF documents if textlayout is enabled.
364        #[cfg(all(feature = "textlayout", feature = "embed-icudtl"))]
365        crate::icu::init();
366
367        let mut stream = RustWStream::new(writer);
368        let document = RCHandle::from_ptr(unsafe {
369            sb::C_SkPDF_MakeDocument(stream.stream_mut(), md.native())
370        })
371        .unwrap();
372
373        Document::new(stream, document)
374    }
375
376    //
377    // Helper for constructing the internal metadata struct and setting associated strings.
378    //
379
380    type InternalMetadata = Handle<SkPDF_Metadata>;
381
382    impl NativeDrop for SkPDF_Metadata {
383        fn drop(&mut self) {
384            unsafe { sb::C_SkPDF_Metadata_destruct(self) }
385        }
386    }
387
388    impl Default for Handle<SkPDF_Metadata> {
389        fn default() -> Self {
390            Self::construct(|pdf_md| unsafe { sb::C_SkPDF_Metadata_Construct(pdf_md) })
391        }
392    }
393
394    pub mod node_id {
395        pub const NOTHING: i32 = 0;
396        pub const OTHER_ARTIFACT: i32 = -1;
397        pub const PAGINATION_ARTIFACT: i32 = -2;
398        pub const PAGINATION_HEADER_ARTIFACT: i32 = -3;
399        pub const PAGINATION_FOOTER_ARTIFACT: i32 = -4;
400        pub const PAGINATION_WATERMARK_ARTIFACT: i32 = -5;
401        pub const LAYOUT_ARTIFACT: i32 = -6;
402        pub const PAGE_ARTIFACT: i32 = -7;
403        pub const BACKGROUND_ARTIFACT: i32 = -8;
404    }
405
406    pub fn set_node_id(canvas: &Canvas, node_id: i32) {
407        unsafe {
408            sb::C_SkPDF_SetNodeId(canvas.native_mut(), node_id);
409        }
410    }
411}
412
413#[cfg(test)]
414mod tests {
415    use crate::pdf::StructureElementNode;
416
417    use super::pdf;
418
419    #[test]
420    fn create_attribute_list() {
421        let mut _al = pdf::AttributeList::default();
422        _al.append_float_array("Owner", "Name", &[1.0, 2.0, 3.0]);
423    }
424
425    #[test]
426    fn structure_element_node_child_vector() {
427        let mut root = StructureElementNode::new("root");
428        root.append_child(StructureElementNode::new("nested"));
429        root.append_child(StructureElementNode::new("nested2"));
430        let v = root.child_vector();
431        assert_eq!(v[0].type_string(), "nested");
432        assert_eq!(v[1].type_string(), "nested2");
433    }
434}