From 4db350cb29b9960cb41caf4d99e83cb076f9a550 Mon Sep 17 00:00:00 2001 From: Hartmut Goebel Date: Sun, 23 Jan 2022 21:21:26 +0100 Subject: [PATCH 1/2] Eliminate the need for perl. Replace the only perl script in "grafik" by a python script. Unfortunately this requires the Python package "lxml" (which is commonly used, though), since the "xml" package from the Python standard library is not quite capable. I put special care in the Python script to generate the same svg as the perl script - although both are not identical. Differences are e.g. trailing zeros in fractional numbers and slightly different xml formatting. --- grafik/Readme.md | 3 +- grafik/aufkleber/Makefile | 2 +- grafik/aufkleber/alle-grau-1234.pl | 256 ----------------------------- grafik/aufkleber/alle-grau-1234.py | 221 +++++++++++++++++++++++++ grafik/shell.nix | 3 +- 5 files changed, 224 insertions(+), 261 deletions(-) delete mode 100755 grafik/aufkleber/alle-grau-1234.pl create mode 100755 grafik/aufkleber/alle-grau-1234.py diff --git a/grafik/Readme.md b/grafik/Readme.md index 3e9fc4f5..5e111ee6 100644 --- a/grafik/Readme.md +++ b/grafik/Readme.md @@ -21,8 +21,7 @@ Um alle Bilder erzeugen können, benötigt man eine Vielzahl an Abhängigkeiten: - sed - ed - libxkbcommon (xkbcli) - - python mit jinja2, more-itertools, numpy, pandas, matplotlib und seaborn - - perl mit XML::Writer + - python mit jinja2, numpy, pandas, matplotlib, seaborn und lxml - php - Linux Libertine - Gentium Plus Compact diff --git a/grafik/aufkleber/Makefile b/grafik/aufkleber/Makefile index 402bb79a..8a67cb0e 100644 --- a/grafik/aufkleber/Makefile +++ b/grafik/aufkleber/Makefile @@ -1,7 +1,7 @@ all: default %-grau-1234.svg: ../../A-REFERENZ-A/%.txt - ./alle-grau-1234.pl $< > $@ + ./alle-grau-1234.py $< > $@ EXTRASVG=\ neo20-grau-1234.svg \ diff --git a/grafik/aufkleber/alle-grau-1234.pl b/grafik/aufkleber/alle-grau-1234.pl deleted file mode 100755 index 4eec3281..00000000 --- a/grafik/aufkleber/alle-grau-1234.pl +++ /dev/null @@ -1,256 +0,0 @@ -#!/usr/bin/env -S perl -w - -use strict; -use warnings; - -use XML::Writer; - -use open ':encoding(utf8)'; -use utf8; - -sub start_svg; -sub end_svg; -sub create_defs; -sub create_key; -sub parse_ref; -sub in2px; -sub create_keys; -sub round; - -my $dpi = 90; -# a4 paper -my $height = in2px(8.268); -my $width = in2px(11.69); - -my $keywidth = in2px(0.75); -my $keyheight = in2px(0.75); -my $labelwidth = in2px(0.5); -my $labelheight = in2px(0.55); - -my $posx = 0; -my $posy = 0; - -my $row = 0; -my %kp_mapping = ( # mapping for keypad, level 4 - 'Hom' => '⇱', - 'KP↑' => '⇡', - 'PgU' => '⇞', - 'KP←' => '⇠', - 'Beg' => '•', - 'KP→' => '⇢', - 'End' => '⇲', - 'KP↓' => '⇣', - 'PgD' => '⇟', - 'Ins' => '⎀', - 'Del' => '⌦', - 'Ent' => '⏎', - 'vec' => ' ⃗' -); - -my $writer = new XML::Writer(ENCODING => 'utf-8', - DATA_MODE => 1, - DATA_INDENT => 2); - -start_svg; -create_defs; - -create_keys(parse_ref); - -end_svg; - -exit; - -# parse reference and return multi-array -sub parse_ref { - my @letters; - open REF, $ARGV[0] - or die 'Error opening reference: '.$!; - while () { - my @layer; - last if /^== Zeichenerläuterungen/; - - # layout blocks - if(/Miniatur ===$/) { - while() { - last if(/^$/); # empty line => end of current block - next if($_ !~ /^│/); # skip horizontal dividers - - push @layer, split /│/; - } - push @letters, [ @layer ]; # push ref - } - } - close REF; - - return @letters; -} - -sub create_keys { - my @letters = @_; - - for (0..$#{$letters[0]}) { # letters - create_key - $letters[0][$_], - $letters[0][$_], - $letters[1][$_], - $letters[2][$_], - $letters[3][$_]; - } - for (0..$#{$letters[7]}) { # numbers - create_key - $letters[7][$_], - $letters[7][$_], - $letters[8][$_], - $letters[9][$_], - $letters[10][$_]; - } -} - -sub in2px { - return ($_[0]||0) * $dpi; -} - -sub start_svg { - $writer->xmlDecl('UTF-8'); - $writer->doctype('svg', '-//W3C//DTD SVG 20001102//EN', - 'http://www.w3.org/TR/2000/CR-SVG-20001102/DTD/svg-20001102.dtd'); - - $writer->startTag('svg', height => $height, width => $width, - 'xmlns' => 'http://www.w3.org/2000/svg', - 'xmlns:xlink' => 'http://www.w3.org/1999/xlink'); -} -sub end_svg { - $writer->endTag('svg'); - $writer->end(); -} - -# create the key template -sub create_defs { - $writer->startTag('defs'); - - # style information - $writer->dataElement('style', ' - text.common { - font-family:Linux Biolinum O, Linux Biolinum; - font-style:normal; - font-variant:normal; - font-stretch:normal; - text-align:center; - text-anchor:middle; - stroke:none; - } - text.main { - /*font-weight:bold;*/ - font-size:19px; - fill:#eeeeee; - stroke-width:4; - } - text.special { - font-size:16px; - stroke-width:3; - } - text.outline { - stroke:#111111; - stroke-linejoin:round; - } - rect#boundary { - fill:none; - stroke:#eeeeee; - stroke-width:'.in2px(0.005).'; - } - rect#border { - fill:#333333; - stroke:#eeeeee; - stroke-width:'.in2px(0.025).'; - } - text.level1 { - } - text.level2 { - } - text.level3 { - fill:#99dd66; - } - text.level4 { - fill:#6699dd; - font-size:13px; - }', type => 'text/css'); - - # boundary of keys - $writer->emptyTag('rect', - id => 'boundary', - width => $keywidth, height => $keyheight, - rx => 5); - # border for keys, actual key stickers - $writer->emptyTag('rect', - id => 'border', - width => $labelwidth, height => $labelheight, - rx => 10); - - $writer->endTag('defs'); -} - -# create a specific key (cloned from template) -# first parameter (0) is first level, and is only used for line breaks in the graphic -# param 1..4 are actual layers -sub create_key { - my @keys = @_; - - $keys[0] = '' unless defined($keys[0]); - $keys[1] = '' unless defined($keys[1]); - s/\s//g for @keys; # remove any space - - # map words to symbols for numblock - foreach (keys %kp_mapping) { - $keys[3] =~ s/\Q$_/\Q$kp_mapping{$_}/; - $keys[4] =~ s/\Q$_/\Q$kp_mapping{$_}/; - } - - return if length($keys[0]) != 1; - - $writer->startTag('g', - transform => "translate($posx,$posy)", - id => 'key_'.(join '', @keys)); - $writer->emptyTag('use', 'xlink:href' => '#boundary'); - $writer->startTag('g', - transform => 'translate('.(($keywidth-$labelwidth)/2.5).', 2)'); - $writer->emptyTag('use', 'xlink:href' => '#border'); - - # add text+outlines to sticker - for(' outline', '') { - $writer->dataElement('text', $keys[1], - transform => 'translate(15,41)', - class => "level1 common main$_") - # do not show e1, if it's the same letter as e2 - # only use for latin letters - unless(#$keys[1] =~ /[a-züöäß]/ && - $keys[1] =~ /\Q$keys[2]/i); - $writer->dataElement('text', $keys[2]||'', - transform => 'translate(15,19)', - class => "level2 common main$_"); - $writer->dataElement('text', $keys[3]||'', - transform => 'translate(32,18)', - class => "level3 common special$_"); - $writer->dataElement('text', $keys[4]||'', - transform => 'translate(32,42)', - class => "level4 common special$_"); - # do not show e4 on keypad - #unless($row > 4 && $keys[0] =~ /\d/i); - } - $writer->endTag('g'); - $writer->endTag('g'); - - $posx += $keywidth; - if($row < 3) { - if($keys[0] =~ /[`´y]/) { # split keyboard rows, dirty way - $posx = ++$row*$keywidth/2; # not really accurate, but works - $posy += $keyheight; - } - } elsif($keys[0] =~ /[-93+j]/) { # keypad - $posx = ++$row*0; - $posy += $keyheight; - } -} - -sub round { - return int($_[0]+.5*($_[0]<=>0)); -} diff --git a/grafik/aufkleber/alle-grau-1234.py b/grafik/aufkleber/alle-grau-1234.py new file mode 100755 index 00000000..af254024 --- /dev/null +++ b/grafik/aufkleber/alle-grau-1234.py @@ -0,0 +1,221 @@ +#!/usr/bin/env python + +import sys +from lxml import etree as ET + + +def in2px(inch): + return round(inch * DPI, 2) + + +DPI = 90 + +# a4 paper +HEIGHT = in2px(8.268) +WIDTH = in2px(11.69) + +KEYWIDTH = in2px(0.75) +KEYHEIGHT = in2px(0.75) +LABELWIDTH = in2px(0.5) +LABELHEIGHT = in2px(0.55) + +KP_MAPPING = { # mapping for keypad, level 4 + 'Hom': '⇱', + 'KP↑': '⇡', + 'PgU': '⇞', + 'KP←': '⇠', + 'Beg': '•', + 'KP→': '⇢', + 'End': '⇲', + 'KP↓': '⇣', + 'PgD': '⇟', + 'Ins': '⎀', + 'Del': '⌦', + 'Ent': '⏎', + 'vec': ' ⃗' +} + +strokewidth = in2px(0.005) +STYLESHEET = ''' +text.common { + font-family:Linux Biolinum O, Linux Biolinum; + font-style:normal; + font-variant:normal; + font-stretch:normal; + text-align:center; + text-anchor:middle; + stroke:none; +} +text.main { + /*font-weight:bold;*/ + font-size:19px; + fill:#eeeeee; + stroke-width:4; +} +text.special { + font-size:16px; + stroke-width:3; +} +text.outline { + stroke:#111111; + stroke-linejoin:round; +} +rect#boundary { + fill:none; + stroke:#eeeeee; + stroke-width:''' + str(in2px(0.005)) + '''px; +} +rect#border { + fill:#333333; + stroke:#eeeeee; + stroke-width:''' + str(in2px(0.025)) + '''px; +} +text.level1 { +} +text.level2 { +} +text.level3 { + fill:#99dd66; +} +text.level4 { + fill:#6699dd; + font-size:13px; +}''' + +NSMAP = {None: 'http://www.w3.org/2000/svg', + 'xlink': 'http://www.w3.org/1999/xlink'} +XLINK = '{%s}' % NSMAP['xlink'] +DOCTYPE = ('') + + +def parse_ref(filename): + '''parse reference and return multi-array''' + with open(filename) as fh: + lines = fh.read().splitlines() + keymap = [] + state = 0 + for line in lines: + line = line.strip() + if state == 0: + if line.endswith("Miniatur ==="): + state = 1 + layer = [] + keymap.append(layer) + continue + elif not line: # empty line: end of current block/layer + state = 0 + layer = None + continue + elif not line.startswith("│"): # skip horizontal dividers + continue + else: + line = [k.strip() for k in line.split("│")] + layer.append(line[1:-1]) + return keymap + + +def create_defs(parent): + '''create the key template''' + node = ET.SubElement(parent, 'defs') + # style sheet + ET.SubElement(node, 'style', type='text/css').text = STYLESHEET + # boundary of keys + ET.SubElement(node, 'rect', id='boundary', + width=str(KEYWIDTH), height=str(KEYHEIGHT), rx="5") + # border for keys, actual key stickers + ET.SubElement(node, 'rect', id='border', + width=str(LABELWIDTH), height=str(LABELHEIGHT), rx="10") + + +def create_keys(parent, keymap): + posy = 0 + + # main keyboard + # TODO remove leading blank key + for row in range(len(keymap[0])): + posx = row / 2 * KEYWIDTH # not really accurate, but works + for key in range(len(keymap[0][row])): + key = create_key(parent, posx, posy, + keymap[0][row][key], + keymap[1][row][key], + keymap[2][row][key], + keymap[3][row][key]) + if key is not None: # advance only if sticker was output + posx += KEYWIDTH + posy += KEYHEIGHT + + # numpad + for row in range(len(keymap[7])): + posx = 0 + for key in range(len(keymap[7][row])): + key = create_key(parent, posx, posy, + keymap[7][row][key], + keymap[8][row][key], + keymap[9][row][key], + keymap[10][row][key]) + if key is not None: # advance only if sticker was output + posx += KEYWIDTH + posy += KEYHEIGHT + + +def create_key(parent, posx, posy, *keys): + '''create a specific key (cloned from template)''' + + if len(keys[0]) != 1: # skip keys like Tab at the left side + return + + def text(level, trans, type): + ET.SubElement( + g1, 'text', + {'transform': f'translate({trans})', + 'class': f"level{level} common {type}{outline}"} + ).text = keys[level-1] + + keys = list(keys) + + # map words to symbols for numblock + keys[2] = KP_MAPPING.get(keys[2], keys[2]) + keys[3] = KP_MAPPING.get(keys[3], keys[3]) + + g0 = ET.SubElement(parent, 'g', + transform=f"translate({posx},{posy})", + id='key_' + ''.join(keys)) + ET.SubElement(g0, 'use', {XLINK + 'href': '#boundary'}) + g1 = ET.SubElement(g0, 'g', + transform=f'translate({(KEYWIDTH-LABELWIDTH)/2.5}, 2)') + ET.SubElement(g1, 'use', {XLINK + 'href': '#border'}) + + # do not show e1, if it's the same letter as e2 + upper_eq_lower = (keys[1].lower() == keys[0]) + + # add text+outlines to sticker + for outline in (' outline', ''): + if not upper_eq_lower: + text(1, '15,41', 'main') + text(2, '15,19', 'main') + text(3, '32,18', 'special') + text(4, '32,42', 'special') + return g0 + + +def main(): + import argparse + parser = argparse.ArgumentParser() + parser.add_argument('filename') + args = parser.parse_args() + + keymap = parse_ref(args.filename) + + root = ET.Element('svg', + height=str(HEIGHT), width=str(WIDTH), + nsmap=NSMAP) + create_defs(root) + create_keys(root, keymap) + + doc = ET.tostring(root, encoding="UTF-8", xml_declaration=True, + doctype=DOCTYPE, pretty_print=True) + sys.stdout.buffer.write(doc) + + +main() diff --git a/grafik/shell.nix b/grafik/shell.nix index aa475964..50394e88 100644 --- a/grafik/shell.nix +++ b/grafik/shell.nix @@ -14,8 +14,7 @@ pkgs.mkShell { python3Packages.seaborn python3Packages.more-itertools python3Packages.jinja2 - perl - perlPackages.XMLWriter + python3Packages.lxml php ]; From 0982d8234ac8da087e081595c414d4b021cdfdac Mon Sep 17 00:00:00 2001 From: Hartmut Goebel Date: Sat, 5 Feb 2022 21:30:59 +0100 Subject: [PATCH 2/2] Eliminate the need for php. Replace the only php script in "grafik" by a python script. Unfortunately this requires the Python package "lxml" (which is commonly used, though), since the "xml" package from the Python standard library is not quite capable. Since the original php script created a quite ugly and redundant svg file, I took the one from "grafik/bilder-einzeln/flat" as a basis. The new image is a bit different from the original, as it takes some ideas from "grafik/aufkleber/alle-grau-1234.py". Anyhow, almost all design parameters can be adjusted in the the stylesheet. --- grafik/Readme.md | 1 - grafik/bilder-uebersicht/Makefile | 6 +- grafik/bilder-uebersicht/all-grau-123456.php | 3476 ------------------ grafik/bilder-uebersicht/all-grau-123456.py | 255 ++ grafik/shell.nix | 1 - 5 files changed, 258 insertions(+), 3481 deletions(-) delete mode 100755 grafik/bilder-uebersicht/all-grau-123456.php create mode 100755 grafik/bilder-uebersicht/all-grau-123456.py diff --git a/grafik/Readme.md b/grafik/Readme.md index 5e111ee6..b0e37e04 100644 --- a/grafik/Readme.md +++ b/grafik/Readme.md @@ -22,7 +22,6 @@ Um alle Bilder erzeugen können, benötigt man eine Vielzahl an Abhängigkeiten: - ed - libxkbcommon (xkbcli) - python mit jinja2, numpy, pandas, matplotlib, seaborn und lxml - - php - Linux Libertine - Gentium Plus Compact - DejaVu Sans Mono diff --git a/grafik/bilder-uebersicht/Makefile b/grafik/bilder-uebersicht/Makefile index ad41df75..26cc588b 100644 --- a/grafik/bilder-uebersicht/Makefile +++ b/grafik/bilder-uebersicht/Makefile @@ -7,13 +7,13 @@ neo-bunt-123456.png: neo-bunt-123456.ods libreoffice --convert-to png $< neo20-grau-123456.svg: - ./all-grau-123456.php neo20 + ./all-grau-123456.py neo20 bone-grau-123456.svg: - ./all-grau-123456.php bone + ./all-grau-123456.py bone neoqwertz-grau-123456.svg: - ./all-grau-123456.php neoqwertz + ./all-grau-123456.py neoqwertz EXTRASVG=\ neo20-grau-123456.svg \ diff --git a/grafik/bilder-uebersicht/all-grau-123456.php b/grafik/bilder-uebersicht/all-grau-123456.php deleted file mode 100755 index 2a4c1f3c..00000000 --- a/grafik/bilder-uebersicht/all-grau-123456.php +++ /dev/null @@ -1,3476 +0,0 @@ -#!/usr/bin/env php - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - Strg - Umsch - Mod 3 - Mod 4 - Alt - Tab - Mod 4 - Strg - Umschalt - Mod 3 - Backspace - Return - {1_1} - {1_2} - {1_3} - {1_4} - {1_5} - {1_6} - - {2_1} - {2_2} - {2_3} - {2_4} - {2_5} - {2_6} - - {3_1} - {3_2} - {3_3} - {3_4} - {3_5} - {3_6} - - {4_1} - {4_2} - {4_3} - {4_4} - {4_5} - {4_6} - - {5_1} - {5_2} - {5_3} - {5_4} - {5_5} - {5_6} - - {6_1} - {6_2} - {6_3} - {6_4} - {6_5} - {6_6} - - {7_1} - {7_2} - {7_3} - {7_4} - {7_5} - {7_6} - - {8_1} - {8_2} - {8_3} - {8_4} - {8_5} - {8_6} - - {9_1} - {9_2} - {9_3} - {9_4} - {9_5} - {9_6} - - {10_1} - {10_2} - {10_3} - {10_4} - {10_5} - {10_6} - - {11_1} - {11_2} - {11_3} - {11_4} - {11_5} - {11_6} - - {12_1} - {12_2} - {12_3} - {12_4} - {12_5} - {12_6} - - {13_1} - {13_2} - {13_3} - {13_4} - {13_5} - {13_6} - - {14_1} - {14_2} - {14_3} - {14_4} - {14_5} - {14_6} - - {15_1} - {15_2} - {15_3} - {15_4} - {15_5} - {15_6} - - {16_1} - {16_2} - {16_3} - {16_4} - {16_5} - {16_6} - - {17_1} - {17_2} - {17_3} - {17_4} - {17_5} - {17_6} - - {18_1} - {18_2} - {18_3} - {18_4} - {18_5} - {18_6} - - {19_1} - {19_2} - {19_3} - {19_4} - {19_5} - {19_6} - - {20_1} - {20_2} - {20_3} - {20_4} - {20_5} - {20_6} - - {21_1} - {21_2} - {21_3} - {21_4} - {21_5} - {21_6} - - {22_1} - {22_2} - {22_3} - {22_4} - {22_5} - {22_6} - - {23_1} - {23_2} - {23_3} - {23_4} - {23_5} - {23_6} - - {24_1} - {24_2} - {24_3} - {24_4} - {24_5} - {24_6} - - {25_1} - {25_2} - {25_3} - {25_4} - {25_5} - {25_6} - - {26_1} - {26_2} - {26_3} - {26_4} - {26_5} - {26_6} - - {27_1} - {27_2} - {27_3} - {27_4} - {27_5} - {27_6} - - {28_1} - {28_2} - {28_3} - {28_4} - {28_5} - {28_6} - - {29_1} - {29_2} - {29_3} - {29_4} - {29_5} - {29_6} - - {30_1} - {30_2} - {30_3} - {30_4} - {30_5} - {30_6} - - {31_1} - {31_2} - {31_3} - {31_4} - {31_5} - {31_6} - - {32_1} - {32_2} - {32_3} - {32_4} - {32_5} - {32_6} - - {33_1} - {33_2} - {33_3} - {33_4} - {33_5} - {33_6} - - {34_1} - {34_2} - {34_3} - {34_4} - {34_5} - {34_6} - - {35_1} - {35_2} - {35_3} - {35_4} - {35_5} - {35_6} - - {36_1} - {36_2} - {36_3} - {36_4} - {36_5} - {36_6} - - {37_1} - {37_2} - {37_3} - {37_4} - {37_5} - {37_6} - - {38_1} - {38_2} - {38_3} - {38_4} - {38_5} - {38_6} - - {39_1} - {39_2} - {39_3} - {39_4} - {39_5} - {39_6} - - {40_1} - {40_2} - {40_3} - {40_4} - {40_5} - {40_6} - - {41_1} - {41_2} - {41_3} - {41_4} - {41_5} - {41_6} - - {42_1} - {42_2} - {42_3} - {42_4} - {42_5} - {42_6} - - {43_1} - {43_2} - {43_3} - {43_4} - {43_5} - {43_6} - - {44_1} - {44_2} - {44_3} - {44_4} - {44_5} - {44_6} - - {45_1} - {45_2} - {45_3} - {45_4} - {45_5} - {45_6} - - {46_1} - {46_2} - {46_3} - {46_4} - {46_5} - {46_6} - - -EOF; - - # Ersetze die Platzhalter mit den Zeichen aus dem Array - foreach ($key as $search => $replace) { - # Die Zeichen werden in HTML-NCRs (numeric character references) umgewandelt, damit sie nicht falsch interpretiert werden. - $svg = preg_replace('/{'.preg_quote($search).'}/', mb_encode_numericentity ($replace, array (0x0, 0xffff, 0, 0xffff), 'UTF-8'), $svg); - } - - # Speichern und Fertig - file_put_contents($layout . '-grau-123456.svg', $svg); -?> diff --git a/grafik/bilder-uebersicht/all-grau-123456.py b/grafik/bilder-uebersicht/all-grau-123456.py new file mode 100755 index 00000000..088f4d74 --- /dev/null +++ b/grafik/bilder-uebersicht/all-grau-123456.py @@ -0,0 +1,255 @@ +#!/usr/bin/env python + +import os +from lxml import etree as ET + +REFERENZ_DIR = '../../A-REFERENZ-A/' + +KEYWIDTH = KEYHEIGHT = 72 +LABELWIDTH = KEYWIDTH +LABELHEIGHT = KEYWIDTH + +KP_MAPPING = { # mapping for keypad, level 4 + 'Hom': '⇱', + 'KP↑': '⇡', + 'PgU': '⇞', + 'KP←': '⇠', + 'Beg': '•', + 'KP→': '⇢', + 'End': '⇲', + 'KP↓': '⇣', + 'PgD': '⇟', + 'Ins': '⎀', + 'Del': '⌦', + 'Ent': '⏎', + 'vec': ' ⃗' +} + +STYLESHEET = ''' +rect.grey { fill:#cccccc !important } +rect.key { fill:white; stroke:black; stroke-width: 1 } + +text { + /* font-family:Linux Biolinum O, Linux Biolinum;*/ + font-family:normal; + font-style:normal; + font-variant:normal; + font-stretch:normal; + stroke:none; + text-align:center; + text-anchor:middle; + dominant-baseline: middle; + fill: black; +} +text.main { font-weight:bold } +text.special { } +text.level1 { font-size:22px } +text.level2 { font-size:22px } +text.level3 { font-size:14px } +text.level4 { font-size:14px } +text.level5 { font-size:14px } +text.level6 { font-size:14px } +text.deadkey { fill: red } +text.modifier { font-size:20px } +''' + +NSMAP = {None: 'http://www.w3.org/2000/svg', + 'xlink': 'http://www.w3.org/1999/xlink'} +XLINK = '{%s}' % NSMAP['xlink'] +DOCTYPE = ('') + + +LAYOUT = ( + [1] * 13 + [2.0], + [1.5] + [1] * 12 + [0], + [1.75] + [1] * 11 + [1.0, 0], + [1.25, 1.0] + [1] * 10 + [2.75, 0], + ) + +SPECIAL_KEYS = ( + (0, 13, 2.0, "Backspace"), + (1, 0, 1.5, "Tab"), + (2, 0, 1.75, "Mod 3"), + (2, 12.75, 1, "Mod 3"), + (3, 0, 1.25, "Umsch"), + (3, 1.25, 1, "Mod 4"), + (3, 12.25, 2.75, "Umschalt"), + (4, 0, 1.5, "Strg"), + (4, 2.5, 1.5, "Alt"), + (4, 9.5, 1.5, "Mod4"), + (4, 13.5, 1.5, "Strg"), + ) + + +def parse_ref(layout): + '''parse reference and return multi-array''' + filename = os.path.join(REFERENZ_DIR, f'{layout}.txt') + with open(filename) as fh: + lines = fh.read().splitlines() + keymap = [] + state = 0 + for line in lines: + line = line.strip() + if state == 0: + if line.endswith("Miniatur ==="): + state = 1 + layer = [] + keymap.append(layer) + continue + elif not line: # empty line: end of current block/layer + state = 0 + layer = None + continue + elif not line.startswith("│"): # skip horizontal dividers + continue + else: + line = [k.strip() for k in line.split("│")] + layer.append(line[1:-1]) + return keymap + + +def create_defs(parent): + '''create the key template''' + node = ET.SubElement(parent, 'defs') + # style sheet + ET.SubElement(node, 'style', type='text/css').text = STYLESHEET + # boundary of keys + # ET.SubElement(node, 'rect', id='boundary', + # width=str(KEYWIDTH), height=str(KEYHEIGHT), rx="0") + # # border for keys, actual key stickers + # ET.SubElement(node, 'rect', id='border', + # width=str(LABELWIDTH), height=str(LABELHEIGHT), rx="0") + + +def draw_keys(parent): + # draw return key first to be overdrawn by others + w = sum(LAYOUT[0]) * KEYWIDTH + posx = sum(LAYOUT[1]) * KEYWIDTH + ET.SubElement(parent, 'rect', {'class': 'key grey'}, + x=str(posx), y=str(KEYHEIGHT + 1), + width=str(w - posx), height=str(2 * KEYHEIGHT)) + + def rect(x, y, kwidth, classes): + kwidth *= KEYWIDTH + ET.SubElement(parent, 'rect', {'class': classes}, + x=str(x), y=str(y), + width=str(kwidth), height=str(KEYHEIGHT)) + return kwidth + + # other keys + posy = 1 + for row in LAYOUT: + posx = 0 + for key in row: + if not key: + continue + elif isinstance(key, float): + kwidth = key * KEYWIDTH + else: + kwidth = rect(posx, posy, key, 'key') + posx += kwidth + posy += KEYHEIGHT + drawing_width = posx + 1 + + # special keys + for row, col_offset, kwidth, label in SPECIAL_KEYS: + rect(col_offset * KEYWIDTH, row * KEYHEIGHT + 1, kwidth, 'key grey') + rect(4 * KEYWIDTH, posy, 5.5, 'key') # Leertaste + + posy += KEYHEIGHT + return drawing_width, posy + + +def create_labels(parent, keymap): + posy = 1 + # main keyboard + for row in range(len(keymap[0])-1): + posx = 0 + for key in range(len(keymap[0][row])): + create_label(parent, posx, posy, + keymap[0][row][key], + keymap[1][row][key], + keymap[2][row][key], + keymap[3][row][key], + keymap[4][row][key], + keymap[5][row][key]) + posx += LAYOUT[row][key] * KEYWIDTH + posy += KEYHEIGHT + + def text(x, y, kwidth, label): + x = x * KEYWIDTH + kwidth * KEYWIDTH / 2 + 1 + y = y * KEYHEIGHT + KEYHEIGHT / 2 + 1 + ET.SubElement( + parent, 'text', + {'x': str(x), 'y': str(y), + 'class': 'modifier'} + ).text = label + + # special keys + for row, col_offset, kwidth, label in SPECIAL_KEYS: + text(col_offset, row, kwidth, label) + text(13.5, 1.25, 1.75, "Return") + + +def create_label(parent, posx, posy, *labels): + '''create a specific key (cloned from template)''' + + def text(level, trans, type): + ET.SubElement( + g, 'text', + {'transform': f'translate({trans})', + 'class': f"level{level} {type} {deadkey}"} + ).text = labels[level-1] + + labels = list(labels) + if len(labels[0]) > 1: + # special keys are labels special above + return + + # map words to symbols for numblock + labels[2] = KP_MAPPING.get(labels[2], labels[2]) + labels[3] = KP_MAPPING.get(labels[3], labels[3]) + + g = ET.SubElement(parent, 'g', + transform=f"translate({posx},{posy})", + id='key_' + ''.join(labels)) + upper_eq_lower = (labels[1].lower() == labels[0]) + deadkey = 'deadkey' if labels[0] in "ˆ`´" else 'live' + + if upper_eq_lower: + # do not show e1, if it's the same letter as e2 + text(2, '15,35', 'main') + else: + text(1, '15,55', 'main') + text(2, '15,20', '') + text(3, '42,50', 'special') + text(4, '42,20', 'special') + text(5, '59,50', 'special') + text(6, '59,20', 'special') + + +def main(): + import argparse + parser = argparse.ArgumentParser() + parser.add_argument('layout') + args = parser.parse_args() + + keymap = parse_ref(args.layout) + + root = ET.Element('svg', + nsmap=NSMAP) + create_defs(root) + width, height = draw_keys(root) + create_labels(root, keymap) + + root.set('width', str(width)) + root.set('height', str(height)) + + doc = ET.tostring(root, encoding="utf-8", xml_declaration=True, + doctype=DOCTYPE, pretty_print=True) + with open(f'{args.layout}-grau-123456.svg', 'wb') as fh: + fh.write(doc) + + +main() diff --git a/grafik/shell.nix b/grafik/shell.nix index 50394e88..e7f45e57 100644 --- a/grafik/shell.nix +++ b/grafik/shell.nix @@ -15,7 +15,6 @@ pkgs.mkShell { python3Packages.more-itertools python3Packages.jinja2 python3Packages.lxml - php ]; # You also need to install the fonts (libertine gentium dejavu ... )