Dépôt officiel du code source de l'ERP OpenConcerto
/trunk/Modules/Module Batch Processing/src/org/openconcerto/modules/common/batchprocessing/ReferenceProcessor.java |
---|
18,18 → 18,18 |
public class ReferenceProcessor extends JPanel implements BatchProcessor { |
private final SQLField field; |
private final BatchField field; |
final SQLElement element; |
private ElementComboBox combo; |
public ReferenceProcessor(SQLField field) { |
public ReferenceProcessor(BatchField field) { |
this.field = field; |
this.element = ComptaPropsConfiguration.getInstanceCompta().getDirectory().getElement(field.getForeignTable()); |
this.element = ComptaPropsConfiguration.getInstanceCompta().getDirectory().getElement(field.getField().getForeignTable()); |
if (element != null) { |
this.setLayout(new BorderLayout()); |
this.add(new JLabel("remplacer par "), BorderLayout.WEST); |
combo = new ElementComboBox(true, 200); |
combo = new ElementComboBox(true, 10); |
combo.setMinimal(); |
combo.setAddIconVisible(false); |
combo.init(element); |
36,7 → 36,7 |
this.add(combo, BorderLayout.CENTER); |
} else { |
this.setLayout(new FlowLayout()); |
this.add(new JLabelWarning("No element for table " + field.getTable().getName())); |
this.add(new JLabelWarning("No element for table " + field.getField().getTable().getName())); |
} |
} |
45,7 → 45,7 |
for (SQLRowAccessor sqlRowAccessor : r) { |
final SQLRowValues rowValues = sqlRowAccessor.createEmptyUpdateRow(); |
rowValues.put(field.getName(), combo.getSelectedId()); |
rowValues.put(field.getField().getName(), combo.getSelectedId()); |
processBeforeUpdate(sqlRowAccessor, rowValues); |
rowValues.update(); |
} |
/trunk/Modules/Module Batch Processing/src/org/openconcerto/modules/common/batchprocessing/product/PurchaseProcessor.java |
---|
2,14 → 2,14 |
import java.math.BigDecimal; |
import org.openconcerto.modules.common.batchprocessing.BatchField; |
import org.openconcerto.modules.common.batchprocessing.NumberProcessor; |
import org.openconcerto.sql.model.SQLField; |
import org.openconcerto.sql.model.SQLRowAccessor; |
import org.openconcerto.sql.model.SQLRowValues; |
public class PurchaseProcessor extends NumberProcessor { |
public PurchaseProcessor(SQLField field) { |
public PurchaseProcessor(BatchField field) { |
super(field); |
} |
/trunk/Modules/Module Batch Processing/src/org/openconcerto/modules/common/batchprocessing/product/TTCProcessor.java |
---|
4,8 → 4,8 |
import org.openconcerto.erp.core.finance.tax.model.TaxeCache; |
import org.openconcerto.erp.utils.ConvertDevise; |
import org.openconcerto.modules.common.batchprocessing.BatchField; |
import org.openconcerto.modules.common.batchprocessing.NumberProcessor; |
import org.openconcerto.sql.model.SQLField; |
import org.openconcerto.sql.model.SQLRow; |
import org.openconcerto.sql.model.SQLRowAccessor; |
import org.openconcerto.sql.model.SQLRowValues; |
12,7 → 12,7 |
public class TTCProcessor extends NumberProcessor { |
public TTCProcessor(SQLField field) { |
public TTCProcessor(BatchField field) { |
super(field); |
} |
/trunk/Modules/Module Batch Processing/src/org/openconcerto/modules/common/batchprocessing/product/TVAProcessor.java |
---|
4,8 → 4,8 |
import org.openconcerto.erp.core.finance.tax.model.TaxeCache; |
import org.openconcerto.erp.utils.ConvertDevise; |
import org.openconcerto.modules.common.batchprocessing.BatchField; |
import org.openconcerto.modules.common.batchprocessing.ReferenceProcessor; |
import org.openconcerto.sql.model.SQLField; |
import org.openconcerto.sql.model.SQLRow; |
import org.openconcerto.sql.model.SQLRowAccessor; |
import org.openconcerto.sql.model.SQLRowValues; |
12,7 → 12,7 |
public class TVAProcessor extends ReferenceProcessor { |
public TVAProcessor(SQLField field) { |
public TVAProcessor(BatchField field) { |
super(field); |
} |
/trunk/Modules/Module Batch Processing/src/org/openconcerto/modules/common/batchprocessing/product/HTProcessor.java |
---|
4,8 → 4,8 |
import org.openconcerto.erp.core.finance.tax.model.TaxeCache; |
import org.openconcerto.erp.utils.ConvertDevise; |
import org.openconcerto.modules.common.batchprocessing.BatchField; |
import org.openconcerto.modules.common.batchprocessing.NumberProcessor; |
import org.openconcerto.sql.model.SQLField; |
import org.openconcerto.sql.model.SQLRow; |
import org.openconcerto.sql.model.SQLRowAccessor; |
import org.openconcerto.sql.model.SQLRowValues; |
12,7 → 12,7 |
public class HTProcessor extends NumberProcessor { |
public HTProcessor(SQLField field) { |
public HTProcessor(BatchField field) { |
super(field); |
} |
/trunk/Modules/Module Batch Processing/src/org/openconcerto/modules/common/batchprocessing/StringProcessor.java |
---|
29,8 → 29,8 |
private JRadioButton bLower; |
private JRadioButton bUpper; |
public StringProcessor(SQLField field) { |
this.field = field; |
public StringProcessor(BatchField field) { |
this.field = field.getField(); |
this.setLayout(new GridBagLayout()); |
bReplace = new JRadioButton("remplacer par"); |
/trunk/Modules/Module Batch Processing/src/org/openconcerto/modules/common/batchprocessing/BatchEditorPanel.java |
---|
24,10 → 24,13 |
import javax.swing.SwingUtilities; |
import javax.swing.SwingWorker; |
import org.openconcerto.sql.Configuration; |
import org.openconcerto.sql.PropsConfiguration; |
import org.openconcerto.sql.element.SQLElementDirectory; |
import org.openconcerto.sql.model.SQLField; |
import org.openconcerto.sql.model.SQLRow; |
import org.openconcerto.sql.model.SQLRowListRSH; |
import org.openconcerto.sql.model.SQLRowValues; |
import org.openconcerto.sql.model.SQLSelect; |
import org.openconcerto.sql.model.SQLTable; |
import org.openconcerto.sql.request.SQLFieldTranslator; |
import org.openconcerto.ui.DefaultGridBagConstraints; |
import org.openconcerto.ui.JLabelBold; |
36,24 → 39,34 |
public class BatchEditorPanel extends JPanel { |
public BatchEditorPanel(final List<SQLRowValues> rows, FieldFilter filter) { |
Configuration conf = PropsConfiguration.getInstance(); |
final SQLFieldTranslator translator = conf.getTranslator(); |
public BatchEditorPanel(final SQLElementDirectory dir, final List<SQLRowValues> rows, FieldFilter filter) { |
SQLFieldTranslator translator = dir.getTranslator(); |
Set<SQLField> fields = rows.get(0).getTable().getFields(); |
List<SQLField> f = new ArrayList<SQLField>(); |
List<BatchField> f = new ArrayList<BatchField>(); |
for (SQLField sqlField : fields) { |
if (ForbiddenFieldName.isAllowed(sqlField.getName()) && translator.getLabelFor(sqlField) != null) { |
if (filter == null || !filter.isFiltered(sqlField)) { |
f.add(sqlField); |
f.add(new BatchField(dir, sqlField, null)); |
} |
} |
} |
SQLTable tableTarif = rows.get(0).getTable().getTable("TARIF"); |
SQLTable tableArticleTarif = rows.get(0).getTable().getTable("ARTICLE_TARIF"); |
SQLSelect sel = new SQLSelect(); |
sel.addSelectStar(tableTarif); |
List<SQLRow> rowTarif = SQLRowListRSH.execute(sel); |
for (SQLRow sqlRow : rowTarif) { |
f.add(new BatchField(dir, tableArticleTarif.getField("PV_HT"), sqlRow)); |
if (tableArticleTarif.contains("POURCENT_REMISE")) { |
f.add(new BatchField(dir, tableArticleTarif.getField("POURCENT_REMISE"), sqlRow)); |
} |
} |
Collections.sort(f, new Comparator<SQLField>() { |
Collections.sort(f, new Comparator<BatchField>() { |
@Override |
public int compare(SQLField o1, SQLField o2) { |
return translator.getLabelFor(o1).compareToIgnoreCase(translator.getLabelFor(o2)); |
public int compare(BatchField o1, BatchField o2) { |
return o1.getComboName().compareToIgnoreCase(o2.getComboName()); |
} |
}); |
this.setLayout(new GridBagLayout()); |
60,11 → 73,11 |
GridBagConstraints c = new DefaultGridBagConstraints(); |
this.add(new JLabel("Champ"), c); |
final JComboBox<SQLField> combo = new JComboBox<SQLField>(f.toArray(new SQLField[1])); |
final JComboBox<BatchField> combo = new JComboBox<BatchField>(f.toArray(new BatchField[1])); |
combo.setRenderer(new DefaultListCellRenderer() { |
@Override |
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { |
String label = translator.getLabelFor(((SQLField) value)); |
String label = ((BatchField) value).getComboName(); |
return super.getListCellRendererComponent(list, label, index, isSelected, cellHasFocus); |
} |
}); |
86,7 → 99,7 |
c.gridy++; |
c.anchor = GridBagConstraints.NORTHWEST; |
final BatchDetailPanel comp = new BatchDetailPanel(); |
comp.setField((SQLField) combo.getSelectedItem()); |
comp.setField((BatchField) combo.getSelectedItem()); |
this.add(comp, c); |
JPanel actions = new JPanel(); |
108,7 → 121,7 |
@Override |
public void itemStateChanged(ItemEvent e) { |
comp.setField((SQLField) combo.getSelectedItem()); |
comp.setField((BatchField) combo.getSelectedItem()); |
} |
}); |
/trunk/Modules/Module Batch Processing/src/org/openconcerto/modules/common/batchprocessing/DateProcessor.java |
---|
17,8 → 17,8 |
private final JDate d = new JDate(true); |
private final SQLField field; |
public DateProcessor(SQLField field) { |
this.field = field; |
public DateProcessor(BatchField field) { |
this.field = field.getField(); |
this.setLayout(new FlowLayout()); |
this.add(new JLabel("forcer la date au ")); |
this.add(d); |
/trunk/Modules/Module Batch Processing/src/org/openconcerto/modules/common/batchprocessing/BooleanProcessor.java |
---|
13,13 → 13,13 |
import org.openconcerto.ui.VFlowLayout; |
public class BooleanProcessor extends JPanel implements BatchProcessor { |
private final SQLField field; |
private final BatchField field; |
private JRadioButton bTrue; |
private JRadioButton bFalse; |
private JRadioButton bInvert; |
public BooleanProcessor(SQLField field) { |
public BooleanProcessor(BatchField field) { |
this.field = field; |
this.setLayout(new VFlowLayout()); |
bTrue = new JRadioButton("forcer à vrai"); |
41,7 → 41,7 |
if (bTrue.isSelected()) { |
for (SQLRowAccessor sqlRowAccessor : r) { |
final SQLRowValues rowValues = sqlRowAccessor.createEmptyUpdateRow(); |
rowValues.put(field.getName(), Boolean.TRUE); |
rowValues.put(field.getField().getName(), Boolean.TRUE); |
processBeforeUpdate(sqlRowAccessor, rowValues); |
rowValues.update(); |
} |
48,7 → 48,7 |
} else if (bFalse.isSelected()) { |
for (SQLRowAccessor sqlRowAccessor : r) { |
final SQLRowValues rowValues = sqlRowAccessor.createEmptyUpdateRow(); |
rowValues.put(field.getName(), Boolean.FALSE); |
rowValues.put(field.getField().getName(), Boolean.FALSE); |
processBeforeUpdate(sqlRowAccessor, rowValues); |
rowValues.update(); |
} |
55,9 → 55,9 |
} else if (bInvert.isSelected()) { |
for (SQLRowAccessor sqlRowAccessor : r) { |
final SQLRowValues rowValues = sqlRowAccessor.createEmptyUpdateRow(); |
final Boolean boolean1 = sqlRowAccessor.asRow().getBoolean(field.getName()); |
final Boolean boolean1 = sqlRowAccessor.asRow().getBoolean(field.getField().getName()); |
if (boolean1 != null) { |
rowValues.put(field.getName(), boolean1.equals(Boolean.FALSE)); |
rowValues.put(field.getField().getName(), boolean1.equals(Boolean.FALSE)); |
processBeforeUpdate(sqlRowAccessor, rowValues); |
rowValues.update(); |
} |
/trunk/Modules/Module Batch Processing/src/org/openconcerto/modules/common/batchprocessing/BatchDetailPanel.java |
---|
22,46 → 22,47 |
this.setLayout(new VFlowLayout()); |
} |
public void setField(SQLField field) { |
public void setField(BatchField batchField) { |
this.removeAll(); |
SQLField field = batchField.getField(); |
final SQLType type = field.getType(); |
final Class<?> javaType = type.getJavaType(); |
final String fName = field.getName(); |
if (fName.equals("PV_TTC")) { |
final NumberProcessor p = new TTCProcessor(field); |
final NumberProcessor p = new TTCProcessor(batchField); |
this.add(p); |
this.processor = p; |
} else if (fName.equals("PV_HT")) { |
final NumberProcessor p = new HTProcessor(field); |
final NumberProcessor p = new HTProcessor(batchField); |
this.add(p); |
this.processor = p; |
} else if (fName.equals("ID_TAXE")) { |
final ReferenceProcessor p = new TVAProcessor(field); |
final ReferenceProcessor p = new TVAProcessor(batchField); |
this.add(p); |
this.processor = p; |
} else if (fName.equals("PA_HT")) { |
final NumberProcessor p = new PurchaseProcessor(field); |
final NumberProcessor p = new PurchaseProcessor(batchField); |
this.add(p); |
this.processor = p; |
} else if (javaType.equals(Boolean.class)) { |
final BooleanProcessor p = new BooleanProcessor(field); |
final BooleanProcessor p = new BooleanProcessor(batchField); |
this.add(p); |
this.processor = p; |
} else if (field.isKey()) { |
final ReferenceProcessor p = new ReferenceProcessor(field); |
final ReferenceProcessor p = new ReferenceProcessor(batchField); |
this.add(p); |
this.processor = p; |
} else if (javaType.equals(String.class)) { |
final StringProcessor p = new StringProcessor(field); |
final StringProcessor p = new StringProcessor(batchField); |
this.add(p); |
this.processor = p; |
} else if (javaType.equals(Date.class)) { |
final DateProcessor p = new DateProcessor(field); |
final DateProcessor p = new DateProcessor(batchField); |
this.add(p); |
this.processor = p; |
} else if (javaType.equals(BigDecimal.class) || javaType.equals(Float.class) || javaType.equals(Double.class) || javaType.equals(Integer.class) || javaType.equals(Long.class)) { |
final NumberProcessor p = new NumberProcessor(field); |
final NumberProcessor p = new NumberProcessor(batchField); |
this.add(p); |
this.processor = p; |
} |
/trunk/Modules/Module Batch Processing/src/org/openconcerto/modules/common/batchprocessing/BatchField.java |
---|
New file |
0,0 → 1,58 |
package org.openconcerto.modules.common.batchprocessing; |
import java.util.List; |
import org.openconcerto.sql.element.SQLElement; |
import org.openconcerto.sql.element.SQLElementDirectory; |
import org.openconcerto.sql.model.SQLField; |
import org.openconcerto.sql.model.SQLRow; |
import org.openconcerto.sql.model.SQLRowAccessor; |
import org.openconcerto.sql.model.SQLRowListRSH; |
import org.openconcerto.sql.model.SQLSelect; |
import org.openconcerto.sql.model.Where; |
import org.openconcerto.sql.request.SQLFieldTranslator; |
public class BatchField { |
private final SQLField field; |
private final SQLRowAccessor foreignLinkRow; |
private final SQLFieldTranslator translator; |
private final SQLElement elementLink; |
public BatchField(SQLElementDirectory dir, SQLField field, SQLRowAccessor foreignLinkRow) { |
this.field = field; |
this.foreignLinkRow = foreignLinkRow; |
this.translator = dir.getTranslator(); |
if (foreignLinkRow == null) { |
this.elementLink = null; |
} else { |
this.elementLink = dir.getElement(foreignLinkRow.getTable()); |
} |
} |
public SQLField getField() { |
return field; |
} |
public SQLRowAccessor getForeignLinkRow() { |
return foreignLinkRow; |
} |
public String getComboName() { |
if (this.foreignLinkRow == null) { |
return this.translator.getLabelFor(this.field); |
} else { |
return this.elementLink.getPluralName() + " " + this.foreignLinkRow.getString("NOM") + " " + this.translator.getLabelFor(this.field); |
} |
} |
public List<SQLRow> getReferentRows(SQLRowAccessor rowOrigin) { |
SQLSelect sel = new SQLSelect(); |
sel.addSelectStar(this.field.getTable()); |
final Where w = new Where(this.field.getTable().getField("ID_" + rowOrigin.getTable().getName()), "=", rowOrigin.getID()); |
sel.setWhere(w.and(new Where(this.field.getTable().getField("ID_" + foreignLinkRow.getTable().getName()), "=", foreignLinkRow.getID()))); |
return SQLRowListRSH.execute(sel); |
} |
} |
/trunk/Modules/Module Batch Processing/src/org/openconcerto/modules/common/batchprocessing/Module.java |
---|
2,7 → 2,6 |
import java.awt.Dimension; |
import java.awt.event.ActionEvent; |
import java.io.File; |
import java.io.IOException; |
import java.util.List; |
9,18 → 8,12 |
import javax.swing.AbstractAction; |
import javax.swing.JFrame; |
import org.openconcerto.erp.config.Gestion; |
import org.openconcerto.erp.modules.AbstractModule; |
import org.openconcerto.erp.modules.ComponentsContext; |
import org.openconcerto.erp.modules.ModuleFactory; |
import org.openconcerto.erp.modules.ModuleManager; |
import org.openconcerto.erp.modules.ModulePackager; |
import org.openconcerto.erp.modules.RuntimeModuleFactory; |
import org.openconcerto.sql.element.SQLElement; |
import org.openconcerto.sql.model.SQLField; |
import org.openconcerto.sql.model.SQLRequestLog; |
import org.openconcerto.sql.model.SQLRowValues; |
import org.openconcerto.sql.ui.ConnexionPanel; |
import org.openconcerto.sql.view.list.IListe; |
import org.openconcerto.sql.view.list.IListeAction.IListeEvent; |
import org.openconcerto.sql.view.list.RowAction; |
33,7 → 26,7 |
} |
@Override |
protected void setupComponents(ComponentsContext ctxt) { |
protected void setupComponents(final ComponentsContext ctxt) { |
super.setupComponents(ctxt); |
final SQLElement element = ctxt.getElement("ARTICLE"); |
60,7 → 53,7 |
}; |
f.setContentPane(new BatchEditorPanel(rows, filter)); |
f.setContentPane(new BatchEditorPanel(ctxt.getElement("ARTICLE").getDirectory(), rows, filter)); |
f.pack(); |
f.setMinimumSize(new Dimension(400, 300)); |
f.setLocationRelativeTo(IListe.get(e)); |
/trunk/Modules/Module Batch Processing/src/org/openconcerto/modules/common/batchprocessing/NumberProcessor.java |
---|
13,7 → 13,7 |
import javax.swing.JRadioButton; |
import javax.swing.JTextField; |
import org.openconcerto.sql.model.SQLField; |
import org.openconcerto.sql.model.SQLRow; |
import org.openconcerto.sql.model.SQLRowAccessor; |
import org.openconcerto.sql.model.SQLRowValues; |
import org.openconcerto.ui.DefaultGridBagConstraints; |
20,7 → 20,7 |
public class NumberProcessor extends JPanel implements BatchProcessor { |
private final SQLField field; |
private final BatchField batchfield; |
// Editors |
final JTextField tReplace = new JTextField(); |
private JRadioButton bReplace; |
30,8 → 30,8 |
final JTextField tRemove = new JTextField(); |
private JRadioButton bRemove; |
public NumberProcessor(SQLField field) { |
this.field = field; |
public NumberProcessor(BatchField field) { |
this.batchfield = field; |
this.setLayout(new GridBagLayout()); |
bReplace = new JRadioButton("remplacer par"); |
110,11 → 110,23 |
if (bReplace.isSelected()) { |
BigDecimal v = new BigDecimal(this.tReplace.getText().trim()); |
for (SQLRowAccessor sqlRowAccessor : r) { |
if (batchfield.getForeignLinkRow() != null) { |
final List<SQLRow> referentRow = batchfield.getReferentRows(sqlRowAccessor); |
for (SQLRow sqlRowT : referentRow) { |
SQLRowValues rowValues = sqlRowT.createEmptyUpdateRow(); |
rowValues.put(batchfield.getField().getName(), decimalToFieldType(v)); |
processBeforeUpdate(sqlRowT, rowValues); |
rowValues.update(); |
} |
} else { |
final SQLRowValues rowValues = sqlRowAccessor.createEmptyUpdateRow(); |
rowValues.put(field.getName(), decimalToFieldType(v)); |
rowValues.put(batchfield.getField().getName(), decimalToFieldType(v)); |
processBeforeUpdate(sqlRowAccessor, rowValues); |
rowValues.update(); |
} |
} |
} else if (bAdd.isSelected()) { |
String t = this.tAdd.getText().trim(); |
127,18 → 139,44 |
BigDecimal v = new BigDecimal(t); |
for (SQLRowAccessor sqlRowAccessor : r) { |
final SQLRowValues rowValues = sqlRowAccessor.createEmptyUpdateRow(); |
final BigDecimal value = sqlRowAccessor.asRow().getBigDecimal(field.getName()); |
if (batchfield.getForeignLinkRow() != null) { |
final List<SQLRow> referentRow = batchfield.getReferentRows(sqlRowAccessor); |
for (SQLRow sqlRowT : referentRow) { |
SQLRowValues rowValues = sqlRowT.createEmptyUpdateRow(); |
BigDecimal value = sqlRowT.asRow().getBigDecimal(batchfield.getField().getName()); |
if (value != null) { |
if (isPercent) { |
rowValues.put(field.getName(), decimalToFieldType(value.multiply(v.divide(new BigDecimal(100)).add(BigDecimal.ONE)))); |
rowValues.put(batchfield.getField().getName(), decimalToFieldType(value.multiply(v.divide(new BigDecimal(100)).add(BigDecimal.ONE)))); |
} else { |
rowValues.put(field.getName(), decimalToFieldType(value.add(v))); |
rowValues.put(batchfield.getField().getName(), decimalToFieldType(value.add(v))); |
} |
processBeforeUpdate(sqlRowT, rowValues); |
rowValues.update(); |
} |
} |
} else { |
final SQLRowValues rowValues; |
final BigDecimal value; |
rowValues = sqlRowAccessor.createEmptyUpdateRow(); |
value = sqlRowAccessor.asRow().getBigDecimal(batchfield.getField().getName()); |
if (value != null) { |
if (isPercent) { |
rowValues.put(batchfield.getField().getName(), decimalToFieldType(value.multiply(v.divide(new BigDecimal(100)).add(BigDecimal.ONE)))); |
} else { |
rowValues.put(batchfield.getField().getName(), decimalToFieldType(value.add(v))); |
} |
processBeforeUpdate(sqlRowAccessor, rowValues); |
rowValues.update(); |
} |
} |
} |
} else if (bRemove.isSelected()) { |
String t = this.tRemove.getText().trim(); |
boolean isPercent = false; |
149,15 → 187,35 |
BigDecimal v = new BigDecimal(t); |
for (SQLRowAccessor sqlRowAccessor : r) { |
final SQLRowValues rowValues = sqlRowAccessor.createEmptyUpdateRow(); |
if (batchfield.getForeignLinkRow() != null) { |
final List<SQLRow> referentRow = batchfield.getReferentRows(sqlRowAccessor); |
if (referentRow != null && !referentRow.isEmpty()) { |
for (SQLRow sqlRowT : referentRow) { |
final BigDecimal value = sqlRowAccessor.asRow().getBigDecimal(field.getName()); |
SQLRowValues rowValues = sqlRowT.createEmptyUpdateRow(); |
final BigDecimal value = sqlRowT.getBigDecimal(batchfield.getField().getName()); |
if (value != null) { |
if (isPercent) { |
rowValues.put(field.getName(), decimalToFieldType(value.multiply(v.divide(new BigDecimal(-100)).add(BigDecimal.ONE)))); |
rowValues.put(batchfield.getField().getName(), decimalToFieldType(value.multiply(v.divide(new BigDecimal(-100)).add(BigDecimal.ONE)))); |
} else { |
rowValues.put(field.getName(), decimalToFieldType(value.add(v))); |
rowValues.put(batchfield.getField().getName(), decimalToFieldType(value.add(v))); |
} |
processBeforeUpdate(sqlRowT, rowValues); |
rowValues.update(); |
} |
} |
} |
} else { |
SQLRowValues rowValues = sqlRowAccessor.createEmptyUpdateRow(); |
final BigDecimal value = sqlRowAccessor.asRow().getBigDecimal(batchfield.getField().getName()); |
if (value != null) { |
if (isPercent) { |
rowValues.put(batchfield.getField().getName(), decimalToFieldType(value.multiply(v.divide(new BigDecimal(-100)).add(BigDecimal.ONE)))); |
} else { |
rowValues.put(batchfield.getField().getName(), decimalToFieldType(value.add(v))); |
} |
processBeforeUpdate(sqlRowAccessor, rowValues); |
rowValues.update(); |
} |
164,9 → 222,10 |
} |
} |
} |
} |
private Object decimalToFieldType(BigDecimal v) { |
final Class<?> javaType = field.getType().getJavaType(); |
final Class<?> javaType = batchfield.getField().getType().getJavaType(); |
if (javaType.equals(BigDecimal.class)) { |
return v; |
} else if (javaType.equals(Float.class)) { |
/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/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/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/Code3Of9Extended.java |
---|
New file |
0,0 → 1,78 |
/* |
* 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 Code 3 of 9 Extended, also known as Code 39e and Code39+. |
* |
* <p> |
* Supports encoding of all characters in the 7-bit ASCII table. A modulo-43 check digit can be |
* added if required. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class Code3Of9Extended extends Symbol { |
private static final String[] E_CODE_39 = { "%U", "$A", "$B", "$C", "$D", "$E", "$F", "$G", "$H", "$I", "$J", "$K", "$L", "$M", "$N", "$O", "$P", "$Q", "$R", "$S", "$T", "$U", "$V", "$W", "$X", |
"$Y", "$Z", "%A", "%B", "%C", "%D", "%E", " ", "/A", "/B", "/C", "/D", "/E", "/F", "/G", "/H", "/I", "/J", "/K", "/L", "-", ".", "/O", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", |
"/Z", "%F", "%G", "%H", "%I", "%J", "%V", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "%K", "%L", |
"%M", "%N", "%O", "%W", "+A", "+B", "+C", "+D", "+E", "+F", "+G", "+H", "+I", "+J", "+K", "+L", "+M", "+N", "+O", "+P", "+Q", "+R", "+S", "+T", "+U", "+V", "+W", "+X", "+Y", "+Z", "%P", |
"%Q", "%R", "%S", "%T" }; |
public enum CheckDigit { |
NONE, MOD43 |
} |
private CheckDigit checkOption = CheckDigit.NONE; |
/** |
* Select addition of optional Modulo-43 check digit or encoding without check digit. |
* |
* @param checkMode check digit option |
*/ |
public void setCheckDigit(final CheckDigit checkMode) { |
this.checkOption = checkMode; |
} |
@Override |
protected void encode() { |
String buffer = ""; |
final int l = this.content.length(); |
int asciicode; |
final Code3Of9 c = new Code3Of9(); |
if (this.checkOption == CheckDigit.MOD43) { |
c.setCheckDigit(Code3Of9.CheckDigit.MOD43); |
} |
if (!this.content.matches("[\u0000-\u007F]+")) { |
throw new OkapiException("Invalid characters in input data"); |
} |
for (int i = 0; i < l; i++) { |
asciicode = this.content.charAt(i); |
buffer += E_CODE_39[asciicode]; |
} |
c.setContent(buffer); |
this.readable = this.content; |
this.pattern = new String[1]; |
this.pattern[0] = c.pattern[0]; |
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/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/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/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 with padding zeroes to make 208 bits */ |
variant = 0; |
for (i = 0; i < 13; i++) { |
this.bitStr[i] = 0; |
} |
for (i = 0; i < 28; i++) { |
this.codeWords[i] = 0; |
} |
local_source = this.binary_string; |
for (i = this.binary_string.length(); i < 208; i++) { |
local_source += "0"; |
} |
for (segment = 0; segment < 13; segment++) { |
strpos = segment * 16; |
this.bitStr[segment] = 0; |
for (i = 0; i < 16; i++) { |
if (local_source.charAt(strpos + i) == '1') { |
this.bitStr[segment] += 0x8000 >> i; |
} |
} |
} |
init928(); |
/* encode codeWords from bitStr */ |
cwCnt = encode928(this.binary_string.length()); |
switch (this.cc_width) { |
case 2: |
switch (cwCnt) { |
case 6: |
variant = 0; |
break; |
case 8: |
variant = 1; |
break; |
case 9: |
variant = 2; |
break; |
case 11: |
variant = 3; |
break; |
case 12: |
variant = 4; |
break; |
case 14: |
variant = 5; |
break; |
case 17: |
variant = 6; |
break; |
} |
break; |
case 3: |
switch (cwCnt) { |
case 8: |
variant = 7; |
break; |
case 10: |
variant = 8; |
break; |
case 12: |
variant = 9; |
break; |
case 14: |
variant = 10; |
break; |
case 17: |
variant = 11; |
break; |
} |
break; |
case 4: |
switch (cwCnt) { |
case 8: |
variant = 12; |
break; |
case 11: |
variant = 13; |
break; |
case 14: |
variant = 14; |
break; |
case 17: |
variant = 15; |
break; |
case 20: |
variant = 16; |
break; |
} |
break; |
} |
rows = CCA_VARIANTS[variant]; |
k = CCA_VARIANTS[17 + variant]; |
offset = CCA_VARIANTS[34 + variant]; |
/* Reed-Solomon error correction */ |
for (i = 0; i < 8; i++) { |
rsCodeWords[i] = 0; |
} |
total = 0; |
info("Composite Codewords: "); |
for (i = 0; i < cwCnt; i++) { |
total = (this.codeWords[i] + rsCodeWords[k - 1]) % 929; |
for (j = k - 1; j >= 0; j--) { |
if (j == 0) { |
rsCodeWords[j] = (929 - total * CCA_COEFFS[offset + j] % 929) % 929; |
} else { |
rsCodeWords[j] = (rsCodeWords[j - 1] + 929 - total * CCA_COEFFS[offset + j] % 929) % 929; |
} |
} |
infoSpace(this.codeWords[i]); |
} |
infoLine(); |
for (j = 0; j < k; j++) { |
if (rsCodeWords[j] != 0) { |
rsCodeWords[j] = 929 - rsCodeWords[j]; |
} |
} |
for (i = k - 1; i >= 0; i--) { |
this.codeWords[cwCnt] = rsCodeWords[i]; |
cwCnt++; |
} |
/* Place data into table */ |
LeftRAPStart = A_RAP_TABLE[variant]; |
CentreRAPStart = A_RAP_TABLE[variant + 17]; |
RightRAPStart = A_RAP_TABLE[variant + 34]; |
StartCluster = A_RAP_TABLE[variant + 51] / 3; |
LeftRAP = LeftRAPStart; |
CentreRAP = CentreRAPStart; |
RightRAP = RightRAPStart; |
Cluster = StartCluster; /* |
* Cluster can be 0, 1 or 2 for Cluster(0), Cluster(3) and |
* Cluster(6) |
*/ |
this.readable = ""; |
this.row_count = rows; |
this.pattern = new String[this.row_count]; |
this.row_height = new int[this.row_count]; |
for (i = 0; i < rows; i++) { |
codebarre = ""; |
offset = 929 * Cluster; |
for (j = 0; j < 5; j++) { |
dummy[j] = 0; |
} |
for (j = 0; j < this.cc_width; j++) { |
dummy[j + 1] = this.codeWords[i * this.cc_width + j]; |
} |
/* Copy the data into codebarre */ |
codebarre += RAPLR[LeftRAP]; |
codebarre += "1"; |
codebarre += CODAGEMC[offset + dummy[1]]; |
codebarre += "1"; |
if (this.cc_width == 3) { |
codebarre += RAPC[CentreRAP]; |
} |
if (this.cc_width >= 2) { |
codebarre += "1"; |
codebarre += CODAGEMC[offset + dummy[2]]; |
codebarre += "1"; |
} |
if (this.cc_width == 4) { |
codebarre += RAPC[CentreRAP]; |
} |
if (this.cc_width >= 3) { |
codebarre += "1"; |
codebarre += CODAGEMC[offset + dummy[3]]; |
codebarre += "1"; |
} |
if (this.cc_width == 4) { |
codebarre += "1"; |
codebarre += CODAGEMC[offset + dummy[4]]; |
codebarre += "1"; |
} |
codebarre += RAPLR[RightRAP]; |
codebarre += "1"; /* stop */ |
/* Now codebarre is a mixture of letters and numbers */ |
flip = 1; |
bin.setLength(0); |
for (loop = 0; loop < codebarre.length(); loop++) { |
if (codebarre.charAt(loop) >= '0' && codebarre.charAt(loop) <= '9') { |
for (k = 0; k < codebarre.charAt(loop) - '0'; k++) { |
if (flip == 0) { |
bin.append('0'); |
} else { |
bin.append('1'); |
} |
} |
if (flip == 0) { |
flip = 1; |
} else { |
flip = 0; |
} |
} else { |
bin.append(PDF_TTF[positionOf(codebarre.charAt(loop), BR_SET)]); |
} |
} |
this.row_height[i] = 2; |
this.pattern[i] = bin2pat(bin); |
/* Set up RAPs and Cluster for next row */ |
LeftRAP++; |
CentreRAP++; |
RightRAP++; |
Cluster++; |
if (LeftRAP == 53) { |
LeftRAP = 1; |
} |
if (CentreRAP == 53) { |
CentreRAP = 1; |
} |
if (RightRAP == 53) { |
RightRAP = 1; |
} |
if (Cluster == 3) { |
Cluster = 0; |
} |
} |
} |
/* initialize pwr928 encoding table */ |
private void init928() { |
int i, j, v; |
final int[] cw = new int[7]; |
cw[6] = 1; |
for (i = 5; i >= 0; i--) { |
cw[i] = 0; |
} |
for (i = 0; i < 7; i++) { |
this.pwr928[0][i] = cw[i]; |
} |
for (j = 1; j < 69; j++) { |
for (v = 0, i = 6; i >= 1; i--) { |
v = 2 * cw[i] + v / 928; |
this.pwr928[j][i] = cw[i] = v % 928; |
} |
this.pwr928[j][0] = cw[0] = 2 * cw[0] + v / 928; |
} |
} |
/* converts bit string to base 928 values, codeWords[0] is highest order */ |
private int encode928(final int bitLng) { |
int i, j, b, bitCnt, cwNdx, cwCnt, cwLng; |
for (cwNdx = cwLng = b = 0; b < bitLng; b += 69, cwNdx += 7) { |
bitCnt = min(bitLng - b, 69); |
cwLng += cwCnt = bitCnt / 10 + 1; |
for (i = 0; i < cwCnt; i++) { |
this.codeWords[cwNdx + i] = 0; /* init 0 */ |
} |
for (i = 0; i < bitCnt; i++) { |
if (getBit(b + bitCnt - i - 1)) { |
for (j = 0; j < cwCnt; j++) { |
this.codeWords[cwNdx + j] += this.pwr928[i][j + 7 - cwCnt]; |
} |
} |
} |
for (i = cwCnt - 1; i > 0; i--) { |
/* add "carries" */ |
this.codeWords[cwNdx + i - 1] += this.codeWords[cwNdx + i] / 928; |
this.codeWords[cwNdx + i] %= 928; |
} |
} |
return cwLng; |
} |
private int min(final int first, final int second) { |
if (first <= second) { |
return first; |
} else { |
return second; |
} |
} |
/* gets bit in bitString at bitPos */ |
private boolean getBit(final int arg) { |
if ((this.bitStr[arg >> 4] & 0x8000 >> (arg & 15)) != 0) { |
return true; |
} else { |
return false; |
} |
} |
private void cc_b() { |
/* CC-B 2D component */ |
int length, i, binloc; |
int k, j, longueur, offset; |
final int[] mccorrection = new int[50]; |
int total; |
final int[] dummy = new int[5]; |
String codebarre; |
final StringBuilder bin = new StringBuilder(); |
int variant, LeftRAPStart, CentreRAPStart, RightRAPStart, StartCluster; |
int LeftRAP, CentreRAP, RightRAP, Cluster, flip, loop; |
int option_2, rows; |
this.inputData = new int[this.binary_string.length() / 8 + 3]; |
length = this.binary_string.length() / 8; |
for (i = 0; i < length; i++) { |
binloc = i * 8; |
this.inputData[i] = 0; |
for (j = 0; j < 8; j++) { |
if (this.binary_string.charAt(binloc + j) == '1') { |
this.inputData[i] += 0x80 >> j; |
} |
} |
} |
this.codeWordCount = 0; |
/* |
* "the CC-B component shall have codeword 920 in the first symbol character position" |
* (section 9a) |
*/ |
this.codeWords[this.codeWordCount] = 920; |
this.codeWordCount++; |
byteprocess(0, length); |
/* Now figure out which variant of the symbol to use and load values accordingly */ |
variant = 0; |
if (this.cc_width == 2) { |
variant = 13; |
if (this.codeWordCount <= 33) { |
variant = 12; |
} |
if (this.codeWordCount <= 29) { |
variant = 11; |
} |
if (this.codeWordCount <= 24) { |
variant = 10; |
} |
if (this.codeWordCount <= 19) { |
variant = 9; |
} |
if (this.codeWordCount <= 13) { |
variant = 8; |
} |
if (this.codeWordCount <= 8) { |
variant = 7; |
} |
} |
if (this.cc_width == 3) { |
variant = 23; |
if (this.codeWordCount <= 70) { |
variant = 22; |
} |
if (this.codeWordCount <= 58) { |
variant = 21; |
} |
if (this.codeWordCount <= 46) { |
variant = 20; |
} |
if (this.codeWordCount <= 34) { |
variant = 19; |
} |
if (this.codeWordCount <= 24) { |
variant = 18; |
} |
if (this.codeWordCount <= 18) { |
variant = 17; |
} |
if (this.codeWordCount <= 14) { |
variant = 16; |
} |
if (this.codeWordCount <= 10) { |
variant = 15; |
} |
if (this.codeWordCount <= 6) { |
variant = 14; |
} |
} |
if (this.cc_width == 4) { |
variant = 34; |
if (this.codeWordCount <= 108) { |
variant = 33; |
} |
if (this.codeWordCount <= 90) { |
variant = 32; |
} |
if (this.codeWordCount <= 72) { |
variant = 31; |
} |
if (this.codeWordCount <= 54) { |
variant = 30; |
} |
if (this.codeWordCount <= 39) { |
variant = 29; |
} |
if (this.codeWordCount <= 30) { |
variant = 28; |
} |
if (this.codeWordCount <= 24) { |
variant = 27; |
} |
if (this.codeWordCount <= 18) { |
variant = 26; |
} |
if (this.codeWordCount <= 12) { |
variant = 25; |
} |
if (this.codeWordCount <= 8) { |
variant = 24; |
} |
} |
/* |
* Now we have the variant we can load the data - from here on the same as MicroPDF417 code |
*/ |
variant--; |
option_2 = MICRO_VARIANTS[variant]; /* columns */ |
rows = MICRO_VARIANTS[variant + 34]; /* rows */ |
k = MICRO_VARIANTS[variant + 68]; /* number of EC CWs */ |
longueur = option_2 * rows - k; /* number of non-EC CWs */ |
i = longueur - this.codeWordCount; /* amount of padding required */ |
offset = MICRO_VARIANTS[variant + 102]; /* coefficient offset */ |
/* We add the padding */ |
while (i > 0) { |
this.codeWords[this.codeWordCount] = 900; |
this.codeWordCount++; |
i--; |
} |
/* Reed-Solomon error correction */ |
longueur = this.codeWordCount; |
for (loop = 0; loop < 50; loop++) { |
mccorrection[loop] = 0; |
} |
total = 0; |
info("Composite Codewords: "); |
for (i = 0; i < longueur; i++) { |
total = (this.codeWords[i] + mccorrection[k - 1]) % 929; |
for (j = k - 1; j >= 0; j--) { |
if (j == 0) { |
mccorrection[j] = (929 - total * MICROCOEFFS[offset + j] % 929) % 929; |
} else { |
mccorrection[j] = (mccorrection[j - 1] + 929 - total * MICROCOEFFS[offset + j] % 929) % 929; |
} |
} |
infoSpace(this.codeWords[i]); |
} |
infoLine(); |
for (j = 0; j < k; j++) { |
if (mccorrection[j] != 0) { |
mccorrection[j] = 929 - mccorrection[j]; |
} |
} |
/* we add these codes to the string */ |
for (i = k - 1; i >= 0; i--) { |
this.codeWords[this.codeWordCount] = mccorrection[i]; |
this.codeWordCount++; |
} |
/* Now get the RAP (Row Address Pattern) start values */ |
LeftRAPStart = RAP_TABLE[variant]; |
CentreRAPStart = RAP_TABLE[variant + 34]; |
RightRAPStart = RAP_TABLE[variant + 68]; |
StartCluster = RAP_TABLE[variant + 102] / 3; |
/* That's all values loaded, get on with the encoding */ |
LeftRAP = LeftRAPStart; |
CentreRAP = CentreRAPStart; |
RightRAP = RightRAPStart; |
Cluster = StartCluster; /* |
* Cluster can be 0, 1 or 2 for Cluster(0), Cluster(3) and |
* Cluster(6) |
*/ |
this.readable = ""; |
this.row_count = rows; |
this.pattern = new String[this.row_count]; |
this.row_height = new int[this.row_count]; |
for (i = 0; i < rows; i++) { |
codebarre = ""; |
offset = 929 * Cluster; |
for (j = 0; j < 5; j++) { |
dummy[j] = 0; |
} |
for (j = 0; j < option_2; j++) { |
dummy[j + 1] = this.codeWords[i * option_2 + j]; |
} |
/* Copy the data into codebarre */ |
codebarre += RAPLR[LeftRAP]; |
codebarre += "1"; |
codebarre += CODAGEMC[offset + dummy[1]]; |
codebarre += "1"; |
if (this.cc_width == 3) { |
codebarre += RAPC[CentreRAP]; |
} |
if (this.cc_width >= 2) { |
codebarre += "1"; |
codebarre += CODAGEMC[offset + dummy[2]]; |
codebarre += "1"; |
} |
if (this.cc_width == 4) { |
codebarre += RAPC[CentreRAP]; |
} |
if (this.cc_width >= 3) { |
codebarre += "1"; |
codebarre += CODAGEMC[offset + dummy[3]]; |
codebarre += "1"; |
} |
if (this.cc_width == 4) { |
codebarre += "1"; |
codebarre += CODAGEMC[offset + dummy[4]]; |
codebarre += "1"; |
} |
codebarre += RAPLR[RightRAP]; |
codebarre += "1"; /* stop */ |
/* Now codebarre is a mixture of letters and numbers */ |
flip = 1; |
bin.setLength(0); |
for (loop = 0; loop < codebarre.length(); loop++) { |
if (codebarre.charAt(loop) >= '0' && codebarre.charAt(loop) <= '9') { |
for (k = 0; k < codebarre.charAt(loop) - '0'; k++) { |
if (flip == 0) { |
bin.append('0'); |
} else { |
bin.append('1'); |
} |
} |
if (flip == 0) { |
flip = 1; |
} else { |
flip = 0; |
} |
} else { |
bin.append(PDF_TTF[positionOf(codebarre.charAt(loop), BR_SET)]); |
} |
} |
this.pattern[i] = bin2pat(bin); |
this.row_height[i] = 2; |
/* Set up RAPs and Cluster for next row */ |
LeftRAP++; |
CentreRAP++; |
RightRAP++; |
Cluster++; |
if (LeftRAP == 53) { |
LeftRAP = 1; |
} |
if (CentreRAP == 53) { |
CentreRAP = 1; |
} |
if (RightRAP == 53) { |
RightRAP = 1; |
} |
if (Cluster == 3) { |
Cluster = 0; |
} |
} |
} |
private void cc_c() { |
/* CC-C 2D component - byte compressed PDF417 */ |
int length, i, binloc, k; |
int offset, longueur, loop, total, j; |
final int[] mccorrection = new int[520]; |
int c1, c2, c3; |
final int[] dummy = new int[35]; |
String codebarre; |
final StringBuilder bin = new StringBuilder(); |
this.inputData = new int[this.binary_string.length() / 8 + 4]; |
length = this.binary_string.length() / 8; |
for (i = 0; i < length; i++) { |
binloc = i * 8; |
this.inputData[i] = 0; |
for (j = 0; j < 8; j++) { |
if (this.binary_string.charAt(binloc + j) == '1') { |
this.inputData[i] += 0x80 >> j; |
} |
} |
} |
this.codeWordCount = 0; |
this.codeWords[this.codeWordCount] = 0; /* space for length descriptor */ |
this.codeWordCount++; |
this.codeWords[this.codeWordCount] = 920; /* CC-C identifier */ |
this.codeWordCount++; |
byteprocess(0, length); |
this.codeWords[0] = this.codeWordCount; |
k = 1; |
for (i = 1; i <= this.ecc + 1; i++) { |
k *= 2; |
} |
/* 796 - we now take care of the Reed Solomon codes */ |
switch (this.ecc) { |
case 1: |
offset = 2; |
break; |
case 2: |
offset = 6; |
break; |
case 3: |
offset = 14; |
break; |
case 4: |
offset = 30; |
break; |
case 5: |
offset = 62; |
break; |
case 6: |
offset = 126; |
break; |
case 7: |
offset = 254; |
break; |
case 8: |
offset = 510; |
break; |
default: |
offset = 0; |
break; |
} |
longueur = this.codeWordCount; |
for (loop = 0; loop < 520; loop++) { |
mccorrection[loop] = 0; |
} |
total = 0; |
info("Composite Codewords: "); |
for (i = 0; i < longueur; i++) { |
total = (this.codeWords[i] + mccorrection[k - 1]) % 929; |
for (j = k - 1; j >= 0; j--) { |
if (j == 0) { |
mccorrection[j] = (929 - total * COEFRS[offset + j] % 929) % 929; |
} else { |
mccorrection[j] = (mccorrection[j - 1] + 929 - total * COEFRS[offset + j] % 929) % 929; |
} |
} |
infoSpace(this.codeWords[i]); |
} |
infoLine(); |
for (j = 0; j < k; j++) { |
if (mccorrection[j] != 0) { |
mccorrection[j] = 929 - mccorrection[j]; |
} |
} |
/* we add these codes to the string */ |
for (i = k - 1; i >= 0; i--) { |
this.codeWords[this.codeWordCount] = mccorrection[i]; |
this.codeWordCount++; |
} |
/* 818 - The CW string is finished */ |
c1 = (this.codeWordCount / this.cc_width - 1) / 3; |
c2 = this.ecc * 3 + (this.codeWordCount / this.cc_width - 1) % 3; |
c3 = this.cc_width - 1; |
this.readable = ""; |
this.row_count = this.codeWordCount / this.cc_width; |
this.pattern = new String[this.row_count]; |
this.row_height = new int[this.row_count]; |
/* we now encode each row */ |
for (i = 0; i <= this.codeWordCount / this.cc_width - 1; i++) { |
for (j = 0; j < this.cc_width; j++) { |
dummy[j + 1] = this.codeWords[i * this.cc_width + j]; |
} |
k = i / 3 * 30; |
switch (i % 3) { |
/* |
* follows this pattern from US Patent 5,243,655: Row 0: L0 (row #, # of rows) R0 (row |
* #, # of columns) Row 1: L1 (row #, security level) R1 (row #, # of rows) Row 2: L2 |
* (row #, # of columns) R2 (row #, security level) Row 3: L3 (row #, # of rows) R3 (row |
* #, # of columns) etc. |
*/ |
case 0: |
dummy[0] = k + c1; |
dummy[this.cc_width + 1] = k + c3; |
break; |
case 1: |
dummy[0] = k + c2; |
dummy[this.cc_width + 1] = k + c1; |
break; |
case 2: |
dummy[0] = k + c3; |
dummy[this.cc_width + 1] = k + c2; |
break; |
} |
codebarre = "+*"; /* Start with a start char and a separator */ |
for (j = 0; j <= this.cc_width + 1; j++) { |
switch (i % 3) { |
case 1: |
offset = 929; |
/* cluster(3) */ break; |
case 2: |
offset = 1858; |
/* cluster(6) */ break; |
default: |
offset = 0; |
/* cluster(0) */ break; |
} |
codebarre += CODAGEMC[offset + dummy[j]]; |
codebarre += "*"; |
} |
codebarre += "-"; |
bin.setLength(0); |
for (loop = 0; loop < codebarre.length(); loop++) { |
bin.append(PDF_TTF[positionOf(codebarre.charAt(loop), BR_SET)]); |
} |
this.pattern[i] = bin2pat(bin); |
this.row_height[i] = 3; |
} |
} |
private void byteprocess(int start, final int length) { |
int len = 0; |
int chunkLen = 0; |
BigInteger mantisa; |
BigInteger total; |
BigInteger word; |
/* select the switch for multiple of 6 bytes */ |
if (this.binary_string.length() % 6 == 0) { |
this.codeWords[this.codeWordCount++] = 924; |
} else { |
this.codeWords[this.codeWordCount++] = 901; |
} |
while (len < length) { |
chunkLen = length - len; |
if (6 <= chunkLen) /* Take groups of 6 */ { |
chunkLen = 6; |
len += chunkLen; |
total = BigInteger.valueOf(0); |
while (chunkLen-- != 0) { |
mantisa = BigInteger.valueOf(this.inputData[start++]); |
total = total.or(mantisa.shiftLeft(chunkLen * 8)); |
} |
chunkLen = 5; |
while (chunkLen-- != 0) { |
word = total.mod(BigInteger.valueOf(900)); |
this.codeWords[this.codeWordCount + chunkLen] = word.intValue(); |
total = total.divide(BigInteger.valueOf(900)); |
} |
this.codeWordCount += 5; |
} else /* If it remain a group of less than 6 bytes */ { |
len += chunkLen; |
while (chunkLen-- != 0) { |
this.codeWords[this.codeWordCount++] = this.inputData[start++]; |
} |
} |
} |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Code128.java |
---|
New file |
0,0 → 1,865 |
/* |
* 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; |
import static java.nio.charset.StandardCharsets.ISO_8859_1; |
/** |
* <p> |
* Implements Code 128 bar code symbology according to ISO/IEC 15417:2007. |
* |
* <p> |
* Code 128 supports encoding of 8-bit ISO 8859-1 (Latin-1) characters. |
* |
* <p> |
* Setting GS1 mode allows encoding in GS1-128 (also known as UCC/EAN-128). |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
* @author Daniel Gredler |
*/ |
public class Code128 extends Symbol { |
private enum Mode { |
NULL, SHIFTA, LATCHA, SHIFTB, LATCHB, SHIFTC, LATCHC, AORB, ABORC |
} |
private enum FMode { |
SHIFTN, LATCHN, SHIFTF, LATCHF |
} |
private enum Composite { |
OFF, CCA, CCB, CCC |
} |
protected static final String[] CODE128_TABLE = { "212222", "222122", "222221", "121223", "121322", "131222", "122213", "122312", "132212", "221213", "221312", "231212", "112232", "122132", |
"122231", "113222", "123122", "123221", "223211", "221132", "221231", "213212", "223112", "312131", "311222", "321122", "321221", "312212", "322112", "322211", "212123", "212321", |
"232121", "111323", "131123", "131321", "112313", "132113", "132311", "211313", "231113", "231311", "112133", "112331", "132131", "113123", "113321", "133121", "313121", "211331", |
"231131", "213113", "213311", "213131", "311123", "311321", "331121", "312113", "312311", "332111", "314111", "221411", "431111", "111224", "111422", "121124", "121421", "141122", |
"141221", "112214", "112412", "122114", "122411", "142112", "142211", "241211", "221114", "413111", "241112", "134111", "111242", "121142", "121241", "114212", "124112", "124211", |
"411212", "421112", "421211", "212141", "214121", "412121", "111143", "111341", "131141", "114113", "114311", "411113", "411311", "113141", "114131", "311141", "411131", "211412", |
"211214", "211232", "2331112" }; |
private boolean suppressModeC = false; |
private Composite compositeMode = Composite.OFF; |
/** |
* Optionally prevents this symbol from using subset mode C for numeric data compression. |
* |
* @param suppressModeC whether or not to prevent this symbol from using subset mode C |
*/ |
public void setSuppressModeC(final boolean suppressModeC) { |
this.suppressModeC = suppressModeC; |
} |
/** |
* Returns whether or not this symbol is prevented from using subset mode C for numeric data |
* compression. |
* |
* @return whether or not this symbol is prevented from using subset mode C for numeric data |
* compression |
*/ |
public boolean getSuppressModeC() { |
return this.suppressModeC; |
} |
protected void setCca() { |
this.compositeMode = Composite.CCA; |
} |
protected void setCcb() { |
this.compositeMode = Composite.CCB; |
} |
protected void setCcc() { |
this.compositeMode = Composite.CCC; |
} |
public void unsetCc() { |
this.compositeMode = Composite.OFF; |
} |
@Override |
protected boolean gs1Supported() { |
return true; |
} |
@Override |
protected void encode() { |
int i, j, k; |
final int input_point = 0; |
Mode mode, last_mode; |
Mode last_set, current_set; |
double glyph_count; |
int bar_characters = 0, total_sum = 0; |
FMode f_state = FMode.LATCHN; |
final Mode[] mode_type = new Mode[200]; |
final int[] mode_length = new int[200]; |
final int[] values = new int[200]; |
int c; |
int linkage_flag = 0; |
int index_point = 0; |
int read = 0; |
this.inputData = toBytes(this.content, ISO_8859_1); |
if (this.inputData == null) { |
throw new OkapiException("Invalid characters in input data"); |
} |
final int sourcelen = this.inputData.length; |
final FMode[] fset = new FMode[200]; |
final Mode[] set = new Mode[200]; /* set[] = Calculated mode for each character */ |
if (sourcelen > 170) { |
throw new OkapiException("Input data too long"); |
} |
/* Detect extended ASCII characters */ |
for (i = 0; i < sourcelen; i++) { |
final int ch = this.inputData[i]; |
if (ch >= 128 && ch != FNC1 && ch != FNC2 && ch != FNC3 && ch != FNC4) { |
fset[i] = FMode.SHIFTF; |
} else { |
fset[i] = FMode.LATCHN; |
} |
} |
/* Decide when to latch to extended mode - Annex E note 3 */ |
j = 0; |
for (i = 0; i < sourcelen; i++) { |
if (fset[i] == FMode.SHIFTF) { |
j++; |
} else { |
j = 0; |
} |
if (j >= 5) { |
for (k = i; k > i - 5; k--) { |
fset[k] = FMode.LATCHF; |
} |
} |
if (j >= 3 && i == sourcelen - 1) { |
for (k = i; k > i - 3; k--) { |
fset[k] = FMode.LATCHF; |
} |
} |
} |
/* |
* Decide if it is worth reverting to 646 encodation for a few characters as described in |
* 4.3.4.2 (d) |
*/ |
for (i = 1; i < sourcelen; i++) { |
if (fset[i - 1] == FMode.LATCHF && fset[i] == FMode.LATCHN) { |
/* Detected a change from 8859-1 to 646 - count how long for */ |
for (j = 0; fset[i + j] == FMode.LATCHN && i + j < sourcelen; j++) { |
; |
} |
if (j < 5 || j < 3 && i + j == sourcelen - 1) { |
/* Uses the same figures recommended by Annex E note 3 */ |
/* Change to shifting back rather than latching back */ |
for (k = 0; k < j; k++) { |
fset[i + k] = FMode.SHIFTN; |
} |
} |
} |
} |
/* Decide on mode using same system as PDF417 and rules of ISO 15417 Annex E */ |
int letter = this.inputData[input_point]; |
int numbers = letter >= '0' && letter <= '9' ? 1 : 0; |
mode = findSubset(letter, numbers); |
mode_type[0] = mode; |
mode_length[0] += length(letter, mode); |
for (i = 1; i < sourcelen; i++) { |
letter = this.inputData[i]; |
last_mode = mode; |
mode = findSubset(letter, numbers); |
if (mode == last_mode) { |
mode_length[index_point] += length(letter, mode); |
} else { |
index_point++; |
mode_type[index_point] = mode; |
mode_length[index_point] = length(letter, mode); |
} |
if (letter >= '0' && letter <= '9') { |
numbers++; |
} else { |
numbers = 0; |
} |
} |
index_point++; |
index_point = reduceSubsetChanges(mode_type, mode_length, index_point); |
/* Put set data into set[] */ |
read = 0; |
for (i = 0; i < index_point; i++) { |
for (j = 0; j < mode_length[i]; j++) { |
set[read] = mode_type[i]; |
read++; |
} |
} |
/* Resolve odd length LATCHC blocks */ |
int cs = 0, nums = 0, fncs = 0; |
for (i = 0; i < read; i++) { |
if (set[i] == Mode.LATCHC) { |
cs++; |
if (this.inputData[i] >= '0' && this.inputData[i] <= '9') { |
nums++; |
} else if (this.inputData[i] == FNC1) { |
fncs++; |
} |
} else { |
resolveOddCs(set, i, cs, nums, fncs); |
cs = 0; |
nums = 0; |
fncs = 0; |
} |
} |
resolveOddCs(set, i, cs, nums, fncs); |
/* Adjust for strings which start with shift characters - make them latch instead */ |
if (set[0] == Mode.SHIFTA) { |
i = 0; |
do { |
set[i] = Mode.LATCHA; |
i++; |
} while (set[i] == Mode.SHIFTA); |
} |
if (set[0] == Mode.SHIFTB) { |
i = 0; |
do { |
set[i] = Mode.LATCHB; |
i++; |
} while (set[i] == Mode.SHIFTB); |
} |
/* |
* Now we can calculate how long the barcode is going to be - and stop it from being too |
* long |
*/ |
last_set = Mode.NULL; |
glyph_count = 0.0; |
for (i = 0; i < sourcelen; i++) { |
if (set[i] == Mode.SHIFTA || set[i] == Mode.SHIFTB) { |
glyph_count += 1.0; |
} |
if (fset[i] == FMode.SHIFTF || fset[i] == FMode.SHIFTN) { |
glyph_count += 1.0; |
} |
if (set[i] == Mode.LATCHA || set[i] == Mode.LATCHB || set[i] == Mode.LATCHC) { |
if (set[i] != last_set) { |
last_set = set[i]; |
glyph_count += 1.0; |
} |
} |
if (i == 0) { |
if (fset[i] == FMode.LATCHF) { |
glyph_count += 2.0; |
} |
} else { |
if (fset[i] == FMode.LATCHF && fset[i - 1] != FMode.LATCHF) { |
glyph_count += 2.0; |
} |
if (fset[i] != FMode.LATCHF && fset[i - 1] == FMode.LATCHF) { |
glyph_count += 2.0; |
} |
} |
if (set[i] == Mode.LATCHC) { |
if (this.inputData[i] == FNC1) { |
glyph_count += 1.0; |
} else { |
glyph_count += 0.5; |
} |
} else { |
glyph_count += 1.0; |
} |
} |
if (glyph_count > 80.0) { |
throw new OkapiException("Input data too long"); |
} |
info("Encoding: "); |
/* So now we know what start character to use - we can get on with it! */ |
if (this.readerInit) { |
/* Reader Initialisation mode */ |
switch (set[0]) { |
case LATCHA: |
values[0] = 103; |
current_set = Mode.LATCHA; |
values[1] = 96; |
bar_characters++; |
info("STARTA FNC3 "); |
break; |
case LATCHB: |
values[0] = 104; |
current_set = Mode.LATCHB; |
values[1] = 96; |
bar_characters++; |
info("STARTB FNC3 "); |
break; |
default: /* Start C */ |
values[0] = 104; |
values[1] = 96; |
values[2] = 99; |
bar_characters += 2; |
current_set = Mode.LATCHC; |
info("STARTB FNC3 CODEC "); |
break; |
} |
} else { |
/* Normal mode */ |
switch (set[0]) { |
case LATCHA: |
values[0] = 103; |
current_set = Mode.LATCHA; |
info("STARTA "); |
break; |
case LATCHB: |
values[0] = 104; |
current_set = Mode.LATCHB; |
info("STARTB "); |
break; |
default: |
values[0] = 105; |
current_set = Mode.LATCHC; |
info("STARTC "); |
break; |
} |
} |
bar_characters++; |
if (this.inputDataType == DataType.GS1) { |
values[1] = 102; |
bar_characters++; |
info("FNC1 "); |
} |
if (fset[0] == FMode.LATCHF) { |
switch (current_set) { |
case LATCHA: |
values[bar_characters] = 101; |
values[bar_characters + 1] = 101; |
info("FNC4 FNC4 "); |
break; |
case LATCHB: |
values[bar_characters] = 100; |
values[bar_characters + 1] = 100; |
info("FNC4 FNC4 "); |
break; |
} |
bar_characters += 2; |
f_state = FMode.LATCHF; |
} |
/* Encode the data */ |
read = 0; |
do { |
if (read != 0 && set[read] != current_set) { /* Latch different code set */ |
switch (set[read]) { |
case LATCHA: |
values[bar_characters] = 101; |
bar_characters++; |
current_set = Mode.LATCHA; |
info("CODEA "); |
break; |
case LATCHB: |
values[bar_characters] = 100; |
bar_characters++; |
current_set = Mode.LATCHB; |
info("CODEB "); |
break; |
case LATCHC: |
values[bar_characters] = 99; |
bar_characters++; |
current_set = Mode.LATCHC; |
info("CODEC "); |
break; |
} |
} |
if (read != 0) { |
if (fset[read] == FMode.LATCHF && f_state == FMode.LATCHN) { |
/* Latch beginning of extended mode */ |
switch (current_set) { |
case LATCHA: |
values[bar_characters] = 101; |
values[bar_characters + 1] = 101; |
info("FNC4 FNC4 "); |
break; |
case LATCHB: |
values[bar_characters] = 100; |
values[bar_characters + 1] = 100; |
info("FNC4 FNC4 "); |
break; |
} |
bar_characters += 2; |
f_state = FMode.LATCHF; |
} |
if (fset[read] == FMode.LATCHN && f_state == FMode.LATCHF) { |
/* Latch end of extended mode */ |
switch (current_set) { |
case LATCHA: |
values[bar_characters] = 101; |
values[bar_characters + 1] = 101; |
info("FNC4 FNC4 "); |
break; |
case LATCHB: |
values[bar_characters] = 100; |
values[bar_characters + 1] = 100; |
info("FNC4 FNC4 "); |
break; |
} |
bar_characters += 2; |
f_state = FMode.LATCHN; |
} |
} |
if (fset[read] == FMode.SHIFTF || fset[read] == FMode.SHIFTN) { |
/* Shift to or from extended mode */ |
switch (current_set) { |
case LATCHA: |
values[bar_characters] = 101; |
info("FNC4 "); |
break; |
case LATCHB: |
values[bar_characters] = 100; |
info("FNC4 "); |
break; |
} |
bar_characters++; |
} |
if (set[read] == Mode.SHIFTA || set[read] == Mode.SHIFTB) { |
/* Insert shift character */ |
values[bar_characters] = 98; |
info("SHFT "); |
bar_characters++; |
} |
/* Encode data characters */ |
c = this.inputData[read]; |
switch (set[read]) { |
case SHIFTA: |
case LATCHA: |
if (c == FNC1) { |
values[bar_characters] = 102; |
info("FNC1 "); |
} else if (c == FNC2) { |
values[bar_characters] = 97; |
info("FNC2 "); |
} else if (c == FNC3) { |
values[bar_characters] = 96; |
info("FNC3 "); |
} else if (c == FNC4) { |
values[bar_characters] = 101; |
info("FNC4 "); |
} else if (c > 127) { |
if (c < 160) { |
values[bar_characters] = c - 128 + 64; |
} else { |
values[bar_characters] = c - 128 - 32; |
} |
infoSpace(values[bar_characters]); |
} else { |
if (c < 32) { |
values[bar_characters] = c + 64; |
} else { |
values[bar_characters] = c - 32; |
} |
infoSpace(values[bar_characters]); |
} |
bar_characters++; |
read++; |
break; |
case SHIFTB: |
case LATCHB: |
if (c == FNC1) { |
values[bar_characters] = 102; |
info("FNC1 "); |
} else if (c == FNC2) { |
values[bar_characters] = 97; |
info("FNC2 "); |
} else if (c == FNC3) { |
values[bar_characters] = 96; |
info("FNC3 "); |
} else if (c == FNC4) { |
values[bar_characters] = 100; |
info("FNC4 "); |
} else if (c > 127) { |
values[bar_characters] = c - 32 - 128; |
infoSpace(values[bar_characters]); |
} else { |
values[bar_characters] = c - 32; |
infoSpace(values[bar_characters]); |
} |
bar_characters++; |
read++; |
break; |
case LATCHC: |
if (c == FNC1) { |
values[bar_characters] = 102; |
info("FNC1 "); |
bar_characters++; |
read++; |
} else { |
final int d = this.inputData[read + 1]; |
final int weight = 10 * (c - '0') + d - '0'; |
values[bar_characters] = weight; |
infoSpace(values[bar_characters]); |
bar_characters++; |
read += 2; |
} |
break; |
} |
} while (read < sourcelen); |
infoLine(); |
/* |
* "...note that the linkage flag is an extra code set character between the last data |
* character and the Symbol Check Character" (GS1 Specification) |
*/ |
/* Linkage flags in GS1-128 are determined by ISO/IEC 24723 section 7.4 */ |
switch (this.compositeMode) { |
case CCA: |
case CCB: |
/* CC-A or CC-B 2D component */ |
switch (set[sourcelen - 1]) { |
case LATCHA: |
linkage_flag = 100; |
break; |
case LATCHB: |
linkage_flag = 99; |
break; |
case LATCHC: |
linkage_flag = 101; |
break; |
} |
infoLine("Linkage Flag: " + linkage_flag); |
break; |
case CCC: |
/* CC-C 2D component */ |
switch (set[sourcelen - 1]) { |
case LATCHA: |
linkage_flag = 99; |
break; |
case LATCHB: |
linkage_flag = 101; |
break; |
case LATCHC: |
linkage_flag = 100; |
break; |
} |
infoLine("Linkage Flag: " + linkage_flag); |
break; |
default: |
break; |
} |
if (linkage_flag != 0) { |
values[bar_characters] = linkage_flag; |
bar_characters++; |
} |
infoLine("Data Codewords: " + bar_characters); |
/* Check digit calculation */ |
for (i = 0; i < bar_characters; i++) { |
total_sum += i == 0 ? values[i] : values[i] * i; |
} |
final int checkDigit = total_sum % 103; |
infoLine("Check Digit: " + checkDigit); |
/* Build pattern string */ |
final StringBuilder dest = new StringBuilder(6 * bar_characters + 6 + 7); |
for (i = 0; i < bar_characters; i++) { |
dest.append(CODE128_TABLE[values[i]]); |
} |
dest.append(CODE128_TABLE[checkDigit]); |
dest.append(CODE128_TABLE[106]); // stop character |
/* Readable text */ |
if (this.inputDataType != DataType.GS1) { |
this.readable = removeFncEscapeSequences(this.content); |
if (this.inputDataType == DataType.HIBC) { |
this.readable = "*" + this.readable + "*"; |
} |
} |
if (this.compositeMode == Composite.OFF) { |
this.pattern = new String[] { dest.toString() }; |
this.row_height = new int[] { -1 }; |
this.row_count = 1; |
} else { |
/* Add the separator pattern for composite symbols */ |
this.pattern = new String[] { "0" + dest, dest.toString() }; |
this.row_height = new int[] { 1, -1 }; |
this.row_count = 2; |
} |
} |
private static String removeFncEscapeSequences(final String s) { |
return s.replace(FNC1_STRING, "").replace(FNC2_STRING, "").replace(FNC3_STRING, "").replace(FNC4_STRING, ""); |
} |
private void resolveOddCs(final Mode[] set, final int i, final int cs, final int nums, final int fncs) { |
if ((nums & 1) != 0) { |
int index; |
Mode m; |
if (i - cs == 0 || fncs > 0) { |
// Rule 2: first block -> swap last digit to A or B |
index = i - 1; |
if (index + 1 < set.length && set[index + 1] != null && set[index + 1] != Mode.LATCHC) { |
// next block is either A or B -- match it |
m = set[index + 1]; |
} else { |
// next block is C, or there is no next block -- just latch to B |
m = Mode.LATCHB; |
} |
} else { |
// Rule 3b: subsequent block -> swap first digit to A or B |
// Note that we make an exception for C blocks which contain one (or more) FNC1 |
// characters, |
// since swapping the first digit would place the FNC1 in an invalid position in the |
// block |
index = i - nums; |
if (index - 1 >= 0 && set[index - 1] != null && set[index - 1] != Mode.LATCHC) { |
// previous block is either A or B -- match it |
m = set[index - 1]; |
} else { |
// previous block is C, or there is no previous block -- just latch to B |
m = Mode.LATCHB; |
} |
} |
set[index] = m; |
} |
} |
private Mode findSubset(final int letter, final int numbers) { |
Mode mode; |
if (letter == FNC1) { |
if (numbers % 2 == 0) { |
/* ISO 15417 Annex E Note 2 */ |
/* |
* FNC1 may use subset C, so long as it doesn't break data into an odd number of |
* digits |
*/ |
mode = Mode.ABORC; |
} else { |
mode = Mode.AORB; |
} |
} else if (letter == FNC2 || letter == FNC3 || letter == FNC4) { |
mode = Mode.AORB; |
} else if (letter <= 31) { |
mode = Mode.SHIFTA; |
} else if (letter >= 48 && letter <= 57) { |
mode = Mode.ABORC; |
} else if (letter <= 95) { |
mode = Mode.AORB; |
} else if (letter <= 127) { |
mode = Mode.SHIFTB; |
} else if (letter <= 159) { |
mode = Mode.SHIFTA; |
} else if (letter <= 223) { |
mode = Mode.AORB; |
} else { |
mode = Mode.SHIFTB; |
} |
if (this.suppressModeC && mode == Mode.ABORC) { |
mode = Mode.AORB; |
} |
return mode; |
} |
private int length(final int letter, final Mode mode) { |
if (letter == FNC1 && mode == Mode.ABORC) { |
/* ISO 15417 Annex E Note 2 */ |
/* Logical length used for making subset switching decisions, not actual length */ |
return 2; |
} else { |
return 1; |
} |
} |
/** Implements rules from ISO 15417 Annex E. Returns the updated index point. */ |
private int reduceSubsetChanges(final Mode[] mode_type, final int[] mode_length, final int index_point) { |
int totalLength = 0; |
int i, length; |
Mode current, last, next; |
for (i = 0; i < index_point; i++) { |
current = mode_type[i]; |
length = mode_length[i]; |
if (i != 0) { |
last = mode_type[i - 1]; |
} else { |
last = Mode.NULL; |
} |
if (i != index_point - 1) { |
next = mode_type[i + 1]; |
} else { |
next = Mode.NULL; |
} |
/* ISO 15417 Annex E Note 2 */ |
/* Calculate difference between logical length and actual length in this block */ |
int extraLength = 0; |
for (int j = 0; j < length - extraLength; j++) { |
if (length(this.inputData[totalLength + j], current) == 2) { |
extraLength++; |
} |
} |
if (i == 0) { /* first block */ |
if (index_point == 1 && length == 2 && current == Mode.ABORC) { /* Rule 1a */ |
mode_type[i] = Mode.LATCHC; |
current = Mode.LATCHC; |
} |
if (current == Mode.ABORC) { |
if (length >= 4) { /* Rule 1b */ |
mode_type[i] = Mode.LATCHC; |
current = Mode.LATCHC; |
} else { |
mode_type[i] = Mode.AORB; |
current = Mode.AORB; |
} |
} |
if (current == Mode.SHIFTA) { /* Rule 1c */ |
mode_type[i] = Mode.LATCHA; |
current = Mode.LATCHA; |
} |
if (current == Mode.AORB && next == Mode.SHIFTA) { /* Rule 1c */ |
mode_type[i] = Mode.LATCHA; |
current = Mode.LATCHA; |
} |
if (current == Mode.AORB) { /* Rule 1d */ |
mode_type[i] = Mode.LATCHB; |
current = Mode.LATCHB; |
} |
} else { |
if (current == Mode.ABORC && length >= 4) { /* Rule 3 */ |
mode_type[i] = Mode.LATCHC; |
current = Mode.LATCHC; |
} |
if (current == Mode.ABORC) { |
mode_type[i] = Mode.AORB; |
current = Mode.AORB; |
} |
if (current == Mode.AORB && last == Mode.LATCHA) { |
mode_type[i] = Mode.LATCHA; |
current = Mode.LATCHA; |
} |
if (current == Mode.AORB && last == Mode.LATCHB) { |
mode_type[i] = Mode.LATCHB; |
current = Mode.LATCHB; |
} |
if (current == Mode.AORB && next == Mode.SHIFTA) { |
mode_type[i] = Mode.LATCHA; |
current = Mode.LATCHA; |
} |
if (current == Mode.AORB && next == Mode.SHIFTB) { |
mode_type[i] = Mode.LATCHB; |
current = Mode.LATCHB; |
} |
if (current == Mode.AORB) { |
mode_type[i] = Mode.LATCHB; |
current = Mode.LATCHB; |
} |
if (current == Mode.SHIFTA && length > 1) { /* Rule 4 */ |
mode_type[i] = Mode.LATCHA; |
current = Mode.LATCHA; |
} |
if (current == Mode.SHIFTB && length > 1) { /* Rule 5 */ |
mode_type[i] = Mode.LATCHB; |
current = Mode.LATCHB; |
} |
if (current == Mode.SHIFTA && last == Mode.LATCHA) { |
mode_type[i] = Mode.LATCHA; |
current = Mode.LATCHA; |
} |
if (current == Mode.SHIFTB && last == Mode.LATCHB) { |
mode_type[i] = Mode.LATCHB; |
current = Mode.LATCHB; |
} |
if (current == Mode.SHIFTA && next == Mode.AORB) { |
mode_type[i] = Mode.LATCHA; |
current = Mode.LATCHA; |
} |
if (current == Mode.SHIFTB && next == Mode.AORB) { |
mode_type[i] = Mode.LATCHB; |
current = Mode.LATCHB; |
} |
if (current == Mode.SHIFTA && last == Mode.LATCHC) { |
mode_type[i] = Mode.LATCHA; |
current = Mode.LATCHA; |
} |
if (current == Mode.SHIFTB && last == Mode.LATCHC) { |
mode_type[i] = Mode.LATCHB; |
current = Mode.LATCHB; |
} |
} /* Rule 2 is implemented elsewhere, Rule 6 is implied */ |
/* ISO 15417 Annex E Note 2 */ |
/* |
* Convert logical length back to actual length for this block, now that we've decided |
* on a subset |
*/ |
mode_length[i] -= extraLength; |
totalLength += mode_length[i]; |
} |
return combineSubsetBlocks(mode_type, mode_length, index_point); |
} |
/** |
* Modifies the specified mode and length arrays to combine adjacent modes of the same type, |
* returning the updated index point. |
*/ |
private int combineSubsetBlocks(final Mode[] mode_type, final int[] mode_length, int index_point) { |
/* bring together same type blocks */ |
if (index_point > 1) { |
for (int i = 1; i < index_point; i++) { |
if (mode_type[i - 1] == mode_type[i]) { |
/* bring together */ |
mode_length[i - 1] = mode_length[i - 1] + mode_length[i]; |
/* decrease the list */ |
for (int j = i + 1; j < index_point; j++) { |
mode_length[j - 1] = mode_length[j]; |
mode_type[j - 1] = mode_type[j]; |
} |
index_point--; |
i--; |
} |
} |
} |
return index_point; |
} |
/** {@inheritDoc} */ |
@Override |
protected int[] getCodewords() { |
return getPatternAsCodewords(6); |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/UspsPackage.java |
---|
New file |
0,0 → 1,129 |
/* |
* Copyright 2015 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
import java.awt.geom.Rectangle2D; |
/** |
* <p> |
* Implements USPS Intelligent Mail Package Barcode (IMpb), a linear barcode based on GS1-128. |
* Includes additional data checks. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
* @see <a href= |
* "https://ribbs.usps.gov/intelligentmail_package/documents/tech_guides/BarcodePackageIMSpec.pdf">IMpb |
* Specification</a> |
*/ |
public class UspsPackage extends Symbol { |
@Override |
protected void encode() { |
if (!this.content.matches("[0-9\\[\\]]+")) { |
/* Input must be numeric only */ |
throw new OkapiException("Invalid IMpb data"); |
} |
if (this.content.length() % 2 != 0) { |
/* Input must be even length */ |
throw new OkapiException("Invalid IMpb data"); |
} |
final Code128 code128 = new Code128(); |
code128.unsetCc(); |
code128.setDataType(DataType.GS1); |
code128.setContent(this.content); |
final boolean fourTwenty = this.content.length() > 4 && this.content.charAt(1) == '4' && this.content.charAt(2) == '2' && this.content.charAt(3) == '0'; |
String hrt = ""; |
int bracketCount = 0; |
for (int i = 0; i < this.content.length(); i++) { |
if (this.content.charAt(i) == '[') { |
bracketCount++; |
} |
if (!(fourTwenty && bracketCount < 2)) { |
if (this.content.charAt(i) >= '0' && this.content.charAt(i) <= '9') { |
hrt += this.content.charAt(i); |
} |
} |
} |
String spacedHrt = ""; |
for (int i = 0; i < hrt.length(); i++) { |
spacedHrt += hrt.charAt(i); |
if (i % 4 == 3) { |
spacedHrt += " "; |
} |
} |
this.encodeInfo = code128.encodeInfo; |
this.readable = spacedHrt; |
this.pattern = new String[1]; |
this.pattern[0] = code128.pattern[0]; |
this.row_count = 1; |
this.row_height = new int[1]; |
this.row_height[0] = -1; |
} |
@Override |
protected void plotSymbol() { |
int xBlock; |
int x, y, w, h; |
boolean black; |
final int offset = 20; |
final int yoffset = 15; |
final String banner = "USPS TRACKING #"; |
this.rectangles.clear(); |
this.texts.clear(); |
y = yoffset; |
h = 0; |
black = true; |
x = 0; |
for (xBlock = 0; xBlock < this.pattern[0].length(); xBlock++) { |
w = this.pattern[0].charAt(xBlock) - '0'; |
if (black) { |
if (this.row_height[0] == -1) { |
h = this.default_height; |
} else { |
h = this.row_height[0]; |
} |
if (w != 0 && h != 0) { |
final Rectangle2D.Double rect = new Rectangle2D.Double(x + offset, y, w, h); |
this.rectangles.add(rect); |
} |
this.symbol_width = x + w + 2 * offset; |
} |
black = !black; |
x += w; |
} |
this.symbol_height = h + 2 * yoffset; |
// Add boundary bars |
final Rectangle2D.Double topBar = new Rectangle2D.Double(0, 0, this.symbol_width, 2); |
final Rectangle2D.Double bottomBar = new Rectangle2D.Double(0, this.symbol_height - 2, this.symbol_width, 2); |
this.rectangles.add(topBar); |
this.rectangles.add(bottomBar); |
this.texts.add(new TextBox(0, this.symbol_height - 6.0, this.symbol_width, this.readable, this.humanReadableAlignment)); |
this.texts.add(new TextBox(0, 12.0, this.symbol_width, banner, this.humanReadableAlignment)); |
} |
/** {@inheritDoc} */ |
@Override |
protected int[] getCodewords() { |
return getPatternAsCodewords(6); |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/DataBarExpanded.java |
---|
New file |
0,0 → 1,1271 |
/* |
* 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.DataBarLimited.getWidths; |
import static uk.org.okapibarcode.util.Strings.binaryAppend; |
import java.nio.charset.StandardCharsets; |
import java.util.Arrays; |
/** |
* <p> |
* Implements GS1 DataBar Expanded Omnidirectional and GS1 DataBar Expanded Stacked Omnidirectional |
* according to ISO/IEC 24724:2011. |
* |
* <p> |
* DataBar expanded encodes GS1 data in either a linear or stacked format. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class DataBarExpanded extends Symbol { |
private static final int[] G_SUM_EXP = { 0, 348, 1388, 2948, 3988 }; |
private static final int[] T_EVEN_EXP = { 4, 20, 52, 104, 204 }; |
private static final int[] MODULES_ODD_EXP = { 12, 10, 8, 6, 4 }; |
private static final int[] MODULES_EVEN_EXP = { 5, 7, 9, 11, 13 }; |
private static final int[] WIDEST_ODD_EXP = { 7, 5, 4, 3, 1 }; |
private static final int[] WIDEST_EVEN_EXP = { 2, 4, 5, 6, 8 }; |
/** Table 14 */ |
private static final int[] CHECKSUM_WEIGHT_EXP = { 1, 3, 9, 27, 81, 32, 96, 77, 20, 60, 180, 118, 143, 7, 21, 63, 189, 145, 13, 39, 117, 140, 209, 205, 193, 157, 49, 147, 19, 57, 171, 91, 62, 186, |
136, 197, 169, 85, 44, 132, 185, 133, 188, 142, 4, 12, 36, 108, 113, 128, 173, 97, 80, 29, 87, 50, 150, 28, 84, 41, 123, 158, 52, 156, 46, 138, 203, 187, 139, 206, 196, 166, 76, 17, 51, |
153, 37, 111, 122, 155, 43, 129, 176, 106, 107, 110, 119, 146, 16, 48, 144, 10, 30, 90, 59, 177, 109, 116, 137, 200, 178, 112, 125, 164, 70, 210, 208, 202, 184, 130, 179, 115, 134, 191, |
151, 31, 93, 68, 204, 190, 148, 22, 66, 198, 172, 94, 71, 2, 6, 18, 54, 162, 64, 192, 154, 40, 120, 149, 25, 75, 14, 42, 126, 167, 79, 26, 78, 23, 69, 207, 199, 175, 103, 98, 83, 38, 114, |
131, 182, 124, 161, 61, 183, 127, 170, 88, 53, 159, 55, 165, 73, 8, 24, 72, 5, 15, 45, 135, 194, 160, 58, 174, 100, 89 }; |
/** Table 15 */ |
private static final int[] FINDER_PATTERN_EXP = { 1, 8, 4, 1, 1, 1, 1, 4, 8, 1, 3, 6, 4, 1, 1, 1, 1, 4, 6, 3, 3, 4, 6, 1, 1, 1, 1, 6, 4, 3, 3, 2, 8, 1, 1, 1, 1, 8, 2, 3, 2, 6, 5, 1, 1, 1, 1, 5, 6, |
2, 2, 2, 9, 1, 1, 1, 1, 9, 2, 2 }; |
/** Table 16 */ |
private static final int[] FINDER_SEQUENCE = { 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 3, 0, 0, 0, 0, 0, 0, 0, 0, 1, 6, 3, 8, 0, 0, 0, 0, 0, 0, 0, 1, 10, 3, 8, 5, 0, 0, 0, 0, 0, 0, 1, 10, 3, 8, 7, |
12, 0, 0, 0, 0, 0, 1, 10, 3, 8, 9, 12, 11, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 10, 9, 0, 0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 0, 1, 2, 3, 4, 5, 8, 7, 10, 9, |
12, 11 }; |
private static final int[] WEIGHT_ROWS = { 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 6, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 10, 3, 4, 13, 14, 0, |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 3, 4, 13, 14, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 3, 4, 13, 14, 11, 12, 21, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, |
3, 4, 13, 14, 15, 16, 21, 22, 19, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 17, 18, 15, 16, |
0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 17, 18, 19, 20, 21, 22, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 13, 14, 11, 12, 17, 18, 15, 16, 21, 22, 19, 20 }; |
private enum EncodeMode { |
NUMERIC, ALPHA, ISOIEC, INVALID_CHAR, ANY_ENC, ALPHA_OR_ISO |
} |
private boolean linkageFlag; |
private int preferredColumns = 2; |
private boolean stacked = true; |
public DataBarExpanded() { |
this.inputDataType = DataType.GS1; |
} |
@Override |
public void setDataType(final DataType dataType) { |
if (dataType != Symbol.DataType.GS1) { |
throw new IllegalArgumentException("Only GS1 data type is supported for DataBar Expanded symbology."); |
} |
} |
@Override |
protected boolean gs1Supported() { |
return true; |
} |
/** |
* Sets the preferred width of a stacked symbol by selecting the number of "columns" or symbol |
* segments in each row of data. |
* |
* @param columns the number of segments in each row |
*/ |
public void setPreferredColumns(final int columns) { |
if (columns < 1 || columns > 10) { |
throw new IllegalArgumentException("Invalid column count: " + columns); |
} |
this.preferredColumns = columns; |
} |
/** |
* Returns the preferred width of a stacked symbol by selecting the number of "columns" or |
* symbol segments in each row of data. |
* |
* @return the number of segments in each row |
*/ |
public int getPreferredColumns() { |
return this.preferredColumns; |
} |
/** |
* Sets whether or not this symbology is stacked. |
* |
* @param stacked <tt>true</tt> for GS1 DataBar Expanded Stacked Omnidirectional, <tt>false</tt> |
* for GS1 DataBar Expanded Omnidirectional |
*/ |
public void setStacked(final boolean stacked) { |
this.stacked = stacked; |
} |
/** |
* Returns whether or not this symbology is stacked. |
* |
* @return <tt>true</tt> for GS1 DataBar Expanded Stacked Omnidirectional, <tt>false</tt> for |
* GS1 DataBar Expanded Omnidirectional |
*/ |
public boolean isStacked() { |
return this.stacked; |
} |
protected void setLinkageFlag(final boolean linkageFlag) { |
this.linkageFlag = linkageFlag; |
} |
@Override |
protected void encode() { |
int i; |
int j; |
int k; |
int data_chars; |
final int[] vs = new int[21]; |
final int[] group = new int[21]; |
final int[] v_odd = new int[21]; |
final int[] v_even = new int[21]; |
final int[][] char_widths = new int[21][8]; |
int checksum; |
int row; |
int check_char; |
int c_group; |
int c_odd; |
int c_even; |
final int[] check_widths = new int[8]; |
int pattern_width; |
final int[] elements = new int[235]; |
int codeblocks; |
int stack_rows; |
int blocksPerRow; |
int current_block; |
int current_row; |
boolean special_case_row; |
int elements_in_sub; |
int reader; |
int writer; |
final int[] sub_elements = new int[235]; |
int l; |
int symbol_row; |
String separator_pattern; |
boolean black; |
boolean left_to_right; |
int compositeOffset; |
this.inputData = toBytes(this.content, StandardCharsets.US_ASCII); |
final StringBuilder binaryString = new StringBuilder(this.inputData.length * 8); |
if (this.linkageFlag) { |
binaryString.append('1'); |
compositeOffset = 1; |
} else { |
binaryString.append('0'); |
compositeOffset = 0; |
} |
final int encodingMethod = calculateBinaryString(this.inputData, binaryString); // updates |
// binaryString |
infoLine("Encoding Method: " + encodingMethod); |
logBinaryStringInfo(binaryString); |
data_chars = binaryString.length() / 12; |
info("Data Characters: "); |
for (i = 0; i < data_chars; i++) { |
vs[i] = 0; |
for (j = 0; j < 12; j++) { |
if (binaryString.charAt(i * 12 + j) == '1') { |
vs[i] += 2048 >> j; |
} |
} |
infoSpace(vs[i]); |
} |
infoLine(); |
for (i = 0; i < data_chars; i++) { |
if (vs[i] <= 347) { |
group[i] = 1; |
} |
if (vs[i] >= 348 && vs[i] <= 1387) { |
group[i] = 2; |
} |
if (vs[i] >= 1388 && vs[i] <= 2947) { |
group[i] = 3; |
} |
if (vs[i] >= 2948 && vs[i] <= 3987) { |
group[i] = 4; |
} |
if (vs[i] >= 3988) { |
group[i] = 5; |
} |
v_odd[i] = (vs[i] - G_SUM_EXP[group[i] - 1]) / T_EVEN_EXP[group[i] - 1]; |
v_even[i] = (vs[i] - G_SUM_EXP[group[i] - 1]) % T_EVEN_EXP[group[i] - 1]; |
int[] widths = getWidths(v_odd[i], MODULES_ODD_EXP[group[i] - 1], 4, WIDEST_ODD_EXP[group[i] - 1], 0); |
char_widths[i][0] = widths[0]; |
char_widths[i][2] = widths[1]; |
char_widths[i][4] = widths[2]; |
char_widths[i][6] = widths[3]; |
widths = getWidths(v_even[i], MODULES_EVEN_EXP[group[i] - 1], 4, WIDEST_EVEN_EXP[group[i] - 1], 1); |
char_widths[i][1] = widths[0]; |
char_widths[i][3] = widths[1]; |
char_widths[i][5] = widths[2]; |
char_widths[i][7] = widths[3]; |
} |
/* 7.2.6 Check character */ |
/* |
* The checksum value is equal to the mod 211 residue of the weighted sum of the widths of |
* the elements in the data characters. |
*/ |
checksum = 0; |
for (i = 0; i < data_chars; i++) { |
row = WEIGHT_ROWS[(data_chars - 2) / 2 * 21 + i]; |
for (j = 0; j < 8; j++) { |
checksum += char_widths[i][j] * CHECKSUM_WEIGHT_EXP[row * 8 + j]; |
} |
} |
check_char = 211 * (data_chars + 1 - 4) + checksum % 211; |
infoLine("Check Character: " + check_char); |
c_group = 1; |
if (check_char >= 348 && check_char <= 1387) { |
c_group = 2; |
} |
if (check_char >= 1388 && check_char <= 2947) { |
c_group = 3; |
} |
if (check_char >= 2948 && check_char <= 3987) { |
c_group = 4; |
} |
if (check_char >= 3988) { |
c_group = 5; |
} |
c_odd = (check_char - G_SUM_EXP[c_group - 1]) / T_EVEN_EXP[c_group - 1]; |
c_even = (check_char - G_SUM_EXP[c_group - 1]) % T_EVEN_EXP[c_group - 1]; |
int[] widths = getWidths(c_odd, MODULES_ODD_EXP[c_group - 1], 4, WIDEST_ODD_EXP[c_group - 1], 0); |
check_widths[0] = widths[0]; |
check_widths[2] = widths[1]; |
check_widths[4] = widths[2]; |
check_widths[6] = widths[3]; |
widths = getWidths(c_even, MODULES_EVEN_EXP[c_group - 1], 4, WIDEST_EVEN_EXP[c_group - 1], 1); |
check_widths[1] = widths[0]; |
check_widths[3] = widths[1]; |
check_widths[5] = widths[2]; |
check_widths[7] = widths[3]; |
/* Initialise element array */ |
pattern_width = ((data_chars + 1) / 2 + (data_chars + 1 & 1)) * 5 + (data_chars + 1) * 8 + 4; |
for (i = 0; i < pattern_width; i++) { |
elements[i] = 0; |
} |
elements[0] = 1; |
elements[1] = 1; |
elements[pattern_width - 2] = 1; |
elements[pattern_width - 1] = 1; |
/* Put finder patterns in element array */ |
for (i = 0; i < (data_chars + 1) / 2 + (data_chars + 1 & 1); i++) { |
k = ((data_chars + 1 - 2) / 2 + (data_chars + 1 & 1) - 1) * 11 + i; |
for (j = 0; j < 5; j++) { |
elements[21 * i + j + 10] = FINDER_PATTERN_EXP[(FINDER_SEQUENCE[k] - 1) * 5 + j]; |
} |
} |
/* Put check character in element array */ |
for (i = 0; i < 8; i++) { |
elements[i + 2] = check_widths[i]; |
} |
/* Put forward reading data characters in element array */ |
for (i = 1; i < data_chars; i += 2) { |
for (j = 0; j < 8; j++) { |
elements[(i - 1) / 2 * 21 + 23 + j] = char_widths[i][j]; |
} |
} |
/* Put reversed data characters in element array */ |
for (i = 0; i < data_chars; i += 2) { |
for (j = 0; j < 8; j++) { |
elements[i / 2 * 21 + 15 + j] = char_widths[i][7 - j]; |
} |
} |
if (!this.stacked) { |
/* Copy elements into symbol */ |
this.row_count = 1 + compositeOffset; |
this.row_height = new int[1 + compositeOffset]; |
this.row_height[0 + compositeOffset] = -1; |
this.pattern = new String[1 + compositeOffset]; |
writer = 0; |
black = false; |
final StringBuilder pat = new StringBuilder("0"); |
final StringBuilder separator_binary = new StringBuilder(); |
for (i = 0; i < pattern_width; i++) { |
pat.append((char) (elements[i] + '0')); |
for (j = 0; j < elements[i]; j++) { |
if (black) { |
separator_binary.append('0'); |
} else { |
separator_binary.append('1'); |
} |
} |
black = !black; |
writer += elements[i]; |
} |
this.pattern[0 + compositeOffset] = pat.toString(); |
separator_binary.setCharAt(0, '0'); |
separator_binary.setCharAt(1, '0'); |
separator_binary.setCharAt(2, '0'); |
separator_binary.setCharAt(3, '0'); |
separator_binary.delete(writer - 4, separator_binary.length()); |
for (j = 0; j < writer / 49; j++) { |
k = 49 * j + 18; |
for (i = 0; i < 15; i++) { |
if (separator_binary.charAt(i + k - 1) == '1' && separator_binary.charAt(i + k) == '1') { |
separator_binary.setCharAt(i + k, '0'); |
} |
} |
} |
if (this.linkageFlag) { |
// Add composite code separator |
this.pattern[0] = bin2pat(separator_binary); |
this.row_height[0] = 1; |
} |
} else { |
/* RSS Expanded Stacked */ |
codeblocks = (data_chars + 1) / 2 + (data_chars + 1) % 2; |
blocksPerRow = this.preferredColumns; |
if (this.linkageFlag && blocksPerRow == 1) { |
/* |
* "There shall be a minimum of four symbol characters in the first row of an RSS |
* Expanded Stacked symbol when it is the linear component of an EAN.UCC Composite |
* symbol." |
*/ |
blocksPerRow = 2; |
} |
stack_rows = codeblocks / blocksPerRow; |
if (codeblocks % blocksPerRow > 0) { |
stack_rows++; |
} |
this.row_count = stack_rows * 4 - 3; |
this.row_height = new int[this.row_count + compositeOffset]; |
this.pattern = new String[this.row_count + compositeOffset]; |
symbol_row = 0; |
current_block = 0; |
for (current_row = 1; current_row <= stack_rows; current_row++) { |
for (i = 0; i < 235; i++) { |
sub_elements[i] = 0; |
} |
special_case_row = false; |
/* Row Start */ |
sub_elements[0] = 1; |
sub_elements[1] = 1; |
elements_in_sub = 2; |
/* Row Data */ |
reader = 0; |
do { |
if ((blocksPerRow & 1) != 0 || (current_row & 1) != 0 |
|| current_row == stack_rows && codeblocks != current_row * blocksPerRow && (current_row * blocksPerRow - codeblocks & 1) != 0) { |
/* left to right */ |
left_to_right = true; |
i = 2 + current_block * 21; |
for (j = 0; j < 21; j++) { |
if (i + j < pattern_width) { |
sub_elements[j + reader * 21 + 2] = elements[i + j]; |
elements_in_sub++; |
} |
} |
} else { |
/* right to left */ |
left_to_right = false; |
if (current_row * blocksPerRow < codeblocks) { |
/* a full row */ |
i = 2 + (current_row * blocksPerRow - reader - 1) * 21; |
for (j = 0; j < 21; j++) { |
if (i + j < pattern_width) { |
sub_elements[20 - j + reader * 21 + 2] = elements[i + j]; |
elements_in_sub++; |
} |
} |
} else { |
/* a partial row */ |
k = current_row * blocksPerRow - codeblocks; |
l = current_row * blocksPerRow - reader - 1; |
i = 2 + (l - k) * 21; |
for (j = 0; j < 21; j++) { |
if (i + j < pattern_width) { |
sub_elements[20 - j + reader * 21 + 2] = elements[i + j]; |
elements_in_sub++; |
} |
} |
} |
} |
reader++; |
current_block++; |
} while (reader < blocksPerRow && current_block < codeblocks); |
/* Row Stop */ |
sub_elements[elements_in_sub] = 1; |
sub_elements[elements_in_sub + 1] = 1; |
elements_in_sub += 2; |
black = true; |
StringBuilder pat = new StringBuilder(); |
this.row_height[symbol_row + compositeOffset] = -1; |
if ((current_row & 1) != 0) { |
pat.append('0'); |
black = false; |
} else { |
if (current_row == stack_rows && codeblocks != current_row * blocksPerRow && (current_row * blocksPerRow - codeblocks & 1) != 0) { |
/* Special case bottom row */ |
special_case_row = true; |
sub_elements[0] = 2; |
pat.append('0'); |
black = false; |
} |
} |
writer = 0; |
final StringBuilder separator_binary = new StringBuilder(); |
for (i = 0; i < elements_in_sub; i++) { |
pat.append((char) (sub_elements[i] + '0')); |
for (j = 0; j < sub_elements[i]; j++) { |
separator_binary.append(black ? '0' : '1'); |
} |
black = !black; |
writer += sub_elements[i]; |
} |
this.pattern[symbol_row + compositeOffset] = pat.toString(); |
separator_binary.setCharAt(0, '0'); |
separator_binary.setCharAt(1, '0'); |
separator_binary.setCharAt(2, '0'); |
separator_binary.setCharAt(3, '0'); |
separator_binary.delete(writer - 4, separator_binary.length()); |
for (j = 0; j < reader; j++) { |
k = 49 * j + (special_case_row ? 19 : 18); |
if (left_to_right) { |
for (i = 0; i < 15; i++) { |
if (separator_binary.charAt(i + k - 1) == '1' && separator_binary.charAt(i + k) == '1') { |
separator_binary.setCharAt(i + k, '0'); |
} |
} |
} else { |
for (i = 14; i >= 0; i--) { |
if (separator_binary.charAt(i + k + 1) == '1' && separator_binary.charAt(i + k) == '1') { |
separator_binary.setCharAt(i + k, '0'); |
} |
} |
} |
} |
separator_pattern = bin2pat(separator_binary); |
if (current_row == 1 && this.linkageFlag) { |
// Add composite code separator |
this.row_height[0] = 1; |
this.pattern[0] = separator_pattern; |
} |
if (current_row != 1) { |
/* middle separator pattern (above current row) */ |
pat = new StringBuilder("05"); |
for (j = 5; j < 49 * blocksPerRow; j += 2) { |
pat.append("11"); |
} |
this.pattern[symbol_row - 2 + compositeOffset] = pat.toString(); |
this.row_height[symbol_row - 2 + compositeOffset] = 1; |
/* bottom separator pattern (above current row) */ |
this.row_height[symbol_row - 1 + compositeOffset] = 1; |
this.pattern[symbol_row - 1 + compositeOffset] = separator_pattern; |
} |
if (current_row != stack_rows) { |
this.row_height[symbol_row + 1 + compositeOffset] = 1; |
this.pattern[symbol_row + 1 + compositeOffset] = separator_pattern; |
} |
symbol_row += 4; |
} |
this.readable = ""; |
this.row_count += compositeOffset; |
} |
} |
/** Handles all data encodation from section 7.2.5 of ISO/IEC 24724. */ |
private static int calculateBinaryString(final int[] inputData, final StringBuilder binaryString) { |
EncodeMode last_mode = EncodeMode.NUMERIC; |
int i; |
boolean latch; |
int remainder, d1, d2, value; |
String padstring; |
int current_length; |
/* |
* Decide whether a compressed data field is required and if so what method to use: method 2 |
* = no compressed data field |
*/ |
int encodingMethod; |
if (inputData.length >= 16 && inputData[0] == '0' && inputData[1] == '1') { |
/* (01) and other AIs */ |
encodingMethod = 1; |
} else { |
/* any AIs */ |
encodingMethod = 2; |
} |
if (inputData.length >= 20 && encodingMethod == 1 && inputData[2] == '9' && inputData[16] == '3') { |
/* Possibly encoding method > 2 */ |
if (inputData.length >= 26 && inputData[17] == '1') { |
/* Methods 3, 7, 9, 11 and 13 */ |
if (inputData[18] == '0') { |
/* (01) and (310x), weight in kilos */ |
double weight = 0; |
for (i = 0; i < 6; i++) { |
weight *= 10; |
weight += inputData[20 + i] - '0'; |
} |
if (weight < 99_999) { /* Maximum weight = 99999 */ |
if (inputData[19] == '3' && inputData.length == 26) { |
/* (01) and (3103) */ |
weight /= 1000.0; |
if (weight <= 32.767) { |
encodingMethod = 3; |
} |
} |
if (inputData.length == 34) { |
if (inputData[26] == '1' && inputData[27] == '1') { |
/* (01), (310x) and (11) - metric weight and production date */ |
encodingMethod = 7; |
} |
if (inputData[26] == '1' && inputData[27] == '3') { |
/* (01), (310x) and (13) - metric weight and packaging date */ |
encodingMethod = 9; |
} |
if (inputData[26] == '1' && inputData[27] == '5') { |
/* (01), (310x) and (15) - metric weight and "best before" date */ |
encodingMethod = 11; |
} |
if (inputData[26] == '1' && inputData[27] == '7') { |
/* (01), (310x) and (17) - metric weight and expiration date */ |
encodingMethod = 13; |
} |
} |
} |
} |
} |
if (inputData.length >= 26 && inputData[17] == '2') { |
/* Methods 4, 8, 10, 12 and 14 */ |
if (inputData[18] == '0') { |
/* (01) and (320x), weight in pounds */ |
double weight = 0; |
for (i = 0; i < 6; i++) { |
weight *= 10; |
weight += inputData[20 + i] - '0'; |
} |
if (weight < 99_999) { /* Maximum weight = 99999 */ |
if ((inputData[19] == '2' || inputData[19] == '3') && inputData.length == 26) { |
/* (01) and (3202)/(3203) */ |
if (inputData[19] == '3') { |
weight /= 1000.0; |
if (weight <= 22.767) { |
encodingMethod = 4; |
} |
} else { |
weight /= 100.0; |
if (weight <= 99.99) { |
encodingMethod = 4; |
} |
} |
} |
if (inputData.length == 34) { |
if (inputData[26] == '1' && inputData[27] == '1') { |
/* (01), (320x) and (11) - English weight and production date */ |
encodingMethod = 8; |
} |
if (inputData[26] == '1' && inputData[27] == '3') { |
/* (01), (320x) and (13) - English weight and packaging date */ |
encodingMethod = 10; |
} |
if (inputData[26] == '1' && inputData[27] == '5') { |
/* (01), (320x) and (15) - English weight and "best before" date */ |
encodingMethod = 12; |
} |
if (inputData[26] == '1' && inputData[27] == '7') { |
/* (01), (320x) and (17) - English weight and expiration date */ |
encodingMethod = 14; |
} |
} |
} |
} |
} |
if (inputData[17] == '9') { |
/* Methods 5 and 6 */ |
if (inputData[18] == '2' && inputData[19] >= '0' && inputData[19] <= '3') { |
/* (01) and (392x) */ |
encodingMethod = 5; |
} |
if (inputData[18] == '3' && inputData[19] >= '0' && inputData[19] <= '3') { |
/* (01) and (393x) */ |
encodingMethod = 6; |
} |
} |
} |
/* Encoding method - Table 10 */ |
/* Variable length symbol bit field is just given a place holder (XX) for the time being */ |
int read_posn; |
switch (encodingMethod) { |
case 1: |
binaryString.append("1XX"); |
read_posn = 16; |
break; |
case 2: |
binaryString.append("00XX"); |
read_posn = 0; |
break; |
case 3: |
binaryString.append("0100"); |
read_posn = inputData.length; |
break; |
case 4: |
binaryString.append("0101"); |
read_posn = inputData.length; |
break; |
case 5: |
binaryString.append("01100XX"); |
read_posn = 20; |
break; |
case 6: |
binaryString.append("01101XX"); |
read_posn = 23; |
break; |
default: /* modes 7 (0111000) to 14 (0111111) */ |
binaryString.append("0" + Integer.toBinaryString(56 + encodingMethod - 7)); |
read_posn = inputData.length; |
break; |
} |
/* |
* Verify that the data to be placed in the compressed data field is all numeric data before |
* carrying out compression |
*/ |
for (i = 0; i < read_posn; i++) { |
if (inputData[i] < '0' || inputData[i] > '9') { |
/* Something is wrong */ |
throw new OkapiException("Invalid characters in input data"); |
} |
} |
/* Now encode the compressed data field */ |
if (encodingMethod == 1) { |
/* Encoding method field "1" - general item identification data */ |
binaryAppend(binaryString, inputData[2] - '0', 4); |
for (i = 1; i < 5; i++) { |
final int group = parseInt(inputData, i * 3, 3); |
binaryAppend(binaryString, group, 10); |
} |
} |
if (encodingMethod == 3 || encodingMethod == 4) { |
/* Encoding method field "0100" - variable weight item (0,001 kilogram increments) */ |
/* |
* Encoding method field "0101" - variable weight item (0,01 or 0,001 pound increment) |
*/ |
for (i = 1; i < 5; i++) { |
final int group = parseInt(inputData, i * 3, 3); |
binaryAppend(binaryString, group, 10); |
} |
int group = parseInt(inputData, 20, 6); |
if (encodingMethod == 4 && inputData[19] == '3') { |
group += 10_000; |
} |
binaryAppend(binaryString, group, 15); |
} |
if (encodingMethod == 5 || encodingMethod == 6) { |
/* Encoding method field "01100" - variable measure item and price */ |
/* |
* Encoding method "01101" - variable measure item and price with ISO 4217 currency code |
*/ |
for (i = 1; i < 5; i++) { |
final int group = parseInt(inputData, i * 3, 3); |
binaryAppend(binaryString, group, 10); |
} |
binaryAppend(binaryString, inputData[19] - '0', 2); |
if (encodingMethod == 6) { |
final int currency = parseInt(inputData, 20, 3); |
binaryAppend(binaryString, currency, 10); |
} |
} |
if (encodingMethod >= 7 && encodingMethod <= 14) { |
/* |
* Encoding method fields "0111000" through "0111111" - variable weight item plus date |
*/ |
for (i = 1; i < 5; i++) { |
final int group = parseInt(inputData, i * 3, 3); |
binaryAppend(binaryString, group, 10); |
} |
int weight = inputData[19] - '0'; |
for (i = 0; i < 5; i++) { |
weight *= 10; |
weight += inputData[21 + i] - '0'; |
} |
binaryAppend(binaryString, weight, 20); |
int date; |
if (inputData.length == 34) { |
/* Date information is included */ |
date = parseInt(inputData, 28, 2) * 384; |
date += (parseInt(inputData, 30, 2) - 1) * 32; |
date += parseInt(inputData, 32, 2); |
} else { |
date = 38_400; |
} |
binaryAppend(binaryString, date, 16); |
} |
/* |
* The compressed data field has been processed if appropriate - the rest of the data (if |
* any) goes into a general-purpose data compaction field |
*/ |
final int[] generalField = Arrays.copyOfRange(inputData, read_posn, inputData.length); |
if (generalField.length != 0) { |
latch = false; |
final EncodeMode[] generalFieldType = new EncodeMode[generalField.length]; |
for (i = 0; i < generalField.length; i++) { |
/* Tables 11, 12, 13 - ISO/IEC 646 encodation */ |
final int c = generalField[i]; |
EncodeMode mode; |
if (c == FNC1) { |
// FNC1 can be encoded in any system |
mode = EncodeMode.ANY_ENC; |
} else if (c >= '0' && c <= '9') { |
// numbers can be encoded in any system, but will usually narrow down to numeric |
// encodation |
mode = EncodeMode.ANY_ENC; |
} else if (c >= 'A' && c <= 'Z' || c == '*' || c == ',' || c == '-' || c == '.' || c == '/') { |
// alphanumeric encodation or ISO/IEC encodation |
mode = EncodeMode.ALPHA_OR_ISO; |
} else if (c >= 'a' && c <= 'z' || c == '!' || c == '"' || c == '%' || c == '&' || c == '\'' || c == '(' || c == ')' || c == '+' || c == ':' || c == ';' || c == '<' || c == '=' |
|| c == '>' || c == '?' || c == '_' || c == ' ') { |
// ISO/IEC encodation |
mode = EncodeMode.ISOIEC; |
} else { |
// unable to encode this character |
mode = EncodeMode.INVALID_CHAR; |
latch = true; |
} |
generalFieldType[i] = mode; |
} |
if (latch) { |
throw new OkapiException("Invalid characters in input data"); |
} |
for (i = 0; i < generalField.length - 1; i++) { |
if (generalFieldType[i] == EncodeMode.ISOIEC && generalField[i + 1] == FNC1) { |
generalFieldType[i + 1] = EncodeMode.ISOIEC; |
} |
} |
for (i = 0; i < generalField.length - 1; i++) { |
if (generalFieldType[i] == EncodeMode.ALPHA_OR_ISO && generalField[i + 1] == FNC1) { |
generalFieldType[i + 1] = EncodeMode.ALPHA_OR_ISO; |
} |
} |
latch = applyGeneralFieldRules(generalFieldType); // modifies generalFieldType |
/* Set initial mode if not NUMERIC */ |
if (generalFieldType[0] == EncodeMode.ALPHA) { |
binaryString.append("0000"); /* Alphanumeric latch */ |
last_mode = EncodeMode.ALPHA; |
} |
if (generalFieldType[0] == EncodeMode.ISOIEC) { |
binaryString.append("0000"); /* Alphanumeric latch */ |
binaryString.append("00100"); /* ISO/IEC 646 latch */ |
last_mode = EncodeMode.ISOIEC; |
} |
i = 0; |
do { |
switch (generalFieldType[i]) { |
case NUMERIC: |
if (last_mode != EncodeMode.NUMERIC) { |
binaryString.append("000"); /* Numeric latch */ |
} |
if (generalField[i] != FNC1) { |
d1 = generalField[i] - '0'; |
} else { |
d1 = 10; |
} |
if (generalField[i + 1] != FNC1) { |
d2 = generalField[i + 1] - '0'; |
} else { |
d2 = 10; |
} |
value = 11 * d1 + d2 + 8; |
binaryAppend(binaryString, value, 7); |
i += 2; |
last_mode = EncodeMode.NUMERIC; |
break; |
case ALPHA: |
if (i != 0) { |
if (last_mode == EncodeMode.NUMERIC) { |
binaryString.append("0000"); /* Alphanumeric latch */ |
} |
if (last_mode == EncodeMode.ISOIEC) { |
binaryString.append("00100"); /* Alphanumeric latch */ |
} |
} |
if (generalField[i] >= '0' && generalField[i] <= '9') { |
value = generalField[i] - 43; |
binaryAppend(binaryString, value, 5); |
} |
if (generalField[i] >= 'A' && generalField[i] <= 'Z') { |
value = generalField[i] - 33; |
binaryAppend(binaryString, value, 6); |
} |
last_mode = EncodeMode.ALPHA; |
if (generalField[i] == FNC1) { |
binaryString.append("01111"); |
// TODO: FNC1 should act as an implicit numeric latch, so the commented out |
// line below should be correct, but ZXing cannot |
// read barcodes which use FNC1 as an implicit numeric latch... so for now, |
// and in order to achieve widest compatibility, |
// we waste 3 bits and don't perform the implicit mode change (see |
// https://sourceforge.net/p/zint/tickets/145/) |
// last_mode = EncodeMode.NUMERIC; |
} /* FNC1 / Numeric latch */ |
if (generalField[i] == '*') { |
binaryString.append("111010"); /* asterisk */ |
} |
if (generalField[i] == ',') { |
binaryString.append("111011"); /* comma */ |
} |
if (generalField[i] == '-') { |
binaryString.append("111100"); /* minus or hyphen */ |
} |
if (generalField[i] == '.') { |
binaryString.append("111101"); /* period or full stop */ |
} |
if (generalField[i] == '/') { |
binaryString.append("111110"); /* slash or solidus */ |
} |
i++; |
break; |
case ISOIEC: |
if (i != 0) { |
if (last_mode == EncodeMode.NUMERIC) { |
binaryString.append("0000"); /* Alphanumeric latch */ |
binaryString.append("00100"); /* ISO/IEC 646 latch */ |
} |
if (last_mode == EncodeMode.ALPHA) { |
binaryString.append("00100"); /* ISO/IEC 646 latch */ |
} |
} |
if (generalField[i] >= '0' && generalField[i] <= '9') { |
value = generalField[i] - 43; |
binaryAppend(binaryString, value, 5); |
} |
if (generalField[i] >= 'A' && generalField[i] <= 'Z') { |
value = generalField[i] - 1; |
binaryAppend(binaryString, value, 7); |
} |
if (generalField[i] >= 'a' && generalField[i] <= 'z') { |
value = generalField[i] - 7; |
binaryAppend(binaryString, value, 7); |
} |
last_mode = EncodeMode.ISOIEC; |
if (generalField[i] == FNC1) { |
binaryString.append("01111"); |
// TODO: FNC1 should act as an implicit numeric latch, so the commented out |
// line below should be correct, but ZXing cannot |
// read barcodes which use FNC1 as an implicit numeric latch... so for now, |
// and in order to achieve widest compatibility, |
// we waste 3 bits and don't perform the implicit mode change (see |
// https://sourceforge.net/p/zint/tickets/145/) |
// last_mode = EncodeMode.NUMERIC; |
} /* FNC1 / Numeric latch */ |
if (generalField[i] == '!') { |
binaryString.append("11101000"); /* exclamation mark */ |
} |
if (generalField[i] == 34) { |
binaryString.append("11101001"); /* quotation mark */ |
} |
if (generalField[i] == 37) { |
binaryString.append("11101010"); /* percent sign */ |
} |
if (generalField[i] == '&') { |
binaryString.append("11101011"); /* ampersand */ |
} |
if (generalField[i] == 39) { |
binaryString.append("11101100"); /* apostrophe */ |
} |
if (generalField[i] == '(') { |
binaryString.append("11101101"); /* left parenthesis */ |
} |
if (generalField[i] == ')') { |
binaryString.append("11101110"); /* right parenthesis */ |
} |
if (generalField[i] == '*') { |
binaryString.append("11101111"); /* asterisk */ |
} |
if (generalField[i] == '+') { |
binaryString.append("11110000"); /* plus sign */ |
} |
if (generalField[i] == ',') { |
binaryString.append("11110001"); /* comma */ |
} |
if (generalField[i] == '-') { |
binaryString.append("11110010"); /* minus or hyphen */ |
} |
if (generalField[i] == '.') { |
binaryString.append("11110011"); /* period or full stop */ |
} |
if (generalField[i] == '/') { |
binaryString.append("11110100"); /* slash or solidus */ |
} |
if (generalField[i] == ':') { |
binaryString.append("11110101"); /* colon */ |
} |
if (generalField[i] == ';') { |
binaryString.append("11110110"); /* semicolon */ |
} |
if (generalField[i] == '<') { |
binaryString.append("11110111"); /* less-than sign */ |
} |
if (generalField[i] == '=') { |
binaryString.append("11111000"); /* equals sign */ |
} |
if (generalField[i] == '>') { |
binaryString.append("11111001"); /* greater-than sign */ |
} |
if (generalField[i] == '?') { |
binaryString.append("11111010"); /* question mark */ |
} |
if (generalField[i] == '_') { |
binaryString.append("11111011"); /* underline or low line */ |
} |
if (generalField[i] == ' ') { |
binaryString.append("11111100"); /* space */ |
} |
i++; |
break; |
} |
current_length = i; |
if (latch) { |
current_length++; |
} |
} while (current_length < generalField.length); |
remainder = calculateRemainder(binaryString.length()); |
if (latch) { |
/* There is still one more numeric digit to encode */ |
if (last_mode == EncodeMode.NUMERIC) { |
if (remainder >= 4 && remainder <= 6) { |
value = generalField[i] - '0'; |
value++; |
binaryAppend(binaryString, value, 4); |
} else { |
d1 = generalField[i] - '0'; |
d2 = 10; |
value = 11 * d1 + d2 + 8; |
binaryAppend(binaryString, value, 7); |
} |
} else { |
value = generalField[i] - 43; |
binaryAppend(binaryString, value, 5); |
} |
} |
} |
if (binaryString.length() > 252) { |
throw new OkapiException("Input too long"); |
} |
remainder = calculateRemainder(binaryString.length()); |
/* Now add padding to binary string (7.2.5.5.4) */ |
i = remainder; |
if (generalField.length != 0 && last_mode == EncodeMode.NUMERIC) { |
padstring = "0000"; |
i -= 4; |
} else { |
padstring = ""; |
} |
for (; i > 0; i -= 5) { |
padstring += "00100"; |
} |
binaryString.append(padstring.substring(0, remainder)); |
/* Patch variable length symbol bit field */ |
char patchEvenOdd, patchSize; |
if ((binaryString.length() / 12 + 1 & 1) == 0) { |
patchEvenOdd = '0'; |
} else { |
patchEvenOdd = '1'; |
} |
if (binaryString.length() <= 156) { |
patchSize = '0'; |
} else { |
patchSize = '1'; |
} |
if (encodingMethod == 1) { |
binaryString.setCharAt(2, patchEvenOdd); |
binaryString.setCharAt(3, patchSize); |
} |
if (encodingMethod == 2) { |
binaryString.setCharAt(3, patchEvenOdd); |
binaryString.setCharAt(4, patchSize); |
} |
if (encodingMethod == 5 || encodingMethod == 6) { |
binaryString.setCharAt(6, patchEvenOdd); |
binaryString.setCharAt(7, patchSize); |
} |
return encodingMethod; |
} |
private static int calculateRemainder(final int binaryStringLength) { |
int remainder = 12 - binaryStringLength % 12; |
if (remainder == 12) { |
remainder = 0; |
} |
if (binaryStringLength < 36) { |
remainder = 36 - binaryStringLength; |
} |
return remainder; |
} |
/** Logs binary string as hexadecimal */ |
private void logBinaryStringInfo(final StringBuilder binaryString) { |
infoLine("Binary Length: " + binaryString.length()); |
info("Binary String: "); |
int nibble = 0; |
for (int i = 0; i < binaryString.length(); i++) { |
switch (i % 4) { |
case 0: |
if (binaryString.charAt(i) == '1') { |
nibble += 8; |
} |
break; |
case 1: |
if (binaryString.charAt(i) == '1') { |
nibble += 4; |
} |
break; |
case 2: |
if (binaryString.charAt(i) == '1') { |
nibble += 2; |
} |
break; |
case 3: |
if (binaryString.charAt(i) == '1') { |
nibble += 1; |
} |
info(Integer.toHexString(nibble)); |
nibble = 0; |
break; |
} |
} |
if (binaryString.length() % 4 != 0) { |
info(Integer.toHexString(nibble)); |
} |
infoLine(); |
} |
/** |
* Attempts to apply encoding rules from sections 7.2.5.5.1 to 7.2.5.5.3 of ISO/IEC 24724:2006 |
*/ |
private static boolean applyGeneralFieldRules(final EncodeMode[] generalFieldType) { |
int block_count, i, j, k; |
EncodeMode current, next, last; |
final int[] blockLength = new int[200]; |
final EncodeMode[] blockType = new EncodeMode[200]; |
block_count = 0; |
blockLength[block_count] = 1; |
blockType[block_count] = generalFieldType[0]; |
for (i = 1; i < generalFieldType.length; i++) { |
current = generalFieldType[i]; |
last = generalFieldType[i - 1]; |
if (current == last) { |
blockLength[block_count] = blockLength[block_count] + 1; |
} else { |
block_count++; |
blockLength[block_count] = 1; |
blockType[block_count] = generalFieldType[i]; |
} |
} |
block_count++; |
for (i = 0; i < block_count; i++) { |
current = blockType[i]; |
next = blockType[i + 1]; |
if (current == EncodeMode.ISOIEC && i != block_count - 1) { |
if (next == EncodeMode.ANY_ENC && blockLength[i + 1] >= 4) { |
blockType[i + 1] = EncodeMode.NUMERIC; |
} |
if (next == EncodeMode.ANY_ENC && blockLength[i + 1] < 4) { |
blockType[i + 1] = EncodeMode.ISOIEC; |
} |
if (next == EncodeMode.ALPHA_OR_ISO && blockLength[i + 1] >= 5) { |
blockType[i + 1] = EncodeMode.ALPHA; |
} |
if (next == EncodeMode.ALPHA_OR_ISO && blockLength[i + 1] < 5) { |
blockType[i + 1] = EncodeMode.ISOIEC; |
} |
} |
if (current == EncodeMode.ALPHA_OR_ISO) { |
blockType[i] = EncodeMode.ALPHA; |
current = EncodeMode.ALPHA; |
} |
if (current == EncodeMode.ALPHA && i != block_count - 1) { |
if (next == EncodeMode.ANY_ENC && blockLength[i + 1] >= 6) { |
blockType[i + 1] = EncodeMode.NUMERIC; |
} |
if (next == EncodeMode.ANY_ENC && blockLength[i + 1] < 6) { |
if (i == block_count - 2 && blockLength[i + 1] >= 4) { |
blockType[i + 1] = EncodeMode.NUMERIC; |
} else { |
blockType[i + 1] = EncodeMode.ALPHA; |
} |
} |
} |
if (current == EncodeMode.ANY_ENC) { |
blockType[i] = EncodeMode.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; |
/* decrease 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] == EncodeMode.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++) { |
generalFieldType[j] = blockType[i]; |
j++; |
} |
} |
if (blockType[block_count - 1] == EncodeMode.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 static int parseInt(final int[] chars, final int index, final int length) { |
int val = 0; |
int pow = (int) Math.pow(10, length - 1); |
for (int i = 0; i < length; i++) { |
final int c = chars[index + i]; |
val += (c - '0') * pow; |
pow /= 10; |
} |
return val; |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/MicroQrCode.java |
---|
New file |
0,0 → 1,1606 |
/* |
* 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.io.UnsupportedEncodingException; |
/** |
* Implements Micro QR Code According to ISO/IEC 18004:2006 <br> |
* A miniature version of the QR Code symbol for short messages. QR Code symbols can encode |
* characters in the Latin-1 set and Kanji characters which are members of the Shift-JIS encoding |
* scheme. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class MicroQrCode extends Symbol { |
public enum EccMode { |
L, M, Q, H |
} |
private enum qrMode { |
NULL, KANJI, BINARY, ALPHANUM, NUMERIC |
} |
/* Table 5 - Encoding/Decoding table for Alphanumeric mode */ |
private static final char[] RHODIUM = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', |
'V', 'W', 'X', 'Y', 'Z', ' ', '$', '%', '*', '+', '-', '.', '/', ':' }; |
private static final int[] QR_ANNEX_C1 = { |
/* Micro QR Code format information */ |
0x4445, 0x4172, 0x4e2b, 0x4b1c, 0x55ae, 0x5099, 0x5fc0, 0x5af7, 0x6793, 0x62a4, 0x6dfd, 0x68ca, 0x7678, 0x734f, 0x7c16, 0x7921, 0x06de, 0x03e9, 0x0cb0, 0x0987, 0x1735, 0x1202, 0x1d5b, |
0x186c, 0x2508, 0x203f, 0x2f66, 0x2a51, 0x34e3, 0x31d4, 0x3e8d, 0x3bba }; |
private static final int[] MICRO_QR_SIZES = { 11, 13, 15, 17 }; |
// user-specified values and settings |
private int preferredVersion; |
private EccMode preferredEccLevel = EccMode.L; |
// internal state calculated when setContent() is called |
private qrMode[] inputMode; |
private StringBuilder binary; |
private final int[] binaryCount = new int[4]; |
private int[] grid; |
private int[] eval; |
/** |
* <p> |
* Sets the preferred symbol size. This value may be ignored if the data string is too large to |
* fit into the specified symbol. Input values correspond to symbol sizes as shown in the |
* following table. |
* |
* <table summary="Range of Micro QR symbol sizes"> |
* <tbody> |
* <tr> |
* <th>Input</th> |
* <th>Version</th> |
* <th>Symbol Size</th> |
* </tr> |
* <tr> |
* <td>1</td> |
* <td>M1</td> |
* <td>11 x 11</td> |
* </tr> |
* <tr> |
* <td>2</td> |
* <td>M2</td> |
* <td>13 x 13</td> |
* </tr> |
* <tr> |
* <td>3</td> |
* <td>M3</td> |
* <td>15 x 15</td> |
* </tr> |
* <tr> |
* <td>4</td> |
* <td>M4</td> |
* <td>17 x 17</td> |
* </tr> |
* </tbody> |
* </table> |
* |
* @param version symbol size |
*/ |
public void setPreferredVersion(final int version) { |
if (version < 0 || version > 4) { // TODO: min 1 |
throw new IllegalArgumentException("Invalid version: " + version); |
} |
this.preferredVersion = version; |
} |
/** |
* Returns the preferred symbol size. |
* |
* @return the preferred symbol size |
* @see #setPreferredVersion(int) |
*/ |
public int getPreferredVersion() { |
return this.preferredVersion; |
} |
/** |
* <p> |
* Set the amount of symbol space allocated to error correction. Levels are predefined according |
* to the following table: |
* |
* <table summary="Micro QR Error correction levels"> |
* <tbody> |
* <tr> |
* <th>ECC Level</th> |
* <th>Error Correction Capacity</th> |
* <th>Recovery Capacity</th> |
* </tr> |
* <tr> |
* <td>L (default)</td> |
* <td>Approx 20% of symbol</td> |
* <td>Approx 7%</td> |
* </tr> |
* <tr> |
* <td>M</td> |
* <td>Approx 37% of symbol</td> |
* <td>Approx 15%</td> |
* </tr> |
* <tr> |
* <td>Q</td> |
* <td>Approx 55% of symbol</td> |
* <td>Approx 25%</td> |
* </tr> |
* <tr> |
* <td>H</td> |
* <td>Approx 65% of symbol</td> |
* <td>Approx 30%</td> |
* </tr> |
* </tbody> |
* </table> |
* |
* @param eccMode error correction level |
*/ |
public void setEccMode(final EccMode eccMode) { |
this.preferredEccLevel = eccMode; |
} |
/** |
* Returns the preferred ECC mode (error correction level). |
* |
* @return the preferred ECC mode |
* @see #setEccMode(EccMode) |
*/ |
public EccMode getEccMode() { |
return this.preferredEccLevel; |
} |
@Override |
protected void encode() { |
int i, j, size; |
final boolean[] version_valid = new boolean[4]; |
int n_count, a_count; |
EccMode ecc_level; |
int version, autoversion; |
int bitmask; |
int format, format_full; |
final StringBuilder bin = new StringBuilder(); |
boolean byteModeUsed; |
boolean alphanumModeUsed; |
boolean kanjiModeUsed; |
if (this.content.length() > 35) { |
throw new OkapiException("Input data too long"); |
} |
inputCharCheck(); |
for (i = 0; i < 4; i++) { |
version_valid[i] = true; |
} |
this.inputMode = new qrMode[40]; |
selectEncodingMode(); |
n_count = 0; |
a_count = 0; |
for (i = 0; i < this.content.length(); i++) { |
if (this.content.charAt(i) >= '0' && this.content.charAt(i) <= '9') { |
n_count++; |
} |
if (isAlphanumeric(this.content.charAt(i))) { |
a_count++; |
} |
} |
if (a_count == this.content.length()) { |
/* All data can be encoded in Alphanumeric mode */ |
for (i = 0; i < this.content.length(); i++) { |
this.inputMode[i] = qrMode.ALPHANUM; |
} |
} |
if (n_count == this.content.length()) { |
/* All data can be encoded in Numeric mode */ |
for (i = 0; i < this.content.length(); i++) { |
this.inputMode[i] = qrMode.NUMERIC; |
} |
} |
byteModeUsed = false; |
alphanumModeUsed = false; |
kanjiModeUsed = false; |
for (i = 0; i < this.content.length(); i++) { |
if (this.inputMode[i] == qrMode.BINARY) { |
byteModeUsed = true; |
} |
if (this.inputMode[i] == qrMode.ALPHANUM) { |
alphanumModeUsed = true; |
} |
if (this.inputMode[i] == qrMode.KANJI) { |
kanjiModeUsed = true; |
} |
} |
getBinaryLength(); |
/* Eliminate possible versions depending on type of content */ |
if (byteModeUsed) { |
version_valid[0] = false; |
version_valid[1] = false; |
} |
if (alphanumModeUsed) { |
version_valid[0] = false; |
} |
if (kanjiModeUsed) { |
version_valid[0] = false; |
version_valid[1] = false; |
} |
/* Eliminate possible versions depending on length of binary data */ |
if (this.binaryCount[0] > 20) { |
version_valid[0] = false; |
} |
if (this.binaryCount[1] > 40) { |
version_valid[1] = false; |
} |
if (this.binaryCount[2] > 84) { |
version_valid[2] = false; |
} |
if (this.binaryCount[3] > 128) { |
throw new OkapiException("Input data too long"); |
} |
/* Eliminate possible versions depending on error correction level specified */ |
ecc_level = this.preferredEccLevel; |
if (ecc_level == EccMode.H) { |
throw new OkapiException("Error correction level H not available"); |
} |
if (ecc_level == EccMode.Q) { |
version_valid[0] = false; |
version_valid[1] = false; |
version_valid[2] = false; |
if (this.binaryCount[3] > 80) { |
throw new OkapiException("Input data too long"); |
} |
} |
if (ecc_level == EccMode.M) { |
version_valid[0] = false; |
if (this.binaryCount[1] > 32) { |
version_valid[1] = false; |
} |
if (this.binaryCount[2] > 68) { |
version_valid[2] = false; |
} |
if (this.binaryCount[3] > 112) { |
throw new OkapiException("Input data too long"); |
} |
} |
autoversion = 3; |
if (version_valid[2]) { |
autoversion = 2; |
} |
if (version_valid[1]) { |
autoversion = 1; |
} |
if (version_valid[0]) { |
autoversion = 0; |
} |
version = autoversion; |
/* Get version from user */ |
if (this.preferredVersion >= 1 && this.preferredVersion <= 4) { |
if (this.preferredVersion - 1 >= autoversion) { |
version = this.preferredVersion - 1; |
} |
} |
/* If there is enough unused space then increase the error correction level */ |
if (version == 3) { |
if (this.binaryCount[3] <= 112) { |
ecc_level = EccMode.M; |
} |
if (this.binaryCount[3] <= 80) { |
ecc_level = EccMode.Q; |
} |
} |
if (version == 2 && this.binaryCount[2] <= 68) { |
ecc_level = EccMode.M; |
} |
if (version == 1 && this.binaryCount[1] <= 32) { |
ecc_level = EccMode.M; |
} |
this.binary = new StringBuilder(); |
generateBinary(version); |
if (this.binary.length() > 128) { |
throw new OkapiException("Input data too long"); |
} |
switch (version) { |
case 0: |
generateM1Symbol(); |
infoLine("Version: M1"); |
break; |
case 1: |
generateM2Symbol(ecc_level); |
infoLine("Version: M2"); |
infoLine("ECC Level: " + levelToLetter(ecc_level)); |
break; |
case 2: |
generateM3Symbol(ecc_level); |
infoLine("Version: M3"); |
infoLine("ECC Level: " + levelToLetter(ecc_level)); |
break; |
case 3: |
generateM4Symbol(ecc_level); |
infoLine("Version: M4"); |
infoLine("ECC Level: " + levelToLetter(ecc_level)); |
break; |
} |
size = MICRO_QR_SIZES[version]; |
this.grid = new int[size * size]; |
for (i = 0; i < size; i++) { |
for (j = 0; j < size; j++) { |
this.grid[i * size + j] = 0; |
} |
} |
setupBitGrid(size); |
populateBitGrid(size); |
bitmask = applyBitmask(size); |
infoLine("Mask Pattern: " + Integer.toBinaryString(bitmask)); |
/* Add format data */ |
format = 0; |
switch (version) { |
case 1: |
switch (ecc_level) { |
case L: |
format = 1; |
break; |
case M: |
format = 2; |
break; |
} |
break; |
case 2: |
switch (ecc_level) { |
case L: |
format = 3; |
break; |
case M: |
format = 4; |
break; |
} |
break; |
case 3: |
switch (ecc_level) { |
case L: |
format = 5; |
break; |
case M: |
format = 6; |
break; |
case Q: |
format = 7; |
break; |
} |
break; |
} |
format_full = QR_ANNEX_C1[(format << 2) + bitmask]; |
if ((format_full & 0x4000) != 0) { |
this.grid[8 * size + 1] += 0x01; |
} |
if ((format_full & 0x2000) != 0) { |
this.grid[8 * size + 2] += 0x01; |
} |
if ((format_full & 0x1000) != 0) { |
this.grid[8 * size + 3] += 0x01; |
} |
if ((format_full & 0x800) != 0) { |
this.grid[8 * size + 4] += 0x01; |
} |
if ((format_full & 0x400) != 0) { |
this.grid[8 * size + 5] += 0x01; |
} |
if ((format_full & 0x200) != 0) { |
this.grid[8 * size + 6] += 0x01; |
} |
if ((format_full & 0x100) != 0) { |
this.grid[8 * size + 7] += 0x01; |
} |
if ((format_full & 0x80) != 0) { |
this.grid[8 * size + 8] += 0x01; |
} |
if ((format_full & 0x40) != 0) { |
this.grid[7 * size + 8] += 0x01; |
} |
if ((format_full & 0x20) != 0) { |
this.grid[6 * size + 8] += 0x01; |
} |
if ((format_full & 0x10) != 0) { |
this.grid[5 * size + 8] += 0x01; |
} |
if ((format_full & 0x08) != 0) { |
this.grid[4 * size + 8] += 0x01; |
} |
if ((format_full & 0x04) != 0) { |
this.grid[3 * size + 8] += 0x01; |
} |
if ((format_full & 0x02) != 0) { |
this.grid[2 * size + 8] += 0x01; |
} |
if ((format_full & 0x01) != 0) { |
this.grid[1 * size + 8] += 0x01; |
} |
this.readable = ""; |
this.pattern = new String[size]; |
this.row_count = size; |
this.row_height = new int[size]; |
for (i = 0; i < size; i++) { |
bin.setLength(0); |
for (j = 0; j < size; j++) { |
if ((this.grid[i * size + j] & 0x01) != 0) { |
bin.append('1'); |
} else { |
bin.append('0'); |
} |
} |
this.pattern[i] = bin2pat(bin); |
this.row_height[i] = 1; |
} |
} |
private void inputCharCheck() { |
int qmarkBefore, qmarkAfter; |
int i; |
byte[] temp; |
/* Check that input includes valid characters */ |
if (this.content.matches("[\u0000-\u00FF]+")) { |
/* All characters in ISO 8859-1 */ |
return; |
} |
/* Otherwise check for Shift-JIS characters */ |
qmarkBefore = 0; |
for (i = 0; i < this.content.length(); i++) { |
if (this.content.charAt(i) == '?') { |
qmarkBefore++; |
} |
} |
try { |
temp = this.content.getBytes("SJIS"); |
} catch (final UnsupportedEncodingException e) { |
throw new OkapiException("Character encoding error"); |
} |
qmarkAfter = 0; |
for (i = 0; i < temp.length; i++) { |
if (temp[i] == '?') { |
qmarkAfter++; |
} |
} |
/* If these values are the same, conversion was successful */ |
if (qmarkBefore != qmarkAfter) { |
throw new OkapiException("Invalid characters in input data"); |
} |
} |
private char levelToLetter(final EccMode ecc_mode) { |
switch (ecc_mode) { |
case L: |
return 'L'; |
case M: |
return 'M'; |
case Q: |
return 'Q'; |
case H: |
return 'H'; |
default: |
return ' '; |
} |
} |
private void selectEncodingMode() { |
int i, j; |
int mlen; |
final int length = this.content.length(); |
for (i = 0; i < length; i++) { |
if (this.content.charAt(i) > 0xff) { |
this.inputMode[i] = qrMode.KANJI; |
} else { |
this.inputMode[i] = qrMode.BINARY; |
if (isAlphanumeric(this.content.charAt(i))) { |
this.inputMode[i] = qrMode.ALPHANUM; |
} |
if (this.content.charAt(i) >= '0' && this.content.charAt(i) <= '9') { |
this.inputMode[i] = qrMode.NUMERIC; |
} |
} |
} |
/* If less than 6 numeric digits together then don't use numeric mode */ |
for (i = 0; i < length; i++) { |
if (this.inputMode[i] == qrMode.NUMERIC) { |
if (i != 0 && this.inputMode[i - 1] != qrMode.NUMERIC || i == 0) { |
mlen = 0; |
while (mlen + i < length && this.inputMode[mlen + i] == qrMode.NUMERIC) { |
mlen++; |
} |
if (mlen < 6) { |
for (j = 0; j < mlen; j++) { |
this.inputMode[i + j] = qrMode.ALPHANUM; |
} |
} |
} |
} |
} |
/* If less than 4 alphanumeric characters together then don't use alphanumeric mode */ |
for (i = 0; i < length; i++) { |
if (this.inputMode[i] == qrMode.ALPHANUM) { |
if (i != 0 && this.inputMode[i - 1] != qrMode.ALPHANUM || i == 0) { |
mlen = 0; |
while (mlen + i < length && this.inputMode[mlen + i] == qrMode.ALPHANUM) { |
mlen++; |
} |
if (mlen < 6) { |
for (j = 0; j < mlen; j++) { |
this.inputMode[i + j] = qrMode.BINARY; |
} |
} |
} |
} |
} |
} |
private boolean isAlphanumeric(final char cglyph) { |
/* Returns true if input glyph is in the Alphanumeric set */ |
boolean retval = false; |
if (cglyph >= '0' && cglyph <= '9') { |
retval = true; |
} |
if (cglyph >= 'A' && cglyph <= 'Z') { |
retval = true; |
} |
switch (cglyph) { |
case ' ': |
case '$': |
case '%': |
case '*': |
case '+': |
case '-': |
case '.': |
case '/': |
case ':': |
retval = true; |
break; |
} |
return retval; |
} |
private String toBinary(final int data, int h) { |
final StringBuilder binary = new StringBuilder(); |
for (; h != 0; h >>= 1) { |
if ((data & h) != 0) { |
binary.append('1'); |
} else { |
binary.append('0'); |
} |
} |
return binary.toString(); |
} |
private void getBinaryLength() { |
int i; |
qrMode currentMode = qrMode.NULL; |
int blockLength; |
/* Always include a terminator */ |
for (i = 0; i < 4; i++) { |
this.binaryCount[i] = 0; |
} |
for (i = 0; i < this.content.length(); i++) { |
if (currentMode != this.inputMode[i]) { |
blockLength = 0; |
do { |
blockLength++; |
} while (i + blockLength < this.content.length() && this.inputMode[i + blockLength] == this.inputMode[i]); |
switch (this.inputMode[i]) { |
case KANJI: |
this.binaryCount[2] += 5 + blockLength * 13; |
this.binaryCount[3] += 7 + blockLength * 13; |
break; |
case BINARY: |
this.binaryCount[2] += 6 + blockLength * 8; |
this.binaryCount[3] += 8 + blockLength * 8; |
break; |
case ALPHANUM: |
int alphaLength; |
if (blockLength % 2 == 1) { |
/* Odd length block */ |
alphaLength = (blockLength - 1) / 2 * 11; |
alphaLength += 6; |
} else { |
/* Even length block */ |
alphaLength = blockLength / 2 * 11; |
} |
this.binaryCount[1] += 4 + alphaLength; |
this.binaryCount[2] += 6 + alphaLength; |
this.binaryCount[3] += 8 + alphaLength; |
break; |
case NUMERIC: |
int numLength; |
switch (blockLength % 3) { |
case 1: |
/* one digit left over */ |
numLength = (blockLength - 1) / 3 * 10; |
numLength += 4; |
break; |
case 2: |
/* two digits left over */ |
numLength = (blockLength - 2) / 3 * 10; |
numLength += 7; |
break; |
default: |
/* blockLength is a multiple of 3 */ |
numLength = blockLength / 3 * 10; |
break; |
} |
this.binaryCount[0] += 3 + numLength; |
this.binaryCount[1] += 5 + numLength; |
this.binaryCount[2] += 7 + numLength; |
this.binaryCount[3] += 9 + numLength; |
break; |
} |
currentMode = this.inputMode[i]; |
} |
} |
/* Add terminator */ |
if (this.binaryCount[1] < 37) { |
this.binaryCount[1] += 5; |
} |
if (this.binaryCount[2] < 81) { |
this.binaryCount[2] += 7; |
} |
if (this.binaryCount[3] < 125) { |
this.binaryCount[3] += 9; |
} |
} |
private void generateBinary(final int version) { |
int position = 0; |
int blockLength, i; |
qrMode data_block; |
int msb, lsb, prod, jis; |
String oneChar; |
byte[] jisBytes; |
int count, first, second, third; |
info("Encoding: "); |
do { |
data_block = this.inputMode[position]; |
blockLength = 0; |
do { |
blockLength++; |
} while (blockLength + position < this.content.length() && this.inputMode[position + blockLength] == data_block); |
switch (data_block) { |
case KANJI: |
/* Kanji mode */ |
/* Mode indicator */ |
switch (version) { |
case 2: |
this.binary.append("11"); |
break; |
case 3: |
this.binary.append("011"); |
break; |
} |
/* Character count indicator */ |
this.binary.append(toBinary(blockLength, 1 << version)); /* version = 2..3 */ |
info("KANJ (" + blockLength + ") "); |
/* Character representation */ |
for (i = 0; i < blockLength; i++) { |
oneChar = ""; |
oneChar += this.content.charAt(position + i); |
/* Convert Unicode input to Shift-JIS */ |
try { |
jisBytes = oneChar.getBytes("SJIS"); |
} catch (final UnsupportedEncodingException e) { |
throw new OkapiException("Character encoding error"); |
} |
jis = (jisBytes[0] & 0xFF) << 8; |
if (jisBytes.length > 1) { |
jis += jisBytes[1] & 0xFF; |
} |
if (jis > 0x9fff) { |
jis -= 0xc140; |
} else { |
jis -= 0x8140; |
} |
msb = (jis & 0xff00) >> 8; |
lsb = jis & 0xff; |
prod = msb * 0xc0 + lsb; |
this.binary.append(toBinary(prod, 0x1000)); |
infoSpace(prod); |
} |
break; |
case BINARY: |
/* Byte mode */ |
/* Mode indicator */ |
switch (version) { |
case 2: |
this.binary.append("10"); |
break; |
case 3: |
this.binary.append("010"); |
break; |
} |
/* Character count indicator */ |
this.binary.append(toBinary(blockLength, 2 << version)); /* version = 2..3 */ |
info("BYTE (" + blockLength + ") "); |
/* Character representation */ |
for (i = 0; i < blockLength; i++) { |
final int lbyte = this.content.charAt(position + i); |
this.binary.append(toBinary(lbyte, 0x80)); |
infoSpace(lbyte); |
} |
break; |
case ALPHANUM: |
/* Alphanumeric mode */ |
/* Mode indicator */ |
switch (version) { |
case 1: |
this.binary.append("1"); |
break; |
case 2: |
this.binary.append("01"); |
break; |
case 3: |
this.binary.append("001"); |
break; |
} |
/* Character count indicator */ |
this.binary.append(toBinary(blockLength, 2 << version)); /* version = 1..3 */ |
info("ALPH (" + blockLength + ") "); |
/* Character representation */ |
i = 0; |
while (i < blockLength) { |
first = positionOf(this.content.charAt(position + i), RHODIUM); |
count = 1; |
prod = first; |
if (i + 1 < blockLength) { |
if (this.inputMode[position + i + 1] == qrMode.ALPHANUM) { |
second = positionOf(this.content.charAt(position + i + 1), RHODIUM); |
count = 2; |
prod = first * 45 + second; |
} |
} |
this.binary.append(toBinary(prod, 1 << 5 * count)); /* count = 1..2 */ |
infoSpace(prod); |
i += 2; |
} |
break; |
case NUMERIC: |
/* Numeric mode */ |
/* Mode indicator */ |
switch (version) { |
case 1: |
this.binary.append("0"); |
break; |
case 2: |
this.binary.append("00"); |
break; |
case 3: |
this.binary.append("000"); |
break; |
} |
/* Character count indicator */ |
this.binary.append(toBinary(blockLength, 4 << version)); /* version = 0..3 */ |
info("NUMB (" + blockLength + ") "); |
/* Character representation */ |
i = 0; |
while (i < blockLength) { |
first = Character.getNumericValue(this.content.charAt(position + i)); |
count = 1; |
prod = first; |
if (i + 1 < blockLength) { |
if (this.inputMode[position + i + 1] == qrMode.NUMERIC) { |
second = Character.getNumericValue(this.content.charAt(position + i + 1)); |
count = 2; |
prod = prod * 10 + second; |
} |
} |
if (i + 2 < blockLength) { |
if (this.inputMode[position + i + 2] == qrMode.NUMERIC) { |
third = Character.getNumericValue(this.content.charAt(position + i + 2)); |
count = 3; |
prod = prod * 10 + third; |
} |
} |
this.binary.append(toBinary(prod, 1 << 3 * count)); /* count = 1..3 */ |
infoSpace(prod); |
i += 3; |
} |
break; |
} |
position += blockLength; |
} while (position < this.content.length() - 1); |
/* Add terminator */ |
switch (version) { |
case 0: |
this.binary.append("000"); |
break; |
case 1: |
if (this.binary.length() < 37) { |
this.binary.append("00000"); |
} |
break; |
case 2: |
if (this.binary.length() < 81) { |
this.binary.append("0000000"); |
} |
break; |
case 3: |
if (this.binary.length() < 125) { |
this.binary.append("000000000"); |
} |
break; |
} |
infoLine(); |
} |
private void generateM1Symbol() { |
int i, latch; |
int bits_total, bits_left, remainder; |
int data_codewords, ecc_codewords; |
final int[] data_blocks = new int[4]; |
final int[] ecc_blocks = new int[3]; |
final ReedSolomon rs = new ReedSolomon(); |
bits_total = 20; |
latch = 0; |
/* Manage last (4-bit) block */ |
bits_left = bits_total - this.binary.length(); |
if (bits_left <= 4) { |
for (i = 0; i < bits_left; i++) { |
this.binary.append("0"); |
} |
latch = 1; |
} |
if (latch == 0) { |
/* Complete current byte */ |
remainder = 8 - this.binary.length() % 8; |
if (remainder == 8) { |
remainder = 0; |
} |
for (i = 0; i < remainder; i++) { |
this.binary.append("0"); |
} |
/* Add padding */ |
bits_left = bits_total - this.binary.length(); |
if (bits_left > 4) { |
remainder = (bits_left - 4) / 8; |
for (i = 0; i < remainder; i++) { |
if ((i & 1) != 0) { |
this.binary.append("00010001"); |
} else { |
this.binary.append("11101100"); |
} |
} |
} |
this.binary.append("0000"); |
} |
data_codewords = 3; |
ecc_codewords = 2; |
/* Copy data into codewords */ |
for (i = 0; i < data_codewords - 1; i++) { |
data_blocks[i] = 0; |
if (this.binary.charAt(i * 8) == '1') { |
data_blocks[i] += 0x80; |
} |
if (this.binary.charAt(i * 8 + 1) == '1') { |
data_blocks[i] += 0x40; |
} |
if (this.binary.charAt(i * 8 + 2) == '1') { |
data_blocks[i] += 0x20; |
} |
if (this.binary.charAt(i * 8 + 3) == '1') { |
data_blocks[i] += 0x10; |
} |
if (this.binary.charAt(i * 8 + 4) == '1') { |
data_blocks[i] += 0x08; |
} |
if (this.binary.charAt(i * 8 + 5) == '1') { |
data_blocks[i] += 0x04; |
} |
if (this.binary.charAt(i * 8 + 6) == '1') { |
data_blocks[i] += 0x02; |
} |
if (this.binary.charAt(i * 8 + 7) == '1') { |
data_blocks[i] += 0x01; |
} |
} |
data_blocks[2] = 0; |
if (this.binary.charAt(16) == '1') { |
data_blocks[2] += 0x08; |
} |
if (this.binary.charAt(17) == '1') { |
data_blocks[2] += 0x04; |
} |
if (this.binary.charAt(18) == '1') { |
data_blocks[2] += 0x02; |
} |
if (this.binary.charAt(19) == '1') { |
data_blocks[2] += 0x01; |
} |
info("Codewords: "); |
for (i = 0; i < data_codewords; i++) { |
infoSpace(data_blocks[i]); |
} |
infoLine(); |
/* Calculate Reed-Solomon error codewords */ |
rs.init_gf(0x11d); |
rs.init_code(ecc_codewords, 0); |
rs.encode(data_codewords, data_blocks); |
for (i = 0; i < ecc_codewords; i++) { |
ecc_blocks[i] = rs.getResult(i); |
} |
/* Add Reed-Solomon codewords to binary data */ |
for (i = 0; i < ecc_codewords; i++) { |
this.binary.append(toBinary(ecc_blocks[ecc_codewords - i - 1], 0x80)); |
} |
} |
private void generateM2Symbol(final EccMode ecc_mode) { |
int i; |
int bits_total, bits_left, remainder; |
int data_codewords, ecc_codewords; |
final int[] data_blocks = new int[6]; |
final int[] ecc_blocks = new int[7]; |
final ReedSolomon rs = new ReedSolomon(); |
bits_total = 40; // ecc_mode == EccMode.L |
if (ecc_mode == EccMode.M) { |
bits_total = 32; |
} |
/* Complete current byte */ |
remainder = 8 - this.binary.length() % 8; |
if (remainder == 8) { |
remainder = 0; |
} |
for (i = 0; i < remainder; i++) { |
this.binary.append("0"); |
} |
/* Add padding */ |
bits_left = bits_total - this.binary.length(); |
remainder = bits_left / 8; |
for (i = 0; i < remainder; i++) { |
if ((i & 1) != 0) { |
this.binary.append("00010001"); |
} else { |
this.binary.append("11101100"); |
} |
} |
data_codewords = 5; |
ecc_codewords = 5; // ecc_mode == EccMode.L |
if (ecc_mode == EccMode.M) { |
data_codewords = 4; |
ecc_codewords = 6; |
} |
/* Copy data into codewords */ |
for (i = 0; i < data_codewords; i++) { |
data_blocks[i] = 0; |
if (this.binary.charAt(i * 8) == '1') { |
data_blocks[i] += 0x80; |
} |
if (this.binary.charAt(i * 8 + 1) == '1') { |
data_blocks[i] += 0x40; |
} |
if (this.binary.charAt(i * 8 + 2) == '1') { |
data_blocks[i] += 0x20; |
} |
if (this.binary.charAt(i * 8 + 3) == '1') { |
data_blocks[i] += 0x10; |
} |
if (this.binary.charAt(i * 8 + 4) == '1') { |
data_blocks[i] += 0x08; |
} |
if (this.binary.charAt(i * 8 + 5) == '1') { |
data_blocks[i] += 0x04; |
} |
if (this.binary.charAt(i * 8 + 6) == '1') { |
data_blocks[i] += 0x02; |
} |
if (this.binary.charAt(i * 8 + 7) == '1') { |
data_blocks[i] += 0x01; |
} |
} |
info("Codewords: "); |
for (i = 0; i < data_codewords; i++) { |
infoSpace(data_blocks[i]); |
} |
infoLine(); |
/* Calculate Reed-Solomon error codewords */ |
rs.init_gf(0x11d); |
rs.init_code(ecc_codewords, 0); |
rs.encode(data_codewords, data_blocks); |
for (i = 0; i < ecc_codewords; i++) { |
ecc_blocks[i] = rs.getResult(i); |
} |
/* Add Reed-Solomon codewords to binary data */ |
for (i = 0; i < ecc_codewords; i++) { |
this.binary.append(toBinary(ecc_blocks[ecc_codewords - i - 1], 0x80)); |
} |
} |
private void generateM3Symbol(final EccMode ecc_mode) { |
int i, latch; |
int bits_total, bits_left, remainder; |
int data_codewords, ecc_codewords; |
final int[] data_blocks = new int[12]; |
final int[] ecc_blocks = new int[12]; |
final ReedSolomon rs = new ReedSolomon(); |
latch = 0; |
bits_total = 84; // ecc_mode == EccMode.L |
if (ecc_mode == EccMode.M) { |
bits_total = 68; |
} |
/* Manage last (4-bit) block */ |
bits_left = bits_total - this.binary.length(); |
if (bits_left <= 4) { |
for (i = 0; i < bits_left; i++) { |
this.binary.append("0"); |
} |
latch = 1; |
} |
if (latch == 0) { |
/* Complete current byte */ |
remainder = 8 - this.binary.length() % 8; |
if (remainder == 8) { |
remainder = 0; |
} |
for (i = 0; i < remainder; i++) { |
this.binary.append("0"); |
} |
/* Add padding */ |
bits_left = bits_total - this.binary.length(); |
if (bits_left > 4) { |
remainder = (bits_left - 4) / 8; |
for (i = 0; i < remainder; i++) { |
if ((i & 1) != 0) { |
this.binary.append("00010001"); |
} else { |
this.binary.append("11101100"); |
} |
} |
} |
this.binary.append("0000"); |
} |
data_codewords = 11; |
ecc_codewords = 6; // ecc_mode == EccMode.L |
if (ecc_mode == EccMode.M) { |
data_codewords = 9; |
ecc_codewords = 8; |
} |
/* Copy data into codewords */ |
for (i = 0; i < data_codewords - 1; i++) { |
data_blocks[i] = 0; |
if (this.binary.charAt(i * 8) == '1') { |
data_blocks[i] += 0x80; |
} |
if (this.binary.charAt(i * 8 + 1) == '1') { |
data_blocks[i] += 0x40; |
} |
if (this.binary.charAt(i * 8 + 2) == '1') { |
data_blocks[i] += 0x20; |
} |
if (this.binary.charAt(i * 8 + 3) == '1') { |
data_blocks[i] += 0x10; |
} |
if (this.binary.charAt(i * 8 + 4) == '1') { |
data_blocks[i] += 0x08; |
} |
if (this.binary.charAt(i * 8 + 5) == '1') { |
data_blocks[i] += 0x04; |
} |
if (this.binary.charAt(i * 8 + 6) == '1') { |
data_blocks[i] += 0x02; |
} |
if (this.binary.charAt(i * 8 + 7) == '1') { |
data_blocks[i] += 0x01; |
} |
} |
if (ecc_mode == EccMode.L) { |
data_blocks[10] = 0; |
if (this.binary.charAt(80) == '1') { |
data_blocks[10] += 0x08; |
} |
if (this.binary.charAt(81) == '1') { |
data_blocks[10] += 0x04; |
} |
if (this.binary.charAt(82) == '1') { |
data_blocks[10] += 0x02; |
} |
if (this.binary.charAt(83) == '1') { |
data_blocks[10] += 0x01; |
} |
} |
if (ecc_mode == EccMode.M) { |
data_blocks[8] = 0; |
if (this.binary.charAt(64) == '1') { |
data_blocks[8] += 0x08; |
} |
if (this.binary.charAt(65) == '1') { |
data_blocks[8] += 0x04; |
} |
if (this.binary.charAt(66) == '1') { |
data_blocks[8] += 0x02; |
} |
if (this.binary.charAt(67) == '1') { |
data_blocks[8] += 0x01; |
} |
} |
info("Codewords: "); |
for (i = 0; i < data_codewords; i++) { |
infoSpace(data_blocks[i]); |
} |
infoLine(); |
/* Calculate Reed-Solomon error codewords */ |
rs.init_gf(0x11d); |
rs.init_code(ecc_codewords, 0); |
rs.encode(data_codewords, data_blocks); |
for (i = 0; i < ecc_codewords; i++) { |
ecc_blocks[i] = rs.getResult(i); |
} |
/* Add Reed-Solomon codewords to binary data */ |
for (i = 0; i < ecc_codewords; i++) { |
this.binary.append(toBinary(ecc_blocks[ecc_codewords - i - 1], 0x80)); |
} |
} |
private void generateM4Symbol(final EccMode ecc_mode) { |
int i; |
int bits_total, bits_left, remainder; |
int data_codewords, ecc_codewords; |
final int[] data_blocks = new int[17]; |
final int[] ecc_blocks = new int[15]; |
final ReedSolomon rs = new ReedSolomon(); |
bits_total = 128; // ecc_mode == EccMode.L |
if (ecc_mode == EccMode.M) { |
bits_total = 112; |
} |
if (ecc_mode == EccMode.Q) { |
bits_total = 80; |
} |
/* Complete current byte */ |
remainder = 8 - this.binary.length() % 8; |
if (remainder == 8) { |
remainder = 0; |
} |
for (i = 0; i < remainder; i++) { |
this.binary.append("0"); |
} |
/* Add padding */ |
bits_left = bits_total - this.binary.length(); |
remainder = bits_left / 8; |
for (i = 0; i < remainder; i++) { |
if ((i & 1) != 0) { |
this.binary.append("00010001"); |
} else { |
this.binary.append("11101100"); |
} |
} |
data_codewords = 16; |
ecc_codewords = 8; // ecc_mode == EccMode.L |
if (ecc_mode == EccMode.M) { |
data_codewords = 14; |
ecc_codewords = 10; |
} |
if (ecc_mode == EccMode.Q) { |
data_codewords = 10; |
ecc_codewords = 14; |
} |
/* Copy data into codewords */ |
for (i = 0; i < data_codewords; i++) { |
data_blocks[i] = 0; |
if (this.binary.charAt(i * 8) == '1') { |
data_blocks[i] += 0x80; |
} |
if (this.binary.charAt(i * 8 + 1) == '1') { |
data_blocks[i] += 0x40; |
} |
if (this.binary.charAt(i * 8 + 2) == '1') { |
data_blocks[i] += 0x20; |
} |
if (this.binary.charAt(i * 8 + 3) == '1') { |
data_blocks[i] += 0x10; |
} |
if (this.binary.charAt(i * 8 + 4) == '1') { |
data_blocks[i] += 0x08; |
} |
if (this.binary.charAt(i * 8 + 5) == '1') { |
data_blocks[i] += 0x04; |
} |
if (this.binary.charAt(i * 8 + 6) == '1') { |
data_blocks[i] += 0x02; |
} |
if (this.binary.charAt(i * 8 + 7) == '1') { |
data_blocks[i] += 0x01; |
} |
} |
info("Codewords: "); |
for (i = 0; i < data_codewords; i++) { |
infoSpace(data_blocks[i]); |
} |
infoLine(); |
/* Calculate Reed-Solomon error codewords */ |
rs.init_gf(0x11d); |
rs.init_code(ecc_codewords, 0); |
rs.encode(data_codewords, data_blocks); |
for (i = 0; i < ecc_codewords; i++) { |
ecc_blocks[i] = rs.getResult(i); |
} |
/* Add Reed-Solomon codewords to binary data */ |
for (i = 0; i < ecc_codewords; i++) { |
this.binary.append(toBinary(ecc_blocks[ecc_codewords - i - 1], 0x80)); |
} |
} |
private void setupBitGrid(final int size) { |
int i, toggle = 1; |
/* Add timing patterns */ |
for (i = 0; i < size; i++) { |
if (toggle == 1) { |
this.grid[i] = 0x21; |
this.grid[i * size] = 0x21; |
toggle = 0; |
} else { |
this.grid[i] = 0x20; |
this.grid[i * size] = 0x20; |
toggle = 1; |
} |
} |
/* Add finder patterns */ |
placeFinderPattern(size, 0, 0); |
/* Add separators */ |
for (i = 0; i < 7; i++) { |
this.grid[7 * size + i] = 0x10; |
this.grid[i * size + 7] = 0x10; |
} |
this.grid[7 * size + 7] = 0x10; |
/* Reserve space for format information */ |
for (i = 0; i < 8; i++) { |
this.grid[8 * size + i] += 0x20; |
this.grid[i * size + 8] += 0x20; |
} |
this.grid[8 * size + 8] += 0x20; |
} |
private void placeFinderPattern(final int size, final int x, final int y) { |
int xp, yp; |
final int[] finder = { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 }; |
for (xp = 0; xp < 7; xp++) { |
for (yp = 0; yp < 7; yp++) { |
if (finder[xp + 7 * yp] == 1) { |
this.grid[(yp + y) * size + xp + x] = 0x11; |
} else { |
this.grid[(yp + y) * size + xp + x] = 0x10; |
} |
} |
} |
} |
private void populateBitGrid(final int size) { |
boolean goingUp = true; |
int row = 0; /* right hand side */ |
int i, n, x, y; |
n = this.binary.length(); |
y = size - 1; |
i = 0; |
do { |
x = size - 2 - row * 2; |
if ((this.grid[y * size + x + 1] & 0xf0) == 0) { |
if (this.binary.charAt(i) == '1') { |
this.grid[y * size + x + 1] = 0x01; |
} else { |
this.grid[y * size + x + 1] = 0x00; |
} |
i++; |
} |
if (i < n) { |
if ((this.grid[y * size + x] & 0xf0) == 0) { |
if (this.binary.charAt(i) == '1') { |
this.grid[y * size + x] = 0x01; |
} else { |
this.grid[y * size + x] = 0x00; |
} |
i++; |
} |
} |
if (goingUp) { |
y--; |
} else { |
y++; |
} |
if (y == 0) { |
/* reached the top */ |
row++; |
y = 1; |
goingUp = false; |
} |
if (y == size) { |
/* reached the bottom */ |
row++; |
y = size - 1; |
goingUp = true; |
} |
} while (i < n); |
} |
private int applyBitmask(final int size) { |
int x, y; |
int p; |
int local_pattern; |
final int[] value = new int[8]; |
int best_val, best_pattern; |
int bit; |
final int[] mask = new int[size * size]; |
this.eval = new int[size * size]; |
/* Perform data masking */ |
for (x = 0; x < size; x++) { |
for (y = 0; y < size; y++) { |
mask[y * size + x] = 0x00; |
if ((this.grid[y * size + x] & 0xf0) == 0) { |
if ((y & 1) == 0) { |
mask[y * size + x] += 0x01; |
} |
if ((y / 2 + x / 3 & 1) == 0) { |
mask[y * size + x] += 0x02; |
} |
if (((y * x & 1) + y * x % 3 & 1) == 0) { |
mask[y * size + x] += 0x04; |
} |
if (((y + x & 1) + y * x % 3 & 1) == 0) { |
mask[y * size + x] += 0x08; |
} |
} |
} |
} |
for (x = 0; x < size; x++) { |
for (y = 0; y < size; y++) { |
if ((this.grid[y * size + x] & 0x01) != 0) { |
p = 0xff; |
} else { |
p = 0x00; |
} |
this.eval[y * size + x] = mask[y * size + x] ^ p; |
} |
} |
/* Evaluate result */ |
for (local_pattern = 0; local_pattern < 4; local_pattern++) { |
value[local_pattern] = evaluateBitmask(size, local_pattern); |
} |
best_pattern = 0; |
best_val = value[0]; |
for (local_pattern = 1; local_pattern < 4; local_pattern++) { |
if (value[local_pattern] > best_val) { |
best_pattern = local_pattern; |
best_val = value[local_pattern]; |
} |
} |
/* Apply mask */ |
for (x = 0; x < size; x++) { |
for (y = 0; y < size; y++) { |
bit = 0; |
switch (best_pattern) { |
case 0: |
if ((mask[y * size + x] & 0x01) != 0) { |
bit = 1; |
} |
break; |
case 1: |
if ((mask[y * size + x] & 0x02) != 0) { |
bit = 1; |
} |
break; |
case 2: |
if ((mask[y * size + x] & 0x04) != 0) { |
bit = 1; |
} |
break; |
case 3: |
if ((mask[y * size + x] & 0x08) != 0) { |
bit = 1; |
} |
break; |
} |
if (bit == 1) { |
if ((this.grid[y * size + x] & 0x01) != 0) { |
this.grid[y * size + x] = 0x00; |
} else { |
this.grid[y * size + x] = 0x01; |
} |
} |
} |
} |
return best_pattern; |
} |
private int evaluateBitmask(final int size, final int pattern) { |
int sum1, sum2, i, filter = 0, retval; |
switch (pattern) { |
case 0: |
filter = 0x01; |
break; |
case 1: |
filter = 0x02; |
break; |
case 2: |
filter = 0x04; |
break; |
case 3: |
filter = 0x08; |
break; |
} |
sum1 = 0; |
sum2 = 0; |
for (i = 1; i < size; i++) { |
if ((this.eval[i * size + size - 1] & filter) != 0) { |
sum1++; |
} |
if ((this.eval[(size - 1) * size + i] & filter) != 0) { |
sum2++; |
} |
} |
if (sum1 <= sum2) { |
retval = sum1 * 16 + sum2; |
} else { |
retval = sum2 * 16 + sum1; |
} |
return retval; |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Pharmacode2Track.java |
---|
New file |
0,0 → 1,117 |
/* |
* Copyright 2014 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
import java.awt.geom.Rectangle2D; |
/** |
* Implements the Two-Track Pharmacode bar code symbology. <br> |
* Pharmacode Two-Track is an alternative system to Pharmacode One-Track used for the identification |
* of pharmaceuticals. The symbology is able to encode whole numbers between 4 and 64570080. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class Pharmacode2Track extends Symbol { |
@Override |
protected void encode() { |
int i, tester = 0; |
String inter, dest; |
if (this.content.length() > 8) { |
throw new OkapiException("Input too long"); |
} |
if (!this.content.matches("[0-9]+")) { |
throw new OkapiException("Invalid characters in data"); |
} |
for (i = 0; i < this.content.length(); i++) { |
tester *= 10; |
tester += Character.getNumericValue(this.content.charAt(i)); |
} |
if (tester < 4 || tester > 64570080) { |
throw new OkapiException("Data out of range"); |
} |
inter = ""; |
do { |
switch (tester % 3) { |
case 0: |
inter += "F"; |
tester = (tester - 3) / 3; |
break; |
case 1: |
inter += "D"; |
tester = (tester - 1) / 3; |
break; |
case 2: |
inter += "A"; |
tester = (tester - 2) / 3; |
break; |
} |
} while (tester != 0); |
dest = ""; |
for (i = inter.length() - 1; i >= 0; i--) { |
dest += inter.charAt(i); |
} |
infoLine("Encoding: " + dest); |
this.readable = ""; |
this.pattern = new String[1]; |
this.pattern[0] = dest; |
this.row_count = 1; |
this.row_height = new int[1]; |
this.row_height[0] = -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++) { |
switch (this.pattern[0].charAt(xBlock)) { |
case 'A': |
y = 0; |
h = this.default_height / 2; |
break; |
case 'D': |
y = this.default_height / 2; |
h = this.default_height / 2; |
break; |
case 'F': |
y = 0; |
h = this.default_height; |
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() * 2; |
this.symbol_height = this.default_height; |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Code11.java |
---|
New file |
0,0 → 1,215 |
/* |
* Copyright 2014 Robin Stuart, Daniel Gredler |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
import static uk.org.okapibarcode.util.Arrays.positionOf; |
/** |
* <p> |
* Implements Code 11 bar code symbology. |
* |
* <p> |
* Code 11 can encode any length string consisting of the digits 0-9 and the dash character (-). One |
* or two modulo-11 check digits are calculated. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
* @author Daniel Gredler |
*/ |
public class Code11 extends Symbol { |
private static final String[] CODE_11_TABLE = { "111121", "211121", "121121", "221111", "112121", "212111", "122111", "111221", "211211", "211111", "112111" }; |
private static final char[] CHARACTER_SET = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-' }; |
/** Ratio of wide bar width to narrow bar width. */ |
private double moduleWidthRatio = 2; |
/** The number of check digits to calculate ({@code 1} or {@code 2}). */ |
private int checkDigitCount = 2; |
/** Optional start delimiter to be shown in the human-readable text. */ |
private Character startDelimiter; |
/** Optional stop delimiter to be shown in the human-readable text. */ |
private Character stopDelimiter; |
/** |
* Sets the ratio of wide bar width to narrow bar width. Valid values are usually between |
* {@code 2} and {@code 3}. The default value is {@code 2}. |
* |
* @param moduleWidthRatio the ratio of wide bar width to narrow bar width |
*/ |
public void setModuleWidthRatio(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; |
} |
/** |
* Sets the number of check digits to calculate ({@code 1} or {@code 2}). The default value is |
* {@code 2}. |
* |
* @param checkDigitCount the number of check digits to calculate |
*/ |
public void setCheckDigitCount(final int checkDigitCount) { |
if (checkDigitCount < 1 || checkDigitCount > 2) { |
throw new IllegalArgumentException("Check digit count must be 1 or 2."); |
} |
this.checkDigitCount = checkDigitCount; |
} |
/** |
* Returns the number of check digits to calculate (1 or 2). |
* |
* @return the number of check digits to calculate |
*/ |
public int getCheckDigitCount() { |
return this.checkDigitCount; |
} |
/** |
* Sets an optional start delimiter to be shown in the human-readable text (defaults to |
* <code>null</code>). |
* |
* @param startDelimiter an optional start delimiter to be shown in the human-readable text |
*/ |
public void setStartDelimiter(final Character startDelimiter) { |
this.startDelimiter = startDelimiter; |
} |
/** |
* Returns the optional start delimiter to be shown in the human-readable text. |
* |
* @return the optional start delimiter to be shown in the human-readable text |
*/ |
public Character getStartDelimiter() { |
return this.startDelimiter; |
} |
/** |
* Sets an optional stop delimiter to be shown in the human-readable text (defaults to |
* <code>null</code>). |
* |
* @param stopDelimiter an optional stop delimiter to be shown in the human-readable text |
*/ |
public void setStopDelimiter(final Character stopDelimiter) { |
this.stopDelimiter = stopDelimiter; |
} |
/** |
* Returns the optional stop delimiter to be shown in the human-readable text. |
* |
* @return the optional stop delimiter to be shown in the human-readable text |
*/ |
public Character getStopDelimiter() { |
return this.stopDelimiter; |
} |
/** {@inheritDoc} */ |
@Override |
protected void encode() { |
if (!this.content.matches("[0-9-]+")) { |
throw new OkapiException("Invalid characters in input"); |
} |
String horizontalSpacing = "112211"; |
String humanReadable = this.content; |
final int length = this.content.length(); |
final int[] weight = new int[length + 1]; |
for (int i = 0; i < length; i++) { |
final char c = this.content.charAt(i); |
weight[i] = positionOf(c, CHARACTER_SET); |
horizontalSpacing += CODE_11_TABLE[weight[i]]; |
} |
final int checkDigitC = getCheckDigitC(weight, length); |
horizontalSpacing += CODE_11_TABLE[checkDigitC]; |
humanReadable += CHARACTER_SET[checkDigitC]; |
infoLine("Check Digit C: " + checkDigitC); |
if (this.checkDigitCount == 2) { |
weight[length] = checkDigitC; |
final int checkDigitK = getCheckDigitK(weight, length + 1); |
horizontalSpacing += CODE_11_TABLE[checkDigitK]; |
humanReadable += CHARACTER_SET[checkDigitK]; |
infoLine("Check Digit K: " + checkDigitK); |
} |
horizontalSpacing += "112211"; |
this.readable = humanReadable; |
if (this.startDelimiter != null) { |
this.readable = this.startDelimiter + this.readable; |
} |
if (this.stopDelimiter != null) { |
this.readable = this.readable + this.stopDelimiter; |
} |
this.pattern = new String[] { horizontalSpacing }; |
this.row_count = 1; |
this.row_height = new int[] { -1 }; |
} |
private static int getCheckDigitC(final int[] weight, final int length) { |
int countC = 0; |
int weightC = 1; |
for (int i = length - 1; i >= 0; i--) { |
countC += weightC * weight[i]; |
weightC++; |
if (weightC > 10) { |
weightC = 1; |
} |
} |
return countC % 11; |
} |
private static int getCheckDigitK(final int[] weight, final int length) { |
int countK = 0; |
int weightK = 1; |
for (int i = length - 1; i >= 0; i--) { |
countK += weightK * weight[i]; |
weightK++; |
if (weightK > 9) { |
weightK = 1; |
} |
} |
return countK % 11; |
} |
/** {@inheritDoc} */ |
@Override |
protected double getModuleWidth(final int originalWidth) { |
if (originalWidth == 1) { |
return 1; |
} else { |
return this.moduleWidthRatio; |
} |
} |
/** {@inheritDoc} */ |
@Override |
protected int[] getCodewords() { |
return getPatternAsCodewords(6); |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/QrCode.java |
---|
New file |
0,0 → 1,1692 |
/* |
* Copyright 2014 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
import static uk.org.okapibarcode.util.Arrays.positionOf; |
import java.nio.CharBuffer; |
import java.nio.charset.Charset; |
/** |
* <p> |
* Implements QR Code bar code symbology According to ISO/IEC 18004:2015. |
* |
* <p> |
* The maximum capacity of a (version 40) QR Code symbol is 7089 numeric digits, 4296 alphanumeric |
* characters or 2953 bytes of data. QR Code symbols can also be used to encode GS1 data. QR Code |
* symbols can encode characters in the Latin-1 set and Kanji characters which are members of the |
* Shift-JIS encoding scheme. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class QrCode extends Symbol { |
public enum EccLevel { |
L, M, Q, H |
} |
private enum QrMode { |
NULL, KANJI, BINARY, ALPHANUM, NUMERIC |
} |
/* Table 5 - Encoding/Decoding table for Alphanumeric mode */ |
private static final char[] RHODIUM = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', |
'V', 'W', 'X', 'Y', 'Z', ' ', '$', '%', '*', '+', '-', '.', '/', ':' }; |
private static final int[] QR_DATA_CODEWORDS_L = { 19, 34, 55, 80, 108, 136, 156, 194, 232, 274, 324, 370, 428, 461, 523, 589, 647, 721, 795, 861, 932, 1006, 1094, 1174, 1276, 1370, 1468, 1531, |
1631, 1735, 1843, 1955, 2071, 2191, 2306, 2434, 2566, 2702, 2812, 2956 }; |
private static final int[] QR_DATA_CODEWORDS_M = { 16, 28, 44, 64, 86, 108, 124, 154, 182, 216, 254, 290, 334, 365, 415, 453, 507, 563, 627, 669, 714, 782, 860, 914, 1000, 1062, 1128, 1193, 1267, |
1373, 1455, 1541, 1631, 1725, 1812, 1914, 1992, 2102, 2216, 2334 }; |
private static final int[] QR_DATA_CODEWORDS_Q = { 13, 22, 34, 48, 62, 76, 88, 110, 132, 154, 180, 206, 244, 261, 295, 325, 367, 397, 445, 485, 512, 568, 614, 664, 718, 754, 808, 871, 911, 985, |
1033, 1115, 1171, 1231, 1286, 1354, 1426, 1502, 1582, 1666 }; |
private static final int[] QR_DATA_CODEWORDS_H = { 9, 16, 26, 36, 46, 60, 66, 86, 100, 122, 140, 158, 180, 197, 223, 253, 283, 313, 341, 385, 406, 442, 464, 514, 538, 596, 628, 661, 701, 745, 793, |
845, 901, 961, 986, 1054, 1096, 1142, 1222, 1276 }; |
private static final int[] QR_BLOCKS_L = { 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25 }; |
private static final int[] QR_BLOCKS_M = { 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49 }; |
private static final int[] QR_BLOCKS_Q = { 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68 }; |
private static final int[] QR_BLOCKS_H = { 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81 }; |
private static final int[] QR_TOTAL_CODEWORDS = { 26, 44, 70, 100, 134, 172, 196, 242, 292, 346, 404, 466, 532, 581, 655, 733, 815, 901, 991, 1085, 1156, 1258, 1364, 1474, 1588, 1706, 1828, 1921, |
2051, 2185, 2323, 2465, 2611, 2761, 2876, 3034, 3196, 3362, 3532, 3706 }; |
private static final int[] QR_SIZES = { 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 65, 69, 73, 77, 81, 85, 89, 93, 97, 101, 105, 109, 113, 117, 121, 125, 129, 133, 137, 141, 145, 149, 153, 157, |
161, 165, 169, 173, 177 }; |
private static final int[] QR_ALIGN_LOOPSIZE = { 0, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7 }; |
private static final int[] QR_TABLE_E1 = { 6, 18, 0, 0, 0, 0, 0, 6, 22, 0, 0, 0, 0, 0, 6, 26, 0, 0, 0, 0, 0, 6, 30, 0, 0, 0, 0, 0, 6, 34, 0, 0, 0, 0, 0, 6, 22, 38, 0, 0, 0, 0, 6, 24, 42, 0, 0, 0, |
0, 6, 26, 46, 0, 0, 0, 0, 6, 28, 50, 0, 0, 0, 0, 6, 30, 54, 0, 0, 0, 0, 6, 32, 58, 0, 0, 0, 0, 6, 34, 62, 0, 0, 0, 0, 6, 26, 46, 66, 0, 0, 0, 6, 26, 48, 70, 0, 0, 0, 6, 26, 50, 74, 0, 0, |
0, 6, 30, 54, 78, 0, 0, 0, 6, 30, 56, 82, 0, 0, 0, 6, 30, 58, 86, 0, 0, 0, 6, 34, 62, 90, 0, 0, 0, 6, 28, 50, 72, 94, 0, 0, 6, 26, 50, 74, 98, 0, 0, 6, 30, 54, 78, 102, 0, 0, 6, 28, 54, |
80, 106, 0, 0, 6, 32, 58, 84, 110, 0, 0, 6, 30, 58, 86, 114, 0, 0, 6, 34, 62, 90, 118, 0, 0, 6, 26, 50, 74, 98, 122, 0, 6, 30, 54, 78, 102, 126, 0, 6, 26, 52, 78, 104, 130, 0, 6, 30, 56, |
82, 108, 134, 0, 6, 34, 60, 86, 112, 138, 0, 6, 30, 58, 86, 114, 142, 0, 6, 34, 62, 90, 118, 146, 0, 6, 30, 54, 78, 102, 126, 150, 6, 24, 50, 76, 102, 128, 154, 6, 28, 54, 80, 106, 132, |
158, 6, 32, 58, 84, 110, 136, 162, 6, 26, 54, 82, 110, 138, 166, 6, 30, 58, 86, 114, 142, 170 }; |
private static final int[] QR_ANNEX_C = { |
/* Format information bit sequences */ |
0x5412, 0x5125, 0x5e7c, 0x5b4b, 0x45f9, 0x40ce, 0x4f97, 0x4aa0, 0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976, 0x1689, 0x13be, 0x1ce7, 0x19d0, 0x0762, 0x0255, 0x0d0c, |
0x083b, 0x355f, 0x3068, 0x3f31, 0x3a06, 0x24b4, 0x2183, 0x2eda, 0x2bed }; |
private static final int[] QR_ANNEX_D = { |
/* Version information bit sequences */ |
0x07c94, 0x085bc, 0x09a99, 0x0a4d3, 0x0bbf6, 0x0c762, 0x0d847, 0x0e60d, 0x0f928, 0x10b78, 0x1145d, 0x12a17, 0x13532, 0x149a6, 0x15683, 0x168c9, 0x177ec, 0x18ec4, 0x191e1, 0x1afab, 0x1b08e, |
0x1cc1a, 0x1d33f, 0x1ed75, 0x1f250, 0x209d5, 0x216f0, 0x228ba, 0x2379f, 0x24b0b, 0x2542e, 0x26a64, 0x27541, 0x28c69 }; |
private int preferredVersion; |
private EccLevel preferredEccLevel = EccLevel.L; |
/** |
* Sets the preferred symbol size / version. This value may be ignored if the data string is too |
* large to fit into the specified symbol. Input values correspond to symbol sizes as shown in |
* the following table: |
* |
* <table summary="Available QR Code sizes"> |
* <tbody> |
* <tr> |
* <th>Input</th> |
* <th>Symbol Size</th> |
* <th>Input</th> |
* <th>Symbol Size</th> |
* </tr> |
* <tr> |
* <td>1</td> |
* <td>21 x 21</td> |
* <td>21</td> |
* <td>101 x 101</td> |
* </tr> |
* <tr> |
* <td>2</td> |
* <td>25 x 25</td> |
* <td>22</td> |
* <td>105 x 105</td> |
* </tr> |
* <tr> |
* <td>3</td> |
* <td>29 x 29</td> |
* <td>23</td> |
* <td>109 x 109</td> |
* </tr> |
* <tr> |
* <td>4</td> |
* <td>33 x 33</td> |
* <td>24</td> |
* <td>113 x 113</td> |
* </tr> |
* <tr> |
* <td>5</td> |
* <td>37 x 37</td> |
* <td>25</td> |
* <td>117 x 117</td> |
* </tr> |
* <tr> |
* <td>6</td> |
* <td>41 x 41</td> |
* <td>26</td> |
* <td>121 x 121</td> |
* </tr> |
* <tr> |
* <td>7</td> |
* <td>45 x 45</td> |
* <td>27</td> |
* <td>125 x 125</td> |
* </tr> |
* <tr> |
* <td>8</td> |
* <td>49 x 49</td> |
* <td>28</td> |
* <td>129 x 129</td> |
* </tr> |
* <tr> |
* <td>9</td> |
* <td>53 x 53</td> |
* <td>29</td> |
* <td>133 x 133</td> |
* </tr> |
* <tr> |
* <td>10</td> |
* <td>57 x 57</td> |
* <td>30</td> |
* <td>137 x 137</td> |
* </tr> |
* <tr> |
* <td>11</td> |
* <td>61 x 61</td> |
* <td>31</td> |
* <td>141 x 141</td> |
* </tr> |
* <tr> |
* <td>12</td> |
* <td>65 x 65</td> |
* <td>32</td> |
* <td>145 x 145</td> |
* </tr> |
* <tr> |
* <td>13</td> |
* <td>69 x 69</td> |
* <td>33</td> |
* <td>149 x 149</td> |
* </tr> |
* <tr> |
* <td>14</td> |
* <td>73 x 73</td> |
* <td>34</td> |
* <td>153 x 153</td> |
* </tr> |
* <tr> |
* <td>15</td> |
* <td>77 x 77</td> |
* <td>35</td> |
* <td>157 x 157</td> |
* </tr> |
* <tr> |
* <td>16</td> |
* <td>81 x 81</td> |
* <td>36</td> |
* <td>161 x 161</td> |
* </tr> |
* <tr> |
* <td>17</td> |
* <td>85 x 85</td> |
* <td>37</td> |
* <td>165 x 165</td> |
* </tr> |
* <tr> |
* <td>18</td> |
* <td>89 x 89</td> |
* <td>38</td> |
* <td>169 x 169</td> |
* </tr> |
* <tr> |
* <td>19</td> |
* <td>93 x 93</td> |
* <td>39</td> |
* <td>173 x 173</td> |
* </tr> |
* <tr> |
* <td>20</td> |
* <td>97 x 97</td> |
* <td>40</td> |
* <td>177 x 177</td> |
* </tr> |
* </tbody> |
* </table> |
* |
* @param version the preferred symbol version |
*/ |
public void setPreferredVersion(final int version) { |
this.preferredVersion = version; |
} |
/** |
* Returns the preferred symbol version. |
* |
* @return the preferred symbol version |
*/ |
public int getPreferredVersion() { |
return this.preferredVersion; |
} |
/** |
* Sets the preferred amount of symbol space allocated to error correction. This value may be |
* ignored if there is room for a higher error correction level. Levels are predefined according |
* to the following table: |
* |
* <table summary="QR Code error correction levels"> |
* <tbody> |
* <tr> |
* <th>ECC Level</th> |
* <th>Error Correction Capacity</th> |
* <th>Recovery Capacity</th> |
* </tr> |
* <tr> |
* <td>L (default)</td> |
* <td>Approx 20% of symbol</td> |
* <td>Approx 7%</td> |
* </tr> |
* <tr> |
* <td>M</td> |
* <td>Approx 37% of symbol</td> |
* <td>Approx 15%</td> |
* </tr> |
* <tr> |
* <td>Q</td> |
* <td>Approx 55% of symbol</td> |
* <td>Approx 25%</td> |
* </tr> |
* <tr> |
* <td>H</td> |
* <td>Approx 65% of symbol</td> |
* <td>Approx 30%</td> |
* </tr> |
* </tbody> |
* </table> |
* |
* @param preferredEccLevel the preferred error correction level |
*/ |
public void setPreferredEccLevel(final EccLevel preferredEccLevel) { |
this.preferredEccLevel = preferredEccLevel; |
} |
/** |
* Returns the preferred amount of symbol space allocated to error correction. |
* |
* @return the preferred amount of symbol space allocated to error correction |
*/ |
public EccLevel getPreferredEccLevel() { |
return this.preferredEccLevel; |
} |
@Override |
protected boolean gs1Supported() { |
return true; |
} |
@Override |
protected void encode() { |
int i, j; |
int est_binlen; |
EccLevel ecc_level; |
int max_cw; |
int targetCwCount, version, blocks; |
int size; |
int bitmask; |
final boolean gs1 = this.inputDataType == DataType.GS1; |
eciProcess(); // Get ECI mode |
if (this.eciMode == 20) { |
/* Shift-JIS encoding, use Kanji mode */ |
final Charset c = Charset.forName("Shift_JIS"); |
this.inputData = new int[this.content.length()]; |
for (i = 0; i < this.inputData.length; i++) { |
final CharBuffer buffer = CharBuffer.wrap(this.content, i, i + 1); |
final byte[] bytes = c.encode(buffer).array(); |
final int value = bytes.length == 2 ? (bytes[0] & 0xff) << 8 | bytes[1] & 0xff : bytes[0]; |
this.inputData[i] = value; |
} |
} else { |
/* inputData already initialized in eciProcess() */ |
} |
QrMode[] inputMode = new QrMode[this.inputData.length]; |
defineMode(inputMode, this.inputData); |
est_binlen = getBinaryLength(40, inputMode, this.inputData, gs1, this.eciMode); |
ecc_level = this.preferredEccLevel; |
switch (this.preferredEccLevel) { |
case L: |
default: |
max_cw = 2956; |
break; |
case M: |
max_cw = 2334; |
break; |
case Q: |
max_cw = 1666; |
break; |
case H: |
max_cw = 1276; |
break; |
} |
if (est_binlen > 8 * max_cw) { |
throw new OkapiException("Input too long for selected error correction level"); |
} |
// ZINT NOTE: this block is different from the corresponding block of code in Zint; |
// it is simplified, but the simplification required that the applyOptimisation method |
// be changed to be free of side effects (by putting the optimized mode array into a |
// new array instead of modifying the existing array) |
version = 40; |
for (i = 39; i >= 0; i--) { |
int[] dataCodewords; |
switch (ecc_level) { |
case L: |
default: |
dataCodewords = QR_DATA_CODEWORDS_L; |
break; |
case M: |
dataCodewords = QR_DATA_CODEWORDS_M; |
break; |
case Q: |
dataCodewords = QR_DATA_CODEWORDS_Q; |
break; |
case H: |
dataCodewords = QR_DATA_CODEWORDS_H; |
break; |
} |
final int proposedVersion = i + 1; |
final int proposedBinLen = getBinaryLength(proposedVersion, inputMode, this.inputData, gs1, this.eciMode); |
if (8 * dataCodewords[i] >= proposedBinLen) { |
version = proposedVersion; |
est_binlen = proposedBinLen; |
} |
} |
inputMode = applyOptimisation(version, inputMode); |
// ZINT NOTE: end of block of code that is different |
// TODO: delete this |
// |
// autosize = 40; |
// for (i = 39; i >= 0; i--) { |
// switch (ecc_level) { |
// case L: |
// if ((8 * QR_DATA_CODEWORDS_L[i]) >= est_binlen) { |
// autosize = i + 1; |
// } |
// break; |
// case M: |
// if ((8 * QR_DATA_CODEWORDS_M[i]) >= est_binlen) { |
// autosize = i + 1; |
// } |
// break; |
// case Q: |
// if ((8 * QR_DATA_CODEWORDS_Q[i]) >= est_binlen) { |
// autosize = i + 1; |
// } |
// break; |
// case H: |
// if ((8 * QR_DATA_CODEWORDS_H[i]) >= est_binlen) { |
// autosize = i + 1; |
// } |
// break; |
// } |
// } |
// |
// // Now see if the optimized binary will fit in a smaller symbol. |
// canShrink = true; |
// |
// do { |
// if (autosize == 1) { |
// est_binlen = getBinaryLength(autosize, inputMode, inputData, gs1, eciMode); // TODO: |
// added |
// canShrink = false; |
// } else { |
// est_binlen = getBinaryLength(autosize - 1, inputMode, inputData, gs1, eciMode); |
// |
// switch (ecc_level) { |
// case L: |
// if ((8 * QR_DATA_CODEWORDS_L[autosize - 2]) < est_binlen) { |
// canShrink = false; |
// } |
// break; |
// case M: |
// if ((8 * QR_DATA_CODEWORDS_M[autosize - 2]) < est_binlen) { |
// canShrink = false; |
// } |
// break; |
// case Q: |
// if ((8 * QR_DATA_CODEWORDS_Q[autosize - 2]) < est_binlen) { |
// canShrink = false; |
// } |
// break; |
// case H: |
// if ((8 * QR_DATA_CODEWORDS_H[autosize - 2]) < est_binlen) { |
// canShrink = false; |
// } |
// break; |
// } |
// |
// if (canShrink) { |
// // Optimization worked - data will fit in a smaller symbol |
// autosize--; |
// } else { |
// // Data did not fit in the smaller symbol, revert to original size |
// est_binlen = getBinaryLength(autosize, inputMode, inputData, gs1, eciMode); |
// } |
// } |
// } while (canShrink); |
// |
// version = autosize; |
if (this.preferredVersion >= 1 && this.preferredVersion <= 40) { |
/* |
* If the user has selected a larger symbol than the smallest available, then use the |
* size the user has selected, and re-optimize for this symbol size. |
*/ |
if (this.preferredVersion > version) { |
version = this.preferredVersion; |
est_binlen = getBinaryLength(this.preferredVersion, inputMode, this.inputData, gs1, this.eciMode); |
inputMode = applyOptimisation(version, inputMode); |
} |
if (this.preferredVersion < version) { |
throw new OkapiException("Input too long for selected symbol size"); |
} |
} |
/* Ensure maximum error correction capacity */ |
if (est_binlen <= QR_DATA_CODEWORDS_M[version - 1] * 8) { |
ecc_level = EccLevel.M; |
} |
if (est_binlen <= QR_DATA_CODEWORDS_Q[version - 1] * 8) { |
ecc_level = EccLevel.Q; |
} |
if (est_binlen <= QR_DATA_CODEWORDS_H[version - 1] * 8) { |
ecc_level = EccLevel.H; |
} |
targetCwCount = QR_DATA_CODEWORDS_L[version - 1]; |
blocks = QR_BLOCKS_L[version - 1]; |
switch (ecc_level) { |
case M: |
targetCwCount = QR_DATA_CODEWORDS_M[version - 1]; |
blocks = QR_BLOCKS_M[version - 1]; |
break; |
case Q: |
targetCwCount = QR_DATA_CODEWORDS_Q[version - 1]; |
blocks = QR_BLOCKS_Q[version - 1]; |
break; |
case H: |
targetCwCount = QR_DATA_CODEWORDS_H[version - 1]; |
blocks = QR_BLOCKS_H[version - 1]; |
break; |
} |
final int[] datastream = new int[targetCwCount + 1]; |
final int[] fullstream = new int[QR_TOTAL_CODEWORDS[version - 1] + 1]; |
qrBinary(datastream, version, targetCwCount, inputMode, this.inputData, gs1, this.eciMode, est_binlen); |
addEcc(fullstream, datastream, version, targetCwCount, blocks); |
size = QR_SIZES[version - 1]; |
final int[] grid = new int[size * size]; |
infoLine("Version: " + version); |
infoLine("ECC Level: " + ecc_level.name()); |
setupGrid(grid, size, version); |
populateGrid(grid, size, fullstream, QR_TOTAL_CODEWORDS[version - 1]); |
if (version >= 7) { |
addVersionInfo(grid, size, version); |
} |
bitmask = applyBitmask(grid, size, ecc_level); |
infoLine("Mask Pattern: " + Integer.toBinaryString(bitmask)); |
addFormatInfo(grid, size, ecc_level, bitmask); |
this.readable = ""; |
this.pattern = new String[size]; |
this.row_count = size; |
this.row_height = new int[size]; |
for (i = 0; i < size; i++) { |
final StringBuilder bin = new StringBuilder(size); |
for (j = 0; j < size; j++) { |
if ((grid[i * size + j] & 0x01) != 0) { |
bin.append('1'); |
} else { |
bin.append('0'); |
} |
} |
this.pattern[i] = bin2pat(bin); |
this.row_height[i] = 1; |
} |
} |
/** Place Kanji / Binary / Alphanumeric / Numeric values in inputMode. */ |
private static void defineMode(final QrMode[] inputMode, final int[] inputData) { |
for (int i = 0; i < inputData.length; i++) { |
if (inputData[i] > 0xff) { |
inputMode[i] = QrMode.KANJI; |
} else { |
inputMode[i] = QrMode.BINARY; |
if (isAlpha(inputData[i])) { |
inputMode[i] = QrMode.ALPHANUM; |
} |
if (inputData[i] == FNC1) { |
inputMode[i] = QrMode.ALPHANUM; |
} |
if (isNumeric(inputData[i])) { |
inputMode[i] = QrMode.NUMERIC; |
} |
} |
} |
// TODO: uncomment |
// /* If less than 6 numeric digits together then don't use numeric mode */ |
// for (int i = 0; i < inputMode.length; i++) { |
// if (inputMode[i] == QrMode.NUMERIC) { |
// if (((i != 0) && (inputMode[i - 1] != QrMode.NUMERIC)) || (i == 0)) { |
// mlen = 0; |
// while (((mlen + i) < inputMode.length) && (inputMode[mlen + i] == QrMode.NUMERIC)) { |
// mlen++; |
// }; |
// if (mlen < 6) { |
// for (int j = 0; j < mlen; j++) { |
// inputMode[i + j] = QrMode.ALPHANUM; |
// } |
// } |
// } |
// } |
// } |
// |
// /* If less than 4 alphanumeric characters together then don't use alphanumeric mode */ |
// for (int i = 0; i < inputMode.length; i++) { |
// if (inputMode[i] == QrMode.ALPHANUM) { |
// if (((i != 0) && (inputMode[i - 1] != QrMode.ALPHANUM)) || (i == 0)) { |
// mlen = 0; |
// while (((mlen + i) < inputMode.length) && (inputMode[mlen + i] == QrMode.ALPHANUM)) { |
// mlen++; |
// }; |
// if (mlen < 4) { |
// for (int j = 0; j < mlen; j++) { |
// inputMode[i + j] = QrMode.BINARY; |
// } |
// } |
// } |
// } |
// } |
} |
/** Calculate the actual bit length of the proposed binary string. */ |
private static int getBinaryLength(final int version, final QrMode[] inputModeUnoptimized, final int[] inputData, final boolean gs1, final int eciMode) { |
int i, j; |
QrMode currentMode; |
final int inputLength = inputModeUnoptimized.length; |
int count = 0; |
int alphaLength; |
int percent = 0; |
// ZINT NOTE: in Zint, this call modifies the input mode array directly; here, we leave |
// the original array alone so that subsequent binary length checks don't irrevocably |
// optimize the mode array for the wrong QR Code version |
final QrMode[] inputMode = applyOptimisation(version, inputModeUnoptimized); |
currentMode = QrMode.NULL; |
if (gs1) { |
count += 4; |
} |
if (eciMode != 3) { |
count += 12; |
} |
for (i = 0; i < inputLength; i++) { |
if (inputMode[i] != currentMode) { |
count += 4; |
switch (inputMode[i]) { |
case KANJI: |
count += tribus(version, 8, 10, 12); |
count += blockLength(i, inputMode) * 13; |
break; |
case BINARY: |
count += tribus(version, 8, 16, 16); |
for (j = i; j < i + blockLength(i, inputMode); j++) { |
if (inputData[j] > 0xff) { |
count += 16; |
} else { |
count += 8; |
} |
} |
break; |
case ALPHANUM: |
count += tribus(version, 9, 11, 13); |
alphaLength = blockLength(i, inputMode); |
// In alphanumeric mode % becomes %% |
if (gs1) { |
for (j = i; j < i + alphaLength; j++) { // TODO: need to do this only if |
// in GS1 mode? or is the other |
// code wrong? |
// https://sourceforge.net/p/zint/tickets/104/#227b |
if (inputData[j] == '%') { |
percent++; |
} |
} |
} |
alphaLength += percent; |
switch (alphaLength % 2) { |
case 0: |
count += alphaLength / 2 * 11; |
break; |
case 1: |
count += (alphaLength - 1) / 2 * 11; |
count += 6; |
break; |
} |
break; |
case NUMERIC: |
count += tribus(version, 10, 12, 14); |
switch (blockLength(i, inputMode) % 3) { |
case 0: |
count += blockLength(i, inputMode) / 3 * 10; |
break; |
case 1: |
count += (blockLength(i, inputMode) - 1) / 3 * 10; |
count += 4; |
break; |
case 2: |
count += (blockLength(i, inputMode) - 2) / 3 * 10; |
count += 7; |
break; |
} |
break; |
} |
currentMode = inputMode[i]; |
} |
} |
return count; |
} |
/** |
* Implements a custom optimization algorithm, because implementation of the algorithm shown in |
* Annex J.2 created LONGER binary sequences. |
*/ |
private static QrMode[] applyOptimisation(final int version, final QrMode[] inputMode) { |
final int inputLength = inputMode.length; |
int blockCount = 0; |
int i, j; |
QrMode currentMode = QrMode.NULL; |
for (i = 0; i < inputLength; i++) { |
if (inputMode[i] != currentMode) { |
currentMode = inputMode[i]; |
blockCount++; |
} |
} |
final int[] blockLength = new int[blockCount]; |
final QrMode[] blockMode = new QrMode[blockCount]; |
j = -1; |
currentMode = QrMode.NULL; |
for (i = 0; i < inputLength; i++) { |
if (inputMode[i] != currentMode) { |
j++; |
blockLength[j] = 1; |
blockMode[j] = inputMode[i]; |
currentMode = inputMode[i]; |
} else { |
blockLength[j]++; |
} |
} |
if (blockCount > 1) { |
// Search forward |
for (i = 0; i <= blockCount - 2; i++) { |
if (blockMode[i] == QrMode.BINARY) { |
switch (blockMode[i + 1]) { |
case KANJI: |
if (blockLength[i + 1] < tribus(version, 4, 5, 6)) { |
blockMode[i + 1] = QrMode.BINARY; |
} |
break; |
case ALPHANUM: |
if (blockLength[i + 1] < tribus(version, 7, 8, 9)) { |
blockMode[i + 1] = QrMode.BINARY; |
} |
break; |
case NUMERIC: |
if (blockLength[i + 1] < tribus(version, 3, 4, 5)) { |
blockMode[i + 1] = QrMode.BINARY; |
} |
break; |
} |
} |
if (blockMode[i] == QrMode.ALPHANUM && blockMode[i + 1] == QrMode.NUMERIC) { |
if (blockLength[i + 1] < tribus(version, 6, 8, 10)) { |
blockMode[i + 1] = QrMode.ALPHANUM; |
} |
} |
} |
// Search backward |
for (i = blockCount - 1; i > 0; i--) { |
if (blockMode[i] == QrMode.BINARY) { |
switch (blockMode[i - 1]) { |
case KANJI: |
if (blockLength[i - 1] < tribus(version, 4, 5, 6)) { |
blockMode[i - 1] = QrMode.BINARY; |
} |
break; |
case ALPHANUM: |
if (blockLength[i - 1] < tribus(version, 7, 8, 9)) { |
blockMode[i - 1] = QrMode.BINARY; |
} |
break; |
case NUMERIC: |
if (blockLength[i - 1] < tribus(version, 3, 4, 5)) { |
blockMode[i - 1] = QrMode.BINARY; |
} |
break; |
} |
} |
if (blockMode[i] == QrMode.ALPHANUM && blockMode[i - 1] == QrMode.NUMERIC) { |
if (blockLength[i - 1] < tribus(version, 6, 8, 10)) { |
blockMode[i - 1] = QrMode.ALPHANUM; |
} |
} |
} |
} |
// ZINT NOTE: this method is different from the original Zint code in that it creates a |
// new array to hold the optimized values and returns it, rather than modifying the |
// original array; this allows this method to be called as many times as we want without |
// worrying about side effects |
final QrMode[] optimized = new QrMode[inputMode.length]; |
j = 0; |
for (int block = 0; block < blockCount; block++) { |
currentMode = blockMode[block]; |
for (i = 0; i < blockLength[block]; i++) { |
optimized[j] = currentMode; |
j++; |
} |
} |
return optimized; |
} |
/** Find the length of the block starting from 'start'. */ |
private static int blockLength(final int start, final QrMode[] inputMode) { |
final QrMode mode = inputMode[start]; |
int count = 0; |
final int i = start; |
do { |
count++; |
} while (i + count < inputMode.length && inputMode[i + count] == mode); |
return count; |
} |
/** Choose from three numbers based on version. */ |
private static int tribus(final int version, final int a, final int b, final int c) { |
if (version < 10) { |
return a; |
} else if (version >= 10 && version <= 26) { |
return b; |
} else { |
return c; |
} |
} |
/** Returns true if input is in the Alphanumeric set (see Table J.1) */ |
private static boolean isAlpha(final int c) { |
return c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == ' ' || c == '$' || c == '%' || c == '*' || c == '+' || c == '-' || c == '.' || c == '/' || c == ':'; |
} |
/** Returns true if input is in the Numeric set (see Table J.1) */ |
private static boolean isNumeric(final int c) { |
return c >= '0' && c <= '9'; |
} |
/** Converts input data to a binary stream and adds padding. */ |
private void qrBinary(final int[] datastream, final int version, final int target_binlen, final QrMode[] inputMode, final int[] inputData, final boolean gs1, final int eciMode, |
final int est_binlen) { |
// TODO: make encodeInfo a StringBuilder, make this method static? |
int position = 0; |
int short_data_block_length, i; |
int padbits; |
int current_binlen, current_bytes; |
int toggle; |
QrMode data_block; |
final StringBuilder binary = new StringBuilder(est_binlen + 12); |
if (gs1) { |
binary.append("0101"); /* FNC1 */ |
} |
if (eciMode != 3) { |
binary.append("0111"); /* ECI (Table 4) */ |
if (eciMode <= 127) { |
binaryAppend(eciMode, 8, binary); /* 000000 to 000127 */ |
} else if (eciMode <= 16383) { |
binaryAppend(0x8000 + eciMode, 16, binary); /* 000000 to 016383 */ |
} else { |
binaryAppend(0xC00000 + eciMode, 24, binary); /* 000000 to 999999 */ |
} |
} |
info("Encoding: "); |
do { |
data_block = inputMode[position]; |
short_data_block_length = 0; |
do { |
short_data_block_length++; |
} while (short_data_block_length + position < inputMode.length && inputMode[position + short_data_block_length] == data_block); |
switch (data_block) { |
case KANJI: |
/* Kanji mode */ |
/* Mode indicator */ |
binary.append("1000"); |
/* Character count indicator */ |
binaryAppend(short_data_block_length, tribus(version, 8, 10, 12), binary); |
info("KNJI "); |
/* Character representation */ |
for (i = 0; i < short_data_block_length; i++) { |
int jis = inputData[position + i]; |
if (jis >= 0x8140 && jis <= 0x9ffc) { |
jis -= 0x8140; |
} else if (jis >= 0xe040 && jis <= 0xebbf) { |
jis -= 0xc140; |
} |
final int prod = (jis >> 8) * 0xc0 + (jis & 0xff); |
binaryAppend(prod, 13, binary); |
infoSpace(prod); |
} |
break; |
case BINARY: |
/* Byte mode */ |
/* Mode indicator */ |
binary.append("0100"); |
/* Character count indicator */ |
binaryAppend(short_data_block_length, tribus(version, 8, 16, 16), binary); |
info("BYTE "); |
/* Character representation */ |
for (i = 0; i < short_data_block_length; i++) { |
int b = inputData[position + i]; |
if (b == FNC1) { |
b = 0x1d; /* FNC1 */ |
} |
binaryAppend(b, 8, binary); |
infoSpace(b); |
} |
break; |
case ALPHANUM: |
/* Alphanumeric mode */ |
/* Mode indicator */ |
binary.append("0010"); |
/* If in GS1 mode, expand FNC1 -> '%' and expand '%' -> '%%' in a new array */ |
int percentCount = 0; |
if (gs1) { |
for (i = 0; i < short_data_block_length; i++) { |
if (inputData[position + i] == '%') { |
percentCount++; |
} |
} |
} |
final int[] inputExpanded = new int[short_data_block_length + percentCount]; |
percentCount = 0; |
for (i = 0; i < short_data_block_length; i++) { |
final int c = inputData[position + i]; |
if (c == FNC1) { |
inputExpanded[i + percentCount] = '%'; /* FNC1 */ |
} else { |
inputExpanded[i + percentCount] = c; |
if (gs1 && c == '%') { |
percentCount++; |
inputExpanded[i + percentCount] = c; |
} |
} |
} |
/* Character count indicator */ |
binaryAppend(inputExpanded.length, tribus(version, 9, 11, 13), binary); |
info("ALPH "); |
/* Character representation */ |
for (i = 0; i + 1 < inputExpanded.length; i += 2) { |
final int first = positionOf((char) inputExpanded[i], RHODIUM); |
final int second = positionOf((char) inputExpanded[i + 1], RHODIUM); |
final int prod = first * 45 + second; |
final int count = 2; |
binaryAppend(prod, 1 + 5 * count, binary); |
infoSpace(prod); |
} |
if (inputExpanded.length % 2 != 0) { |
final int first = positionOf((char) inputExpanded[inputExpanded.length - 1], RHODIUM); |
final int prod = first; |
final int count = 1; |
binaryAppend(prod, 1 + 5 * count, binary); |
infoSpace(prod); |
} |
break; |
case NUMERIC: |
/* Numeric mode */ |
/* Mode indicator */ |
binary.append("0001"); |
/* Character count indicator */ |
binaryAppend(short_data_block_length, tribus(version, 10, 12, 14), binary); |
info("NUMB "); |
/* Character representation */ |
i = 0; |
while (i < short_data_block_length) { |
final int first = Character.getNumericValue(inputData[position + i]); |
int count = 1; |
int prod = first; |
if (i + 1 < short_data_block_length) { |
final int second = Character.getNumericValue(inputData[position + i + 1]); |
count = 2; |
prod = prod * 10 + second; |
if (i + 2 < short_data_block_length) { |
final int third = Character.getNumericValue(inputData[position + i + 2]); |
count = 3; |
prod = prod * 10 + third; |
} |
} |
binaryAppend(prod, 1 + 3 * count, binary); |
infoSpace(prod); |
i += count; |
} |
break; |
} |
position += short_data_block_length; |
} while (position < inputMode.length); |
infoLine(); |
/* Terminator */ |
binary.append("0000"); |
current_binlen = binary.length(); |
padbits = 8 - current_binlen % 8; |
if (padbits == 8) { |
padbits = 0; |
} |
current_bytes = (current_binlen + padbits) / 8; |
/* Padding bits */ |
for (i = 0; i < padbits; i++) { |
binary.append('0'); |
} |
/* Put data into 8-bit codewords */ |
for (i = 0; i < current_bytes; i++) { |
datastream[i] = 0x00; |
for (int p = 0; p < 8; p++) { |
if (binary.charAt(i * 8 + p) == '1') { |
datastream[i] += 0x80 >> p; |
} |
} |
} |
/* Add pad codewords */ |
toggle = 0; |
for (i = current_bytes; i < target_binlen; i++) { |
if (toggle == 0) { |
datastream[i] = 0xec; |
toggle = 1; |
} else { |
datastream[i] = 0x11; |
toggle = 0; |
} |
} |
info("Codewords: "); |
for (i = 0; i < target_binlen; i++) { |
infoSpace(datastream[i]); |
} |
infoLine(); |
} |
private static void binaryAppend(final int value, final int length, final StringBuilder binary) { |
final int start = 0x01 << length - 1; |
for (int i = 0; i < length; i++) { |
if ((value & start >> i) != 0) { |
binary.append('1'); |
} else { |
binary.append('0'); |
} |
} |
} |
/** |
* Splits data into blocks, adds error correction and then interleaves the blocks and error |
* correction data. |
*/ |
private static void addEcc(final int[] fullstream, final int[] datastream, final int version, final int data_cw, final int blocks) { |
final int ecc_cw = QR_TOTAL_CODEWORDS[version - 1] - data_cw; |
final int short_data_block_length = data_cw / blocks; |
final int qty_long_blocks = data_cw % blocks; |
final int qty_short_blocks = blocks - qty_long_blocks; |
final int ecc_block_length = ecc_cw / blocks; |
int i, j, length_this_block, posn; |
final int[] data_block = new int[short_data_block_length + 2]; |
final int[] ecc_block = new int[ecc_block_length + 2]; |
final int[] interleaved_data = new int[data_cw + 2]; |
final int[] interleaved_ecc = new int[ecc_cw + 2]; |
posn = 0; |
for (i = 0; i < blocks; i++) { |
if (i < qty_short_blocks) { |
length_this_block = short_data_block_length; |
} else { |
length_this_block = short_data_block_length + 1; |
} |
for (j = 0; j < ecc_block_length; j++) { |
ecc_block[j] = 0; |
} |
for (j = 0; j < length_this_block; j++) { |
data_block[j] = datastream[posn + j]; |
} |
final ReedSolomon rs = new ReedSolomon(); |
rs.init_gf(0x11d); |
rs.init_code(ecc_block_length, 0); |
rs.encode(length_this_block, data_block); |
for (j = 0; j < ecc_block_length; j++) { |
ecc_block[j] = rs.getResult(j); |
} |
for (j = 0; j < short_data_block_length; j++) { |
interleaved_data[j * blocks + i] = data_block[j]; |
} |
if (i >= qty_short_blocks) { |
interleaved_data[short_data_block_length * blocks + i - qty_short_blocks] = data_block[short_data_block_length]; |
} |
for (j = 0; j < ecc_block_length; j++) { |
interleaved_ecc[j * blocks + i] = ecc_block[ecc_block_length - j - 1]; |
} |
posn += length_this_block; |
} |
for (j = 0; j < data_cw; j++) { |
fullstream[j] = interleaved_data[j]; |
} |
for (j = 0; j < ecc_cw; j++) { |
fullstream[j + data_cw] = interleaved_ecc[j]; |
} |
} |
private static void setupGrid(final int[] grid, final int size, final int version) { |
int i; |
boolean toggle = true; |
/* Add timing patterns */ |
for (i = 0; i < size; i++) { |
if (toggle) { |
grid[6 * size + i] = 0x21; |
grid[i * size + 6] = 0x21; |
toggle = false; |
} else { |
grid[6 * size + i] = 0x20; |
grid[i * size + 6] = 0x20; |
toggle = true; |
} |
} |
/* Add finder patterns */ |
placeFinder(grid, size, 0, 0); |
placeFinder(grid, size, 0, size - 7); |
placeFinder(grid, size, size - 7, 0); |
/* Add separators */ |
for (i = 0; i < 7; i++) { |
grid[7 * size + i] = 0x10; |
grid[i * size + 7] = 0x10; |
grid[7 * size + size - 1 - i] = 0x10; |
grid[i * size + size - 8] = 0x10; |
grid[(size - 8) * size + i] = 0x10; |
grid[(size - 1 - i) * size + 7] = 0x10; |
} |
grid[7 * size + 7] = 0x10; |
grid[7 * size + size - 8] = 0x10; |
grid[(size - 8) * size + 7] = 0x10; |
/* Add alignment patterns */ |
if (version != 1) { |
/* Version 1 does not have alignment patterns */ |
final int loopsize = QR_ALIGN_LOOPSIZE[version - 1]; |
for (int x = 0; x < loopsize; x++) { |
for (int y = 0; y < loopsize; y++) { |
final int xcoord = QR_TABLE_E1[(version - 2) * 7 + x]; |
final int ycoord = QR_TABLE_E1[(version - 2) * 7 + y]; |
if ((grid[ycoord * size + xcoord] & 0x10) == 0) { |
placeAlign(grid, size, xcoord, ycoord); |
} |
} |
} |
} |
/* Reserve space for format information */ |
for (i = 0; i < 8; i++) { |
grid[8 * size + i] += 0x20; |
grid[i * size + 8] += 0x20; |
grid[8 * size + size - 1 - i] = 0x20; |
grid[(size - 1 - i) * size + 8] = 0x20; |
} |
grid[8 * size + 8] += 0x20; |
grid[(size - 1 - 7) * size + 8] = 0x21; /* Dark Module from Figure 25 */ |
/* Reserve space for version information */ |
if (version >= 7) { |
for (i = 0; i < 6; i++) { |
grid[(size - 9) * size + i] = 0x20; |
grid[(size - 10) * size + i] = 0x20; |
grid[(size - 11) * size + i] = 0x20; |
grid[i * size + size - 9] = 0x20; |
grid[i * size + size - 10] = 0x20; |
grid[i * size + size - 11] = 0x20; |
} |
} |
} |
private static void placeFinder(final int[] grid, final int size, final int x, final int y) { |
final int[] finder = { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 }; |
for (int xp = 0; xp < 7; xp++) { |
for (int yp = 0; yp < 7; yp++) { |
if (finder[xp + 7 * yp] == 1) { |
grid[(yp + y) * size + xp + x] = 0x11; |
} else { |
grid[(yp + y) * size + xp + x] = 0x10; |
} |
} |
} |
} |
private static void placeAlign(final int[] grid, final int size, int x, int y) { |
final int[] alignment = { 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1 }; |
x -= 2; |
y -= 2; /* Input values represent centre of pattern */ |
for (int xp = 0; xp < 5; xp++) { |
for (int yp = 0; yp < 5; yp++) { |
if (alignment[xp + 5 * yp] == 1) { |
grid[(yp + y) * size + xp + x] = 0x11; |
} else { |
grid[(yp + y) * size + xp + x] = 0x10; |
} |
} |
} |
} |
private static void populateGrid(final int[] grid, final int size, final int[] fullstream, final int cw) { |
boolean goingUp = true; |
int row = 0; /* right hand side */ |
int i, n, y; |
n = cw * 8; |
y = size - 1; |
i = 0; |
do { |
int x = size - 2 - row * 2; |
if (x < 6) { |
x--; /* skip over vertical timing pattern */ |
} |
if ((grid[y * size + x + 1] & 0xf0) == 0) { |
if (cwbit(fullstream, i)) { |
grid[y * size + x + 1] = 0x01; |
} else { |
grid[y * size + x + 1] = 0x00; |
} |
i++; |
} |
if (i < n) { |
if ((grid[y * size + x] & 0xf0) == 0) { |
if (cwbit(fullstream, i)) { |
grid[y * size + x] = 0x01; |
} else { |
grid[y * size + x] = 0x00; |
} |
i++; |
} |
} |
if (goingUp) { |
y--; |
} else { |
y++; |
} |
if (y == -1) { |
/* reached the top */ |
row++; |
y = 0; |
goingUp = false; |
} |
if (y == size) { |
/* reached the bottom */ |
row++; |
y = size - 1; |
goingUp = true; |
} |
} while (i < n); |
} |
private static boolean cwbit(final int[] fullstream, final int i) { |
return (fullstream[i / 8] & 0x80 >> i % 8) != 0; |
} |
private static int applyBitmask(final int[] grid, final int size, final EccLevel ecc_level) { |
int x, y; |
char p; |
int pattern; |
int best_val, best_pattern; |
final int[] penalty = new int[8]; |
final byte[] mask = new byte[size * size]; |
final byte[] eval = new byte[size * size]; |
/* Perform data masking */ |
for (x = 0; x < size; x++) { |
for (y = 0; y < size; y++) { |
mask[y * size + x] = 0x00; |
// all eight bit mask variants are encoded in the 8 bits of the bytes that make up |
// the mask array |
if ((grid[y * size + x] & 0xf0) == 0) { // exclude areas not to be masked |
if ((y + x & 1) == 0) { |
mask[y * size + x] += (byte) 0x01; |
} |
if ((y & 1) == 0) { |
mask[y * size + x] += (byte) 0x02; |
} |
if (x % 3 == 0) { |
mask[y * size + x] += (byte) 0x04; |
} |
if ((y + x) % 3 == 0) { |
mask[y * size + x] += (byte) 0x08; |
} |
if ((y / 2 + x / 3 & 1) == 0) { |
mask[y * size + x] += (byte) 0x10; |
} |
if ((y * x & 1) + y * x % 3 == 0) { |
mask[y * size + x] += (byte) 0x20; |
} |
if (((y * x & 1) + y * x % 3 & 1) == 0) { |
mask[y * size + x] += (byte) 0x40; |
} |
if (((y + x & 1) + y * x % 3 & 1) == 0) { |
mask[y * size + x] += (byte) 0x80; |
} |
} |
} |
} |
/* Apply data masks to grid, result in eval */ |
for (x = 0; x < size; x++) { |
for (y = 0; y < size; y++) { |
if ((grid[y * size + x] & 0x01) != 0) { |
p = 0xff; |
} else { |
p = 0x00; |
} |
eval[y * size + x] = (byte) (mask[y * size + x] ^ p); |
} |
} |
/* Evaluate result */ |
for (pattern = 0; pattern < 8; pattern++) { |
addFormatInfoEval(eval, size, ecc_level, pattern); |
penalty[pattern] = evaluate(eval, size, pattern); |
} |
best_pattern = 0; |
best_val = penalty[0]; |
for (pattern = 1; pattern < 8; pattern++) { |
if (penalty[pattern] < best_val) { |
best_pattern = pattern; |
best_val = penalty[pattern]; |
} |
} |
/* Apply mask */ |
for (x = 0; x < size; x++) { |
for (y = 0; y < size; y++) { |
if ((mask[y * size + x] & 0x01 << best_pattern) != 0) { |
if ((grid[y * size + x] & 0x01) != 0) { |
grid[y * size + x] = 0x00; |
} else { |
grid[y * size + x] = 0x01; |
} |
} |
} |
} |
return best_pattern; |
} |
/** Adds format information to eval. */ |
private static void addFormatInfoEval(final byte[] eval, final int size, final EccLevel ecc_level, final int pattern) { |
int format = pattern; |
int seq; |
int i; |
switch (ecc_level) { |
case L: |
format += 0x08; |
break; |
case Q: |
format += 0x18; |
break; |
case H: |
format += 0x10; |
break; |
} |
seq = QR_ANNEX_C[format]; |
for (i = 0; i < 6; i++) { |
eval[i * size + 8] = (byte) ((seq >> i & 0x01) != 0 ? 0x01 >> pattern : 0x00); |
} |
for (i = 0; i < 8; i++) { |
eval[8 * size + size - i - 1] = (byte) ((seq >> i & 0x01) != 0 ? 0x01 >> pattern : 0x00); |
} |
for (i = 0; i < 6; i++) { |
eval[8 * size + 5 - i] = (byte) ((seq >> i + 9 & 0x01) != 0 ? 0x01 >> pattern : 0x00); |
} |
for (i = 0; i < 7; i++) { |
eval[(size - 7 + i) * size + 8] = (byte) ((seq >> i + 8 & 0x01) != 0 ? 0x01 >> pattern : 0x00); |
} |
eval[7 * size + 8] = (byte) ((seq >> 6 & 0x01) != 0 ? 0x01 >> pattern : 0x00); |
eval[8 * size + 8] = (byte) ((seq >> 7 & 0x01) != 0 ? 0x01 >> pattern : 0x00); |
eval[8 * size + 7] = (byte) ((seq >> 8 & 0x01) != 0 ? 0x01 >> pattern : 0x00); |
} |
private static int evaluate(final byte[] eval, final int size, final int pattern) { |
int x, y, block, weight; |
int result = 0; |
int state; |
int p; |
int dark_mods; |
int percentage, k; |
int a, b, afterCount, beforeCount; |
final byte[] local = new byte[size * size]; |
// all eight bit mask variants have been encoded in the 8 bits of the bytes |
// that make up the grid array; select them for evaluation according to the |
// desired pattern |
for (x = 0; x < size; x++) { |
for (y = 0; y < size; y++) { |
if ((eval[y * size + x] & 0x01 << pattern) != 0) { |
local[y * size + x] = '1'; |
} else { |
local[y * size + x] = '0'; |
} |
} |
} |
/* Test 1: Adjacent modules in row/column in same colour */ |
/* Vertical */ |
for (x = 0; x < size; x++) { |
state = local[x]; |
block = 0; |
for (y = 0; y < size; y++) { |
if (local[y * size + x] == state) { |
block++; |
} else { |
if (block > 5) { |
result += 3 + block - 5; |
} |
block = 0; |
state = local[y * size + x]; |
} |
} |
if (block > 5) { |
result += 3 + block - 5; |
} |
} |
/* Horizontal */ |
for (y = 0; y < size; y++) { |
state = local[y * size]; |
block = 0; |
for (x = 0; x < size; x++) { |
if (local[y * size + x] == state) { |
block++; |
} else { |
if (block > 5) { |
result += 3 + block - 5; |
} |
block = 0; |
state = local[y * size + x]; |
} |
} |
if (block > 5) { |
result += 3 + block - 5; |
} |
} |
/* Test 2: Block of modules in same color */ |
for (x = 0; x < size - 1; x++) { |
for (y = 0; y < size - 1; y++) { |
if (local[y * size + x] == local[(y + 1) * size + x] && local[y * size + x] == local[y * size + x + 1] && local[y * size + x] == local[(y + 1) * size + x + 1]) { |
result += 3; |
} |
} |
} |
/* Test 3: 1:1:3:1:1 ratio pattern in row/column */ |
/* Vertical */ |
for (x = 0; x < size; x++) { |
for (y = 0; y < size - 7; y++) { |
p = 0; |
for (weight = 0; weight < 7; weight++) { |
if (local[(y + weight) * size + x] == '1') { |
p += 0x40 >> weight; |
} |
} |
if (p == 0x5d) { |
/* Pattern found, check before and after */ |
beforeCount = 0; |
for (b = y - 4; b < y; b++) { |
if (b < 0) { |
beforeCount++; |
} else { |
if (local[b * size + x] == '0') { |
beforeCount++; |
} else { |
beforeCount = 0; |
} |
} |
} |
afterCount = 0; |
for (a = y + 7; a <= y + 10; a++) { |
if (a >= size) { |
afterCount++; |
} else { |
if (local[a * size + x] == '0') { |
afterCount++; |
} else { |
afterCount = 0; |
} |
} |
} |
if (beforeCount == 4 || afterCount == 4) { |
// Pattern is preceded or followed by light area 4 modules wide |
result += 40; |
} |
} |
} |
} |
/* Horizontal */ |
for (y = 0; y < size; y++) { |
for (x = 0; x < size - 7; x++) { |
p = 0; |
for (weight = 0; weight < 7; weight++) { |
if (local[y * size + x + weight] == '1') { |
p += 0x40 >> weight; |
} |
} |
if (p == 0x5d) { |
/* Pattern found, check before and after */ |
beforeCount = 0; |
for (b = x - 4; b < x; b++) { |
if (b < 0) { |
beforeCount++; |
} else { |
if (local[y * size + b] == '0') { |
beforeCount++; |
} else { |
beforeCount = 0; |
} |
} |
} |
afterCount = 0; |
for (a = x + 7; a <= x + 10; a++) { |
if (a >= size) { |
afterCount++; |
} else { |
if (local[y * size + a] == '0') { |
afterCount++; |
} else { |
afterCount = 0; |
} |
} |
} |
if (beforeCount == 4 || afterCount == 4) { |
// Pattern is preceded or followed by light area 4 modules wide |
result += 40; |
} |
} |
} |
} |
/* Test 4: Proportion of dark modules in entire symbol */ |
dark_mods = 0; |
for (x = 0; x < size; x++) { |
for (y = 0; y < size; y++) { |
if (local[y * size + x] == '1') { |
dark_mods++; |
} |
} |
} |
percentage = 100 * (dark_mods / (size * size)); |
if (percentage <= 50) { |
k = (100 - percentage - 50) / 5; |
} else { |
k = (percentage - 50) / 5; |
} |
result += 10 * k; |
return result; |
} |
/* Adds format information to grid. */ |
private static void addFormatInfo(final int[] grid, final int size, final EccLevel ecc_level, final int pattern) { |
int format = pattern; |
int seq; |
int i; |
switch (ecc_level) { |
case L: |
format += 0x08; |
break; |
case Q: |
format += 0x18; |
break; |
case H: |
format += 0x10; |
break; |
} |
seq = QR_ANNEX_C[format]; |
for (i = 0; i < 6; i++) { |
grid[i * size + 8] += seq >> i & 0x01; |
} |
for (i = 0; i < 8; i++) { |
grid[8 * size + size - i - 1] += seq >> i & 0x01; |
} |
for (i = 0; i < 6; i++) { |
grid[8 * size + 5 - i] += seq >> i + 9 & 0x01; |
} |
for (i = 0; i < 7; i++) { |
grid[(size - 7 + i) * size + 8] += seq >> i + 8 & 0x01; |
} |
grid[7 * size + 8] += seq >> 6 & 0x01; |
grid[8 * size + 8] += seq >> 7 & 0x01; |
grid[8 * size + 7] += seq >> 8 & 0x01; |
} |
/** Adds version information. */ |
private static void addVersionInfo(final int[] grid, final int size, final int version) { |
// TODO: Zint masks with 0x41 instead of 0x01; which is correct? |
// https://sourceforge.net/p/zint/tickets/110/ |
final int version_data = QR_ANNEX_D[version - 7]; |
for (int i = 0; i < 6; i++) { |
grid[(size - 11) * size + i] += version_data >> i * 3 & 0x01; |
grid[(size - 10) * size + i] += version_data >> i * 3 + 1 & 0x01; |
grid[(size - 9) * size + i] += version_data >> i * 3 + 2 & 0x01; |
grid[i * size + size - 11] += version_data >> i * 3 & 0x01; |
grid[i * size + size - 10] += version_data >> i * 3 + 1 & 0x01; |
grid[i * size + size - 9] += version_data >> i * 3 + 2 & 0x01; |
} |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/RoyalMail4State.java |
---|
New file |
0,0 → 1,122 |
/* |
* 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> |
* Encodes data according to the Royal Mail 4-State Country Code. |
* |
* <p> |
* Data input can consist of numbers 0-9 and letters A-Z and usually includes delivery postcode |
* followed by house number. A check digit is calculated and added. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class RoyalMail4State extends Symbol { |
private static final String[] ROYAL_TABLE = { "TTFF", "TDAF", "TDFA", "DTAF", "DTFA", "DDAA", "TADF", "TFTF", "TFDA", "DATF", "DADA", "DFTA", "TAFD", "TFAD", "TFFT", "DAAD", "DAFT", "DFAT", |
"ATDF", "ADTF", "ADDA", "FTTF", "FTDA", "FDTA", "ATFD", "ADAD", "ADFT", "FTAD", "FTFT", "FDAT", "AADD", "AFTD", "AFDT", "FATD", "FADT", "FFTT" }; |
private static final 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() { |
String dest; |
int i, top = 0, bottom = 0; |
int row, column; |
int index; |
this.content = this.content.toUpperCase(Locale.ENGLISH); |
if (!this.content.matches("[0-9A-Z]+")) { |
throw new OkapiException("Invalid characters in data"); |
} |
dest = "A"; |
for (i = 0; i < this.content.length(); i++) { |
index = positionOf(this.content.charAt(i), KR_SET); |
dest += ROYAL_TABLE[index]; |
top += (index + 1) % 6; |
bottom += (index / 6 + 1) % 6; |
} |
/* calculate check digit */ |
row = top % 6 - 1; |
column = bottom % 6 - 1; |
if (row == -1) { |
row = 5; |
} |
if (column == -1) { |
column = 5; |
} |
final int check = 6 * row + column; |
dest += ROYAL_TABLE[check]; |
infoLine("Check Digit: " + check); |
/* Stop character */ |
dest += "F"; |
infoLine("Encoding: " + dest); |
this.readable = ""; |
this.pattern = new String[1]; |
this.pattern[0] = dest; |
this.row_count = 1; |
this.row_height = new int[1]; |
this.row_height[0] = -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++) { |
switch (this.pattern[0].charAt(xBlock)) { |
case 'A': |
y = 0; |
h = 5; |
break; |
case 'D': |
y = 3; |
h = 5; |
break; |
case 'F': |
y = 0; |
h = 8; |
break; |
case 'T': |
y = 3; |
h = 2; |
break; |
} |
final Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h); |
this.rectangles.add(rect); |
x += 2; |
} |
this.symbol_width = this.pattern[0].length() * 3; |
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/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", &q |