pdf_writer/
attributes.rs

1use super::*;
2
3/// Writer for an _attribute dictionary_. PDF 1.4+
4///
5/// This struct must set the `/O` attribute by calling any of the methods. This
6/// struct is created by [`ClassMap::single`]. An array of this struct is
7/// created by [`StructElement::attributes`] and [`ClassMap::multiple`].
8pub struct Attributes<'a> {
9    dict: Dict<'a>,
10}
11
12writer!(Attributes: |obj| Self { dict: obj.dict() });
13
14impl<'a> Attributes<'a> {
15    /// Write the `/O` attribute to set the owner.
16    ///
17    /// Should not be called when using any of the other methods.
18    pub fn owner(&mut self, owner: AttributeOwner) -> &mut Self {
19        self.pair(Name(b"O"), owner.to_name());
20        self
21    }
22
23    /// Set the `/O` attribute to user-defined and start writing the `/P` array
24    /// with user properties. PDF 1.6+
25    pub fn user(&mut self) -> TypedArray<'_, UserProperty> {
26        self.pair(Name(b"O"), AttributeOwner::User.to_name());
27        self.insert(Name(b"P")).array().typed()
28    }
29
30    /// Set the `/O` attribute to `Layout` to start writing layout parameters.
31    pub fn layout(self) -> LayoutAttributes<'a> {
32        LayoutAttributes::start_with_dict(self.dict)
33    }
34
35    /// Set the `/O` attribute to `List` to start writing list attributes.
36    pub fn list(self) -> ListAttributes<'a> {
37        ListAttributes::start_with_dict(self.dict)
38    }
39
40    /// Set the `/O` attribute to `PrintField` to start writing attributes for
41    /// the appearance of form fields. PDF 1.6+
42    pub fn field(self) -> FieldAttributes<'a> {
43        FieldAttributes::start_with_dict(self.dict)
44    }
45
46    /// Set the `/O` attribute to `Table` to start writing table attributes.
47    pub fn table(self) -> TableAttributes<'a> {
48        TableAttributes::start_with_dict(self.dict)
49    }
50}
51
52deref!('a, Attributes<'a> => Dict<'a>, dict);
53
54/// Writer for an _user property dictionary_. PDF 1.6+
55///
56/// An array of this struct is created by [`Attributes::user`].
57pub struct UserProperty<'a> {
58    dict: Dict<'a>,
59}
60
61writer!(UserProperty: |obj| Self { dict: obj.dict() });
62
63impl UserProperty<'_> {
64    /// Write the `/N` attribute to set the name of the property.
65    pub fn name(&mut self, name: TextStr) -> &mut Self {
66        self.dict.pair(Name(b"N"), name);
67        self
68    }
69
70    /// Start writing the `/V` attribute to set the value of the property.
71    pub fn value(&mut self) -> Obj<'_> {
72        self.dict.insert(Name(b"V"))
73    }
74
75    /// Write the `/F` attribute to set the format of the property.
76    pub fn format(&mut self, format: TextStr) -> &mut Self {
77        self.dict.pair(Name(b"F"), format);
78        self
79    }
80
81    /// Write the `/H` attribute to determine whether this property is hidden.
82    pub fn hidden(&mut self, hide: bool) -> &mut Self {
83        self.dict.pair(Name(b"H"), hide);
84        self
85    }
86}
87
88deref!('a, UserProperty<'a> => Dict<'a>, dict);
89
90/// Writer for an _layout attributes dictionary_. PDF 1.4+
91///
92/// This struct is created by [`Attributes::layout`].
93pub struct LayoutAttributes<'a> {
94    dict: Dict<'a>,
95}
96
97writer!(LayoutAttributes: |obj| Self::start_with_dict(obj.dict()));
98
99/// General layout attributes.
100impl<'a> LayoutAttributes<'a> {
101    pub(crate) fn start_with_dict(mut dict: Dict<'a>) -> Self {
102        dict.pair(Name(b"O"), AttributeOwner::Layout.to_name());
103        Self { dict }
104    }
105
106    /// Write the `/Placement` attribute.
107    pub fn placement(&mut self, placement: Placement) -> &mut Self {
108        self.dict.pair(Name(b"Placement"), placement.to_name());
109        self
110    }
111
112    /// Write the `/WritingMode` attribute to set the writing direction.
113    pub fn writing_mode(&mut self, mode: WritingMode) -> &mut Self {
114        self.dict.pair(Name(b"WritingMode"), mode.to_name());
115        self
116    }
117
118    /// Write the `/BackgroundColor` attribute to set the background color in
119    /// RGB between `0` and `1`. PDF 1.5+
120    pub fn background_color(&mut self, color: [f32; 3]) -> &mut Self {
121        self.dict
122            .insert(Name(b"BackgroundColor"))
123            .array()
124            .typed()
125            .items(color);
126        self
127    }
128
129    /// Write the `/BorderColor` attribute.
130    pub fn border_color(&mut self, color: [f32; 3]) -> &mut Self {
131        self.dict.insert(Name(b"BorderColor")).array().typed().items(color);
132        self
133    }
134
135    /// Write the `/BorderStyle` attribute.
136    pub fn border_style(&mut self, style: [LayoutBorderStyle; 4]) -> &mut Self {
137        self.dict
138            .insert(Name(b"BorderStyle"))
139            .array()
140            .typed()
141            .items(style.into_iter().map(LayoutBorderStyle::to_name));
142        self
143    }
144
145    /// Write the `/BorderThickness` attribute.
146    pub fn border_thickness(&mut self, thickness: [f32; 4]) -> &mut Self {
147        self.dict
148            .insert(Name(b"BorderThickness"))
149            .array()
150            .typed()
151            .items(thickness);
152        self
153    }
154
155    /// Write the `/Padding` attribute.
156    pub fn padding(&mut self, padding: [f32; 4]) -> &mut Self {
157        self.dict.insert(Name(b"Padding")).array().typed().items(padding);
158        self
159    }
160
161    /// Write the `/Color` attribute.
162    pub fn color(&mut self, color: [f32; 3]) -> &mut Self {
163        self.dict.insert(Name(b"Color")).array().typed().items(color);
164        self
165    }
166}
167
168/// Placement of an element.
169#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
170pub enum Placement {
171    /// Stacked in the block order.
172    Block,
173    /// Stacked in the inline order.
174    Inline,
175    /// Before edge coincides with that of reference area, touching the edge of
176    /// the previous block.
177    Before,
178    /// Start edge coincides with that of reference area, aligned on the
179    /// non-stacking axis of the reference area.
180    Start,
181    /// End edge coincides with that of reference area, aligned on the
182    /// non-stacking axis of the reference area.
183    End,
184}
185
186impl Placement {
187    pub(crate) fn to_name(self) -> Name<'static> {
188        match self {
189            Self::Block => Name(b"Block"),
190            Self::Inline => Name(b"Inline"),
191            Self::Before => Name(b"Before"),
192            Self::Start => Name(b"Start"),
193            Self::End => Name(b"End"),
194        }
195    }
196}
197
198/// Writing direction.
199#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
200pub enum WritingMode {
201    /// Horizontal writing mode, left-to-right.
202    LtrTtb,
203    /// Horizontal writing mode, right-to-left.
204    RtlTtb,
205    /// Vertical writing mode, right-to-left.
206    TtbRtl,
207}
208
209impl WritingMode {
210    pub(crate) fn to_name(self) -> Name<'static> {
211        match self {
212            Self::LtrTtb => Name(b"LrTb"),
213            Self::RtlTtb => Name(b"RlTb"),
214            Self::TtbRtl => Name(b"TbRl"),
215        }
216    }
217}
218
219/// Layout border style.
220#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
221pub enum LayoutBorderStyle {
222    /// No border.
223    None,
224    /// Hidden border.
225    Hidden,
226    /// Solid border.
227    Solid,
228    /// Dashed border.
229    Dashed,
230    /// Dotted border.
231    Dotted,
232    /// Double border.
233    Double,
234    /// Groove border.
235    Groove,
236    /// Ridge border.
237    Ridge,
238    /// Inset border.
239    Inset,
240    /// Outset border.
241    Outset,
242}
243
244impl LayoutBorderStyle {
245    pub(crate) fn to_name(self) -> Name<'static> {
246        match self {
247            Self::None => Name(b"None"),
248            Self::Hidden => Name(b"Hidden"),
249            Self::Solid => Name(b"Solid"),
250            Self::Dashed => Name(b"Dashed"),
251            Self::Dotted => Name(b"Dotted"),
252            Self::Double => Name(b"Double"),
253            Self::Groove => Name(b"Groove"),
254            Self::Ridge => Name(b"Ridge"),
255            Self::Inset => Name(b"Inset"),
256            Self::Outset => Name(b"Outset"),
257        }
258    }
259}
260
261/// Block level elements.
262impl LayoutAttributes<'_> {
263    /// Write the `/SpaceBefore` attribute.
264    pub fn space_before(&mut self, space: f32) -> &mut Self {
265        self.dict.pair(Name(b"SpaceBefore"), space);
266        self
267    }
268
269    /// Write the `/SpaceAfter` attribute.
270    pub fn space_after(&mut self, space: f32) -> &mut Self {
271        self.dict.pair(Name(b"SpaceAfter"), space);
272        self
273    }
274
275    /// Write the `/StartIndent` attribute.
276    pub fn start_indent(&mut self, indent: f32) -> &mut Self {
277        self.dict.pair(Name(b"StartIndent"), indent);
278        self
279    }
280
281    /// Write the `/EndIndent` attribute.
282    pub fn end_indent(&mut self, indent: f32) -> &mut Self {
283        self.dict.pair(Name(b"EndIndent"), indent);
284        self
285    }
286
287    /// Write the `/TextIndent` attribute.
288    pub fn text_indent(&mut self, indent: f32) -> &mut Self {
289        self.dict.pair(Name(b"TextIndent"), indent);
290        self
291    }
292
293    /// Write the `/TextAlign` attribute.
294    pub fn text_align(&mut self, align: TextAlign) -> &mut Self {
295        self.dict.pair(Name(b"TextAlign"), align.to_name());
296        self
297    }
298
299    /// Write the `/Width` attribute for table row groups and illustrative
300    /// elements. No intrinsic height will be assumed if left empty.
301    pub fn width(&mut self, width: f32) -> &mut Self {
302        self.dict.pair(Name(b"Width"), width);
303        self
304    }
305
306    /// Write the `/Height` attribute for table row groups and illustrative
307    /// elements. No intrinsic height will be assumed if left empty.
308    pub fn height(&mut self, height: f32) -> &mut Self {
309        self.dict.pair(Name(b"Height"), height);
310        self
311    }
312}
313
314/// The text alignment.
315#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
316pub enum TextAlign {
317    /// At the start of the inline advance direction.
318    Start,
319    /// Centered.
320    Center,
321    /// At the end of the inline advance direction.
322    End,
323    /// Justified.
324    Justify,
325}
326
327impl TextAlign {
328    pub(crate) fn to_name(self) -> Name<'static> {
329        match self {
330            Self::Start => Name(b"Start"),
331            Self::Center => Name(b"Center"),
332            Self::End => Name(b"End"),
333            Self::Justify => Name(b"Justify"),
334        }
335    }
336}
337
338/// Illustration elements.
339impl LayoutAttributes<'_> {
340    /// Write the `/BBox` attribute.
341    pub fn bbox(&mut self, bbox: Rect) -> &mut Self {
342        self.dict.pair(Name(b"BBox"), bbox);
343        self
344    }
345}
346
347/// Table header and data.
348impl LayoutAttributes<'_> {
349    /// Write the `/BlockAlign` attribute.
350    pub fn block_align(&mut self, align: BlockAlign) -> &mut Self {
351        self.dict.pair(Name(b"BlockAlign"), align.to_name());
352        self
353    }
354
355    /// Write the `/InlineAlign` attribute.
356    pub fn inline_align(&mut self, align: InlineAlign) -> &mut Self {
357        self.dict.pair(Name(b"InlineAlign"), align.to_name());
358        self
359    }
360
361    /// Write the `/TBorderStyle` attribute. PDF 1.5+.
362    pub fn table_border_style(&mut self, style: [LayoutBorderStyle; 4]) -> &mut Self {
363        self.dict
364            .insert(Name(b"TBorderStyle"))
365            .array()
366            .typed()
367            .items(style.into_iter().map(LayoutBorderStyle::to_name));
368        self
369    }
370
371    /// Write the `/TPadding` attribute. PDF 1.5+.
372    pub fn table_padding(&mut self, padding: f32) -> &mut Self {
373        self.dict.pair(Name(b"TPadding"), padding);
374        self
375    }
376}
377
378/// The block alignment.
379#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
380pub enum BlockAlign {
381    /// At the start of the block advance direction.
382    Begin,
383    /// Centered.
384    Middle,
385    /// At the end of the block advance direction.
386    After,
387    /// Justified.
388    Justify,
389}
390
391impl BlockAlign {
392    pub(crate) fn to_name(self) -> Name<'static> {
393        match self {
394            Self::Begin => Name(b"Begin"),
395            Self::Middle => Name(b"Middle"),
396            Self::After => Name(b"After"),
397            Self::Justify => Name(b"Justify"),
398        }
399    }
400}
401
402/// Grouping elements.
403impl LayoutAttributes<'_> {
404    /// Write the `/ColumnCount` attribute. PDF 1.6+.
405    pub fn column_count(&mut self, count: i32) -> &mut Self {
406        self.dict.pair(Name(b"ColumnCount"), count);
407        self
408    }
409
410    /// Start writing the `/ColumnWidths` array. The last number in the array is
411    /// used for all extra columns. PDF 1.6+.
412    pub fn column_widths(&mut self) -> TypedArray<'_, f32> {
413        self.dict.insert(Name(b"ColumnWidths")).array().typed()
414    }
415
416    /// Start writing the `/ColumnGap` array. The last number in the array is used
417    /// for all extra columns. PDF 1.6+.
418    pub fn column_gap(&mut self) -> TypedArray<'_, f32> {
419        self.dict.insert(Name(b"ColumnGap")).array().typed()
420    }
421}
422
423/// The inline alignment.
424#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
425pub enum InlineAlign {
426    /// At the start of the inline advance direction.
427    Start,
428    /// Centered.
429    Center,
430    /// At the end of the inline advance direction.
431    End,
432}
433
434impl InlineAlign {
435    pub(crate) fn to_name(self) -> Name<'static> {
436        match self {
437            Self::Start => Name(b"Start"),
438            Self::Center => Name(b"Center"),
439            Self::End => Name(b"End"),
440        }
441    }
442}
443
444/// Inline elements.
445impl LayoutAttributes<'_> {
446    /// Write the `/LineHeight` attribute.
447    pub fn line_height(&mut self, height: LineHeight) -> &mut Self {
448        height.write(self.dict.insert(Name(b"LineHeight")));
449        self
450    }
451
452    /// Write the `/BaselineShift` attribute.
453    pub fn baseline_shift(&mut self, shift: f32) -> &mut Self {
454        self.dict.pair(Name(b"BaselineShift"), shift);
455        self
456    }
457
458    /// Write the `/TextDecorationType` attribute. PDF 1.5+.
459    pub fn text_decoration_type(&mut self, decoration: TextDecorationType) -> &mut Self {
460        self.dict.pair(Name(b"TextDecorationType"), decoration.to_name());
461        self
462    }
463
464    /// Write the `/TextDecorationColor` attribute in RGB. PDF 1.5+.
465    pub fn text_decoration_color(&mut self, color: [f32; 3]) -> &mut Self {
466        self.dict
467            .insert(Name(b"TextDecorationColor"))
468            .array()
469            .typed()
470            .items(color);
471        self
472    }
473
474    /// Write the `/TextDecorationThickness` attribute. PDF 1.5+.
475    pub fn text_decoration_thickness(&mut self, thickness: f32) -> &mut Self {
476        self.dict.pair(Name(b"TextDecorationThickness"), thickness);
477        self
478    }
479}
480
481/// The height of a line.
482#[derive(Debug, Copy, Clone, PartialEq)]
483pub enum LineHeight {
484    /// Adjust the line height automatically, taking `/BaselineShift` into
485    /// account.
486    Normal,
487    /// Adjust the line height automatically.
488    Auto,
489    /// Set a fixed line height.
490    Custom(f32),
491}
492
493impl LineHeight {
494    pub(crate) fn write(self, obj: Obj) {
495        match self {
496            Self::Normal => obj.primitive(Name(b"Normal")),
497            Self::Auto => obj.primitive(Name(b"Auto")),
498            Self::Custom(height) => obj.primitive(height),
499        }
500    }
501}
502
503/// The text decoration type (over- and underlines).
504#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
505pub enum TextDecorationType {
506    /// No decoration.
507    None,
508    /// Underlined.
509    Underline,
510    /// Line over the text.
511    Overline,
512    /// Strike the text.
513    LineThrough,
514}
515
516impl TextDecorationType {
517    pub(crate) fn to_name(self) -> Name<'static> {
518        match self {
519            Self::None => Name(b"None"),
520            Self::Underline => Name(b"Underline"),
521            Self::Overline => Name(b"Overline"),
522            Self::LineThrough => Name(b"LineThrough"),
523        }
524    }
525}
526
527/// Vertical Text.
528impl LayoutAttributes<'_> {
529    /// Write the `/GlyphOrientationVertical` attribute as an angle between -90
530    /// and 360 in multiples of 90. PDF 1.5+.
531    pub fn glyph_orientation_vertical(&mut self, angle: f32) -> &mut Self {
532        self.dict.pair(Name(b"GlyphOrientationVertical"), angle);
533        self
534    }
535}
536
537/// Ruby annotations.
538impl LayoutAttributes<'_> {
539    /// Write the `/RubyAlign` attribute. PDF 1.5+.
540    pub fn ruby_align(&mut self, align: RubyAlign) -> &mut Self {
541        self.dict.pair(Name(b"RubyAlign"), align.to_name());
542        self
543    }
544
545    /// Write the `/RubyPosition` attribute. PDF 1.5+.
546    pub fn ruby_position(&mut self, position: RubyPosition) -> &mut Self {
547        self.dict.pair(Name(b"RubyPosition"), position.to_name());
548        self
549    }
550}
551
552/// The alignment of a ruby annotation.
553#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
554pub enum RubyAlign {
555    /// At the start of the inline advance direction.
556    Start,
557    /// Centered.
558    Center,
559    /// At the end of the inline advance direction.
560    End,
561    /// Justified.
562    Justify,
563    /// Distribute along the full width of the line with additional space.
564    Distribute,
565}
566
567impl RubyAlign {
568    pub(crate) fn to_name(self) -> Name<'static> {
569        match self {
570            Self::Start => Name(b"Start"),
571            Self::Center => Name(b"Center"),
572            Self::End => Name(b"End"),
573            Self::Justify => Name(b"Justify"),
574            Self::Distribute => Name(b"Distribute"),
575        }
576    }
577}
578
579/// The position of a ruby annotation.
580#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
581pub enum RubyPosition {
582    /// Before edge of the element.
583    Before,
584    /// After edge of the element.
585    After,
586    /// Render as a Warichu.
587    Warichu,
588    /// Render in-line.
589    Inline,
590}
591
592impl RubyPosition {
593    pub(crate) fn to_name(self) -> Name<'static> {
594        match self {
595            Self::Before => Name(b"Before"),
596            Self::After => Name(b"After"),
597            Self::Warichu => Name(b"Warichu"),
598            Self::Inline => Name(b"Inline"),
599        }
600    }
601}
602
603deref!('a, LayoutAttributes<'a> => Dict<'a>, dict);
604
605/// Writer for an _list attributes dictionary_. PDF 1.4+
606///
607/// This struct is created by [`Attributes::list`].
608pub struct ListAttributes<'a> {
609    dict: Dict<'a>,
610}
611
612writer!(ListAttributes: |obj| Self::start_with_dict(obj.dict()));
613
614impl<'a> ListAttributes<'a> {
615    pub(crate) fn start_with_dict(mut dict: Dict<'a>) -> Self {
616        dict.pair(Name(b"O"), AttributeOwner::List.to_name());
617        Self { dict }
618    }
619
620    /// Write the `/ListNumbering` attribute.
621    pub fn list_numbering(&mut self, numbering: ListNumbering) -> &mut Self {
622        self.dict.pair(Name(b"ListNumbering"), numbering.to_name());
623        self
624    }
625}
626
627deref!('a, ListAttributes<'a> => Dict<'a>, dict);
628
629/// The list numbering type.
630#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
631pub enum ListNumbering {
632    /// No numbering.
633    None,
634    /// Solid circular bullets.
635    Disc,
636    /// Open circular bullets.
637    Circle,
638    /// Solid square bullets.
639    Square,
640    /// Decimal numbers.
641    Decimal,
642    /// Lowercase Roman numerals.
643    LowerRoman,
644    /// Uppercase Roman numerals.
645    UpperRoman,
646    /// Lowercase letters.
647    LowerAlpha,
648    /// Uppercase letters.
649    UpperAlpha,
650}
651
652impl ListNumbering {
653    pub(crate) fn to_name(self) -> Name<'static> {
654        match self {
655            Self::None => Name(b"None"),
656            Self::Disc => Name(b"Disc"),
657            Self::Circle => Name(b"Circle"),
658            Self::Square => Name(b"Square"),
659            Self::Decimal => Name(b"Decimal"),
660            Self::LowerRoman => Name(b"LowerRoman"),
661            Self::UpperRoman => Name(b"UpperRoman"),
662            Self::LowerAlpha => Name(b"LowerAlpha"),
663            Self::UpperAlpha => Name(b"UpperAlpha"),
664        }
665    }
666}
667
668/// Writer for an _PrintField attributes dictionary_. PDF 1.6+
669///
670/// This struct is created by [`Attributes::field`].
671pub struct FieldAttributes<'a> {
672    dict: Dict<'a>,
673}
674
675writer!(FieldAttributes: |obj| Self::start_with_dict(obj.dict()));
676
677impl<'a> FieldAttributes<'a> {
678    pub(crate) fn start_with_dict(mut dict: Dict<'a>) -> Self {
679        dict.pair(Name(b"O"), AttributeOwner::PrintField.to_name());
680        Self { dict }
681    }
682
683    /// Write the `/Role` attribute to determine the kind of form control.
684    pub fn role(&mut self, role: FieldRole) -> &mut Self {
685        self.dict.pair(Name(b"Role"), role.to_name());
686        self
687    }
688
689    /// Write the `/checked` attribute to set whether a radio button or checkbox
690    /// is checked.
691    pub fn checked(&mut self, checked: FieldState) -> &mut Self {
692        self.dict.pair(Name(b"checked"), checked.to_name());
693        self
694    }
695
696    /// Write the `/Desc` attribute to set the description of the form control.
697    pub fn description(&mut self, desc: TextStr) -> &mut Self {
698        self.dict.pair(Name(b"Desc"), desc);
699        self
700    }
701}
702
703deref!('a, FieldAttributes<'a> => Dict<'a>, dict);
704
705/// The kind of form control.
706#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
707pub enum FieldRole {
708    /// A button.
709    Button,
710    /// A checkbox.
711    CheckBox,
712    /// A radio button.
713    RadioButton,
714    /// A text field.
715    TextField,
716}
717
718impl FieldRole {
719    pub(crate) fn to_name(self) -> Name<'static> {
720        match self {
721            Self::Button => Name(b"pb"),
722            Self::CheckBox => Name(b"cb"),
723            Self::RadioButton => Name(b"rb"),
724            Self::TextField => Name(b"tv"),
725        }
726    }
727}
728
729/// Whether a check box or radio button is checked.
730#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
731pub enum FieldState {
732    /// The check box or radio button is unchecked.
733    Unchecked,
734    /// The check box or radio button is checked.
735    Checked,
736    /// The check box or radio button is in a quantum superstate.
737    Neutral,
738}
739
740impl FieldState {
741    pub(crate) fn to_name(self) -> Name<'static> {
742        match self {
743            Self::Unchecked => Name(b"off"),
744            Self::Checked => Name(b"on"),
745            Self::Neutral => Name(b"neutral"),
746        }
747    }
748}
749
750/// Writer for a _table attributes dictionary_. PDF 1.4+
751///
752/// This struct is created by [`Attributes::table`].
753pub struct TableAttributes<'a> {
754    dict: Dict<'a>,
755}
756
757writer!(TableAttributes: |obj| Self::start_with_dict(obj.dict()));
758
759impl<'a> TableAttributes<'a> {
760    pub(crate) fn start_with_dict(mut dict: Dict<'a>) -> Self {
761        dict.pair(Name(b"O"), AttributeOwner::Table.to_name());
762        Self { dict }
763    }
764
765    /// Write the `/RowSpan` attribute to set the number of rows that shall be
766    /// spanned by this cell.
767    pub fn row_span(&mut self, row_span: i32) -> &mut Self {
768        self.dict.pair(Name(b"RowSpan"), row_span);
769        self
770    }
771
772    /// Write the `/ColSpan` attribute to set the number of columns that shall
773    /// be spanned by this cell.
774    pub fn col_span(&mut self, col_span: i32) -> &mut Self {
775        self.dict.pair(Name(b"ColSpan"), col_span);
776        self
777    }
778
779    /// Write the `/Headers` attribute to refer to the header cells of the
780    /// table. PDF 1.6+.
781    pub fn headers(&mut self) -> TypedArray<'_, Str> {
782        self.dict.insert(Name(b"Headers")).array().typed()
783    }
784
785    /// Write the `/Scope` attribute to define whether a table header cell
786    /// refers to its row or column.
787    pub fn scope(&mut self, scope: TableHeaderScope) -> &mut Self {
788        self.dict.pair(Name(b"Scope"), scope.to_name());
789        self
790    }
791
792    /// Write the `/Summary` attribute to set the summary of the table. PDF
793    /// 1.7+.
794    pub fn summary(&mut self, summary: TextStr) -> &mut Self {
795        self.dict.pair(Name(b"Summary"), summary);
796        self
797    }
798}
799
800deref!('a, TableAttributes<'a> => Dict<'a>, dict);
801
802/// The scope of a table header cell.
803#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
804pub enum TableHeaderScope {
805    /// The header cell refers to the row.
806    Row,
807    /// The header cell refers to the column.
808    Column,
809    /// The header cell refers to both the row and the column.
810    Both,
811}
812
813impl TableHeaderScope {
814    pub(crate) fn to_name(self) -> Name<'static> {
815        match self {
816            Self::Row => Name(b"Row"),
817            Self::Column => Name(b"Column"),
818            Self::Both => Name(b"Both"),
819        }
820    }
821}
822
823/// Owner of the attribute dictionary.
824#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
825pub enum AttributeOwner {
826    /// General layout attributes.
827    Layout,
828    /// List attributes.
829    List,
830    /// Attributes governing the print out behavior of form fields. PDF 1.7+.
831    PrintField,
832    /// Table attributes.
833    Table,
834    /// Hints for conversion to XML 1.0.
835    Xml,
836    /// Hints for conversion to HTML 3.2.
837    Html3_2,
838    /// Hints for conversion to HTML 4.01.
839    Html4,
840    /// Hints for conversion to OEB 1.0.
841    Oeb,
842    /// Hints for conversion to RTF 1.05.
843    Rtf1_05,
844    /// Hints for conversion to CSS 1.
845    Css1,
846    /// Hints for conversion to CSS 2.
847    Css2,
848    /// User-defined attributes. Requires to set the `/UserProperties` attribute
849    /// of the [`MarkInfo`] dictionary to true. PDF 1.6+
850    User,
851}
852
853impl AttributeOwner {
854    pub(crate) fn to_name(self) -> Name<'static> {
855        match self {
856            Self::Layout => Name(b"Layout"),
857            Self::List => Name(b"List"),
858            Self::PrintField => Name(b"PrintField"),
859            Self::Table => Name(b"Table"),
860            Self::Xml => Name(b"XML-1.00"),
861            Self::Html3_2 => Name(b"HTML-3.20"),
862            Self::Html4 => Name(b"HTML-4.01"),
863            Self::Oeb => Name(b"OEB-1.00"),
864            Self::Rtf1_05 => Name(b"RTF-1.05"),
865            Self::Css1 => Name(b"CSS-1.00"),
866            Self::Css2 => Name(b"CSS-2.00"),
867            Self::User => Name(b"UserDefined"),
868        }
869    }
870}