All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.pnuema.java.barcode.symbologies.Code128 Maven / Gradle / Ivy

There is a newer version: 2.7.2
Show newest version
package com.pnuema.java.barcode.symbologies;

import com.pnuema.java.barcode.BarcodeCommon;
import com.pnuema.java.barcode.IBarcode;

import java.util.ArrayList;
import java.util.List;

import static com.pnuema.java.barcode.utils.CharUtils.getChar;

/**
 * Code 128 encoding
 */
public class Code128 extends BarcodeCommon implements IBarcode {
    public enum TYPES { DYNAMIC, A, B, C };
    private List C128_Code = new ArrayList<>();
    private List formattedData = new ArrayList<>();
    private List encodedData = new ArrayList<>();
    private Entry startCharacter = null;
    private TYPES type = TYPES.DYNAMIC;

    private class CodeCharacter {
        public List rows = new ArrayList<>();
        public int col;
    }

    private static class Entry {
        private String id;
        private String A;
        private String B;
        private String C;
        private String encoding;

        Entry(String id, String a, String b, String c, String encoding) {
            this.id = id;
            A = a;
            B = b;
            C = c;
            this.encoding = encoding;
        }

        String getId() {
            return id;
        }

        String getA() {
            return A;
        }

        String getB() {
            return B;
        }

        String getC() {
            return C;
        }

        public String getEncoding() {
            return encoding;
        }
    }
    
    /**
     * Encodes data in Code128 format
     * @param input Data to encode
     */
    public Code128(String input) {
        setRawData(input);
    }

    /**
     * Encodes data in Code128 format
     * @param input Data to encode
     * @param type Type of encoding to lock to. (Code 128A, Code 128B, Code 128C)
     */
    public Code128(String input, TYPES type) {
        this.type = type;
        setRawData(input);
    }

    private String encodeCode128() {
        //initialize datastructure to hold encoding information
        initCode128();

        return getEncoding();
    }

    private void initCode128() {
        //populate data
        this.C128_Code.add(new Entry ( "0", " ", " ", "00", "11011001100" ));
        this.C128_Code.add(new Entry ( "1", "!", "!", "01", "11001101100" ));
        this.C128_Code.add(new Entry ( "2", "\"", "\"", "02", "11001100110" ));
        this.C128_Code.add(new Entry ( "3", "#", "#", "03", "10010011000" ));
        this.C128_Code.add(new Entry ( "4", "$", "$", "04", "10010001100" ));
        this.C128_Code.add(new Entry ( "5", "%", "%", "05", "10001001100" ));
        this.C128_Code.add(new Entry ( "6", "&", "&", "06", "10011001000" ));
        this.C128_Code.add(new Entry ( "7", "'", "'", "07", "10011000100" ));
        this.C128_Code.add(new Entry ( "8", "(", "(", "08", "10001100100" ));
        this.C128_Code.add(new Entry ( "9", ")", ")", "09", "11001001000" ));
        this.C128_Code.add(new Entry ( "10", "*", "*", "10", "11001000100" ));
        this.C128_Code.add(new Entry ( "11", "+", "+", "11", "11000100100" ));
        this.C128_Code.add(new Entry ( "12", ",", ",", "12", "10110011100" ));
        this.C128_Code.add(new Entry ( "13", "-", "-", "13", "10011011100" ));
        this.C128_Code.add(new Entry ( "14", ".", ".", "14", "10011001110" ));
        this.C128_Code.add(new Entry ( "15", "/", "/", "15", "10111001100" ));
        this.C128_Code.add(new Entry ( "16", "0", "0", "16", "10011101100" ));
        this.C128_Code.add(new Entry ( "17", "1", "1", "17", "10011100110" ));
        this.C128_Code.add(new Entry ( "18", "2", "2", "18", "11001110010" ));
        this.C128_Code.add(new Entry ( "19", "3", "3", "19", "11001011100" ));
        this.C128_Code.add(new Entry ( "20", "4", "4", "20", "11001001110" ));
        this.C128_Code.add(new Entry ( "21", "5", "5", "21", "11011100100" ));
        this.C128_Code.add(new Entry ( "22", "6", "6", "22", "11001110100" ));
        this.C128_Code.add(new Entry ( "23", "7", "7", "23", "11101101110" ));
        this.C128_Code.add(new Entry ( "24", "8", "8", "24", "11101001100" ));
        this.C128_Code.add(new Entry ( "25", "9", "9", "25", "11100101100" ));
        this.C128_Code.add(new Entry ( "26", ":", ":", "26", "11100100110" ));
        this.C128_Code.add(new Entry ( "27", ";", ";", "27", "11101100100" ));
        this.C128_Code.add(new Entry ( "28", "<", "<", "28", "11100110100" ));
        this.C128_Code.add(new Entry ( "29", "=", "=", "29", "11100110010" ));
        this.C128_Code.add(new Entry ( "30", ">", ">", "30", "11011011000" ));
        this.C128_Code.add(new Entry ( "31", "?", "?", "31", "11011000110" ));
        this.C128_Code.add(new Entry ( "32", "@", "@", "32", "11000110110" ));
        this.C128_Code.add(new Entry ( "33", "A", "A", "33", "10100011000" ));
        this.C128_Code.add(new Entry ( "34", "B", "B", "34", "10001011000" ));
        this.C128_Code.add(new Entry ( "35", "C", "C", "35", "10001000110" ));
        this.C128_Code.add(new Entry ( "36", "D", "D", "36", "10110001000" ));
        this.C128_Code.add(new Entry ( "37", "E", "E", "37", "10001101000" ));
        this.C128_Code.add(new Entry ( "38", "F", "F", "38", "10001100010" ));
        this.C128_Code.add(new Entry ( "39", "G", "G", "39", "11010001000" ));
        this.C128_Code.add(new Entry ( "40", "H", "H", "40", "11000101000" ));
        this.C128_Code.add(new Entry ( "41", "I", "I", "41", "11000100010" ));
        this.C128_Code.add(new Entry ( "42", "J", "J", "42", "10110111000" ));
        this.C128_Code.add(new Entry ( "43", "K", "K", "43", "10110001110" ));
        this.C128_Code.add(new Entry ( "44", "L", "L", "44", "10001101110" ));
        this.C128_Code.add(new Entry ( "45", "M", "M", "45", "10111011000" ));
        this.C128_Code.add(new Entry ( "46", "N", "N", "46", "10111000110" ));
        this.C128_Code.add(new Entry ( "47", "O", "O", "47", "10001110110" ));
        this.C128_Code.add(new Entry ( "48", "P", "P", "48", "11101110110" ));
        this.C128_Code.add(new Entry ( "49", "Q", "Q", "49", "11010001110" ));
        this.C128_Code.add(new Entry ( "50", "R", "R", "50", "11000101110" ));
        this.C128_Code.add(new Entry ( "51", "S", "S", "51", "11011101000" ));
        this.C128_Code.add(new Entry ( "52", "T", "T", "52", "11011100010" ));
        this.C128_Code.add(new Entry ( "53", "U", "U", "53", "11011101110" ));
        this.C128_Code.add(new Entry ( "54", "V", "V", "54", "11101011000" ));
        this.C128_Code.add(new Entry ( "55", "W", "W", "55", "11101000110" ));
        this.C128_Code.add(new Entry ( "56", "X", "X", "56", "11100010110" ));
        this.C128_Code.add(new Entry ( "57", "Y", "Y", "57", "11101101000" ));
        this.C128_Code.add(new Entry ( "58", "Z", "Z", "58", "11101100010" ));
        this.C128_Code.add(new Entry ( "59", "[", "[", "59", "11100011010" ));
        this.C128_Code.add(new Entry ( "60","\\", "\\", "60", "11101111010" ));
        this.C128_Code.add(new Entry ( "61", "]", "]", "61", "11001000010" ));
        this.C128_Code.add(new Entry ( "62", "^", "^", "62", "11110001010" ));
        this.C128_Code.add(new Entry ( "63", "_", "_", "63", "10100110000" ));
        this.C128_Code.add(new Entry ( "64", "\0", "`", "64", "10100001100" ));
        this.C128_Code.add(new Entry ( "65", getChar(1), "a", "65", "10010110000" ));
        this.C128_Code.add(new Entry ( "66", getChar(2), "b", "66", "10010000110" ));
        this.C128_Code.add(new Entry ( "67", getChar(3), "c", "67", "10000101100" ));
        this.C128_Code.add(new Entry ( "68", getChar(4), "d", "68", "10000100110" ));
        this.C128_Code.add(new Entry ( "69", getChar(5), "e", "69", "10110010000" ));
        this.C128_Code.add(new Entry ( "70", getChar(6), "f", "70", "10110000100" ));
        this.C128_Code.add(new Entry ( "71", getChar(7), "g", "71", "10011010000" ));
        this.C128_Code.add(new Entry ( "72", getChar(8), "h", "72", "10011000010" ));
        this.C128_Code.add(new Entry ( "73", getChar(9), "i", "73", "10000110100" ));
        this.C128_Code.add(new Entry ( "74", getChar(10), "j", "74", "10000110010" ));
        this.C128_Code.add(new Entry ( "75", getChar(11), "k", "75", "11000010010" ));
        this.C128_Code.add(new Entry ( "76", getChar(12), "l", "76", "11001010000" ));
        this.C128_Code.add(new Entry ( "77", getChar(13), "m", "77", "11110111010" ));
        this.C128_Code.add(new Entry ( "78", getChar(14), "n", "78", "11000010100" ));
        this.C128_Code.add(new Entry ( "79", getChar(15), "o", "79", "10001111010" ));
        this.C128_Code.add(new Entry ( "80", getChar(16), "p", "80", "10100111100" ));
        this.C128_Code.add(new Entry ( "81", getChar(17), "q", "81", "10010111100" ));
        this.C128_Code.add(new Entry ( "82", getChar(18), "r", "82", "10010011110" ));
        this.C128_Code.add(new Entry ( "83", getChar(19), "s", "83", "10111100100" ));
        this.C128_Code.add(new Entry ( "84", getChar(20), "t", "84", "10011110100" ));
        this.C128_Code.add(new Entry ( "85", getChar(21), "u", "85", "10011110010" ));
        this.C128_Code.add(new Entry ( "86", getChar(22), "v", "86", "11110100100" ));
        this.C128_Code.add(new Entry ( "87", getChar(23), "w", "87", "11110010100" ));
        this.C128_Code.add(new Entry ( "88", getChar(24), "x", "88", "11110010010" ));
        this.C128_Code.add(new Entry ( "89", getChar(25), "y", "89", "11011011110" ));
        this.C128_Code.add(new Entry ( "90", getChar(26), "z", "90", "11011110110" ));
        this.C128_Code.add(new Entry ( "91", getChar(27), "{", "91", "11110110110" ));
        this.C128_Code.add(new Entry ( "92", getChar(28), "|", "92", "10101111000" ));
        this.C128_Code.add(new Entry ( "93", getChar(29), "}", "93", "10100011110" ));
        this.C128_Code.add(new Entry ( "94", getChar(30), "~", "94", "10001011110" ));

        this.C128_Code.add(new Entry ( "95", getChar(31), getChar(127), "95", "10111101000" ));
        this.C128_Code.add(new Entry ( "96", getChar(202)/*FNC3*/, getChar(202)/*FNC3*/, "96", "10111100010" ));
        this.C128_Code.add(new Entry ( "97", getChar(201)/*FNC2*/, getChar(201)/*FNC2*/, "97", "11110101000" ));
        this.C128_Code.add(new Entry ( "98", "SHIFT", "SHIFT", "98", "11110100010" ));
        this.C128_Code.add(new Entry ( "99", "CODE_C", "CODE_C", "99", "10111011110" ));
        this.C128_Code.add(new Entry ( "100", "CODE_B", getChar(203)/*FNC4*/, "CODE_B", "10111101110" ));
        this.C128_Code.add(new Entry ( "101", getChar(203)/*FNC4*/, "CODE_A", "CODE_A", "11101011110" ));
        this.C128_Code.add(new Entry ( "102", getChar(200)/*FNC1*/, getChar(200)/*FNC1*/, getChar(200)/*FNC1*/, "11110101110" ));
        this.C128_Code.add(new Entry ( "103", "START_A", "START_A", "START_A", "11010000100" ));
        this.C128_Code.add(new Entry ( "104", "START_B", "START_B", "START_B", "11010010000" ));
        this.C128_Code.add(new Entry ( "105", "START_C", "START_C", "START_C", "11010011100" ));
        this.C128_Code.add(new Entry ( "", "STOP", "STOP", "STOP", "11000111010" ));
     }

    private Entry findRow(String column, String match) {
        for (Entry entry : C128_Code) {
            if (column.equalsIgnoreCase("A") && entry.getA().equals(match)) {
                return entry;
            } else if (column.equalsIgnoreCase("B") && entry.getB().equals(match)) {
                return entry;
            } else if (column.equalsIgnoreCase("C") && entry.getC().equals(match)) {
                return entry;
            } else if (column.equalsIgnoreCase("encoding") && entry.encoding.equals(match)) {
                return entry;
            } else if (column.equalsIgnoreCase("id") && entry.getId().equals(match)) {
                return entry;
            }
        }

        return null;
    }
    private CodeCharacter findStartorCodeCharacter(String s) {
        CodeCharacter returnValue = new CodeCharacter();

        //if two chars are numbers (or FNC1) then START_C or CODE_C
        if (s.length() > 1 && (Character.isDigit(s.toCharArray()[0]) || String.valueOf(s.toCharArray()[0]).equals(getChar(200)) && (Character.isDigit(s.toCharArray()[1]) || String.valueOf(s.toCharArray()[1]).equals(getChar(200))))) {
            if (startCharacter == null) {
                startCharacter = findRow("A", "START_C");
                returnValue.rows.add(startCharacter);
            } else {
                returnValue.rows.add(findRow("A", "CODE_C"));
            }

            returnValue.col = 1;
        } else {
            boolean AFound = false;
            boolean BFound = false;
            for (Entry row : this.C128_Code) {
                try {
                    if (!AFound && s.equals(row.A)) {
                        AFound = true;
                        returnValue.col = 2;

                        if (startCharacter == null) {
                            startCharacter = findRow("A",  "START_A");
                            returnValue.rows.add(startCharacter);
                        } else {
                            returnValue.rows.add(findRow("B", "CODE_A"));//first column is FNC4 so use B
                        }
                    } else if (!BFound && s.equals(row.B)) {
                        BFound = true;
                        returnValue.col = 1;

                        if (startCharacter == null) {
                            startCharacter = findRow("A", "START_B");
                            returnValue.rows.add(startCharacter);
                        } else {
                            returnValue.rows.add(findRow("A", "CODE_B"));
                        }
                    } else if (AFound && BFound) {
                        break;
                    }
                } catch (Exception ex) {
                    error("EC128-1: " + ex.getMessage());
                }
            }

            if (returnValue.rows.isEmpty()) {
                error("EC128-2: Could not determine start character.");
            }
        }

        return returnValue;
    }

    private String CalculateCheckDigit() {
        int checkSum = 0;

        for (int i = 0; i < formattedData.size(); i++) {
            //replace apostrophes with double apostrophes for escape chars
            String s = formattedData.get(i).replace("'", "''");

            //try to find value in the A column
            Entry row = findRow("A",  s);

            //try to find value in the B column
            if (row == null) {
                row = findRow("B", s);
            }

            //try to find value in the C column
            if (row == null) {
                row = findRow("C", s);
            }

            if (row == null) {
                error("EC128-3: No value found in encoding table");
                return null;
            }

            int value = Integer.parseInt(row.getId());
            int addition = value * ((i == 0) ? 1 : i);
            checkSum +=  addition;
        }

        int Remainder = (checkSum % 103);
        Entry retRows = findRow("id", String.valueOf(Remainder));
        if (retRows != null) {
            return retRows.encoding;
        } else {
            return "";
        }
    }
        
    private void breakUpDataForEncoding() {
        StringBuilder temp = new StringBuilder();
        String tempRawData = getRawData();

        //breaking the raw data up for code A and code B will mess up the encoding
        if (this.type == TYPES.A || this.type == TYPES.B) {
            for (char c : getRawData().toCharArray()){
                formattedData.add(String.valueOf(c));
            }
            return;
        } else if (this.type == TYPES.C) {
            if (!checkNumericOnly(getRawData())) {
                error("EC128-6: Only numeric values can be encoded with C128-C.");
            }

            //CODE C: adds a 0 to the front of the Raw_Data if the length is not divisible by 2
            if (getRawData().length() % 2 > 0) {
                tempRawData = "0" + getRawData();
            }
        }

        for (char c : tempRawData.toCharArray()) {
            if (checkNumericOnly(String.valueOf(c))) {
                if (temp.length() == 0) {
                    temp.append(c);
                } else {
                    temp.append(c);
                    formattedData.add(temp.toString());
                    temp = new StringBuilder();
                }
            } else {
                if (temp.length() > 0) {
                    formattedData.add(temp.toString());
                    temp = new StringBuilder();
                }
                formattedData.add(String.valueOf(c));
            }
        }

        //if something is still in temp go ahead and push it onto the queue
        if (temp.length() > 0) {
            formattedData.add(temp.toString());
        }
    }

    private void insertStartandCodeCharacters() {
        Entry currentCodeSet;
        String currentCodeString;

        if (this.type != TYPES.DYNAMIC) {
            switch (this.type) {
                case A:
                    formattedData.add(0, "START_A");
                    break;
                case B:
                    formattedData.add(0, "START_B");
                    break;
                case C:
                    formattedData.add(0, "START_C");
                    break;
                default:
                    error("EC128-4: Unknown start type in fixed type encoding.");
                    break;
            }
        } else {
            try {
                //initial char with start char
                currentCodeSet = null;
                currentCodeString = "";

                for (int i = 0; i < formattedData.size(); i++) {
                    CodeCharacter codeCharacter = findStartorCodeCharacter(formattedData.get(i));
                    List tempStartChars = codeCharacter.rows;

                    //check all the start characters and see if we need to stay with the same codeset or if a change of sets is required
                    boolean sameCodeSet = false;
                    for (Entry row : tempStartChars) {
                        if (row.getA().endsWith(currentCodeString) || row.getB().endsWith(currentCodeString) || row.getC().endsWith(currentCodeString)) {
                            sameCodeSet = true;
                            break;
                        }
                    }

                    //change current code string if not the same as the current code set
                    if (currentCodeSet == null || !sameCodeSet) {
                        currentCodeSet = tempStartChars.get(0);

                        switch (codeCharacter.col) {
                            case 0:
                                currentCodeString = currentCodeSet.getA().substring(currentCodeSet.getA().length() - 1);
                                formattedData.add(i++, currentCodeSet.getA());
                                break;
                            case 1:
                                currentCodeString = currentCodeSet.getB().substring(currentCodeSet.getB().length() - 1);
                                formattedData.add(i++, currentCodeSet.getB());
                                break;
                            case 2:
                                currentCodeString = currentCodeSet.getC().substring(currentCodeSet.getC().length() - 1);
                                formattedData.add(i++, currentCodeSet.getC());
                                break;
                        }
                    }
                }
            } catch (Exception ex) {
                error("EC128-3: Could not insert start and code characters.\n Message: " + ex.getMessage());
            }
        }
    }
    private String getEncoding() {
        //break up data for encoding
        breakUpDataForEncoding();

        //insert the start characters
        insertStartandCodeCharacters();

        StringBuilder encodedData = new StringBuilder();
        for (String s : formattedData) {
            //handle exception with apostrophes in select statements
            String s1 = s.replace("'", "''");
            Entry E_Row;

            //select encoding only for type selected
            switch (this.type) {
                case A:
                    E_Row = findRow("A",  s1);
                    break;
                case B:
                    E_Row = findRow("B",  s1);
                    break;
                case C:
                    E_Row = findRow("C",  s1);
                    break;
                case DYNAMIC:
                    E_Row = findRow("A",  s1);
                    if (E_Row == null) {
                        E_Row = findRow("B", s1);

                        if (E_Row == null) {
                            E_Row = findRow("C", s1);
                        }
                    }
                    break;
                default:
                    E_Row = null;
                    break;
            }

            if (E_Row == null) {
                error("EC128-5: Could not find encoding of a value( " + s1 + " ) in C128 type " + this.type);
                return null;
            }

            encodedData.append(E_Row.encoding);
            this.encodedData.add(E_Row.encoding);
        }

        //add the check digit
        encodedData.append(CalculateCheckDigit());
        this.encodedData.add(CalculateCheckDigit());

        //add the stop character
        Entry stopChar = findRow("A", "STOP");
        if (stopChar != null) {
            encodedData.append(stopChar.encoding);
            this.encodedData.add(stopChar.encoding);
        }

        //add the termination bars
        encodedData.append("11");
        this.encodedData.add("11");

        return encodedData.toString();
    }

    @Override
    public String getEncodedValue() {
        return encodeCode128();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy