Merge pull request #1 from boombuler/master

update
This commit is contained in:
iizotop 2017-06-22 19:24:55 +03:00 committed by GitHub
commit 6d91184dae
46 changed files with 3273 additions and 313 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.vscode/

View File

@ -2,14 +2,47 @@
This is a package for GO which can be used to create different types of barcodes.
## Supported Barcode Types ##
* 2 of 5
* Aztec Code
* Codabar
* Code 128
* Code 39
* EAN 8
* EAN 13
* Code 93
* Datamatrix
* QR Codes
* 2 of 5
* EAN 13
* EAN 8
* PDF 417
* QR Code
## Example ##
This is a simple example on how to create a QR-Code and write it to a png-file
```go
package main
import (
"image/png"
"os"
"github.com/boombuler/barcode"
"github.com/boombuler/barcode/qr"
)
func main() {
// Create the barcode
qrCode, _ := qr.Encode("Hello World", qr.M, qr.Auto)
// Scale the barcode to 200x200 pixels
qrCode, _ = barcode.Scale(qrCode, 200, 200)
// create the output file
file, _ := os.Create("qrcode.png")
defer file.Close()
// encode the barcode as png
png.Encode(file, qrCode)
}
```
## Documentation ##
See [GoDoc](https://godoc.org/github.com/boombuler/barcode)

94
aztec/aztec_test.go Normal file
View File

@ -0,0 +1,94 @@
package aztec
import (
"testing"
)
func encodeTest(t *testing.T, data, wanted string) {
result, err := Encode([]byte(data), DEFAULT_EC_PERCENT, DEFAULT_LAYERS)
if err != nil {
t.Error(err)
} else {
ac, ok := result.(*aztecCode)
if !ok {
t.Error("returned barcode is no aztec code...")
} else if draw := ac.string(); draw != wanted {
t.Errorf("Invalid Barcode returned:\n%s", draw)
}
}
}
func Test_Encode1(t *testing.T) {
encodeTest(t, "This is an example Aztec symbol for Wikipedia.",
"X X X X X X X X \n"+
"X X X X X X X X X X \n"+
"X X X X X X X X X X X \n"+
"X X X X X X X X X X X \n"+
" X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X \n"+
"X X X X X X X X X X \n"+
" X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X \n"+
" X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X \n"+
" X X X \n"+
" X X X X X X X X X X \n"+
" X X X X X X X X X X \n")
}
func Test_Encode2(t *testing.T) {
encodeTest(t, "Aztec Code is a public domain 2D matrix barcode symbology"+
" of nominally square symbols built on a square grid with a "+
"distinctive square bullseye pattern at their center.",
" X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X X X X X X X X X \n"+
" X X X X X X X X X X X X X X X X \n"+
"X X X X X X X X X X X X X \n")
}

62
aztec/azteccode.go Normal file
View File

@ -0,0 +1,62 @@
package aztec
import (
"bytes"
"image"
"image/color"
"github.com/boombuler/barcode"
"github.com/boombuler/barcode/utils"
)
type aztecCode struct {
*utils.BitList
size int
content []byte
}
func newAztecCode(size int) *aztecCode {
return &aztecCode{utils.NewBitList(size * size), size, nil}
}
func (c *aztecCode) Content() string {
return string(c.content)
}
func (c *aztecCode) Metadata() barcode.Metadata {
return barcode.Metadata{barcode.TypeAztec, 2}
}
func (c *aztecCode) ColorModel() color.Model {
return color.Gray16Model
}
func (c *aztecCode) Bounds() image.Rectangle {
return image.Rect(0, 0, c.size, c.size)
}
func (c *aztecCode) At(x, y int) color.Color {
if c.GetBit(x*c.size + y) {
return color.Black
}
return color.White
}
func (c *aztecCode) set(x, y int) {
c.SetBit(x*c.size+y, true)
}
func (c *aztecCode) string() string {
buf := new(bytes.Buffer)
for y := 0; y < c.size; y++ {
for x := 0; x < c.size; x++ {
if c.GetBit(x*c.size + y) {
buf.WriteString("X ")
} else {
buf.WriteString(" ")
}
}
buf.WriteRune('\n')
}
return buf.String()
}

268
aztec/encoder.go Normal file
View File

@ -0,0 +1,268 @@
// Package aztec can create Aztec Code barcodes
package aztec
import (
"fmt"
"github.com/boombuler/barcode"
"github.com/boombuler/barcode/utils"
)
const (
DEFAULT_EC_PERCENT = 33
DEFAULT_LAYERS = 0
max_nb_bits = 32
max_nb_bits_compact = 4
)
var (
word_size = []int{
4, 6, 6, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
}
)
func totalBitsInLayer(layers int, compact bool) int {
tmp := 112
if compact {
tmp = 88
}
return (tmp + 16*layers) * layers
}
func stuffBits(bits *utils.BitList, wordSize int) *utils.BitList {
out := new(utils.BitList)
n := bits.Len()
mask := (1 << uint(wordSize)) - 2
for i := 0; i < n; i += wordSize {
word := 0
for j := 0; j < wordSize; j++ {
if i+j >= n || bits.GetBit(i+j) {
word |= 1 << uint(wordSize-1-j)
}
}
if (word & mask) == mask {
out.AddBits(word&mask, byte(wordSize))
i--
} else if (word & mask) == 0 {
out.AddBits(word|1, byte(wordSize))
i--
} else {
out.AddBits(word, byte(wordSize))
}
}
return out
}
func generateModeMessage(compact bool, layers, messageSizeInWords int) *utils.BitList {
modeMessage := new(utils.BitList)
if compact {
modeMessage.AddBits(layers-1, 2)
modeMessage.AddBits(messageSizeInWords-1, 6)
modeMessage = generateCheckWords(modeMessage, 28, 4)
} else {
modeMessage.AddBits(layers-1, 5)
modeMessage.AddBits(messageSizeInWords-1, 11)
modeMessage = generateCheckWords(modeMessage, 40, 4)
}
return modeMessage
}
func drawModeMessage(matrix *aztecCode, compact bool, matrixSize int, modeMessage *utils.BitList) {
center := matrixSize / 2
if compact {
for i := 0; i < 7; i++ {
offset := center - 3 + i
if modeMessage.GetBit(i) {
matrix.set(offset, center-5)
}
if modeMessage.GetBit(i + 7) {
matrix.set(center+5, offset)
}
if modeMessage.GetBit(20 - i) {
matrix.set(offset, center+5)
}
if modeMessage.GetBit(27 - i) {
matrix.set(center-5, offset)
}
}
} else {
for i := 0; i < 10; i++ {
offset := center - 5 + i + i/5
if modeMessage.GetBit(i) {
matrix.set(offset, center-7)
}
if modeMessage.GetBit(i + 10) {
matrix.set(center+7, offset)
}
if modeMessage.GetBit(29 - i) {
matrix.set(offset, center+7)
}
if modeMessage.GetBit(39 - i) {
matrix.set(center-7, offset)
}
}
}
}
func drawBullsEye(matrix *aztecCode, center, size int) {
for i := 0; i < size; i += 2 {
for j := center - i; j <= center+i; j++ {
matrix.set(j, center-i)
matrix.set(j, center+i)
matrix.set(center-i, j)
matrix.set(center+i, j)
}
}
matrix.set(center-size, center-size)
matrix.set(center-size+1, center-size)
matrix.set(center-size, center-size+1)
matrix.set(center+size, center-size)
matrix.set(center+size, center-size+1)
matrix.set(center+size, center+size-1)
}
// Encode returns an aztec barcode with the given content
func Encode(data []byte, minECCPercent int, userSpecifiedLayers int) (barcode.Barcode, error) {
bits := highlevelEncode(data)
eccBits := ((bits.Len() * minECCPercent) / 100) + 11
totalSizeBits := bits.Len() + eccBits
var layers, TotalBitsInLayer, wordSize int
var compact bool
var stuffedBits *utils.BitList
if userSpecifiedLayers != DEFAULT_LAYERS {
compact = userSpecifiedLayers < 0
if compact {
layers = -userSpecifiedLayers
} else {
layers = userSpecifiedLayers
}
if (compact && layers > max_nb_bits_compact) || (!compact && layers > max_nb_bits) {
return nil, fmt.Errorf("Illegal value %d for layers", userSpecifiedLayers)
}
TotalBitsInLayer = totalBitsInLayer(layers, compact)
wordSize = word_size[layers]
usableBitsInLayers := TotalBitsInLayer - (TotalBitsInLayer % wordSize)
stuffedBits = stuffBits(bits, wordSize)
if stuffedBits.Len()+eccBits > usableBitsInLayers {
return nil, fmt.Errorf("Data to large for user specified layer")
}
if compact && stuffedBits.Len() > wordSize*64 {
return nil, fmt.Errorf("Data to large for user specified layer")
}
} else {
wordSize = 0
stuffedBits = nil
// We look at the possible table sizes in the order Compact1, Compact2, Compact3,
// Compact4, Normal4,... Normal(i) for i < 4 isn't typically used since Compact(i+1)
// is the same size, but has more data.
for i := 0; ; i++ {
if i > max_nb_bits {
return nil, fmt.Errorf("Data too large for an aztec code")
}
compact = i <= 3
layers = i
if compact {
layers = i + 1
}
TotalBitsInLayer = totalBitsInLayer(layers, compact)
if totalSizeBits > TotalBitsInLayer {
continue
}
// [Re]stuff the bits if this is the first opportunity, or if the
// wordSize has changed
if wordSize != word_size[layers] {
wordSize = word_size[layers]
stuffedBits = stuffBits(bits, wordSize)
}
usableBitsInLayers := TotalBitsInLayer - (TotalBitsInLayer % wordSize)
if compact && stuffedBits.Len() > wordSize*64 {
// Compact format only allows 64 data words, though C4 can hold more words than that
continue
}
if stuffedBits.Len()+eccBits <= usableBitsInLayers {
break
}
}
}
messageBits := generateCheckWords(stuffedBits, TotalBitsInLayer, wordSize)
messageSizeInWords := stuffedBits.Len() / wordSize
modeMessage := generateModeMessage(compact, layers, messageSizeInWords)
// allocate symbol
var baseMatrixSize int
if compact {
baseMatrixSize = 11 + layers*4
} else {
baseMatrixSize = 14 + layers*4
}
alignmentMap := make([]int, baseMatrixSize)
var matrixSize int
if compact {
// no alignment marks in compact mode, alignmentMap is a no-op
matrixSize = baseMatrixSize
for i := 0; i < len(alignmentMap); i++ {
alignmentMap[i] = i
}
} else {
matrixSize = baseMatrixSize + 1 + 2*((baseMatrixSize/2-1)/15)
origCenter := baseMatrixSize / 2
center := matrixSize / 2
for i := 0; i < origCenter; i++ {
newOffset := i + i/15
alignmentMap[origCenter-i-1] = center - newOffset - 1
alignmentMap[origCenter+i] = center + newOffset + 1
}
}
code := newAztecCode(matrixSize)
code.content = data
// draw data bits
for i, rowOffset := 0, 0; i < layers; i++ {
rowSize := (layers - i) * 4
if compact {
rowSize += 9
} else {
rowSize += 12
}
for j := 0; j < rowSize; j++ {
columnOffset := j * 2
for k := 0; k < 2; k++ {
if messageBits.GetBit(rowOffset + columnOffset + k) {
code.set(alignmentMap[i*2+k], alignmentMap[i*2+j])
}
if messageBits.GetBit(rowOffset + rowSize*2 + columnOffset + k) {
code.set(alignmentMap[i*2+j], alignmentMap[baseMatrixSize-1-i*2-k])
}
if messageBits.GetBit(rowOffset + rowSize*4 + columnOffset + k) {
code.set(alignmentMap[baseMatrixSize-1-i*2-k], alignmentMap[baseMatrixSize-1-i*2-j])
}
if messageBits.GetBit(rowOffset + rowSize*6 + columnOffset + k) {
code.set(alignmentMap[baseMatrixSize-1-i*2-j], alignmentMap[i*2+k])
}
}
}
rowOffset += rowSize * 8
}
// draw mode message
drawModeMessage(code, compact, matrixSize, modeMessage)
// draw alignment marks
if compact {
drawBullsEye(code, matrixSize/2, 5)
} else {
drawBullsEye(code, matrixSize/2, 7)
for i, j := 0, 0; i < baseMatrixSize/2-1; i, j = i+15, j+16 {
for k := (matrixSize / 2) & 1; k < matrixSize; k += 2 {
code.set(matrixSize/2-j, k)
code.set(matrixSize/2+j, k)
code.set(k, matrixSize/2-j)
code.set(k, matrixSize/2+j)
}
}
}
return code, nil
}

58
aztec/encoder_test.go Normal file
View File

@ -0,0 +1,58 @@
package aztec
import (
"strings"
"testing"
"github.com/boombuler/barcode/utils"
)
func Test_StuffBits(t *testing.T) {
testStuffBits := func(wordSize int, bits string, expected string) {
bl := new(utils.BitList)
for _, r := range bits {
if r == 'X' {
bl.AddBit(true)
} else if r == '.' {
bl.AddBit(false)
}
}
stuffed := stuffBits(bl, wordSize)
expectedBits := strings.Replace(expected, " ", "", -1)
result := bitStr(stuffed)
if result != expectedBits {
t.Errorf("stuffBits failed for %q\nGot: %q", bits, result)
}
}
testStuffBits(5, ".X.X. X.X.X .X.X.",
".X.X. X.X.X .X.X.")
testStuffBits(5, ".X.X. ..... .X.X",
".X.X. ....X ..X.X")
testStuffBits(3, "XX. ... ... ..X XXX .X. ..",
"XX. ..X ..X ..X ..X .XX XX. .X. ..X")
testStuffBits(6, ".X.X.. ...... ..X.XX",
".X.X.. .....X. ..X.XX XXXX.")
testStuffBits(6, ".X.X.. ...... ...... ..X.X.",
".X.X.. .....X .....X ....X. X.XXXX")
testStuffBits(6, ".X.X.. XXXXXX ...... ..X.XX",
".X.X.. XXXXX. X..... ...X.X XXXXX.")
testStuffBits(6,
"...... ..XXXX X..XX. .X.... .X.X.X .....X .X.... ...X.X .....X ....XX ..X... ....X. X..XXX X.XX.X",
".....X ...XXX XX..XX ..X... ..X.X. X..... X.X... ....X. X..... X....X X..X.. .....X X.X..X XXX.XX .XXXXX")
}
func Test_ModeMessage(t *testing.T) {
testModeMessage := func(compact bool, layers, words int, expected string) {
result := bitStr(generateModeMessage(compact, layers, words))
expectedBits := strings.Replace(expected, " ", "", -1)
if result != expectedBits {
t.Errorf("generateModeMessage(%v, %d, %d) failed.\nGot:%s", compact, layers, words, result)
}
}
testModeMessage(true, 2, 29, ".X .XXX.. ...X XX.. ..X .XX. .XX.X")
testModeMessage(true, 4, 64, "XX XXXXXX .X.. ...X ..XX .X.. XX..")
testModeMessage(false, 21, 660, "X.X.. .X.X..X..XX .XXX ..X.. .XXX. .X... ..XXX")
testModeMessage(false, 32, 4096, "XXXXX XXXXXXXXXXX X.X. ..... XXX.X ..X.. X.XXX")
}

61
aztec/errorcorrection.go Normal file
View File

@ -0,0 +1,61 @@
package aztec
import (
"github.com/boombuler/barcode/utils"
)
func bitsToWords(stuffedBits *utils.BitList, wordSize int, wordCount int) []int {
message := make([]int, wordCount)
for i := 0; i < wordCount; i++ {
value := 0
for j := 0; j < wordSize; j++ {
if stuffedBits.GetBit(i*wordSize + j) {
value |= (1 << uint(wordSize-j-1))
}
}
message[i] = value
}
return message
}
func generateCheckWords(bits *utils.BitList, totalBits, wordSize int) *utils.BitList {
rs := utils.NewReedSolomonEncoder(getGF(wordSize))
// bits is guaranteed to be a multiple of the wordSize, so no padding needed
messageWordCount := bits.Len() / wordSize
totalWordCount := totalBits / wordSize
eccWordCount := totalWordCount - messageWordCount
messageWords := bitsToWords(bits, wordSize, messageWordCount)
eccWords := rs.Encode(messageWords, eccWordCount)
startPad := totalBits % wordSize
messageBits := new(utils.BitList)
messageBits.AddBits(0, byte(startPad))
for _, messageWord := range messageWords {
messageBits.AddBits(messageWord, byte(wordSize))
}
for _, eccWord := range eccWords {
messageBits.AddBits(eccWord, byte(wordSize))
}
return messageBits
}
func getGF(wordSize int) *utils.GaloisField {
switch wordSize {
case 4:
return utils.NewGaloisField(0x13, 16, 1)
case 6:
return utils.NewGaloisField(0x43, 64, 1)
case 8:
return utils.NewGaloisField(0x012D, 256, 1)
case 10:
return utils.NewGaloisField(0x409, 1024, 1)
case 12:
return utils.NewGaloisField(0x1069, 4096, 1)
default:
return nil
}
}

171
aztec/highlevel.go Normal file
View File

@ -0,0 +1,171 @@
package aztec
import (
"github.com/boombuler/barcode/utils"
)
func highlevelEncode(data []byte) *utils.BitList {
states := stateSlice{initialState}
for index := 0; index < len(data); index++ {
pairCode := 0
nextChar := byte(0)
if index+1 < len(data) {
nextChar = data[index+1]
}
switch cur := data[index]; {
case cur == '\r' && nextChar == '\n':
pairCode = 2
case cur == '.' && nextChar == ' ':
pairCode = 3
case cur == ',' && nextChar == ' ':
pairCode = 4
case cur == ':' && nextChar == ' ':
pairCode = 5
}
if pairCode > 0 {
// We have one of the four special PUNCT pairs. Treat them specially.
// Get a new set of states for the two new characters.
states = updateStateListForPair(states, data, index, pairCode)
index++
} else {
// Get a new set of states for the new character.
states = updateStateListForChar(states, data, index)
}
}
minBitCnt := int((^uint(0)) >> 1)
var result *state = nil
for _, s := range states {
if s.bitCount < minBitCnt {
minBitCnt = s.bitCount
result = s
}
}
if result != nil {
return result.toBitList(data)
} else {
return new(utils.BitList)
}
}
func simplifyStates(states stateSlice) stateSlice {
var result stateSlice = nil
for _, newState := range states {
add := true
var newResult stateSlice = nil
for _, oldState := range result {
if add && oldState.isBetterThanOrEqualTo(newState) {
add = false
}
if !(add && newState.isBetterThanOrEqualTo(oldState)) {
newResult = append(newResult, oldState)
}
}
if add {
result = append(newResult, newState)
} else {
result = newResult
}
}
return result
}
// We update a set of states for a new character by updating each state
// for the new character, merging the results, and then removing the
// non-optimal states.
func updateStateListForChar(states stateSlice, data []byte, index int) stateSlice {
var result stateSlice = nil
for _, s := range states {
if r := updateStateForChar(s, data, index); len(r) > 0 {
result = append(result, r...)
}
}
return simplifyStates(result)
}
// Return a set of states that represent the possible ways of updating this
// state for the next character. The resulting set of states are added to
// the "result" list.
func updateStateForChar(s *state, data []byte, index int) stateSlice {
var result stateSlice = nil
ch := data[index]
charInCurrentTable := charMap[s.mode][ch] > 0
var stateNoBinary *state = nil
for mode := mode_upper; mode <= mode_punct; mode++ {
charInMode := charMap[mode][ch]
if charInMode > 0 {
if stateNoBinary == nil {
// Only create stateNoBinary the first time it's required.
stateNoBinary = s.endBinaryShift(index)
}
// Try generating the character by latching to its mode
if !charInCurrentTable || mode == s.mode || mode == mode_digit {
// If the character is in the current table, we don't want to latch to
// any other mode except possibly digit (which uses only 4 bits). Any
// other latch would be equally successful *after* this character, and
// so wouldn't save any bits.
res := stateNoBinary.latchAndAppend(mode, charInMode)
result = append(result, res)
}
// Try generating the character by switching to its mode.
if _, ok := shiftTable[s.mode][mode]; !charInCurrentTable && ok {
// It never makes sense to temporarily shift to another mode if the
// character exists in the current mode. That can never save bits.
res := stateNoBinary.shiftAndAppend(mode, charInMode)
result = append(result, res)
}
}
}
if s.bShiftByteCount > 0 || charMap[s.mode][ch] == 0 {
// It's never worthwhile to go into binary shift mode if you're not already
// in binary shift mode, and the character exists in your current mode.
// That can never save bits over just outputting the char in the current mode.
res := s.addBinaryShiftChar(index)
result = append(result, res)
}
return result
}
// We update a set of states for a new character by updating each state
// for the new character, merging the results, and then removing the
// non-optimal states.
func updateStateListForPair(states stateSlice, data []byte, index int, pairCode int) stateSlice {
var result stateSlice = nil
for _, s := range states {
if r := updateStateForPair(s, data, index, pairCode); len(r) > 0 {
result = append(result, r...)
}
}
return simplifyStates(result)
}
func updateStateForPair(s *state, data []byte, index int, pairCode int) stateSlice {
var result stateSlice
stateNoBinary := s.endBinaryShift(index)
// Possibility 1. Latch to MODE_PUNCT, and then append this code
result = append(result, stateNoBinary.latchAndAppend(mode_punct, pairCode))
if s.mode != mode_punct {
// Possibility 2. Shift to MODE_PUNCT, and then append this code.
// Every state except MODE_PUNCT (handled above) can shift
result = append(result, stateNoBinary.shiftAndAppend(mode_punct, pairCode))
}
if pairCode == 3 || pairCode == 4 {
// both characters are in DIGITS. Sometimes better to just add two digits
digitState := stateNoBinary.
latchAndAppend(mode_digit, 16-pairCode). // period or comma in DIGIT
latchAndAppend(mode_digit, 1) // space in DIGIT
result = append(result, digitState)
}
if s.bShiftByteCount > 0 {
// It only makes sense to do the characters as binary if we're already
// in binary mode.
result = append(result, s.addBinaryShiftChar(index).addBinaryShiftChar(index+1))
}
return result
}

132
aztec/highlevel_test.go Normal file
View File

@ -0,0 +1,132 @@
package aztec
import (
"bytes"
"strings"
"testing"
"github.com/boombuler/barcode/utils"
)
func bitStr(bl *utils.BitList) string {
buf := new(bytes.Buffer)
for i := 0; i < bl.Len(); i++ {
if bl.GetBit(i) {
buf.WriteRune('X')
} else {
buf.WriteRune('.')
}
}
return buf.String()
}
func testHighLevelEncodeString(t *testing.T, s, expectedBits string) {
bits := highlevelEncode([]byte(s))
result := bitStr(bits)
expectedBits = strings.Replace(expectedBits, " ", "", -1)
if result != expectedBits {
t.Errorf("invalid result for highlevelEncode(%q). Got:\n%s", s, result)
}
}
func testHighLevelEncodeStringCnt(t *testing.T, s string, expectedBitCnt int) {
bits := highlevelEncode([]byte(s))
if bits.Len() != expectedBitCnt {
t.Errorf("invalid result for highlevelEncode(%q). Got %d, expected %d bits", s, bits.Len(), expectedBitCnt)
}
}
func Test_HighLevelEncode(t *testing.T) {
testHighLevelEncodeString(t, "A. b.",
// 'A' P/S '. ' L/L b D/L '.'
"...X. ..... ...XX XXX.. ...XX XXXX. XX.X")
testHighLevelEncodeString(t, "Lorem ipsum.",
// 'L' L/L 'o' 'r' 'e' 'm' ' ' 'i' 'p' 's' 'u' 'm' D/L '.'
".XX.X XXX.. X.... X..XX ..XX. .XXX. ....X .X.X. X...X X.X.. X.XX. .XXX. XXXX. XX.X")
testHighLevelEncodeString(t, "Lo. Test 123.",
// 'L' L/L 'o' P/S '. ' U/S 'T' 'e' 's' 't' D/L ' ' '1' '2' '3' '.'
".XX.X XXX.. X.... ..... ...XX XXX.. X.X.X ..XX. X.X.. X.X.X XXXX. ...X ..XX .X.. .X.X XX.X")
testHighLevelEncodeString(t, "Lo...x",
// 'L' L/L 'o' D/L '.' '.' '.' U/L L/L 'x'
".XX.X XXX.. X.... XXXX. XX.X XX.X XX.X XXX. XXX.. XX..X")
testHighLevelEncodeString(t, ". x://abc/.",
//P/S '. ' L/L 'x' P/S ':' P/S '/' P/S '/' 'a' 'b' 'c' P/S '/' D/L '.'
"..... ...XX XXX.. XX..X ..... X.X.X ..... X.X.. ..... X.X.. ...X. ...XX ..X.. ..... X.X.. XXXX. XX.X")
// Uses Binary/Shift rather than Lower/Shift to save two bits.
testHighLevelEncodeString(t, "ABCdEFG",
//'A' 'B' 'C' B/S =1 'd' 'E' 'F' 'G'
"...X. ...XX ..X.. XXXXX ....X .XX..X.. ..XX. ..XXX .X...")
testHighLevelEncodeStringCnt(t,
// Found on an airline boarding pass. Several stretches of Binary shift are
// necessary to keep the bitcount so low.
"09 UAG ^160MEUCIQC0sYS/HpKxnBELR1uB85R20OoqqwFGa0q2uEi"+
"Ygh6utAIgLl1aBVM4EOTQtMQQYH9M2Z3Dp4qnA/fwWuQ+M8L3V8U=",
823)
}
func Test_HighLevelEncodeBinary(t *testing.T) {
// binary short form single byte
testHighLevelEncodeString(t, "N\u0000N",
// 'N' B/S =1 '\0' N
".XXXX XXXXX ....X ........ .XXXX") // Encode "N" in UPPER
testHighLevelEncodeString(t, "N\u0000n",
// 'N' B/S =2 '\0' 'n'
".XXXX XXXXX ...X. ........ .XX.XXX.") // Encode "n" in BINARY
// binary short form consecutive bytes
testHighLevelEncodeString(t, "N\x00\x80 A",
// 'N' B/S =2 '\0' \u0080 ' ' 'A'
".XXXX XXXXX ...X. ........ X....... ....X ...X.")
// binary skipping over single character
testHighLevelEncodeString(t, "\x00a\xFF\x80 A",
// B/S =4 '\0' 'a' '\3ff' '\200' ' ' 'A'
"XXXXX ..X.. ........ .XX....X XXXXXXXX X....... ....X ...X.")
// getting into binary mode from digit mode
testHighLevelEncodeString(t, "1234\u0000",
//D/L '1' '2' '3' '4' U/L B/S =1 \0
"XXXX. ..XX .X.. .X.X .XX. XXX. XXXXX ....X ........")
// Create a string in which every character requires binary
sb := new(bytes.Buffer)
for i := 0; i <= 3000; i++ {
sb.WriteByte(byte(128 + (i % 30)))
}
// Test the output generated by Binary/Switch, particularly near the
// places where the encoding changes: 31, 62, and 2047+31=2078
for _, i := range []int{1, 2, 3, 10, 29, 30, 31, 32, 33, 60, 61, 62, 63, 64, 2076, 2077, 2078, 2079, 2080, 2100} {
// This is the expected length of a binary string of length "i"
expectedLength := (8 * i)
switch {
case i <= 31:
expectedLength += 10
case i <= 62:
expectedLength += 20
case i <= 2078:
expectedLength += 21
default:
expectedLength += 31
}
data := string(sb.Bytes()[:i])
// Verify that we are correct about the length.
testHighLevelEncodeStringCnt(t, data, expectedLength)
if i != 1 && i != 32 && i != 2079 {
// The addition of an 'a' at the beginning or end gets merged into the binary code
// in those cases where adding another binary character only adds 8 or 9 bits to the result.
// So we exclude the border cases i=1,32,2079
// A lower case letter at the beginning will be merged into binary mode
testHighLevelEncodeStringCnt(t, "a"+string(sb.Bytes()[:i-1]), expectedLength)
// A lower case letter at the end will also be merged into binary mode
testHighLevelEncodeStringCnt(t, string(sb.Bytes()[:i-1])+"a", expectedLength)
}
// A lower case letter at both ends will enough to latch us into LOWER.
testHighLevelEncodeStringCnt(t, "a"+data+"b", expectedLength+15)
}
}

264
aztec/state.go Normal file
View File

@ -0,0 +1,264 @@
package aztec
import (
"fmt"
"github.com/boombuler/barcode/utils"
)
type encodingMode byte
const (
mode_upper encodingMode = iota // 5 bits
mode_lower // 5 bits
mode_digit // 4 bits
mode_mixed // 5 bits
mode_punct // 5 bits
)
var (
// The Latch Table shows, for each pair of Modes, the optimal method for
// getting from one mode to another. In the worst possible case, this can
// be up to 14 bits. In the best possible case, we are already there!
// The high half-word of each entry gives the number of bits.
// The low half-word of each entry are the actual bits necessary to change
latchTable = map[encodingMode]map[encodingMode]int{
mode_upper: {
mode_upper: 0,
mode_lower: (5 << 16) + 28,
mode_digit: (5 << 16) + 30,
mode_mixed: (5 << 16) + 29,
mode_punct: (10 << 16) + (29 << 5) + 30,
},
mode_lower: {
mode_upper: (9 << 16) + (30 << 4) + 14,
mode_lower: 0,
mode_digit: (5 << 16) + 30,
mode_mixed: (5 << 16) + 29,
mode_punct: (10 << 16) + (29 << 5) + 30,
},
mode_digit: {
mode_upper: (4 << 16) + 14,
mode_lower: (9 << 16) + (14 << 5) + 28,
mode_digit: 0,
mode_mixed: (9 << 16) + (14 << 5) + 29,
mode_punct: (14 << 16) + (14 << 10) + (29 << 5) + 30,
},
mode_mixed: {
mode_upper: (5 << 16) + 29,
mode_lower: (5 << 16) + 28,
mode_digit: (10 << 16) + (29 << 5) + 30,
mode_mixed: 0,
mode_punct: (5 << 16) + 30,
},
mode_punct: {
mode_upper: (5 << 16) + 31,
mode_lower: (10 << 16) + (31 << 5) + 28,
mode_digit: (10 << 16) + (31 << 5) + 30,
mode_mixed: (10 << 16) + (31 << 5) + 29,
mode_punct: 0,
},
}
// A map showing the available shift codes. (The shifts to BINARY are not shown)
shiftTable = map[encodingMode]map[encodingMode]int{
mode_upper: {
mode_punct: 0,
},
mode_lower: {
mode_punct: 0,
mode_upper: 28,
},
mode_mixed: {
mode_punct: 0,
},
mode_digit: {
mode_punct: 0,
mode_upper: 15,
},
}
charMap map[encodingMode][]int
)
type state struct {
mode encodingMode
tokens token
bShiftByteCount int
bitCount int
}
type stateSlice []*state
var initialState *state = &state{
mode: mode_upper,
tokens: nil,
bShiftByteCount: 0,
bitCount: 0,
}
func init() {
charMap = make(map[encodingMode][]int)
charMap[mode_upper] = make([]int, 256)
charMap[mode_lower] = make([]int, 256)
charMap[mode_digit] = make([]int, 256)
charMap[mode_mixed] = make([]int, 256)
charMap[mode_punct] = make([]int, 256)
charMap[mode_upper][' '] = 1
for c := 'A'; c <= 'Z'; c++ {
charMap[mode_upper][int(c)] = int(c - 'A' + 2)
}
charMap[mode_lower][' '] = 1
for c := 'a'; c <= 'z'; c++ {
charMap[mode_lower][c] = int(c - 'a' + 2)
}
charMap[mode_digit][' '] = 1
for c := '0'; c <= '9'; c++ {
charMap[mode_digit][c] = int(c - '0' + 2)
}
charMap[mode_digit][','] = 12
charMap[mode_digit]['.'] = 13
mixedTable := []int{
0, ' ', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 27, 28, 29, 30, 31, '@', '\\', '^',
'_', '`', '|', '~', 127,
}
for i, v := range mixedTable {
charMap[mode_mixed][v] = i
}
punctTable := []int{
0, '\r', 0, 0, 0, 0, '!', '\'', '#', '$', '%', '&', '\'',
'(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?',
'[', ']', '{', '}',
}
for i, v := range punctTable {
if v > 0 {
charMap[mode_punct][v] = i
}
}
}
func (em encodingMode) BitCount() byte {
if em == mode_digit {
return 4
}
return 5
}
// Create a new state representing this state with a latch to a (not
// necessary different) mode, and then a code.
func (s *state) latchAndAppend(mode encodingMode, value int) *state {
bitCount := s.bitCount
tokens := s.tokens
if mode != s.mode {
latch := latchTable[s.mode][mode]
tokens = newSimpleToken(tokens, latch&0xFFFF, byte(latch>>16))
bitCount += latch >> 16
}
tokens = newSimpleToken(tokens, value, mode.BitCount())
return &state{
mode: mode,
tokens: tokens,
bShiftByteCount: 0,
bitCount: bitCount + int(mode.BitCount()),
}
}
// Create a new state representing this state, with a temporary shift
// to a different mode to output a single value.
func (s *state) shiftAndAppend(mode encodingMode, value int) *state {
tokens := s.tokens
// Shifts exist only to UPPER and PUNCT, both with tokens size 5.
tokens = newSimpleToken(tokens, shiftTable[s.mode][mode], s.mode.BitCount())
tokens = newSimpleToken(tokens, value, 5)
return &state{
mode: s.mode,
tokens: tokens,
bShiftByteCount: 0,
bitCount: s.bitCount + int(s.mode.BitCount()) + 5,
}
}
// Create a new state representing this state, but an additional character
// output in Binary Shift mode.
func (s *state) addBinaryShiftChar(index int) *state {
tokens := s.tokens
mode := s.mode
bitCnt := s.bitCount
if s.mode == mode_punct || s.mode == mode_digit {
latch := latchTable[s.mode][mode_upper]
tokens = newSimpleToken(tokens, latch&0xFFFF, byte(latch>>16))
bitCnt += latch >> 16
mode = mode_upper
}
deltaBitCount := 8
if s.bShiftByteCount == 0 || s.bShiftByteCount == 31 {
deltaBitCount = 18
} else if s.bShiftByteCount == 62 {
deltaBitCount = 9
}
result := &state{
mode: mode,
tokens: tokens,
bShiftByteCount: s.bShiftByteCount + 1,
bitCount: bitCnt + deltaBitCount,
}
if result.bShiftByteCount == 2047+31 {
// The string is as long as it's allowed to be. We should end it.
result = result.endBinaryShift(index + 1)
}
return result
}
// Create the state identical to this one, but we are no longer in
// Binary Shift mode.
func (s *state) endBinaryShift(index int) *state {
if s.bShiftByteCount == 0 {
return s
}
tokens := newShiftToken(s.tokens, index-s.bShiftByteCount, s.bShiftByteCount)
return &state{
mode: s.mode,
tokens: tokens,
bShiftByteCount: 0,
bitCount: s.bitCount,
}
}
// Returns true if "this" state is better (or equal) to be in than "that"
// state under all possible circumstances.
func (this *state) isBetterThanOrEqualTo(other *state) bool {
mySize := this.bitCount + (latchTable[this.mode][other.mode] >> 16)
if other.bShiftByteCount > 0 && (this.bShiftByteCount == 0 || this.bShiftByteCount > other.bShiftByteCount) {
mySize += 10 // Cost of entering Binary Shift mode.
}
return mySize <= other.bitCount
}
func (s *state) toBitList(text []byte) *utils.BitList {
tokens := make([]token, 0)
se := s.endBinaryShift(len(text))
for t := se.tokens; t != nil; t = t.prev() {
tokens = append(tokens, t)
}
res := new(utils.BitList)
for i := len(tokens) - 1; i >= 0; i-- {
tokens[i].appendTo(res, text)
}
return res
}
func (s *state) String() string {
tokens := make([]token, 0)
for t := s.tokens; t != nil; t = t.prev() {
tokens = append([]token{t}, tokens...)
}
return fmt.Sprintf("M:%d bits=%d bytes=%d: %v", s.mode, s.bitCount, s.bShiftByteCount, tokens)
}

75
aztec/token.go Normal file
View File

@ -0,0 +1,75 @@
package aztec
import (
"fmt"
"github.com/boombuler/barcode/utils"
)
type token interface {
fmt.Stringer
prev() token
appendTo(bits *utils.BitList, text []byte)
}
type simpleToken struct {
token
value int
bitCount byte
}
type binaryShiftToken struct {
token
bShiftStart int
bShiftByteCnt int
}
func newSimpleToken(prev token, value int, bitCount byte) token {
return &simpleToken{prev, value, bitCount}
}
func newShiftToken(prev token, bShiftStart int, bShiftCnt int) token {
return &binaryShiftToken{prev, bShiftStart, bShiftCnt}
}
func (st *simpleToken) prev() token {
return st.token
}
func (st *simpleToken) appendTo(bits *utils.BitList, text []byte) {
bits.AddBits(st.value, st.bitCount)
}
func (st *simpleToken) String() string {
value := st.value & ((1 << st.bitCount) - 1)
value |= 1 << st.bitCount
return "<" + fmt.Sprintf("%b", value)[1:] + ">"
}
func (bst *binaryShiftToken) prev() token {
return bst.token
}
func (bst *binaryShiftToken) appendTo(bits *utils.BitList, text []byte) {
for i := 0; i < bst.bShiftByteCnt; i++ {
if i == 0 || (i == 31 && bst.bShiftByteCnt <= 62) {
// We need a header before the first character, and before
// character 31 when the total byte code is <= 62
bits.AddBits(31, 5) // BINARY_SHIFT
if bst.bShiftByteCnt > 62 {
bits.AddBits(bst.bShiftByteCnt-31, 16)
} else if i == 0 {
// 1 <= binaryShiftByteCode <= 62
if bst.bShiftByteCnt < 31 {
bits.AddBits(bst.bShiftByteCnt, 5)
} else {
bits.AddBits(31, 5)
}
} else {
// 32 <= binaryShiftCount <= 62 and i == 31
bits.AddBits(bst.bShiftByteCnt-31, 5)
}
}
bits.AddByte(text[bst.bShiftStart+i])
}
}
func (bst *binaryShiftToken) String() string {
return fmt.Sprintf("<%d::%d>", bst.bShiftStart, (bst.bShiftStart + bst.bShiftByteCnt - 1))
}

View File

@ -2,6 +2,21 @@ package barcode
import "image"
const (
TypeAztec = "Aztec"
TypeCodabar = "Codabar"
TypeCode128 = "Code 128"
TypeCode39 = "Code 39"
TypeCode93 = "Code 93"
TypeDataMatrix = "DataMatrix"
TypeEAN8 = "EAN 8"
TypeEAN13 = "EAN 13"
TypePDF = "PDF417"
TypeQR = "QR Code"
Type2of5 = "2 of 5"
Type2of5Interleaved = "2 of 5 (interleaved)"
)
// Contains some meta information about a barcode
type Metadata struct {
// the name of the barcode kind
@ -17,5 +32,11 @@ type Barcode interface {
Metadata() Metadata
// the data that was encoded in this barcode
Content() string
}
// Additional interface that some barcodes might implement to provide
// the value of its checksum.
type BarcodeIntCS interface {
Barcode
CheckSum() int
}

View File

@ -45,5 +45,5 @@ func Encode(content string) (barcode.Barcode, error) {
}
resBits.AddBit(encodingTable[r]...)
}
return utils.New1DCode("Codabar", content, resBits, 0), nil
return utils.New1DCode(barcode.TypeCodabar, content, resBits), nil
}

View File

@ -29,6 +29,13 @@ func shouldUseCTable(nextRunes []rune, curEncoding byte) bool {
return false
}
for i := 0; i < requiredDigits; i++ {
if i%2 == 0 && nextRunes[i] == FNC1 {
requiredDigits++
if len(nextRunes) < requiredDigits {
return false
}
continue
}
if nextRunes[i] < '0' || nextRunes[i] > '9' {
return false
}
@ -36,11 +43,33 @@ func shouldUseCTable(nextRunes []rune, curEncoding byte) bool {
return true
}
func tableContainsRune(table string, r rune) bool {
return strings.ContainsRune(table, r) || r == FNC1 || r == FNC2 || r == FNC3 || r == FNC4
}
func shouldUseATable(nextRunes []rune, curEncoding byte) bool {
nextRune := nextRunes[0]
if !tableContainsRune(bTable, nextRune) || curEncoding == startASymbol {
return tableContainsRune(aTable, nextRune)
}
if curEncoding == 0 {
for _, r := range nextRunes {
if tableContainsRune(abTable, r) {
continue
}
if strings.ContainsRune(aOnlyTable, r) {
return true
}
break
}
}
return false
}
func getCodeIndexList(content []rune) *utils.BitList {
result := new(utils.BitList)
curEncoding := byte(0)
for i := 0; i < len(content); i++ {
if shouldUseCTable(content[i:], curEncoding) {
if curEncoding != startCSymbol {
if curEncoding == byte(0) {
@ -50,10 +79,44 @@ func getCodeIndexList(content []rune) *utils.BitList {
}
curEncoding = startCSymbol
}
if content[i] == FNC1 {
result.AddByte(102)
} else {
idx := (content[i] - '0') * 10
i++
idx = idx + (content[i] - '0')
result.AddByte(byte(idx))
}
} else if shouldUseATable(content[i:], curEncoding) {
if curEncoding != startASymbol {
if curEncoding == byte(0) {
result.AddByte(startASymbol)
} else {
result.AddByte(codeASymbol)
}
curEncoding = startASymbol
}
var idx int
switch content[i] {
case FNC1:
idx = 102
break
case FNC2:
idx = 97
break
case FNC3:
idx = 96
break
case FNC4:
idx = 101
break
default:
idx = strings.IndexRune(aTable, content[i])
break
}
if idx < 0 {
return nil
}
result.AddByte(byte(idx))
} else {
if curEncoding != startBSymbol {
@ -93,7 +156,7 @@ func getCodeIndexList(content []rune) *utils.BitList {
}
// Encode creates a Code 128 barcode for the given content
func Encode(content string) (barcode.Barcode, error) {
func Encode(content string) (barcode.BarcodeIntCS, error) {
contentRunes := strToRunes(content)
if len(contentRunes) <= 0 || len(contentRunes) > 80 {
return nil, fmt.Errorf("content length should be between 1 and 80 runes but got %d", len(contentRunes))
@ -114,7 +177,27 @@ func Encode(content string) (barcode.Barcode, error) {
}
result.AddBit(encodingTable[idx]...)
}
result.AddBit(encodingTable[sum%103]...)
sum = sum % 103
result.AddBit(encodingTable[sum]...)
result.AddBit(encodingTable[stopSymbol]...)
return utils.New1DCode("Code 128", content, result, sum%103), nil
return utils.New1DCodeIntCheckSum(barcode.TypeCode128, content, result, sum), nil
}
func EncodeWithoutChecksum(content string) (barcode.Barcode, error) {
contentRunes := strToRunes(content)
if len(contentRunes) <= 0 || len(contentRunes) > 80 {
return nil, fmt.Errorf("content length should be between 1 and 80 runes but got %d", len(contentRunes))
}
idxList := getCodeIndexList(contentRunes)
if idxList == nil {
return nil, fmt.Errorf("\"%s\" could not be encoded", content)
}
result := new(utils.BitList)
for _, idx := range idxList.GetBytes() {
result.AddBit(encodingTable[idx]...)
}
result.AddBit(encodingTable[stopSymbol]...)
return utils.New1DCode(barcode.TypeCode128, content, result), nil
}

View File

@ -11,13 +11,25 @@ func testEncode(t *testing.T, txt, testResult string) {
t.Error(err)
} else {
if code.Bounds().Max.X != len(testResult) {
t.Errorf("%v: length missmatch", txt)
t.Errorf("%v: length missmatch. Got %d expected %d", txt, code.Bounds().Max.X, len(testResult))
} else {
encoded := ""
failed := false
for i, r := range testResult {
if code.At(i, 0) == color.Black {
encoded += "1"
} else {
encoded += "0"
}
if (code.At(i, 0) == color.Black) != (r == '1') {
failed = true
t.Errorf("%v: code missmatch on position %d", txt, i)
}
}
if failed {
t.Log("Encoded: ", encoded)
}
}
}
}
@ -30,7 +42,8 @@ func Test_EncodeFunctionChars(t *testing.T) {
encStartB := "11010010000"
encStop := "1100011101011"
testEncode(t, string(FNC1)+"123", encStartB+encFNC1+"10011100110"+"11001110010"+"11001011100"+"11001000010"+encStop)
// Special Case FC1 can also be encoded to C Table therefor using 123 as suffix might have unexpected results.
testEncode(t, string(FNC1)+"A23", encStartB+encFNC1+"10100011000"+"11001110010"+"11001011100"+"10100011110"+encStop)
testEncode(t, string(FNC2)+"123", encStartB+encFNC2+"10011100110"+"11001110010"+"11001011100"+"11100010110"+encStop)
testEncode(t, string(FNC3)+"123", encStartB+encFNC3+"10011100110"+"11001110010"+"11001011100"+"11101000110"+encStop)
testEncode(t, string(FNC4)+"123", encStartB+encFNC4+"10011100110"+"11001110010"+"11001011100"+"11100011010"+encStop)
@ -48,4 +61,71 @@ func Test_Unencodable(t *testing.T) {
func Test_EncodeCTable(t *testing.T) {
testEncode(t, "HI345678H", "110100100001100010100011000100010101110111101000101100011100010110110000101001011110111011000101000111011000101100011101011")
testEncode(t, "334455", "11010011100101000110001000110111011101000110100100111101100011101011")
testEncode(t, string(FNC1)+"1234",
"11010011100"+ // Start C
"11110101110"+ // FNC1
"10110011100"+ // 12
"10001011000"+ // 34
"11101001100"+ // CheckSum == 24
"1100011101011") // Stop
}
func Test_shouldUseCTable(t *testing.T) {
if !shouldUseCTable([]rune{FNC1, '1', '2'}, startCSymbol) {
t.Error("[FNC1]12 failed")
}
if shouldUseCTable([]rune{FNC1, '1'}, startCSymbol) {
t.Error("[FNC1]1 failed")
}
if shouldUseCTable([]rune{'0', FNC1, '1'}, startCSymbol) {
t.Error("0[FNC1]1 failed")
}
if !shouldUseCTable([]rune{'0', '1', FNC1, '2', '3'}, startBSymbol) {
t.Error("01[FNC1]23 failed")
}
if shouldUseCTable([]rune{'0', '1', FNC1}, startBSymbol) {
t.Error("01[FNC1] failed")
}
}
func Test_Issue16(t *testing.T) {
if !shouldUseATable([]rune{'\r', 'A'}, 0) {
t.Error("Code should start with A-Table if the text start with \\r")
}
if !shouldUseATable([]rune{FNC1, '\r'}, 0) {
t.Error("Code should start with A-Table if the text start with <FNC1>\\r")
}
if shouldUseATable([]rune{FNC1, '1', '2', '3'}, 0) {
t.Error("Code should not start with A-Table if the text start with <FNC1>123")
}
testEncode(t, string(FNC3)+"$P\rI", "110100001001011110001010010001100111011101101111011101011000100010110001010001100011101011")
}
func Test_Datalogic(t *testing.T) {
// <Start A><FNC3>$P\r<checksum><STOP>
testEncode(t, string(FNC3)+"$P\r",
"11010000100"+ // <Start A>
"10111100010"+ // <FNC3>
"10010001100"+ // $
"11101110110"+ // P
"11110111010"+ // CR
"11000100010"+ // checksum = 'I'
"1100011101011") // STOP
// <Start B><FNC3>$P,Ae,P<CR><checksum><STOP>
testEncode(t, string(FNC3)+"$P,Ae,P\r",
"11010010000"+ // <Start B>
"10111100010"+ // <FNC3>
"10010001100"+ // $
"11101110110"+ // P
"10110011100"+ // ,
"10100011000"+ // A
"10110010000"+ // e
"10110011100"+ // ,
"11101110110"+ // P
"11101011110"+ // <Code A>
"11110111010"+ // <CR>
"10110001000"+ // checksum = 'D'
"1100011101011") // STOP
}

View File

@ -110,10 +110,11 @@ var encodingTable = [107][]bool{
[]bool{true, true, false, false, false, true, true, true, false, true, false, true, true},
}
// const startASymbol byte = 103
const startASymbol byte = 103
const startBSymbol byte = 104
const startCSymbol byte = 105
const codeASymbol byte = 101
const codeBSymbol byte = 100
const codeCSymbol byte = 99
@ -130,4 +131,13 @@ const (
FNC4 = '\u00f4'
)
const bTable = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
const abTable = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
const bTable = abTable + "`abcdefghijklmnopqrstuvwxyz{|}~\u007F"
const aOnlyTable = "\u0000\u0001\u0002\u0003\u0004" + // NUL, SOH, STX, ETX, EOT
"\u0005\u0006\u0007\u0008\u0009" + // ENQ, ACK, BEL, BS, HT
"\u000A\u000B\u000C\u000D\u000E" + // LF, VT, FF, CR, SO
"\u000F\u0010\u0011\u0012\u0013" + // SI, DLE, DC1, DC2, DC3
"\u0014\u0015\u0016\u0017\u0018" + // DC4, NAK, SYN, ETB, CAN
"\u0019\u001A\u001B\u001C\u001D" + // EM, SUB, ESC, FS, GS
"\u001E\u001F" // RS, US
const aTable = abTable + aOnlyTable

View File

@ -113,7 +113,7 @@ func prepare(content string) (string, error) {
// Encode returns a code39 barcode for the given content
// if includeChecksum is set to true, a checksum character is calculated and added to the content
func Encode(content string, includeChecksum bool, fullASCIIMode bool) (barcode.Barcode, error) {
func Encode(content string, includeChecksum bool, fullASCIIMode bool) (barcode.BarcodeIntCS, error) {
if fullASCIIMode {
var err error
content, err = prepare(content)
@ -148,5 +148,5 @@ func Encode(content string, includeChecksum bool, fullASCIIMode bool) (barcode.B
if err != nil {
checkSum = 0
}
return utils.New1DCode("Code 39", content, result, int(checkSum)), nil
return utils.New1DCodeIntCheckSum(barcode.TypeCode39, content, result, int(checkSum)), nil
}

31
code39/encoder_test.go Normal file
View File

@ -0,0 +1,31 @@
package code39
import (
"image/color"
"testing"
)
func doTest(t *testing.T, addCS, fullASCII bool, data, testResult string) {
code, err := Encode(data, addCS, fullASCII)
if err != nil {
t.Error(err)
}
if len(testResult) != code.Bounds().Max.X {
t.Errorf("Invalid code size. Expected %d got %d", len(testResult), code.Bounds().Max.X)
}
for i, r := range testResult {
if (code.At(i, 0) == color.Black) != (r == '1') {
t.Errorf("Failed at position %d", i)
}
}
}
func Test_Encode(t *testing.T) {
doTest(t, false, false, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
"1001011011010110101001011010110100101101101101001010101011001011011010110010101"+
"011011001010101010011011011010100110101011010011010101011001101011010101001101011010"+
"100110110110101001010101101001101101011010010101101101001010101011001101101010110010"+
"101101011001010101101100101100101010110100110101011011001101010101001011010110110010"+
"110101010011011010101010011011010110100101011010110010101101101100101010101001101011"+
"011010011010101011001101010101001011011011010010110101011001011010100101101101")
}

131
code93/encoder.go Normal file
View File

@ -0,0 +1,131 @@
// Package code93 can create Code93 barcodes
package code93
import (
"errors"
"strings"
"github.com/boombuler/barcode"
"github.com/boombuler/barcode/utils"
)
type encodeInfo struct {
value int
data int
}
const (
// Special Function 1 ($)
FNC1 = '\u00f1'
// Special Function 2 (%)
FNC2 = '\u00f2'
// Special Function 3 (/)
FNC3 = '\u00f3'
// Special Function 4 (+)
FNC4 = '\u00f4'
)
var encodeTable = map[rune]encodeInfo{
'0': encodeInfo{0, 0x114}, '1': encodeInfo{1, 0x148}, '2': encodeInfo{2, 0x144},
'3': encodeInfo{3, 0x142}, '4': encodeInfo{4, 0x128}, '5': encodeInfo{5, 0x124},
'6': encodeInfo{6, 0x122}, '7': encodeInfo{7, 0x150}, '8': encodeInfo{8, 0x112},
'9': encodeInfo{9, 0x10A}, 'A': encodeInfo{10, 0x1A8}, 'B': encodeInfo{11, 0x1A4},
'C': encodeInfo{12, 0x1A2}, 'D': encodeInfo{13, 0x194}, 'E': encodeInfo{14, 0x192},
'F': encodeInfo{15, 0x18A}, 'G': encodeInfo{16, 0x168}, 'H': encodeInfo{17, 0x164},
'I': encodeInfo{18, 0x162}, 'J': encodeInfo{19, 0x134}, 'K': encodeInfo{20, 0x11A},
'L': encodeInfo{21, 0x158}, 'M': encodeInfo{22, 0x14C}, 'N': encodeInfo{23, 0x146},
'O': encodeInfo{24, 0x12C}, 'P': encodeInfo{25, 0x116}, 'Q': encodeInfo{26, 0x1B4},
'R': encodeInfo{27, 0x1B2}, 'S': encodeInfo{28, 0x1AC}, 'T': encodeInfo{29, 0x1A6},
'U': encodeInfo{30, 0x196}, 'V': encodeInfo{31, 0x19A}, 'W': encodeInfo{32, 0x16C},
'X': encodeInfo{33, 0x166}, 'Y': encodeInfo{34, 0x136}, 'Z': encodeInfo{35, 0x13A},
'-': encodeInfo{36, 0x12E}, '.': encodeInfo{37, 0x1D4}, ' ': encodeInfo{38, 0x1D2},
'$': encodeInfo{39, 0x1CA}, '/': encodeInfo{40, 0x16E}, '+': encodeInfo{41, 0x176},
'%': encodeInfo{42, 0x1AE}, FNC1: encodeInfo{43, 0x126}, FNC2: encodeInfo{44, 0x1DA},
FNC3: encodeInfo{45, 0x1D6}, FNC4: encodeInfo{46, 0x132}, '*': encodeInfo{47, 0x15E},
}
var extendedTable = []string{
"\u00f2U", "\u00f1A", "\u00f1B", "\u00f1C", "\u00f1D", "\u00f1E", "\u00f1F", "\u00f1G",
"\u00f1H", "\u00f1I", "\u00f1J", "\u00f1K", "\u00f1L", "\u00f1M", "\u00f1N", "\u00f1O",
"\u00f1P", "\u00f1Q", "\u00f1R", "\u00f1S", "\u00f1T", "\u00f1U", "\u00f1V", "\u00f1W",
"\u00f1X", "\u00f1Y", "\u00f1Z", "\u00f2A", "\u00f2B", "\u00f2C", "\u00f2D", "\u00f2E",
" ", "\u00f3A", "\u00f3B", "\u00f3C", "\u00f3D", "\u00f3E", "\u00f3F", "\u00f3G",
"\u00f3H", "\u00f3I", "\u00f3J", "\u00f3K", "\u00f3L", "-", ".", "\u00f3O",
"0", "1", "2", "3", "4", "5", "6", "7",
"8", "9", "\u00f3Z", "\u00f2F", "\u00f2G", "\u00f2H", "\u00f2I", "\u00f2J",
"\u00f2V", "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", "\u00f2K", "\u00f2L", "\u00f2M", "\u00f2N", "\u00f2O",
"\u00f2W", "\u00f4A", "\u00f4B", "\u00f4C", "\u00f4D", "\u00f4E", "\u00f4F", "\u00f4G",
"\u00f4H", "\u00f4I", "\u00f4J", "\u00f4K", "\u00f4L", "\u00f4M", "\u00f4N", "\u00f4O",
"\u00f4P", "\u00f4Q", "\u00f4R", "\u00f4S", "\u00f4T", "\u00f4U", "\u00f4V", "\u00f4W",
"\u00f4X", "\u00f4Y", "\u00f4Z", "\u00f2P", "\u00f2Q", "\u00f2R", "\u00f2S", "\u00f2T",
}
func prepare(content string) (string, error) {
result := ""
for _, r := range content {
if r > 127 {
return "", errors.New("Only ASCII strings can be encoded")
}
result += extendedTable[int(r)]
}
return result, nil
}
// Encode returns a code93 barcode for the given content
// if includeChecksum is set to true, two checksum characters are calculated and added to the content
func Encode(content string, includeChecksum bool, fullASCIIMode bool) (barcode.Barcode, error) {
if fullASCIIMode {
var err error
content, err = prepare(content)
if err != nil {
return nil, err
}
} else if strings.ContainsRune(content, '*') {
return nil, errors.New("invalid data! content may not contain '*'")
}
data := content + string(getChecksum(content, 20))
data += string(getChecksum(data, 15))
data = "*" + data + "*"
result := new(utils.BitList)
for _, r := range data {
info, ok := encodeTable[r]
if !ok {
return nil, errors.New("invalid data!")
}
result.AddBits(info.data, 9)
}
result.AddBit(true)
return utils.New1DCode(barcode.TypeCode93, content, result), nil
}
func getChecksum(content string, maxWeight int) rune {
weight := 1
total := 0
data := []rune(content)
for i := len(data) - 1; i >= 0; i-- {
r := data[i]
info, ok := encodeTable[r]
if !ok {
return ' '
}
total += info.value * weight
if weight++; weight > maxWeight {
weight = 1
}
}
total = total % 47
for r, info := range encodeTable {
if info.value == total {
return r
}
}
return ' '
}

39
code93/encoder_test.go Normal file
View File

@ -0,0 +1,39 @@
package code93
import (
"image/color"
"testing"
)
func doTest(t *testing.T, data, testResult string) {
code, err := Encode(data, true, false)
if err != nil {
t.Error(err)
}
if len(testResult) != code.Bounds().Max.X {
t.Errorf("Invalid code size. Expected %d got %d", len(testResult), code.Bounds().Max.X)
}
for i, r := range testResult {
if (code.At(i, 0) == color.Black) != (r == '1') {
t.Errorf("Failed at position %d", i)
}
}
}
func Test_CheckSum(t *testing.T) {
if r := getChecksum("TEST93", 20); r != '+' {
t.Errorf("Checksum C-Failed. Got %s", string(r))
}
if r := getChecksum("TEST93+", 15); r != '6' {
t.Errorf("Checksum K-Failed. Got %s", string(r))
}
}
func Test_Encode(t *testing.T) {
doTest(t, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
"1010111101101010001101001001101000101100101001100100101100010101011010001011001"+
"001011000101001101001000110101010110001010011001010001101001011001000101101101101001"+
"101100101101011001101001101100101101100110101011011001011001101001101101001110101000"+
"101001010010001010001001010000101001010001001001001001000101010100001000100101000010"+
"101001110101010000101010111101")
}

View File

@ -2,6 +2,7 @@ package datamatrix
import (
"github.com/boombuler/barcode/utils"
"strconv"
)
type setValFunc func(byte)
@ -24,7 +25,7 @@ func (l *codeLayout) Occupied(row, col int) bool {
return l.occupy.GetBit(col + row*l.size.MatrixColumns())
}
func (l *codeLayout) Set(row, col int, value, bitNum byte, occupy bool) {
func (l *codeLayout) Set(row, col int, value, bitNum byte) {
val := ((value >> (7 - bitNum)) & 1) == 1
if row < 0 {
row += l.size.MatrixRows()
@ -34,95 +35,98 @@ func (l *codeLayout) Set(row, col int, value, bitNum byte, occupy bool) {
col += l.size.MatrixColumns()
row += 4 - ((l.size.MatrixColumns() + 4) % 8)
}
l.matrix.SetBit(col+row*l.size.MatrixColumns(), val)
if occupy {
l.occupy.SetBit(col+row*l.size.MatrixColumns(), true)
if l.Occupied(row, col) {
panic("Field already occupied row: " + strconv.Itoa(row) + " col: " + strconv.Itoa(col))
}
l.occupy.SetBit(col+row*l.size.MatrixColumns(), true)
l.matrix.SetBit(col+row*l.size.MatrixColumns(), val)
}
func (l *codeLayout) SetSimple(row, col int, value byte) {
l.Set(row-2, col-2, value, 0, true)
l.Set(row-2, col-1, value, 1, true)
l.Set(row-1, col-2, value, 2, true)
l.Set(row-1, col-1, value, 3, true)
l.Set(row-1, col-0, value, 4, true)
l.Set(row-0, col-2, value, 5, true)
l.Set(row-0, col-1, value, 6, true)
l.Set(row-0, col-0, value, 7, true)
l.Set(row-2, col-2, value, 0)
l.Set(row-2, col-1, value, 1)
l.Set(row-1, col-2, value, 2)
l.Set(row-1, col-1, value, 3)
l.Set(row-1, col-0, value, 4)
l.Set(row-0, col-2, value, 5)
l.Set(row-0, col-1, value, 6)
l.Set(row-0, col-0, value, 7)
}
func (l *codeLayout) Corner1(value byte) {
l.Set(l.size.MatrixRows()-1, 0, value, 0, true)
l.Set(l.size.MatrixRows()-1, 1, value, 1, true)
l.Set(l.size.MatrixRows()-1, 2, value, 2, true)
l.Set(0, l.size.MatrixColumns()-2, value, 3, true)
l.Set(0, l.size.MatrixColumns()-1, value, 4, true)
l.Set(1, l.size.MatrixColumns()-1, value, 5, true)
l.Set(2, l.size.MatrixColumns()-1, value, 6, true)
l.Set(3, l.size.MatrixColumns()-1, value, 7, true)
l.Set(l.size.MatrixRows()-1, 0, value, 0)
l.Set(l.size.MatrixRows()-1, 1, value, 1)
l.Set(l.size.MatrixRows()-1, 2, value, 2)
l.Set(0, l.size.MatrixColumns()-2, value, 3)
l.Set(0, l.size.MatrixColumns()-1, value, 4)
l.Set(1, l.size.MatrixColumns()-1, value, 5)
l.Set(2, l.size.MatrixColumns()-1, value, 6)
l.Set(3, l.size.MatrixColumns()-1, value, 7)
}
func (l *codeLayout) Corner2(value byte) {
l.Set(l.size.MatrixRows()-3, 0, value, 0, true)
l.Set(l.size.MatrixRows()-2, 0, value, 1, true)
l.Set(l.size.MatrixRows()-1, 0, value, 2, true)
l.Set(0, l.size.MatrixColumns()-4, value, 3, true)
l.Set(0, l.size.MatrixColumns()-3, value, 4, true)
l.Set(0, l.size.MatrixColumns()-2, value, 5, true)
l.Set(0, l.size.MatrixColumns()-1, value, 6, true)
l.Set(1, l.size.MatrixColumns()-1, value, 7, true)
l.Set(l.size.MatrixRows()-3, 0, value, 0)
l.Set(l.size.MatrixRows()-2, 0, value, 1)
l.Set(l.size.MatrixRows()-1, 0, value, 2)
l.Set(0, l.size.MatrixColumns()-4, value, 3)
l.Set(0, l.size.MatrixColumns()-3, value, 4)
l.Set(0, l.size.MatrixColumns()-2, value, 5)
l.Set(0, l.size.MatrixColumns()-1, value, 6)
l.Set(1, l.size.MatrixColumns()-1, value, 7)
}
func (l *codeLayout) Corner3(value byte) {
l.Set(l.size.MatrixRows()-3, 0, value, 0, true)
l.Set(l.size.MatrixRows()-2, 0, value, 1, true)
l.Set(l.size.MatrixRows()-1, 0, value, 2, true)
l.Set(0, l.size.MatrixColumns()-2, value, 3, true)
l.Set(0, l.size.MatrixColumns()-1, value, 4, true)
l.Set(1, l.size.MatrixColumns()-1, value, 5, true)
l.Set(2, l.size.MatrixColumns()-1, value, 6, true)
l.Set(3, l.size.MatrixColumns()-1, value, 7, true)
l.Set(l.size.MatrixRows()-3, 0, value, 0)
l.Set(l.size.MatrixRows()-2, 0, value, 1)
l.Set(l.size.MatrixRows()-1, 0, value, 2)
l.Set(0, l.size.MatrixColumns()-2, value, 3)
l.Set(0, l.size.MatrixColumns()-1, value, 4)
l.Set(1, l.size.MatrixColumns()-1, value, 5)
l.Set(2, l.size.MatrixColumns()-1, value, 6)
l.Set(3, l.size.MatrixColumns()-1, value, 7)
}
func (l *codeLayout) Corner4(value byte) {
l.Set(l.size.MatrixRows()-1, 0, value, 0, true)
l.Set(l.size.MatrixRows()-1, l.size.MatrixColumns()-1, value, 1, true)
l.Set(0, l.size.MatrixColumns()-3, value, 2, true)
l.Set(0, l.size.MatrixColumns()-2, value, 3, true)
l.Set(0, l.size.MatrixColumns()-1, value, 4, true)
l.Set(1, l.size.MatrixColumns()-3, value, 5, true)
l.Set(1, l.size.MatrixColumns()-2, value, 6, true)
l.Set(1, l.size.MatrixColumns()-1, value, 7, true)
l.Set(l.size.MatrixRows()-1, 0, value, 0)
l.Set(l.size.MatrixRows()-1, l.size.MatrixColumns()-1, value, 1)
l.Set(0, l.size.MatrixColumns()-3, value, 2)
l.Set(0, l.size.MatrixColumns()-2, value, 3)
l.Set(0, l.size.MatrixColumns()-1, value, 4)
l.Set(1, l.size.MatrixColumns()-3, value, 5)
l.Set(1, l.size.MatrixColumns()-2, value, 6)
l.Set(1, l.size.MatrixColumns()-1, value, 7)
}
func (l *codeLayout) IterateSetter() <-chan setValFunc {
result := make(chan setValFunc)
go func() {
func (l *codeLayout) SetValues(data []byte) {
idx := 0
row := 4
col := 0
for (row < l.size.MatrixRows()) || (col < l.size.MatrixColumns()) {
if (row == l.size.MatrixRows()) && (col == 0) {
result <- l.Corner1
l.Corner1(data[idx])
idx++
}
if (row == l.size.MatrixRows()-2) && (col == 0) && (l.size.MatrixColumns()%4 != 0) {
result <- l.Corner2
l.Corner2(data[idx])
idx++
}
if (row == l.size.MatrixRows()-2) && (col == 0) && (l.size.MatrixColumns()%8 == 4) {
result <- l.Corner3
l.Corner3(data[idx])
idx++
}
if (row == l.size.MatrixRows()+4) && (col == 2) && (l.size.MatrixColumns()%8 == 0) {
result <- l.Corner4
l.Corner4(data[idx])
idx++
}
for true {
if (row < l.size.MatrixRows()) && (col >= 0) && !l.Occupied(row, col) {
r := row
c := col
result <- func(b byte) {
l.SetSimple(r, c, b)
}
l.SetSimple(row, col, data[idx])
idx++
}
row -= 2
col += 2
@ -130,17 +134,13 @@ func (l *codeLayout) IterateSetter() <-chan setValFunc {
break
}
}
row += 1
col += 3
for true {
if (row >= 0) && (col < l.size.MatrixColumns()) && !l.Occupied(row, col) {
r := row
c := col
result <- func(b byte) {
l.SetSimple(r, c, b)
}
l.SetSimple(row, col, data[idx])
idx++
}
row += 2
col -= 2
@ -150,12 +150,12 @@ func (l *codeLayout) IterateSetter() <-chan setValFunc {
}
row += 3
col += 1
}
close(result)
}()
return result
if !l.Occupied(l.size.MatrixRows()-1, l.size.MatrixColumns()-1) {
l.Set(l.size.MatrixRows()-1, l.size.MatrixColumns()-1, 255, 0)
l.Set(l.size.MatrixRows()-2, l.size.MatrixColumns()-2, 255, 0)
}
}
func (l *codeLayout) Merge() *datamatrixCode {

View File

@ -0,0 +1,102 @@
package datamatrix
import (
"bytes"
"testing"
)
func codeFromStr(str string, size *dmCodeSize) *datamatrixCode {
code := newDataMatrixCode(size)
idx := 0
for _, r := range str {
x := idx % size.Columns
y := idx / size.Columns
switch r {
case '#':
code.set(x, y, true)
case '.':
code.set(x, y, false)
default:
continue
}
idx++
}
return code
}
func Test_Issue12(t *testing.T) {
data := `{"po":12,"batchAction":"start_end"}`
realData := addPadding(encodeText(data), 36)
wantedData := []byte{124, 35, 113, 112, 35, 59, 142, 45, 35, 99, 98, 117, 100, 105, 66, 100, 117, 106, 112, 111, 35, 59, 35, 116, 117, 98, 115, 117, 96, 102, 111, 101, 35, 126, 129, 181}
if bytes.Compare(realData, wantedData) != 0 {
t.Error("Data Encoding failed")
return
}
var codeSize *dmCodeSize
for _, s := range codeSizes {
if s.DataCodewords() >= len(wantedData) {
codeSize = s
break
}
}
realECC := ec.calcECC(realData, codeSize)[len(realData):]
wantedECC := []byte{196, 53, 147, 192, 151, 213, 107, 61, 98, 251, 50, 71, 186, 15, 43, 111, 165, 243, 209, 79, 128, 109, 251, 4}
if bytes.Compare(realECC, wantedECC) != 0 {
t.Errorf("Error correction calculation failed\nGot: %v", realECC)
return
}
barcode := `
#.#.#.#.#.#.#.#.#.#.#.#.
#....###..#..#....#...##
##.......#...#.#.#....#.
#.###...##..#...##.##..#
##...####..##..#.#.#.##.
#.###.##.###..#######.##
#..###...##.##..#.##.##.
#.#.#.#.#.#.###....#.#.#
##.#...#.#.#..#...#####.
#...####..#...##..#.#..#
##...#...##.###.#.....#.
#.###.#.##.#.....###..##
##..#####...#..##...###.
###...#.####.##.#.#.#..#
#..###..#.#.####.#.###..
###.#.#..#..#.###.#.##.#
#####.##.###..#.####.#..
#.##.#......#.#..#.#.###
###.#....######.#...##..
##...#..##.###..#...####
#.######.###.##..#...##.
#..#..#.##.#..####...#.#
###.###..#..##.#.##...#.
########################`
bc, err := Encode(data)
if err != nil {
t.Error(err)
return
}
realResult := bc.(*datamatrixCode)
if realResult.Columns != 24 || realResult.Rows != 24 {
t.Errorf("Got wrong barcode size %dx%d", realResult.Columns, realResult.Rows)
return
}
wantedResult := codeFromStr(barcode, realResult.dmCodeSize)
for x := 0; x < wantedResult.Columns; x++ {
for y := 0; y < wantedResult.Rows; y++ {
r := realResult.get(x, y)
w := wantedResult.get(x, y)
if w != r {
t.Errorf("Failed at: c%d/r%d", x, y)
}
}
}
}

View File

@ -1,10 +1,11 @@
package datamatrix
import (
"github.com/boombuler/barcode"
"github.com/boombuler/barcode/utils"
"image"
"image/color"
"github.com/boombuler/barcode"
"github.com/boombuler/barcode/utils"
)
type datamatrixCode struct {
@ -22,7 +23,7 @@ func (c *datamatrixCode) Content() string {
}
func (c *datamatrixCode) Metadata() barcode.Metadata {
return barcode.Metadata{"DataMatrix", 2}
return barcode.Metadata{barcode.TypeDataMatrix, 2}
}
func (c *datamatrixCode) ColorModel() color.Model {
@ -40,10 +41,6 @@ func (c *datamatrixCode) At(x, y int) color.Color {
return color.White
}
func (c *datamatrixCode) CheckSum() int {
return 0
}
func (c *datamatrixCode) get(x, y int) bool {
return c.GetBit(x*c.Rows + y)
}

View File

@ -34,10 +34,8 @@ func Encode(content string) (barcode.Barcode, error) {
func render(data []byte, size *dmCodeSize) *datamatrixCode {
cl := newCodeLayout(size)
setters := cl.IterateSetter()
for _, b := range data {
(<-setters)(b)
}
cl.SetValues(data)
return cl.Merge()
}

View File

@ -5,58 +5,18 @@ import (
)
type errorCorrection struct {
fld *utils.GaloisField
polynomes map[int][]int
rs *utils.ReedSolomonEncoder
}
var ec *errorCorrection = newErrorCorrection()
func newErrorCorrection() *errorCorrection {
result := new(errorCorrection)
result.fld = utils.NewGaloisField(301)
result.polynomes = make(map[int][]int)
return result
}
gf := utils.NewGaloisField(301, 256, 1)
func (ec *errorCorrection) getPolynomial(count int) []int {
poly, ok := ec.polynomes[count]
if !ok {
idx := 1
poly = make([]int, count+1)
poly[0] = 1
for i := 1; i <= count; i++ {
poly[i] = 1
for j := i - 1; j > 0; j-- {
if poly[j] != 0 {
poly[j] = ec.fld.ALogTbl[(int(ec.fld.LogTbl[poly[j]])+idx)%255]
}
poly[j] = ec.fld.AddOrSub(poly[j], poly[j-1])
}
poly[0] = ec.fld.ALogTbl[(int(ec.fld.LogTbl[poly[0]])+idx)%255]
idx++
}
poly = poly[0:count]
ec.polynomes[count] = poly
}
return poly
}
func (ec *errorCorrection) calcECCBlock(data []byte, poly []int) []byte {
ecc := make([]byte, len(poly)+1)
for i := 0; i < len(data); i++ {
k := ec.fld.AddOrSub(int(ecc[0]), int(data[i]))
for j := 0; j < len(ecc)-1; j++ {
ecc[j] = byte(ec.fld.AddOrSub(int(ecc[j+1]), ec.fld.Multiply(k, poly[len(ecc)-j-2])))
}
}
return ecc
return &errorCorrection{utils.NewReedSolomonEncoder(gf)}
}
func (ec *errorCorrection) calcECC(data []byte, size *dmCodeSize) []byte {
poly := ec.getPolynomial(size.ErrorCorrectionCodewordsPerBlock())
dataSize := len(data)
// make some space for error correction codes
data = append(data, make([]byte, size.ECCCount)...)
@ -64,19 +24,19 @@ func (ec *errorCorrection) calcECC(data []byte, size *dmCodeSize) []byte {
for block := 0; block < size.BlockCount; block++ {
dataCnt := size.DataCodewordsForBlock(block)
buff := make([]byte, dataCnt)
buff := make([]int, dataCnt)
// copy the data for the current block to buff
j := 0
for i := block; i < dataSize; i += size.BlockCount {
buff[j] = data[i]
buff[j] = int(data[i])
j++
}
// calc the error correction codes
ecc := ec.calcECCBlock(buff, poly)
ecc := ec.rs.Encode(buff, size.ErrorCorrectionCodewordsPerBlock())
// and append them to the result
j = 0
for i := block; i < size.ErrorCorrectionCodewordsPerBlock()*size.BlockCount; i += size.BlockCount {
data[dataSize+i] = ecc[j]
data[dataSize+i] = byte(ecc[j])
j++
}
}

View File

@ -5,39 +5,6 @@ import (
"testing"
)
func Test_GetPolynomial(t *testing.T) {
var gf_polys map[int][]int = map[int][]int{
5: []int{228, 48, 15, 111, 62},
7: []int{23, 68, 144, 134, 240, 92, 254},
10: []int{28, 24, 185, 166, 223, 248, 116, 255, 110, 61},
11: []int{175, 138, 205, 12, 194, 168, 39, 245, 60, 97, 120},
12: []int{41, 153, 158, 91, 61, 42, 142, 213, 97, 178, 100, 242},
14: []int{156, 97, 192, 252, 95, 9, 157, 119, 138, 45, 18, 186, 83, 185},
18: []int{83, 195, 100, 39, 188, 75, 66, 61, 241, 213, 109, 129, 94, 254, 225, 48, 90, 188},
20: []int{15, 195, 244, 9, 233, 71, 168, 2, 188, 160, 153, 145, 253, 79, 108, 82, 27, 174, 186, 172},
24: []int{52, 190, 88, 205, 109, 39, 176, 21, 155, 197, 251, 223, 155, 21, 5, 172, 254, 124, 12, 181, 184, 96, 50, 193},
28: []int{211, 231, 43, 97, 71, 96, 103, 174, 37, 151, 170, 53, 75, 34, 249, 121, 17, 138, 110, 213, 141, 136, 120, 151, 233, 168, 93, 255},
36: []int{245, 127, 242, 218, 130, 250, 162, 181, 102, 120, 84, 179, 220, 251, 80, 182, 229, 18, 2, 4, 68, 33, 101, 137, 95, 119, 115, 44, 175, 184, 59, 25, 225, 98, 81, 112},
42: []int{77, 193, 137, 31, 19, 38, 22, 153, 247, 105, 122, 2, 245, 133, 242, 8, 175, 95, 100, 9, 167, 105, 214, 111, 57, 121, 21, 1, 253, 57, 54, 101, 248, 202, 69, 50, 150, 177, 226, 5, 9, 5},
48: []int{245, 132, 172, 223, 96, 32, 117, 22, 238, 133, 238, 231, 205, 188, 237, 87, 191, 106, 16, 147, 118, 23, 37, 90, 170, 205, 131, 88, 120, 100, 66, 138, 186, 240, 82, 44, 176, 87, 187, 147, 160, 175, 69, 213, 92, 253, 225, 19},
56: []int{175, 9, 223, 238, 12, 17, 220, 208, 100, 29, 175, 170, 230, 192, 215, 235, 150, 159, 36, 223, 38, 200, 132, 54, 228, 146, 218, 234, 117, 203, 29, 232, 144, 238, 22, 150, 201, 117, 62, 207, 164, 13, 137, 245, 127, 67, 247, 28, 155, 43, 203, 107, 233, 53, 143, 46},
62: []int{242, 93, 169, 50, 144, 210, 39, 118, 202, 188, 201, 189, 143, 108, 196, 37, 185, 112, 134, 230, 245, 63, 197, 190, 250, 106, 185, 221, 175, 64, 114, 71, 161, 44, 147, 6, 27, 218, 51, 63, 87, 10, 40, 130, 188, 17, 163, 31, 176, 170, 4, 107, 232, 7, 94, 166, 224, 124, 86, 47, 11, 204},
68: []int{220, 228, 173, 89, 251, 149, 159, 56, 89, 33, 147, 244, 154, 36, 73, 127, 213, 136, 248, 180, 234, 197, 158, 177, 68, 122, 93, 213, 15, 160, 227, 236, 66, 139, 153, 185, 202, 167, 179, 25, 220, 232, 96, 210, 231, 136, 223, 239, 181, 241, 59, 52, 172, 25, 49, 232, 211, 189, 64, 54, 108, 153, 132, 63, 96, 103, 82, 186},
}
for i, tst := range gf_polys {
res := ec.getPolynomial(i)
if len(res) != len(tst) {
t.Fail()
}
for i := 0; i < len(res); i++ {
if res[i] != tst[i] {
t.Fail()
}
}
}
}
func Test_CalcECC(t *testing.T) {
data := []byte{142, 164, 186}
var size *dmCodeSize = nil

View File

@ -158,7 +158,7 @@ func encodeEAN13(code string) *utils.BitList {
}
// Encode returns a EAN 8 or EAN 13 barcode for the given code
func Encode(code string) (barcode.Barcode, error) {
func Encode(code string) (barcode.BarcodeIntCS, error) {
var checkSum int
if len(code) == 7 || len(code) == 12 {
code += string(calcCheckNum(code))
@ -175,12 +175,12 @@ func Encode(code string) (barcode.Barcode, error) {
if len(code) == 8 {
result := encodeEAN8(code)
if result != nil {
return utils.New1DCode("EAN 8", code, result, checkSum), nil
return utils.New1DCodeIntCheckSum(barcode.TypeEAN8, code, result, checkSum), nil
}
} else if len(code) == 13 {
result := encodeEAN13(code)
if result != nil {
return utils.New1DCode("EAN 13", code, result, checkSum), nil
return utils.New1DCodeIntCheckSum(barcode.TypeEAN13, code, result, checkSum), nil
}
}
return nil, errors.New("invalid ean code data")

416
pdf417/codewords.go Normal file
View File

@ -0,0 +1,416 @@
package pdf417
const start_word = 0x1fea8
const stop_word = 0x3fa29
var codewords = [][]int{
[]int{
0x1d5c0, 0x1eaf0, 0x1f57c, 0x1d4e0, 0x1ea78, 0x1f53e, 0x1a8c0,
0x1d470, 0x1a860, 0x15040, 0x1a830, 0x15020, 0x1adc0, 0x1d6f0,
0x1eb7c, 0x1ace0, 0x1d678, 0x1eb3e, 0x158c0, 0x1ac70, 0x15860,
0x15dc0, 0x1aef0, 0x1d77c, 0x15ce0, 0x1ae78, 0x1d73e, 0x15c70,
0x1ae3c, 0x15ef0, 0x1af7c, 0x15e78, 0x1af3e, 0x15f7c, 0x1f5fa,
0x1d2e0, 0x1e978, 0x1f4be, 0x1a4c0, 0x1d270, 0x1e93c, 0x1a460,
0x1d238, 0x14840, 0x1a430, 0x1d21c, 0x14820, 0x1a418, 0x14810,
0x1a6e0, 0x1d378, 0x1e9be, 0x14cc0, 0x1a670, 0x1d33c, 0x14c60,
0x1a638, 0x1d31e, 0x14c30, 0x1a61c, 0x14ee0, 0x1a778, 0x1d3be,
0x14e70, 0x1a73c, 0x14e38, 0x1a71e, 0x14f78, 0x1a7be, 0x14f3c,
0x14f1e, 0x1a2c0, 0x1d170, 0x1e8bc, 0x1a260, 0x1d138, 0x1e89e,
0x14440, 0x1a230, 0x1d11c, 0x14420, 0x1a218, 0x14410, 0x14408,
0x146c0, 0x1a370, 0x1d1bc, 0x14660, 0x1a338, 0x1d19e, 0x14630,
0x1a31c, 0x14618, 0x1460c, 0x14770, 0x1a3bc, 0x14738, 0x1a39e,
0x1471c, 0x147bc, 0x1a160, 0x1d0b8, 0x1e85e, 0x14240, 0x1a130,
0x1d09c, 0x14220, 0x1a118, 0x1d08e, 0x14210, 0x1a10c, 0x14208,
0x1a106, 0x14360, 0x1a1b8, 0x1d0de, 0x14330, 0x1a19c, 0x14318,
0x1a18e, 0x1430c, 0x14306, 0x1a1de, 0x1438e, 0x14140, 0x1a0b0,
0x1d05c, 0x14120, 0x1a098, 0x1d04e, 0x14110, 0x1a08c, 0x14108,
0x1a086, 0x14104, 0x141b0, 0x14198, 0x1418c, 0x140a0, 0x1d02e,
0x1a04c, 0x1a046, 0x14082, 0x1cae0, 0x1e578, 0x1f2be, 0x194c0,
0x1ca70, 0x1e53c, 0x19460, 0x1ca38, 0x1e51e, 0x12840, 0x19430,
0x12820, 0x196e0, 0x1cb78, 0x1e5be, 0x12cc0, 0x19670, 0x1cb3c,
0x12c60, 0x19638, 0x12c30, 0x12c18, 0x12ee0, 0x19778, 0x1cbbe,
0x12e70, 0x1973c, 0x12e38, 0x12e1c, 0x12f78, 0x197be, 0x12f3c,
0x12fbe, 0x1dac0, 0x1ed70, 0x1f6bc, 0x1da60, 0x1ed38, 0x1f69e,
0x1b440, 0x1da30, 0x1ed1c, 0x1b420, 0x1da18, 0x1ed0e, 0x1b410,
0x1da0c, 0x192c0, 0x1c970, 0x1e4bc, 0x1b6c0, 0x19260, 0x1c938,
0x1e49e, 0x1b660, 0x1db38, 0x1ed9e, 0x16c40, 0x12420, 0x19218,
0x1c90e, 0x16c20, 0x1b618, 0x16c10, 0x126c0, 0x19370, 0x1c9bc,
0x16ec0, 0x12660, 0x19338, 0x1c99e, 0x16e60, 0x1b738, 0x1db9e,
0x16e30, 0x12618, 0x16e18, 0x12770, 0x193bc, 0x16f70, 0x12738,
0x1939e, 0x16f38, 0x1b79e, 0x16f1c, 0x127bc, 0x16fbc, 0x1279e,
0x16f9e, 0x1d960, 0x1ecb8, 0x1f65e, 0x1b240, 0x1d930, 0x1ec9c,
0x1b220, 0x1d918, 0x1ec8e, 0x1b210, 0x1d90c, 0x1b208, 0x1b204,
0x19160, 0x1c8b8, 0x1e45e, 0x1b360, 0x19130, 0x1c89c, 0x16640,
0x12220, 0x1d99c, 0x1c88e, 0x16620, 0x12210, 0x1910c, 0x16610,
0x1b30c, 0x19106, 0x12204, 0x12360, 0x191b8, 0x1c8de, 0x16760,
0x12330, 0x1919c, 0x16730, 0x1b39c, 0x1918e, 0x16718, 0x1230c,
0x12306, 0x123b8, 0x191de, 0x167b8, 0x1239c, 0x1679c, 0x1238e,
0x1678e, 0x167de, 0x1b140, 0x1d8b0, 0x1ec5c, 0x1b120, 0x1d898,
0x1ec4e, 0x1b110, 0x1d88c, 0x1b108, 0x1d886, 0x1b104, 0x1b102,
0x12140, 0x190b0, 0x1c85c, 0x16340, 0x12120, 0x19098, 0x1c84e,
0x16320, 0x1b198, 0x1d8ce, 0x16310, 0x12108, 0x19086, 0x16308,
0x1b186, 0x16304, 0x121b0, 0x190dc, 0x163b0, 0x12198, 0x190ce,
0x16398, 0x1b1ce, 0x1638c, 0x12186, 0x16386, 0x163dc, 0x163ce,
0x1b0a0, 0x1d858, 0x1ec2e, 0x1b090, 0x1d84c, 0x1b088, 0x1d846,
0x1b084, 0x1b082, 0x120a0, 0x19058, 0x1c82e, 0x161a0, 0x12090,
0x1904c, 0x16190, 0x1b0cc, 0x19046, 0x16188, 0x12084, 0x16184,
0x12082, 0x120d8, 0x161d8, 0x161cc, 0x161c6, 0x1d82c, 0x1d826,
0x1b042, 0x1902c, 0x12048, 0x160c8, 0x160c4, 0x160c2, 0x18ac0,
0x1c570, 0x1e2bc, 0x18a60, 0x1c538, 0x11440, 0x18a30, 0x1c51c,
0x11420, 0x18a18, 0x11410, 0x11408, 0x116c0, 0x18b70, 0x1c5bc,
0x11660, 0x18b38, 0x1c59e, 0x11630, 0x18b1c, 0x11618, 0x1160c,
0x11770, 0x18bbc, 0x11738, 0x18b9e, 0x1171c, 0x117bc, 0x1179e,
0x1cd60, 0x1e6b8, 0x1f35e, 0x19a40, 0x1cd30, 0x1e69c, 0x19a20,
0x1cd18, 0x1e68e, 0x19a10, 0x1cd0c, 0x19a08, 0x1cd06, 0x18960,
0x1c4b8, 0x1e25e, 0x19b60, 0x18930, 0x1c49c, 0x13640, 0x11220,
0x1cd9c, 0x1c48e, 0x13620, 0x19b18, 0x1890c, 0x13610, 0x11208,
0x13608, 0x11360, 0x189b8, 0x1c4de, 0x13760, 0x11330, 0x1cdde,
0x13730, 0x19b9c, 0x1898e, 0x13718, 0x1130c, 0x1370c, 0x113b8,
0x189de, 0x137b8, 0x1139c, 0x1379c, 0x1138e, 0x113de, 0x137de,
0x1dd40, 0x1eeb0, 0x1f75c, 0x1dd20, 0x1ee98, 0x1f74e, 0x1dd10,
0x1ee8c, 0x1dd08, 0x1ee86, 0x1dd04, 0x19940, 0x1ccb0, 0x1e65c,
0x1bb40, 0x19920, 0x1eedc, 0x1e64e, 0x1bb20, 0x1dd98, 0x1eece,
0x1bb10, 0x19908, 0x1cc86, 0x1bb08, 0x1dd86, 0x19902, 0x11140,
0x188b0, 0x1c45c, 0x13340, 0x11120, 0x18898, 0x1c44e, 0x17740,
0x13320, 0x19998, 0x1ccce, 0x17720, 0x1bb98, 0x1ddce, 0x18886,
0x17710, 0x13308, 0x19986, 0x17708, 0x11102, 0x111b0, 0x188dc,
0x133b0, 0x11198, 0x188ce, 0x177b0, 0x13398, 0x199ce, 0x17798,
0x1bbce, 0x11186, 0x13386, 0x111dc, 0x133dc, 0x111ce, 0x177dc,
0x133ce, 0x1dca0, 0x1ee58, 0x1f72e, 0x1dc90, 0x1ee4c, 0x1dc88,
0x1ee46, 0x1dc84, 0x1dc82, 0x198a0, 0x1cc58, 0x1e62e, 0x1b9a0,
0x19890, 0x1ee6e, 0x1b990, 0x1dccc, 0x1cc46, 0x1b988, 0x19884,
0x1b984, 0x19882, 0x1b982, 0x110a0, 0x18858, 0x1c42e, 0x131a0,
0x11090, 0x1884c, 0x173a0, 0x13190, 0x198cc, 0x18846, 0x17390,
0x1b9cc, 0x11084, 0x17388, 0x13184, 0x11082, 0x13182, 0x110d8,
0x1886e, 0x131d8, 0x110cc, 0x173d8, 0x131cc, 0x110c6, 0x173cc,
0x131c6, 0x110ee, 0x173ee, 0x1dc50, 0x1ee2c, 0x1dc48, 0x1ee26,
0x1dc44, 0x1dc42, 0x19850, 0x1cc2c, 0x1b8d0, 0x19848, 0x1cc26,
0x1b8c8, 0x1dc66, 0x1b8c4, 0x19842, 0x1b8c2, 0x11050, 0x1882c,
0x130d0, 0x11048, 0x18826, 0x171d0, 0x130c8, 0x19866, 0x171c8,
0x1b8e6, 0x11042, 0x171c4, 0x130c2, 0x171c2, 0x130ec, 0x171ec,
0x171e6, 0x1ee16, 0x1dc22, 0x1cc16, 0x19824, 0x19822, 0x11028,
0x13068, 0x170e8, 0x11022, 0x13062, 0x18560, 0x10a40, 0x18530,
0x10a20, 0x18518, 0x1c28e, 0x10a10, 0x1850c, 0x10a08, 0x18506,
0x10b60, 0x185b8, 0x1c2de, 0x10b30, 0x1859c, 0x10b18, 0x1858e,
0x10b0c, 0x10b06, 0x10bb8, 0x185de, 0x10b9c, 0x10b8e, 0x10bde,
0x18d40, 0x1c6b0, 0x1e35c, 0x18d20, 0x1c698, 0x18d10, 0x1c68c,
0x18d08, 0x1c686, 0x18d04, 0x10940, 0x184b0, 0x1c25c, 0x11b40,
0x10920, 0x1c6dc, 0x1c24e, 0x11b20, 0x18d98, 0x1c6ce, 0x11b10,
0x10908, 0x18486, 0x11b08, 0x18d86, 0x10902, 0x109b0, 0x184dc,
0x11bb0, 0x10998, 0x184ce, 0x11b98, 0x18dce, 0x11b8c, 0x10986,
0x109dc, 0x11bdc, 0x109ce, 0x11bce, 0x1cea0, 0x1e758, 0x1f3ae,
0x1ce90, 0x1e74c, 0x1ce88, 0x1e746, 0x1ce84, 0x1ce82, 0x18ca0,
0x1c658, 0x19da0, 0x18c90, 0x1c64c, 0x19d90, 0x1cecc, 0x1c646,
0x19d88, 0x18c84, 0x19d84, 0x18c82, 0x19d82, 0x108a0, 0x18458,
0x119a0, 0x10890, 0x1c66e, 0x13ba0, 0x11990, 0x18ccc, 0x18446,
0x13b90, 0x19dcc, 0x10884, 0x13b88, 0x11984, 0x10882, 0x11982,
0x108d8, 0x1846e, 0x119d8, 0x108cc, 0x13bd8, 0x119cc, 0x108c6,
0x13bcc, 0x119c6, 0x108ee, 0x119ee, 0x13bee, 0x1ef50, 0x1f7ac,
0x1ef48, 0x1f7a6, 0x1ef44, 0x1ef42, 0x1ce50, 0x1e72c, 0x1ded0,
0x1ef6c, 0x1e726, 0x1dec8, 0x1ef66, 0x1dec4, 0x1ce42, 0x1dec2,
0x18c50, 0x1c62c, 0x19cd0, 0x18c48, 0x1c626, 0x1bdd0, 0x19cc8,
0x1ce66, 0x1bdc8, 0x1dee6, 0x18c42, 0x1bdc4, 0x19cc2, 0x1bdc2,
0x10850, 0x1842c, 0x118d0, 0x10848, 0x18426, 0x139d0, 0x118c8,
0x18c66, 0x17bd0, 0x139c8, 0x19ce6, 0x10842, 0x17bc8, 0x1bde6,
0x118c2, 0x17bc4, 0x1086c, 0x118ec, 0x10866, 0x139ec, 0x118e6,
0x17bec, 0x139e6, 0x17be6, 0x1ef28, 0x1f796, 0x1ef24, 0x1ef22,
0x1ce28, 0x1e716, 0x1de68, 0x1ef36, 0x1de64, 0x1ce22, 0x1de62,
0x18c28, 0x1c616, 0x19c68, 0x18c24, 0x1bce8, 0x19c64, 0x18c22,
0x1bce4, 0x19c62, 0x1bce2, 0x10828, 0x18416, 0x11868, 0x18c36,
0x138e8, 0x11864, 0x10822, 0x179e8, 0x138e4, 0x11862, 0x179e4,
0x138e2, 0x179e2, 0x11876, 0x179f6, 0x1ef12, 0x1de34, 0x1de32,
0x19c34, 0x1bc74, 0x1bc72, 0x11834, 0x13874, 0x178f4, 0x178f2,
0x10540, 0x10520, 0x18298, 0x10510, 0x10508, 0x10504, 0x105b0,
0x10598, 0x1058c, 0x10586, 0x105dc, 0x105ce, 0x186a0, 0x18690,
0x1c34c, 0x18688, 0x1c346, 0x18684, 0x18682, 0x104a0, 0x18258,
0x10da0, 0x186d8, 0x1824c, 0x10d90, 0x186cc, 0x10d88, 0x186c6,
0x10d84, 0x10482, 0x10d82, 0x104d8, 0x1826e, 0x10dd8, 0x186ee,
0x10dcc, 0x104c6, 0x10dc6, 0x104ee, 0x10dee, 0x1c750, 0x1c748,
0x1c744, 0x1c742, 0x18650, 0x18ed0, 0x1c76c, 0x1c326, 0x18ec8,
0x1c766, 0x18ec4, 0x18642, 0x18ec2, 0x10450, 0x10cd0, 0x10448,
0x18226, 0x11dd0, 0x10cc8, 0x10444, 0x11dc8, 0x10cc4, 0x10442,
0x11dc4, 0x10cc2, 0x1046c, 0x10cec, 0x10466, 0x11dec, 0x10ce6,
0x11de6, 0x1e7a8, 0x1e7a4, 0x1e7a2, 0x1c728, 0x1cf68, 0x1e7b6,
0x1cf64, 0x1c722, 0x1cf62, 0x18628, 0x1c316, 0x18e68, 0x1c736,
0x19ee8, 0x18e64, 0x18622, 0x19ee4, 0x18e62, 0x19ee2, 0x10428,
0x18216, 0x10c68, 0x18636, 0x11ce8, 0x10c64, 0x10422, 0x13de8,
0x11ce4, 0x10c62, 0x13de4, 0x11ce2, 0x10436, 0x10c76, 0x11cf6,
0x13df6, 0x1f7d4, 0x1f7d2, 0x1e794, 0x1efb4, 0x1e792, 0x1efb2,
0x1c714, 0x1cf34, 0x1c712, 0x1df74, 0x1cf32, 0x1df72, 0x18614,
0x18e34, 0x18612, 0x19e74, 0x18e32, 0x1bef4,
},
[]int{
0x1f560, 0x1fab8, 0x1ea40, 0x1f530, 0x1fa9c, 0x1ea20, 0x1f518,
0x1fa8e, 0x1ea10, 0x1f50c, 0x1ea08, 0x1f506, 0x1ea04, 0x1eb60,
0x1f5b8, 0x1fade, 0x1d640, 0x1eb30, 0x1f59c, 0x1d620, 0x1eb18,
0x1f58e, 0x1d610, 0x1eb0c, 0x1d608, 0x1eb06, 0x1d604, 0x1d760,
0x1ebb8, 0x1f5de, 0x1ae40, 0x1d730, 0x1eb9c, 0x1ae20, 0x1d718,
0x1eb8e, 0x1ae10, 0x1d70c, 0x1ae08, 0x1d706, 0x1ae04, 0x1af60,
0x1d7b8, 0x1ebde, 0x15e40, 0x1af30, 0x1d79c, 0x15e20, 0x1af18,
0x1d78e, 0x15e10, 0x1af0c, 0x15e08, 0x1af06, 0x15f60, 0x1afb8,
0x1d7de, 0x15f30, 0x1af9c, 0x15f18, 0x1af8e, 0x15f0c, 0x15fb8,
0x1afde, 0x15f9c, 0x15f8e, 0x1e940, 0x1f4b0, 0x1fa5c, 0x1e920,
0x1f498, 0x1fa4e, 0x1e910, 0x1f48c, 0x1e908, 0x1f486, 0x1e904,
0x1e902, 0x1d340, 0x1e9b0, 0x1f4dc, 0x1d320, 0x1e998, 0x1f4ce,
0x1d310, 0x1e98c, 0x1d308, 0x1e986, 0x1d304, 0x1d302, 0x1a740,
0x1d3b0, 0x1e9dc, 0x1a720, 0x1d398, 0x1e9ce, 0x1a710, 0x1d38c,
0x1a708, 0x1d386, 0x1a704, 0x1a702, 0x14f40, 0x1a7b0, 0x1d3dc,
0x14f20, 0x1a798, 0x1d3ce, 0x14f10, 0x1a78c, 0x14f08, 0x1a786,
0x14f04, 0x14fb0, 0x1a7dc, 0x14f98, 0x1a7ce, 0x14f8c, 0x14f86,
0x14fdc, 0x14fce, 0x1e8a0, 0x1f458, 0x1fa2e, 0x1e890, 0x1f44c,
0x1e888, 0x1f446, 0x1e884, 0x1e882, 0x1d1a0, 0x1e8d8, 0x1f46e,
0x1d190, 0x1e8cc, 0x1d188, 0x1e8c6, 0x1d184, 0x1d182, 0x1a3a0,
0x1d1d8, 0x1e8ee, 0x1a390, 0x1d1cc, 0x1a388, 0x1d1c6, 0x1a384,
0x1a382, 0x147a0, 0x1a3d8, 0x1d1ee, 0x14790, 0x1a3cc, 0x14788,
0x1a3c6, 0x14784, 0x14782, 0x147d8, 0x1a3ee, 0x147cc, 0x147c6,
0x147ee, 0x1e850, 0x1f42c, 0x1e848, 0x1f426, 0x1e844, 0x1e842,
0x1d0d0, 0x1e86c, 0x1d0c8, 0x1e866, 0x1d0c4, 0x1d0c2, 0x1a1d0,
0x1d0ec, 0x1a1c8, 0x1d0e6, 0x1a1c4, 0x1a1c2, 0x143d0, 0x1a1ec,
0x143c8, 0x1a1e6, 0x143c4, 0x143c2, 0x143ec, 0x143e6, 0x1e828,
0x1f416, 0x1e824, 0x1e822, 0x1d068, 0x1e836, 0x1d064, 0x1d062,
0x1a0e8, 0x1d076, 0x1a0e4, 0x1a0e2, 0x141e8, 0x1a0f6, 0x141e4,
0x141e2, 0x1e814, 0x1e812, 0x1d034, 0x1d032, 0x1a074, 0x1a072,
0x1e540, 0x1f2b0, 0x1f95c, 0x1e520, 0x1f298, 0x1f94e, 0x1e510,
0x1f28c, 0x1e508, 0x1f286, 0x1e504, 0x1e502, 0x1cb40, 0x1e5b0,
0x1f2dc, 0x1cb20, 0x1e598, 0x1f2ce, 0x1cb10, 0x1e58c, 0x1cb08,
0x1e586, 0x1cb04, 0x1cb02, 0x19740, 0x1cbb0, 0x1e5dc, 0x19720,
0x1cb98, 0x1e5ce, 0x19710, 0x1cb8c, 0x19708, 0x1cb86, 0x19704,
0x19702, 0x12f40, 0x197b0, 0x1cbdc, 0x12f20, 0x19798, 0x1cbce,
0x12f10, 0x1978c, 0x12f08, 0x19786, 0x12f04, 0x12fb0, 0x197dc,
0x12f98, 0x197ce, 0x12f8c, 0x12f86, 0x12fdc, 0x12fce, 0x1f6a0,
0x1fb58, 0x16bf0, 0x1f690, 0x1fb4c, 0x169f8, 0x1f688, 0x1fb46,
0x168fc, 0x1f684, 0x1f682, 0x1e4a0, 0x1f258, 0x1f92e, 0x1eda0,
0x1e490, 0x1fb6e, 0x1ed90, 0x1f6cc, 0x1f246, 0x1ed88, 0x1e484,
0x1ed84, 0x1e482, 0x1ed82, 0x1c9a0, 0x1e4d8, 0x1f26e, 0x1dba0,
0x1c990, 0x1e4cc, 0x1db90, 0x1edcc, 0x1e4c6, 0x1db88, 0x1c984,
0x1db84, 0x1c982, 0x1db82, 0x193a0, 0x1c9d8, 0x1e4ee, 0x1b7a0,
0x19390, 0x1c9cc, 0x1b790, 0x1dbcc, 0x1c9c6, 0x1b788, 0x19384,
0x1b784, 0x19382, 0x1b782, 0x127a0, 0x193d8, 0x1c9ee, 0x16fa0,
0x12790, 0x193cc, 0x16f90, 0x1b7cc, 0x193c6, 0x16f88, 0x12784,
0x16f84, 0x12782, 0x127d8, 0x193ee, 0x16fd8, 0x127cc, 0x16fcc,
0x127c6, 0x16fc6, 0x127ee, 0x1f650, 0x1fb2c, 0x165f8, 0x1f648,
0x1fb26, 0x164fc, 0x1f644, 0x1647e, 0x1f642, 0x1e450, 0x1f22c,
0x1ecd0, 0x1e448, 0x1f226, 0x1ecc8, 0x1f666, 0x1ecc4, 0x1e442,
0x1ecc2, 0x1c8d0, 0x1e46c, 0x1d9d0, 0x1c8c8, 0x1e466, 0x1d9c8,
0x1ece6, 0x1d9c4, 0x1c8c2, 0x1d9c2, 0x191d0, 0x1c8ec, 0x1b3d0,
0x191c8, 0x1c8e6, 0x1b3c8, 0x1d9e6, 0x1b3c4, 0x191c2, 0x1b3c2,
0x123d0, 0x191ec, 0x167d0, 0x123c8, 0x191e6, 0x167c8, 0x1b3e6,
0x167c4, 0x123c2, 0x167c2, 0x123ec, 0x167ec, 0x123e6, 0x167e6,
0x1f628, 0x1fb16, 0x162fc, 0x1f624, 0x1627e, 0x1f622, 0x1e428,
0x1f216, 0x1ec68, 0x1f636, 0x1ec64, 0x1e422, 0x1ec62, 0x1c868,
0x1e436, 0x1d8e8, 0x1c864, 0x1d8e4, 0x1c862, 0x1d8e2, 0x190e8,
0x1c876, 0x1b1e8, 0x1d8f6, 0x1b1e4, 0x190e2, 0x1b1e2, 0x121e8,
0x190f6, 0x163e8, 0x121e4, 0x163e4, 0x121e2, 0x163e2, 0x121f6,
0x163f6, 0x1f614, 0x1617e, 0x1f612, 0x1e414, 0x1ec34, 0x1e412,
0x1ec32, 0x1c834, 0x1d874, 0x1c832, 0x1d872, 0x19074, 0x1b0f4,
0x19072, 0x1b0f2, 0x120f4, 0x161f4, 0x120f2, 0x161f2, 0x1f60a,
0x1e40a, 0x1ec1a, 0x1c81a, 0x1d83a, 0x1903a, 0x1b07a, 0x1e2a0,
0x1f158, 0x1f8ae, 0x1e290, 0x1f14c, 0x1e288, 0x1f146, 0x1e284,
0x1e282, 0x1c5a0, 0x1e2d8, 0x1f16e, 0x1c590, 0x1e2cc, 0x1c588,
0x1e2c6, 0x1c584, 0x1c582, 0x18ba0, 0x1c5d8, 0x1e2ee, 0x18b90,
0x1c5cc, 0x18b88, 0x1c5c6, 0x18b84, 0x18b82, 0x117a0, 0x18bd8,
0x1c5ee, 0x11790, 0x18bcc, 0x11788, 0x18bc6, 0x11784, 0x11782,
0x117d8, 0x18bee, 0x117cc, 0x117c6, 0x117ee, 0x1f350, 0x1f9ac,
0x135f8, 0x1f348, 0x1f9a6, 0x134fc, 0x1f344, 0x1347e, 0x1f342,
0x1e250, 0x1f12c, 0x1e6d0, 0x1e248, 0x1f126, 0x1e6c8, 0x1f366,
0x1e6c4, 0x1e242, 0x1e6c2, 0x1c4d0, 0x1e26c, 0x1cdd0, 0x1c4c8,
0x1e266, 0x1cdc8, 0x1e6e6, 0x1cdc4, 0x1c4c2, 0x1cdc2, 0x189d0,
0x1c4ec, 0x19bd0, 0x189c8, 0x1c4e6, 0x19bc8, 0x1cde6, 0x19bc4,
0x189c2, 0x19bc2, 0x113d0, 0x189ec, 0x137d0, 0x113c8, 0x189e6,
0x137c8, 0x19be6, 0x137c4, 0x113c2, 0x137c2, 0x113ec, 0x137ec,
0x113e6, 0x137e6, 0x1fba8, 0x175f0, 0x1bafc, 0x1fba4, 0x174f8,
0x1ba7e, 0x1fba2, 0x1747c, 0x1743e, 0x1f328, 0x1f996, 0x132fc,
0x1f768, 0x1fbb6, 0x176fc, 0x1327e, 0x1f764, 0x1f322, 0x1767e,
0x1f762, 0x1e228, 0x1f116, 0x1e668, 0x1e224, 0x1eee8, 0x1f776,
0x1e222, 0x1eee4, 0x1e662, 0x1eee2, 0x1c468, 0x1e236, 0x1cce8,
0x1c464, 0x1dde8, 0x1cce4, 0x1c462, 0x1dde4, 0x1cce2, 0x1dde2,
0x188e8, 0x1c476, 0x199e8, 0x188e4, 0x1bbe8, 0x199e4, 0x188e2,
0x1bbe4, 0x199e2, 0x1bbe2, 0x111e8, 0x188f6, 0x133e8, 0x111e4,
0x177e8, 0x133e4, 0x111e2, 0x177e4, 0x133e2, 0x177e2, 0x111f6,
0x133f6, 0x1fb94, 0x172f8, 0x1b97e, 0x1fb92, 0x1727c, 0x1723e,
0x1f314, 0x1317e, 0x1f734, 0x1f312, 0x1737e, 0x1f732, 0x1e214,
0x1e634, 0x1e212, 0x1ee74, 0x1e632, 0x1ee72, 0x1c434, 0x1cc74,
0x1c432, 0x1dcf4, 0x1cc72, 0x1dcf2, 0x18874, 0x198f4, 0x18872,
0x1b9f4, 0x198f2, 0x1b9f2, 0x110f4, 0x131f4, 0x110f2, 0x173f4,
0x131f2, 0x173f2, 0x1fb8a, 0x1717c, 0x1713e, 0x1f30a, 0x1f71a,
0x1e20a, 0x1e61a, 0x1ee3a, 0x1c41a, 0x1cc3a, 0x1dc7a, 0x1883a,
0x1987a, 0x1b8fa, 0x1107a, 0x130fa, 0x171fa, 0x170be, 0x1e150,
0x1f0ac, 0x1e148, 0x1f0a6, 0x1e144, 0x1e142, 0x1c2d0, 0x1e16c,
0x1c2c8, 0x1e166, 0x1c2c4, 0x1c2c2, 0x185d0, 0x1c2ec, 0x185c8,
0x1c2e6, 0x185c4, 0x185c2, 0x10bd0, 0x185ec, 0x10bc8, 0x185e6,
0x10bc4, 0x10bc2, 0x10bec, 0x10be6, 0x1f1a8, 0x1f8d6, 0x11afc,
0x1f1a4, 0x11a7e, 0x1f1a2, 0x1e128, 0x1f096, 0x1e368, 0x1e124,
0x1e364, 0x1e122, 0x1e362, 0x1c268, 0x1e136, 0x1c6e8, 0x1c264,
0x1c6e4, 0x1c262, 0x1c6e2, 0x184e8, 0x1c276, 0x18de8, 0x184e4,
0x18de4, 0x184e2, 0x18de2, 0x109e8, 0x184f6, 0x11be8, 0x109e4,
0x11be4, 0x109e2, 0x11be2, 0x109f6, 0x11bf6, 0x1f9d4, 0x13af8,
0x19d7e, 0x1f9d2, 0x13a7c, 0x13a3e, 0x1f194, 0x1197e, 0x1f3b4,
0x1f192, 0x13b7e, 0x1f3b2, 0x1e114, 0x1e334, 0x1e112, 0x1e774,
0x1e332, 0x1e772, 0x1c234, 0x1c674, 0x1c232, 0x1cef4, 0x1c672,
0x1cef2, 0x18474, 0x18cf4, 0x18472, 0x19df4, 0x18cf2, 0x19df2,
0x108f4, 0x119f4, 0x108f2, 0x13bf4, 0x119f2, 0x13bf2, 0x17af0,
0x1bd7c, 0x17a78, 0x1bd3e, 0x17a3c, 0x17a1e, 0x1f9ca, 0x1397c,
0x1fbda, 0x17b7c, 0x1393e, 0x17b3e, 0x1f18a, 0x1f39a, 0x1f7ba,
0x1e10a, 0x1e31a, 0x1e73a, 0x1ef7a, 0x1c21a, 0x1c63a, 0x1ce7a,
0x1defa, 0x1843a, 0x18c7a, 0x19cfa, 0x1bdfa, 0x1087a, 0x118fa,
0x139fa, 0x17978, 0x1bcbe, 0x1793c, 0x1791e, 0x138be, 0x179be,
0x178bc, 0x1789e, 0x1785e, 0x1e0a8, 0x1e0a4, 0x1e0a2, 0x1c168,
0x1e0b6, 0x1c164, 0x1c162, 0x182e8, 0x1c176, 0x182e4, 0x182e2,
0x105e8, 0x182f6, 0x105e4, 0x105e2, 0x105f6, 0x1f0d4, 0x10d7e,
0x1f0d2, 0x1e094, 0x1e1b4, 0x1e092, 0x1e1b2, 0x1c134, 0x1c374,
0x1c132, 0x1c372, 0x18274, 0x186f4, 0x18272, 0x186f2, 0x104f4,
0x10df4, 0x104f2, 0x10df2, 0x1f8ea, 0x11d7c, 0x11d3e, 0x1f0ca,
0x1f1da, 0x1e08a, 0x1e19a, 0x1e3ba, 0x1c11a, 0x1c33a, 0x1c77a,
0x1823a, 0x1867a, 0x18efa, 0x1047a, 0x10cfa, 0x11dfa, 0x13d78,
0x19ebe, 0x13d3c, 0x13d1e, 0x11cbe, 0x13dbe, 0x17d70, 0x1bebc,
0x17d38, 0x1be9e, 0x17d1c, 0x17d0e, 0x13cbc, 0x17dbc, 0x13c9e,
0x17d9e, 0x17cb8, 0x1be5e, 0x17c9c, 0x17c8e, 0x13c5e, 0x17cde,
0x17c5c, 0x17c4e, 0x17c2e, 0x1c0b4, 0x1c0b2, 0x18174, 0x18172,
0x102f4, 0x102f2, 0x1e0da, 0x1c09a, 0x1c1ba, 0x1813a, 0x1837a,
0x1027a, 0x106fa, 0x10ebe, 0x11ebc, 0x11e9e, 0x13eb8, 0x19f5e,
0x13e9c, 0x13e8e, 0x11e5e, 0x13ede, 0x17eb0, 0x1bf5c, 0x17e98,
0x1bf4e, 0x17e8c, 0x17e86, 0x13e5c, 0x17edc, 0x13e4e, 0x17ece,
0x17e58, 0x1bf2e, 0x17e4c, 0x17e46, 0x13e2e, 0x17e6e, 0x17e2c,
0x17e26, 0x10f5e, 0x11f5c, 0x11f4e, 0x13f58, 0x19fae, 0x13f4c,
0x13f46, 0x11f2e, 0x13f6e, 0x13f2c, 0x13f26,
},
[]int{
0x1abe0, 0x1d5f8, 0x153c0, 0x1a9f0, 0x1d4fc, 0x151e0, 0x1a8f8,
0x1d47e, 0x150f0, 0x1a87c, 0x15078, 0x1fad0, 0x15be0, 0x1adf8,
0x1fac8, 0x159f0, 0x1acfc, 0x1fac4, 0x158f8, 0x1ac7e, 0x1fac2,
0x1587c, 0x1f5d0, 0x1faec, 0x15df8, 0x1f5c8, 0x1fae6, 0x15cfc,
0x1f5c4, 0x15c7e, 0x1f5c2, 0x1ebd0, 0x1f5ec, 0x1ebc8, 0x1f5e6,
0x1ebc4, 0x1ebc2, 0x1d7d0, 0x1ebec, 0x1d7c8, 0x1ebe6, 0x1d7c4,
0x1d7c2, 0x1afd0, 0x1d7ec, 0x1afc8, 0x1d7e6, 0x1afc4, 0x14bc0,
0x1a5f0, 0x1d2fc, 0x149e0, 0x1a4f8, 0x1d27e, 0x148f0, 0x1a47c,
0x14878, 0x1a43e, 0x1483c, 0x1fa68, 0x14df0, 0x1a6fc, 0x1fa64,
0x14cf8, 0x1a67e, 0x1fa62, 0x14c7c, 0x14c3e, 0x1f4e8, 0x1fa76,
0x14efc, 0x1f4e4, 0x14e7e, 0x1f4e2, 0x1e9e8, 0x1f4f6, 0x1e9e4,
0x1e9e2, 0x1d3e8, 0x1e9f6, 0x1d3e4, 0x1d3e2, 0x1a7e8, 0x1d3f6,
0x1a7e4, 0x1a7e2, 0x145e0, 0x1a2f8, 0x1d17e, 0x144f0, 0x1a27c,
0x14478, 0x1a23e, 0x1443c, 0x1441e, 0x1fa34, 0x146f8, 0x1a37e,
0x1fa32, 0x1467c, 0x1463e, 0x1f474, 0x1477e, 0x1f472, 0x1e8f4,
0x1e8f2, 0x1d1f4, 0x1d1f2, 0x1a3f4, 0x1a3f2, 0x142f0, 0x1a17c,
0x14278, 0x1a13e, 0x1423c, 0x1421e, 0x1fa1a, 0x1437c, 0x1433e,
0x1f43a, 0x1e87a, 0x1d0fa, 0x14178, 0x1a0be, 0x1413c, 0x1411e,
0x141be, 0x140bc, 0x1409e, 0x12bc0, 0x195f0, 0x1cafc, 0x129e0,
0x194f8, 0x1ca7e, 0x128f0, 0x1947c, 0x12878, 0x1943e, 0x1283c,
0x1f968, 0x12df0, 0x196fc, 0x1f964, 0x12cf8, 0x1967e, 0x1f962,
0x12c7c, 0x12c3e, 0x1f2e8, 0x1f976, 0x12efc, 0x1f2e4, 0x12e7e,
0x1f2e2, 0x1e5e8, 0x1f2f6, 0x1e5e4, 0x1e5e2, 0x1cbe8, 0x1e5f6,
0x1cbe4, 0x1cbe2, 0x197e8, 0x1cbf6, 0x197e4, 0x197e2, 0x1b5e0,
0x1daf8, 0x1ed7e, 0x169c0, 0x1b4f0, 0x1da7c, 0x168e0, 0x1b478,
0x1da3e, 0x16870, 0x1b43c, 0x16838, 0x1b41e, 0x1681c, 0x125e0,
0x192f8, 0x1c97e, 0x16de0, 0x124f0, 0x1927c, 0x16cf0, 0x1b67c,
0x1923e, 0x16c78, 0x1243c, 0x16c3c, 0x1241e, 0x16c1e, 0x1f934,
0x126f8, 0x1937e, 0x1fb74, 0x1f932, 0x16ef8, 0x1267c, 0x1fb72,
0x16e7c, 0x1263e, 0x16e3e, 0x1f274, 0x1277e, 0x1f6f4, 0x1f272,
0x16f7e, 0x1f6f2, 0x1e4f4, 0x1edf4, 0x1e4f2, 0x1edf2, 0x1c9f4,
0x1dbf4, 0x1c9f2, 0x1dbf2, 0x193f4, 0x193f2, 0x165c0, 0x1b2f0,
0x1d97c, 0x164e0, 0x1b278, 0x1d93e, 0x16470, 0x1b23c, 0x16438,
0x1b21e, 0x1641c, 0x1640e, 0x122f0, 0x1917c, 0x166f0, 0x12278,
0x1913e, 0x16678, 0x1b33e, 0x1663c, 0x1221e, 0x1661e, 0x1f91a,
0x1237c, 0x1fb3a, 0x1677c, 0x1233e, 0x1673e, 0x1f23a, 0x1f67a,
0x1e47a, 0x1ecfa, 0x1c8fa, 0x1d9fa, 0x191fa, 0x162e0, 0x1b178,
0x1d8be, 0x16270, 0x1b13c, 0x16238, 0x1b11e, 0x1621c, 0x1620e,
0x12178, 0x190be, 0x16378, 0x1213c, 0x1633c, 0x1211e, 0x1631e,
0x121be, 0x163be, 0x16170, 0x1b0bc, 0x16138, 0x1b09e, 0x1611c,
0x1610e, 0x120bc, 0x161bc, 0x1209e, 0x1619e, 0x160b8, 0x1b05e,
0x1609c, 0x1608e, 0x1205e, 0x160de, 0x1605c, 0x1604e, 0x115e0,
0x18af8, 0x1c57e, 0x114f0, 0x18a7c, 0x11478, 0x18a3e, 0x1143c,
0x1141e, 0x1f8b4, 0x116f8, 0x18b7e, 0x1f8b2, 0x1167c, 0x1163e,
0x1f174, 0x1177e, 0x1f172, 0x1e2f4, 0x1e2f2, 0x1c5f4, 0x1c5f2,
0x18bf4, 0x18bf2, 0x135c0, 0x19af0, 0x1cd7c, 0x134e0, 0x19a78,
0x1cd3e, 0x13470, 0x19a3c, 0x13438, 0x19a1e, 0x1341c, 0x1340e,
0x112f0, 0x1897c, 0x136f0, 0x11278, 0x1893e, 0x13678, 0x19b3e,
0x1363c, 0x1121e, 0x1361e, 0x1f89a, 0x1137c, 0x1f9ba, 0x1377c,
0x1133e, 0x1373e, 0x1f13a, 0x1f37a, 0x1e27a, 0x1e6fa, 0x1c4fa,
0x1cdfa, 0x189fa, 0x1bae0, 0x1dd78, 0x1eebe, 0x174c0, 0x1ba70,
0x1dd3c, 0x17460, 0x1ba38, 0x1dd1e, 0x17430, 0x1ba1c, 0x17418,
0x1ba0e, 0x1740c, 0x132e0, 0x19978, 0x1ccbe, 0x176e0, 0x13270,
0x1993c, 0x17670, 0x1bb3c, 0x1991e, 0x17638, 0x1321c, 0x1761c,
0x1320e, 0x1760e, 0x11178, 0x188be, 0x13378, 0x1113c, 0x17778,
0x1333c, 0x1111e, 0x1773c, 0x1331e, 0x1771e, 0x111be, 0x133be,
0x177be, 0x172c0, 0x1b970, 0x1dcbc, 0x17260, 0x1b938, 0x1dc9e,
0x17230, 0x1b91c, 0x17218, 0x1b90e, 0x1720c, 0x17206, 0x13170,
0x198bc, 0x17370, 0x13138, 0x1989e, 0x17338, 0x1b99e, 0x1731c,
0x1310e, 0x1730e, 0x110bc, 0x131bc, 0x1109e, 0x173bc, 0x1319e,
0x1739e, 0x17160, 0x1b8b8, 0x1dc5e, 0x17130, 0x1b89c, 0x17118,
0x1b88e, 0x1710c, 0x17106, 0x130b8, 0x1985e, 0x171b8, 0x1309c,
0x1719c, 0x1308e, 0x1718e, 0x1105e, 0x130de, 0x171de, 0x170b0,
0x1b85c, 0x17098, 0x1b84e, 0x1708c, 0x17086, 0x1305c, 0x170dc,
0x1304e, 0x170ce, 0x17058, 0x1b82e, 0x1704c, 0x17046, 0x1302e,
0x1706e, 0x1702c, 0x17026, 0x10af0, 0x1857c, 0x10a78, 0x1853e,
0x10a3c, 0x10a1e, 0x10b7c, 0x10b3e, 0x1f0ba, 0x1e17a, 0x1c2fa,
0x185fa, 0x11ae0, 0x18d78, 0x1c6be, 0x11a70, 0x18d3c, 0x11a38,
0x18d1e, 0x11a1c, 0x11a0e, 0x10978, 0x184be, 0x11b78, 0x1093c,
0x11b3c, 0x1091e, 0x11b1e, 0x109be, 0x11bbe, 0x13ac0, 0x19d70,
0x1cebc, 0x13a60, 0x19d38, 0x1ce9e, 0x13a30, 0x19d1c, 0x13a18,
0x19d0e, 0x13a0c, 0x13a06, 0x11970, 0x18cbc, 0x13b70, 0x11938,
0x18c9e, 0x13b38, 0x1191c, 0x13b1c, 0x1190e, 0x13b0e, 0x108bc,
0x119bc, 0x1089e, 0x13bbc, 0x1199e, 0x13b9e, 0x1bd60, 0x1deb8,
0x1ef5e, 0x17a40, 0x1bd30, 0x1de9c, 0x17a20, 0x1bd18, 0x1de8e,
0x17a10, 0x1bd0c, 0x17a08, 0x1bd06, 0x17a04, 0x13960, 0x19cb8,
0x1ce5e, 0x17b60, 0x13930, 0x19c9c, 0x17b30, 0x1bd9c, 0x19c8e,
0x17b18, 0x1390c, 0x17b0c, 0x13906, 0x17b06, 0x118b8, 0x18c5e,
0x139b8, 0x1189c, 0x17bb8, 0x1399c, 0x1188e, 0x17b9c, 0x1398e,
0x17b8e, 0x1085e, 0x118de, 0x139de, 0x17bde, 0x17940, 0x1bcb0,
0x1de5c, 0x17920, 0x1bc98, 0x1de4e, 0x17910, 0x1bc8c, 0x17908,
0x1bc86, 0x17904, 0x17902, 0x138b0, 0x19c5c, 0x179b0, 0x13898,
0x19c4e, 0x17998, 0x1bcce, 0x1798c, 0x13886, 0x17986, 0x1185c,
0x138dc, 0x1184e, 0x179dc, 0x138ce, 0x179ce, 0x178a0, 0x1bc58,
0x1de2e, 0x17890, 0x1bc4c, 0x17888, 0x1bc46, 0x17884, 0x17882,
0x13858, 0x19c2e, 0x178d8, 0x1384c, 0x178cc, 0x13846, 0x178c6,
0x1182e, 0x1386e, 0x178ee, 0x17850, 0x1bc2c, 0x17848, 0x1bc26,
0x17844, 0x17842, 0x1382c, 0x1786c, 0x13826, 0x17866, 0x17828,
0x1bc16, 0x17824, 0x17822, 0x13816, 0x17836, 0x10578, 0x182be,
0x1053c, 0x1051e, 0x105be, 0x10d70, 0x186bc, 0x10d38, 0x1869e,
0x10d1c, 0x10d0e, 0x104bc, 0x10dbc, 0x1049e, 0x10d9e, 0x11d60,
0x18eb8, 0x1c75e, 0x11d30, 0x18e9c, 0x11d18, 0x18e8e, 0x11d0c,
0x11d06, 0x10cb8, 0x1865e, 0x11db8, 0x10c9c, 0x11d9c, 0x10c8e,
0x11d8e, 0x1045e, 0x10cde, 0x11dde, 0x13d40, 0x19eb0, 0x1cf5c,
0x13d20, 0x19e98, 0x1cf4e, 0x13d10, 0x19e8c, 0x13d08, 0x19e86,
0x13d04, 0x13d02, 0x11cb0, 0x18e5c, 0x13db0, 0x11c98, 0x18e4e,
0x13d98, 0x19ece, 0x13d8c, 0x11c86, 0x13d86, 0x10c5c, 0x11cdc,
0x10c4e, 0x13ddc, 0x11cce, 0x13dce, 0x1bea0, 0x1df58, 0x1efae,
0x1be90, 0x1df4c, 0x1be88, 0x1df46, 0x1be84, 0x1be82, 0x13ca0,
0x19e58, 0x1cf2e, 0x17da0, 0x13c90, 0x19e4c, 0x17d90, 0x1becc,
0x19e46, 0x17d88, 0x13c84, 0x17d84, 0x13c82, 0x17d82, 0x11c58,
0x18e2e, 0x13cd8, 0x11c4c, 0x17dd8, 0x13ccc, 0x11c46, 0x17dcc,
0x13cc6, 0x17dc6, 0x10c2e, 0x11c6e, 0x13cee, 0x17dee, 0x1be50,
0x1df2c, 0x1be48, 0x1df26, 0x1be44, 0x1be42, 0x13c50, 0x19e2c,
0x17cd0, 0x13c48, 0x19e26, 0x17cc8, 0x1be66, 0x17cc4, 0x13c42,
0x17cc2, 0x11c2c, 0x13c6c, 0x11c26, 0x17cec, 0x13c66, 0x17ce6,
0x1be28, 0x1df16, 0x1be24, 0x1be22, 0x13c28, 0x19e16, 0x17c68,
0x13c24, 0x17c64, 0x13c22, 0x17c62, 0x11c16, 0x13c36, 0x17c76,
0x1be14, 0x1be12, 0x13c14, 0x17c34, 0x13c12, 0x17c32, 0x102bc,
0x1029e, 0x106b8, 0x1835e, 0x1069c, 0x1068e, 0x1025e, 0x106de,
0x10eb0, 0x1875c, 0x10e98, 0x1874e, 0x10e8c, 0x10e86, 0x1065c,
0x10edc, 0x1064e, 0x10ece, 0x11ea0, 0x18f58, 0x1c7ae, 0x11e90,
0x18f4c, 0x11e88, 0x18f46, 0x11e84, 0x11e82, 0x10e58, 0x1872e,
0x11ed8, 0x18f6e, 0x11ecc, 0x10e46, 0x11ec6, 0x1062e, 0x10e6e,
0x11eee, 0x19f50, 0x1cfac, 0x19f48, 0x1cfa6, 0x19f44, 0x19f42,
0x11e50, 0x18f2c, 0x13ed0, 0x19f6c, 0x18f26, 0x13ec8, 0x11e44,
0x13ec4, 0x11e42, 0x13ec2, 0x10e2c, 0x11e6c, 0x10e26, 0x13eec,
0x11e66, 0x13ee6, 0x1dfa8, 0x1efd6, 0x1dfa4, 0x1dfa2, 0x19f28,
0x1cf96, 0x1bf68, 0x19f24, 0x1bf64, 0x19f22, 0x1bf62, 0x11e28,
0x18f16, 0x13e68, 0x11e24, 0x17ee8, 0x13e64, 0x11e22, 0x17ee4,
0x13e62, 0x17ee2, 0x10e16, 0x11e36, 0x13e76, 0x17ef6, 0x1df94,
0x1df92, 0x19f14, 0x1bf34, 0x19f12, 0x1bf32, 0x11e14, 0x13e34,
0x11e12, 0x17e74, 0x13e32, 0x17e72, 0x1df8a, 0x19f0a, 0x1bf1a,
0x11e0a, 0x13e1a, 0x17e3a, 0x1035c, 0x1034e, 0x10758, 0x183ae,
0x1074c, 0x10746, 0x1032e, 0x1076e, 0x10f50, 0x187ac, 0x10f48,
0x187a6, 0x10f44, 0x10f42, 0x1072c, 0x10f6c, 0x10726, 0x10f66,
0x18fa8, 0x1c7d6, 0x18fa4, 0x18fa2, 0x10f28, 0x18796, 0x11f68,
0x18fb6, 0x11f64, 0x10f22, 0x11f62, 0x10716, 0x10f36, 0x11f76,
0x1cfd4, 0x1cfd2, 0x18f94, 0x19fb4, 0x18f92, 0x19fb2, 0x10f14,
0x11f34, 0x10f12, 0x13f74, 0x11f32, 0x13f72, 0x1cfca, 0x18f8a,
0x19f9a, 0x10f0a, 0x11f1a, 0x13f3a, 0x103ac, 0x103a6, 0x107a8,
0x183d6, 0x107a4, 0x107a2, 0x10396, 0x107b6, 0x187d4, 0x187d2,
0x10794, 0x10fb4, 0x10792, 0x10fb2, 0x1c7ea,
},
}
func getCodeword(tableId int, word int) int {
return codewords[tableId][word]
}

57
pdf417/dimensions.go Normal file
View File

@ -0,0 +1,57 @@
package pdf417
import "math"
const (
minCols = 2
maxCols = 30
maxRows = 30
minRows = 2
moduleHeight = 2
preferred_ratio = 3.0
)
func calculateNumberOfRows(m, k, c int) int {
r := ((m + 1 + k) / c) + 1
if c*r >= (m + 1 + k + c) {
r--
}
return r
}
func calcDimensions(dataWords, eccWords int) (cols, rows int) {
ratio := 0.0
cols = 0
rows = 0
for c := minCols; c <= maxCols; c++ {
r := calculateNumberOfRows(dataWords, eccWords, c)
if r < minRows {
break
}
if r > maxRows {
continue
}
newRatio := float64(17*cols+69) / float64(rows*moduleHeight)
if rows != 0 && math.Abs(newRatio-preferred_ratio) > math.Abs(ratio-preferred_ratio) {
continue
}
ratio = newRatio
cols = c
rows = r
}
if rows == 0 {
r := calculateNumberOfRows(dataWords, eccWords, minCols)
if r < minRows {
rows = minRows
cols = minCols
}
}
return
}

162
pdf417/encoder.go Normal file
View File

@ -0,0 +1,162 @@
// Package pdf417 can create PDF-417 barcodes
package pdf417
import (
"fmt"
"github.com/boombuler/barcode"
"github.com/boombuler/barcode/utils"
)
const (
padding_codeword = 900
)
// Encodes the given data as PDF417 barcode.
// securityLevel should be between 0 and 8. The higher the number, the more
// additional error-correction codes are added.
func Encode(data string, securityLevel byte) (barcode.Barcode, error) {
if securityLevel >= 9 {
return nil, fmt.Errorf("Invalid security level %d", securityLevel)
}
sl := securitylevel(securityLevel)
dataWords, err := highlevelEncode(data)
if err != nil {
return nil, err
}
columns, rows := calcDimensions(len(dataWords), sl.ErrorCorrectionWordCount())
if columns < minCols || columns > maxCols || rows < minRows || rows > maxRows {
return nil, fmt.Errorf("Unable to fit data in barcode")
}
barcode := new(pdfBarcode)
barcode.data = data
codeWords, err := encodeData(dataWords, columns, sl)
if err != nil {
return nil, err
}
grid := [][]int{}
for i := 0; i < len(codeWords); i += columns {
grid = append(grid, codeWords[i:min(i+columns, len(codeWords))])
}
codes := [][]int{}
for rowNum, row := range grid {
table := rowNum % 3
rowCodes := make([]int, 0, columns+4)
rowCodes = append(rowCodes, start_word)
rowCodes = append(rowCodes, getCodeword(table, getLeftCodeWord(rowNum, rows, columns, securityLevel)))
for _, word := range row {
rowCodes = append(rowCodes, getCodeword(table, word))
}
rowCodes = append(rowCodes, getCodeword(table, getRightCodeWord(rowNum, rows, columns, securityLevel)))
rowCodes = append(rowCodes, stop_word)
codes = append(codes, rowCodes)
}
barcode.code = renderBarcode(codes)
barcode.width = (columns+4)*17 + 1
return barcode, nil
}
func encodeData(dataWords []int, columns int, sl securitylevel) ([]int, error) {
dataCount := len(dataWords)
ecCount := sl.ErrorCorrectionWordCount()
padWords := getPadding(dataCount, ecCount, columns)
dataWords = append(dataWords, padWords...)
length := len(dataWords) + 1
dataWords = append([]int{length}, dataWords...)
ecWords := sl.Compute(dataWords)
return append(dataWords, ecWords...), nil
}
func getLeftCodeWord(rowNum int, rows int, columns int, securityLevel byte) int {
tableId := rowNum % 3
var x int
switch tableId {
case 0:
x = (rows - 3) / 3
case 1:
x = int(securityLevel) * 3
x += (rows - 1) % 3
case 2:
x = columns - 1
}
return 30*(rowNum/3) + x
}
func getRightCodeWord(rowNum int, rows int, columns int, securityLevel byte) int {
tableId := rowNum % 3
var x int
switch tableId {
case 0:
x = columns - 1
case 1:
x = (rows - 1) / 3
case 2:
x = int(securityLevel) * 3
x += (rows - 1) % 3
}
return 30*(rowNum/3) + x
}
func min(a, b int) int {
if a <= b {
return a
}
return b
}
func getPadding(dataCount int, ecCount int, columns int) []int {
totalCount := dataCount + ecCount + 1
mod := totalCount % columns
padding := []int{}
if mod > 0 {
padCount := columns - mod
padding = make([]int, padCount)
for i := 0; i < padCount; i++ {
padding[i] = padding_codeword
}
}
return padding
}
func renderBarcode(codes [][]int) *utils.BitList {
bl := new(utils.BitList)
for _, row := range codes {
lastIdx := len(row) - 1
for i, col := range row {
if i == lastIdx {
bl.AddBits(col, 18)
} else {
bl.AddBits(col, 17)
}
}
}
return bl
}

151
pdf417/errorcorrection.go Normal file
View File

@ -0,0 +1,151 @@
package pdf417
type securitylevel byte
func (level securitylevel) ErrorCorrectionWordCount() int {
return 1 << (uint(level) + 1)
}
var correctionFactors = [][]int{
// Level 0
[]int{27, 917},
// Level 1
[]int{522, 568, 723, 809},
// Level 2
[]int{237, 308, 436, 284, 646, 653, 428, 379},
// Level 3
[]int{
274, 562, 232, 755, 599, 524, 801, 132, 295, 116, 442, 428, 295, 42,
176, 65,
},
// Level 4
[]int{
361, 575, 922, 525, 176, 586, 640, 321, 536, 742, 677, 742, 687,
284, 193, 517, 273, 494, 263, 147, 593, 800, 571, 320, 803, 133,
231, 390, 685, 330, 63, 410,
},
// Level 5
[]int{
539, 422, 6, 93, 862, 771, 453, 106, 610, 287, 107, 505, 733, 877,
381, 612, 723, 476, 462, 172, 430, 609, 858, 822, 543, 376, 511,
400, 672, 762, 283, 184, 440, 35, 519, 31, 460, 594, 225, 535, 517,
352, 605, 158, 651, 201, 488, 502, 648, 733, 717, 83, 404, 97, 280,
771, 840, 629, 4, 381, 843, 623, 264, 543,
},
// Level 6
[]int{
521, 310, 864, 547, 858, 580, 296, 379, 53, 779, 897, 444, 400, 925,
749, 415, 822, 93, 217, 208, 928, 244, 583, 620, 246, 148, 447, 631,
292, 908, 490, 704, 516, 258, 457, 907, 594, 723, 674, 292, 272, 96,
684, 432, 686, 606, 860, 569, 193, 219, 129, 186, 236, 287, 192,
775, 278, 173, 40, 379, 712, 463, 646, 776, 171, 491, 297, 763, 156,
732, 95, 270, 447, 90, 507, 48, 228, 821, 808, 898, 784, 663, 627,
378, 382, 262, 380, 602, 754, 336, 89, 614, 87, 432, 670, 616, 157,
374, 242, 726, 600, 269, 375, 898, 845, 454, 354, 130, 814, 587,
804, 34, 211, 330, 539, 297, 827, 865, 37, 517, 834, 315, 550, 86,
801, 4, 108, 539,
},
// Level 7
[]int{
524, 894, 75, 766, 882, 857, 74, 204, 82, 586, 708, 250, 905, 786,
138, 720, 858, 194, 311, 913, 275, 190, 375, 850, 438, 733, 194,
280, 201, 280, 828, 757, 710, 814, 919, 89, 68, 569, 11, 204, 796,
605, 540, 913, 801, 700, 799, 137, 439, 418, 592, 668, 353, 859,
370, 694, 325, 240, 216, 257, 284, 549, 209, 884, 315, 70, 329, 793,
490, 274, 877, 162, 749, 812, 684, 461, 334, 376, 849, 521, 307,
291, 803, 712, 19, 358, 399, 908, 103, 511, 51, 8, 517, 225, 289,
470, 637, 731, 66, 255, 917, 269, 463, 830, 730, 433, 848, 585, 136,
538, 906, 90, 2, 290, 743, 199, 655, 903, 329, 49, 802, 580, 355,
588, 188, 462, 10, 134, 628, 320, 479, 130, 739, 71, 263, 318, 374,
601, 192, 605, 142, 673, 687, 234, 722, 384, 177, 752, 607, 640,
455, 193, 689, 707, 805, 641, 48, 60, 732, 621, 895, 544, 261, 852,
655, 309, 697, 755, 756, 60, 231, 773, 434, 421, 726, 528, 503, 118,
49, 795, 32, 144, 500, 238, 836, 394, 280, 566, 319, 9, 647, 550,
73, 914, 342, 126, 32, 681, 331, 792, 620, 60, 609, 441, 180, 791,
893, 754, 605, 383, 228, 749, 760, 213, 54, 297, 134, 54, 834, 299,
922, 191, 910, 532, 609, 829, 189, 20, 167, 29, 872, 449, 83, 402,
41, 656, 505, 579, 481, 173, 404, 251, 688, 95, 497, 555, 642, 543,
307, 159, 924, 558, 648, 55, 497, 10,
},
// Level 8
[]int{
352, 77, 373, 504, 35, 599, 428, 207, 409, 574, 118, 498, 285, 380,
350, 492, 197, 265, 920, 155, 914, 299, 229, 643, 294, 871, 306, 88,
87, 193, 352, 781, 846, 75, 327, 520, 435, 543, 203, 666, 249, 346,
781, 621, 640, 268, 794, 534, 539, 781, 408, 390, 644, 102, 476,
499, 290, 632, 545, 37, 858, 916, 552, 41, 542, 289, 122, 272, 383,
800, 485, 98, 752, 472, 761, 107, 784, 860, 658, 741, 290, 204, 681,
407, 855, 85, 99, 62, 482, 180, 20, 297, 451, 593, 913, 142, 808,
684, 287, 536, 561, 76, 653, 899, 729, 567, 744, 390, 513, 192, 516,
258, 240, 518, 794, 395, 768, 848, 51, 610, 384, 168, 190, 826, 328,
596, 786, 303, 570, 381, 415, 641, 156, 237, 151, 429, 531, 207,
676, 710, 89, 168, 304, 402, 40, 708, 575, 162, 864, 229, 65, 861,
841, 512, 164, 477, 221, 92, 358, 785, 288, 357, 850, 836, 827, 736,
707, 94, 8, 494, 114, 521, 2, 499, 851, 543, 152, 729, 771, 95, 248,
361, 578, 323, 856, 797, 289, 51, 684, 466, 533, 820, 669, 45, 902,
452, 167, 342, 244, 173, 35, 463, 651, 51, 699, 591, 452, 578, 37,
124, 298, 332, 552, 43, 427, 119, 662, 777, 475, 850, 764, 364, 578,
911, 283, 711, 472, 420, 245, 288, 594, 394, 511, 327, 589, 777,
699, 688, 43, 408, 842, 383, 721, 521, 560, 644, 714, 559, 62, 145,
873, 663, 713, 159, 672, 729, 624, 59, 193, 417, 158, 209, 563, 564,
343, 693, 109, 608, 563, 365, 181, 772, 677, 310, 248, 353, 708,
410, 579, 870, 617, 841, 632, 860, 289, 536, 35, 777, 618, 586, 424,
833, 77, 597, 346, 269, 757, 632, 695, 751, 331, 247, 184, 45, 787,
680, 18, 66, 407, 369, 54, 492, 228, 613, 830, 922, 437, 519, 644,
905, 789, 420, 305, 441, 207, 300, 892, 827, 141, 537, 381, 662,
513, 56, 252, 341, 242, 797, 838, 837, 720, 224, 307, 631, 61, 87,
560, 310, 756, 665, 397, 808, 851, 309, 473, 795, 378, 31, 647, 915,
459, 806, 590, 731, 425, 216, 548, 249, 321, 881, 699, 535, 673,
782, 210, 815, 905, 303, 843, 922, 281, 73, 469, 791, 660, 162, 498,
308, 155, 422, 907, 817, 187, 62, 16, 425, 535, 336, 286, 437, 375,
273, 610, 296, 183, 923, 116, 667, 751, 353, 62, 366, 691, 379, 687,
842, 37, 357, 720, 742, 330, 5, 39, 923, 311, 424, 242, 749, 321,
54, 669, 316, 342, 299, 534, 105, 667, 488, 640, 672, 576, 540, 316,
486, 721, 610, 46, 656, 447, 171, 616, 464, 190, 531, 297, 321, 762,
752, 533, 175, 134, 14, 381, 433, 717, 45, 111, 20, 596, 284, 736,
138, 646, 411, 877, 669, 141, 919, 45, 780, 407, 164, 332, 899, 165,
726, 600, 325, 498, 655, 357, 752, 768, 223, 849, 647, 63, 310, 863,
251, 366, 304, 282, 738, 675, 410, 389, 244, 31, 121, 303, 263,
},
}
func (level securitylevel) Compute(data []int) []int {
// Correction factors for the given level
factors := correctionFactors[int(level)]
// Number of correction code words
count := level.ErrorCorrectionWordCount()
// Correction code words array, prepopulated with zeros
ecWords := make([]int, count)
for _, value := range data {
temp := (value + ecWords[0]) % 929
for i := count - 1; i >= 0; i-- {
add := 0
if i > 0 {
add = ecWords[count-i]
}
ecWords[count-1-i] = (add + 929 - (temp*factors[i])%929) % 929
}
}
for key, word := range ecWords {
if word > 0 {
ecWords[key] = 929 - word
}
}
return ecWords
}

View File

@ -0,0 +1,61 @@
package pdf417
import (
"testing"
)
var inputData = []int{16, 902, 1, 278, 827, 900, 295, 902, 2, 326, 823, 544, 900, 149, 900, 900}
func TestReedSolomonComputeLevel0(t *testing.T) {
var level securitylevel = 0
expected := []int{156, 765}
compareIntSlice(t, expected, level.Compute(inputData), "SecLvl 0")
}
func TestReedSolomonComputeLevel1(t *testing.T) {
var level securitylevel = 1
expected := []int{168, 875, 63, 355}
compareIntSlice(t, expected, level.Compute(inputData), "SecLvl 1")
}
func TestReedSolomonComputeLevel2(t *testing.T) {
var level securitylevel = 2
expected := []int{628, 715, 393, 299, 863, 601, 169, 708}
compareIntSlice(t, expected, level.Compute(inputData), "SecLvl 2")
}
func TestReedSolomonComputeLevel3(t *testing.T) {
var level securitylevel = 3
expected := []int{232, 176, 793, 616, 476, 406, 855, 445, 84, 518, 522, 721, 607, 2, 42, 578}
compareIntSlice(t, expected, level.Compute(inputData), "SecLvl 3")
}
func TestReedSolomonComputeLevel4(t *testing.T) {
var level securitylevel = 4
expected := []int{281, 156, 276, 668, 44, 252, 877, 30, 549, 856, 773, 639, 420, 330, 693, 329, 283, 723, 480, 482, 102, 925, 535, 892, 374, 472, 837, 331, 343, 608, 390, 364}
compareIntSlice(t, expected, level.Compute(inputData), "SecLvl 4")
}
func TestReedSolomonComputeLevel5(t *testing.T) {
var level securitylevel = 5
expected := []int{31, 850, 18, 870, 53, 477, 837, 130, 533, 186, 266, 450, 39, 492, 542, 653, 499, 887, 618, 103, 364, 313, 906, 396, 270, 735, 593, 81, 557, 712, 810, 48, 167, 533, 205, 577, 503, 126, 449, 189, 859, 471, 493, 849, 554, 76, 878, 893, 168, 497, 251, 704, 311, 650, 283, 268, 462, 223, 659, 763, 176, 34, 544, 304}
compareIntSlice(t, expected, level.Compute(inputData), "SecLvl 5")
}
func TestReedSolomonComputeLevel6(t *testing.T) {
var level securitylevel = 6
expected := []int{345, 775, 909, 489, 650, 568, 869, 577, 574, 349, 885, 317, 492, 222, 783, 451, 647, 385, 168, 366, 118, 655, 643, 551, 179, 880, 880, 752, 132, 206, 765, 862, 727, 240, 32, 266, 911, 287, 813, 437, 868, 201, 681, 867, 567, 398, 508, 564, 504, 676, 785, 554, 831, 566, 424, 93, 515, 275, 61, 544, 272, 621, 374, 922, 779, 663, 789, 295, 631, 536, 755, 465, 485, 416, 76, 412, 76, 431, 28, 614, 767, 419, 600, 779, 94, 584, 647, 846, 121, 97, 790, 205, 424, 793, 263, 271, 694, 522, 437, 817, 382, 164, 113, 849, 178, 602, 554, 261, 415, 737, 401, 675, 203, 271, 649, 120, 765, 209, 522, 687, 420, 32, 60, 266, 270, 228, 304, 270}
compareIntSlice(t, expected, level.Compute(inputData), "SecLvl 6")
}
func TestReedSolomonComputeLevel7(t *testing.T) {
var level securitylevel = 7
expected := []int{142, 203, 799, 4, 105, 137, 793, 914, 225, 636, 60, 171, 490, 180, 414, 141, 399, 599, 829, 288, 108, 268, 444, 481, 795, 146, 655, 778, 189, 32, 597, 206, 208, 711, 845, 608, 642, 636, 540, 795, 845, 466, 492, 659, 138, 800, 912, 171, 92, 438, 225, 301, 777, 449, 230, 448, 326, 182, 892, 681, 543, 582, 732, 758, 162, 587, 685, 378, 646, 356, 354, 25, 839, 839, 556, 253, 501, 771, 745, 616, 473, 293, 669, 822, 613, 684, 229, 265, 110, 438, 144, 727, 317, 605, 414, 497, 82, 278, 267, 323, 43, 894, 624, 282, 790, 579, 430, 255, 802, 553, 922, 604, 68, 692, 809, 909, 663, 589, 735, 670, 298, 158, 201, 68, 124, 64, 67, 338, 694, 373, 225, 579, 309, 699, 920, 432, 717, 72, 126, 819, 142, 755, 473, 630, 331, 758, 730, 65, 359, 451, 236, 16, 56, 31, 87, 587, 125, 385, 384, 197, 352, 383, 173, 271, 38, 558, 810, 260, 521, 680, 7, 319, 650, 334, 695, 708, 0, 562, 365, 204, 114, 185, 560, 746, 767, 449, 797, 688, 63, 135, 818, 805, 3, 536, 908, 532, 400, 698, 49, 212, 630, 93, 157, 275, 3, 20, 611, 179, 302, 282, 876, 665, 241, 206, 474, 80, 217, 460, 462, 751, 719, 571, 536, 794, 522, 385, 598, 756, 162, 212, 758, 662, 361, 223, 587, 857, 503, 382, 615, 86, 283, 541, 847, 518, 406, 736, 486, 408, 226, 342, 784, 772, 211, 888, 234, 335}
compareIntSlice(t, expected, level.Compute(inputData), "SecLvl 7")
}
func TestReedSolomonComputeLevel8(t *testing.T) {
var level securitylevel = 8
expected := []int{538, 446, 840, 510, 163, 708, 177, 666, 423, 600, 707, 913, 770, 571, 156, 683, 676, 697, 898, 776, 128, 851, 163, 854, 135, 661, 880, 279, 92, 324, 397, 207, 379, 223, 574, 9, 70, 858, 878, 579, 61, 551, 261, 388, 315, 856, 266, 865, 923, 38, 313, 62, 381, 198, 265, 256, 385, 878, 347, 532, 821, 53, 855, 225, 697, 826, 263, 334, 207, 565, 460, 496, 705, 599, 383, 289, 178, 168, 401, 268, 555, 190, 922, 284, 180, 810, 891, 832, 636, 813, 894, 495, 701, 484, 204, 793, 129, 164, 444, 228, 636, 98, 809, 57, 736, 697, 727, 534, 889, 480, 898, 773, 234, 851, 880, 843, 714, 443, 412, 489, 578, 468, 367, 663, 11, 686, 319, 352, 345, 670, 106, 106, 219, 466, 439, 350, 538, 66, 852, 175, 465, 731, 332, 110, 926, 491, 18, 422, 736, 797, 624, 376, 728, 526, 735, 200, 502, 923, 789, 529, 923, 706, 384, 869, 172, 548, 520, 463, 813, 384, 793, 231, 190, 653, 864, 351, 400, 525, 487, 828, 654, 307, 141, 638, 770, 775, 282, 54, 758, 197, 492, 320, 86, 790, 275, 237, 923, 25, 591, 605, 61, 824, 79, 631, 532, 337, 867, 423, 340, 597, 682, 923, 287, 408, 503, 361, 881, 196, 468, 759, 746, 389, 124, 784, 198, 865, 538, 451, 178, 772, 653, 121, 497, 598, 711, 716, 241, 159, 429, 88, 799, 761, 639, 105, 54, 807, 351, 435, 793, 873, 360, 8, 881, 479, 693, 576, 849, 875, 771, 621, 134, 863, 8, 171, 799, 924, 103, 63, 491, 538, 597, 855, 697, 499, 7, 886, 286, 85, 107, 220, 319, 124, 197, 150, 729, 899, 585, 540, 676, 414, 256, 856, 596, 259, 882, 436, 26, 273, 753, 127, 679, 390, 654, 42, 276, 420, 247, 629, 116, 803, 131, 25, 403, 645, 462, 897, 151, 622, 108, 167, 227, 831, 887, 662, 739, 263, 829, 56, 624, 317, 908, 378, 39, 393, 861, 338, 202, 179, 907, 109, 360, 736, 554, 342, 594, 125, 433, 394, 195, 698, 844, 912, 530, 842, 337, 294, 528, 231, 735, 93, 8, 579, 42, 148, 609, 233, 782, 887, 888, 915, 620, 78, 137, 161, 282, 217, 775, 564, 33, 195, 36, 584, 679, 775, 476, 309, 230, 303, 708, 143, 679, 502, 814, 193, 508, 532, 542, 580, 603, 641, 338, 361, 542, 537, 810, 394, 764, 136, 167, 611, 881, 775, 267, 433, 142, 202, 828, 363, 101, 728, 660, 583, 483, 786, 717, 190, 809, 422, 567, 741, 695, 310, 120, 177, 47, 494, 345, 508, 16, 639, 402, 625, 286, 298, 358, 54, 705, 916, 291, 424, 375, 883, 655, 675, 498, 498, 884, 862, 365, 310, 805, 763, 855, 354, 777, 543, 53, 773, 120, 408, 234, 728, 438, 914, 3, 670, 546, 465, 449, 923, 51, 546, 709, 648, 96, 320, 682, 326, 848, 234, 855, 791, 20, 97, 901, 351, 317, 764, 767, 312, 206, 139, 610, 578, 646, 264, 389, 238, 675, 595, 430, 88}
compareIntSlice(t, expected, level.Compute(inputData), "SecLvl 8")
}

354
pdf417/highlevel.go Normal file
View File

@ -0,0 +1,354 @@
package pdf417
import (
"errors"
"math/big"
"github.com/boombuler/barcode/utils"
)
type encodingMode byte
type subMode byte
const (
encText encodingMode = iota
encNumeric
encBinary
subUpper subMode = iota
subLower
subMixed
subPunct
latch_to_text = 900
latch_to_byte_padded = 901
latch_to_numeric = 902
latch_to_byte = 924
shift_to_byte = 913
min_numeric_count = 13
)
var (
mixedMap map[rune]int
punctMap map[rune]int
)
func init() {
mixedMap = make(map[rune]int)
mixedRaw := []rune{
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 38, 13, 9, 44, 58,
35, 45, 46, 36, 47, 43, 37, 42, 61, 94, 0, 32, 0, 0, 0,
}
for idx, ch := range mixedRaw {
if ch > 0 {
mixedMap[ch] = idx
}
}
punctMap = make(map[rune]int)
punctRaw := []rune{
59, 60, 62, 64, 91, 92, 93, 95, 96, 126, 33, 13, 9, 44, 58,
10, 45, 46, 36, 47, 34, 124, 42, 40, 41, 63, 123, 125, 39, 0,
}
for idx, ch := range punctRaw {
if ch > 0 {
punctMap[ch] = idx
}
}
}
func determineConsecutiveDigitCount(data []rune) int {
cnt := 0
for _, r := range data {
if utils.RuneToInt(r) == -1 {
break
}
cnt++
}
return cnt
}
func encodeNumeric(digits []rune) ([]int, error) {
digitCount := len(digits)
chunkCount := digitCount / 44
if digitCount%44 != 0 {
chunkCount++
}
codeWords := []int{}
for i := 0; i < chunkCount; i++ {
start := i * 44
end := start + 44
if end > digitCount {
end = digitCount
}
chunk := digits[start:end]
chunkNum := big.NewInt(0)
_, ok := chunkNum.SetString("1"+string(chunk), 10)
if !ok {
return nil, errors.New("Failed converting: " + string(chunk))
}
cws := []int{}
for chunkNum.Cmp(big.NewInt(0)) > 0 {
newChunk, cw := chunkNum.DivMod(chunkNum, big.NewInt(900), big.NewInt(0))
chunkNum = newChunk
cws = append([]int{int(cw.Int64())}, cws...)
}
codeWords = append(codeWords, cws...)
}
return codeWords, nil
}
func determineConsecutiveTextCount(msg []rune) int {
result := 0
isText := func(ch rune) bool {
return ch == '\t' || ch == '\n' || ch == '\r' || (ch >= 32 && ch <= 126)
}
for i, ch := range msg {
numericCount := determineConsecutiveDigitCount(msg[i:])
if numericCount >= min_numeric_count || (numericCount == 0 && !isText(ch)) {
break
}
result++
}
return result
}
func encodeText(text []rune, submode subMode) (subMode, []int) {
isAlphaUpper := func(ch rune) bool {
return ch == ' ' || (ch >= 'A' && ch <= 'Z')
}
isAlphaLower := func(ch rune) bool {
return ch == ' ' || (ch >= 'a' && ch <= 'z')
}
isMixed := func(ch rune) bool {
_, ok := mixedMap[ch]
return ok
}
isPunctuation := func(ch rune) bool {
_, ok := punctMap[ch]
return ok
}
idx := 0
var tmp []int
for idx < len(text) {
ch := text[idx]
switch submode {
case subUpper:
if isAlphaUpper(ch) {
if ch == ' ' {
tmp = append(tmp, 26) //space
} else {
tmp = append(tmp, int(ch-'A'))
}
} else {
if isAlphaLower(ch) {
submode = subLower
tmp = append(tmp, 27) // lower latch
continue
} else if isMixed(ch) {
submode = subMixed
tmp = append(tmp, 28) // mixed latch
continue
} else {
tmp = append(tmp, 29) // punctuation switch
tmp = append(tmp, punctMap[ch])
break
}
}
break
case subLower:
if isAlphaLower(ch) {
if ch == ' ' {
tmp = append(tmp, 26) //space
} else {
tmp = append(tmp, int(ch-'a'))
}
} else {
if isAlphaUpper(ch) {
tmp = append(tmp, 27) //upper switch
tmp = append(tmp, int(ch-'A'))
break
} else if isMixed(ch) {
submode = subMixed
tmp = append(tmp, 28) //mixed latch
continue
} else {
tmp = append(tmp, 29) //punctuation switch
tmp = append(tmp, punctMap[ch])
break
}
}
break
case subMixed:
if isMixed(ch) {
tmp = append(tmp, mixedMap[ch])
} else {
if isAlphaUpper(ch) {
submode = subUpper
tmp = append(tmp, 28) //upper latch
continue
} else if isAlphaLower(ch) {
submode = subLower
tmp = append(tmp, 27) //lower latch
continue
} else {
if idx+1 < len(text) {
next := text[idx+1]
if isPunctuation(next) {
submode = subPunct
tmp = append(tmp, 25) //punctuation latch
continue
}
}
tmp = append(tmp, 29) //punctuation switch
tmp = append(tmp, punctMap[ch])
}
}
break
default: //subPunct
if isPunctuation(ch) {
tmp = append(tmp, punctMap[ch])
} else {
submode = subUpper
tmp = append(tmp, 29) //upper latch
continue
}
}
idx++
}
h := 0
result := []int{}
for i, val := range tmp {
if i%2 != 0 {
h = (h * 30) + val
result = append(result, h)
} else {
h = val
}
}
if len(tmp)%2 != 0 {
result = append(result, (h*30)+29)
}
return submode, result
}
func determineConsecutiveBinaryCount(msg []byte) int {
result := 0
for i, _ := range msg {
numericCount := determineConsecutiveDigitCount([]rune(string(msg[i:])))
if numericCount >= min_numeric_count {
break
}
textCount := determineConsecutiveTextCount([]rune(string(msg[i:])))
if textCount > 5 {
break
}
result++
}
return result
}
func encodeBinary(data []byte, startmode encodingMode) []int {
result := []int{}
count := len(data)
if count == 1 && startmode == encText {
result = append(result, shift_to_byte)
} else if (count % 6) == 0 {
result = append(result, latch_to_byte)
} else {
result = append(result, latch_to_byte_padded)
}
idx := 0
// Encode sixpacks
if count >= 6 {
words := make([]int, 5)
for (count - idx) >= 6 {
var t int64 = 0
for i := 0; i < 6; i++ {
t = t << 8
t += int64(data[idx+i])
}
for i := 0; i < 5; i++ {
words[4-i] = int(t % 900)
t = t / 900
}
result = append(result, words...)
idx += 6
}
}
//Encode rest (remaining n<5 bytes if any)
for i := idx; i < count; i++ {
result = append(result, int(data[i]&0xff))
}
return result
}
func highlevelEncode(dataStr string) ([]int, error) {
encodingMode := encText
textSubMode := subUpper
result := []int{}
data := []byte(dataStr)
for len(data) > 0 {
numericCount := determineConsecutiveDigitCount([]rune(string(data)))
if numericCount >= min_numeric_count || numericCount == len(data) {
result = append(result, latch_to_numeric)
encodingMode = encNumeric
textSubMode = subUpper
numData, err := encodeNumeric([]rune(string(data[:numericCount])))
if err != nil {
return nil, err
}
result = append(result, numData...)
data = data[numericCount:]
} else {
textCount := determineConsecutiveTextCount([]rune(string(data)))
if textCount >= 5 || textCount == len(data) {
if encodingMode != encText {
result = append(result, latch_to_text)
encodingMode = encText
textSubMode = subUpper
}
var txtData []int
textSubMode, txtData = encodeText([]rune(string(data[:textCount])), textSubMode)
result = append(result, txtData...)
data = data[textCount:]
} else {
binaryCount := determineConsecutiveBinaryCount(data)
if binaryCount == 0 {
binaryCount = 1
}
bytes := data[:binaryCount]
if len(bytes) != 1 || encodingMode != encText {
encodingMode = encBinary
textSubMode = subUpper
}
byteData := encodeBinary(bytes, encodingMode)
result = append(result, byteData...)
data = data[binaryCount:]
}
}
}
return result, nil
}

41
pdf417/highlevel_test.go Normal file
View File

@ -0,0 +1,41 @@
package pdf417
import "testing"
func compareIntSlice(t *testing.T, expected, actual []int, testStr string) {
if len(actual) != len(expected) {
t.Errorf("Invalid slice size. Expected %d got %d while encoding %q", len(expected), len(actual), testStr)
return
}
for i, a := range actual {
if e := expected[i]; e != a {
t.Errorf("Unexpected value at position %d. Expected %d got %d while encoding %q", i, e, a, testStr)
}
}
}
func TestHighlevelEncode(t *testing.T) {
runTest := func(msg string, expected ...int) {
if codes, err := highlevelEncode(msg); err != nil {
t.Error(err)
} else {
compareIntSlice(t, expected, codes, msg)
}
}
runTest("01234", 902, 112, 434)
runTest("Super !", 567, 615, 137, 809, 329)
runTest("Super ", 567, 615, 137, 809)
runTest("ABC123", 1, 88, 32, 119)
runTest("123ABC", 841, 63, 840, 32)
}
func TestBinaryEncoder(t *testing.T) {
runTest := func(msg string, expected ...int) {
codes := encodeBinary([]byte(msg), encText)
compareIntSlice(t, expected, codes, msg)
}
runTest("alcool", 924, 163, 238, 432, 766, 244)
runTest("alcoolique", 901, 163, 238, 432, 766, 244, 105, 113, 117, 101)
}

40
pdf417/pdfcode.go Normal file
View File

@ -0,0 +1,40 @@
package pdf417
import (
"image"
"image/color"
"github.com/boombuler/barcode"
"github.com/boombuler/barcode/utils"
)
type pdfBarcode struct {
data string
width int
code *utils.BitList
}
func (c *pdfBarcode) Metadata() barcode.Metadata {
return barcode.Metadata{barcode.TypePDF, 2}
}
func (c *pdfBarcode) Content() string {
return c.data
}
func (c *pdfBarcode) ColorModel() color.Model {
return color.Gray16Model
}
func (c *pdfBarcode) Bounds() image.Rectangle {
height := c.code.Len() / c.width
return image.Rect(0, 0, c.width, height*moduleHeight)
}
func (c *pdfBarcode) At(x, y int) color.Color {
if c.code.GetBit((y/moduleHeight)*c.width + x) {
return color.Black
}
return color.White
}

View File

@ -5,43 +5,25 @@ import (
)
type errorCorrection struct {
fld *utils.GaloisField
polynomes []*utils.GFPoly
rs *utils.ReedSolomonEncoder
}
var ec = newGF()
var ec = newErrorCorrection()
func newGF() *errorCorrection {
fld := utils.NewGaloisField(285)
return &errorCorrection{fld,
[]*utils.GFPoly{
utils.NewGFPoly(fld, []byte{1}),
},
}
}
func (ec *errorCorrection) getPolynomial(degree int) *utils.GFPoly {
if degree >= len(ec.polynomes) {
last := ec.polynomes[len(ec.polynomes)-1]
for d := len(ec.polynomes); d <= degree; d++ {
next := last.Multiply(utils.NewGFPoly(ec.fld, []byte{1, byte(ec.fld.ALogTbl[d-1])}))
ec.polynomes = append(ec.polynomes, next)
last = next
}
}
return ec.polynomes[degree]
func newErrorCorrection() *errorCorrection {
fld := utils.NewGaloisField(285, 256, 0)
return &errorCorrection{utils.NewReedSolomonEncoder(fld)}
}
func (ec *errorCorrection) calcECC(data []byte, eccCount byte) []byte {
generator := ec.getPolynomial(int(eccCount))
info := utils.NewGFPoly(ec.fld, data)
info = info.MultByMonominal(int(eccCount), 1)
_, remainder := info.Divide(generator)
result := make([]byte, eccCount)
numZero := int(eccCount) - len(remainder.Coefficients)
copy(result[numZero:], remainder.Coefficients)
dataInts := make([]int, len(data))
for i := 0; i < len(data); i++ {
dataInts[i] = int(data[i])
}
res := ec.rs.Encode(dataInts, int(eccCount))
result := make([]byte, len(res))
for i := 0; i < len(res); i++ {
result[i] = byte(res[i])
}
return result
}

View File

@ -5,19 +5,6 @@ import (
"testing"
)
func Test_LogTables(t *testing.T) {
for i := 1; i <= 255; i++ {
tmp := ec.fld.LogTbl[i]
if i != ec.fld.ALogTbl[tmp] {
t.Errorf("Invalid LogTables: %d", i)
}
}
if ec.fld.ALogTbl[11] != 232 || ec.fld.ALogTbl[87] != 127 || ec.fld.ALogTbl[225] != 36 {
t.Fail()
}
}
func Test_ErrorCorrection(t *testing.T) {
doTest := func(b []byte, ecc []byte) {
cnt := byte(len(ecc))

View File

@ -20,7 +20,7 @@ func (qr *qrcode) Content() string {
}
func (qr *qrcode) Metadata() barcode.Metadata {
return barcode.Metadata{"QR Code", 2}
return barcode.Metadata{barcode.TypeQR, 2}
}
func (qr *qrcode) ColorModel() color.Model {
@ -46,10 +46,6 @@ func (qr *qrcode) Set(x, y int, val bool) {
qr.data.SetBit(x*qr.dimension+y, val)
}
func (qr *qrcode) CheckSum() int {
return 0
}
func (qr *qrcode) calcPenalty() uint {
return qr.calcPenaltyRule1() + qr.calcPenaltyRule2() + qr.calcPenaltyRule3() + qr.calcPenaltyRule4()
}

View File

@ -16,6 +16,10 @@ type scaledBarcode struct {
rect image.Rectangle
}
type intCSscaledBC struct {
scaledBarcode
}
func (bc *scaledBarcode) Content() string {
return bc.wrapped.Content()
}
@ -36,8 +40,11 @@ func (bc *scaledBarcode) At(x, y int) color.Color {
return bc.wrapperFunc(x, y)
}
func (bc *scaledBarcode) CheckSum() int {
return bc.wrapped.CheckSum()
func (bc *intCSscaledBC) CheckSum() int {
if cs, ok := bc.wrapped.(BarcodeIntCS); ok {
return cs.CheckSum()
}
return 0
}
// Scale returns a resized barcode with the given width and height.
@ -52,6 +59,19 @@ func Scale(bc Barcode, width, height int) (Barcode, error) {
return nil, errors.New("unsupported barcode format")
}
func newScaledBC(wrapped Barcode, wrapperFunc wrapFunc, rect image.Rectangle) Barcode {
result := &scaledBarcode{
wrapped: wrapped,
wrapperFunc: wrapperFunc,
rect: rect,
}
if _, ok := wrapped.(BarcodeIntCS); ok {
return &intCSscaledBC{*result}
}
return result
}
func scale2DCode(bc Barcode, width, height int) (Barcode, error) {
orgBounds := bc.Bounds()
orgWidth := orgBounds.Max.X - orgBounds.Min.X
@ -59,7 +79,7 @@ func scale2DCode(bc Barcode, width, height int) (Barcode, error) {
factor := int(math.Min(float64(width)/float64(orgWidth), float64(height)/float64(orgHeight)))
if factor <= 0 {
return nil, fmt.Errorf("can not scale barcode to an image smaller then %dx%d", orgWidth, orgHeight)
return nil, fmt.Errorf("can not scale barcode to an image smaller than %dx%d", orgWidth, orgHeight)
}
offsetX := (width - (orgWidth * factor)) / 2
@ -77,11 +97,11 @@ func scale2DCode(bc Barcode, width, height int) (Barcode, error) {
return bc.At(x, y)
}
return &scaledBarcode{
return newScaledBC(
bc,
wrap,
image.Rect(0, 0, width, height),
}, nil
), nil
}
func scale1DCode(bc Barcode, width, height int) (Barcode, error) {
@ -90,7 +110,7 @@ func scale1DCode(bc Barcode, width, height int) (Barcode, error) {
factor := int(float64(width) / float64(orgWidth))
if factor <= 0 {
return nil, fmt.Errorf("can not scale barcode to an image smaller then %dx1", orgWidth)
return nil, fmt.Errorf("can not scale barcode to an image smaller than %dx1", orgWidth)
}
offsetX := (width - (orgWidth * factor)) / 2
@ -106,10 +126,9 @@ func scale1DCode(bc Barcode, width, height int) (Barcode, error) {
return bc.At(x, 0)
}
return &scaledBarcode{
return newScaledBC(
bc,
wrap,
image.Rect(0, 0, width, height),
}, nil
), nil
}

View File

@ -130,9 +130,9 @@ func Encode(content string, interleaved bool) (barcode.Barcode, error) {
resBits.AddBit(mode.end...)
kindTxt := ""
if interleaved {
kindTxt = " (interleaved)"
return utils.New1DCode(barcode.Type2of5Interleaved, content, resBits), nil
} else {
return utils.New1DCode(barcode.Type2of5, content, resBits), nil
}
return utils.New1DCode("2 of 5"+kindTxt, content, resBits, -1), nil
}

View File

@ -12,6 +12,10 @@ type base1DCode struct {
*BitList
kind string
content string
}
type base1DCodeIntCS struct {
base1DCode
checksum int
}
@ -38,11 +42,16 @@ func (c *base1DCode) At(x, y int) color.Color {
return color.White
}
func (c *base1DCode) CheckSum() int {
func (c *base1DCodeIntCS) CheckSum() int {
return c.checksum
}
// New1DCode creates a new 1D barcode where the bars are represented by the bits in the bars BitList
func New1DCode(codeKind, content string, bars *BitList, checksum int) barcode.Barcode {
return &base1DCode{bars, codeKind, content, checksum}
func New1DCodeIntCheckSum(codeKind, content string, bars *BitList, checksum int) barcode.BarcodeIntCS {
return &base1DCodeIntCS{base1DCode{bars, codeKind, content}, checksum}
}
// New1DCode creates a new 1D barcode where the bars are represented by the bits in the bars BitList
func New1DCode(codeKind, content string, bars *BitList) barcode.Barcode {
return &base1DCode{bars, codeKind, content}
}

View File

@ -76,7 +76,7 @@ func (bl *BitList) AddByte(b byte) {
// AddBits appends the last (LSB) 'count' bits of 'b' the the end of the list
func (bl *BitList) AddBits(b int, count byte) {
for i := int(count - 1); i >= 0; i-- {
for i := int(count) - 1; i >= 0; i-- {
bl.AddBit(((b >> uint(i)) & 1) == 1)
}
}

View File

@ -2,28 +2,31 @@ package utils
// GaloisField encapsulates galois field arithmetics
type GaloisField struct {
Size int
Base int
ALogTbl []int
LogTbl []int
}
// NewGaloisField creates a new falois field
func NewGaloisField(pp int) *GaloisField {
// NewGaloisField creates a new galois field
func NewGaloisField(pp, fieldSize, b int) *GaloisField {
result := new(GaloisField)
fldSize := 256
result.ALogTbl = make([]int, fldSize)
result.LogTbl = make([]int, fldSize)
result.Size = fieldSize
result.Base = b
result.ALogTbl = make([]int, fieldSize)
result.LogTbl = make([]int, fieldSize)
x := 1
for i := 0; i < fldSize; i++ {
for i := 0; i < fieldSize; i++ {
result.ALogTbl[i] = x
x = x * 2
if x >= fldSize {
x = (x ^ pp) & (fldSize - 1)
if x >= fieldSize {
x = (x ^ pp) & (fieldSize - 1)
}
}
for i := 0; i < fldSize; i++ {
for i := 0; i < fieldSize; i++ {
result.LogTbl[result.ALogTbl[i]] = int(i)
}
@ -31,7 +34,7 @@ func NewGaloisField(pp int) *GaloisField {
}
func (gf *GaloisField) Zero() *GFPoly {
return NewGFPoly(gf, []byte{0})
return NewGFPoly(gf, []int{0})
}
// AddOrSub add or substract two numbers
@ -44,7 +47,7 @@ func (gf *GaloisField) Multiply(a, b int) int {
if a == 0 || b == 0 {
return 0
}
return gf.ALogTbl[(gf.LogTbl[a]+gf.LogTbl[b])%255]
return gf.ALogTbl[(gf.LogTbl[a]+gf.LogTbl[b])%(gf.Size-1)]
}
// Divide divides two numbers
@ -54,9 +57,9 @@ func (gf *GaloisField) Divide(a, b int) int {
} else if a == 0 {
return 0
}
return gf.ALogTbl[(gf.LogTbl[a]-gf.LogTbl[b])%255]
return gf.ALogTbl[(gf.LogTbl[a]-gf.LogTbl[b])%(gf.Size-1)]
}
func (gf *GaloisField) Invers(num int) int {
return gf.ALogTbl[255-gf.LogTbl[num]]
return gf.ALogTbl[(gf.Size-1)-gf.LogTbl[num]]
}

View File

@ -43,7 +43,7 @@ func Test_GF(t *testing.T) {
3, 6, 12, 24, 48, 96, 192, 173, 119, 238, 241, 207, 179, 75, 150, 1,
}
gf := NewGaloisField(301)
gf := NewGaloisField(301, 256, 1)
if len(gf.LogTbl) != len(gf.ALogTbl) || len(gf.LogTbl) != len(log) {
t.Fail()
}

View File

@ -2,7 +2,7 @@ package utils
type GFPoly struct {
gf *GaloisField
Coefficients []byte
Coefficients []int
}
func (gp *GFPoly) Degree() int {
@ -14,7 +14,7 @@ func (gp *GFPoly) Zero() bool {
}
// GetCoefficient returns the coefficient of x ^ degree
func (gp *GFPoly) GetCoefficient(degree int) byte {
func (gp *GFPoly) GetCoefficient(degree int) int {
return gp.Coefficients[gp.Degree()-degree]
}
@ -29,23 +29,23 @@ func (gp *GFPoly) AddOrSubstract(other *GFPoly) *GFPoly {
if len(smallCoeff) > len(largeCoeff) {
largeCoeff, smallCoeff = smallCoeff, largeCoeff
}
sumDiff := make([]byte, len(largeCoeff))
sumDiff := make([]int, len(largeCoeff))
lenDiff := len(largeCoeff) - len(smallCoeff)
copy(sumDiff, largeCoeff[:lenDiff])
for i := lenDiff; i < len(largeCoeff); i++ {
sumDiff[i] = byte(gp.gf.AddOrSub(int(smallCoeff[i-lenDiff]), int(largeCoeff[i])))
sumDiff[i] = int(gp.gf.AddOrSub(int(smallCoeff[i-lenDiff]), int(largeCoeff[i])))
}
return NewGFPoly(gp.gf, sumDiff)
}
func (gp *GFPoly) MultByMonominal(degree int, coeff byte) *GFPoly {
func (gp *GFPoly) MultByMonominal(degree int, coeff int) *GFPoly {
if coeff == 0 {
return gp.gf.Zero()
}
size := len(gp.Coefficients)
result := make([]byte, size+degree)
result := make([]int, size+degree)
for i := 0; i < size; i++ {
result[i] = byte(gp.gf.Multiply(int(gp.Coefficients[i]), int(coeff)))
result[i] = int(gp.gf.Multiply(int(gp.Coefficients[i]), int(coeff)))
}
return NewGFPoly(gp.gf, result)
}
@ -58,12 +58,12 @@ func (gp *GFPoly) Multiply(other *GFPoly) *GFPoly {
aLen := len(aCoeff)
bCoeff := other.Coefficients
bLen := len(bCoeff)
product := make([]byte, aLen+bLen-1)
product := make([]int, aLen+bLen-1)
for i := 0; i < aLen; i++ {
ac := int(aCoeff[i])
for j := 0; j < bLen; j++ {
bc := int(bCoeff[j])
product[i+j] = byte(gp.gf.AddOrSub(int(product[i+j]), gp.gf.Multiply(ac, bc)))
product[i+j] = int(gp.gf.AddOrSub(int(product[i+j]), gp.gf.Multiply(ac, bc)))
}
}
return NewGFPoly(gp.gf, product)
@ -77,7 +77,7 @@ func (gp *GFPoly) Divide(other *GFPoly) (quotient *GFPoly, remainder *GFPoly) {
inversDenomLeadTerm := fld.Invers(int(denomLeadTerm))
for remainder.Degree() >= other.Degree() && !remainder.Zero() {
degreeDiff := remainder.Degree() - other.Degree()
scale := byte(fld.Multiply(int(remainder.GetCoefficient(remainder.Degree())), inversDenomLeadTerm))
scale := int(fld.Multiply(int(remainder.GetCoefficient(remainder.Degree())), inversDenomLeadTerm))
term := other.MultByMonominal(degreeDiff, scale)
itQuot := NewMonominalPoly(fld, degreeDiff, scale)
quotient = quotient.AddOrSubstract(itQuot)
@ -86,16 +86,16 @@ func (gp *GFPoly) Divide(other *GFPoly) (quotient *GFPoly, remainder *GFPoly) {
return
}
func NewMonominalPoly(field *GaloisField, degree int, coeff byte) *GFPoly {
func NewMonominalPoly(field *GaloisField, degree int, coeff int) *GFPoly {
if coeff == 0 {
return field.Zero()
}
result := make([]byte, degree+1)
result := make([]int, degree+1)
result[0] = coeff
return NewGFPoly(field, result)
}
func NewGFPoly(field *GaloisField, coefficients []byte) *GFPoly {
func NewGFPoly(field *GaloisField, coefficients []int) *GFPoly {
for len(coefficients) > 1 && coefficients[0] == 0 {
coefficients = coefficients[1:]
}

44
utils/reedsolomon.go Normal file
View File

@ -0,0 +1,44 @@
package utils
import (
"sync"
)
type ReedSolomonEncoder struct {
gf *GaloisField
polynomes []*GFPoly
m *sync.Mutex
}
func NewReedSolomonEncoder(gf *GaloisField) *ReedSolomonEncoder {
return &ReedSolomonEncoder{
gf, []*GFPoly{NewGFPoly(gf, []int{1})}, new(sync.Mutex),
}
}
func (rs *ReedSolomonEncoder) getPolynomial(degree int) *GFPoly {
rs.m.Lock()
defer rs.m.Unlock()
if degree >= len(rs.polynomes) {
last := rs.polynomes[len(rs.polynomes)-1]
for d := len(rs.polynomes); d <= degree; d++ {
next := last.Multiply(NewGFPoly(rs.gf, []int{1, rs.gf.ALogTbl[d-1+rs.gf.Base]}))
rs.polynomes = append(rs.polynomes, next)
last = next
}
}
return rs.polynomes[degree]
}
func (rs *ReedSolomonEncoder) Encode(data []int, eccCount int) []int {
generator := rs.getPolynomial(eccCount)
info := NewGFPoly(rs.gf, data)
info = info.MultByMonominal(eccCount, 1)
_, remainder := info.Divide(generator)
result := make([]int, eccCount)
numZero := int(eccCount) - len(remainder.Coefficients)
copy(result[numZero:], remainder.Coefficients)
return result
}