go-tinyqr

An experimental minimal QR Code generator  http://go-qrcode.appspot.com
git clone https://git.ce9e.org/go-tinyqr.git

commit
da1b6568686e89143e94f980a98bc2dbd5537f13
parent
7ac0b40b20381309a948070efde6bf158f0b75be
Author
Tom <tfh@skip.org>
Date
2020-06-17 19:49
Encode mixed numeric/alphanumeric data segments as one alphanumeric data segment if that's more efficient. Also add error check to NewWithForcedVersion(): Check if the content is too large for the requested QR code version.

Diffstat

M encoder.go 20 ++++++++++++++++----
M encoder_test.go 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
M qrcode.go 7 +++++++

3 files changed, 74 insertions, 4 deletions


diff --git a/encoder.go b/encoder.go

@@ -172,7 +172,7 @@ func (d *dataEncoder) encode(data []byte) (*bitset.Bitset, error) {
  172   172 	}
  173   173 
  174   174 	// Classify data into unoptimised segments.
  175    -1 	d.classifyDataModes()
   -1   175 	highestRequiredMode := d.classifyDataModes()
  176   176 
  177   177 	// Optimise segments.
  178   178 	err := d.optimiseDataModes()
@@ -190,13 +190,13 @@ func (d *dataEncoder) encode(data []byte) (*bitset.Bitset, error) {
  190   190 		optimizedLength += length
  191   191 	}
  192   192 
  193    -1 	singleByteSegmentLength, err := d.encodedLength(dataModeByte, len(d.data))
   -1   193 	singleByteSegmentLength, err := d.encodedLength(highestRequiredMode, len(d.data))
  194   194 	if err != nil {
  195   195 		return nil, err
  196   196 	}
  197   197 
  198   198 	if singleByteSegmentLength <= optimizedLength {
  199    -1 		d.optimised = []segment{segment{dataMode: dataModeByte, data: d.data}}
   -1   199 		d.optimised = []segment{segment{dataMode: highestRequiredMode, data: d.data}}
  200   200 	}
  201   201 
  202   202 	// Encode data.
@@ -211,9 +211,15 @@ func (d *dataEncoder) encode(data []byte) (*bitset.Bitset, error) {
  211   211 // classifyDataModes classifies the raw data into unoptimised segments.
  212   212 // e.g. "123ZZ#!#!" =>
  213   213 // [numeric, 3, "123"] [alphanumeric, 2, "ZZ"] [byte, 4, "#!#!"].
  214    -1 func (d *dataEncoder) classifyDataModes() {
   -1   214 //
   -1   215 // Returns the highest data mode needed to encode the data. e.g. for a mixed
   -1   216 // numeric/alphanumeric input, the highest is alphanumeric.
   -1   217 //
   -1   218 // dataModeNone < dataModeNumeric < dataModeAlphanumeric < dataModeByte
   -1   219 func (d *dataEncoder) classifyDataModes() dataMode {
  215   220 	var start int
  216   221 	mode := dataModeNone
   -1   222 	highestRequiredMode := mode
  217   223 
  218   224 	for i, v := range d.data {
  219   225 		newMode := dataModeNone
@@ -236,9 +242,15 @@ func (d *dataEncoder) classifyDataModes() {
  236   242 
  237   243 			mode = newMode
  238   244 		}
   -1   245 
   -1   246 		if newMode > highestRequiredMode {
   -1   247 			highestRequiredMode = newMode
   -1   248 		}
  239   249 	}
  240   250 
  241   251 	d.actual = append(d.actual, segment{dataMode: mode, data: d.data[start:len(d.data)]})
   -1   252 
   -1   253 	return highestRequiredMode
  242   254 }
  243   255 
  244   256 // optimiseDataModes optimises the list of segments to reduce the overall output

diff --git a/encoder_test.go b/encoder_test.go

@@ -267,6 +267,57 @@ func TestOptimiseEncoding(t *testing.T) {
  267   267 				{dataModeByte, 8},
  268   268 			},
  269   269 		},
   -1   270 		// HTTPS://ABC.DE/Q/393AABB6998877XYZ0518AUQCRVJN25
   -1   271 		// AAAAAAAAAAAAAAAAANNNAAAANNNNNNNAAANNNNAAAAAAAANN
   -1   272 		// different to below---------^--------------------
   -1   273 		{
   -1   274 			dataEncoderType1To9,
   -1   275 			[]testModeSegment{
   -1   276 				{dataModeAlphanumeric, 17},
   -1   277 				{dataModeNumeric, 3},
   -1   278 				{dataModeAlphanumeric, 4},
   -1   279 				{dataModeNumeric, 7},
   -1   280 				{dataModeAlphanumeric, 3},
   -1   281 				{dataModeNumeric, 4},
   -1   282 				{dataModeAlphanumeric, 8},
   -1   283 				{dataModeNumeric, 2},
   -1   284 			},
   -1   285 			[]testModeSegment{
   -1   286 				{dataModeAlphanumeric, 48},
   -1   287 			},
   -1   288 		},
   -1   289 		// HTTPS://ABC.DE/Q/393AABB699E877XYZ0518AUQCRVJN25
   -1   290 		// AAAAAAAAAAAAAAAAANNNAAAANNNANNNAAANNNNAAAAAAAANN
   -1   291 		// different to above---------^--------------------
   -1   292 		{
   -1   293 			dataEncoderType1To9,
   -1   294 			[]testModeSegment{
   -1   295 				{dataModeAlphanumeric, 17},
   -1   296 				{dataModeNumeric, 3},
   -1   297 				{dataModeAlphanumeric, 4},
   -1   298 				{dataModeNumeric, 3},
   -1   299 				{dataModeAlphanumeric, 1},
   -1   300 				{dataModeNumeric, 3},
   -1   301 				{dataModeAlphanumeric, 3},
   -1   302 				{dataModeNumeric, 4},
   -1   303 				{dataModeAlphanumeric, 8},
   -1   304 				{dataModeNumeric, 2},
   -1   305 			},
   -1   306 			[]testModeSegment{
   -1   307 				{dataModeAlphanumeric, 48},
   -1   308 			},
   -1   309 		},
   -1   310 		// 0123456789
   -1   311 		// NNNNNNNNNN
   -1   312 		{
   -1   313 			dataEncoderType1To9,
   -1   314 			[]testModeSegment{
   -1   315 				{dataModeNumeric, 10},
   -1   316 			},
   -1   317 			[]testModeSegment{
   -1   318 				{dataModeNumeric, 10},
   -1   319 			},
   -1   320 		},
  270   321 	}
  271   322 
  272   323 	for _, test := range tests {

diff --git a/qrcode.go b/qrcode.go

@@ -233,6 +233,13 @@ func NewWithForcedVersion(content string, version int, level RecoveryLevel) (*QR
  233   233 		return nil, errors.New("cannot find QR Code version")
  234   234 	}
  235   235 
   -1   236 	if encoded.Len() > chosenVersion.numDataBits() {
   -1   237 		return nil, fmt.Errorf("Cannot encode QR code: content too large for fixed size QR Code version %d (encoded length is %d bits, maximum length is %d bits)",
   -1   238 			version,
   -1   239 			encoded.Len(),
   -1   240 			chosenVersion.numDataBits())
   -1   241 	}
   -1   242 
  236   243 	q := &QRCode{
  237   244 		Content: content,
  238   245