pdf_writer/
structure.rs

1use std::io::{Cursor, Write};
2use std::num::NonZeroU16;
3
4use super::*;
5use crate::color::SeparationInfo;
6use crate::object::TextStrLike;
7
8/// Writer for a _document catalog dictionary_.
9///
10/// This struct is created by [`Pdf::catalog`].
11pub struct Catalog<'a> {
12    dict: Dict<'a>,
13}
14
15writer!(Catalog: |obj| {
16    let mut dict = obj.dict();
17    dict.pair(Name(b"Type"), Name(b"Catalog"));
18    Self { dict }
19});
20
21impl Catalog<'_> {
22    /// Write the `/Pages` attribute pointing to the root page tree.
23    pub fn pages(&mut self, id: Ref) -> &mut Self {
24        self.pair(Name(b"Pages"), id);
25        self
26    }
27
28    /// Write the `/PageLayout` attribute to determine how the viewer will
29    /// display the document's pages.
30    pub fn page_layout(&mut self, layout: PageLayout) -> &mut Self {
31        self.pair(Name(b"PageLayout"), layout.to_name());
32        self
33    }
34
35    /// Start writing the `/PageLabels` number tree. PDF 1.3+.
36    pub fn page_labels(&mut self) -> NumberTree<'_, Ref> {
37        self.insert(Name(b"PageLabels")).start()
38    }
39
40    /// Write the `/PageMode` attribute to set which chrome elements the viewer
41    /// should show.
42    pub fn page_mode(&mut self, mode: PageMode) -> &mut Self {
43        self.pair(Name(b"PageMode"), mode.to_name());
44        self
45    }
46
47    /// Start writing the `/ViewerPreferences` dictionary. PDF 1.2+.
48    pub fn viewer_preferences(&mut self) -> ViewerPreferences<'_> {
49        self.insert(Name(b"ViewerPreferences")).start()
50    }
51
52    /// Start writing the `/Names` dictionary. PDF 1.2+.
53    pub fn names(&mut self) -> Names<'_> {
54        self.insert(Name(b"Names")).start()
55    }
56
57    /// Write the `/Dests` attribute pointing to a
58    /// [named destinations dictionary](Chunk::destinations). PDF 1.1+.
59    pub fn destinations(&mut self, id: Ref) -> &mut Self {
60        self.pair(Name(b"Dests"), id);
61        self
62    }
63
64    /// Write the `/Outlines` attribute pointing to the root
65    /// [outline dictionary](Outline).
66    pub fn outlines(&mut self, id: Ref) -> &mut Self {
67        self.pair(Name(b"Outlines"), id);
68        self
69    }
70
71    /// Start writing the `/StructTreeRoot` attribute to specify the root of the
72    /// document's structure tree. PDF 1.3+.
73    ///
74    /// Must be present in some PDF/A profiles like PDF/A-2a.
75    pub fn struct_tree_root(&mut self) -> StructTreeRoot<'_> {
76        self.insert(Name(b"StructTreeRoot")).start()
77    }
78
79    /// Start writing the `/MarkInfo` dictionary to specify this document's
80    /// conformance with the tagged PDF specification. PDF 1.4+.
81    ///
82    /// Must be present in some PDF/A profiles like PDF/A-2a.
83    pub fn mark_info(&mut self) -> MarkInfo<'_> {
84        self.insert(Name(b"MarkInfo")).start()
85    }
86
87    /// Write the `/Lang` attribute to specify the language of the document as a
88    /// RFC 3066 language tag. PDF 1.4+.
89    ///
90    /// Required in some PDF/A profiles like PDF/A-2a.
91    pub fn lang(&mut self, lang: TextStr) -> &mut Self {
92        self.pair(Name(b"Lang"), lang);
93        self
94    }
95
96    /// Write the `/Version` attribute to override the PDF version stated in the
97    /// header. PDF 1.4+.
98    pub fn version(&mut self, major: u8, minor: u8) -> &mut Self {
99        self.pair(Name(b"Version"), Name(format!("{major}.{minor}").as_bytes()));
100        self
101    }
102
103    /// Start writing the `/AA` dictionary. This sets the additional actions for
104    /// the whole document. PDF 1.4+.
105    ///
106    /// Note that this attribute is forbidden in PDF/A.
107    pub fn additional_actions(&mut self) -> AdditionalActions<'_> {
108        self.insert(Name(b"AA")).start()
109    }
110
111    /// Start writing the `/AcroForm` dictionary to specify the document wide
112    /// form. PDF 1.2+.
113    pub fn form(&mut self) -> Form<'_> {
114        self.insert(Name(b"AcroForm")).start()
115    }
116
117    /// Write the `/Metadata` attribute to specify the document's metadata. PDF
118    /// 1.4+.
119    ///
120    /// The reference shall point to a [metadata stream](Metadata).
121    pub fn metadata(&mut self, id: Ref) -> &mut Self {
122        self.pair(Name(b"Metadata"), id);
123        self
124    }
125
126    /// Start writing the `/Extensions` dictionary to specify which PDF
127    /// extensions are in use in the document. PDF 1.5+.
128    ///
129    /// The dictionary maps a vendor name to an extension dictionary. The Adobe
130    /// PDF extensions use the Name prefix `ADBE`.
131    pub fn extensions(&mut self) -> TypedDict<'_, DeveloperExtension<'_>> {
132        self.insert(Name(b"Extensions")).dict().typed()
133    }
134
135    /// Start writing the `/SeparationInfo` dictionary to specify which
136    /// separation colors are in use on the page and how it relates to other
137    /// pages in the document. PDF 1.3+.
138    pub fn separation_info(&mut self) -> SeparationInfo<'_> {
139        self.insert(Name(b"SeparationInfo")).start()
140    }
141
142    /// Start writing the `/OutputIntents` array to specify the output
143    /// destinations for the document. PDF 1.4+.
144    ///
145    /// Each entry in the array is an [output intent
146    /// dictionary.](writers::OutputIntent)
147    ///
148    /// Must be present in PDF/X documents, encouraged in PDF/A documents.
149    pub fn output_intents(&mut self) -> TypedArray<'_, OutputIntent<'_>> {
150        self.insert(Name(b"OutputIntents")).array().typed()
151    }
152
153    /// Start writing the `/AF` array to specify the associated files of the
154    /// document. PDF 2.0+ or PDF/A-3.
155    pub fn associated_files(&mut self) -> TypedArray<'_, FileSpec<'_>> {
156        self.insert(Name(b"AF")).array().typed()
157    }
158}
159
160deref!('a, Catalog<'a> => Dict<'a>, dict);
161
162/// How the viewer should lay out the pages in the document.
163#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
164pub enum PageLayout {
165    /// Only a single page at a time.
166    #[default]
167    SinglePage,
168    /// A single, continuously scrolling column of pages.
169    OneColumn,
170    /// Two continuously scrolling columns of pages, laid out with odd-numbered
171    /// pages on the left.
172    TwoColumnLeft,
173    /// Two continuously scrolling columns of pages, laid out with odd-numbered
174    /// pages on the right (like in a left-bound book).
175    TwoColumnRight,
176    /// Only two pages are visible at a time, laid out with odd-numbered pages
177    /// on the left. PDF 1.5+.
178    TwoPageLeft,
179    /// Only two pages are visible at a time, laid out with odd-numbered pages
180    /// on the right (like in a left-bound book). PDF 1.5+.
181    TwoPageRight,
182}
183
184impl PageLayout {
185    pub(crate) fn to_name(self) -> Name<'static> {
186        match self {
187            Self::SinglePage => Name(b"SinglePage"),
188            Self::OneColumn => Name(b"OneColumn"),
189            Self::TwoColumnLeft => Name(b"TwoColumnLeft"),
190            Self::TwoColumnRight => Name(b"TwoColumnRight"),
191            Self::TwoPageLeft => Name(b"TwoPageLeft"),
192            Self::TwoPageRight => Name(b"TwoPageRight"),
193        }
194    }
195}
196
197/// Elements of the viewer chrome that should be visible when opening the
198/// document.
199#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
200pub enum PageMode {
201    /// Neither the document outline panel nor a panel with page preview images
202    /// are visible.
203    #[default]
204    UseNone,
205    /// The document outline panel is visible.
206    UseOutlines,
207    /// A panel with page preview images is visible.
208    UseThumbs,
209    /// Show the document page in full screen mode, with no chrome.
210    FullScreen,
211    /// Show the optional content group panel. PDF 1.5+.
212    UseOC,
213    /// Show the attachments panel. PDF 1.6+.
214    UseAttachments,
215}
216
217impl PageMode {
218    pub(crate) fn to_name(self) -> Name<'static> {
219        match self {
220            Self::UseNone => Name(b"UseNone"),
221            Self::UseOutlines => Name(b"UseOutlines"),
222            Self::UseThumbs => Name(b"UseThumbs"),
223            Self::FullScreen => Name(b"FullScreen"),
224            Self::UseOC => Name(b"UseOC"),
225            Self::UseAttachments => Name(b"UseAttachments"),
226        }
227    }
228}
229
230/// Writer for a _developer extension dictionary_. PDF 1.7+.
231///
232/// An array of this struct is created by [`Catalog::extensions`].
233pub struct DeveloperExtension<'a> {
234    dict: Dict<'a>,
235}
236
237writer!(DeveloperExtension: |obj| {
238    let mut dict = obj.dict();
239    dict.pair(Name(b"Type"), Name(b"DeveloperExtensions"));
240    Self { dict }
241});
242
243impl DeveloperExtension<'_> {
244    /// Write the `/BaseVersion` attribute to specify the version of PDF this
245    /// extension is based on. Required.
246    pub fn base_version(&mut self, major: u8, minor: u8) -> &mut Self {
247        self.pair(Name(b"BaseVersion"), Name(format!("{major}.{minor}").as_bytes()));
248        self
249    }
250
251    /// Write the `/ExtensionLevel` attribute to specify the version of the
252    /// extension. Required.
253    pub fn extension_level(&mut self, level: i32) -> &mut Self {
254        self.pair(Name(b"ExtensionLevel"), level);
255        self
256    }
257}
258
259deref!('a, DeveloperExtension<'a> => Dict<'a>, dict);
260
261/// Writer for a _viewer preference dictionary_.
262///
263/// This struct is created by [`Catalog::viewer_preferences`].
264pub struct ViewerPreferences<'a> {
265    dict: Dict<'a>,
266}
267
268writer!(ViewerPreferences: |obj| Self { dict: obj.dict() });
269
270impl ViewerPreferences<'_> {
271    /// Write the `/HideToolbar` attribute to set whether the viewer should hide
272    /// its toolbars while the document is open.
273    pub fn hide_toolbar(&mut self, hide: bool) -> &mut Self {
274        self.pair(Name(b"HideToolbar"), hide);
275        self
276    }
277
278    /// Write the `/HideMenubar` attribute to set whether the viewer should hide
279    /// its menu bar while the document is open.
280    pub fn hide_menubar(&mut self, hide: bool) -> &mut Self {
281        self.pair(Name(b"HideMenubar"), hide);
282        self
283    }
284
285    /// Write the `/FitWindow` attribute to set whether the viewer should resize
286    /// its window to the size of the first page.
287    pub fn fit_window(&mut self, fit: bool) -> &mut Self {
288        self.pair(Name(b"FitWindow"), fit);
289        self
290    }
291
292    /// Write the `/CenterWindow` attribute to set whether the viewer should
293    /// center its window on the screen.
294    pub fn center_window(&mut self, center: bool) -> &mut Self {
295        self.pair(Name(b"CenterWindow"), center);
296        self
297    }
298
299    /// Write the `/DisplayDocTitle` attribute to set whether the viewer should
300    /// display the document's title from the `Title` entry as the window's title.
301    /// PDF 1.4+
302    pub fn display_doc_title(&mut self, display: bool) -> &mut Self {
303        self.pair(Name(b"DisplayDocTitle"), display);
304        self
305    }
306
307    /// Write the `/NonFullScreenPageMode` attribute to set which chrome
308    /// elements the viewer should show for a document which requests full
309    /// screen rendering in its catalog when it is not shown in full screen
310    /// mode.
311    ///
312    /// Panics if `mode` is [`PageMode::FullScreen`].
313    pub fn non_full_screen_page_mode(&mut self, mode: PageMode) -> &mut Self {
314        assert!(mode != PageMode::FullScreen, "mode must not full screen");
315        self.pair(Name(b"NonFullScreenPageMode"), mode.to_name());
316        self
317    }
318
319    /// Write the `/Direction` attribute to aid the viewer in how to lay out the
320    /// pages visually. PDF 1.3+.
321    pub fn direction(&mut self, dir: Direction) -> &mut Self {
322        self.pair(Name(b"Direction"), dir.to_name());
323        self
324    }
325}
326
327deref!('a, ViewerPreferences<'a> => Dict<'a>, dict);
328
329/// Writer for a _structure tree root dictionary_. PDF 1.3+
330///
331/// This struct is created by [`Catalog::struct_tree_root`].
332pub struct StructTreeRoot<'a> {
333    dict: Dict<'a>,
334}
335
336writer!(StructTreeRoot: |obj| {
337    let mut dict = obj.dict();
338    dict.pair(Name(b"Type"), Name(b"StructTreeRoot"));
339    Self { dict }
340});
341
342impl StructTreeRoot<'_> {
343    /// Write the `/K` attribute to reference the immediate child of this
344    /// element.
345    pub fn child(&mut self, id: Ref) -> &mut Self {
346        self.dict.pair(Name(b"K"), id);
347        self
348    }
349
350    /// Start writing the `/K` attribute to reference the immediate children of
351    /// this element.
352    pub fn children(&mut self) -> TypedArray<'_, Ref> {
353        self.dict.insert(Name(b"K")).array().typed()
354    }
355
356    /// Start writing the `/IDTree` attribute to map element identifiers to
357    /// their corresponding structure element objects. Required if any elements
358    /// have element identifiers.
359    pub fn id_tree(&mut self) -> NameTree<'_, Ref> {
360        self.dict.insert(Name(b"IDTree")).start()
361    }
362
363    /// Start writing the `/ParentTree` attribute to maps structure elements to
364    /// the content items they belong to. Required if any structure elements
365    /// contain content items.
366    pub fn parent_tree(&mut self) -> NumberTree<'_, Ref> {
367        self.dict.insert(Name(b"ParentTree")).start()
368    }
369
370    /// Write the `/ParentTreeNextKey` attribute to specify the next available key
371    /// for the `/ParentTree` dictionary.
372    pub fn parent_tree_next_key(&mut self, key: i32) -> &mut Self {
373        self.dict.pair(Name(b"ParentTreeNextKey"), key);
374        self
375    }
376
377    /// Start writing the `/RoleMap` attribute to map structure element names to
378    /// their approximate equivalents from the standard set of types. PDF 1.4+.
379    ///
380    /// For PDF 2.0 documents, note that this mapping maps to the PDF 1.7 roles.
381    /// For a namespace-aware role-mapping mechanism, see
382    /// [`Namespace::role_map_ns`].
383    pub fn role_map(&mut self) -> RoleMap<'_> {
384        self.dict.insert(Name(b"RoleMap")).start()
385    }
386
387    /// Start writing the `/ClassMap` attribute to map objects designating
388    /// attribute classes to their corresponding attribute objects or arrays
389    /// thereof.
390    pub fn class_map(&mut self) -> ClassMap<'_> {
391        self.dict.insert(Name(b"ClassMap")).start()
392    }
393
394    /// Start writing the `/Namespaces` attribute to specify the namespaces
395    /// occurring in the document. Required if namespaced structure types or
396    /// attributes, including the standard namespace for PDF 2.0, are used.
397    /// Create these dictionaries with [`Chunk::namespace`] PDF 2.0+.
398    pub fn namespaces(&mut self) -> TypedArray<'_, Ref> {
399        self.dict.insert(Name(b"Namespaces")).array().typed()
400    }
401
402    /// Start writing the `PronunciationLexicon` attribute to specify one or
403    /// multiple pronunciation lexicons for the document. PDF 2.0+.
404    ///
405    /// The lexicons shall be XML files conforming to the Pronunciation Lexicon
406    /// Specification (PLS) Version 1.0. Each entry in the array is an indirect
407    /// reference to a [`FileSpec`] dictionary for a lexicon file.
408    pub fn pronunciation_lexicon(&mut self) -> TypedArray<'_, Ref> {
409        self.dict.insert(Name(b"PronunciationLexicon")).array().typed()
410    }
411
412    /// Start writing the `/AF` attribute to specify one or multiple files
413    /// associated with the entire structure tree. PDF 2.0+.
414    pub fn associated_files(&mut self) -> TypedArray<'_, FileSpec<'_>> {
415        self.dict.insert(Name(b"AF")).array().typed()
416    }
417}
418
419deref!('a, StructTreeRoot<'a> => Dict<'a>, dict);
420
421/// Writer for a _structure element dictionary_. PDF 1.3+
422pub struct StructElement<'a> {
423    dict: Dict<'a>,
424}
425
426writer!(StructElement: |obj| {
427    let mut dict = obj.dict();
428    dict.pair(Name(b"Type"), Name(b"StructElem"));
429    Self { dict }
430});
431
432impl StructElement<'_> {
433    /// Write the `/S` attribute to specify the role of this structure element
434    /// using elements from PDF 1.7 and below. Required if neither a PDF 2.0
435    /// structure type is defined using [`Self::kind_2`] nor a custom type is
436    /// specified using [`Self::custom_kind`].
437    pub fn kind(&mut self, role: StructRole) -> &mut Self {
438        self.dict.pair(Name(b"S"), role.to_name());
439        self
440    }
441
442    /// Write the `/S` attribute and the `/NS` attribute to specify the role of
443    /// this structure element in the PDF 2.0 namespace. Required if neither a
444    /// PDF 1.7 structure type is defined using [`Self::kind`] nor a custom type
445    /// is specified using [`Self::custom_kind`].
446    ///
447    /// The `pdf_2_ns` parameter is an indirect reference to a PDF 2.0 namespace
448    /// dictionary. You can create this dictionary by using [`Chunk::namespace`]
449    /// and then calling [`Namespace::pdf_2_ns`] on the returned writer.
450    pub fn kind_2(&mut self, role: StructRole2, pdf_2_ns: Ref) -> &mut Self {
451        self.dict.pair(Name(b"S"), role.to_name(&mut [0; 6]));
452        self.namespace(pdf_2_ns)
453    }
454
455    /// Write the `/S` attribute to specify the role of this structure element
456    /// as a custom name. Required if no standard type is specified with
457    /// [`Self::kind`].
458    ///
459    /// In some PDF/A profiles like PDF/A-2a, custom kinds must be mapped to
460    /// their closest standard type in the role map.
461    ///
462    /// When using the namespaced model of PDF 2.0, using this may also require
463    /// setting a namespace using [`Self::namespace`].
464    pub fn custom_kind(&mut self, name: Name) -> &mut Self {
465        self.dict.pair(Name(b"S"), name);
466        self
467    }
468
469    /// Write the `/P` attribute to specify the parent of this structure
470    /// element. Required.
471    pub fn parent(&mut self, parent: Ref) -> &mut Self {
472        self.dict.pair(Name(b"P"), parent);
473        self
474    }
475
476    /// Write the `/ID` attribute to specify the element identifier of this
477    /// structure element.
478    pub fn id(&mut self, id: Str) -> &mut Self {
479        self.dict.pair(Name(b"ID"), id);
480        self
481    }
482
483    /// Write the `/Ref` attribute to specify to which structure element this
484    /// element refers. Used e.g. for footnotes. PDF 2.0+
485    ///
486    /// The parameter `refs` shall be indirect object references to other
487    /// structure elements.
488    pub fn refs(&mut self, refs: impl IntoIterator<Item = Ref>) -> &mut Self {
489        self.dict.insert(Name(b"Ref")).array().typed().items(refs);
490        self
491    }
492
493    /// Write the `/Pg` attribute to specify the page some or all of this
494    /// structure element is located on.
495    pub fn page(&mut self, page: Ref) -> &mut Self {
496        self.dict.pair(Name(b"Pg"), page);
497        self
498    }
499
500    /// Write the `/K` attribute to reference the immediate child of this
501    /// element.
502    pub fn child(&mut self, id: Ref) -> &mut Self {
503        self.dict.pair(Name(b"K"), id);
504        self
505    }
506
507    /// Start writing the `/K` attribute to reference the immediate marked
508    /// content child of this element.
509    pub fn marked_content_child(&mut self) -> MarkedRef<'_> {
510        self.dict.insert(Name(b"K")).start()
511    }
512
513    /// Start writing the `/K` attribute to reference the immediate object child
514    /// of this element.
515    pub fn object_child(&mut self) -> ObjectRef<'_> {
516        self.dict.insert(Name(b"K")).start()
517    }
518
519    /// Start writing the `/K` attribute to specify the children elements and
520    /// associated marked content sequences.
521    pub fn children(&mut self) -> StructChildren<'_> {
522        self.dict.insert(Name(b"K")).start()
523    }
524
525    /// Start writing the `/A` attribute to specify the attributes of this
526    /// structure element.
527    pub fn attributes(&mut self) -> TypedArray<'_, Attributes<'_>> {
528        self.dict.insert(Name(b"A")).array().typed()
529    }
530
531    /// Start writing the `/C` attribute to associate the structure element with
532    /// an attribute class.
533    pub fn attribute_class(&mut self) -> TypedArray<'_, Name<'_>> {
534        self.dict.insert(Name(b"C")).array().typed()
535    }
536
537    /// Write the `/R` attribute to specify the revision number, starting at 0.
538    pub fn revision(&mut self, revision: i32) -> &mut Self {
539        self.dict.pair(Name(b"R"), revision);
540        self
541    }
542
543    /// Write the `/T` attribute to set a title.
544    pub fn title(&mut self, title: impl TextStrLike) -> &mut Self {
545        self.dict.pair(Name(b"T"), title);
546        self
547    }
548
549    /// Write the `/Lang` attribute to set a language. PDF 1.4+
550    pub fn lang(&mut self, lang: TextStr) -> &mut Self {
551        self.dict.pair(Name(b"Lang"), lang);
552        self
553    }
554
555    /// Write the `/Alt` attribute to provide a description of the structure
556    /// element.
557    pub fn alt(&mut self, alt: impl TextStrLike) -> &mut Self {
558        self.dict.pair(Name(b"Alt"), alt);
559        self
560    }
561
562    /// Write the `/E` attribute to set the expanded form of the abbreviation
563    /// in this structure element. PDF 1.5+
564    pub fn expanded(&mut self, expanded: impl TextStrLike) -> &mut Self {
565        self.dict.pair(Name(b"E"), expanded);
566        self
567    }
568
569    /// Write the `/ActualText` attribute to set the exact text replacement. PDF
570    /// 1.4+
571    pub fn actual_text(&mut self, actual_text: impl TextStrLike) -> &mut Self {
572        self.dict.pair(Name(b"ActualText"), actual_text);
573        self
574    }
575
576    /// Start writing the `/AF` array to specify the associated files of the
577    /// element. PDF 2.0+ or PDF/A-3.
578    pub fn associated_files(&mut self) -> TypedArray<'_, FileSpec<'_>> {
579        self.insert(Name(b"AF")).array().typed()
580    }
581
582    /// Write the `/NS` attribute to indirectly reference a namespace dictionary
583    /// for this structure element type. PDF 2.0+
584    pub fn namespace(&mut self, ns: Ref) -> &mut Self {
585        self.dict.pair(Name(b"NS"), ns);
586        self
587    }
588
589    /// Write the `/PhoneticAlphabet` attribute to specify the phonetic alphabet
590    /// used in the [StructElement::phoneme] attribute. PDF 2.0+
591    pub fn phonetic_alphabet(&mut self, alphabet: PhoneticAlphabet) -> &mut Self {
592        self.dict.pair(Name(b"PhoneticAlphabet"), alphabet.to_name());
593        self
594    }
595
596    /// Write the `/Phoneme` attribute to specify the phonetic pronunciation of
597    /// the text in the structure element. PDF 2.0+
598    pub fn phoneme(&mut self, phoneme: TextStr) -> &mut Self {
599        self.dict.pair(Name(b"Phoneme"), phoneme);
600        self
601    }
602}
603
604deref!('a, StructElement<'a> => Dict<'a>, dict);
605
606/// Writer for a _structure element children array_. PDF 1.3+
607///
608/// This struct is created by [`StructElement::children`].
609pub struct StructChildren<'a> {
610    arr: Array<'a>,
611}
612
613writer!(StructChildren: |obj| Self { arr: obj.array() });
614
615impl StructChildren<'_> {
616    /// Write a structure element child as an indirect object.
617    pub fn struct_element(&mut self, id: Ref) -> &mut Self {
618        self.arr.item(id);
619        self
620    }
621
622    /// Write an integer marked content identifier.
623    pub fn marked_content_id(&mut self, id: i32) -> &mut Self {
624        self.arr.item(id);
625        self
626    }
627
628    /// Start writing a marked content reference dictionary.
629    pub fn marked_content_ref(&mut self) -> MarkedRef<'_> {
630        self.arr.push().start()
631    }
632
633    /// Start writing an object reference dictionary.
634    pub fn object_ref(&mut self) -> ObjectRef<'_> {
635        self.arr.push().start()
636    }
637}
638
639deref!('a, StructChildren<'a> => Array<'a>, arr);
640
641/// Writer for a _marked content reference dictionary_. PDF 1.3+
642///
643/// This struct is created by [`StructChildren::marked_content_ref`] and
644/// [`StructElement::marked_content_child`].
645pub struct MarkedRef<'a> {
646    dict: Dict<'a>,
647}
648
649writer!(MarkedRef: |obj| {
650    let mut dict = obj.dict();
651    dict.pair(Name(b"Type"), Name(b"MCR"));
652    Self { dict }
653});
654
655impl MarkedRef<'_> {
656    /// Write the `/Pg` attribute to specify the page the referenced marked
657    /// content sequence is located on.
658    pub fn page(&mut self, page: Ref) -> &mut Self {
659        self.dict.pair(Name(b"Pg"), page);
660        self
661    }
662
663    /// Write the `/Stm` attribute to specify the content stream containing this
664    /// marked content sequence if it was not on a page. If this content is
665    /// missing, writing the page attribute here or in the associated structure
666    /// element is required.
667    pub fn stream(&mut self, stream: Ref) -> &mut Self {
668        self.dict.pair(Name(b"Stm"), stream);
669        self
670    }
671
672    /// Write the `/StmOwn` attribute to specify which object owns the content
673    /// stream specified by the `/Stm` attribute.
674    pub fn stream_owner(&mut self, owner: Ref) -> &mut Self {
675        self.dict.pair(Name(b"StmOwn"), owner);
676        self
677    }
678
679    /// Write the `/MCID` attribute to specify the integer marked content
680    /// identifier. Required.
681    pub fn marked_content_id(&mut self, id: i32) -> &mut Self {
682        self.dict.pair(Name(b"MCID"), id);
683        self
684    }
685}
686
687deref!('a, MarkedRef<'a> => Dict<'a>, dict);
688
689/// Writer for an _object reference dictionary_. PDF 1.3+
690///
691/// This struct is created by [`StructChildren::object_ref`].
692pub struct ObjectRef<'a> {
693    dict: Dict<'a>,
694}
695
696writer!(ObjectRef: |obj| {
697    let mut dict = obj.dict();
698    dict.pair(Name(b"Type"), Name(b"OBJR"));
699    Self { dict }
700});
701
702impl ObjectRef<'_> {
703    /// Write the `/Pg` attribute to specify the page some or all of this
704    /// structure element is located on.
705    pub fn page(&mut self, page: Ref) -> &mut Self {
706        self.dict.pair(Name(b"Pg"), page);
707        self
708    }
709
710    /// Write the `/Obj` attribute to specify the object to be referenced. Required.
711    pub fn object(&mut self, obj: Ref) -> &mut Self {
712        self.dict.pair(Name(b"Obj"), obj);
713        self
714    }
715}
716
717deref!('a, ObjectRef<'a> => Dict<'a>, dict);
718
719/// Writer for a _role map dictionary_. PDF 1.4+
720///
721/// This struct is created by [`StructTreeRoot::role_map`].
722///
723/// For PDF 2.0 documents, note that this mapping maps to the PDF 1.7 roles. For
724/// a namespace-aware role-mapping mechanism, see [`Namespace::role_map_ns`].
725pub struct RoleMap<'a> {
726    dict: TypedDict<'a, Name<'a>>,
727}
728
729writer!(RoleMap: |obj| Self { dict: obj.dict().typed() });
730
731impl RoleMap<'_> {
732    /// Write an entry mapping a custom name to a pre-defined role.
733    pub fn insert(&mut self, name: Name, role: StructRole) -> &mut Self {
734        self.dict.pair(name, role.to_name());
735        self
736    }
737}
738
739deref!('a, RoleMap<'a> => TypedDict<'a, Name<'a>>, dict);
740
741/// Writer for a _class map dictionary_. PDF 1.4+
742///
743/// This struct is created by [`StructTreeRoot::class_map`].
744pub struct ClassMap<'a> {
745    dict: Dict<'a>,
746}
747
748writer!(ClassMap: |obj| Self { dict: obj.dict() });
749
750impl ClassMap<'_> {
751    /// Start writing an attributes dictionary for a class name.
752    pub fn single(&mut self, name: Name) -> Attributes<'_> {
753        self.dict.insert(name).start()
754    }
755
756    /// Start writing an array of attribute dictionaries for a class name.
757    pub fn multiple(&mut self, name: Name) -> TypedArray<'_, Attributes<'_>> {
758        self.dict.insert(name).array().typed()
759    }
760}
761
762deref!('a, ClassMap<'a> => Dict<'a>, dict);
763
764/// Role the structure element fulfills in the document for PDF 1.7 and below.
765///
766/// These are the predefined standard roles in PDF 1.7 and below, matching the
767/// `https://www.iso.org/pdf/ssn` namespace. The writer may write their own
768/// roles and then provide a mapping with [`StructTreeRoot::role_map`], or, if
769/// writing PDF 2.0, with [`Namespace::role_map_ns`]. PDF 1.4+.
770#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
771pub enum StructRole {
772    /// The whole document.
773    Document,
774    /// A part of a document that may contain multiple articles or sections.
775    Part,
776    /// An article with largely self-contained content.
777    Art,
778    /// Section of a larger document.
779    Sect,
780    /// Generic subdivision.
781    Div,
782    /// A paragraph-level quote.
783    BlockQuote,
784    /// An image or figure caption.
785    Caption,
786    /// Table of contents.
787    TOC,
788    /// Item in the table of contents.
789    TOCI,
790    /// Index of the key terms in the document.
791    Index,
792    /// Element only present for grouping purposes that shall not be exported.
793    NonStruct,
794    /// Element present only for use by the writer and associated products.
795    Private,
796    /// A paragraph
797    P,
798    /// A strongly structured heading.
799    StructuredHeading,
800    /// First-level heading.
801    H1,
802    /// Second-level heading.
803    H2,
804    /// Third-level heading.
805    H3,
806    /// Fourth-level heading.
807    H4,
808    /// Fifth-level heading.
809    H5,
810    /// Sixth-level heading.
811    H6,
812    /// A list.
813    L,
814    /// A list item.
815    LI,
816    /// Label for a list item.
817    Lbl,
818    /// Description of the list item.
819    LBody,
820    /// A table.
821    Table,
822    /// A table row.
823    TR,
824    /// A table header cell.
825    TH,
826    /// A table data cell.
827    TD,
828    /// A table header row group.
829    THead,
830    /// A table data row group.
831    TBody,
832    /// A table footer row group.
833    TFoot,
834    /// A generic inline element.
835    Span,
836    /// An inline quotation.
837    Quote,
838    /// A foot- or endnote.
839    Note,
840    /// A reference to elsewhere in the document.
841    Reference,
842    /// A reference to an external document.
843    BibEntry,
844    /// Computer code.
845    Code,
846    /// A link.
847    Link,
848    /// An association between an annotation and the content it belongs to. PDF
849    /// 1.5+
850    Annot,
851    /// Ruby annotation for CJK text. PDF 1.5+
852    Ruby,
853    /// Warichu annotation for CJK text. PDF 1.5+
854    Warichu,
855    /// Base text of a Ruby annotation. PDF 1.5+
856    RB,
857    /// Annotation text of a Ruby annotation. PDF 1.5+
858    RT,
859    /// Punctuation of a Ruby annotation. PDF 1.5+
860    RP,
861    /// Text of a Warichu annotation. PDF 1.5+
862    WT,
863    /// Punctuation of a Warichu annotation. PDF 1.5+
864    WP,
865    /// Item of graphical content.
866    Figure,
867    /// Mathematical formula.
868    Formula,
869    /// Form widget.
870    Form,
871}
872
873impl StructRole {
874    /// Convert the role into its [`Name`] serialization.
875    pub fn to_name(self) -> Name<'static> {
876        match self {
877            Self::Document => Name(b"Document"),
878            Self::Part => Name(b"Part"),
879            Self::Art => Name(b"Art"),
880            Self::Sect => Name(b"Sect"),
881            Self::Div => Name(b"Div"),
882            Self::BlockQuote => Name(b"BlockQuote"),
883            Self::Caption => Name(b"Caption"),
884            Self::TOC => Name(b"TOC"),
885            Self::TOCI => Name(b"TOCI"),
886            Self::Index => Name(b"Index"),
887            Self::NonStruct => Name(b"NonStruct"),
888            Self::Private => Name(b"Private"),
889            Self::P => Name(b"P"),
890            Self::StructuredHeading => Name(b"H"),
891            Self::H1 => Name(b"H1"),
892            Self::H2 => Name(b"H2"),
893            Self::H3 => Name(b"H3"),
894            Self::H4 => Name(b"H4"),
895            Self::H5 => Name(b"H5"),
896            Self::H6 => Name(b"H6"),
897            Self::L => Name(b"L"),
898            Self::LI => Name(b"LI"),
899            Self::Lbl => Name(b"Lbl"),
900            Self::LBody => Name(b"LBody"),
901            Self::Table => Name(b"Table"),
902            Self::TR => Name(b"TR"),
903            Self::TH => Name(b"TH"),
904            Self::TD => Name(b"TD"),
905            Self::THead => Name(b"THead"),
906            Self::TBody => Name(b"TBody"),
907            Self::TFoot => Name(b"TFoot"),
908            Self::Span => Name(b"Span"),
909            Self::Quote => Name(b"Quote"),
910            Self::Note => Name(b"Note"),
911            Self::Reference => Name(b"Reference"),
912            Self::BibEntry => Name(b"BibEntry"),
913            Self::Code => Name(b"Code"),
914            Self::Link => Name(b"Link"),
915            Self::Annot => Name(b"Annot"),
916            Self::Ruby => Name(b"Ruby"),
917            Self::Warichu => Name(b"Warichu"),
918            Self::RB => Name(b"RB"),
919            Self::RT => Name(b"RT"),
920            Self::RP => Name(b"RP"),
921            Self::WT => Name(b"WT"),
922            Self::WP => Name(b"WP"),
923            Self::Figure => Name(b"Figure"),
924            Self::Formula => Name(b"Formula"),
925            Self::Form => Name(b"Form"),
926        }
927    }
928
929    /// Return the corresponding PDF 2.0 [`StructRole2`] for this role or
930    /// `None`.
931    pub fn into_pdf_2_0(self) -> Option<StructRole2> {
932        match self {
933            Self::Document => Some(StructRole2::Document),
934            Self::Part => Some(StructRole2::Part),
935            Self::Art => None,
936            Self::Sect => Some(StructRole2::Sect),
937            Self::Div => Some(StructRole2::Div),
938            Self::BlockQuote => None,
939            Self::Caption => Some(StructRole2::Caption),
940            Self::TOC => None,
941            Self::TOCI => None,
942            Self::Index => None,
943            Self::NonStruct => Some(StructRole2::NonStruct),
944            Self::Private => None,
945            Self::P => Some(StructRole2::P),
946            Self::StructuredHeading => Some(StructRole2::StructuredHeading),
947            Self::H1 => Some(StructRole2::Heading(NonZeroU16::new(1).unwrap())),
948            Self::H2 => Some(StructRole2::Heading(NonZeroU16::new(2).unwrap())),
949            Self::H3 => Some(StructRole2::Heading(NonZeroU16::new(3).unwrap())),
950            Self::H4 => Some(StructRole2::Heading(NonZeroU16::new(4).unwrap())),
951            Self::H5 => Some(StructRole2::Heading(NonZeroU16::new(5).unwrap())),
952            Self::H6 => Some(StructRole2::Heading(NonZeroU16::new(6).unwrap())),
953            Self::L => Some(StructRole2::L),
954            Self::LI => Some(StructRole2::LI),
955            Self::Lbl => Some(StructRole2::Lbl),
956            Self::LBody => Some(StructRole2::LBody),
957            Self::Table => Some(StructRole2::Table),
958            Self::TR => Some(StructRole2::TR),
959            Self::TH => Some(StructRole2::TH),
960            Self::TD => Some(StructRole2::TD),
961            Self::THead => Some(StructRole2::THead),
962            Self::TBody => Some(StructRole2::TBody),
963            Self::TFoot => Some(StructRole2::TFoot),
964            Self::Span => Some(StructRole2::Span),
965            Self::Quote => Some(StructRole2::Em),
966            Self::Note => Some(StructRole2::FENote),
967            Self::Reference => Some(StructRole2::Link),
968            Self::BibEntry => None,
969            Self::Code => Some(StructRole2::Strong),
970            Self::Link => Some(StructRole2::Link),
971            Self::Annot => Some(StructRole2::Annot),
972            Self::Ruby => Some(StructRole2::Ruby),
973            Self::Warichu => Some(StructRole2::Warichu),
974            Self::RB => Some(StructRole2::RB),
975            Self::RT => Some(StructRole2::RT),
976            Self::RP => Some(StructRole2::WP),
977            Self::WT => Some(StructRole2::WT),
978            Self::WP => Some(StructRole2::WP),
979            Self::Figure => Some(StructRole2::Figure),
980            Self::Formula => Some(StructRole2::Formula),
981            Self::Form => Some(StructRole2::Form),
982        }
983    }
984
985    /// Return the type of the structure element.
986    pub fn role_type(self) -> StructRoleType {
987        match self {
988            Self::Document
989            | Self::Part
990            | Self::Art
991            | Self::Sect
992            | Self::Div
993            | Self::BlockQuote
994            | Self::Caption
995            | Self::TOC
996            | Self::TOCI
997            | Self::Index
998            | Self::NonStruct
999            | Self::Private => StructRoleType::Grouping,
1000            Self::P
1001            | Self::StructuredHeading
1002            | Self::H1
1003            | Self::H2
1004            | Self::H3
1005            | Self::H4
1006            | Self::H5
1007            | Self::H6 => {
1008                StructRoleType::BlockLevel(BlockLevelRoleSubtype::ParagraphLike)
1009            }
1010            Self::L | Self::LI | Self::Lbl | Self::LBody => {
1011                StructRoleType::BlockLevel(BlockLevelRoleSubtype::List)
1012            }
1013            Self::Table => StructRoleType::BlockLevel(BlockLevelRoleSubtype::Table),
1014            Self::TR | Self::TH | Self::TD | Self::THead | Self::TBody | Self::TFoot => {
1015                StructRoleType::Table
1016            }
1017            Self::Span
1018            | Self::Quote
1019            | Self::Note
1020            | Self::Reference
1021            | Self::BibEntry
1022            | Self::Code
1023            | Self::Ruby
1024            | Self::Warichu => {
1025                StructRoleType::InlineLevel(InlineLevelRoleSubtype::Generic)
1026            }
1027            Self::Link => StructRoleType::InlineLevel(InlineLevelRoleSubtype::Link),
1028            Self::Annot => {
1029                StructRoleType::InlineLevel(InlineLevelRoleSubtype::Annotation)
1030            }
1031            Self::RB | Self::RT | Self::RP | Self::WT | Self::WP => {
1032                StructRoleType::InlineLevel(InlineLevelRoleSubtype::RubyWarichu)
1033            }
1034            Self::Figure | Self::Formula | Self::Form => StructRoleType::Illustration,
1035        }
1036    }
1037}
1038
1039/// Type of the PDF 1.7 [structure element](StructRole) in the document,
1040/// determining layout, permitted attributes, and nesting.
1041#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
1042pub enum StructRoleType {
1043    /// Elements used solely to group other elements together.
1044    Grouping,
1045    /// Elements laid out across the block axis, also known as BLSE.
1046    BlockLevel(BlockLevelRoleSubtype),
1047    /// Elements laid out across the inline axis, also known as ILSE.
1048    InlineLevel(InlineLevelRoleSubtype),
1049    /// Elements whose contents consist of one or more graphics objects.
1050    Illustration,
1051    /// Elements that occur in a table, such as rows and cells.
1052    Table,
1053}
1054
1055/// Subtypes of block-level structure roles, determining the layout and
1056/// permitted attributes of the element.
1057#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
1058pub enum BlockLevelRoleSubtype {
1059    /// Block-level elements containing predominantly text content.
1060    ParagraphLike,
1061    /// List-related elements, such as lists and list items.
1062    List,
1063    /// Table-related elements, such as tables and table rows.
1064    Table,
1065}
1066
1067/// Subtypes of inline-level PDF 1.7 structure roles, determining the layout and
1068/// permitted attributes of the element.
1069#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
1070pub enum InlineLevelRoleSubtype {
1071    /// Generic inline elements, such as spans, quotes, and code.
1072    Generic,
1073    /// Links.
1074    Link,
1075    /// Superimposed annotations.
1076    Annotation,
1077    /// Ruby and Warichu annotations, which are used for CJK text.
1078    RubyWarichu,
1079}
1080
1081impl TryFrom<StructRole> for StructRole2 {
1082    type Error = ();
1083
1084    fn try_from(value: StructRole) -> Result<Self, Self::Error> {
1085        value.into_pdf_2_0().ok_or(())
1086    }
1087}
1088
1089/// PDF 2.0 roles the structure element fulfills in the document.
1090///
1091/// These are the predefined standard roles in PDF 2.0, matching the
1092/// `https://www.iso.org/pdf2/ssn` namespace. The writer may write their own
1093/// types and then provide a mapping using [`Namespace::role_map_ns`]. PDF 2.0+.
1094#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
1095pub enum StructRole2 {
1096    /// The whole document.
1097    Document,
1098    /// An incomplete fragment of another document.
1099    DocumentFragment,
1100    /// A part of a document that may contain multiple articles or sections.
1101    Part,
1102    /// Section of a larger document.
1103    Sect,
1104    /// Generic subdivision.
1105    Div,
1106    /// Content distinct from other content within the parent, such as callouts,
1107    /// sidebars, commentary, or background information.
1108    Aside,
1109    /// Element only present for grouping purposes that shall not be exported.
1110    NonStruct,
1111    /// A paragraph
1112    P,
1113    /// Heading with a specific level.
1114    Heading(NonZeroU16),
1115    /// Strongly structured heading.
1116    StructuredHeading,
1117    /// A title of a document.
1118    Title,
1119    /// A foot- or endnote.
1120    FENote,
1121    /// A subdivision within a block level element.
1122    Sub,
1123    /// Label for a list item.
1124    Lbl,
1125    /// A generic inline element.
1126    Span,
1127    /// An emphasized inline element.
1128    Em,
1129    /// An inline element with heightened (strong) importance.
1130    Strong,
1131    /// A link.
1132    Link,
1133    /// An association between an annotation and the content it belongs to.
1134    Annot,
1135    /// Form widget.
1136    Form,
1137    /// Ruby annotation for CJK text.
1138    Ruby,
1139    /// Base text of a Ruby annotation.
1140    RB,
1141    /// Annotation text of a Ruby annotation.
1142    RT,
1143    /// Punctuation in a Ruby annotation.
1144    RP,
1145    /// Warichu annotation for CJK text.
1146    Warichu,
1147    /// Text of a Warichu annotation.
1148    WT,
1149    /// Punctuation of a Warichu annotation.
1150    WP,
1151    /// A list.
1152    L,
1153    /// A list item.
1154    LI,
1155    /// Description of the list item.
1156    LBody,
1157    /// A table.
1158    Table,
1159    /// A table row.
1160    TR,
1161    /// A table header cell.
1162    TH,
1163    /// A table data cell.
1164    TD,
1165    /// A table header row group.
1166    THead,
1167    /// A table data row group.
1168    TBody,
1169    /// A table footer row group.
1170    TFoot,
1171    /// An image or figure caption.
1172    Caption,
1173    /// Item of graphical content.
1174    Figure,
1175    /// Mathematical formula.
1176    Formula,
1177    /// An artifact not part of the logical content of the document.
1178    Artifact,
1179}
1180
1181impl StructRole2 {
1182    /// Convert the role into its [`Name`] serialization.
1183    ///
1184    /// The `buf` parameter is a mutable buffer of 6 bytes that will be used to
1185    /// store the name in the event that the role is a heading with a level.
1186    pub fn to_name(self, buf: &mut [u8; 6]) -> Name<'_> {
1187        Name(match self {
1188            Self::Document => b"Document",
1189            Self::DocumentFragment => b"DocumentFragment",
1190            Self::Part => b"Part",
1191            Self::Sect => b"Sect",
1192            Self::Div => b"Div",
1193            Self::Aside => b"Aside",
1194            Self::NonStruct => b"NonStruct",
1195            Self::P => b"P",
1196            Self::Heading(level) => {
1197                let mut cursor = Cursor::new(buf.as_mut_slice());
1198                write!(&mut cursor, "H{}", level.get()).unwrap();
1199                let pos = cursor.position() as usize;
1200                &buf[..pos]
1201            }
1202            Self::StructuredHeading => b"H",
1203            Self::Title => b"Title",
1204            Self::FENote => b"FENote",
1205            Self::Sub => b"Sub",
1206            Self::Lbl => b"Lbl",
1207            Self::Span => b"Span",
1208            Self::Em => b"Em",
1209            Self::Strong => b"Strong",
1210            Self::Link => b"Link",
1211            Self::Annot => b"Annot",
1212            Self::Form => b"Form",
1213            Self::Ruby => b"Ruby",
1214            Self::RB => b"RB",
1215            Self::RT => b"RT",
1216            Self::RP => b"RP",
1217            Self::Warichu => b"Warichu",
1218            Self::WT => b"WT",
1219            Self::WP => b"WP",
1220            Self::L => b"L",
1221            Self::LI => b"LI",
1222            Self::LBody => b"LBody",
1223            Self::Table => b"Table",
1224            Self::TR => b"TR",
1225            Self::TH => b"TH",
1226            Self::TD => b"TD",
1227            Self::THead => b"THead",
1228            Self::TBody => b"TBody",
1229            Self::TFoot => b"TFoot",
1230            Self::Caption => b"Caption",
1231            Self::Figure => b"Figure",
1232            Self::Formula => b"Formula",
1233            Self::Artifact => b"Artifact",
1234        })
1235    }
1236
1237    /// How the type should be represented in PDF 1.7.
1238    ///
1239    /// The `opts` parameter allows to control how certain roles are represented
1240    /// in PDF 1.7. You can also use its default constructor.
1241    pub fn compatibility_1_7(self, opts: RoleMapOpts) -> StructRole2Compat {
1242        match self {
1243            Self::Document => StructRole2Compat::Compatible(StructRole::Document),
1244            Self::DocumentFragment => StructRole2Compat::RoleMapping(StructRole::Div),
1245            Self::Part => StructRole2Compat::Compatible(StructRole::Part),
1246            Self::Sect => StructRole2Compat::Compatible(StructRole::Sect),
1247            Self::Div => StructRole2Compat::Compatible(StructRole::Div),
1248            Self::Aside => StructRole2Compat::RoleMapping(StructRole::Div),
1249            Self::NonStruct => StructRole2Compat::Compatible(StructRole::NonStruct),
1250            Self::P => StructRole2Compat::Compatible(StructRole::P),
1251            Self::Heading(n) if n.get() == 1 => {
1252                StructRole2Compat::Compatible(StructRole::H1)
1253            }
1254            Self::Heading(n) if n.get() == 2 => {
1255                StructRole2Compat::Compatible(StructRole::H2)
1256            }
1257            Self::Heading(n) if n.get() == 3 => {
1258                StructRole2Compat::Compatible(StructRole::H3)
1259            }
1260            Self::Heading(n) if n.get() == 4 => {
1261                StructRole2Compat::Compatible(StructRole::H4)
1262            }
1263            Self::Heading(n) if n.get() == 5 => {
1264                StructRole2Compat::Compatible(StructRole::H5)
1265            }
1266            Self::Heading(n) if n.get() == 6 => {
1267                StructRole2Compat::Compatible(StructRole::H6)
1268            }
1269            Self::Heading(_) => StructRole2Compat::RoleMapping(
1270                if opts.contains(RoleMapOpts::MAP_HN_TO_H6) {
1271                    StructRole::H6
1272                } else {
1273                    StructRole::P
1274                },
1275            ),
1276            Self::StructuredHeading => {
1277                StructRole2Compat::Compatible(StructRole::StructuredHeading)
1278            }
1279            Self::Title => StructRole2Compat::RoleMapping(
1280                if opts.contains(RoleMapOpts::MAP_TITLE_TO_H1) {
1281                    StructRole::H1
1282                } else {
1283                    StructRole::P
1284                },
1285            ),
1286            Self::FENote => StructRole2Compat::RoleMapping(StructRole::Note),
1287            Self::Sub => StructRole2Compat::RoleMapping(
1288                if opts.contains(RoleMapOpts::MAP_SUB_TO_SPAN) {
1289                    StructRole::Span
1290                } else {
1291                    StructRole::Div
1292                },
1293            ),
1294            Self::Lbl => StructRole2Compat::Compatible(StructRole::Lbl),
1295            Self::Span => StructRole2Compat::Compatible(StructRole::Span),
1296            Self::Em => StructRole2Compat::RoleMapping(StructRole::Span),
1297            Self::Strong => StructRole2Compat::RoleMapping(StructRole::Span),
1298            Self::Link => StructRole2Compat::Compatible(StructRole::Link),
1299            Self::Annot => StructRole2Compat::Compatible(StructRole::Annot),
1300            Self::Form => StructRole2Compat::Compatible(StructRole::Form),
1301            Self::Ruby => StructRole2Compat::Compatible(StructRole::Ruby),
1302            Self::RB => StructRole2Compat::Compatible(StructRole::RB),
1303            Self::RT => StructRole2Compat::Compatible(StructRole::RT),
1304            Self::RP => StructRole2Compat::Compatible(StructRole::RP),
1305            Self::Warichu => StructRole2Compat::Compatible(StructRole::Warichu),
1306            Self::WT => StructRole2Compat::Compatible(StructRole::WT),
1307            Self::WP => StructRole2Compat::Compatible(StructRole::WP),
1308            Self::L => StructRole2Compat::Compatible(StructRole::L),
1309            Self::LI => StructRole2Compat::Compatible(StructRole::LI),
1310            Self::LBody => StructRole2Compat::Compatible(StructRole::LBody),
1311            Self::Table => StructRole2Compat::Compatible(StructRole::Table),
1312            Self::TR => StructRole2Compat::Compatible(StructRole::TR),
1313            Self::TH => StructRole2Compat::Compatible(StructRole::TH),
1314            Self::TD => StructRole2Compat::Compatible(StructRole::TD),
1315            Self::THead => StructRole2Compat::Compatible(StructRole::THead),
1316            Self::TBody => StructRole2Compat::Compatible(StructRole::TBody),
1317            Self::TFoot => StructRole2Compat::Compatible(StructRole::TFoot),
1318            Self::Caption => StructRole2Compat::Compatible(StructRole::Caption),
1319            Self::Figure => StructRole2Compat::Compatible(StructRole::Figure),
1320            Self::Formula => StructRole2Compat::Compatible(StructRole::Formula),
1321            Self::Artifact => StructRole2Compat::RoleMapping(StructRole::Private),
1322        }
1323    }
1324
1325    /// Return the type of the structure element.
1326    pub fn role_type(self) -> StructRoleType2 {
1327        match self {
1328            Self::Document | Self::DocumentFragment => StructRoleType2::Document,
1329            Self::Part | Self::Sect | Self::Div | Self::Aside | Self::NonStruct => {
1330                StructRoleType2::Grouping
1331            }
1332            Self::P
1333            | Self::Heading(_)
1334            | Self::StructuredHeading
1335            | Self::Title
1336            | Self::FENote => StructRoleType2::BlockLevel,
1337            Self::Sub => StructRoleType2::SubBlockLevel,
1338            Self::Lbl
1339            | Self::Span
1340            | Self::Em
1341            | Self::Strong
1342            | Self::Link
1343            | Self::Annot
1344            | Self::Form => {
1345                StructRoleType2::InlineLevel(InlineLevelRoleSubtype2::Generic)
1346            }
1347            Self::Ruby
1348            | Self::RB
1349            | Self::RP
1350            | Self::RT
1351            | Self::Warichu
1352            | Self::WT
1353            | Self::WP => {
1354                StructRoleType2::InlineLevel(InlineLevelRoleSubtype2::RubyWarichu)
1355            }
1356            Self::L | Self::LI | Self::LBody => StructRoleType2::List,
1357            Self::Table
1358            | Self::TR
1359            | Self::TH
1360            | Self::TD
1361            | Self::THead
1362            | Self::TBody
1363            | Self::TFoot => StructRoleType2::Table,
1364            Self::Caption => StructRoleType2::Caption,
1365            Self::Figure => StructRoleType2::Figure,
1366            Self::Formula => StructRoleType2::Formula,
1367            Self::Artifact => StructRoleType2::Artifact,
1368        }
1369    }
1370}
1371
1372bitflags::bitflags! {
1373    /// Options for mapping PDF 2.0 [`StructRole2`] to PDF 1.7 [`StructRole`].
1374    pub struct RoleMapOpts: u8 {
1375        /// Whether to map headings with levels higher than 6 to [`StructRole::H6`]
1376        /// (`true`) or [`StructRole::P`] (`false`).
1377        const MAP_HN_TO_H6 = 1 << 0;
1378        /// Whether to map the `Title` role to [`StructRole::H1`] (`true`) or
1379        /// [`StructRole::P`] (`false`).
1380        const MAP_TITLE_TO_H1 = 1 << 1;
1381        /// Whether to map the `Sub` role to [`StructRole::Span`] (`true`) or
1382        /// [`StructRole::Div`] (`false`).
1383        const MAP_SUB_TO_SPAN = 1 << 2;
1384    }
1385}
1386
1387impl Default for RoleMapOpts {
1388    fn default() -> Self {
1389        Self::empty()
1390    }
1391}
1392
1393/// How a particular PDF 2.0 [`StructRole2`] can be represented in PDF 1.7.
1394#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
1395pub enum StructRole2Compat {
1396    /// The role is a direct match with this PDF 1.7 role.
1397    Compatible(StructRole),
1398    /// The has no direct match with a PDF 1.7 role, but can be mapped to
1399    /// a PDF 1.7 role with a similar meaning.
1400    RoleMapping(StructRole),
1401}
1402
1403impl StructRole2Compat {
1404    /// Return the corresponding PDF 1.7 [`StructRole`] for this role or `None`
1405    /// if there is no exact match.
1406    pub fn into_pdf_1_7(self) -> Option<StructRole> {
1407        match self {
1408            Self::Compatible(role) => Some(role),
1409            Self::RoleMapping(_) => None,
1410        }
1411    }
1412
1413    /// Return the closest equivalent role in the PDF 1.7 namespace.
1414    ///
1415    /// Returns `None` if the role exactly matches a PDF 1.7 role (see
1416    /// [`Self::into_pdf_1_7`]).
1417    pub fn role_mapped_1_7(self) -> Option<StructRole> {
1418        match self {
1419            Self::Compatible(_) => None,
1420            Self::RoleMapping(role) => Some(role),
1421        }
1422    }
1423
1424    /// Return the inner role.
1425    pub fn role(self) -> StructRole {
1426        match self {
1427            Self::Compatible(role) => role,
1428            Self::RoleMapping(role) => role,
1429        }
1430    }
1431}
1432
1433impl TryFrom<StructRole2> for StructRole {
1434    type Error = ();
1435
1436    fn try_from(value: StructRole2) -> Result<Self, Self::Error> {
1437        value
1438            .compatibility_1_7(RoleMapOpts::default())
1439            .into_pdf_1_7()
1440            .ok_or(())
1441    }
1442}
1443
1444/// Type of the PDF 2.0 [structure element](StructRole2) in the document,
1445/// determining layout, permitted attributes, and nesting.
1446#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
1447pub enum StructRoleType2 {
1448    /// Elements representing the whole document or a fragment of it.
1449    Document,
1450    /// Elements used solely to group other elements together.
1451    Grouping,
1452    /// Elements laid out across the block axis, also known as BLSE.
1453    BlockLevel,
1454    /// Elements that appear as sub-divisions of a block-level element.
1455    SubBlockLevel,
1456    /// Elements laid out across the inline axis, also known as ILSE.
1457    InlineLevel(InlineLevelRoleSubtype2),
1458    /// Elements related to lists.
1459    List,
1460    /// Elements related to tables.
1461    Table,
1462    /// Figure captions.
1463    Caption,
1464    /// Figures, such as images and illustrations.
1465    Figure,
1466    /// Mathematical formulas.
1467    Formula,
1468    /// Artifacts not part of the logical content of the document.
1469    Artifact,
1470}
1471
1472/// Subtypes of inline-level structure roles for PDF 2.0, determining the layout and
1473/// permitted attributes of the element.
1474#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
1475pub enum InlineLevelRoleSubtype2 {
1476    /// Generic inline element.
1477    Generic,
1478    /// Ruby or Warichu annotation.
1479    RubyWarichu,
1480}
1481
1482/// Which phonetic alphabet to use for the `/Phonetic` key in the
1483/// [`StructElement`] dictionary.
1484#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
1485pub enum PhoneticAlphabet<'a> {
1486    /// The International Phonetic Alphabet.
1487    #[default]
1488    Ipa,
1489    /// The Extended Speech Assessment Methods Phonetic Alphabet (X-SAMPA).
1490    XSampa,
1491    /// The Pinyin romanization system for Chinese.
1492    Pinyin,
1493    /// The Wade-Giles romanization system for Chinese.
1494    WadeGiles,
1495    /// A custom phonetic alphabet.
1496    Custom(Name<'a>),
1497}
1498
1499impl<'a> PhoneticAlphabet<'a> {
1500    pub(crate) fn to_name(self) -> Name<'a> {
1501        match self {
1502            Self::Ipa => Name(b"ipa"),
1503            Self::XSampa => Name(b"x-sampa"),
1504            Self::Pinyin => Name(b"zh-Latn-pinyin"),
1505            Self::WadeGiles => Name(b"zh-Latn-wadegile"),
1506            Self::Custom(name) => name,
1507        }
1508    }
1509}
1510
1511/// Writer for a _namespace dictionary_. PDF 2.0+
1512///
1513/// This struct is created by [`Chunk::namespace`].
1514pub struct Namespace<'a> {
1515    dict: Dict<'a>,
1516}
1517
1518writer!(Namespace: |obj| {
1519    let mut dict = obj.dict();
1520    dict.pair(Name(b"Type"), Name(b"Namespace"));
1521    Self { dict }
1522});
1523
1524impl Namespace<'_> {
1525    /// Write the `/NS` attribute to specify the identifier (URI) of the namespace.
1526    pub fn ns(&mut self, identifier: TextStr) -> &mut Self {
1527        self.dict.pair(Name(b"NS"), identifier);
1528        self
1529    }
1530
1531    /// Start writing the `/Schema` attribute to point to a schema definition
1532    /// for the namespace. Optional.
1533    pub fn schema(&mut self) -> FileSpec<'_> {
1534        self.dict.insert(Name(b"Schema")).start()
1535    }
1536
1537    /// Start writing the `/RoleMapNS` attribute to map structure elements to
1538    /// elements in another namespace. Optional.
1539    ///
1540    /// For a mechanism to define role mappings compatible with PDF 1.3 and
1541    /// above, see [`StructTreeRoot::role_map`].
1542    pub fn role_map_ns(&mut self) -> NamespaceRoleMap<'_> {
1543        self.dict.insert(Name(b"RoleMapNS")).start()
1544    }
1545
1546    /// Write the namespace dictionary for the _standard structure namespace for
1547    /// PDF 2.0_.
1548    pub fn pdf_2_ns(mut self) {
1549        self.ns(TextStr("https://www.iso.org/pdf2/ssn"));
1550    }
1551
1552    /// Write the namespace dictionary for the _standard structure namespace for
1553    /// PDF 1.7_.
1554    pub fn pdf_1_7_ns(mut self) {
1555        self.ns(TextStr("https://www.iso.org/pdf/ssn"));
1556    }
1557
1558    /// Write the namespace dictionary for MathML 3.0.
1559    pub fn mathml_3_0_ns(mut self) {
1560        self.ns(TextStr("https://www.w3.org/1998/Math/MathML"));
1561    }
1562}
1563
1564deref!('a, Namespace<'a> => Dict<'a>, dict);
1565
1566/// Writer for a _namespace role map dictionary_. PDF 2.0+
1567///
1568/// This struct is created by [`Namespace::role_map_ns`].
1569pub struct NamespaceRoleMap<'a> {
1570    dict: Dict<'a>,
1571}
1572
1573writer!(NamespaceRoleMap: |obj| {
1574    Self { dict: obj.dict() }
1575});
1576
1577impl NamespaceRoleMap<'_> {
1578    /// Write an entry mapping a custom structure type to an element in the
1579    /// standard (PDF 1.7) namespace.
1580    pub fn to_pdf_1_7(&mut self, name: Name, role: StructRole) -> &mut Self {
1581        self.dict.pair(name, role.to_name());
1582        self
1583    }
1584
1585    /// Write an entry mapping a custom structure type to an element in the
1586    /// namespace referenced by the namespace dictionary the value of the
1587    /// `ns_ref` parameter points to.
1588    pub fn to_namespace(&mut self, name: Name, role: Name, ns_ref: Ref) -> &mut Self {
1589        let mut array = self.dict.insert(name).array();
1590        array.item(role);
1591        array.item(ns_ref);
1592        array.finish();
1593        self
1594    }
1595
1596    /// Write an entry mapping a custom structure type to an element in the
1597    /// PDF 2.0 namespace.
1598    ///
1599    /// The `pdf_2_ns` parameter is an indirect reference to a PDF 2.0 namespace
1600    /// dictionary. You can create this dictionary by using [`Chunk::namespace`]
1601    /// and then calling [`Namespace::pdf_2_ns`] on the returned writer.
1602    pub fn to_pdf_2_0(
1603        &mut self,
1604        name: Name,
1605        role: StructRole2,
1606        pdf_2_ns: Ref,
1607    ) -> &mut Self {
1608        self.to_namespace(name, role.to_name(&mut [0; 6]), pdf_2_ns)
1609    }
1610}
1611
1612deref!('a, NamespaceRoleMap<'a> => Dict<'a>, dict);
1613
1614/// Writer for a _mark information dictionary_. PDF 1.4+
1615///
1616/// This struct is created by [`Catalog::mark_info`].
1617pub struct MarkInfo<'a> {
1618    dict: Dict<'a>,
1619}
1620
1621writer!(MarkInfo: |obj| Self { dict: obj.dict() });
1622
1623impl MarkInfo<'_> {
1624    /// Write the `/Marked` attribute to indicate whether the document conforms
1625    /// to the Tagged PDF specification.
1626    ///
1627    /// Must be `true` in some PDF/A profiles like PDF/A-2a.
1628    pub fn marked(&mut self, conformant: bool) -> &mut Self {
1629        self.pair(Name(b"Marked"), conformant);
1630        self
1631    }
1632
1633    /// Write the `/UserProperties` attribute to indicate whether the document
1634    /// contains structure elements with user properties. PDF 1.6+.
1635    pub fn user_properties(&mut self, present: bool) -> &mut Self {
1636        self.pair(Name(b"UserProperties"), present);
1637        self
1638    }
1639
1640    /// Write the `/Suspects` attribute to indicate whether the document
1641    /// contains tag suspects. PDF 1.6+.
1642    pub fn suspects(&mut self, present: bool) -> &mut Self {
1643        self.pair(Name(b"Suspects"), present);
1644        self
1645    }
1646}
1647
1648deref!('a, MarkInfo<'a> => Dict<'a>, dict);
1649
1650/// Predominant reading order of text.
1651///
1652/// Used to aid the viewer with the special ordering in which to display pages.
1653#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
1654pub enum Direction {
1655    /// Left-to-right.
1656    #[default]
1657    L2R,
1658    /// Right-to-left as well as vertical writing systems.
1659    R2L,
1660}
1661
1662impl Direction {
1663    pub(crate) fn to_name(self) -> Name<'static> {
1664        match self {
1665            Self::L2R => Name(b"L2R"),
1666            Self::R2L => Name(b"R2L"),
1667        }
1668    }
1669}
1670
1671/// Writer for a _page label dictionary_.
1672pub struct PageLabel<'a> {
1673    dict: Dict<'a>,
1674}
1675
1676writer!(PageLabel: |obj| {
1677    let mut dict = obj.dict();
1678    dict.pair(Name(b"Type"), Name(b"PageLabel"));
1679    Self { dict }
1680});
1681
1682impl PageLabel<'_> {
1683    /// Write the `/S` attribute to set the page label's numbering style.
1684    ///
1685    /// If this attribute is omitted, only the prefix will be used, there will
1686    /// be no page number.
1687    pub fn style(&mut self, style: NumberingStyle) -> &mut Self {
1688        self.pair(Name(b"S"), style.to_name());
1689        self
1690    }
1691
1692    /// Write the `/P` attribute to set the page label's prefix.
1693    pub fn prefix(&mut self, prefix: impl TextStrLike) -> &mut Self {
1694        self.pair(Name(b"P"), prefix);
1695        self
1696    }
1697
1698    /// Write the `/St` attribute to set the page label's offset.
1699    ///
1700    /// This must be greater or equal to `1` if set.
1701    pub fn offset(&mut self, offset: i32) -> &mut Self {
1702        self.pair(Name(b"St"), offset);
1703        self
1704    }
1705}
1706
1707deref!('a, PageLabel<'a> => Dict<'a>, dict);
1708
1709/// The numbering style of a page label.
1710#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
1711pub enum NumberingStyle {
1712    /// Arabic numerals.
1713    Arabic,
1714    /// Lowercase Roman numerals.
1715    LowerRoman,
1716    /// Uppercase Roman numerals.
1717    UpperRoman,
1718    /// Lowercase letters (a-z, then aa-zz, ...).
1719    LowerAlpha,
1720    /// Uppercase letters (A-Z, then AA-ZZ, ...).
1721    UpperAlpha,
1722}
1723
1724impl NumberingStyle {
1725    pub(crate) fn to_name(self) -> Name<'static> {
1726        match self {
1727            NumberingStyle::Arabic => Name(b"D"),
1728            NumberingStyle::LowerRoman => Name(b"r"),
1729            NumberingStyle::UpperRoman => Name(b"R"),
1730            NumberingStyle::LowerAlpha => Name(b"a"),
1731            NumberingStyle::UpperAlpha => Name(b"A"),
1732        }
1733    }
1734}
1735
1736/// Writer for a _document information dictionary_.
1737///
1738/// This struct is created by [`Pdf::document_info`].
1739pub struct DocumentInfo<'a> {
1740    dict: Dict<'a>,
1741}
1742
1743writer!(DocumentInfo: |obj| Self { dict: obj.dict() });
1744
1745impl DocumentInfo<'_> {
1746    /// Write the `/Title` attribute to set the document's title. PDF 1.1+.
1747    pub fn title(&mut self, title: impl TextStrLike) -> &mut Self {
1748        self.pair(Name(b"Title"), title);
1749        self
1750    }
1751
1752    /// Write the `/Author` attribute to set the document's author.
1753    pub fn author(&mut self, author: impl TextStrLike) -> &mut Self {
1754        self.pair(Name(b"Author"), author);
1755        self
1756    }
1757
1758    /// Write the `/Subject` attribute to set the document's subject. PDF 1.1+.
1759    pub fn subject(&mut self, subject: impl TextStrLike) -> &mut Self {
1760        self.pair(Name(b"Subject"), subject);
1761        self
1762    }
1763
1764    /// Write the `/Keywords` attribute to set terms associated to the document.
1765    /// PDF 1.1+.
1766    pub fn keywords(&mut self, keywords: impl TextStrLike) -> &mut Self {
1767        self.pair(Name(b"Keywords"), keywords);
1768        self
1769    }
1770
1771    /// Write the `/Creator` attribute to set the name of the product that
1772    /// converted or wrote the file that this PDF has been converted from.
1773    pub fn creator(&mut self, creator: impl TextStrLike) -> &mut Self {
1774        self.pair(Name(b"Creator"), creator);
1775        self
1776    }
1777
1778    /// Write the `/Producer` attribute to set the name of the product that
1779    /// converted or wrote this PDF.
1780    pub fn producer(&mut self, producer: impl TextStrLike) -> &mut Self {
1781        self.pair(Name(b"Producer"), producer);
1782        self
1783    }
1784
1785    /// Write the `/CreationDate` attribute to set the date the document was
1786    /// created.
1787    pub fn creation_date(&mut self, date: Date) -> &mut Self {
1788        self.pair(Name(b"CreationDate"), date);
1789        self
1790    }
1791
1792    /// Write the `/ModDate` attribute to set the date the document was last
1793    /// modified.
1794    ///
1795    /// Required if `/PieceInfo` is set in the document catalog.
1796    pub fn modified_date(&mut self, date: Date) -> &mut Self {
1797        self.pair(Name(b"ModDate"), date);
1798        self
1799    }
1800
1801    /// Write the `/Trapped` attribute to set whether the document is fully or
1802    /// partially trapped. PDF 1.3+.
1803    pub fn trapped(&mut self, trapped: TrappingStatus) -> &mut Self {
1804        self.pair(Name(b"Trapped"), trapped.to_name());
1805        self
1806    }
1807}
1808
1809deref!('a, DocumentInfo<'a> => Dict<'a>, dict);
1810
1811/// Whether a document has been adjusted with traps.
1812///
1813/// Those account for colorant misregistration during the printing process.
1814#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
1815pub enum TrappingStatus {
1816    /// The document is fully trapped.
1817    Trapped,
1818    /// The document has not been trapped.
1819    NotTrapped,
1820    /// The document is partially trapped or the trapping status is unknown.
1821    #[default]
1822    Unknown,
1823}
1824
1825impl TrappingStatus {
1826    pub(crate) fn to_name(self) -> Name<'static> {
1827        match self {
1828            TrappingStatus::Trapped => Name(b"True"),
1829            TrappingStatus::NotTrapped => Name(b"False"),
1830            TrappingStatus::Unknown => Name(b"Unknown"),
1831        }
1832    }
1833}
1834
1835/// Writer for a _page tree dictionary_.
1836///
1837/// This struct is created by [`Chunk::pages`].
1838pub struct Pages<'a> {
1839    dict: Dict<'a>,
1840}
1841
1842writer!(Pages: |obj| {
1843    let mut dict = obj.dict();
1844    dict.pair(Name(b"Type"), Name(b"Pages"));
1845    Self { dict }
1846});
1847
1848impl Pages<'_> {
1849    /// Write the `/Parent` attribute. Required except in root node.
1850    pub fn parent(&mut self, parent: Ref) -> &mut Self {
1851        self.pair(Name(b"Parent"), parent);
1852        self
1853    }
1854
1855    /// Write the `/Kids` attributes, listing the immediate children of this
1856    /// node in the page tree. Required.
1857    pub fn kids(&mut self, kids: impl IntoIterator<Item = Ref>) -> &mut Self {
1858        self.insert(Name(b"Kids")).array().items(kids);
1859        self
1860    }
1861
1862    /// Write the `/Count` attribute, specifying how many descendants this node
1863    /// in the page tree has. This may be different to the length of `/Kids`
1864    /// when the tree has multiple layers. Required.
1865    pub fn count(&mut self, count: i32) -> &mut Self {
1866        self.pair(Name(b"Count"), count);
1867        self
1868    }
1869
1870    /// Write the `/MediaBox` attribute.
1871    pub fn media_box(&mut self, rect: Rect) -> &mut Self {
1872        self.pair(Name(b"MediaBox"), rect);
1873        self
1874    }
1875
1876    /// Start writing the `/Resources` dictionary.
1877    pub fn resources(&mut self) -> Resources<'_> {
1878        self.insert(Name(b"Resources")).start()
1879    }
1880}
1881
1882deref!('a, Pages<'a> => Dict<'a>, dict);
1883
1884/// Writer for a _page dictionary_.
1885///
1886/// This struct is created by [`Chunk::page`].
1887pub struct Page<'a> {
1888    dict: Dict<'a>,
1889}
1890
1891writer!(Page: |obj| {
1892    let mut dict = obj.dict();
1893    dict.pair(Name(b"Type"), Name(b"Page"));
1894    Self { dict }
1895});
1896
1897impl Page<'_> {
1898    /// Write the `/Parent` attribute. Required.
1899    pub fn parent(&mut self, parent: Ref) -> &mut Self {
1900        self.pair(Name(b"Parent"), parent);
1901        self
1902    }
1903
1904    /// Write the `/LastModified` attribute. PDF 1.3+.
1905    pub fn last_modified(&mut self, date: Date) -> &mut Self {
1906        self.pair(Name(b"LastModified"), date);
1907        self
1908    }
1909
1910    /// Write the `/MediaBox` attribute. This is the size of the physical medium
1911    /// the page gets printed onto.
1912    pub fn media_box(&mut self, rect: Rect) -> &mut Self {
1913        self.pair(Name(b"MediaBox"), rect);
1914        self
1915    }
1916
1917    /// Write the `/CropBox` attribute. This is the size of the area within
1918    /// which content is visible.
1919    pub fn crop_box(&mut self, rect: Rect) -> &mut Self {
1920        self.pair(Name(b"CropBox"), rect);
1921        self
1922    }
1923
1924    /// Write the `/BleedBox` attribute. This is the size of the area within
1925    /// which content is visible in a print production environment. Most
1926    /// production-aiding marks should be outside of this box. PDF 1.3+.
1927    pub fn bleed_box(&mut self, rect: Rect) -> &mut Self {
1928        self.pair(Name(b"BleedBox"), rect);
1929        self
1930    }
1931
1932    /// Write the `/TrimBox` attribute. This is the size of the produced
1933    /// document after trimming is applied. PDF 1.3+.
1934    pub fn trim_box(&mut self, rect: Rect) -> &mut Self {
1935        self.pair(Name(b"TrimBox"), rect);
1936        self
1937    }
1938
1939    /// Write the `/ArtBox` attribute. This is the area that another program
1940    /// importing this file should use. PDF 1.3+.
1941    pub fn art_box(&mut self, rect: Rect) -> &mut Self {
1942        self.pair(Name(b"ArtBox"), rect);
1943        self
1944    }
1945
1946    /// Start writing the `/Resources` dictionary.
1947    pub fn resources(&mut self) -> Resources<'_> {
1948        self.insert(Name(b"Resources")).start()
1949    }
1950
1951    /// Write the `/Contents` attribute as reference to a single content stream.
1952    ///
1953    /// Such a content stream can be created using the [`Content`] builder and
1954    /// written to the file using [`Chunk::stream`].
1955    pub fn contents(&mut self, id: Ref) -> &mut Self {
1956        self.pair(Name(b"Contents"), id);
1957        self
1958    }
1959
1960    /// Write the `/Contents` attribute as an array.
1961    pub fn contents_array(&mut self, ids: impl IntoIterator<Item = Ref>) -> &mut Self {
1962        self.insert(Name(b"Contents")).array().items(ids);
1963        self
1964    }
1965
1966    /// Write the `/Rotate` attribute. This is the number of degrees the page
1967    /// should be rotated clockwise when displayed. This should be a multiple
1968    /// of 90.
1969    pub fn rotate(&mut self, degrees: i32) -> &mut Self {
1970        self.pair(Name(b"Rotate"), degrees);
1971        self
1972    }
1973
1974    /// Start writing the `/Group` dictionary to set the transparency settings
1975    /// for the page. PDF 1.4+.
1976    ///
1977    /// Required for pages with transparency in PDF/A if no output intent is
1978    /// present.
1979    pub fn group(&mut self) -> Group<'_> {
1980        self.insert(Name(b"Group")).start()
1981    }
1982
1983    /// Write the `/Thumb` attribute to set an [image][ImageXObject] as the page
1984    /// thumbnail. Must be RGB, Grayscale, or an indexed color space based on
1985    /// the former two.
1986    pub fn thumbnail(&mut self, id: Ref) -> &mut Self {
1987        self.pair(Name(b"Thumb"), id);
1988        self
1989    }
1990
1991    /// Write the `/Dur` attribute. This is the amount of seconds the page
1992    /// should be displayed before advancing to the next one. PDF 1.1+.
1993    pub fn duration(&mut self, seconds: f32) -> &mut Self {
1994        self.pair(Name(b"Dur"), seconds);
1995        self
1996    }
1997
1998    /// Start writing the `/Trans` dictionary. This sets a transition effect for
1999    /// advancing to the next page. PDF 1.1+.
2000    pub fn transition(&mut self) -> Transition<'_> {
2001        self.insert(Name(b"Trans")).start()
2002    }
2003
2004    /// Write the `/Annots` (annotations) array.
2005    pub fn annotations(&mut self, ids: impl IntoIterator<Item = Ref>) -> &mut Self {
2006        self.insert(Name(b"Annots")).array().items(ids);
2007        self
2008    }
2009
2010    /// Write the `/StructParents` attribute to indicate the [structure tree
2011    /// elements][StructElement] the contents of this XObject may belong to. PDF 1.3+.
2012    pub fn struct_parents(&mut self, key: i32) -> &mut Self {
2013        self.pair(Name(b"StructParents"), key);
2014        self
2015    }
2016
2017    /// Write the `/Tabs` attribute. This specifies the order in which the
2018    /// annotations should be focused by hitting tab. PDF 1.5+.
2019    pub fn tab_order(&mut self, order: TabOrder) -> &mut Self {
2020        self.pair(Name(b"Tabs"), order.to_name());
2021        self
2022    }
2023
2024    /// Write the `/UserUnit` attribute. This sets how large a user space unit
2025    /// is in printer's points (1/72 inch). This defaults to `1.0`. PDF 1.6+.
2026    pub fn user_unit(&mut self, value: f32) -> &mut Self {
2027        self.pair(Name(b"UserUnit"), value);
2028        self
2029    }
2030
2031    /// Start writing the `/AA` dictionary. This sets the actions to perform
2032    /// when a page is opened or closed. PDF 1.2+.
2033    ///
2034    /// Note that this attribute is forbidden in PDF/A.
2035    pub fn additional_actions(&mut self) -> AdditionalActions<'_> {
2036        self.insert(Name(b"AA")).start()
2037    }
2038
2039    /// Write the `/Metadata` attribute to specify the page's metadata. PDF
2040    /// 1.4+.
2041    ///
2042    /// The reference shall point to a [metadata stream](Metadata).
2043    ///
2044    /// Required in PDF/A.
2045    pub fn metadata(&mut self, id: Ref) -> &mut Self {
2046        self.pair(Name(b"Metadata"), id);
2047        self
2048    }
2049
2050    /// Start writing the `/AF` array to specify the associated files of the
2051    /// page. PDF 2.0+ or PDF/A-3.
2052    pub fn associated_files(&mut self) -> TypedArray<'_, FileSpec<'_>> {
2053        self.insert(Name(b"AF")).array().typed()
2054    }
2055}
2056
2057deref!('a, Page<'a> => Dict<'a>, dict);
2058
2059/// Writer for an _outline dictionary_.
2060///
2061/// This struct is created by [`Chunk::outline`].
2062pub struct Outline<'a> {
2063    dict: Dict<'a>,
2064}
2065
2066writer!(Outline: |obj| {
2067    let mut dict = obj.dict();
2068    dict.pair(Name(b"Type"), Name(b"Outlines"));
2069    Self { dict }
2070});
2071
2072impl Outline<'_> {
2073    /// Write the `/First` attribute which points to the first
2074    /// [item](OutlineItem) in the document's outline.
2075    pub fn first(&mut self, item: Ref) -> &mut Self {
2076        self.pair(Name(b"First"), item);
2077        self
2078    }
2079
2080    /// Write the `/Last` attribute which points to the last [item](OutlineItem)
2081    /// in the document's outline.
2082    pub fn last(&mut self, item: Ref) -> &mut Self {
2083        self.pair(Name(b"Last"), item);
2084        self
2085    }
2086
2087    /// Write the `/Count` attribute. This tells the viewer how many outline
2088    /// elements (at all levels) are currently visible.
2089    ///
2090    /// Panics if `count` is negative.
2091    pub fn count(&mut self, count: i32) -> &mut Self {
2092        assert!(count >= 0, "visible outline count must not be negative");
2093        self.pair(Name(b"Count"), count);
2094        self
2095    }
2096}
2097
2098deref!('a, Outline<'a> => Dict<'a>, dict);
2099
2100/// Writer for an _outline item dictionary_.
2101///
2102/// This struct is created by [`Chunk::outline_item`].
2103pub struct OutlineItem<'a> {
2104    dict: Dict<'a>,
2105}
2106
2107writer!(OutlineItem: |obj| Self { dict: obj.dict() });
2108
2109impl OutlineItem<'_> {
2110    /// Write the `/Title` attribute.
2111    pub fn title(&mut self, title: impl TextStrLike) -> &mut Self {
2112        self.pair(Name(b"Title"), title);
2113        self
2114    }
2115
2116    /// Write the `/Parent` attribute which points to the item's parent or the
2117    /// top-level outline dictionary.
2118    pub fn parent(&mut self, outline: Ref) -> &mut Self {
2119        self.pair(Name(b"Parent"), outline);
2120        self
2121    }
2122
2123    /// Write the `/Prev` attribute which points to the previous item on the
2124    /// item's level.
2125    pub fn prev(&mut self, outline: Ref) -> &mut Self {
2126        self.pair(Name(b"Prev"), outline);
2127        self
2128    }
2129
2130    /// Write the `/Next` attribute which points to the next item on the item's
2131    /// level.
2132    pub fn next(&mut self, outline: Ref) -> &mut Self {
2133        self.pair(Name(b"Next"), outline);
2134        self
2135    }
2136
2137    /// Write the `/First` attribute which points to the item's first child.
2138    pub fn first(&mut self, outline: Ref) -> &mut Self {
2139        self.pair(Name(b"First"), outline);
2140        self
2141    }
2142
2143    /// Write the `/Last` attribute which points to the item's last child.
2144    pub fn last(&mut self, outline: Ref) -> &mut Self {
2145        self.pair(Name(b"Last"), outline);
2146        self
2147    }
2148
2149    /// Write the `/Count` attribute. This tells the viewer how many outline
2150    /// element children are currently visible. If the item is collapsed, this
2151    /// number shall be negative indicating how many elements you would be able
2152    /// to see if it was open.
2153    pub fn count(&mut self, items: i32) -> &mut Self {
2154        self.pair(Name(b"Count"), items);
2155        self
2156    }
2157
2158    /// Start writing the `/Dest` attribute to set the destination of this
2159    /// outline item.
2160    pub fn dest(&mut self) -> Destination<'_> {
2161        self.insert(Name(b"Dest")).start()
2162    }
2163
2164    /// Write the `/Dest` attribute to set the destination of this
2165    /// outline item to a named destination.
2166    pub fn dest_name(&mut self, name: Name) -> &mut Self {
2167        self.pair(Name(b"Dest"), name);
2168        self
2169    }
2170
2171    /// Write the `/C` attribute using an RGB color. This sets the color in
2172    /// which the outline item's title should be rendered. PDF 1.4+.
2173    pub fn color_rgb(&mut self, r: f32, g: f32, b: f32) -> &mut Self {
2174        self.insert(Name(b"C")).array().items([r, g, b]);
2175        self
2176    }
2177
2178    /// Write the `/F` attribute. PDF 1.4+.
2179    pub fn flags(&mut self, flags: OutlineItemFlags) -> &mut Self {
2180        self.pair(Name(b"F"), flags.bits() as i32);
2181        self
2182    }
2183}
2184
2185deref!('a, OutlineItem<'a> => Dict<'a>, dict);
2186
2187bitflags::bitflags! {
2188    /// Bitflags describing the appearance of an outline item.
2189    pub struct OutlineItemFlags: u32 {
2190        /// This renders the outline item italicized.
2191        const ITALIC = 1 << 0;
2192        /// This renders the outline item emboldened.
2193        const BOLD = 1 << 1;
2194    }
2195}
2196
2197/// Writer for a _names dictionary_.
2198///
2199/// This dictionary can map various objects to names using name trees. This
2200/// struct is created by [`Catalog::names`].
2201pub struct Names<'a> {
2202    dict: Dict<'a>,
2203}
2204
2205writer!(Names: |obj| Self { dict: obj.dict() });
2206
2207impl Names<'_> {
2208    /// Start writing the `/Dests` attribute to provide associations for
2209    /// [destinations](Destination).
2210    pub fn destinations(&mut self) -> NameTree<'_, Ref> {
2211        self.dict.insert(Name(b"Dests")).start()
2212    }
2213
2214    /// Start writing the `/AP` attribute to provide associations for appearance
2215    /// streams. PDF 1.3+.
2216    pub fn appearances(&mut self) -> NameTree<'_, Ref> {
2217        self.dict.insert(Name(b"AP")).start()
2218    }
2219
2220    /// Start writing the `/JavaScript` attribute to provide associations for
2221    /// JavaScript actions. PDF 1.3+.
2222    pub fn javascript(&mut self) -> NameTree<'_, Ref> {
2223        self.dict.insert(Name(b"JavaScript")).start()
2224    }
2225
2226    /// Start writing the `/Pages` attribute to name [pages](Page). PDF 1.3+.
2227    pub fn pages(&mut self) -> NameTree<'_, Ref> {
2228        self.dict.insert(Name(b"Pages")).start()
2229    }
2230
2231    /// Start writing the `/Template` attribute to name [pages](Pages) outside
2232    /// of the page tree as templates for interactive forms. PDF 1.3+.
2233    pub fn templates(&mut self) -> NameTree<'_, Ref> {
2234        self.dict.insert(Name(b"Templates")).start()
2235    }
2236
2237    /// Start writing the `/IDS` attribute to map identifiers to Web Capture
2238    /// content sets. PDF 1.3+.
2239    pub fn capture_ids(&mut self) -> NameTree<'_, Ref> {
2240        self.dict.insert(Name(b"IDS")).start()
2241    }
2242
2243    /// Start writing the `/URLS` attribute to map URLs to Web Capture content
2244    /// sets. PDF 1.3+.
2245    pub fn capture_urls(&mut self) -> NameTree<'_, Ref> {
2246        self.dict.insert(Name(b"URLS")).start()
2247    }
2248
2249    /// Start writing the `/EmbeddedFiles` attribute to name [embedded
2250    /// files](EmbeddedFile). PDF 1.4+.
2251    ///
2252    /// Note that this key is forbidden in PDF/A-1, and restricted in PDF/A-2
2253    /// and PDF/A-4.
2254    pub fn embedded_files(&mut self) -> NameTree<'_, Ref> {
2255        self.dict.insert(Name(b"EmbeddedFiles")).start()
2256    }
2257
2258    /// Start writing the `/AlternatePresentations` attribute to name alternate
2259    /// presentations. PDF 1.4+.
2260    ///
2261    /// Note that this key is forbidden in PDF/A.
2262    pub fn alternate_presentations(&mut self) -> NameTree<'_, Ref> {
2263        self.dict.insert(Name(b"AlternatePresentations")).start()
2264    }
2265
2266    /// Start writing the `/Renditions` attribute to name renditions. The names
2267    /// must conform to Unicode. PDF 1.5+.
2268    pub fn renditions(&mut self) -> NameTree<'_, Ref> {
2269        self.dict.insert(Name(b"Renditions")).start()
2270    }
2271}
2272
2273deref!('a, Names<'a> => Dict<'a>, dict);
2274
2275/// Writer for a _destination array_.
2276///
2277/// A dictionary mapping to this struct is created by
2278/// [`Chunk::destinations`]. This struct is also created by
2279/// [`Action::destination`].
2280pub struct Destination<'a> {
2281    array: Array<'a>,
2282}
2283
2284writer!(Destination: |obj| Self { array: obj.array() });
2285
2286impl Destination<'_> {
2287    /// The target page. Required.
2288    pub fn page(mut self, page: Ref) -> Self {
2289        self.item(page);
2290        self
2291    }
2292
2293    /// Write the `/XYZ` command which skips to the specified coordinated.
2294    pub fn xyz(mut self, left: f32, top: f32, zoom: Option<f32>) {
2295        self.item(Name(b"XYZ"));
2296        self.item(left);
2297        self.item(top);
2298        self.item(zoom.unwrap_or_default());
2299    }
2300
2301    /// Write the `/Fit` command which fits all of the referenced page on
2302    /// screen.
2303    pub fn fit(mut self) {
2304        self.item(Name(b"Fit"));
2305    }
2306
2307    /// Write the `/FitH` command which fits the referenced page to the screen
2308    /// width and skips to the specified offset.
2309    pub fn fit_horizontal(mut self, top: f32) {
2310        self.item(Name(b"FitH"));
2311        self.item(top);
2312    }
2313
2314    /// Write the `/FitV` command which fits the referenced page to the screen
2315    /// height and skips to the specified offset.
2316    pub fn fit_vertical(mut self, left: f32) {
2317        self.item(Name(b"FitV"));
2318        self.item(left);
2319    }
2320
2321    /// Write the `/FitR` command which fits the rectangle argument on the
2322    /// screen.
2323    pub fn fit_rect(mut self, rect: Rect) {
2324        self.item(Name(b"FitR"));
2325        self.array.items([rect.x1, rect.y1, rect.x2, rect.y2]);
2326    }
2327
2328    /// Write the `/FitB` command which fits all of the referenced page's
2329    /// content on screen. PDF 1.1+.
2330    pub fn fit_bounding_box(mut self) {
2331        self.item(Name(b"FitB"));
2332    }
2333
2334    /// Write the `/FitBH` command which fits the referenced page's content to
2335    /// the screen width and skips to the specified offset. PDF 1.1+.
2336    pub fn fit_bounding_box_horizontal(mut self, top: f32) {
2337        self.item(Name(b"FitBH"));
2338        self.item(top);
2339    }
2340
2341    /// Write the `/FitBV` command which fits the referenced page's content to
2342    /// the screen height and skips to the specified offset. PDF 1.1+.
2343    pub fn fit_bounding_box_vertical(mut self, left: f32) {
2344        self.item(Name(b"FitBV"));
2345        self.item(left);
2346    }
2347}
2348
2349deref!('a, Destination<'a> => Array<'a>, array);
2350
2351/// What order to tab through the annotations on a page.
2352#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
2353pub enum TabOrder {
2354    /// Navigate the annotations horizontally, then vertically.
2355    RowOrder,
2356    /// Navigate the annotations vertically, then horizontally.
2357    ColumnOrder,
2358    /// Navigate the annotations in the order they appear in the structure tree.
2359    StructureOrder,
2360}
2361
2362impl TabOrder {
2363    pub(crate) fn to_name(self) -> Name<'static> {
2364        match self {
2365            Self::RowOrder => Name(b"R"),
2366            Self::ColumnOrder => Name(b"C"),
2367            Self::StructureOrder => Name(b"S"),
2368        }
2369    }
2370}
2371
2372/// Writer for a _metadata stream_. PDF 1.4+.
2373///
2374/// This struct is created by [`Chunk::metadata`].
2375pub struct Metadata<'a> {
2376    stream: Stream<'a>,
2377}
2378
2379impl<'a> Metadata<'a> {
2380    /// Create a new metadata stream writer.
2381    pub(crate) fn start(mut stream: Stream<'a>) -> Self {
2382        stream.pair(Name(b"Type"), Name(b"Metadata"));
2383        stream.pair(Name(b"Subtype"), Name(b"XML"));
2384        Self { stream }
2385    }
2386}
2387
2388deref!('a, Metadata<'a> => Stream<'a>, stream);
2389
2390#[cfg(test)]
2391mod tests {
2392    use super::*;
2393
2394    #[test]
2395    fn test_max_heading_name() {
2396        let mut buf = [0; 6];
2397        let name = StructRole2::Heading(NonZeroU16::MAX).to_name(&mut buf);
2398        assert_eq!(Name(b"H65535"), name);
2399    }
2400}