tikz-gallery-generator
Custum build of stapix for tikz.pablopie.xyz
- Commit
- 2624bb554ef0db20f193fd539d57a83ff73893a5
- Parent
- 114e44204990754bac9e392cc5ce5986ad4260c5
- Author
- Pablo <pablo-escobar@riseup.net>
- Date
Implemented rendering of licensing info
Implemented a feature to read and write info on each picture's author
and the correspoding redistribution license
Diffstat
5 files changed, 401 insertions, 21 deletions
diff --git a/README.md b/README.md
@@ -26,34 +26,41 @@ To use stapix run:
$ stapix config.yml
```
-The configuration file `config.yml` should consist of a list of file-paths
-pointing to the images the user wants in the gallery and text alternatives
-(visual descriptions) for each image, such as in the following example:
+The configuration file `config.yml` should consist of a list of struct entries
+describing the contents of each picture, such as in the following example:
```yaml
- path: ./path/to/first.jpg
alt: "Text alternative for the first photo"
+ author: John Doe
+ license: PD
- path: ./path/to/second.png
alt: "Text alternative for the second photo"
+ author: Jane Doe
+ license: CC-BY-SA-4
...
```
-Each entry on the list may additionally contain a field named `caption`
-containing additional information to be displayed in the captions of the
-corresponding image, as in the following example:
+Each entry in the list should contain the following fields:
-```yaml
-- path: ./path/to/third.jpg
- alt: "Text alternative for the first photo"
- caption: "The third picture of the gallery"
-```
+* **`path`:** The path to the file in question
+* **`alt`:** Text altenative for the picture
+* **`caption` (optional):** A description of the picture
+* **`author`:** The name of the author of the picture
+* **`author-url` (optional):** A URL to a webpage by/on the picture's author
+* **`license`:** The license type of the picture. Should be one of
+`PD` (public domain), `CC0`, `CC-BY-1`, `CC-BY-2`, `CC-BY-3`, `CC-BY-4`,
+`CC-BY-NC-1`, `CC-BY-NC-2`, `CC-BY-NC-3`, `CC-BY-NC-4`, `CC-BY-NC-SA-1`,
+`CC-BY-NC-SA-2`, `CC-BY-NC-SA-3`, `CC-BY-NC-SA-4`, `CC-BY-ND-1`, `CC-BY-ND-2`,
+`CC-BY-ND-3`, `CC-BY-ND-4`, `CC-BY-NC-ND-1`, `CC-BY-NC-ND-2`, `CC-BY-NC-ND-3`
+or `CC-BY-NC-ND-4`
For best accessibility, the `alt` field should contain a concise visual
description of the picture in question (including subjects, colors and scenery)
to be displayed by screen readers, while the `caption` field should contain
_additional_ information on the picture (such as the location or date when it
-was taken) to be displayed for all users. _The `alt` and `caption` attributes
-should not be the same!_ See
+was taken) to be displayed for all users. **The `alt` and `caption` attributes
+should not be the same!** See
<https://www.htmhell.dev/adventcalendar/2022/22/> for further details.
## Installation
diff --git a/src/main.rs b/src/main.rs
@@ -109,10 +109,16 @@ macro_rules! usage_config {
usage_header_msg = "[USAGE]".yellow().bold());
eprintln!(" - {path_attr} ./path/to/first.jpg
{alt_attr} \"Text alternative for the first photo\"
+ {author_attr} John Doe
+ {license_attr} PD
- {path_attr} ./path/to/second.png
{alt_attr} \"Text alternative for the second photo\"
+ {author_attr} Jane Doe
+ {license_attr} CC-BY-SA-4
...",
- path_attr = "path:".green(), alt_attr = "alt:".green());
+ path_attr = "path:".green(), alt_attr = "alt:".green(),
+ author_attr = "author:".green(),
+ license_attr = "license:".green());
}
}
@@ -329,16 +335,32 @@ fn render_pic_page(pic: &Picture) -> io::Result<()> {
name = Escaped(&pic.file_name))?;
}
writeln!(f, "<div id=\"picture-container\">")?;
+ writeln!(f, "<div id=\"picture-frame\">")?;
writeln!(
f,
"<img alt=\"{alt}\" src=\"/{PHOTOS_PATH}/{file_name}\">",
alt = Escaped(&pic.alt),
file_name = Escaped(&pic.file_name)
)?;
+ writeln!(f, "<div>")?;
+ if let Some(url) = &pic.author_url {
+ writeln!(f, "by <a role=\"author\" href=\"{url}\">{author}</a>",
+ author = Escaped(&pic.author))?;
+ } else {
+ writeln!(f, "by {author}", author = Escaped(&pic.author))?;
+ }
+ if let Some(license) = &pic.license {
+ writeln!(f, "(licensed under <a role=\"license\" href=\"{url}\">{license}</a>)",
+ url = license.url())?;
+ } else {
+ writeln!(f, "(public domain)")?;
+ }
+ writeln!(f, "</div>")?;
+ writeln!(f, "</div>")?;
writeln!(f, "</div>")?;
if let Some(caption) = &pic.caption {
writeln!(f, "<figcaption>")?;
- writeln!(f, "{}", Escaped(&caption))?;
+ writeln!(f, "{}", Escaped(caption))?;
writeln!(f, "</figcaption>")?;
}
writeln!(f, "</figure>")?;
diff --git a/src/picture.rs b/src/picture.rs
@@ -2,7 +2,32 @@ use serde::{
de::{Deserializer, Error, Unexpected},
Deserialize,
};
-use std::path::PathBuf;
+use std::{fmt::{self, Display}, path::PathBuf};
+
+const LICENSES: &'static [&'static str] = &[
+ "PD",
+ "CC0",
+ "CC-BY-1",
+ "CC-BY-2",
+ "CC-BY-3",
+ "CC-BY-4",
+ "CC-BY-NC-1",
+ "CC-BY-NC-2",
+ "CC-BY-NC-3",
+ "CC-BY-NC-4",
+ "CC-BY-NC-SA-1",
+ "CC-BY-NC-SA-2",
+ "CC-BY-NC-SA-3",
+ "CC-BY-NC-SA-4",
+ "CC-BY-ND-1",
+ "CC-BY-ND-2",
+ "CC-BY-ND-3",
+ "CC-BY-ND-4",
+ "CC-BY-NC-ND-1",
+ "CC-BY-NC-ND-2",
+ "CC-BY-NC-ND-3",
+ "CC-BY-NC-ND-4",
+];
/// Info on a individual entry on the gallery
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -11,6 +36,285 @@ pub struct Picture {
pub file_name: String,
pub alt: String,
pub caption: Option<String>,
+ pub license: Option<LicenseType>,
+ pub author: String,
+ pub author_url: Option<String>,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct LicenseType(CreativeCommons);
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+enum CreativeCommons {
+ /// Creative Commons (without attribution)
+ CC0,
+ /// Creative Commons Attributions (derivatives allowed)
+ CCBY {
+ version: CreativeCommonsVersion,
+ non_commercial: bool,
+ share_alike: bool,
+ },
+ // The ND (non-derivatives) option excludes the SA (share alike) option
+ /// Creative Commons Attributions Non-Derivatives
+ CCBYND {
+ version: CreativeCommonsVersion,
+ non_commercial: bool,
+ },
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+enum CreativeCommonsVersion {
+ One,
+ Two,
+ Three,
+ Four,
+}
+
+impl LicenseType {
+ pub const fn url(&self) -> &'static str {
+ match self.0 {
+ // CC0
+ CreativeCommons::CC0 => {
+ "https://creativecommons.org/publicdomain/zero/1.0/"
+ },
+ // CC-BY-1
+ CreativeCommons::CCBY {
+ version: CreativeCommonsVersion::One,
+ non_commercial: false,
+ share_alike: false,
+ } => "http://creativecommons.org/licenses/by/1.0/",
+ // CC-BY-2
+ CreativeCommons::CCBY {
+ version: CreativeCommonsVersion::Two,
+ non_commercial: false,
+ share_alike: false,
+ } => "http://creativecommons.org/licenses/by/2.0/",
+ // CC-BY-3
+ CreativeCommons::CCBY {
+ version: CreativeCommonsVersion::Three,
+ non_commercial: false,
+ share_alike: false,
+ } => "http://creativecommons.org/licenses/by/3.0/",
+ // CC-BY-4
+ CreativeCommons::CCBY {
+ version: CreativeCommonsVersion::Four,
+ non_commercial: false,
+ share_alike: false,
+ } => "http://creativecommons.org/licenses/by/4.0/",
+ // CC-BY-SA-1
+ CreativeCommons::CCBY {
+ version: CreativeCommonsVersion::One,
+ non_commercial: false,
+ share_alike: true,
+ } => "http://creativecommons.org/licenses/by-sa/1.0/",
+ // CC-BY-SA-2
+ CreativeCommons::CCBY {
+ version: CreativeCommonsVersion::Two,
+ non_commercial: false,
+ share_alike: true,
+ } => "http://creativecommons.org/licenses/by-sa/2.0/",
+ // CC-BY-SA-3
+ CreativeCommons::CCBY {
+ version: CreativeCommonsVersion::Three,
+ non_commercial: false,
+ share_alike: true,
+ } => "http://creativecommons.org/licenses/by-sa/3.0/",
+ // CC-BY-SA-4
+ CreativeCommons::CCBY {
+ version: CreativeCommonsVersion::Four,
+ non_commercial: false,
+ share_alike: true,
+ } => "http://creativecommons.org/licenses/by-sa/4.0/",
+ // CC-BY-NC-1
+ CreativeCommons::CCBY {
+ version: CreativeCommonsVersion::One,
+ non_commercial: true,
+ share_alike: false,
+ } => "http://creativecommons.org/licenses/by-nc/1.0/",
+ // CC-BY-NC-2
+ CreativeCommons::CCBY {
+ version: CreativeCommonsVersion::Two,
+ non_commercial: true,
+ share_alike: false,
+ } => "http://creativecommons.org/licenses/by-nc/2.0/",
+ // CC-BY-NC-3
+ CreativeCommons::CCBY {
+ version: CreativeCommonsVersion::Three,
+ non_commercial: true,
+ share_alike: false,
+ } => "http://creativecommons.org/licenses/by-nc/3.0/",
+ // CC-BY-NC-4
+ CreativeCommons::CCBY {
+ version: CreativeCommonsVersion::Four,
+ non_commercial: true,
+ share_alike: false,
+ } => "http://creativecommons.org/licenses/by-nc/4.0/",
+ // CC-BY-NC-SA-1
+ CreativeCommons::CCBY {
+ version: CreativeCommonsVersion::One,
+ non_commercial: true,
+ share_alike: true,
+ } => "http://creativecommons.org/licenses/by-nc-sa/1.0/",
+ // CC-BY-NC-SA-2
+ CreativeCommons::CCBY {
+ version: CreativeCommonsVersion::Two,
+ non_commercial: true,
+ share_alike: true,
+ } => "http://creativecommons.org/licenses/by-nc-sa/2.0/",
+ // CC-BY-NC-SA-3
+ CreativeCommons::CCBY {
+ version: CreativeCommonsVersion::Three,
+ non_commercial: true,
+ share_alike: true,
+ } => "http://creativecommons.org/licenses/by-nc-sa/3.0/",
+ // CC-BY-NC-SA-4
+ CreativeCommons::CCBY {
+ version: CreativeCommonsVersion::Four,
+ non_commercial: true,
+ share_alike: true,
+ } => "http://creativecommons.org/licenses/by-nc-sa/4.0/",
+ // CC-BY-ND-1
+ CreativeCommons::CCBYND {
+ version: CreativeCommonsVersion::One,
+ non_commercial: false,
+ } => "http://creativecommons.org/licenses/by-nd/1.0/",
+ // CC-BY-ND-2
+ CreativeCommons::CCBYND {
+ version: CreativeCommonsVersion::Two,
+ non_commercial: false,
+ } => "http://creativecommons.org/licenses/by-nd/2.0/",
+ // CC-BY-ND-3
+ CreativeCommons::CCBYND {
+ version: CreativeCommonsVersion::Three,
+ non_commercial: false,
+ } => "http://creativecommons.org/licenses/by-nd/3.0/",
+ // CC-BY-ND-4
+ CreativeCommons::CCBYND {
+ version: CreativeCommonsVersion::Four,
+ non_commercial: false,
+ } => "http://creativecommons.org/licenses/by-nd/4.0/",
+ // CC-BY-NC-ND-1
+ CreativeCommons::CCBYND {
+ version: CreativeCommonsVersion::One,
+ non_commercial: true,
+ } => "http://creativecommons.org/licenses/by-nc-nd/1.0/",
+ // CC-BY-NC-ND-2
+ CreativeCommons::CCBYND {
+ version: CreativeCommonsVersion::Two,
+ non_commercial: true,
+ } => "http://creativecommons.org/licenses/by-nc-nd/2.0/",
+ // CC-BY-NC-ND-3
+ CreativeCommons::CCBYND {
+ version: CreativeCommonsVersion::Three,
+ non_commercial: true,
+ } => "http://creativecommons.org/licenses/by-nc-nd/3.0/",
+ // CC-BY-NC-ND-4
+ CreativeCommons::CCBYND {
+ version: CreativeCommonsVersion::Four,
+ non_commercial: true,
+ } => "http://creativecommons.org/licenses/by-nc-nd/4.0/",
+ }
+ }
+
+ pub fn parse(s: &str) -> Result<Option<Self>, ()> {
+ if s == "PD" {
+ return Ok(None);
+ } else if s == "CC0" {
+ return Ok(Some(Self(CreativeCommons::CC0)));
+ }
+
+ if s.len() < 3 {
+ return Err(());
+ }
+
+ let version = match &s[s.len() - 1..] {
+ "1" => CreativeCommonsVersion::One,
+ "2" => CreativeCommonsVersion::Two,
+ "3" => CreativeCommonsVersion::Three,
+ "4" => CreativeCommonsVersion::Four,
+ _ => return Err(())
+ };
+
+ match &s[..s.len() - 1] {
+ "CC-BY-" => {
+ Ok(
+ Some(
+ Self(
+ CreativeCommons::CCBY {
+ version,
+ non_commercial: false,
+ share_alike: false,
+ }
+ )
+ )
+ )
+ },
+ "CC-BY-NC-" => {
+ Ok(
+ Some(
+ Self(
+ CreativeCommons::CCBY {
+ version,
+ non_commercial: true,
+ share_alike: false,
+ }
+ )
+ )
+ )
+ },
+ "CC-BY-SA-" => {
+ Ok(
+ Some(
+ Self(
+ CreativeCommons::CCBY {
+ version,
+ non_commercial: false,
+ share_alike: true,
+ }
+ )
+ )
+ )
+ },
+ "CC-BY-NC-SA-" => {
+ Ok(
+ Some(
+ Self(
+ CreativeCommons::CCBY {
+ version,
+ non_commercial: true,
+ share_alike: true,
+ }
+ )
+ )
+ )
+ },
+ "CC-BY-ND-" => {
+ Ok(
+ Some(
+ Self(
+ CreativeCommons::CCBYND {
+ version,
+ non_commercial: false,
+ }
+ )
+ )
+ )
+ },
+ "CC-BY-NC-ND-" => {
+ Ok(
+ Some(
+ Self(
+ CreativeCommons::CCBYND {
+ version,
+ non_commercial: true,
+ }
+ )
+ )
+ )
+ },
+ _ => Err(())
+ }
+ }
}
impl<'de> Deserialize<'de> for Picture {
@@ -23,23 +327,34 @@ impl<'de> Deserialize<'de> for Picture {
path: String,
alt: String,
caption: Option<String>,
+ license: String,
+ author: String,
+ #[serde(alias = "author-url")]
+ author_url: Option<String>,
}
let Info {
path: path_str,
alt,
caption,
+ license,
+ author,
+ author_url,
} = Info::deserialize(deserializer)?;
- let mut path = PathBuf::new();
- path.push(path_str.clone());
+ let license = LicenseType::parse(&license)
+ .map_err(|_| D::Error::unknown_variant(&license, LICENSES))?;
+ let path = PathBuf::from(&path_str);
if let Some(file_name) = path.file_name().and_then(|s| s.to_str()) {
Ok(Self {
path: path.clone(),
alt: alt.trim().to_string(),
file_name: String::from(file_name),
- caption: caption.clone(),
+ caption,
+ author: author.trim().to_string(),
+ author_url: author_url.map(|s| s.trim().to_string()),
+ license,
})
} else {
Err(D::Error::invalid_value(
@@ -49,3 +364,39 @@ impl<'de> Deserialize<'de> for Picture {
}
}
}
+
+impl Display for LicenseType {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ match self.0 {
+ CreativeCommons::CC0 => write!(f, "CC0"),
+ CreativeCommons::CCBY { version, non_commercial, share_alike } => {
+ write!(f, "CC-BY-")?;
+ if non_commercial {
+ write!(f, "NC-")?;
+ }
+ if share_alike {
+ write!(f, "SA-")?;
+ }
+ write!(f, "{}", version)
+ },
+ CreativeCommons::CCBYND { version, non_commercial } => {
+ write!(f, "CC-BY-")?;
+ if non_commercial {
+ write!(f, "NC-")?;
+ }
+ write!(f, "ND-{}", version)
+ }
+ }
+ }
+}
+
+impl Display for CreativeCommonsVersion {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ match self {
+ CreativeCommonsVersion::One => write!(f, "1.0"),
+ CreativeCommonsVersion::Two => write!(f, "2.0"),
+ CreativeCommonsVersion::Three => write!(f, "3.0"),
+ CreativeCommonsVersion::Four => write!(f, "4.0"),
+ }
+ }
+}