Last active
January 4, 2021 04:00
-
-
Save karronoli/8046f7b6cdc6e357c394086d777db66f to your computer and use it in GitHub Desktop.
Read, Eval, Print Barcode
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This file is automatically @generated by Cargo. | |
# It is not intended for manual editing. | |
[[package]] | |
name = "aho-corasick" | |
version = "0.7.15" | |
source = "registry+https://github.com/rust-lang/crates.io-index" | |
checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" | |
dependencies = [ | |
"memchr", | |
] | |
[[package]] | |
name = "lazy_static" | |
version = "1.4.0" | |
source = "registry+https://github.com/rust-lang/crates.io-index" | |
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" | |
[[package]] | |
name = "memchr" | |
version = "2.3.4" | |
source = "registry+https://github.com/rust-lang/crates.io-index" | |
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" | |
[[package]] | |
name = "read-eval-print-barcode" | |
version = "0.1.0" | |
dependencies = [ | |
"lazy_static", | |
"regex", | |
] | |
[[package]] | |
name = "regex" | |
version = "1.4.2" | |
source = "registry+https://github.com/rust-lang/crates.io-index" | |
checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" | |
dependencies = [ | |
"aho-corasick", | |
"memchr", | |
"regex-syntax", | |
"thread_local", | |
] | |
[[package]] | |
name = "regex-syntax" | |
version = "0.6.21" | |
source = "registry+https://github.com/rust-lang/crates.io-index" | |
checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" | |
[[package]] | |
name = "thread_local" | |
version = "1.0.1" | |
source = "registry+https://github.com/rust-lang/crates.io-index" | |
checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" | |
dependencies = [ | |
"lazy_static", | |
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[package] | |
name = "read-eval-print-barcode" | |
version = "0.1.0" | |
authors = ["karronoli <karo+git@karonori.com>"] | |
edition = "2018" | |
[dependencies] | |
regex = "1" | |
lazy_static = "1.4.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
// Use IntelliSense to learn about possible attributes. | |
// Hover to view descriptions of existing attributes. | |
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | |
"version": "0.2.0", | |
"configurations": [ | |
{ | |
"name": "(lldb) Launch", | |
"type": "cppdbg", | |
"request": "launch", | |
"program": "${workspaceFolder}/target/debug/read-eval-print-barcode", | |
"args": [], | |
"stopAtEntry": false, | |
"cwd": "${workspaceFolder}", | |
"environment": [], | |
"externalConsole": false, | |
"MIMode": "lldb" | |
} | |
] | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#[macro_use] | |
extern crate lazy_static; | |
extern crate regex; | |
use regex::Regex; | |
use std::collections::HashMap; | |
use std::fmt; | |
use std::io::{self, Read}; | |
#[derive(PartialEq, Debug)] | |
enum BarcodeType { | |
NW7, | |
CODE39, | |
} | |
impl fmt::Display for BarcodeType { | |
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
match *self { | |
BarcodeType::NW7 => write!(f, "NW7"), | |
BarcodeType::CODE39 => write!(f, "CODE39"), | |
} | |
} | |
} | |
const ESC: char = '\x1d'; | |
const LF: char = '\x0a'; | |
const NULL: char = '\x00'; | |
const NW7_DIGIT: usize = 9; | |
const NW7_START_A_WITH_QUIET: &str = "01011001001"; | |
const NW7_END_A_WITH_GAP_AND_QUIET: &str = "010110010010"; | |
const CODE39_DIGIT: usize = 15; | |
const CODE39_START_ASTERISK_WITH_QUIET: &str = "0100010111011101"; | |
const CODE39_END_ASTERISK_WITH_GAP_AND_QUIET: &str = "01000101110111010"; | |
lazy_static! { | |
static ref NW7_SYMBOL_TABLE: HashMap<&'static str, char> = { | |
[("101011001", '1'), ("101001011", '2')] | |
.iter() | |
.cloned() | |
.collect() | |
}; | |
static ref CODE39_SYMBOL_TABLE: HashMap<&'static str, char> = { | |
[("111010001010111", '1'), ("101110001010111", '2')] | |
.iter() | |
.cloned() | |
.collect() | |
}; | |
} | |
fn extract(input: &str) -> (BarcodeType, String) { | |
lazy_static! { | |
static ref UNDERSCORE: Regex = Regex::new(r"_").unwrap(); | |
static ref NW7_PATTERN: String = format!( | |
r"(?x) | |
{} | |
(.+) # body | |
{}", | |
NW7_START_A_WITH_QUIET, NW7_END_A_WITH_GAP_AND_QUIET | |
); | |
static ref CODE39_PATTERN: String = format!( | |
r"(?x) | |
{} | |
(.+) # body | |
{}", | |
CODE39_START_ASTERISK_WITH_QUIET, CODE39_END_ASTERISK_WITH_GAP_AND_QUIET | |
); | |
static ref NW7: Regex = Regex::new(&NW7_PATTERN).unwrap(); | |
static ref CODE39: Regex = Regex::new(&CODE39_PATTERN).unwrap(); | |
} | |
let digit = UNDERSCORE.replace_all(input, ""); | |
if NW7.is_match(&digit) { | |
let caps = NW7.captures(&digit).unwrap(); | |
return (BarcodeType::NW7, (&caps[1]).to_string()); | |
} else if CODE39.is_match(&digit) { | |
let caps = CODE39.captures(&digit).unwrap(); | |
return (BarcodeType::CODE39, (&caps[1]).to_string()); | |
} else { | |
panic!("unknown input."); | |
} | |
} | |
fn parse(barcode_type: &BarcodeType, body: &str) -> Vec<char> { | |
let mut result: Vec<char> = vec![]; | |
let mut index = 0; | |
match barcode_type { | |
BarcodeType::NW7 => loop { | |
if index >= body.len() { | |
break; | |
} | |
let gap = &body[index..index + 1]; | |
if gap == "1" { | |
panic!("no gap. i:{}", index); | |
} | |
let character = &body[index + 1..index + 1 + NW7_DIGIT]; | |
if !NW7_SYMBOL_TABLE.contains_key(character) { | |
panic!("no symbol. character:{}", character); | |
} | |
let value = NW7_SYMBOL_TABLE[character]; | |
result.push(value); | |
index += NW7_DIGIT + 1; | |
}, | |
BarcodeType::CODE39 => loop { | |
if index >= body.len() { | |
break; | |
} | |
let gap = &body[index..index + 1]; | |
if gap == "1" { | |
panic!("no gap. i:{}", index); | |
} | |
let character = &body[index + 1..index + 1 + CODE39_DIGIT]; | |
if !CODE39_SYMBOL_TABLE.contains_key(character) { | |
panic!("no symbol. character:{}", character); | |
} | |
let value = CODE39_SYMBOL_TABLE[character]; | |
result.push(value); | |
index += CODE39_DIGIT + 1; | |
}, | |
}; | |
return result; | |
} | |
fn main() -> io::Result<()> { | |
let mut buffer = String::new(); | |
io::stdin().read_to_string(&mut buffer)?; | |
let (barcode_type, body) = extract(&buffer); | |
let result = parse(&barcode_type, &body); | |
print!("type: {}{LF}", barcode_type, LF = LF); | |
let value: String = result.into_iter().collect(); | |
print!("value: {}{LF}", value, LF = LF); | |
print!( | |
"{ESC}w\x03{ESC}h\x32{ESC}f\x00{ESC}H\x00{ESC}{}{NULL}{LF}", | |
match barcode_type { | |
BarcodeType::NW7 => format!("k\x06A{}A", value), | |
BarcodeType::CODE39 => format!("k\x04{}", value), | |
}, | |
ESC = ESC, | |
NULL = NULL, | |
LF = LF | |
); | |
Ok(()) | |
} | |
#[cfg(test)] | |
mod tests { | |
use super::*; | |
#[test] | |
fn nw7_valid() { | |
let input = "0_1011001001_0_101011001_0_101001011_0_10110010010"; | |
let (barcode_type, body) = extract(input); | |
assert_eq!(&barcode_type, &BarcodeType::NW7); | |
let result = parse(&barcode_type, &body); | |
assert_eq!(&result, &['1', '2']); | |
} | |
#[test] | |
fn code39_valid() { | |
let input = "0_100010111011101_0_111010001010111_0_101110001010111_0_100010111011101_0"; | |
let (barcode_type, body) = extract(input); | |
assert_eq!(&barcode_type, &BarcodeType::CODE39); | |
let result = parse(&barcode_type, &body); | |
assert_eq!(&result, &['1', '2']); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use strict; | |
use warnings; | |
use utf8; | |
use Test::More; | |
# 0 を 白線,1 を黒線とする | |
use constant { | |
ESC => "\x{1d}", | |
TYPE_NW7 => 'NW7', | |
TYPE_CODE39 => 'CODE39', | |
NW7_DIGIT => 9, | |
NW7_START_A_WITH_QUIET => '01011001001', # start A | |
NW7_END_A_WITH_GAP_AND_QUIET => '010110010010', # end A | |
CODE39_DIGIT => 15, | |
CODE39_START_ASTERISK_WITH_QUIET => '0100010111011101', # start * | |
CODE39_END_ASTERISK_WITH_GAP_AND_QUIET => '01000101110111010', # end * | |
}; | |
use constant { | |
# NW7 のスタート・ストップキャラクタは A のみ対応 | |
NW7_PATTERN => qr/^ | |
@{[NW7_START_A_WITH_QUIET]} | |
(.+) # body | |
@{[NW7_END_A_WITH_GAP_AND_QUIET]} | |
$/x, | |
# CODE39 のスタート・ストップキャラクタは * のみ対応 | |
CODE39_PATTERN => qr/^ | |
@{[CODE39_START_ASTERISK_WITH_QUIET]} | |
(.+) # body | |
@{[CODE39_END_ASTERISK_WITH_GAP_AND_QUIET]} | |
$/x | |
}; | |
my %NW7_SYMBOL_TABLE = ( | |
'101011001' => '1', | |
'101001011' => '2', | |
); | |
my %CODE39_SYMBOL_TABLE = ( | |
'111010001010111' => '1', | |
'101110001010111' => '2', | |
); | |
sub extract { | |
my ($digit) = @_; | |
# _ は桁の区切りを見易くするためだけに許容する | |
die q(must contain '0' or '1' or '_' only) if $digit =~ /[^01_]/; | |
$digit =~ s/_//g; | |
my $body; | |
if (($body) = $digit =~ NW7_PATTERN) { | |
return (TYPE_NW7, $body); | |
} elsif (($body) = $digit =~ CODE39_PATTERN) { | |
return (TYPE_CODE39, $body); | |
} else { | |
die 'unknown input.'; | |
} | |
} | |
sub parse { | |
my ($type, $body) = @_; | |
my @result; | |
if ($type eq TYPE_NW7) { | |
# キャラクタ間ギャップは細エレメントと同一幅のみ受け付ける | |
for (my $i = 0; $i < length($body); $i += NW7_DIGIT + 1) { | |
my $gap = substr($body, $i, 1); | |
my $character = substr($body, $i + 1, NW7_DIGIT); | |
die 'no gap. i:' . $i if $gap eq '1'; | |
die 'no symbol. character:' . $character unless $NW7_SYMBOL_TABLE{$character}; | |
push @result, $NW7_SYMBOL_TABLE{$character}; | |
} | |
} elsif ($type eq TYPE_CODE39) { | |
# キャラクタ間ギャップは細エレメントと同一幅のみ受け付ける | |
for (my $i = 0; $i < length($body); $i += CODE39_DIGIT + 1) { | |
my $gap = substr($body, $i, 1); | |
my $character = substr($body, $i + 1, CODE39_DIGIT); | |
die 'no gap. i:' . $i if $gap eq '1'; | |
die 'no symbol. character:' . $character unless $CODE39_SYMBOL_TABLE{$character}; | |
push @result, $CODE39_SYMBOL_TABLE{$character}; | |
} | |
} else { | |
die "unknown type. type:$type"; | |
} | |
return @result; | |
} | |
if (-t STDIN) { | |
subtest 'NW7 valid' => sub { | |
plan tests => 2; | |
my $input = '0_1011001001_0_101011001_0_101001011_0_10110010010'; | |
my ($type, $body) = extract($input); | |
is(TYPE_NW7, $type); | |
my @result = parse($type, $body); | |
is_deeply([1, 2], \@result); | |
}; | |
subtest 'CODE39 valid' => sub { | |
plan tests => 2; | |
my $input = '0_100010111011101_0_111010001010111_0_101110001010111_0_100010111011101_0'; | |
my ($type, $body) = extract($input); | |
is(TYPE_CODE39, $type); | |
my @result = parse($type, $body); | |
is_deeply([1, 2], \@result); | |
}; | |
done_testing; | |
} else { | |
chomp(my $line = readline(STDIN)); | |
my ($type, $body) = extract($line); | |
my @result = parse($type, $body); | |
print "type: $type\n"; | |
my $value = join '', @result; | |
print "value: $value\n"; | |
# ESC/POS で印刷する | |
# https://reference.epson-biz.com/modules/ref_escpos_ja/index.php?content_id=128 | |
if ($type eq TYPE_NW7) { | |
print ESC . "w\x{03}" | |
. ESC . "h\x{32}" | |
. ESC . "f\x{00}" | |
. ESC . "H\x{00}" | |
. ESC . "k\x{06}" . "A${value}A\x{00}" | |
. "\x{0a}"; | |
} elsif ($type eq TYPE_CODE39) { | |
print ESC . "w\x{03}" | |
. ESC . "h\x{32}" | |
. ESC . "f\x{00}" | |
. ESC . "H\x{00}" | |
. ESC . "k\x{04}" . "${value}\x{00}" | |
. "\x{0a}"; | |
} else { | |
die 'unknown barcode type.' | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Rust
Perl