use crate::ooxml::opc::constants::target_mode;
use crate::ooxml::opc::error::{OpcError, Result};
use crate::ooxml::opc::packuri::{PackURI, PACKAGE_URI};
use crate::ooxml::opc::phys_pkg::PhysPkgReader;
use quick_xml::events::Event;
use quick_xml::Reader;
use smallvec::SmallVec;
use std::collections::HashMap;
use std::io::{Read, Seek};
#[derive(Debug)]
pub struct SerializedPart {
pub partname: PackURI,
pub content_type: String,
pub reltype: String,
pub blob: Vec<u8>,
pub srels: SmallVec<[SerializedRelationship; 8]>,
}
#[derive(Debug, Clone)]
pub struct SerializedRelationship {
pub base_uri: String,
pub r_id: String,
pub reltype: String,
pub target_ref: String,
pub target_mode: String,
}
impl SerializedRelationship {
#[inline]
pub fn is_external(&self) -> bool {
self.target_mode == target_mode::EXTERNAL
}
pub fn target_partname(&self) -> Result<PackURI> {
if self.is_external() {
return Err(OpcError::InvalidRelationship(
"Cannot get target_partname for external relationship".to_string(),
));
}
PackURI::from_rel_ref(&self.base_uri, &self.target_ref).map_err(OpcError::InvalidPackUri)
}
}
struct ContentTypeMap {
defaults: HashMap<String, String>,
overrides: HashMap<String, String>,
}
impl ContentTypeMap {
fn new() -> Self {
Self {
defaults: HashMap::new(),
overrides: HashMap::new(),
}
}
fn from_xml(xml: &[u8]) -> Result<Self> {
let mut map = Self::new();
let mut reader = Reader::from_reader(xml);
reader.config_mut().trim_text(true);
let mut buf = Vec::new();
loop {
match reader.read_event_into(&mut buf) {
Ok(Event::Empty(ref e)) => {
match e.local_name().as_ref() {
b"Default" => {
let mut extension = None;
let mut content_type = None;
for attr in e.attributes() {
let attr = attr?;
match attr.key.as_ref() {
b"Extension" => {
extension = Some(attr.unescape_value()?.to_string());
}
b"ContentType" => {
content_type = Some(attr.unescape_value()?.to_string());
}
_ => {}
}
}
if let (Some(ext), Some(ct)) = (extension, content_type) {
map.add_default(ext, ct);
}
}
b"Override" => {
let mut partname = None;
let mut content_type = None;
for attr in e.attributes() {
let attr = attr?;
match attr.key.as_ref() {
b"PartName" => {
partname = Some(attr.unescape_value()?.to_string());
}
b"ContentType" => {
content_type = Some(attr.unescape_value()?.to_string());
}
_ => {}
}
}
if let (Some(pn), Some(ct)) = (partname, content_type) {
map.add_override(pn, ct);
}
}
_ => {}
}
}
Ok(Event::Eof) => break,
Err(e) => {
return Err(OpcError::XmlError(format!(
"Content types parse error: {}",
e
)))
}
_ => {}
}
buf.clear();
}
Ok(map)
}
fn add_default(&mut self, extension: String, content_type: String) {
self.defaults.insert(extension.to_lowercase(), content_type);
}
fn add_override(&mut self, partname: String, content_type: String) {
self.overrides.insert(partname, content_type);
}
fn get(&self, pack_uri: &PackURI) -> Result<String> {
if let Some(ct) = self.overrides.get(pack_uri.as_str()) {
return Ok(ct.clone());
}
let ext = pack_uri.ext();
if let Some(ct) = self.defaults.get(ext) {
return Ok(ct.clone());
}
Err(OpcError::ContentTypeNotFound(pack_uri.to_string()))
}
}
pub struct PackageReader {
pkg_srels: SmallVec<[SerializedRelationship; 8]>,
sparts: Vec<SerializedPart>,
}
impl PackageReader {
pub fn from_phys_reader<R: Read + Seek>(mut phys_reader: PhysPkgReader<R>) -> Result<Self> {
let content_types_xml = phys_reader.content_types_xml()?;
let content_types = ContentTypeMap::from_xml(&content_types_xml)?;
let package_uri = PackURI::new(PACKAGE_URI).map_err(OpcError::InvalidPackUri)?;
let pkg_srels = Self::load_rels(&mut phys_reader, &package_uri)?;
let sparts = Self::load_parts(&mut phys_reader, &pkg_srels, &content_types)?;
Ok(Self { pkg_srels, sparts })
}
fn load_rels<R: Read + Seek>(
phys_reader: &mut PhysPkgReader<R>,
source_uri: &PackURI,
) -> Result<SmallVec<[SerializedRelationship; 8]>> {
let rels_xml = match phys_reader.rels_xml_for(source_uri)? {
Some(xml) => xml,
None => return Ok(SmallVec::new()), };
let base_uri = source_uri.base_uri().to_string();
let mut srels = SmallVec::new();
let mut reader = Reader::from_reader(&rels_xml[..]);
reader.config_mut().trim_text(true);
let mut buf = Vec::new();
loop {
match reader.read_event_into(&mut buf) {
Ok(Event::Empty(ref e)) | Ok(Event::Start(ref e)) => {
if e.local_name().as_ref() == b"Relationship" {
let mut r_id = None;
let mut reltype = None;
let mut target_ref = None;
let mut target_mode = target_mode::INTERNAL.to_string();
for attr in e.attributes() {
let attr = attr?;
match attr.key.as_ref() {
b"Id" => r_id = Some(attr.unescape_value()?.to_string()),
b"Type" => reltype = Some(attr.unescape_value()?.to_string()),
b"Target" => target_ref = Some(attr.unescape_value()?.to_string()),
b"TargetMode" => target_mode = attr.unescape_value()?.to_string(),
_ => {}
}
}
if let (Some(id), Some(rt), Some(tr)) = (r_id, reltype, target_ref) {
srels.push(SerializedRelationship {
base_uri: base_uri.clone(),
r_id: id,
reltype: rt,
target_ref: tr,
target_mode,
});
}
}
}
Ok(Event::Eof) => break,
Err(e) => return Err(OpcError::XmlError(format!("Rels parse error: {}", e))),
_ => {}
}
buf.clear();
}
Ok(srels)
}
fn load_parts<R: Read + Seek>(
phys_reader: &mut PhysPkgReader<R>,
pkg_srels: &[SerializedRelationship],
content_types: &ContentTypeMap,
) -> Result<Vec<SerializedPart>> {
let mut sparts = Vec::new();
let mut visited = HashMap::new();
Self::walk_parts(
phys_reader,
pkg_srels,
content_types,
&mut visited,
&mut sparts,
)?;
Ok(sparts)
}
fn walk_parts<R: Read + Seek>(
phys_reader: &mut PhysPkgReader<R>,
srels: &[SerializedRelationship],
content_types: &ContentTypeMap,
visited: &mut HashMap<String, ()>,
sparts: &mut Vec<SerializedPart>,
) -> Result<()> {
for srel in srels {
if srel.is_external() {
continue;
}
let partname = srel.target_partname()?;
let partname_str = partname.to_string();
if visited.contains_key(&partname_str) {
continue;
}
visited.insert(partname_str, ());
let blob = phys_reader.blob_for(&partname)?;
let content_type = content_types.get(&partname)?;
let part_srels = Self::load_rels(phys_reader, &partname)?;
let spart = SerializedPart {
partname: partname.clone(),
content_type,
reltype: srel.reltype.clone(),
blob,
srels: part_srels.clone(),
};
sparts.push(spart);
Self::walk_parts(phys_reader, &part_srels, content_types, visited, sparts)?;
}
Ok(())
}
pub fn iter_sparts(&self) -> impl Iterator<Item = &SerializedPart> {
self.sparts.iter()
}
pub fn pkg_srels(&self) -> &[SerializedRelationship] {
&self.pkg_srels
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_content_type_map() {
let xml = br#"<?xml version="1.0"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension="xml" ContentType="application/xml"/>
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
</Types>"#;
let ct_map = ContentTypeMap::from_xml(xml).unwrap();
let uri = PackURI::new("/test.xml").unwrap();
assert_eq!(ct_map.get(&uri).unwrap(), "application/xml");
let uri = PackURI::new("/word/document.xml").unwrap();
assert_eq!(
ct_map.get(&uri).unwrap(),
"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"
);
}
}