Total beginner in rust, but so far am learning a lot. Just stuck in this problem. I am trying to read a pdf using pdfium_render, save as a jpeg using image crate and encrypt the jpegs then decrypt them to view.
Below is the function I have to export pdf to jpegs, in this function I do the following:
Load the pdfium windows dll
Load a pdf file
Setup a render config and loop through all pages to render into images.
Encrypt then decrypt the message to test image encryption the page using the methods mentioned in this example.
Save the decrypted image to view.
Note, the end goal here is to be able to encrypt the image save it on disk, then load from another program, but with the following code the image is corrupted while if I don't encrypt the image and save it directly to a jpeg, it works.
My question is, what suggestions do you have for image encryption ? What am I missing here ? This is a fun project for me so I am open for any trials, thank you !
Code:
fn export_pdf_to_jpegs(path: &str, password: Option<&str>) -> Result<(), PdfiumError> {
// Renders each page in the PDF file at the given path to a separate JPEG file.
// Bind to a Pdfium library in the same directory as our Rust executable;
// failing that, fall back to using a Pdfium library provided by the operating system.
println!("Creating pdf obj");
let pdfium = Pdfium::new(
Pdfium::bind_to_library(Pdfium::pdfium_platform_library_name_at_path(BIN_PATH))
.or_else(|_| Pdfium::bind_to_system_library())?,
);
// Load the document from the given path...
println!("Loading pdf document from {}", path);
let document = pdfium.load_pdf_from_file(path, password).expect("Can't open pdf file");
// ... set rendering options that will be applied to all pages...
println!("Rendering pdf");
let render_config = PdfRenderConfig::new()
.set_target_width(2000)
.set_maximum_height(2000)
.rotate_if_landscape(PdfBitmapRotation::Degrees90, true);
// ... then render each page to a bitmap image, saving each image to a JPEG file.
let cipher = XChaCha20Poly1305::new(&KEY.into());
for (index, page) in document.pages().iter().enumerate() {
let temp = page.render_with_config(&render_config)
.unwrap()
.as_image()
.as_rgba8()
.unwrap()
.clone();
let dim = temp.dimensions();
println!("{:?}",dim);
println!("Encrypting {}", index);
let encrypted_data = cipher
.encrypt(&NONCE.into(), temp.into_raw().as_ref())
.unwrap();
//fs::write(format!("{}_{}.bin", path, index), encrypted_data);
let image_data_raw: Vec<u8> = cipher
.decrypt(&NONCE.into(), encrypted_data.as_ref()).unwrap();
print_type_of(&image_data_raw);
let image_buffer: image::ImageBuffer<image::Rgb<_>,Vec<_>>=
image::ImageBuffer::from_raw(1414,2000,image_data_raw).unwrap();
image_buffer.save_with_format(
"1.jpg",
image::ImageFormat::Jpeg
);
return Ok(()); // saving one image for debug
}
Ok(())
}
Reusing a nonce to encrypt data with the same key can compromise (some of) the security of the system. You should use a different nonce for each page if you're going to encrypt them separately.
Other than that, I don't see an obvious issue with your cryptography code. Are you sure when you commented out the encryption that all of the code to convert the image into bytes and then back again was the same?
Yup I reuse the same code and I am sure of the render dimensions, I reuse the same render dimensions I print with
let temp = page.render_with_config(&render_config)
.unwrap()
.as_image()
.as_rgba8()
.unwrap()
.clone();
let dim = temp.dimensions();
println!("{:?}",dim);
I encrypt the raw bytes of the image, then load the raw bytes into an image::ImageBuffer using:
let image_buffer: image::ImageBuffer<image::Rgb<_>,Vec<_>>=
image::ImageBuffer::from_raw(1414,2000,image_data_raw).unwrap();
Here is sample pdf page (left) and the output (right):
You said you had tried it without the encryption and it worked, my question was whether you were sure you were doing all of the exact same operations other than the encryption and decryption in that version.
That is the code that works without the encryption and decryption step:
fn export_pdf_to_jpegs(path: &str, password: Option<&str>) -> Result<(), PdfiumError> {
// Renders each page in the PDF file at the given path to a separate JPEG file.
// Bind to a Pdfium library in the same directory as our Rust executable;
// failing that, fall back to using a Pdfium library provided by the operating system.
println!("Creating pdf obj");
let pdfium = Pdfium::new(
Pdfium::bind_to_library(Pdfium::pdfium_platform_library_name_at_path(BIN_PATH))
.or_else(|_| Pdfium::bind_to_system_library())?,
);
// Load the document from the given path...
println!("Loading pdf document from {}", path);
let document = pdfium.load_pdf_from_file(path, password).expect("Can't open pdf file");
// ... set rendering options that will be applied to all pages...
println!("Rendering pdf");
let render_config = PdfRenderConfig::new()
.set_target_width(2000)
.set_maximum_height(2000)
.rotate_if_landscape(PdfBitmapRotation::Degrees90, true);
// ... then render each page to a bitmap image, saving each image to a JPEG file.
for (index, page) in document.pages().iter().enumerate() {
page.render_with_config(&render_config)
.unwrap()
.as_image()
.as_rgba8()
.unwrap()
.save_with_format(
"1.jpg",
image::ImageFormat::Jpeg
);
return Ok(()); // one only for debug
}
Ok(())
}
Only difference is the encryption/decryption code.
Well you aren't doing the round trip through the byte vec there
Does it still work if you do that but not the encryption?
fn export_pdf_to_jpegs(path: &str, password: Option<&str>) -> Result<(), PdfiumError> {
// Renders each page in the PDF file at the given path to a separate JPEG file.
// Bind to a Pdfium library in the same directory as our Rust executable;
// failing that, fall back to using a Pdfium library provided by the operating system.
println!("Creating pdf obj");
let pdfium = Pdfium::new(
Pdfium::bind_to_library(Pdfium::pdfium_platform_library_name_at_path(BIN_PATH))
.or_else(|_| Pdfium::bind_to_system_library())?,
);
// Load the document from the given path...
println!("Loading pdf document from {}", path);
let document = pdfium
.load_pdf_from_file(path, password)
.expect("Can't open pdf file");
// ... set rendering options that will be applied to all pages...
println!("Rendering pdf");
let render_config = PdfRenderConfig::new()
.set_target_width(2000)
.set_maximum_height(2000)
.rotate_if_landscape(PdfBitmapRotation::Degrees90, true);
// ... then render each page to a bitmap image, saving each image to a JPEG file.
let cipher = XChaCha20Poly1305::new(&KEY.into());
for (index, page) in document.pages().iter().enumerate() {
let temp = page
.render_with_config(&render_config)
.unwrap()
.as_image()
.as_rgba8()
.unwrap()
.clone();
let dim = temp.dimensions();
println!("{:?}", dim);
println!("Encrypting {}", index);
let image_data_raw: Vec<u8> = temp.into_raw();
print_type_of(&image_data_raw);
let image_buffer: image::ImageBuffer<image::Rgb<_>, Vec<_>> =
image::ImageBuffer::from_raw(1414, 2000, image_data_raw).unwrap();
image_buffer.save_with_format("1.jpg", image::ImageFormat::Jpeg);
return Ok(()); // saving one image for debug
}
Ok(())
}
oh this actually gave the same corruption on the saved jpeg, so the problem is with how I reread the raw data. I will play around with that and update the thread here if I reach a solution, thanks !