Import from KLE

main
Elnu 1 year ago
parent 31572568da
commit 79813a2549

3
.gitignore vendored

@ -1,3 +1,4 @@
/target /target
.direnv .direnv
output.svg output.svg
layout.json

114
Cargo.lock generated

@ -67,6 +67,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
[[package]]
name = "csscolorparser"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb2a7d3066da2de787b7f032c736763eb7ae5d355f81a68bab2675a96008b0bf"
dependencies = [
"phf",
]
[[package]] [[package]]
name = "derive_more" name = "derive_more"
version = "0.99.17" version = "0.99.17"
@ -86,8 +95,10 @@ version = "0.1.0"
dependencies = [ dependencies = [
"askama", "askama",
"derive_more", "derive_more",
"kle-serial",
"lazy_static", "lazy_static",
"regex", "regex",
"serde_json",
"strum", "strum",
"strum_macros", "strum_macros",
] ]
@ -107,6 +118,23 @@ dependencies = [
"libm", "libm",
] ]
[[package]]
name = "itoa"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
[[package]]
name = "kle-serial"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffbb2235a7a2c256a7d0e6393a2f29b27995dc098b797c0fd02f418166709eae"
dependencies = [
"csscolorparser",
"rgb",
"serde",
]
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"
@ -172,6 +200,48 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
[[package]]
name = "phf"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
dependencies = [
"phf_macros",
"phf_shared",
]
[[package]]
name = "phf_generator"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
dependencies = [
"phf_shared",
"rand",
]
[[package]]
name = "phf_macros"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
dependencies = [
"phf_generator",
"phf_shared",
"proc-macro2",
"quote",
"syn 2.0.28",
]
[[package]]
name = "phf_shared"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
dependencies = [
"siphasher",
]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.66" version = "1.0.66"
@ -190,6 +260,21 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.9.3" version = "1.9.3"
@ -219,6 +304,12 @@ version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2"
[[package]]
name = "rgb"
version = "0.8.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20ec2d3e3fc7a92ced357df9cebd5a10b6fb2aa1ee797bf7e9ce2f17dffc8f59"
[[package]] [[package]]
name = "rustc_version" name = "rustc_version"
version = "0.4.0" version = "0.4.0"
@ -234,6 +325,12 @@ version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
[[package]]
name = "ryu"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]] [[package]]
name = "semver" name = "semver"
version = "1.0.18" version = "1.0.18"
@ -260,6 +357,23 @@ dependencies = [
"syn 2.0.28", "syn 2.0.28",
] ]
[[package]]
name = "serde_json"
version = "1.0.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "siphasher"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
[[package]] [[package]]
name = "strum" name = "strum"
version = "0.25.0" version = "0.25.0"

@ -6,7 +6,9 @@ edition = "2021"
[dependencies] [dependencies]
askama = "0.12.0" askama = "0.12.0"
derive_more = "0.99.17" derive_more = "0.99.17"
kle-serial = "0.2.2"
lazy_static = "1.4.0" lazy_static = "1.4.0"
regex = "1.9.3" regex = "1.9.3"
serde_json = "1.0.105"
strum = "0.25.0" strum = "0.25.0"
strum_macros = "0.25.2" strum_macros = "0.25.2"

@ -1,70 +0,0 @@
mod oyayubi;
pub use oyayubi::OYAYUBI;
use std::fmt::{Display, self};
#[derive(Clone, Copy)]
pub enum Key<'a> {
Oyayubi {
latin: char,
normal: char,
shift: char,
alt_shift: Option<char>,
},
Single {
text: &'a str,
u: f64,
},
Icon {
icon_path: &'a str,
u: f64,
},
Break,
}
pub const fn oyayubi<'a>(latin: char, normal: char, shift: char, alt_shift: Option<char>) -> Key<'a> {
Key::Oyayubi {
latin,
normal,
shift,
alt_shift,
}
}
pub const fn single<'a>(text: &'a str, u: f64) -> Key<'a> {
Key::Single {
text,
u,
}
}
pub const fn icon<'a>(icon_path: &'a str, u: f64) -> Key<'a> {
Key::Icon {
icon_path,
u,
}
}
impl<'a> Key<'a> {
pub fn width_mod(&self) -> f64 {
use Key::*;
match self {
Oyayubi { .. } => 1.0,
Single { u, .. } => *u,
Icon { u, .. } => *u,
Break => 0.0,
}
}
}
impl<'a> Display for Key<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Key::*;
write!(f, "{}", match self {
Oyayubi { latin, .. } => latin.to_string(),
Single { text, .. } => text.to_string(),
Icon { icon_path, .. } => icon_path.to_string(),
Break => "".to_string(),
})
}
}

@ -1,35 +0,0 @@
use super::Key;
use super::oyayubi as k;
pub const OYAYUBI: [Key; 30] = [
k('Q', '。', 'ぁ', None),
k('W', 'か', 'え', None),
k('E', 'た', 'り', None),
k('R', 'こ', 'ゃ', None),
k('T', 'さ', 'れ', None),
k('Y', 'ら', 'よ', Some('ぱ')),
k('U', 'ち', 'に', None),
k('I', 'く', 'る', None),
k('O', 'つ', 'ま', None),
k('P', '', 'ぇ', Some('ぴ')),
k('A', 'う', 'を', None),
k('S', 'し', 'あ', None),
k('D', 'て', 'な', None),
k('F', 'け', 'ゅ', None),
k('G', 'せ', 'も', None),
k('H', 'は', 'み', None),
k('J', 'と', 'お', None),
k('K', 'き', 'の', None),
k('L', 'い', 'ょ', Some('ぽ')),
k(';', 'ん', 'っ', None), // Missing +
k('Z', '', 'ぅ', None),
k('X', 'ひ', 'ー', None),
k('C', 'す', 'ろ', None),
k('V', 'ふ', 'や', None),
k('B', 'へ', 'ぃ', None),
k('N', 'め', 'ぬ', Some('ぷ')),
k('M', 'そ', 'ゆ', None),
k(',', 'ね', 'む', Some('ぺ')), // Missing <
k('.', 'ほ', 'わ', None), // Missing >
k('?', '・', 'ぉ', Some('ゎ')) // Missing /
];

@ -1,9 +1,6 @@
pub mod svg; pub mod svg;
use svg::*; use svg::*;
pub mod key;
use key::*;
use askama::Template; use askama::Template;
use derive_more::From; use derive_more::From;
use std::{fs::OpenOptions, io::Write}; use std::{fs::OpenOptions, io::Write};
@ -13,46 +10,32 @@ use lazy_static::lazy_static;
#[template(path = "document.xml")] #[template(path = "document.xml")]
struct DocumentTemplate<'a> { struct DocumentTemplate<'a> {
dimensions: &'a DocumentDimensions, dimensions: &'a DocumentDimensions,
box_size: SVGMeasure, keyboard: kle_serial::Keyboard,
positions: Vec<(&'a Key<'a>, (SVGMeasure, SVGMeasure))>,
} }
struct DocumentDimensions { struct DocumentDimensions {
width: SVGMeasure, width: SVGMeasure,
height: SVGMeasure, height: SVGMeasure,
margin: SVGMeasure,
}
fn kle_font_units(kle_font_units: &usize) -> SVGMeasure {
return SVGMeasure::new((6 + kle_font_units * 2) as f64, SVGUnit::Pixel);
}
lazy_static! {
static ref LETTER_LANDSCAPE: DocumentDimensions = DocumentDimensions {
width: SVGMeasure::new(11.0, SVGUnit::Inch),
height: SVGMeasure::new(8.5, SVGUnit::Inch),
margin: SVGMeasure::new(0.25, SVGUnit::Inch),
};
} }
#[derive(From, Debug)] #[derive(From, Debug)]
enum Error { enum Error {
Io(std::io::Error), Io(std::io::Error),
Template(askama::Error), Template(askama::Error),
} LayoutParse(serde_json::Error),
fn positions<'a>(
keys: &'a Vec<Key>,
size: SVGMeasure,
margin: SVGMeasure,
padding: SVGMeasure,
dimensions: &DocumentDimensions,
) -> Vec<(&'a Key<'a>, (SVGMeasure, SVGMeasure))> {
let grid = size + padding;
let mut x = margin;
let mut y = margin;
let mut positions = Vec::with_capacity(keys.len());
for key in keys {
if let Key::Break = key {
x = margin;
y = y + grid;
continue;
}
if x + grid > dimensions.width - margin {
x = margin;
y = y + grid;
}
positions.push((key, (x, y)));
x = x + size * key.width_mod() + padding;
}
positions
} }
fn main() -> Result<(), Error> { fn main() -> Result<(), Error> {
@ -61,33 +44,10 @@ fn main() -> Result<(), Error> {
.truncate(true) .truncate(true)
.create(true) .create(true)
.open("output.svg")?; .open("output.svg")?;
let dimensions = DocumentDimensions {
width: SVGMeasure::new(8.5, SVGUnit::Inch),
height: SVGMeasure::new(11.0, SVGUnit::Inch),
};
let box_size = SVGMeasure::new(14.0, SVGUnit::Millimeter);
let padding = SVGMeasure::new(2.5, SVGUnit::Millimeter);
let margin = SVGMeasure::new(0.25, SVGUnit::Inch);
let document = DocumentTemplate { let document = DocumentTemplate {
positions: positions(&KEYS, box_size, margin, padding, &dimensions), dimensions: &LETTER_LANDSCAPE,
dimensions: &dimensions, keyboard: serde_json::from_str(&std::fs::read_to_string("layout.json")?)?,
box_size,
}; };
write!(file, "{}", document.render()?)?; write!(file, "{}", document.render()?)?;
Ok(()) Ok(())
}
lazy_static! {
static ref KEYS: Vec<Key<'static>> = {
let mut keys = OYAYUBI.to_vec();
keys.push(Key::Break);
keys.push(single("親指左", 3.0));
keys.push(single("親指右", 3.0));
keys.push(Key::Break);
keys.push(icon("assets/nixos.svg", 1.0));
keys.push(icon("assets/playpause.svg", 1.0));
keys.push(icon("assets/previous.svg", 1.0));
keys.push(icon("assets/next.svg", 1.0));
keys
};
} }

@ -19,7 +19,7 @@ pub struct SVGMeasure {
} }
impl SVGMeasure { impl SVGMeasure {
pub fn new(measure: f64, unit: SVGUnit) -> Self { pub const fn new(measure: f64, unit: SVGUnit) -> Self {
Self { measure, unit } Self { measure, unit }
} }

@ -4,30 +4,63 @@
<defs> <defs>
<style type="text/css"><![CDATA[@import url('https://fonts.googleapis.com/css2?family=M+PLUS+Rounded+1c:wght@700&display=swap');]]></style> <style type="text/css"><![CDATA[@import url('https://fonts.googleapis.com/css2?family=M+PLUS+Rounded+1c:wght@700&display=swap');]]></style>
</defs> </defs>
{% for (key, (x, y)) in positions %} {% let u = crate::svg::SVGMeasure::new(14.0, crate::svg::SVGUnit::Millimeter) %}
{% let width = box_size * key.width_mod() %} {% let padding = crate::svg::SVGMeasure::new(2.5, crate::svg::SVGUnit::Millimeter) %}
<g x="{{ x }}" y="{{ y }}" transform="translate({{ x.to_user_units() }},{{ y.to_user_units() }})" width="{{ width }}" height="{{ box_size }}"> {% let dy = "0.125em" %}
<rect width="{{ width }}" height="{{ box_size }}" fill="none" stroke="#cccccc" stroke-width="1pt" rx="2mm" /> {% for key in keyboard.keys %}
{% let dy = "0.125em" %} {% let x = u * key.x + padding * key.x + dimensions.margin %}
{% match key %} {% let y = u * key.y + padding * key.y + dimensions.margin %}
{% when crate::key::Key::Oyayubi with { latin, normal, shift, alt_shift } %} {% let width = u * key.width %}
{% let font_size = "0.45cm" %} {% let height = u * key.height %}
{% let latin_font_size = "0.4cm" %} <g x="{{ x }}" y="{{ y }}" transform="translate({{ x.to_user_units() }},{{ y.to_user_units() }})" width="{{ width }}" height="{{ height }}">
{% let alt_shift_font_size = "0.325cm" %} <rect width="{{ width }}" height="{{ height }}" fill="none" stroke="#cccccc" stroke-width="1pt" rx="2mm" />
<text x="{{ width * 0.25 }}" y="{{ box_size * 0.25 }}" dominant-baseline="middle" text-anchor="middle" style="font-family: 'M PLUS Rounded 1c'" font-size="{{ latin_font_size }}" dy="{{ dy }}">{{ latin }}</text>
<text x="{{ width * 0.75 }}" y="{{ box_size * 0.25 }}" dominant-baseline="middle" text-anchor="middle" style="font-family: 'M PLUS Rounded 1c'" font-size="{{ font_size }}" dy="{{ dy }}">{{ shift }}</text> {# top-left #}
<text x="{{ width * 0.75 }}" y="{{ box_size * 0.75 }}" dominant-baseline="middle" text-anchor="middle" style="font-family: 'M PLUS Rounded 1c'" font-size="{{ font_size }}" dy="{{ dy }}">{{ normal }}</text> {% if let Some(legend) = key.legends[0] %}
{% if let Some(alt_shift) = alt_shift %} <text x="{{ width * 0.25 }}" y="{{ height * 0.25 }}" dominant-baseline="middle" text-anchor="middle" style="font-family: 'M PLUS Rounded 1c'" font-size="{{ crate::kle_font_units(legend.size) }}" dy="{{ dy }}">{{ legend.text }}</text>
<text x="{{ width / 2.0 }}" y="{{ box_size / 2.0 }}" dominant-baseline="middle" text-anchor="middle" style="font-family: 'M PLUS Rounded 1c'" font-size="{{ alt_shift_font_size }}" dy="{{ dy }}">{{ alt_shift }}</text> {% endif %}
{% endif %}
{% when crate::key::Key::Single with { text, u } %} {# top-center #}
{% let font_size = "0.5cm" %} {% if let Some(legend) = key.legends[1] %}
<text x="{{ width * 0.5 }}" y="{{ box_size * 0.5 }}" dominant-baseline="middle" text-anchor="middle" style="font-family: 'M PLUS Rounded 1c'" font-size="{{ font_size }}" dy="{{ dy }}">{{ text }}</text> <text x="{{ width * 0.5 }}" y="{{ height * 0.25 }}" dominant-baseline="middle" text-anchor="middle" style="font-family: 'M PLUS Rounded 1c'" font-size="{{ crate::kle_font_units(legend.size) }}" dy="{{ dy }}">{{ legend.text }}</text>
{% when crate::key::Key::Icon with { icon_path, u } %} {% endif %}
{% let icon_width_mm = 7.5 %}
<image x="{{ width.measure * 0.5 - icon_width_mm * 0.5 }}mm" y="{{ box_size.measure * 0.5 - icon_width_mm * 0.5 }}mm" href="{{ icon_path }}" width="{{ icon_width_mm }}mm" height="{{ icon_width_mm }}mm" /> {# top-right #}
{% when crate::key::Key::Break %} {% if let Some(legend) = key.legends[2] %}
{% endmatch %} <text x="{{ width * 0.75 }}" y="{{ height * 0.25 }}" dominant-baseline="middle" text-anchor="middle" style="font-family: 'M PLUS Rounded 1c'" font-size="{{ crate::kle_font_units(legend.size) }}" dy="{{ dy }}">{{ legend.text }}</text>
{% endif %}
{# middle-left #}
{% if let Some(legend) = key.legends[3] %}
<text x="{{ width * 0.25 }}" y="{{ height * 0.5 }}" dominant-baseline="middle" text-anchor="middle" style="font-family: 'M PLUS Rounded 1c'" font-size="{{ crate::kle_font_units(legend.size) }}" dy="{{ dy }}">{{ legend.text }}</text>
{% endif %}
{# middle-center #}
{% if let Some(legend) = key.legends[4] %}
<text x="{{ width * 0.5 }}" y="{{ height * 0.5 }}" dominant-baseline="middle" text-anchor="middle" style="font-family: 'M PLUS Rounded 1c'" font-size="{{ crate::kle_font_units(legend.size) }}" dy="{{ dy }}">{{ legend.text }}</text>
{% endif %}
{# middle-right #}
{% if let Some(legend) = key.legends[5] %}
<text x="{{ width * 0.75 }}" y="{{ height * 0.5 }}" dominant-baseline="middle" text-anchor="middle" style="font-family: 'M PLUS Rounded 1c'" font-size="{{ crate::kle_font_units(legend.size) }}" dy="{{ dy }}">{{ legend.text }}</text>
{% endif %}
{# bottom-left #}
{% if let Some(legend) = key.legends[6] %}
<text x="{{ width * 0.25 }}" y="{{ height * 0.75 }}" dominant-baseline="middle" text-anchor="middle" style="font-family: 'M PLUS Rounded 1c'" font-size="{{ crate::kle_font_units(legend.size) }}" dy="{{ dy }}">{{ legend.text }}</text>
{% endif %}
{# bottom-center #}
{% if let Some(legend) = key.legends[7] %}
<text x="{{ width * 0.55 }}" y="{{ height * 0.75 }}" dominant-baseline="middle" text-anchor="middle" style="font-family: 'M PLUS Rounded 1c'" font-size="{{ crate::kle_font_units(legend.size) }}" dy="{{ dy }}">{{ legend.text }}</text>
{% endif %}
{# bottom-right #}
{% if let Some(legend) = key.legends[8] %}
<text x="{{ width * 0.75 }}" y="{{ height * 0.75 }}" dominant-baseline="middle" text-anchor="middle" style="font-family: 'M PLUS Rounded 1c'" font-size="{{ crate::kle_font_units(legend.size) }}" dy="{{ dy }}">{{ legend.text }}</text>
{% endif %}
{# TODO 9-11 are side #}
</g> </g>
{% endfor %} {% endfor %}
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

@ -51,7 +51,7 @@ Updating `cargoHash`:
pname = "dyesub-tool"; pname = "dyesub-tool";
version = "0.1.0"; version = "0.1.0";
buildAndTestSubdir = "dyesub-tool"; buildAndTestSubdir = "dyesub-tool";
cargoHash = "sha256-QlLyKDZZ+/bB4xBv0j9GoL6Ur3Okdskkazi4WlHhx6Y="; cargoHash = "sha256-kZUTrejN4KVZ91CkpweLivwm9KaMT+fT6Ljq5Vy0Jik=";
meta = meta // { meta = meta // {
description = "A tool for generating dye sublimation transfer sheet SVGs for Japanese thumb shift keycaps."; description = "A tool for generating dye sublimation transfer sheet SVGs for Japanese thumb shift keycaps.";
}; };

Loading…
Cancel
Save