Dépôt officiel du code source de l'ERP OpenConcerto
/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/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 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 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/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 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 Label/src/org/openconcerto/modules/label/ZPLPrinterPanel.java |
---|
New file |
0,0 → 1,409 |
package org.openconcerto.modules.label; |
import java.awt.FlowLayout; |
import java.awt.GridBagConstraints; |
import java.awt.GridBagLayout; |
import java.awt.event.ActionEvent; |
import java.awt.event.ActionListener; |
import java.awt.print.PrinterJob; |
import java.io.BufferedReader; |
import java.io.DataOutputStream; |
import java.io.File; |
import java.io.FileInputStream; |
import java.io.FileOutputStream; |
import java.io.IOException; |
import java.io.StringReader; |
import java.net.Socket; |
import java.nio.charset.StandardCharsets; |
import java.text.DecimalFormat; |
import java.util.ArrayList; |
import java.util.HashMap; |
import java.util.HashSet; |
import java.util.List; |
import java.util.Map; |
import java.util.Properties; |
import java.util.Set; |
import javax.print.DocFlavor; |
import javax.print.DocPrintJob; |
import javax.print.PrintService; |
import javax.print.SimpleDoc; |
import javax.swing.ButtonGroup; |
import javax.swing.JButton; |
import javax.swing.JFrame; |
import javax.swing.JLabel; |
import javax.swing.JOptionPane; |
import javax.swing.JPanel; |
import javax.swing.JRadioButton; |
import javax.swing.JSpinner; |
import javax.swing.JTextField; |
import javax.swing.SpinnerNumberModel; |
import javax.swing.SwingConstants; |
import javax.swing.SwingUtilities; |
import javax.swing.UIManager; |
import javax.swing.UnsupportedLookAndFeelException; |
import org.openconcerto.sql.model.SQLRowAccessor; |
import org.openconcerto.ui.DefaultGridBagConstraints; |
import org.openconcerto.ui.JLabelBold; |
import org.openconcerto.utils.BaseDirs; |
import org.openconcerto.utils.ExceptionHandler; |
import org.openconcerto.utils.FileUtils; |
import org.openconcerto.utils.ProductInfo; |
public class ZPLPrinterPanel extends JPanel { |
private final HashMap<String, String> mapName = new HashMap<>(); |
private final List<String> variables; |
private final List<String> knownVariables = new ArrayList<>(); |
private Map<String, JTextField> editorMap = new HashMap<>(); |
private String zpl; |
private final Properties properties = new Properties(); |
public static void main(String[] args) throws IOException { |
// final File f = new File("Templates/Labels", "50x50.zpl"); |
final File file = new File("Template/Labels", "57x32.zpl"); |
String zpl = FileUtils.read(file); |
SwingUtilities.invokeLater(new Runnable() { |
@Override |
public void run() { |
try { |
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); |
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) { |
e.printStackTrace(); |
} |
ZPLPrinterPanel p = new ZPLPrinterPanel(zpl); |
p.initUI(null); |
JFrame f = new JFrame(); |
f.setTitle(file.getName()); |
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); |
f.setContentPane(p); |
f.pack(); |
f.setLocationRelativeTo(null); |
f.setVisible(true); |
} |
}); |
} |
public ZPLPrinterPanel(String zpl) { |
this.zpl = zpl; |
this.variables = getVariables(zpl); |
knownVariables.add("product.code"); |
knownVariables.add("product.name"); |
knownVariables.add("product.material"); |
knownVariables.add("product.ean13"); |
knownVariables.add("product.price"); |
knownVariables.add("product.pricewithtax"); |
// |
mapName.put("product.name", "Nom"); |
mapName.put("product.code", "Code"); |
mapName.put("product.ean13", "Code à barres"); |
mapName.put("product.price", "Prix HT"); |
mapName.put("product.pricewithtax", "Prix TTC"); |
mapName.put("product.treatment", "Traitement"); |
mapName.put("product.origin", "Origine"); |
mapName.put("product.batch", "Lot"); |
mapName.put("product.size", "Taille"); |
mapName.put("product.color", "Couleur"); |
mapName.put("product.material", "Matière"); |
} |
private final File getPrefFile() { |
final File prefsFolder = BaseDirs.create(ProductInfo.getInstance()).getPreferencesFolder(); |
if (!prefsFolder.exists()) { |
prefsFolder.mkdirs(); |
} |
return new File(prefsFolder, "labels.properties"); |
} |
protected void initUI(SQLRowAccessor row) { |
final File prefsFolder = BaseDirs.create(ProductInfo.getInstance()).getPreferencesFolder(); |
if (!prefsFolder.exists()) { |
prefsFolder.mkdirs(); |
} |
final File file = getPrefFile(); |
System.out.println(file.getAbsolutePath()); |
if (file.exists()) { |
try { |
properties.load(new FileInputStream(file)); |
} catch (IOException e) { |
e.printStackTrace(); |
} |
} |
this.setLayout(new GridBagLayout()); |
GridBagConstraints c = new DefaultGridBagConstraints(); |
c.fill = GridBagConstraints.HORIZONTAL; |
this.removeAll(); |
// Fields |
Set<String> added = new HashSet<>(); |
for (String v : this.knownVariables) { |
if (variables.contains(v)) { |
// Non editable |
String label = getName(v); |
c.gridx = 0; |
c.weightx = 0; |
this.add(new JLabel(label, SwingConstants.RIGHT), c); |
c.gridx++; |
c.weightx = 1; |
String value = getValueAsString(row, v); |
JTextField txt = new JTextField(20); |
if (value != null) { |
txt.setText(value); |
txt.setEditable(false); |
} |
editorMap.put(v, txt); |
this.add(txt, c); |
added.add(v); |
c.gridy++; |
} |
} |
for (String v : this.variables) { |
if (!added.contains(v)) { |
// Editable |
String label = getName(v); |
c.gridx = 0; |
c.weightx = 0; |
this.add(new JLabel(label, SwingConstants.RIGHT), c); |
c.gridx++; |
c.weightx = 1; |
JTextField txt = new JTextField(20); |
editorMap.put(v, txt); |
this.add(txt, c); |
added.add(v); |
c.gridy++; |
} |
} |
c.gridwidth = 2; |
c.gridx = 0; |
this.add(new JLabelBold("Paramètres d'impression"), c); |
// Printer selector |
c.gridx = 0; |
c.gridy++; |
final JPanel l1 = new JPanel(); |
l1.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 0)); |
l1.add(new JLabel("Nombre d'étiquettes")); |
final JSpinner nbLabels = new JSpinner(new SpinnerNumberModel(1, 1, 1000, 10)); |
l1.add(nbLabels); |
this.add(l1, c); |
// Delay |
l1.add(new JLabel(" Pause entre chaque impression")); |
final JSpinner delayLabels = new JSpinner(new SpinnerNumberModel(800, 100, 10000, 100)); |
l1.add(delayLabels); |
this.add(l1, c); |
l1.add(new JLabel("ms")); |
c.gridy++; |
final JPanel lPrintNetwork = new JPanel(); |
lPrintNetwork.setLayout(new FlowLayout(FlowLayout.LEFT, 2, 0)); |
JRadioButton radioNetworkPrinter = new JRadioButton("imprimante réseau IP :"); |
lPrintNetwork.add(radioNetworkPrinter); |
JTextField textPrinterIP = new JTextField(16); |
lPrintNetwork.add(textPrinterIP); |
lPrintNetwork.add(new JLabel(" Port :")); |
JSpinner portZPL = new JSpinner(new SpinnerNumberModel(9100, 24, 10000, 1)); |
lPrintNetwork.add(portZPL); |
this.add(lPrintNetwork, c); |
c.gridy++; |
final JPanel lPrintLocal = new JPanel(); |
lPrintLocal.setLayout(new FlowLayout(FlowLayout.LEFT, 2, 0)); |
JRadioButton radioLocalPrinter = new JRadioButton("imprimante locale"); |
lPrintLocal.add(radioLocalPrinter); |
radioLocalPrinter.setSelected(true); |
this.add(lPrintLocal, c); |
final ButtonGroup gr = new ButtonGroup(); |
gr.add(radioLocalPrinter); |
gr.add(radioNetworkPrinter); |
c.gridy++; |
c.weighty = 1; |
c.gridx = 1; |
c.fill = GridBagConstraints.NONE; |
c.anchor = GridBagConstraints.SOUTHEAST; |
// Restore state from properties |
if (properties.getOrDefault("printerType", "local").equals("local")) { |
radioLocalPrinter.setSelected(true); |
} else { |
radioNetworkPrinter.setSelected(true); |
} |
textPrinterIP.setText(properties.getOrDefault("printerIp", "").toString()); |
portZPL.setValue(Long.parseLong(properties.getOrDefault("printerPort", "9100").toString())); |
nbLabels.setValue(Long.parseLong(properties.getOrDefault("nbLabels", "1").toString())); |
delayLabels.setValue(Long.parseLong(properties.getOrDefault("delay", "800").toString())); |
JButton printButton = new JButton("Imprimer"); |
this.add(printButton, c); |
printButton.addActionListener(new ActionListener() { |
@Override |
public void actionPerformed(ActionEvent e) { |
try { |
if (radioLocalPrinter.isSelected()) { |
properties.put("printerType", "local"); |
} else { |
properties.put("printerType", "network"); |
properties.put("printerIp", textPrinterIP.getText()); |
properties.put("printerPort", portZPL.getValue().toString()); |
} |
properties.put("nbLabels", nbLabels.getValue().toString()); |
properties.put("delay", delayLabels.getValue().toString()); |
// Save Prefs |
properties.store(new FileOutputStream(getPrefFile()), ""); |
} catch (Exception e1) { |
ExceptionHandler.handle("Erreur de sauvegarde de " + getPrefFile().getAbsolutePath(), e1); |
} |
final String code = createZPLCode(); |
System.out.println("ZPL:"); |
System.out.println(code); |
byte[] data = code.getBytes(StandardCharsets.UTF_8); |
if (radioNetworkPrinter.isSelected()) { |
Socket socket = null; |
try { |
socket = new Socket(textPrinterIP.getText(), ((Number) portZPL.getValue()).intValue()); |
final DataOutputStream out = new DataOutputStream(socket.getOutputStream()); |
final int nb = ((Number) nbLabels.getValue()).intValue(); |
for (int i = 0; i < nb; i++) { |
out.write(data); |
} |
} catch (Exception ex) { |
ex.printStackTrace(); |
JOptionPane.showMessageDialog(printButton, "Erreur d'impression réseau : " + ex.getMessage()); |
} finally { |
if (socket != null) { |
try { |
socket.close(); |
} catch (IOException e1) { |
e1.printStackTrace(); |
} |
} |
} |
} else { |
try { |
final PrinterJob pj1 = PrinterJob.getPrinterJob(); |
if (pj1.printDialog()) { |
final PrintService ps = pj1.getPrintService(); |
final DocPrintJob pj = ps.createPrintJob(); |
final SimpleDoc doc = new SimpleDoc(data, DocFlavor.BYTE_ARRAY.AUTOSENSE, null); |
final int nb = ((Number) nbLabels.getValue()).intValue(); |
for (int i = 0; i < nb; i++) { |
pj.print(doc, null); |
Thread.sleep(((Number) delayLabels.getValue()).intValue()); |
} |
} |
} catch (Exception ex) { |
ex.printStackTrace(); |
JOptionPane.showMessageDialog(printButton, "Erreur d'impression locale : " + ex.getMessage()); |
} |
} |
} |
}); |
} |
private String createZPLCode() { |
final BufferedReader reader = new BufferedReader(new StringReader(this.zpl)); |
final StringBuilder builder = new StringBuilder(); |
try { |
String line = reader.readLine(); |
while (line != null) { |
if (line.contains("${")) { |
boolean add = false; |
for (String v : this.editorMap.keySet()) { |
if (line.contains("${" + v + "}")) { |
final String value = this.editorMap.get(v).getText(); |
line = line.replace("${" + v + "}", value); |
if (!value.trim().isEmpty()) { |
add = true; |
} |
} |
} |
if (add) { |
builder.append(line); |
builder.append("\n"); |
} |
} else { |
builder.append(line); |
builder.append("\n"); |
} |
line = reader.readLine(); |
} |
} catch (Exception e) { |
e.printStackTrace(); |
} |
return builder.toString(); |
} |
public String getValueAsString(SQLRowAccessor row, String variableName) { |
if (row == null) { |
return null; |
} |
if (variableName.equals("product.code")) { |
return row.getString("CODE"); |
} else if (variableName.equals("product.name")) { |
return row.getString("NOM"); |
} else if (variableName.equals("product.ean13")) { |
return row.getString("CODE_BARRE"); |
} else if (variableName.equals("product.price")) { |
return new DecimalFormat("#0.00").format(row.getBigDecimal("PV_HT")); |
} else if (variableName.equals("product.pricewithtax")) { |
return new DecimalFormat("#0.00").format(row.getBigDecimal("PV_TTC")); |
} else if (variableName.equals("product.material")) { |
return row.getString("MATIERE"); |
} |
return ""; |
} |
public String getName(String variableName) { |
String n = mapName.get(variableName); |
if (n == null) { |
return variableName; |
} |
return n; |
} |
public List<String> getVariables(String str) { |
final List<String> result = new ArrayList<>(); |
if (str == null || str.length() < 4) { |
return result; |
} |
final int l = str.length() - 1; |
int start = 0; |
boolean inName = false; |
for (int i = 0; i < l; i++) { |
char c1 = str.charAt(i); |
char c2 = str.charAt(i + 1); |
if (!inName) { |
if (c1 == '$' && c2 == '{') { |
start = i + 2; |
inName = true; |
} |
} else if (c2 == '}') { |
final int stop = i + 1; |
String v = str.substring(start, stop); |
result.add(v); |
inName = false; |
} |
} |
return result; |
} |
} |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/16.png |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/16.png |
---|
New file |
Property changes: |
Added: svn:mime-type |
+application/octet-stream |
\ No newline at end of property |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/LabelFrame.java |
---|
20,7 → 20,6 |
import javax.swing.event.ChangeEvent; |
import javax.swing.event.ChangeListener; |
import org.openconcerto.sql.model.SQLRowAccessor; |
import org.openconcerto.ui.DefaultGridBagConstraints; |
import org.openconcerto.utils.ExceptionHandler; |
30,7 → 29,7 |
private static final int DEFAULT_COLS = 4; |
final LabelPanel labelPanel; |
public LabelFrame(List<? extends SQLRowAccessor> list, LabelRenderer labelRenderer) { |
public LabelFrame(List<? extends Label> list, LabelRenderer labelRenderer) { |
final JPanel p = new JPanel(); |
p.setLayout(new GridBagLayout()); |
final GridBagConstraints c = new DefaultGridBagConstraints(); |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/ISO646.java |
---|
New file |
0,0 → 1,30 |
package org.openconcerto.modules.label; |
public class ISO646 { |
public static final String ALLOWED_CHARS = "!\"%&'()*+,-./01234567989:;<=>?ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"; |
public static boolean isValid(char c) { |
final int length = ALLOWED_CHARS.length(); |
for (int i = 0; i < length; i++) { |
if (c == ALLOWED_CHARS.charAt(i)) { |
return true; |
} |
} |
return false; |
} |
public static String clean(String s) { |
final int length = s.length(); |
final StringBuilder b = new StringBuilder(length); |
for (int i = 0; i < length; i++) { |
final char charAt = s.charAt(i); |
if (isValid(charAt)) { |
b.append(s.charAt(i)); |
} else { |
b.append('_'); |
} |
} |
return b.toString(); |
} |
} |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/GS1Label.java |
---|
New file |
0,0 → 1,16 |
package org.openconcerto.modules.label; |
import org.openconcerto.modules.label.gs1.GS1AIElements; |
public class GS1Label extends Label { |
String text; |
String text2; |
GS1AIElements gs1; |
public GS1Label(String text, String text2, GS1AIElements gs1) { |
this.text = text; |
this.text2 = text2; |
this.gs1 = gs1; |
} |
} |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/48.png |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/48.png |
---|
New file |
Property changes: |
Added: svn:mime-type |
+application/octet-stream |
\ No newline at end of property |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/LabelPanel.java |
---|
15,11 → 15,9 |
import javax.swing.JPanel; |
import org.openconcerto.sql.model.SQLRowAccessor; |
public class LabelPanel extends JPanel implements Printable { |
private List<? extends SQLRowAccessor> list; |
private List<? extends Label> list; |
private int lines; |
private int columns; |
private Boolean[][] mask; |
29,7 → 27,7 |
private int topMargin; |
private int leftMargin; |
public LabelPanel(List<? extends SQLRowAccessor> list, int l, int c, LabelRenderer labelRenderer) { |
public LabelPanel(List<? extends Label> list, int l, int c, LabelRenderer labelRenderer) { |
this.list = list; |
this.renderer = labelRenderer; |
setSizeLayoutSize(l, c); |
48,6 → 46,11 |
}); |
} |
public void setList(List<? extends Label> list) { |
this.list = list; |
repaint(); |
} |
private void setSizeLayoutSize(int l, int c) { |
this.lines = l; |
this.columns = c; |
102,7 → 105,7 |
g.drawRect(x, y, w, h); |
// Label |
if (rowIndex < list.size()) { |
SQLRowAccessor row = list.get(rowIndex); |
Label row = list.get(rowIndex); |
renderer.paintLabel(g, row, x, y, w, h, 10f); |
} |
rowIndex++; |
169,7 → 172,7 |
for (int j = 0; j < this.columns; j++) { |
boolean mask = this.mask[i][j]; |
if (!mask && rowIndex < list.size()) { |
SQLRowAccessor row = list.get(rowIndex); |
Label row = list.get(rowIndex); |
renderer.paintLabel(g, row, j * w + imageableX, i * h + imageableY, w, h, 10f); |
} |
rowIndex++; |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/96.png |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/96.png |
---|
New file |
Property changes: |
Added: svn:mime-type |
+application/octet-stream |
\ No newline at end of property |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/logo.png |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/logo.png |
---|
New file |
Property changes: |
Added: svn:mime-type |
+application/octet-stream |
\ No newline at end of property |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/filter/LimitDocumentFilter.java |
---|
New file |
0,0 → 1,30 |
package org.openconcerto.modules.label.filter; |
import javax.swing.text.AttributeSet; |
import javax.swing.text.BadLocationException; |
import javax.swing.text.DocumentFilter; |
public class LimitDocumentFilter extends DocumentFilter { |
private int limit; |
public LimitDocumentFilter(int limit) { |
if (limit <= 0) { |
throw new IllegalArgumentException("Limit can not be <= 0"); |
} |
this.limit = limit; |
} |
@Override |
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException { |
int currentLength = fb.getDocument().getLength(); |
int overLimit = (currentLength + text.length()) - limit - length; |
if (overLimit > 0) { |
text = text.substring(0, text.length() - overLimit); |
} |
if (text.length() > 0) { |
super.replace(fb, offset, length, text, attrs); |
} |
} |
} |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/filter/NumberOfProductDocumentFilter.java |
---|
New file |
0,0 → 1,47 |
package org.openconcerto.modules.label.filter; |
import javax.swing.text.AttributeSet; |
import javax.swing.text.BadLocationException; |
import javax.swing.text.DocumentFilter; |
public class NumberOfProductDocumentFilter extends DocumentFilter { |
private boolean isValid(String testText) { |
if (testText.length() > 8) { |
return false; |
} |
if (testText.isEmpty()) { |
return true; |
} |
int intValue = 0; |
try { |
intValue = Integer.parseInt(testText.trim()); |
} catch (NumberFormatException e) { |
return false; |
} |
if (intValue < 1 || intValue > 99999999) { |
return false; |
} |
return true; |
} |
@Override |
public void insertString(FilterBypass fb, int offset, String text, AttributeSet attr) throws BadLocationException { |
StringBuilder sb = new StringBuilder(); |
sb.append(fb.getDocument().getText(0, fb.getDocument().getLength())); |
sb.insert(offset, text); |
if (isValid(sb.toString())) { |
super.insertString(fb, offset, text, attr); |
} |
} |
@Override |
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException { |
StringBuilder sb = new StringBuilder(); |
sb.append(fb.getDocument().getText(0, fb.getDocument().getLength())); |
int end = offset + length; |
sb.replace(offset, end, text); |
if (isValid(sb.toString())) { |
super.replace(fb, offset, length, text, attrs); |
} |
} |
} |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/filter/BatchDocumentFilter.java |
---|
New file |
0,0 → 1,23 |
package org.openconcerto.modules.label.filter; |
import javax.swing.text.AttributeSet; |
import javax.swing.text.BadLocationException; |
import org.openconcerto.modules.label.ISO646; |
public class BatchDocumentFilter extends LimitDocumentFilter { |
public BatchDocumentFilter() { |
super(20); |
} |
@Override |
public void insertString(FilterBypass fb, int offset, String text, AttributeSet attr) throws BadLocationException { |
super.insertString(fb, offset, ISO646.clean(text), attr); |
} |
@Override |
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException { |
super.replace(fb, offset, length, ISO646.clean(text), attrs); |
} |
} |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/filter/EANDocumentFilter.java |
---|
New file |
0,0 → 1,36 |
package org.openconcerto.modules.label.filter; |
import javax.swing.text.AttributeSet; |
import javax.swing.text.BadLocationException; |
public class EANDocumentFilter extends LimitDocumentFilter { |
public EANDocumentFilter() { |
super(14); |
} |
@Override |
public void insertString(FilterBypass fb, int offset, String text, AttributeSet attr) throws BadLocationException { |
super.insertString(fb, offset, clean(text), attr); |
} |
@Override |
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException { |
super.replace(fb, offset, length, clean(text), attrs); |
} |
public static String clean(String s) { |
final int length = s.length(); |
final StringBuilder b = new StringBuilder(length); |
for (int i = 0; i < length; i++) { |
final char charAt = s.charAt(i); |
if (Character.isDigit(charAt)) { |
b.append(s.charAt(i)); |
} else { |
b.append('0'); |
} |
} |
return b.toString(); |
} |
} |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/filter/ISO646DocumentFilter.java |
---|
New file |
0,0 → 1,19 |
package org.openconcerto.modules.label.filter; |
import javax.swing.text.AttributeSet; |
import javax.swing.text.BadLocationException; |
import javax.swing.text.DocumentFilter; |
import org.openconcerto.modules.label.ISO646; |
public class ISO646DocumentFilter extends DocumentFilter { |
@Override |
public void insertString(FilterBypass fb, int offset, String text, AttributeSet attr) throws BadLocationException { |
super.insertString(fb, offset, ISO646.clean(text), attr); |
} |
@Override |
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException { |
super.replace(fb, offset, length, ISO646.clean(text), attrs); |
} |
} |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/GS1Frame.java |
---|
New file |
0,0 → 1,1049 |
package org.openconcerto.modules.label; |
import java.awt.BasicStroke; |
import java.awt.Color; |
import java.awt.Component; |
import java.awt.Dimension; |
import java.awt.Graphics; |
import java.awt.Graphics2D; |
import java.awt.GridBagConstraints; |
import java.awt.GridBagLayout; |
import java.awt.Image; |
import java.awt.Insets; |
import java.awt.RenderingHints; |
import java.awt.event.ActionEvent; |
import java.awt.event.ActionListener; |
import java.awt.event.WindowAdapter; |
import java.awt.event.WindowEvent; |
import java.awt.geom.AffineTransform; |
import java.awt.image.BufferedImage; |
import java.awt.print.PrinterException; |
import java.awt.print.PrinterJob; |
import java.beans.PropertyChangeEvent; |
import java.beans.PropertyChangeListener; |
import java.io.BufferedReader; |
import java.io.ByteArrayOutputStream; |
import java.io.DataOutputStream; |
import java.io.File; |
import java.io.FileInputStream; |
import java.io.FileNotFoundException; |
import java.io.FileOutputStream; |
import java.io.IOException; |
import java.io.InputStream; |
import java.io.StringReader; |
import java.net.Socket; |
import java.nio.charset.StandardCharsets; |
import java.text.SimpleDateFormat; |
import java.util.ArrayList; |
import java.util.Date; |
import java.util.List; |
import java.util.Properties; |
import javax.print.DocFlavor; |
import javax.print.DocPrintJob; |
import javax.print.PrintService; |
import javax.print.SimpleDoc; |
import javax.swing.ButtonGroup; |
import javax.swing.ImageIcon; |
import javax.swing.JButton; |
import javax.swing.JCheckBox; |
import javax.swing.JComponent; |
import javax.swing.JFrame; |
import javax.swing.JLabel; |
import javax.swing.JOptionPane; |
import javax.swing.JPanel; |
import javax.swing.JRadioButton; |
import javax.swing.JSeparator; |
import javax.swing.JSpinner; |
import javax.swing.JSplitPane; |
import javax.swing.JTabbedPane; |
import javax.swing.JTextField; |
import javax.swing.SpinnerNumberModel; |
import javax.swing.SwingConstants; |
import javax.swing.SwingUtilities; |
import javax.swing.UIManager; |
import javax.swing.UnsupportedLookAndFeelException; |
import javax.swing.event.ChangeEvent; |
import javax.swing.event.ChangeListener; |
import javax.swing.event.DocumentEvent; |
import javax.swing.event.DocumentListener; |
import javax.swing.text.AbstractDocument; |
import javax.swing.text.PlainDocument; |
import org.openconcerto.modules.label.filter.BatchDocumentFilter; |
import org.openconcerto.modules.label.filter.EANDocumentFilter; |
import org.openconcerto.modules.label.filter.LimitDocumentFilter; |
import org.openconcerto.modules.label.filter.NumberOfProductDocumentFilter; |
import org.openconcerto.modules.label.gs1.GS1AIElements; |
import org.openconcerto.modules.label.gs1.GS1Util; |
import org.openconcerto.ui.JDate; |
import uk.org.okapibarcode.backend.Code128; |
import uk.org.okapibarcode.backend.DataMatrix; |
import uk.org.okapibarcode.backend.DataMatrix.ForceMode; |
import uk.org.okapibarcode.backend.Ean; |
import uk.org.okapibarcode.backend.Symbol; |
import uk.org.okapibarcode.output.Java2DRenderer; |
public class GS1Frame extends JFrame { |
private final SimpleDateFormat dfDate = new SimpleDateFormat("yyMMdd"); |
private final JTextField textSupplier = new JTextField(20); |
private final JTextField textName = new JTextField(20); |
private final JTextField textEAN = new JTextField(14); |
private final JTextField textBatch = new JTextField(20); |
private final JSpinner portZPL = new JSpinner(new SpinnerNumberModel(9100, 24, 10000, 1)); |
private final JLabel labelEan = new JLabel("EAN 13/14", SwingConstants.RIGHT); |
private final JTextField textNumberOfProducts = new JTextField(12); |
private final JSpinner labelsSpinner = new JSpinner(new SpinnerNumberModel(5, 0, 9000, 1)); |
private final JTextField labelBarCode = new JTextField(64); |
private final JDate dateDLUODLC = new JDate(); |
private final JRadioButton b1 = new JRadioButton("DDM/DLUO"); |
private final JCheckBox checkIgnoreMargins = new JCheckBox("Ignorer les marges de l'imprimante"); |
private final JRadioButton twoPerPage = new JRadioButton("2"); |
private final JRadioButton fourPerPage = new JRadioButton("4"); |
private final JRadioButton radioZPLUSB = new JRadioButton("imprimante locale"); |
private final JTextField textIP = new JTextField(20); |
private final JTabbedPane tabs = new JTabbedPane(); |
private LabelPanel labelPanel; |
private BufferedImage barcodeImage = null; |
private BufferedImage barcodeImageBatch = null; |
private BufferedImage barcodeDatamatrix = null; |
private final Properties properties = new Properties(); |
public GS1Frame() { |
super("Code à barres GS1-128"); |
final File file = new File("openconcerto-gs1.properties"); |
if (file.exists()) { |
try { |
properties.load(new FileInputStream(file)); |
} catch (IOException e) { |
e.printStackTrace(); |
} |
} |
final JSplitPane split = new JSplitPane(); |
final JPanel left = createLeftContent(); |
final JComponent right = createRightContent(); |
split.setLeftComponent(left); |
split.setRightComponent(right); |
this.setContentPane(split); |
split.setEnabled(false); |
try { |
updateList(); |
} catch (Exception e) { |
e.printStackTrace(); |
} |
this.addWindowListener(new WindowAdapter() { |
@Override |
public void windowClosing(WindowEvent e) { |
try { |
saveProperties(); |
} catch (IOException e1) { |
e1.printStackTrace(); |
} |
dispose(); |
} |
}); |
} |
public void saveProperties() throws FileNotFoundException, IOException { |
properties.setProperty("supplier", this.textSupplier.getText()); |
properties.setProperty("product", this.textName.getText()); |
properties.setProperty("ean", this.textEAN.getText()); |
properties.setProperty("nbOfProdcuts", this.textNumberOfProducts.getText()); |
properties.setProperty("batch", this.textBatch.getText()); |
final Date value = this.dateDLUODLC.getValue(); |
if (value != null) { |
properties.setProperty("sellByDate", String.valueOf(value.getTime())); |
} else { |
properties.setProperty("sellByDate", ""); |
} |
properties.setProperty("sellBy", b1.isSelected() ? "true" : "false"); |
properties.setProperty("nbLabels", this.labelsSpinner.getValue().toString()); |
properties.setProperty("labelsPerPage", fourPerPage.isSelected() ? "4" : "2"); |
properties.setProperty("ignoreMargin", checkIgnoreMargins.isSelected() ? "true" : "false"); |
properties.setProperty("usbPrinter", this.radioZPLUSB.isSelected() ? "true" : "false"); |
properties.setProperty("zplIP", this.textIP.getText()); |
properties.setProperty("zplPort", this.portZPL.getValue().toString()); |
properties.setProperty("tab", String.valueOf(tabs.getSelectedIndex())); |
final FileOutputStream out = new FileOutputStream("openconcerto-gs1.properties"); |
properties.store(out, ""); |
out.flush(); |
out.close(); |
} |
boolean isEANValid() { |
final String ean = this.textEAN.getText().trim(); |
if (ean.length() < 13) { |
return false; |
} |
final char cd1 = Ean.calcDigit(ean.substring(0, ean.length() - 1)); |
final char cd2 = ean.charAt(ean.length() - 1); |
return (cd1 == cd2); |
} |
private void updateList() throws Exception { |
this.barcodeImage = null; |
this.barcodeImageBatch = null; |
this.barcodeDatamatrix = null; |
this.labelPanel.setList(new ArrayList<>()); |
this.labelBarCode.setText(""); |
// Check EAN |
String ean = this.textEAN.getText().trim(); |
if (!isEANValid()) { |
this.labelEan.setForeground(Color.RED); |
} else { |
this.labelEan.setForeground(Color.BLACK); |
} |
if (ean.length() == 13) { |
this.labelEan.setText("EAN 13"); |
} else if (ean.length() == 14) { |
this.labelEan.setText("EAN 14"); |
} else { |
this.labelEan.setText("EAN invalide"); |
this.labelEan.setForeground(Color.RED); |
} |
if (!isEANValid()) { |
return; |
} |
int n = ((Number) this.labelsSpinner.getValue()).intValue(); |
this.labelBarCode.setText(this.getGS1().formatHumanReadable()); |
this.labelBarCode.setEditable(false); |
GS1Label l = new GS1Label(this.textSupplier.getText(), this.textName.getText(), this.getGS1()); |
List<GS1Label> list = new ArrayList<>(n); |
for (int i = 0; i < n; i++) { |
list.add(l); |
} |
this.labelPanel.setList(list); |
// Configure the barcode generator |
// adjust barcode width here |
// 32mm de hatu minimum |
// 0,495mm et 1,016mm pour largeur de barre |
GS1Util util = new GS1Util(); |
{ |
final GS1AIElements gs128 = getGS128(); |
Code128 dataMatrix = new Code128(); |
dataMatrix.setDataType(Symbol.DataType.GS1); |
dataMatrix.setBarHeight(70); |
dataMatrix.setContent(util.formatDataMatrix(gs128)); |
int magnification = 5; |
int borderSize = 3; |
this.barcodeImage = new BufferedImage((dataMatrix.getWidth() * magnification) + (2 * borderSize), (dataMatrix.getHeight() * magnification) + (2 * borderSize), BufferedImage.TYPE_INT_RGB); |
Graphics2D g2d = this.barcodeImage.createGraphics(); |
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
g2d.setColor(Color.WHITE); |
g2d.fillRect(0, 0, (dataMatrix.getWidth() * magnification) + (2 * borderSize), (dataMatrix.getHeight() * magnification) + (2 * borderSize)); |
Java2DRenderer renderer = new Java2DRenderer(g2d, magnification, Color.WHITE, Color.BLACK); |
renderer.render(dataMatrix); |
} |
final GS1AIElements gs128b = getGS128Batch(); |
if (!gs128b.isEmpty()) { |
Code128 dataMatrix = new Code128(); |
dataMatrix.setDataType(Symbol.DataType.GS1); |
dataMatrix.setBarHeight(90); |
dataMatrix.setContent(util.formatDataMatrix(gs128b)); |
int magnification = 4; |
int borderSize = 3; |
this.barcodeImageBatch = new BufferedImage((dataMatrix.getWidth() * magnification) + (2 * borderSize), (int) (dataMatrix.getHeight() * magnification) + (2 * borderSize), |
BufferedImage.TYPE_INT_RGB); |
Graphics2D g2d = this.barcodeImageBatch.createGraphics(); |
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
g2d.setColor(Color.WHITE); |
g2d.fillRect(0, 0, (dataMatrix.getWidth() * magnification) + (2 * borderSize), (dataMatrix.getHeight() * magnification) + (2 * borderSize)); |
Java2DRenderer renderer = new Java2DRenderer(g2d, magnification, Color.WHITE, Color.BLACK); |
renderer.render(dataMatrix); |
} |
{ |
String s = util.formatDataMatrix(getGS1()); |
DataMatrix dataMatrix = new DataMatrix(); |
dataMatrix.setDataType(Symbol.DataType.GS1); |
dataMatrix.setForceMode(ForceMode.SQUARE); |
dataMatrix.setContent(s); |
int magnification = 30; |
int borderSize = 3; |
this.barcodeDatamatrix = new BufferedImage((dataMatrix.getWidth() * magnification) + (2 * borderSize), (dataMatrix.getHeight() * magnification) + (2 * borderSize), |
BufferedImage.TYPE_INT_RGB); |
Graphics2D g2d = this.barcodeDatamatrix.createGraphics(); |
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
g2d.setColor(Color.WHITE); |
g2d.fillRect(0, 0, (dataMatrix.getWidth() * magnification) + (2 * borderSize), (dataMatrix.getHeight() * magnification) + (2 * borderSize)); |
Java2DRenderer renderer = new Java2DRenderer(g2d, magnification, Color.WHITE, Color.BLACK); |
renderer.render(dataMatrix); |
} |
// ImageIO.write(barcodeImage, "PNG", new File("barcode.png")); |
// ImageIO.write(barcodeImageBatch, "PNG", new File("barcode2.png")); |
// ImageIO.write(barcodeDatamatrix, "PNG", new File("datamatrix.png")); |
} |
private JPanel createLeftContent() { |
final JPanel p = new JPanel(); |
p.setLayout(new GridBagLayout()); |
final GridBagConstraints c = new GridBagConstraints(); |
c.fill = GridBagConstraints.BOTH; |
c.gridy = 0; |
c.gridx = 0; |
c.gridwidth = 3; |
p.add(createLogo(), c); |
c.fill = GridBagConstraints.HORIZONTAL; |
c.gridwidth = 1; |
c.gridy++; |
c.anchor = GridBagConstraints.WEST; |
c.insets = new Insets(2, 3, 2, 2); |
// |
c.weightx = 0; |
c.gridwidth = 1; |
p.add(new JLabel("Fournisseur / fabricant", SwingConstants.RIGHT), c); |
c.gridx++; |
c.weightx = 1; |
c.gridwidth = 2; |
this.textSupplier.setText(properties.getProperty("supplier", "")); |
((AbstractDocument) this.textSupplier.getDocument()).setDocumentFilter(new LimitDocumentFilter(22)); |
p.add(this.textSupplier, c); |
// |
c.gridx = 0; |
c.gridy++; |
c.weightx = 0; |
c.gridwidth = 1; |
p.add(new JLabel("Désignation du produit", SwingConstants.RIGHT), c); |
c.gridx++; |
c.weightx = 1; |
c.gridwidth = 2; |
this.textName.setText(properties.getProperty("product", "Biscuit Extra")); |
((AbstractDocument) this.textName.getDocument()).setDocumentFilter(new LimitDocumentFilter(22)); |
p.add(this.textName, c); |
// EAN 13/14 |
c.gridx = 0; |
c.gridy++; |
c.weightx = 0; |
c.gridwidth = 1; |
p.add(this.labelEan, c); |
c.gridx++; |
c.weightx = 1; |
c.gridwidth = 2; |
this.textEAN.setText(properties.getProperty("ean", "7612345678900")); |
((AbstractDocument) this.textEAN.getDocument()).setDocumentFilter(new EANDocumentFilter()); |
p.add(this.textEAN, c); |
// nb de produit |
c.gridx = 0; |
c.gridy++; |
c.weightx = 0; |
c.gridwidth = 1; |
p.add(new JLabel("Nombre par carton", SwingConstants.RIGHT), c); |
c.gridx++; |
c.weightx = 1; |
c.gridwidth = 2; |
c.fill = GridBagConstraints.NONE; |
PlainDocument doc = (PlainDocument) this.textNumberOfProducts.getDocument(); |
doc.setDocumentFilter(new NumberOfProductDocumentFilter()); |
this.textNumberOfProducts.setText(properties.getProperty("nbOfProdcuts", "10")); |
p.add(this.textNumberOfProducts, c); |
// Numéro de lot |
c.gridx = 0; |
c.gridy++; |
c.weightx = 0; |
c.gridwidth = 1; |
c.fill = GridBagConstraints.HORIZONTAL; |
p.add(new JLabel("Lot", SwingConstants.RIGHT), c); |
c.gridx++; |
c.weightx = 1; |
c.gridwidth = 2; |
((AbstractDocument) this.textBatch.getDocument()).setDocumentFilter(new BatchDocumentFilter()); |
this.textBatch.setText(properties.getProperty("batch", "")); |
p.add(this.textBatch, c); |
// DLC |
c.gridx = 1; |
c.gridy++; |
c.weightx = 0; |
c.gridwidth = 1; |
p.add(this.b1, c); |
c.gridx++; |
c.weightx = 1; |
this.dateDLUODLC.setFormat(new SimpleDateFormat("dd/MM/yyyy")); |
if (!properties.getProperty("sellByDate", "").isEmpty()) { |
this.dateDLUODLC.setDateInMillis(Long.parseLong(properties.getProperty("sellByDate"))); |
} |
p.add(this.dateDLUODLC, c); |
// DLUO |
c.gridx = 1; |
c.gridy++; |
c.weightx = 0; |
JRadioButton b2 = new JRadioButton("DLC"); |
p.add(b2, c); |
c.gridx++; |
c.weightx = 1; |
this.b1.setSelected(properties.getProperty("sellBy", "true").equals("true")); |
b2.setSelected(!properties.getProperty("sellBy", "true").equals("true")); |
// Barcode |
c.gridx = 0; |
c.gridy++; |
c.weightx = 0; |
c.gridwidth = 3; |
p.add(new JLabel("Code GS1 complet de l'étiquette", SwingConstants.LEFT), c); |
c.gridy++; |
c.weightx = 1; |
this.labelBarCode.setFont(this.textBatch.getFont()); |
this.labelBarCode.setEnabled(false); |
p.add(this.labelBarCode, c); |
// Nombre d'étiquettes |
c.gridx = 0; |
c.gridy++; |
c.weightx = 0; |
c.gridwidth = 1; |
p.add(new JLabel("Nombre d'étiquettes", SwingConstants.RIGHT), c); |
c.gridx++; |
c.weightx = 0; |
c.fill = GridBagConstraints.NONE; |
this.labelsSpinner.setValue(Integer.parseInt(properties.getProperty("nbLabels", "4"))); |
p.add(this.labelsSpinner, c); |
final JPanel spacer = new JPanel(); |
spacer.setOpaque(false); |
c.gridy++; |
c.weighty = 1; |
p.add(spacer, c); |
final ButtonGroup gr = new ButtonGroup(); |
gr.add(this.b1); |
gr.add(b2); |
// Listeners |
final ChangeListener changeListener = new ChangeListener() { |
@Override |
public void stateChanged(ChangeEvent e) { |
try { |
updateList(); |
} catch (Exception ex) { |
ex.printStackTrace(); |
} |
} |
}; |
this.labelsSpinner.addChangeListener(changeListener); |
final DocumentListener dListener = new DocumentListener() { |
@Override |
public void removeUpdate(DocumentEvent e) { |
changedUpdate(e); |
} |
@Override |
public void insertUpdate(DocumentEvent e) { |
changedUpdate(e); |
} |
@Override |
public void changedUpdate(DocumentEvent e) { |
try { |
updateList(); |
} catch (Exception ex) { |
ex.printStackTrace(); |
} |
} |
}; |
this.textSupplier.getDocument().addDocumentListener(dListener); |
this.textName.getDocument().addDocumentListener(dListener); |
this.textEAN.getDocument().addDocumentListener(dListener); |
this.textNumberOfProducts.getDocument().addDocumentListener(dListener); |
this.textBatch.getDocument().addDocumentListener(dListener); |
this.dateDLUODLC.addValueListener(new PropertyChangeListener() { |
@Override |
public void propertyChange(PropertyChangeEvent evt) { |
try { |
updateList(); |
} catch (Exception e) { |
e.printStackTrace(); |
} |
} |
}); |
this.b1.addChangeListener(changeListener); |
b2.addChangeListener(changeListener); |
return p; |
} |
private Component createLogo() { |
final Image i1 = new ImageIcon(this.getClass().getResource("logo.png")).getImage(); |
final Image i2 = new ImageIcon(this.getClass().getResource("oc-qrcode.png")).getImage(); |
return new JComponent() { |
@Override |
public void paint(Graphics g) { |
super.paint(g); |
g.setColor(Color.WHITE); |
g.fillRect(0, 0, getWidth(), getHeight()); |
g.drawImage(i1, 0, 3, null); |
g.drawImage(i2, getWidth() - i2.getWidth(null), 0, null); |
g.setColor(Color.GRAY); |
g.drawLine(0, 58, getWidth(), 58); |
} |
@Override |
public Dimension getPreferredSize() { |
return new Dimension(200, 59); |
} |
}; |
} |
private JComponent createRightContent() { |
final JPanel panelA4 = new JPanel(); |
panelA4.setOpaque(false); |
panelA4.setLayout(new GridBagLayout()); |
final GridBagConstraints c = new GridBagConstraints(); |
c.fill = GridBagConstraints.HORIZONTAL; |
c.insets = new Insets(2, 3, 2, 2); |
c.gridx = 0; |
c.gridy = 0; |
panelA4.add(createToolBar(), c); |
c.gridy++; |
// panelA4.add(createToolBar2(), c); |
// c.gridy++; |
c.insets = new Insets(0, 0, 0, 0); |
panelA4.add(new JSeparator(JSeparator.HORIZONTAL), c); |
c.gridy++; |
c.weightx = 1; |
c.weighty = 1; |
c.fill = GridBagConstraints.BOTH; |
final int columnCount = fourPerPage.isSelected() ? 2 : 1; |
this.labelPanel = new LabelPanel(new ArrayList<Label>(), 2, columnCount, createRenderer()) { |
@Override |
public Dimension getPreferredSize() { |
return new Dimension(190 * 3, 280 * 3); |
} |
}; |
this.labelPanel.setIgnoreMargins(checkIgnoreMargins.isSelected()); |
panelA4.add(this.labelPanel, c); |
tabs.addTab("Feuilles A4", panelA4); |
tabs.addTab("Imprimante ZPL", createZPLPanel()); |
tabs.setSelectedIndex(Integer.parseInt(properties.getProperty("tab", "0"))); |
return tabs; |
} |
private Component createZPLPanel() { |
final JPanel panelZPL = new JPanel(); |
panelZPL.setOpaque(false); |
panelZPL.setLayout(new GridBagLayout()); |
final GridBagConstraints c = new GridBagConstraints(); |
c.fill = GridBagConstraints.HORIZONTAL; |
c.insets = new Insets(2, 3, 2, 2); |
c.gridx = 0; |
c.gridy = 0; |
c.weightx = 0; |
c.gridwidth = 1; |
c.anchor = GridBagConstraints.EAST; |
radioZPLUSB.setOpaque(false); |
panelZPL.add(radioZPLUSB, c); |
c.gridx += 5; |
c.weightx = 1; |
c.fill = GridBagConstraints.NONE; |
c.insets = new Insets(4, 3, 2, 4); |
final JButton buttonPrintZPL = new JButton("Imprimer"); |
buttonPrintZPL.setOpaque(false); |
panelZPL.add(buttonPrintZPL, c); |
c.insets = new Insets(2, 3, 2, 2); |
c.gridx = 0; |
c.gridy++; |
c.weightx = 0; |
c.fill = GridBagConstraints.HORIZONTAL; |
final JRadioButton radioZPLNetwork = new JRadioButton("imprimante réseau"); |
radioZPLNetwork.setOpaque(false); |
panelZPL.add(radioZPLNetwork, c); |
c.gridx++; |
panelZPL.add(new JLabel("Adresse"), c); |
c.gridx++; |
textIP.setText(properties.getProperty("zplIP", "192.168.1.50")); |
panelZPL.add(textIP, c); |
c.gridx++; |
panelZPL.add(new JLabel("Port"), c); |
this.portZPL.setValue(Integer.parseInt(properties.getProperty("zplPort", "9100"))); |
c.gridx++; |
panelZPL.add(this.portZPL, c); |
c.gridx = 0; |
c.gridy++; |
c.gridwidth = 6; |
radioZPLUSB.setSelected(properties.getProperty("usbPrinter", "true").equals("true")); |
radioZPLNetwork.setSelected(!properties.getProperty("usbPrinter", "true").equals("true")); |
final ButtonGroup g = new ButtonGroup(); |
g.add(radioZPLUSB); |
g.add(radioZPLNetwork); |
c.anchor = GridBagConstraints.NORTHWEST; |
panelZPL.add(new JLabel("L'impression nécessite une imprimante d'étiquettes compatible ZPL (Zebra ou autre)."), c); |
c.gridy++; |
c.weighty = 1; |
panelZPL.add(new JLabel("Vous pouvez paramétrer l'impression par défaut (largeur 6\" à 203 dpi) dans le fichier zpl.txt ."), c); |
buttonPrintZPL.addActionListener(new ActionListener() { |
@Override |
public void actionPerformed(ActionEvent e) { |
if (!isEANValid()) { |
JOptionPane.showMessageDialog(GS1Frame.this, "EAN invalide"); |
return; |
} |
final String code = createZPLCode(); |
System.out.println("ZPL:"); |
System.out.println(code); |
byte[] data = code.getBytes(StandardCharsets.US_ASCII); |
if (radioZPLNetwork.isSelected()) { |
Socket socket = null; |
try { |
socket = new Socket(textIP.getText(), ((Number) GS1Frame.this.portZPL.getValue()).intValue()); |
final DataOutputStream out = new DataOutputStream(socket.getOutputStream()); |
final int nb = ((Number) labelsSpinner.getValue()).intValue(); |
for (int i = 0; i < nb; i++) { |
out.write(data); |
} |
} catch (Exception ex) { |
ex.printStackTrace(); |
JOptionPane.showMessageDialog(buttonPrintZPL, "Erreur d'impression réseau : " + ex.getMessage()); |
} finally { |
if (socket != null) { |
try { |
socket.close(); |
} catch (IOException e1) { |
e1.printStackTrace(); |
} |
} |
} |
} else { |
try { |
final PrinterJob pj1 = PrinterJob.getPrinterJob(); |
if (pj1.printDialog()) { |
final PrintService ps = pj1.getPrintService(); |
final DocPrintJob pj = ps.createPrintJob(); |
final SimpleDoc doc = new SimpleDoc(data, DocFlavor.BYTE_ARRAY.AUTOSENSE, null); |
final int nb = ((Number) labelsSpinner.getValue()).intValue(); |
for (int i = 0; i < nb; i++) { |
pj.print(doc, null); |
} |
} |
} catch (Exception ex) { |
ex.printStackTrace(); |
JOptionPane.showMessageDialog(buttonPrintZPL, "Erreur d'impression locale : " + ex.getMessage()); |
} |
} |
} |
}); |
return panelZPL; |
} |
private LabelRenderer createRenderer() { |
return new LabelRenderer() { |
@Override |
public void paintLabel(Graphics g, Label label, int x, int y, int gridWith, int gridHeight, float fontSize) { |
final Graphics2D g2d = (Graphics2D) g; |
g.setColor(Color.BLACK); |
g2d.setStroke(new BasicStroke(3)); |
String str1 = GS1Frame.this.textSupplier.getText().trim(); |
String str2 = GS1Frame.this.textName.getText(); |
int pos1 = 26; |
if (str1.isEmpty()) { |
str1 = GS1Frame.this.textName.getText(); |
str2 = ""; |
pos1 += 16; |
} |
final AffineTransform defaultAt = g2d.getTransform(); |
final AffineTransform at = ((AffineTransform) (g2d.getTransform().clone())); |
at.rotate(-Math.PI / 2); |
g2d.setTransform(at); |
g.setFont(getFont().deriveFont(15f)); |
final int d1 = 60; |
final int d2 = 60; |
g.drawString(str1, -y - gridHeight + d1, x + pos1); |
g.drawString(str2, -y - gridHeight + d2, x + 58); |
if (GS1Frame.this.barcodeDatamatrix != null) { |
float ratioDatamatrix = 15f; |
final int wd = (int) (GS1Frame.this.barcodeDatamatrix.getWidth() / ratioDatamatrix); |
final int hd = (int) (GS1Frame.this.barcodeDatamatrix.getHeight() / ratioDatamatrix); |
g.drawImage(GS1Frame.this.barcodeDatamatrix, -y - 10 - wd, x + 10, wd, hd, null); |
final int w1 = GS1Frame.this.barcodeImage.getWidth() / 4; |
final int h1 = GS1Frame.this.barcodeImage.getHeight() / 4; |
g.drawImage(GS1Frame.this.barcodeImage, -y + -gridHeight + 60, x + 70, w1, h1, null); |
} |
if (GS1Frame.this.barcodeImageBatch != null) { |
// GS1-128 numero de lot |
final int w2 = GS1Frame.this.barcodeImageBatch.getWidth() / 4; |
final int h2 = GS1Frame.this.barcodeImageBatch.getHeight() / 4; |
g.drawImage(GS1Frame.this.barcodeImageBatch, -y - gridHeight + 60, x + 180, w2, h2, null); |
} |
g2d.setTransform(defaultAt); |
} |
}; |
} |
public JPanel createToolBar() { |
final JPanel toolbar = new JPanel(); |
toolbar.setOpaque(false); |
toolbar.setLayout(new GridBagLayout()); |
final GridBagConstraints c = new GridBagConstraints(); |
c.fill = GridBagConstraints.HORIZONTAL; |
c.insets = new Insets(2, 3, 2, 2); |
c.gridx = 0; |
c.gridy = 0; |
c.weightx = 0; |
c.gridwidth = 1; |
toolbar.add(new JLabel("Etiquettes par page"), c); |
c.gridx++; |
twoPerPage.setOpaque(false); |
fourPerPage.setOpaque(false); |
final ButtonGroup g = new ButtonGroup(); |
g.add(twoPerPage); |
g.add(fourPerPage); |
if (properties.getProperty("labelsPerPage", "4").equals("4")) { |
fourPerPage.setSelected(true); |
} else { |
twoPerPage.setSelected(true); |
} |
toolbar.add(twoPerPage, c); |
c.gridx++; |
toolbar.add(fourPerPage, c); |
c.gridx++; |
checkIgnoreMargins.setOpaque(false); |
checkIgnoreMargins.setSelected(properties.getProperty("ignoreMargin", "false").equals("true")); |
c.gridx++; |
toolbar.add(checkIgnoreMargins, c); |
c.gridx++; |
c.weightx = 1; |
c.fill = GridBagConstraints.NONE; |
c.anchor = GridBagConstraints.NORTHEAST; |
final JButton printButton = new JButton("Imprimer"); |
printButton.setOpaque(false); |
toolbar.add(printButton, c); |
c.gridx++; |
twoPerPage.addActionListener(new ActionListener() { |
@Override |
public void actionPerformed(ActionEvent e) { |
if (twoPerPage.isSelected()) { |
GS1Frame.this.labelPanel.setColumnCount(1); |
} else { |
GS1Frame.this.labelPanel.setColumnCount(2); |
} |
} |
}); |
fourPerPage.addActionListener(new ActionListener() { |
@Override |
public void actionPerformed(ActionEvent e) { |
if (twoPerPage.isSelected()) { |
GS1Frame.this.labelPanel.setColumnCount(1); |
} else { |
GS1Frame.this.labelPanel.setColumnCount(2); |
} |
} |
}); |
printButton.addActionListener(new ActionListener() { |
@Override |
public void actionPerformed(ActionEvent e) { |
if (!isEANValid()) { |
JOptionPane.showMessageDialog(GS1Frame.this, "EAN invalide"); |
return; |
} |
final PrinterJob job = PrinterJob.getPrinterJob(); |
job.setPrintable(GS1Frame.this.labelPanel); |
boolean ok = job.printDialog(); |
if (ok) { |
try { |
job.print(); |
} catch (PrinterException ex) { |
JOptionPane.showMessageDialog(GS1Frame.this, "Print error :" + ex.getMessage()); |
} |
} |
} |
}); |
checkIgnoreMargins.addActionListener(new ActionListener() { |
@Override |
public void actionPerformed(ActionEvent e) { |
GS1Frame.this.labelPanel.setIgnoreMargins(checkIgnoreMargins.isSelected()); |
} |
}); |
return toolbar; |
} |
public JPanel createToolBar2() { |
final JPanel toolbar = new JPanel(); |
toolbar.setOpaque(false); |
toolbar.setLayout(new GridBagLayout()); |
final GridBagConstraints c = new GridBagConstraints(); |
c.insets = new Insets(2, 3, 2, 2); |
c.gridx = 0; |
c.gridy = 0; |
c.weightx = 0; |
c.gridwidth = 1; |
c.fill = GridBagConstraints.NONE; |
toolbar.add(new JLabel("Marge en haut (mm)"), c); |
c.gridx++; |
final JSpinner sLines = new JSpinner(new SpinnerNumberModel(0, 0, 200, 1)); |
toolbar.add(sLines, c); |
c.gridx++; |
toolbar.add(new JLabel("Marge à gauche (mm)"), c); |
c.gridx++; |
final JSpinner sColums = new JSpinner(new SpinnerNumberModel(0, 0, 200, 1)); |
c.weightx = 1; |
toolbar.add(sColums, c); |
c.gridx++; |
sLines.addChangeListener(new ChangeListener() { |
@Override |
public void stateChanged(ChangeEvent e) { |
final Number n = (Number) sLines.getValue(); |
if (n != null) { |
GS1Frame.this.labelPanel.setTopMargin(n.intValue()); |
} |
} |
}); |
sColums.addChangeListener(new ChangeListener() { |
@Override |
public void stateChanged(ChangeEvent e) { |
final Number n = (Number) sColums.getValue(); |
if (n != null) { |
GS1Frame.this.labelPanel.setLeftMargin(n.intValue()); |
} |
} |
}); |
return toolbar; |
} |
public String readZPLScript() { |
if (new File("zpl.txt").exists()) { |
try (InputStream in = new FileInputStream(new File("zpl.txt"))) { |
return readAscii(in); |
} catch (Exception e) { |
e.printStackTrace(); |
} |
} else { |
try (InputStream in = GS1Frame.class.getResourceAsStream("default-zpl.txt")) { |
return readAscii(in); |
} catch (Exception e) { |
e.printStackTrace(); |
} |
} |
return ""; |
} |
protected String createZPLCode() { |
final String script = readZPLScript(); |
final BufferedReader reader = new BufferedReader(new StringReader(script)); |
final StringBuilder builder = new StringBuilder(); |
final GS1Util u = new GS1Util(); |
try { |
String line = reader.readLine(); |
while (line != null) { |
if (line.contains("XXXXXXXXXX")) { |
if (!this.textSupplier.getText().isEmpty()) { |
line = line.replace("XXXXXXXXXX", this.textSupplier.getText()); |
builder.append(line); |
builder.append("\r\n"); |
} |
} else if (line.contains("YYYYYYYYYY")) { |
line = line.replace("YYYYYYYYYY", this.textName.getText()); |
builder.append(line); |
builder.append("\r\n"); |
} else if (line.contains("(02)7612345678900(15)201218(37)9999")) { |
line = line.replace("(02)7612345678900(15)201218(37)9999", getGS128().formatHumanReadable()); |
builder.append(line); |
builder.append("\r\n"); |
} else if (line.contains("(10)LLLLLLLLL")) { |
GS1AIElements batch = getGS128Batch(); |
if (!batch.isEmpty()) { |
line = line.replace("(10)LLLLLLLLL", batch.formatHumanReadable()); |
builder.append(line); |
builder.append("\r\n"); |
} |
} else if (line.contains("OPENCONCERTO")) { |
GS1AIElements all = getGS1(); |
line = line.replace("OPENCONCERTO", u.formatZPL(all)); |
builder.append(line); |
builder.append("\r\n"); |
} else { |
builder.append(line); |
builder.append("\r\n"); |
} |
line = reader.readLine(); |
} |
} catch (Exception e) { |
e.printStackTrace(); |
} |
return builder.toString(); |
} |
private GS1AIElements getGS1() { |
final GS1AIElements gs1 = new GS1AIElements(); |
final String ean = this.textEAN.getText().trim(); |
if (!ean.isEmpty()) { |
if (ean.length() == 13 || (ean.length() == 14 && ean.charAt(0) == '0')) { |
if (ean.length() == 13) { |
gs1.put("02", '0' + ean); |
} else { |
gs1.put("02", ean); |
} |
} else { |
gs1.put("01", ean); |
} |
} |
if (this.dateDLUODLC.getValue() != null) { |
String date = this.dfDate.format(this.dateDLUODLC.getValue()); |
if (this.b1.isSelected()) { |
gs1.put("15", date); |
} else { |
gs1.put("17", date); |
} |
} |
final String nbProducts = this.textNumberOfProducts.getText(); |
if (!nbProducts.trim().isEmpty()) { |
int n = Integer.parseInt(this.textNumberOfProducts.getText()); |
gs1.put("37", String.valueOf(n)); |
} |
final String batch = this.textBatch.getText().trim(); |
if (!batch.isEmpty()) { |
gs1.put("10", batch); |
} |
return gs1; |
} |
private GS1AIElements getGS128Batch() { |
final GS1AIElements gs1 = new GS1AIElements(); |
final String batch = this.textBatch.getText().trim(); |
if (!batch.isEmpty()) { |
gs1.put("10", batch); |
} |
return gs1; |
} |
private GS1AIElements getGS128() { |
final GS1AIElements gs1 = getGS1(); |
gs1.remove("10"); |
return gs1; |
} |
public String readAscii(final InputStream in) throws IOException { |
final ByteArrayOutputStream out = new ByteArrayOutputStream(); |
final byte[] buf = new byte[8192]; |
int length; |
while ((length = in.read(buf)) > 0) { |
out.write(buf, 0, length); |
} |
out.flush(); |
out.close(); |
return new String(out.toByteArray(), StandardCharsets.US_ASCII); |
} |
static List<Image> frameIcon; |
public static synchronized List<Image> getFrameIcon() { |
if (frameIcon == null) { |
frameIcon = new ArrayList<>(); |
final int[] sizes = { 16, 32, 48, 96 }; |
for (int i = 0; i < sizes.length; i++) { |
int v = sizes[i]; |
try { |
frameIcon.add(new ImageIcon(GS1Frame.class.getResource(v + ".png")).getImage()); |
} catch (Exception e) { |
e.printStackTrace(); |
} |
} |
} |
return frameIcon; |
} |
public static void main(String[] args) { |
if (!new File("zpl.txt").exists()) { |
final InputStream in = GS1Frame.class.getResourceAsStream("default-zpl.txt"); |
try (FileOutputStream fOut = new FileOutputStream(new File("zpl.txt"))) { |
byte[] buf = new byte[8192]; |
int length; |
while ((length = in.read(buf)) > 0) { |
fOut.write(buf, 0, length); |
} |
fOut.flush(); |
} catch (Exception e) { |
e.printStackTrace(); |
} |
} |
SwingUtilities.invokeLater(new Runnable() { |
@Override |
public void run() { |
try { |
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); |
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e1) { |
e1.printStackTrace(); |
} |
final GS1Frame f = new GS1Frame(); |
f.setIconImages(getFrameIcon()); |
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); |
f.pack(); |
f.setVisible(true); |
f.setLocationRelativeTo(null); |
f.setResizable(false); |
} |
}); |
} |
} |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/LabelRenderer.java |
---|
2,8 → 2,6 |
import java.awt.Graphics; |
import org.openconcerto.sql.model.SQLRowAccessor; |
public interface LabelRenderer { |
public void paintLabel(Graphics g, SQLRowAccessor row, int x, int y, int gridWith, int gridHeight, float fontSize); |
public void paintLabel(Graphics g, Label label, int x, int y, int gridWith, int gridHeight, float fontSize); |
} |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/gs1/GS1Util.java |
---|
New file |
0,0 → 1,243 |
package org.openconcerto.modules.label.gs1; |
import org.openconcerto.modules.label.ISO646; |
public class GS1Util { |
// ISO/IEC 15424 symbology identifiers |
// ]E0 : EAN-13, UPC-A, or UPC-E : 13 digits |
// ]E1 : Two-digit add-on symbol : 2 digits |
// ]E2 : Five-digit add-on symbol : 5 digits |
// ]E3 : EAN-13, UPC-A, or UPC-E with add-on symbol : 15 or 18 digits |
// ]E4 : EAN-8 : 8 digits |
// ]I1 : ITF-14 : 14 digits |
// ]C1 : GS1-128 : Standard AI element strings |
private static final String GS1_128_SCANNER_PREFIX = "]C1"; |
// ]e0 : GS1 DataBar : Standard AI element strings |
private static final String GS1_DATABAR_SCANNER_PREFIX = "]e0"; |
// ]e1 : GS1 Composite : Data packet containing the data + an encoded symbol separator |
// character. |
// ]e2 : GS1 Composite : Data packet containing the data following an escape mechanism |
// character. |
// ]d2 : GS1 DataMatrix : Standard AI element strings |
private static final String GS1_DATAMATRIX_SCANNER_PREFIX = "]d2"; |
// ]Q3 : GS1 QR Code : Standard AI element strings |
private static final String GS1_QRCODE_SCANNER_PREFIX = "]Q3"; |
// ]J1 : GS1 DotCode : Standard AI element strings |
static final int ERROR_CODE_INVALID_GS1_SCAN = 0; |
static final int ERROR_CODE_UNKNOWN_AI = 1; |
static final int ERROR_CODE_INCOMPLETE_AI = 2; |
static final int ERROR_CODE_NOT_FOUND_SEPARATOR = 3; |
static final int ERROR_CODE_INSUFFICIENT_VALUE_LENGTH = 4; |
static final int ERROR_CODE_EXCEEDED_VALUE_LENGTH = 5; |
static final int ERROR_CODE_CONVERT_DECIMAL_POINT = 5; |
static final int ERROR_CODE_WRONG_DECIMAL_POINT = 6; |
static final int ERROR_CODE_CONVERT_DECIMAL_VALUE = 7; |
public static final char GS_SEPARATOR = '\u001D'; |
// public static final char FNC1_SEPARATOR = 232; |
static final int SPACE_SEPARATOR = ' '; |
int separator; |
public GS1Util() { |
this(GS_SEPARATOR); |
} |
public GS1Util(int separator) { |
this.separator = separator; |
} |
public GS1AIElements parseFromScanner(String scan) throws GS1ParseException { |
if (scan.startsWith(GS1_DATAMATRIX_SCANNER_PREFIX) || scan.startsWith(GS1_128_SCANNER_PREFIX) || scan.startsWith(GS1_DATABAR_SCANNER_PREFIX) || scan.startsWith(GS1_QRCODE_SCANNER_PREFIX)) { |
return parse(scan.substring(3)); |
} |
return parse(scan); |
} |
public GS1AIElements parse(String barcode) throws GS1ParseException { |
if (barcode.length() < 3) { |
throw new GS1ParseException("", ERROR_CODE_INVALID_GS1_SCAN, "code too short"); |
} |
System.err.println("GS1Util.parse()" + barcode); |
GS1AIElements attr = new GS1AIElements(); |
StringBuilder ai = new StringBuilder(); |
int length = barcode.length(); |
for (int i = 0; i < length; ++i) { |
int aiLength = ai.length(); |
if (aiLength > 1) { |
GS1ApplicationIdentifier aii = GS1AIElements.getApplicationIdentifier(ai.toString()); |
if (aii == null) { |
if (aiLength < 4) |
ai.append(barcode.charAt(i)); |
else |
throw new GS1ParseException(ai.toString(), ERROR_CODE_UNKNOWN_AI, "Unknown AI"); |
} else { |
int decimalPoint = 0; |
if (aii.decimalPoint) { |
try { |
decimalPoint = Integer.valueOf(String.valueOf(barcode.charAt(i))); |
} catch (NumberFormatException e) { |
throw new GS1ParseException(ai.toString(), ERROR_CODE_CONVERT_DECIMAL_POINT, "Errow convert to decimal point"); |
} |
if (++i >= length) |
throw new GS1ParseException(ai.toString(), ERROR_CODE_INSUFFICIENT_VALUE_LENGTH, "Insufficient value length"); |
} |
String value; |
if (aii.variableLength) { |
int separatorIndex = barcode.indexOf(this.separator, i); |
if (separatorIndex < 0) { |
if (length - i > aii.length) |
throw new GS1ParseException(ai.toString(), ERROR_CODE_NOT_FOUND_SEPARATOR, "Not found separator"); |
else if (length - i < aii.minLength) |
throw new GS1ParseException(ai.toString(), ERROR_CODE_INSUFFICIENT_VALUE_LENGTH, "Insufficient value length"); |
else { |
value = barcode.substring(i); |
i = length; |
} |
} else if (separatorIndex - i > aii.length) |
throw new GS1ParseException(ai.toString(), ERROR_CODE_EXCEEDED_VALUE_LENGTH, "Exceeded value length"); |
else if (separatorIndex - i < aii.minLength) |
throw new GS1ParseException(ai.toString(), ERROR_CODE_INSUFFICIENT_VALUE_LENGTH, "Insufficient value length"); |
else { |
value = barcode.substring(i, separatorIndex); |
i = separatorIndex; |
} |
} else { |
if (i + aii.length > length) { |
throw new GS1ParseException(ai.toString(), ERROR_CODE_INSUFFICIENT_VALUE_LENGTH, "Insufficient value length"); |
} |
value = barcode.substring(i, i + aii.length); |
i += aii.length - 1; |
} |
if (aii.decimalPoint && decimalPoint > 0) { |
if (decimalPoint >= value.length()) |
throw new GS1ParseException(ai.toString(), ERROR_CODE_WRONG_DECIMAL_POINT, "Decimal point more then value length"); |
try { |
value = String.valueOf(Double.valueOf(value.substring(0, value.length() - decimalPoint) + "." + value.substring(value.length() - decimalPoint))); |
} catch (NumberFormatException e) { |
throw new GS1ParseException(ai.toString(), ERROR_CODE_CONVERT_DECIMAL_VALUE, "Error convert decimal point value"); |
} |
} |
attr.put(ai.toString(), value); |
ai.setLength(0); |
} |
} else |
ai.append(barcode.charAt(i)); |
} |
if (ai.length() > 0) |
throw new GS1ParseException(ai.toString(), ERROR_CODE_INCOMPLETE_AI, "Incomplete AI"); |
return attr; |
} |
public String format(GS1AIElements values) { |
StringBuilder b = new StringBuilder(); |
int size = values.size(); |
for (int i = 0; i < size; i++) { |
String k = values.getKey(i); |
GS1ApplicationIdentifier ai = GS1AIElements.getApplicationIdentifier(k); |
final String value = values.getValue(i); |
b.append(k); |
b.append(value); |
if (ai.variableLength && i < size - 1) { |
b.append((char) this.separator); |
} |
} |
return b.toString(); |
} |
public String formatZPL(GS1AIElements values) { |
StringBuilder b = new StringBuilder(); |
int size = values.size(); |
b.append("@1"); |
for (int i = 0; i < size; i++) { |
String k = values.getKey(i); |
GS1ApplicationIdentifier ai = GS1AIElements.getApplicationIdentifier(k); |
final String value = values.getValue(i); |
b.append(k); |
b.append(value); |
if (ai.variableLength && i < size - 1) { |
b.append("@d029"); |
} |
} |
return b.toString(); |
} |
public String formatDataMatrix(GS1AIElements values) { |
StringBuilder b = new StringBuilder(); |
int size = values.size(); |
// b.append(FNC1_SEPARATOR); |
for (int i = 0; i < size; i++) { |
String k = values.getKey(i); |
GS1ApplicationIdentifier ai = GS1AIElements.getApplicationIdentifier(k); |
final String value = values.getValue(i); |
b.append('['); |
b.append(k); |
b.append(']'); |
b.append(value); |
if (ai.variableLength && i < size - 1) { |
// b.append(GS_SEPARATOR); |
} |
} |
return b.toString(); |
} |
public static void main(String[] args) throws GS1ParseException { |
GS1Util p = new GS1Util('_'); |
System.out.println("GS1Util.main()" + (char) p.separator); |
String barcode = "0104607018700852111806051718062910180605_211"; |
// barcode = "01088888931021461712031510W1040190"; |
GS1AIElements values = p.parse(barcode); |
values.dump(System.out); |
values.put("391", "kookkk"); |
System.out.println(p.format(values)); |
System.out.println(values.formatHumanReadable()); |
GS1AIElements valuesMax = new GS1AIElements(); |
valuesMax.put("02", "01234567891234"); |
valuesMax.put("15", "201202"); |
valuesMax.put("37", "12345678"); |
valuesMax.put("10", "12345678901234567890"); |
GS1Util pStd = new GS1Util(); |
System.out.println(p.format(valuesMax)); |
System.out.println(p.format(valuesMax).length()); |
System.out.println(pStd.format(valuesMax)); |
System.out.println(pStd.format(valuesMax).length()); |
System.err.println("GS1Util.main() GS128 from barcode reader"); |
String gs1128 = "]C10207612345678900152012153745646578"; |
GS1Util p2 = new GS1Util(); |
values = p2.parseFromScanner(gs1128); |
values.dump(System.out); |
} |
public static String showAllChars(String s) { |
StringBuilder b = new StringBuilder(); |
for (int i = 0; i < s.length(); i++) { |
if (ISO646.isValid(s.charAt(i))) { |
b.append(s.charAt(i)); |
} else { |
b.append("[" + (int) s.charAt(i) + "]"); |
} |
} |
return b.toString(); |
} |
} |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/gs1/GS1AIElements.java |
---|
New file |
0,0 → 1,259 |
package org.openconcerto.modules.label.gs1; |
import java.io.PrintStream; |
import java.util.ArrayList; |
import java.util.HashMap; |
public class GS1AIElements { |
static final HashMap<String, GS1ApplicationIdentifier> GS1_128_AI = new HashMap<>(); |
static { |
GS1_128_AI.put("00", new GS1ApplicationIdentifier(0, 18, false, false)); |
GS1_128_AI.put("01", new GS1ApplicationIdentifier(0, 14, false, false)); |
GS1_128_AI.put("02", new GS1ApplicationIdentifier(0, 14, false, false)); |
GS1_128_AI.put("10", new GS1ApplicationIdentifier(0, 20, true, false)); |
GS1_128_AI.put("11", new GS1ApplicationIdentifier(0, 6, false, false)); |
GS1_128_AI.put("12", new GS1ApplicationIdentifier(0, 6, false, false)); |
GS1_128_AI.put("13", new GS1ApplicationIdentifier(0, 6, false, false)); |
GS1_128_AI.put("14", new GS1ApplicationIdentifier(0, 6, false, false)); |
GS1_128_AI.put("15", new GS1ApplicationIdentifier(0, 6, false, false)); |
GS1_128_AI.put("17", new GS1ApplicationIdentifier(0, 6, false, false)); |
GS1_128_AI.put("20", new GS1ApplicationIdentifier(0, 2, false, false)); |
GS1_128_AI.put("21", new GS1ApplicationIdentifier(0, 20, true, false)); |
GS1_128_AI.put("240", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("241", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("242", new GS1ApplicationIdentifier(0, 6, true, false)); |
GS1_128_AI.put("250", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("251", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("253", new GS1ApplicationIdentifier(13, 30, true, false)); |
GS1_128_AI.put("254", new GS1ApplicationIdentifier(0, 20, true, false)); |
GS1_128_AI.put("255", new GS1ApplicationIdentifier(13, 25, true, false)); |
GS1_128_AI.put("30", new GS1ApplicationIdentifier(0, 8, true, false)); |
GS1_128_AI.put("310", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("311", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("312", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("313", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("314", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("315", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("316", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("320", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("321", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("322", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("323", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("324", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("325", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("326", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("327", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("328", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("329", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("330", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("331", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("332", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("333", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("334", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("335", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("336", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("340", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("341", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("342", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("343", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("344", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("345", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("346", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("347", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("348", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("349", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("350", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("351", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("352", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("353", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("354", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("355", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("356", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("357", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("360", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("361", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("362", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("363", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("364", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("365", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("366", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("367", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("368", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("369", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("37", new GS1ApplicationIdentifier(0, 8, true, false)); |
GS1_128_AI.put("390", new GS1ApplicationIdentifier(0, 15, true, true)); |
GS1_128_AI.put("391", new GS1ApplicationIdentifier(3, 18, true, true)); |
GS1_128_AI.put("392", new GS1ApplicationIdentifier(0, 15, true, true)); |
GS1_128_AI.put("393", new GS1ApplicationIdentifier(3, 18, true, true)); |
GS1_128_AI.put("400", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("401", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("402", new GS1ApplicationIdentifier(0, 17, false, false)); |
GS1_128_AI.put("403", new GS1ApplicationIdentifier(3, 30, true, false)); |
GS1_128_AI.put("410", new GS1ApplicationIdentifier(0, 17, false, false)); |
GS1_128_AI.put("411", new GS1ApplicationIdentifier(0, 17, false, false)); |
GS1_128_AI.put("412", new GS1ApplicationIdentifier(0, 17, false, false)); |
GS1_128_AI.put("413", new GS1ApplicationIdentifier(0, 17, false, false)); |
GS1_128_AI.put("414", new GS1ApplicationIdentifier(0, 17, false, false)); |
GS1_128_AI.put("420", new GS1ApplicationIdentifier(0, 20, true, false)); |
GS1_128_AI.put("421", new GS1ApplicationIdentifier(0, 0, true, false)); |
GS1_128_AI.put("422", new GS1ApplicationIdentifier(0, 3, false, false)); |
GS1_128_AI.put("423", new GS1ApplicationIdentifier(3, 15, true, false)); |
GS1_128_AI.put("424", new GS1ApplicationIdentifier(0, 3, false, false)); |
GS1_128_AI.put("425", new GS1ApplicationIdentifier(0, 3, false, false)); |
GS1_128_AI.put("426", new GS1ApplicationIdentifier(0, 3, false, false)); |
GS1_128_AI.put("7001", new GS1ApplicationIdentifier(0, 13, false, false)); |
GS1_128_AI.put("7002", new GS1ApplicationIdentifier(0, 30, false, false)); |
GS1_128_AI.put("7003", new GS1ApplicationIdentifier(0, 10, false, false)); |
GS1_128_AI.put("7004", new GS1ApplicationIdentifier(0, 4, true, false)); |
GS1_128_AI.put("8001", new GS1ApplicationIdentifier(0, 14, false, false)); |
GS1_128_AI.put("8002", new GS1ApplicationIdentifier(0, 20, true, false)); |
GS1_128_AI.put("8003", new GS1ApplicationIdentifier(14, 30, true, false)); |
GS1_128_AI.put("8004", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("8005", new GS1ApplicationIdentifier(0, 6, false, false)); |
GS1_128_AI.put("8006", new GS1ApplicationIdentifier(0, 18, false, false)); |
GS1_128_AI.put("8007", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("8008", new GS1ApplicationIdentifier(8, 12, true, false)); |
GS1_128_AI.put("8018", new GS1ApplicationIdentifier(0, 18, false, false)); |
GS1_128_AI.put("8020", new GS1ApplicationIdentifier(0, 25, true, false)); |
GS1_128_AI.put("8100", new GS1ApplicationIdentifier(0, 6, false, false)); |
GS1_128_AI.put("8101", new GS1ApplicationIdentifier(0, 10, false, false)); |
GS1_128_AI.put("8102", new GS1ApplicationIdentifier(0, 2, false, false)); |
GS1_128_AI.put("8110", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("8200", new GS1ApplicationIdentifier(0, 70, true, false)); |
GS1_128_AI.put("90", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("91", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("92", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("93", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("94", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("95", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("96", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("97", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("98", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("99", new GS1ApplicationIdentifier(0, 30, true, false)); |
} |
private final ArrayList<String> keysAndValues = new ArrayList<>(); |
public GS1AIElements() { |
} |
public boolean isEmpty() { |
return this.keysAndValues.isEmpty(); |
} |
public boolean containsKey(String key) { |
final int size = this.keysAndValues.size(); |
for (int i = 0; i < size; i += 2) { |
if (this.keysAndValues.get(i).equals(key)) { |
return true; |
} |
} |
return false; |
} |
public boolean containsValue(String value) { |
final int size = this.keysAndValues.size(); |
for (int i = 1; i < size; i += 2) { |
if (this.keysAndValues.get(i).equals(value)) { |
return true; |
} |
} |
return false; |
} |
public String getKey(int index) { |
return this.keysAndValues.get(index * 2); |
} |
public String getValue(int index) { |
return this.keysAndValues.get(1 + index * 2); |
} |
public String get(String key) { |
final int size = this.keysAndValues.size(); |
for (int i = 0; i < size; i += 2) { |
if (this.keysAndValues.get(i).equals(key)) { |
return this.keysAndValues.get(i + 1); |
} |
} |
return null; |
} |
public String put(String key, String value) { |
GS1ApplicationIdentifier ai = GS1_128_AI.get(key); |
if (ai == null) { |
throw new IllegalArgumentException("AI " + key + " unknown"); |
} |
if (ai.variableLength) { |
if (value.length() < ai.minLength) { |
throw new IllegalArgumentException("AI " + key + " value length must >= " + ai.minLength + " but is " + value.length() + " for value " + value); |
} |
if (value.length() > ai.length) { |
throw new IllegalArgumentException("AI " + key + " value length must be <= " + ai.length + " but is " + value.length() + " for value " + value); |
} |
} else { |
if (value.length() != ai.length) { |
throw new IllegalArgumentException("AI " + key + " value length must be " + ai.length + " but is " + value.length() + " for value " + value); |
} |
} |
final int size = this.keysAndValues.size(); |
for (int i = 0; i < size; i += 2) { |
if (this.keysAndValues.get(i).equals(key)) { |
final String old = this.keysAndValues.get(i + 1); |
this.keysAndValues.set(i + 1, value); |
return old; |
} |
} |
this.keysAndValues.add(key); |
this.keysAndValues.add(value); |
return null; |
} |
public String remove(String key) { |
final int size = this.keysAndValues.size(); |
for (int i = 0; i < size; i += 2) { |
if (this.keysAndValues.get(i).equals(key)) { |
this.keysAndValues.remove(i); |
return this.keysAndValues.remove(i); |
} |
} |
return null; |
} |
public void clear() { |
this.keysAndValues.clear(); |
} |
public int size() { |
return this.keysAndValues.size() / 2; |
} |
public void dump(PrintStream out) { |
for (int i = 0; i < size(); i++) { |
out.print("("); |
out.print(getKey(i)); |
out.print(")"); |
out.println(getValue(i)); |
} |
out.flush(); |
} |
public static GS1ApplicationIdentifier getApplicationIdentifier(String k) { |
return GS1_128_AI.get(k); |
} |
public String formatHumanReadable() { |
StringBuilder b = new StringBuilder(); |
int size = size(); |
for (int i = 0; i < size; i++) { |
b.append('('); |
b.append(this.getKey(i)); |
b.append(')'); |
b.append(this.getValue(i)); |
} |
return b.toString(); |
} |
} |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/gs1/GS1ApplicationIdentifier.java |
---|
New file |
0,0 → 1,17 |
package org.openconcerto.modules.label.gs1; |
public class GS1ApplicationIdentifier { |
public final int minLength; |
public final int length; |
public final boolean variableLength; |
public final boolean decimalPoint; |
public GS1ApplicationIdentifier(int minLength, int length, boolean variableLength, boolean decimalPoint) { |
this.minLength = minLength; |
this.length = length; |
this.variableLength = variableLength; |
this.decimalPoint = decimalPoint; |
} |
} |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/gs1/GS1ParseException.java |
---|
New file |
0,0 → 1,18 |
package org.openconcerto.modules.label.gs1; |
public class GS1ParseException extends Exception { |
final String ai; |
final int errorCode; |
final String errorMessage; |
public GS1ParseException(final String ai, final int errorCode, final String errorMessage) { |
this.ai = ai; |
this.errorCode = errorCode; |
this.errorMessage = errorMessage; |
} |
@Override |
public String toString() { |
return "AI: " + ai + ", errorCode: " + errorCode + ", errorMessage: " + errorMessage; |
} |
} |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/graphicspl/GraphicsPL.java |
---|
New file |
0,0 → 1,128 |
package org.openconcerto.modules.label.graphicspl; |
import java.awt.Color; |
import java.awt.Graphics; |
import java.awt.Graphics2D; |
import java.awt.image.BufferedImage; |
import java.awt.print.PageFormat; |
import java.awt.print.Printable; |
import java.awt.print.PrinterException; |
import java.awt.print.PrinterJob; |
import java.io.ByteArrayInputStream; |
import java.io.File; |
import java.io.IOException; |
import java.nio.charset.StandardCharsets; |
import java.nio.file.Files; |
import javax.imageio.ImageIO; |
import javax.xml.parsers.DocumentBuilder; |
import javax.xml.parsers.DocumentBuilderFactory; |
import javax.xml.parsers.ParserConfigurationException; |
import org.w3c.dom.Document; |
import org.w3c.dom.Element; |
import org.xml.sax.SAXException; |
public class GraphicsPL { |
String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?><graphicspl height=\"400\" width=\"600\"/>"; |
Document doc; |
private File imgDir; |
public static void main(String[] args) throws Exception { |
GraphicsPL g = new GraphicsPL(); |
g.load(new File("Template/Labels/test.graphicspl")); |
BufferedImage img = g.createImage(10); |
ImageIO.write(img, "png", new File("gpl.png")); |
String zpl = g.getZPL(); |
System.out.println(zpl); |
Printable p = g.createPrintable(); |
PrinterJob job = PrinterJob.getPrinterJob(); |
job.setPrintable(p); |
boolean ok = job.printDialog(); |
if (ok) { |
job.print(); |
} |
} |
public Printable createPrintable() { |
final Element root = this.doc.getDocumentElement(); |
int dpi = 300; |
if (root.hasAttribute("dpi")) { |
dpi = Integer.parseInt(root.getAttribute("dpi")); |
} |
float printRatio = 1f; |
if (root.hasAttribute("printratio")) { |
printRatio = Float.parseFloat(root.getAttribute("printratio")); |
} |
return createPrintable(dpi, printRatio); |
} |
public Printable createPrintable(int dpi, float printRatio) { |
return new Printable() { |
@Override |
public int print(Graphics graphics, PageFormat pf, int pageIndex) throws PrinterException { |
if (pageIndex > 0) { |
return NO_SUCH_PAGE; |
} |
final Element root = GraphicsPL.this.doc.getDocumentElement(); |
final int width = Math.round(printRatio * Integer.parseInt(root.getAttribute("width"))); |
final int height = Math.round(printRatio * Integer.parseInt(root.getAttribute("height"))); |
final Graphics2D g2d = (Graphics2D) graphics; |
float ratio = (printRatio * dpi) / 72f; |
try { |
final BufferedImage img = createImage(ratio); |
g2d.drawImage(img, (int) Math.round(pf.getImageableX()), (int) Math.round(pf.getImageableY()), width, height, null); |
} catch (ParserConfigurationException | SAXException | IOException e) { |
throw new PrinterException(e.getMessage()); |
} |
return PAGE_EXISTS; |
} |
}; |
} |
private String getZPL() throws IOException { |
final ZPLRenderer renderer = new ZPLRenderer(); |
renderer.render(this); |
return renderer.getZPL(); |
} |
private BufferedImage createImage(float ratio) throws ParserConfigurationException, SAXException, IOException { |
final Element root = this.doc.getDocumentElement(); |
final int width = Math.round(ratio * Integer.parseInt(root.getAttribute("width"))); |
final int height = Math.round(ratio * Integer.parseInt(root.getAttribute("height"))); |
final BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); |
final Graphics2D graphics = (Graphics2D) img.getGraphics(); |
graphics.setColor(Color.WHITE); |
graphics.fillRect(0, 0, width, height); |
final Graphics2DRenderer renderer = new Graphics2DRenderer(graphics, ratio); |
renderer.render(this); |
graphics.dispose(); |
return img; |
} |
public Document getDocument() { |
return this.doc; |
} |
private void load(File file) throws ParserConfigurationException, SAXException, IOException { |
this.xml = new String(Files.readAllBytes(file.toPath())); |
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); |
// factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); |
final DocumentBuilder builder = factory.newDocumentBuilder(); |
final ByteArrayInputStream input = new ByteArrayInputStream(this.xml.getBytes(StandardCharsets.UTF_8)); |
this.doc = builder.parse(input); |
this.doc.getDocumentElement().normalize(); |
this.imgDir = file.getParentFile(); |
} |
public File getImageDir() { |
return this.imgDir; |
} |
public void setImageDir(File dir) { |
this.imgDir = dir; |
} |
} |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/graphicspl/ZPLRenderer.java |
---|
New file |
0,0 → 1,234 |
package org.openconcerto.modules.label.graphicspl; |
import java.awt.Color; |
import java.awt.image.BufferedImage; |
import java.io.ByteArrayOutputStream; |
import java.io.IOException; |
import org.openconcerto.utils.ImageUtils; |
import org.openconcerto.utils.StringUtils; |
public class ZPLRenderer extends GPLRenderer { |
private StringBuilder sb = new StringBuilder(); |
public ZPLRenderer() { |
sb.append("^XA\n^CI28\n"); |
} |
@Override |
public void drawText(int x, int y, String text, int align, String fontName, int fontSize, Color color, int maxWidth, boolean wrap) { |
sb.append("^CF0,"); |
sb.append(fontSize); |
// |
if (align == ALIGN_RIGHT) { |
sb.append("^FO"); |
sb.append('0'); |
sb.append(','); |
sb.append(y); |
sb.append("^FB"); |
sb.append(x); |
sb.append(",9999,0"); |
sb.append(",R,0"); |
} else if (align == ALIGN_CENTER) { |
sb.append("^FO"); |
sb.append('0'); |
sb.append(','); |
sb.append(y); |
sb.append("^FB"); |
sb.append(x * 2); |
sb.append(",9999,0"); |
sb.append(",C,0"); |
} else { |
sb.append("^FO"); |
sb.append(x); |
sb.append(','); |
sb.append(y); |
// |
sb.append("^FB"); |
sb.append(maxWidth); |
sb.append(",9999,0"); |
sb.append(",L,0"); |
} |
// |
sb.append("^FD"); |
sb.append(text); |
if (align == ALIGN_CENTER) { |
sb.append("\\&"); |
} |
sb.append("^FS\n"); |
} |
@Override |
public void drawImage(int x, int y, int w, int h, BufferedImage img) { |
try { |
if (w != img.getWidth() || h != img.getHeight()) { |
img = ImageUtils.createQualityResizedImage(img, w, h); |
} |
final int bytesPerRow = (img.getWidth() + 7) / 8; |
int size = bytesPerRow * img.getHeight(); |
byte[] data = getData(img); |
sb.append("^FO"); |
sb.append(x); |
sb.append(","); |
sb.append(y); |
sb.append("^GFA"); |
sb.append(","); |
sb.append(size); |
sb.append(","); |
sb.append(size); |
sb.append(","); |
sb.append(bytesPerRow); |
sb.append(","); |
sb.append(StringUtils.bytesToHexString(data)); |
sb.append("^FS\n"); |
} catch (IOException e) { |
throw new IllegalStateException(e); |
} |
} |
byte[] getData(BufferedImage img) throws IOException { |
ByteArrayOutputStream out = new ByteArrayOutputStream(1024); |
int width = img.getWidth(); |
int height = img.getHeight(); |
// Lines |
for (int i = 0; i < height; i++) { |
int[] pixels = new int[width]; |
img.getRGB(0, i, width, 1, pixels, 0, 4); |
final byte[] encodedLine = encodeLine(pixels); |
out.write(encodedLine); |
} |
return out.toByteArray(); |
} |
private byte[] encodeLine(int[] pixels) throws IOException { |
final int bytesPerRow = (pixels.length + 7) / 8; |
byte[] bytesToEncode = new byte[bytesPerRow]; |
int index = 0; |
for (int i = 0; i < bytesToEncode.length; i++) { |
int points = 0; |
for (int j = 0; j < 8; j++) { |
int c = 0; |
if (index < pixels.length) { |
c = pixels[index]; |
int a = (c & 0xff000000) >> 24; |
int r = (c & 0x00ff0000) >> 16; |
int g = (c & 0x0000ff00) >> 8; |
int b = c & 0x000000ff; |
int grayScale = (int) (21.2671 * r + 71.5160 * g + 7.2169 * b); |
boolean isBlack = grayScale < 12000; |
points = points * 2; |
if (isBlack && a < 0) { |
points++; |
} |
} else { |
points = points * 2; |
} |
index++; |
} |
bytesToEncode[i] = (byte) points; |
} |
return bytesToEncode; |
} |
@Override |
public void fillRectangle(int x, int y, int w, int h, Color color) { |
sb.append("^FO"); |
sb.append(x); |
sb.append(','); |
sb.append(y); |
sb.append("^GB"); |
sb.append(w); |
sb.append(','); |
sb.append(h); |
sb.append(','); |
sb.append(Math.min(w, h)); |
sb.append("^FS\n"); |
} |
@Override |
public void drawRectangle(int x, int y, int w, int h, Color color, int lineWidth) { |
sb.append("^FO"); |
sb.append(x); |
sb.append(','); |
sb.append(y); |
sb.append("^GB"); |
sb.append(w); |
sb.append(','); |
sb.append(h); |
sb.append(','); |
sb.append(lineWidth); |
sb.append("^FS\n"); |
} |
@Override |
public void drawBarcode(int x, int y, int h, int type, String code, int moduleWidth, int fontSize) { |
code = code.trim(); |
sb.append("^FO"); |
sb.append(x); |
sb.append(','); |
sb.append(y); |
sb.append("^CF0,"); |
sb.append(fontSize); |
if (type == BARCODE_EAN13) { |
sb.append("^BY"); |
sb.append(moduleWidth); |
sb.append("^BEN,"); |
sb.append(h); |
sb.append(",Y,N"); |
} else if (type == BARCODE_EAN8) { |
sb.append("^BY"); |
sb.append(moduleWidth); |
sb.append("^B8N,"); |
sb.append(h); |
sb.append(",Y,N"); |
} else if (type == BARCODE_DATAMATRIX) { |
sb.append("^BXN,"); |
sb.append(moduleWidth); |
sb.append(",200"); |
} else if (type == BARCODE_CODE128) { |
sb.append("^BY"); |
sb.append(moduleWidth - 1); |
sb.append("^BCN,"); |
sb.append(h); |
sb.append(",Y,N,Y,D"); |
} else if (type == BARCODE_CODE128_GS1) { |
sb.append("^BY"); |
sb.append(moduleWidth - 1); |
sb.append("^BCN,"); |
sb.append(h); |
sb.append(",Y,N,Y,N"); |
} |
sb.append("^FD"); |
sb.append(code); |
sb.append("^FS\n"); |
} |
public String getZPL() { |
return sb.toString() + "\n^XZ"; |
} |
} |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/graphicspl/Graphics2DRenderer.java |
---|
New file |
0,0 → 1,116 |
package org.openconcerto.modules.label.graphicspl; |
import java.awt.BasicStroke; |
import java.awt.Color; |
import java.awt.Font; |
import java.awt.Graphics2D; |
import java.awt.RenderingHints; |
import java.awt.Stroke; |
import java.awt.geom.AffineTransform; |
import java.awt.image.BufferedImage; |
import uk.org.okapibarcode.backend.Code128; |
import uk.org.okapibarcode.backend.DataMatrix; |
import uk.org.okapibarcode.backend.Ean; |
import uk.org.okapibarcode.backend.QrCode; |
import uk.org.okapibarcode.backend.Symbol; |
import uk.org.okapibarcode.output.Java2DRenderer; |
public class Graphics2DRenderer extends GPLRenderer { |
private final Graphics2D g; |
public Graphics2DRenderer(Graphics2D g, float ratio) { |
super(ratio); |
this.g = g; |
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); |
} |
@Override |
public void drawText(int x, int y, String text, int align, String fontName, int fontSize, Color color, int maxWidth, boolean wrap) { |
final Font font = new Font(fontName, Font.PLAIN, fontSize); |
g.setFont(font.deriveFont(fontSize)); |
g.setColor(color); |
y += g.getFontMetrics().getAscent() - g.getFontMetrics().getDescent(); |
if (align == ALIGN_RIGHT) { |
int w = (int) g.getFontMetrics().getStringBounds(text, g).getWidth(); |
g.drawString(text, x - w, y); |
} else if (align == ALIGN_CENTER) { |
int w = (int) (g.getFontMetrics().getStringBounds(text, g).getWidth() / 2D); |
g.drawString(text, x - w, y); |
} else { |
g.drawString(text, x, y); |
} |
} |
@Override |
public void drawImage(int x, int y, int w, int h, BufferedImage img) { |
g.drawImage(img, x, y, x + w, y + h, 0, 0, img.getWidth(), img.getHeight(), null); |
} |
@Override |
public void fillRectangle(int x, int y, int w, int h, Color color) { |
g.setColor(color); |
g.fillRect(x, y, w, h); |
} |
@Override |
public void drawRectangle(int x, int y, int w, int h, Color color, int lineWidth) { |
Stroke s = g.getStroke(); |
g.setColor(color); |
if (lineWidth != 1) { |
g.setStroke(new BasicStroke(lineWidth)); |
} |
g.drawRect(x, y, w, h); |
g.setStroke(s); |
} |
@Override |
public void drawBarcode(int x, int y, int h, int type, String code, int moduleWidth, int fontSize) { |
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); |
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); |
Symbol symbol = null; |
if (type == BARCODE_EAN8) { |
symbol = new Ean(); |
((Ean) symbol).setMode(Ean.Mode.EAN8); |
symbol.setBarHeight(h); |
} else if (type == BARCODE_EAN13) { |
symbol = new Ean(); |
((Ean) symbol).setMode(Ean.Mode.EAN13); |
symbol.setBarHeight(h); |
} else if (type == BARCODE_CODE128) { |
symbol = new Code128(); |
symbol.setDataType(Symbol.DataType.GS1); |
symbol.setBarHeight(h); |
} else if (type == BARCODE_CODE128_GS1) { |
symbol = new Code128(); |
symbol.setBarHeight(h); |
} else if (type == BARCODE_DATAMATRIX) { |
symbol = new DataMatrix(); |
} else if (type == BARCODE_QRCODE) { |
symbol = new QrCode(); |
} |
if (symbol == null) { |
return; |
} |
symbol.setModuleWidth(moduleWidth); |
symbol.setFontSize(fontSize); |
symbol.setContent(code.trim()); |
AffineTransform aT = g.getTransform(); |
g.setTransform(AffineTransform.getTranslateInstance(x, y)); |
Java2DRenderer renderer = new Java2DRenderer(g, 1, Color.WHITE, Color.BLACK); |
renderer.render(symbol); |
g.setTransform(aT); |
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); |
} |
} |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/graphicspl/GPLRenderer.java |
---|
New file |
0,0 → 1,166 |
package org.openconcerto.modules.label.graphicspl; |
import java.awt.Color; |
import java.awt.image.BufferedImage; |
import java.io.File; |
import java.io.IOException; |
import javax.imageio.ImageIO; |
import org.w3c.dom.Document; |
import org.w3c.dom.Element; |
import org.w3c.dom.Node; |
import org.w3c.dom.NodeList; |
public abstract class GPLRenderer { |
public static final int ALIGN_LEFT = 0; |
public static final int ALIGN_RIGHT = 1; |
public static final int ALIGN_CENTER = 2; |
public static final int BARCODE_EAN8 = 0; |
public static final int BARCODE_EAN13 = 1; |
public static final int BARCODE_CODE128 = 2; |
public static final int BARCODE_CODE128_GS1 = 3; |
public static final int BARCODE_DATAMATRIX = 4; |
public static final int BARCODE_QRCODE = 5; |
private final float ratio; |
public GPLRenderer(float ratio) { |
this.ratio = ratio; |
} |
public GPLRenderer() { |
this.ratio = 1.0f; |
} |
public float getRatio() { |
return ratio; |
} |
public void render(GraphicsPL graphicsPL) throws IOException { |
Document doc = graphicsPL.getDocument(); |
final int width = Integer.parseInt(doc.getDocumentElement().getAttribute("width")); |
NodeList nodeList = doc.getFirstChild().getChildNodes(); |
int size = nodeList.getLength(); |
for (int i = 0; i < size; i++) { |
if (nodeList.item(i).getNodeType() == Node.ELEMENT_NODE) { |
Element element = (Element) nodeList.item(i); |
String name = element.getNodeName(); |
if (name.equals("text")) { |
int x = Math.round(ratio * Integer.parseInt(element.getAttribute("x"))); |
int y = Math.round(ratio * Integer.parseInt(element.getAttribute("y"))); |
String txt = element.getTextContent(); |
Color color = Color.BLACK; |
if (element.hasAttribute("color")) { |
color = Color.decode(element.getAttribute("color")); |
} |
int fontSize = Math.round(ratio * Integer.parseInt(element.getAttribute("fontsize"))); |
String fontName = element.getAttribute("font"); |
int maxWidth = width - x; |
boolean wrap = false; |
int align = ALIGN_LEFT; |
if (element.hasAttribute("align")) { |
if (element.getAttribute("align").equals("right")) { |
align = ALIGN_RIGHT; |
} else if (element.getAttribute("align").equals("center")) { |
align = ALIGN_CENTER; |
} |
} |
if (!element.hasAttribute("visible") || element.getAttribute("visible").equals("true")) { |
drawText(x, y, txt, align, fontName, fontSize, color, maxWidth, wrap); |
} |
} else if (name.equals("rectangle")) { |
int x = Math.round(ratio * Integer.parseInt(element.getAttribute("x"))); |
int y = Math.round(ratio * Integer.parseInt(element.getAttribute("y"))); |
int w = Math.round(ratio * Integer.parseInt(element.getAttribute("width"))); |
int h = Math.round(ratio * Integer.parseInt(element.getAttribute("height"))); |
Color color = Color.BLACK; |
if (element.hasAttribute("color")) { |
color = Color.decode(element.getAttribute("color")); |
} |
if (element.hasAttribute("fill") && element.getAttribute("fill").equals("true")) { |
fillRectangle(x, y, w, h, color); |
} else { |
int lineWidth = Math.round(ratio); |
if (element.hasAttribute("linewidth")) { |
lineWidth = Math.round(ratio * Integer.parseInt(element.getAttribute("linewidth"))); |
} |
drawRectangle(x, y, w, h, color, lineWidth); |
} |
} else if (name.equals("image")) { |
String fileName = element.getAttribute("file"); |
int x = Math.round(ratio * Integer.parseInt(element.getAttribute("x"))); |
int y = Math.round(ratio * Integer.parseInt(element.getAttribute("y"))); |
BufferedImage img = ImageIO.read(new File(graphicsPL.getImageDir(), fileName)); |
int w = Math.round(ratio * img.getWidth()); |
int h = Math.round(ratio * img.getHeight()); |
if (element.hasAttribute("width")) { |
w = Math.round(ratio * Integer.parseInt(element.getAttribute("width"))); |
} |
if (element.hasAttribute("height")) { |
h = Math.round(ratio * Integer.parseInt(element.getAttribute("height"))); |
} |
drawImage(x, y, w, h, img); |
} else if (name.equals("barcode")) { |
int x = Math.round(ratio * Integer.parseInt(element.getAttribute("x"))); |
int y = Math.round(ratio * Integer.parseInt(element.getAttribute("y"))); |
int h = 0; |
if (element.hasAttribute("height")) { |
h = Math.round(ratio * Integer.parseInt(element.getAttribute("height"))); |
} |
String type = element.getAttribute("type"); |
String code = element.getTextContent(); |
int t; |
if (type.equals("ean8")) { |
t = BARCODE_EAN8; |
} else if (type.equals("ean13")) { |
t = BARCODE_EAN13; |
} else if (type.equals("ean128")) { |
t = BARCODE_CODE128; |
} else if (type.equals("gs1")) { |
t = BARCODE_CODE128_GS1; |
} else if (type.equals("datamatrix")) { |
t = BARCODE_DATAMATRIX; |
if (h != 0) { |
System.err.println("ignoring datamatrix height attribute"); |
} |
} else if (type.equals("qrcode")) { |
t = BARCODE_QRCODE; |
} else { |
throw new IllegalArgumentException("unsupported barcode type : " + type); |
} |
int fontSize = Math.round(ratio * 8); |
if (element.hasAttribute("fontsize")) { |
fontSize = Math.round(ratio * Integer.parseInt(element.getAttribute("fontsize"))); |
} |
int moduleWidth = Math.round(ratio); |
if (element.hasAttribute("modulewidth")) { |
moduleWidth = Math.round(ratio * Integer.parseInt(element.getAttribute("modulewidth"))); |
} |
drawBarcode(x, y, h, t, code, moduleWidth, fontSize); |
} else { |
throw new IllegalStateException("unsupported primitive : " + name); |
} |
} |
} |
} |
public abstract void drawText(int x, int y, String text, int align, String fontName, int fontSize, Color color, int maxWidth, boolean wrap); |
public abstract void drawImage(int x, int y, int w, int h, BufferedImage img); |
public abstract void fillRectangle(int x, int y, int w, int h, Color color); |
public abstract void drawRectangle(int x, int y, int w, int h, Color color, int lineWidth); |
public abstract void drawBarcode(int x, int y, int h, int type, String code, int moduleWidth, int fontSize); |
} |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/oc-qrcode.png |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/oc-qrcode.png |
---|
New file |
Property changes: |
Added: svn:mime-type |
+application/octet-stream |
\ No newline at end of property |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/Label.java |
---|
New file |
0,0 → 1,5 |
package org.openconcerto.modules.label; |
public class Label { |
} |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/default-zpl.txt |
---|
New file |
0,0 → 1,20 |
^XA |
^FX Titre |
^CF0,42^FO150,43^FB750,1,0,L^FDXXXXXXXXXX^FS |
^FX DataMatrix |
^FO1010,20^BXN,7,200,,,,@^FDOPENCONCERTO^FS |
^FX Nom du produit |
^CF0,40 |
^FO150,140^FB700,1,0,L^FDYYYYYYYYYY^FS |
^FX Code barre |
^FO150,220 |
^BY3,2,300 |
^BCN,300,Y,N,Y,D |
^FD(02)7612345678900(15)201218(37)9999^FS |
^FO150,620^BY3,2,300^BCN,300,Y,N,Y,D^FD(10)LLLLLLLLL^FS |
^XZ |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/ModuleLabel.java |
---|
4,10 → 4,17 |
import java.awt.Graphics; |
import java.awt.event.ActionEvent; |
import java.awt.geom.Rectangle2D; |
import java.io.File; |
import java.io.IOException; |
import java.nio.charset.StandardCharsets; |
import java.util.ArrayList; |
import java.util.LinkedHashMap; |
import java.util.List; |
import java.util.Map.Entry; |
import java.util.TreeMap; |
import javax.swing.AbstractAction; |
import javax.swing.JFrame; |
import javax.swing.SwingWorker; |
import org.openconcerto.erp.generationDoc.provider.AdresseFullClientValueProvider; |
14,6 → 21,7 |
import org.openconcerto.erp.modules.AbstractModule; |
import org.openconcerto.erp.modules.ComponentsContext; |
import org.openconcerto.erp.modules.ModuleFactory; |
import org.openconcerto.sql.model.SQLRow; |
import org.openconcerto.sql.model.SQLRowAccessor; |
import org.openconcerto.sql.model.SQLRowValues; |
import org.openconcerto.sql.model.SQLRowValuesListFetcher; |
23,10 → 31,12 |
import org.openconcerto.sql.view.list.IListeAction.IListeEvent; |
import org.openconcerto.sql.view.list.RowAction.PredicateRowAction; |
import org.openconcerto.utils.ExceptionHandler; |
import org.openconcerto.utils.FileUtils; |
import org.openconcerto.utils.GestionDevise; |
import org.openconcerto.utils.StringUtils; |
public final class ModuleLabel extends AbstractModule { |
final LinkedHashMap<String, String> zplTemplates = new LinkedHashMap<String, String>(); |
public ModuleLabel(ModuleFactory f) throws IOException { |
super(f); |
34,6 → 44,9 |
@Override |
protected void setupComponents(ComponentsContext ctxt) { |
readTemplates(new File("Template/Labels")); |
readTemplates(new File("Configuration/Template/Labels")); |
final String actionName = "Imprimer les étiquettes"; |
final PredicateRowAction aArticle = new PredicateRowAction(new AbstractAction(actionName) { |
41,26 → 54,31 |
public void actionPerformed(ActionEvent arg0) { |
final IListe list = IListe.get(arg0); |
final List<Integer> selectedIDs = list.getSelection().getSelectedIDs(); |
final SwingWorker<List<SQLRowValues>, String> wworker = new SwingWorker<List<SQLRowValues>, String>() { |
final SQLTable tArticle = list.getSelectedRows().get(0).getTable(); |
final SwingWorker<List<RowValuesLabel>, String> wworker = new SwingWorker<List<RowValuesLabel>, String>() { |
@Override |
protected List<SQLRowValues> doInBackground() throws Exception { |
final SQLTable tArticle = list.getSelectedRows().get(0).getTable(); |
protected List<RowValuesLabel> doInBackground() throws Exception { |
final SQLRowValues graph = new SQLRowValues(tArticle); |
graph.putNulls("NOM", "PV_TTC"); |
final SQLRowValuesListFetcher fetcher = SQLRowValuesListFetcher.create(graph); |
final List<SQLRowValues> values = fetcher.fetch(new Where(tArticle.getKey(), selectedIDs)); |
return values; |
final List<SQLRowValues> rows = fetcher.fetch(new Where(tArticle.getKey(), selectedIDs)); |
final List<RowValuesLabel> list = new ArrayList<>(rows.size()); |
for (SQLRowValues row : rows) { |
list.add(new RowValuesLabel(row)); |
} |
return list; |
} |
@Override |
protected void done() { |
try { |
final List<SQLRowValues> values = get(); |
final List<RowValuesLabel> values = get(); |
final LabelFrame f = new LabelFrame(values, new LabelRenderer() { |
@Override |
public void paintLabel(Graphics g, SQLRowAccessor row, int x, int y, int gridWith, int gridHeight, float fontSize) { |
public void paintLabel(Graphics g, Label label, int x, int y, int gridWith, int gridHeight, float fontSize) { |
g.setColor(Color.BLACK); |
g.setFont(g.getFont().deriveFont(fontSize)); |
// Labels borders |
67,6 → 85,7 |
final int hBorder = 12; |
final int vBorder = 8; |
// Product name |
SQLRowValues row = ((RowValuesLabel) label).getSQLRowValues(); |
final String text = row.getString("NOM"); |
final List<String> l = StringUtils.wrap(text, g.getFontMetrics(), gridWith - 2 * hBorder); |
final int lineHeight = g.getFontMetrics().getHeight(); |
73,7 → 92,7 |
int lineY = y; |
final int margin = gridHeight - l.size() * lineHeight; |
if (margin > 0) { |
lineY += (int) (margin / 2); |
lineY += margin / 2; |
} |
for (String line : l) { |
g.drawString(line, x + hBorder, lineY); |
95,8 → 114,8 |
} catch (Exception e) { |
ExceptionHandler.handle("Erreur d'impression", e); |
} |
} |
}; |
}; |
wworker.execute(); |
} |
107,11 → 126,11 |
public void actionPerformed(ActionEvent arg0) { |
final IListe list = IListe.get(arg0); |
final List<Integer> selectedIDs = list.getSelection().getSelectedIDs(); |
final SwingWorker<List<SQLRowValues>, String> wworker = new SwingWorker<List<SQLRowValues>, String>() { |
final SQLTable tClient = list.getSelectedRows().get(0).getTable(); |
final SwingWorker<List<RowValuesLabel>, String> wworker = new SwingWorker<List<RowValuesLabel>, String>() { |
@Override |
protected List<SQLRowValues> doInBackground() throws Exception { |
final SQLTable tClient = list.getSelectedRows().get(0).getTable(); |
protected List<RowValuesLabel> doInBackground() throws Exception { |
final SQLRowValues graph = new SQLRowValues(tClient); |
graph.putNulls("NOM"); |
final SQLRowValues a1 = graph.putRowValues("ID_ADRESSE"); |
119,17 → 138,24 |
final SQLRowValues a2 = graph.putRowValues("ID_ADRESSE_L"); |
a2.putNulls(a2.getTable().getFieldsName()); |
final SQLRowValuesListFetcher fetcher = SQLRowValuesListFetcher.create(graph); |
final List<SQLRowValues> values = fetcher.fetch(new Where(tClient.getKey(), selectedIDs)); |
return values; |
final List<SQLRowValues> rows = fetcher.fetch(new Where(tClient.getKey(), selectedIDs)); |
final List<RowValuesLabel> list = new ArrayList<>(rows.size()); |
for (SQLRowValues row : rows) { |
list.add(new RowValuesLabel(row)); |
} |
return list; |
} |
@Override |
protected void done() { |
try { |
final List<SQLRowValues> values = get(); |
final List<RowValuesLabel> values = get(); |
final LabelFrame f = new LabelFrame(values, new LabelRenderer() { |
@Override |
public void paintLabel(Graphics g, SQLRowAccessor row, int x, int y, int gridWith, int gridHeight, float fontSize) { |
public void paintLabel(Graphics g, Label label, int x, int y, int gridWith, int gridHeight, float fontSize) { |
SQLRowValues row = ((RowValuesLabel) label).getSQLRowValues(); |
SQLRowAccessor rAddr = row.getForeign("ID_ADRESSE_L"); |
if (rAddr == null || rAddr.isUndefined()) { |
rAddr = row.getForeign("ID_ADRESSE"); |
165,8 → 191,8 |
} catch (Exception e) { |
ExceptionHandler.handle("Erreur d'impression", e); |
} |
} |
}; |
}; |
wworker.execute(); |
} |
176,13 → 202,94 |
aClient.setPredicate(IListeEvent.createSelectionCountPredicate(1, Integer.MAX_VALUE)); |
ctxt.getElement("ARTICLE").getRowActions().add(aArticle); |
ctxt.getElement("CLIENT").getRowActions().add(aClient); |
if (!this.zplTemplates.isEmpty()) { |
for (final Entry<String, String> entry : this.zplTemplates.entrySet()) { |
final String zpl = entry.getValue(); |
final PredicateRowAction action = new PredicateRowAction(new AbstractAction("Imprimer l'étiquette " + entry.getKey()) { |
@Override |
public void actionPerformed(ActionEvent arg0) { |
final ZPLPrinterPanel p = new ZPLPrinterPanel(zpl); |
final JFrame f = new JFrame(); |
final IListe list = IListe.get(arg0); |
final int idProduct = list.getSelection().getSelectedID(); |
final SQLTable tArticle = list.getSelectedRows().get(0).getTable(); |
final SwingWorker<SQLRowValues, String> wworker = new SwingWorker<SQLRowValues, String>() { |
@Override |
protected SQLRowValues doInBackground() throws Exception { |
final SQLRow row = tArticle.getRow(idProduct); |
row.fetchValues(); |
return row.asRowValues(); |
} |
@Override |
protected void done() { |
try { |
final SQLRowValues values = get(); |
p.initUI(values); |
f.setTitle(entry.getKey()); |
f.setContentPane(p); |
f.pack(); |
f.setLocationRelativeTo(null); |
f.setVisible(true); |
} catch (Exception e) { |
ExceptionHandler.handle("Erreur d'impression", e); |
} |
} |
}; |
wworker.execute(); |
} |
}, true, false); |
action.setPredicate(IListeEvent.createSelectionCountPredicate(1, 1)); |
ctxt.getElement("ARTICLE").getRowActions().add(action); |
} |
} |
} |
@Override |
protected void start() { |
} |
private void readTemplates(File templatesDir) { |
System.out.println("ModuleLabel.readTemplates() " + templatesDir.getAbsolutePath()); |
if (templatesDir.exists() && templatesDir.isDirectory()) { |
System.err.println("ModuleLabel.readTemplates() " + templatesDir.getAbsolutePath()); |
File[] files = templatesDir.listFiles(); |
if (files != null) { |
LinkedHashMap<String, String> map = new LinkedHashMap<>(); |
for (File f : files) { |
if (f.getName().endsWith(".zpl")) { |
try { |
String zpl = FileUtils.read(f, StandardCharsets.UTF_8); |
String name = f.getName().substring(0, f.getName().length() - 4).trim(); |
map.put(name, zpl); |
System.err.println("ModuleLabel.readTemplates() add " + name); |
} catch (Exception e) { |
System.err.println(this.getClass().getCanonicalName() + "start() cannot read zpl template : " + f.getAbsolutePath() + " : " + e.getMessage()); |
} |
} |
} |
// Tri de la map par clef |
final TreeMap<String, String> copy = new TreeMap<>(map); |
this.zplTemplates.clear(); |
this.zplTemplates.putAll(copy); |
} |
} else { |
System.err.println("ModuleLabel.readTemplates() " + templatesDir.getAbsolutePath() + " missing"); |
} |
} |
@Override |
protected void stop() { |
// nothing |
} |
} |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/32.png |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/32.png |
---|
New file |
Property changes: |
Added: svn:mime-type |
+application/octet-stream |
\ No newline at end of property |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/RowValuesLabel.java |
---|
New file |
0,0 → 1,16 |
package org.openconcerto.modules.label; |
import org.openconcerto.sql.model.SQLRowValues; |
public class RowValuesLabel extends Label { |
private SQLRowValues row; |
public RowValuesLabel(SQLRowValues row) { |
this.row = row; |
} |
public SQLRowValues getSQLRowValues() { |
return row; |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/util/EciMode.java |
---|
New file |
0,0 → 1,67 |
/* |
* Copyright 2018 Daniel Gredler |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.util; |
import java.nio.charset.Charset; |
import java.nio.charset.UnsupportedCharsetException; |
public class EciMode { |
public static final EciMode NONE = new EciMode(-1, null); |
public final int mode; |
public final Charset charset; |
private EciMode(final int mode, final Charset charset) { |
this.mode = mode; |
this.charset = charset; |
} |
public static EciMode of(final String data, final String charsetName, final int mode) { |
try { |
final Charset charset = Charset.forName(charsetName); |
if (charset.canEncode() && charset.newEncoder().canEncode(data)) { |
return new EciMode(mode, charset); |
} else { |
return NONE; |
} |
} catch (final UnsupportedCharsetException e) { |
return NONE; |
} |
} |
public EciMode or(final String data, final String charsetName, final int mode) { |
if (!equals(NONE)) { |
return this; |
} else { |
return of(data, charsetName, mode); |
} |
} |
@Override |
public boolean equals(final Object other) { |
return other instanceof EciMode && ((EciMode) other).mode == this.mode; |
} |
@Override |
public int hashCode() { |
return Integer.valueOf(this.mode).hashCode(); |
} |
@Override |
public String toString() { |
return "EciMode[mode=" + this.mode + ", charset=" + this.charset + "]"; |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/util/Strings.java |
---|
New file |
0,0 → 1,226 |
/* |
* Copyright 2018 Daniel Gredler |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.util; |
import static java.nio.charset.StandardCharsets.ISO_8859_1; |
import java.nio.charset.StandardCharsets; |
import uk.org.okapibarcode.backend.OkapiException; |
/** |
* String utility class. |
* |
* @author Daniel Gredler |
*/ |
public final class Strings { |
private Strings() { |
// utility class |
} |
/** |
* Replaces raw values with special placeholders, where applicable. |
* |
* @param s the string to add placeholders to |
* @return the specified string, with placeholders added |
* @see <a href="http://www.zint.org.uk/Manual.aspx?type=p&page=4">Zint placeholders</a> |
* @see #unescape(String, boolean) |
*/ |
public static String escape(final String s) { |
final StringBuilder sb = new StringBuilder(s.length() + 10); |
for (int i = 0; i < s.length(); i++) { |
final char c = s.charAt(i); |
switch (c) { |
case '\u0000': |
sb.append("\\0"); // null |
break; |
case '\u0004': |
sb.append("\\E"); // end of transmission |
break; |
case '\u0007': |
sb.append("\\a"); // bell |
break; |
case '\u0008': |
sb.append("\\b"); // backspace |
break; |
case '\u0009': |
sb.append("\\t"); // horizontal tab |
break; |
case '\n': |
sb.append("\\n"); // line feed |
break; |
case '\u000b': |
sb.append("\\v"); // vertical tab |
break; |
case '\u000c': |
sb.append("\\f"); // form feed |
break; |
case '\r': |
sb.append("\\r"); // carriage return |
break; |
case '\u001b': |
sb.append("\\e"); // escape |
break; |
case '\u001d': |
sb.append("\\G"); // group separator |
break; |
case '\u001e': |
sb.append("\\R"); // record separator |
break; |
case '\\': |
sb.append("\\\\"); // escape the escape character |
break; |
default: |
if (c >= 32 && c <= 126) { |
sb.append(c); // printable ASCII |
} else { |
final byte[] bytes = String.valueOf(c).getBytes(ISO_8859_1); |
final String hex = String.format("%02X", bytes[0] & 0xFF); |
sb.append("\\x").append(hex); |
} |
break; |
} |
} |
return sb.toString(); |
} |
/** |
* Replaces any special placeholders with their raw values (not including FNC values). |
* |
* @param s the string to check for placeholders |
* @param lenient whether or not to be lenient with unrecognized escape sequences |
* @return the specified string, with placeholders replaced |
* @see <a href="http://www.zint.org.uk/Manual.aspx?type=p&page=4">Zint placeholders</a> |
* @see #escape(String) |
*/ |
public static String unescape(final String s, final boolean lenient) { |
final StringBuilder sb = new StringBuilder(s.length()); |
for (int i = 0; i < s.length(); i++) { |
final char c = s.charAt(i); |
if (c != '\\') { |
sb.append(c); |
} else { |
if (i + 1 >= s.length()) { |
final String msg = "Error processing escape sequences: expected escape character, found end of string"; |
throw new OkapiException(msg); |
} else { |
final char c2 = s.charAt(i + 1); |
switch (c2) { |
case '0': |
sb.append('\u0000'); // null |
i++; |
break; |
case 'E': |
sb.append('\u0004'); // end of transmission |
i++; |
break; |
case 'a': |
sb.append('\u0007'); // bell |
i++; |
break; |
case 'b': |
sb.append('\u0008'); // backspace |
i++; |
break; |
case 't': |
sb.append('\u0009'); // horizontal tab |
i++; |
break; |
case 'n': |
sb.append('\n'); // line feed |
i++; |
break; |
case 'v': |
sb.append('\u000b'); // vertical tab |
i++; |
break; |
case 'f': |
sb.append('\u000c'); // form feed |
i++; |
break; |
case 'r': |
sb.append('\r'); // carriage return |
i++; |
break; |
case 'e': |
sb.append('\u001b'); // escape |
i++; |
break; |
case 'G': |
sb.append('\u001d'); // group separator |
i++; |
break; |
case 'R': |
sb.append('\u001e'); // record separator |
i++; |
break; |
case '\\': |
sb.append('\\'); // escape the escape character |
i++; |
break; |
case 'x': |
if (i + 3 >= s.length()) { |
final String msg = "Error processing escape sequences: expected hex sequence, found end of string"; |
throw new OkapiException(msg); |
} else { |
final char c3 = s.charAt(i + 2); |
final char c4 = s.charAt(i + 3); |
if (isHex(c3) && isHex(c4)) { |
final byte b = (byte) Integer.parseInt("" + c3 + c4, 16); |
sb.append(new String(new byte[] { b }, StandardCharsets.ISO_8859_1)); |
i += 3; |
} else { |
final String msg = "Error processing escape sequences: expected hex sequence, found '" + c3 + c4 + "'"; |
throw new OkapiException(msg); |
} |
} |
break; |
default: |
if (lenient) { |
sb.append(c); |
} else { |
throw new OkapiException("Error processing escape sequences: expected valid escape character, found '" + c2 + "'"); |
} |
} |
} |
} |
} |
return sb.toString(); |
} |
private static boolean isHex(final char c) { |
return c >= '0' && c <= '9' || c >= 'A' && c <= 'F' || c >= 'a' && c <= 'f'; |
} |
/** |
* Appends the specific integer to the specified string, in binary format, padded to the |
* specified number of digits. |
* |
* @param s the string to append to |
* @param value the value to append, in binary format |
* @param digits the number of digits to pad to |
*/ |
public static void binaryAppend(final StringBuilder s, final int value, final int digits) { |
final int start = 0x01 << digits - 1; |
for (int i = 0; i < digits; i++) { |
if ((value & start >> i) == 0) { |
s.append('0'); |
} else { |
s.append('1'); |
} |
} |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/util/Gs1.java |
---|
New file |
0,0 → 1,596 |
/* |
* Copyright 2018 Robin Stuart, Daniel Gredler |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.util; |
import uk.org.okapibarcode.backend.OkapiException; |
/** |
* GS1 utility class. |
*/ |
public final class Gs1 { |
private Gs1() { |
// utility class |
} |
/** |
* Verifies that the specified data is in good GS1 format <tt>"[AI]data"</tt> pairs, and returns |
* a reduced version of the input string containing FNC1 escape sequences instead of AI |
* brackets. With a few small exceptions, this code matches the Zint GS1 validation code as |
* closely as possible, in order to make it easier to keep in sync. |
* |
* @param s the data string to verify |
* @param fnc1 the string to use to represent FNC1 in the output |
* @return the input data, verified and with FNC1 strings added at the appropriate positions |
* @see <a href="https://sourceforge.net/p/zint/code/ci/master/tree/backend/gs1.c">Corresponding |
* Zint code</a> |
* @see <a href="http://www.gs1.org/docs/gsmp/barcodes/GS1_General_Specifications.pdf">GS1 |
* specification</a> |
*/ |
public static String verify(final String s, final String fnc1) { |
// Enforce compliance with GS1 General Specification |
// http://www.gs1.org/docs/gsmp/barcodes/GS1_General_Specifications.pdf |
final char[] source = s.toCharArray(); |
final StringBuilder reduced = new StringBuilder(source.length); |
final int[] ai_value = new int[100]; |
final int[] ai_location = new int[100]; |
final int[] data_location = new int[100]; |
final int[] data_length = new int[100]; |
int error_latch; |
/* Detect extended ASCII characters */ |
for (int i = 0; i < source.length; i++) { |
if (source[i] >= 128) { |
throw new OkapiException("Extended ASCII characters are not supported by GS1"); |
} |
if (source[i] < 32) { |
throw new OkapiException("Control characters are not supported by GS1"); |
} |
} |
/* Make sure we start with an AI */ |
if (source[0] != '[') { |
throw new OkapiException("Data does not start with an AI"); |
} |
/* Check the position of the brackets */ |
int bracket_level = 0; |
int max_bracket_level = 0; |
int ai_length = 0; |
int max_ai_length = 0; |
int min_ai_length = 5; |
int j = 0; |
boolean ai_latch = false; |
for (int i = 0; i < source.length; i++) { |
ai_length += j; |
if (j == 1 && source[i] != ']' && (source[i] < '0' || source[i] > '9')) { |
ai_latch = true; |
} |
if (source[i] == '[') { |
bracket_level++; |
j = 1; |
} |
if (source[i] == ']') { |
bracket_level--; |
if (ai_length < min_ai_length) { |
min_ai_length = ai_length; |
} |
j = 0; |
ai_length = 0; |
} |
if (bracket_level > max_bracket_level) { |
max_bracket_level = bracket_level; |
} |
if (ai_length > max_ai_length) { |
max_ai_length = ai_length; |
} |
} |
min_ai_length--; |
if (bracket_level != 0) { |
/* Not all brackets are closed */ |
throw new OkapiException("Malformed AI in input data (brackets don't match)"); |
} |
if (max_bracket_level > 1) { |
/* Nested brackets */ |
throw new OkapiException("Found nested brackets in input data"); |
} |
if (max_ai_length > 4) { |
/* AI is too long */ |
throw new OkapiException("Invalid AI in input data (AI too long)"); |
} |
if (min_ai_length <= 1) { |
/* AI is too short */ |
throw new OkapiException("Invalid AI in input data (AI too short)"); |
} |
if (ai_latch) { |
/* Non-numeric data in AI */ |
throw new OkapiException("Invalid AI in input data (non-numeric characters in AI)"); |
} |
int ai_count = 0; |
for (int i = 1; i < source.length; i++) { |
if (source[i - 1] == '[') { |
ai_location[ai_count] = i; |
ai_value[ai_count] = 0; |
for (j = 0; source[i + j] != ']'; j++) { |
ai_value[ai_count] *= 10; |
ai_value[ai_count] += Character.getNumericValue(source[i + j]); |
} |
ai_count++; |
} |
} |
for (int i = 0; i < ai_count; i++) { |
data_location[i] = ai_location[i] + 3; |
if (ai_value[i] >= 100) { |
data_location[i]++; |
} |
if (ai_value[i] >= 1000) { |
data_location[i]++; |
} |
data_length[i] = source.length - data_location[i]; |
for (j = source.length - 1; j >= data_location[i]; j--) { |
if (source[j] == '[') { |
data_length[i] = j - data_location[i]; |
} |
} |
} |
for (int i = 0; i < ai_count; i++) { |
if (data_length[i] == 0) { |
/* No data for given AI */ |
throw new OkapiException("Empty data field in input data"); |
} |
} |
// Check for valid AI values and data lengths according to GS1 General |
// Specification Release 18, January 2018 |
for (int i = 0; i < ai_count; i++) { |
error_latch = 2; |
switch (ai_value[i]) { |
// Length 2 Fixed |
case 20: // VARIANT |
if (data_length[i] != 2) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
break; |
// Length 3 Fixed |
case 422: // ORIGIN |
case 424: // COUNTRY PROCESS |
case 426: // COUNTRY FULL PROCESS |
if (data_length[i] != 3) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
break; |
// Length 4 Fixed |
case 8111: // POINTS |
if (data_length[i] != 4) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
break; |
// Length 6 Fixed |
case 11: // PROD DATE |
case 12: // DUE DATE |
case 13: // PACK DATE |
case 15: // BEST BY |
case 16: // SELL BY |
case 17: // USE BY |
case 7006: // FIRST FREEZE DATE |
case 8005: // PRICE PER UNIT |
if (data_length[i] != 6) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
break; |
// Length 10 Fixed |
case 7003: // EXPIRY TIME |
if (data_length[i] != 10) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
break; |
// Length 13 Fixed |
case 410: // SHIP TO LOC |
case 411: // BILL TO |
case 412: // PURCHASE FROM |
case 413: // SHIP FOR LOC |
case 414: // LOC NO |
case 415: // PAY TO |
case 416: // PROD/SERV LOC |
case 7001: // NSN |
if (data_length[i] != 13) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
break; |
// Length 14 Fixed |
case 1: // GTIN |
case 2: // CONTENT |
case 8001: // DIMENSIONS |
if (data_length[i] != 14) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
break; |
// Length 17 Fixed |
case 402: // GSIN |
if (data_length[i] != 17) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
break; |
// Length 18 Fixed |
case 0: // SSCC |
case 8006: // ITIP |
case 8017: // GSRN PROVIDER |
case 8018: // GSRN RECIPIENT |
if (data_length[i] != 18) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
break; |
// Length 2 Max |
case 7010: // PROD METHOD |
if (data_length[i] > 2) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
break; |
// Length 3 Max |
case 427: // ORIGIN SUBDIVISION |
case 7008: // AQUATIC SPECIES |
if (data_length[i] > 3) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
break; |
// Length 4 Max |
case 7004: // ACTIVE POTENCY |
if (data_length[i] > 4) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
break; |
// Length 6 Max |
case 242: // MTO VARIANT |
if (data_length[i] > 6) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
break; |
// Length 8 Max |
case 30: // VAR COUNT |
case 37: // COUNT |
if (data_length[i] > 8) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
break; |
// Length 10 Max |
case 7009: // FISHING GEAR TYPE |
case 8019: // SRIN |
if (data_length[i] > 10) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
break; |
// Length 12 Max |
case 7005: // CATCH AREA |
case 8011: // CPID SERIAL |
if (data_length[i] > 12) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
break; |
// Length 20 Max |
case 10: // BATCH/LOT |
case 21: // SERIAL |
case 22: // CPV |
case 243: // PCN |
case 254: // GLN EXTENSION COMPONENT |
case 420: // SHIP TO POST |
case 7020: // REFURB LOT |
case 7021: // FUNC STAT |
case 7022: // REV STAT |
case 710: // NHRN PZN |
case 711: // NHRN CIP |
case 712: // NHRN CN |
case 713: // NHRN DRN |
case 714: // NHRN AIM |
case 8002: // CMT NO |
case 8012: // VERSION |
if (data_length[i] > 20) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
break; |
// Length 25 Max |
case 8020: // REF NO |
if (data_length[i] > 25) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
break; |
// Length 30 Max |
case 240: // ADDITIONAL ID |
case 241: // CUST PART NO |
case 250: // SECONDARY SERIAL |
case 251: // REF TO SOURCE |
case 400: // ORDER NUMBER |
case 401: // GINC |
case 403: // ROUTE |
case 7002: // MEAT CUT |
case 7023: // GIAI ASSEMBLY |
case 8004: // GIAI |
case 8010: // CPID |
case 8013: // BUDI-DI |
case 90: // INTERNAL |
if (data_length[i] > 30) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
break; |
// Length 34 Max |
case 8007: // IBAN |
if (data_length[i] > 34) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
break; |
// Length 70 Max |
case 8110: // Coupon code |
case 8112: // Paperless coupon code |
case 8200: // PRODUCT URL |
if (data_length[i] > 70) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
break; |
} |
if (ai_value[i] == 253) { // GDTI |
if (data_length[i] < 14 || data_length[i] > 30) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
} |
if (ai_value[i] == 255) { // GCN |
if (data_length[i] < 14 || data_length[i] > 25) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
} |
if (ai_value[i] >= 3100 && ai_value[i] <= 3169) { |
if (data_length[i] != 6) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
} |
if (ai_value[i] >= 3200 && ai_value[i] <= 3379) { |
if (data_length[i] != 6) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
} |
if (ai_value[i] >= 3400 && ai_value[i] <= 3579) { |
if (data_length[i] != 6) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
} |
if (ai_value[i] >= 3600 && ai_value[i] <= 3699) { |
if (data_length[i] != 6) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
} |
if (ai_value[i] >= 3900 && ai_value[i] <= 3909) { // AMOUNT |
if (data_length[i] > 15) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
} |
if (ai_value[i] >= 3910 && ai_value[i] <= 3919) { // AMOUNT |
if (data_length[i] < 4 || data_length[i] > 18) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
} |
if (ai_value[i] >= 3920 && ai_value[i] <= 3929) { // PRICE |
if (data_length[i] > 15) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
} |
if (ai_value[i] >= 3930 && ai_value[i] <= 3939) { // PRICE |
if (data_length[i] < 4 || data_length[i] > 18) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
} |
if (ai_value[i] >= 3940 && ai_value[i] <= 3949) { // PRCNT OFF |
if (data_length[i] != 4) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
} |
if (ai_value[i] == 421) { // SHIP TO POST |
if (data_length[i] < 3 || data_length[i] > 12) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
} |
if (ai_value[i] == 423 || ai_value[i] == 425) { |
// COUNTRY INITIAL PROCESS || COUNTRY DISASSEMBLY |
if (data_length[i] < 3 || data_length[i] > 15) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
} |
if (ai_value[i] == 7007) { // HARVEST DATE |
if (data_length[i] < 6 || data_length[i] > 12) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
} |
if (ai_value[i] >= 7030 && ai_value[i] <= 7039) { // PROCESSOR # |
if (data_length[i] < 4 || data_length[i] > 30) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
} |
if (ai_value[i] == 8003) { // GRAI |
if (data_length[i] < 15 || data_length[i] > 30) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
} |
if (ai_value[i] == 8008) { // PROD TIME |
if (data_length[i] < 9 || data_length[i] > 12) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
} |
if (ai_value[i] >= 91 && ai_value[i] <= 99) { // INTERNAL |
if (data_length[i] > 90) { |
error_latch = 1; |
} else { |
error_latch = 0; |
} |
} |
if (error_latch == 1) { |
throw new OkapiException("Invalid data length for AI"); |
} |
if (error_latch == 2) { |
throw new OkapiException("Invalid AI value"); |
} |
} |
/* Resolve AI data - put resulting string in 'reduced' */ |
int last_ai = 0; |
boolean fixedLengthAI = true; |
for (int i = 0; i < source.length; i++) { |
if (source[i] != '[' && source[i] != ']') { |
reduced.append(source[i]); |
} |
if (source[i] == '[') { |
/* Start of an AI string */ |
if (!fixedLengthAI) { |
reduced.append(fnc1); |
} |
last_ai = 10 * Character.getNumericValue(source[i + 1]) + Character.getNumericValue(source[i + 2]); |
/* |
* The following values from |
* "GS-1 General Specification version 8.0 issue 2, May 2008" figure 5.4.8.2.1 - 1 |
* "Element Strings with Pre-Defined Length Using Application Identifiers" |
*/ |
fixedLengthAI = last_ai >= 0 && last_ai <= 4 || last_ai >= 11 && last_ai <= 20 || last_ai == 23 |
|| /* legacy support - see 5.3.8.2.2 */ |
last_ai >= 31 && last_ai <= 36 || last_ai == 41; |
} |
} |
return reduced.toString(); |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/util/Doubles.java |
---|
New file |
0,0 → 1,39 |
/* |
* 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.util; |
/** |
* Double utility class. |
* |
* @author Daniel Gredler |
*/ |
public final class Doubles { |
private Doubles() { |
// utility class |
} |
/** |
* It's usually not a good idea to check floating point numbers for exact equality. This method |
* allows us to check for approximate equality. |
* |
* @param d1 the first double |
* @param d2 the second double |
* @return whether or not the two doubles are approximately equal (to within 0.0001) |
*/ |
public static boolean roughlyEqual(final double d1, final double d2) { |
return Math.abs(d1 - d2) < 0.0001; |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/util/Arrays.java |
---|
New file |
0,0 → 1,112 |
/* |
* Copyright 2018 Daniel Gredler |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.util; |
import uk.org.okapibarcode.backend.OkapiException; |
/** |
* Array utility class. |
* |
* @author Daniel Gredler |
*/ |
public final class Arrays { |
private Arrays() { |
// utility class |
} |
/** |
* Returns the position of the specified value in the specified array. |
* |
* @param value the value to search for |
* @param array the array to search in |
* @return the position of the specified value in the specified array |
*/ |
public static int positionOf(final char value, final char[] array) { |
for (int i = 0; i < array.length; i++) { |
if (value == array[i]) { |
return i; |
} |
} |
throw new OkapiException("Unable to find character '" + value + "' in character array."); |
} |
/** |
* Returns the position of the specified value in the specified array. |
* |
* @param value the value to search for |
* @param array the array to search in |
* @return the position of the specified value in the specified array |
*/ |
public static int positionOf(final int value, final int[] array) { |
for (int i = 0; i < array.length; i++) { |
if (value == array[i]) { |
return i; |
} |
} |
throw new OkapiException("Unable to find integer '" + value + "' in integer array."); |
} |
/** |
* Returns <code>true</code> if the specified array contains the specified value. |
* |
* @param array the array to check in |
* @param value the value to check for |
* @return true if the specified array contains the specified value |
*/ |
public static boolean contains(final int[] array, final int value) { |
for (int i = 0; i < array.length; i++) { |
if (array[i] == value) { |
return true; |
} |
} |
return false; |
} |
/** |
* Returns <code>true</code> if the specified array contains the specified sub-array at the |
* specified index. |
* |
* @param array the array to search in |
* @param searchFor the sub-array to search for |
* @param index the index at which to search |
* @return whether or not the specified array contains the specified sub-array at the specified |
* index |
*/ |
public static boolean containsAt(final byte[] array, final byte[] searchFor, final int index) { |
for (int i = 0; i < searchFor.length; i++) { |
if (index + i >= array.length || array[index + i] != searchFor[i]) { |
return false; |
} |
} |
return true; |
} |
/** |
* Inserts the specified array into the specified original array at the specified index. |
* |
* @param original the original array into which we want to insert another array |
* @param index the index at which we want to insert the array |
* @param inserted the array that we want to insert |
* @return the combined array |
*/ |
public static int[] insertArray(final int[] original, final int index, final int[] inserted) { |
final int[] modified = new int[original.length + inserted.length]; |
System.arraycopy(original, 0, modified, 0, index); |
System.arraycopy(inserted, 0, modified, index, inserted.length); |
System.arraycopy(original, index, modified, index + inserted.length, modified.length - index - inserted.length); |
return modified; |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/output/ExtendedOutputStreamWriter.java |
---|
New file |
0,0 → 1,80 |
/* |
* 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.output; |
import java.io.IOException; |
import java.io.OutputStream; |
import java.io.OutputStreamWriter; |
import java.nio.charset.StandardCharsets; |
import java.util.Locale; |
/** |
* {@link OutputStreamWriter} extension which provides some convenience methods for writing numbers. |
*/ |
class ExtendedOutputStreamWriter extends OutputStreamWriter { |
/** Format to use when writing doubles to the stream. */ |
private final String doubleFormat; |
/** |
* Creates a new extended output stream writer, using the UTF-8 charset. |
* |
* @param out the stream to write to |
* @param doubleFormat the format to use when writing doubles to the stream |
*/ |
public ExtendedOutputStreamWriter(final OutputStream out, final String doubleFormat) { |
super(out, StandardCharsets.UTF_8); |
this.doubleFormat = doubleFormat; |
} |
/** {@inheritDoc} */ |
@Override |
public ExtendedOutputStreamWriter append(final CharSequence cs) throws IOException { |
super.append(cs); |
return this; |
} |
/** {@inheritDoc} */ |
@Override |
public ExtendedOutputStreamWriter append(final CharSequence cs, final int start, final int end) throws IOException { |
super.append(cs, start, end); |
return this; |
} |
/** |
* Writes the specified double to the stream, formatted according to the format specified in the |
* constructor. |
* |
* @param d the double to write to the stream |
* @return this writer |
* @throws IOException if an I/O error occurs |
*/ |
public ExtendedOutputStreamWriter append(final double d) throws IOException { |
super.append(String.format(Locale.ROOT, this.doubleFormat, d)); |
return this; |
} |
/** |
* Writes the specified integer to the stream. |
* |
* @param i the integer to write to the stream |
* @return this writer |
* @throws IOException if an I/O error occurs |
*/ |
public ExtendedOutputStreamWriter appendInt(final int i) throws IOException { |
super.append(String.valueOf(i)); |
return this; |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/output/SymbolRenderer.java |
---|
New file |
0,0 → 1,34 |
/* |
* 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.output; |
import java.io.IOException; |
import uk.org.okapibarcode.backend.Symbol; |
/** |
* Renders symbols to some output format. |
*/ |
public interface SymbolRenderer { |
/** |
* Renders the specified symbology. |
* |
* @param symbol the symbology to render |
* @throws IOException if there is an I/O error |
*/ |
void render(Symbol symbol) throws IOException; |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/output/Java2DRenderer.java |
---|
New file |
0,0 → 1,169 |
/* |
* Copyright 2014-2015 Robin Stuart, Robert Elliott, 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.output; |
import static uk.org.okapibarcode.backend.HumanReadableAlignment.CENTER; |
import static uk.org.okapibarcode.backend.HumanReadableAlignment.JUSTIFY; |
import java.awt.Color; |
import java.awt.Font; |
import java.awt.FontMetrics; |
import java.awt.Graphics2D; |
import java.awt.Polygon; |
import java.awt.font.FontRenderContext; |
import java.awt.font.TextAttribute; |
import java.awt.geom.Area; |
import java.awt.geom.Ellipse2D; |
import java.awt.geom.Rectangle2D; |
import java.util.Collections; |
import java.util.List; |
import uk.org.okapibarcode.backend.Hexagon; |
import uk.org.okapibarcode.backend.HumanReadableAlignment; |
import uk.org.okapibarcode.backend.Symbol; |
import uk.org.okapibarcode.backend.TextBox; |
/** |
* Renders symbologies using the Java 2D API. |
*/ |
public class Java2DRenderer implements SymbolRenderer { |
/** The graphics to render to. */ |
private final Graphics2D g2d; |
/** The magnification factor to apply. */ |
private final double magnification; |
/** The paper (background) color. */ |
private final Color paper; |
/** The ink (foreground) color. */ |
private final Color ink; |
/** |
* Creates a new Java 2D renderer. If the specified paper color is <tt>null</tt>, the symbol is |
* drawn without clearing the existing <tt>g2d</tt> background. |
* |
* @param g2d the graphics to render to |
* @param magnification the magnification factor to apply |
* @param paper the paper (background) color (may be <tt>null</tt>) |
* @param ink the ink (foreground) color |
*/ |
public Java2DRenderer(final Graphics2D g2d, final double magnification, final Color paper, final Color ink) { |
this.g2d = g2d; |
this.magnification = magnification; |
this.paper = paper; |
this.ink = ink; |
} |
/** {@inheritDoc} */ |
@Override |
public void render(final Symbol symbol) { |
final int marginX = (int) (symbol.getQuietZoneHorizontal() * this.magnification); |
final int marginY = (int) (symbol.getQuietZoneVertical() * this.magnification); |
Font f = symbol.getFont(); |
if (f != null) { |
f = f.deriveFont((float) (f.getSize2D() * this.magnification)); |
} else { |
f = new Font(symbol.getFontName(), Font.PLAIN, (int) (symbol.getFontSize() * this.magnification)); |
f = f.deriveFont(Collections.singletonMap(TextAttribute.TRACKING, 0)); |
} |
final Font oldFont = this.g2d.getFont(); |
final Color oldColor = this.g2d.getColor(); |
if (this.paper != null) { |
final int w = (int) (symbol.getWidth() * this.magnification); |
final int h = (int) (symbol.getHeight() * this.magnification); |
this.g2d.setColor(this.paper); |
this.g2d.fillRect(0, 0, w, h); |
} |
this.g2d.setColor(this.ink); |
for (final Rectangle2D.Double rect : symbol.getRectangles()) { |
final double x = rect.x * this.magnification + marginX; |
final double y = rect.y * this.magnification + marginY; |
final double w = rect.width * this.magnification; |
final double h = rect.height * this.magnification; |
this.g2d.fillRect((int) x, (int) y, (int) w, (int) h); |
} |
for (final TextBox text : symbol.getTexts()) { |
final HumanReadableAlignment alignment = text.alignment == JUSTIFY && text.text.length() == 1 ? CENTER : text.alignment; |
final Font font = alignment != JUSTIFY ? f : addTracking(f, text.width * this.magnification, text.text, this.g2d); |
this.g2d.setFont(font); |
final FontMetrics fm = this.g2d.getFontMetrics(); |
final Rectangle2D bounds = fm.getStringBounds(text.text, this.g2d); |
final float y = (float) (text.y * this.magnification) + marginY; |
float x; |
switch (alignment) { |
case LEFT: |
case JUSTIFY: |
x = (float) (this.magnification * text.x + marginX); |
break; |
case RIGHT: |
x = (float) (this.magnification * text.x + this.magnification * text.width - bounds.getWidth() + marginX); |
break; |
case CENTER: |
x = (float) (this.magnification * text.x + this.magnification * text.width / 2 - bounds.getWidth() / 2 + marginX); |
break; |
default: |
throw new IllegalStateException("Unknown alignment: " + alignment); |
} |
this.g2d.drawString(text.text, x, y); |
} |
for (final Hexagon hexagon : symbol.getHexagons()) { |
final Polygon polygon = new Polygon(); |
for (int j = 0; j < 6; j++) { |
polygon.addPoint((int) (hexagon.pointX[j] * this.magnification + marginX), (int) (hexagon.pointY[j] * this.magnification + marginY)); |
} |
this.g2d.fill(polygon); |
} |
final List<Ellipse2D.Double> target = symbol.getTarget(); |
for (int i = 0; i + 1 < target.size(); i += 2) { |
final Ellipse2D.Double outer = adjust(target.get(i), this.magnification, marginX, marginY); |
final Ellipse2D.Double inner = adjust(target.get(i + 1), this.magnification, marginX, marginY); |
final Area area = new Area(outer); |
area.subtract(new Area(inner)); |
this.g2d.fill(area); |
} |
this.g2d.setFont(oldFont); |
this.g2d.setColor(oldColor); |
} |
private static Ellipse2D.Double adjust(final Ellipse2D.Double ellipse, final double magnification, final int marginX, final int marginY) { |
final double x = ellipse.x * magnification + marginX; |
final double y = ellipse.y * magnification + marginY; |
final double w = ellipse.width * magnification + marginX; |
final double h = ellipse.height * magnification + marginY; |
return new Ellipse2D.Double(x, y, w, h); |
} |
private static Font addTracking(final Font baseFont, final double maxTextWidth, final String text, final Graphics2D g2d) { |
final FontRenderContext frc = g2d.getFontRenderContext(); |
final double originalWidth = baseFont.getStringBounds(text, frc).getWidth(); |
final double extraSpace = maxTextWidth - originalWidth; |
final double extraSpacePerGap = extraSpace / (text.length() - 1); |
final double scaleX = baseFont.isTransformed() ? baseFont.getTransform().getScaleX() : 1; |
final double tracking = extraSpacePerGap / (baseFont.getSize2D() * scaleX); |
return baseFont.deriveFont(Collections.singletonMap(TextAttribute.TRACKING, tracking)); |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/output/PostScriptRenderer.java |
---|
New file |
0,0 → 1,211 |
/* |
* Copyright 2015 Robin Stuart, Daniel Gredler |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.output; |
import static uk.org.okapibarcode.backend.HumanReadableAlignment.CENTER; |
import static uk.org.okapibarcode.backend.HumanReadableAlignment.JUSTIFY; |
import static uk.org.okapibarcode.util.Doubles.roughlyEqual; |
import java.awt.Color; |
import java.awt.geom.Ellipse2D; |
import java.awt.geom.Rectangle2D; |
import java.io.IOException; |
import java.io.OutputStream; |
import uk.org.okapibarcode.backend.Hexagon; |
import uk.org.okapibarcode.backend.HumanReadableAlignment; |
import uk.org.okapibarcode.backend.Symbol; |
import uk.org.okapibarcode.backend.TextBox; |
/** |
* Renders symbologies to EPS (Encapsulated PostScript). |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
* @author Daniel Gredler |
*/ |
public class PostScriptRenderer implements SymbolRenderer { |
/** The output stream to render to. */ |
private final OutputStream out; |
/** The magnification factor to apply. */ |
private final double magnification; |
/** The paper (background) color. */ |
private final Color paper; |
/** The ink (foreground) color. */ |
private final Color ink; |
/** |
* Creates a new PostScript renderer. |
* |
* @param out the output stream to render to |
* @param magnification the magnification factor to apply |
* @param paper the paper (background) color |
* @param ink the ink (foreground) color |
*/ |
public PostScriptRenderer(final OutputStream out, final double magnification, final Color paper, final Color ink) { |
this.out = out; |
this.magnification = magnification; |
this.paper = paper; |
this.ink = ink; |
} |
/** {@inheritDoc} */ |
@Override |
public void render(final Symbol symbol) throws IOException { |
// All y dimensions are reversed because EPS origin (0,0) is at the bottom left, not top |
// left |
final String content = symbol.getContent(); |
final int width = (int) (symbol.getWidth() * this.magnification); |
final int height = (int) (symbol.getHeight() * this.magnification); |
final int marginX = (int) (symbol.getQuietZoneHorizontal() * this.magnification); |
final int marginY = (int) (symbol.getQuietZoneVertical() * this.magnification); |
String title; |
if (content == null || content.isEmpty()) { |
title = "OkapiBarcode Generated Symbol"; |
} else { |
title = content; |
} |
try (ExtendedOutputStreamWriter writer = new ExtendedOutputStreamWriter(this.out, "%.2f")) { |
// Header |
writer.append("%!PS-Adobe-3.0 EPSF-3.0\n"); |
writer.append("%%Creator: OkapiBarcode\n"); |
writer.append("%%Title: ").append(title).append('\n'); |
writer.append("%%Pages: 0\n"); |
writer.append("%%BoundingBox: 0 0 ").appendInt(width).append(" ").appendInt(height).append("\n"); |
writer.append("%%EndComments\n"); |
// Definitions |
writer.append("/TL { setlinewidth moveto lineto stroke } bind def\n"); |
writer.append("/TC { moveto 0 360 arc 360 0 arcn fill } bind def\n"); |
writer.append("/TH { 0 setlinewidth moveto lineto lineto lineto lineto lineto closepath fill } bind def\n"); |
writer.append("/TB { 2 copy } bind def\n"); |
writer.append("/TR { newpath 4 1 roll exch moveto 1 index 0 rlineto 0 exch rlineto neg 0 rlineto closepath fill } bind def\n"); |
writer.append("/TE { pop pop } bind def\n"); |
// Background |
writer.append("newpath\n"); |
writer.append(this.ink.getRed() / 255.0).append(" ").append(this.ink.getGreen() / 255.0).append(" ").append(this.ink.getBlue() / 255.0).append(" setrgbcolor\n"); |
writer.append(this.paper.getRed() / 255.0).append(" ").append(this.paper.getGreen() / 255.0).append(" ").append(this.paper.getBlue() / 255.0).append(" setrgbcolor\n"); |
writer.append(height).append(" 0.00 TB 0.00 ").append(width).append(" TR\n"); |
// Rectangles |
for (int i = 0; i < symbol.getRectangles().size(); i++) { |
final Rectangle2D.Double rect = symbol.getRectangles().get(i); |
if (i == 0) { |
writer.append("TE\n"); |
writer.append(this.ink.getRed() / 255.0).append(" ").append(this.ink.getGreen() / 255.0).append(" ").append(this.ink.getBlue() / 255.0).append(" setrgbcolor\n"); |
writer.append(rect.height * this.magnification).append(" ").append(height - (rect.y + rect.height) * this.magnification - marginY).append(" TB ") |
.append(rect.x * this.magnification + marginX).append(" ").append(rect.width * this.magnification).append(" TR\n"); |
} else { |
final Rectangle2D.Double prev = symbol.getRectangles().get(i - 1); |
if (!roughlyEqual(rect.height, prev.height) || !roughlyEqual(rect.y, prev.y)) { |
writer.append("TE\n"); |
writer.append(this.ink.getRed() / 255.0).append(" ").append(this.ink.getGreen() / 255.0).append(" ").append(this.ink.getBlue() / 255.0).append(" setrgbcolor\n"); |
writer.append(rect.height * this.magnification).append(" ").append(height - (rect.y + rect.height) * this.magnification - marginY).append(" "); |
} |
writer.append("TB ").append(rect.x * this.magnification + marginX).append(" ").append(rect.width * this.magnification).append(" TR\n"); |
} |
} |
// Text |
for (int i = 0; i < symbol.getTexts().size(); i++) { |
final TextBox text = symbol.getTexts().get(i); |
final HumanReadableAlignment alignment = text.alignment == JUSTIFY && text.text.length() == 1 ? CENTER : text.alignment; |
if (i == 0) { |
writer.append("TE\n"); |
; |
writer.append(this.ink.getRed() / 255.0).append(" ").append(this.ink.getGreen() / 255.0).append(" ").append(this.ink.getBlue() / 255.0).append(" setrgbcolor\n"); |
} |
writer.append("matrix currentmatrix\n"); |
writer.append("/").append(symbol.getFontName()).append(" findfont\n"); |
writer.append(symbol.getFontSize() * this.magnification).append(" scalefont setfont\n"); |
final double y = height - text.y * this.magnification - marginY; |
switch (alignment) { |
case LEFT: |
final double leftX = this.magnification * text.x + marginX; |
writer.append(" 0 0 moveto ").append(leftX).append(" ").append(y).append(" translate 0.00 rotate 0 0 moveto\n"); |
writer.append(" (").append(text.text).append(") show\n"); |
break; |
case JUSTIFY: |
final double textX = this.magnification * text.x + marginX; |
final double textW = this.magnification * text.width; |
writer.append(" 0 0 moveto ").append(textX).append(" ").append(y).append(" translate 0.00 rotate 0 0 moveto\n"); |
writer.append(" (").append(text.text).append(") dup stringwidth pop ").append(textW).append(" sub neg 1 index length 1 sub div 0").append(" 3 -1 roll ashow\n"); |
break; |
case RIGHT: |
final double rightX = this.magnification * text.x + this.magnification * text.width + marginX; |
writer.append(" 0 0 moveto ").append(rightX).append(" ").append(y).append(" translate 0.00 rotate 0 0 moveto\n"); |
writer.append(" (").append(text.text).append(") stringwidth\n"); |
writer.append("pop\n"); |
writer.append("-1 mul 0 rmoveto\n"); |
writer.append(" (").append(text.text).append(") show\n"); |
break; |
case CENTER: |
final double centerX = this.magnification * text.x + this.magnification * text.width / 2 + marginX; |
writer.append(" 0 0 moveto ").append(centerX).append(" ").append(y).append(" translate 0.00 rotate 0 0 moveto\n"); |
writer.append(" (").append(text.text).append(") stringwidth\n"); |
writer.append("pop\n"); |
writer.append("-2 div 0 rmoveto\n"); |
writer.append(" (").append(text.text).append(") show\n"); |
break; |
default: |
throw new IllegalStateException("Unknown alignment: " + alignment); |
} |
writer.append("setmatrix\n"); |
} |
// Circles |
// Because MaxiCode size is fixed, this ignores magnification |
for (int i = 0; i < symbol.getTarget().size(); i += 2) { |
final Ellipse2D.Double ellipse1 = symbol.getTarget().get(i); |
final Ellipse2D.Double ellipse2 = symbol.getTarget().get(i + 1); |
if (i == 0) { |
writer.append("TE\n"); |
writer.append(this.ink.getRed() / 255.0).append(" ").append(this.ink.getGreen() / 255.0).append(" ").append(this.ink.getBlue() / 255.0).append(" setrgbcolor\n"); |
writer.append(this.ink.getRed() / 255.0).append(" ").append(this.ink.getGreen() / 255.0).append(" ").append(this.ink.getBlue() / 255.0).append(" setrgbcolor\n"); |
} |
final double x1 = ellipse1.x + ellipse1.width / 2; |
final double x2 = ellipse2.x + ellipse2.width / 2; |
final double y1 = height - ellipse1.y - ellipse1.width / 2; |
final double y2 = height - ellipse2.y - ellipse2.width / 2; |
final double r1 = ellipse1.width / 2; |
final double r2 = ellipse2.width / 2; |
writer.append(x1 + marginX).append(" ").append(y1 - marginY).append(" ").append(r1).append(" ").append(x2 + marginX).append(" ").append(y2 - marginY).append(" ").append(r2).append(" ") |
.append(x2 + r2 + marginX).append(" ").append(y2 - marginY).append(" TC\n"); |
} |
// Hexagons |
// Because MaxiCode size is fixed, this ignores magnification |
for (int i = 0; i < symbol.getHexagons().size(); i++) { |
final Hexagon hexagon = symbol.getHexagons().get(i); |
for (int j = 0; j < 6; j++) { |
writer.append(hexagon.pointX[j] + marginX).append(" ").append(height - hexagon.pointY[j] - marginY).append(" "); |
} |
writer.append(" TH\n"); |
} |
// Footer |
writer.append("\nshowpage\n"); |
} |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/output/SvgRenderer.java |
---|
New file |
0,0 → 1,225 |
/* |
* Copyright 2014-2015 Robin Stuart, Daniel Gredler |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.output; |
import static uk.org.okapibarcode.backend.HumanReadableAlignment.CENTER; |
import static uk.org.okapibarcode.backend.HumanReadableAlignment.JUSTIFY; |
import java.awt.Color; |
import java.awt.geom.Ellipse2D; |
import java.awt.geom.Rectangle2D; |
import java.io.IOException; |
import java.io.OutputStream; |
import java.io.StringWriter; |
import javax.xml.parsers.DocumentBuilderFactory; |
import javax.xml.parsers.ParserConfigurationException; |
import javax.xml.transform.OutputKeys; |
import javax.xml.transform.Transformer; |
import javax.xml.transform.TransformerException; |
import javax.xml.transform.TransformerFactory; |
import javax.xml.transform.TransformerFactoryConfigurationError; |
import javax.xml.transform.dom.DOMSource; |
import javax.xml.transform.stream.StreamResult; |
import org.w3c.dom.Document; |
import org.w3c.dom.Text; |
import uk.org.okapibarcode.backend.Hexagon; |
import uk.org.okapibarcode.backend.HumanReadableAlignment; |
import uk.org.okapibarcode.backend.Symbol; |
import uk.org.okapibarcode.backend.TextBox; |
/** |
* Renders symbologies to SVG (Scalable Vector Graphics). |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
* @author Daniel Gredler |
*/ |
public class SvgRenderer implements SymbolRenderer { |
/** The output stream to render to. */ |
private final OutputStream out; |
/** The magnification factor to apply. */ |
private final double magnification; |
/** The paper (background) color. */ |
private final Color paper; |
/** The ink (foreground) color. */ |
private final Color ink; |
/** Whether or not to include the XML prolog in the output. */ |
private final boolean xmlProlog; |
/** |
* Creates a new SVG renderer. |
* |
* @param out the output stream to render to |
* @param magnification the magnification factor to apply |
* @param paper the paper (background) color |
* @param ink the ink (foreground) color |
* @param xmlProlog whether or not to include the XML prolog in the output (usually {@code true} |
* for standalone SVG documents, {@code false} for SVG content embedded directly in HTML |
* documents) |
*/ |
public SvgRenderer(final OutputStream out, final double magnification, final Color paper, final Color ink, final boolean xmlProlog) { |
this.out = out; |
this.magnification = magnification; |
this.paper = paper; |
this.ink = ink; |
this.xmlProlog = xmlProlog; |
} |
/** {@inheritDoc} */ |
@Override |
public void render(final Symbol symbol) throws IOException { |
final String content = symbol.getContent(); |
final int width = (int) (symbol.getWidth() * this.magnification); |
final int height = (int) (symbol.getHeight() * this.magnification); |
final int marginX = (int) (symbol.getQuietZoneHorizontal() * this.magnification); |
final int marginY = (int) (symbol.getQuietZoneVertical() * this.magnification); |
String title; |
if (content == null || content.isEmpty()) { |
title = "OkapiBarcode Generated Symbol"; |
} else { |
title = content; |
} |
final String fgColour = String.format("%02X", this.ink.getRed()) + String.format("%02X", this.ink.getGreen()) + String.format("%02X", this.ink.getBlue()); |
final String bgColour = String.format("%02X", this.paper.getRed()) + String.format("%02X", this.paper.getGreen()) + String.format("%02X", this.paper.getBlue()); |
try (ExtendedOutputStreamWriter writer = new ExtendedOutputStreamWriter(this.out, "%.2f")) { |
// XML Prolog |
if (this.xmlProlog) { |
writer.append("<?xml version=\"1.0\" standalone=\"no\"?>\n"); |
writer.append("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n"); |
writer.append(" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"); |
} |
// Header |
writer.append("<svg width=\"").appendInt(width).append("\" height=\"").appendInt(height).append("\" version=\"1.1").append("\" xmlns=\"http://www.w3.org/2000/svg\">\n"); |
writer.append(" <desc>").append(clean(title)).append("</desc>\n"); |
writer.append(" <g id=\"barcode\" fill=\"#").append(fgColour).append("\">\n"); |
writer.append(" <rect x=\"0\" y=\"0\" width=\"").appendInt(width).append("\" height=\"").appendInt(height).append("\" fill=\"#").append(bgColour).append("\" />\n"); |
// Rectangles |
for (int i = 0; i < symbol.getRectangles().size(); i++) { |
final Rectangle2D.Double rect = symbol.getRectangles().get(i); |
writer.append(" <rect x=\"").append(rect.x * this.magnification + marginX).append("\" y=\"").append(rect.y * this.magnification + marginY).append("\" width=\"") |
.append(rect.width * this.magnification).append("\" height=\"").append(rect.height * this.magnification).append("\" />\n"); |
} |
// Text |
for (int i = 0; i < symbol.getTexts().size(); i++) { |
final TextBox text = symbol.getTexts().get(i); |
final HumanReadableAlignment alignment = text.alignment == JUSTIFY && text.text.length() == 1 ? CENTER : text.alignment; |
double x; |
String anchor; |
switch (alignment) { |
case LEFT: |
case JUSTIFY: |
x = this.magnification * text.x + marginX; |
anchor = "start"; |
break; |
case RIGHT: |
x = this.magnification * text.x + this.magnification * text.width + marginX; |
anchor = "end"; |
break; |
case CENTER: |
x = this.magnification * text.x + this.magnification * text.width / 2 + marginX; |
anchor = "middle"; |
break; |
default: |
throw new IllegalStateException("Unknown alignment: " + alignment); |
} |
writer.append(" <text x=\"").append(x).append("\" y=\"").append(text.y * this.magnification + marginY).append("\" text-anchor=\"").append(anchor).append("\"\n"); |
if (alignment == JUSTIFY) { |
writer.append(" textLength=\"").append(text.width * this.magnification).append("\" lengthAdjust=\"spacing\"\n"); |
} |
writer.append(" font-family=\"").append(clean(symbol.getFontName())).append("\" font-size=\"").append(symbol.getFontSize() * this.magnification).append("\" fill=\"#") |
.append(fgColour).append("\">\n"); |
writer.append(" ").append(clean(text.text)).append("\n"); |
writer.append(" </text>\n"); |
} |
// Circles |
for (int i = 0; i < symbol.getTarget().size(); i++) { |
final Ellipse2D.Double ellipse = symbol.getTarget().get(i); |
String color; |
if ((i & 1) == 0) { |
color = fgColour; |
} else { |
color = bgColour; |
} |
writer.append(" <circle cx=\"").append((ellipse.x + ellipse.width / 2) * this.magnification + marginX).append("\" cy=\"") |
.append((ellipse.y + ellipse.width / 2) * this.magnification + marginY).append("\" r=\"").append(ellipse.width / 2 * this.magnification).append("\" fill=\"#").append(color) |
.append("\" />\n"); |
} |
// Hexagons |
for (int i = 0; i < symbol.getHexagons().size(); i++) { |
final Hexagon hexagon = symbol.getHexagons().get(i); |
writer.append(" <path d=\""); |
for (int j = 0; j < 6; j++) { |
if (j == 0) { |
writer.append("M "); |
} else { |
writer.append("L "); |
} |
writer.append(hexagon.pointX[j] * this.magnification + marginX).append(" ").append(hexagon.pointY[j] * this.magnification + marginY).append(" "); |
} |
writer.append("Z\" />\n"); |
} |
// Footer |
writer.append(" </g>\n"); |
writer.append("</svg>\n"); |
} |
} |
/** |
* Cleans / sanitizes the specified string for inclusion in XML. A bit convoluted, but we're |
* trying to do it without adding an external dependency just for this... |
* |
* @param s the string to be cleaned / sanitized |
* @return the cleaned / sanitized string |
*/ |
protected String clean(String s) { |
// remove control characters |
s = s.replaceAll("[\u0000-\u001f]", ""); |
// escape XML characters |
try { |
final Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); |
final Text text = document.createTextNode(s); |
final Transformer transformer = TransformerFactory.newInstance().newTransformer(); |
final DOMSource source = new DOMSource(text); |
final StringWriter writer = new StringWriter(); |
final StreamResult result = new StreamResult(writer); |
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); |
transformer.transform(source, result); |
return writer.toString(); |
} catch (ParserConfigurationException | TransformerException | TransformerFactoryConfigurationError e) { |
return s; |
} |
} |
} |
/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/EanUpcAddOn.java |
---|
New file |
0,0 → 1,112 |
/* |
* Copyright 2014 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
/** |
* <p> |
* Implements EAN/UPC add-on bar code symbology according to BS EN 797:1996. |
* |
* @see Ean |
* @see Upc |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class EanUpcAddOn extends Symbol { |
private static final String[] EAN_SET_A = { "3211", "2221", "2122", "1411", "1132", "1231", "1114", "1312", "1213", "3112" }; |
private static final String[] EAN_SET_B = { "1123", "1222", "2212", "1141", "2311", "1321", "4111", "2131", "3121", "2113" }; |
private static final String[] EAN2_PARITY = { "AA", "AB", "BA", "BB" }; |
private static final String[] EAN5_PARITY = { "BBAAA", "BABAA", "BAABA", "BAAAB", "ABBAA", "AABBA", "AAABB", "ABABA", "ABAAB", "AABAB" }; |
@Override |
protected void encode() { |
if (!this.content.matches("[0-9]+")) { |
throw new OkapiException("Invalid characters in input"); |
} |
if (this.content.length() > 5) { |
throw new OkapiException("Input data too long"); |
} |
final int targetLength = this.content.length() > 2 ? 5 : 2; |
if (this.content.length() < targetLength) { |
for (int i = this.content.length(); i < targetLength; i++) { |
this.content = '0' + this.content; |
} |
} |
final String bars = targetLength == 2 ? ean2(this.content) : ean5(this.content); |
this.readable = this.content; |
this.pattern = new String[] { bars }; |
this.row_count = 1; |
this.row_height = new int[] { -1 }; |
} |
private static String ean2(final String content) { |
final int sum = (content.charAt(0) - '0') * 10 + content.charAt(1) - '0'; |
final String parity = EAN2_PARITY[sum % 4]; |
final StringBuilder sb = new StringBuilder(); |
sb.append("112"); /* Start */ |
for (int i = 0; i < 2; i++) { |
final int val = content.charAt(i) - '0'; |
if (parity.charAt(i) == 'B') { |
sb.append(EAN_SET_B[val]); |
} else { |
sb.append(EAN_SET_A[val]); |
} |
if (i != 1) { /* Glyph separator */ |
sb.append("11"); |
} |
} |
return sb.toString(); |
} |
private static String ean5(final String content) { |
int sum = 0; |
for (int i = 0; i < 5; i++) { |
if (i % 2 == 0) { |
sum += 3 * (content.charAt(i) - '0'); |
} else { |
sum += 9 * (content.charAt(i) - '0'); |
} |
} |
final String parity = EAN5_PARITY[sum % 10]; |
final StringBuilder sb = new StringBuilder(); |
sb.append("112"); /* Start */ |
for (int i = 0; i < 5; i++) { |
final int val = content.charAt(i) - '0'; |
if (parity.charAt(i) == 'B') { |
sb.append(EAN_SET_B[val]); |
} else { |
sb.append(EAN_SET_A[val]); |
} |
if (i != 4) { /* Glyph separator */ |
sb.append("11"); |
} |
} |
return sb.toString(); |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/HumanReadableLocation.java |
---|
New file |
0,0 → 1,30 |
/* |
* Copyright 2015 Daniel Gredler |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
/** |
* The location of a bar code's human-readable text. |
*/ |
public enum HumanReadableLocation { |
/** Display the human-readable text below the bar code. */ |
BOTTOM, |
/** Display the human-readable text above the bar code. */ |
TOP, |
/** Do not display the human-readable text. */ |
NONE |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/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/ReedSolomon.java |
---|
New file |
0,0 → 1,103 |
/* |
* Copyright 2014 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
/** |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class ReedSolomon { |
private int logmod; |
private int rlen; |
private int[] logt; |
private int[] alog; |
private int[] rspoly; |
public int[] res; |
public int getResult(final int count) { |
return this.res[count]; |
} |
public void init_gf(final int poly) { |
int m, b, p, v; |
// Find the top bit, and hence the symbol size |
for (b = 1, m = 0; b <= poly; b <<= 1) { |
m++; |
} |
b >>= 1; |
m--; |
// Calculate the log/alog tables |
this.logmod = (1 << m) - 1; |
this.logt = new int[this.logmod + 1]; |
this.alog = new int[this.logmod]; |
for (p = 1, v = 0; v < this.logmod; v++) { |
this.alog[v] = p; |
this.logt[p] = v; |
p <<= 1; |
if ((p & b) != 0) { |
p ^= poly; |
} |
} |
} |
public void init_code(final int nsym, int index) { |
int i, k; |
this.rspoly = new int[nsym + 1]; |
this.rlen = nsym; |
this.rspoly[0] = 1; |
for (i = 1; i <= nsym; i++) { |
this.rspoly[i] = 1; |
for (k = i - 1; k > 0; k--) { |
if (this.rspoly[k] != 0) { |
this.rspoly[k] = this.alog[(this.logt[this.rspoly[k]] + index) % this.logmod]; |
} |
this.rspoly[k] ^= this.rspoly[k - 1]; |
} |
this.rspoly[0] = this.alog[(this.logt[this.rspoly[0]] + index) % this.logmod]; |
index++; |
} |
} |
public void encode(final int len, final int[] data) { |
int i, k, m; |
this.res = new int[this.rlen]; |
for (i = 0; i < this.rlen; i++) { |
this.res[i] = 0; |
} |
for (i = 0; i < len; i++) { |
m = this.res[this.rlen - 1] ^ data[i]; |
for (k = this.rlen - 1; k > 0; k--) { |
if (m != 0 && this.rspoly[k] != 0) { |
this.res[k] = this.res[k - 1] ^ this.alog[(this.logt[m] + this.logt[this.rspoly[k]]) % this.logmod]; |
} else { |
this.res[k] = this.res[k - 1]; |
} |
} |
if (m != 0 && this.rspoly[0] != 0) { |
this.res[0] = this.alog[(this.logt[m] + this.logt[this.rspoly[0]]) % this.logmod]; |
} else { |
this.res[0] = 0; |
} |
} |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/KixCode.java |
---|
New file |
0,0 → 1,107 |
/* |
* Copyright 2014 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
import static uk.org.okapibarcode.util.Arrays.positionOf; |
import java.awt.geom.Rectangle2D; |
import java.util.Locale; |
/** |
* <p> |
* Implements Dutch Post KIX Code as used by Royal Dutch TPG Post (Netherlands). |
* |
* <p> |
* The input data can consist of digits 0-9 and characters A-Z, and should be 11 characters in |
* length. No check digit is added. |
* |
* <p> |
* KIX Code is the same as RM4SCC, but without the check digit. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
* @see <a href="http://www.tntpost.nl/zakelijk/klantenservice/downloads/kIX_code/download.aspx">KIX |
* Code Specification</a> |
*/ |
public class KixCode extends Symbol { |
private static final String[] ROYAL_TABLE = { "TTFF", "TDAF", "TDFA", "DTAF", "DTFA", "DDAA", "TADF", "TFTF", "TFDA", "DATF", "DADA", "DFTA", "TAFD", "TFAD", "TFFT", "DAAD", "DAFT", "DFAT", |
"ATDF", "ADTF", "ADDA", "FTTF", "FTDA", "FDTA", "ATFD", "ADAD", "ADFT", "FTAD", "FTFT", "FDAT", "AADD", "AFTD", "AFDT", "FATD", "FADT", "FFTT" }; |
private static final char[] KR_SET = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', |
'V', 'W', 'X', 'Y', 'Z' }; |
@Override |
protected void encode() { |
this.content = this.content.toUpperCase(Locale.ENGLISH); |
if (!this.content.matches("[0-9A-Z]+")) { |
throw new OkapiException("Invalid characters in data"); |
} |
final StringBuilder sb = new StringBuilder(this.content.length()); |
for (int i = 0; i < this.content.length(); i++) { |
final int j = positionOf(this.content.charAt(i), KR_SET); |
sb.append(ROYAL_TABLE[j]); |
} |
final String dest = sb.toString(); |
infoLine("Encoding: " + dest); |
this.readable = ""; |
this.pattern = new String[] { dest }; |
this.row_count = 1; |
this.row_height = new int[] { -1 }; |
} |
@Override |
protected void plotSymbol() { |
int xBlock; |
int x, y, w, h; |
this.rectangles.clear(); |
x = 0; |
w = 1; |
y = 0; |
h = 0; |
for (xBlock = 0; xBlock < this.pattern[0].length(); xBlock++) { |
final char c = this.pattern[0].charAt(xBlock); |
switch (c) { |
case 'A': |
y = 0; |
h = 5; |
break; |
case 'D': |
y = 3; |
h = 5; |
break; |
case 'F': |
y = 0; |
h = 8; |
break; |
case 'T': |
y = 3; |
h = 2; |
break; |
default: |
throw new IllegalStateException("Unknown pattern character: " + c); |
} |
this.rectangles.add(new Rectangle2D.Double(x, y, w, h)); |
x += 2; |
} |
this.symbol_width = (this.pattern[0].length() - 1) * 2 + 1; // final bar doesn't need extra |
// whitespace |
this.symbol_height = 8; |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/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/Codabar.java |
---|
New file |
0,0 → 1,95 |
/* |
* Copyright 2014 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
import static uk.org.okapibarcode.util.Arrays.positionOf; |
/** |
* <p> |
* Implements Codabar barcode symbology according to BS EN 798:1996. |
* |
* <p> |
* Also known as NW-7, Monarch, ABC Codabar, USD-4, Ames Code and Code 27. Codabar can encode any |
* length string starting and ending with the letters A-D and containing between these letters the |
* numbers 0-9, dash (-), dollar ($), colon (:), slash (/), full stop (.) or plus (+). No check |
* digit is generated. |
* |
* @author <a href="mailto:jakel2006@me.com">Robert Elliott</a> |
*/ |
public class Codabar extends Symbol { |
private static final String[] CODABAR_TABLE = { "11111221", "11112211", "11121121", "22111111", "11211211", "21111211", "12111121", "12112111", "12211111", "21121111", "11122111", "11221111", |
"21112121", "21211121", "21212111", "11212121", "11221211", "12121121", "11121221", "11122211" }; |
private static final char[] CHARACTER_SET = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '$', ':', '/', '.', '+', 'A', 'B', 'C', 'D' }; |
/** Ratio of wide bar width to narrow bar width. */ |
private double moduleWidthRatio = 2; |
/** |
* Sets the ratio of wide bar width to narrow bar width. Valid values are usually between |
* {@code 2} and {@code 3}. The default value is {@code 2}. |
* |
* @param moduleWidthRatio the ratio of wide bar width to narrow bar width |
*/ |
public void setModuleWidthRatio(final double moduleWidthRatio) { |
this.moduleWidthRatio = moduleWidthRatio; |
} |
/** |
* Returns the ratio of wide bar width to narrow bar width. |
* |
* @return the ratio of wide bar width to narrow bar width |
*/ |
public double getModuleWidthRatio() { |
return this.moduleWidthRatio; |
} |
/** {@inheritDoc} */ |
@Override |
protected void encode() { |
if (!this.content.matches("[A-D]{1}[0-9:/\\$\\.\\+\u002D]+[A-D]{1}")) { |
throw new OkapiException("Invalid characters in input"); |
} |
String horizontalSpacing = ""; |
final int l = this.content.length(); |
for (int i = 0; i < l; i++) { |
horizontalSpacing += CODABAR_TABLE[positionOf(this.content.charAt(i), CHARACTER_SET)]; |
} |
this.readable = this.content; |
this.pattern = new String[] { horizontalSpacing }; |
this.row_count = 1; |
this.row_height = new int[] { -1 }; |
} |
/** {@inheritDoc} */ |
@Override |
protected double getModuleWidth(final int originalWidth) { |
if (originalWidth == 1) { |
return 1; |
} else { |
return this.moduleWidthRatio; |
} |
} |
/** {@inheritDoc} */ |
@Override |
protected int[] getCodewords() { |
return getPatternAsCodewords(8); |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/AustraliaPost.java |
---|
New file |
0,0 → 1,481 |
/* |
* Copyright 2014 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
import static uk.org.okapibarcode.util.Arrays.positionOf; |
import java.awt.geom.Rectangle2D; |
/** |
* Implements the <a href= |
* "http://auspost.com.au/media/documents/a-guide-to-printing-the-4state-barcode-v31-mar2012.pdf">Australia |
* Post 4-State barcode</a>. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class AustraliaPost extends Symbol { |
private static final char[] CHARACTER_SET = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', |
'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', ' ', '#' }; |
private static final String[] N_ENCODING_TABLE = { "00", "01", "02", "10", "11", "12", "20", "21", "22", "30" }; |
private static final String[] C_ENCODING_TABLE = { "222", "300", "301", "302", "310", "311", "312", "320", "321", "322", "000", "001", "002", "010", "011", "012", "020", "021", "022", "100", |
"101", "102", "110", "111", "112", "120", "121", "122", "200", "201", "202", "210", "211", "212", "220", "221", "023", "030", "031", "032", "033", "103", "113", "123", "130", "131", "132", |
"133", "203", "213", "223", "230", "231", "232", "233", "303", "313", "323", "330", "331", "332", "333", "003", "013" }; |
private static final String[] BAR_VALUE_TABLE = { "000", "001", "002", "003", "010", "011", "012", "013", "020", "021", "022", "023", "030", "031", "032", "033", "100", "101", "102", "103", "110", |
"111", "112", "113", "120", "121", "122", "123", "130", "131", "132", "133", "200", "201", "202", "203", "210", "211", "212", "213", "220", "221", "222", "223", "230", "231", "232", "233", |
"300", "301", "302", "303", "310", "311", "312", "313", "320", "321", "322", "323", "330", "331", "332", "333" }; |
private enum ausMode { |
AUSPOST, AUSREPLY, AUSROUTE, AUSREDIRECT |
} |
private ausMode mode; |
public AustraliaPost() { |
this.mode = ausMode.AUSPOST; |
} |
/** |
* Specify encoding of Australia Post Standard Customer Barcode, Customer Barcode 2 or Customer |
* Barcode 3 (37-bar, 52-bar and 67-bar symbols) depending on input data length. Valid data |
* characters are 0-9, A-Z, a-z, space and hash (#). A Format Control Code (FCC) is added and |
* should not be included in the input data. |
* <p> |
* Input data should include a 8-digit Deliver Point ID (DPID) optionally followed by customer |
* information as shown below. |
* <table summary="Permitted Australia Post input data"> |
* <tbody> |
* <tr> |
* <th> |
* <p> |
* Input Length |
* </p> |
* </th> |
* <th> |
* <p> |
* Required Input Format |
* </p> |
* </th> |
* <th> |
* <p> |
* Symbol Length |
* </p> |
* </th> |
* <th> |
* <p> |
* FCC |
* </p> |
* </th> |
* <th> |
* <p> |
* Encoding Table |
* </p> |
* </th> |
* </tr> |
* <tr> |
* <td> |
* <p> |
* 8 |
* </p> |
* </td> |
* <td> |
* <p> |
* 99999999 |
* </p> |
* </td> |
* <td> |
* <p> |
* 37-bar |
* </p> |
* </td> |
* <td> |
* <p> |
* 11 |
* </p> |
* </td> |
* <td> |
* <p> |
* None |
* </p> |
* </td> |
* </tr> |
* <tr> |
* <td> |
* <p> |
* 13 |
* </p> |
* </td> |
* <td> |
* <p> |
* 99999999AAAAA |
* </p> |
* </td> |
* <td> |
* <p> |
* 52-bar |
* </p> |
* </td> |
* <td> |
* <p> |
* 59 |
* </p> |
* </td> |
* <td> |
* <p> |
* C |
* </p> |
* </td> |
* </tr> |
* <tr> |
* <td> |
* <p> |
* 16 |
* </p> |
* </td> |
* <td> |
* <p> |
* 9999999999999999 |
* </p> |
* </td> |
* <td> |
* <p> |
* 52-bar |
* </p> |
* </td> |
* <td> |
* <p> |
* 59 |
* </p> |
* </td> |
* <td> |
* <p> |
* N |
* </p> |
* </td> |
* </tr> |
* <tr> |
* <td> |
* <p> |
* 18 |
* </p> |
* </td> |
* <td> |
* <p> |
* 99999999AAAAAAAAAA |
* </p> |
* </td> |
* <td> |
* <p> |
* 67-bar |
* </p> |
* </td> |
* <td> |
* <p> |
* 62 |
* </p> |
* </td> |
* <td> |
* <p> |
* C |
* </p> |
* </td> |
* </tr> |
* <tr> |
* <td> |
* <p> |
* 23 |
* </p> |
* </td> |
* <td> |
* <p> |
* 99999999999999999999999 |
* </p> |
* </td> |
* <td> |
* <p> |
* 67-bar |
* </p> |
* </td> |
* <td> |
* <p> |
* 62 |
* </p> |
* </td> |
* <td> |
* <p> |
* N |
* </p> |
* </td> |
* </tr> |
* </tbody> |
* </table> |
*/ |
public void setPostMode() { |
this.mode = ausMode.AUSPOST; |
} |
/** |
* Specify encoding of a Reply Paid version of the Australia Post 4-State Barcode (FCC 45) which |
* requires an 8-digit DPID input. |
*/ |
public void setReplyMode() { |
this.mode = ausMode.AUSREPLY; |
} |
/** |
* Specify encoding of a Routing version of the Australia Post 4-State Barcode (FCC 87) which |
* requires an 8-digit DPID input. |
*/ |
public void setRouteMode() { |
this.mode = ausMode.AUSROUTE; |
} |
/** |
* Specify encoding of a Redirection version of the Australia Post 4-State Barcode (FCC 92) |
* which requires an 8-digit DPID input. |
*/ |
public void setRedirectMode() { |
this.mode = ausMode.AUSREDIRECT; |
} |
/** {@inheritDoc} */ |
@Override |
protected void encode() { |
String formatControlCode = "00"; |
String deliveryPointId; |
String barStateValues; |
String zeroPaddedInput = ""; |
int i; |
switch (this.mode) { |
case AUSPOST: |
switch (this.content.length()) { |
case 8: |
formatControlCode = "11"; |
break; |
case 13: |
formatControlCode = "59"; |
break; |
case 16: |
formatControlCode = "59"; |
if (!this.content.matches("[0-9]+")) { |
throw new OkapiException("Invalid characters in data"); |
} |
break; |
case 18: |
formatControlCode = "62"; |
break; |
case 23: |
formatControlCode = "62"; |
if (!this.content.matches("[0-9]+")) { |
throw new OkapiException("Invalid characters in data"); |
} |
break; |
default: |
throw new OkapiException("Auspost input is wrong length"); |
} |
break; |
case AUSREPLY: |
if (this.content.length() > 8) { |
throw new OkapiException("Auspost input is too long"); |
} else { |
formatControlCode = "45"; |
} |
break; |
case AUSROUTE: |
if (this.content.length() > 8) { |
throw new OkapiException("Auspost input is too long"); |
} else { |
formatControlCode = "87"; |
} |
break; |
case AUSREDIRECT: |
if (this.content.length() > 8) { |
throw new OkapiException("Auspost input is too long"); |
} else { |
formatControlCode = "92"; |
} |
break; |
} |
infoLine("FCC: " + formatControlCode); |
if (this.mode != ausMode.AUSPOST) { |
for (i = this.content.length(); i < 8; i++) { |
zeroPaddedInput += "0"; |
} |
} |
zeroPaddedInput += this.content; |
if (!this.content.matches("[0-9A-Za-z #]+")) { |
throw new OkapiException("Invalid characters in data"); |
} |
/* Verify that the first 8 characters are numbers */ |
deliveryPointId = zeroPaddedInput.substring(0, 8); |
if (!deliveryPointId.matches("[0-9]+")) { |
throw new OkapiException("Invalid characters in DPID"); |
} |
infoLine("DPID: " + deliveryPointId); |
/* Start */ |
barStateValues = "13"; |
/* Encode the FCC */ |
for (i = 0; i < 2; i++) { |
barStateValues += N_ENCODING_TABLE[formatControlCode.charAt(i) - '0']; |
} |
/* Delivery Point Identifier (DPID) */ |
for (i = 0; i < 8; i++) { |
barStateValues += N_ENCODING_TABLE[deliveryPointId.charAt(i) - '0']; |
} |
/* Customer Information */ |
switch (zeroPaddedInput.length()) { |
case 13: |
case 18: |
for (i = 8; i < zeroPaddedInput.length(); i++) { |
barStateValues += C_ENCODING_TABLE[positionOf(zeroPaddedInput.charAt(i), CHARACTER_SET)]; |
} |
break; |
case 16: |
case 23: |
for (i = 8; i < zeroPaddedInput.length(); i++) { |
barStateValues += N_ENCODING_TABLE[positionOf(zeroPaddedInput.charAt(i), CHARACTER_SET)]; |
} |
break; |
} |
/* Filler bar */ |
switch (barStateValues.length()) { |
case 22: |
case 37: |
case 52: |
barStateValues += "3"; |
break; |
} |
/* Reed Solomon error correction */ |
barStateValues += calcReedSolomon(barStateValues); |
/* Stop character */ |
barStateValues += "13"; |
infoLine("Total Length: " + barStateValues.length()); |
info("Encoding: "); |
for (i = 0; i < barStateValues.length(); i++) { |
switch (barStateValues.charAt(i)) { |
case '1': |
info("A"); |
break; |
case '2': |
info("D"); |
break; |
case '0': |
info("F"); |
break; |
case '3': |
info("T"); |
break; |
} |
} |
infoLine(); |
this.readable = ""; |
this.pattern = new String[1]; |
this.pattern[0] = barStateValues; |
this.row_count = 1; |
this.row_height = new int[1]; |
this.row_height[0] = -1; |
} |
private String calcReedSolomon(final String oldBarStateValues) { |
final ReedSolomon rs = new ReedSolomon(); |
String newBarStateValues = ""; |
/* Adds Reed-Solomon error correction to auspost */ |
int barStateCount; |
int tripleValueCount = 0; |
final int[] tripleValue = new int[31]; |
for (barStateCount = 2; barStateCount < oldBarStateValues.length(); barStateCount += 3, tripleValueCount++) { |
tripleValue[tripleValueCount] = barStateToDecimal(oldBarStateValues.charAt(barStateCount), 4) + barStateToDecimal(oldBarStateValues.charAt(barStateCount + 1), 2) |
+ barStateToDecimal(oldBarStateValues.charAt(barStateCount + 2), 0); |
} |
rs.init_gf(0x43); |
rs.init_code(4, 1); |
rs.encode(tripleValueCount, tripleValue); |
for (barStateCount = 4; barStateCount > 0; barStateCount--) { |
newBarStateValues += BAR_VALUE_TABLE[rs.getResult(barStateCount - 1)]; |
} |
return newBarStateValues; |
} |
private int barStateToDecimal(final char oldBarStateValues, final int shift) { |
return oldBarStateValues - '0' << shift; |
} |
/** {@inheritDoc} */ |
@Override |
protected void plotSymbol() { |
int xBlock; |
int x, y, w, h; |
this.rectangles.clear(); |
x = 0; |
w = 1; |
y = 0; |
h = 0; |
for (xBlock = 0; xBlock < this.pattern[0].length(); xBlock++) { |
switch (this.pattern[0].charAt(xBlock)) { |
case '1': |
y = 0; |
h = 5; |
break; |
case '2': |
y = 3; |
h = 5; |
break; |
case '0': |
y = 0; |
h = 8; |
break; |
case '3': |
y = 3; |
h = 2; |
break; |
} |
final Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h); |
this.rectangles.add(rect); |
x += 2; |
} |
this.symbol_width = (this.pattern[0].length() - 1) * 2 + 1; // no whitespace needed after |
// the final bar |
this.symbol_height = 8; |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Telepen.java |
---|
New file |
0,0 → 1,166 |
/* |
* Copyright 2014 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
/** |
* <p> |
* Implements Telepen (also known as Telepen Alpha). |
* |
* <p> |
* Telepen can encode ASCII text input and includes a modulo-127 check digit. Telepen Numeric allows |
* compression of numeric data into a Telepen symbol. Data can consist of pairs of numbers or pairs |
* consisting of a numerical digit followed by an X character. Telepen Numeric also includes a |
* modulo-127 check digit. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class Telepen extends Symbol { |
public static enum Mode { |
NORMAL, NUMERIC |
} |
private static final String[] TELE_TABLE = { "1111111111111111", "1131313111", "33313111", "1111313131", "3111313111", "11333131", "13133131", "111111313111", "31333111", "1131113131", "33113131", |
"1111333111", "3111113131", "1113133111", "1311133111", "111111113131", "3131113111", "11313331", "333331", "111131113111", "31113331", "1133113111", "1313113111", "1111113331", |
"31131331", "113111113111", "3311113111", "1111131331", "311111113111", "1113111331", "1311111331", "11111111113111", "31313311", "1131311131", "33311131", "1111313311", "3111311131", |
"11333311", "13133311", "111111311131", "31331131", "1131113311", "33113311", "1111331131", "3111113311", "1113131131", "1311131131", "111111113311", "3131111131", "1131131311", |
"33131311", "111131111131", "3111131311", "1133111131", "1313111131", "111111131311", "3113111311", "113111111131", "3311111131", "111113111311", "311111111131", "111311111311", |
"131111111311", "11111111111131", "3131311111", "11313133", "333133", "111131311111", "31113133", "1133311111", "1313311111", "1111113133", "313333", "113111311111", "3311311111", |
"11113333", "311111311111", "11131333", "13111333", "11111111311111", "31311133", "1131331111", "33331111", "1111311133", "3111331111", "11331133", "13131133", "111111331111", |
"3113131111", "1131111133", "33111133", "111113131111", "3111111133", "111311131111", "131111131111", "111111111133", "31311313", "113131111111", "3331111111", "1111311313", |
"311131111111", "11331313", "13131313", "11111131111111", "3133111111", "1131111313", "33111313", "111133111111", "3111111313", "111313111111", "131113111111", "111111111313", |
"313111111111", "1131131113", "33131113", "11113111111111", "3111131113", "113311111111", "131311111111", "111111131113", "3113111113", "11311111111111", "331111111111", "111113111113", |
"31111111111111", "111311111113", "131111111113" }; |
private Mode mode = Mode.NORMAL; |
public void setMode(final Mode mode) { |
this.mode = mode; |
} |
public Mode getMode() { |
return this.mode; |
} |
@Override |
protected void encode() { |
if (this.mode == Mode.NORMAL) { |
normal_mode(); |
} else { |
numeric_mode(); |
} |
} |
private void normal_mode() { |
int count = 0, asciicode, check_digit; |
String p = ""; |
String dest; |
final int l = this.content.length(); |
if (!this.content.matches("[\u0000-\u007F]+")) { |
throw new OkapiException("Invalid characters in input data"); |
} |
dest = TELE_TABLE['_']; // Start |
for (int i = 0; i < l; i++) { |
asciicode = this.content.charAt(i); |
p += TELE_TABLE[asciicode]; |
count += asciicode; |
} |
check_digit = 127 - count % 127; |
if (check_digit == 127) { |
check_digit = 0; |
} |
p += TELE_TABLE[check_digit]; |
infoLine("Check Digit: " + check_digit); |
dest += p; |
dest += TELE_TABLE['z']; // Stop |
this.readable = this.content; |
this.pattern = new String[1]; |
this.pattern[0] = dest; |
this.row_count = 1; |
this.row_height = new int[1]; |
this.row_height[0] = -1; |
} |
private void numeric_mode() { |
int count = 0, check_digit; |
String p = ""; |
String t; |
String dest; |
final int l = this.content.length(); |
int tl, glyph; |
char c1, c2; |
// FIXME: Ensure no extended ASCII or Unicode characters are entered |
if (!this.content.matches("[0-9X]+")) { |
throw new OkapiException("Invalid characters in input"); |
} |
/* If input is an odd length, add a leading zero */ |
if ((l & 1) == 1) { |
t = "0" + this.content; |
tl = l + 1; |
} else { |
t = this.content; |
tl = l; |
} |
dest = TELE_TABLE['_']; // Start |
for (int i = 0; i < tl; i += 2) { |
c1 = t.charAt(i); |
c2 = t.charAt(i + 1); |
/* Input nX is allowed, but Xn is not */ |
if (c1 == 'X') { |
throw new OkapiException("Invalid position of X in data"); |
} |
if (c2 == 'X') { |
glyph = c1 - '0' + 17; |
count += glyph; |
} else { |
glyph = 10 * (c1 - '0') + c2 - '0' + 27; |
count += glyph; |
} |
p += TELE_TABLE[glyph]; |
} |
check_digit = 127 - count % 127; |
if (check_digit == 127) { |
check_digit = 0; |
} |
p += TELE_TABLE[check_digit]; |
infoLine("Check Digit: " + check_digit); |
dest += p; |
dest += TELE_TABLE['z']; // Stop |
this.readable = this.content; |
this.pattern = new String[1]; |
this.pattern[0] = dest; |
this.row_count = 1; |
this.row_height = new int[1]; |
this.row_height[0] = -1; |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Postnet.java |
---|
New file |
0,0 → 1,156 |
/* |
* Copyright 2014 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
import static uk.org.okapibarcode.backend.HumanReadableLocation.NONE; |
import static uk.org.okapibarcode.backend.HumanReadableLocation.TOP; |
import java.awt.geom.Rectangle2D; |
/** |
* <p> |
* Implements <a href="http://en.wikipedia.org/wiki/POSTNET">POSTNET</a> and |
* <a href="http://en.wikipedia.org/wiki/Postal_Alpha_Numeric_Encoding_Technique">PLANET</a> bar |
* code symbologies. |
* |
* <p> |
* POSTNET and PLANET both use numerical input data and include a modulo-10 check digit. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class Postnet extends Symbol { |
public static enum Mode { |
PLANET, POSTNET |
}; |
private static final String[] PN_TABLE = { "LLSSS", "SSSLL", "SSLSL", "SSLLS", "SLSSL", "SLSLS", "SLLSS", "LSSSL", "LSSLS", "LSLSS" }; |
private static final String[] PL_TABLE = { "SSLLL", "LLLSS", "LLSLS", "LLSSL", "LSLLS", "LSLSL", "LSSLL", "SLLLS", "SLLSL", "SLSLL" }; |
private Mode mode; |
public Postnet() { |
this.mode = Mode.POSTNET; |
this.default_height = 12; |
this.humanReadableLocation = HumanReadableLocation.NONE; |
} |
/** |
* Sets the barcode mode (PLANET or POSTNET). The default mode is POSTNET. |
* |
* @param mode the barcode mode (PLANET or POSTNET) |
*/ |
public void setMode(final Mode mode) { |
this.mode = mode; |
} |
/** |
* Returns the barcode mode (PLANET or POSTNET). The default mode is POSTNET. |
* |
* @return the barcode mode (PLANET or POSTNET) |
*/ |
public Mode getMode() { |
return this.mode; |
} |
@Override |
protected void encode() { |
final String[] table = this.mode == Mode.POSTNET ? PN_TABLE : PL_TABLE; |
encode(table); |
} |
private void encode(final String[] table) { |
int i, sum, check_digit; |
String dest; |
if (this.content.length() > 38) { |
throw new OkapiException("Input too long"); |
} |
if (!this.content.matches("[0-9]+")) { |
throw new OkapiException("Invalid characters in data"); |
} |
sum = 0; |
dest = "L"; |
for (i = 0; i < this.content.length(); i++) { |
dest += table[this.content.charAt(i) - '0']; |
sum += this.content.charAt(i) - '0'; |
} |
check_digit = (10 - sum % 10) % 10; |
infoLine("Check Digit: " + check_digit); |
dest += table[check_digit]; |
dest += "L"; |
infoLine("Encoding: " + dest); |
this.readable = this.content; |
this.pattern = new String[] { dest }; |
this.row_count = 1; |
this.row_height = new int[] { -1 }; |
} |
@Override |
protected void plotSymbol() { |
int xBlock, shortHeight; |
double x, y, w, h; |
this.rectangles.clear(); |
this.texts.clear(); |
int baseY; |
if (this.humanReadableLocation == TOP) { |
baseY = getTheoreticalHumanReadableHeight(); |
} else { |
baseY = 0; |
} |
x = 0; |
w = this.moduleWidth; |
shortHeight = (int) (0.4 * this.default_height); |
for (xBlock = 0; xBlock < this.pattern[0].length(); xBlock++) { |
if (this.pattern[0].charAt(xBlock) == 'L') { |
y = baseY; |
h = this.default_height; |
} else { |
y = baseY + this.default_height - shortHeight; |
h = shortHeight; |
} |
this.rectangles.add(new Rectangle2D.Double(x, y, w, h)); |
x += 2.5 * w; |
} |
this.symbol_width = (int) Math.ceil((this.pattern[0].length() - 1) * 2.5 * w + w); // final |
// bar |
// doesn't |
// need |
// extra |
// whitespace |
this.symbol_height = this.default_height; |
if (this.humanReadableLocation != NONE && !this.readable.isEmpty()) { |
double baseline; |
if (this.humanReadableLocation == TOP) { |
baseline = this.fontSize; |
} else { |
baseline = this.symbol_height + this.fontSize; |
} |
this.texts.add(new TextBox(0, baseline, this.symbol_width, this.readable, this.humanReadableAlignment)); |
} |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/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/TextBox.java |
---|
New file |
0,0 → 1,62 |
/* |
* Copyright 2014-2018 Robin Stuart, Daniel Gredler |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
/** |
* A simple text item class. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
* @author Daniel Gredler |
*/ |
public class TextBox { |
/** The X position of the text's left boundary. */ |
public final double x; |
/** The Y position of the text baseline. */ |
public final double y; |
/** The width of the text box. */ |
public final double width; |
/** The text value. */ |
public final String text; |
/** The text alignment. */ |
public final HumanReadableAlignment alignment; |
/** |
* Creates a new instance. |
* |
* @param x the X position of the text's left boundary |
* @param y the Y position of the text baseline |
* @param width the width of the text box |
* @param text the text value |
* @param alignment the text alignment |
*/ |
public TextBox(final double x, final double y, final double width, final String text, final HumanReadableAlignment alignment) { |
this.x = x; |
this.y = y; |
this.width = width; |
this.text = text; |
this.alignment = alignment; |
} |
/** {@inheritDoc} */ |
@Override |
public String toString() { |
return "TextBox[x=" + this.x + ", y=" + this.y + ", width=" + this.width + ", text=" + this.text + ", alignment=" + this.alignment + "]"; |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/AztecRune.java |
---|
New file |
0,0 → 1,160 |
/* |
* Copyright 2014 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
/** |
* <p> |
* Implements the Aztec Runes bar code symbology according to ISO/IEC 24778:2008 Annex A. |
* |
* <p> |
* Aztec Runes is a fixed-size matrix symbology which can encode whole integer values between 0 and |
* 255. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class AztecRune extends Symbol { |
private static final int[] BIT_PLACEMENT_MAP = { 1, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 29, 1, 0, 0, 0, 0, 0, 0, 0, 1, 9, 28, 1, 0, 1, 1, 1, 1, 1, 0, 1, 10, 27, 1, 0, 1, |
0, 0, 0, 1, 0, 1, 11, 26, 1, 0, 1, 0, 1, 0, 1, 0, 1, 12, 25, 1, 0, 1, 0, 0, 0, 1, 0, 1, 13, 24, 1, 0, 1, 1, 1, 1, 1, 0, 1, 14, 23, 1, 0, 0, 0, 0, 0, 0, 0, 1, 15, 0, 1, 1, 1, 1, 1, 1, 1, 1, |
1, 1, 0, 0, 22, 21, 20, 19, 18, 17, 16, 0, 0 }; |
@Override |
protected void encode() { |
if (!this.content.matches("[0-9]+")) { |
throw new OkapiException("Invalid input data"); |
} |
int decimalValue = 0; |
switch (this.content.length()) { |
case 1: |
decimalValue = this.content.charAt(0) - '0'; |
break; |
case 2: |
decimalValue = 10 * (this.content.charAt(0) - '0'); |
decimalValue += this.content.charAt(1) - '0'; |
break; |
case 3: |
decimalValue = 100 * (this.content.charAt(0) - '0'); |
decimalValue += 10 * (this.content.charAt(1) - '0'); |
decimalValue += this.content.charAt(2) - '0'; |
break; |
default: |
throw new OkapiException("Input too large"); |
} |
if (decimalValue > 255) { |
throw new OkapiException("Input too large"); |
} |
final StringBuilder binaryDataStream = new StringBuilder(28); |
for (int i = 0x80; i > 0; i = i >> 1) { |
if ((decimalValue & i) != 0) { |
binaryDataStream.append('1'); |
} else { |
binaryDataStream.append('0'); |
} |
} |
final int[] dataCodeword = new int[3]; |
dataCodeword[0] = 0; |
dataCodeword[1] = 0; |
for (int i = 0; i < 2; i++) { |
if (binaryDataStream.charAt(i * 4) == '1') { |
dataCodeword[i] += 8; |
} |
if (binaryDataStream.charAt(i * 4 + 1) == '1') { |
dataCodeword[i] += 4; |
} |
if (binaryDataStream.charAt(i * 4 + 2) == '1') { |
dataCodeword[i] += 2; |
} |
if (binaryDataStream.charAt(i * 4 + 3) == '1') { |
dataCodeword[i] += 1; |
} |
} |
final int[] errorCorrectionCodeword = new int[6]; |
final ReedSolomon rs = new ReedSolomon(); |
rs.init_gf(0x13); |
rs.init_code(5, 1); |
rs.encode(2, dataCodeword); |
for (int i = 0; i < 5; i++) { |
errorCorrectionCodeword[i] = rs.getResult(i); |
} |
for (int i = 0; i < 5; i++) { |
if ((errorCorrectionCodeword[4 - i] & 0x08) != 0) { |
binaryDataStream.append('1'); |
} else { |
binaryDataStream.append('0'); |
} |
if ((errorCorrectionCodeword[4 - i] & 0x04) != 0) { |
binaryDataStream.append('1'); |
} else { |
binaryDataStream.append('0'); |
} |
if ((errorCorrectionCodeword[4 - i] & 0x02) != 0) { |
binaryDataStream.append('1'); |
} else { |
binaryDataStream.append('0'); |
} |
if ((errorCorrectionCodeword[4 - i] & 0x01) != 0) { |
binaryDataStream.append('1'); |
} else { |
binaryDataStream.append('0'); |
} |
} |
final StringBuilder reversedBinaryDataStream = new StringBuilder(28); |
for (int i = 0; i < binaryDataStream.length(); i++) { |
if ((i & 1) == 0) { |
if (binaryDataStream.charAt(i) == '0') { |
reversedBinaryDataStream.append('1'); |
} else { |
reversedBinaryDataStream.append('0'); |
} |
} else { |
reversedBinaryDataStream.append(binaryDataStream.charAt(i)); |
} |
} |
infoLine("Binary: " + reversedBinaryDataStream); |
this.readable = ""; |
this.pattern = new String[11]; |
this.row_count = 11; |
this.row_height = new int[11]; |
for (int row = 0; row < 11; row++) { |
final StringBuilder rowBinary = new StringBuilder(11); |
for (int column = 0; column < 11; column++) { |
if (BIT_PLACEMENT_MAP[row * 11 + column] == 1) { |
rowBinary.append('1'); |
} |
if (BIT_PLACEMENT_MAP[row * 11 + column] == 0) { |
rowBinary.append('0'); |
} |
if (BIT_PLACEMENT_MAP[row * 11 + column] >= 2) { |
rowBinary.append(reversedBinaryDataStream.charAt(BIT_PLACEMENT_MAP[row * 11 + column] - 2)); |
} |
} |
this.pattern[row] = bin2pat(rowBinary); |
this.row_height[row] = 1; |
} |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Composite.java |
---|
New file |
0,0 → 1,2769 |
/* |
* Copyright 2014 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
import static uk.org.okapibarcode.util.Arrays.positionOf; |
import java.awt.geom.Rectangle2D; |
import java.math.BigInteger; |
import java.util.ArrayList; |
import java.util.List; |
import uk.org.okapibarcode.backend.DataBar14.Mode; |
/** |
* <p> |
* Implements GS1 Composite symbology according to ISO/IEC 24723:2010. |
* |
* <p> |
* Composite symbols comprise a 2D element which encodes GS1 data and a "linear" element which can |
* be UPC, EAN, Code 128 or GS1 DataBar symbol. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class Composite extends Symbol { |
/** The linear component choices available. */ |
public static enum LinearEncoding { |
UPCA, UPCE, EAN, CODE_128, DATABAR_14, DATABAR_14_STACK, DATABAR_14_STACK_OMNI, DATABAR_LIMITED, DATABAR_EXPANDED, DATABAR_EXPANDED_STACK |
} |
/** The 2D component choices available. */ |
public static enum CompositeMode { |
/** |
* Indicates that the composite symbol uses a MicroPDF417 variant as the 2D component. Of |
* the 2D component choices, this one holds the least amount of data. |
*/ |
CC_A, |
/** |
* Indicates that the composite symbol uses a MicroPDF417 symbol as the 2D component, |
* starting with a codeword of 920. |
*/ |
CC_B, |
/** |
* Indicates that the composite symbol uses a PDF417 symbol as the 2D component, starting |
* with a codeword of 920. Of the 2D component choices, this one holds the most amount of |
* data. May only be used if the linear component is {@link LinearEncoding#CODE_128 Code |
* 128}. |
*/ |
CC_C |
} |
private static enum GeneralFieldMode { |
NUMERIC, ALPHA, ISOIEC, INVALID_CHAR, ANY_ENC, ALPHA_OR_ISO |
} |
/* CC-A component coefficients from ISO/IEC 24728:2006 Annex F */ |
private static final int[] CCA_COEFFS = { |
/* k = 4 */ |
522, 568, 723, 809, |
/* k = 5 */ |
427, 919, 460, 155, 566, |
/* k = 6 */ |
861, 285, 19, 803, 17, 766, |
/* k = 7 */ |
76, 925, 537, 597, 784, 691, 437, |
/* k = 8 */ |
237, 308, 436, 284, 646, 653, 428, 379 }; |
private static final int[] COEFRS = { |
/* k = 2 */ |
27, 917, |
/* k = 4 */ |
522, 568, 723, 809, |
/* k = 8 */ |
237, 308, 436, 284, 646, 653, 428, 379, |
/* k = 16 */ |
274, 562, 232, 755, 599, 524, 801, 132, 295, 116, 442, 428, 295, 42, 176, 65, |
/* k = 32 */ |
361, 575, 922, 525, 176, 586, 640, 321, 536, 742, 677, 742, 687, 284, 193, 517, 273, 494, 263, 147, 593, 800, 571, 320, 803, 133, 231, 390, 685, 330, 63, 410, |
/* k = 64 */ |
539, 422, 6, 93, 862, 771, 453, 106, 610, 287, 107, 505, 733, 877, 381, 612, 723, 476, 462, 172, 430, 609, 858, 822, 543, 376, 511, 400, 672, 762, 283, 184, 440, 35, 519, 31, 460, 594, |
225, 535, 517, 352, 605, 158, 651, 201, 488, 502, 648, 733, 717, 83, 404, 97, 280, 771, 840, 629, 4, 381, 843, 623, 264, 543, |
/* k = 128 */ |
521, 310, 864, 547, 858, 580, 296, 379, 53, 779, 897, 444, 400, 925, 749, 415, 822, 93, 217, 208, 928, 244, 583, 620, 246, 148, 447, 631, 292, 908, 490, 704, 516, 258, 457, 907, 594, 723, |
674, 292, 272, 96, 684, 432, 686, 606, 860, 569, 193, 219, 129, 186, 236, 287, 192, 775, 278, 173, 40, 379, 712, 463, 646, 776, 171, 491, 297, 763, 156, 732, 95, 270, 447, 90, 507, 48, |
228, 821, 808, 898, 784, 663, 627, 378, 382, 262, 380, 602, 754, 336, 89, 614, 87, 432, 670, 616, 157, 374, 242, 726, 600, 269, 375, 898, 845, 454, 354, 130, 814, 587, 804, 34, 211, 330, |
539, 297, 827, 865, 37, 517, 834, 315, 550, 86, 801, 4, 108, 539, |
/* k = 256 */ |
524, 894, 75, 766, 882, 857, 74, 204, 82, 586, 708, 250, 905, 786, 138, 720, 858, 194, 311, 913, 275, 190, 375, 850, 438, 733, 194, 280, 201, 280, 828, 757, 710, 814, 919, 89, 68, 569, 11, |
204, 796, 605, 540, 913, 801, 700, 799, 137, 439, 418, 592, 668, 353, 859, 370, 694, 325, 240, 216, 257, 284, 549, 209, 884, 315, 70, 329, 793, 490, 274, 877, 162, 749, 812, 684, 461, 334, |
376, 849, 521, 307, 291, 803, 712, 19, 358, 399, 908, 103, 511, 51, 8, 517, 225, 289, 470, 637, 731, 66, 255, 917, 269, 463, 830, 730, 433, 848, 585, 136, 538, 906, 90, 2, 290, 743, 199, |
655, 903, 329, 49, 802, 580, 355, 588, 188, 462, 10, 134, 628, 320, 479, 130, 739, 71, 263, 318, 374, 601, 192, 605, 142, 673, 687, 234, 722, 384, 177, 752, 607, 640, 455, 193, 689, 707, |
805, 641, 48, 60, 732, 621, 895, 544, 261, 852, 655, 309, 697, 755, 756, 60, 231, 773, 434, 421, 726, 528, 503, 118, 49, 795, 32, 144, 500, 238, 836, 394, 280, 566, 319, 9, 647, 550, 73, |
914, 342, 126, 32, 681, 331, 792, 620, 60, 609, 441, 180, 791, 893, 754, 605, 383, 228, 749, 760, 213, 54, 297, 134, 54, 834, 299, 922, 191, 910, 532, 609, 829, 189, 20, 167, 29, 872, 449, |
83, 402, 41, 656, 505, 579, 481, 173, 404, 251, 688, 95, 497, 555, 642, 543, 307, 159, 924, 558, 648, 55, 497, 10, |
/* k = 512 */ |
352, 77, 373, 504, 35, 599, 428, 207, 409, 574, 118, 498, 285, 380, 350, 492, 197, 265, 920, 155, 914, 299, 229, 643, 294, 871, 306, 88, 87, 193, 352, 781, 846, 75, 327, 520, 435, 543, |
203, 666, 249, 346, 781, 621, 640, 268, 794, 534, 539, 781, 408, 390, 644, 102, 476, 499, 290, 632, 545, 37, 858, 916, 552, 41, 542, 289, 122, 272, 383, 800, 485, 98, 752, 472, 761, 107, |
784, 860, 658, 741, 290, 204, 681, 407, 855, 85, 99, 62, 482, 180, 20, 297, 451, 593, 913, 142, 808, 684, 287, 536, 561, 76, 653, 899, 729, 567, 744, 390, 513, 192, 516, 258, 240, 518, |
794, 395, 768, 848, 51, 610, 384, 168, 190, 826, 328, 596, 786, 303, 570, 381, 415, 641, 156, 237, 151, 429, 531, 207, 676, 710, 89, 168, 304, 402, 40, 708, 575, 162, 864, 229, 65, 861, |
841, 512, 164, 477, 221, 92, 358, 785, 288, 357, 850, 836, 827, 736, 707, 94, 8, 494, 114, 521, 2, 499, 851, 543, 152, 729, 771, 95, 248, 361, 578, 323, 856, 797, 289, 51, 684, 466, 533, |
820, 669, 45, 902, 452, 167, 342, 244, 173, 35, 463, 651, 51, 699, 591, 452, 578, 37, 124, 298, 332, 552, 43, 427, 119, 662, 777, 475, 850, 764, 364, 578, 911, 283, 711, 472, 420, 245, |
288, 594, 394, 511, 327, 589, 777, 699, 688, 43, 408, 842, 383, 721, 521, 560, 644, 714, 559, 62, 145, 873, 663, 713, 159, 672, 729, 624, 59, 193, 417, 158, 209, 563, 564, 343, 693, 109, |
608, 563, 365, 181, 772, 677, 310, 248, 353, 708, 410, 579, 870, 617, 841, 632, 860, 289, 536, 35, 777, 618, 586, 424, 833, 77, 597, 346, 269, 757, 632, 695, 751, 331, 247, 184, 45, 787, |
680, 18, 66, 407, 369, 54, 492, 228, 613, 830, 922, 437, 519, 644, 905, 789, 420, 305, 441, 207, 300, 892, 827, 141, 537, 381, 662, 513, 56, 252, 341, 242, 797, 838, 837, 720, 224, 307, |
631, 61, 87, 560, 310, 756, 665, 397, 808, 851, 309, 473, 795, 378, 31, 647, 915, 459, 806, 590, 731, 425, 216, 548, 249, 321, 881, 699, 535, 673, 782, 210, 815, 905, 303, 843, 922, 281, |
73, 469, 791, 660, 162, 498, 308, 155, 422, 907, 817, 187, 62, 16, 425, 535, 336, 286, 437, 375, 273, 610, 296, 183, 923, 116, 667, 751, 353, 62, 366, 691, 379, 687, 842, 37, 357, 720, |
742, 330, 5, 39, 923, 311, 424, 242, 749, 321, 54, 669, 316, 342, 299, 534, 105, 667, 488, 640, 672, 576, 540, 316, 486, 721, 610, 46, 656, 447, 171, 616, 464, 190, 531, 297, 321, 762, |
752, 533, 175, 134, 14, 381, 433, 717, 45, 111, 20, 596, 284, 736, 138, 646, 411, 877, 669, 141, 919, 45, 780, 407, 164, 332, 899, 165, 726, 600, 325, 498, 655, 357, 752, 768, 223, 849, |
647, 63, 310, 863, 251, 366, 304, 282, 738, 675, 410, 389, 244, 31, 121, 303, 263 }; |
/* rows, error codewords, k-offset of valid CC-A sizes from ISO/IEC 24723:2006 Table 9 */ |
private static final int[] CCA_VARIANTS = { 5, 6, 7, 8, 9, 10, 12, 4, 5, 6, 7, 8, 3, 4, 5, 6, 7, 4, 4, 5, 5, 6, 6, 7, 4, 5, 6, 7, 7, 4, 5, 6, 7, 8, 0, 0, 4, 4, 9, 9, 15, 0, 4, 9, 15, 15, 0, 4, 9, |
15, 22 }; |
/* |
* following is Left RAP, Centre RAP, Right RAP and Start Cluster from ISO/IEC 24723:2006 tables |
* 10 and 11 |
*/ |
private static final int[] A_RAP_TABLE = { 39, 1, 32, 8, 14, 43, 20, 11, 1, 5, 15, 21, 40, 43, 46, 34, 29, 0, 0, 0, 0, 0, 0, 0, 43, 33, 37, 47, 1, 20, 23, 26, 14, 9, 19, 33, 12, 40, 46, 23, 52, |
23, 13, 17, 27, 33, 52, 3, 6, 46, 41, 6, 0, 3, 3, 3, 0, 3, 3, 0, 3, 6, 6, 0, 0, 0, 0, 3 }; |
private static final String[] CODAGEMC = { "urA", "xfs", "ypy", "unk", "xdw", "yoz", "pDA", "uls", "pBk", "eBA", "pAs", "eAk", "prA", "uvs", "xhy", "pnk", "utw", "xgz", "fDA", "pls", "fBk", "frA", |
"pvs", "uxy", "fnk", "ptw", "uwz", "fls", "psy", "fvs", "pxy", "ftw", "pwz", "fxy", "yrx", "ufk", "xFw", "ymz", "onA", "uds", "xEy", "olk", "ucw", "dBA", "oks", "uci", "dAk", "okg", "dAc", |
"ovk", "uhw", "xaz", "dnA", "ots", "ugy", "dlk", "osw", "ugj", "dks", "osi", "dvk", "oxw", "uiz", "dts", "owy", "dsw", "owj", "dxw", "oyz", "dwy", "dwj", "ofA", "uFs", "xCy", "odk", "uEw", |
"xCj", "clA", "ocs", "uEi", "ckk", "ocg", "ckc", "ckE", "cvA", "ohs", "uay", "ctk", "ogw", "uaj", "css", "ogi", "csg", "csa", "cxs", "oiy", "cww", "oij", "cwi", "cyy", "oFk", "uCw", "xBj", |
"cdA", "oEs", "uCi", "cck", "oEg", "uCb", "ccc", "oEa", "ccE", "oED", "chk", "oaw", "uDj", "cgs", "oai", "cgg", "oab", "cga", "cgD", "obj", "cib", "cFA", "oCs", "uBi", "cEk", "oCg", "uBb", |
"cEc", "oCa", "cEE", "oCD", "cEC", "cas", "cag", "caa", "cCk", "uAr", "oBa", "oBD", "cCB", "tfk", "wpw", "yez", "mnA", "tds", "woy", "mlk", "tcw", "woj", "FBA", "mks", "FAk", "mvk", "thw", |
"wqz", "FnA", "mts", "tgy", "Flk", "msw", "Fks", "Fkg", "Fvk", "mxw", "tiz", "Fts", "mwy", "Fsw", "Fsi", "Fxw", "myz", "Fwy", "Fyz", "vfA", "xps", "yuy", "vdk", "xow", "yuj", "qlA", "vcs", |
"xoi", "qkk", "vcg", "xob", "qkc", "vca", "mfA", "tFs", "wmy", "qvA", "mdk", "tEw", "wmj", "qtk", "vgw", "xqj", "hlA", "Ekk", "mcg", "tEb", "hkk", "qsg", "hkc", "EvA", "mhs", "tay", "hvA", |
"Etk", "mgw", "taj", "htk", "qww", "vij", "hss", "Esg", "hsg", "Exs", "miy", "hxs", "Eww", "mij", "hww", "qyj", "hwi", "Eyy", "hyy", "Eyj", "hyj", "vFk", "xmw", "ytj", "qdA", "vEs", "xmi", |
"qck", "vEg", "xmb", "qcc", "vEa", "qcE", "qcC", "mFk", "tCw", "wlj", "qhk", "mEs", "tCi", "gtA", "Eck", "vai", "tCb", "gsk", "Ecc", "mEa", "gsc", "qga", "mED", "EcC", "Ehk", "maw", "tDj", |
"gxk", "Egs", "mai", "gws", "qii", "mab", "gwg", "Ega", "EgD", "Eiw", "mbj", "gyw", "Eii", "gyi", "Eib", "gyb", "gzj", "qFA", "vCs", "xli", "qEk", "vCg", "xlb", "qEc", "vCa", "qEE", "vCD", |
"qEC", "qEB", "EFA", "mCs", "tBi", "ghA", "EEk", "mCg", "tBb", "ggk", "qag", "vDb", "ggc", "EEE", "mCD", "ggE", "qaD", "ggC", "Eas", "mDi", "gis", "Eag", "mDb", "gig", "qbb", "gia", "EaD", |
"giD", "gji", "gjb", "qCk", "vBg", "xkr", "qCc", "vBa", "qCE", "vBD", "qCC", "qCB", "ECk", "mBg", "tAr", "gak", "ECc", "mBa", "gac", "qDa", "mBD", "gaE", "ECC", "gaC", "ECB", "EDg", "gbg", |
"gba", "gbD", "vAq", "vAn", "qBB", "mAq", "EBE", "gDE", "gDC", "gDB", "lfA", "sps", "wey", "ldk", "sow", "ClA", "lcs", "soi", "Ckk", "lcg", "Ckc", "CkE", "CvA", "lhs", "sqy", "Ctk", "lgw", |
"sqj", "Css", "lgi", "Csg", "Csa", "Cxs", "liy", "Cww", "lij", "Cwi", "Cyy", "Cyj", "tpk", "wuw", "yhj", "ndA", "tos", "wui", "nck", "tog", "wub", "ncc", "toa", "ncE", "toD", "lFk", "smw", |
"wdj", "nhk", "lEs", "smi", "atA", "Cck", "tqi", "smb", "ask", "ngg", "lEa", "asc", "CcE", "asE", "Chk", "law", "snj", "axk", "Cgs", "trj", "aws", "nii", "lab", "awg", "Cga", "awa", "Ciw", |
"lbj", "ayw", "Cii", "ayi", "Cib", "Cjj", "azj", "vpA", "xus", "yxi", "vok", "xug", "yxb", "voc", "xua", "voE", "xuD", "voC", "nFA", "tms", "wti", "rhA", "nEk", "xvi", "wtb", "rgk", "vqg", |
"xvb", "rgc", "nEE", "tmD", "rgE", "vqD", "nEB", "CFA", "lCs", "sli", "ahA", "CEk", "lCg", "slb", "ixA", "agk", "nag", "tnb", "iwk", "rig", "vrb", "lCD", "iwc", "agE", "naD", "iwE", "CEB", |
"Cas", "lDi", "ais", "Cag", "lDb", "iys", "aig", "nbb", "iyg", "rjb", "CaD", "aiD", "Cbi", "aji", "Cbb", "izi", "ajb", "vmk", "xtg", "ywr", "vmc", "xta", "vmE", "xtD", "vmC", "vmB", "nCk", |
"tlg", "wsr", "rak", "nCc", "xtr", "rac", "vna", "tlD", "raE", "nCC", "raC", "nCB", "raB", "CCk", "lBg", "skr", "aak", "CCc", "lBa", "iik", "aac", "nDa", "lBD", "iic", "rba", "CCC", "iiE", |
"aaC", "CCB", "aaB", "CDg", "lBr", "abg", "CDa", "ijg", "aba", "CDD", "ija", "abD", "CDr", "ijr", "vlc", "xsq", "vlE", "xsn", "vlC", "vlB", "nBc", "tkq", "rDc", "nBE", "tkn", "rDE", "vln", |
"rDC", "nBB", "rDB", "CBc", "lAq", "aDc", "CBE", "lAn", "ibc", "aDE", "nBn", "ibE", "rDn", "CBB", "ibC", "aDB", "ibB", "aDq", "ibq", "ibn", "xsf", "vkl", "tkf", "nAm", "nAl", "CAo", "aBo", |
"iDo", "CAl", "aBl", "kpk", "BdA", "kos", "Bck", "kog", "seb", "Bcc", "koa", "BcE", "koD", "Bhk", "kqw", "sfj", "Bgs", "kqi", "Bgg", "kqb", "Bga", "BgD", "Biw", "krj", "Bii", "Bib", "Bjj", |
"lpA", "sus", "whi", "lok", "sug", "loc", "sua", "loE", "suD", "loC", "BFA", "kms", "sdi", "DhA", "BEk", "svi", "sdb", "Dgk", "lqg", "svb", "Dgc", "BEE", "kmD", "DgE", "lqD", "BEB", "Bas", |
"kni", "Dis", "Bag", "knb", "Dig", "lrb", "Dia", "BaD", "Bbi", "Dji", "Bbb", "Djb", "tuk", "wxg", "yir", "tuc", "wxa", "tuE", "wxD", "tuC", "tuB", "lmk", "stg", "nqk", "lmc", "sta", "nqc", |
"tva", "stD", "nqE", "lmC", "nqC", "lmB", "nqB", "BCk", "klg", "Dak", "BCc", "str", "bik", "Dac", "lna", "klD", "bic", "nra", "BCC", "biE", "DaC", "BCB", "DaB", "BDg", "klr", "Dbg", "BDa", |
"bjg", "Dba", "BDD", "bja", "DbD", "BDr", "Dbr", "bjr", "xxc", "yyq", "xxE", "yyn", "xxC", "xxB", "ttc", "wwq", "vvc", "xxq", "wwn", "vvE", "xxn", "vvC", "ttB", "vvB", "llc", "ssq", "nnc", |
"llE", "ssn", "rrc", "nnE", "ttn", "rrE", "vvn", "llB", "rrC", "nnB", "rrB", "BBc", "kkq", "DDc", "BBE", "kkn", "bbc", "DDE", "lln", "jjc", "bbE", "nnn", "BBB", "jjE", "rrn", "DDB", "jjC", |
"BBq", "DDq", "BBn", "bbq", "DDn", "jjq", "bbn", "jjn", "xwo", "yyf", "xwm", "xwl", "tso", "wwf", "vto", "xwv", "vtm", "tsl", "vtl", "lko", "ssf", "nlo", "lkm", "rno", "nlm", "lkl", "rnm", |
"nll", "rnl", "BAo", "kkf", "DBo", "lkv", "bDo", "DBm", "BAl", "jbo", "bDm", "DBl", "jbm", "bDl", "jbl", "DBv", "jbv", "xwd", "vsu", "vst", "nku", "rlu", "rlt", "DAu", "bBu", "jDu", "jDt", |
"ApA", "Aok", "keg", "Aoc", "AoE", "AoC", "Aqs", "Aqg", "Aqa", "AqD", "Ari", "Arb", "kuk", "kuc", "sha", "kuE", "shD", "kuC", "kuB", "Amk", "kdg", "Bqk", "kvg", "kda", "Bqc", "kva", "BqE", |
"kvD", "BqC", "AmB", "BqB", "Ang", "kdr", "Brg", "kvr", "Bra", "AnD", "BrD", "Anr", "Brr", "sxc", "sxE", "sxC", "sxB", "ktc", "lvc", "sxq", "sgn", "lvE", "sxn", "lvC", "ktB", "lvB", "Alc", |
"Bnc", "AlE", "kcn", "Drc", "BnE", "AlC", "DrE", "BnC", "AlB", "DrC", "BnB", "Alq", "Bnq", "Aln", "Drq", "Bnn", "Drn", "wyo", "wym", "wyl", "swo", "txo", "wyv", "txm", "swl", "txl", "kso", |
"sgf", "lto", "swv", "nvo", "ltm", "ksl", "nvm", "ltl", "nvl", "Ako", "kcf", "Blo", "ksv", "Dno", "Blm", "Akl", "bro", "Dnm", "Bll", "brm", "Dnl", "Akv", "Blv", "Dnv", "brv", "yze", "yzd", |
"wye", "xyu", "wyd", "xyt", "swe", "twu", "swd", "vxu", "twt", "vxt", "kse", "lsu", "ksd", "ntu", "lst", "rvu", "ypk", "zew", "xdA", "yos", "zei", "xck", "yog", "zeb", "xcc", "yoa", "xcE", |
"yoD", "xcC", "xhk", "yqw", "zfj", "utA", "xgs", "yqi", "usk", "xgg", "yqb", "usc", "xga", "usE", "xgD", "usC", "uxk", "xiw", "yrj", "ptA", "uws", "xii", "psk", "uwg", "xib", "psc", "uwa", |
"psE", "uwD", "psC", "pxk", "uyw", "xjj", "ftA", "pws", "uyi", "fsk", "pwg", "uyb", "fsc", "pwa", "fsE", "pwD", "fxk", "pyw", "uzj", "fws", "pyi", "fwg", "pyb", "fwa", "fyw", "pzj", "fyi", |
"fyb", "xFA", "yms", "zdi", "xEk", "ymg", "zdb", "xEc", "yma", "xEE", "ymD", "xEC", "xEB", "uhA", "xas", "yni", "ugk", "xag", "ynb", "ugc", "xaa", "ugE", "xaD", "ugC", "ugB", "oxA", "uis", |
"xbi", "owk", "uig", "xbb", "owc", "uia", "owE", "uiD", "owC", "owB", "dxA", "oys", "uji", "dwk", "oyg", "ujb", "dwc", "oya", "dwE", "oyD", "dwC", "dys", "ozi", "dyg", "ozb", "dya", "dyD", |
"dzi", "dzb", "xCk", "ylg", "zcr", "xCc", "yla", "xCE", "ylD", "xCC", "xCB", "uak", "xDg", "ylr", "uac", "xDa", "uaE", "xDD", "uaC", "uaB", "oik", "ubg", "xDr", "oic", "uba", "oiE", "ubD", |
"oiC", "oiB", "cyk", "ojg", "ubr", "cyc", "oja", "cyE", "ojD", "cyC", "cyB", "czg", "ojr", "cza", "czD", "czr", "xBc", "ykq", "xBE", "ykn", "xBC", "xBB", "uDc", "xBq", "uDE", "xBn", "uDC", |
"uDB", "obc", "uDq", "obE", "uDn", "obC", "obB", "cjc", "obq", "cjE", "obn", "cjC", "cjB", "cjq", "cjn", "xAo", "ykf", "xAm", "xAl", "uBo", "xAv", "uBm", "uBl", "oDo", "uBv", "oDm", "oDl", |
"cbo", "oDv", "cbm", "cbl", "xAe", "xAd", "uAu", "uAt", "oBu", "oBt", "wpA", "yes", "zFi", "wok", "yeg", "zFb", "woc", "yea", "woE", "yeD", "woC", "woB", "thA", "wqs", "yfi", "tgk", "wqg", |
"yfb", "tgc", "wqa", "tgE", "wqD", "tgC", "tgB", "mxA", "tis", "wri", "mwk", "tig", "wrb", "mwc", "tia", "mwE", "tiD", "mwC", "mwB", "FxA", "mys", "tji", "Fwk", "myg", "tjb", "Fwc", "mya", |
"FwE", "myD", "FwC", "Fys", "mzi", "Fyg", "mzb", "Fya", "FyD", "Fzi", "Fzb", "yuk", "zhg", "hjs", "yuc", "zha", "hbw", "yuE", "zhD", "hDy", "yuC", "yuB", "wmk", "ydg", "zEr", "xqk", "wmc", |
"zhr", "xqc", "yva", "ydD", "xqE", "wmC", "xqC", "wmB", "xqB", "tak", "wng", "ydr", "vik", "tac", "wna", "vic", "xra", "wnD", "viE", "taC", "viC", "taB", "viB", "mik", "tbg", "wnr", "qyk", |
"mic", "tba", "qyc", "vja", "tbD", "qyE", "miC", "qyC", "miB", "qyB", "Eyk", "mjg", "tbr", "hyk", "Eyc", "mja", "hyc", "qza", "mjD", "hyE", "EyC", "hyC", "EyB", "Ezg", "mjr", "hzg", "Eza", |
"hza", "EzD", "hzD", "Ezr", "ytc", "zgq", "grw", "ytE", "zgn", "gny", "ytC", "glz", "ytB", "wlc", "ycq", "xnc", "wlE", "ycn", "xnE", "ytn", "xnC", "wlB", "xnB", "tDc", "wlq", "vbc", "tDE", |
"wln", "vbE", "xnn", "vbC", "tDB", "vbB", "mbc", "tDq", "qjc", "mbE", "tDn", "qjE", "vbn", "qjC", "mbB", "qjB", "Ejc", "mbq", "gzc", "EjE", "mbn", "gzE", "qjn", "gzC", "EjB", "gzB", "Ejq", |
"gzq", "Ejn", "gzn", "yso", "zgf", "gfy", "ysm", "gdz", "ysl", "wko", "ycf", "xlo", "ysv", "xlm", "wkl", "xll", "tBo", "wkv", "vDo", "tBm", "vDm", "tBl", "vDl", "mDo", "tBv", "qbo", "vDv", |
"qbm", "mDl", "qbl", "Ebo", "mDv", "gjo", "Ebm", "gjm", "Ebl", "gjl", "Ebv", "gjv", "yse", "gFz", "ysd", "wke", "xku", "wkd", "xkt", "tAu", "vBu", "tAt", "vBt", "mBu", "qDu", "mBt", "qDt", |
"EDu", "gbu", "EDt", "gbt", "ysF", "wkF", "xkh", "tAh", "vAx", "mAx", "qBx", "wek", "yFg", "zCr", "wec", "yFa", "weE", "yFD", "weC", "weB", "sqk", "wfg", "yFr", "sqc", "wfa", "sqE", "wfD", |
"sqC", "sqB", "lik", "srg", "wfr", "lic", "sra", "liE", "srD", "liC", "liB", "Cyk", "ljg", "srr", "Cyc", "lja", "CyE", "ljD", "CyC", "CyB", "Czg", "ljr", "Cza", "CzD", "Czr", "yhc", "zaq", |
"arw", "yhE", "zan", "any", "yhC", "alz", "yhB", "wdc", "yEq", "wvc", "wdE", "yEn", "wvE", "yhn", "wvC", "wdB", "wvB", "snc", "wdq", "trc", "snE", "wdn", "trE", "wvn", "trC", "snB", "trB", |
"lbc", "snq", "njc", "lbE", "snn", "njE", "trn", "njC", "lbB", "njB", "Cjc", "lbq", "azc", "CjE", "lbn", "azE", "njn", "azC", "CjB", "azB", "Cjq", "azq", "Cjn", "azn", "zio", "irs", "rfy", |
"zim", "inw", "rdz", "zil", "ily", "ikz", "ygo", "zaf", "afy", "yxo", "ziv", "ivy", "adz", "yxm", "ygl", "itz", "yxl", "wco", "yEf", "wto", "wcm", "xvo", "yxv", "wcl", "xvm", "wtl", "xvl", |
"slo", "wcv", "tno", "slm", "vro", "tnm", "sll", "vrm", "tnl", "vrl", "lDo", "slv", "nbo", "lDm", "rjo", "nbm", "lDl", "rjm", "nbl", "rjl", "Cbo", "lDv", "ajo", "Cbm", "izo", "ajm", "Cbl", |
"izm", "ajl", "izl", "Cbv", "ajv", "zie", "ifw", "rFz", "zid", "idy", "icz", "yge", "aFz", "ywu", "ygd", "ihz", "ywt", "wce", "wsu", "wcd", "xtu", "wst", "xtt", "sku", "tlu", "skt", "vnu", |
"tlt", "vnt", "lBu", "nDu", "lBt", "rbu", "nDt", "rbt", "CDu", "abu", "CDt", "iju", "abt", "ijt", "ziF", "iFy", "iEz", "ygF", "ywh", "wcF", "wsh", "xsx", "skh", "tkx", "vlx", "lAx", "nBx", |
"rDx", "CBx", "aDx", "ibx", "iCz", "wFc", "yCq", "wFE", "yCn", "wFC", "wFB", "sfc", "wFq", "sfE", "wFn", "sfC", "sfB", "krc", "sfq", "krE", "sfn", "krC", "krB", "Bjc", "krq", "BjE", "krn", |
"BjC", "BjB", "Bjq", "Bjn", "yao", "zDf", "Dfy", "yam", "Ddz", "yal", "wEo", "yCf", "who", "wEm", "whm", "wEl", "whl", "sdo", "wEv", "svo", "sdm", "svm", "sdl", "svl", "kno", "sdv", "lro", |
"knm", "lrm", "knl", "lrl", "Bbo", "knv", "Djo", "Bbm", "Djm", "Bbl", "Djl", "Bbv", "Djv", "zbe", "bfw", "npz", "zbd", "bdy", "bcz", "yae", "DFz", "yiu", "yad", "bhz", "yit", "wEe", "wgu", |
"wEd", "wxu", "wgt", "wxt", "scu", "stu", "sct", "tvu", "stt", "tvt", "klu", "lnu", "klt", "nru", "lnt", "nrt", "BDu", "Dbu", "BDt", "bju", "Dbt", "bjt", "jfs", "rpy", "jdw", "roz", "jcy", |
"jcj", "zbF", "bFy", "zjh", "jhy", "bEz", "jgz", "yaF", "yih", "yyx", "wEF", "wgh", "wwx", "xxx", "sch", "ssx", "ttx", "vvx", "kkx", "llx", "nnx", "rrx", "BBx", "DDx", "bbx", "jFw", "rmz", |
"jEy", "jEj", "bCz", "jaz", "jCy", "jCj", "jBj", "wCo", "wCm", "wCl", "sFo", "wCv", "sFm", "sFl", "kfo", "sFv", "kfm", "kfl", "Aro", "kfv", "Arm", "Arl", "Arv", "yDe", "Bpz", "yDd", "wCe", |
"wau", "wCd", "wat", "sEu", "shu", "sEt", "sht", "kdu", "kvu", "kdt", "kvt", "Anu", "Bru", "Ant", "Brt", "zDp", "Dpy", "Doz", "yDF", "ybh", "wCF", "wah", "wix", "sEh", "sgx", "sxx", "kcx", |
"ktx", "lvx", "Alx", "Bnx", "Drx", "bpw", "nuz", "boy", "boj", "Dmz", "bqz", "jps", "ruy", "jow", "ruj", "joi", "job", "bmy", "jqy", "bmj", "jqj", "jmw", "rtj", "jmi", "jmb", "blj", "jnj", |
"jli", "jlb", "jkr", "sCu", "sCt", "kFu", "kFt", "Afu", "Aft", "wDh", "sCh", "sax", "kEx", "khx", "Adx", "Avx", "Buz", "Duy", "Duj", "buw", "nxj", "bui", "bub", "Dtj", "bvj", "jus", "rxi", |
"jug", "rxb", "jua", "juD", "bti", "jvi", "btb", "jvb", "jtg", "rwr", "jta", "jtD", "bsr", "jtr", "jsq", "jsn", "Bxj", "Dxi", "Dxb", "bxg", "nyr", "bxa", "bxD", "Dwr", "bxr", "bwq", "bwn", |
"pjk", "urw", "ejA", "pbs", "uny", "ebk", "pDw", "ulz", "eDs", "pBy", "eBw", "zfc", "fjk", "prw", "zfE", "fbs", "pny", "zfC", "fDw", "plz", "zfB", "fBy", "yrc", "zfq", "frw", "yrE", "zfn", |
"fny", "yrC", "flz", "yrB", "xjc", "yrq", "xjE", "yrn", "xjC", "xjB", "uzc", "xjq", "uzE", "xjn", "uzC", "uzB", "pzc", "uzq", "pzE", "uzn", "pzC", "djA", "ors", "ufy", "dbk", "onw", "udz", |
"dDs", "oly", "dBw", "okz", "dAy", "zdo", "drs", "ovy", "zdm", "dnw", "otz", "zdl", "dly", "dkz", "yno", "zdv", "dvy", "ynm", "dtz", "ynl", "xbo", "ynv", "xbm", "xbl", "ujo", "xbv", "ujm", |
"ujl", "ozo", "ujv", "ozm", "ozl", "crk", "ofw", "uFz", "cns", "ody", "clw", "ocz", "cky", "ckj", "zcu", "cvw", "ohz", "zct", "cty", "csz", "ylu", "cxz", "ylt", "xDu", "xDt", "ubu", "ubt", |
"oju", "ojt", "cfs", "oFy", "cdw", "oEz", "ccy", "ccj", "zch", "chy", "cgz", "ykx", "xBx", "uDx", "cFw", "oCz", "cEy", "cEj", "caz", "cCy", "cCj", "FjA", "mrs", "tfy", "Fbk", "mnw", "tdz", |
"FDs", "mly", "FBw", "mkz", "FAy", "zFo", "Frs", "mvy", "zFm", "Fnw", "mtz", "zFl", "Fly", "Fkz", "yfo", "zFv", "Fvy", "yfm", "Ftz", "yfl", "wro", "yfv", "wrm", "wrl", "tjo", "wrv", "tjm", |
"tjl", "mzo", "tjv", "mzm", "mzl", "qrk", "vfw", "xpz", "hbA", "qns", "vdy", "hDk", "qlw", "vcz", "hBs", "qky", "hAw", "qkj", "hAi", "Erk", "mfw", "tFz", "hrk", "Ens", "mdy", "hns", "qty", |
"mcz", "hlw", "Eky", "hky", "Ekj", "hkj", "zEu", "Evw", "mhz", "zhu", "zEt", "hvw", "Ety", "zht", "hty", "Esz", "hsz", "ydu", "Exz", "yvu", "ydt", "hxz", "yvt", "wnu", "xru", "wnt", "xrt", |
"tbu", "vju", "tbt", "vjt", "mju", "mjt", "grA", "qfs", "vFy", "gnk", "qdw", "vEz", "gls", "qcy", "gkw", "qcj", "gki", "gkb", "Efs", "mFy", "gvs", "Edw", "mEz", "gtw", "qgz", "gsy", "Ecj", |
"gsj", "zEh", "Ehy", "zgx", "gxy", "Egz", "gwz", "ycx", "ytx", "wlx", "xnx", "tDx", "vbx", "mbx", "gfk", "qFw", "vCz", "gds", "qEy", "gcw", "qEj", "gci", "gcb", "EFw", "mCz", "ghw", "EEy", |
"ggy", "EEj", "ggj", "Eaz", "giz", "gFs", "qCy", "gEw", "qCj", "gEi", "gEb", "ECy", "gay", "ECj", "gaj", "gCw", "qBj", "gCi", "gCb", "EBj", "gDj", "gBi", "gBb", "Crk", "lfw", "spz", "Cns", |
"ldy", "Clw", "lcz", "Cky", "Ckj", "zCu", "Cvw", "lhz", "zCt", "Cty", "Csz", "yFu", "Cxz", "yFt", "wfu", "wft", "sru", "srt", "lju", "ljt", "arA", "nfs", "tpy", "ank", "ndw", "toz", "als", |
"ncy", "akw", "ncj", "aki", "akb", "Cfs", "lFy", "avs", "Cdw", "lEz", "atw", "ngz", "asy", "Ccj", "asj", "zCh", "Chy", "zax", "axy", "Cgz", "awz", "yEx", "yhx", "wdx", "wvx", "snx", "trx", |
"lbx", "rfk", "vpw", "xuz", "inA", "rds", "voy", "ilk", "rcw", "voj", "iks", "rci", "ikg", "rcb", "ika", "afk", "nFw", "tmz", "ivk", "ads", "nEy", "its", "rgy", "nEj", "isw", "aci", "isi", |
"acb", "isb", "CFw", "lCz", "ahw", "CEy", "ixw", "agy", "CEj", "iwy", "agj", "iwj", "Caz", "aiz", "iyz", "ifA", "rFs", "vmy", "idk", "rEw", "vmj", "ics", "rEi", "icg", "rEb", "ica", "icD", |
"aFs", "nCy", "ihs", "aEw", "nCj", "igw", "raj", "igi", "aEb", "igb", "CCy", "aay", "CCj", "iiy", "aaj", "iij", "iFk", "rCw", "vlj", "iEs", "rCi", "iEg", "rCb", "iEa", "iED", "aCw", "nBj", |
"iaw", "aCi", "iai", "aCb", "iab", "CBj", "aDj", "ibj", "iCs", "rBi", "iCg", "rBb", "iCa", "iCD", "aBi", "iDi", "aBb", "iDb", "iBg", "rAr", "iBa", "iBD", "aAr", "iBr", "iAq", "iAn", "Bfs", |
"kpy", "Bdw", "koz", "Bcy", "Bcj", "Bhy", "Bgz", "yCx", "wFx", "sfx", "krx", "Dfk", "lpw", "suz", "Dds", "loy", "Dcw", "loj", "Dci", "Dcb", "BFw", "kmz", "Dhw", "BEy", "Dgy", "BEj", "Dgj", |
"Baz", "Diz", "bfA", "nps", "tuy", "bdk", "now", "tuj", "bcs", "noi", "bcg", "nob", "bca", "bcD", "DFs", "lmy", "bhs", "DEw", "lmj", "bgw", "DEi", "bgi", "DEb", "bgb", "BCy", "Day", "BCj", |
"biy", "Daj", "bij", "rpk", "vuw", "xxj", "jdA", "ros", "vui", "jck", "rog", "vub", "jcc", "roa", "jcE", "roD", "jcC", "bFk", "nmw", "ttj", "jhk", "bEs", "nmi", "jgs", "rqi", "nmb", "jgg", |
"bEa", "jga", "bED", "jgD", "DCw", "llj", "baw", "DCi", "jiw", "bai", "DCb", "jii", "bab", "jib", "BBj", "DDj", "bbj", "jjj", "jFA", "rms", "vti", "jEk", "rmg", "vtb", "jEc", "rma", "jEE", |
"rmD", "jEC", "jEB", "bCs", "nli", "jas", "bCg", "nlb", "jag", "rnb", "jaa", "bCD", "jaD", "DBi", "bDi", "DBb", "jbi", "bDb", "jbb", "jCk", "rlg", "vsr", "jCc", "rla", "jCE", "rlD", "jCC", |
"jCB", "bBg", "nkr", "jDg", "bBa", "jDa", "bBD", "jDD", "DAr", "bBr", "jDr", "jBc", "rkq", "jBE", "rkn", "jBC", "jBB", "bAq", "jBq", "bAn", "jBn", "jAo", "rkf", "jAm", "jAl", "bAf", "jAv", |
"Apw", "kez", "Aoy", "Aoj", "Aqz", "Bps", "kuy", "Bow", "kuj", "Boi", "Bob", "Amy", "Bqy", "Amj", "Bqj", "Dpk", "luw", "sxj", "Dos", "lui", "Dog", "lub", "Doa", "DoD", "Bmw", "ktj", "Dqw", |
"Bmi", "Dqi", "Bmb", "Dqb", "Alj", "Bnj", "Drj", "bpA", "nus", "txi", "bok", "nug", "txb", "boc", "nua", "boE", "nuD", "boC", "boB", "Dms", "lti", "bqs", "Dmg", "ltb", "bqg", "nvb", "bqa", |
"DmD", "bqD", "Bli", "Dni", "Blb", "bri", "Dnb", "brb", "ruk", "vxg", "xyr", "ruc", "vxa", "ruE", "vxD", "ruC", "ruB", "bmk", "ntg", "twr", "jqk", "bmc", "nta", "jqc", "rva", "ntD", "jqE", |
"bmC", "jqC", "bmB", "jqB", "Dlg", "lsr", "bng", "Dla", "jrg", "bna", "DlD", "jra", "bnD", "jrD", "Bkr", "Dlr", "bnr", "jrr", "rtc", "vwq", "rtE", "vwn", "rtC", "rtB", "blc", "nsq", "jnc", |
"blE", "nsn", "jnE", "rtn", "jnC", "blB", "jnB", "Dkq", "blq", "Dkn", "jnq", "bln", "jnn", "rso", "vwf", "rsm", "rsl", "bko", "nsf", "jlo", "bkm", "jlm", "bkl", "jll", "Dkf", "bkv", "jlv", |
"rse", "rsd", "bke", "jku", "bkd", "jkt", "Aey", "Aej", "Auw", "khj", "Aui", "Aub", "Adj", "Avj", "Bus", "kxi", "Bug", "kxb", "Bua", "BuD", "Ati", "Bvi", "Atb", "Bvb", "Duk", "lxg", "syr", |
"Duc", "lxa", "DuE", "lxD", "DuC", "DuB", "Btg", "kwr", "Dvg", "lxr", "Dva", "BtD", "DvD", "Asr", "Btr", "Dvr", "nxc", "tyq", "nxE", "tyn", "nxC", "nxB", "Dtc", "lwq", "bvc", "nxq", "lwn", |
"bvE", "DtC", "bvC", "DtB", "bvB", "Bsq", "Dtq", "Bsn", "bvq", "Dtn", "bvn", "vyo", "xzf", "vym", "vyl", "nwo", "tyf", "rxo", "nwm", "rxm", "nwl", "rxl", "Dso", "lwf", "bto", "Dsm", "jvo", |
"btm", "Dsl", "jvm", "btl", "jvl", "Bsf", "Dsv", "btv", "jvv", "vye", "vyd", "nwe", "rwu", "nwd", "rwt", "Dse", "bsu", "Dsd", "jtu", "bst", "jtt", "vyF", "nwF", "rwh", "DsF", "bsh", "jsx", |
"Ahi", "Ahb", "Axg", "kir", "Axa", "AxD", "Agr", "Axr", "Bxc", "kyq", "BxE", "kyn", "BxC", "BxB", "Awq", "Bxq", "Awn", "Bxn", "lyo", "szf", "lym", "lyl", "Bwo", "kyf", "Dxo", "lyv", "Dxm", |
"Bwl", "Dxl", "Awf", "Bwv", "Dxv", "tze", "tzd", "lye", "nyu", "lyd", "nyt", "Bwe", "Dwu", "Bwd", "bxu", "Dwt", "bxt", "tzF", "lyF", "nyh", "BwF", "Dwh", "bwx", "Aiq", "Ain", "Ayo", "kjf", |
"Aym", "Ayl", "Aif", "Ayv", "kze", "kzd", "Aye", "Byu", "Ayd", "Byt", "szp" }; |
private static final char[] BR_SET = { 'A', 'B', 'C', 'D', 'E', 'F', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', |
'z', '*', '+', '-' }; |
private static final String[] PDF_TTF = { "00000", "00001", "00010", "00011", "00100", "00101", "00110", "00111", "01000", "01001", "01010", "01011", "01100", "01101", "01110", "01111", "10000", |
"10001", "10010", "10011", "10100", "10101", "10110", "10111", "11000", "11001", "11010", "11011", "11100", "11101", "11110", "11111", "01", "1111111101010100", "11111101000101001" }; |
/* Left and Right Row Address Pattern from Table 2 */ |
private static final String[] RAPLR = { "", "221311", "311311", "312211", "222211", "213211", "214111", "223111", "313111", "322111", "412111", "421111", "331111", "241111", "232111", "231211", |
"321211", "411211", "411121", "411112", "321112", "312112", "311212", "311221", "311131", "311122", "311113", "221113", "221122", "221131", "221221", "222121", "312121", "321121", |
"231121", "231112", "222112", "213112", "212212", "212221", "212131", "212122", "212113", "211213", "211123", "211132", "211141", "211231", "211222", "211312", "211321", "211411", |
"212311" }; |
/* Centre Row Address Pattern from Table 2 */ |
private static final String[] RAPC = { "", "112231", "121231", "122131", "131131", "131221", "132121", "141121", "141211", "142111", "133111", "132211", "131311", "122311", "123211", "124111", |
"115111", "114211", "114121", "123121", "123112", "122212", "122221", "121321", "121411", "112411", "113311", "113221", "113212", "113122", "122122", "131122", "131113", "122113", |
"113113", "112213", "112222", "112312", "112321", "111421", "111331", "111322", "111232", "111223", "111133", "111124", "111214", "112114", "121114", "121123", "121132", "112132", |
"112141" }; |
private static final int[] MICRO_VARIANTS = { 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 11, 14, 17, 20, 24, 28, 8, 11, 14, 17, 20, 23, |
26, 6, 8, 10, 12, 15, 20, 26, 32, 38, 44, 4, 6, 8, 10, 12, 15, 20, 26, 32, 38, 44, 7, 7, 7, 8, 8, 8, 8, 9, 9, 10, 11, 13, 15, 12, 14, 16, 18, 21, 26, 32, 38, 44, 50, 8, 12, 14, 16, 18, 21, |
26, 32, 38, 44, 50, 0, 0, 0, 7, 7, 7, 7, 15, 15, 24, 34, 57, 84, 45, 70, 99, 115, 133, 154, 180, 212, 250, 294, 7, 45, 70, 99, 115, 133, 154, 180, 212, 250, 294 }; |
/* rows, columns, error codewords, k-offset */ |
/* MicroPDF417 coefficients from ISO/IEC 24728:2006 Annex F */ |
private static final int[] MICROCOEFFS = { |
/* k = 7 */ |
76, 925, 537, 597, 784, 691, 437, |
/* k = 8 */ |
237, 308, 436, 284, 646, 653, 428, 379, |
/* k = 9 */ |
567, 527, 622, 257, 289, 362, 501, 441, 205, |
/* k = 10 */ |
377, 457, 64, 244, 826, 841, 818, 691, 266, 612, |
/* k = 11 */ |
462, 45, 565, 708, 825, 213, 15, 68, 327, 602, 904, |
/* k = 12 */ |
597, 864, 757, 201, 646, 684, 347, 127, 388, 7, 69, 851, |
/* k = 13 */ |
764, 713, 342, 384, 606, 583, 322, 592, 678, 204, 184, 394, 692, |
/* k = 14 */ |
669, 677, 154, 187, 241, 286, 274, 354, 478, 915, 691, 833, 105, 215, |
/* k = 15 */ |
460, 829, 476, 109, 904, 664, 230, 5, 80, 74, 550, 575, 147, 868, 642, |
/* k = 16 */ |
274, 562, 232, 755, 599, 524, 801, 132, 295, 116, 442, 428, 295, 42, 176, 65, |
/* k = 18 */ |
279, 577, 315, 624, 37, 855, 275, 739, 120, 297, 312, 202, 560, 321, 233, 756, 760, 573, |
/* k = 21 */ |
108, 519, 781, 534, 129, 425, 681, 553, 422, 716, 763, 693, 624, 610, 310, 691, 347, 165, 193, 259, 568, |
/* k = 26 */ |
443, 284, 887, 544, 788, 93, 477, 760, 331, 608, 269, 121, 159, 830, 446, 893, 699, 245, 441, 454, 325, 858, 131, 847, 764, 169, |
/* k = 32 */ |
361, 575, 922, 525, 176, 586, 640, 321, 536, 742, 677, 742, 687, 284, 193, 517, 273, 494, 263, 147, 593, 800, 571, 320, 803, 133, 231, 390, 685, 330, 63, 410, |
/* k = 38 */ |
234, 228, 438, 848, 133, 703, 529, 721, 788, 322, 280, 159, 738, 586, 388, 684, 445, 680, 245, 595, 614, 233, 812, 32, 284, 658, 745, 229, 95, 689, 920, 771, 554, 289, 231, 125, 117, 518, |
/* k = 44 */ |
476, 36, 659, 848, 678, 64, 764, 840, 157, 915, 470, 876, 109, 25, 632, 405, 417, 436, 714, 60, 376, 97, 413, 706, 446, 21, 3, 773, 569, 267, 272, 213, 31, 560, 231, 758, 103, 271, 572, |
436, 339, 730, 82, 285, |
/* k = 50 */ |
923, 797, 576, 875, 156, 706, 63, 81, 257, 874, 411, 416, 778, 50, 205, 303, 188, 535, 909, 155, 637, 230, 534, 96, 575, 102, 264, 233, 919, 593, 865, 26, 579, 623, 766, 146, 10, 739, 246, |
127, 71, 244, 211, 477, 920, 876, 427, 820, 718, 435 }; |
/* |
* following is Left RAP, Centre RAP, Right RAP and Start Cluster from ISO/IEC 24728:2006 tables |
* 10, 11 and 12 |
*/ |
private static final int[] RAP_TABLE = { 1, 8, 36, 19, 9, 25, 1, 1, 8, 36, 19, 9, 27, 1, 7, 15, 25, 37, 1, 1, 21, 15, 1, 47, 1, 7, 15, 25, 37, 1, 1, 21, 15, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
0, 1, 7, 15, 25, 37, 17, 9, 29, 31, 25, 19, 1, 7, 15, 25, 37, 17, 9, 29, 31, 25, 9, 8, 36, 19, 17, 33, 1, 9, 8, 36, 19, 17, 35, 1, 7, 15, 25, 37, 33, 17, 37, 47, 49, 43, 1, 7, 15, 25, 37, |
33, 17, 37, 47, 49, 0, 3, 6, 0, 6, 0, 0, 0, 3, 6, 0, 6, 6, 0, 0, 6, 0, 0, 0, 0, 6, 6, 0, 3, 0, 0, 6, 0, 0, 0, 0, 6, 6, 0 }; |
private String binary_string; |
private int ecc; |
private LinearEncoding symbology = LinearEncoding.CODE_128; |
private String general_field; |
private GeneralFieldMode[] general_field_type; |
private int cc_width; |
private final int[][] pwr928 = new int[69][7]; |
private final int[] codeWords = new int[180]; |
private int codeWordCount; |
private final int[] bitStr = new int[13]; |
private int[] inputData; |
private CompositeMode cc_mode; |
private String linearContent; |
private CompositeMode userPreferredMode = CompositeMode.CC_A; |
private int target_bitsize; |
private int remainder; |
private int linearWidth; // Width of Code 128 linear |
public Composite() { |
this.inputDataType = Symbol.DataType.GS1; |
} |
@Override |
public void setDataType(final DataType dataType) { |
if (dataType != Symbol.DataType.GS1) { |
throw new IllegalArgumentException("Only GS1 data type is supported for GS1 Composite symbology."); |
} |
} |
@Override |
protected boolean gs1Supported() { |
return true; |
} |
/** |
* Set the type of linear component included in the composite symbol, this will determine how |
* the lower part of the symbol is encoded. |
* |
* @param linearSymbology The symbology of the linear component |
*/ |
public void setSymbology(final LinearEncoding linearSymbology) { |
this.symbology = linearSymbology; |
} |
/** |
* Returns the type of linear component included in the composite symbol. |
* |
* @return the type of linear component included in the composite symbol |
*/ |
public LinearEncoding getSymbology() { |
return this.symbology; |
} |
/** |
* Set the data to be encoded in the linear component of the composite symbol. |
* |
* @param linearContent the linear data in GS1 format |
*/ |
public void setLinearContent(final String linearContent) { |
this.linearContent = linearContent; |
} |
/** |
* Returns the data encoded in the linear component of the composite symbol. |
* |
* @return the data encoded in the linear component of the composite symbol |
*/ |
public String getLinearContent() { |
return this.linearContent; |
} |
/** |
* Set the preferred encoding method for the 2D component of the composite symbol. This value |
* may be ignored if the amount of data supplied is too big for the selected encoding. Mode CC-C |
* can only be used with a Code 128 linear component. |
* |
* @param userMode Preferred mode |
*/ |
public void setPreferredMode(final CompositeMode userMode) { |
this.userPreferredMode = userMode; |
} |
/** |
* Returns the preferred encoding method for the 2D component of the composite symbol. |
* |
* @return the preferred encoding method for the 2D component of the composite symbol |
*/ |
public CompositeMode getPreferredMode() { |
return this.userPreferredMode; |
} |
@Override |
protected void encode() { |
List<Rectangle2D.Double> linear_rect; |
List<TextBox> linear_txt; |
final List<Rectangle2D.Double> combine_rect = new ArrayList<>(); |
final List<TextBox> combine_txt = new ArrayList<>(); |
String linear_encodeInfo; |
int linear_height; |
int top_shift = 0; // 2D component x-coordinate shift |
int bottom_shift = 0; // linear component x-coordinate shift |
this.linearWidth = 0; |
if (this.linearContent.isEmpty()) { |
throw new OkapiException("No linear data set"); |
} |
// Manage composite component encoding first |
encodeComposite(); |
// Then encode linear component |
switch (this.symbology) { |
case UPCA: |
final Upc upca = new Upc(); |
upca.setMode(Upc.Mode.UPCA); |
upca.setLinkageFlag(true); |
upca.setContent(this.linearContent); |
linear_rect = upca.rectangles; |
linear_txt = upca.texts; |
linear_height = upca.symbol_height; |
linear_encodeInfo = upca.getEncodeInfo(); |
bottom_shift = 6; |
top_shift = 3; |
break; |
case UPCE: |
final Upc upce = new Upc(); |
upce.setMode(Upc.Mode.UPCE); |
upce.setLinkageFlag(true); |
upce.setContent(this.linearContent); |
linear_rect = upce.rectangles; |
linear_txt = upce.texts; |
linear_height = upce.symbol_height; |
linear_encodeInfo = upce.getEncodeInfo(); |
bottom_shift = 6; |
top_shift = 3; |
break; |
case EAN: |
final Ean ean = new Ean(); |
if (eanCalculateVersion() == 8) { |
ean.setMode(Ean.Mode.EAN8); |
bottom_shift = 14; |
} else { |
ean.setMode(Ean.Mode.EAN13); |
bottom_shift = 6; |
top_shift = 3; |
} |
ean.setLinkageFlag(true); |
ean.setContent(this.linearContent); |
linear_rect = ean.rectangles; |
linear_txt = ean.texts; |
linear_height = ean.symbol_height; |
linear_encodeInfo = ean.getEncodeInfo(); |
break; |
case CODE_128: |
final Code128 code128 = new Code128(); |
switch (this.cc_mode) { |
case CC_A: |
code128.setCca(); |
break; |
case CC_B: |
code128.setCcb(); |
break; |
case CC_C: |
code128.setCcc(); |
bottom_shift = 7; |
break; |
} |
code128.setDataType(Symbol.DataType.GS1); |
code128.setContent(this.linearContent); |
this.linearWidth = code128.symbol_width; |
linear_rect = code128.rectangles; |
linear_txt = code128.texts; |
linear_height = code128.symbol_height; |
linear_encodeInfo = code128.getEncodeInfo(); |
break; |
case DATABAR_14: |
final DataBar14 dataBar14 = new DataBar14(); |
dataBar14.setLinkageFlag(true); |
dataBar14.setMode(Mode.LINEAR); |
dataBar14.setContent(this.linearContent); |
linear_rect = dataBar14.rectangles; |
linear_txt = dataBar14.texts; |
linear_height = dataBar14.symbol_height; |
linear_encodeInfo = dataBar14.getEncodeInfo(); |
bottom_shift = 4; |
break; |
case DATABAR_14_STACK_OMNI: |
final DataBar14 dataBar14SO = new DataBar14(); |
dataBar14SO.setLinkageFlag(true); |
dataBar14SO.setMode(Mode.OMNI); |
dataBar14SO.setContent(this.linearContent); |
linear_rect = dataBar14SO.rectangles; |
linear_txt = dataBar14SO.texts; |
linear_height = dataBar14SO.symbol_height; |
linear_encodeInfo = dataBar14SO.getEncodeInfo(); |
top_shift = 1; |
break; |
case DATABAR_14_STACK: |
final DataBar14 dataBar14S = new DataBar14(); |
dataBar14S.setLinkageFlag(true); |
dataBar14S.setMode(Mode.STACKED); |
dataBar14S.setContent(this.linearContent); |
linear_rect = dataBar14S.rectangles; |
linear_txt = dataBar14S.texts; |
linear_height = dataBar14S.symbol_height; |
linear_encodeInfo = dataBar14S.getEncodeInfo(); |
top_shift = 1; |
break; |
case DATABAR_LIMITED: |
final DataBarLimited dataBarLimited = new DataBarLimited(); |
dataBarLimited.setLinkageFlag(); |
dataBarLimited.setContent(this.linearContent); |
linear_rect = dataBarLimited.rectangles; |
linear_txt = dataBarLimited.texts; |
linear_height = dataBarLimited.symbol_height; |
linear_encodeInfo = dataBarLimited.getEncodeInfo(); |
top_shift = 1; |
bottom_shift = 10; |
break; |
case DATABAR_EXPANDED: |
final DataBarExpanded dataBarExpanded = new DataBarExpanded(); |
dataBarExpanded.setLinkageFlag(true); |
dataBarExpanded.setStacked(false); |
dataBarExpanded.setContent(this.linearContent); |
linear_rect = dataBarExpanded.rectangles; |
linear_txt = dataBarExpanded.texts; |
linear_height = dataBarExpanded.symbol_height; |
linear_encodeInfo = dataBarExpanded.getEncodeInfo(); |
top_shift = 2; |
break; |
case DATABAR_EXPANDED_STACK: |
final DataBarExpanded dataBarExpandedS = new DataBarExpanded(); |
dataBarExpandedS.setLinkageFlag(true); |
dataBarExpandedS.setStacked(true); |
dataBarExpandedS.setContent(this.linearContent); |
linear_rect = dataBarExpandedS.rectangles; |
linear_txt = dataBarExpandedS.texts; |
linear_height = dataBarExpandedS.symbol_height; |
linear_encodeInfo = dataBarExpandedS.getEncodeInfo(); |
top_shift = 2; |
break; |
default: |
throw new OkapiException("Linear symbol not recognised"); |
} |
if (this.cc_mode == CompositeMode.CC_C && this.symbology == LinearEncoding.CODE_128) { |
/* |
* Width of composite component depends on width of linear component, so recalculate. |
*/ |
this.row_count = 0; |
this.rectangles.clear(); |
this.symbol_height = 0; |
this.symbol_width = 0; |
this.encodeInfo.setLength(0); |
encodeComposite(); |
} |
if (this.cc_mode != CompositeMode.CC_C && this.symbology == LinearEncoding.CODE_128) { |
if (this.linearWidth > this.symbol_width) { |
top_shift = (this.linearWidth - this.symbol_width) / 2; |
} |
} |
for (final Rectangle2D.Double orig : this.rectangles) { |
combine_rect.add(new Rectangle2D.Double(orig.x + top_shift, orig.y, orig.width, orig.height)); |
} |
for (final Rectangle2D.Double orig : linear_rect) { |
combine_rect.add(new Rectangle2D.Double(orig.x + bottom_shift, orig.y + this.symbol_height, orig.width, orig.height)); |
} |
int max_x = 0; |
for (final Rectangle2D.Double rect : combine_rect) { |
if (rect.x + rect.width > max_x) { |
max_x = (int) Math.ceil(rect.x + rect.width); |
} |
} |
for (final TextBox orig : linear_txt) { |
combine_txt.add(new TextBox(orig.x + bottom_shift, orig.y + this.symbol_height, orig.width, orig.text, this.humanReadableAlignment)); |
} |
this.rectangles = combine_rect; |
this.texts = combine_txt; |
this.symbol_height += linear_height; |
this.symbol_width = max_x; |
info(linear_encodeInfo); |
} |
private void encodeComposite() { |
if (this.content.length() > 2990) { |
throw new OkapiException("2D component input data too long"); |
} |
this.cc_mode = this.userPreferredMode; |
if (this.cc_mode == CompositeMode.CC_C && this.symbology != LinearEncoding.CODE_128) { |
/* CC-C can only be used with a GS1-128 linear part */ |
throw new OkapiException("Invalid mode (CC-C only valid with GS1-128 linear component)"); |
} |
switch (this.symbology) { |
/* Determine width of 2D component according to ISO/IEC 24723 Table 1 */ |
case EAN: |
if (eanCalculateVersion() == 8) { |
this.cc_width = 3; |
} else { |
this.cc_width = 4; |
} |
break; |
case UPCE: |
case DATABAR_14_STACK_OMNI: |
case DATABAR_14_STACK: |
this.cc_width = 2; |
break; |
case DATABAR_LIMITED: |
this.cc_width = 3; |
break; |
case CODE_128: |
case DATABAR_14: |
case DATABAR_EXPANDED: |
case UPCA: |
case DATABAR_EXPANDED_STACK: |
this.cc_width = 4; |
break; |
} |
infoLine("Composite Width: " + this.cc_width); |
if (this.cc_mode == CompositeMode.CC_A && !cc_binary_string()) { |
this.cc_mode = CompositeMode.CC_B; |
} |
if (this.cc_mode == CompositeMode.CC_B) { /* |
* If the data didn't fit into CC-A it is |
* recalculated for CC-B |
*/ |
if (!cc_binary_string()) { |
if (this.symbology != LinearEncoding.CODE_128) { |
throw new OkapiException("Input too long"); |
} else { |
this.cc_mode = CompositeMode.CC_C; |
} |
} |
} |
if (this.cc_mode == CompositeMode.CC_C) { |
/* |
* If the data didn't fit in CC-B (and linear part is GS1-128) it is recalculated for |
* CC-C |
*/ |
if (!cc_binary_string()) { |
throw new OkapiException("Input too long"); |
} |
} |
switch (this.cc_mode) { /* Note that ecc_level is only relevant to CC-C */ |
case CC_A: |
cc_a(); |
infoLine("Composite Type: CC-A"); |
break; |
case CC_B: |
cc_b(); |
infoLine("Composite Type: CC-B"); |
break; |
case CC_C: |
cc_c(); |
infoLine("Composite Type: CC-C"); |
break; |
} |
super.plotSymbol(); |
} |
@Override |
protected void plotSymbol() { |
// empty |
} |
private int eanCalculateVersion() { |
/* Determine if EAN-8 or EAN-13 is being used */ |
int length = 0; |
int i; |
boolean latch; |
latch = true; |
for (i = 0; i < this.linearContent.length(); i++) { |
if (this.linearContent.charAt(i) >= '0' && this.linearContent.charAt(i) <= '9') { |
if (latch) { |
length++; |
} |
} else { |
latch = false; |
} |
} |
if (length <= 7) { |
// EAN-8 |
return 8; |
} else { |
// EAN-13 |
return 13; |
} |
} |
private boolean calculateSymbolSize() { |
int i; |
final int binary_length = this.binary_string.length(); |
if (this.cc_mode == CompositeMode.CC_A) { |
/* CC-A 2D component - calculate remaining space */ |
switch (this.cc_width) { |
case 2: |
if (binary_length > 167) { |
return false; |
} |
if (binary_length <= 167) { |
this.target_bitsize = 167; |
} |
if (binary_length <= 138) { |
this.target_bitsize = 138; |
} |
if (binary_length <= 118) { |
this.target_bitsize = 118; |
} |
if (binary_length <= 108) { |
this.target_bitsize = 108; |
} |
if (binary_length <= 88) { |
this.target_bitsize = 88; |
} |
if (binary_length <= 78) { |
this.target_bitsize = 78; |
} |
if (binary_length <= 59) { |
this.target_bitsize = 59; |
} |
break; |
case 3: |
if (binary_length > 167) { |
return false; |
} |
if (binary_length <= 167) { |
this.target_bitsize = 167; |
} |
if (binary_length <= 138) { |
this.target_bitsize = 138; |
} |
if (binary_length <= 118) { |
this.target_bitsize = 118; |
} |
if (binary_length <= 98) { |
this.target_bitsize = 98; |
} |
if (binary_length <= 78) { |
this.target_bitsize = 78; |
} |
break; |
case 4: |
if (binary_length > 197) { |
return false; |
} |
if (binary_length <= 197) { |
this.target_bitsize = 197; |
} |
if (binary_length <= 167) { |
this.target_bitsize = 167; |
} |
if (binary_length <= 138) { |
this.target_bitsize = 138; |
} |
if (binary_length <= 108) { |
this.target_bitsize = 108; |
} |
if (binary_length <= 78) { |
this.target_bitsize = 78; |
} |
break; |
} |
} |
if (this.cc_mode == CompositeMode.CC_B) { |
/* CC-B 2D component - calculated from ISO/IEC 24728 Table 1 */ |
switch (this.cc_width) { |
case 2: |
if (binary_length > 336) { |
return false; |
} |
if (binary_length <= 336) { |
this.target_bitsize = 336; |
} |
if (binary_length <= 296) { |
this.target_bitsize = 296; |
} |
if (binary_length <= 256) { |
this.target_bitsize = 256; |
} |
if (binary_length <= 208) { |
this.target_bitsize = 208; |
} |
if (binary_length <= 160) { |
this.target_bitsize = 160; |
} |
if (binary_length <= 104) { |
this.target_bitsize = 104; |
} |
if (binary_length <= 56) { |
this.target_bitsize = 56; |
} |
break; |
case 3: |
if (binary_length > 768) { |
return false; |
} |
if (binary_length <= 768) { |
this.target_bitsize = 768; |
} |
if (binary_length <= 648) { |
this.target_bitsize = 648; |
} |
if (binary_length <= 536) { |
this.target_bitsize = 536; |
} |
if (binary_length <= 416) { |
this.target_bitsize = 416; |
} |
if (binary_length <= 304) { |
this.target_bitsize = 304; |
} |
if (binary_length <= 208) { |
this.target_bitsize = 208; |
} |
if (binary_length <= 152) { |
this.target_bitsize = 152; |
} |
if (binary_length <= 112) { |
this.target_bitsize = 112; |
} |
if (binary_length <= 72) { |
this.target_bitsize = 72; |
} |
if (binary_length <= 32) { |
this.target_bitsize = 32; |
} |
break; |
case 4: |
if (binary_length > 1184) { |
return false; |
} |
if (binary_length <= 1184) { |
this.target_bitsize = 1184; |
} |
if (binary_length <= 1016) { |
this.target_bitsize = 1016; |
} |
if (binary_length <= 840) { |
this.target_bitsize = 840; |
} |
if (binary_length <= 672) { |
this.target_bitsize = 672; |
} |
if (binary_length <= 496) { |
this.target_bitsize = 496; |
} |
if (binary_length <= 352) { |
this.target_bitsize = 352; |
} |
if (binary_length <= 264) { |
this.target_bitsize = 264; |
} |
if (binary_length <= 208) { |
this.target_bitsize = 208; |
} |
if (binary_length <= 152) { |
this.target_bitsize = 152; |
} |
if (binary_length <= 96) { |
this.target_bitsize = 96; |
} |
if (binary_length <= 56) { |
this.target_bitsize = 56; |
} |
break; |
} |
} |
if (this.cc_mode == CompositeMode.CC_C) { |
/* CC-C 2D Component is a bit more complex! */ |
int byte_length, codewords_used, ecc_level, ecc_codewords, rows; |
int codewords_total, target_codewords, target_bytesize; |
byte_length = binary_length / 8; |
if (binary_length % 8 != 0) { |
byte_length++; |
} |
codewords_used = byte_length / 6 * 5; |
codewords_used += byte_length % 6; |
ecc_level = 7; |
if (codewords_used <= 1280) { |
ecc_level = 6; |
} |
if (codewords_used <= 640) { |
ecc_level = 5; |
} |
if (codewords_used <= 320) { |
ecc_level = 4; |
} |
if (codewords_used <= 160) { |
ecc_level = 3; |
} |
if (codewords_used <= 40) { |
ecc_level = 2; |
} |
this.ecc = ecc_level; |
ecc_codewords = 1; |
for (i = 1; i <= ecc_level + 1; i++) { |
ecc_codewords *= 2; |
} |
codewords_used += ecc_codewords; |
codewords_used += 3; |
if (this.linearWidth == 0) { |
/* Linear component not yet calculated */ |
this.cc_width = (int) (0.5 + Math.sqrt(codewords_used / 3.0)); |
} else { |
this.cc_width = (this.linearWidth - 53) / 17; |
} |
if (codewords_used / this.cc_width > 90) { |
/* stop the symbol from becoming too high */ |
this.cc_width = this.cc_width + 1; |
} |
rows = codewords_used / this.cc_width; |
if (codewords_used % this.cc_width != 0) { |
rows++; |
} |
while (this.cc_width > 3 * rows) { |
/* stop the symbol from becoming too wide (section 10) */ |
this.cc_width--; |
rows = codewords_used / this.cc_width; |
if (codewords_used % this.cc_width != 0) { |
rows++; |
} |
} |
codewords_total = this.cc_width * rows; |
target_codewords = codewords_total - ecc_codewords; |
target_codewords -= 3; |
target_bytesize = 6 * (target_codewords / 5); |
target_bytesize += target_codewords % 5; |
this.target_bitsize = 8 * target_bytesize; |
} |
this.remainder = this.target_bitsize - binary_length; |
return true; |
} |
private boolean cc_binary_string() { |
/* Handles all data encodation from section 5 of ISO/IEC 24723 */ |
int encoding_method, read_posn, d1, d2, value, alpha_pad; |
int i, j, ai_crop, fnc1_latch; |
int group_val; |
int ai90_mode; |
boolean latch; |
int alpha, alphanum, numeric, test1, test2, test3, next_ai_posn; |
int numeric_value, table3_letter; |
String numeric_part; |
String ninety; |
int latchOffset; |
encoding_method = 1; |
read_posn = 0; |
ai_crop = 0; |
fnc1_latch = 0; |
alpha_pad = 0; |
ai90_mode = 0; |
this.ecc = 0; |
value = 0; |
this.target_bitsize = 0; |
if (this.content.charAt(0) == '1' && (this.content.charAt(1) == '0' || this.content.charAt(1) == '1' || this.content.charAt(1) == '7') && this.content.length() >= 8) { |
/* Source starts (10), (11) or (17) */ |
encoding_method = 2; |
} |
if (this.content.charAt(0) == '9' && this.content.charAt(1) == '0') { |
/* Source starts (90) */ |
encoding_method = 3; |
} |
info("Composite Encodation: "); |
switch (encoding_method) { |
case 1: |
infoLine("0"); |
break; |
case 2: |
infoLine("10"); |
break; |
case 3: |
infoLine("11"); |
break; |
} |
this.binary_string = ""; |
if (encoding_method == 1) { |
this.binary_string += "0"; |
} |
if (encoding_method == 2) { |
/* Encoding Method field "10" - date and lot number */ |
this.binary_string += "10"; |
if (this.content.charAt(1) == '0') { |
/* No date data */ |
this.binary_string += "11"; |
read_posn = 2; |
} else { |
/* Production Date (11) or Expiration Date (17) */ |
group_val = (10 * (this.content.charAt(2) - '0') + this.content.charAt(3) - '0') * 384; |
group_val += (10 * (this.content.charAt(4) - '0') + this.content.charAt(5) - '0' - 1) * 32; |
group_val += 10 * (this.content.charAt(6) - '0') + this.content.charAt(7) - '0'; |
for (j = 0; j < 16; j++) { |
if ((group_val & 0x8000 >> j) == 0) { |
this.binary_string += "0"; |
} else { |
this.binary_string += "1"; |
} |
} |
if (this.content.charAt(1) == '1') { |
/* Production Date AI 11 */ |
this.binary_string += "0"; |
} else { |
/* Expiration Date AI 17 */ |
this.binary_string += "1"; |
} |
read_posn = 8; |
} |
if (read_posn + 2 < this.content.length()) { |
if (this.content.charAt(read_posn) == '1' && this.content.charAt(read_posn + 1) == '0') { |
/* Followed by AI 10 - strip this from general field */ |
read_posn += 2; |
} else { |
/* An FNC1 character needs to be inserted in the general field */ |
fnc1_latch = 1; |
} |
} else { |
fnc1_latch = 1; |
} |
} |
if (encoding_method == 3) { |
/* Encodation Method field of "11" - AI 90 */ |
/* |
* "This encodation method may be used if an element string with an AI 90 occurs at the |
* start of the data message, and if the data field following the two-digit AI 90 starts |
* with an alphanumeric string which complies with a specific format." (para 5.2.2) |
*/ |
j = this.content.length(); |
for (i = this.content.length(); i > 2; i--) { |
if (this.content.charAt(i - 1) == '[') { |
j = i; |
} |
} |
ninety = this.content.substring(2, j - 1); |
/* Find out if the AI 90 data is alphabetic or numeric or both */ |
alpha = 0; |
alphanum = 0; |
numeric = 0; |
for (i = 0; i < ninety.length(); i++) { |
if (ninety.charAt(i) >= 'A' && ninety.charAt(i) <= 'Z') { |
/* Character is alphabetic */ |
alpha += 1; |
} |
if (ninety.charAt(i) >= '0' && ninety.charAt(i) <= '9') { |
/* Character is numeric */ |
numeric += 1; |
} |
switch (ninety.charAt(i)) { |
case '*': |
case ',': |
case '-': |
case '.': |
case '/': |
alphanum += 1; |
break; |
} |
if (!(ninety.charAt(i) >= '0' && ninety.charAt(i) <= '9' || ninety.charAt(i) >= 'A' && ninety.charAt(i) <= 'Z')) { |
if (ninety.charAt(i) != '*' && ninety.charAt(i) != ',' && ninety.charAt(i) != '-' && ninety.charAt(i) != '.' && ninety.charAt(i) != '/') { |
/* An Invalid AI 90 character */ |
throw new OkapiException("Invalid AI 90 data"); |
} |
} |
} |
/* must start with 0, 1, 2 or 3 digits followed by an uppercase character */ |
test1 = -1; |
for (i = 3; i >= 0; i--) { |
if (ninety.charAt(i) >= 'A' && ninety.charAt(i) <= 'Z') { |
test1 = i; |
} |
} |
test2 = 0; |
for (i = 0; i < test1; i++) { |
if (!(ninety.charAt(i) >= '0' && ninety.charAt(i) <= '9')) { |
test2 = 1; |
} |
} |
/* leading zeros are not permitted */ |
test3 = 0; |
if (test1 >= 1 && ninety.charAt(0) == '0') { |
test3 = 1; |
} |
if (test1 != -1 && test2 != 1 && test3 == 0) { |
/* Encodation method "11" can be used */ |
this.binary_string += "11"; |
numeric -= test1; |
alpha--; |
/* Decide on numeric, alpha or alphanumeric mode */ |
/* Alpha mode is a special mode for AI 90 */ |
if (alphanum > 0) { |
/* Alphanumeric mode */ |
this.binary_string += "0"; |
ai90_mode = 1; |
} else { |
if (alpha > numeric) { |
/* Alphabetic mode */ |
this.binary_string += "11"; |
ai90_mode = 2; |
} else { |
/* Numeric mode */ |
this.binary_string += "10"; |
ai90_mode = 3; |
} |
} |
next_ai_posn = 2 + ninety.length(); |
if (this.content.charAt(next_ai_posn) == '[') { |
/* There are more AIs afterwords */ |
if (this.content.charAt(next_ai_posn + 1) == '2' && this.content.charAt(next_ai_posn + 2) == '1') { |
/* AI 21 follows */ |
ai_crop = 1; |
} |
if (this.content.charAt(next_ai_posn + 1) == '8' && this.content.charAt(next_ai_posn + 2) == '0' && this.content.charAt(next_ai_posn + 3) == '0' |
&& this.content.charAt(next_ai_posn + 4) == '4') { |
/* AI 8004 follows */ |
ai_crop = 2; |
} |
} |
switch (ai_crop) { |
case 0: |
this.binary_string += "0"; |
break; |
case 1: |
this.binary_string += "10"; |
break; |
case 2: |
this.binary_string += "11"; |
break; |
} |
if (test1 == 0) { |
numeric_part = "0"; |
} else { |
numeric_part = ninety.substring(0, test1); |
} |
numeric_value = 0; |
for (i = 0; i < numeric_part.length(); i++) { |
numeric_value *= 10; |
numeric_value += numeric_part.charAt(i) - '0'; |
} |
table3_letter = -1; |
if (numeric_value < 31) { |
switch (ninety.charAt(test1)) { |
case 'B': |
table3_letter = 0; |
break; |
case 'D': |
table3_letter = 1; |
break; |
case 'H': |
table3_letter = 2; |
break; |
case 'I': |
table3_letter = 3; |
break; |
case 'J': |
table3_letter = 4; |
break; |
case 'K': |
table3_letter = 5; |
break; |
case 'L': |
table3_letter = 6; |
break; |
case 'N': |
table3_letter = 7; |
break; |
case 'P': |
table3_letter = 8; |
break; |
case 'Q': |
table3_letter = 9; |
break; |
case 'R': |
table3_letter = 10; |
break; |
case 'S': |
table3_letter = 11; |
break; |
case 'T': |
table3_letter = 12; |
break; |
case 'V': |
table3_letter = 13; |
break; |
case 'W': |
table3_letter = 14; |
break; |
case 'Z': |
table3_letter = 15; |
break; |
} |
} |
if (table3_letter != -1) { |
/* Encoding can be done according to 5.2.2 c) 2) */ |
/* five bit binary string representing value before letter */ |
for (j = 0; j < 5; j++) { |
if ((numeric_value & 0x10 >> j) == 0x00) { |
this.binary_string += "0"; |
} else { |
this.binary_string += "1"; |
} |
} |
/* followed by four bit representation of letter from Table 3 */ |
for (j = 0; j < 4; j++) { |
if ((table3_letter & 0x08 >> j) == 0x00) { |
this.binary_string += "0"; |
} else { |
this.binary_string += "1"; |
} |
} |
} else { |
/* Encoding is done according to 5.2.2 c) 3) */ |
this.binary_string += "11111"; |
/* ten bit representation of number */ |
for (j = 0; j < 10; j++) { |
if ((numeric_value & 0x200 >> j) == 0x00) { |
this.binary_string += "0"; |
} else { |
this.binary_string += "1"; |
} |
} |
/* five bit representation of ASCII character */ |
for (j = 0; j < 5; j++) { |
if ((ninety.charAt(test1) - 65 & 0x10 >> j) == 0x00) { |
this.binary_string += "0"; |
} else { |
this.binary_string += "1"; |
} |
} |
} |
read_posn = test1 + 3; |
} else { |
/* Use general field encodation instead */ |
this.binary_string += "0"; |
read_posn = 0; |
} |
/* Now encode the rest of the AI 90 data field */ |
if (ai90_mode == 2) { |
/* Alpha encodation (section 5.2.3) */ |
do { |
if (this.content.charAt(read_posn) >= '0' && this.content.charAt(read_posn) <= '9') { |
for (j = 0; j < 5; j++) { |
if ((this.content.charAt(read_posn) + 4 & 0x10 >> j) == 0x00) { |
this.binary_string += "0"; |
} else { |
this.binary_string += "1"; |
} |
} |
} |
if (this.content.charAt(read_posn) >= 'A' && this.content.charAt(read_posn) <= 'Z') { |
for (j = 0; j < 6; j++) { |
if ((this.content.charAt(read_posn) - 65 & 0x20 >> j) == 0x00) { |
this.binary_string += "0"; |
} else { |
this.binary_string += "1"; |
} |
} |
} |
if (this.content.charAt(read_posn) == '[') { |
this.binary_string += "11111"; |
} |
read_posn++; |
} while (this.content.charAt(read_posn - 1) != '[' && read_posn < this.content.length()); |
alpha_pad = 1; /* This is overwritten if a general field is encoded */ |
} |
if (ai90_mode == 1) { |
/* Alphanumeric mode */ |
do { |
if (this.content.charAt(read_posn) >= '0' && this.content.charAt(read_posn) <= '9') { |
for (j = 0; j < 5; j++) { |
if ((this.content.charAt(read_posn) - 43 & 0x10 >> j) == 0x00) { |
this.binary_string += "0"; |
} else { |
this.binary_string += "1"; |
} |
} |
} |
if (this.content.charAt(read_posn) >= 'A' && this.content.charAt(read_posn) <= 'Z') { |
for (j = 0; j < 6; j++) { |
if ((this.content.charAt(read_posn) - 33 & 0x20 >> j) == 0x00) { |
this.binary_string += "0"; |
} else { |
this.binary_string += "1"; |
} |
} |
} |
switch (this.content.charAt(read_posn)) { |
case '[': |
this.binary_string += "01111"; |
break; |
case '*': |
this.binary_string += "111010"; |
break; |
case ',': |
this.binary_string += "111011"; |
break; |
case '-': |
this.binary_string += "111100"; |
break; |
case '.': |
this.binary_string += "111101"; |
break; |
case '/': |
this.binary_string += "111110"; |
break; |
} |
read_posn++; |
} while (this.content.charAt(read_posn - 1) != '[' && this.content.charAt(read_posn - 1) != '\0'); |
} |
read_posn += 2 * ai_crop; |
} |
/* |
* The compressed data field has been processed if appropriate - the rest of the data (if |
* any) goes into a general-purpose data compaction field |
*/ |
j = 0; |
this.general_field = ""; |
if (fnc1_latch == 1) { |
/* |
* Encodation method "10" has been used but it is not followed by AI 10, so a FNC1 |
* character needs to be added |
*/ |
this.general_field += "["; |
} |
this.general_field += this.content.substring(read_posn); |
latch = false; |
if (this.general_field.length() != 0) { |
alpha_pad = 0; |
this.general_field_type = new GeneralFieldMode[this.general_field.length()]; |
for (i = 0; i < this.general_field.length(); i++) { |
/* Table 13 - ISO/IEC 646 encodation */ |
if (this.general_field.charAt(i) < ' ' || this.general_field.charAt(i) > 'z') { |
this.general_field_type[i] = GeneralFieldMode.INVALID_CHAR; |
latch = true; |
} else { |
this.general_field_type[i] = GeneralFieldMode.ISOIEC; |
} |
if (this.general_field.charAt(i) == '#') { |
this.general_field_type[i] = GeneralFieldMode.INVALID_CHAR; |
latch = true; |
} |
if (this.general_field.charAt(i) == '$') { |
this.general_field_type[i] = GeneralFieldMode.INVALID_CHAR; |
latch = true; |
} |
if (this.general_field.charAt(i) == '@') { |
this.general_field_type[i] = GeneralFieldMode.INVALID_CHAR; |
latch = true; |
} |
if (this.general_field.charAt(i) == 92) { |
this.general_field_type[i] = GeneralFieldMode.INVALID_CHAR; |
latch = true; |
} |
if (this.general_field.charAt(i) == '^') { |
this.general_field_type[i] = GeneralFieldMode.INVALID_CHAR; |
latch = true; |
} |
if (this.general_field.charAt(i) == 96) { |
this.general_field_type[i] = GeneralFieldMode.INVALID_CHAR; |
latch = true; |
} |
/* Table 12 - Alphanumeric encodation */ |
if (this.general_field.charAt(i) >= 'A' && this.general_field.charAt(i) <= 'Z') { |
this.general_field_type[i] = GeneralFieldMode.ALPHA_OR_ISO; |
} |
if (this.general_field.charAt(i) == '*') { |
this.general_field_type[i] = GeneralFieldMode.ALPHA_OR_ISO; |
} |
if (this.general_field.charAt(i) == ',') { |
this.general_field_type[i] = GeneralFieldMode.ALPHA_OR_ISO; |
} |
if (this.general_field.charAt(i) == '-') { |
this.general_field_type[i] = GeneralFieldMode.ALPHA_OR_ISO; |
} |
if (this.general_field.charAt(i) == '.') { |
this.general_field_type[i] = GeneralFieldMode.ALPHA_OR_ISO; |
} |
if (this.general_field.charAt(i) == '/') { |
this.general_field_type[i] = GeneralFieldMode.ALPHA_OR_ISO; |
} |
/* Numeric encodation */ |
if (this.general_field.charAt(i) >= '0' && this.general_field.charAt(i) <= '9') { |
this.general_field_type[i] = GeneralFieldMode.ANY_ENC; |
} |
if (this.general_field.charAt(i) == '[') { |
/* FNC1 can be encoded in any system */ |
this.general_field_type[i] = GeneralFieldMode.ANY_ENC; |
} |
} |
if (latch) { |
/* Invalid characters in input data */ |
throw new OkapiException("Invalid characters in input data"); |
} |
for (i = 0; i < this.general_field.length() - 1; i++) { |
if (this.general_field_type[i] == GeneralFieldMode.ISOIEC && this.general_field.charAt(i + 1) == '[') { |
this.general_field_type[i + 1] = GeneralFieldMode.ISOIEC; |
} |
} |
for (i = 0; i < this.general_field.length() - 1; i++) { |
if (this.general_field_type[i] == GeneralFieldMode.ALPHA_OR_ISO && this.general_field.charAt(i + 1) == '[') { |
this.general_field_type[i + 1] = GeneralFieldMode.ALPHA_OR_ISO; |
} |
} |
latch = applyGeneralFieldRules(); |
i = 0; |
do { |
switch (this.general_field_type[i]) { |
case NUMERIC: |
if (i != 0) { |
if (this.general_field_type[i - 1] != GeneralFieldMode.NUMERIC && this.general_field.charAt(i - 1) != '[') { |
this.binary_string += "000"; /* Numeric latch */ |
} |
} |
if (this.general_field.charAt(i) != '[') { |
d1 = this.general_field.charAt(i) - '0'; |
} else { |
d1 = 10; |
} |
if (i < this.general_field.length() - 1) { |
if (this.general_field.charAt(i + 1) != '[') { |
d2 = this.general_field.charAt(i + 1) - '0'; |
} else { |
d2 = 10; |
} |
} else { |
d2 = 10; |
} |
if (d1 != 10 || d2 != 10) { |
/* If (d1==10)&&(d2==10) then input is either FNC1,FNC1 or FNC1,EOL */ |
value = 11 * d1 + d2 + 8; |
for (j = 0; j < 7; j++) { |
if ((value & 0x40 >> j) == 0x00) { |
this.binary_string += "0"; |
} else { |
this.binary_string += "1"; |
} |
} |
i += 2; |
} |
break; |
case ALPHA: |
if (i != 0) { |
if (this.general_field_type[i - 1] == GeneralFieldMode.NUMERIC || this.general_field.charAt(i - 1) == '[') { |
this.binary_string += "0000"; /* Alphanumeric latch */ |
} |
if (this.general_field_type[i - 1] == GeneralFieldMode.ISOIEC) { |
this.binary_string += "00100"; /* ISO/IEC 646 latch */ |
} |
} |
if (this.general_field.charAt(i) >= '0' && this.general_field.charAt(i) <= '9') { |
value = this.general_field.charAt(i) - 43; |
for (j = 0; j < 5; j++) { |
if ((value & 0x10 >> j) == 0x00) { |
this.binary_string += "0"; |
} else { |
this.binary_string += "1"; |
} |
} |
} |
if (this.general_field.charAt(i) >= 'A' && this.general_field.charAt(i) <= 'Z') { |
value = this.general_field.charAt(i) - 33; |
for (j = 0; j < 6; j++) { |
if ((value & 0x20 >> j) == 0x00) { |
this.binary_string += "0"; |
} else { |
this.binary_string += "1"; |
} |
} |
} |
if (this.general_field.charAt(i) == '[') { |
this.binary_string += "01111"; /* FNC1/Numeric latch */ |
} |
if (this.general_field.charAt(i) == '*') { |
this.binary_string += "111010"; /* asterisk */ |
} |
if (this.general_field.charAt(i) == ',') { |
this.binary_string += "111011"; /* comma */ |
} |
if (this.general_field.charAt(i) == '-') { |
this.binary_string += "111100"; /* minus or hyphen */ |
} |
if (this.general_field.charAt(i) == '.') { |
this.binary_string += "111101"; /* period or full stop */ |
} |
if (this.general_field.charAt(i) == '/') { |
this.binary_string += "111110"; /* slash or solidus */ |
} |
i++; |
break; |
case ISOIEC: |
if (i != 0) { |
if (this.general_field_type[i - 1] == GeneralFieldMode.NUMERIC || this.general_field.charAt(i - 1) == '[') { |
this.binary_string += "0000"; /* Alphanumeric latch */ |
this.binary_string += "00100"; /* ISO/IEC 646 latch */ |
} |
if (this.general_field_type[i - 1] == GeneralFieldMode.ALPHA) { |
this.binary_string += "00100"; /* ISO/IEC 646 latch */ |
} |
} |
if (this.general_field.charAt(i) >= '0' && this.general_field.charAt(i) <= '9') { |
value = this.general_field.charAt(i) - 43; |
for (j = 0; j < 5; j++) { |
if ((value & 0x10 >> j) == 0x00) { |
this.binary_string += "0"; |
} else { |
this.binary_string += "1"; |
} |
} |
} |
if (this.general_field.charAt(i) >= 'A' && this.general_field.charAt(i) <= 'Z') { |
value = this.general_field.charAt(i) - 1; |
for (j = 0; j < 7; j++) { |
if ((value & 0x40 >> j) == 0x00) { |
this.binary_string += "0"; |
} else { |
this.binary_string += "1"; |
} |
} |
} |
if (this.general_field.charAt(i) >= 'a' && this.general_field.charAt(i) <= 'z') { |
value = this.general_field.charAt(i) - 7; |
for (j = 0; j < 7; j++) { |
if ((value & 0x40 >> j) == 0x00) { |
this.binary_string += "0"; |
} else { |
this.binary_string += "1"; |
} |
} |
} |
if (this.general_field.charAt(i) == '[') { |
this.binary_string += "01111"; /* FNC1/Numeric latch */ |
} |
if (this.general_field.charAt(i) == '!') { |
this.binary_string += "11101000"; /* exclamation mark */ |
} |
if (this.general_field.charAt(i) == 34) { |
this.binary_string += "11101001"; /* quotation mark */ |
} |
if (this.general_field.charAt(i) == 37) { |
this.binary_string += "11101010"; /* percent sign */ |
} |
if (this.general_field.charAt(i) == '&') { |
this.binary_string += "11101011"; /* ampersand */ |
} |
if (this.general_field.charAt(i) == 39) { |
this.binary_string += "11101100"; /* apostrophe */ |
} |
if (this.general_field.charAt(i) == '(') { |
this.binary_string += "11101101"; /* left parenthesis */ |
} |
if (this.general_field.charAt(i) == ')') { |
this.binary_string += "11101110"; /* right parenthesis */ |
} |
if (this.general_field.charAt(i) == '*') { |
this.binary_string += "11101111"; /* asterisk */ |
} |
if (this.general_field.charAt(i) == '+') { |
this.binary_string += "11110000"; /* plus sign */ |
} |
if (this.general_field.charAt(i) == ',') { |
this.binary_string += "11110001"; /* comma */ |
} |
if (this.general_field.charAt(i) == '-') { |
this.binary_string += "11110010"; /* minus or hyphen */ |
} |
if (this.general_field.charAt(i) == '.') { |
this.binary_string += "11110011"; /* period or full stop */ |
} |
if (this.general_field.charAt(i) == '/') { |
this.binary_string += "11110100"; /* slash or solidus */ |
} |
if (this.general_field.charAt(i) == ':') { |
this.binary_string += "11110101"; /* colon */ |
} |
if (this.general_field.charAt(i) == ';') { |
this.binary_string += "11110110"; /* semicolon */ |
} |
if (this.general_field.charAt(i) == '<') { |
this.binary_string += "11110111"; /* less-than sign */ |
} |
if (this.general_field.charAt(i) == '=') { |
this.binary_string += "11111000"; /* equals sign */ |
} |
if (this.general_field.charAt(i) == '>') { |
this.binary_string += "11111001"; /* greater-than sign */ |
} |
if (this.general_field.charAt(i) == '?') { |
this.binary_string += "11111010"; /* question mark */ |
} |
if (this.general_field.charAt(i) == '_') { |
this.binary_string += "11111011"; /* underline or low line */ |
} |
if (this.general_field.charAt(i) == ' ') { |
this.binary_string += "11111100"; /* space */ |
} |
i++; |
break; |
} |
latchOffset = 0; |
if (latch) { |
latchOffset = 1; |
} |
} while (i + latchOffset < this.general_field.length()); |
} |
if (!calculateSymbolSize()) { |
return false; |
} |
if (latch) { |
i = this.general_field.length() - 1; |
/* There is still one more numeric digit to encode */ |
if (this.general_field.charAt(i) == '[') { |
this.binary_string += "000001111"; |
} else { |
if (this.remainder >= 4 && this.remainder <= 6) { |
d1 = this.general_field.charAt(i) - '0'; |
d1++; |
for (j = 0; j < 4; j++) { |
if ((value & 0x08 >> j) == 0x00) { |
this.binary_string += "0"; |
} else { |
this.binary_string += "1"; |
} |
} |
} else { |
d1 = this.general_field.charAt(i) - '0'; |
d2 = 10; |
value = 11 * d1 + d2 + 8; |
for (j = 0; j < 7; j++) { |
if ((value & 0x40 >> j) == 0x00) { |
this.binary_string += "0"; |
} else { |
this.binary_string += "1"; |
} |
} |
/* This may push the symbol up to the next size */ |
} |
} |
} |
if (this.binary_string.length() > 11805) { /* (2361 * 5) */ |
throw new OkapiException("Input too long"); |
} |
/* size of the symbol may have changed when adding data in the above sequence */ |
if (!calculateSymbolSize()) { |
return false; |
} |
infoLine("Composite Binary Length: " + this.binary_string.length()); |
displayBinaryString(); |
if (this.binary_string.length() < this.target_bitsize) { |
/* Now add padding to binary string */ |
if (alpha_pad == 1) { |
this.binary_string += "11111"; |
alpha_pad = 0; |
/* Extra FNC1 character required after Alpha encodation (section 5.2.3) */ |
} |
if (this.general_field.length() != 0 && this.general_field_type[this.general_field.length() - 1] == GeneralFieldMode.NUMERIC) { |
this.binary_string += "0000"; |
} |
while (this.binary_string.length() < this.target_bitsize) { |
this.binary_string += "00100"; |
} |
this.binary_string = this.binary_string.substring(0, this.target_bitsize); |
} |
return true; |
} |
private void displayBinaryString() { |
int i, nibble; |
/* Display binary string as hexadecimal */ |
info("Composite Binary String: "); |
nibble = 0; |
for (i = 0; i < this.binary_string.length(); i++) { |
switch (i % 4) { |
case 0: |
if (this.binary_string.charAt(i) == '1') { |
nibble += 8; |
} |
break; |
case 1: |
if (this.binary_string.charAt(i) == '1') { |
nibble += 4; |
} |
break; |
case 2: |
if (this.binary_string.charAt(i) == '1') { |
nibble += 2; |
} |
break; |
case 3: |
if (this.binary_string.charAt(i) == '1') { |
nibble += 1; |
} |
info(Integer.toHexString(nibble)); |
nibble = 0; |
break; |
} |
} |
if (this.binary_string.length() % 4 != 0) { |
info(Integer.toHexString(nibble)); |
} |
infoLine(); |
} |
private boolean applyGeneralFieldRules() { |
/* |
* Attempts to apply encoding rules from secions 7.2.5.5.1 to 7.2.5.5.3 of ISO/IEC |
* 24724:2006 |
*/ |
int block_count, i, j, k; |
GeneralFieldMode current, next, last; |
final int[] blockLength = new int[200]; |
final GeneralFieldMode[] blockType = new GeneralFieldMode[200]; |
block_count = 0; |
blockLength[block_count] = 1; |
blockType[block_count] = this.general_field_type[0]; |
for (i = 1; i < this.general_field.length(); i++) { |
current = this.general_field_type[i]; |
last = this.general_field_type[i - 1]; |
if (current == last) { |
blockLength[block_count] = blockLength[block_count] + 1; |
} else { |
block_count++; |
blockLength[block_count] = 1; |
blockType[block_count] = this.general_field_type[i]; |
} |
} |
block_count++; |
for (i = 0; i < block_count; i++) { |
current = blockType[i]; |
next = blockType[i + 1]; |
if (current == GeneralFieldMode.ISOIEC && i != block_count - 1) { |
if (next == GeneralFieldMode.ANY_ENC && blockLength[i + 1] >= 4) { |
blockType[i + 1] = GeneralFieldMode.NUMERIC; |
} |
if (next == GeneralFieldMode.ANY_ENC && blockLength[i + 1] < 4) { |
blockType[i + 1] = GeneralFieldMode.ISOIEC; |
} |
if (next == GeneralFieldMode.ALPHA_OR_ISO && blockLength[i + 1] >= 5) { |
blockType[i + 1] = GeneralFieldMode.ALPHA; |
} |
if (next == GeneralFieldMode.ALPHA_OR_ISO && blockLength[i + 1] < 5) { |
blockType[i + 1] = GeneralFieldMode.ISOIEC; |
} |
} |
if (current == GeneralFieldMode.ALPHA_OR_ISO) { |
blockType[i] = GeneralFieldMode.ALPHA; |
} |
if (current == GeneralFieldMode.ALPHA && i != block_count - 1) { |
if (next == GeneralFieldMode.ANY_ENC && blockLength[i + 1] >= 6) { |
blockType[i + 1] = GeneralFieldMode.NUMERIC; |
} |
if (next == GeneralFieldMode.ANY_ENC && blockLength[i + 1] < 6) { |
if (i == block_count - 2 && blockLength[i + 1] >= 4) { |
blockType[i + 1] = GeneralFieldMode.NUMERIC; |
} else { |
blockType[i + 1] = GeneralFieldMode.ALPHA; |
} |
} |
} |
if (current == GeneralFieldMode.ANY_ENC) { |
blockType[i] = GeneralFieldMode.NUMERIC; |
} |
} |
if (block_count > 1) { |
i = 1; |
while (i < block_count) { |
if (blockType[i - 1] == blockType[i]) { |
/* bring together */ |
blockLength[i - 1] = blockLength[i - 1] + blockLength[i]; |
j = i + 1; |
/* decreace the list */ |
while (j < block_count) { |
blockLength[j - 1] = blockLength[j]; |
blockType[j - 1] = blockType[j]; |
j++; |
} |
block_count--; |
i--; |
} |
i++; |
} |
} |
for (i = 0; i < block_count - 1; i++) { |
if (blockType[i] == GeneralFieldMode.NUMERIC && (blockLength[i] & 1) != 0) { |
/* Odd size numeric block */ |
blockLength[i] = blockLength[i] - 1; |
blockLength[i + 1] = blockLength[i + 1] + 1; |
} |
} |
j = 0; |
for (i = 0; i < block_count; i++) { |
for (k = 0; k < blockLength[i]; k++) { |
this.general_field_type[j] = blockType[i]; |
j++; |
} |
} |
if (blockType[block_count - 1] == GeneralFieldMode.NUMERIC && (blockLength[block_count - 1] & 1) != 0) { |
/* |
* If the last block is numeric and an odd size, further processing needs to be done |
* outside this procedure |
*/ |
return true; |
} else { |
return false; |
} |
} |
private void cc_a() { |
/* CC-A 2D component */ |
int i, strpos, segment, cwCnt, variant, rows; |
int k, offset, j, total; |
final int[] rsCodeWords = new int[8]; |
int LeftRAPStart, RightRAPStart, CentreRAPStart, StartCluster; |
int LeftRAP, RightRAP, CentreRAP, Cluster; |
final int[] dummy = new int[5]; |
int flip, loop; |
String codebarre; |
final StringBuilder bin = new StringBuilder(); |
String local_source; /* A copy of source but with padding zeroes to make 208 bits */ |
variant = 0; |
for (i = 0; i < 13; i++) { |
this.bitStr[i] = 0; |
} |
for (i = 0; i < 28; i++) { |
this.codeWords[i] = 0; |
} |
local_source = this.binary_string; |
for (i = this.binary_string.length(); i < 208; i++) { |
local_source += "0"; |
} |
for (segment = 0; segment < 13; segment++) { |
strpos = segment * 16; |
this.bitStr[segment] = 0; |
for (i = 0; i < 16; i++) { |
if (local_source.charAt(strpos + i) == '1') { |
this.bitStr[segment] += 0x8000 >> i; |
} |
} |
} |
init928(); |
/* encode codeWords from bitStr */ |
cwCnt = encode928(this.binary_string.length()); |
switch (this.cc_width) { |
case 2: |
switch (cwCnt) { |
case 6: |
variant = 0; |
break; |
case 8: |
variant = 1; |
break; |
case 9: |
variant = 2; |
break; |
case 11: |
variant = 3; |
break; |
case 12: |
variant = 4; |
break; |
case 14: |
variant = 5; |
break; |
case 17: |
variant = 6; |
break; |
} |
break; |
case 3: |
switch (cwCnt) { |
case 8: |
variant = 7; |
break; |
case 10: |
variant = 8; |
break; |
case 12: |
variant = 9; |
break; |
case 14: |
variant = 10; |
break; |
case 17: |
variant = 11; |
break; |
} |
break; |
case 4: |
switch (cwCnt) { |
case 8: |
variant = 12; |
break; |
case 11: |
variant = 13; |
break; |
case 14: |
variant = 14; |
break; |
case 17: |
variant = 15; |
break; |
case 20: |
variant = 16; |
break; |
} |
break; |
} |
rows = CCA_VARIANTS[variant]; |
k = CCA_VARIANTS[17 + variant]; |
offset = CCA_VARIANTS[34 + variant]; |
/* Reed-Solomon error correction */ |
for (i = 0; i < 8; i++) { |
rsCodeWords[i] = 0; |
} |
total = 0; |
info("Composite Codewords: "); |
for (i = 0; i < cwCnt; i++) { |
total = (this.codeWords[i] + rsCodeWords[k - 1]) % 929; |
for (j = k - 1; j >= 0; j--) { |
if (j == 0) { |
rsCodeWords[j] = (929 - total * CCA_COEFFS[offset + j] % 929) % 929; |
} else { |
rsCodeWords[j] = (rsCodeWords[j - 1] + 929 - total * CCA_COEFFS[offset + j] % 929) % 929; |
} |
} |
infoSpace(this.codeWords[i]); |
} |
infoLine(); |
for (j = 0; j < k; j++) { |
if (rsCodeWords[j] != 0) { |
rsCodeWords[j] = 929 - rsCodeWords[j]; |
} |
} |
for (i = k - 1; i >= 0; i--) { |
this.codeWords[cwCnt] = rsCodeWords[i]; |
cwCnt++; |
} |
/* Place data into table */ |
LeftRAPStart = A_RAP_TABLE[variant]; |
CentreRAPStart = A_RAP_TABLE[variant + 17]; |
RightRAPStart = A_RAP_TABLE[variant + 34]; |
StartCluster = A_RAP_TABLE[variant + 51] / 3; |
LeftRAP = LeftRAPStart; |
CentreRAP = CentreRAPStart; |
RightRAP = RightRAPStart; |
Cluster = StartCluster; /* |
* Cluster can be 0, 1 or 2 for Cluster(0), Cluster(3) and |
* Cluster(6) |
*/ |
this.readable = ""; |
this.row_count = rows; |
this.pattern = new String[this.row_count]; |
this.row_height = new int[this.row_count]; |
for (i = 0; i < rows; i++) { |
codebarre = ""; |
offset = 929 * Cluster; |
for (j = 0; j < 5; j++) { |
dummy[j] = 0; |
} |
for (j = 0; j < this.cc_width; j++) { |
dummy[j + 1] = this.codeWords[i * this.cc_width + j]; |
} |
/* Copy the data into codebarre */ |
codebarre += RAPLR[LeftRAP]; |
codebarre += "1"; |
codebarre += CODAGEMC[offset + dummy[1]]; |
codebarre += "1"; |
if (this.cc_width == 3) { |
codebarre += RAPC[CentreRAP]; |
} |
if (this.cc_width >= 2) { |
codebarre += "1"; |
codebarre += CODAGEMC[offset + dummy[2]]; |
codebarre += "1"; |
} |
if (this.cc_width == 4) { |
codebarre += RAPC[CentreRAP]; |
} |
if (this.cc_width >= 3) { |
codebarre += "1"; |
codebarre += CODAGEMC[offset + dummy[3]]; |
codebarre += "1"; |
} |
if (this.cc_width == 4) { |
codebarre += "1"; |
codebarre += CODAGEMC[offset + dummy[4]]; |
codebarre += "1"; |
} |
codebarre += RAPLR[RightRAP]; |
codebarre += "1"; /* stop */ |
/* Now codebarre is a mixture of letters and numbers */ |
flip = 1; |
bin.setLength(0); |
for (loop = 0; loop < codebarre.length(); loop++) { |
if (codebarre.charAt(loop) >= '0' && codebarre.charAt(loop) <= '9') { |
for (k = 0; k < codebarre.charAt(loop) - '0'; k++) { |
if (flip == 0) { |
bin.append('0'); |
} else { |
bin.append('1'); |
} |
} |
if (flip == 0) { |
flip = 1; |
} else { |
flip = 0; |
} |
} else { |
bin.append(PDF_TTF[positionOf(codebarre.charAt(loop), BR_SET)]); |
} |
} |
this.row_height[i] = 2; |
this.pattern[i] = bin2pat(bin); |
/* Set up RAPs and Cluster for next row */ |
LeftRAP++; |
CentreRAP++; |
RightRAP++; |
Cluster++; |
if (LeftRAP == 53) { |
LeftRAP = 1; |
} |
if (CentreRAP == 53) { |
CentreRAP = 1; |
} |
if (RightRAP == 53) { |
RightRAP = 1; |
} |
if (Cluster == 3) { |
Cluster = 0; |
} |
} |
} |
/* initialize pwr928 encoding table */ |
private void init928() { |
int i, j, v; |
final int[] cw = new int[7]; |
cw[6] = 1; |
for (i = 5; i >= 0; i--) { |
cw[i] = 0; |
} |
for (i = 0; i < 7; i++) { |
this.pwr928[0][i] = cw[i]; |
} |
for (j = 1; j < 69; j++) { |
for (v = 0, i = 6; i >= 1; i--) { |
v = 2 * cw[i] + v / 928; |
this.pwr928[j][i] = cw[i] = v % 928; |
} |
this.pwr928[j][0] = cw[0] = 2 * cw[0] + v / 928; |
} |
} |
/* converts bit string to base 928 values, codeWords[0] is highest order */ |
private int encode928(final int bitLng) { |
int i, j, b, bitCnt, cwNdx, cwCnt, cwLng; |
for (cwNdx = cwLng = b = 0; b < bitLng; b += 69, cwNdx += 7) { |
bitCnt = min(bitLng - b, 69); |
cwLng += cwCnt = bitCnt / 10 + 1; |
for (i = 0; i < cwCnt; i++) { |
this.codeWords[cwNdx + i] = 0; /* init 0 */ |
} |
for (i = 0; i < bitCnt; i++) { |
if (getBit(b + bitCnt - i - 1)) { |
for (j = 0; j < cwCnt; j++) { |
this.codeWords[cwNdx + j] += this.pwr928[i][j + 7 - cwCnt]; |
} |
} |
} |
for (i = cwCnt - 1; i > 0; i--) { |
/* add "carries" */ |
this.codeWords[cwNdx + i - 1] += this.codeWords[cwNdx + i] / 928; |
this.codeWords[cwNdx + i] %= 928; |
} |
} |
return cwLng; |
} |
private int min(final int first, final int second) { |
if (first <= second) { |
return first; |
} else { |
return second; |
} |
} |
/* gets bit in bitString at bitPos */ |
private boolean getBit(final int arg) { |
if ((this.bitStr[arg >> 4] & 0x8000 >> (arg & 15)) != 0) { |
return true; |
} else { |
return false; |
} |
} |
private void cc_b() { |
/* CC-B 2D component */ |
int length, i, binloc; |
int k, j, longueur, offset; |
final int[] mccorrection = new int[50]; |
int total; |
final int[] dummy = new int[5]; |
String codebarre; |
final StringBuilder bin = new StringBuilder(); |
int variant, LeftRAPStart, CentreRAPStart, RightRAPStart, StartCluster; |
int LeftRAP, CentreRAP, RightRAP, Cluster, flip, loop; |
int option_2, rows; |
this.inputData = new int[this.binary_string.length() / 8 + 3]; |
length = this.binary_string.length() / 8; |
for (i = 0; i < length; i++) { |
binloc = i * 8; |
this.inputData[i] = 0; |
for (j = 0; j < 8; j++) { |
if (this.binary_string.charAt(binloc + j) == '1') { |
this.inputData[i] += 0x80 >> j; |
} |
} |
} |