Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ xmlwriter = "0.1.0"
poppler-rs = { git = "https://github.com/Doublonmousse/poppler-patch", features = [
"v20_9",
], rev = "3acaa4e28d45a05939a71116b0d5ebd4b1c99b98" }
# inkml parser/writer
writer_inkml = {git = "https://github.com/Doublonmousse/writer_reader_inkml", rev = "9370cf6a65ce566d91c232ca47d2cf08008e605e"}

[patch.crates-io]

Expand Down
1 change: 1 addition & 0 deletions crates/rnote-engine/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ usvg = { workspace = true }
xmlwriter = { workspace = true }
# the long-term plan is to remove the gtk4 dependency entirely after switching to another renderer.
gtk4 = { workspace = true, optional = true }
writer_inkml = {workspace = true}

[dev-dependencies]
approx = { workspace = true }
Expand Down
11 changes: 11 additions & 0 deletions crates/rnote-engine/src/engine/strokecontent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,4 +174,15 @@ impl StrokeContent {

Ok(())
}

pub fn to_inkml(&self, current_dpi: f64) -> anyhow::Result<Vec<u8>> {
writer_inkml::writer(
self.strokes
.iter()
.map(|stroke| stroke.into_inkml(current_dpi))
.filter(|x| x.is_some())
.map(|x| x.unwrap())
.collect(),
)
}
}
51 changes: 51 additions & 0 deletions crates/rnote-engine/src/fileformats/inkmlformat.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use crate::strokes::BrushStroke;
use crate::strokes::Stroke;
use rnote_compose::Color;
use rnote_compose::PenPath;
use rnote_compose::penpath::Element;
use rnote_compose::style::PressureCurve;
use rnote_compose::style::smooth::SmoothOptions;
use std::sync::Arc;
use writer_inkml::{Brush, FormattedStroke};

pub fn inkml_to_stroke(
formatted_stroke: FormattedStroke,
brush: Brush,
dpi: &f64,
) -> Option<Arc<Stroke>> {
let mut smooth_options = SmoothOptions::default();
smooth_options.stroke_color = Some(Color::new(
brush.color.0 as f64 / 255.0,
brush.color.1 as f64 / 255.0,
brush.color.2 as f64 / 255.0,
1.0 - brush.transparency as f64 / 255.0,
));

// converting from cm to px
smooth_options.stroke_width = dpi * brush.stroke_width_cm / 2.54;

// pressure curve
if brush.ignorepressure {
smooth_options.pressure_curve = PressureCurve::Const;
} else {
smooth_options.pressure_curve = PressureCurve::Linear;
}

let penpath = PenPath::try_from_elements(
formatted_stroke
.x
.into_iter()
.zip(formatted_stroke.y)
.zip(formatted_stroke.f)
.map(|((x, y), f)| Element::new(*dpi * na::vector![x, y] / 2.54, f)),
);
if penpath.is_some() {
let new_stroke = BrushStroke::from_penpath(
penpath.unwrap(),
rnote_compose::Style::Smooth(smooth_options),
);
Some(Arc::new(Stroke::BrushStroke(new_stroke)))
} else {
None
}
}
1 change: 1 addition & 0 deletions crates/rnote-engine/src/fileformats/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Modules
pub mod inkmlformat;
pub mod rnoteformat;
pub mod xoppformat;

Expand Down
24 changes: 24 additions & 0 deletions crates/rnote-engine/src/pens/selector/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::snap::SnapCorner;
use crate::store::StrokeKey;
use crate::strokes::Content;
use crate::{Camera, DrawableOnDoc, Engine, WidgetFlags};
use core::str;
use futures::channel::oneshot;
use kurbo::Shape;
use p2d::bounding_volume::{Aabb, BoundingSphere, BoundingVolume};
Expand Down Expand Up @@ -171,6 +172,8 @@ impl PenBehaviour for Selector {
None
};

let dpi = engine_view.document.config.format.dpi();

rayon::spawn(move || {
let result = move || {
if let Some(stroke_content) = stroke_content {
Expand All @@ -186,6 +189,27 @@ impl PenBehaviour for Selector {
serde_json::to_string(&stroke_content)?.into_bytes(),
StrokeContent::MIME_TYPE.to_string(),
));

// add inkml content
let inkml_contents = stroke_content.to_inkml(dpi);
match inkml_contents {
Ok(inkml_bytes) => {
tracing::debug!(
"generated inkml : {:?}",
str::from_utf8(&inkml_bytes)
);
clipboard_content.push((
inkml_bytes.clone(),
"application/x.windows.InkML Format".to_string(),
));
clipboard_content
.push((inkml_bytes, "application/inkml+xml".to_string()));
}
Err(e) => error!(
"Could not convert strokes to inkml to add to the clipboard, {e}"
),
}

if let Some(stroke_content_svg) = stroke_content_svg {
// Add generated Svg
clipboard_content.push((
Expand Down
93 changes: 93 additions & 0 deletions crates/rnote-engine/src/strokes/stroke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use p2d::bounding_volume::Aabb;
use rnote_compose::ext::AabbExt;
use rnote_compose::penpath::Element;
use rnote_compose::shapes::{Rectangle, Shapeable};
use rnote_compose::style::PressureCurve;
use rnote_compose::style::smooth::SmoothOptions;
use rnote_compose::transform::Transform;
use rnote_compose::transform::Transformable;
Expand Down Expand Up @@ -672,4 +673,96 @@ impl Stroke {
}
}
}

/// converts a stroke into the input format used by the inkml writer
pub fn into_inkml(
&self,
current_dpi: f64,
) -> Option<(writer_inkml::FormattedStroke, writer_inkml::Brush)> {
let pixel_to_cm_factor = 2.54 / current_dpi;
match self {
Stroke::BrushStroke(brushstroke) => {
// remark : style is not preserved here, we will always get a smooth
// version
let fill_color = brushstroke.style.stroke_color().unwrap_or_default();
let elements = brushstroke.path.clone().into_elements();
let ignore_pressure = match &brushstroke.style {
Style::Smooth(smooth_options) => match smooth_options.pressure_curve {
PressureCurve::Const => true,
_ => false,
},
Style::Rough(_) => false,
Style::Textured(_) => false,
};
tracing::debug!("formatting strokes");
Some((
writer_inkml::FormattedStroke {
x: elements
.iter()
.map(|element| pixel_to_cm_factor * element.pos.x)
.collect(), // need the scale !
y: elements
.iter()
.map(|element| pixel_to_cm_factor * element.pos.y)
.collect(),
f: elements.iter().map(|element| element.pressure).collect(),
},
writer_inkml::Brush::init(
String::from(""),
(
(fill_color.r * 255.0) as u8,
(fill_color.g * 255.0) as u8,
(fill_color.b * 255.0) as u8,
),
ignore_pressure,
((1.0 - fill_color.a) * 255.0) as u8,
brushstroke.style.stroke_width() * pixel_to_cm_factor,
),
))
}
Stroke::ShapeStroke(ShapeStroke { shape, style, .. }) => {
// partial support for shapes
// everything with no fill
let stroke_color = style.stroke_color().unwrap_or_default();
let brush = writer_inkml::Brush::init(
String::from(""),
(
(stroke_color.r * 255.0) as u8,
(stroke_color.g * 255.0) as u8,
(stroke_color.b * 255.0) as u8,
),
true,
((1.0 - stroke_color.a) * 255.0) as u8,
style.stroke_width() * pixel_to_cm_factor,
);
let mut out_elements: Vec<(f64, f64)> = vec![];
kurbo::flatten(shape.outline_path(), 0.25, |path_el| match path_el {
kurbo::PathEl::MoveTo(pt) => out_elements.push((pt.x, pt.y)),
// technically, a moveto should create a new brush
kurbo::PathEl::LineTo(pt) => out_elements.push((pt.x, pt.y)),
kurbo::PathEl::ClosePath => {
out_elements.push(out_elements[0]);
}
_ => {}
});
// we return ONE stroke at most for now
// only affect arrows for now (though they still render fine ?)
let formatted_stroke = writer_inkml::FormattedStroke {
x: out_elements
.iter()
.map(|element| pixel_to_cm_factor * element.0)
.collect(), // need the scale !
y: out_elements
.iter()
.map(|element| pixel_to_cm_factor * element.1)
.collect(),
f: out_elements.iter().map(|_| 1.0).collect(),
};
Some((formatted_stroke, brush))
}
Stroke::TextStroke(_) => None,
Stroke::VectorImage(_) => None,
Stroke::BitmapImage(_) => None,
}
}
}
1 change: 1 addition & 0 deletions crates/rnote-ui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ tracing = { workspace = true }
tracing-subscriber = { workspace = true }
unicode-segmentation = { workspace = true }
url = { workspace = true }
writer_inkml = { workspace = true}

[build-dependencies]
anyhow = { workspace = true }
Expand Down
Loading