1use super::*;
2use crate::chunk::Settings;
3use crate::object::{is_delimiter_character, TextStrLike};
4
5pub struct Content {
7 buf: Buf,
8 settings: Settings,
9 q_depth: usize,
10}
11
12impl Content {
14 #[allow(clippy::new_without_default)]
17 pub fn new() -> Self {
18 Self::with_settings(Settings::default())
19 }
20
21 pub fn with_settings(settings: Settings) -> Self {
24 Self::with_settings_and_capacity(settings, 1204)
25 }
26
27 pub fn with_capacity(capacity: usize) -> Self {
30 Self::with_settings_and_capacity(Settings::default(), capacity)
31 }
32
33 pub fn with_settings_and_capacity(settings: Settings, capacity: usize) -> Self {
36 Self {
37 buf: Buf::with_capacity(capacity),
38 settings,
39 q_depth: 0,
40 }
41 }
42
43 #[inline]
45 #[allow(clippy::len_without_is_empty)]
46 pub fn len(&self) -> usize {
47 self.buf.len()
48 }
49
50 pub fn reserve(&mut self, additional: usize) {
52 self.buf.reserve(additional);
53 }
54
55 pub fn as_bytes(&self) -> &[u8] {
57 self.buf.as_slice()
58 }
59
60 #[inline]
62 pub fn op<'a>(&'a mut self, operator: &'a str) -> Operation<'a> {
63 Operation::start(&mut self.buf, operator, self.settings)
64 }
65
66 pub fn finish(mut self) -> Buf {
76 if self.buf.last() == Some(&b'\n') {
77 self.buf.inner.pop();
78 }
79 self.buf
80 }
81}
82
83pub struct Operation<'a> {
87 buf: &'a mut Buf,
88 op: &'a str,
89 first: bool,
90 settings: Settings,
91}
92
93impl<'a> Operation<'a> {
94 #[inline]
95 pub(crate) fn start(buf: &'a mut Buf, op: &'a str, settings: Settings) -> Self {
96 Self { buf, op, first: true, settings }
97 }
98
99 #[inline]
101 pub fn operand<T: Primitive>(&mut self, value: T) -> &mut Self {
102 self.obj().primitive(value);
103 self
104 }
105
106 #[inline]
108 pub fn operands<T, I>(&mut self, values: I) -> &mut Self
109 where
110 T: Primitive,
111 I: IntoIterator<Item = T>,
112 {
113 for value in values {
114 self.operand(value);
115 }
116 self
117 }
118
119 #[inline]
121 pub fn obj(&mut self) -> Obj<'_> {
122 let pad_byte = if self.first { b'\n' } else { b' ' };
126
127 let needs_padding = if self.settings.pretty {
131 if !self.buf.is_empty() {
132 self.buf.push(pad_byte);
133 }
134
135 false
136 } else {
137 true
138 };
139
140 self.first = false;
141 Obj::direct(self.buf, 0, self.settings, needs_padding)
142 }
143}
144
145impl Drop for Operation<'_> {
146 #[inline]
147 fn drop(&mut self) {
148 let pad_byte = if self.first { b'\n' } else { b' ' };
149
150 if (self.settings.pretty
153 || self.buf.last().is_some_and(|b| !is_delimiter_character(*b)))
154 && !self.buf.is_empty()
155 {
156 self.buf.push(pad_byte);
157 }
158
159 self.buf.extend(self.op.as_bytes());
160 }
161}
162
163impl Content {
165 #[inline]
169 pub fn set_line_width(&mut self, width: f32) -> &mut Self {
170 assert!(width >= 0.0, "line width must be positive");
171 self.op("w").operand(width);
172 self
173 }
174
175 #[inline]
177 pub fn set_line_cap(&mut self, cap: LineCapStyle) -> &mut Self {
178 self.op("J").operand(cap.to_int());
179 self
180 }
181
182 #[inline]
184 pub fn set_line_join(&mut self, join: LineJoinStyle) -> &mut Self {
185 self.op("j").operand(join.to_int());
186 self
187 }
188
189 #[inline]
191 pub fn set_miter_limit(&mut self, limit: f32) -> &mut Self {
192 self.op("M").operand(limit);
193 self
194 }
195
196 #[inline]
198 pub fn set_dash_pattern(
199 &mut self,
200 array: impl IntoIterator<Item = f32>,
201 phase: f32,
202 ) -> &mut Self {
203 let mut op = self.op("d");
204 op.obj().array().items(array);
205 op.operand(phase);
206 op.finish();
207 self
208 }
209
210 #[inline]
212 pub fn set_rendering_intent(&mut self, intent: RenderingIntent) -> &mut Self {
213 self.op("ri").operand(intent.to_name());
214 self
215 }
216
217 #[inline]
221 pub fn set_flatness(&mut self, tolerance: i32) -> &mut Self {
222 assert!(
223 matches!(tolerance, 0..=100),
224 "flatness tolerance must be between 0 and 100",
225 );
226 self.op("i").operand(tolerance);
227 self
228 }
229
230 #[inline]
232 pub fn set_parameters(&mut self, dict: Name) -> &mut Self {
233 self.op("gs").operand(dict);
234 self
235 }
236}
237
238#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
240pub enum LineCapStyle {
241 ButtCap,
243 RoundCap,
246 ProjectingSquareCap,
249}
250
251impl LineCapStyle {
252 #[inline]
253 pub(crate) fn to_int(self) -> i32 {
254 match self {
255 Self::ButtCap => 0,
256 Self::RoundCap => 1,
257 Self::ProjectingSquareCap => 2,
258 }
259 }
260}
261
262#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
264pub enum LineJoinStyle {
265 MiterJoin,
268 RoundJoin,
270 BevelJoin,
272}
273
274impl LineJoinStyle {
275 #[inline]
276 pub(crate) fn to_int(self) -> i32 {
277 match self {
278 Self::MiterJoin => 0,
279 Self::RoundJoin => 1,
280 Self::BevelJoin => 2,
281 }
282 }
283}
284#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
286pub enum RenderingIntent {
287 AbsoluteColorimetric,
289 #[default]
291 RelativeColorimetric,
292 Saturation,
294 Perceptual,
296}
297
298impl RenderingIntent {
299 #[inline]
300 pub(crate) fn to_name(self) -> Name<'static> {
301 match self {
302 Self::AbsoluteColorimetric => Name(b"AbsoluteColorimetric"),
303 Self::RelativeColorimetric => Name(b"RelativeColorimetric"),
304 Self::Saturation => Name(b"Saturation"),
305 Self::Perceptual => Name(b"Perceptual"),
306 }
307 }
308}
309
310impl Content {
312 #[inline]
314 pub fn save_state(&mut self) -> &mut Self {
315 self.op("q");
316 self.q_depth = self.q_depth.saturating_add(1);
317 self
318 }
319
320 #[inline]
322 pub fn restore_state(&mut self) -> &mut Self {
323 self.op("Q");
324 self.q_depth = self.q_depth.saturating_sub(1);
325 self
326 }
327
328 #[inline]
330 pub fn state_nesting_depth(&self) -> usize {
331 self.q_depth
332 }
333
334 #[inline]
337 pub fn transform(&mut self, matrix: [f32; 6]) -> &mut Self {
338 self.op("cm").operands(matrix);
339 self
340 }
341}
342
343impl Content {
345 #[inline]
347 pub fn move_to(&mut self, x: f32, y: f32) -> &mut Self {
348 self.op("m").operands([x, y]);
349 self
350 }
351
352 #[inline]
354 pub fn line_to(&mut self, x: f32, y: f32) -> &mut Self {
355 self.op("l").operands([x, y]);
356 self
357 }
358
359 #[inline]
362 pub fn cubic_to(
363 &mut self,
364 x1: f32,
365 y1: f32,
366 x2: f32,
367 y2: f32,
368 x3: f32,
369 y3: f32,
370 ) -> &mut Self {
371 self.op("c").operands([x1, y1, x2, y2, x3, y3]);
372 self
373 }
374
375 #[inline]
378 pub fn cubic_to_initial(&mut self, x2: f32, y2: f32, x3: f32, y3: f32) -> &mut Self {
379 self.op("v").operands([x2, y2, x3, y3]);
380 self
381 }
382
383 #[inline]
386 pub fn cubic_to_final(&mut self, x1: f32, y1: f32, x3: f32, y3: f32) -> &mut Self {
387 self.op("y").operands([x1, y1, x3, y3]);
388 self
389 }
390
391 #[inline]
393 pub fn close_path(&mut self) -> &mut Self {
394 self.op("h");
395 self
396 }
397
398 #[inline]
400 pub fn rect(&mut self, x: f32, y: f32, width: f32, height: f32) -> &mut Self {
401 self.op("re").operands([x, y, width, height]);
402 self
403 }
404}
405
406impl Content {
408 #[inline]
410 pub fn stroke(&mut self) -> &mut Self {
411 self.op("S");
412 self
413 }
414
415 #[inline]
417 pub fn close_and_stroke(&mut self) -> &mut Self {
418 self.op("s");
419 self
420 }
421
422 #[inline]
424 pub fn fill_nonzero(&mut self) -> &mut Self {
425 self.op("f");
426 self
427 }
428
429 #[inline]
431 pub fn fill_even_odd(&mut self) -> &mut Self {
432 self.op("f*");
433 self
434 }
435
436 #[inline]
439 pub fn fill_nonzero_and_stroke(&mut self) -> &mut Self {
440 self.op("B");
441 self
442 }
443
444 #[inline]
446 pub fn fill_even_odd_and_stroke(&mut self) -> &mut Self {
447 self.op("B*");
448 self
449 }
450
451 #[inline]
454 pub fn close_fill_nonzero_and_stroke(&mut self) -> &mut Self {
455 self.op("b");
456 self
457 }
458
459 #[inline]
462 pub fn close_fill_even_odd_and_stroke(&mut self) -> &mut Self {
463 self.op("b*");
464 self
465 }
466
467 #[inline]
471 pub fn end_path(&mut self) -> &mut Self {
472 self.op("n");
473 self
474 }
475}
476
477impl Content {
479 #[inline]
482 pub fn clip_nonzero(&mut self) -> &mut Self {
483 self.op("W");
484 self
485 }
486
487 #[inline]
490 pub fn clip_even_odd(&mut self) -> &mut Self {
491 self.op("W*");
492 self
493 }
494}
495
496impl Content {
498 #[inline]
500 pub fn begin_text(&mut self) -> &mut Self {
501 self.op("BT");
502 self
503 }
504
505 #[inline]
507 pub fn end_text(&mut self) -> &mut Self {
508 self.op("ET");
509 self
510 }
511}
512
513impl Content {
515 #[inline]
517 pub fn set_char_spacing(&mut self, spacing: f32) -> &mut Self {
518 self.op("Tc").operand(spacing);
519 self
520 }
521
522 #[inline]
524 pub fn set_word_spacing(&mut self, spacing: f32) -> &mut Self {
525 self.op("Tw").operand(spacing);
526 self
527 }
528
529 #[inline]
531 pub fn set_horizontal_scaling(&mut self, scaling: f32) -> &mut Self {
532 self.op("Tz").operand(scaling);
533 self
534 }
535
536 #[inline]
538 pub fn set_leading(&mut self, leading: f32) -> &mut Self {
539 self.op("TL").operand(leading);
540 self
541 }
542
543 #[inline]
545 pub fn set_font(&mut self, font: Name, size: f32) -> &mut Self {
546 self.op("Tf").operand(font).operand(size);
547 self
548 }
549
550 #[inline]
552 pub fn set_text_rendering_mode(&mut self, mode: TextRenderingMode) -> &mut Self {
553 self.op("Tr").operand(mode.to_int());
554 self
555 }
556
557 #[inline]
559 pub fn set_rise(&mut self, rise: f32) -> &mut Self {
560 self.op("Ts").operand(rise);
561 self
562 }
563}
564
565#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
567pub enum TextRenderingMode {
568 #[default]
570 Fill,
571 Stroke,
573 FillStroke,
575 Invisible,
577 FillClip,
580 StrokeClip,
583 FillStrokeClip,
586 Clip,
588}
589
590impl TextRenderingMode {
591 #[inline]
592 pub(crate) fn to_int(self) -> i32 {
593 match self {
594 Self::Fill => 0,
595 Self::Stroke => 1,
596 Self::FillStroke => 2,
597 Self::Invisible => 3,
598 Self::FillClip => 4,
599 Self::StrokeClip => 5,
600 Self::FillStrokeClip => 6,
601 Self::Clip => 7,
602 }
603 }
604}
605
606impl Content {
608 #[inline]
610 pub fn next_line(&mut self, x: f32, y: f32) -> &mut Self {
611 self.op("Td").operands([x, y]);
612 self
613 }
614
615 #[inline]
618 pub fn next_line_and_set_leading(&mut self, x: f32, y: f32) -> &mut Self {
619 self.op("TD").operands([x, y]);
620 self
621 }
622
623 #[inline]
625 pub fn set_text_matrix(&mut self, matrix: [f32; 6]) -> &mut Self {
626 self.op("Tm").operands(matrix);
627 self
628 }
629
630 #[inline]
633 pub fn next_line_using_leading(&mut self) -> &mut Self {
634 self.op("T*");
635 self
636 }
637}
638
639impl Content {
641 #[inline]
645 pub fn show(&mut self, text: Str) -> &mut Self {
646 self.op("Tj").operand(text);
647 self
648 }
649
650 #[inline]
652 pub fn next_line_show(&mut self, text: Str) -> &mut Self {
653 self.op("'").operand(text);
654 self
655 }
656
657 #[inline]
660 pub fn next_line_show_and_set_word_and_char_spacing(
661 &mut self,
662 word_spacing: f32,
663 char_spacing: f32,
664 text: Str,
665 ) -> &mut Self {
666 self.op("\"").operands([word_spacing, char_spacing]).operand(text);
667 self
668 }
669
670 #[inline]
672 pub fn show_positioned(&mut self) -> ShowPositioned<'_> {
673 ShowPositioned::start(self.op("TJ"))
674 }
675}
676
677pub struct ShowPositioned<'a> {
681 op: Operation<'a>,
682}
683
684impl<'a> ShowPositioned<'a> {
685 #[inline]
686 pub(crate) fn start(op: Operation<'a>) -> Self {
687 Self { op }
688 }
689
690 #[inline]
692 pub fn items(&mut self) -> PositionedItems<'_> {
693 PositionedItems::start(self.op.obj())
694 }
695}
696
697deref!('a, ShowPositioned<'a> => Operation<'a>, op);
698
699pub struct PositionedItems<'a> {
703 array: Array<'a>,
704}
705
706impl<'a> PositionedItems<'a> {
707 #[inline]
708 pub(crate) fn start(obj: Obj<'a>) -> Self {
709 Self { array: obj.array() }
710 }
711
712 #[inline]
716 pub fn show(&mut self, text: Str) -> &mut Self {
717 self.array.item(text);
718 self
719 }
720
721 #[inline]
726 pub fn adjust(&mut self, amount: f32) -> &mut Self {
727 self.array.item(amount);
728 self
729 }
730}
731
732deref!('a, PositionedItems<'a> => Array<'a>, array);
733
734impl Content {
739 #[inline]
743 pub fn start_color_glyph(&mut self, wx: f32) -> &mut Self {
744 self.op("d0").operands([wx, 0.0]);
745 self
746 }
747
748 #[inline]
754 pub fn start_shape_glyph(
755 &mut self,
756 wx: f32,
757 ll_x: f32,
758 ll_y: f32,
759 ur_x: f32,
760 ur_y: f32,
761 ) -> &mut Self {
762 self.op("d1").operands([wx, 0.0, ll_x, ll_y, ur_x, ur_y]);
763 self
764 }
765}
766
767impl Content {
769 #[inline]
774 pub fn set_stroke_color_space<'a>(
775 &mut self,
776 space: impl Into<ColorSpaceOperand<'a>>,
777 ) -> &mut Self {
778 self.op("CS").operand(space.into().to_name());
779 self
780 }
781
782 #[inline]
787 pub fn set_fill_color_space<'a>(
788 &mut self,
789 space: impl Into<ColorSpaceOperand<'a>>,
790 ) -> &mut Self {
791 self.op("cs").operand(space.into().to_name());
792 self
793 }
794
795 #[inline]
798 pub fn set_stroke_color(
799 &mut self,
800 color: impl IntoIterator<Item = f32>,
801 ) -> &mut Self {
802 self.op("SCN").operands(color);
803 self
804 }
805
806 #[inline]
812 pub fn set_stroke_pattern(
813 &mut self,
814 tint: impl IntoIterator<Item = f32>,
815 name: Name,
816 ) -> &mut Self {
817 self.op("SCN").operands(tint).operand(name);
818 self
819 }
820
821 #[inline]
824 pub fn set_fill_color(&mut self, color: impl IntoIterator<Item = f32>) -> &mut Self {
825 self.op("scn").operands(color);
826 self
827 }
828
829 #[inline]
835 pub fn set_fill_pattern(
836 &mut self,
837 tint: impl IntoIterator<Item = f32>,
838 name: Name,
839 ) -> &mut Self {
840 self.op("scn").operands(tint).operand(name);
841 self
842 }
843
844 #[inline]
847 pub fn set_stroke_gray(&mut self, gray: f32) -> &mut Self {
848 self.op("G").operand(gray);
849 self
850 }
851
852 #[inline]
855 pub fn set_fill_gray(&mut self, gray: f32) -> &mut Self {
856 self.op("g").operand(gray);
857 self
858 }
859
860 #[inline]
863 pub fn set_stroke_rgb(&mut self, r: f32, g: f32, b: f32) -> &mut Self {
864 self.op("RG").operands([r, g, b]);
865 self
866 }
867
868 #[inline]
871 pub fn set_fill_rgb(&mut self, r: f32, g: f32, b: f32) -> &mut Self {
872 self.op("rg").operands([r, g, b]);
873 self
874 }
875
876 #[inline]
879 pub fn set_stroke_cmyk(&mut self, c: f32, m: f32, y: f32, k: f32) -> &mut Self {
880 self.op("K").operands([c, m, y, k]);
881 self
882 }
883
884 #[inline]
887 pub fn set_fill_cmyk(&mut self, c: f32, m: f32, y: f32, k: f32) -> &mut Self {
888 self.op("k").operands([c, m, y, k]);
889 self
890 }
891}
892
893#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
896pub enum ColorSpaceOperand<'a> {
897 DeviceGray,
903 DeviceRgb,
908 DeviceCmyk,
913 Pattern,
919 Named(Name<'a>),
927}
928
929impl<'a> ColorSpaceOperand<'a> {
930 #[inline]
931 pub(crate) fn to_name(self) -> Name<'a> {
932 match self {
933 Self::DeviceGray => Name(b"DeviceGray"),
934 Self::DeviceRgb => Name(b"DeviceRGB"),
935 Self::DeviceCmyk => Name(b"DeviceCMYK"),
936 Self::Pattern => Name(b"Pattern"),
937 Self::Named(name) => name,
938 }
939 }
940}
941
942impl<'a> From<Name<'a>> for ColorSpaceOperand<'a> {
943 fn from(name: Name<'a>) -> Self {
944 Self::Named(name)
945 }
946}
947
948impl Content {
950 #[inline]
952 pub fn shading(&mut self, shading: Name) -> &mut Self {
953 self.op("sh").operand(shading);
954 self
955 }
956}
957
958impl Content {
962 #[inline]
964 pub fn x_object(&mut self, name: Name) -> &mut Self {
965 self.op("Do").operand(name);
966 self
967 }
968}
969
970impl Content {
972 #[inline]
974 pub fn marked_content_point(&mut self, tag: Name) -> &mut Self {
975 self.op("MP").operand(tag);
976 self
977 }
978
979 #[inline]
981 pub fn marked_content_point_with_properties(&mut self, tag: Name) -> MarkContent<'_> {
982 let mut op = self.op("DP");
983 op.operand(tag);
984 MarkContent::start(op)
985 }
986
987 #[inline]
989 pub fn begin_marked_content(&mut self, tag: Name) -> &mut Self {
990 self.op("BMC").operand(tag);
991 self
992 }
993
994 #[inline]
996 pub fn begin_marked_content_with_properties(&mut self, tag: Name) -> MarkContent<'_> {
997 let mut op = self.op("BDC");
998 op.operand(tag);
999 MarkContent::start(op)
1000 }
1001
1002 #[inline]
1004 pub fn end_marked_content(&mut self) -> &mut Self {
1005 self.op("EMC");
1006 self
1007 }
1008}
1009
1010pub struct MarkContent<'a> {
1012 op: Operation<'a>,
1013}
1014
1015impl<'a> MarkContent<'a> {
1016 #[inline]
1017 pub(crate) fn start(op: Operation<'a>) -> Self {
1018 Self { op }
1019 }
1020
1021 #[inline]
1024 pub fn properties(&mut self) -> PropertyList<'_> {
1025 self.op.obj().start()
1026 }
1027
1028 #[inline]
1032 pub fn properties_named(mut self, name: Name) {
1033 self.op.operand(name);
1034 }
1035}
1036
1037deref!('a, MarkContent<'a> => Operation<'a>, op);
1038
1039pub struct PropertyList<'a> {
1042 dict: Dict<'a>,
1043}
1044
1045writer!(PropertyList: |obj| Self { dict: obj.dict() });
1046
1047impl<'a> PropertyList<'a> {
1048 #[inline]
1050 pub fn identify(&mut self, identifier: i32) -> &mut Self {
1051 self.pair(Name(b"MCID"), identifier);
1052 self
1053 }
1054
1055 #[inline]
1058 pub fn actual_text(&mut self, text: impl TextStrLike) -> &mut Self {
1059 self.pair(Name(b"ActualText"), text);
1060 self
1061 }
1062
1063 #[inline]
1066 pub fn artifact(self) -> Artifact<'a> {
1067 Artifact::start_with_dict(self.dict)
1068 }
1069}
1070
1071deref!('a, PropertyList<'a> => Dict<'a>, dict);
1072
1073pub struct Artifact<'a> {
1077 dict: Dict<'a>,
1078}
1079
1080writer!(Artifact: |obj| Self::start_with_dict(obj.dict()));
1081
1082impl<'a> Artifact<'a> {
1083 #[inline]
1084 pub(crate) fn start_with_dict(dict: Dict<'a>) -> Self {
1085 Self { dict }
1086 }
1087
1088 #[inline]
1091 pub fn kind(&mut self, kind: ArtifactType) -> &mut Self {
1092 self.pair(Name(b"Type"), kind.to_name());
1093 self
1094 }
1095
1096 #[inline]
1099 pub fn subtype(&mut self, subtype: ArtifactSubtype) -> &mut Self {
1100 self.pair(Name(b"Subtype"), subtype.to_name());
1101 self
1102 }
1103
1104 #[inline]
1107 pub fn bounding_box(&mut self, bbox: Rect) -> &mut Self {
1108 self.pair(Name(b"BBox"), bbox);
1109 self
1110 }
1111
1112 #[inline]
1116 pub fn attached(
1117 &mut self,
1118 attachment: impl IntoIterator<Item = ArtifactAttachment>,
1119 ) -> &mut Self {
1120 self.insert(Name(b"Attached"))
1121 .array()
1122 .typed()
1123 .items(attachment.into_iter().map(ArtifactAttachment::to_name));
1124 self
1125 }
1126}
1127
1128deref!('a, Artifact<'a> => Dict<'a>, dict);
1129
1130#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1132pub enum ArtifactType {
1133 Pagination,
1135 Layout,
1137 Page,
1139 Background,
1141 Inline,
1143}
1144
1145impl ArtifactType {
1146 #[inline]
1147 pub(crate) fn to_name(self) -> Name<'static> {
1148 match self {
1149 Self::Pagination => Name(b"Pagination"),
1150 Self::Layout => Name(b"Layout"),
1151 Self::Page => Name(b"Page"),
1152 Self::Background => Name(b"Background"),
1153 Self::Inline => Name(b"Inline"),
1154 }
1155 }
1156}
1157
1158#[derive(Debug, Clone, PartialEq)]
1160pub enum ArtifactSubtype<'a> {
1161 Header,
1163 Footer,
1165 Watermark,
1167 PageNumber,
1169 Bates,
1171 LineNumber,
1173 Redaction,
1175 Custom(Name<'a>),
1177}
1178
1179impl<'a> ArtifactSubtype<'a> {
1180 #[inline]
1181 pub(crate) fn to_name(self) -> Name<'a> {
1182 match self {
1183 Self::Header => Name(b"Header"),
1184 Self::Footer => Name(b"Footer"),
1185 Self::Watermark => Name(b"Watermark"),
1186 Self::PageNumber => Name(b"PageNum"),
1187 Self::Bates => Name(b"Bates"),
1188 Self::LineNumber => Name(b"LineNum"),
1189 Self::Redaction => Name(b"Redaction"),
1190 Self::Custom(name) => name,
1191 }
1192 }
1193}
1194
1195#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1197#[allow(missing_docs)]
1198pub enum ArtifactAttachment {
1199 Left,
1200 Top,
1201 Right,
1202 Bottom,
1203}
1204
1205impl ArtifactAttachment {
1206 #[inline]
1207 pub(crate) fn to_name(self) -> Name<'static> {
1208 match self {
1209 Self::Left => Name(b"Left"),
1210 Self::Top => Name(b"Top"),
1211 Self::Right => Name(b"Right"),
1212 Self::Bottom => Name(b"Bottom"),
1213 }
1214 }
1215}
1216
1217impl Content {
1219 #[inline]
1221 pub fn begin_compat(&mut self) -> &mut Self {
1222 self.op("BX");
1223 self
1224 }
1225
1226 #[inline]
1228 pub fn end_compat(&mut self) -> &mut Self {
1229 self.op("EX");
1230 self
1231 }
1232}
1233
1234pub struct Resources<'a> {
1239 dict: Dict<'a>,
1240}
1241
1242writer!(Resources: |obj| Self { dict: obj.dict() });
1243
1244impl Resources<'_> {
1245 pub fn x_objects(&mut self) -> Dict<'_> {
1251 self.insert(Name(b"XObject")).dict()
1252 }
1253
1254 pub fn fonts(&mut self) -> Dict<'_> {
1261 self.insert(Name(b"Font")).dict()
1262 }
1263
1264 pub fn color_spaces(&mut self) -> Dict<'_> {
1269 self.insert(Name(b"ColorSpace")).dict()
1270 }
1271
1272 pub fn patterns(&mut self) -> Dict<'_> {
1278 self.insert(Name(b"Pattern")).dict()
1279 }
1280
1281 pub fn shadings(&mut self) -> Dict<'_> {
1286 self.insert(Name(b"Shading")).dict()
1287 }
1288
1289 pub fn ext_g_states(&mut self) -> Dict<'_> {
1294 self.insert(Name(b"ExtGState")).dict()
1295 }
1296
1297 pub fn proc_sets(&mut self, sets: impl IntoIterator<Item = ProcSet>) -> &mut Self {
1303 self.insert(Name(b"ProcSet"))
1304 .array()
1305 .items(sets.into_iter().map(ProcSet::to_name));
1306 self
1307 }
1308
1309 pub fn proc_sets_all(&mut self) -> &mut Self {
1315 self.proc_sets([
1316 ProcSet::Pdf,
1317 ProcSet::Text,
1318 ProcSet::ImageGrayscale,
1319 ProcSet::ImageColor,
1320 ProcSet::ImageIndexed,
1321 ])
1322 }
1323
1324 pub fn properties(&mut self) -> TypedDict<'_, PropertyList<'_>> {
1330 self.insert(Name(b"Properties")).dict().typed()
1331 }
1332}
1333
1334deref!('a, Resources<'a> => Dict<'a>, dict);
1335
1336#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
1341pub enum ProcSet {
1342 Pdf,
1344 Text,
1346 ImageGrayscale,
1348 ImageColor,
1350 ImageIndexed,
1352}
1353
1354impl ProcSet {
1355 pub(crate) fn to_name(self) -> Name<'static> {
1356 match self {
1357 ProcSet::Pdf => Name(b"PDF"),
1358 ProcSet::Text => Name(b"Text"),
1359 ProcSet::ImageGrayscale => Name(b"ImageB"),
1360 ProcSet::ImageColor => Name(b"ImageC"),
1361 ProcSet::ImageIndexed => Name(b"ImageI"),
1362 }
1363 }
1364}
1365
1366pub struct ExtGraphicsState<'a> {
1371 dict: Dict<'a>,
1372}
1373
1374writer!(ExtGraphicsState: |obj| {
1375 let mut dict = obj.dict();
1376 dict.pair(Name(b"Type"), Name(b"ExtGState"));
1377 Self { dict }
1378});
1379
1380impl ExtGraphicsState<'_> {
1381 pub fn line_width(&mut self, width: f32) -> &mut Self {
1383 self.pair(Name(b"LW"), width);
1384 self
1385 }
1386
1387 pub fn line_cap(&mut self, cap: LineCapStyle) -> &mut Self {
1389 self.pair(Name(b"LC"), cap.to_int());
1390 self
1391 }
1392
1393 pub fn line_join(&mut self, join: LineJoinStyle) -> &mut Self {
1395 self.pair(Name(b"LJ"), join.to_int());
1396 self
1397 }
1398
1399 pub fn miter_limit(&mut self, limit: f32) -> &mut Self {
1401 self.pair(Name(b"ML"), limit);
1402 self
1403 }
1404
1405 pub fn dash_pattern(
1407 &mut self,
1408 pattern: impl IntoIterator<Item = f32>,
1409 phase: f32,
1410 ) -> &mut Self {
1411 let mut array = self.insert(Name(b"D")).array();
1412 array.push().array().items(pattern);
1413 array.item(phase);
1414 array.finish();
1415 self
1416 }
1417
1418 pub fn rendering_intent(&mut self, intent: RenderingIntent) -> &mut Self {
1420 self.pair(Name(b"RI"), intent.to_name());
1421 self
1422 }
1423
1424 pub fn overprint(&mut self, overprint: bool) -> &mut Self {
1428 self.pair(Name(b"OP"), overprint);
1429 self
1430 }
1431
1432 pub fn overprint_fill(&mut self, overprint: bool) -> &mut Self {
1435 self.pair(Name(b"op"), overprint);
1436 self
1437 }
1438
1439 pub fn overprint_mode(&mut self, mode: OverprintMode) -> &mut Self {
1444 self.pair(Name(b"OPM"), mode.to_int());
1445 self
1446 }
1447
1448 pub fn font(&mut self, font: Name, size: f32) -> &mut Self {
1450 let mut array = self.insert(Name(b"Font")).array();
1451 array.item(font);
1452 array.item(size);
1453 array.finish();
1454 self
1455 }
1456
1457 pub fn black_generation(&mut self, func: Ref) -> &mut Self {
1459 self.pair(Name(b"BG"), func);
1460 self
1461 }
1462
1463 pub fn black_generation_default(&mut self) -> &mut Self {
1467 self.pair(Name(b"BG2"), Name(b"Default"));
1468 self
1469 }
1470
1471 pub fn undercolor_removal(&mut self, func: Ref) -> &mut Self {
1473 self.pair(Name(b"UCR"), func);
1474 self
1475 }
1476
1477 pub fn undercolor_removal_default(&mut self) -> &mut Self {
1481 self.pair(Name(b"UCR2"), Name(b"Default"));
1482 self
1483 }
1484
1485 pub fn transfer(&mut self, func: Ref) -> &mut Self {
1489 self.pair(Name(b"TR"), func);
1490 self
1491 }
1492
1493 pub fn transfer_default(&mut self) -> &mut Self {
1496 self.pair(Name(b"TR2"), Name(b"Default"));
1497 self
1498 }
1499
1500 pub fn halftone(&mut self, ht: Ref) -> &mut Self {
1504 self.pair(Name(b"HT"), ht);
1505 self
1506 }
1507
1508 pub fn halftone_default(&mut self) -> &mut Self {
1511 self.pair(Name(b"HT"), Name(b"Default"));
1512 self
1513 }
1514
1515 pub fn flatness(&mut self, tolerance: f32) -> &mut Self {
1519 self.pair(Name(b"FL"), tolerance);
1520 self
1521 }
1522
1523 pub fn smoothness(&mut self, tolerance: f32) -> &mut Self {
1525 self.pair(Name(b"SM"), tolerance);
1526 self
1527 }
1528
1529 pub fn stroke_adjustment(&mut self, adjust: bool) -> &mut Self {
1531 self.pair(Name(b"SA"), adjust);
1532 self
1533 }
1534
1535 pub fn blend_mode(&mut self, mode: BlendMode) -> &mut Self {
1539 self.pair(Name(b"BM"), mode.to_name());
1540 self
1541 }
1542
1543 pub fn soft_mask(&mut self) -> SoftMask<'_> {
1547 self.insert(Name(b"SMask")).start()
1548 }
1549
1550 pub fn soft_mask_name(&mut self, mask: Name) -> &mut Self {
1554 self.pair(Name(b"SMask"), mask);
1555 self
1556 }
1557
1558 pub fn stroking_alpha(&mut self, alpha: f32) -> &mut Self {
1562 self.pair(Name(b"CA"), alpha);
1563 self
1564 }
1565
1566 pub fn non_stroking_alpha(&mut self, alpha: f32) -> &mut Self {
1571 self.pair(Name(b"ca"), alpha);
1572 self
1573 }
1574
1575 pub fn alpha_source(&mut self, source: bool) -> &mut Self {
1579 self.pair(Name(b"AIS"), source);
1580 self
1581 }
1582
1583 pub fn text_knockout(&mut self, knockout: bool) -> &mut Self {
1585 self.pair(Name(b"TK"), knockout);
1586 self
1587 }
1588}
1589
1590deref!('a, ExtGraphicsState<'a> => Dict<'a>, dict);
1591
1592#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
1594#[allow(missing_docs)]
1595pub enum BlendMode {
1596 #[default]
1597 Normal,
1598 Multiply,
1599 Screen,
1600 Overlay,
1601 Darken,
1602 Lighten,
1603 ColorDodge,
1604 ColorBurn,
1605 HardLight,
1606 SoftLight,
1607 Difference,
1608 Exclusion,
1609 Hue,
1610 Saturation,
1611 Color,
1612 Luminosity,
1613}
1614
1615impl BlendMode {
1616 pub(crate) fn to_name(self) -> Name<'static> {
1617 match self {
1618 BlendMode::Normal => Name(b"Normal"),
1619 BlendMode::Multiply => Name(b"Multiply"),
1620 BlendMode::Screen => Name(b"Screen"),
1621 BlendMode::Overlay => Name(b"Overlay"),
1622 BlendMode::Darken => Name(b"Darken"),
1623 BlendMode::Lighten => Name(b"Lighten"),
1624 BlendMode::ColorDodge => Name(b"ColorDodge"),
1625 BlendMode::ColorBurn => Name(b"ColorBurn"),
1626 BlendMode::HardLight => Name(b"HardLight"),
1627 BlendMode::SoftLight => Name(b"SoftLight"),
1628 BlendMode::Difference => Name(b"Difference"),
1629 BlendMode::Exclusion => Name(b"Exclusion"),
1630 BlendMode::Hue => Name(b"Hue"),
1631 BlendMode::Saturation => Name(b"Saturation"),
1632 BlendMode::Color => Name(b"Color"),
1633 BlendMode::Luminosity => Name(b"Luminosity"),
1634 }
1635 }
1636}
1637
1638#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
1640pub enum OverprintMode {
1641 #[default]
1644 OverrideAllColorants,
1645 IgnoreZeroChannel,
1652}
1653
1654impl OverprintMode {
1655 pub(crate) fn to_int(self) -> i32 {
1656 match self {
1657 OverprintMode::OverrideAllColorants => 0,
1658 OverprintMode::IgnoreZeroChannel => 1,
1659 }
1660 }
1661}
1662
1663pub struct SoftMask<'a> {
1667 dict: Dict<'a>,
1668}
1669
1670writer!(SoftMask: |obj| {
1671 let mut dict = obj.dict();
1672 dict.pair(Name(b"Type"), Name(b"Mask"));
1673 Self { dict }
1674});
1675
1676impl SoftMask<'_> {
1677 pub fn subtype(&mut self, subtype: MaskType) -> &mut Self {
1679 self.pair(Name(b"S"), subtype.to_name());
1680 self
1681 }
1682
1683 pub fn group(&mut self, group: Ref) -> &mut Self {
1687 self.pair(Name(b"G"), group);
1688 self
1689 }
1690
1691 pub fn backdrop(&mut self, color: impl IntoIterator<Item = f32>) -> &mut Self {
1695 self.insert(Name(b"BC")).array().items(color);
1696 self
1697 }
1698
1699 pub fn transfer_function(&mut self, function: Ref) -> &mut Self {
1702 self.pair(Name(b"TR"), function);
1703 self
1704 }
1705}
1706
1707deref!('a, SoftMask<'a> => Dict<'a>, dict);
1708
1709#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
1711pub enum MaskType {
1712 Alpha,
1714 Luminosity,
1717}
1718
1719impl MaskType {
1720 pub(crate) fn to_name(self) -> Name<'static> {
1721 match self {
1722 MaskType::Alpha => Name(b"Alpha"),
1723 MaskType::Luminosity => Name(b"Luminosity"),
1724 }
1725 }
1726}
1727
1728#[cfg(test)]
1729mod tests {
1730 use super::*;
1731
1732 #[test]
1733 fn test_content_encoding() {
1734 let mut content = Content::new();
1735 content
1736 .save_state()
1737 .rect(1.0, 2.0, 3.0, 4.0)
1738 .fill_nonzero()
1739 .set_dash_pattern([7.0, 2.0], 4.0)
1740 .x_object(Name(b"MyImage"))
1741 .set_fill_pattern([2.0, 3.5], Name(b"MyPattern"))
1742 .restore_state();
1743
1744 assert_eq!(
1745 content.finish().into_vec(),
1746 b"q\n1 2 3 4 re\nf\n[7 2] 4 d\n/MyImage Do\n2 3.5 /MyPattern scn\nQ"
1747 );
1748 }
1749
1750 #[test]
1751 fn test_content_text() {
1752 let mut content = Content::new();
1753
1754 content.set_font(Name(b"F1"), 12.0);
1755 content.begin_text();
1756 content.show_positioned().items();
1757 content
1758 .show_positioned()
1759 .items()
1760 .show(Str(b"AB"))
1761 .adjust(2.0)
1762 .show(Str(b"CD"));
1763 content.end_text();
1764
1765 assert_eq!(
1766 content.finish().into_vec(),
1767 b"/F1 12 Tf\nBT\n[] TJ\n[(AB) 2 (CD)] TJ\nET"
1768 );
1769 }
1770
1771 #[test]
1772 fn test_content_array_no_pretty() {
1773 let mut content = Content::with_settings(Settings { pretty: false });
1774
1775 content.set_font(Name(b"F1"), 12.0);
1776 content.set_font(Name(b"F2"), 15.0);
1777 content.begin_text();
1778 content.show_positioned().items();
1779 content
1780 .show_positioned()
1781 .items()
1782 .show(Str(b"AB"))
1783 .adjust(2.0)
1784 .show(Str(b"CD"))
1785 .adjust(4.0)
1786 .show(Str(b"EF"));
1787 content.end_text();
1788
1789 assert_eq!(
1790 content.finish().into_vec(),
1791 b"/F1 12 Tf/F2 15 Tf\nBT[]TJ[(AB)2(CD)4(EF)]TJ\nET"
1792 );
1793 }
1794
1795 #[test]
1796 fn test_content_dict_no_pretty() {
1797 let mut content = Content::with_settings(Settings { pretty: false });
1798
1799 let mut mc = content.begin_marked_content_with_properties(Name(b"Test"));
1800 let mut properties = mc.properties();
1801 properties.actual_text(TextStr("Actual")).identify(1);
1802 properties.artifact().kind(ArtifactType::Background);
1803 mc.finish();
1804
1805 assert_eq!(
1806 content.finish().into_vec(),
1807 b"/Test<</ActualText(Actual)/MCID 1/Type/Background>>BDC"
1808 );
1809 }
1810}