hanayo/vendor/github.com/boombuler/barcode/pdf417/highlevel.go
2019-02-23 13:29:15 +00:00

355 lines
7.3 KiB
Go

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
}