1use std::borrow::Cow;
2use std::collections::{HashMap, HashSet};
3use std::io::{Cursor, Read, Seek, SeekFrom};
4use std::path::{Path, PathBuf};
5use std::sync::atomic::{AtomicU64, Ordering};
6use std::sync::{Arc, OnceLock};
7
8use bytes::Bytes;
9
10#[cfg(feature = "flat-opc")]
11use super::unexpected_eof;
12use super::{
13 SdkError, part_relationships_path, resolve_relationship_target_path, resolve_zip_file_path,
14};
15use crate::schemas::opc_content_types::{Types, TypesChoice};
16use crate::schemas::opc_relationships::{
17 Relationship as OpcRelationship, Relationships, TargetMode,
18};
19
20#[cfg(feature = "flat-opc")]
21const FLAT_OPC_PACKAGE_NS: &str = "http://schemas.microsoft.com/office/2006/xmlPackage";
22const RELATIONSHIP_CONTENT_TYPE: &str = "application/vnd.openxmlformats-package.relationships+xml";
23
24#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
25pub struct PartId(u32);
26
27impl PartId {
28 #[inline]
29 pub const fn from_index(index: usize) -> Self {
30 Self(index as u32)
31 }
32
33 #[inline]
34 pub const fn index(self) -> usize {
35 self.0 as usize
36 }
37}
38
39#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
40pub(crate) struct PackageId(u64);
41
42impl PackageId {
43 fn new() -> Self {
44 static NEXT_PACKAGE_ID: AtomicU64 = AtomicU64::new(1);
45 Self(NEXT_PACKAGE_ID.fetch_add(1, Ordering::Relaxed))
46 }
47}
48
49#[derive(Clone, Debug, Eq, PartialEq)]
50pub(crate) struct NewPartDescriptor {
51 pub(crate) relationship_type: Cow<'static, str>,
52 pub(crate) content_type: Cow<'static, str>,
53 pub(crate) path_prefix: &'static str,
54 pub(crate) target_name: &'static str,
55 pub(crate) extension: Cow<'static, str>,
56}
57
58pub(crate) fn default_part_extension_for_content_type(content_type: &str) -> Option<&'static str> {
59 match content_type {
60 "image/unknown" => Some(".bin"),
61 "image/bmp" => Some(".bmp"),
62 "image/gif" => Some(".gif"),
63 "image/png" => Some(".png"),
64 "image/jp2" => Some(".jp2"),
65 "image/tif" => Some(".tif"),
66 "image/tiff" => Some(".tiff"),
67 "image/xbm" => Some(".xbm"),
68 "image/x-icon" => Some(".ico"),
69 "image/x-pcx" => Some(".pcx"),
70 "image/x-pcz" => Some(".pcz"),
71 "image/x-emz" => Some(".emz"),
72 "image/x-wmz" => Some(".wmz"),
73 "image/jpeg" => Some(".jpeg"),
74 "image/x-emf" => Some(".emf"),
75 "image/x-wmf" => Some(".wmf"),
76 "image/svg+xml" => Some(".svg"),
77 "audio/aiff" => Some(".aiff"),
78 "audio/midi" => Some(".midi"),
79 "audio/mp3" => Some(".mp3"),
80 "audio/mpegurl" => Some(".m3u"),
81 "audio/wav" => Some(".wav"),
82 "audio/x-ms-wma" => Some(".wma"),
83 "audio/mpeg" => Some(".mpeg"),
84 "audio/ogg" => Some(".ogg"),
85 "video/x-ms-asf-plugin" => Some(".asx"),
86 "video/avi" => Some(".avi"),
87 "video/mp4" => Some(".mp4"),
88 "video/mpg" => Some(".mpg"),
89 "video/mpeg" => Some(".mpeg"),
90 "video/x-ms-wmv" => Some(".wmv"),
91 "video/x-ms-wmx" => Some(".wmx"),
92 "video/x-ms-wvx" => Some(".wvx"),
93 "video/quicktime" => Some(".mov"),
94 "video/ogg" => Some(".ogg"),
95 "video/vc1" => Some(".wmv"),
96 _ => None,
97 }
98}
99
100#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
101pub enum NewPartTargetMode {
102 Fixed,
103 #[default]
104 Indexed,
105}
106
107#[derive(Clone, Debug)]
108pub(crate) enum StoredPartData {
109 Archived {
110 entry_index: usize,
111 bytes: OnceLock<Bytes>,
112 },
113 Owned {
114 bytes: Bytes,
115 original_entry_index: Option<usize>,
116 },
117}
118
119impl StoredPartData {
120 #[inline]
121 fn cached_bytes(&self) -> Option<&[u8]> {
122 match self {
123 Self::Archived { bytes, .. } => bytes.get().map(AsRef::as_ref),
124 Self::Owned { bytes, .. } => Some(bytes.as_ref()),
125 }
126 }
127
128 #[inline]
129 fn original_entry_index(&self) -> Option<usize> {
130 match self {
131 Self::Archived { entry_index, .. } => Some(*entry_index),
132 Self::Owned {
133 original_entry_index,
134 ..
135 } => *original_entry_index,
136 }
137 }
138
139 #[inline]
140 fn unchanged_archive_entry_index(&self) -> Option<usize> {
141 match self {
142 Self::Archived { entry_index, .. } => Some(*entry_index),
143 Self::Owned { .. } => None,
144 }
145 }
146
147 #[inline]
148 fn set_owned(&mut self, bytes: Vec<u8>) {
149 let original_entry_index = self.original_entry_index();
150 *self = Self::Owned {
151 bytes: bytes.into(),
152 original_entry_index,
153 };
154 }
155}
156
157#[derive(Clone, Copy, Debug, Eq, PartialEq)]
158pub(crate) enum PackageSaveEntry {
159 ArchivedExtra(usize),
160 ContentTypes,
161 PackageRelationships,
162 Part(PartId),
163 PartRelationships(PartId),
164}
165
166#[derive(Clone, Debug)]
167enum ArchiveReader {
168 #[cfg(any(unix, windows))]
169 File(PositionedFileReader),
170 Memory(Cursor<Bytes>),
171}
172
173impl Read for ArchiveReader {
174 fn read(&mut self, buffer: &mut [u8]) -> std::io::Result<usize> {
175 match self {
176 #[cfg(any(unix, windows))]
177 Self::File(reader) => reader.read(buffer),
178 Self::Memory(reader) => reader.read(buffer),
179 }
180 }
181}
182
183impl Seek for ArchiveReader {
184 fn seek(&mut self, position: SeekFrom) -> std::io::Result<u64> {
185 match self {
186 #[cfg(any(unix, windows))]
187 Self::File(reader) => reader.seek(position),
188 Self::Memory(reader) => reader.seek(position),
189 }
190 }
191}
192
193#[cfg(any(unix, windows))]
194#[derive(Clone, Debug)]
195struct PositionedFileReader {
196 file: Arc<std::fs::File>,
197 position: u64,
198 length: u64,
199}
200
201#[cfg(any(unix, windows))]
202impl PositionedFileReader {
203 fn new(file: std::fs::File) -> Result<Self, SdkError> {
204 let length = file.metadata()?.len();
205 Ok(Self {
206 file: Arc::new(file),
207 position: 0,
208 length,
209 })
210 }
211}
212
213#[cfg(any(unix, windows))]
214impl Read for PositionedFileReader {
215 fn read(&mut self, buffer: &mut [u8]) -> std::io::Result<usize> {
216 #[cfg(unix)]
217 let read = {
218 use std::os::unix::fs::FileExt as _;
219 self.file.read_at(buffer, self.position)?
220 };
221 #[cfg(windows)]
222 let read = {
223 use std::os::windows::fs::FileExt as _;
224 self.file.seek_read(buffer, self.position)?
225 };
226 self.position = self
227 .position
228 .checked_add(read as u64)
229 .ok_or_else(|| std::io::Error::other("file reader position overflow"))?;
230 Ok(read)
231 }
232}
233
234#[cfg(any(unix, windows))]
235impl Seek for PositionedFileReader {
236 fn seek(&mut self, position: SeekFrom) -> std::io::Result<u64> {
237 let next = match position {
238 SeekFrom::Start(position) => i128::from(position),
239 SeekFrom::Current(offset) => i128::from(self.position) + i128::from(offset),
240 SeekFrom::End(offset) => i128::from(self.length) + i128::from(offset),
241 };
242 self.position = u64::try_from(next)
243 .map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidInput, "invalid seek"))?;
244 Ok(self.position)
245 }
246}
247
248#[derive(Debug)]
249struct ArchiveBacking {
250 archive: zip::ZipArchive<ArchiveReader>,
251}
252
253impl ArchiveBacking {
254 fn read_entry(&self, entry_index: usize) -> Result<Bytes, SdkError> {
255 let mut archive = self.archive.clone();
256 let bytes = read_archive_entry_by_index(&mut archive, entry_index)?;
257 Ok(bytes.into())
258 }
259
260 fn raw_copy_entry<W: std::io::Write + std::io::Seek>(
261 &self,
262 entry_index: usize,
263 target_name: &str,
264 writer: &mut zip::ZipWriter<W>,
265 ) -> Result<(), SdkError> {
266 let mut archive = self.archive.clone();
267 let entry = archive.by_index_raw(entry_index)?;
268 writer.raw_copy_file_rename(entry, target_name)?;
269 Ok(())
270 }
271
272 fn raw_copy_entry_with_original_name<W: std::io::Write + std::io::Seek>(
273 &self,
274 entry_index: usize,
275 writer: &mut zip::ZipWriter<W>,
276 ) -> Result<(), SdkError> {
277 let mut archive = self.archive.clone();
278 let entry = archive.by_index_raw(entry_index)?;
279 writer.raw_copy_file(entry)?;
280 Ok(())
281 }
282
283 fn configure_writer<W: std::io::Write + std::io::Seek>(
284 &self,
285 writer: &mut zip::ZipWriter<W>,
286 ) -> Result<(), SdkError> {
287 writer.set_raw_comment(self.archive.comment().into())?;
288 if let Some(data) = self.archive.raw_zip64_extensible_data_sector() {
289 writer.set_raw_zip64_extensible_data_sector(data.into());
290 }
291 Ok(())
292 }
293}
294
295fn open_package_file(path: &Path) -> Result<std::fs::File, SdkError> {
296 let mut options = std::fs::OpenOptions::new();
297 options.read(true);
298 #[cfg(windows)]
299 {
300 use std::os::windows::fs::OpenOptionsExt as _;
301 const FILE_SHARE_READ: u32 = 0x0000_0001;
302 const FILE_SHARE_WRITE: u32 = 0x0000_0002;
303 const FILE_SHARE_DELETE: u32 = 0x0000_0004;
304 options.share_mode(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE);
305 }
306 Ok(options.open(path)?)
307}
308
309pub(crate) fn create_package_temp_file(
310 target_path: &Path,
311) -> Result<(PathBuf, std::fs::File), SdkError> {
312 static NEXT_TEMP_FILE_ID: AtomicU64 = AtomicU64::new(1);
313 let parent = target_path.parent().unwrap_or_else(|| Path::new("."));
314 let file_name = target_path
315 .file_name()
316 .and_then(|name| name.to_str())
317 .unwrap_or("package");
318
319 for _ in 0..1024 {
320 let id = NEXT_TEMP_FILE_ID.fetch_add(1, Ordering::Relaxed);
321 let path = parent.join(format!(
322 ".{file_name}.ooxmlsdk-{}-{id}.tmp",
323 std::process::id()
324 ));
325 match std::fs::OpenOptions::new()
326 .write(true)
327 .create_new(true)
328 .open(&path)
329 {
330 Ok(file) => return Ok((path, file)),
331 Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists => {}
332 Err(error) => return Err(error.into()),
333 }
334 }
335
336 Err(SdkError::CommonError(format!(
337 "could not create a temporary package file beside {}",
338 target_path.display()
339 )))
340}
341
342pub(crate) fn replace_package_file(
343 temporary_path: &Path,
344 target_path: &Path,
345) -> Result<(), SdkError> {
346 #[cfg(unix)]
347 {
348 std::fs::rename(temporary_path, target_path)?;
349 Ok(())
350 }
351
352 #[cfg(not(unix))]
353 {
354 if !target_path.exists() {
355 std::fs::rename(temporary_path, target_path)?;
356 return Ok(());
357 }
358
359 let backup_path = target_path.with_extension(format!(
360 "ooxmlsdk-backup-{}-{}",
361 std::process::id(),
362 PackageId::new().0
363 ));
364 std::fs::rename(target_path, &backup_path)?;
365 if let Err(error) = std::fs::rename(temporary_path, target_path) {
366 let _ = std::fs::rename(&backup_path, target_path);
367 return Err(error.into());
368 }
369 let _ = std::fs::remove_file(backup_path);
370 Ok(())
371 }
372}
373
374#[derive(Clone, Debug, Eq, PartialEq)]
375pub(crate) struct RelationshipInfo {
376 id: Box<str>,
377 relationship_type: super::XmlRelationshipNamespaceUri,
378 target: Box<str>,
379 target_mode: Option<TargetMode>,
380 target_kind: RelationshipTargetKind,
381 target_part_id: Option<PartId>,
382}
383
384#[derive(Clone, Copy, Debug, Eq, PartialEq)]
385pub enum RelationshipTargetKind {
386 InternalPart,
387 External,
388 Null,
389 Missing,
390}
391
392#[derive(Clone, Copy, Debug, Eq, PartialEq)]
393pub enum ReferenceRelationshipKind {
394 External,
395 Hyperlink,
396 Audio,
397 Media,
398 Video,
399}
400
401impl RelationshipInfo {
402 fn internal_part(
403 id: String,
404 relationship_type: String,
405 target: String,
406 target_part_id: PartId,
407 ) -> Self {
408 Self {
409 id: id.into_boxed_str(),
410 relationship_type: super::XmlRelationshipNamespaceUri::from_uri(&relationship_type),
411 target: target.into_boxed_str(),
412 target_mode: None,
413 target_kind: RelationshipTargetKind::InternalPart,
414 target_part_id: Some(target_part_id),
415 }
416 }
417
418 fn external(
419 id: String,
420 relationship_type: String,
421 target: String,
422 target_mode: Option<TargetMode>,
423 ) -> Self {
424 Self {
425 id: id.into_boxed_str(),
426 relationship_type: super::XmlRelationshipNamespaceUri::from_uri(&relationship_type),
427 target: target.into_boxed_str(),
428 target_mode,
429 target_kind: RelationshipTargetKind::External,
430 target_part_id: None,
431 }
432 }
433
434 fn reference(
435 id: String,
436 relationship_type: String,
437 target: String,
438 target_mode: Option<TargetMode>,
439 ) -> Self {
440 let effective_target_mode = target_mode.unwrap_or(TargetMode::Internal);
441 let target_kind = if matches!(effective_target_mode, TargetMode::External) {
442 RelationshipTargetKind::External
443 } else if target.eq_ignore_ascii_case("NULL") {
444 RelationshipTargetKind::Null
445 } else {
446 RelationshipTargetKind::Missing
447 };
448 Self {
449 id: id.into_boxed_str(),
450 relationship_type: super::XmlRelationshipNamespaceUri::from_uri(&relationship_type),
451 target: target.into_boxed_str(),
452 target_mode,
453 target_kind,
454 target_part_id: None,
455 }
456 }
457
458 #[inline]
459 pub fn id(&self) -> &str {
460 &self.id
461 }
462
463 #[inline]
464 pub fn relationship_type(&self) -> &str {
465 self.relationship_type.as_str()
466 }
467
468 #[inline]
469 pub(crate) fn relationship_type_bytes(&self) -> &[u8] {
470 self.relationship_type.uri_bytes()
471 }
472
473 #[inline]
474 pub fn target(&self) -> &str {
475 &self.target
476 }
477
478 #[inline]
479 pub fn target_mode(&self) -> TargetMode {
480 self.target_mode.unwrap_or(TargetMode::Internal)
481 }
482
483 #[inline]
484 pub fn target_kind(&self) -> RelationshipTargetKind {
485 self.target_kind
486 }
487
488 #[inline]
489 pub fn target_part_id(&self) -> Option<PartId> {
490 self.target_part_id
491 }
492
493 #[inline]
494 pub fn is_reference_relationship(&self) -> bool {
495 self.reference_kind().is_some()
496 }
497
498 #[inline]
499 pub fn reference_kind(&self) -> Option<ReferenceRelationshipKind> {
500 let relationship_type = self.relationship_type_bytes();
501 if super::relationship_type_matches_bytes(relationship_type, super::REL_HYPERLINK) {
502 Some(ReferenceRelationshipKind::Hyperlink)
503 } else if super::relationship_type_matches_bytes(relationship_type, super::REL_AUDIO) {
504 Some(ReferenceRelationshipKind::Audio)
505 } else if super::relationship_type_matches_bytes(relationship_type, super::REL_MEDIA) {
506 Some(ReferenceRelationshipKind::Media)
507 } else if super::relationship_type_matches_bytes(relationship_type, super::REL_VIDEO) {
508 Some(ReferenceRelationshipKind::Video)
509 } else if self.target_kind == RelationshipTargetKind::External {
510 Some(ReferenceRelationshipKind::External)
511 } else {
512 None
513 }
514 }
515
516 fn to_relationship(&self) -> OpcRelationship {
517 OpcRelationship {
518 id: self.id().to_string(),
519 r#type: self.relationship_type().to_string(),
520 target: self.target().to_string(),
521 target_mode: self.target_mode,
522 }
523 }
524}
525
526#[derive(Clone, Debug, Eq, PartialEq)]
527pub struct Relationship {
528 inner: RelationshipInfo,
529}
530
531#[derive(Clone, Copy, Debug, Eq, PartialEq)]
532pub struct RelationshipRef<'a> {
533 inner: &'a RelationshipInfo,
534}
535
536macro_rules! impl_relationship_accessors {
537 ($ident:ident) => {
538 impl $ident {
539 #[inline]
540 pub fn id(&self) -> &str {
541 self.inner.id()
542 }
543
544 #[inline]
545 pub fn relationship_type(&self) -> &str {
546 self.inner.relationship_type()
547 }
548
549 #[inline]
550 pub fn target(&self) -> &str {
551 self.inner.target()
552 }
553
554 #[inline]
555 pub fn target_mode(&self) -> TargetMode {
556 self.inner.target_mode()
557 }
558
559 #[inline]
560 pub fn target_kind(&self) -> RelationshipTargetKind {
561 self.inner.target_kind()
562 }
563
564 #[inline]
565 pub fn target_part_id(&self) -> Option<PartId> {
566 self.inner.target_part_id()
567 }
568
569 #[inline]
570 pub fn reference_kind(&self) -> Option<ReferenceRelationshipKind> {
571 self.inner.reference_kind()
572 }
573
574 #[inline]
575 pub fn is_reference_relationship(&self) -> bool {
576 self.inner.is_reference_relationship()
577 }
578 }
579 };
580}
581
582impl_relationship_accessors!(Relationship);
583
584impl<'a> RelationshipRef<'a> {
585 pub const HYPERLINK_RELATIONSHIP_TYPE: &'static str =
586 "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink";
587 pub const AUDIO_REFERENCE_RELATIONSHIP_TYPE: &'static str =
588 "http://schemas.openxmlformats.org/officeDocument/2006/relationships/audio";
589 pub const MEDIA_REFERENCE_RELATIONSHIP_TYPE: &'static str =
590 "http://schemas.microsoft.com/office/2007/relationships/media";
591 pub const VIDEO_REFERENCE_RELATIONSHIP_TYPE: &'static str =
592 "http://schemas.openxmlformats.org/officeDocument/2006/relationships/video";
593
594 #[inline]
595 pub(crate) const fn new(inner: &'a RelationshipInfo) -> Self {
596 Self { inner }
597 }
598
599 #[inline]
600 pub fn id(&self) -> &'a str {
601 self.inner.id()
602 }
603
604 #[inline]
605 pub fn relationship_type(&self) -> &'a str {
606 self.inner.relationship_type()
607 }
608
609 #[inline]
610 pub fn target(&self) -> &'a str {
611 self.inner.target()
612 }
613
614 #[inline]
615 pub fn target_mode(&self) -> TargetMode {
616 self.inner.target_mode()
617 }
618
619 #[inline]
620 pub fn target_kind(&self) -> RelationshipTargetKind {
621 self.inner.target_kind()
622 }
623
624 #[inline]
625 pub fn target_part_id(&self) -> Option<PartId> {
626 self.inner.target_part_id()
627 }
628
629 #[inline]
630 pub fn reference_kind(&self) -> Option<ReferenceRelationshipKind> {
631 self.inner.reference_kind()
632 }
633
634 #[inline]
635 pub fn is_reference_relationship(&self) -> bool {
636 self.inner.is_reference_relationship()
637 }
638}
639
640impl From<RelationshipInfo> for Relationship {
641 #[inline]
642 fn from(inner: RelationshipInfo) -> Self {
643 Self { inner }
644 }
645}
646
647impl From<RelationshipRef<'_>> for Relationship {
648 #[inline]
649 fn from(value: RelationshipRef<'_>) -> Self {
650 Self {
651 inner: value.inner.clone(),
652 }
653 }
654}
655
656impl<'a> From<&'a RelationshipInfo> for RelationshipRef<'a> {
657 #[inline]
658 fn from(inner: &'a RelationshipInfo) -> Self {
659 Self::new(inner)
660 }
661}
662
663#[doc(hidden)]
664#[derive(Clone, Debug, Default, Eq, PartialEq)]
665pub struct RelationshipSet {
666 relationships: Vec<RelationshipInfo>,
667 by_id: HashMap<Box<str>, usize>,
668 raw_bytes: Option<Box<[u8]>>,
669 archive_entry_index: Option<usize>,
670}
671
672fn next_relationship_id<'a>(relationships: impl Iterator<Item = &'a RelationshipInfo>) -> String {
673 let next = relationships
674 .filter_map(|relationship| relationship.id().strip_prefix("rId"))
675 .filter_map(|suffix| suffix.parse::<u32>().ok())
676 .max()
677 .unwrap_or_default()
678 + 1;
679 format!("rId{next}")
680}
681
682impl RelationshipSet {
683 pub(crate) const HYPERLINK_RELATIONSHIP_TYPE: &'static str =
684 "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink";
685 pub(crate) const AUDIO_REFERENCE_RELATIONSHIP_TYPE: &'static str =
686 "http://schemas.openxmlformats.org/officeDocument/2006/relationships/audio";
687 pub(crate) const MEDIA_REFERENCE_RELATIONSHIP_TYPE: &'static str =
688 "http://schemas.microsoft.com/office/2007/relationships/media";
689 pub(crate) const VIDEO_REFERENCE_RELATIONSHIP_TYPE: &'static str =
690 "http://schemas.openxmlformats.org/officeDocument/2006/relationships/video";
691
692 #[inline]
693 pub(crate) fn is_empty(&self) -> bool {
694 self.relationships.is_empty() && self.raw_bytes.is_none()
695 }
696
697 #[inline]
698 pub(crate) fn iter(&self) -> impl Iterator<Item = &RelationshipInfo> {
699 self.relationships.iter()
700 }
701
702 #[inline]
703 pub(crate) fn get(&self, relationship_id: &str) -> Option<&RelationshipInfo> {
704 self
705 .by_id
706 .get(relationship_id)
707 .and_then(|index| self.relationships.get(*index))
708 }
709
710 #[inline]
711 pub(crate) fn contains_id(&self, relationship_id: &str) -> bool {
712 self.by_id.contains_key(relationship_id)
713 }
714
715 pub(crate) fn next_relationship_id(&self) -> String {
716 next_relationship_id(self.relationships.iter())
717 }
718
719 pub(crate) fn add_external_relationship(
720 &mut self,
721 relationship_id: impl Into<String>,
722 relationship_type: impl Into<String>,
723 target: impl Into<String>,
724 ) -> Result<&RelationshipInfo, SdkError> {
725 self.push_relationship(RelationshipInfo::external(
726 relationship_id.into(),
727 relationship_type.into(),
728 target.into(),
729 Some(TargetMode::External),
730 ))
731 }
732
733 pub(crate) fn add_hyperlink_relationship(
734 &mut self,
735 relationship_id: impl Into<String>,
736 target: impl Into<String>,
737 ) -> Result<&RelationshipInfo, SdkError> {
738 self.add_hyperlink_relationship_with_mode(relationship_id, target, TargetMode::External)
739 }
740
741 pub(crate) fn add_hyperlink_relationship_with_mode(
742 &mut self,
743 relationship_id: impl Into<String>,
744 target: impl Into<String>,
745 target_mode: TargetMode,
746 ) -> Result<&RelationshipInfo, SdkError> {
747 let target_mode = match target_mode {
748 TargetMode::External => Some(TargetMode::External),
749 TargetMode::Internal => None,
750 };
751 self.push_relationship(RelationshipInfo::reference(
752 relationship_id.into(),
753 Self::HYPERLINK_RELATIONSHIP_TYPE.to_string(),
754 target.into(),
755 target_mode,
756 ))
757 }
758
759 pub(crate) fn add_internal_part_relationship(
760 &mut self,
761 relationship_id: impl Into<String>,
762 relationship_type: impl Into<String>,
763 target: impl Into<String>,
764 target_part_id: PartId,
765 ) -> Result<&RelationshipInfo, SdkError> {
766 self.push_relationship(RelationshipInfo::internal_part(
767 relationship_id.into(),
768 relationship_type.into(),
769 target.into(),
770 target_part_id,
771 ))
772 }
773
774 pub(crate) fn add_relationship_info(
775 &mut self,
776 relationship: RelationshipInfo,
777 ) -> Result<&RelationshipInfo, SdkError> {
778 self.push_relationship(relationship)
779 }
780
781 pub(crate) fn remove(&mut self, relationship_id: &str) -> Option<RelationshipInfo> {
782 let index = *self.by_id.get(relationship_id)?;
783 self.raw_bytes = None;
784 let removed = self.relationships.remove(index);
785 self.rebuild_index();
786 Some(removed)
787 }
788
789 pub(crate) fn remove_reference_relationship(
790 &mut self,
791 relationship_id: &str,
792 ) -> Result<RelationshipInfo, SdkError> {
793 let relationship = self.get(relationship_id).ok_or_else(|| {
794 SdkError::CommonError(format!(
795 "reference relationship id {relationship_id} does not exist"
796 ))
797 })?;
798 if !relationship.is_reference_relationship() {
799 return Err(SdkError::CommonError(format!(
800 "relationship id {relationship_id} is not a reference relationship"
801 )));
802 }
803 Ok(
804 self
805 .remove(relationship_id)
806 .expect("relationship was already resolved"),
807 )
808 }
809
810 pub(crate) fn get_external_relationship(
811 &self,
812 relationship_id: &str,
813 ) -> Option<&RelationshipInfo> {
814 self.get(relationship_id).filter(|relationship| {
815 matches!(
816 relationship.reference_kind(),
817 Some(ReferenceRelationshipKind::External)
818 )
819 })
820 }
821
822 pub(crate) fn remove_external_relationship(
823 &mut self,
824 relationship_id: &str,
825 ) -> Result<RelationshipInfo, SdkError> {
826 let relationship = self.get(relationship_id).ok_or_else(|| {
827 SdkError::CommonError(format!(
828 "external relationship id {relationship_id} does not exist"
829 ))
830 })?;
831 if !matches!(
832 relationship.reference_kind(),
833 Some(ReferenceRelationshipKind::External)
834 ) {
835 return Err(SdkError::CommonError(format!(
836 "relationship id {relationship_id} is not an external relationship"
837 )));
838 }
839 Ok(
840 self
841 .remove(relationship_id)
842 .expect("relationship was already resolved"),
843 )
844 }
845
846 pub(crate) fn get_hyperlink_relationship(
847 &self,
848 relationship_id: &str,
849 ) -> Option<&RelationshipInfo> {
850 self.get(relationship_id).filter(|relationship| {
851 matches!(
852 relationship.reference_kind(),
853 Some(ReferenceRelationshipKind::Hyperlink)
854 )
855 })
856 }
857
858 pub(crate) fn change_relationship_id(
859 &mut self,
860 relationship_id: &str,
861 new_relationship_id: impl Into<String>,
862 ) -> Result<(), SdkError> {
863 let new_relationship_id = new_relationship_id.into();
864 if relationship_id == new_relationship_id {
865 return Ok(());
866 }
867 if self.contains_id(&new_relationship_id) {
868 return Err(SdkError::CommonError(format!(
869 "relationship id {new_relationship_id} already exists"
870 )));
871 }
872
873 let Some(index) = self.by_id.get(relationship_id).copied() else {
874 return Err(SdkError::CommonError(format!(
875 "relationship id {relationship_id} does not exist"
876 )));
877 };
878
879 self.relationships[index].id = new_relationship_id.into_boxed_str();
880 self.raw_bytes = None;
881 self.rebuild_index();
882 Ok(())
883 }
884
885 #[inline]
886 pub(crate) fn part_relationships(&self) -> impl Iterator<Item = &RelationshipInfo> {
887 self
888 .relationships
889 .iter()
890 .filter(|relationship| relationship.target_kind() == RelationshipTargetKind::InternalPart)
891 }
892
893 #[inline]
894 pub(crate) fn external_relationships(&self) -> impl Iterator<Item = &RelationshipInfo> {
895 self.relationships.iter().filter(|relationship| {
896 relationship.target_kind() == RelationshipTargetKind::External
897 && !super::relationship_type_matches_bytes(
898 relationship.relationship_type_bytes(),
899 super::REL_HYPERLINK,
900 )
901 })
902 }
903
904 #[inline]
905 pub(crate) fn hyperlink_relationships(&self) -> impl Iterator<Item = &RelationshipInfo> {
906 self.relationships.iter().filter(|relationship| {
907 super::relationship_type_matches_bytes(
908 relationship.relationship_type_bytes(),
909 super::REL_HYPERLINK,
910 )
911 })
912 }
913
914 #[inline]
915 pub(crate) fn data_part_reference_relationships(
916 &self,
917 ) -> impl Iterator<Item = &RelationshipInfo> {
918 self.relationships.iter().filter(|relationship| {
919 crate::common::is_data_part_reference_relationship_type_bytes(
920 relationship.relationship_type_bytes(),
921 )
922 })
923 }
924
925 #[inline]
926 pub(crate) fn first_target_part_by_relationship_type(
927 &self,
928 relationship_type: &str,
929 ) -> Option<PartId> {
930 self.relationships.iter().find_map(|relationship| {
931 super::relationship_type_matches_bytes(
932 relationship.relationship_type_bytes(),
933 relationship_type.as_bytes(),
934 )
935 .then(|| relationship.target_part_id())
936 .flatten()
937 })
938 }
939
940 pub(crate) fn to_relationships(&self) -> Relationships {
941 Relationships {
942 xmlns: vec![super::XmlNamespace::raw(
943 "",
944 "http://schemas.openxmlformats.org/package/2006/relationships",
945 )],
946 relationship: self
947 .relationships
948 .iter()
949 .map(RelationshipInfo::to_relationship)
950 .collect(),
951 }
952 }
953
954 pub(crate) fn to_bytes_cow(&self) -> Result<Cow<'_, [u8]>, SdkError> {
955 if let Some(bytes) = &self.raw_bytes {
956 return Ok(Cow::Borrowed(bytes));
957 }
958 let mut bytes = Vec::with_capacity(32);
959 crate::sdk::SdkType::write_to(&self.to_relationships(), &mut bytes)?;
960 Ok(Cow::Owned(bytes))
961 }
962
963 fn from_relationships(
964 relationships: Option<Relationships>,
965 source_path: &str,
966 by_path: &HashMap<Box<str>, PartId>,
967 ) -> Self {
968 let Some(relationships) = relationships else {
969 return Self::default();
970 };
971
972 let source_parent_path = super::parent_zip_path(source_path);
973 let mut set = Self {
974 relationships: Vec::with_capacity(relationships.relationship.len()),
975 by_id: HashMap::with_capacity(relationships.relationship.len()),
976 raw_bytes: None,
977 archive_entry_index: None,
978 };
979
980 for relationship in relationships.relationship {
981 let info = relationship_info(relationship, &source_parent_path, by_path);
982 set.push_relationship_unchecked(info);
983 }
984
985 set
986 }
987
988 fn from_raw_bytes(bytes: Box<[u8]>) -> Self {
989 Self {
990 relationships: Vec::new(),
991 by_id: HashMap::new(),
992 raw_bytes: Some(bytes),
993 archive_entry_index: None,
994 }
995 }
996
997 #[inline]
998 fn with_archive_entry(mut self, entry_index: usize) -> Self {
999 self.archive_entry_index = Some(entry_index);
1000 self
1001 }
1002
1003 #[inline]
1004 pub(crate) fn raw_archive_entry_index(&self) -> Option<usize> {
1005 self
1006 .raw_bytes
1007 .is_some()
1008 .then_some(self.archive_entry_index)
1009 .flatten()
1010 }
1011
1012 #[inline]
1013 fn original_archive_entry_index(&self) -> Option<usize> {
1014 self.archive_entry_index
1015 }
1016
1017 fn push_relationship(
1018 &mut self,
1019 relationship: RelationshipInfo,
1020 ) -> Result<&RelationshipInfo, SdkError> {
1021 if self.contains_id(relationship.id()) {
1022 return Err(SdkError::CommonError(format!(
1023 "relationship id {} already exists",
1024 relationship.id()
1025 )));
1026 }
1027 self.raw_bytes = None;
1028 self.push_relationship_unchecked(relationship);
1029 Ok(self.relationships.last().expect("pushed relationship"))
1030 }
1031
1032 fn push_relationship_unchecked(&mut self, relationship: RelationshipInfo) {
1033 let index = self.relationships.len();
1034 self.by_id.insert(relationship.id.clone(), index);
1035 self.relationships.push(relationship);
1036 }
1037
1038 fn rebuild_index(&mut self) {
1039 self.by_id.clear();
1040 self.by_id.reserve(self.relationships.len());
1041 for (index, relationship) in self.relationships.iter().enumerate() {
1042 self.by_id.insert(relationship.id.clone(), index);
1043 }
1044 }
1045}
1046
1047#[derive(Clone, Debug)]
1048pub(crate) struct StoredPart {
1049 path: Box<str>,
1050 content_type: Box<str>,
1051 relationship_type: Option<super::XmlRelationshipNamespaceUri>,
1052 kind: crate::parts::PartKind,
1053 relationships: RelationshipSet,
1054 data: StoredPartData,
1055 deleted: bool,
1056}
1057
1058impl StoredPart {
1059 #[inline]
1060 pub(crate) fn is_deleted(&self) -> bool {
1061 self.deleted
1062 }
1063
1064 #[inline]
1065 pub(crate) fn path(&self) -> &str {
1066 &self.path
1067 }
1068
1069 #[inline]
1070 pub(crate) fn content_type(&self) -> &str {
1071 &self.content_type
1072 }
1073
1074 #[inline]
1075 pub(crate) fn relationship_type(&self) -> Option<&str> {
1076 self
1077 .relationship_type
1078 .as_ref()
1079 .map(super::XmlRelationshipNamespaceUri::as_str)
1080 }
1081
1082 #[inline]
1083 pub(crate) fn relationship_type_bytes(&self) -> Option<&[u8]> {
1084 self
1085 .relationship_type
1086 .as_ref()
1087 .map(super::XmlRelationshipNamespaceUri::uri_bytes)
1088 }
1089
1090 #[inline]
1091 pub(crate) fn kind(&self) -> crate::parts::PartKind {
1092 self.kind
1093 }
1094
1095 #[inline]
1096 pub(crate) fn relationships(&self) -> &RelationshipSet {
1097 &self.relationships
1098 }
1099
1100 #[inline]
1101 pub(crate) fn relationships_mut(&mut self) -> &mut RelationshipSet {
1102 &mut self.relationships
1103 }
1104}
1105
1106#[doc(hidden)]
1107#[derive(Debug)]
1108pub struct SdkPackageStorage {
1109 id: PackageId,
1110 archive: Option<Arc<ArchiveBacking>>,
1111 archived_extra_entry_indices: Box<[usize]>,
1112 content_types: Types,
1113 content_types_archive_entry_index: Option<usize>,
1114 package_relationships: RelationshipSet,
1115 parts: Vec<StoredPart>,
1116 by_path: HashMap<Box<str>, PartId>,
1117 preferred_main_part_content_type: Option<&'static str>,
1118}
1119
1120impl Clone for SdkPackageStorage {
1121 fn clone(&self) -> Self {
1122 Self {
1123 id: PackageId::new(),
1124 archive: self.archive.clone(),
1125 archived_extra_entry_indices: self.archived_extra_entry_indices.clone(),
1126 content_types: self.content_types.clone(),
1127 content_types_archive_entry_index: self.content_types_archive_entry_index,
1128 package_relationships: self.package_relationships.clone(),
1129 parts: self.parts.clone(),
1130 by_path: self.by_path.clone(),
1131 preferred_main_part_content_type: self.preferred_main_part_content_type,
1132 }
1133 }
1134}
1135
1136impl SdkPackageStorage {
1137 pub(crate) fn create(preferred_main_part_content_type: Option<&'static str>) -> Self {
1138 Self {
1139 id: PackageId::new(),
1140 archive: None,
1141 archived_extra_entry_indices: Box::new([]),
1142 content_types: empty_content_types(),
1143 content_types_archive_entry_index: None,
1144 package_relationships: RelationshipSet::default(),
1145 parts: Vec::new(),
1146 by_path: HashMap::new(),
1147 preferred_main_part_content_type,
1148 }
1149 }
1150
1151 pub(crate) fn open<R: Read + Seek>(mut reader: R) -> Result<Self, SdkError> {
1152 let length = reader.seek(SeekFrom::End(0))?;
1153 reader.seek(SeekFrom::Start(0))?;
1154 let mut bytes = Vec::new();
1155 let capacity = usize::try_from(length)
1156 .map_err(|_| SdkError::CommonError("package is too large for this platform".to_string()))?;
1157 bytes.try_reserve_exact(capacity).map_err(|error| {
1158 SdkError::CommonError(format!(
1159 "cannot allocate {capacity} bytes for package input: {error}"
1160 ))
1161 })?;
1162 reader.read_to_end(&mut bytes)?;
1163 Self::open_memory(bytes.into())
1164 }
1165
1166 pub(crate) fn open_file(path: &Path) -> Result<Self, SdkError> {
1167 #[cfg(any(unix, windows))]
1168 {
1169 let file = open_package_file(path)?;
1170 let reader = PositionedFileReader::new(file)?;
1171 let archive = zip::ZipArchive::new(ArchiveReader::File(reader))?;
1172 Self::open_archive(archive)
1173 }
1174 #[cfg(not(any(unix, windows)))]
1175 {
1176 Self::open_memory(std::fs::read(path)?.into())
1177 }
1178 }
1179
1180 fn open_memory(bytes: Bytes) -> Result<Self, SdkError> {
1181 let archive = zip::ZipArchive::new(ArchiveReader::Memory(Cursor::new(bytes)))?;
1182 Self::open_archive(archive)
1183 }
1184
1185 fn open_archive(mut archive: zip::ZipArchive<ArchiveReader>) -> Result<Self, SdkError> {
1186 let opened = read_archive_model(&mut archive)?;
1187 let OpenedArchiveModel {
1188 content_types,
1189 content_types_archive_entry_index,
1190 mut raw_parts,
1191 package_relationships,
1192 part_relationships,
1193 } = opened;
1194 let mut claimed_entry_indices = vec![false; archive.len()];
1195 claimed_entry_indices[content_types_archive_entry_index] = true;
1196 for raw_part in &raw_parts {
1197 claimed_entry_indices[raw_part.entry_index] = true;
1198 }
1199 for entry in part_relationships.iter().flatten() {
1200 claimed_entry_indices[entry.entry_index] = true;
1201 }
1202 if let Some(entry) = &package_relationships {
1203 claimed_entry_indices[entry.entry_index] = true;
1204 }
1205 let archived_extra_entry_indices = (0..archive.len())
1206 .filter(|entry_index| !claimed_entry_indices[*entry_index])
1207 .collect::<Box<[_]>>();
1208 let mut by_path = HashMap::with_capacity(raw_parts.len());
1209
1210 for (index, raw_part) in raw_parts.iter().enumerate() {
1211 by_path.insert(raw_part.path.clone(), PartId::from_index(index));
1212 }
1213
1214 let package_relationships = package_relationships
1215 .map(|loaded| {
1216 RelationshipSet::from_relationships(loaded.relationships, "", &by_path)
1217 .with_archive_entry(loaded.entry_index)
1218 })
1219 .unwrap_or_default();
1220
1221 let part_relationships = part_relationships
1222 .into_iter()
1223 .zip(&raw_parts)
1224 .map(|(loaded, raw_part)| {
1225 loaded
1226 .map(|loaded| {
1227 match loaded.relationships {
1228 Some(relationships) => {
1229 RelationshipSet::from_relationships(Some(relationships), &raw_part.path, &by_path)
1230 }
1231 None => RelationshipSet::from_raw_bytes(loaded.bytes.into_boxed_slice()),
1232 }
1233 .with_archive_entry(loaded.entry_index)
1234 })
1235 .unwrap_or_default()
1236 })
1237 .collect::<Vec<_>>();
1238
1239 let relationship_types =
1240 relationship_types_by_part(&package_relationships, &part_relationships)?;
1241
1242 let mut parts = Vec::with_capacity(raw_parts.len());
1243 for (index, (raw_part, relationships)) in
1244 raw_parts.drain(..).zip(part_relationships).enumerate()
1245 {
1246 let relationship_type = relationship_types[index].clone();
1247 let kind = crate::parts::PartKind::classify(
1248 relationship_type
1249 .as_ref()
1250 .map(super::XmlRelationshipNamespaceUri::uri_bytes),
1251 raw_part.content_type.as_bytes(),
1252 &raw_part.path,
1253 );
1254 parts.push(StoredPart {
1255 path: raw_part.path,
1256 content_type: raw_part.content_type,
1257 relationship_type,
1258 kind,
1259 relationships,
1260 data: StoredPartData::Archived {
1261 entry_index: raw_part.entry_index,
1262 bytes: OnceLock::new(),
1263 },
1264 deleted: false,
1265 });
1266 }
1267
1268 let archive = Arc::new(ArchiveBacking { archive });
1269
1270 Ok(Self {
1271 id: PackageId::new(),
1272 archive: Some(archive),
1273 archived_extra_entry_indices,
1274 content_types,
1275 content_types_archive_entry_index: Some(content_types_archive_entry_index),
1276 package_relationships,
1277 parts,
1278 by_path,
1279 preferred_main_part_content_type: None,
1280 })
1281 }
1282
1283 #[cfg(feature = "flat-opc")]
1284 pub(crate) fn open_flat_opc<R: std::io::BufRead>(reader: R) -> Result<Self, SdkError> {
1285 let mut flat_parts = read_flat_opc_parts(reader)?;
1286 flat_parts.sort_by(|left, right| left.path.cmp(&right.path));
1287
1288 let mut raw_parts = Vec::new();
1289 let mut relationship_parts = HashMap::new();
1290 for flat_part in flat_parts {
1291 if is_relationships_part_path(&flat_part.path) {
1292 relationship_parts.insert(flat_part.path, flat_part.bytes);
1293 } else {
1294 raw_parts.push(RawPart {
1295 path: flat_part.path.into_boxed_str(),
1296 content_type: flat_part.content_type.into_boxed_str(),
1297 entry_index: usize::MAX,
1298 bytes: flat_part.bytes,
1299 });
1300 }
1301 }
1302
1303 let mut by_path = HashMap::with_capacity(raw_parts.len());
1304 for (index, raw_part) in raw_parts.iter().enumerate() {
1305 by_path.insert(raw_part.path.clone(), PartId::from_index(index));
1306 }
1307
1308 let content_types = content_types_from_raw_parts(&raw_parts);
1309 let package_relationships = relationships_from_flat_opc_part(
1310 relationship_parts.remove("_rels/.rels").as_deref(),
1311 "",
1312 &by_path,
1313 )?;
1314
1315 let mut part_relationships = Vec::with_capacity(raw_parts.len());
1316 for raw_part in &raw_parts {
1317 let rels_path = part_relationships_path(&raw_part.path);
1318 part_relationships.push(relationships_from_flat_opc_part(
1319 relationship_parts.remove(&rels_path).as_deref(),
1320 &raw_part.path,
1321 &by_path,
1322 )?);
1323 }
1324
1325 let relationship_types =
1326 relationship_types_by_part(&package_relationships, &part_relationships)?;
1327
1328 let mut parts = Vec::with_capacity(raw_parts.len());
1329 for (index, (raw_part, relationships)) in
1330 raw_parts.into_iter().zip(part_relationships).enumerate()
1331 {
1332 let relationship_type = relationship_types[index].clone();
1333 let kind = crate::parts::PartKind::classify(
1334 relationship_type
1335 .as_ref()
1336 .map(super::XmlRelationshipNamespaceUri::uri_bytes),
1337 raw_part.content_type.as_bytes(),
1338 &raw_part.path,
1339 );
1340 parts.push(StoredPart {
1341 path: raw_part.path,
1342 content_type: raw_part.content_type,
1343 relationship_type,
1344 kind,
1345 relationships,
1346 data: StoredPartData::Owned {
1347 bytes: raw_part.bytes.into(),
1348 original_entry_index: None,
1349 },
1350 deleted: false,
1351 });
1352 }
1353
1354 Ok(Self {
1355 id: PackageId::new(),
1356 archive: None,
1357 archived_extra_entry_indices: Box::new([]),
1358 content_types,
1359 content_types_archive_entry_index: None,
1360 package_relationships,
1361 parts,
1362 by_path,
1363 preferred_main_part_content_type: None,
1364 })
1365 }
1366
1367 #[cfg(feature = "flat-opc")]
1368 pub(crate) fn write_flat_opc<W, F>(
1369 &self,
1370 writer: &mut W,
1371 mut part_data: F,
1372 ) -> Result<(), SdkError>
1373 where
1374 W: std::io::Write,
1375 F: FnMut(PartId, &StoredPart) -> Result<Vec<u8>, SdkError>,
1376 {
1377 writer.write_all(br#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>"#)?;
1378 writer.write_all(b"\n<pkg:package")?;
1379 writer.write_all(b" xmlns:pkg=\"")?;
1380 writer.write_all(FLAT_OPC_PACKAGE_NS.as_bytes())?;
1381 writer.write_all(b"\"")?;
1382 writer.write_all(b">\n")?;
1383
1384 if !self.package_relationships.is_empty() {
1385 let bytes = self.package_relationships.to_bytes_cow()?;
1386 write_flat_opc_xml_part(
1387 writer,
1388 "/_rels/.rels",
1389 RELATIONSHIP_CONTENT_TYPE,
1390 bytes.as_ref(),
1391 )?;
1392 }
1393
1394 let alt_chunk_part_ids = self.alt_chunk_part_ids();
1395 for (index, part) in self.parts.iter().enumerate() {
1396 if part.is_deleted() {
1397 continue;
1398 }
1399
1400 let part_id = PartId::from_index(index);
1401 let data = part_data(part_id, part)?;
1402 let part_name = format!("/{}", part.path());
1403 if flat_opc_part_is_xml(part, part_id, &alt_chunk_part_ids) {
1404 write_flat_opc_xml_part(writer, &part_name, part.content_type(), &data)?;
1405 } else {
1406 write_flat_opc_binary_part(writer, &part_name, part.content_type(), &data)?;
1407 }
1408
1409 if !part.relationships().is_empty() {
1410 let bytes = part.relationships().to_bytes_cow()?;
1411 let rels_name = format!("/{}", part_relationships_path(part.path()));
1412 write_flat_opc_xml_part(
1413 writer,
1414 &rels_name,
1415 RELATIONSHIP_CONTENT_TYPE,
1416 bytes.as_ref(),
1417 )?;
1418 }
1419 }
1420
1421 writer.write_all(b"</pkg:package>")?;
1422 Ok(())
1423 }
1424
1425 #[inline]
1426 pub(crate) fn id(&self) -> PackageId {
1427 self.id
1428 }
1429
1430 #[inline]
1431 pub(crate) fn content_types(&self) -> &Types {
1432 &self.content_types
1433 }
1434
1435 pub(crate) fn package_save_entries(&self) -> Vec<PackageSaveEntry> {
1436 let mut entries = Vec::with_capacity(
1437 self.parts.len().saturating_mul(2) + self.archived_extra_entry_indices.len() + 2,
1438 );
1439 let mut sequence = 0usize;
1440 let mut push = |original_entry_index: Option<usize>, entry: PackageSaveEntry| {
1441 entries.push((original_entry_index.unwrap_or(usize::MAX), sequence, entry));
1442 sequence += 1;
1443 };
1444
1445 push(
1446 self.content_types_archive_entry_index,
1447 PackageSaveEntry::ContentTypes,
1448 );
1449 if !self.package_relationships.is_empty() {
1450 push(
1451 self.package_relationships.original_archive_entry_index(),
1452 PackageSaveEntry::PackageRelationships,
1453 );
1454 }
1455 for (index, part) in self.parts.iter().enumerate() {
1456 if part.is_deleted() {
1457 continue;
1458 }
1459 let part_id = PartId::from_index(index);
1460 if !part.relationships().is_empty() {
1461 push(
1462 part.relationships().original_archive_entry_index(),
1463 PackageSaveEntry::PartRelationships(part_id),
1464 );
1465 }
1466 push(
1467 part.data.original_entry_index(),
1468 PackageSaveEntry::Part(part_id),
1469 );
1470 }
1471 for &entry_index in &self.archived_extra_entry_indices {
1472 push(
1473 Some(entry_index),
1474 PackageSaveEntry::ArchivedExtra(entry_index),
1475 );
1476 }
1477
1478 entries.sort_unstable_by_key(|(entry_index, sequence, _)| (*entry_index, *sequence));
1479 entries.into_iter().map(|(_, _, entry)| entry).collect()
1480 }
1481
1482 pub(crate) fn configure_zip_writer<W: std::io::Write + std::io::Seek>(
1483 &self,
1484 writer: &mut zip::ZipWriter<W>,
1485 ) -> Result<(), SdkError> {
1486 if let Some(archive) = &self.archive {
1487 archive.configure_writer(writer)?;
1488 }
1489 Ok(())
1490 }
1491
1492 pub(crate) fn part_bytes(&self, part_id: PartId) -> Result<&[u8], SdkError> {
1493 let part = self.part(part_id).ok_or_else(|| {
1494 SdkError::CommonError(format!(
1495 "part id {part_id:?} is not present in package storage"
1496 ))
1497 })?;
1498 if let Some(bytes) = part.data.cached_bytes() {
1499 return Ok(bytes);
1500 }
1501
1502 let StoredPartData::Archived { entry_index, bytes } = &part.data else {
1503 unreachable!("owned part data always has cached bytes")
1504 };
1505 let archive = self.archive.as_ref().ok_or_else(|| {
1506 SdkError::CommonError("archived part has no package archive backing".to_string())
1507 })?;
1508 let loaded = archive.read_entry(*entry_index)?;
1509 let _ = bytes.set(loaded);
1510 Ok(
1511 bytes
1512 .get()
1513 .expect("part bytes were initialized or another thread initialized them"),
1514 )
1515 }
1516
1517 #[inline]
1518 pub(crate) fn discard_cached_part_bytes(&mut self, part_id: PartId) {
1519 if let Some(StoredPart {
1520 data: StoredPartData::Archived { bytes, .. },
1521 ..
1522 }) = self.part_mut(part_id)
1523 {
1524 let _ = bytes.take();
1525 }
1526 }
1527
1528 pub(crate) fn raw_copy_archive_entry<W: std::io::Write + std::io::Seek>(
1529 &self,
1530 entry_index: usize,
1531 target_name: &str,
1532 writer: &mut zip::ZipWriter<W>,
1533 ) -> Result<bool, SdkError> {
1534 let Some(archive) = &self.archive else {
1535 return Ok(false);
1536 };
1537 archive.raw_copy_entry(entry_index, target_name, writer)?;
1538 Ok(true)
1539 }
1540
1541 pub(crate) fn raw_copy_archived_extra<W: std::io::Write + std::io::Seek>(
1542 &self,
1543 entry_index: usize,
1544 writer: &mut zip::ZipWriter<W>,
1545 ) -> Result<(), SdkError> {
1546 let archive = self.archive.as_ref().ok_or_else(|| {
1547 SdkError::CommonError("archived ZIP entry has no package archive backing".to_string())
1548 })?;
1549 archive.raw_copy_entry_with_original_name(entry_index, writer)
1550 }
1551
1552 pub(crate) fn raw_copy_part<W: std::io::Write + std::io::Seek>(
1553 &self,
1554 part_id: PartId,
1555 writer: &mut zip::ZipWriter<W>,
1556 ) -> Result<bool, SdkError> {
1557 let Some(part) = self.part(part_id) else {
1558 return Ok(false);
1559 };
1560 let Some(entry_index) = part.data.unchanged_archive_entry_index() else {
1561 return Ok(false);
1562 };
1563 self.raw_copy_archive_entry(entry_index, part.path(), writer)
1564 }
1565
1566 #[inline]
1567 pub(crate) fn package_relationships(&self) -> &RelationshipSet {
1568 &self.package_relationships
1569 }
1570
1571 #[inline]
1572 pub(crate) fn package_relationships_mut(&mut self) -> &mut RelationshipSet {
1573 &mut self.package_relationships
1574 }
1575
1576 #[inline]
1577 pub(crate) fn parts(&self) -> &[StoredPart] {
1578 &self.parts
1579 }
1580
1581 #[inline]
1582 pub(crate) fn part(&self, part_id: PartId) -> Option<&StoredPart> {
1583 self
1584 .parts
1585 .get(part_id.index())
1586 .filter(|part| !part.is_deleted())
1587 }
1588
1589 #[inline]
1590 pub(crate) fn part_mut(&mut self, part_id: PartId) -> Option<&mut StoredPart> {
1591 self
1592 .parts
1593 .get_mut(part_id.index())
1594 .filter(|part| !part.is_deleted())
1595 }
1596
1597 #[inline]
1598 pub(crate) fn preferred_main_part_content_type(&self) -> Option<&str> {
1599 self.preferred_main_part_content_type
1600 }
1601
1602 #[inline]
1603 pub(crate) fn set_preferred_main_part_content_type(&mut self, content_type: &'static str) {
1604 self.preferred_main_part_content_type = Some(content_type);
1605 }
1606
1607 #[inline]
1608 pub(crate) fn media_data_parts(&self) -> impl Iterator<Item = (PartId, &StoredPart)> {
1609 self
1610 .parts
1611 .iter()
1612 .enumerate()
1613 .filter(|(_, part)| !part.is_deleted() && is_media_data_part(part))
1614 .map(|(index, part)| (PartId::from_index(index), part))
1615 }
1616
1617 #[inline]
1618 pub(crate) fn relationships(&self, part_id: PartId) -> Option<&RelationshipSet> {
1619 self.part(part_id).map(StoredPart::relationships)
1620 }
1621
1622 #[inline]
1623 pub(crate) fn relationships_mut(&mut self, part_id: PartId) -> Option<&mut RelationshipSet> {
1624 self.part_mut(part_id).map(StoredPart::relationships_mut)
1625 }
1626
1627 #[inline]
1628 pub(crate) fn target_part_id(
1629 &self,
1630 source_part_id: PartId,
1631 relationship_id: &str,
1632 ) -> Option<PartId> {
1633 self
1634 .relationships(source_part_id)?
1635 .get(relationship_id)?
1636 .target_part_id()
1637 }
1638
1639 pub(crate) fn delete_package_part(&mut self, relationship_id: &str) -> Result<bool, SdkError> {
1640 let Some(target_part_id) = self
1641 .package_relationships
1642 .get(relationship_id)
1643 .and_then(RelationshipInfo::target_part_id)
1644 else {
1645 return Ok(false);
1646 };
1647
1648 self.package_relationships.remove(relationship_id);
1649 if !self.is_part_reachable(target_part_id) {
1650 self.delete_unreachable_part_tree(target_part_id);
1651 }
1652 Ok(true)
1653 }
1654
1655 pub(crate) fn delete_child_part(
1656 &mut self,
1657 source_part_id: PartId,
1658 relationship_id: &str,
1659 ) -> Result<bool, SdkError> {
1660 let Some(target_part_id) = self.target_part_id(source_part_id, relationship_id) else {
1661 return Ok(false);
1662 };
1663
1664 let relationships = self.relationships_mut(source_part_id).ok_or_else(|| {
1665 SdkError::CommonError(format!(
1666 "part id {source_part_id:?} is not present in package storage"
1667 ))
1668 })?;
1669 relationships.remove(relationship_id);
1670 if !self.is_part_reachable(target_part_id) {
1671 self.delete_unreachable_part_tree(target_part_id);
1672 }
1673 Ok(true)
1674 }
1675
1676 pub(crate) fn add_package_relationship_to_part(
1677 &mut self,
1678 relationship_id: impl Into<String>,
1679 relationship_type: &str,
1680 target_part_id: PartId,
1681 ) -> Result<String, SdkError> {
1682 let relationship_id = relationship_id.into();
1683 if let Some(existing_relationship_id) =
1684 self.existing_relationship_id_for_target(None, target_part_id)
1685 {
1686 if existing_relationship_id == relationship_id {
1687 return Ok(existing_relationship_id);
1688 }
1689 return Err(SdkError::CommonError(format!(
1690 "part id {target_part_id:?} is already referenced by relationship id {existing_relationship_id}"
1691 )));
1692 }
1693
1694 let target = {
1695 let target_part = self.part(target_part_id).ok_or_else(|| {
1696 SdkError::CommonError(format!(
1697 "part id {target_part_id:?} is not present in package storage"
1698 ))
1699 })?;
1700 target_part.path().to_string()
1701 };
1702
1703 self.package_relationships.add_internal_part_relationship(
1704 relationship_id.clone(),
1705 relationship_type,
1706 target,
1707 target_part_id,
1708 )?;
1709 Ok(relationship_id)
1710 }
1711
1712 pub(crate) fn add_child_relationship_to_part(
1713 &mut self,
1714 source_part_id: PartId,
1715 relationship_id: impl Into<String>,
1716 relationship_type: &str,
1717 target_part_id: PartId,
1718 ) -> Result<String, SdkError> {
1719 let relationship_id = relationship_id.into();
1720 if let Some(existing_relationship_id) =
1721 self.existing_relationship_id_for_target(Some(source_part_id), target_part_id)
1722 {
1723 if existing_relationship_id == relationship_id {
1724 return Ok(existing_relationship_id);
1725 }
1726 return Err(SdkError::CommonError(format!(
1727 "part id {target_part_id:?} is already referenced by relationship id {existing_relationship_id}"
1728 )));
1729 }
1730
1731 let relationship_target = {
1732 let source_part_path = self
1733 .part(source_part_id)
1734 .ok_or_else(|| {
1735 SdkError::CommonError(format!(
1736 "part id {source_part_id:?} is not present in package storage"
1737 ))
1738 })?
1739 .path()
1740 .to_string();
1741 let target_part = self.part(target_part_id).ok_or_else(|| {
1742 SdkError::CommonError(format!(
1743 "part id {target_part_id:?} is not present in package storage"
1744 ))
1745 })?;
1746 relationship_target_from_source(&source_part_path, target_part.path())
1747 };
1748
1749 self
1750 .relationships_mut(source_part_id)
1751 .expect("source part was already resolved")
1752 .add_internal_part_relationship(
1753 relationship_id.clone(),
1754 relationship_type,
1755 relationship_target,
1756 target_part_id,
1757 )?;
1758 Ok(relationship_id)
1759 }
1760
1761 pub(crate) fn create_media_data_part(
1762 &mut self,
1763 content_type: impl Into<String>,
1764 extension: impl AsRef<str>,
1765 ) -> Result<PartId, SdkError> {
1766 let content_type = content_type.into();
1767 if content_type.is_empty() {
1768 return Err(SdkError::CommonError(
1769 "cannot add a media data part with an empty content type".to_string(),
1770 ));
1771 }
1772 let extension = normalized_part_extension(extension.as_ref());
1773 let path = self.next_data_part_path("media/mediadata", &extension);
1774 let part_id = self.push_part(path, &content_type, None);
1775 Ok(part_id)
1776 }
1777
1778 pub(crate) fn add_data_part_reference_relationship(
1779 &mut self,
1780 source_part_id: PartId,
1781 relationship_id: impl Into<String>,
1782 relationship_type: &str,
1783 target_part_id: PartId,
1784 ) -> Result<String, SdkError> {
1785 let relationship_id = relationship_id.into();
1786 let relationship_target = {
1787 let source_part_path = self
1788 .part(source_part_id)
1789 .ok_or_else(|| {
1790 SdkError::CommonError(format!(
1791 "part id {source_part_id:?} is not present in package storage"
1792 ))
1793 })?
1794 .path()
1795 .to_string();
1796 let target_part = self.part(target_part_id).ok_or_else(|| {
1797 SdkError::CommonError(format!(
1798 "part id {target_part_id:?} is not present in package storage"
1799 ))
1800 })?;
1801 relationship_target_from_source(&source_part_path, target_part.path())
1802 };
1803
1804 self
1805 .relationships_mut(source_part_id)
1806 .expect("source part was already resolved")
1807 .add_internal_part_relationship(
1808 relationship_id.clone(),
1809 relationship_type,
1810 relationship_target,
1811 target_part_id,
1812 )?;
1813 Ok(relationship_id)
1814 }
1815
1816 pub(crate) fn import_part_tree_from(
1817 &mut self,
1818 source: &Self,
1819 source_part_id: PartId,
1820 parent_part_id: Option<PartId>,
1821 relationship_id: impl Into<String>,
1822 relationship_type: &str,
1823 mut part_data: impl FnMut(PartId, &StoredPart) -> Result<Vec<u8>, SdkError>,
1824 ) -> Result<(PartId, usize), SdkError> {
1825 let relationship_id = relationship_id.into();
1826 source.part(source_part_id).ok_or_else(|| {
1827 SdkError::CommonError(format!(
1828 "source part id {source_part_id:?} is not present in package storage"
1829 ))
1830 })?;
1831
1832 match parent_part_id {
1833 Some(parent_part_id) => {
1834 let relationships = self.relationships(parent_part_id).ok_or_else(|| {
1835 SdkError::CommonError(format!(
1836 "part id {parent_part_id:?} is not present in package storage"
1837 ))
1838 })?;
1839 if relationships.contains_id(&relationship_id) {
1840 return Err(SdkError::CommonError(format!(
1841 "relationship id {relationship_id} already exists"
1842 )));
1843 }
1844 }
1845 None => {
1846 if self.package_relationships.contains_id(&relationship_id) {
1847 return Err(SdkError::CommonError(format!(
1848 "relationship id {relationship_id} already exists"
1849 )));
1850 }
1851 }
1852 }
1853
1854 let mut part_map = HashMap::new();
1855 let mut added_count = 0;
1856 let imported_part_id = self.import_part_tree_recursive(
1857 source,
1858 source_part_id,
1859 &mut part_map,
1860 &mut added_count,
1861 &mut part_data,
1862 )?;
1863 self.add_imported_part_relationship(
1864 parent_part_id,
1865 relationship_id,
1866 relationship_type,
1867 imported_part_id,
1868 )?;
1869 Ok((imported_part_id, added_count))
1870 }
1871
1872 #[inline]
1873 pub(crate) fn data_part_reference_relationships_to(
1874 &self,
1875 target_part_id: PartId,
1876 ) -> impl Iterator<Item = &RelationshipInfo> {
1877 std::iter::once(self.package_relationships())
1878 .chain(
1879 self
1880 .parts
1881 .iter()
1882 .filter(|part| !part.is_deleted())
1883 .map(StoredPart::relationships),
1884 )
1885 .flat_map(RelationshipSet::data_part_reference_relationships)
1886 .filter(move |relationship| relationship.target_part_id() == Some(target_part_id))
1887 }
1888
1889 pub(crate) fn delete_unused_media_data_parts(&mut self) -> usize {
1890 let unused_part_ids: Vec<_> = self
1891 .media_data_parts()
1892 .filter_map(|(part_id, _)| {
1893 self
1894 .data_part_reference_relationships_to(part_id)
1895 .next()
1896 .is_none()
1897 .then_some(part_id)
1898 })
1899 .collect();
1900
1901 let deleted_count = unused_part_ids.len();
1902 for part_id in unused_part_ids {
1903 let Some(part) = self.part(part_id) else {
1904 continue;
1905 };
1906 let path = part.path().to_string();
1907 if let Some(part) = self.parts.get_mut(part_id.index()) {
1908 part.deleted = true;
1909 }
1910 self.by_path.remove(path.as_str());
1911 self.remove_content_type_override(&path);
1912 }
1913 deleted_count
1914 }
1915
1916 fn import_part_tree_recursive(
1917 &mut self,
1918 source: &Self,
1919 source_part_id: PartId,
1920 part_map: &mut HashMap<PartId, PartId>,
1921 added_count: &mut usize,
1922 part_data: &mut impl FnMut(PartId, &StoredPart) -> Result<Vec<u8>, SdkError>,
1923 ) -> Result<PartId, SdkError> {
1924 if let Some(imported_part_id) = part_map.get(&source_part_id).copied() {
1925 return Ok(imported_part_id);
1926 }
1927
1928 let source_part = source.part(source_part_id).ok_or_else(|| {
1929 SdkError::CommonError(format!(
1930 "source part id {source_part_id:?} is not present in package storage"
1931 ))
1932 })?;
1933 let imported_path = self.unique_import_part_path(source_part.path());
1934 let imported_part_id = self.push_part(
1935 imported_path,
1936 source_part.content_type(),
1937 source_part.relationship_type(),
1938 );
1939 self.set_part_data(imported_part_id, part_data(source_part_id, source_part)?)?;
1940 *added_count += 1;
1941 part_map.insert(source_part_id, imported_part_id);
1942
1943 for relationship in source_part.relationships().iter() {
1944 if let Some(target_part_id) = relationship.target_part_id() {
1945 let imported_target_part_id = self.import_part_tree_recursive(
1946 source,
1947 target_part_id,
1948 part_map,
1949 added_count,
1950 part_data,
1951 )?;
1952 if relationship.is_reference_relationship() {
1953 self.add_data_part_reference_relationship(
1954 imported_part_id,
1955 relationship.id(),
1956 relationship.relationship_type(),
1957 imported_target_part_id,
1958 )?;
1959 } else {
1960 self.add_imported_part_relationship(
1961 Some(imported_part_id),
1962 relationship.id().to_string(),
1963 relationship.relationship_type(),
1964 imported_target_part_id,
1965 )?;
1966 }
1967 } else if relationship.is_reference_relationship()
1968 || relationship.target_kind() == RelationshipTargetKind::External
1969 {
1970 self
1971 .relationships_mut(imported_part_id)
1972 .expect("imported part was just created")
1973 .add_relationship_info(relationship.clone())?;
1974 }
1975 }
1976
1977 Ok(imported_part_id)
1978 }
1979
1980 fn add_imported_part_relationship(
1981 &mut self,
1982 parent_part_id: Option<PartId>,
1983 relationship_id: String,
1984 relationship_type: &str,
1985 target_part_id: PartId,
1986 ) -> Result<(), SdkError> {
1987 let target_part_path = self
1988 .part(target_part_id)
1989 .ok_or_else(|| {
1990 SdkError::CommonError(format!(
1991 "part id {target_part_id:?} is not present in package storage"
1992 ))
1993 })?
1994 .path()
1995 .to_string();
1996 let target = if let Some(parent_part_id) = parent_part_id {
1997 let parent_part_path = self
1998 .part(parent_part_id)
1999 .ok_or_else(|| {
2000 SdkError::CommonError(format!(
2001 "part id {parent_part_id:?} is not present in package storage"
2002 ))
2003 })?
2004 .path()
2005 .to_string();
2006 relationship_target_from_source(&parent_part_path, &target_part_path)
2007 } else {
2008 target_part_path
2009 };
2010 let relationships = match parent_part_id {
2011 Some(parent_part_id) => self.relationships_mut(parent_part_id).ok_or_else(|| {
2012 SdkError::CommonError(format!(
2013 "part id {parent_part_id:?} is not present in package storage"
2014 ))
2015 })?,
2016 None => self.package_relationships_mut(),
2017 };
2018 relationships.add_internal_part_relationship(
2019 relationship_id,
2020 relationship_type,
2021 target,
2022 target_part_id,
2023 )?;
2024 Ok(())
2025 }
2026
2027 pub(crate) fn add_child_part(
2028 &mut self,
2029 source_part_id: PartId,
2030 relationship_id: impl Into<String>,
2031 descriptor: NewPartDescriptor,
2032 ) -> Result<PartId, SdkError> {
2033 if descriptor.relationship_type.is_empty() {
2034 return Err(SdkError::CommonError(
2035 "cannot add a part with an empty relationship type".to_string(),
2036 ));
2037 }
2038 if descriptor.content_type.is_empty() {
2039 return Err(SdkError::CommonError(
2040 "cannot add a part with an empty content type".to_string(),
2041 ));
2042 }
2043 if descriptor.target_name.is_empty() {
2044 return Err(SdkError::CommonError(
2045 "cannot add a part with an empty target name".to_string(),
2046 ));
2047 }
2048
2049 let relationship_id = relationship_id.into();
2050 let source_part_path = self
2051 .part(source_part_id)
2052 .ok_or_else(|| {
2053 SdkError::CommonError(format!(
2054 "part id {source_part_id:?} is not present in package storage"
2055 ))
2056 })?
2057 .path()
2058 .to_string();
2059 if self
2060 .relationships(source_part_id)
2061 .is_some_and(|relationships| relationships.contains_id(&relationship_id))
2062 {
2063 return Err(SdkError::CommonError(format!(
2064 "relationship id {relationship_id} already exists"
2065 )));
2066 }
2067
2068 let child_path = self.next_child_part_path(
2069 &source_part_path,
2070 descriptor.path_prefix,
2071 descriptor.target_name,
2072 descriptor.extension.as_ref(),
2073 descriptor.content_type.as_ref(),
2074 );
2075 let relationship_target = relationship_target_from_source(&source_part_path, &child_path);
2076 let part_id = self.push_part(
2077 child_path,
2078 &descriptor.content_type,
2079 Some(descriptor.relationship_type.as_ref()),
2080 );
2081 self
2082 .relationships_mut(source_part_id)
2083 .expect("source part was already resolved")
2084 .add_internal_part_relationship(
2085 relationship_id,
2086 descriptor.relationship_type.as_ref(),
2087 relationship_target,
2088 part_id,
2089 )?;
2090 Ok(part_id)
2091 }
2092
2093 pub(crate) fn add_package_part(
2094 &mut self,
2095 relationship_id: impl Into<String>,
2096 descriptor: NewPartDescriptor,
2097 target_mode: NewPartTargetMode,
2098 ) -> Result<PartId, SdkError> {
2099 if descriptor.relationship_type.is_empty() {
2100 return Err(SdkError::CommonError(
2101 "cannot add a part with an empty relationship type".to_string(),
2102 ));
2103 }
2104 if descriptor.content_type.is_empty() {
2105 return Err(SdkError::CommonError(
2106 "cannot add a part with an empty content type".to_string(),
2107 ));
2108 }
2109 if descriptor.target_name.is_empty() {
2110 return Err(SdkError::CommonError(
2111 "cannot add a part with an empty target name".to_string(),
2112 ));
2113 }
2114
2115 let relationship_id = relationship_id.into();
2116 if self.package_relationships.contains_id(&relationship_id) {
2117 return Err(SdkError::CommonError(format!(
2118 "relationship id {relationship_id} already exists"
2119 )));
2120 }
2121
2122 let part_path = self.package_part_path(&descriptor, target_mode)?;
2123 let part_id = self.push_part(
2124 part_path.clone(),
2125 &descriptor.content_type,
2126 Some(descriptor.relationship_type.as_ref()),
2127 );
2128 self.package_relationships.add_internal_part_relationship(
2129 relationship_id,
2130 descriptor.relationship_type.as_ref(),
2131 part_path,
2132 part_id,
2133 )?;
2134 Ok(part_id)
2135 }
2136
2137 pub(crate) fn add_child_part_with_path(
2138 &mut self,
2139 source_part_id: PartId,
2140 relationship_id: impl Into<String>,
2141 descriptor: NewPartDescriptor,
2142 part_path: impl AsRef<str>,
2143 ) -> Result<PartId, SdkError> {
2144 if descriptor.relationship_type.is_empty() {
2145 return Err(SdkError::CommonError(
2146 "cannot add a part with an empty relationship type".to_string(),
2147 ));
2148 }
2149 if descriptor.content_type.is_empty() {
2150 return Err(SdkError::CommonError(
2151 "cannot add a part with an empty content type".to_string(),
2152 ));
2153 }
2154
2155 let relationship_id = relationship_id.into();
2156 let source_part_path = self
2157 .part(source_part_id)
2158 .ok_or_else(|| {
2159 SdkError::CommonError(format!(
2160 "part id {source_part_id:?} is not present in package storage"
2161 ))
2162 })?
2163 .path()
2164 .to_string();
2165 if self
2166 .relationships(source_part_id)
2167 .is_some_and(|relationships| relationships.contains_id(&relationship_id))
2168 {
2169 return Err(SdkError::CommonError(format!(
2170 "relationship id {relationship_id} already exists"
2171 )));
2172 }
2173
2174 let child_path =
2175 self.unique_new_part_path(part_path.as_ref(), descriptor.content_type.as_ref())?;
2176
2177 let relationship_target = relationship_target_from_source(&source_part_path, &child_path);
2178 let part_id = self.push_part(
2179 child_path,
2180 &descriptor.content_type,
2181 Some(descriptor.relationship_type.as_ref()),
2182 );
2183 self
2184 .relationships_mut(source_part_id)
2185 .expect("source part was already resolved")
2186 .add_internal_part_relationship(
2187 relationship_id,
2188 descriptor.relationship_type.as_ref(),
2189 relationship_target,
2190 part_id,
2191 )?;
2192 Ok(part_id)
2193 }
2194
2195 fn push_part(
2196 &mut self,
2197 path: String,
2198 content_type: &str,
2199 relationship_type: Option<&str>,
2200 ) -> PartId {
2201 let part_id = PartId::from_index(self.parts.len());
2202 let kind = crate::parts::PartKind::classify(
2203 relationship_type.map(str::as_bytes),
2204 content_type.as_bytes(),
2205 &path,
2206 );
2207 self.parts.push(StoredPart {
2208 path: path.clone().into_boxed_str(),
2209 content_type: content_type.into(),
2210 relationship_type: relationship_type.map(super::XmlRelationshipNamespaceUri::from_uri),
2211 kind,
2212 relationships: RelationshipSet::default(),
2213 data: StoredPartData::Owned {
2214 bytes: Bytes::new(),
2215 original_entry_index: None,
2216 },
2217 deleted: false,
2218 });
2219 self.by_path.insert(path.clone().into_boxed_str(), part_id);
2220 self.add_content_type_override(&path, content_type);
2221 part_id
2222 }
2223
2224 pub(crate) fn set_part_data(
2225 &mut self,
2226 part_id: PartId,
2227 data: impl Into<Vec<u8>>,
2228 ) -> Result<(), SdkError> {
2229 let part = self.part_mut(part_id).ok_or_else(|| {
2230 SdkError::CommonError(format!(
2231 "part id {part_id:?} is not present in package storage"
2232 ))
2233 })?;
2234 part.data.set_owned(data.into());
2235 Ok(())
2236 }
2237
2238 pub(crate) fn feed_part_data<R: Read>(
2239 &mut self,
2240 part_id: PartId,
2241 reader: &mut R,
2242 ) -> Result<(), SdkError> {
2243 let part = self.part_mut(part_id).ok_or_else(|| {
2244 SdkError::CommonError(format!(
2245 "part id {part_id:?} is not present in package storage"
2246 ))
2247 })?;
2248 let mut bytes = Vec::new();
2249 reader.read_to_end(&mut bytes)?;
2250 part.data.set_owned(bytes);
2251 Ok(())
2252 }
2253
2254 fn add_content_type_override(&mut self, path: &str, content_type: &str) {
2255 let part_name = format!("/{path}");
2256 if let Some(existing) = self
2257 .content_types
2258 .types_choice
2259 .iter_mut()
2260 .find_map(|child| match child {
2261 TypesChoice::Override(override_type) if override_type.part_name == part_name => {
2262 Some(override_type)
2263 }
2264 _ => None,
2265 })
2266 {
2267 existing.content_type = content_type.to_string();
2268 return;
2269 }
2270
2271 self.content_types.types_choice.push(TypesChoice::Override(
2272 crate::schemas::opc_content_types::Override {
2273 content_type: content_type.to_string(),
2274 part_name,
2275 },
2276 ));
2277 }
2278
2279 fn remove_content_type_override(&mut self, path: &str) {
2280 let part_name = format!("/{path}");
2281 self
2282 .content_types
2283 .types_choice
2284 .retain(|child| !matches!(child, TypesChoice::Override(override_type) if override_type.part_name == part_name));
2285 }
2286
2287 pub(crate) fn set_part_content_type(
2288 &mut self,
2289 part_id: PartId,
2290 content_type: impl Into<Box<str>>,
2291 ) -> Result<(), SdkError> {
2292 let content_type = content_type.into();
2293 if content_type.is_empty() {
2294 return Err(SdkError::CommonError(
2295 "cannot set a part to an empty content type".to_string(),
2296 ));
2297 }
2298 let path = {
2299 let part = self.part_mut(part_id).ok_or_else(|| {
2300 SdkError::CommonError(format!(
2301 "part id {part_id:?} is not present in package storage"
2302 ))
2303 })?;
2304 part.content_type = content_type.clone();
2305 part.kind = crate::parts::PartKind::classify(
2306 part.relationship_type_bytes(),
2307 content_type.as_bytes(),
2308 part.path(),
2309 );
2310 part.path().to_string()
2311 };
2312 self.add_content_type_override(&path, &content_type);
2313 Ok(())
2314 }
2315
2316 fn existing_relationship_id_for_target(
2317 &self,
2318 source_part_id: Option<PartId>,
2319 target_part_id: PartId,
2320 ) -> Option<String> {
2321 let relationships = match source_part_id {
2322 Some(source_part_id) => self.relationships(source_part_id)?,
2323 None => self.package_relationships(),
2324 };
2325 relationships.iter().find_map(|relationship| {
2326 (relationship.target_part_id() == Some(target_part_id)).then(|| relationship.id().to_string())
2327 })
2328 }
2329
2330 fn is_part_reachable(&self, target_part_id: PartId) -> bool {
2331 let mut visited = HashSet::new();
2332 let mut stack: Vec<_> = self
2333 .package_relationships
2334 .part_relationships()
2335 .filter_map(RelationshipInfo::target_part_id)
2336 .collect();
2337
2338 while let Some(part_id) = stack.pop() {
2339 if !visited.insert(part_id) {
2340 continue;
2341 }
2342 let Some(part) = self.part(part_id) else {
2343 continue;
2344 };
2345 if part_id == target_part_id {
2346 return true;
2347 }
2348 stack.extend(
2349 part
2350 .relationships()
2351 .part_relationships()
2352 .filter_map(RelationshipInfo::target_part_id),
2353 );
2354 }
2355
2356 false
2357 }
2358
2359 fn delete_unreachable_part_tree(&mut self, part_id: PartId) {
2360 let Some(part) = self.part(part_id) else {
2361 return;
2362 };
2363 let child_part_ids: Vec<_> = part
2364 .relationships()
2365 .part_relationships()
2366 .filter_map(RelationshipInfo::target_part_id)
2367 .collect();
2368 let path = part.path().to_string();
2369
2370 if let Some(part) = self.parts.get_mut(part_id.index()) {
2371 part.deleted = true;
2372 }
2373 self.by_path.remove(path.as_str());
2374 self.remove_content_type_override(&path);
2375
2376 for child_part_id in child_part_ids {
2377 if !self.is_part_reachable(child_part_id) {
2378 self.delete_unreachable_part_tree(child_part_id);
2379 }
2380 }
2381 }
2382
2383 fn unique_import_part_path(&self, source_path: &str) -> String {
2384 if !self.by_path.contains_key(source_path) {
2385 return source_path.to_string();
2386 }
2387
2388 let (base, extension) = match source_path.rsplit_once('.') {
2389 Some((base, extension)) => (base, format!(".{extension}")),
2390 None => (source_path, String::new()),
2391 };
2392 for index in 1.. {
2393 let candidate = format!("{base}_copy{index}{extension}");
2394 if !self.by_path.contains_key(candidate.as_str()) {
2395 return candidate;
2396 }
2397 }
2398 unreachable!("unbounded import path search should always find a candidate")
2399 }
2400
2401 fn next_child_part_path(
2402 &self,
2403 source_part_path: &str,
2404 path_prefix: &str,
2405 target_name: &str,
2406 extension: &str,
2407 content_type: &str,
2408 ) -> String {
2409 let directory_path = child_part_directory_path(source_part_path, path_prefix);
2410 let extension = if extension.is_empty() {
2411 ".xml"
2412 } else {
2413 extension
2414 };
2415 let extension = if extension.starts_with('.') {
2416 extension.to_string()
2417 } else {
2418 format!(".{extension}")
2419 };
2420
2421 let mut sequence = first_part_path_sequence(content_type);
2422 loop {
2423 let path = part_path_with_sequence(&directory_path, target_name, &extension, sequence);
2424 if !self.by_path.contains_key(path.as_str()) {
2425 return path;
2426 }
2427 sequence = next_part_path_sequence(sequence);
2428 }
2429 }
2430
2431 fn package_part_path(
2432 &self,
2433 descriptor: &NewPartDescriptor,
2434 target_mode: NewPartTargetMode,
2435 ) -> Result<String, SdkError> {
2436 let directory_path = package_part_directory_path(descriptor.path_prefix);
2437 let extension = normalized_part_extension(descriptor.extension.as_ref());
2438
2439 if matches!(target_mode, NewPartTargetMode::Fixed) {
2440 let path = if directory_path.is_empty() {
2441 format!("{}{}", descriptor.target_name, extension)
2442 } else {
2443 format!("{directory_path}{}{}", descriptor.target_name, extension)
2444 };
2445 if self.by_path.contains_key(path.as_str()) {
2446 return Err(SdkError::CommonError(format!(
2447 "part path {path} already exists"
2448 )));
2449 }
2450 return Ok(path);
2451 }
2452
2453 let mut sequence = first_part_path_sequence(descriptor.content_type.as_ref());
2454 loop {
2455 let path = part_path_with_sequence(
2456 &directory_path,
2457 descriptor.target_name,
2458 &extension,
2459 sequence,
2460 );
2461 if !self.by_path.contains_key(path.as_str()) {
2462 return Ok(path);
2463 }
2464 sequence = next_part_path_sequence(sequence);
2465 }
2466 }
2467
2468 fn next_data_part_path(&self, stem: &str, extension: &str) -> String {
2469 for index in 1.. {
2470 let path = format!("{stem}{index}{extension}");
2471 if !self.by_path.contains_key(path.as_str()) {
2472 return path;
2473 }
2474 }
2475
2476 unreachable!("usize iteration should always find a free data part path")
2477 }
2478
2479 fn unique_new_part_path(&self, path: &str, content_type: &str) -> Result<String, SdkError> {
2480 let normalized = normalized_new_part_path(path)?;
2481 let (stem, extension) = part_path_stem_and_extension(&normalized);
2482
2483 let mut sequence = if is_numbered_part_content_type(content_type) {
2484 1
2485 } else {
2486 0
2487 };
2488
2489 loop {
2490 let candidate = if sequence == 0 {
2491 normalized.clone()
2492 } else {
2493 format!("{stem}{sequence}{extension}")
2494 };
2495 if !self.by_path.contains_key(candidate.as_str()) {
2496 return Ok(candidate);
2497 }
2498 sequence = if sequence == 0 { 2 } else { sequence + 1 };
2499 }
2500 }
2501
2502 #[cfg(feature = "flat-opc")]
2503 fn alt_chunk_part_ids(&self) -> HashSet<PartId> {
2504 self
2505 .parts
2506 .iter()
2507 .filter(|part| !part.is_deleted())
2508 .flat_map(|part| part.relationships().iter())
2509 .chain(self.package_relationships().iter())
2510 .filter(|relationship| {
2511 super::relationship_type_matches_bytes(
2512 relationship.relationship_type_bytes(),
2513 super::REL_AF_CHUNK,
2514 )
2515 })
2516 .filter_map(RelationshipInfo::target_part_id)
2517 .collect()
2518 }
2519}
2520
2521struct RawPart {
2522 path: Box<str>,
2523 content_type: Box<str>,
2524 entry_index: usize,
2525 #[cfg(feature = "flat-opc")]
2526 bytes: Vec<u8>,
2527}
2528
2529struct LoadedRelationshipEntry {
2530 entry_index: usize,
2531 bytes: Vec<u8>,
2532 relationships: Option<Relationships>,
2533}
2534
2535struct OpenedArchiveModel {
2536 content_types: Types,
2537 content_types_archive_entry_index: usize,
2538 raw_parts: Vec<RawPart>,
2539 package_relationships: Option<LoadedRelationshipEntry>,
2540 part_relationships: Vec<Option<LoadedRelationshipEntry>>,
2541}
2542
2543struct ContentTypeResolver<'a> {
2544 overrides: HashMap<&'a str, &'a str>,
2545 defaults: HashMap<&'a str, &'a str>,
2546}
2547
2548impl<'a> ContentTypeResolver<'a> {
2549 fn new(content_types: &'a Types) -> Self {
2550 let mut overrides = HashMap::with_capacity(content_types.types_choice.len());
2551 let mut defaults = HashMap::with_capacity(content_types.types_choice.len());
2552
2553 for child in &content_types.types_choice {
2554 match child {
2555 TypesChoice::Override(override_type) => {
2556 overrides.insert(
2557 override_type.part_name.trim_start_matches('/'),
2558 override_type.content_type.as_str(),
2559 );
2560 }
2561 TypesChoice::Default(default_type) => {
2562 defaults.insert(
2563 default_type.extension.as_str(),
2564 default_type.content_type.as_str(),
2565 );
2566 }
2567 }
2568 }
2569
2570 Self {
2571 overrides,
2572 defaults,
2573 }
2574 }
2575
2576 fn content_type_for_part(&self, path: &str) -> Option<&'a str> {
2577 self.overrides.get(path).copied().or_else(|| {
2578 path
2579 .rsplit_once('.')
2580 .and_then(|(_, extension)| self.defaults.get(extension).copied())
2581 })
2582 }
2583}
2584
2585#[cfg(feature = "flat-opc")]
2586struct FlatOpcPart {
2587 path: String,
2588 content_type: String,
2589 bytes: Vec<u8>,
2590}
2591
2592#[cfg(feature = "flat-opc")]
2593fn content_types_from_raw_parts(raw_parts: &[RawPart]) -> Types {
2594 Types {
2595 types_choice: raw_parts
2596 .iter()
2597 .map(|part| {
2598 TypesChoice::Override(crate::schemas::opc_content_types::Override {
2599 content_type: part.content_type.to_string(),
2600 part_name: format!("/{}", part.path),
2601 })
2602 })
2603 .collect(),
2604 ..empty_content_types()
2605 }
2606}
2607
2608fn empty_content_types() -> Types {
2609 Types {
2610 xmlns: vec![super::XmlNamespace::raw(
2611 "",
2612 "http://schemas.openxmlformats.org/package/2006/content-types",
2613 )],
2614 types_choice: vec![TypesChoice::Default(
2615 crate::schemas::opc_content_types::Default {
2616 extension: "rels".to_string(),
2617 content_type: RELATIONSHIP_CONTENT_TYPE.to_string(),
2618 },
2619 )],
2620 }
2621}
2622
2623#[cfg(feature = "flat-opc")]
2624fn relationships_from_flat_opc_part(
2625 bytes: Option<&[u8]>,
2626 source_path: &str,
2627 by_path: &HashMap<Box<str>, PartId>,
2628) -> Result<RelationshipSet, SdkError> {
2629 let relationships = bytes
2630 .map(<Relationships as crate::sdk::SdkType>::from_bytes)
2631 .transpose()?;
2632 Ok(RelationshipSet::from_relationships(
2633 relationships,
2634 source_path,
2635 by_path,
2636 ))
2637}
2638
2639#[cfg(feature = "flat-opc")]
2640fn read_flat_opc_parts<R: std::io::BufRead>(reader: R) -> Result<Vec<FlatOpcPart>, SdkError> {
2641 let mut xml_reader = super::from_reader_inner(reader);
2642 let mut parts = Vec::new();
2643
2644 loop {
2645 match xml_reader.next_tag_event()? {
2646 super::PayloadEvent::Start(start, empty_tag)
2647 if qname_matches(start.name().as_ref(), b"part") =>
2648 {
2649 if empty_tag {
2650 return Err(SdkError::CommonError(
2651 "Flat OPC part must contain xmlData or binaryData".to_string(),
2652 ));
2653 }
2654 parts.push(read_flat_opc_part(&mut xml_reader, start)?);
2655 }
2656 super::PayloadEvent::Eof => break,
2657 _ => {}
2658 }
2659 }
2660
2661 Ok(parts)
2662}
2663
2664#[cfg(feature = "flat-opc")]
2665fn read_flat_opc_part<R: std::io::BufRead>(
2666 xml_reader: &mut super::xml::IoReader<R>,
2667 start: quick_xml::events::BytesStart<'static>,
2668) -> Result<FlatOpcPart, SdkError> {
2669 let decoder = xml_reader.decoder();
2670 let mut path = None;
2671 let mut content_type = None;
2672
2673 for attr in start.attributes() {
2674 let attr = attr?;
2675 if qname_matches(attr.key.as_ref(), b"name") {
2676 let value = attr.decoded_and_normalized_value(quick_xml::XmlVersion::Implicit1_0, decoder)?;
2677 path = Some(normalize_flat_opc_part_name(&value));
2678 } else if qname_matches(attr.key.as_ref(), b"contentType") {
2679 content_type = Some(
2680 attr
2681 .decoded_and_normalized_value(quick_xml::XmlVersion::Implicit1_0, decoder)?
2682 .into_owned(),
2683 );
2684 }
2685 }
2686
2687 let path =
2688 path.ok_or_else(|| SdkError::CommonError("Flat OPC part missing pkg:name".to_string()))?;
2689 let content_type = content_type
2690 .ok_or_else(|| SdkError::CommonError("Flat OPC part missing pkg:contentType".to_string()))?;
2691 let mut bytes = None;
2692
2693 loop {
2694 match xml_reader.next_tag_event()? {
2695 super::PayloadEvent::Start(start, empty_tag)
2696 if qname_matches(start.name().as_ref(), b"xmlData") =>
2697 {
2698 bytes = Some(read_flat_opc_xml_data(xml_reader, empty_tag)?);
2699 }
2700 super::PayloadEvent::Start(start, empty_tag)
2701 if qname_matches(start.name().as_ref(), b"binaryData") =>
2702 {
2703 bytes = Some(read_flat_opc_binary_data(xml_reader, empty_tag)?);
2704 }
2705 super::PayloadEvent::Start(_, true) => {}
2706 super::PayloadEvent::Start(_, false) => {
2707 return Err(SdkError::CommonError(
2708 "unsupported Flat OPC part child element".to_string(),
2709 ));
2710 }
2711 super::PayloadEvent::End(end) if qname_matches(end.name().as_ref(), b"part") => break,
2712 super::PayloadEvent::Eof => return Err(unexpected_eof("Flat OPC part")),
2713 _ => {}
2714 }
2715 }
2716
2717 let bytes = bytes.ok_or_else(|| {
2718 SdkError::CommonError("Flat OPC part must contain xmlData or binaryData".to_string())
2719 })?;
2720
2721 Ok(FlatOpcPart {
2722 path,
2723 content_type,
2724 bytes,
2725 })
2726}
2727
2728#[cfg(feature = "flat-opc")]
2729fn read_flat_opc_xml_data<R: std::io::BufRead>(
2730 xml_reader: &mut super::xml::IoReader<R>,
2731 empty_tag: bool,
2732) -> Result<Vec<u8>, SdkError> {
2733 if empty_tag {
2734 return Err(SdkError::CommonError(
2735 "Flat OPC xmlData must contain an XML root element".to_string(),
2736 ));
2737 }
2738
2739 let mut bytes = None;
2740 loop {
2741 match xml_reader.next_tag_event()? {
2742 super::PayloadEvent::Start(start, empty_tag) => {
2743 bytes = Some(super::read_outer_xml_io(xml_reader, start, empty_tag)?.into_bytes());
2744 }
2745 super::PayloadEvent::End(end) if qname_matches(end.name().as_ref(), b"xmlData") => break,
2746 super::PayloadEvent::Eof => return Err(unexpected_eof("Flat OPC xmlData")),
2747 _ => {}
2748 }
2749 }
2750
2751 bytes.ok_or_else(|| {
2752 SdkError::CommonError("Flat OPC xmlData must contain an XML root element".to_string())
2753 })
2754}
2755
2756#[cfg(feature = "flat-opc")]
2757fn read_flat_opc_binary_data<R: std::io::BufRead>(
2758 xml_reader: &mut super::xml::IoReader<R>,
2759 empty_tag: bool,
2760) -> Result<Vec<u8>, SdkError> {
2761 if empty_tag {
2762 return Ok(Vec::new());
2763 }
2764
2765 let mut text = String::new();
2766 loop {
2767 match xml_reader.next()? {
2768 super::xml::PayloadEvent::Text(event) => {
2769 text.push_str(event.xml10_content()?.as_ref());
2770 }
2771 super::xml::PayloadEvent::CData(event) => {
2772 text.push_str(event.xml10_content()?.as_ref());
2773 }
2774 super::xml::PayloadEvent::End(end) if qname_matches(end.name().as_ref(), b"binaryData") => {
2775 break;
2776 }
2777 super::xml::PayloadEvent::Eof => return Err(unexpected_eof("Flat OPC binaryData")),
2778 _ => {}
2779 }
2780 }
2781
2782 let compact: Vec<u8> = text
2783 .bytes()
2784 .filter(|byte| !byte.is_ascii_whitespace())
2785 .collect();
2786 base64::Engine::decode(&base64::engine::general_purpose::STANDARD, compact)
2787 .map_err(|err| SdkError::CommonError(format!("invalid Flat OPC binaryData: {err}")))
2788}
2789
2790#[cfg(feature = "flat-opc")]
2791fn write_flat_opc_xml_part<W: std::io::Write>(
2792 writer: &mut W,
2793 name: &str,
2794 content_type: &str,
2795 bytes: &[u8],
2796) -> Result<(), SdkError> {
2797 writer.write_all(b" <pkg:part")?;
2798 writer.write_all(b" pkg:name=\"")?;
2799 super::write_escaped_str(writer, name)?;
2800 writer.write_all(b"\" pkg:contentType=\"")?;
2801 super::write_escaped_str(writer, content_type)?;
2802 writer.write_all(b"\"")?;
2803 writer.write_all(b">\n <pkg:xmlData>")?;
2804 writer.write_all(root_xml_bytes(bytes)?.as_slice())?;
2805 writer.write_all(b"</pkg:xmlData>\n </pkg:part>\n")?;
2806 Ok(())
2807}
2808
2809#[cfg(feature = "flat-opc")]
2810fn write_flat_opc_binary_part<W: std::io::Write>(
2811 writer: &mut W,
2812 name: &str,
2813 content_type: &str,
2814 bytes: &[u8],
2815) -> Result<(), SdkError> {
2816 writer.write_all(b" <pkg:part")?;
2817 writer.write_all(b" pkg:name=\"")?;
2818 super::write_escaped_str(writer, name)?;
2819 writer.write_all(b"\" pkg:contentType=\"")?;
2820 super::write_escaped_str(writer, content_type)?;
2821 writer.write_all(b"\" pkg:compression=\"store\"")?;
2822 writer.write_all(b">\n <pkg:binaryData>")?;
2823 let encoded = base64::Engine::encode(&base64::engine::general_purpose::STANDARD, bytes);
2824 writer.write_all(encoded.as_bytes())?;
2825 writer.write_all(b"</pkg:binaryData>\n </pkg:part>\n")?;
2826 Ok(())
2827}
2828
2829#[cfg(feature = "flat-opc")]
2830fn root_xml_bytes(bytes: &[u8]) -> Result<Vec<u8>, SdkError> {
2831 let mut xml_reader = super::from_bytes_inner(bytes);
2832 loop {
2833 match xml_reader.next_tag_event()? {
2834 super::PayloadEvent::Start(start, empty_tag) => {
2835 return Ok(super::read_outer_xml_borrowed(&mut xml_reader, start, empty_tag)?.into_bytes());
2836 }
2837 super::PayloadEvent::Eof => return Err(unexpected_eof("Flat OPC XML part")),
2838 _ => {}
2839 }
2840 }
2841}
2842
2843#[cfg(feature = "flat-opc")]
2844fn flat_opc_part_is_xml(
2845 part: &StoredPart,
2846 part_id: PartId,
2847 alt_chunk_part_ids: &HashSet<PartId>,
2848) -> bool {
2849 part.content_type().ends_with("xml") && !alt_chunk_part_ids.contains(&part_id)
2850}
2851
2852#[cfg(feature = "flat-opc")]
2853fn qname_matches(qname: &[u8], local_name: &[u8]) -> bool {
2854 qname == local_name || qname.rsplit(|byte| *byte == b':').next() == Some(local_name)
2855}
2856
2857#[cfg(feature = "flat-opc")]
2858fn normalize_flat_opc_part_name(name: &str) -> String {
2859 resolve_zip_file_path(name.trim_start_matches('/'))
2860}
2861
2862fn child_part_directory_path(source_part_path: &str, path_prefix: &str) -> String {
2863 let source_parent_path = super::parent_zip_path(source_part_path);
2864 let mut path = if path_prefix.is_empty() || path_prefix == "." {
2865 source_parent_path
2866 } else if path_prefix == "../media"
2867 && matches!(source_parent_path.as_str(), "word/" | "ppt/" | "xl/")
2868 {
2869 format!("{source_parent_path}media")
2870 } else if path_prefix.starts_with('/') {
2871 path_prefix.to_string()
2872 } else {
2873 let mut path = String::with_capacity(source_parent_path.len() + path_prefix.len() + 1);
2874 path.push_str(&source_parent_path);
2875 path.push_str(path_prefix);
2876 path
2877 };
2878
2879 path = resolve_zip_file_path(&path);
2880 if !path.is_empty() && !path.ends_with('/') {
2881 path.push('/');
2882 }
2883 path
2884}
2885
2886fn package_part_directory_path(path_prefix: &str) -> String {
2887 if path_prefix.is_empty() || path_prefix == "." {
2888 return String::new();
2889 }
2890
2891 let mut path = resolve_zip_file_path(path_prefix);
2892 if !path.is_empty() && !path.ends_with('/') {
2893 path.push('/');
2894 }
2895 path
2896}
2897
2898fn normalized_part_extension(extension: &str) -> String {
2899 let extension = if extension.is_empty() {
2900 ".xml"
2901 } else {
2902 extension
2903 };
2904
2905 if extension.starts_with('.') {
2906 extension.to_string()
2907 } else {
2908 format!(".{extension}")
2909 }
2910}
2911
2912fn normalized_new_part_path(path: &str) -> Result<String, SdkError> {
2913 let path = path.trim_start_matches('/');
2914 if path.is_empty() {
2915 return Err(SdkError::CommonError("part path is empty".to_string()));
2916 }
2917 if path.split('/').any(|component| component == "..") {
2918 return Err(SdkError::CommonError(format!(
2919 "part path {path} is not allowed"
2920 )));
2921 }
2922
2923 let normalized = resolve_zip_file_path(path);
2924 if normalized.is_empty()
2925 || normalized.ends_with('/')
2926 || normalized == "[Content_Types].xml"
2927 || is_relationships_part_path(&normalized)
2928 {
2929 return Err(SdkError::CommonError(format!(
2930 "part path {path} is not allowed"
2931 )));
2932 }
2933
2934 Ok(normalized)
2935}
2936
2937fn part_path_stem_and_extension(path: &str) -> (&str, &str) {
2938 let file_name_start = path.rfind('/').map(|index| index + 1).unwrap_or(0);
2939 let file_name = &path[file_name_start..];
2940 if let Some(extension_start) = file_name.rfind('.') {
2941 let extension_start = file_name_start + extension_start;
2942 (&path[..extension_start], &path[extension_start..])
2943 } else {
2944 (path, "")
2945 }
2946}
2947
2948fn is_numbered_part_content_type(content_type: &str) -> bool {
2949 matches!(
2950 content_type,
2951 "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml"
2952 | "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml"
2953 | "application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml"
2954 | "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml"
2955 | "application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml"
2956 | "application/vnd.openxmlformats-officedocument.drawing+xml"
2957 | "application/vnd.openxmlformats-officedocument.spreadsheetml.externalLink+xml"
2958 | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheetMetadata+xml"
2959 | "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml"
2960 | "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml"
2961 | "application/vnd.openxmlformats-officedocument.spreadsheetml.queryTable+xml"
2962 | "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionLog+xml"
2963 | "application/vnd.openxmlformats-officedocument.spreadsheetml.tableSingleCells+xml"
2964 | "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml"
2965 | "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"
2966 | "application/vnd.openxmlformats-officedocument.presentationml.comments+xml"
2967 | "application/vnd.openxmlformats-officedocument.presentationml.handoutMaster+xml"
2968 | "application/vnd.openxmlformats-officedocument.presentationml.notesMaster+xml"
2969 | "application/vnd.openxmlformats-officedocument.presentationml.notesSlide+xml"
2970 | "application/vnd.openxmlformats-officedocument.presentationml.slide+xml"
2971 | "application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml"
2972 | "application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml"
2973 | "application/vnd.openxmlformats-officedocument.presentationml.slideUpdateInfo+xml"
2974 | "application/vnd.openxmlformats-officedocument.presentationml.tags+xml"
2975 | "application/vnd.openxmlformats-officedocument.drawingml.chart+xml"
2976 | "application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml"
2977 | "application/vnd.openxmlformats-officedocument.drawingml.diagramColors+xml"
2978 | "application/vnd.openxmlformats-officedocument.drawingml.diagramData+xml"
2979 | "application/vnd.openxmlformats-officedocument.drawingml.diagramLayout+xml"
2980 | "application/vnd.openxmlformats-officedocument.drawingml.diagramStyle+xml"
2981 | "application/vnd.openxmlformats-officedocument.theme+xml"
2982 | "application/vnd.openxmlformats-officedocument.themeOverride+xml"
2983 | "application/vnd.openxmlformats-officedocument.customXmlProperties+xml"
2984 | "application/vnd.openxmlformats-officedocument.spreadsheetml.printerSettings"
2985 | "application/vnd.openxmlformats-officedocument.wordprocessingml.printerSettings"
2986 | "application/vnd.openxmlformats-officedocument.presentationml.printerSettings"
2987 )
2988}
2989
2990fn first_part_path_sequence(content_type: &str) -> usize {
2991 usize::from(is_numbered_part_content_type(content_type))
2992}
2993
2994fn next_part_path_sequence(sequence: usize) -> usize {
2995 if sequence == 0 { 2 } else { sequence + 1 }
2996}
2997
2998fn part_path_with_sequence(
2999 directory_path: &str,
3000 target_name: &str,
3001 extension: &str,
3002 sequence: usize,
3003) -> String {
3004 match (directory_path.is_empty(), sequence) {
3005 (true, 0) => format!("{target_name}{extension}"),
3006 (true, sequence) => format!("{target_name}{sequence}{extension}"),
3007 (false, 0) => format!("{directory_path}{target_name}{extension}"),
3008 (false, sequence) => format!("{directory_path}{target_name}{sequence}{extension}"),
3009 }
3010}
3011
3012fn relationship_target_from_source(source_part_path: &str, child_part_path: &str) -> String {
3013 let source_parent_path = super::parent_zip_path(source_part_path);
3014 if let Some(relative) = child_part_path.strip_prefix(&source_parent_path) {
3015 relative.to_string()
3016 } else {
3017 format!("/{child_part_path}")
3018 }
3019}
3020
3021fn relationship_types_by_part(
3022 package_relationships: &RelationshipSet,
3023 part_relationships: &[RelationshipSet],
3024) -> Result<Vec<Option<super::XmlRelationshipNamespaceUri>>, SdkError> {
3025 let mut relationship_types: Vec<Option<super::XmlRelationshipNamespaceUri>> =
3026 vec![None; part_relationships.len()];
3027
3028 for relationship_set in std::iter::once(package_relationships).chain(part_relationships) {
3029 for relationship in relationship_set.iter() {
3030 let Some(part_id) = relationship.target_part_id() else {
3031 continue;
3032 };
3033 let slot = relationship_types.get_mut(part_id.index()).ok_or_else(|| {
3034 SdkError::CommonError(format!(
3035 "relationship target part id {part_id:?} is outside package storage"
3036 ))
3037 })?;
3038 if let Some(existing) = slot {
3039 if !relationship_types_are_compatible_bytes(
3040 existing.uri_bytes(),
3041 relationship.relationship_type_bytes(),
3042 ) {
3043 return Err(SdkError::CommonError(format!(
3044 "same part {:?} is referenced by different relationship types: {} and {}",
3045 part_id,
3046 existing.as_str(),
3047 relationship.relationship_type(),
3048 )));
3049 }
3050 } else {
3051 *slot = Some(super::XmlRelationshipNamespaceUri::from_uri(
3052 relationship.relationship_type(),
3053 ));
3054 }
3055 }
3056 }
3057
3058 Ok(relationship_types)
3059}
3060
3061fn relationship_types_are_compatible_bytes(left: &[u8], right: &[u8]) -> bool {
3062 left == right || data_part_reference_relationship_types_are_compatible_bytes(left, right)
3063}
3064
3065fn data_part_reference_relationship_types_are_compatible_bytes(left: &[u8], right: &[u8]) -> bool {
3066 super::is_data_part_reference_relationship_type_bytes(left)
3067 && super::is_data_part_reference_relationship_type_bytes(right)
3068}
3069
3070fn is_media_data_part(part: &StoredPart) -> bool {
3071 part
3072 .relationship_type_bytes()
3073 .is_some_and(super::is_data_part_reference_relationship_type_bytes)
3074 || media_data_part_content_type(part.content_type())
3075 || part
3076 .path()
3077 .rsplit('/')
3078 .next()
3079 .is_some_and(|file_name| file_name.starts_with("media"))
3080}
3081
3082fn media_data_part_content_type(content_type: &str) -> bool {
3083 content_type.starts_with("audio/") || content_type.starts_with("video/")
3084}
3085
3086fn read_archive_model<R: Read + Seek>(
3087 archive: &mut zip::ZipArchive<R>,
3088) -> Result<OpenedArchiveModel, SdkError> {
3089 let (content_types, content_types_archive_entry_index) = read_content_types(archive)?;
3090 let raw_parts = read_raw_parts(archive, &content_types)?;
3091 let (package_relationships, part_relationships) =
3092 read_required_relationship_entries(archive, &raw_parts)?;
3093
3094 Ok(OpenedArchiveModel {
3095 content_types,
3096 content_types_archive_entry_index,
3097 raw_parts,
3098 package_relationships,
3099 part_relationships,
3100 })
3101}
3102
3103#[derive(Clone, Copy)]
3104enum RelationshipEntryTarget {
3105 Package,
3106 Part(usize),
3107}
3108
3109fn read_required_relationship_entries<R: Read + Seek>(
3110 archive: &mut zip::ZipArchive<R>,
3111 raw_parts: &[RawPart],
3112) -> Result<
3113 (
3114 Option<LoadedRelationshipEntry>,
3115 Vec<Option<LoadedRelationshipEntry>>,
3116 ),
3117 SdkError,
3118> {
3119 let mut required = Vec::with_capacity(raw_parts.len() + 1);
3120 if let Some(entry_index) = archive.index_for_name("_rels/.rels") {
3121 required.push((entry_index, RelationshipEntryTarget::Package));
3122 }
3123 for (part_index, raw_part) in raw_parts.iter().enumerate() {
3124 let path = part_relationships_path(&raw_part.path);
3125 let entry_index = archive.index_for_name(&path).or_else(|| {
3126 lowercase_zip_filename(&path).and_then(|fallback| archive.index_for_name(&fallback))
3127 });
3128 if let Some(entry_index) = entry_index {
3129 required.push((entry_index, RelationshipEntryTarget::Part(part_index)));
3130 }
3131 }
3132 required.sort_unstable_by_key(|(entry_index, _)| *entry_index);
3133
3134 let mut package_relationships = None;
3135 let mut part_relationships = std::iter::repeat_with(|| None)
3136 .take(raw_parts.len())
3137 .collect::<Vec<_>>();
3138 for (entry_index, target) in required {
3139 let bytes = read_archive_entry_by_index(archive, entry_index)?;
3140 let relationships = <Relationships as crate::sdk::SdkType>::from_bytes(&bytes);
3141 let loaded = LoadedRelationshipEntry {
3142 entry_index,
3143 bytes,
3144 relationships: match target {
3145 RelationshipEntryTarget::Package => Some(relationships?),
3146 RelationshipEntryTarget::Part(_) => relationships.ok(),
3147 },
3148 };
3149 match target {
3150 RelationshipEntryTarget::Package => package_relationships = Some(loaded),
3151 RelationshipEntryTarget::Part(part_index) => {
3152 part_relationships[part_index] = Some(loaded);
3153 }
3154 }
3155 }
3156
3157 Ok((package_relationships, part_relationships))
3158}
3159
3160fn read_content_types<R: Read + Seek>(
3161 archive: &mut zip::ZipArchive<R>,
3162) -> Result<(Types, usize), SdkError> {
3163 let entry_index = archive
3164 .index_for_name("[Content_Types].xml")
3165 .ok_or(zip::result::ZipError::FileNotFound)?;
3166 let bytes = read_archive_entry_by_index(archive, entry_index)?;
3167 Ok((
3168 <Types as crate::sdk::SdkType>::from_bytes(&bytes)?,
3169 entry_index,
3170 ))
3171}
3172
3173fn read_raw_parts<R: Read + Seek>(
3174 archive: &mut zip::ZipArchive<R>,
3175 content_types: &Types,
3176) -> Result<Vec<RawPart>, SdkError> {
3177 let mut parts = Vec::new();
3178 let content_type_resolver = ContentTypeResolver::new(content_types);
3179
3180 for (index, entry_name) in archive.file_names().enumerate() {
3181 if entry_name.ends_with('/') {
3182 continue;
3183 }
3184
3185 let path = resolve_zip_file_path(entry_name);
3186 if path == "[Content_Types].xml" || is_relationships_part_path(&path) {
3187 continue;
3188 }
3189
3190 let content_type = content_type_resolver
3191 .content_type_for_part(&path)
3192 .unwrap_or_default();
3193 parts.push(RawPart {
3194 path: path.into_boxed_str(),
3195 content_type: content_type.into(),
3196 entry_index: index,
3197 #[cfg(feature = "flat-opc")]
3198 bytes: Vec::new(),
3199 });
3200 }
3201
3202 parts.sort_by(|left, right| left.path.cmp(&right.path));
3203 Ok(parts)
3204}
3205
3206fn read_archive_entry_by_index<R: Read + Seek>(
3207 archive: &mut zip::ZipArchive<R>,
3208 entry_index: usize,
3209) -> Result<Vec<u8>, SdkError> {
3210 let mut entry = archive.by_index(entry_index)?;
3211 let capacity = usize::try_from(entry.size()).map_err(|_| {
3212 SdkError::CommonError(format!(
3213 "ZIP entry {} is too large for this platform",
3214 entry.name()
3215 ))
3216 })?;
3217 let mut bytes = Vec::new();
3218 bytes.try_reserve_exact(capacity).map_err(|error| {
3219 SdkError::CommonError(format!(
3220 "cannot allocate {capacity} bytes for ZIP entry {}: {error}",
3221 entry.name()
3222 ))
3223 })?;
3224 entry.read_to_end(&mut bytes)?;
3225 Ok(bytes)
3226}
3227
3228fn lowercase_zip_filename(path: &str) -> Option<String> {
3229 let (parent_path, file_name) = path.rsplit_once('/')?;
3230 let lower_file_name = file_name.to_ascii_lowercase();
3231 if file_name == lower_file_name {
3232 return None;
3233 }
3234
3235 let mut fallback = String::with_capacity(path.len());
3236 fallback.push_str(parent_path);
3237 fallback.push('/');
3238 fallback.push_str(&lower_file_name);
3239 Some(fallback)
3240}
3241
3242fn relationship_info(
3243 relationship: OpcRelationship,
3244 source_parent_path: &str,
3245 by_path: &HashMap<Box<str>, PartId>,
3246) -> RelationshipInfo {
3247 let target_mode = relationship.target_mode;
3248 let effective_target_mode = target_mode.unwrap_or(TargetMode::Internal);
3249 let (target_kind, target_part_id) = if matches!(effective_target_mode, TargetMode::Internal) {
3250 if relationship.target.eq_ignore_ascii_case("NULL") {
3251 (RelationshipTargetKind::Null, None)
3252 } else {
3253 let target_path = resolve_zip_file_path(&resolve_relationship_target_path(
3254 source_parent_path,
3255 &relationship.target,
3256 ));
3257 match by_path.get(target_path.as_str()).copied() {
3258 Some(part_id) => (RelationshipTargetKind::InternalPart, Some(part_id)),
3259 None => (RelationshipTargetKind::Missing, None),
3260 }
3261 }
3262 } else {
3263 (RelationshipTargetKind::External, None)
3264 };
3265
3266 RelationshipInfo {
3267 id: relationship.id.into_boxed_str(),
3268 relationship_type: super::XmlRelationshipNamespaceUri::from_uri(&relationship.r#type),
3269 target: relationship.target.into_boxed_str(),
3270 target_mode,
3271 target_kind,
3272 target_part_id,
3273 }
3274}
3275
3276fn is_relationships_part_path(path: &str) -> bool {
3277 path == "_rels/.rels" || path.contains("/_rels/") && path.ends_with(".rels")
3278}
3279
3280#[cfg(test)]
3281mod tests {
3282 use super::*;
3283 use std::io::{Cursor, Write};
3284
3285 #[test]
3286 fn storage_resolves_package_relationship_target_part_id() {
3287 let mut buffer = Cursor::new(Vec::new());
3288 {
3289 let mut zip = zip::ZipWriter::new(&mut buffer);
3290 let options = zip::write::SimpleFileOptions::default();
3291
3292 zip.start_file("[Content_Types].xml", options).unwrap();
3293 zip.write_all(
3294 br#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
3295<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
3296 <Default Extension="xml" ContentType="application/xml"/>
3297 <Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
3298</Types>"#,
3299 )
3300 .unwrap();
3301
3302 zip.add_directory("_rels", options).unwrap();
3303 zip.start_file("_rels/.rels", options).unwrap();
3304 zip.write_all(
3305 br#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
3306<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
3307 <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
3308</Relationships>"#,
3309 )
3310 .unwrap();
3311
3312 zip.add_directory("word", options).unwrap();
3313 zip.start_file("word/document.xml", options).unwrap();
3314 zip.write_all(br#"<w:document xmlns:w="w"/>"#).unwrap();
3315 zip.finish().unwrap();
3316 }
3317
3318 buffer.set_position(0);
3319 let storage = SdkPackageStorage::open(buffer).unwrap();
3320 let relationship = storage.package_relationships().get("rId1").unwrap();
3321 let part_id = relationship.target_part_id().unwrap();
3322 let part = storage.part(part_id).unwrap();
3323
3324 assert_eq!(part.path(), "word/document.xml");
3325 assert_eq!(
3326 part.relationship_type(),
3327 Some("http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument")
3328 );
3329 assert_eq!(
3330 relationship.target_kind(),
3331 RelationshipTargetKind::InternalPart
3332 );
3333 assert!(matches!(
3334 &storage.parts[part_id.index()].data,
3335 StoredPartData::Archived { bytes, .. } if bytes.get().is_none()
3336 ));
3337 assert_eq!(
3338 storage.package_save_entries(),
3339 vec![
3340 PackageSaveEntry::ContentTypes,
3341 PackageSaveEntry::ArchivedExtra(1),
3342 PackageSaveEntry::PackageRelationships,
3343 PackageSaveEntry::ArchivedExtra(3),
3344 PackageSaveEntry::Part(part_id),
3345 ]
3346 );
3347 assert_eq!(
3348 storage.part_bytes(part_id).unwrap(),
3349 br#"<w:document xmlns:w="w"/>"#
3350 );
3351 assert!(matches!(
3352 &storage.parts[part_id.index()].data,
3353 StoredPartData::Archived { bytes, .. } if bytes.get().is_some()
3354 ));
3355 }
3356
3357 #[test]
3358 fn storage_reads_part_relationships_with_lowercase_filename_fallback() {
3359 let mut buffer = Cursor::new(Vec::new());
3360 {
3361 let mut zip = zip::ZipWriter::new(&mut buffer);
3362 let options = zip::write::SimpleFileOptions::default();
3363
3364 zip.start_file("[Content_Types].xml", options).unwrap();
3365 zip.write_all(
3366 br#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
3367<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
3368 <Default Extension="xml" ContentType="application/xml"/>
3369 <Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>
3370 <Override PartName="/xl/worksheets/Sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>
3371 <Override PartName="/xl/drawings/drawing1.xml" ContentType="application/vnd.openxmlformats-officedocument.drawing+xml"/>
3372</Types>"#,
3373 )
3374 .unwrap();
3375
3376 zip.add_directory("_rels", options).unwrap();
3377 zip.start_file("_rels/.rels", options).unwrap();
3378 zip.write_all(
3379 br#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
3380<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
3381 <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
3382</Relationships>"#,
3383 )
3384 .unwrap();
3385
3386 zip.add_directory("xl/_rels", options).unwrap();
3387 zip
3388 .start_file("xl/_rels/workbook.xml.rels", options)
3389 .unwrap();
3390 zip.write_all(
3391 br#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
3392<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
3393 <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/Sheet1.xml"/>
3394</Relationships>"#,
3395 )
3396 .unwrap();
3397
3398 zip.add_directory("xl/worksheets/_rels", options).unwrap();
3399 zip
3400 .start_file("xl/worksheets/_rels/sheet1.xml.rels", options)
3401 .unwrap();
3402 zip.write_all(
3403 br#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
3404<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
3405 <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing" Target="../drawings/drawing1.xml"/>
3406</Relationships>"#,
3407 )
3408 .unwrap();
3409
3410 zip.add_directory("xl", options).unwrap();
3411 zip.start_file("xl/workbook.xml", options).unwrap();
3412 zip.write_all(br#"<workbook/>"#).unwrap();
3413 zip.add_directory("xl/worksheets", options).unwrap();
3414 zip.start_file("xl/worksheets/Sheet1.xml", options).unwrap();
3415 zip.write_all(br#"<worksheet/>"#).unwrap();
3416 zip.add_directory("xl/drawings", options).unwrap();
3417 zip.start_file("xl/drawings/drawing1.xml", options).unwrap();
3418 zip.write_all(br#"<xdr:wsDr xmlns:xdr="xdr"/>"#).unwrap();
3419 zip.finish().unwrap();
3420 }
3421
3422 buffer.set_position(0);
3423 let storage = SdkPackageStorage::open(buffer).unwrap();
3424 let sheet_part_id = storage
3425 .package_relationships()
3426 .get("rId1")
3427 .and_then(RelationshipInfo::target_part_id)
3428 .and_then(|workbook_part_id| storage.relationships(workbook_part_id))
3429 .and_then(|relationships| relationships.get("rId1"))
3430 .and_then(RelationshipInfo::target_part_id)
3431 .unwrap();
3432 let drawing_relationship = storage
3433 .relationships(sheet_part_id)
3434 .unwrap()
3435 .get("rId1")
3436 .unwrap();
3437
3438 assert_eq!(
3439 drawing_relationship.target_kind(),
3440 RelationshipTargetKind::InternalPart
3441 );
3442 let drawing_part = storage
3443 .part(drawing_relationship.target_part_id().unwrap())
3444 .unwrap();
3445 assert_eq!(drawing_part.path(), "xl/drawings/drawing1.xml");
3446 }
3447}