Skip to main content

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.3");
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. Specific to
759    /// PDF/UA-1.
760    pub fn pdfua_amd(&mut self, amd: &str) -> &mut Self {
761        self.element("amd", Namespace::PdfUAId).value(amd);
762        self
763    }
764
765    /// Write the `pdfuaid:corr` property.
766    ///
767    /// The corrigendum specifier this file conforms to, if any. In practice,
768    /// this should never appear since no corrigenda have been published for
769    /// PDF/UA, and ISO does not allow for new corrigenda to be added to ISO
770    /// 14289.
771    pub fn pdfua_corr(&mut self, corr: &str) -> &mut Self {
772        self.element("corr", Namespace::PdfUAId).value(corr);
773        self
774    }
775
776    /// Write the `pdfuaid:rev` property.
777    ///
778    /// Four-digit year of the date of publication or revision. Specific to
779    /// PDF/UA-2.
780    pub fn pdfua_rev(&mut self, rev: i32) -> &mut Self {
781        self.element("rev", Namespace::PdfUAId).value(rev);
782        self
783    }
784}
785
786/// A self-contained thumbnail image.
787///
788/// Created by [`ThumbnailsWriter::add_thumbnail`].
789pub struct ThumbnailWriter<'a, 'n: 'a> {
790    stc: Struct<'a, 'n>,
791}
792
793impl<'a, 'n: 'a> ThumbnailWriter<'a, 'n> {
794    fn start(stc: Struct<'a, 'n>) -> Self {
795        Self { stc }
796    }
797
798    /// Write the `xmpGImg:format` property with a custom format of the
799    /// thumbnail image. Must be "JPEG" for now.
800    pub fn format(&mut self, format: &str) -> &mut Self {
801        self.stc.element("format", Namespace::XmpImage).value(format);
802        self
803    }
804
805    /// Write the `xmpGImg:format` property with the value "JPEG".
806    pub fn format_jpeg(&mut self) -> &mut Self {
807        self.format("JPEG")
808    }
809
810    /// Write the `xmpGImg:width` property.
811    pub fn width(&mut self, width: u64) -> &mut Self {
812        self.stc.element("width", Namespace::XmpImage).value(width as i64);
813        self
814    }
815
816    /// Write the `xmpGImg:height` property.
817    pub fn height(&mut self, height: u64) -> &mut Self {
818        self.stc.element("height", Namespace::XmpImage).value(height as i64);
819        self
820    }
821
822    /// Write the `xmpGImg:image` property.
823    ///
824    /// The image must be a base64-encoded JPEG.
825    pub fn image(&mut self, image: &str) -> &mut Self {
826        self.stc.element("image", Namespace::XmpImage).value(image);
827        self
828    }
829}
830
831deref!('a, 'n, ThumbnailWriter<'a, 'n> => Struct<'a, 'n>, stc);
832
833/// Write a set of thumbnails.
834///
835/// Created by [`XmpWriter::thumbnails`].
836pub struct ThumbnailsWriter<'a, 'n: 'a> {
837    array: Array<'a, 'n>,
838}
839
840impl<'a, 'n: 'a> ThumbnailsWriter<'a, 'n> {
841    fn start(array: Array<'a, 'n>) -> Self {
842        Self { array }
843    }
844
845    /// Add a thumbnail.
846    pub fn add_thumbnail(&mut self) -> ThumbnailWriter<'_, 'n> {
847        ThumbnailWriter::start(self.array.element().obj())
848    }
849}
850
851deref!('a, 'n, ThumbnailsWriter<'a, 'n> => Array<'a, 'n>, array);
852
853/// Writer for a reference to a resource.
854///
855/// Created by [`XmpWriter::derived_from`], [`XmpWriter::managed_from`], or [`ResourceRefsWriter::add_ref`].
856pub struct ResourceRefWriter<'a, 'n: 'a> {
857    stc: Struct<'a, 'n>,
858}
859
860impl<'a, 'n: 'a> ResourceRefWriter<'a, 'n> {
861    fn start(stc: Struct<'a, 'n>) -> Self {
862        Self { stc }
863    }
864
865    /// Write the `stRef:alternatePaths` property.
866    ///
867    /// Fallback paths to the resource.
868    pub fn alternate_paths<'b>(
869        &mut self,
870        paths: impl IntoIterator<Item = &'b str>,
871    ) -> &mut Self {
872        self.stc
873            .element("alternatePaths", Namespace::XmpResourceRef)
874            .ordered_array(paths);
875        self
876    }
877
878    /// Write the `stRef:documentID` property.
879    ///
880    /// The [`XmpWriter::document_id`] of the referenced resource.
881    pub fn document_id(&mut self, id: &str) -> &mut Self {
882        self.stc.element("documentID", Namespace::XmpResourceRef).value(id);
883        self
884    }
885
886    /// Write the `stRef:filePath` property.
887    ///
888    /// The path or URL to the resource.
889    pub fn file_path(&mut self, path: &str) -> &mut Self {
890        self.stc.element("filePath", Namespace::XmpResourceRef).value(path);
891        self
892    }
893
894    /// Write the `stRef:instanceID` property.
895    ///
896    /// The [`XmpWriter::instance_id`] of the referenced resource.
897    pub fn instance_id(&mut self, id: &str) -> &mut Self {
898        self.stc.element("instanceID", Namespace::XmpResourceRef).value(id);
899        self
900    }
901
902    /// Write the `stRef:lastModifyDate` property.
903    ///
904    /// The last modification date of the resource. See [`ResourceEventWriter::when`].
905    pub fn last_modify_date(&mut self, date: DateTime) -> &mut Self {
906        self.stc
907            .element("lastModifyDate", Namespace::XmpResourceRef)
908            .value(date);
909        self
910    }
911
912    /// Write the `stRef:manager` property.
913    ///
914    /// The name of the application that manages the resource. See [`XmpWriter::manager`].
915    pub fn manager(&mut self, manager: &str) -> &mut Self {
916        self.stc.element("manager", Namespace::XmpResourceRef).value(manager);
917        self
918    }
919
920    /// Write the `stRef:managerVariant` property.
921    ///
922    /// The variant of the application that manages the resource. See [`XmpWriter::manager_variant`].
923    pub fn manager_variant(&mut self, variant: &str) -> &mut Self {
924        self.stc
925            .element("managerVariant", Namespace::XmpResourceRef)
926            .value(variant);
927        self
928    }
929
930    /// Write the `stRef:manageTo` property.
931    ///
932    /// The URI of the resource prior to being managed. See [`XmpWriter::manage_to`].
933    pub fn manage_to(&mut self, uri: &str) -> &mut Self {
934        self.stc.element("manageTo", Namespace::XmpResourceRef).value(uri);
935        self
936    }
937
938    /// Write the `stRef:manageUI` property.
939    ///
940    /// An URI to the user interface of the application that manages the resource. See [`XmpWriter::manage_ui`].
941    pub fn manage_ui(&mut self, uri: &str) -> &mut Self {
942        self.stc.element("manageTo", Namespace::XmpResourceRef).value(uri);
943        self
944    }
945
946    /// Write the `stRef:maskMarkers` property.
947    ///
948    /// Whether to process markers for resources in the [`XmpWriter::ingredients`] array.
949    pub fn mask_markers(&mut self, markers: MaskMarkers) -> &mut Self {
950        self.stc
951            .element("maskMarkers", Namespace::XmpResourceRef)
952            .value(markers);
953        self
954    }
955
956    /// Write the `stRef:partMapping` property.
957    ///
958    /// The name or URI of a mapping function to map `fromPart` to `toPart`.
959    pub fn part_mapping(&mut self, mapping: &str) -> &mut Self {
960        self.stc
961            .element("partMapping", Namespace::XmpResourceRef)
962            .value(mapping);
963        self
964    }
965
966    /// Write the `stRef:renditionClass` property.
967    ///
968    /// The rendition class of the referenced resource. See
969    /// [`XmpWriter::rendition_class`].
970    pub fn rendition_class(&mut self, rendition: RenditionClass) -> &mut Self {
971        self.stc
972            .element("renditionClass", Namespace::XmpResourceRef)
973            .value(rendition);
974        self
975    }
976
977    /// Write the `stRef:renditionParams` property.
978    ///
979    /// The rendition parameters of the referenced resource. See
980    /// [`XmpWriter::rendition_params`].
981    pub fn rendition_params(&mut self, params: &str) -> &mut Self {
982        self.stc
983            .element("renditionParams", Namespace::XmpResourceRef)
984            .value(params);
985        self
986    }
987
988    /// Write the `stRef:toPart` property.
989    ///
990    /// For a resource in a [`XmpWriter::ingredients`] array, the part of the root
991    /// resource that the the ingredient corresponds to.
992    pub fn to_part(&mut self, part: &str) -> &mut Self {
993        self.stc.element("toPart", Namespace::XmpResourceRef).value(part);
994        self
995    }
996
997    /// Write the `stRef:versionID` property.
998    ///
999    /// The referenced resource's version ID. See [`XmpWriter::version_id`].
1000    pub fn version_id(&mut self, id: &str) -> &mut Self {
1001        self.stc.element("versionID", Namespace::XmpResourceRef).value(id);
1002        self
1003    }
1004}
1005
1006deref!('a, 'n, ResourceRefWriter<'a, 'n> => Struct<'a, 'n>, stc);
1007
1008/// Writer for a resource reference array.
1009///
1010/// Created by [`XmpWriter::ingredients`].
1011pub struct ResourceRefsWriter<'a, 'n: 'a> {
1012    array: Array<'a, 'n>,
1013}
1014
1015impl<'a, 'n: 'a> ResourceRefsWriter<'a, 'n> {
1016    fn start(array: Array<'a, 'n>) -> Self {
1017        Self { array }
1018    }
1019
1020    /// Add a reference to the array.
1021    pub fn add_ref(&mut self) -> ResourceRefWriter<'_, 'n> {
1022        ResourceRefWriter::start(self.array.element().obj())
1023    }
1024}
1025
1026deref!('a, 'n, ResourceRefsWriter<'a, 'n> => Array<'a, 'n>, array);
1027
1028/// Writer for an event that occurred to a resource.
1029///
1030/// Created by [`VersionWriter::event`] and [`ResourceEventsWriter::add_event`].
1031pub struct ResourceEventWriter<'a, 'n: 'a> {
1032    stc: Struct<'a, 'n>,
1033}
1034
1035impl<'a, 'n: 'a> ResourceEventWriter<'a, 'n> {
1036    fn start(stc: Struct<'a, 'n>) -> Self {
1037        Self { stc }
1038    }
1039
1040    /// Write the `stEvt:action` property.
1041    ///
1042    /// The action that occurred to the resource.
1043    pub fn action(&mut self, action: ResourceEventAction) -> &mut Self {
1044        self.stc.element("action", Namespace::XmpResourceEvent).value(action);
1045        self
1046    }
1047
1048    /// Write the `stEvt:changed` property.
1049    ///
1050    /// Semicolon-separated list of the parts of the resource that changed.
1051    pub fn changed(&mut self, parts: &str) -> &mut Self {
1052        self.stc.element("changed", Namespace::XmpResourceEvent).value(parts);
1053        self
1054    }
1055    /// Write the `stEvt:instanceID` property.
1056    ///
1057    /// Value of the [`XmpWriter::instance_id`] property at the time of the action.
1058    pub fn instance_id(&mut self, id: &str) -> &mut Self {
1059        self.stc.element("instanceID", Namespace::XmpResourceEvent).value(id);
1060        self
1061    }
1062
1063    /// Write the `stEvt:changed` property.
1064    ///
1065    /// Additional parameters for the action.
1066    pub fn parameters(&mut self, params: &str) -> &mut Self {
1067        self.stc
1068            .element("parameters", Namespace::XmpResourceEvent)
1069            .value(params);
1070        self
1071    }
1072
1073    /// Write the `stEvt:softwareAgent` property.
1074    ///
1075    /// The name of the software agent that performed the action.
1076    pub fn software_agent(&mut self, agent: &str) -> &mut Self {
1077        self.stc
1078            .element("softwareAgent", Namespace::XmpResourceEvent)
1079            .value(agent);
1080        self
1081    }
1082
1083    /// Write the `stEvt:when` property.
1084    ///
1085    /// The date and time the action occurred.
1086    pub fn when(&mut self, date: DateTime) -> &mut Self {
1087        self.stc.element("when", Namespace::XmpResourceEvent).value(date);
1088        self
1089    }
1090}
1091
1092deref!('a, 'n, ResourceEventWriter<'a, 'n> => Struct<'a, 'n>, stc);
1093
1094/// Writer for a resource event array.
1095///
1096/// Created by [`XmpWriter::history`].
1097pub struct ResourceEventsWriter<'a, 'n: 'a> {
1098    array: Array<'a, 'n>,
1099}
1100
1101impl<'a, 'n: 'a> ResourceEventsWriter<'a, 'n> {
1102    fn start(array: Array<'a, 'n>) -> Self {
1103        Self { array }
1104    }
1105
1106    /// Add an event to the array.
1107    pub fn add_event(&mut self) -> ResourceEventWriter<'_, 'n> {
1108        ResourceEventWriter::start(self.array.element().obj())
1109    }
1110}
1111
1112deref!('a, 'n, ResourceEventsWriter<'a, 'n> => Array<'a, 'n>, array);
1113
1114/// Writer for an item in a Pantry array.
1115///
1116/// Use the `Deref` impl to access the underlying [`Struct`] and add properties.
1117/// Created by [`PantryWriter::add_item`].
1118pub struct PantryItemWriter<'a, 'n: 'a> {
1119    stc: Struct<'a, 'n>,
1120}
1121
1122impl<'a, 'n: 'a> PantryItemWriter<'a, 'n> {
1123    fn start(stc: Struct<'a, 'n>) -> Self {
1124        Self { stc }
1125    }
1126
1127    /// Write the `xmpMM:instanceID` property. Required.
1128    pub fn instance_id(&mut self, id: &str) -> &mut Self {
1129        self.stc.element("instanceID", Namespace::XmpMedia).value(id);
1130        self
1131    }
1132}
1133
1134deref!('a, 'n, PantryItemWriter<'a, 'n> => Struct<'a, 'n>, stc);
1135
1136/// Writer for a Pantry array.
1137pub struct PantryWriter<'a, 'n: 'a> {
1138    array: Array<'a, 'n>,
1139}
1140
1141impl<'a, 'n: 'a> PantryWriter<'a, 'n> {
1142    fn start(array: Array<'a, 'n>) -> Self {
1143        Self { array }
1144    }
1145
1146    /// Add an item to the array.
1147    pub fn add_item(&mut self) -> PantryItemWriter<'_, 'n> {
1148        PantryItemWriter::start(self.array.element().obj())
1149    }
1150}
1151
1152deref!('a, 'n, PantryWriter<'a, 'n> => Array<'a, 'n>, array);
1153
1154/// Writer for a version struct.
1155///
1156/// Created by [`VersionsWriter::add_version`].
1157pub struct VersionWriter<'a, 'n: 'a> {
1158    stc: Struct<'a, 'n>,
1159}
1160
1161impl<'a, 'n: 'a> VersionWriter<'a, 'n> {
1162    fn start(stc: Struct<'a, 'n>) -> Self {
1163        Self { stc }
1164    }
1165
1166    /// Write the `stVer:comments` property.
1167    ///
1168    /// Comments about the version.
1169    pub fn comments(&mut self, comments: &str) -> &mut Self {
1170        self.stc.element("comments", Namespace::XmpVersion).value(comments);
1171        self
1172    }
1173
1174    /// Start writing the `stVer:event` property.
1175    ///
1176    /// The event that created the version.
1177    pub fn event(&mut self) -> ResourceEventWriter<'_, 'n> {
1178        ResourceEventWriter::start(self.stc.element("event", Namespace::XmpVersion).obj())
1179    }
1180
1181    /// Write the `stVer:modifier` property.
1182    ///
1183    /// The person or organization that created the version.
1184    pub fn modifier(&mut self, modifier: &str) -> &mut Self {
1185        self.stc.element("modifier", Namespace::XmpVersion).value(modifier);
1186        self
1187    }
1188
1189    /// Write the `stVer:modifyDate` property.
1190    ///
1191    /// The date and time the version was created.
1192    pub fn modify_date(&mut self, date: DateTime) -> &mut Self {
1193        self.stc.element("modifyDate", Namespace::XmpVersion).value(date);
1194        self
1195    }
1196
1197    /// Write the `stVer:version` property.
1198    ///
1199    /// The new version number.
1200    pub fn version(&mut self, version: &str) -> &mut Self {
1201        self.stc.element("version", Namespace::XmpVersion).value(version);
1202        self
1203    }
1204}
1205
1206deref!('a, 'n, VersionWriter<'a, 'n> => Struct<'a, 'n>, stc);
1207
1208/// Writer for a versions array.
1209///
1210/// Created by [`XmpWriter::version_ref`].
1211pub struct VersionsWriter<'a, 'n: 'a> {
1212    array: Array<'a, 'n>,
1213}
1214
1215impl<'a, 'n: 'a> VersionsWriter<'a, 'n> {
1216    fn start(array: Array<'a, 'n>) -> Self {
1217        Self { array }
1218    }
1219
1220    /// Add a version to the array.
1221    pub fn add_version(&mut self) -> VersionWriter<'_, 'n> {
1222        VersionWriter::start(self.array.element().obj())
1223    }
1224}
1225
1226deref!('a, 'n, VersionsWriter<'a, 'n> => Array<'a, 'n>, array);
1227
1228/// Writer for a job struct.
1229///
1230/// Created by [`JobsWriter::add_job`].
1231pub struct JobWriter<'a, 'n: 'a> {
1232    stc: Struct<'a, 'n>,
1233}
1234
1235impl<'a, 'n: 'a> JobWriter<'a, 'n> {
1236    fn start(stc: Struct<'a, 'n>) -> Self {
1237        Self { stc }
1238    }
1239
1240    /// Write the `stJob:id` property.
1241    ///
1242    /// The unique identifier for the job.
1243    pub fn id(&mut self, id: &str) -> &mut Self {
1244        self.stc.element("id", Namespace::XmpJob).value(id);
1245        self
1246    }
1247
1248    /// Write the `stJob:name` property.
1249    ///
1250    /// The name of the job.
1251    pub fn name(&mut self, name: &str) -> &mut Self {
1252        self.stc.element("name", Namespace::XmpJob).value(name);
1253        self
1254    }
1255
1256    /// Write the `stJob:url` property.
1257    ///
1258    /// Reference an external job management file.
1259    pub fn url(&mut self, url: &str) -> &mut Self {
1260        self.stc.element("url", Namespace::XmpJob).value(url);
1261        self
1262    }
1263}
1264
1265deref!('a, 'n, JobWriter<'a, 'n> => Struct<'a, 'n>, stc);
1266
1267/// Writer for a job array.
1268///
1269/// Created by [`XmpWriter::jobs`].
1270pub struct JobsWriter<'a, 'n: 'a> {
1271    array: Array<'a, 'n>,
1272}
1273
1274impl<'a, 'n: 'a> JobsWriter<'a, 'n> {
1275    fn start(array: Array<'a, 'n>) -> Self {
1276        Self { array }
1277    }
1278
1279    /// Add a job to the array.
1280    pub fn add_job(&mut self) -> JobWriter<'_, 'n> {
1281        JobWriter::start(self.array.element().obj())
1282    }
1283}
1284
1285deref!('a, 'n, JobsWriter<'a, 'n> => Array<'a, 'n>, array);
1286
1287/// A writer for colorant structs.
1288///
1289/// Created by [`ColorantsWriter::add_colorant`].
1290pub struct ColorantWriter<'a, 'n: 'a> {
1291    stc: Struct<'a, 'n>,
1292}
1293
1294impl<'a, 'n: 'a> ColorantWriter<'a, 'n> {
1295    fn start(stc: Struct<'a, 'n>) -> Self {
1296        Self { stc }
1297    }
1298
1299    /// Write the `xmpG:type` property.
1300    ///
1301    /// Whether this is a spot color or a process color.
1302    pub fn type_(&mut self, kind: ColorantType) -> &mut Self {
1303        self.stc.element("type", Namespace::XmpColorant).value(kind);
1304        self
1305    }
1306
1307    /// Write the `xmpG:swatchName` property.
1308    ///
1309    /// The name of the colorant.
1310    pub fn swatch_name(&mut self, name: &str) -> &mut Self {
1311        self.stc.element("swatchName", Namespace::XmpColorant).value(name);
1312        self
1313    }
1314
1315    /// Write the `xmpG:colorantMode` property.
1316    ///
1317    /// In which color space this colorant is defined.
1318    pub fn colorant_mode(&mut self, mode: ColorantMode) -> &mut Self {
1319        self.stc.element("colorantMode", Namespace::XmpColorant).value(mode);
1320        self
1321    }
1322
1323    /// Write the `xmpG:colorantType` property.
1324    ///
1325    /// The `L` value of a colorant with `xmpG:colorantMode` set to `Lab`.
1326    pub fn l(&mut self, l: f64) -> &mut Self {
1327        self.stc.element("L", Namespace::XmpColorant).value(l);
1328        self
1329    }
1330
1331    /// Write the `xmpG:a` property.
1332    ///
1333    /// The `a` value of a colorant with `xmpG:colorantMode` set to `Lab`.
1334    pub fn a(&mut self, a: i32) -> &mut Self {
1335        self.stc.element("A", Namespace::XmpColorant).value(a);
1336        self
1337    }
1338
1339    /// Write the `xmpG:b` property.
1340    ///
1341    /// The `b` value of a colorant with `xmpG:colorantMode` set to `Lab`.
1342    pub fn b(&mut self, b: i32) -> &mut Self {
1343        self.stc.element("B", Namespace::XmpColorant).value(b);
1344        self
1345    }
1346
1347    /// Write the `xmpG:black` property.
1348    ///
1349    /// The `K` value of a colorant with `xmpG:colorantMode` set to `CMYK`.
1350    pub fn black(&mut self, black: f64) -> &mut Self {
1351        self.stc.element("black", Namespace::XmpColorant).value(black);
1352        self
1353    }
1354
1355    /// Write the `xmpG:cyan` property.
1356    ///
1357    /// The `C` value of a colorant with `xmpG:colorantMode` set to `CMYK`.
1358    pub fn cyan(&mut self, cyan: f64) -> &mut Self {
1359        self.stc.element("cyan", Namespace::XmpColorant).value(cyan);
1360        self
1361    }
1362
1363    /// Write the `xmpG:magenta` property.
1364    ///
1365    /// The `M` value of a colorant with `xmpG:colorantMode` set to `CMYK`.
1366    pub fn magenta(&mut self, magenta: f64) -> &mut Self {
1367        self.stc.element("magenta", Namespace::XmpColorant).value(magenta);
1368        self
1369    }
1370
1371    /// Write the `xmpG:yellow` property.
1372    ///
1373    /// The `Y` value of a colorant with `xmpG:colorantMode` set to `CMYK`.
1374    pub fn yellow(&mut self, yellow: f64) -> &mut Self {
1375        self.stc.element("yellow", Namespace::XmpColorant).value(yellow);
1376        self
1377    }
1378
1379    /// Write the `xmpG:red` property.
1380    ///
1381    /// The `R` value of a colorant with `xmpG:colorantMode` set to `RGB`.
1382    pub fn red(&mut self, red: i32) -> &mut Self {
1383        self.stc.element("red", Namespace::XmpColorant).value(red);
1384        self
1385    }
1386
1387    /// Write the `xmpG:green` property.
1388    ///
1389    /// The `G` value of a colorant with `xmpG:colorantMode` set to `RGB`.
1390    pub fn green(&mut self, green: i32) -> &mut Self {
1391        self.stc.element("green", Namespace::XmpColorant).value(green);
1392        self
1393    }
1394
1395    /// Write the `xmpG:blue` property.
1396    ///
1397    /// The `B` value of a colorant with `xmpG:colorantMode` set to `RGB`.
1398    pub fn blue(&mut self, blue: i32) -> &mut Self {
1399        self.stc.element("blue", Namespace::XmpColorant).value(blue);
1400        self
1401    }
1402}
1403
1404deref!('a, 'n, ColorantWriter<'a, 'n> => Struct<'a, 'n>, stc);
1405
1406/// Writer for an array of colorants.
1407///
1408/// Created by [`XmpWriter::colorants`].
1409pub struct ColorantsWriter<'a, 'n: 'a> {
1410    array: Array<'a, 'n>,
1411}
1412
1413impl<'a, 'n> ColorantsWriter<'a, 'n> {
1414    fn start(array: Array<'a, 'n>) -> Self {
1415        Self { array }
1416    }
1417
1418    /// Add a new colorant to the array.
1419    pub fn add_colorant(&mut self) -> ColorantWriter<'_, 'n> {
1420        ColorantWriter::start(self.array.element().obj())
1421    }
1422}
1423
1424deref!('a, 'n, ColorantsWriter<'a, 'n> => Array<'a, 'n>, array);
1425
1426/// Writer for a dimensions struct.
1427///
1428/// Created by [`XmpWriter::max_page_size`].
1429pub struct DimensionsWriter<'a, 'n: 'a> {
1430    stc: Struct<'a, 'n>,
1431}
1432
1433impl<'a, 'n> DimensionsWriter<'a, 'n> {
1434    fn start(stc: Struct<'a, 'n>) -> Self {
1435        Self { stc }
1436    }
1437
1438    /// Write the `stDim:w` property.
1439    ///
1440    /// The width of the resource.
1441    pub fn width(&mut self, width: f64) -> &mut Self {
1442        self.stc.element("w", Namespace::XmpDimensions).value(width);
1443        self
1444    }
1445
1446    /// Write the `stDim:h` property.
1447    ///
1448    /// The height of the resource.
1449    pub fn height(&mut self, height: f64) -> &mut Self {
1450        self.stc.element("h", Namespace::XmpDimensions).value(height);
1451        self
1452    }
1453
1454    /// Write the `stDim:unit` property.
1455    ///
1456    /// The unit of the width and height properties.
1457    pub fn unit(&mut self, unit: DimensionUnit) -> &mut Self {
1458        self.stc.element("unit", Namespace::XmpDimensions).value(unit);
1459        self
1460    }
1461}
1462
1463deref!('a, 'n, DimensionsWriter<'a, 'n> => Struct<'a, 'n>, stc);
1464
1465/// Writer for a font struct.
1466///
1467/// Created by [`XmpWriter::fonts`].
1468pub struct FontWriter<'a, 'n: 'a> {
1469    stc: Struct<'a, 'n>,
1470}
1471
1472impl<'a, 'n: 'a> FontWriter<'a, 'n> {
1473    fn start(stc: Struct<'a, 'n>) -> Self {
1474        Self { stc }
1475    }
1476
1477    /// Write the `stFnt:childFontFiles` property.
1478    ///
1479    /// An array of font files that make up this font.
1480    pub fn child_font_files<'b>(
1481        &mut self,
1482        files: impl IntoIterator<Item = &'b str>,
1483    ) -> &mut Self {
1484        self.stc
1485            .element("childFontFiles", Namespace::XmpFont)
1486            .ordered_array(files);
1487        self
1488    }
1489
1490    /// Write the `stFnt:composite` property.
1491    ///
1492    /// Whether the font is a composite font.
1493    pub fn composite(&mut self, composite: bool) -> &mut Self {
1494        self.stc.element("composite", Namespace::XmpFont).value(composite);
1495        self
1496    }
1497
1498    /// Write the `stFnt:fontFace` property.
1499    ///
1500    /// The font face name.
1501    pub fn font_face(&mut self, face: &str) -> &mut Self {
1502        self.stc.element("fontFace", Namespace::XmpFont).value(face);
1503        self
1504    }
1505
1506    /// Write the `stFnt:fontFamily` property.
1507    ///
1508    /// The font family name.
1509    pub fn font_family(&mut self, family: &str) -> &mut Self {
1510        self.stc.element("fontFamily", Namespace::XmpFont).value(family);
1511        self
1512    }
1513
1514    /// Write the `stFnt:fontFile` property.
1515    ///
1516    /// The font file name.
1517    pub fn font_file(&mut self, file_name: &str) -> &mut Self {
1518        self.stc.element("fontFileName", Namespace::XmpFont).value(file_name);
1519        self
1520    }
1521
1522    /// Write the `stFnt:fontName` property.
1523    ///
1524    /// The PostScript name of the font.
1525    pub fn font_name(&mut self, name: &str) -> &mut Self {
1526        self.stc.element("fontName", Namespace::XmpFont).value(name);
1527        self
1528    }
1529
1530    /// Write the `stFnt:fontType` property.
1531    ///
1532    /// The font type.
1533    pub fn font_type(&mut self, font_type: FontType) -> &mut Self {
1534        self.stc.element("fontType", Namespace::XmpFont).value(font_type);
1535        self
1536    }
1537
1538    /// Write the `stFnt:versionString` property.
1539    ///
1540    /// The version string of the font.
1541    /// Must be chosen depending on the font type.
1542    /// - **Type 1**: `/version`
1543    /// - **TrueType / OpenType**: `nameId 5`
1544    /// - **CID**: `/CIDFontVersion`
1545    /// - **Bitmap Font**: must be empty
1546    pub fn version_string(&mut self, version: &str) -> &mut Self {
1547        self.stc.element("versionString", Namespace::XmpFont).value(version);
1548        self
1549    }
1550}
1551
1552deref!('a, 'n, FontWriter<'a, 'n> => Struct<'a, 'n>, stc);
1553
1554/// Writer for an array of fonts.
1555///
1556/// Created by [`XmpWriter::fonts`].
1557pub struct FontsWriter<'a, 'n: 'a> {
1558    array: Array<'a, 'n>,
1559}
1560
1561impl<'a, 'n: 'a> FontsWriter<'a, 'n> {
1562    fn start(array: Array<'a, 'n>) -> Self {
1563        Self { array }
1564    }
1565
1566    /// Add a new font to the array.
1567    pub fn add_font(&mut self) -> FontWriter<'_, 'n> {
1568        FontWriter::start(self.array.element().obj())
1569    }
1570}
1571
1572deref!('a, 'n, FontsWriter<'a, 'n> => Array<'a, 'n>, array);