diff --git a/.gitattributes b/.gitattributes index 9d5d7a5f..5d4c0523 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,20 @@ -**/*.properties text +# See: https://stackoverflow.com/questions/170961/whats-the-strategy-for-handling-crlf-carriage-return-line-feed-with-git +# See: https://stackoverflow.com/questions/42667996/enforce-core-autocrlf-input-through-gitattributes +* text=auto + +*.md text +*.xml text +*.svg text +*.eps text +*.seed text +*.form text +*.java text diff=java +*.gradle text diff=java +*.properties text + +gradlew text eol=lf +gradlew.bat text eol=crlf + +*.png binary +*.ttf binary diff --git a/src/main/java/uk/org/okapibarcode/OkapiBarcode.java b/src/main/java/uk/org/okapibarcode/OkapiBarcode.java index 824f82c0..dc462b3c 100644 --- a/src/main/java/uk/org/okapibarcode/OkapiBarcode.java +++ b/src/main/java/uk/org/okapibarcode/OkapiBarcode.java @@ -1,276 +1,276 @@ -/* - * Copyright 2014-2015 Robin Stuart, Robert Elliott - * - * 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. - */ - -package uk.org.okapibarcode; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; - -import com.beust.jcommander.JCommander; - -import uk.org.okapibarcode.gui.OkapiUI; - -/** - * Starts the Okapi Barcode UI. - * - * @author Robin Stuart - * @author Robert Elliott - */ -public class OkapiBarcode { - - /** - * Starts the Okapi Barcode UI. - * - * @param args the command line arguments - */ - public static void main(String[] args) { - - Settings settings = new Settings(); - JCommander cmd = new JCommander(settings); - cmd.parse(args); - - if (!settings.isGuiSupressed()) { - OkapiUI okapiUi = new OkapiUI(); - okapiUi.setVisible(true); - } else { - int returnValue = commandLine(settings); - if (returnValue != 0) { - System.out.println("An error occurred"); - } - } - } - - private static int commandLine(Settings settings) { - - String inputData = settings.getInputData(); - String inputFile = settings.getInputFile(); - - if (settings.isDisplayTypes()) { - System.out.print( - " 1: Code 11 54: Brazilian CepNet 97: Micro QR Code\n" + - " 2: Standard 2of5 55: PDF417 98: HIBC Code 128\n" + - " 3: Interleaved 2of5 56: PDF417 Trunc 99: HIBC Code 39\n" + - " 4: IATA 2of5 57: Maxicode 102: HIBC Data Matrix\n" + - " 6: Data Logic 58: QR Code 104: HIBC QR Code\n" + - " 7: Industrial 2of5 60: Code 128-B 106: HIBC PDF417\n" + - " 8: Code 39 63: AP Standard Customer 108: HIBC MicroPDF417\n" + - " 9: Extended Code 39 66: AP Reply Paid 110: HIBC Codablock-F\n" + - "13: EAN 67: AP Routing 112: HIBC Aztec Code\n" + - "18: Codabar 68: AP Redirection 113: PZN-8\n" + - "20: Code 128 70: RM4SCC 117: USPS IMpb\n" + - "21: Leitcode 71: Data Matrix 128: Aztec Runes\n" + - "22: Identcode 74: Codablock-F 129: Code 32\n" + - "23: Code 16k 75: NVE-18 130: Comp EAN\n" + - "24: Code 49 76: Japanese Post 131: Comp GS1-128\n" + - "25: Code 93 77: Korea Post 132: Comp Databar-14\n" + - "29: Databar-14 79: Databar-14 Stack 133: Comp Databar Ltd\n" + - "30: Databar Limited 80: Databar-14 Stack Omni 134: Comp Databar Ext\n" + - "31: Databar Extended 81: Databar Extended Stack 135: Comp UPC-A\n" + - "32: Telepen Alpha 82: Planet 136: Comp UPC-E\n" + - "34: UPC-A 84: MicroPDF 137: Comp Databar-14 Stack\n" + - "37: UPC-E 85: USPS Intelligent Mail 138: Comp Databar Stack Omni\n" + - "40: Postnet 87: Telepen Numeric 139: Comp Databar Ext Stack\n" + - "47: MSI Plessey 89: ITF-14 140: Channel Code\n" + - "50: Logmars 90: KIX Code 141: Code One \n" + - "51: Pharma One-Track 92: Aztec Code 142: Grid Matrix\n" + - "53: Pharma Two-Track 93: Code 32\n" ); - } - - if (inputData.isEmpty() && inputFile.isEmpty()) { - System.out.println("error: No data received, no symbol generated"); - return 0; - } - - if (inputFile.isEmpty()) { - if (!(settings.isDataBinaryMode())) { - inputData = escapeCharProcess(inputData); - } - MakeBarcode mb = new MakeBarcode(); - mb.process(settings, inputData, settings.getOutputFile()); - } else { - processFile(settings); - } - - return 0; - } - - private static void processFile(Settings settings) { - File name = new File(settings.getInputFile()); - byte[] inputBytes; - String inputData; - int counter = 0; - - if (!(settings.isBatchMode())) { - // Encode all data from selected file in one symbol - try (FileInputStream fis = new FileInputStream(name)) { - inputBytes = new byte[fis.available()]; - fis.read(inputBytes); - inputData = new String(inputBytes, "UTF-8"); - MakeBarcode mb = new MakeBarcode(); - mb.process(settings, inputData, settings.getOutputFile()); - } catch (IOException e) { - System.out.println("File Read Error"); - } - } else { - // Encode each line of input data in a seperate symbol - try (BufferedReader in = new BufferedReader( - new InputStreamReader( - new FileInputStream(name), "UTF8"))) { - - while ((inputData = in.readLine()) != null) { - counter++; - MakeBarcode mb = new MakeBarcode(); - mb.process(settings, inputData, calcFileName(settings, counter)); - } - } catch (UnsupportedEncodingException e) { - System.out.println("Encoding exception"); - } catch (IOException e) { - System.out.println("File Read Error"); - } - } - } - - private static String calcFileName(Settings settings, int counter) { - String fileName = ""; - String number; - int spaces = 0; - int blanks; - int blankPosition; - String template; - - number = Integer.toString(counter); - - if (settings.getOutputFile().equals("out.png")) { - // No filename set by user - template = "~~~~~.png"; - } else { - template = settings.getOutputFile(); - } - - for (int i = 0; i < template.length(); i++) { - switch(template.charAt(i)) { - case '#': - case '~': - spaces++; - break; - } - } - - blanks = spaces - number.length(); - - if (blanks < 0) { - // Not enough room in template for file number - System.out.println("Invalid output filename"); - return "out.png"; - } - - blankPosition = 0; - - for (int i = 0; i < template.length(); i++) { - switch(template.charAt(i)) { - case '#': - if (blankPosition >= blanks) { - fileName += number.charAt(blankPosition - blanks); - } else { - fileName += ' '; - } - blankPosition++; - break; - case '~': - if (blankPosition >= blanks) { - fileName += number.charAt(blankPosition - blanks); - } else { - fileName += '0'; - } - blankPosition++; - break; - default: - fileName += template.charAt(i); - break; - } - } - - return fileName; - } - - private static String escapeCharProcess(String inputString) { - String outputString = ""; - int i = 0; - - do { - if (inputString.charAt(i) == '\\') { - if (i < inputString.length() - 1) { - switch(inputString.charAt(i + 1)) { - case '0': /* Null */ - outputString += 0x00; - break; - case 'E': /* End of Transmission */ - outputString += 0x04; - break; - case 'a': /* Bell */ - outputString += 0x07; - break; - case 'b': /* Backspace */ - outputString += 0x08; - break; - case 't': /* Horizontal tab */ - outputString += 0x09; - break; - case 'n': /* Line feed */ - outputString += 0x0a; - break; - case 'v': /* Vertical tab */ - outputString += 0x0b; - break; - case 'f': /* Form feed */ - outputString += 0x0c; - break; - case 'r': /* Carriage return */ - outputString += 0x0d; - break; - case 'e': /* Escape */ - outputString += 0x1b; - break; - case 'G': /* Group Separator */ - outputString +=0x1d; - break; - case 'R': /* Record Separator */ - outputString += 0x1e; - break; - default: - outputString += '\\'; - outputString += inputString.charAt(i); - break; - } - i += 2; - } else { - outputString += '\\'; - i++; - } - } else { - outputString += inputString.charAt(i); - i++; - } - } while(i < inputString.length()); - - return outputString; - } - -} +/* + * Copyright 2014-2015 Robin Stuart, Robert Elliott + * + * 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. + */ + +package uk.org.okapibarcode; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; + +import com.beust.jcommander.JCommander; + +import uk.org.okapibarcode.gui.OkapiUI; + +/** + * Starts the Okapi Barcode UI. + * + * @author Robin Stuart + * @author Robert Elliott + */ +public class OkapiBarcode { + + /** + * Starts the Okapi Barcode UI. + * + * @param args the command line arguments + */ + public static void main(String[] args) { + + Settings settings = new Settings(); + JCommander cmd = new JCommander(settings); + cmd.parse(args); + + if (!settings.isGuiSupressed()) { + OkapiUI okapiUi = new OkapiUI(); + okapiUi.setVisible(true); + } else { + int returnValue = commandLine(settings); + if (returnValue != 0) { + System.out.println("An error occurred"); + } + } + } + + private static int commandLine(Settings settings) { + + String inputData = settings.getInputData(); + String inputFile = settings.getInputFile(); + + if (settings.isDisplayTypes()) { + System.out.print( + " 1: Code 11 54: Brazilian CepNet 97: Micro QR Code\n" + + " 2: Standard 2of5 55: PDF417 98: HIBC Code 128\n" + + " 3: Interleaved 2of5 56: PDF417 Trunc 99: HIBC Code 39\n" + + " 4: IATA 2of5 57: Maxicode 102: HIBC Data Matrix\n" + + " 6: Data Logic 58: QR Code 104: HIBC QR Code\n" + + " 7: Industrial 2of5 60: Code 128-B 106: HIBC PDF417\n" + + " 8: Code 39 63: AP Standard Customer 108: HIBC MicroPDF417\n" + + " 9: Extended Code 39 66: AP Reply Paid 110: HIBC Codablock-F\n" + + "13: EAN 67: AP Routing 112: HIBC Aztec Code\n" + + "18: Codabar 68: AP Redirection 113: PZN-8\n" + + "20: Code 128 70: RM4SCC 117: USPS IMpb\n" + + "21: Leitcode 71: Data Matrix 128: Aztec Runes\n" + + "22: Identcode 74: Codablock-F 129: Code 32\n" + + "23: Code 16k 75: NVE-18 130: Comp EAN\n" + + "24: Code 49 76: Japanese Post 131: Comp GS1-128\n" + + "25: Code 93 77: Korea Post 132: Comp Databar-14\n" + + "29: Databar-14 79: Databar-14 Stack 133: Comp Databar Ltd\n" + + "30: Databar Limited 80: Databar-14 Stack Omni 134: Comp Databar Ext\n" + + "31: Databar Extended 81: Databar Extended Stack 135: Comp UPC-A\n" + + "32: Telepen Alpha 82: Planet 136: Comp UPC-E\n" + + "34: UPC-A 84: MicroPDF 137: Comp Databar-14 Stack\n" + + "37: UPC-E 85: USPS Intelligent Mail 138: Comp Databar Stack Omni\n" + + "40: Postnet 87: Telepen Numeric 139: Comp Databar Ext Stack\n" + + "47: MSI Plessey 89: ITF-14 140: Channel Code\n" + + "50: Logmars 90: KIX Code 141: Code One \n" + + "51: Pharma One-Track 92: Aztec Code 142: Grid Matrix\n" + + "53: Pharma Two-Track 93: Code 32\n" ); + } + + if (inputData.isEmpty() && inputFile.isEmpty()) { + System.out.println("error: No data received, no symbol generated"); + return 0; + } + + if (inputFile.isEmpty()) { + if (!(settings.isDataBinaryMode())) { + inputData = escapeCharProcess(inputData); + } + MakeBarcode mb = new MakeBarcode(); + mb.process(settings, inputData, settings.getOutputFile()); + } else { + processFile(settings); + } + + return 0; + } + + private static void processFile(Settings settings) { + File name = new File(settings.getInputFile()); + byte[] inputBytes; + String inputData; + int counter = 0; + + if (!(settings.isBatchMode())) { + // Encode all data from selected file in one symbol + try (FileInputStream fis = new FileInputStream(name)) { + inputBytes = new byte[fis.available()]; + fis.read(inputBytes); + inputData = new String(inputBytes, "UTF-8"); + MakeBarcode mb = new MakeBarcode(); + mb.process(settings, inputData, settings.getOutputFile()); + } catch (IOException e) { + System.out.println("File Read Error"); + } + } else { + // Encode each line of input data in a seperate symbol + try (BufferedReader in = new BufferedReader( + new InputStreamReader( + new FileInputStream(name), "UTF8"))) { + + while ((inputData = in.readLine()) != null) { + counter++; + MakeBarcode mb = new MakeBarcode(); + mb.process(settings, inputData, calcFileName(settings, counter)); + } + } catch (UnsupportedEncodingException e) { + System.out.println("Encoding exception"); + } catch (IOException e) { + System.out.println("File Read Error"); + } + } + } + + private static String calcFileName(Settings settings, int counter) { + String fileName = ""; + String number; + int spaces = 0; + int blanks; + int blankPosition; + String template; + + number = Integer.toString(counter); + + if (settings.getOutputFile().equals("out.png")) { + // No filename set by user + template = "~~~~~.png"; + } else { + template = settings.getOutputFile(); + } + + for (int i = 0; i < template.length(); i++) { + switch(template.charAt(i)) { + case '#': + case '~': + spaces++; + break; + } + } + + blanks = spaces - number.length(); + + if (blanks < 0) { + // Not enough room in template for file number + System.out.println("Invalid output filename"); + return "out.png"; + } + + blankPosition = 0; + + for (int i = 0; i < template.length(); i++) { + switch(template.charAt(i)) { + case '#': + if (blankPosition >= blanks) { + fileName += number.charAt(blankPosition - blanks); + } else { + fileName += ' '; + } + blankPosition++; + break; + case '~': + if (blankPosition >= blanks) { + fileName += number.charAt(blankPosition - blanks); + } else { + fileName += '0'; + } + blankPosition++; + break; + default: + fileName += template.charAt(i); + break; + } + } + + return fileName; + } + + private static String escapeCharProcess(String inputString) { + String outputString = ""; + int i = 0; + + do { + if (inputString.charAt(i) == '\\') { + if (i < inputString.length() - 1) { + switch(inputString.charAt(i + 1)) { + case '0': /* Null */ + outputString += 0x00; + break; + case 'E': /* End of Transmission */ + outputString += 0x04; + break; + case 'a': /* Bell */ + outputString += 0x07; + break; + case 'b': /* Backspace */ + outputString += 0x08; + break; + case 't': /* Horizontal tab */ + outputString += 0x09; + break; + case 'n': /* Line feed */ + outputString += 0x0a; + break; + case 'v': /* Vertical tab */ + outputString += 0x0b; + break; + case 'f': /* Form feed */ + outputString += 0x0c; + break; + case 'r': /* Carriage return */ + outputString += 0x0d; + break; + case 'e': /* Escape */ + outputString += 0x1b; + break; + case 'G': /* Group Separator */ + outputString +=0x1d; + break; + case 'R': /* Record Separator */ + outputString += 0x1e; + break; + default: + outputString += '\\'; + outputString += inputString.charAt(i); + break; + } + i += 2; + } else { + outputString += '\\'; + i++; + } + } else { + outputString += inputString.charAt(i); + i++; + } + } while(i < inputString.length()); + + return outputString; + } + +} diff --git a/src/main/java/uk/org/okapibarcode/Settings.java b/src/main/java/uk/org/okapibarcode/Settings.java index 3d54a356..30cf6dcb 100644 --- a/src/main/java/uk/org/okapibarcode/Settings.java +++ b/src/main/java/uk/org/okapibarcode/Settings.java @@ -1,337 +1,337 @@ -/* - * Copyright 2015 Robin Stuart - * - * 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. - */ - -package uk.org.okapibarcode; - -import com.beust.jcommander.Parameter; - -import uk.org.okapibarcode.backend.HumanReadableLocation; -import uk.org.okapibarcode.graphics.Color; - -/** - * - * @author Robin Stuart - */ -public class Settings { - - @Parameter(names = "-cli", description = "Supress GUI loading", required = false) - private boolean supressGui = false; - - @Parameter(names = {"-t", "--types"}, description = "Display table of barcode types", required = false) - private boolean displayTypes = false; - - @Parameter(names = {"-i", "--input"}, description = "Read data from file", required = false) - private String inputFile = ""; - - @Parameter(names = {"-o", "--output"}, description = "Write image to file", required = false) - private String outputFile = "out.png"; - - @Parameter(names = {"-d", "--data"}, description = "Barcode content", required = false) - private String inputData = ""; - - @Parameter(names = {"-b", "--barcode"}, description = "Select barcode type", required = false) - private int symbolType = 20; - - @Parameter(names = "--height", description = "Height of the symbol in multiples of x-dimension", required = false) - private int symbolHeight = 0; - -// @Parameter(names = {"-w", "--whitesp"}, description = "Width of whitespace in multiples of x-dimension", required = false) -// private int symbolWhiteSpace = 0; -// -// @Parameter(names = "--border", description = "Width of border in multiples of x-dimension", required = false) -// private int symbolBorder = 0; - -// @Parameter(names = "--box", description = "Add a box", required = false) -// private boolean addBox = false; -// -// @Parameter(names = "--bind", description = "Add boundary bars", required = false) -// private boolean addBinding = false; - - @Parameter(names = {"-r", "--reverse"}, description = "Reverse colours (white on black)", required = false) - private boolean reverseColour = false; - - @Parameter(names = "--fg", description = "Specify a foreground (ink) colour", required = false) - private String foregroundColour = "000000"; - - @Parameter(names = "--bg", description = "Specify a background (paper) colour", required = false) - private String backgroundColour = "FFFFFF"; - - @Parameter(names = "--scale", description = "Adjust size of output image", required = false) - private int symbolScale = 0; - - // --directpng, --directeps, --directsvg, --dump - -// @Parameter(names = "--rotate", description = "Rotate symbol", required = false) -// private int rotationAngle = 0; - - @Parameter(names = "--cols", description = "Number of columns in PDF417", required = false) - private int symbolColumns = 0; - - @Parameter(names = "--vers", description = "Set QR Code version number", required = false) - private int symbolVersion = 0; - - @Parameter(names = "--secure", description = "Set error correction level", required = false) - private int symbolECC = 0; - - @Parameter(names = "--primary", description = "Add structured primary message", required = false) - private String primaryData = ""; - - @Parameter(names = "--mode", description = "Set encoding mode", required = false) - private int encodeMode = 0; - - @Parameter(names = "--gs1", description = "Treat input as GS1 data", required = false) - private boolean dataGs1Mode = false; - - @Parameter(names = "--binary", description = "Treat input as binary data", required = false) - private boolean dataBinaryMode = false; - - @Parameter(names = "--notext", description = "Remove human readable text", required = false) - private boolean supressHrt = false; - - @Parameter(names = "--textabove", description = "Place human readable text above symbol", required = false) - private boolean superHrt = false; - - @Parameter(names = "--square", description = "Force Data Matrix symbols to be square", required = false) - private boolean makeSquare = false; - - @Parameter(names = "--init", description = "Add reader initialisation code", required = false) - private boolean addReaderInit = false; - - // --smalltext - - @Parameter(names = "--batch", description = "Treat each line of input as a separate data set", required = false) - private boolean batchMode = false; - - /** - * @return the supressGui - */ - public boolean isGuiSupressed() { - return supressGui; - } - - /** - * @return the displayTypes - */ - public boolean isDisplayTypes() { - return displayTypes; - } - - /** - * @return the inputFile - */ - public String getInputFile() { - return inputFile; - } - - /** - * @return the outputFile - */ - public String getOutputFile() { - return outputFile; - } - - /** - * @return the inputData - */ - public String getInputData() { - return inputData; - } - - /** - * @return the symbolType - */ - public int getSymbolType() { - return symbolType; - } - - /** - * @return the symbolHeight - */ - public int getSymbolHeight() { - return symbolHeight; - } - -// /** -// * @return the symbolWhiteSpace -// */ -// public int getSymbolWhiteSpace() { -// return symbolWhiteSpace; -// } - -// /** -// * @return the symbolBorder -// */ -// public int getSymbolBorder() { -// return symbolBorder; -// } - -// /** -// * @return the addBox -// */ -// public boolean isAddBox() { -// return addBox; -// } -// -// /** -// * @return the addBinding -// */ -// public boolean isAddBinding() { -// return addBinding; -// } - - /** - * @return the reverseColour - */ - public boolean isReverseColour() { - return reverseColour; - } - - /** - * @return the foregroundColour - */ - public Color getForegroundColour() { - Color inkColour = Color.BLACK; - String fgColour; - - fgColour = foregroundColour.toUpperCase(); - - if (fgColour.matches("[0-9A-F]{6}")) { - int rgb = Integer.parseInt(fgColour, 16); - inkColour = new Color(rgb); - } - - return inkColour; - } - - /** - * @return the backgroundColour - */ - public Color getBackgroundColour() { - Color paperColour = Color.WHITE; - String bgColour; - - bgColour = backgroundColour.toUpperCase(); - - if (bgColour.matches("[0-9A-F]{6}")) { - int rgb = Integer.parseInt(bgColour, 16); - paperColour = new Color(rgb); - } - - return paperColour; - } - - /** - * @return the symbolScale - */ - public int getSymbolScale() { - return symbolScale; - } - -// /** -// * @return the rotationAngle -// */ -// public int getRotationAngle() { -// return rotationAngle; -// } - - /** - * @return the symbolColumns - */ - public int getSymbolColumns() { - return symbolColumns; - } - - /** - * @return the symbolVersion - */ - public int getSymbolVersion() { - return symbolVersion; - } - - /** - * @return the symbolECC - */ - public int getSymbolECC() { - return symbolECC; - } - - /** - * @return the primaryData - */ - public String getPrimaryData() { - return primaryData; - } - - /** - * @return the encodeMode - */ - public int getEncodeMode() { - return encodeMode; - } - - /** - * @return the dataGs1Mode - */ - public boolean isDataGs1Mode() { - return dataGs1Mode; - } - - /** - * @return the dataBinaryMode - */ - public boolean isDataBinaryMode() { - return dataBinaryMode; - } - - /** - * @return the supressHrt - */ - public HumanReadableLocation getHrtPosition() { - HumanReadableLocation temp = HumanReadableLocation.BOTTOM; - - if(superHrt) { - temp = HumanReadableLocation.TOP; - } - - if(supressHrt) { - temp = HumanReadableLocation.NONE; - } - - return temp; - } - - /** - * @return the makeSquare - */ - public boolean isMakeSquare() { - return makeSquare; - } - - /** - * @return the addReaderInit - */ - public boolean isReaderInit() { - return addReaderInit; - } - - /** - * @return the batchMode - */ - public boolean isBatchMode() { - return batchMode; - } - +/* + * Copyright 2015 Robin Stuart + * + * 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. + */ + +package uk.org.okapibarcode; + +import com.beust.jcommander.Parameter; + +import uk.org.okapibarcode.backend.HumanReadableLocation; +import uk.org.okapibarcode.graphics.Color; + +/** + * + * @author Robin Stuart + */ +public class Settings { + + @Parameter(names = "-cli", description = "Supress GUI loading", required = false) + private boolean supressGui = false; + + @Parameter(names = {"-t", "--types"}, description = "Display table of barcode types", required = false) + private boolean displayTypes = false; + + @Parameter(names = {"-i", "--input"}, description = "Read data from file", required = false) + private String inputFile = ""; + + @Parameter(names = {"-o", "--output"}, description = "Write image to file", required = false) + private String outputFile = "out.png"; + + @Parameter(names = {"-d", "--data"}, description = "Barcode content", required = false) + private String inputData = ""; + + @Parameter(names = {"-b", "--barcode"}, description = "Select barcode type", required = false) + private int symbolType = 20; + + @Parameter(names = "--height", description = "Height of the symbol in multiples of x-dimension", required = false) + private int symbolHeight = 0; + +// @Parameter(names = {"-w", "--whitesp"}, description = "Width of whitespace in multiples of x-dimension", required = false) +// private int symbolWhiteSpace = 0; +// +// @Parameter(names = "--border", description = "Width of border in multiples of x-dimension", required = false) +// private int symbolBorder = 0; + +// @Parameter(names = "--box", description = "Add a box", required = false) +// private boolean addBox = false; +// +// @Parameter(names = "--bind", description = "Add boundary bars", required = false) +// private boolean addBinding = false; + + @Parameter(names = {"-r", "--reverse"}, description = "Reverse colours (white on black)", required = false) + private boolean reverseColour = false; + + @Parameter(names = "--fg", description = "Specify a foreground (ink) colour", required = false) + private String foregroundColour = "000000"; + + @Parameter(names = "--bg", description = "Specify a background (paper) colour", required = false) + private String backgroundColour = "FFFFFF"; + + @Parameter(names = "--scale", description = "Adjust size of output image", required = false) + private int symbolScale = 0; + + // --directpng, --directeps, --directsvg, --dump + +// @Parameter(names = "--rotate", description = "Rotate symbol", required = false) +// private int rotationAngle = 0; + + @Parameter(names = "--cols", description = "Number of columns in PDF417", required = false) + private int symbolColumns = 0; + + @Parameter(names = "--vers", description = "Set QR Code version number", required = false) + private int symbolVersion = 0; + + @Parameter(names = "--secure", description = "Set error correction level", required = false) + private int symbolECC = 0; + + @Parameter(names = "--primary", description = "Add structured primary message", required = false) + private String primaryData = ""; + + @Parameter(names = "--mode", description = "Set encoding mode", required = false) + private int encodeMode = 0; + + @Parameter(names = "--gs1", description = "Treat input as GS1 data", required = false) + private boolean dataGs1Mode = false; + + @Parameter(names = "--binary", description = "Treat input as binary data", required = false) + private boolean dataBinaryMode = false; + + @Parameter(names = "--notext", description = "Remove human readable text", required = false) + private boolean supressHrt = false; + + @Parameter(names = "--textabove", description = "Place human readable text above symbol", required = false) + private boolean superHrt = false; + + @Parameter(names = "--square", description = "Force Data Matrix symbols to be square", required = false) + private boolean makeSquare = false; + + @Parameter(names = "--init", description = "Add reader initialisation code", required = false) + private boolean addReaderInit = false; + + // --smalltext + + @Parameter(names = "--batch", description = "Treat each line of input as a separate data set", required = false) + private boolean batchMode = false; + + /** + * @return the supressGui + */ + public boolean isGuiSupressed() { + return supressGui; + } + + /** + * @return the displayTypes + */ + public boolean isDisplayTypes() { + return displayTypes; + } + + /** + * @return the inputFile + */ + public String getInputFile() { + return inputFile; + } + + /** + * @return the outputFile + */ + public String getOutputFile() { + return outputFile; + } + + /** + * @return the inputData + */ + public String getInputData() { + return inputData; + } + + /** + * @return the symbolType + */ + public int getSymbolType() { + return symbolType; + } + + /** + * @return the symbolHeight + */ + public int getSymbolHeight() { + return symbolHeight; + } + +// /** +// * @return the symbolWhiteSpace +// */ +// public int getSymbolWhiteSpace() { +// return symbolWhiteSpace; +// } + +// /** +// * @return the symbolBorder +// */ +// public int getSymbolBorder() { +// return symbolBorder; +// } + +// /** +// * @return the addBox +// */ +// public boolean isAddBox() { +// return addBox; +// } +// +// /** +// * @return the addBinding +// */ +// public boolean isAddBinding() { +// return addBinding; +// } + + /** + * @return the reverseColour + */ + public boolean isReverseColour() { + return reverseColour; + } + + /** + * @return the foregroundColour + */ + public Color getForegroundColour() { + Color inkColour = Color.BLACK; + String fgColour; + + fgColour = foregroundColour.toUpperCase(); + + if (fgColour.matches("[0-9A-F]{6}")) { + int rgb = Integer.parseInt(fgColour, 16); + inkColour = new Color(rgb); + } + + return inkColour; + } + + /** + * @return the backgroundColour + */ + public Color getBackgroundColour() { + Color paperColour = Color.WHITE; + String bgColour; + + bgColour = backgroundColour.toUpperCase(); + + if (bgColour.matches("[0-9A-F]{6}")) { + int rgb = Integer.parseInt(bgColour, 16); + paperColour = new Color(rgb); + } + + return paperColour; + } + + /** + * @return the symbolScale + */ + public int getSymbolScale() { + return symbolScale; + } + +// /** +// * @return the rotationAngle +// */ +// public int getRotationAngle() { +// return rotationAngle; +// } + + /** + * @return the symbolColumns + */ + public int getSymbolColumns() { + return symbolColumns; + } + + /** + * @return the symbolVersion + */ + public int getSymbolVersion() { + return symbolVersion; + } + + /** + * @return the symbolECC + */ + public int getSymbolECC() { + return symbolECC; + } + + /** + * @return the primaryData + */ + public String getPrimaryData() { + return primaryData; + } + + /** + * @return the encodeMode + */ + public int getEncodeMode() { + return encodeMode; + } + + /** + * @return the dataGs1Mode + */ + public boolean isDataGs1Mode() { + return dataGs1Mode; + } + + /** + * @return the dataBinaryMode + */ + public boolean isDataBinaryMode() { + return dataBinaryMode; + } + + /** + * @return the supressHrt + */ + public HumanReadableLocation getHrtPosition() { + HumanReadableLocation temp = HumanReadableLocation.BOTTOM; + + if(superHrt) { + temp = HumanReadableLocation.TOP; + } + + if(supressHrt) { + temp = HumanReadableLocation.NONE; + } + + return temp; + } + + /** + * @return the makeSquare + */ + public boolean isMakeSquare() { + return makeSquare; + } + + /** + * @return the addReaderInit + */ + public boolean isReaderInit() { + return addReaderInit; + } + + /** + * @return the batchMode + */ + public boolean isBatchMode() { + return batchMode; + } + } \ No newline at end of file diff --git a/src/main/java/uk/org/okapibarcode/backend/AustraliaPost.java b/src/main/java/uk/org/okapibarcode/backend/AustraliaPost.java index 69c5ce10..2af89343 100644 --- a/src/main/java/uk/org/okapibarcode/backend/AustraliaPost.java +++ b/src/main/java/uk/org/okapibarcode/backend/AustraliaPost.java @@ -1,358 +1,358 @@ -/* - * Copyright 2014 Robin Stuart - * - * 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. - */ - -package uk.org.okapibarcode.backend; - -import static uk.org.okapibarcode.util.Arrays.positionOf; - -import uk.org.okapibarcode.graphics.Rectangle; - -/** - * Implements the Australia Post 4-State barcode. - * - * @author Robin Stuart - */ -public class AustraliaPost extends Symbol { - - /** - * The different Australia Post barcode variants available to encode. - */ - public enum Mode { - /** - *

Australia Post Standard Customer Barcode, Customer Barcode 2, or Customer Barcode 3 - * (37-bar, 52-bar and 67-bar symbols) depending on input data length. Valid data characters - * are 0-9, A-Z, a-z, space and hash (#). A Format Control Code (FCC) is added and should not - * be included in the input data. - * - *

Input data should include a 8-digit Deliver Point ID (DPID) optionally followed by - * customer information as shown below. - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Input LengthRequired Input FormatSymbol LengthFCCEncoding Table
89999999937-bar11None
1399999999AAAAA52-bar59C
16999999999999999952-bar59N
1899999999AAAAAAAAAA67-bar62C
239999999999999999999999967-bar62N
- */ - POST, - /** - * Reply Paid version of the Australia Post 4-State Barcode (FCC 45) which requires an 8-digit DPID input. - */ - REPLY, - /** - * Routing version of the Australia Post 4-State Barcode (FCC 87) which requires an 8-digit DPID input. - */ - ROUTE, - /** - * Redirection version of the Australia Post 4-State Barcode (FCC 92) which requires an 8-digit DPID input. - */ - REDIRECT - } - - private static final char[] CHARACTER_SET = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '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', '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', ' ', '#' - }; - - private static final String[] N_ENCODING_TABLE = { - "00", "01", "02", "10", "11", "12", "20", "21", "22", "30" - }; - - private static final String[] C_ENCODING_TABLE = { - "222", "300", "301", "302", "310", "311", "312", "320", "321", "322", - "000", "001", "002", "010", "011", "012", "020", "021", "022", "100", "101", "102", "110", - "111", "112", "120", "121", "122", "200", "201", "202", "210", "211", "212", "220", "221", - "023", "030", "031", "032", "033", "103", "113", "123", "130", "131", "132", "133", "203", - "213", "223", "230", "231", "232", "233", "303", "313", "323", "330", "331", "332", "333", - "003", "013" - }; - - private static final String[] BAR_VALUE_TABLE = { - "000", "001", "002", "003", "010", "011", "012", "013", "020", "021", - "022", "023", "030", "031", "032", "033", "100", "101", "102", "103", "110", "111", "112", - "113", "120", "121", "122", "123", "130", "131", "132", "133", "200", "201", "202", "203", - "210", "211", "212", "213", "220", "221", "222", "223", "230", "231", "232", "233", "300", - "301", "302", "303", "310", "311", "312", "313", "320", "321", "322", "323", "330", "331", - "332", "333" - }; - - private Mode mode = Mode.POST; - - public void setMode(Mode mode) { - this.mode = mode; - } - - public Mode getMode() { - return mode; - } - - /** {@inheritDoc} */ - @Override - protected void encode() { - - if (!content.matches("[0-9A-Za-z #]+")) { - throw OkapiInputException.invalidCharactersInInput(); - } - - String formatControlCode = "00"; - switch (mode) { - case POST: - switch (content.length()) { - case 8: - formatControlCode = "11"; - break; - case 13: - formatControlCode = "59"; - break; - case 16: - formatControlCode = "59"; - if (!content.matches("[0-9]+")) { - throw OkapiInputException.invalidCharactersInInput(); - } - break; - case 18: - formatControlCode = "62"; - break; - case 23: - formatControlCode = "62"; - if (!content.matches("[0-9]+")) { - throw OkapiInputException.invalidCharactersInInput(); - } - break; - default: - throw new OkapiInputException("Input length must be one of 8, 13, 16, 18 or 23"); - } - break; - case REPLY: - if (content.length() > 8) { - throw OkapiInputException.inputTooLong(); - } - formatControlCode = "45"; - break; - case ROUTE: - if (content.length() > 8) { - throw OkapiInputException.inputTooLong(); - } - formatControlCode = "87"; - break; - case REDIRECT: - if (content.length() > 8) { - throw OkapiInputException.inputTooLong(); - } - formatControlCode = "92"; - break; - } - - infoLine("FCC: " + formatControlCode); - - StringBuilder zeroPaddedInput = new StringBuilder(); - if (mode != Mode.POST) { - for (int i = content.length(); i < 8; i++) { - zeroPaddedInput.append('0'); - } - } - zeroPaddedInput.append(content); - - /* Verify that the first 8 characters are numbers */ - String deliveryPointId = zeroPaddedInput.substring(0, 8); - if (!deliveryPointId.matches("[0-9]+")) { - throw new OkapiInputException("Invalid characters in DPID"); - } - infoLine("DPID: " + deliveryPointId); - - /* Start */ - StringBuilder barStateValues = new StringBuilder(); - barStateValues.append("13"); - - /* Encode the FCC */ - for (int i = 0; i < 2; i++) { - barStateValues.append(N_ENCODING_TABLE[formatControlCode.charAt(i) - '0']); - } - - /* Delivery Point Identifier (DPID) */ - for (int i = 0; i < 8; i++) { - barStateValues.append(N_ENCODING_TABLE[deliveryPointId.charAt(i) - '0']); - } - - /* Customer Information */ - switch (zeroPaddedInput.length()) { - case 13: - case 18: - for (int i = 8; i < zeroPaddedInput.length(); i++) { - barStateValues.append(C_ENCODING_TABLE[positionOf(zeroPaddedInput.charAt(i), CHARACTER_SET)]); - } - break; - case 16: - case 23: - for (int i = 8; i < zeroPaddedInput.length(); i++) { - barStateValues.append(N_ENCODING_TABLE[positionOf(zeroPaddedInput.charAt(i), CHARACTER_SET)]); - } - break; - } - - /* Filler bar */ - switch (barStateValues.length()) { - case 22: - case 37: - case 52: - barStateValues.append('3'); - break; - } - - /* Reed Solomon error correction */ - barStateValues.append(calcReedSolomon(barStateValues)); - - /* Stop character */ - barStateValues.append("13"); - - infoLine("Total Length: " + barStateValues.length()); - info("Encoding: "); - for (int i = 0; i < barStateValues.length(); i++) { - switch (barStateValues.charAt(i)) { - case '1': - info('A'); - break; - case '2': - info('D'); - break; - case '0': - info('F'); - break; - case '3': - info('T'); - break; - } - } - infoLine(); - - readable = ""; - pattern = new String[] { barStateValues.toString() }; - row_count = 1; - row_height = new int[] { -1 }; - } - - private CharSequence calcReedSolomon(CharSequence oldBarStateValues) { - - /* Adds Reed-Solomon error correction to auspost */ - - int barStateCount; - int tripleValueCount = 0; - int[] tripleValue = new int[31]; - - for (barStateCount = 2; barStateCount < oldBarStateValues.length(); barStateCount += 3, tripleValueCount++) { - tripleValue[tripleValueCount] = barStateToDecimal(oldBarStateValues.charAt(barStateCount), 4) - + barStateToDecimal(oldBarStateValues.charAt(barStateCount + 1), 2) - + barStateToDecimal(oldBarStateValues.charAt(barStateCount + 2), 0); - } - - ReedSolomon rs = new ReedSolomon(); - rs.init_gf(0x43); - rs.init_code(4, 1); - rs.encode(tripleValueCount, tripleValue); - - StringBuilder newBarStateValues = new StringBuilder(); - for (barStateCount = 4; barStateCount > 0; barStateCount--) { - newBarStateValues.append(BAR_VALUE_TABLE[rs.getResult(barStateCount - 1)]); - } - - return newBarStateValues; - } - - private int barStateToDecimal(char oldBarStateValues, int shift) { - return (oldBarStateValues - '0') << shift; - } - - /** {@inheritDoc} */ - @Override - protected void plotSymbol() { - - int x = 0; - int w = 1; - int y = 0; - int h = 0; - - resetPlotElements(); - - for (int xBlock = 0; xBlock < pattern[0].length(); xBlock++) { - switch (pattern[0].charAt(xBlock)) { - case '1': - y = 0; - h = 5; - break; - case '2': - y = 3; - h = 5; - break; - case '0': - y = 0; - h = 8; - break; - case '3': - y = 3; - h = 2; - break; - } - Rectangle rect = new Rectangle(x, y, w, h); - rectangles.add(rect); - x += 2; - } - - symbol_width = ((pattern[0].length() - 1) * 2) + 1; // no whitespace needed after the final bar - symbol_height = 8; - } -} +/* + * Copyright 2014 Robin Stuart + * + * 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. + */ + +package uk.org.okapibarcode.backend; + +import static uk.org.okapibarcode.util.Arrays.positionOf; + +import uk.org.okapibarcode.graphics.Rectangle; + +/** + * Implements the Australia Post 4-State barcode. + * + * @author Robin Stuart + */ +public class AustraliaPost extends Symbol { + + /** + * The different Australia Post barcode variants available to encode. + */ + public enum Mode { + /** + *

Australia Post Standard Customer Barcode, Customer Barcode 2, or Customer Barcode 3 + * (37-bar, 52-bar and 67-bar symbols) depending on input data length. Valid data characters + * are 0-9, A-Z, a-z, space and hash (#). A Format Control Code (FCC) is added and should not + * be included in the input data. + * + *

Input data should include a 8-digit Deliver Point ID (DPID) optionally followed by + * customer information as shown below. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Input LengthRequired Input FormatSymbol LengthFCCEncoding Table
89999999937-bar11None
1399999999AAAAA52-bar59C
16999999999999999952-bar59N
1899999999AAAAAAAAAA67-bar62C
239999999999999999999999967-bar62N
+ */ + POST, + /** + * Reply Paid version of the Australia Post 4-State Barcode (FCC 45) which requires an 8-digit DPID input. + */ + REPLY, + /** + * Routing version of the Australia Post 4-State Barcode (FCC 87) which requires an 8-digit DPID input. + */ + ROUTE, + /** + * Redirection version of the Australia Post 4-State Barcode (FCC 92) which requires an 8-digit DPID input. + */ + REDIRECT + } + + private static final char[] CHARACTER_SET = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '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', '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', ' ', '#' + }; + + private static final String[] N_ENCODING_TABLE = { + "00", "01", "02", "10", "11", "12", "20", "21", "22", "30" + }; + + private static final String[] C_ENCODING_TABLE = { + "222", "300", "301", "302", "310", "311", "312", "320", "321", "322", + "000", "001", "002", "010", "011", "012", "020", "021", "022", "100", "101", "102", "110", + "111", "112", "120", "121", "122", "200", "201", "202", "210", "211", "212", "220", "221", + "023", "030", "031", "032", "033", "103", "113", "123", "130", "131", "132", "133", "203", + "213", "223", "230", "231", "232", "233", "303", "313", "323", "330", "331", "332", "333", + "003", "013" + }; + + private static final String[] BAR_VALUE_TABLE = { + "000", "001", "002", "003", "010", "011", "012", "013", "020", "021", + "022", "023", "030", "031", "032", "033", "100", "101", "102", "103", "110", "111", "112", + "113", "120", "121", "122", "123", "130", "131", "132", "133", "200", "201", "202", "203", + "210", "211", "212", "213", "220", "221", "222", "223", "230", "231", "232", "233", "300", + "301", "302", "303", "310", "311", "312", "313", "320", "321", "322", "323", "330", "331", + "332", "333" + }; + + private Mode mode = Mode.POST; + + public void setMode(Mode mode) { + this.mode = mode; + } + + public Mode getMode() { + return mode; + } + + /** {@inheritDoc} */ + @Override + protected void encode() { + + if (!content.matches("[0-9A-Za-z #]+")) { + throw OkapiInputException.invalidCharactersInInput(); + } + + String formatControlCode = "00"; + switch (mode) { + case POST: + switch (content.length()) { + case 8: + formatControlCode = "11"; + break; + case 13: + formatControlCode = "59"; + break; + case 16: + formatControlCode = "59"; + if (!content.matches("[0-9]+")) { + throw OkapiInputException.invalidCharactersInInput(); + } + break; + case 18: + formatControlCode = "62"; + break; + case 23: + formatControlCode = "62"; + if (!content.matches("[0-9]+")) { + throw OkapiInputException.invalidCharactersInInput(); + } + break; + default: + throw new OkapiInputException("Input length must be one of 8, 13, 16, 18 or 23"); + } + break; + case REPLY: + if (content.length() > 8) { + throw OkapiInputException.inputTooLong(); + } + formatControlCode = "45"; + break; + case ROUTE: + if (content.length() > 8) { + throw OkapiInputException.inputTooLong(); + } + formatControlCode = "87"; + break; + case REDIRECT: + if (content.length() > 8) { + throw OkapiInputException.inputTooLong(); + } + formatControlCode = "92"; + break; + } + + infoLine("FCC: " + formatControlCode); + + StringBuilder zeroPaddedInput = new StringBuilder(); + if (mode != Mode.POST) { + for (int i = content.length(); i < 8; i++) { + zeroPaddedInput.append('0'); + } + } + zeroPaddedInput.append(content); + + /* Verify that the first 8 characters are numbers */ + String deliveryPointId = zeroPaddedInput.substring(0, 8); + if (!deliveryPointId.matches("[0-9]+")) { + throw new OkapiInputException("Invalid characters in DPID"); + } + infoLine("DPID: " + deliveryPointId); + + /* Start */ + StringBuilder barStateValues = new StringBuilder(); + barStateValues.append("13"); + + /* Encode the FCC */ + for (int i = 0; i < 2; i++) { + barStateValues.append(N_ENCODING_TABLE[formatControlCode.charAt(i) - '0']); + } + + /* Delivery Point Identifier (DPID) */ + for (int i = 0; i < 8; i++) { + barStateValues.append(N_ENCODING_TABLE[deliveryPointId.charAt(i) - '0']); + } + + /* Customer Information */ + switch (zeroPaddedInput.length()) { + case 13: + case 18: + for (int i = 8; i < zeroPaddedInput.length(); i++) { + barStateValues.append(C_ENCODING_TABLE[positionOf(zeroPaddedInput.charAt(i), CHARACTER_SET)]); + } + break; + case 16: + case 23: + for (int i = 8; i < zeroPaddedInput.length(); i++) { + barStateValues.append(N_ENCODING_TABLE[positionOf(zeroPaddedInput.charAt(i), CHARACTER_SET)]); + } + break; + } + + /* Filler bar */ + switch (barStateValues.length()) { + case 22: + case 37: + case 52: + barStateValues.append('3'); + break; + } + + /* Reed Solomon error correction */ + barStateValues.append(calcReedSolomon(barStateValues)); + + /* Stop character */ + barStateValues.append("13"); + + infoLine("Total Length: " + barStateValues.length()); + info("Encoding: "); + for (int i = 0; i < barStateValues.length(); i++) { + switch (barStateValues.charAt(i)) { + case '1': + info('A'); + break; + case '2': + info('D'); + break; + case '0': + info('F'); + break; + case '3': + info('T'); + break; + } + } + infoLine(); + + readable = ""; + pattern = new String[] { barStateValues.toString() }; + row_count = 1; + row_height = new int[] { -1 }; + } + + private CharSequence calcReedSolomon(CharSequence oldBarStateValues) { + + /* Adds Reed-Solomon error correction to auspost */ + + int barStateCount; + int tripleValueCount = 0; + int[] tripleValue = new int[31]; + + for (barStateCount = 2; barStateCount < oldBarStateValues.length(); barStateCount += 3, tripleValueCount++) { + tripleValue[tripleValueCount] = barStateToDecimal(oldBarStateValues.charAt(barStateCount), 4) + + barStateToDecimal(oldBarStateValues.charAt(barStateCount + 1), 2) + + barStateToDecimal(oldBarStateValues.charAt(barStateCount + 2), 0); + } + + ReedSolomon rs = new ReedSolomon(); + rs.init_gf(0x43); + rs.init_code(4, 1); + rs.encode(tripleValueCount, tripleValue); + + StringBuilder newBarStateValues = new StringBuilder(); + for (barStateCount = 4; barStateCount > 0; barStateCount--) { + newBarStateValues.append(BAR_VALUE_TABLE[rs.getResult(barStateCount - 1)]); + } + + return newBarStateValues; + } + + private int barStateToDecimal(char oldBarStateValues, int shift) { + return (oldBarStateValues - '0') << shift; + } + + /** {@inheritDoc} */ + @Override + protected void plotSymbol() { + + int x = 0; + int w = 1; + int y = 0; + int h = 0; + + resetPlotElements(); + + for (int xBlock = 0; xBlock < pattern[0].length(); xBlock++) { + switch (pattern[0].charAt(xBlock)) { + case '1': + y = 0; + h = 5; + break; + case '2': + y = 3; + h = 5; + break; + case '0': + y = 0; + h = 8; + break; + case '3': + y = 3; + h = 2; + break; + } + Rectangle rect = new Rectangle(x, y, w, h); + rectangles.add(rect); + x += 2; + } + + symbol_width = ((pattern[0].length() - 1) * 2) + 1; // no whitespace needed after the final bar + symbol_height = 8; + } +} diff --git a/src/main/java/uk/org/okapibarcode/backend/AztecCode.java b/src/main/java/uk/org/okapibarcode/backend/AztecCode.java index 82195598..f275a0e5 100644 --- a/src/main/java/uk/org/okapibarcode/backend/AztecCode.java +++ b/src/main/java/uk/org/okapibarcode/backend/AztecCode.java @@ -1,1762 +1,1762 @@ -/* - * Copyright 2014 Robin Stuart - * - * 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. - */ - -package uk.org.okapibarcode.backend; - -import static java.nio.charset.StandardCharsets.US_ASCII; -import static uk.org.okapibarcode.util.Arrays.insertArray; - -/** - *

Implements Aztec Code bar code symbology According to ISO/IEC 24778:2008. - * - *

Aztec Code can encode 8-bit ISO 8859-1 (Latin-1) data (except 0x00 Null - * characters) up to a maximum length of approximately 3800 numeric characters, - * 3000 alphabetic characters or 1900 bytes of data in a two-dimensional matrix - * symbol. - * - * @author Robin Stuart - */ -public class AztecCode extends Symbol { - - /* 27 x 27 data grid */ - private static final int[] COMPACT_AZTEC_MAP = { - 609, 608, 411, 413, 415, 417, 419, 421, 423, 425, 427, 429, 431, 433, 435, 437, 439, 441, 443, 445, 447, 449, 451, 453, 455, 457, 459, - 607, 606, 410, 412, 414, 416, 418, 420, 422, 424, 426, 428, 430, 432, 434, 436, 438, 440, 442, 444, 446, 448, 450, 452, 454, 456, 458, - 605, 604, 409, 408, 243, 245, 247, 249, 251, 253, 255, 257, 259, 261, 263, 265, 267, 269, 271, 273, 275, 277, 279, 281, 283, 460, 461, - 603, 602, 407, 406, 242, 244, 246, 248, 250, 252, 254, 256, 258, 260, 262, 264, 266, 268, 270, 272, 274, 276, 278, 280, 282, 462, 463, - 601, 600, 405, 404, 241, 240, 107, 109, 111, 113, 115, 117, 119, 121, 123, 125, 127, 129, 131, 133, 135, 137, 139, 284, 285, 464, 465, - 599, 598, 403, 402, 239, 238, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 286, 287, 466, 467, - 597, 596, 401, 400, 237, 236, 105, 104, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 140, 141, 288, 289, 468, 469, - 595, 594, 399, 398, 235, 234, 103, 102, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 142, 143, 290, 291, 470, 471, - 593, 592, 397, 396, 233, 232, 101, 100, 1, 1, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 0, 1, 28, 29, 144, 145, 292, 293, 472, 473, - 591, 590, 395, 394, 231, 230, 99, 98, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 30, 31, 146, 147, 294, 295, 474, 475, - 589, 588, 393, 392, 229, 228, 97, 96, 2027, 1, 0, 0, 0, 0, 0, 0, 0, 1, 2007, 32, 33, 148, 149, 296, 297, 476, 477, - 587, 586, 391, 390, 227, 226, 95, 94, 2026, 1, 0, 1, 1, 1, 1, 1, 0, 1, 2008, 34, 35, 150, 151, 298, 299, 478, 479, - 585, 584, 389, 388, 225, 224, 93, 92, 2025, 1, 0, 1, 0, 0, 0, 1, 0, 1, 2009, 36, 37, 152, 153, 300, 301, 480, 481, - 583, 582, 387, 386, 223, 222, 91, 90, 2024, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2010, 38, 39, 154, 155, 302, 303, 482, 483, - 581, 580, 385, 384, 221, 220, 89, 88, 2023, 1, 0, 1, 0, 0, 0, 1, 0, 1, 2011, 40, 41, 156, 157, 304, 305, 484, 485, - 579, 578, 383, 382, 219, 218, 87, 86, 2022, 1, 0, 1, 1, 1, 1, 1, 0, 1, 2012, 42, 43, 158, 159, 306, 307, 486, 487, - 577, 576, 381, 380, 217, 216, 85, 84, 2021, 1, 0, 0, 0, 0, 0, 0, 0, 1, 2013, 44, 45, 160, 161, 308, 309, 488, 489, - 575, 574, 379, 378, 215, 214, 83, 82, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 46, 47, 162, 163, 310, 311, 490, 491, - 573, 572, 377, 376, 213, 212, 81, 80, 0, 0, 2020, 2019, 2018, 2017, 2016, 2015, 2014, 0, 0, 48, 49, 164, 165, 312, 313, 492, 493, - 571, 570, 375, 374, 211, 210, 78, 76, 74, 72, 70, 68, 66, 64, 62, 60, 58, 56, 54, 50, 51, 166, 167, 314, 315, 494, 495, - 569, 568, 373, 372, 209, 208, 79, 77, 75, 73, 71, 69, 67, 65, 63, 61, 59, 57, 55, 52, 53, 168, 169, 316, 317, 496, 497, - 567, 566, 371, 370, 206, 204, 202, 200, 198, 196, 194, 192, 190, 188, 186, 184, 182, 180, 178, 176, 174, 170, 171, 318, 319, 498, 499, - 565, 564, 369, 368, 207, 205, 203, 201, 199, 197, 195, 193, 191, 189, 187, 185, 183, 181, 179, 177, 175, 172, 173, 320, 321, 500, 501, - 563, 562, 366, 364, 362, 360, 358, 356, 354, 352, 350, 348, 346, 344, 342, 340, 338, 336, 334, 332, 330, 328, 326, 322, 323, 502, 503, - 561, 560, 367, 365, 363, 361, 359, 357, 355, 353, 351, 349, 347, 345, 343, 341, 339, 337, 335, 333, 331, 329, 327, 324, 325, 504, 505, - 558, 556, 554, 552, 550, 548, 546, 544, 542, 540, 538, 536, 534, 532, 530, 528, 526, 524, 522, 520, 518, 516, 514, 512, 510, 506, 507, - 559, 557, 555, 553, 551, 549, 547, 545, 543, 541, 539, 537, 535, 533, 531, 529, 527, 525, 523, 521, 519, 517, 515, 513, 511, 508, 509 - }; - - private static final int[][] AZTEC_MAP = new int[151][151]; - - /* From Table 2: - * - * 1 = upper - * 2 = lower - * 4 = mixed - * 8 = punctuation - * 16 = digits - * 32 = binary - * - * Values can be OR'ed, so e.g. 12 = 4 | 8, and 23 = 1 | 2 | 4 | 16 - */ - private static final int[] AZTEC_CODE_SET = { - 32, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 12, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 4, 4, 4, 4, 4, 23, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 24, 8, 24, 8, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 8, 8, - 8, 8, 8, 8, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 8, 4, 8, 4, 4, 4, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 8, 4, 8, 4, 4 - }; - - /* From Table 2 */ - private static final int[] AZTEC_SYMBOL_CHAR = { - 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 300, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 15, 16, 17, 18, 19, 1, 6, 7, 8, 9, 10, 11, 12, - 13, 14, 15, 16, 301, 18, 302, 20, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 21, 22, - 23, 24, 25, 26, 20, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, - 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 27, 21, 28, 22, 23, 24, 2, 3, 4, - 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 29, 25, 30, 26, 27 - }; - - /* Problem characters are: - * 300: Carriage Return (ASCII 13) - * 301: Comma (ASCII 44) - * 302: Full Stop (ASCII 46) - */ - private static final String[] PENTBIT = { - "00000", "00001", "00010", "00011", "00100", "00101", "00110", "00111", "01000", "01001", - "01010", "01011", "01100", "01101", "01110", "01111", "10000", "10001", "10010", "10011", "10100", "10101", - "10110", "10111", "11000", "11001", "11010", "11011", "11100", "11101", "11110", "11111" - }; - - private static final String[] QUADBIT = { - "0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", - "1010", "1011", "1100", "1101", "1110", "1111" - }; - - private static final String[] TRIBIT = { - "000", "001", "010", "011", "100", "101", "110", "111" - }; - - /* Codewords per symbol */ - private static final int[] AZTEC_SIZES = { - 21, 48, 60, 88, 120, 156, 196, 240, 230, 272, 316, 364, 416, 470, 528, 588, 652, 720, 790, - 864, 940, 1020, 920, 992, 1066, 1144, 1224, 1306, 1392, 1480, 1570, 1664 - }; - - private static final int[] AZTEC_COMPACT_SIZES = { - 17, 40, 51, 76 - }; - - /* Data bits per symbol maximum with 10% error correction */ - private static final int[] AZTEC_10_DATA_SIZES = { - 96, 246, 408, 616, 840, 1104, 1392, 1704, 2040, 2420, 2820, 3250, 3720, 4200, 4730, - 5270, 5840, 6450, 7080, 7750, 8430, 9150, 9900, 10680, 11484, 12324, 13188, 14076, - 15000, 15948, 16920, 17940 - }; - - /* Data bits per symbol maximum with 23% error correction */ - private static final int[] AZTEC_23_DATA_SIZES = { - 84, 204, 352, 520, 720, 944, 1184, 1456, 1750, 2070, 2410, 2780, 3180, 3590, 4040, - 4500, 5000, 5520, 6060, 6630, 7210, 7830, 8472, 9132, 9816, 10536, 11280, 12036, - 12828, 13644, 14472, 15348 - }; - - /* Data bits per symbol maximum with 36% error correction */ - private static final int[] AZTEC_36_DATA_SIZES = { - 66, 168, 288, 432, 592, 776, 984, 1208, 1450, 1720, 2000, 2300, 2640, 2980, 3350, - 3740, 4150, 4580, 5030, 5500, 5990, 6500, 7032, 7584, 8160, 8760, 9372, 9996, 10656, - 11340, 12024, 12744 - }; - - /* Data bits per symbol maximum with 50% error correction */ - private static final int[] AZTEC_50_DATA_SIZES = { - 48, 126, 216, 328, 456, 600, 760, 936, 1120, 1330, 1550, 1790, 2050, 2320, 2610, - 2910, 3230, 3570, 3920, 4290, 4670, 5070, 5484, 5916, 6360, 6828, 7308, 7800, 8316, - 8844, 9384, 9948 - }; - - private static final int[] AZTEC_COMPACT_10_DATA_SIZES = { - 78, 198, 336, 520 - }; - private static final int[] AZTEC_COMPACT_23_DATA_SIZES = { - 66, 168, 288, 440 - }; - private static final int[] AZTEC_COMPACT_36_DATA_SIZES = { - 48, 138, 232, 360 - }; - private static final int[] AZTEC_COMPACT_50_DATA_SIZES = { - 36, 102, 176, 280 - }; - - private static final int[] AZTEC_OFFSET = { - 66, 64, 62, 60, 57, 55, 53, 51, 49, 47, 45, 42, 40, 38, 36, 34, 32, 30, 28, 25, 23, 21, - 19, 17, 15, 13, 10, 8, 6, 4, 2, 0 - }; - - private static final int[] AZTEC_COMPACT_OFFSET = { - 6, 4, 2, 0 - }; - - /* Initialize AZTEC_MAP */ - static { - - int layer, start, length, n, i; - int x, y; - - for (x = 0; x < 151; x++) { - for (y = 0; y < 151; y++) { - AZTEC_MAP[x][y] = 0; - } - } - - for (layer = 1; layer < 33; layer++) { - start = (112 * (layer - 1)) + (16 * (layer - 1) * (layer - 1)) + 2; - length = 28 + ((layer - 1) * 4) + (layer * 4); - /* Top */ - i = 0; - x = 64 - ((layer - 1) * 2); - y = 63 - ((layer - 1) * 2); - for (n = start; n < (start + length); n += 2) { - AZTEC_MAP[avoidReferenceGrid(x + i)][avoidReferenceGrid(y)] = n; - AZTEC_MAP[avoidReferenceGrid(x + i)][avoidReferenceGrid(y - 1)] = n + 1; - i++; - } - /* Right */ - i = 0; - x = 78 + ((layer - 1) * 2); - y = 64 - ((layer - 1) * 2); - for (n = start + length; n < (start + (length * 2)); n += 2) { - AZTEC_MAP[avoidReferenceGrid(x)][avoidReferenceGrid(y + i)] = n; - AZTEC_MAP[avoidReferenceGrid(x + 1)][avoidReferenceGrid(y + i)] = n + 1; - i++; - } - /* Bottom */ - i = 0; - x = 77 + ((layer - 1) * 2); - y = 78 + ((layer - 1) * 2); - for (n = start + (length * 2); n < (start + (length * 3)); n += 2) { - AZTEC_MAP[avoidReferenceGrid(x - i)][avoidReferenceGrid(y)] = n; - AZTEC_MAP[avoidReferenceGrid(x - i)][avoidReferenceGrid(y + 1)] = n + 1; - i++; - } - /* Left */ - i = 0; - x = 63 - ((layer - 1) * 2); - y = 77 + ((layer - 1) * 2); - for (n = start + (length * 3); n < (start + (length * 4)); n += 2) { - AZTEC_MAP[avoidReferenceGrid(x)][avoidReferenceGrid(y - i)] = n; - AZTEC_MAP[avoidReferenceGrid(x - 1)][avoidReferenceGrid(y - i)] = n + 1; - i++; - } - } - - /* Central finder pattern */ - for (y = 69; y <= 81; y++) { - for (x = 69; x <= 81; x++) { - AZTEC_MAP[x][y] = 1; - } - } - for (y = 70; y <= 80; y++) { - for (x = 70; x <= 80; x++) { - AZTEC_MAP[x][y] = 0; - } - } - for (y = 71; y <= 79; y++) { - for (x = 71; x <= 79; x++) { - AZTEC_MAP[x][y] = 1; - } - } - for (y = 72; y <= 78; y++) { - for (x = 72; x <= 78; x++) { - AZTEC_MAP[x][y] = 0; - } - } - for (y = 73; y <= 77; y++) { - for (x = 73; x <= 77; x++) { - AZTEC_MAP[x][y] = 1; - } - } - for (y = 74; y <= 76; y++) { - for (x = 74; x <= 76; x++) { - AZTEC_MAP[x][y] = 0; - } - } - - /* Guide bars */ - for (y = 11; y < 151; y += 16) { - for (x = 1; x < 151; x += 2) { - AZTEC_MAP[x][y] = 1; - AZTEC_MAP[y][x] = 1; - } - } - - /* Descriptor */ - for (i = 0; i < 10; i++) { /* Top */ - AZTEC_MAP[avoidReferenceGrid(66 + i)][avoidReferenceGrid(64)] = 20000 + i; - } - for (i = 0; i < 10; i++) { /* Right */ - AZTEC_MAP[avoidReferenceGrid(77)][avoidReferenceGrid(66 + i)] = 20010 + i; - } - for (i = 0; i < 10; i++) { /* Bottom */ - AZTEC_MAP[avoidReferenceGrid(75 - i)][avoidReferenceGrid(77)] = 20020 + i; - } - for (i = 0; i < 10; i++) { /* Left */ - AZTEC_MAP[avoidReferenceGrid(64)][avoidReferenceGrid(75 - i)] = 20030 + i; - } - - /* Orientation */ - AZTEC_MAP[avoidReferenceGrid(64)][avoidReferenceGrid(64)] = 1; - AZTEC_MAP[avoidReferenceGrid(65)][avoidReferenceGrid(64)] = 1; - AZTEC_MAP[avoidReferenceGrid(64)][avoidReferenceGrid(65)] = 1; - AZTEC_MAP[avoidReferenceGrid(77)][avoidReferenceGrid(64)] = 1; - AZTEC_MAP[avoidReferenceGrid(77)][avoidReferenceGrid(65)] = 1; - AZTEC_MAP[avoidReferenceGrid(77)][avoidReferenceGrid(76)] = 1; - } - - private static int avoidReferenceGrid(int input) { - int output = input; - if (output > 10) { - output++; - } - if (output > 26) { - output++; - } - if (output > 42) { - output++; - } - if (output > 58) { - output++; - } - if (output > 74) { - output++; - } - if (output > 90) { - output++; - } - if (output > 106) { - output++; - } - if (output > 122) { - output++; - } - if (output > 138) { - output++; - } - return output; - } - - private int preferredSize = 0; - private int preferredEccLevel = 2; - private String structuredAppendMessageId; - private int structuredAppendPosition = 1; - private int structuredAppendTotal = 1; - - /** - *

Sets a preferred symbol size. This value may be ignored if data string is - * too large to fit in the specified symbol size. Values correspond to symbol - * sizes as shown in the following table: - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
InputSymbol SizeInputSymbol Size
1 15 x 15 19 79 x 79
2 19 x 19 20 83 x 83
3 23 x 23 21 87 x 87
4 27 x 27 22 91 x 91
5 19 x 19 23 95 x 95
6 23 x 23 24 101 x 101
7 27 x 27 25 105 x 105
8 31 x 31 26 109 x 109
9 37 x 37 27 113 x 113
10 41 x 41 28 117 x 117
11 45 x 45 29 121 x 121
12 49 x 49 30 125 x 125
13 53 x 53 31 131 x 131
14 57 x 57 32 135 x 135
15 61 x 61 33 139 x 139
16 67 x 67 34 143 x 143
17 71 x 71 35 147 x 147
18 75 x 75 36 151 x 151
- * - *

Note that sizes 1 to 4 are the "compact" Aztec Code symbols; sizes 5 to 36 - * are the "full-range" Aztec Code symbols. - * - * @param size an integer in the range 1 - 36 - */ - public void setPreferredSize(int size) { - if (size < 1 || size > 36) { - throw new IllegalArgumentException("Invalid size: " + size); - } - preferredSize = size; - } - - /** - * Returns the preferred symbol size. - * - * @return the preferred symbol size - */ - public int getPreferredSize() { - return preferredSize; - } - - /** - * Sets the preferred minimum amount of symbol space dedicated to error - * correction. This value will be ignored if a symbol size has been set by - * setPreferredSize. Valid options are: - * - * - * - * - * - * - * - * - * - *
ModeError Correction Capacity
1 > 10% + 3 codewords
2 > 23% + 3 codewords
3 > 36% + 3 codewords
4 > 50% + 3 codewords
- * - * @param eccLevel an integer in the range 1 - 4 - */ - public void setPreferredEccLevel(int eccLevel) { - if (eccLevel < 1 || eccLevel > 4) { - throw new IllegalArgumentException("Invalid ECC level: " + eccLevel); - } - preferredEccLevel = eccLevel; - } - - /** - * Returns the preferred error correction level. - * - * @return the preferred error correction level - */ - public int getPreferredEccLevel() { - return preferredEccLevel; - } - - /** - * If this Aztec Code symbol is part of a series of Aztec Code symbols appended in a structured - * format, this method sets the position of this symbol in the series. Valid values are 1 through - * 26 inclusive. - * - * @param position the position of this Aztec Code symbol in the structured append series - */ - public void setStructuredAppendPosition(int position) { - if (position < 1 || position > 26) { - throw new IllegalArgumentException("Invalid Aztec Code structured append position: " + position); - } - this.structuredAppendPosition = position; - } - - /** - * Returns the position of this Aztec Code symbol in a series of symbols using structured append. - * If this symbol is not part of such a series, this method will return 1. - * - * @return the position of this Aztec Code symbol in a series of symbols using structured append - */ - public int getStructuredAppendPosition() { - return structuredAppendPosition; - } - - /** - * If this Aztec Code symbol is part of a series of Aztec Code symbols appended in a structured - * format, this method sets the total number of symbols in the series. Valid values are - * 1 through 26 inclusive. A value of 1 indicates that this symbol is not part of a structured - * append series. - * - * @param total the total number of Aztec Code symbols in the structured append series - */ - public void setStructuredAppendTotal(int total) { - if (total < 1 || total > 26) { - throw new IllegalArgumentException("Invalid Aztec Code structured append total: " + total); - } - this.structuredAppendTotal = total; - } - - /** - * Returns the size of the series of Aztec Code symbols using structured append that this symbol - * is part of. If this symbol is not part of a structured append series, this method will return - * 1. - * - * @return size of the series that this symbol is part of - */ - public int getStructuredAppendTotal() { - return structuredAppendTotal; - } - - /** - * If this Aztec Code symbol is part of a series of Aztec Code symbols appended in a structured format, - * this method sets the unique message ID for the series. Values may not contain spaces and must contain - * only printable ASCII characters. Message IDs are optional. - * - * @param messageId the unique message ID for the series that this symbol is part of - */ - public void setStructuredAppendMessageId(String messageId) { - if (messageId != null && !messageId.matches("^[\\x21-\\x7F]+$")) { - throw new IllegalArgumentException("Invalid Aztec Code structured append message ID: " + messageId); - } - this.structuredAppendMessageId = messageId; - } - - /** - * Returns the unique message ID of the series of Aztec Code symbols using structured append that this - * symbol is part of. If this symbol is not part of a structured append series, this method will return - * null. - * - * @return the unique message ID for the series that this symbol is part of - */ - public String getStructuredAppendMessageId() { - return structuredAppendMessageId; - } - - @Override - protected boolean gs1Supported() { - return true; - } - - @Override - protected boolean eciSupported() { - return true; - } - - @Override - protected void encode() { - - int layers; - boolean compact; - StringBuilder adjustedString; - - eciProcess(); // Get ECI mode - - /* Optional structured append (Section 8 of spec) */ - /* ML + UL start flag handled later, not part of data */ - if (structuredAppendTotal != 1) { - StringBuilder prefix = new StringBuilder(); - if (structuredAppendMessageId != null) { - prefix.append(' ').append(structuredAppendMessageId).append(' '); - } - prefix.append((char) (structuredAppendPosition + 64)); // 1-26 as A-Z - prefix.append((char) (structuredAppendTotal + 64)); // 1-26 as A-Z - int[] prefixArray = toBytes(prefix.toString(), US_ASCII); - inputData = insertArray(inputData, 0, prefixArray); - } - - String binaryString = generateAztecBinary(); - int dataLength = binaryString.length(); - - if (preferredSize == 0) { - - /* The size of the symbol can be determined by Okapi */ - - int dataMaxSize = 0; - int compLoop = (readerInit ? 1 : 4); - - do { - /* Decide what size symbol to use - the smallest that fits the data */ - - int[] dataSizes; - int[] compactDataSizes; - - switch (preferredEccLevel) { - /* For each level of error correction work out the smallest symbol which the data will fit in */ - case 1: - dataSizes = AZTEC_10_DATA_SIZES; - compactDataSizes = AZTEC_COMPACT_10_DATA_SIZES; - break; - case 2: - dataSizes = AZTEC_23_DATA_SIZES; - compactDataSizes = AZTEC_COMPACT_23_DATA_SIZES; - break; - case 3: - dataSizes = AZTEC_36_DATA_SIZES; - compactDataSizes = AZTEC_COMPACT_36_DATA_SIZES; - break; - case 4: - dataSizes = AZTEC_50_DATA_SIZES; - compactDataSizes = AZTEC_COMPACT_50_DATA_SIZES; - break; - default: - throw new OkapiInputException("Unrecognized ECC level: " + preferredEccLevel); - } - - layers = 0; - compact = false; - - for (int i = 32; i > 0; i--) { - if (dataLength < dataSizes[i - 1]) { - layers = i; - compact = false; - dataMaxSize = dataSizes[i - 1]; - } - } - - for (int i = compLoop; i > 0; i--) { - if (dataLength < compactDataSizes[i - 1]) { - layers = i; - compact = true; - dataMaxSize = compactDataSizes[i - 1]; - } - } - - if (layers == 0) { - /* Couldn't find a symbol which fits the data */ - throw new OkapiInputException("Input too long (too many bits for selected ECC)"); - } - - adjustedString = adjustBinaryString(binaryString, compact, layers); - dataLength = adjustedString.length(); - - } while (dataLength > dataMaxSize); - /* This loop will only repeat on the rare occasions when the rule about not having all 1s or all 0s - means that the binary string has had to be lengthened beyond the maximum number of bits that can - be encoded in a symbol of the selected size */ - - } else { - - /* The size of the symbol has been specified by the user */ - - if (preferredSize >= 1 && preferredSize <= 4) { - compact = true; - layers = preferredSize; - } else { - compact = false; - layers = preferredSize - 4; - } - - adjustedString = adjustBinaryString(binaryString, compact, layers); - - /* Check if the data actually fits into the selected symbol size */ - int codewordSize = getCodewordSize(layers); - int[] sizes = (compact ? AZTEC_COMPACT_SIZES : AZTEC_SIZES); - int dataMaxSize = codewordSize * (sizes[layers - 1] - 3); - if (adjustedString.length() > dataMaxSize) { - throw new OkapiInputException("Data too long for specified Aztec Code symbol size"); - } - } - - if (readerInit && compact && layers > 1) { - throw new OkapiInputException("Symbol is too large for reader initialization"); - } - - if (readerInit && layers > 22) { - throw new OkapiInputException("Symbol is too large for reader initialization"); - } - - int codewordSize = getCodewordSize(layers); - int dataBlocks = adjustedString.length() / codewordSize; - - int eccBlocks; - if (compact) { - eccBlocks = AZTEC_COMPACT_SIZES[layers - 1] - dataBlocks; - } else { - eccBlocks = AZTEC_SIZES[layers - 1] - dataBlocks; - } - - infoLine("Compact Mode: " + compact); - infoLine("Layers: " + layers); - infoLine("Codeword Length: " + codewordSize + " bits"); - infoLine("Data Codewords: " + dataBlocks); - infoLine("ECC Codewords: " + eccBlocks); - - /* Add ECC data to the adjusted string */ - addErrorCorrection(adjustedString, codewordSize, dataBlocks, eccBlocks); - - /* Invert the data so that actual data is on the outside and reed-solomon on the inside */ - for (int i = 0; i < adjustedString.length() / 2; i++) { - int mirror = adjustedString.length() - i - 1; - char c = adjustedString.charAt(i); - adjustedString.setCharAt(i, adjustedString.charAt(mirror)); - adjustedString.setCharAt(mirror, c); - } - - /* Create the descriptor / mode message */ - String descriptor = createDescriptor(compact, layers, dataBlocks); - - /* Plot all of the data into the symbol in pre-defined spiral pattern */ - if (compact) { - - readable = ""; - row_count = 27 - (2 * AZTEC_COMPACT_OFFSET[layers - 1]); - row_height = new int[row_count]; - row_height[0] = -1; - pattern = new String[row_count]; - for (int y = AZTEC_COMPACT_OFFSET[layers - 1]; y < (27 - AZTEC_COMPACT_OFFSET[layers - 1]); y++) { - StringBuilder bin = new StringBuilder(27); - for (int x = AZTEC_COMPACT_OFFSET[layers - 1]; x < (27 - AZTEC_COMPACT_OFFSET[layers - 1]); x++) { - int j = COMPACT_AZTEC_MAP[(y * 27) + x]; - if (j == 0) { - bin.append('0'); - } - if (j == 1) { - bin.append('1'); - } - if (j >= 2) { - if (j - 2 < adjustedString.length()) { - bin.append(adjustedString.charAt(j - 2)); - } else { - if (j >= 2000) { - bin.append(descriptor.charAt(j - 2000)); - } else { - bin.append('0'); - } - } - } - } - row_height[y - AZTEC_COMPACT_OFFSET[layers - 1]] = 1; - pattern[y - AZTEC_COMPACT_OFFSET[layers - 1]] = bin2pat(bin); - } - - } else { - - readable = ""; - row_count = 151 - (2 * AZTEC_OFFSET[layers - 1]); - row_height = new int[row_count]; - row_height[0] = -1; - pattern = new String[row_count]; - for (int y = AZTEC_OFFSET[layers - 1]; y < (151 - AZTEC_OFFSET[layers - 1]); y++) { - StringBuilder bin = new StringBuilder(151); - for (int x = AZTEC_OFFSET[layers - 1]; x < (151 - AZTEC_OFFSET[layers - 1]); x++) { - int j = AZTEC_MAP[x][y]; - if (j == 1) { - bin.append('1'); - } - if (j == 0) { - bin.append('0'); - } - if (j >= 2) { - if (j - 2 < adjustedString.length()) { - bin.append(adjustedString.charAt(j - 2)); - } else { - if (j >= 20000) { - bin.append(descriptor.charAt(j - 20000)); - } else { - bin.append('0'); - } - } - } - } - row_height[y - AZTEC_OFFSET[layers - 1]] = 1; - pattern[y - AZTEC_OFFSET[layers - 1]] = bin2pat(bin); - } - } - } - - private String generateAztecBinary() { - - /* Encode input data into a binary string */ - int i, j, k, bytes; - int curtable, newtable, lasttable, chartype, maplength, blocks; - int[] charmap = new int[2 * inputData.length]; - int[] typemap = new int[2 * inputData.length]; - int[] blockType = new int[inputData.length + 1]; - int[] blockLength = new int[inputData.length + 1]; - - /* Lookup input string in encoding table */ - maplength = 0; - - /* Add FNC1 to beginning of GS1 messages */ - if (inputDataType == DataType.GS1) { - charmap[maplength] = 0; // FLG - typemap[maplength++] = 8; // PUNC - charmap[maplength] = 400; // (0) - typemap[maplength++] = 8; // PUNC - } - - if (eciMode != 3) { - int flagNumber; - - charmap[maplength] = 0; // FLG - typemap[maplength++] = 8; // PUNC - - flagNumber = 6; - - if (eciMode < 100000) { - flagNumber = 5; - } - - if (eciMode < 10000) { - flagNumber = 4; - } - - if (eciMode < 1000) { - flagNumber = 3; - } - - if (eciMode < 100) { - flagNumber = 2; - } - - if (eciMode < 10) { - flagNumber = 1; - } - - charmap[maplength] = 400 + flagNumber; - typemap[maplength++] = 8; // PUNC - } - - for (i = 0; i < inputData.length; i++) { - if (inputData[i] == FNC1) { - /* FNC1 represented by FLG(0) */ - charmap[maplength] = 0; // FLG - typemap[maplength++] = 8; // PUNC - charmap[maplength] = 400; // (0) - typemap[maplength++] = 8; // PUNC - } else { - if ((inputData[i] > 0x7F) || (inputData[i] == 0x00)) { - charmap[maplength] = inputData[i]; - typemap[maplength++] = 32; //BINARY - } else { - charmap[maplength] = AZTEC_SYMBOL_CHAR[inputData[i]]; - typemap[maplength++] = AZTEC_CODE_SET[inputData[i]]; - } - } - } - - /* Look for double character encoding possibilities */ - for (i = 0; i < (maplength - 1); i++) { - if (((charmap[i] == 300) && (charmap[i + 1] == 11)) && ((typemap[i] == 12) && (typemap[i + 1] == 4))) { - /* CR LF combination */ - charmap[i] = 2; - typemap[i] = 8; // PUNC - if ((i + 1) != maplength) { - for (j = i + 1; j < maplength; j++) { - charmap[j] = charmap[j + 1]; - typemap[j] = typemap[j + 1]; - } - } - maplength--; - } - - if (((charmap[i] == 302) && (charmap[i + 1] == 1)) && ((typemap[i] == 24) && (typemap[i + 1] == 23))) { - /* . SP combination */ - charmap[i] = 3; - typemap[i] = 8; // PUNC; - if ((i + 1) != maplength) { - for (j = i + 1; j < maplength; j++) { - charmap[j] = charmap[j + 1]; - typemap[j] = typemap[j + 1]; - } - } - maplength--; - } - - if (((charmap[i] == 301) && (charmap[i + 1] == 1)) && ((typemap[i] == 24) && (typemap[i + 1] == 23))) { - /* , SP combination */ - charmap[i] = 4; - typemap[i] = 8; //PUNC; - if ((i + 1) != maplength) { - for (j = i + 1; j < maplength; j++) { - charmap[j] = charmap[j + 1]; - typemap[j] = typemap[j + 1]; - } - } - maplength--; - } - - if (((charmap[i] == 21) && (charmap[i + 1] == 1)) && ((typemap[i] == 8) && (typemap[i + 1] == 23))) { - /* : SP combination */ - charmap[i] = 5; - typemap[i] = 8; //PUNC; - if ((i + 1) != maplength) { - for (j = i + 1; j < maplength; j++) { - charmap[j] = charmap[j + 1]; - typemap[j] = typemap[j + 1]; - } - } - maplength--; - } - } - - /* look for blocks of characters which use the same table */ - blocks = 0; - for (i = 0; i < maplength; i++) { - if (i > 0 && typemap[i] == typemap[i - 1]) { - blockLength[blocks - 1]++; - } else { - blocks++; - blockType[blocks - 1] = typemap[i]; - blockLength[blocks - 1] = 1; - } - } - - if ((blockType[0] & 1) != 0) { - blockType[0] = 1; - } - if ((blockType[0] & 2) != 0) { - blockType[0] = 2; - } - if ((blockType[0] & 4) != 0) { - blockType[0] = 4; - } - if ((blockType[0] & 8) != 0) { - blockType[0] = 8; - } - - if (blocks > 1) { - - /* look for adjacent blocks which can use the same table (left to right search) */ - for (i = 1; i < blocks; i++) { - if ((blockType[i] & blockType[i - 1]) != 0) { - blockType[i] = (blockType[i] & blockType[i - 1]); - } - } - - if ((blockType[blocks - 1] & 1) != 0) { - blockType[blocks - 1] = 1; - } - if ((blockType[blocks - 1] & 2) != 0) { - blockType[blocks - 1] = 2; - } - if ((blockType[blocks - 1] & 4) != 0) { - blockType[blocks - 1] = 4; - } - if ((blockType[blocks - 1] & 8) != 0) { - blockType[blocks - 1] = 8; - } - - /* look for adjacent blocks which can use the same table (right to left search) */ - for (i = blocks - 2; i > 0; i--) { - if ((blockType[i] & blockType[i + 1]) != 0) { - blockType[i] = (blockType[i] & blockType[i + 1]); - } - } - - /* determine the encoding table for characters which do not fit with adjacent blocks */ - for (i = 1; i < blocks; i++) { - if ((blockType[i] & 8) != 0) { - blockType[i] = 8; - } - if ((blockType[i] & 4) != 0) { - blockType[i] = 4; - } - if ((blockType[i] & 2) != 0) { - blockType[i] = 2; - } - if ((blockType[i] & 1) != 0) { - blockType[i] = 1; - } - } - - /* if less than 4 characters are preceded and followed by binary blocks - then it is more efficient to also encode these in binary - */ - -// for (i = 1; i < blocks - 1; i++) { -// if ((blockType[i - 1] == 32) && (blockLength[i] < 4)) { -// int nonBinaryLength = blockLength[i]; -// for (int l = i; ((l < blocks) && (blockType[l] != 32)); l++) { -// nonBinaryLength += blockLength[l]; -// } -// if (nonBinaryLength < 4) { -// blockType[i] = 32; -// } -// } -// } - - /* Combine blocks of the same type */ - i = 0; - do { - if (blockType[i] == blockType[i + 1]) { - blockLength[i] += blockLength[i + 1]; - for (j = i + 1; j < blocks - 1; j++) { - blockType[j] = blockType[j + 1]; - blockLength[j] = blockLength[j + 1]; - } - blocks--; - } else { - i++; - } - } while (i < blocks - 1); - } - - /* Put the adjusted block data back into typemap */ - j = 0; - for (i = 0; i < blocks; i++) { - if ((blockLength[i] < 3) && (blockType[i] != 32)) { /* Shift character(s) needed */ - - for (k = 0; k < blockLength[i]; k++) { - typemap[j + k] = blockType[i] + 64; - } - } else { /* Latch character (or byte mode) needed */ - - for (k = 0; k < blockLength[i]; k++) { - typemap[j + k] = blockType[i]; - } - } - j += blockLength[i]; - } - - /* Don't shift an initial capital letter */ - if (maplength > 0 && typemap[0] == 65) { - typemap[0] = 1; - } - - /* Problem characters (those that appear in different tables with different values) can now be resolved into their tables */ - for (i = 0; i < maplength; i++) { - if ((charmap[i] >= 300) && (charmap[i] < 400)) { - curtable = typemap[i]; - if (curtable > 64) { - curtable -= 64; - } - switch (charmap[i]) { - case 300: - /* Carriage Return */ - switch (curtable) { - case 8: - charmap[i] = 1; - break; // PUNC - case 4: - charmap[i] = 14; - break; // PUNC - } - break; - case 301: - /* Comma */ - switch (curtable) { - case 8: - charmap[i] = 17; - break; // PUNC - case 16: - charmap[i] = 12; - break; // DIGIT - } - break; - case 302: - /* Full Stop */ - switch (curtable) { - case 8: - charmap[i] = 19; - break; // PUNC - case 16: - charmap[i] = 13; - break; // DIGIT - } - break; - } - } - } - - StringBuilder binaryString = new StringBuilder(); - info("Encoding: "); - curtable = 1; /* start with 1 table */ - lasttable = 1; - - /* Optional structured append start flag (Section 8 of spec) */ - if (structuredAppendTotal != 1) { - binaryString.append(PENTBIT[29]); - info("ML "); - binaryString.append(PENTBIT[29]); - info("UL "); - } - - for (i = 0; i < maplength; i++) { - newtable = curtable; - if ((typemap[i] != curtable) && (charmap[i] < 400)) { - /* Change table */ - if (curtable == 32) { - /* If ending binary mode the current table is the same as when entering binary mode */ - curtable = lasttable; - newtable = lasttable; - } - if (typemap[i] > 64) { - /* Shift character */ - switch (typemap[i]) { - case (64 + 1): - /* To UPPER */ - switch (curtable) { - case 2: - /* US */ - binaryString.append(PENTBIT[28]); - info("US "); - break; - case 4: - /* UL */ - binaryString.append(PENTBIT[29]); - info("UL "); - newtable = 1; - break; - case 8: - /* UL */ - binaryString.append(PENTBIT[31]); - info("UL "); - newtable = 1; - break; - case 16: - /* US */ - binaryString.append(QUADBIT[15]); - info("US "); - break; - } - break; - case (64 + 2): - /* To LOWER */ - switch (curtable) { - case 1: - /* LL */ - binaryString.append(PENTBIT[28]); - info("LL "); - newtable = 2; - break; - case 4: - /* LL */ - binaryString.append(PENTBIT[28]); - info("LL "); - newtable = 2; - break; - case 8: - /* UL LL */ - binaryString.append(PENTBIT[31]); - info("UL "); - binaryString.append(PENTBIT[28]); - info("LL "); - newtable = 2; - break; - case 16: - /* UL LL */ - binaryString.append(QUADBIT[14]); - info("UL "); - binaryString.append(PENTBIT[28]); - info("LL "); - newtable = 2; - break; - } - break; - case (64 + 4): - /* To MIXED */ - switch (curtable) { - case 1: - /* ML */ - binaryString.append(PENTBIT[29]); - info("ML "); - newtable = 4; - break; - case 2: - /* ML */ - binaryString.append(PENTBIT[29]); - info("ML "); - newtable = 4; - break; - case 8: - /* UL ML */ - binaryString.append(PENTBIT[31]); - info("UL "); - binaryString.append(PENTBIT[29]); - info("ML "); - newtable = 4; - break; - case 16: - /* UL ML */ - binaryString.append(QUADBIT[14]); - info("UL "); - binaryString.append(PENTBIT[29]); - info("ML "); - newtable = 4; - break; - } - break; - case (64 + 8): - /* To PUNC */ - switch (curtable) { - case 1: - /* PS */ - binaryString.append(PENTBIT[0]); - info("PS "); - break; - case 2: - /* PS */ - binaryString.append(PENTBIT[0]); - info("PS "); - break; - case 4: - /* PS */ - binaryString.append(PENTBIT[0]); - info("PS "); - break; - case 16: - /* PS */ - binaryString.append(QUADBIT[0]); - info("PS "); - break; - } - break; - case (64 + 16): - /* To DIGIT */ - switch (curtable) { - case 1: - /* DL */ - binaryString.append(PENTBIT[30]); - info("DL "); - newtable = 16; - break; - case 2: - /* DL */ - binaryString.append(PENTBIT[30]); - info("DL "); - newtable = 16; - break; - case 4: - /* UL DL */ - binaryString.append(PENTBIT[29]); - info("UL "); - binaryString.append(PENTBIT[30]); - info("DL "); - newtable = 16; - break; - case 8: - /* UL DL */ - binaryString.append(PENTBIT[31]); - info("UL "); - binaryString.append(PENTBIT[30]); - info("DL "); - newtable = 16; - break; - } - break; - } - } else { - /* Latch character */ - switch (typemap[i]) { - case 1: - /* To UPPER */ - switch (curtable) { - case 2: - /* ML UL */ - binaryString.append(PENTBIT[29]); - info("ML "); - binaryString.append(PENTBIT[29]); - info("UL "); - newtable = 1; - break; - case 4: - /* UL */ - binaryString.append(PENTBIT[29]); - info("UL "); - newtable = 1; - break; - case 8: - /* UL */ - binaryString.append(PENTBIT[31]); - info("UL "); - newtable = 1; - break; - case 16: - /* UL */ - binaryString.append(QUADBIT[14]); - info("UL "); - newtable = 1; - break; - } - break; - case 2: - /* To LOWER */ - switch (curtable) { - case 1: - /* LL */ - binaryString.append(PENTBIT[28]); - info("LL "); - newtable = 2; - break; - case 4: - /* LL */ - binaryString.append(PENTBIT[28]); - info("LL "); - newtable = 2; - break; - case 8: - /* UL LL */ - binaryString.append(PENTBIT[31]); - info("UL "); - binaryString.append(PENTBIT[28]); - info("LL "); - newtable = 2; - break; - case 16: - /* UL LL */ - binaryString.append(QUADBIT[14]); - info("UL "); - binaryString.append(PENTBIT[28]); - info("LL "); - newtable = 2; - break; - } - break; - case 4: - /* To MIXED */ - switch (curtable) { - case 1: - /* ML */ - binaryString.append(PENTBIT[29]); - info("ML "); - newtable = 4; - break; - case 2: - /* ML */ - binaryString.append(PENTBIT[29]); - info("ML "); - newtable = 4; - break; - case 8: - /* UL ML */ - binaryString.append(PENTBIT[31]); - info("UL "); - binaryString.append(PENTBIT[29]); - info("ML "); - newtable = 4; - break; - case 16: - /* UL ML */ - binaryString.append(QUADBIT[14]); - info("UL "); - binaryString.append(PENTBIT[29]); - info("ML "); - newtable = 4; - break; - } - break; - case 8: - /* To PUNC */ - switch (curtable) { - case 1: - /* ML PL */ - binaryString.append(PENTBIT[29]); - info("ML "); - binaryString.append(PENTBIT[30]); - info("PL "); - newtable = 8; - break; - case 2: - /* ML PL */ - binaryString.append(PENTBIT[29]); - info("ML "); - binaryString.append(PENTBIT[30]); - info("PL "); - newtable = 8; - break; - case 4: - /* PL */ - binaryString.append(PENTBIT[30]); - info("PL "); - newtable = 8; - break; - case 16: - /* UL ML PL */ - binaryString.append(QUADBIT[14]); - info("UL "); - binaryString.append(PENTBIT[29]); - info("ML "); - binaryString.append(PENTBIT[30]); - info("PL "); - newtable = 8; - break; - } - break; - case 16: - /* To DIGIT */ - switch (curtable) { - case 1: - /* DL */ - binaryString.append(PENTBIT[30]); - info("DL "); - newtable = 16; - break; - case 2: - /* DL */ - binaryString.append(PENTBIT[30]); - info("DL "); - newtable = 16; - break; - case 4: - /* UL DL */ - binaryString.append(PENTBIT[29]); - info("UL "); - binaryString.append(PENTBIT[30]); - info("DL "); - newtable = 16; - break; - case 8: - /* UL DL */ - binaryString.append(PENTBIT[31]); - info("UL "); - binaryString.append(PENTBIT[30]); - info("DL "); - newtable = 16; - break; - } - break; - case 32: - /* To BINARY */ - lasttable = curtable; - switch (curtable) { - case 1: - /* BS */ - binaryString.append(PENTBIT[31]); - info("BS "); - newtable = 32; - break; - case 2: - /* BS */ - binaryString.append(PENTBIT[31]); - info("BS "); - newtable = 32; - break; - case 4: - /* BS */ - binaryString.append(PENTBIT[31]); - info("BS "); - newtable = 32; - break; - case 8: - /* UL BS */ - binaryString.append(PENTBIT[31]); - info("UL "); - binaryString.append(PENTBIT[31]); - info("BS "); - lasttable = 1; - newtable = 32; - break; - case 16: - /* UL BS */ - binaryString.append(QUADBIT[14]); - info("UL "); - binaryString.append(PENTBIT[31]); - info("BS "); - lasttable = 1; - newtable = 32; - break; - } - - bytes = 0; - do { - bytes++; - } while (typemap[i + (bytes - 1)] == 32); - bytes--; - - if (bytes > 2079) { - throw OkapiInputException.inputTooLong(); - } - - if (bytes > 31) { - /* Put 00000 followed by 11-bit number of bytes less 31 */ - binaryString.append("00000"); - for (int weight = 0x400; weight > 0; weight = weight >> 1) { - if (((bytes - 31) & weight) != 0) { - binaryString.append('1'); - } else { - binaryString.append('0'); - } - } - } else { - /* Put 5-bit number of bytes */ - for (int weight = 0x10; weight > 0; weight = weight >> 1) { - if ((bytes & weight) != 0) { - binaryString.append('1'); - } else { - binaryString.append('0'); - } - } - } - - break; - } - } - } - /* Add data to the binary string */ - curtable = newtable; - chartype = typemap[i]; - if (chartype > 64) { - chartype -= 64; - } - switch (chartype) { - case 1: - case 2: - case 4: - case 8: - if (charmap[i] >= 400) { - info("FLG(" + (charmap[i] - 400) + ") "); - binaryString.append(TRIBIT[charmap[i] - 400]); - if (charmap[i] != 400) { - /* ECI */ - binaryString.append(eciToBinary()); - } - } else { - binaryString.append(PENTBIT[charmap[i]]); - infoSpace(charmap[i]); - } - break; - case 16: - binaryString.append(QUADBIT[charmap[i]]); - infoSpace(charmap[i]); - break; - case 32: - for (int weight = 0x80; weight > 0; weight = weight >> 1) { - if ((charmap[i] & weight) != 0) { - binaryString.append('1'); - } else { - binaryString.append('0'); - } - } - infoSpace(charmap[i]); - break; - } - } - - infoLine(); - - return binaryString.toString(); - } - - /** Adjusts bit stream so that no codewords are all 0s or all 1s, per Section 7.3.1.2 */ - private StringBuilder adjustBinaryString(String binaryString, boolean compact, int layers) { - - StringBuilder adjustedString = new StringBuilder(); - int codewordSize = getCodewordSize(layers); - int ones = 0; - - /* Insert dummy digits needed to prevent codewords of all 0s or all 1s */ - for (int i = 0; i < binaryString.length(); i++) { - if ((adjustedString.length() + 1) % codewordSize == 0) { - if (ones == codewordSize - 1) { - // codeword of B-1 1s, add dummy 0 - adjustedString.append('0'); - i--; - } else if (ones == 0) { - // codeword of B-1 0s, add dummy 1 - adjustedString.append('1'); - i--; - } else { - // no dummy value needed - adjustedString.append(binaryString.charAt(i)); - } - ones = 0; - } else { - adjustedString.append(binaryString.charAt(i)); - if (binaryString.charAt(i) == '1') { - ones++; - } - } - } - - /* Add padding */ - int adjustedLength = adjustedString.length(); - int remainder = adjustedLength % codewordSize; - int padBits = codewordSize - remainder; - if (padBits == codewordSize) { - padBits = 0; - } - for (int i = 0; i < padBits; i++) { - adjustedString.append('1'); - } - adjustedLength = adjustedString.length(); - - /* Make sure padding didn't create an invalid (all 1s) codeword */ - ones = 0; - for (int i = adjustedLength - codewordSize; i < adjustedLength && i >= 0; i++) { - if (adjustedString.charAt(i) == '1') { - ones++; - } - } - if (ones == codewordSize) { - adjustedString.setCharAt(adjustedLength - 1, '0'); - } - - /* Log the codewords */ - info("Codewords: "); - for (int i = 0; i < (adjustedLength / codewordSize); i++) { - int l = 0, m = (1 << (codewordSize - 1)); - for (int j = 0; j < codewordSize; j++) { - if (adjustedString.charAt((i * codewordSize) + j) == '1') { - l += m; - } - m = m >> 1; - } - infoSpace(l); - } - infoLine(); - - /* Return the adjusted bit string */ - return adjustedString; - } - - private String eciToBinary() { - String eciNumber = Integer.toString(eciMode); - StringBuilder binary = new StringBuilder(4 * eciNumber.length()); - for (int i = 0; i < eciNumber.length(); i++) { - binary.append(QUADBIT[(eciNumber.charAt(i) - '0') + 2]); - infoSpace(eciNumber.charAt(i)); - } - return binary.toString(); - } - - /** Creates the descriptor / mode message, per Section 7.2 */ - private String createDescriptor(boolean compact, int layers, int dataBlocks) { - - StringBuilder descriptor = new StringBuilder(); - int descDataSize; - - if (compact) { - /* The first 2 bits represent the number of layers minus 1 */ - if (((layers - 1) & 0x02) != 0) { - descriptor.append('1'); - } else { - descriptor.append('0'); - } - if (((layers - 1) & 0x01) != 0) { - descriptor.append('1'); - } else { - descriptor.append('0'); - } - /* The next 6 bits represent the number of data blocks minus 1 */ - if (readerInit) { - descriptor.append('1'); - } else { - if (((dataBlocks - 1) & 0x20) != 0) { - descriptor.append('1'); - } else { - descriptor.append('0'); - } - } - for (int i = 0x10; i > 0; i = i >> 1) { - if (((dataBlocks - 1) & i) != 0) { - descriptor.append('1'); - } else { - descriptor.append('0'); - } - } - descDataSize = 2; - } else { - /* The first 5 bits represent the number of layers minus 1 */ - for (int i = 0x10; i > 0; i = i >> 1) { - if (((layers - 1) & i) != 0) { - descriptor.append('1'); - } else { - descriptor.append('0'); - } - } - - /* The next 11 bits represent the number of data blocks minus 1 */ - if (readerInit) { - descriptor.append('1'); - } else { - if (((dataBlocks - 1) & 0x400) != 0) { - descriptor.append('1'); - } else { - descriptor.append('0'); - } - } - for (int i = 0x200; i > 0; i = i >> 1) { - if (((dataBlocks - 1) & i) != 0) { - descriptor.append('1'); - } else { - descriptor.append('0'); - } - } - descDataSize = 4; - } - - infoLine("Mode Message: " + descriptor); - - /* Split into 4-bit codewords */ - int[] desc_data = new int[descDataSize]; - for (int i = 0; i < descDataSize; i++) { - for (int weight = 0; weight < 4; weight++) { - if (descriptor.charAt((i * 4) + weight) == '1') { - desc_data[i] += (8 >> weight); - } - } - } - - /* Add Reed-Solomon error correction with Galois Field GF(16) and prime modulus x^4 + x + 1 (Section 7.2.3) */ - ReedSolomon rs = new ReedSolomon(); - rs.init_gf(0x13); - if (compact) { - rs.init_code(5, 1); - rs.encode(2, desc_data); - int[] desc_ecc = new int[6]; - for (int i = 0; i < 5; i++) { - desc_ecc[i] = rs.getResult(i); - } - for (int i = 0; i < 5; i++) { - for (int weight = 0x08; weight > 0; weight = weight >> 1) { - if ((desc_ecc[4 - i] & weight) != 0) { - descriptor.append('1'); - } else { - descriptor.append('0'); - } - } - } - } else { - rs.init_code(6, 1); - rs.encode(4, desc_data); - int[] desc_ecc = new int[6]; - for (int i = 0; i < 6; i++) { - desc_ecc[i] = rs.getResult(i); - } - for (int i = 0; i < 6; i++) { - for (int weight = 0x08; weight > 0; weight = weight >> 1) { - if ((desc_ecc[5 - i] & weight) != 0) { - descriptor.append('1'); - } else { - descriptor.append('0'); - } - } - } - } - - return descriptor.toString(); - } - - /** Adds error correction data to the specified binary string, which already contains the primary data */ - private void addErrorCorrection(StringBuilder adjustedString, int codewordSize, int dataBlocks, int eccBlocks) { - - int x, poly, startWeight; - - /* Split into codewords and calculate Reed-Solomon error correction codes */ - switch (codewordSize) { - case 6: - x = 32; - poly = 0x43; - startWeight = 0x20; - break; - case 8: - x = 128; - poly = 0x12d; - startWeight = 0x80; - break; - case 10: - x = 512; - poly = 0x409; - startWeight = 0x200; - break; - case 12: - x = 2048; - poly = 0x1069; - startWeight = 0x800; - break; - default: - throw new OkapiInternalException("Unrecognized codeword size: " + codewordSize); - } - - ReedSolomon rs = new ReedSolomon(); - int[] data = new int[dataBlocks + 3]; - int[] ecc = new int[eccBlocks + 3]; - - for (int i = 0; i < dataBlocks; i++) { - for (int weight = 0; weight < codewordSize; weight++) { - if (adjustedString.charAt((i * codewordSize) + weight) == '1') { - data[i] += (x >> weight); - } - } - } - - rs.init_gf(poly); - rs.init_code(eccBlocks, 1); - rs.encode(dataBlocks, data); - - for (int i = 0; i < eccBlocks; i++) { - ecc[i] = rs.getResult(i); - } - - for (int i = (eccBlocks - 1); i >= 0; i--) { - for (int weight = startWeight; weight > 0; weight = weight >> 1) { - if ((ecc[i] & weight) != 0) { - adjustedString.append('1'); - } else { - adjustedString.append('0'); - } - } - } - } - - /** Determines codeword bit length - Table 3 */ - private static int getCodewordSize(int layers) { - if (layers >= 23) { - return 12; - } else if (layers >= 9 && layers <= 22) { - return 10; - } else if (layers >= 3 && layers <= 8) { - return 8; - } else { - assert layers <= 2; - return 6; - } - } -} +/* + * Copyright 2014 Robin Stuart + * + * 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. + */ + +package uk.org.okapibarcode.backend; + +import static java.nio.charset.StandardCharsets.US_ASCII; +import static uk.org.okapibarcode.util.Arrays.insertArray; + +/** + *

Implements Aztec Code bar code symbology According to ISO/IEC 24778:2008. + * + *

Aztec Code can encode 8-bit ISO 8859-1 (Latin-1) data (except 0x00 Null + * characters) up to a maximum length of approximately 3800 numeric characters, + * 3000 alphabetic characters or 1900 bytes of data in a two-dimensional matrix + * symbol. + * + * @author Robin Stuart + */ +public class AztecCode extends Symbol { + + /* 27 x 27 data grid */ + private static final int[] COMPACT_AZTEC_MAP = { + 609, 608, 411, 413, 415, 417, 419, 421, 423, 425, 427, 429, 431, 433, 435, 437, 439, 441, 443, 445, 447, 449, 451, 453, 455, 457, 459, + 607, 606, 410, 412, 414, 416, 418, 420, 422, 424, 426, 428, 430, 432, 434, 436, 438, 440, 442, 444, 446, 448, 450, 452, 454, 456, 458, + 605, 604, 409, 408, 243, 245, 247, 249, 251, 253, 255, 257, 259, 261, 263, 265, 267, 269, 271, 273, 275, 277, 279, 281, 283, 460, 461, + 603, 602, 407, 406, 242, 244, 246, 248, 250, 252, 254, 256, 258, 260, 262, 264, 266, 268, 270, 272, 274, 276, 278, 280, 282, 462, 463, + 601, 600, 405, 404, 241, 240, 107, 109, 111, 113, 115, 117, 119, 121, 123, 125, 127, 129, 131, 133, 135, 137, 139, 284, 285, 464, 465, + 599, 598, 403, 402, 239, 238, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 286, 287, 466, 467, + 597, 596, 401, 400, 237, 236, 105, 104, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 140, 141, 288, 289, 468, 469, + 595, 594, 399, 398, 235, 234, 103, 102, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 142, 143, 290, 291, 470, 471, + 593, 592, 397, 396, 233, 232, 101, 100, 1, 1, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 0, 1, 28, 29, 144, 145, 292, 293, 472, 473, + 591, 590, 395, 394, 231, 230, 99, 98, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 30, 31, 146, 147, 294, 295, 474, 475, + 589, 588, 393, 392, 229, 228, 97, 96, 2027, 1, 0, 0, 0, 0, 0, 0, 0, 1, 2007, 32, 33, 148, 149, 296, 297, 476, 477, + 587, 586, 391, 390, 227, 226, 95, 94, 2026, 1, 0, 1, 1, 1, 1, 1, 0, 1, 2008, 34, 35, 150, 151, 298, 299, 478, 479, + 585, 584, 389, 388, 225, 224, 93, 92, 2025, 1, 0, 1, 0, 0, 0, 1, 0, 1, 2009, 36, 37, 152, 153, 300, 301, 480, 481, + 583, 582, 387, 386, 223, 222, 91, 90, 2024, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2010, 38, 39, 154, 155, 302, 303, 482, 483, + 581, 580, 385, 384, 221, 220, 89, 88, 2023, 1, 0, 1, 0, 0, 0, 1, 0, 1, 2011, 40, 41, 156, 157, 304, 305, 484, 485, + 579, 578, 383, 382, 219, 218, 87, 86, 2022, 1, 0, 1, 1, 1, 1, 1, 0, 1, 2012, 42, 43, 158, 159, 306, 307, 486, 487, + 577, 576, 381, 380, 217, 216, 85, 84, 2021, 1, 0, 0, 0, 0, 0, 0, 0, 1, 2013, 44, 45, 160, 161, 308, 309, 488, 489, + 575, 574, 379, 378, 215, 214, 83, 82, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 46, 47, 162, 163, 310, 311, 490, 491, + 573, 572, 377, 376, 213, 212, 81, 80, 0, 0, 2020, 2019, 2018, 2017, 2016, 2015, 2014, 0, 0, 48, 49, 164, 165, 312, 313, 492, 493, + 571, 570, 375, 374, 211, 210, 78, 76, 74, 72, 70, 68, 66, 64, 62, 60, 58, 56, 54, 50, 51, 166, 167, 314, 315, 494, 495, + 569, 568, 373, 372, 209, 208, 79, 77, 75, 73, 71, 69, 67, 65, 63, 61, 59, 57, 55, 52, 53, 168, 169, 316, 317, 496, 497, + 567, 566, 371, 370, 206, 204, 202, 200, 198, 196, 194, 192, 190, 188, 186, 184, 182, 180, 178, 176, 174, 170, 171, 318, 319, 498, 499, + 565, 564, 369, 368, 207, 205, 203, 201, 199, 197, 195, 193, 191, 189, 187, 185, 183, 181, 179, 177, 175, 172, 173, 320, 321, 500, 501, + 563, 562, 366, 364, 362, 360, 358, 356, 354, 352, 350, 348, 346, 344, 342, 340, 338, 336, 334, 332, 330, 328, 326, 322, 323, 502, 503, + 561, 560, 367, 365, 363, 361, 359, 357, 355, 353, 351, 349, 347, 345, 343, 341, 339, 337, 335, 333, 331, 329, 327, 324, 325, 504, 505, + 558, 556, 554, 552, 550, 548, 546, 544, 542, 540, 538, 536, 534, 532, 530, 528, 526, 524, 522, 520, 518, 516, 514, 512, 510, 506, 507, + 559, 557, 555, 553, 551, 549, 547, 545, 543, 541, 539, 537, 535, 533, 531, 529, 527, 525, 523, 521, 519, 517, 515, 513, 511, 508, 509 + }; + + private static final int[][] AZTEC_MAP = new int[151][151]; + + /* From Table 2: + * + * 1 = upper + * 2 = lower + * 4 = mixed + * 8 = punctuation + * 16 = digits + * 32 = binary + * + * Values can be OR'ed, so e.g. 12 = 4 | 8, and 23 = 1 | 2 | 4 | 16 + */ + private static final int[] AZTEC_CODE_SET = { + 32, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 12, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 4, 4, 4, 4, 4, 23, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 24, 8, 24, 8, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 8, 8, + 8, 8, 8, 8, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 8, 4, 8, 4, 4, 4, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 8, 4, 8, 4, 4 + }; + + /* From Table 2 */ + private static final int[] AZTEC_SYMBOL_CHAR = { + 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 300, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 15, 16, 17, 18, 19, 1, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 301, 18, 302, 20, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 21, 22, + 23, 24, 25, 26, 20, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 27, 21, 28, 22, 23, 24, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 29, 25, 30, 26, 27 + }; + + /* Problem characters are: + * 300: Carriage Return (ASCII 13) + * 301: Comma (ASCII 44) + * 302: Full Stop (ASCII 46) + */ + private static final String[] PENTBIT = { + "00000", "00001", "00010", "00011", "00100", "00101", "00110", "00111", "01000", "01001", + "01010", "01011", "01100", "01101", "01110", "01111", "10000", "10001", "10010", "10011", "10100", "10101", + "10110", "10111", "11000", "11001", "11010", "11011", "11100", "11101", "11110", "11111" + }; + + private static final String[] QUADBIT = { + "0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", + "1010", "1011", "1100", "1101", "1110", "1111" + }; + + private static final String[] TRIBIT = { + "000", "001", "010", "011", "100", "101", "110", "111" + }; + + /* Codewords per symbol */ + private static final int[] AZTEC_SIZES = { + 21, 48, 60, 88, 120, 156, 196, 240, 230, 272, 316, 364, 416, 470, 528, 588, 652, 720, 790, + 864, 940, 1020, 920, 992, 1066, 1144, 1224, 1306, 1392, 1480, 1570, 1664 + }; + + private static final int[] AZTEC_COMPACT_SIZES = { + 17, 40, 51, 76 + }; + + /* Data bits per symbol maximum with 10% error correction */ + private static final int[] AZTEC_10_DATA_SIZES = { + 96, 246, 408, 616, 840, 1104, 1392, 1704, 2040, 2420, 2820, 3250, 3720, 4200, 4730, + 5270, 5840, 6450, 7080, 7750, 8430, 9150, 9900, 10680, 11484, 12324, 13188, 14076, + 15000, 15948, 16920, 17940 + }; + + /* Data bits per symbol maximum with 23% error correction */ + private static final int[] AZTEC_23_DATA_SIZES = { + 84, 204, 352, 520, 720, 944, 1184, 1456, 1750, 2070, 2410, 2780, 3180, 3590, 4040, + 4500, 5000, 5520, 6060, 6630, 7210, 7830, 8472, 9132, 9816, 10536, 11280, 12036, + 12828, 13644, 14472, 15348 + }; + + /* Data bits per symbol maximum with 36% error correction */ + private static final int[] AZTEC_36_DATA_SIZES = { + 66, 168, 288, 432, 592, 776, 984, 1208, 1450, 1720, 2000, 2300, 2640, 2980, 3350, + 3740, 4150, 4580, 5030, 5500, 5990, 6500, 7032, 7584, 8160, 8760, 9372, 9996, 10656, + 11340, 12024, 12744 + }; + + /* Data bits per symbol maximum with 50% error correction */ + private static final int[] AZTEC_50_DATA_SIZES = { + 48, 126, 216, 328, 456, 600, 760, 936, 1120, 1330, 1550, 1790, 2050, 2320, 2610, + 2910, 3230, 3570, 3920, 4290, 4670, 5070, 5484, 5916, 6360, 6828, 7308, 7800, 8316, + 8844, 9384, 9948 + }; + + private static final int[] AZTEC_COMPACT_10_DATA_SIZES = { + 78, 198, 336, 520 + }; + private static final int[] AZTEC_COMPACT_23_DATA_SIZES = { + 66, 168, 288, 440 + }; + private static final int[] AZTEC_COMPACT_36_DATA_SIZES = { + 48, 138, 232, 360 + }; + private static final int[] AZTEC_COMPACT_50_DATA_SIZES = { + 36, 102, 176, 280 + }; + + private static final int[] AZTEC_OFFSET = { + 66, 64, 62, 60, 57, 55, 53, 51, 49, 47, 45, 42, 40, 38, 36, 34, 32, 30, 28, 25, 23, 21, + 19, 17, 15, 13, 10, 8, 6, 4, 2, 0 + }; + + private static final int[] AZTEC_COMPACT_OFFSET = { + 6, 4, 2, 0 + }; + + /* Initialize AZTEC_MAP */ + static { + + int layer, start, length, n, i; + int x, y; + + for (x = 0; x < 151; x++) { + for (y = 0; y < 151; y++) { + AZTEC_MAP[x][y] = 0; + } + } + + for (layer = 1; layer < 33; layer++) { + start = (112 * (layer - 1)) + (16 * (layer - 1) * (layer - 1)) + 2; + length = 28 + ((layer - 1) * 4) + (layer * 4); + /* Top */ + i = 0; + x = 64 - ((layer - 1) * 2); + y = 63 - ((layer - 1) * 2); + for (n = start; n < (start + length); n += 2) { + AZTEC_MAP[avoidReferenceGrid(x + i)][avoidReferenceGrid(y)] = n; + AZTEC_MAP[avoidReferenceGrid(x + i)][avoidReferenceGrid(y - 1)] = n + 1; + i++; + } + /* Right */ + i = 0; + x = 78 + ((layer - 1) * 2); + y = 64 - ((layer - 1) * 2); + for (n = start + length; n < (start + (length * 2)); n += 2) { + AZTEC_MAP[avoidReferenceGrid(x)][avoidReferenceGrid(y + i)] = n; + AZTEC_MAP[avoidReferenceGrid(x + 1)][avoidReferenceGrid(y + i)] = n + 1; + i++; + } + /* Bottom */ + i = 0; + x = 77 + ((layer - 1) * 2); + y = 78 + ((layer - 1) * 2); + for (n = start + (length * 2); n < (start + (length * 3)); n += 2) { + AZTEC_MAP[avoidReferenceGrid(x - i)][avoidReferenceGrid(y)] = n; + AZTEC_MAP[avoidReferenceGrid(x - i)][avoidReferenceGrid(y + 1)] = n + 1; + i++; + } + /* Left */ + i = 0; + x = 63 - ((layer - 1) * 2); + y = 77 + ((layer - 1) * 2); + for (n = start + (length * 3); n < (start + (length * 4)); n += 2) { + AZTEC_MAP[avoidReferenceGrid(x)][avoidReferenceGrid(y - i)] = n; + AZTEC_MAP[avoidReferenceGrid(x - 1)][avoidReferenceGrid(y - i)] = n + 1; + i++; + } + } + + /* Central finder pattern */ + for (y = 69; y <= 81; y++) { + for (x = 69; x <= 81; x++) { + AZTEC_MAP[x][y] = 1; + } + } + for (y = 70; y <= 80; y++) { + for (x = 70; x <= 80; x++) { + AZTEC_MAP[x][y] = 0; + } + } + for (y = 71; y <= 79; y++) { + for (x = 71; x <= 79; x++) { + AZTEC_MAP[x][y] = 1; + } + } + for (y = 72; y <= 78; y++) { + for (x = 72; x <= 78; x++) { + AZTEC_MAP[x][y] = 0; + } + } + for (y = 73; y <= 77; y++) { + for (x = 73; x <= 77; x++) { + AZTEC_MAP[x][y] = 1; + } + } + for (y = 74; y <= 76; y++) { + for (x = 74; x <= 76; x++) { + AZTEC_MAP[x][y] = 0; + } + } + + /* Guide bars */ + for (y = 11; y < 151; y += 16) { + for (x = 1; x < 151; x += 2) { + AZTEC_MAP[x][y] = 1; + AZTEC_MAP[y][x] = 1; + } + } + + /* Descriptor */ + for (i = 0; i < 10; i++) { /* Top */ + AZTEC_MAP[avoidReferenceGrid(66 + i)][avoidReferenceGrid(64)] = 20000 + i; + } + for (i = 0; i < 10; i++) { /* Right */ + AZTEC_MAP[avoidReferenceGrid(77)][avoidReferenceGrid(66 + i)] = 20010 + i; + } + for (i = 0; i < 10; i++) { /* Bottom */ + AZTEC_MAP[avoidReferenceGrid(75 - i)][avoidReferenceGrid(77)] = 20020 + i; + } + for (i = 0; i < 10; i++) { /* Left */ + AZTEC_MAP[avoidReferenceGrid(64)][avoidReferenceGrid(75 - i)] = 20030 + i; + } + + /* Orientation */ + AZTEC_MAP[avoidReferenceGrid(64)][avoidReferenceGrid(64)] = 1; + AZTEC_MAP[avoidReferenceGrid(65)][avoidReferenceGrid(64)] = 1; + AZTEC_MAP[avoidReferenceGrid(64)][avoidReferenceGrid(65)] = 1; + AZTEC_MAP[avoidReferenceGrid(77)][avoidReferenceGrid(64)] = 1; + AZTEC_MAP[avoidReferenceGrid(77)][avoidReferenceGrid(65)] = 1; + AZTEC_MAP[avoidReferenceGrid(77)][avoidReferenceGrid(76)] = 1; + } + + private static int avoidReferenceGrid(int input) { + int output = input; + if (output > 10) { + output++; + } + if (output > 26) { + output++; + } + if (output > 42) { + output++; + } + if (output > 58) { + output++; + } + if (output > 74) { + output++; + } + if (output > 90) { + output++; + } + if (output > 106) { + output++; + } + if (output > 122) { + output++; + } + if (output > 138) { + output++; + } + return output; + } + + private int preferredSize = 0; + private int preferredEccLevel = 2; + private String structuredAppendMessageId; + private int structuredAppendPosition = 1; + private int structuredAppendTotal = 1; + + /** + *

Sets a preferred symbol size. This value may be ignored if data string is + * too large to fit in the specified symbol size. Values correspond to symbol + * sizes as shown in the following table: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
InputSymbol SizeInputSymbol Size
1 15 x 15 19 79 x 79
2 19 x 19 20 83 x 83
3 23 x 23 21 87 x 87
4 27 x 27 22 91 x 91
5 19 x 19 23 95 x 95
6 23 x 23 24 101 x 101
7 27 x 27 25 105 x 105
8 31 x 31 26 109 x 109
9 37 x 37 27 113 x 113
10 41 x 41 28 117 x 117
11 45 x 45 29 121 x 121
12 49 x 49 30 125 x 125
13 53 x 53 31 131 x 131
14 57 x 57 32 135 x 135
15 61 x 61 33 139 x 139
16 67 x 67 34 143 x 143
17 71 x 71 35 147 x 147
18 75 x 75 36 151 x 151
+ * + *

Note that sizes 1 to 4 are the "compact" Aztec Code symbols; sizes 5 to 36 + * are the "full-range" Aztec Code symbols. + * + * @param size an integer in the range 1 - 36 + */ + public void setPreferredSize(int size) { + if (size < 1 || size > 36) { + throw new IllegalArgumentException("Invalid size: " + size); + } + preferredSize = size; + } + + /** + * Returns the preferred symbol size. + * + * @return the preferred symbol size + */ + public int getPreferredSize() { + return preferredSize; + } + + /** + * Sets the preferred minimum amount of symbol space dedicated to error + * correction. This value will be ignored if a symbol size has been set by + * setPreferredSize. Valid options are: + * + * + * + * + * + * + * + * + * + *
ModeError Correction Capacity
1 > 10% + 3 codewords
2 > 23% + 3 codewords
3 > 36% + 3 codewords
4 > 50% + 3 codewords
+ * + * @param eccLevel an integer in the range 1 - 4 + */ + public void setPreferredEccLevel(int eccLevel) { + if (eccLevel < 1 || eccLevel > 4) { + throw new IllegalArgumentException("Invalid ECC level: " + eccLevel); + } + preferredEccLevel = eccLevel; + } + + /** + * Returns the preferred error correction level. + * + * @return the preferred error correction level + */ + public int getPreferredEccLevel() { + return preferredEccLevel; + } + + /** + * If this Aztec Code symbol is part of a series of Aztec Code symbols appended in a structured + * format, this method sets the position of this symbol in the series. Valid values are 1 through + * 26 inclusive. + * + * @param position the position of this Aztec Code symbol in the structured append series + */ + public void setStructuredAppendPosition(int position) { + if (position < 1 || position > 26) { + throw new IllegalArgumentException("Invalid Aztec Code structured append position: " + position); + } + this.structuredAppendPosition = position; + } + + /** + * Returns the position of this Aztec Code symbol in a series of symbols using structured append. + * If this symbol is not part of such a series, this method will return 1. + * + * @return the position of this Aztec Code symbol in a series of symbols using structured append + */ + public int getStructuredAppendPosition() { + return structuredAppendPosition; + } + + /** + * If this Aztec Code symbol is part of a series of Aztec Code symbols appended in a structured + * format, this method sets the total number of symbols in the series. Valid values are + * 1 through 26 inclusive. A value of 1 indicates that this symbol is not part of a structured + * append series. + * + * @param total the total number of Aztec Code symbols in the structured append series + */ + public void setStructuredAppendTotal(int total) { + if (total < 1 || total > 26) { + throw new IllegalArgumentException("Invalid Aztec Code structured append total: " + total); + } + this.structuredAppendTotal = total; + } + + /** + * Returns the size of the series of Aztec Code symbols using structured append that this symbol + * is part of. If this symbol is not part of a structured append series, this method will return + * 1. + * + * @return size of the series that this symbol is part of + */ + public int getStructuredAppendTotal() { + return structuredAppendTotal; + } + + /** + * If this Aztec Code symbol is part of a series of Aztec Code symbols appended in a structured format, + * this method sets the unique message ID for the series. Values may not contain spaces and must contain + * only printable ASCII characters. Message IDs are optional. + * + * @param messageId the unique message ID for the series that this symbol is part of + */ + public void setStructuredAppendMessageId(String messageId) { + if (messageId != null && !messageId.matches("^[\\x21-\\x7F]+$")) { + throw new IllegalArgumentException("Invalid Aztec Code structured append message ID: " + messageId); + } + this.structuredAppendMessageId = messageId; + } + + /** + * Returns the unique message ID of the series of Aztec Code symbols using structured append that this + * symbol is part of. If this symbol is not part of a structured append series, this method will return + * null. + * + * @return the unique message ID for the series that this symbol is part of + */ + public String getStructuredAppendMessageId() { + return structuredAppendMessageId; + } + + @Override + protected boolean gs1Supported() { + return true; + } + + @Override + protected boolean eciSupported() { + return true; + } + + @Override + protected void encode() { + + int layers; + boolean compact; + StringBuilder adjustedString; + + eciProcess(); // Get ECI mode + + /* Optional structured append (Section 8 of spec) */ + /* ML + UL start flag handled later, not part of data */ + if (structuredAppendTotal != 1) { + StringBuilder prefix = new StringBuilder(); + if (structuredAppendMessageId != null) { + prefix.append(' ').append(structuredAppendMessageId).append(' '); + } + prefix.append((char) (structuredAppendPosition + 64)); // 1-26 as A-Z + prefix.append((char) (structuredAppendTotal + 64)); // 1-26 as A-Z + int[] prefixArray = toBytes(prefix.toString(), US_ASCII); + inputData = insertArray(inputData, 0, prefixArray); + } + + String binaryString = generateAztecBinary(); + int dataLength = binaryString.length(); + + if (preferredSize == 0) { + + /* The size of the symbol can be determined by Okapi */ + + int dataMaxSize = 0; + int compLoop = (readerInit ? 1 : 4); + + do { + /* Decide what size symbol to use - the smallest that fits the data */ + + int[] dataSizes; + int[] compactDataSizes; + + switch (preferredEccLevel) { + /* For each level of error correction work out the smallest symbol which the data will fit in */ + case 1: + dataSizes = AZTEC_10_DATA_SIZES; + compactDataSizes = AZTEC_COMPACT_10_DATA_SIZES; + break; + case 2: + dataSizes = AZTEC_23_DATA_SIZES; + compactDataSizes = AZTEC_COMPACT_23_DATA_SIZES; + break; + case 3: + dataSizes = AZTEC_36_DATA_SIZES; + compactDataSizes = AZTEC_COMPACT_36_DATA_SIZES; + break; + case 4: + dataSizes = AZTEC_50_DATA_SIZES; + compactDataSizes = AZTEC_COMPACT_50_DATA_SIZES; + break; + default: + throw new OkapiInputException("Unrecognized ECC level: " + preferredEccLevel); + } + + layers = 0; + compact = false; + + for (int i = 32; i > 0; i--) { + if (dataLength < dataSizes[i - 1]) { + layers = i; + compact = false; + dataMaxSize = dataSizes[i - 1]; + } + } + + for (int i = compLoop; i > 0; i--) { + if (dataLength < compactDataSizes[i - 1]) { + layers = i; + compact = true; + dataMaxSize = compactDataSizes[i - 1]; + } + } + + if (layers == 0) { + /* Couldn't find a symbol which fits the data */ + throw new OkapiInputException("Input too long (too many bits for selected ECC)"); + } + + adjustedString = adjustBinaryString(binaryString, compact, layers); + dataLength = adjustedString.length(); + + } while (dataLength > dataMaxSize); + /* This loop will only repeat on the rare occasions when the rule about not having all 1s or all 0s + means that the binary string has had to be lengthened beyond the maximum number of bits that can + be encoded in a symbol of the selected size */ + + } else { + + /* The size of the symbol has been specified by the user */ + + if (preferredSize >= 1 && preferredSize <= 4) { + compact = true; + layers = preferredSize; + } else { + compact = false; + layers = preferredSize - 4; + } + + adjustedString = adjustBinaryString(binaryString, compact, layers); + + /* Check if the data actually fits into the selected symbol size */ + int codewordSize = getCodewordSize(layers); + int[] sizes = (compact ? AZTEC_COMPACT_SIZES : AZTEC_SIZES); + int dataMaxSize = codewordSize * (sizes[layers - 1] - 3); + if (adjustedString.length() > dataMaxSize) { + throw new OkapiInputException("Data too long for specified Aztec Code symbol size"); + } + } + + if (readerInit && compact && layers > 1) { + throw new OkapiInputException("Symbol is too large for reader initialization"); + } + + if (readerInit && layers > 22) { + throw new OkapiInputException("Symbol is too large for reader initialization"); + } + + int codewordSize = getCodewordSize(layers); + int dataBlocks = adjustedString.length() / codewordSize; + + int eccBlocks; + if (compact) { + eccBlocks = AZTEC_COMPACT_SIZES[layers - 1] - dataBlocks; + } else { + eccBlocks = AZTEC_SIZES[layers - 1] - dataBlocks; + } + + infoLine("Compact Mode: " + compact); + infoLine("Layers: " + layers); + infoLine("Codeword Length: " + codewordSize + " bits"); + infoLine("Data Codewords: " + dataBlocks); + infoLine("ECC Codewords: " + eccBlocks); + + /* Add ECC data to the adjusted string */ + addErrorCorrection(adjustedString, codewordSize, dataBlocks, eccBlocks); + + /* Invert the data so that actual data is on the outside and reed-solomon on the inside */ + for (int i = 0; i < adjustedString.length() / 2; i++) { + int mirror = adjustedString.length() - i - 1; + char c = adjustedString.charAt(i); + adjustedString.setCharAt(i, adjustedString.charAt(mirror)); + adjustedString.setCharAt(mirror, c); + } + + /* Create the descriptor / mode message */ + String descriptor = createDescriptor(compact, layers, dataBlocks); + + /* Plot all of the data into the symbol in pre-defined spiral pattern */ + if (compact) { + + readable = ""; + row_count = 27 - (2 * AZTEC_COMPACT_OFFSET[layers - 1]); + row_height = new int[row_count]; + row_height[0] = -1; + pattern = new String[row_count]; + for (int y = AZTEC_COMPACT_OFFSET[layers - 1]; y < (27 - AZTEC_COMPACT_OFFSET[layers - 1]); y++) { + StringBuilder bin = new StringBuilder(27); + for (int x = AZTEC_COMPACT_OFFSET[layers - 1]; x < (27 - AZTEC_COMPACT_OFFSET[layers - 1]); x++) { + int j = COMPACT_AZTEC_MAP[(y * 27) + x]; + if (j == 0) { + bin.append('0'); + } + if (j == 1) { + bin.append('1'); + } + if (j >= 2) { + if (j - 2 < adjustedString.length()) { + bin.append(adjustedString.charAt(j - 2)); + } else { + if (j >= 2000) { + bin.append(descriptor.charAt(j - 2000)); + } else { + bin.append('0'); + } + } + } + } + row_height[y - AZTEC_COMPACT_OFFSET[layers - 1]] = 1; + pattern[y - AZTEC_COMPACT_OFFSET[layers - 1]] = bin2pat(bin); + } + + } else { + + readable = ""; + row_count = 151 - (2 * AZTEC_OFFSET[layers - 1]); + row_height = new int[row_count]; + row_height[0] = -1; + pattern = new String[row_count]; + for (int y = AZTEC_OFFSET[layers - 1]; y < (151 - AZTEC_OFFSET[layers - 1]); y++) { + StringBuilder bin = new StringBuilder(151); + for (int x = AZTEC_OFFSET[layers - 1]; x < (151 - AZTEC_OFFSET[layers - 1]); x++) { + int j = AZTEC_MAP[x][y]; + if (j == 1) { + bin.append('1'); + } + if (j == 0) { + bin.append('0'); + } + if (j >= 2) { + if (j - 2 < adjustedString.length()) { + bin.append(adjustedString.charAt(j - 2)); + } else { + if (j >= 20000) { + bin.append(descriptor.charAt(j - 20000)); + } else { + bin.append('0'); + } + } + } + } + row_height[y - AZTEC_OFFSET[layers - 1]] = 1; + pattern[y - AZTEC_OFFSET[layers - 1]] = bin2pat(bin); + } + } + } + + private String generateAztecBinary() { + + /* Encode input data into a binary string */ + int i, j, k, bytes; + int curtable, newtable, lasttable, chartype, maplength, blocks; + int[] charmap = new int[2 * inputData.length]; + int[] typemap = new int[2 * inputData.length]; + int[] blockType = new int[inputData.length + 1]; + int[] blockLength = new int[inputData.length + 1]; + + /* Lookup input string in encoding table */ + maplength = 0; + + /* Add FNC1 to beginning of GS1 messages */ + if (inputDataType == DataType.GS1) { + charmap[maplength] = 0; // FLG + typemap[maplength++] = 8; // PUNC + charmap[maplength] = 400; // (0) + typemap[maplength++] = 8; // PUNC + } + + if (eciMode != 3) { + int flagNumber; + + charmap[maplength] = 0; // FLG + typemap[maplength++] = 8; // PUNC + + flagNumber = 6; + + if (eciMode < 100000) { + flagNumber = 5; + } + + if (eciMode < 10000) { + flagNumber = 4; + } + + if (eciMode < 1000) { + flagNumber = 3; + } + + if (eciMode < 100) { + flagNumber = 2; + } + + if (eciMode < 10) { + flagNumber = 1; + } + + charmap[maplength] = 400 + flagNumber; + typemap[maplength++] = 8; // PUNC + } + + for (i = 0; i < inputData.length; i++) { + if (inputData[i] == FNC1) { + /* FNC1 represented by FLG(0) */ + charmap[maplength] = 0; // FLG + typemap[maplength++] = 8; // PUNC + charmap[maplength] = 400; // (0) + typemap[maplength++] = 8; // PUNC + } else { + if ((inputData[i] > 0x7F) || (inputData[i] == 0x00)) { + charmap[maplength] = inputData[i]; + typemap[maplength++] = 32; //BINARY + } else { + charmap[maplength] = AZTEC_SYMBOL_CHAR[inputData[i]]; + typemap[maplength++] = AZTEC_CODE_SET[inputData[i]]; + } + } + } + + /* Look for double character encoding possibilities */ + for (i = 0; i < (maplength - 1); i++) { + if (((charmap[i] == 300) && (charmap[i + 1] == 11)) && ((typemap[i] == 12) && (typemap[i + 1] == 4))) { + /* CR LF combination */ + charmap[i] = 2; + typemap[i] = 8; // PUNC + if ((i + 1) != maplength) { + for (j = i + 1; j < maplength; j++) { + charmap[j] = charmap[j + 1]; + typemap[j] = typemap[j + 1]; + } + } + maplength--; + } + + if (((charmap[i] == 302) && (charmap[i + 1] == 1)) && ((typemap[i] == 24) && (typemap[i + 1] == 23))) { + /* . SP combination */ + charmap[i] = 3; + typemap[i] = 8; // PUNC; + if ((i + 1) != maplength) { + for (j = i + 1; j < maplength; j++) { + charmap[j] = charmap[j + 1]; + typemap[j] = typemap[j + 1]; + } + } + maplength--; + } + + if (((charmap[i] == 301) && (charmap[i + 1] == 1)) && ((typemap[i] == 24) && (typemap[i + 1] == 23))) { + /* , SP combination */ + charmap[i] = 4; + typemap[i] = 8; //PUNC; + if ((i + 1) != maplength) { + for (j = i + 1; j < maplength; j++) { + charmap[j] = charmap[j + 1]; + typemap[j] = typemap[j + 1]; + } + } + maplength--; + } + + if (((charmap[i] == 21) && (charmap[i + 1] == 1)) && ((typemap[i] == 8) && (typemap[i + 1] == 23))) { + /* : SP combination */ + charmap[i] = 5; + typemap[i] = 8; //PUNC; + if ((i + 1) != maplength) { + for (j = i + 1; j < maplength; j++) { + charmap[j] = charmap[j + 1]; + typemap[j] = typemap[j + 1]; + } + } + maplength--; + } + } + + /* look for blocks of characters which use the same table */ + blocks = 0; + for (i = 0; i < maplength; i++) { + if (i > 0 && typemap[i] == typemap[i - 1]) { + blockLength[blocks - 1]++; + } else { + blocks++; + blockType[blocks - 1] = typemap[i]; + blockLength[blocks - 1] = 1; + } + } + + if ((blockType[0] & 1) != 0) { + blockType[0] = 1; + } + if ((blockType[0] & 2) != 0) { + blockType[0] = 2; + } + if ((blockType[0] & 4) != 0) { + blockType[0] = 4; + } + if ((blockType[0] & 8) != 0) { + blockType[0] = 8; + } + + if (blocks > 1) { + + /* look for adjacent blocks which can use the same table (left to right search) */ + for (i = 1; i < blocks; i++) { + if ((blockType[i] & blockType[i - 1]) != 0) { + blockType[i] = (blockType[i] & blockType[i - 1]); + } + } + + if ((blockType[blocks - 1] & 1) != 0) { + blockType[blocks - 1] = 1; + } + if ((blockType[blocks - 1] & 2) != 0) { + blockType[blocks - 1] = 2; + } + if ((blockType[blocks - 1] & 4) != 0) { + blockType[blocks - 1] = 4; + } + if ((blockType[blocks - 1] & 8) != 0) { + blockType[blocks - 1] = 8; + } + + /* look for adjacent blocks which can use the same table (right to left search) */ + for (i = blocks - 2; i > 0; i--) { + if ((blockType[i] & blockType[i + 1]) != 0) { + blockType[i] = (blockType[i] & blockType[i + 1]); + } + } + + /* determine the encoding table for characters which do not fit with adjacent blocks */ + for (i = 1; i < blocks; i++) { + if ((blockType[i] & 8) != 0) { + blockType[i] = 8; + } + if ((blockType[i] & 4) != 0) { + blockType[i] = 4; + } + if ((blockType[i] & 2) != 0) { + blockType[i] = 2; + } + if ((blockType[i] & 1) != 0) { + blockType[i] = 1; + } + } + + /* if less than 4 characters are preceded and followed by binary blocks + then it is more efficient to also encode these in binary + */ + +// for (i = 1; i < blocks - 1; i++) { +// if ((blockType[i - 1] == 32) && (blockLength[i] < 4)) { +// int nonBinaryLength = blockLength[i]; +// for (int l = i; ((l < blocks) && (blockType[l] != 32)); l++) { +// nonBinaryLength += blockLength[l]; +// } +// if (nonBinaryLength < 4) { +// blockType[i] = 32; +// } +// } +// } + + /* Combine blocks of the same type */ + i = 0; + do { + if (blockType[i] == blockType[i + 1]) { + blockLength[i] += blockLength[i + 1]; + for (j = i + 1; j < blocks - 1; j++) { + blockType[j] = blockType[j + 1]; + blockLength[j] = blockLength[j + 1]; + } + blocks--; + } else { + i++; + } + } while (i < blocks - 1); + } + + /* Put the adjusted block data back into typemap */ + j = 0; + for (i = 0; i < blocks; i++) { + if ((blockLength[i] < 3) && (blockType[i] != 32)) { /* Shift character(s) needed */ + + for (k = 0; k < blockLength[i]; k++) { + typemap[j + k] = blockType[i] + 64; + } + } else { /* Latch character (or byte mode) needed */ + + for (k = 0; k < blockLength[i]; k++) { + typemap[j + k] = blockType[i]; + } + } + j += blockLength[i]; + } + + /* Don't shift an initial capital letter */ + if (maplength > 0 && typemap[0] == 65) { + typemap[0] = 1; + } + + /* Problem characters (those that appear in different tables with different values) can now be resolved into their tables */ + for (i = 0; i < maplength; i++) { + if ((charmap[i] >= 300) && (charmap[i] < 400)) { + curtable = typemap[i]; + if (curtable > 64) { + curtable -= 64; + } + switch (charmap[i]) { + case 300: + /* Carriage Return */ + switch (curtable) { + case 8: + charmap[i] = 1; + break; // PUNC + case 4: + charmap[i] = 14; + break; // PUNC + } + break; + case 301: + /* Comma */ + switch (curtable) { + case 8: + charmap[i] = 17; + break; // PUNC + case 16: + charmap[i] = 12; + break; // DIGIT + } + break; + case 302: + /* Full Stop */ + switch (curtable) { + case 8: + charmap[i] = 19; + break; // PUNC + case 16: + charmap[i] = 13; + break; // DIGIT + } + break; + } + } + } + + StringBuilder binaryString = new StringBuilder(); + info("Encoding: "); + curtable = 1; /* start with 1 table */ + lasttable = 1; + + /* Optional structured append start flag (Section 8 of spec) */ + if (structuredAppendTotal != 1) { + binaryString.append(PENTBIT[29]); + info("ML "); + binaryString.append(PENTBIT[29]); + info("UL "); + } + + for (i = 0; i < maplength; i++) { + newtable = curtable; + if ((typemap[i] != curtable) && (charmap[i] < 400)) { + /* Change table */ + if (curtable == 32) { + /* If ending binary mode the current table is the same as when entering binary mode */ + curtable = lasttable; + newtable = lasttable; + } + if (typemap[i] > 64) { + /* Shift character */ + switch (typemap[i]) { + case (64 + 1): + /* To UPPER */ + switch (curtable) { + case 2: + /* US */ + binaryString.append(PENTBIT[28]); + info("US "); + break; + case 4: + /* UL */ + binaryString.append(PENTBIT[29]); + info("UL "); + newtable = 1; + break; + case 8: + /* UL */ + binaryString.append(PENTBIT[31]); + info("UL "); + newtable = 1; + break; + case 16: + /* US */ + binaryString.append(QUADBIT[15]); + info("US "); + break; + } + break; + case (64 + 2): + /* To LOWER */ + switch (curtable) { + case 1: + /* LL */ + binaryString.append(PENTBIT[28]); + info("LL "); + newtable = 2; + break; + case 4: + /* LL */ + binaryString.append(PENTBIT[28]); + info("LL "); + newtable = 2; + break; + case 8: + /* UL LL */ + binaryString.append(PENTBIT[31]); + info("UL "); + binaryString.append(PENTBIT[28]); + info("LL "); + newtable = 2; + break; + case 16: + /* UL LL */ + binaryString.append(QUADBIT[14]); + info("UL "); + binaryString.append(PENTBIT[28]); + info("LL "); + newtable = 2; + break; + } + break; + case (64 + 4): + /* To MIXED */ + switch (curtable) { + case 1: + /* ML */ + binaryString.append(PENTBIT[29]); + info("ML "); + newtable = 4; + break; + case 2: + /* ML */ + binaryString.append(PENTBIT[29]); + info("ML "); + newtable = 4; + break; + case 8: + /* UL ML */ + binaryString.append(PENTBIT[31]); + info("UL "); + binaryString.append(PENTBIT[29]); + info("ML "); + newtable = 4; + break; + case 16: + /* UL ML */ + binaryString.append(QUADBIT[14]); + info("UL "); + binaryString.append(PENTBIT[29]); + info("ML "); + newtable = 4; + break; + } + break; + case (64 + 8): + /* To PUNC */ + switch (curtable) { + case 1: + /* PS */ + binaryString.append(PENTBIT[0]); + info("PS "); + break; + case 2: + /* PS */ + binaryString.append(PENTBIT[0]); + info("PS "); + break; + case 4: + /* PS */ + binaryString.append(PENTBIT[0]); + info("PS "); + break; + case 16: + /* PS */ + binaryString.append(QUADBIT[0]); + info("PS "); + break; + } + break; + case (64 + 16): + /* To DIGIT */ + switch (curtable) { + case 1: + /* DL */ + binaryString.append(PENTBIT[30]); + info("DL "); + newtable = 16; + break; + case 2: + /* DL */ + binaryString.append(PENTBIT[30]); + info("DL "); + newtable = 16; + break; + case 4: + /* UL DL */ + binaryString.append(PENTBIT[29]); + info("UL "); + binaryString.append(PENTBIT[30]); + info("DL "); + newtable = 16; + break; + case 8: + /* UL DL */ + binaryString.append(PENTBIT[31]); + info("UL "); + binaryString.append(PENTBIT[30]); + info("DL "); + newtable = 16; + break; + } + break; + } + } else { + /* Latch character */ + switch (typemap[i]) { + case 1: + /* To UPPER */ + switch (curtable) { + case 2: + /* ML UL */ + binaryString.append(PENTBIT[29]); + info("ML "); + binaryString.append(PENTBIT[29]); + info("UL "); + newtable = 1; + break; + case 4: + /* UL */ + binaryString.append(PENTBIT[29]); + info("UL "); + newtable = 1; + break; + case 8: + /* UL */ + binaryString.append(PENTBIT[31]); + info("UL "); + newtable = 1; + break; + case 16: + /* UL */ + binaryString.append(QUADBIT[14]); + info("UL "); + newtable = 1; + break; + } + break; + case 2: + /* To LOWER */ + switch (curtable) { + case 1: + /* LL */ + binaryString.append(PENTBIT[28]); + info("LL "); + newtable = 2; + break; + case 4: + /* LL */ + binaryString.append(PENTBIT[28]); + info("LL "); + newtable = 2; + break; + case 8: + /* UL LL */ + binaryString.append(PENTBIT[31]); + info("UL "); + binaryString.append(PENTBIT[28]); + info("LL "); + newtable = 2; + break; + case 16: + /* UL LL */ + binaryString.append(QUADBIT[14]); + info("UL "); + binaryString.append(PENTBIT[28]); + info("LL "); + newtable = 2; + break; + } + break; + case 4: + /* To MIXED */ + switch (curtable) { + case 1: + /* ML */ + binaryString.append(PENTBIT[29]); + info("ML "); + newtable = 4; + break; + case 2: + /* ML */ + binaryString.append(PENTBIT[29]); + info("ML "); + newtable = 4; + break; + case 8: + /* UL ML */ + binaryString.append(PENTBIT[31]); + info("UL "); + binaryString.append(PENTBIT[29]); + info("ML "); + newtable = 4; + break; + case 16: + /* UL ML */ + binaryString.append(QUADBIT[14]); + info("UL "); + binaryString.append(PENTBIT[29]); + info("ML "); + newtable = 4; + break; + } + break; + case 8: + /* To PUNC */ + switch (curtable) { + case 1: + /* ML PL */ + binaryString.append(PENTBIT[29]); + info("ML "); + binaryString.append(PENTBIT[30]); + info("PL "); + newtable = 8; + break; + case 2: + /* ML PL */ + binaryString.append(PENTBIT[29]); + info("ML "); + binaryString.append(PENTBIT[30]); + info("PL "); + newtable = 8; + break; + case 4: + /* PL */ + binaryString.append(PENTBIT[30]); + info("PL "); + newtable = 8; + break; + case 16: + /* UL ML PL */ + binaryString.append(QUADBIT[14]); + info("UL "); + binaryString.append(PENTBIT[29]); + info("ML "); + binaryString.append(PENTBIT[30]); + info("PL "); + newtable = 8; + break; + } + break; + case 16: + /* To DIGIT */ + switch (curtable) { + case 1: + /* DL */ + binaryString.append(PENTBIT[30]); + info("DL "); + newtable = 16; + break; + case 2: + /* DL */ + binaryString.append(PENTBIT[30]); + info("DL "); + newtable = 16; + break; + case 4: + /* UL DL */ + binaryString.append(PENTBIT[29]); + info("UL "); + binaryString.append(PENTBIT[30]); + info("DL "); + newtable = 16; + break; + case 8: + /* UL DL */ + binaryString.append(PENTBIT[31]); + info("UL "); + binaryString.append(PENTBIT[30]); + info("DL "); + newtable = 16; + break; + } + break; + case 32: + /* To BINARY */ + lasttable = curtable; + switch (curtable) { + case 1: + /* BS */ + binaryString.append(PENTBIT[31]); + info("BS "); + newtable = 32; + break; + case 2: + /* BS */ + binaryString.append(PENTBIT[31]); + info("BS "); + newtable = 32; + break; + case 4: + /* BS */ + binaryString.append(PENTBIT[31]); + info("BS "); + newtable = 32; + break; + case 8: + /* UL BS */ + binaryString.append(PENTBIT[31]); + info("UL "); + binaryString.append(PENTBIT[31]); + info("BS "); + lasttable = 1; + newtable = 32; + break; + case 16: + /* UL BS */ + binaryString.append(QUADBIT[14]); + info("UL "); + binaryString.append(PENTBIT[31]); + info("BS "); + lasttable = 1; + newtable = 32; + break; + } + + bytes = 0; + do { + bytes++; + } while (typemap[i + (bytes - 1)] == 32); + bytes--; + + if (bytes > 2079) { + throw OkapiInputException.inputTooLong(); + } + + if (bytes > 31) { + /* Put 00000 followed by 11-bit number of bytes less 31 */ + binaryString.append("00000"); + for (int weight = 0x400; weight > 0; weight = weight >> 1) { + if (((bytes - 31) & weight) != 0) { + binaryString.append('1'); + } else { + binaryString.append('0'); + } + } + } else { + /* Put 5-bit number of bytes */ + for (int weight = 0x10; weight > 0; weight = weight >> 1) { + if ((bytes & weight) != 0) { + binaryString.append('1'); + } else { + binaryString.append('0'); + } + } + } + + break; + } + } + } + /* Add data to the binary string */ + curtable = newtable; + chartype = typemap[i]; + if (chartype > 64) { + chartype -= 64; + } + switch (chartype) { + case 1: + case 2: + case 4: + case 8: + if (charmap[i] >= 400) { + info("FLG(" + (charmap[i] - 400) + ") "); + binaryString.append(TRIBIT[charmap[i] - 400]); + if (charmap[i] != 400) { + /* ECI */ + binaryString.append(eciToBinary()); + } + } else { + binaryString.append(PENTBIT[charmap[i]]); + infoSpace(charmap[i]); + } + break; + case 16: + binaryString.append(QUADBIT[charmap[i]]); + infoSpace(charmap[i]); + break; + case 32: + for (int weight = 0x80; weight > 0; weight = weight >> 1) { + if ((charmap[i] & weight) != 0) { + binaryString.append('1'); + } else { + binaryString.append('0'); + } + } + infoSpace(charmap[i]); + break; + } + } + + infoLine(); + + return binaryString.toString(); + } + + /** Adjusts bit stream so that no codewords are all 0s or all 1s, per Section 7.3.1.2 */ + private StringBuilder adjustBinaryString(String binaryString, boolean compact, int layers) { + + StringBuilder adjustedString = new StringBuilder(); + int codewordSize = getCodewordSize(layers); + int ones = 0; + + /* Insert dummy digits needed to prevent codewords of all 0s or all 1s */ + for (int i = 0; i < binaryString.length(); i++) { + if ((adjustedString.length() + 1) % codewordSize == 0) { + if (ones == codewordSize - 1) { + // codeword of B-1 1s, add dummy 0 + adjustedString.append('0'); + i--; + } else if (ones == 0) { + // codeword of B-1 0s, add dummy 1 + adjustedString.append('1'); + i--; + } else { + // no dummy value needed + adjustedString.append(binaryString.charAt(i)); + } + ones = 0; + } else { + adjustedString.append(binaryString.charAt(i)); + if (binaryString.charAt(i) == '1') { + ones++; + } + } + } + + /* Add padding */ + int adjustedLength = adjustedString.length(); + int remainder = adjustedLength % codewordSize; + int padBits = codewordSize - remainder; + if (padBits == codewordSize) { + padBits = 0; + } + for (int i = 0; i < padBits; i++) { + adjustedString.append('1'); + } + adjustedLength = adjustedString.length(); + + /* Make sure padding didn't create an invalid (all 1s) codeword */ + ones = 0; + for (int i = adjustedLength - codewordSize; i < adjustedLength && i >= 0; i++) { + if (adjustedString.charAt(i) == '1') { + ones++; + } + } + if (ones == codewordSize) { + adjustedString.setCharAt(adjustedLength - 1, '0'); + } + + /* Log the codewords */ + info("Codewords: "); + for (int i = 0; i < (adjustedLength / codewordSize); i++) { + int l = 0, m = (1 << (codewordSize - 1)); + for (int j = 0; j < codewordSize; j++) { + if (adjustedString.charAt((i * codewordSize) + j) == '1') { + l += m; + } + m = m >> 1; + } + infoSpace(l); + } + infoLine(); + + /* Return the adjusted bit string */ + return adjustedString; + } + + private String eciToBinary() { + String eciNumber = Integer.toString(eciMode); + StringBuilder binary = new StringBuilder(4 * eciNumber.length()); + for (int i = 0; i < eciNumber.length(); i++) { + binary.append(QUADBIT[(eciNumber.charAt(i) - '0') + 2]); + infoSpace(eciNumber.charAt(i)); + } + return binary.toString(); + } + + /** Creates the descriptor / mode message, per Section 7.2 */ + private String createDescriptor(boolean compact, int layers, int dataBlocks) { + + StringBuilder descriptor = new StringBuilder(); + int descDataSize; + + if (compact) { + /* The first 2 bits represent the number of layers minus 1 */ + if (((layers - 1) & 0x02) != 0) { + descriptor.append('1'); + } else { + descriptor.append('0'); + } + if (((layers - 1) & 0x01) != 0) { + descriptor.append('1'); + } else { + descriptor.append('0'); + } + /* The next 6 bits represent the number of data blocks minus 1 */ + if (readerInit) { + descriptor.append('1'); + } else { + if (((dataBlocks - 1) & 0x20) != 0) { + descriptor.append('1'); + } else { + descriptor.append('0'); + } + } + for (int i = 0x10; i > 0; i = i >> 1) { + if (((dataBlocks - 1) & i) != 0) { + descriptor.append('1'); + } else { + descriptor.append('0'); + } + } + descDataSize = 2; + } else { + /* The first 5 bits represent the number of layers minus 1 */ + for (int i = 0x10; i > 0; i = i >> 1) { + if (((layers - 1) & i) != 0) { + descriptor.append('1'); + } else { + descriptor.append('0'); + } + } + + /* The next 11 bits represent the number of data blocks minus 1 */ + if (readerInit) { + descriptor.append('1'); + } else { + if (((dataBlocks - 1) & 0x400) != 0) { + descriptor.append('1'); + } else { + descriptor.append('0'); + } + } + for (int i = 0x200; i > 0; i = i >> 1) { + if (((dataBlocks - 1) & i) != 0) { + descriptor.append('1'); + } else { + descriptor.append('0'); + } + } + descDataSize = 4; + } + + infoLine("Mode Message: " + descriptor); + + /* Split into 4-bit codewords */ + int[] desc_data = new int[descDataSize]; + for (int i = 0; i < descDataSize; i++) { + for (int weight = 0; weight < 4; weight++) { + if (descriptor.charAt((i * 4) + weight) == '1') { + desc_data[i] += (8 >> weight); + } + } + } + + /* Add Reed-Solomon error correction with Galois Field GF(16) and prime modulus x^4 + x + 1 (Section 7.2.3) */ + ReedSolomon rs = new ReedSolomon(); + rs.init_gf(0x13); + if (compact) { + rs.init_code(5, 1); + rs.encode(2, desc_data); + int[] desc_ecc = new int[6]; + for (int i = 0; i < 5; i++) { + desc_ecc[i] = rs.getResult(i); + } + for (int i = 0; i < 5; i++) { + for (int weight = 0x08; weight > 0; weight = weight >> 1) { + if ((desc_ecc[4 - i] & weight) != 0) { + descriptor.append('1'); + } else { + descriptor.append('0'); + } + } + } + } else { + rs.init_code(6, 1); + rs.encode(4, desc_data); + int[] desc_ecc = new int[6]; + for (int i = 0; i < 6; i++) { + desc_ecc[i] = rs.getResult(i); + } + for (int i = 0; i < 6; i++) { + for (int weight = 0x08; weight > 0; weight = weight >> 1) { + if ((desc_ecc[5 - i] & weight) != 0) { + descriptor.append('1'); + } else { + descriptor.append('0'); + } + } + } + } + + return descriptor.toString(); + } + + /** Adds error correction data to the specified binary string, which already contains the primary data */ + private void addErrorCorrection(StringBuilder adjustedString, int codewordSize, int dataBlocks, int eccBlocks) { + + int x, poly, startWeight; + + /* Split into codewords and calculate Reed-Solomon error correction codes */ + switch (codewordSize) { + case 6: + x = 32; + poly = 0x43; + startWeight = 0x20; + break; + case 8: + x = 128; + poly = 0x12d; + startWeight = 0x80; + break; + case 10: + x = 512; + poly = 0x409; + startWeight = 0x200; + break; + case 12: + x = 2048; + poly = 0x1069; + startWeight = 0x800; + break; + default: + throw new OkapiInternalException("Unrecognized codeword size: " + codewordSize); + } + + ReedSolomon rs = new ReedSolomon(); + int[] data = new int[dataBlocks + 3]; + int[] ecc = new int[eccBlocks + 3]; + + for (int i = 0; i < dataBlocks; i++) { + for (int weight = 0; weight < codewordSize; weight++) { + if (adjustedString.charAt((i * codewordSize) + weight) == '1') { + data[i] += (x >> weight); + } + } + } + + rs.init_gf(poly); + rs.init_code(eccBlocks, 1); + rs.encode(dataBlocks, data); + + for (int i = 0; i < eccBlocks; i++) { + ecc[i] = rs.getResult(i); + } + + for (int i = (eccBlocks - 1); i >= 0; i--) { + for (int weight = startWeight; weight > 0; weight = weight >> 1) { + if ((ecc[i] & weight) != 0) { + adjustedString.append('1'); + } else { + adjustedString.append('0'); + } + } + } + } + + /** Determines codeword bit length - Table 3 */ + private static int getCodewordSize(int layers) { + if (layers >= 23) { + return 12; + } else if (layers >= 9 && layers <= 22) { + return 10; + } else if (layers >= 3 && layers <= 8) { + return 8; + } else { + assert layers <= 2; + return 6; + } + } +} diff --git a/src/main/java/uk/org/okapibarcode/backend/AztecRune.java b/src/main/java/uk/org/okapibarcode/backend/AztecRune.java index 2a4c2bfc..3fa962e6 100644 --- a/src/main/java/uk/org/okapibarcode/backend/AztecRune.java +++ b/src/main/java/uk/org/okapibarcode/backend/AztecRune.java @@ -1,170 +1,170 @@ -/* - * Copyright 2014 Robin Stuart - * - * 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. - */ - -package uk.org.okapibarcode.backend; - -/** - *

Implements the Aztec Runes bar code symbology according to ISO/IEC 24778:2008 Annex A. - * - *

Aztec Runes is a fixed-size matrix symbology which can encode whole integer values between 0 and 255. - * - * @author Robin Stuart - */ -public class AztecRune extends Symbol { - - private static final int[] BIT_PLACEMENT_MAP = { - 1, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 29, 1, 0, 0, 0, 0, 0, 0, 0, 1, 9, - 28, 1, 0, 1, 1, 1, 1, 1, 0, 1, 10, - 27, 1, 0, 1, 0, 0, 0, 1, 0, 1, 11, - 26, 1, 0, 1, 0, 1, 0, 1, 0, 1, 12, - 25, 1, 0, 1, 0, 0, 0, 1, 0, 1, 13, - 24, 1, 0, 1, 1, 1, 1, 1, 0, 1, 14, - 23, 1, 0, 0, 0, 0, 0, 0, 0, 1, 15, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 22, 21, 20, 19, 18, 17, 16, 0, 0 - }; - - @Override - protected void encode() { - - if (!content.matches("[0-9]+")) { - throw OkapiInputException.invalidCharactersInInput(); - } - - int decimalValue = 0; - switch (content.length()) { - case 1: - decimalValue = (content.charAt(0) - '0'); - break; - case 2: - decimalValue = 10 * (content.charAt(0) - '0'); - decimalValue += (content.charAt(1) - '0'); - break; - case 3: - decimalValue = 100 * (content.charAt(0) - '0'); - decimalValue += 10 * (content.charAt(1) - '0'); - decimalValue += (content.charAt(2) - '0'); - break; - default: - throw new OkapiInputException("Input too large"); - } - - if (decimalValue > 255) { - throw new OkapiInputException("Input too large"); - } - - StringBuilder binaryDataStream = new StringBuilder(28); - for (int i = 0x80; i > 0; i = i >> 1) { - if ((decimalValue & i) != 0) { - binaryDataStream.append('1'); - } else { - binaryDataStream.append('0'); - } - } - - int[] dataCodeword = new int[3]; - dataCodeword[0] = 0; - dataCodeword[1] = 0; - - for (int i = 0; i < 2; i++) { - if (binaryDataStream.charAt(i * 4) == '1') { - dataCodeword[i] += 8; - } - if (binaryDataStream.charAt((i * 4) + 1) == '1') { - dataCodeword[i] += 4; - } - if (binaryDataStream.charAt((i * 4) + 2) == '1') { - dataCodeword[i] += 2; - } - if (binaryDataStream.charAt((i * 4) + 3) == '1') { - dataCodeword[i] += 1; - } - } - - int[] errorCorrectionCodeword = new int[6]; - - ReedSolomon rs = new ReedSolomon(); - rs.init_gf(0x13); - rs.init_code(5, 1); - rs.encode(2, dataCodeword); - - for (int i = 0; i < 5; i++) { - errorCorrectionCodeword[i] = rs.getResult(i); - } - - for (int i = 0; i < 5; i++) { - if ((errorCorrectionCodeword[4 - i] & 0x08) != 0) { - binaryDataStream.append('1'); - } else { - binaryDataStream.append('0'); - } - if ((errorCorrectionCodeword[4 - i] & 0x04) != 0) { - binaryDataStream.append('1'); - } else { - binaryDataStream.append('0'); - } - if ((errorCorrectionCodeword[4 - i] & 0x02) != 0) { - binaryDataStream.append('1'); - } else { - binaryDataStream.append('0'); - } - if ((errorCorrectionCodeword[4 - i] & 0x01) != 0) { - binaryDataStream.append('1'); - } else { - binaryDataStream.append('0'); - } - } - - StringBuilder reversedBinaryDataStream = new StringBuilder(28); - for (int i = 0; i < binaryDataStream.length(); i++) { - if ((i & 1) == 0) { - if (binaryDataStream.charAt(i) == '0') { - reversedBinaryDataStream.append('1'); - } else { - reversedBinaryDataStream.append('0'); - } - } else { - reversedBinaryDataStream.append(binaryDataStream.charAt(i)); - } - } - - infoLine("Binary: " + reversedBinaryDataStream); - - readable = ""; - pattern = new String[11]; - row_count = 11; - row_height = new int[11]; - - for (int row = 0; row < 11; row++) { - StringBuilder rowBinary = new StringBuilder(11); - for (int column = 0; column < 11; column++) { - if (BIT_PLACEMENT_MAP[(row * 11) + column] == 1) { - rowBinary.append('1'); - } - if (BIT_PLACEMENT_MAP[(row * 11) + column] == 0) { - rowBinary.append('0'); - } - if (BIT_PLACEMENT_MAP[(row * 11) + column] >= 2) { - rowBinary.append(reversedBinaryDataStream.charAt(BIT_PLACEMENT_MAP[(row * 11) + column] - 2)); - } - } - pattern[row] = bin2pat(rowBinary); - row_height[row] = 1; - } - } -} +/* + * Copyright 2014 Robin Stuart + * + * 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. + */ + +package uk.org.okapibarcode.backend; + +/** + *

Implements the Aztec Runes bar code symbology according to ISO/IEC 24778:2008 Annex A. + * + *

Aztec Runes is a fixed-size matrix symbology which can encode whole integer values between 0 and 255. + * + * @author Robin Stuart + */ +public class AztecRune extends Symbol { + + private static final int[] BIT_PLACEMENT_MAP = { + 1, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 29, 1, 0, 0, 0, 0, 0, 0, 0, 1, 9, + 28, 1, 0, 1, 1, 1, 1, 1, 0, 1, 10, + 27, 1, 0, 1, 0, 0, 0, 1, 0, 1, 11, + 26, 1, 0, 1, 0, 1, 0, 1, 0, 1, 12, + 25, 1, 0, 1, 0, 0, 0, 1, 0, 1, 13, + 24, 1, 0, 1, 1, 1, 1, 1, 0, 1, 14, + 23, 1, 0, 0, 0, 0, 0, 0, 0, 1, 15, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 22, 21, 20, 19, 18, 17, 16, 0, 0 + }; + + @Override + protected void encode() { + + if (!content.matches("[0-9]+")) { + throw OkapiInputException.invalidCharactersInInput(); + } + + int decimalValue = 0; + switch (content.length()) { + case 1: + decimalValue = (content.charAt(0) - '0'); + break; + case 2: + decimalValue = 10 * (content.charAt(0) - '0'); + decimalValue += (content.charAt(1) - '0'); + break; + case 3: + decimalValue = 100 * (content.charAt(0) - '0'); + decimalValue += 10 * (content.charAt(1) - '0'); + decimalValue += (content.charAt(2) - '0'); + break; + default: + throw new OkapiInputException("Input too large"); + } + + if (decimalValue > 255) { + throw new OkapiInputException("Input too large"); + } + + StringBuilder binaryDataStream = new StringBuilder(28); + for (int i = 0x80; i > 0; i = i >> 1) { + if ((decimalValue & i) != 0) { + binaryDataStream.append('1'); + } else { + binaryDataStream.append('0'); + } + } + + int[] dataCodeword = new int[3]; + dataCodeword[0] = 0; + dataCodeword[1] = 0; + + for (int i = 0; i < 2; i++) { + if (binaryDataStream.charAt(i * 4) == '1') { + dataCodeword[i] += 8; + } + if (binaryDataStream.charAt((i * 4) + 1) == '1') { + dataCodeword[i] += 4; + } + if (binaryDataStream.charAt((i * 4) + 2) == '1') { + dataCodeword[i] += 2; + } + if (binaryDataStream.charAt((i * 4) + 3) == '1') { + dataCodeword[i] += 1; + } + } + + int[] errorCorrectionCodeword = new int[6]; + + ReedSolomon rs = new ReedSolomon(); + rs.init_gf(0x13); + rs.init_code(5, 1); + rs.encode(2, dataCodeword); + + for (int i = 0; i < 5; i++) { + errorCorrectionCodeword[i] = rs.getResult(i); + } + + for (int i = 0; i < 5; i++) { + if ((errorCorrectionCodeword[4 - i] & 0x08) != 0) { + binaryDataStream.append('1'); + } else { + binaryDataStream.append('0'); + } + if ((errorCorrectionCodeword[4 - i] & 0x04) != 0) { + binaryDataStream.append('1'); + } else { + binaryDataStream.append('0'); + } + if ((errorCorrectionCodeword[4 - i] & 0x02) != 0) { + binaryDataStream.append('1'); + } else { + binaryDataStream.append('0'); + } + if ((errorCorrectionCodeword[4 - i] & 0x01) != 0) { + binaryDataStream.append('1'); + } else { + binaryDataStream.append('0'); + } + } + + StringBuilder reversedBinaryDataStream = new StringBuilder(28); + for (int i = 0; i < binaryDataStream.length(); i++) { + if ((i & 1) == 0) { + if (binaryDataStream.charAt(i) == '0') { + reversedBinaryDataStream.append('1'); + } else { + reversedBinaryDataStream.append('0'); + } + } else { + reversedBinaryDataStream.append(binaryDataStream.charAt(i)); + } + } + + infoLine("Binary: " + reversedBinaryDataStream); + + readable = ""; + pattern = new String[11]; + row_count = 11; + row_height = new int[11]; + + for (int row = 0; row < 11; row++) { + StringBuilder rowBinary = new StringBuilder(11); + for (int column = 0; column < 11; column++) { + if (BIT_PLACEMENT_MAP[(row * 11) + column] == 1) { + rowBinary.append('1'); + } + if (BIT_PLACEMENT_MAP[(row * 11) + column] == 0) { + rowBinary.append('0'); + } + if (BIT_PLACEMENT_MAP[(row * 11) + column] >= 2) { + rowBinary.append(reversedBinaryDataStream.charAt(BIT_PLACEMENT_MAP[(row * 11) + column] - 2)); + } + } + pattern[row] = bin2pat(rowBinary); + row_height[row] = 1; + } + } +} diff --git a/src/main/java/uk/org/okapibarcode/backend/ChannelCode.java b/src/main/java/uk/org/okapibarcode/backend/ChannelCode.java index 322b6c8e..7271bd11 100644 --- a/src/main/java/uk/org/okapibarcode/backend/ChannelCode.java +++ b/src/main/java/uk/org/okapibarcode/backend/ChannelCode.java @@ -1,158 +1,158 @@ -/* - * Copyright 2014 Robin Stuart - * - * 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. - */ - -package uk.org.okapibarcode.backend; - -/** - *

Implements Channel Code according to ANSI/AIM BC12-1998. - * - *

Channel Code encodes whole integer values between 0 and 7,742,862. - * - * @author Robin Stuart - */ -public class ChannelCode extends Symbol { - - private int preferredNumberOfChannels = 3; - - private int[] space = new int[11]; - private int[] bar = new int[11]; - private int currentValue; - private int targetValue; - - /** - * Sets the preferred number of channels used to encode data. This setting will be - * ignored if the value to be encoded requires more channels. - * - * @param channels the preferred number of channels (3 to 8, inclusive) - */ - public void setPreferredNumberOfChannels(int channels) { - if (channels < 3 || channels > 8) { - throw new IllegalArgumentException("Invalid number of channels: " + channels); - } - preferredNumberOfChannels = channels; - } - - /** - * Returns the preferred number of channels used to encode data. - * - * @return the preferred number of channels used to encode data - */ - public int getPreferredNumberOfChannels() { - return preferredNumberOfChannels; - } - - @Override - protected void encode() { - - if (content.length() > 7) { - throw OkapiInputException.inputTooLong(); - } - - if (!content.matches("[0-9]+")) { - throw OkapiInputException.invalidCharactersInInput(); - } - - int channels = preferredNumberOfChannels; - targetValue = Integer.parseInt(content); - switch (channels) { - case 3: - if (targetValue > 26) { - channels++; - } - case 4: - if (targetValue > 292) { - channels++; - } - case 5: - if (targetValue > 3493) { - channels++; - } - case 6: - if (targetValue > 44072) { - channels++; - } - case 7: - if (targetValue > 576688) { - channels++; - } - case 8: - if (targetValue > 7742862) { - channels++; - } - } - - if (channels == 9) { - throw new OkapiInputException("Value out of range"); - } - - infoLine("Channels Used: " + channels); - - for (int i = 0; i < 11; i++) { - bar[i] = 0; - space[i] = 0; - } - - bar[0] = space[1] = bar[1] = space[2] = bar[2] = 1; - currentValue = 0; - pattern = new String[1]; - nextSpace(channels, 3, channels, channels); - - StringBuilder text = new StringBuilder(); - int leadingZeroCount = channels - 1 - content.length(); - for (int i = 0; i < leadingZeroCount; i++) { - text.append('0'); - } - text.append(content); - - readable = text.toString(); - row_count = 1; - row_height = new int[] { -1 }; - } - - private void nextSpace(int channels, int i, int maxSpace, int maxBar) { - for (int s = (i < channels + 2) ? 1 : maxSpace; s <= maxSpace; s++) { - space[i] = s; - nextBar(channels, i, maxBar, maxSpace + 1 - s); - } - } - - private void nextBar(int channels, int i, int maxBar, int maxSpace) { - int b = (space[i] + bar[i - 1] + space[i - 1] + bar[i - 2] > 4) ? 1 : 2; - if (i < channels + 2) { - for (; b <= maxBar; b++) { - bar[i] = b; - nextSpace(channels, i + 1, maxSpace, maxBar + 1 - b); - } - } else if (b <= maxBar) { - bar[i] = maxBar; - checkIfDone(); - currentValue++; - } - } - - private void checkIfDone() { - if (currentValue == targetValue) { - /* Target reached - save the generated pattern */ - StringBuilder sb = new StringBuilder(); - sb.append("11110"); - for (int i = 0; i < 11; i++) { - sb.append((char) (space[i] + '0')); - sb.append((char) (bar[i] + '0')); - } - pattern[0] = sb.toString(); - } - } -} +/* + * Copyright 2014 Robin Stuart + * + * 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. + */ + +package uk.org.okapibarcode.backend; + +/** + *

Implements Channel Code according to ANSI/AIM BC12-1998. + * + *

Channel Code encodes whole integer values between 0 and 7,742,862. + * + * @author Robin Stuart + */ +public class ChannelCode extends Symbol { + + private int preferredNumberOfChannels = 3; + + private int[] space = new int[11]; + private int[] bar = new int[11]; + private int currentValue; + private int targetValue; + + /** + * Sets the preferred number of channels used to encode data. This setting will be + * ignored if the value to be encoded requires more channels. + * + * @param channels the preferred number of channels (3 to 8, inclusive) + */ + public void setPreferredNumberOfChannels(int channels) { + if (channels < 3 || channels > 8) { + throw new IllegalArgumentException("Invalid number of channels: " + channels); + } + preferredNumberOfChannels = channels; + } + + /** + * Returns the preferred number of channels used to encode data. + * + * @return the preferred number of channels used to encode data + */ + public int getPreferredNumberOfChannels() { + return preferredNumberOfChannels; + } + + @Override + protected void encode() { + + if (content.length() > 7) { + throw OkapiInputException.inputTooLong(); + } + + if (!content.matches("[0-9]+")) { + throw OkapiInputException.invalidCharactersInInput(); + } + + int channels = preferredNumberOfChannels; + targetValue = Integer.parseInt(content); + switch (channels) { + case 3: + if (targetValue > 26) { + channels++; + } + case 4: + if (targetValue > 292) { + channels++; + } + case 5: + if (targetValue > 3493) { + channels++; + } + case 6: + if (targetValue > 44072) { + channels++; + } + case 7: + if (targetValue > 576688) { + channels++; + } + case 8: + if (targetValue > 7742862) { + channels++; + } + } + + if (channels == 9) { + throw new OkapiInputException("Value out of range"); + } + + infoLine("Channels Used: " + channels); + + for (int i = 0; i < 11; i++) { + bar[i] = 0; + space[i] = 0; + } + + bar[0] = space[1] = bar[1] = space[2] = bar[2] = 1; + currentValue = 0; + pattern = new String[1]; + nextSpace(channels, 3, channels, channels); + + StringBuilder text = new StringBuilder(); + int leadingZeroCount = channels - 1 - content.length(); + for (int i = 0; i < leadingZeroCount; i++) { + text.append('0'); + } + text.append(content); + + readable = text.toString(); + row_count = 1; + row_height = new int[] { -1 }; + } + + private void nextSpace(int channels, int i, int maxSpace, int maxBar) { + for (int s = (i < channels + 2) ? 1 : maxSpace; s <= maxSpace; s++) { + space[i] = s; + nextBar(channels, i, maxBar, maxSpace + 1 - s); + } + } + + private void nextBar(int channels, int i, int maxBar, int maxSpace) { + int b = (space[i] + bar[i - 1] + space[i - 1] + bar[i - 2] > 4) ? 1 : 2; + if (i < channels + 2) { + for (; b <= maxBar; b++) { + bar[i] = b; + nextSpace(channels, i + 1, maxSpace, maxBar + 1 - b); + } + } else if (b <= maxBar) { + bar[i] = maxBar; + checkIfDone(); + currentValue++; + } + } + + private void checkIfDone() { + if (currentValue == targetValue) { + /* Target reached - save the generated pattern */ + StringBuilder sb = new StringBuilder(); + sb.append("11110"); + for (int i = 0; i < 11; i++) { + sb.append((char) (space[i] + '0')); + sb.append((char) (bar[i] + '0')); + } + pattern[0] = sb.toString(); + } + } +} diff --git a/src/main/java/uk/org/okapibarcode/backend/CodablockF.java b/src/main/java/uk/org/okapibarcode/backend/CodablockF.java index 29e73921..19a91400 100644 --- a/src/main/java/uk/org/okapibarcode/backend/CodablockF.java +++ b/src/main/java/uk/org/okapibarcode/backend/CodablockF.java @@ -1,859 +1,859 @@ -/* - * Copyright 2014 Robin Stuart - * - * 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. - */ - -package uk.org.okapibarcode.backend; - -import java.nio.charset.StandardCharsets; - -import uk.org.okapibarcode.graphics.Rectangle; - -/** - *

Implements Codablock-F according to AIM Europe "Uniform Symbology Specification - Codablock F", 1995. - * - *

Codablock-F is a multi-row symbology using Code 128 encoding. It can encode any 8-bit ISO 8859-1 (Latin-1) - * data up to approximately 1000 alpha-numeric characters or 2000 numeric digits in length. - * - * @author Robin Stuart - */ -public class CodablockF extends Symbol { - - private enum Mode { - SHIFTA, LATCHA, SHIFTB, LATCHB, SHIFTC, LATCHC, AORB, ABORC, CANDB, CANDBB - } - - private enum CfMode { - MODEA, MODEB, MODEC - } - - /* Annex A Table A.1 */ - private static final String[] C_128_TABLE = { - "212222", "222122", "222221", "121223", "121322", "131222", "122213", - "122312", "132212", "221213", "221312", "231212", "112232", "122132", "122231", "113222", - "123122", "123221", "223211", "221132", "221231", "213212", "223112", "312131", "311222", - "321122", "321221", "312212", "322112", "322211", "212123", "212321", "232121", "111323", - "131123", "131321", "112313", "132113", "132311", "211313", "231113", "231311", "112133", - "112331", "132131", "113123", "113321", "133121", "313121", "211331", "231131", "213113", - "213311", "213131", "311123", "311321", "331121", "312113", "312311", "332111", "314111", - "221411", "431111", "111224", "111422", "121124", "121421", "141122", "141221", "112214", - "112412", "122114", "122411", "142112", "142211", "241211", "221114", "413111", "241112", - "134111", "111242", "121142", "121241", "114212", "124112", "124211", "411212", "421112", - "421211", "212141", "214121", "412121", "111143", "111341", "131141", "114113", "114311", - "411113", "411311", "113141", "114131", "311141", "411131", "211412", "211214", "211232", - "2331112"}; - - private int[][] blockmatrix = new int[44][62]; - private int columns_needed; - private int rows_needed; - private CfMode final_mode; - private CfMode[] subset_selector = new CfMode[44]; - - /** - * TODO: It doesn't appear that this symbol should support GS1 (it's not in the GS1 spec and Zint doesn't - * support GS1 with this type of symbology). However, the code below does contain GS1 checks, so we'll mark - * it as supported for now. It's very possible that the code below which supports GS1 only does so because - * it was originally copied from the Code 128 source code (just a suspicion, though). - */ - @Override - protected boolean gs1Supported() { - return true; - } - - @Override - protected void encode() { - - int input_length, i, j, k; - int min_module_height; - Mode last_mode, this_mode; - double estimate_codelength; - String row_pattern; - int[] row_indicator = new int[44]; - int[] row_check = new int[44]; - int k1_sum, k2_sum; - int k1_check, k2_check; - - final_mode = CfMode.MODEA; - - if (!content.matches("[\u0000-\u00FF]*")) { - throw OkapiInputException.invalidCharactersInInput(); - } - - inputData = toBytes(content, StandardCharsets.ISO_8859_1, 0x00); - input_length = inputData.length - 1; - - if (input_length > 5450) { - throw OkapiInputException.inputTooLong(); - } - - /* Make a guess at how many characters will be needed to encode the data */ - estimate_codelength = 0.0; - last_mode = Mode.AORB; /* Codablock always starts with Code A */ - for (i = 0; i < input_length; i++) { - this_mode = findSubset(inputData[i]); - if (this_mode != last_mode) { - estimate_codelength += 1.0; - } - if (this_mode != Mode.ABORC) { - estimate_codelength += 1.0; - } else { - estimate_codelength += 0.5; - } - if (inputData[i] > 127) { - estimate_codelength += 1.0; - } - last_mode = this_mode; - } - - /* Decide symbol size based on the above guess */ - rows_needed = (int) (0.5 + Math.sqrt((estimate_codelength + 2) / 1.45)); - if (rows_needed < 2) { - rows_needed = 2; - } - if (rows_needed > 44) { - rows_needed = 44; - } - columns_needed = (int) (estimate_codelength + 2) / rows_needed; - if (columns_needed < 4) { - columns_needed = 4; - } - if (columns_needed > 62) { - throw OkapiInputException.inputTooLong(); - } - - /* Encode the data */ - data_encode_blockf(); - - /* Add check digits - Annex F */ - k1_sum = 0; - k2_sum = 0; - for(i = 0; i < input_length; i++) { - if(inputData[i] == FNC1) { - k1_sum += (i + 1) * 29; /* GS */ - k2_sum += i * 29; - } else { - k1_sum += (i + 1) * inputData[i]; - k2_sum += i * inputData[i]; - } - } - k1_check = k1_sum % 86; - k2_check = k2_sum % 86; - if((final_mode == CfMode.MODEA) || (final_mode == CfMode.MODEB)) { - k1_check = k1_check + 64; - if(k1_check > 95) { k1_check -= 96; } - k2_check = k2_check + 64; - if(k2_check > 95) { k2_check -= 96; } - } - blockmatrix[rows_needed - 1][columns_needed - 2] = k1_check; - blockmatrix[rows_needed - 1][columns_needed - 1] = k2_check; - - /* Calculate row height (4.6.1.a) */ - min_module_height = (int) (0.55 * (columns_needed + 3)) + 3; - if(min_module_height < 8) { min_module_height = 8; } - - /* Encode the Row Indicator in the First Row of the Symbol - Table D2 */ - if(subset_selector[0] == CfMode.MODEC) { - /* Code C */ - row_indicator[0] = rows_needed - 2; - } else { - /* Code A or B */ - row_indicator[0] = rows_needed + 62; - - if(row_indicator[0] > 95) { - row_indicator[0] -= 95; - } - } - - /* Encode the Row Indicator in the Second and Subsequent Rows of the Symbol - Table D3 */ - for(i = 1; i < rows_needed; i++) { - /* Note that the second row is row number 1 because counting starts from 0 */ - if(subset_selector[i] == CfMode.MODEC) { - /* Code C */ - row_indicator[i] = i + 42; - } else { - /* Code A or B */ - if( i < 6 ) - row_indicator[i] = i + 10; - else - row_indicator[i] = i + 20; - } - } - - /* Calculate row check digits - Annex E */ - for(i = 0; i < rows_needed; i++) { - k = 103; - switch (subset_selector[i]) { - case MODEA: k += 98; - break; - case MODEB: k += 100; - break; - case MODEC: k += 99; - break; - } - k += 2 * row_indicator[i]; - for(j = 0; j < columns_needed; j++) { - k+= (j + 3) * blockmatrix[i][j]; - } - row_check[i] = k % 103; - } - - readable = ""; - row_count = rows_needed; - pattern = new String[row_count]; - row_height = new int[row_count]; - - infoLine("Grid Size: " + columns_needed + " X " + rows_needed); - infoLine("K1 Check Digit: " + k1_check); - infoLine("K2 Check Digit: " + k2_check); - - /* Resolve the data into patterns and place in symbol structure */ - info("Encoding: "); - for(i = 0; i < rows_needed; i++) { - - row_pattern = ""; - /* Start character */ - row_pattern += C_128_TABLE[103]; /* Always Start A */ - - switch (subset_selector[i]) { - case MODEA: - row_pattern += C_128_TABLE[98]; - info("MODEA "); - break; - case MODEB: - row_pattern += C_128_TABLE[100]; - info("MODEB "); - break; - case MODEC: - row_pattern += C_128_TABLE[99]; - info("MODEC "); - break; - } - row_pattern += C_128_TABLE[row_indicator[i]]; - infoSpace(row_indicator[i]); - - for (j = 0; j < columns_needed; j++) { - row_pattern += C_128_TABLE[blockmatrix[i][j]]; - infoSpace(blockmatrix[i][j]); - } - - row_pattern += C_128_TABLE[row_check[i]]; - info("(" + row_check[i] + ") "); - - /* Stop character */ - row_pattern += C_128_TABLE[106]; - - /* Write the information into the symbol */ - pattern[i] = row_pattern; - row_height[i] = 15; - } - infoLine(); - - symbol_height = rows_needed * 15; - } - - private Mode findSubset(int letter) { - Mode mode; - - if (letter == FNC1) { - mode = Mode.AORB; - } else if (letter <= 31) { - mode = Mode.SHIFTA; - } else if ((letter >= 48) && (letter <= 57)) { - mode = Mode.ABORC; - } else if (letter <= 95) { - mode = Mode.AORB; - } else if (letter <= 127) { - mode = Mode.SHIFTB; - } else if (letter <= 159) { - mode = Mode.SHIFTA; - } else if (letter <= 223) { - mode = Mode.AORB; - } else { - mode = Mode.SHIFTB; - } - - return mode; - } - - private void data_encode_blockf() { - - int i, j, input_position, current_row; - int column_position, c; - CfMode current_mode; - boolean done, exit_status; - int input_length = inputData.length - 1; - - exit_status = false; - current_row = 0; - current_mode = CfMode.MODEA; - column_position = 0; - input_position = 0; - c = 0; - - do { - done = false; - /* 'done' ensures that the instructions are followed in the correct order for each input character */ - - if (column_position == 0) { - /* The Beginning of a row */ - c = columns_needed; - current_mode = character_subset_select(input_position); - subset_selector[current_row] = current_mode; - if ((current_row == 0) && (inputDataType == DataType.GS1)) { - /* Section 4.4.7.1 */ - blockmatrix[current_row][column_position] = 102; /* FNC1 */ - column_position++; - c--; - } - } - - if (inputData[input_position] == FNC1) { - blockmatrix[current_row][column_position] = 102; /* FNC1 */ - column_position++; - c--; - input_position++; - done = true; - } - - if (!done) { - if (c <= 2) { - /* Annex B section 1 rule 1 */ - /* Ensure that there is sufficient encodation capacity to continue (using the rules of Annex B.2). */ - switch (current_mode) { - case MODEA: /* Table B1 applies */ - if (findSubset(inputData[input_position]) == Mode.ABORC) { - blockmatrix[current_row][column_position] = a3_convert(inputData[input_position]); - column_position++; - c--; - input_position++; - done = true; - } - - if ((findSubset(inputData[input_position]) == Mode.SHIFTB) && (c == 1)) { - /* Needs two symbols */ - blockmatrix[current_row][column_position] = 100; /* Code B */ - column_position++; - c--; - done = true; - } - - if ((inputData[input_position] >= 244) && (!done)) { - /* Needs three symbols */ - blockmatrix[current_row][column_position] = 100; /* Code B */ - column_position++; - c--; - if (c == 1) { - blockmatrix[current_row][column_position] = 101; /* Code A */ - column_position++; - c--; - } - done = true; - } - - if ((inputData[input_position] >= 128) && (!done) && c == 1) { - /* Needs two symbols */ - blockmatrix[current_row][column_position] = 100; /* Code B */ - column_position++; - c--; - done = true; - } - break; - case MODEB: /* Table B2 applies */ - if (findSubset(inputData[input_position]) == Mode.ABORC) { - blockmatrix[current_row][column_position] = a3_convert(inputData[input_position]); - column_position++; - c--; - input_position++; - done = true; - } - - if ((findSubset(inputData[input_position]) == Mode.SHIFTA) && (c == 1)) { - /* Needs two symbols */ - blockmatrix[current_row][column_position] = 101; /* Code A */ - column_position++; - c--; - done = true; - } - - if (((inputData[input_position] >= 128) - && (inputData[input_position] <= 159)) && (!done)) { - /* Needs three symbols */ - blockmatrix[current_row][column_position] = 101; /* Code A */ - column_position++; - c--; - if (c == 1) { - blockmatrix[current_row][column_position] = 100; /* Code B */ - column_position++; - c--; - } - done = true; - } - - if ((inputData[input_position] >= 160) && (!done) && c == 1) { - /* Needs two symbols */ - blockmatrix[current_row][column_position] = 101; /* Code A */ - column_position++; - c--; - done = true; - } - break; - case MODEC: /* Table B3 applies */ - if ((findSubset(inputData[input_position]) != Mode.ABORC) && (c == 1)) { - /* Needs two symbols */ - blockmatrix[current_row][column_position] = 101; /* Code A */ - column_position++; - c--; - done = true; - } - - if (((findSubset(inputData[input_position]) == Mode.ABORC) - && (findSubset(inputData[input_position + 1]) != Mode.ABORC)) - && (c == 1)) { - /* Needs two symbols */ - blockmatrix[current_row][column_position] = 101; /* Code A */ - column_position++; - c--; - done = true; - } - - if (inputData[input_position] >= 128) { - /* Needs three symbols */ - blockmatrix[current_row][column_position] = 101; /* Code A */ - column_position++; - c--; - if (c == 1) { - blockmatrix[current_row][column_position] = 100; /* Code B */ - column_position++; - c--; - } - } - break; - } - } - } - - if (!done) { - if (((findSubset(inputData[input_position]) == Mode.AORB) - || (findSubset(inputData[input_position]) == Mode.SHIFTA)) - && (current_mode == CfMode.MODEA)) { - /* Annex B section 1 rule 2 */ - /* If in Code Subset A and the next data character can be encoded in Subset A encode the next - character. */ - if (inputData[input_position] >= 128) { - /* Extended ASCII character */ - blockmatrix[current_row][column_position] = 101; /* FNC4 */ - column_position++; - c--; - } - blockmatrix[current_row][column_position] = a3_convert(inputData[input_position]); - column_position++; - c--; - input_position++; - done = true; - } - } - - if (!done) { - if (((findSubset(inputData[input_position]) == Mode.AORB) - || (findSubset(inputData[input_position]) == Mode.SHIFTB)) - && (current_mode == CfMode.MODEB)) { - /* Annex B section 1 rule 3 */ - /* If in Code Subset B and the next data character can be encoded in subset B, encode the next - character. */ - if (inputData[input_position] >= 128) { - /* Extended ASCII character */ - blockmatrix[current_row][column_position] = 100; /* FNC4 */ - column_position++; - c--; - } - blockmatrix[current_row][column_position] = a3_convert(inputData[input_position]); - column_position++; - c--; - input_position++; - done = true; - } - } - - if (!done) { - if (((findSubset(inputData[input_position]) == Mode.ABORC) - && (findSubset(inputData[input_position + 1]) == Mode.ABORC)) - && (current_mode == CfMode.MODEC)) { - /* Annex B section 1 rule 4 */ - /* If in Code Subset C and the next data are 2 digits, encode them. */ - blockmatrix[current_row][column_position] - = ((inputData[input_position] - '0') * 10) - + (inputData[input_position + 1] - '0'); - column_position++; - c--; - input_position += 2; - done = true; - } - } - - if (!done) { - if (((current_mode == CfMode.MODEA) || (current_mode == CfMode.MODEB)) - && ((findSubset(inputData[input_position]) == Mode.ABORC) - || (inputData[input_position] == FNC1))) { - // Count the number of numeric digits - // If 4 or more numeric data characters occur together when in subsets A or B: - // a. If there is an even number of numeric data characters, insert a Code C character before the - // first numeric digit to change to subset C. - // b. If there is an odd number of numeric data characters, insert a Code Set C character immediately - // after the first numeric digit to change to subset C. - i = 0; - j = 0; - do { - i++; - if (inputData[input_position + j] == FNC1) { - i++; - } - j++; - } while ((findSubset(inputData[input_position + j]) == Mode.ABORC) - || (inputData[input_position + j] == FNC1)); - i--; - - if (i >= 4) { - /* Annex B section 1 rule 5 */ - if ((i % 2) == 1) { - /* Annex B section 1 rule 5a */ - blockmatrix[current_row][column_position] = 99; /* Code C */ - column_position++; - c--; - blockmatrix[current_row][column_position] = ((inputData[input_position] - '0') * 10) - + (inputData[input_position + 1] - '0'); - column_position++; - c--; - input_position += 2; - current_mode = CfMode.MODEC; - } else { - /* Annex B section 1 rule 5b */ - blockmatrix[current_row][column_position] = a3_convert(inputData[input_position]); - column_position++; - c--; - input_position++; - } - done = true; - } else { - blockmatrix[current_row][column_position] = a3_convert(inputData[input_position]); - column_position++; - c--; - input_position++; - done = true; - } - } - } - - if (!done) { - if ((current_mode == CfMode.MODEB) && (findSubset(inputData[input_position]) == Mode.SHIFTA)) { - /* Annex B section 1 rule 6 */ - /* When in subset B and an ASCII control character occurs in the data: - a. If there is a lower case character immediately following the control character, insert a Shift - character before the control character. - b. Otherwise, insert a Code A character before the control character to change to subset A. */ - if ((inputData[input_position + 1] >= 96) && (inputData[input_position + 1] <= 127)) { - /* Annex B section 1 rule 6a */ - blockmatrix[current_row][column_position] = 98; /* Shift */ - column_position++; - c--; - if (inputData[input_position] >= 128) { - /* Extended ASCII character */ - blockmatrix[current_row][column_position] = 100; /* FNC4 */ - column_position++; - c--; - } - blockmatrix[current_row][column_position] = a3_convert(inputData[input_position]); - column_position++; - c--; - input_position++; - } else { - /* Annex B section 1 rule 6b */ - blockmatrix[current_row][column_position] = 101; /* Code A */ - column_position++; - c--; - if (inputData[input_position] >= 128) { - /* Extended ASCII character */ - blockmatrix[current_row][column_position] = 100; /* FNC4 */ - column_position++; - c--; - } - blockmatrix[current_row][column_position] = a3_convert(inputData[input_position]); - column_position++; - c--; - input_position++; - current_mode = CfMode.MODEA; - } - done = true; - } - } - - if (!done) { - if ((current_mode == CfMode.MODEA) && (findSubset(inputData[input_position]) == Mode.SHIFTB)) { - /* Annex B section 1 rule 7 */ - /* When in subset A and a lower case character occurs in the data: - a. If following that character, a control character occurs in the data before the occurrence of - another lower case character, insert a Shift character before the lower case character. - b. Otherwise, insert a Code B character before the lower case character to change to subset B. */ - if ((findSubset(inputData[input_position + 1]) == Mode.SHIFTA) - && (findSubset(inputData[input_position + 2]) == Mode.SHIFTB)) { - /* Annex B section 1 rule 7a */ - blockmatrix[current_row][column_position] = 98; /* Shift */ - column_position++; - c--; - if (inputData[input_position] >= 128) { - /* Extended ASCII character */ - blockmatrix[current_row][column_position] = 101; /* FNC4 */ - column_position++; - c--; - } - blockmatrix[current_row][column_position] = a3_convert(inputData[input_position]); - column_position++; - c--; - input_position++; - } else { - /* Annex B section 1 rule 7b */ - blockmatrix[current_row][column_position] = 100; /* Code B */ - column_position++; - c--; - if (inputData[input_position] >= 128) { - /* Extended ASCII character */ - blockmatrix[current_row][column_position] = 101; /* FNC4 */ - column_position++; - c--; - } - blockmatrix[current_row][column_position] = a3_convert(inputData[input_position]); - column_position++; - c--; - input_position++; - current_mode = CfMode.MODEB; - } - done = true; - } - } - - if (!done) { - if ((current_mode == CfMode.MODEC) && ((findSubset(inputData[input_position]) != Mode.ABORC) - || (findSubset(inputData[input_position + 1]) != Mode.ABORC))) { - /* Annex B section 1 rule 8 */ - /* When in subset C and a non-numeric character (or a single digit) occurs in the data, insert a Code - A or Code B character before that character, following rules 8a and 8b to determine between code - subsets A and B. - a. If an ASCII control character (eg NUL) occurs in the data before any lower case character, use - Code A. - b. Otherwise use Code B. */ - if (findSubset(inputData[input_position]) == Mode.SHIFTA) { - /* Annex B section 1 rule 8a */ - blockmatrix[current_row][column_position] = 101; /* Code A */ - column_position++; - c--; - if (inputData[input_position] >= 128) { - /* Extended ASCII character */ - blockmatrix[current_row][column_position] = 101; /* FNC4 */ - column_position++; - c--; - } - blockmatrix[current_row][column_position] = a3_convert(inputData[input_position]); - column_position++; - c--; - input_position++; - current_mode = CfMode.MODEA; - } else { - /* Annex B section 1 rule 8b */ - blockmatrix[current_row][column_position] = 100; /* Code B */ - column_position++; - c--; - if (inputData[input_position] >= 128) { - /* Extended ASCII character */ - blockmatrix[current_row][column_position] = 100; /* FNC4 */ - column_position++; - c--; - } - blockmatrix[current_row][column_position] = a3_convert(inputData[input_position]); - column_position++; - c--; - input_position++; - current_mode = CfMode.MODEB; - } - done = true; - } - } - - if (input_position == input_length) { - /* End of data - Annex B rule 5a */ - if (c == 1) { - if (current_mode == CfMode.MODEA) { - blockmatrix[current_row][column_position] = 100; /* Code B */ - current_mode = CfMode.MODEB; - } else { - blockmatrix[current_row][column_position] = 101; /* Code A */ - current_mode = CfMode.MODEA; - } - column_position++; - c--; - } - - if (c == 0) { - /* Another row is needed */ - column_position = 0; - c = columns_needed; - current_row++; - subset_selector[current_row] = CfMode.MODEA; - current_mode = CfMode.MODEA; - } - - if (c > 2) { - /* Fill up the last row */ - do { - if (current_mode == CfMode.MODEA) { - blockmatrix[current_row][column_position] = 100; /* Code B */ - current_mode = CfMode.MODEB; - } else { - blockmatrix[current_row][column_position] = 101; /* Code A */ - current_mode = CfMode.MODEA; - } - column_position++; - c--; - } while (c > 2); - } - - /* If (c == 2) { do nothing } */ - - exit_status = true; - final_mode = current_mode; - } else { - if (c <= 0) { - /* Start new row - Annex B rule 5b */ - column_position = 0; - current_row++; - if (current_row > 43) { - throw new OkapiInputException("Too many rows."); - } - } - } - - } while (!exit_status); - - if (current_row == 0) { - /* fill up the first row */ - for(c = column_position; c <= columns_needed; c++) { - if(current_mode == CfMode.MODEA) { - blockmatrix[current_row][c] = 100; /* Code B */ - current_mode = CfMode.MODEB; - } else { - blockmatrix[current_row][c] = 101; /* Code A */ - current_mode = CfMode.MODEA; - } - } - current_row++; - /* add a second row */ - subset_selector[current_row] = CfMode.MODEA; - current_mode = CfMode.MODEA; - for(c = 0; c <= columns_needed - 2; c++) { - if(current_mode == CfMode.MODEA) { - blockmatrix[current_row][c] = 100; /* Code B */ - current_mode = CfMode.MODEB; - } else { - blockmatrix[current_row][c] = 101; /* Code A */ - current_mode = CfMode.MODEA; - } - } - } - - rows_needed = current_row + 1; - } - - private CfMode character_subset_select(int input_position) { - - /* Section 4.5.2 - Determining the Character Subset Selector in a Row */ - - if((inputData[input_position] >= '0') && (inputData[input_position] <= '9')) { - /* Rule 1 */ - return CfMode.MODEC; - } - - if((inputData[input_position] >= 128) && (inputData[input_position] <= 160)) { - /* Rule 2 (i) */ - return CfMode.MODEA; - } - - if((inputData[input_position] >= 0) && (inputData[input_position] <= 31)) { - /* Rule 3 */ - return CfMode.MODEA; - } - - /* Rule 4 */ - return CfMode.MODEB; - } - - private int a3_convert(int source) { - /* Annex A section 3 */ - if(source < 32) { return source + 64; } - if((source >= 32) && (source <= 127)) { return source - 32; } - if((source >= 128) && (source <= 159)) { return (source - 128) + 64; } - /* if source >= 160 */ - return (source - 128) - 32; - } - - @Override - protected void plotSymbol() { - int xBlock, yBlock; - int x, y, w, h; - boolean black; - - resetPlotElements(); - - y = 1; - h = 1; - for (yBlock = 0; yBlock < row_count; yBlock++) { - black = true; - x = 0; - for (xBlock = 0; xBlock < pattern[yBlock].length(); xBlock++) { - if (black) { - black = false; - w = pattern[yBlock].charAt(xBlock) - '0'; - if (row_height[yBlock] == -1) { - h = default_height; - } else { - h = row_height[yBlock]; - } - if (w != 0 && h != 0) { - Rectangle rect = new Rectangle(x, y, w, h); - rectangles.add(rect); - } - if ((x + w) > symbol_width) { - symbol_width = x + w; - } - } else { - black = true; - } - x += pattern[yBlock].charAt(xBlock) - '0'; - } - y += h; - if (y > symbol_height) { - symbol_height = y; - } - /* Add bars between rows */ - if (yBlock != (row_count - 1)) { - Rectangle rect = new Rectangle(11, y - 1, (symbol_width - 24), 2); - rectangles.add(rect); - } - } - - /* Add top and bottom binding bars */ - Rectangle top = new Rectangle(0, 0, symbol_width, 2); - rectangles.add(top); - Rectangle bottom = new Rectangle(0, y - 1, symbol_width, 2); - rectangles.add(bottom); - symbol_height += 1; - } -} +/* + * Copyright 2014 Robin Stuart + * + * 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. + */ + +package uk.org.okapibarcode.backend; + +import java.nio.charset.StandardCharsets; + +import uk.org.okapibarcode.graphics.Rectangle; + +/** + *

Implements Codablock-F according to AIM Europe "Uniform Symbology Specification - Codablock F", 1995. + * + *

Codablock-F is a multi-row symbology using Code 128 encoding. It can encode any 8-bit ISO 8859-1 (Latin-1) + * data up to approximately 1000 alpha-numeric characters or 2000 numeric digits in length. + * + * @author Robin Stuart + */ +public class CodablockF extends Symbol { + + private enum Mode { + SHIFTA, LATCHA, SHIFTB, LATCHB, SHIFTC, LATCHC, AORB, ABORC, CANDB, CANDBB + } + + private enum CfMode { + MODEA, MODEB, MODEC + } + + /* Annex A Table A.1 */ + private static final String[] C_128_TABLE = { + "212222", "222122", "222221", "121223", "121322", "131222", "122213", + "122312", "132212", "221213", "221312", "231212", "112232", "122132", "122231", "113222", + "123122", "123221", "223211", "221132", "221231", "213212", "223112", "312131", "311222", + "321122", "321221", "312212", "322112", "322211", "212123", "212321", "232121", "111323", + "131123", "131321", "112313", "132113", "132311", "211313", "231113", "231311", "112133", + "112331", "132131", "113123", "113321", "133121", "313121", "211331", "231131", "213113", + "213311", "213131", "311123", "311321", "331121", "312113", "312311", "332111", "314111", + "221411", "431111", "111224", "111422", "121124", "121421", "141122", "141221", "112214", + "112412", "122114", "122411", "142112", "142211", "241211", "221114", "413111", "241112", + "134111", "111242", "121142", "121241", "114212", "124112", "124211", "411212", "421112", + "421211", "212141", "214121", "412121", "111143", "111341", "131141", "114113", "114311", + "411113", "411311", "113141", "114131", "311141", "411131", "211412", "211214", "211232", + "2331112"}; + + private int[][] blockmatrix = new int[44][62]; + private int columns_needed; + private int rows_needed; + private CfMode final_mode; + private CfMode[] subset_selector = new CfMode[44]; + + /** + * TODO: It doesn't appear that this symbol should support GS1 (it's not in the GS1 spec and Zint doesn't + * support GS1 with this type of symbology). However, the code below does contain GS1 checks, so we'll mark + * it as supported for now. It's very possible that the code below which supports GS1 only does so because + * it was originally copied from the Code 128 source code (just a suspicion, though). + */ + @Override + protected boolean gs1Supported() { + return true; + } + + @Override + protected void encode() { + + int input_length, i, j, k; + int min_module_height; + Mode last_mode, this_mode; + double estimate_codelength; + String row_pattern; + int[] row_indicator = new int[44]; + int[] row_check = new int[44]; + int k1_sum, k2_sum; + int k1_check, k2_check; + + final_mode = CfMode.MODEA; + + if (!content.matches("[\u0000-\u00FF]*")) { + throw OkapiInputException.invalidCharactersInInput(); + } + + inputData = toBytes(content, StandardCharsets.ISO_8859_1, 0x00); + input_length = inputData.length - 1; + + if (input_length > 5450) { + throw OkapiInputException.inputTooLong(); + } + + /* Make a guess at how many characters will be needed to encode the data */ + estimate_codelength = 0.0; + last_mode = Mode.AORB; /* Codablock always starts with Code A */ + for (i = 0; i < input_length; i++) { + this_mode = findSubset(inputData[i]); + if (this_mode != last_mode) { + estimate_codelength += 1.0; + } + if (this_mode != Mode.ABORC) { + estimate_codelength += 1.0; + } else { + estimate_codelength += 0.5; + } + if (inputData[i] > 127) { + estimate_codelength += 1.0; + } + last_mode = this_mode; + } + + /* Decide symbol size based on the above guess */ + rows_needed = (int) (0.5 + Math.sqrt((estimate_codelength + 2) / 1.45)); + if (rows_needed < 2) { + rows_needed = 2; + } + if (rows_needed > 44) { + rows_needed = 44; + } + columns_needed = (int) (estimate_codelength + 2) / rows_needed; + if (columns_needed < 4) { + columns_needed = 4; + } + if (columns_needed > 62) { + throw OkapiInputException.inputTooLong(); + } + + /* Encode the data */ + data_encode_blockf(); + + /* Add check digits - Annex F */ + k1_sum = 0; + k2_sum = 0; + for(i = 0; i < input_length; i++) { + if(inputData[i] == FNC1) { + k1_sum += (i + 1) * 29; /* GS */ + k2_sum += i * 29; + } else { + k1_sum += (i + 1) * inputData[i]; + k2_sum += i * inputData[i]; + } + } + k1_check = k1_sum % 86; + k2_check = k2_sum % 86; + if((final_mode == CfMode.MODEA) || (final_mode == CfMode.MODEB)) { + k1_check = k1_check + 64; + if(k1_check > 95) { k1_check -= 96; } + k2_check = k2_check + 64; + if(k2_check > 95) { k2_check -= 96; } + } + blockmatrix[rows_needed - 1][columns_needed - 2] = k1_check; + blockmatrix[rows_needed - 1][columns_needed - 1] = k2_check; + + /* Calculate row height (4.6.1.a) */ + min_module_height = (int) (0.55 * (columns_needed + 3)) + 3; + if(min_module_height < 8) { min_module_height = 8; } + + /* Encode the Row Indicator in the First Row of the Symbol - Table D2 */ + if(subset_selector[0] == CfMode.MODEC) { + /* Code C */ + row_indicator[0] = rows_needed - 2; + } else { + /* Code A or B */ + row_indicator[0] = rows_needed + 62; + + if(row_indicator[0] > 95) { + row_indicator[0] -= 95; + } + } + + /* Encode the Row Indicator in the Second and Subsequent Rows of the Symbol - Table D3 */ + for(i = 1; i < rows_needed; i++) { + /* Note that the second row is row number 1 because counting starts from 0 */ + if(subset_selector[i] == CfMode.MODEC) { + /* Code C */ + row_indicator[i] = i + 42; + } else { + /* Code A or B */ + if( i < 6 ) + row_indicator[i] = i + 10; + else + row_indicator[i] = i + 20; + } + } + + /* Calculate row check digits - Annex E */ + for(i = 0; i < rows_needed; i++) { + k = 103; + switch (subset_selector[i]) { + case MODEA: k += 98; + break; + case MODEB: k += 100; + break; + case MODEC: k += 99; + break; + } + k += 2 * row_indicator[i]; + for(j = 0; j < columns_needed; j++) { + k+= (j + 3) * blockmatrix[i][j]; + } + row_check[i] = k % 103; + } + + readable = ""; + row_count = rows_needed; + pattern = new String[row_count]; + row_height = new int[row_count]; + + infoLine("Grid Size: " + columns_needed + " X " + rows_needed); + infoLine("K1 Check Digit: " + k1_check); + infoLine("K2 Check Digit: " + k2_check); + + /* Resolve the data into patterns and place in symbol structure */ + info("Encoding: "); + for(i = 0; i < rows_needed; i++) { + + row_pattern = ""; + /* Start character */ + row_pattern += C_128_TABLE[103]; /* Always Start A */ + + switch (subset_selector[i]) { + case MODEA: + row_pattern += C_128_TABLE[98]; + info("MODEA "); + break; + case MODEB: + row_pattern += C_128_TABLE[100]; + info("MODEB "); + break; + case MODEC: + row_pattern += C_128_TABLE[99]; + info("MODEC "); + break; + } + row_pattern += C_128_TABLE[row_indicator[i]]; + infoSpace(row_indicator[i]); + + for (j = 0; j < columns_needed; j++) { + row_pattern += C_128_TABLE[blockmatrix[i][j]]; + infoSpace(blockmatrix[i][j]); + } + + row_pattern += C_128_TABLE[row_check[i]]; + info("(" + row_check[i] + ") "); + + /* Stop character */ + row_pattern += C_128_TABLE[106]; + + /* Write the information into the symbol */ + pattern[i] = row_pattern; + row_height[i] = 15; + } + infoLine(); + + symbol_height = rows_needed * 15; + } + + private Mode findSubset(int letter) { + Mode mode; + + if (letter == FNC1) { + mode = Mode.AORB; + } else if (letter <= 31) { + mode = Mode.SHIFTA; + } else if ((letter >= 48) && (letter <= 57)) { + mode = Mode.ABORC; + } else if (letter <= 95) { + mode = Mode.AORB; + } else if (letter <= 127) { + mode = Mode.SHIFTB; + } else if (letter <= 159) { + mode = Mode.SHIFTA; + } else if (letter <= 223) { + mode = Mode.AORB; + } else { + mode = Mode.SHIFTB; + } + + return mode; + } + + private void data_encode_blockf() { + + int i, j, input_position, current_row; + int column_position, c; + CfMode current_mode; + boolean done, exit_status; + int input_length = inputData.length - 1; + + exit_status = false; + current_row = 0; + current_mode = CfMode.MODEA; + column_position = 0; + input_position = 0; + c = 0; + + do { + done = false; + /* 'done' ensures that the instructions are followed in the correct order for each input character */ + + if (column_position == 0) { + /* The Beginning of a row */ + c = columns_needed; + current_mode = character_subset_select(input_position); + subset_selector[current_row] = current_mode; + if ((current_row == 0) && (inputDataType == DataType.GS1)) { + /* Section 4.4.7.1 */ + blockmatrix[current_row][column_position] = 102; /* FNC1 */ + column_position++; + c--; + } + } + + if (inputData[input_position] == FNC1) { + blockmatrix[current_row][column_position] = 102; /* FNC1 */ + column_position++; + c--; + input_position++; + done = true; + } + + if (!done) { + if (c <= 2) { + /* Annex B section 1 rule 1 */ + /* Ensure that there is sufficient encodation capacity to continue (using the rules of Annex B.2). */ + switch (current_mode) { + case MODEA: /* Table B1 applies */ + if (findSubset(inputData[input_position]) == Mode.ABORC) { + blockmatrix[current_row][column_position] = a3_convert(inputData[input_position]); + column_position++; + c--; + input_position++; + done = true; + } + + if ((findSubset(inputData[input_position]) == Mode.SHIFTB) && (c == 1)) { + /* Needs two symbols */ + blockmatrix[current_row][column_position] = 100; /* Code B */ + column_position++; + c--; + done = true; + } + + if ((inputData[input_position] >= 244) && (!done)) { + /* Needs three symbols */ + blockmatrix[current_row][column_position] = 100; /* Code B */ + column_position++; + c--; + if (c == 1) { + blockmatrix[current_row][column_position] = 101; /* Code A */ + column_position++; + c--; + } + done = true; + } + + if ((inputData[input_position] >= 128) && (!done) && c == 1) { + /* Needs two symbols */ + blockmatrix[current_row][column_position] = 100; /* Code B */ + column_position++; + c--; + done = true; + } + break; + case MODEB: /* Table B2 applies */ + if (findSubset(inputData[input_position]) == Mode.ABORC) { + blockmatrix[current_row][column_position] = a3_convert(inputData[input_position]); + column_position++; + c--; + input_position++; + done = true; + } + + if ((findSubset(inputData[input_position]) == Mode.SHIFTA) && (c == 1)) { + /* Needs two symbols */ + blockmatrix[current_row][column_position] = 101; /* Code A */ + column_position++; + c--; + done = true; + } + + if (((inputData[input_position] >= 128) + && (inputData[input_position] <= 159)) && (!done)) { + /* Needs three symbols */ + blockmatrix[current_row][column_position] = 101; /* Code A */ + column_position++; + c--; + if (c == 1) { + blockmatrix[current_row][column_position] = 100; /* Code B */ + column_position++; + c--; + } + done = true; + } + + if ((inputData[input_position] >= 160) && (!done) && c == 1) { + /* Needs two symbols */ + blockmatrix[current_row][column_position] = 101; /* Code A */ + column_position++; + c--; + done = true; + } + break; + case MODEC: /* Table B3 applies */ + if ((findSubset(inputData[input_position]) != Mode.ABORC) && (c == 1)) { + /* Needs two symbols */ + blockmatrix[current_row][column_position] = 101; /* Code A */ + column_position++; + c--; + done = true; + } + + if (((findSubset(inputData[input_position]) == Mode.ABORC) + && (findSubset(inputData[input_position + 1]) != Mode.ABORC)) + && (c == 1)) { + /* Needs two symbols */ + blockmatrix[current_row][column_position] = 101; /* Code A */ + column_position++; + c--; + done = true; + } + + if (inputData[input_position] >= 128) { + /* Needs three symbols */ + blockmatrix[current_row][column_position] = 101; /* Code A */ + column_position++; + c--; + if (c == 1) { + blockmatrix[current_row][column_position] = 100; /* Code B */ + column_position++; + c--; + } + } + break; + } + } + } + + if (!done) { + if (((findSubset(inputData[input_position]) == Mode.AORB) + || (findSubset(inputData[input_position]) == Mode.SHIFTA)) + && (current_mode == CfMode.MODEA)) { + /* Annex B section 1 rule 2 */ + /* If in Code Subset A and the next data character can be encoded in Subset A encode the next + character. */ + if (inputData[input_position] >= 128) { + /* Extended ASCII character */ + blockmatrix[current_row][column_position] = 101; /* FNC4 */ + column_position++; + c--; + } + blockmatrix[current_row][column_position] = a3_convert(inputData[input_position]); + column_position++; + c--; + input_position++; + done = true; + } + } + + if (!done) { + if (((findSubset(inputData[input_position]) == Mode.AORB) + || (findSubset(inputData[input_position]) == Mode.SHIFTB)) + && (current_mode == CfMode.MODEB)) { + /* Annex B section 1 rule 3 */ + /* If in Code Subset B and the next data character can be encoded in subset B, encode the next + character. */ + if (inputData[input_position] >= 128) { + /* Extended ASCII character */ + blockmatrix[current_row][column_position] = 100; /* FNC4 */ + column_position++; + c--; + } + blockmatrix[current_row][column_position] = a3_convert(inputData[input_position]); + column_position++; + c--; + input_position++; + done = true; + } + } + + if (!done) { + if (((findSubset(inputData[input_position]) == Mode.ABORC) + && (findSubset(inputData[input_position + 1]) == Mode.ABORC)) + && (current_mode == CfMode.MODEC)) { + /* Annex B section 1 rule 4 */ + /* If in Code Subset C and the next data are 2 digits, encode them. */ + blockmatrix[current_row][column_position] + = ((inputData[input_position] - '0') * 10) + + (inputData[input_position + 1] - '0'); + column_position++; + c--; + input_position += 2; + done = true; + } + } + + if (!done) { + if (((current_mode == CfMode.MODEA) || (current_mode == CfMode.MODEB)) + && ((findSubset(inputData[input_position]) == Mode.ABORC) + || (inputData[input_position] == FNC1))) { + // Count the number of numeric digits + // If 4 or more numeric data characters occur together when in subsets A or B: + // a. If there is an even number of numeric data characters, insert a Code C character before the + // first numeric digit to change to subset C. + // b. If there is an odd number of numeric data characters, insert a Code Set C character immediately + // after the first numeric digit to change to subset C. + i = 0; + j = 0; + do { + i++; + if (inputData[input_position + j] == FNC1) { + i++; + } + j++; + } while ((findSubset(inputData[input_position + j]) == Mode.ABORC) + || (inputData[input_position + j] == FNC1)); + i--; + + if (i >= 4) { + /* Annex B section 1 rule 5 */ + if ((i % 2) == 1) { + /* Annex B section 1 rule 5a */ + blockmatrix[current_row][column_position] = 99; /* Code C */ + column_position++; + c--; + blockmatrix[current_row][column_position] = ((inputData[input_position] - '0') * 10) + + (inputData[input_position + 1] - '0'); + column_position++; + c--; + input_position += 2; + current_mode = CfMode.MODEC; + } else { + /* Annex B section 1 rule 5b */ + blockmatrix[current_row][column_position] = a3_convert(inputData[input_position]); + column_position++; + c--; + input_position++; + } + done = true; + } else { + blockmatrix[current_row][column_position] = a3_convert(inputData[input_position]); + column_position++; + c--; + input_position++; + done = true; + } + } + } + + if (!done) { + if ((current_mode == CfMode.MODEB) && (findSubset(inputData[input_position]) == Mode.SHIFTA)) { + /* Annex B section 1 rule 6 */ + /* When in subset B and an ASCII control character occurs in the data: + a. If there is a lower case character immediately following the control character, insert a Shift + character before the control character. + b. Otherwise, insert a Code A character before the control character to change to subset A. */ + if ((inputData[input_position + 1] >= 96) && (inputData[input_position + 1] <= 127)) { + /* Annex B section 1 rule 6a */ + blockmatrix[current_row][column_position] = 98; /* Shift */ + column_position++; + c--; + if (inputData[input_position] >= 128) { + /* Extended ASCII character */ + blockmatrix[current_row][column_position] = 100; /* FNC4 */ + column_position++; + c--; + } + blockmatrix[current_row][column_position] = a3_convert(inputData[input_position]); + column_position++; + c--; + input_position++; + } else { + /* Annex B section 1 rule 6b */ + blockmatrix[current_row][column_position] = 101; /* Code A */ + column_position++; + c--; + if (inputData[input_position] >= 128) { + /* Extended ASCII character */ + blockmatrix[current_row][column_position] = 100; /* FNC4 */ + column_position++; + c--; + } + blockmatrix[current_row][column_position] = a3_convert(inputData[input_position]); + column_position++; + c--; + input_position++; + current_mode = CfMode.MODEA; + } + done = true; + } + } + + if (!done) { + if ((current_mode == CfMode.MODEA) && (findSubset(inputData[input_position]) == Mode.SHIFTB)) { + /* Annex B section 1 rule 7 */ + /* When in subset A and a lower case character occurs in the data: + a. If following that character, a control character occurs in the data before the occurrence of + another lower case character, insert a Shift character before the lower case character. + b. Otherwise, insert a Code B character before the lower case character to change to subset B. */ + if ((findSubset(inputData[input_position + 1]) == Mode.SHIFTA) + && (findSubset(inputData[input_position + 2]) == Mode.SHIFTB)) { + /* Annex B section 1 rule 7a */ + blockmatrix[current_row][column_position] = 98; /* Shift */ + column_position++; + c--; + if (inputData[input_position] >= 128) { + /* Extended ASCII character */ + blockmatrix[current_row][column_position] = 101; /* FNC4 */ + column_position++; + c--; + } + blockmatrix[current_row][column_position] = a3_convert(inputData[input_position]); + column_position++; + c--; + input_position++; + } else { + /* Annex B section 1 rule 7b */ + blockmatrix[current_row][column_position] = 100; /* Code B */ + column_position++; + c--; + if (inputData[input_position] >= 128) { + /* Extended ASCII character */ + blockmatrix[current_row][column_position] = 101; /* FNC4 */ + column_position++; + c--; + } + blockmatrix[current_row][column_position] = a3_convert(inputData[input_position]); + column_position++; + c--; + input_position++; + current_mode = CfMode.MODEB; + } + done = true; + } + } + + if (!done) { + if ((current_mode == CfMode.MODEC) && ((findSubset(inputData[input_position]) != Mode.ABORC) + || (findSubset(inputData[input_position + 1]) != Mode.ABORC))) { + /* Annex B section 1 rule 8 */ + /* When in subset C and a non-numeric character (or a single digit) occurs in the data, insert a Code + A or Code B character before that character, following rules 8a and 8b to determine between code + subsets A and B. + a. If an ASCII control character (eg NUL) occurs in the data before any lower case character, use + Code A. + b. Otherwise use Code B. */ + if (findSubset(inputData[input_position]) == Mode.SHIFTA) { + /* Annex B section 1 rule 8a */ + blockmatrix[current_row][column_position] = 101; /* Code A */ + column_position++; + c--; + if (inputData[input_position] >= 128) { + /* Extended ASCII character */ + blockmatrix[current_row][column_position] = 101; /* FNC4 */ + column_position++; + c--; + } + blockmatrix[current_row][column_position] = a3_convert(inputData[input_position]); + column_position++; + c--; + input_position++; + current_mode = CfMode.MODEA; + } else { + /* Annex B section 1 rule 8b */ + blockmatrix[current_row][column_position] = 100; /* Code B */ + column_position++; + c--; + if (inputData[input_position] >= 128) { + /* Extended ASCII character */ + blockmatrix[current_row][column_position] = 100; /* FNC4 */ + column_position++; + c--; + } + blockmatrix[current_row][column_position] = a3_convert(inputData[input_position]); + column_position++; + c--; + input_position++; + current_mode = CfMode.MODEB; + } + done = true; + } + } + + if (input_position == input_length) { + /* End of data - Annex B rule 5a */ + if (c == 1) { + if (current_mode == CfMode.MODEA) { + blockmatrix[current_row][column_position] = 100; /* Code B */ + current_mode = CfMode.MODEB; + } else { + blockmatrix[current_row][column_position] = 101; /* Code A */ + current_mode = CfMode.MODEA; + } + column_position++; + c--; + } + + if (c == 0) { + /* Another row is needed */ + column_position = 0; + c = columns_needed; + current_row++; + subset_selector[current_row] = CfMode.MODEA; + current_mode = CfMode.MODEA; + } + + if (c > 2) { + /* Fill up the last row */ + do { + if (current_mode == CfMode.MODEA) { + blockmatrix[current_row][column_position] = 100; /* Code B */ + current_mode = CfMode.MODEB; + } else { + blockmatrix[current_row][column_position] = 101; /* Code A */ + current_mode = CfMode.MODEA; + } + column_position++; + c--; + } while (c > 2); + } + + /* If (c == 2) { do nothing } */ + + exit_status = true; + final_mode = current_mode; + } else { + if (c <= 0) { + /* Start new row - Annex B rule 5b */ + column_position = 0; + current_row++; + if (current_row > 43) { + throw new OkapiInputException("Too many rows."); + } + } + } + + } while (!exit_status); + + if (current_row == 0) { + /* fill up the first row */ + for(c = column_position; c <= columns_needed; c++) { + if(current_mode == CfMode.MODEA) { + blockmatrix[current_row][c] = 100; /* Code B */ + current_mode = CfMode.MODEB; + } else { + blockmatrix[current_row][c] = 101; /* Code A */ + current_mode = CfMode.MODEA; + } + } + current_row++; + /* add a second row */ + subset_selector[current_row] = CfMode.MODEA; + current_mode = CfMode.MODEA; + for(c = 0; c <= columns_needed - 2; c++) { + if(current_mode == CfMode.MODEA) { + blockmatrix[current_row][c] = 100; /* Code B */ + current_mode = CfMode.MODEB; + } else { + blockmatrix[current_row][c] = 101; /* Code A */ + current_mode = CfMode.MODEA; + } + } + } + + rows_needed = current_row + 1; + } + + private CfMode character_subset_select(int input_position) { + + /* Section 4.5.2 - Determining the Character Subset Selector in a Row */ + + if((inputData[input_position] >= '0') && (inputData[input_position] <= '9')) { + /* Rule 1 */ + return CfMode.MODEC; + } + + if((inputData[input_position] >= 128) && (inputData[input_position] <= 160)) { + /* Rule 2 (i) */ + return CfMode.MODEA; + } + + if((inputData[input_position] >= 0) && (inputData[input_position] <= 31)) { + /* Rule 3 */ + return CfMode.MODEA; + } + + /* Rule 4 */ + return CfMode.MODEB; + } + + private int a3_convert(int source) { + /* Annex A section 3 */ + if(source < 32) { return source + 64; } + if((source >= 32) && (source <= 127)) { return source - 32; } + if((source >= 128) && (source <= 159)) { return (source - 128) + 64; } + /* if source >= 160 */ + return (source - 128) - 32; + } + + @Override + protected void plotSymbol() { + int xBlock, yBlock; + int x, y, w, h; + boolean black; + + resetPlotElements(); + + y = 1; + h = 1; + for (yBlock = 0; yBlock < row_count; yBlock++) { + black = true; + x = 0; + for (xBlock = 0; xBlock < pattern[yBlock].length(); xBlock++) { + if (black) { + black = false; + w = pattern[yBlock].charAt(xBlock) - '0'; + if (row_height[yBlock] == -1) { + h = default_height; + } else { + h = row_height[yBlock]; + } + if (w != 0 && h != 0) { + Rectangle rect = new Rectangle(x, y, w, h); + rectangles.add(rect); + } + if ((x + w) > symbol_width) { + symbol_width = x + w; + } + } else { + black = true; + } + x += pattern[yBlock].charAt(xBlock) - '0'; + } + y += h; + if (y > symbol_height) { + symbol_height = y; + } + /* Add bars between rows */ + if (yBlock != (row_count - 1)) { + Rectangle rect = new Rectangle(11, y - 1, (symbol_width - 24), 2); + rectangles.add(rect); + } + } + + /* Add top and bottom binding bars */ + Rectangle top = new Rectangle(0, 0, symbol_width, 2); + rectangles.add(top); + Rectangle bottom = new Rectangle(0, y - 1, symbol_width, 2); + rectangles.add(bottom); + symbol_height += 1; + } +} diff --git a/src/main/java/uk/org/okapibarcode/backend/Code11.java b/src/main/java/uk/org/okapibarcode/backend/Code11.java index f916b8ee..721e1e2e 100644 --- a/src/main/java/uk/org/okapibarcode/backend/Code11.java +++ b/src/main/java/uk/org/okapibarcode/backend/Code11.java @@ -1,217 +1,217 @@ -/* - * Copyright 2014 Robin Stuart, Daniel Gredler - * - * 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. - */ - -package uk.org.okapibarcode.backend; - -import static uk.org.okapibarcode.util.Arrays.positionOf; - -/** - *

Implements Code 11 bar code symbology. - * - *

Code 11 can encode any length string consisting of the digits 0-9 and the - * dash character (-). One or two modulo-11 check digits are calculated. - * - * @author Robin Stuart - * @author Daniel Gredler - */ -public class Code11 extends Symbol { - - private static final String[] CODE_11_TABLE = { - "111121", "211121", "121121", "221111", "112121", "212111", - "122111", "111221", "211211", "211111", "112111" - }; - - private static final char[] CHARACTER_SET = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-' - }; - - /** Ratio of wide bar width to narrow bar width. */ - private double moduleWidthRatio = 2; - - /** The number of check digits to calculate ({@code 1} or {@code 2}). */ - private int checkDigitCount = 2; - - /** Optional start delimiter to be shown in the human-readable text. */ - private Character startDelimiter; - - /** Optional stop delimiter to be shown in the human-readable text. */ - private Character stopDelimiter; - - /** - * Sets the ratio of wide bar width to narrow bar width. Valid values are usually - * between {@code 2} and {@code 3}. The default value is {@code 2}. - * - * @param moduleWidthRatio the ratio of wide bar width to narrow bar width - */ - public void setModuleWidthRatio(double moduleWidthRatio) { - this.moduleWidthRatio = moduleWidthRatio; - } - - /** - * Returns the ratio of wide bar width to narrow bar width. - * - * @return the ratio of wide bar width to narrow bar width - */ - public double getModuleWidthRatio() { - return moduleWidthRatio; - } - - /** - * Sets the number of check digits to calculate ({@code 1} or {@code 2}). The default value is {@code 2}. - * - * @param checkDigitCount the number of check digits to calculate - */ - public void setCheckDigitCount(int checkDigitCount) { - if (checkDigitCount < 1 || checkDigitCount > 2) { - throw new IllegalArgumentException("Check digit count must be 1 or 2."); - } - this.checkDigitCount = checkDigitCount; - } - - /** - * Returns the number of check digits to calculate (1 or 2). - * - * @return the number of check digits to calculate - */ - public int getCheckDigitCount() { - return checkDigitCount; - } - - /** - * Sets an optional start delimiter to be shown in the human-readable text (defaults to null). - * - * @param startDelimiter an optional start delimiter to be shown in the human-readable text - */ - public void setStartDelimiter(Character startDelimiter) { - this.startDelimiter = startDelimiter; - } - - /** - * Returns the optional start delimiter to be shown in the human-readable text. - * - * @return the optional start delimiter to be shown in the human-readable text - */ - public Character getStartDelimiter() { - return startDelimiter; - } - - /** - * Sets an optional stop delimiter to be shown in the human-readable text (defaults to null). - * - * @param stopDelimiter an optional stop delimiter to be shown in the human-readable text - */ - public void setStopDelimiter(Character stopDelimiter) { - this.stopDelimiter = stopDelimiter; - } - - /** - * Returns the optional stop delimiter to be shown in the human-readable text. - * - * @return the optional stop delimiter to be shown in the human-readable text - */ - public Character getStopDelimiter() { - return stopDelimiter; - } - - /** {@inheritDoc} */ - @Override - protected void encode() { - - if (!content.matches("[0-9-]+")) { - throw OkapiInputException.invalidCharactersInInput(); - } - - String horizontalSpacing = "112211"; - String humanReadable = content; - int length = content.length(); - int[] weight = new int[length + 1]; - - for (int i = 0; i < length; i++) { - char c = content.charAt(i); - weight[i] = positionOf(c, CHARACTER_SET); - horizontalSpacing += CODE_11_TABLE[weight[i]]; - } - - int checkDigitC = getCheckDigitC(weight, length); - horizontalSpacing += CODE_11_TABLE[checkDigitC]; - humanReadable += CHARACTER_SET[checkDigitC]; - infoLine("Check Digit C: " + checkDigitC); - - if (checkDigitCount == 2) { - weight[length] = checkDigitC; - int checkDigitK = getCheckDigitK(weight, length + 1); - horizontalSpacing += CODE_11_TABLE[checkDigitK]; - humanReadable += CHARACTER_SET[checkDigitK]; - infoLine("Check Digit K: " + checkDigitK); - } - - horizontalSpacing += "112211"; - - readable = humanReadable; - if (startDelimiter != null) { - readable = startDelimiter + readable; - } - if (stopDelimiter != null) { - readable = readable + stopDelimiter; - } - - pattern = new String[] { horizontalSpacing }; - row_count = 1; - row_height = new int[] { -1 }; - } - - private static int getCheckDigitC(int[] weight, int length) { - int countC = 0; - int weightC = 1; - for (int i = length - 1; i >= 0; i--) { - countC += (weightC * weight[i]); - weightC++; - if (weightC > 10) { - weightC = 1; - } - } - return countC % 11; - } - - private static int getCheckDigitK(int[] weight, int length) { - int countK = 0; - int weightK = 1; - for (int i = length - 1; i >= 0; i--) { - countK += (weightK * weight[i]); - weightK++; - if (weightK > 9) { - weightK = 1; - } - } - return countK % 11; - } - - /** {@inheritDoc} */ - @Override - protected double getModuleWidth(int originalWidth) { - if (originalWidth == 1) { - return 1; - } else { - return moduleWidthRatio; - } - } - - /** {@inheritDoc} */ - @Override - protected int[] getCodewords() { - return getPatternAsCodewords(6); - } -} +/* + * Copyright 2014 Robin Stuart, Daniel Gredler + * + * 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. + */ + +package uk.org.okapibarcode.backend; + +import static uk.org.okapibarcode.util.Arrays.positionOf; + +/** + *

Implements Code 11 bar code symbology. + * + *

Code 11 can encode any length string consisting of the digits 0-9 and the + * dash character (-). One or two modulo-11 check digits are calculated. + * + * @author Robin Stuart + * @author Daniel Gredler + */ +public class Code11 extends Symbol { + + private static final String[] CODE_11_TABLE = { + "111121", "211121", "121121", "221111", "112121", "212111", + "122111", "111221", "211211", "211111", "112111" + }; + + private static final char[] CHARACTER_SET = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-' + }; + + /** Ratio of wide bar width to narrow bar width. */ + private double moduleWidthRatio = 2; + + /** The number of check digits to calculate ({@code 1} or {@code 2}). */ + private int checkDigitCount = 2; + + /** Optional start delimiter to be shown in the human-readable text. */ + private Character startDelimiter; + + /** Optional stop delimiter to be shown in the human-readable text. */ + private Character stopDelimiter; + + /** + * Sets the ratio of wide bar width to narrow bar width. Valid values are usually + * between {@code 2} and {@code 3}. The default value is {@code 2}. + * + * @param moduleWidthRatio the ratio of wide bar width to narrow bar width + */ + public void setModuleWidthRatio(double moduleWidthRatio) { + this.moduleWidthRatio = moduleWidthRatio; + } + + /** + * Returns the ratio of wide bar width to narrow bar width. + * + * @return the ratio of wide bar width to narrow bar width + */ + public double getModuleWidthRatio() { + return moduleWidthRatio; + } + + /** + * Sets the number of check digits to calculate ({@code 1} or {@code 2}). The default value is {@code 2}. + * + * @param checkDigitCount the number of check digits to calculate + */ + public void setCheckDigitCount(int checkDigitCount) { + if (checkDigitCount < 1 || checkDigitCount > 2) { + throw new IllegalArgumentException("Check digit count must be 1 or 2."); + } + this.checkDigitCount = checkDigitCount; + } + + /** + * Returns the number of check digits to calculate (1 or 2). + * + * @return the number of check digits to calculate + */ + public int getCheckDigitCount() { + return checkDigitCount; + } + + /** + * Sets an optional start delimiter to be shown in the human-readable text (defaults to null). + * + * @param startDelimiter an optional start delimiter to be shown in the human-readable text + */ + public void setStartDelimiter(Character startDelimiter) { + this.startDelimiter = startDelimiter; + } + + /** + * Returns the optional start delimiter to be shown in the human-readable text. + * + * @return the optional start delimiter to be shown in the human-readable text + */ + public Character getStartDelimiter() { + return startDelimiter; + } + + /** + * Sets an optional stop delimiter to be shown in the human-readable text (defaults to null). + * + * @param stopDelimiter an optional stop delimiter to be shown in the human-readable text + */ + public void setStopDelimiter(Character stopDelimiter) { + this.stopDelimiter = stopDelimiter; + } + + /** + * Returns the optional stop delimiter to be shown in the human-readable text. + * + * @return the optional stop delimiter to be shown in the human-readable text + */ + public Character getStopDelimiter() { + return stopDelimiter; + } + + /** {@inheritDoc} */ + @Override + protected void encode() { + + if (!content.matches("[0-9-]+")) { + throw OkapiInputException.invalidCharactersInInput(); + } + + String horizontalSpacing = "112211"; + String humanReadable = content; + int length = content.length(); + int[] weight = new int[length + 1]; + + for (int i = 0; i < length; i++) { + char c = content.charAt(i); + weight[i] = positionOf(c, CHARACTER_SET); + horizontalSpacing += CODE_11_TABLE[weight[i]]; + } + + int checkDigitC = getCheckDigitC(weight, length); + horizontalSpacing += CODE_11_TABLE[checkDigitC]; + humanReadable += CHARACTER_SET[checkDigitC]; + infoLine("Check Digit C: " + checkDigitC); + + if (checkDigitCount == 2) { + weight[length] = checkDigitC; + int checkDigitK = getCheckDigitK(weight, length + 1); + horizontalSpacing += CODE_11_TABLE[checkDigitK]; + humanReadable += CHARACTER_SET[checkDigitK]; + infoLine("Check Digit K: " + checkDigitK); + } + + horizontalSpacing += "112211"; + + readable = humanReadable; + if (startDelimiter != null) { + readable = startDelimiter + readable; + } + if (stopDelimiter != null) { + readable = readable + stopDelimiter; + } + + pattern = new String[] { horizontalSpacing }; + row_count = 1; + row_height = new int[] { -1 }; + } + + private static int getCheckDigitC(int[] weight, int length) { + int countC = 0; + int weightC = 1; + for (int i = length - 1; i >= 0; i--) { + countC += (weightC * weight[i]); + weightC++; + if (weightC > 10) { + weightC = 1; + } + } + return countC % 11; + } + + private static int getCheckDigitK(int[] weight, int length) { + int countK = 0; + int weightK = 1; + for (int i = length - 1; i >= 0; i--) { + countK += (weightK * weight[i]); + weightK++; + if (weightK > 9) { + weightK = 1; + } + } + return countK % 11; + } + + /** {@inheritDoc} */ + @Override + protected double getModuleWidth(int originalWidth) { + if (originalWidth == 1) { + return 1; + } else { + return moduleWidthRatio; + } + } + + /** {@inheritDoc} */ + @Override + protected int[] getCodewords() { + return getPatternAsCodewords(6); + } +} diff --git a/src/main/java/uk/org/okapibarcode/backend/Code16k.java b/src/main/java/uk/org/okapibarcode/backend/Code16k.java index d9df0e1c..7fc25f1d 100644 --- a/src/main/java/uk/org/okapibarcode/backend/Code16k.java +++ b/src/main/java/uk/org/okapibarcode/backend/Code16k.java @@ -1,787 +1,787 @@ -/* - * Copyright 2014 Robin Stuart - * - * 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. - */ - -package uk.org.okapibarcode.backend; - -import java.nio.charset.StandardCharsets; - -import uk.org.okapibarcode.graphics.Rectangle; - -/** - *

Implements Code 16K symbology according to BS EN 12323:2005. - * - *

Encodes using a stacked symbology based on Code 128. Supports encoding - * of any 8-bit ISO 8859-1 (Latin-1) data with a maximum data capacity of 77 - * alpha-numeric characters or 154 numerical digits. - * - * @author Robin Stuart - */ -public class Code16k extends Symbol { - - private enum Mode { - NULL, SHIFTA, LATCHA, SHIFTB, LATCHB, SHIFTC, LATCHC, AORB, ABORC, CANDB, CANDBB - } - - /* EN 12323 Table 1 - "Code 16K" character encodations */ - private static final String[] C16K_TABLE = { - "212222", "222122", "222221", "121223", "121322", "131222", "122213", - "122312", "132212", "221213", "221312", "231212", "112232", "122132", - "122231", "113222", "123122", "123221", "223211", "221132", "221231", - "213212", "223112", "312131", "311222", "321122", "321221", "312212", - "322112", "322211", "212123", "212321", "232121", "111323", "131123", - "131321", "112313", "132113", "132311", "211313", "231113", "231311", - "112133", "112331", "132131", "113123", "113321", "133121", "313121", - "211331", "231131", "213113", "213311", "213131", "311123", "311321", - "331121", "312113", "312311", "332111", "314111", "221411", "431111", - "111224", "111422", "121124", "121421", "141122", "141221", "112214", - "112412", "122114", "122411", "142112", "142211", "241211", "221114", - "413111", "241112", "134111", "111242", "121142", "121241", "114212", - "124112", "124211", "411212", "421112", "421211", "212141", "214121", - "412121", "111143", "111341", "131141", "114113", "114311", "411113", - "411311", "113141", "114131", "311141", "411131", "211412", "211214", - "211232", "211133" - }; - - /* EN 12323 Table 3 and Table 4 - Start patterns and stop patterns */ - private static final String[] C16K_START_STOP = { - "3211", "2221", "2122", "1411", "1132", "1231", "1114", "3112" - }; - - /* EN 12323 Table 5 - Start and stop values defining row numbers */ - private static final int[] C16K_START_VALUES = { - 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7 - }; - private static final int[] C16K_STOP_VALUES = { - 0, 1, 2, 3, 4, 5, 6, 7, 4, 5, 6, 7, 0, 1, 2, 3 - }; - - private Mode[] block_mode = new Mode[170]; /* RENAME block_mode */ - private int[] block_length = new int[170]; /* RENAME block_length */ - private int block_count; - - @Override - protected boolean gs1Supported() { - return true; - } - - @Override - protected void encode() { - - // TODO: is it possible to share any of this code with Code128, which is more up to date? - - String width_pattern; - int current_row, rows_needed, first_check, second_check; - int indexchaine, pads_needed; - char[] set, fset; - Mode mode; - char last_set, current_set; - int i, j, k, m, read; - int[] values; - int bar_characters; - double glyph_count; - int first_sum, second_sum; - int input_length; - int c_count; - boolean f_state; - - if (!content.matches("[\u0000-\u00FF]+")) { - throw OkapiInputException.invalidCharactersInInput(); - } - - inputData = toBytes(content, StandardCharsets.ISO_8859_1); - input_length = inputData.length; - - bar_characters = 0; - set = new char[160]; - fset = new char[160]; - values = new int[160]; - - if (input_length > 157) { - throw OkapiInputException.inputTooLong(); - } - - /* Detect extended ASCII characters */ - for (i = 0; i < input_length; i++) { - if (inputData[i] >= 128) { - fset[i] = 'f'; - } else { - fset[i] = ' '; - } - } - - /* Decide when to latch to extended mode */ - for (i = 0; i < input_length; i++) { - j = 0; - if (fset[i] == 'f') { - do { - j++; - } while (fset[i + j] == 'f'); - if ((j >= 5) || ((j >= 3) && ((i + j) == (input_length - 1)))) { - for (k = 0; k <= j; k++) { - fset[i + k] = 'F'; - } - } - } - } - - /* Decide if it is worth reverting to 646 encodation for a few characters */ - if (input_length > 1) { - for (i = 1; i < input_length; i++) { - if ((fset[i - 1] == 'F') && (fset[i] == ' ')) { - /* Detected a change from 8859-1 to 646 - count how long for */ - for (j = 0; - (fset[i + j] == ' ') && ((i + j) < input_length); j++); - if ((j < 5) || ((j < 3) && ((i + j) == (input_length - 1)))) { - /* Change to shifting back rather than latching back */ - for (k = 0; k < j; k++) { - fset[i + k] = 'n'; - } - } - } - } - } - - /* Detect mode A, B and C characters */ - block_count = 0; - indexchaine = 0; - - mode = findSubset(inputData[indexchaine]); - if (inputData[indexchaine] == FNC1) { - mode = Mode.ABORC; - } /* FNC1 */ - - for (i = 0; i < 160; i++) { - block_length[i] = 0; - } - - do { - block_mode[block_count] = mode; - while ((block_mode[block_count] == mode) && (indexchaine < input_length)) { - block_length[block_count]++; - indexchaine++; - if (indexchaine < input_length) { - mode = findSubset(inputData[indexchaine]); - if (inputData[indexchaine] == FNC1) { - mode = Mode.ABORC; - } /* FNC1 */ - } - } - block_count++; - } while (indexchaine < input_length); - - reduceSubsetChanges(block_count); - - - /* Put set data into set[] */ - read = 0; - for (i = 0; i < block_count; i++) { - for (j = 0; j < block_length[i]; j++) { - switch (block_mode[i]) { - case SHIFTA: - set[read] = 'a'; - break; - case LATCHA: - set[read] = 'A'; - break; - case SHIFTB: - set[read] = 'b'; - break; - case LATCHB: - set[read] = 'B'; - break; - case LATCHC: - set[read] = 'C'; - break; - } - read++; - } - } - - /* Adjust for strings which start with shift characters - make them latch instead */ - if (set[0] == 'a') { - i = 0; - do { - set[i] = 'A'; - i++; - } while (set[i] == 'a'); - } - - if (set[0] == 'b') { - i = 0; - do { - set[i] = 'B'; - i++; - } while (set[i] == 'b'); - } - - /* Watch out for odd-length Mode C blocks */ - c_count = 0; - for (i = 0; i < read; i++) { - if (set[i] == 'C') { - if (inputData[i] == FNC1) { - if ((c_count & 1) != 0) { - if ((i - c_count) != 0) { - set[i - c_count] = 'B'; - } else { - set[i - 1] = 'B'; - } - } - c_count = 0; - } else { - c_count++; - } - } else { - if ((c_count & 1) != 0) { - if ((i - c_count) != 0) { - set[i - c_count] = 'B'; - } else { - set[i - 1] = 'B'; - } - } - c_count = 0; - } - } - if ((c_count & 1) != 0) { - if ((i - c_count) != 0) { - set[i - c_count] = 'B'; - } else { - set[i - 1] = 'B'; - } - } - for (i = 1; i < read - 1; i++) { - if ((set[i] == 'C') && ((set[i - 1] == 'B') && (set[i + 1] == 'B'))) { - set[i] = 'B'; - } - } - - /* Make sure the data will fit in the symbol */ - last_set = ' '; - glyph_count = 0.0; - for (i = 0; i < input_length; i++) { - if ((set[i] == 'a') || (set[i] == 'b')) { - glyph_count = glyph_count + 1.0; - } - if ((fset[i] == 'f') || (fset[i] == 'n')) { - glyph_count = glyph_count + 1.0; - } - if (((set[i] == 'A') || (set[i] == 'B')) || (set[i] == 'C')) { - if (set[i] != last_set) { - last_set = set[i]; - glyph_count = glyph_count + 1.0; - } - } - if (i == 0) { - if ((set[i] == 'B') && (set[1] == 'C')) { - glyph_count = glyph_count - 1.0; - } - if ((set[i] == 'B') && (set[1] == 'B') && set[2] == 'C') { - glyph_count = glyph_count - 1.0; - } - if (fset[i] == 'F') { - glyph_count = glyph_count + 2.0; - } - } else { - if ((fset[i] == 'F') && (fset[i - 1] != 'F')) { - glyph_count = glyph_count + 2.0; - } - if ((fset[i] != 'F') && (fset[i - 1] == 'F')) { - glyph_count = glyph_count + 2.0; - } - } - - if ((set[i] == 'C') && (inputData[i] != FNC1)) { - glyph_count = glyph_count + 0.5; - } else { - glyph_count = glyph_count + 1.0; - } - } - - if ((inputDataType == DataType.GS1) && (set[0] != 'A')) { - /* FNC1 can be integrated with mode character */ - glyph_count--; - } - - if (glyph_count > 77.0) { - throw OkapiInputException.inputTooLong(); - } - - /* Calculate how tall the symbol will be */ - glyph_count = glyph_count + 2.0; - i = (int) glyph_count; - rows_needed = (i / 5); - if (i % 5 > 0) { - rows_needed++; - } - - if (rows_needed == 1) { - rows_needed = 2; - } - - /* start with the mode character - Table 2 */ - m = 0; - switch (set[0]) { - case 'A': - m = 0; - break; - case 'B': - m = 1; - break; - case 'C': - m = 2; - break; - } - - if(readerInit) { - if(m == 2) { m = 5; } - if((set[0] == 'B') && (set[1] == 'C')) { m = 6; } - values[bar_characters] = (7 * (rows_needed - 2)) + m; /* see 4.3.4.2 */ - values[bar_characters + 1] = 96; /* FNC3 */ - bar_characters += 2; - } else { - if (inputDataType == DataType.GS1) { - /* Integrate FNC1 */ - switch (set[0]) { - case 'B': - m = 3; - break; - case 'C': - m = 4; - break; - } - } else { - if ((set[0] == 'B') && (set[1] == 'C')) { - m = 5; - } - if (((set[0] == 'B') && (set[1] == 'B')) && (set[2] == 'C')) { - m = 6; - } - } - } - values[bar_characters] = (7 * (rows_needed - 2)) + m; /* see 4.3.4.2 */ - bar_characters++; - - current_set = set[0]; - f_state = false; - /* f_state remembers if we are in Extended ASCII mode (value 1) or in ISO/IEC 646 mode (value 0) */ - if (fset[0] == 'F') { - switch (current_set) { - case 'A': - values[bar_characters] = 101; - values[bar_characters + 1] = 101; - break; - case 'B': - values[bar_characters] = 100; - values[bar_characters + 1] = 100; - break; - } - bar_characters += 2; - f_state = true; - } - - read = 0; - - /* Encode the data */ - do { - - if ((read != 0) && (set[read] != set[read - 1])) { /* Latch different code set */ - switch (set[read]) { - case 'A': - values[bar_characters] = 101; - bar_characters++; - current_set = 'A'; - break; - case 'B': - values[bar_characters] = 100; - bar_characters++; - current_set = 'B'; - break; - case 'C': - if (!((read == 1) && (set[0] == 'B'))) { /* Not Mode C/Shift B */ - if (!((read == 2) && ((set[0] == 'B') && (set[1] == 'B')))) { - /* Not Mode C/Double Shift B */ - values[bar_characters] = 99; - bar_characters++; - } - } - current_set = 'C'; - break; - } - } - if (read != 0) { - if ((fset[read] == 'F') && !f_state) { - /* Latch beginning of extended mode */ - switch (current_set) { - case 'A': - values[bar_characters] = 101; - values[bar_characters + 1] = 101; - break; - case 'B': - values[bar_characters] = 100; - values[bar_characters + 1] = 100; - break; - } - bar_characters += 2; - f_state = true; - } - if ((fset[read] == ' ') && f_state) { - /* Latch end of extended mode */ - switch (current_set) { - case 'A': - values[bar_characters] = 101; - values[bar_characters + 1] = 101; - break; - case 'B': - values[bar_characters] = 100; - values[bar_characters + 1] = 100; - break; - } - bar_characters += 2; - f_state = false; - } - } - - if ((fset[i] == 'f') || (fset[i] == 'n')) { - /* Shift extended mode */ - switch (current_set) { - case 'A': - values[bar_characters] = 101; /* FNC 4 */ - break; - case 'B': - values[bar_characters] = 100; /* FNC 4 */ - break; - } - bar_characters++; - } - - if ((set[i] == 'a') || (set[i] == 'b')) { - /* Insert shift character */ - values[bar_characters] = 98; - bar_characters++; - } - - if (inputData[read] != FNC1) { - switch (set[read]) { /* Encode data characters */ - case 'A': - case 'a': - getValueSubsetA(inputData[read], values, bar_characters); - bar_characters++; - read++; - break; - case 'B': - case 'b': - getValueSubsetB(inputData[read], values, bar_characters); - bar_characters++; - read++; - break; - case 'C': - getValueSubsetC(inputData[read], inputData[read + 1], values, bar_characters); - bar_characters++; - read += 2; - break; - } - } else { - values[bar_characters] = 102; - bar_characters++; - read++; - } - - } while (read < input_length); - - pads_needed = 5 - ((bar_characters + 2) % 5); - if (pads_needed == 5) { - pads_needed = 0; - } - if ((bar_characters + pads_needed) < 8) { - pads_needed += 8 - (bar_characters + pads_needed); - } - for (i = 0; i < pads_needed; i++) { - values[bar_characters] = 106; - bar_characters++; - } - - /* Calculate check digits */ - first_sum = 0; - second_sum = 0; - for (i = 0; i < bar_characters; i++) { - first_sum += (i + 2) * values[i]; - second_sum += (i + 1) * values[i]; - } - first_check = first_sum % 107; - second_sum += first_check * (bar_characters + 1); - second_check = second_sum % 107; - values[bar_characters] = first_check; - values[bar_characters + 1] = second_check; - bar_characters += 2; - - readable = ""; - pattern = new String[rows_needed]; - row_count = rows_needed; - row_height = new int[rows_needed]; - - infoLine("Symbol Rows: " + rows_needed); - infoLine("First Check Digit: " + first_check); - infoLine("Second Check Digit: " + second_check); - info("Codewords: "); - - for (current_row = 0; current_row < rows_needed; current_row++) { - - width_pattern = ""; - width_pattern += C16K_START_STOP[C16K_START_VALUES[current_row]]; - width_pattern += "1"; - for (i = 0; i < 5; i++) { - width_pattern += C16K_TABLE[values[(current_row * 5) + i]]; - infoSpace(values[(current_row * 5) + i]); - } - width_pattern += C16K_START_STOP[C16K_STOP_VALUES[current_row]]; - - pattern[current_row] = width_pattern; - row_height[current_row] = 10; - } - infoLine(); - } - - private void getValueSubsetA(int source, int[] values, int bar_chars) { - if (source > 127) { - if (source < 160) { - values[bar_chars] = source + 64 - 128; - } else { - values[bar_chars] = source - 32 - 128; - } - } else { - if (source < 32) { - values[bar_chars] = source + 64; - } else { - values[bar_chars] = source - 32; - } - } - } - - private void getValueSubsetB(int source, int[] values, int bar_chars) { - if (source > 127) { - values[bar_chars] = source - 32 - 128; - } else { - values[bar_chars] = source - 32; - } - } - - private void getValueSubsetC(int source_a, int source_b, int[] values, int bar_chars) { - int weight; - - weight = (10 * Character.getNumericValue(source_a)) + Character.getNumericValue(source_b); - values[bar_chars] = weight; - } - - private Mode findSubset(int letter) { - Mode mode; - - if (letter <= 31) { - mode = Mode.SHIFTA; - } else if ((letter >= 48) && (letter <= 57)) { - mode = Mode.ABORC; - } else if (letter <= 95) { - mode = Mode.AORB; - } else if (letter <= 127) { - mode = Mode.SHIFTB; - } else if (letter <= 159) { - mode = Mode.SHIFTA; - } else if (letter <= 223) { - mode = Mode.AORB; - } else { - mode = Mode.SHIFTB; - } - - return mode; - } - - private void reduceSubsetChanges(int block_count) { /* Implements rules from ISO 15417 Annex E */ - int i, length; - Mode current, last, next; - - for (i = 0; i < block_count; i++) { - current = block_mode[i]; - length = block_length[i]; - if (i != 0) { - last = block_mode[i - 1]; - } else { - last = Mode.NULL; - } - if (i != block_count - 1) { - next = block_mode[i + 1]; - } else { - next = Mode.NULL; - } - - if (i == 0) { /* first block */ - if ((block_count == 1) && ((length == 2) && (current == Mode.ABORC))) { /* Rule 1a */ - block_mode[i] = Mode.LATCHC; - } - if (current == Mode.ABORC) { - if (length >= 4) { /* Rule 1b */ - block_mode[i] = Mode.LATCHC; - } else { - block_mode[i] = Mode.AORB; - current = Mode.AORB; - } - } - if (current == Mode.SHIFTA) { /* Rule 1c */ - block_mode[i] = Mode.LATCHA; - } - if ((current == Mode.AORB) && (next == Mode.SHIFTA)) { /* Rule 1c */ - block_mode[i] = Mode.LATCHA; - current = Mode.LATCHA; - } - if (current == Mode.AORB) { /* Rule 1d */ - block_mode[i] = Mode.LATCHB; - } - } else { - if ((current == Mode.ABORC) && (length >= 4)) { /* Rule 3 */ - block_mode[i] = Mode.LATCHC; - current = Mode.LATCHC; - } - if (current == Mode.ABORC) { - block_mode[i] = Mode.AORB; - current = Mode.AORB; - } - if ((current == Mode.AORB) && (last == Mode.LATCHA)) { - block_mode[i] = Mode.LATCHA; - current = Mode.LATCHA; - } - if ((current == Mode.AORB) && (last == Mode.LATCHB)) { - block_mode[i] = Mode.LATCHB; - current = Mode.LATCHB; - } - if ((current == Mode.AORB) && (next == Mode.SHIFTA)) { - block_mode[i] = Mode.LATCHA; - current = Mode.LATCHA; - } - if ((current == Mode.AORB) && (next == Mode.SHIFTB)) { - block_mode[i] = Mode.LATCHB; - current = Mode.LATCHB; - } - if (current == Mode.AORB) { - block_mode[i] = Mode.LATCHB; - current = Mode.LATCHB; - } - if ((current == Mode.SHIFTA) && (length > 1)) { /* Rule 4 */ - block_mode[i] = Mode.LATCHA; - current = Mode.LATCHA; - } - if ((current == Mode.SHIFTB) && (length > 1)) { /* Rule 5 */ - block_mode[i] = Mode.LATCHB; - current = Mode.LATCHB; - } - if ((current == Mode.SHIFTA) && (last == Mode.LATCHA)) { - block_mode[i] = Mode.LATCHA; - current = Mode.LATCHA; - } - if ((current == Mode.SHIFTB) && (last == Mode.LATCHB)) { - block_mode[i] = Mode.LATCHB; - current = Mode.LATCHB; - } - if ((current == Mode.SHIFTA) && (last == Mode.LATCHC)) { - block_mode[i] = Mode.LATCHA; - current = Mode.LATCHA; - } - if ((current == Mode.SHIFTB) && (last == Mode.LATCHC)) { - block_mode[i] = Mode.LATCHB; - current = Mode.LATCHB; - } - } /* Rule 2 is implimented elsewhere, Rule 6 is implied */ - } - combineSubsetBlocks(block_count); - - } - - private void combineSubsetBlocks(int block_count) { - int i, j; - - /* bring together same type blocks */ - if (block_count > 1) { - i = 1; - while (i < block_count) { - if (block_mode[i - 1] == block_mode[i]) { - /* bring together */ - block_length[i - 1] = block_length[i - 1] + block_length[i]; - j = i + 1; - - /* decreace the list */ - while (j < block_count) { - block_length[j - 1] = block_length[j]; - block_mode[j - 1] = block_mode[j]; - j++; - } - block_count = block_count - 1; - i--; - } - i++; - } - } - } - - @Override - protected void plotSymbol() { - int xBlock, yBlock; - int x, y, w, h; - boolean black; - - resetPlotElements(); - - y = 1; - h = 1; - for (yBlock = 0; yBlock < row_count; yBlock++) { - black = true; - x = 15; - for (xBlock = 0; xBlock < pattern[yBlock].length(); xBlock++) { - if (black) { - black = false; - w = pattern[yBlock].charAt(xBlock) - '0'; - if (row_height[yBlock] == -1) { - h = default_height; - } else { - h = row_height[yBlock]; - } - if (w != 0 && h != 0) { - Rectangle rect = new Rectangle(x, y, w, h); - rectangles.add(rect); - } - if ((x + w) > symbol_width) { - symbol_width = x + w; - } - } else { - black = true; - } - x += pattern[yBlock].charAt(xBlock) - '0'; - } - y += h; - if (y > symbol_height) { - symbol_height = y; - } - /* Add bars between rows */ - if (yBlock != (row_count - 1)) { - Rectangle rect = new Rectangle(15, y - 1, (symbol_width - 15), 2); - rectangles.add(rect); - } - } - - /* Add top and bottom binding bars */ - Rectangle top = new Rectangle(0, 0, (symbol_width + 15), 2); - rectangles.add(top); - Rectangle bottom = new Rectangle(0, y - 1, (symbol_width + 15), 2); - rectangles.add(bottom); - symbol_width += 15; - symbol_height += 1; - } -} +/* + * Copyright 2014 Robin Stuart + * + * 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. + */ + +package uk.org.okapibarcode.backend; + +import java.nio.charset.StandardCharsets; + +import uk.org.okapibarcode.graphics.Rectangle; + +/** + *

Implements Code 16K symbology according to BS EN 12323:2005. + * + *

Encodes using a stacked symbology based on Code 128. Supports encoding + * of any 8-bit ISO 8859-1 (Latin-1) data with a maximum data capacity of 77 + * alpha-numeric characters or 154 numerical digits. + * + * @author Robin Stuart + */ +public class Code16k extends Symbol { + + private enum Mode { + NULL, SHIFTA, LATCHA, SHIFTB, LATCHB, SHIFTC, LATCHC, AORB, ABORC, CANDB, CANDBB + } + + /* EN 12323 Table 1 - "Code 16K" character encodations */ + private static final String[] C16K_TABLE = { + "212222", "222122", "222221", "121223", "121322", "131222", "122213", + "122312", "132212", "221213", "221312", "231212", "112232", "122132", + "122231", "113222", "123122", "123221", "223211", "221132", "221231", + "213212", "223112", "312131", "311222", "321122", "321221", "312212", + "322112", "322211", "212123", "212321", "232121", "111323", "131123", + "131321", "112313", "132113", "132311", "211313", "231113", "231311", + "112133", "112331", "132131", "113123", "113321", "133121", "313121", + "211331", "231131", "213113", "213311", "213131", "311123", "311321", + "331121", "312113", "312311", "332111", "314111", "221411", "431111", + "111224", "111422", "121124", "121421", "141122", "141221", "112214", + "112412", "122114", "122411", "142112", "142211", "241211", "221114", + "413111", "241112", "134111", "111242", "121142", "121241", "114212", + "124112", "124211", "411212", "421112", "421211", "212141", "214121", + "412121", "111143", "111341", "131141", "114113", "114311", "411113", + "411311", "113141", "114131", "311141", "411131", "211412", "211214", + "211232", "211133" + }; + + /* EN 12323 Table 3 and Table 4 - Start patterns and stop patterns */ + private static final String[] C16K_START_STOP = { + "3211", "2221", "2122", "1411", "1132", "1231", "1114", "3112" + }; + + /* EN 12323 Table 5 - Start and stop values defining row numbers */ + private static final int[] C16K_START_VALUES = { + 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7 + }; + private static final int[] C16K_STOP_VALUES = { + 0, 1, 2, 3, 4, 5, 6, 7, 4, 5, 6, 7, 0, 1, 2, 3 + }; + + private Mode[] block_mode = new Mode[170]; /* RENAME block_mode */ + private int[] block_length = new int[170]; /* RENAME block_length */ + private int block_count; + + @Override + protected boolean gs1Supported() { + return true; + } + + @Override + protected void encode() { + + // TODO: is it possible to share any of this code with Code128, which is more up to date? + + String width_pattern; + int current_row, rows_needed, first_check, second_check; + int indexchaine, pads_needed; + char[] set, fset; + Mode mode; + char last_set, current_set; + int i, j, k, m, read; + int[] values; + int bar_characters; + double glyph_count; + int first_sum, second_sum; + int input_length; + int c_count; + boolean f_state; + + if (!content.matches("[\u0000-\u00FF]+")) { + throw OkapiInputException.invalidCharactersInInput(); + } + + inputData = toBytes(content, StandardCharsets.ISO_8859_1); + input_length = inputData.length; + + bar_characters = 0; + set = new char[160]; + fset = new char[160]; + values = new int[160]; + + if (input_length > 157) { + throw OkapiInputException.inputTooLong(); + } + + /* Detect extended ASCII characters */ + for (i = 0; i < input_length; i++) { + if (inputData[i] >= 128) { + fset[i] = 'f'; + } else { + fset[i] = ' '; + } + } + + /* Decide when to latch to extended mode */ + for (i = 0; i < input_length; i++) { + j = 0; + if (fset[i] == 'f') { + do { + j++; + } while (fset[i + j] == 'f'); + if ((j >= 5) || ((j >= 3) && ((i + j) == (input_length - 1)))) { + for (k = 0; k <= j; k++) { + fset[i + k] = 'F'; + } + } + } + } + + /* Decide if it is worth reverting to 646 encodation for a few characters */ + if (input_length > 1) { + for (i = 1; i < input_length; i++) { + if ((fset[i - 1] == 'F') && (fset[i] == ' ')) { + /* Detected a change from 8859-1 to 646 - count how long for */ + for (j = 0; + (fset[i + j] == ' ') && ((i + j) < input_length); j++); + if ((j < 5) || ((j < 3) && ((i + j) == (input_length - 1)))) { + /* Change to shifting back rather than latching back */ + for (k = 0; k < j; k++) { + fset[i + k] = 'n'; + } + } + } + } + } + + /* Detect mode A, B and C characters */ + block_count = 0; + indexchaine = 0; + + mode = findSubset(inputData[indexchaine]); + if (inputData[indexchaine] == FNC1) { + mode = Mode.ABORC; + } /* FNC1 */ + + for (i = 0; i < 160; i++) { + block_length[i] = 0; + } + + do { + block_mode[block_count] = mode; + while ((block_mode[block_count] == mode) && (indexchaine < input_length)) { + block_length[block_count]++; + indexchaine++; + if (indexchaine < input_length) { + mode = findSubset(inputData[indexchaine]); + if (inputData[indexchaine] == FNC1) { + mode = Mode.ABORC; + } /* FNC1 */ + } + } + block_count++; + } while (indexchaine < input_length); + + reduceSubsetChanges(block_count); + + + /* Put set data into set[] */ + read = 0; + for (i = 0; i < block_count; i++) { + for (j = 0; j < block_length[i]; j++) { + switch (block_mode[i]) { + case SHIFTA: + set[read] = 'a'; + break; + case LATCHA: + set[read] = 'A'; + break; + case SHIFTB: + set[read] = 'b'; + break; + case LATCHB: + set[read] = 'B'; + break; + case LATCHC: + set[read] = 'C'; + break; + } + read++; + } + } + + /* Adjust for strings which start with shift characters - make them latch instead */ + if (set[0] == 'a') { + i = 0; + do { + set[i] = 'A'; + i++; + } while (set[i] == 'a'); + } + + if (set[0] == 'b') { + i = 0; + do { + set[i] = 'B'; + i++; + } while (set[i] == 'b'); + } + + /* Watch out for odd-length Mode C blocks */ + c_count = 0; + for (i = 0; i < read; i++) { + if (set[i] == 'C') { + if (inputData[i] == FNC1) { + if ((c_count & 1) != 0) { + if ((i - c_count) != 0) { + set[i - c_count] = 'B'; + } else { + set[i - 1] = 'B'; + } + } + c_count = 0; + } else { + c_count++; + } + } else { + if ((c_count & 1) != 0) { + if ((i - c_count) != 0) { + set[i - c_count] = 'B'; + } else { + set[i - 1] = 'B'; + } + } + c_count = 0; + } + } + if ((c_count & 1) != 0) { + if ((i - c_count) != 0) { + set[i - c_count] = 'B'; + } else { + set[i - 1] = 'B'; + } + } + for (i = 1; i < read - 1; i++) { + if ((set[i] == 'C') && ((set[i - 1] == 'B') && (set[i + 1] == 'B'))) { + set[i] = 'B'; + } + } + + /* Make sure the data will fit in the symbol */ + last_set = ' '; + glyph_count = 0.0; + for (i = 0; i < input_length; i++) { + if ((set[i] == 'a') || (set[i] == 'b')) { + glyph_count = glyph_count + 1.0; + } + if ((fset[i] == 'f') || (fset[i] == 'n')) { + glyph_count = glyph_count + 1.0; + } + if (((set[i] == 'A') || (set[i] == 'B')) || (set[i] == 'C')) { + if (set[i] != last_set) { + last_set = set[i]; + glyph_count = glyph_count + 1.0; + } + } + if (i == 0) { + if ((set[i] == 'B') && (set[1] == 'C')) { + glyph_count = glyph_count - 1.0; + } + if ((set[i] == 'B') && (set[1] == 'B') && set[2] == 'C') { + glyph_count = glyph_count - 1.0; + } + if (fset[i] == 'F') { + glyph_count = glyph_count + 2.0; + } + } else { + if ((fset[i] == 'F') && (fset[i - 1] != 'F')) { + glyph_count = glyph_count + 2.0; + } + if ((fset[i] != 'F') && (fset[i - 1] == 'F')) { + glyph_count = glyph_count + 2.0; + } + } + + if ((set[i] == 'C') && (inputData[i] != FNC1)) { + glyph_count = glyph_count + 0.5; + } else { + glyph_count = glyph_count + 1.0; + } + } + + if ((inputDataType == DataType.GS1) && (set[0] != 'A')) { + /* FNC1 can be integrated with mode character */ + glyph_count--; + } + + if (glyph_count > 77.0) { + throw OkapiInputException.inputTooLong(); + } + + /* Calculate how tall the symbol will be */ + glyph_count = glyph_count + 2.0; + i = (int) glyph_count; + rows_needed = (i / 5); + if (i % 5 > 0) { + rows_needed++; + } + + if (rows_needed == 1) { + rows_needed = 2; + } + + /* start with the mode character - Table 2 */ + m = 0; + switch (set[0]) { + case 'A': + m = 0; + break; + case 'B': + m = 1; + break; + case 'C': + m = 2; + break; + } + + if(readerInit) { + if(m == 2) { m = 5; } + if((set[0] == 'B') && (set[1] == 'C')) { m = 6; } + values[bar_characters] = (7 * (rows_needed - 2)) + m; /* see 4.3.4.2 */ + values[bar_characters + 1] = 96; /* FNC3 */ + bar_characters += 2; + } else { + if (inputDataType == DataType.GS1) { + /* Integrate FNC1 */ + switch (set[0]) { + case 'B': + m = 3; + break; + case 'C': + m = 4; + break; + } + } else { + if ((set[0] == 'B') && (set[1] == 'C')) { + m = 5; + } + if (((set[0] == 'B') && (set[1] == 'B')) && (set[2] == 'C')) { + m = 6; + } + } + } + values[bar_characters] = (7 * (rows_needed - 2)) + m; /* see 4.3.4.2 */ + bar_characters++; + + current_set = set[0]; + f_state = false; + /* f_state remembers if we are in Extended ASCII mode (value 1) or in ISO/IEC 646 mode (value 0) */ + if (fset[0] == 'F') { + switch (current_set) { + case 'A': + values[bar_characters] = 101; + values[bar_characters + 1] = 101; + break; + case 'B': + values[bar_characters] = 100; + values[bar_characters + 1] = 100; + break; + } + bar_characters += 2; + f_state = true; + } + + read = 0; + + /* Encode the data */ + do { + + if ((read != 0) && (set[read] != set[read - 1])) { /* Latch different code set */ + switch (set[read]) { + case 'A': + values[bar_characters] = 101; + bar_characters++; + current_set = 'A'; + break; + case 'B': + values[bar_characters] = 100; + bar_characters++; + current_set = 'B'; + break; + case 'C': + if (!((read == 1) && (set[0] == 'B'))) { /* Not Mode C/Shift B */ + if (!((read == 2) && ((set[0] == 'B') && (set[1] == 'B')))) { + /* Not Mode C/Double Shift B */ + values[bar_characters] = 99; + bar_characters++; + } + } + current_set = 'C'; + break; + } + } + if (read != 0) { + if ((fset[read] == 'F') && !f_state) { + /* Latch beginning of extended mode */ + switch (current_set) { + case 'A': + values[bar_characters] = 101; + values[bar_characters + 1] = 101; + break; + case 'B': + values[bar_characters] = 100; + values[bar_characters + 1] = 100; + break; + } + bar_characters += 2; + f_state = true; + } + if ((fset[read] == ' ') && f_state) { + /* Latch end of extended mode */ + switch (current_set) { + case 'A': + values[bar_characters] = 101; + values[bar_characters + 1] = 101; + break; + case 'B': + values[bar_characters] = 100; + values[bar_characters + 1] = 100; + break; + } + bar_characters += 2; + f_state = false; + } + } + + if ((fset[i] == 'f') || (fset[i] == 'n')) { + /* Shift extended mode */ + switch (current_set) { + case 'A': + values[bar_characters] = 101; /* FNC 4 */ + break; + case 'B': + values[bar_characters] = 100; /* FNC 4 */ + break; + } + bar_characters++; + } + + if ((set[i] == 'a') || (set[i] == 'b')) { + /* Insert shift character */ + values[bar_characters] = 98; + bar_characters++; + } + + if (inputData[read] != FNC1) { + switch (set[read]) { /* Encode data characters */ + case 'A': + case 'a': + getValueSubsetA(inputData[read], values, bar_characters); + bar_characters++; + read++; + break; + case 'B': + case 'b': + getValueSubsetB(inputData[read], values, bar_characters); + bar_characters++; + read++; + break; + case 'C': + getValueSubsetC(inputData[read], inputData[read + 1], values, bar_characters); + bar_characters++; + read += 2; + break; + } + } else { + values[bar_characters] = 102; + bar_characters++; + read++; + } + + } while (read < input_length); + + pads_needed = 5 - ((bar_characters + 2) % 5); + if (pads_needed == 5) { + pads_needed = 0; + } + if ((bar_characters + pads_needed) < 8) { + pads_needed += 8 - (bar_characters + pads_needed); + } + for (i = 0; i < pads_needed; i++) { + values[bar_characters] = 106; + bar_characters++; + } + + /* Calculate check digits */ + first_sum = 0; + second_sum = 0; + for (i = 0; i < bar_characters; i++) { + first_sum += (i + 2) * values[i]; + second_sum += (i + 1) * values[i]; + } + first_check = first_sum % 107; + second_sum += first_check * (bar_characters + 1); + second_check = second_sum % 107; + values[bar_characters] = first_check; + values[bar_characters + 1] = second_check; + bar_characters += 2; + + readable = ""; + pattern = new String[rows_needed]; + row_count = rows_needed; + row_height = new int[rows_needed]; + + infoLine("Symbol Rows: " + rows_needed); + infoLine("First Check Digit: " + first_check); + infoLine("Second Check Digit: " + second_check); + info("Codewords: "); + + for (current_row = 0; current_row < rows_needed; current_row++) { + + width_pattern = ""; + width_pattern += C16K_START_STOP[C16K_START_VALUES[current_row]]; + width_pattern += "1"; + for (i = 0; i < 5; i++) { + width_pattern += C16K_TABLE[values[(current_row * 5) + i]]; + infoSpace(values[(current_row * 5) + i]); + } + width_pattern += C16K_START_STOP[C16K_STOP_VALUES[current_row]]; + + pattern[current_row] = width_pattern; + row_height[current_row] = 10; + } + infoLine(); + } + + private void getValueSubsetA(int source, int[] values, int bar_chars) { + if (source > 127) { + if (source < 160) { + values[bar_chars] = source + 64 - 128; + } else { + values[bar_chars] = source - 32 - 128; + } + } else { + if (source < 32) { + values[bar_chars] = source + 64; + } else { + values[bar_chars] = source - 32; + } + } + } + + private void getValueSubsetB(int source, int[] values, int bar_chars) { + if (source > 127) { + values[bar_chars] = source - 32 - 128; + } else { + values[bar_chars] = source - 32; + } + } + + private void getValueSubsetC(int source_a, int source_b, int[] values, int bar_chars) { + int weight; + + weight = (10 * Character.getNumericValue(source_a)) + Character.getNumericValue(source_b); + values[bar_chars] = weight; + } + + private Mode findSubset(int letter) { + Mode mode; + + if (letter <= 31) { + mode = Mode.SHIFTA; + } else if ((letter >= 48) && (letter <= 57)) { + mode = Mode.ABORC; + } else if (letter <= 95) { + mode = Mode.AORB; + } else if (letter <= 127) { + mode = Mode.SHIFTB; + } else if (letter <= 159) { + mode = Mode.SHIFTA; + } else if (letter <= 223) { + mode = Mode.AORB; + } else { + mode = Mode.SHIFTB; + } + + return mode; + } + + private void reduceSubsetChanges(int block_count) { /* Implements rules from ISO 15417 Annex E */ + int i, length; + Mode current, last, next; + + for (i = 0; i < block_count; i++) { + current = block_mode[i]; + length = block_length[i]; + if (i != 0) { + last = block_mode[i - 1]; + } else { + last = Mode.NULL; + } + if (i != block_count - 1) { + next = block_mode[i + 1]; + } else { + next = Mode.NULL; + } + + if (i == 0) { /* first block */ + if ((block_count == 1) && ((length == 2) && (current == Mode.ABORC))) { /* Rule 1a */ + block_mode[i] = Mode.LATCHC; + } + if (current == Mode.ABORC) { + if (length >= 4) { /* Rule 1b */ + block_mode[i] = Mode.LATCHC; + } else { + block_mode[i] = Mode.AORB; + current = Mode.AORB; + } + } + if (current == Mode.SHIFTA) { /* Rule 1c */ + block_mode[i] = Mode.LATCHA; + } + if ((current == Mode.AORB) && (next == Mode.SHIFTA)) { /* Rule 1c */ + block_mode[i] = Mode.LATCHA; + current = Mode.LATCHA; + } + if (current == Mode.AORB) { /* Rule 1d */ + block_mode[i] = Mode.LATCHB; + } + } else { + if ((current == Mode.ABORC) && (length >= 4)) { /* Rule 3 */ + block_mode[i] = Mode.LATCHC; + current = Mode.LATCHC; + } + if (current == Mode.ABORC) { + block_mode[i] = Mode.AORB; + current = Mode.AORB; + } + if ((current == Mode.AORB) && (last == Mode.LATCHA)) { + block_mode[i] = Mode.LATCHA; + current = Mode.LATCHA; + } + if ((current == Mode.AORB) && (last == Mode.LATCHB)) { + block_mode[i] = Mode.LATCHB; + current = Mode.LATCHB; + } + if ((current == Mode.AORB) && (next == Mode.SHIFTA)) { + block_mode[i] = Mode.LATCHA; + current = Mode.LATCHA; + } + if ((current == Mode.AORB) && (next == Mode.SHIFTB)) { + block_mode[i] = Mode.LATCHB; + current = Mode.LATCHB; + } + if (current == Mode.AORB) { + block_mode[i] = Mode.LATCHB; + current = Mode.LATCHB; + } + if ((current == Mode.SHIFTA) && (length > 1)) { /* Rule 4 */ + block_mode[i] = Mode.LATCHA; + current = Mode.LATCHA; + } + if ((current == Mode.SHIFTB) && (length > 1)) { /* Rule 5 */ + block_mode[i] = Mode.LATCHB; + current = Mode.LATCHB; + } + if ((current == Mode.SHIFTA) && (last == Mode.LATCHA)) { + block_mode[i] = Mode.LATCHA; + current = Mode.LATCHA; + } + if ((current == Mode.SHIFTB) && (last == Mode.LATCHB)) { + block_mode[i] = Mode.LATCHB; + current = Mode.LATCHB; + } + if ((current == Mode.SHIFTA) && (last == Mode.LATCHC)) { + block_mode[i] = Mode.LATCHA; + current = Mode.LATCHA; + } + if ((current == Mode.SHIFTB) && (last == Mode.LATCHC)) { + block_mode[i] = Mode.LATCHB; + current = Mode.LATCHB; + } + } /* Rule 2 is implimented elsewhere, Rule 6 is implied */ + } + combineSubsetBlocks(block_count); + + } + + private void combineSubsetBlocks(int block_count) { + int i, j; + + /* bring together same type blocks */ + if (block_count > 1) { + i = 1; + while (i < block_count) { + if (block_mode[i - 1] == block_mode[i]) { + /* bring together */ + block_length[i - 1] = block_length[i - 1] + block_length[i]; + j = i + 1; + + /* decreace the list */ + while (j < block_count) { + block_length[j - 1] = block_length[j]; + block_mode[j - 1] = block_mode[j]; + j++; + } + block_count = block_count - 1; + i--; + } + i++; + } + } + } + + @Override + protected void plotSymbol() { + int xBlock, yBlock; + int x, y, w, h; + boolean black; + + resetPlotElements(); + + y = 1; + h = 1; + for (yBlock = 0; yBlock < row_count; yBlock++) { + black = true; + x = 15; + for (xBlock = 0; xBlock < pattern[yBlock].length(); xBlock++) { + if (black) { + black = false; + w = pattern[yBlock].charAt(xBlock) - '0'; + if (row_height[yBlock] == -1) { + h = default_height; + } else { + h = row_height[yBlock]; + } + if (w != 0 && h != 0) { + Rectangle rect = new Rectangle(x, y, w, h); + rectangles.add(rect); + } + if ((x + w) > symbol_width) { + symbol_width = x + w; + } + } else { + black = true; + } + x += pattern[yBlock].charAt(xBlock) - '0'; + } + y += h; + if (y > symbol_height) { + symbol_height = y; + } + /* Add bars between rows */ + if (yBlock != (row_count - 1)) { + Rectangle rect = new Rectangle(15, y - 1, (symbol_width - 15), 2); + rectangles.add(rect); + } + } + + /* Add top and bottom binding bars */ + Rectangle top = new Rectangle(0, 0, (symbol_width + 15), 2); + rectangles.add(top); + Rectangle bottom = new Rectangle(0, y - 1, (symbol_width + 15), 2); + rectangles.add(bottom); + symbol_width += 15; + symbol_height += 1; + } +} diff --git a/src/main/java/uk/org/okapibarcode/backend/Code32.java b/src/main/java/uk/org/okapibarcode/backend/Code32.java index d612dc37..81daed26 100644 --- a/src/main/java/uk/org/okapibarcode/backend/Code32.java +++ b/src/main/java/uk/org/okapibarcode/backend/Code32.java @@ -1,109 +1,109 @@ -/* - * Copyright 2014 Robin Stuart - * - * 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. - */ - -package uk.org.okapibarcode.backend; - -/** - *

Implements Code 32, also known as Italian Pharmacode, A variation of Code - * 39 used by the Italian Ministry of Health ("Ministero della Sanità") - * - *

Requires a numeric input up to 8 digits in length. Check digit is - * calculated. - * - * @author Robin Stuart - */ -public class Code32 extends Symbol { - - private static final char[] TABLE = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'B', 'C', 'D', 'F', - 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', - 'W', 'X', 'Y', 'Z' - }; - - @Override - protected void encode() { - - if (content.length() > 8) { - throw OkapiInputException.inputTooLong(); - } - - if (!content.matches("[0-9]+")) { - throw OkapiInputException.invalidCharactersInInput(); - } - - /* Add leading zeros as required */ - String localstr = ""; - for (int i = content.length(); i < 8; i++) { - localstr += "0"; - } - localstr += content; - - /* Calculate the check digit */ - int checksum = 0; - int checkpart = 0; - for (int i = 0; i < 4; i++) { - checkpart = Character.getNumericValue(localstr.charAt(i * 2)); - checksum += checkpart; - checkpart = 2 * Character.getNumericValue(localstr.charAt((i * 2) + 1)); - if (checkpart >= 10) { - checksum += (checkpart - 10) + 1; - } else { - checksum += checkpart; - } - } - - /* Add check digit to data string */ - int checkdigit = checksum % 10; - char check = (char) (checkdigit + '0'); - localstr += check; - infoLine("Check Digit: " + check); - - /* Convert string into an integer value */ - int pharmacode = 0; - for (int i = 0; i < localstr.length(); i++) { - pharmacode *= 10; - pharmacode += Character.getNumericValue(localstr.charAt(i)); - } - - /* Convert from decimal to base-32 */ - int devisor = 33554432; - int[] codeword = new int[6]; - for (int i = 5; i >= 0; i--) { - codeword[i] = pharmacode / devisor; - int remainder = pharmacode % devisor; - pharmacode = remainder; - devisor /= 32; - } - - /* Look up values in 'Tabella di conversione' */ - String risultante = ""; - for (int i = 5; i >= 0; i--) { - risultante += TABLE[codeword[i]]; - } - - /* Plot the barcode using Code 39 */ - - readable = "A" + localstr; - pattern = new String[1]; - row_count = 1; - row_height = new int[] { -1 }; - infoLine("Code 39 Equivalent: " + risultante); - - Code3Of9 c39 = new Code3Of9(); - c39.setContent(risultante); - pattern[0] = c39.pattern[0]; - } -} +/* + * Copyright 2014 Robin Stuart + * + * 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. + */ + +package uk.org.okapibarcode.backend; + +/** + *

Implements Code 32, also known as Italian Pharmacode, A variation of Code + * 39 used by the Italian Ministry of Health ("Ministero della Sanità") + * + *

Requires a numeric input up to 8 digits in length. Check digit is + * calculated. + * + * @author Robin Stuart + */ +public class Code32 extends Symbol { + + private static final char[] TABLE = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'B', 'C', 'D', 'F', + 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', + 'W', 'X', 'Y', 'Z' + }; + + @Override + protected void encode() { + + if (content.length() > 8) { + throw OkapiInputException.inputTooLong(); + } + + if (!content.matches("[0-9]+")) { + throw OkapiInputException.invalidCharactersInInput(); + } + + /* Add leading zeros as required */ + String localstr = ""; + for (int i = content.length(); i < 8; i++) { + localstr += "0"; + } + localstr += content; + + /* Calculate the check digit */ + int checksum = 0; + int checkpart = 0; + for (int i = 0; i < 4; i++) { + checkpart = Character.getNumericValue(localstr.charAt(i * 2)); + checksum += checkpart; + checkpart = 2 * Character.getNumericValue(localstr.charAt((i * 2) + 1)); + if (checkpart >= 10) { + checksum += (checkpart - 10) + 1; + } else { + checksum += checkpart; + } + } + + /* Add check digit to data string */ + int checkdigit = checksum % 10; + char check = (char) (checkdigit + '0'); + localstr += check; + infoLine("Check Digit: " + check); + + /* Convert string into an integer value */ + int pharmacode = 0; + for (int i = 0; i < localstr.length(); i++) { + pharmacode *= 10; + pharmacode += Character.getNumericValue(localstr.charAt(i)); + } + + /* Convert from decimal to base-32 */ + int devisor = 33554432; + int[] codeword = new int[6]; + for (int i = 5; i >= 0; i--) { + codeword[i] = pharmacode / devisor; + int remainder = pharmacode % devisor; + pharmacode = remainder; + devisor /= 32; + } + + /* Look up values in 'Tabella di conversione' */ + String risultante = ""; + for (int i = 5; i >= 0; i--) { + risultante += TABLE[codeword[i]]; + } + + /* Plot the barcode using Code 39 */ + + readable = "A" + localstr; + pattern = new String[1]; + row_count = 1; + row_height = new int[] { -1 }; + infoLine("Code 39 Equivalent: " + risultante); + + Code3Of9 c39 = new Code3Of9(); + c39.setContent(risultante); + pattern[0] = c39.pattern[0]; + } +} diff --git a/src/main/java/uk/org/okapibarcode/backend/Code3Of9.java b/src/main/java/uk/org/okapibarcode/backend/Code3Of9.java index 6fda6000..c21b6498 100644 --- a/src/main/java/uk/org/okapibarcode/backend/Code3Of9.java +++ b/src/main/java/uk/org/okapibarcode/backend/Code3Of9.java @@ -1,160 +1,160 @@ -/* - * Copyright 2014 Robin Stuart - * - * 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. - */ - -package uk.org.okapibarcode.backend; - -import static uk.org.okapibarcode.util.Arrays.positionOf; - -/** - *

Implements Code 39 bar code symbology according to ISO/IEC 16388:2007. - * - *

Input data can be of any length and supports the characters 0-9, A-Z, dash - * (-), full stop (.), space, asterisk (*), dollar ($), slash (/), plus (+) - * and percent (%). The standard does not require a check digit but a - * modulo-43 check digit can be added if required. - * - * @author Robin Stuart - */ -public class Code3Of9 extends Symbol { - - public enum CheckDigit { - NONE, MOD43 - } - - private static final String[] CODE_39 = { - "1112212111", "2112111121", "1122111121", "2122111111", "1112211121", - "2112211111", "1122211111", "1112112121", "2112112111", "1122112111", - "2111121121", "1121121121", "2121121111", "1111221121", "2111221111", - "1121221111", "1111122121", "2111122111", "1121122111", "1111222111", - "2111111221", "1121111221", "2121111211", "1111211221", "2111211211", - "1121211211", "1111112221", "2111112211", "1121112211", "1111212211", - "2211111121", "1221111121", "2221111111", "1211211121", "2211211111", - "1221211111", "1211112121", "2211112111", "1221112111", "1212121111", - "1212111211", "1211121211", "1112121211" - }; - - private static final char[] LOOKUP = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - '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', '-', '.', ' ', '$', - '/', '+', '%' - }; - - private CheckDigit checkOption = CheckDigit.NONE; - private double moduleWidthRatio = 2; - - /** - * Sets the ratio of wide bar width to narrow bar width. Valid values are usually between - * {@code 2} and {@code 3}. The default value is {@code 2}. - * - * @param moduleWidthRatio the ratio of wide bar width to narrow bar width - */ - public void setModuleWidthRatio(double moduleWidthRatio) { - this.moduleWidthRatio = moduleWidthRatio; - } - - /** - * Returns the ratio of wide bar width to narrow bar width. - * - * @return the ratio of wide bar width to narrow bar width - */ - public double getModuleWidthRatio() { - return moduleWidthRatio; - } - - /** - * Select addition of optional Modulo-43 check digit or encoding without check digit. By - * default, no check digit is added. - * - * @param checkMode Check digit option. - */ - public void setCheckDigit(CheckDigit checkMode) { - checkOption = checkMode; - } - - /** - * Returns the check digit mode. - * - * @return the check digit mode - */ - public CheckDigit getCheckDigit() { - return checkOption; - } - - @Override - protected void encode() { - - if (!content.matches("[0-9A-Z\\. \\-$/+%]*")) { - throw OkapiInputException.invalidCharactersInInput(); - } - - String start = "1211212111"; - String stop = "121121211"; - - int patternLength = start.length() + - stop.length() + - (10 * content.length()) + - (checkOption == CheckDigit.MOD43 ? 10 : 0); - - StringBuilder dest = new StringBuilder(patternLength); - dest.append(start); - - int counter = 0; - char checkDigit = ' '; - - for (int i = 0; i < content.length(); i++) { - char c = content.charAt(i); - int index = positionOf(c, LOOKUP); - dest.append(CODE_39[index]); - counter += index; - } - - if (checkOption == CheckDigit.MOD43) { - counter = counter % 43; - checkDigit = LOOKUP[counter]; - int index = positionOf(checkDigit, LOOKUP); - dest.append(CODE_39[index]); - if (checkDigit == ' ') { - // display a space check digit as _, otherwise it looks like an error - checkDigit = '_'; - } - infoLine("Check Digit: " + checkDigit); - } - - dest.append(stop); - - if (checkOption == CheckDigit.MOD43) { - readable = "*" + content + checkDigit + "*"; - } else { - readable = "*" + content + "*"; - } - - pattern = new String[] { dest.toString() }; - row_count = 1; - row_height = new int[] { -1 }; - } - - /** {@inheritDoc} */ - @Override - protected double getModuleWidth(int originalWidth) { - if (originalWidth == 1) { - return 1; - } else { - return moduleWidthRatio; - } - } -} +/* + * Copyright 2014 Robin Stuart + * + * 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. + */ + +package uk.org.okapibarcode.backend; + +import static uk.org.okapibarcode.util.Arrays.positionOf; + +/** + *

Implements Code 39 bar code symbology according to ISO/IEC 16388:2007. + * + *

Input data can be of any length and supports the characters 0-9, A-Z, dash + * (-), full stop (.), space, asterisk (*), dollar ($), slash (/), plus (+) + * and percent (%). The standard does not require a check digit but a + * modulo-43 check digit can be added if required. + * + * @author Robin Stuart + */ +public class Code3Of9 extends Symbol { + + public enum CheckDigit { + NONE, MOD43 + } + + private static final String[] CODE_39 = { + "1112212111", "2112111121", "1122111121", "2122111111", "1112211121", + "2112211111", "1122211111", "1112112121", "2112112111", "1122112111", + "2111121121", "1121121121", "2121121111", "1111221121", "2111221111", + "1121221111", "1111122121", "2111122111", "1121122111", "1111222111", + "2111111221", "1121111221", "2121111211", "1111211221", "2111211211", + "1121211211", "1111112221", "2111112211", "1121112211", "1111212211", + "2211111121", "1221111121", "2221111111", "1211211121", "2211211111", + "1221211111", "1211112121", "2211112111", "1221112111", "1212121111", + "1212111211", "1211121211", "1112121211" + }; + + private static final char[] LOOKUP = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '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', '-', '.', ' ', '$', + '/', '+', '%' + }; + + private CheckDigit checkOption = CheckDigit.NONE; + private double moduleWidthRatio = 2; + + /** + * Sets the ratio of wide bar width to narrow bar width. Valid values are usually between + * {@code 2} and {@code 3}. The default value is {@code 2}. + * + * @param moduleWidthRatio the ratio of wide bar width to narrow bar width + */ + public void setModuleWidthRatio(double moduleWidthRatio) { + this.moduleWidthRatio = moduleWidthRatio; + } + + /** + * Returns the ratio of wide bar width to narrow bar width. + * + * @return the ratio of wide bar width to narrow bar width + */ + public double getModuleWidthRatio() { + return moduleWidthRatio; + } + + /** + * Select addition of optional Modulo-43 check digit or encoding without check digit. By + * default, no check digit is added. + * + * @param checkMode Check digit option. + */ + public void setCheckDigit(CheckDigit checkMode) { + checkOption = checkMode; + } + + /** + * Returns the check digit mode. + * + * @return the check digit mode + */ + public CheckDigit getCheckDigit() { + return checkOption; + } + + @Override + protected void encode() { + + if (!content.matches("[0-9A-Z\\. \\-$/+%]*")) { + throw OkapiInputException.invalidCharactersInInput(); + } + + String start = "1211212111"; + String stop = "121121211"; + + int patternLength = start.length() + + stop.length() + + (10 * content.length()) + + (checkOption == CheckDigit.MOD43 ? 10 : 0); + + StringBuilder dest = new StringBuilder(patternLength); + dest.append(start); + + int counter = 0; + char checkDigit = ' '; + + for (int i = 0; i < content.length(); i++) { + char c = content.charAt(i); + int index = positionOf(c, LOOKUP); + dest.append(CODE_39[index]); + counter += index; + } + + if (checkOption == CheckDigit.MOD43) { + counter = counter % 43; + checkDigit = LOOKUP[counter]; + int index = positionOf(checkDigit, LOOKUP); + dest.append(CODE_39[index]); + if (checkDigit == ' ') { + // display a space check digit as _, otherwise it looks like an error + checkDigit = '_'; + } + infoLine("Check Digit: " + checkDigit); + } + + dest.append(stop); + + if (checkOption == CheckDigit.MOD43) { + readable = "*" + content + checkDigit + "*"; + } else { + readable = "*" + content + "*"; + } + + pattern = new String[] { dest.toString() }; + row_count = 1; + row_height = new int[] { -1 }; + } + + /** {@inheritDoc} */ + @Override + protected double getModuleWidth(int originalWidth) { + if (originalWidth == 1) { + return 1; + } else { + return moduleWidthRatio; + } + } +} diff --git a/src/main/java/uk/org/okapibarcode/backend/Code3Of9Extended.java b/src/main/java/uk/org/okapibarcode/backend/Code3Of9Extended.java index d49b455f..8f5ff600 100644 --- a/src/main/java/uk/org/okapibarcode/backend/Code3Of9Extended.java +++ b/src/main/java/uk/org/okapibarcode/backend/Code3Of9Extended.java @@ -1,91 +1,91 @@ -/* - * Copyright 2014 Robin Stuart - * - * 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. - */ - -package uk.org.okapibarcode.backend; - -/** - *

Implements Code 3 of 9 Extended, also known as Code 39e and Code39+. - * - *

Supports encoding of all characters in the 7-bit ASCII table. A modulo-43 - * check digit can be added if required. - * - * @author Robin Stuart - */ -public class Code3Of9Extended extends Symbol { - - public enum CheckDigit { - NONE, MOD43 - } - - private static final String[] E_CODE_39 = { - "%U", "$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", "%A", "%B", "%C", "%D", "%E", " ", "/A", "/B", "/C", - "/D", "/E", "/F", "/G", "/H", "/I", "/J", "/K", "/L", "-", ".", "/O", - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "/Z", "%F", "%G", - "%H", "%I", "%J", "%V", "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", "%K", "%L", "%M", "%N", "%O", "%W", "+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", "%P", - "%Q", "%R", "%S", "%T" - }; - - private CheckDigit checkDigit = CheckDigit.NONE; - - /** - * Sets the check digit scheme (no check digit, or a modulo-43 check digit). By default, no check digit is added. - * - * @param checkDigit the check digit scheme - */ - public void setCheckDigit(CheckDigit checkDigit) { - this.checkDigit = checkDigit; - } - - /** - * Returns the check digit scheme (no check digit, or a modulo-43 check digit). By default, no check digit is added. - * - * @return the check digit scheme - */ - public CheckDigit getCheckDigit() { - return checkDigit; - } - - @Override - protected void encode() { - - if (!content.matches("[\u0000-\u007F]+")) { - throw OkapiInputException.invalidCharactersInInput(); - } - - StringBuilder s = new StringBuilder(content.length() * 2); - for (int i = 0; i < content.length(); i++) { - char c = content.charAt(i); - s.append(E_CODE_39[c]); - } - - Code3Of9 code39 = new Code3Of9(); - if (checkDigit == CheckDigit.MOD43) { - code39.setCheckDigit(Code3Of9.CheckDigit.MOD43); - } - code39.setContent(s.toString()); - - readable = content; - pattern = new String[] { code39.pattern[0] }; - row_count = 1; - row_height = new int[] { -1 }; - } -} +/* + * Copyright 2014 Robin Stuart + * + * 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. + */ + +package uk.org.okapibarcode.backend; + +/** + *

Implements Code 3 of 9 Extended, also known as Code 39e and Code39+. + * + *

Supports encoding of all characters in the 7-bit ASCII table. A modulo-43 + * check digit can be added if required. + * + * @author Robin Stuart + */ +public class Code3Of9Extended extends Symbol { + + public enum CheckDigit { + NONE, MOD43 + } + + private static final String[] E_CODE_39 = { + "%U", "$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", "%A", "%B", "%C", "%D", "%E", " ", "/A", "/B", "/C", + "/D", "/E", "/F", "/G", "/H", "/I", "/J", "/K", "/L", "-", ".", "/O", + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "/Z", "%F", "%G", + "%H", "%I", "%J", "%V", "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", "%K", "%L", "%M", "%N", "%O", "%W", "+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", "%P", + "%Q", "%R", "%S", "%T" + }; + + private CheckDigit checkDigit = CheckDigit.NONE; + + /** + * Sets the check digit scheme (no check digit, or a modulo-43 check digit). By default, no check digit is added. + * + * @param checkDigit the check digit scheme + */ + public void setCheckDigit(CheckDigit checkDigit) { + this.checkDigit = checkDigit; + } + + /** + * Returns the check digit scheme (no check digit, or a modulo-43 check digit). By default, no check digit is added. + * + * @return the check digit scheme + */ + public CheckDigit getCheckDigit() { + return checkDigit; + } + + @Override + protected void encode() { + + if (!content.matches("[\u0000-\u007F]+")) { + throw OkapiInputException.invalidCharactersInInput(); + } + + StringBuilder s = new StringBuilder(content.length() * 2); + for (int i = 0; i < content.length(); i++) { + char c = content.charAt(i); + s.append(E_CODE_39[c]); + } + + Code3Of9 code39 = new Code3Of9(); + if (checkDigit == CheckDigit.MOD43) { + code39.setCheckDigit(Code3Of9.CheckDigit.MOD43); + } + code39.setContent(s.toString()); + + readable = content; + pattern = new String[] { code39.pattern[0] }; + row_count = 1; + row_height = new int[] { -1 }; + } +} diff --git a/src/main/java/uk/org/okapibarcode/backend/Code49.java b/src/main/java/uk/org/okapibarcode/backend/Code49.java index d49a4cf2..7f578968 100644 --- a/src/main/java/uk/org/okapibarcode/backend/Code49.java +++ b/src/main/java/uk/org/okapibarcode/backend/Code49.java @@ -1,1372 +1,1372 @@ -/* - * Copyright 2014 Robin Stuart - * - * 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. - */ - -package uk.org.okapibarcode.backend; - -import static uk.org.okapibarcode.util.Arrays.positionOf; - -import java.nio.charset.StandardCharsets; - -import uk.org.okapibarcode.graphics.Rectangle; - -/** - *

Implements Code 49 according to ANSI/AIM-BC6-2000. - * - *

Supports full 7-bit ASCII input up to a maximum of 49 characters - * or 81 numeric digits. GS1 data encoding is also supported. - * - * @author Robin Stuart - */ -public class Code49 extends Symbol { - - private static final String[] C49_TABLE7 = { - /* Table 7: Code 49 ASCII Chart */ - "! ", "!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", "!1", "!2", "!3", "!4", "!5", " ", "!6", "!7", "!8", - "$", "%", "!9", "!0", "!-", "!.", "!$", "+", "!/", "-", ".", "/", "0", - "1", "2", "3", "4", "5", "6", "7", "8", "9", "!+", "&1", "&2", "&3", - "&4", "&5", "&6", "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", "&7", "&8", "&9", "&0", "&-", "&.", "&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", "&$", "&/", "&+", - "&%", "& " - }; - - /* Table 5: Check Character Weighting Values */ - private static final int[] C49_X_WEIGHT = { - 1, 9, 31, 26, 2, 12, 17, 23, 37, 18, 22, 6, 27, 44, 15, 43, 39, 11, 13, - 5, 41, 33, 36, 8, 4, 32, 3, 19, 40, 25, 29, 10 - }; - - private static final int[] C49_Y_WEIGHT = { - 9, 31, 26, 2, 12, 17, 23, 37, 18, 22, 6, 27, 44, 15, 43, 39, 11, 13, 5, - 41, 33, 36, 8, 4, 32, 3, 19, 40, 25, 29, 10, 24 - }; - - private static final int[] C49_Z_WEIGHT = { - 31, 26, 2, 12, 17, 23, 37, 18, 22, 6, 27, 44, 15, 43, 39, 11, 13, 5, 41, - 33, 36, 8, 4, 32, 3, 19, 40, 25, 29, 10, 24, 30 - }; - - private static final String[] C49_TABLE4 = { - /* Table 4: Row Parity Pattern for Code 49 Symbols */ - "OEEO", "EOEO", "OOEE", "EEOO", "OEOE", "EOOE", "OOOO", "EEEE" - }; - - private static final String[] C49_APPXE_EVEN = { - /* Appendix E - Code 49 Encodation Patterns (Even Symbol Character Parity) */ - /* Column 1 */ - "11521132", "25112131", "14212132", "25121221", "14221222", "12412132", - "23321221", "12421222", "21521221", "15112222", "15121312", "13312222", - "24221311", "13321312", "11512222", "22421311", "11521312", "25112311", - "14212312", "23312311", "12412312", "21512311", "16121131", "14321131", - "12521131", "15212131", "15221221", "13412131", "13421221", "11612131", - "16112221", "16121311", "14312221", "14321311", "12512221", "12521311", - "15212311", "13412311", "11612311", "11131135", "31131133", "51131131", - "21122134", "41122132", "21131224", "41131222", "11113135", "31113133", - "51113131", "11122225", "31122223", "51122221", "11131315", "31131313", - "51131311", "21113224", "41113222", "21122314", - /* Column 2 */ - "41122312", "11113315", "31113313", "51113311", "12131134", "32131132", - "21231133", "41231131", "22122133", "42122131", "11222134", "22131223", - "42131221", "11231224", "31231222", "12113134", "32113132", "12122224", - "32122222", "12131314", "32131312", "21231313", "41231311", "22113223", - "42113221", "11213224", "22122313", "42122311", "11222314", "31222312", - "12113314", "32113312", "21213313", "41213311", "13131133", "33131131", - "22231132", "11331133", "31331131", "23122132", "12222133", "23131222", - "12231223", "32231221", "21331222", "13113133", "33113131", "13122223", - "33122221", "11313133", "13131313", "33131311", "11322223", "22231312", - "11331313", "31331311", "23113222", "12213223", - /* Column 3 */ - "23122312", "12222313", "32222311", "21322312", "13113313", "33113311", - "22213312", "11313313", "31313311", "14131132", "23231131", "12331132", - "21431131", "24122131", "13222132", "24131221", "13231222", "11422132", - "22331221", "11431222", "14113132", "14122222", "12313132", "14131312", - "12322222", "23231311", "12331312", "21431311", "24113221", "13213222", - "24122311", "13222312", "11413222", "22322311", "11422312", "14113312", - "23213311", "12313312", "21413311", "15131131", "13331131", "14222131", - "14231221", "12422131", "12431221", "15113131", "15122221", "13313131", - "15131311", "13322221", "11513131", "13331311", "11522221", "14213221", - "14222311", "12413221", "12422311", "15113311", - /* Column 4 */ - "13313311", "11513311", "11141134", "31141132", "21132133", "41132131", - "21141223", "41141221", "11123134", "31123132", "11132224", "31132222", - "11141314", "31141312", "21114133", "41114131", "21123223", "41123221", - "21132313", "41132311", "11114224", "31114222", "11123314", "31123312", - "21114313", "41114311", "12141133", "32141131", "21241132", "22132132", - "11232133", "22141222", "11241223", "31241221", "12123133", "32123131", - "12132223", "32132221", "12141313", "32141311", "21241312", "22114132", - "11214133", "22123222", "11223223", "22132312", "11232313", "31232311", - "12114223", "32114221", "12123313", "32123311", "21223312", "22114312", - "11214313", "31214311", "13141132", "22241131", - /* Column 5 */ - "11341132", "23132131", "12232132", "23141221", "12241222", "21341221", - "13123132", "13132222", "11323132", "13141312", "11332222", "22241311", - "11341312", "23114131", "12214132", "23123221", "12223222", "23132311", - "12232312", "21332311", "13114222", "13123312", "11314222", "22223311", - "11323312", "23114311", "12214312", "21314311", "14141131", "12341131", - "13232131", "13241221", "11432131", "14123131", "14132221", "12323131", - "14141311", "12332221", "12341311", "13214131", "13223221", "11414131", - "13232311", "11423221", "11432311", "14114221", "14123311", "12314221", - "12323311", "13214311", "11414311", "11151133", "31151131", "21142132", - "21151222", "11133133", "31133131", "11142223", - /* Column 6 */ - "31142221", "11151313", "31151311", "21124132", "21133222", "21142312", - "11115133", "31115131", "11124223", "31124221", "11133313", "31133311", - "21115222", "21124312", "12151132", "21251131", "22142131", "11242132", - "22151221", "11251222", "12133132", "12142222", "12151312", "21251311", - "22124131", "11224132", "22133221", "11233222", "22142311", "11242312", - "12115132", "12124222", "12133312", "21233311", "22115221", "11215222", - "22124311", "11224312", "13151131", "12242131", "12251221", "13133131", - "13142221", "11333131", "13151311", "11342221", "12224131", "12233221", - "12242311", "13115131", "13124221", "11315131", "13133311", "11324221", - "11333311", "12215221", "12224311", "11161132", - /* Column 7 */ - "21152131", "21161221", "11143132", "11152222", "11161312", "21134131", - "21143221", "21152311", "11125132", "11134222", "11143312", "21116131", - "21125221", "21134311", "12161131", "11252131", "12143131", "12152221", - "12161311", "11234131", "11243221", "11252311", "12125131", "12134221", - "12143311", "11216131", "11225221", "11234311", "11111236", "31111234", - "51111232", "21111325", "41111323", "61111321", "11111416", "31111414", - "51111412", "31211143", "51211141", "12111235", "32111233", "52111231", - "21211234", "41211232", "22111324", "42111322", "11211325", "31211323", - "51211321", "12111415", "32111413", "52111411", "21211414", "41211412", - "12211144", "32211142", "21311143", "41311141", - /* Column 8 */ - "13111234", "33111232", "22211233", "42211231", "11311234", "31311232", - "23111323", "43111321", "12211324", "32211322", "21311323", "41311321", - "13111414", "33111412", "22211413", "42211411", "11311414", "31311412", - "13211143", "33211141", "22311142", "11411143", "31411141", "14111233", - "34111231", "23211232", "12311233", "32311231", "21411232", "24111322", - "13211323", "33211321", "22311322", "11411323", "31411321", "14111413", - "34111411", "23211412", "12311413", "32311411", "21411412", "14211142", - "23311141", "12411142", "21511141", "15111232", "24211231", "13311232", - "22411231", "11511232", "25111321", "14211322", "23311321", "12411322", - "21511321", "15111412", "24211411", "13311412", - /* Column 9 */ - "22411411", "11511412", "15211141", "13411141", "11611141", "16111231", - "14311231", "12511231", "15211321", "13411321", "11611321", "16111411", - "14311411", "12511411", "21121144", "41121142", "11112145", "31112143", - "51112141", "11121235", "31121233", "51121231", "21112234", "41112232", - "21121324", "41121322", "11112325", "31112323", "51112321", "11121415", - "31121413", "51121411", "21112414", "41112412", "22121143", "42121141", - "11221144", "31221142", "12112144", "32112142", "12121234", "32121232", - "21221233", "41221231", "22112233", "42112231", "11212234", "22121323", - "42121321", "11221324", "31221322", "12112324", "32112322", "12121414", - "32121412", "21221413", "41221411", "22112413", - /* Column 10 */ - "42112411", "11212414", "31212412", "23121142", "12221143", "32221141", - "21321142", "13112143", "33112141", "13121233", "33121231", "11312143", - "22221232", "11321233", "31321231", "23112232", "12212233", "23121322", - "12221323", "32221321", "21321322", "13112323", "33112321", "13121413", - "33121411", "11312323", "22221412", "11321413", "31321411", "23112412", - "12212413", "32212411", "21312412", "24121141", "13221142", "22321141", - "11421142", "14112142", "14121232", "12312142", "23221231", "12321232", - "21421231", "24112231", "13212232", "24121321", "13221322", "11412232", - "22321321", "11421322", "14112322", "14121412", "12312322", "23221411", - "12321412", "21421411", "24112411", "13212412", - /* Column 11 */ - "22312411", "11412412", "14221141", "12421141", "15112141", "15121231", - "13312141", "13321231", "11512141", "11521231", "14212231", "14221321", - "12412231", "12421321", "15112321", "15121411", "13312321", "13321411", - "11512321", "11521411", "14212411", "12412411", "21131143", "41131141", - "11122144", "31122142", "11131234", "31131232", "21113143", "41113141", - "21122233", "41122231", "21131323", "41131321", "11113234", "31113232", - "11122324", "31122322", "11131414", "31131412", "21113323", "41113321", - "21122413", "41122411", "11113414", "31113412", "22131142", "11231143", - "31231141", "12122143", "32122141", "12131233", "32131231", "21231232", - "22113142", "11213143", "22122232", "11222233", - /* Column 12 */ - "22131322", "11231323", "31231321", "12113233", "32113231", "12122323", - "32122321", "12131413", "32131411", "21231412", "22113322", "11213323", - "22122412", "11222413", "31222411", "12113413", "32113411", "21213412", - "23131141", "12231142", "21331141", "13122142", "13131232", "11322142", - "22231231", "11331232", "23113141", "12213142", "23122231", "12222232", - "23131321", "12231322", "21331321", "13113232", "13122322", "11313232", - "13131412", "11322322", "22231411", "11331412", "23113321", "12213322", - "23122411", "12222412", "21322411", "13113412", "22213411", "11313412", - "13231141", "11431141", "14122141", "14131231", "12322141", "12331231", - "13213141", "13222231", "11413141", "13231321", - /* Column 13 */ - "11422231", "11431321", "14113231", "14122321", "12313231", "14131411", - "12322321", "12331411", "13213321", "13222411", "11413321", "11422411", - "14113411", "12313411", "21141142", "11132143", "31132141", "11141233", - "31141231", "21123142", "21132232", "21141322", "11114143", "31114141", - "11123233", "31123231", "11132323", "31132321", "11141413", "31141411", - "21114232", "21123322", "21132412", "11114323", "31114321", "11123413", - "31123411", "22141141", "11241142", "12132142", "12141232", "21241231", - "22123141", "11223142", "22132231", "11232232", "22141321", "11241322", - "12114142", "12123232", "12132322", "12141412", "21241411", "22114231", - "11214232", "22123321", "11223322", "22132411", - /* Column 14 */ - "11232412", "12114322", "12123412", "21223411", "12241141", "13132141", - "13141231", "11332141", "11341231", "12223141", "12232231", "12241321", - "13114141", "13123231", "11314141", "13132321", "11323231", "13141411", - "11332321", "11341411", "12214231", "12223321", "12232411", "13114321", - "13123411", "11314321", "11323411", "21151141", "11142142", "11151232", - "21133141", "21142231", "21151321", "11124142", "11133232", "11142322", - "11151412", "21115141", "21124231", "21133321", "21142411", "11115232", - "11124322", "11133412", "11251141", "12142141", "12151231", "11233141", - "11242231", "11251321", "12124141", "12133231", "12142321", "12151411", - "11215141", "11224231", "11233321", "11242411", - /* Column 15 */ - "12115231", "12124321", "12133411", "11152141", "11161231", "11134141", - "11143231", "11152321", "11161411", "11116141", "11125231", "11134321", - "11143411", "21111244", "41111242", "11111335", "31111333", "51111331", - "21111424", "41111422", "11111515", "31111513", "51111511", "21211153", - "41211151", "22111243", "42111241", "11211244", "31211242", "12111334", - "32111332", "21211333", "41211331", "22111423", "42111421", "11211424", - "31211422", "12111514", "32111512", "21211513", "41211511", "22211152", - "11311153", "31311151", "23111242", "12211243", "32211241", "21311242", - "13111333", "33111331", "22211332", "11311333", "31311331", "23111422", - "12211423", "32211421", "21311422", "13111513", - /* Column 16 */ - "33111511", "22211512", "11311513", "31311511", "23211151", "12311152", - "21411151", "24111241", "13211242", "22311241", "11411242", "14111332", - "23211331", "12311332", "21411331", "24111421", "13211422", "22311421", - "11411422", "14111512", "23211511", "12311512", "21411511", "13311151", - "11511151", "14211241", "12411241", "15111331", "13311331", "11511331", - "14211421", "12411421", "15111511", "13311511", "11511511", "31121152", - "21112153", "41112151", "21121243", "41121241", "11112244", "31112242", - "11121334", "31121332", "21112333", "41112331", "21121423", "41121421", - "11112424", "31112422", "11121514", "31121512", "21112513", "41112511", - "12121153", "32121151", "21221152", "22112152", - /* Column 17 */ - "11212153", "22121242", "11221243", "31221241", "12112243", "32112241", - "12121333", "32121331", "21221332", "22112332", "11212333", "22121422", - "11221423", "31221421", "12112423", "32112421", "12121513", "32121511", - "21221512", "22112512", "11212513", "31212511", "13121152", "22221151", - "11321152", "23112151", "12212152", "23121241", "12221242", "21321241", - "13112242", "13121332", "11312242", "22221331", "11321332", "23112331", - "12212332", "23121421", "12221422", "21321421", "13112422", "13121512", - "11312422", "22221511", "11321512", "23112511", "12212512", "21312511", - "14121151", "12321151", "13212151", "13221241", "11412151", "11421241", - "14112241", "14121331", "12312241", "12321331", - /* Column 18 */ - "13212331", "13221421", "11412331", "11421421", "14112421", "14121511", - "12312421", "12321511", "13212511", "11412511", "11131153", "31131151", - "21122152", "21131242", "11113153", "31113151", "11122243", "31122241", - "11131333", "31131331", "21113242", "21122332", "21131422", "11113333", - "31113331", "11122423", "31122421", "11131513", "31131511", "21113422", - "21122512", "12131152", "21231151", "22122151", "11222152", "22131241", - "11231242", "12113152", "12122242", "12131332", "21231331", "22113241", - "11213242", "22122331", "11222332", "22131421", "11231422", "12113332", - "12122422", "12131512", "21231511", "22113421", "11213422", "22122511", - "11222512", "13131151", "11331151", "12222151", - /* Column 19 */ - "12231241", "13113151", "13122241", "11313151", "13131331", "11322241", - "11331331", "12213241", "12222331", "12231421", "13113331", "13122421", - "11313331", "13131511", "11322421", "11331511", "12213421", "12222511", - "11141152", "21132151", "21141241", "11123152", "11132242", "11141332", - "21114151", "21123241", "21132331", "21141421", "11114242", "11123332", - "11132422", "11141512", "21114331", "21123421", "21132511", "12141151", - "11232151", "11241241", "12123151", "12132241", "12141331", "11214151", - "11223241", "11232331", "11241421", "12114241", "12123331", "12132421", - "12141511", "11214331", "11223421", "11232511", "11151151", "11133151", - "11142241", "11151331", "11115151", "11124241", - /* Column 20 */ - "11133331", "11142421", "11151511", "11111254", "31111252", "21111343", - "41111341", "11111434", "31111432", "21111523", "41111521", "11111614", - "31111612", "31211161", "12111253", "32111251", "21211252", "22111342", - "11211343", "31211341", "12111433", "32111431", "21211432", "22111522", - "11211523", "31211521", "12111613", "32111611", "21211612", "12211162", - "21311161", "13111252", "22211251", "11311252", "23111341", "12211342", - "21311341", "13111432", "22211431", "11311432", "23111521", "12211522", - "21311521", "13111612", "22211611", "11311612", "13211161", "11411161", - "14111251", "12311251", "13211341", "11411341", "14111431", "12311431", - "13211521", "11411521", "14111611", "12311611", - /* Column 21 */ - "21121162", "11112163", "31112161", "11121253", "31121251", "21112252", - "21121342", "11112343", "31112341", "11121433", "31121431", "21112432", - "21121522", "11112523", "31112521", "11121613", "31121611", "22121161", - "11221162", "12112162", "12121252", "21221251", "22112251", "11212252", - "22121341", "11221342", "12112342", "12121432", "21221431", "22112431", - "11212432", "22121521", "11221522", "12112522", "12121612", "21221611", - "12221161", "13112161", "13121251", "11312161", "11321251", "32121115", - "52121113", "21221116", "41221114", "61221112", "22112116", "42112114", - "31212115", "51212113", "13121116", "33121114", "22221115", "42221113", - "11321116", "31321114", "51321112", "23112115", - /* Column 22 */ - "43112113", "12212116", "32212114", "52212112", "21312115", "41312113", - "61312111", "14121115", "34121113", "23221114", "43221112", "12321115", - "32321113", "52321111", "21421114", "41421112", "24112114", "13212115", - "33212113", "22312114", "42312112", "11412115", "31412113", "51412111", - "15121114", "24221113", "13321114", "33321112", "22421113", "42421111", - "11521114", "31521112", "25112113", "14212114", "34212112", "23312113", - "43312111", "12412114", "32412112", "21512113", "41512111", "16121113", - "25221112", "14321113", "34321111", "23421112", "12521113", "32521111", - "15212113", "24312112", "13412113", "33412111", "22512112", "11612113", - "31612111", "31131115", "51131113", "21122116", - /* Column 23 */ - "41122114", "61122112", "31113115", "51113113", "12131116", "32131114", - "52131112", "21231115", "41231113", "61231111", "22122115", "42122113", - "11222116", "31222114", "51222112", "12113116", "32113114", "52113112", - "21213115", "41213113", "61213111", "13131115", "33131113", "22231114", - "42231112", "11331115", "31331113", "51331111", "23122114", "43122112", - "12222115", "32222113", "52222111", "21322114", "41322112", "13113115", - "33113113", "22213114", "42213112", "11313115", "31313113", "51313111", - "14131114", "34131112", "23231113", "43231111", "12331114", "32331112", - "21431113", "41431111", "24122113", "13222114", "33222112", "22322113", - "42322111", "11422114", "31422112", "14113114", - /* Column 24 */ - "34113112", "23213113", "43213111", "12313114", "32313112", "21413113", - "41413111", "15131113", "24231112", "13331113", "33331111", "22431112", - "25122112", "14222113", "34222111", "23322112", "12422113", "32422111", - "21522112", "15113113", "24213112", "13313113", "33313111", "22413112", - "11513113", "31513111", "16131112", "25231111", "14331112", "23431111", - "15222112", "24322111", "13422112", "22522111", "16113112", "25213111", - "14313112", "23413111", "12513112", "21613111", "11141116", "31141114", - "51141112", "21132115", "41132113", "61132111", "11123116", "31123114", - "51123112", "21114115", "41114113", "61114111", "12141115", "32141113", - "52141111", "21241114", "41241112", "22132114", - /* Column 25 */ - "42132112", "11232115", "31232113", "51232111", "12123115", "32123113", - "52123111", "21223114", "41223112", "22114114", "42114112", "11214115", - "31214113", "51214111", "13141114", "33141112", "22241113", "42241111", - "11341114", "31341112", "23132113", "43132111", "12232114", "32232112", - "21332113", "41332111", "13123114", "33123112", "22223113", "42223111", - "11323114", "31323112", "23114113", "43114111", "12214114", "32214112", - "21314113", "41314111", "14141113", "34141111", "23241112", "12341113", - "32341111", "24132112", "13232113", "33232111", "22332112", "11432113", - "31432111", "14123113", "34123111", "23223112", "12323113", "32323111", - "21423112", "24114112", "13214113", "33214111", - /* Column 26 */ - "22314112", "11414113", "31414111", "15141112", "24241111", "13341112", - "25132111", "14232112", "23332111", "12432112", "15123112", "24223111", - "13323112", "22423111", "11523112", "25114111", "14214112", "23314111", - "12414112", "21514111", "16141111", "14341111", "15232111", "13432111", - "16123111", "14323111", "12523111", "15214111", "13414111", "11614111", - "11151115", "31151113", "51151111", "21142114", "41142112", "11133115", - "31133113", "51133111", "21124114", "41124112", "11115115", "31115113", - "51115111", "12151114", "32151112", "21251113", "41251111", "22142113", - "42142111", "11242114", "31242112", "12133114", "32133112", "21233113", - "41233111", "22124113", "42124111", "11224114", - /* Column 27 */ - "31224112", "12115114", "32115112", "21215113", "41215111", "13151113", - "33151111", "22251112", "23142112", "12242113", "32242111", "21342112", - "13133113", "33133111", "22233112", "11333113", "31333111", "23124112", - "12224113", "32224111", "21324112", "13115113", "33115111", "22215112", - "11315113", "31315111", "14151112", "23251111", "24142111", "13242112", - "22342111", "14133112", "23233111", "12333112", "21433111", "24124111", - "13224112", "22324111", "11424112", "14115112", "23215111", "12315112", - "21415111", "15151111", "14242111", "15133111", "13333111", "14224111", - "12424111", "15115111", "13315111", "11515111", "11161114", "31161112", - "21152113", "41152111", "11143114", "31143112", - /* Column 28 */ - "21134113", "41134111", "11125114", "31125112", "21116113", "41116111", - "12161113", "32161111", "22152112", "11252113", "31252111", "12143113", - "32143111", "21243112", "22134112", "11234113", "31234111", "12125113", - "32125111", "21225112", "22116112", "11216113", "31216111", "13161112", - "23152111", "12252112", "13143112", "22243111", "11343112", "23134111", - "12234112", "21334111", "13125112", "22225111", "11325112", "23116111", - "12216112", "21316111", "14161111", "13252111", "14143111", "12343111", - "13234111", "11434111", "14125111", "12325111", "13216111", "11416111", - "31111216", "51111214", "31211125", "51211123", "32111215", "52111213", - "21211216", "41211214", "61211212", "12211126", - /* Column 29 */ - "32211124", "52211122", "21311125", "41311123", "61311121", "13111216", - "33111214", "22211215", "42211213", "11311216", "31311214", "51311212", - "13211125", "33211123", "22311124", "42311122", "11411125", "31411123", - "51411121", "14111215", "34111213", "23211214", "43211212", "12311215", - "32311213", "52311211", "21411214", "41411212", "14211124", "34211122", - "23311123", "43311121", "12411124", "32411122", "21511123", "41511121", - "15111214", "24211213", "13311214", "33311212", "22411213", "42411211", - "11511214", "31511212", "15211123", "24311122", "13411123", "33411121", - "22511122", "11611123", "31611121", "16111213", "25211212", "14311213", - "34311211", "23411212", "12511213", "32511211", - /* Column 30 */ - "21611212", "21121126", "41121124", "61121122", "31112125", "51112123", - "31121215", "51121213", "21112216", "41112214", "61112212", "22121125", - "42121123", "11221126", "31221124", "51221122", "12112126", "32112124", - "52112122", "12121216", "32121214", "52121212", "21221215", "41221213", - "61221211", "22112215", "42112213", "11212216", "31212214", "51212212", - "23121124", "43121122", "12221125", "32221123", "52221121", "21321124", - "41321122", "13112125", "33112123", "13121215", "33121213", "11312125", - "22221214", "42221212", "11321215", "31321213", "51321211", "23112214", - "43112212", "12212215", "32212213", "52212211", "21312214", "41312212", - "24121123", "13221124", "33221122", "22321123", - /* Column 31 */ - "42321121", "11421124", "31421122", "14112124", "34112122", "14121214", - "34121212", "12312124", "23221213", "43221211", "12321214", "32321212", - "21421213", "41421211", "24112213", "13212214", "33212212", "22312213", - "42312211", "11412214", "31412212", "25121122", "14221123", "34221121", - "23321122", "12421123", "32421121", "21521122", "15112123", "15121213", - "13312123", "24221212", "13321213", "33321211", "11512123", "22421212", - "11521213", "31521211", "25112212", "14212213", "34212211", "23312212", - "12412213", "32412211", "21512212", "15221122", "24321121", "13421122", - "22521121", "16112122", "16121212", "14312122", "25221211", "14321212", - "12512122", "23421211", "12521212", "15212212", - /* Column 32 */ - "24312211", "13412212", "22512211", "11612212", "21131125", "41131123", - "61131121", "11122126", "31122124", "51122122", "11131216", "31131214", - "51131212", "21113125", "41113123", "61113121", "21122215", "41122213", - "61122211", "11113216", "31113214", "51113212", "22131124", "42131122", - "11231125", "31231123", "51231121", "12122125", "32122123", "52122121", - "12131215", "32131213", "52131211", "21231214", "41231212", "22113124", - "42113122", "11213125", "22122214", "42122212", "11222215", "31222213", - "51222211", "12113215", "32113213", "52113211", "21213214", "41213212", - "23131123", "43131121", "12231124", "32231122", "21331123", "41331121", - "13122124", "33122122", "13131214", "33131212", - /* Column 33 */ - "11322124", "22231213", "42231211", "11331214", "31331212", "23113123", - "43113121", "12213124", "23122213", "43122211", "12222214", "32222212", - "21322213", "41322211", "13113214", "33113212", "22213213", "42213211", - "11313214", "31313212", "24131122", "13231123", "33231121", "22331122", - "11431123", "31431121", "14122123", "34122121", "14131213", "34131211", - "12322123", "23231212", "12331213", "32331211", "21431212", "24113122", - "13213123", "24122212", "13222213", "33222211", "11413123", "22322212", - "11422213", "31422211", "14113213", "34113211", "23213212", "12313213", - "32313211", "21413212", "25131121", "14231122", "23331121", "12431122", - "15122122", "15131212", "13322122", "24231211", - /* Column 34 */ - "13331212", "11522122", "22431211", "25113121", "14213122", "25122211", - "14222212", "12413122", "23322211", "12422212", "21522211", "15113212", - "24213211", "13313212", "22413211", "11513212", "15231121", "13431121", - "16122121", "16131211", "14322121", "14331211", "12522121", "15213121", - "15222211", "13413121", "13422211", "11613121", "16113211", "14313211", - "12513211", "21141124", "41141122", "11132125", "31132123", "51132121", - "11141215", "31141213", "51141211", "21123124", "41123122", "21132214", - "41132212", "11114125", "31114123", "51114121", "11123215", "31123213", - "51123211", "21114214", "41114212", "22141123", "42141121", "11241124", - "31241122", "12132124", "32132122", "12141214", - /* Column 35 */ - "32141212", "21241213", "41241211", "22123123", "42123121", "11223124", - "22132213", "42132211", "11232214", "31232212", "12114124", "32114122", - "12123214", "32123212", "21223213", "41223211", "22114213", "42114211", - "11214214", "31214212", "23141122", "12241123", "32241121", "21341122", - "13132123", "33132121", "13141213", "33141211", "11332123", "22241212", - "11341213", "31341211", "23123122", "12223123", "23132212", "12232213", - "32232211", "21332212", "13114123", "33114121", "13123213", "33123211", - "11314123", "22223212", "11323213", "31323211", "23114212", "12214213", - "32214211", "21314212", "24141121", "13241122", "22341121", "14132122", - "14141212", "12332122", "23241211", "12341212", - /* Column 36 */ - "24123121", "13223122", "24132211", "13232212", "11423122", "22332211", - "11432212", "14114122", "14123212", "12314122", "23223211", "12323212", - "21423211", "24114211", "13214212", "22314211", "11414212", "14241121", - "15132121", "15141211", "13332121", "13341211", "14223121", "14232211", - "12423121", "12432211", "15114121", "15123211", "13314121", "13323211", - "11514121", "11523211", "14214211", "12414211", "21151123", "41151121", - "11142124", "31142122", "11151214", "31151212", "21133123", "41133121", - "21142213", "41142211", "11124124", "31124122", "11133214", "31133212", - "21115123", "41115121", "21124213", "41124211", "11115214", "31115212", - "22151122", "11251123", "31251121", "12142123", - /* Column 37 */ - "32142121", "12151213", "32151211", "21251212", "22133122", "11233123", - "22142212", "11242213", "31242211", "12124123", "32124121", "12133213", - "32133211", "21233212", "22115122", "11215123", "22124212", "11224213", - "31224211", "12115213", "32115211", "21215212", "23151121", "12251122", - "13142122", "13151212", "11342122", "22251211", "23133121", "12233122", - "23142211", "12242212", "21342211", "13124122", "13133212", "11324122", - "22233211", "11333212", "23115121", "12215122", "23124211", "12224212", - "21324211", "13115212", "22215211", "11315212", "13251121", "14142121", - "14151211", "12342121", "13233121", "13242211", "11433121", "14124121", - "14133211", "12324121", "12333211", "13215121", - /* Column 38 */ - "13224211", "11415121", "11424211", "14115211", "12315211", "21161122", - "11152123", "31152121", "11161213", "31161211", "21143122", "21152212", - "11134123", "31134121", "11143213", "31143211", "21125122", "21134212", - "11116123", "31116121", "11125213", "31125211", "22161121", "12152122", - "12161212", "22143121", "11243122", "22152211", "11252212", "12134122", - "12143212", "21243211", "22125121", "11225122", "22134211", "11234212", - "12116122", "12125212", "21225211", "13152121", "13161211", "12243121", - "12252211", "13134121", "13143211", "11334121", "11343211", "12225121", - "12234211", "13116121", "13125211", "11316121", "11325211", "21111226", - "41111224", "61111222", "31111315", "51111313", - /* Column 39 */ - "21211135", "41211133", "61211131", "22111225", "42111223", "11211226", - "31211224", "51211222", "12111316", "32111314", "52111312", "21211315", - "41211313", "61211311", "22211134", "42211132", "11311135", "31311133", - "51311131", "23111224", "43111222", "12211225", "32211223", "52211221", - "21311224", "41311222", "13111315", "33111313", "22211314", "42211312", - "11311315", "31311313", "51311311", "23211133", "43211131", "12311134", - "32311132", "21411133", "41411131", "24111223", "13211224", "33211222", - "22311223", "42311221", "11411224", "31411222", "14111314", "34111312", - "23211313", "43211311", "12311314", "32311312", "21411313", "41411311", - "24211132", "13311133", "33311131", "22411132", - /* Column 40 */ - "11511133", "31511131", "25111222", "14211223", "34211221", "23311222", - "12411223", "32411221", "21511222", "15111313", "24211312", "13311313", - "33311311", "22411312", "11511313", "31511311", "25211131", "14311132", - "23411131", "12511132", "21611131", "15211222", "24311221", "13411222", - "22511221", "11611222", "16111312", "25211311", "14311312", "23411311", - "12511312", "21611311", "31121134", "51121132", "21112135", "41112133", - "61112131", "21121225", "41121223", "61121221", "11112226", "31112224", - "51112222", "11121316", "31121314", "51121312", "21112315", "41112313", - "61112311", "12121135", "32121133", "52121131", "21221134", "41221132", - "22112134", "42112132", "11212135", "22121224", - /* Column 41 */ - "42121222", "11221225", "31221223", "51221221", "12112225", "32112223", - "52112221", "12121315", "32121313", "52121311", "21221314", "41221312", - "22112314", "42112312", "11212315", "31212313", "51212311", "13121134", - "33121132", "22221133", "42221131", "11321134", "31321132", "23112133", - "43112131", "12212134", "23121223", "43121221", "12221224", "32221222", - "21321223", "41321221", "13112224", "33112222", "13121314", "33121312", - "11312224", "22221313", "42221311", "11321314", "31321312", - /* Column 42 */ - "23112313", "43112311", "12212314", "32212312", "21312313", "41312311", - "14121133", "34121131", "23221132", "12321133", "32321131", "21421132", - "24112132", "13212133", "24121222", "13221223", "33221221", "11412133", - "22321222", "11421223", "31421221", "14112223", "34112221", "14121313", - "34121311", "12312223", "23221312", "12321313", "32321311", "21421312", - "24112312", "13212313", "33212311", "22312312", "11412313", "31412311", - "15121132", "24221131", "13321132", "22421131" - }; - - private static final String[] C49_APPXE_ODD = { - /* Appendix E - Code 49 Encodation Patterns (Odd Symbol Character Parity) */ - /* Column 1 */ - "22121116", "42121114", "31221115", "51221113", "32112115", "52112113", - "21212116", "41212114", "61212112", "23121115", "43121113", "12221116", - "32221114", "52221112", "21321115", "41321113", "61321111", "13112116", - "33112114", "22212115", "42212113", "11312116", "31312114", "51312112", - "24121114", "13221115", "33221113", "22321114", "42321112", "11421115", - "31421113", "51421111", "14112115", "34112113", "23212114", "43212112", - "12312115", "32312113", "52312111", "21412114", "41412112", "25121113", - "14221114", "34221112", "23321113", "43321111", "12421114", "32421112", - "21521113", "41521111", "15112114", "24212113", "13312114", "33312112", - "22412113", "42412111", "11512114", "31512112", - /* Column 2 */ - "15221113", "24321112", "13421113", "33421111", "22521112", "16112113", - "25212112", "14312113", "34312111", "23412112", "12512113", "32512111", - "21612112", "21131116", "41131114", "61131112", "31122115", "51122113", - "21113116", "41113114", "61113112", "22131115", "42131113", "11231116", - "31231114", "51231112", "12122116", "32122114", "52122112", "21222115", - "41222113", "61222111", "22113115", "42113113", "11213116", "31213114", - "51213112", "23131114", "43131112", "12231115", "32231113", "52231111", - "21331114", "41331112", "13122115", "33122113", "22222114", "42222112", - "11322115", "31322113", "51322111", "23113114", "43113112", "12213115", - "32213113", "52213111", "21313114", "41313112", - /* Column 3 */ - "24131113", "13231114", "33231112", "22331113", "42331111", "11431114", - "31431112", "14122114", "34122112", "23222113", "43222111", "12322114", - "32322112", "21422113", "41422111", "24113113", "13213114", "33213112", - "22313113", "42313111", "11413114", "31413112", "25131112", "14231113", - "34231111", "23331112", "12431113", "32431111", "15122113", "24222112", - "13322113", "33322111", "22422112", "11522113", "31522111", "25113112", - "14213113", "34213111", "23313112", "12413113", "32413111", "21513112", - "15231112", "24331111", "13431112", "16122112", "25222111", "14322112", - "23422111", "12522112", "15213112", "24313111", "13413112", "22513111", - "11613112", "21141115", "41141113", "61141111", - /* Column 4 */ - "11132116", "31132114", "51132112", "21123115", "41123113", "61123111", - "11114116", "31114114", "51114112", "22141114", "42141112", "11241115", - "31241113", "51241111", "12132115", "32132113", "52132111", "21232114", - "41232112", "22123114", "42123112", "11223115", "31223113", "51223111", - "12114115", "32114113", "52114111", "21214114", "41214112", "23141113", - "43141111", "12241114", "32241112", "21341113", "41341111", "13132114", - "33132112", "22232113", "42232111", "11332114", "31332112", "23123113", - "43123111", "12223114", "32223112", "21323113", "41323111", "13114114", - "33114112", "22214113", "42214111", "11314114", "31314112", "24141112", - "13241113", "33241111", "22341112", "14132113", - /* Column 5 */ - "34132111", "23232112", "12332113", "32332111", "21432112", "24123112", - "13223113", "33223111", "22323112", "11423113", "31423111", "14114113", - "34114111", "23214112", "12314113", "32314111", "21414112", "25141111", - "14241112", "23341111", "15132112", "24232111", "13332112", "22432111", - "25123111", "14223112", "23323111", "12423112", "21523111", "15114112", - "24214111", "13314112", "22414111", "11514112", "15241111", "16132111", - "14332111", "15223111", "13423111", "16114111", "14314111", "12514111", - "21151114", "41151112", "11142115", "31142113", "51142111", "21133114", - "41133112", "11124115", "31124113", "51124111", "21115114", "41115112", - "22151113", "42151111", "11251114", "31251112", - /* Column 6 */ - "12142114", "32142112", "21242113", "41242111", "22133113", "42133111", - "11233114", "31233112", "12124114", "32124112", "21224113", "41224111", - "22115113", "42115111", "11215114", "31215112", "23151112", "12251113", - "32251111", "13142113", "33142111", "22242112", "11342113", "31342111", - "23133112", "12233113", "32233111", "21333112", "13124113", "33124111", - "22224112", "11324113", "31324111", "23115112", "12215113", "32215111", - "21315112", "24151111", "13251112", "14142112", "23242111", "12342112", - "24133111", "13233112", "22333111", "11433112", "14124112", "23224111", - "12324112", "21424111", "24115111", "13215112", "22315111", "11415112", - "14251111", "15142111", "13342111", "14233111", - /* Column 7 */ - "12433111", "15124111", "13324111", "11524111", "14215111", "12415111", - "21161113", "41161111", "11152114", "31152112", "21143113", "41143111", - "11134114", "31134112", "21125113", "41125111", "11116114", "31116112", - "22161112", "12152113", "32152111", "21252112", "22143112", "11243113", - "31243111", "12134113", "32134111", "21234112", "22125112", "11225113", - "31225111", "12116113", "32116111", "21216112", "23161111", "13152112", - "22252111", "23143111", "12243112", "21343111", "13134112", "22234111", - "11334112", "23125111", "12225112", "21325111", "13116112", "22216111", - "11316112", "14152111", "13243111", "14134111", "12334111", "13225111", - "11425111", "14116111", "12316111", "41111215", - /* Column 8 */ - "61111213", "21211126", "41211124", "61211122", "22111216", "42111214", - "31211215", "51211213", "22211125", "42211123", "11311126", "31311124", - "51311122", "23111215", "43111213", "12211216", "32211214", "52211212", - "21311215", "41311213", "61311211", "23211124", "43211122", "12311125", - "32311123", "52311121", "21411124", "41411122", "24111214", "13211215", - "33211213", "22311214", "42311212", "11411215", "31411213", "51411211", - "24211123", "13311124", "33311122", "22411123", "42411121", "11511124", - "31511122", "25111213", "14211214", "34211212", "23311213", "43311211", - "12411214", "32411212", "21511213", "41511211", "25211122", "14311123", - "34311121", "23411122", "12511123", "32511121", - /* Column 9 */ - "21611122", "15211213", "24311212", "13411213", "33411211", "22511212", - "11611213", "31611211", "31121125", "51121123", "21112126", "41112124", - "61112122", "21121216", "41121214", "61121212", "31112215", "51112213", - "12121126", "32121124", "52121122", "21221125", "41221123", "61221121", - "22112125", "42112123", "11212126", "22121215", "42121213", "11221216", - "31221214", "51221212", "12112216", "32112214", "52112212", "21212215", - "41212213", "61212211", "13121125", "33121123", "22221124", "42221122", - "11321125", "31321123", "51321121", "23112124", "43112122", "12212125", - "23121214", "43121212", "12221215", "32221213", "52221211", "21321214", - "41321212", "13112215", "33112213", "22212214", - /* Column 10 */ - "42212212", "11312215", "31312213", "51312211", "14121124", "34121122", - "23221123", "43221121", "12321124", "32321122", "21421123", "41421121", - "24112123", "13212124", "24121213", "13221214", "33221212", "11412124", - "22321213", "42321211", "11421214", "31421212", "14112214", "34112212", - "23212213", "43212211", "12312214", "32312212", "21412213", "41412211", - "15121123", "24221122", "13321123", "33321121", "22421122", "11521123", - "31521121", "25112122", "14212123", "25121212", "14221213", "34221211", - "12412123", "23321212", "12421213", "32421211", "21521212", "15112213", - "24212212", "13312213", "33312211", "22412212", "11512213", "31512211", - "16121122", "25221121", "14321122", "23421121", - /* Column 11 */ - "12521122", "15212122", "15221212", "13412122", "24321211", "13421212", - "11612122", "22521211", "16112212", "25212211", "14312212", "23412211", - "12512212", "21612211", "11131126", "31131124", "51131122", "21122125", - "41122123", "61122121", "21131215", "41131213", "61131211", "11113126", - "31113124", "51113122", "11122216", "31122214", "51122212", "21113215", - "41113213", "61113211", "12131125", "32131123", "52131121", "21231124", - "41231122", "22122124", "42122122", "11222125", "22131214", "42131212", - "11231215", "31231213", "51231211", "12113125", "32113123", "52113121", - "12122215", "32122213", "52122211", "21222214", "41222212", "22113214", - "42113212", "11213215", "31213213", "51213211", - /* Column 12 */ - "13131124", "33131122", "22231123", "42231121", "11331124", "31331122", - "23122123", "43122121", "12222124", "23131213", "43131211", "12231214", - "32231212", "21331213", "41331211", "13113124", "33113122", "13122214", - "33122212", "11313124", "22222213", "42222211", "11322214", "31322212", - "23113213", "43113211", "12213214", "32213212", "21313213", "41313211", - "14131123", "34131121", "23231122", "12331123", "32331121", "21431122", - "24122122", "13222123", "24131212", "13231213", "33231211", "11422123", - "22331212", "11431213", "31431211", "14113123", "34113121", "14122213", - "34122211", "12313123", "23222212", "12322213", "32322211", "21422212", - "24113212", "13213213", "33213211", "22313212", - /* Column 13 */ - "11413213", "31413211", "15131122", "24231121", "13331122", "22431121", - "25122121", "14222122", "25131211", "14231212", "12422122", "23331211", - "12431212", "15113122", "15122212", "13313122", "24222211", "13322212", - "11513122", "22422211", "11522212", "25113211", "14213212", "23313211", - "12413212", "21513211", "16131121", "14331121", "15222121", "15231211", - "13422121", "13431211", "16113121", "16122211", "14313121", "14322211", - "12513121", "12522211", "15213211", "13413211", "11613211", "11141125", - "31141123", "51141121", "21132124", "41132122", "21141214", "41141212", - "11123125", "31123123", "51123121", "11132215", "31132213", "51132211", - "21114124", "41114122", "21123214", "41123212", - /* Column 14 */ - "11114215", "31114213", "51114211", "12141124", "32141122", "21241123", - "41241121", "22132123", "42132121", "11232124", "22141213", "42141211", - "11241214", "31241212", "12123124", "32123122", "12132214", "32132212", - "21232213", "41232211", "22114123", "42114121", "11214124", "22123213", - "42123211", "11223214", "31223212", "12114214", "32114212", "21214213", - "41214211", "13141123", "33141121", "22241122", "11341123", "31341121", - "23132122", "12232123", "23141212", "12241213", "32241211", "21341212", - "13123123", "33123121", "13132213", "33132211", "11323123", "22232212", - "11332213", "31332211", "23114122", "12214123", "23123212", "12223213", - "32223211", "21323212", "13114213", "33114211", - /* Column 15 */ - "22214212", "11314213", "31314211", "14141122", "23241121", "12341122", - "24132121", "13232122", "24141211", "13241212", "11432122", "22341211", - "14123122", "14132212", "12323122", "23232211", "12332212", "21432211", - "24114121", "13214122", "24123211", "13223212", "11414122", "22323211", - "11423212", "14114212", "23214211", "12314212", "21414211", "15141121", - "13341121", "14232121", "14241211", "12432121", "15123121", "15132211", - "13323121", "13332211", "11523121", "14214121", "14223211", "12414121", - "12423211", "15114211", "13314211", "11514211", "11151124", "31151122", - "21142123", "41142121", "21151213", "41151211", "11133124", "31133122", - "11142214", "31142212", "21124123", "41124121", - /* Column 16 */ - "21133213", "41133211", "11115124", "31115122", "11124214", "31124212", - "21115213", "41115211", "12151123", "32151121", "21251122", "22142122", - "11242123", "22151212", "11251213", "31251211", "12133123", "32133121", - "12142213", "32142211", "21242212", "22124122", "11224123", "22133212", - "11233213", "31233211", "12115123", "32115121", "12124213", "32124211", - "21224212", "22115212", "11215213", "31215211", "13151122", "22251121", - "23142121", "12242122", "23151211", "12251212", "13133122", "13142212", - "11333122", "22242211", "11342212", "23124121", "12224122", "23133211", - "12233212", "21333211", "13115122", "13124212", "11315122", "22224211", - "11324212", "23115211", "12215212", "21315211", - /* Column 17 */ - "14151121", "13242121", "13251211", "14133121", "14142211", "12333121", - "12342211", "13224121", "13233211", "11424121", "11433211", "14115121", - "14124211", "12315121", "12324211", "13215211", "11415211", "11161123", - "31161121", "21152122", "21161212", "11143123", "31143121", "11152213", - "31152211", "21134122", "21143212", "11125123", "31125121", "11134213", - "31134211", "21116122", "21125212", "12161122", "22152121", "11252122", - "22161211", "12143122", "12152212", "21252211", "22134121", "11234122", - "22143211", "11243212", "12125122", "12134212", "21234211", "22116121", - "11216122", "22125211", "11225212", "13161121", "12252121", "13143121", - "13152211", "11343121", "12234121", "12243211", - /* Column 18 */ - "13125121", "13134211", "11325121", "11334211", "12216121", "12225211", - "31111225", "51111223", "21111316", "41111314", "61111312", "31211134", - "51211132", "12111226", "32111224", "52111222", "21211225", "41211223", - "61211221", "22111315", "42111313", "11211316", "31211314", "51211312", - "12211135", "32211133", "52211131", "21311134", "41311132", "13111225", - "33111223", "22211224", "42211222", "11311225", "31311223", "51311221", - "23111314", "43111312", "12211315", "32211313", "52211311", "21311314", - "41311312", "13211134", "33211132", "22311133", "42311131", "11411134", - "31411132", "14111224", "34111222", "23211223", "43211221", "12311224", - "32311222", "21411223", "41411221", "24111313", - /* Column 19 */ - "13211314", "33211312", "22311313", "42311311", "11411314", "31411312", - "14211133", "34211131", "23311132", "12411133", "32411131", "21511132", - "15111223", "24211222", "13311223", "33311221", "22411222", "11511223", - "31511221", "25111312", "14211313", "34211311", "23311312", "12411313", - "32411311", "21511312", "15211132", "24311131", "13411132", "22511131", - "11611132", "16111222", "25211221", "14311222", "23411221", "12511222", - "21611221", "15211312", "24311311", "13411312", "22511311", "11611312", - "21121135", "41121133", "61121131", "11112136", "31112134", "51112132", - "11121226", "31121224", "51121222", "21112225", "41112223", "61112221", - "21121315", "41121313", "61121311", "11112316", - /* Column 20 */ - "31112314", "51112312", "22121134", "42121132", "11221135", "31221133", - "51221131", "12112135", "32112133", "52112131", "12121225", "32121223", - "52121221", "21221224", "41221222", "22112224", "42112222", "11212225", - "22121314", "42121312", "11221315", "31221313", "51221311", "12112315", - "32112313", "52112311", "21212314", "41212312", "23121133", "43121131", - "12221134", "32221132", "21321133", "41321131", "13112134", "33112132", - "13121224", "33121222", "11312134", "22221223", "42221221", "11321224", - "31321222", "23112223", "43112221", "12212224", "23121313", "43121311", - "12221314", "32221312", "21321313", "41321311", "13112314", "33112312", - "22212313", "42212311", "11312314", "31312312", - /* Column 21 */ - "24121132", "13221133", "33221131", "22321132", "11421133", "31421131", - "14112133", "34112131", "14121223", "34121221", "12312133", "23221222", - "12321223", "32321221", "21421222", "24112222", "13212223", "24121312", - "13221313", "33221311", "11412223", "22321312", "11421313", "31421311", - "14112313", "34112311", "23212312", "12312313", "32312311", "21412312", - "25121131", "14221132", "23321131", "12421132", "21521131", "15112132", - "15121222", "13312132", "24221221", "13321222", "11512132", "22421221", - "11521222", "25112221", "14212222", "25121311", "14221312", "12412222", - "23321311", "12421312", "21521311", "15112312", "24212311", "13312312", - "22412311", "11512312", "15221131", "13421131", - /* Column 22 */ - "16112131", "16121221", "14312131", "14321221", "12512131", "12521221", - "15212221", "15221311", "13412221", "13421311", "11612221", "16112311", - "14312311", "12512311", "21131134", "41131132", "11122135", "31122133", - "51122131", "11131225", "31131223", "51131221", "21113134", "41113132", - "21122224", "41122222", "21131314", "41131312", "11113225", "31113223", - "51113221", "11122315", "31122313", "51122311", "21113314", "41113312", - "22131133", "42131131", "11231134", "31231132", "12122134", "32122132", - "12131224", "32131222", "21231223", "41231221", "22113133", "42113131", - "11213134", "22122223", "42122221", "11222224", "22131313", "42131311", - "11231314", "31231312", "12113224", "32113222", - /* Column 23 */ - "12122314", "32122312", "21222313", "41222311", "22113313", "42113311", - "11213314", "31213312", "23131132", "12231133", "32231131", "21331132", - "13122133", "33122131", "13131223", "33131221", "11322133", "22231222", - "11331223", "31331221", "23113132", "12213133", "23122222", "12222223", - "23131312", "12231313", "32231311", "21331312", "13113223", "33113221", - "13122313", "33122311", "11313223", "22222312", "11322313", "31322311", - "23113312", "12213313", "32213311", "21313312", "24131131", "13231132", - "22331131", "11431132", "14122132", "14131222", "12322132", "23231221", - "12331222", "21431221", "24113131", "13213132", "24122221", "13222222", - "24131311", "11413132", "13231312", "11422222", - /* Column 24 */ - "22331311", "11431312", "14113222", "14122312", "12313222", "23222311", - "12322312", "21422311", "24113311", "13213312", "22313311", "11413312", - "14231131", "12431131", "15122131", "15131221", "13322131", "13331221", - "11522131", "14213131", "14222221", "12413131", "14231311", "12422221", - "12431311", "15113221", "15122311", "13313221", "13322311", "11513221", - "11522311", "14213311", "12413311", "21141133", "41141131", "11132134", - "31132132", "11141224", "31141222", "21123133", "41123131", "21132223", - "41132221", "21141313", "41141311", "11114134", "31114132", "11123224", - "31123222", "11132314", "31132312", "21114223", "41114221", "21123313", - "41123311", "11114314", "31114312", "22141132", - /* Column 25 */ - "11241133", "31241131", "12132133", "32132131", "12141223", "32141221", - "21241222", "22123132", "11223133", "22132222", "11232223", "22141312", - "11241313", "31241311", "12114133", "32114131", "12123223", "32123221", - "12132313", "32132311", "21232312", "22114222", "11214223", "22123312", - "11223313", "31223311", "12114313", "32114311", "21214312", "23141131", - "12241132", "21341131", "13132132", "13141222", "11332132", "22241221", - "11341222", "23123131", "12223132", "23132221", "12232222", "23141311", - "12241312", "21341311", "13114132", "13123222", "11314132", "13132312", - "11323222", "22232311", "11332312", "23114221", "12214222", "23123311", - "12223312", "21323311", "13114312", "22214311", - /* Column 26 */ - "11314312", "13241131", "14132131", "14141221", "12332131", "12341221", - "13223131", "13232221", "11423131", "13241311", "11432221", "14114131", - "14123221", "12314131", "14132311", "12323221", "12332311", "13214221", - "13223311", "11414221", "11423311", "14114311", "12314311", "21151132", - "11142133", "31142131", "11151223", "31151221", "21133132", "21142222", - "21151312", "11124133", "31124131", "11133223", "31133221", "11142313", - "31142311", "21115132", "21124222", "21133312", "11115223", "31115221", - "11124313", "31124311", "22151131", "11251132", "12142132", "12151222", - "21251221", "22133131", "11233132", "22142221", "11242222", "22151311", - "11251312", "12124132", "12133222", "12142312", - /* Column 27 */ - "21242311", "22115131", "11215132", "22124221", "11224222", "22133311", - "11233312", "12115222", "12124312", "21224311", "12251131", "13142131", - "13151221", "11342131", "12233131", "12242221", "12251311", "13124131", - "13133221", "11324131", "13142311", "11333221", "11342311", "12215131", - "12224221", "12233311", "13115221", "13124311", "11315221", "11324311", - "21161131", "11152132", "11161222", "21143131", "21152221", "21161311", - "11134132", "11143222", "11152312", "21125131", "21134221", "21143311", - "11116132", "11125222", "11134312", "12152131", "12161221", "11243131", - "11252221", "12134131", "12143221", "12152311", "11225131", "11234221", - "11243311", "12116131", "12125221", "12134311", - /* Column 28 */ - "21111235", "41111233", "61111231", "11111326", "31111324", "51111322", - "21111415", "41111413", "61111411", "21211144", "41211142", "22111234", - "42111232", "11211235", "31211233", "51211231", "12111325", "32111323", - "52111321", "21211324", "41211322", "22111414", "42111412", "11211415", - "31211413", "51211411", "22211143", "42211141", "11311144", "31311142", - "23111233", "43111231", "12211234", "32211232", "21311233", "41311231", - "13111324", "33111322", "22211323", "42211321", "11311324", "31311322", - "23111413", "43111411", "12211414", "32211412", "21311413", "41311411", - "23211142", "12311143", "32311141", "21411142", "24111232", "13211233", - "33211231", "22311232", "11411233", "31411231", - /* Column 29 */ - "14111323", "34111321", "23211322", "12311323", "32311321", "21411322", - "24111412", "13211413", "33211411", "22311412", "11411413", "31411411", - "24211141", "13311142", "22411141", "11511142", "25111231", "14211232", - "23311231", "12411232", "21511231", "15111322", "24211321", "13311322", - "22411321", "11511322", "25111411", "14211412", "23311411", "12411412", - "21511411", "14311141", "12511141", "15211231", "13411231", "11611231", - "16111321", "14311321", "12511321", "15211411", "13411411", "11611411", - "31121143", "51121141", "21112144", "41112142", "21121234", "41121232", - "11112235", "31112233", "51112231", "11121325", "31121323", "51121321", - "21112324", "41112322", "21121414", "41121412", - /* Column 30 */ - "11112415", "31112413", "51112411", "12121144", "32121142", "21221143", - "41221141", "22112143", "42112141", "11212144", "22121233", "42121231", - "11221234", "31221232", "12112234", "32112232", "12121324", "32121322", - "21221323", "41221321", "22112323", "42112321", "11212324", "22121413", - "42121411", "11221414", "31221412", "12112414", "32112412", "21212413", - "41212411", "13121143", "33121141", "22221142", "11321143", "31321141", - "23112142", "12212143", "23121232", "12221233", "32221231", "21321232", - "13112233", "33112231", "13121323", "33121321", "11312233", "22221322", - "11321323", "31321321", "23112322", "12212323", "23121412", "12221413", - "32221411", "21321412", "13112413", "33112411", - /* Column 31 */ - "22212412", "11312413", "31312411", "14121142", "23221141", "12321142", - "21421141", "24112141", "13212142", "24121231", "13221232", "11412142", - "22321231", "11421232", "14112232", "14121322", "12312232", "23221321", - "12321322", "21421321", "24112321", "13212322", "24121411", "13221412", - "11412322", "22321411", "11421412", "14112412", "23212411", "12312412", - "21412411", "15121141", "13321141", "11521141", "14212141", "14221231", - "12412141", "12421231", "15112231", "15121321", "13312231", "13321321", - "11512231", "11521321", "14212321", "14221411", "12412321", "12421411", - "15112411", "13312411", "11512411", "11131144", "31131142", "21122143", - "41122141", "21131233", "41131231", "11113144", - /* Column 32 */ - "31113142", "11122234", "31122232", "11131324", "31131322", "21113233", - "41113231", "21122323", "41122321", "21131413", "41131411", "11113324", - "31113322", "11122414", "31122412", "21113413", "41113411", "12131143", - "32131141", "21231142", "22122142", "11222143", "22131232", "11231233", - "31231231", "12113143", "32113141", "12122233", "32122231", "12131323", - "32131321", "21231322", "22113232", "11213233", "22122322", "11222323", - "22131412", "11231413", "31231411", "12113323", "32113321", "12122413", - "32122411", "21222412", "22113412", "11213413", "31213411", "13131142", - "22231141", "11331142", "23122141", "12222142", "23131231", "12231232", - "21331231", "13113142", "13122232", "11313142", - /* Column 33 */ - "13131322", "11322232", "22231321", "11331322", "23113231", "12213232", - "23122321", "12222322", "23131411", "12231412", "21331411", "13113322", - "13122412", "11313322", "22222411", "11322412", "23113411", "12213412", - "21313411", "14131141", "12331141", "13222141", "13231231", "11422141", - "11431231", "14113141", "14122231", "12313141", "14131321", "12322231", - "12331321", "13213231", "13222321", "11413231", "13231411", "11422321", - "11431411", "14113321", "14122411", "12313321", "12322411", "13213411", - "11413411", "11141143", "31141141", "21132142", "21141232", "11123143", - "31123141", "11132233", "31132231", "11141323", "31141321", "21114142", - "21123232", "21132322", "21141412", "11114233", - /* Column 34 */ - "31114231", "11123323", "31123321", "11132413", "31132411", "21114322", - "21123412", "12141142", "21241141", "22132141", "11232142", "22141231", - "11241232", "12123142", "12132232", "12141322", "21241321", "22114141", - "11214142", "22123231", "11223232", "22132321", "11232322", "22141411", - "11241412", "12114232", "12123322", "12132412", "21232411", "22114321", - "11214322", "22123411", "11223412", "13141141", "11341141", "12232141", - "12241231", "13123141", "13132231", "11323141", "13141321", "11332231", - "11341321", "12214141", "12223231", "12232321", "12241411", "13114231", - "13123321", "11314231", "13132411", "11323321", "11332411", "12214321", - "12223411", "11151142", "21142141", "21151231", - /* Column 35 */ - "11133142", "11142232", "11151322", "21124141", "21133231", "21142321", - "21151411", "11115142", "11124232", "11133322", "11142412", "21115231", - "21124321", "21133411", "12151141", "11242141", "11251231", "12133141", - "12142231", "12151321", "11224141", "11233231", "11242321", "11251411", - "12115141", "12124231", "12133321", "12142411", "11215231", "11224321", - "11233411", "11161141", "11143141", "11152231", "11161321", "11125141", - "11134231", "11143321", "11152411", "11111245", "31111243", "51111241", - "21111334", "41111332", "11111425", "31111423", "51111421", "21111514", - "41111512", "31211152", "12111244", "32111242", "21211243", "41211241", - "22111333", "42111331", "11211334", "31211332", - /* Column 36 */ - "12111424", "32111422", "21211423", "41211421", "22111513", "42111511", - "11211514", "31211512", "12211153", "32211151", "21311152", "13111243", - "33111241", "22211242", "11311243", "31311241", "23111332", "12211333", - "32211331", "21311332", "13111423", "33111421", "22211422", "11311423", - "31311421", "23111512", "12211513", "32211511", "21311512", "13211152", - "22311151", "11411152", "14111242", "23211241", "12311242", "21411241", - "24111331", "13211332", "22311331", "11411332", "14111422", "23211421", - "12311422", "21411421", "24111511", "13211512", "22311511", "11411512", - "14211151", "12411151", "15111241", "13311241", "11511241", "14211331", - "12411331", "15111421", "13311421", "11511421", - /* Column 37 */ - "14211511", "12411511", "21121153", "41121151", "11112154", "31112152", - "11121244", "31121242", "21112243", "41112241", "21121333", "41121331", - "11112334", "31112332", "11121424", "31121422", "21112423", "41112421", - "21121513", "41121511", "11112514", "31112512", "22121152", "11221153", - "31221151", "12112153", "32112151", "12121243", "32121241", "21221242", - "22112242", "11212243", "22121332", "11221333", "31221331", "12112333", - "32112331", "12121423", "32121421", "21221422", "22112422", "11212423", - "22121512", "11221513", "31221511", "12112513", "32112511", "21212512", - "23121151", "12221152", "21321151", "13112152", "13121242", "11312152", - "22221241", "11321242", "23112241", "12212242", - /* Column 38 */ - "23121331", "12221332", "21321331", "13112332", "13121422", "11312332", - "22221421", "11321422", "23112421", "12212422", "23121511", "12221512", - "21321511", "13112512", "22212511", "11312512", "13221151", "11421151", - "14112151", "14121241", "12312151", "12321241", "13212241", "13221331", - "11412241", "11421331", "14112331", "14121421", "12312331", "12321421", - "13212421", "13221511", "11412421", "11421511", "14112511", "12312511", - "21131152", "11122153", "31122151", "11131243", "31131241", "21113152", - "21122242", "21131332", "11113243", "31113241", "11122333", "31122331", - "11131423", "31131421", "21113332", "21122422", "21131512", "11113423", - "31113421", "11122513", "31122511", "22131151", - /* Column 39 */ - "11231152", "12122152", "12131242", "21231241", "22113151", "11213152", - "22122241", "11222242", "22131331", "11231332", "12113242", "12122332", - "12131422", "21231421", "22113331", "11213332", "22122421", "11222422", - "22131511", "11231512", "12113422", "12122512", "21222511", "12231151", - "13122151", "13131241", "11322151", "11331241", "12213151", "12222241", - "12231331", "13113241", "13122331", "11313241", "13131421", "11322331", - "11331421", "12213331", "12222421", "12231511", "13113421", "13122511", - "11313421", "11322511", "21141151", "11132152", "11141242", "21123151", - "21132241", "21141331", "11114152", "11123242", "11132332", "11141422", - "21114241", "21123331", "21132421", "21141511", - /* Column 40 */ - "11114332", "11123422", "11132512", "11241151", "12132151", "12141241", - "11223151", "11232241", "11241331", "12114151", "12123241", "12132331", - "12141421", "11214241", "11223331", "11232421", "11241511", "12114331", - "12123421", "12132511", "11142151", "11151241", "11124151", "11133241", - "11142331", "11151421", "11115241", "11124331", "11133421", "11142511", - "21111253", "41111251", "11111344", "31111342", "21111433", "41111431", - "11111524", "31111522", "21111613", "41111611", "21211162", "22111252", - "11211253", "31211251", "12111343", "32111341", "21211342", "22111432", - "11211433", "31211431", "12111523", "32111521", "21211522", "22111612", - "11211613", "31211611", "22211161", "11311162", - /* Column 41 */ - "23111251", "12211252", "21311251", "13111342", "22211341", "11311342", - "23111431", "12211432", "21311431", "13111522", "22211521", "11311522", - "23111611", "12211612", "21311611", "12311161", "13211251", "11411251", - "14111341", "12311341", "13211431", "11411431", "14111521", "12311521", - "13211611", "11411611", "31121161", "21112162", "21121252", "11112253", - "31112251", "11121343", "31121341", "21112342", "21121432", "11112433", - "31112431", "11121523", "31121521", "21112522", "21121612", - /* Column 42 */ - "12121162", "21221161", "22112161", "11212162", "22121251", "11221252", - "12112252", "12121342", "21221341", "22112341", "11212342", "22121431", - "11221432", "12112432", "12121522", "21221521", "22112521", "11212522", - "22121611", "11221612", "13121161", "11321161", "12212161", "12221251", - "13112251", "13121341", "11312251", "11321341", "12212341", "12221431", - "13112431", "13121521", "11312431", "11321521", "12212521", "12221611", - "11131162", "21122161", "21131251", "11113162" - }; - - private static final char[] C49_SET = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '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', '-', '.', ' ', '$', '/', '+', - '%', '!', '&', '*' - }; - - @Override - protected boolean gs1Supported() { - return true; - } - - @Override - protected void encode() { - int codeword_count = 0, pad_count = 0; - int x_count, y_count, z_count, posn_val, local_value; - int[] codewords = new int[170]; - int[][] c_grid = new int[8][8]; - int[][] w_grid = new int[8][4]; - - if (!content.matches("[\u0000-\u007F]+")) { - throw OkapiInputException.invalidCharactersInInput(); - } - - inputData = toBytes(content, StandardCharsets.US_ASCII); - - StringBuilder intermediate = new StringBuilder(inputData.length + 8); - if (inputDataType == DataType.GS1) { - intermediate.append('*'); // FNC1 - } - for (int i = 0; i < inputData.length; i++) { - int c = inputData[i]; - if (c == FNC1) { - intermediate.append('*'); // FNC1 - } else { - intermediate.append(C49_TABLE7[c]); - } - } - - int h = intermediate.length(); - int i = 0; - - do { - if (intermediate.charAt(i) >= '0' && intermediate.charAt(i) <= '9') { - - /* Numeric data */ - int latch = 0; - int j = 0; - do { - if ((i + j) >= h) { - latch = 1; - } else { - if (intermediate.charAt(i + j) >= '0' && intermediate.charAt(i + j) <= '9') { - j++; - } else { - latch = 1; - } - } - } while (latch == 0); - - if (j >= 5) { - - /* Use Numeric Encodation Method */ - int block_count, c; - int block_remain; - int block_value; - - codewords[codeword_count] = 48; /* Numeric Shift */ - codeword_count++; - - block_count = j / 5; - block_remain = j % 5; - - for (c = 0; c < block_count; c++) { - if ((c == block_count - 1) && (block_remain == 2)) { - /* Rule (d) */ - block_value = 100000; - block_value += (intermediate.charAt(i) - '0') * 1000; - block_value += (intermediate.charAt(i + 1) - '0') * 100; - block_value += (intermediate.charAt(i + 2) - '0') * 10; - block_value += intermediate.charAt(i + 3) - '0'; - - codewords[codeword_count] = block_value / (48 * 48); - block_value = block_value - (48 * 48) * codewords[codeword_count]; - codeword_count++; - codewords[codeword_count] = block_value / 48; - block_value = block_value - 48 * codewords[codeword_count]; - codeword_count++; - codewords[codeword_count] = block_value; - codeword_count++; - i += 4; - block_value = (intermediate.charAt(i) - '0') * 100; - block_value += (intermediate.charAt(i + 1) - '0') * 10; - block_value += intermediate.charAt(i + 2) - '0'; - - codewords[codeword_count] = block_value / 48; - block_value = block_value - 48 * codewords[codeword_count]; - codeword_count++; - codewords[codeword_count] = block_value; - codeword_count++; - i += 3; - } else { - block_value = (intermediate.charAt(i) - '0') * 10000; - block_value += (intermediate.charAt(i + 1) - '0') * 1000; - block_value += (intermediate.charAt(i + 2) - '0') * 100; - block_value += (intermediate.charAt(i + 3) - '0') * 10; - block_value += intermediate.charAt(i + 4) - '0'; - - codewords[codeword_count] = block_value / (48 * 48); - block_value = block_value - (48 * 48) * codewords[codeword_count]; - codeword_count++; - codewords[codeword_count] = block_value / 48; - block_value = block_value - 48 * codewords[codeword_count]; - codeword_count++; - codewords[codeword_count] = block_value; - codeword_count++; - i += 5; - } - } - - switch (block_remain) { - case 1: - /* Rule (a) */ - codewords[codeword_count] = positionOf(intermediate.charAt(i), C49_SET); - codeword_count++; - i++; - break; - case 3: - /* Rule (b) */ - block_value = (intermediate.charAt(i) - '0') * 100; - block_value += (intermediate.charAt(i + 1) - '0') * 10; - block_value += intermediate.charAt(i + 2) - '0'; - - codewords[codeword_count] = block_value / 48; - block_value = block_value - 48 * codewords[codeword_count]; - codeword_count++; - codewords[codeword_count] = block_value; - codeword_count++; - i += 3; - break; - case 4: - /* Rule (c) */ - block_value = 100000; - block_value += (intermediate.charAt(i) - '0') * 1000; - block_value += (intermediate.charAt(i + 1) - '0') * 100; - block_value += (intermediate.charAt(i + 2) - '0') * 10; - block_value += intermediate.charAt(i + 3) - '0'; - - codewords[codeword_count] = block_value / (48 * 48); - block_value = block_value - (48 * 48) * codewords[codeword_count]; - codeword_count++; - codewords[codeword_count] = block_value / 48; - block_value = block_value - 48 * codewords[codeword_count]; - codeword_count++; - codewords[codeword_count] = block_value; - codeword_count++; - i += 4; - break; - } - if (i < h) { - /* There is more to add */ - codewords[codeword_count] = 48; /* Numeric Shift */ - codeword_count++; - } - } else { - codewords[codeword_count] = positionOf(intermediate.charAt(i), C49_SET); - codeword_count++; - i++; - } - } else { - codewords[codeword_count] = positionOf(intermediate.charAt(i), C49_SET); - codeword_count++; - i++; - } - } while (i < h); - - int M; - switch (codewords[0]) { /* Set starting mode value */ - case 48: - M = 2; - break; - case 43: - M = 4; - break; - case 44: - M = 5; - break; - default: - M = 0; - break; - } - - if (M != 0) { - for (i = 0; i < codeword_count; i++) { - codewords[i] = codewords[i + 1]; - } - codeword_count--; - } - - if (codeword_count > 49) { - throw OkapiInputException.inputTooLong(); - } - - infoLine("Starting Mode (M): " + M); - - /* Place codewords in code character array (c grid) */ - int rows = 0; - do { - for (i = 0; i < 7; i++) { - if (((rows * 7) + i) < codeword_count) { - c_grid[rows][i] = codewords[(rows * 7) + i]; - } else { - c_grid[rows][i] = 48; /* Pad */ - pad_count++; - } - } - rows++; - } while ((rows * 7) < codeword_count); - - if ((((rows <= 6) && (pad_count < 5))) || (rows > 6) || (rows == 1)) { - /* Add a row */ - for (i = 0; i < 7; i++) { - c_grid[rows][i] = 48; /* Pad */ - } - rows++; - } - - /* Add row count and mode character */ - c_grid[rows - 1][6] = (7 * (rows - 2)) + M; - - /* Add row check character */ - for (i = 0; i < rows - 1; i++) { - int row_sum = 0; - for (int j = 0; j < 7; j++) { - row_sum += c_grid[i][j]; - } - c_grid[i][7] = row_sum % 49; - } - - /* Calculate Symbol Check Characters */ - posn_val = 0; - x_count = c_grid[rows - 1][6] * 20; - y_count = c_grid[rows - 1][6] * 16; - z_count = c_grid[rows - 1][6] * 38; - for (i = 0; i < rows - 1; i++) { - for (int j = 0; j < 4; j++) { - local_value = (c_grid[i][2 * j] * 49) + c_grid[i][(2 * j) + 1]; - x_count += C49_X_WEIGHT[posn_val] * local_value; - y_count += C49_Y_WEIGHT[posn_val] * local_value; - z_count += C49_Z_WEIGHT[posn_val] * local_value; - posn_val++; - } - } - - if (rows > 6) { - /* Add Z Symbol Check */ - c_grid[rows - 1][0] = (z_count % 2401) / 49; - c_grid[rows - 1][1] = (z_count % 2401) % 49; - } - - local_value = (c_grid[rows - 1][0] * 49) + c_grid[rows - 1][1]; - x_count += C49_X_WEIGHT[posn_val] * local_value; - y_count += C49_Y_WEIGHT[posn_val] * local_value; - posn_val++; - - /* Add Y Symbol Check */ - c_grid[rows - 1][2] = (y_count % 2401) / 49; - c_grid[rows - 1][3] = (y_count % 2401) % 49; - - local_value = (c_grid[rows - 1][2] * 49) + c_grid[rows - 1][3]; - x_count += C49_X_WEIGHT[posn_val] * local_value; - - /* Add X Symbol Check */ - c_grid[rows - 1][4] = (x_count % 2401) / 49; - c_grid[rows - 1][5] = (x_count % 2401) % 49; - - infoLine("Check Characters: " + (z_count % 2401) + " " + (y_count % 2401)); - - /* Add last row check character */ - int sum = 0; - for (i = 0; i < 7; i++) { - sum += c_grid[rows - 1][i]; - } - c_grid[rows - 1][7] = sum % 49; - - info("Codewords: "); - /* Transfer data to symbol character array (w grid) */ - for (i = 0; i < rows; i++) { - for (int j = 0; j < 4; j++) { - w_grid[i][j] = (c_grid[i][2 * j] * 49) + c_grid[i][(2 * j) + 1]; - infoSpace(c_grid[i][2 * j]); - infoSpace(c_grid[i][(2 * j) + 1]); - } - } - infoLine(); - - readable = ""; - pattern = new String[rows]; - row_count = rows; - row_height = new int[rows]; - - info("Symbol Characters: "); - for (i = 0; i < rows; i++) { - StringBuilder rowPattern = new StringBuilder(3 + (4 * 8)); - rowPattern.append("11"); /* Start character */ - for (int j = 0; j < 4; j++) { - infoSpace(w_grid[i][j]); - if (i != (rows - 1)) { - if (C49_TABLE4[i].charAt(j) == 'E') { - /* Even Parity */ - rowPattern.append(C49_APPXE_EVEN[w_grid[i][j]]); - } else { - /* Odd Parity */ - rowPattern.append(C49_APPXE_ODD[w_grid[i][j]]); - } - } else { - /* Last row uses all even parity */ - rowPattern.append(C49_APPXE_EVEN[w_grid[i][j]]); - } - } - rowPattern.append('4'); /* Stop character */ - pattern[i] = rowPattern.toString(); - row_height[i] = 10; - } - infoLine(); - } - - @Override - protected void plotSymbol() { - - int xBlock, yBlock; - int x, y, w, h; - boolean black; - - resetPlotElements(); - - y = 1; - h = 1; - for (yBlock = 0; yBlock < row_count; yBlock++) { - black = true; - x = 15; - for (xBlock = 0; xBlock < pattern[yBlock].length(); xBlock++) { - if (black) { - black = false; - w = pattern[yBlock].charAt(xBlock) - '0'; - if (row_height[yBlock] == -1) { - h = default_height; - } else { - h = row_height[yBlock]; - } - if (w != 0 && h != 0) { - Rectangle rect = new Rectangle(x, y, w, h); - rectangles.add(rect); - } - if (x + w > symbol_width) { - symbol_width = x + w; - } - } else { - black = true; - } - x += pattern[yBlock].charAt(xBlock) - '0'; - } - y += h; - if (y > symbol_height) { - symbol_height = y; - } - /* Add bars between rows */ - if (yBlock != row_count - 1) { - Rectangle rect = new Rectangle(15, y - 1, symbol_width - 15, 2); - rectangles.add(rect); - } - } - - /* Add top and bottom binding bars */ - Rectangle top = new Rectangle(0, 0, symbol_width + 15, 2); - rectangles.add(top); - Rectangle bottom = new Rectangle(0, y - 1, symbol_width + 15, 2); - rectangles.add(bottom); - symbol_width += 15; - symbol_height += 1; - } -} +/* + * Copyright 2014 Robin Stuart + * + * 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. + */ + +package uk.org.okapibarcode.backend; + +import static uk.org.okapibarcode.util.Arrays.positionOf; + +import java.nio.charset.StandardCharsets; + +import uk.org.okapibarcode.graphics.Rectangle; + +/** + *

Implements Code 49 according to ANSI/AIM-BC6-2000. + * + *

Supports full 7-bit ASCII input up to a maximum of 49 characters + * or 81 numeric digits. GS1 data encoding is also supported. + * + * @author Robin Stuart + */ +public class Code49 extends Symbol { + + private static final String[] C49_TABLE7 = { + /* Table 7: Code 49 ASCII Chart */ + "! ", "!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", "!1", "!2", "!3", "!4", "!5", " ", "!6", "!7", "!8", + "$", "%", "!9", "!0", "!-", "!.", "!$", "+", "!/", "-", ".", "/", "0", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "!+", "&1", "&2", "&3", + "&4", "&5", "&6", "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", "&7", "&8", "&9", "&0", "&-", "&.", "&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", "&$", "&/", "&+", + "&%", "& " + }; + + /* Table 5: Check Character Weighting Values */ + private static final int[] C49_X_WEIGHT = { + 1, 9, 31, 26, 2, 12, 17, 23, 37, 18, 22, 6, 27, 44, 15, 43, 39, 11, 13, + 5, 41, 33, 36, 8, 4, 32, 3, 19, 40, 25, 29, 10 + }; + + private static final int[] C49_Y_WEIGHT = { + 9, 31, 26, 2, 12, 17, 23, 37, 18, 22, 6, 27, 44, 15, 43, 39, 11, 13, 5, + 41, 33, 36, 8, 4, 32, 3, 19, 40, 25, 29, 10, 24 + }; + + private static final int[] C49_Z_WEIGHT = { + 31, 26, 2, 12, 17, 23, 37, 18, 22, 6, 27, 44, 15, 43, 39, 11, 13, 5, 41, + 33, 36, 8, 4, 32, 3, 19, 40, 25, 29, 10, 24, 30 + }; + + private static final String[] C49_TABLE4 = { + /* Table 4: Row Parity Pattern for Code 49 Symbols */ + "OEEO", "EOEO", "OOEE", "EEOO", "OEOE", "EOOE", "OOOO", "EEEE" + }; + + private static final String[] C49_APPXE_EVEN = { + /* Appendix E - Code 49 Encodation Patterns (Even Symbol Character Parity) */ + /* Column 1 */ + "11521132", "25112131", "14212132", "25121221", "14221222", "12412132", + "23321221", "12421222", "21521221", "15112222", "15121312", "13312222", + "24221311", "13321312", "11512222", "22421311", "11521312", "25112311", + "14212312", "23312311", "12412312", "21512311", "16121131", "14321131", + "12521131", "15212131", "15221221", "13412131", "13421221", "11612131", + "16112221", "16121311", "14312221", "14321311", "12512221", "12521311", + "15212311", "13412311", "11612311", "11131135", "31131133", "51131131", + "21122134", "41122132", "21131224", "41131222", "11113135", "31113133", + "51113131", "11122225", "31122223", "51122221", "11131315", "31131313", + "51131311", "21113224", "41113222", "21122314", + /* Column 2 */ + "41122312", "11113315", "31113313", "51113311", "12131134", "32131132", + "21231133", "41231131", "22122133", "42122131", "11222134", "22131223", + "42131221", "11231224", "31231222", "12113134", "32113132", "12122224", + "32122222", "12131314", "32131312", "21231313", "41231311", "22113223", + "42113221", "11213224", "22122313", "42122311", "11222314", "31222312", + "12113314", "32113312", "21213313", "41213311", "13131133", "33131131", + "22231132", "11331133", "31331131", "23122132", "12222133", "23131222", + "12231223", "32231221", "21331222", "13113133", "33113131", "13122223", + "33122221", "11313133", "13131313", "33131311", "11322223", "22231312", + "11331313", "31331311", "23113222", "12213223", + /* Column 3 */ + "23122312", "12222313", "32222311", "21322312", "13113313", "33113311", + "22213312", "11313313", "31313311", "14131132", "23231131", "12331132", + "21431131", "24122131", "13222132", "24131221", "13231222", "11422132", + "22331221", "11431222", "14113132", "14122222", "12313132", "14131312", + "12322222", "23231311", "12331312", "21431311", "24113221", "13213222", + "24122311", "13222312", "11413222", "22322311", "11422312", "14113312", + "23213311", "12313312", "21413311", "15131131", "13331131", "14222131", + "14231221", "12422131", "12431221", "15113131", "15122221", "13313131", + "15131311", "13322221", "11513131", "13331311", "11522221", "14213221", + "14222311", "12413221", "12422311", "15113311", + /* Column 4 */ + "13313311", "11513311", "11141134", "31141132", "21132133", "41132131", + "21141223", "41141221", "11123134", "31123132", "11132224", "31132222", + "11141314", "31141312", "21114133", "41114131", "21123223", "41123221", + "21132313", "41132311", "11114224", "31114222", "11123314", "31123312", + "21114313", "41114311", "12141133", "32141131", "21241132", "22132132", + "11232133", "22141222", "11241223", "31241221", "12123133", "32123131", + "12132223", "32132221", "12141313", "32141311", "21241312", "22114132", + "11214133", "22123222", "11223223", "22132312", "11232313", "31232311", + "12114223", "32114221", "12123313", "32123311", "21223312", "22114312", + "11214313", "31214311", "13141132", "22241131", + /* Column 5 */ + "11341132", "23132131", "12232132", "23141221", "12241222", "21341221", + "13123132", "13132222", "11323132", "13141312", "11332222", "22241311", + "11341312", "23114131", "12214132", "23123221", "12223222", "23132311", + "12232312", "21332311", "13114222", "13123312", "11314222", "22223311", + "11323312", "23114311", "12214312", "21314311", "14141131", "12341131", + "13232131", "13241221", "11432131", "14123131", "14132221", "12323131", + "14141311", "12332221", "12341311", "13214131", "13223221", "11414131", + "13232311", "11423221", "11432311", "14114221", "14123311", "12314221", + "12323311", "13214311", "11414311", "11151133", "31151131", "21142132", + "21151222", "11133133", "31133131", "11142223", + /* Column 6 */ + "31142221", "11151313", "31151311", "21124132", "21133222", "21142312", + "11115133", "31115131", "11124223", "31124221", "11133313", "31133311", + "21115222", "21124312", "12151132", "21251131", "22142131", "11242132", + "22151221", "11251222", "12133132", "12142222", "12151312", "21251311", + "22124131", "11224132", "22133221", "11233222", "22142311", "11242312", + "12115132", "12124222", "12133312", "21233311", "22115221", "11215222", + "22124311", "11224312", "13151131", "12242131", "12251221", "13133131", + "13142221", "11333131", "13151311", "11342221", "12224131", "12233221", + "12242311", "13115131", "13124221", "11315131", "13133311", "11324221", + "11333311", "12215221", "12224311", "11161132", + /* Column 7 */ + "21152131", "21161221", "11143132", "11152222", "11161312", "21134131", + "21143221", "21152311", "11125132", "11134222", "11143312", "21116131", + "21125221", "21134311", "12161131", "11252131", "12143131", "12152221", + "12161311", "11234131", "11243221", "11252311", "12125131", "12134221", + "12143311", "11216131", "11225221", "11234311", "11111236", "31111234", + "51111232", "21111325", "41111323", "61111321", "11111416", "31111414", + "51111412", "31211143", "51211141", "12111235", "32111233", "52111231", + "21211234", "41211232", "22111324", "42111322", "11211325", "31211323", + "51211321", "12111415", "32111413", "52111411", "21211414", "41211412", + "12211144", "32211142", "21311143", "41311141", + /* Column 8 */ + "13111234", "33111232", "22211233", "42211231", "11311234", "31311232", + "23111323", "43111321", "12211324", "32211322", "21311323", "41311321", + "13111414", "33111412", "22211413", "42211411", "11311414", "31311412", + "13211143", "33211141", "22311142", "11411143", "31411141", "14111233", + "34111231", "23211232", "12311233", "32311231", "21411232", "24111322", + "13211323", "33211321", "22311322", "11411323", "31411321", "14111413", + "34111411", "23211412", "12311413", "32311411", "21411412", "14211142", + "23311141", "12411142", "21511141", "15111232", "24211231", "13311232", + "22411231", "11511232", "25111321", "14211322", "23311321", "12411322", + "21511321", "15111412", "24211411", "13311412", + /* Column 9 */ + "22411411", "11511412", "15211141", "13411141", "11611141", "16111231", + "14311231", "12511231", "15211321", "13411321", "11611321", "16111411", + "14311411", "12511411", "21121144", "41121142", "11112145", "31112143", + "51112141", "11121235", "31121233", "51121231", "21112234", "41112232", + "21121324", "41121322", "11112325", "31112323", "51112321", "11121415", + "31121413", "51121411", "21112414", "41112412", "22121143", "42121141", + "11221144", "31221142", "12112144", "32112142", "12121234", "32121232", + "21221233", "41221231", "22112233", "42112231", "11212234", "22121323", + "42121321", "11221324", "31221322", "12112324", "32112322", "12121414", + "32121412", "21221413", "41221411", "22112413", + /* Column 10 */ + "42112411", "11212414", "31212412", "23121142", "12221143", "32221141", + "21321142", "13112143", "33112141", "13121233", "33121231", "11312143", + "22221232", "11321233", "31321231", "23112232", "12212233", "23121322", + "12221323", "32221321", "21321322", "13112323", "33112321", "13121413", + "33121411", "11312323", "22221412", "11321413", "31321411", "23112412", + "12212413", "32212411", "21312412", "24121141", "13221142", "22321141", + "11421142", "14112142", "14121232", "12312142", "23221231", "12321232", + "21421231", "24112231", "13212232", "24121321", "13221322", "11412232", + "22321321", "11421322", "14112322", "14121412", "12312322", "23221411", + "12321412", "21421411", "24112411", "13212412", + /* Column 11 */ + "22312411", "11412412", "14221141", "12421141", "15112141", "15121231", + "13312141", "13321231", "11512141", "11521231", "14212231", "14221321", + "12412231", "12421321", "15112321", "15121411", "13312321", "13321411", + "11512321", "11521411", "14212411", "12412411", "21131143", "41131141", + "11122144", "31122142", "11131234", "31131232", "21113143", "41113141", + "21122233", "41122231", "21131323", "41131321", "11113234", "31113232", + "11122324", "31122322", "11131414", "31131412", "21113323", "41113321", + "21122413", "41122411", "11113414", "31113412", "22131142", "11231143", + "31231141", "12122143", "32122141", "12131233", "32131231", "21231232", + "22113142", "11213143", "22122232", "11222233", + /* Column 12 */ + "22131322", "11231323", "31231321", "12113233", "32113231", "12122323", + "32122321", "12131413", "32131411", "21231412", "22113322", "11213323", + "22122412", "11222413", "31222411", "12113413", "32113411", "21213412", + "23131141", "12231142", "21331141", "13122142", "13131232", "11322142", + "22231231", "11331232", "23113141", "12213142", "23122231", "12222232", + "23131321", "12231322", "21331321", "13113232", "13122322", "11313232", + "13131412", "11322322", "22231411", "11331412", "23113321", "12213322", + "23122411", "12222412", "21322411", "13113412", "22213411", "11313412", + "13231141", "11431141", "14122141", "14131231", "12322141", "12331231", + "13213141", "13222231", "11413141", "13231321", + /* Column 13 */ + "11422231", "11431321", "14113231", "14122321", "12313231", "14131411", + "12322321", "12331411", "13213321", "13222411", "11413321", "11422411", + "14113411", "12313411", "21141142", "11132143", "31132141", "11141233", + "31141231", "21123142", "21132232", "21141322", "11114143", "31114141", + "11123233", "31123231", "11132323", "31132321", "11141413", "31141411", + "21114232", "21123322", "21132412", "11114323", "31114321", "11123413", + "31123411", "22141141", "11241142", "12132142", "12141232", "21241231", + "22123141", "11223142", "22132231", "11232232", "22141321", "11241322", + "12114142", "12123232", "12132322", "12141412", "21241411", "22114231", + "11214232", "22123321", "11223322", "22132411", + /* Column 14 */ + "11232412", "12114322", "12123412", "21223411", "12241141", "13132141", + "13141231", "11332141", "11341231", "12223141", "12232231", "12241321", + "13114141", "13123231", "11314141", "13132321", "11323231", "13141411", + "11332321", "11341411", "12214231", "12223321", "12232411", "13114321", + "13123411", "11314321", "11323411", "21151141", "11142142", "11151232", + "21133141", "21142231", "21151321", "11124142", "11133232", "11142322", + "11151412", "21115141", "21124231", "21133321", "21142411", "11115232", + "11124322", "11133412", "11251141", "12142141", "12151231", "11233141", + "11242231", "11251321", "12124141", "12133231", "12142321", "12151411", + "11215141", "11224231", "11233321", "11242411", + /* Column 15 */ + "12115231", "12124321", "12133411", "11152141", "11161231", "11134141", + "11143231", "11152321", "11161411", "11116141", "11125231", "11134321", + "11143411", "21111244", "41111242", "11111335", "31111333", "51111331", + "21111424", "41111422", "11111515", "31111513", "51111511", "21211153", + "41211151", "22111243", "42111241", "11211244", "31211242", "12111334", + "32111332", "21211333", "41211331", "22111423", "42111421", "11211424", + "31211422", "12111514", "32111512", "21211513", "41211511", "22211152", + "11311153", "31311151", "23111242", "12211243", "32211241", "21311242", + "13111333", "33111331", "22211332", "11311333", "31311331", "23111422", + "12211423", "32211421", "21311422", "13111513", + /* Column 16 */ + "33111511", "22211512", "11311513", "31311511", "23211151", "12311152", + "21411151", "24111241", "13211242", "22311241", "11411242", "14111332", + "23211331", "12311332", "21411331", "24111421", "13211422", "22311421", + "11411422", "14111512", "23211511", "12311512", "21411511", "13311151", + "11511151", "14211241", "12411241", "15111331", "13311331", "11511331", + "14211421", "12411421", "15111511", "13311511", "11511511", "31121152", + "21112153", "41112151", "21121243", "41121241", "11112244", "31112242", + "11121334", "31121332", "21112333", "41112331", "21121423", "41121421", + "11112424", "31112422", "11121514", "31121512", "21112513", "41112511", + "12121153", "32121151", "21221152", "22112152", + /* Column 17 */ + "11212153", "22121242", "11221243", "31221241", "12112243", "32112241", + "12121333", "32121331", "21221332", "22112332", "11212333", "22121422", + "11221423", "31221421", "12112423", "32112421", "12121513", "32121511", + "21221512", "22112512", "11212513", "31212511", "13121152", "22221151", + "11321152", "23112151", "12212152", "23121241", "12221242", "21321241", + "13112242", "13121332", "11312242", "22221331", "11321332", "23112331", + "12212332", "23121421", "12221422", "21321421", "13112422", "13121512", + "11312422", "22221511", "11321512", "23112511", "12212512", "21312511", + "14121151", "12321151", "13212151", "13221241", "11412151", "11421241", + "14112241", "14121331", "12312241", "12321331", + /* Column 18 */ + "13212331", "13221421", "11412331", "11421421", "14112421", "14121511", + "12312421", "12321511", "13212511", "11412511", "11131153", "31131151", + "21122152", "21131242", "11113153", "31113151", "11122243", "31122241", + "11131333", "31131331", "21113242", "21122332", "21131422", "11113333", + "31113331", "11122423", "31122421", "11131513", "31131511", "21113422", + "21122512", "12131152", "21231151", "22122151", "11222152", "22131241", + "11231242", "12113152", "12122242", "12131332", "21231331", "22113241", + "11213242", "22122331", "11222332", "22131421", "11231422", "12113332", + "12122422", "12131512", "21231511", "22113421", "11213422", "22122511", + "11222512", "13131151", "11331151", "12222151", + /* Column 19 */ + "12231241", "13113151", "13122241", "11313151", "13131331", "11322241", + "11331331", "12213241", "12222331", "12231421", "13113331", "13122421", + "11313331", "13131511", "11322421", "11331511", "12213421", "12222511", + "11141152", "21132151", "21141241", "11123152", "11132242", "11141332", + "21114151", "21123241", "21132331", "21141421", "11114242", "11123332", + "11132422", "11141512", "21114331", "21123421", "21132511", "12141151", + "11232151", "11241241", "12123151", "12132241", "12141331", "11214151", + "11223241", "11232331", "11241421", "12114241", "12123331", "12132421", + "12141511", "11214331", "11223421", "11232511", "11151151", "11133151", + "11142241", "11151331", "11115151", "11124241", + /* Column 20 */ + "11133331", "11142421", "11151511", "11111254", "31111252", "21111343", + "41111341", "11111434", "31111432", "21111523", "41111521", "11111614", + "31111612", "31211161", "12111253", "32111251", "21211252", "22111342", + "11211343", "31211341", "12111433", "32111431", "21211432", "22111522", + "11211523", "31211521", "12111613", "32111611", "21211612", "12211162", + "21311161", "13111252", "22211251", "11311252", "23111341", "12211342", + "21311341", "13111432", "22211431", "11311432", "23111521", "12211522", + "21311521", "13111612", "22211611", "11311612", "13211161", "11411161", + "14111251", "12311251", "13211341", "11411341", "14111431", "12311431", + "13211521", "11411521", "14111611", "12311611", + /* Column 21 */ + "21121162", "11112163", "31112161", "11121253", "31121251", "21112252", + "21121342", "11112343", "31112341", "11121433", "31121431", "21112432", + "21121522", "11112523", "31112521", "11121613", "31121611", "22121161", + "11221162", "12112162", "12121252", "21221251", "22112251", "11212252", + "22121341", "11221342", "12112342", "12121432", "21221431", "22112431", + "11212432", "22121521", "11221522", "12112522", "12121612", "21221611", + "12221161", "13112161", "13121251", "11312161", "11321251", "32121115", + "52121113", "21221116", "41221114", "61221112", "22112116", "42112114", + "31212115", "51212113", "13121116", "33121114", "22221115", "42221113", + "11321116", "31321114", "51321112", "23112115", + /* Column 22 */ + "43112113", "12212116", "32212114", "52212112", "21312115", "41312113", + "61312111", "14121115", "34121113", "23221114", "43221112", "12321115", + "32321113", "52321111", "21421114", "41421112", "24112114", "13212115", + "33212113", "22312114", "42312112", "11412115", "31412113", "51412111", + "15121114", "24221113", "13321114", "33321112", "22421113", "42421111", + "11521114", "31521112", "25112113", "14212114", "34212112", "23312113", + "43312111", "12412114", "32412112", "21512113", "41512111", "16121113", + "25221112", "14321113", "34321111", "23421112", "12521113", "32521111", + "15212113", "24312112", "13412113", "33412111", "22512112", "11612113", + "31612111", "31131115", "51131113", "21122116", + /* Column 23 */ + "41122114", "61122112", "31113115", "51113113", "12131116", "32131114", + "52131112", "21231115", "41231113", "61231111", "22122115", "42122113", + "11222116", "31222114", "51222112", "12113116", "32113114", "52113112", + "21213115", "41213113", "61213111", "13131115", "33131113", "22231114", + "42231112", "11331115", "31331113", "51331111", "23122114", "43122112", + "12222115", "32222113", "52222111", "21322114", "41322112", "13113115", + "33113113", "22213114", "42213112", "11313115", "31313113", "51313111", + "14131114", "34131112", "23231113", "43231111", "12331114", "32331112", + "21431113", "41431111", "24122113", "13222114", "33222112", "22322113", + "42322111", "11422114", "31422112", "14113114", + /* Column 24 */ + "34113112", "23213113", "43213111", "12313114", "32313112", "21413113", + "41413111", "15131113", "24231112", "13331113", "33331111", "22431112", + "25122112", "14222113", "34222111", "23322112", "12422113", "32422111", + "21522112", "15113113", "24213112", "13313113", "33313111", "22413112", + "11513113", "31513111", "16131112", "25231111", "14331112", "23431111", + "15222112", "24322111", "13422112", "22522111", "16113112", "25213111", + "14313112", "23413111", "12513112", "21613111", "11141116", "31141114", + "51141112", "21132115", "41132113", "61132111", "11123116", "31123114", + "51123112", "21114115", "41114113", "61114111", "12141115", "32141113", + "52141111", "21241114", "41241112", "22132114", + /* Column 25 */ + "42132112", "11232115", "31232113", "51232111", "12123115", "32123113", + "52123111", "21223114", "41223112", "22114114", "42114112", "11214115", + "31214113", "51214111", "13141114", "33141112", "22241113", "42241111", + "11341114", "31341112", "23132113", "43132111", "12232114", "32232112", + "21332113", "41332111", "13123114", "33123112", "22223113", "42223111", + "11323114", "31323112", "23114113", "43114111", "12214114", "32214112", + "21314113", "41314111", "14141113", "34141111", "23241112", "12341113", + "32341111", "24132112", "13232113", "33232111", "22332112", "11432113", + "31432111", "14123113", "34123111", "23223112", "12323113", "32323111", + "21423112", "24114112", "13214113", "33214111", + /* Column 26 */ + "22314112", "11414113", "31414111", "15141112", "24241111", "13341112", + "25132111", "14232112", "23332111", "12432112", "15123112", "24223111", + "13323112", "22423111", "11523112", "25114111", "14214112", "23314111", + "12414112", "21514111", "16141111", "14341111", "15232111", "13432111", + "16123111", "14323111", "12523111", "15214111", "13414111", "11614111", + "11151115", "31151113", "51151111", "21142114", "41142112", "11133115", + "31133113", "51133111", "21124114", "41124112", "11115115", "31115113", + "51115111", "12151114", "32151112", "21251113", "41251111", "22142113", + "42142111", "11242114", "31242112", "12133114", "32133112", "21233113", + "41233111", "22124113", "42124111", "11224114", + /* Column 27 */ + "31224112", "12115114", "32115112", "21215113", "41215111", "13151113", + "33151111", "22251112", "23142112", "12242113", "32242111", "21342112", + "13133113", "33133111", "22233112", "11333113", "31333111", "23124112", + "12224113", "32224111", "21324112", "13115113", "33115111", "22215112", + "11315113", "31315111", "14151112", "23251111", "24142111", "13242112", + "22342111", "14133112", "23233111", "12333112", "21433111", "24124111", + "13224112", "22324111", "11424112", "14115112", "23215111", "12315112", + "21415111", "15151111", "14242111", "15133111", "13333111", "14224111", + "12424111", "15115111", "13315111", "11515111", "11161114", "31161112", + "21152113", "41152111", "11143114", "31143112", + /* Column 28 */ + "21134113", "41134111", "11125114", "31125112", "21116113", "41116111", + "12161113", "32161111", "22152112", "11252113", "31252111", "12143113", + "32143111", "21243112", "22134112", "11234113", "31234111", "12125113", + "32125111", "21225112", "22116112", "11216113", "31216111", "13161112", + "23152111", "12252112", "13143112", "22243111", "11343112", "23134111", + "12234112", "21334111", "13125112", "22225111", "11325112", "23116111", + "12216112", "21316111", "14161111", "13252111", "14143111", "12343111", + "13234111", "11434111", "14125111", "12325111", "13216111", "11416111", + "31111216", "51111214", "31211125", "51211123", "32111215", "52111213", + "21211216", "41211214", "61211212", "12211126", + /* Column 29 */ + "32211124", "52211122", "21311125", "41311123", "61311121", "13111216", + "33111214", "22211215", "42211213", "11311216", "31311214", "51311212", + "13211125", "33211123", "22311124", "42311122", "11411125", "31411123", + "51411121", "14111215", "34111213", "23211214", "43211212", "12311215", + "32311213", "52311211", "21411214", "41411212", "14211124", "34211122", + "23311123", "43311121", "12411124", "32411122", "21511123", "41511121", + "15111214", "24211213", "13311214", "33311212", "22411213", "42411211", + "11511214", "31511212", "15211123", "24311122", "13411123", "33411121", + "22511122", "11611123", "31611121", "16111213", "25211212", "14311213", + "34311211", "23411212", "12511213", "32511211", + /* Column 30 */ + "21611212", "21121126", "41121124", "61121122", "31112125", "51112123", + "31121215", "51121213", "21112216", "41112214", "61112212", "22121125", + "42121123", "11221126", "31221124", "51221122", "12112126", "32112124", + "52112122", "12121216", "32121214", "52121212", "21221215", "41221213", + "61221211", "22112215", "42112213", "11212216", "31212214", "51212212", + "23121124", "43121122", "12221125", "32221123", "52221121", "21321124", + "41321122", "13112125", "33112123", "13121215", "33121213", "11312125", + "22221214", "42221212", "11321215", "31321213", "51321211", "23112214", + "43112212", "12212215", "32212213", "52212211", "21312214", "41312212", + "24121123", "13221124", "33221122", "22321123", + /* Column 31 */ + "42321121", "11421124", "31421122", "14112124", "34112122", "14121214", + "34121212", "12312124", "23221213", "43221211", "12321214", "32321212", + "21421213", "41421211", "24112213", "13212214", "33212212", "22312213", + "42312211", "11412214", "31412212", "25121122", "14221123", "34221121", + "23321122", "12421123", "32421121", "21521122", "15112123", "15121213", + "13312123", "24221212", "13321213", "33321211", "11512123", "22421212", + "11521213", "31521211", "25112212", "14212213", "34212211", "23312212", + "12412213", "32412211", "21512212", "15221122", "24321121", "13421122", + "22521121", "16112122", "16121212", "14312122", "25221211", "14321212", + "12512122", "23421211", "12521212", "15212212", + /* Column 32 */ + "24312211", "13412212", "22512211", "11612212", "21131125", "41131123", + "61131121", "11122126", "31122124", "51122122", "11131216", "31131214", + "51131212", "21113125", "41113123", "61113121", "21122215", "41122213", + "61122211", "11113216", "31113214", "51113212", "22131124", "42131122", + "11231125", "31231123", "51231121", "12122125", "32122123", "52122121", + "12131215", "32131213", "52131211", "21231214", "41231212", "22113124", + "42113122", "11213125", "22122214", "42122212", "11222215", "31222213", + "51222211", "12113215", "32113213", "52113211", "21213214", "41213212", + "23131123", "43131121", "12231124", "32231122", "21331123", "41331121", + "13122124", "33122122", "13131214", "33131212", + /* Column 33 */ + "11322124", "22231213", "42231211", "11331214", "31331212", "23113123", + "43113121", "12213124", "23122213", "43122211", "12222214", "32222212", + "21322213", "41322211", "13113214", "33113212", "22213213", "42213211", + "11313214", "31313212", "24131122", "13231123", "33231121", "22331122", + "11431123", "31431121", "14122123", "34122121", "14131213", "34131211", + "12322123", "23231212", "12331213", "32331211", "21431212", "24113122", + "13213123", "24122212", "13222213", "33222211", "11413123", "22322212", + "11422213", "31422211", "14113213", "34113211", "23213212", "12313213", + "32313211", "21413212", "25131121", "14231122", "23331121", "12431122", + "15122122", "15131212", "13322122", "24231211", + /* Column 34 */ + "13331212", "11522122", "22431211", "25113121", "14213122", "25122211", + "14222212", "12413122", "23322211", "12422212", "21522211", "15113212", + "24213211", "13313212", "22413211", "11513212", "15231121", "13431121", + "16122121", "16131211", "14322121", "14331211", "12522121", "15213121", + "15222211", "13413121", "13422211", "11613121", "16113211", "14313211", + "12513211", "21141124", "41141122", "11132125", "31132123", "51132121", + "11141215", "31141213", "51141211", "21123124", "41123122", "21132214", + "41132212", "11114125", "31114123", "51114121", "11123215", "31123213", + "51123211", "21114214", "41114212", "22141123", "42141121", "11241124", + "31241122", "12132124", "32132122", "12141214", + /* Column 35 */ + "32141212", "21241213", "41241211", "22123123", "42123121", "11223124", + "22132213", "42132211", "11232214", "31232212", "12114124", "32114122", + "12123214", "32123212", "21223213", "41223211", "22114213", "42114211", + "11214214", "31214212", "23141122", "12241123", "32241121", "21341122", + "13132123", "33132121", "13141213", "33141211", "11332123", "22241212", + "11341213", "31341211", "23123122", "12223123", "23132212", "12232213", + "32232211", "21332212", "13114123", "33114121", "13123213", "33123211", + "11314123", "22223212", "11323213", "31323211", "23114212", "12214213", + "32214211", "21314212", "24141121", "13241122", "22341121", "14132122", + "14141212", "12332122", "23241211", "12341212", + /* Column 36 */ + "24123121", "13223122", "24132211", "13232212", "11423122", "22332211", + "11432212", "14114122", "14123212", "12314122", "23223211", "12323212", + "21423211", "24114211", "13214212", "22314211", "11414212", "14241121", + "15132121", "15141211", "13332121", "13341211", "14223121", "14232211", + "12423121", "12432211", "15114121", "15123211", "13314121", "13323211", + "11514121", "11523211", "14214211", "12414211", "21151123", "41151121", + "11142124", "31142122", "11151214", "31151212", "21133123", "41133121", + "21142213", "41142211", "11124124", "31124122", "11133214", "31133212", + "21115123", "41115121", "21124213", "41124211", "11115214", "31115212", + "22151122", "11251123", "31251121", "12142123", + /* Column 37 */ + "32142121", "12151213", "32151211", "21251212", "22133122", "11233123", + "22142212", "11242213", "31242211", "12124123", "32124121", "12133213", + "32133211", "21233212", "22115122", "11215123", "22124212", "11224213", + "31224211", "12115213", "32115211", "21215212", "23151121", "12251122", + "13142122", "13151212", "11342122", "22251211", "23133121", "12233122", + "23142211", "12242212", "21342211", "13124122", "13133212", "11324122", + "22233211", "11333212", "23115121", "12215122", "23124211", "12224212", + "21324211", "13115212", "22215211", "11315212", "13251121", "14142121", + "14151211", "12342121", "13233121", "13242211", "11433121", "14124121", + "14133211", "12324121", "12333211", "13215121", + /* Column 38 */ + "13224211", "11415121", "11424211", "14115211", "12315211", "21161122", + "11152123", "31152121", "11161213", "31161211", "21143122", "21152212", + "11134123", "31134121", "11143213", "31143211", "21125122", "21134212", + "11116123", "31116121", "11125213", "31125211", "22161121", "12152122", + "12161212", "22143121", "11243122", "22152211", "11252212", "12134122", + "12143212", "21243211", "22125121", "11225122", "22134211", "11234212", + "12116122", "12125212", "21225211", "13152121", "13161211", "12243121", + "12252211", "13134121", "13143211", "11334121", "11343211", "12225121", + "12234211", "13116121", "13125211", "11316121", "11325211", "21111226", + "41111224", "61111222", "31111315", "51111313", + /* Column 39 */ + "21211135", "41211133", "61211131", "22111225", "42111223", "11211226", + "31211224", "51211222", "12111316", "32111314", "52111312", "21211315", + "41211313", "61211311", "22211134", "42211132", "11311135", "31311133", + "51311131", "23111224", "43111222", "12211225", "32211223", "52211221", + "21311224", "41311222", "13111315", "33111313", "22211314", "42211312", + "11311315", "31311313", "51311311", "23211133", "43211131", "12311134", + "32311132", "21411133", "41411131", "24111223", "13211224", "33211222", + "22311223", "42311221", "11411224", "31411222", "14111314", "34111312", + "23211313", "43211311", "12311314", "32311312", "21411313", "41411311", + "24211132", "13311133", "33311131", "22411132", + /* Column 40 */ + "11511133", "31511131", "25111222", "14211223", "34211221", "23311222", + "12411223", "32411221", "21511222", "15111313", "24211312", "13311313", + "33311311", "22411312", "11511313", "31511311", "25211131", "14311132", + "23411131", "12511132", "21611131", "15211222", "24311221", "13411222", + "22511221", "11611222", "16111312", "25211311", "14311312", "23411311", + "12511312", "21611311", "31121134", "51121132", "21112135", "41112133", + "61112131", "21121225", "41121223", "61121221", "11112226", "31112224", + "51112222", "11121316", "31121314", "51121312", "21112315", "41112313", + "61112311", "12121135", "32121133", "52121131", "21221134", "41221132", + "22112134", "42112132", "11212135", "22121224", + /* Column 41 */ + "42121222", "11221225", "31221223", "51221221", "12112225", "32112223", + "52112221", "12121315", "32121313", "52121311", "21221314", "41221312", + "22112314", "42112312", "11212315", "31212313", "51212311", "13121134", + "33121132", "22221133", "42221131", "11321134", "31321132", "23112133", + "43112131", "12212134", "23121223", "43121221", "12221224", "32221222", + "21321223", "41321221", "13112224", "33112222", "13121314", "33121312", + "11312224", "22221313", "42221311", "11321314", "31321312", + /* Column 42 */ + "23112313", "43112311", "12212314", "32212312", "21312313", "41312311", + "14121133", "34121131", "23221132", "12321133", "32321131", "21421132", + "24112132", "13212133", "24121222", "13221223", "33221221", "11412133", + "22321222", "11421223", "31421221", "14112223", "34112221", "14121313", + "34121311", "12312223", "23221312", "12321313", "32321311", "21421312", + "24112312", "13212313", "33212311", "22312312", "11412313", "31412311", + "15121132", "24221131", "13321132", "22421131" + }; + + private static final String[] C49_APPXE_ODD = { + /* Appendix E - Code 49 Encodation Patterns (Odd Symbol Character Parity) */ + /* Column 1 */ + "22121116", "42121114", "31221115", "51221113", "32112115", "52112113", + "21212116", "41212114", "61212112", "23121115", "43121113", "12221116", + "32221114", "52221112", "21321115", "41321113", "61321111", "13112116", + "33112114", "22212115", "42212113", "11312116", "31312114", "51312112", + "24121114", "13221115", "33221113", "22321114", "42321112", "11421115", + "31421113", "51421111", "14112115", "34112113", "23212114", "43212112", + "12312115", "32312113", "52312111", "21412114", "41412112", "25121113", + "14221114", "34221112", "23321113", "43321111", "12421114", "32421112", + "21521113", "41521111", "15112114", "24212113", "13312114", "33312112", + "22412113", "42412111", "11512114", "31512112", + /* Column 2 */ + "15221113", "24321112", "13421113", "33421111", "22521112", "16112113", + "25212112", "14312113", "34312111", "23412112", "12512113", "32512111", + "21612112", "21131116", "41131114", "61131112", "31122115", "51122113", + "21113116", "41113114", "61113112", "22131115", "42131113", "11231116", + "31231114", "51231112", "12122116", "32122114", "52122112", "21222115", + "41222113", "61222111", "22113115", "42113113", "11213116", "31213114", + "51213112", "23131114", "43131112", "12231115", "32231113", "52231111", + "21331114", "41331112", "13122115", "33122113", "22222114", "42222112", + "11322115", "31322113", "51322111", "23113114", "43113112", "12213115", + "32213113", "52213111", "21313114", "41313112", + /* Column 3 */ + "24131113", "13231114", "33231112", "22331113", "42331111", "11431114", + "31431112", "14122114", "34122112", "23222113", "43222111", "12322114", + "32322112", "21422113", "41422111", "24113113", "13213114", "33213112", + "22313113", "42313111", "11413114", "31413112", "25131112", "14231113", + "34231111", "23331112", "12431113", "32431111", "15122113", "24222112", + "13322113", "33322111", "22422112", "11522113", "31522111", "25113112", + "14213113", "34213111", "23313112", "12413113", "32413111", "21513112", + "15231112", "24331111", "13431112", "16122112", "25222111", "14322112", + "23422111", "12522112", "15213112", "24313111", "13413112", "22513111", + "11613112", "21141115", "41141113", "61141111", + /* Column 4 */ + "11132116", "31132114", "51132112", "21123115", "41123113", "61123111", + "11114116", "31114114", "51114112", "22141114", "42141112", "11241115", + "31241113", "51241111", "12132115", "32132113", "52132111", "21232114", + "41232112", "22123114", "42123112", "11223115", "31223113", "51223111", + "12114115", "32114113", "52114111", "21214114", "41214112", "23141113", + "43141111", "12241114", "32241112", "21341113", "41341111", "13132114", + "33132112", "22232113", "42232111", "11332114", "31332112", "23123113", + "43123111", "12223114", "32223112", "21323113", "41323111", "13114114", + "33114112", "22214113", "42214111", "11314114", "31314112", "24141112", + "13241113", "33241111", "22341112", "14132113", + /* Column 5 */ + "34132111", "23232112", "12332113", "32332111", "21432112", "24123112", + "13223113", "33223111", "22323112", "11423113", "31423111", "14114113", + "34114111", "23214112", "12314113", "32314111", "21414112", "25141111", + "14241112", "23341111", "15132112", "24232111", "13332112", "22432111", + "25123111", "14223112", "23323111", "12423112", "21523111", "15114112", + "24214111", "13314112", "22414111", "11514112", "15241111", "16132111", + "14332111", "15223111", "13423111", "16114111", "14314111", "12514111", + "21151114", "41151112", "11142115", "31142113", "51142111", "21133114", + "41133112", "11124115", "31124113", "51124111", "21115114", "41115112", + "22151113", "42151111", "11251114", "31251112", + /* Column 6 */ + "12142114", "32142112", "21242113", "41242111", "22133113", "42133111", + "11233114", "31233112", "12124114", "32124112", "21224113", "41224111", + "22115113", "42115111", "11215114", "31215112", "23151112", "12251113", + "32251111", "13142113", "33142111", "22242112", "11342113", "31342111", + "23133112", "12233113", "32233111", "21333112", "13124113", "33124111", + "22224112", "11324113", "31324111", "23115112", "12215113", "32215111", + "21315112", "24151111", "13251112", "14142112", "23242111", "12342112", + "24133111", "13233112", "22333111", "11433112", "14124112", "23224111", + "12324112", "21424111", "24115111", "13215112", "22315111", "11415112", + "14251111", "15142111", "13342111", "14233111", + /* Column 7 */ + "12433111", "15124111", "13324111", "11524111", "14215111", "12415111", + "21161113", "41161111", "11152114", "31152112", "21143113", "41143111", + "11134114", "31134112", "21125113", "41125111", "11116114", "31116112", + "22161112", "12152113", "32152111", "21252112", "22143112", "11243113", + "31243111", "12134113", "32134111", "21234112", "22125112", "11225113", + "31225111", "12116113", "32116111", "21216112", "23161111", "13152112", + "22252111", "23143111", "12243112", "21343111", "13134112", "22234111", + "11334112", "23125111", "12225112", "21325111", "13116112", "22216111", + "11316112", "14152111", "13243111", "14134111", "12334111", "13225111", + "11425111", "14116111", "12316111", "41111215", + /* Column 8 */ + "61111213", "21211126", "41211124", "61211122", "22111216", "42111214", + "31211215", "51211213", "22211125", "42211123", "11311126", "31311124", + "51311122", "23111215", "43111213", "12211216", "32211214", "52211212", + "21311215", "41311213", "61311211", "23211124", "43211122", "12311125", + "32311123", "52311121", "21411124", "41411122", "24111214", "13211215", + "33211213", "22311214", "42311212", "11411215", "31411213", "51411211", + "24211123", "13311124", "33311122", "22411123", "42411121", "11511124", + "31511122", "25111213", "14211214", "34211212", "23311213", "43311211", + "12411214", "32411212", "21511213", "41511211", "25211122", "14311123", + "34311121", "23411122", "12511123", "32511121", + /* Column 9 */ + "21611122", "15211213", "24311212", "13411213", "33411211", "22511212", + "11611213", "31611211", "31121125", "51121123", "21112126", "41112124", + "61112122", "21121216", "41121214", "61121212", "31112215", "51112213", + "12121126", "32121124", "52121122", "21221125", "41221123", "61221121", + "22112125", "42112123", "11212126", "22121215", "42121213", "11221216", + "31221214", "51221212", "12112216", "32112214", "52112212", "21212215", + "41212213", "61212211", "13121125", "33121123", "22221124", "42221122", + "11321125", "31321123", "51321121", "23112124", "43112122", "12212125", + "23121214", "43121212", "12221215", "32221213", "52221211", "21321214", + "41321212", "13112215", "33112213", "22212214", + /* Column 10 */ + "42212212", "11312215", "31312213", "51312211", "14121124", "34121122", + "23221123", "43221121", "12321124", "32321122", "21421123", "41421121", + "24112123", "13212124", "24121213", "13221214", "33221212", "11412124", + "22321213", "42321211", "11421214", "31421212", "14112214", "34112212", + "23212213", "43212211", "12312214", "32312212", "21412213", "41412211", + "15121123", "24221122", "13321123", "33321121", "22421122", "11521123", + "31521121", "25112122", "14212123", "25121212", "14221213", "34221211", + "12412123", "23321212", "12421213", "32421211", "21521212", "15112213", + "24212212", "13312213", "33312211", "22412212", "11512213", "31512211", + "16121122", "25221121", "14321122", "23421121", + /* Column 11 */ + "12521122", "15212122", "15221212", "13412122", "24321211", "13421212", + "11612122", "22521211", "16112212", "25212211", "14312212", "23412211", + "12512212", "21612211", "11131126", "31131124", "51131122", "21122125", + "41122123", "61122121", "21131215", "41131213", "61131211", "11113126", + "31113124", "51113122", "11122216", "31122214", "51122212", "21113215", + "41113213", "61113211", "12131125", "32131123", "52131121", "21231124", + "41231122", "22122124", "42122122", "11222125", "22131214", "42131212", + "11231215", "31231213", "51231211", "12113125", "32113123", "52113121", + "12122215", "32122213", "52122211", "21222214", "41222212", "22113214", + "42113212", "11213215", "31213213", "51213211", + /* Column 12 */ + "13131124", "33131122", "22231123", "42231121", "11331124", "31331122", + "23122123", "43122121", "12222124", "23131213", "43131211", "12231214", + "32231212", "21331213", "41331211", "13113124", "33113122", "13122214", + "33122212", "11313124", "22222213", "42222211", "11322214", "31322212", + "23113213", "43113211", "12213214", "32213212", "21313213", "41313211", + "14131123", "34131121", "23231122", "12331123", "32331121", "21431122", + "24122122", "13222123", "24131212", "13231213", "33231211", "11422123", + "22331212", "11431213", "31431211", "14113123", "34113121", "14122213", + "34122211", "12313123", "23222212", "12322213", "32322211", "21422212", + "24113212", "13213213", "33213211", "22313212", + /* Column 13 */ + "11413213", "31413211", "15131122", "24231121", "13331122", "22431121", + "25122121", "14222122", "25131211", "14231212", "12422122", "23331211", + "12431212", "15113122", "15122212", "13313122", "24222211", "13322212", + "11513122", "22422211", "11522212", "25113211", "14213212", "23313211", + "12413212", "21513211", "16131121", "14331121", "15222121", "15231211", + "13422121", "13431211", "16113121", "16122211", "14313121", "14322211", + "12513121", "12522211", "15213211", "13413211", "11613211", "11141125", + "31141123", "51141121", "21132124", "41132122", "21141214", "41141212", + "11123125", "31123123", "51123121", "11132215", "31132213", "51132211", + "21114124", "41114122", "21123214", "41123212", + /* Column 14 */ + "11114215", "31114213", "51114211", "12141124", "32141122", "21241123", + "41241121", "22132123", "42132121", "11232124", "22141213", "42141211", + "11241214", "31241212", "12123124", "32123122", "12132214", "32132212", + "21232213", "41232211", "22114123", "42114121", "11214124", "22123213", + "42123211", "11223214", "31223212", "12114214", "32114212", "21214213", + "41214211", "13141123", "33141121", "22241122", "11341123", "31341121", + "23132122", "12232123", "23141212", "12241213", "32241211", "21341212", + "13123123", "33123121", "13132213", "33132211", "11323123", "22232212", + "11332213", "31332211", "23114122", "12214123", "23123212", "12223213", + "32223211", "21323212", "13114213", "33114211", + /* Column 15 */ + "22214212", "11314213", "31314211", "14141122", "23241121", "12341122", + "24132121", "13232122", "24141211", "13241212", "11432122", "22341211", + "14123122", "14132212", "12323122", "23232211", "12332212", "21432211", + "24114121", "13214122", "24123211", "13223212", "11414122", "22323211", + "11423212", "14114212", "23214211", "12314212", "21414211", "15141121", + "13341121", "14232121", "14241211", "12432121", "15123121", "15132211", + "13323121", "13332211", "11523121", "14214121", "14223211", "12414121", + "12423211", "15114211", "13314211", "11514211", "11151124", "31151122", + "21142123", "41142121", "21151213", "41151211", "11133124", "31133122", + "11142214", "31142212", "21124123", "41124121", + /* Column 16 */ + "21133213", "41133211", "11115124", "31115122", "11124214", "31124212", + "21115213", "41115211", "12151123", "32151121", "21251122", "22142122", + "11242123", "22151212", "11251213", "31251211", "12133123", "32133121", + "12142213", "32142211", "21242212", "22124122", "11224123", "22133212", + "11233213", "31233211", "12115123", "32115121", "12124213", "32124211", + "21224212", "22115212", "11215213", "31215211", "13151122", "22251121", + "23142121", "12242122", "23151211", "12251212", "13133122", "13142212", + "11333122", "22242211", "11342212", "23124121", "12224122", "23133211", + "12233212", "21333211", "13115122", "13124212", "11315122", "22224211", + "11324212", "23115211", "12215212", "21315211", + /* Column 17 */ + "14151121", "13242121", "13251211", "14133121", "14142211", "12333121", + "12342211", "13224121", "13233211", "11424121", "11433211", "14115121", + "14124211", "12315121", "12324211", "13215211", "11415211", "11161123", + "31161121", "21152122", "21161212", "11143123", "31143121", "11152213", + "31152211", "21134122", "21143212", "11125123", "31125121", "11134213", + "31134211", "21116122", "21125212", "12161122", "22152121", "11252122", + "22161211", "12143122", "12152212", "21252211", "22134121", "11234122", + "22143211", "11243212", "12125122", "12134212", "21234211", "22116121", + "11216122", "22125211", "11225212", "13161121", "12252121", "13143121", + "13152211", "11343121", "12234121", "12243211", + /* Column 18 */ + "13125121", "13134211", "11325121", "11334211", "12216121", "12225211", + "31111225", "51111223", "21111316", "41111314", "61111312", "31211134", + "51211132", "12111226", "32111224", "52111222", "21211225", "41211223", + "61211221", "22111315", "42111313", "11211316", "31211314", "51211312", + "12211135", "32211133", "52211131", "21311134", "41311132", "13111225", + "33111223", "22211224", "42211222", "11311225", "31311223", "51311221", + "23111314", "43111312", "12211315", "32211313", "52211311", "21311314", + "41311312", "13211134", "33211132", "22311133", "42311131", "11411134", + "31411132", "14111224", "34111222", "23211223", "43211221", "12311224", + "32311222", "21411223", "41411221", "24111313", + /* Column 19 */ + "13211314", "33211312", "22311313", "42311311", "11411314", "31411312", + "14211133", "34211131", "23311132", "12411133", "32411131", "21511132", + "15111223", "24211222", "13311223", "33311221", "22411222", "11511223", + "31511221", "25111312", "14211313", "34211311", "23311312", "12411313", + "32411311", "21511312", "15211132", "24311131", "13411132", "22511131", + "11611132", "16111222", "25211221", "14311222", "23411221", "12511222", + "21611221", "15211312", "24311311", "13411312", "22511311", "11611312", + "21121135", "41121133", "61121131", "11112136", "31112134", "51112132", + "11121226", "31121224", "51121222", "21112225", "41112223", "61112221", + "21121315", "41121313", "61121311", "11112316", + /* Column 20 */ + "31112314", "51112312", "22121134", "42121132", "11221135", "31221133", + "51221131", "12112135", "32112133", "52112131", "12121225", "32121223", + "52121221", "21221224", "41221222", "22112224", "42112222", "11212225", + "22121314", "42121312", "11221315", "31221313", "51221311", "12112315", + "32112313", "52112311", "21212314", "41212312", "23121133", "43121131", + "12221134", "32221132", "21321133", "41321131", "13112134", "33112132", + "13121224", "33121222", "11312134", "22221223", "42221221", "11321224", + "31321222", "23112223", "43112221", "12212224", "23121313", "43121311", + "12221314", "32221312", "21321313", "41321311", "13112314", "33112312", + "22212313", "42212311", "11312314", "31312312", + /* Column 21 */ + "24121132", "13221133", "33221131", "22321132", "11421133", "31421131", + "14112133", "34112131", "14121223", "34121221", "12312133", "23221222", + "12321223", "32321221", "21421222", "24112222", "13212223", "24121312", + "13221313", "33221311", "11412223", "22321312", "11421313", "31421311", + "14112313", "34112311", "23212312", "12312313", "32312311", "21412312", + "25121131", "14221132", "23321131", "12421132", "21521131", "15112132", + "15121222", "13312132", "24221221", "13321222", "11512132", "22421221", + "11521222", "25112221", "14212222", "25121311", "14221312", "12412222", + "23321311", "12421312", "21521311", "15112312", "24212311", "13312312", + "22412311", "11512312", "15221131", "13421131", + /* Column 22 */ + "16112131", "16121221", "14312131", "14321221", "12512131", "12521221", + "15212221", "15221311", "13412221", "13421311", "11612221", "16112311", + "14312311", "12512311", "21131134", "41131132", "11122135", "31122133", + "51122131", "11131225", "31131223", "51131221", "21113134", "41113132", + "21122224", "41122222", "21131314", "41131312", "11113225", "31113223", + "51113221", "11122315", "31122313", "51122311", "21113314", "41113312", + "22131133", "42131131", "11231134", "31231132", "12122134", "32122132", + "12131224", "32131222", "21231223", "41231221", "22113133", "42113131", + "11213134", "22122223", "42122221", "11222224", "22131313", "42131311", + "11231314", "31231312", "12113224", "32113222", + /* Column 23 */ + "12122314", "32122312", "21222313", "41222311", "22113313", "42113311", + "11213314", "31213312", "23131132", "12231133", "32231131", "21331132", + "13122133", "33122131", "13131223", "33131221", "11322133", "22231222", + "11331223", "31331221", "23113132", "12213133", "23122222", "12222223", + "23131312", "12231313", "32231311", "21331312", "13113223", "33113221", + "13122313", "33122311", "11313223", "22222312", "11322313", "31322311", + "23113312", "12213313", "32213311", "21313312", "24131131", "13231132", + "22331131", "11431132", "14122132", "14131222", "12322132", "23231221", + "12331222", "21431221", "24113131", "13213132", "24122221", "13222222", + "24131311", "11413132", "13231312", "11422222", + /* Column 24 */ + "22331311", "11431312", "14113222", "14122312", "12313222", "23222311", + "12322312", "21422311", "24113311", "13213312", "22313311", "11413312", + "14231131", "12431131", "15122131", "15131221", "13322131", "13331221", + "11522131", "14213131", "14222221", "12413131", "14231311", "12422221", + "12431311", "15113221", "15122311", "13313221", "13322311", "11513221", + "11522311", "14213311", "12413311", "21141133", "41141131", "11132134", + "31132132", "11141224", "31141222", "21123133", "41123131", "21132223", + "41132221", "21141313", "41141311", "11114134", "31114132", "11123224", + "31123222", "11132314", "31132312", "21114223", "41114221", "21123313", + "41123311", "11114314", "31114312", "22141132", + /* Column 25 */ + "11241133", "31241131", "12132133", "32132131", "12141223", "32141221", + "21241222", "22123132", "11223133", "22132222", "11232223", "22141312", + "11241313", "31241311", "12114133", "32114131", "12123223", "32123221", + "12132313", "32132311", "21232312", "22114222", "11214223", "22123312", + "11223313", "31223311", "12114313", "32114311", "21214312", "23141131", + "12241132", "21341131", "13132132", "13141222", "11332132", "22241221", + "11341222", "23123131", "12223132", "23132221", "12232222", "23141311", + "12241312", "21341311", "13114132", "13123222", "11314132", "13132312", + "11323222", "22232311", "11332312", "23114221", "12214222", "23123311", + "12223312", "21323311", "13114312", "22214311", + /* Column 26 */ + "11314312", "13241131", "14132131", "14141221", "12332131", "12341221", + "13223131", "13232221", "11423131", "13241311", "11432221", "14114131", + "14123221", "12314131", "14132311", "12323221", "12332311", "13214221", + "13223311", "11414221", "11423311", "14114311", "12314311", "21151132", + "11142133", "31142131", "11151223", "31151221", "21133132", "21142222", + "21151312", "11124133", "31124131", "11133223", "31133221", "11142313", + "31142311", "21115132", "21124222", "21133312", "11115223", "31115221", + "11124313", "31124311", "22151131", "11251132", "12142132", "12151222", + "21251221", "22133131", "11233132", "22142221", "11242222", "22151311", + "11251312", "12124132", "12133222", "12142312", + /* Column 27 */ + "21242311", "22115131", "11215132", "22124221", "11224222", "22133311", + "11233312", "12115222", "12124312", "21224311", "12251131", "13142131", + "13151221", "11342131", "12233131", "12242221", "12251311", "13124131", + "13133221", "11324131", "13142311", "11333221", "11342311", "12215131", + "12224221", "12233311", "13115221", "13124311", "11315221", "11324311", + "21161131", "11152132", "11161222", "21143131", "21152221", "21161311", + "11134132", "11143222", "11152312", "21125131", "21134221", "21143311", + "11116132", "11125222", "11134312", "12152131", "12161221", "11243131", + "11252221", "12134131", "12143221", "12152311", "11225131", "11234221", + "11243311", "12116131", "12125221", "12134311", + /* Column 28 */ + "21111235", "41111233", "61111231", "11111326", "31111324", "51111322", + "21111415", "41111413", "61111411", "21211144", "41211142", "22111234", + "42111232", "11211235", "31211233", "51211231", "12111325", "32111323", + "52111321", "21211324", "41211322", "22111414", "42111412", "11211415", + "31211413", "51211411", "22211143", "42211141", "11311144", "31311142", + "23111233", "43111231", "12211234", "32211232", "21311233", "41311231", + "13111324", "33111322", "22211323", "42211321", "11311324", "31311322", + "23111413", "43111411", "12211414", "32211412", "21311413", "41311411", + "23211142", "12311143", "32311141", "21411142", "24111232", "13211233", + "33211231", "22311232", "11411233", "31411231", + /* Column 29 */ + "14111323", "34111321", "23211322", "12311323", "32311321", "21411322", + "24111412", "13211413", "33211411", "22311412", "11411413", "31411411", + "24211141", "13311142", "22411141", "11511142", "25111231", "14211232", + "23311231", "12411232", "21511231", "15111322", "24211321", "13311322", + "22411321", "11511322", "25111411", "14211412", "23311411", "12411412", + "21511411", "14311141", "12511141", "15211231", "13411231", "11611231", + "16111321", "14311321", "12511321", "15211411", "13411411", "11611411", + "31121143", "51121141", "21112144", "41112142", "21121234", "41121232", + "11112235", "31112233", "51112231", "11121325", "31121323", "51121321", + "21112324", "41112322", "21121414", "41121412", + /* Column 30 */ + "11112415", "31112413", "51112411", "12121144", "32121142", "21221143", + "41221141", "22112143", "42112141", "11212144", "22121233", "42121231", + "11221234", "31221232", "12112234", "32112232", "12121324", "32121322", + "21221323", "41221321", "22112323", "42112321", "11212324", "22121413", + "42121411", "11221414", "31221412", "12112414", "32112412", "21212413", + "41212411", "13121143", "33121141", "22221142", "11321143", "31321141", + "23112142", "12212143", "23121232", "12221233", "32221231", "21321232", + "13112233", "33112231", "13121323", "33121321", "11312233", "22221322", + "11321323", "31321321", "23112322", "12212323", "23121412", "12221413", + "32221411", "21321412", "13112413", "33112411", + /* Column 31 */ + "22212412", "11312413", "31312411", "14121142", "23221141", "12321142", + "21421141", "24112141", "13212142", "24121231", "13221232", "11412142", + "22321231", "11421232", "14112232", "14121322", "12312232", "23221321", + "12321322", "21421321", "24112321", "13212322", "24121411", "13221412", + "11412322", "22321411", "11421412", "14112412", "23212411", "12312412", + "21412411", "15121141", "13321141", "11521141", "14212141", "14221231", + "12412141", "12421231", "15112231", "15121321", "13312231", "13321321", + "11512231", "11521321", "14212321", "14221411", "12412321", "12421411", + "15112411", "13312411", "11512411", "11131144", "31131142", "21122143", + "41122141", "21131233", "41131231", "11113144", + /* Column 32 */ + "31113142", "11122234", "31122232", "11131324", "31131322", "21113233", + "41113231", "21122323", "41122321", "21131413", "41131411", "11113324", + "31113322", "11122414", "31122412", "21113413", "41113411", "12131143", + "32131141", "21231142", "22122142", "11222143", "22131232", "11231233", + "31231231", "12113143", "32113141", "12122233", "32122231", "12131323", + "32131321", "21231322", "22113232", "11213233", "22122322", "11222323", + "22131412", "11231413", "31231411", "12113323", "32113321", "12122413", + "32122411", "21222412", "22113412", "11213413", "31213411", "13131142", + "22231141", "11331142", "23122141", "12222142", "23131231", "12231232", + "21331231", "13113142", "13122232", "11313142", + /* Column 33 */ + "13131322", "11322232", "22231321", "11331322", "23113231", "12213232", + "23122321", "12222322", "23131411", "12231412", "21331411", "13113322", + "13122412", "11313322", "22222411", "11322412", "23113411", "12213412", + "21313411", "14131141", "12331141", "13222141", "13231231", "11422141", + "11431231", "14113141", "14122231", "12313141", "14131321", "12322231", + "12331321", "13213231", "13222321", "11413231", "13231411", "11422321", + "11431411", "14113321", "14122411", "12313321", "12322411", "13213411", + "11413411", "11141143", "31141141", "21132142", "21141232", "11123143", + "31123141", "11132233", "31132231", "11141323", "31141321", "21114142", + "21123232", "21132322", "21141412", "11114233", + /* Column 34 */ + "31114231", "11123323", "31123321", "11132413", "31132411", "21114322", + "21123412", "12141142", "21241141", "22132141", "11232142", "22141231", + "11241232", "12123142", "12132232", "12141322", "21241321", "22114141", + "11214142", "22123231", "11223232", "22132321", "11232322", "22141411", + "11241412", "12114232", "12123322", "12132412", "21232411", "22114321", + "11214322", "22123411", "11223412", "13141141", "11341141", "12232141", + "12241231", "13123141", "13132231", "11323141", "13141321", "11332231", + "11341321", "12214141", "12223231", "12232321", "12241411", "13114231", + "13123321", "11314231", "13132411", "11323321", "11332411", "12214321", + "12223411", "11151142", "21142141", "21151231", + /* Column 35 */ + "11133142", "11142232", "11151322", "21124141", "21133231", "21142321", + "21151411", "11115142", "11124232", "11133322", "11142412", "21115231", + "21124321", "21133411", "12151141", "11242141", "11251231", "12133141", + "12142231", "12151321", "11224141", "11233231", "11242321", "11251411", + "12115141", "12124231", "12133321", "12142411", "11215231", "11224321", + "11233411", "11161141", "11143141", "11152231", "11161321", "11125141", + "11134231", "11143321", "11152411", "11111245", "31111243", "51111241", + "21111334", "41111332", "11111425", "31111423", "51111421", "21111514", + "41111512", "31211152", "12111244", "32111242", "21211243", "41211241", + "22111333", "42111331", "11211334", "31211332", + /* Column 36 */ + "12111424", "32111422", "21211423", "41211421", "22111513", "42111511", + "11211514", "31211512", "12211153", "32211151", "21311152", "13111243", + "33111241", "22211242", "11311243", "31311241", "23111332", "12211333", + "32211331", "21311332", "13111423", "33111421", "22211422", "11311423", + "31311421", "23111512", "12211513", "32211511", "21311512", "13211152", + "22311151", "11411152", "14111242", "23211241", "12311242", "21411241", + "24111331", "13211332", "22311331", "11411332", "14111422", "23211421", + "12311422", "21411421", "24111511", "13211512", "22311511", "11411512", + "14211151", "12411151", "15111241", "13311241", "11511241", "14211331", + "12411331", "15111421", "13311421", "11511421", + /* Column 37 */ + "14211511", "12411511", "21121153", "41121151", "11112154", "31112152", + "11121244", "31121242", "21112243", "41112241", "21121333", "41121331", + "11112334", "31112332", "11121424", "31121422", "21112423", "41112421", + "21121513", "41121511", "11112514", "31112512", "22121152", "11221153", + "31221151", "12112153", "32112151", "12121243", "32121241", "21221242", + "22112242", "11212243", "22121332", "11221333", "31221331", "12112333", + "32112331", "12121423", "32121421", "21221422", "22112422", "11212423", + "22121512", "11221513", "31221511", "12112513", "32112511", "21212512", + "23121151", "12221152", "21321151", "13112152", "13121242", "11312152", + "22221241", "11321242", "23112241", "12212242", + /* Column 38 */ + "23121331", "12221332", "21321331", "13112332", "13121422", "11312332", + "22221421", "11321422", "23112421", "12212422", "23121511", "12221512", + "21321511", "13112512", "22212511", "11312512", "13221151", "11421151", + "14112151", "14121241", "12312151", "12321241", "13212241", "13221331", + "11412241", "11421331", "14112331", "14121421", "12312331", "12321421", + "13212421", "13221511", "11412421", "11421511", "14112511", "12312511", + "21131152", "11122153", "31122151", "11131243", "31131241", "21113152", + "21122242", "21131332", "11113243", "31113241", "11122333", "31122331", + "11131423", "31131421", "21113332", "21122422", "21131512", "11113423", + "31113421", "11122513", "31122511", "22131151", + /* Column 39 */ + "11231152", "12122152", "12131242", "21231241", "22113151", "11213152", + "22122241", "11222242", "22131331", "11231332", "12113242", "12122332", + "12131422", "21231421", "22113331", "11213332", "22122421", "11222422", + "22131511", "11231512", "12113422", "12122512", "21222511", "12231151", + "13122151", "13131241", "11322151", "11331241", "12213151", "12222241", + "12231331", "13113241", "13122331", "11313241", "13131421", "11322331", + "11331421", "12213331", "12222421", "12231511", "13113421", "13122511", + "11313421", "11322511", "21141151", "11132152", "11141242", "21123151", + "21132241", "21141331", "11114152", "11123242", "11132332", "11141422", + "21114241", "21123331", "21132421", "21141511", + /* Column 40 */ + "11114332", "11123422", "11132512", "11241151", "12132151", "12141241", + "11223151", "11232241", "11241331", "12114151", "12123241", "12132331", + "12141421", "11214241", "11223331", "11232421", "11241511", "12114331", + "12123421", "12132511", "11142151", "11151241", "11124151", "11133241", + "11142331", "11151421", "11115241", "11124331", "11133421", "11142511", + "21111253", "41111251", "11111344", "31111342", "21111433", "41111431", + "11111524", "31111522", "21111613", "41111611", "21211162", "22111252", + "11211253", "31211251", "12111343", "32111341", "21211342", "22111432", + "11211433", "31211431", "12111523", "32111521", "21211522", "22111612", + "11211613", "31211611", "22211161", "11311162", + /* Column 41 */ + "23111251", "12211252", "21311251", "13111342", "22211341", "11311342", + "23111431", "12211432", "21311431", "13111522", "22211521", "11311522", + "23111611", "12211612", "21311611", "12311161", "13211251", "11411251", + "14111341", "12311341", "13211431", "11411431", "14111521", "12311521", + "13211611", "11411611", "31121161", "21112162", "21121252", "11112253", + "31112251", "11121343", "31121341", "21112342", "21121432", "11112433", + "31112431", "11121523", "31121521", "21112522", "21121612", + /* Column 42 */ + "12121162", "21221161", "22112161", "11212162", "22121251", "11221252", + "12112252", "12121342", "21221341", "22112341", "11212342", "22121431", + "11221432", "12112432", "12121522", "21221521", "22112521", "11212522", + "22121611", "11221612", "13121161", "11321161", "12212161", "12221251", + "13112251", "13121341", "11312251", "11321341", "12212341", "12221431", + "13112431", "13121521", "11312431", "11321521", "12212521", "12221611", + "11131162", "21122161", "21131251", "11113162" + }; + + private static final char[] C49_SET = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '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', '-', '.', ' ', '$', '/', '+', + '%', '!', '&', '*' + }; + + @Override + protected boolean gs1Supported() { + return true; + } + + @Override + protected void encode() { + int codeword_count = 0, pad_count = 0; + int x_count, y_count, z_count, posn_val, local_value; + int[] codewords = new int[170]; + int[][] c_grid = new int[8][8]; + int[][] w_grid = new int[8][4]; + + if (!content.matches("[\u0000-\u007F]+")) { + throw OkapiInputException.invalidCharactersInInput(); + } + + inputData = toBytes(content, StandardCharsets.US_ASCII); + + StringBuilder intermediate = new StringBuilder(inputData.length + 8); + if (inputDataType == DataType.GS1) { + intermediate.append('*'); // FNC1 + } + for (int i = 0; i < inputData.length; i++) { + int c = inputData[i]; + if (c == FNC1) { + intermediate.append('*'); // FNC1 + } else { + intermediate.append(C49_TABLE7[c]); + } + } + + int h = intermediate.length(); + int i = 0; + + do { + if (intermediate.charAt(i) >= '0' && intermediate.charAt(i) <= '9') { + + /* Numeric data */ + int latch = 0; + int j = 0; + do { + if ((i + j) >= h) { + latch = 1; + } else { + if (intermediate.charAt(i + j) >= '0' && intermediate.charAt(i + j) <= '9') { + j++; + } else { + latch = 1; + } + } + } while (latch == 0); + + if (j >= 5) { + + /* Use Numeric Encodation Method */ + int block_count, c; + int block_remain; + int block_value; + + codewords[codeword_count] = 48; /* Numeric Shift */ + codeword_count++; + + block_count = j / 5; + block_remain = j % 5; + + for (c = 0; c < block_count; c++) { + if ((c == block_count - 1) && (block_remain == 2)) { + /* Rule (d) */ + block_value = 100000; + block_value += (intermediate.charAt(i) - '0') * 1000; + block_value += (intermediate.charAt(i + 1) - '0') * 100; + block_value += (intermediate.charAt(i + 2) - '0') * 10; + block_value += intermediate.charAt(i + 3) - '0'; + + codewords[codeword_count] = block_value / (48 * 48); + block_value = block_value - (48 * 48) * codewords[codeword_count]; + codeword_count++; + codewords[codeword_count] = block_value / 48; + block_value = block_value - 48 * codewords[codeword_count]; + codeword_count++; + codewords[codeword_count] = block_value; + codeword_count++; + i += 4; + block_value = (intermediate.charAt(i) - '0') * 100; + block_value += (intermediate.charAt(i + 1) - '0') * 10; + block_value += intermediate.charAt(i + 2) - '0'; + + codewords[codeword_count] = block_value / 48; + block_value = block_value - 48 * codewords[codeword_count]; + codeword_count++; + codewords[codeword_count] = block_value; + codeword_count++; + i += 3; + } else { + block_value = (intermediate.charAt(i) - '0') * 10000; + block_value += (intermediate.charAt(i + 1) - '0') * 1000; + block_value += (intermediate.charAt(i + 2) - '0') * 100; + block_value += (intermediate.charAt(i + 3) - '0') * 10; + block_value += intermediate.charAt(i + 4) - '0'; + + codewords[codeword_count] = block_value / (48 * 48); + block_value = block_value - (48 * 48) * codewords[codeword_count]; + codeword_count++; + codewords[codeword_count] = block_value / 48; + block_value = block_value - 48 * codewords[codeword_count]; + codeword_count++; + codewords[codeword_count] = block_value; + codeword_count++; + i += 5; + } + } + + switch (block_remain) { + case 1: + /* Rule (a) */ + codewords[codeword_count] = positionOf(intermediate.charAt(i), C49_SET); + codeword_count++; + i++; + break; + case 3: + /* Rule (b) */ + block_value = (intermediate.charAt(i) - '0') * 100; + block_value += (intermediate.charAt(i + 1) - '0') * 10; + block_value += intermediate.charAt(i + 2) - '0'; + + codewords[codeword_count] = block_value / 48; + block_value = block_value - 48 * codewords[codeword_count]; + codeword_count++; + codewords[codeword_count] = block_value; + codeword_count++; + i += 3; + break; + case 4: + /* Rule (c) */ + block_value = 100000; + block_value += (intermediate.charAt(i) - '0') * 1000; + block_value += (intermediate.charAt(i + 1) - '0') * 100; + block_value += (intermediate.charAt(i + 2) - '0') * 10; + block_value += intermediate.charAt(i + 3) - '0'; + + codewords[codeword_count] = block_value / (48 * 48); + block_value = block_value - (48 * 48) * codewords[codeword_count]; + codeword_count++; + codewords[codeword_count] = block_value / 48; + block_value = block_value - 48 * codewords[codeword_count]; + codeword_count++; + codewords[codeword_count] = block_value; + codeword_count++; + i += 4; + break; + } + if (i < h) { + /* There is more to add */ + codewords[codeword_count] = 48; /* Numeric Shift */ + codeword_count++; + } + } else { + codewords[codeword_count] = positionOf(intermediate.charAt(i), C49_SET); + codeword_count++; + i++; + } + } else { + codewords[codeword_count] = positionOf(intermediate.charAt(i), C49_SET); + codeword_count++; + i++; + } + } while (i < h); + + int M; + switch (codewords[0]) { /* Set starting mode value */ + case 48: + M = 2; + break; + case 43: + M = 4; + break; + case 44: + M = 5; + break; + default: + M = 0; + break; + } + + if (M != 0) { + for (i = 0; i < codeword_count; i++) { + codewords[i] = codewords[i + 1]; + } + codeword_count--; + } + + if (codeword_count > 49) { + throw OkapiInputException.inputTooLong(); + } + + infoLine("Starting Mode (M): " + M); + + /* Place codewords in code character array (c grid) */ + int rows = 0; + do { + for (i = 0; i < 7; i++) { + if (((rows * 7) + i) < codeword_count) { + c_grid[rows][i] = codewords[(rows * 7) + i]; + } else { + c_grid[rows][i] = 48; /* Pad */ + pad_count++; + } + } + rows++; + } while ((rows * 7) < codeword_count); + + if ((((rows <= 6) && (pad_count < 5))) || (rows > 6) || (rows == 1)) { + /* Add a row */ + for (i = 0; i < 7; i++) { + c_grid[rows][i] = 48; /* Pad */ + } + rows++; + } + + /* Add row count and mode character */ + c_grid[rows - 1][6] = (7 * (rows - 2)) + M; + + /* Add row check character */ + for (i = 0; i < rows - 1; i++) { + int row_sum = 0; + for (int j = 0; j < 7; j++) { + row_sum += c_grid[i][j]; + } + c_grid[i][7] = row_sum % 49; + } + + /* Calculate Symbol Check Characters */ + posn_val = 0; + x_count = c_grid[rows - 1][6] * 20; + y_count = c_grid[rows - 1][6] * 16; + z_count = c_grid[rows - 1][6] * 38; + for (i = 0; i < rows - 1; i++) { + for (int j = 0; j < 4; j++) { + local_value = (c_grid[i][2 * j] * 49) + c_grid[i][(2 * j) + 1]; + x_count += C49_X_WEIGHT[posn_val] * local_value; + y_count += C49_Y_WEIGHT[posn_val] * local_value; + z_count += C49_Z_WEIGHT[posn_val] * local_value; + posn_val++; + } + } + + if (rows > 6) { + /* Add Z Symbol Check */ + c_grid[rows - 1][0] = (z_count % 2401) / 49; + c_grid[rows - 1][1] = (z_count % 2401) % 49; + } + + local_value = (c_grid[rows - 1][0] * 49) + c_grid[rows - 1][1]; + x_count += C49_X_WEIGHT[posn_val] * local_value; + y_count += C49_Y_WEIGHT[posn_val] * local_value; + posn_val++; + + /* Add Y Symbol Check */ + c_grid[rows - 1][2] = (y_count % 2401) / 49; + c_grid[rows - 1][3] = (y_count % 2401) % 49; + + local_value = (c_grid[rows - 1][2] * 49) + c_grid[rows - 1][3]; + x_count += C49_X_WEIGHT[posn_val] * local_value; + + /* Add X Symbol Check */ + c_grid[rows - 1][4] = (x_count % 2401) / 49; + c_grid[rows - 1][5] = (x_count % 2401) % 49; + + infoLine("Check Characters: " + (z_count % 2401) + " " + (y_count % 2401)); + + /* Add last row check character */ + int sum = 0; + for (i = 0; i < 7; i++) { + sum += c_grid[rows - 1][i]; + } + c_grid[rows - 1][7] = sum % 49; + + info("Codewords: "); + /* Transfer data to symbol character array (w grid) */ + for (i = 0; i < rows; i++) { + for (int j = 0; j < 4; j++) { + w_grid[i][j] = (c_grid[i][2 * j] * 49) + c_grid[i][(2 * j) + 1]; + infoSpace(c_grid[i][2 * j]); + infoSpace(c_grid[i][(2 * j) + 1]); + } + } + infoLine(); + + readable = ""; + pattern = new String[rows]; + row_count = rows; + row_height = new int[rows]; + + info("Symbol Characters: "); + for (i = 0; i < rows; i++) { + StringBuilder rowPattern = new StringBuilder(3 + (4 * 8)); + rowPattern.append("11"); /* Start character */ + for (int j = 0; j < 4; j++) { + infoSpace(w_grid[i][j]); + if (i != (rows - 1)) { + if (C49_TABLE4[i].charAt(j) == 'E') { + /* Even Parity */ + rowPattern.append(C49_APPXE_EVEN[w_grid[i][j]]); + } else { + /* Odd Parity */ + rowPattern.append(C49_APPXE_ODD[w_grid[i][j]]); + } + } else { + /* Last row uses all even parity */ + rowPattern.append(C49_APPXE_EVEN[w_grid[i][j]]); + } + } + rowPattern.append('4'); /* Stop character */ + pattern[i] = rowPattern.toString(); + row_height[i] = 10; + } + infoLine(); + } + + @Override + protected void plotSymbol() { + + int xBlock, yBlock; + int x, y, w, h; + boolean black; + + resetPlotElements(); + + y = 1; + h = 1; + for (yBlock = 0; yBlock < row_count; yBlock++) { + black = true; + x = 15; + for (xBlock = 0; xBlock < pattern[yBlock].length(); xBlock++) { + if (black) { + black = false; + w = pattern[yBlock].charAt(xBlock) - '0'; + if (row_height[yBlock] == -1) { + h = default_height; + } else { + h = row_height[yBlock]; + } + if (w != 0 && h != 0) { + Rectangle rect = new Rectangle(x, y, w, h); + rectangles.add(rect); + } + if (x + w > symbol_width) { + symbol_width = x + w; + } + } else { + black = true; + } + x += pattern[yBlock].charAt(xBlock) - '0'; + } + y += h; + if (y > symbol_height) { + symbol_height = y; + } + /* Add bars between rows */ + if (yBlock != row_count - 1) { + Rectangle rect = new Rectangle(15, y - 1, symbol_width - 15, 2); + rectangles.add(rect); + } + } + + /* Add top and bottom binding bars */ + Rectangle top = new Rectangle(0, 0, symbol_width + 15, 2); + rectangles.add(top); + Rectangle bottom = new Rectangle(0, y - 1, symbol_width + 15, 2); + rectangles.add(bottom); + symbol_width += 15; + symbol_height += 1; + } +} diff --git a/src/main/java/uk/org/okapibarcode/backend/Code93.java b/src/main/java/uk/org/okapibarcode/backend/Code93.java index be05a04c..b658b20f 100644 --- a/src/main/java/uk/org/okapibarcode/backend/Code93.java +++ b/src/main/java/uk/org/okapibarcode/backend/Code93.java @@ -1,207 +1,207 @@ -/* - * Copyright 2014-2015 Robin Stuart, Daniel Gredler - * - * 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. - */ - -package uk.org.okapibarcode.backend; - -import static uk.org.okapibarcode.util.Arrays.positionOf; - -/** - *

Implements Code 93. - * - *

Supports encoding of 7-bit ASCII text. Two check digits are added. - * - * @author Robin Stuart - * @author Daniel Gredler - */ -public class Code93 extends Symbol { - - /** - * Code 93 control characters, indexed by ASCII codes (NOTE: a = Ctrl $, - * b = Ctrl %, c = Ctrl /, d = Ctrl + for sequences of two characters). - */ - private static final String[] CODE_93_CTRL = { - "bU", "aA", "aB", "aC", "aD", "aE", "aF", "aG", "aH", "aI", - "aJ", "aK", "aL", "aM", "aN", "aO", "aP", "aQ", "aR", "aS", - "aT", "aU", "aV", "aW", "aX", "aY", "aZ", "bA", "bB", "bC", - "bD", "bE", " ", "cA", "cB", "cC", "$", "%", "cF", "cG", - "cH", "cI", "cJ", "+", "cL", "-", ".", "/", "0", "1", - "2", "3", "4", "5", "6", "7", "8", "9", "cZ", "bF", - "bG", "bH", "bI", "bJ", "bV", "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", "bK", "bL", "bM", "bN", "bO", "bW", "dA", "dB", "dC", - "dD", "dE", "dF", "dG", "dH", "dI", "dJ", "dK", "dL", "dM", - "dN", "dO", "dP", "dQ", "dR", "dS", "dT", "dU", "dV", "dW", - "dX", "dY", "dZ", "bP", "bQ", "bR", "bS", "bT" }; - - /** - * Mapping of control characters to pattern table index (NOTE: a = Ctrl $, - * b = Ctrl %, c = Ctrl /, d = Ctrl + for sequences of two characters). - */ - private static final char[] CODE_93_LOOKUP = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - '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', '-', '.', ' ', '$', - '/', '+', '%', 'a', 'b', 'c', 'd' }; - - /** Code 93 pattern table. */ - private static final String[] CODE_93_TABLE = { - "131112", "111213", "111312", "111411", "121113", - "121212", "121311", "111114", "131211", "141111", - "211113", "211212", "211311", "221112", "221211", - "231111", "112113", "112212", "112311", "122112", - "132111", "111123", "111222", "111321", "121122", - "131121", "212112", "212211", "211122", "211221", - "221121", "222111", "112122", "112221", "122121", - "123111", "121131", "311112", "311211", "321111", - "112131", "113121", "211131", "121221", "312111", - "311121", "122211" }; - - /** Whether or not to show check digits in the human-readable text. */ - private boolean showCheckDigits = true; - - /** Optional start/stop delimiter to be shown in the human-readable text. */ - private Character startStopDelimiter; - - /** - * Sets whether or not to show check digits in the human-readable text (defaults to true). - * - * @param showCheckDigits whether or not to show check digits in the human-readable text - */ - public void setShowCheckDigits(boolean showCheckDigits) { - this.showCheckDigits = showCheckDigits; - } - - /** - * Returns whether or not this symbol shows check digits in the human-readable text. - * - * @return whether or not this symbol shows check digits in the human-readable text - */ - public boolean getShowCheckDigits() { - return showCheckDigits; - } - - /** - * Sets an optional start/stop delimiter to be shown in the human-readable text (defaults to null). - * - * @param startStopDelimiter an optional start/stop delimiter to be shown in the human-readable text - */ - public void setStartStopDelimiter(Character startStopDelimiter) { - this.startStopDelimiter = startStopDelimiter; - } - - /** - * Returns the optional start/stop delimiter to be shown in the human-readable text. - * - * @return the optional start/stop delimiter to be shown in the human-readable text - */ - public Character getStartStopDelimiter() { - return startStopDelimiter; - } - - /** {@inheritDoc} */ - @Override - protected void encode() { - - if (!content.matches("[\u0000-\u007F]*")) { - throw OkapiInputException.invalidCharactersInInput(); - } - - char[] controlChars = toControlChars(content); - int l = controlChars.length; - - int[] values = new int[controlChars.length + 2]; - for (int i = 0; i < l; i++) { - values[i] = positionOf(controlChars[i], CODE_93_LOOKUP); - } - - int c = calculateCheckDigitC(values, l); - values[l] = c; - l++; - - int k = calculateCheckDigitK(values, l); - values[l] = k; - l++; - - readable = content; - if (showCheckDigits) { - readable = readable + CODE_93_LOOKUP[c] + CODE_93_LOOKUP[k]; - } - if (startStopDelimiter != null) { - readable = startStopDelimiter + readable + startStopDelimiter; - } - - infoLine("Check Digit C: " + c); - infoLine("Check Digit K: " + k); - pattern = new String[] { toPattern(values) }; - row_count = 1; - row_height = new int[] { -1 }; - } - - private static char[] toControlChars(String s) { - StringBuilder buffer = new StringBuilder(); - char[] chars = s.toCharArray(); - for (int i = 0; i < chars.length; i++) { - int asciiCode = chars[i]; - buffer.append(CODE_93_CTRL[asciiCode]); - } - return buffer.toString().toCharArray(); - } - - private static int calculateCheckDigitC(int[] values, int length) { - int c = 0; - int weight = 1; - for (int i = length - 1; i >= 0; i--) { - c += values[i] * weight; - weight++; - if (weight == 21) { - weight = 1; - } - } - c = c % 47; - return c; - } - - private static int calculateCheckDigitK(int[] values, int length) { - int k = 0; - int weight = 1; - for (int i = length - 1; i >= 0; i--) { - k += values[i] * weight; - weight++; - if (weight == 16) { - weight = 1; - } - } - k = k % 47; - return k; - } - - private static String toPattern(int[] values) { - StringBuilder buffer = new StringBuilder("111141"); - for (int i = 0; i < values.length; i++) { - buffer.append(CODE_93_TABLE[values[i]]); - } - buffer.append("1111411"); - return buffer.toString(); - } - - /** {@inheritDoc} */ - @Override - protected int[] getCodewords() { - return getPatternAsCodewords(6); - } -} +/* + * Copyright 2014-2015 Robin Stuart, Daniel Gredler + * + * 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. + */ + +package uk.org.okapibarcode.backend; + +import static uk.org.okapibarcode.util.Arrays.positionOf; + +/** + *

Implements Code 93. + * + *

Supports encoding of 7-bit ASCII text. Two check digits are added. + * + * @author Robin Stuart + * @author Daniel Gredler + */ +public class Code93 extends Symbol { + + /** + * Code 93 control characters, indexed by ASCII codes (NOTE: a = Ctrl $, + * b = Ctrl %, c = Ctrl /, d = Ctrl + for sequences of two characters). + */ + private static final String[] CODE_93_CTRL = { + "bU", "aA", "aB", "aC", "aD", "aE", "aF", "aG", "aH", "aI", + "aJ", "aK", "aL", "aM", "aN", "aO", "aP", "aQ", "aR", "aS", + "aT", "aU", "aV", "aW", "aX", "aY", "aZ", "bA", "bB", "bC", + "bD", "bE", " ", "cA", "cB", "cC", "$", "%", "cF", "cG", + "cH", "cI", "cJ", "+", "cL", "-", ".", "/", "0", "1", + "2", "3", "4", "5", "6", "7", "8", "9", "cZ", "bF", + "bG", "bH", "bI", "bJ", "bV", "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", "bK", "bL", "bM", "bN", "bO", "bW", "dA", "dB", "dC", + "dD", "dE", "dF", "dG", "dH", "dI", "dJ", "dK", "dL", "dM", + "dN", "dO", "dP", "dQ", "dR", "dS", "dT", "dU", "dV", "dW", + "dX", "dY", "dZ", "bP", "bQ", "bR", "bS", "bT" }; + + /** + * Mapping of control characters to pattern table index (NOTE: a = Ctrl $, + * b = Ctrl %, c = Ctrl /, d = Ctrl + for sequences of two characters). + */ + private static final char[] CODE_93_LOOKUP = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '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', '-', '.', ' ', '$', + '/', '+', '%', 'a', 'b', 'c', 'd' }; + + /** Code 93 pattern table. */ + private static final String[] CODE_93_TABLE = { + "131112", "111213", "111312", "111411", "121113", + "121212", "121311", "111114", "131211", "141111", + "211113", "211212", "211311", "221112", "221211", + "231111", "112113", "112212", "112311", "122112", + "132111", "111123", "111222", "111321", "121122", + "131121", "212112", "212211", "211122", "211221", + "221121", "222111", "112122", "112221", "122121", + "123111", "121131", "311112", "311211", "321111", + "112131", "113121", "211131", "121221", "312111", + "311121", "122211" }; + + /** Whether or not to show check digits in the human-readable text. */ + private boolean showCheckDigits = true; + + /** Optional start/stop delimiter to be shown in the human-readable text. */ + private Character startStopDelimiter; + + /** + * Sets whether or not to show check digits in the human-readable text (defaults to true). + * + * @param showCheckDigits whether or not to show check digits in the human-readable text + */ + public void setShowCheckDigits(boolean showCheckDigits) { + this.showCheckDigits = showCheckDigits; + } + + /** + * Returns whether or not this symbol shows check digits in the human-readable text. + * + * @return whether or not this symbol shows check digits in the human-readable text + */ + public boolean getShowCheckDigits() { + return showCheckDigits; + } + + /** + * Sets an optional start/stop delimiter to be shown in the human-readable text (defaults to null). + * + * @param startStopDelimiter an optional start/stop delimiter to be shown in the human-readable text + */ + public void setStartStopDelimiter(Character startStopDelimiter) { + this.startStopDelimiter = startStopDelimiter; + } + + /** + * Returns the optional start/stop delimiter to be shown in the human-readable text. + * + * @return the optional start/stop delimiter to be shown in the human-readable text + */ + public Character getStartStopDelimiter() { + return startStopDelimiter; + } + + /** {@inheritDoc} */ + @Override + protected void encode() { + + if (!content.matches("[\u0000-\u007F]*")) { + throw OkapiInputException.invalidCharactersInInput(); + } + + char[] controlChars = toControlChars(content); + int l = controlChars.length; + + int[] values = new int[controlChars.length + 2]; + for (int i = 0; i < l; i++) { + values[i] = positionOf(controlChars[i], CODE_93_LOOKUP); + } + + int c = calculateCheckDigitC(values, l); + values[l] = c; + l++; + + int k = calculateCheckDigitK(values, l); + values[l] = k; + l++; + + readable = content; + if (showCheckDigits) { + readable = readable + CODE_93_LOOKUP[c] + CODE_93_LOOKUP[k]; + } + if (startStopDelimiter != null) { + readable = startStopDelimiter + readable + startStopDelimiter; + } + + infoLine("Check Digit C: " + c); + infoLine("Check Digit K: " + k); + pattern = new String[] { toPattern(values) }; + row_count = 1; + row_height = new int[] { -1 }; + } + + private static char[] toControlChars(String s) { + StringBuilder buffer = new StringBuilder(); + char[] chars = s.toCharArray(); + for (int i = 0; i < chars.length; i++) { + int asciiCode = chars[i]; + buffer.append(CODE_93_CTRL[asciiCode]); + } + return buffer.toString().toCharArray(); + } + + private static int calculateCheckDigitC(int[] values, int length) { + int c = 0; + int weight = 1; + for (int i = length - 1; i >= 0; i--) { + c += values[i] * weight; + weight++; + if (weight == 21) { + weight = 1; + } + } + c = c % 47; + return c; + } + + private static int calculateCheckDigitK(int[] values, int length) { + int k = 0; + int weight = 1; + for (int i = length - 1; i >= 0; i--) { + k += values[i] * weight; + weight++; + if (weight == 16) { + weight = 1; + } + } + k = k % 47; + return k; + } + + private static String toPattern(int[] values) { + StringBuilder buffer = new StringBuilder("111141"); + for (int i = 0; i < values.length; i++) { + buffer.append(CODE_93_TABLE[values[i]]); + } + buffer.append("1111411"); + return buffer.toString(); + } + + /** {@inheritDoc} */ + @Override + protected int[] getCodewords() { + return getPatternAsCodewords(6); + } +} diff --git a/src/main/java/uk/org/okapibarcode/backend/CodeOne.java b/src/main/java/uk/org/okapibarcode/backend/CodeOne.java index 5d74d1c8..fa1d3ca5 100644 --- a/src/main/java/uk/org/okapibarcode/backend/CodeOne.java +++ b/src/main/java/uk/org/okapibarcode/backend/CodeOne.java @@ -1,1948 +1,1948 @@ -/* - * Copyright 2014 Robin Stuart - * - * 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. - */ - -package uk.org.okapibarcode.backend; - -import java.math.BigInteger; -import java.nio.charset.StandardCharsets; - -/** - *

Implements Code One. - * - *

Code One is able to encode the ISO 8859-1 (Latin-1) character set or GS1 - * data. There are two types of Code One symbol: variable height symbols - * which are roughly square (versions A thought to H) and fixed-height - * versions (version S and T). Version S symbols can only encode numeric data. - * The width of version S and version T symbols is determined by the length - * of the input data. - * - * @author Robin Stuart - */ -public class CodeOne extends Symbol { - - public enum Version { - NONE, A, B, C, D, E, F, G, H, S, T - } - - private static final int[] C40_SHIFT = { - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3 - }; - - private static final int[] C40_VALUE = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 3, 0, 1, 2, 3, 4, 5, 6, - 7, 8, 9, 10, 11, 12, 13, 14, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, - 17, 18, 19, 20, 21, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, - 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 22, 23, 24, 25, 26, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 - }; - - private static final int[] TEXT_SHIFT = { - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, - 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 3, 3, 3, 3, 3 - }; - - private static final int[] TEXT_VALUE = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 3, 0, 1, 2, 3, 4, 5, 6, - 7, 8, 9, 10, 11, 12, 13, 14, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, - 17, 18, 19, 20, 21, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 22, 23, 24, 25, 26, 0, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, - 33, 34, 35, 36, 37, 38, 39, 27, 28, 29, 30, 31 - }; - - private static final int[] C1_HEIGHT = { - 16, 22, 28, 40, 52, 70, 104, 148 - }; - private static final int[] C1_WIDTH = { - 18, 22, 32, 42, 54, 76, 98, 134 - }; - private static final int[] C1_DATA_LENGTH = { - 10, 19, 44, 91, 182, 370, 732, 1480 - }; - private static final int[] C1_ECC_LENGTH = { - 10, 16, 26, 44, 70, 140, 280, 560 - }; - private static final int[] C1_BLOCKS = { - 1, 1, 1, 1, 1, 2, 4, 8 - }; - private static final int[] C1_DATA_BLOCKS = { - 10, 19, 44, 91, 182, 185, 183, 185 - }; - private static final int[] C1_ECC_BLOCKS = { - 10, 16, 26, 44, 70, 70, 70, 70 - }; - private static final int[] C1_GRID_WIDTH = { - 4, 5, 7, 9, 12, 17, 22, 30 - }; - private static final int[] C1_GRID_HEIGHT = { - 5, 7, 10, 15, 21, 30, 46, 68 - }; - - private enum Mode { - C1_ASCII, C1_C40, C1_DECIMAL, C1_TEXT, C1_EDI, C1_BYTE - } - - private Version preferredVersion = Version.NONE; - - private int[] data = new int[1500]; - private int[][] datagrid = new int[136][120]; - private boolean[][] outputGrid = new boolean[148][134]; - - /** - * Sets the preferred symbol size / version. Versions A to H are square symbols. - * Version S and T are fixed height symbols. This value may be ignored if the input - * data does not fit in the specified version. - * - * @param version the preferred symbol version - */ - public void setPreferredVersion(Version version) { - preferredVersion = version; - } - - /** - * Returns the preferred symbol version. - * - * @return the preferred symbol version - */ - public Version getPreferredVersion() { - return preferredVersion; - } - - @Override - protected boolean gs1Supported() { - return true; - } - - @Override - protected void encode() { - - int size = 1, i, j, data_blocks; - int row, col; - int sub_version = 0; - int codewords; - int[] ecc = new int[600]; - int[] stream = new int[2100]; - int block_width; - int length = content.length(); - ReedSolomon rs = new ReedSolomon(); - int data_length; - int data_cw, ecc_cw; - int[] sub_data = new int[190]; - StringBuilder bin = new StringBuilder(); - - if (!content.matches("[\u0000-\u00FF]+")) { - throw OkapiInputException.invalidCharactersInInput(); - } - - if (preferredVersion == Version.S) { - /* Version S */ - - infoLine("Version: S"); - - if (length > 18) { - throw OkapiInputException.inputTooLong(); - } - - if (!content.matches("[0-9]+?")) { - throw OkapiInputException.invalidCharactersInInput(); - } - - sub_version = 3; - codewords = 12; - block_width = 6; /* Version S-30 */ - if (length <= 12) { - sub_version = 2; - codewords = 8; - block_width = 4; - } /* Version S-20 */ - if (length <= 6) { - sub_version = 1; - codewords = 4; - block_width = 2; - } /* Version S-10 */ - - BigInteger elreg = new BigInteger(content); - - for (i = 0; i < codewords; i++) { - BigInteger codewordValue = elreg.shiftRight(5 * i); - codewordValue = codewordValue.and(BigInteger.valueOf(0b11111)); - data[codewords - i - 1] = codewordValue.intValue(); - } - - logCodewords(codewords); - - rs.init_gf(0x25); - rs.init_code(codewords, 1); - rs.encode(codewords, data); - - infoLine("ECC Codeword Count: " + codewords); - - for (i = 0; i < codewords; i++) { - stream[i] = data[i]; - stream[i + codewords] = rs.getResult(codewords - i - 1); - } - - for (i = 0; i < 136; i++) { - for (j = 0; j < 120; j++) { - datagrid[i][j] = '0'; - } - } - - i = 0; - for (row = 0; row < 2; row++) { - for (col = 0; col < block_width; col++) { - if ((stream[i] & 0x10) != 0) { - datagrid[row * 2][col * 5] = '1'; - } - if ((stream[i] & 0x08) != 0) { - datagrid[row * 2][(col * 5) + 1] = '1'; - } - if ((stream[i] & 0x04) != 0) { - datagrid[row * 2][(col * 5) + 2] = '1'; - } - if ((stream[i] & 0x02) != 0) { - datagrid[(row * 2) + 1][col * 5] = '1'; - } - if ((stream[i] & 0x01) != 0) { - datagrid[(row * 2) + 1][(col * 5) + 1] = '1'; - } - if ((stream[i + 1] & 0x10) != 0) { - datagrid[row * 2][(col * 5) + 3] = '1'; - } - if ((stream[i + 1] & 0x08) != 0) { - datagrid[row * 2][(col * 5) + 4] = '1'; - } - if ((stream[i + 1] & 0x04) != 0) { - datagrid[(row * 2) + 1][(col * 5) + 2] = '1'; - } - if ((stream[i + 1] & 0x02) != 0) { - datagrid[(row * 2) + 1][(col * 5) + 3] = '1'; - } - if ((stream[i + 1] & 0x01) != 0) { - datagrid[(row * 2) + 1][(col * 5) + 4] = '1'; - } - i += 2; - } - } - - infoLine("Grid Size: " + block_width + " X " + 2); - - size = 9; - row_count = 8; - symbol_width = 10 * sub_version + 1; - } - - if (preferredVersion == Version.T) { - /* Version T */ - - infoLine("Version: T"); - - for (i = 0; i < 40; i++) { - data[i] = 0; - } - data_length = encodeAsCode1Data(); - - if (data_length > 38) { - throw OkapiInputException.inputTooLong(); - } - - size = 10; - sub_version = 3; - data_cw = 38; - ecc_cw = 22; - block_width = 12; - if (data_length <= 24) { - sub_version = 2; - data_cw = 24; - ecc_cw = 16; - block_width = 8; - } - if (data_length <= 10) { - sub_version = 1; - data_cw = 10; - ecc_cw = 10; - block_width = 4; - } - - logCodewords(data_length); - - for (i = data_length; i < data_cw; i++) { - data[i] = 129; /* Pad */ - } - - /* Calculate error correction data */ - rs.init_gf(0x12d); - rs.init_code(ecc_cw, 1); - rs.encode(data_cw, data); - - infoLine("ECC Codeword Count: " + ecc_cw); - - /* "Stream" combines data and error correction data */ - for (i = 0; i < data_cw; i++) { - stream[i] = data[i]; - } - for (i = 0; i < ecc_cw; i++) { - stream[data_cw + i] = rs.getResult(ecc_cw - i - 1); - } - - for (i = 0; i < 136; i++) { - for (j = 0; j < 120; j++) { - datagrid[i][j] = '0'; - } - } - - i = 0; - for (row = 0; row < 5; row++) { - for (col = 0; col < block_width; col++) { - if ((stream[i] & 0x80) != 0) { - datagrid[row * 2][col * 4] = '1'; - } - if ((stream[i] & 0x40) != 0) { - datagrid[row * 2][(col * 4) + 1] = '1'; - } - if ((stream[i] & 0x20) != 0) { - datagrid[row * 2][(col * 4) + 2] = '1'; - } - if ((stream[i] & 0x10) != 0) { - datagrid[row * 2][(col * 4) + 3] = '1'; - } - if ((stream[i] & 0x08) != 0) { - datagrid[(row * 2) + 1][col * 4] = '1'; - } - if ((stream[i] & 0x04) != 0) { - datagrid[(row * 2) + 1][(col * 4) + 1] = '1'; - } - if ((stream[i] & 0x02) != 0) { - datagrid[(row * 2) + 1][(col * 4) + 2] = '1'; - } - if ((stream[i] & 0x01) != 0) { - datagrid[(row * 2) + 1][(col * 4) + 3] = '1'; - } - i++; - } - } - - infoLine("Grid Size: " + block_width + " X " + 5); - - row_count = 16; - symbol_width = (sub_version * 16) + 1; - } - - if ((preferredVersion != Version.S) && (preferredVersion != Version.T)) { - /* Version A to H */ - for (i = 0; i < 1500; i++) { - data[i] = 0; - } - data_length = encodeAsCode1Data(); - - for (i = 7; i >= 0; i--) { - if (C1_DATA_LENGTH[i] >= data_length) { - size = i + 1; - } - } - - if (getSize(preferredVersion) > size) { - size = getSize(preferredVersion); - } - - char version = (char) ((size - 1) + 'A'); - infoLine("Version: " + version); - logCodewords(data_length); - - for (i = data_length; i < C1_DATA_LENGTH[size - 1]; i++) { - data[i] = 129; /* Pad */ - } - - /* Calculate error correction data */ - data_length = C1_DATA_LENGTH[size - 1]; - - data_blocks = C1_BLOCKS[size - 1]; - - rs.init_gf(0x12d); - rs.init_code(C1_ECC_BLOCKS[size - 1], 0); - for (i = 0; i < data_blocks; i++) { - for (j = 0; j < C1_DATA_BLOCKS[size - 1]; j++) { - sub_data[j] = data[j * data_blocks + i]; - } - rs.encode(C1_DATA_BLOCKS[size - 1], sub_data); - for (j = 0; j < C1_ECC_BLOCKS[size - 1]; j++) { - ecc[C1_ECC_LENGTH[size - 1] - (j * data_blocks + i) - 1] = rs.getResult(j); - } - } - - infoLine("ECC Codeword Count: " + C1_ECC_LENGTH[size - 1]); - - /* "Stream" combines data and error correction data */ - for (i = 0; i < data_length; i++) { - stream[i] = data[i]; - } - for (i = 0; i < C1_ECC_LENGTH[size - 1]; i++) { - stream[data_length + i] = ecc[i]; - } - - for (i = 0; i < 136; i++) { - for (j = 0; j < 120; j++) { - datagrid[i][j] = '0'; - } - } - - i = 0; - for (row = 0; row < C1_GRID_HEIGHT[size - 1]; row++) { - for (col = 0; col < C1_GRID_WIDTH[size - 1]; col++) { - if ((stream[i] & 0x80) != 0) { - datagrid[row * 2][col * 4] = '1'; - } - if ((stream[i] & 0x40) != 0) { - datagrid[row * 2][(col * 4) + 1] = '1'; - } - if ((stream[i] & 0x20) != 0) { - datagrid[row * 2][(col * 4) + 2] = '1'; - } - if ((stream[i] & 0x10) != 0) { - datagrid[row * 2][(col * 4) + 3] = '1'; - } - if ((stream[i] & 0x08) != 0) { - datagrid[(row * 2) + 1][col * 4] = '1'; - } - if ((stream[i] & 0x04) != 0) { - datagrid[(row * 2) + 1][(col * 4) + 1] = '1'; - } - if ((stream[i] & 0x02) != 0) { - datagrid[(row * 2) + 1][(col * 4) + 2] = '1'; - } - if ((stream[i] & 0x01) != 0) { - datagrid[(row * 2) + 1][(col * 4) + 3] = '1'; - } - i++; - } - } - - infoLine("Grid Size: " + C1_GRID_WIDTH[size - 1] + " X " + C1_GRID_HEIGHT[size - 1]); - - row_count = C1_HEIGHT[size - 1]; - symbol_width = C1_WIDTH[size - 1]; - } - - for (i = 0; i < 148; i++) { - for (j = 0; j < 134; j++) { - outputGrid[i][j] = false; - } - } - - switch (size) { - case 1: - /* Version A */ - plotCentralFinder(6, 3, 1); - plotVerticalBar(4, 6, 1); - plotVerticalBar(12, 5, 0); - setGridModule(5, 12); - plotSpigot(0); - plotSpigot(15); - plotDataBlock(0, 0, 5, 4, 0, 0); - plotDataBlock(0, 4, 5, 12, 0, 2); - plotDataBlock(5, 0, 5, 12, 6, 0); - plotDataBlock(5, 12, 5, 4, 6, 2); - break; - case 2: - /* Version B */ - plotCentralFinder(8, 4, 1); - plotVerticalBar(4, 8, 1); - plotVerticalBar(16, 7, 0); - setGridModule(7, 16); - plotSpigot(0); - plotSpigot(21); - plotDataBlock(0, 0, 7, 4, 0, 0); - plotDataBlock(0, 4, 7, 16, 0, 2); - plotDataBlock(7, 0, 7, 16, 8, 0); - plotDataBlock(7, 16, 7, 4, 8, 2); - break; - case 3: - /* Version C */ - plotCentralFinder(11, 4, 2); - plotVerticalBar(4, 11, 1); - plotVerticalBar(26, 13, 1); - plotVerticalBar(4, 10, 0); - plotVerticalBar(26, 10, 0); - plotSpigot(0); - plotSpigot(27); - plotDataBlock(0, 0, 10, 4, 0, 0); - plotDataBlock(0, 4, 10, 20, 0, 2); - plotDataBlock(0, 24, 10, 4, 0, 4); - plotDataBlock(10, 0, 10, 4, 8, 0); - plotDataBlock(10, 4, 10, 20, 8, 2); - plotDataBlock(10, 24, 10, 4, 8, 4); - break; - case 4: - /* Version D */ - plotCentralFinder(16, 5, 1); - plotVerticalBar(4, 16, 1); - plotVerticalBar(20, 16, 1); - plotVerticalBar(36, 16, 1); - plotVerticalBar(4, 15, 0); - plotVerticalBar(20, 15, 0); - plotVerticalBar(36, 15, 0); - plotSpigot(0); - plotSpigot(12); - plotSpigot(27); - plotSpigot(39); - plotDataBlock(0, 0, 15, 4, 0, 0); - plotDataBlock(0, 4, 15, 14, 0, 2); - plotDataBlock(0, 18, 15, 14, 0, 4); - plotDataBlock(0, 32, 15, 4, 0, 6); - plotDataBlock(15, 0, 15, 4, 10, 0); - plotDataBlock(15, 4, 15, 14, 10, 2); - plotDataBlock(15, 18, 15, 14, 10, 4); - plotDataBlock(15, 32, 15, 4, 10, 6); - break; - case 5: - /* Version E */ - plotCentralFinder(22, 5, 2); - plotVerticalBar(4, 22, 1); - plotVerticalBar(26, 24, 1); - plotVerticalBar(48, 22, 1); - plotVerticalBar(4, 21, 0); - plotVerticalBar(26, 21, 0); - plotVerticalBar(48, 21, 0); - plotSpigot(0); - plotSpigot(12); - plotSpigot(39); - plotSpigot(51); - plotDataBlock(0, 0, 21, 4, 0, 0); - plotDataBlock(0, 4, 21, 20, 0, 2); - plotDataBlock(0, 24, 21, 20, 0, 4); - plotDataBlock(0, 44, 21, 4, 0, 6); - plotDataBlock(21, 0, 21, 4, 10, 0); - plotDataBlock(21, 4, 21, 20, 10, 2); - plotDataBlock(21, 24, 21, 20, 10, 4); - plotDataBlock(21, 44, 21, 4, 10, 6); - break; - case 6: - /* Version F */ - plotCentralFinder(31, 5, 3); - plotVerticalBar(4, 31, 1); - plotVerticalBar(26, 35, 1); - plotVerticalBar(48, 31, 1); - plotVerticalBar(70, 35, 1); - plotVerticalBar(4, 30, 0); - plotVerticalBar(26, 30, 0); - plotVerticalBar(48, 30, 0); - plotVerticalBar(70, 30, 0); - plotSpigot(0); - plotSpigot(12); - plotSpigot(24); - plotSpigot(45); - plotSpigot(57); - plotSpigot(69); - plotDataBlock(0, 0, 30, 4, 0, 0); - plotDataBlock(0, 4, 30, 20, 0, 2); - plotDataBlock(0, 24, 30, 20, 0, 4); - plotDataBlock(0, 44, 30, 20, 0, 6); - plotDataBlock(0, 64, 30, 4, 0, 8); - plotDataBlock(30, 0, 30, 4, 10, 0); - plotDataBlock(30, 4, 30, 20, 10, 2); - plotDataBlock(30, 24, 30, 20, 10, 4); - plotDataBlock(30, 44, 30, 20, 10, 6); - plotDataBlock(30, 64, 30, 4, 10, 8); - break; - case 7: - /* Version G */ - plotCentralFinder(47, 6, 2); - plotVerticalBar(6, 47, 1); - plotVerticalBar(27, 49, 1); - plotVerticalBar(48, 47, 1); - plotVerticalBar(69, 49, 1); - plotVerticalBar(90, 47, 1); - plotVerticalBar(6, 46, 0); - plotVerticalBar(27, 46, 0); - plotVerticalBar(48, 46, 0); - plotVerticalBar(69, 46, 0); - plotVerticalBar(90, 46, 0); - plotSpigot(0); - plotSpigot(12); - plotSpigot(24); - plotSpigot(36); - plotSpigot(67); - plotSpigot(79); - plotSpigot(91); - plotSpigot(103); - plotDataBlock(0, 0, 46, 6, 0, 0); - plotDataBlock(0, 6, 46, 19, 0, 2); - plotDataBlock(0, 25, 46, 19, 0, 4); - plotDataBlock(0, 44, 46, 19, 0, 6); - plotDataBlock(0, 63, 46, 19, 0, 8); - plotDataBlock(0, 82, 46, 6, 0, 10); - plotDataBlock(46, 0, 46, 6, 12, 0); - plotDataBlock(46, 6, 46, 19, 12, 2); - plotDataBlock(46, 25, 46, 19, 12, 4); - plotDataBlock(46, 44, 46, 19, 12, 6); - plotDataBlock(46, 63, 46, 19, 12, 8); - plotDataBlock(46, 82, 46, 6, 12, 10); - break; - case 8: - /* Version H */ - plotCentralFinder(69, 6, 3); - plotVerticalBar(6, 69, 1); - plotVerticalBar(26, 73, 1); - plotVerticalBar(46, 69, 1); - plotVerticalBar(66, 73, 1); - plotVerticalBar(86, 69, 1); - plotVerticalBar(106, 73, 1); - plotVerticalBar(126, 69, 1); - plotVerticalBar(6, 68, 0); - plotVerticalBar(26, 68, 0); - plotVerticalBar(46, 68, 0); - plotVerticalBar(66, 68, 0); - plotVerticalBar(86, 68, 0); - plotVerticalBar(106, 68, 0); - plotVerticalBar(126, 68, 0); - plotSpigot(0); - plotSpigot(12); - plotSpigot(24); - plotSpigot(36); - plotSpigot(48); - plotSpigot(60); - plotSpigot(87); - plotSpigot(99); - plotSpigot(111); - plotSpigot(123); - plotSpigot(135); - plotSpigot(147); - plotDataBlock(0, 0, 68, 6, 0, 0); - plotDataBlock(0, 6, 68, 18, 0, 2); - plotDataBlock(0, 24, 68, 18, 0, 4); - plotDataBlock(0, 42, 68, 18, 0, 6); - plotDataBlock(0, 60, 68, 18, 0, 8); - plotDataBlock(0, 78, 68, 18, 0, 10); - plotDataBlock(0, 96, 68, 18, 0, 12); - plotDataBlock(0, 114, 68, 6, 0, 14); - plotDataBlock(68, 0, 68, 6, 12, 0); - plotDataBlock(68, 6, 68, 18, 12, 2); - plotDataBlock(68, 24, 68, 18, 12, 4); - plotDataBlock(68, 42, 68, 18, 12, 6); - plotDataBlock(68, 60, 68, 18, 12, 8); - plotDataBlock(68, 78, 68, 18, 12, 10); - plotDataBlock(68, 96, 68, 18, 12, 12); - plotDataBlock(68, 114, 68, 6, 12, 14); - break; - case 9: - /* Version S */ - plotHorizontalBar(5, 1); - plotHorizontalBar(7, 1); - setGridModule(6, 0); - setGridModule(6, symbol_width - 1); - resetGridModule(7, 1); - resetGridModule(7, symbol_width - 2); - switch (sub_version) { - case 1: - /* Version S-10 */ - setGridModule(0, 5); - plotDataBlock(0, 0, 4, 5, 0, 0); - plotDataBlock(0, 5, 4, 5, 0, 1); - break; - case 2: - /* Version S-20 */ - setGridModule(0, 10); - setGridModule(4, 10); - plotDataBlock(0, 0, 4, 10, 0, 0); - plotDataBlock(0, 10, 4, 10, 0, 1); - break; - case 3: - /* Version S-30 */ - setGridModule(0, 15); - setGridModule(4, 15); - setGridModule(6, 15); - plotDataBlock(0, 0, 4, 15, 0, 0); - plotDataBlock(0, 15, 4, 15, 0, 1); - break; - } - break; - case 10: - /* Version T */ - plotHorizontalBar(11, 1); - plotHorizontalBar(13, 1); - plotHorizontalBar(15, 1); - setGridModule(12, 0); - setGridModule(12, symbol_width - 1); - setGridModule(14, 0); - setGridModule(14, symbol_width - 1); - resetGridModule(13, 1); - resetGridModule(13, symbol_width - 2); - resetGridModule(15, 1); - resetGridModule(15, symbol_width - 2); - switch (sub_version) { - case 1: - /* Version T-16 */ - setGridModule(0, 8); - setGridModule(10, 8); - plotDataBlock(0, 0, 10, 8, 0, 0); - plotDataBlock(0, 8, 10, 8, 0, 1); - break; - case 2: - /* Version T-32 */ - setGridModule(0, 16); - setGridModule(10, 16); - setGridModule(12, 16); - plotDataBlock(0, 0, 10, 16, 0, 0); - plotDataBlock(0, 16, 10, 16, 0, 1); - break; - case 3: - /* Verion T-48 */ - setGridModule(0, 24); - setGridModule(10, 24); - setGridModule(12, 24); - setGridModule(14, 24); - plotDataBlock(0, 0, 10, 24, 0, 0); - plotDataBlock(0, 24, 10, 24, 0, 1); - break; - } - break; - } - - readable = ""; - pattern = new String[row_count]; - row_height = new int[row_count]; - for (i = 0; i < row_count; i++) { - bin.setLength(0); - for (j = 0; j < symbol_width; j++) { - if (outputGrid[i][j]) { - bin.append('1'); - } else { - bin.append('0'); - } - } - pattern[i] = bin2pat(bin); - row_height[i] = 1; - } - } - - private void logCodewords(int count) { - info("Codewords: "); - for (int i = 0; i < count; i++) { - infoSpace(data[i]); - } - infoLine(); - } - - private int encodeAsCode1Data() { - Mode current_mode, next_mode; - boolean latch; - boolean done; - int sourcePoint, targetPoint, i, j; - int c40_p; - int text_p; - int edi_p; - int byte_start = 0; - int[] c40_buffer = new int[6]; - int[] text_buffer = new int[6]; - int[] edi_buffer = new int[6]; - String decimal_binary = ""; - int length; - int shift_set, value; - int data_left, decimal_count; - int sub_value; - int bits_left_in_byte, target_count; - boolean isTwoDigits; - - inputData = toBytes(content, StandardCharsets.ISO_8859_1); - length = inputData.length; - - sourcePoint = 0; - targetPoint = 0; - c40_p = 0; - text_p = 0; - edi_p = 0; - - if (inputDataType == DataType.GS1) { - data[targetPoint] = 232; - targetPoint++; - } /* FNC1 */ - - /* Step A */ - current_mode = Mode.C1_ASCII; - next_mode = Mode.C1_ASCII; - - do { - if (current_mode != next_mode) { - /* Change mode */ - switch (next_mode) { - case C1_C40: - data[targetPoint] = 230; - targetPoint++; - break; - case C1_TEXT: - data[targetPoint] = 239; - targetPoint++; - break; - case C1_EDI: - data[targetPoint] = 238; - targetPoint++; - break; - case C1_BYTE: - data[targetPoint] = 231; - targetPoint++; - break; - } - } - - if ((current_mode != Mode.C1_BYTE) && (next_mode == Mode.C1_BYTE)) { - byte_start = targetPoint; - } - current_mode = next_mode; - - if (current_mode == Mode.C1_ASCII) { /* Step B - ASCII encodation */ - next_mode = Mode.C1_ASCII; - - if ((length - sourcePoint) >= 21) { /* Step B1 */ - j = 0; - - for (i = 0; i < 21; i++) { - if ((inputData[sourcePoint + i] >= '0') && (inputData[sourcePoint + i] <= '9')) { - j++; - } - } - - if (j == 21) { - next_mode = Mode.C1_DECIMAL; - decimal_binary += "1111"; - } - } - - if ((next_mode == Mode.C1_ASCII) && ((length - sourcePoint) >= 13)) { /* Step B2 */ - j = 0; - - for (i = 0; i < 13; i++) { - if ((inputData[sourcePoint + i] >= '0') && (inputData[sourcePoint + i] <= '9')) { - j++; - } - } - - if (j == 13) { - latch = false; - for (i = sourcePoint + 13; i < length; i++) { - if (!((inputData[i] >= '0') && - (inputData[i] <= '9'))) { - latch = true; - } - } - - if (!(latch)) { - next_mode = Mode.C1_DECIMAL; - decimal_binary += "1111"; - } - } - } - - if (next_mode == Mode.C1_ASCII) { /* Step B3 */ - isTwoDigits = false; - if ((sourcePoint + 1) != length) { - if ((inputData[sourcePoint] >= '0') && (inputData[sourcePoint] <= '9')) { - if ((inputData[sourcePoint + 1] >= '0') && (inputData[sourcePoint + 1] <= '9')) { - // remaining data consists of two numeric digits - data[targetPoint] = (10 * (inputData[sourcePoint] - '0')) - + (inputData[sourcePoint + 1] - '0') + 130; - targetPoint++; - sourcePoint += 2; - isTwoDigits = true; - } - } - } - - if (!(isTwoDigits)) { - if (inputData[sourcePoint] == FNC1) { - if ((length - sourcePoint) >= 15) { /* Step B4 */ - j = 0; - - for (i = 0; i < 15; i++) { - if ((inputData[sourcePoint + i] >= '0') - && (inputData[sourcePoint + i] <= '9')) { - j++; - } - } - - if (j == 15) { - data[targetPoint] = 236; /* FNC1 and change to Decimal */ - targetPoint++; - sourcePoint++; - next_mode = Mode.C1_DECIMAL; - } - } - - if ((length - sourcePoint) >= 7) { /* Step B5 */ - j = 0; - - for (i = 0; i < 7; i++) { - if ((inputData[sourcePoint + i] >= '0') - && (inputData[sourcePoint + i] <= '9')) { - j++; - } - } - - if (j == 7) { - latch = false; - for (i = sourcePoint + 7; i < length; i++) { - if (!((inputData[sourcePoint + i] >= '0') - && (inputData[sourcePoint + i] <= '9'))) { - latch = true; - } - } - - if (!(latch)) { - data[targetPoint] = 236; /* FNC1 and change to Decimal */ - targetPoint++; - sourcePoint++; - next_mode = Mode.C1_DECIMAL; - } - } - } - } - - if (next_mode == Mode.C1_ASCII) { - - /* Step B6 */ - next_mode = lookAheadTest(length, sourcePoint, current_mode); - - if (next_mode == Mode.C1_ASCII) { - if (inputData[sourcePoint] > 127) { - /* Step B7 */ - data[targetPoint] = 235; - targetPoint++; /* FNC4 */ - data[targetPoint] = (inputData[sourcePoint] - 128) + 1; - targetPoint++; - sourcePoint++; - } else { - /* Step B8 */ - if (inputData[sourcePoint] == FNC1) { - data[targetPoint] = 232; - targetPoint++; - sourcePoint++; /* FNC1 */ - } else { - data[targetPoint] = inputData[sourcePoint] + 1; - targetPoint++; - sourcePoint++; - } - } - } - } - } - } - } - - if (current_mode == Mode.C1_C40) { /* Step C - C40 encodation */ - done = false; - next_mode = Mode.C1_C40; - if (c40_p == 0) { - if ((length - sourcePoint) >= 12) { - j = 0; - - for (i = 0; i < 12; i++) { - if ((inputData[sourcePoint + i] >= '0') - && (inputData[sourcePoint + i] <= '9')) { - j++; - } - } - - if (j == 12) { - next_mode = Mode.C1_ASCII; - done = true; - } - } - - if ((length - sourcePoint) >= 8) { - j = 0; - - for (i = 0; i < 8; i++) { - if ((inputData[sourcePoint + i] >= '0') - && (inputData[sourcePoint + i] <= '9')) { - j++; - } - } - - if ((length - sourcePoint) == 8) { - latch = true; - } else { - latch = true; - for (j = sourcePoint + 8; j < length; j++) { - if ((inputData[j] <= '0') || (inputData[j] >= '9')) { - latch = false; - } - } - } - - if ((j == 8) && latch) { - next_mode = Mode.C1_ASCII; - done = true; - } - } - - if (!(done)) { - next_mode = lookAheadTest(length, sourcePoint, current_mode); - } - } - - if (next_mode != Mode.C1_C40) { - data[targetPoint] = 255; - targetPoint++; /* Unlatch */ - } else { - if (inputData[sourcePoint] > 127) { - c40_buffer[c40_p] = 1; - c40_p++; - c40_buffer[c40_p] = 30; - c40_p++; /* Upper Shift */ - shift_set = C40_SHIFT[inputData[sourcePoint] - 128]; - value = C40_VALUE[inputData[sourcePoint] - 128]; - } else { - shift_set = C40_SHIFT[inputData[sourcePoint]]; - value = C40_VALUE[inputData[sourcePoint]]; - } - - if (inputData[sourcePoint] == FNC1) { - shift_set = 2; - value = 27; /* FNC1 */ - } - - if (shift_set != 0) { - c40_buffer[c40_p] = shift_set - 1; - c40_p++; - } - c40_buffer[c40_p] = value; - c40_p++; - - if (c40_p >= 3) { - int iv; - - iv = (1600 * c40_buffer[0]) + (40 * c40_buffer[1]) - + (c40_buffer[2]) + 1; - data[targetPoint] = iv / 256; - targetPoint++; - data[targetPoint] = iv % 256; - targetPoint++; - - c40_buffer[0] = c40_buffer[3]; - c40_buffer[1] = c40_buffer[4]; - c40_buffer[2] = c40_buffer[5]; - c40_buffer[3] = 0; - c40_buffer[4] = 0; - c40_buffer[5] = 0; - c40_p -= 3; - } - sourcePoint++; - } - } - - if (current_mode == Mode.C1_TEXT) { /* Step D - Text encodation */ - done = false; - next_mode = Mode.C1_TEXT; - if (text_p == 0) { - if ((length - sourcePoint) >= 12) { - j = 0; - - for (i = 0; i < 12; i++) { - if ((inputData[sourcePoint + i] >= '0') - && (inputData[sourcePoint + i] <= '9')) { - j++; - } - } - - if (j == 12) { - next_mode = Mode.C1_ASCII; - done = true; - } - } - - if ((length - sourcePoint) >= 8) { - j = 0; - - for (i = 0; i < 8; i++) { - if ((inputData[sourcePoint + i] >= '0') - && (inputData[sourcePoint + i] <= '9')) { - j++; - } - } - - if ((length - sourcePoint) == 8) { - latch = true; - } else { - latch = true; - for (j = sourcePoint + 8; j < length; j++) { - if ((inputData[j] <= '0') || (inputData[j] >= '9')) { - latch = false; - } - } - } - - if ((j == 8) && latch) { - next_mode = Mode.C1_ASCII; - done = true; - } - } - - if (!(done)) { - next_mode = lookAheadTest(length, sourcePoint, current_mode); - } - } - - if (next_mode != Mode.C1_TEXT) { - data[targetPoint] = 255; - targetPoint++; /* Unlatch */ - } else { - if (inputData[sourcePoint] > 127) { - text_buffer[text_p] = 1; - text_p++; - text_buffer[text_p] = 30; - text_p++; /* Upper Shift */ - shift_set = TEXT_SHIFT[inputData[sourcePoint] - 128]; - value = TEXT_VALUE[inputData[sourcePoint] - 128]; - } else { - shift_set = TEXT_SHIFT[inputData[sourcePoint]]; - value = TEXT_VALUE[inputData[sourcePoint]]; - } - - if (inputData[sourcePoint] == FNC1) { - shift_set = 2; - value = 27; /* FNC1 */ - } - - if (shift_set != 0) { - text_buffer[text_p] = shift_set - 1; - text_p++; - } - text_buffer[text_p] = value; - text_p++; - - if (text_p >= 3) { - int iv; - - iv = (1600 * text_buffer[0]) + (40 * text_buffer[1]) - + (text_buffer[2]) + 1; - data[targetPoint] = iv / 256; - targetPoint++; - data[targetPoint] = iv % 256; - targetPoint++; - - text_buffer[0] = text_buffer[3]; - text_buffer[1] = text_buffer[4]; - text_buffer[2] = text_buffer[5]; - text_buffer[3] = 0; - text_buffer[4] = 0; - text_buffer[5] = 0; - text_p -= 3; - } - sourcePoint++; - } - } - - if (current_mode == Mode.C1_EDI) { /* Step E - EDI Encodation */ - - value = 0; - next_mode = Mode.C1_EDI; - if (edi_p == 0) { - if ((length - sourcePoint) >= 12) { - j = 0; - - for (i = 0; i < 12; i++) { - if ((inputData[sourcePoint + i] >= '0') - && (inputData[sourcePoint + i] <= '9')) { - j++; - } - } - - if (j == 12) { - next_mode = Mode.C1_ASCII; - } - } - - if ((length - sourcePoint) >= 8) { - j = 0; - - for (i = 0; i < 8; i++) { - if ((inputData[sourcePoint + i] >= '0') && - (inputData[sourcePoint + i] <= '9')) { - j++; - } - } - - if ((length - sourcePoint) == 8) { - latch = true; - } else { - latch = true; - for (j = sourcePoint + 8; j < length; j++) { - if ((inputData[j] <= '0') || (inputData[j] >= '9')) { - latch = false; - } - } - } - - if ((j == 8) && latch) { - next_mode = Mode.C1_ASCII; - } - } - - if (!((isEdiEncodable(inputData[sourcePoint]) - && isEdiEncodable(inputData[sourcePoint + 1])) - && isEdiEncodable(inputData[sourcePoint + 2]))) { - next_mode = Mode.C1_ASCII; - } - } - - if (next_mode != Mode.C1_EDI) { - data[targetPoint] = 255; - targetPoint++; /* Unlatch */ - } else { - if (inputData[sourcePoint] == 13) { - value = 0; - } - if (inputData[sourcePoint] == '*') { - value = 1; - } - if (inputData[sourcePoint] == '>') { - value = 2; - } - if (inputData[sourcePoint] == ' ') { - value = 3; - } - if ((inputData[sourcePoint] >= '0') && (inputData[sourcePoint] <= '9')) { - value = inputData[sourcePoint] - '0' + 4; - } - if ((inputData[sourcePoint] >= 'A') && (inputData[sourcePoint] <= 'Z')) { - value = inputData[sourcePoint] - 'A' + 14; - } - - edi_buffer[edi_p] = value; - edi_p++; - - if (edi_p >= 3) { - int iv; - - iv = (1600 * edi_buffer[0]) + (40 * edi_buffer[1]) - + (edi_buffer[2]) + 1; - data[targetPoint] = iv / 256; - targetPoint++; - data[targetPoint] = iv % 256; - targetPoint++; - - edi_buffer[0] = edi_buffer[3]; - edi_buffer[1] = edi_buffer[4]; - edi_buffer[2] = edi_buffer[5]; - edi_buffer[3] = 0; - edi_buffer[4] = 0; - edi_buffer[5] = 0; - edi_p -= 3; - } - sourcePoint++; - } - } - - if (current_mode == Mode.C1_DECIMAL) { /* Step F - Decimal encodation */ - - next_mode = Mode.C1_DECIMAL; - - data_left = length - sourcePoint; - decimal_count = 0; - - if (data_left >= 1) { - if ((inputData[sourcePoint] >= '0') && (inputData[sourcePoint] <= '9')) { - decimal_count = 1; - } - } - if (data_left >= 2) { - if ((decimal_count == 1) && ((inputData[sourcePoint + 1] >= '0') - && (inputData[sourcePoint + 1] <= '9'))) { - decimal_count = 2; - } - } - if (data_left >= 3) { - if ((decimal_count == 2) && ((inputData[sourcePoint + 2] >= '0') - && (inputData[sourcePoint + 2] <= '9'))) { - decimal_count = 3; - } - } - - if (decimal_count != 3) { - - /* Finish Decimal mode and go back to ASCII */ - - decimal_binary += "111111"; /* Unlatch */ - - target_count = 3; - if (decimal_binary.length() <= 16) { - target_count = 2; - } - if (decimal_binary.length() <= 8) { - target_count = 1; - } - bits_left_in_byte = (8 * target_count) - decimal_binary.length(); - if (bits_left_in_byte == 8) { - bits_left_in_byte = 0; - } - - if (bits_left_in_byte == 2) { - decimal_binary += "01"; - } - - if ((bits_left_in_byte == 4) || (bits_left_in_byte == 6)) { - if (decimal_count >= 1) { - sub_value = inputData[sourcePoint] - '0' + 1; - - for (i = 0x08; i > 0; i = i >> 1) { - if ((sub_value & i) != 0) { - decimal_binary += "1"; - } else { - decimal_binary += "0"; - } - } - sourcePoint++; - } else { - decimal_binary += "1111"; - } - } - - if (bits_left_in_byte == 6) { - decimal_binary += "01"; - } - - /* Binary buffer is full - transfer to data */ - if (target_count >= 1) { - for(i = 0; i < 8; i++) { - if(decimal_binary.charAt(i) == '1') { - data[targetPoint] += 128 >> i; - } - } - targetPoint++; - } - if (target_count >= 2) { - for(i = 0; i < 8; i++) { - if(decimal_binary.charAt(8 + i) == '1') { - data[targetPoint] += 128 >> i; - } - - } - targetPoint++; - } - if (target_count == 3) { - for(i = 0; i < 8; i++) { - if(decimal_binary.charAt(16 + i) == '1') { - data[targetPoint] += 128 >> i; - } - - } - targetPoint++; - } - - next_mode = Mode.C1_ASCII; - } else { - /* There are three digits - convert the value to binary */ - value = (100 * (inputData[sourcePoint] - '0')) - + (10 * (inputData[sourcePoint + 1] - '0')) - + (inputData[sourcePoint + 2] - '0') + 1; - - for (i = 0x200; i > 0; i = i >> 1) { - if ((value & i) != 0) { - decimal_binary += "1"; - } else { - decimal_binary += "0"; - } - } - sourcePoint += 3; - } - - if (decimal_binary.length() >= 24) { - /* Binary buffer is full - transfer to data */ - for(i = 0; i < 8; i++) { - if (decimal_binary.charAt(i) == '1') { - data[targetPoint] += 128 >> i; - } - - if (decimal_binary.charAt(8 + i) == '1') { - data[targetPoint + 1] += 128 >> i; - } - - if (decimal_binary.charAt(16 + i) == '1') { - data[targetPoint + 2] += 128 >> i; - } - - } - targetPoint += 3; - - if (decimal_binary.length() > 24) { - decimal_binary = decimal_binary.substring(24); - } - } - } - - if (current_mode == Mode.C1_BYTE) { - next_mode = Mode.C1_BYTE; - - if (inputData[sourcePoint] == FNC1) { - next_mode = Mode.C1_ASCII; - } else { - if (inputData[sourcePoint] <= 127) { - next_mode = lookAheadTest(length, sourcePoint, current_mode); - } - } - - if (next_mode != Mode.C1_BYTE) { - /* Insert byte field length */ - if ((targetPoint - byte_start) <= 249) { - for (i = targetPoint; i >= byte_start; i--) { - data[i + 1] = data[i]; - } - data[byte_start] = (targetPoint - byte_start); - targetPoint++; - } else { - for (i = targetPoint; i >= byte_start; i--) { - data[i + 2] = data[i]; - } - data[byte_start] = 249 + ((targetPoint - byte_start) / 250); - data[byte_start + 1] = ((targetPoint - byte_start) % 250); - targetPoint += 2; - } - } else { - data[targetPoint] = inputData[sourcePoint]; - targetPoint++; - sourcePoint++; - } - } - - if (targetPoint > 1480) { - /* Data is too large for symbol */ - throw OkapiInputException.inputTooLong(); - } - - } while (sourcePoint < length); - - /* Empty buffers */ - if (c40_p == 2) { - int iv; - - c40_buffer[2] = 1; - iv = (1600 * c40_buffer[0]) + (40 * c40_buffer[1]) - + (c40_buffer[2]) + 1; - data[targetPoint] = iv / 256; - targetPoint++; - data[targetPoint] = iv % 256; - targetPoint++; - data[targetPoint] = 255; - targetPoint++; /* Unlatch */ - } - if (c40_p == 1) { - int iv; - - c40_buffer[1] = 1; - c40_buffer[2] = 31; /* Pad */ - iv = (1600 * c40_buffer[0]) + (40 * c40_buffer[1]) - + (c40_buffer[2]) + 1; - data[targetPoint] = iv / 256; - targetPoint++; - data[targetPoint] = iv % 256; - targetPoint++; - data[targetPoint] = 255; - targetPoint++; /* Unlatch */ - } - if (text_p == 2) { - int iv; - - text_buffer[2] = 1; - iv = (1600 * text_buffer[0]) + (40 * text_buffer[1]) - + (text_buffer[2]) + 1; - data[targetPoint] = iv / 256; - targetPoint++; - data[targetPoint] = iv % 256; - targetPoint++; - data[targetPoint] = 255; - targetPoint++; /* Unlatch */ - } - if (text_p == 1) { - int iv; - - text_buffer[1] = 1; - text_buffer[2] = 31; /* Pad */ - iv = (1600 * text_buffer[0]) + (40 * text_buffer[1]) - + (text_buffer[2]) + 1; - data[targetPoint] = iv / 256; - targetPoint++; - data[targetPoint] = iv % 256; - targetPoint++; - data[targetPoint] = 255; - targetPoint++; /* Unlatch */ - } - - if (current_mode == Mode.C1_DECIMAL) { - /* Finish Decimal mode and go back to ASCII */ - - decimal_binary += "111111"; /* Unlatch */ - - target_count = 3; - if (decimal_binary.length() <= 16) { - target_count = 2; - } - if (decimal_binary.length() <= 8) { - target_count = 1; - } - bits_left_in_byte = (8 * target_count) - decimal_binary.length(); - if (bits_left_in_byte == 8) { - bits_left_in_byte = 0; - } - - if (bits_left_in_byte == 2) { - decimal_binary += "01"; - } - - if ((bits_left_in_byte == 4) || (bits_left_in_byte == 6)) { - decimal_binary += "1111"; - } - - if (bits_left_in_byte == 6) { - decimal_binary += "01"; - } - - /* Binary buffer is full - transfer to data */ - if (target_count >= 1) { - for(i = 0; i < 8; i++) { - if(decimal_binary.charAt(i) == '1') { - data[targetPoint] += 128 >> i; - } - } - targetPoint++; - } - if (target_count >= 2) { - for(i = 0; i < 8; i++) { - if(decimal_binary.charAt(8 + i) == '1') { - data[targetPoint] += 128 >> i; - } - } - targetPoint++; - } - if (target_count == 3) { - for(i = 0; i < 8; i++) { - if(decimal_binary.charAt(16 + i) == '1') { - data[targetPoint] += 128 >> i; - } - } - targetPoint++; - } - } - - if (current_mode == Mode.C1_BYTE) { - /* Insert byte field length */ - if ((targetPoint - byte_start) <= 249) { - for (i = targetPoint; i >= byte_start; i--) { - data[i + 1] = data[i]; - } - data[byte_start] = (targetPoint - byte_start); - targetPoint++; - } else { - for (i = targetPoint; i >= byte_start; i--) { - data[i + 2] = data[i]; - } - data[byte_start] = 249 + ((targetPoint - byte_start) / 250); - data[byte_start + 1] = ((targetPoint - byte_start) % 250); - targetPoint += 2; - } - } - - /* Re-check length of data */ - if (targetPoint > 1480) { - /* Data is too large for symbol */ - throw OkapiInputException.inputTooLong(); - } - - return targetPoint; - } - - private Mode lookAheadTest(int sourcelen, int position, - Mode current_mode) { - double ascii_count, c40_count, text_count, edi_count, byte_count; - int reduced_char; - int done, best_count, sp; - Mode best_scheme; - - /* Step J */ - if (current_mode == Mode.C1_ASCII) { - ascii_count = 0.0; - c40_count = 1.0; - text_count = 1.0; - edi_count = 1.0; - byte_count = 2.0; - } else { - ascii_count = 1.0; - c40_count = 2.0; - text_count = 2.0; - edi_count = 2.0; - byte_count = 3.0; - } - - switch (current_mode) { - case C1_C40: - c40_count = 0.0; - break; - case C1_TEXT: - text_count = 0.0; - break; - case C1_BYTE: - byte_count = 0.0; - break; - case C1_EDI: - edi_count = 0.0; - break; - } - - for (sp = position; - (sp < sourcelen) && (sp <= (position + 8)); sp++) { - - if (inputData[sp] <= 127) { - reduced_char = inputData[sp]; - } else { - reduced_char = inputData[sp] - 127; - } - - /* Step L */ - if ((inputData[sp] >= '0') && (inputData[sp] <= '9')) { - ascii_count += 0.5; - } else { - ascii_count = roundUpToNextInteger(ascii_count); - if (inputData[sp] > 127) { - ascii_count += 2.0; - } else { - ascii_count += 1.0; - } - } - - /* Step M */ - done = 0; - if (reduced_char == ' ') { - c40_count += (2.0 / 3.0); - done = 1; - } - if ((reduced_char >= '0') && (reduced_char <= '9')) { - c40_count += (2.0 / 3.0); - done = 1; - } - if ((reduced_char >= 'A') && (reduced_char <= 'Z')) { - c40_count += (2.0 / 3.0); - done = 1; - } - if (inputData[sp] > 127) { - c40_count += (4.0 / 3.0); - } - if (done == 0) { - c40_count += (4.0 / 3.0); - } - - /* Step N */ - done = 0; - if (reduced_char == ' ') { - text_count += (2.0 / 3.0); - done = 1; - } - if ((reduced_char >= '0') && (reduced_char <= '9')) { - text_count += (2.0 / 3.0); - done = 1; - } - if ((reduced_char >= 'a') && (reduced_char <= 'z')) { - text_count += (2.0 / 3.0); - done = 1; - } - if (inputData[sp] > 127) { - text_count += (4.0 / 3.0); - } - if (done == 0) { - text_count += (4.0 / 3.0); - } - - /* Step O */ - done = 0; - if (inputData[sp] == 13) { - edi_count += (2.0 / 3.0); - done = 1; - } - if (inputData[sp] == '*') { - edi_count += (2.0 / 3.0); - done = 1; - } - if (inputData[sp] == '>') { - edi_count += (2.0 / 3.0); - done = 1; - } - if (inputData[sp] == ' ') { - edi_count += (2.0 / 3.0); - done = 1; - } - if ((inputData[sp] >= '0') && (inputData[sp] <= '9')) { - edi_count += (2.0 / 3.0); - done = 1; - } - if ((inputData[sp] >= 'A') && (inputData[sp] <= 'Z')) { - edi_count += (2.0 / 3.0); - done = 1; - } - if (inputData[sp] > 127) { - edi_count += (13.0 / 3.0); - } else { - if (done == 0) { - edi_count += (10.0 / 3.0); - } - } - - /* Step P */ - if (inputData[sp] == FNC1) { - byte_count += 3.0; - } else { - byte_count += 1.0; - } - - } - - ascii_count = roundUpToNextInteger(ascii_count); - c40_count = roundUpToNextInteger(c40_count); - text_count = roundUpToNextInteger(text_count); - edi_count = roundUpToNextInteger(edi_count); - byte_count = roundUpToNextInteger(byte_count); - best_scheme = Mode.C1_ASCII; - - if (sp == sourcelen) { - /* Step K */ - best_count = (int) edi_count; - - if (text_count <= best_count) { - best_count = (int) text_count; - best_scheme = Mode.C1_TEXT; - } - - if (c40_count <= best_count) { - best_count = (int) c40_count; - best_scheme = Mode.C1_C40; - } - - if (ascii_count <= best_count) { - best_count = (int) ascii_count; - best_scheme = Mode.C1_ASCII; - } - - if (byte_count <= best_count) { - best_scheme = Mode.C1_BYTE; - } - } else { - /* Step Q */ - - if (((edi_count + 1.0 <= ascii_count) - && (edi_count + 1.0 <= c40_count)) - && ((edi_count + 1.0 <= byte_count) - && (edi_count + 1.0 <= text_count))) { - best_scheme = Mode.C1_EDI; - } - - if ((c40_count + 1.0 <= ascii_count) - && (c40_count + 1.0 <= text_count)) { - - if (c40_count < edi_count) { - best_scheme = Mode.C1_C40; - } else { - if (c40_count == edi_count) { - if (preferEdi(sourcelen, position)) { - best_scheme = Mode.C1_EDI; - } else { - best_scheme = Mode.C1_C40; - } - } - } - } - - if (((text_count + 1.0 <= ascii_count) - && (text_count + 1.0 <= c40_count)) - && ((text_count + 1.0 <= byte_count) - && (text_count + 1.0 <= edi_count))) { - best_scheme = Mode.C1_TEXT; - } - - if (((ascii_count + 1.0 <= byte_count) - && (ascii_count + 1.0 <= c40_count)) - && ((ascii_count + 1.0 <= text_count) - && (ascii_count + 1.0 <= edi_count))) { - best_scheme = Mode.C1_ASCII; - } - - if (((byte_count + 1.0 <= ascii_count) - && (byte_count + 1.0 <= c40_count)) - && ((byte_count + 1.0 <= text_count) - && (byte_count + 1.0 <= edi_count))) { - best_scheme = Mode.C1_BYTE; - } - } - - return best_scheme; - } - - private double roundUpToNextInteger(double input) { - double fraction, output; - - fraction = input - (int) input; - if (fraction > 0.01) { - output = (input - fraction) + 1.0; - } else { - output = input; - } - - return output; - } - - private boolean preferEdi(int sourcelen, int position) { - int i; - - for (i = position; isEdiEncodable(inputData[position + i]) - && ((position + i) < sourcelen); i++); - - if ((position + i) == sourcelen) { - /* Reached end of input */ - return false; - } - - if (inputData[position + i - 1] == 13) { - return true; - } - if (inputData[position + i - 1] == '*') { - return true; - } - if (inputData[position + i - 1] == '>') { - return true; - } - - return false; - } - - private boolean isEdiEncodable(int input) { - boolean result = false; - - if (input == 13) { - result = true; - } - if (input == '*') { - result = true; - } - if (input == '>') { - result = true; - } - if (input == ' ') { - result = true; - } - if ((input >= '0') && (input <= '9')) { - result = true; - } - if ((input >= 'A') && (input <= 'Z')) { - result = true; - } - - return result; - } - - private void plotCentralFinder(int start_row, int row_count, int full_rows) { - for (int i = 0; i < row_count; i++) { - if (i < full_rows) { - plotHorizontalBar(start_row + (i * 2), 1); - } else { - plotHorizontalBar(start_row + (i * 2), 0); - if (i != row_count - 1) { - setGridModule(start_row + (i * 2) + 1, 1); - setGridModule(start_row + (i * 2) + 1, symbol_width - 2); - } - } - } - } - - private void plotHorizontalBar(int row_no, int full) { - if (full != 0) { - for (int i = 0; i < symbol_width; i++) { - setGridModule(row_no, i); - } - } else { - for (int i = 1; i < symbol_width - 1; i++) { - setGridModule(row_no, i); - } - } - } - - private void plotVerticalBar(int column, int height, int top) { - if (top != 0) { - for (int i = 0; i < height; i++) { - setGridModule(i, column); - } - } else { - for (int i = 0; i < height; i++) { - setGridModule(row_count - i - 1, column); - } - } - } - - private void plotSpigot(int row_no) { - for (int i = symbol_width - 1; i > 0; i--) { - if (outputGrid[row_no][i - 1]) { - setGridModule(row_no, i); - } - } - } - - private void plotDataBlock(int start_row, int start_col, int height, int width, int row_offset, int col_offset) { - for (int i = start_row; i < (start_row + height); i++) { - for (int j = start_col; j < (start_col + width); j++) { - if (datagrid[i][j] == '1') { - setGridModule(i + row_offset, j + col_offset); - } - } - } - } - - private void setGridModule(int row, int column) { - outputGrid[row][column] = true; - } - - private void resetGridModule(int row, int column) { - outputGrid[row][column] = false; - } - - private static int getSize(Version version) { - switch(version) { - case A: - return 1; - case B: - return 2; - case C: - return 3; - case D: - return 4; - case E: - return 5; - case F: - return 6; - case G: - return 7; - case H: - return 8; - default: - return 0; - } - } -} +/* + * Copyright 2014 Robin Stuart + * + * 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. + */ + +package uk.org.okapibarcode.backend; + +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; + +/** + *

Implements Code One. + * + *

Code One is able to encode the ISO 8859-1 (Latin-1) character set or GS1 + * data. There are two types of Code One symbol: variable height symbols + * which are roughly square (versions A thought to H) and fixed-height + * versions (version S and T). Version S symbols can only encode numeric data. + * The width of version S and version T symbols is determined by the length + * of the input data. + * + * @author Robin Stuart + */ +public class CodeOne extends Symbol { + + public enum Version { + NONE, A, B, C, D, E, F, G, H, S, T + } + + private static final int[] C40_SHIFT = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3 + }; + + private static final int[] C40_VALUE = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 3, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, + 17, 18, 19, 20, 21, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 22, 23, 24, 25, 26, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 + }; + + private static final int[] TEXT_SHIFT = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, + 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, 3, 3, 3, 3 + }; + + private static final int[] TEXT_VALUE = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 3, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, + 17, 18, 19, 20, 21, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 22, 23, 24, 25, 26, 0, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 27, 28, 29, 30, 31 + }; + + private static final int[] C1_HEIGHT = { + 16, 22, 28, 40, 52, 70, 104, 148 + }; + private static final int[] C1_WIDTH = { + 18, 22, 32, 42, 54, 76, 98, 134 + }; + private static final int[] C1_DATA_LENGTH = { + 10, 19, 44, 91, 182, 370, 732, 1480 + }; + private static final int[] C1_ECC_LENGTH = { + 10, 16, 26, 44, 70, 140, 280, 560 + }; + private static final int[] C1_BLOCKS = { + 1, 1, 1, 1, 1, 2, 4, 8 + }; + private static final int[] C1_DATA_BLOCKS = { + 10, 19, 44, 91, 182, 185, 183, 185 + }; + private static final int[] C1_ECC_BLOCKS = { + 10, 16, 26, 44, 70, 70, 70, 70 + }; + private static final int[] C1_GRID_WIDTH = { + 4, 5, 7, 9, 12, 17, 22, 30 + }; + private static final int[] C1_GRID_HEIGHT = { + 5, 7, 10, 15, 21, 30, 46, 68 + }; + + private enum Mode { + C1_ASCII, C1_C40, C1_DECIMAL, C1_TEXT, C1_EDI, C1_BYTE + } + + private Version preferredVersion = Version.NONE; + + private int[] data = new int[1500]; + private int[][] datagrid = new int[136][120]; + private boolean[][] outputGrid = new boolean[148][134]; + + /** + * Sets the preferred symbol size / version. Versions A to H are square symbols. + * Version S and T are fixed height symbols. This value may be ignored if the input + * data does not fit in the specified version. + * + * @param version the preferred symbol version + */ + public void setPreferredVersion(Version version) { + preferredVersion = version; + } + + /** + * Returns the preferred symbol version. + * + * @return the preferred symbol version + */ + public Version getPreferredVersion() { + return preferredVersion; + } + + @Override + protected boolean gs1Supported() { + return true; + } + + @Override + protected void encode() { + + int size = 1, i, j, data_blocks; + int row, col; + int sub_version = 0; + int codewords; + int[] ecc = new int[600]; + int[] stream = new int[2100]; + int block_width; + int length = content.length(); + ReedSolomon rs = new ReedSolomon(); + int data_length; + int data_cw, ecc_cw; + int[] sub_data = new int[190]; + StringBuilder bin = new StringBuilder(); + + if (!content.matches("[\u0000-\u00FF]+")) { + throw OkapiInputException.invalidCharactersInInput(); + } + + if (preferredVersion == Version.S) { + /* Version S */ + + infoLine("Version: S"); + + if (length > 18) { + throw OkapiInputException.inputTooLong(); + } + + if (!content.matches("[0-9]+?")) { + throw OkapiInputException.invalidCharactersInInput(); + } + + sub_version = 3; + codewords = 12; + block_width = 6; /* Version S-30 */ + if (length <= 12) { + sub_version = 2; + codewords = 8; + block_width = 4; + } /* Version S-20 */ + if (length <= 6) { + sub_version = 1; + codewords = 4; + block_width = 2; + } /* Version S-10 */ + + BigInteger elreg = new BigInteger(content); + + for (i = 0; i < codewords; i++) { + BigInteger codewordValue = elreg.shiftRight(5 * i); + codewordValue = codewordValue.and(BigInteger.valueOf(0b11111)); + data[codewords - i - 1] = codewordValue.intValue(); + } + + logCodewords(codewords); + + rs.init_gf(0x25); + rs.init_code(codewords, 1); + rs.encode(codewords, data); + + infoLine("ECC Codeword Count: " + codewords); + + for (i = 0; i < codewords; i++) { + stream[i] = data[i]; + stream[i + codewords] = rs.getResult(codewords - i - 1); + } + + for (i = 0; i < 136; i++) { + for (j = 0; j < 120; j++) { + datagrid[i][j] = '0'; + } + } + + i = 0; + for (row = 0; row < 2; row++) { + for (col = 0; col < block_width; col++) { + if ((stream[i] & 0x10) != 0) { + datagrid[row * 2][col * 5] = '1'; + } + if ((stream[i] & 0x08) != 0) { + datagrid[row * 2][(col * 5) + 1] = '1'; + } + if ((stream[i] & 0x04) != 0) { + datagrid[row * 2][(col * 5) + 2] = '1'; + } + if ((stream[i] & 0x02) != 0) { + datagrid[(row * 2) + 1][col * 5] = '1'; + } + if ((stream[i] & 0x01) != 0) { + datagrid[(row * 2) + 1][(col * 5) + 1] = '1'; + } + if ((stream[i + 1] & 0x10) != 0) { + datagrid[row * 2][(col * 5) + 3] = '1'; + } + if ((stream[i + 1] & 0x08) != 0) { + datagrid[row * 2][(col * 5) + 4] = '1'; + } + if ((stream[i + 1] & 0x04) != 0) { + datagrid[(row * 2) + 1][(col * 5) + 2] = '1'; + } + if ((stream[i + 1] & 0x02) != 0) { + datagrid[(row * 2) + 1][(col * 5) + 3] = '1'; + } + if ((stream[i + 1] & 0x01) != 0) { + datagrid[(row * 2) + 1][(col * 5) + 4] = '1'; + } + i += 2; + } + } + + infoLine("Grid Size: " + block_width + " X " + 2); + + size = 9; + row_count = 8; + symbol_width = 10 * sub_version + 1; + } + + if (preferredVersion == Version.T) { + /* Version T */ + + infoLine("Version: T"); + + for (i = 0; i < 40; i++) { + data[i] = 0; + } + data_length = encodeAsCode1Data(); + + if (data_length > 38) { + throw OkapiInputException.inputTooLong(); + } + + size = 10; + sub_version = 3; + data_cw = 38; + ecc_cw = 22; + block_width = 12; + if (data_length <= 24) { + sub_version = 2; + data_cw = 24; + ecc_cw = 16; + block_width = 8; + } + if (data_length <= 10) { + sub_version = 1; + data_cw = 10; + ecc_cw = 10; + block_width = 4; + } + + logCodewords(data_length); + + for (i = data_length; i < data_cw; i++) { + data[i] = 129; /* Pad */ + } + + /* Calculate error correction data */ + rs.init_gf(0x12d); + rs.init_code(ecc_cw, 1); + rs.encode(data_cw, data); + + infoLine("ECC Codeword Count: " + ecc_cw); + + /* "Stream" combines data and error correction data */ + for (i = 0; i < data_cw; i++) { + stream[i] = data[i]; + } + for (i = 0; i < ecc_cw; i++) { + stream[data_cw + i] = rs.getResult(ecc_cw - i - 1); + } + + for (i = 0; i < 136; i++) { + for (j = 0; j < 120; j++) { + datagrid[i][j] = '0'; + } + } + + i = 0; + for (row = 0; row < 5; row++) { + for (col = 0; col < block_width; col++) { + if ((stream[i] & 0x80) != 0) { + datagrid[row * 2][col * 4] = '1'; + } + if ((stream[i] & 0x40) != 0) { + datagrid[row * 2][(col * 4) + 1] = '1'; + } + if ((stream[i] & 0x20) != 0) { + datagrid[row * 2][(col * 4) + 2] = '1'; + } + if ((stream[i] & 0x10) != 0) { + datagrid[row * 2][(col * 4) + 3] = '1'; + } + if ((stream[i] & 0x08) != 0) { + datagrid[(row * 2) + 1][col * 4] = '1'; + } + if ((stream[i] & 0x04) != 0) { + datagrid[(row * 2) + 1][(col * 4) + 1] = '1'; + } + if ((stream[i] & 0x02) != 0) { + datagrid[(row * 2) + 1][(col * 4) + 2] = '1'; + } + if ((stream[i] & 0x01) != 0) { + datagrid[(row * 2) + 1][(col * 4) + 3] = '1'; + } + i++; + } + } + + infoLine("Grid Size: " + block_width + " X " + 5); + + row_count = 16; + symbol_width = (sub_version * 16) + 1; + } + + if ((preferredVersion != Version.S) && (preferredVersion != Version.T)) { + /* Version A to H */ + for (i = 0; i < 1500; i++) { + data[i] = 0; + } + data_length = encodeAsCode1Data(); + + for (i = 7; i >= 0; i--) { + if (C1_DATA_LENGTH[i] >= data_length) { + size = i + 1; + } + } + + if (getSize(preferredVersion) > size) { + size = getSize(preferredVersion); + } + + char version = (char) ((size - 1) + 'A'); + infoLine("Version: " + version); + logCodewords(data_length); + + for (i = data_length; i < C1_DATA_LENGTH[size - 1]; i++) { + data[i] = 129; /* Pad */ + } + + /* Calculate error correction data */ + data_length = C1_DATA_LENGTH[size - 1]; + + data_blocks = C1_BLOCKS[size - 1]; + + rs.init_gf(0x12d); + rs.init_code(C1_ECC_BLOCKS[size - 1], 0); + for (i = 0; i < data_blocks; i++) { + for (j = 0; j < C1_DATA_BLOCKS[size - 1]; j++) { + sub_data[j] = data[j * data_blocks + i]; + } + rs.encode(C1_DATA_BLOCKS[size - 1], sub_data); + for (j = 0; j < C1_ECC_BLOCKS[size - 1]; j++) { + ecc[C1_ECC_LENGTH[size - 1] - (j * data_blocks + i) - 1] = rs.getResult(j); + } + } + + infoLine("ECC Codeword Count: " + C1_ECC_LENGTH[size - 1]); + + /* "Stream" combines data and error correction data */ + for (i = 0; i < data_length; i++) { + stream[i] = data[i]; + } + for (i = 0; i < C1_ECC_LENGTH[size - 1]; i++) { + stream[data_length + i] = ecc[i]; + } + + for (i = 0; i < 136; i++) { + for (j = 0; j < 120; j++) { + datagrid[i][j] = '0'; + } + } + + i = 0; + for (row = 0; row < C1_GRID_HEIGHT[size - 1]; row++) { + for (col = 0; col < C1_GRID_WIDTH[size - 1]; col++) { + if ((stream[i] & 0x80) != 0) { + datagrid[row * 2][col * 4] = '1'; + } + if ((stream[i] & 0x40) != 0) { + datagrid[row * 2][(col * 4) + 1] = '1'; + } + if ((stream[i] & 0x20) != 0) { + datagrid[row * 2][(col * 4) + 2] = '1'; + } + if ((stream[i] & 0x10) != 0) { + datagrid[row * 2][(col * 4) + 3] = '1'; + } + if ((stream[i] & 0x08) != 0) { + datagrid[(row * 2) + 1][col * 4] = '1'; + } + if ((stream[i] & 0x04) != 0) { + datagrid[(row * 2) + 1][(col * 4) + 1] = '1'; + } + if ((stream[i] & 0x02) != 0) { + datagrid[(row * 2) + 1][(col * 4) + 2] = '1'; + } + if ((stream[i] & 0x01) != 0) { + datagrid[(row * 2) + 1][(col * 4) + 3] = '1'; + } + i++; + } + } + + infoLine("Grid Size: " + C1_GRID_WIDTH[size - 1] + " X " + C1_GRID_HEIGHT[size - 1]); + + row_count = C1_HEIGHT[size - 1]; + symbol_width = C1_WIDTH[size - 1]; + } + + for (i = 0; i < 148; i++) { + for (j = 0; j < 134; j++) { + outputGrid[i][j] = false; + } + } + + switch (size) { + case 1: + /* Version A */ + plotCentralFinder(6, 3, 1); + plotVerticalBar(4, 6, 1); + plotVerticalBar(12, 5, 0); + setGridModule(5, 12); + plotSpigot(0); + plotSpigot(15); + plotDataBlock(0, 0, 5, 4, 0, 0); + plotDataBlock(0, 4, 5, 12, 0, 2); + plotDataBlock(5, 0, 5, 12, 6, 0); + plotDataBlock(5, 12, 5, 4, 6, 2); + break; + case 2: + /* Version B */ + plotCentralFinder(8, 4, 1); + plotVerticalBar(4, 8, 1); + plotVerticalBar(16, 7, 0); + setGridModule(7, 16); + plotSpigot(0); + plotSpigot(21); + plotDataBlock(0, 0, 7, 4, 0, 0); + plotDataBlock(0, 4, 7, 16, 0, 2); + plotDataBlock(7, 0, 7, 16, 8, 0); + plotDataBlock(7, 16, 7, 4, 8, 2); + break; + case 3: + /* Version C */ + plotCentralFinder(11, 4, 2); + plotVerticalBar(4, 11, 1); + plotVerticalBar(26, 13, 1); + plotVerticalBar(4, 10, 0); + plotVerticalBar(26, 10, 0); + plotSpigot(0); + plotSpigot(27); + plotDataBlock(0, 0, 10, 4, 0, 0); + plotDataBlock(0, 4, 10, 20, 0, 2); + plotDataBlock(0, 24, 10, 4, 0, 4); + plotDataBlock(10, 0, 10, 4, 8, 0); + plotDataBlock(10, 4, 10, 20, 8, 2); + plotDataBlock(10, 24, 10, 4, 8, 4); + break; + case 4: + /* Version D */ + plotCentralFinder(16, 5, 1); + plotVerticalBar(4, 16, 1); + plotVerticalBar(20, 16, 1); + plotVerticalBar(36, 16, 1); + plotVerticalBar(4, 15, 0); + plotVerticalBar(20, 15, 0); + plotVerticalBar(36, 15, 0); + plotSpigot(0); + plotSpigot(12); + plotSpigot(27); + plotSpigot(39); + plotDataBlock(0, 0, 15, 4, 0, 0); + plotDataBlock(0, 4, 15, 14, 0, 2); + plotDataBlock(0, 18, 15, 14, 0, 4); + plotDataBlock(0, 32, 15, 4, 0, 6); + plotDataBlock(15, 0, 15, 4, 10, 0); + plotDataBlock(15, 4, 15, 14, 10, 2); + plotDataBlock(15, 18, 15, 14, 10, 4); + plotDataBlock(15, 32, 15, 4, 10, 6); + break; + case 5: + /* Version E */ + plotCentralFinder(22, 5, 2); + plotVerticalBar(4, 22, 1); + plotVerticalBar(26, 24, 1); + plotVerticalBar(48, 22, 1); + plotVerticalBar(4, 21, 0); + plotVerticalBar(26, 21, 0); + plotVerticalBar(48, 21, 0); + plotSpigot(0); + plotSpigot(12); + plotSpigot(39); + plotSpigot(51); + plotDataBlock(0, 0, 21, 4, 0, 0); + plotDataBlock(0, 4, 21, 20, 0, 2); + plotDataBlock(0, 24, 21, 20, 0, 4); + plotDataBlock(0, 44, 21, 4, 0, 6); + plotDataBlock(21, 0, 21, 4, 10, 0); + plotDataBlock(21, 4, 21, 20, 10, 2); + plotDataBlock(21, 24, 21, 20, 10, 4); + plotDataBlock(21, 44, 21, 4, 10, 6); + break; + case 6: + /* Version F */ + plotCentralFinder(31, 5, 3); + plotVerticalBar(4, 31, 1); + plotVerticalBar(26, 35, 1); + plotVerticalBar(48, 31, 1); + plotVerticalBar(70, 35, 1); + plotVerticalBar(4, 30, 0); + plotVerticalBar(26, 30, 0); + plotVerticalBar(48, 30, 0); + plotVerticalBar(70, 30, 0); + plotSpigot(0); + plotSpigot(12); + plotSpigot(24); + plotSpigot(45); + plotSpigot(57); + plotSpigot(69); + plotDataBlock(0, 0, 30, 4, 0, 0); + plotDataBlock(0, 4, 30, 20, 0, 2); + plotDataBlock(0, 24, 30, 20, 0, 4); + plotDataBlock(0, 44, 30, 20, 0, 6); + plotDataBlock(0, 64, 30, 4, 0, 8); + plotDataBlock(30, 0, 30, 4, 10, 0); + plotDataBlock(30, 4, 30, 20, 10, 2); + plotDataBlock(30, 24, 30, 20, 10, 4); + plotDataBlock(30, 44, 30, 20, 10, 6); + plotDataBlock(30, 64, 30, 4, 10, 8); + break; + case 7: + /* Version G */ + plotCentralFinder(47, 6, 2); + plotVerticalBar(6, 47, 1); + plotVerticalBar(27, 49, 1); + plotVerticalBar(48, 47, 1); + plotVerticalBar(69, 49, 1); + plotVerticalBar(90, 47, 1); + plotVerticalBar(6, 46, 0); + plotVerticalBar(27, 46, 0); + plotVerticalBar(48, 46, 0); + plotVerticalBar(69, 46, 0); + plotVerticalBar(90, 46, 0); + plotSpigot(0); + plotSpigot(12); + plotSpigot(24); + plotSpigot(36); + plotSpigot(67); + plotSpigot(79); + plotSpigot(91); + plotSpigot(103); + plotDataBlock(0, 0, 46, 6, 0, 0); + plotDataBlock(0, 6, 46, 19, 0, 2); + plotDataBlock(0, 25, 46, 19, 0, 4); + plotDataBlock(0, 44, 46, 19, 0, 6); + plotDataBlock(0, 63, 46, 19, 0, 8); + plotDataBlock(0, 82, 46, 6, 0, 10); + plotDataBlock(46, 0, 46, 6, 12, 0); + plotDataBlock(46, 6, 46, 19, 12, 2); + plotDataBlock(46, 25, 46, 19, 12, 4); + plotDataBlock(46, 44, 46, 19, 12, 6); + plotDataBlock(46, 63, 46, 19, 12, 8); + plotDataBlock(46, 82, 46, 6, 12, 10); + break; + case 8: + /* Version H */ + plotCentralFinder(69, 6, 3); + plotVerticalBar(6, 69, 1); + plotVerticalBar(26, 73, 1); + plotVerticalBar(46, 69, 1); + plotVerticalBar(66, 73, 1); + plotVerticalBar(86, 69, 1); + plotVerticalBar(106, 73, 1); + plotVerticalBar(126, 69, 1); + plotVerticalBar(6, 68, 0); + plotVerticalBar(26, 68, 0); + plotVerticalBar(46, 68, 0); + plotVerticalBar(66, 68, 0); + plotVerticalBar(86, 68, 0); + plotVerticalBar(106, 68, 0); + plotVerticalBar(126, 68, 0); + plotSpigot(0); + plotSpigot(12); + plotSpigot(24); + plotSpigot(36); + plotSpigot(48); + plotSpigot(60); + plotSpigot(87); + plotSpigot(99); + plotSpigot(111); + plotSpigot(123); + plotSpigot(135); + plotSpigot(147); + plotDataBlock(0, 0, 68, 6, 0, 0); + plotDataBlock(0, 6, 68, 18, 0, 2); + plotDataBlock(0, 24, 68, 18, 0, 4); + plotDataBlock(0, 42, 68, 18, 0, 6); + plotDataBlock(0, 60, 68, 18, 0, 8); + plotDataBlock(0, 78, 68, 18, 0, 10); + plotDataBlock(0, 96, 68, 18, 0, 12); + plotDataBlock(0, 114, 68, 6, 0, 14); + plotDataBlock(68, 0, 68, 6, 12, 0); + plotDataBlock(68, 6, 68, 18, 12, 2); + plotDataBlock(68, 24, 68, 18, 12, 4); + plotDataBlock(68, 42, 68, 18, 12, 6); + plotDataBlock(68, 60, 68, 18, 12, 8); + plotDataBlock(68, 78, 68, 18, 12, 10); + plotDataBlock(68, 96, 68, 18, 12, 12); + plotDataBlock(68, 114, 68, 6, 12, 14); + break; + case 9: + /* Version S */ + plotHorizontalBar(5, 1); + plotHorizontalBar(7, 1); + setGridModule(6, 0); + setGridModule(6, symbol_width - 1); + resetGridModule(7, 1); + resetGridModule(7, symbol_width - 2); + switch (sub_version) { + case 1: + /* Version S-10 */ + setGridModule(0, 5); + plotDataBlock(0, 0, 4, 5, 0, 0); + plotDataBlock(0, 5, 4, 5, 0, 1); + break; + case 2: + /* Version S-20 */ + setGridModule(0, 10); + setGridModule(4, 10); + plotDataBlock(0, 0, 4, 10, 0, 0); + plotDataBlock(0, 10, 4, 10, 0, 1); + break; + case 3: + /* Version S-30 */ + setGridModule(0, 15); + setGridModule(4, 15); + setGridModule(6, 15); + plotDataBlock(0, 0, 4, 15, 0, 0); + plotDataBlock(0, 15, 4, 15, 0, 1); + break; + } + break; + case 10: + /* Version T */ + plotHorizontalBar(11, 1); + plotHorizontalBar(13, 1); + plotHorizontalBar(15, 1); + setGridModule(12, 0); + setGridModule(12, symbol_width - 1); + setGridModule(14, 0); + setGridModule(14, symbol_width - 1); + resetGridModule(13, 1); + resetGridModule(13, symbol_width - 2); + resetGridModule(15, 1); + resetGridModule(15, symbol_width - 2); + switch (sub_version) { + case 1: + /* Version T-16 */ + setGridModule(0, 8); + setGridModule(10, 8); + plotDataBlock(0, 0, 10, 8, 0, 0); + plotDataBlock(0, 8, 10, 8, 0, 1); + break; + case 2: + /* Version T-32 */ + setGridModule(0, 16); + setGridModule(10, 16); + setGridModule(12, 16); + plotDataBlock(0, 0, 10, 16, 0, 0); + plotDataBlock(0, 16, 10, 16, 0, 1); + break; + case 3: + /* Verion T-48 */ + setGridModule(0, 24); + setGridModule(10, 24); + setGridModule(12, 24); + setGridModule(14, 24); + plotDataBlock(0, 0, 10, 24, 0, 0); + plotDataBlock(0, 24, 10, 24, 0, 1); + break; + } + break; + } + + readable = ""; + pattern = new String[row_count]; + row_height = new int[row_count]; + for (i = 0; i < row_count; i++) { + bin.setLength(0); + for (j = 0; j < symbol_width; j++) { + if (outputGrid[i][j]) { + bin.append('1'); + } else { + bin.append('0'); + } + } + pattern[i] = bin2pat(bin); + row_height[i] = 1; + } + } + + private void logCodewords(int count) { + info("Codewords: "); + for (int i = 0; i < count; i++) { + infoSpace(data[i]); + } + infoLine(); + } + + private int encodeAsCode1Data() { + Mode current_mode, next_mode; + boolean latch; + boolean done; + int sourcePoint, targetPoint, i, j; + int c40_p; + int text_p; + int edi_p; + int byte_start = 0; + int[] c40_buffer = new int[6]; + int[] text_buffer = new int[6]; + int[] edi_buffer = new int[6]; + String decimal_binary = ""; + int length; + int shift_set, value; + int data_left, decimal_count; + int sub_value; + int bits_left_in_byte, target_count; + boolean isTwoDigits; + + inputData = toBytes(content, StandardCharsets.ISO_8859_1); + length = inputData.length; + + sourcePoint = 0; + targetPoint = 0; + c40_p = 0; + text_p = 0; + edi_p = 0; + + if (inputDataType == DataType.GS1) { + data[targetPoint] = 232; + targetPoint++; + } /* FNC1 */ + + /* Step A */ + current_mode = Mode.C1_ASCII; + next_mode = Mode.C1_ASCII; + + do { + if (current_mode != next_mode) { + /* Change mode */ + switch (next_mode) { + case C1_C40: + data[targetPoint] = 230; + targetPoint++; + break; + case C1_TEXT: + data[targetPoint] = 239; + targetPoint++; + break; + case C1_EDI: + data[targetPoint] = 238; + targetPoint++; + break; + case C1_BYTE: + data[targetPoint] = 231; + targetPoint++; + break; + } + } + + if ((current_mode != Mode.C1_BYTE) && (next_mode == Mode.C1_BYTE)) { + byte_start = targetPoint; + } + current_mode = next_mode; + + if (current_mode == Mode.C1_ASCII) { /* Step B - ASCII encodation */ + next_mode = Mode.C1_ASCII; + + if ((length - sourcePoint) >= 21) { /* Step B1 */ + j = 0; + + for (i = 0; i < 21; i++) { + if ((inputData[sourcePoint + i] >= '0') && (inputData[sourcePoint + i] <= '9')) { + j++; + } + } + + if (j == 21) { + next_mode = Mode.C1_DECIMAL; + decimal_binary += "1111"; + } + } + + if ((next_mode == Mode.C1_ASCII) && ((length - sourcePoint) >= 13)) { /* Step B2 */ + j = 0; + + for (i = 0; i < 13; i++) { + if ((inputData[sourcePoint + i] >= '0') && (inputData[sourcePoint + i] <= '9')) { + j++; + } + } + + if (j == 13) { + latch = false; + for (i = sourcePoint + 13; i < length; i++) { + if (!((inputData[i] >= '0') && + (inputData[i] <= '9'))) { + latch = true; + } + } + + if (!(latch)) { + next_mode = Mode.C1_DECIMAL; + decimal_binary += "1111"; + } + } + } + + if (next_mode == Mode.C1_ASCII) { /* Step B3 */ + isTwoDigits = false; + if ((sourcePoint + 1) != length) { + if ((inputData[sourcePoint] >= '0') && (inputData[sourcePoint] <= '9')) { + if ((inputData[sourcePoint + 1] >= '0') && (inputData[sourcePoint + 1] <= '9')) { + // remaining data consists of two numeric digits + data[targetPoint] = (10 * (inputData[sourcePoint] - '0')) + + (inputData[sourcePoint + 1] - '0') + 130; + targetPoint++; + sourcePoint += 2; + isTwoDigits = true; + } + } + } + + if (!(isTwoDigits)) { + if (inputData[sourcePoint] == FNC1) { + if ((length - sourcePoint) >= 15) { /* Step B4 */ + j = 0; + + for (i = 0; i < 15; i++) { + if ((inputData[sourcePoint + i] >= '0') + && (inputData[sourcePoint + i] <= '9')) { + j++; + } + } + + if (j == 15) { + data[targetPoint] = 236; /* FNC1 and change to Decimal */ + targetPoint++; + sourcePoint++; + next_mode = Mode.C1_DECIMAL; + } + } + + if ((length - sourcePoint) >= 7) { /* Step B5 */ + j = 0; + + for (i = 0; i < 7; i++) { + if ((inputData[sourcePoint + i] >= '0') + && (inputData[sourcePoint + i] <= '9')) { + j++; + } + } + + if (j == 7) { + latch = false; + for (i = sourcePoint + 7; i < length; i++) { + if (!((inputData[sourcePoint + i] >= '0') + && (inputData[sourcePoint + i] <= '9'))) { + latch = true; + } + } + + if (!(latch)) { + data[targetPoint] = 236; /* FNC1 and change to Decimal */ + targetPoint++; + sourcePoint++; + next_mode = Mode.C1_DECIMAL; + } + } + } + } + + if (next_mode == Mode.C1_ASCII) { + + /* Step B6 */ + next_mode = lookAheadTest(length, sourcePoint, current_mode); + + if (next_mode == Mode.C1_ASCII) { + if (inputData[sourcePoint] > 127) { + /* Step B7 */ + data[targetPoint] = 235; + targetPoint++; /* FNC4 */ + data[targetPoint] = (inputData[sourcePoint] - 128) + 1; + targetPoint++; + sourcePoint++; + } else { + /* Step B8 */ + if (inputData[sourcePoint] == FNC1) { + data[targetPoint] = 232; + targetPoint++; + sourcePoint++; /* FNC1 */ + } else { + data[targetPoint] = inputData[sourcePoint] + 1; + targetPoint++; + sourcePoint++; + } + } + } + } + } + } + } + + if (current_mode == Mode.C1_C40) { /* Step C - C40 encodation */ + done = false; + next_mode = Mode.C1_C40; + if (c40_p == 0) { + if ((length - sourcePoint) >= 12) { + j = 0; + + for (i = 0; i < 12; i++) { + if ((inputData[sourcePoint + i] >= '0') + && (inputData[sourcePoint + i] <= '9')) { + j++; + } + } + + if (j == 12) { + next_mode = Mode.C1_ASCII; + done = true; + } + } + + if ((length - sourcePoint) >= 8) { + j = 0; + + for (i = 0; i < 8; i++) { + if ((inputData[sourcePoint + i] >= '0') + && (inputData[sourcePoint + i] <= '9')) { + j++; + } + } + + if ((length - sourcePoint) == 8) { + latch = true; + } else { + latch = true; + for (j = sourcePoint + 8; j < length; j++) { + if ((inputData[j] <= '0') || (inputData[j] >= '9')) { + latch = false; + } + } + } + + if ((j == 8) && latch) { + next_mode = Mode.C1_ASCII; + done = true; + } + } + + if (!(done)) { + next_mode = lookAheadTest(length, sourcePoint, current_mode); + } + } + + if (next_mode != Mode.C1_C40) { + data[targetPoint] = 255; + targetPoint++; /* Unlatch */ + } else { + if (inputData[sourcePoint] > 127) { + c40_buffer[c40_p] = 1; + c40_p++; + c40_buffer[c40_p] = 30; + c40_p++; /* Upper Shift */ + shift_set = C40_SHIFT[inputData[sourcePoint] - 128]; + value = C40_VALUE[inputData[sourcePoint] - 128]; + } else { + shift_set = C40_SHIFT[inputData[sourcePoint]]; + value = C40_VALUE[inputData[sourcePoint]]; + } + + if (inputData[sourcePoint] == FNC1) { + shift_set = 2; + value = 27; /* FNC1 */ + } + + if (shift_set != 0) { + c40_buffer[c40_p] = shift_set - 1; + c40_p++; + } + c40_buffer[c40_p] = value; + c40_p++; + + if (c40_p >= 3) { + int iv; + + iv = (1600 * c40_buffer[0]) + (40 * c40_buffer[1]) + + (c40_buffer[2]) + 1; + data[targetPoint] = iv / 256; + targetPoint++; + data[targetPoint] = iv % 256; + targetPoint++; + + c40_buffer[0] = c40_buffer[3]; + c40_buffer[1] = c40_buffer[4]; + c40_buffer[2] = c40_buffer[5]; + c40_buffer[3] = 0; + c40_buffer[4] = 0; + c40_buffer[5] = 0; + c40_p -= 3; + } + sourcePoint++; + } + } + + if (current_mode == Mode.C1_TEXT) { /* Step D - Text encodation */ + done = false; + next_mode = Mode.C1_TEXT; + if (text_p == 0) { + if ((length - sourcePoint) >= 12) { + j = 0; + + for (i = 0; i < 12; i++) { + if ((inputData[sourcePoint + i] >= '0') + && (inputData[sourcePoint + i] <= '9')) { + j++; + } + } + + if (j == 12) { + next_mode = Mode.C1_ASCII; + done = true; + } + } + + if ((length - sourcePoint) >= 8) { + j = 0; + + for (i = 0; i < 8; i++) { + if ((inputData[sourcePoint + i] >= '0') + && (inputData[sourcePoint + i] <= '9')) { + j++; + } + } + + if ((length - sourcePoint) == 8) { + latch = true; + } else { + latch = true; + for (j = sourcePoint + 8; j < length; j++) { + if ((inputData[j] <= '0') || (inputData[j] >= '9')) { + latch = false; + } + } + } + + if ((j == 8) && latch) { + next_mode = Mode.C1_ASCII; + done = true; + } + } + + if (!(done)) { + next_mode = lookAheadTest(length, sourcePoint, current_mode); + } + } + + if (next_mode != Mode.C1_TEXT) { + data[targetPoint] = 255; + targetPoint++; /* Unlatch */ + } else { + if (inputData[sourcePoint] > 127) { + text_buffer[text_p] = 1; + text_p++; + text_buffer[text_p] = 30; + text_p++; /* Upper Shift */ + shift_set = TEXT_SHIFT[inputData[sourcePoint] - 128]; + value = TEXT_VALUE[inputData[sourcePoint] - 128]; + } else { + shift_set = TEXT_SHIFT[inputData[sourcePoint]]; + value = TEXT_VALUE[inputData[sourcePoint]]; + } + + if (inputData[sourcePoint] == FNC1) { + shift_set = 2; + value = 27; /* FNC1 */ + } + + if (shift_set != 0) { + text_buffer[text_p] = shift_set - 1; + text_p++; + } + text_buffer[text_p] = value; + text_p++; + + if (text_p >= 3) { + int iv; + + iv = (1600 * text_buffer[0]) + (40 * text_buffer[1]) + + (text_buffer[2]) + 1; + data[targetPoint] = iv / 256; + targetPoint++; + data[targetPoint] = iv % 256; + targetPoint++; + + text_buffer[0] = text_buffer[3]; + text_buffer[1] = text_buffer[4]; + text_buffer[2] = text_buffer[5]; + text_buffer[3] = 0; + text_buffer[4] = 0; + text_buffer[5] = 0; + text_p -= 3; + } + sourcePoint++; + } + } + + if (current_mode == Mode.C1_EDI) { /* Step E - EDI Encodation */ + + value = 0; + next_mode = Mode.C1_EDI; + if (edi_p == 0) { + if ((length - sourcePoint) >= 12) { + j = 0; + + for (i = 0; i < 12; i++) { + if ((inputData[sourcePoint + i] >= '0') + && (inputData[sourcePoint + i] <= '9')) { + j++; + } + } + + if (j == 12) { + next_mode = Mode.C1_ASCII; + } + } + + if ((length - sourcePoint) >= 8) { + j = 0; + + for (i = 0; i < 8; i++) { + if ((inputData[sourcePoint + i] >= '0') && + (inputData[sourcePoint + i] <= '9')) { + j++; + } + } + + if ((length - sourcePoint) == 8) { + latch = true; + } else { + latch = true; + for (j = sourcePoint + 8; j < length; j++) { + if ((inputData[j] <= '0') || (inputData[j] >= '9')) { + latch = false; + } + } + } + + if ((j == 8) && latch) { + next_mode = Mode.C1_ASCII; + } + } + + if (!((isEdiEncodable(inputData[sourcePoint]) + && isEdiEncodable(inputData[sourcePoint + 1])) + && isEdiEncodable(inputData[sourcePoint + 2]))) { + next_mode = Mode.C1_ASCII; + } + } + + if (next_mode != Mode.C1_EDI) { + data[targetPoint] = 255; + targetPoint++; /* Unlatch */ + } else { + if (inputData[sourcePoint] == 13) { + value = 0; + } + if (inputData[sourcePoint] == '*') { + value = 1; + } + if (inputData[sourcePoint] == '>') { + value = 2; + } + if (inputData[sourcePoint] == ' ') { + value = 3; + } + if ((inputData[sourcePoint] >= '0') && (inputData[sourcePoint] <= '9')) { + value = inputData[sourcePoint] - '0' + 4; + } + if ((inputData[sourcePoint] >= 'A') && (inputData[sourcePoint] <= 'Z')) { + value = inputData[sourcePoint] - 'A' + 14; + } + + edi_buffer[edi_p] = value; + edi_p++; + + if (edi_p >= 3) { + int iv; + + iv = (1600 * edi_buffer[0]) + (40 * edi_buffer[1]) + + (edi_buffer[2]) + 1; + data[targetPoint] = iv / 256; + targetPoint++; + data[targetPoint] = iv % 256; + targetPoint++; + + edi_buffer[0] = edi_buffer[3]; + edi_buffer[1] = edi_buffer[4]; + edi_buffer[2] = edi_buffer[5]; + edi_buffer[3] = 0; + edi_buffer[4] = 0; + edi_buffer[5] = 0; + edi_p -= 3; + } + sourcePoint++; + } + } + + if (current_mode == Mode.C1_DECIMAL) { /* Step F - Decimal encodation */ + + next_mode = Mode.C1_DECIMAL; + + data_left = length - sourcePoint; + decimal_count = 0; + + if (data_left >= 1) { + if ((inputData[sourcePoint] >= '0') && (inputData[sourcePoint] <= '9')) { + decimal_count = 1; + } + } + if (data_left >= 2) { + if ((decimal_count == 1) && ((inputData[sourcePoint + 1] >= '0') + && (inputData[sourcePoint + 1] <= '9'))) { + decimal_count = 2; + } + } + if (data_left >= 3) { + if ((decimal_count == 2) && ((inputData[sourcePoint + 2] >= '0') + && (inputData[sourcePoint + 2] <= '9'))) { + decimal_count = 3; + } + } + + if (decimal_count != 3) { + + /* Finish Decimal mode and go back to ASCII */ + + decimal_binary += "111111"; /* Unlatch */ + + target_count = 3; + if (decimal_binary.length() <= 16) { + target_count = 2; + } + if (decimal_binary.length() <= 8) { + target_count = 1; + } + bits_left_in_byte = (8 * target_count) - decimal_binary.length(); + if (bits_left_in_byte == 8) { + bits_left_in_byte = 0; + } + + if (bits_left_in_byte == 2) { + decimal_binary += "01"; + } + + if ((bits_left_in_byte == 4) || (bits_left_in_byte == 6)) { + if (decimal_count >= 1) { + sub_value = inputData[sourcePoint] - '0' + 1; + + for (i = 0x08; i > 0; i = i >> 1) { + if ((sub_value & i) != 0) { + decimal_binary += "1"; + } else { + decimal_binary += "0"; + } + } + sourcePoint++; + } else { + decimal_binary += "1111"; + } + } + + if (bits_left_in_byte == 6) { + decimal_binary += "01"; + } + + /* Binary buffer is full - transfer to data */ + if (target_count >= 1) { + for(i = 0; i < 8; i++) { + if(decimal_binary.charAt(i) == '1') { + data[targetPoint] += 128 >> i; + } + } + targetPoint++; + } + if (target_count >= 2) { + for(i = 0; i < 8; i++) { + if(decimal_binary.charAt(8 + i) == '1') { + data[targetPoint] += 128 >> i; + } + + } + targetPoint++; + } + if (target_count == 3) { + for(i = 0; i < 8; i++) { + if(decimal_binary.charAt(16 + i) == '1') { + data[targetPoint] += 128 >> i; + } + + } + targetPoint++; + } + + next_mode = Mode.C1_ASCII; + } else { + /* There are three digits - convert the value to binary */ + value = (100 * (inputData[sourcePoint] - '0')) + + (10 * (inputData[sourcePoint + 1] - '0')) + + (inputData[sourcePoint + 2] - '0') + 1; + + for (i = 0x200; i > 0; i = i >> 1) { + if ((value & i) != 0) { + decimal_binary += "1"; + } else { + decimal_binary += "0"; + } + } + sourcePoint += 3; + } + + if (decimal_binary.length() >= 24) { + /* Binary buffer is full - transfer to data */ + for(i = 0; i < 8; i++) { + if (decimal_binary.charAt(i) == '1') { + data[targetPoint] += 128 >> i; + } + + if (decimal_binary.charAt(8 + i) == '1') { + data[targetPoint + 1] += 128 >> i; + } + + if (decimal_binary.charAt(16 + i) == '1') { + data[targetPoint + 2] += 128 >> i; + } + + } + targetPoint += 3; + + if (decimal_binary.length() > 24) { + decimal_binary = decimal_binary.substring(24); + } + } + } + + if (current_mode == Mode.C1_BYTE) { + next_mode = Mode.C1_BYTE; + + if (inputData[sourcePoint] == FNC1) { + next_mode = Mode.C1_ASCII; + } else { + if (inputData[sourcePoint] <= 127) { + next_mode = lookAheadTest(length, sourcePoint, current_mode); + } + } + + if (next_mode != Mode.C1_BYTE) { + /* Insert byte field length */ + if ((targetPoint - byte_start) <= 249) { + for (i = targetPoint; i >= byte_start; i--) { + data[i + 1] = data[i]; + } + data[byte_start] = (targetPoint - byte_start); + targetPoint++; + } else { + for (i = targetPoint; i >= byte_start; i--) { + data[i + 2] = data[i]; + } + data[byte_start] = 249 + ((targetPoint - byte_start) / 250); + data[byte_start + 1] = ((targetPoint - byte_start) % 250); + targetPoint += 2; + } + } else { + data[targetPoint] = inputData[sourcePoint]; + targetPoint++; + sourcePoint++; + } + } + + if (targetPoint > 1480) { + /* Data is too large for symbol */ + throw OkapiInputException.inputTooLong(); + } + + } while (sourcePoint < length); + + /* Empty buffers */ + if (c40_p == 2) { + int iv; + + c40_buffer[2] = 1; + iv = (1600 * c40_buffer[0]) + (40 * c40_buffer[1]) + + (c40_buffer[2]) + 1; + data[targetPoint] = iv / 256; + targetPoint++; + data[targetPoint] = iv % 256; + targetPoint++; + data[targetPoint] = 255; + targetPoint++; /* Unlatch */ + } + if (c40_p == 1) { + int iv; + + c40_buffer[1] = 1; + c40_buffer[2] = 31; /* Pad */ + iv = (1600 * c40_buffer[0]) + (40 * c40_buffer[1]) + + (c40_buffer[2]) + 1; + data[targetPoint] = iv / 256; + targetPoint++; + data[targetPoint] = iv % 256; + targetPoint++; + data[targetPoint] = 255; + targetPoint++; /* Unlatch */ + } + if (text_p == 2) { + int iv; + + text_buffer[2] = 1; + iv = (1600 * text_buffer[0]) + (40 * text_buffer[1]) + + (text_buffer[2]) + 1; + data[targetPoint] = iv / 256; + targetPoint++; + data[targetPoint] = iv % 256; + targetPoint++; + data[targetPoint] = 255; + targetPoint++; /* Unlatch */ + } + if (text_p == 1) { + int iv; + + text_buffer[1] = 1; + text_buffer[2] = 31; /* Pad */ + iv = (1600 * text_buffer[0]) + (40 * text_buffer[1]) + + (text_buffer[2]) + 1; + data[targetPoint] = iv / 256; + targetPoint++; + data[targetPoint] = iv % 256; + targetPoint++; + data[targetPoint] = 255; + targetPoint++; /* Unlatch */ + } + + if (current_mode == Mode.C1_DECIMAL) { + /* Finish Decimal mode and go back to ASCII */ + + decimal_binary += "111111"; /* Unlatch */ + + target_count = 3; + if (decimal_binary.length() <= 16) { + target_count = 2; + } + if (decimal_binary.length() <= 8) { + target_count = 1; + } + bits_left_in_byte = (8 * target_count) - decimal_binary.length(); + if (bits_left_in_byte == 8) { + bits_left_in_byte = 0; + } + + if (bits_left_in_byte == 2) { + decimal_binary += "01"; + } + + if ((bits_left_in_byte == 4) || (bits_left_in_byte == 6)) { + decimal_binary += "1111"; + } + + if (bits_left_in_byte == 6) { + decimal_binary += "01"; + } + + /* Binary buffer is full - transfer to data */ + if (target_count >= 1) { + for(i = 0; i < 8; i++) { + if(decimal_binary.charAt(i) == '1') { + data[targetPoint] += 128 >> i; + } + } + targetPoint++; + } + if (target_count >= 2) { + for(i = 0; i < 8; i++) { + if(decimal_binary.charAt(8 + i) == '1') { + data[targetPoint] += 128 >> i; + } + } + targetPoint++; + } + if (target_count == 3) { + for(i = 0; i < 8; i++) { + if(decimal_binary.charAt(16 + i) == '1') { + data[targetPoint] += 128 >> i; + } + } + targetPoint++; + } + } + + if (current_mode == Mode.C1_BYTE) { + /* Insert byte field length */ + if ((targetPoint - byte_start) <= 249) { + for (i = targetPoint; i >= byte_start; i--) { + data[i + 1] = data[i]; + } + data[byte_start] = (targetPoint - byte_start); + targetPoint++; + } else { + for (i = targetPoint; i >= byte_start; i--) { + data[i + 2] = data[i]; + } + data[byte_start] = 249 + ((targetPoint - byte_start) / 250); + data[byte_start + 1] = ((targetPoint - byte_start) % 250); + targetPoint += 2; + } + } + + /* Re-check length of data */ + if (targetPoint > 1480) { + /* Data is too large for symbol */ + throw OkapiInputException.inputTooLong(); + } + + return targetPoint; + } + + private Mode lookAheadTest(int sourcelen, int position, + Mode current_mode) { + double ascii_count, c40_count, text_count, edi_count, byte_count; + int reduced_char; + int done, best_count, sp; + Mode best_scheme; + + /* Step J */ + if (current_mode == Mode.C1_ASCII) { + ascii_count = 0.0; + c40_count = 1.0; + text_count = 1.0; + edi_count = 1.0; + byte_count = 2.0; + } else { + ascii_count = 1.0; + c40_count = 2.0; + text_count = 2.0; + edi_count = 2.0; + byte_count = 3.0; + } + + switch (current_mode) { + case C1_C40: + c40_count = 0.0; + break; + case C1_TEXT: + text_count = 0.0; + break; + case C1_BYTE: + byte_count = 0.0; + break; + case C1_EDI: + edi_count = 0.0; + break; + } + + for (sp = position; + (sp < sourcelen) && (sp <= (position + 8)); sp++) { + + if (inputData[sp] <= 127) { + reduced_char = inputData[sp]; + } else { + reduced_char = inputData[sp] - 127; + } + + /* Step L */ + if ((inputData[sp] >= '0') && (inputData[sp] <= '9')) { + ascii_count += 0.5; + } else { + ascii_count = roundUpToNextInteger(ascii_count); + if (inputData[sp] > 127) { + ascii_count += 2.0; + } else { + ascii_count += 1.0; + } + } + + /* Step M */ + done = 0; + if (reduced_char == ' ') { + c40_count += (2.0 / 3.0); + done = 1; + } + if ((reduced_char >= '0') && (reduced_char <= '9')) { + c40_count += (2.0 / 3.0); + done = 1; + } + if ((reduced_char >= 'A') && (reduced_char <= 'Z')) { + c40_count += (2.0 / 3.0); + done = 1; + } + if (inputData[sp] > 127) { + c40_count += (4.0 / 3.0); + } + if (done == 0) { + c40_count += (4.0 / 3.0); + } + + /* Step N */ + done = 0; + if (reduced_char == ' ') { + text_count += (2.0 / 3.0); + done = 1; + } + if ((reduced_char >= '0') && (reduced_char <= '9')) { + text_count += (2.0 / 3.0); + done = 1; + } + if ((reduced_char >= 'a') && (reduced_char <= 'z')) { + text_count += (2.0 / 3.0); + done = 1; + } + if (inputData[sp] > 127) { + text_count += (4.0 / 3.0); + } + if (done == 0) { + text_count += (4.0 / 3.0); + } + + /* Step O */ + done = 0; + if (inputData[sp] == 13) { + edi_count += (2.0 / 3.0); + done = 1; + } + if (inputData[sp] == '*') { + edi_count += (2.0 / 3.0); + done = 1; + } + if (inputData[sp] == '>') { + edi_count += (2.0 / 3.0); + done = 1; + } + if (inputData[sp] == ' ') { + edi_count += (2.0 / 3.0); + done = 1; + } + if ((inputData[sp] >= '0') && (inputData[sp] <= '9')) { + edi_count += (2.0 / 3.0); + done = 1; + } + if ((inputData[sp] >= 'A') && (inputData[sp] <= 'Z')) { + edi_count += (2.0 / 3.0); + done = 1; + } + if (inputData[sp] > 127) { + edi_count += (13.0 / 3.0); + } else { + if (done == 0) { + edi_count += (10.0 / 3.0); + } + } + + /* Step P */ + if (inputData[sp] == FNC1) { + byte_count += 3.0; + } else { + byte_count += 1.0; + } + + } + + ascii_count = roundUpToNextInteger(ascii_count); + c40_count = roundUpToNextInteger(c40_count); + text_count = roundUpToNextInteger(text_count); + edi_count = roundUpToNextInteger(edi_count); + byte_count = roundUpToNextInteger(byte_count); + best_scheme = Mode.C1_ASCII; + + if (sp == sourcelen) { + /* Step K */ + best_count = (int) edi_count; + + if (text_count <= best_count) { + best_count = (int) text_count; + best_scheme = Mode.C1_TEXT; + } + + if (c40_count <= best_count) { + best_count = (int) c40_count; + best_scheme = Mode.C1_C40; + } + + if (ascii_count <= best_count) { + best_count = (int) ascii_count; + best_scheme = Mode.C1_ASCII; + } + + if (byte_count <= best_count) { + best_scheme = Mode.C1_BYTE; + } + } else { + /* Step Q */ + + if (((edi_count + 1.0 <= ascii_count) + && (edi_count + 1.0 <= c40_count)) + && ((edi_count + 1.0 <= byte_count) + && (edi_count + 1.0 <= text_count))) { + best_scheme = Mode.C1_EDI; + } + + if ((c40_count + 1.0 <= ascii_count) + && (c40_count + 1.0 <= text_count)) { + + if (c40_count < edi_count) { + best_scheme = Mode.C1_C40; + } else { + if (c40_count == edi_count) { + if (preferEdi(sourcelen, position)) { + best_scheme = Mode.C1_EDI; + } else { + best_scheme = Mode.C1_C40; + } + } + } + } + + if (((text_count + 1.0 <= ascii_count) + && (text_count + 1.0 <= c40_count)) + && ((text_count + 1.0 <= byte_count) + && (text_count + 1.0 <= edi_count))) { + best_scheme = Mode.C1_TEXT; + } + + if (((ascii_count + 1.0 <= byte_count) + && (ascii_count + 1.0 <= c40_count)) + && ((ascii_count + 1.0 <= text_count) + && (ascii_count + 1.0 <= edi_count))) { + best_scheme = Mode.C1_ASCII; + } + + if (((byte_count + 1.0 <= ascii_count) + && (byte_count + 1.0 <= c40_count)) + && ((byte_count + 1.0 <= text_count) + && (byte_count + 1.0 <= edi_count))) { + best_scheme = Mode.C1_BYTE; + } + } + + return best_scheme; + } + + private double roundUpToNextInteger(double input) { + double fraction, output; + + fraction = input - (int) input; + if (fraction > 0.01) { + output = (input - fraction) + 1.0; + } else { + output = input; + } + + return output; + } + + private boolean preferEdi(int sourcelen, int position) { + int i; + + for (i = position; isEdiEncodable(inputData[position + i]) + && ((position + i) < sourcelen); i++); + + if ((position + i) == sourcelen) { + /* Reached end of input */ + return false; + } + + if (inputData[position + i - 1] == 13) { + return true; + } + if (inputData[position + i - 1] == '*') { + return true; + } + if (inputData[position + i - 1] == '>') { + return true; + } + + return false; + } + + private boolean isEdiEncodable(int input) { + boolean result = false; + + if (input == 13) { + result = true; + } + if (input == '*') { + result = true; + } + if (input == '>') { + result = true; + } + if (input == ' ') { + result = true; + } + if ((input >= '0') && (input <= '9')) { + result = true; + } + if ((input >= 'A') && (input <= 'Z')) { + result = true; + } + + return result; + } + + private void plotCentralFinder(int start_row, int row_count, int full_rows) { + for (int i = 0; i < row_count; i++) { + if (i < full_rows) { + plotHorizontalBar(start_row + (i * 2), 1); + } else { + plotHorizontalBar(start_row + (i * 2), 0); + if (i != row_count - 1) { + setGridModule(start_row + (i * 2) + 1, 1); + setGridModule(start_row + (i * 2) + 1, symbol_width - 2); + } + } + } + } + + private void plotHorizontalBar(int row_no, int full) { + if (full != 0) { + for (int i = 0; i < symbol_width; i++) { + setGridModule(row_no, i); + } + } else { + for (int i = 1; i < symbol_width - 1; i++) { + setGridModule(row_no, i); + } + } + } + + private void plotVerticalBar(int column, int height, int top) { + if (top != 0) { + for (int i = 0; i < height; i++) { + setGridModule(i, column); + } + } else { + for (int i = 0; i < height; i++) { + setGridModule(row_count - i - 1, column); + } + } + } + + private void plotSpigot(int row_no) { + for (int i = symbol_width - 1; i > 0; i--) { + if (outputGrid[row_no][i - 1]) { + setGridModule(row_no, i); + } + } + } + + private void plotDataBlock(int start_row, int start_col, int height, int width, int row_offset, int col_offset) { + for (int i = start_row; i < (start_row + height); i++) { + for (int j = start_col; j < (start_col + width); j++) { + if (datagrid[i][j] == '1') { + setGridModule(i + row_offset, j + col_offset); + } + } + } + } + + private void setGridModule(int row, int column) { + outputGrid[row][column] = true; + } + + private void resetGridModule(int row, int column) { + outputGrid[row][column] = false; + } + + private static int getSize(Version version) { + switch(version) { + case A: + return 1; + case B: + return 2; + case C: + return 3; + case D: + return 4; + case E: + return 5; + case F: + return 6; + case G: + return 7; + case H: + return 8; + default: + return 0; + } + } +} diff --git a/src/main/java/uk/org/okapibarcode/backend/DataMatrix.java b/src/main/java/uk/org/okapibarcode/backend/DataMatrix.java index 8b4c8be3..5cb1e575 100644 --- a/src/main/java/uk/org/okapibarcode/backend/DataMatrix.java +++ b/src/main/java/uk/org/okapibarcode/backend/DataMatrix.java @@ -1,1702 +1,1702 @@ -/* - * Copyright 2014 Robin Stuart - * - * 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. - */ - -package uk.org.okapibarcode.backend; - -import static uk.org.okapibarcode.util.Arrays.positionOf; - -import java.util.Arrays; - -/** - *

Implements Data Matrix ECC 200 bar code symbology According to ISO/IEC 16022:2006. - * - *

Data Matrix is a 2D matrix symbology capable of encoding characters in the - * ISO/IEC 8859-1 (Latin-1) character set. - * - * @author Robin Stuart - */ -public class DataMatrix extends Symbol { - - /** Whether or not to try to force the symbol to use a particular shape. */ - public enum ForceMode { - /** Do not try to force the symbol to use a particular shape. */ - NONE, - /** Try to force the symbol to be a square (width = height). */ - SQUARE, - /** Try to force the symbol to be a rectangle (width > height). */ - RECTANGULAR - } - - private enum Mode { - NULL, DM_ASCII, DM_C40, DM_TEXT, DM_X12, DM_EDIFACT, DM_BASE256 - } - - private static final int[] C40_SHIFT = { - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3 - }; - - private static final int[] C40_VALUE = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 3, 0, 1, 2, 3, 4, 5, 6, - 7, 8, 9, 10, 11, 12, 13, 14, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, - 17, 18, 19, 20, 21, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, - 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 22, 23, 24, 25, 26, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 - }; - - private static final int[] TEXT_SHIFT = { - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, - 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 3, 3, 3, 3, 3 - }; - - private static final int[] TEXT_VALUE = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 3, 0, 1, 2, 3, 4, 5, 6, - 7, 8, 9, 10, 11, 12, 13, 14, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, - 17, 18, 19, 20, 21, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 22, 23, 24, 25, 26, 0, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, - 33, 34, 35, 36, 37, 38, 39, 27, 28, 29, 30, 31 - }; - - private static final int[] INT_SYMBOL = { - 0, 1, 3, 5, 7, 8, 10, 12, 13, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 2, 4, 6, 9, 11, 14 - }; - - private static final int[] MATRIX_H = { - 10, 12, 8, 14, 8, 16, 12, 18, 20, 12, 22, 16, 24, 26, 16, 32, 36, 40, - 44, 48, 52, 64, 72, 80, 88, 96, 104, 120, 132, 144 - }; - - private static final int[] MATRIX_W = { - 10, 12, 18, 14, 32, 16, 26, 18, 20, 36, 22, 36, 24, 26, 48, 32, 36, 40, - 44, 48, 52, 64, 72, 80, 88, 96, 104, 120, 132, 144 - }; - - private static final int[] MATRIX_FH = { - 10, 12, 8, 14, 8, 16, 12, 18, 20, 12, 22, 16, 24, 26, 16, 16, 18, 20, - 22, 24, 26, 16, 18, 20, 22, 24, 26, 20, 22, 24 - }; - - private static final int[] MATRIX_FW = { - 10, 12, 18, 14, 16, 16, 26, 18, 20, 18, 22, 18, 24, 26, 24, 16, 18, 20, - 22, 24, 26, 16, 18, 20, 22, 24, 26, 20, 22, 24 - }; - - private static final int[] MATRIX_BYTES = { - 3, 5, 5, 8, 10, 12, 16, 18, 22, 22, 30, 32, 36, 44, 49, 62, 86, 114, - 144, 174, 204, 280, 368, 456, 576, 696, 816, 1050, 1304, 1558 - }; - - private static final int[] MATRIX_DATA_BLOCK = { - 3, 5, 5, 8, 10, 12, 16, 18, 22, 22, 30, 32, 36, 44, 49, 62, 86, 114, - 144, 174, 102, 140, 92, 114, 144, 174, 136, 175, 163, 156 - }; - - private static final int[] MATRIX_RS_BLOCK = { - 5, 7, 7, 10, 11, 12, 14, 14, 18, 18, 20, 24, 24, 28, 28, 36, 42, 48, 56, - 68, 42, 56, 36, 48, 56, 68, 56, 68, 62, 62 - }; - - private static final int DM_SIZES_COUNT = MATRIX_H.length; - - // user-specified values and settings - - private ForceMode forceMode = ForceMode.NONE; - private int preferredSize; - private int structuredAppendFileId = 1; - private int structuredAppendPosition = 1; - private int structuredAppendTotal = 1; - private boolean separatorGs; - - // internal state calculated when setContent() is called - - private int actualSize = -1; - private int[] target = new int[2200]; - private int[] binary = new int[2200]; - private int binary_length; - private Mode last_mode; - private int[] places; - private int process_p; - private int[] process_buffer = new int[8]; - private int codewordCount; - - /** - * Forces the symbol to be either square or rectangular (non-square). Used only - * if a {@link #getPreferredSize() preferred size} has not been specified. - * - * @param forceMode the force mode to use - */ - public void setForceMode(ForceMode forceMode) { - this.forceMode = forceMode; - } - - /** - * Returns the force mode used by this symbol. Used only if a - * {@link #getPreferredSize() preferred size} has not been specified. - * - * @return the force mode used by this symbol - */ - public ForceMode getForceMode() { - return forceMode; - } - - /** - * Sets the preferred symbol size according to the values in the following - * table. Values may be ignored if the data is too big to fit in the - * specified symbol. - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
InputSymbol SizeInputSymbol Size
1 10 x 10 1664 x 64
2 12 x 12 1772 x 72
3 14 x 14 1880 x 80
4 16 x 16 1988 x 88
5 18 x 18 2096 x 96
6 20 x 20 21104 x 104
7 22 x 22 22120 x 120
8 24 x 24 23132 x 132
9 26 x 26 24144 x 144
10 32 x 32 258 x 18
11 36 x 36 268 x 32
12 40 x 40 2712 x 26
13 44 x 44 2812 x 36
14 48 x 48 2916 x 36
15 52 x 52 3016 x 48
- * - * @param size the symbol size to use (1 - 30 inclusive) - */ - public void setPreferredSize(int size) { - preferredSize = size; - } - - /** - * Returns the preferred symbol size. - * - * @return the preferred symbol size - * @see #setPreferredSize(int) - */ - public int getPreferredSize() { - return preferredSize; - } - - /** - * Returns the actual symbol size used. Available after the symbol is encoded. - * - * @return the actual symbol size used - */ - public int getActualSize() { - if (actualSize != -1) { - return actualSize; - } else { - throw new IllegalStateException("Actual size not calculated until symbol is encoded."); - } - } - - /** - * Returns the actual width (columns) used for the symbol. Available after the symbol is encoded. - * - * @return the actual width (columns) used for the symbol - */ - public int getActualWidth() { - int index1 = getActualSize() - 1; - int index2 = INT_SYMBOL[index1]; - return MATRIX_W[index2]; - } - - /** - * Returns the actual height (rows) used for the symbol. Available after the symbol is encoded. - * - * @return the actual height (rows) used for the symbol - */ - public int getActualHeight() { - int index1 = getActualSize() - 1; - int index2 = INT_SYMBOL[index1]; - return MATRIX_H[index2]; - } - - /** - * If this Data Matrix symbol is part of a series of Data Matrix symbols appended in a structured - * format, this method sets the position of this symbol in the series. Valid values are 1 through - * 16 inclusive. - * - * @param position the position of this Data Matrix symbol in the structured append series - */ - public void setStructuredAppendPosition(int position) { - if (position < 1 || position > 16) { - throw new IllegalArgumentException("Invalid Data Matrix structured append position: " + position); - } - this.structuredAppendPosition = position; - } - - /** - * Returns the position of this Data Matrix symbol in a series of symbols using structured append. - * If this symbol is not part of such a series, this method will return 1. - * - * @return the position of this Data Matrix symbol in a series of symbols using structured append - */ - public int getStructuredAppendPosition() { - return structuredAppendPosition; - } - - /** - * If this Data Matrix symbol is part of a series of Data Matrix symbols appended in a structured - * format, this method sets the total number of symbols in the series. Valid values are - * 1 through 16 inclusive. A value of 1 indicates that this symbol is not part of a structured - * append series. - * - * @param total the total number of Data Matrix symbols in the structured append series - */ - public void setStructuredAppendTotal(int total) { - if (total < 1 || total > 16) { - throw new IllegalArgumentException("Invalid Data Matrix structured append total: " + total); - } - this.structuredAppendTotal = total; - } - - /** - * Returns the size of the series of Data Matrix symbols using structured append that this symbol - * is part of. If this symbol is not part of a structured append series, this method will return - * 1. - * - * @return size of the series that this symbol is part of - */ - public int getStructuredAppendTotal() { - return structuredAppendTotal; - } - - /** - * If this Data Matrix symbol is part of a series of Data Matrix symbols appended in a structured format, - * this method sets the unique file ID for the series. Valid values are 1 through 64,516 inclusive. - * - * @param fileId the unique file ID for the series that this symbol is part of - */ - public void setStructuredAppendFileId(int fileId) { - if (fileId < 1 || fileId > 64_516) { - throw new IllegalArgumentException("Invalid Data Matrix structured append file ID: " + fileId); - } - this.structuredAppendFileId = fileId; - } - - /** - * Returns the unique file ID of the series of Data Matrix symbols using structured append that this - * symbol is part of. If this symbol is not part of a structured append series, this method will return - * 1. - * - * @return the unique file ID for the series that this symbol is part of - */ - public int getStructuredAppendFileId() { - return structuredAppendFileId; - } - - /** - * Sets whether or not to use the {@code GS} character as the GS1 separator. By default, the {@code FNC1} - * character is used as the GS1 separator. This method should only be used when encoding {@link DataType#GS1} - * data. - * - * @param separatorGs whether or not to use the {@code GS} character as the GS1 separator - * @see #setDataType(DataType) - */ - public void setGs1SeparatorGs(boolean separatorGs) { - this.separatorGs = separatorGs; - } - - /** - * Returns whether or not this symbol uses the {@code GS} character as the GS1 separator. If {@code false}, - * the {@code FNC1} character is used as the GS1 separator. By default, the {@code FNC1} character is used. - * - * @return whether or not this symbol uses the {@code GS} character as the GS1 separator - * @see #setDataType(DataType) - */ - public boolean getGs1SeparatorGs() { - return this.separatorGs; - } - - @Override - protected boolean gs1Supported() { - return true; - } - - @Override - protected boolean eciSupported() { - return true; - } - - @Override - protected void encode() { - - int i, binlen, skew = 0; - int symbolsize, optionsize, calcsize; - int taillength; - int H, W, FH, FW, datablock, bytes, rsblock; - int x, y, NC, NR, v; - int[] grid; - StringBuilder bin = new StringBuilder(); - - eciProcess(); // Get ECI mode - - binlen = generateCodewords(); - - if (preferredSize >= 1 && preferredSize <= DM_SIZES_COUNT) { - optionsize = INT_SYMBOL[preferredSize - 1]; - } else { - optionsize = -1; - } - - int required = binlen + process_p; - - // In 99% of cases N trailing data characters can be encoded using N codewords, thanks to implicit ASCII - // latches (see encodeRemainder())... but there are two exceptions: - // 1. In X12 encodation when there are 2 trailing data characters, a scenario which requires 3 codewords - // (explicit ASCII latch required) - // 2. In C40 or TEXT encodation when there is one trailing extended ASCII character, and we have already - // performed the Shift 2 + Upper Shift preparation - if (last_mode == Mode.DM_X12 && process_p == 2) { - required++; - } - if ((last_mode == Mode.DM_C40 || last_mode == Mode.DM_TEXT) && process_p == 1 && endsWithUpperShift(target, binlen)) { - required++; - } - - calcsize = DM_SIZES_COUNT - 1; - for (i = DM_SIZES_COUNT - 1; i > -1; i--) { - if (MATRIX_BYTES[i] >= required) { - calcsize = i; - } - } - - if (optionsize == -1) { - // We are in automatic size mode as the exact symbol size was not given - // Now check the detailed search options square only or rectangular only - if (forceMode == ForceMode.SQUARE) { - /* Skip rectangular symbols in square only mode */ - while (calcsize < DM_SIZES_COUNT && MATRIX_H[calcsize] != MATRIX_W[calcsize]) { - calcsize++; - } - } else if (forceMode == ForceMode.RECTANGULAR) { - /* Skip square symbols in rectangular only mode */ - while (calcsize < DM_SIZES_COUNT && MATRIX_H[calcsize] == MATRIX_W[calcsize]) { - calcsize++; - } - } - if (calcsize >= DM_SIZES_COUNT) { - throw new OkapiInputException("Input too long to fit in any of the available symbol sizes"); - } - symbolsize = calcsize; - } else { - // The symbol size was specified by the user - // Thus check if the data fits into this symbol size and use this size - if (calcsize > optionsize) { - throw new OkapiInputException("Input too long to fit in the selected symbol size"); - } - symbolsize = optionsize; - } - - // Now that we know the symbol size we can handle the remaining data in the process buffer. - int symbolsLeft = MATRIX_BYTES[symbolsize] - binlen; - binlen = encodeRemainder(symbolsLeft, binlen); - if (binlen > MATRIX_BYTES[symbolsize]) { - throw new OkapiInternalException("Input unexpectedly too long to fit in the selected symbol size"); - } - - H = MATRIX_H[symbolsize]; - W = MATRIX_W[symbolsize]; - FH = MATRIX_FH[symbolsize]; - FW = MATRIX_FW[symbolsize]; - bytes = MATRIX_BYTES[symbolsize]; - datablock = MATRIX_DATA_BLOCK[symbolsize]; - rsblock = MATRIX_RS_BLOCK[symbolsize]; - - codewordCount = datablock + rsblock; // data codewords + error correction codewords - - taillength = bytes - binlen; - - if (taillength != 0) { - addPadBits(binlen, taillength); - } - - // ecc code - if (symbolsize == 29) { - skew = 1; - } - calculateErrorCorrection(bytes, datablock, rsblock, skew); - NC = W - 2 * (W / FW); - NR = H - 2 * (H / FH); - places = new int[NC * NR]; - placeData(NR, NC); - grid = new int[W * H]; - for (i = 0; i < (W * H); i++) { - grid[i] = 0; - } - for (y = 0; y < H; y += FH) { - for (x = 0; x < W; x++) { - grid[y * W + x] = 1; - } - for (x = 0; x < W; x += 2) { - grid[(y + FH - 1) * W + x] = 1; - } - } - for (x = 0; x < W; x += FW) { - for (y = 0; y < H; y++) { - grid[y * W + x] = 1; - } - for (y = 0; y < H; y += 2) { - grid[y * W + x + FW - 1] = 1; - } - } - for (y = 0; y < NR; y++) { - for (x = 0; x < NC; x++) { - v = places[(NR - y - 1) * NC + x]; - if (v == 1 || (v > 7 && (target[(v >> 3) - 1] & (1 << (v & 7))) != 0)) { - grid[(1 + y + 2 * (y / (FH - 2))) * W + 1 + x + 2 * (x / (FW - 2))] = 1; - } - } - } - - actualSize = positionOf(symbolsize, INT_SYMBOL) + 1; - readable = ""; - pattern = new String[H]; - row_count = H; - row_height = new int[H]; - for (y = H - 1; y >= 0; y--) { - bin.setLength(0); - for (x = 0; x < W; x++) { - if (grid[W * y + x] == 1) { - bin.append('1'); - } else { - bin.append('0'); - } - } - pattern[(H - y) - 1] = bin2pat(bin); - row_height[(H - y) - 1] = moduleWidth; - } - - infoLine("Grid Size: " + W + " X " + H); - infoLine("Data Codewords: " + datablock); - infoLine("ECC Codewords: " + rsblock); - } - - @Override - protected int[] getCodewords() { - return Arrays.copyOf(target, codewordCount); - } - - private int generateCodewords() { - /* Encodes data using ASCII, C40, Text, X12, EDIFACT or Base 256 modes as appropriate */ - /* Supports encoding FNC1 in supporting systems */ - /* Supports ECI encoding for whole message only, not inline switching */ - - info("Encoding: "); - int sp, tp, i; - Mode current_mode, next_mode; - int inputlen = inputData.length; - - sp = 0; - tp = 0; - process_p = 0; - - for (i = 0; i < 8; i++) { - process_buffer[i] = 0; - } - binary_length = 0; - - /* step (a) */ - current_mode = Mode.DM_ASCII; - next_mode = Mode.DM_ASCII; - - if (structuredAppendTotal != 1) { - - /* FNC2 */ - target[tp] = 233; - tp++; - binary[binary_length] = ' '; - binary_length++; - info("FNC2 "); - - /* symbol sequence indicator (position + total) */ - int ssi = ((structuredAppendPosition - 1) << 4) | (17 - structuredAppendTotal); - target[tp] = ssi; - tp++; - binary[binary_length] = ' '; - binary_length++; - infoSpace(ssi); - - /* file identification codeword 1 (valid values 1 - 254) */ - int id1 = 1 + ((structuredAppendFileId - 1) / 254); - target[tp] = id1; - tp++; - binary[binary_length] = ' '; - binary_length++; - infoSpace(id1); - - /* file identification codeword 2 (valid values 1 - 254) */ - int id2 = 1 + ((structuredAppendFileId - 1) % 254); - target[tp] = id2; - tp++; - binary[binary_length] = ' '; - binary_length++; - infoSpace(id2); - } - - if (inputDataType == DataType.GS1) { - target[tp] = 232; - tp++; - binary[binary_length] = ' '; - binary_length++; - info("FNC1 "); - } /* FNC1 */ - - if (readerInit) { - target[tp] = 234; /* FNC3 */ - tp++; /* Reader Programming */ - binary[binary_length] = ' '; - binary_length++; - info("RP "); - } - - if (eciMode != 3) { - target[tp] = 241; // ECI - tp++; - binary[binary_length] = ' '; - binary_length++; - if (eciMode <= 126) { - target[tp] = eciMode + 1; - tp++; - binary[binary_length] = ' '; - binary_length++; - } - if ((eciMode >= 127) && (eciMode <= 16382)) { - target[tp] = ((eciMode - 127) / 254) + 128; - tp++; - binary[binary_length] = ' '; - binary_length++; - target[tp] = ((eciMode - 127) % 254) + 1; - tp++; - binary[binary_length] = ' '; - binary_length++; - } - if (eciMode >= 16383) { - target[tp] = ((eciMode - 16383) / 64516) + 192; - tp++; - binary[binary_length] = ' '; - binary_length++; - target[tp] = (((eciMode - 16383) / 254) % 254) + 1; - tp++; - binary[binary_length] = ' '; - binary_length++; - target[tp] = ((eciMode - 16383) % 254) + 1; - tp++; - binary[binary_length] = ' '; - binary_length++; - } - info("ECI " + eciMode + " "); - } - - /* Check for Macro05/Macro06 */ - /* "[)>[RS]05[GS]...[RS][EOT]" -> CW 236 */ - /* "[)>[RS]06[GS]...[RS][EOT]" -> CW 237 */ - if (tp == 0 & sp == 0 && inputlen >= 9) { - if (inputData[0] == '[' && inputData[1] == ')' && inputData[2] == '>' - && inputData[3] == '\u001e' && inputData[4] == '0' - && (inputData[5] == '5' || inputData[5] == '6') - && inputData[6] == '\u001d' - && inputData[inputlen - 2] == '\u001e' - && inputData[inputlen - 1] == '\u0004') { - /* Output macro codeword */ - if (inputData[5] == '5') { - target[tp] = 236; - info("Macro05 "); - } else { - target[tp] = 237; - info("Macro06 "); - } - tp++; - binary[binary_length] = ' '; - binary_length++; - /* Remove macro characters from input string */ - sp = 7; - inputlen -= 2; - inputData = Arrays.copyOf(inputData, inputData.length - 2); - } - } - - while (sp < inputlen) { - - current_mode = next_mode; - - /* step (b) - ASCII encodation */ - if (current_mode == Mode.DM_ASCII) { - next_mode = Mode.DM_ASCII; - - for (i = 0; i < 8; i++) { - process_buffer[i] = 0; - } - - if (isTwoDigits(sp)) { - target[tp] = (10 * Character.getNumericValue(inputData[sp])) - + Character.getNumericValue(inputData[sp + 1]) + 130; - infoSpace(target[tp] - 130); - tp++; - binary[binary_length] = ' '; - binary_length++; - sp += 2; - } else { - next_mode = lookAheadTest(sp, current_mode); - - if (next_mode != Mode.DM_ASCII) { - switch (next_mode) { - case DM_C40: - target[tp] = 230; - tp++; - binary[binary_length] = ' '; - binary_length++; - info("C40 "); - break; - case DM_TEXT: - target[tp] = 239; - tp++; - binary[binary_length] = ' '; - binary_length++; - info("TEX "); - break; - case DM_X12: - target[tp] = 238; - tp++; - binary[binary_length] = ' '; - binary_length++; - info("X12 "); - break; - case DM_EDIFACT: - target[tp] = 240; - tp++; - binary[binary_length] = ' '; - binary_length++; - info("EDI "); - break; - case DM_BASE256: - target[tp] = 231; - tp++; - binary[binary_length] = ' '; - binary_length++; - info("BAS "); - break; - } - } else { - if (inputData[sp] > 127) { - target[tp] = 235; /* FNC4 */ - - info("FNC4 "); - tp++; - target[tp] = (inputData[sp] - 128) + 1; - infoSpace(target[tp] - 1); - tp++; - binary[binary_length] = ' '; - binary_length++; - binary[binary_length] = ' '; - binary_length++; - } else { - if (inputData[sp] == FNC1) { - if (separatorGs) { - target[tp] = 29 + 1; /* GS */ - info("GS "); - } else { - target[tp] = 232; /* FNC1 */ - info("FNC1 "); - } - } else { - target[tp] = inputData[sp] + 1; - infoSpace(target[tp] - 1); - } - tp++; - binary[binary_length] = ' '; - binary_length++; - } - sp++; - } - } - } - - /* step (c) C40 encodation */ - if (current_mode == Mode.DM_C40) { - int shift_set, value; - - next_mode = Mode.DM_C40; - if (process_p == 0) { - next_mode = lookAheadTest(sp, current_mode); - } - - if (next_mode != Mode.DM_C40) { - target[tp] = 254; - tp++; - binary[binary_length] = ' '; - binary_length++; /* Unlatch */ - - next_mode = Mode.DM_ASCII; - info("ASC "); - } else { - if (inputData[sp] == FNC1) { - if (separatorGs) { - shift_set = 1; - value = 29; /* GS */ - } else { - shift_set = 2; - value = 27; /* FNC1 */ - } - } else if (inputData[sp] > 127) { - process_buffer[process_p] = 1; - process_p++; - process_buffer[process_p] = 30; - process_p++; /* Upper Shift */ - - shift_set = C40_SHIFT[inputData[sp] - 128]; - value = C40_VALUE[inputData[sp] - 128]; - } else { - shift_set = C40_SHIFT[inputData[sp]]; - value = C40_VALUE[inputData[sp]]; +/* + * Copyright 2014 Robin Stuart + * + * 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. + */ + +package uk.org.okapibarcode.backend; + +import static uk.org.okapibarcode.util.Arrays.positionOf; + +import java.util.Arrays; + +/** + *

Implements Data Matrix ECC 200 bar code symbology According to ISO/IEC 16022:2006. + * + *

Data Matrix is a 2D matrix symbology capable of encoding characters in the + * ISO/IEC 8859-1 (Latin-1) character set. + * + * @author Robin Stuart + */ +public class DataMatrix extends Symbol { + + /** Whether or not to try to force the symbol to use a particular shape. */ + public enum ForceMode { + /** Do not try to force the symbol to use a particular shape. */ + NONE, + /** Try to force the symbol to be a square (width = height). */ + SQUARE, + /** Try to force the symbol to be a rectangle (width > height). */ + RECTANGULAR + } + + private enum Mode { + NULL, DM_ASCII, DM_C40, DM_TEXT, DM_X12, DM_EDIFACT, DM_BASE256 + } + + private static final int[] C40_SHIFT = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3 + }; + + private static final int[] C40_VALUE = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 3, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, + 17, 18, 19, 20, 21, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 22, 23, 24, 25, 26, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 + }; + + private static final int[] TEXT_SHIFT = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, + 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, 3, 3, 3, 3 + }; + + private static final int[] TEXT_VALUE = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 3, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, + 17, 18, 19, 20, 21, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 22, 23, 24, 25, 26, 0, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 27, 28, 29, 30, 31 + }; + + private static final int[] INT_SYMBOL = { + 0, 1, 3, 5, 7, 8, 10, 12, 13, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 2, 4, 6, 9, 11, 14 + }; + + private static final int[] MATRIX_H = { + 10, 12, 8, 14, 8, 16, 12, 18, 20, 12, 22, 16, 24, 26, 16, 32, 36, 40, + 44, 48, 52, 64, 72, 80, 88, 96, 104, 120, 132, 144 + }; + + private static final int[] MATRIX_W = { + 10, 12, 18, 14, 32, 16, 26, 18, 20, 36, 22, 36, 24, 26, 48, 32, 36, 40, + 44, 48, 52, 64, 72, 80, 88, 96, 104, 120, 132, 144 + }; + + private static final int[] MATRIX_FH = { + 10, 12, 8, 14, 8, 16, 12, 18, 20, 12, 22, 16, 24, 26, 16, 16, 18, 20, + 22, 24, 26, 16, 18, 20, 22, 24, 26, 20, 22, 24 + }; + + private static final int[] MATRIX_FW = { + 10, 12, 18, 14, 16, 16, 26, 18, 20, 18, 22, 18, 24, 26, 24, 16, 18, 20, + 22, 24, 26, 16, 18, 20, 22, 24, 26, 20, 22, 24 + }; + + private static final int[] MATRIX_BYTES = { + 3, 5, 5, 8, 10, 12, 16, 18, 22, 22, 30, 32, 36, 44, 49, 62, 86, 114, + 144, 174, 204, 280, 368, 456, 576, 696, 816, 1050, 1304, 1558 + }; + + private static final int[] MATRIX_DATA_BLOCK = { + 3, 5, 5, 8, 10, 12, 16, 18, 22, 22, 30, 32, 36, 44, 49, 62, 86, 114, + 144, 174, 102, 140, 92, 114, 144, 174, 136, 175, 163, 156 + }; + + private static final int[] MATRIX_RS_BLOCK = { + 5, 7, 7, 10, 11, 12, 14, 14, 18, 18, 20, 24, 24, 28, 28, 36, 42, 48, 56, + 68, 42, 56, 36, 48, 56, 68, 56, 68, 62, 62 + }; + + private static final int DM_SIZES_COUNT = MATRIX_H.length; + + // user-specified values and settings + + private ForceMode forceMode = ForceMode.NONE; + private int preferredSize; + private int structuredAppendFileId = 1; + private int structuredAppendPosition = 1; + private int structuredAppendTotal = 1; + private boolean separatorGs; + + // internal state calculated when setContent() is called + + private int actualSize = -1; + private int[] target = new int[2200]; + private int[] binary = new int[2200]; + private int binary_length; + private Mode last_mode; + private int[] places; + private int process_p; + private int[] process_buffer = new int[8]; + private int codewordCount; + + /** + * Forces the symbol to be either square or rectangular (non-square). Used only + * if a {@link #getPreferredSize() preferred size} has not been specified. + * + * @param forceMode the force mode to use + */ + public void setForceMode(ForceMode forceMode) { + this.forceMode = forceMode; + } + + /** + * Returns the force mode used by this symbol. Used only if a + * {@link #getPreferredSize() preferred size} has not been specified. + * + * @return the force mode used by this symbol + */ + public ForceMode getForceMode() { + return forceMode; + } + + /** + * Sets the preferred symbol size according to the values in the following + * table. Values may be ignored if the data is too big to fit in the + * specified symbol. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
InputSymbol SizeInputSymbol Size
1 10 x 10 1664 x 64
2 12 x 12 1772 x 72
3 14 x 14 1880 x 80
4 16 x 16 1988 x 88
5 18 x 18 2096 x 96
6 20 x 20 21104 x 104
7 22 x 22 22120 x 120
8 24 x 24 23132 x 132
9 26 x 26 24144 x 144
10 32 x 32 258 x 18
11 36 x 36 268 x 32
12 40 x 40 2712 x 26
13 44 x 44 2812 x 36
14 48 x 48 2916 x 36
15 52 x 52 3016 x 48
+ * + * @param size the symbol size to use (1 - 30 inclusive) + */ + public void setPreferredSize(int size) { + preferredSize = size; + } + + /** + * Returns the preferred symbol size. + * + * @return the preferred symbol size + * @see #setPreferredSize(int) + */ + public int getPreferredSize() { + return preferredSize; + } + + /** + * Returns the actual symbol size used. Available after the symbol is encoded. + * + * @return the actual symbol size used + */ + public int getActualSize() { + if (actualSize != -1) { + return actualSize; + } else { + throw new IllegalStateException("Actual size not calculated until symbol is encoded."); + } + } + + /** + * Returns the actual width (columns) used for the symbol. Available after the symbol is encoded. + * + * @return the actual width (columns) used for the symbol + */ + public int getActualWidth() { + int index1 = getActualSize() - 1; + int index2 = INT_SYMBOL[index1]; + return MATRIX_W[index2]; + } + + /** + * Returns the actual height (rows) used for the symbol. Available after the symbol is encoded. + * + * @return the actual height (rows) used for the symbol + */ + public int getActualHeight() { + int index1 = getActualSize() - 1; + int index2 = INT_SYMBOL[index1]; + return MATRIX_H[index2]; + } + + /** + * If this Data Matrix symbol is part of a series of Data Matrix symbols appended in a structured + * format, this method sets the position of this symbol in the series. Valid values are 1 through + * 16 inclusive. + * + * @param position the position of this Data Matrix symbol in the structured append series + */ + public void setStructuredAppendPosition(int position) { + if (position < 1 || position > 16) { + throw new IllegalArgumentException("Invalid Data Matrix structured append position: " + position); + } + this.structuredAppendPosition = position; + } + + /** + * Returns the position of this Data Matrix symbol in a series of symbols using structured append. + * If this symbol is not part of such a series, this method will return 1. + * + * @return the position of this Data Matrix symbol in a series of symbols using structured append + */ + public int getStructuredAppendPosition() { + return structuredAppendPosition; + } + + /** + * If this Data Matrix symbol is part of a series of Data Matrix symbols appended in a structured + * format, this method sets the total number of symbols in the series. Valid values are + * 1 through 16 inclusive. A value of 1 indicates that this symbol is not part of a structured + * append series. + * + * @param total the total number of Data Matrix symbols in the structured append series + */ + public void setStructuredAppendTotal(int total) { + if (total < 1 || total > 16) { + throw new IllegalArgumentException("Invalid Data Matrix structured append total: " + total); + } + this.structuredAppendTotal = total; + } + + /** + * Returns the size of the series of Data Matrix symbols using structured append that this symbol + * is part of. If this symbol is not part of a structured append series, this method will return + * 1. + * + * @return size of the series that this symbol is part of + */ + public int getStructuredAppendTotal() { + return structuredAppendTotal; + } + + /** + * If this Data Matrix symbol is part of a series of Data Matrix symbols appended in a structured format, + * this method sets the unique file ID for the series. Valid values are 1 through 64,516 inclusive. + * + * @param fileId the unique file ID for the series that this symbol is part of + */ + public void setStructuredAppendFileId(int fileId) { + if (fileId < 1 || fileId > 64_516) { + throw new IllegalArgumentException("Invalid Data Matrix structured append file ID: " + fileId); + } + this.structuredAppendFileId = fileId; + } + + /** + * Returns the unique file ID of the series of Data Matrix symbols using structured append that this + * symbol is part of. If this symbol is not part of a structured append series, this method will return + * 1. + * + * @return the unique file ID for the series that this symbol is part of + */ + public int getStructuredAppendFileId() { + return structuredAppendFileId; + } + + /** + * Sets whether or not to use the {@code GS} character as the GS1 separator. By default, the {@code FNC1} + * character is used as the GS1 separator. This method should only be used when encoding {@link DataType#GS1} + * data. + * + * @param separatorGs whether or not to use the {@code GS} character as the GS1 separator + * @see #setDataType(DataType) + */ + public void setGs1SeparatorGs(boolean separatorGs) { + this.separatorGs = separatorGs; + } + + /** + * Returns whether or not this symbol uses the {@code GS} character as the GS1 separator. If {@code false}, + * the {@code FNC1} character is used as the GS1 separator. By default, the {@code FNC1} character is used. + * + * @return whether or not this symbol uses the {@code GS} character as the GS1 separator + * @see #setDataType(DataType) + */ + public boolean getGs1SeparatorGs() { + return this.separatorGs; + } + + @Override + protected boolean gs1Supported() { + return true; + } + + @Override + protected boolean eciSupported() { + return true; + } + + @Override + protected void encode() { + + int i, binlen, skew = 0; + int symbolsize, optionsize, calcsize; + int taillength; + int H, W, FH, FW, datablock, bytes, rsblock; + int x, y, NC, NR, v; + int[] grid; + StringBuilder bin = new StringBuilder(); + + eciProcess(); // Get ECI mode + + binlen = generateCodewords(); + + if (preferredSize >= 1 && preferredSize <= DM_SIZES_COUNT) { + optionsize = INT_SYMBOL[preferredSize - 1]; + } else { + optionsize = -1; + } + + int required = binlen + process_p; + + // In 99% of cases N trailing data characters can be encoded using N codewords, thanks to implicit ASCII + // latches (see encodeRemainder())... but there are two exceptions: + // 1. In X12 encodation when there are 2 trailing data characters, a scenario which requires 3 codewords + // (explicit ASCII latch required) + // 2. In C40 or TEXT encodation when there is one trailing extended ASCII character, and we have already + // performed the Shift 2 + Upper Shift preparation + if (last_mode == Mode.DM_X12 && process_p == 2) { + required++; + } + if ((last_mode == Mode.DM_C40 || last_mode == Mode.DM_TEXT) && process_p == 1 && endsWithUpperShift(target, binlen)) { + required++; + } + + calcsize = DM_SIZES_COUNT - 1; + for (i = DM_SIZES_COUNT - 1; i > -1; i--) { + if (MATRIX_BYTES[i] >= required) { + calcsize = i; + } + } + + if (optionsize == -1) { + // We are in automatic size mode as the exact symbol size was not given + // Now check the detailed search options square only or rectangular only + if (forceMode == ForceMode.SQUARE) { + /* Skip rectangular symbols in square only mode */ + while (calcsize < DM_SIZES_COUNT && MATRIX_H[calcsize] != MATRIX_W[calcsize]) { + calcsize++; + } + } else if (forceMode == ForceMode.RECTANGULAR) { + /* Skip square symbols in rectangular only mode */ + while (calcsize < DM_SIZES_COUNT && MATRIX_H[calcsize] == MATRIX_W[calcsize]) { + calcsize++; + } + } + if (calcsize >= DM_SIZES_COUNT) { + throw new OkapiInputException("Input too long to fit in any of the available symbol sizes"); + } + symbolsize = calcsize; + } else { + // The symbol size was specified by the user + // Thus check if the data fits into this symbol size and use this size + if (calcsize > optionsize) { + throw new OkapiInputException("Input too long to fit in the selected symbol size"); + } + symbolsize = optionsize; + } + + // Now that we know the symbol size we can handle the remaining data in the process buffer. + int symbolsLeft = MATRIX_BYTES[symbolsize] - binlen; + binlen = encodeRemainder(symbolsLeft, binlen); + if (binlen > MATRIX_BYTES[symbolsize]) { + throw new OkapiInternalException("Input unexpectedly too long to fit in the selected symbol size"); + } + + H = MATRIX_H[symbolsize]; + W = MATRIX_W[symbolsize]; + FH = MATRIX_FH[symbolsize]; + FW = MATRIX_FW[symbolsize]; + bytes = MATRIX_BYTES[symbolsize]; + datablock = MATRIX_DATA_BLOCK[symbolsize]; + rsblock = MATRIX_RS_BLOCK[symbolsize]; + + codewordCount = datablock + rsblock; // data codewords + error correction codewords + + taillength = bytes - binlen; + + if (taillength != 0) { + addPadBits(binlen, taillength); + } + + // ecc code + if (symbolsize == 29) { + skew = 1; + } + calculateErrorCorrection(bytes, datablock, rsblock, skew); + NC = W - 2 * (W / FW); + NR = H - 2 * (H / FH); + places = new int[NC * NR]; + placeData(NR, NC); + grid = new int[W * H]; + for (i = 0; i < (W * H); i++) { + grid[i] = 0; + } + for (y = 0; y < H; y += FH) { + for (x = 0; x < W; x++) { + grid[y * W + x] = 1; + } + for (x = 0; x < W; x += 2) { + grid[(y + FH - 1) * W + x] = 1; + } + } + for (x = 0; x < W; x += FW) { + for (y = 0; y < H; y++) { + grid[y * W + x] = 1; + } + for (y = 0; y < H; y += 2) { + grid[y * W + x + FW - 1] = 1; + } + } + for (y = 0; y < NR; y++) { + for (x = 0; x < NC; x++) { + v = places[(NR - y - 1) * NC + x]; + if (v == 1 || (v > 7 && (target[(v >> 3) - 1] & (1 << (v & 7))) != 0)) { + grid[(1 + y + 2 * (y / (FH - 2))) * W + 1 + x + 2 * (x / (FW - 2))] = 1; + } + } + } + + actualSize = positionOf(symbolsize, INT_SYMBOL) + 1; + readable = ""; + pattern = new String[H]; + row_count = H; + row_height = new int[H]; + for (y = H - 1; y >= 0; y--) { + bin.setLength(0); + for (x = 0; x < W; x++) { + if (grid[W * y + x] == 1) { + bin.append('1'); + } else { + bin.append('0'); + } + } + pattern[(H - y) - 1] = bin2pat(bin); + row_height[(H - y) - 1] = moduleWidth; + } + + infoLine("Grid Size: " + W + " X " + H); + infoLine("Data Codewords: " + datablock); + infoLine("ECC Codewords: " + rsblock); + } + + @Override + protected int[] getCodewords() { + return Arrays.copyOf(target, codewordCount); + } + + private int generateCodewords() { + /* Encodes data using ASCII, C40, Text, X12, EDIFACT or Base 256 modes as appropriate */ + /* Supports encoding FNC1 in supporting systems */ + /* Supports ECI encoding for whole message only, not inline switching */ + + info("Encoding: "); + int sp, tp, i; + Mode current_mode, next_mode; + int inputlen = inputData.length; + + sp = 0; + tp = 0; + process_p = 0; + + for (i = 0; i < 8; i++) { + process_buffer[i] = 0; + } + binary_length = 0; + + /* step (a) */ + current_mode = Mode.DM_ASCII; + next_mode = Mode.DM_ASCII; + + if (structuredAppendTotal != 1) { + + /* FNC2 */ + target[tp] = 233; + tp++; + binary[binary_length] = ' '; + binary_length++; + info("FNC2 "); + + /* symbol sequence indicator (position + total) */ + int ssi = ((structuredAppendPosition - 1) << 4) | (17 - structuredAppendTotal); + target[tp] = ssi; + tp++; + binary[binary_length] = ' '; + binary_length++; + infoSpace(ssi); + + /* file identification codeword 1 (valid values 1 - 254) */ + int id1 = 1 + ((structuredAppendFileId - 1) / 254); + target[tp] = id1; + tp++; + binary[binary_length] = ' '; + binary_length++; + infoSpace(id1); + + /* file identification codeword 2 (valid values 1 - 254) */ + int id2 = 1 + ((structuredAppendFileId - 1) % 254); + target[tp] = id2; + tp++; + binary[binary_length] = ' '; + binary_length++; + infoSpace(id2); + } + + if (inputDataType == DataType.GS1) { + target[tp] = 232; + tp++; + binary[binary_length] = ' '; + binary_length++; + info("FNC1 "); + } /* FNC1 */ + + if (readerInit) { + target[tp] = 234; /* FNC3 */ + tp++; /* Reader Programming */ + binary[binary_length] = ' '; + binary_length++; + info("RP "); + } + + if (eciMode != 3) { + target[tp] = 241; // ECI + tp++; + binary[binary_length] = ' '; + binary_length++; + if (eciMode <= 126) { + target[tp] = eciMode + 1; + tp++; + binary[binary_length] = ' '; + binary_length++; + } + if ((eciMode >= 127) && (eciMode <= 16382)) { + target[tp] = ((eciMode - 127) / 254) + 128; + tp++; + binary[binary_length] = ' '; + binary_length++; + target[tp] = ((eciMode - 127) % 254) + 1; + tp++; + binary[binary_length] = ' '; + binary_length++; + } + if (eciMode >= 16383) { + target[tp] = ((eciMode - 16383) / 64516) + 192; + tp++; + binary[binary_length] = ' '; + binary_length++; + target[tp] = (((eciMode - 16383) / 254) % 254) + 1; + tp++; + binary[binary_length] = ' '; + binary_length++; + target[tp] = ((eciMode - 16383) % 254) + 1; + tp++; + binary[binary_length] = ' '; + binary_length++; + } + info("ECI " + eciMode + " "); + } + + /* Check for Macro05/Macro06 */ + /* "[)>[RS]05[GS]...[RS][EOT]" -> CW 236 */ + /* "[)>[RS]06[GS]...[RS][EOT]" -> CW 237 */ + if (tp == 0 & sp == 0 && inputlen >= 9) { + if (inputData[0] == '[' && inputData[1] == ')' && inputData[2] == '>' + && inputData[3] == '\u001e' && inputData[4] == '0' + && (inputData[5] == '5' || inputData[5] == '6') + && inputData[6] == '\u001d' + && inputData[inputlen - 2] == '\u001e' + && inputData[inputlen - 1] == '\u0004') { + /* Output macro codeword */ + if (inputData[5] == '5') { + target[tp] = 236; + info("Macro05 "); + } else { + target[tp] = 237; + info("Macro06 "); + } + tp++; + binary[binary_length] = ' '; + binary_length++; + /* Remove macro characters from input string */ + sp = 7; + inputlen -= 2; + inputData = Arrays.copyOf(inputData, inputData.length - 2); + } + } + + while (sp < inputlen) { + + current_mode = next_mode; + + /* step (b) - ASCII encodation */ + if (current_mode == Mode.DM_ASCII) { + next_mode = Mode.DM_ASCII; + + for (i = 0; i < 8; i++) { + process_buffer[i] = 0; + } + + if (isTwoDigits(sp)) { + target[tp] = (10 * Character.getNumericValue(inputData[sp])) + + Character.getNumericValue(inputData[sp + 1]) + 130; + infoSpace(target[tp] - 130); + tp++; + binary[binary_length] = ' '; + binary_length++; + sp += 2; + } else { + next_mode = lookAheadTest(sp, current_mode); + + if (next_mode != Mode.DM_ASCII) { + switch (next_mode) { + case DM_C40: + target[tp] = 230; + tp++; + binary[binary_length] = ' '; + binary_length++; + info("C40 "); + break; + case DM_TEXT: + target[tp] = 239; + tp++; + binary[binary_length] = ' '; + binary_length++; + info("TEX "); + break; + case DM_X12: + target[tp] = 238; + tp++; + binary[binary_length] = ' '; + binary_length++; + info("X12 "); + break; + case DM_EDIFACT: + target[tp] = 240; + tp++; + binary[binary_length] = ' '; + binary_length++; + info("EDI "); + break; + case DM_BASE256: + target[tp] = 231; + tp++; + binary[binary_length] = ' '; + binary_length++; + info("BAS "); + break; + } + } else { + if (inputData[sp] > 127) { + target[tp] = 235; /* FNC4 */ + + info("FNC4 "); + tp++; + target[tp] = (inputData[sp] - 128) + 1; + infoSpace(target[tp] - 1); + tp++; + binary[binary_length] = ' '; + binary_length++; + binary[binary_length] = ' '; + binary_length++; + } else { + if (inputData[sp] == FNC1) { + if (separatorGs) { + target[tp] = 29 + 1; /* GS */ + info("GS "); + } else { + target[tp] = 232; /* FNC1 */ + info("FNC1 "); + } + } else { + target[tp] = inputData[sp] + 1; + infoSpace(target[tp] - 1); + } + tp++; + binary[binary_length] = ' '; + binary_length++; + } + sp++; } - - if (shift_set != 0) { - process_buffer[process_p] = shift_set - 1; - process_p++; - } - process_buffer[process_p] = value; - process_p++; - - while (process_p >= 3) { - tp = addTriplet(process_buffer[0], process_buffer[1], process_buffer[2], target, tp); - - binary[binary_length] = ' '; - binary_length++; - binary[binary_length] = ' '; - binary_length++; - info("(" + process_buffer[0] + " " + process_buffer[1] + " " + process_buffer[2] + ") "); - - process_buffer[0] = process_buffer[3]; - process_buffer[1] = process_buffer[4]; - process_buffer[2] = process_buffer[5]; - process_buffer[3] = 0; - process_buffer[4] = 0; - process_buffer[5] = 0; - process_p -= 3; - } - sp++; - } - } - - /* step (d) Text encodation */ - if (current_mode == Mode.DM_TEXT) { - int shift_set, value; - - next_mode = Mode.DM_TEXT; - if (process_p == 0) { - next_mode = lookAheadTest(sp, current_mode); - } - - if (next_mode != Mode.DM_TEXT) { - target[tp] = 254; - tp++; - binary[binary_length] = ' '; - binary_length++; /* Unlatch */ - - next_mode = Mode.DM_ASCII; - info("ASC "); - } else { - if (inputData[sp] == FNC1) { - if (separatorGs) { - shift_set = 1; - value = 29; /* GS */ - } else { - shift_set = 2; - value = 27; /* FNC1 */ - } - } else if (inputData[sp] > 127) { - process_buffer[process_p] = 1; - process_p++; - process_buffer[process_p] = 30; - process_p++; /* Upper Shift */ - - shift_set = TEXT_SHIFT[inputData[sp] - 128]; - value = TEXT_VALUE[inputData[sp] - 128]; - } else { - shift_set = TEXT_SHIFT[inputData[sp]]; - value = TEXT_VALUE[inputData[sp]]; - } - - if (shift_set != 0) { - process_buffer[process_p] = shift_set - 1; - process_p++; - } - process_buffer[process_p] = value; - process_p++; - - while (process_p >= 3) { - tp = addTriplet(process_buffer[0], process_buffer[1], process_buffer[2], target, tp); - - binary[binary_length] = ' '; - binary_length++; - binary[binary_length] = ' '; - binary_length++; - info("(" + process_buffer[0] + " " + process_buffer[1] + " " + process_buffer[2] + ") "); - - process_buffer[0] = process_buffer[3]; - process_buffer[1] = process_buffer[4]; - process_buffer[2] = process_buffer[5]; - process_buffer[3] = 0; - process_buffer[4] = 0; - process_buffer[5] = 0; - process_p -= 3; - } - sp++; - } - } - - /* step (e) X12 encodation */ - if (current_mode == Mode.DM_X12) { - int value = 0; - - if (isX12(inputData[sp])) { - next_mode = Mode.DM_X12; - if (process_p == 0) { - next_mode = lookAheadTest(sp, current_mode); - } - } else { - next_mode = Mode.DM_ASCII; - } - - if (next_mode != Mode.DM_X12) { - sp -= process_p; // we're about to throw away the buffer, so we'll need to re-process buffered data - process_p = 0; // throw away buffer, if any - target[tp] = 254; - tp++; - binary[binary_length] = ' '; - binary_length++; /* Unlatch */ - - next_mode = Mode.DM_ASCII; - info("ASC "); - } else { - if (inputData[sp] == 13) { - value = 0; - } - if (inputData[sp] == '*') { - value = 1; - } - if (inputData[sp] == '>') { - value = 2; - } - if (inputData[sp] == ' ') { - value = 3; - } - if ((inputData[sp] >= '0') && (inputData[sp] <= '9')) { - value = (inputData[sp] - '0') + 4; - } - if ((inputData[sp] >= 'A') && (inputData[sp] <= 'Z')) { - value = (inputData[sp] - 'A') + 14; - } - - process_buffer[process_p] = value; - process_p++; - - while (process_p >= 3) { - tp = addTriplet(process_buffer[0], process_buffer[1], process_buffer[2], target, tp); - - binary[binary_length] = ' '; - binary_length++; - binary[binary_length] = ' '; - binary_length++; - info("(" + process_buffer[0] + " " + process_buffer[1] + " " + process_buffer[2] + ") "); - - process_buffer[0] = process_buffer[3]; - process_buffer[1] = process_buffer[4]; - process_buffer[2] = process_buffer[5]; - process_buffer[3] = 0; - process_buffer[4] = 0; - process_buffer[5] = 0; - process_p -= 3; - } - sp++; - } - } - - /* step (f) EDIFACT encodation */ - if (current_mode == Mode.DM_EDIFACT) { - int value = 0; - - next_mode = Mode.DM_EDIFACT; - if (process_p == 3) { - next_mode = lookAheadTest(sp, current_mode); - } - - if (next_mode != Mode.DM_EDIFACT) { - process_buffer[process_p] = 31; - process_p++; - next_mode = Mode.DM_ASCII; - } else { - if ((inputData[sp] >= '@') && (inputData[sp] <= '^')) { - value = inputData[sp] - '@'; - } - if ((inputData[sp] >= ' ') && (inputData[sp] <= '?')) { - value = inputData[sp]; - } - - process_buffer[process_p] = value; - process_p++; - sp++; - } - - while (process_p >= 4) { - target[tp] = (process_buffer[0] << 2) - + ((process_buffer[1] & 0x30) >> 4); - tp++; - target[tp] = ((process_buffer[1] & 0x0f) << 4) - + ((process_buffer[2] & 0x3c) >> 2); - tp++; - target[tp] = ((process_buffer[2] & 0x03) << 6) - + process_buffer[3]; - tp++; - binary[binary_length] = ' '; - binary_length++; - binary[binary_length] = ' '; - binary_length++; - binary[binary_length] = ' '; - binary_length++; - info("(" + process_buffer[0] + " " + process_buffer[1] + " " + process_buffer[2] + " " + process_buffer[3] + ") "); - - process_buffer[0] = process_buffer[4]; - process_buffer[1] = process_buffer[5]; - process_buffer[2] = process_buffer[6]; - process_buffer[3] = process_buffer[7]; - process_buffer[4] = 0; - process_buffer[5] = 0; - process_buffer[6] = 0; - process_buffer[7] = 0; - process_p -= 4; - } - } - - /* step (g) Base 256 encodation */ - if (current_mode == Mode.DM_BASE256) { - next_mode = lookAheadTest(sp, current_mode); - - if (next_mode == Mode.DM_BASE256) { - target[tp] = inputData[sp]; - infoSpace(target[tp]); - tp++; - sp++; - binary[binary_length] = 'b'; - binary_length++; - } else { - next_mode = Mode.DM_ASCII; - info("ASC "); - } - } - - if (tp > 1558) { - throw new OkapiInputException("Input too long to fit any Data Matrix symbol"); - } - - } /* while */ - - /* Add length and randomising algorithm to b256 */ - i = 0; - while (i < tp) { - if (binary[i] == 'b') { - if ((i == 0) || (binary[i - 1] != 'b')) { - /* start of binary data */ - int binary_count; /* length of b256 data */ - - for (binary_count = 0; binary_count + i < tp && binary[binary_count + i] == 'b'; - binary_count++); - - if (binary_count <= 249) { - insertAt(i, 'b'); - insertValueAt(i, tp, (char) binary_count); - tp++; - } else { - insertAt(i, 'b'); - insertAt(i + 1, 'b'); - insertValueAt(i, tp, (char) ((binary_count / 250) + 249)); - tp++; - insertValueAt(i + 1, tp, (char) (binary_count % 250)); - tp++; - } - } - } - i++; - } - - for (i = 0; i < tp; i++) { - if (binary[i] == 'b') { - int prn, temp; - - prn = ((149 * (i + 1)) % 255) + 1; - temp = target[i] + prn; - if (temp <= 255) { - target[i] = temp; - } else { - target[i] = temp - 256; - } - } - } - - last_mode = current_mode; - return tp; - } - - private int encodeRemainder(int symbols_left, int tp) { - - int inputlen = inputData.length; - - switch (last_mode) { - case DM_C40: - case DM_TEXT: - if (process_p == 1) { // 1 data character left to encode - if (endsWithUpperShift(target, tp)) { - // Normally we would switch back to ASCII mode (either implicitly or explicitly) and encode the - // last data character in ASCII mode... But in this case, we have an extended ASCII value and - // we've already gone through the necessary "Shift 2 + Upper Shift" sequence in C40 or TEXT mode... - // ASCII encodation would actually require 3 codewords (Unlatch + Upper Shift again + value), vs - // 2 codewords (one value triplet) to just stay in C40 or TEXT mode... So we stay in C40 or TEXT - // mode, encode the last extended ASCII char, and pad with another "Shift 2 + Upper Shift" (1, 30). - // Note that in this scenario Zint actually backtracks to the last complete triplet and encodes the - // remainder (which can be quite a bit) in ASCII mode. - tp = addTriplet(process_buffer[0], 1, 30, target, tp); - info("(" + process_buffer[0] + " 1 30) "); - if (symbols_left > 2) { - target[tp] = 254; // Unlatch - tp++; - info("ASC "); - } - } else { - // Standard approach: go back to ASCII mode, either implicitly or explicitly - if (symbols_left > 1) { - target[tp] = 254; // Unlatch and encode remaining data in ASCII - tp++; - info("ASC "); - } - target[tp] = inputData[inputlen - 1] + 1; - infoSpace(target[tp] - 1); - tp++; - } - } else if (process_p == 2) { // 2 data characters left to encode - // Pad with shift 1 value (0) and encode as double. - tp = addTriplet(process_buffer[0], process_buffer[1], 0, target, tp); - info("(" + process_buffer[0] + " " + process_buffer[1] + " 0) "); - if (symbols_left > 2) { - target[tp] = 254; // Unlatch - tp++; - info("ASC "); - } - } else { - if (symbols_left > 0) { - target[tp] = 254; // Unlatch - tp++; - info("ASC "); - } - } - break; - - case DM_X12: - if (symbols_left == 1 && process_p == 1) { - // Unlatch not required, encode directly in ASCII - target[tp] = inputData[inputlen - 1] + 1; - infoSpace(target[tp] - 1); - tp++; - } else { - if (symbols_left > 0) { - target[tp] = 254; // Unlatch - tp++; - info("ASC "); - } - if (process_p == 1) { - target[tp] = inputData[inputlen - 1] + 1; - infoSpace(target[tp] - 1); - tp++; - } else if (process_p == 2) { - target[tp] = inputData[inputlen - 2] + 1; - infoSpace(target[tp] - 1); - tp++; - target[tp] = inputData[inputlen - 1] + 1; - infoSpace(target[tp] - 1); - tp++; - } - } - break; - - case DM_EDIFACT: - if (symbols_left <= 2) { - // Unlatch not required, encode directly in ASCII - if (process_p == 1) { - target[tp] = inputData[inputlen - 1] + 1; - infoSpace(target[tp] - 1); - tp++; - } - if (process_p == 2) { - target[tp] = inputData[inputlen - 2] + 1; - infoSpace(target[tp] - 1); - tp++; - target[tp] = inputData[inputlen - 1] + 1; - infoSpace(target[tp] - 1); - tp++; - } - } else { - // Append EDIFACT unlatch value (31) and empty buffer - if (process_p == 0) { - target[tp] = (31 << 2); - tp++; - info("(31 0 0 0) "); - } else if (process_p == 1) { - target[tp] = ((process_buffer[0] << 2) + ((31 & 0x30) >> 4)); - tp++; - target[tp] = ((31 & 0x0f) << 4); - tp++; - info("(" + process_buffer[0] + " 31 0 0) "); - } else if (process_p == 2) { - target[tp] = ((process_buffer[0] << 2) + ((process_buffer[1] & 0x30) >> 4)); - tp++; - target[tp] = (((process_buffer[1] & 0x0f) << 4) + ((31 & 0x3c) >> 2)); - tp++; - target[tp] = (((31 & 0x03) << 6)); - tp++; - info("(" + process_buffer[0] + " " + process_buffer[1] + " 31 0) "); - } else if (process_p == 3) { - target[tp] = ((process_buffer[0] << 2) + ((process_buffer[1] & 0x30) >> 4)); - tp++; - target[tp] = (((process_buffer[1] & 0x0f) << 4) + ((process_buffer[2] & 0x3c) >> 2)); - tp++; - target[tp] = (((process_buffer[2] & 0x03) << 6) + 31); - tp++; - info("(" + process_buffer[0] + " " + process_buffer[1] + " " + process_buffer[2] + " 31) "); - } - } - break; - } - - infoLine(); - info("Codewords: "); - for (int i = 0; i < tp; i++) { - infoSpace(target[i]); - } - infoLine(); - - return tp; - } - - // C40 and TEXT encodation, per spec section 5.2.5 (3 values -> 2 codewords) - private static int addTriplet(int c1, int c2, int c3, int[] codewords, int i) { - int val = (1600 * c1) + (40 * c2) + c3 + 1; - codewords[i++] = val / 256; - codewords[i++] = val % 256; - return i; - } - - // extracts the last two values in the last C40 or TEXT triplet, and returns - // true if they are Shift 2 (value 1) + Upper Shift (value 30) - private static boolean endsWithUpperShift(int[] codewords, int i) { - return (i >= 2 && (((codewords[i - 2] * 256) + codewords[i - 1]) % 1600) == (40 * 1) + 30 + 1); - } - - private boolean isTwoDigits(int pos) { - return pos + 1 < inputData.length && - Character.isDigit((char) inputData[pos]) && - Character.isDigit((char) inputData[pos + 1]); - } - - private Mode lookAheadTest(int position, Mode current_mode) { - - /* 'look ahead test' from Annex P */ - - double ascii_count, c40_count, text_count, x12_count, edf_count, b256_count, best_count; - int sp; - int sourcelen = inputData.length; - Mode best_scheme = Mode.NULL; - double stiction = (1.0F / 24.0F); // smallest change to act on, to get around floating point inaccuracies - - /* step (j) */ - if (current_mode == Mode.DM_ASCII) { - ascii_count = 0.0; - c40_count = 1.0; - text_count = 1.0; - x12_count = 1.0; - edf_count = 1.0; - b256_count = 1.25; - } else { - ascii_count = 1.0; - c40_count = 2.0; - text_count = 2.0; - x12_count = 2.0; - edf_count = 2.0; - b256_count = 2.25; - } - - switch (current_mode) { - case DM_C40: // (j)(2) - c40_count = 0.0; - break; - case DM_TEXT: // (j)(3) - text_count = 0.0; - break; - case DM_X12: // (j)(4) - x12_count = 0.0; - break; - case DM_EDIFACT: // (j)(5) - edf_count = 0.0; - break; - case DM_BASE256: // (j)(6) - b256_count = 0.0; - break; - } - - sp = position; - - do { - if (sp == sourcelen) { - /* At the end of data ... step (k) */ - ascii_count = Math.ceil(ascii_count); - b256_count = Math.ceil(b256_count); - edf_count = Math.ceil(edf_count); - text_count = Math.ceil(text_count); - x12_count = Math.ceil(x12_count); - c40_count = Math.ceil(c40_count); - - best_count = c40_count; - best_scheme = Mode.DM_C40; // (k)(7) - - if (x12_count < (best_count - stiction)) { - best_count = x12_count; - best_scheme = Mode.DM_X12; // (k)(6) - } - - if (text_count < (best_count - stiction)) { - best_count = text_count; - best_scheme = Mode.DM_TEXT; // (k)(5) - } - - if (edf_count < (best_count - stiction)) { - best_count = edf_count; - best_scheme = Mode.DM_EDIFACT; // (k)(4) - } - - if (b256_count < (best_count - stiction)) { - best_count = b256_count; - best_scheme = Mode.DM_BASE256; // (k)(3) - } - - if (ascii_count <= (best_count + stiction)) { - best_scheme = Mode.DM_ASCII; // (k)(2) - } - } else { - - /* ascii ... step (l) */ - if ((inputData[sp] >= '0') && (inputData[sp] <= '9')) { - ascii_count += 0.5; // (l)(1) - } else { - if (inputData[sp] > 127) { - ascii_count = Math.ceil(ascii_count) + 2.0; // (l)(2) - } else { - ascii_count = Math.ceil(ascii_count) + 1.0; // (l)(3) - } - } - - /* c40 ... step (m) */ - if ((inputData[sp] == ' ') || - (((inputData[sp] >= '0') && (inputData[sp] <= '9')) || - ((inputData[sp] >= 'A') && (inputData[sp] <= 'Z')))) { - c40_count += (2.0 / 3.0); // (m)(1) - } else { - if (inputData[sp] > 127) { - c40_count += (8.0 / 3.0); // (m)(2) - } else { - c40_count += (4.0 / 3.0); // (m)(3) - } - } - - /* text ... step (n) */ - if ((inputData[sp] == ' ') || - (((inputData[sp] >= '0') && (inputData[sp] <= '9')) || - ((inputData[sp] >= 'a') && (inputData[sp] <= 'z')))) { - text_count += (2.0 / 3.0); // (n)(1) - } else { - if (inputData[sp] > 127) { - text_count += (8.0 / 3.0); // (n)(2) - } else { - text_count += (4.0 / 3.0); // (n)(3) - } - } - - /* x12 ... step (o) */ - if (isX12(inputData[sp])) { - x12_count += (2.0 / 3.0); // (o)(1) - } else { - if (inputData[sp] > 127) { - x12_count += (13.0 / 3.0); // (o)(2) - } else { - x12_count += (10.0 / 3.0); // (o)(3) - } - } - - /* edifact ... step (p) */ - if ((inputData[sp] >= ' ') && (inputData[sp] <= '^')) { - edf_count += (3.0 / 4.0); // (p)(1) - } else { - if (inputData[sp] > 127) { - edf_count += 17.0; // (p)(2) > Value changed from ISO - } else { - edf_count += 13.0; // (p)(3) > Value changed from ISO - } - } - if (inputData[sp] == FNC1) { - edf_count += 13.0; // > Value changed from ISO - } - - /* base 256 ... step (q) */ - if (inputData[sp] == FNC1) { - b256_count += 4.0; // (q)(1) - } else { - b256_count += 1.0; // (q)(2) - } - } - - if (sp >= position + 3) { - /* 4 data characters processed ... step (r) */ - - /* step (r)(6) */ - if (((c40_count + 1.0) < (ascii_count - stiction)) && - ((c40_count + 1.0) < (b256_count - stiction)) && - ((c40_count + 1.0) < (edf_count - stiction)) && - ((c40_count + 1.0) < (text_count - stiction))) { - - if (c40_count < (x12_count - stiction)) { - best_scheme = Mode.DM_C40; - } - - if ((c40_count >= (x12_count - stiction)) - && (c40_count <= (x12_count + stiction))) { - if (p_r_6_2_1(sp, sourcelen)) { - // Test (r)(6)(ii)(i) - best_scheme = Mode.DM_X12; - } else { - best_scheme = Mode.DM_C40; - } - } - } - - /* step (r)(5) */ - if (((x12_count + 1.0) < (ascii_count - stiction)) && - ((x12_count + 1.0) < (b256_count - stiction)) && - ((x12_count + 1.0) < (edf_count - stiction)) && - ((x12_count + 1.0) < (text_count - stiction)) && - ((x12_count + 1.0) < (c40_count - stiction))) { - best_scheme = Mode.DM_X12; - } - - /* step (r)(4) */ - if (((text_count + 1.0) < (ascii_count - stiction)) && - ((text_count + 1.0) < (b256_count - stiction)) && - ((text_count + 1.0) < (edf_count - stiction)) && - ((text_count + 1.0) < (x12_count - stiction)) && - ((text_count + 1.0) < (c40_count - stiction))) { - best_scheme = Mode.DM_TEXT; - } - - /* step (r)(3) */ - if (((edf_count + 1.0) < (ascii_count - stiction)) && - ((edf_count + 1.0) < (b256_count - stiction)) && - ((edf_count + 1.0) < (text_count - stiction)) && - ((edf_count + 1.0) < (x12_count - stiction)) && - ((edf_count + 1.0) < (c40_count - stiction))) { - best_scheme = Mode.DM_EDIFACT; - } - - /* step (r)(2) */ - if (((b256_count + 1.0) <= (ascii_count + stiction)) || - (((b256_count + 1.0) < (edf_count - stiction)) && - ((b256_count + 1.0) < (text_count - stiction)) && - ((b256_count + 1.0) < (x12_count - stiction)) && - ((b256_count + 1.0) < (c40_count - stiction)))) { - best_scheme = Mode.DM_BASE256; - } - - /* step (r)(1) */ - if (((ascii_count + 1.0) <= (b256_count + stiction)) && - ((ascii_count + 1.0) <= (edf_count + stiction)) && - ((ascii_count + 1.0) <= (text_count + stiction)) && - ((ascii_count + 1.0) <= (x12_count + stiction)) && - ((ascii_count + 1.0) <= (c40_count + stiction))) { - best_scheme = Mode.DM_ASCII; - } - } - - sp++; - - } while (best_scheme == Mode.NULL); // step (s) - - return best_scheme; - } - - private boolean p_r_6_2_1(int position, int sourcelen) { - /* Annex P section (r)(6)(ii)(I) - "If one of the three X12 terminator/separator characters first - occurs in the yet to be processed data before a non-X12 character..." - */ - - int i; - int nonX12Position = 0; - int specialX12Position = 0; - boolean retval = false; - - for (i = position; i < sourcelen; i++) { - if (nonX12Position == 0 && !isX12(inputData[i])) { - nonX12Position = i; - } - - if (specialX12Position == 0) { - if ((inputData[i] == (char) 13) || - (inputData[i] == '*') || - (inputData[i] == '>')) { - specialX12Position = i; - } - } - } - - if ((nonX12Position != 0) && (specialX12Position != 0)) { - if (specialX12Position < nonX12Position) { - retval = true; - } - } - - return retval; - } - - private boolean isX12(int source) { - return source == 13 || - source == 42 || - source == 62 || - source == 32 || - (source >= '0' && source <= '9') || - (source >= 'A' && source <= 'Z'); - } - - private void calculateErrorCorrection(int bytes, int datablock, int rsblock, int skew) { - // calculate and append ecc code, and if necessary interleave - int blocks = (bytes + 2) / datablock, b; - int n, p; - ReedSolomon rs = new ReedSolomon(); - - rs.init_gf(0x12d); - rs.init_code(rsblock, 1); - - for (b = 0; b < blocks; b++) { - int[] buf = new int[256]; - int[] ecc = new int[256]; - - p = 0; - for (n = b; n < bytes; n += blocks) { - buf[p++] = target[n]; - } - rs.encode(p, buf); - for (n = 0; n < rsblock; n++) { - ecc[n] = rs.getResult(n); - } - p = rsblock - 1; // comes back reversed - for (n = b; n < rsblock * blocks; n += blocks) { - if (skew == 1) { - /* Rotate ecc data to make 144x144 size symbols acceptable */ - /* See http://groups.google.com/group/postscriptbarcode/msg/5ae8fda7757477da */ - if (b < 8) { - target[bytes + n + 2] = ecc[p--]; - } else { - target[bytes + n - 8] = ecc[p--]; - } - } else { - target[bytes + n] = ecc[p--]; - } - } - } - } - - private void insertAt(int pos, char newbit) { - /* Insert a character into the middle of a string at position posn */ - for (int i = binary_length; i > pos; i--) { - binary[i] = binary[i - 1]; - } - binary[pos] = newbit; - binary_length++; - } - - private void insertValueAt(int posn, int streamlen, char newbit) { - int i; - - for (i = streamlen; i > posn; i--) { - target[i] = target[i - 1]; - } - target[posn] = newbit; - } - - private void addPadBits(int tp, int tail_length) { - int i, prn, temp; - - for (i = tail_length; i > 0; i--) { - if (i == tail_length) { - target[tp] = 129; - tp++; /* Pad */ - } else { - prn = ((149 * (tp + 1)) % 253) + 1; - temp = 129 + prn; - if (temp <= 254) { - target[tp] = temp; - tp++; - } else { - target[tp] = temp - 254; - tp++; - } - } - } - } - - private void placeData(int NR, int NC) { - int r, c, p; - // invalidate - for (r = 0; r < NR; r++) { - for (c = 0; c < NC; c++) { - places[r * NC + c] = 0; - } - } - // start - p = 1; - r = 4; - c = 0; - do { - // check corner - if (r == NR && (c == 0)) { - placeCornerA(NR, NC, p++); - } - if (r == NR - 2 && (c == 0) && ((NC % 4) != 0)) { - placeCornerB(NR, NC, p++); - } - if (r == NR - 2 && (c == 0) && (NC % 8) == 4) { - placeCornerC(NR, NC, p++); - } - if (r == NR + 4 && c == 2 && ((NC % 8) == 0)) { - placeCornerD(NR, NC, p++); - } - // up/right - do { - if (r < NR && c >= 0 && (places[r * NC + c] == 0)) { - placeBlock(NR, NC, r, c, p++); - } - r -= 2; - c += 2; - } while (r >= 0 && c < NC); - r++; - c += 3; - // down/left - do { - if (r >= 0 && c < NC && (places[r * NC + c] == 0)) { - placeBlock(NR, NC, r, c, p++); - } - r += 2; - c -= 2; - } while (r < NR && c >= 0); - r += 3; - c++; - } while (r < NR || c < NC); - // unfilled corner - if (places[NR * NC - 1] == 0) { - places[NR * NC - 1] = places[NR * NC - NC - 2] = 1; - } - } - - private void placeCornerA(int NR, int NC, int p) { - placeBit(NR, NC, NR - 1, 0, p, 7); - placeBit(NR, NC, NR - 1, 1, p, 6); - placeBit(NR, NC, NR - 1, 2, p, 5); - placeBit(NR, NC, 0, NC - 2, p, 4); - placeBit(NR, NC, 0, NC - 1, p, 3); - placeBit(NR, NC, 1, NC - 1, p, 2); - placeBit(NR, NC, 2, NC - 1, p, 1); - placeBit(NR, NC, 3, NC - 1, p, 0); - } - - private void placeCornerB(int NR, int NC, int p) { - placeBit(NR, NC, NR - 3, 0, p, 7); - placeBit(NR, NC, NR - 2, 0, p, 6); - placeBit(NR, NC, NR - 1, 0, p, 5); - placeBit(NR, NC, 0, NC - 4, p, 4); - placeBit(NR, NC, 0, NC - 3, p, 3); - placeBit(NR, NC, 0, NC - 2, p, 2); - placeBit(NR, NC, 0, NC - 1, p, 1); - placeBit(NR, NC, 1, NC - 1, p, 0); - } - - private void placeCornerC(int NR, int NC, int p) { - placeBit(NR, NC, NR - 3, 0, p, 7); - placeBit(NR, NC, NR - 2, 0, p, 6); - placeBit(NR, NC, NR - 1, 0, p, 5); - placeBit(NR, NC, 0, NC - 2, p, 4); - placeBit(NR, NC, 0, NC - 1, p, 3); - placeBit(NR, NC, 1, NC - 1, p, 2); - placeBit(NR, NC, 2, NC - 1, p, 1); - placeBit(NR, NC, 3, NC - 1, p, 0); - } - - private void placeCornerD(int NR, int NC, int p) { - placeBit(NR, NC, NR - 1, 0, p, 7); - placeBit(NR, NC, NR - 1, NC - 1, p, 6); - placeBit(NR, NC, 0, NC - 3, p, 5); - placeBit(NR, NC, 0, NC - 2, p, 4); - placeBit(NR, NC, 0, NC - 1, p, 3); - placeBit(NR, NC, 1, NC - 3, p, 2); - placeBit(NR, NC, 1, NC - 2, p, 1); - placeBit(NR, NC, 1, NC - 1, p, 0); - } - - private void placeBlock(int NR, int NC, int r, int c, int p) { - placeBit(NR, NC, r - 2, c - 2, p, 7); - placeBit(NR, NC, r - 2, c - 1, p, 6); - placeBit(NR, NC, r - 1, c - 2, p, 5); - placeBit(NR, NC, r - 1, c - 1, p, 4); - placeBit(NR, NC, r - 1, c - 0, p, 3); - placeBit(NR, NC, r - 0, c - 2, p, 2); - placeBit(NR, NC, r - 0, c - 1, p, 1); - placeBit(NR, NC, r - 0, c - 0, p, 0); - } - - private void placeBit(int NR, int NC, int r, int c, int p, int b) { - if (r < 0) { - r += NR; - c += 4 - ((NR + 4) % 8); - } - if (c < 0) { - c += NC; - r += 4 - ((NC + 4) % 8); - } - places[r * NC + c] = (p << 3) + b; - } -} + } + } + + /* step (c) C40 encodation */ + if (current_mode == Mode.DM_C40) { + int shift_set, value; + + next_mode = Mode.DM_C40; + if (process_p == 0) { + next_mode = lookAheadTest(sp, current_mode); + } + + if (next_mode != Mode.DM_C40) { + target[tp] = 254; + tp++; + binary[binary_length] = ' '; + binary_length++; /* Unlatch */ + + next_mode = Mode.DM_ASCII; + info("ASC "); + } else { + if (inputData[sp] == FNC1) { + if (separatorGs) { + shift_set = 1; + value = 29; /* GS */ + } else { + shift_set = 2; + value = 27; /* FNC1 */ + } + } else if (inputData[sp] > 127) { + process_buffer[process_p] = 1; + process_p++; + process_buffer[process_p] = 30; + process_p++; /* Upper Shift */ + + shift_set = C40_SHIFT[inputData[sp] - 128]; + value = C40_VALUE[inputData[sp] - 128]; + } else { + shift_set = C40_SHIFT[inputData[sp]]; + value = C40_VALUE[inputData[sp]]; + } + + if (shift_set != 0) { + process_buffer[process_p] = shift_set - 1; + process_p++; + } + process_buffer[process_p] = value; + process_p++; + + while (process_p >= 3) { + tp = addTriplet(process_buffer[0], process_buffer[1], process_buffer[2], target, tp); + + binary[binary_length] = ' '; + binary_length++; + binary[binary_length] = ' '; + binary_length++; + info("(" + process_buffer[0] + " " + process_buffer[1] + " " + process_buffer[2] + ") "); + + process_buffer[0] = process_buffer[3]; + process_buffer[1] = process_buffer[4]; + process_buffer[2] = process_buffer[5]; + process_buffer[3] = 0; + process_buffer[4] = 0; + process_buffer[5] = 0; + process_p -= 3; + } + sp++; + } + } + + /* step (d) Text encodation */ + if (current_mode == Mode.DM_TEXT) { + int shift_set, value; + + next_mode = Mode.DM_TEXT; + if (process_p == 0) { + next_mode = lookAheadTest(sp, current_mode); + } + + if (next_mode != Mode.DM_TEXT) { + target[tp] = 254; + tp++; + binary[binary_length] = ' '; + binary_length++; /* Unlatch */ + + next_mode = Mode.DM_ASCII; + info("ASC "); + } else { + if (inputData[sp] == FNC1) { + if (separatorGs) { + shift_set = 1; + value = 29; /* GS */ + } else { + shift_set = 2; + value = 27; /* FNC1 */ + } + } else if (inputData[sp] > 127) { + process_buffer[process_p] = 1; + process_p++; + process_buffer[process_p] = 30; + process_p++; /* Upper Shift */ + + shift_set = TEXT_SHIFT[inputData[sp] - 128]; + value = TEXT_VALUE[inputData[sp] - 128]; + } else { + shift_set = TEXT_SHIFT[inputData[sp]]; + value = TEXT_VALUE[inputData[sp]]; + } + + if (shift_set != 0) { + process_buffer[process_p] = shift_set - 1; + process_p++; + } + process_buffer[process_p] = value; + process_p++; + + while (process_p >= 3) { + tp = addTriplet(process_buffer[0], process_buffer[1], process_buffer[2], target, tp); + + binary[binary_length] = ' '; + binary_length++; + binary[binary_length] = ' '; + binary_length++; + info("(" + process_buffer[0] + " " + process_buffer[1] + " " + process_buffer[2] + ") "); + + process_buffer[0] = process_buffer[3]; + process_buffer[1] = process_buffer[4]; + process_buffer[2] = process_buffer[5]; + process_buffer[3] = 0; + process_buffer[4] = 0; + process_buffer[5] = 0; + process_p -= 3; + } + sp++; + } + } + + /* step (e) X12 encodation */ + if (current_mode == Mode.DM_X12) { + int value = 0; + + if (isX12(inputData[sp])) { + next_mode = Mode.DM_X12; + if (process_p == 0) { + next_mode = lookAheadTest(sp, current_mode); + } + } else { + next_mode = Mode.DM_ASCII; + } + + if (next_mode != Mode.DM_X12) { + sp -= process_p; // we're about to throw away the buffer, so we'll need to re-process buffered data + process_p = 0; // throw away buffer, if any + target[tp] = 254; + tp++; + binary[binary_length] = ' '; + binary_length++; /* Unlatch */ + + next_mode = Mode.DM_ASCII; + info("ASC "); + } else { + if (inputData[sp] == 13) { + value = 0; + } + if (inputData[sp] == '*') { + value = 1; + } + if (inputData[sp] == '>') { + value = 2; + } + if (inputData[sp] == ' ') { + value = 3; + } + if ((inputData[sp] >= '0') && (inputData[sp] <= '9')) { + value = (inputData[sp] - '0') + 4; + } + if ((inputData[sp] >= 'A') && (inputData[sp] <= 'Z')) { + value = (inputData[sp] - 'A') + 14; + } + + process_buffer[process_p] = value; + process_p++; + + while (process_p >= 3) { + tp = addTriplet(process_buffer[0], process_buffer[1], process_buffer[2], target, tp); + + binary[binary_length] = ' '; + binary_length++; + binary[binary_length] = ' '; + binary_length++; + info("(" + process_buffer[0] + " " + process_buffer[1] + " " + process_buffer[2] + ") "); + + process_buffer[0] = process_buffer[3]; + process_buffer[1] = process_buffer[4]; + process_buffer[2] = process_buffer[5]; + process_buffer[3] = 0; + process_buffer[4] = 0; + process_buffer[5] = 0; + process_p -= 3; + } + sp++; + } + } + + /* step (f) EDIFACT encodation */ + if (current_mode == Mode.DM_EDIFACT) { + int value = 0; + + next_mode = Mode.DM_EDIFACT; + if (process_p == 3) { + next_mode = lookAheadTest(sp, current_mode); + } + + if (next_mode != Mode.DM_EDIFACT) { + process_buffer[process_p] = 31; + process_p++; + next_mode = Mode.DM_ASCII; + } else { + if ((inputData[sp] >= '@') && (inputData[sp] <= '^')) { + value = inputData[sp] - '@'; + } + if ((inputData[sp] >= ' ') && (inputData[sp] <= '?')) { + value = inputData[sp]; + } + + process_buffer[process_p] = value; + process_p++; + sp++; + } + + while (process_p >= 4) { + target[tp] = (process_buffer[0] << 2) + + ((process_buffer[1] & 0x30) >> 4); + tp++; + target[tp] = ((process_buffer[1] & 0x0f) << 4) + + ((process_buffer[2] & 0x3c) >> 2); + tp++; + target[tp] = ((process_buffer[2] & 0x03) << 6) + + process_buffer[3]; + tp++; + binary[binary_length] = ' '; + binary_length++; + binary[binary_length] = ' '; + binary_length++; + binary[binary_length] = ' '; + binary_length++; + info("(" + process_buffer[0] + " " + process_buffer[1] + " " + process_buffer[2] + " " + process_buffer[3] + ") "); + + process_buffer[0] = process_buffer[4]; + process_buffer[1] = process_buffer[5]; + process_buffer[2] = process_buffer[6]; + process_buffer[3] = process_buffer[7]; + process_buffer[4] = 0; + process_buffer[5] = 0; + process_buffer[6] = 0; + process_buffer[7] = 0; + process_p -= 4; + } + } + + /* step (g) Base 256 encodation */ + if (current_mode == Mode.DM_BASE256) { + next_mode = lookAheadTest(sp, current_mode); + + if (next_mode == Mode.DM_BASE256) { + target[tp] = inputData[sp]; + infoSpace(target[tp]); + tp++; + sp++; + binary[binary_length] = 'b'; + binary_length++; + } else { + next_mode = Mode.DM_ASCII; + info("ASC "); + } + } + + if (tp > 1558) { + throw new OkapiInputException("Input too long to fit any Data Matrix symbol"); + } + + } /* while */ + + /* Add length and randomising algorithm to b256 */ + i = 0; + while (i < tp) { + if (binary[i] == 'b') { + if ((i == 0) || (binary[i - 1] != 'b')) { + /* start of binary data */ + int binary_count; /* length of b256 data */ + + for (binary_count = 0; binary_count + i < tp && binary[binary_count + i] == 'b'; + binary_count++); + + if (binary_count <= 249) { + insertAt(i, 'b'); + insertValueAt(i, tp, (char) binary_count); + tp++; + } else { + insertAt(i, 'b'); + insertAt(i + 1, 'b'); + insertValueAt(i, tp, (char) ((binary_count / 250) + 249)); + tp++; + insertValueAt(i + 1, tp, (char) (binary_count % 250)); + tp++; + } + } + } + i++; + } + + for (i = 0; i < tp; i++) { + if (binary[i] == 'b') { + int prn, temp; + + prn = ((149 * (i + 1)) % 255) + 1; + temp = target[i] + prn; + if (temp <= 255) { + target[i] = temp; + } else { + target[i] = temp - 256; + } + } + } + + last_mode = current_mode; + return tp; + } + + private int encodeRemainder(int symbols_left, int tp) { + + int inputlen = inputData.length; + + switch (last_mode) { + case DM_C40: + case DM_TEXT: + if (process_p == 1) { // 1 data character left to encode + if (endsWithUpperShift(target, tp)) { + // Normally we would switch back to ASCII mode (either implicitly or explicitly) and encode the + // last data character in ASCII mode... But in this case, we have an extended ASCII value and + // we've already gone through the necessary "Shift 2 + Upper Shift" sequence in C40 or TEXT mode... + // ASCII encodation would actually require 3 codewords (Unlatch + Upper Shift again + value), vs + // 2 codewords (one value triplet) to just stay in C40 or TEXT mode... So we stay in C40 or TEXT + // mode, encode the last extended ASCII char, and pad with another "Shift 2 + Upper Shift" (1, 30). + // Note that in this scenario Zint actually backtracks to the last complete triplet and encodes the + // remainder (which can be quite a bit) in ASCII mode. + tp = addTriplet(process_buffer[0], 1, 30, target, tp); + info("(" + process_buffer[0] + " 1 30) "); + if (symbols_left > 2) { + target[tp] = 254; // Unlatch + tp++; + info("ASC "); + } + } else { + // Standard approach: go back to ASCII mode, either implicitly or explicitly + if (symbols_left > 1) { + target[tp] = 254; // Unlatch and encode remaining data in ASCII + tp++; + info("ASC "); + } + target[tp] = inputData[inputlen - 1] + 1; + infoSpace(target[tp] - 1); + tp++; + } + } else if (process_p == 2) { // 2 data characters left to encode + // Pad with shift 1 value (0) and encode as double. + tp = addTriplet(process_buffer[0], process_buffer[1], 0, target, tp); + info("(" + process_buffer[0] + " " + process_buffer[1] + " 0) "); + if (symbols_left > 2) { + target[tp] = 254; // Unlatch + tp++; + info("ASC "); + } + } else { + if (symbols_left > 0) { + target[tp] = 254; // Unlatch + tp++; + info("ASC "); + } + } + break; + + case DM_X12: + if (symbols_left == 1 && process_p == 1) { + // Unlatch not required, encode directly in ASCII + target[tp] = inputData[inputlen - 1] + 1; + infoSpace(target[tp] - 1); + tp++; + } else { + if (symbols_left > 0) { + target[tp] = 254; // Unlatch + tp++; + info("ASC "); + } + if (process_p == 1) { + target[tp] = inputData[inputlen - 1] + 1; + infoSpace(target[tp] - 1); + tp++; + } else if (process_p == 2) { + target[tp] = inputData[inputlen - 2] + 1; + infoSpace(target[tp] - 1); + tp++; + target[tp] = inputData[inputlen - 1] + 1; + infoSpace(target[tp] - 1); + tp++; + } + } + break; + + case DM_EDIFACT: + if (symbols_left <= 2) { + // Unlatch not required, encode directly in ASCII + if (process_p == 1) { + target[tp] = inputData[inputlen - 1] + 1; + infoSpace(target[tp] - 1); + tp++; + } + if (process_p == 2) { + target[tp] = inputData[inputlen - 2] + 1; + infoSpace(target[tp] - 1); + tp++; + target[tp] = inputData[inputlen - 1] + 1; + infoSpace(target[tp] - 1); + tp++; + } + } else { + // Append EDIFACT unlatch value (31) and empty buffer + if (process_p == 0) { + target[tp] = (31 << 2); + tp++; + info("(31 0 0 0) "); + } else if (process_p == 1) { + target[tp] = ((process_buffer[0] << 2) + ((31 & 0x30) >> 4)); + tp++; + target[tp] = ((31 & 0x0f) << 4); + tp++; + info("(" + process_buffer[0] + " 31 0 0) "); + } else if (process_p == 2) { + target[tp] = ((process_buffer[0] << 2) + ((process_buffer[1] & 0x30) >> 4)); + tp++; + target[tp] = (((process_buffer[1] & 0x0f) << 4) + ((31 & 0x3c) >> 2)); + tp++; + target[tp] = (((31 & 0x03) << 6)); + tp++; + info("(" + process_buffer[0] + " " + process_buffer[1] + " 31 0) "); + } else if (process_p == 3) { + target[tp] = ((process_buffer[0] << 2) + ((process_buffer[1] & 0x30) >> 4)); + tp++; + target[tp] = (((process_buffer[1] & 0x0f) << 4) + ((process_buffer[2] & 0x3c) >> 2)); + tp++; + target[tp] = (((process_buffer[2] & 0x03) << 6) + 31); + tp++; + info("(" + process_buffer[0] + " " + process_buffer[1] + " " + process_buffer[2] + " 31) "); + } + } + break; + } + + infoLine(); + info("Codewords: "); + for (int i = 0; i < tp; i++) { + infoSpace(target[i]); + } + infoLine(); + + return tp; + } + + // C40 and TEXT encodation, per spec section 5.2.5 (3 values -> 2 codewords) + private static int addTriplet(int c1, int c2, int c3, int[] codewords, int i) { + int val = (1600 * c1) + (40 * c2) + c3 + 1; + codewords[i++] = val / 256; + codewords[i++] = val % 256; + return i; + } + + // extracts the last two values in the last C40 or TEXT triplet, and returns + // true if they are Shift 2 (value 1) + Upper Shift (value 30) + private static boolean endsWithUpperShift(int[] codewords, int i) { + return (i >= 2 && (((codewords[i - 2] * 256) + codewords[i - 1]) % 1600) == (40 * 1) + 30 + 1); + } + + private boolean isTwoDigits(int pos) { + return pos + 1 < inputData.length && + Character.isDigit((char) inputData[pos]) && + Character.isDigit((char) inputData[pos + 1]); + } + + private Mode lookAheadTest(int position, Mode current_mode) { + + /* 'look ahead test' from Annex P */ + + double ascii_count, c40_count, text_count, x12_count, edf_count, b256_count, best_count; + int sp; + int sourcelen = inputData.length; + Mode best_scheme = Mode.NULL; + double stiction = (1.0F / 24.0F); // smallest change to act on, to get around floating point inaccuracies + + /* step (j) */ + if (current_mode == Mode.DM_ASCII) { + ascii_count = 0.0; + c40_count = 1.0; + text_count = 1.0; + x12_count = 1.0; + edf_count = 1.0; + b256_count = 1.25; + } else { + ascii_count = 1.0; + c40_count = 2.0; + text_count = 2.0; + x12_count = 2.0; + edf_count = 2.0; + b256_count = 2.25; + } + + switch (current_mode) { + case DM_C40: // (j)(2) + c40_count = 0.0; + break; + case DM_TEXT: // (j)(3) + text_count = 0.0; + break; + case DM_X12: // (j)(4) + x12_count = 0.0; + break; + case DM_EDIFACT: // (j)(5) + edf_count = 0.0; + break; + case DM_BASE256: // (j)(6) + b256_count = 0.0; + break; + } + + sp = position; + + do { + if (sp == sourcelen) { + /* At the end of data ... step (k) */ + ascii_count = Math.ceil(ascii_count); + b256_count = Math.ceil(b256_count); + edf_count = Math.ceil(edf_count); + text_count = Math.ceil(text_count); + x12_count = Math.ceil(x12_count); + c40_count = Math.ceil(c40_count); + + best_count = c40_count; + best_scheme = Mode.DM_C40; // (k)(7) + + if (x12_count < (best_count - stiction)) { + best_count = x12_count; + best_scheme = Mode.DM_X12; // (k)(6) + } + + if (text_count < (best_count - stiction)) { + best_count = text_count; + best_scheme = Mode.DM_TEXT; // (k)(5) + } + + if (edf_count < (best_count - stiction)) { + best_count = edf_count; + best_scheme = Mode.DM_EDIFACT; // (k)(4) + } + + if (b256_count < (best_count - stiction)) { + best_count = b256_count; + best_scheme = Mode.DM_BASE256; // (k)(3) + } + + if (ascii_count <= (best_count + stiction)) { + best_scheme = Mode.DM_ASCII; // (k)(2) + } + } else { + + /* ascii ... step (l) */ + if ((inputData[sp] >= '0') && (inputData[sp] <= '9')) { + ascii_count += 0.5; // (l)(1) + } else { + if (inputData[sp] > 127) { + ascii_count = Math.ceil(ascii_count) + 2.0; // (l)(2) + } else { + ascii_count = Math.ceil(ascii_count) + 1.0; // (l)(3) + } + } + + /* c40 ... step (m) */ + if ((inputData[sp] == ' ') || + (((inputData[sp] >= '0') && (inputData[sp] <= '9')) || + ((inputData[sp] >= 'A') && (inputData[sp] <= 'Z')))) { + c40_count += (2.0 / 3.0); // (m)(1) + } else { + if (inputData[sp] > 127) { + c40_count += (8.0 / 3.0); // (m)(2) + } else { + c40_count += (4.0 / 3.0); // (m)(3) + } + } + + /* text ... step (n) */ + if ((inputData[sp] == ' ') || + (((inputData[sp] >= '0') && (inputData[sp] <= '9')) || + ((inputData[sp] >= 'a') && (inputData[sp] <= 'z')))) { + text_count += (2.0 / 3.0); // (n)(1) + } else { + if (inputData[sp] > 127) { + text_count += (8.0 / 3.0); // (n)(2) + } else { + text_count += (4.0 / 3.0); // (n)(3) + } + } + + /* x12 ... step (o) */ + if (isX12(inputData[sp])) { + x12_count += (2.0 / 3.0); // (o)(1) + } else { + if (inputData[sp] > 127) { + x12_count += (13.0 / 3.0); // (o)(2) + } else { + x12_count += (10.0 / 3.0); // (o)(3) + } + } + + /* edifact ... step (p) */ + if ((inputData[sp] >= ' ') && (inputData[sp] <= '^')) { + edf_count += (3.0 / 4.0); // (p)(1) + } else { + if (inputData[sp] > 127) { + edf_count += 17.0; // (p)(2) > Value changed from ISO + } else { + edf_count += 13.0; // (p)(3) > Value changed from ISO + } + } + if (inputData[sp] == FNC1) { + edf_count += 13.0; // > Value changed from ISO + } + + /* base 256 ... step (q) */ + if (inputData[sp] == FNC1) { + b256_count += 4.0; // (q)(1) + } else { + b256_count += 1.0; // (q)(2) + } + } + + if (sp >= position + 3) { + /* 4 data characters processed ... step (r) */ + + /* step (r)(6) */ + if (((c40_count + 1.0) < (ascii_count - stiction)) && + ((c40_count + 1.0) < (b256_count - stiction)) && + ((c40_count + 1.0) < (edf_count - stiction)) && + ((c40_count + 1.0) < (text_count - stiction))) { + + if (c40_count < (x12_count - stiction)) { + best_scheme = Mode.DM_C40; + } + + if ((c40_count >= (x12_count - stiction)) + && (c40_count <= (x12_count + stiction))) { + if (p_r_6_2_1(sp, sourcelen)) { + // Test (r)(6)(ii)(i) + best_scheme = Mode.DM_X12; + } else { + best_scheme = Mode.DM_C40; + } + } + } + + /* step (r)(5) */ + if (((x12_count + 1.0) < (ascii_count - stiction)) && + ((x12_count + 1.0) < (b256_count - stiction)) && + ((x12_count + 1.0) < (edf_count - stiction)) && + ((x12_count + 1.0) < (text_count - stiction)) && + ((x12_count + 1.0) < (c40_count - stiction))) { + best_scheme = Mode.DM_X12; + } + + /* step (r)(4) */ + if (((text_count + 1.0) < (ascii_count - stiction)) && + ((text_count + 1.0) < (b256_count - stiction)) && + ((text_count + 1.0) < (edf_count - stiction)) && + ((text_count + 1.0) < (x12_count - stiction)) && + ((text_count + 1.0) < (c40_count - stiction))) { + best_scheme = Mode.DM_TEXT; + } + + /* step (r)(3) */ + if (((edf_count + 1.0) < (ascii_count - stiction)) && + ((edf_count + 1.0) < (b256_count - stiction)) && + ((edf_count + 1.0) < (text_count - stiction)) && + ((edf_count + 1.0) < (x12_count - stiction)) && + ((edf_count + 1.0) < (c40_count - stiction))) { + best_scheme = Mode.DM_EDIFACT; + } + + /* step (r)(2) */ + if (((b256_count + 1.0) <= (ascii_count + stiction)) || + (((b256_count + 1.0) < (edf_count - stiction)) && + ((b256_count + 1.0) < (text_count - stiction)) && + ((b256_count + 1.0) < (x12_count - stiction)) && + ((b256_count + 1.0) < (c40_count - stiction)))) { + best_scheme = Mode.DM_BASE256; + } + + /* step (r)(1) */ + if (((ascii_count + 1.0) <= (b256_count + stiction)) && + ((ascii_count + 1.0) <= (edf_count + stiction)) && + ((ascii_count + 1.0) <= (text_count + stiction)) && + ((ascii_count + 1.0) <= (x12_count + stiction)) && + ((ascii_count + 1.0) <= (c40_count + stiction))) { + best_scheme = Mode.DM_ASCII; + } + } + + sp++; + + } while (best_scheme == Mode.NULL); // step (s) + + return best_scheme; + } + + private boolean p_r_6_2_1(int position, int sourcelen) { + /* Annex P section (r)(6)(ii)(I) + "If one of the three X12 terminator/separator characters first + occurs in the yet to be processed data before a non-X12 character..." + */ + + int i; + int nonX12Position = 0; + int specialX12Position = 0; + boolean retval = false; + + for (i = position; i < sourcelen; i++) { + if (nonX12Position == 0 && !isX12(inputData[i])) { + nonX12Position = i; + } + + if (specialX12Position == 0) { + if ((inputData[i] == (char) 13) || + (inputData[i] == '*') || + (inputData[i] == '>')) { + specialX12Position = i; + } + } + } + + if ((nonX12Position != 0) && (specialX12Position != 0)) { + if (specialX12Position < nonX12Position) { + retval = true; + } + } + + return retval; + } + + private boolean isX12(int source) { + return source == 13 || + source == 42 || + source == 62 || + source == 32 || + (source >= '0' && source <= '9') || + (source >= 'A' && source <= 'Z'); + } + + private void calculateErrorCorrection(int bytes, int datablock, int rsblock, int skew) { + // calculate and append ecc code, and if necessary interleave + int blocks = (bytes + 2) / datablock, b; + int n, p; + ReedSolomon rs = new ReedSolomon(); + + rs.init_gf(0x12d); + rs.init_code(rsblock, 1); + + for (b = 0; b < blocks; b++) { + int[] buf = new int[256]; + int[] ecc = new int[256]; + + p = 0; + for (n = b; n < bytes; n += blocks) { + buf[p++] = target[n]; + } + rs.encode(p, buf); + for (n = 0; n < rsblock; n++) { + ecc[n] = rs.getResult(n); + } + p = rsblock - 1; // comes back reversed + for (n = b; n < rsblock * blocks; n += blocks) { + if (skew == 1) { + /* Rotate ecc data to make 144x144 size symbols acceptable */ + /* See http://groups.google.com/group/postscriptbarcode/msg/5ae8fda7757477da */ + if (b < 8) { + target[bytes + n + 2] = ecc[p--]; + } else { + target[bytes + n - 8] = ecc[p--]; + } + } else { + target[bytes + n] = ecc[p--]; + } + } + } + } + + private void insertAt(int pos, char newbit) { + /* Insert a character into the middle of a string at position posn */ + for (int i = binary_length; i > pos; i--) { + binary[i] = binary[i - 1]; + } + binary[pos] = newbit; + binary_length++; + } + + private void insertValueAt(int posn, int streamlen, char newbit) { + int i; + + for (i = streamlen; i > posn; i--) { + target[i] = target[i - 1]; + } + target[posn] = newbit; + } + + private void addPadBits(int tp, int tail_length) { + int i, prn, temp; + + for (i = tail_length; i > 0; i--) { + if (i == tail_length) { + target[tp] = 129; + tp++; /* Pad */ + } else { + prn = ((149 * (tp + 1)) % 253) + 1; + temp = 129 + prn; + if (temp <= 254) { + target[tp] = temp; + tp++; + } else { + target[tp] = temp - 254; + tp++; + } + } + } + } + + private void placeData(int NR, int NC) { + int r, c, p; + // invalidate + for (r = 0; r < NR; r++) { + for (c = 0; c < NC; c++) { + places[r * NC + c] = 0; + } + } + // start + p = 1; + r = 4; + c = 0; + do { + // check corner + if (r == NR && (c == 0)) { + placeCornerA(NR, NC, p++); + } + if (r == NR - 2 && (c == 0) && ((NC % 4) != 0)) { + placeCornerB(NR, NC, p++); + } + if (r == NR - 2 && (c == 0) && (NC % 8) == 4) { + placeCornerC(NR, NC, p++); + } + if (r == NR + 4 && c == 2 && ((NC % 8) == 0)) { + placeCornerD(NR, NC, p++); + } + // up/right + do { + if (r < NR && c >= 0 && (places[r * NC + c] == 0)) { + placeBlock(NR, NC, r, c, p++); + } + r -= 2; + c += 2; + } while (r >= 0 && c < NC); + r++; + c += 3; + // down/left + do { + if (r >= 0 && c < NC && (places[r * NC + c] == 0)) { + placeBlock(NR, NC, r, c, p++); + } + r += 2; + c -= 2; + } while (r < NR && c >= 0); + r += 3; + c++; + } while (r < NR || c < NC); + // unfilled corner + if (places[NR * NC - 1] == 0) { + places[NR * NC - 1] = places[NR * NC - NC - 2] = 1; + } + } + + private void placeCornerA(int NR, int NC, int p) { + placeBit(NR, NC, NR - 1, 0, p, 7); + placeBit(NR, NC, NR - 1, 1, p, 6); + placeBit(NR, NC, NR - 1, 2, p, 5); + placeBit(NR, NC, 0, NC - 2, p, 4); + placeBit(NR, NC, 0, NC - 1, p, 3); + placeBit(NR, NC, 1, NC - 1, p, 2); + placeBit(NR, NC, 2, NC - 1, p, 1); + placeBit(NR, NC, 3, NC - 1, p, 0); + } + + private void placeCornerB(int NR, int NC, int p) { + placeBit(NR, NC, NR - 3, 0, p, 7); + placeBit(NR, NC, NR - 2, 0, p, 6); + placeBit(NR, NC, NR - 1, 0, p, 5); + placeBit(NR, NC, 0, NC - 4, p, 4); + placeBit(NR, NC, 0, NC - 3, p, 3); + placeBit(NR, NC, 0, NC - 2, p, 2); + placeBit(NR, NC, 0, NC - 1, p, 1); + placeBit(NR, NC, 1, NC - 1, p, 0); + } + + private void placeCornerC(int NR, int NC, int p) { + placeBit(NR, NC, NR - 3, 0, p, 7); + placeBit(NR, NC, NR - 2, 0, p, 6); + placeBit(NR, NC, NR - 1, 0, p, 5); + placeBit(NR, NC, 0, NC - 2, p, 4); + placeBit(NR, NC, 0, NC - 1, p, 3); + placeBit(NR, NC, 1, NC - 1, p, 2); + placeBit(NR, NC, 2, NC - 1, p, 1); + placeBit(NR, NC, 3, NC - 1, p, 0); + } + + private void placeCornerD(int NR, int NC, int p) { + placeBit(NR, NC, NR - 1, 0, p, 7); + placeBit(NR, NC, NR - 1, NC - 1, p, 6); + placeBit(NR, NC, 0, NC - 3, p, 5); + placeBit(NR, NC, 0, NC - 2, p, 4); + placeBit(NR, NC, 0, NC - 1, p, 3); + placeBit(NR, NC, 1, NC - 3, p, 2); + placeBit(NR, NC, 1, NC - 2, p, 1); + placeBit(NR, NC, 1, NC - 1, p, 0); + } + + private void placeBlock(int NR, int NC, int r, int c, int p) { + placeBit(NR, NC, r - 2, c - 2, p, 7); + placeBit(NR, NC, r - 2, c - 1, p, 6); + placeBit(NR, NC, r - 1, c - 2, p, 5); + placeBit(NR, NC, r - 1, c - 1, p, 4); + placeBit(NR, NC, r - 1, c - 0, p, 3); + placeBit(NR, NC, r - 0, c - 2, p, 2); + placeBit(NR, NC, r - 0, c - 1, p, 1); + placeBit(NR, NC, r - 0, c - 0, p, 0); + } + + private void placeBit(int NR, int NC, int r, int c, int p, int b) { + if (r < 0) { + r += NR; + c += 4 - ((NR + 4) % 8); + } + if (c < 0) { + c += NC; + r += 4 - ((NC + 4) % 8); + } + places[r * NC + c] = (p << 3) + b; + } +} diff --git a/src/main/java/uk/org/okapibarcode/backend/EanUpcAddOn.java b/src/main/java/uk/org/okapibarcode/backend/EanUpcAddOn.java index 2350dbf3..9e7e6245 100644 --- a/src/main/java/uk/org/okapibarcode/backend/EanUpcAddOn.java +++ b/src/main/java/uk/org/okapibarcode/backend/EanUpcAddOn.java @@ -1,121 +1,121 @@ -/* - * Copyright 2014 Robin Stuart - * - * 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. - */ - -package uk.org.okapibarcode.backend; - -/** - *

Implements EAN/UPC add-on bar code symbology according to BS EN 797:1996. - * - * @see Ean - * @see Upc - * @author Robin Stuart - */ -public class EanUpcAddOn extends Symbol { - - private static final String[] EAN_SET_A = { - "3211", "2221", "2122", "1411", "1132", "1231", "1114", "1312", "1213", "3112" - }; - - private static final String[] EAN_SET_B = { - "1123", "1222", "2212", "1141", "2311", "1321", "4111", "2131", "3121", "2113" - }; - - private static final String[] EAN2_PARITY = { - "AA", "AB", "BA", "BB" - }; - - private static final String[] EAN5_PARITY = { - "BBAAA", "BABAA", "BAABA", "BAAAB", "ABBAA", "AABBA", "AAABB", "ABABA", "ABAAB", "AABAB" - }; - - @Override - protected void encode() { - - if (!content.matches("[0-9]+")) { - throw OkapiInputException.invalidCharactersInInput(); - } - - if (content.length() > 5) { - throw OkapiInputException.inputTooLong(); - } - - int targetLength = (content.length() > 2 ? 5 : 2); - - if (content.length() < targetLength) { - for (int i = content.length(); i < targetLength; i++) { - content = '0' + content; - } - } - - String bars = (targetLength == 2 ? ean2(content) : ean5(content)); - - readable = content; - pattern = new String[] { bars }; - row_count = 1; - row_height = new int[] { -1 }; - } - - private static String ean2(String content) { - - int sum = ((content.charAt(0) - '0') * 10) + (content.charAt(1) - '0'); - String parity = EAN2_PARITY[sum % 4]; - - StringBuilder sb = new StringBuilder(); - sb.append("112"); /* Start */ - for (int i = 0; i < 2; i++) { - int val = content.charAt(i) - '0'; - if (parity.charAt(i) == 'B') { - sb.append(EAN_SET_B[val]); - } else { - sb.append(EAN_SET_A[val]); - } - if (i != 1) { /* Glyph separator */ - sb.append("11"); - } - } - - return sb.toString(); - } - - private static String ean5(String content) { - - int sum = 0; - for (int i = 0; i < 5; i++) { - if (i % 2 == 0) { - sum += 3 * (content.charAt(i) - '0'); - } else { - sum += 9 * (content.charAt(i) - '0'); - } - } - String parity = EAN5_PARITY[sum % 10]; - - StringBuilder sb = new StringBuilder(); - sb.append("112"); /* Start */ - for (int i = 0; i < 5; i++) { - int val = content.charAt(i) - '0'; - if (parity.charAt(i) == 'B') { - sb.append(EAN_SET_B[val]); - } else { - sb.append(EAN_SET_A[val]); - } - if (i != 4) { /* Glyph separator */ - sb.append("11"); - } - } - - return sb.toString(); - } -} +/* + * Copyright 2014 Robin Stuart + * + * 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. + */ + +package uk.org.okapibarcode.backend; + +/** + *

Implements EAN/UPC add-on bar code symbology according to BS EN 797:1996. + * + * @see Ean + * @see Upc + * @author Robin Stuart + */ +public class EanUpcAddOn extends Symbol { + + private static final String[] EAN_SET_A = { + "3211", "2221", "2122", "1411", "1132", "1231", "1114", "1312", "1213", "3112" + }; + + private static final String[] EAN_SET_B = { + "1123", "1222", "2212", "1141", "2311", "1321", "4111", "2131", "3121", "2113" + }; + + private static final String[] EAN2_PARITY = { + "AA", "AB", "BA", "BB" + }; + + private static final String[] EAN5_PARITY = { + "BBAAA", "BABAA", "BAABA", "BAAAB", "ABBAA", "AABBA", "AAABB", "ABABA", "ABAAB", "AABAB" + }; + + @Override + protected void encode() { + + if (!content.matches("[0-9]+")) { + throw OkapiInputException.invalidCharactersInInput(); + } + + if (content.length() > 5) { + throw OkapiInputException.inputTooLong(); + } + + int targetLength = (content.length() > 2 ? 5 : 2); + + if (content.length() < targetLength) { + for (int i = content.length(); i < targetLength; i++) { + content = '0' + content; + } + } + + String bars = (targetLength == 2 ? ean2(content) : ean5(content)); + + readable = content; + pattern = new String[] { bars }; + row_count = 1; + row_height = new int[] { -1 }; + } + + private static String ean2(String content) { + + int sum = ((content.charAt(0) - '0') * 10) + (content.charAt(1) - '0'); + String parity = EAN2_PARITY[sum % 4]; + + StringBuilder sb = new StringBuilder(); + sb.append("112"); /* Start */ + for (int i = 0; i < 2; i++) { + int val = content.charAt(i) - '0'; + if (parity.charAt(i) == 'B') { + sb.append(EAN_SET_B[val]); + } else { + sb.append(EAN_SET_A[val]); + } + if (i != 1) { /* Glyph separator */ + sb.append("11"); + } + } + + return sb.toString(); + } + + private static String ean5(String content) { + + int sum = 0; + for (int i = 0; i < 5; i++) { + if (i % 2 == 0) { + sum += 3 * (content.charAt(i) - '0'); + } else { + sum += 9 * (content.charAt(i) - '0'); + } + } + String parity = EAN5_PARITY[sum % 10]; + + StringBuilder sb = new StringBuilder(); + sb.append("112"); /* Start */ + for (int i = 0; i < 5; i++) { + int val = content.charAt(i) - '0'; + if (parity.charAt(i) == 'B') { + sb.append(EAN_SET_B[val]); + } else { + sb.append(EAN_SET_A[val]); + } + if (i != 4) { /* Glyph separator */ + sb.append("11"); + } + } + + return sb.toString(); + } +} diff --git a/src/main/java/uk/org/okapibarcode/backend/GridMatrix.java b/src/main/java/uk/org/okapibarcode/backend/GridMatrix.java index fc66add2..41f07f3e 100644 --- a/src/main/java/uk/org/okapibarcode/backend/GridMatrix.java +++ b/src/main/java/uk/org/okapibarcode/backend/GridMatrix.java @@ -1,1905 +1,1905 @@ -/* - * Copyright 2014 Robin Stuart - * - * 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. - */ - -package uk.org.okapibarcode.backend; - -import static uk.org.okapibarcode.util.Arrays.positionOf; - -import java.nio.charset.Charset; -import java.nio.charset.UnsupportedCharsetException; - -/** - *

Implements Grid Matrix bar code symbology according to AIMD014. - * - *

Grid Matrix is a matrix symbology which can encode characters in the ISO/IEC 8859-1 (Latin-1) - * character set as well as those in the GB-2312 character set. Input is assumed to be formatted as - * a UTF string. - * - * @author Robin Stuart - */ -public class GridMatrix extends Symbol { - - private static final char[] SHIFT_SET = { - /* From Table 7 - Encoding of control characters */ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* NULL -> SI */ - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* DLE -> US */ - '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', - ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~' - }; - - private static final int[] GM_RECOMMEND_CW = { - 9, 30, 59, 114, 170, 237, 315, 405, 506, 618, 741, 875, 1021 - }; - - private static final int[] GM_MAX_CW = { - 11, 40, 79, 146, 218, 305, 405, 521, 650, 794, 953, 1125, 1313 - }; - - private static final int[] GM_DATA_CODEWORDS = { - 0, 15, 13, 11, 9, - 45, 40, 35, 30, 25, - 89, 79, 69, 59, 49, - 146, 130, 114, 98, 81, - 218, 194, 170, 146, 121, - 305, 271, 237, 203, 169, - 405, 360, 315, 270, 225, - 521, 463, 405, 347, 289, - 650, 578, 506, 434, 361, - 794, 706, 618, 530, 441, - 953, 847, 741, 635, 529, - 1125, 1000, 875, 750, 625, - 1313, 1167, 1021, 875, 729 - }; - - private static final int[] GM_N1 = { - 18, 50, 98, 81, 121, 113, 113, 116, 121, 126, 118, 125, 122 - }; - private static final int[] GM_B1 = { - 1, 1, 1, 2, 2, 2, 2, 3, 2, 7, 5, 10, 6 - }; - private static final int[] GM_B2 = { - 0, 0, 0, 0, 0, 1, 2, 2, 4, 0, 4, 0, 6 - }; - - private static final int[] GM_EBEB = { - /* E1 B3 E2 B4 */ - 0, 0, 0, 0, // version 1 - 3, 1, 0, 0, - 5, 1, 0, 0, - 7, 1, 0, 0, - 9, 1, 0, 0, - 5, 1, 0, 0, // version 2 - 10, 1, 0, 0, - 15, 1, 0, 0, - 20, 1, 0, 0, - 25, 1, 0, 0, - 9, 1, 0, 0, // version 3 - 19, 1, 0, 0, - 29, 1, 0, 0, - 39, 1, 0, 0, - 49, 1, 0, 0, - 8, 2, 0, 0, // version 4 - 16, 2, 0, 0, - 24, 2, 0, 0, - 32, 2, 0, 0, - 41, 1, 10, 1, - 12, 2, 0, 0, // version 5 - 24, 2, 0, 0, - 36, 2, 0, 0, - 48, 2, 0, 0, - 61, 1, 60, 1, - 11, 3, 0, 0, // version 6 - 23, 1, 22, 2, - 34, 2, 33, 1, - 45, 3, 0, 0, - 57, 1, 56, 2, - 12, 1, 11, 3, // version 7 - 23, 2, 22, 2, - 34, 3, 33, 1, - 45, 4, 0, 0, - 57, 1, 56, 3, - 12, 2, 11, 3, // version 8 - 23, 5, 0, 0, - 35, 3, 34, 2, - 47, 1, 46, 4, - 58, 4, 57, 1, - 12, 6, 0, 0, // version 9 - 24, 6, 0, 0, - 36, 6, 0, 0, - 48, 6, 0, 0, - 61, 1, 60, 5, - 13, 4, 12, 3, // version 10 - 26, 1, 25, 6, - 38, 5, 37, 2, - 51, 2, 50, 5, - 63, 7, 0, 0, - 12, 6, 11, 3, // version 11 - 24, 4, 23, 5, - 36, 2, 35, 7, - 47, 9, 0, 0, - 59, 7, 58, 2, - 13, 5, 12, 5, // version 12 - 25, 10, 0, 0, - 38, 5, 37, 5, - 50, 10, 0, 0, - 63, 5, 62, 5, - 13, 1, 12, 11, //version 13 - 25, 3, 24, 9, - 37, 5, 36, 7, - 49, 7, 48, 5, - 61, 9, 60, 3 - }; - - private static final int[] GM_MACRO_MATRIX = { - 728, 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, - 727, 624, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 651, - 726, 623, 528, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 553, 652, - 725, 622, 527, 440, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 463, 554, 653, - 724, 621, 526, 439, 360, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 381, 464, 555, 654, - 723, 620, 525, 438, 359, 288, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 307, 382, 465, 556, 655, - 722, 619, 524, 437, 358, 287, 224, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 241, 308, 383, 466, 557, 656, - 721, 618, 523, 436, 357, 286, 223, 168, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 183, 242, 309, 384, 467, 558, 657, - 720, 617, 522, 435, 356, 285, 222, 167, 120, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 133, 184, 243, 310, 385, 468, 559, 658, - 719, 616, 521, 434, 355, 284, 221, 166, 119, 80, 49, 50, 51, 52, 53, 54, 55, 56, 91, 134, 185, 244, 311, 386, 469, 560, 659, - 718, 615, 520, 433, 354, 283, 220, 165, 118, 79, 48, 25, 26, 27, 28, 29, 30, 57, 92, 135, 186, 245, 312, 387, 470, 561, 660, - 717, 614, 519, 432, 353, 282, 219, 164, 117, 78, 47, 24, 9, 10, 11, 12, 31, 58, 93, 136, 187, 246, 313, 388, 471, 562, 661, - 716, 613, 518, 431, 352, 281, 218, 163, 116, 77, 46, 23, 8, 1, 2, 13, 32, 59, 94, 137, 188, 247, 314, 389, 472, 563, 662, - 715, 612, 517, 430, 351, 280, 217, 162, 115, 76, 45, 22, 7, 0, 3, 14, 33, 60, 95, 138, 189, 248, 315, 390, 473, 564, 663, - 714, 611, 516, 429, 350, 279, 216, 161, 114, 75, 44, 21, 6, 5, 4, 15, 34, 61, 96, 139, 190, 249, 316, 391, 474, 565, 664, - 713, 610, 515, 428, 349, 278, 215, 160, 113, 74, 43, 20, 19, 18, 17, 16, 35, 62, 97, 140, 191, 250, 317, 392, 475, 566, 665, - 712, 609, 514, 427, 348, 277, 214, 159, 112, 73, 42, 41, 40, 39, 38, 37, 36, 63, 98, 141, 192, 251, 318, 393, 476, 567, 666, - 711, 608, 513, 426, 347, 276, 213, 158, 111, 72, 71, 70, 69, 68, 67, 66, 65, 64, 99, 142, 193, 252, 319, 394, 477, 568, 667, - 710, 607, 512, 425, 346, 275, 212, 157, 110, 109, 108, 107, 106, 105, 104, 103, 102, 101, 100, 143, 194, 253, 320, 395, 478, 569, 668, - 709, 606, 511, 424, 345, 274, 211, 156, 155, 154, 153, 152, 151, 150, 149, 148, 147, 146, 145, 144, 195, 254, 321, 396, 479, 570, 669, - 708, 605, 510, 423, 344, 273, 210, 209, 208, 207, 206, 205, 204, 203, 202, 201, 200, 199, 198, 197, 196, 255, 322, 397, 480, 571, 670, - 707, 604, 509, 422, 343, 272, 271, 270, 269, 268, 267, 266, 265, 264, 263, 262, 261, 260, 259, 258, 257, 256, 323, 398, 481, 572, 671, - 706, 603, 508, 421, 342, 341, 340, 339, 338, 337, 336, 335, 334, 333, 332, 331, 330, 329, 328, 327, 326, 325, 324, 399, 482, 573, 672, - 705, 602, 507, 420, 419, 418, 417, 416, 415, 414, 413, 412, 411, 410, 409, 408, 407, 406, 405, 404, 403, 402, 401, 400, 483, 574, 673, - 704, 601, 506, 505, 504, 503, 502, 501, 500, 499, 498, 497, 496, 495, 494, 493, 492, 491, 490, 489, 488, 487, 486, 485, 484, 575, 674, - 703, 600, 599, 598, 597, 596, 595, 594, 593, 592, 591, 590, 589, 588, 587, 586, 585, 584, 583, 582, 581, 580, 579, 578, 577, 576, 675, - 702, 701, 700, 699, 698, 697, 696, 695, 694, 693, 692, 691, 690, 689, 688, 687, 686, 685, 684, 683, 682, 681, 680, 679, 678, 677, 676 - }; - - private static final char[] MIXED_ALPHANUM_SET = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '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', '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', ' ' - }; - - private enum Mode { - NULL, GM_NUMBER, GM_LOWER, GM_UPPER, GM_MIXED, GM_CONTROL, GM_BYTE, GM_CHINESE - } - - private StringBuilder binary; - private int[] word = new int[1460]; - private boolean[] grid; - private Mode appxDnextSection = Mode.NULL; - private Mode appxDlastSection = Mode.NULL; - private int preferredVersion = 0; - private int preferredEccLevel = -1; - - /** - * Set preferred size, or "version" of the symbol according to the following - * table. This value may be ignored if the data to be encoded does not fit - * into a symbol of the selected size. - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
InputSize
1 18 x 18
2 30 x 30
3 42 x 42
4 54 x 54
5 66 x 66
6 78 x 78
7 90 x 90
8 102 x 102
9 114 x 114
10 126 x 126
11 138 x 138
12 150 x 150
13 162 x 162
- * - * @param version symbol version - */ - public void setPreferredVersion(int version) { - preferredVersion = version; - } - - /** - * Set the preferred amount of the symbol which should be dedicated to error - * correction data. Values should be selected from the following table: - * - * - * - * - * - * - * - * - * - * - *
ModeError Correction Capacity
1 Approximately 10%
2 Approximately 20%
3 Approximately 30%
4 Approximately 40%
5 Approximately 50%
- * - * @param eccLevel error correction level - */ - public void setPreferredEccLevel(int eccLevel) { - preferredEccLevel = eccLevel; - } - - @Override - protected boolean eciSupported() { - return true; - } - - @Override - protected void encode() { - int size, modules, dark, error_number; - int auto_layers, min_layers, layers, auto_ecc_level, min_ecc_level, ecc_level; - int x, y, i; - int data_cw, input_latch = 0; - int data_max; - int length; - StringBuilder bin = new StringBuilder(); - - for (i = 0; i < 1460; i++) { - word[i] = 0; - } - - try { - Charset gb2312 = Charset.forName("GB2312"); - if (gb2312.newEncoder().canEncode(content)) { - /* GB2312 will work, use Chinese compaction */ - byte[] inputBytes = content.getBytes(gb2312); - inputData = new int[inputBytes.length]; - length = 0; - for (i = 0; i < inputBytes.length; i++) { - if (((inputBytes[i] & 0xFF) >= 0xA1) && ((inputBytes[i] & 0xFF) <= 0xF7)) { - /* Double byte character */ - inputData[length] = ((inputBytes[i] & 0xFF) * 256) + (inputBytes[i + 1] & 0xFF); - i++; - length++; - } else { - /* Single byte character */ - inputData[length] = inputBytes[i] & 0xFF; - length++; - } - } - infoLine("Using GB2312 character encoding"); - eciMode = 29; - } else { - /* GB2312 encoding won't work, use other ECI mode */ - eciProcess(); // Get ECI mode - length = inputData.length; - } - } catch (UnsupportedCharsetException e) { - throw new OkapiInternalException("Byte conversion encoding error"); - } - - error_number = encodeGridMatrixBinary(length, readerInit); - if (error_number != 0) { - throw OkapiInputException.inputTooLong(); - } - - /* Determine the size of the symbol */ - data_cw = binary.length() / 7; - - auto_layers = 1; - for (i = 0; i < 13; i++) { - if (GM_RECOMMEND_CW[i] < data_cw) { - auto_layers = i + 1; - } - } - - min_layers = 13; - for (i = 12; i > 0; i--) { - if (GM_MAX_CW[(i - 1)] >= data_cw) { - min_layers = i; - } - } - layers = auto_layers; - auto_ecc_level = 3; - if (layers == 1) { - auto_ecc_level = 5; - } - if ((layers == 2) || (layers == 3)) { - auto_ecc_level = 4; - } - min_ecc_level = 1; - if (layers == 1) { - min_ecc_level = 4; - } - if ((layers == 2) || (layers == 3)) { - min_ecc_level = 2; - } - ecc_level = auto_ecc_level; - - if ((preferredVersion >= 1) && (preferredVersion <= 13)) { - input_latch = 1; - if (preferredVersion > min_layers) { - layers = preferredVersion; - } else { - layers = min_layers; - } - } - - if (input_latch == 1) { - auto_ecc_level = 3; - if (layers == 1) { - auto_ecc_level = 5; - } - if ((layers == 2) || (layers == 3)) { - auto_ecc_level = 4; - } - ecc_level = auto_ecc_level; - if (data_cw > GM_DATA_CODEWORDS[(5 * (layers - 1)) + (ecc_level - 1)]) { - layers++; - } - } - - if (input_latch == 0) { - if ((preferredEccLevel >= 1) && (preferredEccLevel <= 5)) { - if (preferredEccLevel > min_ecc_level) { - ecc_level = preferredEccLevel; - } else { - ecc_level = min_ecc_level; - } - } - if (data_cw > GM_DATA_CODEWORDS[(5 * (layers - 1)) + (ecc_level - 1)]) { - do { - layers++; - } while ((data_cw > GM_DATA_CODEWORDS[(5 * (layers - 1)) + (ecc_level - 1)]) && (layers <= 13)); - } - } - - data_max = 1313; - switch (ecc_level) { - case 2: - data_max = 1167; - break; - case 3: - data_max = 1021; - break; - case 4: - data_max = 875; - break; - case 5: - data_max = 729; - break; - } - - if (data_cw > data_max) { - throw OkapiInputException.inputTooLong(); - } - - addErrorCorrection(data_cw, layers, ecc_level); - size = 6 + (layers * 12); - modules = 1 + (layers * 2); - - infoLine("Layers: " + layers); - infoLine("ECC Level: " + ecc_level); - infoLine("Data Codewords: " + data_cw); - infoLine("ECC Codewords: " + GM_DATA_CODEWORDS[((layers - 1) * 5) + (ecc_level - 1)]); - infoLine("Grid Size: " + modules + " X " + modules); - - grid = new boolean[size * size]; - - for (x = 0; x < size; x++) { - for (y = 0; y < size; y++) { - grid[(y * size) + x] = false; - } - } - - placeDataInGrid(modules, size); - addLayerId(size, layers, modules, ecc_level); - - /* Add macromodule frames */ - for (x = 0; x < modules; x++) { - dark = 1 - (x & 1); - for (y = 0; y < modules; y++) { - if (dark == 1) { - for (i = 0; i < 5; i++) { - grid[((y * 6) * size) + (x * 6) + i] = true; - grid[(((y * 6) + 5) * size) + (x * 6) + i] = true; - grid[(((y * 6) + i) * size) + (x * 6)] = true; - grid[(((y * 6) + i) * size) + (x * 6) + 5] = true; - } - grid[(((y * 6) + 5) * size) + (x * 6) + 5] = true; - dark = 0; - } else { - dark = 1; - } - } - } - - /* Copy values to symbol */ - symbol_width = size; - row_count = size; - row_height = new int[row_count]; - pattern = new String[row_count]; - - for (x = 0; x < size; x++) { - bin.setLength(0); - for (y = 0; y < size; y++) { - if (grid[(x * size) + y]) { - bin.append('1'); - } else { - bin.append('0'); - } - } - row_height[x] = 1; - pattern[x] = bin2pat(bin); - } - } - - private int encodeGridMatrixBinary(int length, boolean reader) { - /* Create a binary stream representation of the input data. - 7 sets are defined - Chinese characters, Numerals, Lower case letters, Upper case letters, - Mixed numerals and letters, Control characters and 8-bit binary data */ - int sp, glyph = 0; - Mode current_mode, next_mode, last_mode; - int c1, c2; - boolean done; - int p = 0, ppos; - int punt = 0; - int number_pad_posn; - int byte_count_posn = 0, byte_count = 0; - int shift, i; - int[] numbuf = new int[3]; - Mode[] modeMap = calculateModeMap(length); - - binary = new StringBuilder(); - - sp = 0; - current_mode = Mode.NULL; - number_pad_posn = 0; - - info("Encoding: "); - - if (reader) { - binary.append("1010"); /* FNC3 - Reader Initialisation */ - info("INIT "); - } - - if ((eciMode != 3) && (eciMode != 29)) { - binary.append("1100"); /* ECI */ - - if ((eciMode >= 0) && (eciMode <= 1023)) { - binary.append('0'); - for (i = 0x200; i > 0; i = i >> 1) { - if ((eciMode & i) != 0) { - binary.append('1'); - } else { - binary.append('0'); - } - } - } - - if ((eciMode >= 1024) && (eciMode <= 32767)) { - binary.append("10"); - for (i = 0x4000; i > 0; i = i >> 1) { - if ((eciMode & i) != 0) { - binary.append('1'); - } else { - binary.append('0'); - } - } - } - - if ((eciMode >= 32768) && (eciMode <= 811799)) { - binary.append("11"); - for (i = 0x80000; i > 0; i = i >> 1) { - if ((eciMode & i) != 0) { - binary.append('1'); - } else { - binary.append('0'); - } - } - } - - info("ECI "); - infoSpace(eciMode); - } - - do { - next_mode = modeMap[sp]; - - if (next_mode != current_mode) { - switch (current_mode) { - case NULL: - switch (next_mode) { - case GM_CHINESE: - binary.append("0001"); - break; - case GM_NUMBER: - binary.append("0010"); - break; - case GM_LOWER: - binary.append("0011"); - break; - case GM_UPPER: - binary.append("0100"); - break; - case GM_MIXED: - binary.append("0101"); - break; - case GM_BYTE: - binary.append("0111"); - break; - } - break; - case GM_CHINESE: - switch (next_mode) { - case GM_NUMBER: - binary.append("1111111100001"); - break; // 8161 - case GM_LOWER: - binary.append("1111111100010"); - break; // 8162 - case GM_UPPER: - binary.append("1111111100011"); - break; // 8163 - case GM_MIXED: - binary.append("1111111100100"); - break; // 8164 - case GM_BYTE: - binary.append("1111111100101"); - break; // 8165 - } - break; - case GM_NUMBER: - /* add numeric block padding value */ - switch (p) { - case 1: - binary.insert(number_pad_posn, "10"); - break; // 2 pad digits - case 2: - binary.insert(number_pad_posn, "01"); - break; // 1 pad digit - case 3: - binary.insert(number_pad_posn, "00"); - break; // 0 pad digits - } - - switch (next_mode) { - case GM_CHINESE: - binary.append("1111111011"); - break; // 1019 - case GM_LOWER: - binary.append("1111111100"); - break; // 1020 - case GM_UPPER: - binary.append("1111111101"); - break; // 1021 - case GM_MIXED: - binary.append("1111111110"); - break; // 1022 - case GM_BYTE: - binary.append("1111111111"); - break; // 1023 - } - break; - case GM_LOWER: - case GM_UPPER: - switch (next_mode) { - case GM_CHINESE: - binary.append("11100"); - break; // 28 - case GM_NUMBER: - binary.append("11101"); - break; // 29 - case GM_LOWER: - case GM_UPPER: - binary.append("11110"); - break; // 30 - case GM_MIXED: - binary.append("1111100"); - break; // 124 - case GM_BYTE: - binary.append("1111110"); - break; // 126 - } - break; - case GM_MIXED: - switch (next_mode) { - case GM_CHINESE: - binary.append("1111110001"); - break; // 1009 - case GM_NUMBER: - binary.append("1111110010"); - break; // 1010 - case GM_LOWER: - binary.append("1111110011"); - break; // 1011 - case GM_UPPER: - binary.append("1111110100"); - break; // 1012 - case GM_BYTE: - binary.append("1111110111"); - break; // 1015 - } - break; - case GM_BYTE: - /* add byte block length indicator */ - addByteCount(byte_count_posn, byte_count); - byte_count = 0; - switch (next_mode) { - case GM_CHINESE: - binary.append("0001"); - break; // 1 - case GM_NUMBER: - binary.append("0010"); - break; // 2 - case GM_LOWER: - binary.append("0011"); - break; // 3 - case GM_UPPER: - binary.append("0100"); - break; // 4 - case GM_MIXED: - binary.append("0101"); - break; // 5 - } - break; - } - - switch (next_mode) { - case GM_CHINESE: - info("CHIN "); - break; - case GM_NUMBER: - info("NUMB "); - break; - case GM_LOWER: - info("LOWR "); - break; - case GM_UPPER: - info("UPPR "); - break; - case GM_MIXED: - info("MIXD "); - break; - case GM_BYTE: - info("BYTE "); - break; - } - - } - last_mode = current_mode; - current_mode = next_mode; - - switch (current_mode) { - case GM_CHINESE: - done = false; - if (inputData[sp] > 0xff) { - /* GB2312 character */ - c1 = (inputData[sp] & 0xff00) >> 8; - c2 = inputData[sp] & 0xff; - - if ((c1 >= 0xa0) && (c1 <= 0xa9)) { - glyph = (0x60 * (c1 - 0xa1)) + (c2 - 0xa0); - } - if ((c1 >= 0xb0) && (c1 <= 0xf7)) { - glyph = (0x60 * (c1 - 0xb0 + 9)) + (c2 - 0xa0); - } - done = true; - } - if (!(done)) { - if (sp != (length - 1)) { - if ((inputData[sp] == 13) && (inputData[sp + 1] == 10)) { - /* End of Line */ - glyph = 7776; - sp++; - } - done = true; - } - } - if (!(done)) { - if (sp != (length - 1)) { - if (((inputData[sp] >= '0') && (inputData[sp] <= '9')) && ((inputData[sp + 1] >= '0') && (inputData[sp + 1] <= '9'))) { - /* Two digits */ - glyph = 8033 + (10 * (inputData[sp] - '0')) + (inputData[sp + 1] - '0'); - sp++; - } - } - } - if (!(done)) { - /* Byte value */ - glyph = 7777 + inputData[sp]; - } - - infoSpace(glyph); - - for (i = 0x1000; i > 0; i = i >> 1) { - if ((glyph & i) != 0) { - binary.append('1'); - } else { - binary.append('0'); - } - } - sp++; - break; - - case GM_NUMBER: - if (last_mode != current_mode) { - /* Reserve a space for numeric digit padding value (2 bits) */ - number_pad_posn = binary.length(); - } - p = 0; - ppos = -1; - - /* Numeric compression can also include certain combinations of - non-numeric character */ - numbuf[0] = '0'; - numbuf[1] = '0'; - numbuf[2] = '0'; - do { - if ((inputData[sp] >= '0') && (inputData[sp] <= '9')) { - numbuf[p] = inputData[sp]; - p++; - } - switch (inputData[sp]) { - case ' ': - case '+': - case '-': - case '.': - case ',': - punt = inputData[sp]; - ppos = p; - break; - } - if (sp < (length - 1)) { - if ((inputData[sp] == 13) && (inputData[sp + 1] == 10)) { - /* */ - punt = inputData[sp]; - sp++; - ppos = p; - } - } - sp++; - } while ((p < 3) && (sp < length)); - - if (ppos != -1) { - switch (punt) { - case ' ': - glyph = 0; - break; - case '+': - glyph = 3; - break; - case '-': - glyph = 6; - break; - case '.': - glyph = 9; - break; - case ',': - glyph = 12; - break; - case 0x13: - glyph = 15; - break; - } - glyph += ppos; - glyph += 1000; - - infoSpace(glyph); - - for (i = 0x200; i > 0; i = i >> 1) { - if ((glyph & i) != 0) { - binary.append('1'); - } else { - binary.append('0'); - } - } - } - - glyph = (100 * (numbuf[0] - '0')) + (10 * (numbuf[1] - '0')) + (numbuf[2] - '0'); - infoSpace(glyph); - - for (i = 0x200; i > 0; i = i >> 1) { - if ((glyph & i) != 0) { - binary.append('1'); - } else { - binary.append('0'); - } - } - break; - - case GM_BYTE: - if (last_mode != current_mode) { - /* Reserve space for byte block length indicator (9 bits) */ - byte_count_posn = binary.length(); - } - if (byte_count == 512) { - /* Maximum byte block size is 512 bytes. If longer is needed then start a new block */ - addByteCount(byte_count_posn, byte_count); - binary.append("0111"); - byte_count_posn = binary.length(); - byte_count = 0; - } - - glyph = inputData[sp]; - infoSpace(glyph); - for (i = 0x80; i > 0; i = i >> 1) { - if ((glyph & i) != 0) { - binary.append('1'); - } else { - binary.append('0'); - } - } - sp++; - byte_count++; - break; - - case GM_MIXED: - shift = 1; - if ((inputData[sp] >= '0') && (inputData[sp] <= '9')) { - shift = 0; - } - if ((inputData[sp] >= 'A') && (inputData[sp] <= 'Z')) { - shift = 0; - } - if ((inputData[sp] >= 'a') && (inputData[sp] <= 'z')) { - shift = 0; - } - if (inputData[sp] == ' ') { - shift = 0; - } - - if (shift == 0) { - /* Mixed Mode character */ - glyph = positionOf((char) inputData[sp], MIXED_ALPHANUM_SET); - infoSpace(glyph); - - for (i = 0x20; i > 0; i = i >> 1) { - if ((glyph & i) != 0) { - binary.append('1'); - } else { - binary.append('0'); - } - } - } else { - /* Shift Mode character */ - binary.append("1111110110"); /* 1014 - shift indicator */ - - addShiftCharacter(inputData[sp]); - } - - sp++; - break; - - case GM_UPPER: - shift = 1; - if ((inputData[sp] >= 'A') && (inputData[sp] <= 'Z')) { - shift = 0; - } - if (inputData[sp] == ' ') { - shift = 0; - } - - if (shift == 0) { - /* Upper Case character */ - glyph = positionOf((char) inputData[sp], MIXED_ALPHANUM_SET) - 10; - if (glyph == 52) { - // Space character - glyph = 26; - } - infoSpace(glyph); - - for (i = 0x10; i > 0; i = i >> 1) { - if ((glyph & i) != 0) { - binary.append('1'); - } else { - binary.append('0'); - } - } - - } else { - /* Shift Mode character */ - binary.append("1111101"); /* 127 - shift indicator */ - - addShiftCharacter(inputData[sp]); - } - - sp++; - break; - - case GM_LOWER: - shift = 1; - if ((inputData[sp] >= 'a') && (inputData[sp] <= 'z')) { - shift = 0; - } - if (inputData[sp] == ' ') { - shift = 0; - } - - if (shift == 0) { - /* Lower Case character */ - glyph = positionOf((char) inputData[sp], MIXED_ALPHANUM_SET) - 36; - infoSpace(glyph); - - for (i = 0x10; i > 0; i = i >> 1) { - if ((glyph & i) != 0) { - binary.append('1'); - } else { - binary.append('0'); - } - } - - } else { - /* Shift Mode character */ - binary.append("1111101"); /* 127 - shift indicator */ - - addShiftCharacter(inputData[sp]); - } - - sp++; - break; - } - if (binary.length() > 9191) { - return 1; - } - - } while (sp < length); - - infoLine(); - - if (current_mode == Mode.GM_NUMBER) { - /* add numeric block padding value */ - switch (p) { - case 1: - binary.insert(number_pad_posn, "10"); - break; // 2 pad digits - case 2: - binary.insert(number_pad_posn, "01"); - break; // 1 pad digit - case 3: - binary.insert(number_pad_posn, "00"); - break; // 0 pad digits - } - } - - if (current_mode == Mode.GM_BYTE) { - /* Add byte block length indicator */ - addByteCount(byte_count_posn, byte_count); - } - - /* Add "end of data" character */ - switch (current_mode) { - case GM_CHINESE: - binary.append("1111111100000"); - break; // 8160 - case GM_NUMBER: - binary.append("1111111010"); - break; // 1018 - case GM_LOWER: - case GM_UPPER: - binary.append("11011"); - break; // 27 - case GM_MIXED: - binary.append("1111110000"); - break; // 1008 - case GM_BYTE: - binary.append("0000"); - break; // 0 - } - - /* Add padding bits if required */ - p = 7 - (binary.length() % 7); - if (p == 7) { - p = 0; - } - for (i = 0; i < p; i++) { - binary.append('0'); - } - - if (binary.length() > 9191) { - return 1; - } - - return 0; - } - - private Mode[] calculateModeMap(int length) { - Mode[] modeMap = new Mode[length]; - int i; - int digitStart, digitLength; - boolean digits; - int spaceStart, spaceLength; - boolean spaces; - int[] segmentLength; - Mode[] segmentType; - int[] segmentStart; - int segmentCount; - - // Step 1 - // Characters in GB2312 are encoded as Chinese characters - for (i = 0; i < length; i++) { - modeMap[i] = Mode.NULL; - if (inputData[i] > 0xFF) { - modeMap[i] = Mode.GM_CHINESE; - } - } - - // Consecutive characters, if preceeded by or followed - // by chinese characters, are encoded as chinese characters. - if (length > 3) { - i = 1; - do { - if ((inputData[i] == 13) && (inputData[i + 1] == 10)) { - // End of line (CR/LF) - - if (modeMap[i - 1] == Mode.GM_CHINESE) { - modeMap[i] = Mode.GM_CHINESE; - modeMap[i + 1] = Mode.GM_CHINESE; - } - i += 2; - } else { - i++; - } - } while (i < length - 1); - - i = length - 3; - do { - if ((inputData[i] == 13) && (inputData[i + 1] == 10)) { - // End of line (CR/LF) - if (modeMap[i + 2] == Mode.GM_CHINESE) { - modeMap[i] = Mode.GM_CHINESE; - modeMap[i + 1] = Mode.GM_CHINESE; - } - i -= 2; - } else { - i--; - } - } while (i > 0); - } - - // Digit pairs between chinese characters encode as chinese characters. - digits = false; - digitLength = 0; - digitStart = 0; - for (i = 1; i < length - 1; i++) { - if ((inputData[i] >= 48) && (inputData[i] <= 57)) { - // '0' to '9' - if (digits == false) { - digits = true; - digitLength = 1; - digitStart = i; - } else { - digitLength++; - } - } else { - if (digits == true) { - if ((digitLength % 2) == 0) { - if ((modeMap[digitStart - 1] == Mode.GM_CHINESE) && - (modeMap[i] == Mode.GM_CHINESE)) { - for(int j = 0; j < digitLength; j++) { - modeMap[i - j - 1] = Mode.GM_CHINESE; - } - } - } - digits = false; - } - } - } - - // Step 2: all characters 'a' to 'z' are lowercase. - for (i = 0; i < length; i++) { - if ((inputData[i] >= 97) && (inputData[i] <= 122)) { - modeMap[i] = Mode.GM_LOWER; - } - } - - // Step 3: all characters 'A' to 'Z' are uppercase. - for (i = 0; i < length; i++) { - if ((inputData[i] >= 65) && (inputData[i] <= 90)) { - modeMap[i] = Mode.GM_UPPER; - } - } - - // Step 4: find consecutive characters preceeded or followed - // by uppercase or lowercase. - spaces = false; - spaceLength = 0; - spaceStart = 0; - for (i = 1; i < length - 1; i++) { - if (inputData[i] == 32) { - if (spaces == false) { - spaces = true; - spaceLength = 1; - spaceStart = i; - } else { - spaceLength++; - } - } else { - if (spaces == true) { - - Mode modeX = modeMap[spaceStart - 1]; - Mode modeY = modeMap[i]; - - if ((modeX == Mode.GM_LOWER) || (modeX == Mode.GM_UPPER)) { - for (int j = 0; j < spaceLength; j++) { - modeMap[i - j - 1] = modeX; - } - } else { - if ((modeY == Mode.GM_LOWER) || (modeY == Mode.GM_UPPER)) { - for (int j = 0; j < spaceLength; j++) { - modeMap[i - j - 1] = modeY; - } - } - } - spaces = false; - } - } - } - - // Step 5: Unassigned characters '0' to '9' are assigned as numerals. - // Non-numeric characters in table 7 are also assigned as numerals. - for(i = 0; i < length; i++) { - if(modeMap[i] == Mode.NULL) { - if ((inputData[i] >= 48) && (inputData[i] <= 57)) { - // '0' to '9' - modeMap[i] = Mode.GM_NUMBER; - } else { - switch (inputData[i]) { - case 32: // Space - case 43: // '+' - case 45: // '-' - case 46: // "." - case 44: // "," - modeMap[i] = Mode.GM_NUMBER; - break; - case 13: // CR - if (i < length - 1) { - if (inputData[i + 1] == 10) { // LF - // - modeMap[i] = Mode.GM_NUMBER; - modeMap[i + 1] = Mode.GM_NUMBER; - } - } - } - } - } - } - - // Step 6: The remining unassigned bytes are assigned as 8-bit binary - for(i = 0; i < length; i++) { - if (modeMap[i] == Mode.NULL) { - modeMap[i] = Mode.GM_BYTE; - } - } - - // break into segments - segmentLength = new int[length]; - segmentType = new Mode[length]; - segmentStart = new int[length]; - - segmentCount = 0; - segmentLength[0] = 1; - segmentType[0] = modeMap[0]; - segmentStart[0] = 0; - for (i = 1; i < length; i++) { - if (modeMap[i] == modeMap[i - 1]) { - segmentLength[segmentCount]++; - } else { - segmentCount++; - segmentLength[segmentCount] = 1; - segmentType[segmentCount] = modeMap[i]; - segmentStart[segmentCount] = i; - } - } - - // A segment can be a control segment if - // a) It is not the start segment of the data stream - // b) All characters are control characters - // c) The length of the segment is no more than 3 - // d) The previous segment is not chinese - if (segmentCount > 1) { - for (i = 1; i < segmentCount; i++) { // (a) - if ((segmentLength[i] <= 3) && (segmentType[i - 1] != Mode.GM_CHINESE)) { // (c) and (d) - boolean controlLatch = true; - for (int j = 0; j < segmentLength[i]; j++) { - boolean thischarLatch = false; - for(int k = 0; k < 63; k++) { - if (inputData[segmentStart[i] + j] == SHIFT_SET[k]) { - thischarLatch = true; - } - } - - if (!(thischarLatch)) { - // This character is not a control character - controlLatch = false; - } - } - - if (controlLatch) { // (b) - segmentType[i] = Mode.GM_CONTROL; - } - } - } - } - - // Stages 7 to 9 - if (segmentCount >= 3) { - for (i = 0; i < segmentCount - 1; i++) { - Mode pm, tm, nm, lm; - int tl, nl, ll, position; - boolean lastSegment = false; - - if (i == 0) { - pm = Mode.NULL; - } else { - pm = segmentType[i - 1]; - } - - tm = segmentType[i]; - tl = segmentLength[i]; - - nm = segmentType[i + 1]; - nl = segmentLength[i + 1]; - - lm = segmentType[i + 2]; - ll = segmentLength[i + 2]; - - position = segmentStart[i]; - - if (i + 2 == segmentCount) { - lastSegment = true; - } - - segmentType[i] = getBestMode(pm, tm, nm, lm, tl, nl, ll, position, lastSegment); - - if (segmentType[i] == Mode.GM_CONTROL) { - segmentType[i] = segmentType[i - 1]; - } - } - - segmentType[i] = appxDnextSection; - segmentType[i + 1] = appxDlastSection; - - if (segmentType[i] == Mode.GM_CONTROL) { - segmentType[i] = segmentType[i - 1]; - } - if (segmentType[i + 1] == Mode.GM_CONTROL) { - segmentType[i + 1] = segmentType[i]; - } - -// Uncomment these lines to override mode selection and generate symbol as shown -// in image D.1 for the test data "AAT2556 电池充电器+降压转换器 200mA至2A tel:86 019 82512738" -// segmentType[9] = gmMode.GM_LOWER; -// segmentType[10] = gmMode.GM_LOWER; - } - - // Copy segments back to modeMap - for (i = 0; i < segmentCount; i++) { - for (int j = 0; j < segmentLength[i]; j++) { - modeMap[segmentStart[i] + j] = segmentType[i]; - } - } - - return modeMap; - } - - private boolean isTransitionValid(Mode previousMode, Mode thisMode) { - // Filters possible encoding data types from table D.1 - boolean isValid = false; - - switch (previousMode) { - case GM_CHINESE: - switch (thisMode) { - case GM_CHINESE: - case GM_BYTE: - isValid = true; - break; - } - break; - case GM_NUMBER: - switch (thisMode) { - case GM_NUMBER: - case GM_MIXED: - case GM_BYTE: - case GM_CHINESE: - isValid = true; - break; - } - break; - case GM_LOWER: - switch (thisMode) { - case GM_LOWER: - case GM_MIXED: - case GM_BYTE: - case GM_CHINESE: - isValid = true; - break; - } - break; - case GM_UPPER: - switch (thisMode) { - case GM_UPPER: - case GM_MIXED: - case GM_BYTE: - case GM_CHINESE: - isValid = true; - break; - } - break; - case GM_CONTROL: - switch (thisMode) { - case GM_CONTROL: - case GM_BYTE: - case GM_CHINESE: - isValid = true; - break; - } - break; - case GM_BYTE: - switch (thisMode) { - case GM_BYTE: - case GM_CHINESE: - isValid = true; - break; - } - break; - } - - return isValid; - } - - private Mode intToMode(int input) { - Mode retVal; - - switch (input) { - case 1: - retVal = Mode.GM_CHINESE; - break; - case 2: - retVal = Mode.GM_BYTE; - break; - case 3: - retVal = Mode.GM_CONTROL; - break; - case 4: - retVal = Mode.GM_MIXED; - break; - case 5: - retVal = Mode.GM_UPPER; - break; - case 6: - retVal = Mode.GM_LOWER; - break; - case 7: - retVal = Mode.GM_NUMBER; - break; - default: - retVal = Mode.NULL; - break; - } - - return retVal; - } - - private Mode getBestMode(Mode pm, Mode tm, Mode nm, Mode lm, int tl, int nl, int ll, int position, boolean lastSegment) { - int tmi, nmi, lmi; - Mode bestMode = tm; - int binaryLength; - int bestBinaryLength = Integer.MAX_VALUE; - - for(tmi = 1; tmi < 8; tmi++) { - if (isTransitionValid(tm, intToMode(tmi))) { - for(nmi = 1; nmi < 8; nmi++) { - if (isTransitionValid(nm, intToMode(nmi))) { - for (lmi = 1; lmi < 8; lmi++) { - if (isTransitionValid(lm, intToMode(lmi))) { - binaryLength = getBinaryLength(pm, intToMode(tmi), intToMode(nmi), intToMode(lmi), tl, nl, ll, position, lastSegment); - if (binaryLength <= bestBinaryLength) { - bestMode = intToMode(tmi); - appxDnextSection = intToMode(nmi); - appxDlastSection = intToMode(lmi); - bestBinaryLength = binaryLength; - } - } - } - } - } - } - } - - return bestMode; - } - - private int getBinaryLength(Mode pm, Mode tm, Mode nm, Mode lm, int tl, int nl, int ll, int position, boolean lastSegment) { - - int binaryLength = getChunkLength(pm, tm, tl, position); - binaryLength += getChunkLength(tm, nm, nl, (position + tl)); - binaryLength += getChunkLength(nm, lm, ll, (position + tl + nl)); - - if (lastSegment) { - switch (lm) { - case GM_CHINESE: - binaryLength += 13; - break; - case GM_NUMBER: - binaryLength += 10; - break; - case GM_LOWER: - case GM_UPPER: - binaryLength += 5; - break; - case GM_MIXED: - binaryLength += 10; - break; - case GM_BYTE: - binaryLength += 4; - break; - } - } - - return binaryLength; - } - - private int getChunkLength(Mode lastMode, Mode thisMode, int thisLength, int position) { - int byteLength; - - switch (thisMode) { - case GM_CHINESE: - byteLength = calcChineseLength(position, thisLength); - break; - case GM_NUMBER: - byteLength = calcNumberLength(position, thisLength); - break; - case GM_LOWER: - byteLength = 5 * thisLength; - break; - case GM_UPPER: - byteLength = 5 * thisLength; - break; - case GM_MIXED: - byteLength = calcMixedLength(position, thisLength); - break; - case GM_CONTROL: - byteLength = 6 * thisLength; - break; - default: - //case GM_BYTE: - byteLength = calcByteLength(position, thisLength); - break; - } - - switch (lastMode) { - case NULL: - byteLength += 4; - break; - case GM_CHINESE: - if ((thisMode != Mode.GM_CHINESE) && (thisMode != Mode.GM_CONTROL)) { - byteLength += 13; - } - break; - case GM_NUMBER: - if ((thisMode != Mode.GM_CHINESE) && (thisMode != Mode.GM_CONTROL)) { - byteLength += 10; - } - break; - case GM_LOWER: - switch (thisMode) { - case GM_CHINESE: - case GM_NUMBER: - case GM_UPPER: - byteLength += 5; - break; - case GM_MIXED: - case GM_CONTROL: - case GM_BYTE: - byteLength += 7; - break; - } - break; - case GM_UPPER: - switch (thisMode) { - case GM_CHINESE: - case GM_NUMBER: - case GM_LOWER: - byteLength += 5; - break; - case GM_MIXED: - case GM_CONTROL: - case GM_BYTE: - byteLength += 7; - break; - } - break; - case GM_MIXED: - if (thisMode != Mode.GM_MIXED) { - byteLength += 10; - } - break; - case GM_BYTE: - if (thisMode != Mode.GM_BYTE) { - byteLength += 4; - } - break; - } - - if ((lastMode != Mode.GM_BYTE) && (thisMode == Mode.GM_BYTE)) { - byteLength += 9; - } - - if ((lastMode != Mode.GM_NUMBER) && (thisMode == Mode.GM_NUMBER)) { - byteLength += 2; - } - - return byteLength; - } - - private int calcChineseLength(int position, int length) { - int i = 0; - int bits = 0; - - do { - bits += 13; - - if (i < length) { - if ((inputData[position + i] == 13) && (inputData[position + i + 1] == 10)) { - // - i++; - } - - if (((inputData[position + i] >= 48) && (inputData[position + i] <= 57)) && - ((inputData[position + i + 1] >= 48) && (inputData[position + i + 1] <= 57))) { - // two digits - i++; - } - } - i++; - } while (i < length); - - return bits; - } - - private int calcMixedLength(int position, int length) { - int bits = 0; - int i; - - for (i = 0; i < length; i++) { - bits += 6; - for(int k = 0; k < 63; k++) { - if (inputData[position + i] == SHIFT_SET[k]) { - bits += 10; - } - } - } - - return bits; - } - - private int calcNumberLength(int position, int length) { - int i; - int bits = 0; - int numbers = 0; - int nonnumbers = 0; - - for (i = 0; i < length; i++) { - if ((inputData[position + i] >= 48) && (inputData[position + i] <= 57)) { - numbers++; - } else { - nonnumbers++; - } - - if (i != 0) { - if ((inputData[position + i] == 10) && (inputData[position + i - 1] == 13)) { - // - nonnumbers--; - } - } - - if (numbers == 3) { - if (nonnumbers == 1) { - bits += 20; - } else { - bits += 10; - } - if (nonnumbers > 1) { - // Invalid encoding - bits += 100; - } - numbers = 0; - nonnumbers = 0; - } - } - - if (numbers > 0) { - if (nonnumbers == 1) { - bits += 20; - } else { - bits += 10; - } - } - - if (nonnumbers > 1) { - // Invalid - bits += 100; - } - - if (!((inputData[position + i - 1] >= 48) && (inputData[position + i - 1] <= 57))) { - // Data must end with a digit - bits += 100; - } - - - return bits; - } - - private int calcByteLength(int position, int length) { - int i; - int bits = 0; - - for (i = 0; i < length; i++) { - if (inputData[position + i] <= 0xFF) { - bits += 8; - } else { - bits += 16; - } - } - - return bits; - } - - private void addByteCount(int byte_count_posn, int byte_count) { - /* Add the length indicator for byte encoded blocks */ - for (int i = 0; i < 9; i++) { - if ((byte_count & (0x100 >> i)) != 0) { - binary.insert(byte_count_posn + i, '0'); - } else { - binary.insert(byte_count_posn + i, '1'); - } - } - } - - void addShiftCharacter(int shifty) { - /* Add a control character to the data stream */ - int i; - int glyph = 0; - - for (i = 0; i < 64; i++) { - if (SHIFT_SET[i] == shifty) { - glyph = i; - } - } - - info("SHT/"); - infoSpace(glyph); - - for (i = 0x20; i > 0; i = i >> 1) { - if ((glyph & i) != 0) { - binary.append('1'); - } else { - binary.append('0'); - } - } - } - - private void addErrorCorrection(int data_posn, int layers, int ecc_level) { - int data_cw, i, j, wp; - int n1, b1, n2, b2, e1, b3, e2; - int block_size, data_size, ecc_size; - int[] data = new int[1320]; - int[] block = new int[130]; - int[] data_block = new int[115]; - int[] ecc_block = new int[70]; - ReedSolomon rs = new ReedSolomon(); - - data_cw = GM_DATA_CODEWORDS[((layers - 1) * 5) + (ecc_level - 1)]; - - for (i = 0; i < 1320; i++) { - data[i] = 0; - } - - /* Convert from binary sream to 7-bit codewords */ - for (i = 0; i < data_posn; i++) { - for (j = 0; j < 7; j++) { - if (binary.charAt((i * 7) + j) == '1') { - data[i] += 0x40 >> j; - } - } - } - - info("Codewords: "); - for (i = 0; i < data_posn; i++) { - infoSpace(data[i]); - } - infoLine(); - - /* Add padding codewords */ - data[data_posn] = 0x00; - for (i = (data_posn + 1); i < data_cw; i++) { - if ((i & 1) != 0) { - data[i] = 0x7e; - } else { - data[i] = 0x00; - } - } - - /* Get block sizes */ - n1 = GM_N1[(layers - 1)]; - b1 = GM_B1[(layers - 1)]; - n2 = n1 - 1; - b2 = GM_B2[(layers - 1)]; - e1 = GM_EBEB[((layers - 1) * 20) + ((ecc_level - 1) * 4)]; - b3 = GM_EBEB[((layers - 1) * 20) + ((ecc_level - 1) * 4) + 1]; - e2 = GM_EBEB[((layers - 1) * 20) + ((ecc_level - 1) * 4) + 2]; - - /* Split the data into blocks */ - wp = 0; - for (i = 0; i < (b1 + b2); i++) { - if (i < b1) { - block_size = n1; - } else { - block_size = n2; - } - if (i < b3) { - ecc_size = e1; - } else { - ecc_size = e2; - } - data_size = block_size - ecc_size; - - /* printf("block %d/%d: data %d / ecc %d\n", i + 1, (b1 + b2), data_size, ecc_size);*/ - for (j = 0; j < data_size; j++) { - data_block[j] = data[wp]; - wp++; - } - - /* Calculate ECC data for this block */ - rs.init_gf(0x89); - rs.init_code(ecc_size, 1); - rs.encode(data_size, data_block); - for (j = 0; j < ecc_size; j++) { - ecc_block[j] = rs.getResult(j); - } - - /* Correct error correction data but in reverse order */ - for (j = 0; j < data_size; j++) { - block[j] = data_block[j]; - } - for (j = 0; j < ecc_size; j++) { - block[(j + data_size)] = ecc_block[ecc_size - j - 1]; - } - - for (j = 0; j < n2; j++) { - word[((b1 + b2) * j) + i] = block[j]; - } - if (block_size == n1) { - word[((b1 + b2) * (n1 - 1)) + i] = block[(n1 - 1)]; - } - } - } - - private void placeDataInGrid(int modules, int size) { - int x, y, macromodule, offset; - - offset = 13 - ((modules - 1) / 2); - for (y = 0; y < modules; y++) { - for (x = 0; x < modules; x++) { - macromodule = GM_MACRO_MATRIX[((y + offset) * 27) + (x + offset)]; - placeMacroModule(x, y, word[macromodule * 2], word[(macromodule * 2) + 1], size); - } - } - } - - void placeMacroModule(int x, int y, int word1, int word2, int size) { - int i, j; - - i = (x * 6) + 1; - j = (y * 6) + 1; - - if ((word2 & 0x40) != 0) { - grid[(j * size) + i + 2] = true; - } - if ((word2 & 0x20) != 0) { - grid[(j * size) + i + 3] = true; - } - if ((word2 & 0x10) != 0) { - grid[((j + 1) * size) + i] = true; - } - if ((word2 & 0x08) != 0) { - grid[((j + 1) * size) + i + 1] = true; - } - if ((word2 & 0x04) != 0) { - grid[((j + 1) * size) + i + 2] = true; - } - if ((word2 & 0x02) != 0) { - grid[((j + 1) * size) + i + 3] = true; - } - if ((word2 & 0x01) != 0) { - grid[((j + 2) * size) + i] = true; - } - if ((word1 & 0x40) != 0) { - grid[((j + 2) * size) + i + 1] = true; - } - if ((word1 & 0x20) != 0) { - grid[((j + 2) * size) + i + 2] = true; - } - if ((word1 & 0x10) != 0) { - grid[((j + 2) * size) + i + 3] = true; - } - if ((word1 & 0x08) != 0) { - grid[((j + 3) * size) + i] = true; - } - if ((word1 & 0x04) != 0) { - grid[((j + 3) * size) + i + 1] = true; - } - if ((word1 & 0x02) != 0) { - grid[((j + 3) * size) + i + 2] = true; - } - if ((word1 & 0x01) != 0) { - grid[((j + 3) * size) + i + 3] = true; - } - } - - private void addLayerId(int size, int layers, int modules, int ecc_level) { - /* Place the layer ID into each macromodule */ - - int i, j, layer, start, stop; - int[] layerid = new int[layers + 1]; - int[] id = new int[modules * modules]; - - - /* Calculate Layer IDs */ - for (i = 0; i <= layers; i++) { - if (ecc_level == 1) { - layerid[i] = 3 - (i % 4); - } else { - layerid[i] = (i + 5 - ecc_level) % 4; - } - } - - for (i = 0; i < modules; i++) { - for (j = 0; j < modules; j++) { - id[(i * modules) + j] = 0; - } - } - - /* Calculate which value goes in each macromodule */ - start = modules / 2; - stop = modules / 2; - for (layer = 0; layer <= layers; layer++) { - for (i = start; i <= stop; i++) { - id[(start * modules) + i] = layerid[layer]; - id[(i * modules) + start] = layerid[layer]; - id[((modules - start - 1) * modules) + i] = layerid[layer]; - id[(i * modules) + (modules - start - 1)] = layerid[layer]; - } - start--; - stop++; - } - - /* Place the data in the grid */ - for (i = 0; i < modules; i++) { - for (j = 0; j < modules; j++) { - if ((id[(i * modules) + j] & 0x02) != 0) { - grid[(((i * 6) + 1) * size) + (j * 6) + 1] = true; - } - if ((id[(i * modules) + j] & 0x01) != 0) { - grid[(((i * 6) + 1) * size) + (j * 6) + 2] = true; - } - } - } - } -} +/* + * Copyright 2014 Robin Stuart + * + * 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. + */ + +package uk.org.okapibarcode.backend; + +import static uk.org.okapibarcode.util.Arrays.positionOf; + +import java.nio.charset.Charset; +import java.nio.charset.UnsupportedCharsetException; + +/** + *

Implements Grid Matrix bar code symbology according to AIMD014. + * + *

Grid Matrix is a matrix symbology which can encode characters in the ISO/IEC 8859-1 (Latin-1) + * character set as well as those in the GB-2312 character set. Input is assumed to be formatted as + * a UTF string. + * + * @author Robin Stuart + */ +public class GridMatrix extends Symbol { + + private static final char[] SHIFT_SET = { + /* From Table 7 - Encoding of control characters */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* NULL -> SI */ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* DLE -> US */ + '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', + ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~' + }; + + private static final int[] GM_RECOMMEND_CW = { + 9, 30, 59, 114, 170, 237, 315, 405, 506, 618, 741, 875, 1021 + }; + + private static final int[] GM_MAX_CW = { + 11, 40, 79, 146, 218, 305, 405, 521, 650, 794, 953, 1125, 1313 + }; + + private static final int[] GM_DATA_CODEWORDS = { + 0, 15, 13, 11, 9, + 45, 40, 35, 30, 25, + 89, 79, 69, 59, 49, + 146, 130, 114, 98, 81, + 218, 194, 170, 146, 121, + 305, 271, 237, 203, 169, + 405, 360, 315, 270, 225, + 521, 463, 405, 347, 289, + 650, 578, 506, 434, 361, + 794, 706, 618, 530, 441, + 953, 847, 741, 635, 529, + 1125, 1000, 875, 750, 625, + 1313, 1167, 1021, 875, 729 + }; + + private static final int[] GM_N1 = { + 18, 50, 98, 81, 121, 113, 113, 116, 121, 126, 118, 125, 122 + }; + private static final int[] GM_B1 = { + 1, 1, 1, 2, 2, 2, 2, 3, 2, 7, 5, 10, 6 + }; + private static final int[] GM_B2 = { + 0, 0, 0, 0, 0, 1, 2, 2, 4, 0, 4, 0, 6 + }; + + private static final int[] GM_EBEB = { + /* E1 B3 E2 B4 */ + 0, 0, 0, 0, // version 1 + 3, 1, 0, 0, + 5, 1, 0, 0, + 7, 1, 0, 0, + 9, 1, 0, 0, + 5, 1, 0, 0, // version 2 + 10, 1, 0, 0, + 15, 1, 0, 0, + 20, 1, 0, 0, + 25, 1, 0, 0, + 9, 1, 0, 0, // version 3 + 19, 1, 0, 0, + 29, 1, 0, 0, + 39, 1, 0, 0, + 49, 1, 0, 0, + 8, 2, 0, 0, // version 4 + 16, 2, 0, 0, + 24, 2, 0, 0, + 32, 2, 0, 0, + 41, 1, 10, 1, + 12, 2, 0, 0, // version 5 + 24, 2, 0, 0, + 36, 2, 0, 0, + 48, 2, 0, 0, + 61, 1, 60, 1, + 11, 3, 0, 0, // version 6 + 23, 1, 22, 2, + 34, 2, 33, 1, + 45, 3, 0, 0, + 57, 1, 56, 2, + 12, 1, 11, 3, // version 7 + 23, 2, 22, 2, + 34, 3, 33, 1, + 45, 4, 0, 0, + 57, 1, 56, 3, + 12, 2, 11, 3, // version 8 + 23, 5, 0, 0, + 35, 3, 34, 2, + 47, 1, 46, 4, + 58, 4, 57, 1, + 12, 6, 0, 0, // version 9 + 24, 6, 0, 0, + 36, 6, 0, 0, + 48, 6, 0, 0, + 61, 1, 60, 5, + 13, 4, 12, 3, // version 10 + 26, 1, 25, 6, + 38, 5, 37, 2, + 51, 2, 50, 5, + 63, 7, 0, 0, + 12, 6, 11, 3, // version 11 + 24, 4, 23, 5, + 36, 2, 35, 7, + 47, 9, 0, 0, + 59, 7, 58, 2, + 13, 5, 12, 5, // version 12 + 25, 10, 0, 0, + 38, 5, 37, 5, + 50, 10, 0, 0, + 63, 5, 62, 5, + 13, 1, 12, 11, //version 13 + 25, 3, 24, 9, + 37, 5, 36, 7, + 49, 7, 48, 5, + 61, 9, 60, 3 + }; + + private static final int[] GM_MACRO_MATRIX = { + 728, 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, + 727, 624, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 651, + 726, 623, 528, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 553, 652, + 725, 622, 527, 440, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 463, 554, 653, + 724, 621, 526, 439, 360, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 381, 464, 555, 654, + 723, 620, 525, 438, 359, 288, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 307, 382, 465, 556, 655, + 722, 619, 524, 437, 358, 287, 224, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 241, 308, 383, 466, 557, 656, + 721, 618, 523, 436, 357, 286, 223, 168, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 183, 242, 309, 384, 467, 558, 657, + 720, 617, 522, 435, 356, 285, 222, 167, 120, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 133, 184, 243, 310, 385, 468, 559, 658, + 719, 616, 521, 434, 355, 284, 221, 166, 119, 80, 49, 50, 51, 52, 53, 54, 55, 56, 91, 134, 185, 244, 311, 386, 469, 560, 659, + 718, 615, 520, 433, 354, 283, 220, 165, 118, 79, 48, 25, 26, 27, 28, 29, 30, 57, 92, 135, 186, 245, 312, 387, 470, 561, 660, + 717, 614, 519, 432, 353, 282, 219, 164, 117, 78, 47, 24, 9, 10, 11, 12, 31, 58, 93, 136, 187, 246, 313, 388, 471, 562, 661, + 716, 613, 518, 431, 352, 281, 218, 163, 116, 77, 46, 23, 8, 1, 2, 13, 32, 59, 94, 137, 188, 247, 314, 389, 472, 563, 662, + 715, 612, 517, 430, 351, 280, 217, 162, 115, 76, 45, 22, 7, 0, 3, 14, 33, 60, 95, 138, 189, 248, 315, 390, 473, 564, 663, + 714, 611, 516, 429, 350, 279, 216, 161, 114, 75, 44, 21, 6, 5, 4, 15, 34, 61, 96, 139, 190, 249, 316, 391, 474, 565, 664, + 713, 610, 515, 428, 349, 278, 215, 160, 113, 74, 43, 20, 19, 18, 17, 16, 35, 62, 97, 140, 191, 250, 317, 392, 475, 566, 665, + 712, 609, 514, 427, 348, 277, 214, 159, 112, 73, 42, 41, 40, 39, 38, 37, 36, 63, 98, 141, 192, 251, 318, 393, 476, 567, 666, + 711, 608, 513, 426, 347, 276, 213, 158, 111, 72, 71, 70, 69, 68, 67, 66, 65, 64, 99, 142, 193, 252, 319, 394, 477, 568, 667, + 710, 607, 512, 425, 346, 275, 212, 157, 110, 109, 108, 107, 106, 105, 104, 103, 102, 101, 100, 143, 194, 253, 320, 395, 478, 569, 668, + 709, 606, 511, 424, 345, 274, 211, 156, 155, 154, 153, 152, 151, 150, 149, 148, 147, 146, 145, 144, 195, 254, 321, 396, 479, 570, 669, + 708, 605, 510, 423, 344, 273, 210, 209, 208, 207, 206, 205, 204, 203, 202, 201, 200, 199, 198, 197, 196, 255, 322, 397, 480, 571, 670, + 707, 604, 509, 422, 343, 272, 271, 270, 269, 268, 267, 266, 265, 264, 263, 262, 261, 260, 259, 258, 257, 256, 323, 398, 481, 572, 671, + 706, 603, 508, 421, 342, 341, 340, 339, 338, 337, 336, 335, 334, 333, 332, 331, 330, 329, 328, 327, 326, 325, 324, 399, 482, 573, 672, + 705, 602, 507, 420, 419, 418, 417, 416, 415, 414, 413, 412, 411, 410, 409, 408, 407, 406, 405, 404, 403, 402, 401, 400, 483, 574, 673, + 704, 601, 506, 505, 504, 503, 502, 501, 500, 499, 498, 497, 496, 495, 494, 493, 492, 491, 490, 489, 488, 487, 486, 485, 484, 575, 674, + 703, 600, 599, 598, 597, 596, 595, 594, 593, 592, 591, 590, 589, 588, 587, 586, 585, 584, 583, 582, 581, 580, 579, 578, 577, 576, 675, + 702, 701, 700, 699, 698, 697, 696, 695, 694, 693, 692, 691, 690, 689, 688, 687, 686, 685, 684, 683, 682, 681, 680, 679, 678, 677, 676 + }; + + private static final char[] MIXED_ALPHANUM_SET = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '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', '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', ' ' + }; + + private enum Mode { + NULL, GM_NUMBER, GM_LOWER, GM_UPPER, GM_MIXED, GM_CONTROL, GM_BYTE, GM_CHINESE + } + + private StringBuilder binary; + private int[] word = new int[1460]; + private boolean[] grid; + private Mode appxDnextSection = Mode.NULL; + private Mode appxDlastSection = Mode.NULL; + private int preferredVersion = 0; + private int preferredEccLevel = -1; + + /** + * Set preferred size, or "version" of the symbol according to the following + * table. This value may be ignored if the data to be encoded does not fit + * into a symbol of the selected size. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
InputSize
1 18 x 18
2 30 x 30
3 42 x 42
4 54 x 54
5 66 x 66
6 78 x 78
7 90 x 90
8 102 x 102
9 114 x 114
10 126 x 126
11 138 x 138
12 150 x 150
13 162 x 162
+ * + * @param version symbol version + */ + public void setPreferredVersion(int version) { + preferredVersion = version; + } + + /** + * Set the preferred amount of the symbol which should be dedicated to error + * correction data. Values should be selected from the following table: + * + * + * + * + * + * + * + * + * + * + *
ModeError Correction Capacity
1 Approximately 10%
2 Approximately 20%
3 Approximately 30%
4 Approximately 40%
5 Approximately 50%
+ * + * @param eccLevel error correction level + */ + public void setPreferredEccLevel(int eccLevel) { + preferredEccLevel = eccLevel; + } + + @Override + protected boolean eciSupported() { + return true; + } + + @Override + protected void encode() { + int size, modules, dark, error_number; + int auto_layers, min_layers, layers, auto_ecc_level, min_ecc_level, ecc_level; + int x, y, i; + int data_cw, input_latch = 0; + int data_max; + int length; + StringBuilder bin = new StringBuilder(); + + for (i = 0; i < 1460; i++) { + word[i] = 0; + } + + try { + Charset gb2312 = Charset.forName("GB2312"); + if (gb2312.newEncoder().canEncode(content)) { + /* GB2312 will work, use Chinese compaction */ + byte[] inputBytes = content.getBytes(gb2312); + inputData = new int[inputBytes.length]; + length = 0; + for (i = 0; i < inputBytes.length; i++) { + if (((inputBytes[i] & 0xFF) >= 0xA1) && ((inputBytes[i] & 0xFF) <= 0xF7)) { + /* Double byte character */ + inputData[length] = ((inputBytes[i] & 0xFF) * 256) + (inputBytes[i + 1] & 0xFF); + i++; + length++; + } else { + /* Single byte character */ + inputData[length] = inputBytes[i] & 0xFF; + length++; + } + } + infoLine("Using GB2312 character encoding"); + eciMode = 29; + } else { + /* GB2312 encoding won't work, use other ECI mode */ + eciProcess(); // Get ECI mode + length = inputData.length; + } + } catch (UnsupportedCharsetException e) { + throw new OkapiInternalException("Byte conversion encoding error"); + } + + error_number = encodeGridMatrixBinary(length, readerInit); + if (error_number != 0) { + throw OkapiInputException.inputTooLong(); + } + + /* Determine the size of the symbol */ + data_cw = binary.length() / 7; + + auto_layers = 1; + for (i = 0; i < 13; i++) { + if (GM_RECOMMEND_CW[i] < data_cw) { + auto_layers = i + 1; + } + } + + min_layers = 13; + for (i = 12; i > 0; i--) { + if (GM_MAX_CW[(i - 1)] >= data_cw) { + min_layers = i; + } + } + layers = auto_layers; + auto_ecc_level = 3; + if (layers == 1) { + auto_ecc_level = 5; + } + if ((layers == 2) || (layers == 3)) { + auto_ecc_level = 4; + } + min_ecc_level = 1; + if (layers == 1) { + min_ecc_level = 4; + } + if ((layers == 2) || (layers == 3)) { + min_ecc_level = 2; + } + ecc_level = auto_ecc_level; + + if ((preferredVersion >= 1) && (preferredVersion <= 13)) { + input_latch = 1; + if (preferredVersion > min_layers) { + layers = preferredVersion; + } else { + layers = min_layers; + } + } + + if (input_latch == 1) { + auto_ecc_level = 3; + if (layers == 1) { + auto_ecc_level = 5; + } + if ((layers == 2) || (layers == 3)) { + auto_ecc_level = 4; + } + ecc_level = auto_ecc_level; + if (data_cw > GM_DATA_CODEWORDS[(5 * (layers - 1)) + (ecc_level - 1)]) { + layers++; + } + } + + if (input_latch == 0) { + if ((preferredEccLevel >= 1) && (preferredEccLevel <= 5)) { + if (preferredEccLevel > min_ecc_level) { + ecc_level = preferredEccLevel; + } else { + ecc_level = min_ecc_level; + } + } + if (data_cw > GM_DATA_CODEWORDS[(5 * (layers - 1)) + (ecc_level - 1)]) { + do { + layers++; + } while ((data_cw > GM_DATA_CODEWORDS[(5 * (layers - 1)) + (ecc_level - 1)]) && (layers <= 13)); + } + } + + data_max = 1313; + switch (ecc_level) { + case 2: + data_max = 1167; + break; + case 3: + data_max = 1021; + break; + case 4: + data_max = 875; + break; + case 5: + data_max = 729; + break; + } + + if (data_cw > data_max) { + throw OkapiInputException.inputTooLong(); + } + + addErrorCorrection(data_cw, layers, ecc_level); + size = 6 + (layers * 12); + modules = 1 + (layers * 2); + + infoLine("Layers: " + layers); + infoLine("ECC Level: " + ecc_level); + infoLine("Data Codewords: " + data_cw); + infoLine("ECC Codewords: " + GM_DATA_CODEWORDS[((layers - 1) * 5) + (ecc_level - 1)]); + infoLine("Grid Size: " + modules + " X " + modules); + + grid = new boolean[size * size]; + + for (x = 0; x < size; x++) { + for (y = 0; y < size; y++) { + grid[(y * size) + x] = false; + } + } + + placeDataInGrid(modules, size); + addLayerId(size, layers, modules, ecc_level); + + /* Add macromodule frames */ + for (x = 0; x < modules; x++) { + dark = 1 - (x & 1); + for (y = 0; y < modules; y++) { + if (dark == 1) { + for (i = 0; i < 5; i++) { + grid[((y * 6) * size) + (x * 6) + i] = true; + grid[(((y * 6) + 5) * size) + (x * 6) + i] = true; + grid[(((y * 6) + i) * size) + (x * 6)] = true; + grid[(((y * 6) + i) * size) + (x * 6) + 5] = true; + } + grid[(((y * 6) + 5) * size) + (x * 6) + 5] = true; + dark = 0; + } else { + dark = 1; + } + } + } + + /* Copy values to symbol */ + symbol_width = size; + row_count = size; + row_height = new int[row_count]; + pattern = new String[row_count]; + + for (x = 0; x < size; x++) { + bin.setLength(0); + for (y = 0; y < size; y++) { + if (grid[(x * size) + y]) { + bin.append('1'); + } else { + bin.append('0'); + } + } + row_height[x] = 1; + pattern[x] = bin2pat(bin); + } + } + + private int encodeGridMatrixBinary(int length, boolean reader) { + /* Create a binary stream representation of the input data. + 7 sets are defined - Chinese characters, Numerals, Lower case letters, Upper case letters, + Mixed numerals and letters, Control characters and 8-bit binary data */ + int sp, glyph = 0; + Mode current_mode, next_mode, last_mode; + int c1, c2; + boolean done; + int p = 0, ppos; + int punt = 0; + int number_pad_posn; + int byte_count_posn = 0, byte_count = 0; + int shift, i; + int[] numbuf = new int[3]; + Mode[] modeMap = calculateModeMap(length); + + binary = new StringBuilder(); + + sp = 0; + current_mode = Mode.NULL; + number_pad_posn = 0; + + info("Encoding: "); + + if (reader) { + binary.append("1010"); /* FNC3 - Reader Initialisation */ + info("INIT "); + } + + if ((eciMode != 3) && (eciMode != 29)) { + binary.append("1100"); /* ECI */ + + if ((eciMode >= 0) && (eciMode <= 1023)) { + binary.append('0'); + for (i = 0x200; i > 0; i = i >> 1) { + if ((eciMode & i) != 0) { + binary.append('1'); + } else { + binary.append('0'); + } + } + } + + if ((eciMode >= 1024) && (eciMode <= 32767)) { + binary.append("10"); + for (i = 0x4000; i > 0; i = i >> 1) { + if ((eciMode & i) != 0) { + binary.append('1'); + } else { + binary.append('0'); + } + } + } + + if ((eciMode >= 32768) && (eciMode <= 811799)) { + binary.append("11"); + for (i = 0x80000; i > 0; i = i >> 1) { + if ((eciMode & i) != 0) { + binary.append('1'); + } else { + binary.append('0'); + } + } + } + + info("ECI "); + infoSpace(eciMode); + } + + do { + next_mode = modeMap[sp]; + + if (next_mode != current_mode) { + switch (current_mode) { + case NULL: + switch (next_mode) { + case GM_CHINESE: + binary.append("0001"); + break; + case GM_NUMBER: + binary.append("0010"); + break; + case GM_LOWER: + binary.append("0011"); + break; + case GM_UPPER: + binary.append("0100"); + break; + case GM_MIXED: + binary.append("0101"); + break; + case GM_BYTE: + binary.append("0111"); + break; + } + break; + case GM_CHINESE: + switch (next_mode) { + case GM_NUMBER: + binary.append("1111111100001"); + break; // 8161 + case GM_LOWER: + binary.append("1111111100010"); + break; // 8162 + case GM_UPPER: + binary.append("1111111100011"); + break; // 8163 + case GM_MIXED: + binary.append("1111111100100"); + break; // 8164 + case GM_BYTE: + binary.append("1111111100101"); + break; // 8165 + } + break; + case GM_NUMBER: + /* add numeric block padding value */ + switch (p) { + case 1: + binary.insert(number_pad_posn, "10"); + break; // 2 pad digits + case 2: + binary.insert(number_pad_posn, "01"); + break; // 1 pad digit + case 3: + binary.insert(number_pad_posn, "00"); + break; // 0 pad digits + } + + switch (next_mode) { + case GM_CHINESE: + binary.append("1111111011"); + break; // 1019 + case GM_LOWER: + binary.append("1111111100"); + break; // 1020 + case GM_UPPER: + binary.append("1111111101"); + break; // 1021 + case GM_MIXED: + binary.append("1111111110"); + break; // 1022 + case GM_BYTE: + binary.append("1111111111"); + break; // 1023 + } + break; + case GM_LOWER: + case GM_UPPER: + switch (next_mode) { + case GM_CHINESE: + binary.append("11100"); + break; // 28 + case GM_NUMBER: + binary.append("11101"); + break; // 29 + case GM_LOWER: + case GM_UPPER: + binary.append("11110"); + break; // 30 + case GM_MIXED: + binary.append("1111100"); + break; // 124 + case GM_BYTE: + binary.append("1111110"); + break; // 126 + } + break; + case GM_MIXED: + switch (next_mode) { + case GM_CHINESE: + binary.append("1111110001"); + break; // 1009 + case GM_NUMBER: + binary.append("1111110010"); + break; // 1010 + case GM_LOWER: + binary.append("1111110011"); + break; // 1011 + case GM_UPPER: + binary.append("1111110100"); + break; // 1012 + case GM_BYTE: + binary.append("1111110111"); + break; // 1015 + } + break; + case GM_BYTE: + /* add byte block length indicator */ + addByteCount(byte_count_posn, byte_count); + byte_count = 0; + switch (next_mode) { + case GM_CHINESE: + binary.append("0001"); + break; // 1 + case GM_NUMBER: + binary.append("0010"); + break; // 2 + case GM_LOWER: + binary.append("0011"); + break; // 3 + case GM_UPPER: + binary.append("0100"); + break; // 4 + case GM_MIXED: + binary.append("0101"); + break; // 5 + } + break; + } + + switch (next_mode) { + case GM_CHINESE: + info("CHIN "); + break; + case GM_NUMBER: + info("NUMB "); + break; + case GM_LOWER: + info("LOWR "); + break; + case GM_UPPER: + info("UPPR "); + break; + case GM_MIXED: + info("MIXD "); + break; + case GM_BYTE: + info("BYTE "); + break; + } + + } + last_mode = current_mode; + current_mode = next_mode; + + switch (current_mode) { + case GM_CHINESE: + done = false; + if (inputData[sp] > 0xff) { + /* GB2312 character */ + c1 = (inputData[sp] & 0xff00) >> 8; + c2 = inputData[sp] & 0xff; + + if ((c1 >= 0xa0) && (c1 <= 0xa9)) { + glyph = (0x60 * (c1 - 0xa1)) + (c2 - 0xa0); + } + if ((c1 >= 0xb0) && (c1 <= 0xf7)) { + glyph = (0x60 * (c1 - 0xb0 + 9)) + (c2 - 0xa0); + } + done = true; + } + if (!(done)) { + if (sp != (length - 1)) { + if ((inputData[sp] == 13) && (inputData[sp + 1] == 10)) { + /* End of Line */ + glyph = 7776; + sp++; + } + done = true; + } + } + if (!(done)) { + if (sp != (length - 1)) { + if (((inputData[sp] >= '0') && (inputData[sp] <= '9')) && ((inputData[sp + 1] >= '0') && (inputData[sp + 1] <= '9'))) { + /* Two digits */ + glyph = 8033 + (10 * (inputData[sp] - '0')) + (inputData[sp + 1] - '0'); + sp++; + } + } + } + if (!(done)) { + /* Byte value */ + glyph = 7777 + inputData[sp]; + } + + infoSpace(glyph); + + for (i = 0x1000; i > 0; i = i >> 1) { + if ((glyph & i) != 0) { + binary.append('1'); + } else { + binary.append('0'); + } + } + sp++; + break; + + case GM_NUMBER: + if (last_mode != current_mode) { + /* Reserve a space for numeric digit padding value (2 bits) */ + number_pad_posn = binary.length(); + } + p = 0; + ppos = -1; + + /* Numeric compression can also include certain combinations of + non-numeric character */ + numbuf[0] = '0'; + numbuf[1] = '0'; + numbuf[2] = '0'; + do { + if ((inputData[sp] >= '0') && (inputData[sp] <= '9')) { + numbuf[p] = inputData[sp]; + p++; + } + switch (inputData[sp]) { + case ' ': + case '+': + case '-': + case '.': + case ',': + punt = inputData[sp]; + ppos = p; + break; + } + if (sp < (length - 1)) { + if ((inputData[sp] == 13) && (inputData[sp + 1] == 10)) { + /* */ + punt = inputData[sp]; + sp++; + ppos = p; + } + } + sp++; + } while ((p < 3) && (sp < length)); + + if (ppos != -1) { + switch (punt) { + case ' ': + glyph = 0; + break; + case '+': + glyph = 3; + break; + case '-': + glyph = 6; + break; + case '.': + glyph = 9; + break; + case ',': + glyph = 12; + break; + case 0x13: + glyph = 15; + break; + } + glyph += ppos; + glyph += 1000; + + infoSpace(glyph); + + for (i = 0x200; i > 0; i = i >> 1) { + if ((glyph & i) != 0) { + binary.append('1'); + } else { + binary.append('0'); + } + } + } + + glyph = (100 * (numbuf[0] - '0')) + (10 * (numbuf[1] - '0')) + (numbuf[2] - '0'); + infoSpace(glyph); + + for (i = 0x200; i > 0; i = i >> 1) { + if ((glyph & i) != 0) { + binary.append('1'); + } else { + binary.append('0'); + } + } + break; + + case GM_BYTE: + if (last_mode != current_mode) { + /* Reserve space for byte block length indicator (9 bits) */ + byte_count_posn = binary.length(); + } + if (byte_count == 512) { + /* Maximum byte block size is 512 bytes. If longer is needed then start a new block */ + addByteCount(byte_count_posn, byte_count); + binary.append("0111"); + byte_count_posn = binary.length(); + byte_count = 0; + } + + glyph = inputData[sp]; + infoSpace(glyph); + for (i = 0x80; i > 0; i = i >> 1) { + if ((glyph & i) != 0) { + binary.append('1'); + } else { + binary.append('0'); + } + } + sp++; + byte_count++; + break; + + case GM_MIXED: + shift = 1; + if ((inputData[sp] >= '0') && (inputData[sp] <= '9')) { + shift = 0; + } + if ((inputData[sp] >= 'A') && (inputData[sp] <= 'Z')) { + shift = 0; + } + if ((inputData[sp] >= 'a') && (inputData[sp] <= 'z')) { + shift = 0; + } + if (inputData[sp] == ' ') { + shift = 0; + } + + if (shift == 0) { + /* Mixed Mode character */ + glyph = positionOf((char) inputData[sp], MIXED_ALPHANUM_SET); + infoSpace(glyph); + + for (i = 0x20; i > 0; i = i >> 1) { + if ((glyph & i) != 0) { + binary.append('1'); + } else { + binary.append('0'); + } + } + } else { + /* Shift Mode character */ + binary.append("1111110110"); /* 1014 - shift indicator */ + + addShiftCharacter(inputData[sp]); + } + + sp++; + break; + + case GM_UPPER: + shift = 1; + if ((inputData[sp] >= 'A') && (inputData[sp] <= 'Z')) { + shift = 0; + } + if (inputData[sp] == ' ') { + shift = 0; + } + + if (shift == 0) { + /* Upper Case character */ + glyph = positionOf((char) inputData[sp], MIXED_ALPHANUM_SET) - 10; + if (glyph == 52) { + // Space character + glyph = 26; + } + infoSpace(glyph); + + for (i = 0x10; i > 0; i = i >> 1) { + if ((glyph & i) != 0) { + binary.append('1'); + } else { + binary.append('0'); + } + } + + } else { + /* Shift Mode character */ + binary.append("1111101"); /* 127 - shift indicator */ + + addShiftCharacter(inputData[sp]); + } + + sp++; + break; + + case GM_LOWER: + shift = 1; + if ((inputData[sp] >= 'a') && (inputData[sp] <= 'z')) { + shift = 0; + } + if (inputData[sp] == ' ') { + shift = 0; + } + + if (shift == 0) { + /* Lower Case character */ + glyph = positionOf((char) inputData[sp], MIXED_ALPHANUM_SET) - 36; + infoSpace(glyph); + + for (i = 0x10; i > 0; i = i >> 1) { + if ((glyph & i) != 0) { + binary.append('1'); + } else { + binary.append('0'); + } + } + + } else { + /* Shift Mode character */ + binary.append("1111101"); /* 127 - shift indicator */ + + addShiftCharacter(inputData[sp]); + } + + sp++; + break; + } + if (binary.length() > 9191) { + return 1; + } + + } while (sp < length); + + infoLine(); + + if (current_mode == Mode.GM_NUMBER) { + /* add numeric block padding value */ + switch (p) { + case 1: + binary.insert(number_pad_posn, "10"); + break; // 2 pad digits + case 2: + binary.insert(number_pad_posn, "01"); + break; // 1 pad digit + case 3: + binary.insert(number_pad_posn, "00"); + break; // 0 pad digits + } + } + + if (current_mode == Mode.GM_BYTE) { + /* Add byte block length indicator */ + addByteCount(byte_count_posn, byte_count); + } + + /* Add "end of data" character */ + switch (current_mode) { + case GM_CHINESE: + binary.append("1111111100000"); + break; // 8160 + case GM_NUMBER: + binary.append("1111111010"); + break; // 1018 + case GM_LOWER: + case GM_UPPER: + binary.append("11011"); + break; // 27 + case GM_MIXED: + binary.append("1111110000"); + break; // 1008 + case GM_BYTE: + binary.append("0000"); + break; // 0 + } + + /* Add padding bits if required */ + p = 7 - (binary.length() % 7); + if (p == 7) { + p = 0; + } + for (i = 0; i < p; i++) { + binary.append('0'); + } + + if (binary.length() > 9191) { + return 1; + } + + return 0; + } + + private Mode[] calculateModeMap(int length) { + Mode[] modeMap = new Mode[length]; + int i; + int digitStart, digitLength; + boolean digits; + int spaceStart, spaceLength; + boolean spaces; + int[] segmentLength; + Mode[] segmentType; + int[] segmentStart; + int segmentCount; + + // Step 1 + // Characters in GB2312 are encoded as Chinese characters + for (i = 0; i < length; i++) { + modeMap[i] = Mode.NULL; + if (inputData[i] > 0xFF) { + modeMap[i] = Mode.GM_CHINESE; + } + } + + // Consecutive characters, if preceeded by or followed + // by chinese characters, are encoded as chinese characters. + if (length > 3) { + i = 1; + do { + if ((inputData[i] == 13) && (inputData[i + 1] == 10)) { + // End of line (CR/LF) + + if (modeMap[i - 1] == Mode.GM_CHINESE) { + modeMap[i] = Mode.GM_CHINESE; + modeMap[i + 1] = Mode.GM_CHINESE; + } + i += 2; + } else { + i++; + } + } while (i < length - 1); + + i = length - 3; + do { + if ((inputData[i] == 13) && (inputData[i + 1] == 10)) { + // End of line (CR/LF) + if (modeMap[i + 2] == Mode.GM_CHINESE) { + modeMap[i] = Mode.GM_CHINESE; + modeMap[i + 1] = Mode.GM_CHINESE; + } + i -= 2; + } else { + i--; + } + } while (i > 0); + } + + // Digit pairs between chinese characters encode as chinese characters. + digits = false; + digitLength = 0; + digitStart = 0; + for (i = 1; i < length - 1; i++) { + if ((inputData[i] >= 48) && (inputData[i] <= 57)) { + // '0' to '9' + if (digits == false) { + digits = true; + digitLength = 1; + digitStart = i; + } else { + digitLength++; + } + } else { + if (digits == true) { + if ((digitLength % 2) == 0) { + if ((modeMap[digitStart - 1] == Mode.GM_CHINESE) && + (modeMap[i] == Mode.GM_CHINESE)) { + for(int j = 0; j < digitLength; j++) { + modeMap[i - j - 1] = Mode.GM_CHINESE; + } + } + } + digits = false; + } + } + } + + // Step 2: all characters 'a' to 'z' are lowercase. + for (i = 0; i < length; i++) { + if ((inputData[i] >= 97) && (inputData[i] <= 122)) { + modeMap[i] = Mode.GM_LOWER; + } + } + + // Step 3: all characters 'A' to 'Z' are uppercase. + for (i = 0; i < length; i++) { + if ((inputData[i] >= 65) && (inputData[i] <= 90)) { + modeMap[i] = Mode.GM_UPPER; + } + } + + // Step 4: find consecutive characters preceeded or followed + // by uppercase or lowercase. + spaces = false; + spaceLength = 0; + spaceStart = 0; + for (i = 1; i < length - 1; i++) { + if (inputData[i] == 32) { + if (spaces == false) { + spaces = true; + spaceLength = 1; + spaceStart = i; + } else { + spaceLength++; + } + } else { + if (spaces == true) { + + Mode modeX = modeMap[spaceStart - 1]; + Mode modeY = modeMap[i]; + + if ((modeX == Mode.GM_LOWER) || (modeX == Mode.GM_UPPER)) { + for (int j = 0; j < spaceLength; j++) { + modeMap[i - j - 1] = modeX; + } + } else { + if ((modeY == Mode.GM_LOWER) || (modeY == Mode.GM_UPPER)) { + for (int j = 0; j < spaceLength; j++) { + modeMap[i - j - 1] = modeY; + } + } + } + spaces = false; + } + } + } + + // Step 5: Unassigned characters '0' to '9' are assigned as numerals. + // Non-numeric characters in table 7 are also assigned as numerals. + for(i = 0; i < length; i++) { + if(modeMap[i] == Mode.NULL) { + if ((inputData[i] >= 48) && (inputData[i] <= 57)) { + // '0' to '9' + modeMap[i] = Mode.GM_NUMBER; + } else { + switch (inputData[i]) { + case 32: // Space + case 43: // '+' + case 45: // '-' + case 46: // "." + case 44: // "," + modeMap[i] = Mode.GM_NUMBER; + break; + case 13: // CR + if (i < length - 1) { + if (inputData[i + 1] == 10) { // LF + // + modeMap[i] = Mode.GM_NUMBER; + modeMap[i + 1] = Mode.GM_NUMBER; + } + } + } + } + } + } + + // Step 6: The remining unassigned bytes are assigned as 8-bit binary + for(i = 0; i < length; i++) { + if (modeMap[i] == Mode.NULL) { + modeMap[i] = Mode.GM_BYTE; + } + } + + // break into segments + segmentLength = new int[length]; + segmentType = new Mode[length]; + segmentStart = new int[length]; + + segmentCount = 0; + segmentLength[0] = 1; + segmentType[0] = modeMap[0]; + segmentStart[0] = 0; + for (i = 1; i < length; i++) { + if (modeMap[i] == modeMap[i - 1]) { + segmentLength[segmentCount]++; + } else { + segmentCount++; + segmentLength[segmentCount] = 1; + segmentType[segmentCount] = modeMap[i]; + segmentStart[segmentCount] = i; + } + } + + // A segment can be a control segment if + // a) It is not the start segment of the data stream + // b) All characters are control characters + // c) The length of the segment is no more than 3 + // d) The previous segment is not chinese + if (segmentCount > 1) { + for (i = 1; i < segmentCount; i++) { // (a) + if ((segmentLength[i] <= 3) && (segmentType[i - 1] != Mode.GM_CHINESE)) { // (c) and (d) + boolean controlLatch = true; + for (int j = 0; j < segmentLength[i]; j++) { + boolean thischarLatch = false; + for(int k = 0; k < 63; k++) { + if (inputData[segmentStart[i] + j] == SHIFT_SET[k]) { + thischarLatch = true; + } + } + + if (!(thischarLatch)) { + // This character is not a control character + controlLatch = false; + } + } + + if (controlLatch) { // (b) + segmentType[i] = Mode.GM_CONTROL; + } + } + } + } + + // Stages 7 to 9 + if (segmentCount >= 3) { + for (i = 0; i < segmentCount - 1; i++) { + Mode pm, tm, nm, lm; + int tl, nl, ll, position; + boolean lastSegment = false; + + if (i == 0) { + pm = Mode.NULL; + } else { + pm = segmentType[i - 1]; + } + + tm = segmentType[i]; + tl = segmentLength[i]; + + nm = segmentType[i + 1]; + nl = segmentLength[i + 1]; + + lm = segmentType[i + 2]; + ll = segmentLength[i + 2]; + + position = segmentStart[i]; + + if (i + 2 == segmentCount) { + lastSegment = true; + } + + segmentType[i] = getBestMode(pm, tm, nm, lm, tl, nl, ll, position, lastSegment); + + if (segmentType[i] == Mode.GM_CONTROL) { + segmentType[i] = segmentType[i - 1]; + } + } + + segmentType[i] = appxDnextSection; + segmentType[i + 1] = appxDlastSection; + + if (segmentType[i] == Mode.GM_CONTROL) { + segmentType[i] = segmentType[i - 1]; + } + if (segmentType[i + 1] == Mode.GM_CONTROL) { + segmentType[i + 1] = segmentType[i]; + } + +// Uncomment these lines to override mode selection and generate symbol as shown +// in image D.1 for the test data "AAT2556 电池充电器+降压转换器 200mA至2A tel:86 019 82512738" +// segmentType[9] = gmMode.GM_LOWER; +// segmentType[10] = gmMode.GM_LOWER; + } + + // Copy segments back to modeMap + for (i = 0; i < segmentCount; i++) { + for (int j = 0; j < segmentLength[i]; j++) { + modeMap[segmentStart[i] + j] = segmentType[i]; + } + } + + return modeMap; + } + + private boolean isTransitionValid(Mode previousMode, Mode thisMode) { + // Filters possible encoding data types from table D.1 + boolean isValid = false; + + switch (previousMode) { + case GM_CHINESE: + switch (thisMode) { + case GM_CHINESE: + case GM_BYTE: + isValid = true; + break; + } + break; + case GM_NUMBER: + switch (thisMode) { + case GM_NUMBER: + case GM_MIXED: + case GM_BYTE: + case GM_CHINESE: + isValid = true; + break; + } + break; + case GM_LOWER: + switch (thisMode) { + case GM_LOWER: + case GM_MIXED: + case GM_BYTE: + case GM_CHINESE: + isValid = true; + break; + } + break; + case GM_UPPER: + switch (thisMode) { + case GM_UPPER: + case GM_MIXED: + case GM_BYTE: + case GM_CHINESE: + isValid = true; + break; + } + break; + case GM_CONTROL: + switch (thisMode) { + case GM_CONTROL: + case GM_BYTE: + case GM_CHINESE: + isValid = true; + break; + } + break; + case GM_BYTE: + switch (thisMode) { + case GM_BYTE: + case GM_CHINESE: + isValid = true; + break; + } + break; + } + + return isValid; + } + + private Mode intToMode(int input) { + Mode retVal; + + switch (input) { + case 1: + retVal = Mode.GM_CHINESE; + break; + case 2: + retVal = Mode.GM_BYTE; + break; + case 3: + retVal = Mode.GM_CONTROL; + break; + case 4: + retVal = Mode.GM_MIXED; + break; + case 5: + retVal = Mode.GM_UPPER; + break; + case 6: + retVal = Mode.GM_LOWER; + break; + case 7: + retVal = Mode.GM_NUMBER; + break; + default: + retVal = Mode.NULL; + break; + } + + return retVal; + } + + private Mode getBestMode(Mode pm, Mode tm, Mode nm, Mode lm, int tl, int nl, int ll, int position, boolean lastSegment) { + int tmi, nmi, lmi; + Mode bestMode = tm; + int binaryLength; + int bestBinaryLength = Integer.MAX_VALUE; + + for(tmi = 1; tmi < 8; tmi++) { + if (isTransitionValid(tm, intToMode(tmi))) { + for(nmi = 1; nmi < 8; nmi++) { + if (isTransitionValid(nm, intToMode(nmi))) { + for (lmi = 1; lmi < 8; lmi++) { + if (isTransitionValid(lm, intToMode(lmi))) { + binaryLength = getBinaryLength(pm, intToMode(tmi), intToMode(nmi), intToMode(lmi), tl, nl, ll, position, lastSegment); + if (binaryLength <= bestBinaryLength) { + bestMode = intToMode(tmi); + appxDnextSection = intToMode(nmi); + appxDlastSection = intToMode(lmi); + bestBinaryLength = binaryLength; + } + } + } + } + } + } + } + + return bestMode; + } + + private int getBinaryLength(Mode pm, Mode tm, Mode nm, Mode lm, int tl, int nl, int ll, int position, boolean lastSegment) { + + int binaryLength = getChunkLength(pm, tm, tl, position); + binaryLength += getChunkLength(tm, nm, nl, (position + tl)); + binaryLength += getChunkLength(nm, lm, ll, (position + tl + nl)); + + if (lastSegment) { + switch (lm) { + case GM_CHINESE: + binaryLength += 13; + break; + case GM_NUMBER: + binaryLength += 10; + break; + case GM_LOWER: + case GM_UPPER: + binaryLength += 5; + break; + case GM_MIXED: + binaryLength += 10; + break; + case GM_BYTE: + binaryLength += 4; + break; + } + } + + return binaryLength; + } + + private int getChunkLength(Mode lastMode, Mode thisMode, int thisLength, int position) { + int byteLength; + + switch (thisMode) { + case GM_CHINESE: + byteLength = calcChineseLength(position, thisLength); + break; + case GM_NUMBER: + byteLength = calcNumberLength(position, thisLength); + break; + case GM_LOWER: + byteLength = 5 * thisLength; + break; + case GM_UPPER: + byteLength = 5 * thisLength; + break; + case GM_MIXED: + byteLength = calcMixedLength(position, thisLength); + break; + case GM_CONTROL: + byteLength = 6 * thisLength; + break; + default: + //case GM_BYTE: + byteLength = calcByteLength(position, thisLength); + break; + } + + switch (lastMode) { + case NULL: + byteLength += 4; + break; + case GM_CHINESE: + if ((thisMode != Mode.GM_CHINESE) && (thisMode != Mode.GM_CONTROL)) { + byteLength += 13; + } + break; + case GM_NUMBER: + if ((thisMode != Mode.GM_CHINESE) && (thisMode != Mode.GM_CONTROL)) { + byteLength += 10; + } + break; + case GM_LOWER: + switch (thisMode) { + case GM_CHINESE: + case GM_NUMBER: + case GM_UPPER: + byteLength += 5; + break; + case GM_MIXED: + case GM_CONTROL: + case GM_BYTE: + byteLength += 7; + break; + } + break; + case GM_UPPER: + switch (thisMode) { + case GM_CHINESE: + case GM_NUMBER: + case GM_LOWER: + byteLength += 5; + break; + case GM_MIXED: + case GM_CONTROL: + case GM_BYTE: + byteLength += 7; + break; + } + break; + case GM_MIXED: + if (thisMode != Mode.GM_MIXED) { + byteLength += 10; + } + break; + case GM_BYTE: + if (thisMode != Mode.GM_BYTE) { + byteLength += 4; + } + break; + } + + if ((lastMode != Mode.GM_BYTE) && (thisMode == Mode.GM_BYTE)) { + byteLength += 9; + } + + if ((lastMode != Mode.GM_NUMBER) && (thisMode == Mode.GM_NUMBER)) { + byteLength += 2; + } + + return byteLength; + } + + private int calcChineseLength(int position, int length) { + int i = 0; + int bits = 0; + + do { + bits += 13; + + if (i < length) { + if ((inputData[position + i] == 13) && (inputData[position + i + 1] == 10)) { + // + i++; + } + + if (((inputData[position + i] >= 48) && (inputData[position + i] <= 57)) && + ((inputData[position + i + 1] >= 48) && (inputData[position + i + 1] <= 57))) { + // two digits + i++; + } + } + i++; + } while (i < length); + + return bits; + } + + private int calcMixedLength(int position, int length) { + int bits = 0; + int i; + + for (i = 0; i < length; i++) { + bits += 6; + for(int k = 0; k < 63; k++) { + if (inputData[position + i] == SHIFT_SET[k]) { + bits += 10; + } + } + } + + return bits; + } + + private int calcNumberLength(int position, int length) { + int i; + int bits = 0; + int numbers = 0; + int nonnumbers = 0; + + for (i = 0; i < length; i++) { + if ((inputData[position + i] >= 48) && (inputData[position + i] <= 57)) { + numbers++; + } else { + nonnumbers++; + } + + if (i != 0) { + if ((inputData[position + i] == 10) && (inputData[position + i - 1] == 13)) { + // + nonnumbers--; + } + } + + if (numbers == 3) { + if (nonnumbers == 1) { + bits += 20; + } else { + bits += 10; + } + if (nonnumbers > 1) { + // Invalid encoding + bits += 100; + } + numbers = 0; + nonnumbers = 0; + } + } + + if (numbers > 0) { + if (nonnumbers == 1) { + bits += 20; + } else { + bits += 10; + } + } + + if (nonnumbers > 1) { + // Invalid + bits += 100; + } + + if (!((inputData[position + i - 1] >= 48) && (inputData[position + i - 1] <= 57))) { + // Data must end with a digit + bits += 100; + } + + + return bits; + } + + private int calcByteLength(int position, int length) { + int i; + int bits = 0; + + for (i = 0; i < length; i++) { + if (inputData[position + i] <= 0xFF) { + bits += 8; + } else { + bits += 16; + } + } + + return bits; + } + + private void addByteCount(int byte_count_posn, int byte_count) { + /* Add the length indicator for byte encoded blocks */ + for (int i = 0; i < 9; i++) { + if ((byte_count & (0x100 >> i)) != 0) { + binary.insert(byte_count_posn + i, '0'); + } else { + binary.insert(byte_count_posn + i, '1'); + } + } + } + + void addShiftCharacter(int shifty) { + /* Add a control character to the data stream */ + int i; + int glyph = 0; + + for (i = 0; i < 64; i++) { + if (SHIFT_SET[i] == shifty) { + glyph = i; + } + } + + info("SHT/"); + infoSpace(glyph); + + for (i = 0x20; i > 0; i = i >> 1) { + if ((glyph & i) != 0) { + binary.append('1'); + } else { + binary.append('0'); + } + } + } + + private void addErrorCorrection(int data_posn, int layers, int ecc_level) { + int data_cw, i, j, wp; + int n1, b1, n2, b2, e1, b3, e2; + int block_size, data_size, ecc_size; + int[] data = new int[1320]; + int[] block = new int[130]; + int[] data_block = new int[115]; + int[] ecc_block = new int[70]; + ReedSolomon rs = new ReedSolomon(); + + data_cw = GM_DATA_CODEWORDS[((layers - 1) * 5) + (ecc_level - 1)]; + + for (i = 0; i < 1320; i++) { + data[i] = 0; + } + + /* Convert from binary sream to 7-bit codewords */ + for (i = 0; i < data_posn; i++) { + for (j = 0; j < 7; j++) { + if (binary.charAt((i * 7) + j) == '1') { + data[i] += 0x40 >> j; + } + } + } + + info("Codewords: "); + for (i = 0; i < data_posn; i++) { + infoSpace(data[i]); + } + infoLine(); + + /* Add padding codewords */ + data[data_posn] = 0x00; + for (i = (data_posn + 1); i < data_cw; i++) { + if ((i & 1) != 0) { + data[i] = 0x7e; + } else { + data[i] = 0x00; + } + } + + /* Get block sizes */ + n1 = GM_N1[(layers - 1)]; + b1 = GM_B1[(layers - 1)]; + n2 = n1 - 1; + b2 = GM_B2[(layers - 1)]; + e1 = GM_EBEB[((layers - 1) * 20) + ((ecc_level - 1) * 4)]; + b3 = GM_EBEB[((layers - 1) * 20) + ((ecc_level - 1) * 4) + 1]; + e2 = GM_EBEB[((layers - 1) * 20) + ((ecc_level - 1) * 4) + 2]; + + /* Split the data into blocks */ + wp = 0; + for (i = 0; i < (b1 + b2); i++) { + if (i < b1) { + block_size = n1; + } else { + block_size = n2; + } + if (i < b3) { + ecc_size = e1; + } else { + ecc_size = e2; + } + data_size = block_size - ecc_size; + + /* printf("block %d/%d: data %d / ecc %d\n", i + 1, (b1 + b2), data_size, ecc_size);*/ + for (j = 0; j < data_size; j++) { + data_block[j] = data[wp]; + wp++; + } + + /* Calculate ECC data for this block */ + rs.init_gf(0x89); + rs.init_code(ecc_size, 1); + rs.encode(data_size, data_block); + for (j = 0; j < ecc_size; j++) { + ecc_block[j] = rs.getResult(j); + } + + /* Correct error correction data but in reverse order */ + for (j = 0; j < data_size; j++) { + block[j] = data_block[j]; + } + for (j = 0; j < ecc_size; j++) { + block[(j + data_size)] = ecc_block[ecc_size - j - 1]; + } + + for (j = 0; j < n2; j++) { + word[((b1 + b2) * j) + i] = block[j]; + } + if (block_size == n1) { + word[((b1 + b2) * (n1 - 1)) + i] = block[(n1 - 1)]; + } + } + } + + private void placeDataInGrid(int modules, int size) { + int x, y, macromodule, offset; + + offset = 13 - ((modules - 1) / 2); + for (y = 0; y < modules; y++) { + for (x = 0; x < modules; x++) { + macromodule = GM_MACRO_MATRIX[((y + offset) * 27) + (x + offset)]; + placeMacroModule(x, y, word[macromodule * 2], word[(macromodule * 2) + 1], size); + } + } + } + + void placeMacroModule(int x, int y, int word1, int word2, int size) { + int i, j; + + i = (x * 6) + 1; + j = (y * 6) + 1; + + if ((word2 & 0x40) != 0) { + grid[(j * size) + i + 2] = true; + } + if ((word2 & 0x20) != 0) { + grid[(j * size) + i + 3] = true; + } + if ((word2 & 0x10) != 0) { + grid[((j + 1) * size) + i] = true; + } + if ((word2 & 0x08) != 0) { + grid[((j + 1) * size) + i + 1] = true; + } + if ((word2 & 0x04) != 0) { + grid[((j + 1) * size) + i + 2] = true; + } + if ((word2 & 0x02) != 0) { + grid[((j + 1) * size) + i + 3] = true; + } + if ((word2 & 0x01) != 0) { + grid[((j + 2) * size) + i] = true; + } + if ((word1 & 0x40) != 0) { + grid[((j + 2) * size) + i + 1] = true; + } + if ((word1 & 0x20) != 0) { + grid[((j + 2) * size) + i + 2] = true; + } + if ((word1 & 0x10) != 0) { + grid[((j + 2) * size) + i + 3] = true; + } + if ((word1 & 0x08) != 0) { + grid[((j + 3) * size) + i] = true; + } + if ((word1 & 0x04) != 0) { + grid[((j + 3) * size) + i + 1] = true; + } + if ((word1 & 0x02) != 0) { + grid[((j + 3) * size) + i + 2] = true; + } + if ((word1 & 0x01) != 0) { + grid[((j + 3) * size) + i + 3] = true; + } + } + + private void addLayerId(int size, int layers, int modules, int ecc_level) { + /* Place the layer ID into each macromodule */ + + int i, j, layer, start, stop; + int[] layerid = new int[layers + 1]; + int[] id = new int[modules * modules]; + + + /* Calculate Layer IDs */ + for (i = 0; i <= layers; i++) { + if (ecc_level == 1) { + layerid[i] = 3 - (i % 4); + } else { + layerid[i] = (i + 5 - ecc_level) % 4; + } + } + + for (i = 0; i < modules; i++) { + for (j = 0; j < modules; j++) { + id[(i * modules) + j] = 0; + } + } + + /* Calculate which value goes in each macromodule */ + start = modules / 2; + stop = modules / 2; + for (layer = 0; layer <= layers; layer++) { + for (i = start; i <= stop; i++) { + id[(start * modules) + i] = layerid[layer]; + id[(i * modules) + start] = layerid[layer]; + id[((modules - start - 1) * modules) + i] = layerid[layer]; + id[(i * modules) + (modules - start - 1)] = layerid[layer]; + } + start--; + stop++; + } + + /* Place the data in the grid */ + for (i = 0; i < modules; i++) { + for (j = 0; j < modules; j++) { + if ((id[(i * modules) + j] & 0x02) != 0) { + grid[(((i * 6) + 1) * size) + (j * 6) + 1] = true; + } + if ((id[(i * modules) + j] & 0x01) != 0) { + grid[(((i * 6) + 1) * size) + (j * 6) + 2] = true; + } + } + } + } +} diff --git a/src/main/java/uk/org/okapibarcode/backend/JapanPost.java b/src/main/java/uk/org/okapibarcode/backend/JapanPost.java index 6c60fb7e..24240291 100644 --- a/src/main/java/uk/org/okapibarcode/backend/JapanPost.java +++ b/src/main/java/uk/org/okapibarcode/backend/JapanPost.java @@ -1,143 +1,143 @@ -/* - * Copyright 2014 Robin Stuart - * - * 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. - */ - -package uk.org.okapibarcode.backend; - -import static uk.org.okapibarcode.util.Arrays.positionOf; - -import java.util.Locale; - -import uk.org.okapibarcode.graphics.Rectangle; - -/** - *

Implements the Japanese Postal Code symbology as used to encode address - * data for mail items in Japan. Valid input characters are digits 0-9, - * characters A-Z and the dash (-) character. A modulo-19 check digit is - * added and should not be included in the input data. - * - * @author Robin Stuart - */ -public class JapanPost extends Symbol { - - private static final String[] JAPAN_TABLE = { - "FFT", "FDA", "DFA", "FAD", "FTF", "DAF", "AFD", "ADF", "TFF", "FTT", - "TFT", "DAT", "DTA", "ADT", "TDA", "ATD", "TAD", "TTF", "FFF" - }; - - private static final char[] KASUT_SET = { - '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', 'a', 'b', 'c', - 'd', 'e', 'f', 'g', 'h' - }; - - private static final char[] CH_KASUT_SET = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', 'a', 'b', 'c', - 'd', 'e', 'f', 'g', 'h' - }; - - @Override - protected void encode() { - - content = content.toUpperCase(Locale.ENGLISH); - if (!content.matches("[0-9A-Z\\-]*")) { - throw OkapiInputException.invalidCharactersInInput(); - } - - StringBuilder inter = new StringBuilder(40); - for (int i = 0; i < content.length() && inter.length() < 20; i++) { - char c = content.charAt(i); - if ((c >= '0' && c <= '9') || c == '-') { - inter.append(c); - } else if (c >= 'A' && c <= 'J') { - inter.append('a'); - inter.append(CH_KASUT_SET[(c - 'A')]); - } else if (c >= 'K' && c <= 'O') { - inter.append('b'); - inter.append(CH_KASUT_SET[(c - 'K')]); - } else if (c >= 'U' && c <= 'Z') { - inter.append('c'); - inter.append(CH_KASUT_SET[(c - 'U')]); - } - } - for (int i = inter.length(); i < 20; i++) { - inter.append('d'); - } - - int sum = 0; - int destLen = 7 + (20 * 3); - StringBuilder dest = new StringBuilder(destLen); - dest.append("FD"); - for (int i = 0; i < 20; i++) { - dest.append(JAPAN_TABLE[positionOf(inter.charAt(i), KASUT_SET)]); - sum += positionOf(inter.charAt(i), CH_KASUT_SET); - } - - /* Calculate check digit */ - int check = 19 - (sum % 19); - if (check == 19) { - check = 0; - } - dest.append(JAPAN_TABLE[positionOf(CH_KASUT_SET[check], KASUT_SET)]); - dest.append("DF"); - - assert dest.length() == destLen; - - infoLine("Encoding: " + dest); - infoLine("Check Digit: " + check); - - readable = ""; - pattern = new String[] { dest.toString() }; - row_count = 1; - row_height = new int[] { -1 }; - } - - @Override - protected void plotSymbol() { - - int x = 0; - int w = 1; - int y = 0; - int h = 0; - - resetPlotElements(); - - for (int xBlock = 0; xBlock < pattern[0].length(); xBlock++) { - switch (pattern[0].charAt(xBlock)) { - case 'A': - y = 0; - h = 5; - break; - case 'D': - y = 3; - h = 5; - break; - case 'F': - y = 0; - h = 8; - break; - case 'T': - y = 3; - h = 2; - break; - } - Rectangle rect = new Rectangle(x, y, w, h); - rectangles.add(rect); - x += 2; - } - - symbol_width = pattern[0].length() * 2; - symbol_height = 8; - } -} +/* + * Copyright 2014 Robin Stuart + * + * 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. + */ + +package uk.org.okapibarcode.backend; + +import static uk.org.okapibarcode.util.Arrays.positionOf; + +import java.util.Locale; + +import uk.org.okapibarcode.graphics.Rectangle; + +/** + *

Implements the Japanese Postal Code symbology as used to encode address + * data for mail items in Japan. Valid input characters are digits 0-9, + * characters A-Z and the dash (-) character. A modulo-19 check digit is + * added and should not be included in the input data. + * + * @author Robin Stuart + */ +public class JapanPost extends Symbol { + + private static final String[] JAPAN_TABLE = { + "FFT", "FDA", "DFA", "FAD", "FTF", "DAF", "AFD", "ADF", "TFF", "FTT", + "TFT", "DAT", "DTA", "ADT", "TDA", "ATD", "TAD", "TTF", "FFF" + }; + + private static final char[] KASUT_SET = { + '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', 'a', 'b', 'c', + 'd', 'e', 'f', 'g', 'h' + }; + + private static final char[] CH_KASUT_SET = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', 'a', 'b', 'c', + 'd', 'e', 'f', 'g', 'h' + }; + + @Override + protected void encode() { + + content = content.toUpperCase(Locale.ENGLISH); + if (!content.matches("[0-9A-Z\\-]*")) { + throw OkapiInputException.invalidCharactersInInput(); + } + + StringBuilder inter = new StringBuilder(40); + for (int i = 0; i < content.length() && inter.length() < 20; i++) { + char c = content.charAt(i); + if ((c >= '0' && c <= '9') || c == '-') { + inter.append(c); + } else if (c >= 'A' && c <= 'J') { + inter.append('a'); + inter.append(CH_KASUT_SET[(c - 'A')]); + } else if (c >= 'K' && c <= 'O') { + inter.append('b'); + inter.append(CH_KASUT_SET[(c - 'K')]); + } else if (c >= 'U' && c <= 'Z') { + inter.append('c'); + inter.append(CH_KASUT_SET[(c - 'U')]); + } + } + for (int i = inter.length(); i < 20; i++) { + inter.append('d'); + } + + int sum = 0; + int destLen = 7 + (20 * 3); + StringBuilder dest = new StringBuilder(destLen); + dest.append("FD"); + for (int i = 0; i < 20; i++) { + dest.append(JAPAN_TABLE[positionOf(inter.charAt(i), KASUT_SET)]); + sum += positionOf(inter.charAt(i), CH_KASUT_SET); + } + + /* Calculate check digit */ + int check = 19 - (sum % 19); + if (check == 19) { + check = 0; + } + dest.append(JAPAN_TABLE[positionOf(CH_KASUT_SET[check], KASUT_SET)]); + dest.append("DF"); + + assert dest.length() == destLen; + + infoLine("Encoding: " + dest); + infoLine("Check Digit: " + check); + + readable = ""; + pattern = new String[] { dest.toString() }; + row_count = 1; + row_height = new int[] { -1 }; + } + + @Override + protected void plotSymbol() { + + int x = 0; + int w = 1; + int y = 0; + int h = 0; + + resetPlotElements(); + + for (int xBlock = 0; xBlock < pattern[0].length(); xBlock++) { + switch (pattern[0].charAt(xBlock)) { + case 'A': + y = 0; + h = 5; + break; + case 'D': + y = 3; + h = 5; + break; + case 'F': + y = 0; + h = 8; + break; + case 'T': + y = 3; + h = 2; + break; + } + Rectangle rect = new Rectangle(x, y, w, h); + rectangles.add(rect); + x += 2; + } + + symbol_width = pattern[0].length() * 2; + symbol_height = 8; + } +} diff --git a/src/main/java/uk/org/okapibarcode/backend/KixCode.java b/src/main/java/uk/org/okapibarcode/backend/KixCode.java index 48cd788f..e10e9c62 100644 --- a/src/main/java/uk/org/okapibarcode/backend/KixCode.java +++ b/src/main/java/uk/org/okapibarcode/backend/KixCode.java @@ -1,114 +1,114 @@ -/* - * Copyright 2014 Robin Stuart - * - * 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. - */ - -package uk.org.okapibarcode.backend; - -import static uk.org.okapibarcode.util.Arrays.positionOf; - -import java.util.Locale; - -import uk.org.okapibarcode.graphics.Rectangle; - -/** - *

Implements Dutch Post KIX Code as used by Royal Dutch TPG Post (Netherlands). - * - *

The input data can consist of digits 0-9 and characters A-Z, and should be 11 - * characters in length. No check digit is added. - * - *

KIX Code is the same as RM4SCC, but without the check digit. - * - * @author Robin Stuart - * @see KIX Code Specification - */ -public class KixCode extends Symbol { - - private static final String[] ROYAL_TABLE = { - "TTFF", "TDAF", "TDFA", "DTAF", "DTFA", "DDAA", "TADF", "TFTF", "TFDA", - "DATF", "DADA", "DFTA", "TAFD", "TFAD", "TFFT", "DAAD", "DAFT", "DFAT", - "ATDF", "ADTF", "ADDA", "FTTF", "FTDA", "FDTA", "ATFD", "ADAD", "ADFT", - "FTAD", "FTFT", "FDAT", "AADD", "AFTD", "AFDT", "FATD", "FADT", "FFTT" - }; - - private static final char[] KR_SET = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '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' - }; - - @Override - protected void encode() { - - content = content.toUpperCase(Locale.ENGLISH); - - if(!content.matches("[0-9A-Z]+")) { - throw OkapiInputException.invalidCharactersInInput(); - } - - StringBuilder sb = new StringBuilder(content.length()); - for (int i = 0; i < content.length(); i++) { - int j = positionOf(content.charAt(i), KR_SET); - sb.append(ROYAL_TABLE[j]); - } - - String dest = sb.toString(); - infoLine("Encoding: " + dest); - - readable = ""; - pattern = new String[] { dest }; - row_count = 1; - row_height = new int[] { -1 }; - } - - @Override - protected void plotSymbol() { - int xBlock; - int x, y, w, h; - - resetPlotElements(); - - x = 0; - w = 1; - y = 0; - h = 0; - for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) { - char c = pattern[0].charAt(xBlock); - switch (c) { - case 'A': - y = 0; - h = 5; - break; - case 'D': - y = 3; - h = 5; - break; - case 'F': - y = 0; - h = 8; - break; - case 'T': - y = 3; - h = 2; - break; - default: - throw new OkapiInternalException("Unknown pattern character: " + c); - } - rectangles.add(new Rectangle(x, y, w, h)); - x += 2; - } - symbol_width = ((pattern[0].length() - 1) * 2) + 1; // final bar doesn't need extra whitespace - symbol_height = 8; - } -} +/* + * Copyright 2014 Robin Stuart + * + * 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. + */ + +package uk.org.okapibarcode.backend; + +import static uk.org.okapibarcode.util.Arrays.positionOf; + +import java.util.Locale; + +import uk.org.okapibarcode.graphics.Rectangle; + +/** + *

Implements Dutch Post KIX Code as used by Royal Dutch TPG Post (Netherlands). + * + *

The input data can consist of digits 0-9 and characters A-Z, and should be 11 + * characters in length. No check digit is added. + * + *

KIX Code is the same as RM4SCC, but without the check digit. + * + * @author Robin Stuart + * @see KIX Code Specification + */ +public class KixCode extends Symbol { + + private static final String[] ROYAL_TABLE = { + "TTFF", "TDAF", "TDFA", "DTAF", "DTFA", "DDAA", "TADF", "TFTF", "TFDA", + "DATF", "DADA", "DFTA", "TAFD", "TFAD", "TFFT", "DAAD", "DAFT", "DFAT", + "ATDF", "ADTF", "ADDA", "FTTF", "FTDA", "FDTA", "ATFD", "ADAD", "ADFT", + "FTAD", "FTFT", "FDAT", "AADD", "AFTD", "AFDT", "FATD", "FADT", "FFTT" + }; + + private static final char[] KR_SET = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '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' + }; + + @Override + protected void encode() { + + content = content.toUpperCase(Locale.ENGLISH); + + if(!content.matches("[0-9A-Z]+")) { + throw OkapiInputException.invalidCharactersInInput(); + } + + StringBuilder sb = new StringBuilder(content.length()); + for (int i = 0; i < content.length(); i++) { + int j = positionOf(content.charAt(i), KR_SET); + sb.append(ROYAL_TABLE[j]); + } + + String dest = sb.toString(); + infoLine("Encoding: " + dest); + + readable = ""; + pattern = new String[] { dest }; + row_count = 1; + row_height = new int[] { -1 }; + } + + @Override + protected void plotSymbol() { + int xBlock; + int x, y, w, h; + + resetPlotElements(); + + x = 0; + w = 1; + y = 0; + h = 0; + for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) { + char c = pattern[0].charAt(xBlock); + switch (c) { + case 'A': + y = 0; + h = 5; + break; + case 'D': + y = 3; + h = 5; + break; + case 'F': + y = 0; + h = 8; + break; + case 'T': + y = 3; + h = 2; + break; + default: + throw new OkapiInternalException("Unknown pattern character: " + c); + } + rectangles.add(new Rectangle(x, y, w, h)); + x += 2; + } + symbol_width = ((pattern[0].length() - 1) * 2) + 1; // final bar doesn't need extra whitespace + symbol_height = 8; + } +} diff --git a/src/main/java/uk/org/okapibarcode/backend/KoreaPost.java b/src/main/java/uk/org/okapibarcode/backend/KoreaPost.java index 2a4741ed..432e2680 100644 --- a/src/main/java/uk/org/okapibarcode/backend/KoreaPost.java +++ b/src/main/java/uk/org/okapibarcode/backend/KoreaPost.java @@ -1,70 +1,70 @@ -/* - * Copyright 2014 Robin Stuart - * - * 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. - */ - -package uk.org.okapibarcode.backend; - -/** - *

Implements Korea Post Barcode. Input should consist of of a six-digit number. A Modulo-10 - * check digit is calculated and added, and should not form part of the input data. - * - * @author Robin Stuart - */ -public class KoreaPost extends Symbol { - - private static final String[] KOREA_TABLE = { - "1313150613", "0713131313", "0417131313", "1506131313", "0413171313", - "17171313", "1315061313", "0413131713", "17131713", "13171713" - }; - - @Override - protected void encode() { - - if (!content.matches("[0-9]+")) { - throw OkapiInputException.invalidCharactersInInput(); - } - - if (content.length() > 6) { - throw OkapiInputException.inputTooLong(); - } - - StringBuilder padded = new StringBuilder(6); - for (int i = content.length(); i < 6; i++) { - padded.append('0'); - } - padded.append(content); - - int total = 0; - String accumulator = ""; - for (int i = 0; i < padded.length(); i++) { - int j = Character.getNumericValue(padded.charAt(i)); - accumulator += KOREA_TABLE[j]; - total += j; - } - - int check = 10 - (total % 10); - if (check == 10) { - check = 0; - } - accumulator += KOREA_TABLE[check]; - infoLine("Check Digit: " + check); - - readable = padded.toString() + check; - pattern = new String[] { accumulator }; - row_count = 1; - row_height = new int[] { -1 }; - } - -} +/* + * Copyright 2014 Robin Stuart + * + * 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. + */ + +package uk.org.okapibarcode.backend; + +/** + *

Implements Korea Post Barcode. Input should consist of of a six-digit number. A Modulo-10 + * check digit is calculated and added, and should not form part of the input data. + * + * @author Robin Stuart + */ +public class KoreaPost extends Symbol { + + private static final String[] KOREA_TABLE = { + "1313150613", "0713131313", "0417131313", "1506131313", "0413171313", + "17171313", "1315061313", "0413131713", "17131713", "13171713" + }; + + @Override + protected void encode() { + + if (!content.matches("[0-9]+")) { + throw OkapiInputException.invalidCharactersInInput(); + } + + if (content.length() > 6) { + throw OkapiInputException.inputTooLong(); + } + + StringBuilder padded = new StringBuilder(6); + for (int i = content.length(); i < 6; i++) { + padded.append('0'); + } + padded.append(content); + + int total = 0; + String accumulator = ""; + for (int i = 0; i < padded.length(); i++) { + int j = Character.getNumericValue(padded.charAt(i)); + accumulator += KOREA_TABLE[j]; + total += j; + } + + int check = 10 - (total % 10); + if (check == 10) { + check = 0; + } + accumulator += KOREA_TABLE[check]; + infoLine("Check Digit: " + check); + + readable = padded.toString() + check; + pattern = new String[] { accumulator }; + row_count = 1; + row_height = new int[] { -1 }; + } + +} diff --git a/src/main/java/uk/org/okapibarcode/backend/Logmars.java b/src/main/java/uk/org/okapibarcode/backend/Logmars.java index a2b61df7..dffc7189 100644 --- a/src/main/java/uk/org/okapibarcode/backend/Logmars.java +++ b/src/main/java/uk/org/okapibarcode/backend/Logmars.java @@ -1,116 +1,116 @@ -/* - * Copyright 2014 Robin Stuart - * - * 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. - */ - -package uk.org.okapibarcode.backend; - -import static uk.org.okapibarcode.util.Arrays.positionOf; - -/** - * Implements the LOGMARS (Logistics Applications of Automated Marking - * and Reading Symbols) standard used by the US Department of Defense. - * Input data can be of any length and supports the characters 0-9, A-Z, dash - * (-), full stop (.), space, dollar ($), slash (/), plus (+) and percent (%). - * A Modulo-43 check digit is calculated and added, and should not form part - * of the input data. - * - * @author Robin Stuart - */ -public class Logmars extends Symbol { - - private static final String[] CODE39LM = { - "1113313111", "3113111131", "1133111131", "3133111111", "1113311131", - "3113311111", "1133311111", "1113113131", "3113113111", "1133113111", - "3111131131", "1131131131", "3131131111", "1111331131", "3111331111", - "1131331111", "1111133131", "3111133111", "1131133111", "1111333111", - "3111111331", "1131111331", "3131111311", "1111311331", "3111311311", - "1131311311", "1111113331", "3111113311", "1131113311", "1111313311", - "3311111131", "1331111131", "3331111111", "1311311131", "3311311111", - "1331311111", "1311113131", "3311113111", "1331113111", "1313131111", - "1313111311", "1311131311", "1113131311" - }; - - private static final char[] LOOKUP = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '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', '-', '.', ' ', '$', '/', '+', - '%' - }; - - /** Ratio of wide bar width to narrow bar width. */ - private double moduleWidthRatio = 3; - - /** - * Sets the ratio of wide bar width to narrow bar width. Valid values are usually - * between {@code 2} and {@code 3}. The default value is {@code 3}. - * - * @param moduleWidthRatio the ratio of wide bar width to narrow bar width - */ - public void setModuleWidthRatio(double moduleWidthRatio) { - this.moduleWidthRatio = moduleWidthRatio; - } - - /** - * Returns the ratio of wide bar width to narrow bar width. - * - * @return the ratio of wide bar width to narrow bar width - */ - public double getModuleWidthRatio() { - return moduleWidthRatio; - } - - /** {@inheritDoc} */ - @Override - protected double getModuleWidth(int originalWidth) { - if (originalWidth == 1) { - return 1; - } else { - return moduleWidthRatio; - } - } - - /** {@inheritDoc} */ - @Override - protected void encode() { - - if (!content.matches("[0-9A-Z\\. \\-$/+%]*")) { - throw OkapiInputException.invalidCharactersInInput(); - } - - int counter = 0; - int len = (content.length() * 10) + 29; - StringBuilder p = new StringBuilder(len); - p.append("1311313111"); - for (int i = 0; i < content.length(); i++) { - char c = content.charAt(i); - int val = positionOf(c, LOOKUP); - counter += val; - p.append(CODE39LM[val]); - } - - counter = counter % 43; - char checkDigit = LOOKUP[counter]; - infoLine("Check Digit: " + checkDigit); - p.append(CODE39LM[counter]); - p.append("131131311"); - - assert len == p.length(); - - readable = content + checkDigit; - pattern = new String[] { p.toString() }; - row_count = 1; - row_height = new int[] { -1 }; - } -} +/* + * Copyright 2014 Robin Stuart + * + * 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. + */ + +package uk.org.okapibarcode.backend; + +import static uk.org.okapibarcode.util.Arrays.positionOf; + +/** + * Implements the LOGMARS (Logistics Applications of Automated Marking + * and Reading Symbols) standard used by the US Department of Defense. + * Input data can be of any length and supports the characters 0-9, A-Z, dash + * (-), full stop (.), space, dollar ($), slash (/), plus (+) and percent (%). + * A Modulo-43 check digit is calculated and added, and should not form part + * of the input data. + * + * @author Robin Stuart + */ +public class Logmars extends Symbol { + + private static final String[] CODE39LM = { + "1113313111", "3113111131", "1133111131", "3133111111", "1113311131", + "3113311111", "1133311111", "1113113131", "3113113111", "1133113111", + "3111131131", "1131131131", "3131131111", "1111331131", "3111331111", + "1131331111", "1111133131", "3111133111", "1131133111", "1111333111", + "3111111331", "1131111331", "3131111311", "1111311331", "3111311311", + "1131311311", "1111113331", "3111113311", "1131113311", "1111313311", + "3311111131", "1331111131", "3331111111", "1311311131", "3311311111", + "1331311111", "1311113131", "3311113111", "1331113111", "1313131111", + "1313111311", "1311131311", "1113131311" + }; + + private static final char[] LOOKUP = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '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', '-', '.', ' ', '$', '/', '+', + '%' + }; + + /** Ratio of wide bar width to narrow bar width. */ + private double moduleWidthRatio = 3; + + /** + * Sets the ratio of wide bar width to narrow bar width. Valid values are usually + * between {@code 2} and {@code 3}. The default value is {@code 3}. + * + * @param moduleWidthRatio the ratio of wide bar width to narrow bar width + */ + public void setModuleWidthRatio(double moduleWidthRatio) { + this.moduleWidthRatio = moduleWidthRatio; + } + + /** + * Returns the ratio of wide bar width to narrow bar width. + * + * @return the ratio of wide bar width to narrow bar width + */ + public double getModuleWidthRatio() { + return moduleWidthRatio; + } + + /** {@inheritDoc} */ + @Override + protected double getModuleWidth(int originalWidth) { + if (originalWidth == 1) { + return 1; + } else { + return moduleWidthRatio; + } + } + + /** {@inheritDoc} */ + @Override + protected void encode() { + + if (!content.matches("[0-9A-Z\\. \\-$/+%]*")) { + throw OkapiInputException.invalidCharactersInInput(); + } + + int counter = 0; + int len = (content.length() * 10) + 29; + StringBuilder p = new StringBuilder(len); + p.append("1311313111"); + for (int i = 0; i < content.length(); i++) { + char c = content.charAt(i); + int val = positionOf(c, LOOKUP); + counter += val; + p.append(CODE39LM[val]); + } + + counter = counter % 43; + char checkDigit = LOOKUP[counter]; + infoLine("Check Digit: " + checkDigit); + p.append(CODE39LM[counter]); + p.append("131131311"); + + assert len == p.length(); + + readable = content + checkDigit; + pattern = new String[] { p.toString() }; + row_count = 1; + row_height = new int[] { -1 }; + } +} diff --git a/src/main/java/uk/org/okapibarcode/backend/MaxiCode.java b/src/main/java/uk/org/okapibarcode/backend/MaxiCode.java index 0b3811aa..4926192e 100644 --- a/src/main/java/uk/org/okapibarcode/backend/MaxiCode.java +++ b/src/main/java/uk/org/okapibarcode/backend/MaxiCode.java @@ -1,922 +1,922 @@ -/* - * Copyright 2014-2015 Robin Stuart, Daniel Gredler - * - * 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. - */ - -package uk.org.okapibarcode.backend; - -import static uk.org.okapibarcode.util.Arrays.contains; -import static uk.org.okapibarcode.util.Arrays.insertArray; - -import java.util.Arrays; - -import uk.org.okapibarcode.graphics.Circle; -import uk.org.okapibarcode.graphics.Hexagon; - -/** - *

- * Implements MaxiCode according to ISO 16023:2000. - * - *

- * MaxiCode employs a pattern of hexagons around a central 'bulls-eye' - * finder pattern. Encoding in several modes is supported, but encoding in - * Mode 2 and 3 require primary messages to be set. Input characters can be - * any from the ISO 8859-1 (Latin-1) character set. - * - *

- * TODO: Add ECI functionality. - * - * @author Robin Stuart - * @author Daniel Gredler - */ -public class MaxiCode extends Symbol { - - /** MaxiCode module sequence, from ISO/IEC 16023 Figure 5 (30 x 33 data grid). */ - private static final int[] MAXICODE_GRID = { - 122, 121, 128, 127, 134, 133, 140, 139, 146, 145, 152, 151, 158, 157, 164, 163, 170, 169, 176, 175, 182, 181, 188, 187, 194, 193, 200, 199, 0, 0, - 124, 123, 130, 129, 136, 135, 142, 141, 148, 147, 154, 153, 160, 159, 166, 165, 172, 171, 178, 177, 184, 183, 190, 189, 196, 195, 202, 201, 817, 0, - 126, 125, 132, 131, 138, 137, 144, 143, 150, 149, 156, 155, 162, 161, 168, 167, 174, 173, 180, 179, 186, 185, 192, 191, 198, 197, 204, 203, 819, 818, - 284, 283, 278, 277, 272, 271, 266, 265, 260, 259, 254, 253, 248, 247, 242, 241, 236, 235, 230, 229, 224, 223, 218, 217, 212, 211, 206, 205, 820, 0, - 286, 285, 280, 279, 274, 273, 268, 267, 262, 261, 256, 255, 250, 249, 244, 243, 238, 237, 232, 231, 226, 225, 220, 219, 214, 213, 208, 207, 822, 821, - 288, 287, 282, 281, 276, 275, 270, 269, 264, 263, 258, 257, 252, 251, 246, 245, 240, 239, 234, 233, 228, 227, 222, 221, 216, 215, 210, 209, 823, 0, - 290, 289, 296, 295, 302, 301, 308, 307, 314, 313, 320, 319, 326, 325, 332, 331, 338, 337, 344, 343, 350, 349, 356, 355, 362, 361, 368, 367, 825, 824, - 292, 291, 298, 297, 304, 303, 310, 309, 316, 315, 322, 321, 328, 327, 334, 333, 340, 339, 346, 345, 352, 351, 358, 357, 364, 363, 370, 369, 826, 0, - 294, 293, 300, 299, 306, 305, 312, 311, 318, 317, 324, 323, 330, 329, 336, 335, 342, 341, 348, 347, 354, 353, 360, 359, 366, 365, 372, 371, 828, 827, - 410, 409, 404, 403, 398, 397, 392, 391, 80, 79, 0, 0, 14, 13, 38, 37, 3, 0, 45, 44, 110, 109, 386, 385, 380, 379, 374, 373, 829, 0, - 412, 411, 406, 405, 400, 399, 394, 393, 82, 81, 41, 0, 16, 15, 40, 39, 4, 0, 0, 46, 112, 111, 388, 387, 382, 381, 376, 375, 831, 830, - 414, 413, 408, 407, 402, 401, 396, 395, 84, 83, 42, 0, 0, 0, 0, 0, 6, 5, 48, 47, 114, 113, 390, 389, 384, 383, 378, 377, 832, 0, - 416, 415, 422, 421, 428, 427, 104, 103, 56, 55, 17, 0, 0, 0, 0, 0, 0, 0, 21, 20, 86, 85, 434, 433, 440, 439, 446, 445, 834, 833, - 418, 417, 424, 423, 430, 429, 106, 105, 58, 57, 0, 0, 0, 0, 0, 0, 0, 0, 23, 22, 88, 87, 436, 435, 442, 441, 448, 447, 835, 0, - 420, 419, 426, 425, 432, 431, 108, 107, 60, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 90, 89, 438, 437, 444, 443, 450, 449, 837, 836, - 482, 481, 476, 475, 470, 469, 49, 0, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 54, 53, 464, 463, 458, 457, 452, 451, 838, 0, - 484, 483, 478, 477, 472, 471, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 466, 465, 460, 459, 454, 453, 840, 839, - 486, 485, 480, 479, 474, 473, 52, 51, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 43, 468, 467, 462, 461, 456, 455, 841, 0, - 488, 487, 494, 493, 500, 499, 98, 97, 62, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 92, 91, 506, 505, 512, 511, 518, 517, 843, 842, - 490, 489, 496, 495, 502, 501, 100, 99, 64, 63, 0, 0, 0, 0, 0, 0, 0, 0, 29, 28, 94, 93, 508, 507, 514, 513, 520, 519, 844, 0, - 492, 491, 498, 497, 504, 503, 102, 101, 66, 65, 18, 0, 0, 0, 0, 0, 0, 0, 19, 30, 96, 95, 510, 509, 516, 515, 522, 521, 846, 845, - 560, 559, 554, 553, 548, 547, 542, 541, 74, 73, 33, 0, 0, 0, 0, 0, 0, 11, 68, 67, 116, 115, 536, 535, 530, 529, 524, 523, 847, 0, - 562, 561, 556, 555, 550, 549, 544, 543, 76, 75, 0, 0, 8, 7, 36, 35, 12, 0, 70, 69, 118, 117, 538, 537, 532, 531, 526, 525, 849, 848, - 564, 563, 558, 557, 552, 551, 546, 545, 78, 77, 0, 34, 10, 9, 26, 25, 0, 0, 72, 71, 120, 119, 540, 539, 534, 533, 528, 527, 850, 0, - 566, 565, 572, 571, 578, 577, 584, 583, 590, 589, 596, 595, 602, 601, 608, 607, 614, 613, 620, 619, 626, 625, 632, 631, 638, 637, 644, 643, 852, 851, - 568, 567, 574, 573, 580, 579, 586, 585, 592, 591, 598, 597, 604, 603, 610, 609, 616, 615, 622, 621, 628, 627, 634, 633, 640, 639, 646, 645, 853, 0, - 570, 569, 576, 575, 582, 581, 588, 587, 594, 593, 600, 599, 606, 605, 612, 611, 618, 617, 624, 623, 630, 629, 636, 635, 642, 641, 648, 647, 855, 854, - 728, 727, 722, 721, 716, 715, 710, 709, 704, 703, 698, 697, 692, 691, 686, 685, 680, 679, 674, 673, 668, 667, 662, 661, 656, 655, 650, 649, 856, 0, - 730, 729, 724, 723, 718, 717, 712, 711, 706, 705, 700, 699, 694, 693, 688, 687, 682, 681, 676, 675, 670, 669, 664, 663, 658, 657, 652, 651, 858, 857, - 732, 731, 726, 725, 720, 719, 714, 713, 708, 707, 702, 701, 696, 695, 690, 689, 684, 683, 678, 677, 672, 671, 666, 665, 660, 659, 654, 653, 859, 0, - 734, 733, 740, 739, 746, 745, 752, 751, 758, 757, 764, 763, 770, 769, 776, 775, 782, 781, 788, 787, 794, 793, 800, 799, 806, 805, 812, 811, 861, 860, - 736, 735, 742, 741, 748, 747, 754, 753, 760, 759, 766, 765, 772, 771, 778, 777, 784, 783, 790, 789, 796, 795, 802, 801, 808, 807, 814, 813, 862, 0, - 738, 737, 744, 743, 750, 749, 756, 755, 762, 761, 768, 767, 774, 773, 780, 779, 786, 785, 792, 791, 798, 797, 804, 803, 810, 809, 816, 815, 864, 863 - }; - - /** - * ASCII character to Code Set mapping, from ISO/IEC 16023 Appendix A. - * 1 = Set A, 2 = Set B, 3 = Set C, 4 = Set D, 5 = Set E. - * 0 refers to special characters that fit into more than one set (e.g. GS). - */ - private static final int[] MAXICODE_SET = { - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 0, 2, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, - 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 4, 5, 5, 5, 5, 5, 5, 4, 5, 3, 4, 3, 5, 5, 4, 4, 3, 3, 3, - 4, 3, 5, 4, 4, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 - }; - - /** ASCII character to symbol value, from ISO/IEC 16023 Appendix A. */ - private static final int[] MAXICODE_SYMBOL_CHAR = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 30, 28, 29, 30, 35, 32, 53, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 37, - 38, 39, 40, 41, 52, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 42, 43, 44, 45, 46, 0, 1, 2, 3, - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 32, 54, 34, 35, 36, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 47, 48, - 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 36, - 37, 37, 38, 39, 40, 41, 42, 43, 38, 44, 37, 39, 38, 45, 46, 40, 41, 39, 40, 41, - 42, 42, 47, 43, 44, 43, 44, 45, 45, 46, 47, 46, 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 32, - 33, 34, 35, 36, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 32, 33, 34, 35, 36 - }; - - private int mode; - private int structuredAppendPosition = 1; - private int structuredAppendTotal = 1; - private String primaryData = ""; - private int[] codewords; - private int[] set = new int[144]; - private int[] character = new int[144]; - private boolean[][] grid = new boolean[33][30]; - - /** - * Sets the MaxiCode mode to use. Only modes 2 to 6 are supported. - * - * @param mode the MaxiCode mode to use - */ - public void setMode(int mode) { - if (mode < 2 || mode > 6) { - throw new IllegalArgumentException("Invalid MaxiCode mode: " + mode); - } - this.mode = mode; - } - - /** - * Returns the MaxiCode mode being used. Only modes 2 to 6 are supported. - * - * @return the MaxiCode mode being used - */ - public int getMode() { - return mode; - } - - /** - * If this MaxiCode symbol is part of a series of MaxiCode symbols appended in a structured format, this method sets the - * position of this symbol in the series. Valid values are 1 through 8 inclusive. - * - * @param position the position of this MaxiCode symbol in the structured append series - */ - public void setStructuredAppendPosition(int position) { - if (position < 1 || position > 8) { - throw new IllegalArgumentException("Invalid MaxiCode structured append position: " + position); - } - this.structuredAppendPosition = position; - } - - /** - * Returns the position of this MaxiCode symbol in a series of symbols using structured append. If this symbol is not part of - * such a series, this method will return 1. - * - * @return the position of this MaxiCode symbol in a series of symbols using structured append - */ - public int getStructuredAppendPosition() { - return structuredAppendPosition; - } - - /** - * If this MaxiCode symbol is part of a series of MaxiCode symbols appended in a structured format, this method sets the total - * number of symbols in the series. Valid values are 1 through 8 inclusive. A value of 1 indicates that this symbol is not - * part of a structured append series. - * - * @param total the total number of MaxiCode symbols in the structured append series - */ - public void setStructuredAppendTotal(int total) { - if (total < 1 || total > 8) { - throw new IllegalArgumentException("Invalid MaxiCode structured append total: " + total); - } - this.structuredAppendTotal = total; - } - - /** - * Returns the size of the series of MaxiCode symbols using structured append that this symbol is part of. If this symbol is - * not part of a structured append series, this method will return 1. - * - * @return size of the series that this symbol is part of - */ - public int getStructuredAppendTotal() { - return structuredAppendTotal; - } - - /** - * Sets the primary data. Should only be used for modes 2 and 3. Must conform to the following structure: - * - * - * - * - * - * - *
CharactersMeaning
1-9Postal code data which can consist of up to 9 digits (for mode 2) or up to 6 - * alphanumeric characters (for mode 3). Remaining unused characters should be - * filled with the SPACE character (ASCII 32).
10-12Three-digit country code according to ISO-3166.
13-15Three digit service code. This depends on your parcel courier.
- * - * @param primary the primary data - */ - public void setPrimary(String primary) { - primaryData = primary; - } - - /** - * Returns the primary data for this MaxiCode symbol. Should only be used for modes 2 and 3. - * - * @return the primary data for this MaxiCode symbol - */ - public String getPrimary() { - return primaryData; - } - - @Override - protected boolean eciSupported() { - return true; - } - - /** {@inheritDoc} */ - @Override - protected void encode() { - - eciProcess(); - - // mode 2 -> mode 3 if postal code isn't strictly numeric - if (mode == 2) { - for (int i = 0; i < 9 && i < primaryData.length(); i++) { - if (primaryData.charAt(i) < '0' || primaryData.charAt(i) > '9') { - mode = 3; - break; - } - } - } - - // initialize the set and character arrays - processText(); - - // start building the codeword array, starting with a copy of the character data - // insert primary message if this is a structured carrier message; insert mode otherwise - codewords = Arrays.copyOf(character, character.length); - if (mode == 2 || mode == 3) { - int[] primary = getPrimaryCodewords(); - codewords = insertArray(codewords, 0, primary); - } else { - codewords = insertArray(codewords, 0, new int[] { mode }); - } - - // insert structured append flag if necessary - if (structuredAppendTotal > 1) { - - int[] flag = new int[2]; - flag[0] = 33; // padding - flag[1] = ((structuredAppendPosition - 1) << 3) | (structuredAppendTotal - 1); // position + total - - int index; - if (mode == 2 || mode == 3) { - index = 10; // first two data symbols in the secondary message - } else { - index = 1; // first two data symbols in the primary message (first symbol at index 0 isn't a data symbol) - } - - codewords = insertArray(codewords, index, flag); - } - - int secondaryMax, secondaryECMax; - if (mode == 5) { - // 68 data codewords, 56 error corrections in secondary message - secondaryMax = 68; - secondaryECMax = 56; - } else { - // 84 data codewords, 40 error corrections in secondary message - secondaryMax = 84; - secondaryECMax = 40; - } - - // truncate data codewords to maximum data space available - int totalMax = secondaryMax + 10; - if (codewords.length > totalMax) { - codewords = Arrays.copyOfRange(codewords, 0, totalMax); - } - - // insert primary error correction between primary message and secondary message (always EEC) - int[] primary = Arrays.copyOfRange(codewords, 0, 10); - int[] primaryCheck = getErrorCorrection(primary, 10); - codewords = insertArray(codewords, 10, primaryCheck); - - // calculate secondary error correction - int[] secondary = Arrays.copyOfRange(codewords, 20, codewords.length); - int[] secondaryOdd = new int[secondary.length / 2]; - int[] secondaryEven = new int[secondary.length / 2]; - for (int i = 0; i < secondary.length; i++) { - if ((i & 1) != 0) { // odd - secondaryOdd[(i - 1) / 2] = secondary[i]; - } else { // even - secondaryEven[i / 2] = secondary[i]; - } - } - int[] secondaryECOdd = getErrorCorrection(secondaryOdd, secondaryECMax / 2); - int[] secondaryECEven = getErrorCorrection(secondaryEven, secondaryECMax / 2); - - // add secondary error correction after secondary message - codewords = Arrays.copyOf(codewords, codewords.length + secondaryECOdd.length + secondaryECEven.length); - for (int i = 0; i < secondaryECOdd.length; i++) { - codewords[20 + secondaryMax + (2 * i) + 1] = secondaryECOdd[i]; - } - for (int i = 0; i < secondaryECEven.length; i++) { - codewords[20 + secondaryMax + (2 * i)] = secondaryECEven[i]; - } - - infoLine("Mode: " + mode); - infoLine("ECC Codewords: " + secondaryECMax); - info("Codewords: "); - for (int i = 0; i < codewords.length; i++) { - infoSpace(codewords[i]); - } - infoLine(); - - // copy data into symbol grid - int[] bit_pattern = new int[7]; - for (int i = 0; i < 33; i++) { - for (int j = 0; j < 30; j++) { - - int block = (MAXICODE_GRID[(i * 30) + j] + 5) / 6; - int bit = (MAXICODE_GRID[(i * 30) + j] + 5) % 6; - - if (block != 0) { - - bit_pattern[0] = (codewords[block - 1] & 0x20) >> 5; - bit_pattern[1] = (codewords[block - 1] & 0x10) >> 4; - bit_pattern[2] = (codewords[block - 1] & 0x8) >> 3; - bit_pattern[3] = (codewords[block - 1] & 0x4) >> 2; - bit_pattern[4] = (codewords[block - 1] & 0x2) >> 1; - bit_pattern[5] = (codewords[block - 1] & 0x1); - - if (bit_pattern[bit] != 0) { - grid[i][j] = true; - } else { - grid[i][j] = false; - } - } - } - } - - // add orientation markings - grid[0][28] = true; // top right filler - grid[0][29] = true; - grid[9][10] = true; // top left marker - grid[9][11] = true; - grid[10][11] = true; - grid[15][7] = true; // left hand marker - grid[16][8] = true; - grid[16][20] = true; // right hand marker - grid[17][20] = true; - grid[22][10] = true; // bottom left marker - grid[23][10] = true; - grid[22][17] = true; // bottom right marker - grid[23][17] = true; - - // the following is provided for compatibility, but the results are not useful - row_count = 33; - readable = ""; - pattern = new String[33]; - row_height = new int[33]; - for (int i = 0; i < 33; i++) { - StringBuilder bin = new StringBuilder(30); - for (int j = 0; j < 30; j++) { - if (grid[i][j]) { - bin.append("1"); - } else { - bin.append("0"); - } - } - pattern[i] = bin2pat(bin); - row_height[i] = 1; - } - } - - /** - * Extracts the postal code, country code and service code from the primary data and returns the corresponding primary message - * codewords. - * - * @return the primary message codewords - */ - private int[] getPrimaryCodewords() { - - assert mode == 2 || mode == 3; - - if (primaryData.length() != 15) { - throw new OkapiInputException("Invalid primary string"); - } - - for (int i = 9; i < 15; i++) { /* check that country code and service are numeric */ - if (primaryData.charAt(i) < '0' || primaryData.charAt(i) > '9') { - throw new OkapiInputException("Invalid primary string"); - } - } - - String postcode; - if (mode == 2) { - postcode = primaryData.substring(0, 9); - int index = postcode.indexOf(' '); - if (index != -1) { - postcode = postcode.substring(0, index); - } - } else { - assert mode == 3; - postcode = primaryData.substring(0, 6); - } - - int country = Integer.parseInt(primaryData.substring(9, 12)); - int service = Integer.parseInt(primaryData.substring(12, 15)); - - infoLine("Postal Code: " + postcode); - infoLine("Country Code: " + country); - infoLine("Service: " + service); - - if (mode == 2) { - return getMode2PrimaryCodewords(postcode, country, service); - } else { - assert mode == 3; - return getMode3PrimaryCodewords(postcode, country, service); - } - } - - /** - * Returns the primary message codewords for mode 2. - * - * @param postcode the postal code - * @param country the country code - * @param service the service code - * @return the primary message, as codewords - */ - private static int[] getMode2PrimaryCodewords(String postcode, int country, int service) { - - for (int i = 0; i < postcode.length(); i++) { - if (postcode.charAt(i) < '0' || postcode.charAt(i) > '9') { - postcode = postcode.substring(0, i); - break; - } - } - - int postcodeNum = Integer.parseInt(postcode); - - int[] primary = new int[10]; - primary[0] = ((postcodeNum & 0x03) << 4) | 2; - primary[1] = ((postcodeNum & 0xfc) >> 2); - primary[2] = ((postcodeNum & 0x3f00) >> 8); - primary[3] = ((postcodeNum & 0xfc000) >> 14); - primary[4] = ((postcodeNum & 0x3f00000) >> 20); - primary[5] = ((postcodeNum & 0x3c000000) >> 26) | ((postcode.length() & 0x3) << 4); - primary[6] = ((postcode.length() & 0x3c) >> 2) | ((country & 0x3) << 4); - primary[7] = (country & 0xfc) >> 2; - primary[8] = ((country & 0x300) >> 8) | ((service & 0xf) << 2); - primary[9] = ((service & 0x3f0) >> 4); - - return primary; - } - - /** - * Returns the primary message codewords for mode 3. - * - * @param postcode the postal code - * @param country the country code - * @param service the service code - * @return the primary message, as codewords - */ - private static int[] getMode3PrimaryCodewords(String postcode, int country, int service) { - - int[] postcodeNums = new int[postcode.length()]; - - postcode = postcode.toUpperCase(); - for (int i = 0; i < postcodeNums.length; i++) { - postcodeNums[i] = postcode.charAt(i); - if (postcode.charAt(i) >= 'A' && postcode.charAt(i) <= 'Z') { - // (Capital) letters shifted to Code Set A values - postcodeNums[i] -= 64; - } - if (postcodeNums[i] == 27 || postcodeNums[i] == 31 || postcodeNums[i] == 33 || postcodeNums[i] >= 59) { - // Not a valid postal code character, use space instead - postcodeNums[i] = 32; - } - // Input characters lower than 27 (NUL - SUB) in postal code are interpreted as capital - // letters in Code Set A (e.g. LF becomes 'J') - } - - int[] primary = new int[10]; - primary[0] = ((postcodeNums[5] & 0x03) << 4) | 3; - primary[1] = ((postcodeNums[4] & 0x03) << 4) | ((postcodeNums[5] & 0x3c) >> 2); - primary[2] = ((postcodeNums[3] & 0x03) << 4) | ((postcodeNums[4] & 0x3c) >> 2); - primary[3] = ((postcodeNums[2] & 0x03) << 4) | ((postcodeNums[3] & 0x3c) >> 2); - primary[4] = ((postcodeNums[1] & 0x03) << 4) | ((postcodeNums[2] & 0x3c) >> 2); - primary[5] = ((postcodeNums[0] & 0x03) << 4) | ((postcodeNums[1] & 0x3c) >> 2); - primary[6] = ((postcodeNums[0] & 0x3c) >> 2) | ((country & 0x3) << 4); - primary[7] = (country & 0xfc) >> 2; - primary[8] = ((country & 0x300) >> 8) | ((service & 0xf) << 2); - primary[9] = ((service & 0x3f0) >> 4); - - return primary; - } - - /** - * Formats text according to Appendix A, populating the {@link #set} and {@link #character} arrays. - * - * @return true if the content fits in this symbol and was formatted; false otherwise - */ - private void processText() { - - int length = content.length(); - int i, j, count, current_set; - - if (length > 138) { - throw OkapiInputException.inputTooLong(); - } - - for (i = 0; i < 144; i++) { - set[i] = -1; - character[i] = 0; - } - - for (i = 0; i < length; i++) { - /* Look up characters in table from Appendix A - this gives - value and code set for most characters */ - set[i] = MAXICODE_SET[inputData[i]]; - character[i] = MAXICODE_SYMBOL_CHAR[inputData[i]]; - } - - // If a character can be represented in more than one code set, pick which version to use. - if (set[0] == 0) { - if (character[0] == 13) { - character[0] = 0; - } - set[0] = 1; - } - - for (i = 1; i < length; i++) { - if (set[i] == 0) { - /* Special character that can be represented in more than one code set. */ - if (character[i] == 13) { - /* Carriage Return */ - set[i] = bestSurroundingSet(i, length, 1, 5); - if (set[i] == 5) { - character[i] = 13; - } else { - character[i] = 0; - } - } else if (character[i] == 28) { - /* FS */ - set[i] = bestSurroundingSet(i, length, 1, 2, 3, 4, 5); - if (set[i] == 5) { - character[i] = 32; - } - } else if (character[i] == 29) { - /* GS */ - set[i] = bestSurroundingSet(i, length, 1, 2, 3, 4, 5); - if (set[i] == 5) { - character[i] = 33; - } - } else if (character[i] == 30) { - /* RS */ - set[i] = bestSurroundingSet(i, length, 1, 2, 3, 4, 5); - if (set[i] == 5) { - character[i] = 34; - } - } else if (character[i] == 32) { - /* Space */ - set[i] = bestSurroundingSet(i, length, 1, 2, 3, 4, 5); - if (set[i] == 1) { - character[i] = 32; - } else if (set[i] == 2) { - character[i] = 47; - } else { - character[i] = 59; - } - } else if (character[i] == 44) { - /* Comma */ - set[i] = bestSurroundingSet(i, length, 1, 2); - if (set[i] == 2) { - character[i] = 48; - } - } else if (character[i] == 46) { - /* Full Stop */ - set[i] = bestSurroundingSet(i, length, 1, 2); - if (set[i] == 2) { - character[i] = 49; - } - } else if (character[i] == 47) { - /* Slash */ - set[i] = bestSurroundingSet(i, length, 1, 2); - if (set[i] == 2) { - character[i] = 50; - } - } else if (character[i] == 58) { - /* Colon */ - set[i] = bestSurroundingSet(i, length, 1, 2); - if (set[i] == 2) { - character[i] = 51; - } - } - } - } - - for (i = length; i < set.length; i++) { - /* Add the padding */ - if (set[length - 1] == 2) { - set[i] = 2; - } else { - set[i] = 1; - } - character[i] = 33; - } - - /* Find candidates for number compression (not allowed in primary message in modes 2 and 3). */ - if (mode == 2 || mode == 3) { - j = 9; - } else { - j = 0; - } - count = 0; - for (i = j; i < 143; i++) { - if (set[i] == 1 && character[i] >= 48 && character[i] <= 57) { - /* Character is a number */ - count++; - } else { - count = 0; - } - if (count == 9) { - /* Nine digits in a row can be compressed */ - set[i] = 6; - set[i - 1] = 6; - set[i - 2] = 6; - set[i - 3] = 6; - set[i - 4] = 6; - set[i - 5] = 6; - set[i - 6] = 6; - set[i - 7] = 6; - set[i - 8] = 6; - count = 0; - } - } - - /* Add shift and latch characters */ - current_set = 1; - i = 0; - do { - if ((set[i] != current_set) && (set[i] != 6)) { - switch (set[i]) { - case 1: - if (i + 1 < set.length && set[i + 1] == 1) { - if (i + 2 < set.length && set[i + 2] == 1) { - if (i + 3 < set.length && set[i + 3] == 1) { - /* Latch A */ - insert(i, 63); - current_set = 1; - length++; - i += 3; - } else { - /* 3 Shift A */ - insert(i, 57); - length++; - i += 2; - } - } else { - /* 2 Shift A */ - insert(i, 56); - length++; - i++; - } - } else { - /* Shift A */ - insert(i, 59); - length++; - } - break; - case 2: - if (i + 1 < set.length && set[i + 1] == 2) { - /* Latch B */ - insert(i, 63); - current_set = 2; - length++; - i++; - } else { - /* Shift B */ - insert(i, 59); - length++; - } - break; - case 3: - if (i + 3 < set.length && set[i + 1] == 3 && set[i + 2] == 3 && set[i + 3] == 3) { - /* Lock In C */ - insert(i, 60); - insert(i, 60); - current_set = 3; - length++; - i += 3; - } else { - /* Shift C */ - insert(i, 60); - length++; - } - break; - case 4: - if (i + 3 < set.length && set[i + 1] == 4 && set[i + 2] == 4 && set[i + 3] == 4) { - /* Lock In D */ - insert(i, 61); - insert(i, 61); - current_set = 4; - length++; - i += 3; - } else { - /* Shift D */ - insert(i, 61); - length++; - } - break; - case 5: - if (i + 3 < set.length && set[i + 1] == 5 && set[i + 2] == 5 && set[i + 3] == 5) { - /* Lock In E */ - insert(i, 62); - insert(i, 62); - current_set = 5; - length++; - i += 3; - } else { - /* Shift E */ - insert(i, 62); - length++; - } - break; - default: - throw new OkapiInternalException("Unexpected set " + set[i] + " at index " + i + "."); - } - i++; - } - i++; - } while (i < set.length); - - /* Number compression has not been forgotten! It's handled below. */ - i = 0; - do { - if (set[i] == 6) { - /* Number compression */ - int value = 0; - for (j = 0; j < 9; j++) { - value *= 10; - value += (character[i + j] - '0'); - } - character[i] = 31; /* NS */ - character[i + 1] = (value & 0x3f000000) >> 24; - character[i + 2] = (value & 0xfc0000) >> 18; - character[i + 3] = (value & 0x3f000) >> 12; - character[i + 4] = (value & 0xfc0) >> 6; - character[i + 5] = (value & 0x3f); - i += 6; - for (j = i; j < 140; j++) { - set[j] = set[j + 3]; - character[j] = character[j + 3]; - } - length -= 3; - } else { - i++; - } - } while (i < set.length); - - /* Inject ECI codes to beginning of data, according to Table 3 */ - if (eciMode != 3) { - insert(0, 27); // ECI - if (eciMode >= 0 && eciMode <= 31) { - insert(1, eciMode & 0x1F); - length += 2; - } - if (eciMode >= 32 && eciMode <= 1023) { - insert(1, 0x20 + (eciMode >> 6)); - insert(2, eciMode & 0x3F); - length += 3; - } - if (eciMode >= 1024 && eciMode <= 32767) { - insert(1, 0x30 + (eciMode >> 12)); - insert(2, (eciMode >> 6) & 0x3F); - insert(3, eciMode & 0x3F); - length += 4; - } - if (eciMode >= 32768 && eciMode <= 999999) { - insert(1, 0x38 + (eciMode >> 18)); - insert(2, (eciMode >> 12) & 0x3F); - insert(3, (eciMode >> 6) & 0x3F); - insert(4, eciMode & 0x3F); - length += 5; - } - } - - /* Make sure we haven't exceeded the maximum data length. */ - int maxLength; - if (mode == 2 || mode == 3) { - maxLength = 84; - } else if (mode == 4 || mode == 6) { - maxLength = 93; - } else if (mode == 5) { - maxLength = 77; - } else { - maxLength = 0; // impossible - } - if (length > maxLength) { - throw OkapiInputException.inputTooLong(); - } - } - - /** - * Guesses the best set to use at the specified index by looking at the surrounding sets. In general, characters in - * lower-numbered sets are more common, so we choose them if we can. If no good surrounding sets can be found, the default - * value returned is the first value from the valid set. - * - * @param index the current index - * @param length the maximum length to look at - * @param valid the valid sets for this index - * @return the best set to use at the specified index - */ - private int bestSurroundingSet(int index, int length, int... valid) { - int option1 = set[index - 1]; - if (index + 1 < length) { - // we have two options to check - int option2 = set[index + 1]; - if (contains(valid, option1) && contains(valid, option2)) { - return Math.min(option1, option2); - } else if (contains(valid, option1)) { - return option1; - } else if (contains(valid, option2)) { - return option2; - } else { - return valid[0]; - } - } else { - // we only have one option to check - if (contains(valid, option1)) { - return option1; - } else { - return valid[0]; - } - } - } - - /** - * Moves everything up so that the specified shift or latch character can be inserted. - * - * @param position the position beyond which everything needs to be shifted - * @param c the latch or shift character to insert at the specified position, after everything has been shifted - */ - private void insert(int position, int c) { - for (int i = 143; i > position; i--) { - set[i] = set[i - 1]; - character[i] = character[i - 1]; - } - character[position] = c; - } - - /** - * Returns the error correction codewords for the specified data codewords. - * - * @param codewords the codewords that we need error correction codewords for - * @param ecclen the number of error correction codewords needed - * @return the error correction codewords for the specified data codewords - */ - private static int[] getErrorCorrection(int[] codewords, int ecclen) { - - ReedSolomon rs = new ReedSolomon(); - rs.init_gf(0x43); - rs.init_code(ecclen, 1); - rs.encode(codewords.length, codewords); - - int[] results = new int[ecclen]; - for (int i = 0; i < ecclen; i++) { - results[i] = rs.getResult(results.length - 1 - i); - } - - return results; - } - - /** {@inheritDoc} */ - @Override - protected void plotSymbol() { - - resetPlotElements(); - - symbol_height = 72; - symbol_width = 74; - - // hexagons - for (int row = 0; row < 33; row++) { - for (int col = 0; col < 30; col++) { - if (grid[row][col]) { - double x = (2.46 * col) + 1.23; - if ((row & 1) != 0) { - x += 1.23; - } - double y = (2.135 * row) + 1.43; - hexagons.add(new Hexagon(x, y)); - } - } - } - - // circles - double[] radii = { 10.85, 8.97, 7.10, 5.22, 3.31, 1.43 }; - for (int i = 0; i < radii.length; i++) { - target.add(new Circle(35.76, 35.60, radii[i])); - } - } - - /** {@inheritDoc} */ - @Override - protected int[] getCodewords() { - return codewords; - } -} +/* + * Copyright 2014-2015 Robin Stuart, Daniel Gredler + * + * 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. + */ + +package uk.org.okapibarcode.backend; + +import static uk.org.okapibarcode.util.Arrays.contains; +import static uk.org.okapibarcode.util.Arrays.insertArray; + +import java.util.Arrays; + +import uk.org.okapibarcode.graphics.Circle; +import uk.org.okapibarcode.graphics.Hexagon; + +/** + *

+ * Implements MaxiCode according to ISO 16023:2000. + * + *

+ * MaxiCode employs a pattern of hexagons around a central 'bulls-eye' + * finder pattern. Encoding in several modes is supported, but encoding in + * Mode 2 and 3 require primary messages to be set. Input characters can be + * any from the ISO 8859-1 (Latin-1) character set. + * + *

+ * TODO: Add ECI functionality. + * + * @author Robin Stuart + * @author Daniel Gredler + */ +public class MaxiCode extends Symbol { + + /** MaxiCode module sequence, from ISO/IEC 16023 Figure 5 (30 x 33 data grid). */ + private static final int[] MAXICODE_GRID = { + 122, 121, 128, 127, 134, 133, 140, 139, 146, 145, 152, 151, 158, 157, 164, 163, 170, 169, 176, 175, 182, 181, 188, 187, 194, 193, 200, 199, 0, 0, + 124, 123, 130, 129, 136, 135, 142, 141, 148, 147, 154, 153, 160, 159, 166, 165, 172, 171, 178, 177, 184, 183, 190, 189, 196, 195, 202, 201, 817, 0, + 126, 125, 132, 131, 138, 137, 144, 143, 150, 149, 156, 155, 162, 161, 168, 167, 174, 173, 180, 179, 186, 185, 192, 191, 198, 197, 204, 203, 819, 818, + 284, 283, 278, 277, 272, 271, 266, 265, 260, 259, 254, 253, 248, 247, 242, 241, 236, 235, 230, 229, 224, 223, 218, 217, 212, 211, 206, 205, 820, 0, + 286, 285, 280, 279, 274, 273, 268, 267, 262, 261, 256, 255, 250, 249, 244, 243, 238, 237, 232, 231, 226, 225, 220, 219, 214, 213, 208, 207, 822, 821, + 288, 287, 282, 281, 276, 275, 270, 269, 264, 263, 258, 257, 252, 251, 246, 245, 240, 239, 234, 233, 228, 227, 222, 221, 216, 215, 210, 209, 823, 0, + 290, 289, 296, 295, 302, 301, 308, 307, 314, 313, 320, 319, 326, 325, 332, 331, 338, 337, 344, 343, 350, 349, 356, 355, 362, 361, 368, 367, 825, 824, + 292, 291, 298, 297, 304, 303, 310, 309, 316, 315, 322, 321, 328, 327, 334, 333, 340, 339, 346, 345, 352, 351, 358, 357, 364, 363, 370, 369, 826, 0, + 294, 293, 300, 299, 306, 305, 312, 311, 318, 317, 324, 323, 330, 329, 336, 335, 342, 341, 348, 347, 354, 353, 360, 359, 366, 365, 372, 371, 828, 827, + 410, 409, 404, 403, 398, 397, 392, 391, 80, 79, 0, 0, 14, 13, 38, 37, 3, 0, 45, 44, 110, 109, 386, 385, 380, 379, 374, 373, 829, 0, + 412, 411, 406, 405, 400, 399, 394, 393, 82, 81, 41, 0, 16, 15, 40, 39, 4, 0, 0, 46, 112, 111, 388, 387, 382, 381, 376, 375, 831, 830, + 414, 413, 408, 407, 402, 401, 396, 395, 84, 83, 42, 0, 0, 0, 0, 0, 6, 5, 48, 47, 114, 113, 390, 389, 384, 383, 378, 377, 832, 0, + 416, 415, 422, 421, 428, 427, 104, 103, 56, 55, 17, 0, 0, 0, 0, 0, 0, 0, 21, 20, 86, 85, 434, 433, 440, 439, 446, 445, 834, 833, + 418, 417, 424, 423, 430, 429, 106, 105, 58, 57, 0, 0, 0, 0, 0, 0, 0, 0, 23, 22, 88, 87, 436, 435, 442, 441, 448, 447, 835, 0, + 420, 419, 426, 425, 432, 431, 108, 107, 60, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 90, 89, 438, 437, 444, 443, 450, 449, 837, 836, + 482, 481, 476, 475, 470, 469, 49, 0, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 54, 53, 464, 463, 458, 457, 452, 451, 838, 0, + 484, 483, 478, 477, 472, 471, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 466, 465, 460, 459, 454, 453, 840, 839, + 486, 485, 480, 479, 474, 473, 52, 51, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 43, 468, 467, 462, 461, 456, 455, 841, 0, + 488, 487, 494, 493, 500, 499, 98, 97, 62, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 92, 91, 506, 505, 512, 511, 518, 517, 843, 842, + 490, 489, 496, 495, 502, 501, 100, 99, 64, 63, 0, 0, 0, 0, 0, 0, 0, 0, 29, 28, 94, 93, 508, 507, 514, 513, 520, 519, 844, 0, + 492, 491, 498, 497, 504, 503, 102, 101, 66, 65, 18, 0, 0, 0, 0, 0, 0, 0, 19, 30, 96, 95, 510, 509, 516, 515, 522, 521, 846, 845, + 560, 559, 554, 553, 548, 547, 542, 541, 74, 73, 33, 0, 0, 0, 0, 0, 0, 11, 68, 67, 116, 115, 536, 535, 530, 529, 524, 523, 847, 0, + 562, 561, 556, 555, 550, 549, 544, 543, 76, 75, 0, 0, 8, 7, 36, 35, 12, 0, 70, 69, 118, 117, 538, 537, 532, 531, 526, 525, 849, 848, + 564, 563, 558, 557, 552, 551, 546, 545, 78, 77, 0, 34, 10, 9, 26, 25, 0, 0, 72, 71, 120, 119, 540, 539, 534, 533, 528, 527, 850, 0, + 566, 565, 572, 571, 578, 577, 584, 583, 590, 589, 596, 595, 602, 601, 608, 607, 614, 613, 620, 619, 626, 625, 632, 631, 638, 637, 644, 643, 852, 851, + 568, 567, 574, 573, 580, 579, 586, 585, 592, 591, 598, 597, 604, 603, 610, 609, 616, 615, 622, 621, 628, 627, 634, 633, 640, 639, 646, 645, 853, 0, + 570, 569, 576, 575, 582, 581, 588, 587, 594, 593, 600, 599, 606, 605, 612, 611, 618, 617, 624, 623, 630, 629, 636, 635, 642, 641, 648, 647, 855, 854, + 728, 727, 722, 721, 716, 715, 710, 709, 704, 703, 698, 697, 692, 691, 686, 685, 680, 679, 674, 673, 668, 667, 662, 661, 656, 655, 650, 649, 856, 0, + 730, 729, 724, 723, 718, 717, 712, 711, 706, 705, 700, 699, 694, 693, 688, 687, 682, 681, 676, 675, 670, 669, 664, 663, 658, 657, 652, 651, 858, 857, + 732, 731, 726, 725, 720, 719, 714, 713, 708, 707, 702, 701, 696, 695, 690, 689, 684, 683, 678, 677, 672, 671, 666, 665, 660, 659, 654, 653, 859, 0, + 734, 733, 740, 739, 746, 745, 752, 751, 758, 757, 764, 763, 770, 769, 776, 775, 782, 781, 788, 787, 794, 793, 800, 799, 806, 805, 812, 811, 861, 860, + 736, 735, 742, 741, 748, 747, 754, 753, 760, 759, 766, 765, 772, 771, 778, 777, 784, 783, 790, 789, 796, 795, 802, 801, 808, 807, 814, 813, 862, 0, + 738, 737, 744, 743, 750, 749, 756, 755, 762, 761, 768, 767, 774, 773, 780, 779, 786, 785, 792, 791, 798, 797, 804, 803, 810, 809, 816, 815, 864, 863 + }; + + /** + * ASCII character to Code Set mapping, from ISO/IEC 16023 Appendix A. + * 1 = Set A, 2 = Set B, 3 = Set C, 4 = Set D, 5 = Set E. + * 0 refers to special characters that fit into more than one set (e.g. GS). + */ + private static final int[] MAXICODE_SET = { + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 0, 2, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, + 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 4, 5, 5, 5, 5, 5, 5, 4, 5, 3, 4, 3, 5, 5, 4, 4, 3, 3, 3, + 4, 3, 5, 4, 4, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 + }; + + /** ASCII character to symbol value, from ISO/IEC 16023 Appendix A. */ + private static final int[] MAXICODE_SYMBOL_CHAR = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 30, 28, 29, 30, 35, 32, 53, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 37, + 38, 39, 40, 41, 52, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 42, 43, 44, 45, 46, 0, 1, 2, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 32, 54, 34, 35, 36, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 36, + 37, 37, 38, 39, 40, 41, 42, 43, 38, 44, 37, 39, 38, 45, 46, 40, 41, 39, 40, 41, + 42, 42, 47, 43, 44, 43, 44, 45, 45, 46, 47, 46, 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 32, + 33, 34, 35, 36, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 32, 33, 34, 35, 36 + }; + + private int mode; + private int structuredAppendPosition = 1; + private int structuredAppendTotal = 1; + private String primaryData = ""; + private int[] codewords; + private int[] set = new int[144]; + private int[] character = new int[144]; + private boolean[][] grid = new boolean[33][30]; + + /** + * Sets the MaxiCode mode to use. Only modes 2 to 6 are supported. + * + * @param mode the MaxiCode mode to use + */ + public void setMode(int mode) { + if (mode < 2 || mode > 6) { + throw new IllegalArgumentException("Invalid MaxiCode mode: " + mode); + } + this.mode = mode; + } + + /** + * Returns the MaxiCode mode being used. Only modes 2 to 6 are supported. + * + * @return the MaxiCode mode being used + */ + public int getMode() { + return mode; + } + + /** + * If this MaxiCode symbol is part of a series of MaxiCode symbols appended in a structured format, this method sets the + * position of this symbol in the series. Valid values are 1 through 8 inclusive. + * + * @param position the position of this MaxiCode symbol in the structured append series + */ + public void setStructuredAppendPosition(int position) { + if (position < 1 || position > 8) { + throw new IllegalArgumentException("Invalid MaxiCode structured append position: " + position); + } + this.structuredAppendPosition = position; + } + + /** + * Returns the position of this MaxiCode symbol in a series of symbols using structured append. If this symbol is not part of + * such a series, this method will return 1. + * + * @return the position of this MaxiCode symbol in a series of symbols using structured append + */ + public int getStructuredAppendPosition() { + return structuredAppendPosition; + } + + /** + * If this MaxiCode symbol is part of a series of MaxiCode symbols appended in a structured format, this method sets the total + * number of symbols in the series. Valid values are 1 through 8 inclusive. A value of 1 indicates that this symbol is not + * part of a structured append series. + * + * @param total the total number of MaxiCode symbols in the structured append series + */ + public void setStructuredAppendTotal(int total) { + if (total < 1 || total > 8) { + throw new IllegalArgumentException("Invalid MaxiCode structured append total: " + total); + } + this.structuredAppendTotal = total; + } + + /** + * Returns the size of the series of MaxiCode symbols using structured append that this symbol is part of. If this symbol is + * not part of a structured append series, this method will return 1. + * + * @return size of the series that this symbol is part of + */ + public int getStructuredAppendTotal() { + return structuredAppendTotal; + } + + /** + * Sets the primary data. Should only be used for modes 2 and 3. Must conform to the following structure: + * + * + * + * + * + * + *
CharactersMeaning
1-9Postal code data which can consist of up to 9 digits (for mode 2) or up to 6 + * alphanumeric characters (for mode 3). Remaining unused characters should be + * filled with the SPACE character (ASCII 32).
10-12Three-digit country code according to ISO-3166.
13-15Three digit service code. This depends on your parcel courier.
+ * + * @param primary the primary data + */ + public void setPrimary(String primary) { + primaryData = primary; + } + + /** + * Returns the primary data for this MaxiCode symbol. Should only be used for modes 2 and 3. + * + * @return the primary data for this MaxiCode symbol + */ + public String getPrimary() { + return primaryData; + } + + @Override + protected boolean eciSupported() { + return true; + } + + /** {@inheritDoc} */ + @Override + protected void encode() { + + eciProcess(); + + // mode 2 -> mode 3 if postal code isn't strictly numeric + if (mode == 2) { + for (int i = 0; i < 9 && i < primaryData.length(); i++) { + if (primaryData.charAt(i) < '0' || primaryData.charAt(i) > '9') { + mode = 3; + break; + } + } + } + + // initialize the set and character arrays + processText(); + + // start building the codeword array, starting with a copy of the character data + // insert primary message if this is a structured carrier message; insert mode otherwise + codewords = Arrays.copyOf(character, character.length); + if (mode == 2 || mode == 3) { + int[] primary = getPrimaryCodewords(); + codewords = insertArray(codewords, 0, primary); + } else { + codewords = insertArray(codewords, 0, new int[] { mode }); + } + + // insert structured append flag if necessary + if (structuredAppendTotal > 1) { + + int[] flag = new int[2]; + flag[0] = 33; // padding + flag[1] = ((structuredAppendPosition - 1) << 3) | (structuredAppendTotal - 1); // position + total + + int index; + if (mode == 2 || mode == 3) { + index = 10; // first two data symbols in the secondary message + } else { + index = 1; // first two data symbols in the primary message (first symbol at index 0 isn't a data symbol) + } + + codewords = insertArray(codewords, index, flag); + } + + int secondaryMax, secondaryECMax; + if (mode == 5) { + // 68 data codewords, 56 error corrections in secondary message + secondaryMax = 68; + secondaryECMax = 56; + } else { + // 84 data codewords, 40 error corrections in secondary message + secondaryMax = 84; + secondaryECMax = 40; + } + + // truncate data codewords to maximum data space available + int totalMax = secondaryMax + 10; + if (codewords.length > totalMax) { + codewords = Arrays.copyOfRange(codewords, 0, totalMax); + } + + // insert primary error correction between primary message and secondary message (always EEC) + int[] primary = Arrays.copyOfRange(codewords, 0, 10); + int[] primaryCheck = getErrorCorrection(primary, 10); + codewords = insertArray(codewords, 10, primaryCheck); + + // calculate secondary error correction + int[] secondary = Arrays.copyOfRange(codewords, 20, codewords.length); + int[] secondaryOdd = new int[secondary.length / 2]; + int[] secondaryEven = new int[secondary.length / 2]; + for (int i = 0; i < secondary.length; i++) { + if ((i & 1) != 0) { // odd + secondaryOdd[(i - 1) / 2] = secondary[i]; + } else { // even + secondaryEven[i / 2] = secondary[i]; + } + } + int[] secondaryECOdd = getErrorCorrection(secondaryOdd, secondaryECMax / 2); + int[] secondaryECEven = getErrorCorrection(secondaryEven, secondaryECMax / 2); + + // add secondary error correction after secondary message + codewords = Arrays.copyOf(codewords, codewords.length + secondaryECOdd.length + secondaryECEven.length); + for (int i = 0; i < secondaryECOdd.length; i++) { + codewords[20 + secondaryMax + (2 * i) + 1] = secondaryECOdd[i]; + } + for (int i = 0; i < secondaryECEven.length; i++) { + codewords[20 + secondaryMax + (2 * i)] = secondaryECEven[i]; + } + + infoLine("Mode: " + mode); + infoLine("ECC Codewords: " + secondaryECMax); + info("Codewords: "); + for (int i = 0; i < codewords.length; i++) { + infoSpace(codewords[i]); + } + infoLine(); + + // copy data into symbol grid + int[] bit_pattern = new int[7]; + for (int i = 0; i < 33; i++) { + for (int j = 0; j < 30; j++) { + + int block = (MAXICODE_GRID[(i * 30) + j] + 5) / 6; + int bit = (MAXICODE_GRID[(i * 30) + j] + 5) % 6; + + if (block != 0) { + + bit_pattern[0] = (codewords[block - 1] & 0x20) >> 5; + bit_pattern[1] = (codewords[block - 1] & 0x10) >> 4; + bit_pattern[2] = (codewords[block - 1] & 0x8) >> 3; + bit_pattern[3] = (codewords[block - 1] & 0x4) >> 2; + bit_pattern[4] = (codewords[block - 1] & 0x2) >> 1; + bit_pattern[5] = (codewords[block - 1] & 0x1); + + if (bit_pattern[bit] != 0) { + grid[i][j] = true; + } else { + grid[i][j] = false; + } + } + } + } + + // add orientation markings + grid[0][28] = true; // top right filler + grid[0][29] = true; + grid[9][10] = true; // top left marker + grid[9][11] = true; + grid[10][11] = true; + grid[15][7] = true; // left hand marker + grid[16][8] = true; + grid[16][20] = true; // right hand marker + grid[17][20] = true; + grid[22][10] = true; // bottom left marker + grid[23][10] = true; + grid[22][17] = true; // bottom right marker + grid[23][17] = true; + + // the following is provided for compatibility, but the results are not useful + row_count = 33; + readable = ""; + pattern = new String[33]; + row_height = new int[33]; + for (int i = 0; i < 33; i++) { + StringBuilder bin = new StringBuilder(30); + for (int j = 0; j < 30; j++) { + if (grid[i][j]) { + bin.append("1"); + } else { + bin.append("0"); + } + } + pattern[i] = bin2pat(bin); + row_height[i] = 1; + } + } + + /** + * Extracts the postal code, country code and service code from the primary data and returns the corresponding primary message + * codewords. + * + * @return the primary message codewords + */ + private int[] getPrimaryCodewords() { + + assert mode == 2 || mode == 3; + + if (primaryData.length() != 15) { + throw new OkapiInputException("Invalid primary string"); + } + + for (int i = 9; i < 15; i++) { /* check that country code and service are numeric */ + if (primaryData.charAt(i) < '0' || primaryData.charAt(i) > '9') { + throw new OkapiInputException("Invalid primary string"); + } + } + + String postcode; + if (mode == 2) { + postcode = primaryData.substring(0, 9); + int index = postcode.indexOf(' '); + if (index != -1) { + postcode = postcode.substring(0, index); + } + } else { + assert mode == 3; + postcode = primaryData.substring(0, 6); + } + + int country = Integer.parseInt(primaryData.substring(9, 12)); + int service = Integer.parseInt(primaryData.substring(12, 15)); + + infoLine("Postal Code: " + postcode); + infoLine("Country Code: " + country); + infoLine("Service: " + service); + + if (mode == 2) { + return getMode2PrimaryCodewords(postcode, country, service); + } else { + assert mode == 3; + return getMode3PrimaryCodewords(postcode, country, service); + } + } + + /** + * Returns the primary message codewords for mode 2. + * + * @param postcode the postal code + * @param country the country code + * @param service the service code + * @return the primary message, as codewords + */ + private static int[] getMode2PrimaryCodewords(String postcode, int country, int service) { + + for (int i = 0; i < postcode.length(); i++) { + if (postcode.charAt(i) < '0' || postcode.charAt(i) > '9') { + postcode = postcode.substring(0, i); + break; + } + } + + int postcodeNum = Integer.parseInt(postcode); + + int[] primary = new int[10]; + primary[0] = ((postcodeNum & 0x03) << 4) | 2; + primary[1] = ((postcodeNum & 0xfc) >> 2); + primary[2] = ((postcodeNum & 0x3f00) >> 8); + primary[3] = ((postcodeNum & 0xfc000) >> 14); + primary[4] = ((postcodeNum & 0x3f00000) >> 20); + primary[5] = ((postcodeNum & 0x3c000000) >> 26) | ((postcode.length() & 0x3) << 4); + primary[6] = ((postcode.length() & 0x3c) >> 2) | ((country & 0x3) << 4); + primary[7] = (country & 0xfc) >> 2; + primary[8] = ((country & 0x300) >> 8) | ((service & 0xf) << 2); + primary[9] = ((service & 0x3f0) >> 4); + + return primary; + } + + /** + * Returns the primary message codewords for mode 3. + * + * @param postcode the postal code + * @param country the country code + * @param service the service code + * @return the primary message, as codewords + */ + private static int[] getMode3PrimaryCodewords(String postcode, int country, int service) { + + int[] postcodeNums = new int[postcode.length()]; + + postcode = postcode.toUpperCase(); + for (int i = 0; i < postcodeNums.length; i++) { + postcodeNums[i] = postcode.charAt(i); + if (postcode.charAt(i) >= 'A' && postcode.charAt(i) <= 'Z') { + // (Capital) letters shifted to Code Set A values + postcodeNums[i] -= 64; + } + if (postcodeNums[i] == 27 || postcodeNums[i] == 31 || postcodeNums[i] == 33 || postcodeNums[i] >= 59) { + // Not a valid postal code character, use space instead + postcodeNums[i] = 32; + } + // Input characters lower than 27 (NUL - SUB) in postal code are interpreted as capital + // letters in Code Set A (e.g. LF becomes 'J') + } + + int[] primary = new int[10]; + primary[0] = ((postcodeNums[5] & 0x03) << 4) | 3; + primary[1] = ((postcodeNums[4] & 0x03) << 4) | ((postcodeNums[5] & 0x3c) >> 2); + primary[2] = ((postcodeNums[3] & 0x03) << 4) | ((postcodeNums[4] & 0x3c) >> 2); + primary[3] = ((postcodeNums[2] & 0x03) << 4) | ((postcodeNums[3] & 0x3c) >> 2); + primary[4] = ((postcodeNums[1] & 0x03) << 4) | ((postcodeNums[2] & 0x3c) >> 2); + primary[5] = ((postcodeNums[0] & 0x03) << 4) | ((postcodeNums[1] & 0x3c) >> 2); + primary[6] = ((postcodeNums[0] & 0x3c) >> 2) | ((country & 0x3) << 4); + primary[7] = (country & 0xfc) >> 2; + primary[8] = ((country & 0x300) >> 8) | ((service & 0xf) << 2); + primary[9] = ((service & 0x3f0) >> 4); + + return primary; + } + + /** + * Formats text according to Appendix A, populating the {@link #set} and {@link #character} arrays. + * + * @return true if the content fits in this symbol and was formatted; false otherwise + */ + private void processText() { + + int length = content.length(); + int i, j, count, current_set; + + if (length > 138) { + throw OkapiInputException.inputTooLong(); + } + + for (i = 0; i < 144; i++) { + set[i] = -1; + character[i] = 0; + } + + for (i = 0; i < length; i++) { + /* Look up characters in table from Appendix A - this gives + value and code set for most characters */ + set[i] = MAXICODE_SET[inputData[i]]; + character[i] = MAXICODE_SYMBOL_CHAR[inputData[i]]; + } + + // If a character can be represented in more than one code set, pick which version to use. + if (set[0] == 0) { + if (character[0] == 13) { + character[0] = 0; + } + set[0] = 1; + } + + for (i = 1; i < length; i++) { + if (set[i] == 0) { + /* Special character that can be represented in more than one code set. */ + if (character[i] == 13) { + /* Carriage Return */ + set[i] = bestSurroundingSet(i, length, 1, 5); + if (set[i] == 5) { + character[i] = 13; + } else { + character[i] = 0; + } + } else if (character[i] == 28) { + /* FS */ + set[i] = bestSurroundingSet(i, length, 1, 2, 3, 4, 5); + if (set[i] == 5) { + character[i] = 32; + } + } else if (character[i] == 29) { + /* GS */ + set[i] = bestSurroundingSet(i, length, 1, 2, 3, 4, 5); + if (set[i] == 5) { + character[i] = 33; + } + } else if (character[i] == 30) { + /* RS */ + set[i] = bestSurroundingSet(i, length, 1, 2, 3, 4, 5); + if (set[i] == 5) { + character[i] = 34; + } + } else if (character[i] == 32) { + /* Space */ + set[i] = bestSurroundingSet(i, length, 1, 2, 3, 4, 5); + if (set[i] == 1) { + character[i] = 32; + } else if (set[i] == 2) { + character[i] = 47; + } else { + character[i] = 59; + } + } else if (character[i] == 44) { + /* Comma */ + set[i] = bestSurroundingSet(i, length, 1, 2); + if (set[i] == 2) { + character[i] = 48; + } + } else if (character[i] == 46) { + /* Full Stop */ + set[i] = bestSurroundingSet(i, length, 1, 2); + if (set[i] == 2) { + character[i] = 49; + } + } else if (character[i] == 47) { + /* Slash */ + set[i] = bestSurroundingSet(i, length, 1, 2); + if (set[i] == 2) { + character[i] = 50; + } + } else if (character[i] == 58) { + /* Colon */ + set[i] = bestSurroundingSet(i, length, 1, 2); + if (set[i] == 2) { + character[i] = 51; + } + } + } + } + + for (i = length; i < set.length; i++) { + /* Add the padding */ + if (set[length - 1] == 2) { + set[i] = 2; + } else { + set[i] = 1; + } + character[i] = 33; + } + + /* Find candidates for number compression (not allowed in primary message in modes 2 and 3). */ + if (mode == 2 || mode == 3) { + j = 9; + } else { + j = 0; + } + count = 0; + for (i = j; i < 143; i++) { + if (set[i] == 1 && character[i] >= 48 && character[i] <= 57) { + /* Character is a number */ + count++; + } else { + count = 0; + } + if (count == 9) { + /* Nine digits in a row can be compressed */ + set[i] = 6; + set[i - 1] = 6; + set[i - 2] = 6; + set[i - 3] = 6; + set[i - 4] = 6; + set[i - 5] = 6; + set[i - 6] = 6; + set[i - 7] = 6; + set[i - 8] = 6; + count = 0; + } + } + + /* Add shift and latch characters */ + current_set = 1; + i = 0; + do { + if ((set[i] != current_set) && (set[i] != 6)) { + switch (set[i]) { + case 1: + if (i + 1 < set.length && set[i + 1] == 1) { + if (i + 2 < set.length && set[i + 2] == 1) { + if (i + 3 < set.length && set[i + 3] == 1) { + /* Latch A */ + insert(i, 63); + current_set = 1; + length++; + i += 3; + } else { + /* 3 Shift A */ + insert(i, 57); + length++; + i += 2; + } + } else { + /* 2 Shift A */ + insert(i, 56); + length++; + i++; + } + } else { + /* Shift A */ + insert(i, 59); + length++; + } + break; + case 2: + if (i + 1 < set.length && set[i + 1] == 2) { + /* Latch B */ + insert(i, 63); + current_set = 2; + length++; + i++; + } else { + /* Shift B */ + insert(i, 59); + length++; + } + break; + case 3: + if (i + 3 < set.length && set[i + 1] == 3 && set[i + 2] == 3 && set[i + 3] == 3) { + /* Lock In C */ + insert(i, 60); + insert(i, 60); + current_set = 3; + length++; + i += 3; + } else { + /* Shift C */ + insert(i, 60); + length++; + } + break; + case 4: + if (i + 3 < set.length && set[i + 1] == 4 && set[i + 2] == 4 && set[i + 3] == 4) { + /* Lock In D */ + insert(i, 61); + insert(i, 61); + current_set = 4; + length++; + i += 3; + } else { + /* Shift D */ + insert(i, 61); + length++; + } + break; + case 5: + if (i + 3 < set.length && set[i + 1] == 5 && set[i + 2] == 5 && set[i + 3] == 5) { + /* Lock In E */ + insert(i, 62); + insert(i, 62); + current_set = 5; + length++; + i += 3; + } else { + /* Shift E */ + insert(i, 62); + length++; + } + break; + default: + throw new OkapiInternalException("Unexpected set " + set[i] + " at index " + i + "."); + } + i++; + } + i++; + } while (i < set.length); + + /* Number compression has not been forgotten! It's handled below. */ + i = 0; + do { + if (set[i] == 6) { + /* Number compression */ + int value = 0; + for (j = 0; j < 9; j++) { + value *= 10; + value += (character[i + j] - '0'); + } + character[i] = 31; /* NS */ + character[i + 1] = (value & 0x3f000000) >> 24; + character[i + 2] = (value & 0xfc0000) >> 18; + character[i + 3] = (value & 0x3f000) >> 12; + character[i + 4] = (value & 0xfc0) >> 6; + character[i + 5] = (value & 0x3f); + i += 6; + for (j = i; j < 140; j++) { + set[j] = set[j + 3]; + character[j] = character[j + 3]; + } + length -= 3; + } else { + i++; + } + } while (i < set.length); + + /* Inject ECI codes to beginning of data, according to Table 3 */ + if (eciMode != 3) { + insert(0, 27); // ECI + if (eciMode >= 0 && eciMode <= 31) { + insert(1, eciMode & 0x1F); + length += 2; + } + if (eciMode >= 32 && eciMode <= 1023) { + insert(1, 0x20 + (eciMode >> 6)); + insert(2, eciMode & 0x3F); + length += 3; + } + if (eciMode >= 1024 && eciMode <= 32767) { + insert(1, 0x30 + (eciMode >> 12)); + insert(2, (eciMode >> 6) & 0x3F); + insert(3, eciMode & 0x3F); + length += 4; + } + if (eciMode >= 32768 && eciMode <= 999999) { + insert(1, 0x38 + (eciMode >> 18)); + insert(2, (eciMode >> 12) & 0x3F); + insert(3, (eciMode >> 6) & 0x3F); + insert(4, eciMode & 0x3F); + length += 5; + } + } + + /* Make sure we haven't exceeded the maximum data length. */ + int maxLength; + if (mode == 2 || mode == 3) { + maxLength = 84; + } else if (mode == 4 || mode == 6) { + maxLength = 93; + } else if (mode == 5) { + maxLength = 77; + } else { + maxLength = 0; // impossible + } + if (length > maxLength) { + throw OkapiInputException.inputTooLong(); + } + } + + /** + * Guesses the best set to use at the specified index by looking at the surrounding sets. In general, characters in + * lower-numbered sets are more common, so we choose them if we can. If no good surrounding sets can be found, the default + * value returned is the first value from the valid set. + * + * @param index the current index + * @param length the maximum length to look at + * @param valid the valid sets for this index + * @return the best set to use at the specified index + */ + private int bestSurroundingSet(int index, int length, int... valid) { + int option1 = set[index - 1]; + if (index + 1 < length) { + // we have two options to check + int option2 = set[index + 1]; + if (contains(valid, option1) && contains(valid, option2)) { + return Math.min(option1, option2); + } else if (contains(valid, option1)) { + return option1; + } else if (contains(valid, option2)) { + return option2; + } else { + return valid[0]; + } + } else { + // we only have one option to check + if (contains(valid, option1)) { + return option1; + } else { + return valid[0]; + } + } + } + + /** + * Moves everything up so that the specified shift or latch character can be inserted. + * + * @param position the position beyond which everything needs to be shifted + * @param c the latch or shift character to insert at the specified position, after everything has been shifted + */ + private void insert(int position, int c) { + for (int i = 143; i > position; i--) { + set[i] = set[i - 1]; + character[i] = character[i - 1]; + } + character[position] = c; + } + + /** + * Returns the error correction codewords for the specified data codewords. + * + * @param codewords the codewords that we need error correction codewords for + * @param ecclen the number of error correction codewords needed + * @return the error correction codewords for the specified data codewords + */ + private static int[] getErrorCorrection(int[] codewords, int ecclen) { + + ReedSolomon rs = new ReedSolomon(); + rs.init_gf(0x43); + rs.init_code(ecclen, 1); + rs.encode(codewords.length, codewords); + + int[] results = new int[ecclen]; + for (int i = 0; i < ecclen; i++) { + results[i] = rs.getResult(results.length - 1 - i); + } + + return results; + } + + /** {@inheritDoc} */ + @Override + protected void plotSymbol() { + + resetPlotElements(); + + symbol_height = 72; + symbol_width = 74; + + // hexagons + for (int row = 0; row < 33; row++) { + for (int col = 0; col < 30; col++) { + if (grid[row][col]) { + double x = (2.46 * col) + 1.23; + if ((row & 1) != 0) { + x += 1.23; + } + double y = (2.135 * row) + 1.43; + hexagons.add(new Hexagon(x, y)); + } + } + } + + // circles + double[] radii = { 10.85, 8.97, 7.10, 5.22, 3.31, 1.43 }; + for (int i = 0; i < radii.length; i++) { + target.add(new Circle(35.76, 35.60, radii[i])); + } + } + + /** {@inheritDoc} */ + @Override + protected int[] getCodewords() { + return codewords; + } +} diff --git a/src/main/java/uk/org/okapibarcode/backend/MsiPlessey.java b/src/main/java/uk/org/okapibarcode/backend/MsiPlessey.java index 335468d5..02a55ea8 100644 --- a/src/main/java/uk/org/okapibarcode/backend/MsiPlessey.java +++ b/src/main/java/uk/org/okapibarcode/backend/MsiPlessey.java @@ -1,218 +1,218 @@ -/* - * Copyright 2014 Robin Stuart - * - * 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. - */ - -package uk.org.okapibarcode.backend; - -/** - *

Implements the MSI (Modified Plessey) bar code symbology. - * - *

MSI Plessey can encode a string of numeric digits and has a range of check digit options. - * - * @author Robin Stuart - * @author Daniel Gredler - */ -public class MsiPlessey extends Symbol { - - public enum CheckDigit { - NONE, MOD10, MOD10_MOD10, MOD11, MOD11_MOD10 - } - - private final static String[] MSI_PLESS_TABLE = { - "12121212", "12121221", "12122112", "12122121", "12211212", - "12211221", "12212112", "12212121", "21121212", "21121221" - }; - - private CheckDigit checkDigit = CheckDigit.NONE; - private boolean checkDigitInHumanReadableText = true; - private double moduleWidthRatio = 2; - - /** - * Sets the check digit scheme to use. Options are: None, Modulo-10, 2 x Modulo-10, Modulo-11, or Modulo-11 & 10. - * - * @param checkDigit the type of check digit to add to symbol - */ - public void setCheckDigit(CheckDigit checkDigit) { - this.checkDigit = checkDigit; - } - - /** - * Returns the check digit scheme being used. - * - * @return the check digit scheme being used - */ - public CheckDigit getCheckDigit() { - return checkDigit; - } - - /** - * Sets whether or not the check digit is shown in the human-readable text. Defaults to {@code true}. - * - * @param checkDigitInHumanReadableText whether or not the check digit is shown in the human-readable text - */ - public void setCheckDigitInHumanReadableText(boolean checkDigitInHumanReadableText) { - this.checkDigitInHumanReadableText = checkDigitInHumanReadableText; - } - - /** - * Returns whether or not the check digit is shown in the human-readable text. - * - * @return whether or not the check digit is shown in the human-readable text - */ - public boolean getCheckDigitInHumanReadableText() { - return checkDigitInHumanReadableText; - } - - /** - * Sets the ratio of wide bar width to narrow bar width. Valid values are usually - * between {@code 2} and {@code 3}. The default value is {@code 2}. - * - * @param moduleWidthRatio the ratio of wide bar width to narrow bar width - */ - public void setModuleWidthRatio(double moduleWidthRatio) { - this.moduleWidthRatio = moduleWidthRatio; - } - - /** - * Returns the ratio of wide bar width to narrow bar width. - * - * @return the ratio of wide bar width to narrow bar width - */ - public double getModuleWidthRatio() { - return moduleWidthRatio; - } - - /** {@inheritDoc} */ - @Override - protected void encode() { - - if (!content.matches("[0-9]*")) { - throw OkapiInputException.invalidCharactersInInput(); - } - - int length = content.length(); - int maxExpectedLength = 2 + ((length + maxCheckDigits(checkDigit)) * 8) + 3; - StringBuilder intermediate = new StringBuilder(maxExpectedLength); - intermediate.append("21"); // Start - - for (int i = 0; i < length; i++) { - char c = content.charAt(i); - int n = Character.getNumericValue(c); - intermediate.append(MSI_PLESS_TABLE[n]); - } - - String data = content; - - if (checkDigit == CheckDigit.MOD10 || checkDigit == CheckDigit.MOD10_MOD10) { - /* Add a Modulo-10 check digit */ - int checkDigit = calcMod10(content); - intermediate.append(MSI_PLESS_TABLE[checkDigit]); - data += checkDigit; - } - - if (checkDigit == CheckDigit.MOD11 || checkDigit == CheckDigit.MOD11_MOD10) { - /* Add a Modulo-11 check digit */ - int checkDigit = calcMod11(content); - data += checkDigit; - if (checkDigit == 10) { - intermediate.append(MSI_PLESS_TABLE[1]); - intermediate.append(MSI_PLESS_TABLE[0]); - } else { - intermediate.append(MSI_PLESS_TABLE[checkDigit]); - } - } - - if (checkDigit == CheckDigit.MOD10_MOD10 || checkDigit == CheckDigit.MOD11_MOD10) { - /* Add a second Modulo-10 check digit */ - int checkDigit = calcMod10(data); - intermediate.append(MSI_PLESS_TABLE[checkDigit]); - data += checkDigit; - } - - intermediate.append("121"); // Stop - - assert maxExpectedLength >= intermediate.length(); - assert maxExpectedLength - intermediate.length() <= 8; - - readable = (checkDigitInHumanReadableText ? data : content); - pattern = new String[] { intermediate.toString() }; - row_count = 1; - row_height = new int[] { -1 }; - } - - private static int maxCheckDigits(CheckDigit scheme) { - switch (scheme) { - case NONE: return 0; - case MOD10: return 1; - case MOD11: return 2; - case MOD10_MOD10: return 2; - case MOD11_MOD10: return 3; - default: throw new OkapiInternalException("Unknown check digit scheme: " + scheme); - } - } - - private static int calcMod10(String s) { - - int sum = 0; - int parity = s.length() % 2; - for (int i = s.length() - 1; i >= 0; i--) { - int val = s.charAt(i) - '0'; - if (i % 2 == parity) { - sum += val; - } else { - val *= 2; - sum += val % 10; - sum += val / 10; - } - } - - int checkDigit = 10 - (sum % 10); - if (checkDigit == 10) { - checkDigit = 0; - } - - return checkDigit; - } - - private static int calcMod11(String s) { - - int sum = 0; - int weight = 2; - for (int i = s.length() - 1; i >= 0; i--) { - sum += (s.charAt(i) - '0') * weight; - weight++; - if (weight == 8) { - weight = 2; - } - } - - int checkDigit = 11 - (sum % 11); - if (checkDigit == 11) { - checkDigit = 0; - } - - return checkDigit; - } - - /** {@inheritDoc} */ - @Override - protected double getModuleWidth(int originalWidth) { - if (originalWidth == 1) { - return 1; - } else { - return moduleWidthRatio; - } - } -} +/* + * Copyright 2014 Robin Stuart + * + * 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. + */ + +package uk.org.okapibarcode.backend; + +/** + *

Implements the MSI (Modified Plessey) bar code symbology. + * + *

MSI Plessey can encode a string of numeric digits and has a range of check digit options. + * + * @author Robin Stuart + * @author Daniel Gredler + */ +public class MsiPlessey extends Symbol { + + public enum CheckDigit { + NONE, MOD10, MOD10_MOD10, MOD11, MOD11_MOD10 + } + + private final static String[] MSI_PLESS_TABLE = { + "12121212", "12121221", "12122112", "12122121", "12211212", + "12211221", "12212112", "12212121", "21121212", "21121221" + }; + + private CheckDigit checkDigit = CheckDigit.NONE; + private boolean checkDigitInHumanReadableText = true; + private double moduleWidthRatio = 2; + + /** + * Sets the check digit scheme to use. Options are: None, Modulo-10, 2 x Modulo-10, Modulo-11, or Modulo-11 & 10. + * + * @param checkDigit the type of check digit to add to symbol + */ + public void setCheckDigit(CheckDigit checkDigit) { + this.checkDigit = checkDigit; + } + + /** + * Returns the check digit scheme being used. + * + * @return the check digit scheme being used + */ + public CheckDigit getCheckDigit() { + return checkDigit; + } + + /** + * Sets whether or not the check digit is shown in the human-readable text. Defaults to {@code true}. + * + * @param checkDigitInHumanReadableText whether or not the check digit is shown in the human-readable text + */ + public void setCheckDigitInHumanReadableText(boolean checkDigitInHumanReadableText) { + this.checkDigitInHumanReadableText = checkDigitInHumanReadableText; + } + + /** + * Returns whether or not the check digit is shown in the human-readable text. + * + * @return whether or not the check digit is shown in the human-readable text + */ + public boolean getCheckDigitInHumanReadableText() { + return checkDigitInHumanReadableText; + } + + /** + * Sets the ratio of wide bar width to narrow bar width. Valid values are usually + * between {@code 2} and {@code 3}. The default value is {@code 2}. + * + * @param moduleWidthRatio the ratio of wide bar width to narrow bar width + */ + public void setModuleWidthRatio(double moduleWidthRatio) { + this.moduleWidthRatio = moduleWidthRatio; + } + + /** + * Returns the ratio of wide bar width to narrow bar width. + * + * @return the ratio of wide bar width to narrow bar width + */ + public double getModuleWidthRatio() { + return moduleWidthRatio; + } + + /** {@inheritDoc} */ + @Override + protected void encode() { + + if (!content.matches("[0-9]*")) { + throw OkapiInputException.invalidCharactersInInput(); + } + + int length = content.length(); + int maxExpectedLength = 2 + ((length + maxCheckDigits(checkDigit)) * 8) + 3; + StringBuilder intermediate = new StringBuilder(maxExpectedLength); + intermediate.append("21"); // Start + + for (int i = 0; i < length; i++) { + char c = content.charAt(i); + int n = Character.getNumericValue(c); + intermediate.append(MSI_PLESS_TABLE[n]); + } + + String data = content; + + if (checkDigit == CheckDigit.MOD10 || checkDigit == CheckDigit.MOD10_MOD10) { + /* Add a Modulo-10 check digit */ + int checkDigit = calcMod10(content); + intermediate.append(MSI_PLESS_TABLE[checkDigit]); + data += checkDigit; + } + + if (checkDigit == CheckDigit.MOD11 || checkDigit == CheckDigit.MOD11_MOD10) { + /* Add a Modulo-11 check digit */ + int checkDigit = calcMod11(content); + data += checkDigit; + if (checkDigit == 10) { + intermediate.append(MSI_PLESS_TABLE[1]); + intermediate.append(MSI_PLESS_TABLE[0]); + } else { + intermediate.append(MSI_PLESS_TABLE[checkDigit]); + } + } + + if (checkDigit == CheckDigit.MOD10_MOD10 || checkDigit == CheckDigit.MOD11_MOD10) { + /* Add a second Modulo-10 check digit */ + int checkDigit = calcMod10(data); + intermediate.append(MSI_PLESS_TABLE[checkDigit]); + data += checkDigit; + } + + intermediate.append("121"); // Stop + + assert maxExpectedLength >= intermediate.length(); + assert maxExpectedLength - intermediate.length() <= 8; + + readable = (checkDigitInHumanReadableText ? data : content); + pattern = new String[] { intermediate.toString() }; + row_count = 1; + row_height = new int[] { -1 }; + } + + private static int maxCheckDigits(CheckDigit scheme) { + switch (scheme) { + case NONE: return 0; + case MOD10: return 1; + case MOD11: return 2; + case MOD10_MOD10: return 2; + case MOD11_MOD10: return 3; + default: throw new OkapiInternalException("Unknown check digit scheme: " + scheme); + } + } + + private static int calcMod10(String s) { + + int sum = 0; + int parity = s.length() % 2; + for (int i = s.length() - 1; i >= 0; i--) { + int val = s.charAt(i) - '0'; + if (i % 2 == parity) { + sum += val; + } else { + val *= 2; + sum += val % 10; + sum += val / 10; + } + } + + int checkDigit = 10 - (sum % 10); + if (checkDigit == 10) { + checkDigit = 0; + } + + return checkDigit; + } + + private static int calcMod11(String s) { + + int sum = 0; + int weight = 2; + for (int i = s.length() - 1; i >= 0; i--) { + sum += (s.charAt(i) - '0') * weight; + weight++; + if (weight == 8) { + weight = 2; + } + } + + int checkDigit = 11 - (sum % 11); + if (checkDigit == 11) { + checkDigit = 0; + } + + return checkDigit; + } + + /** {@inheritDoc} */ + @Override + protected double getModuleWidth(int originalWidth) { + if (originalWidth == 1) { + return 1; + } else { + return moduleWidthRatio; + } + } +} diff --git a/src/main/java/uk/org/okapibarcode/backend/Pdf417.java b/src/main/java/uk/org/okapibarcode/backend/Pdf417.java index cca91b81..350c75aa 100644 --- a/src/main/java/uk/org/okapibarcode/backend/Pdf417.java +++ b/src/main/java/uk/org/okapibarcode/backend/Pdf417.java @@ -1,1818 +1,1818 @@ -/* - * Copyright 2014-2017 Robin Stuart, Daniel Gredler - * - * 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. - */ - -package uk.org.okapibarcode.backend; - -import static uk.org.okapibarcode.util.Arrays.positionOf; - -import java.math.BigInteger; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import uk.org.okapibarcode.util.EciMode; - -/** - *

Implements PDF417 bar code symbology and MicroPDF417 bar code symbology - * according to ISO/IEC 15438:2006 and ISO/IEC 24728:2006 respectively. - * - *

PDF417 supports encoding up to the ISO standard maximum symbol size of 925 - * codewords which (at error correction level 0) allows a maximum data size - * of 1850 text characters, or 2710 digits. The maximum size MicroPDF417 symbol - * can hold 250 alphanumeric characters or 366 digits. - * - * @author Robin Stuart - * @author Daniel Gredler - */ -public class Pdf417 extends Symbol { - - public enum Mode { - /** Normal PDF417. */ - NORMAL, - /** Truncated PDF417. */ - TRUNCATED, - /** MicroPDF417. */ - MICRO - } - - private enum EncodingMode { - FALSE, TEX, BYT, NUM - } - - private int[] codeWords = new int[2700]; - private int codeWordCount; - private Mode symbolMode = Mode.NORMAL; - private boolean forceByteCompaction; - private Integer columns; - private Integer rows; - private int preferredEccLevel = -1; - private String structuredAppendFileId = "000"; - private int structuredAppendPosition = 1; - private int structuredAppendTotal = 1; - private String structuredAppendFileName; - private boolean structuredAppendIncludeSegmentCount; - - private static final int MAX_NUMERIC_COMPACTION_BLOCK_SIZE = 44; - - private static final int[] COEFRS = { - /* k = 2 */ - 27, 917, - - /* k = 4 */ - 522, 568, 723, 809, - - /* k = 8 */ - 237, 308, 436, 284, 646, 653, 428, 379, - - /* k = 16 */ - 274, 562, 232, 755, 599, 524, 801, 132, 295, 116, 442, 428, 295, 42, 176, 65, - - /* k = 32 */ - 361, 575, 922, 525, 176, 586, 640, 321, 536, 742, 677, 742, 687, 284, 193, 517, - 273, 494, 263, 147, 593, 800, 571, 320, 803, 133, 231, 390, 685, 330, 63, 410, - - /* k = 64 */ - 539, 422, 6, 93, 862, 771, 453, 106, 610, 287, 107, 505, 733, 877, 381, 612, - 723, 476, 462, 172, 430, 609, 858, 822, 543, 376, 511, 400, 672, 762, 283, 184, - 440, 35, 519, 31, 460, 594, 225, 535, 517, 352, 605, 158, 651, 201, 488, 502, - 648, 733, 717, 83, 404, 97, 280, 771, 840, 629, 4, 381, 843, 623, 264, 543, - - /* k = 128 */ - 521, 310, 864, 547, 858, 580, 296, 379, 53, 779, 897, 444, 400, 925, 749, 415, - 822, 93, 217, 208, 928, 244, 583, 620, 246, 148, 447, 631, 292, 908, 490, 704, - 516, 258, 457, 907, 594, 723, 674, 292, 272, 96, 684, 432, 686, 606, 860, 569, - 193, 219, 129, 186, 236, 287, 192, 775, 278, 173, 40, 379, 712, 463, 646, 776, - 171, 491, 297, 763, 156, 732, 95, 270, 447, 90, 507, 48, 228, 821, 808, 898, - 784, 663, 627, 378, 382, 262, 380, 602, 754, 336, 89, 614, 87, 432, 670, 616, - 157, 374, 242, 726, 600, 269, 375, 898, 845, 454, 354, 130, 814, 587, 804, 34, - 211, 330, 539, 297, 827, 865, 37, 517, 834, 315, 550, 86, 801, 4, 108, 539, - - /* k = 256 */ - 524, 894, 75, 766, 882, 857, 74, 204, 82, 586, 708, 250, 905, 786, 138, 720, - 858, 194, 311, 913, 275, 190, 375, 850, 438, 733, 194, 280, 201, 280, 828, 757, - 710, 814, 919, 89, 68, 569, 11, 204, 796, 605, 540, 913, 801, 700, 799, 137, - 439, 418, 592, 668, 353, 859, 370, 694, 325, 240, 216, 257, 284, 549, 209, 884, - 315, 70, 329, 793, 490, 274, 877, 162, 749, 812, 684, 461, 334, 376, 849, 521, - 307, 291, 803, 712, 19, 358, 399, 908, 103, 511, 51, 8, 517, 225, 289, 470, - 637, 731, 66, 255, 917, 269, 463, 830, 730, 433, 848, 585, 136, 538, 906, 90, - 2, 290, 743, 199, 655, 903, 329, 49, 802, 580, 355, 588, 188, 462, 10, 134, - 628, 320, 479, 130, 739, 71, 263, 318, 374, 601, 192, 605, 142, 673, 687, 234, - 722, 384, 177, 752, 607, 640, 455, 193, 689, 707, 805, 641, 48, 60, 732, 621, - 895, 544, 261, 852, 655, 309, 697, 755, 756, 60, 231, 773, 434, 421, 726, 528, - 503, 118, 49, 795, 32, 144, 500, 238, 836, 394, 280, 566, 319, 9, 647, 550, - 73, 914, 342, 126, 32, 681, 331, 792, 620, 60, 609, 441, 180, 791, 893, 754, - 605, 383, 228, 749, 760, 213, 54, 297, 134, 54, 834, 299, 922, 191, 910, 532, - 609, 829, 189, 20, 167, 29, 872, 449, 83, 402, 41, 656, 505, 579, 481, 173, - 404, 251, 688, 95, 497, 555, 642, 543, 307, 159, 924, 558, 648, 55, 497, 10, - - /* k = 512 */ - 352, 77, 373, 504, 35, 599, 428, 207, 409, 574, 118, 498, 285, 380, 350, 492, - 197, 265, 920, 155, 914, 299, 229, 643, 294, 871, 306, 88, 87, 193, 352, 781, - 846, 75, 327, 520, 435, 543, 203, 666, 249, 346, 781, 621, 640, 268, 794, 534, - 539, 781, 408, 390, 644, 102, 476, 499, 290, 632, 545, 37, 858, 916, 552, 41, - 542, 289, 122, 272, 383, 800, 485, 98, 752, 472, 761, 107, 784, 860, 658, 741, - 290, 204, 681, 407, 855, 85, 99, 62, 482, 180, 20, 297, 451, 593, 913, 142, - 808, 684, 287, 536, 561, 76, 653, 899, 729, 567, 744, 390, 513, 192, 516, 258, - 240, 518, 794, 395, 768, 848, 51, 610, 384, 168, 190, 826, 328, 596, 786, 303, - 570, 381, 415, 641, 156, 237, 151, 429, 531, 207, 676, 710, 89, 168, 304, 402, - 40, 708, 575, 162, 864, 229, 65, 861, 841, 512, 164, 477, 221, 92, 358, 785, - 288, 357, 850, 836, 827, 736, 707, 94, 8, 494, 114, 521, 2, 499, 851, 543, - 152, 729, 771, 95, 248, 361, 578, 323, 856, 797, 289, 51, 684, 466, 533, 820, - 669, 45, 902, 452, 167, 342, 244, 173, 35, 463, 651, 51, 699, 591, 452, 578, - 37, 124, 298, 332, 552, 43, 427, 119, 662, 777, 475, 850, 764, 364, 578, 911, - 283, 711, 472, 420, 245, 288, 594, 394, 511, 327, 589, 777, 699, 688, 43, 408, - 842, 383, 721, 521, 560, 644, 714, 559, 62, 145, 873, 663, 713, 159, 672, 729, - 624, 59, 193, 417, 158, 209, 563, 564, 343, 693, 109, 608, 563, 365, 181, 772, - 677, 310, 248, 353, 708, 410, 579, 870, 617, 841, 632, 860, 289, 536, 35, 777, - 618, 586, 424, 833, 77, 597, 346, 269, 757, 632, 695, 751, 331, 247, 184, 45, - 787, 680, 18, 66, 407, 369, 54, 492, 228, 613, 830, 922, 437, 519, 644, 905, - 789, 420, 305, 441, 207, 300, 892, 827, 141, 537, 381, 662, 513, 56, 252, 341, - 242, 797, 838, 837, 720, 224, 307, 631, 61, 87, 560, 310, 756, 665, 397, 808, - 851, 309, 473, 795, 378, 31, 647, 915, 459, 806, 590, 731, 425, 216, 548, 249, - 321, 881, 699, 535, 673, 782, 210, 815, 905, 303, 843, 922, 281, 73, 469, 791, - 660, 162, 498, 308, 155, 422, 907, 817, 187, 62, 16, 425, 535, 336, 286, 437, - 375, 273, 610, 296, 183, 923, 116, 667, 751, 353, 62, 366, 691, 379, 687, 842, - 37, 357, 720, 742, 330, 5, 39, 923, 311, 424, 242, 749, 321, 54, 669, 316, - 342, 299, 534, 105, 667, 488, 640, 672, 576, 540, 316, 486, 721, 610, 46, 656, - 447, 171, 616, 464, 190, 531, 297, 321, 762, 752, 533, 175, 134, 14, 381, 433, - 717, 45, 111, 20, 596, 284, 736, 138, 646, 411, 877, 669, 141, 919, 45, 780, - 407, 164, 332, 899, 165, 726, 600, 325, 498, 655, 357, 752, 768, 223, 849, 647, - 63, 310, 863, 251, 366, 304, 282, 738, 675, 410, 389, 244, 31, 121, 303, 263 - }; - - private static final String[] CODAGEMC = { - "urA", "xfs", "ypy", "unk", "xdw", "yoz", "pDA", "uls", "pBk", "eBA", - "pAs", "eAk", "prA", "uvs", "xhy", "pnk", "utw", "xgz", "fDA", "pls", "fBk", "frA", "pvs", - "uxy", "fnk", "ptw", "uwz", "fls", "psy", "fvs", "pxy", "ftw", "pwz", "fxy", "yrx", "ufk", - "xFw", "ymz", "onA", "uds", "xEy", "olk", "ucw", "dBA", "oks", "uci", "dAk", "okg", "dAc", - "ovk", "uhw", "xaz", "dnA", "ots", "ugy", "dlk", "osw", "ugj", "dks", "osi", "dvk", "oxw", - "uiz", "dts", "owy", "dsw", "owj", "dxw", "oyz", "dwy", "dwj", "ofA", "uFs", "xCy", "odk", - "uEw", "xCj", "clA", "ocs", "uEi", "ckk", "ocg", "ckc", "ckE", "cvA", "ohs", "uay", "ctk", - "ogw", "uaj", "css", "ogi", "csg", "csa", "cxs", "oiy", "cww", "oij", "cwi", "cyy", "oFk", - "uCw", "xBj", "cdA", "oEs", "uCi", "cck", "oEg", "uCb", "ccc", "oEa", "ccE", "oED", "chk", - "oaw", "uDj", "cgs", "oai", "cgg", "oab", "cga", "cgD", "obj", "cib", "cFA", "oCs", "uBi", - "cEk", "oCg", "uBb", "cEc", "oCa", "cEE", "oCD", "cEC", "cas", "cag", "caa", "cCk", "uAr", - "oBa", "oBD", "cCB", "tfk", "wpw", "yez", "mnA", "tds", "woy", "mlk", "tcw", "woj", "FBA", - "mks", "FAk", "mvk", "thw", "wqz", "FnA", "mts", "tgy", "Flk", "msw", "Fks", "Fkg", "Fvk", - "mxw", "tiz", "Fts", "mwy", "Fsw", "Fsi", "Fxw", "myz", "Fwy", "Fyz", "vfA", "xps", "yuy", - "vdk", "xow", "yuj", "qlA", "vcs", "xoi", "qkk", "vcg", "xob", "qkc", "vca", "mfA", "tFs", - "wmy", "qvA", "mdk", "tEw", "wmj", "qtk", "vgw", "xqj", "hlA", "Ekk", "mcg", "tEb", "hkk", - "qsg", "hkc", "EvA", "mhs", "tay", "hvA", "Etk", "mgw", "taj", "htk", "qww", "vij", "hss", - "Esg", "hsg", "Exs", "miy", "hxs", "Eww", "mij", "hww", "qyj", "hwi", "Eyy", "hyy", "Eyj", - "hyj", "vFk", "xmw", "ytj", "qdA", "vEs", "xmi", "qck", "vEg", "xmb", "qcc", "vEa", "qcE", - "qcC", "mFk", "tCw", "wlj", "qhk", "mEs", "tCi", "gtA", "Eck", "vai", "tCb", "gsk", "Ecc", - "mEa", "gsc", "qga", "mED", "EcC", "Ehk", "maw", "tDj", "gxk", "Egs", "mai", "gws", "qii", - "mab", "gwg", "Ega", "EgD", "Eiw", "mbj", "gyw", "Eii", "gyi", "Eib", "gyb", "gzj", "qFA", - "vCs", "xli", "qEk", "vCg", "xlb", "qEc", "vCa", "qEE", "vCD", "qEC", "qEB", "EFA", "mCs", - "tBi", "ghA", "EEk", "mCg", "tBb", "ggk", "qag", "vDb", "ggc", "EEE", "mCD", "ggE", "qaD", - "ggC", "Eas", "mDi", "gis", "Eag", "mDb", "gig", "qbb", "gia", "EaD", "giD", "gji", "gjb", - "qCk", "vBg", "xkr", "qCc", "vBa", "qCE", "vBD", "qCC", "qCB", "ECk", "mBg", "tAr", "gak", - "ECc", "mBa", "gac", "qDa", "mBD", "gaE", "ECC", "gaC", "ECB", "EDg", "gbg", "gba", "gbD", - "vAq", "vAn", "qBB", "mAq", "EBE", "gDE", "gDC", "gDB", "lfA", "sps", "wey", "ldk", "sow", - "ClA", "lcs", "soi", "Ckk", "lcg", "Ckc", "CkE", "CvA", "lhs", "sqy", "Ctk", "lgw", "sqj", - "Css", "lgi", "Csg", "Csa", "Cxs", "liy", "Cww", "lij", "Cwi", "Cyy", "Cyj", "tpk", "wuw", - "yhj", "ndA", "tos", "wui", "nck", "tog", "wub", "ncc", "toa", "ncE", "toD", "lFk", "smw", - "wdj", "nhk", "lEs", "smi", "atA", "Cck", "tqi", "smb", "ask", "ngg", "lEa", "asc", "CcE", - "asE", "Chk", "law", "snj", "axk", "Cgs", "trj", "aws", "nii", "lab", "awg", "Cga", "awa", - "Ciw", "lbj", "ayw", "Cii", "ayi", "Cib", "Cjj", "azj", "vpA", "xus", "yxi", "vok", "xug", - "yxb", "voc", "xua", "voE", "xuD", "voC", "nFA", "tms", "wti", "rhA", "nEk", "xvi", "wtb", - "rgk", "vqg", "xvb", "rgc", "nEE", "tmD", "rgE", "vqD", "nEB", "CFA", "lCs", "sli", "ahA", - "CEk", "lCg", "slb", "ixA", "agk", "nag", "tnb", "iwk", "rig", "vrb", "lCD", "iwc", "agE", - "naD", "iwE", "CEB", "Cas", "lDi", "ais", "Cag", "lDb", "iys", "aig", "nbb", "iyg", "rjb", - "CaD", "aiD", "Cbi", "aji", "Cbb", "izi", "ajb", "vmk", "xtg", "ywr", "vmc", "xta", "vmE", - "xtD", "vmC", "vmB", "nCk", "tlg", "wsr", "rak", "nCc", "xtr", "rac", "vna", "tlD", "raE", - "nCC", "raC", "nCB", "raB", "CCk", "lBg", "skr", "aak", "CCc", "lBa", "iik", "aac", "nDa", - "lBD", "iic", "rba", "CCC", "iiE", "aaC", "CCB", "aaB", "CDg", "lBr", "abg", "CDa", "ijg", - "aba", "CDD", "ija", "abD", "CDr", "ijr", "vlc", "xsq", "vlE", "xsn", "vlC", "vlB", "nBc", - "tkq", "rDc", "nBE", "tkn", "rDE", "vln", "rDC", "nBB", "rDB", "CBc", "lAq", "aDc", "CBE", - "lAn", "ibc", "aDE", "nBn", "ibE", "rDn", "CBB", "ibC", "aDB", "ibB", "aDq", "ibq", "ibn", - "xsf", "vkl", "tkf", "nAm", "nAl", "CAo", "aBo", "iDo", "CAl", "aBl", "kpk", "BdA", "kos", - "Bck", "kog", "seb", "Bcc", "koa", "BcE", "koD", "Bhk", "kqw", "sfj", "Bgs", "kqi", "Bgg", - "kqb", "Bga", "BgD", "Biw", "krj", "Bii", "Bib", "Bjj", "lpA", "sus", "whi", "lok", "sug", - "loc", "sua", "loE", "suD", "loC", "BFA", "kms", "sdi", "DhA", "BEk", "svi", "sdb", "Dgk", - "lqg", "svb", "Dgc", "BEE", "kmD", "DgE", "lqD", "BEB", "Bas", "kni", "Dis", "Bag", "knb", - "Dig", "lrb", "Dia", "BaD", "Bbi", "Dji", "Bbb", "Djb", "tuk", "wxg", "yir", "tuc", "wxa", - "tuE", "wxD", "tuC", "tuB", "lmk", "stg", "nqk", "lmc", "sta", "nqc", "tva", "stD", "nqE", - "lmC", "nqC", "lmB", "nqB", "BCk", "klg", "Dak", "BCc", "str", "bik", "Dac", "lna", "klD", - "bic", "nra", "BCC", "biE", "DaC", "BCB", "DaB", "BDg", "klr", "Dbg", "BDa", "bjg", "Dba", - "BDD", "bja", "DbD", "BDr", "Dbr", "bjr", "xxc", "yyq", "xxE", "yyn", "xxC", "xxB", "ttc", - "wwq", "vvc", "xxq", "wwn", "vvE", "xxn", "vvC", "ttB", "vvB", "llc", "ssq", "nnc", "llE", - "ssn", "rrc", "nnE", "ttn", "rrE", "vvn", "llB", "rrC", "nnB", "rrB", "BBc", "kkq", "DDc", - "BBE", "kkn", "bbc", "DDE", "lln", "jjc", "bbE", "nnn", "BBB", "jjE", "rrn", "DDB", "jjC", - "BBq", "DDq", "BBn", "bbq", "DDn", "jjq", "bbn", "jjn", "xwo", "yyf", "xwm", "xwl", "tso", - "wwf", "vto", "xwv", "vtm", "tsl", "vtl", "lko", "ssf", "nlo", "lkm", "rno", "nlm", "lkl", - "rnm", "nll", "rnl", "BAo", "kkf", "DBo", "lkv", "bDo", "DBm", "BAl", "jbo", "bDm", "DBl", - "jbm", "bDl", "jbl", "DBv", "jbv", "xwd", "vsu", "vst", "nku", "rlu", "rlt", "DAu", "bBu", - "jDu", "jDt", "ApA", "Aok", "keg", "Aoc", "AoE", "AoC", "Aqs", "Aqg", "Aqa", "AqD", "Ari", - "Arb", "kuk", "kuc", "sha", "kuE", "shD", "kuC", "kuB", "Amk", "kdg", "Bqk", "kvg", "kda", - "Bqc", "kva", "BqE", "kvD", "BqC", "AmB", "BqB", "Ang", "kdr", "Brg", "kvr", "Bra", "AnD", - "BrD", "Anr", "Brr", "sxc", "sxE", "sxC", "sxB", "ktc", "lvc", "sxq", "sgn", "lvE", "sxn", - "lvC", "ktB", "lvB", "Alc", "Bnc", "AlE", "kcn", "Drc", "BnE", "AlC", "DrE", "BnC", "AlB", - "DrC", "BnB", "Alq", "Bnq", "Aln", "Drq", "Bnn", "Drn", "wyo", "wym", "wyl", "swo", "txo", - "wyv", "txm", "swl", "txl", "kso", "sgf", "lto", "swv", "nvo", "ltm", "ksl", "nvm", "ltl", - "nvl", "Ako", "kcf", "Blo", "ksv", "Dno", "Blm", "Akl", "bro", "Dnm", "Bll", "brm", "Dnl", - "Akv", "Blv", "Dnv", "brv", "yze", "yzd", "wye", "xyu", "wyd", "xyt", "swe", "twu", "swd", - "vxu", "twt", "vxt", "kse", "lsu", "ksd", "ntu", "lst", "rvu", "ypk", "zew", "xdA", "yos", - "zei", "xck", "yog", "zeb", "xcc", "yoa", "xcE", "yoD", "xcC", "xhk", "yqw", "zfj", "utA", - "xgs", "yqi", "usk", "xgg", "yqb", "usc", "xga", "usE", "xgD", "usC", "uxk", "xiw", "yrj", - "ptA", "uws", "xii", "psk", "uwg", "xib", "psc", "uwa", "psE", "uwD", "psC", "pxk", "uyw", - "xjj", "ftA", "pws", "uyi", "fsk", "pwg", "uyb", "fsc", "pwa", "fsE", "pwD", "fxk", "pyw", - "uzj", "fws", "pyi", "fwg", "pyb", "fwa", "fyw", "pzj", "fyi", "fyb", "xFA", "yms", "zdi", - "xEk", "ymg", "zdb", "xEc", "yma", "xEE", "ymD", "xEC", "xEB", "uhA", "xas", "yni", "ugk", - "xag", "ynb", "ugc", "xaa", "ugE", "xaD", "ugC", "ugB", "oxA", "uis", "xbi", "owk", "uig", - "xbb", "owc", "uia", "owE", "uiD", "owC", "owB", "dxA", "oys", "uji", "dwk", "oyg", "ujb", - "dwc", "oya", "dwE", "oyD", "dwC", "dys", "ozi", "dyg", "ozb", "dya", "dyD", "dzi", "dzb", - "xCk", "ylg", "zcr", "xCc", "yla", "xCE", "ylD", "xCC", "xCB", "uak", "xDg", "ylr", "uac", - "xDa", "uaE", "xDD", "uaC", "uaB", "oik", "ubg", "xDr", "oic", "uba", "oiE", "ubD", "oiC", - "oiB", "cyk", "ojg", "ubr", "cyc", "oja", "cyE", "ojD", "cyC", "cyB", "czg", "ojr", "cza", - "czD", "czr", "xBc", "ykq", "xBE", "ykn", "xBC", "xBB", "uDc", "xBq", "uDE", "xBn", "uDC", - "uDB", "obc", "uDq", "obE", "uDn", "obC", "obB", "cjc", "obq", "cjE", "obn", "cjC", "cjB", - "cjq", "cjn", "xAo", "ykf", "xAm", "xAl", "uBo", "xAv", "uBm", "uBl", "oDo", "uBv", "oDm", - "oDl", "cbo", "oDv", "cbm", "cbl", "xAe", "xAd", "uAu", "uAt", "oBu", "oBt", "wpA", "yes", - "zFi", "wok", "yeg", "zFb", "woc", "yea", "woE", "yeD", "woC", "woB", "thA", "wqs", "yfi", - "tgk", "wqg", "yfb", "tgc", "wqa", "tgE", "wqD", "tgC", "tgB", "mxA", "tis", "wri", "mwk", - "tig", "wrb", "mwc", "tia", "mwE", "tiD", "mwC", "mwB", "FxA", "mys", "tji", "Fwk", "myg", - "tjb", "Fwc", "mya", "FwE", "myD", "FwC", "Fys", "mzi", "Fyg", "mzb", "Fya", "FyD", "Fzi", - "Fzb", "yuk", "zhg", "hjs", "yuc", "zha", "hbw", "yuE", "zhD", "hDy", "yuC", "yuB", "wmk", - "ydg", "zEr", "xqk", "wmc", "zhr", "xqc", "yva", "ydD", "xqE", "wmC", "xqC", "wmB", "xqB", - "tak", "wng", "ydr", "vik", "tac", "wna", "vic", "xra", "wnD", "viE", "taC", "viC", "taB", - "viB", "mik", "tbg", "wnr", "qyk", "mic", "tba", "qyc", "vja", "tbD", "qyE", "miC", "qyC", - "miB", "qyB", "Eyk", "mjg", "tbr", "hyk", "Eyc", "mja", "hyc", "qza", "mjD", "hyE", "EyC", - "hyC", "EyB", "Ezg", "mjr", "hzg", "Eza", "hza", "EzD", "hzD", "Ezr", "ytc", "zgq", "grw", - "ytE", "zgn", "gny", "ytC", "glz", "ytB", "wlc", "ycq", "xnc", "wlE", "ycn", "xnE", "ytn", - "xnC", "wlB", "xnB", "tDc", "wlq", "vbc", "tDE", "wln", "vbE", "xnn", "vbC", "tDB", "vbB", - "mbc", "tDq", "qjc", "mbE", "tDn", "qjE", "vbn", "qjC", "mbB", "qjB", "Ejc", "mbq", "gzc", - "EjE", "mbn", "gzE", "qjn", "gzC", "EjB", "gzB", "Ejq", "gzq", "Ejn", "gzn", "yso", "zgf", - "gfy", "ysm", "gdz", "ysl", "wko", "ycf", "xlo", "ysv", "xlm", "wkl", "xll", "tBo", "wkv", - "vDo", "tBm", "vDm", "tBl", "vDl", "mDo", "tBv", "qbo", "vDv", "qbm", "mDl", "qbl", "Ebo", - "mDv", "gjo", "Ebm", "gjm", "Ebl", "gjl", "Ebv", "gjv", "yse", "gFz", "ysd", "wke", "xku", - "wkd", "xkt", "tAu", "vBu", "tAt", "vBt", "mBu", "qDu", "mBt", "qDt", "EDu", "gbu", "EDt", - "gbt", "ysF", "wkF", "xkh", "tAh", "vAx", "mAx", "qBx", "wek", "yFg", "zCr", "wec", "yFa", - "weE", "yFD", "weC", "weB", "sqk", "wfg", "yFr", "sqc", "wfa", "sqE", "wfD", "sqC", "sqB", - "lik", "srg", "wfr", "lic", "sra", "liE", "srD", "liC", "liB", "Cyk", "ljg", "srr", "Cyc", - "lja", "CyE", "ljD", "CyC", "CyB", "Czg", "ljr", "Cza", "CzD", "Czr", "yhc", "zaq", "arw", - "yhE", "zan", "any", "yhC", "alz", "yhB", "wdc", "yEq", "wvc", "wdE", "yEn", "wvE", "yhn", - "wvC", "wdB", "wvB", "snc", "wdq", "trc", "snE", "wdn", "trE", "wvn", "trC", "snB", "trB", - "lbc", "snq", "njc", "lbE", "snn", "njE", "trn", "njC", "lbB", "njB", "Cjc", "lbq", "azc", - "CjE", "lbn", "azE", "njn", "azC", "CjB", "azB", "Cjq", "azq", "Cjn", "azn", "zio", "irs", - "rfy", "zim", "inw", "rdz", "zil", "ily", "ikz", "ygo", "zaf", "afy", "yxo", "ziv", "ivy", - "adz", "yxm", "ygl", "itz", "yxl", "wco", "yEf", "wto", "wcm", "xvo", "yxv", "wcl", "xvm", - "wtl", "xvl", "slo", "wcv", "tno", "slm", "vro", "tnm", "sll", "vrm", "tnl", "vrl", "lDo", - "slv", "nbo", "lDm", "rjo", "nbm", "lDl", "rjm", "nbl", "rjl", "Cbo", "lDv", "ajo", "Cbm", - "izo", "ajm", "Cbl", "izm", "ajl", "izl", "Cbv", "ajv", "zie", "ifw", "rFz", "zid", "idy", - "icz", "yge", "aFz", "ywu", "ygd", "ihz", "ywt", "wce", "wsu", "wcd", "xtu", "wst", "xtt", - "sku", "tlu", "skt", "vnu", "tlt", "vnt", "lBu", "nDu", "lBt", "rbu", "nDt", "rbt", "CDu", - "abu", "CDt", "iju", "abt", "ijt", "ziF", "iFy", "iEz", "ygF", "ywh", "wcF", "wsh", "xsx", - "skh", "tkx", "vlx", "lAx", "nBx", "rDx", "CBx", "aDx", "ibx", "iCz", "wFc", "yCq", "wFE", - "yCn", "wFC", "wFB", "sfc", "wFq", "sfE", "wFn", "sfC", "sfB", "krc", "sfq", "krE", "sfn", - "krC", "krB", "Bjc", "krq", "BjE", "krn", "BjC", "BjB", "Bjq", "Bjn", "yao", "zDf", "Dfy", - "yam", "Ddz", "yal", "wEo", "yCf", "who", "wEm", "whm", "wEl", "whl", "sdo", "wEv", "svo", - "sdm", "svm", "sdl", "svl", "kno", "sdv", "lro", "knm", "lrm", "knl", "lrl", "Bbo", "knv", - "Djo", "Bbm", "Djm", "Bbl", "Djl", "Bbv", "Djv", "zbe", "bfw", "npz", "zbd", "bdy", "bcz", - "yae", "DFz", "yiu", "yad", "bhz", "yit", "wEe", "wgu", "wEd", "wxu", "wgt", "wxt", "scu", - "stu", "sct", "tvu", "stt", "tvt", "klu", "lnu", "klt", "nru", "lnt", "nrt", "BDu", "Dbu", - "BDt", "bju", "Dbt", "bjt", "jfs", "rpy", "jdw", "roz", "jcy", "jcj", "zbF", "bFy", "zjh", - "jhy", "bEz", "jgz", "yaF", "yih", "yyx", "wEF", "wgh", "wwx", "xxx", "sch", "ssx", "ttx", - "vvx", "kkx", "llx", "nnx", "rrx", "BBx", "DDx", "bbx", "jFw", "rmz", "jEy", "jEj", "bCz", - "jaz", "jCy", "jCj", "jBj", "wCo", "wCm", "wCl", "sFo", "wCv", "sFm", "sFl", "kfo", "sFv", - "kfm", "kfl", "Aro", "kfv", "Arm", "Arl", "Arv", "yDe", "Bpz", "yDd", "wCe", "wau", "wCd", - "wat", "sEu", "shu", "sEt", "sht", "kdu", "kvu", "kdt", "kvt", "Anu", "Bru", "Ant", "Brt", - "zDp", "Dpy", "Doz", "yDF", "ybh", "wCF", "wah", "wix", "sEh", "sgx", "sxx", "kcx", "ktx", - "lvx", "Alx", "Bnx", "Drx", "bpw", "nuz", "boy", "boj", "Dmz", "bqz", "jps", "ruy", "jow", - "ruj", "joi", "job", "bmy", "jqy", "bmj", "jqj", "jmw", "rtj", "jmi", "jmb", "blj", "jnj", - "jli", "jlb", "jkr", "sCu", "sCt", "kFu", "kFt", "Afu", "Aft", "wDh", "sCh", "sax", "kEx", - "khx", "Adx", "Avx", "Buz", "Duy", "Duj", "buw", "nxj", "bui", "bub", "Dtj", "bvj", "jus", - "rxi", "jug", "rxb", "jua", "juD", "bti", "jvi", "btb", "jvb", "jtg", "rwr", "jta", "jtD", - "bsr", "jtr", "jsq", "jsn", "Bxj", "Dxi", "Dxb", "bxg", "nyr", "bxa", "bxD", "Dwr", "bxr", - "bwq", "bwn", "pjk", "urw", "ejA", "pbs", "uny", "ebk", "pDw", "ulz", "eDs", "pBy", "eBw", - "zfc", "fjk", "prw", "zfE", "fbs", "pny", "zfC", "fDw", "plz", "zfB", "fBy", "yrc", "zfq", - "frw", "yrE", "zfn", "fny", "yrC", "flz", "yrB", "xjc", "yrq", "xjE", "yrn", "xjC", "xjB", - "uzc", "xjq", "uzE", "xjn", "uzC", "uzB", "pzc", "uzq", "pzE", "uzn", "pzC", "djA", "ors", - "ufy", "dbk", "onw", "udz", "dDs", "oly", "dBw", "okz", "dAy", "zdo", "drs", "ovy", "zdm", - "dnw", "otz", "zdl", "dly", "dkz", "yno", "zdv", "dvy", "ynm", "dtz", "ynl", "xbo", "ynv", - "xbm", "xbl", "ujo", "xbv", "ujm", "ujl", "ozo", "ujv", "ozm", "ozl", "crk", "ofw", "uFz", - "cns", "ody", "clw", "ocz", "cky", "ckj", "zcu", "cvw", "ohz", "zct", "cty", "csz", "ylu", - "cxz", "ylt", "xDu", "xDt", "ubu", "ubt", "oju", "ojt", "cfs", "oFy", "cdw", "oEz", "ccy", - "ccj", "zch", "chy", "cgz", "ykx", "xBx", "uDx", "cFw", "oCz", "cEy", "cEj", "caz", "cCy", - "cCj", "FjA", "mrs", "tfy", "Fbk", "mnw", "tdz", "FDs", "mly", "FBw", "mkz", "FAy", "zFo", - "Frs", "mvy", "zFm", "Fnw", "mtz", "zFl", "Fly", "Fkz", "yfo", "zFv", "Fvy", "yfm", "Ftz", - "yfl", "wro", "yfv", "wrm", "wrl", "tjo", "wrv", "tjm", "tjl", "mzo", "tjv", "mzm", "mzl", - "qrk", "vfw", "xpz", "hbA", "qns", "vdy", "hDk", "qlw", "vcz", "hBs", "qky", "hAw", "qkj", - "hAi", "Erk", "mfw", "tFz", "hrk", "Ens", "mdy", "hns", "qty", "mcz", "hlw", "Eky", "hky", - "Ekj", "hkj", "zEu", "Evw", "mhz", "zhu", "zEt", "hvw", "Ety", "zht", "hty", "Esz", "hsz", - "ydu", "Exz", "yvu", "ydt", "hxz", "yvt", "wnu", "xru", "wnt", "xrt", "tbu", "vju", "tbt", - "vjt", "mju", "mjt", "grA", "qfs", "vFy", "gnk", "qdw", "vEz", "gls", "qcy", "gkw", "qcj", - "gki", "gkb", "Efs", "mFy", "gvs", "Edw", "mEz", "gtw", "qgz", "gsy", "Ecj", "gsj", "zEh", - "Ehy", "zgx", "gxy", "Egz", "gwz", "ycx", "ytx", "wlx", "xnx", "tDx", "vbx", "mbx", "gfk", - "qFw", "vCz", "gds", "qEy", "gcw", "qEj", "gci", "gcb", "EFw", "mCz", "ghw", "EEy", "ggy", - "EEj", "ggj", "Eaz", "giz", "gFs", "qCy", "gEw", "qCj", "gEi", "gEb", "ECy", "gay", "ECj", - "gaj", "gCw", "qBj", "gCi", "gCb", "EBj", "gDj", "gBi", "gBb", "Crk", "lfw", "spz", "Cns", - "ldy", "Clw", "lcz", "Cky", "Ckj", "zCu", "Cvw", "lhz", "zCt", "Cty", "Csz", "yFu", "Cxz", - "yFt", "wfu", "wft", "sru", "srt", "lju", "ljt", "arA", "nfs", "tpy", "ank", "ndw", "toz", - "als", "ncy", "akw", "ncj", "aki", "akb", "Cfs", "lFy", "avs", "Cdw", "lEz", "atw", "ngz", - "asy", "Ccj", "asj", "zCh", "Chy", "zax", "axy", "Cgz", "awz", "yEx", "yhx", "wdx", "wvx", - "snx", "trx", "lbx", "rfk", "vpw", "xuz", "inA", "rds", "voy", "ilk", "rcw", "voj", "iks", - "rci", "ikg", "rcb", "ika", "afk", "nFw", "tmz", "ivk", "ads", "nEy", "its", "rgy", "nEj", - "isw", "aci", "isi", "acb", "isb", "CFw", "lCz", "ahw", "CEy", "ixw", "agy", "CEj", "iwy", - "agj", "iwj", "Caz", "aiz", "iyz", "ifA", "rFs", "vmy", "idk", "rEw", "vmj", "ics", "rEi", - "icg", "rEb", "ica", "icD", "aFs", "nCy", "ihs", "aEw", "nCj", "igw", "raj", "igi", "aEb", - "igb", "CCy", "aay", "CCj", "iiy", "aaj", "iij", "iFk", "rCw", "vlj", "iEs", "rCi", "iEg", - "rCb", "iEa", "iED", "aCw", "nBj", "iaw", "aCi", "iai", "aCb", "iab", "CBj", "aDj", "ibj", - "iCs", "rBi", "iCg", "rBb", "iCa", "iCD", "aBi", "iDi", "aBb", "iDb", "iBg", "rAr", "iBa", - "iBD", "aAr", "iBr", "iAq", "iAn", "Bfs", "kpy", "Bdw", "koz", "Bcy", "Bcj", "Bhy", "Bgz", - "yCx", "wFx", "sfx", "krx", "Dfk", "lpw", "suz", "Dds", "loy", "Dcw", "loj", "Dci", "Dcb", - "BFw", "kmz", "Dhw", "BEy", "Dgy", "BEj", "Dgj", "Baz", "Diz", "bfA", "nps", "tuy", "bdk", - "now", "tuj", "bcs", "noi", "bcg", "nob", "bca", "bcD", "DFs", "lmy", "bhs", "DEw", "lmj", - "bgw", "DEi", "bgi", "DEb", "bgb", "BCy", "Day", "BCj", "biy", "Daj", "bij", "rpk", "vuw", - "xxj", "jdA", "ros", "vui", "jck", "rog", "vub", "jcc", "roa", "jcE", "roD", "jcC", "bFk", - "nmw", "ttj", "jhk", "bEs", "nmi", "jgs", "rqi", "nmb", "jgg", "bEa", "jga", "bED", "jgD", - "DCw", "llj", "baw", "DCi", "jiw", "bai", "DCb", "jii", "bab", "jib", "BBj", "DDj", "bbj", - "jjj", "jFA", "rms", "vti", "jEk", "rmg", "vtb", "jEc", "rma", "jEE", "rmD", "jEC", "jEB", - "bCs", "nli", "jas", "bCg", "nlb", "jag", "rnb", "jaa", "bCD", "jaD", "DBi", "bDi", "DBb", - "jbi", "bDb", "jbb", "jCk", "rlg", "vsr", "jCc", "rla", "jCE", "rlD", "jCC", "jCB", "bBg", - "nkr", "jDg", "bBa", "jDa", "bBD", "jDD", "DAr", "bBr", "jDr", "jBc", "rkq", "jBE", "rkn", - "jBC", "jBB", "bAq", "jBq", "bAn", "jBn", "jAo", "rkf", "jAm", "jAl", "bAf", "jAv", "Apw", - "kez", "Aoy", "Aoj", "Aqz", "Bps", "kuy", "Bow", "kuj", "Boi", "Bob", "Amy", "Bqy", "Amj", - "Bqj", "Dpk", "luw", "sxj", "Dos", "lui", "Dog", "lub", "Doa", "DoD", "Bmw", "ktj", "Dqw", - "Bmi", "Dqi", "Bmb", "Dqb", "Alj", "Bnj", "Drj", "bpA", "nus", "txi", "bok", "nug", "txb", - "boc", "nua", "boE", "nuD", "boC", "boB", "Dms", "lti", "bqs", "Dmg", "ltb", "bqg", "nvb", - "bqa", "DmD", "bqD", "Bli", "Dni", "Blb", "bri", "Dnb", "brb", "ruk", "vxg", "xyr", "ruc", - "vxa", "ruE", "vxD", "ruC", "ruB", "bmk", "ntg", "twr", "jqk", "bmc", "nta", "jqc", "rva", - "ntD", "jqE", "bmC", "jqC", "bmB", "jqB", "Dlg", "lsr", "bng", "Dla", "jrg", "bna", "DlD", - "jra", "bnD", "jrD", "Bkr", "Dlr", "bnr", "jrr", "rtc", "vwq", "rtE", "vwn", "rtC", "rtB", - "blc", "nsq", "jnc", "blE", "nsn", "jnE", "rtn", "jnC", "blB", "jnB", "Dkq", "blq", "Dkn", - "jnq", "bln", "jnn", "rso", "vwf", "rsm", "rsl", "bko", "nsf", "jlo", "bkm", "jlm", "bkl", - "jll", "Dkf", "bkv", "jlv", "rse", "rsd", "bke", "jku", "bkd", "jkt", "Aey", "Aej", "Auw", - "khj", "Aui", "Aub", "Adj", "Avj", "Bus", "kxi", "Bug", "kxb", "Bua", "BuD", "Ati", "Bvi", - "Atb", "Bvb", "Duk", "lxg", "syr", "Duc", "lxa", "DuE", "lxD", "DuC", "DuB", "Btg", "kwr", - "Dvg", "lxr", "Dva", "BtD", "DvD", "Asr", "Btr", "Dvr", "nxc", "tyq", "nxE", "tyn", "nxC", - "nxB", "Dtc", "lwq", "bvc", "nxq", "lwn", "bvE", "DtC", "bvC", "DtB", "bvB", "Bsq", "Dtq", - "Bsn", "bvq", "Dtn", "bvn", "vyo", "xzf", "vym", "vyl", "nwo", "tyf", "rxo", "nwm", "rxm", - "nwl", "rxl", "Dso", "lwf", "bto", "Dsm", "jvo", "btm", "Dsl", "jvm", "btl", "jvl", "Bsf", - "Dsv", "btv", "jvv", "vye", "vyd", "nwe", "rwu", "nwd", "rwt", "Dse", "bsu", "Dsd", "jtu", - "bst", "jtt", "vyF", "nwF", "rwh", "DsF", "bsh", "jsx", "Ahi", "Ahb", "Axg", "kir", "Axa", - "AxD", "Agr", "Axr", "Bxc", "kyq", "BxE", "kyn", "BxC", "BxB", "Awq", "Bxq", "Awn", "Bxn", - "lyo", "szf", "lym", "lyl", "Bwo", "kyf", "Dxo", "lyv", "Dxm", "Bwl", "Dxl", "Awf", "Bwv", - "Dxv", "tze", "tzd", "lye", "nyu", "lyd", "nyt", "Bwe", "Dwu", "Bwd", "bxu", "Dwt", "bxt", - "tzF", "lyF", "nyh", "BwF", "Dwh", "bwx", "Aiq", "Ain", "Ayo", "kjf", "Aym", "Ayl", "Aif", - "Ayv", "kze", "kzd", "Aye", "Byu", "Ayd", "Byt", "szp" - }; - - private static final char[] BR_SET = { - 'A', 'B', 'C', 'D', 'E', 'F', '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', '*', '+', '-' - }; - - private static final String[] PDF_TTF = { - "00000", "00001", "00010", "00011", "00100", "00101", "00110", "00111", - "01000", "01001", "01010", "01011", "01100", "01101", "01110", "01111", "10000", "10001", - "10010", "10011", "10100", "10101", "10110", "10111", "11000", "11001", "11010", - "11011", "11100", "11101", "11110", "11111", "01", "1111111101010100", "11111101000101001" - }; - - private static final int[] ASCII_X = { - 7, 8, 8, 4, 12, 4, 4, 8, 8, 8, 12, 4, 12, 12, 12, 12, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 12, 8, 8, 4, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 8, 8, 8, 4, 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 8, 8, 8, 8 - }; - - private static final int[] ASCII_Y = { - 26, 10, 20, 15, 18, 21, 10, 28, 23, 24, 22, 20, 13, 16, 17, 19, 0, 1, 2, 3, - 4, 5, 6, 7, 8, 9, 14, 0, 1, 23, 2, 25, 3, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 4, 5, 6, 24, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 21, 27, 9 - }; - - private static final int[] MICRO_AUTOSIZE = { - 4, 6, 7, 8, 8, 10, 10, 12, 12, 13, 14, 16, 18, 18, 19, 20, 24, 24, 24, 29, 30, 33, 34, 37, 39, 46, 54, 58, 70, 72, 82, 90, 108, 126, // max codeword counts - 1, 14, 2, 7, 24, 3, 15, 25, 4, 8, 16, 5, 17, 26, 9, 6, 10, 18, 27, 11, 28, 12, 19, 13, 29, 20, 30, 21, 22, 31, 23, 32, 33, 34 // corresponding variant - }; - - /* Rows, columns, error codewords, k-offset of valid MicroPDF417 sizes from ISO/IEC 24728:2006 */ - private static final int[] MICRO_VARIANTS = { - 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // columns - 11, 14, 17, 20, 24, 28, 8, 11, 14, 17, 20, 23, 26, 6, 8, 10, 12, 15, 20, 26, 32, 38, 44, 4, 6, 8, 10, 12, 15, 20, 26, 32, 38, 44, // rows - 7, 7, 7, 8, 8, 8, 8, 9, 9, 10, 11, 13, 15, 12, 14, 16, 18, 21, 26, 32, 38, 44, 50, 8, 12, 14, 16, 18, 21, 26, 32, 38, 44, 50, // k (EC codewords) - 0, 0, 0, 7, 7, 7, 7, 15, 15, 24, 34, 57, 84, 45, 70, 99, 115, 133, 154, 180, 212, 250, 294, 7, 45, 70, 99, 115, 133, 154, 180, 212, 250, 294 // offset - }; - - /* Following is Left RAP, Centre RAP, Right RAP and Start Cluster from ISO/IEC 24728:2006 tables 10, 11 and 12 */ - private static final int[] RAP_TABLE = { - 1, 8, 36, 19, 9, 25, 1, 1, 8, 36, 19, 9, 27, 1, 7, 15, 25, 37, 1, 1, 21, 15, 1, 47, 1, 7, 15, 25, 37, 1, 1, 21, 15, 1, // left RAP - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 7, 15, 25, 37, 17, 9, 29, 31, 25, 19, 1, 7, 15, 25, 37, 17, 9, 29, 31, 25, // centre RAP - 9, 8, 36, 19, 17, 33, 1, 9, 8, 36, 19, 17, 35, 1, 7, 15, 25, 37, 33, 17, 37, 47, 49, 43, 1, 7, 15, 25, 37, 33, 17, 37, 47, 49, // right RAP - 0, 3, 6, 0, 6, 0, 0, 0, 3, 6, 0, 6, 6, 0, 0, 6, 0, 0, 0, 0, 6, 6, 0, 3, 0, 0, 6, 0, 0, 0, 0, 6, 6, 0 // start cluster - }; - - /* Left and Right Row Address Pattern from Table 2 */ - private static final String[] RAPLR = { - "", "221311", "311311", "312211", "222211", "213211", "214111", "223111", - "313111", "322111", "412111", "421111", "331111", "241111", "232111", "231211", "321211", - "411211", "411121", "411112", "321112", "312112", "311212", "311221", "311131", "311122", - "311113", "221113", "221122", "221131", "221221", "222121", "312121", "321121", "231121", - "231112", "222112", "213112", "212212", "212221", "212131", "212122", "212113", "211213", - "211123", "211132", "211141", "211231", "211222", "211312", "211321", "211411", "212311" - }; - - /* Centre Row Address Pattern from Table 2 */ - private static final String[] RAPC = { - "", "112231", "121231", "122131", "131131", "131221", "132121", "141121", - "141211", "142111", "133111", "132211", "131311", "122311", "123211", "124111", "115111", - "114211", "114121", "123121", "123112", "122212", "122221", "121321", "121411", "112411", - "113311", "113221", "113212", "113122", "122122", "131122", "131113", "122113", "113113", - "112213", "112222", "112312", "112321", "111421", "111331", "111322", "111232", "111223", - "111133", "111124", "111214", "112114", "121114", "121123", "121132", "112132", "112141" - }; - - /* MicroPDF417 coefficients from ISO/IEC 24728:2006 Annex F */ - private static final int[] MICRO_COEFFS = { - /* k = 7 */ - 76, 925, 537, 597, 784, 691, 437, - - /* k = 8 */ - 237, 308, 436, 284, 646, 653, 428, 379, - - /* k = 9 */ - 567, 527, 622, 257, 289, 362, 501, 441, 205, - - /* k = 10 */ - 377, 457, 64, 244, 826, 841, 818, 691, 266, 612, - - /* k = 11 */ - 462, 45, 565, 708, 825, 213, 15, 68, 327, 602, 904, - - /* k = 12 */ - 597, 864, 757, 201, 646, 684, 347, 127, 388, 7, 69, 851, - - /* k = 13 */ - 764, 713, 342, 384, 606, 583, 322, 592, 678, 204, 184, 394, 692, - - /* k = 14 */ - 669, 677, 154, 187, 241, 286, 274, 354, 478, 915, 691, 833, 105, 215, - - /* k = 15 */ - 460, 829, 476, 109, 904, 664, 230, 5, 80, 74, 550, 575, 147, 868, 642, - - /* k = 16 */ - 274, 562, 232, 755, 599, 524, 801, 132, 295, 116, 442, 428, 295, 42, 176, 65, - - /* k = 18 */ - 279, 577, 315, 624, 37, 855, 275, 739, 120, 297, 312, 202, 560, 321, 233, 756, - 760, 573, - - /* k = 21 */ - 108, 519, 781, 534, 129, 425, 681, 553, 422, 716, 763, 693, 624, 610, 310, 691, - 347, 165, 193, 259, 568, - - /* k = 26 */ - 443, 284, 887, 544, 788, 93, 477, 760, 331, 608, 269, 121, 159, 830, 446, 893, - 699, 245, 441, 454, 325, 858, 131, 847, 764, 169, - - /* k = 32 */ - 361, 575, 922, 525, 176, 586, 640, 321, 536, 742, 677, 742, 687, 284, 193, 517, - 273, 494, 263, 147, 593, 800, 571, 320, 803, 133, 231, 390, 685, 330, 63, 410, - - /* k = 38 */ - 234, 228, 438, 848, 133, 703, 529, 721, 788, 322, 280, 159, 738, 586, 388, 684, - 445, 680, 245, 595, 614, 233, 812, 32, 284, 658, 745, 229, 95, 689, 920, 771, - 554, 289, 231, 125, 117, 518, - - /* k = 44 */ - 476, 36, 659, 848, 678, 64, 764, 840, 157, 915, 470, 876, 109, 25, 632, 405, - 417, 436, 714, 60, 376, 97, 413, 706, 446, 21, 3, 773, 569, 267, 272, 213, - 31, 560, 231, 758, 103, 271, 572, 436, 339, 730, 82, 285, - - /* k = 50 */ - 923, 797, 576, 875, 156, 706, 63, 81, 257, 874, 411, 416, 778, 50, 205, 303, - 188, 535, 909, 155, 637, 230, 534, 96, 575, 102, 264, 233, 919, 593, 865, 26, - 579, 623, 766, 146, 10, 739, 246, 127, 71, 244, 211, 477, 920, 876, 427, 820, - 718, 435 - }; - - /** - * Creates a new PDF417 symbol instance. - */ - public Pdf417() { - setBarHeight(3); - } - - /** - * Sets the default bar height (height of a single row) for this symbol (default value is 3). - * - * @param barHeight the default bar height for this symbol - */ - @Override - public void setBarHeight(int barHeight) { - super.setBarHeight(barHeight); - } - - /** - * Sets the width of the symbol by specifying the number of columns - * of data codewords. Valid values are 1-30 for PDF417 and 1-4 - * for MicroPDF417. - * - * @param columns the number of data columns in the symbol - */ - public void setDataColumns(int columns) { - this.columns = columns; - } - - /** - * Returns the number of data columns used by this symbol, or {@code null} - * if the number of data columns has not been set. - * - * @return the number of data columns used by this symbol - */ - public Integer getDataColumns() { - return columns; - } - - /** - * Sets the height of the symbol by specifying the number of rows - * of data codewords. Valid values are 3-90 for PDF417 and 4-44 - * for MicroPDF417. - * - * @param rows the number of rows in the symbol - */ - public void setRows(int rows) { - this.rows = rows; - } - - /** - * Returns the number of rows used by this symbol, or {@code null} if - * the number of rows has not been set. - * - * @return the number of rows used by this symbol - */ - public Integer getRows() { - return rows; - } - - /** - * Set the amount of the symbol which is dedicated to error correction - * codewords. The number of codewords of error correction data is - * determined by 2(eccLevel + 1). This attribute is ignored - * when using {@link Mode#MICRO micro} mode. - * - * @param eccLevel level of error correction (0-8) - */ - public void setPreferredEccLevel(int eccLevel) { - if (eccLevel < 0 || eccLevel > 8) { - throw new IllegalArgumentException("ECC level must be between 0 and 8."); - } - preferredEccLevel = eccLevel; - } - - /** - * Returns the preferred error correction level. - * - * @return the preferred error correction level - */ - public int getPreferredEccLevel() { - return preferredEccLevel; - } - - /** - * Forces the use of the specified MicroPDF417 variant. Only valid - * when using {@link Mode#MICRO micro} mode. - * - * @param variant the MicroPDF417 variant to use - */ - public void setVariant(int variant) { - if (symbolMode != Mode.MICRO) { - throw new IllegalArgumentException("Can only set variant when using MICRO mode."); - } - if (variant < 1 || variant > 34) { - throw new IllegalArgumentException("Variant must be between 1 and 34."); - } - this.columns = MICRO_VARIANTS[variant - 1]; - this.rows = MICRO_VARIANTS[variant - 1 + 34]; - } - - /** - * If this PDF417 symbol is part of a series of PDF417 symbols appended in a structured format - * (Macro PDF417), this method sets the position of this symbol in the series. Valid values are - * 1 through 99,999 inclusive. - * - * @param position the position of this PDF417 symbol in the structured append series - */ - public void setStructuredAppendPosition(int position) { - if (position < 1 || position > 99_999) { - throw new IllegalArgumentException("Invalid PDF417 structured append position: " + position); - } - this.structuredAppendPosition = position; - } - - /** - * Returns the position of this PDF417 symbol in a series of symbols using structured append - * (Macro PDF417). If this symbol is not part of such a series, this method will return 1. - * - * @return the position of this PDF417 symbol in a series of symbols using structured append - */ - public int getStructuredAppendPosition() { - return structuredAppendPosition; - } - - /** - * If this PDF417 symbol is part of a series of PDF417 symbols appended in a structured format - * (Macro PDF417), this method sets the total number of symbols in the series. Valid values are - * 1 through 99,999 inclusive. A value of 1 indicates that this symbol is not part of a structured - * append series. - * - * @param total the total number of PDF417 symbols in the structured append series - */ - public void setStructuredAppendTotal(int total) { - if (total < 1 || total > 99_999) { - throw new IllegalArgumentException("Invalid PDF417 structured append total: " + total); - } - this.structuredAppendTotal = total; - } - - /** - * Returns the size of the series of PDF417 symbols using structured append (Macro PDF417) that - * this symbol is part of. If this symbol is not part of a structured append series, this method - * will return 1. - * - * @return size of the series that this symbol is part of - */ - public int getStructuredAppendTotal() { - return structuredAppendTotal; - } - - /** - * If this PDF417 symbol is part of a series of PDF417 symbols appended in a structured format - * (Macro PDF417), this method sets the unique file ID for the series. The value must be numeric, - * and must consist of a series of triplets (3 digits) with values between 000 and 899. - * - * @param structuredAppendFileId the unique file ID for the series that this symbol is part of - */ - public void setStructuredAppendFileId(String structuredAppendFileId) { - if (!structuredAppendFileId.matches("^(?:[0-8][0-9][0-9])+$")) { - throw new IllegalArgumentException("Invalid PDF417 structured append file ID: " + structuredAppendFileId); - } - this.structuredAppendFileId = structuredAppendFileId; - } - - /** - * Returns the unique file ID of the series of PDF417 symbols using structured append (Macro PDF417) - * that this symbol is part of. If this symbol is not part of a structured append series, this method - * will return 000. - * - * @return the unique file ID for the series that this symbol is part of - */ - public String getStructuredAppendFileId() { - return structuredAppendFileId; - } - - /** - * If this PDF417 symbol is part of a series of PDF417 symbols appended in a structured format - * (Macro PDF417), this method sets the optional file name field for the symbol series. - * - * @param structuredAppendFileName the file name for the series that this symbol is part of - */ - public void setStructuredAppendFileName(String structuredAppendFileName) { - this.structuredAppendFileName = structuredAppendFileName; - } - - /** - * Returns the optional file name of the series of PDF417 symbols using structured append (Macro PDF417) - * that this symbol is part of. If this symbol is not part of a structured append series, this method - * will return null. - * - * @return the optional file name for the series that this symbol is part of - */ - public String getStructuredAppendFileName() { - return structuredAppendFileName; - } - - /** - * If this PDF417 symbol is part of a series of PDF417 symbols appended in a structured format - * (Macro PDF417), this method sets whether or not the optional segment count field is included - * in the control block. Since the segment count is optional, it is not included by default. - * - * @param structuredAppendIncludeSegmentCount whether or not the segment count is included - */ - public void setStructuredAppendIncludeSegmentCount(boolean structuredAppendIncludeSegmentCount) { - this.structuredAppendIncludeSegmentCount = structuredAppendIncludeSegmentCount; - } - - /** - * Returns whether or not the segment count is included in the control block if this symbol is part of - * a series of PDF417 symbols appended in a structured format (Macro PDF417). By default, the segment - * count is not included. - * - * @return whether or not the segment count is included - */ - public boolean getStructuredAppendIncludeSegmentCount() { - return structuredAppendIncludeSegmentCount; - } - - /** - * Sets the type of PDF417 symbol created. Defaults to {@link Mode#NORMAL}. - * - * @param mode the type of PDF417 symbol created - */ - public void setMode(Mode mode) { - symbolMode = mode; - } - - /** - * Returns the type of PDF417 symbol created. Defaults to {@link Mode#NORMAL}. - * - * @return the type of PDF417 symbol created - */ - public Mode getMode() { - return symbolMode; - } - - /** - *

Sets the binary data to be encoded and triggers encoding, forcing the use - * of byte compaction mode. Input data will be assumed to be of the type set by - * {@link #setDataType(DataType)}. - * - *

Use {@link #setContent(String)} instead if the data to be encoded is not - * binary data or you do not want to force the use of byte compaction mode. - * - *

NOTE: Forcing the use of byte compaction mode is usually sub-optimal, - * and will result in larger symbols than would otherwise be possible. This method - * should only be used if your downstream systems require the use of byte - * compaction mode, which is not usually the case. - * - * @param data the data to encode - * @throws OkapiException if no data or data is invalid - * @see #setContent(String) - */ - public void setContent(byte[] data) { - this.forceByteCompaction = true; - super.setContent(new String(data, StandardCharsets.ISO_8859_1)); - } - - @Override - protected boolean eciSupported() { - return true; - } - - @Override - protected void encode() { - - eciProcess(); - - switch (symbolMode) { - case MICRO: - processMicroPdf417(); - break; - case NORMAL: - case TRUNCATED: - default: - processPdf417(); - break; - } - } - - private void processPdf417() { - int j, loop, offset; - int[] mccorrection = new int[520]; - int total; - int c1, c2, c3; - int[] dummy = new int[35]; - int selectedECCLevel; - StringBuilder codebarre = new StringBuilder(); - StringBuilder bin = new StringBuilder(); - - List< Block > blocks = createBlocks(inputData, forceByteCompaction); - - /* now compress the data */ - codeWordCount = 0; - - if (readerInit) { - codeWords[codeWordCount] = 921; /* Reader Initialisation */ - codeWordCount++; - } - - processEci(eciMode); - - int blockCount = 0; - for (int i = 0; i < blocks.size(); i++) { - Block block = blocks.get(i); - switch (block.mode) { - case TEX: - /* text mode */ - boolean firstBlock = (i == 0); - processText(inputData, blockCount, block.length, firstBlock); - break; - case BYT: - /* octet stream mode */ - EncodingMode lastMode = (i == 0 ? EncodingMode.TEX : blocks.get(i - 1).mode); - processBytes(inputData, blockCount, block.length, lastMode); - break; - case NUM: - /* numeric mode */ - processNumbers(inputData, blockCount, block.length, false); - break; - default: - throw new OkapiInternalException("Unknown block type: " + block.mode); - } - blockCount += block.length; - } - - int dataCount = codeWordCount; - int macroCount = addMacroCodewords(); - - /* Now take care of the number of CWs per row */ - - // if we have to default the ECC level, do so per the - // recommendations in the specification (Table E.1) - selectedECCLevel = preferredEccLevel; - if (selectedECCLevel < 0) { - if (codeWordCount <= 40) { - selectedECCLevel = 2; - } else if (codeWordCount <= 160) { - selectedECCLevel = 3; - } else if (codeWordCount <= 320) { - selectedECCLevel = 4; - } else { - selectedECCLevel = 5; - } - } - - int k = 1 << (selectedECCLevel + 1); // error correction codeword count - int needed = 1 + dataCount + macroCount + k; // not including padding codewords - - validateRows(3, 90); - validateColumns(1, 30); - - if (columns != null) { - if (rows != null) { - // user specified both columns and rows; make sure the data fits - if (columns * rows < needed) { - throw new OkapiInputException("Too few rows (" + rows + ") and columns (" + columns + ") to hold codewords (" + needed + ")"); - } - } else { - // user only specified column count; figure out row count - rows = (int) Math.max(Math.ceil(needed / (double) columns), 3); - } - } else { - if (rows != null) { - // user only specified row count; figure out column count - columns = (int) Math.ceil(needed / (double) rows); - } else { - // user didn't specify columns or rows; figure both out - columns = (int) (0.5 + Math.sqrt((needed - 1) / 3.0)); - rows = (int) Math.ceil(needed / (double) columns); - } - } - - validateRows(3, 90); - validateColumns(1, 30); - - /* add the padding (before the Macro PDF417 control block, if it exists) */ - int padCount = (columns * rows) - codeWordCount - k - 1; - int macroStart = codeWordCount - macroCount; - System.arraycopy(codeWords, macroStart, codeWords, macroStart + padCount, macroCount); - Arrays.fill(codeWords, macroStart, macroStart + padCount, 900); - codeWordCount += padCount; - - /* add the length descriptor */ - for (int i = codeWordCount; i > 0; i--) { - codeWords[i] = codeWords[i - 1]; - } - codeWordCount++; - codeWords[0] = codeWordCount; - - /* add codeword info to debug output */ - info("Codewords: "); - for (int i = 0; i < codeWordCount; i++) { - infoSpace(codeWords[i]); - } - infoLine(); - infoLine("Length Descriptor Codewords: 1"); - infoLine("Data Codewords: " + dataCount); - infoLine("Padding Codewords: " + padCount); - infoLine("Macro Codewords: " + macroCount); - infoLine("ECC Codewords: " + k); - infoLine("Total Codewords: " + (columns * rows)); - - /* 796 - we now take care of the Reed Solomon codes */ - switch (selectedECCLevel) { - case 1: - offset = 2; - break; - case 2: - offset = 6; - break; - case 3: - offset = 14; - break; - case 4: - offset = 30; - break; - case 5: - offset = 62; - break; - case 6: - offset = 126; - break; - case 7: - offset = 254; - break; - case 8: - offset = 510; - break; - default: - offset = 0; - break; - } - - for (loop = 0; loop < 520; loop++) { - mccorrection[loop] = 0; - } - - for (int i = 0; i < codeWordCount; i++) { - total = (codeWords[i] + mccorrection[k - 1]) % 929; - for (j = k - 1; j > 0; j--) { - mccorrection[j] = (mccorrection[j - 1] + 929 - (total * COEFRS[offset + j]) % 929) % 929; - } - mccorrection[0] = (929 - (total * COEFRS[offset + j]) % 929) % 929; - } - - /* we add these codes to the string */ - for (int i = k - 1; i >= 0; i--) { - codeWords[codeWordCount++] = mccorrection[i] != 0 ? 929 - mccorrection[i] : 0; - } - - /* make sure total codeword count isn't too high */ - checkCodewordCount(codeWordCount); - - assert 1 + dataCount + padCount + macroCount + k == columns * rows; - assert codeWordCount == columns * rows; - - /* 818 - The CW string is finished */ - c1 = (rows - 1) / 3; - c2 = (selectedECCLevel * 3) + (rows - 1) % 3; - c3 = columns - 1; - - readable = ""; - row_count = rows; - pattern = new String[rows]; - row_height = new int[rows]; - infoLine("Grid Size: " + columns + " X " + rows); - - /* we now encode each row */ - for (int i = 0; i < rows; i++) { - for (j = 0; j < columns; j++) { - dummy[j + 1] = codeWords[i * columns + j]; - } - k = (i / 3) * 30; - switch (i % 3) { - case 0: - offset = 0; // cluster 0 - dummy[0] = k + c1; // left row indicator - dummy[columns + 1] = k + c3; // right row indicator - break; - case 1: - offset = 929; // cluster 3 - dummy[0] = k + c2; // left row indicator - dummy[columns + 1] = k + c1; // right row indicator - break; - case 2: - offset = 1858; // cluster 6 - dummy[0] = k + c3; // left row indicator - dummy[columns + 1] = k + c2; // right row indicator - break; - } - codebarre.setLength(0); - codebarre.append("+*"); - for (j = 0; j <= columns + 1; j++) { - if (!(symbolMode == Mode.TRUNCATED && j > columns)) { - codebarre.append(CODAGEMC[offset + dummy[j]]); - codebarre.append('*'); - } - } - if(symbolMode != Mode.TRUNCATED) { - codebarre.append('-'); - } - bin.setLength(0); - for (j = 0; j < codebarre.length(); j++) { - bin.append(PDF_TTF[positionOf(codebarre.charAt(j), BR_SET)]); - } - pattern[i] = bin2pat(bin); - row_height[i] = default_height; - } - } - - private void processMicroPdf417() { /* like PDF417 only much smaller! */ - - int k, j, longueur, offset; - int total; - int LeftRAPStart, CentreRAPStart, RightRAPStart, StartCluster; - int LeftRAP, CentreRAP, RightRAP, Cluster, flip, loop; - int[] dummy = new int[5]; - int[] mccorrection = new int[50]; - StringBuilder codebarre = new StringBuilder(); - StringBuilder bin = new StringBuilder(); - - /* Encoding starts out the same as PDF417, so use the same code */ - - List< Block > blocks = createBlocks(inputData, forceByteCompaction); - - /* 541 - now compress the data */ - codeWordCount = 0; - if (readerInit) { - codeWords[codeWordCount] = 921; /* Reader Initialisation */ - codeWordCount++; - } - - processEci(eciMode); - - int blockCount = 0; - for (int i = 0; i < blocks.size(); i++) { - Block block = blocks.get(i); - switch (block.mode) { - case TEX: - /* text mode */ - processText(inputData, blockCount, block.length, false); // TODO: this shouldn't always be false? - break; - case BYT: - /* octet stream mode */ - EncodingMode lastMode = (i == 0 ? EncodingMode.TEX : blocks.get(i - 1).mode); - processBytes(inputData, blockCount, block.length, lastMode); - break; - case NUM: - /* numeric mode */ - processNumbers(inputData, blockCount, block.length, false); - break; - default: - throw new OkapiInternalException("Unknown block type: " + block.mode); - } - blockCount += block.length; - } - - int dataCount = codeWordCount; - int macroCount = addMacroCodewords(); - - /* This is where it all changes! */ - - validateRows(4, 44); - validateColumns(1, 4); - - if (columns != null) { - int max; - switch (columns) { - case 1: - max = 20; - break; - case 2: - max = 37; - break; - case 3: - max = 82; - break; - case 4: - max = 126; - break; - default: - throw new OkapiInputException("Invalid column count: " + columns); - } - if (codeWordCount > max) { - throw new OkapiInputException("Too few columns (" + columns + ") to hold data codewords (" + codeWordCount + ")"); - } - } - - /* Now figure out which variant of the symbol to use and load values accordingly */ - - int variant = getMicroPdf417Variant(codeWordCount, columns, rows); - - /* Now we have the variant we can load the data */ - - variant--; - columns = MICRO_VARIANTS[variant]; /* columns */ - rows = MICRO_VARIANTS[variant + 34]; /* rows */ - k = MICRO_VARIANTS[variant + 68]; /* number of EC CWs */ - longueur = (columns * rows) - k; /* number of non-EC CWs */ - int padCount = longueur - codeWordCount; /* amount of padding required */ - offset = MICRO_VARIANTS[variant + 102]; /* coefficient offset */ - - /* add the padding (before the Macro PDF417 control block, if it exists) */ - int macroStart = codeWordCount - macroCount; - System.arraycopy(codeWords, macroStart, codeWords, macroStart + padCount, macroCount); - Arrays.fill(codeWords, macroStart, macroStart + padCount, 900); - codeWordCount += padCount; - - /* add codeword info to debug output */ - info("Codewords: "); - for (int i = 0; i < codeWordCount; i++) { - infoSpace(codeWords[i]); - } - infoLine(); - infoLine("Data Codewords: " + dataCount); - infoLine("Padding Codewords: " + padCount); - infoLine("Macro Codewords: " + macroCount); - infoLine("ECC Codewords: " + k); - infoLine("Total Codewords: " + (columns * rows)); - - /* Reed-Solomon error correction */ - longueur = codeWordCount; - for (loop = 0; loop < 50; loop++) { - mccorrection[loop] = 0; - } - - for (int i = 0; i < longueur; i++) { - total = (codeWords[i] + mccorrection[k - 1]) % 929; - for (j = k - 1; j >= 0; j--) { - if (j == 0) { - mccorrection[j] = (929 - (total * MICRO_COEFFS[offset + j]) % 929) % 929; - } else { - mccorrection[j] = (mccorrection[j - 1] + 929 - (total * MICRO_COEFFS[offset + j]) % 929) % 929; - } - } - } - - for (j = 0; j < k; j++) { - if (mccorrection[j] != 0) { - mccorrection[j] = 929 - mccorrection[j]; - } - } - /* we add these codes to the string */ - for (int i = k - 1; i >= 0; i--) { - codeWords[codeWordCount] = mccorrection[i]; - codeWordCount++; - } - - assert dataCount + padCount + macroCount + k == columns * rows; - assert codeWordCount == columns * rows; - - /* Now get the RAP (Row Address Pattern) start values */ - LeftRAPStart = RAP_TABLE[variant]; - CentreRAPStart = RAP_TABLE[variant + 34]; - RightRAPStart = RAP_TABLE[variant + 68]; - StartCluster = RAP_TABLE[variant + 102] / 3; - - /* That's all values loaded, get on with the encoding */ - - LeftRAP = LeftRAPStart; - CentreRAP = CentreRAPStart; - RightRAP = RightRAPStart; - Cluster = StartCluster; /* Cluster can be 0, 1 or 2 for Cluster(0), Cluster(3) and Cluster(6) */ - - readable = ""; - pattern = new String[rows]; - row_count = rows; - row_height = new int[rows]; - - infoLine("Grid Size: " + columns + " X " + row_count); - - for (int i = 0; i < rows; i++) { - codebarre.setLength(0); - offset = 929 * Cluster; - for (j = 0; j < 5; j++) { - dummy[j] = 0; - } - for (j = 0; j < columns; j++) { - dummy[j + 1] = codeWords[i * columns + j]; - } - - /* Copy the data into codebarre */ - codebarre.append(RAPLR[LeftRAP]); - codebarre.append('1'); - codebarre.append(CODAGEMC[offset + dummy[1]]); - codebarre.append('1'); - if (columns == 3) { - codebarre.append(RAPC[CentreRAP]); - } - if (columns >= 2) { - codebarre.append('1'); - codebarre.append(CODAGEMC[offset + dummy[2]]); - codebarre.append('1'); - } - if (columns == 4) { - codebarre.append(RAPC[CentreRAP]); - } - if (columns >= 3) { - codebarre.append('1'); - codebarre.append(CODAGEMC[offset + dummy[3]]); - codebarre.append('1'); - } - if (columns == 4) { - codebarre.append('1'); - codebarre.append(CODAGEMC[offset + dummy[4]]); - codebarre.append('1'); - } - codebarre.append(RAPLR[RightRAP]); - codebarre.append('1'); /* stop */ - - /* Now codebarre is a mixture of letters and numbers */ - - flip = 1; - bin.setLength(0); - for (loop = 0; loop < codebarre.length(); loop++) { - if ((codebarre.charAt(loop) >= '0') && (codebarre.charAt(loop) <= '9')) { - for (k = 0; k < Character.getNumericValue(codebarre.charAt(loop)); k++) { - if (flip == 0) { - bin.append('0'); - } else { - bin.append('1'); - } - } - if (flip == 0) { - flip = 1; - } else { - flip = 0; - } - } else { - bin.append(PDF_TTF[positionOf(codebarre.charAt(loop), BR_SET)]); - } - } - - /* so now pattern[] holds the string of '1's and '0's. - copy this to the symbol */ - pattern[i] = bin2pat(bin); - row_height[i] = default_height; - - /* Set up RAPs and Cluster for next row */ - LeftRAP++; - CentreRAP++; - RightRAP++; - Cluster++; - - if (LeftRAP == 53) { - LeftRAP = 1; - } - if (CentreRAP == 53) { - CentreRAP = 1; - } - if (RightRAP == 53) { - RightRAP = 1; - } - if (Cluster == 3) { - Cluster = 0; - } - } - } - - private void validateRows(int min, int max) { - if (rows != null) { - if (rows < min) { - throw new OkapiInputException("Too few rows (" + rows + ")"); - } else if (rows > max) { - throw new OkapiInputException("Too many rows (" + rows + ")"); - } - } - } - - private void validateColumns(int min, int max) { - if (columns != null) { - if (columns < min) { - throw new OkapiInputException("Too few columns (" + columns + ")"); - } else if (columns > max) { - throw new OkapiInputException("Too many columns (" + columns + ")"); - } - } - } - - private static void checkCodewordCount(int count) { - if (count > 929) { - throw new OkapiInputException("Too many codewords required (" + count + ", but max is 929)"); - } - } - - private static EncodingMode chooseMode(int codeascii) { - if (codeascii >= '0' && codeascii <= '9') { - return EncodingMode.NUM; - } else if (codeascii == '\t' || codeascii == '\n' || codeascii == '\r' || (codeascii >= ' ' && codeascii <= '~')) { - return EncodingMode.TEX; - } else { - return EncodingMode.BYT; - } - } - - private static int getMicroPdf417Variant(int codeWordCount, Integer columns, Integer rows) { - for (int i = 0; i < 34; i++) { - int maxCodewordCount = MICRO_AUTOSIZE[i]; - if (codeWordCount <= maxCodewordCount) { - int variant = MICRO_AUTOSIZE[i + 34]; - int columnsForThisVariant = MICRO_VARIANTS[variant - 1]; - int rowsForThisVariant = MICRO_VARIANTS[variant - 1 + 34]; - if ((columns == null || columns == columnsForThisVariant) && (rows == null || rows == rowsForThisVariant)) { - return variant; - } - } - } - throw new OkapiInputException("Unable to determine MicroPDF417 variant for " + codeWordCount + " codewords"); - } - - /** Determines the encoding block groups for the specified data. */ - private static List< Block > createBlocks(int[] data, boolean forceByteCompaction) { - - if (forceByteCompaction) { - return Collections.singletonList(new Block(EncodingMode.BYT, data.length)); - } - - List< Block > blocks = new ArrayList<>(); - Block current = null; - - for (int i = 0; i < data.length; i++) { - EncodingMode mode = chooseMode(data[i]); - if ((current != null && current.mode == mode) && - (mode != EncodingMode.NUM || current.length < MAX_NUMERIC_COMPACTION_BLOCK_SIZE)) { - current.length++; - } else { - current = new Block(mode, 1); - blocks.add(current); - } - } - - smoothBlocks(blocks); - - return blocks; - } - - /** Combines adjacent blocks of different types in very specific scenarios. */ - private static void smoothBlocks(List< Block > blocks) { - - for (int i = 0; i < blocks.size(); i++) { - Block block = blocks.get(i); - EncodingMode last = (i > 0 ? blocks.get(i - 1).mode : EncodingMode.FALSE); - EncodingMode next = (i < blocks.size() - 1 ? blocks.get(i + 1).mode : EncodingMode.FALSE); - if (block.mode == EncodingMode.NUM) { - if (i == 0) { /* first block */ - if (next == EncodingMode.TEX && block.length < 8) { - block.mode = EncodingMode.TEX; - } else if (next == EncodingMode.BYT && block.length == 1) { - block.mode = EncodingMode.BYT; - } - } else if (i == blocks.size() - 1) { /* last block */ - if (last == EncodingMode.TEX && block.length < 7) { - block.mode = EncodingMode.TEX; - } else if (last == EncodingMode.BYT && block.length == 1) { - block.mode = EncodingMode.BYT; - } - } else { /* not first or last block */ - if (last == EncodingMode.BYT && next == EncodingMode.BYT && block.length < 4) { - block.mode = EncodingMode.BYT; - } else if (last == EncodingMode.BYT && next == EncodingMode.TEX && block.length < 4) { - block.mode = EncodingMode.TEX; - } else if (last == EncodingMode.TEX && next == EncodingMode.BYT && block.length < 5) { - block.mode = EncodingMode.TEX; - } else if (last == EncodingMode.TEX && next == EncodingMode.TEX && block.length < 8) { - block.mode = EncodingMode.TEX; - } else if (last == EncodingMode.NUM && next == EncodingMode.TEX && block.length < 8) { - block.mode = EncodingMode.TEX; - } - } - } - } - - mergeBlocks(blocks); - - for (int i = 0; i < blocks.size(); i++) { - Block block = blocks.get(i); - EncodingMode last = (i > 0 ? blocks.get(i - 1).mode : EncodingMode.FALSE); - EncodingMode next = (i < blocks.size() - 1 ? blocks.get(i + 1).mode : EncodingMode.FALSE); - if (block.mode == EncodingMode.TEX && i > 0) { /* not the first */ - if (i == blocks.size() - 1) { /* the last one */ - if (last == EncodingMode.BYT && block.length == 1) { - block.mode = EncodingMode.BYT; - } - } else { /* not the last one */ - if (last == EncodingMode.BYT && next == EncodingMode.BYT && block.length < 5) { - block.mode = EncodingMode.BYT; - } - if (((last == EncodingMode.BYT && next != EncodingMode.BYT) || - (last != EncodingMode.BYT && next == EncodingMode.BYT)) && (block.length < 3)) { - block.mode = EncodingMode.BYT; - } - } - } - } - - mergeBlocks(blocks); - } - - /** Combines adjacent blocks of the same type. */ - private static void mergeBlocks(List< Block > blocks) { - for (int i = 1; i < blocks.size(); i++) { - Block b1 = blocks.get(i - 1); - Block b2 = blocks.get(i); - if ((b1.mode == b2.mode) && - (b1.mode != EncodingMode.NUM || b1.length + b2.length <= MAX_NUMERIC_COMPACTION_BLOCK_SIZE)) { - b1.length += b2.length; - blocks.remove(i); - i--; - } - } - } - - private void processEci(int eci) { - if (eci == 3) { - return; // default, no need to specify - } - /* Encoding ECI assignment number, from ISO/IEC 15438 Table 8 */ - if (eci <= 899) { - checkCodewordCount(codeWordCount + 2); - codeWords[codeWordCount++] = 927; - codeWords[codeWordCount++] = eci; - } else if (eci >= 900 && eci <= 810899) { - checkCodewordCount(codeWordCount + 3); - codeWords[codeWordCount++] = 926; - codeWords[codeWordCount++] = (eci / 900) - 1; - codeWords[codeWordCount++] = eci % 900; - } else if (eci >= 810900 && eci <= 811799) { - checkCodewordCount(codeWordCount + 2); - codeWords[codeWordCount++] = 925; - codeWords[codeWordCount++] = eci - 810900; - } - } - - private void processText(int[] data, int start, int length, boolean skipLatch) { - int j, blockIndext, curtable; - int codeascii; - int wnet = 0; - int[] listet0 = new int[length]; - int[] listet1 = new int[length]; - int[] chainet = new int[length * 4]; - - /* listet will contain the table numbers and the value of each characters */ - for (blockIndext = 0; blockIndext < length; blockIndext++) { - codeascii = data[start + blockIndext]; - switch (codeascii) { - case '\t': - listet0[blockIndext] = 12; - listet1[blockIndext] = 12; - break; - case '\n': - listet0[blockIndext] = 8; - listet1[blockIndext] = 15; - break; - case '\r': - listet0[blockIndext] = 12; - listet1[blockIndext] = 11; - break; - default: - listet0[blockIndext] = ASCII_X[codeascii - 32]; - listet1[blockIndext] = ASCII_Y[codeascii - 32]; - break; - } - } - - curtable = 1; /* default table */ - for (j = 0; j < length; j++) { - if ((listet0[j] & curtable) != 0) { /* The character is in the current table */ - chainet[wnet] = listet1[j]; - wnet++; - } else { /* Obliged to change table */ - boolean flag = false; /* True if we change table for only one character */ - if (j == (length - 1)) { - flag = true; - } else { - if ((listet0[j] & listet0[j + 1]) == 0) { - flag = true; - } - } - - if (flag) { /* we change only one character - look for temporary switch */ - if (((listet0[j] & 1) != 0) && (curtable == 2)) { /* T_UPP */ - chainet[wnet] = 27; - chainet[wnet + 1] = listet1[j]; - wnet += 2; - } - if ((listet0[j] & 8) != 0) { /* T_PUN */ - chainet[wnet] = 29; - chainet[wnet + 1] = listet1[j]; - wnet += 2; - } - if (!((((listet0[j] & 1) != 0) && (curtable == 2)) || ((listet0[j] & 8) != 0))) { - /* No temporary switch available */ - flag = false; - } - } - - if (!(flag)) { - int newtable; - - if (j == (length - 1)) { - newtable = listet0[j]; - } else { - if ((listet0[j] & listet0[j + 1]) == 0) { - newtable = listet0[j]; - } else { - newtable = listet0[j] & listet0[j + 1]; - } - } - - /* Maintain the first if several tables are possible */ - switch (newtable) { - case 3: - case 5: - case 7: - case 9: - case 11: - case 13: - case 15: - newtable = 1; - break; - case 6: - case 10: - case 14: - newtable = 2; - break; - case 12: - newtable = 4; - break; - } - - /* select the switch */ - switch (curtable) { - case 1: - switch (newtable) { - case 2: - chainet[wnet] = 27; - wnet++; - break; - case 4: - chainet[wnet] = 28; - wnet++; - break; - case 8: - chainet[wnet] = 28; - wnet++; - chainet[wnet] = 25; - wnet++; - break; - } - break; - case 2: - switch (newtable) { - case 1: - chainet[wnet] = 28; - wnet++; - chainet[wnet] = 28; - wnet++; - break; - case 4: - chainet[wnet] = 28; - wnet++; - break; - case 8: - chainet[wnet] = 28; - wnet++; - chainet[wnet] = 25; - wnet++; - break; - } - break; - case 4: - switch (newtable) { - case 1: - chainet[wnet] = 28; - wnet++; - break; - case 2: - chainet[wnet] = 27; - wnet++; - break; - case 8: - chainet[wnet] = 25; - wnet++; - break; - } - break; - case 8: - switch (newtable) { - case 1: - chainet[wnet] = 29; - wnet++; - break; - case 2: - chainet[wnet] = 29; - wnet++; - chainet[wnet] = 27; - wnet++; - break; - case 4: - chainet[wnet] = 29; - wnet++; - chainet[wnet] = 28; - wnet++; - break; - } - break; - } - curtable = newtable; - /* at last we add the character */ - chainet[wnet] = listet1[j]; - wnet++; - } - } - } - - if ((wnet & 1) != 0) { - chainet[wnet] = 29; - wnet++; - } - - /* Now translate the string chainet into codewords */ - - checkCodewordCount(codeWordCount + (skipLatch ? 0 : 1) + (wnet / 2)); - - if (!skipLatch) { - // text compaction mode is the default mode for PDF417, - // so no need for an explicit latch if this is the first block - codeWords[codeWordCount] = 900; - codeWordCount++; - } - - for (j = 0; j < wnet; j += 2) { - int cw_number = (30 * chainet[j]) + chainet[j + 1]; - codeWords[codeWordCount] = cw_number; - codeWordCount++; - } - } - - private void processBytes(int[] data, int start, int length, EncodingMode lastMode) { - int len = 0; - int chunkLen = 0; - BigInteger mantisa; - BigInteger total; - BigInteger word; - - mantisa = new BigInteger("0"); - total = new BigInteger("0"); - - checkCodewordCount(codeWordCount + 1 + (5 * length / 6) + (length % 6)); - - if (length == 1 && lastMode == EncodingMode.TEX) { - codeWords[codeWordCount++] = 913; - codeWords[codeWordCount++] = data[start]; - } else { - /* select the switch for multiple of 6 bytes */ - if (length % 6 == 0) { - codeWords[codeWordCount++] = 924; - } else { - codeWords[codeWordCount++] = 901; - } - - while (len < length) { - chunkLen = length - len; - if (6 <= chunkLen) /* Take groups of 6 */{ - chunkLen = 6; - len += chunkLen; - total = BigInteger.valueOf(0); - - while ((chunkLen--) != 0) { - mantisa = BigInteger.valueOf(data[start++]); - total = total.or(mantisa.shiftLeft(chunkLen * 8)); - } - - chunkLen = 5; - - while ((chunkLen--) != 0) { - - word = total.mod(BigInteger.valueOf(900)); - codeWords[codeWordCount + chunkLen] = word.intValue(); - total = total.divide(BigInteger.valueOf(900)); - } - codeWordCount += 5; - } else /* If it remain a group of less than 6 bytes */{ - len += chunkLen; - while ((chunkLen--) != 0) { - codeWords[codeWordCount++] = data[start++]; - } - } - } - } - } - - private void processNumbers(int[] data, int start, int length, boolean skipLatch) { - - BigInteger tVal, dVal; - int[] d = new int[16]; - int cw_count; - - StringBuilder t = new StringBuilder(length + 1); - t.append('1'); - for (int i = 0; i < length; i++) { - t.append((char) data[start + i]); - } - - tVal = new BigInteger(t.toString()); - - cw_count = 0; - do { - dVal = tVal.mod(BigInteger.valueOf(900)); - d[cw_count] = dVal.intValue(); - tVal = tVal.divide(BigInteger.valueOf(900)); - cw_count++; - } while (tVal.compareTo(BigInteger.ZERO) == 1); - - checkCodewordCount(codeWordCount + (skipLatch ? 0 : 1) + cw_count); - - if (!skipLatch) { - // we don't need to latch to numeric mode in some cases, e.g. - // during numeric compaction of the Macro PDF417 segment index - codeWords[codeWordCount++] = 902; - } - - for (int i = cw_count - 1; i >= 0; i--) { - codeWords[codeWordCount++] = d[i]; - } - } - - private void processFiveDigits(int i) { - assert i >= 0; - assert i <= 99_999; - int[] data = new int[5]; - for (int x = data.length - 1; x >= 0; x--) { - data[x] = '0' + (i % 10); - i /= 10; - } - processNumbers(data, 0, data.length, true); - } - - /** Adds the Macro PDF417 control block codewords (if any). */ - private int addMacroCodewords() { - - // if the structured append series size is 1, this isn't - // actually part of a structured append series - if (structuredAppendTotal == 1) { - return 0; - } - - int macroStart = codeWordCount; - - // add the Macro marker codeword - checkCodewordCount(codeWordCount + 1); - codeWords[codeWordCount++] = 928; - - // add the segment index, padded with leading zeros to five digits - // use numeric compaction, but no latch - int segmentIndex = structuredAppendPosition - 1; - processFiveDigits(segmentIndex); - - // add the file ID (base 900, which is easy since we limit - // file ID values to triples in the range 0 to 899) - checkCodewordCount(codeWordCount + (structuredAppendFileId.length() / 3)); - for (int i = 0; i < structuredAppendFileId.length(); i += 3) { - int number = Integer.parseInt(structuredAppendFileId.substring(i, i + 3)); - codeWords[codeWordCount++] = number; - } - - // optional fields: add the file name, if specified - if (structuredAppendFileName != null && !structuredAppendFileName.isEmpty()) { - checkCodewordCount(codeWordCount + 2); - codeWords[codeWordCount++] = 923; - codeWords[codeWordCount++] = 000; - EciMode eci = EciMode.chooseFor(structuredAppendFileName, 3, 26); - int[] data2 = toBytes(structuredAppendFileName, eci.charset); - processEci(eci.mode); - processText(data2, 0, data2.length, true); - } - - // optional fields: add segment count, if requested - if (structuredAppendIncludeSegmentCount) { - checkCodewordCount(codeWordCount + 2); - codeWords[codeWordCount++] = 923; - codeWords[codeWordCount++] = 001; - processFiveDigits(structuredAppendTotal); - } - - // add the terminator to the last symbol of the series - boolean last = (structuredAppendPosition == structuredAppendTotal); - if (last) { - checkCodewordCount(codeWordCount + 1); - codeWords[codeWordCount++] = 922; - } - - return codeWordCount - macroStart; - } - - private static class Block { - - public EncodingMode mode; - public int length; - - public Block(EncodingMode mode, int length) { - this.mode = mode; - this.length = length; - } - - @Override - public String toString() { - return mode + "x" + length; - } - } -} +/* + * Copyright 2014-2017 Robin Stuart, Daniel Gredler + * + * 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. + */ + +package uk.org.okapibarcode.backend; + +import static uk.org.okapibarcode.util.Arrays.positionOf; + +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import uk.org.okapibarcode.util.EciMode; + +/** + *

Implements PDF417 bar code symbology and MicroPDF417 bar code symbology + * according to ISO/IEC 15438:2006 and ISO/IEC 24728:2006 respectively. + * + *

PDF417 supports encoding up to the ISO standard maximum symbol size of 925 + * codewords which (at error correction level 0) allows a maximum data size + * of 1850 text characters, or 2710 digits. The maximum size MicroPDF417 symbol + * can hold 250 alphanumeric characters or 366 digits. + * + * @author Robin Stuart + * @author Daniel Gredler + */ +public class Pdf417 extends Symbol { + + public enum Mode { + /** Normal PDF417. */ + NORMAL, + /** Truncated PDF417. */ + TRUNCATED, + /** MicroPDF417. */ + MICRO + } + + private enum EncodingMode { + FALSE, TEX, BYT, NUM + } + + private int[] codeWords = new int[2700]; + private int codeWordCount; + private Mode symbolMode = Mode.NORMAL; + private boolean forceByteCompaction; + private Integer columns; + private Integer rows; + private int preferredEccLevel = -1; + private String structuredAppendFileId = "000"; + private int structuredAppendPosition = 1; + private int structuredAppendTotal = 1; + private String structuredAppendFileName; + private boolean structuredAppendIncludeSegmentCount; + + private static final int MAX_NUMERIC_COMPACTION_BLOCK_SIZE = 44; + + private static final int[] COEFRS = { + /* k = 2 */ + 27, 917, + + /* k = 4 */ + 522, 568, 723, 809, + + /* k = 8 */ + 237, 308, 436, 284, 646, 653, 428, 379, + + /* k = 16 */ + 274, 562, 232, 755, 599, 524, 801, 132, 295, 116, 442, 428, 295, 42, 176, 65, + + /* k = 32 */ + 361, 575, 922, 525, 176, 586, 640, 321, 536, 742, 677, 742, 687, 284, 193, 517, + 273, 494, 263, 147, 593, 800, 571, 320, 803, 133, 231, 390, 685, 330, 63, 410, + + /* k = 64 */ + 539, 422, 6, 93, 862, 771, 453, 106, 610, 287, 107, 505, 733, 877, 381, 612, + 723, 476, 462, 172, 430, 609, 858, 822, 543, 376, 511, 400, 672, 762, 283, 184, + 440, 35, 519, 31, 460, 594, 225, 535, 517, 352, 605, 158, 651, 201, 488, 502, + 648, 733, 717, 83, 404, 97, 280, 771, 840, 629, 4, 381, 843, 623, 264, 543, + + /* k = 128 */ + 521, 310, 864, 547, 858, 580, 296, 379, 53, 779, 897, 444, 400, 925, 749, 415, + 822, 93, 217, 208, 928, 244, 583, 620, 246, 148, 447, 631, 292, 908, 490, 704, + 516, 258, 457, 907, 594, 723, 674, 292, 272, 96, 684, 432, 686, 606, 860, 569, + 193, 219, 129, 186, 236, 287, 192, 775, 278, 173, 40, 379, 712, 463, 646, 776, + 171, 491, 297, 763, 156, 732, 95, 270, 447, 90, 507, 48, 228, 821, 808, 898, + 784, 663, 627, 378, 382, 262, 380, 602, 754, 336, 89, 614, 87, 432, 670, 616, + 157, 374, 242, 726, 600, 269, 375, 898, 845, 454, 354, 130, 814, 587, 804, 34, + 211, 330, 539, 297, 827, 865, 37, 517, 834, 315, 550, 86, 801, 4, 108, 539, + + /* k = 256 */ + 524, 894, 75, 766, 882, 857, 74, 204, 82, 586, 708, 250, 905, 786, 138, 720, + 858, 194, 311, 913, 275, 190, 375, 850, 438, 733, 194, 280, 201, 280, 828, 757, + 710, 814, 919, 89, 68, 569, 11, 204, 796, 605, 540, 913, 801, 700, 799, 137, + 439, 418, 592, 668, 353, 859, 370, 694, 325, 240, 216, 257, 284, 549, 209, 884, + 315, 70, 329, 793, 490, 274, 877, 162, 749, 812, 684, 461, 334, 376, 849, 521, + 307, 291, 803, 712, 19, 358, 399, 908, 103, 511, 51, 8, 517, 225, 289, 470, + 637, 731, 66, 255, 917, 269, 463, 830, 730, 433, 848, 585, 136, 538, 906, 90, + 2, 290, 743, 199, 655, 903, 329, 49, 802, 580, 355, 588, 188, 462, 10, 134, + 628, 320, 479, 130, 739, 71, 263, 318, 374, 601, 192, 605, 142, 673, 687, 234, + 722, 384, 177, 752, 607, 640, 455, 193, 689, 707, 805, 641, 48, 60, 732, 621, + 895, 544, 261, 852, 655, 309, 697, 755, 756, 60, 231, 773, 434, 421, 726, 528, + 503, 118, 49, 795, 32, 144, 500, 238, 836, 394, 280, 566, 319, 9, 647, 550, + 73, 914, 342, 126, 32, 681, 331, 792, 620, 60, 609, 441, 180, 791, 893, 754, + 605, 383, 228, 749, 760, 213, 54, 297, 134, 54, 834, 299, 922, 191, 910, 532, + 609, 829, 189, 20, 167, 29, 872, 449, 83, 402, 41, 656, 505, 579, 481, 173, + 404, 251, 688, 95, 497, 555, 642, 543, 307, 159, 924, 558, 648, 55, 497, 10, + + /* k = 512 */ + 352, 77, 373, 504, 35, 599, 428, 207, 409, 574, 118, 498, 285, 380, 350, 492, + 197, 265, 920, 155, 914, 299, 229, 643, 294, 871, 306, 88, 87, 193, 352, 781, + 846, 75, 327, 520, 435, 543, 203, 666, 249, 346, 781, 621, 640, 268, 794, 534, + 539, 781, 408, 390, 644, 102, 476, 499, 290, 632, 545, 37, 858, 916, 552, 41, + 542, 289, 122, 272, 383, 800, 485, 98, 752, 472, 761, 107, 784, 860, 658, 741, + 290, 204, 681, 407, 855, 85, 99, 62, 482, 180, 20, 297, 451, 593, 913, 142, + 808, 684, 287, 536, 561, 76, 653, 899, 729, 567, 744, 390, 513, 192, 516, 258, + 240, 518, 794, 395, 768, 848, 51, 610, 384, 168, 190, 826, 328, 596, 786, 303, + 570, 381, 415, 641, 156, 237, 151, 429, 531, 207, 676, 710, 89, 168, 304, 402, + 40, 708, 575, 162, 864, 229, 65, 861, 841, 512, 164, 477, 221, 92, 358, 785, + 288, 357, 850, 836, 827, 736, 707, 94, 8, 494, 114, 521, 2, 499, 851, 543, + 152, 729, 771, 95, 248, 361, 578, 323, 856, 797, 289, 51, 684, 466, 533, 820, + 669, 45, 902, 452, 167, 342, 244, 173, 35, 463, 651, 51, 699, 591, 452, 578, + 37, 124, 298, 332, 552, 43, 427, 119, 662, 777, 475, 850, 764, 364, 578, 911, + 283, 711, 472, 420, 245, 288, 594, 394, 511, 327, 589, 777, 699, 688, 43, 408, + 842, 383, 721, 521, 560, 644, 714, 559, 62, 145, 873, 663, 713, 159, 672, 729, + 624, 59, 193, 417, 158, 209, 563, 564, 343, 693, 109, 608, 563, 365, 181, 772, + 677, 310, 248, 353, 708, 410, 579, 870, 617, 841, 632, 860, 289, 536, 35, 777, + 618, 586, 424, 833, 77, 597, 346, 269, 757, 632, 695, 751, 331, 247, 184, 45, + 787, 680, 18, 66, 407, 369, 54, 492, 228, 613, 830, 922, 437, 519, 644, 905, + 789, 420, 305, 441, 207, 300, 892, 827, 141, 537, 381, 662, 513, 56, 252, 341, + 242, 797, 838, 837, 720, 224, 307, 631, 61, 87, 560, 310, 756, 665, 397, 808, + 851, 309, 473, 795, 378, 31, 647, 915, 459, 806, 590, 731, 425, 216, 548, 249, + 321, 881, 699, 535, 673, 782, 210, 815, 905, 303, 843, 922, 281, 73, 469, 791, + 660, 162, 498, 308, 155, 422, 907, 817, 187, 62, 16, 425, 535, 336, 286, 437, + 375, 273, 610, 296, 183, 923, 116, 667, 751, 353, 62, 366, 691, 379, 687, 842, + 37, 357, 720, 742, 330, 5, 39, 923, 311, 424, 242, 749, 321, 54, 669, 316, + 342, 299, 534, 105, 667, 488, 640, 672, 576, 540, 316, 486, 721, 610, 46, 656, + 447, 171, 616, 464, 190, 531, 297, 321, 762, 752, 533, 175, 134, 14, 381, 433, + 717, 45, 111, 20, 596, 284, 736, 138, 646, 411, 877, 669, 141, 919, 45, 780, + 407, 164, 332, 899, 165, 726, 600, 325, 498, 655, 357, 752, 768, 223, 849, 647, + 63, 310, 863, 251, 366, 304, 282, 738, 675, 410, 389, 244, 31, 121, 303, 263 + }; + + private static final String[] CODAGEMC = { + "urA", "xfs", "ypy", "unk", "xdw", "yoz", "pDA", "uls", "pBk", "eBA", + "pAs", "eAk", "prA", "uvs", "xhy", "pnk", "utw", "xgz", "fDA", "pls", "fBk", "frA", "pvs", + "uxy", "fnk", "ptw", "uwz", "fls", "psy", "fvs", "pxy", "ftw", "pwz", "fxy", "yrx", "ufk", + "xFw", "ymz", "onA", "uds", "xEy", "olk", "ucw", "dBA", "oks", "uci", "dAk", "okg", "dAc", + "ovk", "uhw", "xaz", "dnA", "ots", "ugy", "dlk", "osw", "ugj", "dks", "osi", "dvk", "oxw", + "uiz", "dts", "owy", "dsw", "owj", "dxw", "oyz", "dwy", "dwj", "ofA", "uFs", "xCy", "odk", + "uEw", "xCj", "clA", "ocs", "uEi", "ckk", "ocg", "ckc", "ckE", "cvA", "ohs", "uay", "ctk", + "ogw", "uaj", "css", "ogi", "csg", "csa", "cxs", "oiy", "cww", "oij", "cwi", "cyy", "oFk", + "uCw", "xBj", "cdA", "oEs", "uCi", "cck", "oEg", "uCb", "ccc", "oEa", "ccE", "oED", "chk", + "oaw", "uDj", "cgs", "oai", "cgg", "oab", "cga", "cgD", "obj", "cib", "cFA", "oCs", "uBi", + "cEk", "oCg", "uBb", "cEc", "oCa", "cEE", "oCD", "cEC", "cas", "cag", "caa", "cCk", "uAr", + "oBa", "oBD", "cCB", "tfk", "wpw", "yez", "mnA", "tds", "woy", "mlk", "tcw", "woj", "FBA", + "mks", "FAk", "mvk", "thw", "wqz", "FnA", "mts", "tgy", "Flk", "msw", "Fks", "Fkg", "Fvk", + "mxw", "tiz", "Fts", "mwy", "Fsw", "Fsi", "Fxw", "myz", "Fwy", "Fyz", "vfA", "xps", "yuy", + "vdk", "xow", "yuj", "qlA", "vcs", "xoi", "qkk", "vcg", "xob", "qkc", "vca", "mfA", "tFs", + "wmy", "qvA", "mdk", "tEw", "wmj", "qtk", "vgw", "xqj", "hlA", "Ekk", "mcg", "tEb", "hkk", + "qsg", "hkc", "EvA", "mhs", "tay", "hvA", "Etk", "mgw", "taj", "htk", "qww", "vij", "hss", + "Esg", "hsg", "Exs", "miy", "hxs", "Eww", "mij", "hww", "qyj", "hwi", "Eyy", "hyy", "Eyj", + "hyj", "vFk", "xmw", "ytj", "qdA", "vEs", "xmi", "qck", "vEg", "xmb", "qcc", "vEa", "qcE", + "qcC", "mFk", "tCw", "wlj", "qhk", "mEs", "tCi", "gtA", "Eck", "vai", "tCb", "gsk", "Ecc", + "mEa", "gsc", "qga", "mED", "EcC", "Ehk", "maw", "tDj", "gxk", "Egs", "mai", "gws", "qii", + "mab", "gwg", "Ega", "EgD", "Eiw", "mbj", "gyw", "Eii", "gyi", "Eib", "gyb", "gzj", "qFA", + "vCs", "xli", "qEk", "vCg", "xlb", "qEc", "vCa", "qEE", "vCD", "qEC", "qEB", "EFA", "mCs", + "tBi", "ghA", "EEk", "mCg", "tBb", "ggk", "qag", "vDb", "ggc", "EEE", "mCD", "ggE", "qaD", + "ggC", "Eas", "mDi", "gis", "Eag", "mDb", "gig", "qbb", "gia", "EaD", "giD", "gji", "gjb", + "qCk", "vBg", "xkr", "qCc", "vBa", "qCE", "vBD", "qCC", "qCB", "ECk", "mBg", "tAr", "gak", + "ECc", "mBa", "gac", "qDa", "mBD", "gaE", "ECC", "gaC", "ECB", "EDg", "gbg", "gba", "gbD", + "vAq", "vAn", "qBB", "mAq", "EBE", "gDE", "gDC", "gDB", "lfA", "sps", "wey", "ldk", "sow", + "ClA", "lcs", "soi", "Ckk", "lcg", "Ckc", "CkE", "CvA", "lhs", "sqy", "Ctk", "lgw", "sqj", + "Css", "lgi", "Csg", "Csa", "Cxs", "liy", "Cww", "lij", "Cwi", "Cyy", "Cyj", "tpk", "wuw", + "yhj", "ndA", "tos", "wui", "nck", "tog", "wub", "ncc", "toa", "ncE", "toD", "lFk", "smw", + "wdj", "nhk", "lEs", "smi", "atA", "Cck", "tqi", "smb", "ask", "ngg", "lEa", "asc", "CcE", + "asE", "Chk", "law", "snj", "axk", "Cgs", "trj", "aws", "nii", "lab", "awg", "Cga", "awa", + "Ciw", "lbj", "ayw", "Cii", "ayi", "Cib", "Cjj", "azj", "vpA", "xus", "yxi", "vok", "xug", + "yxb", "voc", "xua", "voE", "xuD", "voC", "nFA", "tms", "wti", "rhA", "nEk", "xvi", "wtb", + "rgk", "vqg", "xvb", "rgc", "nEE", "tmD", "rgE", "vqD", "nEB", "CFA", "lCs", "sli", "ahA", + "CEk", "lCg", "slb", "ixA", "agk", "nag", "tnb", "iwk", "rig", "vrb", "lCD", "iwc", "agE", + "naD", "iwE", "CEB", "Cas", "lDi", "ais", "Cag", "lDb", "iys", "aig", "nbb", "iyg", "rjb", + "CaD", "aiD", "Cbi", "aji", "Cbb", "izi", "ajb", "vmk", "xtg", "ywr", "vmc", "xta", "vmE", + "xtD", "vmC", "vmB", "nCk", "tlg", "wsr", "rak", "nCc", "xtr", "rac", "vna", "tlD", "raE", + "nCC", "raC", "nCB", "raB", "CCk", "lBg", "skr", "aak", "CCc", "lBa", "iik", "aac", "nDa", + "lBD", "iic", "rba", "CCC", "iiE", "aaC", "CCB", "aaB", "CDg", "lBr", "abg", "CDa", "ijg", + "aba", "CDD", "ija", "abD", "CDr", "ijr", "vlc", "xsq", "vlE", "xsn", "vlC", "vlB", "nBc", + "tkq", "rDc", "nBE", "tkn", "rDE", "vln", "rDC", "nBB", "rDB", "CBc", "lAq", "aDc", "CBE", + "lAn", "ibc", "aDE", "nBn", "ibE", "rDn", "CBB", "ibC", "aDB", "ibB", "aDq", "ibq", "ibn", + "xsf", "vkl", "tkf", "nAm", "nAl", "CAo", "aBo", "iDo", "CAl", "aBl", "kpk", "BdA", "kos", + "Bck", "kog", "seb", "Bcc", "koa", "BcE", "koD", "Bhk", "kqw", "sfj", "Bgs", "kqi", "Bgg", + "kqb", "Bga", "BgD", "Biw", "krj", "Bii", "Bib", "Bjj", "lpA", "sus", "whi", "lok", "sug", + "loc", "sua", "loE", "suD", "loC", "BFA", "kms", "sdi", "DhA", "BEk", "svi", "sdb", "Dgk", + "lqg", "svb", "Dgc", "BEE", "kmD", "DgE", "lqD", "BEB", "Bas", "kni", "Dis", "Bag", "knb", + "Dig", "lrb", "Dia", "BaD", "Bbi", "Dji", "Bbb", "Djb", "tuk", "wxg", "yir", "tuc", "wxa", + "tuE", "wxD", "tuC", "tuB", "lmk", "stg", "nqk", "lmc", "sta", "nqc", "tva", "stD", "nqE", + "lmC", "nqC", "lmB", "nqB", "BCk", "klg", "Dak", "BCc", "str", "bik", "Dac", "lna", "klD", + "bic", "nra", "BCC", "biE", "DaC", "BCB", "DaB", "BDg", "klr", "Dbg", "BDa", "bjg", "Dba", + "BDD", "bja", "DbD", "BDr", "Dbr", "bjr", "xxc", "yyq", "xxE", "yyn", "xxC", "xxB", "ttc", + "wwq", "vvc", "xxq", "wwn", "vvE", "xxn", "vvC", "ttB", "vvB", "llc", "ssq", "nnc", "llE", + "ssn", "rrc", "nnE", "ttn", "rrE", "vvn", "llB", "rrC", "nnB", "rrB", "BBc", "kkq", "DDc", + "BBE", "kkn", "bbc", "DDE", "lln", "jjc", "bbE", "nnn", "BBB", "jjE", "rrn", "DDB", "jjC", + "BBq", "DDq", "BBn", "bbq", "DDn", "jjq", "bbn", "jjn", "xwo", "yyf", "xwm", "xwl", "tso", + "wwf", "vto", "xwv", "vtm", "tsl", "vtl", "lko", "ssf", "nlo", "lkm", "rno", "nlm", "lkl", + "rnm", "nll", "rnl", "BAo", "kkf", "DBo", "lkv", "bDo", "DBm", "BAl", "jbo", "bDm", "DBl", + "jbm", "bDl", "jbl", "DBv", "jbv", "xwd", "vsu", "vst", "nku", "rlu", "rlt", "DAu", "bBu", + "jDu", "jDt", "ApA", "Aok", "keg", "Aoc", "AoE", "AoC", "Aqs", "Aqg", "Aqa", "AqD", "Ari", + "Arb", "kuk", "kuc", "sha", "kuE", "shD", "kuC", "kuB", "Amk", "kdg", "Bqk", "kvg", "kda", + "Bqc", "kva", "BqE", "kvD", "BqC", "AmB", "BqB", "Ang", "kdr", "Brg", "kvr", "Bra", "AnD", + "BrD", "Anr", "Brr", "sxc", "sxE", "sxC", "sxB", "ktc", "lvc", "sxq", "sgn", "lvE", "sxn", + "lvC", "ktB", "lvB", "Alc", "Bnc", "AlE", "kcn", "Drc", "BnE", "AlC", "DrE", "BnC", "AlB", + "DrC", "BnB", "Alq", "Bnq", "Aln", "Drq", "Bnn", "Drn", "wyo", "wym", "wyl", "swo", "txo", + "wyv", "txm", "swl", "txl", "kso", "sgf", "lto", "swv", "nvo", "ltm", "ksl", "nvm", "ltl", + "nvl", "Ako", "kcf", "Blo", "ksv", "Dno", "Blm", "Akl", "bro", "Dnm", "Bll", "brm", "Dnl", + "Akv", "Blv", "Dnv", "brv", "yze", "yzd", "wye", "xyu", "wyd", "xyt", "swe", "twu", "swd", + "vxu", "twt", "vxt", "kse", "lsu", "ksd", "ntu", "lst", "rvu", "ypk", "zew", "xdA", "yos", + "zei", "xck", "yog", "zeb", "xcc", "yoa", "xcE", "yoD", "xcC", "xhk", "yqw", "zfj", "utA", + "xgs", "yqi", "usk", "xgg", "yqb", "usc", "xga", "usE", "xgD", "usC", "uxk", "xiw", "yrj", + "ptA", "uws", "xii", "psk", "uwg", "xib", "psc", "uwa", "psE", "uwD", "psC", "pxk", "uyw", + "xjj", "ftA", "pws", "uyi", "fsk", "pwg", "uyb", "fsc", "pwa", "fsE", "pwD", "fxk", "pyw", + "uzj", "fws", "pyi", "fwg", "pyb", "fwa", "fyw", "pzj", "fyi", "fyb", "xFA", "yms", "zdi", + "xEk", "ymg", "zdb", "xEc", "yma", "xEE", "ymD", "xEC", "xEB", "uhA", "xas", "yni", "ugk", + "xag", "ynb", "ugc", "xaa", "ugE", "xaD", "ugC", "ugB", "oxA", "uis", "xbi", "owk", "uig", + "xbb", "owc", "uia", "owE", "uiD", "owC", "owB", "dxA", "oys", "uji", "dwk", "oyg", "ujb", + "dwc", "oya", "dwE", "oyD", "dwC", "dys", "ozi", "dyg", "ozb", "dya", "dyD", "dzi", "dzb", + "xCk", "ylg", "zcr", "xCc", "yla", "xCE", "ylD", "xCC", "xCB", "uak", "xDg", "ylr", "uac", + "xDa", "uaE", "xDD", "uaC", "uaB", "oik", "ubg", "xDr", "oic", "uba", "oiE", "ubD", "oiC", + "oiB", "cyk", "ojg", "ubr", "cyc", "oja", "cyE", "ojD", "cyC", "cyB", "czg", "ojr", "cza", + "czD", "czr", "xBc", "ykq", "xBE", "ykn", "xBC", "xBB", "uDc", "xBq", "uDE", "xBn", "uDC", + "uDB", "obc", "uDq", "obE", "uDn", "obC", "obB", "cjc", "obq", "cjE", "obn", "cjC", "cjB", + "cjq", "cjn", "xAo", "ykf", "xAm", "xAl", "uBo", "xAv", "uBm", "uBl", "oDo", "uBv", "oDm", + "oDl", "cbo", "oDv", "cbm", "cbl", "xAe", "xAd", "uAu", "uAt", "oBu", "oBt", "wpA", "yes", + "zFi", "wok", "yeg", "zFb", "woc", "yea", "woE", "yeD", "woC", "woB", "thA", "wqs", "yfi", + "tgk", "wqg", "yfb", "tgc", "wqa", "tgE", "wqD", "tgC", "tgB", "mxA", "tis", "wri", "mwk", + "tig", "wrb", "mwc", "tia", "mwE", "tiD", "mwC", "mwB", "FxA", "mys", "tji", "Fwk", "myg", + "tjb", "Fwc", "mya", "FwE", "myD", "FwC", "Fys", "mzi", "Fyg", "mzb", "Fya", "FyD", "Fzi", + "Fzb", "yuk", "zhg", "hjs", "yuc", "zha", "hbw", "yuE", "zhD", "hDy", "yuC", "yuB", "wmk", + "ydg", "zEr", "xqk", "wmc", "zhr", "xqc", "yva", "ydD", "xqE", "wmC", "xqC", "wmB", "xqB", + "tak", "wng", "ydr", "vik", "tac", "wna", "vic", "xra", "wnD", "viE", "taC", "viC", "taB", + "viB", "mik", "tbg", "wnr", "qyk", "mic", "tba", "qyc", "vja", "tbD", "qyE", "miC", "qyC", + "miB", "qyB", "Eyk", "mjg", "tbr", "hyk", "Eyc", "mja", "hyc", "qza", "mjD", "hyE", "EyC", + "hyC", "EyB", "Ezg", "mjr", "hzg", "Eza", "hza", "EzD", "hzD", "Ezr", "ytc", "zgq", "grw", + "ytE", "zgn", "gny", "ytC", "glz", "ytB", "wlc", "ycq", "xnc", "wlE", "ycn", "xnE", "ytn", + "xnC", "wlB", "xnB", "tDc", "wlq", "vbc", "tDE", "wln", "vbE", "xnn", "vbC", "tDB", "vbB", + "mbc", "tDq", "qjc", "mbE", "tDn", "qjE", "vbn", "qjC", "mbB", "qjB", "Ejc", "mbq", "gzc", + "EjE", "mbn", "gzE", "qjn", "gzC", "EjB", "gzB", "Ejq", "gzq", "Ejn", "gzn", "yso", "zgf", + "gfy", "ysm", "gdz", "ysl", "wko", "ycf", "xlo", "ysv", "xlm", "wkl", "xll", "tBo", "wkv", + "vDo", "tBm", "vDm", "tBl", "vDl", "mDo", "tBv", "qbo", "vDv", "qbm", "mDl", "qbl", "Ebo", + "mDv", "gjo", "Ebm", "gjm", "Ebl", "gjl", "Ebv", "gjv", "yse", "gFz", "ysd", "wke", "xku", + "wkd", "xkt", "tAu", "vBu", "tAt", "vBt", "mBu", "qDu", "mBt", "qDt", "EDu", "gbu", "EDt", + "gbt", "ysF", "wkF", "xkh", "tAh", "vAx", "mAx", "qBx", "wek", "yFg", "zCr", "wec", "yFa", + "weE", "yFD", "weC", "weB", "sqk", "wfg", "yFr", "sqc", "wfa", "sqE", "wfD", "sqC", "sqB", + "lik", "srg", "wfr", "lic", "sra", "liE", "srD", "liC", "liB", "Cyk", "ljg", "srr", "Cyc", + "lja", "CyE", "ljD", "CyC", "CyB", "Czg", "ljr", "Cza", "CzD", "Czr", "yhc", "zaq", "arw", + "yhE", "zan", "any", "yhC", "alz", "yhB", "wdc", "yEq", "wvc", "wdE", "yEn", "wvE", "yhn", + "wvC", "wdB", "wvB", "snc", "wdq", "trc", "snE", "wdn", "trE", "wvn", "trC", "snB", "trB", + "lbc", "snq", "njc", "lbE", "snn", "njE", "trn", "njC", "lbB", "njB", "Cjc", "lbq", "azc", + "CjE", "lbn", "azE", "njn", "azC", "CjB", "azB", "Cjq", "azq", "Cjn", "azn", "zio", "irs", + "rfy", "zim", "inw", "rdz", "zil", "ily", "ikz", "ygo", "zaf", "afy", "yxo", "ziv", "ivy", + "adz", "yxm", "ygl", "itz", "yxl", "wco", "yEf", "wto", "wcm", "xvo", "yxv", "wcl", "xvm", + "wtl", "xvl", "slo", "wcv", "tno", "slm", "vro", "tnm", "sll", "vrm", "tnl", "vrl", "lDo", + "slv", "nbo", "lDm", "rjo", "nbm", "lDl", "rjm", "nbl", "rjl", "Cbo", "lDv", "ajo", "Cbm", + "izo", "ajm", "Cbl", "izm", "ajl", "izl", "Cbv", "ajv", "zie", "ifw", "rFz", "zid", "idy", + "icz", "yge", "aFz", "ywu", "ygd", "ihz", "ywt", "wce", "wsu", "wcd", "xtu", "wst", "xtt", + "sku", "tlu", "skt", "vnu", "tlt", "vnt", "lBu", "nDu", "lBt", "rbu", "nDt", "rbt", "CDu", + "abu", "CDt", "iju", "abt", "ijt", "ziF", "iFy", "iEz", "ygF", "ywh", "wcF", "wsh", "xsx", + "skh", "tkx", "vlx", "lAx", "nBx", "rDx", "CBx", "aDx", "ibx", "iCz", "wFc", "yCq", "wFE", + "yCn", "wFC", "wFB", "sfc", "wFq", "sfE", "wFn", "sfC", "sfB", "krc", "sfq", "krE", "sfn", + "krC", "krB", "Bjc", "krq", "BjE", "krn", "BjC", "BjB", "Bjq", "Bjn", "yao", "zDf", "Dfy", + "yam", "Ddz", "yal", "wEo", "yCf", "who", "wEm", "whm", "wEl", "whl", "sdo", "wEv", "svo", + "sdm", "svm", "sdl", "svl", "kno", "sdv", "lro", "knm", "lrm", "knl", "lrl", "Bbo", "knv", + "Djo", "Bbm", "Djm", "Bbl", "Djl", "Bbv", "Djv", "zbe", "bfw", "npz", "zbd", "bdy", "bcz", + "yae", "DFz", "yiu", "yad", "bhz", "yit", "wEe", "wgu", "wEd", "wxu", "wgt", "wxt", "scu", + "stu", "sct", "tvu", "stt", "tvt", "klu", "lnu", "klt", "nru", "lnt", "nrt", "BDu", "Dbu", + "BDt", "bju", "Dbt", "bjt", "jfs", "rpy", "jdw", "roz", "jcy", "jcj", "zbF", "bFy", "zjh", + "jhy", "bEz", "jgz", "yaF", "yih", "yyx", "wEF", "wgh", "wwx", "xxx", "sch", "ssx", "ttx", + "vvx", "kkx", "llx", "nnx", "rrx", "BBx", "DDx", "bbx", "jFw", "rmz", "jEy", "jEj", "bCz", + "jaz", "jCy", "jCj", "jBj", "wCo", "wCm", "wCl", "sFo", "wCv", "sFm", "sFl", "kfo", "sFv", + "kfm", "kfl", "Aro", "kfv", "Arm", "Arl", "Arv", "yDe", "Bpz", "yDd", "wCe", "wau", "wCd", + "wat", "sEu", "shu", "sEt", "sht", "kdu", "kvu", "kdt", "kvt", "Anu", "Bru", "Ant", "Brt", + "zDp", "Dpy", "Doz", "yDF", "ybh", "wCF", "wah", "wix", "sEh", "sgx", "sxx", "kcx", "ktx", + "lvx", "Alx", "Bnx", "Drx", "bpw", "nuz", "boy", "boj", "Dmz", "bqz", "jps", "ruy", "jow", + "ruj", "joi", "job", "bmy", "jqy", "bmj", "jqj", "jmw", "rtj", "jmi", "jmb", "blj", "jnj", + "jli", "jlb", "jkr", "sCu", "sCt", "kFu", "kFt", "Afu", "Aft", "wDh", "sCh", "sax", "kEx", + "khx", "Adx", "Avx", "Buz", "Duy", "Duj", "buw", "nxj", "bui", "bub", "Dtj", "bvj", "jus", + "rxi", "jug", "rxb", "jua", "juD", "bti", "jvi", "btb", "jvb", "jtg", "rwr", "jta", "jtD", + "bsr", "jtr", "jsq", "jsn", "Bxj", "Dxi", "Dxb", "bxg", "nyr", "bxa", "bxD", "Dwr", "bxr", + "bwq", "bwn", "pjk", "urw", "ejA", "pbs", "uny", "ebk", "pDw", "ulz", "eDs", "pBy", "eBw", + "zfc", "fjk", "prw", "zfE", "fbs", "pny", "zfC", "fDw", "plz", "zfB", "fBy", "yrc", "zfq", + "frw", "yrE", "zfn", "fny", "yrC", "flz", "yrB", "xjc", "yrq", "xjE", "yrn", "xjC", "xjB", + "uzc", "xjq", "uzE", "xjn", "uzC", "uzB", "pzc", "uzq", "pzE", "uzn", "pzC", "djA", "ors", + "ufy", "dbk", "onw", "udz", "dDs", "oly", "dBw", "okz", "dAy", "zdo", "drs", "ovy", "zdm", + "dnw", "otz", "zdl", "dly", "dkz", "yno", "zdv", "dvy", "ynm", "dtz", "ynl", "xbo", "ynv", + "xbm", "xbl", "ujo", "xbv", "ujm", "ujl", "ozo", "ujv", "ozm", "ozl", "crk", "ofw", "uFz", + "cns", "ody", "clw", "ocz", "cky", "ckj", "zcu", "cvw", "ohz", "zct", "cty", "csz", "ylu", + "cxz", "ylt", "xDu", "xDt", "ubu", "ubt", "oju", "ojt", "cfs", "oFy", "cdw", "oEz", "ccy", + "ccj", "zch", "chy", "cgz", "ykx", "xBx", "uDx", "cFw", "oCz", "cEy", "cEj", "caz", "cCy", + "cCj", "FjA", "mrs", "tfy", "Fbk", "mnw", "tdz", "FDs", "mly", "FBw", "mkz", "FAy", "zFo", + "Frs", "mvy", "zFm", "Fnw", "mtz", "zFl", "Fly", "Fkz", "yfo", "zFv", "Fvy", "yfm", "Ftz", + "yfl", "wro", "yfv", "wrm", "wrl", "tjo", "wrv", "tjm", "tjl", "mzo", "tjv", "mzm", "mzl", + "qrk", "vfw", "xpz", "hbA", "qns", "vdy", "hDk", "qlw", "vcz", "hBs", "qky", "hAw", "qkj", + "hAi", "Erk", "mfw", "tFz", "hrk", "Ens", "mdy", "hns", "qty", "mcz", "hlw", "Eky", "hky", + "Ekj", "hkj", "zEu", "Evw", "mhz", "zhu", "zEt", "hvw", "Ety", "zht", "hty", "Esz", "hsz", + "ydu", "Exz", "yvu", "ydt", "hxz", "yvt", "wnu", "xru", "wnt", "xrt", "tbu", "vju", "tbt", + "vjt", "mju", "mjt", "grA", "qfs", "vFy", "gnk", "qdw", "vEz", "gls", "qcy", "gkw", "qcj", + "gki", "gkb", "Efs", "mFy", "gvs", "Edw", "mEz", "gtw", "qgz", "gsy", "Ecj", "gsj", "zEh", + "Ehy", "zgx", "gxy", "Egz", "gwz", "ycx", "ytx", "wlx", "xnx", "tDx", "vbx", "mbx", "gfk", + "qFw", "vCz", "gds", "qEy", "gcw", "qEj", "gci", "gcb", "EFw", "mCz", "ghw", "EEy", "ggy", + "EEj", "ggj", "Eaz", "giz", "gFs", "qCy", "gEw", "qCj", "gEi", "gEb", "ECy", "gay", "ECj", + "gaj", "gCw", "qBj", "gCi", "gCb", "EBj", "gDj", "gBi", "gBb", "Crk", "lfw", "spz", "Cns", + "ldy", "Clw", "lcz", "Cky", "Ckj", "zCu", "Cvw", "lhz", "zCt", "Cty", "Csz", "yFu", "Cxz", + "yFt", "wfu", "wft", "sru", "srt", "lju", "ljt", "arA", "nfs", "tpy", "ank", "ndw", "toz", + "als", "ncy", "akw", "ncj", "aki", "akb", "Cfs", "lFy", "avs", "Cdw", "lEz", "atw", "ngz", + "asy", "Ccj", "asj", "zCh", "Chy", "zax", "axy", "Cgz", "awz", "yEx", "yhx", "wdx", "wvx", + "snx", "trx", "lbx", "rfk", "vpw", "xuz", "inA", "rds", "voy", "ilk", "rcw", "voj", "iks", + "rci", "ikg", "rcb", "ika", "afk", "nFw", "tmz", "ivk", "ads", "nEy", "its", "rgy", "nEj", + "isw", "aci", "isi", "acb", "isb", "CFw", "lCz", "ahw", "CEy", "ixw", "agy", "CEj", "iwy", + "agj", "iwj", "Caz", "aiz", "iyz", "ifA", "rFs", "vmy", "idk", "rEw", "vmj", "ics", "rEi", + "icg", "rEb", "ica", "icD", "aFs", "nCy", "ihs", "aEw", "nCj", "igw", "raj", "igi", "aEb", + "igb", "CCy", "aay", "CCj", "iiy", "aaj", "iij", "iFk", "rCw", "vlj", "iEs", "rCi", "iEg", + "rCb", "iEa", "iED", "aCw", "nBj", "iaw", "aCi", "iai", "aCb", "iab", "CBj", "aDj", "ibj", + "iCs", "rBi", "iCg", "rBb", "iCa", "iCD", "aBi", "iDi", "aBb", "iDb", "iBg", "rAr", "iBa", + "iBD", "aAr", "iBr", "iAq", "iAn", "Bfs", "kpy", "Bdw", "koz", "Bcy", "Bcj", "Bhy", "Bgz", + "yCx", "wFx", "sfx", "krx", "Dfk", "lpw", "suz", "Dds", "loy", "Dcw", "loj", "Dci", "Dcb", + "BFw", "kmz", "Dhw", "BEy", "Dgy", "BEj", "Dgj", "Baz", "Diz", "bfA", "nps", "tuy", "bdk", + "now", "tuj", "bcs", "noi", "bcg", "nob", "bca", "bcD", "DFs", "lmy", "bhs", "DEw", "lmj", + "bgw", "DEi", "bgi", "DEb", "bgb", "BCy", "Day", "BCj", "biy", "Daj", "bij", "rpk", "vuw", + "xxj", "jdA", "ros", "vui", "jck", "rog", "vub", "jcc", "roa", "jcE", "roD", "jcC", "bFk", + "nmw", "ttj", "jhk", "bEs", "nmi", "jgs", "rqi", "nmb", "jgg", "bEa", "jga", "bED", "jgD", + "DCw", "llj", "baw", "DCi", "jiw", "bai", "DCb", "jii", "bab", "jib", "BBj", "DDj", "bbj", + "jjj", "jFA", "rms", "vti", "jEk", "rmg", "vtb", "jEc", "rma", "jEE", "rmD", "jEC", "jEB", + "bCs", "nli", "jas", "bCg", "nlb", "jag", "rnb", "jaa", "bCD", "jaD", "DBi", "bDi", "DBb", + "jbi", "bDb", "jbb", "jCk", "rlg", "vsr", "jCc", "rla", "jCE", "rlD", "jCC", "jCB", "bBg", + "nkr", "jDg", "bBa", "jDa", "bBD", "jDD", "DAr", "bBr", "jDr", "jBc", "rkq", "jBE", "rkn", + "jBC", "jBB", "bAq", "jBq", "bAn", "jBn", "jAo", "rkf", "jAm", "jAl", "bAf", "jAv", "Apw", + "kez", "Aoy", "Aoj", "Aqz", "Bps", "kuy", "Bow", "kuj", "Boi", "Bob", "Amy", "Bqy", "Amj", + "Bqj", "Dpk", "luw", "sxj", "Dos", "lui", "Dog", "lub", "Doa", "DoD", "Bmw", "ktj", "Dqw", + "Bmi", "Dqi", "Bmb", "Dqb", "Alj", "Bnj", "Drj", "bpA", "nus", "txi", "bok", "nug", "txb", + "boc", "nua", "boE", "nuD", "boC", "boB", "Dms", "lti", "bqs", "Dmg", "ltb", "bqg", "nvb", + "bqa", "DmD", "bqD", "Bli", "Dni", "Blb", "bri", "Dnb", "brb", "ruk", "vxg", "xyr", "ruc", + "vxa", "ruE", "vxD", "ruC", "ruB", "bmk", "ntg", "twr", "jqk", "bmc", "nta", "jqc", "rva", + "ntD", "jqE", "bmC", "jqC", "bmB", "jqB", "Dlg", "lsr", "bng", "Dla", "jrg", "bna", "DlD", + "jra", "bnD", "jrD", "Bkr", "Dlr", "bnr", "jrr", "rtc", "vwq", "rtE", "vwn", "rtC", "rtB", + "blc", "nsq", "jnc", "blE", "nsn", "jnE", "rtn", "jnC", "blB", "jnB", "Dkq", "blq", "Dkn", + "jnq", "bln", "jnn", "rso", "vwf", "rsm", "rsl", "bko", "nsf", "jlo", "bkm", "jlm", "bkl", + "jll", "Dkf", "bkv", "jlv", "rse", "rsd", "bke", "jku", "bkd", "jkt", "Aey", "Aej", "Auw", + "khj", "Aui", "Aub", "Adj", "Avj", "Bus", "kxi", "Bug", "kxb", "Bua", "BuD", "Ati", "Bvi", + "Atb", "Bvb", "Duk", "lxg", "syr", "Duc", "lxa", "DuE", "lxD", "DuC", "DuB", "Btg", "kwr", + "Dvg", "lxr", "Dva", "BtD", "DvD", "Asr", "Btr", "Dvr", "nxc", "tyq", "nxE", "tyn", "nxC", + "nxB", "Dtc", "lwq", "bvc", "nxq", "lwn", "bvE", "DtC", "bvC", "DtB", "bvB", "Bsq", "Dtq", + "Bsn", "bvq", "Dtn", "bvn", "vyo", "xzf", "vym", "vyl", "nwo", "tyf", "rxo", "nwm", "rxm", + "nwl", "rxl", "Dso", "lwf", "bto", "Dsm", "jvo", "btm", "Dsl", "jvm", "btl", "jvl", "Bsf", + "Dsv", "btv", "jvv", "vye", "vyd", "nwe", "rwu", "nwd", "rwt", "Dse", "bsu", "Dsd", "jtu", + "bst", "jtt", "vyF", "nwF", "rwh", "DsF", "bsh", "jsx", "Ahi", "Ahb", "Axg", "kir", "Axa", + "AxD", "Agr", "Axr", "Bxc", "kyq", "BxE", "kyn", "BxC", "BxB", "Awq", "Bxq", "Awn", "Bxn", + "lyo", "szf", "lym", "lyl", "Bwo", "kyf", "Dxo", "lyv", "Dxm", "Bwl", "Dxl", "Awf", "Bwv", + "Dxv", "tze", "tzd", "lye", "nyu", "lyd", "nyt", "Bwe", "Dwu", "Bwd", "bxu", "Dwt", "bxt", + "tzF", "lyF", "nyh", "BwF", "Dwh", "bwx", "Aiq", "Ain", "Ayo", "kjf", "Aym", "Ayl", "Aif", + "Ayv", "kze", "kzd", "Aye", "Byu", "Ayd", "Byt", "szp" + }; + + private static final char[] BR_SET = { + 'A', 'B', 'C', 'D', 'E', 'F', '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', '*', '+', '-' + }; + + private static final String[] PDF_TTF = { + "00000", "00001", "00010", "00011", "00100", "00101", "00110", "00111", + "01000", "01001", "01010", "01011", "01100", "01101", "01110", "01111", "10000", "10001", + "10010", "10011", "10100", "10101", "10110", "10111", "11000", "11001", "11010", + "11011", "11100", "11101", "11110", "11111", "01", "1111111101010100", "11111101000101001" + }; + + private static final int[] ASCII_X = { + 7, 8, 8, 4, 12, 4, 4, 8, 8, 8, 12, 4, 12, 12, 12, 12, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 12, 8, 8, 4, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 8, 8, 8, 4, 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 8, 8, 8, 8 + }; + + private static final int[] ASCII_Y = { + 26, 10, 20, 15, 18, 21, 10, 28, 23, 24, 22, 20, 13, 16, 17, 19, 0, 1, 2, 3, + 4, 5, 6, 7, 8, 9, 14, 0, 1, 23, 2, 25, 3, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 4, 5, 6, 24, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 21, 27, 9 + }; + + private static final int[] MICRO_AUTOSIZE = { + 4, 6, 7, 8, 8, 10, 10, 12, 12, 13, 14, 16, 18, 18, 19, 20, 24, 24, 24, 29, 30, 33, 34, 37, 39, 46, 54, 58, 70, 72, 82, 90, 108, 126, // max codeword counts + 1, 14, 2, 7, 24, 3, 15, 25, 4, 8, 16, 5, 17, 26, 9, 6, 10, 18, 27, 11, 28, 12, 19, 13, 29, 20, 30, 21, 22, 31, 23, 32, 33, 34 // corresponding variant + }; + + /* Rows, columns, error codewords, k-offset of valid MicroPDF417 sizes from ISO/IEC 24728:2006 */ + private static final int[] MICRO_VARIANTS = { + 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // columns + 11, 14, 17, 20, 24, 28, 8, 11, 14, 17, 20, 23, 26, 6, 8, 10, 12, 15, 20, 26, 32, 38, 44, 4, 6, 8, 10, 12, 15, 20, 26, 32, 38, 44, // rows + 7, 7, 7, 8, 8, 8, 8, 9, 9, 10, 11, 13, 15, 12, 14, 16, 18, 21, 26, 32, 38, 44, 50, 8, 12, 14, 16, 18, 21, 26, 32, 38, 44, 50, // k (EC codewords) + 0, 0, 0, 7, 7, 7, 7, 15, 15, 24, 34, 57, 84, 45, 70, 99, 115, 133, 154, 180, 212, 250, 294, 7, 45, 70, 99, 115, 133, 154, 180, 212, 250, 294 // offset + }; + + /* Following is Left RAP, Centre RAP, Right RAP and Start Cluster from ISO/IEC 24728:2006 tables 10, 11 and 12 */ + private static final int[] RAP_TABLE = { + 1, 8, 36, 19, 9, 25, 1, 1, 8, 36, 19, 9, 27, 1, 7, 15, 25, 37, 1, 1, 21, 15, 1, 47, 1, 7, 15, 25, 37, 1, 1, 21, 15, 1, // left RAP + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 7, 15, 25, 37, 17, 9, 29, 31, 25, 19, 1, 7, 15, 25, 37, 17, 9, 29, 31, 25, // centre RAP + 9, 8, 36, 19, 17, 33, 1, 9, 8, 36, 19, 17, 35, 1, 7, 15, 25, 37, 33, 17, 37, 47, 49, 43, 1, 7, 15, 25, 37, 33, 17, 37, 47, 49, // right RAP + 0, 3, 6, 0, 6, 0, 0, 0, 3, 6, 0, 6, 6, 0, 0, 6, 0, 0, 0, 0, 6, 6, 0, 3, 0, 0, 6, 0, 0, 0, 0, 6, 6, 0 // start cluster + }; + + /* Left and Right Row Address Pattern from Table 2 */ + private static final String[] RAPLR = { + "", "221311", "311311", "312211", "222211", "213211", "214111", "223111", + "313111", "322111", "412111", "421111", "331111", "241111", "232111", "231211", "321211", + "411211", "411121", "411112", "321112", "312112", "311212", "311221", "311131", "311122", + "311113", "221113", "221122", "221131", "221221", "222121", "312121", "321121", "231121", + "231112", "222112", "213112", "212212", "212221", "212131", "212122", "212113", "211213", + "211123", "211132", "211141", "211231", "211222", "211312", "211321", "211411", "212311" + }; + + /* Centre Row Address Pattern from Table 2 */ + private static final String[] RAPC = { + "", "112231", "121231", "122131", "131131", "131221", "132121", "141121", + "141211", "142111", "133111", "132211", "131311", "122311", "123211", "124111", "115111", + "114211", "114121", "123121", "123112", "122212", "122221", "121321", "121411", "112411", + "113311", "113221", "113212", "113122", "122122", "131122", "131113", "122113", "113113", + "112213", "112222", "112312", "112321", "111421", "111331", "111322", "111232", "111223", + "111133", "111124", "111214", "112114", "121114", "121123", "121132", "112132", "112141" + }; + + /* MicroPDF417 coefficients from ISO/IEC 24728:2006 Annex F */ + private static final int[] MICRO_COEFFS = { + /* k = 7 */ + 76, 925, 537, 597, 784, 691, 437, + + /* k = 8 */ + 237, 308, 436, 284, 646, 653, 428, 379, + + /* k = 9 */ + 567, 527, 622, 257, 289, 362, 501, 441, 205, + + /* k = 10 */ + 377, 457, 64, 244, 826, 841, 818, 691, 266, 612, + + /* k = 11 */ + 462, 45, 565, 708, 825, 213, 15, 68, 327, 602, 904, + + /* k = 12 */ + 597, 864, 757, 201, 646, 684, 347, 127, 388, 7, 69, 851, + + /* k = 13 */ + 764, 713, 342, 384, 606, 583, 322, 592, 678, 204, 184, 394, 692, + + /* k = 14 */ + 669, 677, 154, 187, 241, 286, 274, 354, 478, 915, 691, 833, 105, 215, + + /* k = 15 */ + 460, 829, 476, 109, 904, 664, 230, 5, 80, 74, 550, 575, 147, 868, 642, + + /* k = 16 */ + 274, 562, 232, 755, 599, 524, 801, 132, 295, 116, 442, 428, 295, 42, 176, 65, + + /* k = 18 */ + 279, 577, 315, 624, 37, 855, 275, 739, 120, 297, 312, 202, 560, 321, 233, 756, + 760, 573, + + /* k = 21 */ + 108, 519, 781, 534, 129, 425, 681, 553, 422, 716, 763, 693, 624, 610, 310, 691, + 347, 165, 193, 259, 568, + + /* k = 26 */ + 443, 284, 887, 544, 788, 93, 477, 760, 331, 608, 269, 121, 159, 830, 446, 893, + 699, 245, 441, 454, 325, 858, 131, 847, 764, 169, + + /* k = 32 */ + 361, 575, 922, 525, 176, 586, 640, 321, 536, 742, 677, 742, 687, 284, 193, 517, + 273, 494, 263, 147, 593, 800, 571, 320, 803, 133, 231, 390, 685, 330, 63, 410, + + /* k = 38 */ + 234, 228, 438, 848, 133, 703, 529, 721, 788, 322, 280, 159, 738, 586, 388, 684, + 445, 680, 245, 595, 614, 233, 812, 32, 284, 658, 745, 229, 95, 689, 920, 771, + 554, 289, 231, 125, 117, 518, + + /* k = 44 */ + 476, 36, 659, 848, 678, 64, 764, 840, 157, 915, 470, 876, 109, 25, 632, 405, + 417, 436, 714, 60, 376, 97, 413, 706, 446, 21, 3, 773, 569, 267, 272, 213, + 31, 560, 231, 758, 103, 271, 572, 436, 339, 730, 82, 285, + + /* k = 50 */ + 923, 797, 576, 875, 156, 706, 63, 81, 257, 874, 411, 416, 778, 50, 205, 303, + 188, 535, 909, 155, 637, 230, 534, 96, 575, 102, 264, 233, 919, 593, 865, 26, + 579, 623, 766, 146, 10, 739, 246, 127, 71, 244, 211, 477, 920, 876, 427, 820, + 718, 435 + }; + + /** + * Creates a new PDF417 symbol instance. + */ + public Pdf417() { + setBarHeight(3); + } + + /** + * Sets the default bar height (height of a single row) for this symbol (default value is 3). + * + * @param barHeight the default bar height for this symbol + */ + @Override + public void setBarHeight(int barHeight) { + super.setBarHeight(barHeight); + } + + /** + * Sets the width of the symbol by specifying the number of columns + * of data codewords. Valid values are 1-30 for PDF417 and 1-4 + * for MicroPDF417. + * + * @param columns the number of data columns in the symbol + */ + public void setDataColumns(int columns) { + this.columns = columns; + } + + /** + * Returns the number of data columns used by this symbol, or {@code null} + * if the number of data columns has not been set. + * + * @return the number of data columns used by this symbol + */ + public Integer getDataColumns() { + return columns; + } + + /** + * Sets the height of the symbol by specifying the number of rows + * of data codewords. Valid values are 3-90 for PDF417 and 4-44 + * for MicroPDF417. + * + * @param rows the number of rows in the symbol + */ + public void setRows(int rows) { + this.rows = rows; + } + + /** + * Returns the number of rows used by this symbol, or {@code null} if + * the number of rows has not been set. + * + * @return the number of rows used by this symbol + */ + public Integer getRows() { + return rows; + } + + /** + * Set the amount of the symbol which is dedicated to error correction + * codewords. The number of codewords of error correction data is + * determined by 2(eccLevel + 1). This attribute is ignored + * when using {@link Mode#MICRO micro} mode. + * + * @param eccLevel level of error correction (0-8) + */ + public void setPreferredEccLevel(int eccLevel) { + if (eccLevel < 0 || eccLevel > 8) { + throw new IllegalArgumentException("ECC level must be between 0 and 8."); + } + preferredEccLevel = eccLevel; + } + + /** + * Returns the preferred error correction level. + * + * @return the preferred error correction level + */ + public int getPreferredEccLevel() { + return preferredEccLevel; + } + + /** + * Forces the use of the specified MicroPDF417 variant. Only valid + * when using {@link Mode#MICRO micro} mode. + * + * @param variant the MicroPDF417 variant to use + */ + public void setVariant(int variant) { + if (symbolMode != Mode.MICRO) { + throw new IllegalArgumentException("Can only set variant when using MICRO mode."); + } + if (variant < 1 || variant > 34) { + throw new IllegalArgumentException("Variant must be between 1 and 34."); + } + this.columns = MICRO_VARIANTS[variant - 1]; + this.rows = MICRO_VARIANTS[variant - 1 + 34]; + } + + /** + * If this PDF417 symbol is part of a series of PDF417 symbols appended in a structured format + * (Macro PDF417), this method sets the position of this symbol in the series. Valid values are + * 1 through 99,999 inclusive. + * + * @param position the position of this PDF417 symbol in the structured append series + */ + public void setStructuredAppendPosition(int position) { + if (position < 1 || position > 99_999) { + throw new IllegalArgumentException("Invalid PDF417 structured append position: " + position); + } + this.structuredAppendPosition = position; + } + + /** + * Returns the position of this PDF417 symbol in a series of symbols using structured append + * (Macro PDF417). If this symbol is not part of such a series, this method will return 1. + * + * @return the position of this PDF417 symbol in a series of symbols using structured append + */ + public int getStructuredAppendPosition() { + return structuredAppendPosition; + } + + /** + * If this PDF417 symbol is part of a series of PDF417 symbols appended in a structured format + * (Macro PDF417), this method sets the total number of symbols in the series. Valid values are + * 1 through 99,999 inclusive. A value of 1 indicates that this symbol is not part of a structured + * append series. + * + * @param total the total number of PDF417 symbols in the structured append series + */ + public void setStructuredAppendTotal(int total) { + if (total < 1 || total > 99_999) { + throw new IllegalArgumentException("Invalid PDF417 structured append total: " + total); + } + this.structuredAppendTotal = total; + } + + /** + * Returns the size of the series of PDF417 symbols using structured append (Macro PDF417) that + * this symbol is part of. If this symbol is not part of a structured append series, this method + * will return 1. + * + * @return size of the series that this symbol is part of + */ + public int getStructuredAppendTotal() { + return structuredAppendTotal; + } + + /** + * If this PDF417 symbol is part of a series of PDF417 symbols appended in a structured format + * (Macro PDF417), this method sets the unique file ID for the series. The value must be numeric, + * and must consist of a series of triplets (3 digits) with values between 000 and 899. + * + * @param structuredAppendFileId the unique file ID for the series that this symbol is part of + */ + public void setStructuredAppendFileId(String structuredAppendFileId) { + if (!structuredAppendFileId.matches("^(?:[0-8][0-9][0-9])+$")) { + throw new IllegalArgumentException("Invalid PDF417 structured append file ID: " + structuredAppendFileId); + } + this.structuredAppendFileId = structuredAppendFileId; + } + + /** + * Returns the unique file ID of the series of PDF417 symbols using structured append (Macro PDF417) + * that this symbol is part of. If this symbol is not part of a structured append series, this method + * will return 000. + * + * @return the unique file ID for the series that this symbol is part of + */ + public String getStructuredAppendFileId() { + return structuredAppendFileId; + } + + /** + * If this PDF417 symbol is part of a series of PDF417 symbols appended in a structured format + * (Macro PDF417), this method sets the optional file name field for the symbol series. + * + * @param structuredAppendFileName the file name for the series that this symbol is part of + */ + public void setStructuredAppendFileName(String structuredAppendFileName) { + this.structuredAppendFileName = structuredAppendFileName; + } + + /** + * Returns the optional file name of the series of PDF417 symbols using structured append (Macro PDF417) + * that this symbol is part of. If this symbol is not part of a structured append series, this method + * will return null. + * + * @return the optional file name for the series that this symbol is part of + */ + public String getStructuredAppendFileName() { + return structuredAppendFileName; + } + + /** + * If this PDF417 symbol is part of a series of PDF417 symbols appended in a structured format + * (Macro PDF417), this method sets whether or not the optional segment count field is included + * in the control block. Since the segment count is optional, it is not included by default. + * + * @param structuredAppendIncludeSegmentCount whether or not the segment count is included + */ + public void setStructuredAppendIncludeSegmentCount(boolean structuredAppendIncludeSegmentCount) { + this.structuredAppendIncludeSegmentCount = structuredAppendIncludeSegmentCount; + } + + /** + * Returns whether or not the segment count is included in the control block if this symbol is part of + * a series of PDF417 symbols appended in a structured format (Macro PDF417). By default, the segment + * count is not included. + * + * @return whether or not the segment count is included + */ + public boolean getStructuredAppendIncludeSegmentCount() { + return structuredAppendIncludeSegmentCount; + } + + /** + * Sets the type of PDF417 symbol created. Defaults to {@link Mode#NORMAL}. + * + * @param mode the type of PDF417 symbol created + */ + public void setMode(Mode mode) { + symbolMode = mode; + } + + /** + * Returns the type of PDF417 symbol created. Defaults to {@link Mode#NORMAL}. + * + * @return the type of PDF417 symbol created + */ + public Mode getMode() { + return symbolMode; + } + + /** + *

Sets the binary data to be encoded and triggers encoding, forcing the use + * of byte compaction mode. Input data will be assumed to be of the type set by + * {@link #setDataType(DataType)}. + * + *

Use {@link #setContent(String)} instead if the data to be encoded is not + * binary data or you do not want to force the use of byte compaction mode. + * + *

NOTE: Forcing the use of byte compaction mode is usually sub-optimal, + * and will result in larger symbols than would otherwise be possible. This method + * should only be used if your downstream systems require the use of byte + * compaction mode, which is not usually the case. + * + * @param data the data to encode + * @throws OkapiException if no data or data is invalid + * @see #setContent(String) + */ + public void setContent(byte[] data) { + this.forceByteCompaction = true; + super.setContent(new String(data, StandardCharsets.ISO_8859_1)); + } + + @Override + protected boolean eciSupported() { + return true; + } + + @Override + protected void encode() { + + eciProcess(); + + switch (symbolMode) { + case MICRO: + processMicroPdf417(); + break; + case NORMAL: + case TRUNCATED: + default: + processPdf417(); + break; + } + } + + private void processPdf417() { + int j, loop, offset; + int[] mccorrection = new int[520]; + int total; + int c1, c2, c3; + int[] dummy = new int[35]; + int selectedECCLevel; + StringBuilder codebarre = new StringBuilder(); + StringBuilder bin = new StringBuilder(); + + List< Block > blocks = createBlocks(inputData, forceByteCompaction); + + /* now compress the data */ + codeWordCount = 0; + + if (readerInit) { + codeWords[codeWordCount] = 921; /* Reader Initialisation */ + codeWordCount++; + } + + processEci(eciMode); + + int blockCount = 0; + for (int i = 0; i < blocks.size(); i++) { + Block block = blocks.get(i); + switch (block.mode) { + case TEX: + /* text mode */ + boolean firstBlock = (i == 0); + processText(inputData, blockCount, block.length, firstBlock); + break; + case BYT: + /* octet stream mode */ + EncodingMode lastMode = (i == 0 ? EncodingMode.TEX : blocks.get(i - 1).mode); + processBytes(inputData, blockCount, block.length, lastMode); + break; + case NUM: + /* numeric mode */ + processNumbers(inputData, blockCount, block.length, false); + break; + default: + throw new OkapiInternalException("Unknown block type: " + block.mode); + } + blockCount += block.length; + } + + int dataCount = codeWordCount; + int macroCount = addMacroCodewords(); + + /* Now take care of the number of CWs per row */ + + // if we have to default the ECC level, do so per the + // recommendations in the specification (Table E.1) + selectedECCLevel = preferredEccLevel; + if (selectedECCLevel < 0) { + if (codeWordCount <= 40) { + selectedECCLevel = 2; + } else if (codeWordCount <= 160) { + selectedECCLevel = 3; + } else if (codeWordCount <= 320) { + selectedECCLevel = 4; + } else { + selectedECCLevel = 5; + } + } + + int k = 1 << (selectedECCLevel + 1); // error correction codeword count + int needed = 1 + dataCount + macroCount + k; // not including padding codewords + + validateRows(3, 90); + validateColumns(1, 30); + + if (columns != null) { + if (rows != null) { + // user specified both columns and rows; make sure the data fits + if (columns * rows < needed) { + throw new OkapiInputException("Too few rows (" + rows + ") and columns (" + columns + ") to hold codewords (" + needed + ")"); + } + } else { + // user only specified column count; figure out row count + rows = (int) Math.max(Math.ceil(needed / (double) columns), 3); + } + } else { + if (rows != null) { + // user only specified row count; figure out column count + columns = (int) Math.ceil(needed / (double) rows); + } else { + // user didn't specify columns or rows; figure both out + columns = (int) (0.5 + Math.sqrt((needed - 1) / 3.0)); + rows = (int) Math.ceil(needed / (double) columns); + } + } + + validateRows(3, 90); + validateColumns(1, 30); + + /* add the padding (before the Macro PDF417 control block, if it exists) */ + int padCount = (columns * rows) - codeWordCount - k - 1; + int macroStart = codeWordCount - macroCount; + System.arraycopy(codeWords, macroStart, codeWords, macroStart + padCount, macroCount); + Arrays.fill(codeWords, macroStart, macroStart + padCount, 900); + codeWordCount += padCount; + + /* add the length descriptor */ + for (int i = codeWordCount; i > 0; i--) { + codeWords[i] = codeWords[i - 1]; + } + codeWordCount++; + codeWords[0] = codeWordCount; + + /* add codeword info to debug output */ + info("Codewords: "); + for (int i = 0; i < codeWordCount; i++) { + infoSpace(codeWords[i]); + } + infoLine(); + infoLine("Length Descriptor Codewords: 1"); + infoLine("Data Codewords: " + dataCount); + infoLine("Padding Codewords: " + padCount); + infoLine("Macro Codewords: " + macroCount); + infoLine("ECC Codewords: " + k); + infoLine("Total Codewords: " + (columns * rows)); + + /* 796 - we now take care of the Reed Solomon codes */ + switch (selectedECCLevel) { + case 1: + offset = 2; + break; + case 2: + offset = 6; + break; + case 3: + offset = 14; + break; + case 4: + offset = 30; + break; + case 5: + offset = 62; + break; + case 6: + offset = 126; + break; + case 7: + offset = 254; + break; + case 8: + offset = 510; + break; + default: + offset = 0; + break; + } + + for (loop = 0; loop < 520; loop++) { + mccorrection[loop] = 0; + } + + for (int i = 0; i < codeWordCount; i++) { + total = (codeWords[i] + mccorrection[k - 1]) % 929; + for (j = k - 1; j > 0; j--) { + mccorrection[j] = (mccorrection[j - 1] + 929 - (total * COEFRS[offset + j]) % 929) % 929; + } + mccorrection[0] = (929 - (total * COEFRS[offset + j]) % 929) % 929; + } + + /* we add these codes to the string */ + for (int i = k - 1; i >= 0; i--) { + codeWords[codeWordCount++] = mccorrection[i] != 0 ? 929 - mccorrection[i] : 0; + } + + /* make sure total codeword count isn't too high */ + checkCodewordCount(codeWordCount); + + assert 1 + dataCount + padCount + macroCount + k == columns * rows; + assert codeWordCount == columns * rows; + + /* 818 - The CW string is finished */ + c1 = (rows - 1) / 3; + c2 = (selectedECCLevel * 3) + (rows - 1) % 3; + c3 = columns - 1; + + readable = ""; + row_count = rows; + pattern = new String[rows]; + row_height = new int[rows]; + infoLine("Grid Size: " + columns + " X " + rows); + + /* we now encode each row */ + for (int i = 0; i < rows; i++) { + for (j = 0; j < columns; j++) { + dummy[j + 1] = codeWords[i * columns + j]; + } + k = (i / 3) * 30; + switch (i % 3) { + case 0: + offset = 0; // cluster 0 + dummy[0] = k + c1; // left row indicator + dummy[columns + 1] = k + c3; // right row indicator + break; + case 1: + offset = 929; // cluster 3 + dummy[0] = k + c2; // left row indicator + dummy[columns + 1] = k + c1; // right row indicator + break; + case 2: + offset = 1858; // cluster 6 + dummy[0] = k + c3; // left row indicator + dummy[columns + 1] = k + c2; // right row indicator + break; + } + codebarre.setLength(0); + codebarre.append("+*"); + for (j = 0; j <= columns + 1; j++) { + if (!(symbolMode == Mode.TRUNCATED && j > columns)) { + codebarre.append(CODAGEMC[offset + dummy[j]]); + codebarre.append('*'); + } + } + if(symbolMode != Mode.TRUNCATED) { + codebarre.append('-'); + } + bin.setLength(0); + for (j = 0; j < codebarre.length(); j++) { + bin.append(PDF_TTF[positionOf(codebarre.charAt(j), BR_SET)]); + } + pattern[i] = bin2pat(bin); + row_height[i] = default_height; + } + } + + private void processMicroPdf417() { /* like PDF417 only much smaller! */ + + int k, j, longueur, offset; + int total; + int LeftRAPStart, CentreRAPStart, RightRAPStart, StartCluster; + int LeftRAP, CentreRAP, RightRAP, Cluster, flip, loop; + int[] dummy = new int[5]; + int[] mccorrection = new int[50]; + StringBuilder codebarre = new StringBuilder(); + StringBuilder bin = new StringBuilder(); + + /* Encoding starts out the same as PDF417, so use the same code */ + + List< Block > blocks = createBlocks(inputData, forceByteCompaction); + + /* 541 - now compress the data */ + codeWordCount = 0; + if (readerInit) { + codeWords[codeWordCount] = 921; /* Reader Initialisation */ + codeWordCount++; + } + + processEci(eciMode); + + int blockCount = 0; + for (int i = 0; i < blocks.size(); i++) { + Block block = blocks.get(i); + switch (block.mode) { + case TEX: + /* text mode */ + processText(inputData, blockCount, block.length, false); // TODO: this shouldn't always be false? + break; + case BYT: + /* octet stream mode */ + EncodingMode lastMode = (i == 0 ? EncodingMode.TEX : blocks.get(i - 1).mode); + processBytes(inputData, blockCount, block.length, lastMode); + break; + case NUM: + /* numeric mode */ + processNumbers(inputData, blockCount, block.length, false); + break; + default: + throw new OkapiInternalException("Unknown block type: " + block.mode); + } + blockCount += block.length; + } + + int dataCount = codeWordCount; + int macroCount = addMacroCodewords(); + + /* This is where it all changes! */ + + validateRows(4, 44); + validateColumns(1, 4); + + if (columns != null) { + int max; + switch (columns) { + case 1: + max = 20; + break; + case 2: + max = 37; + break; + case 3: + max = 82; + break; + case 4: + max = 126; + break; + default: + throw new OkapiInputException("Invalid column count: " + columns); + } + if (codeWordCount > max) { + throw new OkapiInputException("Too few columns (" + columns + ") to hold data codewords (" + codeWordCount + ")"); + } + } + + /* Now figure out which variant of the symbol to use and load values accordingly */ + + int variant = getMicroPdf417Variant(codeWordCount, columns, rows); + + /* Now we have the variant we can load the data */ + + variant--; + columns = MICRO_VARIANTS[variant]; /* columns */ + rows = MICRO_VARIANTS[variant + 34]; /* rows */ + k = MICRO_VARIANTS[variant + 68]; /* number of EC CWs */ + longueur = (columns * rows) - k; /* number of non-EC CWs */ + int padCount = longueur - codeWordCount; /* amount of padding required */ + offset = MICRO_VARIANTS[variant + 102]; /* coefficient offset */ + + /* add the padding (before the Macro PDF417 control block, if it exists) */ + int macroStart = codeWordCount - macroCount; + System.arraycopy(codeWords, macroStart, codeWords, macroStart + padCount, macroCount); + Arrays.fill(codeWords, macroStart, macroStart + padCount, 900); + codeWordCount += padCount; + + /* add codeword info to debug output */ + info("Codewords: "); + for (int i = 0; i < codeWordCount; i++) { + infoSpace(codeWords[i]); + } + infoLine(); + infoLine("Data Codewords: " + dataCount); + infoLine("Padding Codewords: " + padCount); + infoLine("Macro Codewords: " + macroCount); + infoLine("ECC Codewords: " + k); + infoLine("Total Codewords: " + (columns * rows)); + + /* Reed-Solomon error correction */ + longueur = codeWordCount; + for (loop = 0; loop < 50; loop++) { + mccorrection[loop] = 0; + } + + for (int i = 0; i < longueur; i++) { + total = (codeWords[i] + mccorrection[k - 1]) % 929; + for (j = k - 1; j >= 0; j--) { + if (j == 0) { + mccorrection[j] = (929 - (total * MICRO_COEFFS[offset + j]) % 929) % 929; + } else { + mccorrection[j] = (mccorrection[j - 1] + 929 - (total * MICRO_COEFFS[offset + j]) % 929) % 929; + } + } + } + + for (j = 0; j < k; j++) { + if (mccorrection[j] != 0) { + mccorrection[j] = 929 - mccorrection[j]; + } + } + /* we add these codes to the string */ + for (int i = k - 1; i >= 0; i--) { + codeWords[codeWordCount] = mccorrection[i]; + codeWordCount++; + } + + assert dataCount + padCount + macroCount + k == columns * rows; + assert codeWordCount == columns * rows; + + /* Now get the RAP (Row Address Pattern) start values */ + LeftRAPStart = RAP_TABLE[variant]; + CentreRAPStart = RAP_TABLE[variant + 34]; + RightRAPStart = RAP_TABLE[variant + 68]; + StartCluster = RAP_TABLE[variant + 102] / 3; + + /* That's all values loaded, get on with the encoding */ + + LeftRAP = LeftRAPStart; + CentreRAP = CentreRAPStart; + RightRAP = RightRAPStart; + Cluster = StartCluster; /* Cluster can be 0, 1 or 2 for Cluster(0), Cluster(3) and Cluster(6) */ + + readable = ""; + pattern = new String[rows]; + row_count = rows; + row_height = new int[rows]; + + infoLine("Grid Size: " + columns + " X " + row_count); + + for (int i = 0; i < rows; i++) { + codebarre.setLength(0); + offset = 929 * Cluster; + for (j = 0; j < 5; j++) { + dummy[j] = 0; + } + for (j = 0; j < columns; j++) { + dummy[j + 1] = codeWords[i * columns + j]; + } + + /* Copy the data into codebarre */ + codebarre.append(RAPLR[LeftRAP]); + codebarre.append('1'); + codebarre.append(CODAGEMC[offset + dummy[1]]); + codebarre.append('1'); + if (columns == 3) { + codebarre.append(RAPC[CentreRAP]); + } + if (columns >= 2) { + codebarre.append('1'); + codebarre.append(CODAGEMC[offset + dummy[2]]); + codebarre.append('1'); + } + if (columns == 4) { + codebarre.append(RAPC[CentreRAP]); + } + if (columns >= 3) { + codebarre.append('1'); + codebarre.append(CODAGEMC[offset + dummy[3]]); + codebarre.append('1'); + } + if (columns == 4) { + codebarre.append('1'); + codebarre.append(CODAGEMC[offset + dummy[4]]); + codebarre.append('1'); + } + codebarre.append(RAPLR[RightRAP]); + codebarre.append('1'); /* stop */ + + /* Now codebarre is a mixture of letters and numbers */ + + flip = 1; + bin.setLength(0); + for (loop = 0; loop < codebarre.length(); loop++) { + if ((codebarre.charAt(loop) >= '0') && (codebarre.charAt(loop) <= '9')) { + for (k = 0; k < Character.getNumericValue(codebarre.charAt(loop)); k++) { + if (flip == 0) { + bin.append('0'); + } else { + bin.append('1'); + } + } + if (flip == 0) { + flip = 1; + } else { + flip = 0; + } + } else { + bin.append(PDF_TTF[positionOf(codebarre.charAt(loop), BR_SET)]); + } + } + + /* so now pattern[] holds the string of '1's and '0's. - copy this to the symbol */ + pattern[i] = bin2pat(bin); + row_height[i] = default_height; + + /* Set up RAPs and Cluster for next row */ + LeftRAP++; + CentreRAP++; + RightRAP++; + Cluster++; + + if (LeftRAP == 53) { + LeftRAP = 1; + } + if (CentreRAP == 53) { + CentreRAP = 1; + } + if (RightRAP == 53) { + RightRAP = 1; + } + if (Cluster == 3) { + Cluster = 0; + } + } + } + + private void validateRows(int min, int max) { + if (rows != null) { + if (rows < min) { + throw new OkapiInputException("Too few rows (" + rows + ")"); + } else if (rows > max) { + throw new OkapiInputException("Too many rows (" + rows + ")"); + } + } + } + + private void validateColumns(int min, int max) { + if (columns != null) { + if (columns < min) { + throw new OkapiInputException("Too few columns (" + columns + ")"); + } else if (columns > max) { + throw new OkapiInputException("Too many columns (" + columns + ")"); + } + } + } + + private static void checkCodewordCount(int count) { + if (count > 929) { + throw new OkapiInputException("Too many codewords required (" + count + ", but max is 929)"); + } + } + + private static EncodingMode chooseMode(int codeascii) { + if (codeascii >= '0' && codeascii <= '9') { + return EncodingMode.NUM; + } else if (codeascii == '\t' || codeascii == '\n' || codeascii == '\r' || (codeascii >= ' ' && codeascii <= '~')) { + return EncodingMode.TEX; + } else { + return EncodingMode.BYT; + } + } + + private static int getMicroPdf417Variant(int codeWordCount, Integer columns, Integer rows) { + for (int i = 0; i < 34; i++) { + int maxCodewordCount = MICRO_AUTOSIZE[i]; + if (codeWordCount <= maxCodewordCount) { + int variant = MICRO_AUTOSIZE[i + 34]; + int columnsForThisVariant = MICRO_VARIANTS[variant - 1]; + int rowsForThisVariant = MICRO_VARIANTS[variant - 1 + 34]; + if ((columns == null || columns == columnsForThisVariant) && (rows == null || rows == rowsForThisVariant)) { + return variant; + } + } + } + throw new OkapiInputException("Unable to determine MicroPDF417 variant for " + codeWordCount + " codewords"); + } + + /** Determines the encoding block groups for the specified data. */ + private static List< Block > createBlocks(int[] data, boolean forceByteCompaction) { + + if (forceByteCompaction) { + return Collections.singletonList(new Block(EncodingMode.BYT, data.length)); + } + + List< Block > blocks = new ArrayList<>(); + Block current = null; + + for (int i = 0; i < data.length; i++) { + EncodingMode mode = chooseMode(data[i]); + if ((current != null && current.mode == mode) && + (mode != EncodingMode.NUM || current.length < MAX_NUMERIC_COMPACTION_BLOCK_SIZE)) { + current.length++; + } else { + current = new Block(mode, 1); + blocks.add(current); + } + } + + smoothBlocks(blocks); + + return blocks; + } + + /** Combines adjacent blocks of different types in very specific scenarios. */ + private static void smoothBlocks(List< Block > blocks) { + + for (int i = 0; i < blocks.size(); i++) { + Block block = blocks.get(i); + EncodingMode last = (i > 0 ? blocks.get(i - 1).mode : EncodingMode.FALSE); + EncodingMode next = (i < blocks.size() - 1 ? blocks.get(i + 1).mode : EncodingMode.FALSE); + if (block.mode == EncodingMode.NUM) { + if (i == 0) { /* first block */ + if (next == EncodingMode.TEX && block.length < 8) { + block.mode = EncodingMode.TEX; + } else if (next == EncodingMode.BYT && block.length == 1) { + block.mode = EncodingMode.BYT; + } + } else if (i == blocks.size() - 1) { /* last block */ + if (last == EncodingMode.TEX && block.length < 7) { + block.mode = EncodingMode.TEX; + } else if (last == EncodingMode.BYT && block.length == 1) { + block.mode = EncodingMode.BYT; + } + } else { /* not first or last block */ + if (last == EncodingMode.BYT && next == EncodingMode.BYT && block.length < 4) { + block.mode = EncodingMode.BYT; + } else if (last == EncodingMode.BYT && next == EncodingMode.TEX && block.length < 4) { + block.mode = EncodingMode.TEX; + } else if (last == EncodingMode.TEX && next == EncodingMode.BYT && block.length < 5) { + block.mode = EncodingMode.TEX; + } else if (last == EncodingMode.TEX && next == EncodingMode.TEX && block.length < 8) { + block.mode = EncodingMode.TEX; + } else if (last == EncodingMode.NUM && next == EncodingMode.TEX && block.length < 8) { + block.mode = EncodingMode.TEX; + } + } + } + } + + mergeBlocks(blocks); + + for (int i = 0; i < blocks.size(); i++) { + Block block = blocks.get(i); + EncodingMode last = (i > 0 ? blocks.get(i - 1).mode : EncodingMode.FALSE); + EncodingMode next = (i < blocks.size() - 1 ? blocks.get(i + 1).mode : EncodingMode.FALSE); + if (block.mode == EncodingMode.TEX && i > 0) { /* not the first */ + if (i == blocks.size() - 1) { /* the last one */ + if (last == EncodingMode.BYT && block.length == 1) { + block.mode = EncodingMode.BYT; + } + } else { /* not the last one */ + if (last == EncodingMode.BYT && next == EncodingMode.BYT && block.length < 5) { + block.mode = EncodingMode.BYT; + } + if (((last == EncodingMode.BYT && next != EncodingMode.BYT) || + (last != EncodingMode.BYT && next == EncodingMode.BYT)) && (block.length < 3)) { + block.mode = EncodingMode.BYT; + } + } + } + } + + mergeBlocks(blocks); + } + + /** Combines adjacent blocks of the same type. */ + private static void mergeBlocks(List< Block > blocks) { + for (int i = 1; i < blocks.size(); i++) { + Block b1 = blocks.get(i - 1); + Block b2 = blocks.get(i); + if ((b1.mode == b2.mode) && + (b1.mode != EncodingMode.NUM || b1.length + b2.length <= MAX_NUMERIC_COMPACTION_BLOCK_SIZE)) { + b1.length += b2.length; + blocks.remove(i); + i--; + } + } + } + + private void processEci(int eci) { + if (eci == 3) { + return; // default, no need to specify + } + /* Encoding ECI assignment number, from ISO/IEC 15438 Table 8 */ + if (eci <= 899) { + checkCodewordCount(codeWordCount + 2); + codeWords[codeWordCount++] = 927; + codeWords[codeWordCount++] = eci; + } else if (eci >= 900 && eci <= 810899) { + checkCodewordCount(codeWordCount + 3); + codeWords[codeWordCount++] = 926; + codeWords[codeWordCount++] = (eci / 900) - 1; + codeWords[codeWordCount++] = eci % 900; + } else if (eci >= 810900 && eci <= 811799) { + checkCodewordCount(codeWordCount + 2); + codeWords[codeWordCount++] = 925; + codeWords[codeWordCount++] = eci - 810900; + } + } + + private void processText(int[] data, int start, int length, boolean skipLatch) { + int j, blockIndext, curtable; + int codeascii; + int wnet = 0; + int[] listet0 = new int[length]; + int[] listet1 = new int[length]; + int[] chainet = new int[length * 4]; + + /* listet will contain the table numbers and the value of each characters */ + for (blockIndext = 0; blockIndext < length; blockIndext++) { + codeascii = data[start + blockIndext]; + switch (codeascii) { + case '\t': + listet0[blockIndext] = 12; + listet1[blockIndext] = 12; + break; + case '\n': + listet0[blockIndext] = 8; + listet1[blockIndext] = 15; + break; + case '\r': + listet0[blockIndext] = 12; + listet1[blockIndext] = 11; + break; + default: + listet0[blockIndext] = ASCII_X[codeascii - 32]; + listet1[blockIndext] = ASCII_Y[codeascii - 32]; + break; + } + } + + curtable = 1; /* default table */ + for (j = 0; j < length; j++) { + if ((listet0[j] & curtable) != 0) { /* The character is in the current table */ + chainet[wnet] = listet1[j]; + wnet++; + } else { /* Obliged to change table */ + boolean flag = false; /* True if we change table for only one character */ + if (j == (length - 1)) { + flag = true; + } else { + if ((listet0[j] & listet0[j + 1]) == 0) { + flag = true; + } + } + + if (flag) { /* we change only one character - look for temporary switch */ + if (((listet0[j] & 1) != 0) && (curtable == 2)) { /* T_UPP */ + chainet[wnet] = 27; + chainet[wnet + 1] = listet1[j]; + wnet += 2; + } + if ((listet0[j] & 8) != 0) { /* T_PUN */ + chainet[wnet] = 29; + chainet[wnet + 1] = listet1[j]; + wnet += 2; + } + if (!((((listet0[j] & 1) != 0) && (curtable == 2)) || ((listet0[j] & 8) != 0))) { + /* No temporary switch available */ + flag = false; + } + } + + if (!(flag)) { + int newtable; + + if (j == (length - 1)) { + newtable = listet0[j]; + } else { + if ((listet0[j] & listet0[j + 1]) == 0) { + newtable = listet0[j]; + } else { + newtable = listet0[j] & listet0[j + 1]; + } + } + + /* Maintain the first if several tables are possible */ + switch (newtable) { + case 3: + case 5: + case 7: + case 9: + case 11: + case 13: + case 15: + newtable = 1; + break; + case 6: + case 10: + case 14: + newtable = 2; + break; + case 12: + newtable = 4; + break; + } + + /* select the switch */ + switch (curtable) { + case 1: + switch (newtable) { + case 2: + chainet[wnet] = 27; + wnet++; + break; + case 4: + chainet[wnet] = 28; + wnet++; + break; + case 8: + chainet[wnet] = 28; + wnet++; + chainet[wnet] = 25; + wnet++; + break; + } + break; + case 2: + switch (newtable) { + case 1: + chainet[wnet] = 28; + wnet++; + chainet[wnet] = 28; + wnet++; + break; + case 4: + chainet[wnet] = 28; + wnet++; + break; + case 8: + chainet[wnet] = 28; + wnet++; + chainet[wnet] = 25; + wnet++; + break; + } + break; + case 4: + switch (newtable) { + case 1: + chainet[wnet] = 28; + wnet++; + break; + case 2: + chainet[wnet] = 27; + wnet++; + break; + case 8: + chainet[wnet] = 25; + wnet++; + break; + } + break; + case 8: + switch (newtable) { + case 1: + chainet[wnet] = 29; + wnet++; + break; + case 2: + chainet[wnet] = 29; + wnet++; + chainet[wnet] = 27; + wnet++; + break; + case 4: + chainet[wnet] = 29; + wnet++; + chainet[wnet] = 28; + wnet++; + break; + } + break; + } + curtable = newtable; + /* at last we add the character */ + chainet[wnet] = listet1[j]; + wnet++; + } + } + } + + if ((wnet & 1) != 0) { + chainet[wnet] = 29; + wnet++; + } + + /* Now translate the string chainet into codewords */ + + checkCodewordCount(codeWordCount + (skipLatch ? 0 : 1) + (wnet / 2)); + + if (!skipLatch) { + // text compaction mode is the default mode for PDF417, + // so no need for an explicit latch if this is the first block + codeWords[codeWordCount] = 900; + codeWordCount++; + } + + for (j = 0; j < wnet; j += 2) { + int cw_number = (30 * chainet[j]) + chainet[j + 1]; + codeWords[codeWordCount] = cw_number; + codeWordCount++; + } + } + + private void processBytes(int[] data, int start, int length, EncodingMode lastMode) { + int len = 0; + int chunkLen = 0; + BigInteger mantisa; + BigInteger total; + BigInteger word; + + mantisa = new BigInteger("0"); + total = new BigInteger("0"); + + checkCodewordCount(codeWordCount + 1 + (5 * length / 6) + (length % 6)); + + if (length == 1 && lastMode == EncodingMode.TEX) { + codeWords[codeWordCount++] = 913; + codeWords[codeWordCount++] = data[start]; + } else { + /* select the switch for multiple of 6 bytes */ + if (length % 6 == 0) { + codeWords[codeWordCount++] = 924; + } else { + codeWords[codeWordCount++] = 901; + } + + while (len < length) { + chunkLen = length - len; + if (6 <= chunkLen) /* Take groups of 6 */{ + chunkLen = 6; + len += chunkLen; + total = BigInteger.valueOf(0); + + while ((chunkLen--) != 0) { + mantisa = BigInteger.valueOf(data[start++]); + total = total.or(mantisa.shiftLeft(chunkLen * 8)); + } + + chunkLen = 5; + + while ((chunkLen--) != 0) { + + word = total.mod(BigInteger.valueOf(900)); + codeWords[codeWordCount + chunkLen] = word.intValue(); + total = total.divide(BigInteger.valueOf(900)); + } + codeWordCount += 5; + } else /* If it remain a group of less than 6 bytes */{ + len += chunkLen; + while ((chunkLen--) != 0) { + codeWords[codeWordCount++] = data[start++]; + } + } + } + } + } + + private void processNumbers(int[] data, int start, int length, boolean skipLatch) { + + BigInteger tVal, dVal; + int[] d = new int[16]; + int cw_count; + + StringBuilder t = new StringBuilder(length + 1); + t.append('1'); + for (int i = 0; i < length; i++) { + t.append((char) data[start + i]); + } + + tVal = new BigInteger(t.toString()); + + cw_count = 0; + do { + dVal = tVal.mod(BigInteger.valueOf(900)); + d[cw_count] = dVal.intValue(); + tVal = tVal.divide(BigInteger.valueOf(900)); + cw_count++; + } while (tVal.compareTo(BigInteger.ZERO) == 1); + + checkCodewordCount(codeWordCount + (skipLatch ? 0 : 1) + cw_count); + + if (!skipLatch) { + // we don't need to latch to numeric mode in some cases, e.g. + // during numeric compaction of the Macro PDF417 segment index + codeWords[codeWordCount++] = 902; + } + + for (int i = cw_count - 1; i >= 0; i--) { + codeWords[codeWordCount++] = d[i]; + } + } + + private void processFiveDigits(int i) { + assert i >= 0; + assert i <= 99_999; + int[] data = new int[5]; + for (int x = data.length - 1; x >= 0; x--) { + data[x] = '0' + (i % 10); + i /= 10; + } + processNumbers(data, 0, data.length, true); + } + + /** Adds the Macro PDF417 control block codewords (if any). */ + private int addMacroCodewords() { + + // if the structured append series size is 1, this isn't + // actually part of a structured append series + if (structuredAppendTotal == 1) { + return 0; + } + + int macroStart = codeWordCount; + + // add the Macro marker codeword + checkCodewordCount(codeWordCount + 1); + codeWords[codeWordCount++] = 928; + + // add the segment index, padded with leading zeros to five digits + // use numeric compaction, but no latch + int segmentIndex = structuredAppendPosition - 1; + processFiveDigits(segmentIndex); + + // add the file ID (base 900, which is easy since we limit + // file ID values to triples in the range 0 to 899) + checkCodewordCount(codeWordCount + (structuredAppendFileId.length() / 3)); + for (int i = 0; i < structuredAppendFileId.length(); i += 3) { + int number = Integer.parseInt(structuredAppendFileId.substring(i, i + 3)); + codeWords[codeWordCount++] = number; + } + + // optional fields: add the file name, if specified + if (structuredAppendFileName != null && !structuredAppendFileName.isEmpty()) { + checkCodewordCount(codeWordCount + 2); + codeWords[codeWordCount++] = 923; + codeWords[codeWordCount++] = 000; + EciMode eci = EciMode.chooseFor(structuredAppendFileName, 3, 26); + int[] data2 = toBytes(structuredAppendFileName, eci.charset); + processEci(eci.mode); + processText(data2, 0, data2.length, true); + } + + // optional fields: add segment count, if requested + if (structuredAppendIncludeSegmentCount) { + checkCodewordCount(codeWordCount + 2); + codeWords[codeWordCount++] = 923; + codeWords[codeWordCount++] = 001; + processFiveDigits(structuredAppendTotal); + } + + // add the terminator to the last symbol of the series + boolean last = (structuredAppendPosition == structuredAppendTotal); + if (last) { + checkCodewordCount(codeWordCount + 1); + codeWords[codeWordCount++] = 922; + } + + return codeWordCount - macroStart; + } + + private static class Block { + + public EncodingMode mode; + public int length; + + public Block(EncodingMode mode, int length) { + this.mode = mode; + this.length = length; + } + + @Override + public String toString() { + return mode + "x" + length; + } + } +} diff --git a/src/main/java/uk/org/okapibarcode/backend/Pharmacode.java b/src/main/java/uk/org/okapibarcode/backend/Pharmacode.java index 09b16c93..036c4d05 100644 --- a/src/main/java/uk/org/okapibarcode/backend/Pharmacode.java +++ b/src/main/java/uk/org/okapibarcode/backend/Pharmacode.java @@ -1,71 +1,71 @@ -/* - * Copyright 2014 Robin Stuart - * - * 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. - */ - -package uk.org.okapibarcode.backend; - -/** - *

Implements the Pharmacode - * bar code symbology. - * - *

Pharmacode is used for the identification of pharmaceuticals. The symbology is - * able to encode whole numbers between 3 and 131070. - * - * @author Robin Stuart - */ -public class Pharmacode extends Symbol { - - @Override - protected void encode() { - - if (content.length() > 6) { - throw OkapiInputException.inputTooLong(); - } - - if (!content.matches("[0-9]+")) { - throw OkapiInputException.invalidCharactersInInput(); - } - - int tester = Integer.parseInt(content); - if (tester < 3 || tester > 131070) { - throw new OkapiInputException("Data out of range"); - } - - StringBuilder inter = new StringBuilder(); - do { - if ((tester & 1) == 0) { - inter.append('W'); - tester = (tester - 2) / 2; - } else { - inter.append('N'); - tester = (tester - 1) / 2; - } - } while (tester != 0); - - StringBuilder dest = new StringBuilder(inter.length() * 2); - for (int i = inter.length() - 1; i >= 0; i--) { - if (inter.charAt(i) == 'W') { - dest.append("32"); - } else { - dest.append("12"); - } - } - - readable = ""; - pattern = new String[] { dest.toString() }; - row_count = 1; - row_height = new int[] { -1 }; - } -} +/* + * Copyright 2014 Robin Stuart + * + * 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. + */ + +package uk.org.okapibarcode.backend; + +/** + *

Implements the Pharmacode + * bar code symbology. + * + *

Pharmacode is used for the identification of pharmaceuticals. The symbology is + * able to encode whole numbers between 3 and 131070. + * + * @author Robin Stuart + */ +public class Pharmacode extends Symbol { + + @Override + protected void encode() { + + if (content.length() > 6) { + throw OkapiInputException.inputTooLong(); + } + + if (!content.matches("[0-9]+")) { + throw OkapiInputException.invalidCharactersInInput(); + } + + int tester = Integer.parseInt(content); + if (tester < 3 || tester > 131070) { + throw new OkapiInputException("Data out of range"); + } + + StringBuilder inter = new StringBuilder(); + do { + if ((tester & 1) == 0) { + inter.append('W'); + tester = (tester - 2) / 2; + } else { + inter.append('N'); + tester = (tester - 1) / 2; + } + } while (tester != 0); + + StringBuilder dest = new StringBuilder(inter.length() * 2); + for (int i = inter.length() - 1; i >= 0; i--) { + if (inter.charAt(i) == 'W') { + dest.append("32"); + } else { + dest.append("12"); + } + } + + readable = ""; + pattern = new String[] { dest.toString() }; + row_count = 1; + row_height = new int[] { -1 }; + } +} diff --git a/src/main/java/uk/org/okapibarcode/backend/Pharmacode2Track.java b/src/main/java/uk/org/okapibarcode/backend/Pharmacode2Track.java index 7fa5ed45..73baf584 100644 --- a/src/main/java/uk/org/okapibarcode/backend/Pharmacode2Track.java +++ b/src/main/java/uk/org/okapibarcode/backend/Pharmacode2Track.java @@ -1,108 +1,108 @@ -/* - * Copyright 2014 Robin Stuart - * - * 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. - */ - -package uk.org.okapibarcode.backend; - -import uk.org.okapibarcode.graphics.Rectangle; - -/** - *

Implements the Two-Track Pharmacode bar code symbology. - * - *

Pharmacode Two-Track is an alternative system to Pharmacode One-Track used - * for the identification of pharmaceuticals. The symbology is able to encode - * whole numbers between 4 and 64570080. - * - * @author Robin Stuart - */ -public class Pharmacode2Track extends Symbol { - - @Override - protected void encode() { - - if (content.length() > 8) { - throw OkapiInputException.inputTooLong(); - } - - if (!content.matches("[0-9]+")) { - throw OkapiInputException.invalidCharactersInInput(); - } - - int tester = Integer.parseInt(content); - if (tester < 4 || tester > 64570080) { - throw new OkapiInputException("Data out of range"); - } - - StringBuilder dest = new StringBuilder(); - do { - switch (tester % 3) { - case 0: - dest.append('F'); - tester = (tester - 3) / 3; - break; - case 1: - dest.append('D'); - tester = (tester - 1) / 3; - break; - case 2: - dest.append('A'); - tester = (tester - 2) / 3; - break; - } - } while (tester != 0); - - dest.reverse(); - infoLine("Encoding: " + dest); - - readable = ""; - pattern = new String[] { dest.toString() }; - row_count = 1; - row_height = new int[] { -1 }; - } - - @Override - protected void plotSymbol() { - - int x = 0; - int w = 1; - int y = 0; - int h = 0; - - resetPlotElements(); - - for (int xBlock = 0; xBlock < pattern[0].length(); xBlock++) { - switch (pattern[0].charAt(xBlock)) { - case 'A': - y = 0; - h = default_height / 2; - break; - case 'D': - y = default_height / 2; - h = default_height / 2; - break; - case 'F': - y = 0; - h = default_height; - break; - } - Rectangle rect = new Rectangle(x, y, w, h); - rectangles.add(rect); - x += 2; - } - - symbol_width = pattern[0].length() * 2; - symbol_height = default_height; - } -} +/* + * Copyright 2014 Robin Stuart + * + * 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. + */ + +package uk.org.okapibarcode.backend; + +import uk.org.okapibarcode.graphics.Rectangle; + +/** + *

Implements the Two-Track Pharmacode bar code symbology. + * + *

Pharmacode Two-Track is an alternative system to Pharmacode One-Track used + * for the identification of pharmaceuticals. The symbology is able to encode + * whole numbers between 4 and 64570080. + * + * @author Robin Stuart + */ +public class Pharmacode2Track extends Symbol { + + @Override + protected void encode() { + + if (content.length() > 8) { + throw OkapiInputException.inputTooLong(); + } + + if (!content.matches("[0-9]+")) { + throw OkapiInputException.invalidCharactersInInput(); + } + + int tester = Integer.parseInt(content); + if (tester < 4 || tester > 64570080) { + throw new OkapiInputException("Data out of range"); + } + + StringBuilder dest = new StringBuilder(); + do { + switch (tester % 3) { + case 0: + dest.append('F'); + tester = (tester - 3) / 3; + break; + case 1: + dest.append('D'); + tester = (tester - 1) / 3; + break; + case 2: + dest.append('A'); + tester = (tester - 2) / 3; + break; + } + } while (tester != 0); + + dest.reverse(); + infoLine("Encoding: " + dest); + + readable = ""; + pattern = new String[] { dest.toString() }; + row_count = 1; + row_height = new int[] { -1 }; + } + + @Override + protected void plotSymbol() { + + int x = 0; + int w = 1; + int y = 0; + int h = 0; + + resetPlotElements(); + + for (int xBlock = 0; xBlock < pattern[0].length(); xBlock++) { + switch (pattern[0].charAt(xBlock)) { + case 'A': + y = 0; + h = default_height / 2; + break; + case 'D': + y = default_height / 2; + h = default_height / 2; + break; + case 'F': + y = 0; + h = default_height; + break; + } + Rectangle rect = new Rectangle(x, y, w, h); + rectangles.add(rect); + x += 2; + } + + symbol_width = pattern[0].length() * 2; + symbol_height = default_height; + } +} diff --git a/src/main/java/uk/org/okapibarcode/backend/Pharmazentralnummer.java b/src/main/java/uk/org/okapibarcode/backend/Pharmazentralnummer.java index f4c7d313..f4ff7867 100644 --- a/src/main/java/uk/org/okapibarcode/backend/Pharmazentralnummer.java +++ b/src/main/java/uk/org/okapibarcode/backend/Pharmazentralnummer.java @@ -1,69 +1,69 @@ -/* - * Copyright 2015 Robin Stuart - * - * 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. - */ - -package uk.org.okapibarcode.backend; - -/** - *

PZN8 is a Code 39 based symbology used by the pharmaceutical industry in - * Germany. PZN8 encodes a 7-digit number and includes a modulo-10 check digit. - * - * @author Robin Stuart - */ -public class Pharmazentralnummer extends Symbol { - - @Override - protected void encode() { - - int len = content.length(); - if (len > 7) { - throw OkapiInputException.inputTooLong(); - } - - if (!content.matches("[0-9]+")) { - throw OkapiInputException.invalidCharactersInInput(); - } - - StringBuilder localstr = new StringBuilder(); - localstr.append('-'); - int zeroes = 7 - len + 1; - for (int i = 1; i < zeroes; i++) { - localstr.append('0'); - } - localstr.append(content); - - int count = 0; - for (int i = 1; i < 8; i++) { - count += i * Character.getNumericValue(localstr.charAt(i)); - } - - int check_digit = count % 11; - if (check_digit == 10) { - throw new OkapiInputException("Not a valid PZN identifier, check digit is 10"); - } - - infoLine("Check Digit: " + check_digit); - localstr.append((char) (check_digit + '0')); - - Code3Of9 code39 = new Code3Of9(); - code39.setContent(localstr.toString()); - - readable = "PZN" + localstr; - pattern = new String[1]; - pattern[0] = code39.pattern[0]; - row_count = 1; - row_height = new int[] { -1 }; - } -} +/* + * Copyright 2015 Robin Stuart + * + * 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. + */ + +package uk.org.okapibarcode.backend; + +/** + *

PZN8 is a Code 39 based symbology used by the pharmaceutical industry in + * Germany. PZN8 encodes a 7-digit number and includes a modulo-10 check digit. + * + * @author Robin Stuart + */ +public class Pharmazentralnummer extends Symbol { + + @Override + protected void encode() { + + int len = content.length(); + if (len > 7) { + throw OkapiInputException.inputTooLong(); + } + + if (!content.matches("[0-9]+")) { + throw OkapiInputException.invalidCharactersInInput(); + } + + StringBuilder localstr = new StringBuilder(); + localstr.append('-'); + int zeroes = 7 - len + 1; + for (int i = 1; i < zeroes; i++) { + localstr.append('0'); + } + localstr.append(content); + + int count = 0; + for (int i = 1; i < 8; i++) { + count += i * Character.getNumericValue(localstr.charAt(i)); + } + + int check_digit = count % 11; + if (check_digit == 10) { + throw new OkapiInputException("Not a valid PZN identifier, check digit is 10"); + } + + infoLine("Check Digit: " + check_digit); + localstr.append((char) (check_digit + '0')); + + Code3Of9 code39 = new Code3Of9(); + code39.setContent(localstr.toString()); + + readable = "PZN" + localstr; + pattern = new String[1]; + pattern[0] = code39.pattern[0]; + row_count = 1; + row_height = new int[] { -1 }; + } +} diff --git a/src/main/java/uk/org/okapibarcode/backend/Postnet.java b/src/main/java/uk/org/okapibarcode/backend/Postnet.java index 43b69261..3f7eed80 100644 --- a/src/main/java/uk/org/okapibarcode/backend/Postnet.java +++ b/src/main/java/uk/org/okapibarcode/backend/Postnet.java @@ -1,178 +1,178 @@ -/* - * Copyright 2014 Robin Stuart - * - * 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. - */ - -package uk.org.okapibarcode.backend; - -import static uk.org.okapibarcode.backend.HumanReadableLocation.NONE; -import static uk.org.okapibarcode.backend.HumanReadableLocation.TOP; - -import uk.org.okapibarcode.graphics.Rectangle; -import uk.org.okapibarcode.graphics.TextBox; - -/** - *

Implements POSTNET and - * PLANET - * bar code symbologies. - * - *

POSTNET and PLANET both use numerical input data and include a modulo-10 check digit. - * - * @author Robin Stuart - */ -public class Postnet extends Symbol { - - public static enum Mode { - PLANET, POSTNET - } - - private static final String[] PN_TABLE = { - "LLSSS", "SSSLL", "SSLSL", "SSLLS", "SLSSL", "SLSLS", "SLLSS", "LSSSL", "LSSLS", "LSLSS" - }; - - private static final String[] PL_TABLE = { - "SSLLL", "LLLSS", "LLSLS", "LLSSL", "LSLLS", "LSLSL", "LSSLL", "SLLLS", "SLLSL", "SLSLL" - }; - - private Mode mode; - private double moduleWidthRatio; - - public Postnet() { - this.mode = Mode.POSTNET; - this.moduleWidthRatio = 1.5; - this.default_height = 12; - this.humanReadableLocation = HumanReadableLocation.NONE; - } - - /** - * Sets the barcode mode (PLANET or POSTNET). The default mode is POSTNET. - * - * @param mode the barcode mode (PLANET or POSTNET) - */ - public void setMode(Mode mode) { - this.mode = mode; - } - - /** - * Returns the barcode mode (PLANET or POSTNET). The default mode is POSTNET. - * - * @return the barcode mode (PLANET or POSTNET) - */ - public Mode getMode() { - return mode; - } - - /** - * Sets the ratio of space width to bar width. The default value is {@code 1.5} (spaces are 50% wider than bars). - * - * @param moduleWidthRatio the ratio of space width to bar width - */ - public void setModuleWidthRatio(double moduleWidthRatio) { - this.moduleWidthRatio = moduleWidthRatio; - } - - /** - * Returns the ratio of space width to bar width. - * - * @return the ratio of space width to bar width - */ - public double getModuleWidthRatio() { - return moduleWidthRatio; - } - - @Override - protected void encode() { - String[] table = (mode == Mode.POSTNET ? PN_TABLE : PL_TABLE); - encode(table); - } - - private void encode(String[] table) { - - if (content.length() > 38) { - throw OkapiInputException.inputTooLong(); - } - - if (!content.matches("[0-9]*")) { - throw OkapiInputException.invalidCharactersInInput(); - } - - int sum = 0; - int destLen = 7 + (content.length() * 5); - StringBuilder dest = new StringBuilder(destLen); - dest.append('L'); - - for (int i = 0; i < content.length(); i++) { - dest.append(table[content.charAt(i) - '0']); - sum += content.charAt(i) - '0'; - } - - int checkDigit = (10 - (sum % 10)) % 10; - infoLine("Check Digit: " + checkDigit); - - dest.append(table[checkDigit]); - dest.append('L'); - - assert dest.length() == destLen; - - infoLine("Encoding: " + dest); - readable = content; - pattern = new String[] { dest.toString() }; - row_count = 1; - row_height = new int[] { -1 }; - } - - @Override - protected void plotSymbol() { - int xBlock, shortHeight; - double x, y, w, h, dx; - - resetPlotElements(); - - int baseY; - if (humanReadableLocation == TOP) { - baseY = getTheoreticalHumanReadableHeight(); - } else { - baseY = 0; - } - - x = 0; - w = moduleWidth; - dx = (1 + moduleWidthRatio) * w; - shortHeight = (int) (0.4 * default_height); - for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) { - if (pattern[0].charAt(xBlock) == 'L') { - y = baseY; - h = default_height; - } else { - y = baseY + default_height - shortHeight; - h = shortHeight; - } - rectangles.add(new Rectangle(x, y, w, h)); - x += dx; - } - - symbol_width = (int) Math.ceil(((pattern[0].length() - 1) * dx) + w); // final bar doesn't need extra whitespace - symbol_height = default_height; - - if (humanReadableLocation != NONE && !readable.isEmpty()) { - double baseline; - if (humanReadableLocation == TOP) { - baseline = fontSize; - } else { - baseline = symbol_height + fontSize; - } - texts.add(new TextBox(0, baseline, symbol_width, readable, humanReadableAlignment)); - } - } -} +/* + * Copyright 2014 Robin Stuart + * + * 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. + */ + +package uk.org.okapibarcode.backend; + +import static uk.org.okapibarcode.backend.HumanReadableLocation.NONE; +import static uk.org.okapibarcode.backend.HumanReadableLocation.TOP; + +import uk.org.okapibarcode.graphics.Rectangle; +import uk.org.okapibarcode.graphics.TextBox; + +/** + *

Implements POSTNET and + * PLANET + * bar code symbologies. + * + *

POSTNET and PLANET both use numerical input data and include a modulo-10 check digit. + * + * @author Robin Stuart + */ +public class Postnet extends Symbol { + + public static enum Mode { + PLANET, POSTNET + } + + private static final String[] PN_TABLE = { + "LLSSS", "SSSLL", "SSLSL", "SSLLS", "SLSSL", "SLSLS", "SLLSS", "LSSSL", "LSSLS", "LSLSS" + }; + + private static final String[] PL_TABLE = { + "SSLLL", "LLLSS", "LLSLS", "LLSSL", "LSLLS", "LSLSL", "LSSLL", "SLLLS", "SLLSL", "SLSLL" + }; + + private Mode mode; + private double moduleWidthRatio; + + public Postnet() { + this.mode = Mode.POSTNET; + this.moduleWidthRatio = 1.5; + this.default_height = 12; + this.humanReadableLocation = HumanReadableLocation.NONE; + } + + /** + * Sets the barcode mode (PLANET or POSTNET). The default mode is POSTNET. + * + * @param mode the barcode mode (PLANET or POSTNET) + */ + public void setMode(Mode mode) { + this.mode = mode; + } + + /** + * Returns the barcode mode (PLANET or POSTNET). The default mode is POSTNET. + * + * @return the barcode mode (PLANET or POSTNET) + */ + public Mode getMode() { + return mode; + } + + /** + * Sets the ratio of space width to bar width. The default value is {@code 1.5} (spaces are 50% wider than bars). + * + * @param moduleWidthRatio the ratio of space width to bar width + */ + public void setModuleWidthRatio(double moduleWidthRatio) { + this.moduleWidthRatio = moduleWidthRatio; + } + + /** + * Returns the ratio of space width to bar width. + * + * @return the ratio of space width to bar width + */ + public double getModuleWidthRatio() { + return moduleWidthRatio; + } + + @Override + protected void encode() { + String[] table = (mode == Mode.POSTNET ? PN_TABLE : PL_TABLE); + encode(table); + } + + private void encode(String[] table) { + + if (content.length() > 38) { + throw OkapiInputException.inputTooLong(); + } + + if (!content.matches("[0-9]*")) { + throw OkapiInputException.invalidCharactersInInput(); + } + + int sum = 0; + int destLen = 7 + (content.length() * 5); + StringBuilder dest = new StringBuilder(destLen); + dest.append('L'); + + for (int i = 0; i < content.length(); i++) { + dest.append(table[content.charAt(i) - '0']); + sum += content.charAt(i) - '0'; + } + + int checkDigit = (10 - (sum % 10)) % 10; + infoLine("Check Digit: " + checkDigit); + + dest.append(table[checkDigit]); + dest.append('L'); + + assert dest.length() == destLen; + + infoLine("Encoding: " + dest); + readable = content; + pattern = new String[] { dest.toString() }; + row_count = 1; + row_height = new int[] { -1 }; + } + + @Override + protected void plotSymbol() { + int xBlock, shortHeight; + double x, y, w, h, dx; + + resetPlotElements(); + + int baseY; + if (humanReadableLocation == TOP) { + baseY = getTheoreticalHumanReadableHeight(); + } else { + baseY = 0; + } + + x = 0; + w = moduleWidth; + dx = (1 + moduleWidthRatio) * w; + shortHeight = (int) (0.4 * default_height); + for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) { + if (pattern[0].charAt(xBlock) == 'L') { + y = baseY; + h = default_height; + } else { + y = baseY + default_height - shortHeight; + h = shortHeight; + } + rectangles.add(new Rectangle(x, y, w, h)); + x += dx; + } + + symbol_width = (int) Math.ceil(((pattern[0].length() - 1) * dx) + w); // final bar doesn't need extra whitespace + symbol_height = default_height; + + if (humanReadableLocation != NONE && !readable.isEmpty()) { + double baseline; + if (humanReadableLocation == TOP) { + baseline = fontSize; + } else { + baseline = symbol_height + fontSize; + } + texts.add(new TextBox(0, baseline, symbol_width, readable, humanReadableAlignment)); + } + } +} diff --git a/src/main/java/uk/org/okapibarcode/backend/RoyalMail4State.java b/src/main/java/uk/org/okapibarcode/backend/RoyalMail4State.java index 2df7db27..45724d4a 100644 --- a/src/main/java/uk/org/okapibarcode/backend/RoyalMail4State.java +++ b/src/main/java/uk/org/okapibarcode/backend/RoyalMail4State.java @@ -1,136 +1,136 @@ -/* - * Copyright 2014 Robin Stuart - * - * 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. - */ - -package uk.org.okapibarcode.backend; - -import static uk.org.okapibarcode.util.Arrays.positionOf; - -import java.util.Locale; - -import uk.org.okapibarcode.graphics.Rectangle; - -/** - *

Encodes data according to the Royal Mail 4-State Country Code. - * - *

Data input can consist of numbers 0-9 and letters A-Z and usually includes - * delivery postal code followed by house number. A check digit is calculated - * and added. - * - * @author Robin Stuart - */ -public class RoyalMail4State extends Symbol { - - private static final String[] ROYAL_TABLE = { - "TTFF", "TDAF", "TDFA", "DTAF", "DTFA", "DDAA", "TADF", "TFTF", "TFDA", "DATF", - "DADA", "DFTA", "TAFD", "TFAD", "TFFT", "DAAD", "DAFT", "DFAT", "ATDF", "ADTF", - "ADDA", "FTTF", "FTDA", "FDTA", "ATFD", "ADAD", "ADFT", "FTAD", "FTFT", "FDAT", - "AADD", "AFTD", "AFDT", "FATD", "FADT", "FFTT" - }; - - private static final int[][] ROYAL_VALUES = { - { 1, 1 }, { 1, 2 }, { 1, 3 }, { 1, 4 }, { 1, 5 }, { 1, 0 }, { 2, 1 }, { 2, 2 }, { 2, 3 }, { 2, 4 }, - { 2, 5 }, { 2, 0 }, { 3, 1 }, { 3, 2 }, { 3, 3 }, { 3, 4 }, { 3, 5 }, { 3, 0 }, { 4, 1 }, { 4, 2 }, - { 4, 3 }, { 4, 4 }, { 4, 5 }, { 4, 0 }, { 5, 1 }, { 5, 2 }, { 5, 3 }, { 5, 4 }, { 5, 5 }, { 5, 0 }, - { 0, 1 }, { 0, 2 }, { 0, 3 }, { 0, 4 }, { 0, 5 }, { 0, 0 } - }; - - private static final char[] KR_SET = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - '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' - }; - - @Override - protected void encode() { - - content = content.toUpperCase(Locale.ENGLISH); - if (!content.matches("[0-9A-Z]+")) { - throw OkapiInputException.invalidCharactersInInput(); - } - - int top = 0; - int bottom = 0; - StringBuilder dest = new StringBuilder(); - dest.append('A'); - for (int i = 0; i < content.length(); i++) { - int index = positionOf(content.charAt(i), KR_SET); - dest.append(ROYAL_TABLE[index]); - top += ROYAL_VALUES[index][0]; - bottom += ROYAL_VALUES[index][1]; - } - - /* calculate check digit */ - int row = (top % 6) - 1; - int column = (bottom % 6) - 1; - if (row == -1) { - row = 5; - } - if (column == -1) { - column = 5; - } - int check = (6 * row) + column; - dest.append(ROYAL_TABLE[check]); - infoLine("Check Digit: " + check); - - /* stop character */ - dest.append('F'); - infoLine("Encoding: " + dest); - - readable = ""; - pattern = new String[] { dest.toString() }; - row_count = 1; - row_height = new int[] { -1 }; - } - - @Override - protected void plotSymbol() { - - int x = 0; - int w = 1; - int y = 0; - int h = 0; - - resetPlotElements(); - - for (int xBlock = 0; xBlock < pattern[0].length(); xBlock++) { - switch (pattern[0].charAt(xBlock)) { - case 'A': - y = 0; - h = 5; - break; - case 'D': - y = 3; - h = 5; - break; - case 'F': - y = 0; - h = 8; - break; - case 'T': - y = 3; - h = 2; - break; - } - Rectangle rect = new Rectangle(x, y, w, h); - rectangles.add(rect); - x += 2; - } - - symbol_width = pattern[0].length() * 2; - symbol_height = 8; - } -} +/* + * Copyright 2014 Robin Stuart + * + * 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. + */ + +package uk.org.okapibarcode.backend; + +import static uk.org.okapibarcode.util.Arrays.positionOf; + +import java.util.Locale; + +import uk.org.okapibarcode.graphics.Rectangle; + +/** + *

Encodes data according to the Royal Mail 4-State Country Code. + * + *

Data input can consist of numbers 0-9 and letters A-Z and usually includes + * delivery postal code followed by house number. A check digit is calculated + * and added. + * + * @author Robin Stuart + */ +public class RoyalMail4State extends Symbol { + + private static final String[] ROYAL_TABLE = { + "TTFF", "TDAF", "TDFA", "DTAF", "DTFA", "DDAA", "TADF", "TFTF", "TFDA", "DATF", + "DADA", "DFTA", "TAFD", "TFAD", "TFFT", "DAAD", "DAFT", "DFAT", "ATDF", "ADTF", + "ADDA", "FTTF", "FTDA", "FDTA", "ATFD", "ADAD", "ADFT", "FTAD", "FTFT", "FDAT", + "AADD", "AFTD", "AFDT", "FATD", "FADT", "FFTT" + }; + + private static final int[][] ROYAL_VALUES = { + { 1, 1 }, { 1, 2 }, { 1, 3 }, { 1, 4 }, { 1, 5 }, { 1, 0 }, { 2, 1 }, { 2, 2 }, { 2, 3 }, { 2, 4 }, + { 2, 5 }, { 2, 0 }, { 3, 1 }, { 3, 2 }, { 3, 3 }, { 3, 4 }, { 3, 5 }, { 3, 0 }, { 4, 1 }, { 4, 2 }, + { 4, 3 }, { 4, 4 }, { 4, 5 }, { 4, 0 }, { 5, 1 }, { 5, 2 }, { 5, 3 }, { 5, 4 }, { 5, 5 }, { 5, 0 }, + { 0, 1 }, { 0, 2 }, { 0, 3 }, { 0, 4 }, { 0, 5 }, { 0, 0 } + }; + + private static final char[] KR_SET = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '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' + }; + + @Override + protected void encode() { + + content = content.toUpperCase(Locale.ENGLISH); + if (!content.matches("[0-9A-Z]+")) { + throw OkapiInputException.invalidCharactersInInput(); + } + + int top = 0; + int bottom = 0; + StringBuilder dest = new StringBuilder(); + dest.append('A'); + for (int i = 0; i < content.length(); i++) { + int index = positionOf(content.charAt(i), KR_SET); + dest.append(ROYAL_TABLE[index]); + top += ROYAL_VALUES[index][0]; + bottom += ROYAL_VALUES[index][1]; + } + + /* calculate check digit */ + int row = (top % 6) - 1; + int column = (bottom % 6) - 1; + if (row == -1) { + row = 5; + } + if (column == -1) { + column = 5; + } + int check = (6 * row) + column; + dest.append(ROYAL_TABLE[check]); + infoLine("Check Digit: " + check); + + /* stop character */ + dest.append('F'); + infoLine("Encoding: " + dest); + + readable = ""; + pattern = new String[] { dest.toString() }; + row_count = 1; + row_height = new int[] { -1 }; + } + + @Override + protected void plotSymbol() { + + int x = 0; + int w = 1; + int y = 0; + int h = 0; + + resetPlotElements(); + + for (int xBlock = 0; xBlock < pattern[0].length(); xBlock++) { + switch (pattern[0].charAt(xBlock)) { + case 'A': + y = 0; + h = 5; + break; + case 'D': + y = 3; + h = 5; + break; + case 'F': + y = 0; + h = 8; + break; + case 'T': + y = 3; + h = 2; + break; + } + Rectangle rect = new Rectangle(x, y, w, h); + rectangles.add(rect); + x += 2; + } + + symbol_width = pattern[0].length() * 2; + symbol_height = 8; + } +} diff --git a/src/main/java/uk/org/okapibarcode/backend/Telepen.java b/src/main/java/uk/org/okapibarcode/backend/Telepen.java index 92022132..e7842873 100644 --- a/src/main/java/uk/org/okapibarcode/backend/Telepen.java +++ b/src/main/java/uk/org/okapibarcode/backend/Telepen.java @@ -1,163 +1,163 @@ -/* - * Copyright 2014 Robin Stuart - * - * 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. - */ - -package uk.org.okapibarcode.backend; - -/** - *

Implements Telepen (also known as Telepen Alpha). - * - *

Telepen can encode ASCII text input and includes a modulo-127 check digit. - * Telepen Numeric allows compression of numeric data into a Telepen symbol. Data - * can consist of pairs of numbers or pairs consisting of a numerical digit followed - * by an X character. Telepen Numeric also includes a modulo-127 check digit. - * - * @author Robin Stuart - */ -public class Telepen extends Symbol { - - public static enum Mode { - NORMAL, NUMERIC - } - - private static final String[] TELE_TABLE = { - "1111111111111111", "1131313111", "33313111", "1111313131", - "3111313111", "11333131", "13133131", "111111313111", "31333111", - "1131113131", "33113131", "1111333111", "3111113131", "1113133111", - "1311133111", "111111113131", "3131113111", "11313331", "333331", - "111131113111", "31113331", "1133113111", "1313113111", "1111113331", - "31131331", "113111113111", "3311113111", "1111131331", "311111113111", - "1113111331", "1311111331", "11111111113111", "31313311", "1131311131", - "33311131", "1111313311", "3111311131", "11333311", "13133311", - "111111311131", "31331131", "1131113311", "33113311", "1111331131", - "3111113311", "1113131131", "1311131131", "111111113311", "3131111131", - "1131131311", "33131311", "111131111131", "3111131311", "1133111131", - "1313111131", "111111131311", "3113111311", "113111111131", - "3311111131", "111113111311", "311111111131", "111311111311", - "131111111311", "11111111111131", "3131311111", "11313133", "333133", - "111131311111", "31113133", "1133311111", "1313311111", "1111113133", - "313333", "113111311111", "3311311111", "11113333", "311111311111", - "11131333", "13111333", "11111111311111", "31311133", "1131331111", - "33331111", "1111311133", "3111331111", "11331133", "13131133", - "111111331111", "3113131111", "1131111133", "33111133", "111113131111", - "3111111133", "111311131111", "131111131111", "111111111133", - "31311313", "113131111111", "3331111111", "1111311313", "311131111111", - "11331313", "13131313", "11111131111111", "3133111111", "1131111313", - "33111313", "111133111111", "3111111313", "111313111111", - "131113111111", "111111111313", "313111111111", "1131131113", - "33131113", "11113111111111", "3111131113", "113311111111", - "131311111111", "111111131113", "3113111113", "11311111111111", - "331111111111", "111113111113", "31111111111111", "111311111113", - "131111111113" - }; - - private Mode mode = Mode.NORMAL; - - public void setMode(Mode mode) { - this.mode = mode; - } - - public Mode getMode() { - return mode; - } - - @Override - protected void encode() { - if (mode == Mode.NORMAL) { - normalMode(); - } else { - numericMode(); - } - } - - private void normalMode() { - - if (!content.matches("[\u0000-\u007F]+")) { - throw OkapiInputException.invalidCharactersInInput(); - } - - int count = 0; - StringBuilder dest = new StringBuilder(); - dest.append(TELE_TABLE['_']); // Start - for (int i = 0; i < content.length(); i++) { - int asciicode = content.charAt(i); - dest.append(TELE_TABLE[asciicode]); - count += asciicode; - } - - int check_digit = 127 - (count % 127); - if (check_digit == 127) { - check_digit = 0; - } - infoLine("Check Digit: " + check_digit); - - dest.append(TELE_TABLE[check_digit]); - dest.append(TELE_TABLE['z']); // Stop - - readable = content; - pattern = new String[] { dest.toString() }; - row_count = 1; - row_height = new int[] { -1 }; - } - - private void numericMode() { - - if (!content.matches("[0-9X]+")) { - throw OkapiInputException.invalidCharactersInInput(); - } - - /* If input is an odd length, add a leading zero */ - String t; - if ((content.length() & 1) == 1) { - t = "0" + content; - } else { - t = content; - } - - int count = 0; - StringBuilder dest = new StringBuilder(); - dest.append(TELE_TABLE['_']); // Start - for (int i = 0; i < t.length(); i += 2) { - char c1 = t.charAt(i); - char c2 = t.charAt(i + 1); - /* Input nX is allowed, but Xn is not */ - if (c1 == 'X') { - throw new OkapiInputException("Invalid position of X in data"); - } - int glyph; - if (c2 == 'X') { - glyph = (c1 - '0') + 17; - } else { - glyph = ((10 * (c1 - '0')) + (c2 - '0')) + 27; - } - count += glyph; - dest.append(TELE_TABLE[glyph]); - } - - int check_digit = 127 - (count % 127); - if (check_digit == 127) { - check_digit = 0; - } - infoLine("Check Digit: " + check_digit); - - dest.append(TELE_TABLE[check_digit]); - dest.append(TELE_TABLE['z']); // Stop - - readable = content; - pattern = new String[] { dest.toString() }; - row_count = 1; - row_height = new int[] { -1 }; - } -} +/* + * Copyright 2014 Robin Stuart + * + * 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. + */ + +package uk.org.okapibarcode.backend; + +/** + *

Implements Telepen (also known as Telepen Alpha). + * + *

Telepen can encode ASCII text input and includes a modulo-127 check digit. + * Telepen Numeric allows compression of numeric data into a Telepen symbol. Data + * can consist of pairs of numbers or pairs consisting of a numerical digit followed + * by an X character. Telepen Numeric also includes a modulo-127 check digit. + * + * @author Robin Stuart + */ +public class Telepen extends Symbol { + + public static enum Mode { + NORMAL, NUMERIC + } + + private static final String[] TELE_TABLE = { + "1111111111111111", "1131313111", "33313111", "1111313131", + "3111313111", "11333131", "13133131", "111111313111", "31333111", + "1131113131", "33113131", "1111333111", "3111113131", "1113133111", + "1311133111", "111111113131", "3131113111", "11313331", "333331", + "111131113111", "31113331", "1133113111", "1313113111", "1111113331", + "31131331", "113111113111", "3311113111", "1111131331", "311111113111", + "1113111331", "1311111331", "11111111113111", "31313311", "1131311131", + "33311131", "1111313311", "3111311131", "11333311", "13133311", + "111111311131", "31331131", "1131113311", "33113311", "1111331131", + "3111113311", "1113131131", "1311131131", "111111113311", "3131111131", + "1131131311", "33131311", "111131111131", "3111131311", "1133111131", + "1313111131", "111111131311", "3113111311", "113111111131", + "3311111131", "111113111311", "311111111131", "111311111311", + "131111111311", "11111111111131", "3131311111", "11313133", "333133", + "111131311111", "31113133", "1133311111", "1313311111", "1111113133", + "313333", "113111311111", "3311311111", "11113333", "311111311111", + "11131333", "13111333", "11111111311111", "31311133", "1131331111", + "33331111", "1111311133", "3111331111", "11331133", "13131133", + "111111331111", "3113131111", "1131111133", "33111133", "111113131111", + "3111111133", "111311131111", "131111131111", "111111111133", + "31311313", "113131111111", "3331111111", "1111311313", "311131111111", + "11331313", "13131313", "11111131111111", "3133111111", "1131111313", + "33111313", "111133111111", "3111111313", "111313111111", + "131113111111", "111111111313", "313111111111", "1131131113", + "33131113", "11113111111111", "3111131113", "113311111111", + "131311111111", "111111131113", "3113111113", "11311111111111", + "331111111111", "111113111113", "31111111111111", "111311111113", + "131111111113" + }; + + private Mode mode = Mode.NORMAL; + + public void setMode(Mode mode) { + this.mode = mode; + } + + public Mode getMode() { + return mode; + } + + @Override + protected void encode() { + if (mode == Mode.NORMAL) { + normalMode(); + } else { + numericMode(); + } + } + + private void normalMode() { + + if (!content.matches("[\u0000-\u007F]+")) { + throw OkapiInputException.invalidCharactersInInput(); + } + + int count = 0; + StringBuilder dest = new StringBuilder(); + dest.append(TELE_TABLE['_']); // Start + for (int i = 0; i < content.length(); i++) { + int asciicode = content.charAt(i); + dest.append(TELE_TABLE[asciicode]); + count += asciicode; + } + + int check_digit = 127 - (count % 127); + if (check_digit == 127) { + check_digit = 0; + } + infoLine("Check Digit: " + check_digit); + + dest.append(TELE_TABLE[check_digit]); + dest.append(TELE_TABLE['z']); // Stop + + readable = content; + pattern = new String[] { dest.toString() }; + row_count = 1; + row_height = new int[] { -1 }; + } + + private void numericMode() { + + if (!content.matches("[0-9X]+")) { + throw OkapiInputException.invalidCharactersInInput(); + } + + /* If input is an odd length, add a leading zero */ + String t; + if ((content.length() & 1) == 1) { + t = "0" + content; + } else { + t = content; + } + + int count = 0; + StringBuilder dest = new StringBuilder(); + dest.append(TELE_TABLE['_']); // Start + for (int i = 0; i < t.length(); i += 2) { + char c1 = t.charAt(i); + char c2 = t.charAt(i + 1); + /* Input nX is allowed, but Xn is not */ + if (c1 == 'X') { + throw new OkapiInputException("Invalid position of X in data"); + } + int glyph; + if (c2 == 'X') { + glyph = (c1 - '0') + 17; + } else { + glyph = ((10 * (c1 - '0')) + (c2 - '0')) + 27; + } + count += glyph; + dest.append(TELE_TABLE[glyph]); + } + + int check_digit = 127 - (count % 127); + if (check_digit == 127) { + check_digit = 0; + } + infoLine("Check Digit: " + check_digit); + + dest.append(TELE_TABLE[check_digit]); + dest.append(TELE_TABLE['z']); // Stop + + readable = content; + pattern = new String[] { dest.toString() }; + row_count = 1; + row_height = new int[] { -1 }; + } +} diff --git a/src/main/java/uk/org/okapibarcode/backend/UspsOneCode.java b/src/main/java/uk/org/okapibarcode/backend/UspsOneCode.java index c059b473..e58adde2 100644 --- a/src/main/java/uk/org/okapibarcode/backend/UspsOneCode.java +++ b/src/main/java/uk/org/okapibarcode/backend/UspsOneCode.java @@ -1,605 +1,605 @@ -/* - * Copyright 2014 Robin Stuart - * - * 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. - */ - -package uk.org.okapibarcode.backend; - -import static uk.org.okapibarcode.backend.HumanReadableLocation.NONE; -import static uk.org.okapibarcode.backend.HumanReadableLocation.TOP; - -import java.math.BigInteger; - -import uk.org.okapibarcode.graphics.Rectangle; -import uk.org.okapibarcode.graphics.TextAlignment; -import uk.org.okapibarcode.graphics.TextBox; - -/** - *

Implements USPS OneCode (also known as Intelligent Mail Barcode) according to USPS-B-3200F. - * - *

OneCode is a fixed length (65-bar) symbol which combines routing and customer information - * in a single symbol. Input data consists of a 20 digit tracking code, followed by a dash (-), - * followed by a delivery point ZIP code which can be 0, 5, 9 or 11 digits in length. - * - * @author Robin Stuart - * @see USPS OneCode Specification - */ -public class UspsOneCode extends Symbol { - - /* The following lookup tables were generated using the code in Appendix C */ - - /** Appendix D Table 1 - 5 of 13 characters */ - private static final int[] APPX_D_I = { - 0x001F, 0x1F00, 0x002F, 0x1E80, 0x0037, 0x1D80, 0x003B, 0x1B80, 0x003D, 0x1780, - 0x003E, 0x0F80, 0x004F, 0x1E40, 0x0057, 0x1D40, 0x005B, 0x1B40, 0x005D, 0x1740, - 0x005E, 0x0F40, 0x0067, 0x1CC0, 0x006B, 0x1AC0, 0x006D, 0x16C0, 0x006E, 0x0EC0, - 0x0073, 0x19C0, 0x0075, 0x15C0, 0x0076, 0x0DC0, 0x0079, 0x13C0, 0x007A, 0x0BC0, - 0x007C, 0x07C0, 0x008F, 0x1E20, 0x0097, 0x1D20, 0x009B, 0x1B20, 0x009D, 0x1720, - 0x009E, 0x0F20, 0x00A7, 0x1CA0, 0x00AB, 0x1AA0, 0x00AD, 0x16A0, 0x00AE, 0x0EA0, - 0x00B3, 0x19A0, 0x00B5, 0x15A0, 0x00B6, 0x0DA0, 0x00B9, 0x13A0, 0x00BA, 0x0BA0, - 0x00BC, 0x07A0, 0x00C7, 0x1C60, 0x00CB, 0x1A60, 0x00CD, 0x1660, 0x00CE, 0x0E60, - 0x00D3, 0x1960, 0x00D5, 0x1560, 0x00D6, 0x0D60, 0x00D9, 0x1360, 0x00DA, 0x0B60, - 0x00DC, 0x0760, 0x00E3, 0x18E0, 0x00E5, 0x14E0, 0x00E6, 0x0CE0, 0x00E9, 0x12E0, - 0x00EA, 0x0AE0, 0x00EC, 0x06E0, 0x00F1, 0x11E0, 0x00F2, 0x09E0, 0x00F4, 0x05E0, - 0x00F8, 0x03E0, 0x010F, 0x1E10, 0x0117, 0x1D10, 0x011B, 0x1B10, 0x011D, 0x1710, - 0x011E, 0x0F10, 0x0127, 0x1C90, 0x012B, 0x1A90, 0x012D, 0x1690, 0x012E, 0x0E90, - 0x0133, 0x1990, 0x0135, 0x1590, 0x0136, 0x0D90, 0x0139, 0x1390, 0x013A, 0x0B90, - 0x013C, 0x0790, 0x0147, 0x1C50, 0x014B, 0x1A50, 0x014D, 0x1650, 0x014E, 0x0E50, - 0x0153, 0x1950, 0x0155, 0x1550, 0x0156, 0x0D50, 0x0159, 0x1350, 0x015A, 0x0B50, - 0x015C, 0x0750, 0x0163, 0x18D0, 0x0165, 0x14D0, 0x0166, 0x0CD0, 0x0169, 0x12D0, - 0x016A, 0x0AD0, 0x016C, 0x06D0, 0x0171, 0x11D0, 0x0172, 0x09D0, 0x0174, 0x05D0, - 0x0178, 0x03D0, 0x0187, 0x1C30, 0x018B, 0x1A30, 0x018D, 0x1630, 0x018E, 0x0E30, - 0x0193, 0x1930, 0x0195, 0x1530, 0x0196, 0x0D30, 0x0199, 0x1330, 0x019A, 0x0B30, - 0x019C, 0x0730, 0x01A3, 0x18B0, 0x01A5, 0x14B0, 0x01A6, 0x0CB0, 0x01A9, 0x12B0, - 0x01AA, 0x0AB0, 0x01AC, 0x06B0, 0x01B1, 0x11B0, 0x01B2, 0x09B0, 0x01B4, 0x05B0, - 0x01B8, 0x03B0, 0x01C3, 0x1870, 0x01C5, 0x1470, 0x01C6, 0x0C70, 0x01C9, 0x1270, - 0x01CA, 0x0A70, 0x01CC, 0x0670, 0x01D1, 0x1170, 0x01D2, 0x0970, 0x01D4, 0x0570, - 0x01D8, 0x0370, 0x01E1, 0x10F0, 0x01E2, 0x08F0, 0x01E4, 0x04F0, 0x01E8, 0x02F0, - 0x020F, 0x1E08, 0x0217, 0x1D08, 0x021B, 0x1B08, 0x021D, 0x1708, 0x021E, 0x0F08, - 0x0227, 0x1C88, 0x022B, 0x1A88, 0x022D, 0x1688, 0x022E, 0x0E88, 0x0233, 0x1988, - 0x0235, 0x1588, 0x0236, 0x0D88, 0x0239, 0x1388, 0x023A, 0x0B88, 0x023C, 0x0788, - 0x0247, 0x1C48, 0x024B, 0x1A48, 0x024D, 0x1648, 0x024E, 0x0E48, 0x0253, 0x1948, - 0x0255, 0x1548, 0x0256, 0x0D48, 0x0259, 0x1348, 0x025A, 0x0B48, 0x025C, 0x0748, - 0x0263, 0x18C8, 0x0265, 0x14C8, 0x0266, 0x0CC8, 0x0269, 0x12C8, 0x026A, 0x0AC8, - 0x026C, 0x06C8, 0x0271, 0x11C8, 0x0272, 0x09C8, 0x0274, 0x05C8, 0x0278, 0x03C8, - 0x0287, 0x1C28, 0x028B, 0x1A28, 0x028D, 0x1628, 0x028E, 0x0E28, 0x0293, 0x1928, - 0x0295, 0x1528, 0x0296, 0x0D28, 0x0299, 0x1328, 0x029A, 0x0B28, 0x029C, 0x0728, - 0x02A3, 0x18A8, 0x02A5, 0x14A8, 0x02A6, 0x0CA8, 0x02A9, 0x12A8, 0x02AA, 0x0AA8, - 0x02AC, 0x06A8, 0x02B1, 0x11A8, 0x02B2, 0x09A8, 0x02B4, 0x05A8, 0x02B8, 0x03A8, - 0x02C3, 0x1868, 0x02C5, 0x1468, 0x02C6, 0x0C68, 0x02C9, 0x1268, 0x02CA, 0x0A68, - 0x02CC, 0x0668, 0x02D1, 0x1168, 0x02D2, 0x0968, 0x02D4, 0x0568, 0x02D8, 0x0368, - 0x02E1, 0x10E8, 0x02E2, 0x08E8, 0x02E4, 0x04E8, 0x0307, 0x1C18, 0x030B, 0x1A18, - 0x030D, 0x1618, 0x030E, 0x0E18, 0x0313, 0x1918, 0x0315, 0x1518, 0x0316, 0x0D18, - 0x0319, 0x1318, 0x031A, 0x0B18, 0x031C, 0x0718, 0x0323, 0x1898, 0x0325, 0x1498, - 0x0326, 0x0C98, 0x0329, 0x1298, 0x032A, 0x0A98, 0x032C, 0x0698, 0x0331, 0x1198, - 0x0332, 0x0998, 0x0334, 0x0598, 0x0338, 0x0398, 0x0343, 0x1858, 0x0345, 0x1458, - 0x0346, 0x0C58, 0x0349, 0x1258, 0x034A, 0x0A58, 0x034C, 0x0658, 0x0351, 0x1158, - 0x0352, 0x0958, 0x0354, 0x0558, 0x0361, 0x10D8, 0x0362, 0x08D8, 0x0364, 0x04D8, - 0x0383, 0x1838, 0x0385, 0x1438, 0x0386, 0x0C38, 0x0389, 0x1238, 0x038A, 0x0A38, - 0x038C, 0x0638, 0x0391, 0x1138, 0x0392, 0x0938, 0x0394, 0x0538, 0x03A1, 0x10B8, - 0x03A2, 0x08B8, 0x03A4, 0x04B8, 0x03C1, 0x1078, 0x03C2, 0x0878, 0x03C4, 0x0478, - 0x040F, 0x1E04, 0x0417, 0x1D04, 0x041B, 0x1B04, 0x041D, 0x1704, 0x041E, 0x0F04, - 0x0427, 0x1C84, 0x042B, 0x1A84, 0x042D, 0x1684, 0x042E, 0x0E84, 0x0433, 0x1984, - 0x0435, 0x1584, 0x0436, 0x0D84, 0x0439, 0x1384, 0x043A, 0x0B84, 0x043C, 0x0784, - 0x0447, 0x1C44, 0x044B, 0x1A44, 0x044D, 0x1644, 0x044E, 0x0E44, 0x0453, 0x1944, - 0x0455, 0x1544, 0x0456, 0x0D44, 0x0459, 0x1344, 0x045A, 0x0B44, 0x045C, 0x0744, - 0x0463, 0x18C4, 0x0465, 0x14C4, 0x0466, 0x0CC4, 0x0469, 0x12C4, 0x046A, 0x0AC4, - 0x046C, 0x06C4, 0x0471, 0x11C4, 0x0472, 0x09C4, 0x0474, 0x05C4, 0x0487, 0x1C24, - 0x048B, 0x1A24, 0x048D, 0x1624, 0x048E, 0x0E24, 0x0493, 0x1924, 0x0495, 0x1524, - 0x0496, 0x0D24, 0x0499, 0x1324, 0x049A, 0x0B24, 0x049C, 0x0724, 0x04A3, 0x18A4, - 0x04A5, 0x14A4, 0x04A6, 0x0CA4, 0x04A9, 0x12A4, 0x04AA, 0x0AA4, 0x04AC, 0x06A4, - 0x04B1, 0x11A4, 0x04B2, 0x09A4, 0x04B4, 0x05A4, 0x04C3, 0x1864, 0x04C5, 0x1464, - 0x04C6, 0x0C64, 0x04C9, 0x1264, 0x04CA, 0x0A64, 0x04CC, 0x0664, 0x04D1, 0x1164, - 0x04D2, 0x0964, 0x04D4, 0x0564, 0x04E1, 0x10E4, 0x04E2, 0x08E4, 0x0507, 0x1C14, - 0x050B, 0x1A14, 0x050D, 0x1614, 0x050E, 0x0E14, 0x0513, 0x1914, 0x0515, 0x1514, - 0x0516, 0x0D14, 0x0519, 0x1314, 0x051A, 0x0B14, 0x051C, 0x0714, 0x0523, 0x1894, - 0x0525, 0x1494, 0x0526, 0x0C94, 0x0529, 0x1294, 0x052A, 0x0A94, 0x052C, 0x0694, - 0x0531, 0x1194, 0x0532, 0x0994, 0x0534, 0x0594, 0x0543, 0x1854, 0x0545, 0x1454, - 0x0546, 0x0C54, 0x0549, 0x1254, 0x054A, 0x0A54, 0x054C, 0x0654, 0x0551, 0x1154, - 0x0552, 0x0954, 0x0561, 0x10D4, 0x0562, 0x08D4, 0x0583, 0x1834, 0x0585, 0x1434, - 0x0586, 0x0C34, 0x0589, 0x1234, 0x058A, 0x0A34, 0x058C, 0x0634, 0x0591, 0x1134, - 0x0592, 0x0934, 0x05A1, 0x10B4, 0x05A2, 0x08B4, 0x05C1, 0x1074, 0x05C2, 0x0874, - 0x0607, 0x1C0C, 0x060B, 0x1A0C, 0x060D, 0x160C, 0x060E, 0x0E0C, 0x0613, 0x190C, - 0x0615, 0x150C, 0x0616, 0x0D0C, 0x0619, 0x130C, 0x061A, 0x0B0C, 0x061C, 0x070C, - 0x0623, 0x188C, 0x0625, 0x148C, 0x0626, 0x0C8C, 0x0629, 0x128C, 0x062A, 0x0A8C, - 0x062C, 0x068C, 0x0631, 0x118C, 0x0632, 0x098C, 0x0643, 0x184C, 0x0645, 0x144C, - 0x0646, 0x0C4C, 0x0649, 0x124C, 0x064A, 0x0A4C, 0x0651, 0x114C, 0x0652, 0x094C, - 0x0661, 0x10CC, 0x0662, 0x08CC, 0x0683, 0x182C, 0x0685, 0x142C, 0x0686, 0x0C2C, - 0x0689, 0x122C, 0x068A, 0x0A2C, 0x0691, 0x112C, 0x0692, 0x092C, 0x06A1, 0x10AC, - 0x06A2, 0x08AC, 0x06C1, 0x106C, 0x06C2, 0x086C, 0x0703, 0x181C, 0x0705, 0x141C, - 0x0706, 0x0C1C, 0x0709, 0x121C, 0x070A, 0x0A1C, 0x0711, 0x111C, 0x0712, 0x091C, - 0x0721, 0x109C, 0x0722, 0x089C, 0x0741, 0x105C, 0x0742, 0x085C, 0x0781, 0x103C, - 0x0782, 0x083C, 0x080F, 0x1E02, 0x0817, 0x1D02, 0x081B, 0x1B02, 0x081D, 0x1702, - 0x081E, 0x0F02, 0x0827, 0x1C82, 0x082B, 0x1A82, 0x082D, 0x1682, 0x082E, 0x0E82, - 0x0833, 0x1982, 0x0835, 0x1582, 0x0836, 0x0D82, 0x0839, 0x1382, 0x083A, 0x0B82, - 0x0847, 0x1C42, 0x084B, 0x1A42, 0x084D, 0x1642, 0x084E, 0x0E42, 0x0853, 0x1942, - 0x0855, 0x1542, 0x0856, 0x0D42, 0x0859, 0x1342, 0x085A, 0x0B42, 0x0863, 0x18C2, - 0x0865, 0x14C2, 0x0866, 0x0CC2, 0x0869, 0x12C2, 0x086A, 0x0AC2, 0x0871, 0x11C2, - 0x0872, 0x09C2, 0x0887, 0x1C22, 0x088B, 0x1A22, 0x088D, 0x1622, 0x088E, 0x0E22, - 0x0893, 0x1922, 0x0895, 0x1522, 0x0896, 0x0D22, 0x0899, 0x1322, 0x089A, 0x0B22, - 0x08A3, 0x18A2, 0x08A5, 0x14A2, 0x08A6, 0x0CA2, 0x08A9, 0x12A2, 0x08AA, 0x0AA2, - 0x08B1, 0x11A2, 0x08B2, 0x09A2, 0x08C3, 0x1862, 0x08C5, 0x1462, 0x08C6, 0x0C62, - 0x08C9, 0x1262, 0x08CA, 0x0A62, 0x08D1, 0x1162, 0x08D2, 0x0962, 0x08E1, 0x10E2, - 0x0907, 0x1C12, 0x090B, 0x1A12, 0x090D, 0x1612, 0x090E, 0x0E12, 0x0913, 0x1912, - 0x0915, 0x1512, 0x0916, 0x0D12, 0x0919, 0x1312, 0x091A, 0x0B12, 0x0923, 0x1892, - 0x0925, 0x1492, 0x0926, 0x0C92, 0x0929, 0x1292, 0x092A, 0x0A92, 0x0931, 0x1192, - 0x0932, 0x0992, 0x0943, 0x1852, 0x0945, 0x1452, 0x0946, 0x0C52, 0x0949, 0x1252, - 0x094A, 0x0A52, 0x0951, 0x1152, 0x0961, 0x10D2, 0x0983, 0x1832, 0x0985, 0x1432, - 0x0986, 0x0C32, 0x0989, 0x1232, 0x098A, 0x0A32, 0x0991, 0x1132, 0x09A1, 0x10B2, - 0x09C1, 0x1072, 0x0A07, 0x1C0A, 0x0A0B, 0x1A0A, 0x0A0D, 0x160A, 0x0A0E, 0x0E0A, - 0x0A13, 0x190A, 0x0A15, 0x150A, 0x0A16, 0x0D0A, 0x0A19, 0x130A, 0x0A1A, 0x0B0A, - 0x0A23, 0x188A, 0x0A25, 0x148A, 0x0A26, 0x0C8A, 0x0A29, 0x128A, 0x0A2A, 0x0A8A, - 0x0A31, 0x118A, 0x0A43, 0x184A, 0x0A45, 0x144A, 0x0A46, 0x0C4A, 0x0A49, 0x124A, - 0x0A51, 0x114A, 0x0A61, 0x10CA, 0x0A83, 0x182A, 0x0A85, 0x142A, 0x0A86, 0x0C2A, - 0x0A89, 0x122A, 0x0A91, 0x112A, 0x0AA1, 0x10AA, 0x0AC1, 0x106A, 0x0B03, 0x181A, - 0x0B05, 0x141A, 0x0B06, 0x0C1A, 0x0B09, 0x121A, 0x0B11, 0x111A, 0x0B21, 0x109A, - 0x0B41, 0x105A, 0x0B81, 0x103A, 0x0C07, 0x1C06, 0x0C0B, 0x1A06, 0x0C0D, 0x1606, - 0x0C0E, 0x0E06, 0x0C13, 0x1906, 0x0C15, 0x1506, 0x0C16, 0x0D06, 0x0C19, 0x1306, - 0x0C23, 0x1886, 0x0C25, 0x1486, 0x0C26, 0x0C86, 0x0C29, 0x1286, 0x0C31, 0x1186, - 0x0C43, 0x1846, 0x0C45, 0x1446, 0x0C49, 0x1246, 0x0C51, 0x1146, 0x0C61, 0x10C6, - 0x0C83, 0x1826, 0x0C85, 0x1426, 0x0C89, 0x1226, 0x0C91, 0x1126, 0x0CA1, 0x10A6, - 0x0CC1, 0x1066, 0x0D03, 0x1816, 0x0D05, 0x1416, 0x0D09, 0x1216, 0x0D11, 0x1116, - 0x0D21, 0x1096, 0x0D41, 0x1056, 0x0D81, 0x1036, 0x0E03, 0x180E, 0x0E05, 0x140E, - 0x0E09, 0x120E, 0x0E11, 0x110E, 0x0E21, 0x108E, 0x0E41, 0x104E, 0x0E81, 0x102E, - 0x0F01, 0x101E, 0x100F, 0x1E01, 0x1017, 0x1D01, 0x101B, 0x1B01, 0x101D, 0x1701, - 0x1027, 0x1C81, 0x102B, 0x1A81, 0x102D, 0x1681, 0x1033, 0x1981, 0x1035, 0x1581, - 0x1039, 0x1381, 0x1047, 0x1C41, 0x104B, 0x1A41, 0x104D, 0x1641, 0x1053, 0x1941, - 0x1055, 0x1541, 0x1059, 0x1341, 0x1063, 0x18C1, 0x1065, 0x14C1, 0x1069, 0x12C1, - 0x1071, 0x11C1, 0x1087, 0x1C21, 0x108B, 0x1A21, 0x108D, 0x1621, 0x1093, 0x1921, - 0x1095, 0x1521, 0x1099, 0x1321, 0x10A3, 0x18A1, 0x10A5, 0x14A1, 0x10A9, 0x12A1, - 0x10B1, 0x11A1, 0x10C3, 0x1861, 0x10C5, 0x1461, 0x10C9, 0x1261, 0x10D1, 0x1161, - 0x1107, 0x1C11, 0x110B, 0x1A11, 0x110D, 0x1611, 0x1113, 0x1911, 0x1115, 0x1511, - 0x1119, 0x1311, 0x1123, 0x1891, 0x1125, 0x1491, 0x1129, 0x1291, 0x1131, 0x1191, - 0x1143, 0x1851, 0x1145, 0x1451, 0x1149, 0x1251, 0x1183, 0x1831, 0x1185, 0x1431, - 0x1189, 0x1231, 0x1207, 0x1C09, 0x120B, 0x1A09, 0x120D, 0x1609, 0x1213, 0x1909, - 0x1215, 0x1509, 0x1219, 0x1309, 0x1223, 0x1889, 0x1225, 0x1489, 0x1229, 0x1289, - 0x1243, 0x1849, 0x1245, 0x1449, 0x1283, 0x1829, 0x1285, 0x1429, 0x1303, 0x1819, - 0x1305, 0x1419, 0x1407, 0x1C05, 0x140B, 0x1A05, 0x140D, 0x1605, 0x1413, 0x1905, - 0x1415, 0x1505, 0x1423, 0x1885, 0x1425, 0x1485, 0x1443, 0x1845, 0x1483, 0x1825, - 0x1503, 0x1815, 0x1603, 0x180D, 0x1807, 0x1C03, 0x180B, 0x1A03, 0x1813, 0x1903, - 0x1823, 0x1883, 0x1843, 0x1445, 0x1249, 0x1151, 0x10E1, 0x0C46, 0x0A4A, 0x0952, - 0x08E2, 0x064C, 0x0554, 0x04E4, 0x0358, 0x02E8, 0x01F0 - }; - - /** Appendix D Table II - 2 of 13 characters */ - private static final int[] APPX_D_II = { - 0x0003, 0x1800, 0x0005, 0x1400, 0x0006, 0x0C00, 0x0009, 0x1200, 0x000A, 0x0A00, - 0x000C, 0x0600, 0x0011, 0x1100, 0x0012, 0x0900, 0x0014, 0x0500, 0x0018, 0x0300, - 0x0021, 0x1080, 0x0022, 0x0880, 0x0024, 0x0480, 0x0028, 0x0280, 0x0030, 0x0180, - 0x0041, 0x1040, 0x0042, 0x0840, 0x0044, 0x0440, 0x0048, 0x0240, 0x0050, 0x0140, - 0x0060, 0x00C0, 0x0081, 0x1020, 0x0082, 0x0820, 0x0084, 0x0420, 0x0088, 0x0220, - 0x0090, 0x0120, 0x0101, 0x1010, 0x0102, 0x0810, 0x0104, 0x0410, 0x0108, 0x0210, - 0x0201, 0x1008, 0x0202, 0x0808, 0x0204, 0x0408, 0x0401, 0x1004, 0x0402, 0x0804, - 0x0801, 0x1002, 0x1001, 0x0802, 0x0404, 0x0208, 0x0110, 0x00A0 - }; - - /** Appendix D Table IV - Bar-to-Character Mapping (reverse lookup) */ - private static final int[] APPX_D_IV = { - 67, 6, 78, 16, 86, 95, 34, 40, 45, 113, 117, 121, 62, 87, 18, 104, 41, 76, 57, 119, 115, 72, 97, - 2, 127, 26, 105, 35, 122, 52, 114, 7, 24, 82, 68, 63, 94, 44, 77, 112, 70, 100, 39, 30, 107, - 15, 125, 85, 10, 65, 54, 88, 20, 106, 46, 66, 8, 116, 29, 61, 99, 80, 90, 37, 123, 51, 25, 84, - 129, 56, 4, 109, 96, 28, 36, 47, 11, 71, 33, 102, 21, 9, 17, 49, 124, 79, 64, 91, 42, 69, 53, - 60, 14, 1, 27, 103, 126, 75, 89, 50, 120, 19, 32, 110, 92, 111, 130, 59, 31, 12, 81, 43, 55, - 5, 74, 22, 101, 128, 58, 118, 48, 108, 38, 98, 93, 23, 83, 13, 73, 3 - }; - - private double moduleWidthRatio; - private double shortHeightPercentage; - private double longHeightPercentage; - - public UspsOneCode() { - this.default_height = 8; - this.humanReadableLocation = HumanReadableLocation.NONE; - this.humanReadableAlignment = TextAlignment.LEFT; // spec section 2.4.2 - this.moduleWidthRatio = 1.43; - this.shortHeightPercentage = 0.25; - this.longHeightPercentage = 0.625; - } - - /** - * Sets the ratio of space width to bar width. The default value is {@code 1.43} (spaces are 43% wider than bars). - * - * @param moduleWidthRatio the ratio of space width to bar width - */ - public void setModuleWidthRatio(double moduleWidthRatio) { - this.moduleWidthRatio = moduleWidthRatio; - } - - /** - * Returns the ratio of space width to bar width. - * - * @return the ratio of space width to bar width - */ - public double getModuleWidthRatio() { - return moduleWidthRatio; - } - - /** - * Sets the percentage of the full symbol height used for short bars (0 - 1). The default value is {@code 0.25} (25% of full symbol height). - * - * @param shortHeightPercentage the percentage of the full symbol height used for short bars - */ - public void setShortHeightPercentage(double shortHeightPercentage) { - if (shortHeightPercentage < 0 || shortHeightPercentage > 1) { - throw new IllegalArgumentException("Height percentage must be between 0 and 1."); - } - this.shortHeightPercentage = shortHeightPercentage; - } - - /** - * Returns the percentage of the full symbol height used for short bars. - * - * @return the percentage of the full symbol height used for short bars - */ - public double getShortHeightPercentage() { - return shortHeightPercentage; - } - - /** - * Sets the percentage of the full symbol height used for long bars (0 - 1). The default value is {@code 0.625} (62.5% of full symbol height). - * - * @param longHeightPercentage the percentage of the full symbol height used for long bars - */ - public void setLongHeightPercentage(double longHeightPercentage) { - if (longHeightPercentage < 0 || longHeightPercentage > 1) { - throw new IllegalArgumentException("Height percentage must be between 0 and 1."); - } - this.longHeightPercentage = longHeightPercentage; - } - - /** - * Returns the percentage of the full symbol height used for long bars. - * - * @return the percentage of the full symbol height used for long bars - */ - public double getLongHeightPercentage() { - return longHeightPercentage; - } - - @Override - protected void encode() { - String zip = ""; - String zip_adder; - String tracker = ""; - int i, j; - int length = content.length(); - BigInteger accum; - BigInteger x_reg; - BigInteger mask; - int usps_crc; - int[] codeword = new int[10]; - int[] characters = new int[10]; - boolean[] bar_map = new boolean[130]; - char c; - - if (!content.matches("[0-9\u002D]*")) { - throw OkapiInputException.invalidCharactersInInput(); - } - - if (length > 32) { - throw OkapiInputException.inputTooLong(); - } - - /* separate the tracking code from the routing code */ - j = 0; - for (i = 0; i < length; i++) { - if (content.charAt(i) == '-') { - j = 1; - } else { - if (j == 0) { - /* reading tracker */ - tracker += content.charAt(i); - } else { - /* reading zip code */ - zip += content.charAt(i); - } - } - } - - if (tracker.length() != 20) { - throw new OkapiInputException("Tracking code is not correct length"); - } - - if (zip.length() > 11) { - throw new OkapiInputException("ZIP code is not correct length"); - } - - /* *** Step 1 - Conversion of Data Fields into Binary Data *** */ - - /* Routing code first */ - if (zip.length() > 0) { - x_reg = new BigInteger(zip); - } else { - x_reg = new BigInteger("0"); - } - - /* add weight to routing code */ - if (zip.length() > 9) { - zip_adder = "1000100001"; - } else { - if (zip.length() > 5) { - zip_adder = "100001"; - } else { - if (zip.length() > 0) { - zip_adder = "1"; - } else { - zip_adder = "0"; - } - } - } - - accum = new BigInteger(zip_adder); - accum = accum.add(x_reg); - accum = accum.multiply(BigInteger.valueOf(10)); - accum = accum.add(BigInteger.valueOf(Character.getNumericValue(tracker.charAt(0)))); - accum = accum.multiply(BigInteger.valueOf(5)); - accum = accum.add(BigInteger.valueOf(Character.getNumericValue(tracker.charAt(1)))); - for (i = 2; i < tracker.length(); i++) { - accum = accum.multiply(BigInteger.valueOf(10)); - accum = accum.add(BigInteger.valueOf(Character.getNumericValue(tracker.charAt(i)))); - } - - /* *** Step 2 - Generation of 11-bit CRC on Binary Data *** */ - - int[] byte_array = new int[13]; - for (i = 0; i < byte_array.length; i++) { - mask = accum.shiftRight(96 - (8 * i)); - mask = mask.and(new BigInteger("255")); - byte_array[i] = mask.intValue(); - } - - usps_crc = USPS_MSB_Math_CRC11GenerateFrameCheckSequence(byte_array); - - /* *** Step 3 - Conversion from Binary Data to Codewords *** */ - - /* start with codeword J which is base 636 */ - x_reg = accum.mod(BigInteger.valueOf(636)); - codeword[9] = x_reg.intValue(); - accum = accum.subtract(x_reg); - accum = accum.divide(BigInteger.valueOf(636)); - - for (i = 8; i >= 0; i--) { - x_reg = accum.mod(BigInteger.valueOf(1365)); - codeword[i] = x_reg.intValue(); - accum = accum.subtract(x_reg); - accum = accum.divide(BigInteger.valueOf(1365)); - } - - for (i = 0; i < 9; i++) { - if (codeword[i] == 1365) { - codeword[i] = 0; - } - } - - /* *** Step 4 - Inserting Additional Information into Codewords *** */ - - codeword[9] = codeword[9] * 2; - - if (usps_crc >= 1024) { - codeword[0] += 659; - } - - info("Codewords: "); - for (i = 0; i < 10; i++) { - infoSpace(codeword[i]); - } - infoLine(); - - /* *** Step 5 - Conversion from Codewords to Characters *** */ - - for (i = 0; i < 10; i++) { - if (codeword[i] < 1287) { - characters[i] = APPX_D_I[codeword[i]]; - } else { - characters[i] = APPX_D_II[codeword[i] - 1287]; - } - } - - for (i = 0; i < 10; i++) { - if ((usps_crc & (1 << i)) != 0) { - characters[i] = 0x1FFF - characters[i]; - } - } - - /* *** Step 6 - Conversion from Characters to the Intelligent Mail Barcode *** */ - - for (i = 0; i < 10; i++) { - for (j = 0; j < 13; j++) { - if ((characters[i] & (1 << j)) == 0) { - bar_map[APPX_D_IV[(13 * i) + j] - 1] = false; - } else { - bar_map[APPX_D_IV[(13 * i) + j] - 1] = true; - } - } - } - - readable = formatHumanReadableText(content); - pattern = new String[1]; - row_count = 1; - row_height = new int[1]; - row_height[0] = -1; - - pattern[0] = ""; - for (i = 0; i < 65; i++) { - c = 'T'; - if (bar_map[i]) { - c = 'D'; - } - if (bar_map[i + 65]) { - c = 'A'; - } - if (bar_map[i] && bar_map[i + 65]) { - c = 'F'; - } - pattern[0] += c; - } - - infoLine("Encoding: " + pattern[0]); - } - - private static int USPS_MSB_Math_CRC11GenerateFrameCheckSequence(int[] bytes) { - - int generatorPolynomial = 0x0F35; - int frameCheckSequence = 0x07FF; - int data; - int byteIndex, bit; - int byteArrayPtr = 0; - - /* Do most significant byte skipping the 2 most significant bits */ - data = bytes[byteArrayPtr] << 5; - byteArrayPtr++; - for (bit = 2; bit < 8; bit++) { - if (((frameCheckSequence ^ data) & 0x400) != 0) - frameCheckSequence = (frameCheckSequence << 1) ^ generatorPolynomial; - else - frameCheckSequence = (frameCheckSequence << 1); - frameCheckSequence &= 0x7FF; - data <<= 1; - } - - /* Do rest of the bytes */ - for (byteIndex = 1; byteIndex < 13; byteIndex++) { - data = bytes[byteArrayPtr] << 3; - byteArrayPtr++; - for (bit = 0; bit < 8; bit++) { - if (((frameCheckSequence ^ data) & 0x0400) != 0) { - frameCheckSequence = (frameCheckSequence << 1) ^ generatorPolynomial; - } else { - frameCheckSequence = (frameCheckSequence << 1); - } - frameCheckSequence &= 0x7FF; - data <<= 1; - } - } - - return frameCheckSequence; - } - - /** - *

Formats the barcode content into the correct human-readable format, per section 2.4.3 of the spec: - * - *

The human-readable information, when required, shall consist of the 20-digit tracking code and the 5-, 9-, or 11-digit - * routing code, if present. The fields of the tracking code, as defined in 2.1.3, shall be separated with a space added - * between data fields. When the barcode contains a routing code, the 5-digit ZIP Code, the 4-digit add-on, and the - * remaining 2 digits shall be separated with a space added between data fields. - * - *

Appendix F contains a good overview of the different IMb constructs / formats. - * - * @param content the content to be formatted - * @return the formatted content - */ - protected static String formatHumanReadableText(String content) { - StringBuilder hrt = new StringBuilder(50); - boolean mid9 = false; // 9-digit mailer ID instead of 6-digit mailer ID - boolean tracing = true; // STID indicates Origin IMb Tracing Services (050, 052) - boolean pimb = true; // barcode identifier (BI) is 94, indicating pIMb - boolean mpe5 = false; // if MPE = 5, it's a CFS/RFS variant of pIMb - int i = 0; - for (char c : content.toCharArray()) { - if (c < '0' || c > '9') { - continue; - } - if (i == 5 && c == '9') { - mid9 = true; - } - if ((i == 2 && c != '0') || (i == 3 && c != '5') || (i == 4 && c != '0' && c != '2')) { - tracing = false; - } - if ((i == 0 && c != '9') || (i == 1 && c != '4')) { - pimb = false; - } - if (i == 5 && c == '5') { - mpe5 = true; - } - if ((i == 2) // BI -> STID - || (i == 5) // STID -> ... - || (i == 6 && pimb) - || (i == 10 && pimb) - || (i == 13 && pimb && !mpe5) - || (i == 15 && pimb && !mpe5) - || (i == 11 && !mid9 && !tracing && !pimb) - || (i == 14 && mid9 && !tracing && !pimb) - || (i == 20) // ... -> zip-5 - || (i == 25) // zip-5 -> zip-4 - || (i == 29)) { // zip-4 -> zip-2 - hrt.append(' '); - } - hrt.append(c); - i++; - } - return hrt.toString().trim(); - } - - @Override - protected void plotSymbol() { - int xBlock, shortHeight, longHeight; - double x, y, w, h, dx; - - resetPlotElements(); - - int baseY; - if (humanReadableLocation == TOP) { - baseY = getTheoreticalHumanReadableHeight(); - } else { - baseY = 0; - } - - x = 0; - w = moduleWidth; - y = 0; - h = 0; - dx = (1 + moduleWidthRatio) * w; - shortHeight = (int) (shortHeightPercentage * default_height); - longHeight = (int) (longHeightPercentage * default_height); - for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) { - - switch (pattern[0].charAt(xBlock)) { - case 'A': - y = baseY; - h = longHeight; - break; - case 'D': - y = baseY + default_height - longHeight; - h = longHeight; - break; - case 'F': - y = baseY; - h = default_height; - break; - case 'T': - y = baseY + default_height - longHeight; - h = shortHeight; - break; - } - - Rectangle rect = new Rectangle(x, y, w, h); - rectangles.add(rect); - - x += dx; - } - - symbol_width = (int) Math.ceil(((pattern[0].length() - 1) * dx) + w); // final bar doesn't need extra whitespace - symbol_height = default_height; - - if (humanReadableLocation != NONE && !readable.isEmpty()) { - double baseline; - if (humanReadableLocation == TOP) { - baseline = fontSize; - } else { - baseline = symbol_height + fontSize; - } - texts.add(new TextBox(0, baseline, symbol_width, readable, humanReadableAlignment)); - } - } -} +/* + * Copyright 2014 Robin Stuart + * + * 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. + */ + +package uk.org.okapibarcode.backend; + +import static uk.org.okapibarcode.backend.HumanReadableLocation.NONE; +import static uk.org.okapibarcode.backend.HumanReadableLocation.TOP; + +import java.math.BigInteger; + +import uk.org.okapibarcode.graphics.Rectangle; +import uk.org.okapibarcode.graphics.TextAlignment; +import uk.org.okapibarcode.graphics.TextBox; + +/** + *

Implements USPS OneCode (also known as Intelligent Mail Barcode) according to USPS-B-3200F. + * + *

OneCode is a fixed length (65-bar) symbol which combines routing and customer information + * in a single symbol. Input data consists of a 20 digit tracking code, followed by a dash (-), + * followed by a delivery point ZIP code which can be 0, 5, 9 or 11 digits in length. + * + * @author Robin Stuart + * @see USPS OneCode Specification + */ +public class UspsOneCode extends Symbol { + + /* The following lookup tables were generated using the code in Appendix C */ + + /** Appendix D Table 1 - 5 of 13 characters */ + private static final int[] APPX_D_I = { + 0x001F, 0x1F00, 0x002F, 0x1E80, 0x0037, 0x1D80, 0x003B, 0x1B80, 0x003D, 0x1780, + 0x003E, 0x0F80, 0x004F, 0x1E40, 0x0057, 0x1D40, 0x005B, 0x1B40, 0x005D, 0x1740, + 0x005E, 0x0F40, 0x0067, 0x1CC0, 0x006B, 0x1AC0, 0x006D, 0x16C0, 0x006E, 0x0EC0, + 0x0073, 0x19C0, 0x0075, 0x15C0, 0x0076, 0x0DC0, 0x0079, 0x13C0, 0x007A, 0x0BC0, + 0x007C, 0x07C0, 0x008F, 0x1E20, 0x0097, 0x1D20, 0x009B, 0x1B20, 0x009D, 0x1720, + 0x009E, 0x0F20, 0x00A7, 0x1CA0, 0x00AB, 0x1AA0, 0x00AD, 0x16A0, 0x00AE, 0x0EA0, + 0x00B3, 0x19A0, 0x00B5, 0x15A0, 0x00B6, 0x0DA0, 0x00B9, 0x13A0, 0x00BA, 0x0BA0, + 0x00BC, 0x07A0, 0x00C7, 0x1C60, 0x00CB, 0x1A60, 0x00CD, 0x1660, 0x00CE, 0x0E60, + 0x00D3, 0x1960, 0x00D5, 0x1560, 0x00D6, 0x0D60, 0x00D9, 0x1360, 0x00DA, 0x0B60, + 0x00DC, 0x0760, 0x00E3, 0x18E0, 0x00E5, 0x14E0, 0x00E6, 0x0CE0, 0x00E9, 0x12E0, + 0x00EA, 0x0AE0, 0x00EC, 0x06E0, 0x00F1, 0x11E0, 0x00F2, 0x09E0, 0x00F4, 0x05E0, + 0x00F8, 0x03E0, 0x010F, 0x1E10, 0x0117, 0x1D10, 0x011B, 0x1B10, 0x011D, 0x1710, + 0x011E, 0x0F10, 0x0127, 0x1C90, 0x012B, 0x1A90, 0x012D, 0x1690, 0x012E, 0x0E90, + 0x0133, 0x1990, 0x0135, 0x1590, 0x0136, 0x0D90, 0x0139, 0x1390, 0x013A, 0x0B90, + 0x013C, 0x0790, 0x0147, 0x1C50, 0x014B, 0x1A50, 0x014D, 0x1650, 0x014E, 0x0E50, + 0x0153, 0x1950, 0x0155, 0x1550, 0x0156, 0x0D50, 0x0159, 0x1350, 0x015A, 0x0B50, + 0x015C, 0x0750, 0x0163, 0x18D0, 0x0165, 0x14D0, 0x0166, 0x0CD0, 0x0169, 0x12D0, + 0x016A, 0x0AD0, 0x016C, 0x06D0, 0x0171, 0x11D0, 0x0172, 0x09D0, 0x0174, 0x05D0, + 0x0178, 0x03D0, 0x0187, 0x1C30, 0x018B, 0x1A30, 0x018D, 0x1630, 0x018E, 0x0E30, + 0x0193, 0x1930, 0x0195, 0x1530, 0x0196, 0x0D30, 0x0199, 0x1330, 0x019A, 0x0B30, + 0x019C, 0x0730, 0x01A3, 0x18B0, 0x01A5, 0x14B0, 0x01A6, 0x0CB0, 0x01A9, 0x12B0, + 0x01AA, 0x0AB0, 0x01AC, 0x06B0, 0x01B1, 0x11B0, 0x01B2, 0x09B0, 0x01B4, 0x05B0, + 0x01B8, 0x03B0, 0x01C3, 0x1870, 0x01C5, 0x1470, 0x01C6, 0x0C70, 0x01C9, 0x1270, + 0x01CA, 0x0A70, 0x01CC, 0x0670, 0x01D1, 0x1170, 0x01D2, 0x0970, 0x01D4, 0x0570, + 0x01D8, 0x0370, 0x01E1, 0x10F0, 0x01E2, 0x08F0, 0x01E4, 0x04F0, 0x01E8, 0x02F0, + 0x020F, 0x1E08, 0x0217, 0x1D08, 0x021B, 0x1B08, 0x021D, 0x1708, 0x021E, 0x0F08, + 0x0227, 0x1C88, 0x022B, 0x1A88, 0x022D, 0x1688, 0x022E, 0x0E88, 0x0233, 0x1988, + 0x0235, 0x1588, 0x0236, 0x0D88, 0x0239, 0x1388, 0x023A, 0x0B88, 0x023C, 0x0788, + 0x0247, 0x1C48, 0x024B, 0x1A48, 0x024D, 0x1648, 0x024E, 0x0E48, 0x0253, 0x1948, + 0x0255, 0x1548, 0x0256, 0x0D48, 0x0259, 0x1348, 0x025A, 0x0B48, 0x025C, 0x0748, + 0x0263, 0x18C8, 0x0265, 0x14C8, 0x0266, 0x0CC8, 0x0269, 0x12C8, 0x026A, 0x0AC8, + 0x026C, 0x06C8, 0x0271, 0x11C8, 0x0272, 0x09C8, 0x0274, 0x05C8, 0x0278, 0x03C8, + 0x0287, 0x1C28, 0x028B, 0x1A28, 0x028D, 0x1628, 0x028E, 0x0E28, 0x0293, 0x1928, + 0x0295, 0x1528, 0x0296, 0x0D28, 0x0299, 0x1328, 0x029A, 0x0B28, 0x029C, 0x0728, + 0x02A3, 0x18A8, 0x02A5, 0x14A8, 0x02A6, 0x0CA8, 0x02A9, 0x12A8, 0x02AA, 0x0AA8, + 0x02AC, 0x06A8, 0x02B1, 0x11A8, 0x02B2, 0x09A8, 0x02B4, 0x05A8, 0x02B8, 0x03A8, + 0x02C3, 0x1868, 0x02C5, 0x1468, 0x02C6, 0x0C68, 0x02C9, 0x1268, 0x02CA, 0x0A68, + 0x02CC, 0x0668, 0x02D1, 0x1168, 0x02D2, 0x0968, 0x02D4, 0x0568, 0x02D8, 0x0368, + 0x02E1, 0x10E8, 0x02E2, 0x08E8, 0x02E4, 0x04E8, 0x0307, 0x1C18, 0x030B, 0x1A18, + 0x030D, 0x1618, 0x030E, 0x0E18, 0x0313, 0x1918, 0x0315, 0x1518, 0x0316, 0x0D18, + 0x0319, 0x1318, 0x031A, 0x0B18, 0x031C, 0x0718, 0x0323, 0x1898, 0x0325, 0x1498, + 0x0326, 0x0C98, 0x0329, 0x1298, 0x032A, 0x0A98, 0x032C, 0x0698, 0x0331, 0x1198, + 0x0332, 0x0998, 0x0334, 0x0598, 0x0338, 0x0398, 0x0343, 0x1858, 0x0345, 0x1458, + 0x0346, 0x0C58, 0x0349, 0x1258, 0x034A, 0x0A58, 0x034C, 0x0658, 0x0351, 0x1158, + 0x0352, 0x0958, 0x0354, 0x0558, 0x0361, 0x10D8, 0x0362, 0x08D8, 0x0364, 0x04D8, + 0x0383, 0x1838, 0x0385, 0x1438, 0x0386, 0x0C38, 0x0389, 0x1238, 0x038A, 0x0A38, + 0x038C, 0x0638, 0x0391, 0x1138, 0x0392, 0x0938, 0x0394, 0x0538, 0x03A1, 0x10B8, + 0x03A2, 0x08B8, 0x03A4, 0x04B8, 0x03C1, 0x1078, 0x03C2, 0x0878, 0x03C4, 0x0478, + 0x040F, 0x1E04, 0x0417, 0x1D04, 0x041B, 0x1B04, 0x041D, 0x1704, 0x041E, 0x0F04, + 0x0427, 0x1C84, 0x042B, 0x1A84, 0x042D, 0x1684, 0x042E, 0x0E84, 0x0433, 0x1984, + 0x0435, 0x1584, 0x0436, 0x0D84, 0x0439, 0x1384, 0x043A, 0x0B84, 0x043C, 0x0784, + 0x0447, 0x1C44, 0x044B, 0x1A44, 0x044D, 0x1644, 0x044E, 0x0E44, 0x0453, 0x1944, + 0x0455, 0x1544, 0x0456, 0x0D44, 0x0459, 0x1344, 0x045A, 0x0B44, 0x045C, 0x0744, + 0x0463, 0x18C4, 0x0465, 0x14C4, 0x0466, 0x0CC4, 0x0469, 0x12C4, 0x046A, 0x0AC4, + 0x046C, 0x06C4, 0x0471, 0x11C4, 0x0472, 0x09C4, 0x0474, 0x05C4, 0x0487, 0x1C24, + 0x048B, 0x1A24, 0x048D, 0x1624, 0x048E, 0x0E24, 0x0493, 0x1924, 0x0495, 0x1524, + 0x0496, 0x0D24, 0x0499, 0x1324, 0x049A, 0x0B24, 0x049C, 0x0724, 0x04A3, 0x18A4, + 0x04A5, 0x14A4, 0x04A6, 0x0CA4, 0x04A9, 0x12A4, 0x04AA, 0x0AA4, 0x04AC, 0x06A4, + 0x04B1, 0x11A4, 0x04B2, 0x09A4, 0x04B4, 0x05A4, 0x04C3, 0x1864, 0x04C5, 0x1464, + 0x04C6, 0x0C64, 0x04C9, 0x1264, 0x04CA, 0x0A64, 0x04CC, 0x0664, 0x04D1, 0x1164, + 0x04D2, 0x0964, 0x04D4, 0x0564, 0x04E1, 0x10E4, 0x04E2, 0x08E4, 0x0507, 0x1C14, + 0x050B, 0x1A14, 0x050D, 0x1614, 0x050E, 0x0E14, 0x0513, 0x1914, 0x0515, 0x1514, + 0x0516, 0x0D14, 0x0519, 0x1314, 0x051A, 0x0B14, 0x051C, 0x0714, 0x0523, 0x1894, + 0x0525, 0x1494, 0x0526, 0x0C94, 0x0529, 0x1294, 0x052A, 0x0A94, 0x052C, 0x0694, + 0x0531, 0x1194, 0x0532, 0x0994, 0x0534, 0x0594, 0x0543, 0x1854, 0x0545, 0x1454, + 0x0546, 0x0C54, 0x0549, 0x1254, 0x054A, 0x0A54, 0x054C, 0x0654, 0x0551, 0x1154, + 0x0552, 0x0954, 0x0561, 0x10D4, 0x0562, 0x08D4, 0x0583, 0x1834, 0x0585, 0x1434, + 0x0586, 0x0C34, 0x0589, 0x1234, 0x058A, 0x0A34, 0x058C, 0x0634, 0x0591, 0x1134, + 0x0592, 0x0934, 0x05A1, 0x10B4, 0x05A2, 0x08B4, 0x05C1, 0x1074, 0x05C2, 0x0874, + 0x0607, 0x1C0C, 0x060B, 0x1A0C, 0x060D, 0x160C, 0x060E, 0x0E0C, 0x0613, 0x190C, + 0x0615, 0x150C, 0x0616, 0x0D0C, 0x0619, 0x130C, 0x061A, 0x0B0C, 0x061C, 0x070C, + 0x0623, 0x188C, 0x0625, 0x148C, 0x0626, 0x0C8C, 0x0629, 0x128C, 0x062A, 0x0A8C, + 0x062C, 0x068C, 0x0631, 0x118C, 0x0632, 0x098C, 0x0643, 0x184C, 0x0645, 0x144C, + 0x0646, 0x0C4C, 0x0649, 0x124C, 0x064A, 0x0A4C, 0x0651, 0x114C, 0x0652, 0x094C, + 0x0661, 0x10CC, 0x0662, 0x08CC, 0x0683, 0x182C, 0x0685, 0x142C, 0x0686, 0x0C2C, + 0x0689, 0x122C, 0x068A, 0x0A2C, 0x0691, 0x112C, 0x0692, 0x092C, 0x06A1, 0x10AC, + 0x06A2, 0x08AC, 0x06C1, 0x106C, 0x06C2, 0x086C, 0x0703, 0x181C, 0x0705, 0x141C, + 0x0706, 0x0C1C, 0x0709, 0x121C, 0x070A, 0x0A1C, 0x0711, 0x111C, 0x0712, 0x091C, + 0x0721, 0x109C, 0x0722, 0x089C, 0x0741, 0x105C, 0x0742, 0x085C, 0x0781, 0x103C, + 0x0782, 0x083C, 0x080F, 0x1E02, 0x0817, 0x1D02, 0x081B, 0x1B02, 0x081D, 0x1702, + 0x081E, 0x0F02, 0x0827, 0x1C82, 0x082B, 0x1A82, 0x082D, 0x1682, 0x082E, 0x0E82, + 0x0833, 0x1982, 0x0835, 0x1582, 0x0836, 0x0D82, 0x0839, 0x1382, 0x083A, 0x0B82, + 0x0847, 0x1C42, 0x084B, 0x1A42, 0x084D, 0x1642, 0x084E, 0x0E42, 0x0853, 0x1942, + 0x0855, 0x1542, 0x0856, 0x0D42, 0x0859, 0x1342, 0x085A, 0x0B42, 0x0863, 0x18C2, + 0x0865, 0x14C2, 0x0866, 0x0CC2, 0x0869, 0x12C2, 0x086A, 0x0AC2, 0x0871, 0x11C2, + 0x0872, 0x09C2, 0x0887, 0x1C22, 0x088B, 0x1A22, 0x088D, 0x1622, 0x088E, 0x0E22, + 0x0893, 0x1922, 0x0895, 0x1522, 0x0896, 0x0D22, 0x0899, 0x1322, 0x089A, 0x0B22, + 0x08A3, 0x18A2, 0x08A5, 0x14A2, 0x08A6, 0x0CA2, 0x08A9, 0x12A2, 0x08AA, 0x0AA2, + 0x08B1, 0x11A2, 0x08B2, 0x09A2, 0x08C3, 0x1862, 0x08C5, 0x1462, 0x08C6, 0x0C62, + 0x08C9, 0x1262, 0x08CA, 0x0A62, 0x08D1, 0x1162, 0x08D2, 0x0962, 0x08E1, 0x10E2, + 0x0907, 0x1C12, 0x090B, 0x1A12, 0x090D, 0x1612, 0x090E, 0x0E12, 0x0913, 0x1912, + 0x0915, 0x1512, 0x0916, 0x0D12, 0x0919, 0x1312, 0x091A, 0x0B12, 0x0923, 0x1892, + 0x0925, 0x1492, 0x0926, 0x0C92, 0x0929, 0x1292, 0x092A, 0x0A92, 0x0931, 0x1192, + 0x0932, 0x0992, 0x0943, 0x1852, 0x0945, 0x1452, 0x0946, 0x0C52, 0x0949, 0x1252, + 0x094A, 0x0A52, 0x0951, 0x1152, 0x0961, 0x10D2, 0x0983, 0x1832, 0x0985, 0x1432, + 0x0986, 0x0C32, 0x0989, 0x1232, 0x098A, 0x0A32, 0x0991, 0x1132, 0x09A1, 0x10B2, + 0x09C1, 0x1072, 0x0A07, 0x1C0A, 0x0A0B, 0x1A0A, 0x0A0D, 0x160A, 0x0A0E, 0x0E0A, + 0x0A13, 0x190A, 0x0A15, 0x150A, 0x0A16, 0x0D0A, 0x0A19, 0x130A, 0x0A1A, 0x0B0A, + 0x0A23, 0x188A, 0x0A25, 0x148A, 0x0A26, 0x0C8A, 0x0A29, 0x128A, 0x0A2A, 0x0A8A, + 0x0A31, 0x118A, 0x0A43, 0x184A, 0x0A45, 0x144A, 0x0A46, 0x0C4A, 0x0A49, 0x124A, + 0x0A51, 0x114A, 0x0A61, 0x10CA, 0x0A83, 0x182A, 0x0A85, 0x142A, 0x0A86, 0x0C2A, + 0x0A89, 0x122A, 0x0A91, 0x112A, 0x0AA1, 0x10AA, 0x0AC1, 0x106A, 0x0B03, 0x181A, + 0x0B05, 0x141A, 0x0B06, 0x0C1A, 0x0B09, 0x121A, 0x0B11, 0x111A, 0x0B21, 0x109A, + 0x0B41, 0x105A, 0x0B81, 0x103A, 0x0C07, 0x1C06, 0x0C0B, 0x1A06, 0x0C0D, 0x1606, + 0x0C0E, 0x0E06, 0x0C13, 0x1906, 0x0C15, 0x1506, 0x0C16, 0x0D06, 0x0C19, 0x1306, + 0x0C23, 0x1886, 0x0C25, 0x1486, 0x0C26, 0x0C86, 0x0C29, 0x1286, 0x0C31, 0x1186, + 0x0C43, 0x1846, 0x0C45, 0x1446, 0x0C49, 0x1246, 0x0C51, 0x1146, 0x0C61, 0x10C6, + 0x0C83, 0x1826, 0x0C85, 0x1426, 0x0C89, 0x1226, 0x0C91, 0x1126, 0x0CA1, 0x10A6, + 0x0CC1, 0x1066, 0x0D03, 0x1816, 0x0D05, 0x1416, 0x0D09, 0x1216, 0x0D11, 0x1116, + 0x0D21, 0x1096, 0x0D41, 0x1056, 0x0D81, 0x1036, 0x0E03, 0x180E, 0x0E05, 0x140E, + 0x0E09, 0x120E, 0x0E11, 0x110E, 0x0E21, 0x108E, 0x0E41, 0x104E, 0x0E81, 0x102E, + 0x0F01, 0x101E, 0x100F, 0x1E01, 0x1017, 0x1D01, 0x101B, 0x1B01, 0x101D, 0x1701, + 0x1027, 0x1C81, 0x102B, 0x1A81, 0x102D, 0x1681, 0x1033, 0x1981, 0x1035, 0x1581, + 0x1039, 0x1381, 0x1047, 0x1C41, 0x104B, 0x1A41, 0x104D, 0x1641, 0x1053, 0x1941, + 0x1055, 0x1541, 0x1059, 0x1341, 0x1063, 0x18C1, 0x1065, 0x14C1, 0x1069, 0x12C1, + 0x1071, 0x11C1, 0x1087, 0x1C21, 0x108B, 0x1A21, 0x108D, 0x1621, 0x1093, 0x1921, + 0x1095, 0x1521, 0x1099, 0x1321, 0x10A3, 0x18A1, 0x10A5, 0x14A1, 0x10A9, 0x12A1, + 0x10B1, 0x11A1, 0x10C3, 0x1861, 0x10C5, 0x1461, 0x10C9, 0x1261, 0x10D1, 0x1161, + 0x1107, 0x1C11, 0x110B, 0x1A11, 0x110D, 0x1611, 0x1113, 0x1911, 0x1115, 0x1511, + 0x1119, 0x1311, 0x1123, 0x1891, 0x1125, 0x1491, 0x1129, 0x1291, 0x1131, 0x1191, + 0x1143, 0x1851, 0x1145, 0x1451, 0x1149, 0x1251, 0x1183, 0x1831, 0x1185, 0x1431, + 0x1189, 0x1231, 0x1207, 0x1C09, 0x120B, 0x1A09, 0x120D, 0x1609, 0x1213, 0x1909, + 0x1215, 0x1509, 0x1219, 0x1309, 0x1223, 0x1889, 0x1225, 0x1489, 0x1229, 0x1289, + 0x1243, 0x1849, 0x1245, 0x1449, 0x1283, 0x1829, 0x1285, 0x1429, 0x1303, 0x1819, + 0x1305, 0x1419, 0x1407, 0x1C05, 0x140B, 0x1A05, 0x140D, 0x1605, 0x1413, 0x1905, + 0x1415, 0x1505, 0x1423, 0x1885, 0x1425, 0x1485, 0x1443, 0x1845, 0x1483, 0x1825, + 0x1503, 0x1815, 0x1603, 0x180D, 0x1807, 0x1C03, 0x180B, 0x1A03, 0x1813, 0x1903, + 0x1823, 0x1883, 0x1843, 0x1445, 0x1249, 0x1151, 0x10E1, 0x0C46, 0x0A4A, 0x0952, + 0x08E2, 0x064C, 0x0554, 0x04E4, 0x0358, 0x02E8, 0x01F0 + }; + + /** Appendix D Table II - 2 of 13 characters */ + private static final int[] APPX_D_II = { + 0x0003, 0x1800, 0x0005, 0x1400, 0x0006, 0x0C00, 0x0009, 0x1200, 0x000A, 0x0A00, + 0x000C, 0x0600, 0x0011, 0x1100, 0x0012, 0x0900, 0x0014, 0x0500, 0x0018, 0x0300, + 0x0021, 0x1080, 0x0022, 0x0880, 0x0024, 0x0480, 0x0028, 0x0280, 0x0030, 0x0180, + 0x0041, 0x1040, 0x0042, 0x0840, 0x0044, 0x0440, 0x0048, 0x0240, 0x0050, 0x0140, + 0x0060, 0x00C0, 0x0081, 0x1020, 0x0082, 0x0820, 0x0084, 0x0420, 0x0088, 0x0220, + 0x0090, 0x0120, 0x0101, 0x1010, 0x0102, 0x0810, 0x0104, 0x0410, 0x0108, 0x0210, + 0x0201, 0x1008, 0x0202, 0x0808, 0x0204, 0x0408, 0x0401, 0x1004, 0x0402, 0x0804, + 0x0801, 0x1002, 0x1001, 0x0802, 0x0404, 0x0208, 0x0110, 0x00A0 + }; + + /** Appendix D Table IV - Bar-to-Character Mapping (reverse lookup) */ + private static final int[] APPX_D_IV = { + 67, 6, 78, 16, 86, 95, 34, 40, 45, 113, 117, 121, 62, 87, 18, 104, 41, 76, 57, 119, 115, 72, 97, + 2, 127, 26, 105, 35, 122, 52, 114, 7, 24, 82, 68, 63, 94, 44, 77, 112, 70, 100, 39, 30, 107, + 15, 125, 85, 10, 65, 54, 88, 20, 106, 46, 66, 8, 116, 29, 61, 99, 80, 90, 37, 123, 51, 25, 84, + 129, 56, 4, 109, 96, 28, 36, 47, 11, 71, 33, 102, 21, 9, 17, 49, 124, 79, 64, 91, 42, 69, 53, + 60, 14, 1, 27, 103, 126, 75, 89, 50, 120, 19, 32, 110, 92, 111, 130, 59, 31, 12, 81, 43, 55, + 5, 74, 22, 101, 128, 58, 118, 48, 108, 38, 98, 93, 23, 83, 13, 73, 3 + }; + + private double moduleWidthRatio; + private double shortHeightPercentage; + private double longHeightPercentage; + + public UspsOneCode() { + this.default_height = 8; + this.humanReadableLocation = HumanReadableLocation.NONE; + this.humanReadableAlignment = TextAlignment.LEFT; // spec section 2.4.2 + this.moduleWidthRatio = 1.43; + this.shortHeightPercentage = 0.25; + this.longHeightPercentage = 0.625; + } + + /** + * Sets the ratio of space width to bar width. The default value is {@code 1.43} (spaces are 43% wider than bars). + * + * @param moduleWidthRatio the ratio of space width to bar width + */ + public void setModuleWidthRatio(double moduleWidthRatio) { + this.moduleWidthRatio = moduleWidthRatio; + } + + /** + * Returns the ratio of space width to bar width. + * + * @return the ratio of space width to bar width + */ + public double getModuleWidthRatio() { + return moduleWidthRatio; + } + + /** + * Sets the percentage of the full symbol height used for short bars (0 - 1). The default value is {@code 0.25} (25% of full symbol height). + * + * @param shortHeightPercentage the percentage of the full symbol height used for short bars + */ + public void setShortHeightPercentage(double shortHeightPercentage) { + if (shortHeightPercentage < 0 || shortHeightPercentage > 1) { + throw new IllegalArgumentException("Height percentage must be between 0 and 1."); + } + this.shortHeightPercentage = shortHeightPercentage; + } + + /** + * Returns the percentage of the full symbol height used for short bars. + * + * @return the percentage of the full symbol height used for short bars + */ + public double getShortHeightPercentage() { + return shortHeightPercentage; + } + + /** + * Sets the percentage of the full symbol height used for long bars (0 - 1). The default value is {@code 0.625} (62.5% of full symbol height). + * + * @param longHeightPercentage the percentage of the full symbol height used for long bars + */ + public void setLongHeightPercentage(double longHeightPercentage) { + if (longHeightPercentage < 0 || longHeightPercentage > 1) { + throw new IllegalArgumentException("Height percentage must be between 0 and 1."); + } + this.longHeightPercentage = longHeightPercentage; + } + + /** + * Returns the percentage of the full symbol height used for long bars. + * + * @return the percentage of the full symbol height used for long bars + */ + public double getLongHeightPercentage() { + return longHeightPercentage; + } + + @Override + protected void encode() { + String zip = ""; + String zip_adder; + String tracker = ""; + int i, j; + int length = content.length(); + BigInteger accum; + BigInteger x_reg; + BigInteger mask; + int usps_crc; + int[] codeword = new int[10]; + int[] characters = new int[10]; + boolean[] bar_map = new boolean[130]; + char c; + + if (!content.matches("[0-9\u002D]*")) { + throw OkapiInputException.invalidCharactersInInput(); + } + + if (length > 32) { + throw OkapiInputException.inputTooLong(); + } + + /* separate the tracking code from the routing code */ + j = 0; + for (i = 0; i < length; i++) { + if (content.charAt(i) == '-') { + j = 1; + } else { + if (j == 0) { + /* reading tracker */ + tracker += content.charAt(i); + } else { + /* reading zip code */ + zip += content.charAt(i); + } + } + } + + if (tracker.length() != 20) { + throw new OkapiInputException("Tracking code is not correct length"); + } + + if (zip.length() > 11) { + throw new OkapiInputException("ZIP code is not correct length"); + } + + /* *** Step 1 - Conversion of Data Fields into Binary Data *** */ + + /* Routing code first */ + if (zip.length() > 0) { + x_reg = new BigInteger(zip); + } else { + x_reg = new BigInteger("0"); + } + + /* add weight to routing code */ + if (zip.length() > 9) { + zip_adder = "1000100001"; + } else { + if (zip.length() > 5) { + zip_adder = "100001"; + } else { + if (zip.length() > 0) { + zip_adder = "1"; + } else { + zip_adder = "0"; + } + } + } + + accum = new BigInteger(zip_adder); + accum = accum.add(x_reg); + accum = accum.multiply(BigInteger.valueOf(10)); + accum = accum.add(BigInteger.valueOf(Character.getNumericValue(tracker.charAt(0)))); + accum = accum.multiply(BigInteger.valueOf(5)); + accum = accum.add(BigInteger.valueOf(Character.getNumericValue(tracker.charAt(1)))); + for (i = 2; i < tracker.length(); i++) { + accum = accum.multiply(BigInteger.valueOf(10)); + accum = accum.add(BigInteger.valueOf(Character.getNumericValue(tracker.charAt(i)))); + } + + /* *** Step 2 - Generation of 11-bit CRC on Binary Data *** */ + + int[] byte_array = new int[13]; + for (i = 0; i < byte_array.length; i++) { + mask = accum.shiftRight(96 - (8 * i)); + mask = mask.and(new BigInteger("255")); + byte_array[i] = mask.intValue(); + } + + usps_crc = USPS_MSB_Math_CRC11GenerateFrameCheckSequence(byte_array); + + /* *** Step 3 - Conversion from Binary Data to Codewords *** */ + + /* start with codeword J which is base 636 */ + x_reg = accum.mod(BigInteger.valueOf(636)); + codeword[9] = x_reg.intValue(); + accum = accum.subtract(x_reg); + accum = accum.divide(BigInteger.valueOf(636)); + + for (i = 8; i >= 0; i--) { + x_reg = accum.mod(BigInteger.valueOf(1365)); + codeword[i] = x_reg.intValue(); + accum = accum.subtract(x_reg); + accum = accum.divide(BigInteger.valueOf(1365)); + } + + for (i = 0; i < 9; i++) { + if (codeword[i] == 1365) { + codeword[i] = 0; + } + } + + /* *** Step 4 - Inserting Additional Information into Codewords *** */ + + codeword[9] = codeword[9] * 2; + + if (usps_crc >= 1024) { + codeword[0] += 659; + } + + info("Codewords: "); + for (i = 0; i < 10; i++) { + infoSpace(codeword[i]); + } + infoLine(); + + /* *** Step 5 - Conversion from Codewords to Characters *** */ + + for (i = 0; i < 10; i++) { + if (codeword[i] < 1287) { + characters[i] = APPX_D_I[codeword[i]]; + } else { + characters[i] = APPX_D_II[codeword[i] - 1287]; + } + } + + for (i = 0; i < 10; i++) { + if ((usps_crc & (1 << i)) != 0) { + characters[i] = 0x1FFF - characters[i]; + } + } + + /* *** Step 6 - Conversion from Characters to the Intelligent Mail Barcode *** */ + + for (i = 0; i < 10; i++) { + for (j = 0; j < 13; j++) { + if ((characters[i] & (1 << j)) == 0) { + bar_map[APPX_D_IV[(13 * i) + j] - 1] = false; + } else { + bar_map[APPX_D_IV[(13 * i) + j] - 1] = true; + } + } + } + + readable = formatHumanReadableText(content); + pattern = new String[1]; + row_count = 1; + row_height = new int[1]; + row_height[0] = -1; + + pattern[0] = ""; + for (i = 0; i < 65; i++) { + c = 'T'; + if (bar_map[i]) { + c = 'D'; + } + if (bar_map[i + 65]) { + c = 'A'; + } + if (bar_map[i] && bar_map[i + 65]) { + c = 'F'; + } + pattern[0] += c; + } + + infoLine("Encoding: " + pattern[0]); + } + + private static int USPS_MSB_Math_CRC11GenerateFrameCheckSequence(int[] bytes) { + + int generatorPolynomial = 0x0F35; + int frameCheckSequence = 0x07FF; + int data; + int byteIndex, bit; + int byteArrayPtr = 0; + + /* Do most significant byte skipping the 2 most significant bits */ + data = bytes[byteArrayPtr] << 5; + byteArrayPtr++; + for (bit = 2; bit < 8; bit++) { + if (((frameCheckSequence ^ data) & 0x400) != 0) + frameCheckSequence = (frameCheckSequence << 1) ^ generatorPolynomial; + else + frameCheckSequence = (frameCheckSequence << 1); + frameCheckSequence &= 0x7FF; + data <<= 1; + } + + /* Do rest of the bytes */ + for (byteIndex = 1; byteIndex < 13; byteIndex++) { + data = bytes[byteArrayPtr] << 3; + byteArrayPtr++; + for (bit = 0; bit < 8; bit++) { + if (((frameCheckSequence ^ data) & 0x0400) != 0) { + frameCheckSequence = (frameCheckSequence << 1) ^ generatorPolynomial; + } else { + frameCheckSequence = (frameCheckSequence << 1); + } + frameCheckSequence &= 0x7FF; + data <<= 1; + } + } + + return frameCheckSequence; + } + + /** + *

Formats the barcode content into the correct human-readable format, per section 2.4.3 of the spec: + * + *

The human-readable information, when required, shall consist of the 20-digit tracking code and the 5-, 9-, or 11-digit + * routing code, if present. The fields of the tracking code, as defined in 2.1.3, shall be separated with a space added + * between data fields. When the barcode contains a routing code, the 5-digit ZIP Code, the 4-digit add-on, and the + * remaining 2 digits shall be separated with a space added between data fields. + * + *

Appendix F contains a good overview of the different IMb constructs / formats. + * + * @param content the content to be formatted + * @return the formatted content + */ + protected static String formatHumanReadableText(String content) { + StringBuilder hrt = new StringBuilder(50); + boolean mid9 = false; // 9-digit mailer ID instead of 6-digit mailer ID + boolean tracing = true; // STID indicates Origin IMb Tracing Services (050, 052) + boolean pimb = true; // barcode identifier (BI) is 94, indicating pIMb + boolean mpe5 = false; // if MPE = 5, it's a CFS/RFS variant of pIMb + int i = 0; + for (char c : content.toCharArray()) { + if (c < '0' || c > '9') { + continue; + } + if (i == 5 && c == '9') { + mid9 = true; + } + if ((i == 2 && c != '0') || (i == 3 && c != '5') || (i == 4 && c != '0' && c != '2')) { + tracing = false; + } + if ((i == 0 && c != '9') || (i == 1 && c != '4')) { + pimb = false; + } + if (i == 5 && c == '5') { + mpe5 = true; + } + if ((i == 2) // BI -> STID + || (i == 5) // STID -> ... + || (i == 6 && pimb) + || (i == 10 && pimb) + || (i == 13 && pimb && !mpe5) + || (i == 15 && pimb && !mpe5) + || (i == 11 && !mid9 && !tracing && !pimb) + || (i == 14 && mid9 && !tracing && !pimb) + || (i == 20) // ... -> zip-5 + || (i == 25) // zip-5 -> zip-4 + || (i == 29)) { // zip-4 -> zip-2 + hrt.append(' '); + } + hrt.append(c); + i++; + } + return hrt.toString().trim(); + } + + @Override + protected void plotSymbol() { + int xBlock, shortHeight, longHeight; + double x, y, w, h, dx; + + resetPlotElements(); + + int baseY; + if (humanReadableLocation == TOP) { + baseY = getTheoreticalHumanReadableHeight(); + } else { + baseY = 0; + } + + x = 0; + w = moduleWidth; + y = 0; + h = 0; + dx = (1 + moduleWidthRatio) * w; + shortHeight = (int) (shortHeightPercentage * default_height); + longHeight = (int) (longHeightPercentage * default_height); + for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) { + + switch (pattern[0].charAt(xBlock)) { + case 'A': + y = baseY; + h = longHeight; + break; + case 'D': + y = baseY + default_height - longHeight; + h = longHeight; + break; + case 'F': + y = baseY; + h = default_height; + break; + case 'T': + y = baseY + default_height - longHeight; + h = shortHeight; + break; + } + + Rectangle rect = new Rectangle(x, y, w, h); + rectangles.add(rect); + + x += dx; + } + + symbol_width = (int) Math.ceil(((pattern[0].length() - 1) * dx) + w); // final bar doesn't need extra whitespace + symbol_height = default_height; + + if (humanReadableLocation != NONE && !readable.isEmpty()) { + double baseline; + if (humanReadableLocation == TOP) { + baseline = fontSize; + } else { + baseline = symbol_height + fontSize; + } + texts.add(new TextBox(0, baseline, symbol_width, readable, humanReadableAlignment)); + } + } +} diff --git a/src/main/java/uk/org/okapibarcode/backend/UspsPackage.java b/src/main/java/uk/org/okapibarcode/backend/UspsPackage.java index 719aef8d..87b1cfba 100644 --- a/src/main/java/uk/org/okapibarcode/backend/UspsPackage.java +++ b/src/main/java/uk/org/okapibarcode/backend/UspsPackage.java @@ -1,125 +1,125 @@ -/* - * Copyright 2015 Robin Stuart - * - * 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. - */ - -package uk.org.okapibarcode.backend; - -import uk.org.okapibarcode.graphics.Rectangle; -import uk.org.okapibarcode.graphics.TextBox; - -/** - *

Implements USPS Intelligent Mail Package Barcode (IMpb), a linear barcode based on GS1-128. - * Includes additional data checks. - * - * @author Robin Stuart - * @see IMpb Specification - */ -public class UspsPackage extends Symbol { - - @Override - protected void encode() { - - if (!content.matches("[0-9\\[\\]]+")) { - /* Input must be numeric only */ - throw OkapiInputException.invalidCharactersInInput(); - } - - if (content.length() % 2 != 0) { - /* Input must be even length */ - throw new OkapiInputException("Invalid IMpb data"); - } - - Code128 code128 = new Code128(); - code128.unsetCc(); - code128.setDataType(DataType.GS1); - code128.setContent(content); - - boolean fourTwenty = content.length() >= 5 && - content.charAt(0) == '[' && - content.charAt(1) == '4' && - content.charAt(2) == '2' && - content.charAt(3) == '0' && - content.charAt(4) == ']'; - - StringBuilder hrt = new StringBuilder(); - int start = (fourTwenty ? content.indexOf('[', 5) : 0); - if (start != -1) { - int digits = 0; - for (int i = start; i < content.length(); i++) { - char c = content.charAt(i); - if (c >= '0' && c <= '9') { - hrt.append(c); - digits++; - if (digits % 4 == 0) { - hrt.append(' '); - } - } - } - } - - encodeInfo = code128.encodeInfo; - readable = hrt.toString(); - pattern = new String[] { code128.pattern[0] }; - row_count = 1; - row_height = new int[] { -1 }; - } - - @Override - protected void plotSymbol() { - - resetPlotElements(); - - int offset = 20; - int yoffset = 15; - int x = 0; - int y = yoffset; - int h = default_height; - boolean black = true; - - for (int xBlock = 0; xBlock < pattern[0].length(); xBlock++) { - int w = pattern[0].charAt(xBlock) - '0'; - if (black) { - rectangles.add(new Rectangle(x + offset, y, w, h)); - symbol_width = x + w + (2 * offset); - } - black = !black; - x += w; - } - symbol_height = h + (2 * yoffset); - - // add boundary bars - Rectangle topBar = new Rectangle(0, 0, symbol_width, 2); - Rectangle bottomBar = new Rectangle(0, symbol_height - 2, symbol_width, 2); - rectangles.add(topBar); - rectangles.add(bottomBar); - - // add banner and human-readable text - texts.add(new TextBox(0, 12.0, symbol_width, "USPS TRACKING #", humanReadableAlignment)); - texts.add(new TextBox(0, symbol_height - 6.0, symbol_width, readable, humanReadableAlignment)); - } - - @Override - public int getHeight() { - // because of the custom layout logic in plotSymbol(), we store all height components (e.g. human - // readable text height, quiet zone, boundary bars) here; this is not the case for most symbologies - return symbol_height; - } - - /** {@inheritDoc} */ - @Override - protected int[] getCodewords() { - return getPatternAsCodewords(6); - } -} +/* + * Copyright 2015 Robin Stuart + * + * 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. + */ + +package uk.org.okapibarcode.backend; + +import uk.org.okapibarcode.graphics.Rectangle; +import uk.org.okapibarcode.graphics.TextBox; + +/** + *

Implements USPS Intelligent Mail Package Barcode (IMpb), a linear barcode based on GS1-128. + * Includes additional data checks. + * + * @author Robin Stuart + * @see IMpb Specification + */ +public class UspsPackage extends Symbol { + + @Override + protected void encode() { + + if (!content.matches("[0-9\\[\\]]+")) { + /* Input must be numeric only */ + throw OkapiInputException.invalidCharactersInInput(); + } + + if (content.length() % 2 != 0) { + /* Input must be even length */ + throw new OkapiInputException("Invalid IMpb data"); + } + + Code128 code128 = new Code128(); + code128.unsetCc(); + code128.setDataType(DataType.GS1); + code128.setContent(content); + + boolean fourTwenty = content.length() >= 5 && + content.charAt(0) == '[' && + content.charAt(1) == '4' && + content.charAt(2) == '2' && + content.charAt(3) == '0' && + content.charAt(4) == ']'; + + StringBuilder hrt = new StringBuilder(); + int start = (fourTwenty ? content.indexOf('[', 5) : 0); + if (start != -1) { + int digits = 0; + for (int i = start; i < content.length(); i++) { + char c = content.charAt(i); + if (c >= '0' && c <= '9') { + hrt.append(c); + digits++; + if (digits % 4 == 0) { + hrt.append(' '); + } + } + } + } + + encodeInfo = code128.encodeInfo; + readable = hrt.toString(); + pattern = new String[] { code128.pattern[0] }; + row_count = 1; + row_height = new int[] { -1 }; + } + + @Override + protected void plotSymbol() { + + resetPlotElements(); + + int offset = 20; + int yoffset = 15; + int x = 0; + int y = yoffset; + int h = default_height; + boolean black = true; + + for (int xBlock = 0; xBlock < pattern[0].length(); xBlock++) { + int w = pattern[0].charAt(xBlock) - '0'; + if (black) { + rectangles.add(new Rectangle(x + offset, y, w, h)); + symbol_width = x + w + (2 * offset); + } + black = !black; + x += w; + } + symbol_height = h + (2 * yoffset); + + // add boundary bars + Rectangle topBar = new Rectangle(0, 0, symbol_width, 2); + Rectangle bottomBar = new Rectangle(0, symbol_height - 2, symbol_width, 2); + rectangles.add(topBar); + rectangles.add(bottomBar); + + // add banner and human-readable text + texts.add(new TextBox(0, 12.0, symbol_width, "USPS TRACKING #", humanReadableAlignment)); + texts.add(new TextBox(0, symbol_height - 6.0, symbol_width, readable, humanReadableAlignment)); + } + + @Override + public int getHeight() { + // because of the custom layout logic in plotSymbol(), we store all height components (e.g. human + // readable text height, quiet zone, boundary bars) here; this is not the case for most symbologies + return symbol_height; + } + + /** {@inheritDoc} */ + @Override + protected int[] getCodewords() { + return getPatternAsCodewords(6); + } +} diff --git a/src/main/java/uk/org/okapibarcode/graphics/Hexagon.java b/src/main/java/uk/org/okapibarcode/graphics/Hexagon.java index 16182ac9..f28ade34 100644 --- a/src/main/java/uk/org/okapibarcode/graphics/Hexagon.java +++ b/src/main/java/uk/org/okapibarcode/graphics/Hexagon.java @@ -1,68 +1,68 @@ -/* - * Copyright 2014 Robin Stuart - * - * 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. - */ - -package uk.org.okapibarcode.graphics; - -import java.util.Objects; - -/** - * A hexagonal shape. - * - * @author Robin Stuart - */ -public final class Hexagon { - - private static final double INK_SPREAD = 1.25; - - private static final double[] OFFSET_X = { 0.0, 0.86, 0.86, 0.0, -0.86, -0.86 }; - private static final double[] OFFSET_Y = { 1.0, 0.5, -0.5, -1.0, -0.5, 0.5 }; - - public final double centreX; - public final double centreY; - public final double[] pointX = new double[6]; - public final double[] pointY = new double[6]; - - public Hexagon(double centreX, double centreY) { - this.centreX = centreX; - this.centreY = centreY; - for (int i = 0; i < 6; i++) { - pointX[i] = centreX + (OFFSET_X[i] * INK_SPREAD); - pointY[i] = centreY + (OFFSET_Y[i] * INK_SPREAD); - } - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object other) { - if (!(other instanceof Hexagon)) { - return false; - } - Hexagon h = (Hexagon) other; - return centreX == h.centreX && centreY == h.centreY; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - return Objects.hash(centreX, centreY); - } - - /** {@inheritDoc} */ - @Override - public String toString() { - return "Hexagon[centreX=" + centreX + ", centreY=" + centreY + "]"; - } -} +/* + * Copyright 2014 Robin Stuart + * + * 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. + */ + +package uk.org.okapibarcode.graphics; + +import java.util.Objects; + +/** + * A hexagonal shape. + * + * @author Robin Stuart + */ +public final class Hexagon { + + private static final double INK_SPREAD = 1.25; + + private static final double[] OFFSET_X = { 0.0, 0.86, 0.86, 0.0, -0.86, -0.86 }; + private static final double[] OFFSET_Y = { 1.0, 0.5, -0.5, -1.0, -0.5, 0.5 }; + + public final double centreX; + public final double centreY; + public final double[] pointX = new double[6]; + public final double[] pointY = new double[6]; + + public Hexagon(double centreX, double centreY) { + this.centreX = centreX; + this.centreY = centreY; + for (int i = 0; i < 6; i++) { + pointX[i] = centreX + (OFFSET_X[i] * INK_SPREAD); + pointY[i] = centreY + (OFFSET_Y[i] * INK_SPREAD); + } + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object other) { + if (!(other instanceof Hexagon)) { + return false; + } + Hexagon h = (Hexagon) other; + return centreX == h.centreX && centreY == h.centreY; + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return Objects.hash(centreX, centreY); + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return "Hexagon[centreX=" + centreX + ", centreY=" + centreY + "]"; + } +} diff --git a/src/main/java/uk/org/okapibarcode/graphics/TextBox.java b/src/main/java/uk/org/okapibarcode/graphics/TextBox.java index dfdbe4d1..900440af 100644 --- a/src/main/java/uk/org/okapibarcode/graphics/TextBox.java +++ b/src/main/java/uk/org/okapibarcode/graphics/TextBox.java @@ -1,82 +1,82 @@ -/* - * Copyright 2014-2018 Robin Stuart, Daniel Gredler - * - * 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. - */ - -package uk.org.okapibarcode.graphics; - -import java.util.Objects; - -/** - * A simple text item class. - * - * @author Robin Stuart - * @author Daniel Gredler - */ -public final class TextBox { - - /** The X position of the text's left boundary. */ - public final double x; - - /** The Y position of the text baseline. */ - public final double y; - - /** The width of the text box. */ - public final double width; - - /** The text value. */ - public final String text; - - /** The text alignment. */ - public final TextAlignment alignment; - - /** - * Creates a new instance. - * - * @param x the X position of the text's left boundary - * @param y the Y position of the text baseline - * @param width the width of the text box - * @param text the text value - * @param alignment the text alignment - */ - public TextBox(double x, double y, double width, String text, TextAlignment alignment) { - this.x = x; - this.y = y; - this.width = width; - this.text = Objects.requireNonNull(text); - this.alignment = Objects.requireNonNull(alignment); - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object other) { - if (!(other instanceof TextBox)) { - return false; - } - TextBox t = (TextBox) other; - return x == t.x && y == t.y && width == t.width && Objects.equals(text, t.text) && alignment == t.alignment; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - return Objects.hash(x, y, width, text, alignment); - } - - /** {@inheritDoc} */ - @Override - public String toString() { - return "TextBox[x=" + x + ", y=" + y + ", width=" + width + ", text=" + text + ", alignment=" + alignment + "]"; - } -} +/* + * Copyright 2014-2018 Robin Stuart, Daniel Gredler + * + * 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. + */ + +package uk.org.okapibarcode.graphics; + +import java.util.Objects; + +/** + * A simple text item class. + * + * @author Robin Stuart + * @author Daniel Gredler + */ +public final class TextBox { + + /** The X position of the text's left boundary. */ + public final double x; + + /** The Y position of the text baseline. */ + public final double y; + + /** The width of the text box. */ + public final double width; + + /** The text value. */ + public final String text; + + /** The text alignment. */ + public final TextAlignment alignment; + + /** + * Creates a new instance. + * + * @param x the X position of the text's left boundary + * @param y the Y position of the text baseline + * @param width the width of the text box + * @param text the text value + * @param alignment the text alignment + */ + public TextBox(double x, double y, double width, String text, TextAlignment alignment) { + this.x = x; + this.y = y; + this.width = width; + this.text = Objects.requireNonNull(text); + this.alignment = Objects.requireNonNull(alignment); + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object other) { + if (!(other instanceof TextBox)) { + return false; + } + TextBox t = (TextBox) other; + return x == t.x && y == t.y && width == t.width && Objects.equals(text, t.text) && alignment == t.alignment; + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return Objects.hash(x, y, width, text, alignment); + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return "TextBox[x=" + x + ", y=" + y + ", width=" + width + ", text=" + text + ", alignment=" + alignment + "]"; + } +} diff --git a/src/main/java/uk/org/okapibarcode/gui/OpenFile.java b/src/main/java/uk/org/okapibarcode/gui/OpenFile.java index e5c56f72..bf2a83ed 100644 --- a/src/main/java/uk/org/okapibarcode/gui/OpenFile.java +++ b/src/main/java/uk/org/okapibarcode/gui/OpenFile.java @@ -1,55 +1,55 @@ -/* - * Copyright 2014 Robin Stuart - * - * 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. - */ - -package uk.org.okapibarcode.gui; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; - -/** - * Open a file and read contents - * - * @author Robin Stuart - */ -public class OpenFile { - public static String ReadFile (File file, boolean isBatch) throws IOException { - String file_data = ""; - try (FileInputStream fis = new FileInputStream(file)) { - int count; - - if (file.isFile() && file.canRead()) { - if (isBatch) { - for (count = 0; count < file.length(); count++) { - file_data += (char) fis.read(); - } - } else { - // Limit size of input - if (file.length() < 3000) { - for (count = 0; count < file.length(); count++) { - file_data += (char) fis.read(); - } - } else { - System.out.println("Input file too big"); - } - } - } else { - System.out.println("I/O Error"); - } - } - return file_data; - } -} +/* + * Copyright 2014 Robin Stuart + * + * 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. + */ + +package uk.org.okapibarcode.gui; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; + +/** + * Open a file and read contents + * + * @author Robin Stuart + */ +public class OpenFile { + public static String ReadFile (File file, boolean isBatch) throws IOException { + String file_data = ""; + try (FileInputStream fis = new FileInputStream(file)) { + int count; + + if (file.isFile() && file.canRead()) { + if (isBatch) { + for (count = 0; count < file.length(); count++) { + file_data += (char) fis.read(); + } + } else { + // Limit size of input + if (file.length() < 3000) { + for (count = 0; count < file.length(); count++) { + file_data += (char) fis.read(); + } + } else { + System.out.println("Input file too big"); + } + } + } else { + System.out.println("I/O Error"); + } + } + return file_data; + } +} diff --git a/src/main/java/uk/org/okapibarcode/gui/SymbolType.java b/src/main/java/uk/org/okapibarcode/gui/SymbolType.java index 83333b5c..b59b2ee0 100644 --- a/src/main/java/uk/org/okapibarcode/gui/SymbolType.java +++ b/src/main/java/uk/org/okapibarcode/gui/SymbolType.java @@ -1,52 +1,53 @@ -/* - * Copyright 2014 Robin Stuart - * - * 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. - */ - -package uk.org.okapibarcode.gui; - -/** - * Simple container for symbology information - * - * @author Robin Stuart - */ -public class SymbolType { - public enum Encoding { CHANNEL_CODE, CODABAR, CODE_11, CODE25_MATRIX, - CODE25_IATA, CODE25_INDUSTRY, CODE25_INTERLEAVED, CODE25_DATALOGIC, ITF14, - CODE39, CODE39_EXTENDED, CODE93, DOD_LOGMARS, CODE_128, NVE18, EAN, - MSI_PLESSEY, TELEPEN, TELEPEN_NUMERIC, UPC_A, UPC_E, CODABLOCK_F, CODE16K, - CODE49, PDF417, PDF417_TRUNCATED, PDF417_MICRO, AZTEC, AZTEC_RUNE, - DATAMATRIX, CODE_ONE, GRIDMATRIX, MAXICODE, QR, QR_MICRO, - DB14, DB14_STACKED, DB14_STACKED_OMNIDIRECT, DB_LIMITED, - DB_EXPANDED, DB_EXPANDED_STACKED, AUSPOST, AUSPOST_REPLY, - AUSPOST_REROUTE, AUSPOST_REDIRECT, BRAZIL_CEPNET, DP_LEITCODE, - DP_IDENTCODE, KIX_CODE, JAPAN_POST, KOREA_POST, RM4SCC, USPS_IMAIL, - CODE39_HIBC, USPS_POSTNET, USPS_PLANET, CODE_32, AZTEC_HIBC, CODABLOCK_HIBC, - CODE_128_HIBC, DATAMATRIX_HIBC, PDF417_HIBC, PDF417_MICRO_HIBC, - QR_HIBC, PHARMA, PHARMA_TWOTRACK, PZN, USPS_IMPB - } - - String guiLabel; // GUI interface name - Encoding symbology; - - public SymbolType(String label, Encoding encoding) { - guiLabel = label; - symbology = encoding; - } - - @Override - public String toString(){ - return guiLabel; - } -} +/* + * Copyright 2014 Robin Stuart + * + * 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. + */ + +package uk.org.okapibarcode.gui; + +/** + * Simple container for symbology information + * + * @author Robin Stuart + */ +public class SymbolType { + + public enum Encoding { CHANNEL_CODE, CODABAR, CODE_11, CODE25_MATRIX, + CODE25_IATA, CODE25_INDUSTRY, CODE25_INTERLEAVED, CODE25_DATALOGIC, ITF14, + CODE39, CODE39_EXTENDED, CODE93, DOD_LOGMARS, CODE_128, NVE18, EAN, + MSI_PLESSEY, TELEPEN, TELEPEN_NUMERIC, UPC_A, UPC_E, CODABLOCK_F, CODE16K, + CODE49, PDF417, PDF417_TRUNCATED, PDF417_MICRO, AZTEC, AZTEC_RUNE, + DATAMATRIX, CODE_ONE, GRIDMATRIX, MAXICODE, QR, QR_MICRO, + DB14, DB14_STACKED, DB14_STACKED_OMNIDIRECT, DB_LIMITED, + DB_EXPANDED, DB_EXPANDED_STACKED, AUSPOST, AUSPOST_REPLY, + AUSPOST_REROUTE, AUSPOST_REDIRECT, BRAZIL_CEPNET, DP_LEITCODE, + DP_IDENTCODE, KIX_CODE, JAPAN_POST, KOREA_POST, RM4SCC, USPS_IMAIL, + CODE39_HIBC, USPS_POSTNET, USPS_PLANET, CODE_32, AZTEC_HIBC, CODABLOCK_HIBC, + CODE_128_HIBC, DATAMATRIX_HIBC, PDF417_HIBC, PDF417_MICRO_HIBC, + QR_HIBC, PHARMA, PHARMA_TWOTRACK, PZN, USPS_IMPB + } + + String guiLabel; // GUI interface name + Encoding symbology; + + public SymbolType(String label, Encoding encoding) { + guiLabel = label; + symbology = encoding; + } + + @Override + public String toString(){ + return guiLabel; + } +}