Dépôt officiel du code source de l'ERP OpenConcerto
/trunk/Modules/Module Expense/src/org/openconcerto/modules/humanresources/travel/expense/ExpenseSQLElement.java |
---|
54,7 → 54,7 |
} |
@Override |
public SQLComponent createComponent() { |
public SQLComponent createComponent() { |
final String groupId = this.getCode() + ".default"; |
final Group group = GlobalMapper.getInstance().getGroup(groupId); |
if (group == null) { |
/trunk/Modules/Module Expense/src/org/openconcerto/modules/humanresources/travel/expense/ExpenseGroup.java |
---|
6,33 → 6,32 |
public class ExpenseGroup extends Group { |
public ExpenseGroup() { |
super(ExpenseSQLElement.ELEMENT_CODE + ".default"); |
final Group g = new Group(ExpenseSQLElement.ELEMENT_CODE + ".identifier"); |
g.addItem("DATE"); |
g.addItem("ID_USER_COMMON"); |
public ExpenseGroup() { |
super(ExpenseSQLElement.ELEMENT_CODE + ".default", LayoutHints.DEFAULT_SEPARATED_GROUP_HINTS); |
final Group g = new Group(ExpenseSQLElement.ELEMENT_CODE + ".identifier"); |
g.addItem("DATE", LayoutHints.DEFAULT_LARGE_FIELD_HINTS); |
g.addItem("ID_USER_COMMON", LayoutHints.DEFAULT_LARGE_FIELD_HINTS); |
this.add(g); |
this.add(g); |
final Group gDescription = new Group(ExpenseSQLElement.ELEMENT_CODE + ".description"); |
gDescription.add(new Item("DESCRIPTION", new LayoutHints(true, true, true, true, true, true))); |
this.add(gDescription); |
final Group gDescription = new Group(ExpenseSQLElement.ELEMENT_CODE + ".description"); |
gDescription.addItem("DESCRIPTION", new LayoutHints(true, true, true, true, true, true, true, true)); |
gDescription.addItem("ID_EXPENSE_STATE", LayoutHints.DEFAULT_LARGE_FIELD_HINTS); |
this.add(gDescription); |
final Group gTravel = new Group(ExpenseSQLElement.ELEMENT_CODE + ".travel", LayoutHints.DEFAULT_SEPARATED_GROUP_HINTS); |
gTravel.addItem("TRAVEL_DISTANCE"); |
gTravel.addItem("TRAVEL_RATE"); |
gTravel.addItem("TRAVEL_AMOUNT"); |
this.add(gTravel); |
final Group gTravel = new Group(ExpenseSQLElement.ELEMENT_CODE + ".travel", |
LayoutHints.DEFAULT_SEPARATED_GROUP_HINTS); |
gTravel.addItem("TRAVEL_DISTANCE", LayoutHints.DEFAULT_LARGE_FIELD_HINTS); |
gTravel.addItem("TRAVEL_RATE", LayoutHints.DEFAULT_LARGE_FIELD_HINTS); |
gTravel.addItem("TRAVEL_AMOUNT", LayoutHints.DEFAULT_LARGE_FIELD_HINTS); |
this.add(gTravel); |
final Group gAddress = new Group(ExpenseSQLElement.ELEMENT_CODE + ".misc", LayoutHints.DEFAULT_SEPARATED_GROUP_HINTS); |
gAddress.addItem("MISC_AMOUNT"); |
this.add(gAddress); |
final Group gAddress = new Group(ExpenseSQLElement.ELEMENT_CODE + ".misc", |
LayoutHints.DEFAULT_SEPARATED_GROUP_HINTS); |
gAddress.addItem("MISC_AMOUNT", LayoutHints.DEFAULT_LARGE_FIELD_HINTS); |
// gAddress.addItem("ID_EXPENSE_STATE", LayoutHints.DEFAULT_LARGE_FIELD_HINTS); |
this.add(gAddress); |
final Group gState = new Group(ExpenseSQLElement.ELEMENT_CODE + ".state"); |
gState.addItem("ID_EXPENSE_STATE"); |
} |
this.add(gState); |
} |
} |
/trunk/Modules/Module Expense/src/org/openconcerto/modules/humanresources/travel/expense/translation_fr.xml |
---|
New file |
0,0 → 1,5 |
<translation lang="fr"> |
<item id="humanresources.travel.expense.travel" label="Frais kilometriques" /> |
<item id="humanresources.travel.expense.misc" label="Frais annexes" /> |
<item id="humanresources.travel.expense.default" label="Description" /> |
</translation> |
/trunk/Modules/Module Expense/src/org/openconcerto/modules/humanresources/travel/expense/labels_fr.xml |
---|
1,7 → 1,7 |
<?xml version="1.0" encoding="UTF-8" ?> |
<ROOT> |
<TABLE name="EXPENSE_STATE"> |
<FIELD name="NAME" label="Nom" /> |
<FIELD name="NAME" label="Statut" /> |
</TABLE> |
<TABLE name="EXPENSE"> |
<FIELD name="NAME" label="Nom" /> |
12,9 → 12,9 |
<FIELD name="TRAVEL_RATE" label="Tarif kilométrique" /> |
<FIELD name="TRAVEL_AMOUNT" label="Montant des frais kilométriques" /> |
<FIELD name="MISC_AMOUNT" label="Montant des frais annexes" /> |
<FIELD name="ID_EXPENSE_STATE" label="Status" /> |
<FIELD name="ID_EXPENSE_STATE" label="Statut" /> |
<FIELD name="ID_USER_COMMON" label="Employé" /> |
<FIELD name="ID_EXPENSE_STATE" label="Status" /> |
<FIELD name="ID_EXPENSE_STATE" label="Statut" /> |
</TABLE> |
</ROOT> |
/trunk/Modules/Module Expense/src/org/openconcerto/modules/humanresources/travel/expense/ExpenseSQLComponent.java |
---|
7,6 → 7,7 |
import java.util.Set; |
import javax.swing.JComponent; |
import javax.swing.JLabel; |
import javax.swing.text.AbstractDocument; |
import javax.swing.text.JTextComponent; |
13,9 → 14,10 |
import org.openconcerto.erp.core.common.ui.DeviseField; |
import org.openconcerto.sql.element.GroupSQLComponent; |
import org.openconcerto.sql.element.SQLElement; |
import org.openconcerto.sql.sqlobject.ElementComboBox; |
import org.openconcerto.sql.sqlobject.itemview.VWRowItemView; |
import org.openconcerto.ui.JDate; |
import org.openconcerto.ui.component.ITextArea; |
import org.openconcerto.ui.JLabelBold; |
import org.openconcerto.ui.component.text.TextComponentUtils; |
import org.openconcerto.ui.group.Group; |
import org.openconcerto.utils.text.DocumentFilterList; |
23,73 → 25,91 |
import org.openconcerto.utils.text.LimitedSizeDocumentFilter; |
public class ExpenseSQLComponent extends GroupSQLComponent { |
private DecimalFormat decimalFormat = new DecimalFormat("0.##"); |
private DecimalFormat decimalFormat = new DecimalFormat("0.##"); |
public ExpenseSQLComponent(SQLElement element, Group group) { |
super(element, group); |
public ExpenseSQLComponent(SQLElement element, Group group) { |
super(element, group); |
} |
} |
@Override |
protected void initDone() { |
super.initDone(); |
final PropertyChangeListener listener = new PropertyChangeListener() { |
@Override |
protected void initDone() { |
super.initDone(); |
final PropertyChangeListener listener = new PropertyChangeListener() { |
@Override |
public void propertyChange(PropertyChangeEvent evt) { |
updateAmount(); |
} |
}; |
@Override |
public void propertyChange(PropertyChangeEvent evt) { |
updateAmount(); |
} |
}; |
final AbstractDocument comp1 = (AbstractDocument) TextComponentUtils.getDocument(getView("TRAVEL_DISTANCE").getComp()); |
DocumentFilterList.add(comp1, new LimitedSizeDocumentFilter(5), FilterType.SIMPLE_FILTER); |
getView("TRAVEL_DISTANCE").addValueListener(listener); |
final AbstractDocument comp1 = (AbstractDocument) TextComponentUtils |
.getDocument(getView("TRAVEL_DISTANCE").getComp()); |
DocumentFilterList.add(comp1, new LimitedSizeDocumentFilter(5), FilterType.SIMPLE_FILTER); |
getView("TRAVEL_DISTANCE").addValueListener(listener); |
final AbstractDocument comp2 = (AbstractDocument) TextComponentUtils.getDocument(getView("TRAVEL_RATE").getComp()); |
DocumentFilterList.add(comp2, new LimitedSizeDocumentFilter(5), FilterType.SIMPLE_FILTER); |
final AbstractDocument comp2 = (AbstractDocument) TextComponentUtils |
.getDocument(getView("TRAVEL_RATE").getComp()); |
DocumentFilterList.add(comp2, new LimitedSizeDocumentFilter(5), FilterType.SIMPLE_FILTER); |
getView("TRAVEL_RATE").addValueListener(listener); |
} |
getView("TRAVEL_RATE").addValueListener(listener); |
} |
@Override |
protected Set<String> createRequiredNames() { |
final Set<String> s = new HashSet<String>(1); |
s.add("DATE"); |
s.add("DESCRIPTION"); |
s.add("ID_USER_COMMON"); |
s.add("ID_EXPENSE_STATE"); |
return s; |
} |
@Override |
protected Set<String> createRequiredNames() { |
final Set<String> s = new HashSet<String>(1); |
s.add("DATE"); |
s.add("DESCRIPTION"); |
s.add("ID_USER_COMMON"); |
s.add("ID_EXPENSE_STATE"); |
return s; |
} |
@Override |
public JComponent getEditor(String id) { |
if (id.equals("DESCRIPTION")) { |
return new ITextArea(); |
} else if (id.equals("DATE")) { |
return new JDate(true); |
} else if (id.endsWith("AMOUNT")) { |
return new DeviseField(); |
} |
return super.getEditor(id); |
} |
@Override |
public JComponent createEditor(String id) { |
if(id.equals("ID_USER_COMMON") || id.equals("ID_EXPENSE_STATE")) { |
ElementComboBox comp = new ElementComboBox(false, 15); |
((ElementComboBox) comp).init(getElement().getForeignElement(id)); |
return comp; |
} |
else if (id.equals("DATE")) { |
return new JDate(true); |
} else if (id.endsWith("AMOUNT")) { |
return new DeviseField(); |
} |
return super.createEditor(id); |
} |
private void updateAmount() { |
float v1 = getFloat("TRAVEL_DISTANCE"); |
float v2 = getFloat("TRAVEL_RATE"); |
float total = v1 * v2; |
if (total > 1000000 || total < 0) { |
total = 0; |
} |
final String valueOf = decimalFormat.format(total); |
((JTextComponent) getView("TRAVEL_AMOUNT").getComp()).setText(valueOf); |
} |
@Override |
public JLabel getLabel(String id) { |
if (id.equals("humanresources.travel.expense.travel")) { |
return new JLabelBold("Frais kilometriques"); |
} else if (id.equals("humanresources.travel.expense.default")) { |
return new JLabelBold("Descriptif"); |
} else if (id.equals("humanresources.travel.expense.misc")) { |
return new JLabelBold("Frais annexes"); |
} else { |
return (JLabel) super.getLabel(id); |
} |
} |
private float getFloat(String id) { |
final Number n = (Number) ((VWRowItemView<?>) getView(id)).getWrapper().getValue(); |
if (n == null) { |
return 0; |
} |
return n.floatValue(); |
} |
private void updateAmount() { |
float v1 = getFloat("TRAVEL_DISTANCE"); |
float v2 = getFloat("TRAVEL_RATE"); |
float total = v1 * v2; |
if (total > 1000000 || total < 0) { |
total = 0; |
} |
final String valueOf = decimalFormat.format(total); |
((JTextComponent) getView("TRAVEL_AMOUNT").getComp()).setText(valueOf); |
} |
private float getFloat(String id) { |
final Number n = (Number) ((VWRowItemView<?>) getView(id)).getWrapper().getValue(); |
if (n == null) { |
return 0; |
} |
return n.floatValue(); |
} |
} |
/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,10 → 110,22 |
if (bReplace.isSelected()) { |
BigDecimal v = new BigDecimal(this.tReplace.getText().trim()); |
for (SQLRowAccessor sqlRowAccessor : r) { |
final SQLRowValues rowValues = sqlRowAccessor.createEmptyUpdateRow(); |
rowValues.put(field.getName(), decimalToFieldType(v)); |
processBeforeUpdate(sqlRowAccessor, rowValues); |
rowValues.update(); |
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(batchfield.getField().getName(), decimalToFieldType(v)); |
processBeforeUpdate(sqlRowAccessor, rowValues); |
rowValues.update(); |
} |
} |
} else if (bAdd.isSelected()) { |
127,16 → 139,42 |
BigDecimal v = new BigDecimal(t); |
for (SQLRowAccessor sqlRowAccessor : r) { |
final SQLRowValues rowValues = sqlRowAccessor.createEmptyUpdateRow(); |
final BigDecimal value = sqlRowAccessor.asRow().getBigDecimal(field.getName()); |
if (value != null) { |
if (isPercent) { |
rowValues.put(field.getName(), decimalToFieldType(value.multiply(v.divide(new BigDecimal(100)).add(BigDecimal.ONE)))); |
} else { |
rowValues.put(field.getName(), decimalToFieldType(value.add(v))); |
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(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(sqlRowT, rowValues); |
rowValues.update(); |
} |
} |
processBeforeUpdate(sqlRowAccessor, 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()) { |
149,17 → 187,38 |
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()); |
if (value != null) { |
if (isPercent) { |
rowValues.put(field.getName(), decimalToFieldType(value.multiply(v.divide(new BigDecimal(-100)).add(BigDecimal.ONE)))); |
} else { |
rowValues.put(field.getName(), decimalToFieldType(value.add(v))); |
SQLRowValues rowValues = sqlRowT.createEmptyUpdateRow(); |
final BigDecimal value = sqlRowT.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(sqlRowT, rowValues); |
rowValues.update(); |
} |
} |
} |
processBeforeUpdate(sqlRowAccessor, 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(); |
} |
} |
} |
} |
166,7 → 225,7 |
} |
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/Hexagon.java |
---|
New file |
0,0 → 1,41 |
/* |
* 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; |
/** |
* Calculate a set of points to make a hexagon |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class Hexagon { |
private static final double INK_SPREAD = 1.25; |
private static final double[] OFFSET_X = { 0.0, 0.86, 0.86, 0.0, -0.86, -0.86 }; |
private static final double[] OFFSET_Y = { 1.0, 0.5, -0.5, -1.0, -0.5, 0.5 }; |
public final double centreX; |
public final double centreY; |
public final double[] pointX = new double[6]; |
public final double[] pointY = new double[6]; |
public Hexagon(final double centreX, final double centreY) { |
this.centreX = centreX; |
this.centreY = centreY; |
for (int i = 0; i < 6; i++) { |
this.pointX[i] = centreX + OFFSET_X[i] * INK_SPREAD; |
this.pointY[i] = centreY + OFFSET_Y[i] * INK_SPREAD; |
} |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Code16k.java |
---|
New file |
0,0 → 1,779 |
/* |
* 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; |
import java.nio.charset.StandardCharsets; |
/** |
* <p> |
* Implements Code 16K symbology according to BS EN 12323:2005. |
* |
* <p> |
* Encodes using a stacked symbology based on Code 128. Supports encoding of any 8-bit ISO 8859-1 |
* (Latin-1) data with a maximum data capacity of 77 alpha-numeric characters or 154 numerical |
* digits. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class Code16k extends Symbol { |
private enum Mode { |
NULL, SHIFTA, LATCHA, SHIFTB, LATCHB, SHIFTC, LATCHC, AORB, ABORC, CANDB, CANDBB |
} |
/* EN 12323 Table 1 - "Code 16K" character encodations */ |
private static final String[] C16K_TABLE = { "212222", "222122", "222221", "121223", "121322", "131222", "122213", "122312", "132212", "221213", "221312", "231212", "112232", "122132", "122231", |
"113222", "123122", "123221", "223211", "221132", "221231", "213212", "223112", "312131", "311222", "321122", "321221", "312212", "322112", "322211", "212123", "212321", "232121", |
"111323", "131123", "131321", "112313", "132113", "132311", "211313", "231113", "231311", "112133", "112331", "132131", "113123", "113321", "133121", "313121", "211331", "231131", |
"213113", "213311", "213131", "311123", "311321", "331121", "312113", "312311", "332111", "314111", "221411", "431111", "111224", "111422", "121124", "121421", "141122", "141221", |
"112214", "112412", "122114", "122411", "142112", "142211", "241211", "221114", "413111", "241112", "134111", "111242", "121142", "121241", "114212", "124112", "124211", "411212", |
"421112", "421211", "212141", "214121", "412121", "111143", "111341", "131141", "114113", "114311", "411113", "411311", "113141", "114131", "311141", "411131", "211412", "211214", |
"211232", "211133" }; |
/* EN 12323 Table 3 and Table 4 - Start patterns and stop patterns */ |
private static final String[] C16K_START_STOP = { "3211", "2221", "2122", "1411", "1132", "1231", "1114", "3112" }; |
/* EN 12323 Table 5 - Start and stop values defining row numbers */ |
private static final int[] C16K_START_VALUES = { 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7 }; |
private static final int[] C16K_STOP_VALUES = { 0, 1, 2, 3, 4, 5, 6, 7, 4, 5, 6, 7, 0, 1, 2, 3 }; |
private final Mode[] block_mode = new Mode[170]; /* RENAME block_mode */ |
private final int[] block_length = new int[170]; /* RENAME block_length */ |
private int block_count; |
@Override |
protected boolean gs1Supported() { |
return true; |
} |
@Override |
protected void encode() { |
// TODO: is it possible to share any of this code with Code128, which is more up to date? |
String width_pattern; |
int current_row, rows_needed, first_check, second_check; |
int indexchaine, pads_needed; |
char[] set, fset; |
Mode mode; |
char last_set, current_set; |
int i, j, k, m, read; |
int[] values; |
int bar_characters; |
double glyph_count; |
int first_sum, second_sum; |
int input_length; |
int c_count; |
boolean f_state; |
if (!this.content.matches("[\u0000-\u00FF]+")) { |
throw new OkapiException("Invalid characters in input data"); |
} |
this.inputData = toBytes(this.content, StandardCharsets.ISO_8859_1); |
input_length = this.inputData.length; |
bar_characters = 0; |
set = new char[160]; |
fset = new char[160]; |
values = new int[160]; |
if (input_length > 157) { |
throw new OkapiException("Input too long"); |
} |
/* Detect extended ASCII characters */ |
for (i = 0; i < input_length; i++) { |
if (this.inputData[i] >= 128) { |
fset[i] = 'f'; |
} else { |
fset[i] = ' '; |
} |
} |
/* Decide when to latch to extended mode */ |
for (i = 0; i < input_length; i++) { |
j = 0; |
if (fset[i] == 'f') { |
do { |
j++; |
} while (fset[i + j] == 'f'); |
if (j >= 5 || j >= 3 && i + j == input_length - 1) { |
for (k = 0; k <= j; k++) { |
fset[i + k] = 'F'; |
} |
} |
} |
} |
/* Decide if it is worth reverting to 646 encodation for a few characters */ |
if (input_length > 1) { |
for (i = 1; i < input_length; i++) { |
if (fset[i - 1] == 'F' && fset[i] == ' ') { |
/* Detected a change from 8859-1 to 646 - count how long for */ |
for (j = 0; fset[i + j] == ' ' && i + j < input_length; j++) { |
; |
} |
if (j < 5 || j < 3 && i + j == input_length - 1) { |
/* Change to shifting back rather than latching back */ |
for (k = 0; k < j; k++) { |
fset[i + k] = 'n'; |
} |
} |
} |
} |
} |
/* Detect mode A, B and C characters */ |
this.block_count = 0; |
indexchaine = 0; |
mode = findSubset(this.inputData[indexchaine]); |
if (this.inputData[indexchaine] == FNC1) { |
mode = Mode.ABORC; |
} /* FNC1 */ |
for (i = 0; i < 160; i++) { |
this.block_length[i] = 0; |
} |
do { |
this.block_mode[this.block_count] = mode; |
while (this.block_mode[this.block_count] == mode && indexchaine < input_length) { |
this.block_length[this.block_count]++; |
indexchaine++; |
if (indexchaine < input_length) { |
mode = findSubset(this.inputData[indexchaine]); |
if (this.inputData[indexchaine] == FNC1) { |
mode = Mode.ABORC; |
} /* FNC1 */ |
} |
} |
this.block_count++; |
} while (indexchaine < input_length); |
reduceSubsetChanges(this.block_count); |
/* Put set data into set[] */ |
read = 0; |
for (i = 0; i < this.block_count; i++) { |
for (j = 0; j < this.block_length[i]; j++) { |
switch (this.block_mode[i]) { |
case SHIFTA: |
set[read] = 'a'; |
break; |
case LATCHA: |
set[read] = 'A'; |
break; |
case SHIFTB: |
set[read] = 'b'; |
break; |
case LATCHB: |
set[read] = 'B'; |
break; |
case LATCHC: |
set[read] = 'C'; |
break; |
} |
read++; |
} |
} |
/* Adjust for strings which start with shift characters - make them latch instead */ |
if (set[0] == 'a') { |
i = 0; |
do { |
set[i] = 'A'; |
i++; |
} while (set[i] == 'a'); |
} |
if (set[0] == 'b') { |
i = 0; |
do { |
set[i] = 'B'; |
i++; |
} while (set[i] == 'b'); |
} |
/* Watch out for odd-length Mode C blocks */ |
c_count = 0; |
for (i = 0; i < read; i++) { |
if (set[i] == 'C') { |
if (this.inputData[i] == FNC1) { |
if ((c_count & 1) != 0) { |
if (i - c_count != 0) { |
set[i - c_count] = 'B'; |
} else { |
set[i - 1] = 'B'; |
} |
} |
c_count = 0; |
} else { |
c_count++; |
} |
} else { |
if ((c_count & 1) != 0) { |
if (i - c_count != 0) { |
set[i - c_count] = 'B'; |
} else { |
set[i - 1] = 'B'; |
} |
} |
c_count = 0; |
} |
} |
if ((c_count & 1) != 0) { |
if (i - c_count != 0) { |
set[i - c_count] = 'B'; |
} else { |
set[i - 1] = 'B'; |
} |
} |
for (i = 1; i < read - 1; i++) { |
if (set[i] == 'C' && set[i - 1] == 'B' && set[i + 1] == 'B') { |
set[i] = 'B'; |
} |
} |
/* Make sure the data will fit in the symbol */ |
last_set = ' '; |
glyph_count = 0.0; |
for (i = 0; i < input_length; i++) { |
if (set[i] == 'a' || set[i] == 'b') { |
glyph_count = glyph_count + 1.0; |
} |
if (fset[i] == 'f' || fset[i] == 'n') { |
glyph_count = glyph_count + 1.0; |
} |
if (set[i] == 'A' || set[i] == 'B' || set[i] == 'C') { |
if (set[i] != last_set) { |
last_set = set[i]; |
glyph_count = glyph_count + 1.0; |
} |
} |
if (i == 0) { |
if (set[i] == 'B' && set[1] == 'C') { |
glyph_count = glyph_count - 1.0; |
} |
if (set[i] == 'B' && set[1] == 'B' && set[2] == 'C') { |
glyph_count = glyph_count - 1.0; |
} |
if (fset[i] == 'F') { |
glyph_count = glyph_count + 2.0; |
} |
} else { |
if (fset[i] == 'F' && fset[i - 1] != 'F') { |
glyph_count = glyph_count + 2.0; |
} |
if (fset[i] != 'F' && fset[i - 1] == 'F') { |
glyph_count = glyph_count + 2.0; |
} |
} |
if (set[i] == 'C' && this.inputData[i] != FNC1) { |
glyph_count = glyph_count + 0.5; |
} else { |
glyph_count = glyph_count + 1.0; |
} |
} |
if (this.inputDataType == DataType.GS1 && set[0] != 'A') { |
/* FNC1 can be integrated with mode character */ |
glyph_count--; |
} |
if (glyph_count > 77.0) { |
throw new OkapiException("Input too long"); |
} |
/* Calculate how tall the symbol will be */ |
glyph_count = glyph_count + 2.0; |
i = (int) glyph_count; |
rows_needed = i / 5; |
if (i % 5 > 0) { |
rows_needed++; |
} |
if (rows_needed == 1) { |
rows_needed = 2; |
} |
/* start with the mode character - Table 2 */ |
m = 0; |
switch (set[0]) { |
case 'A': |
m = 0; |
break; |
case 'B': |
m = 1; |
break; |
case 'C': |
m = 2; |
break; |
} |
if (this.readerInit) { |
if (m == 2) { |
m = 5; |
} |
if (this.inputDataType == DataType.GS1) { |
throw new OkapiException("Cannot use both GS1 mode and Reader Initialisation"); |
} else { |
if (set[0] == 'B' && set[1] == 'C') { |
m = 6; |
} |
} |
values[bar_characters] = 7 * (rows_needed - 2) + m; /* see 4.3.4.2 */ |
values[bar_characters + 1] = 96; /* FNC3 */ |
bar_characters += 2; |
} else { |
if (this.inputDataType == DataType.GS1) { |
/* Integrate FNC1 */ |
switch (set[0]) { |
case 'B': |
m = 3; |
break; |
case 'C': |
m = 4; |
break; |
} |
} else { |
if (set[0] == 'B' && set[1] == 'C') { |
m = 5; |
} |
if (set[0] == 'B' && set[1] == 'B' && set[2] == 'C') { |
m = 6; |
} |
} |
} |
values[bar_characters] = 7 * (rows_needed - 2) + m; /* see 4.3.4.2 */ |
bar_characters++; |
// } |
current_set = set[0]; |
f_state = false; |
/* |
* f_state remembers if we are in Extended ASCII mode (value 1) or in ISO/IEC 646 mode |
* (value 0) |
*/ |
if (fset[0] == 'F') { |
switch (current_set) { |
case 'A': |
values[bar_characters] = 101; |
values[bar_characters + 1] = 101; |
break; |
case 'B': |
values[bar_characters] = 100; |
values[bar_characters + 1] = 100; |
break; |
} |
bar_characters += 2; |
f_state = true; |
} |
read = 0; |
/* Encode the data */ |
do { |
if (read != 0 && set[read] != set[read - 1]) { /* Latch different code set */ |
switch (set[read]) { |
case 'A': |
values[bar_characters] = 101; |
bar_characters++; |
current_set = 'A'; |
break; |
case 'B': |
values[bar_characters] = 100; |
bar_characters++; |
current_set = 'B'; |
break; |
case 'C': |
if (!(read == 1 && set[0] == 'B')) { /* Not Mode C/Shift B */ |
if (!(read == 2 && set[0] == 'B' && set[1] == 'B')) { |
/* Not Mode C/Double Shift B */ |
values[bar_characters] = 99; |
bar_characters++; |
} |
} |
current_set = 'C'; |
break; |
} |
} |
if (read != 0) { |
if (fset[read] == 'F' && !f_state) { |
/* Latch beginning of extended mode */ |
switch (current_set) { |
case 'A': |
values[bar_characters] = 101; |
values[bar_characters + 1] = 101; |
break; |
case 'B': |
values[bar_characters] = 100; |
values[bar_characters + 1] = 100; |
break; |
} |
bar_characters += 2; |
f_state = true; |
} |
if (fset[read] == ' ' && f_state) { |
/* Latch end of extended mode */ |
switch (current_set) { |
case 'A': |
values[bar_characters] = 101; |
values[bar_characters + 1] = 101; |
break; |
case 'B': |
values[bar_characters] = 100; |
values[bar_characters + 1] = 100; |
break; |
} |
bar_characters += 2; |
f_state = false; |
} |
} |
if (fset[i] == 'f' || fset[i] == 'n') { |
/* Shift extended mode */ |
switch (current_set) { |
case 'A': |
values[bar_characters] = 101; /* FNC 4 */ |
break; |
case 'B': |
values[bar_characters] = 100; /* FNC 4 */ |
break; |
} |
bar_characters++; |
} |
if (set[i] == 'a' || set[i] == 'b') { |
/* Insert shift character */ |
values[bar_characters] = 98; |
bar_characters++; |
} |
if (this.inputData[read] != FNC1) { |
switch (set[read]) { /* Encode data characters */ |
case 'A': |
case 'a': |
getValueSubsetA(this.inputData[read], values, bar_characters); |
bar_characters++; |
read++; |
break; |
case 'B': |
case 'b': |
getValueSubsetB(this.inputData[read], values, bar_characters); |
bar_characters++; |
read++; |
break; |
case 'C': |
getValueSubsetC(this.inputData[read], this.inputData[read + 1], values, bar_characters); |
bar_characters++; |
read += 2; |
break; |
} |
} else { |
values[bar_characters] = 102; |
bar_characters++; |
read++; |
} |
} while (read < input_length); |
pads_needed = 5 - (bar_characters + 2) % 5; |
if (pads_needed == 5) { |
pads_needed = 0; |
} |
if (bar_characters + pads_needed < 8) { |
pads_needed += 8 - (bar_characters + pads_needed); |
} |
for (i = 0; i < pads_needed; i++) { |
values[bar_characters] = 106; |
bar_characters++; |
} |
/* Calculate check digits */ |
first_sum = 0; |
second_sum = 0; |
for (i = 0; i < bar_characters; i++) { |
first_sum += (i + 2) * values[i]; |
second_sum += (i + 1) * values[i]; |
} |
first_check = first_sum % 107; |
second_sum += first_check * (bar_characters + 1); |
second_check = second_sum % 107; |
values[bar_characters] = first_check; |
values[bar_characters + 1] = second_check; |
bar_characters += 2; |
this.readable = ""; |
this.pattern = new String[rows_needed]; |
this.row_count = rows_needed; |
this.row_height = new int[rows_needed]; |
infoLine("Symbol Rows: " + rows_needed); |
infoLine("First Check Digit: " + first_check); |
infoLine("Second Check Digit: " + second_check); |
info("Codewords: "); |
for (current_row = 0; current_row < rows_needed; current_row++) { |
width_pattern = ""; |
width_pattern += C16K_START_STOP[C16K_START_VALUES[current_row]]; |
width_pattern += "1"; |
for (i = 0; i < 5; i++) { |
width_pattern += C16K_TABLE[values[current_row * 5 + i]]; |
infoSpace(values[current_row * 5 + i]); |
} |
width_pattern += C16K_START_STOP[C16K_STOP_VALUES[current_row]]; |
this.pattern[current_row] = width_pattern; |
this.row_height[current_row] = 10; |
} |
infoLine(); |
} |
private void getValueSubsetA(final int source, final int[] values, final int bar_chars) { |
if (source > 127) { |
if (source < 160) { |
values[bar_chars] = source + 64 - 128; |
} else { |
values[bar_chars] = source - 32 - 128; |
} |
} else { |
if (source < 32) { |
values[bar_chars] = source + 64; |
} else { |
values[bar_chars] = source - 32; |
} |
} |
} |
private void getValueSubsetB(final int source, final int[] values, final int bar_chars) { |
if (source > 127) { |
values[bar_chars] = source - 32 - 128; |
} else { |
values[bar_chars] = source - 32; |
} |
} |
private void getValueSubsetC(final int source_a, final int source_b, final int[] values, final int bar_chars) { |
int weight; |
weight = 10 * Character.getNumericValue(source_a) + Character.getNumericValue(source_b); |
values[bar_chars] = weight; |
} |
private Mode findSubset(final int letter) { |
Mode mode; |
if (letter <= 31) { |
mode = Mode.SHIFTA; |
} else if (letter >= 48 && letter <= 57) { |
mode = Mode.ABORC; |
} else if (letter <= 95) { |
mode = Mode.AORB; |
} else if (letter <= 127) { |
mode = Mode.SHIFTB; |
} else if (letter <= 159) { |
mode = Mode.SHIFTA; |
} else if (letter <= 223) { |
mode = Mode.AORB; |
} else { |
mode = Mode.SHIFTB; |
} |
return mode; |
} |
private void reduceSubsetChanges( |
final int block_count) { /* Implements rules from ISO 15417 Annex E */ |
int i, length; |
Mode current, last, next; |
for (i = 0; i < block_count; i++) { |
current = this.block_mode[i]; |
length = this.block_length[i]; |
if (i != 0) { |
last = this.block_mode[i - 1]; |
} else { |
last = Mode.NULL; |
} |
if (i != block_count - 1) { |
next = this.block_mode[i + 1]; |
} else { |
next = Mode.NULL; |
} |
if (i == 0) { /* first block */ |
if (block_count == 1 && length == 2 && current == Mode.ABORC) { /* Rule 1a */ |
this.block_mode[i] = Mode.LATCHC; |
} |
if (current == Mode.ABORC) { |
if (length >= 4) { /* Rule 1b */ |
this.block_mode[i] = Mode.LATCHC; |
} else { |
this.block_mode[i] = Mode.AORB; |
current = Mode.AORB; |
} |
} |
if (current == Mode.SHIFTA) { /* Rule 1c */ |
this.block_mode[i] = Mode.LATCHA; |
} |
if (current == Mode.AORB && next == Mode.SHIFTA) { /* Rule 1c */ |
this.block_mode[i] = Mode.LATCHA; |
current = Mode.LATCHA; |
} |
if (current == Mode.AORB) { /* Rule 1d */ |
this.block_mode[i] = Mode.LATCHB; |
} |
} else { |
if (current == Mode.ABORC && length >= 4) { /* Rule 3 */ |
this.block_mode[i] = Mode.LATCHC; |
current = Mode.LATCHC; |
} |
if (current == Mode.ABORC) { |
this.block_mode[i] = Mode.AORB; |
current = Mode.AORB; |
} |
if (current == Mode.AORB && last == Mode.LATCHA) { |
this.block_mode[i] = Mode.LATCHA; |
current = Mode.LATCHA; |
} |
if (current == Mode.AORB && last == Mode.LATCHB) { |
this.block_mode[i] = Mode.LATCHB; |
current = Mode.LATCHB; |
} |
if (current == Mode.AORB && next == Mode.SHIFTA) { |
this.block_mode[i] = Mode.LATCHA; |
current = Mode.LATCHA; |
} |
if (current == Mode.AORB && next == Mode.SHIFTB) { |
this.block_mode[i] = Mode.LATCHB; |
current = Mode.LATCHB; |
} |
if (current == Mode.AORB) { |
this.block_mode[i] = Mode.LATCHB; |
current = Mode.LATCHB; |
} |
if (current == Mode.SHIFTA && length > 1) { /* Rule 4 */ |
this.block_mode[i] = Mode.LATCHA; |
current = Mode.LATCHA; |
} |
if (current == Mode.SHIFTB && length > 1) { /* Rule 5 */ |
this.block_mode[i] = Mode.LATCHB; |
current = Mode.LATCHB; |
} |
if (current == Mode.SHIFTA && last == Mode.LATCHA) { |
this.block_mode[i] = Mode.LATCHA; |
current = Mode.LATCHA; |
} |
if (current == Mode.SHIFTB && last == Mode.LATCHB) { |
this.block_mode[i] = Mode.LATCHB; |
current = Mode.LATCHB; |
} |
if (current == Mode.SHIFTA && last == Mode.LATCHC) { |
this.block_mode[i] = Mode.LATCHA; |
current = Mode.LATCHA; |
} |
if (current == Mode.SHIFTB && last == Mode.LATCHC) { |
this.block_mode[i] = Mode.LATCHB; |
current = Mode.LATCHB; |
} |
} /* Rule 2 is implimented elsewhere, Rule 6 is implied */ |
} |
combineSubsetBlocks(block_count); |
} |
private void combineSubsetBlocks(int block_count) { |
int i, j; |
/* bring together same type blocks */ |
if (block_count > 1) { |
i = 1; |
while (i < block_count) { |
if (this.block_mode[i - 1] == this.block_mode[i]) { |
/* bring together */ |
this.block_length[i - 1] = this.block_length[i - 1] + this.block_length[i]; |
j = i + 1; |
/* decreace the list */ |
while (j < block_count) { |
this.block_length[j - 1] = this.block_length[j]; |
this.block_mode[j - 1] = this.block_mode[j]; |
j++; |
} |
block_count = block_count - 1; |
i--; |
} |
i++; |
} |
} |
} |
@Override |
protected void plotSymbol() { |
int xBlock, yBlock; |
int x, y, w, h; |
boolean black; |
this.rectangles.clear(); |
y = 1; |
h = 1; |
for (yBlock = 0; yBlock < this.row_count; yBlock++) { |
black = true; |
x = 15; |
for (xBlock = 0; xBlock < this.pattern[yBlock].length(); xBlock++) { |
if (black) { |
black = false; |
w = this.pattern[yBlock].charAt(xBlock) - '0'; |
if (this.row_height[yBlock] == -1) { |
h = this.default_height; |
} else { |
h = this.row_height[yBlock]; |
} |
if (w != 0 && h != 0) { |
final Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h); |
this.rectangles.add(rect); |
} |
if (x + w > this.symbol_width) { |
this.symbol_width = x + w; |
} |
} else { |
black = true; |
} |
x += this.pattern[yBlock].charAt(xBlock) - '0'; |
} |
y += h; |
if (y > this.symbol_height) { |
this.symbol_height = y; |
} |
/* Add bars between rows */ |
if (yBlock != this.row_count - 1) { |
final Rectangle2D.Double rect = new Rectangle2D.Double(15, y - 1, this.symbol_width - 15, 2); |
this.rectangles.add(rect); |
} |
} |
/* Add top and bottom binding bars */ |
final Rectangle2D.Double top = new Rectangle2D.Double(0, 0, this.symbol_width + 15, 2); |
this.rectangles.add(top); |
final Rectangle2D.Double bottom = new Rectangle2D.Double(0, y - 1, this.symbol_width + 15, 2); |
this.rectangles.add(bottom); |
this.symbol_width += 15; |
this.symbol_height += 1; |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/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/Pharmazentralnummer.java |
---|
New file |
0,0 → 1,76 |
/* |
* Copyright 2015 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
/** |
* PZN8 is a Code 39 based symbology used by the pharmaceutical industry in Germany. PZN8 encodes a |
* 7 digit number and includes a modulo-10 check digit. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class Pharmazentralnummer extends Symbol { |
/* |
* Pharmazentral Nummer is a Code 3 of 9 symbol with an extra check digit. Now generates PZN-8. |
*/ |
@Override |
protected void encode() { |
final int l = this.content.length(); |
String localstr; |
int zeroes, count = 0, check_digit; |
final Code3Of9 c = new Code3Of9(); |
if (l > 7) { |
throw new OkapiException("Input data too long"); |
} |
if (!this.content.matches("[0-9]+")) { |
throw new OkapiException("Invalid characters in input"); |
} |
localstr = "-"; |
zeroes = 7 - l + 1; |
for (int i = 1; i < zeroes; i++) { |
localstr += '0'; |
} |
localstr += this.content; |
for (int i = 1; i < 8; i++) { |
count += i * Character.getNumericValue(localstr.charAt(i)); |
} |
check_digit = count % 11; |
if (check_digit == 11) { |
check_digit = 0; |
} |
if (check_digit == 10) { |
throw new OkapiException("Not a valid PZN identifier"); |
} |
infoLine("Check Digit: " + check_digit); |
localstr += (char) (check_digit + '0'); |
c.setContent(localstr); |
this.readable = "PZN" + localstr; |
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/JapanPost.java |
---|
New file |
0,0 → 1,143 |
/* |
* Copyright 2014 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
import static uk.org.okapibarcode.util.Arrays.positionOf; |
import java.awt.geom.Rectangle2D; |
import java.util.Locale; |
/** |
* <p> |
* Implements the Japanese Postal Code symbology as used to encode address data for mail items in |
* Japan. Valid input characters are digits 0-9, characters A-Z and the dash (-) character. A |
* modulo-19 check digit is added and should not be included in the input data. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class JapanPost extends Symbol { |
private static final String[] JAPAN_TABLE = { "FFT", "FDA", "DFA", "FAD", "FTF", "DAF", "AFD", "ADF", "TFF", "FTT", "TFT", "DAT", "DTA", "ADT", "TDA", "ATD", "TAD", "TTF", "FFF" }; |
private static final char[] KASUT_SET = { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' }; |
private static final char[] CH_KASUT_SET = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' }; |
@Override |
protected void encode() { |
String dest; |
String inter; |
int i, sum, check; |
char c; |
this.content = this.content.toUpperCase(Locale.ENGLISH); |
if (!this.content.matches("[0-9A-Z\\-]+")) { |
throw new OkapiException("Invalid characters in data"); |
} |
inter = ""; |
for (i = 0; i < this.content.length() && inter.length() < 20; i++) { |
c = this.content.charAt(i); |
if (c >= '0' && c <= '9') { |
inter += c; |
} |
if (c == '-') { |
inter += c; |
} |
if (c >= 'A' && c <= 'J') { |
inter += 'a'; |
inter += CH_KASUT_SET[c - 'A']; |
} |
if (c >= 'K' && c <= 'O') { |
inter += 'b'; |
inter += CH_KASUT_SET[c - 'K']; |
} |
if (c >= 'U' && c <= 'Z') { |
inter += 'c'; |
inter += CH_KASUT_SET[c - 'U']; |
} |
} |
for (i = inter.length(); i < 20; i++) { |
inter += "d"; |
} |
dest = "FD"; |
sum = 0; |
for (i = 0; i < 20; i++) { |
dest += JAPAN_TABLE[positionOf(inter.charAt(i), KASUT_SET)]; |
sum += positionOf(inter.charAt(i), CH_KASUT_SET); |
} |
/* Calculate check digit */ |
check = 19 - sum % 19; |
if (check == 19) { |
check = 0; |
} |
dest += JAPAN_TABLE[positionOf(CH_KASUT_SET[check], KASUT_SET)]; |
dest += "DF"; |
infoLine("Encoding: " + dest); |
infoLine("Check Digit: " + check); |
this.readable = ""; |
this.pattern = new String[] { dest }; |
this.row_count = 1; |
this.row_height = new int[] { -1 }; |
} |
@Override |
protected void plotSymbol() { |
int xBlock; |
int x, y, w, h; |
this.rectangles.clear(); |
x = 0; |
w = 1; |
y = 0; |
h = 0; |
for (xBlock = 0; xBlock < this.pattern[0].length(); xBlock++) { |
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/AztecCode.java |
---|
New file |
0,0 → 1,1824 |
/* |
* Copyright 2014 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
import static java.nio.charset.StandardCharsets.US_ASCII; |
import static uk.org.okapibarcode.util.Arrays.insertArray; |
/** |
* <p> |
* Implements Aztec Code bar code symbology According to ISO/IEC 24778:2008. |
* |
* <p> |
* Aztec Code can encode 8-bit ISO 8859-1 (Latin-1) data (except 0x00 Null characters) up to a |
* maximum length of approximately 3800 numeric characters, 3000 alphabetic characters or 1900 bytes |
* of data in a two-dimensional matrix symbol. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class AztecCode extends Symbol { |
/* 27 x 27 data grid */ |
private static final int[] COMPACT_AZTEC_MAP = { 609, 608, 411, 413, 415, 417, 419, 421, 423, 425, 427, 429, 431, 433, 435, 437, 439, 441, 443, 445, 447, 449, 451, 453, 455, 457, 459, 607, 606, |
410, 412, 414, 416, 418, 420, 422, 424, 426, 428, 430, 432, 434, 436, 438, 440, 442, 444, 446, 448, 450, 452, 454, 456, 458, 605, 604, 409, 408, 243, 245, 247, 249, 251, 253, 255, 257, |
259, 261, 263, 265, 267, 269, 271, 273, 275, 277, 279, 281, 283, 460, 461, 603, 602, 407, 406, 242, 244, 246, 248, 250, 252, 254, 256, 258, 260, 262, 264, 266, 268, 270, 272, 274, 276, |
278, 280, 282, 462, 463, 601, 600, 405, 404, 241, 240, 107, 109, 111, 113, 115, 117, 119, 121, 123, 125, 127, 129, 131, 133, 135, 137, 139, 284, 285, 464, 465, 599, 598, 403, 402, 239, |
238, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 286, 287, 466, 467, 597, 596, 401, 400, 237, 236, 105, 104, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, |
23, 25, 27, 140, 141, 288, 289, 468, 469, 595, 594, 399, 398, 235, 234, 103, 102, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 142, 143, 290, 291, 470, 471, 593, 592, 397, 396, 233, |
232, 101, 100, 1, 1, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 0, 1, 28, 29, 144, 145, 292, 293, 472, 473, 591, 590, 395, 394, 231, 230, 99, 98, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 30, 31, |
146, 147, 294, 295, 474, 475, 589, 588, 393, 392, 229, 228, 97, 96, 2027, 1, 0, 0, 0, 0, 0, 0, 0, 1, 2007, 32, 33, 148, 149, 296, 297, 476, 477, 587, 586, 391, 390, 227, 226, 95, 94, 2026, |
1, 0, 1, 1, 1, 1, 1, 0, 1, 2008, 34, 35, 150, 151, 298, 299, 478, 479, 585, 584, 389, 388, 225, 224, 93, 92, 2025, 1, 0, 1, 0, 0, 0, 1, 0, 1, 2009, 36, 37, 152, 153, 300, 301, 480, 481, |
583, 582, 387, 386, 223, 222, 91, 90, 2024, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2010, 38, 39, 154, 155, 302, 303, 482, 483, 581, 580, 385, 384, 221, 220, 89, 88, 2023, 1, 0, 1, 0, 0, 0, 1, 0, 1, |
2011, 40, 41, 156, 157, 304, 305, 484, 485, 579, 578, 383, 382, 219, 218, 87, 86, 2022, 1, 0, 1, 1, 1, 1, 1, 0, 1, 2012, 42, 43, 158, 159, 306, 307, 486, 487, 577, 576, 381, 380, 217, 216, |
85, 84, 2021, 1, 0, 0, 0, 0, 0, 0, 0, 1, 2013, 44, 45, 160, 161, 308, 309, 488, 489, 575, 574, 379, 378, 215, 214, 83, 82, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 46, 47, 162, 163, 310, 311, 490, |
491, 573, 572, 377, 376, 213, 212, 81, 80, 0, 0, 2020, 2019, 2018, 2017, 2016, 2015, 2014, 0, 0, 48, 49, 164, 165, 312, 313, 492, 493, 571, 570, 375, 374, 211, 210, 78, 76, 74, 72, 70, 68, |
66, 64, 62, 60, 58, 56, 54, 50, 51, 166, 167, 314, 315, 494, 495, 569, 568, 373, 372, 209, 208, 79, 77, 75, 73, 71, 69, 67, 65, 63, 61, 59, 57, 55, 52, 53, 168, 169, 316, 317, 496, 497, |
567, 566, 371, 370, 206, 204, 202, 200, 198, 196, 194, 192, 190, 188, 186, 184, 182, 180, 178, 176, 174, 170, 171, 318, 319, 498, 499, 565, 564, 369, 368, 207, 205, 203, 201, 199, 197, |
195, 193, 191, 189, 187, 185, 183, 181, 179, 177, 175, 172, 173, 320, 321, 500, 501, 563, 562, 366, 364, 362, 360, 358, 356, 354, 352, 350, 348, 346, 344, 342, 340, 338, 336, 334, 332, |
330, 328, 326, 322, 323, 502, 503, 561, 560, 367, 365, 363, 361, 359, 357, 355, 353, 351, 349, 347, 345, 343, 341, 339, 337, 335, 333, 331, 329, 327, 324, 325, 504, 505, 558, 556, 554, |
552, 550, 548, 546, 544, 542, 540, 538, 536, 534, 532, 530, 528, 526, 524, 522, 520, 518, 516, 514, 512, 510, 506, 507, 559, 557, 555, 553, 551, 549, 547, 545, 543, 541, 539, 537, 535, |
533, 531, 529, 527, 525, 523, 521, 519, 517, 515, 513, 511, 508, 509 }; |
private static final int[][] AZTEC_MAP = new int[151][151]; |
/* |
* From Table 2: |
* |
* 1 = upper 2 = lower 4 = mixed 8 = punctuation 16 = digits 32 = binary |
* |
* Values can be OR'ed, so e.g. 12 = 4 | 8, and 23 = 1 | 2 | 4 | 16 |
*/ |
private static final int[] AZTEC_CODE_SET = { 32, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 12, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 4, 4, 4, 4, 4, 23, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, |
24, 8, 24, 8, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 8, 8, 8, 8, 8, 8, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 8, 4, 8, 4, 4, 4, 2, 2, 2, 2, 2, 2, |
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 8, 4, 8, 4, 4 }; |
/* From Table 2 */ |
private static final int[] AZTEC_SYMBOL_CHAR = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 300, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 15, 16, 17, 18, 19, 1, 6, 7, 8, 9, 10, 11, 12, |
13, 14, 15, 16, 301, 18, 302, 20, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 21, 22, 23, 24, 25, 26, 20, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, |
27, 27, 21, 28, 22, 23, 24, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 29, 25, 30, 26, 27 }; |
/* |
* Problem characters are: 300: Carriage Return (ASCII 13) 301: Comma (ASCII 44) 302: Full Stop |
* (ASCII 46) |
*/ |
private static final String[] PENTBIT = { "00000", "00001", "00010", "00011", "00100", "00101", "00110", "00111", "01000", "01001", "01010", "01011", "01100", "01101", "01110", "01111", "10000", |
"10001", "10010", "10011", "10100", "10101", "10110", "10111", "11000", "11001", "11010", "11011", "11100", "11101", "11110", "11111" }; |
private static final String[] QUADBIT = { "0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111" }; |
private static final String[] TRIBIT = { "000", "001", "010", "011", "100", "101", "110", "111" }; |
/* Codewords per symbol */ |
private static final int[] AZTEC_SIZES = { 21, 48, 60, 88, 120, 156, 196, 240, 230, 272, 316, 364, 416, 470, 528, 588, 652, 720, 790, 864, 940, 1020, 920, 992, 1066, 1144, 1224, 1306, 1392, 1480, |
1570, 1664 }; |
private static final int[] AZTEC_COMPACT_SIZES = { 17, 40, 51, 76 }; |
/* Data bits per symbol maximum with 10% error correction */ |
private static final int[] AZTEC_10_DATA_SIZES = { 96, 246, 408, 616, 840, 1104, 1392, 1704, 2040, 2420, 2820, 3250, 3720, 4200, 4730, 5270, 5840, 6450, 7080, 7750, 8430, 9150, 9900, 10680, 11484, |
12324, 13188, 14076, 15000, 15948, 16920, 17940 }; |
/* Data bits per symbol maximum with 23% error correction */ |
private static final int[] AZTEC_23_DATA_SIZES = { 84, 204, 352, 520, 720, 944, 1184, 1456, 1750, 2070, 2410, 2780, 3180, 3590, 4040, 4500, 5000, 5520, 6060, 6630, 7210, 7830, 8472, 9132, 9816, |
10536, 11280, 12036, 12828, 13644, 14472, 15348 }; |
/* Data bits per symbol maximum with 36% error correction */ |
private static final int[] AZTEC_36_DATA_SIZES = { 66, 168, 288, 432, 592, 776, 984, 1208, 1450, 1720, 2000, 2300, 2640, 2980, 3350, 3740, 4150, 4580, 5030, 5500, 5990, 6500, 7032, 7584, 8160, |
8760, 9372, 9996, 10656, 11340, 12024, 12744 }; |
/* Data bits per symbol maximum with 50% error correction */ |
private static final int[] AZTEC_50_DATA_SIZES = { 48, 126, 216, 328, 456, 600, 760, 936, 1120, 1330, 1550, 1790, 2050, 2320, 2610, 2910, 3230, 3570, 3920, 4290, 4670, 5070, 5484, 5916, 6360, |
6828, 7308, 7800, 8316, 8844, 9384, 9948 }; |
private static final int[] AZTEC_COMPACT_10_DATA_SIZES = { 78, 198, 336, 520 }; |
private static final int[] AZTEC_COMPACT_23_DATA_SIZES = { 66, 168, 288, 440 }; |
private static final int[] AZTEC_COMPACT_36_DATA_SIZES = { 48, 138, 232, 360 }; |
private static final int[] AZTEC_COMPACT_50_DATA_SIZES = { 36, 102, 176, 280 }; |
private static final int[] AZTEC_OFFSET = { 66, 64, 62, 60, 57, 55, 53, 51, 49, 47, 45, 42, 40, 38, 36, 34, 32, 30, 28, 25, 23, 21, 19, 17, 15, 13, 10, 8, 6, 4, 2, 0 }; |
private static final int[] AZTEC_COMPACT_OFFSET = { 6, 4, 2, 0 }; |
/* Initialize AZTEC_MAP */ |
static { |
int layer, start, length, n, i; |
int x, y; |
for (x = 0; x < 151; x++) { |
for (y = 0; y < 151; y++) { |
AZTEC_MAP[x][y] = 0; |
} |
} |
for (layer = 1; layer < 33; layer++) { |
start = 112 * (layer - 1) + 16 * (layer - 1) * (layer - 1) + 2; |
length = 28 + (layer - 1) * 4 + layer * 4; |
/* Top */ |
i = 0; |
x = 64 - (layer - 1) * 2; |
y = 63 - (layer - 1) * 2; |
for (n = start; n < start + length; n += 2) { |
AZTEC_MAP[avoidReferenceGrid(x + i)][avoidReferenceGrid(y)] = n; |
AZTEC_MAP[avoidReferenceGrid(x + i)][avoidReferenceGrid(y - 1)] = n + 1; |
i++; |
} |
/* Right */ |
i = 0; |
x = 78 + (layer - 1) * 2; |
y = 64 - (layer - 1) * 2; |
for (n = start + length; n < start + length * 2; n += 2) { |
AZTEC_MAP[avoidReferenceGrid(x)][avoidReferenceGrid(y + i)] = n; |
AZTEC_MAP[avoidReferenceGrid(x + 1)][avoidReferenceGrid(y + i)] = n + 1; |
i++; |
} |
/* Bottom */ |
i = 0; |
x = 77 + (layer - 1) * 2; |
y = 78 + (layer - 1) * 2; |
for (n = start + length * 2; n < start + length * 3; n += 2) { |
AZTEC_MAP[avoidReferenceGrid(x - i)][avoidReferenceGrid(y)] = n; |
AZTEC_MAP[avoidReferenceGrid(x - i)][avoidReferenceGrid(y + 1)] = n + 1; |
i++; |
} |
/* Left */ |
i = 0; |
x = 63 - (layer - 1) * 2; |
y = 77 + (layer - 1) * 2; |
for (n = start + length * 3; n < start + length * 4; n += 2) { |
AZTEC_MAP[avoidReferenceGrid(x)][avoidReferenceGrid(y - i)] = n; |
AZTEC_MAP[avoidReferenceGrid(x - 1)][avoidReferenceGrid(y - i)] = n + 1; |
i++; |
} |
} |
/* Central finder pattern */ |
for (y = 69; y <= 81; y++) { |
for (x = 69; x <= 81; x++) { |
AZTEC_MAP[x][y] = 1; |
} |
} |
for (y = 70; y <= 80; y++) { |
for (x = 70; x <= 80; x++) { |
AZTEC_MAP[x][y] = 0; |
} |
} |
for (y = 71; y <= 79; y++) { |
for (x = 71; x <= 79; x++) { |
AZTEC_MAP[x][y] = 1; |
} |
} |
for (y = 72; y <= 78; y++) { |
for (x = 72; x <= 78; x++) { |
AZTEC_MAP[x][y] = 0; |
} |
} |
for (y = 73; y <= 77; y++) { |
for (x = 73; x <= 77; x++) { |
AZTEC_MAP[x][y] = 1; |
} |
} |
for (y = 74; y <= 76; y++) { |
for (x = 74; x <= 76; x++) { |
AZTEC_MAP[x][y] = 0; |
} |
} |
/* Guide bars */ |
for (y = 11; y < 151; y += 16) { |
for (x = 1; x < 151; x += 2) { |
AZTEC_MAP[x][y] = 1; |
AZTEC_MAP[y][x] = 1; |
} |
} |
/* Descriptor */ |
for (i = 0; i < 10; i++) { /* Top */ |
AZTEC_MAP[avoidReferenceGrid(66 + i)][avoidReferenceGrid(64)] = 20000 + i; |
} |
for (i = 0; i < 10; i++) { /* Right */ |
AZTEC_MAP[avoidReferenceGrid(77)][avoidReferenceGrid(66 + i)] = 20010 + i; |
} |
for (i = 0; i < 10; i++) { /* Bottom */ |
AZTEC_MAP[avoidReferenceGrid(75 - i)][avoidReferenceGrid(77)] = 20020 + i; |
} |
for (i = 0; i < 10; i++) { /* Left */ |
AZTEC_MAP[avoidReferenceGrid(64)][avoidReferenceGrid(75 - i)] = 20030 + i; |
} |
/* Orientation */ |
AZTEC_MAP[avoidReferenceGrid(64)][avoidReferenceGrid(64)] = 1; |
AZTEC_MAP[avoidReferenceGrid(65)][avoidReferenceGrid(64)] = 1; |
AZTEC_MAP[avoidReferenceGrid(64)][avoidReferenceGrid(65)] = 1; |
AZTEC_MAP[avoidReferenceGrid(77)][avoidReferenceGrid(64)] = 1; |
AZTEC_MAP[avoidReferenceGrid(77)][avoidReferenceGrid(65)] = 1; |
AZTEC_MAP[avoidReferenceGrid(77)][avoidReferenceGrid(76)] = 1; |
} |
private static int avoidReferenceGrid(final int input) { |
int output = input; |
if (output > 10) { |
output++; |
} |
if (output > 26) { |
output++; |
} |
if (output > 42) { |
output++; |
} |
if (output > 58) { |
output++; |
} |
if (output > 74) { |
output++; |
} |
if (output > 90) { |
output++; |
} |
if (output > 106) { |
output++; |
} |
if (output > 122) { |
output++; |
} |
if (output > 138) { |
output++; |
} |
return output; |
} |
private int preferredSize = 0; |
private int preferredEccLevel = 2; |
private String structuredAppendMessageId; |
private int structuredAppendPosition = 1; |
private int structuredAppendTotal = 1; |
/** |
* <p> |
* Sets a preferred symbol size. This value may be ignored if data string is too large to fit in |
* the specified symbol size. Values correspond to symbol sizes as shown in the following table: |
* |
* <table summary="Available Aztec Code symbol sizes"> |
* <tbody> |
* <tr> |
* <th>Input</th> |
* <th>Symbol Size</th> |
* <th>Input</th> |
* <th>Symbol Size</th> |
* </tr> |
* <tr> |
* <td>1</td> |
* <td>15 x 15</td> |
* <td>19</td> |
* <td>79 x 79</td> |
* </tr> |
* <tr> |
* <td>2</td> |
* <td>19 x 19</td> |
* <td>20</td> |
* <td>83 x 83</td> |
* </tr> |
* <tr> |
* <td>3</td> |
* <td>23 x 23</td> |
* <td>21</td> |
* <td>87 x 87</td> |
* </tr> |
* <tr> |
* <td>4</td> |
* <td>27 x 27</td> |
* <td>22</td> |
* <td>91 x 91</td> |
* </tr> |
* <tr> |
* <td>5</td> |
* <td>19 x 19</td> |
* <td>23</td> |
* <td>95 x 95</td> |
* </tr> |
* <tr> |
* <td>6</td> |
* <td>23 x 23</td> |
* <td>24</td> |
* <td>101 x 101</td> |
* </tr> |
* <tr> |
* <td>7</td> |
* <td>27 x 27</td> |
* <td>25</td> |
* <td>105 x 105</td> |
* </tr> |
* <tr> |
* <td>8</td> |
* <td>31 x 31</td> |
* <td>26</td> |
* <td>109 x 109</td> |
* </tr> |
* <tr> |
* <td>9</td> |
* <td>37 x 37</td> |
* <td>27</td> |
* <td>113 x 113</td> |
* </tr> |
* <tr> |
* <td>10</td> |
* <td>41 x 41</td> |
* <td>28</td> |
* <td>117 x 117</td> |
* </tr> |
* <tr> |
* <td>11</td> |
* <td>45 x 45</td> |
* <td>29</td> |
* <td>121 x 121</td> |
* </tr> |
* <tr> |
* <td>12</td> |
* <td>49 x 49</td> |
* <td>30</td> |
* <td>125 x 125</td> |
* </tr> |
* <tr> |
* <td>13</td> |
* <td>53 x 53</td> |
* <td>31</td> |
* <td>131 x 131</td> |
* </tr> |
* <tr> |
* <td>14</td> |
* <td>57 x 57</td> |
* <td>32</td> |
* <td>135 x 135</td> |
* </tr> |
* <tr> |
* <td>15</td> |
* <td>61 x 61</td> |
* <td>33</td> |
* <td>139 x 139</td> |
* </tr> |
* <tr> |
* <td>16</td> |
* <td>67 x 67</td> |
* <td>34</td> |
* <td>143 x 143</td> |
* </tr> |
* <tr> |
* <td>17</td> |
* <td>71 x 71</td> |
* <td>35</td> |
* <td>147 x 147</td> |
* </tr> |
* <tr> |
* <td>18</td> |
* <td>75 x 75</td> |
* <td>36</td> |
* <td>151 x 151</td> |
* </tr> |
* </tbody> |
* </table> |
* |
* <p> |
* Note that sizes 1 to 4 are the "compact" Aztec Code symbols; sizes 5 to 36 are the |
* "full-range" Aztec Code symbols. |
* |
* @param size an integer in the range 1 - 36 |
*/ |
public void setPreferredSize(final int size) { |
if (size < 1 || size > 36) { |
throw new IllegalArgumentException("Invalid size: " + size); |
} |
this.preferredSize = size; |
} |
/** |
* Returns the preferred symbol size. |
* |
* @return the preferred symbol size |
*/ |
public int getPreferredSize() { |
return this.preferredSize; |
} |
/** |
* Sets the preferred minimum amount of symbol space dedicated to error correction. This value |
* will be ignored if a symbol size has been set by <code>setPreferredSize</code>. Valid options |
* are: |
* |
* <table summary="Error correction options"> |
* <tbody> |
* <tr> |
* <th>Mode</th> |
* <th>Error Correction Capacity</th> |
* </tr> |
* <tr> |
* <td>1</td> |
* <td>> 10% + 3 codewords</td> |
* </tr> |
* <tr> |
* <td>2</td> |
* <td>> 23% + 3 codewords</td> |
* </tr> |
* <tr> |
* <td>3</td> |
* <td>> 36% + 3 codewords</td> |
* </tr> |
* <tr> |
* <td>4</td> |
* <td>> 50% + 3 codewords</td> |
* </tr> |
* </tbody> |
* </table> |
* |
* @param eccLevel an integer in the range 1 - 4 |
*/ |
public void setPreferredEccLevel(final int eccLevel) { |
if (eccLevel < 1 || eccLevel > 4) { |
throw new IllegalArgumentException("Invalid ECC level: " + eccLevel); |
} |
this.preferredEccLevel = eccLevel; |
} |
/** |
* Returns the preferred error correction level. |
* |
* @return the preferred error correction level |
*/ |
public int getPreferredEccLevel() { |
return this.preferredEccLevel; |
} |
/** |
* If this Aztec Code symbol is part of a series of Aztec Code symbols appended in a structured |
* format, this method sets the position of this symbol in the series. Valid values are 1 |
* through 26 inclusive. |
* |
* @param position the position of this Aztec Code symbol in the structured append series |
*/ |
public void setStructuredAppendPosition(final int position) { |
if (position < 1 || position > 26) { |
throw new IllegalArgumentException("Invalid Aztec Code structured append position: " + position); |
} |
this.structuredAppendPosition = position; |
} |
/** |
* Returns the position of this Aztec Code symbol in a series of symbols using structured |
* append. If this symbol is not part of such a series, this method will return <code>1</code>. |
* |
* @return the position of this Aztec Code symbol in a series of symbols using structured append |
*/ |
public int getStructuredAppendPosition() { |
return this.structuredAppendPosition; |
} |
/** |
* If this Aztec Code symbol is part of a series of Aztec Code symbols appended in a structured |
* format, this method sets the total number of symbols in the series. Valid values are 1 |
* through 26 inclusive. A value of 1 indicates that this symbol is not part of a structured |
* append series. |
* |
* @param total the total number of Aztec Code symbols in the structured append series |
*/ |
public void setStructuredAppendTotal(final int total) { |
if (total < 1 || total > 26) { |
throw new IllegalArgumentException("Invalid Aztec Code structured append total: " + total); |
} |
this.structuredAppendTotal = total; |
} |
/** |
* Returns the size of the series of Aztec Code symbols using structured append that this symbol |
* is part of. If this symbol is not part of a structured append series, this method will return |
* <code>1</code>. |
* |
* @return size of the series that this symbol is part of |
*/ |
public int getStructuredAppendTotal() { |
return this.structuredAppendTotal; |
} |
/** |
* If this Aztec Code symbol is part of a series of Aztec Code symbols appended in a structured |
* format, this method sets the unique message ID for the series. Values may not contain spaces |
* and must contain only printable ASCII characters. Message IDs are optional. |
* |
* @param messageId the unique message ID for the series that this symbol is part of |
*/ |
public void setStructuredAppendMessageId(final String messageId) { |
if (messageId != null && !messageId.matches("^[\\x21-\\x7F]+$")) { |
throw new IllegalArgumentException("Invalid Aztec Code structured append message ID: " + messageId); |
} |
this.structuredAppendMessageId = messageId; |
} |
/** |
* Returns the unique message ID of the series of Aztec Code symbols using structured append |
* that this symbol is part of. If this symbol is not part of a structured append series, this |
* method will return <code>null</code>. |
* |
* @return the unique message ID for the series that this symbol is part of |
*/ |
public String getStructuredAppendMessageId() { |
return this.structuredAppendMessageId; |
} |
@Override |
protected boolean gs1Supported() { |
return true; |
} |
@Override |
protected void encode() { |
int layers; |
boolean compact; |
StringBuilder adjustedString; |
if (this.inputDataType == DataType.GS1 && this.readerInit) { |
throw new OkapiException("Cannot encode in GS1 and Reader Initialisation mode at the same time"); |
} |
eciProcess(); // Get ECI mode |
/* Optional structured append (Section 8 of spec) */ |
/* ML + UL start flag handled later, not part of data */ |
if (this.structuredAppendTotal != 1) { |
final StringBuilder prefix = new StringBuilder(); |
if (this.structuredAppendMessageId != null) { |
prefix.append(' ').append(this.structuredAppendMessageId).append(' '); |
} |
prefix.append((char) (this.structuredAppendPosition + 64)); // 1-26 as A-Z |
prefix.append((char) (this.structuredAppendTotal + 64)); // 1-26 as A-Z |
final int[] prefixArray = toBytes(prefix.toString(), US_ASCII); |
this.inputData = insertArray(this.inputData, 0, prefixArray); |
} |
final String binaryString = generateAztecBinary(); |
int dataLength = binaryString.length(); |
if (this.preferredSize == 0) { |
/* The size of the symbol can be determined by Okapi */ |
int dataMaxSize = 0; |
final int compLoop = this.readerInit ? 1 : 4; |
do { |
/* Decide what size symbol to use - the smallest that fits the data */ |
int[] dataSizes; |
int[] compactDataSizes; |
switch (this.preferredEccLevel) { |
/* |
* For each level of error correction work out the smallest symbol which the data |
* will fit in |
*/ |
case 1: |
dataSizes = AZTEC_10_DATA_SIZES; |
compactDataSizes = AZTEC_COMPACT_10_DATA_SIZES; |
break; |
case 2: |
dataSizes = AZTEC_23_DATA_SIZES; |
compactDataSizes = AZTEC_COMPACT_23_DATA_SIZES; |
break; |
case 3: |
dataSizes = AZTEC_36_DATA_SIZES; |
compactDataSizes = AZTEC_COMPACT_36_DATA_SIZES; |
break; |
case 4: |
dataSizes = AZTEC_50_DATA_SIZES; |
compactDataSizes = AZTEC_COMPACT_50_DATA_SIZES; |
break; |
default: |
throw new OkapiException("Unrecognized ECC level: " + this.preferredEccLevel); |
} |
layers = 0; |
compact = false; |
for (int i = 32; i > 0; i--) { |
if (dataLength < dataSizes[i - 1]) { |
layers = i; |
compact = false; |
dataMaxSize = dataSizes[i - 1]; |
} |
} |
for (int i = compLoop; i > 0; i--) { |
if (dataLength < compactDataSizes[i - 1]) { |
layers = i; |
compact = true; |
dataMaxSize = compactDataSizes[i - 1]; |
} |
} |
if (layers == 0) { |
/* Couldn't find a symbol which fits the data */ |
throw new OkapiException("Input too long (too many bits for selected ECC)"); |
} |
adjustedString = adjustBinaryString(binaryString, compact, layers); |
dataLength = adjustedString.length(); |
} while (dataLength > dataMaxSize); |
/* |
* This loop will only repeat on the rare occasions when the rule about not having all |
* 1s or all 0s means that the binary string has had to be lengthened beyond the maximum |
* number of bits that can be encoded in a symbol of the selected size |
*/ |
} else { |
/* The size of the symbol has been specified by the user */ |
if (this.preferredSize >= 1 && this.preferredSize <= 4) { |
compact = true; |
layers = this.preferredSize; |
} else { |
compact = false; |
layers = this.preferredSize - 4; |
} |
adjustedString = adjustBinaryString(binaryString, compact, layers); |
/* Check if the data actually fits into the selected symbol size */ |
final int codewordSize = getCodewordSize(layers); |
final int[] sizes = compact ? AZTEC_COMPACT_SIZES : AZTEC_SIZES; |
final int dataMaxSize = codewordSize * (sizes[layers - 1] - 3); |
if (adjustedString.length() > dataMaxSize) { |
throw new OkapiException("Data too long for specified Aztec Code symbol size"); |
} |
} |
if (this.readerInit && compact && layers > 1) { |
throw new OkapiException("Symbol is too large for reader initialization"); |
} |
if (this.readerInit && layers > 22) { |
throw new OkapiException("Symbol is too large for reader initialization"); |
} |
final int codewordSize = getCodewordSize(layers); |
final int dataBlocks = adjustedString.length() / codewordSize; |
int eccBlocks; |
if (compact) { |
eccBlocks = AZTEC_COMPACT_SIZES[layers - 1] - dataBlocks; |
} else { |
eccBlocks = AZTEC_SIZES[layers - 1] - dataBlocks; |
} |
infoLine("Compact Mode: " + compact); |
infoLine("Layers: " + layers); |
infoLine("Codeword Length: " + codewordSize + " bits"); |
infoLine("Data Codewords: " + dataBlocks); |
infoLine("ECC Codewords: " + eccBlocks); |
/* Add ECC data to the adjusted string */ |
addErrorCorrection(adjustedString, codewordSize, dataBlocks, eccBlocks); |
/* Invert the data so that actual data is on the outside and reed-solomon on the inside */ |
for (int i = 0; i < adjustedString.length() / 2; i++) { |
final int mirror = adjustedString.length() - i - 1; |
final char c = adjustedString.charAt(i); |
adjustedString.setCharAt(i, adjustedString.charAt(mirror)); |
adjustedString.setCharAt(mirror, c); |
} |
/* Create the descriptor / mode message */ |
final String descriptor = createDescriptor(compact, layers, dataBlocks); |
/* Plot all of the data into the symbol in pre-defined spiral pattern */ |
if (compact) { |
this.readable = ""; |
this.row_count = 27 - 2 * AZTEC_COMPACT_OFFSET[layers - 1]; |
this.row_height = new int[this.row_count]; |
this.row_height[0] = -1; |
this.pattern = new String[this.row_count]; |
for (int y = AZTEC_COMPACT_OFFSET[layers - 1]; y < 27 - AZTEC_COMPACT_OFFSET[layers - 1]; y++) { |
final StringBuilder bin = new StringBuilder(27); |
for (int x = AZTEC_COMPACT_OFFSET[layers - 1]; x < 27 - AZTEC_COMPACT_OFFSET[layers - 1]; x++) { |
final int j = COMPACT_AZTEC_MAP[y * 27 + x]; |
if (j == 0) { |
bin.append('0'); |
} |
if (j == 1) { |
bin.append('1'); |
} |
if (j >= 2) { |
if (j - 2 < adjustedString.length()) { |
bin.append(adjustedString.charAt(j - 2)); |
} else { |
if (j >= 2000) { |
bin.append(descriptor.charAt(j - 2000)); |
} else { |
bin.append('0'); |
} |
} |
} |
} |
this.row_height[y - AZTEC_COMPACT_OFFSET[layers - 1]] = 1; |
this.pattern[y - AZTEC_COMPACT_OFFSET[layers - 1]] = bin2pat(bin); |
} |
} else { |
this.readable = ""; |
this.row_count = 151 - 2 * AZTEC_OFFSET[layers - 1]; |
this.row_height = new int[this.row_count]; |
this.row_height[0] = -1; |
this.pattern = new String[this.row_count]; |
for (int y = AZTEC_OFFSET[layers - 1]; y < 151 - AZTEC_OFFSET[layers - 1]; y++) { |
final StringBuilder bin = new StringBuilder(151); |
for (int x = AZTEC_OFFSET[layers - 1]; x < 151 - AZTEC_OFFSET[layers - 1]; x++) { |
final int j = AZTEC_MAP[x][y]; |
if (j == 1) { |
bin.append('1'); |
} |
if (j == 0) { |
bin.append('0'); |
} |
if (j >= 2) { |
if (j - 2 < adjustedString.length()) { |
bin.append(adjustedString.charAt(j - 2)); |
} else { |
if (j >= 20000) { |
bin.append(descriptor.charAt(j - 20000)); |
} else { |
bin.append('0'); |
} |
} |
} |
} |
this.row_height[y - AZTEC_OFFSET[layers - 1]] = 1; |
this.pattern[y - AZTEC_OFFSET[layers - 1]] = bin2pat(bin); |
} |
} |
} |
private String generateAztecBinary() { |
/* Encode input data into a binary string */ |
int i, j, k, bytes; |
int curtable, newtable, lasttable, chartype, maplength, blocks; |
final int[] charmap = new int[2 * this.inputData.length]; |
final int[] typemap = new int[2 * this.inputData.length]; |
final int[] blockType = new int[this.inputData.length + 1]; |
final int[] blockLength = new int[this.inputData.length + 1]; |
/* Lookup input string in encoding table */ |
maplength = 0; |
/* Add FNC1 to beginning of GS1 messages */ |
if (this.inputDataType == DataType.GS1) { |
charmap[maplength] = 0; // FLG |
typemap[maplength++] = 8; // PUNC |
charmap[maplength] = 400; // (0) |
typemap[maplength++] = 8; // PUNC |
} |
if (this.eciMode != 3) { |
int flagNumber; |
charmap[maplength] = 0; // FLG |
typemap[maplength++] = 8; // PUNC |
flagNumber = 6; |
if (this.eciMode < 100000) { |
flagNumber = 5; |
} |
if (this.eciMode < 10000) { |
flagNumber = 4; |
} |
if (this.eciMode < 1000) { |
flagNumber = 3; |
} |
if (this.eciMode < 100) { |
flagNumber = 2; |
} |
if (this.eciMode < 10) { |
flagNumber = 1; |
} |
charmap[maplength] = 400 + flagNumber; |
typemap[maplength++] = 8; // PUNC |
} |
for (i = 0; i < this.inputData.length; i++) { |
if (this.inputData[i] == FNC1) { |
/* FNC1 represented by FLG(0) */ |
charmap[maplength] = 0; // FLG |
typemap[maplength++] = 8; // PUNC |
charmap[maplength] = 400; // (0) |
typemap[maplength++] = 8; // PUNC |
} else { |
if (this.inputData[i] > 0x7F || this.inputData[i] == 0x00) { |
charmap[maplength] = this.inputData[i]; |
typemap[maplength++] = 32; // BINARY |
} else { |
charmap[maplength] = AZTEC_SYMBOL_CHAR[this.inputData[i]]; |
typemap[maplength++] = AZTEC_CODE_SET[this.inputData[i]]; |
} |
} |
} |
/* Look for double character encoding possibilities */ |
for (i = 0; i < maplength - 1; i++) { |
if (charmap[i] == 300 && charmap[i + 1] == 11 && typemap[i] == 12 && typemap[i + 1] == 4) { |
/* CR LF combination */ |
charmap[i] = 2; |
typemap[i] = 8; // PUNC |
if (i + 1 != maplength) { |
for (j = i + 1; j < maplength; j++) { |
charmap[j] = charmap[j + 1]; |
typemap[j] = typemap[j + 1]; |
} |
} |
maplength--; |
} |
if (charmap[i] == 302 && charmap[i + 1] == 1 && typemap[i] == 24 && typemap[i + 1] == 23) { |
/* . SP combination */ |
charmap[i] = 3; |
typemap[i] = 8; // PUNC; |
if (i + 1 != maplength) { |
for (j = i + 1; j < maplength; j++) { |
charmap[j] = charmap[j + 1]; |
typemap[j] = typemap[j + 1]; |
} |
} |
maplength--; |
} |
if (charmap[i] == 301 && charmap[i + 1] == 1 && typemap[i] == 24 && typemap[i + 1] == 23) { |
/* , SP combination */ |
charmap[i] = 4; |
typemap[i] = 8; // PUNC; |
if (i + 1 != maplength) { |
for (j = i + 1; j < maplength; j++) { |
charmap[j] = charmap[j + 1]; |
typemap[j] = typemap[j + 1]; |
} |
} |
maplength--; |
} |
if (charmap[i] == 21 && charmap[i + 1] == 1 && typemap[i] == 8 && typemap[i + 1] == 23) { |
/* : SP combination */ |
charmap[i] = 5; |
typemap[i] = 8; // PUNC; |
if (i + 1 != maplength) { |
for (j = i + 1; j < maplength; j++) { |
charmap[j] = charmap[j + 1]; |
typemap[j] = typemap[j + 1]; |
} |
} |
maplength--; |
} |
} |
/* look for blocks of characters which use the same table */ |
blocks = 0; |
for (i = 0; i < maplength; i++) { |
if (i > 0 && typemap[i] == typemap[i - 1]) { |
blockLength[blocks - 1]++; |
} else { |
blocks++; |
blockType[blocks - 1] = typemap[i]; |
blockLength[blocks - 1] = 1; |
} |
} |
if ((blockType[0] & 1) != 0) { |
blockType[0] = 1; |
} |
if ((blockType[0] & 2) != 0) { |
blockType[0] = 2; |
} |
if ((blockType[0] & 4) != 0) { |
blockType[0] = 4; |
} |
if ((blockType[0] & 8) != 0) { |
blockType[0] = 8; |
} |
if (blocks > 1) { |
/* look for adjacent blocks which can use the same table (left to right search) */ |
for (i = 1; i < blocks; i++) { |
if ((blockType[i] & blockType[i - 1]) != 0) { |
blockType[i] = blockType[i] & blockType[i - 1]; |
} |
} |
if ((blockType[blocks - 1] & 1) != 0) { |
blockType[blocks - 1] = 1; |
} |
if ((blockType[blocks - 1] & 2) != 0) { |
blockType[blocks - 1] = 2; |
} |
if ((blockType[blocks - 1] & 4) != 0) { |
blockType[blocks - 1] = 4; |
} |
if ((blockType[blocks - 1] & 8) != 0) { |
blockType[blocks - 1] = 8; |
} |
/* look for adjacent blocks which can use the same table (right to left search) */ |
for (i = blocks - 2; i > 0; i--) { |
if ((blockType[i] & blockType[i + 1]) != 0) { |
blockType[i] = blockType[i] & blockType[i + 1]; |
} |
} |
/* determine the encoding table for characters which do not fit with adjacent blocks */ |
for (i = 1; i < blocks; i++) { |
if ((blockType[i] & 8) != 0) { |
blockType[i] = 8; |
} |
if ((blockType[i] & 4) != 0) { |
blockType[i] = 4; |
} |
if ((blockType[i] & 2) != 0) { |
blockType[i] = 2; |
} |
if ((blockType[i] & 1) != 0) { |
blockType[i] = 1; |
} |
} |
/* |
* if less than 4 characters are preceded and followed by binary blocks then it is more |
* efficient to also encode these in binary |
*/ |
// for (i = 1; i < blocks - 1; i++) { |
// if ((blockType[i - 1] == 32) && (blockLength[i] < 4)) { |
// int nonBinaryLength = blockLength[i]; |
// for (int l = i; ((l < blocks) && (blockType[l] != 32)); l++) { |
// nonBinaryLength += blockLength[l]; |
// } |
// if (nonBinaryLength < 4) { |
// blockType[i] = 32; |
// } |
// } |
// } |
/* Combine blocks of the same type */ |
i = 0; |
do { |
if (blockType[i] == blockType[i + 1]) { |
blockLength[i] += blockLength[i + 1]; |
for (j = i + 1; j < blocks - 1; j++) { |
blockType[j] = blockType[j + 1]; |
blockLength[j] = blockLength[j + 1]; |
} |
blocks--; |
} else { |
i++; |
} |
} while (i < blocks - 1); |
} |
/* Put the adjusted block data back into typemap */ |
j = 0; |
for (i = 0; i < blocks; i++) { |
if (blockLength[i] < 3 && blockType[i] != 32) { /* Shift character(s) needed */ |
for (k = 0; k < blockLength[i]; k++) { |
typemap[j + k] = blockType[i] + 64; |
} |
} else { /* Latch character (or byte mode) needed */ |
for (k = 0; k < blockLength[i]; k++) { |
typemap[j + k] = blockType[i]; |
} |
} |
j += blockLength[i]; |
} |
/* Don't shift an initial capital letter */ |
if (maplength > 0 && typemap[0] == 65) { |
typemap[0] = 1; |
} |
/* |
* Problem characters (those that appear in different tables with different values) can now |
* be resolved into their tables |
*/ |
for (i = 0; i < maplength; i++) { |
if (charmap[i] >= 300 && charmap[i] < 400) { |
curtable = typemap[i]; |
if (curtable > 64) { |
curtable -= 64; |
} |
switch (charmap[i]) { |
case 300: |
/* Carriage Return */ |
switch (curtable) { |
case 8: |
charmap[i] = 1; |
break; // PUNC |
case 4: |
charmap[i] = 14; |
break; // PUNC |
} |
break; |
case 301: |
/* Comma */ |
switch (curtable) { |
case 8: |
charmap[i] = 17; |
break; // PUNC |
case 16: |
charmap[i] = 12; |
break; // DIGIT |
} |
break; |
case 302: |
/* Full Stop */ |
switch (curtable) { |
case 8: |
charmap[i] = 19; |
break; // PUNC |
case 16: |
charmap[i] = 13; |
break; // DIGIT |
} |
break; |
} |
} |
} |
final StringBuilder binaryString = new StringBuilder(); |
info("Encoding: "); |
curtable = 1; /* start with 1 table */ |
lasttable = 1; |
/* Optional structured append start flag (Section 8 of spec) */ |
if (this.structuredAppendTotal != 1) { |
binaryString.append(PENTBIT[29]); |
info("ML "); |
binaryString.append(PENTBIT[29]); |
info("UL "); |
} |
for (i = 0; i < maplength; i++) { |
newtable = curtable; |
if (typemap[i] != curtable && charmap[i] < 400) { |
/* Change table */ |
if (curtable == 32) { |
/* |
* If ending binary mode the current table is the same as when entering binary |
* mode |
*/ |
curtable = lasttable; |
newtable = lasttable; |
} |
if (typemap[i] > 64) { |
/* Shift character */ |
switch (typemap[i]) { |
case 64 + 1: |
/* To UPPER */ |
switch (curtable) { |
case 2: |
/* US */ |
binaryString.append(PENTBIT[28]); |
info("US "); |
break; |
case 4: |
/* UL */ |
binaryString.append(PENTBIT[29]); |
info("UL "); |
newtable = 1; |
break; |
case 8: |
/* UL */ |
binaryString.append(PENTBIT[31]); |
info("UL "); |
newtable = 1; |
break; |
case 16: |
/* US */ |
binaryString.append(QUADBIT[15]); |
info("US "); |
break; |
} |
break; |
case 64 + 2: |
/* To LOWER */ |
switch (curtable) { |
case 1: |
/* LL */ |
binaryString.append(PENTBIT[28]); |
info("LL "); |
newtable = 2; |
break; |
case 4: |
/* LL */ |
binaryString.append(PENTBIT[28]); |
info("LL "); |
newtable = 2; |
break; |
case 8: |
/* UL LL */ |
binaryString.append(PENTBIT[31]); |
info("UL "); |
binaryString.append(PENTBIT[28]); |
info("LL "); |
newtable = 2; |
break; |
case 16: |
/* UL LL */ |
binaryString.append(QUADBIT[14]); |
info("UL "); |
binaryString.append(PENTBIT[28]); |
info("LL "); |
newtable = 2; |
break; |
} |
break; |
case 64 + 4: |
/* To MIXED */ |
switch (curtable) { |
case 1: |
/* ML */ |
binaryString.append(PENTBIT[29]); |
info("ML "); |
newtable = 4; |
break; |
case 2: |
/* ML */ |
binaryString.append(PENTBIT[29]); |
info("ML "); |
newtable = 4; |
break; |
case 8: |
/* UL ML */ |
binaryString.append(PENTBIT[31]); |
info("UL "); |
binaryString.append(PENTBIT[29]); |
info("ML "); |
newtable = 4; |
break; |
case 16: |
/* UL ML */ |
binaryString.append(QUADBIT[14]); |
info("UL "); |
binaryString.append(PENTBIT[29]); |
info("ML "); |
newtable = 4; |
break; |
} |
break; |
case 64 + 8: |
/* To PUNC */ |
switch (curtable) { |
case 1: |
/* PS */ |
binaryString.append(PENTBIT[0]); |
info("PS "); |
break; |
case 2: |
/* PS */ |
binaryString.append(PENTBIT[0]); |
info("PS "); |
break; |
case 4: |
/* PS */ |
binaryString.append(PENTBIT[0]); |
info("PS "); |
break; |
case 16: |
/* PS */ |
binaryString.append(QUADBIT[0]); |
info("PS "); |
break; |
} |
break; |
case 64 + 16: |
/* To DIGIT */ |
switch (curtable) { |
case 1: |
/* DL */ |
binaryString.append(PENTBIT[30]); |
info("DL "); |
newtable = 16; |
break; |
case 2: |
/* DL */ |
binaryString.append(PENTBIT[30]); |
info("DL "); |
newtable = 16; |
break; |
case 4: |
/* UL DL */ |
binaryString.append(PENTBIT[29]); |
info("UL "); |
binaryString.append(PENTBIT[30]); |
info("DL "); |
newtable = 16; |
break; |
case 8: |
/* UL DL */ |
binaryString.append(PENTBIT[31]); |
info("UL "); |
binaryString.append(PENTBIT[30]); |
info("DL "); |
newtable = 16; |
break; |
} |
break; |
} |
} else { |
/* Latch character */ |
switch (typemap[i]) { |
case 1: |
/* To UPPER */ |
switch (curtable) { |
case 2: |
/* ML UL */ |
binaryString.append(PENTBIT[29]); |
info("ML "); |
binaryString.append(PENTBIT[29]); |
info("UL "); |
newtable = 1; |
break; |
case 4: |
/* UL */ |
binaryString.append(PENTBIT[29]); |
info("UL "); |
newtable = 1; |
break; |
case 8: |
/* UL */ |
binaryString.append(PENTBIT[31]); |
info("UL "); |
newtable = 1; |
break; |
case 16: |
/* UL */ |
binaryString.append(QUADBIT[14]); |
info("UL "); |
newtable = 1; |
break; |
} |
break; |
case 2: |
/* To LOWER */ |
switch (curtable) { |
case 1: |
/* LL */ |
binaryString.append(PENTBIT[28]); |
info("LL "); |
newtable = 2; |
break; |
case 4: |
/* LL */ |
binaryString.append(PENTBIT[28]); |
info("LL "); |
newtable = 2; |
break; |
case 8: |
/* UL LL */ |
binaryString.append(PENTBIT[31]); |
info("UL "); |
binaryString.append(PENTBIT[28]); |
info("LL "); |
newtable = 2; |
break; |
case 16: |
/* UL LL */ |
binaryString.append(QUADBIT[14]); |
info("UL "); |
binaryString.append(PENTBIT[28]); |
info("LL "); |
newtable = 2; |
break; |
} |
break; |
case 4: |
/* To MIXED */ |
switch (curtable) { |
case 1: |
/* ML */ |
binaryString.append(PENTBIT[29]); |
info("ML "); |
newtable = 4; |
break; |
case 2: |
/* ML */ |
binaryString.append(PENTBIT[29]); |
info("ML "); |
newtable = 4; |
break; |
case 8: |
/* UL ML */ |
binaryString.append(PENTBIT[31]); |
info("UL "); |
binaryString.append(PENTBIT[29]); |
info("ML "); |
newtable = 4; |
break; |
case 16: |
/* UL ML */ |
binaryString.append(QUADBIT[14]); |
info("UL "); |
binaryString.append(PENTBIT[29]); |
info("ML "); |
newtable = 4; |
break; |
} |
break; |
case 8: |
/* To PUNC */ |
switch (curtable) { |
case 1: |
/* ML PL */ |
binaryString.append(PENTBIT[29]); |
info("ML "); |
binaryString.append(PENTBIT[30]); |
info("PL "); |
newtable = 8; |
break; |
case 2: |
/* ML PL */ |
binaryString.append(PENTBIT[29]); |
info("ML "); |
binaryString.append(PENTBIT[30]); |
info("PL "); |
newtable = 8; |
break; |
case 4: |
/* PL */ |
binaryString.append(PENTBIT[30]); |
info("PL "); |
newtable = 8; |
break; |
case 16: |
/* UL ML PL */ |
binaryString.append(QUADBIT[14]); |
info("UL "); |
binaryString.append(PENTBIT[29]); |
info("ML "); |
binaryString.append(PENTBIT[30]); |
info("PL "); |
newtable = 8; |
break; |
} |
break; |
case 16: |
/* To DIGIT */ |
switch (curtable) { |
case 1: |
/* DL */ |
binaryString.append(PENTBIT[30]); |
info("DL "); |
newtable = 16; |
break; |
case 2: |
/* DL */ |
binaryString.append(PENTBIT[30]); |
info("DL "); |
newtable = 16; |
break; |
case 4: |
/* UL DL */ |
binaryString.append(PENTBIT[29]); |
info("UL "); |
binaryString.append(PENTBIT[30]); |
info("DL "); |
newtable = 16; |
break; |
case 8: |
/* UL DL */ |
binaryString.append(PENTBIT[31]); |
info("UL "); |
binaryString.append(PENTBIT[30]); |
info("DL "); |
newtable = 16; |
break; |
} |
break; |
case 32: |
/* To BINARY */ |
lasttable = curtable; |
switch (curtable) { |
case 1: |
/* BS */ |
binaryString.append(PENTBIT[31]); |
info("BS "); |
newtable = 32; |
break; |
case 2: |
/* BS */ |
binaryString.append(PENTBIT[31]); |
info("BS "); |
newtable = 32; |
break; |
case 4: |
/* BS */ |
binaryString.append(PENTBIT[31]); |
info("BS "); |
newtable = 32; |
break; |
case 8: |
/* UL BS */ |
binaryString.append(PENTBIT[31]); |
info("UL "); |
binaryString.append(PENTBIT[31]); |
info("BS "); |
lasttable = 1; |
newtable = 32; |
break; |
case 16: |
/* UL BS */ |
binaryString.append(QUADBIT[14]); |
info("UL "); |
binaryString.append(PENTBIT[31]); |
info("BS "); |
lasttable = 1; |
newtable = 32; |
break; |
} |
bytes = 0; |
do { |
bytes++; |
} while (typemap[i + bytes - 1] == 32); |
bytes--; |
if (bytes > 2079) { |
throw new OkapiException("Input too long"); |
} |
if (bytes > 31) { |
/* Put 00000 followed by 11-bit number of bytes less 31 */ |
binaryString.append("00000"); |
for (int weight = 0x400; weight > 0; weight = weight >> 1) { |
if ((bytes - 31 & weight) != 0) { |
binaryString.append('1'); |
} else { |
binaryString.append('0'); |
} |
} |
} else { |
/* Put 5-bit number of bytes */ |
for (int weight = 0x10; weight > 0; weight = weight >> 1) { |
if ((bytes & weight) != 0) { |
binaryString.append('1'); |
} else { |
binaryString.append('0'); |
} |
} |
} |
break; |
} |
} |
} |
/* Add data to the binary string */ |
curtable = newtable; |
chartype = typemap[i]; |
if (chartype > 64) { |
chartype -= 64; |
} |
switch (chartype) { |
case 1: |
case 2: |
case 4: |
case 8: |
if (charmap[i] >= 400) { |
info("FLG(" + (charmap[i] - 400) + ") "); |
binaryString.append(TRIBIT[charmap[i] - 400]); |
if (charmap[i] != 400) { |
/* ECI */ |
binaryString.append(eciToBinary()); |
} |
} else { |
binaryString.append(PENTBIT[charmap[i]]); |
infoSpace(charmap[i]); |
} |
break; |
case 16: |
binaryString.append(QUADBIT[charmap[i]]); |
infoSpace(charmap[i]); |
break; |
case 32: |
for (int weight = 0x80; weight > 0; weight = weight >> 1) { |
if ((charmap[i] & weight) != 0) { |
binaryString.append('1'); |
} else { |
binaryString.append('0'); |
} |
} |
infoSpace(charmap[i]); |
break; |
} |
} |
infoLine(); |
return binaryString.toString(); |
} |
/** Adjusts bit stream so that no codewords are all 0s or all 1s, per Section 7.3.1.2 */ |
private StringBuilder adjustBinaryString(final String binaryString, final boolean compact, final int layers) { |
final StringBuilder adjustedString = new StringBuilder(); |
final int codewordSize = getCodewordSize(layers); |
int ones = 0; |
/* Insert dummy digits needed to prevent codewords of all 0s or all 1s */ |
for (int i = 0; i < binaryString.length(); i++) { |
if ((adjustedString.length() + 1) % codewordSize == 0) { |
if (ones == codewordSize - 1) { |
// codeword of B-1 1s, add dummy 0 |
adjustedString.append('0'); |
i--; |
} else if (ones == 0) { |
// codeword of B-1 0s, add dummy 1 |
adjustedString.append('1'); |
i--; |
} else { |
// no dummy value needed |
adjustedString.append(binaryString.charAt(i)); |
} |
ones = 0; |
} else { |
adjustedString.append(binaryString.charAt(i)); |
if (binaryString.charAt(i) == '1') { |
ones++; |
} |
} |
} |
/* Add padding */ |
int adjustedLength = adjustedString.length(); |
final int remainder = adjustedLength % codewordSize; |
int padBits = codewordSize - remainder; |
if (padBits == codewordSize) { |
padBits = 0; |
} |
for (int i = 0; i < padBits; i++) { |
adjustedString.append('1'); |
} |
adjustedLength = adjustedString.length(); |
/* Make sure padding didn't create an invalid (all 1s) codeword */ |
ones = 0; |
for (int i = adjustedLength - codewordSize; i < adjustedLength && i >= 0; i++) { |
if (adjustedString.charAt(i) == '1') { |
ones++; |
} |
} |
if (ones == codewordSize) { |
adjustedString.setCharAt(adjustedLength - 1, '0'); |
} |
/* Log the codewords */ |
info("Codewords: "); |
for (int i = 0; i < adjustedLength / codewordSize; i++) { |
int l = 0, m = 1 << codewordSize - 1; |
for (int j = 0; j < codewordSize; j++) { |
if (adjustedString.charAt(i * codewordSize + j) == '1') { |
l += m; |
} |
m = m >> 1; |
} |
infoSpace(l); |
} |
infoLine(); |
/* Return the adjusted bit string */ |
return adjustedString; |
} |
private String eciToBinary() { |
final String eciNumber = Integer.toString(this.eciMode); |
final StringBuilder binary = new StringBuilder(4 * eciNumber.length()); |
for (int i = 0; i < eciNumber.length(); i++) { |
binary.append(QUADBIT[eciNumber.charAt(i) - '0' + 2]); |
infoSpace(eciNumber.charAt(i)); |
} |
return binary.toString(); |
} |
/** Creates the descriptor / mode message, per Section 7.2 */ |
private String createDescriptor(final boolean compact, final int layers, final int dataBlocks) { |
final StringBuilder descriptor = new StringBuilder(); |
int descDataSize; |
if (compact) { |
/* The first 2 bits represent the number of layers minus 1 */ |
if ((layers - 1 & 0x02) != 0) { |
descriptor.append('1'); |
} else { |
descriptor.append('0'); |
} |
if ((layers - 1 & 0x01) != 0) { |
descriptor.append('1'); |
} else { |
descriptor.append('0'); |
} |
/* The next 6 bits represent the number of data blocks minus 1 */ |
if (this.readerInit) { |
descriptor.append('1'); |
} else { |
if ((dataBlocks - 1 & 0x20) != 0) { |
descriptor.append('1'); |
} else { |
descriptor.append('0'); |
} |
} |
for (int i = 0x10; i > 0; i = i >> 1) { |
if ((dataBlocks - 1 & i) != 0) { |
descriptor.append('1'); |
} else { |
descriptor.append('0'); |
} |
} |
descDataSize = 2; |
} else { |
/* The first 5 bits represent the number of layers minus 1 */ |
for (int i = 0x10; i > 0; i = i >> 1) { |
if ((layers - 1 & i) != 0) { |
descriptor.append('1'); |
} else { |
descriptor.append('0'); |
} |
} |
/* The next 11 bits represent the number of data blocks minus 1 */ |
if (this.readerInit) { |
descriptor.append('1'); |
} else { |
if ((dataBlocks - 1 & 0x400) != 0) { |
descriptor.append('1'); |
} else { |
descriptor.append('0'); |
} |
} |
for (int i = 0x200; i > 0; i = i >> 1) { |
if ((dataBlocks - 1 & i) != 0) { |
descriptor.append('1'); |
} else { |
descriptor.append('0'); |
} |
} |
descDataSize = 4; |
} |
infoLine("Mode Message: " + descriptor); |
/* Split into 4-bit codewords */ |
final int[] desc_data = new int[descDataSize]; |
for (int i = 0; i < descDataSize; i++) { |
for (int weight = 0; weight < 4; weight++) { |
if (descriptor.charAt(i * 4 + weight) == '1') { |
desc_data[i] += 8 >> weight; |
} |
} |
} |
/* |
* Add Reed-Solomon error correction with Galois Field GF(16) and prime modulus x^4 + x + 1 |
* (Section 7.2.3) |
*/ |
final ReedSolomon rs = new ReedSolomon(); |
rs.init_gf(0x13); |
if (compact) { |
rs.init_code(5, 1); |
rs.encode(2, desc_data); |
final int[] desc_ecc = new int[6]; |
for (int i = 0; i < 5; i++) { |
desc_ecc[i] = rs.getResult(i); |
} |
for (int i = 0; i < 5; i++) { |
for (int weight = 0x08; weight > 0; weight = weight >> 1) { |
if ((desc_ecc[4 - i] & weight) != 0) { |
descriptor.append('1'); |
} else { |
descriptor.append('0'); |
} |
} |
} |
} else { |
rs.init_code(6, 1); |
rs.encode(4, desc_data); |
final int[] desc_ecc = new int[6]; |
for (int i = 0; i < 6; i++) { |
desc_ecc[i] = rs.getResult(i); |
} |
for (int i = 0; i < 6; i++) { |
for (int weight = 0x08; weight > 0; weight = weight >> 1) { |
if ((desc_ecc[5 - i] & weight) != 0) { |
descriptor.append('1'); |
} else { |
descriptor.append('0'); |
} |
} |
} |
} |
return descriptor.toString(); |
} |
/** |
* Adds error correction data to the specified binary string, which already contains the primary |
* data |
*/ |
private void addErrorCorrection(final StringBuilder adjustedString, final int codewordSize, final int dataBlocks, final int eccBlocks) { |
int x, poly, startWeight; |
/* Split into codewords and calculate Reed-Solomon error correction codes */ |
switch (codewordSize) { |
case 6: |
x = 32; |
poly = 0x43; |
startWeight = 0x20; |
break; |
case 8: |
x = 128; |
poly = 0x12d; |
startWeight = 0x80; |
break; |
case 10: |
x = 512; |
poly = 0x409; |
startWeight = 0x200; |
break; |
case 12: |
x = 2048; |
poly = 0x1069; |
startWeight = 0x800; |
break; |
default: |
throw new OkapiException("Unrecognized codeword size: " + codewordSize); |
} |
final ReedSolomon rs = new ReedSolomon(); |
final int[] data = new int[dataBlocks + 3]; |
final int[] ecc = new int[eccBlocks + 3]; |
for (int i = 0; i < dataBlocks; i++) { |
for (int weight = 0; weight < codewordSize; weight++) { |
if (adjustedString.charAt(i * codewordSize + weight) == '1') { |
data[i] += x >> weight; |
} |
} |
} |
rs.init_gf(poly); |
rs.init_code(eccBlocks, 1); |
rs.encode(dataBlocks, data); |
for (int i = 0; i < eccBlocks; i++) { |
ecc[i] = rs.getResult(i); |
} |
for (int i = eccBlocks - 1; i >= 0; i--) { |
for (int weight = startWeight; weight > 0; weight = weight >> 1) { |
if ((ecc[i] & weight) != 0) { |
adjustedString.append('1'); |
} else { |
adjustedString.append('0'); |
} |
} |
} |
} |
/** Determines codeword bit length - Table 3 */ |
private static int getCodewordSize(final int layers) { |
if (layers >= 23) { |
return 12; |
} else if (layers >= 9 && layers <= 22) { |
return 10; |
} else if (layers >= 3 && layers <= 8) { |
return 8; |
} else { |
assert layers <= 2; |
return 6; |
} |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Nve18.java |
---|
New file |
0,0 → 1,88 |
/* |
* 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; |
/** |
* <p> |
* Calculate NVE-18 (Nummer der Versandeinheit), also known as SSCC-18 (Serial Shipping Container |
* Code). |
* |
* <p> |
* Encodes a 17-digit number, adding a modulo-10 check digit. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class Nve18 extends Symbol { |
@Override |
protected void encode() { |
String gs1Equivalent = ""; |
int zeroes; |
int count = 0; |
int c, cdigit; |
int p = 0; |
if (this.content.length() > 17) { |
throw new OkapiException("Input data too long"); |
} |
if (!this.content.matches("[0-9]+")) { |
throw new OkapiException("Invalid characters in input"); |
} |
// Add leading zeroes |
zeroes = 17 - this.content.length(); |
for (int i = 0; i < zeroes; i++) { |
gs1Equivalent += "0"; |
} |
gs1Equivalent += this.content; |
// Add Modulus-10 check digit |
for (int i = gs1Equivalent.length() - 1; i >= 0; i--) { |
c = Character.getNumericValue(gs1Equivalent.charAt(i)); |
if (p % 2 == 0) { |
c = c * 3; |
} |
count += c; |
p++; |
} |
cdigit = 10 - count % 10; |
if (cdigit == 10) { |
cdigit = 0; |
} |
infoLine("NVE Check Digit: " + cdigit); |
this.content = "[00]" + gs1Equivalent + cdigit; |
// Defer to Code 128 |
final Code128 code128 = new Code128(); |
code128.setDataType(DataType.GS1); |
code128.setHumanReadableLocation(this.humanReadableLocation); |
code128.setContent(this.content); |
this.readable = code128.readable; |
this.pattern = code128.pattern; |
this.row_count = code128.row_count; |
this.row_height = code128.row_height; |
this.symbol_height = code128.symbol_height; |
this.symbol_width = code128.symbol_width; |
this.rectangles = code128.rectangles; |
this.texts = code128.texts; |
info(code128.encodeInfo); |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Ean.java |
---|
New file |
0,0 → 1,352 |
/* |
* 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.BOTTOM; |
import static uk.org.okapibarcode.backend.HumanReadableLocation.NONE; |
import static uk.org.okapibarcode.backend.HumanReadableLocation.TOP; |
import java.awt.geom.Rectangle2D; |
/** |
* <p> |
* Implements EAN bar code symbology according to BS EN 797:1996. |
* |
* <p> |
* European Article Number data can be encoded in EAN-8 or EAN-13 format requiring a 7-digit or |
* 12-digit input respectively. EAN-13 numbers map to Global Trade Identification Numbers (GTIN) |
* whereas EAN-8 symbols are generally for internal use only. Check digit is calculated and should |
* not be in input data. Leading zeroes are added as required. |
* |
* <p> |
* Add-on content can be appended to the main symbol content by adding a <tt>'+'</tt> character, |
* followed by the add-on content (up to 5 digits). |
* |
* @author <a href="mailto:jakel2006@me.com">Robert Elliott</a> |
*/ |
public class Ean extends Symbol { |
public enum Mode { |
EAN8, EAN13 |
}; |
private static final String[] EAN13_PARITY = { "AAAAAA", "AABABB", "AABBAB", "AABBBA", "ABAABB", "ABBAAB", "ABBBAA", "ABABAB", "ABABBA", "ABBABA" }; |
private static final String[] EAN_SET_A = { "3211", "2221", "2122", "1411", "1132", "1231", "1114", "1312", "1213", "3112" }; |
private static final String[] EAN_SET_B = { "1123", "1222", "2212", "1141", "2311", "1321", "4111", "2131", "3121", "2113" }; |
private Mode mode = Mode.EAN13; |
private int guardPatternExtraHeight = 5; |
private boolean linkageFlag; |
private EanUpcAddOn addOn; |
/** Creates a new instance. */ |
public Ean() { |
this.humanReadableAlignment = HumanReadableAlignment.JUSTIFY; |
} |
/** |
* Sets the EAN mode (EAN-8 or EAN-13). The default is EAN-13. |
* |
* @param mode the EAN mode (EAN-8 or EAN-13) |
*/ |
public void setMode(final Mode mode) { |
this.mode = mode; |
} |
/** |
* Returns the EAN mode (EAN-8 or EAN-13). |
* |
* @return the EAN mode (EAN-8 or EAN-13) |
*/ |
public Mode getMode() { |
return this.mode; |
} |
/** |
* Sets the extra height used for the guard patterns. The default value is <code>5</code>. |
* |
* @param guardPatternExtraHeight the extra height used for the guard patterns |
*/ |
public void setGuardPatternExtraHeight(final int guardPatternExtraHeight) { |
this.guardPatternExtraHeight = guardPatternExtraHeight; |
} |
/** |
* Returns the extra height used for the guard patterns. |
* |
* @return the extra height used for the guard patterns |
*/ |
public int getGuardPatternExtraHeight() { |
return this.guardPatternExtraHeight; |
} |
/** |
* Sets the linkage flag. If set to <code>true</code>, this symbol is part of a composite |
* symbol. |
* |
* @param linkageFlag the linkage flag |
*/ |
protected void setLinkageFlag(final boolean linkageFlag) { |
this.linkageFlag = linkageFlag; |
} |
@Override |
protected void encode() { |
separateContent(); |
if (this.content.isEmpty()) { |
throw new OkapiException("Missing EAN data"); |
} |
if (this.mode == Mode.EAN8) { |
ean8(); |
} else { |
ean13(); |
} |
} |
private void separateContent() { |
final int splitPoint = this.content.indexOf('+'); |
if (splitPoint == -1) { |
// there is no add-on data |
this.addOn = null; |
} else if (splitPoint == this.content.length() - 1) { |
// we found the add-on separator, but no add-on data |
throw new OkapiException("Invalid add-on data"); |
} else { |
// there is a '+' in the input data, use an add-on EAN2 or EAN5 |
this.addOn = new EanUpcAddOn(); |
this.addOn.font = this.font; |
this.addOn.fontName = this.fontName; |
this.addOn.fontSize = this.fontSize; |
this.addOn.humanReadableLocation = this.humanReadableLocation == NONE ? NONE : TOP; |
this.addOn.moduleWidth = this.moduleWidth; |
this.addOn.default_height = this.default_height + this.guardPatternExtraHeight - 8; |
this.addOn.setContent(this.content.substring(splitPoint + 1)); |
this.content = this.content.substring(0, splitPoint); |
} |
} |
private void ean13() { |
this.content = validateAndPad(this.content, 12); |
final char check = calcDigit(this.content); |
infoLine("Check Digit: " + check); |
final String hrt = this.content + check; |
final char parityChar = hrt.charAt(0); |
final String parity = EAN13_PARITY[parityChar - '0']; |
infoLine("Parity Digit: " + parityChar); |
final StringBuilder dest = new StringBuilder("111"); |
for (int i = 1; i < 13; i++) { |
if (i == 7) { |
dest.append("11111"); |
} |
if (i <= 6) { |
if (parity.charAt(i - 1) == 'B') { |
dest.append(EAN_SET_B[hrt.charAt(i) - '0']); |
} else { |
dest.append(EAN_SET_A[hrt.charAt(i) - '0']); |
} |
} else { |
dest.append(EAN_SET_A[hrt.charAt(i) - '0']); |
} |
} |
dest.append("111"); |
this.readable = hrt; |
this.pattern = new String[] { dest.toString() }; |
this.row_count = 1; |
this.row_height = new int[] { -1 }; |
} |
private void ean8() { |
this.content = validateAndPad(this.content, 7); |
final char check = calcDigit(this.content); |
infoLine("Check Digit: " + check); |
final String hrt = this.content + check; |
final StringBuilder dest = new StringBuilder("111"); |
for (int i = 0; i < 8; i++) { |
if (i == 4) { |
dest.append("11111"); |
} |
dest.append(EAN_SET_A[hrt.charAt(i) - '0']); |
} |
dest.append("111"); |
this.readable = hrt; |
this.pattern = new String[] { dest.toString() }; |
this.row_count = 1; |
this.row_height = new int[] { -1 }; |
} |
protected static String validateAndPad(String s, final int targetLength) { |
if (!s.matches("[0-9]+")) { |
throw new OkapiException("Invalid characters in input"); |
} |
if (s.length() > targetLength) { |
throw new OkapiException("Input data too long"); |
} |
if (s.length() < targetLength) { |
for (int i = s.length(); i < targetLength; i++) { |
s = '0' + s; |
} |
} |
return s; |
} |
public static char calcDigit(final String s) { |
int count = 0; |
int p = 0; |
for (int i = s.length() - 1; i >= 0; i--) { |
int c = Character.getNumericValue(s.charAt(i)); |
if (p % 2 == 0) { |
c = c * 3; |
} |
count += c; |
p++; |
} |
int cdigit = 10 - count % 10; |
if (cdigit == 10) { |
cdigit = 0; |
} |
return (char) (cdigit + '0'); |
} |
@Override |
protected void plotSymbol() { |
int xBlock; |
int x, y, w, h; |
boolean black = true; |
final int compositeOffset = this.linkageFlag ? 6 : 0; // space for composite separator above |
final int hrtOffset = this.humanReadableLocation == TOP ? getTheoreticalHumanReadableHeight() : 0; // space |
// for |
// HRT |
// above |
this.rectangles.clear(); |
this.texts.clear(); |
x = 0; |
/* Draw the bars in the symbology */ |
for (xBlock = 0; xBlock < this.pattern[0].length(); xBlock++) { |
w = this.pattern[0].charAt(xBlock) - '0'; |
if (black) { |
y = 0; |
h = this.default_height; |
/* Add extension to guide bars */ |
if (this.mode == Mode.EAN13) { |
if (x < 3 || x > 91 || x > 45 && x < 49) { |
h += this.guardPatternExtraHeight; |
} |
if (this.linkageFlag && (x == 0 || x == 94)) { |
h += 2; |
y -= 2; |
} |
} else { |
if (x < 3 || x > 62 || x > 30 && x < 35) { |
h += this.guardPatternExtraHeight; |
} |
if (this.linkageFlag && (x == 0 || x == 66)) { |
h += 2; |
y -= 2; |
} |
} |
final Rectangle2D.Double rect = new Rectangle2D.Double(scale(x), y + compositeOffset + hrtOffset, scale(w), h); |
this.rectangles.add(rect); |
this.symbol_width = Math.max(this.symbol_width, (int) rect.getMaxX()); |
this.symbol_height = Math.max(this.symbol_height, (int) rect.getHeight()); |
} |
black = !black; |
x += w; |
} |
/* Add separator for composite symbology, if necessary */ |
if (this.linkageFlag) { |
if (this.mode == Mode.EAN13) { |
this.rectangles.add(new Rectangle2D.Double(scale(0), 0, scale(1), 2)); |
this.rectangles.add(new Rectangle2D.Double(scale(94), 0, scale(1), 2)); |
this.rectangles.add(new Rectangle2D.Double(scale(-1), 2, scale(1), 2)); |
this.rectangles.add(new Rectangle2D.Double(scale(95), 2, scale(1), 2)); |
} else { // EAN8 |
this.rectangles.add(new Rectangle2D.Double(scale(0), 0, scale(1), 2)); |
this.rectangles.add(new Rectangle2D.Double(scale(66), 0, scale(1), 2)); |
this.rectangles.add(new Rectangle2D.Double(scale(-1), 2, scale(1), 2)); |
this.rectangles.add(new Rectangle2D.Double(scale(67), 2, scale(1), 2)); |
} |
this.symbol_height += 4; |
} |
/* Now add the text */ |
if (this.humanReadableLocation == BOTTOM) { |
this.symbol_height -= this.guardPatternExtraHeight; |
final double baseline = this.symbol_height + this.fontSize; |
if (this.mode == Mode.EAN13) { |
this.texts.add(new TextBox(scale(-9), baseline, scale(4), this.readable.substring(0, 1), HumanReadableAlignment.RIGHT)); |
this.texts.add(new TextBox(scale(5), baseline, scale(39), this.readable.substring(1, 7), this.humanReadableAlignment)); |
this.texts.add(new TextBox(scale(51), baseline, scale(39), this.readable.substring(7, 13), this.humanReadableAlignment)); |
} else { // EAN8 |
this.texts.add(new TextBox(scale(5), baseline, scale(25), this.readable.substring(0, 4), this.humanReadableAlignment)); |
this.texts.add(new TextBox(scale(37), baseline, scale(25), this.readable.substring(4, 8), this.humanReadableAlignment)); |
} |
} else if (this.humanReadableLocation == TOP) { |
final double baseline = this.fontSize; |
final int width = this.mode == Mode.EAN13 ? 94 : 66; |
this.texts.add(new TextBox(scale(0), baseline, scale(width), this.readable, this.humanReadableAlignment)); |
} |
/* Now add the add-on symbol, if necessary */ |
if (this.addOn != null) { |
final int gap = 9; |
final int baseX = this.symbol_width + scale(gap); |
final Rectangle2D.Double r1 = this.rectangles.get(0); |
final Rectangle2D.Double ar1 = this.addOn.rectangles.get(0); |
final int baseY = (int) (r1.y + r1.getHeight() - ar1.y - ar1.getHeight()); |
for (final TextBox t : this.addOn.getTexts()) { |
this.texts.add(new TextBox(baseX + t.x, baseY + t.y, t.width, t.text, t.alignment)); |
} |
for (final Rectangle2D.Double r : this.addOn.getRectangles()) { |
this.rectangles.add(new Rectangle2D.Double(baseX + r.x, baseY + r.y, r.width, r.height)); |
} |
this.symbol_width += scale(gap) + this.addOn.symbol_width; |
this.pattern[0] = this.pattern[0] + gap + this.addOn.pattern[0]; |
} |
} |
/** Scales the specified width or x-dimension according to the current module width. */ |
private int scale(final int w) { |
return this.moduleWidth * w; |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/DataBar14.java |
---|
New file |
0,0 → 1,616 |
/* |
* 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 java.math.BigInteger; |
/** |
* <p> |
* Implements GS1 DataBar Omnidirectional and GS1 DataBar Truncated according to ISO/IEC 24724:2011. |
* |
* <p> |
* Input data should be a 13-digit Global Trade Identification Number (GTIN) without check digit or |
* Application Identifier [01]. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class DataBar14 extends Symbol { |
public enum Mode { |
/** DataBar-14 */ |
LINEAR, |
/** DataBar-14 Omnidirectional */ |
OMNI, |
/** DataBar-14 Omnidirectional Stacked */ |
STACKED |
} |
private static final int[] G_SUM_TABLE = { 0, 161, 961, 2015, 2715, 0, 336, 1036, 1516 }; |
private static final int[] T_TABLE = { 1, 10, 34, 70, 126, 4, 20, 48, 81 }; |
private static final int[] MODULES_ODD = { 12, 10, 8, 6, 4, 5, 7, 9, 11 }; |
private static final int[] MODULES_EVEN = { 4, 6, 8, 10, 12, 10, 8, 6, 4 }; |
private static final int[] WIDEST_ODD = { 8, 6, 4, 3, 1, 2, 4, 6, 8 }; |
private static final int[] WIDEST_EVEN = { 1, 3, 5, 6, 8, 7, 5, 3, 1 }; |
private static final int[] CHECKSUM_WEIGHT = { /* Table 5 */ |
1, 3, 9, 27, 2, 6, 18, 54, 4, 12, 36, 29, 8, 24, 72, 58, 16, 48, 65, 37, 32, 17, 51, 74, 64, 34, 23, 69, 49, 68, 46, 59 }; |
private static final int[] FINDER_PATTERN = { 3, 8, 2, 1, 1, 3, 5, 5, 1, 1, 3, 3, 7, 1, 1, 3, 1, 9, 1, 1, 2, 7, 4, 1, 1, 2, 5, 6, 1, 1, 2, 3, 8, 1, 1, 1, 5, 7, 1, 1, 1, 3, 9, 1, 1 }; |
private boolean linkageFlag; |
private Mode mode = Mode.LINEAR; |
@Override |
public void setDataType(final DataType dummy) { |
// Do nothing! |
} |
/** |
* Although this is a GS1 symbology, input data is expected to omit the [01] Application |
* Identifier, as well as the check digit. Thus, the input data is not considered GS1-format |
* data. |
*/ |
@Override |
protected boolean gs1Supported() { |
return false; |
} |
protected void setLinkageFlag(final boolean linkageFlag) { |
this.linkageFlag = linkageFlag; |
} |
protected boolean getLinkageFlag() { |
return this.linkageFlag; |
} |
/** |
* Sets the symbol mode. The default is {@link Mode#LINEAR}. |
* |
* @param mode the symbol mode |
*/ |
public void setMode(final Mode mode) { |
this.mode = mode; |
} |
/** |
* Returns the symbol mode. |
* |
* @return the symbol mode |
*/ |
public Mode getMode() { |
return this.mode; |
} |
@Override |
protected void encode() { |
final boolean[][] grid = new boolean[5][100]; |
BigInteger accum; |
BigInteger left_reg; |
BigInteger right_reg; |
final int[] data_character = new int[4]; |
final int[] data_group = new int[4]; |
final int[] v_odd = new int[4]; |
final int[] v_even = new int[4]; |
int i; |
final int[][] data_widths = new int[8][4]; |
int checksum; |
int c_left; |
int c_right; |
final int[] total_widths = new int[46]; |
int writer; |
char latch; |
int j; |
int count; |
int check_digit; |
final StringBuilder bin = new StringBuilder(); |
int compositeOffset = 0; |
if (this.content.length() > 13) { |
throw new OkapiException("Input too long"); |
} |
if (!this.content.matches("[0-9]+?")) { |
throw new OkapiException("Invalid characters in input"); |
} |
accum = new BigInteger(this.content); |
if (this.linkageFlag) { |
accum = accum.add(new BigInteger("10000000000000")); |
compositeOffset = 1; |
} |
/* Calculate left and right pair values */ |
left_reg = accum.divide(new BigInteger("4537077")); |
right_reg = accum.mod(new BigInteger("4537077")); |
/* Calculate four data characters */ |
accum = left_reg.divide(new BigInteger("1597")); |
data_character[0] = accum.intValue(); |
accum = left_reg.mod(new BigInteger("1597")); |
data_character[1] = accum.intValue(); |
accum = right_reg.divide(new BigInteger("1597")); |
data_character[2] = accum.intValue(); |
accum = right_reg.mod(new BigInteger("1597")); |
data_character[3] = accum.intValue(); |
info("Data Characters: "); |
for (i = 0; i < 4; i++) { |
infoSpace(data_character[i]); |
} |
infoLine(); |
/* Calculate odd and even subset values */ |
if (data_character[0] >= 0 && data_character[0] <= 160) { |
data_group[0] = 0; |
} |
if (data_character[0] >= 161 && data_character[0] <= 960) { |
data_group[0] = 1; |
} |
if (data_character[0] >= 961 && data_character[0] <= 2014) { |
data_group[0] = 2; |
} |
if (data_character[0] >= 2015 && data_character[0] <= 2714) { |
data_group[0] = 3; |
} |
if (data_character[0] >= 2715 && data_character[0] <= 2840) { |
data_group[0] = 4; |
} |
if (data_character[1] >= 0 && data_character[1] <= 335) { |
data_group[1] = 5; |
} |
if (data_character[1] >= 336 && data_character[1] <= 1035) { |
data_group[1] = 6; |
} |
if (data_character[1] >= 1036 && data_character[1] <= 1515) { |
data_group[1] = 7; |
} |
if (data_character[1] >= 1516 && data_character[1] <= 1596) { |
data_group[1] = 8; |
} |
if (data_character[3] >= 0 && data_character[3] <= 335) { |
data_group[3] = 5; |
} |
if (data_character[3] >= 336 && data_character[3] <= 1035) { |
data_group[3] = 6; |
} |
if (data_character[3] >= 1036 && data_character[3] <= 1515) { |
data_group[3] = 7; |
} |
if (data_character[3] >= 1516 && data_character[3] <= 1596) { |
data_group[3] = 8; |
} |
if (data_character[2] >= 0 && data_character[2] <= 160) { |
data_group[2] = 0; |
} |
if (data_character[2] >= 161 && data_character[2] <= 960) { |
data_group[2] = 1; |
} |
if (data_character[2] >= 961 && data_character[2] <= 2014) { |
data_group[2] = 2; |
} |
if (data_character[2] >= 2015 && data_character[2] <= 2714) { |
data_group[2] = 3; |
} |
if (data_character[2] >= 2715 && data_character[2] <= 2840) { |
data_group[2] = 4; |
} |
v_odd[0] = (data_character[0] - G_SUM_TABLE[data_group[0]]) / T_TABLE[data_group[0]]; |
v_even[0] = (data_character[0] - G_SUM_TABLE[data_group[0]]) % T_TABLE[data_group[0]]; |
v_odd[1] = (data_character[1] - G_SUM_TABLE[data_group[1]]) % T_TABLE[data_group[1]]; |
v_even[1] = (data_character[1] - G_SUM_TABLE[data_group[1]]) / T_TABLE[data_group[1]]; |
v_odd[3] = (data_character[3] - G_SUM_TABLE[data_group[3]]) % T_TABLE[data_group[3]]; |
v_even[3] = (data_character[3] - G_SUM_TABLE[data_group[3]]) / T_TABLE[data_group[3]]; |
v_odd[2] = (data_character[2] - G_SUM_TABLE[data_group[2]]) / T_TABLE[data_group[2]]; |
v_even[2] = (data_character[2] - G_SUM_TABLE[data_group[2]]) % T_TABLE[data_group[2]]; |
/* Use RSS subset width algorithm */ |
for (i = 0; i < 4; i++) { |
if (i == 0 || i == 2) { |
int[] widths = getWidths(v_odd[i], MODULES_ODD[data_group[i]], 4, WIDEST_ODD[data_group[i]], 1); |
data_widths[0][i] = widths[0]; |
data_widths[2][i] = widths[1]; |
data_widths[4][i] = widths[2]; |
data_widths[6][i] = widths[3]; |
widths = getWidths(v_even[i], MODULES_EVEN[data_group[i]], 4, WIDEST_EVEN[data_group[i]], 0); |
data_widths[1][i] = widths[0]; |
data_widths[3][i] = widths[1]; |
data_widths[5][i] = widths[2]; |
data_widths[7][i] = widths[3]; |
} else { |
int[] widths = getWidths(v_odd[i], MODULES_ODD[data_group[i]], 4, WIDEST_ODD[data_group[i]], 0); |
data_widths[0][i] = widths[0]; |
data_widths[2][i] = widths[1]; |
data_widths[4][i] = widths[2]; |
data_widths[6][i] = widths[3]; |
widths = getWidths(v_even[i], MODULES_EVEN[data_group[i]], 4, WIDEST_EVEN[data_group[i]], 1); |
data_widths[1][i] = widths[0]; |
data_widths[3][i] = widths[1]; |
data_widths[5][i] = widths[2]; |
data_widths[7][i] = widths[3]; |
} |
} |
/* Calculate the checksum */ |
checksum = 0; |
for (i = 0; i < 8; i++) { |
checksum += CHECKSUM_WEIGHT[i] * data_widths[i][0]; |
checksum += CHECKSUM_WEIGHT[i + 8] * data_widths[i][1]; |
checksum += CHECKSUM_WEIGHT[i + 16] * data_widths[i][2]; |
checksum += CHECKSUM_WEIGHT[i + 24] * data_widths[i][3]; |
} |
checksum %= 79; |
/* Calculate the two check characters */ |
if (checksum >= 8) { |
checksum++; |
} |
if (checksum >= 72) { |
checksum++; |
} |
c_left = checksum / 9; |
c_right = checksum % 9; |
infoLine("Checksum: " + checksum); |
/* Put element widths together */ |
total_widths[0] = 1; |
total_widths[1] = 1; |
total_widths[44] = 1; |
total_widths[45] = 1; |
for (i = 0; i < 8; i++) { |
total_widths[i + 2] = data_widths[i][0]; |
total_widths[i + 15] = data_widths[7 - i][1]; |
total_widths[i + 23] = data_widths[i][3]; |
total_widths[i + 36] = data_widths[7 - i][2]; |
} |
for (i = 0; i < 5; i++) { |
total_widths[i + 10] = FINDER_PATTERN[i + 5 * c_left]; |
total_widths[i + 31] = FINDER_PATTERN[4 - i + 5 * c_right]; |
} |
this.row_count = 0; |
final boolean[] separator = new boolean[100]; |
for (i = 0; i < separator.length; i++) { |
separator[i] = false; |
} |
/* Put this data into the symbol */ |
if (this.mode == Mode.LINEAR) { |
writer = 0; |
latch = '0'; |
for (i = 0; i < 46; i++) { |
for (j = 0; j < total_widths[i]; j++) { |
if (latch == '1') { |
grid[this.row_count][writer] = true; |
} |
writer++; |
} |
if (latch == '1') { |
latch = '0'; |
} else { |
latch = '1'; |
} |
} |
if (this.symbol_width < writer) { |
this.symbol_width = writer; |
} |
if (this.linkageFlag) { |
/* separator pattern for composite symbol */ |
for (i = 4; i < 92; i++) { |
separator[i] = !grid[0][i]; |
} |
latch = '1'; |
for (i = 16; i < 32; i++) { |
if (!grid[0][i]) { |
if (latch == '1') { |
separator[i] = true; |
latch = '0'; |
} else { |
separator[i] = false; |
latch = '1'; |
} |
} else { |
separator[i] = false; |
latch = '1'; |
} |
} |
latch = '1'; |
for (i = 63; i < 78; i++) { |
if (!grid[0][i]) { |
if (latch == '1') { |
separator[i] = true; |
latch = '0'; |
} else { |
separator[i] = false; |
latch = '1'; |
} |
} else { |
separator[i] = false; |
latch = '1'; |
} |
} |
} |
this.row_count = this.row_count + 1; |
count = 0; |
check_digit = 0; |
/* Calculate check digit from Annex A and place human readable text */ |
final StringBuilder hrt = new StringBuilder(14); |
for (i = this.content.length(); i < 13; i++) { |
hrt.append('0'); |
} |
hrt.append(this.content); |
for (i = 0; i < 13; i++) { |
count += hrt.charAt(i) - '0'; |
if ((i & 1) == 0) { |
count += 2 * (hrt.charAt(i) - '0'); |
} |
} |
check_digit = 10 - count % 10; |
if (check_digit == 10) { |
check_digit = 0; |
} |
infoLine("Check Digit: " + check_digit); |
hrt.append((char) (check_digit + '0')); |
this.readable = "(01)" + hrt; |
} |
if (this.mode == Mode.STACKED) { |
/* top row */ |
writer = 0; |
latch = '0'; |
for (i = 0; i < 23; i++) { |
for (j = 0; j < total_widths[i]; j++) { |
grid[this.row_count][writer] = latch == '1'; |
writer++; |
} |
if (latch == '1') { |
latch = '0'; |
} else { |
latch = '1'; |
} |
} |
grid[this.row_count][writer] = true; |
grid[this.row_count][writer + 1] = false; |
/* bottom row */ |
this.row_count = this.row_count + 2; |
grid[this.row_count][0] = true; |
grid[this.row_count][1] = false; |
writer = 0; |
latch = '1'; |
for (i = 23; i < 46; i++) { |
for (j = 0; j < total_widths[i]; j++) { |
grid[this.row_count][writer + 2] = latch == '1'; |
writer++; |
} |
if (latch == '1') { |
latch = '0'; |
} else { |
latch = '1'; |
} |
} |
/* separator pattern */ |
for (i = 1; i < 46; i++) { |
if (grid[this.row_count - 2][i] == grid[this.row_count][i]) { |
if (!grid[this.row_count - 2][i]) { |
grid[this.row_count - 1][i] = true; |
} |
} else { |
if (!grid[this.row_count - 1][i - 1]) { |
grid[this.row_count - 1][i] = true; |
} |
} |
} |
for (i = 0; i < 4; i++) { |
grid[this.row_count - 1][i] = false; |
} |
if (this.linkageFlag) { |
/* separator pattern for composite symbol */ |
for (i = 4; i < 46; i++) { |
separator[i] = !grid[0][i]; |
} |
latch = '1'; |
for (i = 16; i < 32; i++) { |
if (!grid[0][i]) { |
if (latch == '1') { |
separator[i] = true; |
latch = '0'; |
} else { |
separator[i] = false; |
latch = '1'; |
} |
} else { |
separator[i] = false; |
latch = '1'; |
} |
} |
} |
this.row_count = this.row_count + 1; |
if (this.symbol_width < 50) { |
this.symbol_width = 50; |
} |
} |
if (this.mode == Mode.OMNI) { |
/* top row */ |
writer = 0; |
latch = '0'; |
for (i = 0; i < 23; i++) { |
for (j = 0; j < total_widths[i]; j++) { |
grid[this.row_count][writer] = latch == '1'; |
writer++; |
} |
latch = latch == '1' ? '0' : '1'; |
} |
grid[this.row_count][writer] = true; |
grid[this.row_count][writer + 1] = false; |
/* bottom row */ |
this.row_count = this.row_count + 4; |
grid[this.row_count][0] = true; |
grid[this.row_count][1] = false; |
writer = 0; |
latch = '1'; |
for (i = 23; i < 46; i++) { |
for (j = 0; j < total_widths[i]; j++) { |
grid[this.row_count][writer + 2] = latch == '1'; |
writer++; |
} |
if (latch == '1') { |
latch = '0'; |
} else { |
latch = '1'; |
} |
} |
/* middle separator */ |
for (i = 5; i < 46; i += 2) { |
grid[this.row_count - 2][i] = true; |
} |
/* top separator */ |
for (i = 4; i < 46; i++) { |
if (!grid[this.row_count - 4][i]) { |
grid[this.row_count - 3][i] = true; |
} |
} |
latch = '1'; |
for (i = 17; i < 33; i++) { |
if (!grid[this.row_count - 4][i]) { |
if (latch == '1') { |
grid[this.row_count - 3][i] = true; |
latch = '0'; |
} else { |
grid[this.row_count - 3][i] = false; |
latch = '1'; |
} |
} else { |
grid[this.row_count - 3][i] = false; |
latch = '1'; |
} |
} |
/* bottom separator */ |
for (i = 4; i < 46; i++) { |
if (!grid[this.row_count][i]) { |
grid[this.row_count - 1][i] = true; |
} |
} |
latch = '1'; |
for (i = 16; i < 32; i++) { |
if (!grid[this.row_count][i]) { |
if (latch == '1') { |
grid[this.row_count - 1][i] = true; |
latch = '0'; |
} else { |
grid[this.row_count - 1][i] = false; |
latch = '1'; |
} |
} else { |
grid[this.row_count - 1][i] = false; |
latch = '1'; |
} |
} |
if (this.symbol_width < 50) { |
this.symbol_width = 50; |
} |
if (this.linkageFlag) { |
/* separator pattern for composite symbol */ |
for (i = 4; i < 46; i++) { |
separator[i] = !grid[0][i]; |
} |
latch = '1'; |
for (i = 16; i < 32; i++) { |
if (!grid[0][i]) { |
if (latch == '1') { |
separator[i] = true; |
latch = '0'; |
} else { |
separator[i] = false; |
latch = '1'; |
} |
} else { |
separator[i] = false; |
latch = '1'; |
} |
} |
} |
this.row_count = this.row_count + 1; |
} |
this.pattern = new String[this.row_count + compositeOffset]; |
this.row_height = new int[this.row_count + compositeOffset]; |
if (this.linkageFlag) { |
bin.setLength(0); |
for (j = 0; j < this.symbol_width; j++) { |
if (separator[j]) { |
bin.append('1'); |
} else { |
bin.append('0'); |
} |
} |
this.pattern[0] = bin2pat(bin); |
this.row_height[0] = 1; |
} |
for (i = 0; i < this.row_count; i++) { |
bin.setLength(0); |
for (j = 0; j < this.symbol_width; j++) { |
if (grid[i][j]) { |
bin.append('1'); |
} else { |
bin.append('0'); |
} |
} |
this.pattern[i + compositeOffset] = bin2pat(bin); |
} |
if (this.mode == Mode.LINEAR) { |
this.row_height[0 + compositeOffset] = -1; |
} |
if (this.mode == Mode.STACKED) { |
this.row_height[0 + compositeOffset] = 5; |
this.row_height[1 + compositeOffset] = 1; |
this.row_height[2 + compositeOffset] = 7; |
} |
if (this.mode == Mode.OMNI) { |
this.row_height[0 + compositeOffset] = -1; |
this.row_height[1 + compositeOffset] = 1; |
this.row_height[2 + compositeOffset] = 1; |
this.row_height[3 + compositeOffset] = 1; |
this.row_height[4 + compositeOffset] = -1; |
} |
if (this.linkageFlag) { |
this.row_count++; |
} |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/ChannelCode.java |
---|
New file |
0,0 → 1,167 |
/* |
* 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 Channel Code according to ANSI/AIM BC12-1998. |
* |
* <p> |
* Channel Code encodes whole integer values between 0 and 7,742,862. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class ChannelCode extends Symbol { |
private int preferredNumberOfChannels; |
private final int[] space = new int[11]; |
private final int[] bar = new int[11]; |
private double currentValue; |
private double targetValue; |
/** |
* Sets the preferred number of channels used to encode data. This setting will be ignored if |
* the value to be encoded requires more channels. |
* |
* @param channels the preferred number of channels (3 to 8, inclusive) |
*/ |
public void setPreferredNumberOfChannels(final int channels) { |
if (channels < 3 || channels > 8) { |
throw new IllegalArgumentException("Invalid Channel Code number of channels: " + channels); |
} |
this.preferredNumberOfChannels = channels; |
} |
/** |
* Returns the preferred number of channels used to encode data. |
* |
* @return the preferred number of channels used to encode data |
*/ |
public int getPreferredNumberOfChannels() { |
return this.preferredNumberOfChannels; |
} |
@Override |
protected void encode() { |
int channels; |
int i; |
int leadingZeroCount; |
if (this.content.length() > 7) { |
throw new OkapiException("Input too long"); |
} |
if (!this.content.matches("[0-9]+")) { |
throw new OkapiException("Invalid characters in input"); |
} |
if (this.preferredNumberOfChannels <= 2 || this.preferredNumberOfChannels > 8) { |
channels = 3; |
} else { |
channels = this.preferredNumberOfChannels; |
} |
this.targetValue = Integer.parseInt(this.content); |
switch (channels) { |
case 3: |
if (this.targetValue > 26) { |
channels++; |
} |
case 4: |
if (this.targetValue > 292) { |
channels++; |
} |
case 5: |
if (this.targetValue > 3493) { |
channels++; |
} |
case 6: |
if (this.targetValue > 44072) { |
channels++; |
} |
case 7: |
if (this.targetValue > 576688) { |
channels++; |
} |
case 8: |
if (this.targetValue > 7742862) { |
channels++; |
} |
} |
if (channels == 9) { |
throw new OkapiException("Value out of range"); |
} |
infoLine("Channels Used: " + channels); |
for (i = 0; i < 11; i++) { |
this.bar[i] = 0; |
this.space[i] = 0; |
} |
this.bar[0] = this.space[1] = this.bar[1] = this.space[2] = this.bar[2] = 1; |
this.currentValue = 0; |
this.pattern = new String[1]; |
nextSpace(channels, 3, channels, channels); |
leadingZeroCount = channels - 1 - this.content.length(); |
this.readable = ""; |
for (i = 0; i < leadingZeroCount; i++) { |
this.readable += "0"; |
} |
this.readable += this.content; |
this.row_count = 1; |
this.row_height = new int[] { -1 }; |
} |
private void nextSpace(final int channels, final int i, final int maxSpace, final int maxBar) { |
for (int s = i < channels + 2 ? 1 : maxSpace; s <= maxSpace; s++) { |
this.space[i] = s; |
nextBar(channels, i, maxBar, maxSpace + 1 - s); |
} |
} |
private void nextBar(final int channels, final int i, final int maxBar, final int maxSpace) { |
int b = this.space[i] + this.bar[i - 1] + this.space[i - 1] + this.bar[i - 2] > 4 ? 1 : 2; |
if (i < channels + 2) { |
for (; b <= maxBar; b++) { |
this.bar[i] = b; |
nextSpace(channels, i + 1, maxSpace, maxBar + 1 - b); |
} |
} else if (b <= maxBar) { |
this.bar[i] = maxBar; |
checkIfDone(); |
this.currentValue++; |
} |
} |
private void checkIfDone() { |
if (this.currentValue == this.targetValue) { |
/* Target reached - save the generated pattern */ |
final StringBuilder sb = new StringBuilder(); |
sb.append("11110"); |
for (int i = 0; i < 11; i++) { |
sb.append((char) (this.space[i] + '0')); |
sb.append((char) (this.bar[i] + '0')); |
} |
this.pattern[0] = sb.toString(); |
} |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Logmars.java |
---|
New file |
0,0 → 1,98 |
/* |
* Copyright 2014 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
import static uk.org.okapibarcode.util.Arrays.positionOf; |
/** |
* Implements the LOGMARS (Logistics Applications of Automated Marking and Reading Symbols) standard |
* used by the US Department of Defense. Input data can be of any length and supports the characters |
* 0-9, A-Z, dash (-), full stop (.), space, dollar ($), slash (/), plus (+) and percent (%). A |
* Modulo-43 check digit is calculated and added, and should not form part of the input data. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class Logmars extends Symbol { |
private static final String[] CODE39LM = { "1113313111", "3113111131", "1133111131", "3133111111", "1113311131", "3113311111", "1133311111", "1113113131", "3113113111", "1133113111", "3111131131", |
"1131131131", "3131131111", "1111331131", "3111331111", "1131331111", "1111133131", "3111133111", "1131133111", "1111333111", "3111111331", "1131111331", "3131111311", "1111311331", |
"3111311311", "1131311311", "1111113331", "3111113311", "1131113311", "1111313311", "3311111131", "1331111131", "3331111111", "1311311131", "3311311111", "1331311111", "1311113131", |
"3311113111", "1331113111", "1313131111", "1313111311", "1311131311", "1113131311" }; |
private static final char[] LOOKUP = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', |
'V', 'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+', '%' }; |
/** Ratio of wide bar width to narrow bar width. */ |
private double moduleWidthRatio = 3; |
/** |
* Sets the ratio of wide bar width to narrow bar width. Valid values are usually between |
* {@code 2} and {@code 3}. The default value is {@code 3}. |
* |
* @param moduleWidthRatio the ratio of wide bar width to narrow bar width |
*/ |
public void setModuleWidthRatio(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 double getModuleWidth(final int originalWidth) { |
if (originalWidth == 1) { |
return 1; |
} else { |
return this.moduleWidthRatio; |
} |
} |
/** {@inheritDoc} */ |
@Override |
protected void encode() { |
if (!this.content.matches("[0-9A-Z\\. \\-$/+%]*")) { |
throw new OkapiException("Invalid characters in input"); |
} |
String p = ""; |
final int l = this.content.length(); |
int charval, counter = 0; |
char thischar; |
char checkDigit; |
for (int i = 0; i < l; i++) { |
thischar = this.content.charAt(i); |
charval = positionOf(thischar, LOOKUP); |
counter += charval; |
p += CODE39LM[charval]; |
} |
counter = counter % 43; |
checkDigit = LOOKUP[counter]; |
infoLine("Check Digit: " + checkDigit); |
p += CODE39LM[counter]; |
this.readable = this.content + checkDigit; |
this.pattern = new String[] { "1311313111" + p + "131131311" }; |
this.row_count = 1; |
this.row_height = new int[] { -1 }; |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/KoreaPost.java |
---|
New file |
0,0 → 1,64 |
/* |
* 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 Korea Post Barcode. Input should consist of of a six-digit number. A Modulo-10 check |
* digit is calculated and added, and should not form part of the input data. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class KoreaPost extends Symbol { |
private static final String[] KOREA_TABLE = { "1313150613", "0713131313", "0417131313", "1506131313", "0413171313", "17171313", "1315061313", "0413131713", "17131713", "13171713" }; |
@Override |
protected void encode() { |
if (!this.content.matches("[0-9]+")) { |
throw new OkapiException("Invalid characters in input"); |
} |
if (this.content.length() > 6) { |
throw new OkapiException("Input data too long"); |
} |
String padded = ""; |
for (int i = 0; i < 6 - this.content.length(); i++) { |
padded += "0"; |
} |
padded += this.content; |
int total = 0; |
String accumulator = ""; |
for (int i = 0; i < padded.length(); i++) { |
final int j = Character.getNumericValue(padded.charAt(i)); |
accumulator += KOREA_TABLE[j]; |
total += j; |
} |
int checkd = 10 - total % 10; |
if (checkd == 10) { |
checkd = 0; |
} |
infoLine("Check Digit: " + checkd); |
accumulator += KOREA_TABLE[checkd]; |
this.readable = padded + checkd; |
this.pattern = new String[] { accumulator }; |
this.row_count = 1; |
this.row_height = new int[] { -1 }; |
} |
} |
/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/CodeOne.java |
---|
New file |
0,0 → 1,1869 |
/* |
* Copyright 2014 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
import java.math.BigInteger; |
import java.nio.charset.StandardCharsets; |
/** |
* <p> |
* Implements Code One. |
* |
* <p> |
* Code One is able to encode the ISO 8859-1 (Latin-1) character set or GS1 data. There are two |
* types of Code One symbol: variable height symbols which are roughly square (versions A thought to |
* H) and fixed-height versions (version S and T). Version S symbols can only encode numeric data. |
* The width of version S and version T symbols is determined by the length of the input data. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class CodeOne extends Symbol { |
public enum Version { |
NONE, A, B, C, D, E, F, G, H, S, T |
} |
private static final int[] C40_SHIFT = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, |
0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, |
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }; |
private static final int[] C40_VALUE = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 3, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, |
11, 12, 13, 14, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 21, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 22, |
23, 24, 25, 26, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 }; |
private static final int[] TEXT_SHIFT = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, |
0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3 }; |
private static final int[] TEXT_VALUE = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 3, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, |
11, 12, 13, 14, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 21, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 22, 23, 24, 25, |
26, 0, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 27, 28, 29, 30, 31 }; |
private static final int[] C1_HEIGHT = { 16, 22, 28, 40, 52, 70, 104, 148 }; |
private static final int[] C1_WIDTH = { 18, 22, 32, 42, 54, 76, 98, 134 }; |
private static final int[] C1_DATA_LENGTH = { 10, 19, 44, 91, 182, 370, 732, 1480 }; |
private static final int[] C1_ECC_LENGTH = { 10, 16, 26, 44, 70, 140, 280, 560 }; |
private static final int[] C1_BLOCKS = { 1, 1, 1, 1, 1, 2, 4, 8 }; |
private static final int[] C1_DATA_BLOCKS = { 10, 19, 44, 91, 182, 185, 183, 185 }; |
private static final int[] C1_ECC_BLOCKS = { 10, 16, 26, 44, 70, 70, 70, 70 }; |
private static final int[] C1_GRID_WIDTH = { 4, 5, 7, 9, 12, 17, 22, 30 }; |
private static final int[] C1_GRID_HEIGHT = { 5, 7, 10, 15, 21, 30, 46, 68 }; |
private enum Mode { |
C1_ASCII, C1_C40, C1_DECIMAL, C1_TEXT, C1_EDI, C1_BYTE |
} |
private Version preferredVersion = Version.NONE; |
private final int[] data = new int[1500]; |
private final int[][] datagrid = new int[136][120]; |
private final boolean[][] outputGrid = new boolean[148][134]; |
/** |
* Sets the preferred symbol size / version. Versions A to H are square symbols. Version S and T |
* are fixed height symbols. This value may be ignored if the input data does not fit in the |
* specified version. |
* |
* @param version the preferred symbol version |
*/ |
public void setPreferredVersion(final Version version) { |
this.preferredVersion = version; |
} |
/** |
* Returns the preferred symbol version. |
* |
* @return the preferred symbol version |
*/ |
public Version getPreferredVersion() { |
return this.preferredVersion; |
} |
@Override |
protected boolean gs1Supported() { |
return true; |
} |
@Override |
protected void encode() { |
int size = 1, i, j, data_blocks; |
int row, col; |
int sub_version = 0; |
int codewords; |
final int[] ecc = new int[600]; |
final int[] stream = new int[2100]; |
int block_width; |
final int length = this.content.length(); |
final ReedSolomon rs = new ReedSolomon(); |
int data_length; |
int data_cw, ecc_cw; |
final int[] sub_data = new int[190]; |
final StringBuilder bin = new StringBuilder(); |
if (!this.content.matches("[\u0000-\u00FF]+")) { |
throw new OkapiException("Invalid characters in input data"); |
} |
if (this.preferredVersion == Version.S) { |
/* Version S */ |
infoLine("Version: S"); |
if (length > 18) { |
throw new OkapiException("Input data too long"); |
} |
if (!this.content.matches("[0-9]+?")) { |
throw new OkapiException("Invalid characters in input"); |
} |
sub_version = 3; |
codewords = 12; |
block_width = 6; /* Version S-30 */ |
if (length <= 12) { |
sub_version = 2; |
codewords = 8; |
block_width = 4; |
} /* Version S-20 */ |
if (length <= 6) { |
sub_version = 1; |
codewords = 4; |
block_width = 2; |
} /* Version S-10 */ |
final BigInteger elreg = new BigInteger(this.content); |
for (i = 0; i < codewords; i++) { |
BigInteger codewordValue = elreg.shiftRight(5 * i); |
codewordValue = codewordValue.and(BigInteger.valueOf(0b11111)); |
this.data[codewords - i - 1] = codewordValue.intValue(); |
} |
logCodewords(codewords); |
rs.init_gf(0x25); |
rs.init_code(codewords, 1); |
rs.encode(codewords, this.data); |
infoLine("ECC Codeword Count: " + codewords); |
for (i = 0; i < codewords; i++) { |
stream[i] = this.data[i]; |
stream[i + codewords] = rs.getResult(codewords - i - 1); |
} |
for (i = 0; i < 136; i++) { |
for (j = 0; j < 120; j++) { |
this.datagrid[i][j] = '0'; |
} |
} |
i = 0; |
for (row = 0; row < 2; row++) { |
for (col = 0; col < block_width; col++) { |
if ((stream[i] & 0x10) != 0) { |
this.datagrid[row * 2][col * 5] = '1'; |
} |
if ((stream[i] & 0x08) != 0) { |
this.datagrid[row * 2][col * 5 + 1] = '1'; |
} |
if ((stream[i] & 0x04) != 0) { |
this.datagrid[row * 2][col * 5 + 2] = '1'; |
} |
if ((stream[i] & 0x02) != 0) { |
this.datagrid[row * 2 + 1][col * 5] = '1'; |
} |
if ((stream[i] & 0x01) != 0) { |
this.datagrid[row * 2 + 1][col * 5 + 1] = '1'; |
} |
if ((stream[i + 1] & 0x10) != 0) { |
this.datagrid[row * 2][col * 5 + 3] = '1'; |
} |
if ((stream[i + 1] & 0x08) != 0) { |
this.datagrid[row * 2][col * 5 + 4] = '1'; |
} |
if ((stream[i + 1] & 0x04) != 0) { |
this.datagrid[row * 2 + 1][col * 5 + 2] = '1'; |
} |
if ((stream[i + 1] & 0x02) != 0) { |
this.datagrid[row * 2 + 1][col * 5 + 3] = '1'; |
} |
if ((stream[i + 1] & 0x01) != 0) { |
this.datagrid[row * 2 + 1][col * 5 + 4] = '1'; |
} |
i += 2; |
} |
} |
infoLine("Grid Size: " + block_width + " X " + 2); |
size = 9; |
this.row_count = 8; |
this.symbol_width = 10 * sub_version + 1; |
} |
if (this.preferredVersion == Version.T) { |
/* Version T */ |
infoLine("Version: T"); |
for (i = 0; i < 40; i++) { |
this.data[i] = 0; |
} |
data_length = encodeAsCode1Data(); |
if (data_length > 38) { |
throw new OkapiException("Input data too long"); |
} |
size = 10; |
sub_version = 3; |
data_cw = 38; |
ecc_cw = 22; |
block_width = 12; |
if (data_length <= 24) { |
sub_version = 2; |
data_cw = 24; |
ecc_cw = 16; |
block_width = 8; |
} |
if (data_length <= 10) { |
sub_version = 1; |
data_cw = 10; |
ecc_cw = 10; |
block_width = 4; |
} |
logCodewords(data_length); |
for (i = data_length; i < data_cw; i++) { |
this.data[i] = 129; /* Pad */ |
} |
/* Calculate error correction data */ |
rs.init_gf(0x12d); |
rs.init_code(ecc_cw, 1); |
rs.encode(data_cw, this.data); |
infoLine("ECC Codeword Count: " + ecc_cw); |
/* "Stream" combines data and error correction data */ |
for (i = 0; i < data_cw; i++) { |
stream[i] = this.data[i]; |
} |
for (i = 0; i < ecc_cw; i++) { |
stream[data_cw + i] = rs.getResult(ecc_cw - i - 1); |
} |
for (i = 0; i < 136; i++) { |
for (j = 0; j < 120; j++) { |
this.datagrid[i][j] = '0'; |
} |
} |
i = 0; |
for (row = 0; row < 5; row++) { |
for (col = 0; col < block_width; col++) { |
if ((stream[i] & 0x80) != 0) { |
this.datagrid[row * 2][col * 4] = '1'; |
} |
if ((stream[i] & 0x40) != 0) { |
this.datagrid[row * 2][col * 4 + 1] = '1'; |
} |
if ((stream[i] & 0x20) != 0) { |
this.datagrid[row * 2][col * 4 + 2] = '1'; |
} |
if ((stream[i] & 0x10) != 0) { |
this.datagrid[row * 2][col * 4 + 3] = '1'; |
} |
if ((stream[i] & 0x08) != 0) { |
this.datagrid[row * 2 + 1][col * 4] = '1'; |
} |
if ((stream[i] & 0x04) != 0) { |
this.datagrid[row * 2 + 1][col * 4 + 1] = '1'; |
} |
if ((stream[i] & 0x02) != 0) { |
this.datagrid[row * 2 + 1][col * 4 + 2] = '1'; |
} |
if ((stream[i] & 0x01) != 0) { |
this.datagrid[row * 2 + 1][col * 4 + 3] = '1'; |
} |
i++; |
} |
} |
infoLine("Grid Size: " + block_width + " X " + 5); |
this.row_count = 16; |
this.symbol_width = sub_version * 16 + 1; |
} |
if (this.preferredVersion != Version.S && this.preferredVersion != Version.T) { |
/* Version A to H */ |
for (i = 0; i < 1500; i++) { |
this.data[i] = 0; |
} |
data_length = encodeAsCode1Data(); |
for (i = 7; i >= 0; i--) { |
if (C1_DATA_LENGTH[i] >= data_length) { |
size = i + 1; |
} |
} |
if (getSize(this.preferredVersion) > size) { |
size = getSize(this.preferredVersion); |
} |
final char version = (char) (size - 1 + 'A'); |
infoLine("Version: " + version); |
logCodewords(data_length); |
for (i = data_length; i < C1_DATA_LENGTH[size - 1]; i++) { |
this.data[i] = 129; /* Pad */ |
} |
/* Calculate error correction data */ |
data_length = C1_DATA_LENGTH[size - 1]; |
data_blocks = C1_BLOCKS[size - 1]; |
rs.init_gf(0x12d); |
rs.init_code(C1_ECC_BLOCKS[size - 1], 0); |
for (i = 0; i < data_blocks; i++) { |
for (j = 0; j < C1_DATA_BLOCKS[size - 1]; j++) { |
sub_data[j] = this.data[j * data_blocks + i]; |
} |
rs.encode(C1_DATA_BLOCKS[size - 1], sub_data); |
for (j = 0; j < C1_ECC_BLOCKS[size - 1]; j++) { |
ecc[C1_ECC_LENGTH[size - 1] - (j * data_blocks + i) - 1] = rs.getResult(j); |
} |
} |
infoLine("ECC Codeword Count: " + C1_ECC_LENGTH[size - 1]); |
/* "Stream" combines data and error correction data */ |
for (i = 0; i < data_length; i++) { |
stream[i] = this.data[i]; |
} |
for (i = 0; i < C1_ECC_LENGTH[size - 1]; i++) { |
stream[data_length + i] = ecc[i]; |
} |
for (i = 0; i < 136; i++) { |
for (j = 0; j < 120; j++) { |
this.datagrid[i][j] = '0'; |
} |
} |
i = 0; |
for (row = 0; row < C1_GRID_HEIGHT[size - 1]; row++) { |
for (col = 0; col < C1_GRID_WIDTH[size - 1]; col++) { |
if ((stream[i] & 0x80) != 0) { |
this.datagrid[row * 2][col * 4] = '1'; |
} |
if ((stream[i] & 0x40) != 0) { |
this.datagrid[row * 2][col * 4 + 1] = '1'; |
} |
if ((stream[i] & 0x20) != 0) { |
this.datagrid[row * 2][col * 4 + 2] = '1'; |
} |
if ((stream[i] & 0x10) != 0) { |
this.datagrid[row * 2][col * 4 + 3] = '1'; |
} |
if ((stream[i] & 0x08) != 0) { |
this.datagrid[row * 2 + 1][col * 4] = '1'; |
} |
if ((stream[i] & 0x04) != 0) { |
this.datagrid[row * 2 + 1][col * 4 + 1] = '1'; |
} |
if ((stream[i] & 0x02) != 0) { |
this.datagrid[row * 2 + 1][col * 4 + 2] = '1'; |
} |
if ((stream[i] & 0x01) != 0) { |
this.datagrid[row * 2 + 1][col * 4 + 3] = '1'; |
} |
i++; |
} |
} |
infoLine("Grid Size: " + C1_GRID_WIDTH[size - 1] + " X " + C1_GRID_HEIGHT[size - 1]); |
this.row_count = C1_HEIGHT[size - 1]; |
this.symbol_width = C1_WIDTH[size - 1]; |
} |
for (i = 0; i < 148; i++) { |
for (j = 0; j < 134; j++) { |
this.outputGrid[i][j] = false; |
} |
} |
switch (size) { |
case 1: |
/* Version A */ |
plotCentralFinder(6, 3, 1); |
plotVerticalBar(4, 6, 1); |
plotVerticalBar(12, 5, 0); |
setGridModule(5, 12); |
plotSpigot(0); |
plotSpigot(15); |
plotDataBlock(0, 0, 5, 4, 0, 0); |
plotDataBlock(0, 4, 5, 12, 0, 2); |
plotDataBlock(5, 0, 5, 12, 6, 0); |
plotDataBlock(5, 12, 5, 4, 6, 2); |
break; |
case 2: |
/* Version B */ |
plotCentralFinder(8, 4, 1); |
plotVerticalBar(4, 8, 1); |
plotVerticalBar(16, 7, 0); |
setGridModule(7, 16); |
plotSpigot(0); |
plotSpigot(21); |
plotDataBlock(0, 0, 7, 4, 0, 0); |
plotDataBlock(0, 4, 7, 16, 0, 2); |
plotDataBlock(7, 0, 7, 16, 8, 0); |
plotDataBlock(7, 16, 7, 4, 8, 2); |
break; |
case 3: |
/* Version C */ |
plotCentralFinder(11, 4, 2); |
plotVerticalBar(4, 11, 1); |
plotVerticalBar(26, 13, 1); |
plotVerticalBar(4, 10, 0); |
plotVerticalBar(26, 10, 0); |
plotSpigot(0); |
plotSpigot(27); |
plotDataBlock(0, 0, 10, 4, 0, 0); |
plotDataBlock(0, 4, 10, 20, 0, 2); |
plotDataBlock(0, 24, 10, 4, 0, 4); |
plotDataBlock(10, 0, 10, 4, 8, 0); |
plotDataBlock(10, 4, 10, 20, 8, 2); |
plotDataBlock(10, 24, 10, 4, 8, 4); |
break; |
case 4: |
/* Version D */ |
plotCentralFinder(16, 5, 1); |
plotVerticalBar(4, 16, 1); |
plotVerticalBar(20, 16, 1); |
plotVerticalBar(36, 16, 1); |
plotVerticalBar(4, 15, 0); |
plotVerticalBar(20, 15, 0); |
plotVerticalBar(36, 15, 0); |
plotSpigot(0); |
plotSpigot(12); |
plotSpigot(27); |
plotSpigot(39); |
plotDataBlock(0, 0, 15, 4, 0, 0); |
plotDataBlock(0, 4, 15, 14, 0, 2); |
plotDataBlock(0, 18, 15, 14, 0, 4); |
plotDataBlock(0, 32, 15, 4, 0, 6); |
plotDataBlock(15, 0, 15, 4, 10, 0); |
plotDataBlock(15, 4, 15, 14, 10, 2); |
plotDataBlock(15, 18, 15, 14, 10, 4); |
plotDataBlock(15, 32, 15, 4, 10, 6); |
break; |
case 5: |
/* Version E */ |
plotCentralFinder(22, 5, 2); |
plotVerticalBar(4, 22, 1); |
plotVerticalBar(26, 24, 1); |
plotVerticalBar(48, 22, 1); |
plotVerticalBar(4, 21, 0); |
plotVerticalBar(26, 21, 0); |
plotVerticalBar(48, 21, 0); |
plotSpigot(0); |
plotSpigot(12); |
plotSpigot(39); |
plotSpigot(51); |
plotDataBlock(0, 0, 21, 4, 0, 0); |
plotDataBlock(0, 4, 21, 20, 0, 2); |
plotDataBlock(0, 24, 21, 20, 0, 4); |
plotDataBlock(0, 44, 21, 4, 0, 6); |
plotDataBlock(21, 0, 21, 4, 10, 0); |
plotDataBlock(21, 4, 21, 20, 10, 2); |
plotDataBlock(21, 24, 21, 20, 10, 4); |
plotDataBlock(21, 44, 21, 4, 10, 6); |
break; |
case 6: |
/* Version F */ |
plotCentralFinder(31, 5, 3); |
plotVerticalBar(4, 31, 1); |
plotVerticalBar(26, 35, 1); |
plotVerticalBar(48, 31, 1); |
plotVerticalBar(70, 35, 1); |
plotVerticalBar(4, 30, 0); |
plotVerticalBar(26, 30, 0); |
plotVerticalBar(48, 30, 0); |
plotVerticalBar(70, 30, 0); |
plotSpigot(0); |
plotSpigot(12); |
plotSpigot(24); |
plotSpigot(45); |
plotSpigot(57); |
plotSpigot(69); |
plotDataBlock(0, 0, 30, 4, 0, 0); |
plotDataBlock(0, 4, 30, 20, 0, 2); |
plotDataBlock(0, 24, 30, 20, 0, 4); |
plotDataBlock(0, 44, 30, 20, 0, 6); |
plotDataBlock(0, 64, 30, 4, 0, 8); |
plotDataBlock(30, 0, 30, 4, 10, 0); |
plotDataBlock(30, 4, 30, 20, 10, 2); |
plotDataBlock(30, 24, 30, 20, 10, 4); |
plotDataBlock(30, 44, 30, 20, 10, 6); |
plotDataBlock(30, 64, 30, 4, 10, 8); |
break; |
case 7: |
/* Version G */ |
plotCentralFinder(47, 6, 2); |
plotVerticalBar(6, 47, 1); |
plotVerticalBar(27, 49, 1); |
plotVerticalBar(48, 47, 1); |
plotVerticalBar(69, 49, 1); |
plotVerticalBar(90, 47, 1); |
plotVerticalBar(6, 46, 0); |
plotVerticalBar(27, 46, 0); |
plotVerticalBar(48, 46, 0); |
plotVerticalBar(69, 46, 0); |
plotVerticalBar(90, 46, 0); |
plotSpigot(0); |
plotSpigot(12); |
plotSpigot(24); |
plotSpigot(36); |
plotSpigot(67); |
plotSpigot(79); |
plotSpigot(91); |
plotSpigot(103); |
plotDataBlock(0, 0, 46, 6, 0, 0); |
plotDataBlock(0, 6, 46, 19, 0, 2); |
plotDataBlock(0, 25, 46, 19, 0, 4); |
plotDataBlock(0, 44, 46, 19, 0, 6); |
plotDataBlock(0, 63, 46, 19, 0, 8); |
plotDataBlock(0, 82, 46, 6, 0, 10); |
plotDataBlock(46, 0, 46, 6, 12, 0); |
plotDataBlock(46, 6, 46, 19, 12, 2); |
plotDataBlock(46, 25, 46, 19, 12, 4); |
plotDataBlock(46, 44, 46, 19, 12, 6); |
plotDataBlock(46, 63, 46, 19, 12, 8); |
plotDataBlock(46, 82, 46, 6, 12, 10); |
break; |
case 8: |
/* Version H */ |
plotCentralFinder(69, 6, 3); |
plotVerticalBar(6, 69, 1); |
plotVerticalBar(26, 73, 1); |
plotVerticalBar(46, 69, 1); |
plotVerticalBar(66, 73, 1); |
plotVerticalBar(86, 69, 1); |
plotVerticalBar(106, 73, 1); |
plotVerticalBar(126, 69, 1); |
plotVerticalBar(6, 68, 0); |
plotVerticalBar(26, 68, 0); |
plotVerticalBar(46, 68, 0); |
plotVerticalBar(66, 68, 0); |
plotVerticalBar(86, 68, 0); |
plotVerticalBar(106, 68, 0); |
plotVerticalBar(126, 68, 0); |
plotSpigot(0); |
plotSpigot(12); |
plotSpigot(24); |
plotSpigot(36); |
plotSpigot(48); |
plotSpigot(60); |
plotSpigot(87); |
plotSpigot(99); |
plotSpigot(111); |
plotSpigot(123); |
plotSpigot(135); |
plotSpigot(147); |
plotDataBlock(0, 0, 68, 6, 0, 0); |
plotDataBlock(0, 6, 68, 18, 0, 2); |
plotDataBlock(0, 24, 68, 18, 0, 4); |
plotDataBlock(0, 42, 68, 18, 0, 6); |
plotDataBlock(0, 60, 68, 18, 0, 8); |
plotDataBlock(0, 78, 68, 18, 0, 10); |
plotDataBlock(0, 96, 68, 18, 0, 12); |
plotDataBlock(0, 114, 68, 6, 0, 14); |
plotDataBlock(68, 0, 68, 6, 12, 0); |
plotDataBlock(68, 6, 68, 18, 12, 2); |
plotDataBlock(68, 24, 68, 18, 12, 4); |
plotDataBlock(68, 42, 68, 18, 12, 6); |
plotDataBlock(68, 60, 68, 18, 12, 8); |
plotDataBlock(68, 78, 68, 18, 12, 10); |
plotDataBlock(68, 96, 68, 18, 12, 12); |
plotDataBlock(68, 114, 68, 6, 12, 14); |
break; |
case 9: |
/* Version S */ |
plotHorizontalBar(5, 1); |
plotHorizontalBar(7, 1); |
setGridModule(6, 0); |
setGridModule(6, this.symbol_width - 1); |
resetGridModule(7, 1); |
resetGridModule(7, this.symbol_width - 2); |
switch (sub_version) { |
case 1: |
/* Version S-10 */ |
setGridModule(0, 5); |
plotDataBlock(0, 0, 4, 5, 0, 0); |
plotDataBlock(0, 5, 4, 5, 0, 1); |
break; |
case 2: |
/* Version S-20 */ |
setGridModule(0, 10); |
setGridModule(4, 10); |
plotDataBlock(0, 0, 4, 10, 0, 0); |
plotDataBlock(0, 10, 4, 10, 0, 1); |
break; |
case 3: |
/* Version S-30 */ |
setGridModule(0, 15); |
setGridModule(4, 15); |
setGridModule(6, 15); |
plotDataBlock(0, 0, 4, 15, 0, 0); |
plotDataBlock(0, 15, 4, 15, 0, 1); |
break; |
} |
break; |
case 10: |
/* Version T */ |
plotHorizontalBar(11, 1); |
plotHorizontalBar(13, 1); |
plotHorizontalBar(15, 1); |
setGridModule(12, 0); |
setGridModule(12, this.symbol_width - 1); |
setGridModule(14, 0); |
setGridModule(14, this.symbol_width - 1); |
resetGridModule(13, 1); |
resetGridModule(13, this.symbol_width - 2); |
resetGridModule(15, 1); |
resetGridModule(15, this.symbol_width - 2); |
switch (sub_version) { |
case 1: |
/* Version T-16 */ |
setGridModule(0, 8); |
setGridModule(10, 8); |
plotDataBlock(0, 0, 10, 8, 0, 0); |
plotDataBlock(0, 8, 10, 8, 0, 1); |
break; |
case 2: |
/* Version T-32 */ |
setGridModule(0, 16); |
setGridModule(10, 16); |
setGridModule(12, 16); |
plotDataBlock(0, 0, 10, 16, 0, 0); |
plotDataBlock(0, 16, 10, 16, 0, 1); |
break; |
case 3: |
/* Verion T-48 */ |
setGridModule(0, 24); |
setGridModule(10, 24); |
setGridModule(12, 24); |
setGridModule(14, 24); |
plotDataBlock(0, 0, 10, 24, 0, 0); |
plotDataBlock(0, 24, 10, 24, 0, 1); |
break; |
} |
break; |
} |
this.readable = ""; |
this.pattern = new String[this.row_count]; |
this.row_height = new int[this.row_count]; |
for (i = 0; i < this.row_count; i++) { |
bin.setLength(0); |
for (j = 0; j < this.symbol_width; j++) { |
if (this.outputGrid[i][j]) { |
bin.append('1'); |
} else { |
bin.append('0'); |
} |
} |
this.pattern[i] = bin2pat(bin); |
this.row_height[i] = 1; |
} |
} |
private void logCodewords(final int count) { |
info("Codewords: "); |
for (int i = 0; i < count; i++) { |
infoSpace(this.data[i]); |
} |
infoLine(); |
} |
private int encodeAsCode1Data() { |
Mode current_mode, next_mode; |
boolean latch; |
boolean done; |
int sourcePoint, targetPoint, i, j; |
int c40_p; |
int text_p; |
int edi_p; |
int byte_start = 0; |
final int[] c40_buffer = new int[6]; |
final int[] text_buffer = new int[6]; |
final int[] edi_buffer = new int[6]; |
String decimal_binary = ""; |
int length; |
int shift_set, value; |
int data_left, decimal_count; |
int sub_value; |
int bits_left_in_byte, target_count; |
boolean isTwoDigits; |
this.inputData = toBytes(this.content, StandardCharsets.ISO_8859_1); |
length = this.inputData.length; |
sourcePoint = 0; |
targetPoint = 0; |
c40_p = 0; |
text_p = 0; |
edi_p = 0; |
if (this.inputDataType == DataType.GS1) { |
this.data[targetPoint] = 232; |
targetPoint++; |
} /* FNC1 */ |
/* Step A */ |
current_mode = Mode.C1_ASCII; |
next_mode = Mode.C1_ASCII; |
do { |
if (current_mode != next_mode) { |
/* Change mode */ |
switch (next_mode) { |
case C1_C40: |
this.data[targetPoint] = 230; |
targetPoint++; |
break; |
case C1_TEXT: |
this.data[targetPoint] = 239; |
targetPoint++; |
break; |
case C1_EDI: |
this.data[targetPoint] = 238; |
targetPoint++; |
break; |
case C1_BYTE: |
this.data[targetPoint] = 231; |
targetPoint++; |
break; |
} |
} |
if (current_mode != Mode.C1_BYTE && next_mode == Mode.C1_BYTE) { |
byte_start = targetPoint; |
} |
current_mode = next_mode; |
if (current_mode == Mode.C1_ASCII) { /* Step B - ASCII encodation */ |
next_mode = Mode.C1_ASCII; |
if (length - sourcePoint >= 21) { /* Step B1 */ |
j = 0; |
for (i = 0; i < 21; i++) { |
if (this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9') { |
j++; |
} |
} |
if (j == 21) { |
next_mode = Mode.C1_DECIMAL; |
decimal_binary += "1111"; |
} |
} |
if (next_mode == Mode.C1_ASCII && length - sourcePoint >= 13) { /* Step B2 */ |
j = 0; |
for (i = 0; i < 13; i++) { |
if (this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9') { |
j++; |
} |
} |
if (j == 13) { |
latch = false; |
for (i = sourcePoint + 13; i < length; i++) { |
if (!(this.inputData[i] >= '0' && this.inputData[i] <= '9')) { |
latch = true; |
} |
} |
if (!latch) { |
next_mode = Mode.C1_DECIMAL; |
decimal_binary += "1111"; |
} |
} |
} |
if (next_mode == Mode.C1_ASCII) { /* Step B3 */ |
isTwoDigits = false; |
if (sourcePoint + 1 != length) { |
if (this.inputData[sourcePoint] >= '0' && this.inputData[sourcePoint] <= '9') { |
if (this.inputData[sourcePoint + 1] >= '0' && this.inputData[sourcePoint + 1] <= '9') { |
// remaining data consists of two numeric digits |
this.data[targetPoint] = 10 * (this.inputData[sourcePoint] - '0') + this.inputData[sourcePoint + 1] - '0' + 130; |
targetPoint++; |
sourcePoint += 2; |
isTwoDigits = true; |
} |
} |
} |
if (!isTwoDigits) { |
if (this.inputData[sourcePoint] == FNC1) { |
if (length - sourcePoint >= 15) { /* Step B4 */ |
j = 0; |
for (i = 0; i < 15; i++) { |
if (this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9') { |
j++; |
} |
} |
if (j == 15) { |
this.data[targetPoint] = 236; /* FNC1 and change to Decimal */ |
targetPoint++; |
sourcePoint++; |
next_mode = Mode.C1_DECIMAL; |
} |
} |
if (length - sourcePoint >= 7) { /* Step B5 */ |
j = 0; |
for (i = 0; i < 7; i++) { |
if (this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9') { |
j++; |
} |
} |
if (j == 7) { |
latch = false; |
for (i = sourcePoint + 7; i < length; i++) { |
if (!(this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9')) { |
latch = true; |
} |
} |
if (!latch) { |
this.data[targetPoint] = 236; /* |
* FNC1 and change to Decimal |
*/ |
targetPoint++; |
sourcePoint++; |
next_mode = Mode.C1_DECIMAL; |
} |
} |
} |
} |
if (next_mode == Mode.C1_ASCII) { |
/* Step B6 */ |
next_mode = lookAheadTest(length, sourcePoint, current_mode); |
if (next_mode == Mode.C1_ASCII) { |
if (this.inputData[sourcePoint] > 127) { |
/* Step B7 */ |
this.data[targetPoint] = 235; |
targetPoint++; /* FNC4 */ |
this.data[targetPoint] = this.inputData[sourcePoint] - 128 + 1; |
targetPoint++; |
sourcePoint++; |
} else { |
/* Step B8 */ |
if (this.inputData[sourcePoint] == FNC1) { |
this.data[targetPoint] = 232; |
targetPoint++; |
sourcePoint++; /* FNC1 */ |
} else { |
this.data[targetPoint] = this.inputData[sourcePoint] + 1; |
targetPoint++; |
sourcePoint++; |
} |
} |
} |
} |
} |
} |
} |
if (current_mode == Mode.C1_C40) { /* Step C - C40 encodation */ |
done = false; |
next_mode = Mode.C1_C40; |
if (c40_p == 0) { |
if (length - sourcePoint >= 12) { |
j = 0; |
for (i = 0; i < 12; i++) { |
if (this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9') { |
j++; |
} |
} |
if (j == 12) { |
next_mode = Mode.C1_ASCII; |
done = true; |
} |
} |
if (length - sourcePoint >= 8) { |
j = 0; |
for (i = 0; i < 8; i++) { |
if (this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9') { |
j++; |
} |
} |
if (length - sourcePoint == 8) { |
latch = true; |
} else { |
latch = true; |
for (j = sourcePoint + 8; j < length; j++) { |
if (this.inputData[j] <= '0' || this.inputData[j] >= '9') { |
latch = false; |
} |
} |
} |
if (j == 8 && latch) { |
next_mode = Mode.C1_ASCII; |
done = true; |
} |
} |
if (!done) { |
next_mode = lookAheadTest(length, sourcePoint, current_mode); |
} |
} |
if (next_mode != Mode.C1_C40) { |
this.data[targetPoint] = 255; |
targetPoint++; /* Unlatch */ |
} else { |
if (this.inputData[sourcePoint] > 127) { |
c40_buffer[c40_p] = 1; |
c40_p++; |
c40_buffer[c40_p] = 30; |
c40_p++; /* Upper Shift */ |
shift_set = C40_SHIFT[this.inputData[sourcePoint] - 128]; |
value = C40_VALUE[this.inputData[sourcePoint] - 128]; |
} else { |
shift_set = C40_SHIFT[this.inputData[sourcePoint]]; |
value = C40_VALUE[this.inputData[sourcePoint]]; |
} |
if (this.inputData[sourcePoint] == FNC1) { |
shift_set = 2; |
value = 27; /* FNC1 */ |
} |
if (shift_set != 0) { |
c40_buffer[c40_p] = shift_set - 1; |
c40_p++; |
} |
c40_buffer[c40_p] = value; |
c40_p++; |
if (c40_p >= 3) { |
int iv; |
iv = 1600 * c40_buffer[0] + 40 * c40_buffer[1] + c40_buffer[2] + 1; |
this.data[targetPoint] = iv / 256; |
targetPoint++; |
this.data[targetPoint] = iv % 256; |
targetPoint++; |
c40_buffer[0] = c40_buffer[3]; |
c40_buffer[1] = c40_buffer[4]; |
c40_buffer[2] = c40_buffer[5]; |
c40_buffer[3] = 0; |
c40_buffer[4] = 0; |
c40_buffer[5] = 0; |
c40_p -= 3; |
} |
sourcePoint++; |
} |
} |
if (current_mode == Mode.C1_TEXT) { /* Step D - Text encodation */ |
done = false; |
next_mode = Mode.C1_TEXT; |
if (text_p == 0) { |
if (length - sourcePoint >= 12) { |
j = 0; |
for (i = 0; i < 12; i++) { |
if (this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9') { |
j++; |
} |
} |
if (j == 12) { |
next_mode = Mode.C1_ASCII; |
done = true; |
} |
} |
if (length - sourcePoint >= 8) { |
j = 0; |
for (i = 0; i < 8; i++) { |
if (this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9') { |
j++; |
} |
} |
if (length - sourcePoint == 8) { |
latch = true; |
} else { |
latch = true; |
for (j = sourcePoint + 8; j < length; j++) { |
if (this.inputData[j] <= '0' || this.inputData[j] >= '9') { |
latch = false; |
} |
} |
} |
if (j == 8 && latch) { |
next_mode = Mode.C1_ASCII; |
done = true; |
} |
} |
if (!done) { |
next_mode = lookAheadTest(length, sourcePoint, current_mode); |
} |
} |
if (next_mode != Mode.C1_TEXT) { |
this.data[targetPoint] = 255; |
targetPoint++; /* Unlatch */ |
} else { |
if (this.inputData[sourcePoint] > 127) { |
text_buffer[text_p] = 1; |
text_p++; |
text_buffer[text_p] = 30; |
text_p++; /* Upper Shift */ |
shift_set = TEXT_SHIFT[this.inputData[sourcePoint] - 128]; |
value = TEXT_VALUE[this.inputData[sourcePoint] - 128]; |
} else { |
shift_set = TEXT_SHIFT[this.inputData[sourcePoint]]; |
value = TEXT_VALUE[this.inputData[sourcePoint]]; |
} |
if (this.inputData[sourcePoint] == FNC1) { |
shift_set = 2; |
value = 27; /* FNC1 */ |
} |
if (shift_set != 0) { |
text_buffer[text_p] = shift_set - 1; |
text_p++; |
} |
text_buffer[text_p] = value; |
text_p++; |
if (text_p >= 3) { |
int iv; |
iv = 1600 * text_buffer[0] + 40 * text_buffer[1] + text_buffer[2] + 1; |
this.data[targetPoint] = iv / 256; |
targetPoint++; |
this.data[targetPoint] = iv % 256; |
targetPoint++; |
text_buffer[0] = text_buffer[3]; |
text_buffer[1] = text_buffer[4]; |
text_buffer[2] = text_buffer[5]; |
text_buffer[3] = 0; |
text_buffer[4] = 0; |
text_buffer[5] = 0; |
text_p -= 3; |
} |
sourcePoint++; |
} |
} |
if (current_mode == Mode.C1_EDI) { /* Step E - EDI Encodation */ |
value = 0; |
next_mode = Mode.C1_EDI; |
if (edi_p == 0) { |
if (length - sourcePoint >= 12) { |
j = 0; |
for (i = 0; i < 12; i++) { |
if (this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9') { |
j++; |
} |
} |
if (j == 12) { |
next_mode = Mode.C1_ASCII; |
} |
} |
if (length - sourcePoint >= 8) { |
j = 0; |
for (i = 0; i < 8; i++) { |
if (this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9') { |
j++; |
} |
} |
if (length - sourcePoint == 8) { |
latch = true; |
} else { |
latch = true; |
for (j = sourcePoint + 8; j < length; j++) { |
if (this.inputData[j] <= '0' || this.inputData[j] >= '9') { |
latch = false; |
} |
} |
} |
if (j == 8 && latch) { |
next_mode = Mode.C1_ASCII; |
} |
} |
if (!(isEdiEncodable(this.inputData[sourcePoint]) && isEdiEncodable(this.inputData[sourcePoint + 1]) && isEdiEncodable(this.inputData[sourcePoint + 2]))) { |
next_mode = Mode.C1_ASCII; |
} |
} |
if (next_mode != Mode.C1_EDI) { |
this.data[targetPoint] = 255; |
targetPoint++; /* Unlatch */ |
} else { |
if (this.inputData[sourcePoint] == 13) { |
value = 0; |
} |
if (this.inputData[sourcePoint] == '*') { |
value = 1; |
} |
if (this.inputData[sourcePoint] == '>') { |
value = 2; |
} |
if (this.inputData[sourcePoint] == ' ') { |
value = 3; |
} |
if (this.inputData[sourcePoint] >= '0' && this.inputData[sourcePoint] <= '9') { |
value = this.inputData[sourcePoint] - '0' + 4; |
} |
if (this.inputData[sourcePoint] >= 'A' && this.inputData[sourcePoint] <= 'Z') { |
value = this.inputData[sourcePoint] - 'A' + 14; |
} |
edi_buffer[edi_p] = value; |
edi_p++; |
if (edi_p >= 3) { |
int iv; |
iv = 1600 * edi_buffer[0] + 40 * edi_buffer[1] + edi_buffer[2] + 1; |
this.data[targetPoint] = iv / 256; |
targetPoint++; |
this.data[targetPoint] = iv % 256; |
targetPoint++; |
edi_buffer[0] = edi_buffer[3]; |
edi_buffer[1] = edi_buffer[4]; |
edi_buffer[2] = edi_buffer[5]; |
edi_buffer[3] = 0; |
edi_buffer[4] = 0; |
edi_buffer[5] = 0; |
edi_p -= 3; |
} |
sourcePoint++; |
} |
} |
if (current_mode == Mode.C1_DECIMAL) { /* Step F - Decimal encodation */ |
next_mode = Mode.C1_DECIMAL; |
data_left = length - sourcePoint; |
decimal_count = 0; |
if (data_left >= 1) { |
if (this.inputData[sourcePoint] >= '0' && this.inputData[sourcePoint] <= '9') { |
decimal_count = 1; |
} |
} |
if (data_left >= 2) { |
if (decimal_count == 1 && this.inputData[sourcePoint + 1] >= '0' && this.inputData[sourcePoint + 1] <= '9') { |
decimal_count = 2; |
} |
} |
if (data_left >= 3) { |
if (decimal_count == 2 && this.inputData[sourcePoint + 2] >= '0' && this.inputData[sourcePoint + 2] <= '9') { |
decimal_count = 3; |
} |
} |
if (decimal_count != 3) { |
/* Finish Decimal mode and go back to ASCII */ |
decimal_binary += "111111"; /* Unlatch */ |
target_count = 3; |
if (decimal_binary.length() <= 16) { |
target_count = 2; |
} |
if (decimal_binary.length() <= 8) { |
target_count = 1; |
} |
bits_left_in_byte = 8 * target_count - decimal_binary.length(); |
if (bits_left_in_byte == 8) { |
bits_left_in_byte = 0; |
} |
if (bits_left_in_byte == 2) { |
decimal_binary += "01"; |
} |
if (bits_left_in_byte == 4 || bits_left_in_byte == 6) { |
if (decimal_count >= 1) { |
sub_value = this.inputData[sourcePoint] - '0' + 1; |
for (i = 0x08; i > 0; i = i >> 1) { |
if ((sub_value & i) != 0) { |
decimal_binary += "1"; |
} else { |
decimal_binary += "0"; |
} |
} |
sourcePoint++; |
} else { |
decimal_binary += "1111"; |
} |
} |
if (bits_left_in_byte == 6) { |
decimal_binary += "01"; |
} |
/* Binary buffer is full - transfer to data */ |
if (target_count >= 1) { |
for (i = 0; i < 8; i++) { |
if (decimal_binary.charAt(i) == '1') { |
this.data[targetPoint] += 128 >> i; |
} |
} |
targetPoint++; |
} |
if (target_count >= 2) { |
for (i = 0; i < 8; i++) { |
if (decimal_binary.charAt(8 + i) == '1') { |
this.data[targetPoint] += 128 >> i; |
} |
} |
targetPoint++; |
} |
if (target_count == 3) { |
for (i = 0; i < 8; i++) { |
if (decimal_binary.charAt(16 + i) == '1') { |
this.data[targetPoint] += 128 >> i; |
} |
} |
targetPoint++; |
} |
next_mode = Mode.C1_ASCII; |
} else { |
/* There are three digits - convert the value to binary */ |
value = 100 * (this.inputData[sourcePoint] - '0') + 10 * (this.inputData[sourcePoint + 1] - '0') + this.inputData[sourcePoint + 2] - '0' + 1; |
for (i = 0x200; i > 0; i = i >> 1) { |
if ((value & i) != 0) { |
decimal_binary += "1"; |
} else { |
decimal_binary += "0"; |
} |
} |
sourcePoint += 3; |
} |
if (decimal_binary.length() >= 24) { |
/* Binary buffer is full - transfer to data */ |
for (i = 0; i < 8; i++) { |
if (decimal_binary.charAt(i) == '1') { |
this.data[targetPoint] += 128 >> i; |
} |
if (decimal_binary.charAt(8 + i) == '1') { |
this.data[targetPoint + 1] += 128 >> i; |
} |
if (decimal_binary.charAt(16 + i) == '1') { |
this.data[targetPoint + 2] += 128 >> i; |
} |
} |
targetPoint += 3; |
if (decimal_binary.length() > 24) { |
decimal_binary = decimal_binary.substring(24); |
} |
} |
} |
if (current_mode == Mode.C1_BYTE) { |
next_mode = Mode.C1_BYTE; |
if (this.inputData[sourcePoint] == FNC1) { |
next_mode = Mode.C1_ASCII; |
} else { |
if (this.inputData[sourcePoint] <= 127) { |
next_mode = lookAheadTest(length, sourcePoint, current_mode); |
} |
} |
if (next_mode != Mode.C1_BYTE) { |
/* Insert byte field length */ |
if (targetPoint - byte_start <= 249) { |
for (i = targetPoint; i >= byte_start; i--) { |
this.data[i + 1] = this.data[i]; |
} |
this.data[byte_start] = targetPoint - byte_start; |
targetPoint++; |
} else { |
for (i = targetPoint; i >= byte_start; i--) { |
this.data[i + 2] = this.data[i]; |
} |
this.data[byte_start] = 249 + (targetPoint - byte_start) / 250; |
this.data[byte_start + 1] = (targetPoint - byte_start) % 250; |
targetPoint += 2; |
} |
} else { |
this.data[targetPoint] = this.inputData[sourcePoint]; |
targetPoint++; |
sourcePoint++; |
} |
} |
if (targetPoint > 1480) { |
/* Data is too large for symbol */ |
throw new OkapiException("Input data too long"); |
} |
} while (sourcePoint < length); |
/* Empty buffers */ |
if (c40_p == 2) { |
int iv; |
c40_buffer[2] = 1; |
iv = 1600 * c40_buffer[0] + 40 * c40_buffer[1] + c40_buffer[2] + 1; |
this.data[targetPoint] = iv / 256; |
targetPoint++; |
this.data[targetPoint] = iv % 256; |
targetPoint++; |
this.data[targetPoint] = 255; |
targetPoint++; /* Unlatch */ |
} |
if (c40_p == 1) { |
int iv; |
c40_buffer[1] = 1; |
c40_buffer[2] = 31; /* Pad */ |
iv = 1600 * c40_buffer[0] + 40 * c40_buffer[1] + c40_buffer[2] + 1; |
this.data[targetPoint] = iv / 256; |
targetPoint++; |
this.data[targetPoint] = iv % 256; |
targetPoint++; |
this.data[targetPoint] = 255; |
targetPoint++; /* Unlatch */ |
} |
if (text_p == 2) { |
int iv; |
text_buffer[2] = 1; |
iv = 1600 * text_buffer[0] + 40 * text_buffer[1] + text_buffer[2] + 1; |
this.data[targetPoint] = iv / 256; |
targetPoint++; |
this.data[targetPoint] = iv % 256; |
targetPoint++; |
this.data[targetPoint] = 255; |
targetPoint++; /* Unlatch */ |
} |
if (text_p == 1) { |
int iv; |
text_buffer[1] = 1; |
text_buffer[2] = 31; /* Pad */ |
iv = 1600 * text_buffer[0] + 40 * text_buffer[1] + text_buffer[2] + 1; |
this.data[targetPoint] = iv / 256; |
targetPoint++; |
this.data[targetPoint] = iv % 256; |
targetPoint++; |
this.data[targetPoint] = 255; |
targetPoint++; /* Unlatch */ |
} |
if (current_mode == Mode.C1_DECIMAL) { |
/* Finish Decimal mode and go back to ASCII */ |
decimal_binary += "111111"; /* Unlatch */ |
target_count = 3; |
if (decimal_binary.length() <= 16) { |
target_count = 2; |
} |
if (decimal_binary.length() <= 8) { |
target_count = 1; |
} |
bits_left_in_byte = 8 * target_count - decimal_binary.length(); |
if (bits_left_in_byte == 8) { |
bits_left_in_byte = 0; |
} |
if (bits_left_in_byte == 2) { |
decimal_binary += "01"; |
} |
if (bits_left_in_byte == 4 || bits_left_in_byte == 6) { |
decimal_binary += "1111"; |
} |
if (bits_left_in_byte == 6) { |
decimal_binary += "01"; |
} |
/* Binary buffer is full - transfer to data */ |
if (target_count >= 1) { |
for (i = 0; i < 8; i++) { |
if (decimal_binary.charAt(i) == '1') { |
this.data[targetPoint] += 128 >> i; |
} |
} |
targetPoint++; |
} |
if (target_count >= 2) { |
for (i = 0; i < 8; i++) { |
if (decimal_binary.charAt(8 + i) == '1') { |
this.data[targetPoint] += 128 >> i; |
} |
} |
targetPoint++; |
} |
if (target_count == 3) { |
for (i = 0; i < 8; i++) { |
if (decimal_binary.charAt(16 + i) == '1') { |
this.data[targetPoint] += 128 >> i; |
} |
} |
targetPoint++; |
} |
} |
if (current_mode == Mode.C1_BYTE) { |
/* Insert byte field length */ |
if (targetPoint - byte_start <= 249) { |
for (i = targetPoint; i >= byte_start; i--) { |
this.data[i + 1] = this.data[i]; |
} |
this.data[byte_start] = targetPoint - byte_start; |
targetPoint++; |
} else { |
for (i = targetPoint; i >= byte_start; i--) { |
this.data[i + 2] = this.data[i]; |
} |
this.data[byte_start] = 249 + (targetPoint - byte_start) / 250; |
this.data[byte_start + 1] = (targetPoint - byte_start) % 250; |
targetPoint += 2; |
} |
} |
/* Re-check length of data */ |
if (targetPoint > 1480) { |
/* Data is too large for symbol */ |
throw new OkapiException("Input data too long"); |
} |
return targetPoint; |
} |
private Mode lookAheadTest(final int sourcelen, final int position, final Mode current_mode) { |
double ascii_count, c40_count, text_count, edi_count, byte_count; |
int reduced_char; |
int done, best_count, sp; |
Mode best_scheme; |
/* Step J */ |
if (current_mode == Mode.C1_ASCII) { |
ascii_count = 0.0; |
c40_count = 1.0; |
text_count = 1.0; |
edi_count = 1.0; |
byte_count = 2.0; |
} else { |
ascii_count = 1.0; |
c40_count = 2.0; |
text_count = 2.0; |
edi_count = 2.0; |
byte_count = 3.0; |
} |
switch (current_mode) { |
case C1_C40: |
c40_count = 0.0; |
break; |
case C1_TEXT: |
text_count = 0.0; |
break; |
case C1_BYTE: |
byte_count = 0.0; |
break; |
case C1_EDI: |
edi_count = 0.0; |
break; |
} |
for (sp = position; sp < sourcelen && sp <= position + 8; sp++) { |
if (this.inputData[sp] <= 127) { |
reduced_char = this.inputData[sp]; |
} else { |
reduced_char = this.inputData[sp] - 127; |
} |
/* Step L */ |
if (this.inputData[sp] >= '0' && this.inputData[sp] <= '9') { |
ascii_count += 0.5; |
} else { |
ascii_count = roundUpToNextInteger(ascii_count); |
if (this.inputData[sp] > 127) { |
ascii_count += 2.0; |
} else { |
ascii_count += 1.0; |
} |
} |
/* Step M */ |
done = 0; |
if (reduced_char == ' ') { |
c40_count += 2.0 / 3.0; |
done = 1; |
} |
if (reduced_char >= '0' && reduced_char <= '9') { |
c40_count += 2.0 / 3.0; |
done = 1; |
} |
if (reduced_char >= 'A' && reduced_char <= 'Z') { |
c40_count += 2.0 / 3.0; |
done = 1; |
} |
if (this.inputData[sp] > 127) { |
c40_count += 4.0 / 3.0; |
} |
if (done == 0) { |
c40_count += 4.0 / 3.0; |
} |
/* Step N */ |
done = 0; |
if (reduced_char == ' ') { |
text_count += 2.0 / 3.0; |
done = 1; |
} |
if (reduced_char >= '0' && reduced_char <= '9') { |
text_count += 2.0 / 3.0; |
done = 1; |
} |
if (reduced_char >= 'a' && reduced_char <= 'z') { |
text_count += 2.0 / 3.0; |
done = 1; |
} |
if (this.inputData[sp] > 127) { |
text_count += 4.0 / 3.0; |
} |
if (done == 0) { |
text_count += 4.0 / 3.0; |
} |
/* Step O */ |
done = 0; |
if (this.inputData[sp] == 13) { |
edi_count += 2.0 / 3.0; |
done = 1; |
} |
if (this.inputData[sp] == '*') { |
edi_count += 2.0 / 3.0; |
done = 1; |
} |
if (this.inputData[sp] == '>') { |
edi_count += 2.0 / 3.0; |
done = 1; |
} |
if (this.inputData[sp] == ' ') { |
edi_count += 2.0 / 3.0; |
done = 1; |
} |
if (this.inputData[sp] >= '0' && this.inputData[sp] <= '9') { |
edi_count += 2.0 / 3.0; |
done = 1; |
} |
if (this.inputData[sp] >= 'A' && this.inputData[sp] <= 'Z') { |
edi_count += 2.0 / 3.0; |
done = 1; |
} |
if (this.inputData[sp] > 127) { |
edi_count += 13.0 / 3.0; |
} else { |
if (done == 0) { |
edi_count += 10.0 / 3.0; |
} |
} |
/* Step P */ |
if (this.inputData[sp] == FNC1) { |
byte_count += 3.0; |
} else { |
byte_count += 1.0; |
} |
} |
ascii_count = roundUpToNextInteger(ascii_count); |
c40_count = roundUpToNextInteger(c40_count); |
text_count = roundUpToNextInteger(text_count); |
edi_count = roundUpToNextInteger(edi_count); |
byte_count = roundUpToNextInteger(byte_count); |
best_scheme = Mode.C1_ASCII; |
if (sp == sourcelen) { |
/* Step K */ |
best_count = (int) edi_count; |
if (text_count <= best_count) { |
best_count = (int) text_count; |
best_scheme = Mode.C1_TEXT; |
} |
if (c40_count <= best_count) { |
best_count = (int) c40_count; |
best_scheme = Mode.C1_C40; |
} |
if (ascii_count <= best_count) { |
best_count = (int) ascii_count; |
best_scheme = Mode.C1_ASCII; |
} |
if (byte_count <= best_count) { |
best_scheme = Mode.C1_BYTE; |
} |
} else { |
/* Step Q */ |
if (edi_count + 1.0 <= ascii_count && edi_count + 1.0 <= c40_count && edi_count + 1.0 <= byte_count && edi_count + 1.0 <= text_count) { |
best_scheme = Mode.C1_EDI; |
} |
if (c40_count + 1.0 <= ascii_count && c40_count + 1.0 <= text_count) { |
if (c40_count < edi_count) { |
best_scheme = Mode.C1_C40; |
} else { |
if (c40_count == edi_count) { |
if (preferEdi(sourcelen, position)) { |
best_scheme = Mode.C1_EDI; |
} else { |
best_scheme = Mode.C1_C40; |
} |
} |
} |
} |
if (text_count + 1.0 <= ascii_count && text_count + 1.0 <= c40_count && text_count + 1.0 <= byte_count && text_count + 1.0 <= edi_count) { |
best_scheme = Mode.C1_TEXT; |
} |
if (ascii_count + 1.0 <= byte_count && ascii_count + 1.0 <= c40_count && ascii_count + 1.0 <= text_count && ascii_count + 1.0 <= edi_count) { |
best_scheme = Mode.C1_ASCII; |
} |
if (byte_count + 1.0 <= ascii_count && byte_count + 1.0 <= c40_count && byte_count + 1.0 <= text_count && byte_count + 1.0 <= edi_count) { |
best_scheme = Mode.C1_BYTE; |
} |
} |
return best_scheme; |
} |
private double roundUpToNextInteger(final double input) { |
double fraction, output; |
fraction = input - (int) input; |
if (fraction > 0.01) { |
output = input - fraction + 1.0; |
} else { |
output = input; |
} |
return output; |
} |
private boolean preferEdi(final int sourcelen, final int position) { |
int i; |
for (i = position; isEdiEncodable(this.inputData[position + i]) && position + i < sourcelen; i++) { |
; |
} |
if (position + i == sourcelen) { |
/* Reached end of input */ |
return false; |
} |
if (this.inputData[position + i - 1] == 13) { |
return true; |
} |
if (this.inputData[position + i - 1] == '*') { |
return true; |
} |
if (this.inputData[position + i - 1] == '>') { |
return true; |
} |
return false; |
} |
private boolean isEdiEncodable(final int input) { |
boolean result = false; |
if (input == 13) { |
result = true; |
} |
if (input == '*') { |
result = true; |
} |
if (input == '>') { |
result = true; |
} |
if (input == ' ') { |
result = true; |
} |
if (input >= '0' && input <= '9') { |
result = true; |
} |
if (input >= 'A' && input <= 'Z') { |
result = true; |
} |
return result; |
} |
private void plotCentralFinder(final int start_row, final int row_count, final int full_rows) { |
for (int i = 0; i < row_count; i++) { |
if (i < full_rows) { |
plotHorizontalBar(start_row + i * 2, 1); |
} else { |
plotHorizontalBar(start_row + i * 2, 0); |
if (i != row_count - 1) { |
setGridModule(start_row + i * 2 + 1, 1); |
setGridModule(start_row + i * 2 + 1, this.symbol_width - 2); |
} |
} |
} |
} |
private void plotHorizontalBar(final int row_no, final int full) { |
if (full != 0) { |
for (int i = 0; i < this.symbol_width; i++) { |
setGridModule(row_no, i); |
} |
} else { |
for (int i = 1; i < this.symbol_width - 1; i++) { |
setGridModule(row_no, i); |
} |
} |
} |
private void plotVerticalBar(final int column, final int height, final int top) { |
if (top != 0) { |
for (int i = 0; i < height; i++) { |
setGridModule(i, column); |
} |
} else { |
for (int i = 0; i < height; i++) { |
setGridModule(this.row_count - i - 1, column); |
} |
} |
} |
private void plotSpigot(final int row_no) { |
for (int i = this.symbol_width - 1; i > 0; i--) { |
if (this.outputGrid[row_no][i - 1]) { |
setGridModule(row_no, i); |
} |
} |
} |
private void plotDataBlock(final int start_row, final int start_col, final int height, final int width, final int row_offset, final int col_offset) { |
for (int i = start_row; i < start_row + height; i++) { |
for (int j = start_col; j < start_col + width; j++) { |
if (this.datagrid[i][j] == '1') { |
setGridModule(i + row_offset, j + col_offset); |
} |
} |
} |
} |
private void setGridModule(final int row, final int column) { |
this.outputGrid[row][column] = true; |
} |
private void resetGridModule(final int row, final int column) { |
this.outputGrid[row][column] = false; |
} |
private static int getSize(final Version version) { |
switch (version) { |
case A: |
return 1; |
case B: |
return 2; |
case C: |
return 3; |
case D: |
return 4; |
case E: |
return 5; |
case F: |
return 6; |
case G: |
return 7; |
case H: |
return 8; |
default: |
return 0; |
} |
} |
} |
/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/EanUpcAddOn.java |
---|
New file |
0,0 → 1,112 |
/* |
* 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 EAN/UPC add-on bar code symbology according to BS EN 797:1996. |
* |
* @see Ean |
* @see Upc |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class EanUpcAddOn extends Symbol { |
private static final String[] EAN_SET_A = { "3211", "2221", "2122", "1411", "1132", "1231", "1114", "1312", "1213", "3112" }; |
private static final String[] EAN_SET_B = { "1123", "1222", "2212", "1141", "2311", "1321", "4111", "2131", "3121", "2113" }; |
private static final String[] EAN2_PARITY = { "AA", "AB", "BA", "BB" }; |
private static final String[] EAN5_PARITY = { "BBAAA", "BABAA", "BAABA", "BAAAB", "ABBAA", "AABBA", "AAABB", "ABABA", "ABAAB", "AABAB" }; |
@Override |
protected void encode() { |
if (!this.content.matches("[0-9]+")) { |
throw new OkapiException("Invalid characters in input"); |
} |
if (this.content.length() > 5) { |
throw new OkapiException("Input data too long"); |
} |
final int targetLength = this.content.length() > 2 ? 5 : 2; |
if (this.content.length() < targetLength) { |
for (int i = this.content.length(); i < targetLength; i++) { |
this.content = '0' + this.content; |
} |
} |
final String bars = targetLength == 2 ? ean2(this.content) : ean5(this.content); |
this.readable = this.content; |
this.pattern = new String[] { bars }; |
this.row_count = 1; |
this.row_height = new int[] { -1 }; |
} |
private static String ean2(final String content) { |
final int sum = (content.charAt(0) - '0') * 10 + content.charAt(1) - '0'; |
final String parity = EAN2_PARITY[sum % 4]; |
final StringBuilder sb = new StringBuilder(); |
sb.append("112"); /* Start */ |
for (int i = 0; i < 2; i++) { |
final int val = content.charAt(i) - '0'; |
if (parity.charAt(i) == 'B') { |
sb.append(EAN_SET_B[val]); |
} else { |
sb.append(EAN_SET_A[val]); |
} |
if (i != 1) { /* Glyph separator */ |
sb.append("11"); |
} |
} |
return sb.toString(); |
} |
private static String ean5(final String content) { |
int sum = 0; |
for (int i = 0; i < 5; i++) { |
if (i % 2 == 0) { |
sum += 3 * (content.charAt(i) - '0'); |
} else { |
sum += 9 * (content.charAt(i) - '0'); |
} |
} |
final String parity = EAN5_PARITY[sum % 10]; |
final StringBuilder sb = new StringBuilder(); |
sb.append("112"); /* Start */ |
for (int i = 0; i < 5; i++) { |
final int val = content.charAt(i) - '0'; |
if (parity.charAt(i) == 'B') { |
sb.append(EAN_SET_B[val]); |
} else { |
sb.append(EAN_SET_A[val]); |
} |
if (i != 4) { /* Glyph separator */ |
sb.append("11"); |
} |
} |
return sb.toString(); |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/HumanReadableLocation.java |
---|
New file |
0,0 → 1,30 |
/* |
* Copyright 2015 Daniel Gredler |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
/** |
* The location of a bar code's human-readable text. |
*/ |
public enum HumanReadableLocation { |
/** Display the human-readable text below the bar code. */ |
BOTTOM, |
/** Display the human-readable text above the bar code. */ |
TOP, |
/** Do not display the human-readable text. */ |
NONE |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/ReedSolomon.java |
---|
New file |
0,0 → 1,103 |
/* |
* Copyright 2014 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
/** |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class ReedSolomon { |
private int logmod; |
private int rlen; |
private int[] logt; |
private int[] alog; |
private int[] rspoly; |
public int[] res; |
public int getResult(final int count) { |
return this.res[count]; |
} |
public void init_gf(final int poly) { |
int m, b, p, v; |
// Find the top bit, and hence the symbol size |
for (b = 1, m = 0; b <= poly; b <<= 1) { |
m++; |
} |
b >>= 1; |
m--; |
// Calculate the log/alog tables |
this.logmod = (1 << m) - 1; |
this.logt = new int[this.logmod + 1]; |
this.alog = new int[this.logmod]; |
for (p = 1, v = 0; v < this.logmod; v++) { |
this.alog[v] = p; |
this.logt[p] = v; |
p <<= 1; |
if ((p & b) != 0) { |
p ^= poly; |
} |
} |
} |
public void init_code(final int nsym, int index) { |
int i, k; |
this.rspoly = new int[nsym + 1]; |
this.rlen = nsym; |
this.rspoly[0] = 1; |
for (i = 1; i <= nsym; i++) { |
this.rspoly[i] = 1; |
for (k = i - 1; k > 0; k--) { |
if (this.rspoly[k] != 0) { |
this.rspoly[k] = this.alog[(this.logt[this.rspoly[k]] + index) % this.logmod]; |
} |
this.rspoly[k] ^= this.rspoly[k - 1]; |
} |
this.rspoly[0] = this.alog[(this.logt[this.rspoly[0]] + index) % this.logmod]; |
index++; |
} |
} |
public void encode(final int len, final int[] data) { |
int i, k, m; |
this.res = new int[this.rlen]; |
for (i = 0; i < this.rlen; i++) { |
this.res[i] = 0; |
} |
for (i = 0; i < len; i++) { |
m = this.res[this.rlen - 1] ^ data[i]; |
for (k = this.rlen - 1; k > 0; k--) { |
if (m != 0 && this.rspoly[k] != 0) { |
this.res[k] = this.res[k - 1] ^ this.alog[(this.logt[m] + this.logt[this.rspoly[k]]) % this.logmod]; |
} else { |
this.res[k] = this.res[k - 1]; |
} |
} |
if (m != 0 && this.rspoly[0] != 0) { |
this.res[0] = this.alog[(this.logt[m] + this.logt[this.rspoly[0]]) % this.logmod]; |
} else { |
this.res[0] = 0; |
} |
} |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/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/KixCode.java |
---|
New file |
0,0 → 1,107 |
/* |
* Copyright 2014 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
import static uk.org.okapibarcode.util.Arrays.positionOf; |
import java.awt.geom.Rectangle2D; |
import java.util.Locale; |
/** |
* <p> |
* Implements Dutch Post KIX Code as used by Royal Dutch TPG Post (Netherlands). |
* |
* <p> |
* The input data can consist of digits 0-9 and characters A-Z, and should be 11 characters in |
* length. No check digit is added. |
* |
* <p> |
* KIX Code is the same as RM4SCC, but without the check digit. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
* @see <a href="http://www.tntpost.nl/zakelijk/klantenservice/downloads/kIX_code/download.aspx">KIX |
* Code Specification</a> |
*/ |
public class KixCode extends Symbol { |
private static final String[] ROYAL_TABLE = { "TTFF", "TDAF", "TDFA", "DTAF", "DTFA", "DDAA", "TADF", "TFTF", "TFDA", "DATF", "DADA", "DFTA", "TAFD", "TFAD", "TFFT", "DAAD", "DAFT", "DFAT", |
"ATDF", "ADTF", "ADDA", "FTTF", "FTDA", "FDTA", "ATFD", "ADAD", "ADFT", "FTAD", "FTFT", "FDAT", "AADD", "AFTD", "AFDT", "FATD", "FADT", "FFTT" }; |
private static final char[] KR_SET = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', |
'V', 'W', 'X', 'Y', 'Z' }; |
@Override |
protected void encode() { |
this.content = this.content.toUpperCase(Locale.ENGLISH); |
if (!this.content.matches("[0-9A-Z]+")) { |
throw new OkapiException("Invalid characters in data"); |
} |
final StringBuilder sb = new StringBuilder(this.content.length()); |
for (int i = 0; i < this.content.length(); i++) { |
final int j = positionOf(this.content.charAt(i), KR_SET); |
sb.append(ROYAL_TABLE[j]); |
} |
final String dest = sb.toString(); |
infoLine("Encoding: " + dest); |
this.readable = ""; |
this.pattern = new String[] { dest }; |
this.row_count = 1; |
this.row_height = new int[] { -1 }; |
} |
@Override |
protected void plotSymbol() { |
int xBlock; |
int x, y, w, h; |
this.rectangles.clear(); |
x = 0; |
w = 1; |
y = 0; |
h = 0; |
for (xBlock = 0; xBlock < this.pattern[0].length(); xBlock++) { |
final char c = this.pattern[0].charAt(xBlock); |
switch (c) { |
case 'A': |
y = 0; |
h = 5; |
break; |
case 'D': |
y = 3; |
h = 5; |
break; |
case 'F': |
y = 0; |
h = 8; |
break; |
case 'T': |
y = 3; |
h = 2; |
break; |
default: |
throw new IllegalStateException("Unknown pattern character: " + c); |
} |
this.rectangles.add(new Rectangle2D.Double(x, y, w, h)); |
x += 2; |
} |
this.symbol_width = (this.pattern[0].length() - 1) * 2 + 1; // final bar doesn't need extra |
// whitespace |
this.symbol_height = 8; |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Code49.java |
---|
New file |
0,0 → 1,850 |
/* |
* Copyright 2014 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
import static uk.org.okapibarcode.util.Arrays.positionOf; |
import java.awt.geom.Rectangle2D; |
import java.nio.charset.StandardCharsets; |
/** |
* <p> |
* Implements Code 49 according to ANSI/AIM-BC6-2000. |
* |
* <p> |
* Supports full 7-bit ASCII input up to a maximum of 49 characters or 81 numeric digits. GS1 data |
* encoding is also supported. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class Code49 extends Symbol { |
private static final String[] C49_TABLE7 = { |
/* Table 7: Code 49 ASCII Chart */ |
"! ", "!A", "!B", "!C", "!D", "!E", "!F", "!G", "!H", "!I", "!J", "!K", "!L", "!M", "!N", "!O", "!P", "!Q", "!R", "!S", "!T", "!U", "!V", "!W", "!X", "!Y", "!Z", "!1", "!2", "!3", "!4", |
"!5", " ", "!6", "!7", "!8", "$", "%", "!9", "!0", "!-", "!.", "!$", "+", "!/", "-", ".", "/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "!+", "&1", "&2", "&3", "&4", "&5", "&6", |
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "&7", "&8", "&9", "&0", "&-", "&.", "&A", "&B", "&C", |
"&D", "&E", "&F", "&G", "&H", "&I", "&J", "&K", "&L", "&M", "&N", "&O", "&P", "&Q", "&R", "&S", "&T", "&U", "&V", "&W", "&X", "&Y", "&Z", "&$", "&/", "&+", "&%", "& " }; |
/* Table 5: Check Character Weighting Values */ |
private static final int[] C49_X_WEIGHT = { 1, 9, 31, 26, 2, 12, 17, 23, 37, 18, 22, 6, 27, 44, 15, 43, 39, 11, 13, 5, 41, 33, 36, 8, 4, 32, 3, 19, 40, 25, 29, 10 }; |
private static final int[] C49_Y_WEIGHT = { 9, 31, 26, 2, 12, 17, 23, 37, 18, 22, 6, 27, 44, 15, 43, 39, 11, 13, 5, 41, 33, 36, 8, 4, 32, 3, 19, 40, 25, 29, 10, 24 }; |
private static final int[] C49_Z_WEIGHT = { 31, 26, 2, 12, 17, 23, 37, 18, 22, 6, 27, 44, 15, 43, 39, 11, 13, 5, 41, 33, 36, 8, 4, 32, 3, 19, 40, 25, 29, 10, 24, 30 }; |
private static final String[] C49_TABLE4 = { |
/* Table 4: Row Parity Pattern for Code 49 Symbols */ |
"OEEO", "EOEO", "OOEE", "EEOO", "OEOE", "EOOE", "OOOO", "EEEE" }; |
private static final String[] C49_APPXE_EVEN = { |
/* Appendix E - Code 49 Encodation Patterns (Even Symbol Character Parity) */ |
/* Column 1 */ |
"11521132", "25112131", "14212132", "25121221", "14221222", "12412132", "23321221", "12421222", "21521221", "15112222", "15121312", "13312222", "24221311", "13321312", "11512222", |
"22421311", "11521312", "25112311", "14212312", "23312311", "12412312", "21512311", "16121131", "14321131", "12521131", "15212131", "15221221", "13412131", "13421221", "11612131", |
"16112221", "16121311", "14312221", "14321311", "12512221", "12521311", "15212311", "13412311", "11612311", "11131135", "31131133", "51131131", "21122134", "41122132", "21131224", |
"41131222", "11113135", "31113133", "51113131", "11122225", "31122223", "51122221", "11131315", "31131313", "51131311", "21113224", "41113222", "21122314", |
/* Column 2 */ |
"41122312", "11113315", "31113313", "51113311", "12131134", "32131132", "21231133", "41231131", "22122133", "42122131", "11222134", "22131223", "42131221", "11231224", "31231222", |
"12113134", "32113132", "12122224", "32122222", "12131314", "32131312", "21231313", "41231311", "22113223", "42113221", "11213224", "22122313", "42122311", "11222314", "31222312", |
"12113314", "32113312", "21213313", "41213311", "13131133", "33131131", "22231132", "11331133", "31331131", "23122132", "12222133", "23131222", "12231223", "32231221", "21331222", |
"13113133", "33113131", "13122223", "33122221", "11313133", "13131313", "33131311", "11322223", "22231312", "11331313", "31331311", "23113222", "12213223", |
/* Column 3 */ |
"23122312", "12222313", "32222311", "21322312", "13113313", "33113311", "22213312", "11313313", "31313311", "14131132", "23231131", "12331132", "21431131", "24122131", "13222132", |
"24131221", "13231222", "11422132", "22331221", "11431222", "14113132", "14122222", "12313132", "14131312", "12322222", "23231311", "12331312", "21431311", "24113221", "13213222", |
"24122311", "13222312", "11413222", "22322311", "11422312", "14113312", "23213311", "12313312", "21413311", "15131131", "13331131", "14222131", "14231221", "12422131", "12431221", |
"15113131", "15122221", "13313131", "15131311", "13322221", "11513131", "13331311", "11522221", "14213221", "14222311", "12413221", "12422311", "15113311", |
/* Column 4 */ |
"13313311", "11513311", "11141134", "31141132", "21132133", "41132131", "21141223", "41141221", "11123134", "31123132", "11132224", "31132222", "11141314", "31141312", "21114133", |
"41114131", "21123223", "41123221", "21132313", "41132311", "11114224", "31114222", "11123314", "31123312", "21114313", "41114311", "12141133", "32141131", "21241132", "22132132", |
"11232133", "22141222", "11241223", "31241221", "12123133", "32123131", "12132223", "32132221", "12141313", "32141311", "21241312", "22114132", "11214133", "22123222", "11223223", |
"22132312", "11232313", "31232311", "12114223", "32114221", "12123313", "32123311", "21223312", "22114312", "11214313", "31214311", "13141132", "22241131", |
/* Column 5 */ |
"11341132", "23132131", "12232132", "23141221", "12241222", "21341221", "13123132", "13132222", "11323132", "13141312", "11332222", "22241311", "11341312", "23114131", "12214132", |
"23123221", "12223222", "23132311", "12232312", "21332311", "13114222", "13123312", "11314222", "22223311", "11323312", "23114311", "12214312", "21314311", "14141131", "12341131", |
"13232131", "13241221", "11432131", "14123131", "14132221", "12323131", "14141311", "12332221", "12341311", "13214131", "13223221", "11414131", "13232311", "11423221", "11432311", |
"14114221", "14123311", "12314221", "12323311", "13214311", "11414311", "11151133", "31151131", "21142132", "21151222", "11133133", "31133131", "11142223", |
/* Column 6 */ |
"31142221", "11151313", "31151311", "21124132", "21133222", "21142312", "11115133", "31115131", "11124223", "31124221", "11133313", "31133311", "21115222", "21124312", "12151132", |
"21251131", "22142131", "11242132", "22151221", "11251222", "12133132", "12142222", "12151312", "21251311", "22124131", "11224132", "22133221", "11233222", "22142311", "11242312", |
"12115132", "12124222", "12133312", "21233311", "22115221", "11215222", "22124311", "11224312", "13151131", "12242131", "12251221", "13133131", "13142221", "11333131", "13151311", |
"11342221", "12224131", "12233221", "12242311", "13115131", "13124221", "11315131", "13133311", "11324221", "11333311", "12215221", "12224311", "11161132", |
/* Column 7 */ |
"21152131", "21161221", "11143132", "11152222", "11161312", "21134131", "21143221", "21152311", "11125132", "11134222", "11143312", "21116131", "21125221", "21134311", "12161131", |
"11252131", "12143131", "12152221", "12161311", "11234131", "11243221", "11252311", "12125131", "12134221", "12143311", "11216131", "11225221", "11234311", "11111236", "31111234", |
"51111232", "21111325", "41111323", "61111321", "11111416", "31111414", "51111412", "31211143", "51211141", "12111235", "32111233", "52111231", "21211234", "41211232", "22111324", |
"42111322", "11211325", "31211323", "51211321", "12111415", "32111413", "52111411", "21211414", "41211412", "12211144", "32211142", "21311143", "41311141", |
/* Column 8 */ |
"13111234", "33111232", "22211233", "42211231", "11311234", "31311232", "23111323", "43111321", "12211324", "32211322", "21311323", "41311321", "13111414", "33111412", "22211413", |
"42211411", "11311414", "31311412", "13211143", "33211141", "22311142", "11411143", "31411141", "14111233", "34111231", "23211232", "12311233", "32311231", "21411232", "24111322", |
"13211323", "33211321", "22311322", "11411323", "31411321", "14111413", "34111411", "23211412", "12311413", "32311411", "21411412", "14211142", "23311141", "12411142", "21511141", |
"15111232", "24211231", "13311232", "22411231", "11511232", "25111321", "14211322", "23311321", "12411322", "21511321", "15111412", "24211411", "13311412", |
/* Column 9 */ |
"22411411", "11511412", "15211141", "13411141", "11611141", "16111231", "14311231", "12511231", "15211321", "13411321", "11611321", "16111411", "14311411", "12511411", "21121144", |
"41121142", "11112145", "31112143", "51112141", "11121235", "31121233", "51121231", "21112234", "41112232", "21121324", "41121322", "11112325", "31112323", "51112321", "11121415", |
"31121413", "51121411", "21112414", "41112412", "22121143", "42121141", "11221144", "31221142", "12112144", "32112142", "12121234", "32121232", "21221233", "41221231", "22112233", |
"42112231", "11212234", "22121323", "42121321", "11221324", "31221322", "12112324", "32112322", "12121414", "32121412", "21221413", "41221411", "22112413", |
/* Column 10 */ |
"42112411", "11212414", "31212412", "23121142", "12221143", "32221141", "21321142", "13112143", "33112141", "13121233", "33121231", "11312143", "22221232", "11321233", "31321231", |
"23112232", "12212233", "23121322", "12221323", "32221321", "21321322", "13112323", "33112321", "13121413", "33121411", "11312323", "22221412", "11321413", "31321411", "23112412", |
"12212413", "32212411", "21312412", "24121141", "13221142", "22321141", "11421142", "14112142", "14121232", "12312142", "23221231", "12321232", "21421231", "24112231", "13212232", |
"24121321", "13221322", "11412232", "22321321", "11421322", "14112322", "14121412", "12312322", "23221411", "12321412", "21421411", "24112411", "13212412", |
/* Column 11 */ |
"22312411", "11412412", "14221141", "12421141", "15112141", "15121231", "13312141", "13321231", "11512141", "11521231", "14212231", "14221321", "12412231", "12421321", "15112321", |
"15121411", "13312321", "13321411", "11512321", "11521411", "14212411", "12412411", "21131143", "41131141", "11122144", "31122142", "11131234", "31131232", "21113143", "41113141", |
"21122233", "41122231", "21131323", "41131321", "11113234", "31113232", "11122324", "31122322", "11131414", "31131412", "21113323", "41113321", "21122413", "41122411", "11113414", |
"31113412", "22131142", "11231143", "31231141", "12122143", "32122141", "12131233", "32131231", "21231232", "22113142", "11213143", "22122232", "11222233", |
/* Column 12 */ |
"22131322", "11231323", "31231321", "12113233", "32113231", "12122323", "32122321", "12131413", "32131411", "21231412", "22113322", "11213323", "22122412", "11222413", "31222411", |
"12113413", "32113411", "21213412", "23131141", "12231142", "21331141", "13122142", "13131232", "11322142", "22231231", "11331232", "23113141", "12213142", "23122231", "12222232", |
"23131321", "12231322", "21331321", "13113232", "13122322", "11313232", "13131412", "11322322", "22231411", "11331412", "23113321", "12213322", "23122411", "12222412", "21322411", |
"13113412", "22213411", "11313412", "13231141", "11431141", "14122141", "14131231", "12322141", "12331231", "13213141", "13222231", "11413141", "13231321", |
/* Column 13 */ |
"11422231", "11431321", "14113231", "14122321", "12313231", "14131411", "12322321", "12331411", "13213321", "13222411", "11413321", "11422411", "14113411", "12313411", "21141142", |
"11132143", "31132141", "11141233", "31141231", "21123142", "21132232", "21141322", "11114143", "31114141", "11123233", "31123231", "11132323", "31132321", "11141413", "31141411", |
"21114232", "21123322", "21132412", "11114323", "31114321", "11123413", "31123411", "22141141", "11241142", "12132142", "12141232", "21241231", "22123141", "11223142", "22132231", |
"11232232", "22141321", "11241322", "12114142", "12123232", "12132322", "12141412", "21241411", "22114231", "11214232", "22123321", "11223322", "22132411", |
/* Column 14 */ |
"11232412", "12114322", "12123412", "21223411", "12241141", "13132141", "13141231", "11332141", "11341231", "12223141", "12232231", "12241321", "13114141", "13123231", "11314141", |
"13132321", "11323231", "13141411", "11332321", "11341411", "12214231", "12223321", "12232411", "13114321", "13123411", "11314321", "11323411", "21151141", "11142142", "11151232", |
"21133141", "21142231", "21151321", "11124142", "11133232", "11142322", "11151412", "21115141", "21124231", "21133321", "21142411", "11115232", "11124322", "11133412", "11251141", |
"12142141", "12151231", "11233141", "11242231", "11251321", "12124141", "12133231", "12142321", "12151411", "11215141", "11224231", "11233321", "11242411", |
/* Column 15 */ |
"12115231", "12124321", "12133411", "11152141", "11161231", "11134141", "11143231", "11152321", "11161411", "11116141", "11125231", "11134321", "11143411", "21111244", "41111242", |
"11111335", "31111333", "51111331", "21111424", "41111422", "11111515", "31111513", "51111511", "21211153", "41211151", "22111243", "42111241", "11211244", "31211242", "12111334", |
"32111332", "21211333", "41211331", "22111423", "42111421", "11211424", "31211422", "12111514", "32111512", "21211513", "41211511", "22211152", "11311153", "31311151", "23111242", |
"12211243", "32211241", "21311242", "13111333", "33111331", "22211332", "11311333", "31311331", "23111422", "12211423", "32211421", "21311422", "13111513", |
/* Column 16 */ |
"33111511", "22211512", "11311513", "31311511", "23211151", "12311152", "21411151", "24111241", "13211242", "22311241", "11411242", "14111332", "23211331", "12311332", "21411331", |
"24111421", "13211422", "22311421", "11411422", "14111512", "23211511", "12311512", "21411511", "13311151", "11511151", "14211241", "12411241", "15111331", "13311331", "11511331", |
"14211421", "12411421", "15111511", "13311511", "11511511", "31121152", "21112153", "41112151", "21121243", "41121241", "11112244", "31112242", "11121334", "31121332", "21112333", |
"41112331", "21121423", "41121421", "11112424", "31112422", "11121514", "31121512", "21112513", "41112511", "12121153", "32121151", "21221152", "22112152", |
/* Column 17 */ |
"11212153", "22121242", "11221243", "31221241", "12112243", "32112241", "12121333", "32121331", "21221332", "22112332", "11212333", "22121422", "11221423", "31221421", "12112423", |
"32112421", "12121513", "32121511", "21221512", "22112512", "11212513", "31212511", "13121152", "22221151", "11321152", "23112151", "12212152", "23121241", "12221242", "21321241", |
"13112242", "13121332", "11312242", "22221331", "11321332", "23112331", "12212332", "23121421", "12221422", "21321421", "13112422", "13121512", "11312422", "22221511", "11321512", |
"23112511", "12212512", "21312511", "14121151", "12321151", "13212151", "13221241", "11412151", "11421241", "14112241", "14121331", "12312241", "12321331", |
/* Column 18 */ |
"13212331", "13221421", "11412331", "11421421", "14112421", "14121511", "12312421", "12321511", "13212511", "11412511", "11131153", "31131151", "21122152", "21131242", "11113153", |
"31113151", "11122243", "31122241", "11131333", "31131331", "21113242", "21122332", "21131422", "11113333", "31113331", "11122423", "31122421", "11131513", "31131511", "21113422", |
"21122512", "12131152", "21231151", "22122151", "11222152", "22131241", "11231242", "12113152", "12122242", "12131332", "21231331", "22113241", "11213242", "22122331", "11222332", |
"22131421", "11231422", "12113332", "12122422", "12131512", "21231511", "22113421", "11213422", "22122511", "11222512", "13131151", "11331151", "12222151", |
/* Column 19 */ |
"12231241", "13113151", "13122241", "11313151", "13131331", "11322241", "11331331", "12213241", "12222331", "12231421", "13113331", "13122421", "11313331", "13131511", "11322421", |
"11331511", "12213421", "12222511", "11141152", "21132151", "21141241", "11123152", "11132242", "11141332", "21114151", "21123241", "21132331", "21141421", "11114242", "11123332", |
"11132422", "11141512", "21114331", "21123421", "21132511", "12141151", "11232151", "11241241", "12123151", "12132241", "12141331", "11214151", "11223241", "11232331", "11241421", |
"12114241", "12123331", "12132421", "12141511", "11214331", "11223421", "11232511", "11151151", "11133151", "11142241", "11151331", "11115151", "11124241", |
/* Column 20 */ |
"11133331", "11142421", "11151511", "11111254", "31111252", "21111343", "41111341", "11111434", "31111432", "21111523", "41111521", "11111614", "31111612", "31211161", "12111253", |
"32111251", "21211252", "22111342", "11211343", "31211341", "12111433", "32111431", "21211432", "22111522", "11211523", "31211521", "12111613", "32111611", "21211612", "12211162", |
"21311161", "13111252", "22211251", "11311252", "23111341", "12211342", "21311341", "13111432", "22211431", "11311432", "23111521", "12211522", "21311521", "13111612", "22211611", |
"11311612", "13211161", "11411161", "14111251", "12311251", "13211341", "11411341", "14111431", "12311431", "13211521", "11411521", "14111611", "12311611", |
/* Column 21 */ |
"21121162", "11112163", "31112161", "11121253", "31121251", "21112252", "21121342", "11112343", "31112341", "11121433", "31121431", "21112432", "21121522", "11112523", "31112521", |
"11121613", "31121611", "22121161", "11221162", "12112162", "12121252", "21221251", "22112251", "11212252", "22121341", "11221342", "12112342", "12121432", "21221431", "22112431", |
"11212432", "22121521", "11221522", "12112522", "12121612", "21221611", "12221161", "13112161", "13121251", "11312161", "11321251", "32121115", "52121113", "21221116", "41221114", |
"61221112", "22112116", "42112114", "31212115", "51212113", "13121116", "33121114", "22221115", "42221113", "11321116", "31321114", "51321112", "23112115", |
/* Column 22 */ |
"43112113", "12212116", "32212114", "52212112", "21312115", "41312113", "61312111", "14121115", "34121113", "23221114", "43221112", "12321115", "32321113", "52321111", "21421114", |
"41421112", "24112114", "13212115", "33212113", "22312114", "42312112", "11412115", "31412113", "51412111", "15121114", "24221113", "13321114", "33321112", "22421113", "42421111", |
"11521114", "31521112", "25112113", "14212114", "34212112", "23312113", "43312111", "12412114", "32412112", "21512113", "41512111", "16121113", "25221112", "14321113", "34321111", |
"23421112", "12521113", "32521111", "15212113", "24312112", "13412113", "33412111", "22512112", "11612113", "31612111", "31131115", "51131113", "21122116", |
/* Column 23 */ |
"41122114", "61122112", "31113115", "51113113", "12131116", "32131114", "52131112", "21231115", "41231113", "61231111", "22122115", "42122113", "11222116", "31222114", "51222112", |
"12113116", "32113114", "52113112", "21213115", "41213113", "61213111", "13131115", "33131113", "22231114", "42231112", "11331115", "31331113", "51331111", "23122114", "43122112", |
"12222115", "32222113", "52222111", "21322114", "41322112", "13113115", "33113113", "22213114", "42213112", "11313115", "31313113", "51313111", "14131114", "34131112", "23231113", |
"43231111", "12331114", "32331112", "21431113", "41431111", "24122113", "13222114", "33222112", "22322113", "42322111", "11422114", "31422112", "14113114", |
/* Column 24 */ |
"34113112", "23213113", "43213111", "12313114", "32313112", "21413113", "41413111", "15131113", "24231112", "13331113", "33331111", "22431112", "25122112", "14222113", "34222111", |
"23322112", "12422113", "32422111", "21522112", "15113113", "24213112", "13313113", "33313111", "22413112", "11513113", "31513111", "16131112", "25231111", "14331112", "23431111", |
"15222112", "24322111", "13422112", "22522111", "16113112", "25213111", "14313112", "23413111", "12513112", "21613111", "11141116", "31141114", "51141112", "21132115", "41132113", |
"61132111", "11123116", "31123114", "51123112", "21114115", "41114113", "61114111", "12141115", "32141113", "52141111", "21241114", "41241112", "22132114", |
/* Column 25 */ |
"42132112", "11232115", "31232113", "51232111", "12123115", "32123113", "52123111", "21223114", "41223112", "22114114", "42114112", "11214115", "31214113", "51214111", "13141114", |
"33141112", "22241113", "42241111", "11341114", "31341112", "23132113", "43132111", "12232114", "32232112", "21332113", "41332111", "13123114", "33123112", "22223113", "42223111", |
"11323114", "31323112", "23114113", "43114111", "12214114", "32214112", "21314113", "41314111", "14141113", "34141111", "23241112", "12341113", "32341111", "24132112", "13232113", |
"33232111", "22332112", "11432113", "31432111", "14123113", "34123111", "23223112", "12323113", "32323111", "21423112", "24114112", "13214113", "33214111", |
/* Column 26 */ |
"22314112", "11414113", "31414111", "15141112", "24241111", "13341112", "25132111", "14232112", "23332111", "12432112", "15123112", "24223111", "13323112", "22423111", "11523112", |
"25114111", "14214112", "23314111", "12414112", "21514111", "16141111", "14341111", "15232111", "13432111", "16123111", "14323111", "12523111", "15214111", "13414111", "11614111", |
"11151115", "31151113", "51151111", "21142114", "41142112", "11133115", "31133113", "51133111", "21124114", "41124112", "11115115", "31115113", "51115111", "12151114", "32151112", |
"21251113", "41251111", "22142113", "42142111", "11242114", "31242112", "12133114", "32133112", "21233113", "41233111", "22124113", "42124111", "11224114", |
/* Column 27 */ |
"31224112", "12115114", "32115112", "21215113", "41215111", "13151113", "33151111", "22251112", "23142112", "12242113", "32242111", "21342112", "13133113", "33133111", "22233112", |
"11333113", "31333111", "23124112", "12224113", "32224111", "21324112", "13115113", "33115111", "22215112", "11315113", "31315111", "14151112", "23251111", "24142111", "13242112", |
"22342111", "14133112", "23233111", "12333112", "21433111", "24124111", "13224112", "22324111", "11424112", "14115112", "23215111", "12315112", "21415111", "15151111", "14242111", |
"15133111", "13333111", "14224111", "12424111", "15115111", "13315111", "11515111", "11161114", "31161112", "21152113", "41152111", "11143114", "31143112", |
/* Column 28 */ |
"21134113", "41134111", "11125114", "31125112", "21116113", "41116111", "12161113", "32161111", "22152112", "11252113", "31252111", "12143113", "32143111", "21243112", "22134112", |
"11234113", "31234111", "12125113", "32125111", "21225112", "22116112", "11216113", "31216111", "13161112", "23152111", "12252112", "13143112", "22243111", "11343112", "23134111", |
"12234112", "21334111", "13125112", "22225111", "11325112", "23116111", "12216112", "21316111", "14161111", "13252111", "14143111", "12343111", "13234111", "11434111", "14125111", |
"12325111", "13216111", "11416111", "31111216", "51111214", "31211125", "51211123", "32111215", "52111213", "21211216", "41211214", "61211212", "12211126", |
/* Column 29 */ |
"32211124", "52211122", "21311125", "41311123", "61311121", "13111216", "33111214", "22211215", "42211213", "11311216", "31311214", "51311212", "13211125", "33211123", "22311124", |
"42311122", "11411125", "31411123", "51411121", "14111215", "34111213", "23211214", "43211212", "12311215", "32311213", "52311211", "21411214", "41411212", "14211124", "34211122", |
"23311123", "43311121", "12411124", "32411122", "21511123", "41511121", "15111214", "24211213", "13311214", "33311212", "22411213", "42411211", "11511214", "31511212", "15211123", |
"24311122", "13411123", "33411121", "22511122", "11611123", "31611121", "16111213", "25211212", "14311213", "34311211", "23411212", "12511213", "32511211", |
/* Column 30 */ |
"21611212", "21121126", "41121124", "61121122", "31112125", "51112123", "31121215", "51121213", "21112216", "41112214", "61112212", "22121125", "42121123", "11221126", "31221124", |
"51221122", "12112126", "32112124", "52112122", "12121216", "32121214", "52121212", "21221215", "41221213", "61221211", "22112215", "42112213", "11212216", "31212214", "51212212", |
"23121124", "43121122", "12221125", "32221123", "52221121", "21321124", "41321122", "13112125", "33112123", "13121215", "33121213", "11312125", "22221214", "42221212", "11321215", |
"31321213", "51321211", "23112214", "43112212", "12212215", "32212213", "52212211", "21312214", "41312212", "24121123", "13221124", "33221122", "22321123", |
/* Column 31 */ |
"42321121", "11421124", "31421122", "14112124", "34112122", "14121214", "34121212", "12312124", "23221213", "43221211", "12321214", "32321212", "21421213", "41421211", "24112213", |
"13212214", "33212212", "22312213", "42312211", "11412214", "31412212", "25121122", "14221123", "34221121", "23321122", "12421123", "32421121", "21521122", "15112123", "15121213", |
"13312123", "24221212", "13321213", "33321211", "11512123", "22421212", "11521213", "31521211", "25112212", "14212213", "34212211", "23312212", "12412213", "32412211", "21512212", |
"15221122", "24321121", "13421122", "22521121", "16112122", "16121212", "14312122", "25221211", "14321212", "12512122", "23421211", "12521212", "15212212", |
/* Column 32 */ |
"24312211", "13412212", "22512211", "11612212", "21131125", "41131123", "61131121", "11122126", "31122124", "51122122", "11131216", "31131214", "51131212", "21113125", "41113123", |
"61113121", "21122215", "41122213", "61122211", "11113216", "31113214", "51113212", "22131124", "42131122", "11231125", "31231123", "51231121", "12122125", "32122123", "52122121", |
"12131215", "32131213", "52131211", "21231214", "41231212", "22113124", "42113122", "11213125", "22122214", "42122212", "11222215", "31222213", "51222211", "12113215", "32113213", |
"52113211", "21213214", "41213212", "23131123", "43131121", "12231124", "32231122", "21331123", "41331121", "13122124", "33122122", "13131214", "33131212", |
/* Column 33 */ |
"11322124", "22231213", "42231211", "11331214", "31331212", "23113123", "43113121", "12213124", "23122213", "43122211", "12222214", "32222212", "21322213", "41322211", "13113214", |
"33113212", "22213213", "42213211", "11313214", "31313212", "24131122", "13231123", "33231121", "22331122", "11431123", "31431121", "14122123", "34122121", "14131213", "34131211", |
"12322123", "23231212", "12331213", "32331211", "21431212", "24113122", "13213123", "24122212", "13222213", "33222211", "11413123", "22322212", "11422213", "31422211", "14113213", |
"34113211", "23213212", "12313213", "32313211", "21413212", "25131121", "14231122", "23331121", "12431122", "15122122", "15131212", "13322122", "24231211", |
/* Column 34 */ |
"13331212", "11522122", "22431211", "25113121", "14213122", "25122211", "14222212", "12413122", "23322211", "12422212", "21522211", "15113212", "24213211", "13313212", "22413211", |
"11513212", "15231121", "13431121", "16122121", "16131211", "14322121", "14331211", "12522121", "15213121", "15222211", "13413121", "13422211", "11613121", "16113211", "14313211", |
"12513211", "21141124", "41141122", "11132125", "31132123", "51132121", "11141215", "31141213", "51141211", "21123124", "41123122", "21132214", "41132212", "11114125", "31114123", |
"51114121", "11123215", "31123213", "51123211", "21114214", "41114212", "22141123", "42141121", "11241124", "31241122", "12132124", "32132122", "12141214", |
/* Column 35 */ |
"32141212", "21241213", "41241211", "22123123", "42123121", "11223124", "22132213", "42132211", "11232214", "31232212", "12114124", "32114122", "12123214", "32123212", "21223213", |
"41223211", "22114213", "42114211", "11214214", "31214212", "23141122", "12241123", "32241121", "21341122", "13132123", "33132121", "13141213", "33141211", "11332123", "22241212", |
"11341213", "31341211", "23123122", "12223123", "23132212", "12232213", "32232211", "21332212", "13114123", "33114121", "13123213", "33123211", "11314123", "22223212", "11323213", |
"31323211", "23114212", "12214213", "32214211", "21314212", "24141121", "13241122", "22341121", "14132122", "14141212", "12332122", "23241211", "12341212", |
/* Column 36 */ |
"24123121", "13223122", "24132211", "13232212", "11423122", "22332211", "11432212", "14114122", "14123212", "12314122", "23223211", "12323212", "21423211", "24114211", "13214212", |
"22314211", "11414212", "14241121", "15132121", "15141211", "13332121", "13341211", "14223121", "14232211", "12423121", "12432211", "15114121", "15123211", "13314121", "13323211", |
"11514121", "11523211", "14214211", "12414211", "21151123", "41151121", "11142124", "31142122", "11151214", "31151212", "21133123", "41133121", "21142213", "41142211", "11124124", |
"31124122", "11133214", "31133212", "21115123", "41115121", "21124213", "41124211", "11115214", "31115212", "22151122", "11251123", "31251121", "12142123", |
/* Column 37 */ |
"32142121", "12151213", "32151211", "21251212", "22133122", "11233123", "22142212", "11242213", "31242211", "12124123", "32124121", "12133213", "32133211", "21233212", "22115122", |
"11215123", "22124212", "11224213", "31224211", "12115213", "32115211", "21215212", "23151121", "12251122", "13142122", "13151212", "11342122", "22251211", "23133121", "12233122", |
"23142211", "12242212", "21342211", "13124122", "13133212", "11324122", "22233211", "11333212", "23115121", "12215122", "23124211", "12224212", "21324211", "13115212", "22215211", |
"11315212", "13251121", "14142121", "14151211", "12342121", "13233121", "13242211", "11433121", "14124121", "14133211", "12324121", "12333211", "13215121", |
/* Column 38 */ |
"13224211", "11415121", "11424211", "14115211", "12315211", "21161122", "11152123", "31152121", "11161213", "31161211", "21143122", "21152212", "11134123", "31134121", "11143213", |
"31143211", "21125122", "21134212", "11116123", "31116121", "11125213", "31125211", "22161121", "12152122", "12161212", "22143121", "11243122", "22152211", "11252212", "12134122", |
"12143212", "21243211", "22125121", "11225122", "22134211", "11234212", "12116122", "12125212", "21225211", "13152121", "13161211", "12243121", "12252211", "13134121", "13143211", |
"11334121", "11343211", "12225121", "12234211", "13116121", "13125211", "11316121", "11325211", "21111226", "41111224", "61111222", "31111315", "51111313", |
/* Column 39 */ |
"21211135", "41211133", "61211131", "22111225", "42111223", "11211226", "31211224", "51211222", "12111316", "32111314", "52111312", "21211315", "41211313", "61211311", "22211134", |
"42211132", "11311135", "31311133", "51311131", "23111224", "43111222", "12211225", "32211223", "52211221", "21311224", "41311222", "13111315", "33111313", "22211314", "42211312", |
"11311315", "31311313", "51311311", "23211133", "43211131", "12311134", "32311132", "21411133", "41411131", "24111223", "13211224", "33211222", "22311223", "42311221", "11411224", |
"31411222", "14111314", "34111312", "23211313", "43211311", "12311314", "32311312", "21411313", "41411311", "24211132", "13311133", "33311131", "22411132", |
/* Column 40 */ |
"11511133", "31511131", "25111222", "14211223", "34211221", "23311222", "12411223", "32411221", "21511222", "15111313", "24211312", "13311313", "33311311", "22411312", "11511313", |
"31511311", "25211131", "14311132", "23411131", "12511132", "21611131", "15211222", "24311221", "13411222", "22511221", "11611222", "16111312", "25211311", "14311312", "23411311", |
"12511312", "21611311", "31121134", "51121132", "21112135", "41112133", "61112131", "21121225", "41121223", "61121221", "11112226", "31112224", "51112222", "11121316", "31121314", |
"51121312", "21112315", "41112313", "61112311", "12121135", "32121133", "52121131", "21221134", "41221132", "22112134", "42112132", "11212135", "22121224", |
/* Column 41 */ |
"42121222", "11221225", "31221223", "51221221", "12112225", "32112223", "52112221", "12121315", "32121313", "52121311", "21221314", "41221312", "22112314", "42112312", "11212315", |
"31212313", "51212311", "13121134", "33121132", "22221133", "42221131", "11321134", "31321132", "23112133", "43112131", "12212134", "23121223", "43121221", "12221224", "32221222", |
"21321223", "41321221", "13112224", "33112222", "13121314", "33121312", "11312224", "22221313", "42221311", "11321314", "31321312", |
/* Column 42 */ |
"23112313", "43112311", "12212314", "32212312", "21312313", "41312311", "14121133", "34121131", "23221132", "12321133", "32321131", "21421132", "24112132", "13212133", "24121222", |
"13221223", "33221221", "11412133", "22321222", "11421223", "31421221", "14112223", "34112221", "14121313", "34121311", "12312223", "23221312", "12321313", "32321311", "21421312", |
"24112312", "13212313", "33212311", "22312312", "11412313", "31412311", "15121132", "24221131", "13321132", "22421131" }; |
private static final String[] C49_APPXE_ODD = { |
/* Appendix E - Code 49 Encodation Patterns (Odd Symbol Character Parity) */ |
/* Column 1 */ |
"22121116", "42121114", "31221115", "51221113", "32112115", "52112113", "21212116", "41212114", "61212112", "23121115", "43121113", "12221116", "32221114", "52221112", "21321115", |
"41321113", "61321111", "13112116", "33112114", "22212115", "42212113", "11312116", "31312114", "51312112", "24121114", "13221115", "33221113", "22321114", "42321112", "11421115", |
"31421113", "51421111", "14112115", "34112113", "23212114", "43212112", "12312115", "32312113", "52312111", "21412114", "41412112", "25121113", "14221114", "34221112", "23321113", |
"43321111", "12421114", "32421112", "21521113", "41521111", "15112114", "24212113", "13312114", "33312112", "22412113", "42412111", "11512114", "31512112", |
/* Column 2 */ |
"15221113", "24321112", "13421113", "33421111", "22521112", "16112113", "25212112", "14312113", "34312111", "23412112", "12512113", "32512111", "21612112", "21131116", "41131114", |
"61131112", "31122115", "51122113", "21113116", "41113114", "61113112", "22131115", "42131113", "11231116", "31231114", "51231112", "12122116", "32122114", "52122112", "21222115", |
"41222113", "61222111", "22113115", "42113113", "11213116", "31213114", "51213112", "23131114", "43131112", "12231115", "32231113", "52231111", "21331114", "41331112", "13122115", |
"33122113", "22222114", "42222112", "11322115", "31322113", "51322111", "23113114", "43113112", "12213115", "32213113", "52213111", "21313114", "41313112", |
/* Column 3 */ |
"24131113", "13231114", "33231112", "22331113", "42331111", "11431114", "31431112", "14122114", "34122112", "23222113", "43222111", "12322114", "32322112", "21422113", "41422111", |
"24113113", "13213114", "33213112", "22313113", "42313111", "11413114", "31413112", "25131112", "14231113", "34231111", "23331112", "12431113", "32431111", "15122113", "24222112", |
"13322113", "33322111", "22422112", "11522113", "31522111", "25113112", "14213113", "34213111", "23313112", "12413113", "32413111", "21513112", "15231112", "24331111", "13431112", |
"16122112", "25222111", "14322112", "23422111", "12522112", "15213112", "24313111", "13413112", "22513111", "11613112", "21141115", "41141113", "61141111", |
/* Column 4 */ |
"11132116", "31132114", "51132112", "21123115", "41123113", "61123111", "11114116", "31114114", "51114112", "22141114", "42141112", "11241115", "31241113", "51241111", "12132115", |
"32132113", "52132111", "21232114", "41232112", "22123114", "42123112", "11223115", "31223113", "51223111", "12114115", "32114113", "52114111", "21214114", "41214112", "23141113", |
"43141111", "12241114", "32241112", "21341113", "41341111", "13132114", "33132112", "22232113", "42232111", "11332114", "31332112", "23123113", "43123111", "12223114", "32223112", |
"21323113", "41323111", "13114114", "33114112", "22214113", "42214111", "11314114", "31314112", "24141112", "13241113", "33241111", "22341112", "14132113", |
/* Column 5 */ |
"34132111", "23232112", "12332113", "32332111", "21432112", "24123112", "13223113", "33223111", "22323112", "11423113", "31423111", "14114113", "34114111", "23214112", "12314113", |
"32314111", "21414112", "25141111", "14241112", "23341111", "15132112", "24232111", "13332112", "22432111", "25123111", "14223112", "23323111", "12423112", "21523111", "15114112", |
"24214111", "13314112", "22414111", "11514112", "15241111", "16132111", "14332111", "15223111", "13423111", "16114111", "14314111", "12514111", "21151114", "41151112", "11142115", |
"31142113", "51142111", "21133114", "41133112", "11124115", "31124113", "51124111", "21115114", "41115112", "22151113", "42151111", "11251114", "31251112", |
/* Column 6 */ |
"12142114", "32142112", "21242113", "41242111", "22133113", "42133111", "11233114", "31233112", "12124114", "32124112", "21224113", "41224111", "22115113", "42115111", "11215114", |
"31215112", "23151112", "12251113", "32251111", "13142113", "33142111", "22242112", "11342113", "31342111", "23133112", "12233113", "32233111", "21333112", "13124113", "33124111", |
"22224112", "11324113", "31324111", "23115112", "12215113", "32215111", "21315112", "24151111", "13251112", "14142112", "23242111", "12342112", "24133111", "13233112", "22333111", |
"11433112", "14124112", "23224111", "12324112", "21424111", "24115111", "13215112", "22315111", "11415112", "14251111", "15142111", "13342111", "14233111", |
/* Column 7 */ |
"12433111", "15124111", "13324111", "11524111", "14215111", "12415111", "21161113", "41161111", "11152114", "31152112", "21143113", "41143111", "11134114", "31134112", "21125113", |
"41125111", "11116114", "31116112", "22161112", "12152113", "32152111", "21252112", "22143112", "11243113", "31243111", "12134113", "32134111", "21234112", "22125112", "11225113", |
"31225111", "12116113", "32116111", "21216112", "23161111", "13152112", "22252111", "23143111", "12243112", "21343111", "13134112", "22234111", "11334112", "23125111", "12225112", |
"21325111", "13116112", "22216111", "11316112", "14152111", "13243111", "14134111", "12334111", "13225111", "11425111", "14116111", "12316111", "41111215", |
/* Column 8 */ |
"61111213", "21211126", "41211124", "61211122", "22111216", "42111214", "31211215", "51211213", "22211125", "42211123", "11311126", "31311124", "51311122", "23111215", "43111213", |
"12211216", "32211214", "52211212", "21311215", "41311213", "61311211", "23211124", "43211122", "12311125", "32311123", "52311121", "21411124", "41411122", "24111214", "13211215", |
"33211213", "22311214", "42311212", "11411215", "31411213", "51411211", "24211123", "13311124", "33311122", "22411123", "42411121", "11511124", "31511122", "25111213", "14211214", |
"34211212", "23311213", "43311211", "12411214", "32411212", "21511213", "41511211", "25211122", "14311123", "34311121", "23411122", "12511123", "32511121", |
/* Column 9 */ |
"21611122", "15211213", "24311212", "13411213", "33411211", "22511212", "11611213", "31611211", "31121125", "51121123", "21112126", "41112124", "61112122", "21121216", "41121214", |
"61121212", "31112215", "51112213", "12121126", "32121124", "52121122", "21221125", "41221123", "61221121", "22112125", "42112123", "11212126", "22121215", "42121213", "11221216", |
"31221214", "51221212", "12112216", "32112214", "52112212", "21212215", "41212213", "61212211", "13121125", "33121123", "22221124", "42221122", "11321125", "31321123", "51321121", |
"23112124", "43112122", "12212125", "23121214", "43121212", "12221215", "32221213", "52221211", "21321214", "41321212", "13112215", "33112213", "22212214", |
/* Column 10 */ |
"42212212", "11312215", "31312213", "51312211", "14121124", "34121122", "23221123", "43221121", "12321124", "32321122", "21421123", "41421121", "24112123", "13212124", "24121213", |
"13221214", "33221212", "11412124", "22321213", "42321211", "11421214", "31421212", "14112214", "34112212", "23212213", "43212211", "12312214", "32312212", "21412213", "41412211", |
"15121123", "24221122", "13321123", "33321121", "22421122", "11521123", "31521121", "25112122", "14212123", "25121212", "14221213", "34221211", "12412123", "23321212", "12421213", |
"32421211", "21521212", "15112213", "24212212", "13312213", "33312211", "22412212", "11512213", "31512211", "16121122", "25221121", "14321122", "23421121", |
/* Column 11 */ |
"12521122", "15212122", "15221212", "13412122", "24321211", "13421212", "11612122", "22521211", "16112212", "25212211", "14312212", "23412211", "12512212", "21612211", "11131126", |
"31131124", "51131122", "21122125", "41122123", "61122121", "21131215", "41131213", "61131211", "11113126", "31113124", "51113122", "11122216", "31122214", "51122212", "21113215", |
"41113213", "61113211", "12131125", "32131123", "52131121", "21231124", "41231122", "22122124", "42122122", "11222125", "22131214", "42131212", "11231215", "31231213", "51231211", |
"12113125", "32113123", "52113121", "12122215", "32122213", "52122211", "21222214", "41222212", "22113214", "42113212", "11213215", "31213213", "51213211", |
/* Column 12 */ |
"13131124", "33131122", "22231123", "42231121", "11331124", "31331122", "23122123", "43122121", "12222124", "23131213", "43131211", "12231214", "32231212", "21331213", "41331211", |
"13113124", "33113122", "13122214", "33122212", "11313124", "22222213", "42222211", "11322214", "31322212", "23113213", "43113211", "12213214", "32213212", "21313213", "41313211", |
"14131123", "34131121", "23231122", "12331123", "32331121", "21431122", "24122122", "13222123", "24131212", "13231213", "33231211", "11422123", "22331212", "11431213", "31431211", |
"14113123", "34113121", "14122213", "34122211", "12313123", "23222212", "12322213", "32322211", "21422212", "24113212", "13213213", "33213211", "22313212", |
/* Column 13 */ |
"11413213", "31413211", "15131122", "24231121", "13331122", "22431121", "25122121", "14222122", "25131211", "14231212", "12422122", "23331211", "12431212", "15113122", "15122212", |
"13313122", "24222211", "13322212", "11513122", "22422211", "11522212", "25113211", "14213212", "23313211", "12413212", "21513211", "16131121", "14331121", "15222121", "15231211", |
"13422121", "13431211", "16113121", "16122211", "14313121", "14322211", "12513121", "12522211", "15213211", "13413211", "11613211", "11141125", "31141123", "51141121", "21132124", |
"41132122", "21141214", "41141212", "11123125", "31123123", "51123121", "11132215", "31132213", "51132211", "21114124", "41114122", "21123214", "41123212", |
/* Column 14 */ |
"11114215", "31114213", "51114211", "12141124", "32141122", "21241123", "41241121", "22132123", "42132121", "11232124", "22141213", "42141211", "11241214", "31241212", "12123124", |
"32123122", "12132214", "32132212", "21232213", "41232211", "22114123", "42114121", "11214124", "22123213", "42123211", "11223214", "31223212", "12114214", "32114212", "21214213", |
"41214211", "13141123", "33141121", "22241122", "11341123", "31341121", "23132122", "12232123", "23141212", "12241213", "32241211", "21341212", "13123123", "33123121", "13132213", |
"33132211", "11323123", "22232212", "11332213", "31332211", "23114122", "12214123", "23123212", "12223213", "32223211", "21323212", "13114213", "33114211", |
/* Column 15 */ |
"22214212", "11314213", "31314211", "14141122", "23241121", "12341122", "24132121", "13232122", "24141211", "13241212", "11432122", "22341211", "14123122", "14132212", "12323122", |
"23232211", "12332212", "21432211", "24114121", "13214122", "24123211", "13223212", "11414122", "22323211", "11423212", "14114212", "23214211", "12314212", "21414211", "15141121", |
"13341121", "14232121", "14241211", "12432121", "15123121", "15132211", "13323121", "13332211", "11523121", "14214121", "14223211", "12414121", "12423211", "15114211", "13314211", |
"11514211", "11151124", "31151122", "21142123", "41142121", "21151213", "41151211", "11133124", "31133122", "11142214", "31142212", "21124123", "41124121", |
/* Column 16 */ |
"21133213", "41133211", "11115124", "31115122", "11124214", "31124212", "21115213", "41115211", "12151123", "32151121", "21251122", "22142122", "11242123", "22151212", "11251213", |
"31251211", "12133123", "32133121", "12142213", "32142211", "21242212", "22124122", "11224123", "22133212", "11233213", "31233211", "12115123", "32115121", "12124213", "32124211", |
"21224212", "22115212", "11215213", "31215211", "13151122", "22251121", "23142121", "12242122", "23151211", "12251212", "13133122", "13142212", "11333122", "22242211", "11342212", |
"23124121", "12224122", "23133211", "12233212", "21333211", "13115122", "13124212", "11315122", "22224211", "11324212", "23115211", "12215212", "21315211", |
/* Column 17 */ |
"14151121", "13242121", "13251211", "14133121", "14142211", "12333121", "12342211", "13224121", "13233211", "11424121", "11433211", "14115121", "14124211", "12315121", "12324211", |
"13215211", "11415211", "11161123", "31161121", "21152122", "21161212", "11143123", "31143121", "11152213", "31152211", "21134122", "21143212", "11125123", "31125121", "11134213", |
"31134211", "21116122", "21125212", "12161122", "22152121", "11252122", "22161211", "12143122", "12152212", "21252211", "22134121", "11234122", "22143211", "11243212", "12125122", |
"12134212", "21234211", "22116121", "11216122", "22125211", "11225212", "13161121", "12252121", "13143121", "13152211", "11343121", "12234121", "12243211", |
/* Column 18 */ |
"13125121", "13134211", "11325121", "11334211", "12216121", "12225211", "31111225", "51111223", "21111316", "41111314", "61111312", "31211134", "51211132", "12111226", "32111224", |
"52111222", "21211225", "41211223", "61211221", "22111315", "42111313", "11211316", "31211314", "51211312", "12211135", "32211133", "52211131", "21311134", "41311132", "13111225", |
"33111223", "22211224", "42211222", "11311225", "31311223", "51311221", "23111314", "43111312", "12211315", "32211313", "52211311", "21311314", "41311312", "13211134", "33211132", |
"22311133", "42311131", "11411134", "31411132", "14111224", "34111222", "23211223", "43211221", "12311224", "32311222", "21411223", "41411221", "24111313", |
/* Column 19 */ |
"13211314", "33211312", "22311313", "42311311", "11411314", "31411312", "14211133", "34211131", "23311132", "12411133", "32411131", "21511132", "15111223", "24211222", "13311223", |
"33311221", "22411222", "11511223", "31511221", "25111312", "14211313", "34211311", "23311312", "12411313", "32411311", "21511312", "15211132", "24311131", "13411132", "22511131", |
"11611132", "16111222", "25211221", "14311222", "23411221", "12511222", "21611221", "15211312", "24311311", "13411312", "22511311", "11611312", "21121135", "41121133", "61121131", |
"11112136", "31112134", "51112132", "11121226", "31121224", "51121222", "21112225", "41112223", "61112221", "21121315", "41121313", "61121311", "11112316", |
/* Column 20 */ |
"31112314", "51112312", "22121134", "42121132", "11221135", "31221133", "51221131", "12112135", "32112133", "52112131", "12121225", "32121223", "52121221", "21221224", "41221222", |
"22112224", "42112222", "11212225", "22121314", "42121312", "11221315", "31221313", "51221311", "12112315", "32112313", "52112311", "21212314", "41212312", "23121133", "43121131", |
"12221134", "32221132", "21321133", "41321131", "13112134", "33112132", "13121224", "33121222", "11312134", "22221223", "42221221", "11321224", "31321222", "23112223", "43112221", |
"12212224", "23121313", "43121311", "12221314", "32221312", "21321313", "41321311", "13112314", "33112312", "22212313", "42212311", "11312314", "31312312", |
/* Column 21 */ |
"24121132", "13221133", "33221131", "22321132", "11421133", "31421131", "14112133", "34112131", "14121223", "34121221", "12312133", "23221222", "12321223", "32321221", "21421222", |
"24112222", "13212223", "24121312", "13221313", "33221311", "11412223", "22321312", "11421313", "31421311", "14112313", "34112311", "23212312", "12312313", "32312311", "21412312", |
"25121131", "14221132", "23321131", "12421132", "21521131", "15112132", "15121222", "13312132", "24221221", "13321222", "11512132", "22421221", "11521222", "25112221", "14212222", |
"25121311", "14221312", "12412222", "23321311", "12421312", "21521311", "15112312", "24212311", "13312312", "22412311", "11512312", "15221131", "13421131", |
/* Column 22 */ |
"16112131", "16121221", "14312131", "14321221", "12512131", "12521221", "15212221", "15221311", "13412221", "13421311", "11612221", "16112311", "14312311", "12512311", "21131134", |
"41131132", "11122135", "31122133", "51122131", "11131225", "31131223", "51131221", "21113134", "41113132", "21122224", "41122222", "21131314", "41131312", "11113225", "31113223", |
"51113221", "11122315", "31122313", "51122311", "21113314", "41113312", "22131133", "42131131", "11231134", "31231132", "12122134", "32122132", "12131224", "32131222", "21231223", |
"41231221", "22113133", "42113131", "11213134", "22122223", "42122221", "11222224", "22131313", "42131311", "11231314", "31231312", "12113224", "32113222", |
/* Column 23 */ |
"12122314", "32122312", "21222313", "41222311", "22113313", "42113311", "11213314", "31213312", "23131132", "12231133", "32231131", "21331132", "13122133", "33122131", "13131223", |
"33131221", "11322133", "22231222", "11331223", "31331221", "23113132", "12213133", "23122222", "12222223", "23131312", "12231313", "32231311", "21331312", "13113223", "33113221", |
"13122313", "33122311", "11313223", "22222312", "11322313", "31322311", "23113312", "12213313", "32213311", "21313312", "24131131", "13231132", "22331131", "11431132", "14122132", |
"14131222", "12322132", "23231221", "12331222", "21431221", "24113131", "13213132", "24122221", "13222222", "24131311", "11413132", "13231312", "11422222", |
/* Column 24 */ |
"22331311", "11431312", "14113222", "14122312", "12313222", "23222311", "12322312", "21422311", "24113311", "13213312", "22313311", "11413312", "14231131", "12431131", "15122131", |
"15131221", "13322131", "13331221", "11522131", "14213131", "14222221", "12413131", "14231311", "12422221", "12431311", "15113221", "15122311", "13313221", "13322311", "11513221", |
"11522311", "14213311", "12413311", "21141133", "41141131", "11132134", "31132132", "11141224", "31141222", "21123133", "41123131", "21132223", "41132221", "21141313", "41141311", |
"11114134", "31114132", "11123224", "31123222", "11132314", "31132312", "21114223", "41114221", "21123313", "41123311", "11114314", "31114312", "22141132", |
/* Column 25 */ |
"11241133", "31241131", "12132133", "32132131", "12141223", "32141221", "21241222", "22123132", "11223133", "22132222", "11232223", "22141312", "11241313", "31241311", "12114133", |
"32114131", "12123223", "32123221", "12132313", "32132311", "21232312", "22114222", "11214223", "22123312", "11223313", "31223311", "12114313", "32114311", "21214312", "23141131", |
"12241132", "21341131", "13132132", "13141222", "11332132", "22241221", "11341222", "23123131", "12223132", "23132221", "12232222", "23141311", "12241312", "21341311", "13114132", |
"13123222", "11314132", "13132312", "11323222", "22232311", "11332312", "23114221", "12214222", "23123311", "12223312", "21323311", "13114312", "22214311", |
/* Column 26 */ |
"11314312", "13241131", "14132131", "14141221", "12332131", "12341221", "13223131", "13232221", "11423131", "13241311", "11432221", "14114131", "14123221", "12314131", "14132311", |
"12323221", "12332311", "13214221", "13223311", "11414221", "11423311", "14114311", "12314311", "21151132", "11142133", "31142131", "11151223", "31151221", "21133132", "21142222", |
"21151312", "11124133", "31124131", "11133223", "31133221", "11142313", "31142311", "21115132", "21124222", "21133312", "11115223", "31115221", "11124313", "31124311", "22151131", |
"11251132", "12142132", "12151222", "21251221", "22133131", "11233132", "22142221", "11242222", "22151311", "11251312", "12124132", "12133222", "12142312", |
/* Column 27 */ |
"21242311", "22115131", "11215132", "22124221", "11224222", "22133311", "11233312", "12115222", "12124312", "21224311", "12251131", "13142131", "13151221", "11342131", "12233131", |
"12242221", "12251311", "13124131", "13133221", "11324131", "13142311", "11333221", "11342311", "12215131", "12224221", "12233311", "13115221", "13124311", "11315221", "11324311", |
"21161131", "11152132", "11161222", "21143131", "21152221", "21161311", "11134132", "11143222", "11152312", "21125131", "21134221", "21143311", "11116132", "11125222", "11134312", |
"12152131", "12161221", "11243131", "11252221", "12134131", "12143221", "12152311", "11225131", "11234221", "11243311", "12116131", "12125221", "12134311", |
/* Column 28 */ |
"21111235", "41111233", "61111231", "11111326", "31111324", "51111322", "21111415", "41111413", "61111411", "21211144", "41211142", "22111234", "42111232", "11211235", "31211233", |
"51211231", "12111325", "32111323", "52111321", "21211324", "41211322", "22111414", "42111412", "11211415", "31211413", "51211411", "22211143", "42211141", "11311144", "31311142", |
"23111233", "43111231", "12211234", "32211232", "21311233", "41311231", "13111324", "33111322", "22211323", "42211321", "11311324", "31311322", "23111413", "43111411", "12211414", |
"32211412", "21311413", "41311411", "23211142", "12311143", "32311141", "21411142", "24111232", "13211233", "33211231", "22311232", "11411233", "31411231", |
/* Column 29 */ |
"14111323", "34111321", "23211322", "12311323", "32311321", "21411322", "24111412", "13211413", "33211411", "22311412", "11411413", "31411411", "24211141", "13311142", "22411141", |
"11511142", "25111231", "14211232", "23311231", "12411232", "21511231", "15111322", "24211321", "13311322", "22411321", "11511322", "25111411", "14211412", "23311411", "12411412", |
"21511411", "14311141", "12511141", "15211231", "13411231", "11611231", "16111321", "14311321", "12511321", "15211411", "13411411", "11611411", "31121143", "51121141", "21112144", |
"41112142", "21121234", "41121232", "11112235", "31112233", "51112231", "11121325", "31121323", "51121321", "21112324", "41112322", "21121414", "41121412", |
/* Column 30 */ |
"11112415", "31112413", "51112411", "12121144", "32121142", "21221143", "41221141", "22112143", "42112141", "11212144", "22121233", "42121231", "11221234", "31221232", "12112234", |
"32112232", "12121324", "32121322", "21221323", "41221321", "22112323", "42112321", "11212324", "22121413", "42121411", "11221414", "31221412", "12112414", "32112412", "21212413", |
"41212411", "13121143", "33121141", "22221142", "11321143", "31321141", "23112142", "12212143", "23121232", "12221233", "32221231", "21321232", "13112233", "33112231", "13121323", |
"33121321", "11312233", "22221322", "11321323", "31321321", "23112322", "12212323", "23121412", "12221413", "32221411", "21321412", "13112413", "33112411", |
/* Column 31 */ |
"22212412", "11312413", "31312411", "14121142", "23221141", "12321142", "21421141", "24112141", "13212142", "24121231", "13221232", "11412142", "22321231", "11421232", "14112232", |
"14121322", "12312232", "23221321", "12321322", "21421321", "24112321", "13212322", "24121411", "13221412", "11412322", "22321411", "11421412", "14112412", "23212411", "12312412", |
"21412411", "15121141", "13321141", "11521141", "14212141", "14221231", "12412141", "12421231", "15112231", "15121321", "13312231", "13321321", "11512231", "11521321", "14212321", |
"14221411", "12412321", "12421411", "15112411", "13312411", "11512411", "11131144", "31131142", "21122143", "41122141", "21131233", "41131231", "11113144", |
/* Column 32 */ |
"31113142", "11122234", "31122232", "11131324", "31131322", "21113233", "41113231", "21122323", "41122321", "21131413", "41131411", "11113324", "31113322", "11122414", "31122412", |
"21113413", "41113411", "12131143", "32131141", "21231142", "22122142", "11222143", "22131232", "11231233", "31231231", "12113143", "32113141", "12122233", "32122231", "12131323", |
"32131321", "21231322", "22113232", "11213233", "22122322", "11222323", "22131412", "11231413", "31231411", "12113323", "32113321", "12122413", "32122411", "21222412", "22113412", |
"11213413", "31213411", "13131142", "22231141", "11331142", "23122141", "12222142", "23131231", "12231232", "21331231", "13113142", "13122232", "11313142", |
/* Column 33 */ |
"13131322", "11322232", "22231321", "11331322", "23113231", "12213232", "23122321", "12222322", "23131411", "12231412", "21331411", "13113322", "13122412", "11313322", "22222411", |
"11322412", "23113411", "12213412", "21313411", "14131141", "12331141", "13222141", "13231231", "11422141", "11431231", "14113141", "14122231", "12313141", "14131321", "12322231", |
"12331321", "13213231", "13222321", "11413231", "13231411", "11422321", "11431411", "14113321", "14122411", "12313321", "12322411", "13213411", "11413411", "11141143", "31141141", |
"21132142", "21141232", "11123143", "31123141", "11132233", "31132231", "11141323", "31141321", "21114142", "21123232", "21132322", "21141412", "11114233", |
/* Column 34 */ |
"31114231", "11123323", "31123321", "11132413", "31132411", "21114322", "21123412", "12141142", "21241141", "22132141", "11232142", "22141231", "11241232", "12123142", "12132232", |
"12141322", "21241321", "22114141", "11214142", "22123231", "11223232", "22132321", "11232322", "22141411", "11241412", "12114232", "12123322", "12132412", "21232411", "22114321", |
"11214322", "22123411", "11223412", "13141141", "11341141", "12232141", "12241231", "13123141", "13132231", "11323141", "13141321", "11332231", "11341321", "12214141", "12223231", |
"12232321", "12241411", "13114231", "13123321", "11314231", "13132411", "11323321", "11332411", "12214321", "12223411", "11151142", "21142141", "21151231", |
/* Column 35 */ |
"11133142", "11142232", "11151322", "21124141", "21133231", "21142321", "21151411", "11115142", "11124232", "11133322", "11142412", "21115231", "21124321", "21133411", "12151141", |
"11242141", "11251231", "12133141", "12142231", "12151321", "11224141", "11233231", "11242321", "11251411", "12115141", "12124231", "12133321", "12142411", "11215231", "11224321", |
"11233411", "11161141", "11143141", "11152231", "11161321", "11125141", "11134231", "11143321", "11152411", "11111245", "31111243", "51111241", "21111334", "41111332", "11111425", |
"31111423", "51111421", "21111514", "41111512", "31211152", "12111244", "32111242", "21211243", "41211241", "22111333", "42111331", "11211334", "31211332", |
/* Column 36 */ |
"12111424", "32111422", "21211423", "41211421", "22111513", "42111511", "11211514", "31211512", "12211153", "32211151", "21311152", "13111243", "33111241", "22211242", "11311243", |
"31311241", "23111332", "12211333", "32211331", "21311332", "13111423", "33111421", "22211422", "11311423", "31311421", "23111512", "12211513", "32211511", "21311512", "13211152", |
"22311151", "11411152", "14111242", "23211241", "12311242", "21411241", "24111331", "13211332", "22311331", "11411332", "14111422", "23211421", "12311422", "21411421", "24111511", |
"13211512", "22311511", "11411512", "14211151", "12411151", "15111241", "13311241", "11511241", "14211331", "12411331", "15111421", "13311421", "11511421", |
/* Column 37 */ |
"14211511", "12411511", "21121153", "41121151", "11112154", "31112152", "11121244", "31121242", "21112243", "41112241", "21121333", "41121331", "11112334", "31112332", "11121424", |
"31121422", "21112423", "41112421", "21121513", "41121511", "11112514", "31112512", "22121152", "11221153", "31221151", "12112153", "32112151", "12121243", "32121241", "21221242", |
"22112242", "11212243", "22121332", "11221333", "31221331", "12112333", "32112331", "12121423", "32121421", "21221422", "22112422", "11212423", "22121512", "11221513", "31221511", |
"12112513", "32112511", "21212512", "23121151", "12221152", "21321151", "13112152", "13121242", "11312152", "22221241", "11321242", "23112241", "12212242", |
/* Column 38 */ |
"23121331", "12221332", "21321331", "13112332", "13121422", "11312332", "22221421", "11321422", "23112421", "12212422", "23121511", "12221512", "21321511", "13112512", "22212511", |
"11312512", "13221151", "11421151", "14112151", "14121241", "12312151", "12321241", "13212241", "13221331", "11412241", "11421331", "14112331", "14121421", "12312331", "12321421", |
"13212421", "13221511", "11412421", "11421511", "14112511", "12312511", "21131152", "11122153", "31122151", "11131243", "31131241", "21113152", "21122242", "21131332", "11113243", |
"31113241", "11122333", "31122331", "11131423", "31131421", "21113332", "21122422", "21131512", "11113423", "31113421", "11122513", "31122511", "22131151", |
/* Column 39 */ |
"11231152", "12122152", "12131242", "21231241", "22113151", "11213152", "22122241", "11222242", "22131331", "11231332", "12113242", "12122332", "12131422", "21231421", "22113331", |
"11213332", "22122421", "11222422", "22131511", "11231512", "12113422", "12122512", "21222511", "12231151", "13122151", "13131241", "11322151", "11331241", "12213151", "12222241", |
"12231331", "13113241", "13122331", "11313241", "13131421", "11322331", "11331421", "12213331", "12222421", "12231511", "13113421", "13122511", "11313421", "11322511", "21141151", |
"11132152", "11141242", "21123151", "21132241", "21141331", "11114152", "11123242", "11132332", "11141422", "21114241", "21123331", "21132421", "21141511", |
/* Column 40 */ |
"11114332", "11123422", "11132512", "11241151", "12132151", "12141241", "11223151", "11232241", "11241331", "12114151", "12123241", "12132331", "12141421", "11214241", "11223331", |
"11232421", "11241511", "12114331", "12123421", "12132511", "11142151", "11151241", "11124151", "11133241", "11142331", "11151421", "11115241", "11124331", "11133421", "11142511", |
"21111253", "41111251", "11111344", "31111342", "21111433", "41111431", "11111524", "31111522", "21111613", "41111611", "21211162", "22111252", "11211253", "31211251", "12111343", |
"32111341", "21211342", "22111432", "11211433", "31211431", "12111523", "32111521", "21211522", "22111612", "11211613", "31211611", "22211161", "11311162", |
/* Column 41 */ |
"23111251", "12211252", "21311251", "13111342", "22211341", "11311342", "23111431", "12211432", "21311431", "13111522", "22211521", "11311522", "23111611", "12211612", "21311611", |
"12311161", "13211251", "11411251", "14111341", "12311341", "13211431", "11411431", "14111521", "12311521", "13211611", "11411611", "31121161", "21112162", "21121252", "11112253", |
"31112251", "11121343", "31121341", "21112342", "21121432", "11112433", "31112431", "11121523", "31121521", "21112522", "21121612", |
/* Column 42 */ |
"12121162", "21221161", "22112161", "11212162", "22121251", "11221252", "12112252", "12121342", "21221341", "22112341", "11212342", "22121431", "11221432", "12112432", "12121522", |
"21221521", "22112521", "11212522", "22121611", "11221612", "13121161", "11321161", "12212161", "12221251", "13112251", "13121341", "11312251", "11321341", "12212341", "12221431", |
"13112431", "13121521", "11312431", "11321521", "12212521", "12221611", "11131162", "21122161", "21131251", "11113162" }; |
private static final char[] C49_SET = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', |
'V', 'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+', '%', '!', '&', '*' }; |
@Override |
protected boolean gs1Supported() { |
return true; |
} |
@Override |
protected void encode() { |
int i, codeword_count = 0, h, j, M, rows, pad_count = 0; |
int x_count, y_count, z_count, posn_val, local_value; |
String intermediate = ""; |
String localpattern = ""; |
final int[] codewords = new int[170]; |
final int[][] c_grid = new int[8][8]; |
final int[][] w_grid = new int[8][4]; |
if (!this.content.matches("[\u0000-\u007F]+")) { |
throw new OkapiException("Invalid characters in input data"); |
} |
this.inputData = toBytes(this.content, StandardCharsets.US_ASCII); |
if (this.inputDataType == DataType.GS1) { |
intermediate += "*"; // FNC1 |
} |
for (i = 0; i < this.inputData.length; i++) { |
final int c = this.inputData[i]; |
if (c == FNC1) { |
intermediate += "*"; // FNC1 |
} else { |
intermediate += C49_TABLE7[c]; |
} |
} |
h = intermediate.length(); |
i = 0; |
do { |
if (intermediate.charAt(i) >= '0' && intermediate.charAt(i) <= '9') { |
/* Numeric data */ |
int latch = 0; |
j = 0; |
do { |
if (i + j >= h) { |
latch = 1; |
} else { |
if (intermediate.charAt(i + j) >= '0' && intermediate.charAt(i + j) <= '9') { |
j++; |
} else { |
latch = 1; |
} |
} |
} while (latch == 0); |
if (j >= 5) { |
/* Use Numeric Encodation Method */ |
int block_count, c; |
int block_remain; |
int block_value; |
codewords[codeword_count] = 48; /* Numeric Shift */ |
codeword_count++; |
block_count = j / 5; |
block_remain = j % 5; |
for (c = 0; c < block_count; c++) { |
if (c == block_count - 1 && block_remain == 2) { |
/* Rule (d) */ |
block_value = 100000; |
block_value += (intermediate.charAt(i) - '0') * 1000; |
block_value += (intermediate.charAt(i + 1) - '0') * 100; |
block_value += (intermediate.charAt(i + 2) - '0') * 10; |
block_value += intermediate.charAt(i + 3) - '0'; |
codewords[codeword_count] = block_value / (48 * 48); |
block_value = block_value - 48 * 48 * codewords[codeword_count]; |
codeword_count++; |
codewords[codeword_count] = block_value / 48; |
block_value = block_value - 48 * codewords[codeword_count]; |
codeword_count++; |
codewords[codeword_count] = block_value; |
codeword_count++; |
i += 4; |
block_value = (intermediate.charAt(i) - '0') * 100; |
block_value += (intermediate.charAt(i + 1) - '0') * 10; |
block_value += intermediate.charAt(i + 2) - '0'; |
codewords[codeword_count] = block_value / 48; |
block_value = block_value - 48 * codewords[codeword_count]; |
codeword_count++; |
codewords[codeword_count] = block_value; |
codeword_count++; |
i += 3; |
} else { |
block_value = (intermediate.charAt(i) - '0') * 10000; |
block_value += (intermediate.charAt(i + 1) - '0') * 1000; |
block_value += (intermediate.charAt(i + 2) - '0') * 100; |
block_value += (intermediate.charAt(i + 3) - '0') * 10; |
block_value += intermediate.charAt(i + 4) - '0'; |
codewords[codeword_count] = block_value / (48 * 48); |
block_value = block_value - 48 * 48 * codewords[codeword_count]; |
codeword_count++; |
codewords[codeword_count] = block_value / 48; |
block_value = block_value - 48 * codewords[codeword_count]; |
codeword_count++; |
codewords[codeword_count] = block_value; |
codeword_count++; |
i += 5; |
} |
} |
switch (block_remain) { |
case 1: |
/* Rule (a) */ |
codewords[codeword_count] = positionOf(intermediate.charAt(i), C49_SET); |
codeword_count++; |
i++; |
break; |
case 3: |
/* Rule (b) */ |
block_value = (intermediate.charAt(i) - '0') * 100; |
block_value += (intermediate.charAt(i + 1) - '0') * 10; |
block_value += intermediate.charAt(i + 2) - '0'; |
codewords[codeword_count] = block_value / 48; |
block_value = block_value - 48 * codewords[codeword_count]; |
codeword_count++; |
codewords[codeword_count] = block_value; |
codeword_count++; |
i += 3; |
break; |
case 4: |
/* Rule (c) */ |
block_value = 100000; |
block_value += (intermediate.charAt(i) - '0') * 1000; |
block_value += (intermediate.charAt(i + 1) - '0') * 100; |
block_value += (intermediate.charAt(i + 2) - '0') * 10; |
block_value += intermediate.charAt(i + 3) - '0'; |
codewords[codeword_count] = block_value / (48 * 48); |
block_value = block_value - 48 * 48 * codewords[codeword_count]; |
codeword_count++; |
codewords[codeword_count] = block_value / 48; |
block_value = block_value - 48 * codewords[codeword_count]; |
codeword_count++; |
codewords[codeword_count] = block_value; |
codeword_count++; |
i += 4; |
break; |
} |
if (i < h) { |
/* There is more to add */ |
codewords[codeword_count] = 48; /* Numeric Shift */ |
codeword_count++; |
} |
} else { |
codewords[codeword_count] = positionOf(intermediate.charAt(i), C49_SET); |
codeword_count++; |
i++; |
} |
} else { |
codewords[codeword_count] = positionOf(intermediate.charAt(i), C49_SET); |
codeword_count++; |
i++; |
} |
} while (i < h); |
switch (codewords[0]) { /* Set starting mode value */ |
case 48: |
M = 2; |
break; |
case 43: |
M = 4; |
break; |
case 44: |
M = 5; |
break; |
default: |
M = 0; |
break; |
} |
if (M != 0) { |
for (i = 0; i < codeword_count; i++) { |
codewords[i] = codewords[i + 1]; |
} |
codeword_count--; |
} |
if (codeword_count > 49) { |
throw new OkapiException("Input too long"); |
} |
infoLine("Starting Mode (M): " + M); |
/* Place codewords in code character array (c grid) */ |
rows = 0; |
do { |
for (i = 0; i < 7; i++) { |
if (rows * 7 + i < codeword_count) { |
c_grid[rows][i] = codewords[rows * 7 + i]; |
} else { |
c_grid[rows][i] = 48; /* Pad */ |
pad_count++; |
} |
} |
rows++; |
} while (rows * 7 < codeword_count); |
if (rows <= 6 && pad_count < 5 || rows > 6 || rows == 1) { |
/* Add a row */ |
for (i = 0; i < 7; i++) { |
c_grid[rows][i] = 48; /* Pad */ |
} |
rows++; |
} |
/* Add row count and mode character */ |
c_grid[rows - 1][6] = 7 * (rows - 2) + M; |
/* Add row check character */ |
for (i = 0; i < rows - 1; i++) { |
int row_sum = 0; |
for (j = 0; j < 7; j++) { |
row_sum += c_grid[i][j]; |
} |
c_grid[i][7] = row_sum % 49; |
} |
/* Calculate Symbol Check Characters */ |
posn_val = 0; |
x_count = c_grid[rows - 1][6] * 20; |
y_count = c_grid[rows - 1][6] * 16; |
z_count = c_grid[rows - 1][6] * 38; |
for (i = 0; i < rows - 1; i++) { |
for (j = 0; j < 4; j++) { |
local_value = c_grid[i][2 * j] * 49 + c_grid[i][2 * j + 1]; |
x_count += C49_X_WEIGHT[posn_val] * local_value; |
y_count += C49_Y_WEIGHT[posn_val] * local_value; |
z_count += C49_Z_WEIGHT[posn_val] * local_value; |
posn_val++; |
} |
} |
if (rows > 6) { |
/* Add Z Symbol Check */ |
c_grid[rows - 1][0] = z_count % 2401 / 49; |
c_grid[rows - 1][1] = z_count % 2401 % 49; |
} |
local_value = c_grid[rows - 1][0] * 49 + c_grid[rows - 1][1]; |
x_count += C49_X_WEIGHT[posn_val] * local_value; |
y_count += C49_Y_WEIGHT[posn_val] * local_value; |
posn_val++; |
/* Add Y Symbol Check */ |
c_grid[rows - 1][2] = y_count % 2401 / 49; |
c_grid[rows - 1][3] = y_count % 2401 % 49; |
local_value = c_grid[rows - 1][2] * 49 + c_grid[rows - 1][3]; |
x_count += C49_X_WEIGHT[posn_val] * local_value; |
/* Add X Symbol Check */ |
c_grid[rows - 1][4] = x_count % 2401 / 49; |
c_grid[rows - 1][5] = x_count % 2401 % 49; |
infoLine("Check Characters: " + z_count % 2401 + " " + y_count % 2401); |
/* Add last row check character */ |
j = 0; |
for (i = 0; i < 7; i++) { |
j += c_grid[rows - 1][i]; |
} |
c_grid[rows - 1][7] = j % 49; |
info("Codewords: "); |
/* Transfer data to symbol character array (w grid) */ |
for (i = 0; i < rows; i++) { |
for (j = 0; j < 4; j++) { |
w_grid[i][j] = c_grid[i][2 * j] * 49 + c_grid[i][2 * j + 1]; |
infoSpace(c_grid[i][2 * j]); |
infoSpace(c_grid[i][2 * j + 1]); |
} |
} |
infoLine(); |
this.readable = ""; |
this.pattern = new String[rows]; |
this.row_count = rows; |
this.row_height = new int[rows]; |
info("Symbol Characters: "); |
for (i = 0; i < rows; i++) { |
localpattern = "11"; /* Start character */ |
for (j = 0; j < 4; j++) { |
infoSpace(w_grid[i][j]); |
if (i != rows - 1) { |
if (C49_TABLE4[i].charAt(j) == 'E') { |
/* Even Parity */ |
localpattern += C49_APPXE_EVEN[w_grid[i][j]]; |
} else { |
/* Odd Parity */ |
localpattern += C49_APPXE_ODD[w_grid[i][j]]; |
} |
} else { |
/* Last row uses all even parity */ |
localpattern += C49_APPXE_EVEN[w_grid[i][j]]; |
} |
} |
localpattern += "4"; /* Stop character */ |
this.pattern[i] = localpattern; |
this.row_height[i] = 10; |
} |
infoLine(); |
} |
@Override |
protected void plotSymbol() { |
int xBlock, yBlock; |
int x, y, w, h; |
boolean black; |
this.rectangles.clear(); |
y = 1; |
h = 1; |
for (yBlock = 0; yBlock < this.row_count; yBlock++) { |
black = true; |
x = 15; |
for (xBlock = 0; xBlock < this.pattern[yBlock].length(); xBlock++) { |
if (black) { |
black = false; |
w = this.pattern[yBlock].charAt(xBlock) - '0'; |
if (this.row_height[yBlock] == -1) { |
h = this.default_height; |
} else { |
h = this.row_height[yBlock]; |
} |
if (w != 0 && h != 0) { |
final Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h); |
this.rectangles.add(rect); |
} |
if (x + w > this.symbol_width) { |
this.symbol_width = x + w; |
} |
} else { |
black = true; |
} |
x += this.pattern[yBlock].charAt(xBlock) - '0'; |
} |
y += h; |
if (y > this.symbol_height) { |
this.symbol_height = y; |
} |
/* Add bars between rows */ |
if (yBlock != this.row_count - 1) { |
final Rectangle2D.Double rect = new Rectangle2D.Double(15, y - 1, this.symbol_width - 15, 2); |
this.rectangles.add(rect); |
} |
} |
/* Add top and bottom binding bars */ |
final Rectangle2D.Double top = new Rectangle2D.Double(0, 0, this.symbol_width + 15, 2); |
this.rectangles.add(top); |
final Rectangle2D.Double bottom = new Rectangle2D.Double(0, y - 1, this.symbol_width + 15, 2); |
this.rectangles.add(bottom); |
this.symbol_width += 15; |
this.symbol_height += 1; |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/HumanReadableAlignment.java |
---|
New file |
0,0 → 1,34 |
/* |
* Copyright 2018 Daniel Gredler |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
/** |
* The text alignment of a bar code's human-readable text. |
*/ |
public enum HumanReadableAlignment { |
/** Left-align the human-readable text. */ |
LEFT, |
/** Right-align the human-readable text. */ |
RIGHT, |
/** Center the human-readable text. */ |
CENTER, |
/** Justify the human-readable text by adjusting the spaces between the characters. */ |
JUSTIFY |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/MaxiCode.java |
---|
New file |
0,0 → 1,913 |
/* |
* Copyright 2014-2015 Robin Stuart, Daniel Gredler |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
import static uk.org.okapibarcode.util.Arrays.contains; |
import static uk.org.okapibarcode.util.Arrays.insertArray; |
import java.awt.geom.Ellipse2D; |
import java.util.Arrays; |
/** |
* <p> |
* Implements MaxiCode according to ISO 16023:2000. |
* |
* <p> |
* MaxiCode employs a pattern of hexagons around a central 'bulls-eye' finder pattern. Encoding in |
* several modes is supported, but encoding in Mode 2 and 3 require primary messages to be set. |
* Input characters can be any from the ISO 8859-1 (Latin-1) character set. |
* |
* <p> |
* TODO: Add ECI functionality. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
* @author Daniel Gredler |
*/ |
public class MaxiCode extends Symbol { |
/** MaxiCode module sequence, from ISO/IEC 16023 Figure 5 (30 x 33 data grid). */ |
private static final int[] MAXICODE_GRID = { 122, 121, 128, 127, 134, 133, 140, 139, 146, 145, 152, 151, 158, 157, 164, 163, 170, 169, 176, 175, 182, 181, 188, 187, 194, 193, 200, 199, 0, 0, 124, |
123, 130, 129, 136, 135, 142, 141, 148, 147, 154, 153, 160, 159, 166, 165, 172, 171, 178, 177, 184, 183, 190, 189, 196, 195, 202, 201, 817, 0, 126, 125, 132, 131, 138, 137, 144, 143, 150, |
149, 156, 155, 162, 161, 168, 167, 174, 173, 180, 179, 186, 185, 192, 191, 198, 197, 204, 203, 819, 818, 284, 283, 278, 277, 272, 271, 266, 265, 260, 259, 254, 253, 248, 247, 242, 241, |
236, 235, 230, 229, 224, 223, 218, 217, 212, 211, 206, 205, 820, 0, 286, 285, 280, 279, 274, 273, 268, 267, 262, 261, 256, 255, 250, 249, 244, 243, 238, 237, 232, 231, 226, 225, 220, 219, |
214, 213, 208, 207, 822, 821, 288, 287, 282, 281, 276, 275, 270, 269, 264, 263, 258, 257, 252, 251, 246, 245, 240, 239, 234, 233, 228, 227, 222, 221, 216, 215, 210, 209, 823, 0, 290, 289, |
296, 295, 302, 301, 308, 307, 314, 313, 320, 319, 326, 325, 332, 331, 338, 337, 344, 343, 350, 349, 356, 355, 362, 361, 368, 367, 825, 824, 292, 291, 298, 297, 304, 303, 310, 309, 316, |
315, 322, 321, 328, 327, 334, 333, 340, 339, 346, 345, 352, 351, 358, 357, 364, 363, 370, 369, 826, 0, 294, 293, 300, 299, 306, 305, 312, 311, 318, 317, 324, 323, 330, 329, 336, 335, 342, |
341, 348, 347, 354, 353, 360, 359, 366, 365, 372, 371, 828, 827, 410, 409, 404, 403, 398, 397, 392, 391, 80, 79, 0, 0, 14, 13, 38, 37, 3, 0, 45, 44, 110, 109, 386, 385, 380, 379, 374, 373, |
829, 0, 412, 411, 406, 405, 400, 399, 394, 393, 82, 81, 41, 0, 16, 15, 40, 39, 4, 0, 0, 46, 112, 111, 388, 387, 382, 381, 376, 375, 831, 830, 414, 413, 408, 407, 402, 401, 396, 395, 84, |
83, 42, 0, 0, 0, 0, 0, 6, 5, 48, 47, 114, 113, 390, 389, 384, 383, 378, 377, 832, 0, 416, 415, 422, 421, 428, 427, 104, 103, 56, 55, 17, 0, 0, 0, 0, 0, 0, 0, 21, 20, 86, 85, 434, 433, 440, |
439, 446, 445, 834, 833, 418, 417, 424, 423, 430, 429, 106, 105, 58, 57, 0, 0, 0, 0, 0, 0, 0, 0, 23, 22, 88, 87, 436, 435, 442, 441, 448, 447, 835, 0, 420, 419, 426, 425, 432, 431, 108, |
107, 60, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 90, 89, 438, 437, 444, 443, 450, 449, 837, 836, 482, 481, 476, 475, 470, 469, 49, 0, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 54, 53, 464, 463, 458, |
457, 452, 451, 838, 0, 484, 483, 478, 477, 472, 471, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 466, 465, 460, 459, 454, 453, 840, 839, 486, 485, 480, 479, 474, 473, 52, 51, 32, 0, |
0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 43, 468, 467, 462, 461, 456, 455, 841, 0, 488, 487, 494, 493, 500, 499, 98, 97, 62, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 92, 91, 506, 505, 512, 511, 518, |
517, 843, 842, 490, 489, 496, 495, 502, 501, 100, 99, 64, 63, 0, 0, 0, 0, 0, 0, 0, 0, 29, 28, 94, 93, 508, 507, 514, 513, 520, 519, 844, 0, 492, 491, 498, 497, 504, 503, 102, 101, 66, 65, |
18, 0, 0, 0, 0, 0, 0, 0, 19, 30, 96, 95, 510, 509, 516, 515, 522, 521, 846, 845, 560, 559, 554, 553, 548, 547, 542, 541, 74, 73, 33, 0, 0, 0, 0, 0, 0, 11, 68, 67, 116, 115, 536, 535, 530, |
529, 524, 523, 847, 0, 562, 561, 556, 555, 550, 549, 544, 543, 76, 75, 0, 0, 8, 7, 36, 35, 12, 0, 70, 69, 118, 117, 538, 537, 532, 531, 526, 525, 849, 848, 564, 563, 558, 557, 552, 551, |
546, 545, 78, 77, 0, 34, 10, 9, 26, 25, 0, 0, 72, 71, 120, 119, 540, 539, 534, 533, 528, 527, 850, 0, 566, 565, 572, 571, 578, 577, 584, 583, 590, 589, 596, 595, 602, 601, 608, 607, 614, |
613, 620, 619, 626, 625, 632, 631, 638, 637, 644, 643, 852, 851, 568, 567, 574, 573, 580, 579, 586, 585, 592, 591, 598, 597, 604, 603, 610, 609, 616, 615, 622, 621, 628, 627, 634, 633, |
640, 639, 646, 645, 853, 0, 570, 569, 576, 575, 582, 581, 588, 587, 594, 593, 600, 599, 606, 605, 612, 611, 618, 617, 624, 623, 630, 629, 636, 635, 642, 641, 648, 647, 855, 854, 728, 727, |
722, 721, 716, 715, 710, 709, 704, 703, 698, 697, 692, 691, 686, 685, 680, 679, 674, 673, 668, 667, 662, 661, 656, 655, 650, 649, 856, 0, 730, 729, 724, 723, 718, 717, 712, 711, 706, 705, |
700, 699, 694, 693, 688, 687, 682, 681, 676, 675, 670, 669, 664, 663, 658, 657, 652, 651, 858, 857, 732, 731, 726, 725, 720, 719, 714, 713, 708, 707, 702, 701, 696, 695, 690, 689, 684, |
683, 678, 677, 672, 671, 666, 665, 660, 659, 654, 653, 859, 0, 734, 733, 740, 739, 746, 745, 752, 751, 758, 757, 764, 763, 770, 769, 776, 775, 782, 781, 788, 787, 794, 793, 800, 799, 806, |
805, 812, 811, 861, 860, 736, 735, 742, 741, 748, 747, 754, 753, 760, 759, 766, 765, 772, 771, 778, 777, 784, 783, 790, 789, 796, 795, 802, 801, 808, 807, 814, 813, 862, 0, 738, 737, 744, |
743, 750, 749, 756, 755, 762, 761, 768, 767, 774, 773, 780, 779, 786, 785, 792, 791, 798, 797, 804, 803, 810, 809, 816, 815, 864, 863 }; |
/** |
* ASCII character to Code Set mapping, from ISO/IEC 16023 Appendix A. 1 = Set A, 2 = Set B, 3 = |
* Set C, 4 = Set D, 5 = Set E. 0 refers to special characters that fit into more than one set |
* (e.g. GS). |
*/ |
private static final int[] MAXICODE_SET = { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 0, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, |
1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, |
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, 5, 4, 5, 3, 4, 3, 5, 5, 4, 4, |
3, 3, 3, 4, 3, 5, 4, 4, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, |
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }; |
/** ASCII character to symbol value, from ISO/IEC 16023 Appendix A. */ |
private static final int[] MAXICODE_SYMBOL_CHAR = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 30, 28, 29, 30, 35, 32, 53, 34, 35, 36, 37, |
38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 37, 38, 39, 40, 41, 52, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, |
23, 24, 25, 26, 42, 43, 44, 45, 46, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 32, 54, 34, 35, 36, 48, 49, 50, 51, 52, 53, 54, 55, |
56, 57, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 36, 37, 37, 38, 39, 40, 41, 42, 43, 38, 44, 37, 39, 38, 45, 46, 40, 41, 39, 40, 41, 42, 42, 47, |
43, 44, 43, 44, 45, 45, 46, 47, 46, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 32, 33, 34, 35, 36, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, |
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 32, 33, 34, 35, 36 }; |
private int mode; |
private int structuredAppendPosition = 1; |
private int structuredAppendTotal = 1; |
private String primaryData = ""; |
private int[] codewords; |
private final int[] set = new int[144]; |
private final int[] character = new int[144]; |
private final boolean[][] grid = new boolean[33][30]; |
/** |
* Sets the MaxiCode mode to use. Only modes 2 to 6 are supported. |
* |
* @param mode the MaxiCode mode to use |
*/ |
public void setMode(final int mode) { |
if (mode < 2 || mode > 6) { |
throw new IllegalArgumentException("Invalid MaxiCode mode: " + mode); |
} |
this.mode = mode; |
} |
/** |
* Returns the MaxiCode mode being used. Only modes 2 to 6 are supported. |
* |
* @return the MaxiCode mode being used |
*/ |
public int getMode() { |
return this.mode; |
} |
/** |
* If this MaxiCode symbol is part of a series of MaxiCode symbols appended in a structured |
* format, this method sets the position of this symbol in the series. Valid values are 1 |
* through 8 inclusive. |
* |
* @param position the position of this MaxiCode symbol in the structured append series |
*/ |
public void setStructuredAppendPosition(final int position) { |
if (position < 1 || position > 8) { |
throw new IllegalArgumentException("Invalid MaxiCode structured append position: " + position); |
} |
this.structuredAppendPosition = position; |
} |
/** |
* Returns the position of this MaxiCode symbol in a series of symbols using structured append. |
* If this symbol is not part of such a series, this method will return <code>1</code>. |
* |
* @return the position of this MaxiCode symbol in a series of symbols using structured append |
*/ |
public int getStructuredAppendPosition() { |
return this.structuredAppendPosition; |
} |
/** |
* If this MaxiCode symbol is part of a series of MaxiCode symbols appended in a structured |
* format, this method sets the total number of symbols in the series. Valid values are 1 |
* through 8 inclusive. A value of 1 indicates that this symbol is not part of a structured |
* append series. |
* |
* @param total the total number of MaxiCode symbols in the structured append series |
*/ |
public void setStructuredAppendTotal(final int total) { |
if (total < 1 || total > 8) { |
throw new IllegalArgumentException("Invalid MaxiCode structured append total: " + total); |
} |
this.structuredAppendTotal = total; |
} |
/** |
* Returns the size of the series of MaxiCode symbols using structured append that this symbol |
* is part of. If this symbol is not part of a structured append series, this method will return |
* <code>1</code>. |
* |
* @return size of the series that this symbol is part of |
*/ |
public int getStructuredAppendTotal() { |
return this.structuredAppendTotal; |
} |
/** |
* Sets the primary data. Should only be used for modes 2 and 3. Must conform to the following |
* structure: |
* |
* <table summary="Expected primary data structure."> |
* <tr> |
* <th>Characters</th> |
* <th>Meaning</th> |
* </tr> |
* <tr> |
* <td>1-9</td> |
* <td>Postal code data which can consist of up to 9 digits (for mode 2) or up to 6 alphanumeric |
* characters (for mode 3). Remaining unused characters should be filled with the SPACE |
* character (ASCII 32).</td> |
* </tr> |
* <tr> |
* <td>10-12</td> |
* <td>Three-digit country code according to ISO-3166.</td> |
* </tr> |
* <tr> |
* <td>13-15</td> |
* <td>Three digit service code. This depends on your parcel courier.</td> |
* </tr> |
* </table> |
* |
* @param primary the primary data |
*/ |
public void setPrimary(final String primary) { |
this.primaryData = primary; |
} |
/** |
* Returns the primary data for this MaxiCode symbol. Should only be used for modes 2 and 3. |
* |
* @return the primary data for this MaxiCode symbol |
*/ |
public String getPrimary() { |
return this.primaryData; |
} |
/** {@inheritDoc} */ |
@Override |
protected void encode() { |
eciProcess(); |
// mode 2 -> mode 3 if postal code isn't strictly numeric |
if (this.mode == 2) { |
for (int i = 0; i < 10 && i < this.primaryData.length(); i++) { |
if (this.primaryData.charAt(i) < '0' || this.primaryData.charAt(i) > '9') { |
this.mode = 3; |
break; |
} |
} |
} |
// initialize the set and character arrays |
processText(); |
// start building the codeword array, starting with a copy of the character data |
// insert primary message if this is a structured carrier message; insert mode otherwise |
this.codewords = Arrays.copyOf(this.character, this.character.length); |
if (this.mode == 2 || this.mode == 3) { |
final int[] primary = getPrimaryCodewords(); |
this.codewords = insertArray(this.codewords, 0, primary); |
} else { |
this.codewords = insertArray(this.codewords, 0, new int[] { this.mode }); |
} |
// insert structured append flag if necessary |
if (this.structuredAppendTotal > 1) { |
final int[] flag = new int[2]; |
flag[0] = 33; // padding |
flag[1] = this.structuredAppendPosition - 1 << 3 | this.structuredAppendTotal - 1; // position |
// + |
// total |
int index; |
if (this.mode == 2 || this.mode == 3) { |
index = 10; // first two data symbols in the secondary message |
} else { |
index = 1; // first two data symbols in the primary message (first symbol at index 0 |
// isn't a data symbol) |
} |
this.codewords = insertArray(this.codewords, index, flag); |
} |
int secondaryMax, secondaryECMax; |
if (this.mode == 5) { |
// 68 data codewords, 56 error corrections in secondary message |
secondaryMax = 68; |
secondaryECMax = 56; |
} else { |
// 84 data codewords, 40 error corrections in secondary message |
secondaryMax = 84; |
secondaryECMax = 40; |
} |
// truncate data codewords to maximum data space available |
final int totalMax = secondaryMax + 10; |
if (this.codewords.length > totalMax) { |
this.codewords = Arrays.copyOfRange(this.codewords, 0, totalMax); |
} |
// insert primary error correction between primary message and secondary message (always |
// EEC) |
final int[] primary = Arrays.copyOfRange(this.codewords, 0, 10); |
final int[] primaryCheck = getErrorCorrection(primary, 10); |
this.codewords = insertArray(this.codewords, 10, primaryCheck); |
// calculate secondary error correction |
final int[] secondary = Arrays.copyOfRange(this.codewords, 20, this.codewords.length); |
final int[] secondaryOdd = new int[secondary.length / 2]; |
final int[] secondaryEven = new int[secondary.length / 2]; |
for (int i = 0; i < secondary.length; i++) { |
if ((i & 1) != 0) { // odd |
secondaryOdd[(i - 1) / 2] = secondary[i]; |
} else { // even |
secondaryEven[i / 2] = secondary[i]; |
} |
} |
final int[] secondaryECOdd = getErrorCorrection(secondaryOdd, secondaryECMax / 2); |
final int[] secondaryECEven = getErrorCorrection(secondaryEven, secondaryECMax / 2); |
// add secondary error correction after secondary message |
this.codewords = Arrays.copyOf(this.codewords, this.codewords.length + secondaryECOdd.length + secondaryECEven.length); |
for (int i = 0; i < secondaryECOdd.length; i++) { |
this.codewords[20 + secondaryMax + 2 * i + 1] = secondaryECOdd[i]; |
} |
for (int i = 0; i < secondaryECEven.length; i++) { |
this.codewords[20 + secondaryMax + 2 * i] = secondaryECEven[i]; |
} |
infoLine("Mode: " + this.mode); |
infoLine("ECC Codewords: " + secondaryECMax); |
info("Codewords: "); |
for (int i = 0; i < this.codewords.length; i++) { |
infoSpace(this.codewords[i]); |
} |
infoLine(); |
// copy data into symbol grid |
final int[] bit_pattern = new int[7]; |
for (int i = 0; i < 33; i++) { |
for (int j = 0; j < 30; j++) { |
final int block = (MAXICODE_GRID[i * 30 + j] + 5) / 6; |
final int bit = (MAXICODE_GRID[i * 30 + j] + 5) % 6; |
if (block != 0) { |
bit_pattern[0] = (this.codewords[block - 1] & 0x20) >> 5; |
bit_pattern[1] = (this.codewords[block - 1] & 0x10) >> 4; |
bit_pattern[2] = (this.codewords[block - 1] & 0x8) >> 3; |
bit_pattern[3] = (this.codewords[block - 1] & 0x4) >> 2; |
bit_pattern[4] = (this.codewords[block - 1] & 0x2) >> 1; |
bit_pattern[5] = this.codewords[block - 1] & 0x1; |
if (bit_pattern[bit] != 0) { |
this.grid[i][j] = true; |
} else { |
this.grid[i][j] = false; |
} |
} |
} |
} |
// add orientation markings |
this.grid[0][28] = true; // top right filler |
this.grid[0][29] = true; |
this.grid[9][10] = true; // top left marker |
this.grid[9][11] = true; |
this.grid[10][11] = true; |
this.grid[15][7] = true; // left hand marker |
this.grid[16][8] = true; |
this.grid[16][20] = true; // right hand marker |
this.grid[17][20] = true; |
this.grid[22][10] = true; // bottom left marker |
this.grid[23][10] = true; |
this.grid[22][17] = true; // bottom right marker |
this.grid[23][17] = true; |
// the following is provided for compatibility, but the results are not useful |
this.row_count = 33; |
this.readable = ""; |
this.pattern = new String[33]; |
this.row_height = new int[33]; |
for (int i = 0; i < 33; i++) { |
final StringBuilder bin = new StringBuilder(30); |
for (int j = 0; j < 30; j++) { |
if (this.grid[i][j]) { |
bin.append("1"); |
} else { |
bin.append("0"); |
} |
} |
this.pattern[i] = bin2pat(bin); |
this.row_height[i] = 1; |
} |
this.symbol_height = 72; |
this.symbol_width = 74; |
} |
/** |
* Extracts the postal code, country code and service code from the primary data and returns the |
* corresponding primary message codewords. |
* |
* @return the primary message codewords |
*/ |
private int[] getPrimaryCodewords() { |
assert this.mode == 2 || this.mode == 3; |
if (this.primaryData.length() != 15) { |
throw new OkapiException("Invalid Primary String"); |
} |
for (int i = 9; i < 15; i++) { /* check that country code and service are numeric */ |
if (this.primaryData.charAt(i) < '0' || this.primaryData.charAt(i) > '9') { |
throw new OkapiException("Invalid Primary String"); |
} |
} |
String postcode; |
if (this.mode == 2) { |
postcode = this.primaryData.substring(0, 9); |
final int index = postcode.indexOf(' '); |
if (index != -1) { |
postcode = postcode.substring(0, index); |
} |
} else { |
assert this.mode == 3; |
postcode = this.primaryData.substring(0, 6); |
} |
final int country = Integer.parseInt(this.primaryData.substring(9, 12)); |
final int service = Integer.parseInt(this.primaryData.substring(12, 15)); |
infoLine("Postal Code: " + postcode); |
infoLine("Country Code: " + country); |
infoLine("Service: " + service); |
if (this.mode == 2) { |
return getMode2PrimaryCodewords(postcode, country, service); |
} else { |
assert this.mode == 3; |
return getMode3PrimaryCodewords(postcode, country, service); |
} |
} |
/** |
* Returns the primary message codewords for mode 2. |
* |
* @param postcode the postal code |
* @param country the country code |
* @param service the service code |
* @return the primary message, as codewords |
*/ |
private static int[] getMode2PrimaryCodewords(String postcode, final int country, final int service) { |
for (int i = 0; i < postcode.length(); i++) { |
if (postcode.charAt(i) < '0' || postcode.charAt(i) > '9') { |
postcode = postcode.substring(0, i); |
break; |
} |
} |
final int postcodeNum = Integer.parseInt(postcode); |
final int[] primary = new int[10]; |
primary[0] = (postcodeNum & 0x03) << 4 | 2; |
primary[1] = (postcodeNum & 0xfc) >> 2; |
primary[2] = (postcodeNum & 0x3f00) >> 8; |
primary[3] = (postcodeNum & 0xfc000) >> 14; |
primary[4] = (postcodeNum & 0x3f00000) >> 20; |
primary[5] = (postcodeNum & 0x3c000000) >> 26 | (postcode.length() & 0x3) << 4; |
primary[6] = (postcode.length() & 0x3c) >> 2 | (country & 0x3) << 4; |
primary[7] = (country & 0xfc) >> 2; |
primary[8] = (country & 0x300) >> 8 | (service & 0xf) << 2; |
primary[9] = (service & 0x3f0) >> 4; |
return primary; |
} |
/** |
* Returns the primary message codewords for mode 3. |
* |
* @param postcode the postal code |
* @param country the country code |
* @param service the service code |
* @return the primary message, as codewords |
*/ |
private static int[] getMode3PrimaryCodewords(String postcode, final int country, final int service) { |
final int[] postcodeNums = new int[postcode.length()]; |
postcode = postcode.toUpperCase(); |
for (int i = 0; i < postcodeNums.length; i++) { |
postcodeNums[i] = postcode.charAt(i); |
if (postcode.charAt(i) >= 'A' && postcode.charAt(i) <= 'Z') { |
// (Capital) letters shifted to Code Set A values |
postcodeNums[i] -= 64; |
} |
if (postcodeNums[i] == 27 || postcodeNums[i] == 31 || postcodeNums[i] == 33 || postcodeNums[i] >= 59) { |
// Not a valid postal code character, use space instead |
postcodeNums[i] = 32; |
} |
// Input characters lower than 27 (NUL - SUB) in postal code are interpreted as capital |
// letters in Code Set A (e.g. LF becomes 'J') |
} |
final int[] primary = new int[10]; |
primary[0] = (postcodeNums[5] & 0x03) << 4 | 3; |
primary[1] = (postcodeNums[4] & 0x03) << 4 | (postcodeNums[5] & 0x3c) >> 2; |
primary[2] = (postcodeNums[3] & 0x03) << 4 | (postcodeNums[4] & 0x3c) >> 2; |
primary[3] = (postcodeNums[2] & 0x03) << 4 | (postcodeNums[3] & 0x3c) >> 2; |
primary[4] = (postcodeNums[1] & 0x03) << 4 | (postcodeNums[2] & 0x3c) >> 2; |
primary[5] = (postcodeNums[0] & 0x03) << 4 | (postcodeNums[1] & 0x3c) >> 2; |
primary[6] = (postcodeNums[0] & 0x3c) >> 2 | (country & 0x3) << 4; |
primary[7] = (country & 0xfc) >> 2; |
primary[8] = (country & 0x300) >> 8 | (service & 0xf) << 2; |
primary[9] = (service & 0x3f0) >> 4; |
return primary; |
} |
/** |
* Formats text according to Appendix A, populating the {@link #set} and {@link #character} |
* arrays. |
* |
* @return true if the content fits in this symbol and was formatted; false otherwise |
*/ |
private void processText() { |
int length = this.content.length(); |
int i, j, count, current_set; |
if (length > 138) { |
throw new OkapiException("Input data too long"); |
} |
for (i = 0; i < 144; i++) { |
this.set[i] = -1; |
this.character[i] = 0; |
} |
for (i = 0; i < length; i++) { |
/* |
* Look up characters in table from Appendix A - this gives value and code set for most |
* characters |
*/ |
this.set[i] = MAXICODE_SET[this.inputData[i]]; |
this.character[i] = MAXICODE_SYMBOL_CHAR[this.inputData[i]]; |
} |
// If a character can be represented in more than one code set, pick which version to use. |
if (this.set[0] == 0) { |
if (this.character[0] == 13) { |
this.character[0] = 0; |
} |
this.set[0] = 1; |
} |
for (i = 1; i < length; i++) { |
if (this.set[i] == 0) { |
/* Special character that can be represented in more than one code set. */ |
if (this.character[i] == 13) { |
/* Carriage Return */ |
this.set[i] = bestSurroundingSet(i, length, 1, 5); |
if (this.set[i] == 5) { |
this.character[i] = 13; |
} else { |
this.character[i] = 0; |
} |
} else if (this.character[i] == 28) { |
/* FS */ |
this.set[i] = bestSurroundingSet(i, length, 1, 2, 3, 4, 5); |
if (this.set[i] == 5) { |
this.character[i] = 32; |
} |
} else if (this.character[i] == 29) { |
/* GS */ |
this.set[i] = bestSurroundingSet(i, length, 1, 2, 3, 4, 5); |
if (this.set[i] == 5) { |
this.character[i] = 33; |
} |
} else if (this.character[i] == 30) { |
/* RS */ |
this.set[i] = bestSurroundingSet(i, length, 1, 2, 3, 4, 5); |
if (this.set[i] == 5) { |
this.character[i] = 34; |
} |
} else if (this.character[i] == 32) { |
/* Space */ |
this.set[i] = bestSurroundingSet(i, length, 1, 2, 3, 4, 5); |
if (this.set[i] == 1) { |
this.character[i] = 32; |
} else if (this.set[i] == 2) { |
this.character[i] = 47; |
} else { |
this.character[i] = 59; |
} |
} else if (this.character[i] == 44) { |
/* Comma */ |
this.set[i] = bestSurroundingSet(i, length, 1, 2); |
if (this.set[i] == 2) { |
this.character[i] = 48; |
} |
} else if (this.character[i] == 46) { |
/* Full Stop */ |
this.set[i] = bestSurroundingSet(i, length, 1, 2); |
if (this.set[i] == 2) { |
this.character[i] = 49; |
} |
} else if (this.character[i] == 47) { |
/* Slash */ |
this.set[i] = bestSurroundingSet(i, length, 1, 2); |
if (this.set[i] == 2) { |
this.character[i] = 50; |
} |
} else if (this.character[i] == 58) { |
/* Colon */ |
this.set[i] = bestSurroundingSet(i, length, 1, 2); |
if (this.set[i] == 2) { |
this.character[i] = 51; |
} |
} |
} |
} |
for (i = length; i < this.set.length; i++) { |
/* Add the padding */ |
if (this.set[length - 1] == 2) { |
this.set[i] = 2; |
} else { |
this.set[i] = 1; |
} |
this.character[i] = 33; |
} |
/* |
* Find candidates for number compression (not allowed in primary message in modes 2 and 3). |
*/ |
if (this.mode == 2 || this.mode == 3) { |
j = 9; |
} else { |
j = 0; |
} |
count = 0; |
for (i = j; i < 143; i++) { |
if (this.set[i] == 1 && this.character[i] >= 48 && this.character[i] <= 57) { |
/* Character is a number */ |
count++; |
} else { |
count = 0; |
} |
if (count == 9) { |
/* Nine digits in a row can be compressed */ |
this.set[i] = 6; |
this.set[i - 1] = 6; |
this.set[i - 2] = 6; |
this.set[i - 3] = 6; |
this.set[i - 4] = 6; |
this.set[i - 5] = 6; |
this.set[i - 6] = 6; |
this.set[i - 7] = 6; |
this.set[i - 8] = 6; |
count = 0; |
} |
} |
/* Add shift and latch characters */ |
current_set = 1; |
i = 0; |
do { |
if (this.set[i] != current_set && this.set[i] != 6) { |
switch (this.set[i]) { |
case 1: |
if (i + 1 < this.set.length && this.set[i + 1] == 1) { |
if (i + 2 < this.set.length && this.set[i + 2] == 1) { |
if (i + 3 < this.set.length && this.set[i + 3] == 1) { |
/* Latch A */ |
insert(i, 63); |
current_set = 1; |
length++; |
i += 3; |
} else { |
/* 3 Shift A */ |
insert(i, 57); |
length++; |
i += 2; |
} |
} else { |
/* 2 Shift A */ |
insert(i, 56); |
length++; |
i++; |
} |
} else { |
/* Shift A */ |
insert(i, 59); |
length++; |
} |
break; |
case 2: |
if (i + 1 < this.set.length && this.set[i + 1] == 2) { |
/* Latch B */ |
insert(i, 63); |
current_set = 2; |
length++; |
i++; |
} else { |
/* Shift B */ |
insert(i, 59); |
length++; |
} |