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

How to set the DPI of an image before saving it? #482

Open
xYx-c opened this issue May 15, 2024 · 4 comments
Open

How to set the DPI of an image before saving it? #482

xYx-c opened this issue May 15, 2024 · 4 comments

Comments

@xYx-c
Copy link

xYx-c commented May 15, 2024

pub fn build_dpi_chunk(dpi: u32) -> Vec<u8> {
    let dpm = 39.370_08 * dpi as f32;
    let rounded_dpm = dpm.round() as u32;

    let mut data = Vec::new();
    data.extend_from_slice(&rounded_dpm.to_be_bytes());
    data.extend_from_slice(&rounded_dpm.to_be_bytes());

    data.push(1);
    data
}

pub fn png_with_dpi<P>(imgbuf: ImageBuffer<P, Vec<u8>>, dpi: u32) -> Result<ImageBuffer<P, Vec<u8>>, std::io::Error>
where
    P: Pixel<Subpixel = u8>,
{
    let (width, height) = imgbuf.dimensions();
    let mut encoder = png::Encoder::new(imgbuf.clone().into_raw(), width, height);

    match <P as Pixel>::CHANNEL_COUNT {
        4 => encoder.set_color(png::ColorType::Rgba),
        3 => encoder.set_color(png::ColorType::Rgb),
        _ => {
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidData,
                "Incorrect color channel count / format.",
            ))
        },
    }
    encoder.set_depth(png::BitDepth::Eight);
    encoder.set_compression(png::Compression::Best);
    let data = build_dpi_chunk(dpi);

    let mut writer = encoder.write_header()?;

    writer.write_chunk(png::chunk::pHYs, data.as_slice())?;
    writer.write_image_data(&imgbuf)?;


    Ok(imgbuf)
}

I have tried using like this but it doesn't work correctly.

@fintelia
Copy link
Contributor

The first argument to Encoder::new is the writer you want to encode the PNG data into. Often this will be an empty Vec:

let mut encoded = Vec::new();
let mut encoder = png::Encoder::new(&mut encoded, width, height);

And then at the end you need to finish the encoding and return a copy of the encoded data rather than the initial image:

writer.finish()?;
Ok(encoded)

@xYx-c
Copy link
Author

xYx-c commented May 16, 2024

Thanks for the help!

call method:

pub fn png_with_dpi<P>(imgbuf: ImageBuffer<P, Vec<u8>>, dpi: u32) -> Result<Vec<u8>, std::io::Error>
where
    P: Pixel<Subpixel = u8>,
{
    let (width, height) = imgbuf.dimensions();
    let mut encoded = Vec::new();
    let mut encoder = png::Encoder::new(&mut encoded, width, height);

    match <P as Pixel>::CHANNEL_COUNT {
        4 => encoder.set_color(png::ColorType::Rgba),
        3 => encoder.set_color(png::ColorType::Rgb),
        _ => {
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidData,
                "Incorrect color channel count / format.",
            ))
        },
    }
    encoder.set_depth(png::BitDepth::Eight);
    encoder.set_compression(png::Compression::Best);
    let data = build_dpi_chunk(dpi);

    let mut writer = encoder.write_header()?;

    writer.write_chunk(png::chunk::pHYs, data.as_slice())?;
    writer.write_image_data(&imgbuf)?;

    writer.finish()?;
    Ok(encoded)
}

png_with_dpi(merge_image, 300)?;

result:

x@26707:~/Downloads$ identify -verbose ff3a0f48613d4a529c06d8d5c94b2a4d.png
Image:
  Filename: ff3a0f48613d4a529c06d8d5c94b2a4d.png
  Format: PNG (Portable Network Graphics)
  Mime type: image/png
  Class: DirectClass
  Geometry: 1504x2028+0+0
  Resolution: 118.11x118.11
  ...

Resolution: Doesn't seem to meet expectations?

I used imagemagick to verify the results.

@fintelia
Copy link
Contributor

300 DPI = 118.11 pixels/cm, are you sure this isn't the output you expect?

@xYx-c
Copy link
Author

xYx-c commented May 17, 2024

thank you very much for your help.
does meet the expected results.
But when I use software like PhtotShow to open the picture, it shows 900ppi. After conversion, it is 354.331px/cm.

After that, I used a third-party software to set it to 118.11, and the correct reading on the PS was 300ppi.

x@26707:~/Downloads$ convert 030226201d6c4653a9ad492218582d5a.png -density 118.11 118_11.png
x@26707:~/Downloads$ identify -verbose 118_11.png 
Image:
  Filename: 118_11.png
  Format: PNG (Portable Network Graphics)
  Mime type: image/png
  Class: DirectClass
  Geometry: 1504x2028+0+0
  Resolution: 118.11x118.11
  ...

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

2 participants