xmp_writer/
lib.rs

1/*!
2*Write XMP metadata, step by step.*
3
4XMP is a metadata format developed by Adobe. It is either embedded into
5files (e.g. PDF, JPEG, TIFF) or stored in a separate "side-car" file.
6
7This crate provides a simple API to write XMP metadata. Start by creating
8a new [`XmpWriter`], then add entries to it. Finally, call [`XmpWriter::finish`] to
9get the XMP metadata as a byte vector. Some properties contain a complex data type like a
10struct or an array. In this case, the writer returns a new struct that can be used to
11write the data. The reference to the struct must be dropped before the writer can be used
12again.
13
14## Example
15
16```rust
17use xmp_writer::{LangId, DateTime, XmpWriter};
18
19let mut writer = XmpWriter::new();
20writer.creator(["Martin Haug"]);
21writer.title([(Some(LangId("de")), "Titel"), (None, "Title")]);
22writer.num_pages(3);
23writer.pdf_keywords("Keyword1, Keyword2");
24writer.description([(None, "Description")]);
25writer.date([DateTime::date(2021, 11, 06)]);
26
27let mut colors = writer.colorants();
28colors.add_colorant().swatch_name("Red");
29colors.add_colorant().swatch_name("Green");
30drop(colors);
31
32writer.creator_tool("xmp-writer 0.3.0");
33
34println!("{}", writer.finish(None));
35```
36
37## See also
38- [XMP Specification, Part 1: Basics](https://github.com/adobe/XMP-Toolkit-SDK/blob/main/docs/XMPSpecificationPart1.pdf)
39- [XMP Specification, Part 2: Additional Properties](https://github.com/adobe/XMP-Toolkit-SDK/blob/main/docs/XMPSpecificationPart2.pdf)
40- [XMP Specification, Part 3: File Embedding and Interchange](https://github.com/adobe/XMP-Toolkit-SDK/blob/main/docs/XMPSpecificationPart3.pdf)
41*/
42
43#![deny(missing_docs)]
44
45#[cfg(feature = "pdfa")]
46pub mod pdfa;
47mod types;
48
49use std::collections::BTreeSet;
50use std::fmt::Write;
51
52#[cfg(feature = "pdfa")]
53use pdfa::PdfAExtSchemasWriter;
54
55pub use types::*;
56
57/// Implements `Deref` and `DerefMut` by delegating to a field of a struct.
58macro_rules! deref {
59    ($a:lifetime, $b:lifetime, $from:ty => $to:ty, $field:ident) => {
60        impl<$a, $b> std::ops::Deref for $from {
61            type Target = $to;
62
63            #[inline]
64            fn deref(&self) -> &Self::Target {
65                &self.$field
66            }
67        }
68
69        impl<$a, $b> std::ops::DerefMut for $from {
70            #[inline]
71            fn deref_mut(&mut self) -> &mut Self::Target {
72                &mut self.$field
73            }
74        }
75    };
76}
77pub(crate) use deref;
78
79/// The main writer struct.
80///
81/// Use [`XmpWriter::new`] to create a new instance and get the resulting XMP
82/// metadata by calling [`XmpWriter::finish`].
83#[derive(Default)]
84pub struct XmpWriter<'a> {
85    pub(crate) buf: String,
86    namespaces: BTreeSet<Namespace<'a>>,
87}
88
89impl<'n> XmpWriter<'n> {
90    /// Create a new XMP writer.
91    pub fn new() -> XmpWriter<'n> {
92        Self::default()
93    }
94
95    /// Add a custom element to the XMP metadata.
96    #[inline]
97    pub fn element<'a>(
98        &'a mut self,
99        name: &'a str,
100        namespace: Namespace<'n>,
101    ) -> Element<'a, 'n> {
102        Element::start(self, name, namespace)
103    }
104
105    /// Finish the XMP metadata and return it as a byte vector.
106    pub fn finish(self, about: Option<&str>) -> String {
107        let mut buf = String::with_capacity(280 + self.buf.len());
108        buf.push_str("<?xpacket begin=\"\u{feff}\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>");
109
110        write!(
111            &mut buf,
112            "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\" x:xmptk=\"xmp-writer\"><rdf:RDF xmlns:rdf=\"{}\"><rdf:Description rdf:about=\"{}\"",
113            Namespace::Rdf.url(),
114            about.unwrap_or(""),
115        )
116        .unwrap();
117
118        for namespace in self.namespaces.into_iter().filter(|ns| &Namespace::Rdf != ns) {
119            write!(&mut buf, " xmlns:{}=\"{}\" ", namespace.prefix(), namespace.url())
120                .unwrap();
121        }
122
123        buf.push('>');
124        buf.push_str(&self.buf);
125        buf.push_str("</rdf:Description></rdf:RDF></x:xmpmeta><?xpacket end=\"r\"?>");
126        buf
127    }
128}
129
130/// XMP Dublin Core Schema.
131impl XmpWriter<'_> {
132    /// Write the `dc:contributor` property.
133    ///
134    /// All entities responsible for making contributions to the resource not
135    /// listed in [`XmpWriter::creator`].
136    pub fn contributor<'a>(
137        &mut self,
138        contributor: impl IntoIterator<Item = &'a str>,
139    ) -> &mut Self {
140        self.element("contributor", Namespace::DublinCore)
141            .unordered_array(contributor);
142        self
143    }
144
145    /// Write the `dc:coverage` property.
146    ///
147    /// The scope of the resource.
148    pub fn coverage(&mut self, coverage: &str) -> &mut Self {
149        self.element("coverage", Namespace::DublinCore).value(coverage);
150        self
151    }
152
153    /// Write the `dc:creator` property.
154    ///
155    /// An entity primarily responsible for making the resource.
156    pub fn creator<'a>(
157        &mut self,
158        creator: impl IntoIterator<Item = &'a str>,
159    ) -> &mut Self {
160        self.element("creator", Namespace::DublinCore).ordered_array(creator);
161        self
162    }
163
164    /// Write the `dc:date` property.
165    ///
166    /// Date(s) that something happened to the resource.
167    pub fn date(&mut self, date: impl IntoIterator<Item = DateTime>) -> &mut Self {
168        self.element("date", Namespace::DublinCore).ordered_array(date);
169        self
170    }
171
172    /// Write the `dc:description` property.
173    ///
174    /// An account of the resource, possibly in multiple languages.
175    pub fn description<'a>(
176        &mut self,
177        description: impl IntoIterator<Item = (Option<LangId<'a>>, &'a str)>,
178    ) -> &mut Self {
179        self.element("description", Namespace::DublinCore)
180            .language_alternative(description);
181        self
182    }
183
184    /// Write the `dc:format` property.
185    ///
186    /// The mime type of the resource.
187    pub fn format(&mut self, mime: &str) -> &mut Self {
188        self.element("format", Namespace::DublinCore).value(mime);
189        self
190    }
191
192    /// Write the `dc:identifier` property.
193    ///
194    /// An unambiguous reference to the resource within a given context.
195    pub fn identifier(&mut self, id: &str) -> &mut Self {
196        self.element("identifier", Namespace::DublinCore).value(id);
197        self
198    }
199
200    /// Write the `dc:language` property.
201    ///
202    /// Languges used in the resource.
203    pub fn language<'a>(
204        &mut self,
205        lang: impl IntoIterator<Item = LangId<'a>>,
206    ) -> &mut Self {
207        self.element("language", Namespace::DublinCore).unordered_array(lang);
208        self
209    }
210
211    /// Write the `dc:publisher` property.
212    ///
213    /// Publishers of the resource.
214    pub fn publisher<'a>(
215        &mut self,
216        publisher: impl IntoIterator<Item = &'a str>,
217    ) -> &mut Self {
218        self.element("publisher", Namespace::DublinCore)
219            .unordered_array(publisher);
220        self
221    }
222
223    /// Write the `dc:relation` property.
224    ///
225    /// List of related resources.
226    pub fn relation<'a>(
227        &mut self,
228        relation: impl IntoIterator<Item = &'a str>,
229    ) -> &mut Self {
230        self.element("relation", Namespace::DublinCore)
231            .unordered_array(relation);
232        self
233    }
234
235    /// Write the `dc:rights` property.
236    ///
237    /// Informal rights statements, possibly in multiple languages.
238    pub fn rights<'a>(
239        &mut self,
240        rights: impl IntoIterator<Item = (Option<LangId<'a>>, &'a str)>,
241    ) -> &mut Self {
242        self.element("rights", Namespace::DublinCore)
243            .language_alternative(rights);
244        self
245    }
246
247    /// Write the `dc:source` property.
248    ///
249    /// A related resource from which the described resource is derived.
250    pub fn source(&mut self, source: &str) -> &mut Self {
251        self.element("source", Namespace::DublinCore).value(source);
252        self
253    }
254
255    /// Write the `dc:subject` property.
256    ///
257    /// A list of phrases or keywords that specify the topic of the resource.
258    pub fn subject<'a>(
259        &mut self,
260        subject: impl IntoIterator<Item = &'a str>,
261    ) -> &mut Self {
262        self.element("subject", Namespace::DublinCore)
263            .unordered_array(subject);
264        self
265    }
266
267    /// Write the `dc:title` property.
268    ///
269    /// A name given to the resource, possibly in multiple languages.
270    pub fn title<'a>(
271        &mut self,
272        title: impl IntoIterator<Item = (Option<LangId<'a>>, &'a str)>,
273    ) -> &mut Self {
274        self.element("title", Namespace::DublinCore)
275            .language_alternative(title);
276        self
277    }
278
279    /// Write the `dc:type` property.
280    ///
281    /// The nature or genre of the resource. Please use [`XmpWriter::format`] to
282    /// specify the mime type.
283    pub fn type_<'a>(&mut self, kind: impl IntoIterator<Item = &'a str>) -> &mut Self {
284        self.element("type", Namespace::DublinCore).unordered_array(kind);
285        self
286    }
287}
288
289/// XMP Basic Schema.
290impl<'n> XmpWriter<'n> {
291    /// Write the `xmp:BaseURL` property.
292    ///
293    /// The base URL for relative URLs in the document.
294    pub fn base_url(&mut self, url: &str) -> &mut Self {
295        self.element("BaseURL", Namespace::Xmp).value(url);
296        self
297    }
298
299    /// Write the `xmp:CreateDate` property.
300    ///
301    /// The date and time the resource was created.
302    pub fn create_date(&mut self, date: DateTime) -> &mut Self {
303        self.element("CreateDate", Namespace::Xmp).value(date);
304        self
305    }
306
307    /// Write the `xmp:CreatorTool` property.
308    ///
309    /// The name of the application used to create the resource.
310    pub fn creator_tool(&mut self, tool: &str) -> &mut Self {
311        self.element("CreatorTool", Namespace::Xmp).value(tool);
312        self
313    }
314
315    /// Write the `xmp:Identifier` property.
316    ///
317    /// Unordered array of text strings that identify the resource. The
318    /// [`XmpWriter::idq_scheme`] method can be used to specify the scheme.
319    pub fn xmp_identifier<'a>(
320        &mut self,
321        id: impl IntoIterator<Item = &'a str>,
322    ) -> &mut Self {
323        self.element("Identifier", Namespace::Xmp).unordered_array(id);
324        self
325    }
326
327    /// Write the `xmp:Label` property.
328    ///
329    /// A user-defined label for the resource.
330    pub fn label(&mut self, label: &str) -> &mut Self {
331        self.element("Label", Namespace::Xmp).value(label);
332        self
333    }
334
335    /// Write the `xmp:MetadataDate` property.
336    ///
337    /// The date and time the metadata for the resource was last changed.
338    pub fn metadata_date(&mut self, date: DateTime) -> &mut Self {
339        self.element("MetadataDate", Namespace::Xmp).value(date);
340        self
341    }
342
343    /// Write the `xmp:ModifyDate` property.
344    ///
345    /// The date and time the resource was last modified.
346    pub fn modify_date(&mut self, date: DateTime) -> &mut Self {
347        self.element("ModifyDate", Namespace::Xmp).value(date);
348        self
349    }
350
351    /// Write the `xmp:Nickname` property.
352    ///
353    /// A short informal name for the resource.
354    pub fn nickname(&mut self, nickname: &str) -> &mut Self {
355        self.element("Nickname", Namespace::Xmp).value(nickname);
356        self
357    }
358
359    /// Write the `xmp:Rating` property.
360    ///
361    /// A user-assigned rating of the resource.
362    pub fn rating(&mut self, rating: i64) -> &mut Self {
363        self.element("Rating", Namespace::Xmp).value(rating);
364        self
365    }
366
367    /// Start writing the `xmp:Thumbnails` property.
368    ///
369    /// A thumbnail image of the resource.
370    pub fn thumbnails(&mut self) -> ThumbnailsWriter<'_, 'n> {
371        ThumbnailsWriter::start(
372            self.element("Thumbnails", Namespace::Xmp)
373                .array(RdfCollectionType::Alt),
374        )
375    }
376}
377
378/// XMP Rights Management Schema.
379impl XmpWriter<'_> {
380    /// Write the `xmpRights:Certificate` property.
381    ///
382    /// A URL with a rights management certificate.
383    pub fn certificate(&mut self, cert: &str) -> &mut Self {
384        self.element("Certificate", Namespace::XmpRights).value(cert);
385        self
386    }
387
388    /// Write the `xmpRights:Marked` property.
389    ///
390    /// Whether the resource has been marked as rights managed. If false, the
391    /// resource is in the public domain.
392    pub fn marked(&mut self, marked: bool) -> &mut Self {
393        self.element("Marked", Namespace::XmpRights).value(marked);
394        self
395    }
396
397    /// Write the `xmpRights:Owner` property.
398    ///
399    /// A list of people or organizations owning the resource.
400    pub fn owner<'a>(&mut self, owner: impl IntoIterator<Item = &'a str>) -> &mut Self {
401        self.element("Owner", Namespace::XmpRights).unordered_array(owner);
402        self
403    }
404
405    /// Write the `xmpRights:UsageTerms` property.
406    ///
407    /// Under what conditions the resource may be used.
408    pub fn usage_terms<'a>(
409        &mut self,
410        terms: impl IntoIterator<Item = (Option<LangId<'a>>, &'a str)>,
411    ) -> &mut Self {
412        self.element("UsageTerms", Namespace::XmpRights)
413            .language_alternative(terms);
414        self
415    }
416
417    /// Write the `xmpRights:WebStatement` property.
418    ///
419    /// A URL with a rights management statement.
420    pub fn web_statement(&mut self, statement: &str) -> &mut Self {
421        self.element("WebStatement", Namespace::XmpRights).value(statement);
422        self
423    }
424}
425
426/// XMP Media Management Schema.
427impl<'n> XmpWriter<'n> {
428    /// Start writing the `xmpMM:DerivedFrom` property.
429    ///
430    /// The document from which this document is derived.
431    pub fn derived_from(&mut self) -> ResourceRefWriter<'_, 'n> {
432        ResourceRefWriter::start(self.element("DerivedFrom", Namespace::XmpMedia).obj())
433    }
434
435    /// Write the `xmpMM:DocumentID` property.
436    ///
437    /// A common identifier for the document and all of its versions /
438    /// renditions.
439    pub fn document_id(&mut self, id: &str) -> &mut Self {
440        self.element("DocumentID", Namespace::XmpMedia).value(id);
441        self
442    }
443
444    /// Start writing the `xmpMM:History` property.
445    ///
446    /// A list of actions taken on the document.
447    pub fn history(&mut self) -> ResourceEventsWriter<'_, 'n> {
448        ResourceEventsWriter::start(
449            self.element("History", Namespace::XmpMedia)
450                .array(RdfCollectionType::Seq),
451        )
452    }
453
454    /// Write the `xmpMM:Ingredients` property.
455    ///
456    /// A list of resources that were used to create the document.
457    pub fn ingredients(&mut self) -> ResourceRefsWriter<'_, 'n> {
458        ResourceRefsWriter::start(
459            self.element("Ingredients", Namespace::XmpMedia)
460                .array(RdfCollectionType::Bag),
461        )
462    }
463
464    /// Write the `xmpMM:InstanceID` property.
465    ///
466    /// A unique identifier for the rendition of the document, updated each
467    /// time the document is saved.
468    pub fn instance_id(&mut self, id: &str) -> &mut Self {
469        self.element("InstanceID", Namespace::XmpMedia).value(id);
470        self
471    }
472
473    /// Start writing the `xmpMM:ManagedFrom` property.
474    ///
475    /// A reference to the document before it was managed.
476    pub fn managed_from(&mut self) -> ResourceRefWriter<'_, 'n> {
477        ResourceRefWriter::start(self.element("ManagedFrom", Namespace::XmpMedia).obj())
478    }
479
480    /// Write the `xmpMM:Manager` property.
481    ///
482    /// The name of the application that manages the document.
483    pub fn manager(&mut self, manager: &str) -> &mut Self {
484        self.element("Manager", Namespace::XmpMedia).value(manager);
485        self
486    }
487
488    /// Write the `xmpMM:ManageTo` property.
489    ///
490    /// The URI of the document in the management system.
491    pub fn manage_to(&mut self, uri: &str) -> &mut Self {
492        self.element("ManageTo", Namespace::XmpMedia).value(uri);
493        self
494    }
495
496    /// Write the `xmpMM:ManageUI` property.
497    ///
498    /// A web page that allows the user to manage the document.
499    pub fn manage_ui(&mut self, uri: &str) -> &mut Self {
500        self.element("ManageUI", Namespace::XmpMedia).value(uri);
501        self
502    }
503
504    /// Write the `xmpMM:ManagerVariant` property.
505    ///
506    /// The name of the variant of the application that manages the document.
507    pub fn manager_variant(&mut self, variant: &str) -> &mut Self {
508        self.element("ManagerVariant", Namespace::XmpMedia).value(variant);
509        self
510    }
511
512    /// Write the `xmpMM:OriginalDocumentID` property.
513    ///
514    /// The ID of the resource from which this document was derived.
515    pub fn original_doc_id(&mut self, id: &str) -> &mut Self {
516        self.element("OriginalDocumentID", Namespace::XmpMedia).value(id);
517        self
518    }
519
520    /// Start writing the `xmpMM:Pantry` property.
521    ///
522    /// An unordered array of structs with custom properties, each of which must
523    /// have an `xmpMM:InstanceID` property.
524    pub fn pantry(&mut self) -> PantryWriter<'_, 'n> {
525        PantryWriter::start(
526            self.element("Pantry", Namespace::XmpMedia)
527                .array(RdfCollectionType::Bag),
528        )
529    }
530
531    /// Write the `xmpMM:RenditionClass` property.
532    ///
533    /// The type of the rendition. Shall be absent or [`RenditionClass::Default`]
534    /// if this is not a derived document.
535    pub fn rendition_class(&mut self, class: RenditionClass) -> &mut Self {
536        self.element("RenditionClass", Namespace::XmpMedia).value(class);
537        self
538    }
539
540    /// Write the `xmpMM:RenditionParams` property.
541    ///
542    /// The parameters used to create the rendition.
543    pub fn rendition_params(&mut self, params: &str) -> &mut Self {
544        self.element("RenditionParams", Namespace::XmpMedia).value(params);
545        self
546    }
547
548    /// Write the `xmpMM:VersionID` property.
549    ///
550    /// A unique identifier for the version of the document.
551    pub fn version_id(&mut self, id: &str) -> &mut Self {
552        self.element("VersionID", Namespace::XmpMedia).value(id);
553        self
554    }
555
556    /// Start writing the `xmpMM:Versions` property.
557    ///
558    /// The list of versions of the document, starting with the oldest version.
559    pub fn version_ref(&mut self) -> VersionsWriter<'_, 'n> {
560        VersionsWriter::start(
561            self.element("Versions", Namespace::XmpMedia)
562                .array(RdfCollectionType::Seq),
563        )
564    }
565}
566
567/// Basic Job Management.
568impl<'n> XmpWriter<'n> {
569    /// Start writing the `xmpBJ:JobRef` property.
570    ///
571    /// A reference to jobs in a system that involves this resource.
572    pub fn jobs(&mut self) -> JobsWriter<'_, 'n> {
573        JobsWriter::start(
574            self.element("Job", Namespace::XmpJobManagement)
575                .array(RdfCollectionType::Bag),
576        )
577    }
578}
579
580/// Paged-text.
581impl<'n> XmpWriter<'n> {
582    /// Start writing the `xmpTPg:NPages` property.
583    ///
584    /// Colorants used in the document.
585    pub fn colorants(&mut self) -> ColorantsWriter<'_, 'n> {
586        ColorantsWriter::start(
587            self.element("Colorants", Namespace::XmpPaged)
588                .array(RdfCollectionType::Seq),
589        )
590    }
591
592    /// Start writing the `xmpTPg:Fonts` property.
593    ///
594    /// Fonts used in the document.
595    pub fn fonts(&mut self) -> FontsWriter<'_, 'n> {
596        FontsWriter::start(
597            self.element("Fonts", Namespace::XmpPaged)
598                .array(RdfCollectionType::Bag),
599        )
600    }
601
602    /// Start writing the `xmpTPg:MaxPageSize` property.
603    ///
604    /// The maximum page size in the document.
605    pub fn max_page_size(&mut self) -> DimensionsWriter<'_, 'n> {
606        DimensionsWriter::start(self.element("MaxPageSize", Namespace::XmpPaged).obj())
607    }
608
609    /// Write the `xmpTPg:NPages` property.
610    ///
611    /// The number of pages in the document.
612    pub fn num_pages(&mut self, num: u32) -> &mut Self {
613        self.element("NPages", Namespace::XmpPaged).value(num as i64);
614        self
615    }
616
617    /// Write the `xmpTPg:PlateNames` property.
618    ///
619    /// The names of the plates needed to print the document.
620    pub fn plate_names<'a>(
621        &mut self,
622        names: impl IntoIterator<Item = &'a str>,
623    ) -> &mut Self {
624        self.element("PlateNames", Namespace::XmpPaged).ordered_array(names);
625        self
626    }
627}
628
629// TODO: Dynamic Media
630
631/// XMPIDQ.
632impl XmpWriter<'_> {
633    /// Write the `xmpidq:GImg` property.
634    ///
635    /// Identifies the scheme of the [`XmpWriter::xmp_identifier`] property.
636    pub fn idq_scheme(&mut self, scheme: &str) -> &mut Self {
637        self.element("Scheme", Namespace::XmpIdq).value(scheme);
638        self
639    }
640}
641
642/// Adobe PDF.
643impl XmpWriter<'_> {
644    /// Write the `pdf:Keywords` property.
645    ///
646    /// The document's keywords.
647    pub fn pdf_keywords(&mut self, keywords: &str) -> &mut Self {
648        self.element("Keywords", Namespace::AdobePdf).value(keywords);
649        self
650    }
651
652    /// Write the `pdf:PDFVersion` property.
653    ///
654    /// The version of the PDF specification to which the document conforms
655    /// (e.g. `"1.0", "1.7"`)
656    pub fn pdf_version(&mut self, version: &str) -> &mut Self {
657        self.element("PDFVersion", Namespace::AdobePdf).value(version);
658        self
659    }
660
661    /// Write the `pdf:Producer` property.
662    ///
663    /// The name of the application that created the PDF document.
664    pub fn producer(&mut self, producer: &str) -> &mut Self {
665        self.element("Producer", Namespace::AdobePdf).value(producer);
666        self
667    }
668
669    /// Write the `pdf:Trapped` property.
670    ///
671    /// Whether the document has been trapped.
672    pub fn trapped(&mut self, trapped: bool) -> &mut Self {
673        self.element("Trapped", Namespace::AdobePdf).value(trapped);
674        self
675    }
676}
677
678/// PDF/A , PDF/UA and PDF/X.
679impl<'n> XmpWriter<'n> {
680    /// Write the `pdfaid:part` property.
681    ///
682    /// The part of the PDF/A standard to which the document conforms (e.g.
683    /// `1, 4`)
684    #[cfg(feature = "pdfa")]
685    pub fn pdfa_part(&mut self, part: i32) -> &mut Self {
686        self.element("part", Namespace::PdfAId).value(part);
687        self
688    }
689
690    /// Write the `pdfaid:amd` property.
691    ///
692    /// The amendment specifier this file conforms to, if any.
693    #[cfg(feature = "pdfa")]
694    pub fn pdfa_amd(&mut self, amd: &str) -> &mut Self {
695        self.element("amd", Namespace::PdfAId).value(amd);
696        self
697    }
698
699    /// Write the `pdfaid:corr` property.
700    ///
701    /// The corrigendum specifier this file conforms to, if any.
702    #[cfg(feature = "pdfa")]
703    pub fn pdfa_corr(&mut self, corr: &str) -> &mut Self {
704        self.element("corr", Namespace::PdfAId).value(corr);
705        self
706    }
707
708    /// Start writing the `pdfaExtension:schemas` property.
709    ///
710    /// Description of all extension schemas used in the document.
711    #[cfg(feature = "pdfa")]
712    pub fn extension_schemas(&mut self) -> PdfAExtSchemasWriter<'_, 'n> {
713        PdfAExtSchemasWriter::start(
714            self.element("schemas", Namespace::PdfAExtension)
715                .array(RdfCollectionType::Bag),
716        )
717    }
718
719    /// Write the `pdfaid:conformance` property.
720    ///
721    /// The conformance level of the PDF/A standard to which the document
722    /// conforms (e.g. `"A", "B"`)
723    #[cfg(feature = "pdfa")]
724    pub fn pdfa_conformance(&mut self, conformance: &str) -> &mut Self {
725        self.element("conformance", Namespace::PdfAId).value(conformance);
726        self
727    }
728
729    /// Write the `pdfaid:rev` property.
730    ///
731    /// Four-digit year of the date of publication or revision.
732    #[cfg(feature = "pdfa")]
733    pub fn pdfa_rev(&mut self, rev: i32) -> &mut Self {
734        self.element("rev", Namespace::PdfAId).value(rev);
735        self
736    }
737
738    /// Write the `pdfxid:GTS_PDFXVersion` property.
739    ///
740    /// The version of the PDF/X standard to which the document conforms (e.g.
741    /// `"PDF/X-3:2003"`)
742    pub fn pdfx_version(&mut self, version: &str) -> &mut Self {
743        self.element("GTS_PDFXVersion", Namespace::PdfXId).value(version);
744        self
745    }
746
747    /// Write the `pdfuaid:part` property.
748    ///
749    /// The part of the PDF/UA standard to which the document conforms (e.g.
750    /// `1`)
751    pub fn pdfua_part(&mut self, part: i32) -> &mut Self {
752        self.element("part", Namespace::PdfUAId).value(part);
753        self
754    }
755
756    /// Write the `pdfuaid:amd` property.
757    ///
758    /// The amendment specifier this file conforms to, if any.
759    pub fn pdfua_amd(&mut self, amd: &str) -> &mut Self {
760        self.element("amd", Namespace::PdfUAId).value(amd);
761        self
762    }
763
764    /// Write the `pdfuaid:corr` property.
765    ///
766    /// The corrigendum specifier this file conforms to, if any.
767    pub fn pdfua_corr(&mut self, corr: &str) -> &mut Self {
768        self.element("corr", Namespace::PdfUAId).value(corr);
769        self
770    }
771}
772
773/// A self-contained thumbnail image.
774///
775/// Created by [`ThumbnailsWriter::add_thumbnail`].
776pub struct ThumbnailWriter<'a, 'n: 'a> {
777    stc: Struct<'a, 'n>,
778}
779
780impl<'a, 'n: 'a> ThumbnailWriter<'a, 'n> {
781    fn start(stc: Struct<'a, 'n>) -> Self {
782        Self { stc }
783    }
784
785    /// Write the `xmpGImg:format` property with a custom format of the
786    /// thumbnail image. Must be "JPEG" for now.
787    pub fn format(&mut self, format: &str) -> &mut Self {
788        self.stc.element("format", Namespace::XmpImage).value(format);
789        self
790    }
791
792    /// Write the `xmpGImg:format` property with the value "JPEG".
793    pub fn format_jpeg(&mut self) -> &mut Self {
794        self.format("JPEG")
795    }
796
797    /// Write the `xmpGImg:width` property.
798    pub fn width(&mut self, width: u64) -> &mut Self {
799        self.stc.element("width", Namespace::XmpImage).value(width as i64);
800        self
801    }
802
803    /// Write the `xmpGImg:height` property.
804    pub fn height(&mut self, height: u64) -> &mut Self {
805        self.stc.element("height", Namespace::XmpImage).value(height as i64);
806        self
807    }
808
809    /// Write the `xmpGImg:image` property.
810    ///
811    /// The image must be a base64-encoded JPEG.
812    pub fn image(&mut self, image: &str) -> &mut Self {
813        self.stc.element("image", Namespace::XmpImage).value(image);
814        self
815    }
816}
817
818deref!('a, 'n, ThumbnailWriter<'a, 'n> => Struct<'a, 'n>, stc);
819
820/// Write a set of thumbnails.
821///
822/// Created by [`XmpWriter::thumbnails`].
823pub struct ThumbnailsWriter<'a, 'n: 'a> {
824    array: Array<'a, 'n>,
825}
826
827impl<'a, 'n: 'a> ThumbnailsWriter<'a, 'n> {
828    fn start(array: Array<'a, 'n>) -> Self {
829        Self { array }
830    }
831
832    /// Add a thumbnail.
833    pub fn add_thumbnail(&mut self) -> ThumbnailWriter<'_, 'n> {
834        ThumbnailWriter::start(self.array.element().obj())
835    }
836}
837
838deref!('a, 'n, ThumbnailsWriter<'a, 'n> => Array<'a, 'n>, array);
839
840/// Writer for a reference to a resource.
841///
842/// Created by [`XmpWriter::derived_from`], [`XmpWriter::managed_from`], or [`ResourceRefsWriter::add_ref`].
843pub struct ResourceRefWriter<'a, 'n: 'a> {
844    stc: Struct<'a, 'n>,
845}
846
847impl<'a, 'n: 'a> ResourceRefWriter<'a, 'n> {
848    fn start(stc: Struct<'a, 'n>) -> Self {
849        Self { stc }
850    }
851
852    /// Write the `stRef:alternatePaths` property.
853    ///
854    /// Fallback paths to the resource.
855    pub fn alternate_paths<'b>(
856        &mut self,
857        paths: impl IntoIterator<Item = &'b str>,
858    ) -> &mut Self {
859        self.stc
860            .element("alternatePaths", Namespace::XmpResourceRef)
861            .ordered_array(paths);
862        self
863    }
864
865    /// Write the `stRef:documentID` property.
866    ///
867    /// The [`XmpWriter::document_id`] of the referenced resource.
868    pub fn document_id(&mut self, id: &str) -> &mut Self {
869        self.stc.element("documentID", Namespace::XmpResourceRef).value(id);
870        self
871    }
872
873    /// Write the `stRef:filePath` property.
874    ///
875    /// The path or URL to the resource.
876    pub fn file_path(&mut self, path: &str) -> &mut Self {
877        self.stc.element("filePath", Namespace::XmpResourceRef).value(path);
878        self
879    }
880
881    /// Write the `stRef:instanceID` property.
882    ///
883    /// The [`XmpWriter::instance_id`] of the referenced resource.
884    pub fn instance_id(&mut self, id: &str) -> &mut Self {
885        self.stc.element("instanceID", Namespace::XmpResourceRef).value(id);
886        self
887    }
888
889    /// Write the `stRef:lastModifyDate` property.
890    ///
891    /// The last modification date of the resource. See [`ResourceEventWriter::when`].
892    pub fn last_modify_date(&mut self, date: DateTime) -> &mut Self {
893        self.stc
894            .element("lastModifyDate", Namespace::XmpResourceRef)
895            .value(date);
896        self
897    }
898
899    /// Write the `stRef:manager` property.
900    ///
901    /// The name of the application that manages the resource. See [`XmpWriter::manager`].
902    pub fn manager(&mut self, manager: &str) -> &mut Self {
903        self.stc.element("manager", Namespace::XmpResourceRef).value(manager);
904        self
905    }
906
907    /// Write the `stRef:managerVariant` property.
908    ///
909    /// The variant of the application that manages the resource. See [`XmpWriter::manager_variant`].
910    pub fn manager_variant(&mut self, variant: &str) -> &mut Self {
911        self.stc
912            .element("managerVariant", Namespace::XmpResourceRef)
913            .value(variant);
914        self
915    }
916
917    /// Write the `stRef:manageTo` property.
918    ///
919    /// The URI of the resource prior to being managed. See [`XmpWriter::manage_to`].
920    pub fn manage_to(&mut self, uri: &str) -> &mut Self {
921        self.stc.element("manageTo", Namespace::XmpResourceRef).value(uri);
922        self
923    }
924
925    /// Write the `stRef:manageUI` property.
926    ///
927    /// An URI to the user interface of the application that manages the resource. See [`XmpWriter::manage_ui`].
928    pub fn manage_ui(&mut self, uri: &str) -> &mut Self {
929        self.stc.element("manageTo", Namespace::XmpResourceRef).value(uri);
930        self
931    }
932
933    /// Write the `stRef:maskMarkers` property.
934    ///
935    /// Whether to process markers for resources in the [`XmpWriter::ingredients`] array.
936    pub fn mask_markers(&mut self, markers: MaskMarkers) -> &mut Self {
937        self.stc
938            .element("maskMarkers", Namespace::XmpResourceRef)
939            .value(markers);
940        self
941    }
942
943    /// Write the `stRef:partMapping` property.
944    ///
945    /// The name or URI of a mapping function to map `fromPart` to `toPart`.
946    pub fn part_mapping(&mut self, mapping: &str) -> &mut Self {
947        self.stc
948            .element("partMapping", Namespace::XmpResourceRef)
949            .value(mapping);
950        self
951    }
952
953    /// Write the `stRef:renditionClass` property.
954    ///
955    /// The rendition class of the referenced resource. See
956    /// [`XmpWriter::rendition_class`].
957    pub fn rendition_class(&mut self, rendition: RenditionClass) -> &mut Self {
958        self.stc
959            .element("renditionClass", Namespace::XmpResourceRef)
960            .value(rendition);
961        self
962    }
963
964    /// Write the `stRef:renditionParams` property.
965    ///
966    /// The rendition parameters of the referenced resource. See
967    /// [`XmpWriter::rendition_params`].
968    pub fn rendition_params(&mut self, params: &str) -> &mut Self {
969        self.stc
970            .element("renditionParams", Namespace::XmpResourceRef)
971            .value(params);
972        self
973    }
974
975    /// Write the `stRef:toPart` property.
976    ///
977    /// For a resource in a [`XmpWriter::ingredients`] array, the part of the root
978    /// resource that the the ingredient corresponds to.
979    pub fn to_part(&mut self, part: &str) -> &mut Self {
980        self.stc.element("toPart", Namespace::XmpResourceRef).value(part);
981        self
982    }
983
984    /// Write the `stRef:versionID` property.
985    ///
986    /// The referenced resource's version ID. See [`XmpWriter::version_id`].
987    pub fn version_id(&mut self, id: &str) -> &mut Self {
988        self.stc.element("versionID", Namespace::XmpResourceRef).value(id);
989        self
990    }
991}
992
993deref!('a, 'n, ResourceRefWriter<'a, 'n> => Struct<'a, 'n>, stc);
994
995/// Writer for a resource reference array.
996///
997/// Created by [`XmpWriter::ingredients`].
998pub struct ResourceRefsWriter<'a, 'n: 'a> {
999    array: Array<'a, 'n>,
1000}
1001
1002impl<'a, 'n: 'a> ResourceRefsWriter<'a, 'n> {
1003    fn start(array: Array<'a, 'n>) -> Self {
1004        Self { array }
1005    }
1006
1007    /// Add a reference to the array.
1008    pub fn add_ref(&mut self) -> ResourceRefWriter<'_, 'n> {
1009        ResourceRefWriter::start(self.array.element().obj())
1010    }
1011}
1012
1013deref!('a, 'n, ResourceRefsWriter<'a, 'n> => Array<'a, 'n>, array);
1014
1015/// Writer for an event that occurred to a resource.
1016///
1017/// Created by [`VersionWriter::event`] and [`ResourceEventsWriter::add_event`].
1018pub struct ResourceEventWriter<'a, 'n: 'a> {
1019    stc: Struct<'a, 'n>,
1020}
1021
1022impl<'a, 'n: 'a> ResourceEventWriter<'a, 'n> {
1023    fn start(stc: Struct<'a, 'n>) -> Self {
1024        Self { stc }
1025    }
1026
1027    /// Write the `stEvt:action` property.
1028    ///
1029    /// The action that occurred to the resource.
1030    pub fn action(&mut self, action: ResourceEventAction) -> &mut Self {
1031        self.stc.element("action", Namespace::XmpResourceEvent).value(action);
1032        self
1033    }
1034
1035    /// Write the `stEvt:changed` property.
1036    ///
1037    /// Semicolon-separated list of the parts of the resource that changed.
1038    pub fn changed(&mut self, parts: &str) -> &mut Self {
1039        self.stc.element("changed", Namespace::XmpResourceEvent).value(parts);
1040        self
1041    }
1042    /// Write the `stEvt:instanceID` property.
1043    ///
1044    /// Value of the [`XmpWriter::instance_id`] property at the time of the action.
1045    pub fn instance_id(&mut self, id: &str) -> &mut Self {
1046        self.stc.element("instanceID", Namespace::XmpResourceEvent).value(id);
1047        self
1048    }
1049
1050    /// Write the `stEvt:changed` property.
1051    ///
1052    /// Additional parameters for the action.
1053    pub fn parameters(&mut self, params: &str) -> &mut Self {
1054        self.stc
1055            .element("parameters", Namespace::XmpResourceEvent)
1056            .value(params);
1057        self
1058    }
1059
1060    /// Write the `stEvt:softwareAgent` property.
1061    ///
1062    /// The name of the software agent that performed the action.
1063    pub fn software_agent(&mut self, agent: &str) -> &mut Self {
1064        self.stc
1065            .element("softwareAgent", Namespace::XmpResourceEvent)
1066            .value(agent);
1067        self
1068    }
1069
1070    /// Write the `stEvt:when` property.
1071    ///
1072    /// The date and time the action occurred.
1073    pub fn when(&mut self, date: DateTime) -> &mut Self {
1074        self.stc.element("when", Namespace::XmpResourceEvent).value(date);
1075        self
1076    }
1077}
1078
1079deref!('a, 'n, ResourceEventWriter<'a, 'n> => Struct<'a, 'n>, stc);
1080
1081/// Writer for a resource event array.
1082///
1083/// Created by [`XmpWriter::history`].
1084pub struct ResourceEventsWriter<'a, 'n: 'a> {
1085    array: Array<'a, 'n>,
1086}
1087
1088impl<'a, 'n: 'a> ResourceEventsWriter<'a, 'n> {
1089    fn start(array: Array<'a, 'n>) -> Self {
1090        Self { array }
1091    }
1092
1093    /// Add an event to the array.
1094    pub fn add_event(&mut self) -> ResourceEventWriter<'_, 'n> {
1095        ResourceEventWriter::start(self.array.element().obj())
1096    }
1097}
1098
1099deref!('a, 'n, ResourceEventsWriter<'a, 'n> => Array<'a, 'n>, array);
1100
1101/// Writer for an item in a Pantry array.
1102///
1103/// Use the `Deref` impl to access the underlying [`Struct`] and add properties.
1104/// Created by [`PantryWriter::add_item`].
1105pub struct PantryItemWriter<'a, 'n: 'a> {
1106    stc: Struct<'a, 'n>,
1107}
1108
1109impl<'a, 'n: 'a> PantryItemWriter<'a, 'n> {
1110    fn start(stc: Struct<'a, 'n>) -> Self {
1111        Self { stc }
1112    }
1113
1114    /// Write the `xmpMM:instanceID` property. Required.
1115    pub fn instance_id(&mut self, id: &str) -> &mut Self {
1116        self.stc.element("instanceID", Namespace::XmpMedia).value(id);
1117        self
1118    }
1119}
1120
1121deref!('a, 'n, PantryItemWriter<'a, 'n> => Struct<'a, 'n>, stc);
1122
1123/// Writer for a Pantry array.
1124pub struct PantryWriter<'a, 'n: 'a> {
1125    array: Array<'a, 'n>,
1126}
1127
1128impl<'a, 'n: 'a> PantryWriter<'a, 'n> {
1129    fn start(array: Array<'a, 'n>) -> Self {
1130        Self { array }
1131    }
1132
1133    /// Add an item to the array.
1134    pub fn add_item(&mut self) -> PantryItemWriter<'_, 'n> {
1135        PantryItemWriter::start(self.array.element().obj())
1136    }
1137}
1138
1139deref!('a, 'n, PantryWriter<'a, 'n> => Array<'a, 'n>, array);
1140
1141/// Writer for a version struct.
1142///
1143/// Created by [`VersionsWriter::add_version`].
1144pub struct VersionWriter<'a, 'n: 'a> {
1145    stc: Struct<'a, 'n>,
1146}
1147
1148impl<'a, 'n: 'a> VersionWriter<'a, 'n> {
1149    fn start(stc: Struct<'a, 'n>) -> Self {
1150        Self { stc }
1151    }
1152
1153    /// Write the `stVer:comments` property.
1154    ///
1155    /// Comments about the version.
1156    pub fn comments(&mut self, comments: &str) -> &mut Self {
1157        self.stc.element("comments", Namespace::XmpVersion).value(comments);
1158        self
1159    }
1160
1161    /// Start writing the `stVer:event` property.
1162    ///
1163    /// The event that created the version.
1164    pub fn event(&mut self) -> ResourceEventWriter<'_, 'n> {
1165        ResourceEventWriter::start(self.stc.element("event", Namespace::XmpVersion).obj())
1166    }
1167
1168    /// Write the `stVer:modifier` property.
1169    ///
1170    /// The person or organization that created the version.
1171    pub fn modifier(&mut self, modifier: &str) -> &mut Self {
1172        self.stc.element("modifier", Namespace::XmpVersion).value(modifier);
1173        self
1174    }
1175
1176    /// Write the `stVer:modifyDate` property.
1177    ///
1178    /// The date and time the version was created.
1179    pub fn modify_date(&mut self, date: DateTime) -> &mut Self {
1180        self.stc.element("modifyDate", Namespace::XmpVersion).value(date);
1181        self
1182    }
1183
1184    /// Write the `stVer:version` property.
1185    ///
1186    /// The new version number.
1187    pub fn version(&mut self, version: &str) -> &mut Self {
1188        self.stc.element("version", Namespace::XmpVersion).value(version);
1189        self
1190    }
1191}
1192
1193deref!('a, 'n, VersionWriter<'a, 'n> => Struct<'a, 'n>, stc);
1194
1195/// Writer for a versions array.
1196///
1197/// Created by [`XmpWriter::version_ref`].
1198pub struct VersionsWriter<'a, 'n: 'a> {
1199    array: Array<'a, 'n>,
1200}
1201
1202impl<'a, 'n: 'a> VersionsWriter<'a, 'n> {
1203    fn start(array: Array<'a, 'n>) -> Self {
1204        Self { array }
1205    }
1206
1207    /// Add a version to the array.
1208    pub fn add_version(&mut self) -> VersionWriter<'_, 'n> {
1209        VersionWriter::start(self.array.element().obj())
1210    }
1211}
1212
1213deref!('a, 'n, VersionsWriter<'a, 'n> => Array<'a, 'n>, array);
1214
1215/// Writer for a job struct.
1216///
1217/// Created by [`JobsWriter::add_job`].
1218pub struct JobWriter<'a, 'n: 'a> {
1219    stc: Struct<'a, 'n>,
1220}
1221
1222impl<'a, 'n: 'a> JobWriter<'a, 'n> {
1223    fn start(stc: Struct<'a, 'n>) -> Self {
1224        Self { stc }
1225    }
1226
1227    /// Write the `stJob:id` property.
1228    ///
1229    /// The unique identifier for the job.
1230    pub fn id(&mut self, id: &str) -> &mut Self {
1231        self.stc.element("id", Namespace::XmpJob).value(id);
1232        self
1233    }
1234
1235    /// Write the `stJob:name` property.
1236    ///
1237    /// The name of the job.
1238    pub fn name(&mut self, name: &str) -> &mut Self {
1239        self.stc.element("name", Namespace::XmpJob).value(name);
1240        self
1241    }
1242
1243    /// Write the `stJob:url` property.
1244    ///
1245    /// Reference an external job management file.
1246    pub fn url(&mut self, url: &str) -> &mut Self {
1247        self.stc.element("url", Namespace::XmpJob).value(url);
1248        self
1249    }
1250}
1251
1252deref!('a, 'n, JobWriter<'a, 'n> => Struct<'a, 'n>, stc);
1253
1254/// Writer for a job array.
1255///
1256/// Created by [`XmpWriter::jobs`].
1257pub struct JobsWriter<'a, 'n: 'a> {
1258    array: Array<'a, 'n>,
1259}
1260
1261impl<'a, 'n: 'a> JobsWriter<'a, 'n> {
1262    fn start(array: Array<'a, 'n>) -> Self {
1263        Self { array }
1264    }
1265
1266    /// Add a job to the array.
1267    pub fn add_job(&mut self) -> JobWriter<'_, 'n> {
1268        JobWriter::start(self.array.element().obj())
1269    }
1270}
1271
1272deref!('a, 'n, JobsWriter<'a, 'n> => Array<'a, 'n>, array);
1273
1274/// A writer for colorant structs.
1275///
1276/// Created by [`ColorantsWriter::add_colorant`].
1277pub struct ColorantWriter<'a, 'n: 'a> {
1278    stc: Struct<'a, 'n>,
1279}
1280
1281impl<'a, 'n: 'a> ColorantWriter<'a, 'n> {
1282    fn start(stc: Struct<'a, 'n>) -> Self {
1283        Self { stc }
1284    }
1285
1286    /// Write the `xmpG:type` property.
1287    ///
1288    /// Whether this is a spot color or a process color.
1289    pub fn type_(&mut self, kind: ColorantType) -> &mut Self {
1290        self.stc.element("type", Namespace::XmpColorant).value(kind);
1291        self
1292    }
1293
1294    /// Write the `xmpG:swatchName` property.
1295    ///
1296    /// The name of the colorant.
1297    pub fn swatch_name(&mut self, name: &str) -> &mut Self {
1298        self.stc.element("swatchName", Namespace::XmpColorant).value(name);
1299        self
1300    }
1301
1302    /// Write the `xmpG:colorantMode` property.
1303    ///
1304    /// In which color space this colorant is defined.
1305    pub fn colorant_mode(&mut self, mode: ColorantMode) -> &mut Self {
1306        self.stc.element("colorantMode", Namespace::XmpColorant).value(mode);
1307        self
1308    }
1309
1310    /// Write the `xmpG:colorantType` property.
1311    ///
1312    /// The `L` value of a colorant with `xmpG:colorantMode` set to `Lab`.
1313    pub fn l(&mut self, l: f64) -> &mut Self {
1314        self.stc.element("L", Namespace::XmpColorant).value(l);
1315        self
1316    }
1317
1318    /// Write the `xmpG:a` property.
1319    ///
1320    /// The `a` value of a colorant with `xmpG:colorantMode` set to `Lab`.
1321    pub fn a(&mut self, a: i32) -> &mut Self {
1322        self.stc.element("A", Namespace::XmpColorant).value(a);
1323        self
1324    }
1325
1326    /// Write the `xmpG:b` property.
1327    ///
1328    /// The `b` value of a colorant with `xmpG:colorantMode` set to `Lab`.
1329    pub fn b(&mut self, b: i32) -> &mut Self {
1330        self.stc.element("B", Namespace::XmpColorant).value(b);
1331        self
1332    }
1333
1334    /// Write the `xmpG:black` property.
1335    ///
1336    /// The `K` value of a colorant with `xmpG:colorantMode` set to `CMYK`.
1337    pub fn black(&mut self, black: f64) -> &mut Self {
1338        self.stc.element("black", Namespace::XmpColorant).value(black);
1339        self
1340    }
1341
1342    /// Write the `xmpG:cyan` property.
1343    ///
1344    /// The `C` value of a colorant with `xmpG:colorantMode` set to `CMYK`.
1345    pub fn cyan(&mut self, cyan: f64) -> &mut Self {
1346        self.stc.element("cyan", Namespace::XmpColorant).value(cyan);
1347        self
1348    }
1349
1350    /// Write the `xmpG:magenta` property.
1351    ///
1352    /// The `M` value of a colorant with `xmpG:colorantMode` set to `CMYK`.
1353    pub fn magenta(&mut self, magenta: f64) -> &mut Self {
1354        self.stc.element("magenta", Namespace::XmpColorant).value(magenta);
1355        self
1356    }
1357
1358    /// Write the `xmpG:yellow` property.
1359    ///
1360    /// The `Y` value of a colorant with `xmpG:colorantMode` set to `CMYK`.
1361    pub fn yellow(&mut self, yellow: f64) -> &mut Self {
1362        self.stc.element("yellow", Namespace::XmpColorant).value(yellow);
1363        self
1364    }
1365
1366    /// Write the `xmpG:red` property.
1367    ///
1368    /// The `R` value of a colorant with `xmpG:colorantMode` set to `RGB`.
1369    pub fn red(&mut self, red: i32) -> &mut Self {
1370        self.stc.element("red", Namespace::XmpColorant).value(red);
1371        self
1372    }
1373
1374    /// Write the `xmpG:green` property.
1375    ///
1376    /// The `G` value of a colorant with `xmpG:colorantMode` set to `RGB`.
1377    pub fn green(&mut self, green: i32) -> &mut Self {
1378        self.stc.element("green", Namespace::XmpColorant).value(green);
1379        self
1380    }
1381
1382    /// Write the `xmpG:blue` property.
1383    ///
1384    /// The `B` value of a colorant with `xmpG:colorantMode` set to `RGB`.
1385    pub fn blue(&mut self, blue: i32) -> &mut Self {
1386        self.stc.element("blue", Namespace::XmpColorant).value(blue);
1387        self
1388    }
1389}
1390
1391deref!('a, 'n, ColorantWriter<'a, 'n> => Struct<'a, 'n>, stc);
1392
1393/// Writer for an array of colorants.
1394///
1395/// Created by [`XmpWriter::colorants`].
1396pub struct ColorantsWriter<'a, 'n: 'a> {
1397    array: Array<'a, 'n>,
1398}
1399
1400impl<'a, 'n> ColorantsWriter<'a, 'n> {
1401    fn start(array: Array<'a, 'n>) -> Self {
1402        Self { array }
1403    }
1404
1405    /// Add a new colorant to the array.
1406    pub fn add_colorant(&mut self) -> ColorantWriter<'_, 'n> {
1407        ColorantWriter::start(self.array.element().obj())
1408    }
1409}
1410
1411deref!('a, 'n, ColorantsWriter<'a, 'n> => Array<'a, 'n>, array);
1412
1413/// Writer for a dimensions struct.
1414///
1415/// Created by [`XmpWriter::max_page_size`].
1416pub struct DimensionsWriter<'a, 'n: 'a> {
1417    stc: Struct<'a, 'n>,
1418}
1419
1420impl<'a, 'n> DimensionsWriter<'a, 'n> {
1421    fn start(stc: Struct<'a, 'n>) -> Self {
1422        Self { stc }
1423    }
1424
1425    /// Write the `stDim:w` property.
1426    ///
1427    /// The width of the resource.
1428    pub fn width(&mut self, width: f64) -> &mut Self {
1429        self.stc.element("w", Namespace::XmpDimensions).value(width);
1430        self
1431    }
1432
1433    /// Write the `stDim:h` property.
1434    ///
1435    /// The height of the resource.
1436    pub fn height(&mut self, height: f64) -> &mut Self {
1437        self.stc.element("h", Namespace::XmpDimensions).value(height);
1438        self
1439    }
1440
1441    /// Write the `stDim:unit` property.
1442    ///
1443    /// The unit of the width and height properties.
1444    pub fn unit(&mut self, unit: DimensionUnit) -> &mut Self {
1445        self.stc.element("unit", Namespace::XmpDimensions).value(unit);
1446        self
1447    }
1448}
1449
1450deref!('a, 'n, DimensionsWriter<'a, 'n> => Struct<'a, 'n>, stc);
1451
1452/// Writer for a font struct.
1453///
1454/// Created by [`XmpWriter::fonts`].
1455pub struct FontWriter<'a, 'n: 'a> {
1456    stc: Struct<'a, 'n>,
1457}
1458
1459impl<'a, 'n: 'a> FontWriter<'a, 'n> {
1460    fn start(stc: Struct<'a, 'n>) -> Self {
1461        Self { stc }
1462    }
1463
1464    /// Write the `stFnt:childFontFiles` property.
1465    ///
1466    /// An array of font files that make up this font.
1467    pub fn child_font_files<'b>(
1468        &mut self,
1469        files: impl IntoIterator<Item = &'b str>,
1470    ) -> &mut Self {
1471        self.stc
1472            .element("childFontFiles", Namespace::XmpFont)
1473            .ordered_array(files);
1474        self
1475    }
1476
1477    /// Write the `stFnt:composite` property.
1478    ///
1479    /// Whether the font is a composite font.
1480    pub fn composite(&mut self, composite: bool) -> &mut Self {
1481        self.stc.element("composite", Namespace::XmpFont).value(composite);
1482        self
1483    }
1484
1485    /// Write the `stFnt:fontFace` property.
1486    ///
1487    /// The font face name.
1488    pub fn font_face(&mut self, face: &str) -> &mut Self {
1489        self.stc.element("fontFace", Namespace::XmpFont).value(face);
1490        self
1491    }
1492
1493    /// Write the `stFnt:fontFamily` property.
1494    ///
1495    /// The font family name.
1496    pub fn font_family(&mut self, family: &str) -> &mut Self {
1497        self.stc.element("fontFamily", Namespace::XmpFont).value(family);
1498        self
1499    }
1500
1501    /// Write the `stFnt:fontFile` property.
1502    ///
1503    /// The font file name.
1504    pub fn font_file(&mut self, file_name: &str) -> &mut Self {
1505        self.stc.element("fontFileName", Namespace::XmpFont).value(file_name);
1506        self
1507    }
1508
1509    /// Write the `stFnt:fontName` property.
1510    ///
1511    /// The PostScript name of the font.
1512    pub fn font_name(&mut self, name: &str) -> &mut Self {
1513        self.stc.element("fontName", Namespace::XmpFont).value(name);
1514        self
1515    }
1516
1517    /// Write the `stFnt:fontType` property.
1518    ///
1519    /// The font type.
1520    pub fn font_type(&mut self, font_type: FontType) -> &mut Self {
1521        self.stc.element("fontType", Namespace::XmpFont).value(font_type);
1522        self
1523    }
1524
1525    /// Write the `stFnt:versionString` property.
1526    ///
1527    /// The version string of the font.
1528    /// Must be chosen depending on the font type.
1529    /// - **Type 1**: `/version`
1530    /// - **TrueType / OpenType**: `nameId 5`
1531    /// - **CID**: `/CIDFontVersion`
1532    /// - **Bitmap Font**: must be empty
1533    pub fn version_string(&mut self, version: &str) -> &mut Self {
1534        self.stc.element("versionString", Namespace::XmpFont).value(version);
1535        self
1536    }
1537}
1538
1539deref!('a, 'n, FontWriter<'a, 'n> => Struct<'a, 'n>, stc);
1540
1541/// Writer for an array of fonts.
1542///
1543/// Created by [`XmpWriter::fonts`].
1544pub struct FontsWriter<'a, 'n: 'a> {
1545    array: Array<'a, 'n>,
1546}
1547
1548impl<'a, 'n: 'a> FontsWriter<'a, 'n> {
1549    fn start(array: Array<'a, 'n>) -> Self {
1550        Self { array }
1551    }
1552
1553    /// Add a new font to the array.
1554    pub fn add_font(&mut self) -> FontWriter<'_, 'n> {
1555        FontWriter::start(self.array.element().obj())
1556    }
1557}
1558
1559deref!('a, 'n, FontsWriter<'a, 'n> => Array<'a, 'n>, array);