UNPKG

@zxing/library

Version:

TypeScript port of ZXing multi-format 1D/2D barcode image processing library.

255 lines (254 loc) 11.5 kB
/* * Copyright 2013 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var __values = (this && this.__values) || function(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); }; // import java.util.Collection; // import java.util.Collections; import Collections from '../../util/Collections'; // import java.util.Comparator; // import java.util.Iterator; // import java.util.LinkedList; import State from './State'; import * as C from './EncoderConstants'; import * as CharMap from './CharMap'; import * as ShiftTable from './ShiftTable'; import StringUtils from '../../common/StringUtils'; /** * This produces nearly optimal encodings of text into the first-level of * encoding used by Aztec code. * * It uses a dynamic algorithm. For each prefix of the string, it determines * a set of encodings that could lead to this prefix. We repeatedly add a * character and generate a new set of optimal encodings until we have read * through the entire input. * * @author Frank Yellin * @author Rustam Abdullaev */ var HighLevelEncoder = /** @class */ (function () { function HighLevelEncoder(text) { this.text = text; } /** * @return text represented by this encoder encoded as a {@link BitArray} */ HighLevelEncoder.prototype.encode = function () { var spaceCharCode = StringUtils.getCharCode(' '); var lineBreakCharCode = StringUtils.getCharCode('\n'); var states = Collections.singletonList(State.INITIAL_STATE); for (var index = 0; index < this.text.length; index++) { var pairCode = void 0; var nextChar = index + 1 < this.text.length ? this.text[index + 1] : 0; switch (this.text[index]) { case StringUtils.getCharCode('\r'): pairCode = nextChar === lineBreakCharCode ? 2 : 0; break; case StringUtils.getCharCode('.'): pairCode = nextChar === spaceCharCode ? 3 : 0; break; case StringUtils.getCharCode(','): pairCode = nextChar === spaceCharCode ? 4 : 0; break; case StringUtils.getCharCode(':'): pairCode = nextChar === spaceCharCode ? 5 : 0; break; default: pairCode = 0; } if (pairCode > 0) { // We have one of the four special PUNCT pairs. Treat them specially. // Get a new set of states for the two new characters. states = HighLevelEncoder.updateStateListForPair(states, index, pairCode); index++; } else { // Get a new set of states for the new character. states = this.updateStateListForChar(states, index); } } // We are left with a set of states. Find the shortest one. var minState = Collections.min(states, function (a, b) { return a.getBitCount() - b.getBitCount(); }); // Convert it to a bit array, and return. return minState.toBitArray(this.text); }; // We update a set of states for a new character by updating each state // for the new character, merging the results, and then removing the // non-optimal states. HighLevelEncoder.prototype.updateStateListForChar = function (states, index) { var e_1, _a; var result = []; try { for (var states_1 = __values(states), states_1_1 = states_1.next(); !states_1_1.done; states_1_1 = states_1.next()) { var state = states_1_1.value /*State*/; this.updateStateForChar(state, index, result); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (states_1_1 && !states_1_1.done && (_a = states_1.return)) _a.call(states_1); } finally { if (e_1) throw e_1.error; } } return HighLevelEncoder.simplifyStates(result); }; // Return a set of states that represent the possible ways of updating this // state for the next character. The resulting set of states are added to // the "result" list. HighLevelEncoder.prototype.updateStateForChar = function (state, index, result) { var ch = (this.text[index] & 0xff); var charInCurrentTable = CharMap.CHAR_MAP[state.getMode()][ch] > 0; var stateNoBinary = null; for (var mode /*int*/ = 0; mode <= C.MODE_PUNCT; mode++) { var charInMode = CharMap.CHAR_MAP[mode][ch]; if (charInMode > 0) { if (stateNoBinary == null) { // Only create stateNoBinary the first time it's required. stateNoBinary = state.endBinaryShift(index); } // Try generating the character by latching to its mode if (!charInCurrentTable || mode === state.getMode() || mode === C.MODE_DIGIT) { // If the character is in the current table, we don't want to latch to // any other mode except possibly digit (which uses only 4 bits). Any // other latch would be equally successful *after* this character, and // so wouldn't save any bits. var latchState = stateNoBinary.latchAndAppend(mode, charInMode); result.push(latchState); } // Try generating the character by switching to its mode. if (!charInCurrentTable && ShiftTable.SHIFT_TABLE[state.getMode()][mode] >= 0) { // It never makes sense to temporarily shift to another mode if the // character exists in the current mode. That can never save bits. var shiftState = stateNoBinary.shiftAndAppend(mode, charInMode); result.push(shiftState); } } } if (state.getBinaryShiftByteCount() > 0 || CharMap.CHAR_MAP[state.getMode()][ch] === 0) { // It's never worthwhile to go into binary shift mode if you're not already // in binary shift mode, and the character exists in your current mode. // That can never save bits over just outputting the char in the current mode. var binaryState = state.addBinaryShiftChar(index); result.push(binaryState); } }; HighLevelEncoder.updateStateListForPair = function (states, index, pairCode) { var e_2, _a; var result = []; try { for (var states_2 = __values(states), states_2_1 = states_2.next(); !states_2_1.done; states_2_1 = states_2.next()) { var state = states_2_1.value /*State*/; this.updateStateForPair(state, index, pairCode, result); } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (states_2_1 && !states_2_1.done && (_a = states_2.return)) _a.call(states_2); } finally { if (e_2) throw e_2.error; } } return this.simplifyStates(result); }; HighLevelEncoder.updateStateForPair = function (state, index, pairCode, result) { var stateNoBinary = state.endBinaryShift(index); // Possibility 1. Latch to C.MODE_PUNCT, and then append this code result.push(stateNoBinary.latchAndAppend(C.MODE_PUNCT, pairCode)); if (state.getMode() !== C.MODE_PUNCT) { // Possibility 2. Shift to C.MODE_PUNCT, and then append this code. // Every state except C.MODE_PUNCT (handled above) can shift result.push(stateNoBinary.shiftAndAppend(C.MODE_PUNCT, pairCode)); } if (pairCode === 3 || pairCode === 4) { // both characters are in DIGITS. Sometimes better to just add two digits var digitState = stateNoBinary .latchAndAppend(C.MODE_DIGIT, 16 - pairCode) // period or comma in DIGIT .latchAndAppend(C.MODE_DIGIT, 1); // space in DIGIT result.push(digitState); } if (state.getBinaryShiftByteCount() > 0) { // It only makes sense to do the characters as binary if we're already // in binary mode. var binaryState = state .addBinaryShiftChar(index) .addBinaryShiftChar(index + 1); result.push(binaryState); } }; HighLevelEncoder.simplifyStates = function (states) { var e_3, _a, e_4, _b; var result = []; try { for (var states_3 = __values(states), states_3_1 = states_3.next(); !states_3_1.done; states_3_1 = states_3.next()) { var newState = states_3_1.value; var add = true; var _loop_1 = function (oldState) { if (oldState.isBetterThanOrEqualTo(newState)) { add = false; return "break"; } if (newState.isBetterThanOrEqualTo(oldState)) { // iterator.remove(); result = result.filter(function (x) { return x !== oldState; }); // remove old state } }; try { for (var result_1 = (e_4 = void 0, __values(result)), result_1_1 = result_1.next(); !result_1_1.done; result_1_1 = result_1.next()) { var oldState = result_1_1.value; var state_1 = _loop_1(oldState); if (state_1 === "break") break; } } catch (e_4_1) { e_4 = { error: e_4_1 }; } finally { try { if (result_1_1 && !result_1_1.done && (_b = result_1.return)) _b.call(result_1); } finally { if (e_4) throw e_4.error; } } if (add) { result.push(newState); } } } catch (e_3_1) { e_3 = { error: e_3_1 }; } finally { try { if (states_3_1 && !states_3_1.done && (_a = states_3.return)) _a.call(states_3); } finally { if (e_3) throw e_3.error; } } return result; }; return HighLevelEncoder; }()); export default HighLevelEncoder;