mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-19 02:48:12 +08:00
Export current document as SVG when pressing Ctrl+Shift+S (#160)
* Export current document when pressing Ctrl+Shift+S * Use a blob for download * Add Ctrl + E shortcut, match on lower case * Don't mount element in DOM * Polish some keybindings * Add initialization for MappingEntries * Implement svg coloring * Add newline after svg tag * Add spaces to svg style format * Fix more svg formatting * Add space before /> * Remove duplicate whitespace
This commit is contained in:
@@ -55,7 +55,7 @@ impl Color {
|
||||
pub fn components(&self) -> (f32, f32, f32, f32) {
|
||||
(self.red, self.green, self.blue, self.alpha)
|
||||
}
|
||||
pub fn as_hex(&self) -> String {
|
||||
pub fn rgba_hex(&self) -> String {
|
||||
format!(
|
||||
"{:02X?}{:02X?}{:02X?}{:02X?}",
|
||||
(self.r() * 255.) as u8,
|
||||
@@ -64,4 +64,7 @@ impl Color {
|
||||
(self.a() * 255.) as u8,
|
||||
)
|
||||
}
|
||||
pub fn rgb_hex(&self) -> String {
|
||||
format!("{:02X?}{:02X?}{:02X?}", (self.r() * 255.) as u8, (self.g() * 255.) as u8, (self.b() * 255.) as u8,)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ impl LayerData for Circle {
|
||||
fn render(&mut self, svg: &mut String) {
|
||||
let _ = write!(
|
||||
svg,
|
||||
r#"<circle cx="{}" cy="{}" r="{}" {} />"#,
|
||||
r#"<circle cx="{}" cy="{}" r="{}"{} />"#,
|
||||
self.shape.center.x,
|
||||
self.shape.center.y,
|
||||
self.shape.radius,
|
||||
|
||||
@@ -25,7 +25,7 @@ impl LayerData for Ellipse {
|
||||
|
||||
let _ = write!(
|
||||
svg,
|
||||
r#"<ellipse cx="0" cy="0" rx="{}" ry="{}" transform="translate({} {}) rotate({})" {} />"#,
|
||||
r#"<ellipse cx="0" cy="0" rx="{}" ry="{}" transform="translate({} {}) rotate({})"{} />"#,
|
||||
rx,
|
||||
ry,
|
||||
cx,
|
||||
|
||||
@@ -23,6 +23,6 @@ impl LayerData for Line {
|
||||
let kurbo::Point { x: x1, y: y1 } = self.shape.p0;
|
||||
let kurbo::Point { x: x2, y: y2 } = self.shape.p1;
|
||||
|
||||
let _ = write!(svg, r#"<line x1="{}" y1="{}" x2="{}" y2="{}" {} />"#, x1, y1, x2, y2, self.style.render(),);
|
||||
let _ = write!(svg, r#"<line x1="{}" y1="{}" x2="{}" y2="{}"{} />"#, x1, y1, x2, y2, self.style.render(),);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,10 +24,13 @@ impl LayerData for PolyLine {
|
||||
return;
|
||||
}
|
||||
let _ = write!(svg, r#"<polyline points=""#);
|
||||
for p in &self.points {
|
||||
let _ = write!(svg, " {:.3} {:.3}", p.x, p.y);
|
||||
let mut points = self.points.iter();
|
||||
let first = points.next().unwrap();
|
||||
let _ = write!(svg, "{:.3} {:.3}", first.x, first.y);
|
||||
for point in points {
|
||||
let _ = write!(svg, " {:.3} {:.3}", point.x, point.y);
|
||||
}
|
||||
let _ = write!(svg, r#"" {}/>"#, self.style.render());
|
||||
let _ = write!(svg, r#""{} />"#, self.style.render());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,5 +44,5 @@ fn polyline_should_render() {
|
||||
|
||||
let mut svg = String::new();
|
||||
polyline.render(&mut svg);
|
||||
assert_eq!(r#"<polyline points=" 3.000 4.124 1.000 5.540" style="stroke: #00FF00FF;stroke-width:0.4;"/>"#, svg);
|
||||
assert_eq!(r##"<polyline points="3.000 4.124 1.000 5.540" stroke="#00FF00" stroke-width="0.4" />"##, svg);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ impl LayerData for Rect {
|
||||
fn render(&mut self, svg: &mut String) {
|
||||
let _ = write!(
|
||||
svg,
|
||||
r#"<rect x="{}" y="{}" width="{}" height="{}" {} />"#,
|
||||
r#"<rect x="{}" y="{}" width="{}" height="{}"{} />"#,
|
||||
self.shape.min_x(),
|
||||
self.shape.min_y(),
|
||||
self.shape.width(),
|
||||
|
||||
@@ -26,7 +26,7 @@ impl LayerData for Shape {
|
||||
fn render(&mut self, svg: &mut String) {
|
||||
let _ = write!(
|
||||
svg,
|
||||
r#"<polygon points="{}" transform="translate({} {}) scale({} {})" {} />"#,
|
||||
r#"<polygon points="{}" transform="translate({} {}) scale({} {})"{} />"#,
|
||||
self.shape,
|
||||
self.bounding_rect.origin().x,
|
||||
self.bounding_rect.origin().y,
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
use crate::color::Color;
|
||||
use serde::{Deserialize, Serialize};
|
||||
const OPACITY_PERCISION: usize = 3;
|
||||
|
||||
fn format_opacity(name: &str, opacity: f32) -> String {
|
||||
if (opacity - 1.).abs() > 10f32.powi(-(OPACITY_PERCISION as i32)) {
|
||||
format!(r#" {}-opacity="{:.percision$}""#, name, opacity, percision = OPACITY_PERCISION)
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize)]
|
||||
@@ -15,8 +24,8 @@ impl Fill {
|
||||
}
|
||||
pub fn render(&self) -> String {
|
||||
match self.color {
|
||||
Some(c) => format!("fill: #{};", c.as_hex()),
|
||||
None => "fill: none;".to_string(),
|
||||
Some(c) => format!(r##" fill="#{}"{}"##, c.rgb_hex(), format_opacity("fill", c.a())),
|
||||
None => r#" fill="none""#.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,7 +42,7 @@ impl Stroke {
|
||||
Self { color, width }
|
||||
}
|
||||
pub fn render(&self) -> String {
|
||||
format!("stroke: #{};stroke-width:{};", self.color.as_hex(), self.width)
|
||||
format!(r##" stroke="#{}"{} stroke-width="{}""##, self.color.rgb_hex(), format_opacity("stroke", self.color.a()), self.width)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +58,7 @@ impl PathStyle {
|
||||
}
|
||||
pub fn render(&self) -> String {
|
||||
format!(
|
||||
"style=\"{}{}\"",
|
||||
"{}{}",
|
||||
match self.fill {
|
||||
Some(fill) => fill.render(),
|
||||
None => String::new(),
|
||||
|
||||
@@ -39,6 +39,9 @@ impl ShapePoints {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: The display impl and iter impl share large amounts of code and should be refactored. (Display should use the Iterator)
|
||||
// TODO: Once that is done, the trailing space from the display impl should be removed
|
||||
// Also consider implementing index
|
||||
impl std::fmt::Display for ShapePoints {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fn rotate(v: &Vec2, theta: f64) -> Vec2 {
|
||||
|
||||
Reference in New Issue
Block a user