~ireas/public-inbox

genpdf-rs: LineStyle implemented for FramedElement and FrameCellDecorator v1 APPLIED

ap4sc: 1
 LineStyle implemented for FramedElement and FrameCellDecorator

 3 files changed, 130 insertions(+), 38 deletions(-)
#527941 archlinux-msrv.yml success
#527942 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/23378/mbox | git am -3
Learn more about email & git
View this thread in the archives

[PATCH genpdf-rs] LineStyle implemented for FramedElement and FrameCellDecorator Export this patch

For LineStyle add From<Color>, Mm instead of f64 thickness, default to black and 1.0 thickness

Implement LineStyle for FrameCellDecorator

Add documentation for LineStyle

Remove rounded rect code comments

Accept impl into line style on FramedElement and Table
---
 src/elements.rs | 76 +++++++++++++++++++++++++++++++------------------
 src/render.rs   | 20 ++++++-------
 src/style.rs    | 72 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 130 insertions(+), 38 deletions(-)

diff --git a/src/elements.rs b/src/elements.rs
index 1ff2dea..00a8a77 100644
--- a/src/elements.rs
+++ b/src/elements.rs
@@ -49,7 +49,7 @@ use std::mem;

use crate::error::{Error, ErrorKind};
use crate::render;
use crate::style::{Style, StyledString};
use crate::style::{Style, StyledString, LineStyle};
use crate::wrap;
use crate::{Alignment, Context, Element, Margins, Mm, Position, RenderResult, Size};

@@ -595,14 +595,22 @@ impl<E: Element> Element for StyledElement<E> {
pub struct FramedElement<E: Element> {
    element: E,
    is_first: bool,
    line_style: LineStyle,
}

impl<E: Element> FramedElement<E> {
    /// Creates a new framed element that wraps the given element.
    pub fn new(element: E) -> FramedElement<E> {
        FramedElement {
            element,
        FramedElement::with_line_style(element, LineStyle::new())
    }

    /// Creates a new framed element that wraps the given element,
    /// and with the given line style.
    pub fn with_line_style(element: E, line_style: impl Into<LineStyle>) -> FramedElement<E> {
        Self {
            is_first: true,
            element,
            line_style: line_style.into(),
        }
    }
}
@@ -615,30 +623,32 @@ impl<E: Element> Element for FramedElement<E> {
        style: Style,
    ) -> Result<RenderResult, Error> {
        let result = self.element.render(context, area.clone(), style)?;
        area.draw_line(
            vec![Position::default(), Position::new(0, result.size.height)],
            style,
        );
        area.draw_line(
            vec![
                Position::new(area.size().width, 0),
                Position::new(area.size().width, result.size.height),
            ],
            style,
        );
        
        if self.is_first {
            area.draw_line(
                vec![Position::default(), Position::new(area.size().width, 0)],
                style,
                vec![Position::new(area.size().width, result.size.height),
                Position::new(area.size().width, 0),
                Position::default(), 
                Position::new(0, result.size.height)],
                self.line_style,
            );
        }
        if !result.has_more {
            area.draw_line(
                vec![
                    Position::new(0, result.size.height),
                    Position::new(area.size().width, result.size.height),
                ],
                style,
                vec![Position::default(), 
                Position::new(0, result.size.height),
                Position::new(area.size().width, result.size.height),
                Position::new(area.size().width, 0)],
                self.line_style,
            );
        } else {
            area.draw_line(
                vec![Position::default(), Position::new(0, result.size.height)],
                self.line_style,
            );
            area.draw_line(
                vec![Position::new(area.size().width, 0), Position::new(area.size().width, result.size.height)],
                self.line_style,
            );
        }
        self.is_first = false;
@@ -909,7 +919,6 @@ pub trait CellDecorator {
        row: usize,
        has_more: bool,
        area: render::Area<'_>,
        style: Style,
    );
}

@@ -925,6 +934,7 @@ pub struct FrameCellDecorator {
    inner: bool,
    outer: bool,
    cont: bool,
    line_style: LineStyle,
    num_columns: usize,
    num_rows: usize,
    last_row: Option<usize>,
@@ -942,6 +952,17 @@ impl FrameCellDecorator {
        }
    }

    /// Creates a new frame cell decorator with the given border settings, as well as a line style.
    pub fn with_line_style(inner: bool, outer: bool, cont: bool, line_style: impl Into<LineStyle>) -> FrameCellDecorator {
        Self {
            inner,
            outer,
            cont,
            line_style: line_style.into(),
            ..Default::default()
        }
    }

    fn print_left(&self, column: usize) -> bool {
        if column == 0 {
            self.outer
@@ -993,14 +1014,13 @@ impl CellDecorator for FrameCellDecorator {
        row: usize,
        has_more: bool,
        area: render::Area<'_>,
        style: Style,
    ) {
        let size = area.size();

        if self.print_left(column) {
            area.draw_line(
                vec![Position::default(), Position::new(0, size.height)],
                style,
                self.line_style,
            );
        }

@@ -1010,14 +1030,14 @@ impl CellDecorator for FrameCellDecorator {
                    Position::new(size.width, 0),
                    Position::new(size.width, size.height),
                ],
                style,
                self.line_style,
            );
        }

        if self.print_top(row) {
            area.draw_line(
                vec![Position::default(), Position::new(size.width, 0)],
                style,
                self.line_style,
            );
        }

@@ -1027,7 +1047,7 @@ impl CellDecorator for FrameCellDecorator {
                    Position::new(0, size.height),
                    Position::new(size.width, size.height),
                ],
                style,
                self.line_style,
            );
        }

@@ -1213,7 +1233,7 @@ impl TableLayout {
        if let Some(decorator) = &mut self.cell_decorator {
            for (i, mut area) in areas.into_iter().enumerate() {
                area.set_height(row_height);
                decorator.decorate_cell(i, self.render_idx, result.has_more, area, style);
                decorator.decorate_cell(i, self.render_idx, result.has_more, area);
            }
        }

diff --git a/src/render.rs b/src/render.rs
index 5ecce1e..cdea762 100644
--- a/src/render.rs
+++ b/src/render.rs
@@ -22,7 +22,7 @@ use std::io;

use crate::error::{Context as _, Error, ErrorKind};
use crate::fonts;
use crate::style::{Color, Style};
use crate::style::{Color, Style, LineStyle};
use crate::{Margins, Mm, Position, Size};

#[cfg(feature = "images")]
@@ -333,11 +333,10 @@ impl<'a> Area<'a> {
        );
    }

    /// Draws a line with the given points and the given style.
    /// Draws a line with the given points and the given line style.
    ///
    /// Currently, this method only uses the color of the given style as the outline color (if set).
    /// The points are relative to the upper left corner of the area.
    pub fn draw_line(&self, points: Vec<Position>, style: Style) {
    pub fn draw_line(&self, points: Vec<Position>, line_style: LineStyle) {
        let line_points: Vec<_> = points
            .into_iter()
            .map(|pos| (self.transform_position(pos).into(), false))
@@ -349,13 +348,14 @@ impl<'a> Area<'a> {
            has_stroke: true,
            is_clipping_path: false,
        };
        if let Some(color) = style.color() {
            self.layer().set_outline_color(color.into());
        }
        self.layer().set_outline_thickness(line_style.thickness().into());
        self.layer().set_outline_color(line_style.color().into());

        self.layer().add_shape(line);
        if style.color().is_some() {
            self.layer().set_outline_color(Color::Rgb(0, 0, 0).into());
        }

        let default_line_style = LineStyle::default();
        self.layer().set_outline_thickness(printpdf::Pt::from(line_style.thickness()).0);
        self.layer().set_outline_color(default_line_style.color().into());
    }

    /// Tries to draw the given string at the given position and returns `true` if the area was
diff --git a/src/style.rs b/src/style.rs
index e77258a..d7615b4 100644
--- a/src/style.rs
+++ b/src/style.rs
@@ -528,3 +528,75 @@ impl<'s> From<StyledString> for StyledCow<'s> {
        StyledCow::new(s.s, s.style)
    }
}

/// A style for a line, used in styling borders and shapes.
///
/// Fields:
/// - the line thickness in millimeters, (defaults to 1.0)
/// - the color of the line, see [`Color`][] (defaults to black)
///
/// [`Color`]: enum.Color.html
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct LineStyle {    
    thickness: Mm, // NOTE: 0.0 is a special value, it does not make the line disappear, but rather makes it appear 1px wide across all devices
    color: Color,
}

impl Default for LineStyle { 
    fn default() -> LineStyle {
        LineStyle {
            thickness: Mm::from(1.0),
            color: Color::Rgb(0, 0, 0),
        }
    }
}

impl From<Color> for LineStyle {
    fn from(color: Color) -> LineStyle {
        LineStyle {
            color,
            ..LineStyle::default()
        }
    }
}

impl LineStyle {
    /// Creates a new line style with default values.
    pub fn new() -> LineStyle {
        LineStyle::default()
    }

    /// Sets the line thickness. Setting this to 0.0 will not hide the line,
    /// rather it's a special value that tells PDF viewers to render the line
    /// as 1px regardless of the display size and zoom.
    pub fn set_thickness(&mut self, thickness: Mm) {
        self.thickness = thickness;
    }

    /// Sets the line thickness and returns the line style.
    pub fn with_thickness(mut self, thickness: Mm) -> Self {
        self.set_thickness(thickness);
        self
    }

    /// Returns the line thickness if set.
    pub fn thickness(&self) -> Mm {
        self.thickness
    }

    /// Sets the line color.
    pub fn set_color(&mut self, color: Color) {
        self.color = color;
    }

    /// Sets the line color and returns the line style.
    pub fn with_color(mut self, color: Color) -> Self {
        self.set_color(color);
        self
    }

    /// Returns the line color if set.
    pub fn color(&self) -> Color {
        self.color
    }
}
\ No newline at end of file
-- 
2.28.0
builds.sr.ht
genpdf-rs/patches: FAILED in 7m14s

[LineStyle implemented for FramedElement and FrameCellDecorator][0] from [ap4sc][1]

[0]: https://lists.sr.ht/~ireas/public-inbox/patches/23378
[1]: mailto:ap4sc4@gmail.com

✗ #527942 FAILED  genpdf-rs/patches/archlinux.yml      https://builds.sr.ht/~ireas/job/527942
✓ #527941 SUCCESS genpdf-rs/patches/archlinux-msrv.yml https://builds.sr.ht/~ireas/job/527941
Thanks for the patch!  Nevermind the build failure, I’ll fix that while
reviewing.
Thank you again for the patch!  I’ve made some small changes and applied
it to the master branch.

The build failure was due to some formatting issues - please run cargo
fmt before submitting a patch.

/Robin