~ireas/public-inbox

genpdf-rs: Adds image support. v2 APPLIED

Alexander Dean-Kennedy: 1
 Adds image support.

 15 files changed, 580 insertions(+), 26 deletions(-)
#363958 archlinux-msrv.yml failed
#363959 archlinux.yml failed
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~ireas/public-inbox/patches/15888/mbox | git am -3
Learn more about email & git
View this thread in the archives

[PATCH genpdf-rs v2] Adds image support. Export this patch

#### [Purpose / Implementation Overview]
* Changes `elements` module to a directory to allow for conditional subelements.
* Gates this feature behind `images` feature-flag.
* Adds support to the demo, and includes a couple public-domain images.
* Adds another demo document for full functionality testing.
* BREAKING: Moves `Alignment` struct out of `elements` up to root.

### [Testing]
```
cargo run --example demo demo.pdf
cargo run --features images --example demo demo-with-images.pdf
cargo run --features images --example images images.pdf
```
---
 CHANGELOG.md                         |   2 +
 Cargo.toml                           |   8 +
 README.md                            |   4 +-
 examples/build_all.sh                |   7 +
 examples/demo.rs                     |  33 +++-
 examples/images.rs                   | 139 +++++++++++++++
 examples/images/README.md            |   4 +
 examples/images/test_image.bmp       | Bin 0 -> 66186 bytes
 examples/images/test_image.jpg       | Bin 0 -> 3854 bytes
 examples/images/test_image.png       | Bin 0 -> 11920 bytes
 src/elements/images.rs               | 245 +++++++++++++++++++++++++++
 src/{elements.rs => elements/mod.rs} |  29 +---
 src/error.rs                         |  14 ++
 src/lib.rs                           |  89 ++++++++++
 src/render.rs                        |  32 ++++
 15 files changed, 580 insertions(+), 26 deletions(-)
 create mode 100755 examples/build_all.sh
 create mode 100644 examples/images.rs
 create mode 100644 examples/images/README.md
 create mode 100644 examples/images/test_image.bmp
 create mode 100644 examples/images/test_image.jpg
 create mode 100644 examples/images/test_image.png
 create mode 100644 src/elements/images.rs
 rename src/{elements.rs => elements/mod.rs} (98%)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2a02833..963c381 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -31,6 +31,7 @@ SPDX-License-Identifier: CC0-1.0
- Remove the `Document::set_margins` method (use a `PageDecorator` instead).
- Replace the `PdfprintError` variant of `ErrorKind` with `PdfError` and
  `PdfIndexError`.
- Move `Alignment` struct out of `elements` module.

## Non-Breaking Changes

@@ -46,6 +47,7 @@ SPDX-License-Identifier: CC0-1.0
- Add support for kerning and add the `Font::kerning` and `Font::glyph_ids`
  methods.
- Add the `error::Context` trait for easier error generation.
- Adds support for `Image` as a possible insertable element.

## Bug Fixes

diff --git a/Cargo.toml b/Cargo.toml
index 329a259..34c33f9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,6 +20,10 @@ readme = "README.md"
lopdf = "0.26"
rusttype = "0.8"

[dependencies.image]
version = "0.23.12"
optional = true

[dependencies.hyphenation]
version = "0.8"
optional = true
@@ -37,5 +41,9 @@ features = ["add", "add_assign", "from", "into", "mul", "mul_assign", "sum"]
version = "0.8"
features = ["embed_en-us"]

[features]
default = []
images = ["image", "printpdf/embedded_images"]

[package.metadata.docs.rs]
all-features = true
diff --git a/README.md b/README.md
index 1e1d6a0..2a7411d 100644
--- a/README.md
+++ b/README.md
@@ -53,10 +53,13 @@ For more information, see the [API documentation](https://docs.rs/genpdf).
- Layout of elements sequentially or in tables
- Rudimentary support for shapes
- Page headers and custom page decorations
- Embedding images (scale, position, rotate).

## Cargo Features

This crate has the following Cargo features (deactivated per default):

- `images`: Adds support for embedding images.
- `hyphenation`:  Adds support for hyphenation using the [`hyphenation`][] crate.

[`hyphenation`]: https://lib.rs/crates/hyphenation
@@ -65,7 +68,6 @@ This crate has the following Cargo features (deactivated per default):

These features are currently not supported but planned for future versions:
- Improved support for drawing shapes
- Images
- Advanced text formatting

See also the [`genpdf-rs` issue tracker](https://todo.sr.ht/~ireas/genpdf-rs).
diff --git a/examples/build_all.sh b/examples/build_all.sh
new file mode 100755
index 0000000..a852756
--- /dev/null
+++ b/examples/build_all.sh
@@ -0,0 +1,7 @@
#!/bin/sh
# Build all the example pdf documents.
# As we add additional optional features, we will want to try all
# combinations and how they interact with one another.
cargo run --example demo demo.pdf \
   && cargo run --features images --example demo demo-with-images.pdf \
   && cargo run --features images --example images images.pdf
diff --git a/examples/demo.rs b/examples/demo.rs
index 6f77d03..e2299ff 100644
--- a/examples/demo.rs
+++ b/examples/demo.rs
@@ -15,6 +15,7 @@

use std::env;

use genpdf::Alignment;
use genpdf::Element as _;
use genpdf::{elements, fonts, style};

@@ -53,7 +54,7 @@ fn main() {
        if page > 1 {
            layout.push(
                elements::Paragraph::new(format!("Page {}", page))
                    .aligned(elements::Alignment::Center),
                    .aligned(Alignment::Center),
            );
            layout.push(elements::Break::new(1));
        }
@@ -78,7 +79,7 @@ fn main() {

    doc.push(
        elements::Paragraph::new("genpdf Demo Document")
            .aligned(elements::Alignment::Center)
            .aligned(Alignment::Center)
            .styled(style::Style::new().bold().with_font_size(20)),
    );
    doc.push(elements::Break::new(1.5));
@@ -176,7 +177,7 @@ fn main() {
        "You already saw lists and formatted centered text. Here are some other examples:",
    ));
    doc.push(
        elements::Paragraph::new("This is right-aligned text.").aligned(elements::Alignment::Right),
        elements::Paragraph::new("This is right-aligned text.").aligned(Alignment::Right),
    );
    doc.push(
        elements::Paragraph::new("And this paragraph has a frame drawn around it and is colored.")
@@ -198,6 +199,10 @@ fn main() {
    );
    doc.push(elements::Break::new(1.5));

    doc.push(elements::Paragraph::new("Embedding images also works using the 'images' feature."));
    #[cfg(feature = "images")]
    images::do_image_test(&mut doc);

    doc.push(elements::Paragraph::new("Here is an example table:"));

    let mut table = elements::TableLayout::new(vec![1, 2]);
@@ -287,3 +292,25 @@ fn main() {
    doc.render_to_file(output_file)
        .expect("Failed to write output file");
}

// Only import the images if the feature is enabled. This helps verify our handling of feature toggles.
#[cfg(feature = "images")]
mod images {
    use super::*;

    const IMAGE_PATH_JPG: &'static str = "examples/images/test_image.jpg";

    pub fn do_image_test(doc: &mut genpdf::Document) {
        doc.push(elements::Paragraph::new("Here is an example image with default position/scale:"));
        doc.push(elements::Image::from_path(IMAGE_PATH_JPG).expect("Unable to load image"));
        doc.push(elements::Paragraph::new("and here is one that is centered, rotated, and scaled some."));
        doc.push(
            elements::Image::from_path(IMAGE_PATH_JPG)
                .expect("Unable to load image")
                .with_alignment(Alignment::Center)
                .with_scale(genpdf::Scale::new(0.5, 2))
                .with_clockwise_rotation(45.0)
        );
        doc.push(elements::Paragraph::new("For a full example of image functionality, please see images.pdf."));
    }
}
\ No newline at end of file
diff --git a/examples/images.rs b/examples/images.rs
new file mode 100644
index 0000000..4e55f38
--- /dev/null
+++ b/examples/images.rs
@@ -0,0 +1,139 @@
use std::env;

use genpdf::Alignment;
use genpdf::Element as _;
use genpdf::{elements, fonts, style};

const FONT_DIR: &'static str = "/usr/share/fonts/truetype/liberation";
const DEFAULT_FONT_NAME: &'static str = "LiberationSans";

const IMAGE_PATH_JPG: &'static str = "examples/images/test_image.jpg";
const IMAGE_PATH_BMP: &'static str = "examples/images/test_image.bmp";
const IMAGE_PATH_PNG: &'static str = "examples/images/test_image.png";

fn main() {
    let args: Vec<_> = env::args().skip(1).collect();
    if args.len() != 1 {
        panic!("Missing argument: output file");
    }
    let output_file = &args[0];

    let default_font =
        fonts::from_files(FONT_DIR, DEFAULT_FONT_NAME, Some(fonts::Builtin::Helvetica))
            .expect("Failed to load the default font family");

    let mut doc = genpdf::Document::new(default_font);
    doc.set_title("genpdf Demo Document");
    doc.set_minimal_conformance();
    doc.set_line_spacing(1.25);

    let mut decorator = genpdf::SimplePageDecorator::new();
    decorator.set_margins(10);
    decorator.set_header(|page| {
        let mut layout = elements::LinearLayout::vertical();
        if page > 1 {
            layout.push(
                elements::Paragraph::new(format!("Page {}", page))
                    .aligned(Alignment::Center),
            );
            layout.push(elements::Break::new(1));
        }
        layout.styled(style::Style::new().with_font_size(10))
    });
    doc.set_page_decorator(decorator);

    doc.push(
        elements::Paragraph::new("genpdf Image Tests")
            .aligned(Alignment::Center)
            .styled(style::Style::new().bold().with_font_size(20)),
    );
    doc.push(elements::Break::new(1.5));
    doc.push(elements::Paragraph::new(
        "You may also: override the position, dpi, and/or default line-breaks, etc. See image here =>"
    ));

    doc.push(
        elements::Image::from_path(IMAGE_PATH_JPG)
            .expect("Unable to load alt image")
            .with_position(genpdf::Position::new(170, -10)) // far over to right and down
            .with_clockwise_rotation(90.0)
    );

    // adding a break to avoid the image posted above with an "absolute image.
    doc.push(elements::Break::new(2));

    // IMAGE FILE TYPE HANDLING:
    doc.push(elements::Paragraph::new(
        "Table with image format/scaling tests:"
    ));
    let mut img_table = elements::TableLayout::new(vec![2, 2, 2, 2]);
    img_table.set_cell_decorator(elements::FrameCellDecorator::new(true, true, false));
    img_table
        .row()
        .element(elements::Text::new("Format").padded(1))
        .element(elements::Text::new("1:1").padded(1))
        .element(elements::Text::new("Half Size").padded(1))
        .element(elements::Text::new("Double Size").padded(1))
        .push()
        .expect("Invalid Row.");
    for (ftype, path) in vec![
        ("BMP", IMAGE_PATH_BMP), ("JPG", IMAGE_PATH_JPG), ("PNG", IMAGE_PATH_PNG)
    ] {
        let img = elements::Image::from_path(path).expect("invalid image");
        img_table
            .row()
            .element(elements::Paragraph::new(ftype).padded(1))
            .element(img.clone())
            .element(img.clone().with_scale(genpdf::Scale::new(0.5, 0.5)))
            .element(img.clone().with_scale(genpdf::Scale::new(2, 2)))
            .push()
            .expect("Invalid row.");
    }
    doc.push(img_table);

    doc.push(elements::Break::new(2));
    doc.push(elements::Paragraph::new(
        "Table with image rotation/offset calculation tests:"
    ));
    let mut rot_table = elements::TableLayout::new(vec![2, 2, 2, 2, 2, 2, 2]);
    rot_table.set_cell_decorator(elements::FrameCellDecorator::new(true, true, false));
    rot_table
        .row()
        .element(elements::Text::new("Rot").padded(1))
        .element(elements::Text::new("30°").padded(1))
        .element(elements::Text::new("45°").padded(1))
        .element(elements::Text::new("90").padded(1))
        .element(elements::Text::new("120").padded(1))
        .element(elements::Text::new("150").padded(1))
        .element(elements::Text::new("180").padded(1))
        .push()
        .expect("Invalid Row.");
    let img = elements::Image::from_path(IMAGE_PATH_JPG).expect("invalid image");
    rot_table
        .row()
        .element(elements::Text::new("Positive").padded(1))
        .element(img.clone().with_clockwise_rotation(30.0))
        .element(img.clone().with_clockwise_rotation(45.0))
        .element(img.clone().with_clockwise_rotation(90.0))
        .element(img.clone().with_clockwise_rotation(120.0))
        .element(img.clone().with_clockwise_rotation(150.0))
        .element(img.clone().with_clockwise_rotation(180.0))
        .push()
        .expect("Invalid Row.");
    rot_table
        .row()
        .element(elements::Text::new("Negative").padded(1))
        .element(img.clone().with_clockwise_rotation(-30.0))
        .element(img.clone().with_clockwise_rotation(-45.0))
        .element(img.clone().with_clockwise_rotation(-90.0))
        .element(img.clone().with_clockwise_rotation(-120.0))
        .element(img.clone().with_clockwise_rotation(-150.0))
        .element(img.clone().with_clockwise_rotation(-180.0))
        .push()
        .expect("Invalid Row.");
    doc.push(rot_table);


    doc.render_to_file(output_file)
        .expect("Failed to write output file");
}
diff --git a/examples/images/README.md b/examples/images/README.md
new file mode 100644
index 0000000..a214094
--- /dev/null
+++ b/examples/images/README.md
@@ -0,0 +1,4 @@
# Note of Usage
Test image pulled from public-domain:
https://free-images.com/display/black_white_cup_hand.html

diff --git a/examples/images/test_image.bmp b/examples/images/test_image.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..8fe2feb0c4a1cd477574d7b6fc49d60c84bd7bb0
GIT binary patch
literal 66186
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

literal 0
HcmV?d00001

diff --git a/examples/images/test_image.jpg b/examples/images/test_image.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..1c41033aadf9366caa56a57fb21fb03f61190a26
GIT binary patch
literal 3854
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
a@IflsPrs?^Zhr#PnW*Yx?S8fM_x}fC2iHmf

literal 0
HcmV?d00001

diff --git a/examples/images/test_image.png b/examples/images/test_image.png
new file mode 100644
index 0000000000000000000000000000000000000000..bc141adc7d12ffb7e157650142e5f0609e433970
GIT binary patch
literal 11920
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
WIxsM&EX)}I0000<MNUMnLSTaC0Re~r

literal 0
HcmV?d00001

diff --git a/src/elements/images.rs b/src/elements/images.rs
new file mode 100644
index 0000000..ab1f4fe
--- /dev/null
+++ b/src/elements/images.rs
@@ -0,0 +1,245 @@
/// Image support for genpdf-rs.

use std::path;
use image::GenericImageView;

use crate::{Alignment, Context, Element, Mm, Position, RenderResult, Rotation, Scale, Size};
use crate::error::Error;
use crate::{render, style};

/// An Image to embed in the PDF.
///
/// This struct is merely a wrapper around the configurations [`printpdf`][] exposes.
///
/// # Example:
/// ```rust
/// use std::convert::TryFrom;
/// use genpdf::{elements, Scale};
/// let image = elements::Image::from_path("examples/images/test_image.jpg")
///       .expect("Image loaded from test image.")
///       .with_alignment(elements::Alignment::Center) // Center the image on the page.
///       .with_scale(genpdf::Scale::new(0.5, 2)); // Squeeze and then stretch upwards.
/// ```
#[derive(Clone)]
pub struct Image {
    data: image::DynamicImage,

    /// Used for positioning if no absolute position is given.
    alignment: Alignment,

    /// The absolute position within the given area.
    ///
    /// If no position is set, we use the Alignment.
    position: Option<Position>,

    /// Scaling of the image, default is 1:1.
    scale: Scale,

    /// The number of degrees of clockwise rotation.
    rotation: Rotation,

    /// DPI override if you know better. Defaults to let `printpdf` define it.
    dpi: Option<f64>,
}

impl Image {
    /// If somehow you have a dynamic image directly from the image package.
    pub fn from_dynamic_image(data: image::DynamicImage) -> Self {
        Image {
            data,
            alignment: Alignment::default(),
            position: None,
            scale: Scale::default(),
            rotation: Rotation::default(),
            dpi: None,
        }
    }

    /// If you have a reader, we can pass that to the image library to pull it in.
    pub fn from_reader<R>(reader: R) -> Result<Self, Error>
        where
            R: std::io::BufRead,
            R: std::io::Read,
            R: std::io::Seek,
    {
        let data = image::io::Reader::new(reader)
            .with_guessed_format()
            .map_err(|e| Error::new("Unable to determine image format.", e))?
            .decode()
            .map_err(|e| Error::new("Unable to decode image.", e))?;
        Ok(Image::from_dynamic_image(data))
    }

    /// Try to convert a path into an Image. Unable to use TryFrom since Path isn't Sized.
    pub fn from_path(path: impl AsRef<path::Path>) -> Result<Self, Error> {
        // currently, the only reliable file formats are bmp/jpeg/png
        // this is an issue of the image library, not a fault of printpdf
        image::io::Reader::open(path)
            .map_err(|e| Error::new("Unable to open path.", e))?
            .with_guessed_format()
            .map_err(|e| Error::new("Unable to determine image format.", e))?
            .decode()
            .map_err(|e| Error::new("Unable to decode image.", e))
            .map(Image::from_dynamic_image)
    }

    /// Translate the image over to position.
    pub fn set_position(&mut self, position: impl Into<Position>) {
        self.position = Some(position.into());
    }

    /// Translate the image over to position and return the image for chaining.
    pub fn with_position(mut self, position: impl Into<Position>) -> Self {
        self.set_position(position);
        self
    }

    /// Scale the image.
    pub fn set_scale(&mut self, scale: impl Into<Scale>) {
        self.scale = scale.into();
    }

    /// Scale the image and return the image for chaining.
    pub fn with_scale(mut self, scale: impl Into<Scale>) -> Self {
        self.set_scale(scale);
        self
    }

    /// Calculate positioning based on the image and document size.
    pub fn set_alignment(&mut self, alignment: impl Into<Alignment>) {
        self.alignment = alignment.into();
    }

    /// Set the alignment to use and return the image for chaining.
    pub fn with_alignment(mut self, alignment: impl Into<Alignment>) -> Self {
        self.set_alignment(alignment);
        self
    }

    /// Determine the offset from left-side based on provided Alignment.
    fn get_offset(&self, width: Mm, max_width: Mm) -> Position {
        let horizontal_offset: Mm = match self.alignment {
            Alignment::Left => Mm::default(),
            Alignment::Center => (max_width - width) / 2.0,
            Alignment::Right => max_width - width,
        };
        Position::new(horizontal_offset, 0)
    }

    /// Calculates a guess for the size of the image based on the dpi/pixel-count/scale.
    fn get_size(&self) -> Size {
        let mmpi: f64 = 25.4; // millimeters per inch
        // Assume 300 DPI to be consistent with printpdf.
        let dpi: f64 = self.dpi.unwrap_or(300.0);
        let (px_width, px_height) = self.data.dimensions();
        let (scale_width, scale_height): (f64, f64) = (self.scale.x, self.scale.y);
        Size::new(
            mmpi * ((scale_width * px_width as f64) / dpi),
            mmpi * ((scale_height * px_height as f64) / dpi)
        )
    }

    /// Set the clockwise rotation of the image around the bottom left corner.
    pub fn set_clockwise_rotation(&mut self, rotation: impl Into<Rotation>) {
        self.rotation = rotation.into();
    }

    /// Set the clockwise rotation of the image around the bottom left corner;
    /// and then return it for chaining.
    pub fn with_clockwise_rotation(mut self, rotation: impl Into<Rotation>) -> Self {
        self.set_clockwise_rotation(rotation);
        self
    }

    /// Set the expected DPI of the encoded Image.
    pub fn set_dpi(&mut self, dpi: f64) {
        self.dpi = Some(dpi);
    }

    /// Set the expected DPI of the encoded Image and then return for chaining.
    pub fn with_dpi(mut self, dpi: f64) -> Self {
        self.set_dpi(dpi);
        self
    }
}

impl Element for Image {
    fn render(
        &mut self,
        _context: &Context,
        area: render::Area<'_>,
        _style: style::Style
    ) -> Result<RenderResult, Error> {
        let mut result = RenderResult::default();
        let true_size = self.get_size();
        let (bb_origin, bb_size) = bounding_box_offset_and_size(&self.rotation, &true_size);

        let mut position: Position = if let Some(position) = self.position {
            position
        } else {
            // Update the result size to be based on the bounding-box size/offset.
            result.size = bb_size;

            // No position override given; so we calculate the Alignment offset based on
            // the area-size and width of the bounding box.
            self.get_offset(bb_size.width, area.size().width)
        };

        // Fix the position with the bounding-box's origin which was changed from
        // (0,0) when it was rotated in any way.
        position += bb_origin;

        // Insert/Render the image with the overridden/calculated position.
        area.add_image(&self.data, position, self.scale, self.rotation, self.dpi);

        // Always false as we can't safely do this unless we want to try to do "sub-images".
        // This is technically possible with the `image` package, but it is potentially more
        // work than necessary. I'd rather support an "Auto-Scale" method to fit to area.
        result.has_more = false;

        Ok(result)
    }
}

/// Given the Size of a box (width/height), compute the bounding-box size when
/// rotated some degrees and where the "minimum" corner is (which should be the
/// new origin/offset). Note, this is not very optimized.
fn bounding_box_offset_and_size(rotation: &Rotation, size: &Size) -> (Position, Size) {
    let theta = rotation.degrees.to_radians();
    let (ct, st) = (theta.cos(), theta.sin());
    let (w, h): (f64, f64) = (size.width.into(), size.height.into());
    match rotation.degrees {
        d if d >    0.0 && d <=    90.0 => {
            let alpha = 180.0 - (rotation.degrees + 90.0);
            let ca = alpha.to_radians().cos();
            let (hct, wct) = (h * ct, w * ct);
            let (hst, wst) = (h * st, w * st);
            let (bb_w, bb_h) = (hst + wct, wst + hct);
            (Position::new(h * ca, bb_h), Size::new(bb_w, bb_h))
        },
        d if d >  90.0 && d <=  180.0 => {
            let alpha = (rotation.degrees - 90.0).to_radians();
            let (ca, sa) = (alpha.cos(), alpha.sin());
            let (bb_w, bb_h) = (w*sa + h*ca, w*ca + h*sa);
            (Position::new(bb_w, w*ca), Size::new(bb_w, bb_h))
        },
        d if d <   0.0 && d >   -90.0 => {
            let (hct, wct) = (h * ct, w * ct);
            let (hst, wst) = (h * st, w * st);
            let (bb_w, bb_h) = (hst + wct, hct + wst);
            (Position::new(0, hct), Size::new(bb_w, bb_h))
        },
        d if d <= -90.0 && d >= -180.0 => {
            let alpha = (180.0 + rotation.degrees).to_radians();
            let (ca, sa) = (alpha.cos(), alpha.sin());
            let (bb_w, bb_h) = (h*sa + w*ca, h*ca + w*sa);
            (Position::new(w*ca, 0), Size::new(bb_w, bb_h))
        },
        _ =>
        // This section is only for degrees == 0.0, but I use the default match due to:
        //       https://github.com/rust-lang/rust/issues/41620
        // Rotation's degrees should be restricted to [-180,180] so these
        // ranges should be complete.
            (Position::new(0, h), size.clone()),
    }
}
diff --git a/src/elements.rs b/src/elements/mod.rs
similarity index 98%
rename from src/elements.rs
rename to src/elements/mod.rs
index ec1371d..be61657 100644
--- a/src/elements.rs
+++ b/src/elements/mod.rs
@@ -46,7 +46,13 @@ use crate::error::{Error, ErrorKind};
use crate::render;
use crate::style::{Style, StyledString};
use crate::wrap;
use crate::{Context, Element, Margins, Mm, Position, RenderResult, Size};
use crate::{Alignment, Context, Element, Margins, Mm, Position, RenderResult, Size};

// Import images and re-export from elements.
#[cfg(feature = "images")]
mod images;
#[cfg(feature = "images")]
pub use images::*;

/// Arranges a list of elements sequentially.
///
@@ -179,27 +185,6 @@ impl Element for Text {
    }
}

/// The alignment of a [`Paragraph`][].
///
/// The default alignment is left-flushed.
///
/// [`Paragraph`]: struct.Paragraph.html
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Alignment {
    /// Left-flushed.
    Left,
    /// Right-flushed.
    Right,
    /// Centered.
    Center,
}

impl Default for Alignment {
    fn default() -> Alignment {
        Alignment::Left
    }
}

/// A multi-line wrapped paragraph of formatted text.
///
/// If the text of this paragraph is longer than the page width, the paragraph is wrapped at word
diff --git a/src/error.rs b/src/error.rs
index 7005d82..4280fd8 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -85,6 +85,9 @@ impl error::Error for Error {
            ErrorKind::PdfError(err) => Some(err),
            ErrorKind::PdfIndexError(err) => Some(err),
            ErrorKind::RusttypeError(err) => Some(err),

            #[cfg(feature = "images",)]
            ErrorKind::ImageError(err) => Some(err),
        }
    }
}
@@ -111,6 +114,10 @@ pub enum ErrorKind {
    PdfIndexError(printpdf::IndexError),
    /// An error caused by `rusttype`.
    RusttypeError(rusttype::Error),

    /// An error caused by an image.
    #[cfg(feature = "images",)]
    ImageError(image::ImageError),
}

impl From<io::Error> for ErrorKind {
@@ -147,3 +154,10 @@ impl From<rusttype::Error> for ErrorKind {
        ErrorKind::RusttypeError(error)
    }
}

#[cfg(feature = "images",)]
impl From<image::ImageError> for ErrorKind {
    fn from(error: image::ImageError) -> ErrorKind {
        ErrorKind::ImageError(error)
    }
}
diff --git a/src/lib.rs b/src/lib.rs
index 39b23bd..e29e9d6 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -276,6 +276,28 @@ impl From<Mm> for printpdf::Pt {
    }
}

/// The alignment of a [`Paragraph`][] or ['Image'][].
///
/// The default alignment is left-flushed.
///
/// [`Paragraph`]: struct.Paragraph.html
/// [`Image`]: struct.Image.html
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Alignment {
    /// Left-flushed.
    Left,
    /// Right-flushed.
    Right,
    /// Centered.
    Center,
}

impl Default for Alignment {
    fn default() -> Alignment {
        Alignment::Left
    }
}

/// A position on a PDF layer, measured in millimeters.
///
/// All positions used by `genpdf` are measured from the top left corner of the reference area.
@@ -309,6 +331,73 @@ impl<X: Into<Mm>, Y: Into<Mm>> From<(X, Y)> for Position {
    }
}

/// A rotation in degrees clock-wise in range [-180.0, 180.0] inclusive.
#[derive(Clone, Copy, Default, Debug, PartialEq, PartialOrd, Add, AddAssign, Sub, SubAssign)]
pub struct Rotation {
    degrees: f64,
}

impl Rotation {
    /// Create a new rotation with the given number of degrees.
    pub fn from_degrees(rotation: f64) -> Self {
        let degrees: f64 = match rotation % 360.0 {
            deg if deg > 180.0 => 360.0 - deg,
            deg if deg < -180.0 => 360.0 + deg,
            deg => deg,
        };
        Rotation { degrees }
    }
}

impl From<f64> for Rotation {
    fn from(degrees: f64) -> Rotation {
        // Perhaps a poor assumption that we'll always work with degrees?
        Rotation::from_degrees(degrees)
    }
}

impl From<Rotation> for Option<f64> {
    fn from(rotation: Rotation) -> Option<f64> {
        if rotation.degrees != 0.0 {
            Some(rotation.degrees)
        } else {
            None
        }
    }
}

/// A size to stretch an image on a PDF layer; measured in percentage.
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Add, AddAssign, Sub, SubAssign)]
pub struct Scale {
    /// The percentage to scale on the x-axis.
    pub x: f64,
    /// The percentage to scale on the y-axis.
    pub y: f64,
}

// Overriding default of (0,0) as that would scale it to 0.
impl Default for Scale {
    fn default() -> Scale {
        Scale::new(1,1)
    }
}

impl Scale {
    /// Creates a new scale for the given x/y values.
    pub fn new(x: impl Into<f64>, y: impl Into<f64>) -> Scale {
        Scale {
            x: x.into(),
            y: y.into(),
        }
    }
}

impl<X: Into<f64>, Y: Into<f64>> From<(X, Y)> for Scale {
    fn from(values: (X, Y)) -> Scale {
        Scale::new(values.0, values.1)
    }
}

/// A size of an area on a PDF layer, measured in millimeters.
#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd, Add, AddAssign, Sub, SubAssign)]
pub struct Size {
diff --git a/src/render.rs b/src/render.rs
index 74455dc..129d468 100644
--- a/src/render.rs
+++ b/src/render.rs
@@ -25,6 +25,9 @@ use crate::fonts;
use crate::style::{Color, Style};
use crate::{Margins, Mm, Position, Size};

#[cfg(feature = "images",)]
use crate::{Rotation, Scale};

/// Renders a PDF document with one or more pages.
///
/// This is a wrapper around a [`printpdf::PdfDocumentReference`][].
@@ -297,6 +300,35 @@ impl<'a> Area<'a> {
        areas
    }

    /// Inserts an image into the document.
    ///
    /// The position is assumed to be relative to the upper left hand corner of the area.
    /// Your position will need to compensate for rotation/scale/dpi. Using the Image's
    /// render functionality will will do this for you and is the recommended way to
    /// insert an image into an Area.
    #[cfg(feature = "images",)]
    pub fn add_image(
        &self,
        image: &image::DynamicImage,
        position: Position,
        scale: Scale,
        rotation: Rotation,
        dpi: Option<f64>
    ) {
        let dynamic_image = printpdf::Image::from_dynamic_image(image);
        let real_position = self.transform_position(position);
        let layer = self.layer().clone();
        dynamic_image.add_to_layer(
            layer,
            Some(real_position.x.into()),
            Some(real_position.y.into()),
            rotation.into(),
            Some(scale.x),
            Some(scale.y),
            dpi,
        );
    }

    /// Draws a line with the given points and the given style.
    ///
    /// Currently, this method only uses the color of the given style as the outline color (if set).
-- 
2.25.1
builds.sr.ht
genpdf-rs/patches: FAILED in 46s

[Adds image support.][0] v2 from [Alexander Dean-Kennedy][1]

[0]: https://lists.sr.ht/~ireas/public-inbox/patches/15888
[1]: mailto:dstar@slackless.com

✗ #363958 FAILED genpdf-rs/patches/archlinux-msrv.yml https://builds.sr.ht/~ireas/job/363958
✗ #363959 FAILED genpdf-rs/patches/archlinux.yml      https://builds.sr.ht/~ireas/job/363959
Hi everyone,

apologies for the long delay.  I’ve finally finished reviewing the patch
and applied it with some modifications.  I’ve released v0.2.0-alpha.0
for testing.

The modifications mostly affect formatting and documentation.  I think I
also fixed a bug in Rotation::from_degrees for the case where degrees >
180.  Please let me know if you agree that the current implementation
and tests are correct.

You can find all modifications here:
	https://git.sr.ht/~ireas/genpdf-rs/commit/027d31a607044ad9d2174d7231ea7d04d2e4180f	

Before releasing v0.2.0, I’d like to have a closer look at the
bounding_box_offset_and_size function in src/elements/images.rs:
- I think it would be easier to read if this was an if statement instead
  of ifs inside a match.  Maybe we can even use Range*::contains.
- We should have some tests.
- I’d like to add some comments explaining the idea behind the
  calculations.

Thank you so much for the patch and for your patience!

Robin