OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Compare Revisions

Regard whitespace Rev 180 → Rev 181

/trunk/Modules/Module Badge/src/org/openconcerto/modules/badge/Module.java
31,7 → 31,7
import org.openconcerto.erp.core.common.element.AdresseSQLElement;
import org.openconcerto.erp.core.common.element.ComptaSQLConfElement;
import org.openconcerto.erp.core.common.ui.ListeViewPanel;
import org.openconcerto.erp.core.customerrelationship.customer.element.ContactSQLElement;
import org.openconcerto.erp.core.customerrelationship.customer.element.ComptaContactSQLElement;
import org.openconcerto.erp.core.customerrelationship.customer.element.CustomerSQLElement;
import org.openconcerto.erp.modules.AbstractModule;
import org.openconcerto.erp.modules.ComponentsContext;
166,6 → 166,7
 
createTable.addForeignColumn("ID_ADRESSE", addrElem.getTable());
createTable.addForeignColumn("ID_PLAGE_HORAIRE", new SQLName("PLAGE_HORAIRE"), SQLSyntax.ID_NAME, null);
ctxt.executeSQL();
}
// at least v1.0
 
240,7 → 241,7
vals.put("CODE", code);
newClient = vals.insert();
}
final SQLRowValues contactVals = new SQLRowValues(ctxt.getElementDirectory().getElement(ContactSQLElement.class).getTable());
final SQLRowValues contactVals = new SQLRowValues(ctxt.getElementDirectory().getElement(ComptaContactSQLElement.class).getTable());
contactVals.putForeignID("ID_CLIENT", newClient);
contactVals.load(adhR.asRow(), Arrays.asList("NOM", "DATE_NAISSANCE"));
contactVals.put("PRENOM", firstName);
/trunk/Modules/Module Lead/src/org/openconcerto/modules/customerrelationship/lead/visit/CustomerVisitSQLElement.java
28,6 → 28,10
final List<String> l = new ArrayList<String>();
l.add("DATE");
l.add("ID_CLIENT");
l.add("INFORMATION");
if (getTable().contains("ID_COMMERCIAL")) {
l.add("ID_COMMERCIAL");
}
l.add("NEXTCONTACT_DATE");
return l;
}
/trunk/Modules/Module Lead/src/org/openconcerto/modules/customerrelationship/lead/visit/LeadActionItemTable.java
New file
0,0 → 1,106
package org.openconcerto.modules.customerrelationship.lead.visit;
 
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.io.File;
import java.sql.Timestamp;
import java.util.Date;
import java.util.List;
import java.util.Vector;
 
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ToolTipManager;
 
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.model.SQLBackgroundTableCache;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.users.UserManager;
import org.openconcerto.sql.view.list.RowValuesTable;
import org.openconcerto.sql.view.list.RowValuesTableControlPanel;
import org.openconcerto.sql.view.list.RowValuesTableModel;
import org.openconcerto.sql.view.list.RowValuesTableRenderer;
import org.openconcerto.sql.view.list.SQLTableElement;
import org.openconcerto.ui.table.TimestampTableCellEditor;
 
public class LeadActionItemTable extends JPanel {
 
private RowValuesTable table;
final RowValuesTableControlPanel comp;
 
public LeadActionItemTable(SQLElement elt) {
this.setOpaque(false);
this.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.gridwidth = 1;
c.gridheight = 1;
c.gridx = 0;
c.gridy = 0;
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1;
c.weighty = 0;
 
List<SQLTableElement> list = new Vector<SQLTableElement>();
 
SQLTableElement tableElementDateL = new SQLTableElement(elt.getTable().getField("DATE"), Timestamp.class, new TimestampTableCellEditor());
list.add(tableElementDateL);
 
SQLTableElement tableElementTps = new SQLTableElement(elt.getTable().getField("INFORMATION"));
list.add(tableElementTps);
 
if (elt.getTable().contains("ID_COMMERCIAL")) {
SQLTableElement tableElementCom = new SQLTableElement(elt.getTable().getField("ID_COMMERCIAL"));
list.add(tableElementCom);
}
 
SQLRowValues rowValsDefault = new SQLRowValues(elt.getTable());
rowValsDefault.put("DATE", new Date());
if (elt.getTable().contains("ID_COMMERCIAL")) {
final int idUser = UserManager.getInstance().getCurrentUser().getId();
final SQLElement eltComm = elt.getForeignElement("ID_COMMERCIAL");
SQLRow rowsComm = SQLBackgroundTableCache.getInstance().getCacheForTable(eltComm.getTable()).getFirstRowContains(idUser, eltComm.getTable().getField("ID_USER_COMMON"));
 
if (rowsComm != null) {
rowValsDefault.put("ID_COMMERCIAL", rowsComm.getID());
}
}
final RowValuesTableModel model = new RowValuesTableModel(elt, list, elt.getTable().getField("DATE"), false, rowValsDefault);
 
this.table = new RowValuesTable(model, new File(Configuration.getInstance().getConfDir(), "Table" + File.separator + "Table_LeadActionItemTable.xml"));
ToolTipManager.sharedInstance().unregisterComponent(this.table);
ToolTipManager.sharedInstance().unregisterComponent(this.table.getTableHeader());
this.comp = new RowValuesTableControlPanel(this.table);
this.add(this.comp, c);
 
c.gridy++;
c.fill = GridBagConstraints.BOTH;
c.weightx = 1;
c.weighty = 1;
this.add(new JScrollPane(this.table), c);
this.table.setDefaultRenderer(Long.class, new RowValuesTableRenderer());
}
 
public void updateField(String field, int id) {
this.table.updateField(field, id);
}
 
public void insertFrom(String field, SQLRowValues row) {
this.table.insertFrom(field, row);
}
 
public void insertFrom(String field, int id) {
this.table.insertFrom(field, id);
}
 
public RowValuesTableModel getModel() {
return this.table.getRowValuesTableModel();
}
 
public void setEditable(boolean b) {
this.comp.setEditable(b);
this.table.setEditable(b);
}
 
}
/trunk/Modules/Module Lead/src/org/openconcerto/modules/customerrelationship/lead/visit/LeadVisitSQLElement.java
28,6 → 28,11
final List<String> l = new ArrayList<String>();
l.add("DATE");
l.add("ID_LEAD");
l.add("INFORMATION");
if (getTable().contains("ID_COMMERCIAL")) {
l.add("ID_COMMERCIAL");
}
 
l.add("NEXTCONTACT_DATE");
return l;
}
/trunk/Modules/Module Lead/src/org/openconcerto/modules/customerrelationship/lead/importer/LeadImporter.java
19,10 → 19,15
import org.openconcerto.openoffice.spreadsheet.Sheet;
import org.openconcerto.openoffice.spreadsheet.SpreadSheet;
import org.openconcerto.sql.model.DBRoot;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowListRSH;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLRowValuesCluster;
import org.openconcerto.sql.model.SQLRowValuesCluster.StoreMode;
import org.openconcerto.sql.model.SQLRowValuesListFetcher;
import org.openconcerto.sql.model.SQLRowValuesCluster.StoreMode;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.utils.cc.IdentityHashSet;
import org.openconcerto.utils.text.CSVReader;
import org.openconcerto.utils.text.CharsetHelper;
76,6 → 81,82
public abstract T convert(String[] line);
}
 
public Map<Object, LeadCSV> exportLeads(SQLTable tableLead, File dir2save, File sheetFile) throws Exception {
List<String[]> adresse = new ArrayList<String[]>();
List<String[]> leadList = new ArrayList<String[]>();
 
Map<Object, LeadCSV> leadMap = new HashMap<Object, LeadCSV>();
 
SQLSelect sel = new SQLSelect();
sel.addSelectStar(tableLead);
List<SQLRow> leads = SQLRowListRSH.execute(sel);
int i = 1;
for (SQLRow lead : leads) {
 
int idAdr = adresse.size();
AdresseCSV adr = createAdresse(i, lead.getForeign("ID_ADRESSE"));
adresse.add(adr.toCSVLine());
LeadCSV leadCsv = createLeadFromRow(i, lead, Integer.valueOf(adr.getId().toString()));
leadList.add(leadCsv.toCSVLine());
leadMap.put(leadCsv.getId(), leadCsv);
i++;
}
 
DataImporter importer = new DataImporter(tableLead);
final File csvFile = new File(dir2save, "Lead.csv");
csvFile.createNewFile();
importer.exportModelToCSV(csvFile, leadList);
DataImporter importerAdr = new DataImporter(tableLead.getForeignTable("ID_ADRESSE"));
final File csvFile2 = new File(dir2save, "Address.csv");
csvFile2.createNewFile();
importerAdr.exportModelToCSV(csvFile2, adresse);
 
return leadMap;
}
 
public AdresseCSV createAdresse(int i, SQLRow rowAdr) {
 
String street = rowAdr.getString("RUE");
final String ville = rowAdr.getString("VILLE");
final String cp = rowAdr.getString("CODE_POSTAL");
 
AdresseCSV adrLine = new AdresseCSV(i, street, ville, cp);
 
return adrLine;
}
 
public LeadCSV createLeadFromRow(int i, SQLRowAccessor row, int idAdr) {
 
LeadCSV leadLine = new LeadCSV(i, row.getString("COMPANY"), "");
 
leadLine.setIdAdr(idAdr);
 
leadLine.setPhone(row.getString("PHONE"));
leadLine.setMail(row.getString("EMAIL"));
leadLine.setCell(row.getString("MOBILE"));
leadLine.setFax(row.getString("FAX"));
leadLine.setContact(row.getString("NAME"));
leadLine.setLocalisation(row.getString("LOCALISATION"));
leadLine.setSecteur(row.getString("INDUSTRY"));
leadLine.setEffectif(String.valueOf(row.getInt("EMPLOYEES")));
leadLine.setOrigine(row.getString("SOURCE"));
 
leadLine.setSiret(row.getString("SIRET"));
leadLine.setApe(row.getString("APE"));
 
leadLine.setNom(row.getString("NAME"));
leadLine.setPrenom(row.getString("FIRSTNAME"));
leadLine.setDesc(row.getString("INFORMATION"));
// rowVals.put("REVENUE", (getBudget().trim().length()==0?0:Integer);
leadLine.setDispo(row.getString("DISPO"));
leadLine.setTypeT(row.getString("INDUSTRY"));
leadLine.setStatut(row.getString("STATUS"));
leadLine.setInfos(row.getString("INFOS"));
 
return leadLine;
 
}
 
public LeadCSV createLead(int i, Sheet sheet, int idAdr, int id) {
final Cell<SpreadSheet> cell0 = sheet.getImmutableCellAt(0, i);
final String societeName = cell0.getValue().toString().trim();
/trunk/Modules/Module Lead/src/org/openconcerto/modules/customerrelationship/lead/LeadGroup.java
1,6 → 1,5
package org.openconcerto.modules.customerrelationship.lead;
 
import org.openconcerto.sql.users.UserManager;
import org.openconcerto.sql.users.rights.UserRights;
import org.openconcerto.sql.users.rights.UserRightsManager;
import org.openconcerto.ui.group.Group;
11,52 → 10,59
public LeadGroup() {
super("customerrelationship.lead.default");
final Group g = new Group("customerrelationship.lead.identifier");
g.addItem("NUMBER");
g.addItem("DATE");
g.addItem("NUMBER", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
g.addItem("DATE", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
g.addItem("COMPANY", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
this.add(g);
 
final Group gContact = new Group("customerrelationship.lead.person", LayoutHints.DEFAULT_SEPARATED_GROUP_HINTS);
gContact.addItem("NAME");
gContact.addItem("FIRSTNAME");
gContact.addItem("ID_TITRE_PERSONNEL");
gContact.addItem("ID_TITRE_PERSONNEL", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gContact.addItem("NAME", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gContact.addItem("FIRSTNAME", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
this.add(gContact);
 
final Group gCustomer = new Group("customerrelationship.lead.contact", LayoutHints.DEFAULT_SEPARATED_GROUP_HINTS);
gCustomer.addItem("ROLE", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gCustomer.addItem("PHONE");
gCustomer.addItem("MOBILE");
gCustomer.addItem("FAX");
gCustomer.addItem("PHONE", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gCustomer.addItem("MOBILE", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gCustomer.addItem("FAX", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gCustomer.addItem("EMAIL", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gCustomer.addItem("WEBSITE", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
this.add(gCustomer);
 
final Group gAddress = new Group("customerrelationship.lead.address", LayoutHints.DEFAULT_SEPARATED_GROUP_HINTS);
gAddress.addItem("ID_ADRESSE");
gAddress.addItem("ID_ADRESSE", LayoutHints.DEFAULT_VERY_LARGE_FIELD_HINTS);
this.add(gAddress);
 
final Group gInfos = new Group("customerrelationship.lead.info");
gInfos.addItem("INFORMATION", new LayoutHints(true, true, true, true, true, true));
gInfos.addItem("INDUSTRY");
gInfos.addItem("REVENUE");
gInfos.addItem("EMPLOYEES");
gInfos.addItem("INFOS", new LayoutHints(true, true, true, true, true, true));
final Group gInfos = new Group("customerrelationship.lead.info", LayoutHints.DEFAULT_SEPARATED_GROUP_HINTS);
gInfos.addItem("INFORMATION", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gInfos.addItem("INDUSTRY", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gInfos.addItem("REVENUE", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gInfos.addItem("EMPLOYEES", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gInfos.addItem("INFOS", new LayoutHints(true, true, true, true, true, true, true, true));
this.add(gInfos);
 
final Group gState = new Group("customerrelationship.lead.state");
gState.addItem("RATING");
gState.addItem("SOURCE");
gState.addItem("STATUS");
gState.addItem("ID_COMMERCIAL");
gState.addItem("REMIND_DATE");
final Group gState = new Group("customerrelationship.lead.state", LayoutHints.DEFAULT_SEPARATED_GROUP_HINTS);
gState.addItem("RATING", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gState.addItem("SOURCE", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gState.addItem("STATUS", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gState.addItem("ID_COMMERCIAL", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gState.addItem("REMIND_DATE", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
UserRights rights = UserRightsManager.getCurrentUserRights();
if (rights.haveRight("CLIENT_PROSPECT")) {
gState.addItem("ID_CLIENT");
gState.addItem("ID_CLIENT", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
}
gState.addItem("DISPO");
 
gState.addItem("DISPO", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
this.add(gState);
 
final Group gItems = new Group("customerrelationship.lead.items.call.tab");
gItems.addItem("customerrelationship.lead.items.call", LayoutHints.DEFAULT_VERY_LARGE_TEXT_HINTS);
this.add(gItems);
 
final Group gItems2 = new Group("customerrelationship.lead.items.visit.tab");
gItems2.addItem("customerrelationship.lead.items.visit", LayoutHints.DEFAULT_VERY_LARGE_TEXT_HINTS);
this.add(gItems2);
 
}
 
public static void main(String[] args) {
/trunk/Modules/Module Lead/src/org/openconcerto/modules/customerrelationship/lead/call/CustomerCallSQLElement.java
15,6 → 15,7
import org.openconcerto.sql.element.SQLComponent;
import org.openconcerto.ui.JDate;
import org.openconcerto.ui.JLabelBold;
import org.openconcerto.ui.component.ITextArea;
 
public class CustomerCallSQLElement extends ModuleElement {
 
27,6 → 28,11
final List<String> l = new ArrayList<String>();
l.add("DATE");
l.add("ID_CLIENT");
l.add("INFORMATION");
if (getTable().contains("ID_COMMERCIAL")) {
l.add("ID_COMMERCIAL");
}
return l;
}
 
54,10 → 60,8
@Override
public JComponent createEditor(String id) {
if (id.equals("INFORMATION")) {
final JTextArea jTextArea = new JTextArea();
jTextArea.setFont(new JLabel().getFont());
jTextArea.setMinimumSize(new Dimension(200, 150));
jTextArea.setPreferredSize(new Dimension(200, 150));
final ITextArea jTextArea = new ITextArea();
jTextArea.setRows(20);
return new JScrollPane(jTextArea);
} else if (id.equals("DATE")) {
return new JDate(true);
/trunk/Modules/Module Lead/src/org/openconcerto/modules/customerrelationship/lead/call/LeadCallSQLElement.java
7,7 → 7,6
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
 
import org.openconcerto.erp.modules.AbstractModule;
import org.openconcerto.erp.modules.ModuleElement;
15,6 → 14,7
import org.openconcerto.sql.element.SQLComponent;
import org.openconcerto.ui.JDate;
import org.openconcerto.ui.JLabelBold;
import org.openconcerto.ui.component.ITextArea;
 
public class LeadCallSQLElement extends ModuleElement {
 
27,6 → 27,11
final List<String> l = new ArrayList<String>();
l.add("DATE");
l.add("ID_LEAD");
l.add("INFORMATION");
if (getTable().contains("ID_COMMERCIAL")) {
l.add("ID_COMMERCIAL");
}
 
return l;
}
 
54,10 → 59,8
@Override
public JComponent createEditor(String id) {
if (id.equals("INFORMATION")) {
final JTextArea jTextArea = new JTextArea();
jTextArea.setFont(new JLabel().getFont());
jTextArea.setMinimumSize(new Dimension(200, 150));
jTextArea.setPreferredSize(new Dimension(200, 150));
final ITextArea jTextArea = new ITextArea();
jTextArea.setRows(20);
return new JScrollPane(jTextArea);
} else if (id.equals("DATE")) {
return new JDate(true);
/trunk/Modules/Module Lead/src/org/openconcerto/modules/customerrelationship/lead/LeadSQLElement.java
4,6 → 4,7
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Set;
54,6 → 55,38
super(module, Module.TABLE_LEAD);
 
// Call
final RowAction.PredicateRowAction addDuplicateAction = new RowAction.PredicateRowAction(new AbstractAction("Créer à partir de") {
 
@Override
public void actionPerformed(ActionEvent e) {
SQLRow sRow = IListe.get(e).getSelectedRow().asRow();
final SQLTable table = LeadSQLElement.this.getTable().getTable(Module.TABLE_LEAD);
final SQLElement leadElt = LeadSQLElement.this.getDirectory().getElement(table);
EditFrame editFrame = new EditFrame(leadElt);
final SQLRowValues sqlRowValues = new SQLRowValues(table);
sqlRowValues.put("COMPANY", sRow.getObject("COMPANY"));
sqlRowValues.put("PHONE", sRow.getObject("PHONE"));
sqlRowValues.put("FAX", sRow.getObject("FAX"));
sqlRowValues.put("WEBSITE", sRow.getObject("WEBSITE"));
sqlRowValues.put("DATE", new Date());
SQLRowValues adr = new SQLRowValues(sRow.getForeign("ID_ADRESSE").asRowValues());
sqlRowValues.put("ID_ADRESSE", adr);
sqlRowValues.put("INDUSTRY", sRow.getObject("INDUSTRY"));
sqlRowValues.put("REVENUE", sRow.getObject("REVENUE"));
sqlRowValues.put("EMPLOYEES", sRow.getObject("EMPLOYEES"));
sqlRowValues.put("LOCALISATION", sRow.getObject("LOCALISATION"));
sqlRowValues.put("SIRET", sRow.getObject("SIRET"));
sqlRowValues.put("APE", sRow.getObject("APE"));
 
editFrame.getSQLComponent().select(sqlRowValues);
FrameUtil.show(editFrame);
}
}, true) {
};
addDuplicateAction.setPredicate(IListeEvent.getSingleSelectionPredicate());
getRowActions().add(addDuplicateAction);
 
// Call
final RowAction.PredicateRowAction addCallAction = new RowAction.PredicateRowAction(new AbstractAction("Appeler") {
 
@Override
121,6 → 154,79
}
}
 
if (getTable().contains("MODIFICATION_DATE")) {
BaseSQLTableModelColumn dateM = new BaseSQLTableModelColumn("Date de modification", Date.class) {
 
@Override
protected Object show_(SQLRowAccessor r) {
return r.getObject("MODIFICATION_DATE");
}
 
@Override
public Set<FieldPath> getPaths() {
Path p = new Path(getTable());
return CollectionUtils.createSet(new FieldPath(p, "MODIFICATION_DATE"));
}
};
source.getColumns().add(1, dateM);
}
 
BaseSQLTableModelColumn dateV = new BaseSQLTableModelColumn("Visite", Date.class) {
 
@Override
protected Object show_(SQLRowAccessor r) {
Date d = null;
Collection<? extends SQLRowAccessor> l = r.getReferentRows(r.getTable().getTable("LEAD_VISIT"));
for (SQLRowAccessor sqlRowAccessor : l) {
if (d != null && sqlRowAccessor.getObject("DATE") != null && d.before(sqlRowAccessor.getDate("DATE").getTime())) {
d = sqlRowAccessor.getDate("DATE").getTime();
} else {
if (d == null && sqlRowAccessor.getObject("DATE") != null) {
d = sqlRowAccessor.getDate("DATE").getTime();
}
}
 
}
return d;
}
 
@Override
public Set<FieldPath> getPaths() {
Path p = new Path(getTable());
p = p.add(p.getLast().getTable("LEAD_VISIT"));
return CollectionUtils.createSet(new FieldPath(p, "DATE"));
}
};
source.getColumns().add(1, dateV);
 
BaseSQLTableModelColumn dateA = new BaseSQLTableModelColumn("Appel", Date.class) {
 
@Override
protected Object show_(SQLRowAccessor r) {
Date d = null;
Collection<? extends SQLRowAccessor> l = r.getReferentRows(r.getTable().getTable("LEAD_CALL"));
for (SQLRowAccessor sqlRowAccessor : l) {
if (d != null && sqlRowAccessor.getObject("DATE") != null && d.before(sqlRowAccessor.getDate("DATE").getTime())) {
d = sqlRowAccessor.getDate("DATE").getTime();
} else {
if (d == null && sqlRowAccessor.getObject("DATE") != null) {
d = sqlRowAccessor.getDate("DATE").getTime();
}
}
 
}
return d;
}
 
@Override
public Set<FieldPath> getPaths() {
Path p = new Path(getTable());
p = p.add(p.getLast().getTable("LEAD_CALL"));
return CollectionUtils.createSet(new FieldPath(p, "DATE"));
}
};
source.getColumns().add(1, dateA);
 
BaseSQLTableModelColumn adresse = new BaseSQLTableModelColumn("Adresse", String.class) {
 
@Override
189,6 → 295,32
};
source.getColumns().add(ville);
 
BaseSQLTableModelColumn dpt = new BaseSQLTableModelColumn("Département", String.class) {
 
@Override
protected Object show_(SQLRowAccessor r) {
 
String s = r.getForeign("ID_ADRESSE").getString("CODE_POSTAL");
if (s != null && s.length() >= 2) {
return s.substring(0, 2);
} else {
return s;
}
 
}
 
@Override
public Set<FieldPath> getPaths() {
Path p = new Path(getTable());
final SQLTable clientT = getTable().getForeignTable("ID_CLIENT");
p = p.add(clientT);
p = p.add(clientT.getField("ID_ADRESSE"));
return CollectionUtils.createSet(new FieldPath(p, "VILLE"), new FieldPath(p, "CODE_POSTAL"));
}
};
 
source.getColumns().add(dpt);
 
if (getTable().contains("REMIND_DATE")) {
BaseSQLTableModelColumn dateRemind = new BaseSQLTableModelColumn("Date de rappel", Date.class) {
 
/trunk/Modules/Module Lead/src/org/openconcerto/modules/customerrelationship/lead/translation_fr.xml
New file
0,0 → 1,4
<translation lang="fr">
<item id="customerrelationship.lead.items.call.tab" label="Appels" />
<item id="customerrelationship.lead.items.visit.tab" label="Visites" />
</translation>
/trunk/Modules/Module Lead/src/org/openconcerto/modules/customerrelationship/lead/LeadCustomerSQLInjector.java
3,6 → 3,8
*/
package org.openconcerto.modules.customerrelationship.lead;
 
import java.util.Date;
 
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.model.SQLInjector;
import org.openconcerto.sql.model.SQLRowAccessor;
26,6 → 28,8
map(leadTable.getField("MOBILE"), customerTable.getField("TEL_P"));
// map(leadTable.getField("INFORMATION"), customerTable.getField("INFOS"));
map(getSource().getField("INFOS"), getDestination().getField("INFOS"));
remove(leadTable.getField("DATE"), customerTable.getField("DATE"));
mapDefaultValues(customerTable.getField("DATE"), new Date());
remove(leadTable.getField("ID_ADRESSE"), customerTable.getField("ID_ADRESSE"));
}
 
/trunk/Modules/Module Lead/src/org/openconcerto/modules/customerrelationship/lead/LeadSQLComponent.java
5,13 → 5,17
 
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JTextField;
 
import org.openconcerto.modules.customerrelationship.lead.visit.LeadActionItemTable;
import org.openconcerto.sql.element.GroupSQLComponent;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.model.SQLBackgroundTableCache;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.sqlobject.ElementComboBox;
import org.openconcerto.sql.sqlobject.SQLSearchableTextCombo;
import org.openconcerto.sql.users.UserManager;
import org.openconcerto.ui.JDate;
21,8 → 25,12
import org.openconcerto.ui.group.Group;
 
public class LeadSQLComponent extends GroupSQLComponent {
 
private LeadActionItemTable tableCall, tableVisit;
 
public LeadSQLComponent(SQLElement element, Group group) {
super(element, group);
startTabGroupAfter("customerrelationship.lead.state");
}
 
@Override
37,10 → 45,26
public JComponent getLabel(String id) {
if (id.equals("customerrelationship.lead.person")) {
return new JLabelBold("Contact");
}
if (id.equals("customerrelationship.lead.items.visit.tab")) {
return new JLabelBold("Visites");
}
if (id.equals("customerrelationship.lead.items.visit")) {
return new JLabelBold("");
}
if (id.equals("customerrelationship.lead.items.call.tab")) {
return new JLabelBold("Appels");
}
if (id.equals("customerrelationship.lead.items.call")) {
return new JLabelBold("");
} else if (id.equals("customerrelationship.lead.contact")) {
return new JLabel();
} else if (id.equals("customerrelationship.lead.address")) {
return new JLabelBold("Adresse");
} else if (id.equals("customerrelationship.lead.info")) {
return new JLabelBold("Infos");
} else if (id.equals("customerrelationship.lead.state")) {
return new JLabelBold("Statut");
} else {
return super.getLabel(id);
}
48,20 → 72,61
 
@Override
public JComponent createEditor(String id) {
 
if (id.equals("INFORMATION") || id.equals("INFOS")) {
final ITextArea jTextArea = new ITextArea();
final ITextArea jTextArea = new ITextArea(3,3);
jTextArea.setFont(new JLabel().getFont());
return jTextArea;
} else if (id.equals("ID_COMMERCIAL") || id.equals("ID_TITRE_PERSONNEL")) {
ElementComboBox comp = new ElementComboBox(false, 1);
((ElementComboBox) comp).init(getElement().getForeignElement(id));
return comp;
} else if (id.equals("customerrelationship.lead.items.call")) {
tableCall = new LeadActionItemTable(getElement().getDirectory().getElement(Module.TABLE_LEAD_CALL));
return tableCall;
} else if (id.equals("customerrelationship.lead.items.visit")) {
tableVisit = new LeadActionItemTable(getElement().getDirectory().getElement(Module.TABLE_LEAD_VISIT));
return tableVisit;
} else if (id.equals("INDUSTRY") || id.equals("STATUS") || id.equals("RATING") || id.equals("SOURCE") || id.equals("DISPO")) {
return new SQLSearchableTextCombo(ComboLockedMode.UNLOCKED, 1, 20, false);
return new SQLSearchableTextCombo(ComboLockedMode.UNLOCKED, 1, 1, false);
} else if (id.equals("DATE")) {
return new JDate(true);
}
return super.createEditor(id);
JComponent comp = super.createEditor(id);
if(comp.getClass() == JTextField.class) {
JTextField jtxt = new JTextField(10);
comp = jtxt;
}
 
return comp;
}
 
@Override
public int insert(SQLRow order) {
int id = super.insert(order);
this.tableCall.updateField("ID_LEAD", id);
this.tableVisit.updateField("ID_LEAD", id);
return id;
}
 
@Override
public void select(SQLRowAccessor r) {
super.select(r);
if (r != null) {
this.tableCall.insertFrom("ID_LEAD", r.getID());
this.tableVisit.insertFrom("ID_LEAD", r.getID());
}
}
 
@Override
public void update() {
super.update();
this.tableCall.updateField("ID_LEAD", getSelectedID());
this.tableVisit.updateField("ID_LEAD", getSelectedID());
}
 
@Override
protected SQLRowValues createDefaults() {
SQLRowValues rowVals = new SQLRowValues(getTable());
rowVals.put("STATUS", "Nouveau");
/trunk/Modules/Module Lead/src/org/openconcerto/modules/customerrelationship/lead/labels_fr.xml
38,6 → 38,7
name="rapport d'appel prospect" namePlural="rapports d'appel prospect">
<FIELD name="DATE" label="Date de l'appel" />
<FIELD name="ID_LEAD" label="Entreprise" />
<FIELD name="ID_COMMERCIAL" label="Commercial" />
<FIELD name="INFORMATION" label="Résumé de l'appel" />
<FIELD name="NEXTCONTACT_DATE" label="Date de prochain contact" />
<FIELD name="NEXTCONTACT_INFO" label="Motif de prochain contact" />
47,6 → 48,7
</element>
<element refid="org.openconcerto.modules.customerrelationship.lead/CUSTOMER_CALL" nameClass="masculine"
name="rapport d'appel client" namePlural="rapports d'appel client">
<FIELD name="ID_COMMERCIAL" label="Commercial" />
<FIELD name="DATE" label="Date de l'appel" />
<FIELD name="ID_CLIENT" label="Client" />
<FIELD name="INFORMATION" label="Résumé de l'appel" />
/trunk/Modules/Module Project/src/org/openconcerto/modules/project/Module.java
94,7 → 94,6
import org.openconcerto.utils.Tuple2;
import org.openconcerto.utils.cc.IExnClosure;
import org.openconcerto.utils.cc.ITransformer;
import org.openconcerto.utils.i18n.TranslationManager;
 
public final class Module extends AbstractModule {
 
191,7 → 190,6
@Override
protected void setupElements(final SQLElementDirectory dir) {
super.setupElements(dir);
TranslationManager.getInstance().addTranslationStreamFromClass(this.getClass());
dir.addSQLElement(ProjectSQLElement.class);
dir.addSQLElement(ProjectStateSQLElement.class);
dir.addSQLElement(ProjectTypeSQLElement.class);
607,7 → 605,7
protected void setupComponents(final ComponentsContext ctxt) {
 
DBRoot root = ComptaPropsConfiguration.getInstanceCompta().getRootSociete();
List<String> table2check = Arrays.asList("BON_RECEPTION", "DEMANDE_PRIX", "DEMANDE_ACHAT_ELEMENT");
List<String> table2check = Arrays.asList("BON_RECEPTION", "DEMANDE_PRIX", "DEMANDE_ACHAT_ELEMENT", "FACTURE_FOURNISSEUR");
for (String table : table2check) {
if (root.contains(table)) {
SQLTable tableCR = root.getTable(table);
/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;
41,11 → 43,13
}
};
 
final AbstractDocument comp1 = (AbstractDocument) TextComponentUtils.getDocument(getView("TRAVEL_DISTANCE").getComp());
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());
final AbstractDocument comp2 = (AbstractDocument) TextComponentUtils
.getDocument(getView("TRAVEL_RATE").getComp());
DocumentFilterList.add(comp2, new LimitedSizeDocumentFilter(5), FilterType.SIMPLE_FILTER);
 
getView("TRAVEL_RATE").addValueListener(listener);
62,17 → 66,33
}
 
@Override
public JComponent getEditor(String id) {
if (id.equals("DESCRIPTION")) {
return new ITextArea();
} else if (id.equals("DATE")) {
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.getEditor(id);
return super.createEditor(id);
}
 
@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 void updateAmount() {
float v1 = getFloat("TRAVEL_DISTANCE");
float v2 = getFloat("TRAVEL_RATE");
/trunk/Modules/Module Expense/src/org/openconcerto/modules/humanresources/travel/expense/ExpenseGroup.java
7,32 → 7,31
public class ExpenseGroup extends Group {
 
public ExpenseGroup() {
super(ExpenseSQLElement.ELEMENT_CODE + ".default");
super(ExpenseSQLElement.ELEMENT_CODE + ".default", LayoutHints.DEFAULT_SEPARATED_GROUP_HINTS);
final Group g = new Group(ExpenseSQLElement.ELEMENT_CODE + ".identifier");
g.addItem("DATE");
g.addItem("ID_USER_COMMON");
g.addItem("DATE", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
g.addItem("ID_USER_COMMON", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
 
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)));
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");
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");
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 Batch Processing/src/org/openconcerto/modules/common/batchprocessing/DateProcessor.java
17,8 → 17,8
private final JDate d = new JDate(true);
private final SQLField field;
 
public DateProcessor(SQLField field) {
this.field = field;
public DateProcessor(BatchField field) {
this.field = field.getField();
this.setLayout(new FlowLayout());
this.add(new JLabel("forcer la date au "));
this.add(d);
/trunk/Modules/Module Batch Processing/src/org/openconcerto/modules/common/batchprocessing/BooleanProcessor.java
13,13 → 13,13
import org.openconcerto.ui.VFlowLayout;
 
public class BooleanProcessor extends JPanel implements BatchProcessor {
private final SQLField field;
private final BatchField field;
 
private JRadioButton bTrue;
private JRadioButton bFalse;
private JRadioButton bInvert;
 
public BooleanProcessor(SQLField field) {
public BooleanProcessor(BatchField field) {
this.field = field;
this.setLayout(new VFlowLayout());
bTrue = new JRadioButton("forcer à vrai");
41,7 → 41,7
if (bTrue.isSelected()) {
for (SQLRowAccessor sqlRowAccessor : r) {
final SQLRowValues rowValues = sqlRowAccessor.createEmptyUpdateRow();
rowValues.put(field.getName(), Boolean.TRUE);
rowValues.put(field.getField().getName(), Boolean.TRUE);
processBeforeUpdate(sqlRowAccessor, rowValues);
rowValues.update();
}
48,7 → 48,7
} else if (bFalse.isSelected()) {
for (SQLRowAccessor sqlRowAccessor : r) {
final SQLRowValues rowValues = sqlRowAccessor.createEmptyUpdateRow();
rowValues.put(field.getName(), Boolean.FALSE);
rowValues.put(field.getField().getName(), Boolean.FALSE);
processBeforeUpdate(sqlRowAccessor, rowValues);
rowValues.update();
}
55,9 → 55,9
} else if (bInvert.isSelected()) {
for (SQLRowAccessor sqlRowAccessor : r) {
final SQLRowValues rowValues = sqlRowAccessor.createEmptyUpdateRow();
final Boolean boolean1 = sqlRowAccessor.asRow().getBoolean(field.getName());
final Boolean boolean1 = sqlRowAccessor.asRow().getBoolean(field.getField().getName());
if (boolean1 != null) {
rowValues.put(field.getName(), boolean1.equals(Boolean.FALSE));
rowValues.put(field.getField().getName(), boolean1.equals(Boolean.FALSE));
processBeforeUpdate(sqlRowAccessor, rowValues);
rowValues.update();
}
/trunk/Modules/Module Batch Processing/src/org/openconcerto/modules/common/batchprocessing/BatchDetailPanel.java
22,46 → 22,47
this.setLayout(new VFlowLayout());
}
 
public void setField(SQLField field) {
public void setField(BatchField batchField) {
this.removeAll();
 
SQLField field = batchField.getField();
final SQLType type = field.getType();
final Class<?> javaType = type.getJavaType();
final String fName = field.getName();
if (fName.equals("PV_TTC")) {
final NumberProcessor p = new TTCProcessor(field);
final NumberProcessor p = new TTCProcessor(batchField);
this.add(p);
this.processor = p;
} else if (fName.equals("PV_HT")) {
final NumberProcessor p = new HTProcessor(field);
final NumberProcessor p = new HTProcessor(batchField);
this.add(p);
this.processor = p;
} else if (fName.equals("ID_TAXE")) {
final ReferenceProcessor p = new TVAProcessor(field);
final ReferenceProcessor p = new TVAProcessor(batchField);
this.add(p);
this.processor = p;
} else if (fName.equals("PA_HT")) {
final NumberProcessor p = new PurchaseProcessor(field);
final NumberProcessor p = new PurchaseProcessor(batchField);
this.add(p);
this.processor = p;
} else if (javaType.equals(Boolean.class)) {
final BooleanProcessor p = new BooleanProcessor(field);
final BooleanProcessor p = new BooleanProcessor(batchField);
this.add(p);
this.processor = p;
} else if (field.isKey()) {
final ReferenceProcessor p = new ReferenceProcessor(field);
final ReferenceProcessor p = new ReferenceProcessor(batchField);
this.add(p);
this.processor = p;
} else if (javaType.equals(String.class)) {
final StringProcessor p = new StringProcessor(field);
final StringProcessor p = new StringProcessor(batchField);
this.add(p);
this.processor = p;
} else if (javaType.equals(Date.class)) {
final DateProcessor p = new DateProcessor(field);
final DateProcessor p = new DateProcessor(batchField);
this.add(p);
this.processor = p;
} else if (javaType.equals(BigDecimal.class) || javaType.equals(Float.class) || javaType.equals(Double.class) || javaType.equals(Integer.class) || javaType.equals(Long.class)) {
final NumberProcessor p = new NumberProcessor(field);
final NumberProcessor p = new NumberProcessor(batchField);
this.add(p);
this.processor = p;
}
/trunk/Modules/Module Batch Processing/src/org/openconcerto/modules/common/batchprocessing/BatchField.java
New file
0,0 → 1,58
package org.openconcerto.modules.common.batchprocessing;
 
import java.util.List;
 
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.element.SQLElementDirectory;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowListRSH;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.request.SQLFieldTranslator;
 
public class BatchField {
 
private final SQLField field;
private final SQLRowAccessor foreignLinkRow;
private final SQLFieldTranslator translator;
private final SQLElement elementLink;
 
public BatchField(SQLElementDirectory dir, SQLField field, SQLRowAccessor foreignLinkRow) {
this.field = field;
this.foreignLinkRow = foreignLinkRow;
 
this.translator = dir.getTranslator();
if (foreignLinkRow == null) {
this.elementLink = null;
} else {
this.elementLink = dir.getElement(foreignLinkRow.getTable());
}
}
 
public SQLField getField() {
return field;
}
 
public SQLRowAccessor getForeignLinkRow() {
return foreignLinkRow;
}
 
public String getComboName() {
if (this.foreignLinkRow == null) {
return this.translator.getLabelFor(this.field);
} else {
return this.elementLink.getPluralName() + " " + this.foreignLinkRow.getString("NOM") + " " + this.translator.getLabelFor(this.field);
}
}
 
public List<SQLRow> getReferentRows(SQLRowAccessor rowOrigin) {
SQLSelect sel = new SQLSelect();
sel.addSelectStar(this.field.getTable());
final Where w = new Where(this.field.getTable().getField("ID_" + rowOrigin.getTable().getName()), "=", rowOrigin.getID());
sel.setWhere(w.and(new Where(this.field.getTable().getField("ID_" + foreignLinkRow.getTable().getName()), "=", foreignLinkRow.getID())));
return SQLRowListRSH.execute(sel);
}
 
}
/trunk/Modules/Module Batch Processing/src/org/openconcerto/modules/common/batchprocessing/Module.java
2,7 → 2,6
 
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.IOException;
import java.util.List;
 
9,18 → 8,12
import javax.swing.AbstractAction;
import javax.swing.JFrame;
 
import org.openconcerto.erp.config.Gestion;
import org.openconcerto.erp.modules.AbstractModule;
import org.openconcerto.erp.modules.ComponentsContext;
import org.openconcerto.erp.modules.ModuleFactory;
import org.openconcerto.erp.modules.ModuleManager;
import org.openconcerto.erp.modules.ModulePackager;
import org.openconcerto.erp.modules.RuntimeModuleFactory;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRequestLog;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.ui.ConnexionPanel;
import org.openconcerto.sql.view.list.IListe;
import org.openconcerto.sql.view.list.IListeAction.IListeEvent;
import org.openconcerto.sql.view.list.RowAction;
33,7 → 26,7
}
 
@Override
protected void setupComponents(ComponentsContext ctxt) {
protected void setupComponents(final ComponentsContext ctxt) {
 
super.setupComponents(ctxt);
final SQLElement element = ctxt.getElement("ARTICLE");
60,7 → 53,7
 
};
 
f.setContentPane(new BatchEditorPanel(rows, filter));
f.setContentPane(new BatchEditorPanel(ctxt.getElement("ARTICLE").getDirectory(), rows, filter));
f.pack();
f.setMinimumSize(new Dimension(400, 300));
f.setLocationRelativeTo(IListe.get(e));
/trunk/Modules/Module Batch Processing/src/org/openconcerto/modules/common/batchprocessing/NumberProcessor.java
13,7 → 13,7
import javax.swing.JRadioButton;
import javax.swing.JTextField;
 
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.ui.DefaultGridBagConstraints;
20,7 → 20,7
 
public class NumberProcessor extends JPanel implements BatchProcessor {
 
private final SQLField field;
private final BatchField batchfield;
// Editors
final JTextField tReplace = new JTextField();
private JRadioButton bReplace;
30,8 → 30,8
final JTextField tRemove = new JTextField();
private JRadioButton bRemove;
 
public NumberProcessor(SQLField field) {
this.field = field;
public NumberProcessor(BatchField field) {
this.batchfield = field;
 
this.setLayout(new GridBagLayout());
bReplace = new JRadioButton("remplacer par");
110,11 → 110,23
if (bReplace.isSelected()) {
BigDecimal v = new BigDecimal(this.tReplace.getText().trim());
for (SQLRowAccessor sqlRowAccessor : r) {
 
if (batchfield.getForeignLinkRow() != null) {
 
final List<SQLRow> referentRow = batchfield.getReferentRows(sqlRowAccessor);
for (SQLRow sqlRowT : referentRow) {
SQLRowValues rowValues = sqlRowT.createEmptyUpdateRow();
rowValues.put(batchfield.getField().getName(), decimalToFieldType(v));
processBeforeUpdate(sqlRowT, rowValues);
rowValues.update();
}
} else {
final SQLRowValues rowValues = sqlRowAccessor.createEmptyUpdateRow();
rowValues.put(field.getName(), decimalToFieldType(v));
rowValues.put(batchfield.getField().getName(), decimalToFieldType(v));
processBeforeUpdate(sqlRowAccessor, rowValues);
rowValues.update();
}
}
} else if (bAdd.isSelected()) {
 
String t = this.tAdd.getText().trim();
127,18 → 139,44
BigDecimal v = new BigDecimal(t);
 
for (SQLRowAccessor sqlRowAccessor : r) {
final SQLRowValues rowValues = sqlRowAccessor.createEmptyUpdateRow();
final BigDecimal value = sqlRowAccessor.asRow().getBigDecimal(field.getName());
 
if (batchfield.getForeignLinkRow() != null) {
 
final List<SQLRow> referentRow = batchfield.getReferentRows(sqlRowAccessor);
for (SQLRow sqlRowT : referentRow) {
 
SQLRowValues rowValues = sqlRowT.createEmptyUpdateRow();
BigDecimal value = sqlRowT.asRow().getBigDecimal(batchfield.getField().getName());
 
if (value != null) {
if (isPercent) {
rowValues.put(field.getName(), decimalToFieldType(value.multiply(v.divide(new BigDecimal(100)).add(BigDecimal.ONE))));
rowValues.put(batchfield.getField().getName(), decimalToFieldType(value.multiply(v.divide(new BigDecimal(100)).add(BigDecimal.ONE))));
} else {
rowValues.put(field.getName(), decimalToFieldType(value.add(v)));
rowValues.put(batchfield.getField().getName(), decimalToFieldType(value.add(v)));
}
processBeforeUpdate(sqlRowT, rowValues);
rowValues.update();
}
}
} else {
 
final SQLRowValues rowValues;
final BigDecimal value;
 
rowValues = sqlRowAccessor.createEmptyUpdateRow();
value = sqlRowAccessor.asRow().getBigDecimal(batchfield.getField().getName());
 
if (value != null) {
if (isPercent) {
rowValues.put(batchfield.getField().getName(), decimalToFieldType(value.multiply(v.divide(new BigDecimal(100)).add(BigDecimal.ONE))));
} else {
rowValues.put(batchfield.getField().getName(), decimalToFieldType(value.add(v)));
}
processBeforeUpdate(sqlRowAccessor, rowValues);
rowValues.update();
}
}
}
} else if (bRemove.isSelected()) {
String t = this.tRemove.getText().trim();
boolean isPercent = false;
149,15 → 187,35
 
BigDecimal v = new BigDecimal(t);
for (SQLRowAccessor sqlRowAccessor : r) {
final SQLRowValues rowValues = sqlRowAccessor.createEmptyUpdateRow();
if (batchfield.getForeignLinkRow() != null) {
final List<SQLRow> referentRow = batchfield.getReferentRows(sqlRowAccessor);
if (referentRow != null && !referentRow.isEmpty()) {
for (SQLRow sqlRowT : referentRow) {
 
final BigDecimal value = sqlRowAccessor.asRow().getBigDecimal(field.getName());
SQLRowValues rowValues = sqlRowT.createEmptyUpdateRow();
final BigDecimal value = sqlRowT.getBigDecimal(batchfield.getField().getName());
if (value != null) {
if (isPercent) {
rowValues.put(field.getName(), decimalToFieldType(value.multiply(v.divide(new BigDecimal(-100)).add(BigDecimal.ONE))));
rowValues.put(batchfield.getField().getName(), decimalToFieldType(value.multiply(v.divide(new BigDecimal(-100)).add(BigDecimal.ONE))));
} else {
rowValues.put(field.getName(), decimalToFieldType(value.add(v)));
rowValues.put(batchfield.getField().getName(), decimalToFieldType(value.add(v)));
}
processBeforeUpdate(sqlRowT, rowValues);
rowValues.update();
}
}
}
} else {
 
SQLRowValues rowValues = sqlRowAccessor.createEmptyUpdateRow();
final BigDecimal value = sqlRowAccessor.asRow().getBigDecimal(batchfield.getField().getName());
 
if (value != null) {
if (isPercent) {
rowValues.put(batchfield.getField().getName(), decimalToFieldType(value.multiply(v.divide(new BigDecimal(-100)).add(BigDecimal.ONE))));
} else {
rowValues.put(batchfield.getField().getName(), decimalToFieldType(value.add(v)));
}
processBeforeUpdate(sqlRowAccessor, rowValues);
rowValues.update();
}
164,9 → 222,10
}
}
}
}
 
private Object decimalToFieldType(BigDecimal v) {
final Class<?> javaType = field.getType().getJavaType();
final Class<?> javaType = batchfield.getField().getType().getJavaType();
if (javaType.equals(BigDecimal.class)) {
return v;
} else if (javaType.equals(Float.class)) {
/trunk/Modules/Module 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 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/MicroQrCode.java
New file
0,0 → 1,1606
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
import static uk.org.okapibarcode.util.Arrays.positionOf;
 
import java.io.UnsupportedEncodingException;
 
/**
* Implements Micro QR Code According to ISO/IEC 18004:2006 <br>
* A miniature version of the QR Code symbol for short messages. QR Code symbols can encode
* characters in the Latin-1 set and Kanji characters which are members of the Shift-JIS encoding
* scheme.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class MicroQrCode extends Symbol {
 
public enum EccMode {
L, M, Q, H
}
 
private enum qrMode {
NULL, KANJI, BINARY, ALPHANUM, NUMERIC
}
 
/* Table 5 - Encoding/Decoding table for Alphanumeric mode */
private static final char[] RHODIUM = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
'V', 'W', 'X', 'Y', 'Z', ' ', '$', '%', '*', '+', '-', '.', '/', ':' };
 
private static final int[] QR_ANNEX_C1 = {
/* Micro QR Code format information */
0x4445, 0x4172, 0x4e2b, 0x4b1c, 0x55ae, 0x5099, 0x5fc0, 0x5af7, 0x6793, 0x62a4, 0x6dfd, 0x68ca, 0x7678, 0x734f, 0x7c16, 0x7921, 0x06de, 0x03e9, 0x0cb0, 0x0987, 0x1735, 0x1202, 0x1d5b,
0x186c, 0x2508, 0x203f, 0x2f66, 0x2a51, 0x34e3, 0x31d4, 0x3e8d, 0x3bba };
 
private static final int[] MICRO_QR_SIZES = { 11, 13, 15, 17 };
 
// user-specified values and settings
 
private int preferredVersion;
private EccMode preferredEccLevel = EccMode.L;
 
// internal state calculated when setContent() is called
 
private qrMode[] inputMode;
private StringBuilder binary;
private final int[] binaryCount = new int[4];
private int[] grid;
private int[] eval;
 
/**
* <p>
* Sets the preferred symbol size. This value may be ignored if the data string is too large to
* fit into the specified symbol. Input values correspond to symbol sizes as shown in the
* following table.
*
* <table summary="Range of Micro QR symbol sizes">
* <tbody>
* <tr>
* <th>Input</th>
* <th>Version</th>
* <th>Symbol Size</th>
* </tr>
* <tr>
* <td>1</td>
* <td>M1</td>
* <td>11 x 11</td>
* </tr>
* <tr>
* <td>2</td>
* <td>M2</td>
* <td>13 x 13</td>
* </tr>
* <tr>
* <td>3</td>
* <td>M3</td>
* <td>15 x 15</td>
* </tr>
* <tr>
* <td>4</td>
* <td>M4</td>
* <td>17 x 17</td>
* </tr>
* </tbody>
* </table>
*
* @param version symbol size
*/
public void setPreferredVersion(final int version) {
if (version < 0 || version > 4) { // TODO: min 1
throw new IllegalArgumentException("Invalid version: " + version);
}
this.preferredVersion = version;
}
 
/**
* Returns the preferred symbol size.
*
* @return the preferred symbol size
* @see #setPreferredVersion(int)
*/
public int getPreferredVersion() {
return this.preferredVersion;
}
 
/**
* <p>
* Set the amount of symbol space allocated to error correction. Levels are predefined according
* to the following table:
*
* <table summary="Micro QR Error correction levels">
* <tbody>
* <tr>
* <th>ECC Level</th>
* <th>Error Correction Capacity</th>
* <th>Recovery Capacity</th>
* </tr>
* <tr>
* <td>L (default)</td>
* <td>Approx 20% of symbol</td>
* <td>Approx 7%</td>
* </tr>
* <tr>
* <td>M</td>
* <td>Approx 37% of symbol</td>
* <td>Approx 15%</td>
* </tr>
* <tr>
* <td>Q</td>
* <td>Approx 55% of symbol</td>
* <td>Approx 25%</td>
* </tr>
* <tr>
* <td>H</td>
* <td>Approx 65% of symbol</td>
* <td>Approx 30%</td>
* </tr>
* </tbody>
* </table>
*
* @param eccMode error correction level
*/
public void setEccMode(final EccMode eccMode) {
this.preferredEccLevel = eccMode;
}
 
/**
* Returns the preferred ECC mode (error correction level).
*
* @return the preferred ECC mode
* @see #setEccMode(EccMode)
*/
public EccMode getEccMode() {
return this.preferredEccLevel;
}
 
@Override
protected void encode() {
int i, j, size;
final boolean[] version_valid = new boolean[4];
int n_count, a_count;
EccMode ecc_level;
int version, autoversion;
int bitmask;
int format, format_full;
final StringBuilder bin = new StringBuilder();
boolean byteModeUsed;
boolean alphanumModeUsed;
boolean kanjiModeUsed;
 
if (this.content.length() > 35) {
throw new OkapiException("Input data too long");
}
 
inputCharCheck();
 
for (i = 0; i < 4; i++) {
version_valid[i] = true;
}
 
this.inputMode = new qrMode[40];
selectEncodingMode();
 
n_count = 0;
a_count = 0;
for (i = 0; i < this.content.length(); i++) {
if (this.content.charAt(i) >= '0' && this.content.charAt(i) <= '9') {
n_count++;
}
if (isAlphanumeric(this.content.charAt(i))) {
a_count++;
}
}
 
if (a_count == this.content.length()) {
/* All data can be encoded in Alphanumeric mode */
for (i = 0; i < this.content.length(); i++) {
this.inputMode[i] = qrMode.ALPHANUM;
}
}
 
if (n_count == this.content.length()) {
/* All data can be encoded in Numeric mode */
for (i = 0; i < this.content.length(); i++) {
this.inputMode[i] = qrMode.NUMERIC;
}
}
 
byteModeUsed = false;
alphanumModeUsed = false;
kanjiModeUsed = false;
 
for (i = 0; i < this.content.length(); i++) {
if (this.inputMode[i] == qrMode.BINARY) {
byteModeUsed = true;
}
 
if (this.inputMode[i] == qrMode.ALPHANUM) {
alphanumModeUsed = true;
}
 
if (this.inputMode[i] == qrMode.KANJI) {
kanjiModeUsed = true;
}
}
 
getBinaryLength();
 
/* Eliminate possible versions depending on type of content */
if (byteModeUsed) {
version_valid[0] = false;
version_valid[1] = false;
}
 
if (alphanumModeUsed) {
version_valid[0] = false;
}
 
if (kanjiModeUsed) {
version_valid[0] = false;
version_valid[1] = false;
}
 
/* Eliminate possible versions depending on length of binary data */
if (this.binaryCount[0] > 20) {
version_valid[0] = false;
}
if (this.binaryCount[1] > 40) {
version_valid[1] = false;
}
if (this.binaryCount[2] > 84) {
version_valid[2] = false;
}
if (this.binaryCount[3] > 128) {
throw new OkapiException("Input data too long");
}
 
/* Eliminate possible versions depending on error correction level specified */
ecc_level = this.preferredEccLevel;
 
if (ecc_level == EccMode.H) {
throw new OkapiException("Error correction level H not available");
}
 
if (ecc_level == EccMode.Q) {
version_valid[0] = false;
version_valid[1] = false;
version_valid[2] = false;
if (this.binaryCount[3] > 80) {
throw new OkapiException("Input data too long");
}
}
 
if (ecc_level == EccMode.M) {
version_valid[0] = false;
if (this.binaryCount[1] > 32) {
version_valid[1] = false;
}
if (this.binaryCount[2] > 68) {
version_valid[2] = false;
}
if (this.binaryCount[3] > 112) {
throw new OkapiException("Input data too long");
}
}
 
autoversion = 3;
if (version_valid[2]) {
autoversion = 2;
}
if (version_valid[1]) {
autoversion = 1;
}
if (version_valid[0]) {
autoversion = 0;
}
 
version = autoversion;
/* Get version from user */
if (this.preferredVersion >= 1 && this.preferredVersion <= 4) {
if (this.preferredVersion - 1 >= autoversion) {
version = this.preferredVersion - 1;
}
}
 
/* If there is enough unused space then increase the error correction level */
if (version == 3) {
if (this.binaryCount[3] <= 112) {
ecc_level = EccMode.M;
}
if (this.binaryCount[3] <= 80) {
ecc_level = EccMode.Q;
}
}
 
if (version == 2 && this.binaryCount[2] <= 68) {
ecc_level = EccMode.M;
}
 
if (version == 1 && this.binaryCount[1] <= 32) {
ecc_level = EccMode.M;
}
 
this.binary = new StringBuilder();
generateBinary(version);
if (this.binary.length() > 128) {
throw new OkapiException("Input data too long");
}
 
switch (version) {
case 0:
generateM1Symbol();
infoLine("Version: M1");
break;
case 1:
generateM2Symbol(ecc_level);
infoLine("Version: M2");
infoLine("ECC Level: " + levelToLetter(ecc_level));
break;
case 2:
generateM3Symbol(ecc_level);
infoLine("Version: M3");
infoLine("ECC Level: " + levelToLetter(ecc_level));
break;
case 3:
generateM4Symbol(ecc_level);
infoLine("Version: M4");
infoLine("ECC Level: " + levelToLetter(ecc_level));
break;
}
 
size = MICRO_QR_SIZES[version];
 
this.grid = new int[size * size];
 
for (i = 0; i < size; i++) {
for (j = 0; j < size; j++) {
this.grid[i * size + j] = 0;
}
}
 
setupBitGrid(size);
populateBitGrid(size);
bitmask = applyBitmask(size);
 
infoLine("Mask Pattern: " + Integer.toBinaryString(bitmask));
 
/* Add format data */
format = 0;
switch (version) {
case 1:
switch (ecc_level) {
case L:
format = 1;
break;
case M:
format = 2;
break;
}
break;
case 2:
switch (ecc_level) {
case L:
format = 3;
break;
case M:
format = 4;
break;
}
break;
case 3:
switch (ecc_level) {
case L:
format = 5;
break;
case M:
format = 6;
break;
case Q:
format = 7;
break;
}
break;
}
 
format_full = QR_ANNEX_C1[(format << 2) + bitmask];
 
if ((format_full & 0x4000) != 0) {
this.grid[8 * size + 1] += 0x01;
}
if ((format_full & 0x2000) != 0) {
this.grid[8 * size + 2] += 0x01;
}
if ((format_full & 0x1000) != 0) {
this.grid[8 * size + 3] += 0x01;
}
if ((format_full & 0x800) != 0) {
this.grid[8 * size + 4] += 0x01;
}
if ((format_full & 0x400) != 0) {
this.grid[8 * size + 5] += 0x01;
}
if ((format_full & 0x200) != 0) {
this.grid[8 * size + 6] += 0x01;
}
if ((format_full & 0x100) != 0) {
this.grid[8 * size + 7] += 0x01;
}
if ((format_full & 0x80) != 0) {
this.grid[8 * size + 8] += 0x01;
}
if ((format_full & 0x40) != 0) {
this.grid[7 * size + 8] += 0x01;
}
if ((format_full & 0x20) != 0) {
this.grid[6 * size + 8] += 0x01;
}
if ((format_full & 0x10) != 0) {
this.grid[5 * size + 8] += 0x01;
}
if ((format_full & 0x08) != 0) {
this.grid[4 * size + 8] += 0x01;
}
if ((format_full & 0x04) != 0) {
this.grid[3 * size + 8] += 0x01;
}
if ((format_full & 0x02) != 0) {
this.grid[2 * size + 8] += 0x01;
}
if ((format_full & 0x01) != 0) {
this.grid[1 * size + 8] += 0x01;
}
 
this.readable = "";
this.pattern = new String[size];
this.row_count = size;
this.row_height = new int[size];
for (i = 0; i < size; i++) {
bin.setLength(0);
for (j = 0; j < size; j++) {
if ((this.grid[i * size + j] & 0x01) != 0) {
bin.append('1');
} else {
bin.append('0');
}
}
this.pattern[i] = bin2pat(bin);
this.row_height[i] = 1;
}
}
 
private void inputCharCheck() {
int qmarkBefore, qmarkAfter;
int i;
byte[] temp;
 
/* Check that input includes valid characters */
 
if (this.content.matches("[\u0000-\u00FF]+")) {
/* All characters in ISO 8859-1 */
return;
}
 
/* Otherwise check for Shift-JIS characters */
qmarkBefore = 0;
for (i = 0; i < this.content.length(); i++) {
if (this.content.charAt(i) == '?') {
qmarkBefore++;
}
}
 
try {
temp = this.content.getBytes("SJIS");
} catch (final UnsupportedEncodingException e) {
throw new OkapiException("Character encoding error");
}
 
qmarkAfter = 0;
for (i = 0; i < temp.length; i++) {
if (temp[i] == '?') {
qmarkAfter++;
}
}
 
/* If these values are the same, conversion was successful */
if (qmarkBefore != qmarkAfter) {
throw new OkapiException("Invalid characters in input data");
}
}
 
private char levelToLetter(final EccMode ecc_mode) {
switch (ecc_mode) {
case L:
return 'L';
case M:
return 'M';
case Q:
return 'Q';
case H:
return 'H';
default:
return ' ';
}
}
 
private void selectEncodingMode() {
int i, j;
int mlen;
final int length = this.content.length();
 
for (i = 0; i < length; i++) {
if (this.content.charAt(i) > 0xff) {
this.inputMode[i] = qrMode.KANJI;
} else {
this.inputMode[i] = qrMode.BINARY;
if (isAlphanumeric(this.content.charAt(i))) {
this.inputMode[i] = qrMode.ALPHANUM;
}
if (this.content.charAt(i) >= '0' && this.content.charAt(i) <= '9') {
this.inputMode[i] = qrMode.NUMERIC;
}
}
}
 
/* If less than 6 numeric digits together then don't use numeric mode */
for (i = 0; i < length; i++) {
if (this.inputMode[i] == qrMode.NUMERIC) {
if (i != 0 && this.inputMode[i - 1] != qrMode.NUMERIC || i == 0) {
mlen = 0;
while (mlen + i < length && this.inputMode[mlen + i] == qrMode.NUMERIC) {
mlen++;
}
if (mlen < 6) {
for (j = 0; j < mlen; j++) {
this.inputMode[i + j] = qrMode.ALPHANUM;
}
}
}
}
}
 
/* If less than 4 alphanumeric characters together then don't use alphanumeric mode */
for (i = 0; i < length; i++) {
if (this.inputMode[i] == qrMode.ALPHANUM) {
if (i != 0 && this.inputMode[i - 1] != qrMode.ALPHANUM || i == 0) {
mlen = 0;
while (mlen + i < length && this.inputMode[mlen + i] == qrMode.ALPHANUM) {
mlen++;
}
if (mlen < 6) {
for (j = 0; j < mlen; j++) {
this.inputMode[i + j] = qrMode.BINARY;
}
}
}
}
}
}
 
private boolean isAlphanumeric(final char cglyph) {
/* Returns true if input glyph is in the Alphanumeric set */
boolean retval = false;
 
if (cglyph >= '0' && cglyph <= '9') {
retval = true;
}
if (cglyph >= 'A' && cglyph <= 'Z') {
retval = true;
}
switch (cglyph) {
case ' ':
case '$':
case '%':
case '*':
case '+':
case '-':
case '.':
case '/':
case ':':
retval = true;
break;
}
 
return retval;
}
 
private String toBinary(final int data, int h) {
final StringBuilder binary = new StringBuilder();
for (; h != 0; h >>= 1) {
if ((data & h) != 0) {
binary.append('1');
} else {
binary.append('0');
}
}
return binary.toString();
}
 
private void getBinaryLength() {
int i;
qrMode currentMode = qrMode.NULL;
int blockLength;
 
/* Always include a terminator */
for (i = 0; i < 4; i++) {
this.binaryCount[i] = 0;
}
 
for (i = 0; i < this.content.length(); i++) {
if (currentMode != this.inputMode[i]) {
 
blockLength = 0;
do {
blockLength++;
} while (i + blockLength < this.content.length() && this.inputMode[i + blockLength] == this.inputMode[i]);
 
switch (this.inputMode[i]) {
case KANJI:
this.binaryCount[2] += 5 + blockLength * 13;
this.binaryCount[3] += 7 + blockLength * 13;
 
break;
case BINARY:
this.binaryCount[2] += 6 + blockLength * 8;
this.binaryCount[3] += 8 + blockLength * 8;
break;
case ALPHANUM:
int alphaLength;
 
if (blockLength % 2 == 1) {
/* Odd length block */
alphaLength = (blockLength - 1) / 2 * 11;
alphaLength += 6;
} else {
/* Even length block */
alphaLength = blockLength / 2 * 11;
}
 
this.binaryCount[1] += 4 + alphaLength;
this.binaryCount[2] += 6 + alphaLength;
this.binaryCount[3] += 8 + alphaLength;
break;
case NUMERIC:
int numLength;
 
switch (blockLength % 3) {
case 1:
/* one digit left over */
numLength = (blockLength - 1) / 3 * 10;
numLength += 4;
break;
case 2:
/* two digits left over */
numLength = (blockLength - 2) / 3 * 10;
numLength += 7;
break;
default:
/* blockLength is a multiple of 3 */
numLength = blockLength / 3 * 10;
break;
}
 
this.binaryCount[0] += 3 + numLength;
this.binaryCount[1] += 5 + numLength;
this.binaryCount[2] += 7 + numLength;
this.binaryCount[3] += 9 + numLength;
break;
}
currentMode = this.inputMode[i];
}
}
 
/* Add terminator */
if (this.binaryCount[1] < 37) {
this.binaryCount[1] += 5;
}
 
if (this.binaryCount[2] < 81) {
this.binaryCount[2] += 7;
}
 
if (this.binaryCount[3] < 125) {
this.binaryCount[3] += 9;
}
}
 
private void generateBinary(final int version) {
int position = 0;
int blockLength, i;
qrMode data_block;
int msb, lsb, prod, jis;
String oneChar;
byte[] jisBytes;
int count, first, second, third;
 
info("Encoding: ");
 
do {
data_block = this.inputMode[position];
blockLength = 0;
do {
blockLength++;
} while (blockLength + position < this.content.length() && this.inputMode[position + blockLength] == data_block);
 
switch (data_block) {
case KANJI:
/* Kanji mode */
/* Mode indicator */
switch (version) {
case 2:
this.binary.append("11");
break;
case 3:
this.binary.append("011");
break;
}
 
/* Character count indicator */
this.binary.append(toBinary(blockLength, 1 << version)); /* version = 2..3 */
 
info("KANJ (" + blockLength + ") ");
 
/* Character representation */
for (i = 0; i < blockLength; i++) {
oneChar = "";
oneChar += this.content.charAt(position + i);
 
/* Convert Unicode input to Shift-JIS */
try {
jisBytes = oneChar.getBytes("SJIS");
} catch (final UnsupportedEncodingException e) {
throw new OkapiException("Character encoding error");
}
 
jis = (jisBytes[0] & 0xFF) << 8;
if (jisBytes.length > 1) {
jis += jisBytes[1] & 0xFF;
}
 
if (jis > 0x9fff) {
jis -= 0xc140;
} else {
jis -= 0x8140;
}
msb = (jis & 0xff00) >> 8;
lsb = jis & 0xff;
prod = msb * 0xc0 + lsb;
 
this.binary.append(toBinary(prod, 0x1000));
 
infoSpace(prod);
}
 
break;
case BINARY:
/* Byte mode */
/* Mode indicator */
switch (version) {
case 2:
this.binary.append("10");
break;
case 3:
this.binary.append("010");
break;
}
 
/* Character count indicator */
this.binary.append(toBinary(blockLength, 2 << version)); /* version = 2..3 */
 
info("BYTE (" + blockLength + ") ");
 
/* Character representation */
for (i = 0; i < blockLength; i++) {
final int lbyte = this.content.charAt(position + i);
this.binary.append(toBinary(lbyte, 0x80));
infoSpace(lbyte);
}
 
break;
case ALPHANUM:
/* Alphanumeric mode */
/* Mode indicator */
switch (version) {
case 1:
this.binary.append("1");
break;
case 2:
this.binary.append("01");
break;
case 3:
this.binary.append("001");
break;
}
 
/* Character count indicator */
this.binary.append(toBinary(blockLength, 2 << version)); /* version = 1..3 */
 
info("ALPH (" + blockLength + ") ");
 
/* Character representation */
i = 0;
while (i < blockLength) {
first = positionOf(this.content.charAt(position + i), RHODIUM);
count = 1;
prod = first;
 
if (i + 1 < blockLength) {
if (this.inputMode[position + i + 1] == qrMode.ALPHANUM) {
second = positionOf(this.content.charAt(position + i + 1), RHODIUM);
count = 2;
prod = first * 45 + second;
}
}
 
this.binary.append(toBinary(prod, 1 << 5 * count)); /* count = 1..2 */
 
infoSpace(prod);
 
i += 2;
}
 
break;
case NUMERIC:
/* Numeric mode */
/* Mode indicator */
switch (version) {
case 1:
this.binary.append("0");
break;
case 2:
this.binary.append("00");
break;
case 3:
this.binary.append("000");
break;
}
 
/* Character count indicator */
this.binary.append(toBinary(blockLength, 4 << version)); /* version = 0..3 */
 
info("NUMB (" + blockLength + ") ");
 
/* Character representation */
i = 0;
while (i < blockLength) {
first = Character.getNumericValue(this.content.charAt(position + i));
count = 1;
prod = first;
 
if (i + 1 < blockLength) {
if (this.inputMode[position + i + 1] == qrMode.NUMERIC) {
second = Character.getNumericValue(this.content.charAt(position + i + 1));
count = 2;
prod = prod * 10 + second;
}
}
 
if (i + 2 < blockLength) {
if (this.inputMode[position + i + 2] == qrMode.NUMERIC) {
third = Character.getNumericValue(this.content.charAt(position + i + 2));
count = 3;
prod = prod * 10 + third;
}
}
 
this.binary.append(toBinary(prod, 1 << 3 * count)); /* count = 1..3 */
 
infoSpace(prod);
 
i += 3;
}
break;
}
 
position += blockLength;
} while (position < this.content.length() - 1);
 
/* Add terminator */
switch (version) {
case 0:
this.binary.append("000");
break;
case 1:
if (this.binary.length() < 37) {
this.binary.append("00000");
}
break;
case 2:
if (this.binary.length() < 81) {
this.binary.append("0000000");
}
break;
case 3:
if (this.binary.length() < 125) {
this.binary.append("000000000");
}
break;
}
 
infoLine();
}
 
private void generateM1Symbol() {
int i, latch;
int bits_total, bits_left, remainder;
int data_codewords, ecc_codewords;
final int[] data_blocks = new int[4];
final int[] ecc_blocks = new int[3];
final ReedSolomon rs = new ReedSolomon();
 
bits_total = 20;
latch = 0;
 
/* Manage last (4-bit) block */
bits_left = bits_total - this.binary.length();
if (bits_left <= 4) {
for (i = 0; i < bits_left; i++) {
this.binary.append("0");
}
latch = 1;
}
 
if (latch == 0) {
/* Complete current byte */
remainder = 8 - this.binary.length() % 8;
if (remainder == 8) {
remainder = 0;
}
for (i = 0; i < remainder; i++) {
this.binary.append("0");
}
 
/* Add padding */
bits_left = bits_total - this.binary.length();
if (bits_left > 4) {
remainder = (bits_left - 4) / 8;
for (i = 0; i < remainder; i++) {
if ((i & 1) != 0) {
this.binary.append("00010001");
} else {
this.binary.append("11101100");
}
}
}
this.binary.append("0000");
}
 
data_codewords = 3;
ecc_codewords = 2;
 
/* Copy data into codewords */
for (i = 0; i < data_codewords - 1; i++) {
data_blocks[i] = 0;
if (this.binary.charAt(i * 8) == '1') {
data_blocks[i] += 0x80;
}
if (this.binary.charAt(i * 8 + 1) == '1') {
data_blocks[i] += 0x40;
}
if (this.binary.charAt(i * 8 + 2) == '1') {
data_blocks[i] += 0x20;
}
if (this.binary.charAt(i * 8 + 3) == '1') {
data_blocks[i] += 0x10;
}
if (this.binary.charAt(i * 8 + 4) == '1') {
data_blocks[i] += 0x08;
}
if (this.binary.charAt(i * 8 + 5) == '1') {
data_blocks[i] += 0x04;
}
if (this.binary.charAt(i * 8 + 6) == '1') {
data_blocks[i] += 0x02;
}
if (this.binary.charAt(i * 8 + 7) == '1') {
data_blocks[i] += 0x01;
}
}
data_blocks[2] = 0;
if (this.binary.charAt(16) == '1') {
data_blocks[2] += 0x08;
}
if (this.binary.charAt(17) == '1') {
data_blocks[2] += 0x04;
}
if (this.binary.charAt(18) == '1') {
data_blocks[2] += 0x02;
}
if (this.binary.charAt(19) == '1') {
data_blocks[2] += 0x01;
}
 
info("Codewords: ");
for (i = 0; i < data_codewords; i++) {
infoSpace(data_blocks[i]);
}
infoLine();
 
/* Calculate Reed-Solomon error codewords */
rs.init_gf(0x11d);
rs.init_code(ecc_codewords, 0);
rs.encode(data_codewords, data_blocks);
for (i = 0; i < ecc_codewords; i++) {
ecc_blocks[i] = rs.getResult(i);
}
 
/* Add Reed-Solomon codewords to binary data */
for (i = 0; i < ecc_codewords; i++) {
this.binary.append(toBinary(ecc_blocks[ecc_codewords - i - 1], 0x80));
}
}
 
private void generateM2Symbol(final EccMode ecc_mode) {
int i;
int bits_total, bits_left, remainder;
int data_codewords, ecc_codewords;
final int[] data_blocks = new int[6];
final int[] ecc_blocks = new int[7];
final ReedSolomon rs = new ReedSolomon();
 
bits_total = 40; // ecc_mode == EccMode.L
if (ecc_mode == EccMode.M) {
bits_total = 32;
}
 
/* Complete current byte */
remainder = 8 - this.binary.length() % 8;
if (remainder == 8) {
remainder = 0;
}
for (i = 0; i < remainder; i++) {
this.binary.append("0");
}
 
/* Add padding */
bits_left = bits_total - this.binary.length();
remainder = bits_left / 8;
for (i = 0; i < remainder; i++) {
if ((i & 1) != 0) {
this.binary.append("00010001");
} else {
this.binary.append("11101100");
}
}
 
data_codewords = 5;
ecc_codewords = 5; // ecc_mode == EccMode.L
if (ecc_mode == EccMode.M) {
data_codewords = 4;
ecc_codewords = 6;
}
 
/* Copy data into codewords */
for (i = 0; i < data_codewords; i++) {
data_blocks[i] = 0;
if (this.binary.charAt(i * 8) == '1') {
data_blocks[i] += 0x80;
}
if (this.binary.charAt(i * 8 + 1) == '1') {
data_blocks[i] += 0x40;
}
if (this.binary.charAt(i * 8 + 2) == '1') {
data_blocks[i] += 0x20;
}
if (this.binary.charAt(i * 8 + 3) == '1') {
data_blocks[i] += 0x10;
}
if (this.binary.charAt(i * 8 + 4) == '1') {
data_blocks[i] += 0x08;
}
if (this.binary.charAt(i * 8 + 5) == '1') {
data_blocks[i] += 0x04;
}
if (this.binary.charAt(i * 8 + 6) == '1') {
data_blocks[i] += 0x02;
}
if (this.binary.charAt(i * 8 + 7) == '1') {
data_blocks[i] += 0x01;
}
}
 
info("Codewords: ");
for (i = 0; i < data_codewords; i++) {
infoSpace(data_blocks[i]);
}
infoLine();
 
/* Calculate Reed-Solomon error codewords */
rs.init_gf(0x11d);
rs.init_code(ecc_codewords, 0);
rs.encode(data_codewords, data_blocks);
for (i = 0; i < ecc_codewords; i++) {
ecc_blocks[i] = rs.getResult(i);
}
 
/* Add Reed-Solomon codewords to binary data */
for (i = 0; i < ecc_codewords; i++) {
this.binary.append(toBinary(ecc_blocks[ecc_codewords - i - 1], 0x80));
}
}
 
private void generateM3Symbol(final EccMode ecc_mode) {
int i, latch;
int bits_total, bits_left, remainder;
int data_codewords, ecc_codewords;
final int[] data_blocks = new int[12];
final int[] ecc_blocks = new int[12];
final ReedSolomon rs = new ReedSolomon();
 
latch = 0;
 
bits_total = 84; // ecc_mode == EccMode.L
if (ecc_mode == EccMode.M) {
bits_total = 68;
}
 
/* Manage last (4-bit) block */
bits_left = bits_total - this.binary.length();
if (bits_left <= 4) {
for (i = 0; i < bits_left; i++) {
this.binary.append("0");
}
latch = 1;
}
 
if (latch == 0) {
/* Complete current byte */
remainder = 8 - this.binary.length() % 8;
if (remainder == 8) {
remainder = 0;
}
for (i = 0; i < remainder; i++) {
this.binary.append("0");
}
 
/* Add padding */
bits_left = bits_total - this.binary.length();
if (bits_left > 4) {
remainder = (bits_left - 4) / 8;
for (i = 0; i < remainder; i++) {
if ((i & 1) != 0) {
this.binary.append("00010001");
} else {
this.binary.append("11101100");
}
}
}
this.binary.append("0000");
}
 
data_codewords = 11;
ecc_codewords = 6; // ecc_mode == EccMode.L
if (ecc_mode == EccMode.M) {
data_codewords = 9;
ecc_codewords = 8;
}
 
/* Copy data into codewords */
for (i = 0; i < data_codewords - 1; i++) {
data_blocks[i] = 0;
if (this.binary.charAt(i * 8) == '1') {
data_blocks[i] += 0x80;
}
if (this.binary.charAt(i * 8 + 1) == '1') {
data_blocks[i] += 0x40;
}
if (this.binary.charAt(i * 8 + 2) == '1') {
data_blocks[i] += 0x20;
}
if (this.binary.charAt(i * 8 + 3) == '1') {
data_blocks[i] += 0x10;
}
if (this.binary.charAt(i * 8 + 4) == '1') {
data_blocks[i] += 0x08;
}
if (this.binary.charAt(i * 8 + 5) == '1') {
data_blocks[i] += 0x04;
}
if (this.binary.charAt(i * 8 + 6) == '1') {
data_blocks[i] += 0x02;
}
if (this.binary.charAt(i * 8 + 7) == '1') {
data_blocks[i] += 0x01;
}
}
 
if (ecc_mode == EccMode.L) {
data_blocks[10] = 0;
if (this.binary.charAt(80) == '1') {
data_blocks[10] += 0x08;
}
if (this.binary.charAt(81) == '1') {
data_blocks[10] += 0x04;
}
if (this.binary.charAt(82) == '1') {
data_blocks[10] += 0x02;
}
if (this.binary.charAt(83) == '1') {
data_blocks[10] += 0x01;
}
}
 
if (ecc_mode == EccMode.M) {
data_blocks[8] = 0;
if (this.binary.charAt(64) == '1') {
data_blocks[8] += 0x08;
}
if (this.binary.charAt(65) == '1') {
data_blocks[8] += 0x04;
}
if (this.binary.charAt(66) == '1') {
data_blocks[8] += 0x02;
}
if (this.binary.charAt(67) == '1') {
data_blocks[8] += 0x01;
}
}
 
info("Codewords: ");
for (i = 0; i < data_codewords; i++) {
infoSpace(data_blocks[i]);
}
infoLine();
 
/* Calculate Reed-Solomon error codewords */
rs.init_gf(0x11d);
rs.init_code(ecc_codewords, 0);
rs.encode(data_codewords, data_blocks);
for (i = 0; i < ecc_codewords; i++) {
ecc_blocks[i] = rs.getResult(i);
}
 
/* Add Reed-Solomon codewords to binary data */
for (i = 0; i < ecc_codewords; i++) {
this.binary.append(toBinary(ecc_blocks[ecc_codewords - i - 1], 0x80));
}
}
 
private void generateM4Symbol(final EccMode ecc_mode) {
int i;
int bits_total, bits_left, remainder;
int data_codewords, ecc_codewords;
final int[] data_blocks = new int[17];
final int[] ecc_blocks = new int[15];
final ReedSolomon rs = new ReedSolomon();
 
bits_total = 128; // ecc_mode == EccMode.L
if (ecc_mode == EccMode.M) {
bits_total = 112;
}
if (ecc_mode == EccMode.Q) {
bits_total = 80;
}
 
/* Complete current byte */
remainder = 8 - this.binary.length() % 8;
if (remainder == 8) {
remainder = 0;
}
for (i = 0; i < remainder; i++) {
this.binary.append("0");
}
 
/* Add padding */
bits_left = bits_total - this.binary.length();
remainder = bits_left / 8;
for (i = 0; i < remainder; i++) {
if ((i & 1) != 0) {
this.binary.append("00010001");
} else {
this.binary.append("11101100");
}
}
 
data_codewords = 16;
ecc_codewords = 8; // ecc_mode == EccMode.L
if (ecc_mode == EccMode.M) {
data_codewords = 14;
ecc_codewords = 10;
}
if (ecc_mode == EccMode.Q) {
data_codewords = 10;
ecc_codewords = 14;
}
 
/* Copy data into codewords */
for (i = 0; i < data_codewords; i++) {
data_blocks[i] = 0;
if (this.binary.charAt(i * 8) == '1') {
data_blocks[i] += 0x80;
}
if (this.binary.charAt(i * 8 + 1) == '1') {
data_blocks[i] += 0x40;
}
if (this.binary.charAt(i * 8 + 2) == '1') {
data_blocks[i] += 0x20;
}
if (this.binary.charAt(i * 8 + 3) == '1') {
data_blocks[i] += 0x10;
}
if (this.binary.charAt(i * 8 + 4) == '1') {
data_blocks[i] += 0x08;
}
if (this.binary.charAt(i * 8 + 5) == '1') {
data_blocks[i] += 0x04;
}
if (this.binary.charAt(i * 8 + 6) == '1') {
data_blocks[i] += 0x02;
}
if (this.binary.charAt(i * 8 + 7) == '1') {
data_blocks[i] += 0x01;
}
}
 
info("Codewords: ");
for (i = 0; i < data_codewords; i++) {
infoSpace(data_blocks[i]);
}
infoLine();
 
/* Calculate Reed-Solomon error codewords */
rs.init_gf(0x11d);
rs.init_code(ecc_codewords, 0);
rs.encode(data_codewords, data_blocks);
for (i = 0; i < ecc_codewords; i++) {
ecc_blocks[i] = rs.getResult(i);
}
 
/* Add Reed-Solomon codewords to binary data */
for (i = 0; i < ecc_codewords; i++) {
this.binary.append(toBinary(ecc_blocks[ecc_codewords - i - 1], 0x80));
}
}
 
private void setupBitGrid(final int size) {
int i, toggle = 1;
 
/* Add timing patterns */
for (i = 0; i < size; i++) {
if (toggle == 1) {
this.grid[i] = 0x21;
this.grid[i * size] = 0x21;
toggle = 0;
} else {
this.grid[i] = 0x20;
this.grid[i * size] = 0x20;
toggle = 1;
}
}
 
/* Add finder patterns */
placeFinderPattern(size, 0, 0);
 
/* Add separators */
for (i = 0; i < 7; i++) {
this.grid[7 * size + i] = 0x10;
this.grid[i * size + 7] = 0x10;
}
this.grid[7 * size + 7] = 0x10;
 
/* Reserve space for format information */
for (i = 0; i < 8; i++) {
this.grid[8 * size + i] += 0x20;
this.grid[i * size + 8] += 0x20;
}
this.grid[8 * size + 8] += 0x20;
}
 
private void placeFinderPattern(final int size, final int x, final int y) {
int xp, yp;
 
final int[] finder = { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 };
 
for (xp = 0; xp < 7; xp++) {
for (yp = 0; yp < 7; yp++) {
if (finder[xp + 7 * yp] == 1) {
this.grid[(yp + y) * size + xp + x] = 0x11;
} else {
this.grid[(yp + y) * size + xp + x] = 0x10;
}
}
}
}
 
private void populateBitGrid(final int size) {
boolean goingUp = true;
int row = 0; /* right hand side */
 
int i, n, x, y;
 
n = this.binary.length();
y = size - 1;
i = 0;
do {
x = size - 2 - row * 2;
 
if ((this.grid[y * size + x + 1] & 0xf0) == 0) {
if (this.binary.charAt(i) == '1') {
this.grid[y * size + x + 1] = 0x01;
} else {
this.grid[y * size + x + 1] = 0x00;
}
i++;
}
 
if (i < n) {
if ((this.grid[y * size + x] & 0xf0) == 0) {
if (this.binary.charAt(i) == '1') {
this.grid[y * size + x] = 0x01;
} else {
this.grid[y * size + x] = 0x00;
}
i++;
}
}
 
if (goingUp) {
y--;
} else {
y++;
}
if (y == 0) {
/* reached the top */
row++;
y = 1;
goingUp = false;
}
if (y == size) {
/* reached the bottom */
row++;
y = size - 1;
goingUp = true;
}
} while (i < n);
}
 
private int applyBitmask(final int size) {
int x, y;
int p;
int local_pattern;
final int[] value = new int[8];
int best_val, best_pattern;
int bit;
 
final int[] mask = new int[size * size];
this.eval = new int[size * size];
 
/* Perform data masking */
for (x = 0; x < size; x++) {
for (y = 0; y < size; y++) {
mask[y * size + x] = 0x00;
 
if ((this.grid[y * size + x] & 0xf0) == 0) {
if ((y & 1) == 0) {
mask[y * size + x] += 0x01;
}
 
if ((y / 2 + x / 3 & 1) == 0) {
mask[y * size + x] += 0x02;
}
 
if (((y * x & 1) + y * x % 3 & 1) == 0) {
mask[y * size + x] += 0x04;
}
 
if (((y + x & 1) + y * x % 3 & 1) == 0) {
mask[y * size + x] += 0x08;
}
}
}
}
 
for (x = 0; x < size; x++) {
for (y = 0; y < size; y++) {
if ((this.grid[y * size + x] & 0x01) != 0) {
p = 0xff;
} else {
p = 0x00;
}
 
this.eval[y * size + x] = mask[y * size + x] ^ p;
}
}
 
/* Evaluate result */
for (local_pattern = 0; local_pattern < 4; local_pattern++) {
value[local_pattern] = evaluateBitmask(size, local_pattern);
}
 
best_pattern = 0;
best_val = value[0];
for (local_pattern = 1; local_pattern < 4; local_pattern++) {
if (value[local_pattern] > best_val) {
best_pattern = local_pattern;
best_val = value[local_pattern];
}
}
 
/* Apply mask */
for (x = 0; x < size; x++) {
for (y = 0; y < size; y++) {
bit = 0;
switch (best_pattern) {
case 0:
if ((mask[y * size + x] & 0x01) != 0) {
bit = 1;
}
break;
case 1:
if ((mask[y * size + x] & 0x02) != 0) {
bit = 1;
}
break;
case 2:
if ((mask[y * size + x] & 0x04) != 0) {
bit = 1;
}
break;
case 3:
if ((mask[y * size + x] & 0x08) != 0) {
bit = 1;
}
break;
}
if (bit == 1) {
if ((this.grid[y * size + x] & 0x01) != 0) {
this.grid[y * size + x] = 0x00;
} else {
this.grid[y * size + x] = 0x01;
}
}
}
}
 
return best_pattern;
}
 
private int evaluateBitmask(final int size, final int pattern) {
int sum1, sum2, i, filter = 0, retval;
 
switch (pattern) {
case 0:
filter = 0x01;
break;
case 1:
filter = 0x02;
break;
case 2:
filter = 0x04;
break;
case 3:
filter = 0x08;
break;
}
 
sum1 = 0;
sum2 = 0;
for (i = 1; i < size; i++) {
if ((this.eval[i * size + size - 1] & filter) != 0) {
sum1++;
}
if ((this.eval[(size - 1) * size + i] & filter) != 0) {
sum2++;
}
}
 
if (sum1 <= sum2) {
retval = sum1 * 16 + sum2;
} else {
retval = sum2 * 16 + sum1;
}
 
return retval;
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/DataBarExpanded.java
New file
0,0 → 1,1271
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
import static uk.org.okapibarcode.backend.DataBarLimited.getWidths;
import static uk.org.okapibarcode.util.Strings.binaryAppend;
 
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
 
/**
* <p>
* Implements GS1 DataBar Expanded Omnidirectional and GS1 DataBar Expanded Stacked Omnidirectional
* according to ISO/IEC 24724:2011.
*
* <p>
* DataBar expanded encodes GS1 data in either a linear or stacked format.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class DataBarExpanded extends Symbol {
 
private static final int[] G_SUM_EXP = { 0, 348, 1388, 2948, 3988 };
 
private static final int[] T_EVEN_EXP = { 4, 20, 52, 104, 204 };
 
private static final int[] MODULES_ODD_EXP = { 12, 10, 8, 6, 4 };
 
private static final int[] MODULES_EVEN_EXP = { 5, 7, 9, 11, 13 };
 
private static final int[] WIDEST_ODD_EXP = { 7, 5, 4, 3, 1 };
 
private static final int[] WIDEST_EVEN_EXP = { 2, 4, 5, 6, 8 };
 
/** Table 14 */
private static final int[] CHECKSUM_WEIGHT_EXP = { 1, 3, 9, 27, 81, 32, 96, 77, 20, 60, 180, 118, 143, 7, 21, 63, 189, 145, 13, 39, 117, 140, 209, 205, 193, 157, 49, 147, 19, 57, 171, 91, 62, 186,
136, 197, 169, 85, 44, 132, 185, 133, 188, 142, 4, 12, 36, 108, 113, 128, 173, 97, 80, 29, 87, 50, 150, 28, 84, 41, 123, 158, 52, 156, 46, 138, 203, 187, 139, 206, 196, 166, 76, 17, 51,
153, 37, 111, 122, 155, 43, 129, 176, 106, 107, 110, 119, 146, 16, 48, 144, 10, 30, 90, 59, 177, 109, 116, 137, 200, 178, 112, 125, 164, 70, 210, 208, 202, 184, 130, 179, 115, 134, 191,
151, 31, 93, 68, 204, 190, 148, 22, 66, 198, 172, 94, 71, 2, 6, 18, 54, 162, 64, 192, 154, 40, 120, 149, 25, 75, 14, 42, 126, 167, 79, 26, 78, 23, 69, 207, 199, 175, 103, 98, 83, 38, 114,
131, 182, 124, 161, 61, 183, 127, 170, 88, 53, 159, 55, 165, 73, 8, 24, 72, 5, 15, 45, 135, 194, 160, 58, 174, 100, 89 };
 
/** Table 15 */
private static final int[] FINDER_PATTERN_EXP = { 1, 8, 4, 1, 1, 1, 1, 4, 8, 1, 3, 6, 4, 1, 1, 1, 1, 4, 6, 3, 3, 4, 6, 1, 1, 1, 1, 6, 4, 3, 3, 2, 8, 1, 1, 1, 1, 8, 2, 3, 2, 6, 5, 1, 1, 1, 1, 5, 6,
2, 2, 2, 9, 1, 1, 1, 1, 9, 2, 2 };
 
/** Table 16 */
private static final int[] FINDER_SEQUENCE = { 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 3, 0, 0, 0, 0, 0, 0, 0, 0, 1, 6, 3, 8, 0, 0, 0, 0, 0, 0, 0, 1, 10, 3, 8, 5, 0, 0, 0, 0, 0, 0, 1, 10, 3, 8, 7,
12, 0, 0, 0, 0, 0, 1, 10, 3, 8, 9, 12, 11, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 10, 9, 0, 0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 0, 1, 2, 3, 4, 5, 8, 7, 10, 9,
12, 11 };
 
private static final int[] WEIGHT_ROWS = { 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 6, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 10, 3, 4, 13, 14, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 3, 4, 13, 14, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 3, 4, 13, 14, 11, 12, 21, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18,
3, 4, 13, 14, 15, 16, 21, 22, 19, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 17, 18, 15, 16,
0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 17, 18, 19, 20, 21, 22, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 13, 14, 11, 12, 17, 18, 15, 16, 21, 22, 19, 20 };
 
private enum EncodeMode {
NUMERIC, ALPHA, ISOIEC, INVALID_CHAR, ANY_ENC, ALPHA_OR_ISO
}
 
private boolean linkageFlag;
private int preferredColumns = 2;
private boolean stacked = true;
 
public DataBarExpanded() {
this.inputDataType = DataType.GS1;
}
 
@Override
public void setDataType(final DataType dataType) {
if (dataType != Symbol.DataType.GS1) {
throw new IllegalArgumentException("Only GS1 data type is supported for DataBar Expanded symbology.");
}
}
 
@Override
protected boolean gs1Supported() {
return true;
}
 
/**
* Sets the preferred width of a stacked symbol by selecting the number of "columns" or symbol
* segments in each row of data.
*
* @param columns the number of segments in each row
*/
public void setPreferredColumns(final int columns) {
if (columns < 1 || columns > 10) {
throw new IllegalArgumentException("Invalid column count: " + columns);
}
this.preferredColumns = columns;
}
 
/**
* Returns the preferred width of a stacked symbol by selecting the number of "columns" or
* symbol segments in each row of data.
*
* @return the number of segments in each row
*/
public int getPreferredColumns() {
return this.preferredColumns;
}
 
/**
* Sets whether or not this symbology is stacked.
*
* @param stacked <tt>true</tt> for GS1 DataBar Expanded Stacked Omnidirectional, <tt>false</tt>
* for GS1 DataBar Expanded Omnidirectional
*/
public void setStacked(final boolean stacked) {
this.stacked = stacked;
}
 
/**
* Returns whether or not this symbology is stacked.
*
* @return <tt>true</tt> for GS1 DataBar Expanded Stacked Omnidirectional, <tt>false</tt> for
* GS1 DataBar Expanded Omnidirectional
*/
public boolean isStacked() {
return this.stacked;
}
 
protected void setLinkageFlag(final boolean linkageFlag) {
this.linkageFlag = linkageFlag;
}
 
@Override
protected void encode() {
int i;
int j;
int k;
int data_chars;
final int[] vs = new int[21];
final int[] group = new int[21];
final int[] v_odd = new int[21];
final int[] v_even = new int[21];
final int[][] char_widths = new int[21][8];
int checksum;
int row;
int check_char;
int c_group;
int c_odd;
int c_even;
final int[] check_widths = new int[8];
int pattern_width;
final int[] elements = new int[235];
int codeblocks;
int stack_rows;
int blocksPerRow;
int current_block;
int current_row;
boolean special_case_row;
int elements_in_sub;
int reader;
int writer;
final int[] sub_elements = new int[235];
int l;
int symbol_row;
String separator_pattern;
boolean black;
boolean left_to_right;
int compositeOffset;
 
this.inputData = toBytes(this.content, StandardCharsets.US_ASCII);
 
final StringBuilder binaryString = new StringBuilder(this.inputData.length * 8);
 
if (this.linkageFlag) {
binaryString.append('1');
compositeOffset = 1;
} else {
binaryString.append('0');
compositeOffset = 0;
}
 
final int encodingMethod = calculateBinaryString(this.inputData, binaryString); // updates
// binaryString
infoLine("Encoding Method: " + encodingMethod);
logBinaryStringInfo(binaryString);
 
data_chars = binaryString.length() / 12;
 
info("Data Characters: ");
for (i = 0; i < data_chars; i++) {
vs[i] = 0;
for (j = 0; j < 12; j++) {
if (binaryString.charAt(i * 12 + j) == '1') {
vs[i] += 2048 >> j;
}
}
infoSpace(vs[i]);
}
infoLine();
 
for (i = 0; i < data_chars; i++) {
if (vs[i] <= 347) {
group[i] = 1;
}
if (vs[i] >= 348 && vs[i] <= 1387) {
group[i] = 2;
}
if (vs[i] >= 1388 && vs[i] <= 2947) {
group[i] = 3;
}
if (vs[i] >= 2948 && vs[i] <= 3987) {
group[i] = 4;
}
if (vs[i] >= 3988) {
group[i] = 5;
}
v_odd[i] = (vs[i] - G_SUM_EXP[group[i] - 1]) / T_EVEN_EXP[group[i] - 1];
v_even[i] = (vs[i] - G_SUM_EXP[group[i] - 1]) % T_EVEN_EXP[group[i] - 1];
 
int[] widths = getWidths(v_odd[i], MODULES_ODD_EXP[group[i] - 1], 4, WIDEST_ODD_EXP[group[i] - 1], 0);
char_widths[i][0] = widths[0];
char_widths[i][2] = widths[1];
char_widths[i][4] = widths[2];
char_widths[i][6] = widths[3];
 
widths = getWidths(v_even[i], MODULES_EVEN_EXP[group[i] - 1], 4, WIDEST_EVEN_EXP[group[i] - 1], 1);
char_widths[i][1] = widths[0];
char_widths[i][3] = widths[1];
char_widths[i][5] = widths[2];
char_widths[i][7] = widths[3];
}
 
/* 7.2.6 Check character */
/*
* The checksum value is equal to the mod 211 residue of the weighted sum of the widths of
* the elements in the data characters.
*/
checksum = 0;
for (i = 0; i < data_chars; i++) {
row = WEIGHT_ROWS[(data_chars - 2) / 2 * 21 + i];
for (j = 0; j < 8; j++) {
checksum += char_widths[i][j] * CHECKSUM_WEIGHT_EXP[row * 8 + j];
 
}
}
 
check_char = 211 * (data_chars + 1 - 4) + checksum % 211;
 
infoLine("Check Character: " + check_char);
 
c_group = 1;
if (check_char >= 348 && check_char <= 1387) {
c_group = 2;
}
if (check_char >= 1388 && check_char <= 2947) {
c_group = 3;
}
if (check_char >= 2948 && check_char <= 3987) {
c_group = 4;
}
if (check_char >= 3988) {
c_group = 5;
}
 
c_odd = (check_char - G_SUM_EXP[c_group - 1]) / T_EVEN_EXP[c_group - 1];
c_even = (check_char - G_SUM_EXP[c_group - 1]) % T_EVEN_EXP[c_group - 1];
 
int[] widths = getWidths(c_odd, MODULES_ODD_EXP[c_group - 1], 4, WIDEST_ODD_EXP[c_group - 1], 0);
check_widths[0] = widths[0];
check_widths[2] = widths[1];
check_widths[4] = widths[2];
check_widths[6] = widths[3];
 
widths = getWidths(c_even, MODULES_EVEN_EXP[c_group - 1], 4, WIDEST_EVEN_EXP[c_group - 1], 1);
check_widths[1] = widths[0];
check_widths[3] = widths[1];
check_widths[5] = widths[2];
check_widths[7] = widths[3];
 
/* Initialise element array */
pattern_width = ((data_chars + 1) / 2 + (data_chars + 1 & 1)) * 5 + (data_chars + 1) * 8 + 4;
for (i = 0; i < pattern_width; i++) {
elements[i] = 0;
}
 
elements[0] = 1;
elements[1] = 1;
elements[pattern_width - 2] = 1;
elements[pattern_width - 1] = 1;
 
/* Put finder patterns in element array */
for (i = 0; i < (data_chars + 1) / 2 + (data_chars + 1 & 1); i++) {
k = ((data_chars + 1 - 2) / 2 + (data_chars + 1 & 1) - 1) * 11 + i;
for (j = 0; j < 5; j++) {
elements[21 * i + j + 10] = FINDER_PATTERN_EXP[(FINDER_SEQUENCE[k] - 1) * 5 + j];
}
}
 
/* Put check character in element array */
for (i = 0; i < 8; i++) {
elements[i + 2] = check_widths[i];
}
 
/* Put forward reading data characters in element array */
for (i = 1; i < data_chars; i += 2) {
for (j = 0; j < 8; j++) {
elements[(i - 1) / 2 * 21 + 23 + j] = char_widths[i][j];
}
}
 
/* Put reversed data characters in element array */
for (i = 0; i < data_chars; i += 2) {
for (j = 0; j < 8; j++) {
elements[i / 2 * 21 + 15 + j] = char_widths[i][7 - j];
}
}
 
if (!this.stacked) {
/* Copy elements into symbol */
this.row_count = 1 + compositeOffset;
this.row_height = new int[1 + compositeOffset];
this.row_height[0 + compositeOffset] = -1;
this.pattern = new String[1 + compositeOffset];
 
writer = 0;
black = false;
final StringBuilder pat = new StringBuilder("0");
final StringBuilder separator_binary = new StringBuilder();
for (i = 0; i < pattern_width; i++) {
pat.append((char) (elements[i] + '0'));
for (j = 0; j < elements[i]; j++) {
if (black) {
separator_binary.append('0');
} else {
separator_binary.append('1');
}
}
black = !black;
writer += elements[i];
}
this.pattern[0 + compositeOffset] = pat.toString();
 
separator_binary.setCharAt(0, '0');
separator_binary.setCharAt(1, '0');
separator_binary.setCharAt(2, '0');
separator_binary.setCharAt(3, '0');
separator_binary.delete(writer - 4, separator_binary.length());
for (j = 0; j < writer / 49; j++) {
k = 49 * j + 18;
for (i = 0; i < 15; i++) {
if (separator_binary.charAt(i + k - 1) == '1' && separator_binary.charAt(i + k) == '1') {
separator_binary.setCharAt(i + k, '0');
}
}
}
if (this.linkageFlag) {
// Add composite code separator
this.pattern[0] = bin2pat(separator_binary);
this.row_height[0] = 1;
}
 
} else {
/* RSS Expanded Stacked */
codeblocks = (data_chars + 1) / 2 + (data_chars + 1) % 2;
 
blocksPerRow = this.preferredColumns;
 
if (this.linkageFlag && blocksPerRow == 1) {
/*
* "There shall be a minimum of four symbol characters in the first row of an RSS
* Expanded Stacked symbol when it is the linear component of an EAN.UCC Composite
* symbol."
*/
blocksPerRow = 2;
}
 
stack_rows = codeblocks / blocksPerRow;
if (codeblocks % blocksPerRow > 0) {
stack_rows++;
}
 
this.row_count = stack_rows * 4 - 3;
this.row_height = new int[this.row_count + compositeOffset];
this.pattern = new String[this.row_count + compositeOffset];
symbol_row = 0;
 
current_block = 0;
for (current_row = 1; current_row <= stack_rows; current_row++) {
for (i = 0; i < 235; i++) {
sub_elements[i] = 0;
}
special_case_row = false;
 
/* Row Start */
sub_elements[0] = 1;
sub_elements[1] = 1;
elements_in_sub = 2;
 
/* Row Data */
reader = 0;
do {
if ((blocksPerRow & 1) != 0 || (current_row & 1) != 0
|| current_row == stack_rows && codeblocks != current_row * blocksPerRow && (current_row * blocksPerRow - codeblocks & 1) != 0) {
/* left to right */
left_to_right = true;
i = 2 + current_block * 21;
for (j = 0; j < 21; j++) {
if (i + j < pattern_width) {
sub_elements[j + reader * 21 + 2] = elements[i + j];
elements_in_sub++;
}
}
} else {
/* right to left */
left_to_right = false;
if (current_row * blocksPerRow < codeblocks) {
/* a full row */
i = 2 + (current_row * blocksPerRow - reader - 1) * 21;
for (j = 0; j < 21; j++) {
if (i + j < pattern_width) {
sub_elements[20 - j + reader * 21 + 2] = elements[i + j];
elements_in_sub++;
}
}
} else {
/* a partial row */
k = current_row * blocksPerRow - codeblocks;
l = current_row * blocksPerRow - reader - 1;
i = 2 + (l - k) * 21;
for (j = 0; j < 21; j++) {
if (i + j < pattern_width) {
sub_elements[20 - j + reader * 21 + 2] = elements[i + j];
elements_in_sub++;
}
}
}
}
reader++;
current_block++;
} while (reader < blocksPerRow && current_block < codeblocks);
 
/* Row Stop */
sub_elements[elements_in_sub] = 1;
sub_elements[elements_in_sub + 1] = 1;
elements_in_sub += 2;
 
black = true;
StringBuilder pat = new StringBuilder();
this.row_height[symbol_row + compositeOffset] = -1;
 
if ((current_row & 1) != 0) {
pat.append('0');
black = false;
} else {
if (current_row == stack_rows && codeblocks != current_row * blocksPerRow && (current_row * blocksPerRow - codeblocks & 1) != 0) {
/* Special case bottom row */
special_case_row = true;
sub_elements[0] = 2;
pat.append('0');
black = false;
}
}
 
writer = 0;
 
final StringBuilder separator_binary = new StringBuilder();
for (i = 0; i < elements_in_sub; i++) {
pat.append((char) (sub_elements[i] + '0'));
for (j = 0; j < sub_elements[i]; j++) {
separator_binary.append(black ? '0' : '1');
}
black = !black;
writer += sub_elements[i];
}
this.pattern[symbol_row + compositeOffset] = pat.toString();
separator_binary.setCharAt(0, '0');
separator_binary.setCharAt(1, '0');
separator_binary.setCharAt(2, '0');
separator_binary.setCharAt(3, '0');
separator_binary.delete(writer - 4, separator_binary.length());
for (j = 0; j < reader; j++) {
k = 49 * j + (special_case_row ? 19 : 18);
if (left_to_right) {
for (i = 0; i < 15; i++) {
if (separator_binary.charAt(i + k - 1) == '1' && separator_binary.charAt(i + k) == '1') {
separator_binary.setCharAt(i + k, '0');
}
}
} else {
for (i = 14; i >= 0; i--) {
if (separator_binary.charAt(i + k + 1) == '1' && separator_binary.charAt(i + k) == '1') {
separator_binary.setCharAt(i + k, '0');
}
}
}
}
separator_pattern = bin2pat(separator_binary);
 
if (current_row == 1 && this.linkageFlag) {
// Add composite code separator
this.row_height[0] = 1;
this.pattern[0] = separator_pattern;
}
 
if (current_row != 1) {
/* middle separator pattern (above current row) */
pat = new StringBuilder("05");
for (j = 5; j < 49 * blocksPerRow; j += 2) {
pat.append("11");
}
this.pattern[symbol_row - 2 + compositeOffset] = pat.toString();
this.row_height[symbol_row - 2 + compositeOffset] = 1;
/* bottom separator pattern (above current row) */
this.row_height[symbol_row - 1 + compositeOffset] = 1;
this.pattern[symbol_row - 1 + compositeOffset] = separator_pattern;
}
 
if (current_row != stack_rows) {
this.row_height[symbol_row + 1 + compositeOffset] = 1;
this.pattern[symbol_row + 1 + compositeOffset] = separator_pattern;
}
 
symbol_row += 4;
}
this.readable = "";
this.row_count += compositeOffset;
}
}
 
/** Handles all data encodation from section 7.2.5 of ISO/IEC 24724. */
private static int calculateBinaryString(final int[] inputData, final StringBuilder binaryString) {
 
EncodeMode last_mode = EncodeMode.NUMERIC;
int i;
boolean latch;
int remainder, d1, d2, value;
String padstring;
int current_length;
 
/*
* Decide whether a compressed data field is required and if so what method to use: method 2
* = no compressed data field
*/
 
int encodingMethod;
if (inputData.length >= 16 && inputData[0] == '0' && inputData[1] == '1') {
/* (01) and other AIs */
encodingMethod = 1;
} else {
/* any AIs */
encodingMethod = 2;
}
 
if (inputData.length >= 20 && encodingMethod == 1 && inputData[2] == '9' && inputData[16] == '3') {
 
/* Possibly encoding method > 2 */
 
if (inputData.length >= 26 && inputData[17] == '1') {
/* Methods 3, 7, 9, 11 and 13 */
if (inputData[18] == '0') {
/* (01) and (310x), weight in kilos */
double weight = 0;
for (i = 0; i < 6; i++) {
weight *= 10;
weight += inputData[20 + i] - '0';
}
if (weight < 99_999) { /* Maximum weight = 99999 */
if (inputData[19] == '3' && inputData.length == 26) {
/* (01) and (3103) */
weight /= 1000.0;
if (weight <= 32.767) {
encodingMethod = 3;
}
}
if (inputData.length == 34) {
if (inputData[26] == '1' && inputData[27] == '1') {
/* (01), (310x) and (11) - metric weight and production date */
encodingMethod = 7;
}
if (inputData[26] == '1' && inputData[27] == '3') {
/* (01), (310x) and (13) - metric weight and packaging date */
encodingMethod = 9;
}
if (inputData[26] == '1' && inputData[27] == '5') {
/* (01), (310x) and (15) - metric weight and "best before" date */
encodingMethod = 11;
}
if (inputData[26] == '1' && inputData[27] == '7') {
/* (01), (310x) and (17) - metric weight and expiration date */
encodingMethod = 13;
}
}
}
}
}
 
if (inputData.length >= 26 && inputData[17] == '2') {
/* Methods 4, 8, 10, 12 and 14 */
if (inputData[18] == '0') {
/* (01) and (320x), weight in pounds */
double weight = 0;
for (i = 0; i < 6; i++) {
weight *= 10;
weight += inputData[20 + i] - '0';
}
if (weight < 99_999) { /* Maximum weight = 99999 */
if ((inputData[19] == '2' || inputData[19] == '3') && inputData.length == 26) {
/* (01) and (3202)/(3203) */
if (inputData[19] == '3') {
weight /= 1000.0;
if (weight <= 22.767) {
encodingMethod = 4;
}
} else {
weight /= 100.0;
if (weight <= 99.99) {
encodingMethod = 4;
}
}
}
if (inputData.length == 34) {
if (inputData[26] == '1' && inputData[27] == '1') {
/* (01), (320x) and (11) - English weight and production date */
encodingMethod = 8;
}
if (inputData[26] == '1' && inputData[27] == '3') {
/* (01), (320x) and (13) - English weight and packaging date */
encodingMethod = 10;
}
if (inputData[26] == '1' && inputData[27] == '5') {
/* (01), (320x) and (15) - English weight and "best before" date */
encodingMethod = 12;
}
if (inputData[26] == '1' && inputData[27] == '7') {
/* (01), (320x) and (17) - English weight and expiration date */
encodingMethod = 14;
}
}
}
}
}
 
if (inputData[17] == '9') {
/* Methods 5 and 6 */
if (inputData[18] == '2' && inputData[19] >= '0' && inputData[19] <= '3') {
/* (01) and (392x) */
encodingMethod = 5;
}
if (inputData[18] == '3' && inputData[19] >= '0' && inputData[19] <= '3') {
/* (01) and (393x) */
encodingMethod = 6;
}
}
}
 
/* Encoding method - Table 10 */
/* Variable length symbol bit field is just given a place holder (XX) for the time being */
int read_posn;
switch (encodingMethod) {
case 1:
binaryString.append("1XX");
read_posn = 16;
break;
case 2:
binaryString.append("00XX");
read_posn = 0;
break;
case 3:
binaryString.append("0100");
read_posn = inputData.length;
break;
case 4:
binaryString.append("0101");
read_posn = inputData.length;
break;
case 5:
binaryString.append("01100XX");
read_posn = 20;
break;
case 6:
binaryString.append("01101XX");
read_posn = 23;
break;
default: /* modes 7 (0111000) to 14 (0111111) */
binaryString.append("0" + Integer.toBinaryString(56 + encodingMethod - 7));
read_posn = inputData.length;
break;
}
 
/*
* Verify that the data to be placed in the compressed data field is all numeric data before
* carrying out compression
*/
for (i = 0; i < read_posn; i++) {
if (inputData[i] < '0' || inputData[i] > '9') {
/* Something is wrong */
throw new OkapiException("Invalid characters in input data");
}
}
 
/* Now encode the compressed data field */
 
if (encodingMethod == 1) {
/* Encoding method field "1" - general item identification data */
binaryAppend(binaryString, inputData[2] - '0', 4);
for (i = 1; i < 5; i++) {
final int group = parseInt(inputData, i * 3, 3);
binaryAppend(binaryString, group, 10);
}
}
 
if (encodingMethod == 3 || encodingMethod == 4) {
/* Encoding method field "0100" - variable weight item (0,001 kilogram increments) */
/*
* Encoding method field "0101" - variable weight item (0,01 or 0,001 pound increment)
*/
for (i = 1; i < 5; i++) {
final int group = parseInt(inputData, i * 3, 3);
binaryAppend(binaryString, group, 10);
}
int group = parseInt(inputData, 20, 6);
if (encodingMethod == 4 && inputData[19] == '3') {
group += 10_000;
}
binaryAppend(binaryString, group, 15);
}
 
if (encodingMethod == 5 || encodingMethod == 6) {
/* Encoding method field "01100" - variable measure item and price */
/*
* Encoding method "01101" - variable measure item and price with ISO 4217 currency code
*/
for (i = 1; i < 5; i++) {
final int group = parseInt(inputData, i * 3, 3);
binaryAppend(binaryString, group, 10);
}
binaryAppend(binaryString, inputData[19] - '0', 2);
if (encodingMethod == 6) {
final int currency = parseInt(inputData, 20, 3);
binaryAppend(binaryString, currency, 10);
}
}
 
if (encodingMethod >= 7 && encodingMethod <= 14) {
/*
* Encoding method fields "0111000" through "0111111" - variable weight item plus date
*/
for (i = 1; i < 5; i++) {
final int group = parseInt(inputData, i * 3, 3);
binaryAppend(binaryString, group, 10);
}
int weight = inputData[19] - '0';
for (i = 0; i < 5; i++) {
weight *= 10;
weight += inputData[21 + i] - '0';
}
binaryAppend(binaryString, weight, 20);
int date;
if (inputData.length == 34) {
/* Date information is included */
date = parseInt(inputData, 28, 2) * 384;
date += (parseInt(inputData, 30, 2) - 1) * 32;
date += parseInt(inputData, 32, 2);
} else {
date = 38_400;
}
binaryAppend(binaryString, date, 16);
}
 
/*
* The compressed data field has been processed if appropriate - the rest of the data (if
* any) goes into a general-purpose data compaction field
*/
 
final int[] generalField = Arrays.copyOfRange(inputData, read_posn, inputData.length);
 
if (generalField.length != 0) {
 
latch = false;
final EncodeMode[] generalFieldType = new EncodeMode[generalField.length];
 
for (i = 0; i < generalField.length; i++) {
/* Tables 11, 12, 13 - ISO/IEC 646 encodation */
final int c = generalField[i];
EncodeMode mode;
if (c == FNC1) {
// FNC1 can be encoded in any system
mode = EncodeMode.ANY_ENC;
} else if (c >= '0' && c <= '9') {
// numbers can be encoded in any system, but will usually narrow down to numeric
// encodation
mode = EncodeMode.ANY_ENC;
} else if (c >= 'A' && c <= 'Z' || c == '*' || c == ',' || c == '-' || c == '.' || c == '/') {
// alphanumeric encodation or ISO/IEC encodation
mode = EncodeMode.ALPHA_OR_ISO;
} else if (c >= 'a' && c <= 'z' || c == '!' || c == '"' || c == '%' || c == '&' || c == '\'' || c == '(' || c == ')' || c == '+' || c == ':' || c == ';' || c == '<' || c == '='
|| c == '>' || c == '?' || c == '_' || c == ' ') {
// ISO/IEC encodation
mode = EncodeMode.ISOIEC;
} else {
// unable to encode this character
mode = EncodeMode.INVALID_CHAR;
latch = true;
}
generalFieldType[i] = mode;
}
 
if (latch) {
throw new OkapiException("Invalid characters in input data");
}
 
for (i = 0; i < generalField.length - 1; i++) {
if (generalFieldType[i] == EncodeMode.ISOIEC && generalField[i + 1] == FNC1) {
generalFieldType[i + 1] = EncodeMode.ISOIEC;
}
}
 
for (i = 0; i < generalField.length - 1; i++) {
if (generalFieldType[i] == EncodeMode.ALPHA_OR_ISO && generalField[i + 1] == FNC1) {
generalFieldType[i + 1] = EncodeMode.ALPHA_OR_ISO;
}
}
 
latch = applyGeneralFieldRules(generalFieldType); // modifies generalFieldType
 
/* Set initial mode if not NUMERIC */
if (generalFieldType[0] == EncodeMode.ALPHA) {
binaryString.append("0000"); /* Alphanumeric latch */
last_mode = EncodeMode.ALPHA;
}
if (generalFieldType[0] == EncodeMode.ISOIEC) {
binaryString.append("0000"); /* Alphanumeric latch */
binaryString.append("00100"); /* ISO/IEC 646 latch */
last_mode = EncodeMode.ISOIEC;
}
 
i = 0;
do {
switch (generalFieldType[i]) {
case NUMERIC:
if (last_mode != EncodeMode.NUMERIC) {
binaryString.append("000"); /* Numeric latch */
}
if (generalField[i] != FNC1) {
d1 = generalField[i] - '0';
} else {
d1 = 10;
}
if (generalField[i + 1] != FNC1) {
d2 = generalField[i + 1] - '0';
} else {
d2 = 10;
}
value = 11 * d1 + d2 + 8;
binaryAppend(binaryString, value, 7);
i += 2;
last_mode = EncodeMode.NUMERIC;
break;
 
case ALPHA:
if (i != 0) {
if (last_mode == EncodeMode.NUMERIC) {
binaryString.append("0000"); /* Alphanumeric latch */
}
if (last_mode == EncodeMode.ISOIEC) {
binaryString.append("00100"); /* Alphanumeric latch */
}
}
if (generalField[i] >= '0' && generalField[i] <= '9') {
value = generalField[i] - 43;
binaryAppend(binaryString, value, 5);
}
if (generalField[i] >= 'A' && generalField[i] <= 'Z') {
value = generalField[i] - 33;
binaryAppend(binaryString, value, 6);
}
last_mode = EncodeMode.ALPHA;
if (generalField[i] == FNC1) {
binaryString.append("01111");
// TODO: FNC1 should act as an implicit numeric latch, so the commented out
// line below should be correct, but ZXing cannot
// read barcodes which use FNC1 as an implicit numeric latch... so for now,
// and in order to achieve widest compatibility,
// we waste 3 bits and don't perform the implicit mode change (see
// https://sourceforge.net/p/zint/tickets/145/)
// last_mode = EncodeMode.NUMERIC;
} /* FNC1 / Numeric latch */
if (generalField[i] == '*') {
binaryString.append("111010"); /* asterisk */
}
if (generalField[i] == ',') {
binaryString.append("111011"); /* comma */
}
if (generalField[i] == '-') {
binaryString.append("111100"); /* minus or hyphen */
}
if (generalField[i] == '.') {
binaryString.append("111101"); /* period or full stop */
}
if (generalField[i] == '/') {
binaryString.append("111110"); /* slash or solidus */
}
i++;
break;
 
case ISOIEC:
if (i != 0) {
if (last_mode == EncodeMode.NUMERIC) {
binaryString.append("0000"); /* Alphanumeric latch */
binaryString.append("00100"); /* ISO/IEC 646 latch */
}
if (last_mode == EncodeMode.ALPHA) {
binaryString.append("00100"); /* ISO/IEC 646 latch */
}
}
if (generalField[i] >= '0' && generalField[i] <= '9') {
value = generalField[i] - 43;
binaryAppend(binaryString, value, 5);
}
if (generalField[i] >= 'A' && generalField[i] <= 'Z') {
value = generalField[i] - 1;
binaryAppend(binaryString, value, 7);
}
if (generalField[i] >= 'a' && generalField[i] <= 'z') {
value = generalField[i] - 7;
binaryAppend(binaryString, value, 7);
}
last_mode = EncodeMode.ISOIEC;
if (generalField[i] == FNC1) {
binaryString.append("01111");
// TODO: FNC1 should act as an implicit numeric latch, so the commented out
// line below should be correct, but ZXing cannot
// read barcodes which use FNC1 as an implicit numeric latch... so for now,
// and in order to achieve widest compatibility,
// we waste 3 bits and don't perform the implicit mode change (see
// https://sourceforge.net/p/zint/tickets/145/)
// last_mode = EncodeMode.NUMERIC;
} /* FNC1 / Numeric latch */
if (generalField[i] == '!') {
binaryString.append("11101000"); /* exclamation mark */
}
if (generalField[i] == 34) {
binaryString.append("11101001"); /* quotation mark */
}
if (generalField[i] == 37) {
binaryString.append("11101010"); /* percent sign */
}
if (generalField[i] == '&') {
binaryString.append("11101011"); /* ampersand */
}
if (generalField[i] == 39) {
binaryString.append("11101100"); /* apostrophe */
}
if (generalField[i] == '(') {
binaryString.append("11101101"); /* left parenthesis */
}
if (generalField[i] == ')') {
binaryString.append("11101110"); /* right parenthesis */
}
if (generalField[i] == '*') {
binaryString.append("11101111"); /* asterisk */
}
if (generalField[i] == '+') {
binaryString.append("11110000"); /* plus sign */
}
if (generalField[i] == ',') {
binaryString.append("11110001"); /* comma */
}
if (generalField[i] == '-') {
binaryString.append("11110010"); /* minus or hyphen */
}
if (generalField[i] == '.') {
binaryString.append("11110011"); /* period or full stop */
}
if (generalField[i] == '/') {
binaryString.append("11110100"); /* slash or solidus */
}
if (generalField[i] == ':') {
binaryString.append("11110101"); /* colon */
}
if (generalField[i] == ';') {
binaryString.append("11110110"); /* semicolon */
}
if (generalField[i] == '<') {
binaryString.append("11110111"); /* less-than sign */
}
if (generalField[i] == '=') {
binaryString.append("11111000"); /* equals sign */
}
if (generalField[i] == '>') {
binaryString.append("11111001"); /* greater-than sign */
}
if (generalField[i] == '?') {
binaryString.append("11111010"); /* question mark */
}
if (generalField[i] == '_') {
binaryString.append("11111011"); /* underline or low line */
}
if (generalField[i] == ' ') {
binaryString.append("11111100"); /* space */
}
i++;
break;
}
 
current_length = i;
if (latch) {
current_length++;
}
 
} while (current_length < generalField.length);
 
remainder = calculateRemainder(binaryString.length());
 
if (latch) {
/* There is still one more numeric digit to encode */
if (last_mode == EncodeMode.NUMERIC) {
if (remainder >= 4 && remainder <= 6) {
value = generalField[i] - '0';
value++;
binaryAppend(binaryString, value, 4);
} else {
d1 = generalField[i] - '0';
d2 = 10;
value = 11 * d1 + d2 + 8;
binaryAppend(binaryString, value, 7);
}
} else {
value = generalField[i] - 43;
binaryAppend(binaryString, value, 5);
}
}
}
 
if (binaryString.length() > 252) {
throw new OkapiException("Input too long");
}
 
remainder = calculateRemainder(binaryString.length());
 
/* Now add padding to binary string (7.2.5.5.4) */
i = remainder;
if (generalField.length != 0 && last_mode == EncodeMode.NUMERIC) {
padstring = "0000";
i -= 4;
} else {
padstring = "";
}
for (; i > 0; i -= 5) {
padstring += "00100";
}
 
binaryString.append(padstring.substring(0, remainder));
 
/* Patch variable length symbol bit field */
char patchEvenOdd, patchSize;
if ((binaryString.length() / 12 + 1 & 1) == 0) {
patchEvenOdd = '0';
} else {
patchEvenOdd = '1';
}
if (binaryString.length() <= 156) {
patchSize = '0';
} else {
patchSize = '1';
}
 
if (encodingMethod == 1) {
binaryString.setCharAt(2, patchEvenOdd);
binaryString.setCharAt(3, patchSize);
}
if (encodingMethod == 2) {
binaryString.setCharAt(3, patchEvenOdd);
binaryString.setCharAt(4, patchSize);
}
if (encodingMethod == 5 || encodingMethod == 6) {
binaryString.setCharAt(6, patchEvenOdd);
binaryString.setCharAt(7, patchSize);
}
 
return encodingMethod;
}
 
private static int calculateRemainder(final int binaryStringLength) {
int remainder = 12 - binaryStringLength % 12;
if (remainder == 12) {
remainder = 0;
}
if (binaryStringLength < 36) {
remainder = 36 - binaryStringLength;
}
return remainder;
}
 
/** Logs binary string as hexadecimal */
private void logBinaryStringInfo(final StringBuilder binaryString) {
 
infoLine("Binary Length: " + binaryString.length());
info("Binary String: ");
 
int nibble = 0;
for (int i = 0; i < binaryString.length(); i++) {
switch (i % 4) {
case 0:
if (binaryString.charAt(i) == '1') {
nibble += 8;
}
break;
case 1:
if (binaryString.charAt(i) == '1') {
nibble += 4;
}
break;
case 2:
if (binaryString.charAt(i) == '1') {
nibble += 2;
}
break;
case 3:
if (binaryString.charAt(i) == '1') {
nibble += 1;
}
info(Integer.toHexString(nibble));
nibble = 0;
break;
}
}
 
if (binaryString.length() % 4 != 0) {
info(Integer.toHexString(nibble));
}
 
infoLine();
}
 
/**
* Attempts to apply encoding rules from sections 7.2.5.5.1 to 7.2.5.5.3 of ISO/IEC 24724:2006
*/
private static boolean applyGeneralFieldRules(final EncodeMode[] generalFieldType) {
 
int block_count, i, j, k;
EncodeMode current, next, last;
final int[] blockLength = new int[200];
final EncodeMode[] blockType = new EncodeMode[200];
 
block_count = 0;
 
blockLength[block_count] = 1;
blockType[block_count] = generalFieldType[0];
 
for (i = 1; i < generalFieldType.length; i++) {
current = generalFieldType[i];
last = generalFieldType[i - 1];
 
if (current == last) {
blockLength[block_count] = blockLength[block_count] + 1;
} else {
block_count++;
blockLength[block_count] = 1;
blockType[block_count] = generalFieldType[i];
}
}
 
block_count++;
 
for (i = 0; i < block_count; i++) {
current = blockType[i];
next = blockType[i + 1];
 
if (current == EncodeMode.ISOIEC && i != block_count - 1) {
if (next == EncodeMode.ANY_ENC && blockLength[i + 1] >= 4) {
blockType[i + 1] = EncodeMode.NUMERIC;
}
if (next == EncodeMode.ANY_ENC && blockLength[i + 1] < 4) {
blockType[i + 1] = EncodeMode.ISOIEC;
}
if (next == EncodeMode.ALPHA_OR_ISO && blockLength[i + 1] >= 5) {
blockType[i + 1] = EncodeMode.ALPHA;
}
if (next == EncodeMode.ALPHA_OR_ISO && blockLength[i + 1] < 5) {
blockType[i + 1] = EncodeMode.ISOIEC;
}
}
 
if (current == EncodeMode.ALPHA_OR_ISO) {
blockType[i] = EncodeMode.ALPHA;
current = EncodeMode.ALPHA;
}
 
if (current == EncodeMode.ALPHA && i != block_count - 1) {
if (next == EncodeMode.ANY_ENC && blockLength[i + 1] >= 6) {
blockType[i + 1] = EncodeMode.NUMERIC;
}
if (next == EncodeMode.ANY_ENC && blockLength[i + 1] < 6) {
if (i == block_count - 2 && blockLength[i + 1] >= 4) {
blockType[i + 1] = EncodeMode.NUMERIC;
} else {
blockType[i + 1] = EncodeMode.ALPHA;
}
}
}
 
if (current == EncodeMode.ANY_ENC) {
blockType[i] = EncodeMode.NUMERIC;
}
}
 
if (block_count > 1) {
i = 1;
while (i < block_count) {
if (blockType[i - 1] == blockType[i]) {
/* bring together */
blockLength[i - 1] = blockLength[i - 1] + blockLength[i];
j = i + 1;
 
/* decrease the list */
while (j < block_count) {
blockLength[j - 1] = blockLength[j];
blockType[j - 1] = blockType[j];
j++;
}
block_count--;
i--;
}
i++;
}
}
 
for (i = 0; i < block_count - 1; i++) {
if (blockType[i] == EncodeMode.NUMERIC && (blockLength[i] & 1) != 0) {
/* Odd size numeric block */
blockLength[i] = blockLength[i] - 1;
blockLength[i + 1] = blockLength[i + 1] + 1;
}
}
 
j = 0;
for (i = 0; i < block_count; i++) {
for (k = 0; k < blockLength[i]; k++) {
generalFieldType[j] = blockType[i];
j++;
}
}
 
if (blockType[block_count - 1] == EncodeMode.NUMERIC && (blockLength[block_count - 1] & 1) != 0) {
/*
* If the last block is numeric and an odd size, further processing needs to be done
* outside this procedure
*/
return true;
} else {
return false;
}
}
 
private static int parseInt(final int[] chars, final int index, final int length) {
int val = 0;
int pow = (int) Math.pow(10, length - 1);
for (int i = 0; i < length; i++) {
final int c = chars[index + i];
val += (c - '0') * pow;
pow /= 10;
}
return val;
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/GridMatrix.java
New file
0,0 → 1,1878
/*
* 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.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
 
/**
* <p>
* Implements Grid Matrix bar code symbology according to AIMD014.
*
* <p>
* Grid Matrix is a matrix symbology which can encode characters in the ISO/IEC 8859-1 (Latin-1)
* character set as well as those in the GB-2312 character set. Input is assumed to be formatted as
* a UTF string.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class GridMatrix extends Symbol {
 
private static final char[] SHIFT_SET = {
/* From Table 7 - Encoding of control characters */
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /*
* NULL
* ->
* SI
*/
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /*
* DLE
* ->
* US
*/
'!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~' };
 
private static final int[] GM_RECOMMEND_CW = { 9, 30, 59, 114, 170, 237, 315, 405, 506, 618, 741, 875, 1021 };
 
private static final int[] GM_MAX_CW = { 11, 40, 79, 146, 218, 305, 405, 521, 650, 794, 953, 1125, 1313 };
 
private static final int[] GM_DATA_CODEWORDS = { 0, 15, 13, 11, 9, 45, 40, 35, 30, 25, 89, 79, 69, 59, 49, 146, 130, 114, 98, 81, 218, 194, 170, 146, 121, 305, 271, 237, 203, 169, 405, 360, 315,
270, 225, 521, 463, 405, 347, 289, 650, 578, 506, 434, 361, 794, 706, 618, 530, 441, 953, 847, 741, 635, 529, 1125, 1000, 875, 750, 625, 1313, 1167, 1021, 875, 729 };
 
private static final int[] GM_N1 = { 18, 50, 98, 81, 121, 113, 113, 116, 121, 126, 118, 125, 122 };
private static final int[] GM_B1 = { 1, 1, 1, 2, 2, 2, 2, 3, 2, 7, 5, 10, 6 };
private static final int[] GM_B2 = { 0, 0, 0, 0, 0, 1, 2, 2, 4, 0, 4, 0, 6 };
 
private static final int[] GM_EBEB = {
/* E1 B3 E2 B4 */
0, 0, 0, 0, // version 1
3, 1, 0, 0, 5, 1, 0, 0, 7, 1, 0, 0, 9, 1, 0, 0, 5, 1, 0, 0, // version 2
10, 1, 0, 0, 15, 1, 0, 0, 20, 1, 0, 0, 25, 1, 0, 0, 9, 1, 0, 0, // version 3
19, 1, 0, 0, 29, 1, 0, 0, 39, 1, 0, 0, 49, 1, 0, 0, 8, 2, 0, 0, // version 4
16, 2, 0, 0, 24, 2, 0, 0, 32, 2, 0, 0, 41, 1, 10, 1, 12, 2, 0, 0, // version 5
24, 2, 0, 0, 36, 2, 0, 0, 48, 2, 0, 0, 61, 1, 60, 1, 11, 3, 0, 0, // version 6
23, 1, 22, 2, 34, 2, 33, 1, 45, 3, 0, 0, 57, 1, 56, 2, 12, 1, 11, 3, // version 7
23, 2, 22, 2, 34, 3, 33, 1, 45, 4, 0, 0, 57, 1, 56, 3, 12, 2, 11, 3, // version 8
23, 5, 0, 0, 35, 3, 34, 2, 47, 1, 46, 4, 58, 4, 57, 1, 12, 6, 0, 0, // version 9
24, 6, 0, 0, 36, 6, 0, 0, 48, 6, 0, 0, 61, 1, 60, 5, 13, 4, 12, 3, // version 10
26, 1, 25, 6, 38, 5, 37, 2, 51, 2, 50, 5, 63, 7, 0, 0, 12, 6, 11, 3, // version 11
24, 4, 23, 5, 36, 2, 35, 7, 47, 9, 0, 0, 59, 7, 58, 2, 13, 5, 12, 5, // version 12
25, 10, 0, 0, 38, 5, 37, 5, 50, 10, 0, 0, 63, 5, 62, 5, 13, 1, 12, 11, // version 13
25, 3, 24, 9, 37, 5, 36, 7, 49, 7, 48, 5, 61, 9, 60, 3 };
 
private static final int[] GM_MACRO_MATRIX = { 728, 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, 727, 624, 529,
530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 651, 726, 623, 528, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450,
451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 553, 652, 725, 622, 527, 440, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379,
380, 463, 554, 653, 724, 621, 526, 439, 360, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 381, 464, 555, 654, 723, 620, 525, 438, 359, 288,
225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 307, 382, 465, 556, 655, 722, 619, 524, 437, 358, 287, 224, 169, 170, 171, 172, 173, 174, 175, 176, 177,
178, 179, 180, 181, 182, 241, 308, 383, 466, 557, 656, 721, 618, 523, 436, 357, 286, 223, 168, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 183, 242, 309, 384, 467, 558,
657, 720, 617, 522, 435, 356, 285, 222, 167, 120, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 133, 184, 243, 310, 385, 468, 559, 658, 719, 616, 521, 434, 355, 284, 221, 166, 119, 80, 49, 50,
51, 52, 53, 54, 55, 56, 91, 134, 185, 244, 311, 386, 469, 560, 659, 718, 615, 520, 433, 354, 283, 220, 165, 118, 79, 48, 25, 26, 27, 28, 29, 30, 57, 92, 135, 186, 245, 312, 387, 470, 561,
660, 717, 614, 519, 432, 353, 282, 219, 164, 117, 78, 47, 24, 9, 10, 11, 12, 31, 58, 93, 136, 187, 246, 313, 388, 471, 562, 661, 716, 613, 518, 431, 352, 281, 218, 163, 116, 77, 46, 23, 8,
1, 2, 13, 32, 59, 94, 137, 188, 247, 314, 389, 472, 563, 662, 715, 612, 517, 430, 351, 280, 217, 162, 115, 76, 45, 22, 7, 0, 3, 14, 33, 60, 95, 138, 189, 248, 315, 390, 473, 564, 663, 714,
611, 516, 429, 350, 279, 216, 161, 114, 75, 44, 21, 6, 5, 4, 15, 34, 61, 96, 139, 190, 249, 316, 391, 474, 565, 664, 713, 610, 515, 428, 349, 278, 215, 160, 113, 74, 43, 20, 19, 18, 17,
16, 35, 62, 97, 140, 191, 250, 317, 392, 475, 566, 665, 712, 609, 514, 427, 348, 277, 214, 159, 112, 73, 42, 41, 40, 39, 38, 37, 36, 63, 98, 141, 192, 251, 318, 393, 476, 567, 666, 711,
608, 513, 426, 347, 276, 213, 158, 111, 72, 71, 70, 69, 68, 67, 66, 65, 64, 99, 142, 193, 252, 319, 394, 477, 568, 667, 710, 607, 512, 425, 346, 275, 212, 157, 110, 109, 108, 107, 106,
105, 104, 103, 102, 101, 100, 143, 194, 253, 320, 395, 478, 569, 668, 709, 606, 511, 424, 345, 274, 211, 156, 155, 154, 153, 152, 151, 150, 149, 148, 147, 146, 145, 144, 195, 254, 321,
396, 479, 570, 669, 708, 605, 510, 423, 344, 273, 210, 209, 208, 207, 206, 205, 204, 203, 202, 201, 200, 199, 198, 197, 196, 255, 322, 397, 480, 571, 670, 707, 604, 509, 422, 343, 272,
271, 270, 269, 268, 267, 266, 265, 264, 263, 262, 261, 260, 259, 258, 257, 256, 323, 398, 481, 572, 671, 706, 603, 508, 421, 342, 341, 340, 339, 338, 337, 336, 335, 334, 333, 332, 331,
330, 329, 328, 327, 326, 325, 324, 399, 482, 573, 672, 705, 602, 507, 420, 419, 418, 417, 416, 415, 414, 413, 412, 411, 410, 409, 408, 407, 406, 405, 404, 403, 402, 401, 400, 483, 574,
673, 704, 601, 506, 505, 504, 503, 502, 501, 500, 499, 498, 497, 496, 495, 494, 493, 492, 491, 490, 489, 488, 487, 486, 485, 484, 575, 674, 703, 600, 599, 598, 597, 596, 595, 594, 593,
592, 591, 590, 589, 588, 587, 586, 585, 584, 583, 582, 581, 580, 579, 578, 577, 576, 675, 702, 701, 700, 699, 698, 697, 696, 695, 694, 693, 692, 691, 690, 689, 688, 687, 686, 685, 684,
683, 682, 681, 680, 679, 678, 677, 676 };
 
private static final char[] MIXED_ALPHANUM_SET = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', ' ' };
 
private enum Mode {
NULL, GM_NUMBER, GM_LOWER, GM_UPPER, GM_MIXED, GM_CONTROL, GM_BYTE, GM_CHINESE
}
 
private StringBuilder binary;
private final int[] word = new int[1460];
private boolean[] grid;
private Mode appxDnextSection = Mode.NULL;
private Mode appxDlastSection = Mode.NULL;
private int preferredVersion = 0;
private int preferredEccLevel = -1;
 
/**
* Set preferred size, or "version" of the symbol according to the following table. This value
* may be ignored if the data to be encoded does not fit into a symbol of the selected size.
*
* <table summary="Available Grid Matrix symbol sizes">
* <tbody>
* <tr>
* <th>Input</th>
* <th>Size</th>
* </tr>
* <tr>
* <td>1</td>
* <td>18 x 18</td>
* </tr>
* <tr>
* <td>2</td>
* <td>30 x 30</td>
* </tr>
* <tr>
* <td>3</td>
* <td>42 x 42</td>
* </tr>
* <tr>
* <td>4</td>
* <td>54 x 54</td>
* </tr>
* <tr>
* <td>5</td>
* <td>66 x 66</td>
* </tr>
* <tr>
* <td>6</td>
* <td>78 x 78</td>
* </tr>
* <tr>
* <td>7</td>
* <td>90 x 90</td>
* </tr>
* <tr>
* <td>8</td>
* <td>102 x 102</td>
* </tr>
* <tr>
* <td>9</td>
* <td>114 x 114</td>
* </tr>
* <tr>
* <td>10</td>
* <td>126 x 126</td>
* </tr>
* <tr>
* <td>11</td>
* <td>138 x 138</td>
* </tr>
* <tr>
* <td>12</td>
* <td>150 x 150</td>
* </tr>
* <tr>
* <td>13</td>
* <td>162 x 162</td>
* </tr>
* </tbody>
* </table>
*
* @param version symbol version
*/
public void setPreferredVersion(final int version) {
this.preferredVersion = version;
}
 
/**
* Set the preferred amount of the symbol which should be dedicated to error correction data.
* Values should be selected from the following table:
*
* <table summary="Available options for error correction capacity">
* <tbody>
* <tr>
* <th>Mode</th>
* <th>Error Correction Capacity</th>
* </tr>
* <tr>
* <td>1</td>
* <td>Approximately 10%</td>
* </tr>
* <tr>
* <td>2</td>
* <td>Approximately 20%</td>
* </tr>
* <tr>
* <td>3</td>
* <td>Approximately 30%</td>
* </tr>
* <tr>
* <td>4</td>
* <td>Approximately 40%</td>
* </tr>
* <tr>
* <td>5</td>
* <td>Approximately 50%</td>
* </tr>
* </tbody>
* </table>
*
* @param eccLevel error correction level
*/
public void setPreferredEccLevel(final int eccLevel) {
this.preferredEccLevel = eccLevel;
}
 
@Override
protected void encode() {
int size, modules, dark, error_number;
int auto_layers, min_layers, layers, auto_ecc_level, min_ecc_level, ecc_level;
int x, y, i;
int data_cw, input_latch = 0;
int data_max;
int length;
final StringBuilder bin = new StringBuilder();
 
for (i = 0; i < 1460; i++) {
this.word[i] = 0;
}
 
try {
final Charset gb2312 = Charset.forName("GB2312");
if (gb2312.newEncoder().canEncode(this.content)) {
/* GB2312 will work, use Chinese compaction */
final byte[] inputBytes = this.content.getBytes(gb2312);
this.inputData = new int[inputBytes.length];
length = 0;
for (i = 0; i < inputBytes.length; i++) {
if ((inputBytes[i] & 0xFF) >= 0xA1 && (inputBytes[i] & 0xFF) <= 0xF7) {
/* Double byte character */
this.inputData[length] = (inputBytes[i] & 0xFF) * 256 + (inputBytes[i + 1] & 0xFF);
i++;
length++;
} else {
/* Single byte character */
this.inputData[length] = inputBytes[i] & 0xFF;
length++;
}
}
infoLine("Using GB2312 character encoding");
this.eciMode = 29;
} else {
/* GB2312 encoding won't work, use other ECI mode */
eciProcess(); // Get ECI mode
length = this.inputData.length;
}
} catch (final UnsupportedCharsetException e) {
throw new OkapiException("Byte conversion encoding error");
}
 
error_number = encodeGridMatrixBinary(length, this.readerInit);
if (error_number != 0) {
throw new OkapiException("Input data too long");
}
 
/* Determine the size of the symbol */
data_cw = this.binary.length() / 7;
 
auto_layers = 1;
for (i = 0; i < 13; i++) {
if (GM_RECOMMEND_CW[i] < data_cw) {
auto_layers = i + 1;
}
}
 
min_layers = 13;
for (i = 12; i > 0; i--) {
if (GM_MAX_CW[i - 1] >= data_cw) {
min_layers = i;
}
}
layers = auto_layers;
auto_ecc_level = 3;
if (layers == 1) {
auto_ecc_level = 5;
}
if (layers == 2 || layers == 3) {
auto_ecc_level = 4;
}
min_ecc_level = 1;
if (layers == 1) {
min_ecc_level = 4;
}
if (layers == 2 || layers == 3) {
min_ecc_level = 2;
}
ecc_level = auto_ecc_level;
 
if (this.preferredVersion >= 1 && this.preferredVersion <= 13) {
input_latch = 1;
if (this.preferredVersion > min_layers) {
layers = this.preferredVersion;
} else {
layers = min_layers;
}
}
 
if (input_latch == 1) {
auto_ecc_level = 3;
if (layers == 1) {
auto_ecc_level = 5;
}
if (layers == 2 || layers == 3) {
auto_ecc_level = 4;
}
ecc_level = auto_ecc_level;
if (data_cw > GM_DATA_CODEWORDS[5 * (layers - 1) + ecc_level - 1]) {
layers++;
}
}
 
if (input_latch == 0) {
if (this.preferredEccLevel >= 1 && this.preferredEccLevel <= 5) {
if (this.preferredEccLevel > min_ecc_level) {
ecc_level = this.preferredEccLevel;
} else {
ecc_level = min_ecc_level;
}
}
if (data_cw > GM_DATA_CODEWORDS[5 * (layers - 1) + ecc_level - 1]) {
do {
layers++;
} while (data_cw > GM_DATA_CODEWORDS[5 * (layers - 1) + ecc_level - 1] && layers <= 13);
}
}
 
data_max = 1313;
switch (ecc_level) {
case 2:
data_max = 1167;
break;
case 3:
data_max = 1021;
break;
case 4:
data_max = 875;
break;
case 5:
data_max = 729;
break;
}
 
if (data_cw > data_max) {
throw new OkapiException("Input data too long");
}
 
addErrorCorrection(data_cw, layers, ecc_level);
size = 6 + layers * 12;
modules = 1 + layers * 2;
 
infoLine("Layers: " + layers);
infoLine("ECC Level: " + ecc_level);
infoLine("Data Codewords: " + data_cw);
infoLine("ECC Codewords: " + GM_DATA_CODEWORDS[(layers - 1) * 5 + ecc_level - 1]);
infoLine("Grid Size: " + modules + " X " + modules);
 
this.grid = new boolean[size * size];
 
for (x = 0; x < size; x++) {
for (y = 0; y < size; y++) {
this.grid[y * size + x] = false;
}
}
 
placeDataInGrid(modules, size);
addLayerId(size, layers, modules, ecc_level);
 
/* Add macromodule frames */
for (x = 0; x < modules; x++) {
dark = 1 - (x & 1);
for (y = 0; y < modules; y++) {
if (dark == 1) {
for (i = 0; i < 5; i++) {
this.grid[y * 6 * size + x * 6 + i] = true;
this.grid[(y * 6 + 5) * size + x * 6 + i] = true;
this.grid[(y * 6 + i) * size + x * 6] = true;
this.grid[(y * 6 + i) * size + x * 6 + 5] = true;
}
this.grid[(y * 6 + 5) * size + x * 6 + 5] = true;
dark = 0;
} else {
dark = 1;
}
}
}
 
/* Copy values to symbol */
this.symbol_width = size;
this.row_count = size;
this.row_height = new int[this.row_count];
this.pattern = new String[this.row_count];
 
for (x = 0; x < size; x++) {
bin.setLength(0);
for (y = 0; y < size; y++) {
if (this.grid[x * size + y]) {
bin.append('1');
} else {
bin.append('0');
}
}
this.row_height[x] = 1;
this.pattern[x] = bin2pat(bin);
}
}
 
private int encodeGridMatrixBinary(final int length, final boolean reader) {
/*
* Create a binary stream representation of the input data. 7 sets are defined - Chinese
* characters, Numerals, Lower case letters, Upper case letters, Mixed numerals and letters,
* Control characters and 8-bit binary data
*/
int sp, glyph = 0;
Mode current_mode, next_mode, last_mode;
int c1, c2;
boolean done;
int p = 0, ppos;
int punt = 0;
int number_pad_posn;
int byte_count_posn = 0, byte_count = 0;
int shift, i;
final int[] numbuf = new int[3];
final Mode[] modeMap = calculateModeMap(length);
 
this.binary = new StringBuilder();
 
sp = 0;
current_mode = Mode.NULL;
number_pad_posn = 0;
 
info("Encoding: ");
 
if (reader) {
this.binary.append("1010"); /* FNC3 - Reader Initialisation */
info("INIT ");
}
 
if (this.eciMode != 3 && this.eciMode != 29) {
this.binary.append("1100"); /* ECI */
 
if (this.eciMode >= 0 && this.eciMode <= 1023) {
this.binary.append('0');
for (i = 0x200; i > 0; i = i >> 1) {
if ((this.eciMode & i) != 0) {
this.binary.append('1');
} else {
this.binary.append('0');
}
}
}
 
if (this.eciMode >= 1024 && this.eciMode <= 32767) {
this.binary.append("10");
for (i = 0x4000; i > 0; i = i >> 1) {
if ((this.eciMode & i) != 0) {
this.binary.append('1');
} else {
this.binary.append('0');
}
}
}
 
if (this.eciMode >= 32768 && this.eciMode <= 811799) {
this.binary.append("11");
for (i = 0x80000; i > 0; i = i >> 1) {
if ((this.eciMode & i) != 0) {
this.binary.append('1');
} else {
this.binary.append('0');
}
}
}
 
info("ECI ");
infoSpace(this.eciMode);
}
 
do {
next_mode = modeMap[sp];
 
if (next_mode != current_mode) {
switch (current_mode) {
case NULL:
switch (next_mode) {
case GM_CHINESE:
this.binary.append("0001");
break;
case GM_NUMBER:
this.binary.append("0010");
break;
case GM_LOWER:
this.binary.append("0011");
break;
case GM_UPPER:
this.binary.append("0100");
break;
case GM_MIXED:
this.binary.append("0101");
break;
case GM_BYTE:
this.binary.append("0111");
break;
}
break;
case GM_CHINESE:
switch (next_mode) {
case GM_NUMBER:
this.binary.append("1111111100001");
break; // 8161
case GM_LOWER:
this.binary.append("1111111100010");
break; // 8162
case GM_UPPER:
this.binary.append("1111111100011");
break; // 8163
case GM_MIXED:
this.binary.append("1111111100100");
break; // 8164
case GM_BYTE:
this.binary.append("1111111100101");
break; // 8165
}
break;
case GM_NUMBER:
/* add numeric block padding value */
switch (p) {
case 1:
this.binary.insert(number_pad_posn, "10");
break; // 2 pad digits
case 2:
this.binary.insert(number_pad_posn, "01");
break; // 1 pad digit
case 3:
this.binary.insert(number_pad_posn, "00");
break; // 0 pad digits
}
 
switch (next_mode) {
case GM_CHINESE:
this.binary.append("1111111011");
break; // 1019
case GM_LOWER:
this.binary.append("1111111100");
break; // 1020
case GM_UPPER:
this.binary.append("1111111101");
break; // 1021
case GM_MIXED:
this.binary.append("1111111110");
break; // 1022
case GM_BYTE:
this.binary.append("1111111111");
break; // 1023
}
break;
case GM_LOWER:
case GM_UPPER:
switch (next_mode) {
case GM_CHINESE:
this.binary.append("11100");
break; // 28
case GM_NUMBER:
this.binary.append("11101");
break; // 29
case GM_LOWER:
case GM_UPPER:
this.binary.append("11110");
break; // 30
case GM_MIXED:
this.binary.append("1111100");
break; // 124
case GM_BYTE:
this.binary.append("1111110");
break; // 126
}
break;
case GM_MIXED:
switch (next_mode) {
case GM_CHINESE:
this.binary.append("1111110001");
break; // 1009
case GM_NUMBER:
this.binary.append("1111110010");
break; // 1010
case GM_LOWER:
this.binary.append("1111110011");
break; // 1011
case GM_UPPER:
this.binary.append("1111110100");
break; // 1012
case GM_BYTE:
this.binary.append("1111110111");
break; // 1015
}
break;
case GM_BYTE:
/* add byte block length indicator */
addByteCount(byte_count_posn, byte_count);
byte_count = 0;
switch (next_mode) {
case GM_CHINESE:
this.binary.append("0001");
break; // 1
case GM_NUMBER:
this.binary.append("0010");
break; // 2
case GM_LOWER:
this.binary.append("0011");
break; // 3
case GM_UPPER:
this.binary.append("0100");
break; // 4
case GM_MIXED:
this.binary.append("0101");
break; // 5
}
break;
}
 
switch (next_mode) {
case GM_CHINESE:
info("CHIN ");
break;
case GM_NUMBER:
info("NUMB ");
break;
case GM_LOWER:
info("LOWR ");
break;
case GM_UPPER:
info("UPPR ");
break;
case GM_MIXED:
info("MIXD ");
break;
case GM_BYTE:
info("BYTE ");
break;
}
 
}
last_mode = current_mode;
current_mode = next_mode;
 
switch (current_mode) {
case GM_CHINESE:
done = false;
if (this.inputData[sp] > 0xff) {
/* GB2312 character */
c1 = (this.inputData[sp] & 0xff00) >> 8;
c2 = this.inputData[sp] & 0xff;
 
if (c1 >= 0xa0 && c1 <= 0xa9) {
glyph = 0x60 * (c1 - 0xa1) + c2 - 0xa0;
}
if (c1 >= 0xb0 && c1 <= 0xf7) {
glyph = 0x60 * (c1 - 0xb0 + 9) + c2 - 0xa0;
}
done = true;
}
if (!done) {
if (sp != length - 1) {
if (this.inputData[sp] == 13 && this.inputData[sp + 1] == 10) {
/* End of Line */
glyph = 7776;
sp++;
}
done = true;
}
}
if (!done) {
if (sp != length - 1) {
if (this.inputData[sp] >= '0' && this.inputData[sp] <= '9' && this.inputData[sp + 1] >= '0' && this.inputData[sp + 1] <= '9') {
/* Two digits */
glyph = 8033 + 10 * (this.inputData[sp] - '0') + this.inputData[sp + 1] - '0';
sp++;
}
}
}
if (!done) {
/* Byte value */
glyph = 7777 + this.inputData[sp];
}
 
infoSpace(glyph);
 
for (i = 0x1000; i > 0; i = i >> 1) {
if ((glyph & i) != 0) {
this.binary.append('1');
} else {
this.binary.append('0');
}
}
sp++;
break;
 
case GM_NUMBER:
if (last_mode != current_mode) {
/* Reserve a space for numeric digit padding value (2 bits) */
number_pad_posn = this.binary.length();
}
p = 0;
ppos = -1;
 
/*
* Numeric compression can also include certain combinations of non-numeric
* character
*/
numbuf[0] = '0';
numbuf[1] = '0';
numbuf[2] = '0';
do {
if (this.inputData[sp] >= '0' && this.inputData[sp] <= '9') {
numbuf[p] = this.inputData[sp];
p++;
}
switch (this.inputData[sp]) {
case ' ':
case '+':
case '-':
case '.':
case ',':
punt = this.inputData[sp];
ppos = p;
break;
}
if (sp < length - 1) {
if (this.inputData[sp] == 13 && this.inputData[sp + 1] == 10) {
/* <end of line> */
punt = this.inputData[sp];
sp++;
ppos = p;
}
}
sp++;
} while (p < 3 && sp < length);
 
if (ppos != -1) {
switch (punt) {
case ' ':
glyph = 0;
break;
case '+':
glyph = 3;
break;
case '-':
glyph = 6;
break;
case '.':
glyph = 9;
break;
case ',':
glyph = 12;
break;
case 0x13:
glyph = 15;
break;
}
glyph += ppos;
glyph += 1000;
 
infoSpace(glyph);
 
for (i = 0x200; i > 0; i = i >> 1) {
if ((glyph & i) != 0) {
this.binary.append('1');
} else {
this.binary.append('0');
}
}
}
 
glyph = 100 * (numbuf[0] - '0') + 10 * (numbuf[1] - '0') + numbuf[2] - '0';
infoSpace(glyph);
 
for (i = 0x200; i > 0; i = i >> 1) {
if ((glyph & i) != 0) {
this.binary.append('1');
} else {
this.binary.append('0');
}
}
break;
 
case GM_BYTE:
if (last_mode != current_mode) {
/* Reserve space for byte block length indicator (9 bits) */
byte_count_posn = this.binary.length();
}
if (byte_count == 512) {
/*
* Maximum byte block size is 512 bytes. If longer is needed then start a new
* block
*/
addByteCount(byte_count_posn, byte_count);
this.binary.append("0111");
byte_count_posn = this.binary.length();
byte_count = 0;
}
 
glyph = this.inputData[sp];
infoSpace(glyph);
for (i = 0x80; i > 0; i = i >> 1) {
if ((glyph & i) != 0) {
this.binary.append('1');
} else {
this.binary.append('0');
}
}
sp++;
byte_count++;
break;
 
case GM_MIXED:
shift = 1;
if (this.inputData[sp] >= '0' && this.inputData[sp] <= '9') {
shift = 0;
}
if (this.inputData[sp] >= 'A' && this.inputData[sp] <= 'Z') {
shift = 0;
}
if (this.inputData[sp] >= 'a' && this.inputData[sp] <= 'z') {
shift = 0;
}
if (this.inputData[sp] == ' ') {
shift = 0;
}
 
if (shift == 0) {
/* Mixed Mode character */
glyph = positionOf((char) this.inputData[sp], MIXED_ALPHANUM_SET);
infoSpace(glyph);
 
for (i = 0x20; i > 0; i = i >> 1) {
if ((glyph & i) != 0) {
this.binary.append('1');
} else {
this.binary.append('0');
}
}
} else {
/* Shift Mode character */
this.binary.append("1111110110"); /* 1014 - shift indicator */
 
addShiftCharacter(this.inputData[sp]);
}
 
sp++;
break;
 
case GM_UPPER:
shift = 1;
if (this.inputData[sp] >= 'A' && this.inputData[sp] <= 'Z') {
shift = 0;
}
if (this.inputData[sp] == ' ') {
shift = 0;
}
 
if (shift == 0) {
/* Upper Case character */
glyph = positionOf((char) this.inputData[sp], MIXED_ALPHANUM_SET) - 10;
if (glyph == 52) {
// Space character
glyph = 26;
}
infoSpace(glyph);
 
for (i = 0x10; i > 0; i = i >> 1) {
if ((glyph & i) != 0) {
this.binary.append('1');
} else {
this.binary.append('0');
}
}
 
} else {
/* Shift Mode character */
this.binary.append("1111101"); /* 127 - shift indicator */
 
addShiftCharacter(this.inputData[sp]);
}
 
sp++;
break;
 
case GM_LOWER:
shift = 1;
if (this.inputData[sp] >= 'a' && this.inputData[sp] <= 'z') {
shift = 0;
}
if (this.inputData[sp] == ' ') {
shift = 0;
}
 
if (shift == 0) {
/* Lower Case character */
glyph = positionOf((char) this.inputData[sp], MIXED_ALPHANUM_SET) - 36;
infoSpace(glyph);
 
for (i = 0x10; i > 0; i = i >> 1) {
if ((glyph & i) != 0) {
this.binary.append('1');
} else {
this.binary.append('0');
}
}
 
} else {
/* Shift Mode character */
this.binary.append("1111101"); /* 127 - shift indicator */
 
addShiftCharacter(this.inputData[sp]);
}
 
sp++;
break;
}
if (this.binary.length() > 9191) {
return 1;
}
 
} while (sp < length);
 
infoLine();
 
if (current_mode == Mode.GM_NUMBER) {
/* add numeric block padding value */
switch (p) {
case 1:
this.binary.insert(number_pad_posn, "10");
break; // 2 pad digits
case 2:
this.binary.insert(number_pad_posn, "01");
break; // 1 pad digit
case 3:
this.binary.insert(number_pad_posn, "00");
break; // 0 pad digits
}
}
 
if (current_mode == Mode.GM_BYTE) {
/* Add byte block length indicator */
addByteCount(byte_count_posn, byte_count);
}
 
/* Add "end of data" character */
switch (current_mode) {
case GM_CHINESE:
this.binary.append("1111111100000");
break; // 8160
case GM_NUMBER:
this.binary.append("1111111010");
break; // 1018
case GM_LOWER:
case GM_UPPER:
this.binary.append("11011");
break; // 27
case GM_MIXED:
this.binary.append("1111110000");
break; // 1008
case GM_BYTE:
this.binary.append("0000");
break; // 0
}
 
/* Add padding bits if required */
p = 7 - this.binary.length() % 7;
if (p == 7) {
p = 0;
}
for (i = 0; i < p; i++) {
this.binary.append('0');
}
 
if (this.binary.length() > 9191) {
return 1;
}
 
return 0;
}
 
private Mode[] calculateModeMap(final int length) {
final Mode[] modeMap = new Mode[length];
int i;
int digitStart, digitLength;
boolean digits;
int spaceStart, spaceLength;
boolean spaces;
int[] segmentLength;
Mode[] segmentType;
int[] segmentStart;
int segmentCount;
 
// Step 1
// Characters in GB2312 are encoded as Chinese characters
for (i = 0; i < length; i++) {
modeMap[i] = Mode.NULL;
if (this.inputData[i] > 0xFF) {
modeMap[i] = Mode.GM_CHINESE;
}
}
 
// Consecutive <end of line> characters, if preceeded by or followed
// by chinese characters, are encoded as chinese characters.
if (length > 3) {
i = 1;
do {
if (this.inputData[i] == 13 && this.inputData[i + 1] == 10) {
// End of line (CR/LF)
 
if (modeMap[i - 1] == Mode.GM_CHINESE) {
modeMap[i] = Mode.GM_CHINESE;
modeMap[i + 1] = Mode.GM_CHINESE;
}
i += 2;
} else {
i++;
}
} while (i < length - 1);
 
i = length - 3;
do {
if (this.inputData[i] == 13 && this.inputData[i + 1] == 10) {
// End of line (CR/LF)
if (modeMap[i + 2] == Mode.GM_CHINESE) {
modeMap[i] = Mode.GM_CHINESE;
modeMap[i + 1] = Mode.GM_CHINESE;
}
i -= 2;
} else {
i--;
}
} while (i > 0);
}
 
// Digit pairs between chinese characters encode as chinese characters.
digits = false;
digitLength = 0;
digitStart = 0;
for (i = 1; i < length - 1; i++) {
if (this.inputData[i] >= 48 && this.inputData[i] <= 57) {
// '0' to '9'
if (digits == false) {
digits = true;
digitLength = 1;
digitStart = i;
} else {
digitLength++;
}
} else {
if (digits == true) {
if (digitLength % 2 == 0) {
if (modeMap[digitStart - 1] == Mode.GM_CHINESE && modeMap[i] == Mode.GM_CHINESE) {
for (int j = 0; j < digitLength; j++) {
modeMap[i - j - 1] = Mode.GM_CHINESE;
}
}
}
digits = false;
}
}
}
 
// Step 2: all characters 'a' to 'z' are lowercase.
for (i = 0; i < length; i++) {
if (this.inputData[i] >= 97 && this.inputData[i] <= 122) {
modeMap[i] = Mode.GM_LOWER;
}
}
 
// Step 3: all characters 'A' to 'Z' are uppercase.
for (i = 0; i < length; i++) {
if (this.inputData[i] >= 65 && this.inputData[i] <= 90) {
modeMap[i] = Mode.GM_UPPER;
}
}
 
// Step 4: find consecutive <space> characters preceeded or followed
// by uppercase or lowercase.
spaces = false;
spaceLength = 0;
spaceStart = 0;
for (i = 1; i < length - 1; i++) {
if (this.inputData[i] == 32) {
if (spaces == false) {
spaces = true;
spaceLength = 1;
spaceStart = i;
} else {
spaceLength++;
}
} else {
if (spaces == true) {
 
final Mode modeX = modeMap[spaceStart - 1];
final Mode modeY = modeMap[i];
 
if (modeX == Mode.GM_LOWER || modeX == Mode.GM_UPPER) {
for (int j = 0; j < spaceLength; j++) {
modeMap[i - j - 1] = modeX;
}
} else {
if (modeY == Mode.GM_LOWER || modeY == Mode.GM_UPPER) {
for (int j = 0; j < spaceLength; j++) {
modeMap[i - j - 1] = modeY;
}
}
}
spaces = false;
}
}
}
 
// Step 5: Unassigned characters '0' to '9' are assigned as numerals.
// Non-numeric characters in table 7 are also assigned as numerals.
for (i = 0; i < length; i++) {
if (modeMap[i] == Mode.NULL) {
if (this.inputData[i] >= 48 && this.inputData[i] <= 57) {
// '0' to '9'
modeMap[i] = Mode.GM_NUMBER;
} else {
switch (this.inputData[i]) {
case 32: // Space
case 43: // '+'
case 45: // '-'
case 46: // "."
case 44: // ","
modeMap[i] = Mode.GM_NUMBER;
break;
case 13: // CR
if (i < length - 1) {
if (this.inputData[i + 1] == 10) { // LF
// <end of line>
modeMap[i] = Mode.GM_NUMBER;
modeMap[i + 1] = Mode.GM_NUMBER;
}
}
}
}
}
}
 
// Step 6: The remining unassigned bytes are assigned as 8-bit binary
for (i = 0; i < length; i++) {
if (modeMap[i] == Mode.NULL) {
modeMap[i] = Mode.GM_BYTE;
}
}
 
// break into segments
segmentLength = new int[length];
segmentType = new Mode[length];
segmentStart = new int[length];
 
segmentCount = 0;
segmentLength[0] = 1;
segmentType[0] = modeMap[0];
segmentStart[0] = 0;
for (i = 1; i < length; i++) {
if (modeMap[i] == modeMap[i - 1]) {
segmentLength[segmentCount]++;
} else {
segmentCount++;
segmentLength[segmentCount] = 1;
segmentType[segmentCount] = modeMap[i];
segmentStart[segmentCount] = i;
}
}
 
// A segment can be a control segment if
// a) It is not the start segment of the data stream
// b) All characters are control characters
// c) The length of the segment is no more than 3
// d) The previous segment is not chinese
if (segmentCount > 1) {
for (i = 1; i < segmentCount; i++) { // (a)
if (segmentLength[i] <= 3 && segmentType[i - 1] != Mode.GM_CHINESE) { // (c) and (d)
boolean controlLatch = true;
for (int j = 0; j < segmentLength[i]; j++) {
boolean thischarLatch = false;
for (int k = 0; k < 63; k++) {
if (this.inputData[segmentStart[i] + j] == SHIFT_SET[k]) {
thischarLatch = true;
}
}
 
if (!thischarLatch) {
// This character is not a control character
controlLatch = false;
}
}
 
if (controlLatch) { // (b)
segmentType[i] = Mode.GM_CONTROL;
}
}
}
}
 
// Stages 7 to 9
if (segmentCount >= 3) {
for (i = 0; i < segmentCount - 1; i++) {
Mode pm, tm, nm, lm;
int tl, nl, ll, position;
boolean lastSegment = false;
 
if (i == 0) {
pm = Mode.NULL;
} else {
pm = segmentType[i - 1];
}
 
tm = segmentType[i];
tl = segmentLength[i];
 
nm = segmentType[i + 1];
nl = segmentLength[i + 1];
 
lm = segmentType[i + 2];
ll = segmentLength[i + 2];
 
position = segmentStart[i];
 
if (i + 2 == segmentCount) {
lastSegment = true;
}
 
segmentType[i] = getBestMode(pm, tm, nm, lm, tl, nl, ll, position, lastSegment);
 
if (segmentType[i] == Mode.GM_CONTROL) {
segmentType[i] = segmentType[i - 1];
}
}
 
segmentType[i] = this.appxDnextSection;
segmentType[i + 1] = this.appxDlastSection;
 
if (segmentType[i] == Mode.GM_CONTROL) {
segmentType[i] = segmentType[i - 1];
}
if (segmentType[i + 1] == Mode.GM_CONTROL) {
segmentType[i + 1] = segmentType[i];
}
 
// Uncomment these lines to override mode selection and generate symbol as shown
// in image D.1 for the test data "AAT2556 电池充电器+降压转换器 200mA至2A tel:86 019 82512738"
// segmentType[9] = gmMode.GM_LOWER;
// segmentType[10] = gmMode.GM_LOWER;
}
 
// Copy segments back to modeMap
for (i = 0; i < segmentCount; i++) {
for (int j = 0; j < segmentLength[i]; j++) {
modeMap[segmentStart[i] + j] = segmentType[i];
}
}
 
return modeMap;
}
 
private boolean isTransitionValid(final Mode previousMode, final Mode thisMode) {
// Filters possible encoding data types from table D.1
boolean isValid = false;
 
switch (previousMode) {
case GM_CHINESE:
switch (thisMode) {
case GM_CHINESE:
case GM_BYTE:
isValid = true;
break;
}
break;
case GM_NUMBER:
switch (thisMode) {
case GM_NUMBER:
case GM_MIXED:
case GM_BYTE:
case GM_CHINESE:
isValid = true;
break;
}
break;
case GM_LOWER:
switch (thisMode) {
case GM_LOWER:
case GM_MIXED:
case GM_BYTE:
case GM_CHINESE:
isValid = true;
break;
}
break;
case GM_UPPER:
switch (thisMode) {
case GM_UPPER:
case GM_MIXED:
case GM_BYTE:
case GM_CHINESE:
isValid = true;
break;
}
break;
case GM_CONTROL:
switch (thisMode) {
case GM_CONTROL:
case GM_BYTE:
case GM_CHINESE:
isValid = true;
break;
}
break;
case GM_BYTE:
switch (thisMode) {
case GM_BYTE:
case GM_CHINESE:
isValid = true;
break;
}
break;
}
 
return isValid;
}
 
private Mode intToMode(final int input) {
Mode retVal;
 
switch (input) {
case 1:
retVal = Mode.GM_CHINESE;
break;
case 2:
retVal = Mode.GM_BYTE;
break;
case 3:
retVal = Mode.GM_CONTROL;
break;
case 4:
retVal = Mode.GM_MIXED;
break;
case 5:
retVal = Mode.GM_UPPER;
break;
case 6:
retVal = Mode.GM_LOWER;
break;
case 7:
retVal = Mode.GM_NUMBER;
break;
default:
retVal = Mode.NULL;
break;
}
 
return retVal;
}
 
private Mode getBestMode(final Mode pm, final Mode tm, final Mode nm, final Mode lm, final int tl, final int nl, final int ll, final int position, final boolean lastSegment) {
int tmi, nmi, lmi;
Mode bestMode = tm;
int binaryLength;
int bestBinaryLength = Integer.MAX_VALUE;
 
for (tmi = 1; tmi < 8; tmi++) {
if (isTransitionValid(tm, intToMode(tmi))) {
for (nmi = 1; nmi < 8; nmi++) {
if (isTransitionValid(nm, intToMode(nmi))) {
for (lmi = 1; lmi < 8; lmi++) {
if (isTransitionValid(lm, intToMode(lmi))) {
binaryLength = getBinaryLength(pm, intToMode(tmi), intToMode(nmi), intToMode(lmi), tl, nl, ll, position, lastSegment);
if (binaryLength <= bestBinaryLength) {
bestMode = intToMode(tmi);
this.appxDnextSection = intToMode(nmi);
this.appxDlastSection = intToMode(lmi);
bestBinaryLength = binaryLength;
}
}
}
}
}
}
}
 
return bestMode;
}
 
private int getBinaryLength(final Mode pm, final Mode tm, final Mode nm, final Mode lm, final int tl, final int nl, final int ll, final int position, final boolean lastSegment) {
 
int binaryLength = getChunkLength(pm, tm, tl, position);
binaryLength += getChunkLength(tm, nm, nl, position + tl);
binaryLength += getChunkLength(nm, lm, ll, position + tl + nl);
 
if (lastSegment) {
switch (lm) {
case GM_CHINESE:
binaryLength += 13;
break;
case GM_NUMBER:
binaryLength += 10;
break;
case GM_LOWER:
case GM_UPPER:
binaryLength += 5;
break;
case GM_MIXED:
binaryLength += 10;
break;
case GM_BYTE:
binaryLength += 4;
break;
}
}
 
return binaryLength;
}
 
private int getChunkLength(final Mode lastMode, final Mode thisMode, final int thisLength, final int position) {
int byteLength;
 
switch (thisMode) {
case GM_CHINESE:
byteLength = calcChineseLength(position, thisLength);
break;
case GM_NUMBER:
byteLength = calcNumberLength(position, thisLength);
break;
case GM_LOWER:
byteLength = 5 * thisLength;
break;
case GM_UPPER:
byteLength = 5 * thisLength;
break;
case GM_MIXED:
byteLength = calcMixedLength(position, thisLength);
break;
case GM_CONTROL:
byteLength = 6 * thisLength;
break;
default:
// case GM_BYTE:
byteLength = calcByteLength(position, thisLength);
break;
}
 
switch (lastMode) {
case NULL:
byteLength += 4;
break;
case GM_CHINESE:
if (thisMode != Mode.GM_CHINESE && thisMode != Mode.GM_CONTROL) {
byteLength += 13;
}
break;
case GM_NUMBER:
if (thisMode != Mode.GM_CHINESE && thisMode != Mode.GM_CONTROL) {
byteLength += 10;
}
break;
case GM_LOWER:
switch (thisMode) {
case GM_CHINESE:
case GM_NUMBER:
case GM_UPPER:
byteLength += 5;
break;
case GM_MIXED:
case GM_CONTROL:
case GM_BYTE:
byteLength += 7;
break;
}
break;
case GM_UPPER:
switch (thisMode) {
case GM_CHINESE:
case GM_NUMBER:
case GM_LOWER:
byteLength += 5;
break;
case GM_MIXED:
case GM_CONTROL:
case GM_BYTE:
byteLength += 7;
break;
}
break;
case GM_MIXED:
if (thisMode != Mode.GM_MIXED) {
byteLength += 10;
}
break;
case GM_BYTE:
if (thisMode != Mode.GM_BYTE) {
byteLength += 4;
}
break;
}
 
if (lastMode != Mode.GM_BYTE && thisMode == Mode.GM_BYTE) {
byteLength += 9;
}
 
if (lastMode != Mode.GM_NUMBER && thisMode == Mode.GM_NUMBER) {
byteLength += 2;
}
 
return byteLength;
}
 
private int calcChineseLength(final int position, final int length) {
int i = 0;
int bits = 0;
 
do {
bits += 13;
 
if (i < length) {
if (this.inputData[position + i] == 13 && this.inputData[position + i + 1] == 10) {
// <end of line>
i++;
}
 
if (this.inputData[position + i] >= 48 && this.inputData[position + i] <= 57 && this.inputData[position + i + 1] >= 48 && this.inputData[position + i + 1] <= 57) {
// two digits
i++;
}
}
i++;
} while (i < length);
 
return bits;
}
 
private int calcMixedLength(final int position, final int length) {
int bits = 0;
int i;
 
for (i = 0; i < length; i++) {
bits += 6;
for (int k = 0; k < 63; k++) {
if (this.inputData[position + i] == SHIFT_SET[k]) {
bits += 10;
}
}
}
 
return bits;
}
 
private int calcNumberLength(final int position, final int length) {
int i;
int bits = 0;
int numbers = 0;
int nonnumbers = 0;
 
for (i = 0; i < length; i++) {
if (this.inputData[position + i] >= 48 && this.inputData[position + i] <= 57) {
numbers++;
} else {
nonnumbers++;
}
 
if (i != 0) {
if (this.inputData[position + i] == 10 && this.inputData[position + i - 1] == 13) {
// <end of line>
nonnumbers--;
}
}
 
if (numbers == 3) {
if (nonnumbers == 1) {
bits += 20;
} else {
bits += 10;
}
if (nonnumbers > 1) {
// Invalid encoding
bits += 100;
}
numbers = 0;
nonnumbers = 0;
}
}
 
if (numbers > 0) {
if (nonnumbers == 1) {
bits += 20;
} else {
bits += 10;
}
}
 
if (nonnumbers > 1) {
// Invalid
bits += 100;
}
 
if (!(this.inputData[position + i - 1] >= 48 && this.inputData[position + i - 1] <= 57)) {
// Data must end with a digit
bits += 100;
}
 
return bits;
}
 
private int calcByteLength(final int position, final int length) {
int i;
int bits = 0;
 
for (i = 0; i < length; i++) {
if (this.inputData[position + i] <= 0xFF) {
bits += 8;
} else {
bits += 16;
}
}
 
return bits;
}
 
private void addByteCount(final int byte_count_posn, final int byte_count) {
/* Add the length indicator for byte encoded blocks */
for (int i = 0; i < 9; i++) {
if ((byte_count & 0x100 >> i) != 0) {
this.binary.insert(byte_count_posn + i, '0');
} else {
this.binary.insert(byte_count_posn + i, '1');
}
}
}
 
void addShiftCharacter(final int shifty) {
/* Add a control character to the data stream */
int i;
int glyph = 0;
 
for (i = 0; i < 64; i++) {
if (SHIFT_SET[i] == shifty) {
glyph = i;
}
}
 
info("SHT/");
infoSpace(glyph);
 
for (i = 0x20; i > 0; i = i >> 1) {
if ((glyph & i) != 0) {
this.binary.append('1');
} else {
this.binary.append('0');
}
}
}
 
private void addErrorCorrection(final int data_posn, final int layers, final int ecc_level) {
int data_cw, i, j, wp;
int n1, b1, n2, b2, e1, b3, e2;
int block_size, data_size, ecc_size;
final int[] data = new int[1320];
final int[] block = new int[130];
final int[] data_block = new int[115];
final int[] ecc_block = new int[70];
final ReedSolomon rs = new ReedSolomon();
 
data_cw = GM_DATA_CODEWORDS[(layers - 1) * 5 + ecc_level - 1];
 
for (i = 0; i < 1320; i++) {
data[i] = 0;
}
 
/* Convert from binary sream to 7-bit codewords */
for (i = 0; i < data_posn; i++) {
for (j = 0; j < 7; j++) {
if (this.binary.charAt(i * 7 + j) == '1') {
data[i] += 0x40 >> j;
}
}
}
 
info("Codewords: ");
for (i = 0; i < data_posn; i++) {
infoSpace(data[i]);
}
infoLine();
 
/* Add padding codewords */
data[data_posn] = 0x00;
for (i = data_posn + 1; i < data_cw; i++) {
if ((i & 1) != 0) {
data[i] = 0x7e;
} else {
data[i] = 0x00;
}
}
 
/* Get block sizes */
n1 = GM_N1[layers - 1];
b1 = GM_B1[layers - 1];
n2 = n1 - 1;
b2 = GM_B2[layers - 1];
e1 = GM_EBEB[(layers - 1) * 20 + (ecc_level - 1) * 4];
b3 = GM_EBEB[(layers - 1) * 20 + (ecc_level - 1) * 4 + 1];
e2 = GM_EBEB[(layers - 1) * 20 + (ecc_level - 1) * 4 + 2];
 
/* Split the data into blocks */
wp = 0;
for (i = 0; i < b1 + b2; i++) {
if (i < b1) {
block_size = n1;
} else {
block_size = n2;
}
if (i < b3) {
ecc_size = e1;
} else {
ecc_size = e2;
}
data_size = block_size - ecc_size;
 
/* printf("block %d/%d: data %d / ecc %d\n", i + 1, (b1 + b2), data_size, ecc_size); */
for (j = 0; j < data_size; j++) {
data_block[j] = data[wp];
wp++;
}
 
/* Calculate ECC data for this block */
rs.init_gf(0x89);
rs.init_code(ecc_size, 1);
rs.encode(data_size, data_block);
for (j = 0; j < ecc_size; j++) {
ecc_block[j] = rs.getResult(j);
}
 
/* Correct error correction data but in reverse order */
for (j = 0; j < data_size; j++) {
block[j] = data_block[j];
}
for (j = 0; j < ecc_size; j++) {
block[j + data_size] = ecc_block[ecc_size - j - 1];
}
 
for (j = 0; j < n2; j++) {
this.word[(b1 + b2) * j + i] = block[j];
}
if (block_size == n1) {
this.word[(b1 + b2) * (n1 - 1) + i] = block[n1 - 1];
}
}
}
 
private void placeDataInGrid(final int modules, final int size) {
int x, y, macromodule, offset;
 
offset = 13 - (modules - 1) / 2;
for (y = 0; y < modules; y++) {
for (x = 0; x < modules; x++) {
macromodule = GM_MACRO_MATRIX[(y + offset) * 27 + x + offset];
placeMacroModule(x, y, this.word[macromodule * 2], this.word[macromodule * 2 + 1], size);
}
}
}
 
void placeMacroModule(final int x, final int y, final int word1, final int word2, final int size) {
int i, j;
 
i = x * 6 + 1;
j = y * 6 + 1;
 
if ((word2 & 0x40) != 0) {
this.grid[j * size + i + 2] = true;
}
if ((word2 & 0x20) != 0) {
this.grid[j * size + i + 3] = true;
}
if ((word2 & 0x10) != 0) {
this.grid[(j + 1) * size + i] = true;
}
if ((word2 & 0x08) != 0) {
this.grid[(j + 1) * size + i + 1] = true;
}
if ((word2 & 0x04) != 0) {
this.grid[(j + 1) * size + i + 2] = true;
}
if ((word2 & 0x02) != 0) {
this.grid[(j + 1) * size + i + 3] = true;
}
if ((word2 & 0x01) != 0) {
this.grid[(j + 2) * size + i] = true;
}
if ((word1 & 0x40) != 0) {
this.grid[(j + 2) * size + i + 1] = true;
}
if ((word1 & 0x20) != 0) {
this.grid[(j + 2) * size + i + 2] = true;
}
if ((word1 & 0x10) != 0) {
this.grid[(j + 2) * size + i + 3] = true;
}
if ((word1 & 0x08) != 0) {
this.grid[(j + 3) * size + i] = true;
}
if ((word1 & 0x04) != 0) {
this.grid[(j + 3) * size + i + 1] = true;
}
if ((word1 & 0x02) != 0) {
this.grid[(j + 3) * size + i + 2] = true;
}
if ((word1 & 0x01) != 0) {
this.grid[(j + 3) * size + i + 3] = true;
}
}
 
private void addLayerId(final int size, final int layers, final int modules, final int ecc_level) {
/* Place the layer ID into each macromodule */
 
int i, j, layer, start, stop;
final int[] layerid = new int[layers + 1];
final int[] id = new int[modules * modules];
 
/* Calculate Layer IDs */
for (i = 0; i <= layers; i++) {
if (ecc_level == 1) {
layerid[i] = 3 - i % 4;
} else {
layerid[i] = (i + 5 - ecc_level) % 4;
}
}
 
for (i = 0; i < modules; i++) {
for (j = 0; j < modules; j++) {
id[i * modules + j] = 0;
}
}
 
/* Calculate which value goes in each macromodule */
start = modules / 2;
stop = modules / 2;
for (layer = 0; layer <= layers; layer++) {
for (i = start; i <= stop; i++) {
id[start * modules + i] = layerid[layer];
id[i * modules + start] = layerid[layer];
id[(modules - start - 1) * modules + i] = layerid[layer];
id[i * modules + modules - start - 1] = layerid[layer];
}
start--;
stop++;
}
 
/* Place the data in the grid */
for (i = 0; i < modules; i++) {
for (j = 0; j < modules; j++) {
if ((id[i * modules + j] & 0x02) != 0) {
this.grid[(i * 6 + 1) * size + j * 6 + 1] = true;
}
if ((id[i * modules + j] & 0x01) != 0) {
this.grid[(i * 6 + 1) * size + j * 6 + 2] = true;
}
}
}
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Code3Of9.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;
 
/**
* <p>
* Implements Code 39 bar code symbology according to ISO/IEC 16388:2007.
*
* <p>
* Input data can be of any length and supports the characters 0-9, A-Z, dash (-), full stop (.),
* space, asterisk (*), dollar ($), slash (/), plus (+) and percent (%). The standard does not
* require a check digit but a modulo-43 check digit can be added if required.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class Code3Of9 extends Symbol {
 
public enum CheckDigit {
NONE, MOD43
}
 
private static final String[] CODE_39 = { "1112212111", "2112111121", "1122111121", "2122111111", "1112211121", "2112211111", "1122211111", "1112112121", "2112112111", "1122112111", "2111121121",
"1121121121", "2121121111", "1111221121", "2111221111", "1121221111", "1111122121", "2111122111", "1121122111", "1111222111", "2111111221", "1121111221", "2121111211", "1111211221",
"2111211211", "1121211211", "1111112221", "2111112211", "1121112211", "1111212211", "2211111121", "1221111121", "2221111111", "1211211121", "2211211111", "1221211111", "1211112121",
"2211112111", "1221112111", "1212121111", "1212111211", "1211121211", "1112121211" };
 
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', '-', '.', ' ', '$', '/', '+', '%' };
 
private CheckDigit checkOption = CheckDigit.NONE;
private double moduleWidthRatio = 2;
 
/**
* Sets the ratio of wide bar width to narrow bar width. Valid values are usually between
* {@code 2} and {@code 3}. The default value is {@code 2}.
*
* @param moduleWidthRatio the ratio of wide bar width to narrow bar width
*/
public void setModuleWidthRatio(final double moduleWidthRatio) {
this.moduleWidthRatio = moduleWidthRatio;
}
 
/**
* Returns the ratio of wide bar width to narrow bar width.
*
* @return the ratio of wide bar width to narrow bar width
*/
public double getModuleWidthRatio() {
return this.moduleWidthRatio;
}
 
/**
* Select addition of optional Modulo-43 check digit or encoding without check digit. By
* default, no check digit is added.
*
* @param checkMode Check digit option.
*/
public void setCheckDigit(final CheckDigit checkMode) {
this.checkOption = checkMode;
}
 
/**
* Returns the check digit mode.
*
* @return the check digit mode
*/
public CheckDigit getCheckDigit() {
return this.checkOption;
}
 
@Override
protected void encode() {
 
if (!this.content.matches("[0-9A-Z\\. \\-$/+%]*")) {
throw new OkapiException("Invalid characters in input");
}
 
final String start = "1211212111";
final String stop = "121121211";
 
final int patternLength = start.length() + stop.length() + 10 * this.content.length() + (this.checkOption == CheckDigit.MOD43 ? 10 : 0);
 
final StringBuilder dest = new StringBuilder(patternLength);
dest.append(start);
 
int counter = 0;
char checkDigit = ' ';
 
for (int i = 0; i < this.content.length(); i++) {
final char c = this.content.charAt(i);
final int index = positionOf(c, LOOKUP);
dest.append(CODE_39[index]);
counter += index;
}
 
if (this.checkOption == CheckDigit.MOD43) {
counter = counter % 43;
checkDigit = LOOKUP[counter];
final int index = positionOf(checkDigit, LOOKUP);
dest.append(CODE_39[index]);
if (checkDigit == ' ') {
// display a space check digit as _, otherwise it looks like an error
checkDigit = '_';
}
infoLine("Check Digit: " + checkDigit);
}
 
dest.append(stop);
 
if (this.checkOption == CheckDigit.MOD43) {
this.readable = "*" + this.content + checkDigit + "*";
} else {
this.readable = "*" + this.content + "*";
}
 
this.pattern = new String[] { dest.toString() };
this.row_count = 1;
this.row_height = new int[] { -1 };
}
 
/** {@inheritDoc} */
@Override
protected double getModuleWidth(final int originalWidth) {
if (originalWidth == 1) {
return 1;
} else {
return this.moduleWidthRatio;
}
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/RoyalMail4State.java
New file
0,0 → 1,122
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
import static uk.org.okapibarcode.util.Arrays.positionOf;
 
import java.awt.geom.Rectangle2D;
import java.util.Locale;
 
/**
* <p>
* Encodes data according to the Royal Mail 4-State Country Code.
*
* <p>
* Data input can consist of numbers 0-9 and letters A-Z and usually includes delivery postcode
* followed by house number. A check digit is calculated and added.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class RoyalMail4State extends Symbol {
 
private static final String[] ROYAL_TABLE = { "TTFF", "TDAF", "TDFA", "DTAF", "DTFA", "DDAA", "TADF", "TFTF", "TFDA", "DATF", "DADA", "DFTA", "TAFD", "TFAD", "TFFT", "DAAD", "DAFT", "DFAT",
"ATDF", "ADTF", "ADDA", "FTTF", "FTDA", "FDTA", "ATFD", "ADAD", "ADFT", "FTAD", "FTFT", "FDAT", "AADD", "AFTD", "AFDT", "FATD", "FADT", "FFTT" };
 
private static final char[] KR_SET = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
'V', 'W', 'X', 'Y', 'Z' };
 
@Override
protected void encode() {
String dest;
int i, top = 0, bottom = 0;
int row, column;
int index;
 
this.content = this.content.toUpperCase(Locale.ENGLISH);
if (!this.content.matches("[0-9A-Z]+")) {
throw new OkapiException("Invalid characters in data");
}
dest = "A";
 
for (i = 0; i < this.content.length(); i++) {
index = positionOf(this.content.charAt(i), KR_SET);
dest += ROYAL_TABLE[index];
top += (index + 1) % 6;
bottom += (index / 6 + 1) % 6;
}
 
/* calculate check digit */
row = top % 6 - 1;
column = bottom % 6 - 1;
if (row == -1) {
row = 5;
}
if (column == -1) {
column = 5;
}
final int check = 6 * row + column;
dest += ROYAL_TABLE[check];
infoLine("Check Digit: " + check);
 
/* Stop character */
dest += "F";
 
infoLine("Encoding: " + dest);
this.readable = "";
this.pattern = new String[1];
this.pattern[0] = dest;
this.row_count = 1;
this.row_height = new int[1];
this.row_height[0] = -1;
}
 
@Override
protected void plotSymbol() {
int xBlock;
int x, y, w, h;
 
this.rectangles.clear();
x = 0;
w = 1;
y = 0;
h = 0;
for (xBlock = 0; xBlock < this.pattern[0].length(); xBlock++) {
switch (this.pattern[0].charAt(xBlock)) {
case 'A':
y = 0;
h = 5;
break;
case 'D':
y = 3;
h = 5;
break;
case 'F':
y = 0;
h = 8;
break;
case 'T':
y = 3;
h = 2;
break;
}
 
final Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h);
this.rectangles.add(rect);
 
x += 2;
}
this.symbol_width = this.pattern[0].length() * 3;
this.symbol_height = 8;
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Code32.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;
 
/**
* <p>
* Implements Code 32, also known as Italian Pharmacode, A variation of Code 39 used by the Italian
* Ministry of Health ("Ministero della Sanità")
*
* <p>
* Requires a numeric input up to 8 digits in length. Check digit is calculated.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class Code32 extends Symbol {
 
private static final char[] TABLE = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y',
'Z' };
 
@Override
protected void encode() {
int i, checksum, checkpart, checkdigit;
int pharmacode, remainder, devisor;
String localstr, risultante;
final int[] codeword = new int[6];
final Code3Of9 c39 = new Code3Of9();
 
if (this.content.length() > 8) {
throw new OkapiException("Input too long");
}
 
if (!this.content.matches("[0-9]+")) {
throw new OkapiException("Invalid characters in input");
}
 
/* Add leading zeros as required */
localstr = "";
for (i = this.content.length(); i < 8; i++) {
localstr += "0";
}
localstr += this.content;
 
/* Calculate the check digit */
checksum = 0;
checkpart = 0;
for (i = 0; i < 4; i++) {
checkpart = Character.getNumericValue(localstr.charAt(i * 2));
checksum += checkpart;
checkpart = 2 * Character.getNumericValue(localstr.charAt(i * 2 + 1));
if (checkpart >= 10) {
checksum += checkpart - 10 + 1;
} else {
checksum += checkpart;
}
}
 
/* Add check digit to data string */
checkdigit = checksum % 10;
final char check = (char) (checkdigit + '0');
localstr += check;
infoLine("Check Digit: " + check);
 
/* Convert string into an integer value */
pharmacode = 0;
for (i = 0; i < localstr.length(); i++) {
pharmacode *= 10;
pharmacode += Character.getNumericValue(localstr.charAt(i));
}
 
/* Convert from decimal to base-32 */
devisor = 33554432;
for (i = 5; i >= 0; i--) {
codeword[i] = pharmacode / devisor;
remainder = pharmacode % devisor;
pharmacode = remainder;
devisor /= 32;
}
 
/* Look up values in 'Tabella di conversione' */
risultante = "";
for (i = 5; i >= 0; i--) {
risultante += TABLE[codeword[i]];
}
 
/* Plot the barcode using Code 39 */
 
this.readable = "A" + localstr;
this.pattern = new String[1];
this.row_count = 1;
this.row_height = new int[] { -1 };
infoLine("Code 39 Equivalent: " + risultante);
 
c39.setContent(risultante);
this.pattern[0] = c39.pattern[0];
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/OkapiException.java
New file
0,0 → 1,36
/*
* 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;
 
/**
* An Okapi Barcode exception.
*
* @author Daniel Gredler
*/
public class OkapiException extends RuntimeException {
 
/** Serial version UID. */
private static final long serialVersionUID = -630504124631140642L;
 
/**
* Creates a new instance.
*
* @param message the error message
*/
public OkapiException(final String message) {
super(message);
}
 
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/UspsOneCode.java
New file
0,0 → 1,458
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
import static uk.org.okapibarcode.backend.HumanReadableLocation.NONE;
import static uk.org.okapibarcode.backend.HumanReadableLocation.TOP;
 
import java.awt.geom.Rectangle2D;
import java.math.BigInteger;
 
/**
* <p>
* Implements USPS OneCode (also known as Intelligent Mail Barcode) according to USPS-B-3200F.
*
* <p>
* OneCode is a fixed length (65-bar) symbol which combines routing and customer information in a
* single symbol. Input data consists of a 20 digit tracking code, followed by a dash (-), followed
* by a delivery point ZIP code which can be 0, 5, 9 or 11 digits in length.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
* @see <a href=
* "https://ribbs.usps.gov/intelligentmail_mailpieces/documents/tech_guides/SPUSPSG.pdf">USPS
* OneCode Specification</a>
*/
public class UspsOneCode extends Symbol {
 
/* The following lookup tables were generated using the code in Appendix C */
 
/** Appendix D Table 1 - 5 of 13 characters */
private static final int[] APPX_D_I = { 0x001F, 0x1F00, 0x002F, 0x1E80, 0x0037, 0x1D80, 0x003B, 0x1B80, 0x003D, 0x1780, 0x003E, 0x0F80, 0x004F, 0x1E40, 0x0057, 0x1D40, 0x005B, 0x1B40, 0x005D,
0x1740, 0x005E, 0x0F40, 0x0067, 0x1CC0, 0x006B, 0x1AC0, 0x006D, 0x16C0, 0x006E, 0x0EC0, 0x0073, 0x19C0, 0x0075, 0x15C0, 0x0076, 0x0DC0, 0x0079, 0x13C0, 0x007A, 0x0BC0, 0x007C, 0x07C0,
0x008F, 0x1E20, 0x0097, 0x1D20, 0x009B, 0x1B20, 0x009D, 0x1720, 0x009E, 0x0F20, 0x00A7, 0x1CA0, 0x00AB, 0x1AA0, 0x00AD, 0x16A0, 0x00AE, 0x0EA0, 0x00B3, 0x19A0, 0x00B5, 0x15A0, 0x00B6,
0x0DA0, 0x00B9, 0x13A0, 0x00BA, 0x0BA0, 0x00BC, 0x07A0, 0x00C7, 0x1C60, 0x00CB, 0x1A60, 0x00CD, 0x1660, 0x00CE, 0x0E60, 0x00D3, 0x1960, 0x00D5, 0x1560, 0x00D6, 0x0D60, 0x00D9, 0x1360,
0x00DA, 0x0B60, 0x00DC, 0x0760, 0x00E3, 0x18E0, 0x00E5, 0x14E0, 0x00E6, 0x0CE0, 0x00E9, 0x12E0, 0x00EA, 0x0AE0, 0x00EC, 0x06E0, 0x00F1, 0x11E0, 0x00F2, 0x09E0, 0x00F4, 0x05E0, 0x00F8,
0x03E0, 0x010F, 0x1E10, 0x0117, 0x1D10, 0x011B, 0x1B10, 0x011D, 0x1710, 0x011E, 0x0F10, 0x0127, 0x1C90, 0x012B, 0x1A90, 0x012D, 0x1690, 0x012E, 0x0E90, 0x0133, 0x1990, 0x0135, 0x1590,
0x0136, 0x0D90, 0x0139, 0x1390, 0x013A, 0x0B90, 0x013C, 0x0790, 0x0147, 0x1C50, 0x014B, 0x1A50, 0x014D, 0x1650, 0x014E, 0x0E50, 0x0153, 0x1950, 0x0155, 0x1550, 0x0156, 0x0D50, 0x0159,
0x1350, 0x015A, 0x0B50, 0x015C, 0x0750, 0x0163, 0x18D0, 0x0165, 0x14D0, 0x0166, 0x0CD0, 0x0169, 0x12D0, 0x016A, 0x0AD0, 0x016C, 0x06D0, 0x0171, 0x11D0, 0x0172, 0x09D0, 0x0174, 0x05D0,
0x0178, 0x03D0, 0x0187, 0x1C30, 0x018B, 0x1A30, 0x018D, 0x1630, 0x018E, 0x0E30, 0x0193, 0x1930, 0x0195, 0x1530, 0x0196, 0x0D30, 0x0199, 0x1330, 0x019A, 0x0B30, 0x019C, 0x0730, 0x01A3,
0x18B0, 0x01A5, 0x14B0, 0x01A6, 0x0CB0, 0x01A9, 0x12B0, 0x01AA, 0x0AB0, 0x01AC, 0x06B0, 0x01B1, 0x11B0, 0x01B2, 0x09B0, 0x01B4, 0x05B0, 0x01B8, 0x03B0, 0x01C3, 0x1870, 0x01C5, 0x1470,
0x01C6, 0x0C70, 0x01C9, 0x1270, 0x01CA, 0x0A70, 0x01CC, 0x0670, 0x01D1, 0x1170, 0x01D2, 0x0970, 0x01D4, 0x0570, 0x01D8, 0x0370, 0x01E1, 0x10F0, 0x01E2, 0x08F0, 0x01E4, 0x04F0, 0x01E8,
0x02F0, 0x020F, 0x1E08, 0x0217, 0x1D08, 0x021B, 0x1B08, 0x021D, 0x1708, 0x021E, 0x0F08, 0x0227, 0x1C88, 0x022B, 0x1A88, 0x022D, 0x1688, 0x022E, 0x0E88, 0x0233, 0x1988, 0x0235, 0x1588,
0x0236, 0x0D88, 0x0239, 0x1388, 0x023A, 0x0B88, 0x023C, 0x0788, 0x0247, 0x1C48, 0x024B, 0x1A48, 0x024D, 0x1648, 0x024E, 0x0E48, 0x0253, 0x1948, 0x0255, 0x1548, 0x0256, 0x0D48, 0x0259,
0x1348, 0x025A, 0x0B48, 0x025C, 0x0748, 0x0263, 0x18C8, 0x0265, 0x14C8, 0x0266, 0x0CC8, 0x0269, 0x12C8, 0x026A, 0x0AC8, 0x026C, 0x06C8, 0x0271, 0x11C8, 0x0272, 0x09C8, 0x0274, 0x05C8,
0x0278, 0x03C8, 0x0287, 0x1C28, 0x028B, 0x1A28, 0x028D, 0x1628, 0x028E, 0x0E28, 0x0293, 0x1928, 0x0295, 0x1528, 0x0296, 0x0D28, 0x0299, 0x1328, 0x029A, 0x0B28, 0x029C, 0x0728, 0x02A3,
0x18A8, 0x02A5, 0x14A8, 0x02A6, 0x0CA8, 0x02A9, 0x12A8, 0x02AA, 0x0AA8, 0x02AC, 0x06A8, 0x02B1, 0x11A8, 0x02B2, 0x09A8, 0x02B4, 0x05A8, 0x02B8, 0x03A8, 0x02C3, 0x1868, 0x02C5, 0x1468,
0x02C6, 0x0C68, 0x02C9, 0x1268, 0x02CA, 0x0A68, 0x02CC, 0x0668, 0x02D1, 0x1168, 0x02D2, 0x0968, 0x02D4, 0x0568, 0x02D8, 0x0368, 0x02E1, 0x10E8, 0x02E2, 0x08E8, 0x02E4, 0x04E8, 0x0307,
0x1C18, 0x030B, 0x1A18, 0x030D, 0x1618, 0x030E, 0x0E18, 0x0313, 0x1918, 0x0315, 0x1518, 0x0316, 0x0D18, 0x0319, 0x1318, 0x031A, 0x0B18, 0x031C, 0x0718, 0x0323, 0x1898, 0x0325, 0x1498,
0x0326, 0x0C98, 0x0329, 0x1298, 0x032A, 0x0A98, 0x032C, 0x0698, 0x0331, 0x1198, 0x0332, 0x0998, 0x0334, 0x0598, 0x0338, 0x0398, 0x0343, 0x1858, 0x0345, 0x1458, 0x0346, 0x0C58, 0x0349,
0x1258, 0x034A, 0x0A58, 0x034C, 0x0658, 0x0351, 0x1158, 0x0352, 0x0958, 0x0354, 0x0558, 0x0361, 0x10D8, 0x0362, 0x08D8, 0x0364, 0x04D8, 0x0383, 0x1838, 0x0385, 0x1438, 0x0386, 0x0C38,
0x0389, 0x1238, 0x038A, 0x0A38, 0x038C, 0x0638, 0x0391, 0x1138, 0x0392, 0x0938, 0x0394, 0x0538, 0x03A1, 0x10B8, 0x03A2, 0x08B8, 0x03A4, 0x04B8, 0x03C1, 0x1078, 0x03C2, 0x0878, 0x03C4,
0x0478, 0x040F, 0x1E04, 0x0417, 0x1D04, 0x041B, 0x1B04, 0x041D, 0x1704, 0x041E, 0x0F04, 0x0427, 0x1C84, 0x042B, 0x1A84, 0x042D, 0x1684, 0x042E, 0x0E84, 0x0433, 0x1984, 0x0435, 0x1584,
0x0436, 0x0D84, 0x0439, 0x1384, 0x043A, 0x0B84, 0x043C, 0x0784, 0x0447, 0x1C44, 0x044B, 0x1A44, 0x044D, 0x1644, 0x044E, 0x0E44, 0x0453, 0x1944, 0x0455, 0x1544, 0x0456, 0x0D44, 0x0459,
0x1344, 0x045A, 0x0B44, 0x045C, 0x0744, 0x0463, 0x18C4, 0x0465, 0x14C4, 0x0466, 0x0CC4, 0x0469, 0x12C4, 0x046A, 0x0AC4, 0x046C, 0x06C4, 0x0471, 0x11C4, 0x0472, 0x09C4, 0x0474, 0x05C4,
0x0487, 0x1C24, 0x048B, 0x1A24, 0x048D, 0x1624, 0x048E, 0x0E24, 0x0493, 0x1924, 0x0495, 0x1524, 0x0496, 0x0D24, 0x0499, 0x1324, 0x049A, 0x0B24, 0x049C, 0x0724, 0x04A3, 0x18A4, 0x04A5,
0x14A4, 0x04A6, 0x0CA4, 0x04A9, 0x12A4, 0x04AA, 0x0AA4, 0x04AC, 0x06A4, 0x04B1, 0x11A4, 0x04B2, 0x09A4, 0x04B4, 0x05A4, 0x04C3, 0x1864, 0x04C5, 0x1464, 0x04C6, 0x0C64, 0x04C9, 0x1264,
0x04CA, 0x0A64, 0x04CC, 0x0664, 0x04D1, 0x1164, 0x04D2, 0x0964, 0x04D4, 0x0564, 0x04E1, 0x10E4, 0x04E2, 0x08E4, 0x0507, 0x1C14, 0x050B, 0x1A14, 0x050D, 0x1614, 0x050E, 0x0E14, 0x0513,
0x1914, 0x0515, 0x1514, 0x0516, 0x0D14, 0x0519, 0x1314, 0x051A, 0x0B14, 0x051C, 0x0714, 0x0523, 0x1894, 0x0525, 0x1494, 0x0526, 0x0C94, 0x0529, 0x1294, 0x052A, 0x0A94, 0x052C, 0x0694,
0x0531, 0x1194, 0x0532, 0x0994, 0x0534, 0x0594, 0x0543, 0x1854, 0x0545, 0x1454, 0x0546, 0x0C54, 0x0549, 0x1254, 0x054A, 0x0A54, 0x054C, 0x0654, 0x0551, 0x1154, 0x0552, 0x0954, 0x0561,
0x10D4, 0x0562, 0x08D4, 0x0583, 0x1834, 0x0585, 0x1434, 0x0586, 0x0C34, 0x0589, 0x1234, 0x058A, 0x0A34, 0x058C, 0x0634, 0x0591, 0x1134, 0x0592, 0x0934, 0x05A1, 0x10B4, 0x05A2, 0x08B4,
0x05C1, 0x1074, 0x05C2, 0x0874, 0x0607, 0x1C0C, 0x060B, 0x1A0C, 0x060D, 0x160C, 0x060E, 0x0E0C, 0x0613, 0x190C, 0x0615, 0x150C, 0x0616, 0x0D0C, 0x0619, 0x130C, 0x061A, 0x0B0C, 0x061C,
0x070C, 0x0623, 0x188C, 0x0625, 0x148C, 0x0626, 0x0C8C, 0x0629, 0x128C, 0x062A, 0x0A8C, 0x062C, 0x068C, 0x0631, 0x118C, 0x0632, 0x098C, 0x0643, 0x184C, 0x0645, 0x144C, 0x0646, 0x0C4C,
0x0649, 0x124C, 0x064A, 0x0A4C, 0x0651, 0x114C, 0x0652, 0x094C, 0x0661, 0x10CC, 0x0662, 0x08CC, 0x0683, 0x182C, 0x0685, 0x142C, 0x0686, 0x0C2C, 0x0689, 0x122C, 0x068A, 0x0A2C, 0x0691,
0x112C, 0x0692, 0x092C, 0x06A1, 0x10AC, 0x06A2, 0x08AC, 0x06C1, 0x106C, 0x06C2, 0x086C, 0x0703, 0x181C, 0x0705, 0x141C, 0x0706, 0x0C1C, 0x0709, 0x121C, 0x070A, 0x0A1C, 0x0711, 0x111C,
0x0712, 0x091C, 0x0721, 0x109C, 0x0722, 0x089C, 0x0741, 0x105C, 0x0742, 0x085C, 0x0781, 0x103C, 0x0782, 0x083C, 0x080F, 0x1E02, 0x0817, 0x1D02, 0x081B, 0x1B02, 0x081D, 0x1702, 0x081E,
0x0F02, 0x0827, 0x1C82, 0x082B, 0x1A82, 0x082D, 0x1682, 0x082E, 0x0E82, 0x0833, 0x1982, 0x0835, 0x1582, 0x0836, 0x0D82, 0x0839, 0x1382, 0x083A, 0x0B82, 0x0847, 0x1C42, 0x084B, 0x1A42,
0x084D, 0x1642, 0x084E, 0x0E42, 0x0853, 0x1942, 0x0855, 0x1542, 0x0856, 0x0D42, 0x0859, 0x1342, 0x085A, 0x0B42, 0x0863, 0x18C2, 0x0865, 0x14C2, 0x0866, 0x0CC2, 0x0869, 0x12C2, 0x086A,
0x0AC2, 0x0871, 0x11C2, 0x0872, 0x09C2, 0x0887, 0x1C22, 0x088B, 0x1A22, 0x088D, 0x1622, 0x088E, 0x0E22, 0x0893, 0x1922, 0x0895, 0x1522, 0x0896, 0x0D22, 0x0899, 0x1322, 0x089A, 0x0B22,
0x08A3, 0x18A2, 0x08A5, 0x14A2, 0x08A6, 0x0CA2, 0x08A9, 0x12A2, 0x08AA, 0x0AA2, 0x08B1, 0x11A2, 0x08B2, 0x09A2, 0x08C3, 0x1862, 0x08C5, 0x1462, 0x08C6, 0x0C62, 0x08C9, 0x1262, 0x08CA,
0x0A62, 0x08D1, 0x1162, 0x08D2, 0x0962, 0x08E1, 0x10E2, 0x0907, 0x1C12, 0x090B, 0x1A12, 0x090D, 0x1612, 0x090E, 0x0E12, 0x0913, 0x1912, 0x0915, 0x1512, 0x0916, 0x0D12, 0x0919, 0x1312,
0x091A, 0x0B12, 0x0923, 0x1892, 0x0925, 0x1492, 0x0926, 0x0C92, 0x0929, 0x1292, 0x092A, 0x0A92, 0x0931, 0x1192, 0x0932, 0x0992, 0x0943, 0x1852, 0x0945, 0x1452, 0x0946, 0x0C52, 0x0949,
0x1252, 0x094A, 0x0A52, 0x0951, 0x1152, 0x0961, 0x10D2, 0x0983, 0x1832, 0x0985, 0x1432, 0x0986, 0x0C32, 0x0989, 0x1232, 0x098A, 0x0A32, 0x0991, 0x1132, 0x09A1, 0x10B2, 0x09C1, 0x1072,
0x0A07, 0x1C0A, 0x0A0B, 0x1A0A, 0x0A0D, 0x160A, 0x0A0E, 0x0E0A, 0x0A13, 0x190A, 0x0A15, 0x150A, 0x0A16, 0x0D0A, 0x0A19, 0x130A, 0x0A1A, 0x0B0A, 0x0A23, 0x188A, 0x0A25, 0x148A, 0x0A26,
0x0C8A, 0x0A29, 0x128A, 0x0A2A, 0x0A8A, 0x0A31, 0x118A, 0x0A43, 0x184A, 0x0A45, 0x144A, 0x0A46, 0x0C4A, 0x0A49, 0x124A, 0x0A51, 0x114A, 0x0A61, 0x10CA, 0x0A83, 0x182A, 0x0A85, 0x142A,
0x0A86, 0x0C2A, 0x0A89, 0x122A, 0x0A91, 0x112A, 0x0AA1, 0x10AA, 0x0AC1, 0x106A, 0x0B03, 0x181A, 0x0B05, 0x141A, 0x0B06, 0x0C1A, 0x0B09, 0x121A, 0x0B11, 0x111A, 0x0B21, 0x109A, 0x0B41,
0x105A, 0x0B81, 0x103A, 0x0C07, 0x1C06, 0x0C0B, 0x1A06, 0x0C0D, 0x1606, 0x0C0E, 0x0E06, 0x0C13, 0x1906, 0x0C15, 0x1506, 0x0C16, 0x0D06, 0x0C19, 0x1306, 0x0C23, 0x1886, 0x0C25, 0x1486,
0x0C26, 0x0C86, 0x0C29, 0x1286, 0x0C31, 0x1186, 0x0C43, 0x1846, 0x0C45, 0x1446, 0x0C49, 0x1246, 0x0C51, 0x1146, 0x0C61, 0x10C6, 0x0C83, 0x1826, 0x0C85, 0x1426, 0x0C89, 0x1226, 0x0C91,
0x1126, 0x0CA1, 0x10A6, 0x0CC1, 0x1066, 0x0D03, 0x1816, 0x0D05, 0x1416, 0x0D09, 0x1216, 0x0D11, 0x1116, 0x0D21, 0x1096, 0x0D41, 0x1056, 0x0D81, 0x1036, 0x0E03, 0x180E, 0x0E05, 0x140E,
0x0E09, 0x120E, 0x0E11, 0x110E, 0x0E21, 0x108E, 0x0E41, 0x104E, 0x0E81, 0x102E, 0x0F01, 0x101E, 0x100F, 0x1E01, 0x1017, 0x1D01, 0x101B, 0x1B01, 0x101D, 0x1701, 0x1027, 0x1C81, 0x102B,
0x1A81, 0x102D, 0x1681, 0x1033, 0x1981, 0x1035, 0x1581, 0x1039, 0x1381, 0x1047, 0x1C41, 0x104B, 0x1A41, 0x104D, 0x1641, 0x1053, 0x1941, 0x1055, 0x1541, 0x1059, 0x1341, 0x1063, 0x18C1,
0x1065, 0x14C1, 0x1069, 0x12C1, 0x1071, 0x11C1, 0x1087, 0x1C21, 0x108B, 0x1A21, 0x108D, 0x1621, 0x1093, 0x1921, 0x1095, 0x1521, 0x1099, 0x1321, 0x10A3, 0x18A1, 0x10A5, 0x14A1, 0x10A9,
0x12A1, 0x10B1, 0x11A1, 0x10C3, 0x1861, 0x10C5, 0x1461, 0x10C9, 0x1261, 0x10D1, 0x1161, 0x1107, 0x1C11, 0x110B, 0x1A11, 0x110D, 0x1611, 0x1113, 0x1911, 0x1115, 0x1511, 0x1119, 0x1311,
0x1123, 0x1891, 0x1125, 0x1491, 0x1129, 0x1291, 0x1131, 0x1191, 0x1143, 0x1851, 0x1145, 0x1451, 0x1149, 0x1251, 0x1183, 0x1831, 0x1185, 0x1431, 0x1189, 0x1231, 0x1207, 0x1C09, 0x120B,
0x1A09, 0x120D, 0x1609, 0x1213, 0x1909, 0x1215, 0x1509, 0x1219, 0x1309, 0x1223, 0x1889, 0x1225, 0x1489, 0x1229, 0x1289, 0x1243, 0x1849, 0x1245, 0x1449, 0x1283, 0x1829, 0x1285, 0x1429,
0x1303, 0x1819, 0x1305, 0x1419, 0x1407, 0x1C05, 0x140B, 0x1A05, 0x140D, 0x1605, 0x1413, 0x1905, 0x1415, 0x1505, 0x1423, 0x1885, 0x1425, 0x1485, 0x1443, 0x1845, 0x1483, 0x1825, 0x1503,
0x1815, 0x1603, 0x180D, 0x1807, 0x1C03, 0x180B, 0x1A03, 0x1813, 0x1903, 0x1823, 0x1883, 0x1843, 0x1445, 0x1249, 0x1151, 0x10E1, 0x0C46, 0x0A4A, 0x0952, 0x08E2, 0x064C, 0x0554, 0x04E4,
0x0358, 0x02E8, 0x01F0 };
 
/** Appendix D Table II - 2 of 13 characters */
private static final int[] APPX_D_II = { 0x0003, 0x1800, 0x0005, 0x1400, 0x0006, 0x0C00, 0x0009, 0x1200, 0x000A, 0x0A00, 0x000C, 0x0600, 0x0011, 0x1100, 0x0012, 0x0900, 0x0014, 0x0500, 0x0018,
0x0300, 0x0021, 0x1080, 0x0022, 0x0880, 0x0024, 0x0480, 0x0028, 0x0280, 0x0030, 0x0180, 0x0041, 0x1040, 0x0042, 0x0840, 0x0044, 0x0440, 0x0048, 0x0240, 0x0050, 0x0140, 0x0060, 0x00C0,
0x0081, 0x1020, 0x0082, 0x0820, 0x0084, 0x0420, 0x0088, 0x0220, 0x0090, 0x0120, 0x0101, 0x1010, 0x0102, 0x0810, 0x0104, 0x0410, 0x0108, 0x0210, 0x0201, 0x1008, 0x0202, 0x0808, 0x0204,
0x0408, 0x0401, 0x1004, 0x0402, 0x0804, 0x0801, 0x1002, 0x1001, 0x0802, 0x0404, 0x0208, 0x0110, 0x00A0 };
 
/** Appendix D Table IV - Bar-to-Character Mapping (reverse lookup) */
private static final int[] APPX_D_IV = { 67, 6, 78, 16, 86, 95, 34, 40, 45, 113, 117, 121, 62, 87, 18, 104, 41, 76, 57, 119, 115, 72, 97, 2, 127, 26, 105, 35, 122, 52, 114, 7, 24, 82, 68, 63, 94,
44, 77, 112, 70, 100, 39, 30, 107, 15, 125, 85, 10, 65, 54, 88, 20, 106, 46, 66, 8, 116, 29, 61, 99, 80, 90, 37, 123, 51, 25, 84, 129, 56, 4, 109, 96, 28, 36, 47, 11, 71, 33, 102, 21, 9,
17, 49, 124, 79, 64, 91, 42, 69, 53, 60, 14, 1, 27, 103, 126, 75, 89, 50, 120, 19, 32, 110, 92, 111, 130, 59, 31, 12, 81, 43, 55, 5, 74, 22, 101, 128, 58, 118, 48, 108, 38, 98, 93, 23, 83,
13, 73, 3 };
 
public UspsOneCode() {
this.default_height = 8;
this.humanReadableLocation = HumanReadableLocation.NONE;
this.humanReadableAlignment = HumanReadableAlignment.LEFT; // spec section 2.4.2
}
 
@Override
protected void encode() {
String zip = "";
String zip_adder;
String tracker = "";
int i, j;
final int length = this.content.length();
BigInteger accum;
BigInteger x_reg;
BigInteger mask;
int usps_crc;
final int[] codeword = new int[10];
final int[] characters = new int[10];
final boolean[] bar_map = new boolean[130];
char c;
 
if (!this.content.matches("[0-9\u002D]+")) {
throw new OkapiException("Invalid characters in input data");
}
 
if (length > 32) {
throw new OkapiException("Input too long");
}
 
/* separate the tracking code from the routing code */
j = 0;
for (i = 0; i < length; i++) {
if (this.content.charAt(i) == '-') {
j = 1;
} else {
if (j == 0) {
/* reading tracker */
tracker += this.content.charAt(i);
} else {
/* reading zip code */
zip += this.content.charAt(i);
}
}
}
 
if (tracker.length() != 20) {
throw new OkapiException("Invalid length tracking code");
}
 
if (zip.length() > 11) {
throw new OkapiException("Invalid ZIP code");
}
 
/* *** Step 1 - Conversion of Data Fields into Binary Data *** */
 
/* Routing code first */
if (zip.length() > 0) {
x_reg = new BigInteger(zip);
} else {
x_reg = new BigInteger("0");
}
 
/* add weight to routing code */
if (zip.length() > 9) {
zip_adder = "1000100001";
} else {
if (zip.length() > 5) {
zip_adder = "100001";
} else {
if (zip.length() > 0) {
zip_adder = "1";
} else {
zip_adder = "0";
}
}
}
 
accum = new BigInteger(zip_adder);
accum = accum.add(x_reg);
accum = accum.multiply(BigInteger.valueOf(10));
accum = accum.add(BigInteger.valueOf(Character.getNumericValue(tracker.charAt(0))));
accum = accum.multiply(BigInteger.valueOf(5));
accum = accum.add(BigInteger.valueOf(Character.getNumericValue(tracker.charAt(1))));
for (i = 2; i < tracker.length(); i++) {
accum = accum.multiply(BigInteger.valueOf(10));
accum = accum.add(BigInteger.valueOf(Character.getNumericValue(tracker.charAt(i))));
}
 
/* *** Step 2 - Generation of 11-bit CRC on Binary Data *** */
 
final int[] byte_array = new int[13];
for (i = 0; i < byte_array.length; i++) {
mask = accum.shiftRight(96 - 8 * i);
mask = mask.and(new BigInteger("255"));
byte_array[i] = mask.intValue();
}
 
usps_crc = USPS_MSB_Math_CRC11GenerateFrameCheckSequence(byte_array);
 
/* *** Step 3 - Conversion from Binary Data to Codewords *** */
 
/* start with codeword J which is base 636 */
x_reg = accum.mod(BigInteger.valueOf(636));
codeword[9] = x_reg.intValue();
accum = accum.subtract(x_reg);
accum = accum.divide(BigInteger.valueOf(636));
 
for (i = 8; i >= 0; i--) {
x_reg = accum.mod(BigInteger.valueOf(1365));
codeword[i] = x_reg.intValue();
accum = accum.subtract(x_reg);
accum = accum.divide(BigInteger.valueOf(1365));
}
 
for (i = 0; i < 9; i++) {
if (codeword[i] == 1365) {
codeword[i] = 0;
}
}
 
/* *** Step 4 - Inserting Additional Information into Codewords *** */
 
codeword[9] = codeword[9] * 2;
 
if (usps_crc >= 1024) {
codeword[0] += 659;
}
 
info("Codewords: ");
for (i = 0; i < 10; i++) {
infoSpace(codeword[i]);
}
infoLine();
 
/* *** Step 5 - Conversion from Codewords to Characters *** */
 
for (i = 0; i < 10; i++) {
if (codeword[i] < 1287) {
characters[i] = APPX_D_I[codeword[i]];
} else {
characters[i] = APPX_D_II[codeword[i] - 1287];
}
}
 
for (i = 0; i < 10; i++) {
if ((usps_crc & 1 << i) != 0) {
characters[i] = 0x1FFF - characters[i];
}
}
 
/* *** Step 6 - Conversion from Characters to the Intelligent Mail Barcode *** */
 
for (i = 0; i < 10; i++) {
for (j = 0; j < 13; j++) {
if ((characters[i] & 1 << j) == 0) {
bar_map[APPX_D_IV[13 * i + j] - 1] = false;
} else {
bar_map[APPX_D_IV[13 * i + j] - 1] = true;
}
}
}
 
this.readable = formatHumanReadableText(this.content);
this.pattern = new String[1];
this.row_count = 1;
this.row_height = new int[1];
this.row_height[0] = -1;
 
this.pattern[0] = "";
for (i = 0; i < 65; i++) {
c = 'T';
if (bar_map[i]) {
c = 'D';
}
if (bar_map[i + 65]) {
c = 'A';
}
if (bar_map[i] && bar_map[i + 65]) {
c = 'F';
}
this.pattern[0] += c;
}
 
infoLine("Encoding: " + this.pattern[0]);
}
 
private static int USPS_MSB_Math_CRC11GenerateFrameCheckSequence(final int[] bytes) {
 
final int generatorPolynomial = 0x0F35;
int frameCheckSequence = 0x07FF;
int data;
int byteIndex, bit;
int byteArrayPtr = 0;
 
/* Do most significant byte skipping the 2 most significant bits */
data = bytes[byteArrayPtr] << 5;
byteArrayPtr++;
for (bit = 2; bit < 8; bit++) {
if (((frameCheckSequence ^ data) & 0x400) != 0) {
frameCheckSequence = frameCheckSequence << 1 ^ generatorPolynomial;
} else {
frameCheckSequence = frameCheckSequence << 1;
}
frameCheckSequence &= 0x7FF;
data <<= 1;
}
 
/* Do rest of the bytes */
for (byteIndex = 1; byteIndex < 13; byteIndex++) {
data = bytes[byteArrayPtr] << 3;
byteArrayPtr++;
for (bit = 0; bit < 8; bit++) {
if (((frameCheckSequence ^ data) & 0x0400) != 0) {
frameCheckSequence = frameCheckSequence << 1 ^ generatorPolynomial;
} else {
frameCheckSequence = frameCheckSequence << 1;
}
frameCheckSequence &= 0x7FF;
data <<= 1;
}
}
 
return frameCheckSequence;
}
 
/**
* <p>
* Formats the barcode content into the correct human-readable format, per section 2.4.3 of the
* spec:
*
* <p>
* The human-readable information, when required, shall consist of the 20-digit tracking code
* and the 5-, 9-, or 11-digit routing code, if present. The fields of the tracking code, as
* defined in 2.1.3, shall be separated with a space added between data fields. When the barcode
* contains a routing code, the 5-digit ZIP Code, the 4-digit add-on, and the remaining 2 digits
* shall be separated with a space added between data fields.
*
* <p>
* Appendix F contains a good overview of the different IMb constructs / formats.
*
* @param content the content to be formatted
* @return the formatted content
*/
protected static String formatHumanReadableText(final String content) {
final StringBuilder hrt = new StringBuilder(50);
boolean mid9 = false; // 9-digit mailer ID instead of 6-digit mailer ID
boolean tracing = true; // STID indicates Origin IMb Tracing Services (050, 052)
boolean pimb = true; // barcode identifier (BI) is 94, indicating pIMb
boolean mpe5 = false; // if MPE = 5, it's a CFS/RFS variant of pIMb
int i = 0;
for (final char c : content.toCharArray()) {
if (c < '0' || c > '9') {
continue;
}
if (i == 5 && c == '9') {
mid9 = true;
}
if (i == 2 && c != '0' || i == 3 && c != '5' || i == 4 && c != '0' && c != '2') {
tracing = false;
}
if (i == 0 && c != '9' || i == 1 && c != '4') {
pimb = false;
}
if (i == 5 && c == '5') {
mpe5 = true;
}
if (i == 2 // BI -> STID
|| i == 5 // STID -> ...
|| i == 6 && pimb || i == 10 && pimb || i == 13 && pimb && !mpe5 || i == 15 && pimb && !mpe5 || i == 11 && !mid9 && !tracing && !pimb || i == 14 && mid9 && !tracing && !pimb
|| i == 20 // ... -> zip-5
|| i == 25 // zip-5 -> zip-4
|| i == 29) { // zip-4 -> zip-2
hrt.append(' ');
}
hrt.append(c);
i++;
}
return hrt.toString().trim();
}
 
@Override
protected void plotSymbol() {
int xBlock, shortHeight, longHeight;
double x, y, w, h;
 
this.rectangles.clear();
this.texts.clear();
 
int baseY;
if (this.humanReadableLocation == TOP) {
baseY = getTheoreticalHumanReadableHeight();
} else {
baseY = 0;
}
 
x = 0;
w = this.moduleWidth;
y = 0;
h = 0;
shortHeight = (int) (0.25 * this.default_height);
longHeight = (int) (0.625 * this.default_height);
for (xBlock = 0; xBlock < this.pattern[0].length(); xBlock++) {
 
switch (this.pattern[0].charAt(xBlock)) {
case 'A':
y = baseY;
h = longHeight;
break;
case 'D':
y = baseY + this.default_height - longHeight;
h = longHeight;
break;
case 'F':
y = baseY;
h = this.default_height;
break;
case 'T':
y = baseY + this.default_height - longHeight;
h = shortHeight;
break;
}
 
final Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h);
this.rectangles.add(rect);
 
x += 2.43 * w;
}
 
this.symbol_width = (int) Math.ceil((this.pattern[0].length() - 1) * 2.43 * w + w); // final
// bar
// doesn't
// need
// extra
// whitespace
this.symbol_height = this.default_height;
 
if (this.humanReadableLocation != NONE && !this.readable.isEmpty()) {
double baseline;
if (this.humanReadableLocation == TOP) {
baseline = this.fontSize;
} else {
baseline = this.symbol_height + this.fontSize;
}
this.texts.add(new TextBox(0, baseline, this.symbol_width, this.readable, this.humanReadableAlignment));
}
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Upc.java
New file
0,0 → 1,411
/*
* 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.Ean.calcDigit;
import static uk.org.okapibarcode.backend.Ean.validateAndPad;
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;
import java.util.Arrays;
 
/**
* <p>
* Implements UPC bar code symbology according to BS EN 797:1996.
*
* <p>
* UPC-A requires an 11 digit article number. The check digit is calculated. UPC-E is a
* zero-compressed version of UPC-A developed for smaller packages. The code requires a 6 digit
* article number (digits 0-9). The check digit is calculated. Also supports Number System 1
* encoding by entering a 7-digit article number stating with the digit 1.
*
* <p>
* EAN-2 and EAN-5 add-on symbols can be added using the '+' character followed by the add-on data.
*
* @author <a href="mailto:jakel2006@me.com">Robert Elliott</a>
*/
public class Upc extends Symbol {
 
public static enum Mode {
UPCA, UPCE
};
 
private static final String[] SET_AC = { "3211", "2221", "2122", "1411", "1132", "1231", "1114", "1312", "1213", "3112" };
 
private static final String[] SET_B = { "1123", "1222", "2212", "1141", "2311", "1321", "4111", "2131", "3121", "2113" };
 
/* Number set for UPC-E symbol (EN Table 4) */
private static final String[] UPC_PARITY_0 = { "BBBAAA", "BBABAA", "BBAABA", "BBAAAB", "BABBAA", "BAABBA", "BAAABB", "BABABA", "BABAAB", "BAABAB" };
 
/* Not covered by BS EN 797 */
private static final String[] UPC_PARITY_1 = { "AAABBB", "AABABB", "AABBAB", "AABBBA", "ABAABB", "ABBAAB", "ABBBAA", "ABABAB", "ABABBA", "ABBABA" };
 
private Mode mode = Mode.UPCA;
private boolean showCheckDigit = true;
private int guardPatternExtraHeight = 5;
private boolean linkageFlag;
private EanUpcAddOn addOn;
 
/** Creates a new instance. */
public Upc() {
this.humanReadableAlignment = HumanReadableAlignment.JUSTIFY;
}
 
/**
* Sets the UPC mode (UPC-A or UPC-E). The default is UPC-A.
*
* @param mode the UPC mode (UPC-A or UPC-E)
*/
public void setMode(final Mode mode) {
this.mode = mode;
}
 
/**
* Returns the UPC mode (UPC-A or UPC-E).
*
* @return the UPC mode (UPC-A or UPC-E)
*/
public Mode getMode() {
return this.mode;
}
 
/**
* Sets whether or not to show the check digit in the human-readable text.
*
* @param showCheckDigit whether or not to show the check digit in the human-readable text
*/
public void setShowCheckDigit(final boolean showCheckDigit) {
this.showCheckDigit = showCheckDigit;
}
 
/**
* Returns whether or not to show the check digit in the human-readable text.
*
* @return whether or not to show the check digit in the human-readable text
*/
public boolean getShowCheckDigit() {
return this.showCheckDigit;
}
 
/**
* 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 UPC data");
}
 
if (this.mode == Mode.UPCA) {
upca();
} else {
upce();
}
}
 
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 upca() {
 
this.content = validateAndPad(this.content, 11);
 
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 < 12; i++) {
if (i == 6) {
dest.append("11111");
}
dest.append(SET_AC[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 upce() {
 
this.content = validateAndPad(this.content, 7);
 
final String expanded = expandToEquivalentUpcA(this.content, true);
infoLine("UPC-A Equivalent: " + expanded);
 
final char check = calcDigit(expanded);
infoLine("Check Digit: " + check);
 
final String hrt = this.content + check;
 
final int numberSystem = getNumberSystem(this.content);
final String[] parityArray = numberSystem == 1 ? UPC_PARITY_1 : UPC_PARITY_0;
final String parity = parityArray[check - '0'];
 
final StringBuilder dest = new StringBuilder("111");
for (int i = 0; i < 6; i++) {
if (parity.charAt(i) == 'A') {
dest.append(SET_AC[this.content.charAt(i + 1) - '0']);
} else { // B
dest.append(SET_B[this.content.charAt(i + 1) - '0']);
}
}
dest.append("111111");
 
this.readable = hrt;
this.pattern = new String[] { dest.toString() };
this.row_count = 1;
this.row_height = new int[] { -1 };
}
 
/**
* Expands the zero-compressed UPC-E code to make a UPC-A equivalent (EN Table 5).
*
* @param content the UPC-E code to expand
* @param validate whether or not to validate the input
* @return the UPC-A equivalent of the specified UPC-E code
*/
protected String expandToEquivalentUpcA(final String content, final boolean validate) {
 
final char[] upce = content.toCharArray();
final char[] upca = new char[11];
Arrays.fill(upca, '0');
upca[0] = upce[0];
upca[1] = upce[1];
upca[2] = upce[2];
 
final char emode = upce[6];
 
switch (emode) {
case '0':
case '1':
case '2':
upca[3] = emode;
upca[8] = upce[3];
upca[9] = upce[4];
upca[10] = upce[5];
break;
case '3':
upca[3] = upce[3];
upca[9] = upce[4];
upca[10] = upce[5];
if (validate && (upce[3] == '0' || upce[3] == '1' || upce[3] == '2')) {
/* Note 1 - "X3 shall not be equal to 0, 1 or 2" */
throw new OkapiException("Invalid UPC-E data");
}
break;
case '4':
upca[3] = upce[3];
upca[4] = upce[4];
upca[10] = upce[5];
if (validate && upce[4] == '0') {
/* Note 2 - "X4 shall not be equal to 0" */
throw new OkapiException("Invalid UPC-E data");
}
break;
default:
upca[3] = upce[3];
upca[4] = upce[4];
upca[5] = upce[5];
upca[10] = emode;
if (validate && upce[5] == '0') {
/* Note 3 - "X5 shall not be equal to 0" */
throw new OkapiException("Invalid UPC-E data");
}
break;
}
 
return new String(upca);
}
 
/** Two number systems can be used: system 0 and system 1. */
private static int getNumberSystem(final String content) {
switch (content.charAt(0)) {
case '0':
return 0;
case '1':
return 1;
default:
throw new OkapiException("Invalid input data");
}
}
 
@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.UPCA) {
if (x < 10 || x > 84 || x > 45 && x < 49) {
h += this.guardPatternExtraHeight;
}
if (this.linkageFlag && (x == 0 || x == 94)) {
h += 2;
y -= 2;
}
} else {
if (x < 4 || x > 45) {
h += this.guardPatternExtraHeight;
}
if (this.linkageFlag && (x == 0 || x == 50)) {
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.UPCA) {
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 { // UPCE
this.rectangles.add(new Rectangle2D.Double(scale(0), 0, scale(1), 2));
this.rectangles.add(new Rectangle2D.Double(scale(50), 0, scale(1), 2));
this.rectangles.add(new Rectangle2D.Double(scale(-1), 2, scale(1), 2));
this.rectangles.add(new Rectangle2D.Double(scale(51), 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.UPCA) {
this.texts.add(new TextBox(scale(-9), baseline, scale(4), this.readable.substring(0, 1), HumanReadableAlignment.RIGHT));
this.texts.add(new TextBox(scale(12), baseline, scale(32), this.readable.substring(1, 6), this.humanReadableAlignment));
this.texts.add(new TextBox(scale(51), baseline, scale(32), this.readable.substring(6, 11), this.humanReadableAlignment));
if (this.showCheckDigit) {
this.texts.add(new TextBox(scale(97), baseline, scale(4), this.readable.substring(11, 12), HumanReadableAlignment.LEFT));
}
} else { // UPCE
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));
if (this.showCheckDigit) {
this.texts.add(new TextBox(scale(53), baseline, scale(4), this.readable.substring(7, 8), HumanReadableAlignment.LEFT));
}
}
} else if (this.humanReadableLocation == TOP) {
final double baseline = this.fontSize;
final int width = this.mode == Mode.UPCA ? 94 : 50;
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/CodablockF.java
New file
0,0 → 1,869
/*
* 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 Codablock-F according to AIM Europe "Uniform Symbology Specification - Codablock F",
* 1995.
*
* <p>
* Codablock-F is a multi-row symbology using Code 128 encoding. It can encode any 8-bit ISO 8859-1
* (Latin-1) data up to approximately 1000 alpha-numeric characters or 2000 numeric digits in
* length.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class CodablockF extends Symbol {
 
private enum Mode {
SHIFTA, LATCHA, SHIFTB, LATCHB, SHIFTC, LATCHC, AORB, ABORC, CANDB, CANDBB
}
 
private enum CfMode {
MODEA, MODEB, MODEC
}
 
/* Annex A Table A.1 */
private static final String[] C_128_TABLE = { "212222", "222122", "222221", "121223", "121322", "131222", "122213", "122312", "132212", "221213", "221312", "231212", "112232", "122132", "122231",
"113222", "123122", "123221", "223211", "221132", "221231", "213212", "223112", "312131", "311222", "321122", "321221", "312212", "322112", "322211", "212123", "212321", "232121",
"111323", "131123", "131321", "112313", "132113", "132311", "211313", "231113", "231311", "112133", "112331", "132131", "113123", "113321", "133121", "313121", "211331", "231131",
"213113", "213311", "213131", "311123", "311321", "331121", "312113", "312311", "332111", "314111", "221411", "431111", "111224", "111422", "121124", "121421", "141122", "141221",
"112214", "112412", "122114", "122411", "142112", "142211", "241211", "221114", "413111", "241112", "134111", "111242", "121142", "121241", "114212", "124112", "124211", "411212",
"421112", "421211", "212141", "214121", "412121", "111143", "111341", "131141", "114113", "114311", "411113", "411311", "113141", "114131", "311141", "411131", "211412", "211214",
"211232", "2331112" };
 
private final int[][] blockmatrix = new int[44][62];
private int columns_needed;
private int rows_needed;
private CfMode final_mode;
private final CfMode[] subset_selector = new CfMode[44];
 
/**
* TODO: It doesn't appear that this symbol should support GS1 (it's not in the GS1 spec and
* Zint doesn't support GS1 with this type of symbology). However, the code below does contain
* GS1 checks, so we'll mark it as supported for now. It's very possible that the code below
* which supports GS1 only does so because it was originally copied from the Code 128 source
* code (just a suspicion, though).
*/
@Override
protected boolean gs1Supported() {
return true;
}
 
@Override
protected void encode() {
 
int input_length, i, j, k;
int min_module_height;
Mode last_mode, this_mode;
double estimate_codelength;
String row_pattern;
final int[] row_indicator = new int[44];
final int[] row_check = new int[44];
int k1_sum, k2_sum;
int k1_check, k2_check;
 
this.final_mode = CfMode.MODEA;
 
if (!this.content.matches("[\u0000-\u00FF]+")) {
throw new OkapiException("Invalid characters in input data");
}
 
this.inputData = toBytes(this.content, StandardCharsets.ISO_8859_1, 0x00);
input_length = this.inputData.length - 1;
 
if (input_length > 5450) {
throw new OkapiException("Input data too long");
}
 
/* Make a guess at how many characters will be needed to encode the data */
estimate_codelength = 0.0;
last_mode = Mode.AORB; /* Codablock always starts with Code A */
for (i = 0; i < input_length; i++) {
this_mode = findSubset(this.inputData[i]);
if (this_mode != last_mode) {
estimate_codelength += 1.0;
}
if (this_mode != Mode.ABORC) {
estimate_codelength += 1.0;
} else {
estimate_codelength += 0.5;
}
if (this.inputData[i] > 127) {
estimate_codelength += 1.0;
}
last_mode = this_mode;
}
 
/* Decide symbol size based on the above guess */
this.rows_needed = (int) (0.5 + Math.sqrt((estimate_codelength + 2) / 1.45));
if (this.rows_needed < 2) {
this.rows_needed = 2;
}
if (this.rows_needed > 44) {
this.rows_needed = 44;
}
this.columns_needed = (int) (estimate_codelength + 2) / this.rows_needed;
if (this.columns_needed < 4) {
this.columns_needed = 4;
}
if (this.columns_needed > 62) {
throw new OkapiException("Input data too long");
}
 
/* Encode the data */
data_encode_blockf();
 
/* Add check digits - Annex F */
k1_sum = 0;
k2_sum = 0;
for (i = 0; i < input_length; i++) {
if (this.inputData[i] == FNC1) {
k1_sum += (i + 1) * 29; /* GS */
k2_sum += i * 29;
} else {
k1_sum += (i + 1) * this.inputData[i];
k2_sum += i * this.inputData[i];
}
}
k1_check = k1_sum % 86;
k2_check = k2_sum % 86;
if (this.final_mode == CfMode.MODEA || this.final_mode == CfMode.MODEB) {
k1_check = k1_check + 64;
if (k1_check > 95) {
k1_check -= 96;
}
k2_check = k2_check + 64;
if (k2_check > 95) {
k2_check -= 96;
}
}
this.blockmatrix[this.rows_needed - 1][this.columns_needed - 2] = k1_check;
this.blockmatrix[this.rows_needed - 1][this.columns_needed - 1] = k2_check;
 
/* Calculate row height (4.6.1.a) */
min_module_height = (int) (0.55 * (this.columns_needed + 3)) + 3;
if (min_module_height < 8) {
min_module_height = 8;
}
 
/* Encode the Row Indicator in the First Row of the Symbol - Table D2 */
if (this.subset_selector[0] == CfMode.MODEC) {
/* Code C */
row_indicator[0] = this.rows_needed - 2;
} else {
/* Code A or B */
row_indicator[0] = this.rows_needed + 62;
 
if (row_indicator[0] > 95) {
row_indicator[0] -= 95;
}
}
 
/* Encode the Row Indicator in the Second and Subsequent Rows of the Symbol - Table D3 */
for (i = 1; i < this.rows_needed; i++) {
/* Note that the second row is row number 1 because counting starts from 0 */
if (this.subset_selector[i] == CfMode.MODEC) {
/* Code C */
row_indicator[i] = i + 42;
} else {
/* Code A or B */
if (i < 6) {
row_indicator[i] = i + 10;
} else {
row_indicator[i] = i + 20;
}
}
}
 
/* Calculate row check digits - Annex E */
for (i = 0; i < this.rows_needed; i++) {
k = 103;
switch (this.subset_selector[i]) {
case MODEA:
k += 98;
break;
case MODEB:
k += 100;
break;
case MODEC:
k += 99;
break;
}
k += 2 * row_indicator[i];
for (j = 0; j < this.columns_needed; j++) {
k += (j + 3) * this.blockmatrix[i][j];
}
row_check[i] = k % 103;
}
 
this.readable = "";
this.row_count = this.rows_needed;
this.pattern = new String[this.row_count];
this.row_height = new int[this.row_count];
 
infoLine("Grid Size: " + this.columns_needed + " X " + this.rows_needed);
infoLine("K1 Check Digit: " + k1_check);
infoLine("K2 Check Digit: " + k2_check);
 
/* Resolve the data into patterns and place in symbol structure */
info("Encoding: ");
for (i = 0; i < this.rows_needed; i++) {
 
row_pattern = "";
/* Start character */
row_pattern += C_128_TABLE[103]; /* Always Start A */
 
switch (this.subset_selector[i]) {
case MODEA:
row_pattern += C_128_TABLE[98];
info("MODEA ");
break;
case MODEB:
row_pattern += C_128_TABLE[100];
info("MODEB ");
break;
case MODEC:
row_pattern += C_128_TABLE[99];
info("MODEC ");
break;
}
row_pattern += C_128_TABLE[row_indicator[i]];
infoSpace(row_indicator[i]);
 
for (j = 0; j < this.columns_needed; j++) {
row_pattern += C_128_TABLE[this.blockmatrix[i][j]];
infoSpace(this.blockmatrix[i][j]);
}
 
row_pattern += C_128_TABLE[row_check[i]];
info("(" + row_check[i] + ") ");
 
/* Stop character */
row_pattern += C_128_TABLE[106];
 
/* Write the information into the symbol */
this.pattern[i] = row_pattern;
this.row_height[i] = 15;
}
infoLine();
 
this.symbol_height = this.rows_needed * 15;
}
 
private Mode findSubset(final int letter) {
Mode mode;
 
if (letter == FNC1) {
mode = Mode.AORB;
} else if (letter <= 31) {
mode = Mode.SHIFTA;
} else if (letter >= 48 && letter <= 57) {
mode = Mode.ABORC;
} else if (letter <= 95) {
mode = Mode.AORB;
} else if (letter <= 127) {
mode = Mode.SHIFTB;
} else if (letter <= 159) {
mode = Mode.SHIFTA;
} else if (letter <= 223) {
mode = Mode.AORB;
} else {
mode = Mode.SHIFTB;
}
 
return mode;
}
 
private void data_encode_blockf() {
 
int i, j, input_position, current_row;
int column_position, c;
CfMode current_mode;
boolean done, exit_status;
final int input_length = this.inputData.length - 1;
 
exit_status = false;
current_row = 0;
current_mode = CfMode.MODEA;
column_position = 0;
input_position = 0;
c = 0;
 
do {
done = false;
/*
* 'done' ensures that the instructions are followed in the correct order for each input
* character
*/
 
if (column_position == 0) {
/* The Beginning of a row */
c = this.columns_needed;
current_mode = character_subset_select(input_position);
this.subset_selector[current_row] = current_mode;
if (current_row == 0 && this.inputDataType == DataType.GS1) {
/* Section 4.4.7.1 */
this.blockmatrix[current_row][column_position] = 102; /* FNC1 */
column_position++;
c--;
}
}
 
if (this.inputData[input_position] == FNC1) {
this.blockmatrix[current_row][column_position] = 102; /* FNC1 */
column_position++;
c--;
input_position++;
done = true;
}
 
if (!done) {
if (c <= 2) {
/* Annex B section 1 rule 1 */
/*
* Ensure that there is sufficient encodation capacity to continue (using the
* rules of Annex B.2).
*/
switch (current_mode) {
case MODEA: /* Table B1 applies */
if (findSubset(this.inputData[input_position]) == Mode.ABORC) {
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]);
column_position++;
c--;
input_position++;
done = true;
}
 
if (findSubset(this.inputData[input_position]) == Mode.SHIFTB && c == 1) {
/* Needs two symbols */
this.blockmatrix[current_row][column_position] = 100; /* Code B */
column_position++;
c--;
done = true;
}
 
if (this.inputData[input_position] >= 244 && !done) {
/* Needs three symbols */
this.blockmatrix[current_row][column_position] = 100; /* Code B */
column_position++;
c--;
if (c == 1) {
this.blockmatrix[current_row][column_position] = 101; /* Code A */
column_position++;
c--;
}
done = true;
}
 
if (this.inputData[input_position] >= 128 && !done && c == 1) {
/* Needs two symbols */
this.blockmatrix[current_row][column_position] = 100; /* Code B */
column_position++;
c--;
done = true;
}
break;
case MODEB: /* Table B2 applies */
if (findSubset(this.inputData[input_position]) == Mode.ABORC) {
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]);
column_position++;
c--;
input_position++;
done = true;
}
 
if (findSubset(this.inputData[input_position]) == Mode.SHIFTA && c == 1) {
/* Needs two symbols */
this.blockmatrix[current_row][column_position] = 101; /* Code A */
column_position++;
c--;
done = true;
}
 
if (this.inputData[input_position] >= 128 && this.inputData[input_position] <= 159 && !done) {
/* Needs three symbols */
this.blockmatrix[current_row][column_position] = 101; /* Code A */
column_position++;
c--;
if (c == 1) {
this.blockmatrix[current_row][column_position] = 100; /* Code B */
column_position++;
c--;
}
done = true;
}
 
if (this.inputData[input_position] >= 160 && !done && c == 1) {
/* Needs two symbols */
this.blockmatrix[current_row][column_position] = 101; /* Code A */
column_position++;
c--;
done = true;
}
break;
case MODEC: /* Table B3 applies */
if (findSubset(this.inputData[input_position]) != Mode.ABORC && c == 1) {
/* Needs two symbols */
this.blockmatrix[current_row][column_position] = 101; /* Code A */
column_position++;
c--;
done = true;
}
 
if (findSubset(this.inputData[input_position]) == Mode.ABORC && findSubset(this.inputData[input_position + 1]) != Mode.ABORC && c == 1) {
/* Needs two symbols */
this.blockmatrix[current_row][column_position] = 101; /* Code A */
column_position++;
c--;
done = true;
}
 
if (this.inputData[input_position] >= 128) {
/* Needs three symbols */
this.blockmatrix[current_row][column_position] = 101; /* Code A */
column_position++;
c--;
if (c == 1) {
this.blockmatrix[current_row][column_position] = 100; /* Code B */
column_position++;
c--;
}
}
break;
}
}
}
 
if (!done) {
if ((findSubset(this.inputData[input_position]) == Mode.AORB || findSubset(this.inputData[input_position]) == Mode.SHIFTA) && current_mode == CfMode.MODEA) {
/* Annex B section 1 rule 2 */
/*
* If in Code Subset A and the next data character can be encoded in Subset A
* encode the next character.
*/
if (this.inputData[input_position] >= 128) {
/* Extended ASCII character */
this.blockmatrix[current_row][column_position] = 101; /* FNC4 */
column_position++;
c--;
}
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]);
column_position++;
c--;
input_position++;
done = true;
}
}
 
if (!done) {
if ((findSubset(this.inputData[input_position]) == Mode.AORB || findSubset(this.inputData[input_position]) == Mode.SHIFTB) && current_mode == CfMode.MODEB) {
/* Annex B section 1 rule 3 */
/*
* If in Code Subset B and the next data character can be encoded in subset B,
* encode the next character.
*/
if (this.inputData[input_position] >= 128) {
/* Extended ASCII character */
this.blockmatrix[current_row][column_position] = 100; /* FNC4 */
column_position++;
c--;
}
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]);
column_position++;
c--;
input_position++;
done = true;
}
}
 
if (!done) {
if (findSubset(this.inputData[input_position]) == Mode.ABORC && findSubset(this.inputData[input_position + 1]) == Mode.ABORC && current_mode == CfMode.MODEC) {
/* Annex B section 1 rule 4 */
/* If in Code Subset C and the next data are 2 digits, encode them. */
this.blockmatrix[current_row][column_position] = (this.inputData[input_position] - '0') * 10 + this.inputData[input_position + 1] - '0';
column_position++;
c--;
input_position += 2;
done = true;
}
}
 
if (!done) {
if ((current_mode == CfMode.MODEA || current_mode == CfMode.MODEB) && (findSubset(this.inputData[input_position]) == Mode.ABORC || this.inputData[input_position] == FNC1)) {
// Count the number of numeric digits
// If 4 or more numeric data characters occur together when in subsets A or B:
// a. If there is an even number of numeric data characters, insert a Code C
// character before the
// first numeric digit to change to subset C.
// b. If there is an odd number of numeric data characters, insert a Code Set C
// character immediately
// after the first numeric digit to change to subset C.
i = 0;
j = 0;
do {
i++;
if (this.inputData[input_position + j] == FNC1) {
i++;
}
j++;
} while (findSubset(this.inputData[input_position + j]) == Mode.ABORC || this.inputData[input_position + j] == FNC1);
i--;
 
if (i >= 4) {
/* Annex B section 1 rule 5 */
if (i % 2 == 1) {
/* Annex B section 1 rule 5a */
this.blockmatrix[current_row][column_position] = 99; /* Code C */
column_position++;
c--;
this.blockmatrix[current_row][column_position] = (this.inputData[input_position] - '0') * 10 + this.inputData[input_position + 1] - '0';
column_position++;
c--;
input_position += 2;
current_mode = CfMode.MODEC;
} else {
/* Annex B section 1 rule 5b */
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]);
column_position++;
c--;
input_position++;
}
done = true;
} else {
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]);
column_position++;
c--;
input_position++;
done = true;
}
}
}
 
if (!done) {
if (current_mode == CfMode.MODEB && findSubset(this.inputData[input_position]) == Mode.SHIFTA) {
/* Annex B section 1 rule 6 */
/*
* When in subset B and an ASCII control character occurs in the data: a. If
* there is a lower case character immediately following the control character,
* insert a Shift character before the control character. b. Otherwise, insert a
* Code A character before the control character to change to subset A.
*/
if (this.inputData[input_position + 1] >= 96 && this.inputData[input_position + 1] <= 127) {
/* Annex B section 1 rule 6a */
this.blockmatrix[current_row][column_position] = 98; /* Shift */
column_position++;
c--;
if (this.inputData[input_position] >= 128) {
/* Extended ASCII character */
this.blockmatrix[current_row][column_position] = 100; /* FNC4 */
column_position++;
c--;
}
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]);
column_position++;
c--;
input_position++;
} else {
/* Annex B section 1 rule 6b */
this.blockmatrix[current_row][column_position] = 101; /* Code A */
column_position++;
c--;
if (this.inputData[input_position] >= 128) {
/* Extended ASCII character */
this.blockmatrix[current_row][column_position] = 100; /* FNC4 */
column_position++;
c--;
}
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]);
column_position++;
c--;
input_position++;
current_mode = CfMode.MODEA;
}
done = true;
}
}
 
if (!done) {
if (current_mode == CfMode.MODEA && findSubset(this.inputData[input_position]) == Mode.SHIFTB) {
/* Annex B section 1 rule 7 */
/*
* When in subset A and a lower case character occurs in the data: a. If
* following that character, a control character occurs in the data before the
* occurrence of another lower case character, insert a Shift character before
* the lower case character. b. Otherwise, insert a Code B character before the
* lower case character to change to subset B.
*/
if (findSubset(this.inputData[input_position + 1]) == Mode.SHIFTA && findSubset(this.inputData[input_position + 2]) == Mode.SHIFTB) {
/* Annex B section 1 rule 7a */
this.blockmatrix[current_row][column_position] = 98; /* Shift */
column_position++;
c--;
if (this.inputData[input_position] >= 128) {
/* Extended ASCII character */
this.blockmatrix[current_row][column_position] = 101; /* FNC4 */
column_position++;
c--;
}
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]);
column_position++;
c--;
input_position++;
} else {
/* Annex B section 1 rule 7b */
this.blockmatrix[current_row][column_position] = 100; /* Code B */
column_position++;
c--;
if (this.inputData[input_position] >= 128) {
/* Extended ASCII character */
this.blockmatrix[current_row][column_position] = 101; /* FNC4 */
column_position++;
c--;
}
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]);
column_position++;
c--;
input_position++;
current_mode = CfMode.MODEB;
}
done = true;
}
}
 
if (!done) {
if (current_mode == CfMode.MODEC && (findSubset(this.inputData[input_position]) != Mode.ABORC || findSubset(this.inputData[input_position + 1]) != Mode.ABORC)) {
/* Annex B section 1 rule 8 */
/*
* When in subset C and a non-numeric character (or a single digit) occurs in
* the data, insert a Code A or Code B character before that character,
* following rules 8a and 8b to determine between code subsets A and B. a. If an
* ASCII control character (eg NUL) occurs in the data before any lower case
* character, use Code A. b. Otherwise use Code B.
*/
if (findSubset(this.inputData[input_position]) == Mode.SHIFTA) {
/* Annex B section 1 rule 8a */
this.blockmatrix[current_row][column_position] = 101; /* Code A */
column_position++;
c--;
if (this.inputData[input_position] >= 128) {
/* Extended ASCII character */
this.blockmatrix[current_row][column_position] = 101; /* FNC4 */
column_position++;
c--;
}
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]);
column_position++;
c--;
input_position++;
current_mode = CfMode.MODEA;
} else {
/* Annex B section 1 rule 8b */
this.blockmatrix[current_row][column_position] = 100; /* Code B */
column_position++;
c--;
if (this.inputData[input_position] >= 128) {
/* Extended ASCII character */
this.blockmatrix[current_row][column_position] = 100; /* FNC4 */
column_position++;
c--;
}
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]);
column_position++;
c--;
input_position++;
current_mode = CfMode.MODEB;
}
done = true;
}
}
 
if (input_position == input_length) {
/* End of data - Annex B rule 5a */
if (c == 1) {
if (current_mode == CfMode.MODEA) {
this.blockmatrix[current_row][column_position] = 100; /* Code B */
current_mode = CfMode.MODEB;
} else {
this.blockmatrix[current_row][column_position] = 101; /* Code A */
current_mode = CfMode.MODEA;
}
column_position++;
c--;
}
 
if (c == 0) {
/* Another row is needed */
column_position = 0;
c = this.columns_needed;
current_row++;
this.subset_selector[current_row] = CfMode.MODEA;
current_mode = CfMode.MODEA;
}
 
if (c > 2) {
/* Fill up the last row */
do {
if (current_mode == CfMode.MODEA) {
this.blockmatrix[current_row][column_position] = 100; /* Code B */
current_mode = CfMode.MODEB;
} else {
this.blockmatrix[current_row][column_position] = 101; /* Code A */
current_mode = CfMode.MODEA;
}
column_position++;
c--;
} while (c > 2);
}
 
/* If (c == 2) { do nothing } */
 
exit_status = true;
this.final_mode = current_mode;
} else {
if (c <= 0) {
/* Start new row - Annex B rule 5b */
column_position = 0;
current_row++;
if (current_row > 43) {
throw new OkapiException("Too many rows.");
}
}
}
 
} while (!exit_status);
 
if (current_row == 0) {
/* fill up the first row */
for (c = column_position; c <= this.columns_needed; c++) {
if (current_mode == CfMode.MODEA) {
this.blockmatrix[current_row][c] = 100; /* Code B */
current_mode = CfMode.MODEB;
} else {
this.blockmatrix[current_row][c] = 101; /* Code A */
current_mode = CfMode.MODEA;
}
}
current_row++;
/* add a second row */
this.subset_selector[current_row] = CfMode.MODEA;
current_mode = CfMode.MODEA;
for (c = 0; c <= this.columns_needed - 2; c++) {
if (current_mode == CfMode.MODEA) {
this.blockmatrix[current_row][c] = 100; /* Code B */
current_mode = CfMode.MODEB;
} else {
this.blockmatrix[current_row][c] = 101; /* Code A */
current_mode = CfMode.MODEA;
}
}
}
 
this.rows_needed = current_row + 1;
}
 
private CfMode character_subset_select(final int input_position) {
 
/* Section 4.5.2 - Determining the Character Subset Selector in a Row */
 
if (this.inputData[input_position] >= '0' && this.inputData[input_position] <= '9') {
/* Rule 1 */
return CfMode.MODEC;
}
 
if (this.inputData[input_position] >= 128 && this.inputData[input_position] <= 160) {
/* Rule 2 (i) */
return CfMode.MODEA;
}
 
if (this.inputData[input_position] >= 0 && this.inputData[input_position] <= 31) {
/* Rule 3 */
return CfMode.MODEA;
}
 
/* Rule 4 */
return CfMode.MODEB;
}
 
private int a3_convert(final int source) {
/* Annex A section 3 */
if (source < 32) {
return source + 64;
}
if (source >= 32 && source <= 127) {
return source - 32;
}
if (source >= 128 && source <= 159) {
return source - 128 + 64;
}
/* if source >= 160 */
return source - 128 - 32;
}
 
@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 = 0;
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(11, y - 1, this.symbol_width - 24, 2);
this.rectangles.add(rect);
}
}
 
/* Add top and bottom binding bars */
final Rectangle2D.Double top = new Rectangle2D.Double(0, 0, this.symbol_width, 2);
this.rectangles.add(top);
final Rectangle2D.Double bottom = new Rectangle2D.Double(0, y - 1, this.symbol_width, 2);
this.rectangles.add(bottom);
this.symbol_height += 1;
}
}
/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>&gt; 10% + 3 codewords</td>
* </tr>
* <tr>
* <td>2</td>
* <td>&gt; 23% + 3 codewords</td>
* </tr>
* <tr>
* <td>3</td>
* <td>&gt; 36% + 3 codewords</td>
* </tr>
* <tr>
* <td>4</td>
* <td>&gt; 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';
}