summaryrefslogtreecommitdiffstats
path: root/src/org/uic/barcode/staticFrame
diff options
context:
space:
mode:
Diffstat (limited to 'src/org/uic/barcode/staticFrame')
-rw-r--r--src/org/uic/barcode/staticFrame/DataRecord.java199
-rw-r--r--src/org/uic/barcode/staticFrame/GENERICDataRecord.java62
-rw-r--r--src/org/uic/barcode/staticFrame/StaticFrame.java762
-rw-r--r--src/org/uic/barcode/staticFrame/UFLEXDataRecord.java90
-rw-r--r--src/org/uic/barcode/staticFrame/UHEADDataRecord.java269
-rw-r--r--src/org/uic/barcode/staticFrame/UTLAYDataRecord.java272
-rw-r--r--src/org/uic/barcode/staticFrame/package.html21
7 files changed, 1675 insertions, 0 deletions
diff --git a/src/org/uic/barcode/staticFrame/DataRecord.java b/src/org/uic/barcode/staticFrame/DataRecord.java
new file mode 100644
index 0000000..27e128f
--- /dev/null
+++ b/src/org/uic/barcode/staticFrame/DataRecord.java
@@ -0,0 +1,199 @@
+package org.uic.barcode.staticFrame;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+
+import org.uic.ticket.EncodingFormatException;
+
+/**
+ * The Class DataRecord implements the basic decoding and encoding
+ * of the data record structure, the split into tag, version, length and content.
+ *
+ * Implementing classes must provide decoding and encoding of the content
+ *
+ */
+public abstract class DataRecord {
+
+ /** The id tag. */
+ protected String idTag;
+
+ /** The version id. */
+ protected String versionId;
+
+ /** The content. */
+ protected byte[] content;
+
+ /**
+ * Instantiates a new data record.
+ *
+ * @param idTag the id tag
+ * @param version the version
+ */
+ public DataRecord (String idTag, String version) {
+ this.idTag = idTag;
+ this.versionId = version;
+ }
+
+ /**
+ * Instantiates a new data record.
+ *
+ * @param idTag the id tag
+ */
+ public DataRecord (String idTag) {
+ this.idTag = idTag;
+ }
+
+ /**
+ * Encode.
+ *
+ * @return the byte[]
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ public byte[] encode() throws IOException, EncodingFormatException {
+
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+
+ encodeContent();
+
+ //size of tag + version + length
+ int length = 12;
+
+ //size of data
+ length = length + content.length;
+
+ String lengthElement = String.format("%04d",length);
+
+ while (idTag.length() < 6) {
+ idTag = idTag + " ";
+ }
+
+ while (versionId.length() < 2) {
+ versionId = "0" + versionId;
+ }
+
+ outputStream.write(idTag.getBytes());
+
+ outputStream.write(versionId.getBytes());
+
+ outputStream.write(lengthElement.getBytes());
+
+ outputStream.write(content);
+
+ return outputStream.toByteArray();
+ }
+
+ /**
+ * Decode.
+ *
+ * @param byteData the byte data
+ * @return the int
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ public int decode(byte[] byteData) throws IOException, EncodingFormatException {
+
+ int offset = 0;
+ String tag = new String(Arrays.copyOfRange(byteData, offset, offset + 6));
+ this.setIdTag(tag);
+ offset = offset + 6;
+
+ String version = new String(Arrays.copyOfRange(byteData, offset, offset + 2));
+ this.setVersionId(version);
+ offset = offset + 2;
+
+ String dataSize = new String(Arrays.copyOfRange(byteData, offset, offset + 4));
+ offset = offset + 4;
+
+ int length = Integer.parseInt(dataSize) - 12;
+ this.setData(Arrays.copyOfRange(byteData, offset, offset + length));
+
+ decodeContent();
+
+ return length + 12;
+ }
+
+
+ /**
+ * Gets the id tag.
+ *
+ * @return the id tag
+ */
+ public String getIdTag() {
+ return idTag;
+ }
+
+
+ /**
+ * Sets the id tag.
+ *
+ * @param idTag the new id tag
+ */
+ public void setIdTag(String idTag) {
+ this.idTag = idTag;
+ }
+
+
+ /**
+ * Gets the version id.
+ *
+ * @return the version id
+ */
+ public String getVersionId() {
+ return versionId;
+ }
+
+
+ /**
+ * Sets the version id.
+ *
+ * @param versionId the new version id
+ */
+ public void setVersionId(String versionId) {
+ this.versionId = versionId;
+ }
+
+
+ /**
+ * Gets the data.
+ *
+ * @return the data
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ protected byte[] getData() throws IOException, EncodingFormatException {
+ return content;
+ }
+
+
+ /**
+ * Sets the data.
+ *
+ * @param data the new data
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ protected void setData(byte[] data) throws IOException, EncodingFormatException {
+ this.content = data;
+ }
+
+
+
+ /**
+ * Decode content.
+ *
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ protected abstract void decodeContent() throws IOException, EncodingFormatException;
+
+ /**
+ * Encode content.
+ *
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ protected abstract void encodeContent() throws IOException, EncodingFormatException;
+
+}
diff --git a/src/org/uic/barcode/staticFrame/GENERICDataRecord.java b/src/org/uic/barcode/staticFrame/GENERICDataRecord.java
new file mode 100644
index 0000000..2477af9
--- /dev/null
+++ b/src/org/uic/barcode/staticFrame/GENERICDataRecord.java
@@ -0,0 +1,62 @@
+package org.uic.barcode.staticFrame;
+
+import java.io.IOException;
+
+import org.uic.ticket.EncodingFormatException;
+
+
+/**
+ * The Class GENERICDataRecord implements a generic bilateral data record included in a static bar code frame.
+ */
+public class GENERICDataRecord extends DataRecord {
+
+ /**
+ * Instantiates a new GENERIC data record.
+ *
+ * @param idTag the id tag
+ */
+ public GENERICDataRecord(String idTag) {
+ super(idTag);
+ }
+
+ /**
+ * Decode content.
+ *
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ @Override
+ protected void decodeContent() throws IOException, EncodingFormatException {
+ // Do Nothing, needs to be implemented by subclasses
+ }
+
+ /**
+ * Encode content.
+ *
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ @Override
+ protected void encodeContent() throws IOException, EncodingFormatException {
+ // Do Nothing, needs to be implemented by subclasses
+ }
+
+ /**
+ * Gets the content.
+ *
+ * @return the content
+ */
+ public byte[] getContent() {
+ return content;
+ }
+
+ /**
+ * Sets the content.
+ *
+ * @param content the new content
+ */
+ public void setContent(byte[] content) {
+ this.content = content;
+ }
+
+}
diff --git a/src/org/uic/barcode/staticFrame/StaticFrame.java b/src/org/uic/barcode/staticFrame/StaticFrame.java
new file mode 100644
index 0000000..7a0f029
--- /dev/null
+++ b/src/org/uic/barcode/staticFrame/StaticFrame.java
@@ -0,0 +1,762 @@
+package org.uic.barcode.staticFrame;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.Security;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.Provider.Service;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.zip.DataFormatException;
+import java.util.zip.Deflater;
+import java.util.zip.Inflater;
+
+import org.uic.ticket.EncodingFormatException;
+
+
+/**
+ * The Class StaticHeader implements the static bar code header frame defined in UIC IRS 90918-9.
+ * It allows to decode and encode the bar code content and to add sub-records as defined in the IRS 90918-9 for:
+ * - additional header data
+ * - Ticket Layout content
+ * - Flexible content
+ * - bilateral data records
+ */
+public class StaticFrame {
+
+ /** The additional header record. */
+ private UHEADDataRecord headerRecord;
+
+ /** The bar code version. */
+ private int version;
+
+ /** The u_flex. */
+ private UFLEXDataRecord uFlex;
+
+ /** The u_tlay. */
+ private UTLAYDataRecord uTlay;
+
+ /** The security provider. */
+ private String securityProvider;
+
+ /** The signature key. */
+ private String signatureKey;
+
+ /** The signature. */
+ private byte[] signature;
+
+ /** The data records. */
+ private ArrayList<DataRecord> dataRecords = new ArrayList<DataRecord>();
+
+
+ private byte[] signedData = null;
+
+ /**
+ * Instantiates a new static header frame.
+ */
+ public StaticFrame (){ }
+
+
+
+ /**
+ * Instantiates a new static header and decodes the provided data.
+ *
+ * @param bytes the bar code data
+ * @throws EncodingFormatException the encoding format exception
+ * @throws DataFormatException the data format exception
+ * @throws IOException Signals that an I/O exception has occurred.
+ */
+ public StaticFrame (byte[] bytes) throws EncodingFormatException, DataFormatException, IOException{
+ decode(bytes);
+ }
+
+
+ /**
+ * Encode the barcode data.
+ *
+ * @param version the barcode version
+ * @return byte[] the encoded data as
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws Exception the exception
+ */
+ /*
+ * creates a UIC bar code of version 1
+ *
+ * limits:
+ * - version 1 allows for signatures up to 50 byte length
+ * - max data length 2048 Byte
+ * input:
+ * data to be included
+ * provider of the signature
+ * processing:
+ * 1. create header informations
+ * 2. compression of the data content
+ * 3. adding a signature
+ * output:
+ * raw data to be included in an aztec bar code
+ *
+ */
+ public byte[] encode() throws IOException, Exception {
+
+ if (headerRecord == null && uFlex == null && uTlay == null
+ && (dataRecords == null || dataRecords.isEmpty())) return null;
+
+ if (signedData == null) {
+ signedData = encodeData();
+ }
+
+ if (version != 1 && version != 2) {
+ throw (new Exception(String.format("UIC Barcode Version %d not supported", version)));
+ }
+
+ if (signedData.length < 1) {
+ throw new IOException("data missing!");
+ }
+ if (signedData.length > 2048) {
+ throw new IOException("too many data!"); //2048 should be enough
+ }
+
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+
+ //UIC bar code version 1
+ String header = "#UT01";
+ if (version == 2) {
+ header = "#UT02";
+ }
+ outputStream.write(header.getBytes());
+
+ outputStream.write(securityProvider.getBytes());
+
+
+ while (signatureKey.length() < 5) {
+ signatureKey = "0" + signatureKey;
+ }
+ outputStream.write(signatureKey.getBytes());
+
+ if (signature.length < 1) {
+ // signature too small for bar code version 1
+ throw new IOException("signature size too small!");
+ }
+
+ if (version == 1) {
+ if (signature.length > 50) {
+ // signature too large for bar code version 1
+ throw new IOException("signature size too large!");
+ }
+ outputStream.write(Arrays.copyOfRange(signature, 0, 50));
+ } else if (version == 2) {
+ BigInteger[] bInts = null;
+ byte zeroByte = 0;
+
+ bInts = decodeSignatureIntegerSequence(signature);
+ byte[] r = toUnsignedBytes(bInts[0]);
+
+ byte[] s = toUnsignedBytes(bInts[1]);
+
+ if (r.length > 32 || s.length > 32) {
+ throw (new EncodingFormatException(String.format("DSA signature too big")));
+ }
+ for (int i = 0; i < 32 - r.length; i++) {
+ outputStream.write(zeroByte);
+ }
+ outputStream.write(r);
+ for (int i = 0; i < 32 - s.length; i++) {
+ outputStream.write(zeroByte);
+ }
+ outputStream.write(s);
+ //outputStream.write(Arrays.copyOfRange(signature, 0, 64));
+ }
+
+ String length = String.format("%04d", signedData.length);
+ outputStream.write(length.getBytes());
+
+ outputStream.write(signedData);
+
+ outputStream.close();
+
+ return outputStream.toByteArray();
+ }
+
+
+ /**
+ * Adds a proprietary data record.
+ *
+ * @param record the record
+ */
+ public void addDataRecord(DataRecord record) {
+ dataRecords.add(record);
+ }
+
+ /**
+ * Gets the version of the header frame.
+ *
+ * @return the version
+ */
+ public int getVersion() {
+ return version;
+ }
+
+ /**
+ * Sets the version of the header frame.
+ * supported values are 1 and 2
+ *
+ * @param version the new version
+ */
+ public void setVersion(int version) {
+ this.version = version;
+ }
+
+ /**
+ * Gets the security provider.
+ *
+ * @return the security provider
+ */
+ public String getSecurityProvider() {
+ return securityProvider;
+ }
+
+ /**
+ * Sets the security provider.
+ *
+ * @param securityProvider the new security provider
+ */
+ public void setSecurityProvider(String securityProvider) {
+ this.securityProvider = securityProvider;
+ }
+
+ /**
+ * Gets the signature key identifier.
+ *
+ * @return the signature key
+ */
+ public String getSignatureKey() {
+ return signatureKey;
+ }
+
+ /**
+ * Sets the signature key identifier.
+ *
+ * @param signatureKey the new signature key
+ */
+ public void setSignatureKey(String signatureKey) {
+ this.signatureKey = signatureKey;
+ }
+
+ /**
+ * Gets the signature.
+ *
+ * @return the signature
+ */
+ public byte[] getSignature() {
+ return signature;
+ }
+
+ /**
+ * Sets the signature.
+ *
+ * @param signature the new signature
+ */
+ public void setSignature(byte[] signature) {
+ this.signature = signature;
+ }
+
+ /**
+ * Gets the additional header record.
+ *
+ * @return the header record
+ */
+ public UHEADDataRecord getHeaderRecord() {
+ return headerRecord;
+ }
+
+ /**
+ * Gets the list of bilateral data records.
+ *
+ * @return the data records
+ */
+ public ArrayList<DataRecord> getDataRecords() {
+ return dataRecords;
+ }
+
+ /**
+ * Gets the data for signing.
+ *
+ * @return the data to be signed
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ public byte[] getDataForSignature() throws IOException, EncodingFormatException {
+ // data compression
+ if (signedData != null) return signedData;
+
+ Deflater deflater = new Deflater();
+ byte[] data = encodeData();
+ deflater.setInput(data);
+ ByteArrayOutputStream compressStream = new ByteArrayOutputStream(data.length);
+ byte[] buffer = new byte[2048];
+ deflater.finish();
+ while (!deflater.finished()) {
+ int count = deflater.deflate(buffer); // returns the number of result bytes
+ compressStream.write(buffer, 0, count);
+ }
+ compressStream.close();
+
+ return compressStream.toByteArray();
+ }
+
+ /**
+ * Get the encoded data for the bar code.
+ *
+ * @return the byte[]
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ private byte[] encodeData() throws IOException, EncodingFormatException {
+
+ if (this.uFlex == null && this.uTlay == null && this.headerRecord == null &&
+ (dataRecords == null || dataRecords.isEmpty())) return null;
+
+ ByteArrayOutputStream totalStream = new ByteArrayOutputStream();
+
+ //encode header for layout
+ if (headerRecord != null) {
+ byte[] header = headerRecord.encode();
+
+ if (header != null && header.length > 0) {
+ totalStream.write(header);
+ }
+ }
+
+ //encode layout
+ if (uTlay != null) {
+ byte[] layout = uTlay.encode();
+ if (layout != null && layout.length > 0) {
+ totalStream.write(layout);
+ }
+ }
+
+ if (uFlex != null) {
+ byte[] content = uFlex.encode();
+ if (content != null && content.length > 0){
+ totalStream.write(content);
+ }
+ }
+
+ //third party content
+ for (DataRecord dataRecord : dataRecords){
+
+ byte[] content = dataRecord.encode();
+ if (content != null && content.length > 0){
+ totalStream.write(content);
+ }
+ }
+ return totalStream.toByteArray();
+ }
+
+ /**
+ * Encode signature integer sequence.
+ *
+ * Support function to format two parameters as DER encoded integer list
+ * to get a valid formated DSA signature from the signature parameter
+ *
+ * @param i1 the i 1
+ * @param i2 the i 2
+ * @return the byte[]
+ * @throws IOException Signals that an I/O exception has occurred.
+ */
+ public static byte[] encodeSignatureIntegerSequence(BigInteger i1, BigInteger i2) throws IOException {
+
+ //SEQUENCE OF --> tag 16
+ int sequenceTag = 16 + 32; // (bits 6 = 1 constructed)
+ //INTEGER --> tag 2
+ int integerTag = 2;
+
+ byte[] b1 = toUnsignedBytes(i1);
+ int lb1 = b1.length;
+ byte[] b2 = toUnsignedBytes(i2);
+ int lb2 = b2.length;
+
+ int sequenceLength = lb1 + lb2 + 4;
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ out.write((byte) sequenceTag);
+ out.write((byte) sequenceLength);
+ out.write((byte) integerTag);
+ out.write((byte) lb1);
+ out.write(b1);
+ out.write((byte) integerTag);
+ out.write((byte) lb2);
+ out.write(b2);
+
+ return out.toByteArray();
+ }
+
+ /**
+ * Decode signature integer sequence.
+ *
+ * Support function to decode a DSA signature
+ * Provides the two DSA signature parameter encoded in a DSA signature
+ *
+ * @param bytes the bytes
+ * @return the big integer[]
+ * @throws Exception the exception
+ */
+ public static BigInteger[] decodeSignatureIntegerSequence(byte[] bytes) throws Exception {
+
+ int sequenceTag = (int) bytes[0];
+
+ if (sequenceTag != 48) throw new Exception("signature is not a sequence");
+
+ int sequenceLength = (int) bytes[1];
+
+ if (sequenceLength < 6) throw new Exception("signature sequence too short");
+
+ BigInteger[] result = new BigInteger[2];
+
+ int offset = 2;
+ int i = 0;
+ while (offset < bytes.length && i < 2) {
+ int integerTag = (int) bytes[offset];
+ if (integerTag != 2) throw new Exception("signature is not an integer sequence");
+ int integerLength = (int) bytes[offset + 1];
+ byte[] value = Arrays.copyOfRange(bytes, offset + 2, offset + 2 + integerLength);
+ result[i] = new BigInteger(+1, value);
+ offset = offset + integerLength + 2;
+ i++;
+ }
+
+ return result;
+ }
+
+ /**
+ * Decode.
+ *
+ * @param inputData the input data
+ * @throws EncodingFormatException the encoding format exception
+ * @throws DataFormatException the data format exception
+ * @throws IOException Signals that an I/O exception has occurred.
+ */
+ public void decode(byte[] inputData) throws EncodingFormatException, DataFormatException, IOException {
+
+
+ int offset = 0;
+ String headerTag = new String( Arrays.copyOfRange(inputData,offset,offset + 3));
+ offset = offset + 3;
+ if (!headerTag.equals("#UT")) {
+ throw (new EncodingFormatException("not a UIC barcode"));
+ }
+
+
+ String versionValue = new String(Arrays.copyOfRange(inputData,offset,offset + 2));
+ offset = offset + 2;
+ int barcodeVersion = 0;
+ try {
+ barcodeVersion = Integer.parseInt(versionValue);
+ this.setVersion(barcodeVersion);
+ } catch (NumberFormatException e2) {
+ throw (new EncodingFormatException(String.format("UIC Barcode Version %s not supported", versionValue)));
+ }
+
+ String providerValue = new String( Arrays.copyOfRange(inputData,offset,offset + 4));
+ this.setSecurityProvider(providerValue);
+ offset = offset + 4;
+
+ String signatureKeyIdValue = new String( Arrays.copyOfRange(inputData,offset,offset + 5));
+ this.setSignatureKey(signatureKeyIdValue);
+ offset = offset + 5;
+
+ byte[] sealdata = null;
+
+ if (barcodeVersion == 1) {
+ sealdata = Arrays.copyOfRange(inputData, offset, offset + 50);
+ signature = trimDsaSignature(sealdata);
+ offset = offset + 50;
+ } else if (barcodeVersion == 2) {
+ sealdata = Arrays.copyOfRange(inputData, offset, offset + 64);
+ signature = recombineDsaSignature(sealdata);
+ offset = offset + 64;
+ } else {
+ throw (new EncodingFormatException(String.format("UIC Barcode Version %s not supported", versionValue)));
+ }
+
+
+ String lengthValue = new String( Arrays.copyOfRange(inputData,offset,offset + 4));
+ offset = offset + 4;
+
+ int dataLength = 0;
+ dataLength = Integer.parseInt(lengthValue);
+
+ signedData = Arrays.copyOfRange(inputData, offset, offset + dataLength);
+
+ ByteBuffer containedDataBuffer = ByteBuffer.allocate(dataLength);
+ containedDataBuffer.put(signedData);
+
+ byte[] inflatedDataBuffer = new byte[2000];
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ Inflater inflater = new Inflater();
+ byte[] inflaterInput = containedDataBuffer.array();
+ inflater.setInput(inflaterInput);
+ while (!inflater.finished()) {
+ int count = inflater.inflate(inflatedDataBuffer,0,2000);
+ if (inflater.needsDictionary()) {
+ break;
+ }
+ outputStream.write(inflatedDataBuffer, 0, count);
+ }
+
+ outputStream.close();
+
+ byte[] byteData = outputStream.toByteArray();
+
+ offset = 0;
+ int remainingBytes = byteData.length;
+
+ while (remainingBytes > 0) {
+
+ String tag = new String(Arrays.copyOfRange(byteData, offset, offset + 6));
+ int length = 0;
+
+ if (tag.startsWith("U_TLAY")) {
+ UTLAYDataRecord record = new UTLAYDataRecord();
+ length = record.decode(Arrays.copyOfRange(byteData, offset, byteData.length));
+ this.uTlay = record;
+ } else if (tag.startsWith("U_FLEX")) {
+ UFLEXDataRecord record = new UFLEXDataRecord();
+ length = record.decode(Arrays.copyOfRange(byteData, offset, byteData.length));
+ this.uFlex = record;
+ } else if (tag.startsWith("U_HEAD")) {
+ UHEADDataRecord record = new UHEADDataRecord();
+ length = record.decode(Arrays.copyOfRange(byteData, offset, byteData.length));
+ this.headerRecord = record;
+ } else {
+ DataRecord record = new GENERICDataRecord(tag);
+ length = record.decode(Arrays.copyOfRange(byteData, offset, byteData.length));
+ addDataRecord(record);
+ }
+ offset = offset + length;
+ remainingBytes = remainingBytes - length;
+ }
+ }
+
+
+ private byte[] recombineDsaSignature(byte[] sealdata) throws IOException {
+
+ //check whether the encoding is wrong and the sealdata contain a signature
+ //remove trailing zeroes from the signature
+ BigInteger[] bInts = null;
+ try {
+ bInts = decodeSignatureIntegerSequence(sealdata);
+ byte[] sig = encodeSignatureIntegerSequence(bInts[0],bInts[1]);
+ //decoding the entire signature was ok, so there was no split
+ return sig;
+ } catch (Exception e) {
+ //the signature is correctly implemented, continue with recombination
+ }
+
+ // split the data into two blocks
+ int length = sealdata.length / 2;
+ byte[] rBytes = Arrays.copyOfRange(sealdata, 0, length);
+ byte[] sBytes = Arrays.copyOfRange(sealdata, length, length + length);
+
+ //convert to BigInteger to get rid of leading zeroes
+ BigInteger r = new BigInteger(1,rBytes);
+ BigInteger s = new BigInteger(1,sBytes);
+
+ //encode as DSA signature structure
+ //SEQUENCE OF --> tag 16
+ int sequenceTag = 16 + 32; // (bits 6 = 1 constructed)
+ //INTEGER --> tag 2
+ int integerTag = 2;
+ byte[] b1 = toUnsignedBytes(r);
+ int lb1 = b1.length;
+ byte[] b2 = toUnsignedBytes(s);
+ int lb2 = b2.length;
+ int sequenceLength = lb1 + lb2 + 4;
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write((byte) sequenceTag);
+ out.write((byte) sequenceLength);
+ out.write((byte) integerTag);
+ out.write((byte) lb1);
+ out.write(b1);
+ out.write((byte) integerTag);
+ out.write((byte) lb2);
+ out.write(b2);
+ return out.toByteArray();
+
+
+ }
+
+ private static byte[] toUnsignedBytes(BigInteger i) {
+ byte[] b = i.abs().toByteArray();
+ //remove top sign bit
+ if (b[0] == 0) {
+ b = Arrays.copyOfRange(b, 1, b.length);
+ }
+ return b;
+ }
+
+
+ private byte[] trimDsaSignature(byte[] sealdata) throws EncodingFormatException {
+ //remove trailing zeroes from the signature
+ BigInteger[] bInts = null;
+ try {
+ bInts = decodeSignatureIntegerSequence(sealdata);
+ return encodeSignatureIntegerSequence(bInts[0],bInts[1]);
+ } catch (Exception e) {
+ throw (new EncodingFormatException(String.format("Invalid DSA signature")));
+ }
+ }
+
+
+
+ /**
+ * Verify the signature
+ *
+ * Note: an appropriate security provider (e.g. BC) must be registered before
+ *
+ * @param key the key
+ * @param algo the algorithm name
+ * @return true, if successful
+ * @throws InvalidKeyException the invalid key exception
+ * @throws NoSuchAlgorithmException the no such algorithm exception
+ * @throws SignatureException the signature exception
+ * @throws IllegalArgumentException the illegal argument exception
+ * @throws UnsupportedOperationException the unsupported operation exception
+ * @throws EncodingFormatException
+ * @throws IOException
+ */
+ public boolean ByAlgorithmName(PublicKey key, String algo) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException, IllegalArgumentException, UnsupportedOperationException, IOException, EncodingFormatException {
+ Signature sig = Signature.getInstance(algo);
+ sig.initVerify(key);
+ sig.update(this.getDataForSignature());
+ return sig.verify(this.getSignature());
+ }
+
+ /**
+ * Verify the signature
+ *
+ * Note: an appropriate security provider (e.g. BC) must be registered before
+ *
+ * @param key the key
+ * @param singningAlg the Object ID of the signing algorithm
+ * @return true, if successful
+ * @throws InvalidKeyException the invalid key exception
+ * @throws NoSuchAlgorithmException the no such algorithm exception
+ * @throws SignatureException the signature exception
+ * @throws IllegalArgumentException the illegal argument exception
+ * @throws UnsupportedOperationException the unsupported operatign exception
+ * @throws EncodingFormatException
+ * @throws IOException
+ */
+ public boolean verifyByAlgorithmOid(PublicKey key, String signingAlg) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException, IllegalArgumentException, UnsupportedOperationException, IOException, EncodingFormatException {
+ //find the algorithm name for the signature OID
+ String algo = null;
+ Provider[] provs = Security.getProviders();
+ for (Provider prov : provs) {
+ Service service = prov.getService("Signature",signingAlg);
+ if (service != null) {
+ algo = service.getAlgorithm();
+ }
+ }
+ Signature sig = Signature.getInstance(algo);
+ sig.initVerify(key);
+ sig.update(getDataForSignature());
+ return sig.verify(this.getSignature());
+ }
+
+ /**
+ * Sign the contained data block.
+ *
+ * Note: an appropriate security provider (e.g. BC) must be registered before
+ *
+ * @param key the key
+ * @param singningAlg the Object ID of the signing algorithm
+ * @return
+ * @throws NoSuchAlgorithmException the no such algorithm exception
+ * @throws InvalidKeyException the invalid key exception
+ * @throws SignatureException the signature exception
+ * @throws EncodingFormatException
+ * @throws IOException
+ */
+ public void signByAlgorithmOID(PrivateKey key,String signingAlg) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, IOException, EncodingFormatException {
+ //find the algorithm name for the signature OID
+ String algo = null;
+ Provider[] provs = Security.getProviders();
+ for (Provider prov : provs) {
+ Service service = prov.getService("Signature",signingAlg);
+ if (service != null) {
+ algo = service.getAlgorithm();
+ }
+ }
+ Signature sig = Signature.getInstance(algo);
+ sig.initSign(key);
+ signedData = getDataForSignature();
+ sig.update(signedData);
+ signature = sig.sign();
+ }
+
+ /**
+ * Sign the contained data block.
+ *
+ * Note: an appropriate security provider (e.g. BC) must be registered before
+ *
+ * @param key the key
+ * @param algo the name of the signing algorithm
+ * @return
+ * @throws NoSuchAlgorithmException the no such algorithm exception
+ * @throws InvalidKeyException the invalid key exception
+ * @throws SignatureException the signature exception
+ * @throws EncodingFormatException
+ * @throws IOException
+ */
+ public void signUsingAlgorithmName(PrivateKey key,String algo) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, IOException, EncodingFormatException {
+ Signature sig = Signature.getInstance(algo);
+ sig.initSign(key);
+ sig.update(getDataForSignature());
+ signature = sig.sign();
+ }
+
+
+
+ public UFLEXDataRecord getuFlex() {
+ return uFlex;
+ }
+
+
+
+ public UTLAYDataRecord getuTlay() {
+ return uTlay;
+ }
+
+
+
+ public void setuFlex(UFLEXDataRecord uFlex) {
+ this.uFlex = uFlex;
+ }
+
+
+
+ public void setuTlay(UTLAYDataRecord uTlay) {
+ this.uTlay = uTlay;
+ }
+
+
+
+ public void setHeaderRecord(UHEADDataRecord headerRecord) {
+ this.headerRecord = headerRecord;
+ }
+
+
+
+
+}
diff --git a/src/org/uic/barcode/staticFrame/UFLEXDataRecord.java b/src/org/uic/barcode/staticFrame/UFLEXDataRecord.java
new file mode 100644
index 0000000..9731198
--- /dev/null
+++ b/src/org/uic/barcode/staticFrame/UFLEXDataRecord.java
@@ -0,0 +1,90 @@
+package org.uic.barcode.staticFrame;
+
+import java.io.IOException;
+
+import org.uic.ticket.EncodingFormatException;
+import org.uic.ticket.UicRailTicketCoder;
+import org.uic.ticket.api.spec.IUicRailTicket;
+
+/**
+ * The Class UFLEXDataRecord implements the dara record to hold the data of an ASN.1 PER encoded UIC ticket.
+ */
+public class UFLEXDataRecord extends DataRecord {
+
+
+ /** The ticket. */
+ private IUicRailTicket ticket;
+
+ /**
+ * Instantiates a new UFLEX data record.
+ */
+ public UFLEXDataRecord() {
+ super("U_FLEX");
+ }
+
+ /**
+ * Instantiates a new UFLEX data record.
+ *
+ * @param version the version
+ */
+ public UFLEXDataRecord(String version) {
+ super("U_FLEX", version);
+ }
+
+
+ /**
+ * Gets the ticket.
+ *
+ * @return the ticket
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ public IUicRailTicket getTicket() throws IOException, EncodingFormatException {
+ if (ticket != null) {
+ return ticket;
+ }
+ return null;
+ }
+
+ /**
+ * Sets the ticket.
+ *
+ * @param ticket the new ticket
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ public void setTicket(IUicRailTicket ticket) throws IOException, EncodingFormatException {
+ this.ticket = ticket;
+ super.setData(null);
+ }
+
+
+ /**
+ * Decode content.
+ *
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ @Override
+ protected void decodeContent() throws IOException, EncodingFormatException {
+ UicRailTicketCoder coder = new UicRailTicketCoder();
+ int version = Integer.parseInt(super.getVersionId());
+ this.ticket = coder.decodeFromAsn(content,version);
+ }
+
+ /**
+ * Encode content.
+ *
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ @Override
+ protected void encodeContent() throws IOException, EncodingFormatException {
+ UicRailTicketCoder coder = new UicRailTicketCoder();
+ int version = Integer.parseInt(super.getVersionId());
+ content = coder.encode(ticket, version);
+ }
+
+
+
+}
diff --git a/src/org/uic/barcode/staticFrame/UHEADDataRecord.java b/src/org/uic/barcode/staticFrame/UHEADDataRecord.java
new file mode 100644
index 0000000..e2ec301
--- /dev/null
+++ b/src/org/uic/barcode/staticFrame/UHEADDataRecord.java
@@ -0,0 +1,269 @@
+package org.uic.barcode.staticFrame;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+
+import org.uic.ticket.EncodingFormatException;
+
+/**
+ * The Class UHEADDataRecord implements the additional header record of a statis UIC bar code
+ */
+public class UHEADDataRecord extends DataRecord{
+
+
+ /** The issuing date. */
+ private Date issuingDate = null;
+
+ /** The flags. */
+ private int flags;
+
+ /** The issuer. */
+ private String issuer;
+
+ /** The identifier. */
+ private String identifier;
+
+ /** The language. */
+ private String language;
+
+ /** The additional language. */
+ private String additionalLanguage;
+
+ /**
+ * Instantiates a new UHEAD data record.
+ */
+ public UHEADDataRecord() {
+ super("U_HEAD");
+ }
+
+
+ /**
+ * Gets the issuing date.
+ *
+ * @return the issuing date
+ */
+ public Date getIssuingDate() {
+ return issuingDate;
+ }
+
+ /**
+ * Sets the issuing date.
+ *
+ * @param issuingDate the new issuing date
+ */
+ public void setIssuingDate(Date issuingDate) {
+ this.issuingDate = issuingDate;
+ }
+
+ /**
+ * Gets the flags.
+ *
+ * @return the flags
+ */
+ public int getFlags() {
+ return flags;
+ }
+
+ /**
+ * Sets the flags.
+ *
+ * @param flags the new flags
+ */
+ public void setFlags(int flags) {
+ this.flags = flags;
+ }
+
+ /**
+ * Gets the issuer.
+ *
+ * @return the issuer
+ */
+ public String getIssuer() {
+ return issuer;
+ }
+
+ /**
+ * Sets the issuer.
+ *
+ * @param issuer the new issuer
+ */
+ public void setIssuer(String issuer) {
+ this.issuer = issuer;
+ }
+
+ /**
+ * Gets the identifier.
+ *
+ * @return the identifier
+ */
+ public String getIdentifier() {
+ return identifier;
+ }
+
+ /**
+ * Sets the identifier.
+ *
+ * @param identifier the new identifier
+ */
+ public void setIdentifier(String identifier) {
+ this.identifier = identifier;
+ }
+
+ /**
+ * Gets the language.
+ *
+ * @return the language
+ */
+ public String getLanguage() {
+ return language;
+ }
+
+ /**
+ * Sets the language.
+ *
+ * @param language the new language
+ */
+ public void setLanguage(String language) {
+ this.language = language;
+ }
+
+ /**
+ * Gets the additional language.
+ *
+ * @return the additional language
+ */
+ public String getAdditionalLanguage() {
+ return additionalLanguage;
+ }
+
+ /**
+ * Sets the additional language.
+ *
+ * @param additionalLanguage the new additional language
+ */
+ public void setAdditionalLanguage(String additionalLanguage) {
+ this.additionalLanguage = additionalLanguage;
+ }
+
+
+ /**
+ * Decode content.
+ *
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ protected void decodeContent() throws IOException, EncodingFormatException{
+
+ if (content == null || content.length == 0 ) return;
+
+ issuer = decodeString(content, 0 , 4);
+
+ identifier = decodeString(content, 4 , 20);
+
+ String issuingDateString = decodeString(content, 24 , 12);
+
+ String flagsString = decodeString(content,36 , 1);
+
+ language = decodeString(content, 37 , 2);
+
+ additionalLanguage = decodeString(content,39 , 2);
+
+
+ try {
+ flags = Integer.parseInt(flagsString);
+ } catch (Exception e) {
+ flags = 9;
+ }
+
+ // date format "DDMMYYYYHHMM"
+ SimpleDateFormat formatter = new SimpleDateFormat("ddMMyyyyhhmm");
+ try {
+ issuingDate = formatter.parse(issuingDateString);
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+
+ /**
+ * Decode string.
+ *
+ * @param byteData the byte data
+ * @param offset the offset
+ * @param length the length
+ * @return the string
+ */
+ private String decodeString(byte[] byteData, int offset, int length) {
+
+ char[] chars = new char[length];
+
+ for (int i = 0; i < length && i < byteData.length;i++) {
+ byte byteValue = byteData[offset + i];
+ if (byteValue == '\n') {
+ byteValue = ' ';
+ }
+ chars[i] = (char) byteValue;
+ }
+
+ return String.copyValueOf(chars);
+ }
+
+ /**
+ * Encode content.
+ *
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ protected void encodeContent() throws IOException, EncodingFormatException {
+
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+
+ String issuerElement = String.format("%4s", this.issuer);
+
+ String idElement = String.format("%20s", this.identifier);
+
+ //DDMMYYYYHHMM"
+ Calendar now = Calendar.getInstance();
+
+ // issuing date can be in the ticket or in the header
+ if (this.issuingDate != null) {
+ now.setTime(this.issuingDate);
+ }
+
+ String timeElement = String.format("%02d%02d%04d%02d%02d",
+ now.get(Calendar.DAY_OF_MONTH),
+ now.get(Calendar.MONTH),
+ now.get(Calendar.YEAR),
+ now.get(Calendar.HOUR),
+ now.get(Calendar.MINUTE));
+
+
+ String flagsElement = String.format("%01d",this.flags);
+
+
+ String languageElement = String.format("%2s%2s" ,this.language, this.additionalLanguage);
+
+ try {
+
+ outputStream.write(issuerElement.getBytes());
+ outputStream.write(idElement.getBytes());
+ outputStream.write(timeElement.getBytes());
+
+ outputStream.write(flagsElement.getBytes());
+ outputStream.write(languageElement.getBytes());
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ super.setData(outputStream.toByteArray());
+
+
+ }
+
+}
diff --git a/src/org/uic/barcode/staticFrame/UTLAYDataRecord.java b/src/org/uic/barcode/staticFrame/UTLAYDataRecord.java
new file mode 100644
index 0000000..2e9a2dc
--- /dev/null
+++ b/src/org/uic/barcode/staticFrame/UTLAYDataRecord.java
@@ -0,0 +1,272 @@
+/*
+ *
+ */
+package org.uic.barcode.staticFrame;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+
+import org.uic.barcode.staticHeader.ticketLayoutBarcode.FormatType;
+import org.uic.barcode.staticHeader.ticketLayoutBarcode.LayoutElement;
+import org.uic.barcode.staticHeader.ticketLayoutBarcode.TicketLayout;
+import org.uic.ticket.EncodingFormatException;
+
+/**
+ * The Class UTLAYDataRecord implements a bar code data record containing the ticket layout.
+ */
+public class UTLAYDataRecord extends DataRecord {
+
+ /** The ticket layout. */
+ private TicketLayout layout;
+
+ /**
+ * Instantiates a new empty UTLAY data record.
+ */
+ public UTLAYDataRecord() {
+ super("U_TLAY","01");
+ }
+
+ /**
+ * Decode utf-8 string.
+ *
+ * @param byteData the byte data
+ * @param offset the offset
+ * @param length the length
+ * @return the string
+ * @throws UnsupportedEncodingException the unsupported encoding exception
+ */
+ private static String decodeUtf8String(byte[] byteData, int offset, int length) throws UnsupportedEncodingException {
+ byte[] bytes = new byte[length];
+ for (int i = 0; i < length; i++){
+ bytes[i] = byteData[i + offset];
+ }
+ return StandardCharsets.UTF_8.decode(ByteBuffer.wrap(bytes)).toString();
+ }
+
+ /**
+ * Decode string.
+ *
+ * @param byteData the byte data
+ * @param offset the offset
+ * @param length the length
+ * @return the string
+ */
+ private static String decodeString(byte[] byteData, int offset, int length) {
+ byte[] bytes = new byte[length];
+ for (int i = 0; i < length; i++){
+ bytes[i] = byteData[i + offset];
+ }
+ return StandardCharsets.ISO_8859_1.decode(ByteBuffer.wrap(bytes)).toString();
+ }
+
+ /**
+ * Encode utf-8.
+ *
+ * @param value the value
+ * @return the byte[]
+ */
+ private static byte[] encodeUtf8(String value) {
+
+ try {
+ return value.getBytes("UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException("UTF8 String encoding wrong!",e);
+ }
+ }
+
+ /**
+ * To string.
+ *
+ * @return the string
+ */
+ public String toString() {
+
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("TLB: ").append(layout.getLayoutStandard()).append('\n');
+
+ for (LayoutElement e : layout.getElements()){
+ sb.append("column: ").append(e.getColumn()).append(" - ");
+ sb.append("line: ").append(e.getLine()).append(" - ");
+ sb.append("width: ").append(e.getWidth()).append(" - ");
+ sb.append("heigth: ").append(e.getHeight()).append(" - ");
+ sb.append("text: ").append(e.getText()).append(" - ");
+ sb.append("format: ").append(e.getFormat().toString()).append('\n');
+ }
+
+ return sb.toString();
+
+ }
+
+ /**
+ * Decode content.
+ *
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ @Override
+ protected void decodeContent() throws IOException, EncodingFormatException {
+
+ layout = new TicketLayout();
+
+ if (content == null || content.length == 0 ) return;
+
+ int offset = 0;
+
+ String layoutType = decodeString(content, offset , 4);
+ layout.setLayoutStandard(layoutType);
+ offset = offset + 4;
+
+ String numberValue = decodeString(content, offset , 4);
+ offset = offset + 4;
+
+ int elements = 0;
+ try {
+ elements = Integer.parseInt(numberValue);
+ } catch(NumberFormatException e){
+ //Do Nothing
+ }
+
+ int remainingBytes = content.length - offset;
+
+ for (int i = 0; i < elements && remainingBytes > 0 ;i++){
+
+ String lineValue = decodeString(content, offset , 2);
+ offset = offset + 2;
+ int line = 0;
+ try {
+ line = Integer.parseInt(lineValue);
+ } catch(NumberFormatException e){
+ //Do Nothing
+ }
+ String columnValue = decodeString(content, offset , 2);
+ offset = offset + 2;
+ int column = 0;
+ try {
+ column = Integer.parseInt(columnValue);
+ } catch(NumberFormatException e){
+ //Do Nothing
+ }
+ String heightValue = decodeString(content, offset , 2);
+ offset = offset + 2;
+ int height = 0;
+ try {
+ height = Integer.parseInt(heightValue);
+ } catch(NumberFormatException e){
+ //Do Nothing
+ }
+ String widthValue = decodeString(content, offset , 2);
+ offset = offset + 2;
+ int width = 0;
+ try {
+ width = Integer.parseInt(widthValue);
+ } catch(NumberFormatException e){
+ //Do Nothing
+ }
+ String formatValue = decodeString(content, offset , 1);
+ offset = offset + 1;
+ int format = 0;
+ try {
+ format = Integer.parseInt(formatValue);
+ } catch(NumberFormatException e){
+ //Do Nothing
+ }
+ String lengthValue = decodeString(content, offset , 4);
+ offset = offset + 4;
+ int length = 0;
+ try {
+ length = Integer.parseInt(lengthValue);
+ } catch(NumberFormatException e){
+ //Do Nothing
+ }
+
+ String text;
+ try {
+ text = decodeUtf8String(content, offset ,length);
+ } catch (UnsupportedEncodingException e) {
+ text = "unsupported character set";
+ }
+ offset = offset + length;
+
+ LayoutElement layoutElement = new LayoutElement();
+
+ layoutElement.setColumn(column);
+ layoutElement.setLine(line);
+ layoutElement.setHeight(height);
+ layoutElement.setWidth(width);
+ layoutElement.setText(text);
+
+ layoutElement.setFormat(FormatType.values()[format]);
+
+ layout.addLayoutElement(layoutElement);
+
+ }
+
+ }
+
+ /**
+ * Encode content.
+ *
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ @Override
+ protected void encodeContent() throws IOException, EncodingFormatException {
+
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+
+ if (layout == null || layout.getElements() == null || layout.getElements().isEmpty()) {
+ return;
+ }
+
+ //number of text elements
+ String numberOfFields = String.format("%04d",layout.getElements().size());
+
+ outputStream.write(layout.getLayoutStandard().getBytes());
+ outputStream.write(numberOfFields.getBytes());
+
+ for (LayoutElement e : layout.getElements()){
+
+ String line = String.format("%02d",e.getLine());
+ String column = String.format("%02d",e.getColumn());
+ String heigth = String.format("%02d",e.getHeight());
+ String width = String.format("%02d",e.getWidth());
+ String format = String.format("%01d",e.getFormat().ordinal());
+ String size = String.format("%04d",encodeUtf8(e.getText()).length);
+
+ outputStream.write(line.getBytes());
+ outputStream.write(column.getBytes());
+ outputStream.write(heigth.getBytes());
+ outputStream.write(width.getBytes());
+ outputStream.write(format.getBytes());
+ outputStream.write(size.getBytes());
+ outputStream.write(encodeUtf8(e.getText()));
+
+ }
+
+ content = outputStream.toByteArray();
+ }
+
+ /**
+ * Sets the layout.
+ *
+ * @param layout the new layout
+ */
+ public void setLayout(TicketLayout layout) {
+ this.layout = layout;
+ }
+
+
+ /**
+ * Gets the layout.
+ *
+ * @return the layout
+ */
+ public TicketLayout getLayout() {
+ return layout;
+ }
+
+}
diff --git a/src/org/uic/barcode/staticFrame/package.html b/src/org/uic/barcode/staticFrame/package.html
new file mode 100644
index 0000000..b76540b
--- /dev/null
+++ b/src/org/uic/barcode/staticFrame/package.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head></head>
+<body>
+
+<h1>static bar code header frame</h1>
+
+<p>Provides an implementation of the static bar code header frame specified in UIC IRS 90918-9 including:<br/><br/>
+
+<ul>
+ <li> encoding of the data for creating a bar code</li>
+ <li> decoding of data from bar code content</li>
+ <li> support for the additional header information required with the TLB content</li>
+ <li> support for the TLB bar code content</li>
+ <li> support for the FCB content (using the UIC FCB implementation)</li>
+ <li> support for bilaterally on unilaterally defined additional content </li>
+</ul>
+</p>
+
+</body>
+</html> \ No newline at end of file