Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Only Rgb8 Image Display Correctly After Embedded in PDF #367

Open
lanyeeee opened this issue Dec 28, 2024 · 0 comments
Open

Only Rgb8 Image Display Correctly After Embedded in PDF #367

lanyeeee opened this issue Dec 28, 2024 · 0 comments

Comments

@lanyeeee
Copy link
Contributor

Description

There are serious issues with the embed_image embed feature.
Through testing, only Rgb8 images can display correctly, while all other image formats fail to display properly.

Affected Image Formats

The following formats are currently not working correctly when embedded in PDF:

  • L8 (8-bit grayscale)
  • La8 (8-bit grayscale with alpha)
  • Rgba8 (8-bit RGB with alpha)
  • L16 (16-bit grayscale)
  • La16 (16-bit grayscale with alpha)
  • Rgb16 (16-bit RGB)
  • Rgba16 (16-bit RGBA)
  • Rgb32F (32-bit float RGB)
  • Rgba32F (32-bit float RGBA)

Current Behavior

  • RGB8: ✅ Displays correctly
  • All other formats: ❌ Either not displaying at all or displaying with incorrect colors/appearance

Expected Behavior

All common image formats should be properly converted and embedded into the PDF with correct color representation and display.

Technical Details

The current implementation in xobject.rs has several issues:

  1. Incorrect handling of bits non-Rgb8 formats
    let bits = color_type.bits_per_pixel() / 3;
  2. Improper color space selection for various image formats

    lopdf/src/xobject.rs

    Lines 77 to 86 in 34e2d20

    let color_space = match color_type {
    ColorType::L8 => b"DeviceGray".to_vec(),
    ColorType::La8 => b"DeviceGray".to_vec(),
    ColorType::Rgb8 => b"DeviceRGB".to_vec(),
    ColorType::Rgb16 => b"DeviceRGB".to_vec(),
    ColorType::La16 => b"DeviceN".to_vec(),
    ColorType::Rgba8 => b"DeviceN".to_vec(),
    ColorType::Rgba16 => b"DeviceN".to_vec(),
    _ => b"Indexed".to_vec(),
    };
  3. Incorrect handling of pdf content(image data)

    lopdf/src/xobject.rs

    Lines 100 to 103 in 34e2d20

    let mut img_object = Stream::new(dict, img.unwrap().into_bytes());
    // Ignore any compression error.
    let _ = img_object.compress();
    Ok(img_object)

Steps to Reproduce

1. main.rs

use image::GenericImageView;
use lopdf::content::{Content, Operation};
use lopdf::{dictionary, Document, Object, Stream};
use std::path::Path;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let pdf_path = Path::new("output.pdf");

    let mut img_paths = std::fs::read_dir("images")?
        .filter_map(|entry| entry.ok())
        .map(|entry| entry.path())
        .collect::<Vec<_>>();
    // sort by file name
    img_paths.sort_by(|a, b| a.file_name().cmp(&b.file_name()));

    let mut doc = Document::with_version("1.5");
    let pages_id = doc.new_object_id();
    let mut page_ids = vec![];

    for img_path in img_paths {
        let img = image::open(&img_path)?;
        let (width, height) = img.dimensions();
        let color_type = img.color();
        println!("Image: {img_path:?}, width: {width}, height: {height}, color type: {color_type:?}");

        let image_stream = lopdf::xobject::image(img_path)?;

        let img_id = doc.add_object(image_stream);
        let img_name = format!("X{}", img_id.0);

        let cm_operation = Operation::new(
            "cm",
            vec![width.into(), 0.into(), 0.into(), height.into(), 0.into(), 0.into()],
        );

        let do_operation = Operation::new("Do", vec![Object::Name(img_name.as_bytes().to_vec())]);
        let content = Content {
            operations: vec![cm_operation, do_operation],
        };

        let content_id = doc.add_object(Stream::new(dictionary! {}, content.encode()?));
        let page_id = doc.add_object(dictionary! {
            "Type" => "Page",
            "Parent" => pages_id,
            "Contents" => content_id,
            "MediaBox" => vec![0.into(), 0.into(), width.into(), height.into()],
        });

        doc.add_xobject(page_id, img_name.as_bytes(), img_id)?;
        // add page to doc
        page_ids.push(page_id);
    }

    let pages_dict = dictionary! {
        "Type" => "Pages",
        "Count" => page_ids.len() as u32,
        "Kids" => page_ids.into_iter().map(Object::Reference).collect::<Vec<_>>(),
    };
    doc.objects.insert(pages_id, Object::Dictionary(pages_dict));

    let catalog_id = doc.add_object(dictionary! {
        "Type" => "Catalog",
        "Pages" => pages_id,
    });
    doc.trailer.set("Root", catalog_id);

    doc.compress();

    doc.save(pdf_path)?;
    Ok(())
}

2. Cargo.toml

[dependencies]
lopdf = { version = "0.34.0", features = ["embed_image"] }
image = { version = "0.25.5" }

3. download and unzip images to project root
images.zip

4. cargo run
image in images will be Embed to output.pdf
You will see that none of the images except Rgb8 display properly

lopdf Version

0.34.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant