1use super::*;
2use crate::object::TextStrLike;
3
4pub struct Content {
6 buf: Buf,
7 q_depth: usize,
8}
9
10impl Content {
12 #[allow(clippy::new_without_default)]
15 pub fn new() -> Self {
16 Self::with_capacity(1024)
17 }
18
19 pub fn with_capacity(capacity: usize) -> Self {
21 Self { buf: Buf::with_capacity(capacity), q_depth: 0 }
22 }
23
24 #[inline]
26 pub fn op<'a>(&'a mut self, operator: &'a str) -> Operation<'a> {
27 Operation::start(&mut self.buf, operator)
28 }
29
30 pub fn finish(mut self) -> Buf {
40 if self.buf.last() == Some(&b'\n') {
41 self.buf.inner.pop();
42 }
43 self.buf
44 }
45}
46
47pub struct Operation<'a> {
51 buf: &'a mut Buf,
52 op: &'a str,
53 first: bool,
54}
55
56impl<'a> Operation<'a> {
57 #[inline]
58 pub(crate) fn start(buf: &'a mut Buf, op: &'a str) -> Self {
59 Self { buf, op, first: true }
60 }
61
62 #[inline]
64 pub fn operand<T: Primitive>(&mut self, value: T) -> &mut Self {
65 self.obj().primitive(value);
66 self
67 }
68
69 #[inline]
71 pub fn operands<T, I>(&mut self, values: I) -> &mut Self
72 where
73 T: Primitive,
74 I: IntoIterator<Item = T>,
75 {
76 for value in values {
77 self.operand(value);
78 }
79 self
80 }
81
82 #[inline]
84 pub fn obj(&mut self) -> Obj<'_> {
85 if !self.first {
86 self.buf.push(b' ');
87 }
88 self.first = false;
89 Obj::direct(self.buf, 0)
90 }
91}
92
93impl Drop for Operation<'_> {
94 #[inline]
95 fn drop(&mut self) {
96 if !self.first {
97 self.buf.push(b' ');
98 }
99 self.buf.extend(self.op.as_bytes());
100 self.buf.push(b'\n');
101 }
102}
103
104impl Content {
106 #[inline]
110 pub fn set_line_width(&mut self, width: f32) -> &mut Self {
111 assert!(width >= 0.0, "line width must be positive");
112 self.op("w").operand(width);
113 self
114 }
115
116 #[inline]
118 pub fn set_line_cap(&mut self, cap: LineCapStyle) -> &mut Self {
119 self.op("J").operand(cap.to_int());
120 self
121 }
122
123 #[inline]
125 pub fn set_line_join(&mut self, join: LineJoinStyle) -> &mut Self {
126 self.op("j").operand(join.to_int());
127 self
128 }
129
130 #[inline]
132 pub fn set_miter_limit(&mut self, limit: f32) -> &mut Self {
133 self.op("M").operand(limit);
134 self
135 }
136
137 #[inline]
139 pub fn set_dash_pattern(
140 &mut self,
141 array: impl IntoIterator<Item = f32>,
142 phase: f32,
143 ) -> &mut Self {
144 let mut op = self.op("d");
145 op.obj().array().items(array);
146 op.operand(phase);
147 op.finish();
148 self
149 }
150
151 #[inline]
153 pub fn set_rendering_intent(&mut self, intent: RenderingIntent) -> &mut Self {
154 self.op("ri").operand(intent.to_name());
155 self
156 }
157
158 #[inline]
162 pub fn set_flatness(&mut self, tolerance: i32) -> &mut Self {
163 assert!(
164 matches!(tolerance, 0..=100),
165 "flatness tolerance must be between 0 and 100",
166 );
167 self.op("i").operand(tolerance);
168 self
169 }
170
171 #[inline]
173 pub fn set_parameters(&mut self, dict: Name) -> &mut Self {
174 self.op("gs").operand(dict);
175 self
176 }
177}
178
179#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
181pub enum LineCapStyle {
182 ButtCap,
184 RoundCap,
187 ProjectingSquareCap,
190}
191
192impl LineCapStyle {
193 #[inline]
194 pub(crate) fn to_int(self) -> i32 {
195 match self {
196 Self::ButtCap => 0,
197 Self::RoundCap => 1,
198 Self::ProjectingSquareCap => 2,
199 }
200 }
201}
202
203#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
205pub enum LineJoinStyle {
206 MiterJoin,
209 RoundJoin,
211 BevelJoin,
213}
214
215impl LineJoinStyle {
216 #[inline]
217 pub(crate) fn to_int(self) -> i32 {
218 match self {
219 Self::MiterJoin => 0,
220 Self::RoundJoin => 1,
221 Self::BevelJoin => 2,
222 }
223 }
224}
225#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
227pub enum RenderingIntent {
228 AbsoluteColorimetric,
230 #[default]
232 RelativeColorimetric,
233 Saturation,
235 Perceptual,
237}
238
239impl RenderingIntent {
240 #[inline]
241 pub(crate) fn to_name(self) -> Name<'static> {
242 match self {
243 Self::AbsoluteColorimetric => Name(b"AbsoluteColorimetric"),
244 Self::RelativeColorimetric => Name(b"RelativeColorimetric"),
245 Self::Saturation => Name(b"Saturation"),
246 Self::Perceptual => Name(b"Perceptual"),
247 }
248 }
249}
250
251impl Content {
253 #[inline]
255 pub fn save_state(&mut self) -> &mut Self {
256 self.op("q");
257 self.q_depth = self.q_depth.saturating_add(1);
258 self
259 }
260
261 #[inline]
263 pub fn restore_state(&mut self) -> &mut Self {
264 self.op("Q");
265 self.q_depth = self.q_depth.saturating_sub(1);
266 self
267 }
268
269 #[inline]
271 pub fn state_nesting_depth(&self) -> usize {
272 self.q_depth
273 }
274
275 #[inline]
278 pub fn transform(&mut self, matrix: [f32; 6]) -> &mut Self {
279 self.op("cm").operands(matrix);
280 self
281 }
282}
283
284impl Content {
286 #[inline]
288 pub fn move_to(&mut self, x: f32, y: f32) -> &mut Self {
289 self.op("m").operands([x, y]);
290 self
291 }
292
293 #[inline]
295 pub fn line_to(&mut self, x: f32, y: f32) -> &mut Self {
296 self.op("l").operands([x, y]);
297 self
298 }
299
300 #[inline]
303 pub fn cubic_to(
304 &mut self,
305 x1: f32,
306 y1: f32,
307 x2: f32,
308 y2: f32,
309 x3: f32,
310 y3: f32,
311 ) -> &mut Self {
312 self.op("c").operands([x1, y1, x2, y2, x3, y3]);
313 self
314 }
315
316 #[inline]
319 pub fn cubic_to_initial(&mut self, x2: f32, y2: f32, x3: f32, y3: f32) -> &mut Self {
320 self.op("v").operands([x2, y2, x3, y3]);
321 self
322 }
323
324 #[inline]
327 pub fn cubic_to_final(&mut self, x1: f32, y1: f32, x3: f32, y3: f32) -> &mut Self {
328 self.op("y").operands([x1, y1, x3, y3]);
329 self
330 }
331
332 #[inline]
334 pub fn close_path(&mut self) -> &mut Self {
335 self.op("h");
336 self
337 }
338
339 #[inline]
341 pub fn rect(&mut self, x: f32, y: f32, width: f32, height: f32) -> &mut Self {
342 self.op("re").operands([x, y, width, height]);
343 self
344 }
345}
346
347impl Content {
349 #[inline]
351 pub fn stroke(&mut self) -> &mut Self {
352 self.op("S");
353 self
354 }
355
356 #[inline]
358 pub fn close_and_stroke(&mut self) -> &mut Self {
359 self.op("s");
360 self
361 }
362
363 #[inline]
365 pub fn fill_nonzero(&mut self) -> &mut Self {
366 self.op("f");
367 self
368 }
369
370 #[inline]
372 pub fn fill_even_odd(&mut self) -> &mut Self {
373 self.op("f*");
374 self
375 }
376
377 #[inline]
380 pub fn fill_nonzero_and_stroke(&mut self) -> &mut Self {
381 self.op("B");
382 self
383 }
384
385 #[inline]
387 pub fn fill_even_odd_and_stroke(&mut self) -> &mut Self {
388 self.op("B*");
389 self
390 }
391
392 #[inline]
395 pub fn close_fill_nonzero_and_stroke(&mut self) -> &mut Self {
396 self.op("b");
397 self
398 }
399
400 #[inline]
403 pub fn close_fill_even_odd_and_stroke(&mut self) -> &mut Self {
404 self.op("b*");
405 self
406 }
407
408 #[inline]
412 pub fn end_path(&mut self) -> &mut Self {
413 self.op("n");
414 self
415 }
416}
417
418impl Content {
420 #[inline]
423 pub fn clip_nonzero(&mut self) -> &mut Self {
424 self.op("W");
425 self
426 }
427
428 #[inline]
431 pub fn clip_even_odd(&mut self) -> &mut Self {
432 self.op("W*");
433 self
434 }
435}
436
437impl Content {
439 #[inline]
441 pub fn begin_text(&mut self) -> &mut Self {
442 self.op("BT");
443 self
444 }
445
446 #[inline]
448 pub fn end_text(&mut self) -> &mut Self {
449 self.op("ET");
450 self
451 }
452}
453
454impl Content {
456 #[inline]
458 pub fn set_char_spacing(&mut self, spacing: f32) -> &mut Self {
459 self.op("Tc").operand(spacing);
460 self
461 }
462
463 #[inline]
465 pub fn set_word_spacing(&mut self, spacing: f32) -> &mut Self {
466 self.op("Tw").operand(spacing);
467 self
468 }
469
470 #[inline]
472 pub fn set_horizontal_scaling(&mut self, scaling: f32) -> &mut Self {
473 self.op("Tz").operand(scaling);
474 self
475 }
476
477 #[inline]
479 pub fn set_leading(&mut self, leading: f32) -> &mut Self {
480 self.op("TL").operand(leading);
481 self
482 }
483
484 #[inline]
486 pub fn set_font(&mut self, font: Name, size: f32) -> &mut Self {
487 self.op("Tf").operand(font).operand(size);
488 self
489 }
490
491 #[inline]
493 pub fn set_text_rendering_mode(&mut self, mode: TextRenderingMode) -> &mut Self {
494 self.op("Tr").operand(mode.to_int());
495 self
496 }
497
498 #[inline]
500 pub fn set_rise(&mut self, rise: f32) -> &mut Self {
501 self.op("Ts").operand(rise);
502 self
503 }
504}
505
506#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
508pub enum TextRenderingMode {
509 #[default]
511 Fill,
512 Stroke,
514 FillStroke,
516 Invisible,
518 FillClip,
521 StrokeClip,
524 FillStrokeClip,
527 Clip,
529}
530
531impl TextRenderingMode {
532 #[inline]
533 pub(crate) fn to_int(self) -> i32 {
534 match self {
535 Self::Fill => 0,
536 Self::Stroke => 1,
537 Self::FillStroke => 2,
538 Self::Invisible => 3,
539 Self::FillClip => 4,
540 Self::StrokeClip => 5,
541 Self::FillStrokeClip => 6,
542 Self::Clip => 7,
543 }
544 }
545}
546
547impl Content {
549 #[inline]
551 pub fn next_line(&mut self, x: f32, y: f32) -> &mut Self {
552 self.op("Td").operands([x, y]);
553 self
554 }
555
556 #[inline]
559 pub fn next_line_and_set_leading(&mut self, x: f32, y: f32) -> &mut Self {
560 self.op("TD").operands([x, y]);
561 self
562 }
563
564 #[inline]
566 pub fn set_text_matrix(&mut self, matrix: [f32; 6]) -> &mut Self {
567 self.op("Tm").operands(matrix);
568 self
569 }
570
571 #[inline]
574 pub fn next_line_using_leading(&mut self) -> &mut Self {
575 self.op("T*");
576 self
577 }
578}
579
580impl Content {
582 #[inline]
586 pub fn show(&mut self, text: Str) -> &mut Self {
587 self.op("Tj").operand(text);
588 self
589 }
590
591 #[inline]
593 pub fn next_line_show(&mut self, text: Str) -> &mut Self {
594 self.op("'").operand(text);
595 self
596 }
597
598 #[inline]
601 pub fn next_line_show_and_set_word_and_char_spacing(
602 &mut self,
603 word_spacing: f32,
604 char_spacing: f32,
605 text: Str,
606 ) -> &mut Self {
607 self.op("\"").operands([word_spacing, char_spacing]).operand(text);
608 self
609 }
610
611 #[inline]
613 pub fn show_positioned(&mut self) -> ShowPositioned<'_> {
614 ShowPositioned::start(self.op("TJ"))
615 }
616}
617
618pub struct ShowPositioned<'a> {
622 op: Operation<'a>,
623}
624
625impl<'a> ShowPositioned<'a> {
626 #[inline]
627 pub(crate) fn start(op: Operation<'a>) -> Self {
628 Self { op }
629 }
630
631 #[inline]
633 pub fn items(&mut self) -> PositionedItems<'_> {
634 PositionedItems::start(self.op.obj())
635 }
636}
637
638deref!('a, ShowPositioned<'a> => Operation<'a>, op);
639
640pub struct PositionedItems<'a> {
644 array: Array<'a>,
645}
646
647impl<'a> PositionedItems<'a> {
648 #[inline]
649 pub(crate) fn start(obj: Obj<'a>) -> Self {
650 Self { array: obj.array() }
651 }
652
653 #[inline]
657 pub fn show(&mut self, text: Str) -> &mut Self {
658 self.array.item(text);
659 self
660 }
661
662 #[inline]
667 pub fn adjust(&mut self, amount: f32) -> &mut Self {
668 self.array.item(amount);
669 self
670 }
671}
672
673deref!('a, PositionedItems<'a> => Array<'a>, array);
674
675impl Content {
680 #[inline]
684 pub fn start_color_glyph(&mut self, wx: f32) -> &mut Self {
685 self.op("d0").operands([wx, 0.0]);
686 self
687 }
688
689 #[inline]
695 pub fn start_shape_glyph(
696 &mut self,
697 wx: f32,
698 ll_x: f32,
699 ll_y: f32,
700 ur_x: f32,
701 ur_y: f32,
702 ) -> &mut Self {
703 self.op("d1").operands([wx, 0.0, ll_x, ll_y, ur_x, ur_y]);
704 self
705 }
706}
707
708impl Content {
710 #[inline]
715 pub fn set_stroke_color_space<'a>(
716 &mut self,
717 space: impl Into<ColorSpaceOperand<'a>>,
718 ) -> &mut Self {
719 self.op("CS").operand(space.into().to_name());
720 self
721 }
722
723 #[inline]
728 pub fn set_fill_color_space<'a>(
729 &mut self,
730 space: impl Into<ColorSpaceOperand<'a>>,
731 ) -> &mut Self {
732 self.op("cs").operand(space.into().to_name());
733 self
734 }
735
736 #[inline]
739 pub fn set_stroke_color(
740 &mut self,
741 color: impl IntoIterator<Item = f32>,
742 ) -> &mut Self {
743 self.op("SCN").operands(color);
744 self
745 }
746
747 #[inline]
753 pub fn set_stroke_pattern(
754 &mut self,
755 tint: impl IntoIterator<Item = f32>,
756 name: Name,
757 ) -> &mut Self {
758 self.op("SCN").operands(tint).operand(name);
759 self
760 }
761
762 #[inline]
765 pub fn set_fill_color(&mut self, color: impl IntoIterator<Item = f32>) -> &mut Self {
766 self.op("scn").operands(color);
767 self
768 }
769
770 #[inline]
776 pub fn set_fill_pattern(
777 &mut self,
778 tint: impl IntoIterator<Item = f32>,
779 name: Name,
780 ) -> &mut Self {
781 self.op("scn").operands(tint).operand(name);
782 self
783 }
784
785 #[inline]
788 pub fn set_stroke_gray(&mut self, gray: f32) -> &mut Self {
789 self.op("G").operand(gray);
790 self
791 }
792
793 #[inline]
796 pub fn set_fill_gray(&mut self, gray: f32) -> &mut Self {
797 self.op("g").operand(gray);
798 self
799 }
800
801 #[inline]
804 pub fn set_stroke_rgb(&mut self, r: f32, g: f32, b: f32) -> &mut Self {
805 self.op("RG").operands([r, g, b]);
806 self
807 }
808
809 #[inline]
812 pub fn set_fill_rgb(&mut self, r: f32, g: f32, b: f32) -> &mut Self {
813 self.op("rg").operands([r, g, b]);
814 self
815 }
816
817 #[inline]
820 pub fn set_stroke_cmyk(&mut self, c: f32, m: f32, y: f32, k: f32) -> &mut Self {
821 self.op("K").operands([c, m, y, k]);
822 self
823 }
824
825 #[inline]
828 pub fn set_fill_cmyk(&mut self, c: f32, m: f32, y: f32, k: f32) -> &mut Self {
829 self.op("k").operands([c, m, y, k]);
830 self
831 }
832}
833
834#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
837pub enum ColorSpaceOperand<'a> {
838 DeviceGray,
844 DeviceRgb,
849 DeviceCmyk,
854 Pattern,
860 Named(Name<'a>),
868}
869
870impl<'a> ColorSpaceOperand<'a> {
871 #[inline]
872 pub(crate) fn to_name(self) -> Name<'a> {
873 match self {
874 Self::DeviceGray => Name(b"DeviceGray"),
875 Self::DeviceRgb => Name(b"DeviceRGB"),
876 Self::DeviceCmyk => Name(b"DeviceCMYK"),
877 Self::Pattern => Name(b"Pattern"),
878 Self::Named(name) => name,
879 }
880 }
881}
882
883impl<'a> From<Name<'a>> for ColorSpaceOperand<'a> {
884 fn from(name: Name<'a>) -> Self {
885 Self::Named(name)
886 }
887}
888
889impl Content {
891 #[inline]
893 pub fn shading(&mut self, shading: Name) -> &mut Self {
894 self.op("sh").operand(shading);
895 self
896 }
897}
898
899impl Content {
903 #[inline]
905 pub fn x_object(&mut self, name: Name) -> &mut Self {
906 self.op("Do").operand(name);
907 self
908 }
909}
910
911impl Content {
913 #[inline]
915 pub fn marked_content_point(&mut self, tag: Name) -> &mut Self {
916 self.op("MP").operand(tag);
917 self
918 }
919
920 #[inline]
922 pub fn marked_content_point_with_properties(&mut self, tag: Name) -> MarkContent<'_> {
923 let mut op = self.op("DP");
924 op.operand(tag);
925 MarkContent::start(op)
926 }
927
928 #[inline]
930 pub fn begin_marked_content(&mut self, tag: Name) -> &mut Self {
931 self.op("BMC").operand(tag);
932 self
933 }
934
935 #[inline]
937 pub fn begin_marked_content_with_properties(&mut self, tag: Name) -> MarkContent<'_> {
938 let mut op = self.op("BDC");
939 op.operand(tag);
940 MarkContent::start(op)
941 }
942
943 #[inline]
945 pub fn end_marked_content(&mut self) -> &mut Self {
946 self.op("EMC");
947 self
948 }
949}
950
951pub struct MarkContent<'a> {
953 op: Operation<'a>,
954}
955
956impl<'a> MarkContent<'a> {
957 #[inline]
958 pub(crate) fn start(op: Operation<'a>) -> Self {
959 Self { op }
960 }
961
962 #[inline]
965 pub fn properties(&mut self) -> PropertyList<'_> {
966 self.op.obj().start()
967 }
968
969 #[inline]
973 pub fn properties_named(mut self, name: Name) {
974 self.op.operand(name);
975 }
976}
977
978deref!('a, MarkContent<'a> => Operation<'a>, op);
979
980pub struct PropertyList<'a> {
983 dict: Dict<'a>,
984}
985
986writer!(PropertyList: |obj| Self { dict: obj.dict() });
987
988impl<'a> PropertyList<'a> {
989 #[inline]
991 pub fn identify(&mut self, identifier: i32) -> &mut Self {
992 self.pair(Name(b"MCID"), identifier);
993 self
994 }
995
996 #[inline]
999 pub fn actual_text(&mut self, text: impl TextStrLike) -> &mut Self {
1000 self.pair(Name(b"ActualText"), text);
1001 self
1002 }
1003
1004 #[inline]
1007 pub fn artifact(self) -> Artifact<'a> {
1008 Artifact::start_with_dict(self.dict)
1009 }
1010}
1011
1012deref!('a, PropertyList<'a> => Dict<'a>, dict);
1013
1014pub struct Artifact<'a> {
1018 dict: Dict<'a>,
1019}
1020
1021writer!(Artifact: |obj| Self::start_with_dict(obj.dict()));
1022
1023impl<'a> Artifact<'a> {
1024 #[inline]
1025 pub(crate) fn start_with_dict(dict: Dict<'a>) -> Self {
1026 Self { dict }
1027 }
1028
1029 #[inline]
1032 pub fn kind(&mut self, kind: ArtifactType) -> &mut Self {
1033 self.pair(Name(b"Type"), kind.to_name());
1034 self
1035 }
1036
1037 #[inline]
1040 pub fn subtype(&mut self, subtype: ArtifactSubtype) -> &mut Self {
1041 self.pair(Name(b"Subtype"), subtype.to_name());
1042 self
1043 }
1044
1045 #[inline]
1048 pub fn bounding_box(&mut self, bbox: Rect) -> &mut Self {
1049 self.pair(Name(b"BBox"), bbox);
1050 self
1051 }
1052
1053 #[inline]
1057 pub fn attached(
1058 &mut self,
1059 attachment: impl IntoIterator<Item = ArtifactAttachment>,
1060 ) -> &mut Self {
1061 self.insert(Name(b"Attached"))
1062 .array()
1063 .typed()
1064 .items(attachment.into_iter().map(ArtifactAttachment::to_name));
1065 self
1066 }
1067}
1068
1069deref!('a, Artifact<'a> => Dict<'a>, dict);
1070
1071#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1073pub enum ArtifactType {
1074 Pagination,
1076 Layout,
1078 Page,
1080 Background,
1082 Inline,
1084}
1085
1086impl ArtifactType {
1087 #[inline]
1088 pub(crate) fn to_name(self) -> Name<'static> {
1089 match self {
1090 Self::Pagination => Name(b"Pagination"),
1091 Self::Layout => Name(b"Layout"),
1092 Self::Page => Name(b"Page"),
1093 Self::Background => Name(b"Background"),
1094 Self::Inline => Name(b"Inline"),
1095 }
1096 }
1097}
1098
1099#[derive(Debug, Clone, PartialEq)]
1101pub enum ArtifactSubtype<'a> {
1102 Header,
1104 Footer,
1106 Watermark,
1108 PageNumber,
1110 Bates,
1112 LineNumber,
1114 Redaction,
1116 Custom(Name<'a>),
1118}
1119
1120impl<'a> ArtifactSubtype<'a> {
1121 #[inline]
1122 pub(crate) fn to_name(self) -> Name<'a> {
1123 match self {
1124 Self::Header => Name(b"Header"),
1125 Self::Footer => Name(b"Footer"),
1126 Self::Watermark => Name(b"Watermark"),
1127 Self::PageNumber => Name(b"PageNum"),
1128 Self::Bates => Name(b"Bates"),
1129 Self::LineNumber => Name(b"LineNum"),
1130 Self::Redaction => Name(b"Redaction"),
1131 Self::Custom(name) => name,
1132 }
1133 }
1134}
1135
1136#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1138#[allow(missing_docs)]
1139pub enum ArtifactAttachment {
1140 Left,
1141 Top,
1142 Right,
1143 Bottom,
1144}
1145
1146impl ArtifactAttachment {
1147 #[inline]
1148 pub(crate) fn to_name(self) -> Name<'static> {
1149 match self {
1150 Self::Left => Name(b"Left"),
1151 Self::Top => Name(b"Top"),
1152 Self::Right => Name(b"Right"),
1153 Self::Bottom => Name(b"Bottom"),
1154 }
1155 }
1156}
1157
1158impl Content {
1160 #[inline]
1162 pub fn begin_compat(&mut self) -> &mut Self {
1163 self.op("BX");
1164 self
1165 }
1166
1167 #[inline]
1169 pub fn end_compat(&mut self) -> &mut Self {
1170 self.op("EX");
1171 self
1172 }
1173}
1174
1175pub struct Resources<'a> {
1180 dict: Dict<'a>,
1181}
1182
1183writer!(Resources: |obj| Self { dict: obj.dict() });
1184
1185impl Resources<'_> {
1186 pub fn x_objects(&mut self) -> Dict<'_> {
1192 self.insert(Name(b"XObject")).dict()
1193 }
1194
1195 pub fn fonts(&mut self) -> Dict<'_> {
1202 self.insert(Name(b"Font")).dict()
1203 }
1204
1205 pub fn color_spaces(&mut self) -> Dict<'_> {
1210 self.insert(Name(b"ColorSpace")).dict()
1211 }
1212
1213 pub fn patterns(&mut self) -> Dict<'_> {
1219 self.insert(Name(b"Pattern")).dict()
1220 }
1221
1222 pub fn shadings(&mut self) -> Dict<'_> {
1227 self.insert(Name(b"Shading")).dict()
1228 }
1229
1230 pub fn ext_g_states(&mut self) -> Dict<'_> {
1235 self.insert(Name(b"ExtGState")).dict()
1236 }
1237
1238 pub fn proc_sets(&mut self, sets: impl IntoIterator<Item = ProcSet>) -> &mut Self {
1244 self.insert(Name(b"ProcSet"))
1245 .array()
1246 .items(sets.into_iter().map(ProcSet::to_name));
1247 self
1248 }
1249
1250 pub fn proc_sets_all(&mut self) -> &mut Self {
1256 self.proc_sets([
1257 ProcSet::Pdf,
1258 ProcSet::Text,
1259 ProcSet::ImageGrayscale,
1260 ProcSet::ImageColor,
1261 ProcSet::ImageIndexed,
1262 ])
1263 }
1264
1265 pub fn properties(&mut self) -> TypedDict<'_, PropertyList<'_>> {
1271 self.insert(Name(b"Properties")).dict().typed()
1272 }
1273}
1274
1275deref!('a, Resources<'a> => Dict<'a>, dict);
1276
1277#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
1282pub enum ProcSet {
1283 Pdf,
1285 Text,
1287 ImageGrayscale,
1289 ImageColor,
1291 ImageIndexed,
1293}
1294
1295impl ProcSet {
1296 pub(crate) fn to_name(self) -> Name<'static> {
1297 match self {
1298 ProcSet::Pdf => Name(b"PDF"),
1299 ProcSet::Text => Name(b"Text"),
1300 ProcSet::ImageGrayscale => Name(b"ImageB"),
1301 ProcSet::ImageColor => Name(b"ImageC"),
1302 ProcSet::ImageIndexed => Name(b"ImageI"),
1303 }
1304 }
1305}
1306
1307pub struct ExtGraphicsState<'a> {
1312 dict: Dict<'a>,
1313}
1314
1315writer!(ExtGraphicsState: |obj| {
1316 let mut dict = obj.dict();
1317 dict.pair(Name(b"Type"), Name(b"ExtGState"));
1318 Self { dict }
1319});
1320
1321impl ExtGraphicsState<'_> {
1322 pub fn line_width(&mut self, width: f32) -> &mut Self {
1324 self.pair(Name(b"LW"), width);
1325 self
1326 }
1327
1328 pub fn line_cap(&mut self, cap: LineCapStyle) -> &mut Self {
1330 self.pair(Name(b"LC"), cap.to_int());
1331 self
1332 }
1333
1334 pub fn line_join(&mut self, join: LineJoinStyle) -> &mut Self {
1336 self.pair(Name(b"LJ"), join.to_int());
1337 self
1338 }
1339
1340 pub fn miter_limit(&mut self, limit: f32) -> &mut Self {
1342 self.pair(Name(b"ML"), limit);
1343 self
1344 }
1345
1346 pub fn dash_pattern(
1348 &mut self,
1349 pattern: impl IntoIterator<Item = f32>,
1350 phase: f32,
1351 ) -> &mut Self {
1352 let mut array = self.insert(Name(b"D")).array();
1353 array.push().array().items(pattern);
1354 array.item(phase);
1355 array.finish();
1356 self
1357 }
1358
1359 pub fn rendering_intent(&mut self, intent: RenderingIntent) -> &mut Self {
1361 self.pair(Name(b"RI"), intent.to_name());
1362 self
1363 }
1364
1365 pub fn overprint(&mut self, overprint: bool) -> &mut Self {
1369 self.pair(Name(b"OP"), overprint);
1370 self
1371 }
1372
1373 pub fn overprint_fill(&mut self, overprint: bool) -> &mut Self {
1376 self.pair(Name(b"op"), overprint);
1377 self
1378 }
1379
1380 pub fn overprint_mode(&mut self, mode: OverprintMode) -> &mut Self {
1385 self.pair(Name(b"OPM"), mode.to_int());
1386 self
1387 }
1388
1389 pub fn font(&mut self, font: Name, size: f32) -> &mut Self {
1391 let mut array = self.insert(Name(b"Font")).array();
1392 array.item(font);
1393 array.item(size);
1394 array.finish();
1395 self
1396 }
1397
1398 pub fn black_generation(&mut self, func: Ref) -> &mut Self {
1400 self.pair(Name(b"BG"), func);
1401 self
1402 }
1403
1404 pub fn black_generation_default(&mut self) -> &mut Self {
1408 self.pair(Name(b"BG2"), Name(b"Default"));
1409 self
1410 }
1411
1412 pub fn undercolor_removal(&mut self, func: Ref) -> &mut Self {
1414 self.pair(Name(b"UCR"), func);
1415 self
1416 }
1417
1418 pub fn undercolor_removal_default(&mut self) -> &mut Self {
1422 self.pair(Name(b"UCR2"), Name(b"Default"));
1423 self
1424 }
1425
1426 pub fn transfer(&mut self, func: Ref) -> &mut Self {
1430 self.pair(Name(b"TR"), func);
1431 self
1432 }
1433
1434 pub fn transfer_default(&mut self) -> &mut Self {
1437 self.pair(Name(b"TR2"), Name(b"Default"));
1438 self
1439 }
1440
1441 pub fn halftone(&mut self, ht: Ref) -> &mut Self {
1445 self.pair(Name(b"HT"), ht);
1446 self
1447 }
1448
1449 pub fn halftone_default(&mut self) -> &mut Self {
1452 self.pair(Name(b"HT"), Name(b"Default"));
1453 self
1454 }
1455
1456 pub fn flatness(&mut self, tolerance: f32) -> &mut Self {
1460 self.pair(Name(b"FL"), tolerance);
1461 self
1462 }
1463
1464 pub fn smoothness(&mut self, tolerance: f32) -> &mut Self {
1466 self.pair(Name(b"SM"), tolerance);
1467 self
1468 }
1469
1470 pub fn stroke_adjustment(&mut self, adjust: bool) -> &mut Self {
1472 self.pair(Name(b"SA"), adjust);
1473 self
1474 }
1475
1476 pub fn blend_mode(&mut self, mode: BlendMode) -> &mut Self {
1480 self.pair(Name(b"BM"), mode.to_name());
1481 self
1482 }
1483
1484 pub fn soft_mask(&mut self) -> SoftMask<'_> {
1488 self.insert(Name(b"SMask")).start()
1489 }
1490
1491 pub fn soft_mask_name(&mut self, mask: Name) -> &mut Self {
1495 self.pair(Name(b"SMask"), mask);
1496 self
1497 }
1498
1499 pub fn stroking_alpha(&mut self, alpha: f32) -> &mut Self {
1503 self.pair(Name(b"CA"), alpha);
1504 self
1505 }
1506
1507 pub fn non_stroking_alpha(&mut self, alpha: f32) -> &mut Self {
1512 self.pair(Name(b"ca"), alpha);
1513 self
1514 }
1515
1516 pub fn alpha_source(&mut self, source: bool) -> &mut Self {
1520 self.pair(Name(b"AIS"), source);
1521 self
1522 }
1523
1524 pub fn text_knockout(&mut self, knockout: bool) -> &mut Self {
1526 self.pair(Name(b"TK"), knockout);
1527 self
1528 }
1529}
1530
1531deref!('a, ExtGraphicsState<'a> => Dict<'a>, dict);
1532
1533#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
1535#[allow(missing_docs)]
1536pub enum BlendMode {
1537 #[default]
1538 Normal,
1539 Multiply,
1540 Screen,
1541 Overlay,
1542 Darken,
1543 Lighten,
1544 ColorDodge,
1545 ColorBurn,
1546 HardLight,
1547 SoftLight,
1548 Difference,
1549 Exclusion,
1550 Hue,
1551 Saturation,
1552 Color,
1553 Luminosity,
1554}
1555
1556impl BlendMode {
1557 pub(crate) fn to_name(self) -> Name<'static> {
1558 match self {
1559 BlendMode::Normal => Name(b"Normal"),
1560 BlendMode::Multiply => Name(b"Multiply"),
1561 BlendMode::Screen => Name(b"Screen"),
1562 BlendMode::Overlay => Name(b"Overlay"),
1563 BlendMode::Darken => Name(b"Darken"),
1564 BlendMode::Lighten => Name(b"Lighten"),
1565 BlendMode::ColorDodge => Name(b"ColorDodge"),
1566 BlendMode::ColorBurn => Name(b"ColorBurn"),
1567 BlendMode::HardLight => Name(b"HardLight"),
1568 BlendMode::SoftLight => Name(b"SoftLight"),
1569 BlendMode::Difference => Name(b"Difference"),
1570 BlendMode::Exclusion => Name(b"Exclusion"),
1571 BlendMode::Hue => Name(b"Hue"),
1572 BlendMode::Saturation => Name(b"Saturation"),
1573 BlendMode::Color => Name(b"Color"),
1574 BlendMode::Luminosity => Name(b"Luminosity"),
1575 }
1576 }
1577}
1578
1579#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
1581pub enum OverprintMode {
1582 #[default]
1585 OverrideAllColorants,
1586 IgnoreZeroChannel,
1593}
1594
1595impl OverprintMode {
1596 pub(crate) fn to_int(self) -> i32 {
1597 match self {
1598 OverprintMode::OverrideAllColorants => 0,
1599 OverprintMode::IgnoreZeroChannel => 1,
1600 }
1601 }
1602}
1603
1604pub struct SoftMask<'a> {
1608 dict: Dict<'a>,
1609}
1610
1611writer!(SoftMask: |obj| {
1612 let mut dict = obj.dict();
1613 dict.pair(Name(b"Type"), Name(b"Mask"));
1614 Self { dict }
1615});
1616
1617impl SoftMask<'_> {
1618 pub fn subtype(&mut self, subtype: MaskType) -> &mut Self {
1620 self.pair(Name(b"S"), subtype.to_name());
1621 self
1622 }
1623
1624 pub fn group(&mut self, group: Ref) -> &mut Self {
1628 self.pair(Name(b"G"), group);
1629 self
1630 }
1631
1632 pub fn backdrop(&mut self, color: impl IntoIterator<Item = f32>) -> &mut Self {
1636 self.insert(Name(b"BC")).array().items(color);
1637 self
1638 }
1639
1640 pub fn transfer_function(&mut self, function: Ref) -> &mut Self {
1643 self.pair(Name(b"TR"), function);
1644 self
1645 }
1646}
1647
1648deref!('a, SoftMask<'a> => Dict<'a>, dict);
1649
1650#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
1652pub enum MaskType {
1653 Alpha,
1655 Luminosity,
1658}
1659
1660impl MaskType {
1661 pub(crate) fn to_name(self) -> Name<'static> {
1662 match self {
1663 MaskType::Alpha => Name(b"Alpha"),
1664 MaskType::Luminosity => Name(b"Luminosity"),
1665 }
1666 }
1667}
1668
1669#[cfg(test)]
1670mod tests {
1671 use super::*;
1672
1673 #[test]
1674 fn test_content_encoding() {
1675 let mut content = Content::new();
1676 content
1677 .save_state()
1678 .rect(1.0, 2.0, 3.0, 4.0)
1679 .fill_nonzero()
1680 .set_dash_pattern([7.0, 2.0], 4.0)
1681 .x_object(Name(b"MyImage"))
1682 .set_fill_pattern([2.0, 3.5], Name(b"MyPattern"))
1683 .restore_state();
1684
1685 assert_eq!(
1686 content.finish().into_vec(),
1687 b"q\n1 2 3 4 re\nf\n[7 2] 4 d\n/MyImage Do\n2 3.5 /MyPattern scn\nQ"
1688 );
1689 }
1690
1691 #[test]
1692 fn test_content_text() {
1693 let mut content = Content::new();
1694
1695 content.set_font(Name(b"F1"), 12.0);
1696 content.begin_text();
1697 content.show_positioned().items();
1698 content
1699 .show_positioned()
1700 .items()
1701 .show(Str(b"AB"))
1702 .adjust(2.0)
1703 .show(Str(b"CD"));
1704 content.end_text();
1705
1706 assert_eq!(
1707 content.finish().into_vec(),
1708 b"/F1 12 Tf\nBT\n[] TJ\n[(AB) 2 (CD)] TJ\nET"
1709 );
1710 }
1711}