123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- // 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
- }
|