OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Compare Revisions

Regard whitespace Rev 180 → Rev 181

/trunk/Modules/Module Label/.settings/org.eclipse.jdt.core.prefs
New file
0,0 → 1,7
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.8
/trunk/Modules/Module Label/.classpath
1,7 → 1,7
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry combineaccessrules="false" kind="src" path="/OpenConcerto"/>
<classpathentry kind="output" path="bin"/>
</classpath>
/trunk/Modules/Module Label/lib/barcode4j-2.1.0.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/Modules/Module Label/lib/barcode4j-2.1.0.jar
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/Modules/Module Label/lib/jbarcode-0.2.8.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/Modules/Module Label/lib/jbarcode-0.2.8.jar
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/logo.png
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/logo.png
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/filter/LimitDocumentFilter.java
New file
0,0 → 1,30
package org.openconcerto.modules.label.filter;
 
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
 
public class LimitDocumentFilter extends DocumentFilter {
 
private int limit;
 
public LimitDocumentFilter(int limit) {
if (limit <= 0) {
throw new IllegalArgumentException("Limit can not be <= 0");
}
this.limit = limit;
}
 
@Override
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
int currentLength = fb.getDocument().getLength();
int overLimit = (currentLength + text.length()) - limit - length;
if (overLimit > 0) {
text = text.substring(0, text.length() - overLimit);
}
if (text.length() > 0) {
super.replace(fb, offset, length, text, attrs);
}
}
 
}
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/filter/NumberOfProductDocumentFilter.java
New file
0,0 → 1,47
package org.openconcerto.modules.label.filter;
 
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
 
public class NumberOfProductDocumentFilter extends DocumentFilter {
private boolean isValid(String testText) {
if (testText.length() > 8) {
return false;
}
if (testText.isEmpty()) {
return true;
}
int intValue = 0;
try {
intValue = Integer.parseInt(testText.trim());
} catch (NumberFormatException e) {
return false;
}
if (intValue < 1 || intValue > 99999999) {
return false;
}
return true;
}
 
@Override
public void insertString(FilterBypass fb, int offset, String text, AttributeSet attr) throws BadLocationException {
StringBuilder sb = new StringBuilder();
sb.append(fb.getDocument().getText(0, fb.getDocument().getLength()));
sb.insert(offset, text);
if (isValid(sb.toString())) {
super.insertString(fb, offset, text, attr);
}
}
 
@Override
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
StringBuilder sb = new StringBuilder();
sb.append(fb.getDocument().getText(0, fb.getDocument().getLength()));
int end = offset + length;
sb.replace(offset, end, text);
if (isValid(sb.toString())) {
super.replace(fb, offset, length, text, attrs);
}
}
}
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/filter/BatchDocumentFilter.java
New file
0,0 → 1,23
package org.openconcerto.modules.label.filter;
 
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
 
import org.openconcerto.modules.label.ISO646;
 
public class BatchDocumentFilter extends LimitDocumentFilter {
 
public BatchDocumentFilter() {
super(20);
}
 
@Override
public void insertString(FilterBypass fb, int offset, String text, AttributeSet attr) throws BadLocationException {
super.insertString(fb, offset, ISO646.clean(text), attr);
}
 
@Override
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
super.replace(fb, offset, length, ISO646.clean(text), attrs);
}
}
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/filter/EANDocumentFilter.java
New file
0,0 → 1,36
package org.openconcerto.modules.label.filter;
 
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
 
public class EANDocumentFilter extends LimitDocumentFilter {
 
public EANDocumentFilter() {
super(14);
}
 
@Override
public void insertString(FilterBypass fb, int offset, String text, AttributeSet attr) throws BadLocationException {
super.insertString(fb, offset, clean(text), attr);
}
 
@Override
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
super.replace(fb, offset, length, clean(text), attrs);
}
 
public static String clean(String s) {
final int length = s.length();
final StringBuilder b = new StringBuilder(length);
for (int i = 0; i < length; i++) {
final char charAt = s.charAt(i);
if (Character.isDigit(charAt)) {
b.append(s.charAt(i));
} else {
b.append('0');
}
}
return b.toString();
}
 
}
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/filter/ISO646DocumentFilter.java
New file
0,0 → 1,19
package org.openconcerto.modules.label.filter;
 
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
 
import org.openconcerto.modules.label.ISO646;
 
public class ISO646DocumentFilter extends DocumentFilter {
@Override
public void insertString(FilterBypass fb, int offset, String text, AttributeSet attr) throws BadLocationException {
super.insertString(fb, offset, ISO646.clean(text), attr);
}
 
@Override
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
super.replace(fb, offset, length, ISO646.clean(text), attrs);
}
}
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/GS1Frame.java
New file
0,0 → 1,1049
package org.openconcerto.modules.label;
 
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;
 
import javax.print.DocFlavor;
import javax.print.DocPrintJob;
import javax.print.PrintService;
import javax.print.SimpleDoc;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JSeparator;
import javax.swing.JSpinner;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.AbstractDocument;
import javax.swing.text.PlainDocument;
 
import org.openconcerto.modules.label.filter.BatchDocumentFilter;
import org.openconcerto.modules.label.filter.EANDocumentFilter;
import org.openconcerto.modules.label.filter.LimitDocumentFilter;
import org.openconcerto.modules.label.filter.NumberOfProductDocumentFilter;
import org.openconcerto.modules.label.gs1.GS1AIElements;
import org.openconcerto.modules.label.gs1.GS1Util;
import org.openconcerto.ui.JDate;
 
import uk.org.okapibarcode.backend.Code128;
import uk.org.okapibarcode.backend.DataMatrix;
import uk.org.okapibarcode.backend.DataMatrix.ForceMode;
import uk.org.okapibarcode.backend.Ean;
import uk.org.okapibarcode.backend.Symbol;
import uk.org.okapibarcode.output.Java2DRenderer;
 
public class GS1Frame extends JFrame {
 
private final SimpleDateFormat dfDate = new SimpleDateFormat("yyMMdd");
private final JTextField textSupplier = new JTextField(20);
private final JTextField textName = new JTextField(20);
private final JTextField textEAN = new JTextField(14);
private final JTextField textBatch = new JTextField(20);
private final JSpinner portZPL = new JSpinner(new SpinnerNumberModel(9100, 24, 10000, 1));
private final JLabel labelEan = new JLabel("EAN 13/14", SwingConstants.RIGHT);
private final JTextField textNumberOfProducts = new JTextField(12);
private final JSpinner labelsSpinner = new JSpinner(new SpinnerNumberModel(5, 0, 9000, 1));
private final JTextField labelBarCode = new JTextField(64);
private final JDate dateDLUODLC = new JDate();
private final JRadioButton b1 = new JRadioButton("DDM/DLUO");
private final JCheckBox checkIgnoreMargins = new JCheckBox("Ignorer les marges de l'imprimante");
private final JRadioButton twoPerPage = new JRadioButton("2");
private final JRadioButton fourPerPage = new JRadioButton("4");
private final JRadioButton radioZPLUSB = new JRadioButton("imprimante locale");
private final JTextField textIP = new JTextField(20);
private final JTabbedPane tabs = new JTabbedPane();
private LabelPanel labelPanel;
private BufferedImage barcodeImage = null;
private BufferedImage barcodeImageBatch = null;
private BufferedImage barcodeDatamatrix = null;
private final Properties properties = new Properties();
 
public GS1Frame() {
super("Code à barres GS1-128");
 
final File file = new File("openconcerto-gs1.properties");
if (file.exists()) {
try {
properties.load(new FileInputStream(file));
} catch (IOException e) {
e.printStackTrace();
}
}
 
final JSplitPane split = new JSplitPane();
final JPanel left = createLeftContent();
final JComponent right = createRightContent();
split.setLeftComponent(left);
split.setRightComponent(right);
this.setContentPane(split);
split.setEnabled(false);
try {
updateList();
} catch (Exception e) {
e.printStackTrace();
}
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
try {
saveProperties();
} catch (IOException e1) {
e1.printStackTrace();
}
dispose();
}
});
}
 
public void saveProperties() throws FileNotFoundException, IOException {
properties.setProperty("supplier", this.textSupplier.getText());
properties.setProperty("product", this.textName.getText());
properties.setProperty("ean", this.textEAN.getText());
properties.setProperty("nbOfProdcuts", this.textNumberOfProducts.getText());
properties.setProperty("batch", this.textBatch.getText());
final Date value = this.dateDLUODLC.getValue();
if (value != null) {
properties.setProperty("sellByDate", String.valueOf(value.getTime()));
} else {
properties.setProperty("sellByDate", "");
}
properties.setProperty("sellBy", b1.isSelected() ? "true" : "false");
properties.setProperty("nbLabels", this.labelsSpinner.getValue().toString());
properties.setProperty("labelsPerPage", fourPerPage.isSelected() ? "4" : "2");
properties.setProperty("ignoreMargin", checkIgnoreMargins.isSelected() ? "true" : "false");
properties.setProperty("usbPrinter", this.radioZPLUSB.isSelected() ? "true" : "false");
properties.setProperty("zplIP", this.textIP.getText());
properties.setProperty("zplPort", this.portZPL.getValue().toString());
properties.setProperty("tab", String.valueOf(tabs.getSelectedIndex()));
 
final FileOutputStream out = new FileOutputStream("openconcerto-gs1.properties");
properties.store(out, "");
out.flush();
out.close();
}
 
boolean isEANValid() {
final String ean = this.textEAN.getText().trim();
if (ean.length() < 13) {
return false;
}
final char cd1 = Ean.calcDigit(ean.substring(0, ean.length() - 1));
final char cd2 = ean.charAt(ean.length() - 1);
return (cd1 == cd2);
}
 
private void updateList() throws Exception {
this.barcodeImage = null;
this.barcodeImageBatch = null;
this.barcodeDatamatrix = null;
this.labelPanel.setList(new ArrayList<>());
this.labelBarCode.setText("");
// Check EAN
String ean = this.textEAN.getText().trim();
if (!isEANValid()) {
this.labelEan.setForeground(Color.RED);
} else {
this.labelEan.setForeground(Color.BLACK);
}
if (ean.length() == 13) {
this.labelEan.setText("EAN 13");
} else if (ean.length() == 14) {
this.labelEan.setText("EAN 14");
} else {
this.labelEan.setText("EAN invalide");
this.labelEan.setForeground(Color.RED);
}
 
if (!isEANValid()) {
return;
}
 
int n = ((Number) this.labelsSpinner.getValue()).intValue();
this.labelBarCode.setText(this.getGS1().formatHumanReadable());
this.labelBarCode.setEditable(false);
GS1Label l = new GS1Label(this.textSupplier.getText(), this.textName.getText(), this.getGS1());
List<GS1Label> list = new ArrayList<>(n);
for (int i = 0; i < n; i++) {
list.add(l);
}
this.labelPanel.setList(list);
 
// Configure the barcode generator
// adjust barcode width here
// 32mm de hatu minimum
// 0,495mm et 1,016mm pour largeur de barre
 
GS1Util util = new GS1Util();
 
{
final GS1AIElements gs128 = getGS128();
Code128 dataMatrix = new Code128();
dataMatrix.setDataType(Symbol.DataType.GS1);
dataMatrix.setBarHeight(70);
dataMatrix.setContent(util.formatDataMatrix(gs128));
int magnification = 5;
int borderSize = 3;
this.barcodeImage = new BufferedImage((dataMatrix.getWidth() * magnification) + (2 * borderSize), (dataMatrix.getHeight() * magnification) + (2 * borderSize), BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = this.barcodeImage.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, (dataMatrix.getWidth() * magnification) + (2 * borderSize), (dataMatrix.getHeight() * magnification) + (2 * borderSize));
Java2DRenderer renderer = new Java2DRenderer(g2d, magnification, Color.WHITE, Color.BLACK);
renderer.render(dataMatrix);
}
final GS1AIElements gs128b = getGS128Batch();
if (!gs128b.isEmpty()) {
Code128 dataMatrix = new Code128();
dataMatrix.setDataType(Symbol.DataType.GS1);
dataMatrix.setBarHeight(90);
dataMatrix.setContent(util.formatDataMatrix(gs128b));
int magnification = 4;
int borderSize = 3;
this.barcodeImageBatch = new BufferedImage((dataMatrix.getWidth() * magnification) + (2 * borderSize), (int) (dataMatrix.getHeight() * magnification) + (2 * borderSize),
BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = this.barcodeImageBatch.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, (dataMatrix.getWidth() * magnification) + (2 * borderSize), (dataMatrix.getHeight() * magnification) + (2 * borderSize));
Java2DRenderer renderer = new Java2DRenderer(g2d, magnification, Color.WHITE, Color.BLACK);
renderer.render(dataMatrix);
}
 
{
String s = util.formatDataMatrix(getGS1());
DataMatrix dataMatrix = new DataMatrix();
dataMatrix.setDataType(Symbol.DataType.GS1);
dataMatrix.setForceMode(ForceMode.SQUARE);
dataMatrix.setContent(s);
int magnification = 30;
int borderSize = 3;
this.barcodeDatamatrix = new BufferedImage((dataMatrix.getWidth() * magnification) + (2 * borderSize), (dataMatrix.getHeight() * magnification) + (2 * borderSize),
BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = this.barcodeDatamatrix.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, (dataMatrix.getWidth() * magnification) + (2 * borderSize), (dataMatrix.getHeight() * magnification) + (2 * borderSize));
Java2DRenderer renderer = new Java2DRenderer(g2d, magnification, Color.WHITE, Color.BLACK);
renderer.render(dataMatrix);
}
 
// ImageIO.write(barcodeImage, "PNG", new File("barcode.png"));
// ImageIO.write(barcodeImageBatch, "PNG", new File("barcode2.png"));
// ImageIO.write(barcodeDatamatrix, "PNG", new File("datamatrix.png"));
 
}
 
private JPanel createLeftContent() {
final JPanel p = new JPanel();
p.setLayout(new GridBagLayout());
final GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH;
c.gridy = 0;
c.gridx = 0;
c.gridwidth = 3;
p.add(createLogo(), c);
c.fill = GridBagConstraints.HORIZONTAL;
c.gridwidth = 1;
c.gridy++;
c.anchor = GridBagConstraints.WEST;
c.insets = new Insets(2, 3, 2, 2);
//
c.weightx = 0;
c.gridwidth = 1;
p.add(new JLabel("Fournisseur / fabricant", SwingConstants.RIGHT), c);
c.gridx++;
c.weightx = 1;
c.gridwidth = 2;
this.textSupplier.setText(properties.getProperty("supplier", ""));
((AbstractDocument) this.textSupplier.getDocument()).setDocumentFilter(new LimitDocumentFilter(22));
p.add(this.textSupplier, c);
 
//
c.gridx = 0;
c.gridy++;
c.weightx = 0;
c.gridwidth = 1;
p.add(new JLabel("Désignation du produit", SwingConstants.RIGHT), c);
c.gridx++;
c.weightx = 1;
c.gridwidth = 2;
this.textName.setText(properties.getProperty("product", "Biscuit Extra"));
((AbstractDocument) this.textName.getDocument()).setDocumentFilter(new LimitDocumentFilter(22));
p.add(this.textName, c);
 
// EAN 13/14
c.gridx = 0;
c.gridy++;
c.weightx = 0;
c.gridwidth = 1;
p.add(this.labelEan, c);
c.gridx++;
c.weightx = 1;
c.gridwidth = 2;
this.textEAN.setText(properties.getProperty("ean", "7612345678900"));
((AbstractDocument) this.textEAN.getDocument()).setDocumentFilter(new EANDocumentFilter());
p.add(this.textEAN, c);
// nb de produit
c.gridx = 0;
c.gridy++;
c.weightx = 0;
c.gridwidth = 1;
p.add(new JLabel("Nombre par carton", SwingConstants.RIGHT), c);
c.gridx++;
c.weightx = 1;
c.gridwidth = 2;
c.fill = GridBagConstraints.NONE;
PlainDocument doc = (PlainDocument) this.textNumberOfProducts.getDocument();
doc.setDocumentFilter(new NumberOfProductDocumentFilter());
this.textNumberOfProducts.setText(properties.getProperty("nbOfProdcuts", "10"));
p.add(this.textNumberOfProducts, c);
// Numéro de lot
c.gridx = 0;
c.gridy++;
c.weightx = 0;
c.gridwidth = 1;
c.fill = GridBagConstraints.HORIZONTAL;
p.add(new JLabel("Lot", SwingConstants.RIGHT), c);
c.gridx++;
c.weightx = 1;
c.gridwidth = 2;
((AbstractDocument) this.textBatch.getDocument()).setDocumentFilter(new BatchDocumentFilter());
this.textBatch.setText(properties.getProperty("batch", ""));
p.add(this.textBatch, c);
// DLC
c.gridx = 1;
c.gridy++;
c.weightx = 0;
c.gridwidth = 1;
 
p.add(this.b1, c);
c.gridx++;
c.weightx = 1;
this.dateDLUODLC.setFormat(new SimpleDateFormat("dd/MM/yyyy"));
if (!properties.getProperty("sellByDate", "").isEmpty()) {
this.dateDLUODLC.setDateInMillis(Long.parseLong(properties.getProperty("sellByDate")));
}
p.add(this.dateDLUODLC, c);
// DLUO
c.gridx = 1;
c.gridy++;
c.weightx = 0;
JRadioButton b2 = new JRadioButton("DLC");
p.add(b2, c);
c.gridx++;
c.weightx = 1;
 
this.b1.setSelected(properties.getProperty("sellBy", "true").equals("true"));
b2.setSelected(!properties.getProperty("sellBy", "true").equals("true"));
// Barcode
c.gridx = 0;
c.gridy++;
c.weightx = 0;
c.gridwidth = 3;
p.add(new JLabel("Code GS1 complet de l'étiquette", SwingConstants.LEFT), c);
c.gridy++;
c.weightx = 1;
this.labelBarCode.setFont(this.textBatch.getFont());
this.labelBarCode.setEnabled(false);
p.add(this.labelBarCode, c);
 
// Nombre d'étiquettes
c.gridx = 0;
c.gridy++;
c.weightx = 0;
c.gridwidth = 1;
p.add(new JLabel("Nombre d'étiquettes", SwingConstants.RIGHT), c);
c.gridx++;
c.weightx = 0;
c.fill = GridBagConstraints.NONE;
 
this.labelsSpinner.setValue(Integer.parseInt(properties.getProperty("nbLabels", "4")));
 
p.add(this.labelsSpinner, c);
 
final JPanel spacer = new JPanel();
spacer.setOpaque(false);
c.gridy++;
c.weighty = 1;
p.add(spacer, c);
 
final ButtonGroup gr = new ButtonGroup();
gr.add(this.b1);
gr.add(b2);
 
// Listeners
 
final ChangeListener changeListener = new ChangeListener() {
 
@Override
public void stateChanged(ChangeEvent e) {
try {
updateList();
} catch (Exception ex) {
ex.printStackTrace();
}
}
};
this.labelsSpinner.addChangeListener(changeListener);
 
final DocumentListener dListener = new DocumentListener() {
 
@Override
public void removeUpdate(DocumentEvent e) {
changedUpdate(e);
}
 
@Override
public void insertUpdate(DocumentEvent e) {
changedUpdate(e);
 
}
 
@Override
public void changedUpdate(DocumentEvent e) {
try {
updateList();
} catch (Exception ex) {
ex.printStackTrace();
}
}
};
this.textSupplier.getDocument().addDocumentListener(dListener);
this.textName.getDocument().addDocumentListener(dListener);
this.textEAN.getDocument().addDocumentListener(dListener);
this.textNumberOfProducts.getDocument().addDocumentListener(dListener);
this.textBatch.getDocument().addDocumentListener(dListener);
 
this.dateDLUODLC.addValueListener(new PropertyChangeListener() {
 
@Override
public void propertyChange(PropertyChangeEvent evt) {
try {
updateList();
} catch (Exception e) {
e.printStackTrace();
}
 
}
});
this.b1.addChangeListener(changeListener);
b2.addChangeListener(changeListener);
return p;
}
 
private Component createLogo() {
final Image i1 = new ImageIcon(this.getClass().getResource("logo.png")).getImage();
final Image i2 = new ImageIcon(this.getClass().getResource("oc-qrcode.png")).getImage();
return new JComponent() {
@Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
g.drawImage(i1, 0, 3, null);
g.drawImage(i2, getWidth() - i2.getWidth(null), 0, null);
g.setColor(Color.GRAY);
g.drawLine(0, 58, getWidth(), 58);
}
 
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 59);
}
};
}
 
private JComponent createRightContent() {
 
final JPanel panelA4 = new JPanel();
panelA4.setOpaque(false);
panelA4.setLayout(new GridBagLayout());
final GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.insets = new Insets(2, 3, 2, 2);
c.gridx = 0;
c.gridy = 0;
panelA4.add(createToolBar(), c);
c.gridy++;
// panelA4.add(createToolBar2(), c);
// c.gridy++;
c.insets = new Insets(0, 0, 0, 0);
panelA4.add(new JSeparator(JSeparator.HORIZONTAL), c);
c.gridy++;
c.weightx = 1;
c.weighty = 1;
c.fill = GridBagConstraints.BOTH;
final int columnCount = fourPerPage.isSelected() ? 2 : 1;
this.labelPanel = new LabelPanel(new ArrayList<Label>(), 2, columnCount, createRenderer()) {
@Override
public Dimension getPreferredSize() {
return new Dimension(190 * 3, 280 * 3);
}
};
this.labelPanel.setIgnoreMargins(checkIgnoreMargins.isSelected());
panelA4.add(this.labelPanel, c);
tabs.addTab("Feuilles A4", panelA4);
tabs.addTab("Imprimante ZPL", createZPLPanel());
tabs.setSelectedIndex(Integer.parseInt(properties.getProperty("tab", "0")));
return tabs;
}
 
private Component createZPLPanel() {
final JPanel panelZPL = new JPanel();
panelZPL.setOpaque(false);
panelZPL.setLayout(new GridBagLayout());
final GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.insets = new Insets(2, 3, 2, 2);
c.gridx = 0;
c.gridy = 0;
c.weightx = 0;
c.gridwidth = 1;
c.anchor = GridBagConstraints.EAST;
 
radioZPLUSB.setOpaque(false);
panelZPL.add(radioZPLUSB, c);
c.gridx += 5;
c.weightx = 1;
c.fill = GridBagConstraints.NONE;
c.insets = new Insets(4, 3, 2, 4);
final JButton buttonPrintZPL = new JButton("Imprimer");
buttonPrintZPL.setOpaque(false);
panelZPL.add(buttonPrintZPL, c);
c.insets = new Insets(2, 3, 2, 2);
c.gridx = 0;
c.gridy++;
c.weightx = 0;
c.fill = GridBagConstraints.HORIZONTAL;
final JRadioButton radioZPLNetwork = new JRadioButton("imprimante réseau");
radioZPLNetwork.setOpaque(false);
panelZPL.add(radioZPLNetwork, c);
c.gridx++;
panelZPL.add(new JLabel("Adresse"), c);
c.gridx++;
 
textIP.setText(properties.getProperty("zplIP", "192.168.1.50"));
panelZPL.add(textIP, c);
c.gridx++;
panelZPL.add(new JLabel("Port"), c);
this.portZPL.setValue(Integer.parseInt(properties.getProperty("zplPort", "9100")));
c.gridx++;
panelZPL.add(this.portZPL, c);
c.gridx = 0;
c.gridy++;
c.gridwidth = 6;
 
radioZPLUSB.setSelected(properties.getProperty("usbPrinter", "true").equals("true"));
radioZPLNetwork.setSelected(!properties.getProperty("usbPrinter", "true").equals("true"));
 
final ButtonGroup g = new ButtonGroup();
g.add(radioZPLUSB);
g.add(radioZPLNetwork);
 
c.anchor = GridBagConstraints.NORTHWEST;
panelZPL.add(new JLabel("L'impression nécessite une imprimante d'étiquettes compatible ZPL (Zebra ou autre)."), c);
c.gridy++;
c.weighty = 1;
panelZPL.add(new JLabel("Vous pouvez paramétrer l'impression par défaut (largeur 6\" à 203 dpi) dans le fichier zpl.txt ."), c);
buttonPrintZPL.addActionListener(new ActionListener() {
 
@Override
public void actionPerformed(ActionEvent e) {
if (!isEANValid()) {
JOptionPane.showMessageDialog(GS1Frame.this, "EAN invalide");
return;
}
 
final String code = createZPLCode();
System.out.println("ZPL:");
System.out.println(code);
byte[] data = code.getBytes(StandardCharsets.US_ASCII);
if (radioZPLNetwork.isSelected()) {
Socket socket = null;
try {
socket = new Socket(textIP.getText(), ((Number) GS1Frame.this.portZPL.getValue()).intValue());
final DataOutputStream out = new DataOutputStream(socket.getOutputStream());
final int nb = ((Number) labelsSpinner.getValue()).intValue();
for (int i = 0; i < nb; i++) {
out.write(data);
}
} catch (Exception ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(buttonPrintZPL, "Erreur d'impression réseau : " + ex.getMessage());
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
 
}
} else {
try {
final PrinterJob pj1 = PrinterJob.getPrinterJob();
if (pj1.printDialog()) {
final PrintService ps = pj1.getPrintService();
final DocPrintJob pj = ps.createPrintJob();
final SimpleDoc doc = new SimpleDoc(data, DocFlavor.BYTE_ARRAY.AUTOSENSE, null);
final int nb = ((Number) labelsSpinner.getValue()).intValue();
for (int i = 0; i < nb; i++) {
pj.print(doc, null);
}
}
} catch (Exception ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(buttonPrintZPL, "Erreur d'impression locale : " + ex.getMessage());
}
}
}
 
});
 
return panelZPL;
 
}
 
private LabelRenderer createRenderer() {
return new LabelRenderer() {
 
@Override
public void paintLabel(Graphics g, Label label, int x, int y, int gridWith, int gridHeight, float fontSize) {
final Graphics2D g2d = (Graphics2D) g;
g.setColor(Color.BLACK);
g2d.setStroke(new BasicStroke(3));
String str1 = GS1Frame.this.textSupplier.getText().trim();
String str2 = GS1Frame.this.textName.getText();
int pos1 = 26;
if (str1.isEmpty()) {
str1 = GS1Frame.this.textName.getText();
str2 = "";
pos1 += 16;
}
 
final AffineTransform defaultAt = g2d.getTransform();
final AffineTransform at = ((AffineTransform) (g2d.getTransform().clone()));
at.rotate(-Math.PI / 2);
g2d.setTransform(at);
g.setFont(getFont().deriveFont(15f));
final int d1 = 60;
 
final int d2 = 60;
g.drawString(str1, -y - gridHeight + d1, x + pos1);
g.drawString(str2, -y - gridHeight + d2, x + 58);
if (GS1Frame.this.barcodeDatamatrix != null) {
float ratioDatamatrix = 15f;
final int wd = (int) (GS1Frame.this.barcodeDatamatrix.getWidth() / ratioDatamatrix);
final int hd = (int) (GS1Frame.this.barcodeDatamatrix.getHeight() / ratioDatamatrix);
g.drawImage(GS1Frame.this.barcodeDatamatrix, -y - 10 - wd, x + 10, wd, hd, null);
final int w1 = GS1Frame.this.barcodeImage.getWidth() / 4;
final int h1 = GS1Frame.this.barcodeImage.getHeight() / 4;
g.drawImage(GS1Frame.this.barcodeImage, -y + -gridHeight + 60, x + 70, w1, h1, null);
 
}
if (GS1Frame.this.barcodeImageBatch != null) {
// GS1-128 numero de lot
final int w2 = GS1Frame.this.barcodeImageBatch.getWidth() / 4;
final int h2 = GS1Frame.this.barcodeImageBatch.getHeight() / 4;
g.drawImage(GS1Frame.this.barcodeImageBatch, -y - gridHeight + 60, x + 180, w2, h2, null);
}
g2d.setTransform(defaultAt);
 
}
};
 
}
 
public JPanel createToolBar() {
final JPanel toolbar = new JPanel();
toolbar.setOpaque(false);
toolbar.setLayout(new GridBagLayout());
final GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.insets = new Insets(2, 3, 2, 2);
c.gridx = 0;
c.gridy = 0;
c.weightx = 0;
c.gridwidth = 1;
toolbar.add(new JLabel("Etiquettes par page"), c);
c.gridx++;
 
twoPerPage.setOpaque(false);
fourPerPage.setOpaque(false);
final ButtonGroup g = new ButtonGroup();
g.add(twoPerPage);
g.add(fourPerPage);
 
if (properties.getProperty("labelsPerPage", "4").equals("4")) {
fourPerPage.setSelected(true);
} else {
twoPerPage.setSelected(true);
}
 
toolbar.add(twoPerPage, c);
c.gridx++;
toolbar.add(fourPerPage, c);
c.gridx++;
checkIgnoreMargins.setOpaque(false);
checkIgnoreMargins.setSelected(properties.getProperty("ignoreMargin", "false").equals("true"));
c.gridx++;
toolbar.add(checkIgnoreMargins, c);
c.gridx++;
c.weightx = 1;
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.NORTHEAST;
final JButton printButton = new JButton("Imprimer");
printButton.setOpaque(false);
toolbar.add(printButton, c);
c.gridx++;
 
twoPerPage.addActionListener(new ActionListener() {
 
@Override
public void actionPerformed(ActionEvent e) {
if (twoPerPage.isSelected()) {
GS1Frame.this.labelPanel.setColumnCount(1);
} else {
GS1Frame.this.labelPanel.setColumnCount(2);
}
 
}
});
 
fourPerPage.addActionListener(new ActionListener() {
 
@Override
public void actionPerformed(ActionEvent e) {
if (twoPerPage.isSelected()) {
GS1Frame.this.labelPanel.setColumnCount(1);
} else {
GS1Frame.this.labelPanel.setColumnCount(2);
}
 
}
});
 
printButton.addActionListener(new ActionListener() {
 
@Override
public void actionPerformed(ActionEvent e) {
if (!isEANValid()) {
JOptionPane.showMessageDialog(GS1Frame.this, "EAN invalide");
return;
}
 
final PrinterJob job = PrinterJob.getPrinterJob();
job.setPrintable(GS1Frame.this.labelPanel);
boolean ok = job.printDialog();
if (ok) {
try {
job.print();
} catch (PrinterException ex) {
JOptionPane.showMessageDialog(GS1Frame.this, "Print error :" + ex.getMessage());
}
}
 
}
});
checkIgnoreMargins.addActionListener(new ActionListener() {
 
@Override
public void actionPerformed(ActionEvent e) {
GS1Frame.this.labelPanel.setIgnoreMargins(checkIgnoreMargins.isSelected());
 
}
});
return toolbar;
}
 
public JPanel createToolBar2() {
final JPanel toolbar = new JPanel();
toolbar.setOpaque(false);
toolbar.setLayout(new GridBagLayout());
final GridBagConstraints c = new GridBagConstraints();
c.insets = new Insets(2, 3, 2, 2);
c.gridx = 0;
c.gridy = 0;
c.weightx = 0;
c.gridwidth = 1;
c.fill = GridBagConstraints.NONE;
toolbar.add(new JLabel("Marge en haut (mm)"), c);
c.gridx++;
final JSpinner sLines = new JSpinner(new SpinnerNumberModel(0, 0, 200, 1));
toolbar.add(sLines, c);
c.gridx++;
toolbar.add(new JLabel("Marge à gauche (mm)"), c);
c.gridx++;
final JSpinner sColums = new JSpinner(new SpinnerNumberModel(0, 0, 200, 1));
c.weightx = 1;
toolbar.add(sColums, c);
c.gridx++;
 
sLines.addChangeListener(new ChangeListener() {
 
@Override
public void stateChanged(ChangeEvent e) {
final Number n = (Number) sLines.getValue();
if (n != null) {
GS1Frame.this.labelPanel.setTopMargin(n.intValue());
}
}
});
sColums.addChangeListener(new ChangeListener() {
 
@Override
public void stateChanged(ChangeEvent e) {
final Number n = (Number) sColums.getValue();
if (n != null) {
GS1Frame.this.labelPanel.setLeftMargin(n.intValue());
}
}
});
 
return toolbar;
}
 
public String readZPLScript() {
if (new File("zpl.txt").exists()) {
try (InputStream in = new FileInputStream(new File("zpl.txt"))) {
return readAscii(in);
} catch (Exception e) {
e.printStackTrace();
}
} else {
try (InputStream in = GS1Frame.class.getResourceAsStream("default-zpl.txt")) {
return readAscii(in);
} catch (Exception e) {
e.printStackTrace();
}
 
}
return "";
}
 
protected String createZPLCode() {
final String script = readZPLScript();
final BufferedReader reader = new BufferedReader(new StringReader(script));
final StringBuilder builder = new StringBuilder();
final GS1Util u = new GS1Util();
try {
String line = reader.readLine();
while (line != null) {
if (line.contains("XXXXXXXXXX")) {
if (!this.textSupplier.getText().isEmpty()) {
line = line.replace("XXXXXXXXXX", this.textSupplier.getText());
builder.append(line);
builder.append("\r\n");
}
} else if (line.contains("YYYYYYYYYY")) {
line = line.replace("YYYYYYYYYY", this.textName.getText());
builder.append(line);
builder.append("\r\n");
} else if (line.contains("(02)7612345678900(15)201218(37)9999")) {
line = line.replace("(02)7612345678900(15)201218(37)9999", getGS128().formatHumanReadable());
builder.append(line);
builder.append("\r\n");
 
} else if (line.contains("(10)LLLLLLLLL")) {
GS1AIElements batch = getGS128Batch();
if (!batch.isEmpty()) {
line = line.replace("(10)LLLLLLLLL", batch.formatHumanReadable());
builder.append(line);
builder.append("\r\n");
}
} else if (line.contains("OPENCONCERTO")) {
GS1AIElements all = getGS1();
line = line.replace("OPENCONCERTO", u.formatZPL(all));
builder.append(line);
builder.append("\r\n");
 
} else {
builder.append(line);
builder.append("\r\n");
}
 
line = reader.readLine();
}
} catch (Exception e) {
e.printStackTrace();
}
 
return builder.toString();
}
 
private GS1AIElements getGS1() {
final GS1AIElements gs1 = new GS1AIElements();
final String ean = this.textEAN.getText().trim();
if (!ean.isEmpty()) {
if (ean.length() == 13 || (ean.length() == 14 && ean.charAt(0) == '0')) {
if (ean.length() == 13) {
gs1.put("02", '0' + ean);
} else {
gs1.put("02", ean);
}
} else {
gs1.put("01", ean);
}
}
 
if (this.dateDLUODLC.getValue() != null) {
String date = this.dfDate.format(this.dateDLUODLC.getValue());
if (this.b1.isSelected()) {
gs1.put("15", date);
} else {
gs1.put("17", date);
}
}
 
final String nbProducts = this.textNumberOfProducts.getText();
if (!nbProducts.trim().isEmpty()) {
int n = Integer.parseInt(this.textNumberOfProducts.getText());
gs1.put("37", String.valueOf(n));
 
}
final String batch = this.textBatch.getText().trim();
if (!batch.isEmpty()) {
gs1.put("10", batch);
}
 
return gs1;
}
 
private GS1AIElements getGS128Batch() {
final GS1AIElements gs1 = new GS1AIElements();
final String batch = this.textBatch.getText().trim();
if (!batch.isEmpty()) {
gs1.put("10", batch);
}
return gs1;
}
 
private GS1AIElements getGS128() {
final GS1AIElements gs1 = getGS1();
gs1.remove("10");
return gs1;
}
 
public String readAscii(final InputStream in) throws IOException {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
final byte[] buf = new byte[8192];
int length;
while ((length = in.read(buf)) > 0) {
out.write(buf, 0, length);
}
out.flush();
out.close();
return new String(out.toByteArray(), StandardCharsets.US_ASCII);
}
 
static List<Image> frameIcon;
 
public static synchronized List<Image> getFrameIcon() {
if (frameIcon == null) {
frameIcon = new ArrayList<>();
final int[] sizes = { 16, 32, 48, 96 };
for (int i = 0; i < sizes.length; i++) {
int v = sizes[i];
try {
frameIcon.add(new ImageIcon(GS1Frame.class.getResource(v + ".png")).getImage());
} catch (Exception e) {
e.printStackTrace();
}
}
}
return frameIcon;
}
 
public static void main(String[] args) {
 
if (!new File("zpl.txt").exists()) {
final InputStream in = GS1Frame.class.getResourceAsStream("default-zpl.txt");
try (FileOutputStream fOut = new FileOutputStream(new File("zpl.txt"))) {
byte[] buf = new byte[8192];
int length;
while ((length = in.read(buf)) > 0) {
fOut.write(buf, 0, length);
}
fOut.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
SwingUtilities.invokeLater(new Runnable() {
 
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e1) {
e1.printStackTrace();
}
 
final GS1Frame f = new GS1Frame();
f.setIconImages(getFrameIcon());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
f.setLocationRelativeTo(null);
f.setResizable(false);
 
}
});
 
}
 
}
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/LabelRenderer.java
2,8 → 2,6
 
import java.awt.Graphics;
 
import org.openconcerto.sql.model.SQLRowAccessor;
 
public interface LabelRenderer {
public void paintLabel(Graphics g, SQLRowAccessor row, int x, int y, int gridWith, int gridHeight, float fontSize);
public void paintLabel(Graphics g, Label label, int x, int y, int gridWith, int gridHeight, float fontSize);
}
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/gs1/GS1AIElements.java
New file
0,0 → 1,259
package org.openconcerto.modules.label.gs1;
 
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashMap;
 
public class GS1AIElements {
static final HashMap<String, GS1ApplicationIdentifier> GS1_128_AI = new HashMap<>();
static {
GS1_128_AI.put("00", new GS1ApplicationIdentifier(0, 18, false, false));
GS1_128_AI.put("01", new GS1ApplicationIdentifier(0, 14, false, false));
GS1_128_AI.put("02", new GS1ApplicationIdentifier(0, 14, false, false));
GS1_128_AI.put("10", new GS1ApplicationIdentifier(0, 20, true, false));
GS1_128_AI.put("11", new GS1ApplicationIdentifier(0, 6, false, false));
GS1_128_AI.put("12", new GS1ApplicationIdentifier(0, 6, false, false));
GS1_128_AI.put("13", new GS1ApplicationIdentifier(0, 6, false, false));
GS1_128_AI.put("14", new GS1ApplicationIdentifier(0, 6, false, false));
GS1_128_AI.put("15", new GS1ApplicationIdentifier(0, 6, false, false));
GS1_128_AI.put("17", new GS1ApplicationIdentifier(0, 6, false, false));
GS1_128_AI.put("20", new GS1ApplicationIdentifier(0, 2, false, false));
GS1_128_AI.put("21", new GS1ApplicationIdentifier(0, 20, true, false));
GS1_128_AI.put("240", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("241", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("242", new GS1ApplicationIdentifier(0, 6, true, false));
GS1_128_AI.put("250", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("251", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("253", new GS1ApplicationIdentifier(13, 30, true, false));
GS1_128_AI.put("254", new GS1ApplicationIdentifier(0, 20, true, false));
GS1_128_AI.put("255", new GS1ApplicationIdentifier(13, 25, true, false));
GS1_128_AI.put("30", new GS1ApplicationIdentifier(0, 8, true, false));
GS1_128_AI.put("310", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("311", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("312", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("313", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("314", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("315", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("316", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("320", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("321", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("322", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("323", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("324", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("325", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("326", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("327", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("328", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("329", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("330", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("331", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("332", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("333", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("334", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("335", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("336", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("340", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("341", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("342", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("343", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("344", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("345", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("346", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("347", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("348", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("349", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("350", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("351", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("352", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("353", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("354", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("355", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("356", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("357", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("360", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("361", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("362", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("363", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("364", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("365", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("366", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("367", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("368", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("369", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("37", new GS1ApplicationIdentifier(0, 8, true, false));
GS1_128_AI.put("390", new GS1ApplicationIdentifier(0, 15, true, true));
GS1_128_AI.put("391", new GS1ApplicationIdentifier(3, 18, true, true));
GS1_128_AI.put("392", new GS1ApplicationIdentifier(0, 15, true, true));
GS1_128_AI.put("393", new GS1ApplicationIdentifier(3, 18, true, true));
GS1_128_AI.put("400", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("401", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("402", new GS1ApplicationIdentifier(0, 17, false, false));
GS1_128_AI.put("403", new GS1ApplicationIdentifier(3, 30, true, false));
GS1_128_AI.put("410", new GS1ApplicationIdentifier(0, 17, false, false));
GS1_128_AI.put("411", new GS1ApplicationIdentifier(0, 17, false, false));
GS1_128_AI.put("412", new GS1ApplicationIdentifier(0, 17, false, false));
GS1_128_AI.put("413", new GS1ApplicationIdentifier(0, 17, false, false));
GS1_128_AI.put("414", new GS1ApplicationIdentifier(0, 17, false, false));
GS1_128_AI.put("420", new GS1ApplicationIdentifier(0, 20, true, false));
GS1_128_AI.put("421", new GS1ApplicationIdentifier(0, 0, true, false));
GS1_128_AI.put("422", new GS1ApplicationIdentifier(0, 3, false, false));
GS1_128_AI.put("423", new GS1ApplicationIdentifier(3, 15, true, false));
GS1_128_AI.put("424", new GS1ApplicationIdentifier(0, 3, false, false));
GS1_128_AI.put("425", new GS1ApplicationIdentifier(0, 3, false, false));
GS1_128_AI.put("426", new GS1ApplicationIdentifier(0, 3, false, false));
GS1_128_AI.put("7001", new GS1ApplicationIdentifier(0, 13, false, false));
GS1_128_AI.put("7002", new GS1ApplicationIdentifier(0, 30, false, false));
GS1_128_AI.put("7003", new GS1ApplicationIdentifier(0, 10, false, false));
GS1_128_AI.put("7004", new GS1ApplicationIdentifier(0, 4, true, false));
GS1_128_AI.put("8001", new GS1ApplicationIdentifier(0, 14, false, false));
GS1_128_AI.put("8002", new GS1ApplicationIdentifier(0, 20, true, false));
GS1_128_AI.put("8003", new GS1ApplicationIdentifier(14, 30, true, false));
GS1_128_AI.put("8004", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("8005", new GS1ApplicationIdentifier(0, 6, false, false));
GS1_128_AI.put("8006", new GS1ApplicationIdentifier(0, 18, false, false));
GS1_128_AI.put("8007", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("8008", new GS1ApplicationIdentifier(8, 12, true, false));
GS1_128_AI.put("8018", new GS1ApplicationIdentifier(0, 18, false, false));
GS1_128_AI.put("8020", new GS1ApplicationIdentifier(0, 25, true, false));
GS1_128_AI.put("8100", new GS1ApplicationIdentifier(0, 6, false, false));
GS1_128_AI.put("8101", new GS1ApplicationIdentifier(0, 10, false, false));
GS1_128_AI.put("8102", new GS1ApplicationIdentifier(0, 2, false, false));
GS1_128_AI.put("8110", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("8200", new GS1ApplicationIdentifier(0, 70, true, false));
GS1_128_AI.put("90", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("91", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("92", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("93", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("94", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("95", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("96", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("97", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("98", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("99", new GS1ApplicationIdentifier(0, 30, true, false));
}
 
private final ArrayList<String> keysAndValues = new ArrayList<>();
 
public GS1AIElements() {
 
}
 
public boolean isEmpty() {
return this.keysAndValues.isEmpty();
}
 
public boolean containsKey(String key) {
final int size = this.keysAndValues.size();
for (int i = 0; i < size; i += 2) {
if (this.keysAndValues.get(i).equals(key)) {
return true;
}
}
return false;
}
 
public boolean containsValue(String value) {
final int size = this.keysAndValues.size();
for (int i = 1; i < size; i += 2) {
if (this.keysAndValues.get(i).equals(value)) {
return true;
}
}
return false;
}
 
public String getKey(int index) {
return this.keysAndValues.get(index * 2);
}
 
public String getValue(int index) {
return this.keysAndValues.get(1 + index * 2);
}
 
public String get(String key) {
final int size = this.keysAndValues.size();
for (int i = 0; i < size; i += 2) {
if (this.keysAndValues.get(i).equals(key)) {
 
return this.keysAndValues.get(i + 1);
}
}
return null;
}
 
public String put(String key, String value) {
GS1ApplicationIdentifier ai = GS1_128_AI.get(key);
if (ai == null) {
throw new IllegalArgumentException("AI " + key + " unknown");
}
if (ai.variableLength) {
if (value.length() < ai.minLength) {
throw new IllegalArgumentException("AI " + key + " value length must >= " + ai.minLength + " but is " + value.length() + " for value " + value);
}
if (value.length() > ai.length) {
throw new IllegalArgumentException("AI " + key + " value length must be <= " + ai.length + " but is " + value.length() + " for value " + value);
}
} else {
if (value.length() != ai.length) {
throw new IllegalArgumentException("AI " + key + " value length must be " + ai.length + " but is " + value.length() + " for value " + value);
}
}
 
final int size = this.keysAndValues.size();
for (int i = 0; i < size; i += 2) {
if (this.keysAndValues.get(i).equals(key)) {
final String old = this.keysAndValues.get(i + 1);
this.keysAndValues.set(i + 1, value);
return old;
}
}
this.keysAndValues.add(key);
this.keysAndValues.add(value);
return null;
}
 
public String remove(String key) {
final int size = this.keysAndValues.size();
for (int i = 0; i < size; i += 2) {
if (this.keysAndValues.get(i).equals(key)) {
this.keysAndValues.remove(i);
return this.keysAndValues.remove(i);
}
}
return null;
}
 
public void clear() {
this.keysAndValues.clear();
}
 
public int size() {
return this.keysAndValues.size() / 2;
}
 
public void dump(PrintStream out) {
for (int i = 0; i < size(); i++) {
out.print("(");
out.print(getKey(i));
out.print(")");
out.println(getValue(i));
}
out.flush();
}
 
public static GS1ApplicationIdentifier getApplicationIdentifier(String k) {
return GS1_128_AI.get(k);
}
 
public String formatHumanReadable() {
StringBuilder b = new StringBuilder();
int size = size();
for (int i = 0; i < size; i++) {
b.append('(');
b.append(this.getKey(i));
b.append(')');
b.append(this.getValue(i));
}
return b.toString();
}
}
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/gs1/GS1ApplicationIdentifier.java
New file
0,0 → 1,17
package org.openconcerto.modules.label.gs1;
 
public class GS1ApplicationIdentifier {
 
public final int minLength;
public final int length;
public final boolean variableLength;
public final boolean decimalPoint;
 
public GS1ApplicationIdentifier(int minLength, int length, boolean variableLength, boolean decimalPoint) {
this.minLength = minLength;
this.length = length;
this.variableLength = variableLength;
this.decimalPoint = decimalPoint;
}
}
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/gs1/GS1ParseException.java
New file
0,0 → 1,18
package org.openconcerto.modules.label.gs1;
 
public class GS1ParseException extends Exception {
final String ai;
final int errorCode;
final String errorMessage;
 
public GS1ParseException(final String ai, final int errorCode, final String errorMessage) {
this.ai = ai;
this.errorCode = errorCode;
this.errorMessage = errorMessage;
}
 
@Override
public String toString() {
return "AI: " + ai + ", errorCode: " + errorCode + ", errorMessage: " + errorMessage;
}
}
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/gs1/GS1Util.java
New file
0,0 → 1,243
package org.openconcerto.modules.label.gs1;
 
import org.openconcerto.modules.label.ISO646;
 
public class GS1Util {
 
// ISO/IEC 15424 symbology identifiers
// ]E0 : EAN-13, UPC-A, or UPC-E : 13 digits
// ]E1 : Two-digit add-on symbol : 2 digits
// ]E2 : Five-digit add-on symbol : 5 digits
// ]E3 : EAN-13, UPC-A, or UPC-E with add-on symbol : 15 or 18 digits
// ]E4 : EAN-8 : 8 digits
// ]I1 : ITF-14 : 14 digits
// ]C1 : GS1-128 : Standard AI element strings
private static final String GS1_128_SCANNER_PREFIX = "]C1";
// ]e0 : GS1 DataBar : Standard AI element strings
private static final String GS1_DATABAR_SCANNER_PREFIX = "]e0";
// ]e1 : GS1 Composite : Data packet containing the data + an encoded symbol separator
// character.
// ]e2 : GS1 Composite : Data packet containing the data following an escape mechanism
// character.
// ]d2 : GS1 DataMatrix : Standard AI element strings
private static final String GS1_DATAMATRIX_SCANNER_PREFIX = "]d2";
// ]Q3 : GS1 QR Code : Standard AI element strings
private static final String GS1_QRCODE_SCANNER_PREFIX = "]Q3";
// ]J1 : GS1 DotCode : Standard AI element strings
 
static final int ERROR_CODE_INVALID_GS1_SCAN = 0;
static final int ERROR_CODE_UNKNOWN_AI = 1;
static final int ERROR_CODE_INCOMPLETE_AI = 2;
static final int ERROR_CODE_NOT_FOUND_SEPARATOR = 3;
static final int ERROR_CODE_INSUFFICIENT_VALUE_LENGTH = 4;
static final int ERROR_CODE_EXCEEDED_VALUE_LENGTH = 5;
static final int ERROR_CODE_CONVERT_DECIMAL_POINT = 5;
static final int ERROR_CODE_WRONG_DECIMAL_POINT = 6;
static final int ERROR_CODE_CONVERT_DECIMAL_VALUE = 7;
 
public static final char GS_SEPARATOR = '\u001D';
// public static final char FNC1_SEPARATOR = 232;
 
static final int SPACE_SEPARATOR = ' ';
 
int separator;
 
public GS1Util() {
this(GS_SEPARATOR);
}
 
public GS1Util(int separator) {
this.separator = separator;
}
 
public GS1AIElements parseFromScanner(String scan) throws GS1ParseException {
if (scan.startsWith(GS1_DATAMATRIX_SCANNER_PREFIX) || scan.startsWith(GS1_128_SCANNER_PREFIX) || scan.startsWith(GS1_DATABAR_SCANNER_PREFIX) || scan.startsWith(GS1_QRCODE_SCANNER_PREFIX)) {
return parse(scan.substring(3));
}
return parse(scan);
}
 
public GS1AIElements parse(String barcode) throws GS1ParseException {
if (barcode.length() < 3) {
throw new GS1ParseException("", ERROR_CODE_INVALID_GS1_SCAN, "code too short");
}
 
System.err.println("GS1Util.parse()" + barcode);
GS1AIElements attr = new GS1AIElements();
StringBuilder ai = new StringBuilder();
 
int length = barcode.length();
for (int i = 0; i < length; ++i) {
int aiLength = ai.length();
if (aiLength > 1) {
GS1ApplicationIdentifier aii = GS1AIElements.getApplicationIdentifier(ai.toString());
if (aii == null) {
if (aiLength < 4)
ai.append(barcode.charAt(i));
else
throw new GS1ParseException(ai.toString(), ERROR_CODE_UNKNOWN_AI, "Unknown AI");
} else {
int decimalPoint = 0;
if (aii.decimalPoint) {
try {
decimalPoint = Integer.valueOf(String.valueOf(barcode.charAt(i)));
} catch (NumberFormatException e) {
throw new GS1ParseException(ai.toString(), ERROR_CODE_CONVERT_DECIMAL_POINT, "Errow convert to decimal point");
}
 
if (++i >= length)
throw new GS1ParseException(ai.toString(), ERROR_CODE_INSUFFICIENT_VALUE_LENGTH, "Insufficient value length");
}
 
String value;
 
if (aii.variableLength) {
int separatorIndex = barcode.indexOf(this.separator, i);
 
if (separatorIndex < 0) {
if (length - i > aii.length)
throw new GS1ParseException(ai.toString(), ERROR_CODE_NOT_FOUND_SEPARATOR, "Not found separator");
else if (length - i < aii.minLength)
throw new GS1ParseException(ai.toString(), ERROR_CODE_INSUFFICIENT_VALUE_LENGTH, "Insufficient value length");
else {
value = barcode.substring(i);
i = length;
}
} else if (separatorIndex - i > aii.length)
throw new GS1ParseException(ai.toString(), ERROR_CODE_EXCEEDED_VALUE_LENGTH, "Exceeded value length");
else if (separatorIndex - i < aii.minLength)
throw new GS1ParseException(ai.toString(), ERROR_CODE_INSUFFICIENT_VALUE_LENGTH, "Insufficient value length");
else {
value = barcode.substring(i, separatorIndex);
i = separatorIndex;
}
} else {
if (i + aii.length > length) {
throw new GS1ParseException(ai.toString(), ERROR_CODE_INSUFFICIENT_VALUE_LENGTH, "Insufficient value length");
}
value = barcode.substring(i, i + aii.length);
i += aii.length - 1;
}
 
if (aii.decimalPoint && decimalPoint > 0) {
if (decimalPoint >= value.length())
throw new GS1ParseException(ai.toString(), ERROR_CODE_WRONG_DECIMAL_POINT, "Decimal point more then value length");
 
try {
value = String.valueOf(Double.valueOf(value.substring(0, value.length() - decimalPoint) + "." + value.substring(value.length() - decimalPoint)));
} catch (NumberFormatException e) {
throw new GS1ParseException(ai.toString(), ERROR_CODE_CONVERT_DECIMAL_VALUE, "Error convert decimal point value");
}
}
 
attr.put(ai.toString(), value);
 
ai.setLength(0);
}
} else
ai.append(barcode.charAt(i));
}
 
if (ai.length() > 0)
throw new GS1ParseException(ai.toString(), ERROR_CODE_INCOMPLETE_AI, "Incomplete AI");
 
return attr;
}
 
public String format(GS1AIElements values) {
 
StringBuilder b = new StringBuilder();
int size = values.size();
for (int i = 0; i < size; i++) {
String k = values.getKey(i);
GS1ApplicationIdentifier ai = GS1AIElements.getApplicationIdentifier(k);
final String value = values.getValue(i);
b.append(k);
b.append(value);
if (ai.variableLength && i < size - 1) {
b.append((char) this.separator);
}
}
return b.toString();
}
 
public String formatZPL(GS1AIElements values) {
 
StringBuilder b = new StringBuilder();
int size = values.size();
b.append("@1");
for (int i = 0; i < size; i++) {
String k = values.getKey(i);
GS1ApplicationIdentifier ai = GS1AIElements.getApplicationIdentifier(k);
final String value = values.getValue(i);
b.append(k);
b.append(value);
if (ai.variableLength && i < size - 1) {
b.append("@d029");
}
}
return b.toString();
}
 
public String formatDataMatrix(GS1AIElements values) {
 
StringBuilder b = new StringBuilder();
int size = values.size();
// b.append(FNC1_SEPARATOR);
for (int i = 0; i < size; i++) {
String k = values.getKey(i);
GS1ApplicationIdentifier ai = GS1AIElements.getApplicationIdentifier(k);
final String value = values.getValue(i);
b.append('[');
b.append(k);
b.append(']');
b.append(value);
if (ai.variableLength && i < size - 1) {
// b.append(GS_SEPARATOR);
}
}
return b.toString();
}
 
public static void main(String[] args) throws GS1ParseException {
GS1Util p = new GS1Util('_');
System.out.println("GS1Util.main()" + (char) p.separator);
String barcode = "0104607018700852111806051718062910180605_211";
// barcode = "01088888931021461712031510W1040190";
GS1AIElements values = p.parse(barcode);
values.dump(System.out);
 
values.put("391", "kookkk");
System.out.println(p.format(values));
System.out.println(values.formatHumanReadable());
 
GS1AIElements valuesMax = new GS1AIElements();
valuesMax.put("02", "01234567891234");
valuesMax.put("15", "201202");
valuesMax.put("37", "12345678");
valuesMax.put("10", "12345678901234567890");
GS1Util pStd = new GS1Util();
System.out.println(p.format(valuesMax));
System.out.println(p.format(valuesMax).length());
System.out.println(pStd.format(valuesMax));
System.out.println(pStd.format(valuesMax).length());
System.err.println("GS1Util.main() GS128 from barcode reader");
String gs1128 = "]C10207612345678900152012153745646578";
GS1Util p2 = new GS1Util();
values = p2.parseFromScanner(gs1128);
values.dump(System.out);
}
 
public static String showAllChars(String s) {
StringBuilder b = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
if (ISO646.isValid(s.charAt(i))) {
b.append(s.charAt(i));
} else {
b.append("[" + (int) s.charAt(i) + "]");
}
}
return b.toString();
}
 
}
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/graphicspl/GPLRenderer.java
New file
0,0 → 1,166
package org.openconcerto.modules.label.graphicspl;
 
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
 
import javax.imageio.ImageIO;
 
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
 
public abstract class GPLRenderer {
public static final int ALIGN_LEFT = 0;
public static final int ALIGN_RIGHT = 1;
public static final int ALIGN_CENTER = 2;
 
public static final int BARCODE_EAN8 = 0;
public static final int BARCODE_EAN13 = 1;
public static final int BARCODE_CODE128 = 2;
public static final int BARCODE_CODE128_GS1 = 3;
public static final int BARCODE_DATAMATRIX = 4;
public static final int BARCODE_QRCODE = 5;
private final float ratio;
 
public GPLRenderer(float ratio) {
this.ratio = ratio;
}
 
public GPLRenderer() {
this.ratio = 1.0f;
}
 
public float getRatio() {
return ratio;
}
 
public void render(GraphicsPL graphicsPL) throws IOException {
 
Document doc = graphicsPL.getDocument();
final int width = Integer.parseInt(doc.getDocumentElement().getAttribute("width"));
NodeList nodeList = doc.getFirstChild().getChildNodes();
int size = nodeList.getLength();
for (int i = 0; i < size; i++) {
 
if (nodeList.item(i).getNodeType() == Node.ELEMENT_NODE) {
Element element = (Element) nodeList.item(i);
String name = element.getNodeName();
if (name.equals("text")) {
int x = Math.round(ratio * Integer.parseInt(element.getAttribute("x")));
int y = Math.round(ratio * Integer.parseInt(element.getAttribute("y")));
String txt = element.getTextContent();
Color color = Color.BLACK;
if (element.hasAttribute("color")) {
color = Color.decode(element.getAttribute("color"));
}
int fontSize = Math.round(ratio * Integer.parseInt(element.getAttribute("fontsize")));
String fontName = element.getAttribute("font");
int maxWidth = width - x;
boolean wrap = false;
int align = ALIGN_LEFT;
if (element.hasAttribute("align")) {
if (element.getAttribute("align").equals("right")) {
align = ALIGN_RIGHT;
} else if (element.getAttribute("align").equals("center")) {
align = ALIGN_CENTER;
}
}
if (!element.hasAttribute("visible") || element.getAttribute("visible").equals("true")) {
drawText(x, y, txt, align, fontName, fontSize, color, maxWidth, wrap);
}
 
} else if (name.equals("rectangle")) {
int x = Math.round(ratio * Integer.parseInt(element.getAttribute("x")));
int y = Math.round(ratio * Integer.parseInt(element.getAttribute("y")));
int w = Math.round(ratio * Integer.parseInt(element.getAttribute("width")));
int h = Math.round(ratio * Integer.parseInt(element.getAttribute("height")));
Color color = Color.BLACK;
if (element.hasAttribute("color")) {
color = Color.decode(element.getAttribute("color"));
}
 
if (element.hasAttribute("fill") && element.getAttribute("fill").equals("true")) {
fillRectangle(x, y, w, h, color);
} else {
int lineWidth = Math.round(ratio);
if (element.hasAttribute("linewidth")) {
lineWidth = Math.round(ratio * Integer.parseInt(element.getAttribute("linewidth")));
}
drawRectangle(x, y, w, h, color, lineWidth);
}
 
} else if (name.equals("image")) {
String fileName = element.getAttribute("file");
int x = Math.round(ratio * Integer.parseInt(element.getAttribute("x")));
int y = Math.round(ratio * Integer.parseInt(element.getAttribute("y")));
BufferedImage img = ImageIO.read(new File(graphicsPL.getImageDir(), fileName));
int w = Math.round(ratio * img.getWidth());
int h = Math.round(ratio * img.getHeight());
if (element.hasAttribute("width")) {
w = Math.round(ratio * Integer.parseInt(element.getAttribute("width")));
}
if (element.hasAttribute("height")) {
h = Math.round(ratio * Integer.parseInt(element.getAttribute("height")));
}
drawImage(x, y, w, h, img);
 
} else if (name.equals("barcode")) {
int x = Math.round(ratio * Integer.parseInt(element.getAttribute("x")));
int y = Math.round(ratio * Integer.parseInt(element.getAttribute("y")));
int h = 0;
 
if (element.hasAttribute("height")) {
h = Math.round(ratio * Integer.parseInt(element.getAttribute("height")));
}
String type = element.getAttribute("type");
String code = element.getTextContent();
int t;
if (type.equals("ean8")) {
t = BARCODE_EAN8;
} else if (type.equals("ean13")) {
t = BARCODE_EAN13;
} else if (type.equals("ean128")) {
t = BARCODE_CODE128;
} else if (type.equals("gs1")) {
t = BARCODE_CODE128_GS1;
} else if (type.equals("datamatrix")) {
t = BARCODE_DATAMATRIX;
if (h != 0) {
System.err.println("ignoring datamatrix height attribute");
}
} else if (type.equals("qrcode")) {
t = BARCODE_QRCODE;
} else {
throw new IllegalArgumentException("unsupported barcode type : " + type);
}
 
int fontSize = Math.round(ratio * 8);
if (element.hasAttribute("fontsize")) {
fontSize = Math.round(ratio * Integer.parseInt(element.getAttribute("fontsize")));
}
int moduleWidth = Math.round(ratio);
if (element.hasAttribute("modulewidth")) {
moduleWidth = Math.round(ratio * Integer.parseInt(element.getAttribute("modulewidth")));
}
drawBarcode(x, y, h, t, code, moduleWidth, fontSize);
} else {
throw new IllegalStateException("unsupported primitive : " + name);
}
}
}
}
 
public abstract void drawText(int x, int y, String text, int align, String fontName, int fontSize, Color color, int maxWidth, boolean wrap);
 
public abstract void drawImage(int x, int y, int w, int h, BufferedImage img);
 
public abstract void fillRectangle(int x, int y, int w, int h, Color color);
 
public abstract void drawRectangle(int x, int y, int w, int h, Color color, int lineWidth);
 
public abstract void drawBarcode(int x, int y, int h, int type, String code, int moduleWidth, int fontSize);
 
}
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/graphicspl/GraphicsPL.java
New file
0,0 → 1,128
package org.openconcerto.modules.label.graphicspl;
 
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
 
import javax.imageio.ImageIO;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
 
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
 
public class GraphicsPL {
String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?><graphicspl height=\"400\" width=\"600\"/>";
Document doc;
private File imgDir;
 
public static void main(String[] args) throws Exception {
GraphicsPL g = new GraphicsPL();
g.load(new File("Template/Labels/test.graphicspl"));
BufferedImage img = g.createImage(10);
ImageIO.write(img, "png", new File("gpl.png"));
String zpl = g.getZPL();
System.out.println(zpl);
Printable p = g.createPrintable();
PrinterJob job = PrinterJob.getPrinterJob();
job.setPrintable(p);
boolean ok = job.printDialog();
if (ok) {
job.print();
}
 
}
 
public Printable createPrintable() {
final Element root = this.doc.getDocumentElement();
int dpi = 300;
if (root.hasAttribute("dpi")) {
dpi = Integer.parseInt(root.getAttribute("dpi"));
}
float printRatio = 1f;
if (root.hasAttribute("printratio")) {
printRatio = Float.parseFloat(root.getAttribute("printratio"));
}
return createPrintable(dpi, printRatio);
}
 
public Printable createPrintable(int dpi, float printRatio) {
return new Printable() {
 
@Override
public int print(Graphics graphics, PageFormat pf, int pageIndex) throws PrinterException {
if (pageIndex > 0) {
return NO_SUCH_PAGE;
}
final Element root = GraphicsPL.this.doc.getDocumentElement();
final int width = Math.round(printRatio * Integer.parseInt(root.getAttribute("width")));
final int height = Math.round(printRatio * Integer.parseInt(root.getAttribute("height")));
final Graphics2D g2d = (Graphics2D) graphics;
float ratio = (printRatio * dpi) / 72f;
try {
final BufferedImage img = createImage(ratio);
g2d.drawImage(img, (int) Math.round(pf.getImageableX()), (int) Math.round(pf.getImageableY()), width, height, null);
} catch (ParserConfigurationException | SAXException | IOException e) {
throw new PrinterException(e.getMessage());
}
return PAGE_EXISTS;
}
};
}
 
private String getZPL() throws IOException {
final ZPLRenderer renderer = new ZPLRenderer();
renderer.render(this);
return renderer.getZPL();
}
 
private BufferedImage createImage(float ratio) throws ParserConfigurationException, SAXException, IOException {
final Element root = this.doc.getDocumentElement();
final int width = Math.round(ratio * Integer.parseInt(root.getAttribute("width")));
final int height = Math.round(ratio * Integer.parseInt(root.getAttribute("height")));
final BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
final Graphics2D graphics = (Graphics2D) img.getGraphics();
graphics.setColor(Color.WHITE);
graphics.fillRect(0, 0, width, height);
final Graphics2DRenderer renderer = new Graphics2DRenderer(graphics, ratio);
renderer.render(this);
graphics.dispose();
return img;
}
 
public Document getDocument() {
return this.doc;
}
 
private void load(File file) throws ParserConfigurationException, SAXException, IOException {
this.xml = new String(Files.readAllBytes(file.toPath()));
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
final DocumentBuilder builder = factory.newDocumentBuilder();
final ByteArrayInputStream input = new ByteArrayInputStream(this.xml.getBytes(StandardCharsets.UTF_8));
this.doc = builder.parse(input);
this.doc.getDocumentElement().normalize();
this.imgDir = file.getParentFile();
}
 
public File getImageDir() {
return this.imgDir;
}
 
public void setImageDir(File dir) {
this.imgDir = dir;
}
 
}
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/graphicspl/ZPLRenderer.java
New file
0,0 → 1,234
package org.openconcerto.modules.label.graphicspl;
 
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
 
import org.openconcerto.utils.ImageUtils;
import org.openconcerto.utils.StringUtils;
 
public class ZPLRenderer extends GPLRenderer {
 
private StringBuilder sb = new StringBuilder();
 
public ZPLRenderer() {
sb.append("^XA\n^CI28\n");
}
 
@Override
public void drawText(int x, int y, String text, int align, String fontName, int fontSize, Color color, int maxWidth, boolean wrap) {
sb.append("^CF0,");
sb.append(fontSize);
//
 
if (align == ALIGN_RIGHT) {
sb.append("^FO");
sb.append('0');
sb.append(',');
sb.append(y);
sb.append("^FB");
sb.append(x);
sb.append(",9999,0");
 
sb.append(",R,0");
} else if (align == ALIGN_CENTER) {
sb.append("^FO");
sb.append('0');
sb.append(',');
sb.append(y);
sb.append("^FB");
sb.append(x * 2);
sb.append(",9999,0");
 
sb.append(",C,0");
} else {
sb.append("^FO");
sb.append(x);
sb.append(',');
sb.append(y);
 
//
sb.append("^FB");
sb.append(maxWidth);
sb.append(",9999,0");
 
sb.append(",L,0");
}
//
 
sb.append("^FD");
sb.append(text);
if (align == ALIGN_CENTER) {
sb.append("\\&");
}
 
sb.append("^FS\n");
}
 
@Override
public void drawImage(int x, int y, int w, int h, BufferedImage img) {
try {
if (w != img.getWidth() || h != img.getHeight()) {
img = ImageUtils.createQualityResizedImage(img, w, h);
}
 
final int bytesPerRow = (img.getWidth() + 7) / 8;
int size = bytesPerRow * img.getHeight();
byte[] data = getData(img);
sb.append("^FO");
sb.append(x);
sb.append(",");
sb.append(y);
sb.append("^GFA");
sb.append(",");
sb.append(size);
sb.append(",");
sb.append(size);
sb.append(",");
sb.append(bytesPerRow);
sb.append(",");
sb.append(StringUtils.bytesToHexString(data));
sb.append("^FS\n");
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
 
byte[] getData(BufferedImage img) throws IOException {
 
ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
int width = img.getWidth();
 
int height = img.getHeight();
// Lines
for (int i = 0; i < height; i++) {
int[] pixels = new int[width];
img.getRGB(0, i, width, 1, pixels, 0, 4);
final byte[] encodedLine = encodeLine(pixels);
out.write(encodedLine);
}
return out.toByteArray();
}
 
private byte[] encodeLine(int[] pixels) throws IOException {
final int bytesPerRow = (pixels.length + 7) / 8;
byte[] bytesToEncode = new byte[bytesPerRow];
int index = 0;
for (int i = 0; i < bytesToEncode.length; i++) {
int points = 0;
for (int j = 0; j < 8; j++) {
 
int c = 0;
if (index < pixels.length) {
c = pixels[index];
int a = (c & 0xff000000) >> 24;
int r = (c & 0x00ff0000) >> 16;
int g = (c & 0x0000ff00) >> 8;
int b = c & 0x000000ff;
int grayScale = (int) (21.2671 * r + 71.5160 * g + 7.2169 * b);
boolean isBlack = grayScale < 12000;
points = points * 2;
if (isBlack && a < 0) {
points++;
}
 
} else {
points = points * 2;
}
 
index++;
}
bytesToEncode[i] = (byte) points;
 
}
return bytesToEncode;
}
 
@Override
public void fillRectangle(int x, int y, int w, int h, Color color) {
sb.append("^FO");
sb.append(x);
sb.append(',');
sb.append(y);
 
sb.append("^GB");
sb.append(w);
sb.append(',');
sb.append(h);
sb.append(',');
sb.append(Math.min(w, h));
sb.append("^FS\n");
 
}
 
@Override
public void drawRectangle(int x, int y, int w, int h, Color color, int lineWidth) {
sb.append("^FO");
sb.append(x);
sb.append(',');
sb.append(y);
 
sb.append("^GB");
sb.append(w);
sb.append(',');
sb.append(h);
sb.append(',');
sb.append(lineWidth);
sb.append("^FS\n");
}
 
@Override
public void drawBarcode(int x, int y, int h, int type, String code, int moduleWidth, int fontSize) {
code = code.trim();
sb.append("^FO");
sb.append(x);
sb.append(',');
sb.append(y);
sb.append("^CF0,");
sb.append(fontSize);
if (type == BARCODE_EAN13) {
sb.append("^BY");
sb.append(moduleWidth);
 
sb.append("^BEN,");
sb.append(h);
sb.append(",Y,N");
 
} else if (type == BARCODE_EAN8) {
sb.append("^BY");
sb.append(moduleWidth);
 
sb.append("^B8N,");
sb.append(h);
sb.append(",Y,N");
 
} else if (type == BARCODE_DATAMATRIX) {
sb.append("^BXN,");
sb.append(moduleWidth);
sb.append(",200");
} else if (type == BARCODE_CODE128) {
sb.append("^BY");
sb.append(moduleWidth - 1);
sb.append("^BCN,");
sb.append(h);
sb.append(",Y,N,Y,D");
 
} else if (type == BARCODE_CODE128_GS1) {
sb.append("^BY");
sb.append(moduleWidth - 1);
sb.append("^BCN,");
sb.append(h);
sb.append(",Y,N,Y,N");
 
}
sb.append("^FD");
sb.append(code);
sb.append("^FS\n");
}
 
public String getZPL() {
return sb.toString() + "\n^XZ";
}
 
}
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/graphicspl/Graphics2DRenderer.java
New file
0,0 → 1,116
package org.openconcerto.modules.label.graphicspl;
 
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
 
import uk.org.okapibarcode.backend.Code128;
import uk.org.okapibarcode.backend.DataMatrix;
import uk.org.okapibarcode.backend.Ean;
import uk.org.okapibarcode.backend.QrCode;
import uk.org.okapibarcode.backend.Symbol;
import uk.org.okapibarcode.output.Java2DRenderer;
 
public class Graphics2DRenderer extends GPLRenderer {
 
private final Graphics2D g;
 
public Graphics2DRenderer(Graphics2D g, float ratio) {
super(ratio);
this.g = g;
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
}
 
@Override
public void drawText(int x, int y, String text, int align, String fontName, int fontSize, Color color, int maxWidth, boolean wrap) {
final Font font = new Font(fontName, Font.PLAIN, fontSize);
g.setFont(font.deriveFont(fontSize));
g.setColor(color);
y += g.getFontMetrics().getAscent() - g.getFontMetrics().getDescent();
if (align == ALIGN_RIGHT) {
int w = (int) g.getFontMetrics().getStringBounds(text, g).getWidth();
g.drawString(text, x - w, y);
} else if (align == ALIGN_CENTER) {
int w = (int) (g.getFontMetrics().getStringBounds(text, g).getWidth() / 2D);
g.drawString(text, x - w, y);
} else {
g.drawString(text, x, y);
}
}
 
@Override
public void drawImage(int x, int y, int w, int h, BufferedImage img) {
g.drawImage(img, x, y, x + w, y + h, 0, 0, img.getWidth(), img.getHeight(), null);
}
 
@Override
public void fillRectangle(int x, int y, int w, int h, Color color) {
g.setColor(color);
g.fillRect(x, y, w, h);
}
 
@Override
public void drawRectangle(int x, int y, int w, int h, Color color, int lineWidth) {
Stroke s = g.getStroke();
g.setColor(color);
if (lineWidth != 1) {
g.setStroke(new BasicStroke(lineWidth));
}
g.drawRect(x, y, w, h);
g.setStroke(s);
}
 
@Override
public void drawBarcode(int x, int y, int h, int type, String code, int moduleWidth, int fontSize) {
 
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
 
Symbol symbol = null;
if (type == BARCODE_EAN8) {
symbol = new Ean();
((Ean) symbol).setMode(Ean.Mode.EAN8);
symbol.setBarHeight(h);
} else if (type == BARCODE_EAN13) {
symbol = new Ean();
((Ean) symbol).setMode(Ean.Mode.EAN13);
symbol.setBarHeight(h);
} else if (type == BARCODE_CODE128) {
symbol = new Code128();
symbol.setDataType(Symbol.DataType.GS1);
symbol.setBarHeight(h);
} else if (type == BARCODE_CODE128_GS1) {
symbol = new Code128();
symbol.setBarHeight(h);
} else if (type == BARCODE_DATAMATRIX) {
symbol = new DataMatrix();
} else if (type == BARCODE_QRCODE) {
symbol = new QrCode();
}
 
if (symbol == null) {
return;
}
symbol.setModuleWidth(moduleWidth);
symbol.setFontSize(fontSize);
symbol.setContent(code.trim());
 
AffineTransform aT = g.getTransform();
 
g.setTransform(AffineTransform.getTranslateInstance(x, y));
 
Java2DRenderer renderer = new Java2DRenderer(g, 1, Color.WHITE, Color.BLACK);
renderer.render(symbol);
 
g.setTransform(aT);
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
 
}
 
}
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/oc-qrcode.png
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/oc-qrcode.png
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/Label.java
New file
0,0 → 1,5
package org.openconcerto.modules.label;
 
public class Label {
 
}
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/default-zpl.txt
New file
0,0 → 1,20
^XA
 
^FX Titre
^CF0,42^FO150,43^FB750,1,0,L^FDXXXXXXXXXX^FS
^FX DataMatrix
^FO1010,20^BXN,7,200,,,,@^FDOPENCONCERTO^FS
 
^FX Nom du produit
^CF0,40
^FO150,140^FB700,1,0,L^FDYYYYYYYYYY^FS
 
^FX Code barre
^FO150,220
^BY3,2,300
^BCN,300,Y,N,Y,D
^FD(02)7612345678900(15)201218(37)9999^FS
 
^FO150,620^BY3,2,300^BCN,300,Y,N,Y,D^FD(10)LLLLLLLLL^FS
 
^XZ
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/ModuleLabel.java
4,10 → 4,17
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;
import java.util.TreeMap;
 
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.SwingWorker;
 
import org.openconcerto.erp.generationDoc.provider.AdresseFullClientValueProvider;
14,6 → 21,7
import org.openconcerto.erp.modules.AbstractModule;
import org.openconcerto.erp.modules.ComponentsContext;
import org.openconcerto.erp.modules.ModuleFactory;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLRowValuesListFetcher;
23,10 → 31,12
import org.openconcerto.sql.view.list.IListeAction.IListeEvent;
import org.openconcerto.sql.view.list.RowAction.PredicateRowAction;
import org.openconcerto.utils.ExceptionHandler;
import org.openconcerto.utils.FileUtils;
import org.openconcerto.utils.GestionDevise;
import org.openconcerto.utils.StringUtils;
 
public final class ModuleLabel extends AbstractModule {
final LinkedHashMap<String, String> zplTemplates = new LinkedHashMap<String, String>();
 
public ModuleLabel(ModuleFactory f) throws IOException {
super(f);
34,6 → 44,9
 
@Override
protected void setupComponents(ComponentsContext ctxt) {
readTemplates(new File("Template/Labels"));
readTemplates(new File("Configuration/Template/Labels"));
 
final String actionName = "Imprimer les étiquettes";
final PredicateRowAction aArticle = new PredicateRowAction(new AbstractAction(actionName) {
 
41,26 → 54,31
public void actionPerformed(ActionEvent arg0) {
final IListe list = IListe.get(arg0);
final List<Integer> selectedIDs = list.getSelection().getSelectedIDs();
final SwingWorker<List<SQLRowValues>, String> wworker = new SwingWorker<List<SQLRowValues>, String>() {
final SQLTable tArticle = list.getSelectedRows().get(0).getTable();
final SwingWorker<List<RowValuesLabel>, String> wworker = new SwingWorker<List<RowValuesLabel>, String>() {
 
@Override
protected List<SQLRowValues> doInBackground() throws Exception {
final SQLTable tArticle = list.getSelectedRows().get(0).getTable();
protected List<RowValuesLabel> doInBackground() throws Exception {
final SQLRowValues graph = new SQLRowValues(tArticle);
graph.putNulls("NOM", "PV_TTC");
final SQLRowValuesListFetcher fetcher = SQLRowValuesListFetcher.create(graph);
final List<SQLRowValues> values = fetcher.fetch(new Where(tArticle.getKey(), selectedIDs));
return values;
final List<SQLRowValues> rows = fetcher.fetch(new Where(tArticle.getKey(), selectedIDs));
final List<RowValuesLabel> list = new ArrayList<>(rows.size());
for (SQLRowValues row : rows) {
list.add(new RowValuesLabel(row));
}
return list;
}
 
@Override
protected void done() {
try {
final List<SQLRowValues> values = get();
final List<RowValuesLabel> values = get();
 
final LabelFrame f = new LabelFrame(values, new LabelRenderer() {
 
@Override
public void paintLabel(Graphics g, SQLRowAccessor row, int x, int y, int gridWith, int gridHeight, float fontSize) {
public void paintLabel(Graphics g, Label label, int x, int y, int gridWith, int gridHeight, float fontSize) {
g.setColor(Color.BLACK);
g.setFont(g.getFont().deriveFont(fontSize));
// Labels borders
67,6 → 85,7
final int hBorder = 12;
final int vBorder = 8;
// Product name
SQLRowValues row = ((RowValuesLabel) label).getSQLRowValues();
final String text = row.getString("NOM");
final List<String> l = StringUtils.wrap(text, g.getFontMetrics(), gridWith - 2 * hBorder);
final int lineHeight = g.getFontMetrics().getHeight();
73,7 → 92,7
int lineY = y;
final int margin = gridHeight - l.size() * lineHeight;
if (margin > 0) {
lineY += (int) (margin / 2);
lineY += margin / 2;
}
for (String line : l) {
g.drawString(line, x + hBorder, lineY);
95,8 → 114,8
} catch (Exception e) {
ExceptionHandler.handle("Erreur d'impression", e);
}
}
};
};
wworker.execute();
 
}
107,11 → 126,11
public void actionPerformed(ActionEvent arg0) {
final IListe list = IListe.get(arg0);
final List<Integer> selectedIDs = list.getSelection().getSelectedIDs();
final SwingWorker<List<SQLRowValues>, String> wworker = new SwingWorker<List<SQLRowValues>, String>() {
final SQLTable tClient = list.getSelectedRows().get(0).getTable();
final SwingWorker<List<RowValuesLabel>, String> wworker = new SwingWorker<List<RowValuesLabel>, String>() {
 
@Override
protected List<SQLRowValues> doInBackground() throws Exception {
final SQLTable tClient = list.getSelectedRows().get(0).getTable();
protected List<RowValuesLabel> doInBackground() throws Exception {
final SQLRowValues graph = new SQLRowValues(tClient);
graph.putNulls("NOM");
final SQLRowValues a1 = graph.putRowValues("ID_ADRESSE");
119,17 → 138,24
final SQLRowValues a2 = graph.putRowValues("ID_ADRESSE_L");
a2.putNulls(a2.getTable().getFieldsName());
final SQLRowValuesListFetcher fetcher = SQLRowValuesListFetcher.create(graph);
final List<SQLRowValues> values = fetcher.fetch(new Where(tClient.getKey(), selectedIDs));
return values;
final List<SQLRowValues> rows = fetcher.fetch(new Where(tClient.getKey(), selectedIDs));
 
final List<RowValuesLabel> list = new ArrayList<>(rows.size());
for (SQLRowValues row : rows) {
list.add(new RowValuesLabel(row));
}
return list;
}
 
@Override
protected void done() {
try {
final List<SQLRowValues> values = get();
final List<RowValuesLabel> values = get();
final LabelFrame f = new LabelFrame(values, new LabelRenderer() {
 
@Override
public void paintLabel(Graphics g, SQLRowAccessor row, int x, int y, int gridWith, int gridHeight, float fontSize) {
public void paintLabel(Graphics g, Label label, int x, int y, int gridWith, int gridHeight, float fontSize) {
SQLRowValues row = ((RowValuesLabel) label).getSQLRowValues();
SQLRowAccessor rAddr = row.getForeign("ID_ADRESSE_L");
if (rAddr == null || rAddr.isUndefined()) {
rAddr = row.getForeign("ID_ADRESSE");
165,8 → 191,8
} catch (Exception e) {
ExceptionHandler.handle("Erreur d'impression", e);
}
}
};
};
wworker.execute();
 
}
176,13 → 202,94
aClient.setPredicate(IListeEvent.createSelectionCountPredicate(1, Integer.MAX_VALUE));
ctxt.getElement("ARTICLE").getRowActions().add(aArticle);
ctxt.getElement("CLIENT").getRowActions().add(aClient);
 
if (!this.zplTemplates.isEmpty()) {
for (final Entry<String, String> entry : this.zplTemplates.entrySet()) {
final String zpl = entry.getValue();
final PredicateRowAction action = new PredicateRowAction(new AbstractAction("Imprimer l'étiquette " + entry.getKey()) {
 
@Override
public void actionPerformed(ActionEvent arg0) {
final ZPLPrinterPanel p = new ZPLPrinterPanel(zpl);
final JFrame f = new JFrame();
final IListe list = IListe.get(arg0);
final int idProduct = list.getSelection().getSelectedID();
final SQLTable tArticle = list.getSelectedRows().get(0).getTable();
 
final SwingWorker<SQLRowValues, String> wworker = new SwingWorker<SQLRowValues, String>() {
 
@Override
protected SQLRowValues doInBackground() throws Exception {
final SQLRow row = tArticle.getRow(idProduct);
row.fetchValues();
return row.asRowValues();
}
 
@Override
protected void done() {
try {
final SQLRowValues values = get();
p.initUI(values);
f.setTitle(entry.getKey());
f.setContentPane(p);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
 
} catch (Exception e) {
ExceptionHandler.handle("Erreur d'impression", e);
}
}
};
wworker.execute();
 
}
}, true, false);
 
action.setPredicate(IListeEvent.createSelectionCountPredicate(1, 1));
ctxt.getElement("ARTICLE").getRowActions().add(action);
}
}
 
}
 
@Override
protected void start() {
 
}
 
private void readTemplates(File templatesDir) {
System.out.println("ModuleLabel.readTemplates() " + templatesDir.getAbsolutePath());
if (templatesDir.exists() && templatesDir.isDirectory()) {
System.err.println("ModuleLabel.readTemplates() " + templatesDir.getAbsolutePath());
File[] files = templatesDir.listFiles();
if (files != null) {
LinkedHashMap<String, String> map = new LinkedHashMap<>();
for (File f : files) {
if (f.getName().endsWith(".zpl")) {
try {
String zpl = FileUtils.read(f, StandardCharsets.UTF_8);
String name = f.getName().substring(0, f.getName().length() - 4).trim();
map.put(name, zpl);
System.err.println("ModuleLabel.readTemplates() add " + name);
} catch (Exception e) {
System.err.println(this.getClass().getCanonicalName() + "start() cannot read zpl template : " + f.getAbsolutePath() + " : " + e.getMessage());
}
}
}
// Tri de la map par clef
final TreeMap<String, String> copy = new TreeMap<>(map);
this.zplTemplates.clear();
this.zplTemplates.putAll(copy);
}
 
} else {
System.err.println("ModuleLabel.readTemplates() " + templatesDir.getAbsolutePath() + " missing");
}
}
 
@Override
protected void stop() {
// nothing
}
}
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/RowValuesLabel.java
New file
0,0 → 1,16
package org.openconcerto.modules.label;
 
import org.openconcerto.sql.model.SQLRowValues;
 
public class RowValuesLabel extends Label {
private SQLRowValues row;
 
public RowValuesLabel(SQLRowValues row) {
this.row = row;
}
 
public SQLRowValues getSQLRowValues() {
return row;
}
 
}
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/32.png
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/32.png
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/ZPLPrinterPanel.java
New file
0,0 → 1,409
package org.openconcerto.modules.label;
 
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.print.PrinterJob;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
 
import javax.print.DocFlavor;
import javax.print.DocPrintJob;
import javax.print.PrintService;
import javax.print.SimpleDoc;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
 
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.ui.DefaultGridBagConstraints;
import org.openconcerto.ui.JLabelBold;
import org.openconcerto.utils.BaseDirs;
import org.openconcerto.utils.ExceptionHandler;
import org.openconcerto.utils.FileUtils;
import org.openconcerto.utils.ProductInfo;
 
public class ZPLPrinterPanel extends JPanel {
private final HashMap<String, String> mapName = new HashMap<>();
private final List<String> variables;
private final List<String> knownVariables = new ArrayList<>();
private Map<String, JTextField> editorMap = new HashMap<>();
private String zpl;
private final Properties properties = new Properties();
 
public static void main(String[] args) throws IOException {
// final File f = new File("Templates/Labels", "50x50.zpl");
final File file = new File("Template/Labels", "57x32.zpl");
String zpl = FileUtils.read(file);
SwingUtilities.invokeLater(new Runnable() {
 
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) {
e.printStackTrace();
}
ZPLPrinterPanel p = new ZPLPrinterPanel(zpl);
p.initUI(null);
JFrame f = new JFrame();
f.setTitle(file.getName());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setContentPane(p);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
});
 
}
 
public ZPLPrinterPanel(String zpl) {
this.zpl = zpl;
this.variables = getVariables(zpl);
knownVariables.add("product.code");
knownVariables.add("product.name");
knownVariables.add("product.material");
 
knownVariables.add("product.ean13");
knownVariables.add("product.price");
knownVariables.add("product.pricewithtax");
//
mapName.put("product.name", "Nom");
mapName.put("product.code", "Code");
mapName.put("product.ean13", "Code à barres");
mapName.put("product.price", "Prix HT");
mapName.put("product.pricewithtax", "Prix TTC");
mapName.put("product.treatment", "Traitement");
mapName.put("product.origin", "Origine");
mapName.put("product.batch", "Lot");
mapName.put("product.size", "Taille");
mapName.put("product.color", "Couleur");
mapName.put("product.material", "Matière");
}
 
private final File getPrefFile() {
final File prefsFolder = BaseDirs.create(ProductInfo.getInstance()).getPreferencesFolder();
if (!prefsFolder.exists()) {
prefsFolder.mkdirs();
}
return new File(prefsFolder, "labels.properties");
}
 
protected void initUI(SQLRowAccessor row) {
final File prefsFolder = BaseDirs.create(ProductInfo.getInstance()).getPreferencesFolder();
if (!prefsFolder.exists()) {
prefsFolder.mkdirs();
}
 
final File file = getPrefFile();
System.out.println(file.getAbsolutePath());
if (file.exists()) {
try {
properties.load(new FileInputStream(file));
} catch (IOException e) {
e.printStackTrace();
}
}
 
this.setLayout(new GridBagLayout());
GridBagConstraints c = new DefaultGridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
this.removeAll();
 
// Fields
 
Set<String> added = new HashSet<>();
for (String v : this.knownVariables) {
if (variables.contains(v)) {
// Non editable
String label = getName(v);
c.gridx = 0;
c.weightx = 0;
this.add(new JLabel(label, SwingConstants.RIGHT), c);
c.gridx++;
c.weightx = 1;
 
String value = getValueAsString(row, v);
JTextField txt = new JTextField(20);
if (value != null) {
txt.setText(value);
txt.setEditable(false);
}
editorMap.put(v, txt);
this.add(txt, c);
added.add(v);
c.gridy++;
}
}
for (String v : this.variables) {
if (!added.contains(v)) {
// Editable
String label = getName(v);
c.gridx = 0;
c.weightx = 0;
this.add(new JLabel(label, SwingConstants.RIGHT), c);
c.gridx++;
c.weightx = 1;
JTextField txt = new JTextField(20);
editorMap.put(v, txt);
this.add(txt, c);
added.add(v);
c.gridy++;
}
 
}
 
c.gridwidth = 2;
c.gridx = 0;
this.add(new JLabelBold("Paramètres d'impression"), c);
// Printer selector
c.gridx = 0;
c.gridy++;
final JPanel l1 = new JPanel();
l1.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 0));
 
l1.add(new JLabel("Nombre d'étiquettes"));
final JSpinner nbLabels = new JSpinner(new SpinnerNumberModel(1, 1, 1000, 10));
l1.add(nbLabels);
this.add(l1, c);
// Delay
l1.add(new JLabel(" Pause entre chaque impression"));
final JSpinner delayLabels = new JSpinner(new SpinnerNumberModel(800, 100, 10000, 100));
l1.add(delayLabels);
this.add(l1, c);
l1.add(new JLabel("ms"));
 
c.gridy++;
 
final JPanel lPrintNetwork = new JPanel();
lPrintNetwork.setLayout(new FlowLayout(FlowLayout.LEFT, 2, 0));
JRadioButton radioNetworkPrinter = new JRadioButton("imprimante réseau IP :");
lPrintNetwork.add(radioNetworkPrinter);
JTextField textPrinterIP = new JTextField(16);
lPrintNetwork.add(textPrinterIP);
lPrintNetwork.add(new JLabel(" Port :"));
JSpinner portZPL = new JSpinner(new SpinnerNumberModel(9100, 24, 10000, 1));
lPrintNetwork.add(portZPL);
this.add(lPrintNetwork, c);
 
c.gridy++;
final JPanel lPrintLocal = new JPanel();
lPrintLocal.setLayout(new FlowLayout(FlowLayout.LEFT, 2, 0));
JRadioButton radioLocalPrinter = new JRadioButton("imprimante locale");
lPrintLocal.add(radioLocalPrinter);
radioLocalPrinter.setSelected(true);
this.add(lPrintLocal, c);
 
final ButtonGroup gr = new ButtonGroup();
gr.add(radioLocalPrinter);
gr.add(radioNetworkPrinter);
c.gridy++;
c.weighty = 1;
c.gridx = 1;
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.SOUTHEAST;
 
// Restore state from properties
if (properties.getOrDefault("printerType", "local").equals("local")) {
radioLocalPrinter.setSelected(true);
} else {
radioNetworkPrinter.setSelected(true);
}
textPrinterIP.setText(properties.getOrDefault("printerIp", "").toString());
portZPL.setValue(Long.parseLong(properties.getOrDefault("printerPort", "9100").toString()));
nbLabels.setValue(Long.parseLong(properties.getOrDefault("nbLabels", "1").toString()));
delayLabels.setValue(Long.parseLong(properties.getOrDefault("delay", "800").toString()));
// Print
 
JButton printButton = new JButton("Imprimer");
this.add(printButton, c);
printButton.addActionListener(new ActionListener() {
 
@Override
public void actionPerformed(ActionEvent e) {
try {
if (radioLocalPrinter.isSelected()) {
properties.put("printerType", "local");
} else {
properties.put("printerType", "network");
properties.put("printerIp", textPrinterIP.getText());
properties.put("printerPort", portZPL.getValue().toString());
}
properties.put("nbLabels", nbLabels.getValue().toString());
properties.put("delay", delayLabels.getValue().toString());
// Save Prefs
properties.store(new FileOutputStream(getPrefFile()), "");
} catch (Exception e1) {
ExceptionHandler.handle("Erreur de sauvegarde de " + getPrefFile().getAbsolutePath(), e1);
}
final String code = createZPLCode();
System.out.println("ZPL:");
System.out.println(code);
byte[] data = code.getBytes(StandardCharsets.UTF_8);
if (radioNetworkPrinter.isSelected()) {
Socket socket = null;
try {
socket = new Socket(textPrinterIP.getText(), ((Number) portZPL.getValue()).intValue());
final DataOutputStream out = new DataOutputStream(socket.getOutputStream());
final int nb = ((Number) nbLabels.getValue()).intValue();
for (int i = 0; i < nb; i++) {
out.write(data);
}
} catch (Exception ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(printButton, "Erreur d'impression réseau : " + ex.getMessage());
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
 
}
} else {
try {
final PrinterJob pj1 = PrinterJob.getPrinterJob();
if (pj1.printDialog()) {
final PrintService ps = pj1.getPrintService();
final DocPrintJob pj = ps.createPrintJob();
final SimpleDoc doc = new SimpleDoc(data, DocFlavor.BYTE_ARRAY.AUTOSENSE, null);
final int nb = ((Number) nbLabels.getValue()).intValue();
for (int i = 0; i < nb; i++) {
pj.print(doc, null);
Thread.sleep(((Number) delayLabels.getValue()).intValue());
}
}
} catch (Exception ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(printButton, "Erreur d'impression locale : " + ex.getMessage());
}
}
 
}
});
 
}
 
private String createZPLCode() {
 
final BufferedReader reader = new BufferedReader(new StringReader(this.zpl));
final StringBuilder builder = new StringBuilder();
 
try {
String line = reader.readLine();
while (line != null) {
if (line.contains("${")) {
boolean add = false;
for (String v : this.editorMap.keySet()) {
if (line.contains("${" + v + "}")) {
final String value = this.editorMap.get(v).getText();
line = line.replace("${" + v + "}", value);
if (!value.trim().isEmpty()) {
add = true;
}
}
}
if (add) {
builder.append(line);
builder.append("\n");
}
 
} else {
builder.append(line);
builder.append("\n");
}
line = reader.readLine();
}
 
} catch (Exception e) {
e.printStackTrace();
}
 
return builder.toString();
}
 
public String getValueAsString(SQLRowAccessor row, String variableName) {
if (row == null) {
return null;
}
if (variableName.equals("product.code")) {
return row.getString("CODE");
} else if (variableName.equals("product.name")) {
return row.getString("NOM");
} else if (variableName.equals("product.ean13")) {
return row.getString("CODE_BARRE");
} else if (variableName.equals("product.price")) {
return new DecimalFormat("#0.00").format(row.getBigDecimal("PV_HT"));
} else if (variableName.equals("product.pricewithtax")) {
return new DecimalFormat("#0.00").format(row.getBigDecimal("PV_TTC"));
} else if (variableName.equals("product.material")) {
return row.getString("MATIERE");
}
return "";
}
 
public String getName(String variableName) {
String n = mapName.get(variableName);
if (n == null) {
return variableName;
}
return n;
}
 
public List<String> getVariables(String str) {
final List<String> result = new ArrayList<>();
if (str == null || str.length() < 4) {
return result;
}
final int l = str.length() - 1;
int start = 0;
boolean inName = false;
for (int i = 0; i < l; i++) {
char c1 = str.charAt(i);
char c2 = str.charAt(i + 1);
if (!inName) {
if (c1 == '$' && c2 == '{') {
start = i + 2;
inName = true;
}
} else if (c2 == '}') {
final int stop = i + 1;
String v = str.substring(start, stop);
result.add(v);
inName = false;
}
}
return result;
}
}
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/16.png
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/16.png
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/LabelFrame.java
20,7 → 20,6
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
 
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.ui.DefaultGridBagConstraints;
import org.openconcerto.utils.ExceptionHandler;
 
30,7 → 29,7
private static final int DEFAULT_COLS = 4;
final LabelPanel labelPanel;
 
public LabelFrame(List<? extends SQLRowAccessor> list, LabelRenderer labelRenderer) {
public LabelFrame(List<? extends Label> list, LabelRenderer labelRenderer) {
final JPanel p = new JPanel();
p.setLayout(new GridBagLayout());
final GridBagConstraints c = new DefaultGridBagConstraints();
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/ISO646.java
New file
0,0 → 1,30
package org.openconcerto.modules.label;
 
public class ISO646 {
public static final String ALLOWED_CHARS = "!\"%&'()*+,-./01234567989:;<=>?ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz";
 
public static boolean isValid(char c) {
final int length = ALLOWED_CHARS.length();
for (int i = 0; i < length; i++) {
if (c == ALLOWED_CHARS.charAt(i)) {
return true;
}
}
return false;
}
 
public static String clean(String s) {
final int length = s.length();
final StringBuilder b = new StringBuilder(length);
for (int i = 0; i < length; i++) {
final char charAt = s.charAt(i);
if (isValid(charAt)) {
b.append(s.charAt(i));
} else {
b.append('_');
}
}
return b.toString();
}
 
}
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/GS1Label.java
New file
0,0 → 1,16
package org.openconcerto.modules.label;
 
import org.openconcerto.modules.label.gs1.GS1AIElements;
 
public class GS1Label extends Label {
String text;
String text2;
GS1AIElements gs1;
 
public GS1Label(String text, String text2, GS1AIElements gs1) {
this.text = text;
this.text2 = text2;
this.gs1 = gs1;
}
 
}
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/48.png
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/48.png
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/LabelPanel.java
15,11 → 15,9
 
import javax.swing.JPanel;
 
import org.openconcerto.sql.model.SQLRowAccessor;
 
public class LabelPanel extends JPanel implements Printable {
 
private List<? extends SQLRowAccessor> list;
private List<? extends Label> list;
private int lines;
private int columns;
private Boolean[][] mask;
29,7 → 27,7
private int topMargin;
private int leftMargin;
 
public LabelPanel(List<? extends SQLRowAccessor> list, int l, int c, LabelRenderer labelRenderer) {
public LabelPanel(List<? extends Label> list, int l, int c, LabelRenderer labelRenderer) {
this.list = list;
this.renderer = labelRenderer;
setSizeLayoutSize(l, c);
48,6 → 46,11
});
}
 
public void setList(List<? extends Label> list) {
this.list = list;
repaint();
}
 
private void setSizeLayoutSize(int l, int c) {
this.lines = l;
this.columns = c;
102,7 → 105,7
g.drawRect(x, y, w, h);
// Label
if (rowIndex < list.size()) {
SQLRowAccessor row = list.get(rowIndex);
Label row = list.get(rowIndex);
renderer.paintLabel(g, row, x, y, w, h, 10f);
}
rowIndex++;
169,7 → 172,7
for (int j = 0; j < this.columns; j++) {
boolean mask = this.mask[i][j];
if (!mask && rowIndex < list.size()) {
SQLRowAccessor row = list.get(rowIndex);
Label row = list.get(rowIndex);
renderer.paintLabel(g, row, j * w + imageableX, i * h + imageableY, w, h, 10f);
}
rowIndex++;
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/96.png
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/96.png
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/ReedSolomon.java
New file
0,0 → 1,103
/*
* 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;
 
/**
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class ReedSolomon {
private int logmod;
private int rlen;
 
private int[] logt;
private int[] alog;
private int[] rspoly;
public int[] res;
 
public int getResult(final int count) {
return this.res[count];
}
 
public void init_gf(final int poly) {
int m, b, p, v;
 
// Find the top bit, and hence the symbol size
for (b = 1, m = 0; b <= poly; b <<= 1) {
m++;
}
b >>= 1;
m--;
 
// Calculate the log/alog tables
this.logmod = (1 << m) - 1;
this.logt = new int[this.logmod + 1];
this.alog = new int[this.logmod];
 
for (p = 1, v = 0; v < this.logmod; v++) {
this.alog[v] = p;
this.logt[p] = v;
p <<= 1;
if ((p & b) != 0) {
p ^= poly;
}
}
}
 
public void init_code(final int nsym, int index) {
int i, k;
 
this.rspoly = new int[nsym + 1];
 
this.rlen = nsym;
 
this.rspoly[0] = 1;
for (i = 1; i <= nsym; i++) {
this.rspoly[i] = 1;
for (k = i - 1; k > 0; k--) {
if (this.rspoly[k] != 0) {
this.rspoly[k] = this.alog[(this.logt[this.rspoly[k]] + index) % this.logmod];
}
this.rspoly[k] ^= this.rspoly[k - 1];
}
this.rspoly[0] = this.alog[(this.logt[this.rspoly[0]] + index) % this.logmod];
index++;
}
}
 
public void encode(final int len, final int[] data) {
int i, k, m;
 
this.res = new int[this.rlen];
for (i = 0; i < this.rlen; i++) {
this.res[i] = 0;
}
 
for (i = 0; i < len; i++) {
m = this.res[this.rlen - 1] ^ data[i];
for (k = this.rlen - 1; k > 0; k--) {
if (m != 0 && this.rspoly[k] != 0) {
this.res[k] = this.res[k - 1] ^ this.alog[(this.logt[m] + this.logt[this.rspoly[k]]) % this.logmod];
} else {
this.res[k] = this.res[k - 1];
}
}
if (m != 0 && this.rspoly[0] != 0) {
this.res[0] = this.alog[(this.logt[m] + this.logt[this.rspoly[0]]) % this.logmod];
} else {
this.res[0] = 0;
}
}
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/KixCode.java
New file
0,0 → 1,107
/*
* 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.awt.geom.Rectangle2D;
import java.util.Locale;
 
/**
* <p>
* Implements Dutch Post KIX Code as used by Royal Dutch TPG Post (Netherlands).
*
* <p>
* 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.
*
* <p>
* KIX Code is the same as RM4SCC, but without the check digit.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
* @see <a href="http://www.tntpost.nl/zakelijk/klantenservice/downloads/kIX_code/download.aspx">KIX
* Code Specification</a>
*/
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() {
 
this.content = this.content.toUpperCase(Locale.ENGLISH);
 
if (!this.content.matches("[0-9A-Z]+")) {
throw new OkapiException("Invalid characters in data");
}
 
final StringBuilder sb = new StringBuilder(this.content.length());
for (int i = 0; i < this.content.length(); i++) {
final int j = positionOf(this.content.charAt(i), KR_SET);
sb.append(ROYAL_TABLE[j]);
}
 
final String dest = sb.toString();
infoLine("Encoding: " + dest);
 
this.readable = "";
this.pattern = new String[] { dest };
this.row_count = 1;
this.row_height = new int[] { -1 };
}
 
@Override
protected void plotSymbol() {
int xBlock;
int x, y, w, h;
 
this.rectangles.clear();
x = 0;
w = 1;
y = 0;
h = 0;
for (xBlock = 0; xBlock < this.pattern[0].length(); xBlock++) {
final char c = this.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 IllegalStateException("Unknown pattern character: " + c);
}
this.rectangles.add(new Rectangle2D.Double(x, y, w, h));
x += 2;
}
this.symbol_width = (this.pattern[0].length() - 1) * 2 + 1; // final bar doesn't need extra
// whitespace
this.symbol_height = 8;
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Code93.java
New file
0,0 → 1,189
/*
* 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;
 
/**
* <p>
* Implements <a href="http://en.wikipedia.org/wiki/Code_93">Code 93</a>.
*
* <p>
* Supports encoding of 7-bit ASCII text. Two check digits are added.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
* @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
* <code>true</code>).
*
* @param showCheckDigits whether or not to show check digits in the human-readable text
*/
public void setShowCheckDigits(final 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 this.showCheckDigits;
}
 
/**
* Sets an optional start/stop delimiter to be shown in the human-readable text (defaults to
* <code>null</code>).
*
* @param startStopDelimiter an optional start/stop delimiter to be shown in the human-readable
* text
*/
public void setStartStopDelimiter(final 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 this.startStopDelimiter;
}
 
/** {@inheritDoc} */
@Override
protected void encode() {
 
final char[] controlChars = toControlChars(this.content);
int l = controlChars.length;
 
if (!this.content.matches("[\u0000-\u007F]+")) {
throw new OkapiException("Invalid characters in input data");
}
 
final int[] values = new int[controlChars.length + 2];
for (int i = 0; i < l; i++) {
values[i] = positionOf(controlChars[i], CODE_93_LOOKUP);
}
 
final int c = calculateCheckDigitC(values, l);
values[l] = c;
l++;
 
final int k = calculateCheckDigitK(values, l);
values[l] = k;
l++;
 
this.readable = this.content;
if (this.showCheckDigits) {
this.readable = this.readable + CODE_93_LOOKUP[c] + CODE_93_LOOKUP[k];
}
if (this.startStopDelimiter != null) {
this.readable = this.startStopDelimiter + this.readable + this.startStopDelimiter;
}
 
infoLine("Check Digit C: " + c);
infoLine("Check Digit K: " + k);
this.pattern = new String[] { toPattern(values) };
this.row_count = 1;
this.row_height = new int[] { -1 };
}
 
private static char[] toControlChars(final String s) {
final StringBuilder buffer = new StringBuilder();
final char[] chars = s.toCharArray();
for (int i = 0; i < chars.length; i++) {
final int asciiCode = chars[i];
buffer.append(CODE_93_CTRL[asciiCode]);
}
return buffer.toString().toCharArray();
}
 
private static int calculateCheckDigitC(final int[] values, final 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(final int[] values, final 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(final int[] values) {
final 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);
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/HumanReadableAlignment.java
New file
0,0 → 1,34
/*
* Copyright 2018 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;
 
/**
* The text alignment of a bar code's human-readable text.
*/
public enum HumanReadableAlignment {
 
/** Left-align the human-readable text. */
LEFT,
 
/** Right-align the human-readable text. */
RIGHT,
 
/** Center the human-readable text. */
CENTER,
 
/** Justify the human-readable text by adjusting the spaces between the characters. */
JUSTIFY
 
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Code49.java
New file
0,0 → 1,850
/*
* 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.awt.geom.Rectangle2D;
import java.nio.charset.StandardCharsets;
 
/**
* <p>
* Implements Code 49 according to ANSI/AIM-BC6-2000.
*
* <p>
* Supports full 7-bit ASCII input up to a maximum of 49 characters or 81 numeric digits. GS1 data
* encoding is also supported.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
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 i, codeword_count = 0, h, j, M, rows, pad_count = 0;
int x_count, y_count, z_count, posn_val, local_value;
String intermediate = "";
String localpattern = "";
final int[] codewords = new int[170];
final int[][] c_grid = new int[8][8];
final int[][] w_grid = new int[8][4];
 
if (!this.content.matches("[\u0000-\u007F]+")) {
throw new OkapiException("Invalid characters in input data");
}
 
this.inputData = toBytes(this.content, StandardCharsets.US_ASCII);
 
if (this.inputDataType == DataType.GS1) {
intermediate += "*"; // FNC1
}
 
for (i = 0; i < this.inputData.length; i++) {
final int c = this.inputData[i];
if (c == FNC1) {
intermediate += "*"; // FNC1
} else {
intermediate += C49_TABLE7[c];
}
}
 
h = intermediate.length();
 
i = 0;
do {
if (intermediate.charAt(i) >= '0' && intermediate.charAt(i) <= '9') {
 
/* Numeric data */
int latch = 0;
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);
 
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 new OkapiException("Input too long");
}
 
infoLine("Starting Mode (M): " + M);
 
/* Place codewords in code character array (c grid) */
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 (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 (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 */
j = 0;
for (i = 0; i < 7; i++) {
j += c_grid[rows - 1][i];
}
c_grid[rows - 1][7] = j % 49;
 
info("Codewords: ");
/* Transfer data to symbol character array (w grid) */
for (i = 0; i < rows; i++) {
for (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();
 
this.readable = "";
this.pattern = new String[rows];
this.row_count = rows;
this.row_height = new int[rows];
 
info("Symbol Characters: ");
for (i = 0; i < rows; i++) {
localpattern = "11"; /* Start character */
for (j = 0; j < 4; j++) {
infoSpace(w_grid[i][j]);
if (i != rows - 1) {
if (C49_TABLE4[i].charAt(j) == 'E') {
/* Even Parity */
localpattern += C49_APPXE_EVEN[w_grid[i][j]];
} else {
/* Odd Parity */
localpattern += C49_APPXE_ODD[w_grid[i][j]];
}
} else {
/* Last row uses all even parity */
localpattern += C49_APPXE_EVEN[w_grid[i][j]];
}
}
localpattern += "4"; /* Stop character */
 
this.pattern[i] = localpattern;
this.row_height[i] = 10;
 
}
infoLine();
}
 
@Override
protected void plotSymbol() {
 
int xBlock, yBlock;
int x, y, w, h;
boolean black;
 
this.rectangles.clear();
y = 1;
h = 1;
for (yBlock = 0; yBlock < this.row_count; yBlock++) {
black = true;
x = 15;
for (xBlock = 0; xBlock < this.pattern[yBlock].length(); xBlock++) {
if (black) {
black = false;
w = this.pattern[yBlock].charAt(xBlock) - '0';
if (this.row_height[yBlock] == -1) {
h = this.default_height;
} else {
h = this.row_height[yBlock];
}
if (w != 0 && h != 0) {
final Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h);
this.rectangles.add(rect);
}
if (x + w > this.symbol_width) {
this.symbol_width = x + w;
}
} else {
black = true;
}
x += this.pattern[yBlock].charAt(xBlock) - '0';
}
y += h;
if (y > this.symbol_height) {
this.symbol_height = y;
}
/* Add bars between rows */
if (yBlock != this.row_count - 1) {
final Rectangle2D.Double rect = new Rectangle2D.Double(15, y - 1, this.symbol_width - 15, 2);
this.rectangles.add(rect);
}
}
 
/* Add top and bottom binding bars */
final Rectangle2D.Double top = new Rectangle2D.Double(0, 0, this.symbol_width + 15, 2);
this.rectangles.add(top);
final Rectangle2D.Double bottom = new Rectangle2D.Double(0, y - 1, this.symbol_width + 15, 2);
this.rectangles.add(bottom);
this.symbol_width += 15;
this.symbol_height += 1;
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/MaxiCode.java
New file
0,0 → 1,913
/*
* 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.awt.geom.Ellipse2D;
import java.util.Arrays;
 
/**
* <p>
* Implements MaxiCode according to ISO 16023:2000.
*
* <p>
* 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.
*
* <p>
* TODO: Add ECI functionality.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
* @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 final int[] set = new int[144];
private final int[] character = new int[144];
private final 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(final 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 this.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(final 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 <code>1</code>.
*
* @return the position of this MaxiCode symbol in a series of symbols using structured append
*/
public int getStructuredAppendPosition() {
return this.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(final 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
* <code>1</code>.
*
* @return size of the series that this symbol is part of
*/
public int getStructuredAppendTotal() {
return this.structuredAppendTotal;
}
 
/**
* Sets the primary data. Should only be used for modes 2 and 3. Must conform to the following
* structure:
*
* <table summary="Expected primary data structure.">
* <tr>
* <th>Characters</th>
* <th>Meaning</th>
* </tr>
* <tr>
* <td>1-9</td>
* <td>Postal 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).</td>
* </tr>
* <tr>
* <td>10-12</td>
* <td>Three-digit country code according to ISO-3166.</td>
* </tr>
* <tr>
* <td>13-15</td>
* <td>Three digit service code. This depends on your parcel courier.</td>
* </tr>
* </table>
*
* @param primary the primary data
*/
public void setPrimary(final String primary) {
this.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 this.primaryData;
}
 
/** {@inheritDoc} */
@Override
protected void encode() {
 
eciProcess();
 
// mode 2 -> mode 3 if postal code isn't strictly numeric
if (this.mode == 2) {
for (int i = 0; i < 10 && i < this.primaryData.length(); i++) {
if (this.primaryData.charAt(i) < '0' || this.primaryData.charAt(i) > '9') {
this.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
this.codewords = Arrays.copyOf(this.character, this.character.length);
if (this.mode == 2 || this.mode == 3) {
final int[] primary = getPrimaryCodewords();
this.codewords = insertArray(this.codewords, 0, primary);
} else {
this.codewords = insertArray(this.codewords, 0, new int[] { this.mode });
}
 
// insert structured append flag if necessary
if (this.structuredAppendTotal > 1) {
 
final int[] flag = new int[2];
flag[0] = 33; // padding
flag[1] = this.structuredAppendPosition - 1 << 3 | this.structuredAppendTotal - 1; // position
// +
// total
 
int index;
if (this.mode == 2 || this.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)
}
 
this.codewords = insertArray(this.codewords, index, flag);
}
 
int secondaryMax, secondaryECMax;
if (this.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
final int totalMax = secondaryMax + 10;
if (this.codewords.length > totalMax) {
this.codewords = Arrays.copyOfRange(this.codewords, 0, totalMax);
}
 
// insert primary error correction between primary message and secondary message (always
// EEC)
final int[] primary = Arrays.copyOfRange(this.codewords, 0, 10);
final int[] primaryCheck = getErrorCorrection(primary, 10);
this.codewords = insertArray(this.codewords, 10, primaryCheck);
 
// calculate secondary error correction
final int[] secondary = Arrays.copyOfRange(this.codewords, 20, this.codewords.length);
final int[] secondaryOdd = new int[secondary.length / 2];
final 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];
}
}
final int[] secondaryECOdd = getErrorCorrection(secondaryOdd, secondaryECMax / 2);
final int[] secondaryECEven = getErrorCorrection(secondaryEven, secondaryECMax / 2);
 
// add secondary error correction after secondary message
this.codewords = Arrays.copyOf(this.codewords, this.codewords.length + secondaryECOdd.length + secondaryECEven.length);
for (int i = 0; i < secondaryECOdd.length; i++) {
this.codewords[20 + secondaryMax + 2 * i + 1] = secondaryECOdd[i];
}
for (int i = 0; i < secondaryECEven.length; i++) {
this.codewords[20 + secondaryMax + 2 * i] = secondaryECEven[i];
}
 
infoLine("Mode: " + this.mode);
infoLine("ECC Codewords: " + secondaryECMax);
info("Codewords: ");
for (int i = 0; i < this.codewords.length; i++) {
infoSpace(this.codewords[i]);
}
infoLine();
 
// copy data into symbol grid
final int[] bit_pattern = new int[7];
for (int i = 0; i < 33; i++) {
for (int j = 0; j < 30; j++) {
 
final int block = (MAXICODE_GRID[i * 30 + j] + 5) / 6;
final int bit = (MAXICODE_GRID[i * 30 + j] + 5) % 6;
 
if (block != 0) {
 
bit_pattern[0] = (this.codewords[block - 1] & 0x20) >> 5;
bit_pattern[1] = (this.codewords[block - 1] & 0x10) >> 4;
bit_pattern[2] = (this.codewords[block - 1] & 0x8) >> 3;
bit_pattern[3] = (this.codewords[block - 1] & 0x4) >> 2;
bit_pattern[4] = (this.codewords[block - 1] & 0x2) >> 1;
bit_pattern[5] = this.codewords[block - 1] & 0x1;
 
if (bit_pattern[bit] != 0) {
this.grid[i][j] = true;
} else {
this.grid[i][j] = false;
}
}
}
}
 
// add orientation markings
this.grid[0][28] = true; // top right filler
this.grid[0][29] = true;
this.grid[9][10] = true; // top left marker
this.grid[9][11] = true;
this.grid[10][11] = true;
this.grid[15][7] = true; // left hand marker
this.grid[16][8] = true;
this.grid[16][20] = true; // right hand marker
this.grid[17][20] = true;
this.grid[22][10] = true; // bottom left marker
this.grid[23][10] = true;
this.grid[22][17] = true; // bottom right marker
this.grid[23][17] = true;
 
// the following is provided for compatibility, but the results are not useful
this.row_count = 33;
this.readable = "";
this.pattern = new String[33];
this.row_height = new int[33];
for (int i = 0; i < 33; i++) {
final StringBuilder bin = new StringBuilder(30);
for (int j = 0; j < 30; j++) {
if (this.grid[i][j]) {
bin.append("1");
} else {
bin.append("0");
}
}
this.pattern[i] = bin2pat(bin);
this.row_height[i] = 1;
}
this.symbol_height = 72;
this.symbol_width = 74;
}
 
/**
* 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 this.mode == 2 || this.mode == 3;
 
if (this.primaryData.length() != 15) {
throw new OkapiException("Invalid Primary String");
}
 
for (int i = 9; i < 15; i++) { /* check that country code and service are numeric */
if (this.primaryData.charAt(i) < '0' || this.primaryData.charAt(i) > '9') {
throw new OkapiException("Invalid Primary String");
}
}
 
String postcode;
if (this.mode == 2) {
postcode = this.primaryData.substring(0, 9);
final int index = postcode.indexOf(' ');
if (index != -1) {
postcode = postcode.substring(0, index);
}
} else {
assert this.mode == 3;
postcode = this.primaryData.substring(0, 6);
}
 
final int country = Integer.parseInt(this.primaryData.substring(9, 12));
final int service = Integer.parseInt(this.primaryData.substring(12, 15));
 
infoLine("Postal Code: " + postcode);
infoLine("Country Code: " + country);
infoLine("Service: " + service);
 
if (this.mode == 2) {
return getMode2PrimaryCodewords(postcode, country, service);
} else {
assert this.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, final int country, final 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;
}
}
 
final int postcodeNum = Integer.parseInt(postcode);
 
final 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, final int country, final int service) {
 
final 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')
}
 
final 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 = this.content.length();
int i, j, count, current_set;
 
if (length > 138) {
throw new OkapiException("Input data too long");
}
 
for (i = 0; i < 144; i++) {
this.set[i] = -1;
this.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
*/
this.set[i] = MAXICODE_SET[this.inputData[i]];
this.character[i] = MAXICODE_SYMBOL_CHAR[this.inputData[i]];
}
 
// If a character can be represented in more than one code set, pick which version to use.
if (this.set[0] == 0) {
if (this.character[0] == 13) {
this.character[0] = 0;
}
this.set[0] = 1;
}
 
for (i = 1; i < length; i++) {
if (this.set[i] == 0) {
/* Special character that can be represented in more than one code set. */
if (this.character[i] == 13) {
/* Carriage Return */
this.set[i] = bestSurroundingSet(i, length, 1, 5);
if (this.set[i] == 5) {
this.character[i] = 13;
} else {
this.character[i] = 0;
}
} else if (this.character[i] == 28) {
/* FS */
this.set[i] = bestSurroundingSet(i, length, 1, 2, 3, 4, 5);
if (this.set[i] == 5) {
this.character[i] = 32;
}
} else if (this.character[i] == 29) {
/* GS */
this.set[i] = bestSurroundingSet(i, length, 1, 2, 3, 4, 5);
if (this.set[i] == 5) {
this.character[i] = 33;
}
} else if (this.character[i] == 30) {
/* RS */
this.set[i] = bestSurroundingSet(i, length, 1, 2, 3, 4, 5);
if (this.set[i] == 5) {
this.character[i] = 34;
}
} else if (this.character[i] == 32) {
/* Space */
this.set[i] = bestSurroundingSet(i, length, 1, 2, 3, 4, 5);
if (this.set[i] == 1) {
this.character[i] = 32;
} else if (this.set[i] == 2) {
this.character[i] = 47;
} else {
this.character[i] = 59;
}
} else if (this.character[i] == 44) {
/* Comma */
this.set[i] = bestSurroundingSet(i, length, 1, 2);
if (this.set[i] == 2) {
this.character[i] = 48;
}
} else if (this.character[i] == 46) {
/* Full Stop */
this.set[i] = bestSurroundingSet(i, length, 1, 2);
if (this.set[i] == 2) {
this.character[i] = 49;
}
} else if (this.character[i] == 47) {
/* Slash */
this.set[i] = bestSurroundingSet(i, length, 1, 2);
if (this.set[i] == 2) {
this.character[i] = 50;
}
} else if (this.character[i] == 58) {
/* Colon */
this.set[i] = bestSurroundingSet(i, length, 1, 2);
if (this.set[i] == 2) {
this.character[i] = 51;
}
}
}
}
 
for (i = length; i < this.set.length; i++) {
/* Add the padding */
if (this.set[length - 1] == 2) {
this.set[i] = 2;
} else {
this.set[i] = 1;
}
this.character[i] = 33;
}
 
/*
* Find candidates for number compression (not allowed in primary message in modes 2 and 3).
*/
if (this.mode == 2 || this.mode == 3) {
j = 9;
} else {
j = 0;
}
count = 0;
for (i = j; i < 143; i++) {
if (this.set[i] == 1 && this.character[i] >= 48 && this.character[i] <= 57) {
/* Character is a number */
count++;
} else {
count = 0;
}
if (count == 9) {
/* Nine digits in a row can be compressed */
this.set[i] = 6;
this.set[i - 1] = 6;
this.set[i - 2] = 6;
this.set[i - 3] = 6;
this.set[i - 4] = 6;
this.set[i - 5] = 6;
this.set[i - 6] = 6;
this.set[i - 7] = 6;
this.set[i - 8] = 6;
count = 0;
}
}
 
/* Add shift and latch characters */
current_set = 1;
i = 0;
do {
if (this.set[i] != current_set && this.set[i] != 6) {
switch (this.set[i]) {
case 1:
if (i + 1 < this.set.length && this.set[i + 1] == 1) {
if (i + 2 < this.set.length && this.set[i + 2] == 1) {
if (i + 3 < this.set.length && this.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 < this.set.length && this.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 < this.set.length && this.set[i + 1] == 3 && this.set[i + 2] == 3 && this.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 < this.set.length && this.set[i + 1] == 4 && this.set[i + 2] == 4 && this.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 < this.set.length && this.set[i + 1] == 5 && this.set[i + 2] == 5 && this.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 OkapiException("Unexpected set " + this.set[i] + " at index " + i + ".");
}
i++;
}
i++;
} while (i < this.set.length);
 
/* Number compression has not been forgotten! It's handled below. */
i = 0;
do {
if (this.set[i] == 6) {
/* Number compression */
int value = 0;
for (j = 0; j < 9; j++) {
value *= 10;
value += this.character[i + j] - '0';
}
this.character[i] = 31; /* NS */
this.character[i + 1] = (value & 0x3f000000) >> 24;
this.character[i + 2] = (value & 0xfc0000) >> 18;
this.character[i + 3] = (value & 0x3f000) >> 12;
this.character[i + 4] = (value & 0xfc0) >> 6;
this.character[i + 5] = value & 0x3f;
i += 6;
for (j = i; j < 140; j++) {
this.set[j] = this.set[j + 3];
this.character[j] = this.character[j + 3];
}
length -= 3;
} else {
i++;
}
} while (i < this.set.length);
 
/* Inject ECI codes to beginning of data, according to Table 3 */
if (this.eciMode != 3) {
insert(0, 27); // ECI
 
if (this.eciMode >= 0 && this.eciMode <= 31) {
insert(1, this.eciMode & 0x1F);
length += 2;
}
 
if (this.eciMode >= 32 && this.eciMode <= 1023) {
insert(1, 0x20 + (this.eciMode >> 6));
insert(2, this.eciMode & 0x3F);
length += 3;
}
 
if (this.eciMode >= 1024 && this.eciMode <= 32767) {
insert(1, 0x30 + (this.eciMode >> 12));
insert(2, this.eciMode >> 6 & 0x3F);
insert(3, this.eciMode & 0x3F);
length += 4;
}
 
if (this.eciMode >= 32768 && this.eciMode <= 999999) {
insert(1, 0x38 + (this.eciMode >> 18));
insert(2, this.eciMode >> 12 & 0x3F);
insert(3, this.eciMode >> 6 & 0x3F);
insert(4, this.eciMode & 0x3F);
length += 5;
}
}
 
/* Make sure we haven't exceeded the maximum data length. */
int maxLength;
if (this.mode == 2 || this.mode == 3) {
maxLength = 84;
} else if (this.mode == 4 || this.mode == 6) {
maxLength = 93;
} else if (this.mode == 5) {
maxLength = 77;
} else {
maxLength = 0; // impossible
}
if (length > maxLength) {
throw new OkapiException("Input data too long");
}
}
 
/**
* 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(final int index, final int length, final int... valid) {
final int option1 = this.set[index - 1];
if (index + 1 < length) {
// we have two options to check
final int option2 = this.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(final int position, final int c) {
for (int i = 143; i > position; i--) {
this.set[i] = this.set[i - 1];
this.character[i] = this.character[i - 1];
}
this.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(final int[] codewords, final int ecclen) {
 
final ReedSolomon rs = new ReedSolomon();
rs.init_gf(0x43);
rs.init_code(ecclen, 1);
rs.encode(codewords.length, codewords);
 
final 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() {
 
// hexagons
for (int row = 0; row < 33; row++) {
for (int col = 0; col < 30; col++) {
if (this.grid[row][col]) {
double x = 2.46 * col + 1.23;
if ((row & 1) != 0) {
x += 1.23;
}
final double y = 2.135 * row + 1.43;
this.hexagons.add(new Hexagon(x, y));
}
}
}
 
// circles
final double[] radii = { 10.85, 8.97, 7.10, 5.22, 3.31, 1.43 };
for (int i = 0; i < radii.length; i++) {
final Ellipse2D.Double circle = new Ellipse2D.Double();
circle.setFrameFromCenter(35.76, 35.60, 35.76 + radii[i], 35.60 + radii[i]);
this.target.add(circle);
}
}
 
/** {@inheritDoc} */
@Override
protected int[] getCodewords() {
return this.codewords;
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Codabar.java
New file
0,0 → 1,95
/*
* 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;
 
/**
* <p>
* Implements Codabar barcode symbology according to BS EN 798:1996.
*
* <p>
* Also known as NW-7, Monarch, ABC Codabar, USD-4, Ames Code and Code 27. Codabar can encode any
* length string starting and ending with the letters A-D and containing between these letters the
* numbers 0-9, dash (-), dollar ($), colon (:), slash (/), full stop (.) or plus (+). No check
* digit is generated.
*
* @author <a href="mailto:jakel2006@me.com">Robert Elliott</a>
*/
public class Codabar extends Symbol {
 
private static final String[] CODABAR_TABLE = { "11111221", "11112211", "11121121", "22111111", "11211211", "21111211", "12111121", "12112111", "12211111", "21121111", "11122111", "11221111",
"21112121", "21211121", "21212111", "11212121", "11221211", "12121121", "11121221", "11122211" };
 
private static final char[] CHARACTER_SET = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '$', ':', '/', '.', '+', 'A', 'B', 'C', 'D' };
 
/** Ratio of wide bar width to narrow bar width. */
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(final 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 this.moduleWidthRatio;
}
 
/** {@inheritDoc} */
@Override
protected void encode() {
 
if (!this.content.matches("[A-D]{1}[0-9:/\\$\\.\\+\u002D]+[A-D]{1}")) {
throw new OkapiException("Invalid characters in input");
}
 
String horizontalSpacing = "";
 
final int l = this.content.length();
for (int i = 0; i < l; i++) {
horizontalSpacing += CODABAR_TABLE[positionOf(this.content.charAt(i), CHARACTER_SET)];
}
 
this.readable = this.content;
this.pattern = new String[] { horizontalSpacing };
this.row_count = 1;
this.row_height = new int[] { -1 };
}
 
/** {@inheritDoc} */
@Override
protected double getModuleWidth(final int originalWidth) {
if (originalWidth == 1) {
return 1;
} else {
return this.moduleWidthRatio;
}
}
 
/** {@inheritDoc} */
@Override
protected int[] getCodewords() {
return getPatternAsCodewords(8);
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/AustraliaPost.java
New file
0,0 → 1,481
/*
* 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.awt.geom.Rectangle2D;
 
/**
* Implements the <a href=
* "http://auspost.com.au/media/documents/a-guide-to-printing-the-4state-barcode-v31-mar2012.pdf">Australia
* Post 4-State barcode</a>.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class AustraliaPost extends Symbol {
 
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 enum ausMode {
AUSPOST, AUSREPLY, AUSROUTE, AUSREDIRECT
}
 
private ausMode mode;
 
public AustraliaPost() {
this.mode = ausMode.AUSPOST;
}
 
/**
* Specify encoding of 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.
* <p>
* Input data should include a 8-digit Deliver Point ID (DPID) optionally followed by customer
* information as shown below.
* <table summary="Permitted Australia Post input data">
* <tbody>
* <tr>
* <th>
* <p>
* Input Length
* </p>
* </th>
* <th>
* <p>
* Required Input Format
* </p>
* </th>
* <th>
* <p>
* Symbol Length
* </p>
* </th>
* <th>
* <p>
* FCC
* </p>
* </th>
* <th>
* <p>
* Encoding Table
* </p>
* </th>
* </tr>
* <tr>
* <td>
* <p>
* 8
* </p>
* </td>
* <td>
* <p>
* 99999999
* </p>
* </td>
* <td>
* <p>
* 37-bar
* </p>
* </td>
* <td>
* <p>
* 11
* </p>
* </td>
* <td>
* <p>
* None
* </p>
* </td>
* </tr>
* <tr>
* <td>
* <p>
* 13
* </p>
* </td>
* <td>
* <p>
* 99999999AAAAA
* </p>
* </td>
* <td>
* <p>
* 52-bar
* </p>
* </td>
* <td>
* <p>
* 59
* </p>
* </td>
* <td>
* <p>
* C
* </p>
* </td>
* </tr>
* <tr>
* <td>
* <p>
* 16
* </p>
* </td>
* <td>
* <p>
* 9999999999999999
* </p>
* </td>
* <td>
* <p>
* 52-bar
* </p>
* </td>
* <td>
* <p>
* 59
* </p>
* </td>
* <td>
* <p>
* N
* </p>
* </td>
* </tr>
* <tr>
* <td>
* <p>
* 18
* </p>
* </td>
* <td>
* <p>
* 99999999AAAAAAAAAA
* </p>
* </td>
* <td>
* <p>
* 67-bar
* </p>
* </td>
* <td>
* <p>
* 62
* </p>
* </td>
* <td>
* <p>
* C
* </p>
* </td>
* </tr>
* <tr>
* <td>
* <p>
* 23
* </p>
* </td>
* <td>
* <p>
* 99999999999999999999999
* </p>
* </td>
* <td>
* <p>
* 67-bar
* </p>
* </td>
* <td>
* <p>
* 62
* </p>
* </td>
* <td>
* <p>
* N
* </p>
* </td>
* </tr>
* </tbody>
* </table>
*/
public void setPostMode() {
this.mode = ausMode.AUSPOST;
}
 
/**
* Specify encoding of a Reply Paid version of the Australia Post 4-State Barcode (FCC 45) which
* requires an 8-digit DPID input.
*/
public void setReplyMode() {
this.mode = ausMode.AUSREPLY;
}
 
/**
* Specify encoding of a Routing version of the Australia Post 4-State Barcode (FCC 87) which
* requires an 8-digit DPID input.
*/
public void setRouteMode() {
this.mode = ausMode.AUSROUTE;
}
 
/**
* Specify encoding of a Redirection version of the Australia Post 4-State Barcode (FCC 92)
* which requires an 8-digit DPID input.
*/
public void setRedirectMode() {
this.mode = ausMode.AUSREDIRECT;
}
 
/** {@inheritDoc} */
@Override
protected void encode() {
String formatControlCode = "00";
String deliveryPointId;
String barStateValues;
String zeroPaddedInput = "";
int i;
 
switch (this.mode) {
case AUSPOST:
switch (this.content.length()) {
case 8:
formatControlCode = "11";
break;
case 13:
formatControlCode = "59";
break;
case 16:
formatControlCode = "59";
if (!this.content.matches("[0-9]+")) {
throw new OkapiException("Invalid characters in data");
}
break;
case 18:
formatControlCode = "62";
break;
case 23:
formatControlCode = "62";
if (!this.content.matches("[0-9]+")) {
throw new OkapiException("Invalid characters in data");
}
break;
default:
throw new OkapiException("Auspost input is wrong length");
}
break;
case AUSREPLY:
if (this.content.length() > 8) {
throw new OkapiException("Auspost input is too long");
} else {
formatControlCode = "45";
}
break;
case AUSROUTE:
if (this.content.length() > 8) {
throw new OkapiException("Auspost input is too long");
} else {
formatControlCode = "87";
}
break;
case AUSREDIRECT:
if (this.content.length() > 8) {
throw new OkapiException("Auspost input is too long");
} else {
formatControlCode = "92";
}
break;
}
 
infoLine("FCC: " + formatControlCode);
 
if (this.mode != ausMode.AUSPOST) {
for (i = this.content.length(); i < 8; i++) {
zeroPaddedInput += "0";
}
}
zeroPaddedInput += this.content;
 
if (!this.content.matches("[0-9A-Za-z #]+")) {
throw new OkapiException("Invalid characters in data");
}
 
/* Verify that the first 8 characters are numbers */
deliveryPointId = zeroPaddedInput.substring(0, 8);
 
if (!deliveryPointId.matches("[0-9]+")) {
throw new OkapiException("Invalid characters in DPID");
}
 
infoLine("DPID: " + deliveryPointId);
 
/* Start */
barStateValues = "13";
 
/* Encode the FCC */
for (i = 0; i < 2; i++) {
barStateValues += N_ENCODING_TABLE[formatControlCode.charAt(i) - '0'];
}
 
/* Delivery Point Identifier (DPID) */
for (i = 0; i < 8; i++) {
barStateValues += N_ENCODING_TABLE[deliveryPointId.charAt(i) - '0'];
}
 
/* Customer Information */
switch (zeroPaddedInput.length()) {
case 13:
case 18:
for (i = 8; i < zeroPaddedInput.length(); i++) {
barStateValues += C_ENCODING_TABLE[positionOf(zeroPaddedInput.charAt(i), CHARACTER_SET)];
}
break;
case 16:
case 23:
for (i = 8; i < zeroPaddedInput.length(); i++) {
barStateValues += N_ENCODING_TABLE[positionOf(zeroPaddedInput.charAt(i), CHARACTER_SET)];
}
break;
}
 
/* Filler bar */
switch (barStateValues.length()) {
case 22:
case 37:
case 52:
barStateValues += "3";
break;
}
 
/* Reed Solomon error correction */
barStateValues += calcReedSolomon(barStateValues);
 
/* Stop character */
barStateValues += "13";
 
infoLine("Total Length: " + barStateValues.length());
info("Encoding: ");
for (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();
 
this.readable = "";
this.pattern = new String[1];
this.pattern[0] = barStateValues;
this.row_count = 1;
this.row_height = new int[1];
this.row_height[0] = -1;
}
 
private String calcReedSolomon(final String oldBarStateValues) {
final ReedSolomon rs = new ReedSolomon();
String newBarStateValues = "";
 
/* Adds Reed-Solomon error correction to auspost */
 
int barStateCount;
int tripleValueCount = 0;
final 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);
}
 
rs.init_gf(0x43);
rs.init_code(4, 1);
rs.encode(tripleValueCount, tripleValue);
 
for (barStateCount = 4; barStateCount > 0; barStateCount--) {
newBarStateValues += BAR_VALUE_TABLE[rs.getResult(barStateCount - 1)];
}
 
return newBarStateValues;
}
 
private int barStateToDecimal(final char oldBarStateValues, final int shift) {
return oldBarStateValues - '0' << shift;
}
 
/** {@inheritDoc} */
@Override
protected void plotSymbol() {
int xBlock;
int x, y, w, h;
 
this.rectangles.clear();
x = 0;
w = 1;
y = 0;
h = 0;
for (xBlock = 0; xBlock < this.pattern[0].length(); xBlock++) {
switch (this.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;
}
 
final Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h);
this.rectangles.add(rect);
 
x += 2;
}
 
this.symbol_width = (this.pattern[0].length() - 1) * 2 + 1; // no whitespace needed after
// the final bar
this.symbol_height = 8;
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Telepen.java
New file
0,0 → 1,166
/*
* 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;
 
/**
* <p>
* Implements Telepen (also known as Telepen Alpha).
*
* <p>
* 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 <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
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(final Mode mode) {
this.mode = mode;
}
 
public Mode getMode() {
return this.mode;
}
 
@Override
protected void encode() {
if (this.mode == Mode.NORMAL) {
normal_mode();
} else {
numeric_mode();
}
}
 
private void normal_mode() {
int count = 0, asciicode, check_digit;
String p = "";
String dest;
 
final int l = this.content.length();
 
if (!this.content.matches("[\u0000-\u007F]+")) {
throw new OkapiException("Invalid characters in input data");
}
 
dest = TELE_TABLE['_']; // Start
for (int i = 0; i < l; i++) {
asciicode = this.content.charAt(i);
p += TELE_TABLE[asciicode];
count += asciicode;
}
 
check_digit = 127 - count % 127;
if (check_digit == 127) {
check_digit = 0;
}
 
p += TELE_TABLE[check_digit];
 
infoLine("Check Digit: " + check_digit);
 
dest += p;
dest += TELE_TABLE['z']; // Stop
 
this.readable = this.content;
this.pattern = new String[1];
this.pattern[0] = dest;
this.row_count = 1;
this.row_height = new int[1];
this.row_height[0] = -1;
}
 
private void numeric_mode() {
int count = 0, check_digit;
String p = "";
String t;
String dest;
final int l = this.content.length();
int tl, glyph;
char c1, c2;
 
// FIXME: Ensure no extended ASCII or Unicode characters are entered
if (!this.content.matches("[0-9X]+")) {
throw new OkapiException("Invalid characters in input");
}
 
/* If input is an odd length, add a leading zero */
if ((l & 1) == 1) {
t = "0" + this.content;
tl = l + 1;
} else {
t = this.content;
tl = l;
}
 
dest = TELE_TABLE['_']; // Start
for (int i = 0; i < tl; i += 2) {
 
c1 = t.charAt(i);
c2 = t.charAt(i + 1);
 
/* Input nX is allowed, but Xn is not */
if (c1 == 'X') {
throw new OkapiException("Invalid position of X in data");
}
 
if (c2 == 'X') {
glyph = c1 - '0' + 17;
count += glyph;
} else {
glyph = 10 * (c1 - '0') + c2 - '0' + 27;
count += glyph;
}
 
p += TELE_TABLE[glyph];
}
 
check_digit = 127 - count % 127;
if (check_digit == 127) {
check_digit = 0;
}
 
p += TELE_TABLE[check_digit];
 
infoLine("Check Digit: " + check_digit);
 
dest += p;
dest += TELE_TABLE['z']; // Stop
this.readable = this.content;
this.pattern = new String[1];
this.pattern[0] = dest;
this.row_count = 1;
this.row_height = new int[1];
this.row_height[0] = -1;
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Postnet.java
New file
0,0 → 1,156
/*
* 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.awt.geom.Rectangle2D;
 
/**
* <p>
* Implements <a href="http://en.wikipedia.org/wiki/POSTNET">POSTNET</a> and
* <a href="http://en.wikipedia.org/wiki/Postal_Alpha_Numeric_Encoding_Technique">PLANET</a> bar
* code symbologies.
*
* <p>
* POSTNET and PLANET both use numerical input data and include a modulo-10 check digit.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
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;
 
public Postnet() {
this.mode = Mode.POSTNET;
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(final 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 this.mode;
}
 
@Override
protected void encode() {
final String[] table = this.mode == Mode.POSTNET ? PN_TABLE : PL_TABLE;
encode(table);
}
 
private void encode(final String[] table) {
int i, sum, check_digit;
String dest;
 
if (this.content.length() > 38) {
throw new OkapiException("Input too long");
}
 
if (!this.content.matches("[0-9]+")) {
throw new OkapiException("Invalid characters in data");
}
 
sum = 0;
dest = "L";
 
for (i = 0; i < this.content.length(); i++) {
dest += table[this.content.charAt(i) - '0'];
sum += this.content.charAt(i) - '0';
}
 
check_digit = (10 - sum % 10) % 10;
infoLine("Check Digit: " + check_digit);
 
dest += table[check_digit];
dest += "L";
 
infoLine("Encoding: " + dest);
this.readable = this.content;
this.pattern = new String[] { dest };
this.row_count = 1;
this.row_height = new int[] { -1 };
}
 
@Override
protected void plotSymbol() {
int xBlock, shortHeight;
double x, y, w, h;
 
this.rectangles.clear();
this.texts.clear();
 
int baseY;
if (this.humanReadableLocation == TOP) {
baseY = getTheoreticalHumanReadableHeight();
} else {
baseY = 0;
}
 
x = 0;
w = this.moduleWidth;
shortHeight = (int) (0.4 * this.default_height);
for (xBlock = 0; xBlock < this.pattern[0].length(); xBlock++) {
if (this.pattern[0].charAt(xBlock) == 'L') {
y = baseY;
h = this.default_height;
} else {
y = baseY + this.default_height - shortHeight;
h = shortHeight;
}
this.rectangles.add(new Rectangle2D.Double(x, y, w, h));
x += 2.5 * w;
}
 
this.symbol_width = (int) Math.ceil((this.pattern[0].length() - 1) * 2.5 * w + w); // final
// bar
// doesn't
// need
// extra
// whitespace
this.symbol_height = this.default_height;
 
if (this.humanReadableLocation != NONE && !this.readable.isEmpty()) {
double baseline;
if (this.humanReadableLocation == TOP) {
baseline = this.fontSize;
} else {
baseline = this.symbol_height + this.fontSize;
}
this.texts.add(new TextBox(0, baseline, this.symbol_width, this.readable, this.humanReadableAlignment));
}
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/TextBox.java
New file
0,0 → 1,62
/*
* 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.backend;
 
/**
* A simple text item class.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
* @author Daniel Gredler
*/
public 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 HumanReadableAlignment 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(final double x, final double y, final double width, final String text, final HumanReadableAlignment alignment) {
this.x = x;
this.y = y;
this.width = width;
this.text = text;
this.alignment = alignment;
}
 
/** {@inheritDoc} */
@Override
public String toString() {
return "TextBox[x=" + this.x + ", y=" + this.y + ", width=" + this.width + ", text=" + this.text + ", alignment=" + this.alignment + "]";
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/MsiPlessey.java
New file
0,0 → 1,205
/*
* 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;
 
/**
* <p>
* Implements the MSI (Modified Plessey) bar code symbology.
*
* <p>
* MSI Plessey can encode a string of numeric digits and has a range of check digit options.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
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;
 
/**
* Set the check digit scheme to use. Options are: None, Modulo-10, 2 x Modulo-10, Modulo-11 and
* Modulo-11 &amp; 10.
*
* @param checkDigit the type of check digit to add to symbol
*/
public void setCheckDigit(final CheckDigit checkDigit) {
this.checkDigit = checkDigit;
}
 
/**
* Returns the check digit scheme being used.
*
* @return the check digit scheme being used
*/
public CheckDigit getCheckDigit() {
return this.checkDigit;
}
 
@Override
protected void encode() {
 
String intermediate;
final int length = this.content.length();
int i;
String evenString;
String oddString;
String addupString;
int spacer;
int addup;
int weight;
int checkDigit1;
int checkDigit2;
 
if (!this.content.matches("[0-9]+")) {
throw new OkapiException("Invalid characters in input");
}
 
intermediate = "21"; // Start
for (i = 0; i < length; i++) {
intermediate += MSI_PLESS_TABLE[Character.getNumericValue(this.content.charAt(i))];
}
 
this.readable = this.content;
 
if (this.checkDigit == CheckDigit.MOD10 || this.checkDigit == CheckDigit.MOD10_MOD10) {
/* Add Modulo-10 check digit */
evenString = "";
oddString = "";
 
spacer = this.content.length() & 1;
 
for (i = this.content.length() - 1; i >= 0; i--) {
if (spacer == 1) {
if ((i & 1) != 0) {
evenString = this.content.charAt(i) + evenString;
} else {
oddString = this.content.charAt(i) + oddString;
}
} else {
if ((i & 1) != 0) {
oddString = this.content.charAt(i) + oddString;
} else {
evenString = this.content.charAt(i) + evenString;
}
}
}
 
if (oddString.length() == 0) {
addupString = "0";
} else {
addupString = Integer.toString(Integer.parseInt(oddString) * 2);
}
 
addupString += evenString;
 
addup = 0;
for (i = 0; i < addupString.length(); i++) {
addup += addupString.charAt(i) - '0';
}
 
checkDigit1 = 10 - addup % 10;
if (checkDigit1 == 10) {
checkDigit1 = 0;
}
 
intermediate += MSI_PLESS_TABLE[checkDigit1];
this.readable += checkDigit1;
}
 
if (this.checkDigit == CheckDigit.MOD11 || this.checkDigit == CheckDigit.MOD11_MOD10) {
/* Add a Modulo-11 check digit */
weight = 2;
addup = 0;
for (i = this.content.length() - 1; i >= 0; i--) {
addup += (this.content.charAt(i) - '0') * weight;
weight++;
 
if (weight == 8) {
weight = 2;
}
}
 
checkDigit1 = 11 - addup % 11;
 
if (checkDigit1 == 11) {
checkDigit1 = 0;
}
 
this.readable += checkDigit1;
if (checkDigit1 == 10) {
intermediate += MSI_PLESS_TABLE[1];
intermediate += MSI_PLESS_TABLE[0];
} else {
intermediate += MSI_PLESS_TABLE[checkDigit1];
}
}
 
if (this.checkDigit == CheckDigit.MOD10_MOD10 || this.checkDigit == CheckDigit.MOD11_MOD10) {
/* Add a second Modulo-10 check digit */
evenString = "";
oddString = "";
 
spacer = this.readable.length() & 1;
 
for (i = this.readable.length() - 1; i >= 0; i--) {
if (spacer == 1) {
if ((i & 1) != 0) {
evenString = this.readable.charAt(i) + evenString;
} else {
oddString = this.readable.charAt(i) + oddString;
}
} else {
if ((i & 1) != 0) {
oddString = this.readable.charAt(i) + oddString;
} else {
evenString = this.readable.charAt(i) + evenString;
}
}
}
 
if (oddString.length() == 0) {
addupString = "0";
} else {
addupString = Integer.toString(Integer.parseInt(oddString) * 2);
}
 
addupString += evenString;
 
addup = 0;
for (i = 0; i < addupString.length(); i++) {
addup += addupString.charAt(i) - '0';
}
 
checkDigit2 = 10 - addup % 10;
if (checkDigit2 == 10) {
checkDigit2 = 0;
}
 
intermediate += MSI_PLESS_TABLE[checkDigit2];
this.readable += checkDigit2;
}
 
intermediate += "121"; // Stop
 
this.pattern = new String[] { intermediate };
this.row_count = 1;
this.row_height = new int[] { -1 };
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/DataMatrix.java
New file
0,0 → 1,1635
/*
* 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;
 
/**
* <p>
* Implements Data Matrix ECC 200 bar code symbology According to ISO/IEC 16022:2006.
*
* <p>
* Data Matrix is a 2D matrix symbology capable of encoding characters in the ISO/IEC 8859-1
* (Latin-1) character set.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class DataMatrix extends Symbol {
 
public enum ForceMode {
NONE, SQUARE, 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;
 
// internal state calculated when setContent() is called
 
private int actualSize = -1;
private final int[] target = new int[2200];
private final int[] binary = new int[2200];
private int binary_length;
private Mode last_mode;
private int[] places;
private int process_p;
private final int[] process_buffer = new int[8];
private int codewordCount;
 
/**
* Forces the symbol to be either square or rectangular (non-square).
*
* @param forceMode the force mode to use
*/
public void setForceMode(final ForceMode forceMode) {
this.forceMode = forceMode;
}
 
/**
* Returns the force mode used by this symbol.
*
* @return the force mode used by this symbol
*/
public ForceMode getForceMode() {
return this.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, or if
* {@link #setForceMode(ForceMode)} has been invoked.
*
* <table summary="Available Data Matrix symbol sizes">
* <tbody>
* <tr>
* <th>Input</th>
* <th>Symbol Size</th>
* <th>Input</th>
* <th>Symbol Size</th>
* </tr>
* <tr>
* <td>1</td>
* <td>10 x 10</td>
* <td>16</td>
* <td>64 x 64</td>
* </tr>
* <tr>
* <td>2</td>
* <td>12 x 12</td>
* <td>17</td>
* <td>72 x 72</td>
* </tr>
* <tr>
* <td>3</td>
* <td>14 x 14</td>
* <td>18</td>
* <td>80 x 80</td>
* </tr>
* <tr>
* <td>4</td>
* <td>16 x 16</td>
* <td>19</td>
* <td>88 x 88</td>
* </tr>
* <tr>
* <td>5</td>
* <td>18 x 18</td>
* <td>20</td>
* <td>96 x 96</td>
* </tr>
* <tr>
* <td>6</td>
* <td>20 x 20</td>
* <td>21</td>
* <td>104 x 104</td>
* </tr>
* <tr>
* <td>7</td>
* <td>22 x 22</td>
* <td>22</td>
* <td>120 x 120</td>
* </tr>
* <tr>
* <td>8</td>
* <td>24 x 24</td>
* <td>23</td>
* <td>132 x 132</td>
* </tr>
* <tr>
* <td>9</td>
* <td>26 x 26</td>
* <td>24</td>
* <td>144 x 144</td>
* </tr>
* <tr>
* <td>10</td>
* <td>32 x 32</td>
* <td>25</td>
* <td>8 x 18</td>
* </tr>
* <tr>
* <td>11</td>
* <td>36 x 36</td>
* <td>26</td>
* <td>8 x 32</td>
* </tr>
* <tr>
* <td>12</td>
* <td>40 x 40</td>
* <td>27</td>
* <td>12 x 26</td>
* </tr>
* <tr>
* <td>13</td>
* <td>44 x 44</td>
* <td>28</td>
* <td>12 x 36</td>
* </tr>
* <tr>
* <td>14</td>
* <td>48 x 48</td>
* <td>29</td>
* <td>16 x 36</td>
* </tr>
* <tr>
* <td>15</td>
* <td>52 x 52</td>
* <td>30</td>
* <td>16 x 48</td>
* </tr>
* </tbody>
* </table>
*
* @param size the symbol size to use (1 - 30 inclusive)
*/
public void setPreferredSize(final int size) {
this.preferredSize = size;
}
 
/**
* Returns the preferred symbol size.
*
* @return the preferred symbol size
* @see #setPreferredSize(int)
*/
public int getPreferredSize() {
return this.preferredSize;
}
 
/**
* Returns the actual symbol size used. Available after the symbol is encoded.
*
* @return the actual symbol size used
*/
public int getActualSize() {
if (this.actualSize != -1) {
return this.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() {
final int index1 = getActualSize() - 1;
final 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() {
final int index1 = getActualSize() - 1;
final 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(final 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 <code>1</code>.
*
* @return the position of this Data Matrix symbol in a series of symbols using structured
* append
*/
public int getStructuredAppendPosition() {
return this.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(final 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 <code>1</code>.
*
* @return size of the series that this symbol is part of
*/
public int getStructuredAppendTotal() {
return this.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(final 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 <code>1</code>.
*
* @return the unique file ID for the series that this symbol is part of
*/
public int getStructuredAppendFileId() {
return this.structuredAppendFileId;
}
 
@Override
protected boolean gs1Supported() {
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;
final StringBuilder bin = new StringBuilder();
 
eciProcess(); // Get ECI mode
 
binlen = generateCodewords();
 
if (this.preferredSize >= 1 && this.preferredSize <= DM_SIZES_COUNT) {
optionsize = INT_SYMBOL[this.preferredSize - 1];
} else {
optionsize = -1;
}
 
calcsize = DM_SIZES_COUNT - 1;
for (i = DM_SIZES_COUNT - 1; i > -1; i--) {
if (MATRIX_BYTES[i] >= binlen + this.process_p) {
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 (this.forceMode == ForceMode.SQUARE) {
/* Skip rectangular symbols in square only mode */
while (calcsize < DM_SIZES_COUNT && MATRIX_H[calcsize] != MATRIX_W[calcsize]) {
calcsize++;
}
} else if (this.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 OkapiException("Data too long to fit in symbol");
}
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 OkapiException("Input too long for selected symbol size");
}
symbolsize = optionsize;
}
 
// Now we know the symbol size we can handle the remaining data in the process buffer.
final int symbolsLeft = MATRIX_BYTES[symbolsize] - binlen;
binlen = encodeRemainder(symbolsLeft, binlen);
 
if (binlen > MATRIX_BYTES[symbolsize]) {
throw new OkapiException("Data too long to fit in symbol");
}
 
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];
 
this.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);
this.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 = this.places[(NR - y - 1) * NC + x];
if (v == 1 || v > 7 && (this.target[(v >> 3) - 1] & 1 << (v & 7)) != 0) {
grid[(1 + y + 2 * (y / (FH - 2))) * W + 1 + x + 2 * (x / (FW - 2))] = 1;
}
}
}
 
this.actualSize = positionOf(symbolsize, INT_SYMBOL) + 1;
this.readable = "";
this.pattern = new String[H];
this.row_count = H;
this.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');
}
}
this.pattern[H - y - 1] = bin2pat(bin);
this.row_height[H - y - 1] = this.moduleWidth;
}
 
infoLine("Grid Size: " + W + " X " + H);
infoLine("Data Codewords: " + datablock);
infoLine("ECC Codewords: " + rsblock);
}
 
@Override
protected int[] getCodewords() {
return Arrays.copyOf(this.target, this.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 = this.inputData.length;
 
sp = 0;
tp = 0;
this.process_p = 0;
 
for (i = 0; i < 8; i++) {
this.process_buffer[i] = 0;
}
this.binary_length = 0;
 
/* step (a) */
current_mode = Mode.DM_ASCII;
next_mode = Mode.DM_ASCII;
 
if (this.structuredAppendTotal != 1) {
 
/* FNC2 */
this.target[tp] = 233;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("FNC2 ");
 
/* symbol sequence indicator (position + total) */
final int ssi = this.structuredAppendPosition - 1 << 4 | 17 - this.structuredAppendTotal;
this.target[tp] = ssi;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
infoSpace(ssi);
 
/* file identification codeword 1 (valid values 1 - 254) */
final int id1 = 1 + (this.structuredAppendFileId - 1) / 254;
this.target[tp] = id1;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
infoSpace(id1);
 
/* file identification codeword 2 (valid values 1 - 254) */
final int id2 = 1 + (this.structuredAppendFileId - 1) % 254;
this.target[tp] = id2;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
infoSpace(id2);
}
 
if (this.inputDataType == DataType.GS1) {
this.target[tp] = 232;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("FNC1 ");
} /* FNC1 */
 
if (this.readerInit) {
if (this.inputDataType == DataType.GS1) {
throw new OkapiException("Cannot encode in GS1 mode and Reader Initialisation at the same time");
} else {
this.target[tp] = 234; /* FNC3 */
tp++; /* Reader Programming */
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("RP ");
}
}
 
if (this.eciMode != 3) {
this.target[tp] = 241; // ECI
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
if (this.eciMode <= 126) {
this.target[tp] = this.eciMode + 1;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
}
if (this.eciMode >= 127 && this.eciMode <= 16382) {
this.target[tp] = (this.eciMode - 127) / 254 + 128;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
this.target[tp] = (this.eciMode - 127) % 254 + 1;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
}
if (this.eciMode >= 16383) {
this.target[tp] = (this.eciMode - 16383) / 64516 + 192;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
this.target[tp] = (this.eciMode - 16383) / 254 % 254 + 1;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
this.target[tp] = (this.eciMode - 16383) % 254 + 1;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
}
info("ECI " + this.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 (this.inputData[0] == '[' && this.inputData[1] == ')' && this.inputData[2] == '>' && this.inputData[3] == '\u001e' && this.inputData[4] == '0'
&& (this.inputData[5] == '5' || this.inputData[5] == '6') && this.inputData[6] == '\u001d' && this.inputData[inputlen - 2] == '\u001e'
&& this.inputData[inputlen - 1] == '\u0004') {
/* Output macro codeword */
if (this.inputData[5] == '5') {
this.target[tp] = 236;
info("Macro05 ");
} else {
this.target[tp] = 237;
info("Macro06 ");
}
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
/* Remove macro characters from input string */
sp = 7;
inputlen -= 2;
this.inputData = Arrays.copyOf(this.inputData, this.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++) {
this.process_buffer[i] = 0;
}
 
if (isTwoDigits(sp)) {
this.target[tp] = 10 * Character.getNumericValue(this.inputData[sp]) + Character.getNumericValue(this.inputData[sp + 1]) + 130;
infoSpace(this.target[tp] - 130);
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
sp += 2;
} else {
next_mode = lookAheadTest(sp, current_mode);
 
if (next_mode != Mode.DM_ASCII) {
switch (next_mode) {
case DM_C40:
this.target[tp] = 230;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("C40 ");
break;
case DM_TEXT:
this.target[tp] = 239;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("TEX ");
break;
case DM_X12:
this.target[tp] = 238;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("X12 ");
break;
case DM_EDIFACT:
this.target[tp] = 240;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("EDI ");
break;
case DM_BASE256:
this.target[tp] = 231;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("BAS ");
break;
}
} else {
if (this.inputData[sp] > 127) {
this.target[tp] = 235; /* FNC4 */
 
info("FNC4 ");
tp++;
this.target[tp] = this.inputData[sp] - 128 + 1;
infoSpace(this.target[tp] - 1);
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
} else {
if (this.inputData[sp] == FNC1) {
this.target[tp] = 232; /* FNC1 */
info("FNC1 ");
} else {
this.target[tp] = this.inputData[sp] + 1;
infoSpace(this.target[tp] - 1);
}
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
}
sp++;
}
}
}
 
/* step (c) C40 encodation */
if (current_mode == Mode.DM_C40) {
int shift_set, value;
 
next_mode = Mode.DM_C40;
if (this.process_p == 0) {
next_mode = lookAheadTest(sp, current_mode);
}
 
if (next_mode != Mode.DM_C40) {
this.target[tp] = 254;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++; /* Unlatch */
 
next_mode = Mode.DM_ASCII;
info("ASC ");
} else {
if (this.inputData[sp] == FNC1) {
shift_set = 2;
value = 27; /* FNC1 */
} else if (this.inputData[sp] > 127) {
this.process_buffer[this.process_p] = 1;
this.process_p++;
this.process_buffer[this.process_p] = 30;
this.process_p++; /* Upper Shift */
 
shift_set = C40_SHIFT[this.inputData[sp] - 128];
value = C40_VALUE[this.inputData[sp] - 128];
} else {
shift_set = C40_SHIFT[this.inputData[sp]];
value = C40_VALUE[this.inputData[sp]];
}
 
if (shift_set != 0) {
this.process_buffer[this.process_p] = shift_set - 1;
this.process_p++;
}
this.process_buffer[this.process_p] = value;
this.process_p++;
 
while (this.process_p >= 3) {
int iv;
 
iv = 1600 * this.process_buffer[0] + 40 * this.process_buffer[1] + this.process_buffer[2] + 1;
this.target[tp] = iv / 256;
tp++;
this.target[tp] = iv % 256;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("(" + this.process_buffer[0] + " " + this.process_buffer[1] + " " + this.process_buffer[2] + ") ");
 
this.process_buffer[0] = this.process_buffer[3];
this.process_buffer[1] = this.process_buffer[4];
this.process_buffer[2] = this.process_buffer[5];
this.process_buffer[3] = 0;
this.process_buffer[4] = 0;
this.process_buffer[5] = 0;
this.process_p -= 3;
}
sp++;
}
}
 
/* step (d) Text encodation */
if (current_mode == Mode.DM_TEXT) {
int shift_set, value;
 
next_mode = Mode.DM_TEXT;
if (this.process_p == 0) {
next_mode = lookAheadTest(sp, current_mode);
}
 
if (next_mode != Mode.DM_TEXT) {
this.target[tp] = 254;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++; /* Unlatch */
 
next_mode = Mode.DM_ASCII;
info("ASC ");
} else {
if (this.inputData[sp] == FNC1) {
shift_set = 2;
value = 27; /* FNC1 */
} else if (this.inputData[sp] > 127) {
this.process_buffer[this.process_p] = 1;
this.process_p++;
this.process_buffer[this.process_p] = 30;
this.process_p++; /* Upper Shift */
 
shift_set = TEXT_SHIFT[this.inputData[sp] - 128];
value = TEXT_VALUE[this.inputData[sp] - 128];
} else {
shift_set = TEXT_SHIFT[this.inputData[sp]];
value = TEXT_VALUE[this.inputData[sp]];
}
 
if (shift_set != 0) {
this.process_buffer[this.process_p] = shift_set - 1;
this.process_p++;
}
this.process_buffer[this.process_p] = value;
this.process_p++;
 
while (this.process_p >= 3) {
int iv;
 
iv = 1600 * this.process_buffer[0] + 40 * this.process_buffer[1] + this.process_buffer[2] + 1;
this.target[tp] = iv / 256;
tp++;
this.target[tp] = iv % 256;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("(" + this.process_buffer[0] + " " + this.process_buffer[1] + " " + this.process_buffer[2] + ") ");
 
this.process_buffer[0] = this.process_buffer[3];
this.process_buffer[1] = this.process_buffer[4];
this.process_buffer[2] = this.process_buffer[5];
this.process_buffer[3] = 0;
this.process_buffer[4] = 0;
this.process_buffer[5] = 0;
this.process_p -= 3;
}
sp++;
}
}
 
/* step (e) X12 encodation */
if (current_mode == Mode.DM_X12) {
int value = 0;
 
next_mode = Mode.DM_X12;
if (this.process_p == 0) {
next_mode = lookAheadTest(sp, current_mode);
}
 
if (next_mode != Mode.DM_X12) {
this.target[tp] = 254;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++; /* Unlatch */
 
next_mode = Mode.DM_ASCII;
info("ASC ");
} else {
if (this.inputData[sp] == 13) {
value = 0;
}
if (this.inputData[sp] == '*') {
value = 1;
}
if (this.inputData[sp] == '>') {
value = 2;
}
if (this.inputData[sp] == ' ') {
value = 3;
}
if (this.inputData[sp] >= '0' && this.inputData[sp] <= '9') {
value = this.inputData[sp] - '0' + 4;
}
if (this.inputData[sp] >= 'A' && this.inputData[sp] <= 'Z') {
value = this.inputData[sp] - 'A' + 14;
}
 
this.process_buffer[this.process_p] = value;
this.process_p++;
 
while (this.process_p >= 3) {
int iv;
 
iv = 1600 * this.process_buffer[0] + 40 * this.process_buffer[1] + this.process_buffer[2] + 1;
this.target[tp] = iv / 256;
tp++;
this.target[tp] = iv % 256;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("(" + this.process_buffer[0] + " " + this.process_buffer[1] + " " + this.process_buffer[2] + ") ");
 
this.process_buffer[0] = this.process_buffer[3];
this.process_buffer[1] = this.process_buffer[4];
this.process_buffer[2] = this.process_buffer[5];
this.process_buffer[3] = 0;
this.process_buffer[4] = 0;
this.process_buffer[5] = 0;
this.process_p -= 3;
}
sp++;
}
}
 
/* step (f) EDIFACT encodation */
if (current_mode == Mode.DM_EDIFACT) {
int value = 0;
 
next_mode = Mode.DM_EDIFACT;
if (this.process_p == 3) {
next_mode = lookAheadTest(sp, current_mode);
}
 
if (next_mode != Mode.DM_EDIFACT) {
this.process_buffer[this.process_p] = 31;
this.process_p++;
next_mode = Mode.DM_ASCII;
} else {
if (this.inputData[sp] >= '@' && this.inputData[sp] <= '^') {
value = this.inputData[sp] - '@';
}
if (this.inputData[sp] >= ' ' && this.inputData[sp] <= '?') {
value = this.inputData[sp];
}
 
this.process_buffer[this.process_p] = value;
this.process_p++;
sp++;
}
 
while (this.process_p >= 4) {
this.target[tp] = (this.process_buffer[0] << 2) + ((this.process_buffer[1] & 0x30) >> 4);
tp++;
this.target[tp] = ((this.process_buffer[1] & 0x0f) << 4) + ((this.process_buffer[2] & 0x3c) >> 2);
tp++;
this.target[tp] = ((this.process_buffer[2] & 0x03) << 6) + this.process_buffer[3];
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("(" + this.process_buffer[0] + " " + this.process_buffer[1] + " " + this.process_buffer[2] + ") ");
 
this.process_buffer[0] = this.process_buffer[4];
this.process_buffer[1] = this.process_buffer[5];
this.process_buffer[2] = this.process_buffer[6];
this.process_buffer[3] = this.process_buffer[7];
this.process_buffer[4] = 0;
this.process_buffer[5] = 0;
this.process_buffer[6] = 0;
this.process_buffer[7] = 0;
this.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) {
this.target[tp] = this.inputData[sp];
infoSpace(this.target[tp]);
tp++;
sp++;
this.binary[this.binary_length] = 'b';
this.binary_length++;
} else {
next_mode = Mode.DM_ASCII;
info("ASC ");
}
}
 
if (tp > 1558) {
throw new OkapiException("Data too long to fit in symbol");
}
 
} /* while */
 
/* Add length and randomising algorithm to b256 */
i = 0;
while (i < tp) {
if (this.binary[i] == 'b') {
if (i == 0 || this.binary[i - 1] != 'b') {
/* start of binary data */
int binary_count; /* length of b256 data */
 
for (binary_count = 0; binary_count + i < tp && this.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 (this.binary[i] == 'b') {
int prn, temp;
 
prn = 149 * (i + 1) % 255 + 1;
temp = this.target[i] + prn;
if (temp <= 255) {
this.target[i] = temp;
} else {
this.target[i] = temp - 256;
}
}
}
 
infoLine();
info("Codewords: ");
for (i = 0; i < tp; i++) {
infoSpace(this.target[i]);
}
infoLine();
 
this.last_mode = current_mode;
return tp;
}
 
private int encodeRemainder(final int symbols_left, int target_length) {
 
final int inputlen = this.inputData.length;
 
switch (this.last_mode) {
case DM_C40:
case DM_TEXT:
if (this.process_p == 1) // 1 data character left to encode.
{
if (symbols_left > 1) {
this.target[target_length] = 254;
target_length++; // Unlatch and encode remaining data in ascii.
}
this.target[target_length] = this.inputData[inputlen - 1] + 1;
target_length++;
} else if (this.process_p == 2) // 2 data characters left to encode.
{
// Pad with shift 1 value (0) and encode as double.
final int intValue = 1600 * this.process_buffer[0] + 40 * this.process_buffer[1] + 1; // ie
// (0
// +
// 1).
this.target[target_length] = intValue / 256;
target_length++;
this.target[target_length] = intValue % 256;
target_length++;
if (symbols_left > 2) {
this.target[target_length] = 254; // Unlatch
target_length++;
}
} else {
if (symbols_left > 0) {
this.target[target_length] = 254; // Unlatch
target_length++;
}
}
break;
 
case DM_X12:
if (symbols_left == this.process_p && this.process_p == 1) {
// Unlatch not required!
this.target[target_length] = this.inputData[inputlen - 1] + 1;
target_length++;
} else {
this.target[target_length] = 254;
target_length++; // Unlatch.
 
if (this.process_p == 1) {
this.target[target_length] = this.inputData[inputlen - 1] + 1;
target_length++;
}
 
if (this.process_p == 2) {
this.target[target_length] = this.inputData[inputlen - 2] + 1;
target_length++;
this.target[target_length] = this.inputData[inputlen - 1] + 1;
target_length++;
}
}
break;
 
case DM_EDIFACT:
if (symbols_left <= 2) // Unlatch not required!
{
if (this.process_p == 1) {
this.target[target_length] = this.inputData[inputlen - 1] + 1;
target_length++;
}
 
if (this.process_p == 2) {
this.target[target_length] = this.inputData[inputlen - 2] + 1;
target_length++;
this.target[target_length] = this.inputData[inputlen - 1] + 1;
target_length++;
}
} else {
// Append edifact unlatch value (31) and empty buffer
if (this.process_p == 0) {
this.target[target_length] = 31 << 2;
target_length++;
}
 
if (this.process_p == 1) {
this.target[target_length] = (this.process_buffer[0] << 2) + ((31 & 0x30) >> 4);
target_length++;
this.target[target_length] = (31 & 0x0f) << 4;
target_length++;
}
 
if (this.process_p == 2) {
this.target[target_length] = (this.process_buffer[0] << 2) + ((this.process_buffer[1] & 0x30) >> 4);
target_length++;
this.target[target_length] = ((this.process_buffer[1] & 0x0f) << 4) + ((31 & 0x3c) >> 2);
target_length++;
this.target[target_length] = (31 & 0x03) << 6;
target_length++;
}
 
if (this.process_p == 3) {
this.target[target_length] = (this.process_buffer[0] << 2) + ((this.process_buffer[1] & 0x30) >> 4);
target_length++;
this.target[target_length] = ((this.process_buffer[1] & 0x0f) << 4) + ((this.process_buffer[2] & 0x3c) >> 2);
target_length++;
this.target[target_length] = ((this.process_buffer[2] & 0x03) << 6) + 31;
target_length++;
}
}
break;
}
 
return target_length;
}
 
private boolean isTwoDigits(final int pos) {
return pos + 1 < this.inputData.length && Character.isDigit((char) this.inputData[pos]) && Character.isDigit((char) this.inputData[pos + 1]);
}
 
private Mode lookAheadTest(final int position, final 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;
final int sourcelen = this.inputData.length;
Mode best_scheme = Mode.NULL;
final 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 (this.inputData[sp] >= '0' && this.inputData[sp] <= '9') {
ascii_count += 0.5; // (l)(1)
} else {
if (this.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 (this.inputData[sp] == ' ' || this.inputData[sp] >= '0' && this.inputData[sp] <= '9' || this.inputData[sp] >= 'A' && this.inputData[sp] <= 'Z') {
c40_count += 2.0 / 3.0; // (m)(1)
} else {
if (this.inputData[sp] > 127) {
c40_count += 8.0 / 3.0; // (m)(2)
} else {
c40_count += 4.0 / 3.0; // (m)(3)
}
}
 
/* text ... step (n) */
if (this.inputData[sp] == ' ' || this.inputData[sp] >= '0' && this.inputData[sp] <= '9' || this.inputData[sp] >= 'a' && this.inputData[sp] <= 'z') {
text_count += 2.0 / 3.0; // (n)(1)
} else {
if (this.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(this.inputData[sp])) {
x12_count += 2.0 / 3.0; // (o)(1)
} else {
if (this.inputData[sp] > 127) {
x12_count += 13.0 / 3.0; // (o)(2)
} else {
x12_count += 10.0 / 3.0; // (o)(3)
}
}
 
/* edifact ... step (p) */
if (this.inputData[sp] >= ' ' && this.inputData[sp] <= '^') {
edf_count += 3.0 / 4.0; // (p)(1)
} else {
if (this.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 (this.inputData[sp] == FNC1) {
edf_count += 13.0; // > Value changed from ISO
}
 
/* base 256 ... step (q) */
if (this.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(final int position, final 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(this.inputData[i])) {
nonX12Position = i;
}
 
if (specialX12Position == 0) {
if (this.inputData[i] == (char) 13 || this.inputData[i] == '*' || this.inputData[i] == '>') {
specialX12Position = i;
}
}
}
 
if (nonX12Position != 0 && specialX12Position != 0) {
if (specialX12Position < nonX12Position) {
retval = true;
}
}
 
return retval;
}
 
private boolean isX12(final int source) {
if (source == 13) {
return true;
}
if (source == 42) {
return true;
}
if (source == 62) {
return true;
}
if (source == 32) {
return true;
}
if (source >= '0' && source <= '9') {
return true;
}
if (source >= 'A' && source <= 'Z') {
return true;
}
 
return false;
}
 
private void calculateErrorCorrection(final int bytes, final int datablock, final int rsblock, final int skew) {
// calculate and append ecc code, and if necessary interleave
final int blocks = (bytes + 2) / datablock;
int b;
int n, p;
final ReedSolomon rs = new ReedSolomon();
 
rs.init_gf(0x12d);
rs.init_code(rsblock, 1);
 
for (b = 0; b < blocks; b++) {
final int[] buf = new int[256];
final int[] ecc = new int[256];
 
p = 0;
for (n = b; n < bytes; n += blocks) {
buf[p++] = this.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) {
this.target[bytes + n + 2] = ecc[p--];
} else {
this.target[bytes + n - 8] = ecc[p--];
}
} else {
this.target[bytes + n] = ecc[p--];
}
}
}
}
 
private void insertAt(final int pos, final char newbit) {
/* Insert a character into the middle of a string at position posn */
for (int i = this.binary_length; i > pos; i--) {
this.binary[i] = this.binary[i - 1];
}
this.binary[pos] = newbit;
this.binary_length++;
}
 
private void insertValueAt(final int posn, final int streamlen, final char newbit) {
int i;
 
for (i = streamlen; i > posn; i--) {
this.target[i] = this.target[i - 1];
}
this.target[posn] = newbit;
}
 
private void addPadBits(int tp, final int tail_length) {
int i, prn, temp;
 
for (i = tail_length; i > 0; i--) {
if (i == tail_length) {
this.target[tp] = 129;
tp++; /* Pad */
} else {
prn = 149 * (tp + 1) % 253 + 1;
temp = 129 + prn;
if (temp <= 254) {
this.target[tp] = temp;
tp++;
} else {
this.target[tp] = temp - 254;
tp++;
}
}
}
}
 
private void placeData(final int NR, final int NC) {
int r, c, p;
// invalidate
for (r = 0; r < NR; r++) {
for (c = 0; c < NC; c++) {
this.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 && this.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 && this.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 (this.places[NR * NC - 1] == 0) {
this.places[NR * NC - 1] = this.places[NR * NC - NC - 2] = 1;
}
}
 
private void placeCornerA(final int NR, final int NC, final 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(final int NR, final int NC, final 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(final int NR, final int NC, final 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(final int NR, final int NC, final 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(final int NR, final int NC, final int r, final int c, final 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(final int NR, final int NC, int r, int c, final int p, final int b) {
if (r < 0) {
r += NR;
c += 4 - (NR + 4) % 8;
}
if (c < 0) {
c += NC;
r += 4 - (NC + 4) % 8;
}
this.places[r * NC + c] = (p << 3) + b;
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/AztecRune.java
New file
0,0 → 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;
 
/**
* <p>
* Implements the Aztec Runes bar code symbology according to ISO/IEC 24778:2008 Annex A.
*
* <p>
* Aztec Runes is a fixed-size matrix symbology which can encode whole integer values between 0 and
* 255.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
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 (!this.content.matches("[0-9]+")) {
throw new OkapiException("Invalid input data");
}
 
int decimalValue = 0;
switch (this.content.length()) {
case 1:
decimalValue = this.content.charAt(0) - '0';
break;
case 2:
decimalValue = 10 * (this.content.charAt(0) - '0');
decimalValue += this.content.charAt(1) - '0';
break;
case 3:
decimalValue = 100 * (this.content.charAt(0) - '0');
decimalValue += 10 * (this.content.charAt(1) - '0');
decimalValue += this.content.charAt(2) - '0';
break;
default:
throw new OkapiException("Input too large");
}
 
if (decimalValue > 255) {
throw new OkapiException("Input too large");
}
 
final 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');
}
}
 
final 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;
}
}
 
final int[] errorCorrectionCodeword = new int[6];
 
final 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');
}
}
 
final 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);
 
this.readable = "";
this.pattern = new String[11];
this.row_count = 11;
this.row_height = new int[11];
 
for (int row = 0; row < 11; row++) {
final 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));
}
}
this.pattern[row] = bin2pat(rowBinary);
this.row_height[row] = 1;
}
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Composite.java
New file
0,0 → 1,2769
/*
* 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.awt.geom.Rectangle2D;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
 
import uk.org.okapibarcode.backend.DataBar14.Mode;
 
/**
* <p>
* Implements GS1 Composite symbology according to ISO/IEC 24723:2010.
*
* <p>
* Composite symbols comprise a 2D element which encodes GS1 data and a "linear" element which can
* be UPC, EAN, Code 128 or GS1 DataBar symbol.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class Composite extends Symbol {
 
/** The linear component choices available. */
public static enum LinearEncoding {
UPCA, UPCE, EAN, CODE_128, DATABAR_14, DATABAR_14_STACK, DATABAR_14_STACK_OMNI, DATABAR_LIMITED, DATABAR_EXPANDED, DATABAR_EXPANDED_STACK
}
 
/** The 2D component choices available. */
public static enum CompositeMode {
/**
* Indicates that the composite symbol uses a MicroPDF417 variant as the 2D component. Of
* the 2D component choices, this one holds the least amount of data.
*/
CC_A,
/**
* Indicates that the composite symbol uses a MicroPDF417 symbol as the 2D component,
* starting with a codeword of 920.
*/
CC_B,
/**
* Indicates that the composite symbol uses a PDF417 symbol as the 2D component, starting
* with a codeword of 920. Of the 2D component choices, this one holds the most amount of
* data. May only be used if the linear component is {@link LinearEncoding#CODE_128 Code
* 128}.
*/
CC_C
}
 
private static enum GeneralFieldMode {
NUMERIC, ALPHA, ISOIEC, INVALID_CHAR, ANY_ENC, ALPHA_OR_ISO
}
 
/* CC-A component coefficients from ISO/IEC 24728:2006 Annex F */
private static final int[] CCA_COEFFS = {
/* k = 4 */
522, 568, 723, 809,
/* k = 5 */
427, 919, 460, 155, 566,
/* k = 6 */
861, 285, 19, 803, 17, 766,
/* k = 7 */
76, 925, 537, 597, 784, 691, 437,
/* k = 8 */
237, 308, 436, 284, 646, 653, 428, 379 };
 
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 };
 
/* rows, error codewords, k-offset of valid CC-A sizes from ISO/IEC 24723:2006 Table 9 */
private static final int[] CCA_VARIANTS = { 5, 6, 7, 8, 9, 10, 12, 4, 5, 6, 7, 8, 3, 4, 5, 6, 7, 4, 4, 5, 5, 6, 6, 7, 4, 5, 6, 7, 7, 4, 5, 6, 7, 8, 0, 0, 4, 4, 9, 9, 15, 0, 4, 9, 15, 15, 0, 4, 9,
15, 22 };
 
/*
* following is Left RAP, Centre RAP, Right RAP and Start Cluster from ISO/IEC 24723:2006 tables
* 10 and 11
*/
private static final int[] A_RAP_TABLE = { 39, 1, 32, 8, 14, 43, 20, 11, 1, 5, 15, 21, 40, 43, 46, 34, 29, 0, 0, 0, 0, 0, 0, 0, 43, 33, 37, 47, 1, 20, 23, 26, 14, 9, 19, 33, 12, 40, 46, 23, 52,
23, 13, 17, 27, 33, 52, 3, 6, 46, 41, 6, 0, 3, 3, 3, 0, 3, 3, 0, 3, 6, 6, 0, 0, 0, 0, 3 };
 
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" };
 
/* 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" };
 
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, 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, 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, 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 };
 
/* rows, columns, error codewords, k-offset */
/* MicroPDF417 coefficients from ISO/IEC 24728:2006 Annex F */
private static final int[] MICROCOEFFS = {
/* 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 };
 
/*
* 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, 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, 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, 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 };
 
private String binary_string;
private int ecc;
private LinearEncoding symbology = LinearEncoding.CODE_128;
private String general_field;
private GeneralFieldMode[] general_field_type;
private int cc_width;
private final int[][] pwr928 = new int[69][7];
private final int[] codeWords = new int[180];
private int codeWordCount;
private final int[] bitStr = new int[13];
private int[] inputData;
private CompositeMode cc_mode;
private String linearContent;
private CompositeMode userPreferredMode = CompositeMode.CC_A;
private int target_bitsize;
private int remainder;
private int linearWidth; // Width of Code 128 linear
 
public Composite() {
this.inputDataType = Symbol.DataType.GS1;
}
 
@Override
public void setDataType(final DataType dataType) {
if (dataType != Symbol.DataType.GS1) {
throw new IllegalArgumentException("Only GS1 data type is supported for GS1 Composite symbology.");
}
}
 
@Override
protected boolean gs1Supported() {
return true;
}
 
/**
* Set the type of linear component included in the composite symbol, this will determine how
* the lower part of the symbol is encoded.
*
* @param linearSymbology The symbology of the linear component
*/
public void setSymbology(final LinearEncoding linearSymbology) {
this.symbology = linearSymbology;
}
 
/**
* Returns the type of linear component included in the composite symbol.
*
* @return the type of linear component included in the composite symbol
*/
public LinearEncoding getSymbology() {
return this.symbology;
}
 
/**
* Set the data to be encoded in the linear component of the composite symbol.
*
* @param linearContent the linear data in GS1 format
*/
public void setLinearContent(final String linearContent) {
this.linearContent = linearContent;
}
 
/**
* Returns the data encoded in the linear component of the composite symbol.
*
* @return the data encoded in the linear component of the composite symbol
*/
public String getLinearContent() {
return this.linearContent;
}
 
/**
* Set the preferred encoding method for the 2D component of the composite symbol. This value
* may be ignored if the amount of data supplied is too big for the selected encoding. Mode CC-C
* can only be used with a Code 128 linear component.
*
* @param userMode Preferred mode
*/
public void setPreferredMode(final CompositeMode userMode) {
this.userPreferredMode = userMode;
}
 
/**
* Returns the preferred encoding method for the 2D component of the composite symbol.
*
* @return the preferred encoding method for the 2D component of the composite symbol
*/
public CompositeMode getPreferredMode() {
return this.userPreferredMode;
}
 
@Override
protected void encode() {
 
List<Rectangle2D.Double> linear_rect;
List<TextBox> linear_txt;
final List<Rectangle2D.Double> combine_rect = new ArrayList<>();
final List<TextBox> combine_txt = new ArrayList<>();
String linear_encodeInfo;
int linear_height;
int top_shift = 0; // 2D component x-coordinate shift
int bottom_shift = 0; // linear component x-coordinate shift
this.linearWidth = 0;
 
if (this.linearContent.isEmpty()) {
throw new OkapiException("No linear data set");
}
 
// Manage composite component encoding first
encodeComposite();
 
// Then encode linear component
switch (this.symbology) {
case UPCA:
final Upc upca = new Upc();
upca.setMode(Upc.Mode.UPCA);
upca.setLinkageFlag(true);
upca.setContent(this.linearContent);
linear_rect = upca.rectangles;
linear_txt = upca.texts;
linear_height = upca.symbol_height;
linear_encodeInfo = upca.getEncodeInfo();
bottom_shift = 6;
top_shift = 3;
break;
case UPCE:
final Upc upce = new Upc();
upce.setMode(Upc.Mode.UPCE);
upce.setLinkageFlag(true);
upce.setContent(this.linearContent);
linear_rect = upce.rectangles;
linear_txt = upce.texts;
linear_height = upce.symbol_height;
linear_encodeInfo = upce.getEncodeInfo();
bottom_shift = 6;
top_shift = 3;
break;
case EAN:
final Ean ean = new Ean();
if (eanCalculateVersion() == 8) {
ean.setMode(Ean.Mode.EAN8);
bottom_shift = 14;
} else {
ean.setMode(Ean.Mode.EAN13);
bottom_shift = 6;
top_shift = 3;
}
ean.setLinkageFlag(true);
ean.setContent(this.linearContent);
linear_rect = ean.rectangles;
linear_txt = ean.texts;
linear_height = ean.symbol_height;
linear_encodeInfo = ean.getEncodeInfo();
break;
case CODE_128:
final Code128 code128 = new Code128();
switch (this.cc_mode) {
case CC_A:
code128.setCca();
break;
case CC_B:
code128.setCcb();
break;
case CC_C:
code128.setCcc();
bottom_shift = 7;
break;
}
code128.setDataType(Symbol.DataType.GS1);
code128.setContent(this.linearContent);
this.linearWidth = code128.symbol_width;
linear_rect = code128.rectangles;
linear_txt = code128.texts;
linear_height = code128.symbol_height;
linear_encodeInfo = code128.getEncodeInfo();
break;
case DATABAR_14:
final DataBar14 dataBar14 = new DataBar14();
dataBar14.setLinkageFlag(true);
dataBar14.setMode(Mode.LINEAR);
dataBar14.setContent(this.linearContent);
linear_rect = dataBar14.rectangles;
linear_txt = dataBar14.texts;
linear_height = dataBar14.symbol_height;
linear_encodeInfo = dataBar14.getEncodeInfo();
bottom_shift = 4;
break;
case DATABAR_14_STACK_OMNI:
final DataBar14 dataBar14SO = new DataBar14();
dataBar14SO.setLinkageFlag(true);
dataBar14SO.setMode(Mode.OMNI);
dataBar14SO.setContent(this.linearContent);
linear_rect = dataBar14SO.rectangles;
linear_txt = dataBar14SO.texts;
linear_height = dataBar14SO.symbol_height;
linear_encodeInfo = dataBar14SO.getEncodeInfo();
top_shift = 1;
break;
case DATABAR_14_STACK:
final DataBar14 dataBar14S = new DataBar14();
dataBar14S.setLinkageFlag(true);
dataBar14S.setMode(Mode.STACKED);
dataBar14S.setContent(this.linearContent);
linear_rect = dataBar14S.rectangles;
linear_txt = dataBar14S.texts;
linear_height = dataBar14S.symbol_height;
linear_encodeInfo = dataBar14S.getEncodeInfo();
top_shift = 1;
break;
case DATABAR_LIMITED:
final DataBarLimited dataBarLimited = new DataBarLimited();
dataBarLimited.setLinkageFlag();
dataBarLimited.setContent(this.linearContent);
linear_rect = dataBarLimited.rectangles;
linear_txt = dataBarLimited.texts;
linear_height = dataBarLimited.symbol_height;
linear_encodeInfo = dataBarLimited.getEncodeInfo();
top_shift = 1;
bottom_shift = 10;
break;
case DATABAR_EXPANDED:
final DataBarExpanded dataBarExpanded = new DataBarExpanded();
dataBarExpanded.setLinkageFlag(true);
dataBarExpanded.setStacked(false);
dataBarExpanded.setContent(this.linearContent);
linear_rect = dataBarExpanded.rectangles;
linear_txt = dataBarExpanded.texts;
linear_height = dataBarExpanded.symbol_height;
linear_encodeInfo = dataBarExpanded.getEncodeInfo();
top_shift = 2;
break;
case DATABAR_EXPANDED_STACK:
final DataBarExpanded dataBarExpandedS = new DataBarExpanded();
dataBarExpandedS.setLinkageFlag(true);
dataBarExpandedS.setStacked(true);
dataBarExpandedS.setContent(this.linearContent);
linear_rect = dataBarExpandedS.rectangles;
linear_txt = dataBarExpandedS.texts;
linear_height = dataBarExpandedS.symbol_height;
linear_encodeInfo = dataBarExpandedS.getEncodeInfo();
top_shift = 2;
break;
default:
throw new OkapiException("Linear symbol not recognised");
}
 
if (this.cc_mode == CompositeMode.CC_C && this.symbology == LinearEncoding.CODE_128) {
/*
* Width of composite component depends on width of linear component, so recalculate.
*/
this.row_count = 0;
this.rectangles.clear();
this.symbol_height = 0;
this.symbol_width = 0;
this.encodeInfo.setLength(0);
encodeComposite();
}
 
if (this.cc_mode != CompositeMode.CC_C && this.symbology == LinearEncoding.CODE_128) {
if (this.linearWidth > this.symbol_width) {
top_shift = (this.linearWidth - this.symbol_width) / 2;
}
}
 
for (final Rectangle2D.Double orig : this.rectangles) {
combine_rect.add(new Rectangle2D.Double(orig.x + top_shift, orig.y, orig.width, orig.height));
}
 
for (final Rectangle2D.Double orig : linear_rect) {
combine_rect.add(new Rectangle2D.Double(orig.x + bottom_shift, orig.y + this.symbol_height, orig.width, orig.height));
}
 
int max_x = 0;
for (final Rectangle2D.Double rect : combine_rect) {
if (rect.x + rect.width > max_x) {
max_x = (int) Math.ceil(rect.x + rect.width);
}
}
 
for (final TextBox orig : linear_txt) {
combine_txt.add(new TextBox(orig.x + bottom_shift, orig.y + this.symbol_height, orig.width, orig.text, this.humanReadableAlignment));
}
 
this.rectangles = combine_rect;
this.texts = combine_txt;
this.symbol_height += linear_height;
this.symbol_width = max_x;
info(linear_encodeInfo);
}
 
private void encodeComposite() {
 
if (this.content.length() > 2990) {
throw new OkapiException("2D component input data too long");
}
 
this.cc_mode = this.userPreferredMode;
 
if (this.cc_mode == CompositeMode.CC_C && this.symbology != LinearEncoding.CODE_128) {
/* CC-C can only be used with a GS1-128 linear part */
throw new OkapiException("Invalid mode (CC-C only valid with GS1-128 linear component)");
}
 
switch (this.symbology) {
/* Determine width of 2D component according to ISO/IEC 24723 Table 1 */
case EAN:
if (eanCalculateVersion() == 8) {
this.cc_width = 3;
} else {
this.cc_width = 4;
}
break;
case UPCE:
case DATABAR_14_STACK_OMNI:
case DATABAR_14_STACK:
this.cc_width = 2;
break;
case DATABAR_LIMITED:
this.cc_width = 3;
break;
case CODE_128:
case DATABAR_14:
case DATABAR_EXPANDED:
case UPCA:
case DATABAR_EXPANDED_STACK:
this.cc_width = 4;
break;
}
 
infoLine("Composite Width: " + this.cc_width);
 
if (this.cc_mode == CompositeMode.CC_A && !cc_binary_string()) {
this.cc_mode = CompositeMode.CC_B;
}
 
if (this.cc_mode == CompositeMode.CC_B) { /*
* If the data didn't fit into CC-A it is
* recalculated for CC-B
*/
if (!cc_binary_string()) {
if (this.symbology != LinearEncoding.CODE_128) {
throw new OkapiException("Input too long");
} else {
this.cc_mode = CompositeMode.CC_C;
}
}
}
 
if (this.cc_mode == CompositeMode.CC_C) {
/*
* If the data didn't fit in CC-B (and linear part is GS1-128) it is recalculated for
* CC-C
*/
if (!cc_binary_string()) {
throw new OkapiException("Input too long");
}
}
 
switch (this.cc_mode) { /* Note that ecc_level is only relevant to CC-C */
case CC_A:
cc_a();
infoLine("Composite Type: CC-A");
break;
case CC_B:
cc_b();
infoLine("Composite Type: CC-B");
break;
case CC_C:
cc_c();
infoLine("Composite Type: CC-C");
break;
}
 
super.plotSymbol();
}
 
@Override
protected void plotSymbol() {
// empty
}
 
private int eanCalculateVersion() {
/* Determine if EAN-8 or EAN-13 is being used */
 
int length = 0;
int i;
boolean latch;
 
latch = true;
for (i = 0; i < this.linearContent.length(); i++) {
if (this.linearContent.charAt(i) >= '0' && this.linearContent.charAt(i) <= '9') {
if (latch) {
length++;
}
} else {
latch = false;
}
}
 
if (length <= 7) {
// EAN-8
return 8;
} else {
// EAN-13
return 13;
}
}
 
private boolean calculateSymbolSize() {
int i;
final int binary_length = this.binary_string.length();
if (this.cc_mode == CompositeMode.CC_A) {
/* CC-A 2D component - calculate remaining space */
switch (this.cc_width) {
case 2:
if (binary_length > 167) {
return false;
}
if (binary_length <= 167) {
this.target_bitsize = 167;
}
if (binary_length <= 138) {
this.target_bitsize = 138;
}
if (binary_length <= 118) {
this.target_bitsize = 118;
}
if (binary_length <= 108) {
this.target_bitsize = 108;
}
if (binary_length <= 88) {
this.target_bitsize = 88;
}
if (binary_length <= 78) {
this.target_bitsize = 78;
}
if (binary_length <= 59) {
this.target_bitsize = 59;
}
break;
case 3:
if (binary_length > 167) {
return false;
}
if (binary_length <= 167) {
this.target_bitsize = 167;
}
if (binary_length <= 138) {
this.target_bitsize = 138;
}
if (binary_length <= 118) {
this.target_bitsize = 118;
}
if (binary_length <= 98) {
this.target_bitsize = 98;
}
if (binary_length <= 78) {
this.target_bitsize = 78;
}
break;
case 4:
if (binary_length > 197) {
return false;
}
if (binary_length <= 197) {
this.target_bitsize = 197;
}
if (binary_length <= 167) {
this.target_bitsize = 167;
}
if (binary_length <= 138) {
this.target_bitsize = 138;
}
if (binary_length <= 108) {
this.target_bitsize = 108;
}
if (binary_length <= 78) {
this.target_bitsize = 78;
}
break;
}
}
 
if (this.cc_mode == CompositeMode.CC_B) {
/* CC-B 2D component - calculated from ISO/IEC 24728 Table 1 */
switch (this.cc_width) {
case 2:
if (binary_length > 336) {
return false;
}
if (binary_length <= 336) {
this.target_bitsize = 336;
}
if (binary_length <= 296) {
this.target_bitsize = 296;
}
if (binary_length <= 256) {
this.target_bitsize = 256;
}
if (binary_length <= 208) {
this.target_bitsize = 208;
}
if (binary_length <= 160) {
this.target_bitsize = 160;
}
if (binary_length <= 104) {
this.target_bitsize = 104;
}
if (binary_length <= 56) {
this.target_bitsize = 56;
}
break;
case 3:
if (binary_length > 768) {
return false;
}
if (binary_length <= 768) {
this.target_bitsize = 768;
}
if (binary_length <= 648) {
this.target_bitsize = 648;
}
if (binary_length <= 536) {
this.target_bitsize = 536;
}
if (binary_length <= 416) {
this.target_bitsize = 416;
}
if (binary_length <= 304) {
this.target_bitsize = 304;
}
if (binary_length <= 208) {
this.target_bitsize = 208;
}
if (binary_length <= 152) {
this.target_bitsize = 152;
}
if (binary_length <= 112) {
this.target_bitsize = 112;
}
if (binary_length <= 72) {
this.target_bitsize = 72;
}
if (binary_length <= 32) {
this.target_bitsize = 32;
}
break;
case 4:
if (binary_length > 1184) {
return false;
}
if (binary_length <= 1184) {
this.target_bitsize = 1184;
}
if (binary_length <= 1016) {
this.target_bitsize = 1016;
}
if (binary_length <= 840) {
this.target_bitsize = 840;
}
if (binary_length <= 672) {
this.target_bitsize = 672;
}
if (binary_length <= 496) {
this.target_bitsize = 496;
}
if (binary_length <= 352) {
this.target_bitsize = 352;
}
if (binary_length <= 264) {
this.target_bitsize = 264;
}
if (binary_length <= 208) {
this.target_bitsize = 208;
}
if (binary_length <= 152) {
this.target_bitsize = 152;
}
if (binary_length <= 96) {
this.target_bitsize = 96;
}
if (binary_length <= 56) {
this.target_bitsize = 56;
}
break;
}
}
 
if (this.cc_mode == CompositeMode.CC_C) {
/* CC-C 2D Component is a bit more complex! */
int byte_length, codewords_used, ecc_level, ecc_codewords, rows;
int codewords_total, target_codewords, target_bytesize;
 
byte_length = binary_length / 8;
if (binary_length % 8 != 0) {
byte_length++;
}
 
codewords_used = byte_length / 6 * 5;
codewords_used += byte_length % 6;
 
ecc_level = 7;
if (codewords_used <= 1280) {
ecc_level = 6;
}
if (codewords_used <= 640) {
ecc_level = 5;
}
if (codewords_used <= 320) {
ecc_level = 4;
}
if (codewords_used <= 160) {
ecc_level = 3;
}
if (codewords_used <= 40) {
ecc_level = 2;
}
this.ecc = ecc_level;
ecc_codewords = 1;
for (i = 1; i <= ecc_level + 1; i++) {
ecc_codewords *= 2;
}
 
codewords_used += ecc_codewords;
codewords_used += 3;
 
if (this.linearWidth == 0) {
/* Linear component not yet calculated */
this.cc_width = (int) (0.5 + Math.sqrt(codewords_used / 3.0));
} else {
this.cc_width = (this.linearWidth - 53) / 17;
}
 
if (codewords_used / this.cc_width > 90) {
/* stop the symbol from becoming too high */
this.cc_width = this.cc_width + 1;
}
 
rows = codewords_used / this.cc_width;
if (codewords_used % this.cc_width != 0) {
rows++;
}
 
while (this.cc_width > 3 * rows) {
/* stop the symbol from becoming too wide (section 10) */
this.cc_width--;
 
rows = codewords_used / this.cc_width;
if (codewords_used % this.cc_width != 0) {
rows++;
}
}
 
codewords_total = this.cc_width * rows;
 
target_codewords = codewords_total - ecc_codewords;
target_codewords -= 3;
 
target_bytesize = 6 * (target_codewords / 5);
target_bytesize += target_codewords % 5;
 
this.target_bitsize = 8 * target_bytesize;
}
 
this.remainder = this.target_bitsize - binary_length;
return true;
}
 
private boolean cc_binary_string() {
/* Handles all data encodation from section 5 of ISO/IEC 24723 */
int encoding_method, read_posn, d1, d2, value, alpha_pad;
int i, j, ai_crop, fnc1_latch;
int group_val;
int ai90_mode;
boolean latch;
int alpha, alphanum, numeric, test1, test2, test3, next_ai_posn;
int numeric_value, table3_letter;
String numeric_part;
String ninety;
int latchOffset;
 
encoding_method = 1;
read_posn = 0;
ai_crop = 0;
fnc1_latch = 0;
alpha_pad = 0;
ai90_mode = 0;
this.ecc = 0;
value = 0;
this.target_bitsize = 0;
 
if (this.content.charAt(0) == '1' && (this.content.charAt(1) == '0' || this.content.charAt(1) == '1' || this.content.charAt(1) == '7') && this.content.length() >= 8) {
/* Source starts (10), (11) or (17) */
encoding_method = 2;
}
 
if (this.content.charAt(0) == '9' && this.content.charAt(1) == '0') {
/* Source starts (90) */
encoding_method = 3;
}
 
info("Composite Encodation: ");
switch (encoding_method) {
case 1:
infoLine("0");
break;
case 2:
infoLine("10");
break;
case 3:
infoLine("11");
break;
}
 
this.binary_string = "";
 
if (encoding_method == 1) {
this.binary_string += "0";
}
 
if (encoding_method == 2) {
/* Encoding Method field "10" - date and lot number */
 
this.binary_string += "10";
 
if (this.content.charAt(1) == '0') {
/* No date data */
this.binary_string += "11";
read_posn = 2;
} else {
/* Production Date (11) or Expiration Date (17) */
group_val = (10 * (this.content.charAt(2) - '0') + this.content.charAt(3) - '0') * 384;
group_val += (10 * (this.content.charAt(4) - '0') + this.content.charAt(5) - '0' - 1) * 32;
group_val += 10 * (this.content.charAt(6) - '0') + this.content.charAt(7) - '0';
 
for (j = 0; j < 16; j++) {
if ((group_val & 0x8000 >> j) == 0) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
 
if (this.content.charAt(1) == '1') {
/* Production Date AI 11 */
this.binary_string += "0";
} else {
/* Expiration Date AI 17 */
this.binary_string += "1";
}
read_posn = 8;
}
 
if (read_posn + 2 < this.content.length()) {
if (this.content.charAt(read_posn) == '1' && this.content.charAt(read_posn + 1) == '0') {
/* Followed by AI 10 - strip this from general field */
read_posn += 2;
} else {
/* An FNC1 character needs to be inserted in the general field */
fnc1_latch = 1;
}
} else {
fnc1_latch = 1;
}
}
 
if (encoding_method == 3) {
/* Encodation Method field of "11" - AI 90 */
/*
* "This encodation method may be used if an element string with an AI 90 occurs at the
* start of the data message, and if the data field following the two-digit AI 90 starts
* with an alphanumeric string which complies with a specific format." (para 5.2.2)
*/
 
j = this.content.length();
for (i = this.content.length(); i > 2; i--) {
if (this.content.charAt(i - 1) == '[') {
j = i;
}
}
 
ninety = this.content.substring(2, j - 1);
 
/* Find out if the AI 90 data is alphabetic or numeric or both */
 
alpha = 0;
alphanum = 0;
numeric = 0;
 
for (i = 0; i < ninety.length(); i++) {
 
if (ninety.charAt(i) >= 'A' && ninety.charAt(i) <= 'Z') {
/* Character is alphabetic */
alpha += 1;
}
 
if (ninety.charAt(i) >= '0' && ninety.charAt(i) <= '9') {
/* Character is numeric */
numeric += 1;
}
 
switch (ninety.charAt(i)) {
case '*':
case ',':
case '-':
case '.':
case '/':
alphanum += 1;
break;
}
 
if (!(ninety.charAt(i) >= '0' && ninety.charAt(i) <= '9' || ninety.charAt(i) >= 'A' && ninety.charAt(i) <= 'Z')) {
if (ninety.charAt(i) != '*' && ninety.charAt(i) != ',' && ninety.charAt(i) != '-' && ninety.charAt(i) != '.' && ninety.charAt(i) != '/') {
/* An Invalid AI 90 character */
throw new OkapiException("Invalid AI 90 data");
}
}
}
 
/* must start with 0, 1, 2 or 3 digits followed by an uppercase character */
test1 = -1;
for (i = 3; i >= 0; i--) {
if (ninety.charAt(i) >= 'A' && ninety.charAt(i) <= 'Z') {
test1 = i;
}
}
 
test2 = 0;
for (i = 0; i < test1; i++) {
if (!(ninety.charAt(i) >= '0' && ninety.charAt(i) <= '9')) {
test2 = 1;
}
}
 
/* leading zeros are not permitted */
test3 = 0;
if (test1 >= 1 && ninety.charAt(0) == '0') {
test3 = 1;
}
 
if (test1 != -1 && test2 != 1 && test3 == 0) {
/* Encodation method "11" can be used */
this.binary_string += "11";
 
numeric -= test1;
alpha--;
 
/* Decide on numeric, alpha or alphanumeric mode */
/* Alpha mode is a special mode for AI 90 */
 
if (alphanum > 0) {
/* Alphanumeric mode */
this.binary_string += "0";
ai90_mode = 1;
} else {
if (alpha > numeric) {
/* Alphabetic mode */
this.binary_string += "11";
ai90_mode = 2;
} else {
/* Numeric mode */
this.binary_string += "10";
ai90_mode = 3;
}
}
 
next_ai_posn = 2 + ninety.length();
 
if (this.content.charAt(next_ai_posn) == '[') {
/* There are more AIs afterwords */
if (this.content.charAt(next_ai_posn + 1) == '2' && this.content.charAt(next_ai_posn + 2) == '1') {
/* AI 21 follows */
ai_crop = 1;
}
 
if (this.content.charAt(next_ai_posn + 1) == '8' && this.content.charAt(next_ai_posn + 2) == '0' && this.content.charAt(next_ai_posn + 3) == '0'
&& this.content.charAt(next_ai_posn + 4) == '4') {
/* AI 8004 follows */
ai_crop = 2;
}
}
 
switch (ai_crop) {
case 0:
this.binary_string += "0";
break;
case 1:
this.binary_string += "10";
break;
case 2:
this.binary_string += "11";
break;
}
 
if (test1 == 0) {
numeric_part = "0";
} else {
numeric_part = ninety.substring(0, test1);
}
 
numeric_value = 0;
for (i = 0; i < numeric_part.length(); i++) {
numeric_value *= 10;
numeric_value += numeric_part.charAt(i) - '0';
}
 
table3_letter = -1;
if (numeric_value < 31) {
switch (ninety.charAt(test1)) {
case 'B':
table3_letter = 0;
break;
case 'D':
table3_letter = 1;
break;
case 'H':
table3_letter = 2;
break;
case 'I':
table3_letter = 3;
break;
case 'J':
table3_letter = 4;
break;
case 'K':
table3_letter = 5;
break;
case 'L':
table3_letter = 6;
break;
case 'N':
table3_letter = 7;
break;
case 'P':
table3_letter = 8;
break;
case 'Q':
table3_letter = 9;
break;
case 'R':
table3_letter = 10;
break;
case 'S':
table3_letter = 11;
break;
case 'T':
table3_letter = 12;
break;
case 'V':
table3_letter = 13;
break;
case 'W':
table3_letter = 14;
break;
case 'Z':
table3_letter = 15;
break;
}
}
 
if (table3_letter != -1) {
/* Encoding can be done according to 5.2.2 c) 2) */
/* five bit binary string representing value before letter */
for (j = 0; j < 5; j++) {
if ((numeric_value & 0x10 >> j) == 0x00) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
 
/* followed by four bit representation of letter from Table 3 */
for (j = 0; j < 4; j++) {
if ((table3_letter & 0x08 >> j) == 0x00) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
} else {
/* Encoding is done according to 5.2.2 c) 3) */
this.binary_string += "11111";
/* ten bit representation of number */
for (j = 0; j < 10; j++) {
if ((numeric_value & 0x200 >> j) == 0x00) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
 
/* five bit representation of ASCII character */
for (j = 0; j < 5; j++) {
if ((ninety.charAt(test1) - 65 & 0x10 >> j) == 0x00) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
}
 
read_posn = test1 + 3;
} else {
/* Use general field encodation instead */
this.binary_string += "0";
read_posn = 0;
}
 
/* Now encode the rest of the AI 90 data field */
if (ai90_mode == 2) {
/* Alpha encodation (section 5.2.3) */
do {
if (this.content.charAt(read_posn) >= '0' && this.content.charAt(read_posn) <= '9') {
for (j = 0; j < 5; j++) {
if ((this.content.charAt(read_posn) + 4 & 0x10 >> j) == 0x00) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
}
 
if (this.content.charAt(read_posn) >= 'A' && this.content.charAt(read_posn) <= 'Z') {
for (j = 0; j < 6; j++) {
if ((this.content.charAt(read_posn) - 65 & 0x20 >> j) == 0x00) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
}
 
if (this.content.charAt(read_posn) == '[') {
this.binary_string += "11111";
}
 
read_posn++;
} while (this.content.charAt(read_posn - 1) != '[' && read_posn < this.content.length());
alpha_pad = 1; /* This is overwritten if a general field is encoded */
}
 
if (ai90_mode == 1) {
/* Alphanumeric mode */
do {
if (this.content.charAt(read_posn) >= '0' && this.content.charAt(read_posn) <= '9') {
for (j = 0; j < 5; j++) {
if ((this.content.charAt(read_posn) - 43 & 0x10 >> j) == 0x00) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
}
 
if (this.content.charAt(read_posn) >= 'A' && this.content.charAt(read_posn) <= 'Z') {
for (j = 0; j < 6; j++) {
if ((this.content.charAt(read_posn) - 33 & 0x20 >> j) == 0x00) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
}
 
switch (this.content.charAt(read_posn)) {
case '[':
this.binary_string += "01111";
break;
case '*':
this.binary_string += "111010";
break;
case ',':
this.binary_string += "111011";
break;
case '-':
this.binary_string += "111100";
break;
case '.':
this.binary_string += "111101";
break;
case '/':
this.binary_string += "111110";
break;
}
 
read_posn++;
} while (this.content.charAt(read_posn - 1) != '[' && this.content.charAt(read_posn - 1) != '\0');
}
 
read_posn += 2 * ai_crop;
}
 
/*
* The compressed data field has been processed if appropriate - the rest of the data (if
* any) goes into a general-purpose data compaction field
*/
 
j = 0;
this.general_field = "";
if (fnc1_latch == 1) {
/*
* Encodation method "10" has been used but it is not followed by AI 10, so a FNC1
* character needs to be added
*/
this.general_field += "[";
}
 
this.general_field += this.content.substring(read_posn);
 
latch = false;
if (this.general_field.length() != 0) {
alpha_pad = 0;
 
this.general_field_type = new GeneralFieldMode[this.general_field.length()];
 
for (i = 0; i < this.general_field.length(); i++) {
/* Table 13 - ISO/IEC 646 encodation */
if (this.general_field.charAt(i) < ' ' || this.general_field.charAt(i) > 'z') {
this.general_field_type[i] = GeneralFieldMode.INVALID_CHAR;
latch = true;
} else {
this.general_field_type[i] = GeneralFieldMode.ISOIEC;
}
 
if (this.general_field.charAt(i) == '#') {
this.general_field_type[i] = GeneralFieldMode.INVALID_CHAR;
latch = true;
}
if (this.general_field.charAt(i) == '$') {
this.general_field_type[i] = GeneralFieldMode.INVALID_CHAR;
latch = true;
}
if (this.general_field.charAt(i) == '@') {
this.general_field_type[i] = GeneralFieldMode.INVALID_CHAR;
latch = true;
}
if (this.general_field.charAt(i) == 92) {
this.general_field_type[i] = GeneralFieldMode.INVALID_CHAR;
latch = true;
}
if (this.general_field.charAt(i) == '^') {
this.general_field_type[i] = GeneralFieldMode.INVALID_CHAR;
latch = true;
}
if (this.general_field.charAt(i) == 96) {
this.general_field_type[i] = GeneralFieldMode.INVALID_CHAR;
latch = true;
}
 
/* Table 12 - Alphanumeric encodation */
if (this.general_field.charAt(i) >= 'A' && this.general_field.charAt(i) <= 'Z') {
this.general_field_type[i] = GeneralFieldMode.ALPHA_OR_ISO;
}
if (this.general_field.charAt(i) == '*') {
this.general_field_type[i] = GeneralFieldMode.ALPHA_OR_ISO;
}
if (this.general_field.charAt(i) == ',') {
this.general_field_type[i] = GeneralFieldMode.ALPHA_OR_ISO;
}
if (this.general_field.charAt(i) == '-') {
this.general_field_type[i] = GeneralFieldMode.ALPHA_OR_ISO;
}
if (this.general_field.charAt(i) == '.') {
this.general_field_type[i] = GeneralFieldMode.ALPHA_OR_ISO;
}
if (this.general_field.charAt(i) == '/') {
this.general_field_type[i] = GeneralFieldMode.ALPHA_OR_ISO;
}
 
/* Numeric encodation */
if (this.general_field.charAt(i) >= '0' && this.general_field.charAt(i) <= '9') {
this.general_field_type[i] = GeneralFieldMode.ANY_ENC;
}
if (this.general_field.charAt(i) == '[') {
/* FNC1 can be encoded in any system */
this.general_field_type[i] = GeneralFieldMode.ANY_ENC;
}
 
}
 
if (latch) {
/* Invalid characters in input data */
throw new OkapiException("Invalid characters in input data");
}
 
for (i = 0; i < this.general_field.length() - 1; i++) {
if (this.general_field_type[i] == GeneralFieldMode.ISOIEC && this.general_field.charAt(i + 1) == '[') {
this.general_field_type[i + 1] = GeneralFieldMode.ISOIEC;
}
}
 
for (i = 0; i < this.general_field.length() - 1; i++) {
if (this.general_field_type[i] == GeneralFieldMode.ALPHA_OR_ISO && this.general_field.charAt(i + 1) == '[') {
this.general_field_type[i + 1] = GeneralFieldMode.ALPHA_OR_ISO;
}
}
 
latch = applyGeneralFieldRules();
 
i = 0;
do {
switch (this.general_field_type[i]) {
case NUMERIC:
if (i != 0) {
if (this.general_field_type[i - 1] != GeneralFieldMode.NUMERIC && this.general_field.charAt(i - 1) != '[') {
this.binary_string += "000"; /* Numeric latch */
}
}
 
if (this.general_field.charAt(i) != '[') {
d1 = this.general_field.charAt(i) - '0';
} else {
d1 = 10;
}
 
if (i < this.general_field.length() - 1) {
if (this.general_field.charAt(i + 1) != '[') {
d2 = this.general_field.charAt(i + 1) - '0';
} else {
d2 = 10;
}
} else {
d2 = 10;
}
 
if (d1 != 10 || d2 != 10) {
/* If (d1==10)&&(d2==10) then input is either FNC1,FNC1 or FNC1,EOL */
value = 11 * d1 + d2 + 8;
 
for (j = 0; j < 7; j++) {
if ((value & 0x40 >> j) == 0x00) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
 
i += 2;
}
break;
 
case ALPHA:
if (i != 0) {
if (this.general_field_type[i - 1] == GeneralFieldMode.NUMERIC || this.general_field.charAt(i - 1) == '[') {
this.binary_string += "0000"; /* Alphanumeric latch */
}
if (this.general_field_type[i - 1] == GeneralFieldMode.ISOIEC) {
this.binary_string += "00100"; /* ISO/IEC 646 latch */
}
}
 
if (this.general_field.charAt(i) >= '0' && this.general_field.charAt(i) <= '9') {
 
value = this.general_field.charAt(i) - 43;
 
for (j = 0; j < 5; j++) {
if ((value & 0x10 >> j) == 0x00) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
}
 
if (this.general_field.charAt(i) >= 'A' && this.general_field.charAt(i) <= 'Z') {
 
value = this.general_field.charAt(i) - 33;
 
for (j = 0; j < 6; j++) {
if ((value & 0x20 >> j) == 0x00) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
}
 
if (this.general_field.charAt(i) == '[') {
this.binary_string += "01111"; /* FNC1/Numeric latch */
}
if (this.general_field.charAt(i) == '*') {
this.binary_string += "111010"; /* asterisk */
}
if (this.general_field.charAt(i) == ',') {
this.binary_string += "111011"; /* comma */
}
if (this.general_field.charAt(i) == '-') {
this.binary_string += "111100"; /* minus or hyphen */
}
if (this.general_field.charAt(i) == '.') {
this.binary_string += "111101"; /* period or full stop */
}
if (this.general_field.charAt(i) == '/') {
this.binary_string += "111110"; /* slash or solidus */
}
 
i++;
break;
 
case ISOIEC:
if (i != 0) {
if (this.general_field_type[i - 1] == GeneralFieldMode.NUMERIC || this.general_field.charAt(i - 1) == '[') {
this.binary_string += "0000"; /* Alphanumeric latch */
this.binary_string += "00100"; /* ISO/IEC 646 latch */
}
if (this.general_field_type[i - 1] == GeneralFieldMode.ALPHA) {
this.binary_string += "00100"; /* ISO/IEC 646 latch */
}
}
 
if (this.general_field.charAt(i) >= '0' && this.general_field.charAt(i) <= '9') {
 
value = this.general_field.charAt(i) - 43;
 
for (j = 0; j < 5; j++) {
if ((value & 0x10 >> j) == 0x00) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
}
 
if (this.general_field.charAt(i) >= 'A' && this.general_field.charAt(i) <= 'Z') {
 
value = this.general_field.charAt(i) - 1;
 
for (j = 0; j < 7; j++) {
if ((value & 0x40 >> j) == 0x00) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
}
 
if (this.general_field.charAt(i) >= 'a' && this.general_field.charAt(i) <= 'z') {
 
value = this.general_field.charAt(i) - 7;
 
for (j = 0; j < 7; j++) {
if ((value & 0x40 >> j) == 0x00) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
}
 
if (this.general_field.charAt(i) == '[') {
this.binary_string += "01111"; /* FNC1/Numeric latch */
}
if (this.general_field.charAt(i) == '!') {
this.binary_string += "11101000"; /* exclamation mark */
}
if (this.general_field.charAt(i) == 34) {
this.binary_string += "11101001"; /* quotation mark */
}
if (this.general_field.charAt(i) == 37) {
this.binary_string += "11101010"; /* percent sign */
}
if (this.general_field.charAt(i) == '&') {
this.binary_string += "11101011"; /* ampersand */
}
if (this.general_field.charAt(i) == 39) {
this.binary_string += "11101100"; /* apostrophe */
}
if (this.general_field.charAt(i) == '(') {
this.binary_string += "11101101"; /* left parenthesis */
}
if (this.general_field.charAt(i) == ')') {
this.binary_string += "11101110"; /* right parenthesis */
}
if (this.general_field.charAt(i) == '*') {
this.binary_string += "11101111"; /* asterisk */
}
if (this.general_field.charAt(i) == '+') {
this.binary_string += "11110000"; /* plus sign */
}
if (this.general_field.charAt(i) == ',') {
this.binary_string += "11110001"; /* comma */
}
if (this.general_field.charAt(i) == '-') {
this.binary_string += "11110010"; /* minus or hyphen */
}
if (this.general_field.charAt(i) == '.') {
this.binary_string += "11110011"; /* period or full stop */
}
if (this.general_field.charAt(i) == '/') {
this.binary_string += "11110100"; /* slash or solidus */
}
if (this.general_field.charAt(i) == ':') {
this.binary_string += "11110101"; /* colon */
}
if (this.general_field.charAt(i) == ';') {
this.binary_string += "11110110"; /* semicolon */
}
if (this.general_field.charAt(i) == '<') {
this.binary_string += "11110111"; /* less-than sign */
}
if (this.general_field.charAt(i) == '=') {
this.binary_string += "11111000"; /* equals sign */
}
if (this.general_field.charAt(i) == '>') {
this.binary_string += "11111001"; /* greater-than sign */
}
if (this.general_field.charAt(i) == '?') {
this.binary_string += "11111010"; /* question mark */
}
if (this.general_field.charAt(i) == '_') {
this.binary_string += "11111011"; /* underline or low line */
}
if (this.general_field.charAt(i) == ' ') {
this.binary_string += "11111100"; /* space */
}
 
i++;
break;
}
 
latchOffset = 0;
if (latch) {
latchOffset = 1;
}
} while (i + latchOffset < this.general_field.length());
}
 
if (!calculateSymbolSize()) {
return false;
}
 
if (latch) {
i = this.general_field.length() - 1;
/* There is still one more numeric digit to encode */
 
if (this.general_field.charAt(i) == '[') {
this.binary_string += "000001111";
} else {
if (this.remainder >= 4 && this.remainder <= 6) {
d1 = this.general_field.charAt(i) - '0';
d1++;
 
for (j = 0; j < 4; j++) {
if ((value & 0x08 >> j) == 0x00) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
} else {
d1 = this.general_field.charAt(i) - '0';
d2 = 10;
 
value = 11 * d1 + d2 + 8;
 
for (j = 0; j < 7; j++) {
if ((value & 0x40 >> j) == 0x00) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
/* This may push the symbol up to the next size */
}
}
}
 
if (this.binary_string.length() > 11805) { /* (2361 * 5) */
throw new OkapiException("Input too long");
}
 
/* size of the symbol may have changed when adding data in the above sequence */
if (!calculateSymbolSize()) {
return false;
}
 
infoLine("Composite Binary Length: " + this.binary_string.length());
displayBinaryString();
 
if (this.binary_string.length() < this.target_bitsize) {
/* Now add padding to binary string */
if (alpha_pad == 1) {
this.binary_string += "11111";
alpha_pad = 0;
/* Extra FNC1 character required after Alpha encodation (section 5.2.3) */
}
 
if (this.general_field.length() != 0 && this.general_field_type[this.general_field.length() - 1] == GeneralFieldMode.NUMERIC) {
this.binary_string += "0000";
}
 
while (this.binary_string.length() < this.target_bitsize) {
this.binary_string += "00100";
}
 
this.binary_string = this.binary_string.substring(0, this.target_bitsize);
}
 
return true;
}
 
private void displayBinaryString() {
int i, nibble;
/* Display binary string as hexadecimal */
 
info("Composite Binary String: ");
nibble = 0;
for (i = 0; i < this.binary_string.length(); i++) {
switch (i % 4) {
case 0:
if (this.binary_string.charAt(i) == '1') {
nibble += 8;
}
break;
case 1:
if (this.binary_string.charAt(i) == '1') {
nibble += 4;
}
break;
case 2:
if (this.binary_string.charAt(i) == '1') {
nibble += 2;
}
break;
case 3:
if (this.binary_string.charAt(i) == '1') {
nibble += 1;
}
info(Integer.toHexString(nibble));
nibble = 0;
break;
}
}
 
if (this.binary_string.length() % 4 != 0) {
info(Integer.toHexString(nibble));
}
infoLine();
}
 
private boolean applyGeneralFieldRules() {
/*
* Attempts to apply encoding rules from secions 7.2.5.5.1 to 7.2.5.5.3 of ISO/IEC
* 24724:2006
*/
 
int block_count, i, j, k;
GeneralFieldMode current, next, last;
final int[] blockLength = new int[200];
final GeneralFieldMode[] blockType = new GeneralFieldMode[200];
 
block_count = 0;
 
blockLength[block_count] = 1;
blockType[block_count] = this.general_field_type[0];
 
for (i = 1; i < this.general_field.length(); i++) {
current = this.general_field_type[i];
last = this.general_field_type[i - 1];
 
if (current == last) {
blockLength[block_count] = blockLength[block_count] + 1;
} else {
block_count++;
blockLength[block_count] = 1;
blockType[block_count] = this.general_field_type[i];
}
}
 
block_count++;
 
for (i = 0; i < block_count; i++) {
current = blockType[i];
next = blockType[i + 1];
 
if (current == GeneralFieldMode.ISOIEC && i != block_count - 1) {
if (next == GeneralFieldMode.ANY_ENC && blockLength[i + 1] >= 4) {
blockType[i + 1] = GeneralFieldMode.NUMERIC;
}
if (next == GeneralFieldMode.ANY_ENC && blockLength[i + 1] < 4) {
blockType[i + 1] = GeneralFieldMode.ISOIEC;
}
if (next == GeneralFieldMode.ALPHA_OR_ISO && blockLength[i + 1] >= 5) {
blockType[i + 1] = GeneralFieldMode.ALPHA;
}
if (next == GeneralFieldMode.ALPHA_OR_ISO && blockLength[i + 1] < 5) {
blockType[i + 1] = GeneralFieldMode.ISOIEC;
}
}
 
if (current == GeneralFieldMode.ALPHA_OR_ISO) {
blockType[i] = GeneralFieldMode.ALPHA;
}
 
if (current == GeneralFieldMode.ALPHA && i != block_count - 1) {
if (next == GeneralFieldMode.ANY_ENC && blockLength[i + 1] >= 6) {
blockType[i + 1] = GeneralFieldMode.NUMERIC;
}
if (next == GeneralFieldMode.ANY_ENC && blockLength[i + 1] < 6) {
if (i == block_count - 2 && blockLength[i + 1] >= 4) {
blockType[i + 1] = GeneralFieldMode.NUMERIC;
} else {
blockType[i + 1] = GeneralFieldMode.ALPHA;
}
}
}
 
if (current == GeneralFieldMode.ANY_ENC) {
blockType[i] = GeneralFieldMode.NUMERIC;
}
}
 
if (block_count > 1) {
i = 1;
while (i < block_count) {
if (blockType[i - 1] == blockType[i]) {
/* bring together */
blockLength[i - 1] = blockLength[i - 1] + blockLength[i];
j = i + 1;
 
/* decreace the list */
while (j < block_count) {
blockLength[j - 1] = blockLength[j];
blockType[j - 1] = blockType[j];
j++;
}
block_count--;
i--;
}
i++;
}
}
 
for (i = 0; i < block_count - 1; i++) {
if (blockType[i] == GeneralFieldMode.NUMERIC && (blockLength[i] & 1) != 0) {
/* Odd size numeric block */
blockLength[i] = blockLength[i] - 1;
blockLength[i + 1] = blockLength[i + 1] + 1;
}
}
 
j = 0;
for (i = 0; i < block_count; i++) {
for (k = 0; k < blockLength[i]; k++) {
this.general_field_type[j] = blockType[i];
j++;
}
}
 
if (blockType[block_count - 1] == GeneralFieldMode.NUMERIC && (blockLength[block_count - 1] & 1) != 0) {
/*
* If the last block is numeric and an odd size, further processing needs to be done
* outside this procedure
*/
return true;
} else {
return false;
}
}
 
private void cc_a() {
/* CC-A 2D component */
int i, strpos, segment, cwCnt, variant, rows;
int k, offset, j, total;
final int[] rsCodeWords = new int[8];
int LeftRAPStart, RightRAPStart, CentreRAPStart, StartCluster;
int LeftRAP, RightRAP, CentreRAP, Cluster;
final int[] dummy = new int[5];
int flip, loop;
String codebarre;
final StringBuilder bin = new StringBuilder();
String local_source; /* A copy of source but wi