1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
use crate::{
assert_empty,
catalog::{assert_len, Collection},
error::{ParseError, PdfResult},
objects::{Dictionary, Object, ObjectType},
Resolve,
};
#[derive(Debug, Clone)]
pub enum FileSpecification {
Simple(FileSpecificationString),
Full(FullFileSpecification),
}
impl FileSpecification {
pub fn from_obj(obj: Object, resolver: &mut dyn Resolve) -> PdfResult<Self> {
match resolver.resolve(obj)? {
Object::String(s) => Ok(FileSpecification::Simple(FileSpecificationString::new(s))),
Object::Dictionary(dict) => Ok(FileSpecification::Full(
FullFileSpecification::from_dict(dict, resolver)?,
)),
obj => Err(ParseError::MismatchedObjectType {
found: obj,
expected: ObjectType::Dictionary,
}),
}
}
}
#[derive(Debug, Clone)]
pub struct FullFileSpecification {
/// The name of the file system that shall be used to interpret this file
/// specification.
///
/// If this entry is present, all other entries in the dictionary shall be
/// interpreted by the designated file system. PDF shall define only one
/// standard file system name, URL; an application can register other names.
///
/// This entry shall be independent of the F and UF entries.
file_system: Option<String>,
/// A file specification string as descibed by the `FileSpecificationString` docs
/// or (if the file system is URL) a uniform resource locator.
///
/// The UF entry should be used in addition to the F entry. The UF entry provides
/// cross-platform and cross-language compatibility and the F entry provides backwards
/// compatibility.
file_specification_string: Option<FileSpecificationString>,
/// A Unicode text string that provides file specification. This is a text string
/// encoded using PDFDocEncoding or UTF-16BE with a leading byte-order marker.
///
/// The F entry should be included along with this entry for backwards compatibility
/// reasons.
unicode_file_specification_string: Option<FileSpecificationString>,
/// An array of two byte strings constituting a file identifier that should be included
/// in the referenced file.
///
/// NOTE: The use of this entry improves an application's chances of finding the
/// intended file and allows it to warn the user if the file has changed since the link
/// was made.
id: Option<FileIdentifier>,
/// A flag indicating whether the file referenced by the file specification is volatile
/// (changes frequently with time).
///
/// If the value is true, applications shall not cache a copy of the file. For example,
/// a movie annotation referencing a URL to a live video camera could set this flag to
/// true to notify the conforming reader that it should re-acquire the movie each time
/// it is played.
///
/// Default value: false.
is_volatile: bool,
/// A dictionary containing a subset of the keys F, and UF, corresponding to the entries
/// by those names in the file specification dictionary.
///
/// The value of each such key shall be an embedded file stream containing the corresponding
/// file. If this entry is present, the Type entry is required and the file specification
/// dictionary shall be indirectly referenced.
ef: Option<Dictionary>,
/// A dictionary with the same structure as the EF dictionary, which shall be present.
///
/// Each key in the RF dictionary shall also be present in the EF dictionary. Each value
/// shall be a related files arra identifying files that are related to the corresponding
/// file in the EF dictionary.
///
/// If this entry is present, the Type entry is required and the file specification dictionary
/// shall be indirectly referenced.
rf: Option<Dictionary>,
/// Descriptive text associated with the file specification.
///
/// It shall be used for files in the EmbeddedFiles name tree
description: Option<String>,
/// A collection item dictionary, which shall be used to create the user interface for
/// portable collections
collection_item_dict: Option<Collection>,
}
impl FullFileSpecification {
const TYPE: &'static str = "Typespec";
pub(crate) fn from_dict(mut dict: Dictionary, resolver: &mut dyn Resolve) -> PdfResult<Self> {
dict.expect_type(Self::TYPE, resolver, false)?;
let file_system = dict.get_name("Fs", resolver)?;
let file_specification_string = dict
.get_string("F", resolver)?
.map(FileSpecificationString::new);
let unicode_file_specification_string = dict
.get_string("UF", resolver)?
.map(FileSpecificationString::new);
dict.get_string("DOS", resolver)?;
dict.get_string("Mac", resolver)?;
dict.get_string("Unix", resolver)?;
let id = dict
.get_arr("UF", resolver)?
.map(|objs| FileIdentifier::from_arr(objs, resolver))
.transpose()?;
let is_volatile = dict.get_bool("V", resolver)?.unwrap_or(false);
let ef = dict.get_dict("EF", resolver)?;
let rf = dict.get_dict("RF", resolver)?;
let description = dict.get_string("Desc", resolver)?;
let collection_item_dict = dict.get_dict("CI", resolver)?.map(|_| todo!());
assert_empty(dict);
Ok(FullFileSpecification {
file_system,
file_specification_string,
unicode_file_specification_string,
id,
is_volatile,
ef,
rf,
description,
collection_item_dict,
})
}
}
/// The standard format for representing a simple file specification in string form divides
/// the string into component substrings separated by the SOLIDUS character (2Fh) (/). The
/// SOLIDUS is a generic component separator that shall be mapped to the appropriate
/// platform-specific separator when generating a platform-dependent file name. Any of the
/// components may be empty. If a component contains one or more literal SOLIDI, each shall
/// be preceded by a REVERSE SOLIDUS (5Ch) (\), which in turn shall be preceded by another
/// REVERSE SOLIDUS to indicate that it is part of the string and not an escape character.
#[derive(Debug, Clone)]
pub struct FileSpecificationString(String);
impl FileSpecificationString {
pub fn new(s: String) -> Self {
Self(s)
}
}
#[derive(Debug, Clone)]
struct EmbeddedFileStream;
#[derive(Debug, Clone)]
struct RelatedFilesArray;
#[derive(Debug, Clone)]
pub struct FileIdentifier(String, String);
impl FileIdentifier {
pub fn from_arr(arr: Vec<Object>, resolver: &mut dyn Resolve) -> PdfResult<Self> {
assert_len(&arr, 2)?;
let mut iter = arr.into_iter().map(|obj| resolver.assert_string(obj));
Ok(FileIdentifier(iter.next().unwrap()?, iter.next().unwrap()?))
}
}