UNPKG

@zxing/library

Version:

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

196 lines (195 loc) 7.59 kB
import StringBuilder from '../../util/StringBuilder'; import HighLevelEncoder from './HighLevelEncoder'; import { C40_ENCODATION, LATCH_TO_C40, ASCII_ENCODATION, C40_UNLATCH, } from './constants'; export class C40Encoder { getEncodingMode() { return C40_ENCODATION; } encodeMaximal(context) { const buffer = new StringBuilder(); let lastCharSize = 0; let backtrackStartPosition = context.pos; let backtrackBufferLength = 0; while (context.hasMoreCharacters()) { const c = context.getCurrentChar(); context.pos++; lastCharSize = this.encodeChar(c, buffer); if (buffer.length() % 3 === 0) { backtrackStartPosition = context.pos; backtrackBufferLength = buffer.length(); } } if (backtrackBufferLength !== buffer.length()) { const unwritten = Math.floor((buffer.length() / 3) * 2); const curCodewordCount = Math.floor(context.getCodewordCount() + unwritten + 1); // +1 for the latch to C40 context.updateSymbolInfo(curCodewordCount); const available = context.getSymbolInfo().getDataCapacity() - curCodewordCount; const rest = Math.floor(buffer.length() % 3); if ((rest === 2 && available !== 2) || (rest === 1 && (lastCharSize > 3 || available !== 1))) { // buffer.setLength(backtrackBufferLength); context.pos = backtrackStartPosition; } } if (buffer.length() > 0) { context.writeCodeword(LATCH_TO_C40); } this.handleEOD(context, buffer); } encode(context) { // step C const buffer = new StringBuilder(); while (context.hasMoreCharacters()) { const c = context.getCurrentChar(); context.pos++; let lastCharSize = this.encodeChar(c, buffer); const unwritten = Math.floor(buffer.length() / 3) * 2; const curCodewordCount = context.getCodewordCount() + unwritten; context.updateSymbolInfo(curCodewordCount); const available = context.getSymbolInfo().getDataCapacity() - curCodewordCount; if (!context.hasMoreCharacters()) { // Avoid having a single C40 value in the last triplet const removed = new StringBuilder(); if (buffer.length() % 3 === 2 && available !== 2) { lastCharSize = this.backtrackOneCharacter(context, buffer, removed, lastCharSize); } while (buffer.length() % 3 === 1 && (lastCharSize > 3 || available !== 1)) { lastCharSize = this.backtrackOneCharacter(context, buffer, removed, lastCharSize); } break; } const count = buffer.length(); if (count % 3 === 0) { const newMode = HighLevelEncoder.lookAheadTest(context.getMessage(), context.pos, this.getEncodingMode()); if (newMode !== this.getEncodingMode()) { // Return to ASCII encodation, which will actually handle latch to new mode context.signalEncoderChange(ASCII_ENCODATION); break; } } } this.handleEOD(context, buffer); } backtrackOneCharacter(context, buffer, removed, lastCharSize) { const count = buffer.length(); const test = buffer.toString().substring(0, count - lastCharSize); buffer.setLengthToZero(); buffer.append(test); // buffer.delete(count - lastCharSize, count); /*for (let i = count - lastCharSize; i < count; i++) { buffer.deleteCharAt(i); }*/ context.pos--; const c = context.getCurrentChar(); lastCharSize = this.encodeChar(c, removed); context.resetSymbolInfo(); // Deal with possible reduction in symbol size return lastCharSize; } writeNextTriplet(context, buffer) { context.writeCodewords(this.encodeToCodewords(buffer.toString())); const test = buffer.toString().substring(3); buffer.setLengthToZero(); buffer.append(test); // buffer.delete(0, 3); /*for (let i = 0; i < 3; i++) { buffer.deleteCharAt(i); }*/ } /** * Handle "end of data" situations * * @param context the encoder context * @param buffer the buffer with the remaining encoded characters */ handleEOD(context, buffer) { const unwritten = Math.floor((buffer.length() / 3) * 2); const rest = buffer.length() % 3; const curCodewordCount = context.getCodewordCount() + unwritten; context.updateSymbolInfo(curCodewordCount); const available = context.getSymbolInfo().getDataCapacity() - curCodewordCount; if (rest === 2) { buffer.append('\0'); // Shift 1 while (buffer.length() >= 3) { this.writeNextTriplet(context, buffer); } if (context.hasMoreCharacters()) { context.writeCodeword(C40_UNLATCH); } } else if (available === 1 && rest === 1) { while (buffer.length() >= 3) { this.writeNextTriplet(context, buffer); } if (context.hasMoreCharacters()) { context.writeCodeword(C40_UNLATCH); } // else no unlatch context.pos--; } else if (rest === 0) { while (buffer.length() >= 3) { this.writeNextTriplet(context, buffer); } if (available > 0 || context.hasMoreCharacters()) { context.writeCodeword(C40_UNLATCH); } } else { throw new Error('Unexpected case. Please report!'); } context.signalEncoderChange(ASCII_ENCODATION); } encodeChar(c, sb) { if (c === ' '.charCodeAt(0)) { sb.append(0o3); return 1; } if (c >= '0'.charCodeAt(0) && c <= '9'.charCodeAt(0)) { sb.append(c - 48 + 4); return 1; } if (c >= 'A'.charCodeAt(0) && c <= 'Z'.charCodeAt(0)) { sb.append(c - 65 + 14); return 1; } if (c < ' '.charCodeAt(0)) { sb.append(0o0); // Shift 1 Set sb.append(c); return 2; } if (c <= '/'.charCodeAt(0)) { sb.append(0o1); // Shift 2 Set sb.append(c - 33); return 2; } if (c <= '@'.charCodeAt(0)) { sb.append(0o1); // Shift 2 Set sb.append(c - 58 + 15); return 2; } if (c <= '_'.charCodeAt(0)) { sb.append(0o1); // Shift 2 Set sb.append(c - 91 + 22); return 2; } if (c <= 127) { sb.append(0o2); // Shift 3 Set sb.append(c - 96); return 2; } sb.append(`${0o1}\u001e`); // Shift 2, Upper Shift let len = 2; len += this.encodeChar(c - 128, sb); return len; } encodeToCodewords(sb) { const v = 1600 * sb.charCodeAt(0) + 40 * sb.charCodeAt(1) + sb.charCodeAt(2) + 1; const cw1 = v / 256; const cw2 = v % 256; const result = new StringBuilder(); result.append(cw1); result.append(cw2); return result.toString(); } }