summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2022-09-03 09:03:13 +0300
committerLars Wirzenius <liw@liw.fi>2022-09-03 09:08:20 +0300
commitba31a2ad25fa0ae3f3fd49eaa5174f9520248b90 (patch)
tree72335bc6156243c300b387a025a3a3ad96603930 /src
parent404006dfb651687713b7b8aa3183ed5c2fbe8acb (diff)
downloadsubplot-ba31a2ad25fa0ae3f3fd49eaa5174f9520248b90.tar.gz
refactor: add a type for SVG images
This allows us to not use a generic byte vector, and slightly lessens the chance of mistakes. Also, it strongly encodes what we need to know about and do with SVG images. Sponsored-by: author
Diffstat (limited to 'src')
-rw-r--r--src/diagrams.rs40
-rw-r--r--src/lib.rs2
-rw-r--r--src/typeset.rs8
3 files changed, 38 insertions, 12 deletions
diff --git a/src/diagrams.rs b/src/diagrams.rs
index 6e0b875..a62553f 100644
--- a/src/diagrams.rs
+++ b/src/diagrams.rs
@@ -59,6 +59,32 @@ lazy_static! {
static ref JAVA_PATH: Mutex<PathBuf> = Mutex::new(env!("BUILTIN_JAVA_PATH").into());
}
+/// An SVG image.
+///
+/// SVG images are vector images, but we only need to treat them as
+/// opaque blobs of bytes, so we don't try to represent them in any
+/// other way.
+pub struct Svg {
+ data: Vec<u8>,
+}
+
+impl Svg {
+ fn new(data: Vec<u8>) -> Self {
+ Self { data }
+ }
+
+ /// Return slice of the bytes of the image.
+ pub fn data(&self) -> &[u8] {
+ &self.data
+ }
+
+ /// Number of bytes in the binary representation of the image.
+ #[allow(clippy::len_without_is_empty)] // is-empty doesn't make sense
+ pub fn len(&self) -> usize {
+ self.data.len()
+ }
+}
+
/// A code block with markup for a diagram.
///
/// The code block will be converted to an SVG image using an external
@@ -71,7 +97,7 @@ lazy_static! {
/// for the trait.
pub trait DiagramMarkup {
/// Convert the markup into an SVG.
- fn as_svg(&self) -> Result<Vec<u8>, SubplotError>;
+ fn as_svg(&self) -> Result<Svg, SubplotError>;
}
/// A code block with pikchr markup.
@@ -98,12 +124,12 @@ impl PikchrMarkup {
}
impl DiagramMarkup for PikchrMarkup {
- fn as_svg(&self) -> Result<Vec<u8>, SubplotError> {
+ fn as_svg(&self) -> Result<Svg, SubplotError> {
let mut flags = pikchr::PikchrFlags::default();
flags.generate_plain_errors();
let image = pikchr::Pikchr::render(&self.markup, self.class.as_deref(), flags)
.map_err(SubplotError::PikchrRenderError)?;
- Ok(image.as_bytes().to_vec())
+ Ok(Svg::new(image.as_bytes().to_vec()))
}
}
@@ -129,7 +155,7 @@ impl DotMarkup {
}
impl DiagramMarkup for DotMarkup {
- fn as_svg(&self) -> Result<Vec<u8>, SubplotError> {
+ fn as_svg(&self) -> Result<Svg, SubplotError> {
let path = DOT_PATH.lock().unwrap().clone();
let mut child = Command::new(&path)
.arg("-Tsvg")
@@ -146,7 +172,7 @@ impl DiagramMarkup for DotMarkup {
.wait_with_output()
.map_err(SubplotError::WaitForChild)?;
if output.status.success() {
- Ok(output.stdout)
+ Ok(Svg::new(output.stdout))
} else {
Err(SubplotError::child_failed("dot", &output))
}
@@ -196,7 +222,7 @@ impl PlantumlMarkup {
}
impl DiagramMarkup for PlantumlMarkup {
- fn as_svg(&self) -> Result<Vec<u8>, SubplotError> {
+ fn as_svg(&self) -> Result<Svg, SubplotError> {
let path = JAVA_PATH.lock().unwrap().clone();
let mut cmd = Command::new(&path);
cmd.arg("-Djava.awt.headless=true")
@@ -225,7 +251,7 @@ impl DiagramMarkup for PlantumlMarkup {
.wait_with_output()
.map_err(SubplotError::WaitForChild)?;
if output.status.success() {
- Ok(output.stdout)
+ Ok(Svg::new(output.stdout))
} else {
Err(SubplotError::child_failed("plantuml", &output))
}
diff --git a/src/lib.rs b/src/lib.rs
index 3b4e844..23076d5 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -21,7 +21,7 @@ pub use error::Warnings;
pub mod resource;
mod diagrams;
-pub use diagrams::{DiagramMarkup, DotMarkup, MarkupOpts, PikchrMarkup, PlantumlMarkup};
+pub use diagrams::{DiagramMarkup, DotMarkup, MarkupOpts, PikchrMarkup, PlantumlMarkup, Svg};
mod datafiles;
pub use datafiles::DataFile;
diff --git a/src/typeset.rs b/src/typeset.rs
index 9522e69..f63206a 100644
--- a/src/typeset.rs
+++ b/src/typeset.rs
@@ -4,7 +4,7 @@ use crate::PartialStep;
use crate::ScenarioStep;
use crate::StepKind;
use crate::SubplotError;
-use crate::{DiagramMarkup, DotMarkup, PikchrMarkup, PlantumlMarkup};
+use crate::{DiagramMarkup, DotMarkup, PikchrMarkup, PlantumlMarkup, Svg};
use crate::{Warning, Warnings};
use pandoc_ast::Attr;
@@ -213,7 +213,7 @@ pub fn roadmap_to_block(yaml: &str, warnings: &mut Warnings) -> Block {
// Typeset an SVG, represented as a byte vector, as a Pandoc AST Block
// element.
-fn typeset_svg(svg: Vec<u8>) -> Block {
+fn typeset_svg(svg: Svg) -> Block {
let url = svg_as_data_url(svg);
let attr = ("".to_string(), vec![], vec![]);
let img = Inline::Image(attr, vec![], (url, "".to_string()));
@@ -223,7 +223,7 @@ fn typeset_svg(svg: Vec<u8>) -> Block {
// Convert an SVG, represented as a byte vector, into a data: URL,
// which can be inlined so the image can be rendered without
// referencing external files.
-fn svg_as_data_url(svg: Vec<u8>) -> String {
- let svg = base64::encode(&svg);
+fn svg_as_data_url(svg: Svg) -> String {
+ let svg = base64::encode(svg.data());
format!("data:image/svg+xml;base64,{}", svg)
}