| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- package pdf417
- import "fmt"
- // Since each code word consists of 2 characters, a padding value is
- // needed when encoding a single character. 29 is used as padding because
- // it's a switch in all 4 submodes, and doesn't add any data.
- const padding_value = 29
- type subMode byte
- const (
- subUpper subMode = iota
- subLower
- subMixed
- subPunct
- )
- const (
- switchUpper = '\u00f1'
- switchUpperSingle = '\u00f2'
- switchLower = '\u00f3'
- switchMixed = '\u00f4'
- switchPunct = '\u00f5'
- switchPunctSingle = '\u00f6'
- )
- type textEncoder struct {
- Switching map[subMode]map[subMode][]rune
- SwitchSubmode map[rune]subMode
- ReverseLookup map[rune]map[subMode]int
- }
- func newTextEncoder() *textEncoder {
- encoder := new(textEncoder)
- characterTables := map[subMode][]rune{
- subUpper: []rune{
- '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', ' ',
- switchLower,
- switchMixed,
- switchPunctSingle,
- },
- subLower: []rune{
- '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', ' ',
- switchUpperSingle,
- switchMixed,
- switchPunctSingle,
- },
- subMixed: []rune{
- '0', '1', '2', '3', '4', '5', '6', '7', '8',
- '9', '&', '\r', '\t', ',', ':', '#', '-', '.',
- '$', '/', '+', '%', '*', '=', '^',
- switchPunct, ' ',
- switchLower,
- switchUpper,
- switchPunctSingle,
- },
- subPunct: []rune{
- ';', '<', '>', '@', '[', '\\', ']', '_', '`',
- '~', '!', '\r', '\t', ',', ':', '\n', '-', '.',
- '$', '/', 'g', '|', '*', '(', ')', '?', '{', '}', '\'',
- switchUpper,
- },
- }
- encoder.Switching = map[subMode]map[subMode][]rune{
- subUpper: map[subMode][]rune{
- subLower: []rune{switchLower},
- subMixed: []rune{switchMixed},
- subPunct: []rune{switchMixed, switchPunct},
- },
- subLower: map[subMode][]rune{
- subUpper: []rune{switchMixed, switchUpper},
- subMixed: []rune{switchMixed},
- subPunct: []rune{switchMixed, switchPunct},
- },
- subMixed: map[subMode][]rune{
- subUpper: []rune{switchUpper},
- subLower: []rune{switchLower},
- subPunct: []rune{switchPunct},
- },
- subPunct: map[subMode][]rune{
- subUpper: []rune{switchUpper},
- subLower: []rune{switchUpper, switchLower},
- subMixed: []rune{switchUpper, switchMixed},
- },
- }
- encoder.SwitchSubmode = map[rune]subMode{
- switchUpper: subUpper,
- switchLower: subLower,
- switchPunct: subPunct,
- switchMixed: subMixed,
- }
- encoder.ReverseLookup = make(map[rune]map[subMode]int)
- for submode, codes := range characterTables {
- for row, char := range codes {
- if encoder.ReverseLookup[char] == nil {
- encoder.ReverseLookup[char] = make(map[subMode]int)
- }
- encoder.ReverseLookup[char][submode] = int(row)
- }
- }
- return encoder
- }
- func (encoder *textEncoder) CanEncode(char rune) bool {
- switch char {
- case switchUpper,
- switchUpperSingle,
- switchLower,
- switchMixed,
- switchPunct,
- switchPunctSingle:
- return false
- default:
- return encoder.ReverseLookup[char] != nil
- }
- }
- func (*textEncoder) GetSwitchCode(data string) int {
- return switchCodeText
- }
- func (encoder *textEncoder) Encode(data string) ([]int, error) {
- interim, err := encoder.encodeInterim(data)
- if err != nil {
- return interim, err
- }
- return encoder.encodeFinal(interim), nil
- }
- func (encoder *textEncoder) encodeInterim(data string) ([]int, error) {
- submode := subUpper
- codes := []int{}
- var err error
- for _, char := range data {
- if !encoder.existsInSubmode(char, submode) {
- prevSubmode := submode
- submode, err = encoder.getSubmode(char)
- if err != nil {
- return codes, err
- }
- switchCodes := encoder.getSwitchCodes(prevSubmode, submode)
- codes = append(codes, switchCodes...)
- }
- codes = append(
- codes,
- encoder.getCharacterCode(char, submode),
- )
- }
- return codes, nil
- }
- func (encoder *textEncoder) getSubmode(char rune) (subMode, error) {
- if lookup, ok := encoder.ReverseLookup[char]; ok {
- for key := range lookup {
- return key, nil
- }
- }
- return subLower, fmt.Errorf("unable to find submode for %q", char)
- }
- func (encoder *textEncoder) getSwitchCodes(from, to subMode) []int {
- switches := encoder.Switching[from][to]
- codes := []int{}
- for _, switcher := range switches {
- codes = append(codes, encoder.getCharacterCode(switcher, from))
- from = encoder.SwitchSubmode[switcher]
- }
- return codes
- }
- func (*textEncoder) encodeFinal(codes []int) []int {
- codeWords := []int{}
- chunks := [][]int{}
- chunkPart := []int{}
- i := 0
- for _, code := range codes {
- chunkPart = append(chunkPart, code)
- i++
- if i%2 == 0 {
- chunks = append(chunks, chunkPart)
- chunkPart = []int{}
- }
- }
- if len(chunkPart) > 0 {
- chunks = append(chunks, chunkPart)
- }
- for _, chunk := range chunks {
- if len(chunk) == 1 {
- chunk = append(chunk, padding_value)
- }
- codeWords = append(
- codeWords,
- 30*chunk[0]+chunk[1],
- )
- }
- return codeWords
- }
- func (encoder *textEncoder) getCharacterCode(char rune, submode subMode) int {
- cw, ok := encoder.ReverseLookup[char][submode]
- if !ok {
- panic("This is not possible")
- }
- return cw
- }
- func (encoder *textEncoder) existsInSubmode(char rune, submode subMode) bool {
- _, ok := encoder.ReverseLookup[char][submode]
- return ok
- }
|