pdf_writer/
attributes.rs

1use super::*;
2use crate::object::TextStrLike;
3use crate::types::{ArtifactSubtype, ArtifactType};
4
5/// Writer for an _attribute dictionary_. PDF 1.4+
6///
7/// This struct must set the `/O` attribute by calling any of the methods. This
8/// struct is created by [`ClassMap::single`]. An array of this struct is
9/// created by [`StructElement::attributes`] and [`ClassMap::multiple`].
10pub struct Attributes<'a> {
11    dict: Dict<'a>,
12}
13
14writer!(Attributes: |obj| Self { dict: obj.dict() });
15
16impl<'a> Attributes<'a> {
17    /// Write the `/O` attribute to set the owner.
18    ///
19    /// Should not be called when using any of the other methods.
20    ///
21    /// The `iso32000_2_2020` parameter is used to determine the name for the
22    /// CSS 1 and CSS 2 variants. Set it to `true` when writing a PDF 2.0 file
23    /// conforming to the 2020 edition of the standard or later.
24    pub fn owner(&mut self, owner: AttributeOwner, iso32000_2_2020: bool) -> &mut Self {
25        self.pair(Name(b"O"), owner.to_name(iso32000_2_2020));
26        self
27    }
28
29    /// Set the `/O` attribute to user-defined and start writing the `/P` array
30    /// with user properties. PDF 1.6+
31    pub fn user(&mut self) -> TypedArray<'_, UserProperty<'_>> {
32        self.pair(Name(b"O"), AttributeOwner::User.to_name(false));
33        self.insert(Name(b"P")).array().typed()
34    }
35
36    /// Set the `/O` attribute to `Layout` to start writing layout parameters.
37    pub fn layout(self) -> LayoutAttributes<'a> {
38        LayoutAttributes::start_with_dict(self.dict)
39    }
40
41    /// Set the `/O` attribute to `List` to start writing list attributes.
42    pub fn list(self) -> ListAttributes<'a> {
43        ListAttributes::start_with_dict(self.dict)
44    }
45
46    /// Set the `/O` attribute to `PrintField` to start writing attributes for
47    /// the appearance of form fields. PDF 1.6+
48    pub fn field(self) -> FieldAttributes<'a> {
49        FieldAttributes::start_with_dict(self.dict)
50    }
51
52    /// Set the `/O` attribute to `Table` to start writing table attributes.
53    pub fn table(self) -> TableAttributes<'a> {
54        TableAttributes::start_with_dict(self.dict)
55    }
56
57    /// Set the `/O` attribute to `Artifact` to start writing attributes for
58    /// artifacts. PDF 2.0+
59    pub fn artifact(self) -> ArtifactAttributes<'a> {
60        ArtifactAttributes::start_with_dict(self.dict)
61    }
62
63    /// Set the `/O` attribute to `FENote` to start writing attributes for
64    /// footnote elements. PDF/UA-2
65    pub fn note(self) -> FENoteAttributes<'a> {
66        FENoteAttributes::start_with_dict(self.dict)
67    }
68
69    /// Set the `/O` attribute to `NSO` and set the `/NS` attribute to an
70    /// indirect reference to a [`Namespace`] dictionary to start writing
71    /// attributes for a namespace. PDF 2.0+
72    pub fn namespace(self, ns: Ref) -> Dict<'a> {
73        let mut dict = self.dict;
74        dict.pair(Name(b"O"), AttributeOwner::NSO.to_name(true));
75        dict.pair(Name(b"NS"), ns);
76        dict
77    }
78}
79
80deref!('a, Attributes<'a> => Dict<'a>, dict);
81
82/// Writer for an _user property dictionary_. PDF 1.6+
83///
84/// An array of this struct is created by [`Attributes::user`].
85pub struct UserProperty<'a> {
86    dict: Dict<'a>,
87}
88
89writer!(UserProperty: |obj| Self { dict: obj.dict() });
90
91impl UserProperty<'_> {
92    /// Write the `/N` attribute to set the name of the property.
93    pub fn name(&mut self, name: impl TextStrLike) -> &mut Self {
94        self.dict.pair(Name(b"N"), name);
95        self
96    }
97
98    /// Start writing the `/V` attribute to set the value of the property.
99    pub fn value(&mut self) -> Obj<'_> {
100        self.dict.insert(Name(b"V"))
101    }
102
103    /// Write the `/F` attribute to set the format of the property.
104    pub fn format(&mut self, format: impl TextStrLike) -> &mut Self {
105        self.dict.pair(Name(b"F"), format);
106        self
107    }
108
109    /// Write the `/H` attribute to determine whether this property is hidden in
110    /// the user interface.
111    pub fn hidden(&mut self, hide: bool) -> &mut Self {
112        self.dict.pair(Name(b"H"), hide);
113        self
114    }
115}
116
117deref!('a, UserProperty<'a> => Dict<'a>, dict);
118
119/// Writer for an _layout attributes dictionary_. PDF 1.4+
120///
121/// This struct is created by [`Attributes::layout`].
122pub struct LayoutAttributes<'a> {
123    dict: Dict<'a>,
124}
125
126writer!(LayoutAttributes: |obj| Self::start_with_dict(obj.dict()));
127
128/// General layout attributes.
129impl<'a> LayoutAttributes<'a> {
130    pub(crate) fn start_with_dict(mut dict: Dict<'a>) -> Self {
131        dict.pair(Name(b"O"), AttributeOwner::Layout.to_name(false));
132        Self { dict }
133    }
134
135    /// Write the `/Placement` attribute.
136    pub fn placement(&mut self, placement: Placement) -> &mut Self {
137        self.dict.pair(Name(b"Placement"), placement.to_name());
138        self
139    }
140
141    /// Write the `/WritingMode` attribute to set the writing direction.
142    pub fn writing_mode(&mut self, mode: WritingMode) -> &mut Self {
143        self.dict.pair(Name(b"WritingMode"), mode.to_name());
144        self
145    }
146
147    /// Write the `/BackgroundColor` attribute to set the background color in
148    /// RGB between `0` and `1`. PDF 1.5+
149    pub fn background_color(&mut self, color: [f32; 3]) -> &mut Self {
150        self.dict
151            .insert(Name(b"BackgroundColor"))
152            .array()
153            .typed()
154            .items(color);
155        self
156    }
157
158    /// Write the `/BorderColor` attribute.
159    pub fn border_color(&mut self, color: Sides<[f32; 3]>) -> &mut Self {
160        let obj = self.dict.insert(Name(b"BorderColor"));
161        color.write_iter(obj);
162        self
163    }
164
165    /// Write the `/BorderStyle` attribute.
166    pub fn border_style(&mut self, style: Sides<LayoutBorderStyle>) -> &mut Self {
167        let obj = self.dict.insert(Name(b"BorderStyle"));
168        style.write_map_primitive(obj, LayoutBorderStyle::to_name);
169        self
170    }
171
172    /// Write the `/BorderThickness` attribute.
173    pub fn border_thickness(&mut self, thickness: Sides<f32>) -> &mut Self {
174        let obj = self.dict.insert(Name(b"BorderThickness"));
175        thickness.write_primitive(obj);
176        self
177    }
178
179    /// Write the `/Padding` attribute.
180    pub fn padding(&mut self, padding: Sides<f32>) -> &mut Self {
181        let obj = self.dict.insert(Name(b"Padding"));
182        padding.write_primitive(obj);
183        self
184    }
185
186    /// Write the `/Color` attribute.
187    pub fn color(&mut self, color: [f32; 3]) -> &mut Self {
188        self.dict.insert(Name(b"Color")).array().typed().items(color);
189        self
190    }
191}
192
193/// A value that applies to the four sides of a rectangular container.
194#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
195pub struct Sides<T> {
196    /// The value before the rectangle, in the block progression direction. In
197    /// the LTR writing mode, this is the top side.
198    pub before: T,
199    /// The value after the rectangle, in the block progression direction. In
200    /// the LTR writing mode, this is the bottom side.
201    pub after: T,
202    /// The value at the inline start edge of the rectangle. In the LTR writing
203    /// mode, this is the left side.
204    pub start: T,
205    /// The value at the inline end edge of the rectangle. In the LTR writing
206    /// mode, this is the right side.
207    pub end: T,
208}
209
210impl<T> Sides<T> {
211    /// Create a new `Sides` struct with the given values for each side.
212    pub fn new(before: T, after: T, start: T, end: T) -> Self {
213        Self { before, after, start, end }
214    }
215
216    /// Create a new `Sides` struct with an array ordered as `[before, after,
217    /// start, end]`, just like in the PDF specification.
218    pub fn from_array(array: [T; 4]) -> Self {
219        let [before, after, start, end] = array;
220        Self { before, after, start, end }
221    }
222
223    /// Create a new `Sides` with the same value for all sides.
224    pub fn uniform(value: T) -> Self
225    where
226        T: Clone,
227    {
228        Self {
229            before: value.clone(),
230            after: value.clone(),
231            start: value.clone(),
232            end: value,
233        }
234    }
235
236    /// Check if all sides have the same value.
237    fn is_uniform(&self) -> bool
238    where
239        T: PartialEq,
240    {
241        self.before == self.after && self.before == self.start && self.before == self.end
242    }
243
244    /// Get the sides as an array ordered as `[before, after, start, end]`.
245    fn into_array(self) -> [T; 4] {
246        [self.before, self.after, self.start, self.end]
247    }
248
249    /// Write the sides of a primitive type.
250    fn write_primitive(self, obj: Obj<'_>)
251    where
252        T: Primitive + PartialEq,
253    {
254        self.write_map_primitive(obj, |side| side);
255    }
256
257    /// Write the sides of a value that can be mapped to a primitive type with a
258    /// closure.
259    fn write_map_primitive<P>(self, obj: Obj<'_>, mut to_primitive: impl FnMut(T) -> P)
260    where
261        T: PartialEq,
262        P: Primitive,
263    {
264        if self.is_uniform() {
265            obj.primitive(to_primitive(self.before));
266        } else {
267            let mut array = obj.array();
268            for side in self.into_array() {
269                array.push().primitive(to_primitive(side));
270            }
271        }
272    }
273
274    /// Write the sides of an iterable type containing primitive values.
275    fn write_iter<P>(self, obj: Obj<'_>)
276    where
277        T: IntoIterator<Item = P> + PartialEq,
278        P: Primitive,
279    {
280        if self.is_uniform() {
281            obj.array().typed().items(self.before);
282        } else {
283            let mut array = obj.array();
284            for side in self.into_array() {
285                array.push().array().typed().items(side);
286            }
287        }
288    }
289}
290
291/// Placement of an element.
292#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
293pub enum Placement {
294    /// Stacked in the block order.
295    Block,
296    /// Stacked in the inline order.
297    Inline,
298    /// Before edge coincides with that of reference area, touching the edge of
299    /// the previous block.
300    Before,
301    /// Start edge coincides with that of reference area, aligned on the
302    /// non-stacking axis of the reference area.
303    Start,
304    /// End edge coincides with that of reference area, aligned on the
305    /// non-stacking axis of the reference area.
306    End,
307}
308
309impl Placement {
310    pub(crate) fn to_name(self) -> Name<'static> {
311        match self {
312            Self::Block => Name(b"Block"),
313            Self::Inline => Name(b"Inline"),
314            Self::Before => Name(b"Before"),
315            Self::Start => Name(b"Start"),
316            Self::End => Name(b"End"),
317        }
318    }
319}
320
321/// Writing direction.
322#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
323pub enum WritingMode {
324    /// Horizontal writing mode, left-to-right.
325    #[default]
326    LtrTtb,
327    /// Horizontal writing mode, right-to-left.
328    RtlTtb,
329    /// Vertical writing mode, right-to-left.
330    TtbRtl,
331}
332
333impl WritingMode {
334    pub(crate) fn to_name(self) -> Name<'static> {
335        match self {
336            Self::LtrTtb => Name(b"LrTb"),
337            Self::RtlTtb => Name(b"RlTb"),
338            Self::TtbRtl => Name(b"TbRl"),
339        }
340    }
341}
342
343/// Layout border style.
344#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
345pub enum LayoutBorderStyle {
346    /// No border.
347    #[default]
348    None,
349    /// Hidden border.
350    Hidden,
351    /// Solid border.
352    Solid,
353    /// Dashed border.
354    Dashed,
355    /// Dotted border.
356    Dotted,
357    /// Double border.
358    Double,
359    /// Groove border.
360    Groove,
361    /// Ridge border.
362    Ridge,
363    /// Inset border.
364    Inset,
365    /// Outset border.
366    Outset,
367}
368
369impl LayoutBorderStyle {
370    pub(crate) fn to_name(self) -> Name<'static> {
371        match self {
372            Self::None => Name(b"None"),
373            Self::Hidden => Name(b"Hidden"),
374            Self::Solid => Name(b"Solid"),
375            Self::Dashed => Name(b"Dashed"),
376            Self::Dotted => Name(b"Dotted"),
377            Self::Double => Name(b"Double"),
378            Self::Groove => Name(b"Groove"),
379            Self::Ridge => Name(b"Ridge"),
380            Self::Inset => Name(b"Inset"),
381            Self::Outset => Name(b"Outset"),
382        }
383    }
384}
385
386/// Block level elements.
387impl LayoutAttributes<'_> {
388    /// Write the `/SpaceBefore` attribute.
389    pub fn space_before(&mut self, space: f32) -> &mut Self {
390        self.dict.pair(Name(b"SpaceBefore"), space);
391        self
392    }
393
394    /// Write the `/SpaceAfter` attribute.
395    pub fn space_after(&mut self, space: f32) -> &mut Self {
396        self.dict.pair(Name(b"SpaceAfter"), space);
397        self
398    }
399
400    /// Write the `/StartIndent` attribute.
401    pub fn start_indent(&mut self, indent: f32) -> &mut Self {
402        self.dict.pair(Name(b"StartIndent"), indent);
403        self
404    }
405
406    /// Write the `/EndIndent` attribute.
407    pub fn end_indent(&mut self, indent: f32) -> &mut Self {
408        self.dict.pair(Name(b"EndIndent"), indent);
409        self
410    }
411
412    /// Write the `/TextIndent` attribute.
413    pub fn text_indent(&mut self, indent: f32) -> &mut Self {
414        self.dict.pair(Name(b"TextIndent"), indent);
415        self
416    }
417
418    /// Write the `/TextAlign` attribute.
419    pub fn text_align(&mut self, align: TextAlign) -> &mut Self {
420        self.dict.pair(Name(b"TextAlign"), align.to_name());
421        self
422    }
423
424    /// Write the `/Width` attribute for table row groups and illustrative
425    /// elements. No intrinsic height will be assumed if left empty.
426    pub fn width(&mut self, width: f32) -> &mut Self {
427        self.dict.pair(Name(b"Width"), width);
428        self
429    }
430
431    /// Write the `/Height` attribute for table row groups and illustrative
432    /// elements. No intrinsic height will be assumed if left empty.
433    pub fn height(&mut self, height: f32) -> &mut Self {
434        self.dict.pair(Name(b"Height"), height);
435        self
436    }
437}
438
439/// The text alignment.
440#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
441pub enum TextAlign {
442    /// At the start of the inline advance direction.
443    #[default]
444    Start,
445    /// Centered.
446    Center,
447    /// At the end of the inline advance direction.
448    End,
449    /// Justified.
450    Justify,
451}
452
453impl TextAlign {
454    pub(crate) fn to_name(self) -> Name<'static> {
455        match self {
456            Self::Start => Name(b"Start"),
457            Self::Center => Name(b"Center"),
458            Self::End => Name(b"End"),
459            Self::Justify => Name(b"Justify"),
460        }
461    }
462}
463
464/// Illustration elements.
465impl LayoutAttributes<'_> {
466    /// Write the `/BBox` attribute.
467    pub fn bbox(&mut self, bbox: Rect) -> &mut Self {
468        self.dict.pair(Name(b"BBox"), bbox);
469        self
470    }
471}
472
473/// Table header and data.
474impl LayoutAttributes<'_> {
475    /// Write the `/BlockAlign` attribute.
476    pub fn block_align(&mut self, align: BlockAlign) -> &mut Self {
477        self.dict.pair(Name(b"BlockAlign"), align.to_name());
478        self
479    }
480
481    /// Write the `/InlineAlign` attribute.
482    pub fn inline_align(&mut self, align: InlineAlign) -> &mut Self {
483        self.dict.pair(Name(b"InlineAlign"), align.to_name());
484        self
485    }
486
487    /// Write the `/TBorderStyle` attribute. PDF 1.5+.
488    pub fn table_border_style(&mut self, style: Sides<LayoutBorderStyle>) -> &mut Self {
489        let obj = self.dict.insert(Name(b"TBorderStyle"));
490        style.write_map_primitive(obj, LayoutBorderStyle::to_name);
491        self
492    }
493
494    /// Write the `/TPadding` attribute. PDF 1.5+.
495    pub fn table_padding(&mut self, padding: Sides<f32>) -> &mut Self {
496        let obj = self.dict.insert(Name(b"TPadding"));
497        padding.write_primitive(obj);
498        self
499    }
500}
501
502/// The block alignment.
503#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
504pub enum BlockAlign {
505    /// At the start of the block advance direction.
506    #[default]
507    Before,
508    /// Centered.
509    Middle,
510    /// At the end of the block advance direction.
511    After,
512    /// Justified.
513    Justify,
514}
515
516impl BlockAlign {
517    pub(crate) fn to_name(self) -> Name<'static> {
518        match self {
519            Self::Before => Name(b"Before"),
520            Self::Middle => Name(b"Middle"),
521            Self::After => Name(b"After"),
522            Self::Justify => Name(b"Justify"),
523        }
524    }
525}
526
527/// Grouping elements.
528impl LayoutAttributes<'_> {
529    /// Write the `/ColumnCount` attribute. PDF 1.6+.
530    pub fn column_count(&mut self, count: i32) -> &mut Self {
531        self.dict.pair(Name(b"ColumnCount"), count);
532        self
533    }
534
535    /// Start writing the `/ColumnWidths` array. PDF 1.6+.
536    pub fn column_widths(&mut self) -> TrackSizes<'_> {
537        TrackSizes::start(self.dict.insert(Name(b"ColumnWidths")))
538    }
539
540    /// Start writing the `/ColumnGap` array. PDF 1.6+.
541    pub fn column_gap(&mut self) -> TrackSizes<'_> {
542        TrackSizes::start(self.dict.insert(Name(b"ColumnGap")))
543    }
544}
545
546/// Writer for a _column tracks attribute value_. PDF 1.6+.
547///
548/// This writer is created by [`LayoutAttributes::column_widths`] and
549/// [`LayoutAttributes::column_gap`].
550pub struct TrackSizes<'a> {
551    obj: Obj<'a>,
552}
553
554writer!(TrackSizes: |obj| Self { obj });
555
556impl<'a> TrackSizes<'a> {
557    /// Write the same value for all column or gap tracks.
558    pub fn uniform(self, value: f32) {
559        self.obj.primitive(value);
560    }
561
562    /// Start writing an array of values for individual tracks. If there are
563    /// more tracks than values, the last value is used for all remaining
564    /// tracks.
565    pub fn individual(self) -> TypedArray<'a, f32> {
566        self.obj.array().typed()
567    }
568}
569
570deref!('a, TrackSizes<'a> => Obj<'a>, obj);
571
572/// The inline alignment.
573#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
574pub enum InlineAlign {
575    /// At the start of the inline advance direction.
576    #[default]
577    Start,
578    /// Centered.
579    Center,
580    /// At the end of the inline advance direction.
581    End,
582}
583
584impl InlineAlign {
585    pub(crate) fn to_name(self) -> Name<'static> {
586        match self {
587            Self::Start => Name(b"Start"),
588            Self::Center => Name(b"Center"),
589            Self::End => Name(b"End"),
590        }
591    }
592}
593
594/// Inline elements.
595impl LayoutAttributes<'_> {
596    /// Write the `/LineHeight` attribute.
597    pub fn line_height(&mut self, height: LineHeight) -> &mut Self {
598        height.write(self.dict.insert(Name(b"LineHeight")));
599        self
600    }
601
602    /// Write the `/BaselineShift` attribute.
603    pub fn baseline_shift(&mut self, shift: f32) -> &mut Self {
604        self.dict.pair(Name(b"BaselineShift"), shift);
605        self
606    }
607
608    /// Write the `/TextPosition` attribute. PDF 2.0+.
609    pub fn text_position(&mut self, position: LayoutTextPosition) -> &mut Self {
610        self.dict.pair(Name(b"TextPosition"), position.to_name());
611        self
612    }
613
614    /// Write the `/TextDecorationType` attribute. PDF 1.5+.
615    pub fn text_decoration_type(&mut self, decoration: TextDecorationType) -> &mut Self {
616        self.dict.pair(Name(b"TextDecorationType"), decoration.to_name());
617        self
618    }
619
620    /// Write the `/TextDecorationColor` attribute in RGB. PDF 1.5+.
621    pub fn text_decoration_color(&mut self, color: [f32; 3]) -> &mut Self {
622        self.dict
623            .insert(Name(b"TextDecorationColor"))
624            .array()
625            .typed()
626            .items(color);
627        self
628    }
629
630    /// Write the `/TextDecorationThickness` attribute. PDF 1.5+.
631    pub fn text_decoration_thickness(&mut self, thickness: f32) -> &mut Self {
632        self.dict.pair(Name(b"TextDecorationThickness"), thickness);
633        self
634    }
635}
636
637/// The height of a line.
638#[derive(Debug, Copy, Clone, Default, PartialEq)]
639pub enum LineHeight {
640    /// Adjust the line height automatically, taking `/BaselineShift` into
641    /// account.
642    #[default]
643    Normal,
644    /// Adjust the line height automatically.
645    Auto,
646    /// Set a fixed line height.
647    Custom(f32),
648}
649
650impl LineHeight {
651    pub(crate) fn write(self, obj: Obj) {
652        match self {
653            Self::Normal => obj.primitive(Name(b"Normal")),
654            Self::Auto => obj.primitive(Name(b"Auto")),
655            Self::Custom(height) => obj.primitive(height),
656        }
657    }
658}
659
660/// Where the text is positioned relative to the baseline.
661#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
662pub enum LayoutTextPosition {
663    /// At the baseline.
664    #[default]
665    Normal,
666    /// Above the baseline.
667    Superscript,
668    /// Below the baseline.
669    Subscript,
670}
671
672impl LayoutTextPosition {
673    pub(crate) fn to_name(self) -> Name<'static> {
674        match self {
675            Self::Normal => Name(b"Normal"),
676            Self::Superscript => Name(b"Sup"),
677            Self::Subscript => Name(b"Sub"),
678        }
679    }
680}
681
682/// The text decoration type (over- and underlines).
683#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
684pub enum TextDecorationType {
685    /// No decoration.
686    #[default]
687    None,
688    /// Underlined.
689    Underline,
690    /// Line over the text.
691    Overline,
692    /// Strike the text.
693    LineThrough,
694}
695
696impl TextDecorationType {
697    pub(crate) fn to_name(self) -> Name<'static> {
698        match self {
699            Self::None => Name(b"None"),
700            Self::Underline => Name(b"Underline"),
701            Self::Overline => Name(b"Overline"),
702            Self::LineThrough => Name(b"LineThrough"),
703        }
704    }
705}
706
707/// The orientation of a glyph when the inline-progression is top-to-bottom or
708/// bottom-to-top.
709#[derive(Debug, Copy, Clone, Default, PartialEq)]
710pub enum GlyphOrientationVertical {
711    /// Orient the glyph automatically.
712    #[default]
713    Auto,
714    /// An angle between -180 and +360 degrees in multiples of 90 degrees.
715    Angle(i32),
716}
717
718impl GlyphOrientationVertical {
719    pub(crate) fn write(self, obj: Obj) {
720        match self {
721            Self::Auto => obj.primitive(Name(b"Auto")),
722            Self::Angle(angle) => obj.primitive(angle),
723        }
724    }
725}
726
727/// Vertical Text.
728impl LayoutAttributes<'_> {
729    /// Write the `/GlyphOrientationVertical` attribute. PDF 1.5+.
730    pub fn glyph_orientation_vertical(
731        &mut self,
732        orientation: GlyphOrientationVertical,
733    ) -> &mut Self {
734        orientation.write(self.dict.insert(Name(b"GlyphOrientationVertical")));
735        self
736    }
737}
738
739/// Ruby annotations.
740impl LayoutAttributes<'_> {
741    /// Write the `/RubyAlign` attribute. PDF 1.5+.
742    pub fn ruby_align(&mut self, align: RubyAlign) -> &mut Self {
743        self.dict.pair(Name(b"RubyAlign"), align.to_name());
744        self
745    }
746
747    /// Write the `/RubyPosition` attribute. PDF 1.5+.
748    pub fn ruby_position(&mut self, position: RubyPosition) -> &mut Self {
749        self.dict.pair(Name(b"RubyPosition"), position.to_name());
750        self
751    }
752}
753
754/// The alignment of a ruby annotation.
755#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
756pub enum RubyAlign {
757    /// At the start of the inline advance direction.
758    Start,
759    /// Centered.
760    Center,
761    /// At the end of the inline advance direction.
762    End,
763    /// Justified.
764    Justify,
765    /// Distribute along the full width of the line with additional space.
766    #[default]
767    Distribute,
768}
769
770impl RubyAlign {
771    pub(crate) fn to_name(self) -> Name<'static> {
772        match self {
773            Self::Start => Name(b"Start"),
774            Self::Center => Name(b"Center"),
775            Self::End => Name(b"End"),
776            Self::Justify => Name(b"Justify"),
777            Self::Distribute => Name(b"Distribute"),
778        }
779    }
780}
781
782/// The position of a ruby annotation.
783#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
784pub enum RubyPosition {
785    /// Before edge of the element.
786    #[default]
787    Before,
788    /// After edge of the element.
789    After,
790    /// Render as a Warichu.
791    Warichu,
792    /// Render in-line.
793    Inline,
794}
795
796impl RubyPosition {
797    pub(crate) fn to_name(self) -> Name<'static> {
798        match self {
799            Self::Before => Name(b"Before"),
800            Self::After => Name(b"After"),
801            Self::Warichu => Name(b"Warichu"),
802            Self::Inline => Name(b"Inline"),
803        }
804    }
805}
806
807deref!('a, LayoutAttributes<'a> => Dict<'a>, dict);
808
809/// Writer for an _list attributes dictionary_. PDF 1.4+
810///
811/// This struct is created by [`Attributes::list`].
812pub struct ListAttributes<'a> {
813    dict: Dict<'a>,
814}
815
816writer!(ListAttributes: |obj| Self::start_with_dict(obj.dict()));
817
818impl<'a> ListAttributes<'a> {
819    pub(crate) fn start_with_dict(mut dict: Dict<'a>) -> Self {
820        dict.pair(Name(b"O"), AttributeOwner::List.to_name(false));
821        Self { dict }
822    }
823
824    /// Write the `/ListNumbering` attribute.
825    pub fn list_numbering(&mut self, numbering: ListNumbering) -> &mut Self {
826        self.dict.pair(Name(b"ListNumbering"), numbering.to_name());
827        self
828    }
829
830    /// Write the `/ContinuedList` attribute. PDF 2.0+.
831    ///
832    /// Setting this to `true` indicates that the list continues from the
833    /// previous list, or from the list indicated by the `/ContinuedFrom`
834    /// attribute.
835    pub fn continued_list(&mut self, continued: bool) -> &mut Self {
836        self.dict.pair(Name(b"ContinuedList"), continued);
837        self
838    }
839
840    /// Write the `/ContinuedFrom` attribute. PDF 2.0+.
841    ///
842    /// Also see [`ListAttributes::continued_list`].
843    pub fn continued_from(&mut self, id: Str) -> &mut Self {
844        self.dict.pair(Name(b"ContinuedFrom"), id);
845        self
846    }
847}
848
849deref!('a, ListAttributes<'a> => Dict<'a>, dict);
850
851/// The list numbering type.
852#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
853pub enum ListNumbering {
854    /// No numbering.
855    #[default]
856    None,
857    /// An unordered list with unspecified bullets. PDF 2.0+.
858    Unordered,
859    /// An ordered list with unspecified numbering. PDF 2.0+.
860    Ordered,
861    /// A list defining terms and their definitions. PDF 2.0+.
862    Description,
863    /// Solid circular bullets.
864    Disc,
865    /// Open circular bullets.
866    Circle,
867    /// Solid square bullets.
868    Square,
869    /// Decimal numbers.
870    Decimal,
871    /// Lowercase Roman numerals.
872    LowerRoman,
873    /// Uppercase Roman numerals.
874    UpperRoman,
875    /// Lowercase letters.
876    LowerAlpha,
877    /// Uppercase letters.
878    UpperAlpha,
879}
880
881impl ListNumbering {
882    pub(crate) fn to_name(self) -> Name<'static> {
883        match self {
884            Self::None => Name(b"None"),
885            Self::Unordered => Name(b"Unordered"),
886            Self::Ordered => Name(b"Ordered"),
887            Self::Description => Name(b"Description"),
888            Self::Disc => Name(b"Disc"),
889            Self::Circle => Name(b"Circle"),
890            Self::Square => Name(b"Square"),
891            Self::Decimal => Name(b"Decimal"),
892            Self::LowerRoman => Name(b"LowerRoman"),
893            Self::UpperRoman => Name(b"UpperRoman"),
894            Self::LowerAlpha => Name(b"LowerAlpha"),
895            Self::UpperAlpha => Name(b"UpperAlpha"),
896        }
897    }
898}
899
900/// Writer for an _PrintField attributes dictionary_. PDF 1.6+
901///
902/// This struct is created by [`Attributes::field`].
903pub struct FieldAttributes<'a> {
904    dict: Dict<'a>,
905}
906
907writer!(FieldAttributes: |obj| Self::start_with_dict(obj.dict()));
908
909impl<'a> FieldAttributes<'a> {
910    pub(crate) fn start_with_dict(mut dict: Dict<'a>) -> Self {
911        dict.pair(Name(b"O"), AttributeOwner::PrintField.to_name(false));
912        Self { dict }
913    }
914
915    /// Write the `/Role` attribute to determine the kind of form control.
916    pub fn role(&mut self, role: FieldRole) -> &mut Self {
917        self.dict.pair(Name(b"Role"), role.to_name());
918        self
919    }
920
921    /// Write the `/checked` or `/Checked` attribute to set whether a radio
922    /// button or checkbox is checked.
923    ///
924    /// The `pdf2` parameter determines whether the attribute is written as
925    /// `/checked` or `/Checked`. PDF 2.0+ has deprecated the lowercase
926    /// `/checked` attribute.
927    pub fn checked(&mut self, checked: FieldState, pdf2: bool) -> &mut Self {
928        self.dict
929            .pair(Name(if pdf2 { b"Checked" } else { b"checked" }), checked.to_name());
930        self
931    }
932
933    /// Write the `/Desc` attribute to set the description of the form control.
934    pub fn description(&mut self, desc: impl TextStrLike) -> &mut Self {
935        self.dict.pair(Name(b"Desc"), desc);
936        self
937    }
938}
939
940deref!('a, FieldAttributes<'a> => Dict<'a>, dict);
941
942/// The kind of form control.
943#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
944pub enum FieldRole {
945    /// A button.
946    Button,
947    /// A checkbox.
948    CheckBox,
949    /// A radio button.
950    RadioButton,
951    /// A text field.
952    TextField,
953    /// A list box. PDF 2.0+.
954    ListBox,
955}
956
957impl FieldRole {
958    pub(crate) fn to_name(self) -> Name<'static> {
959        match self {
960            Self::Button => Name(b"pb"),
961            Self::CheckBox => Name(b"cb"),
962            Self::RadioButton => Name(b"rb"),
963            Self::TextField => Name(b"tv"),
964            Self::ListBox => Name(b"lb"),
965        }
966    }
967}
968
969/// Whether a check box or radio button is checked.
970#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
971pub enum FieldState {
972    /// The check box or radio button is unchecked.
973    #[default]
974    Unchecked,
975    /// The check box or radio button is checked.
976    Checked,
977    /// The check box or radio button is in a quantum superstate.
978    Neutral,
979}
980
981impl FieldState {
982    pub(crate) fn to_name(self) -> Name<'static> {
983        match self {
984            Self::Unchecked => Name(b"off"),
985            Self::Checked => Name(b"on"),
986            Self::Neutral => Name(b"neutral"),
987        }
988    }
989}
990
991/// Writer for a _table attributes dictionary_. PDF 1.4+
992///
993/// This struct is created by [`Attributes::table`].
994pub struct TableAttributes<'a> {
995    dict: Dict<'a>,
996}
997
998writer!(TableAttributes: |obj| Self::start_with_dict(obj.dict()));
999
1000impl<'a> TableAttributes<'a> {
1001    pub(crate) fn start_with_dict(mut dict: Dict<'a>) -> Self {
1002        dict.pair(Name(b"O"), AttributeOwner::Table.to_name(false));
1003        Self { dict }
1004    }
1005
1006    /// Write the `/RowSpan` attribute to set the number of rows that shall be
1007    /// spanned by this cell.
1008    pub fn row_span(&mut self, row_span: i32) -> &mut Self {
1009        self.dict.pair(Name(b"RowSpan"), row_span);
1010        self
1011    }
1012
1013    /// Write the `/ColSpan` attribute to set the number of columns that shall
1014    /// be spanned by this cell.
1015    pub fn col_span(&mut self, col_span: i32) -> &mut Self {
1016        self.dict.pair(Name(b"ColSpan"), col_span);
1017        self
1018    }
1019
1020    /// Write the `/Headers` attribute to refer to the header cells of the
1021    /// table. PDF 1.6+.
1022    pub fn headers(&mut self) -> TypedArray<'_, Str<'_>> {
1023        self.dict.insert(Name(b"Headers")).array().typed()
1024    }
1025
1026    /// Write the `/Scope` attribute to define whether a table header cell
1027    /// refers to its row or column.
1028    pub fn scope(&mut self, scope: TableHeaderScope) -> &mut Self {
1029        self.dict.pair(Name(b"Scope"), scope.to_name());
1030        self
1031    }
1032
1033    /// Write the `/Summary` attribute to set the summary of the table. PDF
1034    /// 1.7+.
1035    pub fn summary(&mut self, summary: impl TextStrLike) -> &mut Self {
1036        self.dict.pair(Name(b"Summary"), summary);
1037        self
1038    }
1039
1040    /// Write the `/Short` attribute to set a short form of the table header's
1041    /// content. PDF 2.0+.
1042    pub fn short(&mut self, short: impl TextStrLike) -> &mut Self {
1043        self.dict.pair(Name(b"Short"), short);
1044        self
1045    }
1046}
1047
1048deref!('a, TableAttributes<'a> => Dict<'a>, dict);
1049
1050/// The scope of a table header cell.
1051#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
1052pub enum TableHeaderScope {
1053    /// The header cell refers to the row.
1054    Row,
1055    /// The header cell refers to the column.
1056    Column,
1057    /// The header cell refers to both the row and the column.
1058    Both,
1059}
1060
1061impl TableHeaderScope {
1062    pub(crate) fn to_name(self) -> Name<'static> {
1063        match self {
1064            Self::Row => Name(b"Row"),
1065            Self::Column => Name(b"Column"),
1066            Self::Both => Name(b"Both"),
1067        }
1068    }
1069}
1070
1071/// Writer for a _artifact attributes dictionary_. PDF 2.0+
1072///
1073/// This struct is created by [`Attributes::artifact`].
1074pub struct ArtifactAttributes<'a> {
1075    dict: Dict<'a>,
1076}
1077
1078writer!(ArtifactAttributes: |obj| Self::start_with_dict(obj.dict()));
1079
1080impl<'a> ArtifactAttributes<'a> {
1081    pub(crate) fn start_with_dict(mut dict: Dict<'a>) -> Self {
1082        dict.pair(Name(b"O"), AttributeOwner::Artifact.to_name(true));
1083        Self { dict }
1084    }
1085
1086    /// Write the `/Type` attribute to set the type of artifact.
1087    pub fn artifact_type(&mut self, artifact_type: ArtifactType) -> &mut Self {
1088        self.dict.pair(Name(b"Type"), artifact_type.to_name());
1089        self
1090    }
1091
1092    /// Write the `/BBox` attribute to set the bounding box of the artifact.
1093    pub fn bbox(&mut self, bbox: Rect) -> &mut Self {
1094        self.dict.pair(Name(b"BBox"), bbox);
1095        self
1096    }
1097
1098    /// Write the `/Subtype` attribute to set the subtype of pagination or
1099    /// inline artifacts.
1100    pub fn subtype(&mut self, subtype: ArtifactSubtype) -> &mut Self {
1101        self.dict.pair(Name(b"Subtype"), subtype.to_name());
1102        self
1103    }
1104}
1105
1106deref!('a, ArtifactAttributes<'a> => Dict<'a>, dict);
1107
1108/// Writer for a _foot- and endnote attributes dictionary_. PDF/UA-2
1109///
1110/// This struct is created by [`Attributes::note`].
1111pub struct FENoteAttributes<'a> {
1112    dict: Dict<'a>,
1113}
1114
1115writer!(FENoteAttributes: |obj| Self::start_with_dict(obj.dict()));
1116
1117impl<'a> FENoteAttributes<'a> {
1118    pub(crate) fn start_with_dict(mut dict: Dict<'a>) -> Self {
1119        dict.pair(Name(b"O"), AttributeOwner::FENote.to_name(true));
1120        Self { dict }
1121    }
1122
1123    /// Write the `/NoteType` attribute to set the type of note.
1124    pub fn note_type(&mut self, note_type: NoteType) -> &mut Self {
1125        self.dict.pair(Name(b"NoteType"), note_type.to_name());
1126        self
1127    }
1128}
1129
1130deref!('a, FENoteAttributes<'a> => Dict<'a>, dict);
1131
1132/// The type of foot- or endnote.
1133#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
1134pub enum NoteType {
1135    /// A footnote.
1136    Footnote,
1137    /// An endnote.
1138    Endnote,
1139    /// The type is not specified.
1140    None,
1141}
1142
1143impl NoteType {
1144    pub(crate) fn to_name(self) -> Name<'static> {
1145        match self {
1146            Self::Footnote => Name(b"Footnote"),
1147            Self::Endnote => Name(b"Endnote"),
1148            Self::None => Name(b"None"),
1149        }
1150    }
1151}
1152
1153/// Owner of the attribute dictionary.
1154#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
1155pub enum AttributeOwner {
1156    /// General layout attributes.
1157    Layout,
1158    /// List attributes.
1159    List,
1160    /// Attributes governing the print out behavior of form fields. PDF 1.7+.
1161    PrintField,
1162    /// Table attributes.
1163    Table,
1164    /// Attributes covering artifacts. PDF 2.0+.
1165    Artifact,
1166    /// Hints for conversion to XML 1.0.
1167    Xml,
1168    /// Hints for conversion to HTML 3.2.
1169    Html3_2,
1170    /// Hints for conversion to HTML 4.01.
1171    Html4,
1172    /// Hints for conversion to HTML 5.
1173    Html5,
1174    /// Hints for conversion to OEB 1.0.
1175    Oeb,
1176    /// Hints for conversion to RTF 1.05.
1177    Rtf1_05,
1178    /// Hints for conversion to CSS 1.
1179    Css1,
1180    /// Hints for conversion to CSS 2.
1181    Css2,
1182    /// Hints for conversion to CSS 3. PDF 2.0+.
1183    Css3,
1184    /// Hints for converting to a format expressed in RDFa 1.1. PDF 2.0+.
1185    Rdfa1_1,
1186    /// ARIA 1.1 accessibility attributes for assistive technology. PDF 2.0+.
1187    Aria1_1,
1188    /// Attribute governing the interpretation of `FENote` elements. PDF/UA-2.
1189    FENote,
1190    /// The attribute owner is a namespace. PDF 2.0+.
1191    NSO,
1192    /// User-defined attributes. Requires to set the `/UserProperties` attribute
1193    /// of the [`MarkInfo`] dictionary to true. PDF 1.6+
1194    User,
1195}
1196
1197impl AttributeOwner {
1198    /// Convert the attribute owner to a name.
1199    ///
1200    /// The `iso32000_2_2020` parameter is used to determine the name for the
1201    /// CSS 1 and CSS 2 variants. Set it to `true` when writing a PDF 2.0 file
1202    /// conforming to the 2020 edition of the standard or later.
1203    pub(crate) fn to_name(self, iso32000_2_2020: bool) -> Name<'static> {
1204        match self {
1205            Self::Layout => Name(b"Layout"),
1206            Self::List => Name(b"List"),
1207            Self::PrintField => Name(b"PrintField"),
1208            Self::Table => Name(b"Table"),
1209            Self::Artifact => Name(b"Artifact"),
1210            Self::Xml => Name(b"XML-1.00"),
1211            Self::Html3_2 => Name(b"HTML-3.20"),
1212            Self::Html4 => Name(b"HTML-4.01"),
1213            Self::Html5 => Name(b"HTML-5.00"),
1214            Self::Oeb => Name(b"OEB-1.00"),
1215            Self::Rtf1_05 => Name(b"RTF-1.05"),
1216            Self::Css1 if iso32000_2_2020 => Name(b"CSS-1"),
1217            Self::Css2 if iso32000_2_2020 => Name(b"CSS-2"),
1218            Self::Css1 => Name(b"CSS-1.00"),
1219            Self::Css2 => Name(b"CSS-2.00"),
1220            Self::Css3 => Name(b"CSS-3"),
1221            Self::Rdfa1_1 => Name(b"RDFa-1.10"),
1222            Self::Aria1_1 => Name(b"ARIA-1.1"),
1223            Self::FENote => Name(b"FENote"),
1224            Self::NSO => Name(b"NSO"),
1225            Self::User => Name(b"UserProperties"),
1226        }
1227    }
1228}