1use std::io::Write;
7
8use flate2::write::ZlibEncoder;
9use flate2::Compression;
10use pdfboss_core::crypt::Sha256;
11use pdfboss_core::object::decode_text_string;
12use pdfboss_core::xref::{parse_section_at, startxref};
13use pdfboss_core::{Dict, Document, FastMap, Name, ObjRef, Object, Page, Stream, XrefKind};
14
15use crate::error::{Error, Result};
16use crate::importer::{rect_array, Importer};
17use crate::pdf::{text_string, Date, Metadata};
18use crate::ser::{serialize_dict, serialize_object};
19use crate::writer::{WriteOptions, Writer, XrefStyle};
20
21const FORM_NAME: &str = "PdfbossWatermark";
25
26fn free_form_name(base: &Document, pages: &[Page]) -> Result<String> {
36 let mut candidate = FORM_NAME.to_string();
37 let mut next = 2;
38 while xobject_name_taken(base, pages, &candidate)? {
39 candidate = format!("{FORM_NAME}{next}");
40 next += 1;
41 }
42 Ok(candidate)
43}
44
45fn xobject_name_taken(base: &Document, pages: &[Page], candidate: &str) -> Result<bool> {
53 for page in pages {
54 if page.object_ref().is_none() {
55 continue;
56 }
57 let Some(existing) = page.resources.get("XObject") else {
58 continue;
59 };
60 let existing = base.resolve(existing).map_err(core_error)?;
61 let Some(dict) = existing.as_dict() else {
62 continue;
63 };
64 if dict.get(candidate).is_some() {
65 return Ok(true);
66 }
67 }
68 Ok(false)
69}
70
71fn fetch_pages(base: &Document) -> Result<Vec<Page>> {
76 (0..base.page_count())
77 .map(|index| base.page(index).map_err(core_error))
78 .collect()
79}
80
81fn wrapper_streams(form_name: &str, under: bool) -> (Vec<u8>, Vec<u8>) {
85 if under {
86 return (
87 format!("q /{form_name} Do Q\nq\n").into_bytes(),
88 b"Q\n".to_vec(),
89 );
90 }
91 (
92 b"q\n".to_vec(),
93 format!("Q\nq /{form_name} Do Q\n").into_bytes(),
94 )
95}
96
97fn watermark_rewrite_placed(
101 base: &Document,
102 overlay: &Document,
103 options: WriteOptions,
104 under: bool,
105) -> Result<Vec<u8>> {
106 let pages = fetch_pages(base)?;
107 let form_name = free_form_name(base, &pages)?;
108 let mut writer = Writer::new(options);
109 let (prefix_bytes, suffix_bytes) = wrapper_streams(&form_name, under);
110 let prefix = writer.put_stream_raw(Dict::new(), prefix_bytes);
111 let suffix = writer.put_stream_raw(Dict::new(), suffix_bytes);
112 let form = overlay_form(&mut writer, overlay)?;
113
114 let trailer = &base.xref().trailer;
115 let root = trailer.get_ref("Root").ok_or(Error::MissingRoot)?;
116 let mut importer = Importer::new(&mut writer, base)?;
117 let new_root = importer.reference(root);
118 let new_info = trailer.get_ref("Info").map(|info| importer.reference(info));
119 for page in &pages {
120 let Some(page_ref) = page.object_ref() else {
121 continue;
122 };
123 let dict = marked_page_dict(&mut importer, base, page, form, prefix, suffix, &form_name)?;
124 importer.substitute(page_ref, dict);
125 }
126 importer.finish()?;
127 if let Some(new_info) = new_info {
128 writer.set_info(new_info);
129 }
130 writer.finish(new_root)
131}
132
133pub fn watermark_with(
147 base: &Document,
148 overlay: &Document,
149 options: WriteOptions,
150) -> Result<Vec<u8>> {
151 watermark_rewrite_placed(base, overlay, options, false)
152}
153
154pub fn watermark_under_with(
164 base: &Document,
165 overlay: &Document,
166 options: WriteOptions,
167) -> Result<Vec<u8>> {
168 watermark_rewrite_placed(base, overlay, options, true)
169}
170
171fn marked_page_dict(
176 importer: &mut Importer,
177 base: &Document,
178 page: &Page,
179 form: ObjRef,
180 prefix: ObjRef,
181 suffix: ObjRef,
182 form_name: &str,
183) -> Result<Object> {
184 let mut dict = importer.copy_dict(page.dict())?;
185 let mut resources = importer.copy_dict(&page.resources)?;
186 let mut xobjects = match page.resources.get("XObject") {
187 Some(existing) => {
188 let existing = base.resolve(existing).map_err(core_error)?;
189 match existing.as_dict() {
190 Some(d) => importer.copy_dict(d)?,
191 None => Dict::new(),
192 }
193 }
194 None => Dict::new(),
195 };
196 xobjects.insert(name(form_name), Object::Ref(form));
197 resources.insert(name("XObject"), Object::Dict(xobjects));
198 dict.insert(name("Resources"), Object::Dict(resources));
199 dict.insert(name("Type"), Object::Name(name("Page")));
200 let mut contents = vec![Object::Ref(prefix)];
201 match page.dict().get("Contents") {
202 Some(Object::Array(items)) => {
203 for item in items {
204 contents.push(importer.copy(item)?);
205 }
206 }
207 Some(Object::Ref(r)) => match base.get(*r).map_err(core_error)? {
208 Object::Array(items) => {
209 for item in &items {
210 contents.push(importer.copy(item)?);
211 }
212 }
213 _ => contents.push(Object::Ref(importer.reference(*r))),
214 },
215 _ => {}
216 }
217 contents.push(Object::Ref(suffix));
218 dict.insert(name("Contents"), Object::Array(contents));
219 Ok(Object::Dict(dict))
220}
221
222fn overlay_form(writer: &mut Writer, overlay: &Document) -> Result<ObjRef> {
226 let page = overlay.page(0).map_err(core_error)?;
227 let content = page.content(overlay).map_err(core_error)?;
228 let resources = {
229 let mut importer = Importer::new(writer, overlay)?;
230 let resources = importer.copy_dict(&page.resources)?;
231 importer.finish()?;
232 resources
233 };
234 let mut dict = Dict::new();
235 dict.insert(name("Type"), Object::Name(name("XObject")));
236 dict.insert(name("Subtype"), Object::Name(name("Form")));
237 dict.insert(name("FormType"), Object::Int(1));
238 dict.insert(name("BBox"), rect_array(page.media_box));
239 dict.insert(name("Resources"), Object::Dict(resources));
240 dict.insert(name("Filter"), Object::Name(name("FlateDecode")));
241 let form = writer.reserve();
242 writer.fill(
243 form,
244 Object::Stream(Stream {
245 dict,
246 data: deflate(&content),
247 }),
248 )?;
249 Ok(form)
250}
251
252fn watermark_placed(base: &Document, overlay: &Document, under: bool) -> Result<Vec<u8>> {
265 let pages = fetch_pages(base)?;
266 let form_name = free_form_name(base, &pages)?;
267 let mut update = Update::new(base)?;
268 let form = update.overlay.import_form(overlay)?;
269 let (prefix_bytes, suffix_bytes) = wrapper_streams(&form_name, under);
270 let prefix = update
271 .overlay
272 .put(Object::Stream(plain_stream(prefix_bytes)));
273 let suffix = update
274 .overlay
275 .put(Object::Stream(plain_stream(suffix_bytes)));
276 for page in &pages {
277 let Some(page_ref) = page.object_ref() else {
278 continue;
279 };
280 let mut dict = page.dict().clone();
281 let mut resources = page.resources.clone();
282 let mut xobjects = match resources.get("XObject") {
283 Some(existing) => base
284 .resolve(existing)
285 .map_err(core_error)?
286 .as_dict()
287 .cloned()
288 .unwrap_or_default(),
289 None => Dict::new(),
290 };
291 xobjects.insert(name(&form_name), Object::Ref(form));
292 resources.insert(name("XObject"), Object::Dict(xobjects));
293 dict.insert(name("Resources"), Object::Dict(resources));
294 let mut contents = vec![Object::Ref(prefix)];
295 match dict.get("Contents").cloned() {
296 Some(Object::Array(items)) => contents.extend(items),
297 Some(Object::Ref(r)) => match base.get(r).map_err(core_error)? {
298 Object::Array(items) => contents.extend(items),
299 _ => contents.push(Object::Ref(r)),
300 },
301 _ => {}
302 }
303 contents.push(Object::Ref(suffix));
304 dict.insert(name("Contents"), Object::Array(contents));
305 update.set(page_ref, Object::Dict(dict));
306 }
307 update.bytes()
308}
309
310pub fn watermark(base: &Document, overlay: &Document) -> Result<Vec<u8>> {
329 watermark_placed(base, overlay, false)
330}
331
332pub fn watermark_under(base: &Document, overlay: &Document) -> Result<Vec<u8>> {
342 watermark_placed(base, overlay, true)
343}
344
345pub fn rotate_pages(update: &mut Update, pages: &[usize], by: i32) -> Result<()> {
355 if by % 90 != 0 {
356 return Err(Error::Other(
357 "rotation must be a multiple of 90 degrees".to_string(),
358 ));
359 }
360 for &index in pages {
361 let page = update.doc.page(index).map_err(core_error)?;
362 let Some(page_ref) = page.object_ref() else {
363 return Err(Error::Other(format!(
364 "page {} is inlined into /Kids and cannot be edited in place",
365 index + 1
366 )));
367 };
368 let mut dict = page.dict().clone();
369 let rotate = (page.rotate + by).rem_euclid(360);
370 dict.insert(name("Rotate"), Object::Int(i64::from(rotate)));
371 update.set(page_ref, Object::Dict(dict));
372 }
373 Ok(())
374}
375
376#[derive(Debug, Clone)]
380pub struct OverlayBase {
381 pub prev: u64,
384 pub kind: XrefStyle,
389 pub size: u32,
392 pub root: ObjRef,
394 pub info: Option<ObjRef>,
396 pub id: Option<Object>,
398}
399
400impl OverlayBase {
401 pub fn from_document(doc: &Document) -> Result<OverlayBase> {
413 if doc.is_encrypted() {
414 return Err(Error::EncryptedBase);
415 }
416 let trailer = &doc.xref().trailer;
417 let root = trailer.get_ref("Root").ok_or(Error::MissingRoot)?;
418 let (prev, kind) = match doc.xref().newest_section() {
419 Some(section) => (section.offset, xref_style(section.kind)),
420 None => {
421 let offset = startxref(doc.bytes()).ok_or(Error::MissingStartxref)?;
422 let kind = xref_style(
423 parse_section_at(doc.bytes(), offset)
424 .map_err(core_error)?
425 .kind,
426 );
427 (offset as u64, kind)
428 }
429 };
430 let highest = doc.xref().iter().map(|(num, _)| num).max().unwrap_or(0);
431 let declared = trailer.get_int("Size").unwrap_or(0).max(0) as u32;
432 Ok(OverlayBase {
433 prev,
434 kind,
435 size: declared.max(highest + 1),
436 root,
437 info: trailer.get_ref("Info"),
438 id: trailer.get("ID").cloned(),
439 })
440 }
441}
442
443fn xref_style(kind: XrefKind) -> XrefStyle {
446 match kind {
447 XrefKind::Table => XrefStyle::Table,
448 XrefKind::Stream => XrefStyle::Stream,
449 }
450}
451
452#[derive(Debug, Clone)]
455enum Change {
456 Set(Object),
457 Free,
458}
459
460#[derive(Debug, Clone)]
463pub struct Overlay {
464 base: OverlayBase,
465 next: u32,
466 objects: Vec<(ObjRef, Change)>,
467 imported: FastMap<ObjRef, ObjRef>,
468 info: Option<ObjRef>,
469}
470
471impl Overlay {
472 pub fn new(base: OverlayBase) -> Overlay {
475 let next = base.size;
476 Overlay {
477 base,
478 next,
479 objects: Vec::new(),
480 imported: FastMap::default(),
481 info: None,
482 }
483 }
484
485 pub fn set(&mut self, r: ObjRef, obj: Object) {
494 if r.num == 0 {
495 return;
496 }
497 self.next = self.next.max(r.num.saturating_add(1));
498 self.objects.push((r, Change::Set(obj)));
499 }
500
501 pub fn remove(&mut self, r: ObjRef) {
509 if r.num == 0 {
510 return;
511 }
512 self.next = self.next.max(r.num.saturating_add(1));
513 let gen = r.gen.saturating_add(1);
514 self.objects
515 .push((ObjRef { num: r.num, gen }, Change::Free));
516 }
517
518 pub fn reserve(&mut self) -> ObjRef {
521 let r = ObjRef {
522 num: self.next,
523 gen: 0,
524 };
525 self.next += 1;
526 r
527 }
528
529 pub fn put(&mut self, obj: Object) -> ObjRef {
531 let r = self.reserve();
532 self.set(r, obj);
533 r
534 }
535
536 pub fn set_info(&mut self, r: ObjRef) {
539 self.info = Some(r);
540 }
541
542 pub fn is_empty(&self) -> bool {
544 self.objects.is_empty()
545 }
546
547 pub fn section(&self, start: u64) -> Result<Vec<u8>> {
555 if self.is_empty() {
556 return Err(Error::EmptyUpdate);
557 }
558 let mut last: FastMap<u32, usize> = FastMap::default();
559 for (index, (r, _)) in self.objects.iter().enumerate() {
560 last.insert(r.num, index);
561 }
562 let mut winners: Vec<usize> = last.into_values().collect();
563 winners.sort_by_key(|&index| self.objects[index].0.num);
564 let mut out = Vec::new();
565 let mut rows: Vec<Row> = Vec::with_capacity(winners.len() + 1);
566 let mut freed: Vec<ObjRef> = Vec::new();
567 for index in winners {
568 let (r, change) = &self.objects[index];
569 match change {
570 Change::Set(obj) => {
571 rows.push(Row::InFile(*r, start as usize + out.len()));
572 write_indirect(&mut out, *r, obj)?;
573 }
574 Change::Free => freed.push(*r),
575 }
576 }
577 if !freed.is_empty() {
578 freed.sort_by_key(|r| r.num);
579 let head = freed.first().map_or(0, |r| r.num);
580 rows.push(Row::Free {
581 num: 0,
582 gen: 65535,
583 next: head,
584 });
585 for (index, r) in freed.iter().enumerate() {
586 let next = freed.get(index + 1).map_or(0, |n| n.num);
587 rows.push(Row::Free {
588 num: r.num,
589 gen: r.gen,
590 next,
591 });
592 }
593 }
594 rows.sort_by_key(Row::num);
595
596 let mut trailer = Dict::new();
597 trailer.insert(name("Root"), Object::Ref(self.base.root));
598 if let Some(info) = self.info.or(self.base.info) {
599 trailer.insert(name("Info"), Object::Ref(info));
600 }
601 if let Some(id) = rotated_id(&self.base, &out, &freed) {
602 trailer.insert(name("ID"), id);
603 }
604 trailer.insert(name("Prev"), Object::Int(self.base.prev as i64));
605 match self.base.kind {
606 XrefStyle::Stream => finish_stream(&mut out, start, rows, trailer, self.next)?,
607 XrefStyle::Table => finish_table(&mut out, start, &rows, trailer, self.next)?,
608 }
609 Ok(out)
610 }
611
612 pub(crate) fn import_form(&mut self, overlay: &Document) -> Result<ObjRef> {
621 if overlay.is_encrypted() {
622 return Err(Error::EncryptedBase);
623 }
624 let page = overlay.page(0).map_err(core_error)?;
625 let content = page.content(overlay).map_err(core_error)?;
626 let resources = self.import_object(overlay, &Object::Dict(page.resources.clone()))?;
627 let mut dict = Dict::new();
628 dict.insert(name("Type"), Object::Name(name("XObject")));
629 dict.insert(name("Subtype"), Object::Name(name("Form")));
630 dict.insert(name("FormType"), Object::Int(1));
631 dict.insert(name("BBox"), rect_array(page.media_box));
632 dict.insert(name("Resources"), resources);
633 dict.insert(name("Filter"), Object::Name(name("FlateDecode")));
634 Ok(self.put(Object::Stream(Stream {
635 dict,
636 data: deflate(&content),
637 })))
638 }
639
640 pub(crate) fn import_object(&mut self, source: &Document, obj: &Object) -> Result<Object> {
645 Ok(match obj {
646 Object::Ref(r) => {
647 if let Some(copied) = self.imported.get(r) {
648 return Ok(Object::Ref(*copied));
649 }
650 let copied = self.reserve();
651 self.imported.insert(*r, copied);
652 let body = source.get(*r).map_err(core_error)?;
653 let body = self.import_object(source, &body)?;
654 self.objects.push((copied, Change::Set(body)));
655 Object::Ref(copied)
656 }
657 Object::Dict(d) => Object::Dict(self.import_dict(source, d)?),
658 Object::Array(items) => Object::Array(
659 items
660 .iter()
661 .map(|item| self.import_object(source, item))
662 .collect::<Result<Vec<Object>>>()?,
663 ),
664 Object::Stream(s) => {
665 let mut dict = s.dict.clone();
666 dict.remove("Length");
667 Object::Stream(Stream {
668 dict: self.import_dict(source, &dict)?,
669 data: s.data.clone(),
670 })
671 }
672 other => other.clone(),
673 })
674 }
675
676 pub(crate) fn import_dict(&mut self, source: &Document, dict: &Dict) -> Result<Dict> {
677 let mut out = Dict::new();
678 for (key, value) in dict.iter() {
679 out.insert(key.clone(), self.import_object(source, value)?);
680 }
681 Ok(out)
682 }
683}
684
685pub fn set_metadata_with(
701 overlay: &mut Overlay,
702 existing_info: Option<(ObjRef, Dict)>,
703 xmp_ref: Option<ObjRef>,
704 meta: Metadata,
705) -> Result<()> {
706 let (target, existing_dict) = match existing_info {
707 Some((r, dict)) => (r, Some(dict)),
708 None => (overlay.reserve(), None),
709 };
710 let (dict, merged) = merge_metadata(existing_dict, &meta);
711 overlay.set(target, Object::Dict(dict));
712 overlay.set_info(target);
713 let Some(xmp_ref) = xmp_ref else {
714 return Ok(());
715 };
716 overlay.set(xmp_ref, xmp_metadata_stream(&merged));
717 Ok(())
718}
719
720pub(crate) fn merge_metadata(existing: Option<Dict>, meta: &Metadata) -> (Dict, Metadata) {
731 let mut dict = existing.unwrap_or_default();
732 apply_metadata_fields(&mut dict, meta);
733 let merged = metadata_from_info(&dict);
734 (dict, merged)
735}
736
737pub(crate) fn xmp_metadata_stream(meta: &Metadata) -> Object {
741 let mut xmp_dict = Dict::new();
742 xmp_dict.insert(name("Type"), Object::Name(name("Metadata")));
743 xmp_dict.insert(name("Subtype"), Object::Name(name("XML")));
744 Object::Stream(Stream {
745 dict: xmp_dict,
746 data: crate::xmp::packet(meta),
747 })
748}
749
750pub(crate) fn resolve_dict(doc: &Document, dict: &Dict) -> Dict {
757 let mut out = Dict::new();
758 for (key, value) in dict.iter() {
759 let resolved = doc.resolve(value).unwrap_or_else(|_| value.clone());
760 out.insert(key.clone(), resolved);
761 }
762 out
763}
764
765pub(crate) fn catalog_metadata_ref(doc: &Document, root: ObjRef) -> Option<ObjRef> {
769 let catalog = doc.get(root).ok()?;
770 match catalog.as_dict()?.get("Metadata")? {
771 Object::Ref(r) => Some(*r),
772 _ => None,
773 }
774}
775
776fn apply_metadata_fields(dict: &mut Dict, meta: &Metadata) {
779 let texts = [
780 ("Title", &meta.title),
781 ("Author", &meta.author),
782 ("Subject", &meta.subject),
783 ("Keywords", &meta.keywords),
784 ("Creator", &meta.creator),
785 ("Producer", &meta.producer),
786 ];
787 for (key, value) in texts {
788 if let Some(value) = value {
789 dict.insert(name(key), text_string(value));
790 }
791 }
792 let dates = [
793 ("CreationDate", meta.creation_date),
794 ("ModDate", meta.modification_date),
795 ];
796 for (key, value) in dates {
797 if let Some(date) = value {
798 dict.insert(name(key), Object::String(date.to_pdf_string().into_bytes()));
799 }
800 }
801}
802
803fn metadata_from_info(dict: &Dict) -> Metadata {
807 Metadata {
808 title: info_text(dict, "Title"),
809 author: info_text(dict, "Author"),
810 subject: info_text(dict, "Subject"),
811 keywords: info_text(dict, "Keywords"),
812 creator: info_text(dict, "Creator"),
813 producer: info_text(dict, "Producer"),
814 creation_date: info_date(dict, "CreationDate"),
815 modification_date: info_date(dict, "ModDate"),
816 }
817}
818
819fn info_text(dict: &Dict, key: &str) -> Option<String> {
821 Some(decode_text_string(dict.get(key)?.as_str_bytes()?))
822}
823
824fn info_date(dict: &Dict, key: &str) -> Option<Date> {
827 Date::parse_pdf(&decode_text_string(dict.get(key)?.as_str_bytes()?))
828}
829
830pub fn start_offset(base: &[u8]) -> (u64, bool) {
835 let pad = !matches!(base.last(), Some(b'\n') | Some(b'\r'));
836 (base.len() as u64 + u64::from(pad), pad)
837}
838
839pub struct Update<'a> {
841 doc: &'a Document,
842 overlay: Overlay,
843}
844
845impl<'a> Update<'a> {
846 pub fn new(doc: &'a Document) -> Result<Update<'a>> {
849 let base = OverlayBase::from_document(doc)?;
850 Ok(Update {
851 doc,
852 overlay: Overlay::new(base),
853 })
854 }
855
856 pub fn set(&mut self, r: ObjRef, obj: Object) {
859 self.overlay.set(r, obj);
860 }
861
862 pub fn remove(&mut self, r: ObjRef) {
864 self.overlay.remove(r);
865 }
866
867 pub fn reserve(&mut self) -> ObjRef {
870 self.overlay.reserve()
871 }
872
873 pub fn overlay(&self) -> &Overlay {
875 &self.overlay
876 }
877
878 pub fn set_metadata(&mut self, meta: Metadata) -> Result<()> {
887 let info_ref = self.overlay.info.or(self.overlay.base.info);
888 let existing_info = info_ref.and_then(|r| {
889 let dict = self.doc.get(r).ok()?.as_dict()?.clone();
890 Some((r, resolve_dict(self.doc, &dict)))
891 });
892 let xmp_ref = catalog_metadata_ref(self.doc, self.overlay.base.root);
893 set_metadata_with(&mut self.overlay, existing_info, xmp_ref, meta)
894 }
895
896 fn parts(&self) -> Result<(&[u8], bool, Vec<u8>)> {
901 let base = self.doc.bytes();
902 let (start, pad) = start_offset(base);
903 let section = self.overlay.section(start)?;
904 Ok((base, pad, section))
905 }
906
907 pub fn append_into(&self, mut out: impl std::io::Write) -> Result<()> {
912 let (base, pad, section) = self.parts()?;
913 out.write_all(base)?;
914 if pad {
915 out.write_all(b"\n")?;
916 }
917 out.write_all(§ion)?;
918 Ok(())
919 }
920
921 pub fn save(&self, path: impl AsRef<std::path::Path>) -> Result<()> {
925 let (base, pad, section) = self.parts()?;
926 let mut file = std::fs::File::create(path)?;
927 file.write_all(base)?;
928 if pad {
929 file.write_all(b"\n")?;
930 }
931 file.write_all(§ion)?;
932 Ok(())
933 }
934
935 pub fn bytes(&self) -> Result<Vec<u8>> {
937 let mut out = Vec::new();
938 self.append_into(&mut out)?;
939 Ok(out)
940 }
941}
942
943#[derive(Debug, Clone, Copy)]
947enum Row {
948 InFile(ObjRef, usize),
949 Free { num: u32, gen: u16, next: u32 },
950}
951
952impl Row {
953 fn num(&self) -> u32 {
954 match self {
955 Row::InFile(r, _) => r.num,
956 Row::Free { num, .. } => *num,
957 }
958 }
959}
960
961fn contiguous_runs(rows: &[Row]) -> Vec<(usize, usize)> {
966 let mut runs = Vec::new();
967 let mut begin = 0;
968 while begin < rows.len() {
969 let mut end = begin + 1;
970 while end < rows.len() && rows[end].num() == rows[end - 1].num() + 1 {
971 end += 1;
972 }
973 runs.push((begin, end - begin));
974 begin = end;
975 }
976 runs
977}
978
979fn rotated_id(base: &OverlayBase, body: &[u8], freed: &[ObjRef]) -> Option<Object> {
989 let Some(Object::Array(halves)) = &base.id else {
990 return None;
991 };
992 let Some(Object::String(first)) = halves.first() else {
993 return None;
994 };
995 let mut hasher = Sha256::new();
996 hasher.update(first);
997 hasher.update(&base.prev.to_le_bytes());
998 hasher.update(body);
999 for r in freed {
1000 hasher.update(&r.num.to_le_bytes());
1001 hasher.update(&r.gen.to_le_bytes());
1002 }
1003 let digest = hasher.finalize();
1004 Some(Object::Array(vec![
1005 Object::String(first.clone()),
1006 Object::String(digest[..16].to_vec()),
1007 ]))
1008}
1009
1010fn finish_stream(
1014 out: &mut Vec<u8>,
1015 start: u64,
1016 mut rows: Vec<Row>,
1017 mut dict: Dict,
1018 mut next: u32,
1019) -> Result<()> {
1020 let xref_ref = ObjRef { num: next, gen: 0 };
1021 next += 1;
1022 let xref_offset = start as usize + out.len();
1023 rows.push(Row::InFile(xref_ref, xref_offset));
1024 rows.sort_by_key(Row::num);
1025 let runs = contiguous_runs(&rows);
1026 let mut index = Vec::with_capacity(runs.len() * 2);
1027 for (begin, len) in runs {
1028 index.push(Object::Int(i64::from(rows[begin].num())));
1029 index.push(Object::Int(len as i64));
1030 }
1031 let mut data = Vec::with_capacity(rows.len() * 7);
1032 for row in &rows {
1033 match row {
1034 Row::InFile(r, offset) => {
1035 data.push(1);
1036 data.extend_from_slice(&field_offset(*offset)?.to_be_bytes());
1037 data.extend_from_slice(&r.gen.to_be_bytes());
1038 }
1039 Row::Free {
1040 gen,
1041 next: free_next,
1042 ..
1043 } => {
1044 data.push(0);
1045 data.extend_from_slice(&free_next.to_be_bytes());
1046 data.extend_from_slice(&gen.to_be_bytes());
1047 }
1048 }
1049 }
1050 dict.insert(name("Type"), Object::Name(name("XRef")));
1051 dict.insert(name("Size"), Object::Int(i64::from(next)));
1052 dict.insert(
1053 name("W"),
1054 Object::Array(vec![Object::Int(1), Object::Int(4), Object::Int(2)]),
1055 );
1056 dict.insert(name("Index"), Object::Array(index));
1057 write_indirect(out, xref_ref, &Object::Stream(Stream { dict, data }))?;
1058 out.extend_from_slice(format!("startxref\n{xref_offset}\n%%EOF\n").as_bytes());
1059 Ok(())
1060}
1061
1062fn finish_table(
1067 out: &mut Vec<u8>,
1068 start: u64,
1069 rows: &[Row],
1070 mut dict: Dict,
1071 size: u32,
1072) -> Result<()> {
1073 let xref_offset = start as usize + out.len();
1074 out.extend_from_slice(b"xref\n");
1075 for (begin, len) in contiguous_runs(rows) {
1076 out.extend_from_slice(format!("{} {}\n", rows[begin].num(), len).as_bytes());
1077 for row in &rows[begin..begin + len] {
1078 match row {
1079 Row::InFile(r, offset) => out.extend_from_slice(
1080 format!("{:010} {:05} n \n", table_offset(*offset)?, r.gen).as_bytes(),
1081 ),
1082 Row::Free { gen, next, .. } => {
1083 out.extend_from_slice(format!("{next:010} {gen:05} f \n").as_bytes())
1084 }
1085 }
1086 }
1087 }
1088 dict.insert(name("Size"), Object::Int(i64::from(size)));
1089 out.extend_from_slice(b"trailer\n");
1090 serialize_dict(&dict, out)?;
1091 out.extend_from_slice(format!("\nstartxref\n{xref_offset}\n%%EOF\n").as_bytes());
1092 Ok(())
1093}
1094
1095fn write_indirect(out: &mut Vec<u8>, r: ObjRef, obj: &Object) -> Result<()> {
1098 out.extend_from_slice(format!("{} {} obj\n", r.num, r.gen).as_bytes());
1099 match obj {
1100 Object::Stream(s) => {
1101 let mut dict = s.dict.clone();
1102 dict.insert(name("Length"), Object::Int(s.data.len() as i64));
1103 serialize_dict(&dict, out)?;
1104 out.extend_from_slice(b"\nstream\n");
1105 out.extend_from_slice(&s.data);
1106 out.extend_from_slice(b"\nendstream\nendobj\n");
1107 }
1108 direct => {
1109 serialize_object(direct, out)?;
1110 out.extend_from_slice(b"\nendobj\n");
1111 }
1112 }
1113 Ok(())
1114}
1115
1116fn plain_stream(data: Vec<u8>) -> Stream {
1118 Stream {
1119 dict: Dict::new(),
1120 data,
1121 }
1122}
1123
1124pub(crate) fn deflate(data: &[u8]) -> Vec<u8> {
1125 let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default());
1126 encoder
1127 .write_all(data)
1128 .expect("writing into a Vec cannot fail");
1129 encoder
1130 .finish()
1131 .expect("finishing an in-memory zlib stream cannot fail")
1132}
1133
1134fn field_offset(position: usize) -> Result<u32> {
1136 u32::try_from(position)
1137 .map_err(|_| Error::Other("file offset exceeds the 4-byte xref field".to_string()))
1138}
1139
1140fn table_offset(position: usize) -> Result<usize> {
1142 if position as u64 <= 9_999_999_999 {
1143 return Ok(position);
1144 }
1145 Err(Error::Other(
1146 "file offset exceeds the 10-digit xref table field".to_string(),
1147 ))
1148}
1149
1150fn name(text: &str) -> Name {
1151 Name(text.to_string())
1152}
1153
1154pub(crate) fn core_error(error: pdfboss_core::Error) -> Error {
1155 Error::Other(error.to_string())
1156}