use crate::error::{Error, Result};
use crate::lexer::{token, Token};
use crate::object::{Object, ObjectRef};
use nom::IResult;
use std::collections::HashMap;
pub fn decode_literal_string_escapes(raw: &[u8]) -> Vec<u8> {
let mut result = Vec::with_capacity(raw.len());
let mut i = 0;
while i < raw.len() {
if raw[i] == b'\\' && i + 1 < raw.len() {
match raw[i + 1] {
b'n' => {
result.push(b'\n');
i += 2;
},
b'r' => {
result.push(b'\r');
i += 2;
},
b't' => {
result.push(b'\t');
i += 2;
},
b'b' => {
result.push(8); i += 2;
},
b'f' => {
result.push(12); i += 2;
},
b'(' => {
result.push(b'(');
i += 2;
},
b')' => {
result.push(b')');
i += 2;
},
b'\\' => {
result.push(b'\\');
i += 2;
},
b'\n' => {
i += 2; },
b'\r' => {
i += 2;
if i < raw.len() && raw[i] == b'\n' {
i += 1;
}
},
c if c.is_ascii_digit() && c < b'8' => {
let start = i + 1;
let mut octal_value = 0u32;
let mut octal_len = 0;
for j in 0..3 {
if start + j < raw.len() {
let digit = raw[start + j];
if (b'0'..b'8').contains(&digit) {
octal_value = octal_value * 8 + (digit - b'0') as u32;
octal_len += 1;
} else {
break;
}
} else {
break;
}
}
if octal_len > 0 {
result.push((octal_value & 0xFF) as u8);
i += 1 + octal_len; } else {
result.push(b'\\');
i += 1;
}
},
_ => {
result.push(b'\\');
i += 1;
},
}
} else {
result.push(raw[i]);
i += 1;
}
}
result
}
pub fn parse_object(input: &[u8]) -> IResult<&[u8], Object> {
let (input, tok) = token(input)?;
match tok {
Token::Null => Ok((input, Object::Null)),
Token::True => Ok((input, Object::Boolean(true))),
Token::False => Ok((input, Object::Boolean(false))),
Token::Integer(i) => {
if let Ok((input2, Token::Integer(gen))) = token(input) {
if let Ok((input3, Token::R)) = token(input2) {
return Ok((input3, Object::Reference(ObjectRef::new(i as u32, gen as u16))));
}
}
Ok((input, Object::Integer(i)))
},
Token::Real(r) => Ok((input, Object::Real(r))),
Token::LiteralString(bytes) => {
let decoded = decode_literal_string_escapes(bytes);
Ok((input, Object::String(decoded)))
},
Token::HexString(hex_bytes) => {
match decode_hex(hex_bytes) {
Ok(decoded) => Ok((input, Object::String(decoded))),
Err(_) => Err(nom::Err::Failure(nom::error::Error::new(
input,
nom::error::ErrorKind::Fail,
))),
}
},
Token::Name(name) => Ok((input, Object::Name(name))),
Token::ArrayStart => parse_array(input),
Token::DictStart => {
let (remaining, dict_obj) = parse_dictionary(input)?;
if let Ok((stream_input, Token::StreamStart)) = token(remaining) {
let dict = match dict_obj {
Object::Dictionary(d) => d,
_ => {
return Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::Tag,
)));
},
};
let (final_input, stream_data) = parse_stream_data(stream_input, &dict)?;
return Ok((
final_input,
Object::Stream {
dict,
data: bytes::Bytes::from(stream_data),
},
));
}
Ok((remaining, dict_obj))
},
_ => {
Err(nom::Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::Tag)))
},
}
}
fn parse_stream_data<'a>(
input: &'a [u8],
dict: &HashMap<String, Object>,
) -> IResult<&'a [u8], Vec<u8>> {
let input = if input.starts_with(b"\r\n") {
&input[2..]
} else if input.starts_with(b"\n") {
&input[1..]
} else if input.starts_with(b"\r") {
log::warn!(
"SPEC VIOLATION: Stream keyword followed by CR alone (should be CRLF or LF). \
Accepting in lenient mode for compatibility. \
PDF Spec: ISO 32000-1:2008, Section 7.3.8.1"
);
&input[1..]
} else {
log::warn!(
"SPEC VIOLATION: No newline after stream keyword (should be CRLF or LF). \
PDF Spec: ISO 32000-1:2008, Section 7.3.8.1"
);
input
};
if let Some(length_obj) = dict.get("Length") {
if let Some(length) = length_obj.as_integer() {
let length = length as usize;
if length <= input.len() {
let stream_data = input[..length].to_vec();
let remaining = &input[length..];
let ws_result: nom::IResult<&[u8], &[u8]> =
nom::bytes::complete::take_while(|c: u8| c.is_ascii_whitespace())(remaining);
if let Ok((remaining, _)) = ws_result {
if let Ok((remaining, _)) = token(remaining) {
return Ok((remaining, stream_data));
}
}
}
log::warn!("Stream /Length {} is incorrect, falling back to endstream scan", length);
if let Some(pos) = find_endstream(input) {
let mut end = pos;
if end > 0 && input[end - 1] == b'\n' {
end -= 1;
}
if end > 0 && input[end - 1] == b'\r' {
end -= 1;
}
let stream_data = input[..end].to_vec();
let remaining = &input[pos..];
let (remaining, _) = token(remaining)?;
return Ok((remaining, stream_data));
}
}
}
if let Some(pos) = find_endstream(input) {
let mut end = pos;
if end > 0 && input[end - 1] == b'\n' {
end -= 1;
}
if end > 0 && input[end - 1] == b'\r' {
end -= 1;
}
let stream_data = input[..end].to_vec();
let remaining = &input[pos..];
let (remaining, _) = token(remaining)?;
return Ok((remaining, stream_data));
}
Err(nom::Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::Eof)))
}
fn find_endstream(input: &[u8]) -> Option<usize> {
let keyword = b"endstream";
input
.windows(keyword.len())
.position(|window| window == keyword)
}
fn parse_array(input: &[u8]) -> IResult<&[u8], Object> {
let mut objects = Vec::new();
let mut remaining = input;
loop {
let token_result = token(remaining);
match token_result {
Ok((inp, tok)) => {
if tok == Token::ArrayEnd {
return Ok((inp, Object::Array(objects)));
}
match parse_object(remaining) {
Ok((inp, obj)) => {
objects.push(obj);
remaining = inp;
},
Err(e) => {
if remaining.is_empty() {
return Ok((remaining, Object::Array(objects)));
}
return Err(e);
},
}
},
Err(nom::Err::Incomplete(_)) | Err(nom::Err::Error(_)) if remaining.is_empty() => {
return Ok((remaining, Object::Array(objects)));
},
Err(e) => return Err(e),
}
}
}
fn parse_dictionary(input: &[u8]) -> IResult<&[u8], Object> {
let mut dict = HashMap::new();
let mut remaining = input;
loop {
let token_result = token(remaining);
match token_result {
Ok((inp, tok)) => {
if tok == Token::DictEnd {
return Ok((inp, Object::Dictionary(dict)));
}
match tok {
Token::Name(key) => {
match parse_object(inp) {
Ok((inp, value)) => {
dict.insert(key, value);
remaining = inp;
},
Err(e) => {
if inp.is_empty() {
return Ok((inp, Object::Dictionary(dict)));
}
return Err(e);
},
}
},
_ => {
if remaining.is_empty() {
return Ok((remaining, Object::Dictionary(dict)));
}
return Err(nom::Err::Error(nom::error::Error::new(
remaining,
nom::error::ErrorKind::Tag,
)));
},
}
},
Err(nom::Err::Incomplete(_)) | Err(nom::Err::Error(_)) if remaining.is_empty() => {
return Ok((remaining, Object::Dictionary(dict)));
},
Err(e) => return Err(e),
}
}
}
pub fn decode_hex(hex_bytes: &[u8]) -> Result<Vec<u8>> {
let hex_str: Vec<u8> = hex_bytes
.iter()
.filter(|&&c| !c.is_ascii_whitespace())
.copied()
.collect();
if hex_str.is_empty() {
return Ok(Vec::new());
}
fn hex_val(b: u8) -> u8 {
match b {
b'0'..=b'9' => b - b'0',
b'a'..=b'f' => b - b'a' + 10,
b'A'..=b'F' => b - b'A' + 10,
_ => 0, }
}
let mut result = Vec::with_capacity(hex_str.len() / 2 + 1);
for chunk in hex_str.chunks(2) {
match chunk.len() {
2 => {
let byte = (hex_val(chunk[0]) << 4) | hex_val(chunk[1]);
result.push(byte);
},
1 => {
let byte = hex_val(chunk[0]) << 4;
result.push(byte);
},
_ => {
return Err(Error::ParseError {
offset: 0,
reason: "Invalid hex string chunk size".to_string(),
});
},
}
}
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_null() {
let input = b"null";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::Null);
}
#[test]
fn test_parse_boolean_true() {
let input = b"true";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::Boolean(true));
}
#[test]
fn test_parse_boolean_false() {
let input = b"false";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::Boolean(false));
}
#[test]
fn test_parse_integer() {
let input = b"42";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::Integer(42));
}
#[test]
fn test_parse_negative_integer() {
let input = b"-123";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::Integer(-123));
}
#[test]
#[allow(clippy::approx_constant)]
fn test_parse_real() {
let input = b"3.14";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::Real(3.14));
}
#[test]
fn test_parse_name() {
let input = b"/Type";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::Name("Type".to_string()));
}
#[test]
fn test_parse_literal_string() {
let input = b"(Hello World)";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::String(b"Hello World".to_vec()));
}
#[test]
fn test_parse_empty_literal_string() {
let input = b"()";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::String(b"".to_vec()));
}
#[test]
fn test_escape_sequence_newline() {
let input = b"(Line1\\nLine2)";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::String(b"Line1\nLine2".to_vec()));
}
#[test]
fn test_escape_sequence_carriage_return() {
let input = b"(Line1\\rLine2)";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::String(b"Line1\rLine2".to_vec()));
}
#[test]
fn test_escape_sequence_tab() {
let input = b"(Col1\\tCol2)";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::String(b"Col1\tCol2".to_vec()));
}
#[test]
fn test_escape_sequence_backspace() {
let input = b"(Text\\bmore)";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::String(b"Text\x08more".to_vec()));
}
#[test]
fn test_escape_sequence_form_feed() {
let input = b"(Page1\\fPage2)";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::String(b"Page1\x0CPage2".to_vec()));
}
#[test]
fn test_escape_sequence_parentheses() {
let input = b"(Open \\( Close \\))";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::String(b"Open ( Close )".to_vec()));
}
#[test]
fn test_escape_sequence_backslash() {
let input = b"(Path\\\\to\\\\file)";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::String(b"Path\\to\\file".to_vec()));
}
#[test]
fn test_escape_sequence_octal_three_digits() {
let input = b"(Section \\247)";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::String(b"Section \xa7".to_vec()));
}
#[test]
fn test_escape_sequence_octal_two_digits() {
let input = b"(Plus \\53)";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::String(b"Plus +".to_vec()));
}
#[test]
fn test_escape_sequence_octal_one_digit() {
let input = b"(Bell \\7)";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::String(b"Bell \x07".to_vec()));
}
#[test]
fn test_escape_sequence_octal_stops_at_non_octal() {
let input = b"(Value \\128)";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::String(b"Value \n8".to_vec()));
}
#[test]
fn test_escape_sequence_real_pdf_case() {
let input = b"(\\247 71.01\\26115 Temporary certificate.)";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::String(b"\xa7 71.01\xb115 Temporary certificate.".to_vec()));
}
#[test]
fn test_escape_sequence_line_continuation() {
let input = b"(This is a long \\\nstring)";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::String(b"This is a long string".to_vec()));
}
#[test]
fn test_escape_sequence_mixed() {
let input = b"(Tab:\\tNewline:\\nOctal:\\53)";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::String(b"Tab:\tNewline:\nOctal:+".to_vec()));
}
#[test]
fn test_decode_literal_string_escapes_directly() {
assert_eq!(decode_literal_string_escapes(b"Hello"), b"Hello");
assert_eq!(decode_literal_string_escapes(b"\\n"), b"\n");
assert_eq!(decode_literal_string_escapes(b"\\247"), b"\xa7");
assert_eq!(decode_literal_string_escapes(b"\\(\\)"), b"()");
assert_eq!(decode_literal_string_escapes(b"\\\\"), b"\\");
}
#[test]
fn test_parse_hex_string() {
let input = b"<48656C6C6F>";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::String(b"Hello".to_vec()));
}
#[test]
fn test_parse_hex_string_with_whitespace() {
let input = b"<48 65 6C 6C 6F>";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::String(b"Hello".to_vec()));
}
#[test]
fn test_parse_empty_hex_string() {
let input = b"<>";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::String(b"".to_vec()));
}
#[test]
fn test_parse_hex_string_odd_length() {
let input = b"<ABC>";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::String(vec![0xAB, 0xC0]));
}
#[test]
fn test_decode_hex() {
let result = decode_hex(b"48656C6C6F").unwrap();
assert_eq!(result, b"Hello");
}
#[test]
fn test_decode_hex_with_whitespace() {
let result = decode_hex(b"48 65 6C 6C 6F").unwrap();
assert_eq!(result, b"Hello");
}
#[test]
fn test_decode_hex_empty() {
let result = decode_hex(b"").unwrap();
assert_eq!(result, b"");
}
#[test]
fn test_decode_hex_odd_length() {
let result = decode_hex(b"ABC").unwrap();
assert_eq!(result, vec![0xAB, 0xC0]);
}
#[test]
fn test_parse_indirect_reference() {
let input = b"10 0 R";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::Reference(ObjectRef::new(10, 0)));
}
#[test]
fn test_parse_indirect_reference_with_generation() {
let input = b"42 5 R";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::Reference(ObjectRef::new(42, 5)));
}
#[test]
fn test_parse_integer_not_reference() {
let input = b"10";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::Integer(10));
}
#[test]
fn test_parse_empty_array() {
let input = b"[]";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::Array(vec![]));
}
#[test]
fn test_parse_array_with_integers() {
let input = b"[ 1 2 3 ]";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(
obj,
Object::Array(vec![Object::Integer(1), Object::Integer(2), Object::Integer(3),])
);
}
#[test]
fn test_parse_array_mixed_types() {
let input = b"[ 1 /Name (string) true ]";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(
obj,
Object::Array(vec![
Object::Integer(1),
Object::Name("Name".to_string()),
Object::String(b"string".to_vec()),
Object::Boolean(true),
])
);
}
#[test]
fn test_parse_nested_arrays() {
let input = b"[ 1 [ 2 3 ] 4 ]";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(
obj,
Object::Array(vec![
Object::Integer(1),
Object::Array(vec![Object::Integer(2), Object::Integer(3)]),
Object::Integer(4),
])
);
}
#[test]
fn test_parse_array_with_references() {
let input = b"[ 10 0 R 20 0 R ]";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(
obj,
Object::Array(vec![
Object::Reference(ObjectRef::new(10, 0)),
Object::Reference(ObjectRef::new(20, 0)),
])
);
}
#[test]
fn test_parse_empty_dictionary() {
let input = b"<<>>";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::Dictionary(HashMap::new()));
}
#[test]
fn test_parse_dictionary_single_entry() {
let input = b"<< /Type /Page >>";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
let dict = obj.as_dict().unwrap();
assert_eq!(dict.len(), 1);
assert_eq!(dict.get("Type").unwrap().as_name(), Some("Page"));
}
#[test]
fn test_parse_dictionary_multiple_entries() {
let input = b"<< /Type /Page /Count 3 /Title (My Page) >>";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
let dict = obj.as_dict().unwrap();
assert_eq!(dict.len(), 3);
assert_eq!(dict.get("Type").unwrap().as_name(), Some("Page"));
assert_eq!(dict.get("Count").unwrap().as_integer(), Some(3));
assert_eq!(dict.get("Title").unwrap().as_string(), Some(&b"My Page"[..]));
}
#[test]
fn test_parse_dictionary_with_array() {
let input = b"<< /MediaBox [ 0 0 612 792 ] >>";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
let dict = obj.as_dict().unwrap();
assert_eq!(dict.len(), 1);
let media_box = dict.get("MediaBox").unwrap().as_array().unwrap();
assert_eq!(media_box.len(), 4);
}
#[test]
fn test_parse_nested_dictionaries() {
let input = b"<< /Outer << /Inner /Value >> >>";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
let dict = obj.as_dict().unwrap();
let inner = dict.get("Outer").unwrap().as_dict().unwrap();
assert_eq!(inner.get("Inner").unwrap().as_name(), Some("Value"));
}
#[test]
fn test_parse_dictionary_with_reference() {
let input = b"<< /Pages 2 0 R >>";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
let dict = obj.as_dict().unwrap();
assert_eq!(dict.get("Pages").unwrap().as_reference(), Some(ObjectRef::new(2, 0)));
}
#[test]
fn test_parse_complex_nested_structure() {
let input = b"<< /Type /Catalog /Pages [ 1 0 R 2 0 R ] /Metadata << /Author (John) >> >>";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
let dict = obj.as_dict().unwrap();
assert_eq!(dict.get("Type").unwrap().as_name(), Some("Catalog"));
let pages = dict.get("Pages").unwrap().as_array().unwrap();
assert_eq!(pages.len(), 2);
let metadata = dict.get("Metadata").unwrap().as_dict().unwrap();
assert_eq!(metadata.get("Author").unwrap().as_string(), Some(&b"John"[..]));
}
#[test]
fn test_parse_unclosed_array() {
let input = b"[ 1 2 3";
let result = parse_object(input);
assert!(result.is_ok());
let (_, obj) = result.unwrap();
let arr = obj.as_array().unwrap();
assert_eq!(arr.len(), 3);
assert_eq!(arr[0].as_integer(), Some(1));
assert_eq!(arr[1].as_integer(), Some(2));
assert_eq!(arr[2].as_integer(), Some(3));
}
#[test]
fn test_parse_unclosed_dictionary() {
let input = b"<< /Type /Page";
let result = parse_object(input);
assert!(result.is_ok());
let (_, obj) = result.unwrap();
let dict = obj.as_dict().unwrap();
assert_eq!(dict.get("Type").and_then(|o| o.as_name()), Some("Page"));
}
#[test]
fn test_parse_dictionary_missing_value() {
let input = b"<< /Type >>";
let result = parse_object(input);
assert!(result.is_err());
}
#[test]
fn test_parse_dictionary_non_name_key() {
let input = b"<< 123 /Value >>";
let result = parse_object(input);
assert!(result.is_err());
}
#[test]
fn test_parse_with_leading_whitespace() {
let input = b" \n\t 42";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
assert_eq!(obj, Object::Integer(42));
}
#[test]
fn test_parse_array_with_extra_whitespace() {
let input = b"[ 1 2 3 ]";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
let arr = obj.as_array().unwrap();
assert_eq!(arr.len(), 3);
}
#[test]
fn test_parse_dictionary_with_extra_whitespace() {
let input = b"<< /Type /Page >>";
let (remaining, obj) = parse_object(input).unwrap();
assert_eq!(remaining, &b""[..]);
let dict = obj.as_dict().unwrap();
assert_eq!(dict.get("Type").unwrap().as_name(), Some("Page"));
}
}