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