pdf_writer/annotations.rs
1use super::*;
2
3/// Writer for an _annotation dictionary_.
4///
5/// An array of this struct is created by [`Chunk::annotation`].
6pub struct Annotation<'a> {
7 pub(crate) dict: Dict<'a>,
8}
9
10writer!(Annotation: |obj| {
11 let mut dict = obj.dict();
12 dict.pair(Name(b"Type"), Name(b"Annot"));
13 Self { dict }
14});
15
16impl Annotation<'_> {
17 /// Write the `/Subtype` attribute to tell the viewer the type of this
18 /// particular annotation.
19 pub fn subtype(&mut self, kind: AnnotationType) -> &mut Self {
20 self.pair(Name(b"Subtype"), kind.to_name());
21 self
22 }
23
24 /// Write the `/Rect` attribute. This is the location and dimensions of the
25 /// annotation on the page.
26 pub fn rect(&mut self, rect: Rect) -> &mut Self {
27 self.pair(Name(b"Rect"), rect);
28 self
29 }
30
31 /// Write the `/Contents` attribute. This is the content or alt-text,
32 /// depending on the [`AnnotationType`].
33 pub fn contents(&mut self, text: TextStr) -> &mut Self {
34 self.pair(Name(b"Contents"), text);
35 self
36 }
37
38 /// Write the `/P` attribute. This provides an indirect reference to the
39 /// page object with which this annotation is associated. Required for the
40 /// subtype `Screen` associated with rendition actions. PDF 1.3+.
41 pub fn page(&mut self, id: Ref) -> &mut Self {
42 self.pair(Name(b"P"), id);
43 self
44 }
45
46 /// Write the `/NM` attribute. This uniquely identifies the annotation on the
47 /// page. PDF 1.3+.
48 pub fn name(&mut self, text: TextStr) -> &mut Self {
49 self.pair(Name(b"NM"), text);
50 self
51 }
52
53 /// Write the `/M` attribute, specifying the date the annotation was last
54 /// modified. PDF 1.1+.
55 pub fn modified(&mut self, date: Date) -> &mut Self {
56 self.pair(Name(b"M"), date);
57 self
58 }
59
60 /// Write the `/F` attribute.
61 ///
62 /// Required for all annotations in PDF/A except for `Popup`.
63 pub fn flags(&mut self, flags: AnnotationFlags) -> &mut Self {
64 self.pair(Name(b"F"), flags.bits() as i32);
65 self
66 }
67
68 /// Start writing the `/AP` dictionary to set how the annotation shall
69 /// be presented visually. If this dictionary contains sub dictionaries,
70 /// [`Self::appearance_state`] must be set. PDF 1.2+.
71 ///
72 /// Required for many annotations in PDF/A.
73 pub fn appearance(&mut self) -> Appearance<'_> {
74 self.insert(Name(b"AP")).start()
75 }
76
77 /// Write the `/AS` attribute to set the annotation's current appearance
78 /// state from the `/AP` subdictionary. Must be set if [`Self::appearance`]
79 /// has one or more subdictionaries. PDF 1.2+.
80 pub fn appearance_state(&mut self, name: Name) -> &mut Self {
81 self.pair(Name(b"AS"), name);
82 self
83 }
84
85 /// Write the `/Border` attribute. This describes the look of the border
86 /// around the annotation, including width and horizontal and vertical
87 /// border radii. The function may also receive a dash pattern which
88 /// specifies the lengths and gaps of the border segments on a dashed
89 /// border. Although all PDF versions accept `/Border`, this feature
90 /// specifically is only available in PDF 1.1 or later.
91 pub fn border(
92 &mut self,
93 h_radius: f32,
94 v_radius: f32,
95 width: f32,
96 dash_pattern: Option<&[f32]>,
97 ) -> &mut Self {
98 let mut array = self.insert(Name(b"Border")).array();
99 array.item(h_radius);
100 array.item(v_radius);
101 array.item(width);
102
103 if let Some(pattern) = dash_pattern {
104 array.push().array().items(pattern);
105 }
106
107 array.finish();
108 self
109 }
110
111 /// Start writing the `/BS` attribute. These are some more elaborate border
112 /// settings taking precedence over `/B` for some annotation types. PDF 1.2+.
113 pub fn border_style(&mut self) -> BorderStyle<'_> {
114 self.insert(Name(b"BS")).start()
115 }
116
117 /// Write the `/C` attribute forcing a transparent color. This sets the
118 /// annotations background color and its popup title bar color. PDF 1.1+.
119 pub fn color_transparent(&mut self) -> &mut Self {
120 self.insert(Name(b"C")).array();
121 self
122 }
123
124 /// Write the `/C` attribute using a grayscale color. This sets the
125 /// annotations background color and its popup title bar color. PDF 1.1+.
126 pub fn color_gray(&mut self, gray: f32) -> &mut Self {
127 self.insert(Name(b"C")).array().item(gray);
128 self
129 }
130
131 /// Write the `/C` attribute using an RGB color. This sets the annotations
132 /// background color and its popup title bar color. PDF 1.1+.
133 pub fn color_rgb(&mut self, r: f32, g: f32, b: f32) -> &mut Self {
134 self.insert(Name(b"C")).array().items([r, g, b]);
135 self
136 }
137
138 /// Write the `/C` attribute using a CMYK color. This sets the annotations
139 /// background color and its popup title bar color. PDF 1.1+.
140 pub fn color_cmyk(&mut self, c: f32, m: f32, y: f32, k: f32) -> &mut Self {
141 self.insert(Name(b"C")).array().items([c, m, y, k]);
142 self
143 }
144
145 /// Write the `/StructParent` attribute to indicate the [structure tree
146 /// element][StructElement] this annotation belongs to. PDF 1.3+.
147 pub fn struct_parent(&mut self, key: i32) -> &mut Self {
148 self.pair(Name(b"StructParent"), key);
149 self
150 }
151
152 /// Start writing the `/A` dictionary. Only permissible for the subtypes
153 /// `Link` and `Widget`.
154 ///
155 /// Note that this attribute is forbidden in PDF/A.
156 pub fn action(&mut self) -> Action<'_> {
157 self.insert(Name(b"A")).start()
158 }
159
160 /// Start writing the `/AA` dictionary. Only permissible for the subtype
161 /// `Widget`. PDF 1.3+.
162 ///
163 /// Note that this attribute is forbidden in PDF/A.
164 pub fn additional_actions(&mut self) -> AdditionalActions<'_> {
165 self.insert(Name(b"AA")).start()
166 }
167
168 /// Write the `/H` attribute to set what effect is used to convey that the
169 /// user is pressing a link or widget annotation. Only permissible for the
170 /// subtypes `Link` and `Widget`. PDF 1.2+.
171 pub fn highlight(&mut self, effect: HighlightEffect) -> &mut Self {
172 self.pair(Name(b"H"), effect.to_name());
173 self
174 }
175
176 /// Write the `/T` attribute. This is in the title bar of markup annotations
177 /// and should be the name of the annotation author. PDF 1.1+.
178 pub fn author(&mut self, text: TextStr) -> &mut Self {
179 self.pair(Name(b"T"), text);
180 self
181 }
182
183 /// Write the `/Subj` attribute. This is the subject of the annotation.
184 /// PDF 1.5+.
185 pub fn subject(&mut self, text: TextStr) -> &mut Self {
186 self.pair(Name(b"Subj"), text);
187 self
188 }
189
190 /// Write the `/QuadPoints` attribute, specifying the region in which the
191 /// link should be activated. PDF 1.6+.
192 pub fn quad_points(
193 &mut self,
194 coordinates: impl IntoIterator<Item = f32>,
195 ) -> &mut Self {
196 self.insert(Name(b"QuadPoints")).array().items(coordinates);
197 self
198 }
199
200 /// Write the `/L` attribute. This defines the start and end point of a
201 /// line annotation
202 pub fn line_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32) -> &mut Self {
203 self.insert(Name(b"L")).array().items([x1, y1, x2, y2]);
204 self
205 }
206
207 /// Start writing the `/FS` attribute, setting which file to reference.
208 pub fn file_spec(&mut self) -> FileSpec<'_> {
209 self.insert(Name(b"FS")).start()
210 }
211
212 /// Write the `/Name` attribute. Refer to the specification to see which
213 /// names are allowed for which annotation types.
214 pub fn icon(&mut self, icon: AnnotationIcon) -> &mut Self {
215 self.pair(Name(b"Name"), icon.to_name());
216 self
217 }
218
219 /// Start writing the `/MK` dictionary. Only permissible for the subtype
220 /// `Widget`.
221 pub fn appearance_characteristics(&mut self) -> AppearanceCharacteristics<'_> {
222 self.insert(Name(b"MK")).start()
223 }
224
225 /// Write the `/Parent` attribute. Only permissible for the subtype
226 /// `Widget`.
227 pub fn parent(&mut self, id: Ref) -> &mut Self {
228 self.pair(Name(b"Parent"), id);
229 self
230 }
231
232 /// Start writing the `/AF` array to specify the associated files of the
233 /// annotation. PDF 2.0+ or PDF/A-3.
234 pub fn associated_files(&mut self) -> TypedArray<'_, FileSpec> {
235 self.insert(Name(b"AF")).array().typed()
236 }
237}
238
239deref!('a, Annotation<'a> => Dict<'a>, dict);
240
241/// Kind of the annotation to produce.
242#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
243pub enum AnnotationType {
244 /// Inline text.
245 Text,
246 /// A link.
247 Link,
248 /// A line. PDF 1.3+.
249 Line,
250 /// A square. PDF 1.3+.
251 Square,
252 /// A circle. PDF 1.3+.
253 Circle,
254 /// Highlighting the text on the page. PDF 1.3+.
255 Highlight,
256 /// Underline the text on the page. PDF 1.3+.
257 Underline,
258 /// Squiggly underline of the text on the page. PDF 1.4+.
259 Squiggly,
260 /// Strike out the text on the page. PDF 1.3+.
261 StrikeOut,
262 /// A reference to another file. PDF 1.3+.
263 ///
264 /// Note that this annotation type is forbidden in PDF/A-1 and restricted in
265 /// other PDF/A parts.
266 FileAttachment,
267 /// A widget annotation. PDF 1.2+.
268 Widget,
269 /// A screen annotation. PDF 1.5+.
270 ///
271 /// Note that this annotation type is forbidden in PDF/A.
272 Screen,
273}
274
275impl AnnotationType {
276 pub(crate) fn to_name(self) -> Name<'static> {
277 match self {
278 Self::Text => Name(b"Text"),
279 Self::Link => Name(b"Link"),
280 Self::Line => Name(b"Line"),
281 Self::Square => Name(b"Square"),
282 Self::Circle => Name(b"Circle"),
283 Self::Highlight => Name(b"Highlight"),
284 Self::Underline => Name(b"Underline"),
285 Self::Squiggly => Name(b"Squiggly"),
286 Self::StrikeOut => Name(b"StrikeOut"),
287 Self::FileAttachment => Name(b"FileAttachment"),
288 Self::Widget => Name(b"Widget"),
289 Self::Screen => Name(b"Screen"),
290 }
291 }
292}
293
294/// Possible icons for an annotation.
295#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
296pub enum AnnotationIcon<'a> {
297 /// Speech bubble. For use with text annotations.
298 Comment,
299 /// For use with text annotations.
300 Key,
301 /// Sticky note. For use with text annotations.
302 Note,
303 /// Question mark or manual. For use with text annotations.
304 Help,
305 /// For use with text annotations.
306 NewParagraph,
307 /// For use with text annotations.
308 Paragraph,
309 /// A plus or similar. For use with text annotations.
310 Insert,
311 /// Chart. For use with file attachment annotations.
312 Graph,
313 /// For use with file attachment annotations.
314 PushPin,
315 /// For use with file attachment annotations.
316 Paperclip,
317 /// For use with file attachment annotations.
318 Tag,
319 /// A custom icon name.
320 Custom(Name<'a>),
321}
322
323impl<'a> AnnotationIcon<'a> {
324 pub(crate) fn to_name(self) -> Name<'a> {
325 match self {
326 Self::Comment => Name(b"Comment"),
327 Self::Key => Name(b"Key"),
328 Self::Note => Name(b"Note"),
329 Self::Help => Name(b"Help"),
330 Self::NewParagraph => Name(b"NewParagraph"),
331 Self::Paragraph => Name(b"Paragraph"),
332 Self::Insert => Name(b"Insert"),
333 Self::Graph => Name(b"Graph"),
334 Self::PushPin => Name(b"PushPin"),
335 Self::Paperclip => Name(b"Paperclip"),
336 Self::Tag => Name(b"Tag"),
337 Self::Custom(name) => name,
338 }
339 }
340}
341
342bitflags::bitflags! {
343 /// Bitflags describing various characteristics of annotations.
344 pub struct AnnotationFlags: u32 {
345 /// This will hide the annotation if the viewer does not recognize its
346 /// subtype. Otherwise, it will be rendered as specified in its appearance
347 /// stream.
348 ///
349 /// Must not be set for PDF/A.
350 const INVISIBLE = 1 << 0;
351 /// This hides the annotation from view and disallows interaction. PDF 1.2+.
352 ///
353 /// Must not be set for PDF/A.
354 const HIDDEN = 1 << 1;
355 /// Print the annotation. If not set, it will be always hidden on print.
356 /// PDF 1.2+.
357 ///
358 /// Must be set for PDF/A.
359 const PRINT = 1 << 2;
360 /// Do not zoom the annotation appearance if the document is zoomed in.
361 /// PDF 1.3+.
362 ///
363 /// Must be set for text annotations in PDF/A.
364 const NO_ZOOM = 1 << 3;
365 /// Do not rotate the annotation appearance if the document is zoomed in.
366 /// PDF 1.3+.
367 ///
368 /// Must be set for text annotations in PDF/A.
369 const NO_ROTATE = 1 << 4;
370 /// Do not view the annotation on screen. It may still show on print.
371 /// PDF 1.3+.
372 ///
373 /// Must not be set for PDF/A.
374 const NO_VIEW = 1 << 5;
375 /// Do not allow interactions. PDF 1.3+.
376 const READ_ONLY = 1 << 6;
377 /// Do not allow the user to delete or reposition the annotation. Contents
378 /// may still be changed. PDF 1.4+.
379 const LOCKED = 1 << 7;
380 /// Invert the interpretation of the `no_view` flag for certain events.
381 /// PDF 1.5+.
382 ///
383 /// Must not be set for PDF/A.
384 const TOGGLE_NO_VIEW = 1 << 8;
385 /// Do not allow content changes. PDF 1.7+.
386 const LOCKED_CONTENTS = 1 << 9;
387 }
388}
389
390/// Writer for an _appearance characteristics dictionary_.
391///
392/// This struct is created by [`Annotation::appearance_characteristics`].
393pub struct AppearanceCharacteristics<'a> {
394 dict: Dict<'a>,
395}
396
397writer!(AppearanceCharacteristics: |obj| Self { dict: obj.dict() });
398
399impl AppearanceCharacteristics<'_> {
400 /// Write the `/R` attribute. This is the number of degrees the widget
401 /// annotation should be rotated by counterclockwise relative to its page
402 /// when displayed. This should be a multiple of 90.
403 pub fn rotate(&mut self, degrees: i32) -> &mut Self {
404 self.pair(Name(b"R"), degrees);
405 self
406 }
407
408 /// Write the `/BC` attribute forcing a transparent color. This sets the
409 /// widget annotation's border color.
410 pub fn border_color_transparent(&mut self) -> &mut Self {
411 self.insert(Name(b"BC")).array();
412 self
413 }
414
415 /// Write the `/BC` attribute using a grayscale color. This sets the
416 /// widget annotation's border color.
417 pub fn border_color_gray(&mut self, gray: f32) -> &mut Self {
418 self.insert(Name(b"BC")).array().item(gray);
419 self
420 }
421
422 /// Write the `/BC` attribute using an RGB color. This sets the widget
423 /// annotation's border color.
424 pub fn border_color_rgb(&mut self, r: f32, g: f32, b: f32) -> &mut Self {
425 self.insert(Name(b"BC")).array().items([r, g, b]);
426 self
427 }
428
429 /// Write the `/BC` attribute using an RGB color. This sets the widget
430 /// annotation's border color.
431 pub fn border_color_cymk(&mut self, c: f32, y: f32, m: f32, k: f32) -> &mut Self {
432 self.insert(Name(b"BC")).array().items([c, y, m, k]);
433 self
434 }
435
436 /// Write the `/BG` attribute forcing a transparent color. This sets the
437 /// widget annotation's background color.
438 pub fn background_color_transparent(&mut self) -> &mut Self {
439 self.insert(Name(b"BG")).array();
440 self
441 }
442
443 /// Write the `/BG` attribute using a grayscale color. This sets the
444 /// widget annotation's backround color.
445 pub fn background_color_gray(&mut self, gray: f32) -> &mut Self {
446 self.insert(Name(b"BG")).array().item(gray);
447 self
448 }
449
450 /// Write the `/BG` attribute using an RGB color. This sets the widget
451 /// annotation's backround color.
452 pub fn background_color_rgb(&mut self, r: f32, g: f32, b: f32) -> &mut Self {
453 self.insert(Name(b"BG")).array().items([r, g, b]);
454 self
455 }
456
457 /// Write the `/BG` attribute using an RGB color. This sets the widget
458 /// annotation's backround color.
459 pub fn background_color_cymk(&mut self, c: f32, y: f32, m: f32, k: f32) -> &mut Self {
460 self.insert(Name(b"BG")).array().items([c, y, m, k]);
461 self
462 }
463
464 /// Write the `/CA` attribute. This sets the widget annotation's normal
465 /// caption. Only permissible for button fields.
466 pub fn normal_caption(&mut self, caption: TextStr) -> &mut Self {
467 self.pair(Name(b"CA"), caption);
468 self
469 }
470
471 /// Write the `/RC` attribute. This sets the widget annotation's rollover
472 /// (hover) caption. Only permissible for push button fields.
473 pub fn rollover_caption(&mut self, caption: TextStr) -> &mut Self {
474 self.pair(Name(b"RC"), caption);
475 self
476 }
477
478 /// Write the `/AC` attribute. This sets the widget annotation's alternate
479 /// (down) caption. Only permissible for push button fields.
480 pub fn alterante_caption(&mut self, caption: TextStr) -> &mut Self {
481 self.pair(Name(b"AC"), caption);
482 self
483 }
484
485 /// Write the `/I` attribute. This sets the widget annotation's normal icon
486 /// as a reference to a [`FormXObject`]. Only permissible for push button
487 /// fields.
488 pub fn normal_icon(&mut self, id: Ref) -> &mut Self {
489 self.pair(Name(b"I"), id);
490 self
491 }
492
493 /// Write the `/RI` attribute. This sets the widget annotation's rollover
494 /// (hover) icon as a reference to a [`FormXObject`]. Only permissible for
495 /// push button fields.
496 pub fn rollover_icon(&mut self, id: Ref) -> &mut Self {
497 self.pair(Name(b"RI"), id);
498 self
499 }
500
501 /// Write the `/IX` attribute. This sets the widget annotation's alternate
502 /// (down) icon as a reference to a [`FormXObject`]. Only permissible for
503 /// push button fields.
504 pub fn alternate_icon(&mut self, id: Ref) -> &mut Self {
505 self.pair(Name(b"IX"), id);
506 self
507 }
508
509 /// Start writing the `/IF` dictonary. This sets the widget annotation's
510 /// icon display characteristics. Only permissible for push button fields.
511 pub fn icon_fit(&mut self) -> IconFit<'_> {
512 self.insert(Name(b"IF")).start()
513 }
514
515 /// Write the `/TP` attribute. This sets the widget annotation's caption
516 /// position relative to the annotation's icon. Only permissible for push
517 /// button fields.
518 pub fn text_position(&mut self, position: TextPosition) -> &mut Self {
519 self.pair(Name(b"TP"), position as i32);
520 self
521 }
522}
523
524deref!('a, AppearanceCharacteristics<'a> => Dict<'a>, dict);
525
526/// The position the text of the widget annotation's caption relative to its
527/// icon.
528#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
529pub enum TextPosition {
530 /// Hide icon, show only caption.
531 CaptionOnly = 0,
532 /// Hide caption, show only icon.
533 IconOnly = 1,
534 /// The caption should be placed below the icon.
535 Below = 2,
536 /// The caption should be placed above the icon.
537 Above = 3,
538 /// The caption should be placed to the right of the icon.
539 Right = 4,
540 /// The caption should be placed to the left of the icon.
541 Left = 5,
542 /// The caption should be placed overlaid directly on the icon.
543 Overlaid = 6,
544}
545
546/// Writer for an _icon fit dictionary_.
547///
548/// This struct is created by [`AppearanceCharacteristics::icon_fit`].
549pub struct IconFit<'a> {
550 dict: Dict<'a>,
551}
552
553writer!(IconFit: |obj| Self { dict: obj.dict() });
554
555impl IconFit<'_> {
556 /// Write the `/SW` attribute. This sets under which circumstances the icon
557 /// of the widget annotation should be scaled.
558 pub fn scale(&mut self, value: IconScale) -> &mut Self {
559 self.pair(Name(b"SW"), value.to_name());
560 self
561 }
562
563 /// Write the `/S` attribute. This sets the scaling type of this annoation.
564 pub fn scale_type(&mut self, value: IconScaleType) -> &mut Self {
565 self.pair(Name(b"S"), value.to_name());
566 self
567 }
568
569 /// Write the `/A` attribute. This sets the widget annotation's leftover
570 /// space if proportional scaling is applied given as fractions between
571 /// `0.0` and `1.0`.
572 pub fn leftover_space(&mut self, x: f32, y: f32) -> &mut Self {
573 self.insert(Name(b"A")).array().items([x, y]);
574 self
575 }
576
577 /// Wrtite the `/FB` attribute. This sets whether the border line width
578 /// should be ignored when scaling the icon to fit the annotation bounds.
579 /// PDF 1.5+.
580 pub fn fit_bounds(&mut self, fit: bool) -> &mut Self {
581 self.pair(Name(b"FB"), fit);
582 self
583 }
584}
585
586deref!('a, IconFit<'a> => Dict<'a>, dict);
587
588/// How the icon in a push button field should be scaled.
589#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
590pub enum IconScale {
591 /// Always scale the icon.
592 Always,
593 /// Scale the icon only when the icon is bigger than the annotation
594 /// rectangle.
595 Bigger,
596 /// Scale the icon only when the icon is smaller than the annotation
597 /// rectangle.
598 Smaller,
599 /// Never scale the icon.
600 Never,
601}
602
603impl IconScale {
604 pub(crate) fn to_name(self) -> Name<'static> {
605 match self {
606 Self::Always => Name(b"A"),
607 Self::Bigger => Name(b"B"),
608 Self::Smaller => Name(b"S"),
609 Self::Never => Name(b"N"),
610 }
611 }
612}
613
614/// How the icon in a push button field should be scaled.
615#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
616pub enum IconScaleType {
617 /// Scale the icon to fill the annotation rectangle exactly, without regard
618 /// to its original aspect ratio (ratio of width to height).
619 Anamorphic,
620 /// Scale the icon to fit the width or height of the annotation rectangle
621 /// while maintaining the icon’s original aspect ratio. If the required
622 /// horizontal and vertical scaling factors are different, use the smaller
623 /// of the two, centering the icon within the annotation rectangle in the
624 /// other dimension.
625 Proportional,
626}
627
628impl IconScaleType {
629 pub(crate) fn to_name(self) -> Name<'static> {
630 match self {
631 Self::Anamorphic => Name(b"A"),
632 Self::Proportional => Name(b"P"),
633 }
634 }
635}
636
637/// Highlighting effect applied when a user holds the mouse button over an
638/// annotation.
639#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
640pub enum HighlightEffect {
641 /// No effect.
642 None,
643 /// Invert the colors inside of the annotation rect.
644 Invert,
645 /// Invert the colors on the annotation border.
646 Outline,
647 /// Make the annotation rect's area appear depressed.
648 Push,
649}
650
651impl HighlightEffect {
652 pub(crate) fn to_name(self) -> Name<'static> {
653 match self {
654 Self::None => Name(b"N"),
655 Self::Invert => Name(b"I"),
656 Self::Outline => Name(b"O"),
657 Self::Push => Name(b"P"),
658 }
659 }
660}
661
662/// Writer for an _appearance dictionary_.
663///
664/// This struct is created by [`Annotation::appearance`].
665pub struct Appearance<'a> {
666 dict: Dict<'a>,
667}
668
669writer!(Appearance: |obj| Self { dict: obj.dict() });
670
671impl Appearance<'_> {
672 /// Start writing the `/N` stream or dictionary to set the annotation's
673 /// normal appearance.
674 pub fn normal(&mut self) -> AppearanceEntry<'_> {
675 self.insert(Name(b"N")).start()
676 }
677
678 /// Start writing the `/R` stream or dictionary to set the annotation's
679 /// rollover (hover) appearance.
680 pub fn rollover(&mut self) -> AppearanceEntry<'_> {
681 self.insert(Name(b"R")).start()
682 }
683
684 /// Start writing the `/D` stream or dictionary to set the annotation's
685 /// alternate (down) appearance.
686 pub fn alternate(&mut self) -> AppearanceEntry<'_> {
687 self.insert(Name(b"D")).start()
688 }
689}
690
691deref!('a, Appearance<'a> => Dict<'a>, dict);
692
693/// Writer for an _appearance stream_ or an _appearance subdictionary_.
694///
695/// This struct is created by [`Appearance::normal`], [`Appearance::rollover`]
696/// and [`Appearance::alternate`].
697pub struct AppearanceEntry<'a> {
698 obj: Obj<'a>,
699}
700
701writer!(AppearanceEntry: |obj| Self { obj });
702
703impl<'a> AppearanceEntry<'a> {
704 /// Write an indirect reference to a [`FormXObject`] containing the
705 /// appearance stream.
706 pub fn stream(self, id: Ref) {
707 self.obj.primitive(id);
708 }
709
710 /// Start writing an appearance subdictionary containing indirect references
711 /// to [`FormXObject`]s for each appearance state.
712 pub fn streams(self) -> TypedDict<'a, Ref> {
713 self.obj.dict().typed()
714 }
715}
716
717deref!('a, AppearanceEntry<'a> => Obj<'a>, obj);
718
719/// Writer for an _border style dictionary_.
720///
721/// This struct is created by [`Annotation::border_style`].
722pub struct BorderStyle<'a> {
723 dict: Dict<'a>,
724}
725
726writer!(BorderStyle: |obj| {
727 let mut dict = obj.dict();
728 dict.pair(Name(b"Type"), Name(b"Border"));
729 Self { dict }
730});
731
732impl BorderStyle<'_> {
733 /// Write the `/W` attribute. This is the width of the border in points.
734 pub fn width(&mut self, points: f32) -> &mut Self {
735 self.pair(Name(b"W"), points);
736 self
737 }
738
739 /// Write the `/S` attribute.
740 pub fn style(&mut self, style: BorderType) -> &mut Self {
741 self.pair(Name(b"S"), style.to_name());
742 self
743 }
744
745 /// Write the `/D` attribute to set the repeating lengths of dashes and gaps
746 /// in between.
747 pub fn dashes(&mut self, dash_pattern: impl IntoIterator<Item = f32>) -> &mut Self {
748 self.insert(Name(b"D")).array().items(dash_pattern);
749 self
750 }
751}
752
753deref!('a, BorderStyle<'a> => Dict<'a>, dict);
754
755/// The kind of line to draw on the border.
756#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
757pub enum BorderType {
758 /// A solid line.
759 Solid,
760 /// A dashed line, dash pattern may be specified further elsewhere.
761 Dashed,
762 /// A line with a 3D effect.
763 Beveled,
764 /// A line that makes the rectangle appear depressed.
765 Inset,
766 /// A single line at the bottom of the border rectangle.
767 Underline,
768}
769
770impl BorderType {
771 pub(crate) fn to_name(self) -> Name<'static> {
772 match self {
773 Self::Solid => Name(b"S"),
774 Self::Dashed => Name(b"D"),
775 Self::Beveled => Name(b"B"),
776 Self::Inset => Name(b"I"),
777 Self::Underline => Name(b"U"),
778 }
779 }
780}
781
782#[cfg(test)]
783mod tests {
784 use super::*;
785
786 #[test]
787 fn test_annotations() {
788 test!(
789 crate::tests::slice(|w| {
790 w.annotation(Ref::new(1)).rect(Rect::new(0.0, 0.0, 1.0, 1.0));
791 w.annotation(Ref::new(2)).rect(Rect::new(1.0, 1.0, 0.0, 0.0));
792 }),
793 b"1 0 obj",
794 b"<<",
795 b" /Type /Annot",
796 b" /Rect [0 0 1 1]",
797 b">>",
798 b"endobj\n",
799 b"2 0 obj",
800 b"<<",
801 b" /Type /Annot",
802 b" /Rect [1 1 0 0]",
803 b">>",
804 b"endobj\n\n",
805 );
806 }
807}