├── example ├── .gitignore ├── dub.sdl └── source │ └── app.d ├── .gitignore ├── source └── barcode │ ├── qr │ ├── package.d │ ├── ecl.d │ ├── util.d │ ├── qrsegment.d │ └── qrcode.d │ ├── package.d │ ├── qrwrap.d │ ├── types.d │ ├── ean13.d │ ├── itf.d │ ├── code39.d │ ├── svgdraw.d │ ├── util.d │ └── code128.d ├── README.md ├── dub.sdl ├── .vscode └── launch.json ├── .travis.yml ├── LICENSE └── .appveyor.yml /example/.gitignore: -------------------------------------------------------------------------------- 1 | *.svg 2 | gen 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | *-test-lib* 3 | dub.selections.json 4 | 5 | # Compiled Static libraries 6 | *.a 7 | -------------------------------------------------------------------------------- /example/dub.sdl: -------------------------------------------------------------------------------- 1 | name "gen" 2 | description "Generate svg barcodes" 3 | dependency "barcode" path="../" 4 | -------------------------------------------------------------------------------- /source/barcode/qr/package.d: -------------------------------------------------------------------------------- 1 | module barcode.qr; 2 | 3 | public: 4 | import barcode.qr.ecl; 5 | import barcode.qr.qrcode; 6 | import barcode.qr.qrsegment; 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Barcode generators 2 | 3 | Implements some common bar codes: EAN13, CODE39, CODE128, QrCode, ITF. 4 | 5 | See example: barcode svg generator. 6 | -------------------------------------------------------------------------------- /dub.sdl: -------------------------------------------------------------------------------- 1 | name "barcode" 2 | description "Bar code generators (EAN13, CODE39, CODE128, QrCode)" 3 | authors "Oleg Butko (deviator)" 4 | copyright "Copyright © 2017, 2018, 2020, deviator" 5 | license "MIT" 6 | 7 | targetType "library" -------------------------------------------------------------------------------- /source/barcode/package.d: -------------------------------------------------------------------------------- 1 | module barcode; 2 | 3 | public: 4 | import barcode.types; 5 | import barcode.qrwrap; 6 | import barcode.code128; 7 | import barcode.code39; 8 | import barcode.ean13; 9 | import barcode.itf; 10 | import barcode.svgdraw; 11 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Debug", 6 | "type": "gdb", 7 | "request": "launch", 8 | "target": "barcode-test-lib", 9 | "cwd": "${workspaceRoot}" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | os: 3 | - linux 4 | - osx 5 | language: d 6 | d: 7 | - ldc 8 | - ldc-beta 9 | - ldc-1.8.0 10 | - ldc-1.4.0 11 | - dmd 12 | - dmd-nightly 13 | - dmd-2.079.1 14 | - dmd-2.078.3 15 | script: 16 | - dub test --compiler=${DC} --build=unittest-cov 17 | matrix: 18 | exclude: 19 | - {os: osx, d: ldc-1.4.0} 20 | after_success: 21 | - bash <(curl -s https://codecov.io/bash) 22 | -------------------------------------------------------------------------------- /source/barcode/qr/ecl.d: -------------------------------------------------------------------------------- 1 | // 2 | module barcode.qr.ecl; 3 | 4 | // Error correction level 5 | struct ECL 6 | { 7 | /// 8 | enum low = ECL(0,1), 9 | medium = ECL(1,0), 10 | quartile = ECL(2,3), 11 | high = ECL(3,2); 12 | 13 | /// 14 | int ord, formatBits; 15 | 16 | /// 17 | alias ord this; 18 | } 19 | 20 | /// 21 | @safe 22 | unittest 23 | { 24 | auto ecl = ECL.medium; 25 | assert ([7,9,8][ecl] == 9); 26 | assert (ecl.formatBits == 0); 27 | } 28 | -------------------------------------------------------------------------------- /source/barcode/qrwrap.d: -------------------------------------------------------------------------------- 1 | module barcode.qrwrap; 2 | 3 | import std.exception; 4 | import barcode.qr; 5 | import barcode.types; 6 | 7 | class Qr : BarCodeEncoder 8 | { 9 | ECL ecl; 10 | uint minVer, maxVer; 11 | 12 | pure @safe: 13 | 14 | this(ECL ecl=ECL.high, uint minVer=1, uint maxVer=40) 15 | { 16 | enforce(minVer >= 1 && minVer <= maxVer && maxVer <= 40, 17 | "wrong min/max version"); 18 | 19 | this.ecl = ecl; 20 | this.minVer = minVer; 21 | this.maxVer = maxVer; 22 | } 23 | 24 | BarCode encode(string str) 25 | { 26 | auto segs = QrSegment.makeSegments(str); 27 | auto qrcode = QrCode.encodeSegments(segs, ecl, minVer, maxVer, -1, true); 28 | return BarCode(qrcode.size, qrcode.modules, "qrcode"); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /source/barcode/types.d: -------------------------------------------------------------------------------- 1 | module barcode.types; 2 | 3 | public import std.bitmanip : BitArray; 4 | 5 | /// 6 | struct BarCode 7 | { 8 | /// 9 | size_t width; 10 | /// 11 | BitArray data; 12 | /// 13 | string type; 14 | 15 | pure const 16 | { 17 | /// 18 | bool opIndex(size_t x, size_t y) @trusted 19 | { 20 | if (0 <= x && x < width && 21 | 0 <= y && y < height) 22 | return data[cast(uint)(y*width+x)]; 23 | return false; 24 | } 25 | 26 | /// 27 | bool opIndex(size_t i) @trusted 28 | { 29 | if (0 <= i && i < data.length) 30 | return data[cast(uint)i]; 31 | return false; 32 | } 33 | 34 | /// 35 | auto height() @property @safe { return data.length / width; } 36 | } 37 | } 38 | 39 | /// 40 | interface BarCodeEncoder 41 | { 42 | /// 43 | BarCode encode(string str) @safe; 44 | } 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017, 2018, 2020 Oleg Butko 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /source/barcode/qr/util.d: -------------------------------------------------------------------------------- 1 | module barcode.qr.util; 2 | 3 | // std.bitmanip.BitArray store bits in other order 4 | 5 | struct BitBuffer 6 | { 7 | ubyte[] data; 8 | int length; 9 | 10 | alias getBytes = data; 11 | 12 | pure @safe: 13 | void appendBits(long val, int len) 14 | { 15 | auto nlen = length + len; 16 | reserve(nlen); 17 | for (int i = len - 1; i >= 0; i--, length++) 18 | data[length>>3] |= ((val >> i) & 1) << (7-(length&7)); 19 | } 20 | 21 | void reserve(int nlen) 22 | { 23 | if (nlen > data.length * 8) 24 | data.length += (nlen - data.length * 8 + 7) / 8; 25 | } 26 | 27 | void appendData(const(ubyte)[] arr, int len) 28 | { 29 | auto nlen = length + len; 30 | reserve(nlen); 31 | for (int i = 0; i < len; i++, length++) 32 | { 33 | int bit = (arr[i >> 3] >> (7-(i&7))) & 1; 34 | data[length>>3] |= bit << (7-(length & 7)); 35 | } 36 | } 37 | 38 | bool opIndex(size_t i) const 39 | { return cast(bool)((data[i>>3] >> (7-(i&7))) & 1); } 40 | 41 | void opIndexAssign(int val, size_t i) 42 | { 43 | auto bit = cast(bool)val; 44 | data[i>>3] |= bit << (7-(i&7)); 45 | } 46 | } 47 | 48 | @safe 49 | unittest 50 | { 51 | BitBuffer arr; 52 | arr.appendBits(0b101101, 4); 53 | assert (arr.length == 4); 54 | assert (arr[0] == true); 55 | assert (arr[1] == true); 56 | assert (arr[2] == false); 57 | assert (arr[3] == true); 58 | arr.appendBits(0b101, 3); 59 | assert (arr.length == 7); 60 | assert (arr[4] == true); 61 | assert (arr[5] == false); 62 | assert (arr[6] == true); 63 | 64 | arr[5] = 1; 65 | assert (arr[5] == true); 66 | } 67 | -------------------------------------------------------------------------------- /example/source/app.d: -------------------------------------------------------------------------------- 1 | import std.algorithm; 2 | import std.stdio; 3 | import std.string; 4 | import std.getopt; 5 | import std.conv : text; 6 | 7 | import barcode; 8 | 9 | enum listNames = ["Qr", "Code128", "Code39", "EAN13", "ITF"]; 10 | 11 | int fail(string msg="", bool usage=true) 12 | { 13 | if (msg != "") stderr.writeln(msg); 14 | if (usage) stderr.writefln("use: gen [-o] %-(%s %) ", listNames.map!(a=>"[-"~a~"]")); 15 | return 1; 16 | } 17 | 18 | int main(string[] args) 19 | { 20 | if (args.length < 2) return fail(); 21 | 22 | string output = "output.svg"; 23 | getopt(args, std.getopt.config.passThrough, "output|o", &output); 24 | args = args[1..$]; // first is program name 25 | 26 | BarCodeEncoder[string] list; 27 | 28 | size_t nn; 29 | 30 | foreach (i, arg; args) 31 | { 32 | nn = i; 33 | if (arg.startsWith("-")) 34 | { 35 | if(!listNames.canFind(arg[1..$])) 36 | return fail(text("unkonwn arg: ", arg)); 37 | 38 | mixin (mix); 39 | } 40 | else break; 41 | } 42 | 43 | if (list.length == 0) list[listNames[0]] = mixin("new " ~ listNames[0]); 44 | 45 | auto str = args[nn..$].join(" "); 46 | 47 | writeln ("output: ", output); 48 | writefln(" use: %-(%s, %)", list.keys); 49 | writeln (" data: ", str); 50 | 51 | auto bbcsd = new BaseBarCodeSvgDrawer; 52 | bbcsd.fixSizeMode = true; 53 | bbcsd.W = 400; 54 | 55 | foreach (enc; list.values) 56 | { 57 | auto bc = enc.encode(str); 58 | bbcsd.H = bc.type == "qrcode" ? 400 : 50; 59 | auto f = File(bc.type ~ "_" ~ output, "w"); 60 | f.write(bbcsd.draw(bc)); 61 | } 62 | 63 | return 0; 64 | } 65 | 66 | string mix() pure 67 | { 68 | string[] ret; 69 | 70 | foreach (n; listNames) 71 | ret ~= text(`if (arg[1..$] == "`, n, `") list["`, n, `"] = new `, n ,`;`); 72 | 73 | return ret.join('\n'); 74 | } -------------------------------------------------------------------------------- /source/barcode/ean13.d: -------------------------------------------------------------------------------- 1 | /// 2 | module barcode.ean13; 3 | 4 | import std.experimental.logger; 5 | 6 | import std.algorithm; 7 | import std.exception; 8 | import std.string; 9 | import std.range; 10 | import std.array; 11 | import std.typecons : tuple; 12 | import std.ascii; 13 | 14 | import barcode.types; 15 | import barcode.util; 16 | 17 | /// 18 | class EAN13 : BarCodeEncoder 19 | { 20 | protected: 21 | 22 | enum lead_trailer = bitsStr!"#-#"; 23 | enum separator = bitsStr!"-#-#-"; 24 | 25 | enum Bits!ushort[10][2] modules_AB = [[ 26 | bitsStr!"---##-#", 27 | bitsStr!"--##--#", 28 | bitsStr!"--#--##", 29 | bitsStr!"-####-#", 30 | bitsStr!"-#---##", 31 | bitsStr!"-##---#", 32 | bitsStr!"-#-####", 33 | bitsStr!"-###-##", 34 | bitsStr!"-##-###", 35 | bitsStr!"---#-##" 36 | ], 37 | [ 38 | bitsStr!"-#--###", 39 | bitsStr!"-##--##", 40 | bitsStr!"--##-##", 41 | bitsStr!"-#----#", 42 | bitsStr!"--###-#", 43 | bitsStr!"-###--#", 44 | bitsStr!"----#-#", 45 | bitsStr!"--#---#", 46 | bitsStr!"---#--#", 47 | bitsStr!"--#-###" 48 | ]]; 49 | 50 | enum Bits!ushort[10] modules_C = [ 51 | bitsStr!"###--#-", 52 | bitsStr!"##--##-", 53 | bitsStr!"##-##--", 54 | bitsStr!"#----#-", 55 | bitsStr!"#-###--", 56 | bitsStr!"#--###-", 57 | bitsStr!"#-#----", 58 | bitsStr!"#---#--", 59 | bitsStr!"#--#---", 60 | bitsStr!"###-#--" 61 | ]; 62 | 63 | enum ubyte[6][10] parities = [ 64 | [0, 0, 0, 0, 0, 0], 65 | [0, 0, 1, 0, 1, 1], 66 | [0, 0, 1, 1, 0, 1], 67 | [0, 0, 1, 1, 1, 0], 68 | [0, 1, 0, 0, 1, 1], 69 | [0, 1, 1, 0, 0, 1], 70 | [0, 1, 1, 1, 0, 0], 71 | [0, 1, 0, 1, 0, 1], 72 | [0, 1, 0, 1, 1, 0], 73 | [0, 1, 1, 0, 1, 0] 74 | ]; 75 | 76 | enum MODULE = 7; 77 | enum DIGITS = 12; 78 | enum WIDTH = lead_trailer.count * 2 + separator.count + MODULE + DIGITS; 79 | 80 | public: 81 | pure: 82 | 83 | /// 84 | override BarCode encode(string data) 85 | { 86 | enforce(data.length == DIGITS, format("length of data must be %s", DIGITS)); 87 | enforce(data.all!isDigit, "all symbols must be a numbers"); 88 | 89 | BitArray ret; 90 | 91 | size_t idx(char ch) { return cast(size_t)ch - cast(size_t)'0'; } 92 | void append(T)(Bits!T bb) { ret.addBits(bb); } 93 | 94 | append(lead_trailer); 95 | 96 | int checkSum = 0; 97 | foreach (i; 0 .. DIGITS) 98 | checkSum += (i%2 == 1 ? 1 : 3) * idx(data[i]); 99 | 100 | checkSum %= 10; 101 | checkSum = checkSum == 0 ? 0 : 10 - checkSum; 102 | 103 | assert (checkSum >= 0 && checkSum < 10, "checkSum calc wrong"); 104 | 105 | auto pp = parities[checkSum]; 106 | 107 | foreach (i; 0 .. 6) append(modules_AB[pp[i]][idx(data[i])]); 108 | 109 | append(separator); 110 | 111 | foreach (i; 6 .. 12) append(modules_C[idx(data[i])]); 112 | 113 | append(lead_trailer); 114 | 115 | return BarCode(ret.length, ret, "ean13"); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /source/barcode/itf.d: -------------------------------------------------------------------------------- 1 | /// Interleaved 2 of 5 2 | module barcode.itf; 3 | 4 | import std.algorithm; 5 | import std.exception; 6 | import std.string; 7 | import std.range; 8 | import std.array; 9 | import std.typecons : tuple; 10 | import std.ascii; 11 | import std.conv : to; 12 | 13 | import barcode.types; 14 | import barcode.util; 15 | 16 | /// 17 | class ITF : BarCodeEncoder 18 | { 19 | protected: 20 | 21 | // used flags of width lines and gaps: - (narow), # (wide) 22 | // width controls in drawMask function (X and W enums) 23 | enum start = bitsStr!"----"; 24 | enum stop = bitsStr!"#--"; 25 | 26 | enum Bits!ubyte[10] modules = [ 27 | bitsStr!"--##-", 28 | bitsStr!"#---#", 29 | bitsStr!"-#--#", 30 | bitsStr!"##---", 31 | bitsStr!"--#-#", 32 | bitsStr!"#-#--", 33 | bitsStr!"-##--", 34 | bitsStr!"---##", 35 | bitsStr!"#--#-", 36 | bitsStr!"-#-#-", 37 | ]; 38 | 39 | public: 40 | pure: 41 | /// 42 | this(AppendCheckSum acs=AppendCheckSum.no) { appendCheckSum = acs; } 43 | 44 | /// 45 | AppendCheckSum appendCheckSum; 46 | 47 | /// 48 | override BarCode encode(string sdata) 49 | { 50 | enforce(sdata.all!isDigit, "all symbols must be a numbers"); 51 | 52 | auto data = sdata.map!(a=>cast(ubyte)(a-'0')).array; 53 | 54 | if (appendCheckSum) data ~= checkSum(data); 55 | if (data.length%2) data = [ubyte(0)] ~ data; 56 | 57 | assert (data.length%2 == 0); 58 | 59 | BitArray ret; 60 | ret.addBits(start.drawMask); 61 | 62 | for (auto i = 0; i < data.length; i+=2) 63 | ret.addBits(combine(modules[data[i]], modules[data[i+1]]).drawMask); 64 | 65 | ret.addBits(stop.drawMask); 66 | return BarCode(ret.length, ret, "itf"); 67 | } 68 | } 69 | 70 | private: 71 | 72 | Bits!ulong combine(A, B)(auto ref const Bits!A a, auto ref const Bits!B b) 73 | { 74 | enforce(a.count == b.count); 75 | 76 | uint val; 77 | 78 | foreach (i; 0..a.count) 79 | val |= (b[i] | (a[i] << 1)) << (i*2); 80 | 81 | return Bits!ulong(a.count*2, val); 82 | } 83 | 84 | @safe 85 | unittest 86 | { 87 | assert (combine(bitsStr!"##", bitsStr!"--") == bitsStr!"#-#-"); 88 | assert (combine(bitsStr!"#-", bitsStr!"-#") == bitsStr!"#--#"); 89 | assert (combine(bitsStr!"#", bitsStr!"-") == bitsStr!"#-"); 90 | } 91 | 92 | ubyte checkSum(ubyte[] data) pure @safe 93 | { 94 | uint a, b; 95 | 96 | foreach (i, ch; data) 97 | if (i%2) a += ch; 98 | else b += ch; 99 | 100 | if (data.length%2) b *= 3; 101 | else a *= 3; 102 | 103 | return (10 - (a+b)%10)%10; 104 | } 105 | 106 | @safe 107 | unittest 108 | { 109 | assert(checkSum("1937".map!(a=>cast(ubyte)(a-'0')).array) == 8); 110 | } 111 | 112 | Bits!ulong drawMask(T)(auto ref const Bits!T mask, bool bar=true) 113 | { 114 | enum X = 1; 115 | enum W = 3; 116 | 117 | uint val, cur; 118 | 119 | bool black = !(bar ^ (mask.count%2)); 120 | 121 | foreach (i; 0 .. mask.count) 122 | { 123 | foreach (k; 0 .. (mask[i] ? W : X)) 124 | val |= black << cur++; 125 | black = !black; 126 | } 127 | 128 | return Bits!ulong(cur, val); 129 | } 130 | 131 | @safe 132 | unittest 133 | { 134 | immutable v1 = ITF.start.drawMask; 135 | assert(v1 == bitsStr!"#-#-"); 136 | const v2 = ITF.stop.drawMask; 137 | assert(v2 == bitsStr!"###-#"); 138 | auto v3 = ITF.stop.drawMask(false); 139 | assert(v3 == bitsStr!"---#-"); 140 | } 141 | -------------------------------------------------------------------------------- /.appveyor.yml: -------------------------------------------------------------------------------- 1 | platform: x64 2 | environment: 3 | matrix: 4 | - DC: dmd 5 | DVersion: beta 6 | arch: x64 7 | - DC: dmd 8 | DVersion: beta 9 | arch: x86 10 | - DC: dmd 11 | DVersion: stable 12 | arch: x64 13 | - DC: dmd 14 | DVersion: stable 15 | arch: x86 16 | - DC: ldc 17 | DVersion: beta 18 | arch: x86 19 | - DC: ldc 20 | DVersion: beta 21 | arch: x64 22 | - DC: ldc 23 | DVersion: stable 24 | arch: x86 25 | - DC: ldc 26 | DVersion: stable 27 | arch: x64 28 | 29 | skip_tags: false 30 | branches: 31 | only: 32 | - master 33 | 34 | install: 35 | - ps: function ResolveLatestDMD 36 | { 37 | $version = $env:DVersion; 38 | if($version -eq "stable") { 39 | $latest = (Invoke-WebRequest "http://downloads.dlang.org/releases/LATEST").toString(); 40 | $url = "http://downloads.dlang.org/releases/2.x/$($latest)/dmd.$($latest).windows.7z"; 41 | }elseif($version -eq "beta") { 42 | $latest = (Invoke-WebRequest "http://downloads.dlang.org/pre-releases/LATEST").toString(); 43 | $latestVersion = $latest.split("-")[0].split("~")[0]; 44 | $url = "http://downloads.dlang.org/pre-releases/2.x/$($latestVersion)/dmd.$($latest).windows.7z"; 45 | }else { 46 | $url = "http://downloads.dlang.org/releases/2.x/$($version)/dmd.$($version).windows.7z"; 47 | } 48 | $env:PATH += ";C:\dmd2\windows\bin;"; 49 | return $url; 50 | } 51 | - ps: function ResolveLatestLDC 52 | { 53 | $version = $env:DVersion; 54 | $arch = $env:arch; 55 | if($version -eq "stable") { 56 | $latest = (Invoke-WebRequest "https://ldc-developers.github.io/LATEST").toString().replace("`n","").replace("`r",""); 57 | $url = "https://github.com/ldc-developers/ldc/releases/download/v$($latest)/ldc2-$($latest)-windows-$($arch).7z"; 58 | }elseif($version -eq "beta") { 59 | $latest = (Invoke-WebRequest "https://ldc-developers.github.io/LATEST_BETA").toString().replace("`n","").replace("`r",""); 60 | $url = "https://github.com/ldc-developers/ldc/releases/download/v$($latest)/ldc2-$($latest)-windows-$($arch).7z"; 61 | } else { 62 | $latest = $version; 63 | $url = "https://github.com/ldc-developers/ldc/releases/download/v$($version)/ldc2-$($version)-windows-$($arch).7z"; 64 | } 65 | $env:PATH += ";C:\ldc2-$($latest)-windows-$($arch)\bin"; 66 | $env:DC = "ldc2"; 67 | return $url; 68 | } 69 | - ps: function SetUpDCompiler 70 | { 71 | $env:toolchain = "msvc"; 72 | if($env:DC -eq "dmd"){ 73 | echo "downloading ..."; 74 | $url = ResolveLatestDMD; 75 | echo $url; 76 | Invoke-WebRequest $url -OutFile "c:\dmd.7z"; 77 | echo "finished."; 78 | pushd c:\\; 79 | 7z x dmd.7z > $null; 80 | popd; 81 | } 82 | elseif($env:DC -eq "ldc"){ 83 | echo "downloading ..."; 84 | $url = ResolveLatestLDC; 85 | echo $url; 86 | Invoke-WebRequest $url -OutFile "c:\ldc.zip"; 87 | echo "finished."; 88 | pushd c:\\; 89 | 7z x ldc.zip > $null; 90 | popd; 91 | } 92 | } 93 | - ps: SetUpDCompiler 94 | 95 | build_script: 96 | - ps: if($env:arch -eq "x86"){ 97 | $env:compilersetupargs = "x86"; 98 | $env:Darch = "x86"; 99 | $env:DConf = "m32"; 100 | }elseif($env:arch -eq "x64"){ 101 | $env:compilersetupargs = "amd64"; 102 | $env:Darch = "x86_64"; 103 | $env:DConf = "m64"; 104 | } 105 | - ps: $env:compilersetup = "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall"; 106 | - '"%compilersetup%" %compilersetupargs%' 107 | 108 | test_script: 109 | - echo %PLATFORM% 110 | - echo %Darch% 111 | - echo %DC% 112 | - echo %PATH% 113 | - '%DC% --version' 114 | - dub test --arch=%Darch% --compiler=%DC% 115 | -------------------------------------------------------------------------------- /source/barcode/code39.d: -------------------------------------------------------------------------------- 1 | /// 2 | module barcode.code39; 3 | 4 | import std.experimental.logger; 5 | 6 | import std.exception; 7 | import std.string; 8 | import std.range; 9 | import std.array; 10 | import std.typecons : tuple; 11 | 12 | import barcode.types; 13 | import barcode.util; 14 | 15 | /// 16 | class Code39 : BarCodeEncoder 17 | { 18 | pure: 19 | /// 20 | this(AppendCheckSum acs=AppendCheckSum.no) { appendCheckSum = acs; } 21 | 22 | /// 23 | AppendCheckSum appendCheckSum; 24 | 25 | /// 26 | override BarCode encode(string str) 27 | { 28 | checkStr(str); 29 | 30 | BitArray ret; 31 | 32 | void append(char ch) { ret.addBits(table[ch]); } 33 | 34 | append('*'); // start 35 | 36 | ushort checkSum; 37 | 38 | foreach (char c; str) 39 | { 40 | append(c); 41 | checkSum += checkVal[c]; 42 | } 43 | 44 | checkSum %= 43; 45 | 46 | if (appendCheckSum) 47 | append(checkValInv[checkSum]); 48 | 49 | append('*'); // stop 50 | 51 | return BarCode(ret.length, ret, "code39"); 52 | } 53 | } 54 | 55 | private: 56 | 57 | void checkStr(string str) pure @safe 58 | { 59 | foreach (char c; str) 60 | { 61 | enforce(c != '*', "symbol '*' is not allowed in code39"); 62 | enforce(c in table, "symbol '" ~ c ~ "' is not allowed in code39"); 63 | } 64 | } 65 | 66 | Bits!uint drawMask()(auto ref const Bits!ushort mask) 67 | { 68 | enum X = 1; 69 | enum W = 3; 70 | 71 | uint val; 72 | uint cur = X; // past gap 73 | 74 | foreach (i; 0 .. mask.count) 75 | { 76 | auto black = (i%2) == 0; 77 | auto n = mask[i] ? W : X; 78 | foreach (k; 0 .. n) 79 | val |= black << cur++; 80 | } 81 | 82 | return Bits!uint(cur, val); 83 | } 84 | 85 | @safe 86 | unittest 87 | { 88 | auto v = drawMask(bitsStr!"--#--#-"); 89 | assert(v == bitsStr!"#-###-#---#-"); 90 | } 91 | 92 | enum Bits!uint[char] table = src_table.getDict!((i,a) => tuple(a.ch, drawMask(a.mask))); 93 | enum ushort[char] checkVal = src_table.getDict!((i,a) => tuple(a.ch, i)); 94 | enum char[ushort] checkValInv = src_table.getDict!((i,a) => tuple(cast(ushort)i, a.ch)); 95 | 96 | struct Sym { char ch; Bits!ushort mask; } 97 | 98 | @safe 99 | unittest 100 | { 101 | // W=3 102 | assert(table['0'] == bitsStr!"#-#---###-###-#-"); 103 | } 104 | 105 | enum src_table = 106 | [ // used flags of width lines and gaps: - (narow), # (wide) 107 | // width controls in drawMask function (X and W enums) 108 | Sym('0', bitsStr!"---##-#--"), 109 | Sym('1', bitsStr!"#--#----#"), 110 | Sym('2', bitsStr!"--##----#"), 111 | Sym('3', bitsStr!"#-##-----"), 112 | Sym('4', bitsStr!"---##---#"), 113 | Sym('5', bitsStr!"#--##----"), 114 | Sym('6', bitsStr!"--###----"), 115 | Sym('7', bitsStr!"---#--#-#"), 116 | Sym('8', bitsStr!"#--#--#--"), 117 | Sym('9', bitsStr!"--##--#--"), 118 | Sym('A', bitsStr!"#----#--#"), 119 | Sym('B', bitsStr!"--#--#--#"), 120 | Sym('C', bitsStr!"#-#--#---"), 121 | Sym('D', bitsStr!"----##--#"), 122 | Sym('E', bitsStr!"#---##---"), 123 | Sym('F', bitsStr!"--#-##---"), 124 | Sym('G', bitsStr!"-----##-#"), 125 | Sym('H', bitsStr!"#----##--"), 126 | Sym('I', bitsStr!"--#--##--"), 127 | Sym('J', bitsStr!"----###--"), 128 | Sym('K', bitsStr!"#------##"), 129 | Sym('L', bitsStr!"--#----##"), 130 | Sym('M', bitsStr!"#-#----#-"), 131 | Sym('N', bitsStr!"----#--##"), 132 | Sym('O', bitsStr!"#---#--#-"), 133 | Sym('P', bitsStr!"--#-#--#-"), 134 | Sym('Q', bitsStr!"------###"), 135 | Sym('R', bitsStr!"#-----##-"), 136 | Sym('S', bitsStr!"--#---##-"), 137 | Sym('T', bitsStr!"----#-##-"), 138 | Sym('U', bitsStr!"##------#"), 139 | Sym('V', bitsStr!"-##-----#"), 140 | Sym('W', bitsStr!"###------"), 141 | Sym('X', bitsStr!"-#--#---#"), 142 | Sym('Y', bitsStr!"##--#----"), 143 | Sym('Z', bitsStr!"-##-#----"), 144 | Sym('-', bitsStr!"-#----#-#"), 145 | Sym('.', bitsStr!"##----#--"), 146 | Sym(' ', bitsStr!"-##---#--"), 147 | Sym('$', bitsStr!"-#-#-#---"), 148 | Sym('/', bitsStr!"-#-#---#-"), 149 | Sym('+', bitsStr!"-#---#-#-"), 150 | Sym('%', bitsStr!"---#-#-#-"), 151 | Sym('*', bitsStr!"-#--#-#--") 152 | ]; 153 | -------------------------------------------------------------------------------- /source/barcode/svgdraw.d: -------------------------------------------------------------------------------- 1 | /// 2 | module barcode.svgdraw; 3 | 4 | import std.string : format, join; 5 | import std.conv : text; 6 | 7 | import barcode.types; 8 | 9 | /// 10 | interface BarCodeSvgDrawer 11 | { 12 | /// 13 | string draw(BarCode bc); 14 | } 15 | 16 | /// 17 | class BaseBarCodeSvgDrawer : BarCodeSvgDrawer 18 | { 19 | /// 20 | bool fixSizeMode = false; 21 | /// 22 | bool withBackground = true; 23 | 24 | /// 25 | float borderX=50.0f, borderY=50.0f; 26 | /// 27 | float W=10.0f, H=10.0f; 28 | 29 | string bgColor = "#FFFFFF"; 30 | string fgColor = "#000000"; 31 | 32 | /// 33 | struct DrawData 34 | { 35 | /// 36 | string svgpath; 37 | /// 38 | long w, h; 39 | } 40 | 41 | DrawData buildPath(BarCode bc) 42 | { 43 | auto cW = W, cH = H; 44 | 45 | if (fixSizeMode) 46 | { 47 | cW /= bc.width; 48 | cH /= bc.height; 49 | } 50 | 51 | string[] paths; 52 | 53 | long start = -1; 54 | float len = 0; 55 | 56 | foreach (size_t y; 0..bc.height) 57 | foreach (size_t x; 0..bc.width) 58 | { 59 | if (bc[x,y]) 60 | { 61 | if (start == -1) 62 | start = x; 63 | len += cW; 64 | } 65 | 66 | if ((!bc[x,y] || x == bc.width-1) && start != -1) 67 | { 68 | paths ~= "M%s,%sh%sv%sh-%sz" 69 | .format(start*cW+borderX, 70 | y*cH+borderY, len, cH, len); 71 | start = -1; 72 | len = 0; 73 | } 74 | } 75 | 76 | long w = cast(long)(bc.width * cW + borderX * 2); 77 | long h = cast(long)(bc.height * cH + borderY * 2); 78 | 79 | return DrawData(paths.join(" "), w, h); 80 | } 81 | 82 | /// 83 | string draw(BarCode bc) 84 | { 85 | string bgStr; 86 | if (withBackground) 87 | bgStr = ``; 88 | 89 | auto dd = buildPath(bc); 90 | 91 | return text(` 92 | 93 | 94 | 95 | `, bgStr, ` 96 | 97 | `).flineOffset; 98 | } 99 | } 100 | 101 | class PseudoRasterBarCodeSvgDrawer : BaseBarCodeSvgDrawer 102 | { 103 | override DrawData buildPath(BarCode bc) 104 | { 105 | auto cW = W, cH = H; 106 | 107 | if (fixSizeMode) 108 | { 109 | cW /= bc.width; 110 | cH /= bc.height; 111 | } 112 | 113 | string[] paths; 114 | 115 | long start = -1; 116 | float len = 0; 117 | 118 | foreach (size_t y; 0..bc.height) 119 | foreach (size_t x; 0..bc.width) 120 | { 121 | if (bc[x,y]) 122 | paths ~= "M%s,%sh%sv%sh-%sz" 123 | .format(x*cW+borderX, y*cH+borderY, cW, cH, cW); 124 | } 125 | 126 | long w = cast(long)(bc.width * cW + borderX * 2); 127 | long h = cast(long)(bc.height * cH + borderY * 2); 128 | 129 | return DrawData(paths.join(" "), w, h); 130 | } 131 | } 132 | 133 | string flineOffset(string txt) @property @safe 134 | { 135 | import std.string; 136 | import std.algorithm; 137 | string[] res; 138 | ptrdiff_t offset = -1; 139 | foreach (ln; txt.splitLines.map!(a=>a.stripRight)) 140 | { 141 | // skip empty lines 142 | auto sln = ln.strip; 143 | if (sln.length == 0) 144 | { 145 | if (res.length) res ~= ""; 146 | continue; 147 | } 148 | 149 | if (offset == -1) 150 | offset = ln.length - sln.length; 151 | 152 | res ~= ln[min(offset, ln.length - sln.length)..$]; 153 | } 154 | return res.join("\n"); 155 | } 156 | 157 | @safe 158 | unittest 159 | { 160 | enum txt = (" \n some \n text \n "~ 161 | " \n here \n\n end ").flineOffset; 162 | enum exp = "some\ntext\n\n here\n\nend"; 163 | 164 | static assert(txt == exp); 165 | } 166 | 167 | @safe 168 | unittest 169 | { 170 | enum txt = ` 171 | some text 172 | with 173 | wrong formated 174 | lines`.flineOffset; 175 | enum exp = "some text\n" ~ 176 | "with\n" ~ 177 | "wrong formated\n" ~ 178 | " lines"; 179 | static assert(txt == exp); 180 | } -------------------------------------------------------------------------------- /source/barcode/util.d: -------------------------------------------------------------------------------- 1 | module barcode.util; 2 | 3 | import std.exception; 4 | import std.typecons : Flag; 5 | import std.bitmanip : bitfields; 6 | 7 | public import std.typecons : tuple; 8 | public import std.bitmanip : BitArray; 9 | 10 | alias AppendCheckSum = Flag!"appendCheckSum"; 11 | 12 | /// 13 | struct Bits(T=ulong) 14 | if (is(T==ubyte) || is(T==ushort) || is(T==uint) || is(T==ulong)) 15 | { 16 | static if (is(T==ubyte)) enum COUNTBITS = 3; 17 | else static if (is(T==ushort)) enum COUNTBITS = 4; 18 | else static if (is(T==uint)) enum COUNTBITS = 5; 19 | else static if (is(T==ulong)) enum COUNTBITS = 6; 20 | 21 | enum VALUEBITS = T.sizeof * 8 - COUNTBITS; 22 | 23 | mixin(bitfields!(ubyte, "count", COUNTBITS, 24 | T, "value", VALUEBITS)); 25 | 26 | alias value this; 27 | 28 | 29 | pure nothrow @nogc @safe: 30 | 31 | /// 32 | this(ulong cnt, ulong val) 33 | { 34 | count = cast(ubyte)cnt; 35 | value = cast(T)val; 36 | } 37 | 38 | /// 39 | bool opIndex(size_t i) const 40 | { return cast(bool)((value>>i)&1); } 41 | 42 | /// 43 | bool opEquals(X)(auto ref const Bits!X v) const 44 | { return v.value == value && v.count == count; } 45 | 46 | /// 47 | bool opEquals(X)(X v) const 48 | if (!is(X : Bits!U, U)) 49 | { return v == value; } 50 | } 51 | 52 | @safe 53 | unittest 54 | { 55 | static assert(bitsStr!"---".count == 3); 56 | static assert(bitsStr!"--".count == 2); 57 | static assert(bitsStr!"---" != bitsStr!"--"); 58 | } 59 | 60 | void addBits(ref BitArray ba, size_t value, int bits) @safe pure 61 | { 62 | enforce(bits <= size_t.sizeof*8, "so many bits"); 63 | enforce(bits >= 0, "bits must be more that 0"); 64 | addBits(ba, Bits!ulong(bits, value)); 65 | } 66 | 67 | @trusted // BitArray 68 | unittest 69 | { 70 | BitArray ba; 71 | ba.addBits(0b11000111010, 11); 72 | const tst = BitArray([1,1,0,0,0,1,1,1,0,1,0]); 73 | assert (ba == tst); 74 | } 75 | 76 | void addBits(T)(ref BitArray ba, auto ref const(Bits!T) bits) @trusted pure 77 | { foreach_reverse (i; 0 .. bits.count) ba ~= bits[i]; } 78 | 79 | @trusted // BitArray 80 | unittest 81 | { 82 | BitArray ba; 83 | ba.addBits(bitsStr!"##---###-#-"); 84 | auto tst = BitArray([1,1,0,0,0,1,1,1,0,1,0]); 85 | assert (ba == tst); 86 | } 87 | 88 | // for more readable bits writing 89 | template bitsStr(string mask, char ONE='#') 90 | if (mask.length <= 58) 91 | { 92 | static pure auto bitsImpl(T)() nothrow @nogc 93 | { 94 | ulong ret; 95 | foreach (i; 0 .. mask.length) 96 | ret |= cast(ulong)(mask[i] == ONE) << (mask.length - 1 - i); 97 | return Bits!T(mask.length, ret); 98 | } 99 | 100 | static if (mask.length < 6) alias S = ubyte; 101 | else static if (mask.length < 13) alias S = ushort; 102 | else static if (mask.length < 28) alias S = uint; 103 | else alias S = ulong; 104 | 105 | static if (mask.length >= 1) enum bitsStr = bitsImpl!S(); 106 | else static assert(0, "can't create 0 bits value"); 107 | } 108 | 109 | @safe 110 | unittest 111 | { 112 | assert(1 == bitsStr!"#"); 113 | assert(0 == bitsStr!"-"); 114 | assert(0b1100011101011 == bitsStr!"##---###-#-##"); 115 | assert(bitsStr!"---".count == 3); 116 | } 117 | 118 | auto getDict(alias F, T)(T[] arr) 119 | { 120 | alias frt = typeof(F(size_t(0), T.init)); 121 | alias KEY = typeof(frt.init[0]); 122 | alias VAL = typeof(frt.init[1]); 123 | 124 | VAL[KEY] ret; 125 | foreach (i, e; arr) 126 | { 127 | auto tmp = F(i, e); 128 | ret[tmp[0]] = tmp[1]; 129 | } 130 | return ret; 131 | } 132 | 133 | @safe 134 | unittest 135 | { 136 | static struct X { char ch; ushort mask; } 137 | enum data = [ X('0', 0b001), X('1', 0b010), X('2', 0b100), ]; 138 | 139 | { 140 | enum ushort[char] t = getDict!((i,a) => tuple(a.ch, a.mask))(data); 141 | static assert(t.keys.length == 3); 142 | assert('0' in t); 143 | assert(t['0'] == 0b001); 144 | assert('1' in t); 145 | assert(t['1'] == 0b010); 146 | assert('2' in t); 147 | assert(t['2'] == 0b100); 148 | } 149 | 150 | { 151 | enum ushort[char] t = getDict!((i,a) => tuple(a.ch, i))(data); 152 | static assert(t.keys.length == 3); 153 | assert('0' in t); 154 | assert(t['0'] == 0); 155 | assert('1' in t); 156 | assert(t['1'] == 1); 157 | assert('2' in t); 158 | assert(t['2'] == 2); 159 | } 160 | 161 | { 162 | enum char[ubyte] t = getDict!((i,a) => tuple(cast(ubyte)i, a.ch))(data); 163 | static assert(t.keys.length == 3); 164 | assert(0 in t); 165 | assert(t[0] == '0'); 166 | assert(1 in t); 167 | assert(t[1] == '1'); 168 | assert(2 in t); 169 | assert(t[2] == '2'); 170 | } 171 | } -------------------------------------------------------------------------------- /source/barcode/qr/qrsegment.d: -------------------------------------------------------------------------------- 1 | module barcode.qr.qrsegment; 2 | 3 | import std.algorithm; 4 | import std.exception; 5 | import std.ascii; 6 | 7 | import barcode.qr.util; 8 | 9 | struct QrSegment 10 | { 11 | pure @safe: 12 | static struct Mode 13 | { 14 | enum numeric = Mode(1, [10, 12, 14]), 15 | alphanumeric = Mode(2, [ 9, 11, 13]), 16 | bytes = Mode(4, [ 8, 16, 16]), 17 | kanji = Mode(8, [ 8, 10, 12]); 18 | 19 | ubyte bits; 20 | private ubyte[3] cc; 21 | 22 | pure @safe: 23 | ubyte numCharCountBits(int ver) const 24 | { 25 | if ( 1 <= ver && ver <= 9) return cc[0]; 26 | else if (10 <= ver && ver <= 26) return cc[1]; 27 | else if (27 <= ver && ver <= 40) return cc[2]; 28 | else throw new Exception("Version number out of range"); 29 | } 30 | } 31 | 32 | Mode mode; 33 | int numChars; 34 | ubyte[] data; 35 | int bitLength; 36 | 37 | static QrSegment makeBytes(const(ubyte)[] d) 38 | { return QrSegment(Mode.bytes, cast(int)d.length, d, cast(int)d.length*8); } 39 | 40 | static QrSegment makeNumeric(string digits) 41 | { 42 | BitBuffer bb; 43 | int accumData = 0; 44 | int accumCount = 0; 45 | int charCount = 0; 46 | foreach (char c; digits) 47 | { 48 | if (c < '0' || c > '9') 49 | throw new Exception("String contains non-numeric " ~ 50 | "characters in numeric mode"); 51 | accumData = accumData * 10 + (c - '0'); 52 | accumCount++; 53 | if (accumCount == 3) 54 | { 55 | bb.appendBits(accumData, 10); 56 | accumData = 0; 57 | accumCount = 0; 58 | } 59 | charCount++; 60 | } 61 | if (accumCount > 0) // 1 or 2 digits remaining 62 | bb.appendBits(accumData, accumCount * 3 + 1); 63 | return QrSegment(Mode.numeric, charCount, bb.getBytes, cast(int)bb.length); 64 | } 65 | 66 | static QrSegment makeAlphanumeric(string text) 67 | { 68 | BitBuffer bb; 69 | int accumData = 0; 70 | int accumCount = 0; 71 | int charCount = 0; 72 | foreach (char c; text) 73 | { 74 | if (c < ' ' || c > 'Z') 75 | throw new Exception("String contains unencodable " ~ 76 | "characters in alphanumeric mode"); 77 | accumData = accumData * 45 + encodingTable[c - ' ']; 78 | accumCount++; 79 | if (accumCount == 2) 80 | { 81 | bb.appendBits(accumData, 11); 82 | accumData = 0; 83 | accumCount = 0; 84 | } 85 | charCount++; 86 | } 87 | if (accumCount > 0) // 1 character remaining 88 | bb.appendBits(accumData, 6); 89 | return QrSegment(Mode.alphanumeric, charCount, bb.getBytes, cast(int)bb.length); 90 | } 91 | 92 | static QrSegment[] makeSegments(string text) 93 | { 94 | // Select the most efficient segment encoding automatically 95 | if (text.length == 0) return []; 96 | else if (QrSegment.isNumeric(text)) 97 | return [QrSegment.makeNumeric(text)]; 98 | else if (QrSegment.isAlphanumeric(text)) 99 | return [QrSegment.makeAlphanumeric(text)]; 100 | else 101 | return [QrSegment.makeBytes(cast(ubyte[])text.dup)]; 102 | } 103 | 104 | static bool isAlphanumeric(string text) 105 | { 106 | return text.map!(a=>cast(int)a) 107 | .all!(c => ' ' <= c && c <= 'Z' && encodingTable[c - ' '] != -1); 108 | } 109 | 110 | static bool isNumeric(string text) { return text.all!isDigit; } 111 | 112 | pure this(Mode md, int nc, const(ubyte)[] d, int bl) 113 | { 114 | mode = md; 115 | numChars = nc; 116 | data = d.dup; 117 | bitLength = bl; 118 | } 119 | 120 | static int getTotalBits(const(QrSegment)[] segs, int vers) 121 | { 122 | enforce(1 <= vers && vers <= 40, "unknown vers"); 123 | int result = 0; 124 | foreach (seg; segs) 125 | { 126 | int ccbits = seg.mode.numCharCountBits(vers); 127 | if (seg.numChars >= (1 << ccbits)) 128 | return -1; 129 | result += 4 + ccbits + seg.bitLength; 130 | } 131 | return result; 132 | } 133 | 134 | private: 135 | enum byte[59] encodingTable = [ 136 | // SP, !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ;, <, =, >, ?, @, // ASCII codes 32 to 64 137 | 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, -1, // Array indices 0 to 32 138 | 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, // Array indices 33 to 58 139 | // A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, // ASCII codes 65 to 90 140 | ]; 141 | } 142 | -------------------------------------------------------------------------------- /source/barcode/code128.d: -------------------------------------------------------------------------------- 1 | /// 2 | module barcode.code128; 3 | 4 | import std.experimental.logger; 5 | 6 | import std.exception; 7 | import std.string; 8 | import std.range; 9 | import std.array; 10 | import std.typecons : tuple; 11 | 12 | import barcode.types; 13 | import barcode.util; 14 | 15 | /// 16 | class Code128 : BarCodeEncoder 17 | { 18 | enum stopSymbol = bitsStr!"##---###-#-##"; 19 | 20 | this() { } 21 | 22 | override BarCode encode(string str) 23 | { 24 | auto syms = parseStrToSymbol(str); 25 | auto chsm = calcCheckSumm(syms); 26 | 27 | BitArray ret; 28 | 29 | foreach (s; syms) 30 | ret.addBits(s.mask); 31 | 32 | ret.addBits(chsm.mask); 33 | ret.addBits(stopSymbol); 34 | return BarCode(ret.length, ret, "code128"); 35 | } 36 | } 37 | 38 | private: 39 | 40 | struct Sym 41 | { 42 | size_t num; 43 | string A, B, C; 44 | Bits!ushort mask; 45 | 46 | this(string a, string b, string c, Bits!ushort bts) pure 47 | { 48 | A = a; 49 | B = b; 50 | C = c; 51 | mask = bts; 52 | } 53 | } 54 | 55 | Sym symByNum(size_t num) pure @safe { return src_table[num]; } 56 | 57 | Sym symByA(const(char)[] ch...) @safe { return src_table[sym_by_a[ch.idup]]; } 58 | Sym symByB(const(char)[] ch...) @safe { return src_table[sym_by_b[ch.idup]]; } 59 | Sym symByC(const(char)[] ch...) @safe { return src_table[sym_by_c[ch.idup]]; } 60 | 61 | @safe 62 | unittest 63 | { 64 | assert(symByA("E") == symByB("E")); 65 | char[] tmp = ['E']; 66 | assert(symByB(tmp) == symByB("E")); 67 | } 68 | 69 | enum size_t[string] sym_by_a = src_table.getDict!((i,v) => tuple(v.A, i)); 70 | enum size_t[string] sym_by_b = src_table.getDict!((i,v) => tuple(v.B, i)); 71 | enum size_t[string] sym_by_c = src_table.getDict!((i,v) => tuple(v.C, i)); 72 | 73 | Sym[] parseStrToSymbol(string str, int state=0) @safe 74 | { 75 | import std.algorithm; 76 | import std.ascii; 77 | 78 | enforce(0 <= state && state <= 3, "unknown last state"); 79 | 80 | Sym[] setA() 81 | { 82 | auto ret = [0: [symByA(StartA)], 83 | 1: [/+ already in A mode +/], 84 | 2: [symByB(CODE_A)], 85 | 3: [symByC(CODE_A)]][state]; 86 | state = 1; 87 | return ret; 88 | } 89 | 90 | Sym[] setB() 91 | { 92 | auto ret = [0: [symByA(StartB)], 93 | 1: [symByA(CODE_B)], 94 | 2: [/+ already in B mode +/], 95 | 3: [symByC(CODE_B)]][state]; 96 | state = 2; 97 | return ret; 98 | } 99 | 100 | Sym[] setC() @safe 101 | { 102 | auto ret = [0: [symByA(StartC)], 103 | 1: [symByA(CODE_C)], 104 | 2: [symByB(CODE_C)], 105 | 3: [/+ already in C +/]][state]; 106 | state = 3; 107 | return ret; 108 | } 109 | 110 | Sym[] encA(const(char)[] ch...) @safe { return setA ~ symByA(ch); } 111 | Sym[] encB(const(char)[] ch...) @safe { return setB ~ symByB(ch); } 112 | Sym[] encC(const(char)[] ch...) @safe { return setC ~ symByC(ch); } 113 | 114 | Sym[] encState(const(char)[] ch...) @safe 115 | { 116 | if (state == 1) return [symByA(ch)]; 117 | else return setB ~ symByB(ch); 118 | } 119 | 120 | Sym[] encShift(const(char)[] ch...) @safe 121 | { 122 | if (state == 1) return [symByA(Shift)] ~ symByB(ch); 123 | else if (state == 2) return [symByB(Shift)] ~ symByA(ch); 124 | else assert(0, "logic error in code"); 125 | } 126 | 127 | Sym[] encSwitch(const(char)[] ch...) @safe 128 | { return [1: encB(ch), 2: encA(ch)][state]; } 129 | 130 | bool isOtherSpec(const(char)[] ch...) @safe 131 | { 132 | if (state == 1) return SpecB.canFind(ch); 133 | else if (state == 2) return SpecA.canFind(ch); 134 | else return false; 135 | } 136 | 137 | bool isSpec(const(char)[] ch...) @safe 138 | { 139 | if (state == 1) return SpecA.canFind(ch); 140 | else if (state == 2) return SpecB.canFind(ch); 141 | else return false; 142 | } 143 | 144 | Sym[] ret; 145 | 146 | while (str.length) 147 | { 148 | auto dc = str.digitsSequenceCount; 149 | if (dc >= 4 && (state == 0 || (dc % 2)==0)) 150 | { 151 | while (str.digitsSequenceCount >= 2) 152 | { 153 | ret ~= encC(str[0..2]); 154 | str = str[2..$]; 155 | } 156 | } 157 | else 158 | { 159 | auto ch = str[0]; 160 | auto sAc = str.specACount, sBc = str.specBCount; 161 | if (!sAc && !sBc) ret ~= encState(ch); 162 | else if ( sAc && !sBc) ret ~= encA(ch); 163 | else if ( sBc && !sAc) ret ~= encB(ch); 164 | else if (str.length >= 2) // if str.length == 1 one of first 3 statements are was true 165 | { 166 | if (isOtherSpec(ch)) 167 | { 168 | if (isSpec(str[1])) ret ~= encShift(ch); 169 | else ret ~= encSwitch(ch); 170 | } 171 | else 172 | ret ~= encState(ch); 173 | } 174 | else assert(0, "logic error in code"); 175 | 176 | str = str[1..$]; 177 | } 178 | } 179 | 180 | return ret; 181 | } 182 | 183 | @safe 184 | unittest 185 | { 186 | import std.algorithm : equal; 187 | import std.conv : text; 188 | 189 | { 190 | auto g = parseStrToSymbol("EIA50-1234-123456"); 191 | auto m = getSymbol(StartB, "E", "I", "A", "5", "0", 192 | "-", CODE_C, "12", "34", 193 | CODE_B, "-", CODE_C, "12", "34", "56"); 194 | assert(equal(m, g)); 195 | } 196 | { 197 | auto g = parseStrToSymbol("EIA50-12345-1234567"); 198 | auto m = getSymbol(StartB, "E", "I", "A", "5", "0", 199 | "-", "1", CODE_C, "23", "45", 200 | CODE_B, "-", "1", CODE_C, "23", "45", "67"); 201 | assert(equal(m, g), text("\n", m, "\n", g)); 202 | } 203 | { 204 | auto g = parseStrToSymbol("oneOFthis\0ABC"); 205 | auto m = getSymbol(StartB, "o", "n", "e", "O", "F", "t", "h", "i", "s", 206 | CODE_A, NUL, "A", "B", "C"); 207 | assert(equal(m, g), text("\n", m, "\n", g)); 208 | } 209 | } 210 | 211 | size_t digitsSequenceCount(string str) @safe pure nothrow @nogc 212 | { 213 | import std.ascii; 214 | foreach (i; 0 .. str.length) 215 | if (!str[i].isDigit) 216 | return i; 217 | return str.length; 218 | } 219 | 220 | @safe 221 | unittest 222 | { 223 | assert("0123".digitsSequenceCount == 4); 224 | assert("01ab23".digitsSequenceCount == 2); 225 | assert("0431ab23".digitsSequenceCount == 4); 226 | assert("ab0431ab23".digitsSequenceCount == 0); 227 | } 228 | 229 | size_t specACount(string str) @safe 230 | { 231 | import std.algorithm; 232 | size_t ret; 233 | foreach (i; 0 .. str.length) 234 | if (SpecA.canFind(""~str[i])) 235 | ret++; 236 | return ret; 237 | } 238 | 239 | size_t specBCount(string str) @safe 240 | { 241 | import std.algorithm; 242 | size_t ret; 243 | foreach (i; 0 .. str.length) 244 | if (SpecB.canFind(""~str[i])) 245 | ret++; 246 | return ret; 247 | } 248 | 249 | Sym calcCheckSumm(Sym[] symbol) pure @safe 250 | { 251 | enforce(symbol.length >= 1); 252 | 253 | size_t tmp = symbol[0].num; 254 | foreach(i, sym; symbol) 255 | tmp += sym.num * i; // first in tmp yet 256 | tmp %= 103; 257 | return symByNum(tmp); 258 | } 259 | 260 | @safe 261 | unittest 262 | { 263 | auto arr = [StartB, "A", "I", "M", CODE_C, "12", "34"]; 264 | assert(calcCheckSumm(getSymbol(arr)).num == 87); 265 | } 266 | 267 | Sym[] getSymbol(string[] arr...) @safe 268 | { 269 | enum State { A,B,C } 270 | State curr; 271 | switch (arr[0]) 272 | { 273 | case StartA: curr = State.A; break; 274 | case StartB: curr = State.B; break; 275 | case StartC: curr = State.C; break; 276 | default: throw new Exception("arr mush starts from StartX symbol"); 277 | } 278 | bool shift; 279 | Sym symByCurr(string v) 280 | { 281 | final switch (curr) 282 | { 283 | case State.A: return shift ? symByB(v) : symByA(v); 284 | case State.B: return shift ? symByA(v) : symByB(v); 285 | case State.C: return symByC(v); 286 | } 287 | } 288 | 289 | Sym[] ret = [symByA(arr[0])]; 290 | 291 | foreach (v; arr[1..$]) 292 | { 293 | ret ~= symByCurr(v); 294 | shift = false; 295 | switch (v) 296 | { 297 | case CODE_A: curr = State.A; break; 298 | case CODE_B: curr = State.B; break; 299 | case CODE_C: curr = State.C; break; 300 | case Shift: shift = true; break; 301 | default: break; 302 | } 303 | } 304 | 305 | return ret; 306 | } 307 | 308 | auto setNum(Sym[] tbl) { foreach (i, ref v; tbl) v.num = i; return tbl; } 309 | 310 | enum src_table = 311 | [ // used origin mask: # (black), - (white) 312 | Sym( ` `, ` `, "00", bitsStr!"##-##--##--"), 313 | Sym( `!`, `!`, "01", bitsStr!"##--##-##--"), 314 | Sym( `"`, `"`, "02", bitsStr!"##--##--##-"), 315 | Sym( `#`, `#`, "03", bitsStr!"#--#--##---"), 316 | Sym( `$`, `$`, "04", bitsStr!"#--#---##--"), 317 | Sym( `%`, `%`, "05", bitsStr!"#---#--##--"), 318 | Sym( `&`, `&`, "06", bitsStr!"#--##--#---"), 319 | Sym( `'`, `'`, "07", bitsStr!"#--##---#--"), 320 | Sym( `(`, `(`, "08", bitsStr!"#---##--#--"), 321 | Sym( `)`, `)`, "09", bitsStr!"##--#--#---"), 322 | Sym( `*`, `*`, "10", bitsStr!"##--#---#--"), 323 | Sym( `+`, `+`, "11", bitsStr!"##---#--#--"), 324 | Sym( `,`, `,`, "12", bitsStr!"#-##--###--"), 325 | Sym( `-`, `-`, "13", bitsStr!"#--##-###--"), 326 | Sym( `.`, `.`, "14", bitsStr!"#--##--###-"), 327 | Sym( `/`, `/`, "15", bitsStr!"#-###--##--"), 328 | Sym( `0`, `0`, "16", bitsStr!"#--###-##--"), 329 | Sym( `1`, `1`, "17", bitsStr!"#--###--##-"), 330 | Sym( `2`, `2`, "18", bitsStr!"##--###--#-"), 331 | Sym( `3`, `3`, "19", bitsStr!"##--#-###--"), 332 | Sym( `4`, `4`, "20", bitsStr!"##--#--###-"), 333 | Sym( `5`, `5`, "21", bitsStr!"##-###--#--"), 334 | Sym( `6`, `6`, "22", bitsStr!"##--###-#--"), 335 | Sym( `7`, `7`, "23", bitsStr!"###-##-###-"), 336 | Sym( `8`, `8`, "24", bitsStr!"###-#--##--"), 337 | Sym( `9`, `9`, "25", bitsStr!"###--#-##--"), 338 | Sym( `:`, `:`, "26", bitsStr!"###--#--##-"), 339 | Sym( `;`, `;`, "27", bitsStr!"###-##--#--"), 340 | Sym( `<`, `<`, "28", bitsStr!"###--##-#--"), 341 | Sym( `=`, `=`, "29", bitsStr!"###--##--#-"), 342 | Sym( `>`, `>`, "30", bitsStr!"##-##-##---"), 343 | Sym( `?`, `?`, "31", bitsStr!"##-##---##-"), 344 | Sym( `@`, `@`, "32", bitsStr!"##---##-##-"), 345 | Sym( `A`, `A`, "33", bitsStr!"#-#---##---"), 346 | Sym( `B`, `B`, "34", bitsStr!"#---#-##---"), 347 | Sym( `C`, `C`, "35", bitsStr!"#---#---##-"), 348 | Sym( `D`, `D`, "36", bitsStr!"#-##---#---"), 349 | Sym( `E`, `E`, "37", bitsStr!"#---##-#---"), 350 | Sym( `F`, `F`, "38", bitsStr!"#---##---#-"), 351 | 352 | Sym( `G`, `G`, "39", bitsStr!"##-#---#---"), 353 | Sym( `H`, `H`, "40", bitsStr!"##---#-#---"), 354 | Sym( `I`, `I`, "41", bitsStr!"##---#---#-"), 355 | Sym( `J`, `J`, "42", bitsStr!"#-##-###---"), 356 | Sym( `K`, `K`, "43", bitsStr!"#-##---###-"), 357 | Sym( `L`, `L`, "44", bitsStr!"#---##-###-"), 358 | Sym( `M`, `M`, "45", bitsStr!"#-###-##---"), 359 | Sym( `N`, `N`, "46", bitsStr!"#-###---##-"), 360 | Sym( `O`, `O`, "47", bitsStr!"#---###-##-"), 361 | Sym( `P`, `P`, "48", bitsStr!"###-###-##-"), 362 | Sym( `Q`, `Q`, "49", bitsStr!"##-#---###-"), 363 | Sym( `R`, `R`, "50", bitsStr!"##---#-###-"), 364 | Sym( `S`, `S`, "51", bitsStr!"##-###-#---"), 365 | Sym( `T`, `T`, "52", bitsStr!"##-###---#-"), 366 | Sym( `U`, `U`, "53", bitsStr!"##-###-###-"), 367 | Sym( `V`, `V`, "54", bitsStr!"###-#-##---"), 368 | Sym( `W`, `W`, "55", bitsStr!"###-#---##-"), 369 | Sym( `X`, `X`, "56", bitsStr!"###---#-##-"), 370 | Sym( `Y`, `Y`, "57", bitsStr!"###-##-#---"), 371 | Sym( `Z`, `Z`, "58", bitsStr!"###-##---#-"), 372 | Sym( `[`, `[`, "59", bitsStr!"###---##-#-"), 373 | Sym( `\`, `\`, "60", bitsStr!"###-####-#-"), 374 | Sym( `]`, `]`, "61", bitsStr!"##--#----#-"), 375 | Sym( `^`, `^`, "62", bitsStr!"####---#-#-"), 376 | Sym( `_`, `_`, "63", bitsStr!"#-#--##----"), 377 | Sym( NUL, "`", "64", bitsStr!"#-#----##--"), 378 | Sym( SOH, "a", "65", bitsStr!"#--#-##----"), 379 | Sym( STX, "b", "66", bitsStr!"#--#----##-"), 380 | Sym( ETX, "c", "67", bitsStr!"#----#-##--"), 381 | Sym( EOT, "d", "68", bitsStr!"#----#--##-"), 382 | Sym( ENQ, "e", "69", bitsStr!"#-##--#----"), 383 | Sym( ACK, "f", "70", bitsStr!"#-##----#--"), 384 | Sym( BEL, "g", "71", bitsStr!"#--##-#----"), 385 | Sym( BS, "h", "72", bitsStr!"#--##----#-"), 386 | Sym( HT, "i", "73", bitsStr!"#----##-#--"), 387 | Sym( LF, "j", "74", bitsStr!"#----##--#-"), 388 | Sym( VT, "k", "75", bitsStr!"##----#--#-"), 389 | Sym( FF, "l", "76", bitsStr!"##--#-#----"), 390 | Sym( CR, "m", "77", bitsStr!"####-###-#-"), 391 | Sym( SO, "n", "78", bitsStr!"##----#-#--"), 392 | Sym( SI, "o", "79", bitsStr!"#---####-#-"), 393 | 394 | Sym( DLE, "p", "80", bitsStr!"#-#--####--"), 395 | Sym( DC1, "q", "81", bitsStr!"#--#-####--"), 396 | Sym( DC2, "r", "82", bitsStr!"#--#--####-"), 397 | Sym( DC3, "s", "83", bitsStr!"#-####--#--"), 398 | Sym( DC4, "t", "84", bitsStr!"#--####-#--"), 399 | Sym( NAK, "u", "85", bitsStr!"#--####--#-"), 400 | Sym( SYN, "v", "86", bitsStr!"####-#--#--"), 401 | Sym( ETB, "w", "87", bitsStr!"####--#-#--"), 402 | Sym( CAN, "x", "88", bitsStr!"####--#--#-"), 403 | Sym( EM, "y", "89", bitsStr!"##-##-####-"), 404 | Sym( SUB, "z", "90", bitsStr!"##-####-##-"), 405 | Sym( ESC, "{", "91", bitsStr!"####-##-##-"), 406 | Sym( FS, "|", "92", bitsStr!"#-#-####---"), 407 | Sym( GS, "}", "93", bitsStr!"#-#---####-"), 408 | Sym( RS, "~", "94", bitsStr!"#---#-####-"), 409 | Sym( US, DEL, "95", bitsStr!"#-####-#---"), 410 | Sym( FNC3, FNC3, "96", bitsStr!"#-####---#-"), 411 | Sym( FNC2, FNC2, "97", bitsStr!"####-#-#---"), 412 | Sym( Shift, Shift, "98", bitsStr!"####-#---#-"), 413 | Sym(CODE_C, CODE_C, "99", bitsStr!"#-###-####-"), 414 | Sym(CODE_B, FNC4, CODE_B, bitsStr!"#-####-###-"), 415 | Sym( FNC4, CODE_A, CODE_A, bitsStr!"###-#-####-"), 416 | Sym( FNC1, FNC1, FNC1, bitsStr!"####-#-###-"), 417 | Sym(StartA, StartA, StartA, bitsStr!"##-#----#--"), 418 | Sym(StartB, StartB, StartB, bitsStr!"##-#--#----"), 419 | Sym(StartC, StartC, StartC, bitsStr!"##-#--###--"), 420 | ].setNum; 421 | 422 | @safe 423 | unittest 424 | { 425 | assert(symByB(StartB).num == 104); 426 | assert(symByB("A").num == 33); 427 | assert(symByB("I").num == 41); 428 | assert(symByB("M").num == 45); 429 | assert(symByB(CODE_C).num == 99); 430 | assert(symByC("12").num == 12); 431 | assert(symByC("34").num == 34); 432 | 433 | assert(symByB(CODE_A).num == 101); 434 | 435 | assert(symByA(FS) == symByB("|")); 436 | assert(symByA(FS) == symByC("92")); 437 | } 438 | 439 | string chSym(ubyte v) { return "" ~ cast(char)(v); } 440 | 441 | enum NUL = chSym( 0); 442 | enum SOH = chSym( 1); 443 | enum STX = chSym( 2); 444 | enum ETX = chSym( 3); 445 | enum EOT = chSym( 4); 446 | enum ENQ = chSym( 5); 447 | enum ACK = chSym( 6); 448 | enum BEL = chSym( 7); 449 | enum BS = chSym( 8); 450 | enum HT = chSym( 9); 451 | enum LF = chSym(10); 452 | enum VT = chSym(11); 453 | enum FF = chSym(12); 454 | enum CR = chSym(13); 455 | enum SO = chSym(14); 456 | enum SI = chSym(15); 457 | 458 | enum DLE = chSym(16); 459 | enum DC1 = chSym(17); 460 | enum DC2 = chSym(18); 461 | enum DC3 = chSym(19); 462 | enum DC4 = chSym(20); 463 | enum NAK = chSym(21); 464 | enum SYN = chSym(22); 465 | enum ETB = chSym(23); 466 | enum CAN = chSym(24); 467 | enum EM = chSym(25); 468 | enum SUB = chSym(26); 469 | enum ESC = chSym(27); 470 | enum FS = chSym(28); 471 | enum GS = chSym(29); 472 | enum RS = chSym(30); 473 | enum US = chSym(31); 474 | enum DEL = chSym(127); 475 | 476 | enum FNC1 = NUL ~ "!FNC1"; 477 | enum FNC2 = NUL ~ "!FNC2"; 478 | enum FNC3 = NUL ~ "!FNC3"; 479 | enum FNC4 = NUL ~ "!FNC4"; 480 | enum Shift = NUL ~ "!Shift"; 481 | enum CODE_A = NUL ~ "!CODE_A"; 482 | enum CODE_B = NUL ~ "!CODE_B"; 483 | enum CODE_C = NUL ~ "!CODE_C"; 484 | enum StartA = NUL ~ "!StartA"; 485 | enum StartB = NUL ~ "!StartB"; 486 | enum StartC = NUL ~ "!StartC"; 487 | 488 | enum SpecA = [ NUL, SOH, STX, ETX, EOT, ENQ, ACK, BEL, BS, HT, LF, VT, FF, 489 | CR, SO, SI, DLE, DC1, DC2, DC3, DC4, NAK, SYN, ETB, CAN, 490 | EM, SUB, ESC, FS, GS, RS, US ]; 491 | 492 | enum SpecB = ["`", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", 493 | "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", 494 | "z", "{", "|", "}", "~", DEL]; 495 | -------------------------------------------------------------------------------- /source/barcode/qr/qrcode.d: -------------------------------------------------------------------------------- 1 | module barcode.qr.qrcode; 2 | 3 | import std.experimental.logger; 4 | 5 | import std.math : abs; 6 | import std.algorithm; 7 | import std.exception; 8 | import std.string; 9 | import std.range; 10 | import std.bitmanip : BitArray; 11 | import std.array; 12 | import std.traits : EnumMembers; 13 | import std.typecons : Tuple, tuple; 14 | 15 | import barcode.qr.qrsegment; 16 | import barcode.qr.util; 17 | import barcode.qr.ecl; 18 | 19 | struct QrCode 20 | { 21 | pure @safe: 22 | static QrCode encodeSegments(QrSegment[] segs, ECL ecl, 23 | int minVer=1, int maxVer=40, int mask=-1, bool boostecl=true) 24 | { 25 | int sv = -1; 26 | int usedbits; 27 | foreach (v; minVer .. maxVer+1) 28 | { 29 | auto capasity = getNumDataCodewords(v, ecl) * 8; 30 | usedbits = QrSegment.getTotalBits(segs, v); 31 | if (usedbits != -1 && usedbits <= capasity) 32 | { 33 | sv = v; 34 | break; 35 | } 36 | enforce(v != maxVer, new Exception("Too big data for qr")); 37 | } 38 | 39 | if (boostecl) 40 | { 41 | foreach (newecl; [ECL.medium, ECL.quartile, ECL.high]) 42 | if (usedbits <= getNumDataCodewords(sv, newecl) * 8) 43 | ecl = newecl; 44 | } 45 | 46 | auto capacitybits = getNumDataCodewords(sv, ecl) * 8; 47 | 48 | BitBuffer bb; 49 | foreach (i, seg; segs) 50 | { 51 | bb.appendBits(seg.mode.bits, 4); 52 | bb.appendBits(seg.numChars, seg.mode.numCharCountBits(sv)); 53 | bb.appendData(seg.data, seg.bitLength); 54 | } 55 | 56 | bb.appendBits(0, min(4, capacitybits - bb.length)); 57 | bb.appendBits(0, (8 - bb.length % 8) % 8); 58 | 59 | foreach (pad; [0xEC, 0x11].cycle) 60 | { 61 | if (bb.length >= capacitybits) break; 62 | bb.appendBits(pad, 8); 63 | } 64 | 65 | assert (bb.length % 8 == 0); 66 | 67 | return QrCode(bb, mask, sv, ecl); 68 | } 69 | 70 | int vers; 71 | int size; 72 | ECL ecl; 73 | int mask; 74 | 75 | BitArray modules; 76 | BitArray isFunction; 77 | 78 | size_t crd(int x, int y) const nothrow { return size * y + x; } 79 | 80 | this(BitBuffer bb, int mask, uint vers, ECL ecl) @trusted 81 | { 82 | enforce(-1 <= mask && mask <= 7, "unknown mask"); 83 | enforce(1 <= vers && vers <= 40, "unknown vers"); 84 | enforce(bb.length == getNumDataCodewords(vers, ecl) * 8); 85 | 86 | this.vers = vers; 87 | this.ecl = ecl; 88 | this.size = vers * 4 + 17; 89 | 90 | modules.length = size*size; 91 | isFunction.length = size*size; 92 | 93 | drawFunctionPatterns(); 94 | auto allcw = appendErrorCorrection(bb.getBytes); 95 | drawCodewords(allcw); 96 | 97 | if (mask == -1) 98 | { 99 | auto minPenalty = int.max; 100 | foreach (i; 0 .. 8) 101 | { 102 | drawFormatBits(i); 103 | applyMask(i); 104 | int penalty = getPenaltyScore(); 105 | if (penalty < minPenalty) 106 | { 107 | mask = i; 108 | minPenalty = penalty; 109 | } 110 | applyMask(i); // undoes the mask due to XOR 111 | } 112 | } 113 | 114 | assert (0 <= mask && mask <= 7); 115 | 116 | drawFormatBits(mask); 117 | applyMask(mask); 118 | this.mask = mask; 119 | } 120 | 121 | void drawFunctionPatterns() 122 | { 123 | foreach (i; 0 .. size) 124 | { 125 | setFunctionModule(6, i, i % 2 == 0); 126 | setFunctionModule(i, 6, i % 2 == 0); 127 | } 128 | 129 | drawFinderPattern(3, 3); 130 | drawFinderPattern(size - 4, 3); 131 | drawFinderPattern(3, size - 4); 132 | 133 | auto alignPatPos = getAlignmentPatternPosition(vers); 134 | auto n = cast(int)alignPatPos.length; 135 | auto skips = [[0,0], [0, n-1], [n-1,0]]; 136 | 137 | foreach (int i; 0 .. n) 138 | foreach (int j; 0 .. n) 139 | if (!skips.canFind([i,j])) 140 | drawAlignmentPattern(alignPatPos[i], alignPatPos[j]); 141 | 142 | drawFormatBits(0); 143 | drawVersion(); 144 | } 145 | 146 | void drawFormatBits(int mask) 147 | { 148 | auto data = ecl.formatBits << 3 | mask; 149 | auto rem = data; 150 | 151 | foreach (i; 0 .. 10) 152 | rem = (rem << 1) ^ ((rem >> 9) * 0x537); 153 | 154 | data = data << 10 | rem; 155 | data ^= 0x5412; 156 | assert (data >> 15 == 0); 157 | 158 | bool checkI(int i) { return ((data >> i) & 1) != 0; } 159 | 160 | foreach (i; 0 .. 6) 161 | setFunctionModule(8, i, checkI(i)); 162 | 163 | setFunctionModule(8, 7, checkI(6)); 164 | setFunctionModule(8, 8, checkI(7)); 165 | setFunctionModule(7, 8, checkI(8)); 166 | 167 | foreach (i; 9 .. 15) 168 | setFunctionModule(14 - i, 8, checkI(i)); 169 | 170 | foreach (i; 0 .. 8) 171 | setFunctionModule(size - 1 - i, 8, checkI(i)); 172 | foreach (i; 8 .. 15) 173 | setFunctionModule(8, size - 15 + i, checkI(i)); 174 | 175 | setFunctionModule(8, size - 8, true); 176 | } 177 | 178 | void drawVersion() 179 | { 180 | if (vers < 7) return; 181 | auto rem = vers; 182 | foreach (i; 0 .. 12) 183 | rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); 184 | auto data = vers << 12 | rem; 185 | assert (data >> 18 == 0); 186 | 187 | foreach (i; 0 .. 18) 188 | { 189 | auto bit = ((data >> i) & 1) != 0; 190 | auto a = size - 11 + i % 3, b = i / 3; 191 | setFunctionModule(a, b, bit); 192 | setFunctionModule(b, a, bit); 193 | } 194 | } 195 | 196 | void drawFinderPattern(int x, int y) 197 | { 198 | foreach (i; -4 .. 5) 199 | foreach (j; -4 .. 5) 200 | { 201 | auto dist = max(abs(i), abs(j)); 202 | auto xx = x + j, yy = y + i; 203 | 204 | if (0 <= xx && xx < size && 205 | 0 <= yy && yy < size) 206 | setFunctionModule(xx, yy, dist != 2 && dist != 4); 207 | } 208 | } 209 | 210 | void drawAlignmentPattern(int x, int y) 211 | { 212 | foreach (i; -2 .. 3) 213 | foreach (j; -2 .. 3) 214 | setFunctionModule(x+j, y+i, max(abs(i), abs(j)) != 1); 215 | } 216 | 217 | void setFunctionModule(int x, int y, bool isBlack) @trusted 218 | { 219 | modules[crd(x,y)] = isBlack; 220 | isFunction[crd(x,y)] = true; 221 | } 222 | 223 | auto appendErrorCorrection(const(ubyte)[] data) const 224 | { 225 | assert (data.length == getNumDataCodewords(vers, ecl)); 226 | 227 | auto nb = numErrorCorrectionBlocks[ecl][vers]; // numblocks 228 | auto tc = numErrorCorrectionCodewords[ecl][vers]; // totalecc 229 | 230 | assert (tc % nb == 0); 231 | auto blen = tc / nb; // blockecclen 232 | // numshortblocks 233 | auto nsb = nb - getNumRawDataModules(vers) / 8 % nb; 234 | // shortblocklen 235 | auto sblen = getNumRawDataModules(vers) / 8 / nb; 236 | 237 | ubyte[][] blocks; 238 | auto rs = ReadSolomonGenerator(blen); 239 | int k = 0; 240 | 241 | foreach (i; 0 .. nb) 242 | { 243 | auto l = k+sblen-blen+(i= nsb) 256 | res ~= blk[i]; 257 | 258 | assert (res.length == getNumRawDataModules(vers) / 8); 259 | 260 | return res; 261 | } 262 | 263 | void drawCodewords(const(ubyte)[] data) @trusted 264 | { 265 | size_t i = 0; 266 | for (int right = size - 1; right >= 1; right -= 2) 267 | { 268 | if (right == 6) right = 5; 269 | for (int vert = 0; vert < size; vert++) 270 | { 271 | for (int j = 0; j < 2; j++) 272 | { 273 | int x = right - j; // Actual x coordinate 274 | bool upwards = ((right & 2) == 0) ^ (x < 6); 275 | int y = upwards ? size - 1 - vert : vert; // Actual y coordinate 276 | if (!isFunction[crd(x,y)] && i < data.length * 8) { 277 | modules[crd(x,y)] = ((data[i >> 3] >> (7 - (i & 7))) & 1) != 0; 278 | i++; 279 | } 280 | } 281 | } 282 | } 283 | 284 | assert (i == data.length*8); 285 | } 286 | 287 | void applyMask(int mask) @trusted 288 | { 289 | enforce(0 <= mask && mask <= 7, new Exception("unknown mask")); 290 | 291 | auto masker = maskPatterns[mask]; 292 | foreach (y; 0 .. size) 293 | foreach (x; 0 .. size) 294 | modules[crd(x,y)] = modules[crd(x,y)] ^ 295 | ((masker(x, y) == 0) && 296 | (!isFunction[crd(x,y)])); 297 | } 298 | 299 | enum maskPatterns = [ 300 | (int x, int y) @safe @nogc pure nothrow { return (x + y) % 2; }, 301 | (int x, int y) @safe @nogc pure nothrow { return y % 2; }, 302 | (int x, int y) @safe @nogc pure nothrow { return x % 3; }, 303 | (int x, int y) @safe @nogc pure nothrow { return (x + y) % 3; }, 304 | (int x, int y) @safe @nogc pure nothrow { return (x / 3 + y / 2) % 2; }, 305 | (int x, int y) @safe @nogc pure nothrow { return x * y % 2 + x * y % 3; }, 306 | (int x, int y) @safe @nogc pure nothrow { return (x * y % 2 + x * y % 3) % 2; }, 307 | (int x, int y) @safe @nogc pure nothrow { return ((x + y) % 2 + x * y % 3) % 2; } 308 | ]; 309 | 310 | int getPenaltyScore() @trusted 311 | { 312 | int res; 313 | 314 | foreach (y; 0 .. size) 315 | { 316 | auto clrx = modules[crd(0,y)]; 317 | auto runx = 1; 318 | foreach (x; 1 .. size) 319 | { 320 | if (modules[crd(x,y)] != clrx) 321 | { 322 | clrx = modules[crd(x,y)]; 323 | runx = 1; 324 | } 325 | else 326 | { 327 | runx++; 328 | if (runx == 5) res += Penalty.N1; 329 | else if (runx > 5) res++; 330 | } 331 | } 332 | } 333 | 334 | foreach (x; 0 .. size) 335 | { 336 | auto clry = modules[crd(x,0)]; 337 | auto runy = 1; 338 | foreach (y; 1 .. size) 339 | { 340 | if (modules[crd(x,y)] != clry) 341 | { 342 | clry = modules[crd(x,y)]; 343 | runy = 1; 344 | } 345 | else 346 | { 347 | runy += 1; 348 | if (runy == 5) res += Penalty.N1; 349 | else if (runy > 5) res += 1; 350 | } 351 | } 352 | } 353 | 354 | foreach (y; 0 .. size-1) 355 | foreach (x; 0 .. size-1) 356 | if (modules[crd(x,y)] == modules[crd(x+1,y)] && 357 | modules[crd(x,y)] == modules[crd(x,y+1)] && 358 | modules[crd(x,y)] == modules[crd(x+1,y+1)]) 359 | res += Penalty.N2; 360 | 361 | foreach (y; 0 .. size) 362 | { 363 | auto bits = 0; 364 | foreach (x; 0 .. size) 365 | { 366 | bits = ((bits << 1) & 0x7FF) | (modules[crd(x,y)] ? 1 : 0); 367 | if (x >= 10 && (bits == 0x05D || bits == 0x5D0)) 368 | res += Penalty.N3; 369 | } 370 | } 371 | 372 | foreach (x; 0 .. size) 373 | { 374 | auto bits = 0; 375 | foreach (y; 0 .. size) 376 | { 377 | bits = ((bits << 1) & 0x7FF) | (modules[crd(x,y)] ? 1 : 0); 378 | if (y >= 10 && (bits == 0x05D || bits == 0x5D0)) 379 | res += Penalty.N3; 380 | } 381 | } 382 | 383 | int black; 384 | foreach (i; 0..modules.length) if (modules[i]) black++; 385 | auto total = size*size; 386 | 387 | for (int k = 0; black*20 < (9-k)*total || black*20 > (11+k)*total; k++) 388 | res += Penalty.N4; 389 | 390 | return res; 391 | } 392 | 393 | int[] getAlignmentPatternPosition(int ver) 394 | { 395 | enforce(1 <= ver && ver <= 40, "Version number out of range"); 396 | 397 | if (ver == 1) return []; 398 | 399 | int numAlign = ver / 7 + 2; 400 | int step = ver == 32 ? 26 : 401 | (ver * 4 + numAlign * 2 + 1) / (2 * numAlign - 2) * 2; 402 | int[] res; 403 | int sz = ver * 4 + 17; 404 | for (int i = 0, pos = sz - 7; i < numAlign - 1; i++, pos -= step) 405 | res = [pos] ~ res; 406 | return [6] ~ res; 407 | } 408 | 409 | static int getNumRawDataModules(int ver) 410 | { 411 | enforce(1 <= ver && ver <= 40, "Version number out of range"); 412 | 413 | int res = (16 * ver + 128) * ver + 64; 414 | 415 | if (ver >= 2) { 416 | int numAlign = ver / 7 + 2; 417 | res -= (25 * numAlign - 10) * numAlign - 55; 418 | if (ver >= 7) res -= 18 * 2; 419 | } 420 | 421 | return res; 422 | } 423 | 424 | static int getNumDataCodewords(int ver, ECL ecl) 425 | { 426 | enforce(1 <= ver && ver <= 40, "unknown version"); 427 | return getNumRawDataModules(ver) / 8 428 | - numErrorCorrectionCodewords[ecl][ver]; 429 | } 430 | 431 | enum Penalty 432 | { 433 | N1 = 3, 434 | N2 = 3, 435 | N3 = 40, 436 | N4 = 10 437 | } 438 | 439 | enum numErrorCorrectionCodewords = [ 440 | // Low 441 | [-1, 7, 10, 15, 20, 26, 36, 40, 48, 60, 72, 442 | 80, 96, 104, 120, 132, 144, 168, 180, 196, 224, 443 | 224, 252, 270, 300, 312, 336, 360, 390, 420, 450, 444 | 480, 510, 540, 570, 570, 600, 630, 660, 720, 750], 445 | 446 | // Medium 447 | [-1, 10, 16, 26, 36, 48, 64, 72, 88, 110, 130, 448 | 150, 176, 198, 216, 240, 280, 308, 338, 364, 416, 449 | 442, 476, 504, 560, 588, 644, 700, 728, 784, 812, 450 | 868, 924, 980, 1036, 1064, 1120, 1204, 1260, 1316, 1372], 451 | 452 | // Quartile 453 | [-1, 13, 22, 36, 52, 72, 96, 108, 132, 160, 192, 454 | 224, 260, 288, 320, 360, 408, 448, 504, 546, 600, 455 | 644, 690, 750, 810, 870, 952, 1020, 1050, 1140, 1200, 456 | 1290, 1350, 1440, 1530, 1590, 1680, 1770, 1860, 1950, 2040], 457 | 458 | // High 459 | [-1, 17, 28, 44, 64, 88, 112, 130, 156, 192, 224, 460 | 264, 308, 352, 384, 432, 480, 532, 588, 650, 700, 461 | 750, 816, 900, 960, 1050, 1110, 1200, 1260, 1350, 1440, 462 | 1530, 1620, 1710, 1800, 1890, 1980, 2100, 2220, 2310, 2430] 463 | ]; 464 | 465 | enum numErrorCorrectionBlocks = [ 466 | [-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 467 | 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 468 | 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 469 | 16, 17, 18, 19, 19, 20, 21, 22, 24, 25], 470 | 471 | [-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 472 | 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 473 | 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 474 | 31, 33, 35, 37, 38, 40, 43, 45, 47, 49], 475 | 476 | [-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 477 | 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 478 | 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 479 | 43, 45, 48, 51, 53, 56, 59, 62, 65, 68], 480 | 481 | [-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 482 | 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 483 | 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 484 | 51, 54, 57, 60, 63, 66, 70, 74, 77, 81] 485 | ]; 486 | 487 | static struct ReadSolomonGenerator 488 | { 489 | pure: 490 | private: 491 | ubyte[] coefficients; 492 | 493 | static ubyte multiply(ubyte x, ubyte y) 494 | { 495 | // Russian peasant multiplication 496 | int z = 0; 497 | for (int i = 7; i >= 0; i--) 498 | { 499 | z = (z << 1) ^ ((z >> 7) * 0x11D); 500 | z ^= ((y >> i) & 1) * x; 501 | } 502 | assert (z >> 8 == 0, "Assertion error"); 503 | return cast(ubyte)z; 504 | } 505 | 506 | public: 507 | 508 | this(int degree) 509 | { 510 | enforce(2 <= degree && degree <= 254, "Degree out of range"); 511 | 512 | // Start with the monomial x^0 513 | coefficients.length = degree; 514 | coefficients[$-1] = 1; 515 | 516 | // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), 517 | // drop the highest term, and store the rest of the coefficients in order of descending powers. 518 | // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). 519 | int root = 1; 520 | foreach (i; 0 .. degree) 521 | { 522 | // Multiply the current product by (x - r^i) 523 | foreach (j, ref c; coefficients) 524 | { 525 | c = multiply(c, cast(ubyte)root); 526 | if (j + 1 < coefficients.length) 527 | c ^= coefficients[j+1]; 528 | } 529 | root = (root << 1) ^ ((root >> 7) * 0x11D); // Multiply by 0x02 mod GF(2^8/0x11D) 530 | } 531 | } 532 | 533 | ubyte[] getRemainder(const(ubyte)[] data) const 534 | { 535 | // Compute the remainder by performing polynomial division 536 | auto result = new ubyte[](coefficients.length);//coefficients.dup; 537 | foreach (ref val; data) 538 | { 539 | ubyte factor = val ^ result.front; 540 | result.popFront; 541 | result ~= 0; 542 | foreach (j; 0 .. result.length) 543 | result[j] ^= multiply(coefficients[j], factor); 544 | } 545 | return result; 546 | } 547 | } 548 | } 549 | --------------------------------------------------------------------------------