Преглед изворни кода

finished aztec code generator.

fixes #10
boombuler пре 9 година
родитељ
комит
d039b6bd02

+ 94 - 0
aztec/aztec_test.go

@@ -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")
+}

+ 66 - 0
aztec/azteccode.go

@@ -0,0 +1,66 @@
+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{"Aztec", 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) CheckSum() int {
+	return 0
+}
+
+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()
+}

+ 267 - 0
aztec/encoder.go

@@ -0,0 +1,267 @@
+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 - 0
aztec/encoder_test.go

@@ -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 - 0
aztec/errorcorrection.go

@@ -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
+	}
+}

+ 7 - 47
datamatrix/errorcorrection.go

@@ -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
-}
-
-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
-}
+	gf := utils.NewGaloisField(301, 256, 1)
 
-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++
 		}
 	}

+ 0 - 33
datamatrix/errorcorrection_test.go

@@ -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

+ 14 - 39
qr/errorcorrection.go

@@ -2,53 +2,28 @@ package qr
 
 import (
 	"github.com/boombuler/barcode/utils"
-	"sync"
 )
 
 type errorCorrection struct {
-	fld *utils.GaloisField
-
-	m         *sync.Mutex
-	polynomes []*utils.GFPoly
+	rs *utils.ReedSolomonEncoder
 }
 
-var ec = newGF()
-
-func newGF() *errorCorrection {
-	fld := utils.NewGaloisField(285)
+var ec = newErrorCorrection()
 
-	return &errorCorrection{fld,
-		new(sync.Mutex),
-		[]*utils.GFPoly{
-			utils.NewGFPoly(fld, []byte{1}),
-		},
-	}
-}
-
-func (ec *errorCorrection) getPolynomial(degree int) *utils.GFPoly {
-	ec.m.Lock()
-	defer ec.m.Unlock()
-
-	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
 }

+ 0 - 13
qr/errorcorrection_test.go

@@ -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))

+ 1 - 1
utils/bitlist.go

@@ -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)
 	}
 }

+ 16 - 13
utils/galoisfield.go

@@ -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]]
 }

+ 1 - 1
utils/galoisfield_test.go

@@ -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()
 	}

+ 13 - 13
utils/gfpoly.go

@@ -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 - 0
utils/reedsolomon.go

@@ -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
+}