git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/barcode/qr/versioninfo.go (about)

     1  package qr
     2  
     3  import "math"
     4  
     5  // ErrorCorrectionLevel indicates the amount of "backup data" stored in the QR code
     6  type ErrorCorrectionLevel byte
     7  
     8  const (
     9  	// L recovers 7% of data
    10  	L ErrorCorrectionLevel = iota
    11  	// M recovers 15% of data
    12  	M
    13  	// Q recovers 25% of data
    14  	Q
    15  	// H recovers 30% of data
    16  	H
    17  )
    18  
    19  func (ecl ErrorCorrectionLevel) String() string {
    20  	switch ecl {
    21  	case L:
    22  		return "L"
    23  	case M:
    24  		return "M"
    25  	case Q:
    26  		return "Q"
    27  	case H:
    28  		return "H"
    29  	}
    30  	return "unknown"
    31  }
    32  
    33  type encodingMode byte
    34  
    35  const (
    36  	numericMode      encodingMode = 1
    37  	alphaNumericMode encodingMode = 2
    38  	byteMode         encodingMode = 4
    39  	kanjiMode        encodingMode = 8
    40  )
    41  
    42  type versionInfo struct {
    43  	Version                          byte
    44  	Level                            ErrorCorrectionLevel
    45  	ErrorCorrectionCodewordsPerBlock byte
    46  	NumberOfBlocksInGroup1           byte
    47  	DataCodeWordsPerBlockInGroup1    byte
    48  	NumberOfBlocksInGroup2           byte
    49  	DataCodeWordsPerBlockInGroup2    byte
    50  }
    51  
    52  var versionInfos = []*versionInfo{
    53  	&versionInfo{1, L, 7, 1, 19, 0, 0},
    54  	&versionInfo{1, M, 10, 1, 16, 0, 0},
    55  	&versionInfo{1, Q, 13, 1, 13, 0, 0},
    56  	&versionInfo{1, H, 17, 1, 9, 0, 0},
    57  	&versionInfo{2, L, 10, 1, 34, 0, 0},
    58  	&versionInfo{2, M, 16, 1, 28, 0, 0},
    59  	&versionInfo{2, Q, 22, 1, 22, 0, 0},
    60  	&versionInfo{2, H, 28, 1, 16, 0, 0},
    61  	&versionInfo{3, L, 15, 1, 55, 0, 0},
    62  	&versionInfo{3, M, 26, 1, 44, 0, 0},
    63  	&versionInfo{3, Q, 18, 2, 17, 0, 0},
    64  	&versionInfo{3, H, 22, 2, 13, 0, 0},
    65  	&versionInfo{4, L, 20, 1, 80, 0, 0},
    66  	&versionInfo{4, M, 18, 2, 32, 0, 0},
    67  	&versionInfo{4, Q, 26, 2, 24, 0, 0},
    68  	&versionInfo{4, H, 16, 4, 9, 0, 0},
    69  	&versionInfo{5, L, 26, 1, 108, 0, 0},
    70  	&versionInfo{5, M, 24, 2, 43, 0, 0},
    71  	&versionInfo{5, Q, 18, 2, 15, 2, 16},
    72  	&versionInfo{5, H, 22, 2, 11, 2, 12},
    73  	&versionInfo{6, L, 18, 2, 68, 0, 0},
    74  	&versionInfo{6, M, 16, 4, 27, 0, 0},
    75  	&versionInfo{6, Q, 24, 4, 19, 0, 0},
    76  	&versionInfo{6, H, 28, 4, 15, 0, 0},
    77  	&versionInfo{7, L, 20, 2, 78, 0, 0},
    78  	&versionInfo{7, M, 18, 4, 31, 0, 0},
    79  	&versionInfo{7, Q, 18, 2, 14, 4, 15},
    80  	&versionInfo{7, H, 26, 4, 13, 1, 14},
    81  	&versionInfo{8, L, 24, 2, 97, 0, 0},
    82  	&versionInfo{8, M, 22, 2, 38, 2, 39},
    83  	&versionInfo{8, Q, 22, 4, 18, 2, 19},
    84  	&versionInfo{8, H, 26, 4, 14, 2, 15},
    85  	&versionInfo{9, L, 30, 2, 116, 0, 0},
    86  	&versionInfo{9, M, 22, 3, 36, 2, 37},
    87  	&versionInfo{9, Q, 20, 4, 16, 4, 17},
    88  	&versionInfo{9, H, 24, 4, 12, 4, 13},
    89  	&versionInfo{10, L, 18, 2, 68, 2, 69},
    90  	&versionInfo{10, M, 26, 4, 43, 1, 44},
    91  	&versionInfo{10, Q, 24, 6, 19, 2, 20},
    92  	&versionInfo{10, H, 28, 6, 15, 2, 16},
    93  	&versionInfo{11, L, 20, 4, 81, 0, 0},
    94  	&versionInfo{11, M, 30, 1, 50, 4, 51},
    95  	&versionInfo{11, Q, 28, 4, 22, 4, 23},
    96  	&versionInfo{11, H, 24, 3, 12, 8, 13},
    97  	&versionInfo{12, L, 24, 2, 92, 2, 93},
    98  	&versionInfo{12, M, 22, 6, 36, 2, 37},
    99  	&versionInfo{12, Q, 26, 4, 20, 6, 21},
   100  	&versionInfo{12, H, 28, 7, 14, 4, 15},
   101  	&versionInfo{13, L, 26, 4, 107, 0, 0},
   102  	&versionInfo{13, M, 22, 8, 37, 1, 38},
   103  	&versionInfo{13, Q, 24, 8, 20, 4, 21},
   104  	&versionInfo{13, H, 22, 12, 11, 4, 12},
   105  	&versionInfo{14, L, 30, 3, 115, 1, 116},
   106  	&versionInfo{14, M, 24, 4, 40, 5, 41},
   107  	&versionInfo{14, Q, 20, 11, 16, 5, 17},
   108  	&versionInfo{14, H, 24, 11, 12, 5, 13},
   109  	&versionInfo{15, L, 22, 5, 87, 1, 88},
   110  	&versionInfo{15, M, 24, 5, 41, 5, 42},
   111  	&versionInfo{15, Q, 30, 5, 24, 7, 25},
   112  	&versionInfo{15, H, 24, 11, 12, 7, 13},
   113  	&versionInfo{16, L, 24, 5, 98, 1, 99},
   114  	&versionInfo{16, M, 28, 7, 45, 3, 46},
   115  	&versionInfo{16, Q, 24, 15, 19, 2, 20},
   116  	&versionInfo{16, H, 30, 3, 15, 13, 16},
   117  	&versionInfo{17, L, 28, 1, 107, 5, 108},
   118  	&versionInfo{17, M, 28, 10, 46, 1, 47},
   119  	&versionInfo{17, Q, 28, 1, 22, 15, 23},
   120  	&versionInfo{17, H, 28, 2, 14, 17, 15},
   121  	&versionInfo{18, L, 30, 5, 120, 1, 121},
   122  	&versionInfo{18, M, 26, 9, 43, 4, 44},
   123  	&versionInfo{18, Q, 28, 17, 22, 1, 23},
   124  	&versionInfo{18, H, 28, 2, 14, 19, 15},
   125  	&versionInfo{19, L, 28, 3, 113, 4, 114},
   126  	&versionInfo{19, M, 26, 3, 44, 11, 45},
   127  	&versionInfo{19, Q, 26, 17, 21, 4, 22},
   128  	&versionInfo{19, H, 26, 9, 13, 16, 14},
   129  	&versionInfo{20, L, 28, 3, 107, 5, 108},
   130  	&versionInfo{20, M, 26, 3, 41, 13, 42},
   131  	&versionInfo{20, Q, 30, 15, 24, 5, 25},
   132  	&versionInfo{20, H, 28, 15, 15, 10, 16},
   133  	&versionInfo{21, L, 28, 4, 116, 4, 117},
   134  	&versionInfo{21, M, 26, 17, 42, 0, 0},
   135  	&versionInfo{21, Q, 28, 17, 22, 6, 23},
   136  	&versionInfo{21, H, 30, 19, 16, 6, 17},
   137  	&versionInfo{22, L, 28, 2, 111, 7, 112},
   138  	&versionInfo{22, M, 28, 17, 46, 0, 0},
   139  	&versionInfo{22, Q, 30, 7, 24, 16, 25},
   140  	&versionInfo{22, H, 24, 34, 13, 0, 0},
   141  	&versionInfo{23, L, 30, 4, 121, 5, 122},
   142  	&versionInfo{23, M, 28, 4, 47, 14, 48},
   143  	&versionInfo{23, Q, 30, 11, 24, 14, 25},
   144  	&versionInfo{23, H, 30, 16, 15, 14, 16},
   145  	&versionInfo{24, L, 30, 6, 117, 4, 118},
   146  	&versionInfo{24, M, 28, 6, 45, 14, 46},
   147  	&versionInfo{24, Q, 30, 11, 24, 16, 25},
   148  	&versionInfo{24, H, 30, 30, 16, 2, 17},
   149  	&versionInfo{25, L, 26, 8, 106, 4, 107},
   150  	&versionInfo{25, M, 28, 8, 47, 13, 48},
   151  	&versionInfo{25, Q, 30, 7, 24, 22, 25},
   152  	&versionInfo{25, H, 30, 22, 15, 13, 16},
   153  	&versionInfo{26, L, 28, 10, 114, 2, 115},
   154  	&versionInfo{26, M, 28, 19, 46, 4, 47},
   155  	&versionInfo{26, Q, 28, 28, 22, 6, 23},
   156  	&versionInfo{26, H, 30, 33, 16, 4, 17},
   157  	&versionInfo{27, L, 30, 8, 122, 4, 123},
   158  	&versionInfo{27, M, 28, 22, 45, 3, 46},
   159  	&versionInfo{27, Q, 30, 8, 23, 26, 24},
   160  	&versionInfo{27, H, 30, 12, 15, 28, 16},
   161  	&versionInfo{28, L, 30, 3, 117, 10, 118},
   162  	&versionInfo{28, M, 28, 3, 45, 23, 46},
   163  	&versionInfo{28, Q, 30, 4, 24, 31, 25},
   164  	&versionInfo{28, H, 30, 11, 15, 31, 16},
   165  	&versionInfo{29, L, 30, 7, 116, 7, 117},
   166  	&versionInfo{29, M, 28, 21, 45, 7, 46},
   167  	&versionInfo{29, Q, 30, 1, 23, 37, 24},
   168  	&versionInfo{29, H, 30, 19, 15, 26, 16},
   169  	&versionInfo{30, L, 30, 5, 115, 10, 116},
   170  	&versionInfo{30, M, 28, 19, 47, 10, 48},
   171  	&versionInfo{30, Q, 30, 15, 24, 25, 25},
   172  	&versionInfo{30, H, 30, 23, 15, 25, 16},
   173  	&versionInfo{31, L, 30, 13, 115, 3, 116},
   174  	&versionInfo{31, M, 28, 2, 46, 29, 47},
   175  	&versionInfo{31, Q, 30, 42, 24, 1, 25},
   176  	&versionInfo{31, H, 30, 23, 15, 28, 16},
   177  	&versionInfo{32, L, 30, 17, 115, 0, 0},
   178  	&versionInfo{32, M, 28, 10, 46, 23, 47},
   179  	&versionInfo{32, Q, 30, 10, 24, 35, 25},
   180  	&versionInfo{32, H, 30, 19, 15, 35, 16},
   181  	&versionInfo{33, L, 30, 17, 115, 1, 116},
   182  	&versionInfo{33, M, 28, 14, 46, 21, 47},
   183  	&versionInfo{33, Q, 30, 29, 24, 19, 25},
   184  	&versionInfo{33, H, 30, 11, 15, 46, 16},
   185  	&versionInfo{34, L, 30, 13, 115, 6, 116},
   186  	&versionInfo{34, M, 28, 14, 46, 23, 47},
   187  	&versionInfo{34, Q, 30, 44, 24, 7, 25},
   188  	&versionInfo{34, H, 30, 59, 16, 1, 17},
   189  	&versionInfo{35, L, 30, 12, 121, 7, 122},
   190  	&versionInfo{35, M, 28, 12, 47, 26, 48},
   191  	&versionInfo{35, Q, 30, 39, 24, 14, 25},
   192  	&versionInfo{35, H, 30, 22, 15, 41, 16},
   193  	&versionInfo{36, L, 30, 6, 121, 14, 122},
   194  	&versionInfo{36, M, 28, 6, 47, 34, 48},
   195  	&versionInfo{36, Q, 30, 46, 24, 10, 25},
   196  	&versionInfo{36, H, 30, 2, 15, 64, 16},
   197  	&versionInfo{37, L, 30, 17, 122, 4, 123},
   198  	&versionInfo{37, M, 28, 29, 46, 14, 47},
   199  	&versionInfo{37, Q, 30, 49, 24, 10, 25},
   200  	&versionInfo{37, H, 30, 24, 15, 46, 16},
   201  	&versionInfo{38, L, 30, 4, 122, 18, 123},
   202  	&versionInfo{38, M, 28, 13, 46, 32, 47},
   203  	&versionInfo{38, Q, 30, 48, 24, 14, 25},
   204  	&versionInfo{38, H, 30, 42, 15, 32, 16},
   205  	&versionInfo{39, L, 30, 20, 117, 4, 118},
   206  	&versionInfo{39, M, 28, 40, 47, 7, 48},
   207  	&versionInfo{39, Q, 30, 43, 24, 22, 25},
   208  	&versionInfo{39, H, 30, 10, 15, 67, 16},
   209  	&versionInfo{40, L, 30, 19, 118, 6, 119},
   210  	&versionInfo{40, M, 28, 18, 47, 31, 48},
   211  	&versionInfo{40, Q, 30, 34, 24, 34, 25},
   212  	&versionInfo{40, H, 30, 20, 15, 61, 16},
   213  }
   214  
   215  func (vi *versionInfo) totalDataBytes() int {
   216  	g1Data := int(vi.NumberOfBlocksInGroup1) * int(vi.DataCodeWordsPerBlockInGroup1)
   217  	g2Data := int(vi.NumberOfBlocksInGroup2) * int(vi.DataCodeWordsPerBlockInGroup2)
   218  	return (g1Data + g2Data)
   219  }
   220  
   221  func (vi *versionInfo) charCountBits(m encodingMode) byte {
   222  	switch m {
   223  	case numericMode:
   224  		if vi.Version < 10 {
   225  			return 10
   226  		} else if vi.Version < 27 {
   227  			return 12
   228  		}
   229  		return 14
   230  
   231  	case alphaNumericMode:
   232  		if vi.Version < 10 {
   233  			return 9
   234  		} else if vi.Version < 27 {
   235  			return 11
   236  		}
   237  		return 13
   238  
   239  	case byteMode:
   240  		if vi.Version < 10 {
   241  			return 8
   242  		}
   243  		return 16
   244  
   245  	case kanjiMode:
   246  		if vi.Version < 10 {
   247  			return 8
   248  		} else if vi.Version < 27 {
   249  			return 10
   250  		}
   251  		return 12
   252  	default:
   253  		return 0
   254  	}
   255  }
   256  
   257  func (vi *versionInfo) modulWidth() int {
   258  	return ((int(vi.Version) - 1) * 4) + 21
   259  }
   260  
   261  func (vi *versionInfo) alignmentPatternPlacements() []int {
   262  	if vi.Version == 1 {
   263  		return make([]int, 0)
   264  	}
   265  
   266  	first := 6
   267  	last := vi.modulWidth() - 7
   268  	space := float64(last - first)
   269  	count := int(math.Ceil(space/28)) + 1
   270  
   271  	result := make([]int, count)
   272  	result[0] = first
   273  	result[len(result)-1] = last
   274  	if count > 2 {
   275  		step := int(math.Ceil(float64(last-first) / float64(count-1)))
   276  		if step%2 == 1 {
   277  			frac := float64(last-first) / float64(count-1)
   278  			_, x := math.Modf(frac)
   279  			if x >= 0.5 {
   280  				frac = math.Ceil(frac)
   281  			} else {
   282  				frac = math.Floor(frac)
   283  			}
   284  
   285  			if int(frac)%2 == 0 {
   286  				step--
   287  			} else {
   288  				step++
   289  			}
   290  		}
   291  
   292  		for i := 1; i <= count-2; i++ {
   293  			result[i] = last - (step * (count - 1 - i))
   294  		}
   295  	}
   296  
   297  	return result
   298  }
   299  
   300  func findSmallestVersionInfo(ecl ErrorCorrectionLevel, mode encodingMode, dataBits int) *versionInfo {
   301  	dataBits = dataBits + 4 // mode indicator
   302  	for _, vi := range versionInfos {
   303  		if vi.Level == ecl {
   304  			if (vi.totalDataBytes() * 8) >= (dataBits + int(vi.charCountBits(mode))) {
   305  				return vi
   306  			}
   307  		}
   308  	}
   309  	return nil
   310  }